推薦系統(tǒng)遇上深度學習(三)--DeepFM模型理論和實踐轉載

原理鏈接

推薦系統(tǒng)遇上深度學習(三)--DeepFM模型理論和實踐

推薦系統(tǒng)遇上深度學習系列:
推薦系統(tǒng)遇上深度學習(一)--FM模型理論和實踐:http://www.reibang.com/p/152ae633fb00
推薦系統(tǒng)遇上深度學習(二)--FFM模型理論和實踐:http://www.reibang.com/p/781cde3d5f3d

文章目錄

1、背景

特征組合的挑戰(zhàn)
對于一個基于CTR預估的推薦系統(tǒng),最重要的是學習到用戶點擊行為背后隱含的特征組合眠副。在不同的推薦場景中,低階組合特征或者高階組合特征可能都會對最終的CTR產(chǎn)生影響竣稽。

之前介紹的因子分解機(Factorization Machines, FM)通過對于每一維特征的隱變量內積來提取特征組合囱怕。最終的結果也非常好。但是毫别,雖然理論上來講FM可以對高階特征組合進行建模娃弓,但實際上因為計算復雜度的原因一般都只用到了二階特征組合。

那么對于高階的特征組合來說岛宦,我們很自然的想法台丛,通過多層的神經(jīng)網(wǎng)絡即DNN去解決。

DNN的局限
下面的圖片來自于張俊林教授在AI大會上所使用的PPT砾肺。

我們之前也介紹過了挽霉,對于離散特征的處理防嗡,我們使用的是將特征轉換成為one-hot的形式,但是將One-hot類型的特征輸入到DNN中侠坎,會導致網(wǎng)絡參數(shù)太多:

image

如何解決這個問題呢蚁趁,類似于FFM中的思想,將特征分為不同的field:

image

再加兩層的全鏈接層硅蹦,讓Dense Vector進行組合荣德,那么高階特征的組合就出來了

image

但是低階和高階特征組合隱含地體現(xiàn)在隱藏層中,如果我們希望把低階特征組合單獨建模童芹,然后融合高階特征組合涮瞻。

image

即將DNN與FM進行一個合理的融合:

image

二者的融合總的來說有兩種形式,一是串行結構假褪,二是并行結構

image
image

而我們今天要講到的DeepFM署咽,就是并行結構中的一種典型代表。

2生音、DeepFM模型

我們先來看一下DeepFM的模型結構:

image

DeepFM包含兩部分:神經(jīng)網(wǎng)絡部分與因子分解機部分宁否,分別負責低階特征的提取和高階特征的提取。這兩部分共享同樣的輸入缀遍。DeepFM的預測結果可以寫為:

image

FM部分

FM部分的詳細結構如下:

image

FM部分是一個因子分解機慕匠。關于因子分解機可以參閱文章[Rendle, 2010] Steffen Rendle. Factorization machines. In ICDM, 2010.。因為引入了隱變量的原因域醇,對于幾乎不出現(xiàn)或者很少出現(xiàn)的隱變量台谊,F(xiàn)M也可以很好的學習。

FM的輸出公式為:

image

深度部分

image

深度部分是一個前饋神經(jīng)網(wǎng)絡譬挚。與圖像或者語音這類輸入不同锅铅,圖像語音的輸入一般是連續(xù)而且密集的,然而用于CTR的輸入一般是及其稀疏的减宣。因此需要重新設計網(wǎng)絡結構盐须。具體實現(xiàn)中為,在第一層隱含層之前漆腌,引入一個嵌入層來完成將輸入向量壓縮到低維稠密向量贼邓。

image

嵌入層(embedding layer)的結構如上圖所示。當前網(wǎng)絡結構有兩個有趣的特性闷尿,1)盡管不同field的輸入長度不同塑径,但是embedding之后向量的長度均為K。2)在FM里得到的隱變量Vik現(xiàn)在作為了嵌入層網(wǎng)絡的權重悠砚。

這里的第二點如何理解呢晓勇,假設我們的k=5堂飞,首先灌旧,對于輸入的一條記錄绑咱,同一個field 只有一個位置是1,那么在由輸入得到dense vector的過程中枢泰,輸入層只有一個神經(jīng)元起作用描融,得到的dense vector其實就是輸入層到embedding層該神經(jīng)元相連的五條線的權重,即vi1衡蚂,vi2窿克,vi3,vi4毛甲,vi5年叮。這五個值組合起來就是我們在FM中所提到的Vi。在FM部分和DNN部分玻募,這一塊是共享權重的只损,對同一個特征來說,得到的Vi是相同的七咧。

有關模型具體如何操作跃惫,我們可以通過代碼來進一步加深認識。

3艾栋、相關知識

我們先來講兩個代碼中會用到的相關知識吧爆存,代碼是參考的github上星數(shù)最多的DeepFM實現(xiàn)代碼。

Gini Normalization
代碼中將CTR預估問題設定為一個二分類問題蝗砾,繪制了Gini Normalization來評價不同模型的效果先较。這個是什么東西,不太懂遥诉,百度了很多拇泣,發(fā)現(xiàn)了一個比較通俗易懂的介紹。

假設我們有下面兩組結果矮锈,分別表示預測值和實際值:

predictions = [0.9, 0.3, 0.8, 0.75, 0.65, 0.6, 0.78, 0.7, 0.05, 0.4, 0.4, 0.05, 0.5, 0.1, 0.1]actual = [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]

然后我們將預測值按照從小到大排列霉翔,并根據(jù)索引序對實際值進行排序:

Sorted Actual Values [0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1]

然后,我們可以畫出如下的圖片:

image

接下來我們將數(shù)據(jù)Normalization到0苞笨,1之間债朵。并畫出45度線。

image

橙色區(qū)域的面積瀑凝,就是我們得到的Normalization的Gini系數(shù)序芦。

這里,由于我們是將預測概率從小到大排的粤咪,所以我們希望實際值中的0盡可能出現(xiàn)在前面谚中,因此Normalization的Gini系數(shù)越大,分類效果越好。

embedding_lookup
在tensorflow中有個embedding_lookup函數(shù)宪塔,我們可以直接根據(jù)一個序號來得到一個詞或者一個特征的embedding值磁奖,那么他內部其實是包含一個網(wǎng)絡結構的,如下圖所示:

image

假設我們想要找到2的embedding值某筐,這個值其實是輸入層第二個神經(jīng)元與embedding層連線的權重值比搭。

之前有大佬跟我探討word2vec輸入的問題,現(xiàn)在也算是有個比較明確的答案南誊,輸入其實就是one-hot Embedding身诺,而word2vec要學習的是new Embedding。

4抄囚、代碼解析

好霉赡,一貫的風格,先來介紹幾個地址:
原代碼地址:https://github.com/ChenglongChen/tensorflow-DeepFM
本文代碼地址:https://github.com/princewen/tensorflow_practice/tree/master/Basic-DeepFM-model
數(shù)據(jù)下載地址:https://www.kaggle.com/c/porto-seguro-safe-driver-prediction

好了幔托,話不多說同廉,我們來看看代碼目錄吧,接下來柑司,我們將主要對網(wǎng)絡的構建進行介紹迫肖,而對數(shù)據(jù)的處理,流程的控制部分攒驰,相信大家根據(jù)代碼就可以看懂蟆湖。

項目結構
項目結構如下:

image

其實還應該有一個存放data的路徑。config.py保存了我們模型的一些配置玻粪。DataReader對數(shù)據(jù)進行處理隅津,得到模型可以使用的輸入。DeepFM是我們構建的模型劲室。main是項目的入口伦仍。metrics是計算normalized gini系數(shù)的代碼。

模型輸入

模型的輸入主要有下面幾個部分:

self.feat_index = tf.placeholder(tf.int32,                                 shape=[None,None],                                 name='feat_index')self.feat_value = tf.placeholder(tf.float32,                               shape=[None,None],                               name='feat_value')self.label = tf.placeholder(tf.float32,shape=[None,1],name='label')self.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_deep_deep')

feat_index是特征的一個序號很洋,主要用于通過embedding_lookup選擇我們的embedding充蓝。feat_value是對應的特征值,如果是離散特征的話喉磁,就是1谓苟,如果不是離散特征的話,就保留原來的特征值协怒。label是實際值涝焙。還定義了兩個dropout來防止過擬合。

權重構建
權重的設定主要有兩部分孕暇,第一部分是從輸入到embedding中的權重仑撞,其實也就是我們的dense vector赤兴。另一部分就是深度神經(jīng)網(wǎng)絡每一層的權重。第二部分很好理解隧哮,我們主要來看看第一部分:

#embeddingsweights['feature_embeddings'] = tf.Variable(    tf.random_normal([self.feature_size,self.embedding_size],0.0,0.01),    name='feature_embeddings')weights['feature_bias'] = tf.Variable(tf.random_normal([self.feature_size,1],0.0,1.0),name='feature_bias')

weights['feature_embeddings'] 存放的每一個值其實就是FM中的vik搀缠,所以它是N * F * K的。其中N代表數(shù)據(jù)量的大小近迁,F(xiàn)代表feture的大小(將離散特征轉換成one-hot之后的特征總量),K代表dense vector的大小。

weights['feature_bias']是FM中的一次項的權重簸州。

Embedding part
這個部分很簡單啦鉴竭,是根據(jù)feat_index選擇對應的weights['feature_embeddings']中的embedding值,然后再與對應的feat_value相乘就可以了:

# modelself.embeddings = tf.nn.embedding_lookup(self.weights['feature_embeddings'],self.feat_index) # N * F * Kfeat_value = tf.reshape(self.feat_value,shape=[-1,self.field_size,1])self.embeddings = tf.multiply(self.embeddings,feat_value)

FM part
首先來回顧一下我們之前對FM的化簡公式岸浑,之前去今日頭條面試還問到過公式的推導搏存。

image

所以我們的二次項可以根據(jù)化簡公式輕松的得到,再加上我們的一次項矢洲,F(xiàn)M的part就算完了璧眠。同時更為方便的是,由于權重共享读虏,我們這里可以直接用Embedding part計算出的embeddings來得到我們的二次項:

# first order termself.y_first_order = tf.nn.embedding_lookup(self.weights['feature_bias'],self.feat_index)self.y_first_order = tf.reduce_sum(tf.multiply(self.y_first_order,feat_value),2)self.y_first_order = tf.nn.dropout(self.y_first_order,self.dropout_keep_fm[0])# 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# squre-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)self.y_second_order = tf.nn.dropout(self.y_second_order,self.dropout_keep_fm[1])

DNN part
DNNpart的話责静,就是將Embedding part的輸出再經(jīng)過幾層全鏈接層:

# Deep componentself.y_deep = tf.reshape(self.embeddings,shape=[-1,self.field_size * self.embedding_size])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])    self.y_deep = self.deep_layers_activation(self.y_deep)    self.y_deep = tf.nn.dropout(self.y_deep,self.dropout_keep_deep[i+1])

最后,我們要將DNN和FM兩部分的輸出進行結合:

concat_input = tf.concat([self.y_first_order, self.y_second_order, self.y_deep], axis=1)

損失及優(yōu)化器
我們可以使用logloss(如果定義為分類問題)盖桥,或者mse(如果定義為預測問題)灾螃,以及多種的優(yōu)化器去進行嘗試,這些根據(jù)不同的參數(shù)設定得到:

# lossif self.loss_type == "logloss":    self.out = tf.nn.sigmoid(self.out)    self.loss = tf.losses.log_loss(self.label, self.out)elif self.loss_type == "mse":    self.loss = tf.nn.l2_loss(tf.subtract(self.label, self.out))# l2 regularization on weightsif self.l2_reg > 0:    self.loss += tf.contrib.layers.l2_regularizer(        self.l2_reg)(self.weights["concat_projection"])    if self.use_deep:        for i in range(len(self.deep_layers)):            self.loss += tf.contrib.layers.l2_regularizer(                self.l2_reg)(self.weights["layer_%d" % I])if self.optimizer_type == "adam":    self.optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate, beta1=0.9, beta2=0.999,                                            epsilon=1e-8).minimize(self.loss)elif self.optimizer_type == "adagrad":    self.optimizer = tf.train.AdagradOptimizer(learning_rate=self.learning_rate,                                               initial_accumulator_value=1e-8).minimize(self.loss)elif self.optimizer_type == "gd":    self.optimizer = tf.train.GradientDescentOptimizer(learning_rate=self.learning_rate).minimize(self.loss)elif self.optimizer_type == "momentum":    self.optimizer = tf.train.MomentumOptimizer(learning_rate=self.learning_rate, momentum=0.95).minimize(        self.loss)

模型效果
前面提到了揩徊,我們用logloss作為損失函數(shù)去進行模型的參數(shù)更新腰鬼,但是代碼中輸出了模型的 Normalization 的 Gini值來進行模型評價,我們可以對比一下(記住塑荒,Gini值越大越好呦):

image

好啦熄赡,本文只是提供一個引子,有關DeepFM更多的知識大家可以更多的進行學習呦齿税。

參考資料

1彼硫、http://www.360doc.com/content/17/0315/10/10408243_637001469.shtml
2、https://blog.csdn.net/u010665216/article/details/78528261

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末凌箕,一起剝皮案震驚了整個濱河市乌助,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌陌知,老刑警劉巖他托,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仆葡,居然都是意外死亡赏参,警方通過查閱死者的電腦和手機志笼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來把篓,“玉大人纫溃,你說我怎么就攤上這事∪脱冢” “怎么了紊浩?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疗锐。 經(jīng)常有香客問我坊谁,道長,這世上最難降的妖魔是什么滑臊? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任口芍,我火速辦了婚禮,結果婚禮上雇卷,老公的妹妹穿的比我還像新娘鬓椭。我一直安慰自己,他們只是感情好关划,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布小染。 她就那樣靜靜地躺著,像睡著了一般贮折。 火紅的嫁衣襯著肌膚如雪氧映。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天脱货,我揣著相機與錄音岛都,去河邊找鬼。 笑死振峻,一個胖子當著我的面吹牛臼疫,可吹牛的內容都是我干的。 我是一名探鬼主播扣孟,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼烫堤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凤价?” 一聲冷哼從身側響起鸽斟,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎利诺,沒想到半個月后富蓄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡慢逾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年立倍,在試婚紗的時候發(fā)現(xiàn)自己被綠了灭红。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡口注,死狀恐怖变擒,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情寝志,我是刑警寧澤娇斑,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站材部,受9級特大地震影響毫缆,放射性物質發(fā)生泄漏。R本人自食惡果不足惜败富,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摩窃。 院中可真熱鬧兽叮,春花似錦、人聲如沸猾愿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒂秘。三九已至泽本,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姻僧,已是汗流浹背规丽。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留撇贺,地道東北人赌莺。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像松嘶,于是被迫代替她去往敵國和親艘狭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內容