推荐模型-上下文感知-2017:Deep&Cross(DCN)【替换Wide&Deep的Wide部分;Wide还需要人工特征工程,而Cross可以进行特征的自动交叉,避免了基于业务理解的人工特征组合】

《原始论文:Deep & Cross Network for Ad Click Predictions》

推荐模型-上下文感知-2017:Deep&Cross(DCN)【替换Wide&Deep的Wide部分;Wide还需要人工特征工程,而Cross可以进行特征的自动交叉,避免了基于业务理解的人工特征组合】_第1张图片
推荐模型-上下文感知-2017:Deep&Cross(DCN)【替换Wide&Deep的Wide部分;Wide还需要人工特征工程,而Cross可以进行特征的自动交叉,避免了基于业务理解的人工特征组合】_第2张图片
Deep&Cross模型(DCN)其实就是对Wide&Deep模型中Wide部分进行改进的模型。

Wide&Deep模型原理很简单,但是最主要的是要掌握Wide&Deep这种线性和非线性,处理高维稀疏向量和embedding稠密向量的方式,能够使得模型同时具有泛化能力和记忆能力。能够根据最新数据在最短时间内以最小的代价做出回应(更新参数)。

一、Deep&Cross结构

Deep&Cross模型是2017年由斯坦福大学和谷歌在ADKDD会议上联合提出的,该模型是对Wide&Deep模型的一种改进。

  • 由于Wide&Deep模型的Wide部分的特征交互需要特征工程,而手工设计特征工程非常的繁琐。
  • 2阶的FM模型在线性的时间复杂度中自动进行特征交互,但是这些特征交互的表现能力并不够,并且随着阶数的上升,模型复杂度会大幅度提高。

所以作者对Wide部分进行更改,提出了一个Cross Network来自动进行特征之间的交叉,并且网络的时间和空间复杂度都是线性的。

通过与Deep部分相结合,构成了深度交叉网络(Deep&Cross Network),简称DCN。

模型的结构也非常简洁,从下往上依次为:Embedding和Stacking层、Cross网络层与Deep网络层并列、输出合并层,得到最终的预测结果。

推荐模型-上下文感知-2017:Deep&Cross(DCN)【替换Wide&Deep的Wide部分;Wide还需要人工特征工程,而Cross可以进行特征的自动交叉,避免了基于业务理解的人工特征组合】_第3张图片
可以看到,和W&D模型不同的是在embedding部分和Cross部分,在本文中也是着重讲一下这两部分。其中我打算先讲一下Cross层再讲embedding。

1、Cross 部分

这是本文最大的创新点—Cross网络(Cross Network),设计该网络的目的是增加特征之间的交互力度。交叉网络由多个交叉层组成,假设第l层的输出向量是 x l x_l xl,那么对于第 l + 1 l+1 l+1 层的输出向量 x l + 1 x_{l+1} xl+1 表示为:
在这里插入图片描述
可以看到, 交叉层的操作的二阶部分非常类似PNN提到的外积操作, 在此基础上增加了外积操作的权重向量 w l w_l wl, 以及原输入向量 x l x_l xl 和偏置向量 b l b_l bl。 交叉层的可视化如下:
推荐模型-上下文感知-2017:Deep&Cross(DCN)【替换Wide&Deep的Wide部分;Wide还需要人工特征工程,而Cross可以进行特征的自动交叉,避免了基于业务理解的人工特征组合】_第4张图片
其实看到这里,Cross模型的基本思想就被讲完了,但是并没有讲清楚。

接下里我会盘点一下这样做的好处,并且从我的理解角度一一进行解析,希望能给小伙伴们带来一个全新的理解。首先上一个大佬总结的例子来进行分析。

推荐模型-上下文感知-2017:Deep&Cross(DCN)【替换Wide&Deep的Wide部分;Wide还需要人工特征工程,而Cross可以进行特征的自动交叉,避免了基于业务理解的人工特征组合】_第5张图片

2、Embedding层

为什么会后面讲这个呢,主要是不知道大家在前面发现没有Cross层只有一个特征的输入即 x 0 x_0 x0

我当时看的时候一直在想,输出的特征不应该是二维的么,为什么这里是一维的,那么这个一维的 x x x 是代表每一个用户的全部特征呢,还是同一特征(如ID)下所有用户的数据呢?后面才看明白,原来是同一特征(如ID)下所有用户的数据,只不过这里的 x 0 x_0 x0 是将所有的特征都转化成了一维向量进行输入。

用一位大佬解释的截图来说明,这里我就不多打文字解释了。

推荐模型-上下文感知-2017:Deep&Cross(DCN)【替换Wide&Deep的Wide部分;Wide还需要人工特征工程,而Cross可以进行特征的自动交叉,避免了基于业务理解的人工特征组合】_第6张图片
后面的代码复现也会体现出这些来的。

3、其他层

二、代码实现

class CrossNetwork(nn.Module):
    """
    Cross Network
    """
    def __init__(self, layer_num, input_dim):
        super(CrossNetwork, self).__init__()
        self.layer_num = layer_num
        
        # 定义网络层的参数
        self.cross_weights = nn.ParameterList([nn.Parameter(torch.rand(input_dim, 1)) for i in range(self.layer_num)])
        self.cross_bias = nn.ParameterList([nn.Parameter(torch.rand(input_dim, 1)) for i in range(self.layer_num)])
    
    def forward(self, x):
        # x是(None, dim)的形状, 先扩展一个维度到(None, dim, 1)
        x_0 = torch.unsqueeze(x, dim=2)
        x = x_0.clone()
        xT = x_0.clone().permute((0, 2, 1))     # (None, 1, dim)
        for i in range(self.layer_num):
            x = torch.matmul(torch.bmm(x_0, xT), self.cross_weights[i]) + self.cross_bias[i] + x   # (None, dim, 1)
            xT = x.clone().permute((0, 2, 1))   # (None, 1, dim)
        
        x = torch.squeeze(x)  # (None, dim)
        return x
 
 
class DCN(nn.Module):
    def __init__(self, feature_columns, hidden_units, layer_num, dnn_dropout=0.):
        super(DCN, self).__init__()
        self.dense_feature_cols, self.sparse_feature_cols = feature_columns
        
        # embedding 
        self.embed_layers = nn.ModuleDict({
            'embed_' + str(i): nn.Embedding(num_embeddings=feat['feat_num'], embedding_dim=feat['embed_dim'])
            for i, feat in enumerate(self.sparse_feature_cols)
        })
        
        hidden_units.insert(0, len(self.dense_feature_cols) + len(self.sparse_feature_cols)*self.sparse_feature_cols[0]['embed_dim'])
        self.dnn_network = Dnn(hidden_units)
        self.cross_network = CrossNetwork(layer_num, hidden_units[0])         # layer_num是交叉网络的层数, hidden_units[0]表示输入的整体维度大小
        self.final_linear = nn.Linear(hidden_units[-1]+hidden_units[0], 1)
    
    def forward(self, x):
        dense_input, sparse_inputs = x[:, :len(self.dense_feature_cols)], x[:, len(self.dense_feature_cols):]
        sparse_inputs = sparse_inputs.long()
        sparse_embeds = [self.embed_layers['embed_'+str(i)](sparse_inputs[:, i]) for i in range(sparse_inputs.shape[1])]
        sparse_embeds = torch.cat(sparse_embeds, axis=-1)
        
        x = torch.cat([sparse_embeds, dense_input], axis=-1)
        
        # cross Network
        cross_out = self.cross_network(x)
        
        # Deep Network
        deep_out = self.dnn_network(x)
 
        #  Concatenate
        total_x = torch.cat([cross_out, deep_out], axis=-1)
        
        # out
        outputs = F.sigmoid(self.final_linear(total_x))
        
        return outputs  



参考资料:
推荐算法之Deep&Cross模型
Deep&Cross相比Wide&Deep作了哪些改进?Deep&Cross模型的Cross网络是怎么操作的?

你可能感兴趣的:(#,RS/上下文推荐(FM系列),推荐系统)