论文链接:https://arxiv.org/pdf/1703.04247.pdf
动机
对于点击率(CTR)问题,提升推荐准确率的一个方法是特征组合,现有的方法(论文发布前)在对高阶,或者低阶特征相互作用有很强的偏好,需要对特征数据进行专业的特征工程进行处理。使得推荐系统能够实时的对特征数据进行处理,然后对用户进行推荐,因此开始不断的使用DNN等拓展知识进行尝试。
一、在不断的在DNN相关知识进行拓展的基础上经历了这几个过程。
(1)使用DNN网络解决推荐问题的时候会存在网络参数可能过于庞大的问题,这是因为在进行特征处理的时候我们需要使用one-hot编码处理离散特征,这会导致输入的维度猛增。
(2)为了解决DNN参数量过大,可以采用非常经典的Field思想,将one-hot特征转换为Dense Vector.
(3)此时通过增加全连接层就可以实现高阶的特征组合。
二、由于仅仅使用DNN并没是有使低阶数据的特征进行组合,因此我们增加FM来表示低阶的特征组合。
结合FNN和DNN有两种方式可以并行结合还可以串行结合。
对这个算法流程图的分析,我们可以看到与DNN不同的是输入的不在是Feature field,而是Embedding,在FNN中将特征进行了一次嵌入操作。这样做优化了特征的表示,同时能够使得算法结果得到提高。
三、Wide&Deep
Wide&Deep模型就不做过多介绍了,但是在输出层将高阶与低阶特征进行结合,但是在实际使用过程中,这个结果还是很难把控的,因为这个算法中的Wide部分与Deep部分是并行的,在设置参数或者训练的过程中很容易使得模型偏向某一方。
DeepFM
经过上述算法的逐步发展,DeepFM终于出世了。
预测模型联合训练的公式:
通过这个公式是不是与Wide&Deep又所相似呢?在这个训练模型中实际上就是将Wide部分替换为FM部分,然后再输入部分中将最开始的Feature进行了一层嵌入表示作为后面FM和DNN模型中的混合输入。作为点击预测,只有两种可能性(0,1)。
这是FM的公式表示,其中i, j分别代表特征,Vi,Vj表示的是潜在向量。<w, x>代表的是加法单元。反应的是Inner Product中的功能交互的重要性。
对于DNN模型而言:
通过这个图我们可以清楚的看到,k决定的是嵌入层中嵌入向量的维度,表示嵌入向量的大小,通过嵌入层将Sparse Featrue转换为k为的新的嵌入层表示,这解决了数据稀疏性的问题。
Denote Embedding layer的表示为:
这个地方仅仅是DNN的输入表示,再DNN中进行的迭代操作如下:
这里的激活函数依旧是sigmoid函数,b表示的是偏置量,l表示在DNN中进行的第几层。
最终我们得到了这样的一个关于yDNN的表达式。
在论文中可以找到这样的一张对比图:
这张图介绍了除DeepFM外的集中点击量预测的深度推荐模型图。
这个算法的基本介绍也就到这里了,当然如果还有什么不明白的地方建议论文的第二部分,讲解的非常详细。
代码实现
def DeepFM(linear_feature_columns, dnn_feature_columns):
# 构建输入层,即所有特征对应的Input()层,这里使用字典的形式返回,方便后续构建模型
dense_input_dict, sparse_input_dict = build_input_layers(linear_feature_columns + dnn_feature_columns)
# 将linear部分的特征中sparse特征筛选出来,后面用来做1维的embedding
linear_sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), linear_feature_columns))
# 构建模型的输入层,模型的输入层不能是字典的形式,应该将字典的形式转换成列表的形式
# 注意:这里实际的输入与Input()层的对应,是通过模型输入时候的字典数据的key与对应name的Input层
input_layers = list(dense_input_dict.values()) + list(sparse_input_dict.values())
# linear_logits由两部分组成,分别是dense特征的logits和sparse特征的logits
linear_logits = get_linear_logits(dense_input_dict, sparse_input_dict, linear_sparse_feature_columns)
# 构建维度为k的embedding层,这里使用字典的形式返回,方便后面搭建模型
# embedding层用户构建FM交叉部分和DNN的输入部分
embedding_layers = build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False)
# 将输入到dnn中的所有sparse特征筛选出来
dnn_sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), dnn_feature_columns))
fm_logits = get_fm_logits(sparse_input_dict, dnn_sparse_feature_columns, embedding_layers) # 只考虑二阶项
# 将所有的Embedding都拼起来,一起输入到dnn中
dnn_logits = get_dnn_logits(sparse_input_dict, dnn_sparse_feature_columns, embedding_layers)
# 将linear,FM,dnn的logits相加作为最终的logits
output_logits = Add()([linear_logits, fm_logits, dnn_logits])
# 这里的激活函数使用sigmoid
output_layers = Activation("sigmoid")(output_logits)
model = Model(input_layers, output_layers)
return model
一个整体的模型架构图,帮助大家更好的了解每一块以及前向传播
思考题我想应该可以在上面的内容中找到答案了吧。