推荐模型-上下文感知-2016:Wide&Deep【Wide部分:记忆能力(原始特征)】【Deep部分:泛化能力(交叉特征)】【开创了组合模型的构造方法, 对深度学习推荐模型的发展产生了重大的影响】

在这里插入图片描述
在这里插入图片描述
Heng-Tze Cheng et al. “Wide & Deep Learning for Recommender Systems.” in RecSys 2016.

从上面图里面也会看到Wide&Deep的重要性, 是处于一个核心的地位, 后面的很多深度学习模型都是基于此模型的架构进行的改进。 所以该模型的提出在业界是有非常大的影响力的。

在这个模型中小伙伴们主要还是学习这个模型的思想。

在CTR预估任务中,线性模型仍然很重要,主要是因为利用手工构造的交叉组合特征来使线性模型具有“记忆性”,模型记住共现频率较高的特征组合,往往也能达到一个不错的baseline,且可解释性强。

但这种方式有着较为明显的缺点:

  • 首先,特征工程需要耗费太多精力。
  • 其次,因为模型是强行记住这些组合特征的,所以对于未曾出现过的特征组合,权重系数为0,无法进行泛化。

为了加强模型的泛化能力,研究者引入了DNN结构,将高维稀疏特征编码为低维稠密的Embedding vector,这种基于Embedding的方式能够有效提高模型的泛化能力。但是,现实世界是没有银弹的。基于Embedding的方式可能因为数据长尾分布,导致长尾的一些特征值无法被充分学习,其对应的Embedding vector是不准确的,这便会造成模型泛化过度。

简单解释一下就是embedding的这种思路, 如果碰到了共现矩阵高度稀疏且高秩(比如user有特殊的爱好, 或者item比较小众) ,很难非常效率的学习出低维度的表示。这种情况下,大部分的query-item都没有什么关系。但是dense embedding会导致几乎所有的query-item预测值都是非0的,这就导致了推荐过度泛化,会推荐一些不那么相关的物品。相反,简单的linear model却可以通过cross-product transformation来记住这些excption rules。

所以根据上面的分析, 我们发现:

  • 简单的模型, 比如协同过滤, 逻辑回归等,能够从历史数据中学习到高频共现的特征组合能力, 但是泛化能力不足;
  • 而像矩阵分解, embedding再加上深度学习网络, 能够利用相关性的传递性去探索历史数据中未出现的特征组合, 挖掘数据潜在的关联模式, 但是对于某些特定的场景(数据分布长尾, 共现矩阵稀疏高秩)很难有效学习低纬度的表示, 造成推荐的过渡泛化。

既然这两种模型的优缺点都这么的互补, 为啥不组合一下子他俩呢?2016年,Google提出Wide&Deep模型,将线性模型与DNN很好的结合起来,在提高模型泛化能力的同时,兼顾模型的记忆性。Wide&Deep这种线性模型与DNN的并行连接模式,后来成为推荐领域的经典模式, 奠定了后面深度学习模型的基础。 这个是一个里程碑式的改变, 但仔细看看人家的模型架构, 会发现并没有多复杂, 甚至比之前的PNN, Neural CF等都简单。但是人家的这种思想却是有着重大意义的。 后面我们会具体学习这个结构, 但是在这之前, 想借着王喆老师书上的例子聊聊“Memorization"和”Generalization"。

一、模型的记忆能力和泛化能力

1、记忆能力

记忆能力:模型直接学习并利用历史数据中物品和特征的“共现频率”的能力。

一般来说,协同过滤、逻辑回归等简单模型有较强的“记忆 能力”。由于这类模型的结构简单,原始数据往往可以直接影响推荐结果,产生 类似于“如果点击过A ,就推荐B” 这类规则式的推荐,这就相当于模型直接记 住了历史数据的分布特点,并利用这些记忆进行推荐。

因为Wide&Deep是由谷歌应用商店(Google Play)推荐团队提出的,所以 这里以App推荐的场景为例,解释什么是模型的“记忆能力”。

假 设 在 Google Play推荐模型的训练过程中,设置如下组合特征:AND (user_installed_app=netflix, impression_app=pandora)(简称 netflix&pandora), 它 代表用户已经安装了 netflix这款应用,而且曾在应用商店中看到过pandora这款 应用。

如果以“最终是否安装pandora” 为数据标签(label) , 则可以轻而易举地 统计出netflix&pandora这个特征和安装pandora这个标签之间的共现频率。

假设 二者的共现频率高达10% (全局的平均应用安装率为1% ),这个特征如此之强, 以至于在设计模型时,希望模型一发现有这个特征,就推荐pandora这款应用(就 像一个深刻的记忆点一样印在脑海里),这就是所谓的模型的“记忆能力”。

像逻 辑回归这类简单模型,如果发现这样的“强特征”,则其相应的权重就会在模型 训练过程中被调整得非常大,这样就实现了对这个特征的直接记忆。

相反,对于 多层神经网络来说,特征会被多层处理,不断与其他特征进行交叉,因此模型对这个强特征的记忆反而没有简单模型深刻。

2、泛化能力

泛化能力:“泛化能力”可以被理解为模型传递特征的相关性,以及发掘稀疏甚至从未 出现过的稀有特征与最终标签相关性的能力。

矩阵分解比协同过滤的泛化能力 强,因为矩阵分解引入了隐向量这样的结构,使得数据稀少的用户或者物品也能 生成隐向量,从而获得有数据支撑的推荐得分,这就是非常典型的将全局数据传 递到稀疏物品上,从而提高泛化能力的例子。再比如,深度神经网络通过特征的 多次自动组合,可以深度发掘数据中潜在的模式,即使是非常稀疏的特征向量输 入,也能得到较稳定平滑的推荐概率,这就是简单模型所缺乏的“泛化能力”。

"泛化能力“可以被理解为模型传递特征的相关性, 以及发掘稀疏甚至从未出现过的稀有特征与最终标签相关性的能力。比如矩阵分解, embedding等, 使得数据稀少的用户或者物品也能生成隐向量, 从而获得由数据支撑的推荐得分, 将全局数据传递到了稀疏物品上, 提高泛化能力。再比如神经网络, 通过特征自动组合, 可以深度发掘数据中的潜在模式,提高泛化等。

所以, Wide&Deep模型的直接动机就是将两者进行融合, 使得模型既有了简单模型的这种“记忆能力”, 也有了神经网络的这种“泛化能力”, 这也是记忆与泛化结合的伟大模式的初始尝试。下面就来具体看一下W&D模型的结构。

二、Wide&Deep模型的结构

既然简单模型的“记忆能力”强,深度神经网络的“泛化能力”强,那么设 计 Wide&Deep模型的直接动机就是将二者融合,具体的模型结构如图3-13所示。
在这里插入图片描述

W&D的模型如下面中间的图所示(左边的是wide部分, 也就是一个简单的线性模型, 右边是deep部分, 一个经典的DNN模型)

在这里插入图片描述
W&D模型把单输入层的Wide部分和由Embedding层和多隐层组成的 Deep部分连接起来, 一起输入最终的输出层得到预测结果。

  • 单层的wide层善于处理大量的稀疏的id类特征,
  • Deep部分利用神经网络表达能力强的特点,进行深层的特征交 叉,挖掘藏在特征背后的数据模式。

最终,利用逻辑回归模型,输出层将Wide 部分和Deep部分组合起来,形成统一的模型。

注:NerualCF混合模型,也是由两个模型,一个神经网络,一个矩阵分解来进行了组合,也是和W&D一样的组合,那么为什么效果就不一样呢?当时我看到这里的时候的确是有些混淆了,然后又重新翻看了之前的博客。其实这两个模型还是有很大区别的,在NeuralCF上,两个分块都是基于矩阵分解来进行的下一步,也就是说并没有对特征进行明显的划分,而是将其全部进行了无差别交叉,这样就和前面说的一样,损失了很多信息。而且都是将特征分别进行了embedding变成了稠密向量,而这样子就不能够使得线性模型拥有着很强的记忆能力,对高维稀疏向量处理也显得乏力。

1、Wide部分

Wide部分是一个广义的线性模型, 公式如下:
在这里插入图片描述
输入的X特征包括原始特征和转换的特征, 还有一些离散的id类特征给它(神经网络那边是不喜欢这种高稀疏的离散id特征的, 巧了,wide这边喜欢)。 其中一种比较重要的转换操作就是cross-product transformation(原始特征的交互特征),公式如下:
在这里插入图片描述
如果两个特征同时为1的时候, 这个特征就是1, 否则就是0, 这是一种特征组合, 往往我们在特征工程的时候常常会做一些这种特征。 比如 And(gender=female, language=en)=1, 当且仅当gender=female, language=en的时候, 否则就是0。

对于wide部分训练时候使用的优化器是带正则的FTRL算法(Follow-the-regularized-leader),我们可以把FTRL当作一个稀疏性很好,精度又不错的随机梯度下降方法, 该算法是非常注重模型稀疏性质的,也就是说W&D模型采用L1 FTRL是想让Wide部分变得更加的稀疏,即Wide部分的大部分参数都为0,这就大大压缩了模型权重及特征向量的维度。Wide部分模型训练完之后留下来的特征都是非常重要的,那么模型的“记忆能力”就可以理解为发现"直接的",“暴力的”,“显然的”关联规则的能力。 例如, Google W&D期望wide部分发现这样的规则:用户安装了应用A,此时曝光应用B,用户安装应用B的概率大。 所以对于稀疏性的规则的考量, 和具体的业务场景有关。同样, 这部分的输入特征也是有讲究的, 后面会看谷歌的那个例子。

这里再补充一点上面wide部分使用FTRL算法的原因, 也就是为啥这边要注重模型稀疏性质的原因, 上面的压缩模型权重,减少服务的时候存储压力是其一, 还有一个是来自工业上的经验, 就是对模型的实时更新更加有利, 能够在实时更新的时候, 尽量的加大实时的那部分数据对于参数更新的响应速度,不至于用实时数据更新好久也没有更新动原来的大模型。毕竟它注重稀疏性质,一旦察觉到新来的这部分数据某些特征变了, 就立即加大权重或者直接置为0, 这样就能只记住实时数据的关键特征了, 使得模型能够更好的实时服务。 deep端的这种普通梯度下降的方式是不行的,这种都是一般使用类似L2正则的方式, 更新参数的时候尽量的慢慢减小所有的w参数, 更新起来是会很慢的, 不太能反应实时变化。 像FTRL这种, 用的类似于L1正则的方式, 这俩的区别是显然的了。

2、Deep部分

该部分主要是一个Embedding+MLP的神经网络模型。大规模稀疏特征通过embedding转化为低维密集型特征。然后特征进行拼接输入到MLP中,挖掘藏在特征背后的数据模式。
在这里插入图片描述
输入的特征有两类, 一类是数值型特征, 一类是类别型特征(会经embedding)。我们知道DNN模型随着层数的增加,中间的特征就越抽象,也就提高了模型的泛化能力。 对于Deep部分的DNN模型作者使用了深度学习常用的优化器AdaGrad,这也是为了使得模型可以得到更精确的解。

3、联合训练

W&D模型是将两部分输出的结果结合起来联合训练,将deep和wide部分的输出重新使用一个逻辑回归模型做最终的预测,输出概率值。联合训练的数学形式如下:
在这里插入图片描述
Wide&Deep模型本身的结构非常简单的,但是如何根据自己的场景去选择哪些特征放在Wide部分,哪些特征放在Deep部分是用好该模型的一个前提。总体模型如下图所示:
在这里插入图片描述

在这里插入图片描述

我们重点看看这两部分的输入特征:

  • Deep部分: 全量的特征向量, 包括用户年龄(age), 已安装应用数量(#app installs), 设备类型(device class), 已安装应用(installed app), 曝光应用impression app)等特征。 其中:
    • 已安装应用、曝光应用等类别型特征,需要经过Embedding层输入连接层(Concatenated Embedding ) , 拼接成 1200维的 Embedding向量,再依次经过3 层 ReLU全连接层,最终输入LogLoss输出层;
    • 数值型的特征和前面的特征拼接起来直接输入连接层, 经过3层的Relu全连接层。
  • Wide部分输入仅仅是已安装应用和曝光应用两类特征。 其中已安装应用代表用户的历史行为, 而曝光应用代表当前待推荐应用。
    • 选择这两部分是想发现当前曝光APP和用户已安装APP之间的关联关系, 以充分发挥Wide的记忆能力, 影响最终的得分。
    • 这部分是L1正则化的FTRL优化器, 可能是因为这两个id类特征向量组合, 在维度爆炸的同时, 会让原本已经非常稀疏的multihot特征向量变得更加稀疏。 因此采用FTRL过滤掉那些稀疏特征是非常好的工程经验。
    • Wide部分组合“已安装应用”和 “曝光应用”两个特征的函数被称为交叉 积 变 换 (Cross Product Transformation ) 函数。
  • 两者结合: 在通过交叉积变换层操作完成特征组合之后,Wide部分将组合特征输入最 终的LogLoss输出层,与 Deep部分的输出一同参与最后的目标拟合,完成Wide 与 Deep部分的融合。

这个就是Wide&Deep模型的全貌了, 该模型开创了组合模型的构造方法, 对深度学习推荐模型的发展产生了重大的影响。

下面就用pytorch搭建一个Wide&Deep模型, 完成一个电子商品推荐的任务。

这是工业上常用的一个模型, 下面整理一些工业上使用的经验:

  • 像上面说的,这个模型的wide和deep端接收的特征是不一样的, wide端一般会接收一些重要的交互特征,高维的稀疏离散特征, 而deep端接收的是一些连续特征
  • 这两端用的梯度下降的方式不一样, wide段用的是那种带有L1正则的那种方式,L1有特征选择的作用, 注重稀疏性些, deep端用的就是普通的梯度下降方式,带L2正则
  • wide部分是直接与输出连着的, 这个其实和ResNet的那种原理有点像, 知识在某种程度上都是想通的。
  • Wide&Deep是一种架构,不是说一定非得是这样的形式, 具体要跟着具体业务来, 还得进行扩展,比如某些特征,既不适合wide也不适合deep,而是适合FM,那就把这部分特征过一个FM, 和wide deep端的输出拼起来得到最后的输出,其实是可以任意改造的。

三、模型复现

import torch.nn as nn
import torch
 
class Linear(nn.Module):
    """
    Linear part
    """
 
    def __init__(self, input_dim):
        super(Linear, self).__init__()
        self.linear = nn.Linear(in_features=input_dim, out_features=1)
 
    def forward(self, x):
        return self.linear(x)
 
 
class Dnn(nn.Module):
    """
    Dnn part
    """
 
    def __init__(self, hidden_units, dropout=0.):
        """
        hidden_units: 列表, 每个元素表示每一层的神经单元个数, 比如[256, 128, 64], 两层网络, 第一层神经单元128, 第二层64, 第一个维度是输入维度
        dropout: 失活率
        """
        super(Dnn, self).__init__()
 
        self.dnn_network = nn.ModuleList(
            [nn.Linear(layer[0], layer[1]) for layer in list(zip(hidden_units[:-1], hidden_units[1:]))])
        self.dropout = nn.Dropout(p=dropout)
 
    def forward(self, x):
        for linear in self.dnn_network:
            x = linear(x)
            x = F.relu(x)
 
        x = self.dropout(x)
        return x
 
 
class WideDeep(nn.Module):
    def __init__(self, feature_columns, hidden_units, dnn_dropout=0.):
        super(WideDeep, 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.linear = Linear(len(self.dense_feature_cols))
        self.final_linear = nn.Linear(hidden_units[-1], 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)
 
        dnn_input = torch.cat([sparse_embeds, dense_input], axis=-1)
 
        # Wide
        wide_out = self.linear(dense_input)
 
        # Deep
        deep_out = self.dnn_network(dnn_input)
        deep_out = self.final_linear(deep_out)
 
        # out
        outputs = F.sigmoid(0.5 * (wide_out + deep_out))
 
        return outputs



参考资料:
推荐系统入门(四):Wide&Deep(附代码)
推荐算法之Wide&Deep模型
【深度学习推荐模型】Deep&Cross模型 —— Wide&Deep模型的进化,高效的高阶特征交叉
AI上推荐 之 Wide&Deep与Deep&Cross模型(记忆与泛化并存的华丽转身)
谷歌一出马全都不能打:Wide&Deep以及Deep&Cross
推荐系统6–Wide&Deep与Deep&Cross模型(综合原始特征及交叉特征)
Wide & Deep模型
Wide & Deep 模型
深度推荐模型 Wide&Deep (附代码)Wang-程序员宅基地
Wide & Deep 模型
wide & deep
推荐系统6–Wide&Deep与Deep&Cross模型(综合原始特征及交叉特征)
wide&deep模型中如何确定哪些特征适用于wide侧哪些特征适用于deep侧?
推荐系统经典模型 Wide & Deep 详解 (全网之最)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值