深度學(xué)習(xí)CTR預(yù)估(三)——FNM和FNN

DeepFM介紹過當(dāng)前ctr預(yù)估的深度模型有兩種結(jié)構(gòu),并行結(jié)構(gòu)和串行結(jié)構(gòu)白指,DeepFM是典型的并行結(jié)構(gòu)留晚,本文所介紹的FNN和FNM都是串行結(jié)構(gòu)

1、FNN原理

  • FNN采用了FM經(jīng)過預(yù)訓(xùn)練得到的隱含層和其權(quán)重(隱向量)作為DNN網(wǎng)絡(luò)的初始值
  • FNN是FM+MLP的結(jié)構(gòu)告嘲,但是并不是端到端的結(jié)構(gòu)倔丈,MLP的輸入是FM模型訓(xùn)練好之后的的向量作為MLP的輸入
    其結(jié)構(gòu)為:


    FNN結(jié)構(gòu).png

2、FNM原理

2.1 FM和FNM的比較

FNM模型在FM的二階交互的基礎(chǔ)上加入了一個DNN模型状蜗,F(xiàn)M的模型公式為:

FM公式.png

其中n為特征的個數(shù)需五,而其二階表達(dá)式為:
FM二階.png

觀察二階的推導(dǎo)公式,其中k為隱向量的維度轧坎,如果我們不把隱向量的維度進(jìn)行相加宏邮,那么二階特征組合輸出的結(jié)果就是一個k為的向量,而這個k維的向量就是FNM模型中DNN的輸入。
因此FNM的模型公式為
FNM公式.png

因?yàn)闆]有\sum^{k}_{f=1}的存在蜜氨,所以f(x)括號中就是NFM模型的二階特征交叉項(xiàng)械筛,是一個k維的向量。將這個k維向量輸入DNN結(jié)構(gòu)中就得到了FNM的二階交互的預(yù)測結(jié)果飒炎。

2.2 FNM的結(jié)構(gòu)

FNM結(jié)構(gòu).png

圖中顯示的是二階特征組合的DNN結(jié)構(gòu)埋哟,也就是f(x)的結(jié)構(gòu)模型。

  • 在二階特征交叉的時(shí)候使用交互池郎汪,論文中在交互池之后對數(shù)據(jù)做Batch Normalization處理赤赊。
  • 當(dāng)FNM的深層交互層為1的時(shí)候,也就是DNN沒有隱層煞赢,那么NFM就變成了FM
  • FNM和FNN的主要區(qū)別就是DNN層的輸入向量不同抛计,F(xiàn)NM的在深度層的輸入為兩兩特征向量元素相乘之后疊加,其維度跟特征向量的維度是一樣的照筑,而FNN輸入深度層的向量為特征向量的concatenate吹截,因此深度層的參數(shù)FNM會少很多。

3凝危、實(shí)驗(yàn)代碼

本次只實(shí)現(xiàn)了NFM的代碼波俄,根據(jù)DeepFM的代碼進(jìn)行修改程序,評測采用了RMSE

3.1 數(shù)據(jù)預(yù)處理

def get_feature_dict(df,num_col):
    '''
    特征向量字典蛾默,其格式為{field:{特征:編號}}
    :param df:
    :return: {field:{特征:編號}}
    '''
    feature_dict={}
    total_feature=0
    df.drop('rate',axis=1,inplace=True)
    for col in df.columns:
        if col in num_col:
            feature_dict[col]=total_feature
            total_feature += 1
        else:
            unique_feature = df[col].unique()
            feature_dict[col]=dict(zip(unique_feature,range(total_feature,total_feature+len(unique_feature))))
            total_feature += len(unique_feature)
    return feature_dict,total_feature

def get_data(df,feature_dict):
    '''

    :param df:
    :return:
    '''
    y = df[['rate']].values
    dd = df.drop('rate',axis=1)
    df_index = dd.copy()
    df_value = dd.copy()
    for col in df_index.columns:
        if col in num_col:
            df_index[col] = feature_dict[col]
        else:
            df_index[col] = df_index[col].map(feature_dict[col])
            df_value[col] = 1.0
    xi=df_index.values.tolist()
    xv=df_value.values.tolist()
    return xi,xv,y

3.2 NFM模型

模型實(shí)驗(yàn)過程:


FNM.png
3.2.1 設(shè)置權(quán)重初始化

根據(jù)上述公式原理弟断,NFM模型可以分成兩個部分,F(xiàn)M部分和DNN部分趴生,F(xiàn)M部分的權(quán)重有偏置項(xiàng)w_0阀趴,一階權(quán)重w,二階權(quán)重v苍匆;DNN部分為全連接層權(quán)重

 '''1刘急、權(quán)重初始化分為FM部分和Deep部分'''

    #FM權(quán)重
    w_0 = tf.Variable(tf.constant(0.1),name='bias')
    w = tf.Variable(tf.random_normal([feature_size, 1], mean=0, stddev=0.01),name='first_weight')
    v = tf.Variable(tf.random_normal([feature_size, embedding_size], mean=0, stddev=0.01),name='second_weight')
    #DeepLayer權(quán)重

    weights={}
    num_layer = len(deep_layers)
    input_size = embedding_size
    glorot = np.sqrt(2.0 / (input_size + deep_layers[0]))

    weights['layer_0'] = tf.Variable(
        np.random.normal(loc=0, scale=glorot, size=(input_size, deep_layers[0])), dtype=np.float32
    )
    weights['bias_0'] = tf.Variable(
        np.random.normal(loc=0, scale=glorot, size=(1, deep_layers[0])), dtype=np.float32
    )

    for i in range(1, num_layer):
        glorot = np.sqrt(2.0 / (deep_layers[i - 1] + deep_layers[i]))
        weights["layer_%d" % i] = tf.Variable(
            np.random.normal(loc=0, scale=glorot, size=(deep_layers[i - 1], deep_layers[i])),
            dtype=np.float32)  # layers[i-1] * layers[i]
        weights["bias_%d" % i] = tf.Variable(
            np.random.normal(loc=0, scale=glorot, size=(1, deep_layers[i])),
            dtype=np.float32)  # 1 * layer[i]
3.2.2 模型輸入和Embedding層
#輸入
feat_index = tf.placeholder(tf.int32,[None,None],name='feat_index')
feat_value = tf.placeholder(tf.float32,[None,None],name='feat_value')
label = tf.placeholder(tf.float32,shape=[None,1],name='label')
#Embedding Layer
embedding_first =tf.nn.embedding_lookup(w,feat_index)   #None*F *1   F是field_size大小,也就是不同域的個數(shù)
embedding = tf.nn.embedding_lookup(v,feat_index)      #None * F * embedding_size
feat_val = tf.reshape(feat_value,[-1,field_size,1])
3.2.3 模型輸入和Embedding層

1)一階和偏置項(xiàng)浸踩,得到的向量維度為None*1叔汁,None指的是輸入的樣本數(shù)

'''3、模型'''
    # first_order term +偏置
    y_first_order= tf.reduce_sum(tf.multiply(embedding_first,feat_val),2)  # None*F
    y_first_order_num = tf.reduce_sum(y_first_order,1,keepdims=True)    # None*1
    liner = tf.add(y_first_order_num, w_0)  # None*1

2)二階交互池检碗,得到一個k為的向量None*k据块,輸入到DNN模型當(dāng)中

    # second_order term 
    embeddings = tf.multiply(embedding,feat_val)    #N*F*K
    sum_square = tf.square(tf.reduce_sum(embedding,1)) #N*K
    square_sum = tf.reduce_sum(tf.square(embedding),1)  #N*k
    y_second_order = 0.5* tf.subtract(sum_square,square_sum)   #N*k

3)深度層,把交互池的結(jié)果經(jīng)過神經(jīng)網(wǎng)絡(luò)折剃,并輸出結(jié)果N*1

    #DeepLayer
    y_deep = y_second_order
    for i in range(len(deep_layers)):
        y_deep = tf.add(tf.matmul(y_deep,weights['layer_%d'%i]),weights['bias_%d' %i])
        y_deep = tf.nn.relu(y_deep)
        y_deep = tf.nn.dropout(y_deep,dropout_deep[i])     #N*deep_layers[i]

4)最終輸出

    # 輸出
    out = tf.add(liner,tf.reduce_sum(y_deep,1,keepdims=True))  #N*1

5)損失函數(shù)

 loss = tf.nn.l2_loss(tf.subtract(out,label))
 optimizer = tf.train.AdamOptimizer(lr,beta1=0.9,beta2=0.999,epsilon=1e-8).minimize(loss)

完整代碼見:
https://github.com/garfieldsun/recsys/tree/master/NFM

參考資料:
1另假、FNN論文
2、FNM論文
3怕犁、https://daiwk.github.io/posts/dl-dl-ctr-models.html
4边篮、http://www.reibang.com/p/4e65723ee632

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末己莺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子戈轿,更是在濱河造成了極大的恐慌凌受,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件思杯,死亡現(xiàn)場離奇詭異胜蛉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)色乾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門誊册,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杈湾,你說我怎么就攤上這事∪列耄” “怎么了漆撞?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長于宙。 經(jīng)常有香客問我浮驳,道長,這世上最難降的妖魔是什么捞魁? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任至会,我火速辦了婚禮,結(jié)果婚禮上谱俭,老公的妹妹穿的比我還像新娘奉件。我一直安慰自己,他們只是感情好昆著,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布县貌。 她就那樣靜靜地躺著,像睡著了一般凑懂。 火紅的嫁衣襯著肌膚如雪煤痕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天接谨,我揣著相機(jī)與錄音摆碉,去河邊找鬼。 笑死脓豪,一個胖子當(dāng)著我的面吹牛巷帝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扫夜,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼锅睛,長吁一口氣:“原來是場噩夢啊……” “哼埠巨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起现拒,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤辣垒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后印蔬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勋桶,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年侥猬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了例驹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡退唠,死狀恐怖鹃锈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瞧预,我是刑警寧澤屎债,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站垢油,受9級特大地震影響盆驹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜滩愁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一躯喇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硝枉,春花似錦廉丽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弧可,卻和暖如春蔑匣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背棕诵。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工裁良, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人校套。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓价脾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親笛匙。 傳聞我的和親對象是個殘疾皇子侨把,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評論 2 350

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