王喆《深度学习推荐系统》
文章出处:https://zhongqiang.blog.csdn.net/article/details/108948440
1. 前言
今天是推荐系统深度学习模型的第一篇,会整理两个比较简单的神经网络模型AutoRec和DeepCrossing模型, 这两个模型的进化方式是增加了深度神经网络的层数和结构复杂度。 前者是将深度学习的思想用于推荐系统的初步尝试, 后者是一次深度学习框架在推荐系统中的完整应用。我们会先从最简单的神经网络模型AutoRec开始, 介绍其相关原理和局限性, 这个模型2015年由澳大利亚国立大学提出, 是个单隐层的神经网络推荐模型,网络结构简单,非常适合深度学习推荐系统的入门模型,但由于比较简单, 表达能力不足, 并没有真正的被应用。 在2016年,微软基于ResNet的经典DNN结构, 提出了DeepCrossing模型, 该模型完整的解决了从特征工程、稀疏向量稠密化, 多层神经网络进行优化目标拟合等一系列深度学习在推荐系统中的应用问题, 为后面研究打下了良好的基础, 这个模型也是本篇内容的重点部分, 首先会介绍该模型的原理, 结构特点, 然后进行网络的复现, 完成一个点击率预测的任务。
大纲如下:
- AutoRec的结构及推荐原理
- DeepCrossing模型原理部分
- DeepCrossing模型的代码实践
- 总结
Ok, let’s go!
2. AutoRec的结构及推荐原理
这是2015年由澳大利亚国立大学提出来的将自编码器的思想和协同过滤结合起来的一种单隐层的神经网络推荐模型。 它的基本原理是利用协同过滤中的共现矩阵, 完成物品向量或者用户向量的自编码。 再利用自编码的结果得到用户对物品的预估评分, 进而进行的推荐排序。
关于自编码器的知识, 这里不详细整理, 可以参考王喆老师的书, 简单说一下重点, 自编码器指能够完成数据“自编码”的模型。 这貌似是句废话。 这是一个单隐层的神经网络。长下面这个样子:
自编码器输出层的节点数与输入层相等,训练这个网络以期望得到近似恒等函数, 假设数据向量是r, 自编码器的作用是将向量r作为输入, 通过自编码器, 得到的输出向量尽量接近其本身。
那么AutoRec模型的结构以及如何利用重建函数得到最终推荐列表呢? 这就是AutoRec的核心内容了。
2.1 AutoRec模型的结构
AutoRec使用单隐层神经网络结构来解决重建函数的问题。 模型的结构图如下(该图来自《深度学习推荐系统》:
为防止过拟合, 还加入了L2正则, 即最终的目标函数为:
但我们要明白, 这个东西训练完了之后, 当我输入一个物品向量的时候, 经过这三层网络, 可以得到一个和输入向量维度相同的向量, 但是和输入向量又不太一样, 毕竟这个输出是衡量了所有的输入, 尽可能的使得对于所有的输入, 都进行重建, 这样模型有一定的泛化能力了。
那么我们得到了这个向量, 怎么用呢?
2.2 基于AutoRec模型的推荐过程
和协同过滤一样, AutoRec也分为基于物品的和基于用户的, 上面这个就是基于物品的, 称为I-AutoRec。 如果把用户的评分向量作为输入, 就得到了U-AutoRec。 这个输入之后得到的输出, 就是用户对所有物品的一个评分。 这样就直接可以根据这个评分做推荐了。 但是往往用户向量会比较稀疏, 可能会影响模型效果。
AutoRec模型由于结构比较简单, 表达能力不足,并且往往共现矩阵非常的稀疏, 更加加大了模型的预测难度。 所以这个东西是将深度学习思想应用于推荐系统的初步尝试, 拉开了使用深度学习思想解决推荐问题的序幕。But, 没有真正的投入到实践。
3. DeepCrossing模型原理
这个模型就是一个真正的把深度学习架构应用于推荐系统中的模型了, 2016年由微软提出, 完整的解决了特征工程、稀疏向量稠密化, 多层神经网络进行优化目标拟合等一系列深度学习再推荐系统的应用问题。 这个模型涉及到的技术比较基础,在传统神经网络的基础上加入了embedding, 残差连接等思想, 且结构比较简单, 对初学者复现和学习都比较友好。
DeepCrossing模型应用场景是微软搜索引擎Bing中的搜索广告推荐, 用户在输入搜索词之后, 搜索引擎除了返回相关结果, 还返回与搜索词相关的广告,Deep Crossing的优化目标就是预测对于某一广告, 用户是否会点击, 依然是点击率预测的一个问题。
这种场景下, 我们的输入一般会有类别型特征, 比如广告id, 和数值型特征, 比如广告预算,两种情况。 对于类别型特征, 我们需要进行one-hot编码处理, 而数值型特征, 一般需要进行归一化处理, 这样算是把数据进行了一个简单清洗。 DeepCrossing模型就是利用这些特征向量进行CTR预估,那么它的结构长啥样, 又是怎么做CTR预估的呢? 这又是DeepCrossing的核心内容。
3.1 DeepCrossing模型的网络结构
为了完成端到端的训练, DeepCrossing模型要在内部网络结构中解决如下问题:
- 离散类特征编码后过于稀疏, 不利于直接输入神经网络训练, 需要解决稀疏特征向量稠密化的问题
- 如何解决特征自动交叉组合的问题
- 如何在输出层中达成问题设定的优化目标
DeepCrossing分别设置了不同神经网络层解决上述问题。 模型结构如下:
下面分别介绍一下各层的作用:
Embedding层: 将稀疏的类别型特征转成稠密的Embedding向量,Embedding的维度会远小于原始的系数特征向量。 Embedding是NLP里面常用的一种技术, 之前主要是用来把单词进行embedding, 这里我是第一次看到和在普通的类别数据上使用embedding。待会代码实践的时候, 会发现有些代码实践上的差别。这里的Feature #1表示的类别特征(one-hot编码后的稀疏特征向量), Feature #2是数值型特征, 不用embedding, 直接到了Stacking层。 关于Embedding层的实现, 往往一个全连接层即可, Pytorch中有实现好的层可以直接用。 而关于推荐系统中Embedding的不同方法, 后面会结合王喆老师书上的内容单独整理一下。但和NLP里面的embedding技术异曲同工, 比如Word2Vec, 语言模型等。
Stacking层:这个层是把不同的Embedding特征和数值型特征拼接在一起, 形成新的包含全部特征的特征向量, 该层通常也称为连接层
Multiple Residual Units层: 该层的主要结构是多层感知机, 但DeepCrossing采用了残差网络进行的连接, 关于残差网络, 这里不做过多的介绍, 具体实现的时候, 会提下过程。通过多层残差网络对特征向量各个维度充分的交叉组合, 使得模型能够抓取更多的非线性特征和组合特征信息, 增加模型的表达能力。
Scoring层: 这个作为输出层, 为了拟合优化目标存在。 对于CTR预估二分类问题, Scoring往往采用逻辑回归, 对于多分类, 往往采用Softmax模型
这就是DeepCrossing的结构了, 比较清晰和简单, 没有引入特殊的模型结构, 只是常规的Embedding+多层神经网络。但这个网络模型的出现, 有革命意义。 DeepCrossing模型中没有任何人工特征工程的参与, 只需要清洗一下, 原始特征经Embedding后输入神经网络层, 自主交叉和学习。 相比于FM, FFM只具备二阶特征交叉能力的模型, DeepCrossing可以通过调整神经网络的深度进行特征之间的“深度交叉”, 这也是Deep Crossing名称的由来。 了解一下。
顺便说一下, 这个模型由于都是常规的结构, 对于小白复现起来比较友好, 所以这是一个适合复现练手的模型,后面就是复现这个模型, 然后完成一个点击率预测的任务。
4. DeepCrossing代码实践
这里是尝试把这个模型复现一下, 来完成一个简单的任务, 为了和前面保持统一, 这里实验依然用的Criteo数据集, 但由于数据量太大, 做了采样, 取了一部分。 关于数据的预处理, 就是读入, 做了简单的缺失值填充。 具体的可以参考下面的GitHub链接, 这里主要是看看这个模型的复现过程, 我这里是参考了下面子耀TF2.0的代码用Pytorch写的, 一方面是刚刚学习完Pytorch, 想找个机会练手, 而这个模型刚好对小白友好。 另一方面也是为了自己动手走一遍, 熟悉上面的处理流程。
Pytorch进行模型的训练分为四个步骤: 准备数据, 建立模型, 训练模型,使用和保存。
关于数据准备, 导入数据清洗, 然后封装成DataLoader结构即可。这里主要是模型建立部分的一些细节解释。首先, 如果建立这模型, 模型结构图再放一遍:
我们下面看看如何构建这个模型, 首先, 会有embedding层, 这个由于不同的类别特征会通过不同的embedding, 且每个类别的取值个数不一样, 所以有多少个类别特征就需要多少次embedding。 所以这里就有了第一个细节, 类别特征的编码其实用的LabelEncoder而不是One-hotEncoder, 我觉得原因就是LabelEncoder就类似于把每个类别下的不同取值映射到了一个字典里面去。 通过这个LabelEncoder的取值, 就可以直接拿出对应的embedding向量来。Pytorch中, 实现这一层, 需要用一个ModuleDict
来实现, 里面的每个值都是embedding层。这里出现了第二个细节, 就是由于每个类别的取值不一样, 需要实现先把每个特征的取值个数记录下来, 这样是embedding的输入维度。在NLP中, 我们是先将单词建立成一个字典, 字典的维度就是embedding的输入维度, 而这里, 我们用LabelEncoder的方式代替了字典, 而类别的取值个数就是输入维度, 异曲同工的。
然后就是残差层, 因为Stacking不需要特殊的层, 只需要把特征拼接即可。 残差层其实就是实现了神经网络的运算过程, 只不过稍微有点不同的是残差网络加了跳远链接:
所以我们需要先定义一个这样的残差块网络结构, 看这个结构, 两层线性可以用两个nn.Linear来搞定, 剩下的就是跳远那部分, 在前向传播的时候加过去即可。 代码如下:
1 | class Residual_block(nn.Module): |
这是上面残差块的实现, 而实际应用中, 我们可能很多个这样的残差块结构, 下面我们用了三个, 每一个里面神经单元的个数不一样。 所以用了ModuleList
,然后加了一层Dropout层缓解过拟合, 最后一个线性层加sigmoid完成scoring层的实现。 具体代码如下:
1 | # 定义deep Crossing 网络 |
应该挺好理解的, 前向传播部分的逻辑, 首先得先把输入向量X分成两部分处理, 因为数值型和类别型的处理方式不一样, 类别型经过embedding, 数值型是直接进入stacking。 所以这是前向传播的第一行代码。 而由于Pytorch中, embedding层输入要求是long类型, 转一下, 这是第二行代码。 第三行代码就是不同的类别特征分别embedding。 第四行代码是把类别型特征进行拼接, 第五行代码是数值型和类别型拼接, 这就是stacking层的任务。 后面就是经过残差网络, sigmoid激活输出。
这样就完成了DeepCrossing模型的搭建。 关于训练过程, 我这里用了脚本式的训练风格, 顺便复习了一下Pytorch的训练的整个流程。具体细节参考链接吧。如果对TF2.0感兴趣, 也可以参考下面2.0的复现链接, 这里面是各个推荐系统的TF2.0实现, 现在正尝试跟着大佬的步伐用Pytorch走一遍这些模型, 并结合博客进行整理理论知识。
5. 总结
这里简单的总结一下, 由于这篇文章是深度学习模型的第一篇文章, 所以相对来说比较简单, 不是太复杂,这篇文章主要整理了两个最基本的模型AutoRec和DeepCrossing模型。 前者是一个简单的自编码器神经网络, 但表达能力不强, 没有被使用。 后者是深度学习框架在推荐系统的一次完整应用, 用的是比较传统的模块, 但自主完成了稀疏变稠密, 深度交叉特征学习等, 为后面的深度学习的研究奠定了基础, 这两个模型都是从改变神经网络的复杂程度上进行了进化。 本篇文章首先从AutoRec开始, 介绍了其基本原理, 在推荐中如何使用, 然后分析了局限, 引出了DeepCrossing模型, 介绍原理和结构并尝试复现, 具体实验代码可以参考下面的链接。
下一次的两个模型, 从改变特征交叉方式方式的角度完成了进化, 一个是改变了用户向量和物品向量互操作方式的神经网络协同过滤(NeuralCF), 一个是定义了多种特征向量交叉操作的PNN(Product-based Neural Network, 基于积操作的神经网络)模型。这个模型还是采用了DeepCrossing的模型堆叠结构, 只不过把Stacking层换成了Product 层。 具体的下一篇文章进行整理了。
参考:
- 王喆 - 《深度学习推荐系统》
- Deep Crossing: Web-Scale Modeling without Manually Crafted Combinatorial Features论文原文
- Autoencoders meet collaborative filtering论文原文
- DeepCrossing模型的论文导读
- DeepCrossing模型的TF2.0复现
整理这篇文章的同时, 也刚建立了一个GitHub项目, 准备后面把各种主流的推荐模型复现一遍,并用通俗易懂的语言进行注释和逻辑整理, 今天的DeepCrossing模型已经上传, 参考的TF2.0的复现过程, 写成了Pytorch代码, 感兴趣的可以看一下 😉
筋斗云:https://github.com/zhongqiangwu960812/AI-RecommenderSystem