由于上传了很多代码运行结果截图,所以。。。首先。。。多图预警!!!

本文主要侧重于代码实现,关于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代码实现的简单结果…

欢迎大家一起讨论鸭~


推荐阅读:
相关文章