深度學習CTR預估(四)——AFM及其tensorflow實現

1奈籽、FM回顧

1.1 引言

FM是一種監(jiān)督學習痛悯,在線性單特征模型中加入了二階特征交互鸵鸥,但是兩兩特征交互有個明顯的缺點就是所有交互特征的權重都是相同的嵌巷,顯而易見萄凤,不同特征的重要性是不同的,比如女性-籃球和男性-籃球搪哪,其交互的權重肯定是不一樣的靡努,顯然FM模型并沒有考慮到這一點。
因此在AFM模型中引入了交叉項的權重晓折,不同的特征交叉項惑朦,由于影響的效果不同,所以這個權重也不同漓概,文章中把這個權重稱為注意力權重漾月。但是AFM有個很大的不足就是參數過于多,計算量非常大胃珍。

1.2 FM公式

\hat{y}(x):=w_0+\sum^{n}_{i=1}{w_ix_i}+\sum^{n}_{i=1}\sum^{n}_{j=i+1}{<v_i,v_j>x_ix_j}
其二階second-order項簡化公式為:

FM二階.png

由公式可以看出梁肿,若embedding_size也就是最外層的求和不考慮的話,那二階交叉項得到的就是一個k維的矩陣觅彰,這個k維的矩陣輸入DNN中就可以進行模型的變換吩蔑,這個矩陣下邊就記為[k]。

模型 結構 與DNN結合
DeepFM 并行結構 [k]直接輸入DNN中填抬,與FM公式進行concat烛芬,相當于FM二階后再加入高階特征
FNN 串行結構 非end-to-end模型,把一次項二次項權重w,v預訓練好赘娄,而后輸入DNN中
FNM 串行結構 [k]直接輸入DNN中仆潮,替換FM公式中的二階交互項
AFM 串行結構 在二次項交互權重<v_i,v_j>之上再加入了注意力權重系數,替換原始的二次項

2 AFM結構

2.1 AFM原理

因為二階交互項的重要性不同,因此在交互項之前加入了一個注意力因子擅憔,這個注意力因子就表示交互項的重要程度鸵闪。這個就是Attention機制
FM的二階項表達:
f_{pi}(\varepsilon)=\sum^{n}_{i=1}\sum^{n}_{j=i+1}{<v_i,v_j>x_ix_j}=\sum^{n}_{i=1}\sum^{n}_{j=i+1}v_ix_i\odot v_jx_j
AFM的二次項表達
f_{att}(f_{pi}(\varepsilon))=\sum^{n}_{i=1}\sum^{n}_{j=i+1}a_{ij}{<v_i,v_j>x_ix_j}=\sum^{n}_{i=1}\sum^{n}_{j=i+1}a_{ij}{v_ix_i\odot v_jx_j}

2.2 AFM結構

結構.png

上圖就是AFM二次交叉項的結構圖,其忽略了一次項和偏置的部分暑诸,前三部分就是FM的二次項部分蚌讼,后邊加入的注意力網絡attention-based pooling才是本文的創(chuàng)新和核心,文中加入一個attention net生成注意力權重參數a_{ij}个榕,加入二次項交叉項之前篡石,而且直接對二次項特征維度進行累加,生成加權和西采。

2.2.1

attention net網絡的表示公式凰萨,求得其交互項的注意力權重a_{ij},因為權重系數表示的是交叉項的相關程度械馆,因此a的系數表示越大表示其相關程度越高胖眷,a系數的綜合為1,因此設計一個注意力機制網絡霹崎,而后通過softmax函數珊搀,求出系數的值。

attention net.png

得到注意力權重后尾菇, 對FM的表達式進行改寫為:
AFM.png

其中權重,,,境析,k為embedding后的向量的維度,t為attention network的隱藏維度派诬。
由公式可以看出劳淆,AFM在FM的基礎上只是加入了注意力機制,并沒有使用DNN的網絡結構進行非線性的學習其他高階特征默赂。

3沛鸵、實驗代碼

實驗過程中還是使用了自己的數據集,特征數量feature_size將近3w缆八,field_size為69谒臼,因此其交互項為69*68/2=2346個。根據之前NFM的代碼進行改寫耀里,loss函數為MSE,使用Adam作為優(yōu)化拾氓,同樣采用了RMSE進行評分預測:

3.1數據預處理

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 AFM模型

3.2.1 設置權重初始化

權重的設置分為FM部分權重和attention部分的權重

#FM權重
    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')

    
3.2.2 embedding層和placeholder的設置
    feat_index = tf.compat.v1.placeholder(tf.int32,[None,None],name='feat_index')
    feat_value = tf.compat.v1.placeholder(tf.float32,[None,None],name='feat_value')
    label = tf.compat.v1.placeholder(tf.float32,shape=[None,1],name='label')
     
    embedding_first =tf.nn.embedding_lookup(w,feat_index)   #None*F *1   F是field_size大小,也就是不同域的個數
    embedding = tf.nn.embedding_lookup(v,feat_index)      #None * F * embedding_size
    feat_val = tf.reshape(feat_value,[-1,field_size,1])
  #vx的計算
    embedding_vx = tf.multiply(embedding_first,feat_val)
3.2.2 模型的設置

1)線性部分

    y_first_order= tf.reduce_sum(embedding_vx,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)attention部分
(a)attention權重初始化(K為embedding_size,A為attention_size)
公式中:
W的權重維度為 K * A
b的權重維度為 1* K
h的權重維度為 1* K
p的權重維度為 1* K

#Attention部分的權重
   weights={}
   glorot = np.sqrt(2.0 / (attention_size + embedding_size))

    weights['attention_w'] = tf.Variable(
        np.random.normal(loc=0, scale=glorot, size=(embedding_size,attention_size)), dtype=np.float32
    )
    weights['attention_b'] = tf.Variable(
        np.random.normal(loc=0, scale=glorot, size=(attention_size,)), dtype=np.float32
    )
    weights['attention_h'] = tf.Variable(
        np.random.normal(loc=0, scale=glorot, size=(attention_size,)), dtype=np.float32
    )
    weights['attention_p'] = tf.Variable(
        np.random.normal(loc=0, scale=glorot, size=(embedding_size,)), dtype=np.float32
    )

(b) Attention Net
按照公式來計算注意力權重

attention net.png

為了得到每一個兩兩交叉項的向量,也就是求得

交叉項的embedding房官,我們寫了個循環(huán)趾徽,如下邊代碼,通過stack使之變成一個張量翰守,而后通過第一維和第二維的轉置得到一個None* F(F-1)/2* K維的張量孵奶。 F(F-1)/2* K表示一個樣本所有交互項的交互矩陣。

# 交互項的矩陣 #None*F(F-1)/2*k
    attention_weight_list=[]
    for i in range(field_size):
        for j in range(i+1,field_size):
            attention_weight_list.append(tf.multiply(embedding_vx[:,i,:],embedding_vx[:,j,:]))
    attention_cross = tf.stack(attention_weight_list)
    attention_cross = tf.transpose(attention_cross,perm=[1,0,2],name='transpose')   #None*F(F-1)/2*k

得到了attention_cross交互矩陣后蜡峰,根據公式:


attention net.png

計算權重系數了袁,使用softmax把權重系數總和限制為1,其最后輸出矩陣大小為None* F(F-1)/2* 1

# 可以帶入權重attention
    attention_weight = tf.add(tf.matmul(attention_cross,weights['attention_w']),weights['attention_b'])  #None*F(F-1)/2*A
    attention_weight_a = tf.reduce_sum(tf.multiply(tf.nn.relu(attention_weight),weights['attention_h']),axis=2)  #None*F(F-1)/2
    attention_weight_out = tf.reshape(softmax(attention_weight_a),[-1,int(field_size*(field_size-1)/2),1]) #None*F(F-1)/2*1

(softmax的函數)

def softmax(x):
    if len(x.shape) > 1:
        fenzi_exp= tf.exp(x - tf.reduce_max(x))
        fenmu = 1.0/tf.reduce_sum(fenzi_exp,axis=1,keepdims=True)
        result = fenzi_exp * fenmu
    else:
        result = tf.exp(x - tf.reduce_max(x))/tf.reduce_sum(tf.exp(x - tf.reduce_max(x)))
    return result

(c)二次項

 #二階second_order的輸出
    #所有交叉項相加
    atten_add = tf.reduce_sum(tf.multiply(attention_cross,attention_weight_out),axis=1)     #N*K
    second_order = tf.reduce_sum(tf.multiply(atten_add,weights['attention_p']),axis=1,keepdims=True)  #N*1

3)輸出和優(yōu)化

   out = tf.add(liner,second_order)  #N*1
   #loss
    loss = tf.nn.l2_loss(tf.subtract(out,label))
    optimizer = tf.compat.v1.train.AdamOptimizer(lr,beta1=0.9,beta2=0.999,epsilon=1e-8).minimize(loss)

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

參考博客:
1湿颅、論文
2载绿、http://www.reibang.com/p/83d3b2a1e55d

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市油航,隨后出現的幾起案子崭庸,更是在濱河造成了極大的恐慌,老刑警劉巖谊囚,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怕享,死亡現場離奇詭異,居然都是意外死亡镰踏,警方通過查閱死者的電腦和手機函筋,發(fā)現死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來余境,“玉大人驻呐,你說我怎么就攤上這事》祭矗” “怎么了含末?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長即舌。 經常有香客問我佣盒,道長,這世上最難降的妖魔是什么顽聂? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任肥惭,我火速辦了婚禮,結果婚禮上紊搪,老公的妹妹穿的比我還像新娘蜜葱。我一直安慰自己,他們只是感情好耀石,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布牵囤。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪揭鳞。 梳的紋絲不亂的頭發(fā)上炕贵,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音野崇,去河邊找鬼称开。 笑死,一個胖子當著我的面吹牛乓梨,可吹牛的內容都是我干的鳖轰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼督禽,長吁一口氣:“原來是場噩夢啊……” “哼脆霎!你這毒婦竟也來了?” 一聲冷哼從身側響起狈惫,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤睛蛛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胧谈,有當地人在樹林里發(fā)現了一具尸體忆肾,經...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年菱肖,在試婚紗的時候發(fā)現自己被綠了客冈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡稳强,死狀恐怖场仲,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情退疫,我是刑警寧澤渠缕,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站褒繁,受9級特大地震影響亦鳞,放射性物質發(fā)生泄漏。R本人自食惡果不足惜棒坏,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一燕差、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坝冕,春花似錦徒探、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吵血。三九已至,卻和暖如春偷溺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钱贯。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工挫掏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秩命。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓尉共,卻偏偏與公主長得像,于是被迫代替她去往敵國和親弃锐。 傳聞我的和親對象是個殘疾皇子袄友,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內容