深度學(xué)習(xí)推薦系統(tǒng)-NFM

NFM

0.結(jié)論

  • NFM是FM的神經(jīng)網(wǎng)絡(luò)化嘗試
  • NFM用神經(jīng)網(wǎng)絡(luò)代替FM中二階隱向量交叉的操作了赵,相比于FM竞穷,NFM的表達(dá)能力和特征交叉能力更強
  • 局限性在于結(jié)構(gòu)與PNN近似孕惜,特征工程層面的優(yōu)化嘗試幾乎窮盡,模型進(jìn)一步提升的空間非常小

1. 動機

NFM(Neural Factorization Machines)是2017年由新加坡國立大學(xué)的何向南教授等人在SIGIR會議上提出的一個模型戒洼,傳統(tǒng)的FM模型僅局限于線性表達(dá)和二階交互燕垃, 無法勝任生活中各種具有復(fù)雜結(jié)構(gòu)和規(guī)律性的真實數(shù)據(jù)枢劝, 針對FM的這點不足, 作者提出了一種將FM融合進(jìn)DNN的策略利术,通過引進(jìn)了一個特征交叉池化層的結(jié)構(gòu),使得FM與DNN進(jìn)行了完美銜接低矮,這樣就組合了FM的建模低階特征交互能力和DNN學(xué)習(xí)高階特征交互和非線性的能力印叁,形成了深度學(xué)習(xí)時代的神經(jīng)FM模型(NFM)。

那么NFM具體是怎么做的呢军掂? 首先看一下NFM的公式:


我們對比FM轮蜕, 就會發(fā)現(xiàn)變化的是第三項,前兩項還是原來的蝗锥, 因為我們說FM的一個問題跃洛,就是只能到二階交叉, 且是線性模型终议, 這是他本身的一個局限性汇竭, 而如果想突破這個局限性, 就需要從他的公式本身下點功夫穴张, 于是乎细燎,作者在這里改進(jìn)的思路就是用一個表達(dá)能力更強的函數(shù)來替代原FM中二階隱向量內(nèi)積的部分

image

而這個表達(dá)能力更強的函數(shù)呢皂甘, 我們很容易就可以想到神經(jīng)網(wǎng)絡(luò)來充當(dāng)玻驻,因為神經(jīng)網(wǎng)絡(luò)理論上可以擬合任何復(fù)雜能力的函數(shù), 所以作者真的就把這個f(x)換成了一個神經(jīng)網(wǎng)絡(luò)偿枕,當(dāng)然不是一個簡單的DNN璧瞬, 而是依然底層考慮了交叉户辫,然后高層使用的DNN網(wǎng)絡(luò), 這個也就是我們最終的NFM網(wǎng)絡(luò)了:

image

這個結(jié)構(gòu)嗤锉,如果前面看過了PNN的伙伴會發(fā)現(xiàn)渔欢,這個結(jié)構(gòu)和PNN非常像,只不過那里是一個product_layer档冬, 而這里換成了Bi-Interaction Pooling了膘茎, 這個也是NFM的核心結(jié)構(gòu)了。這里注意酷誓, 這個結(jié)構(gòu)中披坏,忽略了一階部分,只可視化出來了f(x)盐数, 我們還是下面從底層一點點的對這個網(wǎng)絡(luò)進(jìn)行剖析棒拂。

2. 模型結(jié)構(gòu)與原理

2.1 Input 和Embedding層

輸入層的特征為了方便,文章指定了稀疏離散特征(當(dāng)然在實際場景的應(yīng)用中玫氢,分為數(shù)值特征與分類特征)帚屉。Embedding層(在該模型中其實就是一個全連接層)將高維的稀疏特征轉(zhuǎn)化為低維的密集特征表示。

2.2 Bi-Interaction Pooling layer

在Embedding層和神經(jīng)網(wǎng)絡(luò)之間加入了特征交叉池化層是本網(wǎng)絡(luò)的核心創(chuàng)新了漾峡,正是因為這個結(jié)構(gòu)攻旦,實現(xiàn)了FM與DNN的無縫連接, 組成了一個大的網(wǎng)絡(luò)生逸,且能夠正常的反向傳播牢屋。假設(shè)\mathcal{V}_{x}是所有特征embedding的集合, 那么在特征交叉池化層的操作:

f _{B I}\left(\mathcal{V}_{x}\right)=\sum_{i=1}^{n} \sum_{j=i+1}^{n} x_{i} \mathbf{v}_{i} \odot x_{j} \mathbf{v}_{j}

\odot表示兩個向量的元素積操作槽袄,即兩個向量對應(yīng)維度相乘得到的元素積向量(可不是點乘呀)烙无,其中第k維的操作:
\left(v_{i} \odot v_{j}\right)_{k}=\boldsymbol{v}_{i k} \boldsymbol{v}_{j k}

這便定義了在embedding空間特征的二階交互,這個不仔細(xì)看會和感覺FM的最后一項很像遍尺,但是不一樣截酷,一定要注意這個地方不是兩個隱向量的內(nèi)積,而是元素積乾戏,也就是這一個交叉完了之后k個維度不求和迂苛,最后會得到一個k維向量,而FM那里內(nèi)積的話最后得到一個數(shù)鼓择, 在進(jìn)行兩兩Embedding元素積之后灾部,對交叉特征向量取和, 得到該層的輸出向量惯退, 很顯然赌髓, 輸出是一個k維的向量。

注意, 之前的FM到這里其實就完事了锁蠕, 上面就是輸出了夷野,而這里很大的一點改進(jìn)就是加入特征池化層之后, 把二階交互的信息合并荣倾, 且上面接了一個DNN網(wǎng)絡(luò)悯搔, 這樣就能夠增強FM的表達(dá)能力了, 因為FM只能到二階舌仍, 而這里的DNN可以進(jìn)行多階且非線性妒貌,只要FM把二階的學(xué)習(xí)好了, DNN這塊學(xué)習(xí)來會更加容易铸豁, 作者在論文中也說明了這一點灌曙,且通過后面的實驗證實了這個觀點。

如果不加DNN节芥, NFM就退化成了FM在刺,所以改進(jìn)的關(guān)鍵就在于加了一個這樣的層,組合了一下二階交叉的信息头镊,然后又給了DNN進(jìn)行高階交叉的學(xué)習(xí)蚣驼,成了一種“加強版”的FM。

Bi-Interaction層不需要額外的模型學(xué)習(xí)參數(shù)相艇,更重要的是它在一個線性的時間內(nèi)完成計算颖杏,和FM一致的,即時間復(fù)雜度為O\left(k N_{x}\right)坛芽,N_x為embedding向量的數(shù)量留储。

2.3 隱藏層

即由多個全連接層構(gòu)成。不過在此之前靡馁,作者還采用了Dropout****和Batch Normalization欲鹏,原因如下:

Dropout:是神經(jīng)網(wǎng)絡(luò)的正則化技術(shù)机久,為了防止過擬合臭墨;

Batch Normalization:該層是對輸入的每個小批量(min-batch)標(biāo)準(zhǔn)化為零均值的單位方差的高斯分布(zero-mean unit-variance Gaussian distribution)。作者使用BN膘盖,是為了避免embedding向量的更新將輸入層的分布更改為隱藏層或輸出層胧弛;

2.4 預(yù)測層

通過邏輯回歸將隱藏層的向量轉(zhuǎn)變?yōu)樽詈箢A(yù)測的結(jié)果。
NFM相比較于其他模型的核心創(chuàng)新點是特征交叉池化層侠畔,基于它结缚,實現(xiàn)了FM和DNN的無縫連接,使得DNN可以在底層就學(xué)習(xí)到包含更多信息的組合特征软棺,這時候红竭,就會減少DNN的很多負(fù)擔(dān),只需要很少的隱藏層就可以學(xué)習(xí)到高階特征信息。NFM相比之前的DNN茵宪, 模型結(jié)構(gòu)更淺最冰,更簡單,但是性能更好稀火,訓(xùn)練和調(diào)參更容易暖哨。集合FM二階交叉線性和DNN高階交叉非線性的優(yōu)勢,非常適合處理稀疏數(shù)據(jù)的場景任務(wù)凰狞。在對NFM的真實訓(xùn)練過程中篇裁,也會用到像Dropout和BatchNormalization這樣的技術(shù)來緩解過擬合和在過大的改變數(shù)據(jù)分布。

下面通過代碼看下NFM的具體實現(xiàn)過程赡若, 學(xué)習(xí)一些細(xì)節(jié)达布。

3. 代碼實現(xiàn)

下面我們看下NFM的代碼復(fù)現(xiàn),這里主要是給大家說一下這個模型的設(shè)計邏輯斩熊,參考了deepctr的函數(shù)API的編程風(fēng)格往枣, 具體的代碼以及示例大家可以去參考后面的GitHub,里面已經(jīng)給出了詳細(xì)的注釋粉渠, 這里主要分析模型的邏輯這塊分冈。關(guān)于函數(shù)API的編程式風(fēng)格,我們還給出了一份文檔霸株, 大家可以先看這個雕沉,再看后面的代碼部分,會更加舒服些去件。下面開始:

這里主要說一下NFM模型的總體運行邏輯坡椒, 這樣可以讓大家從宏觀的層面去把握模型的設(shè)計過程躏尉, 該模型所使用的數(shù)據(jù)集是criteo數(shù)據(jù)集涯保,具體介紹參考后面的GitHub。 數(shù)據(jù)集的特征會分為dense特征(連續(xù))和sparse特征(離散)空郊, 所以模型的輸入層接收這兩種輸入宫莱。但是我們這里把輸入分成了linear input和dnn input兩種情況丈攒,而每種情況都有可能包含上面這兩種輸入。因為我們后面的模型邏輯會分這兩部分走授霸,這里有個細(xì)節(jié)要注意巡验,就是光看上面那個NFM模型的話,是沒有看到它線性特征處理的那部分的碘耳,也就是FM的前半部分公式那里圖里面是沒有的显设。但是這里我們要加上。


所以模型的邏輯我們分成了兩大部分辛辨,這里我分別給大家解釋下每一塊做了什么事情:

  1. linear part: 這部分是有關(guān)于線性計算捕捂,也就是FM的前半部分w1x1+w2x2...wnxn+b的計算瑟枫。對于這一塊的計算,我們用了一個get_linear_logits函數(shù)實現(xiàn)指攒,后面再說力奋,總之通過這個函數(shù),我們就可以實現(xiàn)上面這個公式的計算過程幽七,得到linear的輸出
  2. dnn part: 這部分是后面交叉特征的那部分計算景殷,F(xiàn)M的最后那部分公式f(x)。 這一塊主要是針對離散的特征澡屡,首先過embedding猿挚, 然后過特征交叉池化層,這個計算我們用了get_bi_interaction_pooling_output函數(shù)實現(xiàn)驶鹉, 得到輸出之后又過了DNN網(wǎng)絡(luò)绩蜻,最后得到dnn的輸出

模型的最后輸出結(jié)果,就是把這兩個部分的輸出結(jié)果加和(當(dāng)然也可以加權(quán))室埋,再過一個sigmoid得到办绝。所以NFM的模型定義就出來了:

def NFM(linear_feature_columns, dnn_feature_columns):
    """
    搭建NFM模型,上面已經(jīng)把所有組塊都寫好了姚淆,這里拼起來就好
    :param linear_feature_columns: A list. 里面的每個元素是namedtuple(元組的一種擴(kuò)展類型孕蝉,同時支持序號和屬性名訪問組件)類型,表示的是linear數(shù)據(jù)的特征封裝版
    :param dnn_feature_columns: A list. 里面的每個元素是namedtuple(元組的一種擴(kuò)展類型腌逢,同時支持序號和屬性名訪問組件)類型降淮,表示的是DNN數(shù)據(jù)的特征封裝版
    """
    # 構(gòu)建輸入層,即所有特征對應(yīng)的Input()層搏讶, 這里使用字典的形式返回佳鳖, 方便后續(xù)構(gòu)建模型
    # 構(gòu)建模型的輸入層,模型的輸入層不能是字典的形式媒惕,應(yīng)該將字典的形式轉(zhuǎn)換成列表的形式
    # 注意:這里實際的輸入與Input()層的對應(yīng)系吩,是通過模型輸入時候的字典數(shù)據(jù)的key與對應(yīng)name的Input層
    dense_input_dict, sparse_input_dict = build_input_layers(linear_feature_columns+dnn_feature_columns)
    input_layers = list(dense_input_dict.values()) + list(sparse_input_dict.values())
    
    # 線性部分的計算 w1x1 + w2x2 + ..wnxn + b部分,dense特征和sparse兩部分的計算結(jié)果組成妒蔚,具體看上面細(xì)節(jié)
    linear_logits = get_linear_logits(dense_input_dict, sparse_input_dict, linear_feature_columns)
    
    # DNN部分的計算
    # 首先穿挨,在這里構(gòu)建DNN部分的embedding層,之所以寫在這里面睛,是為了靈活的遷移到其他網(wǎng)絡(luò)上絮蒿,這里用字典的形式返回
    # embedding層用于構(gòu)建FM交叉部分以及DNN的輸入部分
    embedding_layers = build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False)
    
    # 過特征交叉池化層
    pooling_output = get_bi_interaction_pooling_output(sparse_input_dict, dnn_feature_columns, embedding_layers)
    
    # 加個BatchNormalization
    pooling_output = BatchNormalization()(pooling_output)
    
    # dnn部分的計算
    dnn_logits = get_dnn_logits(pooling_output)
    
    # 線性部分和dnn部分的結(jié)果相加尊搬,最后再過個sigmoid
    output_logits = Add()([linear_logits, dnn_logits])
    output_layers = Activation("sigmoid")(output_logits)
    
    model = Model(inputs=input_layers, outputs=output_layers)
    
    return model

有了上面的解釋叁鉴,這個模型的宏觀層面相信就很容易理解了。關(guān)于這每一塊的細(xì)節(jié)佛寿,這里就不解釋了幌墓,在我們給出的GitHub代碼中但壮,我們已經(jīng)加了非常詳細(xì)的注釋,大家看那個應(yīng)該很容易看明白常侣, 為了方便大家的閱讀蜡饵,我們這里還給大家畫了一個整體的模型架構(gòu)圖,幫助大家更好的了解每一塊以及前向傳播胳施。

image

下面是一個通過keras畫的模型結(jié)構(gòu)圖溯祸,為了更好的顯示,數(shù)值特征和類別特征都只是選擇了一小部分舞肆,畫圖的代碼也在github中焦辅。

nfm

4. 思考題

  1. NFM中的特征交叉與FM中的特征交叉有何異同,分別從原理和代碼實現(xiàn)上進(jìn)行對比分析
    答:
    從原理上來看:
    NFM則是將FM的二次交叉后的向量作為DNN的輸入,即引入了非線性變換來提升模型非線性表達(dá)能力椿胯,又學(xué)習(xí)到高階的組合特征筷登。

從代碼上來看

  • 采用特征pooling的方式代替DeepFM中二階特征向量橫向連接操作,得到的結(jié)果向量由n*k維優(yōu)化為k維哩盲,大大減少訓(xùn)練參數(shù)數(shù)目前方。(n為特征域個數(shù),k為embedding向量維度)
  • 采用sum pooling的方式綜合二階特征信息廉油,可能會有信息損失

FM layer樣例:

class FM_Layer(Layer):
    def __init__(self):
        super(FM_Layer, self).__init__()

    def call(self, inputs):
        # 優(yōu)化后的公式為: 0.5 * 求和(和的平方-平方的和)  =>> B x 1
        concated_embeds_value = inputs # B x n x k

        square_of_sum = tf.square(tf.reduce_sum(concated_embeds_value, axis=1, keepdims=True)) # B x 1 x k
        sum_of_square = tf.reduce_sum(concated_embeds_value * concated_embeds_value, axis=1, keepdims=True) # B x1 xk
        cross_term = square_of_sum - sum_of_square # B x 1 x k
        cross_term = 0.5 * tf.reduce_sum(cross_term, axis=2, keepdims=False) # B x 1

        return cross_term

    def compute_output_shape(self, input_shape):
        return (None, 1)

NFM Bi-Interaction Layer樣例

class BiInteractionPooling(Layer):
    def __init__(self):
        super(BiInteractionPooling, self).__init__()

    def call(self, inputs):
        # 優(yōu)化后的公式為: 0.5 * (和的平方-平方的和)  =>> B x k
        concated_embeds_value = inputs # B x n x k

        square_of_sum = tf.square(tf.reduce_sum(concated_embeds_value, axis=1, keepdims=False)) # B x k
        sum_of_square = tf.reduce_sum(concated_embeds_value * concated_embeds_value, axis=1, keepdims=False) # B x k
        cross_term = 0.5 * (square_of_sum - sum_of_square) # B x k

        return cross_term

    def compute_output_shape(self, input_shape):
        return (None, input_shape[2])

5. 參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抒线,隨后出現(xiàn)的幾起案子莺匠,更是在濱河造成了極大的恐慌,老刑警劉巖十兢,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趣竣,死亡現(xiàn)場離奇詭異,居然都是意外死亡旱物,警方通過查閱死者的電腦和手機遥缕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宵呛,“玉大人单匣,你說我怎么就攤上這事”λ耄” “怎么了户秤?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長逮矛。 經(jīng)常有香客問我鸡号,道長,這世上最難降的妖魔是什么须鼎? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任鲸伴,我火速辦了婚禮府蔗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘汞窗。我一直安慰自己姓赤,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布仲吏。 她就那樣靜靜地躺著不铆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裹唆。 梳的紋絲不亂的頭發(fā)上狂男,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音品腹,去河邊找鬼岖食。 笑死,一個胖子當(dāng)著我的面吹牛舞吭,可吹牛的內(nèi)容都是我干的泡垃。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼羡鸥,長吁一口氣:“原來是場噩夢啊……” “哼蔑穴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起惧浴,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤存和,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后衷旅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捐腿,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年柿顶,在試婚紗的時候發(fā)現(xiàn)自己被綠了茄袖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘁锯,死狀恐怖宪祥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情家乘,我是刑警寧澤蝗羊,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站仁锯,受9級特大地震影響耀找,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扑馁,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一涯呻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腻要,春花似錦复罐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至趟济,卻和暖如春乱投,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背顷编。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工戚炫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人媳纬。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓双肤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钮惠。 傳聞我的和親對象是個殘疾皇子茅糜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容