【论文解读】DeepFM论文总结
本次要總結分享的是 推薦/CTR 領域內著名的deepfm[1] 論文,參考的代碼tensorflow-DeepFM[2],該論文方法較為簡單,實現起來也比較容易,該方法在工業界十分常用。
「建議在非深色主題下閱讀本文,pc端閱讀點擊文末左下角“原文鏈接”,體驗更佳」
@
論文動機及創新點
模型結構
輸入數據
FM 部分
Deep 部分
代碼分析
數據預處理
定義 DeepFM 模型超參數
構圖
總結
論文動機及創新點
在 deepfm 提出之前,現有的模型很難很好的提取低階和高階的交互特征,或者需要足夠豐富的人工特征工程才能進行。
一些特性交互很容易理解,可以由專家(對業務邏輯很了解的人)設計。然而,大多數其他的特征交互都隱藏在數據中,難以識別先驗信息(例如,經典的關聯規則“尿布和啤酒”是從數據中挖掘出來的,而不是由專家發現),這只能通過機器學習自動獲取。即使對于易于理解的交互特征,專家似乎也不太可能對它們進行窮盡式的建模,尤其是當特征數量很大的時候。
所以對于許多數據挖掘類比賽,特征工程的工作量幾乎占到工作量的 95%以上,大部分甚至一些優秀選手,首先一股腦的把所能想到的特征都使用上,然后根據效果做些適當特征選擇。當選取的特征效果的確很好時,把構建這些特征的思路包裝成某一個聽起來很高逼格的”方法論“。
?很容易想到,有沒有什么辦法,能讓模型能端到端的進行特征學習呢?從而避免繁雜的人工特征工程過程。deepfm 論文里就是基于這一動機,將 fm 模型和 DNN 模型聯合起來進行訓練,其中 fm 模型可能捕捉到一些低階的交互特征,而 DNN 模型捕捉一些高階模型。該聯合模型可以進行端到端的訓練學習。
Deepfm 模型中的 Deep 部分和 fm 部分共享 embedding,極大減少了需要學習的參數,使其訓練過程很有效率。
和以往的 CTR 模型相比,Deepfm 模型效果最好。
模型結構
在這里插入圖片描述上圖為 DeepFM 的網絡結構圖,由左邊的 FM 模型和右邊的 DNN 模型組成,兩個子模型共享下方的輸入 embedding。
輸入數據
假定有 個樣本,每個樣本由 組成,其中
由 個特征組成,其中包含了 「類別」型特征 和 「數值」型特征組成,每個特征可理解為一個 。
其中 類別 型特征可用 one-hot 編碼表示,數值型特征用其本身數值或者離散化在 one-hot 表示
?這里定義 表示特征個數; 表示 (數值特征個數+類別特征「取值」個數); 表示
類別特征 one-hot 后向量長度即為取值個數
?FM 部分
這里定義兩個參數矩陣
feature_bias:shape 為 的一階參數矩陣
feature_embeddings:shape 為 的二階參數矩陣
<> 表示內積不得不說:這篇論文里面的網絡圖都畫的好丑
上式中 第一項<w,x> 表示提取一階特征,第二項表示提取二階交叉特征;每個樣本在類別型 特征上只有一個取值。
d 表示 ,第二項是在不同 field 之間做二階交叉特征計算。
對于一階特征中的參數 表示從 feature_bias 參數矩陣中 lookup 得到一個參數,樣本中每個特征都能得到一個對應向量(長度為 1)。
二階特征中, 分別表示 的隱向量,可以從 feature_embeddings 參數矩陣中 lookup 得到(長度為 k)。
在這里插入圖片描述
這里可以理解將數值特征也embedding成向量,一個數值特征只對應一個embedding向量,而一個類別特征的不同取值則對應不同向量,但向量長度均為k,對應論文里說:即使不同的field長度不一樣(one-hot向量長度不一樣),但是都能embedding成度相同的向量。
?二階交叉特征推導:
上式中, 表示 , 表示隱向量從 feature*embeddings (shape 為[feature_size,k]) 參數矩陣中 lookup 得到的參數,那么對于第 個 ,其得到的 shape 為 ,,因此 表示 第 個分量,對于類別型特征, 非 0 即 1。
由上面分析可知,每個輸入特征都有對應的 參數向量對應。
Deep 部分
這部分更容易理解了,就是個 DNN 網絡,模型輸入為上圖中的 Dense_Embeddings:
注意:FM 與 Deep 部分共享輸入的 embedding feature,也就是他們共同影響 Dense_Embeddings。
代碼分析
這部分參考的是 tensorflow-DeepFM[3]
數據預處理
該部分對數據集中特征進行了編號,一個連續特征用一個編號,類別特征不同取值用不同編號
def?gen_feat_dict(self):if?self.dfTrain?is?None:dfTrain?=?pd.read_csv(self.trainfile)else:dfTrain?=?self.dfTrainif?self.dfTest?is?None:dfTest?=?pd.read_csv(self.testfile)else:dfTest?=?self.dfTestdf?=?pd.concat([dfTrain,?dfTest])self.feat_dict?=?{}tc?=?0for?col?in?df.columns:if?col?in?self.ignore_cols:continueif?col?in?self.numeric_cols:#?map?to?a?single?indexself.feat_dict[col]?=?tctc?+=?1else:us?=?df[col].unique()self.feat_dict[col]?=?dict(zip(us,?range(tc,?len(us)+tc)))tc?+=?len(us)self.feat_dim?=?tc由上述代碼可以看出 feat_dim 就是我們前面定義的 feature_size
?def?parse(self,?infile=None,?df=None,?has_label=False):assert?not?((infile?is?None)?and?(df?is?None)),?"infile?or?df?at?least?one?is?set"assert?not?((infile?is?not?None)?and?(df?is?not?None)),?"only?one?can?be?set"if?infile?is?None:dfi?=?df.copy()else:dfi?=?pd.read_csv(infile)if?has_label:y?=?dfi["target"].values.tolist()dfi.drop(["id",?"target"],?axis=1,?inplace=True)else:ids?=?dfi["id"].values.tolist()dfi.drop(["id"],?axis=1,?inplace=True)#?dfi?for?feature?index#?dfv?for?feature?value?which?can?be?either?binary?(1/0)?or?float?(e.g.,?10.24)dfv?=?dfi.copy()for?col?in?dfi.columns:if?col?in?self.feat_dict.ignore_cols:dfi.drop(col,?axis=1,?inplace=True)dfv.drop(col,?axis=1,?inplace=True)continueif?col?in?self.feat_dict.numeric_cols:dfi[col]?=?self.feat_dict.feat_dict[col]else:dfi[col]?=?dfi[col].map(self.feat_dict.feat_dict[col])dfv[col]?=?1.#?list?of?list?of?feature?indices?of?each?sample?in?the?datasetXi?=?dfi.values.tolist()#?list?of?list?of?feature?values?of?each?sample?in?the?datasetXv?=?dfv.values.tolist()if?has_label:return?Xi,?Xv,?yelse:return?Xi,?Xv,?ids由上面代碼可以看出:dfi 表示特征的編號,對于一個類別特征,不同取值其編號不同;dfv 表示該特征值,對于數值型特征就是該值本身,類別特征全是 1(表示取到了該編號的類別值)。
定義 DeepFM 模型超參數
class?DeepFM(BaseEstimator,?TransformerMixin):def?__init__(self,?feature_size,?field_size,embedding_size=8,?dropout_fm=[1.0,?1.0],deep_layers=[32,?32],?dropout_deep=[0.5,?0.5,?0.5],deep_layers_activation=tf.nn.relu,epoch=10,?batch_size=256,learning_rate=0.001,?optimizer_type="adam",batch_norm=0,?batch_norm_decay=0.995,verbose=False,?random_seed=2016,use_fm=True,?use_deep=True,loss_type="logloss",?eval_metric=roc_auc_score,l2_reg=0.0,?greater_is_better=True):assert?(use_fm?or?use_deep)assert?loss_type?in?["logloss",?"mse"],?\"loss_type?can?be?either?'logloss'?for?classification?task?or?'mse'?for?regression?task"self.feature_size?=?feature_size????????#?M=數值型特征個數+類別型特征取值個數,就是feat_dimself.field_size?=?field_size????????????#?F=特征個數self.embedding_size?=?embedding_size????#?K=embedding_sizeself.dropout_fm?=?dropout_fmself.deep_layers?=?deep_layersself.dropout_deep?=?dropout_deepself.deep_layers_activation?=?deep_layers_activationself.use_fm?=?use_fmself.use_deep?=?use_deepself.l2_reg?=?l2_regself.epoch?=?epochself.batch_size?=?batch_sizeself.learning_rate?=?learning_rateself.optimizer_type?=?optimizer_typeself.batch_norm?=?batch_normself.batch_norm_decay?=?batch_norm_decayself.verbose?=?verboseself.random_seed?=?random_seedself.loss_type?=?loss_typeself.eval_metric?=?eval_metricself.greater_is_better?=?greater_is_betterself.train_result,?self.valid_result?=?[],?[]self._init_graph()構圖
feature_bias:shape 為 的一階參數矩陣 feature_embeddings:shape 為 的二階參數矩陣
def?_init_graph(self):self.graph?=?tf.Graph()with?self.graph.as_default():tf.set_random_seed(self.random_seed)self.feat_index?=?tf.placeholder(tf.int32,?shape=[None,?None],name="feat_index")??#?None?*?Fself.feat_value?=?tf.placeholder(tf.float32,?shape=[None,?None],name="feat_value")??#?None?*?Fself.label?=?tf.placeholder(tf.float32,?shape=[None,?1],?name="label")??#?None?*?1self.dropout_keep_fm?=?tf.placeholder(tf.float32,?shape=[None],?name="dropout_keep_fm")self.dropout_keep_deep?=?tf.placeholder(tf.float32,?shape=[None],?name="dropout_keep_deep")self.train_phase?=?tf.placeholder(tf.bool,?name="train_phase")self.weights?=?self._initialize_weights()#?modelself.embeddings?=?tf.nn.embedding_lookup(self.weights["feature_embeddings"],self.feat_index)??#?None?*?F?*?Kfeat_value?=?tf.reshape(self.feat_value,?shape=[-1,?self.field_size,?1])self.embeddings?=?tf.multiply(self.embeddings,?feat_value)?##?供下面的FM和Deep部分使用#?----------?first?order?term?----------self.y_first_order?=?tf.nn.embedding_lookup(self.weights["feature_bias"],?self.feat_index)?#?None?*?F?*?1self.y_first_order?=?tf.reduce_sum(tf.multiply(self.y_first_order,?feat_value),?2)??#?None?*?Fself.y_first_order?=?tf.nn.dropout(self.y_first_order,?self.dropout_keep_fm[0])?#?None?*?F#?----------?second?order?term?---------------#?sum_square?partself.summed_features_emb?=?tf.reduce_sum(self.embeddings,?1)??#?None?*?Kself.summed_features_emb_square?=?tf.square(self.summed_features_emb)??#?None?*?K#?square_sum?partself.squared_features_emb?=?tf.square(self.embeddings)self.squared_sum_features_emb?=?tf.reduce_sum(self.squared_features_emb,?1)??#?None?*?K#?second?orderself.y_second_order?=?0.5?*?tf.subtract(self.summed_features_emb_square,?self.squared_sum_features_emb)??#?None?*?Kself.y_second_order?=?tf.nn.dropout(self.y_second_order,?self.dropout_keep_fm[1])??#?None?*?K#?----------?Deep?component?----------self.y_deep?=?tf.reshape(self.embeddings,?shape=[-1,?self.field_size?*?self.embedding_size])?#?None?*?(F*K)self.y_deep?=?tf.nn.dropout(self.y_deep,?self.dropout_keep_deep[0])for?i?in?range(0,?len(self.deep_layers)):self.y_deep?=?tf.add(tf.matmul(self.y_deep,?self.weights["layer_%d"?%i]),?self.weights["bias_%d"%i])?#?None?*?layer[i]?*?1if?self.batch_norm:self.y_deep?=?self.batch_norm_layer(self.y_deep,?train_phase=self.train_phase,?scope_bn="bn_%d"?%i)?#?None?*?layer[i]?*?1self.y_deep?=?self.deep_layers_activation(self.y_deep)self.y_deep?=?tf.nn.dropout(self.y_deep,?self.dropout_keep_deep[1+i])?#?dropout?at?each?Deep?layer#?----------?DeepFM?----------if?self.use_fm?and?self.use_deep:concat_input?=?tf.concat([self.y_first_order,?self.y_second_order,?self.y_deep],?axis=1)elif?self.use_fm:concat_input?=?tf.concat([self.y_first_order,?self.y_second_order],?axis=1)elif?self.use_deep:concat_input?=?self.y_deepself.out?=?tf.add(tf.matmul(concat_input,?self.weights["concat_projection"]),?self.weights["concat_bias"])這里需要注意:本實現代碼中,「也對連續特征也直接做了 embedding」,用的時候也可以把連續特征改為 deep 側的直接輸入,另外針對多值離散特征這里也沒有處理。
總結
該方法將 FM(捕捉低級特征)和 Deep(捕捉高級特征)進行端到端的聯合訓練,并且共享輸入 embedding,這是現在非常常見的做法。
論文講到該方法可以一定程度避免人工特征工程,從模型看的確做到了無腦交叉,模型自動學習各種交叉的權重。
Reference
[1]
deepfm: https://arxiv.org/abs/1703.04247
[2]tensorflow-DeepFM: https://github.com/ChenglongChen/tensorflow-DeepFM
[3]tensorflow-DeepFM: https://github.com/ChenglongChen/tensorflow-DeepFM
往期精彩回顧適合初學者入門人工智能的路線及資料下載機器學習及深度學習筆記等資料打印機器學習在線手冊深度學習筆記專輯《統計學習方法》的代碼復現專輯 AI基礎下載機器學習的數學基礎專輯 獲取本站知識星球優惠券,復制鏈接直接打開: https://t.zsxq.com/qFiUFMV 本站qq群704220115。加入微信群請掃碼:總結
以上是生活随笔為你收集整理的【论文解读】DeepFM论文总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python基础】Python十大文件
- 下一篇: 精通C++之前,你必须知道的几件事!