DeepCrossing是一个真正的的将深度学习应用于推荐系统的模型。在学习DeepCrossing之前,可以先了解一下另一个使用深度学习的推荐系统模型AutoRec。
AutoRec
AutoRec模型是一种基于自编码思想的深度学习推荐模型。我们都知道,推荐系统的数据一般都是一个用户物品的共现矩阵,我们的任务就是将这个矩阵上面缺失的数据补全。传统的协同过滤使用相似度来度量用户或者物品之间的相似性,以此来作为推荐的标准。但是这样的方法表达能力比较弱,而且由于用户物品的评分矩阵往往比较稀疏,这样的方法效果就不是那么的好。因此就考虑将擅长表达的深度学习用到推荐系统中来,AutoRec就是这样一个尝试。
AutoRec使用自编码器来处理共现矩阵,以此实现对用户和物品向量的自编码,进而预测用户评分。
由上图可以看到,自编码前后的输入输出数量是一致的,这样我们就可以得到预测评分的值。
模型的结构如图(图片来自《深度学习推荐系统》)。把这个图竖起来,就变成了我们熟悉的神经网络结构图了。同样的,AutoRec像协同过滤一样,可以使用用户向量来预测,也可以使用物品向量来预测。了解了AutoRec之后,就来看一下今天学习的DeepCrossing的原理及实现。
DeepCrossing原理
为了完成端到端的训练, DeepCrossing模型要在内部网络结构中解决如下问题:
- 离散类特征编码后过于稀疏, 不利于直接输入神经网络训练, 需要解决稀疏特征向量稠密化的问题
- 如何解决特征自动交叉组合的问题
- 如何在输出层中达成问题设定的优化目标
模型的结构如下图所示(图片来自参考资料)
如上图所示,模型大致分为四个部分,Embedding层、Stacking层、Multiple Residual Units层、Scoring层。
Embedding层主要是为了处理稀疏的类别特征,将类别特征转换为稠密的向量。而数值型特征不需要进行embedding处理,直接连接到stacking层。
stacking层就是将数值特征和生成的embedding特征进行拼接作为下一层的输入。
#将所有的dense特征拼接到一起
dense_dnn_list = list(dense_input_dict.values())
dense_dnn_inputs = Concatenate(axis=1)(dense_dnn_list) # B x n (n表示数值特征的数量)
# 因为需要将其与dense特征拼接到一起所以需要Flatten,不进行Flatten的Embedding层输出的维度为:Bx1xdim
sparse_dnn_list = concat_embedding_list(dnn_feature_columns, sparse_input_dict, embedding_layer_dict, flatten=True)
sparse_dnn_inputs = Concatenate(axis=1)(sparse_dnn_list) # B x m*dim (n表示类别特征的数量,dim表示embedding的维度)
# 将dense特征和Sparse特征拼接到一起
dnn_inputs = Concatenate(axis=1)([dense_dnn_inputs, sparse_dnn_inputs]) # B x (n + m*dim)
这里用到的flatten是将张量保留0轴进行扁平化。B代表Batchsize。
经过了stacking层的数据处理之后,下一步需要将数据放入残差网络中进行连接。所谓的残差网络,就是将经过神经网络的输出值再与输入值相加。如下图所示(图片来自参考资料):
残差单元的定义如下:
# DNN残差块的定义
class ResidualBlock(Layer):
def __init__(self, units): # units表示的是DNN隐藏层神经元数量
super(ResidualBlock, self).__init__()
self.units = units
def build(self, input_shape):
out_dim = input_shape[-1]
self.dnn1 = Dense(self.units, activation='relu')
self.dnn2 = Dense(out_dim, activation='relu') # 保证输入的维度和输出的维度一致才能进行残差连接
def call(self, inputs):
x = inputs
x = self.dnn1(x)
x = self.dnn2(x)
x = Activation('relu')(x + inputs) # 残差操作
return x
最后的输出层通过逻辑回归叠加多个残差块实现评分概率预测。
# block_nums表示DNN残差块的数量
def get_dnn_logits(dnn_inputs, block_nums=3):
dnn_out = dnn_inputs
for i in range(block_nums):
dnn_out = ResidualBlock(64)(dnn_out)
# 将dnn的输出转化成logits
dnn_logits = Dense(1, activation='sigmoid')(dnn_out)
return dnn_logits
代码运行结果
为了验证模型的性能,使用criteo数据集来进行实验。criteo是经典的点击率预估比赛的数据集。数据集下载
模型使用的评价指标为AUC。
模型训练结果如上图所示。
参考资料:
1、DataWhale组队学习:https://github.com/datawhalechina/team-learning-rs/blob/master/DeepRecommendationModel/DeepCrossing.md
2、https://blog.csdn.net/wuzhongqiang/article/details/108948440