在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)為:
2、FNM原理
2.1 FM和FNM的比較
FNM模型在FM的二階交互的基礎(chǔ)上加入了一個DNN模型状蜗,F(xiàn)M的模型公式為:
其中n為特征的個數(shù)需五,而其二階表達(dá)式為:
觀察二階的推導(dǎo)公式,其中k為隱向量的維度轧坎,如果我們不把隱向量的維度進(jìn)行相加宏邮,那么二階特征組合輸出的結(jié)果就是一個k為的向量,而這個k維的向量就是FNM模型中DNN的輸入。
因此FNM的模型公式為
因?yàn)闆]有的存在蜜氨,所以括號中就是NFM模型的二階特征交叉項(xiàng)械筛,是一個k維的向量。將這個k維向量輸入DNN結(jié)構(gòu)中就得到了FNM的二階交互的預(yù)測結(jié)果飒炎。
2.2 FNM的結(jié)構(gòu)
圖中顯示的是二階特征組合的DNN結(jié)構(gòu)埋哟,也就是的結(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)過程:
3.2.1 設(shè)置權(quán)重初始化
根據(jù)上述公式原理弟断,NFM模型可以分成兩個部分,F(xiàn)M部分和DNN部分趴生,F(xiàn)M部分的權(quán)重有偏置項(xiàng)阀趴,一階權(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