由於上傳了很多代碼運行結果截圖,所以。。。首先。。。多圖預警!!!

本文主要側重於代碼實現,關於DeepFM模型結構理論可以查看這篇:

dengdeng:CTR預估之DeepFM(ppt)?

zhuanlan.zhihu.com
圖標

一、訓練數據預處理

(1)實驗數據

訓練集:

測試集

(2) 特徵編碼 one-hot encoding

在做one-hot encoding 之前,需要先將訓練集和測試集合併:

代碼:

df = pd.concat([dfTrain, dfTest], axis=0)

代碼運行結果:

然後進行one-hot encoding,離散特徵和連續特徵分開編碼處理,其中每個離散特徵中每一類作為一維特徵,而每個連續特徵則作為一維特徵:

for col in df.columns:
# 連續特徵只有一個編號
if col in NUMERIC_FEATURES:
feat_dict[col] = total_cnt
total_cnt += 1
continue

# 離散特徵,有多少個取值就有多少個編號
unique_vals = df[col].unique() #[2,3,1,0]
unique_cnt = df[col].nunique() #4
feat_dict[col] = dict(zip(unique_vals, range(total_cnt, total_cnt + unique_cnt)))
total_cnt += unique_cnt

feat_size = total_cnt

代碼運行結果:

特徵維度總共為9維,其中離散特徵featcat_1 :3維,featcat_2 :4維,並對其中每一類對應編碼為0-6維特徵;連續特徵feat_num_1和feat_num_2分別對應編碼為7、8維特徵。

二、建立模型

(1)embedding 操作

一般用於CTR預估的數據中,存在著大量的類別特徵,而類別特徵經過one-hot encoding 則會急劇增加特徵維度,並且特別的稀疏。這樣的特徵直接用於訓練模型的話,會導致訓練模型的參數特別多,從而影響模型訓練效率。DeepFM 論文中採用embedding 操作,將稀疏的特徵轉化為稠密特徵。

在TensorFlow中,有一個非常好用的embedding操作API:tf.nn.embedding_lookup(weights, ids),只需要輸入初始權重weights和特徵對應的ids,根據特徵對應的索引值,獲得各個特徵對應的權重。再與特徵值做位乘,即可得到embedding結果。

因此,首先要先獲得輸入特徵對應的索引值以及對離散特徵全部賦值為1的全新特徵值:

代碼:

for col in dfi.columns:

if col in NUMERIC_FEATURES: # 連續特徵1個維度,對應1個編號,這個編號是一個定值
dfi[col] = feat_dict[col]
else:
# 離散特徵。不同取值對應不同的特徵維度,編號也是不同的。
dfi[col] = dfi[col].map(feat_dict[col])
dfv[col] = 1.0

代碼結果:

特徵索引dfi
離散特徵賦值為1後的特徵值dfv

代碼:

# Sparse Features -> Dense Embedding
embeddings_origin = tf.nn.embedding_lookup(weights[feature_embedding], ids=feat_index) # [None, field_size, embedding_size]

embeddings = tf.multiply(embeddings_origin, feat_value_reshape) # [None, field_size, embedding_size] 注意:multiply不是矩陣相乘,而是矩陣對應位置相乘。這裡應用了broadcast機制。

代碼運行結果:

embeddings

embeddings 的維度為5??4??8

(2)FM

FM 的作用就是用來特徵組合,其公式如下:

從公式中可以看出,FM 包含兩部分,一個是前面的 <omega,x> ,另一個則是 <V_{i},V_{j}>x_{j1}*x_{j2}* ,論文中將第一部分稱為Addition 單元,第二部分稱為內積單元。Addition 單元反映的是一階特徵,內積單元反映的是二階組合特徵。

一階特徵就是將輸入的特徵值與權重相乘得到。

代碼:

# --------- 一維特徵 -----------
y_first_order = tf.nn.embedding_lookup(weights[feature_bias], ids=feat_index) # [None, field_size, 1]
w_mul_x = tf.multiply(y_first_order, feat_value_reshape) # [None, field_size, 1] Wi * Xi 維度結果如何計算得到???是對應點乘->位乘
y_first_order = tf.reduce_sum(input_tensor=w_mul_x, axis=2) # [None, field_size]縮減維度,得到FM_1維的輸出

代碼運行結果:

對於計算二階組合特徵,上述FM內積單元計算過於複雜,可以通過簡化得到:

FM內積部分化簡公式

代碼:

# --------- 二維組合特徵 ----------
embeddings = tf.multiply(embeddings_origin, feat_value_reshape) # [None, field_size, embedding_size] 注意:multiply不是矩陣相乘,而是矩陣對應位置相乘。這裡應用了broadcast機制。

# sum_square part 先sum,再square
summed_features_emb = tf.reduce_sum(input_tensor=embeddings, axis=1) # [None, embedding_size]
summed_features_emb_square = tf.square(summed_features_emb) # [None, embedding_size]

# square_sum part
squared_features_emb = tf.square(embeddings)# [None, field_size, embedding_size]
squared_features_emb_summed = tf.reduce_sum(input_tensor=squared_features_emb, axis=1) # [None, embedding_size]

# second order FM_2維組合特徵輸出結果,維度為embedding_size
y_second_order = 0.5 * tf.subtract(summed_features_emb_square, squared_features_emb_summed) # [None, embedding_size]#對應FM 論文的化簡公式

代碼運行結果:

(三)Deep

Deep 單元主要用於對特徵進行高階特徵組合,論文中Deep 單元為普通的全連接網路,網路輸入層為embedding之後的結果,其結構如下圖所示:

代碼:

# ----------- Deep Component ------------
y_deep = tf.reshape(embeddings_origin, shape=[-1, config.field_size * config.embedding_size]) # [None, field_size * embedding_size]
for i in range(0, len(deep_layers)):
y_deep = tf.add(tf.matmul(y_deep, weights[layer_%d % i]), weights[bias_%d % i])
y_deep = config.deep_layers_activation(y_deep)#激勵層

代碼運行結果:

(四)輸出

在分別得到FM單元和Deep單元的輸出後,將兩部分的輸出concat合併之後,經一層全連接層,再經sigmoid激勵函數,便得到最終預測的概率值。

代碼:

# ----------- output -----------
concat_input = tf.concat([y_first_order, y_second_order, y_deep], axis=1)
out = tf.add(tf.matmul(concat_input, weights[concat_projection]), weights[concat_bias])
out = tf.nn.sigmoid(out)

代碼運行結果:

以上就是DeepFM代碼實現的簡單結果…

歡迎大家一起討論鴨~


推薦閱讀:
相关文章