DeepFM
0.結(jié)論
- DeepFM對W&D模型的改進之處:用FM替代了原來Wide部分,加強了淺層網(wǎng)絡部分的特征組合能力
- DeepFM改進之處與DeepCrossing的思路基本一致绑改,唯一不同之處在于DeepCrossing模型利用多層Cross網(wǎng)絡進行特征組合纳猫,而DeepFM模型利用FM進行特征組合
- FM的局限性:屬于二階特征交叉的模型暑始,無法擴展到三階以上球及,限制其模型的表達
1. 動機
對于CTR問題合愈,被證明的最有效的提升任務表現(xiàn)的策略是特征組合(Feature Interaction), 在CTR問題的探究歷史上來看就是如何更好地學習特征組合照捡,進而更加精確地描述數(shù)據(jù)的特點』安啵可以說這是基礎(chǔ)推薦模型到深度學習推薦模型遵循的一個主要的思想栗精。而組合特征大牛們研究過組合二階特征,三階甚至更高階瞻鹏,但是面臨一個問題就是隨著階數(shù)的提升悲立,復雜度就成幾何倍的升高。這樣即使模型的表現(xiàn)更好了新博,但是推薦系統(tǒng)在實時性的要求也不能滿足了薪夕。所以很多模型的出現(xiàn)都是為了解決另外一個更加深入的問題:如何更高效的學習特征組合?
為了解決上述問題赫悄,出現(xiàn)了FM和FFM來優(yōu)化LR的特征組合較差這一個問題原献。并且在這個時候科學家們已經(jīng)發(fā)現(xiàn)了DNN在特征組合方面的優(yōu)勢馏慨,所以又出現(xiàn)了FNN和PNN等使用深度網(wǎng)絡的模型。但是DNN也存在局限性姑隅。
-
==DNN局限==
當我們使用DNN網(wǎng)絡解決推薦問題的時候存在網(wǎng)絡參數(shù)過于龐大的問題写隶,這是因為在進行特征處理的時候我們需要使用one-hot編碼來處理離散特征,這會導致輸入的維度猛增讲仰。這里借用AI大會的一張圖片:
這樣龐大的參數(shù)量也是不實際的慕趴。為了解決DNN參數(shù)量過大的局限性,可以采用非常經(jīng)典的Field思想鄙陡,將OneHot特征轉(zhuǎn)換為Dense Vector
此時通過增加全連接層就可以實現(xiàn)高階的特征組合冕房,如下圖所示:
但是仍然缺少低階的特征組合,于是增加FM來表示低階的特征組合趁矾。
- ==FNN和PNN==
結(jié)合FM和DNN其實有兩種方式耙册,可以并行結(jié)合也可以串行結(jié)合。這兩種方式各有幾種代表模型愈魏。在DeepFM之前有FNN觅玻,雖然在影響力上可能并不如DeepFM,但是了解FNN的思想對我們理解DeepFM的特點和優(yōu)點是很有幫助的培漏。
FNN是使用預訓練好的FM模塊溪厘,得到隱向量,然后把隱向量作為DNN的輸入牌柄,但是經(jīng)過實驗進一步發(fā)現(xiàn)畸悬,在Embedding layer和hidden layer1之間增加一個product層(如上圖所示)可以提高模型的表現(xiàn),所以提出了PNN珊佣,使用product layer替換FM預訓練層蹋宦。
- ==Wide&Deep==
FNN和PNN模型仍然有一個比較明顯的尚未解決的缺點:對于低階組合特征學習到的比較少,這一點主要是由于FM和DNN的串行方式導致的咒锻,也就是雖然FM學到了低階特征組合冷冗,但是DNN的全連接結(jié)構(gòu)導致低階特征并不能在DNN的輸出端較好的表現(xiàn)』笸В看來我們已經(jīng)找到問題了蒿辙,將串行方式改進為并行方式能比較好的解決這個問題。于是Google提出了Wide&Deep模型滨巴,但是如果深入探究Wide&Deep的構(gòu)成方式思灌,雖然將整個模型的結(jié)構(gòu)調(diào)整為了并行結(jié)構(gòu),在實際的使用中Wide Module中的部分需要較為精巧的特征工程恭取,換句話說人工處理對于模型的效果具有比較大的影響(這一點可以在Wide&Deep模型部分得到驗證)泰偿。
如上圖所示,該模型仍然存在問題:在output Units階段直接將低階和高階特征進行組合蜈垮,很容易讓模型最終偏向?qū)W習到低階或者高階的特征耗跛,而不能做到很好的結(jié)合裕照。
綜上所示,DeepFM模型橫空出世课兄。
2. 模型的結(jié)構(gòu)與原理
前面的Field和Embedding處理是和前面的方法是相同的牍氛,如上圖中的綠色部分;DeepFM將Wide部分替換為了FM layer如上圖中的藍色部分
這幅圖其實有很多的點需要注意烟阐,很多人都一眼略過了搬俊,這里我個人認為在DeepFM模型中有三點需要注意:
- Deep模型部分
- FM模型部分
- Sparse Feature中黃色和灰色節(jié)點代表什么意思
2.1 FM
詳細內(nèi)容參考FM模型部分的內(nèi)容,下圖是FM的一個結(jié)構(gòu)圖蜒茄,從圖中大致可以看出FM Layer是由一階特征和二階特征Concatenate到一起在經(jīng)過一個Sigmoid得到logits(結(jié)合FM的公式一起看)唉擂,所以在實現(xiàn)的時候需要單獨考慮linear部分和FM交叉特征部分。
2.2 Deep
Deep架構(gòu)圖
Deep Module是為了學習高階的特征組合檀葛,在上圖中使用用全連接的方式將Dense Embedding輸入到Hidden Layer玩祟,這里面Dense Embeddings就是為了解決DNN中的參數(shù)爆炸問題,這也是推薦模型中常用的處理方法屿聋。
Embedding層的輸出是將所有id類特征對應的embedding向量concat到到一起輸入到DNN中空扎。其中表示第i個field的embedding,m是field的數(shù)量润讥。
上一層的輸出作為下一層的輸入转锈,我們得到:
其中表示激活函數(shù),分別表示該層的輸入楚殿、權(quán)重和偏置撮慨。
最后進入DNN部分輸出使用sigmod激活函數(shù)進行激活:
3. 代碼實現(xiàn)
DeepFM在模型的結(jié)構(gòu)圖中顯示,模型大致由兩部分組成脆粥,一部分是FM砌溺,還有一部分就是DNN, 而FM又由一階特征部分與二階特征交叉部分組成,所以可以將整個模型拆成三部分变隔,分別是一階特征處理linear部分规伐,二階特征交叉FM以及DNN的高階特征交叉。在下面的代碼中也能夠清晰的看到這個結(jié)構(gòu)匣缘。此外每一部分可能由是由不同的特征組成猖闪,所以在構(gòu)建模型的時候需要分別對這三部分輸入的特征進行選擇。
linear_logits: 這部分是有關(guān)于線性計算孵户,也就是FM的前半部分的計算萧朝。對于這一塊的計算岔留,我們用了一個get_linear_logits函數(shù)實現(xiàn)夏哭,后面再說,總之通過這個函數(shù)献联,我們就可以實現(xiàn)上面這個公式的計算過程竖配,得到linear的輸出何址, 這部分特征由數(shù)值特征和類別特征的onehot編碼組成的一維向量組成,實際應用中根據(jù)自己的業(yè)務放置不同的一階特征(這里的dense特征并不是必須的进胯,有可能會將數(shù)值特征進行分桶用爪,然后在當做類別特征來處理)
fm_logits: 這一塊主要是針對離散的特征,首先過embedding胁镐,然后使用FM特征交叉的方式偎血,兩兩特征進行交叉,得到新的特征向量盯漂,最后計算交叉特征的logits
dnn_logits: 這一塊主要是針對離散的特征颇玷,首先過embedding,然后將得到的embedding拼接成一個向量(具體的可以看代碼就缆,也可以看一下下面的模型結(jié)構(gòu)圖)帖渠,通過dnn學習類別特征之間的隱式特征交叉并輸出logits值
def DeepFM(linear_feature_columns, dnn_feature_columns):
# 構(gòu)建輸入層,即所有特征對應的Input()層竭宰,這里使用字典的形式返回空郊,方便后續(xù)構(gòu)建模型
dense_input_dict, sparse_input_dict = build_input_layers(linear_feature_columns + dnn_feature_columns)
# 將linear部分的特征中sparse特征篩選出來,后面用來做1維的embedding
linear_sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), linear_feature_columns))
# 構(gòu)建模型的輸入層切揭,模型的輸入層不能是字典的形式狞甚,應該將字典的形式轉(zhuǎn)換成列表的形式
# 注意:這里實際的輸入與Input()層的對應,是通過模型輸入時候的字典數(shù)據(jù)的key與對應name的Input層
input_layers = list(dense_input_dict.values()) + list(sparse_input_dict.values())
# linear_logits由兩部分組成伴箩,分別是dense特征的logits和sparse特征的logits
linear_logits = get_linear_logits(dense_input_dict, sparse_input_dict, linear_sparse_feature_columns)
# 構(gòu)建維度為k的embedding層入愧,這里使用字典的形式返回,方便后面搭建模型
# embedding層用戶構(gòu)建FM交叉部分和DNN的輸入部分
embedding_layers = build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False)
# 將輸入到dnn中的所有sparse特征篩選出來
dnn_sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), dnn_feature_columns))
fm_logits = get_fm_logits(sparse_input_dict, dnn_sparse_feature_columns, embedding_layers) # 只考慮二階項
# 將所有的Embedding都拼起來嗤谚,一起輸入到dnn中
dnn_logits = get_dnn_logits(sparse_input_dict, dnn_sparse_feature_columns, embedding_layers)
# 將linear,FM,dnn的logits相加作為最終的logits
output_logits = Add()([linear_logits, fm_logits, dnn_logits])
# 這里的激活函數(shù)使用sigmoid
output_layers = Activation("sigmoid")(output_logits)
model = Model(input_layers, output_layers)
return model
關(guān)于每一塊的細節(jié)棺蛛,這里就不解釋了,在我們給出的GitHub代碼中巩步,我們已經(jīng)加了非常詳細的注釋旁赊,大家看那個應該很容易看明白, 為了方便大家的閱讀椅野,我們這里還給大家畫了一個整體的模型架構(gòu)圖终畅,幫助大家更好的了解每一塊以及前向傳播(畫的圖不是很規(guī)范,先將就看一下竟闪,后面我們會統(tǒng)一在優(yōu)化一下這個手工圖)离福。
下面是一個通過keras畫的模型結(jié)構(gòu)圖,為了更好的顯示炼蛤,數(shù)值特征和類別特征都只是選擇了一小部分妖爷。
4. 思考
-
如果對于FM采用隨機梯度下降SGD訓練模型參數(shù),請寫出模型各個參數(shù)的梯度和FM參數(shù)訓練的復雜度
答:模型各個參數(shù)的梯度理朋,及參數(shù)訓練的復雜度絮识,如下
- 對于下圖所示绿聘,根據(jù)你的理解Sparse Feature中的不同顏色節(jié)點分別表示什么意思
答: Sparse Feature中對Field采用One-Hot編碼,對于每個Field而言次舌,其含有N個灰色節(jié)點 與 1個黃色節(jié)點熄攘,其中灰色節(jié)點即為對應0,黃色節(jié)點對應1