YouTube DNN论文精读

目录

Abstract

YouTube DNN代表了现存的最大规模和最复杂的个性化推荐系统。在这篇文章中, 我们从一个很高的角度描述了整个系统的结构并且集中介绍了深度学习给推荐系统带来的巨大提升。 这篇文章根据拆分成为采用传统的检索的两段式:

  • 首先我们介绍了一个候选集生成模型。

  • 然后我们介绍了一个不同的排序模型。

在论文中我们还提供设计、迭代和维护一个面对用户的大规模推荐系统的经验和见解。

1.Introduction

YouTube DNN是全世界最大的视频创作、分享、发现平台。 YouTube推荐系统负责帮助亿万级用户从日益增长的视频语料中发现个性化的内容。 在这篇文章中,我们主要介绍深度学习对YouTube DNN推荐带来的巨大影响。 在图1中给出了YouTube DNN的主页情况。

在YouTube DNN的视频推荐中主要存在以下三个问题:

  • Scale: 许多已经存在的算法在小数据集上能够跑通, 但是在我们的数据规模上无法有效运行。 高度专业化的分布式学习算法和高效的服务系统 对于处理YouTube庞大的用户群和语料库至关重要。

  • Freshness: YouTube有一个大规模的动态语料库,每分秒钟有几个小时的视频上传。 推荐系统应该在尽可能少的用户行为下能够对新上传的内容作出较快的响应。 系统能够通过exploration和exploitation来平衡新内容和已经验证过的内容。

  • Noise: 由于用户行为的不可观测性和用户行为的稀疏性使得用户行为本身就难以被理解。 我们很难能够获得用户满意度的真实表达,更多的是对有噪声的隐式反馈信号进行建模。 此外,与内容相关联的元数据在没有良好定义实体的情况下很难被结构化。 我们的算法需要对我们这种特殊的数据集具有鲁棒性。

在谷歌的产品矩阵中,YouTube经历一个根本性的范式转变, 使得深度学习成为解决所有问题的通用手段。 我们在谷歌大脑上构建了我们的模型,最近开源成了TensorFlow。 TensorFlow提供了一个合适的框架来使用大规模数据实验各种深度神经网络结构。 我们的模型学习了大约在上亿的样本上学习了大约一亿的参数。

与矩阵分解方法的大量研究相反, 在推荐系统中使用深度神经网络的方法相关的工作较少。 深度神经网络在论文[17]中用于推荐新闻,在论文[8]中用户推荐论文, 在[20]中用于review评分。在论文[22]中协同过滤被公式化为深度神经网络, 在论文[18]中被公式化为自动编码器。Elkahky等人使用深度神经网络学习 对跨领域的用户进行建模。在内容推荐领域里面, Burges等人使用深度网络做音乐推荐。

我们的论文组织结构如下:

在第二节中我们对推荐系统做了一个简要地概述, 第三节中我们描述了推荐系统中候选集生成的具体细节, 包括如何训练以及推荐系统服务的部署。 在第四小节中介绍了排序模型的细节,包括如何使用logistic regression对播放时长进行建模(而不是点击率)。 实验结果将显示隐藏层深度在这种情况下对实验结果有帮助。 最后,在第5小节中,我们给出了我们的结论和经验教训。

2.System Overview

在图2中,我们给出了整个系统的结构,整个系统由两个神经网络组成: 一个生成候选集另外一个对用户召回结果进行排序。

候选集生成网络以YouTube的用户行为历史作为输入, 从大规模的视频语料中挑选一个小的子集(百级别的)。 这些候选集基本上跟用户的具有较高的相关性。 候选集生成网络通过协同过滤提供广泛的个性化, 用户之间的相似性用粗糙的特征来表示,例如观看视频的id, 查询的历史以及人口统计信息。

给出的一个最好的几个视频的候选列表中, 需要一个精细的表示来区分候选视频之间相对的重要性。 排序网络通过使用丰富的用户特征和行为特征设计一个目标函数 来完成这项任务。最后,通过得分对这些视频进行排序, 得分最高的视频将被提供给这些用户。

这两个过程的方法能够使得我们从大规模语料库中给用户推荐视频, 同时保证那一小部分视频呈现给用户的时候具有较高的准确性。 此外这种方法能够混合其他来源的候选项,如我们在早期的工作中描述的那样。

在开发的过程中,我们广泛地使用离线的评估指标例如(precision, recall, ranking loss等等) 来指导模型在我们的系统上的迭代,然而最后决定模型是否有效,我们取决于线上的AB实验。

在线上实验当中,我们可以测量点击率、观看时长和许多其他衡量用户参与度的指标的细微变化。 这是非常重要的,因为实时的AB测试并总是和离线结果相关。

3.Candidate Generation

在候选集生成阶段,需要从大规模的候选集中筛选出成百上千的与用户相关的视频。 在文章[23]中描述了一个基于矩阵分解的推荐方法。 我们早期的迭代模拟这种因子分解的方式采用浅层的神经网络对用户行为做嵌入。

从这个角度上来看,我们的方法可以看成是一种因式分解技术的通用推广。

3.1 Recommendation as classification

我们可以将候选集生成的过程看成是一个极端的多分类问题。 那么这个时候问题就转化成了一个特殊的分类准确性问题。 一个用户$U$, 上下文为$V$的用户分类准确性的问题。

\[P(w_t=i|U,C) = \frac{e^{v_iu}}{\sum_{j \in V}e^{v_ju}}\]

其中$u\in R^N$表示用户和场景对的高维embedding,$v_j \in R^N$ 表示每个候选的视频。在这种情况下,embedding只是将稀疏的实体转化为了$R^N$中的稠密向量。

深度神经网络的任务是根据用户的历史和上下文来学习用户的嵌入, 使得使用softmax分类器来区分视频是有用的。虽然在YouTube DNN 中存在显式反馈的机制,但是我们仍然使用用户的隐式播放来训练模型。 当用户看完了一个视频,当成是一个正样本。 我们做这样选择的原因是采用隐式反馈具有更大的数据量级来完成显式 反馈稀疏的情况之下的深度推荐。

  • 高效的多分类问题

为了高效地训练具有成百万分类的模型, 我们依赖一个技术从back-ground分布中采样负样本, 然后通过重要性采样校正这个数据。对于每一个样本, 需要对正样本和负样本计算最小化的交叉熵损失。 在真实的训练中,我们采样了几千个负样本, 那么对应整个候选集来看相比于传统的softmax做了100倍以上的加速。 另外的可供选择的加速方法是采用分层的softmax,但是在softmax 中我们无法达到相当的准确度。在遍历分层softmax的时候, 遍历每个节点的时候通常涉及到区分通常不相关的分类集合, 这增加了分类的难度,从而降低了分类的性能。

在serving阶段,我们需要计算最可能的N个分类, 来挑选出最可能的top N个视频展现给用户。 在数十毫秒的严格服务延时下对成百万规模的物品进行评分 需要一个次线性的近似得分计算方案。 YouTube以前的系统依赖于哈希并且在文章[24]中提供了一个相似度计算的方法。 由于在服务阶段我们只需要一个相对的排序, 不需要在来自softmax输出层的校准似然性, 评分问题可以简化为内积空间中的近邻检索问题。 在论文[12]中给出了一个通用的向量检索库。 我们AB测试的结果对线上使用的近邻检索方法并不是很敏感。

3.2 Model Architecture

受到CBOW模型的鼓舞,我们在一个固定的词汇表中学习到每一个视频的embedding然后 喂入到一个前向的神经网络。 用户的观影历史被描述成为一个变长的由稀疏视频id组成的变长序列, 这些视频vid通过嵌入映射成一个稠密的向量表示。 神经网络需要等长稠密向量输入,在这我们采用简单的对embedding求平均的方式。 求平均的方式是几种(sum, component-wise max, 等等)策略里面效果最好的。 重要的是这些向量通过正常的反向传播进行梯度的更新。 所有的特征首先在第一层做一个拼接,然后紧跟着使用几个带ReLU激活函数的全连接层。 图3中给出了包含非视频观看的YouTube DNN的模型结构。

##3.3 Heterogeneous Signals

使用深度神经网络作为一个一般化的矩阵分解方法有一个重要的优点, 任意的连续的和类别特征都可以很容易地加入到模型当中去。 用户的搜索历史采用和用户的播放历史相同的嵌入方式。 每一个查询都被标记成unigrams和bigrams并且每一个embedding都被嵌入。 在进行平均之后,一个用户查询的embedding表示用户的稠密搜索历史的总结。 人口统计特征对于提供优先级次序来说非常重要,这样对于新用户就能够进行合理的推荐。 用户的地理位置和设备信息被嵌入和concat到特征表示当中去。 简单的一些二进制特征和连续特征例如用户性别、登录状态和年龄在网络中直接 被归一化[0,1]之间的真实值。

  • Example Age Feature

在YouTube上每秒有很多个小时的视频上传。推荐这些新鲜的视频对于YouTube 这个推荐产品而言是十分重要的。我们观察到用户用户更喜欢新的内容, 尽管是以牺牲相关性为代价。此外,除了推荐用户感兴趣的新视频这个直接目的之外, 还有一个次要的目的就是引导和传播内容信息。

机器学习系统通常对于过去建立了一个隐含的偏差。 因为机器学习系统通过学习过去的样本来训练模型。 视频的热度分布是非常不稳定的,但是我们的推荐器 在训练语料上的多项式分布将反应几个星期的训练窗口中的平均观看可能性。 为了纠正这种情况,我们将训练样本的上传时间作为训练当中的一个特征输入。 在serving的阶段这个特征会被设置为0(或者轻微的负数)来反应模型在训练窗口 的最末端进行预测。

图4展示了这种方法在任意选择的视频上的效果,在图中分别给出了BaseLine Model, With Example Age以及Empirical Distribution三个的分布。

其中Empirical Distribution表示给出的随机某个视频热度的经验分布, With Example Age表示使用了Example Age属性之后输入不同Example对这个类别概率的预测情况。 BaseLine模型表示不适用BaseLine的情况, 因此使用BaseLine Model只能得到训练窗口下的平均类别概率。

根据图4中的结果,我们发现在使用了Example Age之后我们能够明显的预测视频的热度分布。

3.4 Label and Context Selection

需要强调的一个点是推荐通常涉及到解决一个代理问题, 并且将这个结果转到特定的场景下。在推荐领域里面一个经典的假设是这个样子的: 准确的得分预测会带来高效地电影推荐。

我们代理学习问题的选择对线上的A/B测试性能的影响非常重要但是离线评估的时候非常困难。 训练的样本来自YouTube的所有观看(及时这些embeeded在其他站点上), 而不仅仅是在我们的推荐产品当中,否则新的内容将很难出现, 推荐系统将更多地偏向于利用。如果用户通过推荐之外的其他渠道发现了我们的视频, 我们希望能够通过协同过滤的方式快速递推荐给其他用户。

提升评估指标的另外一个方式就是给用户生成固定长度的训练样本, 这使得我们的用户在损失函数中拥有同等的权重。 这种方法防止了高度活跃的用户对损失的影响比较高。

值得注意的是,我们需要非常小心地从分类器中预留出信息, 以防止模型利用站点的结构过渡拟合了代理问题。 我们来考虑这么一个例子:用户刚刚发布了”泰勒·斯威夫特”的搜索查询。 由于我们的问题是预测下一个观看的视频, 给定该信息推荐系统将预测的最可能观看的视频是那些出现在”泰勒·斯威夫特”的相应搜索结果页面上的视频。 毫无疑问,复制用户最后搜索页面的结果作为主页的推荐表现很差。 通过丢弃序列信息并通过无序的令牌包来表示查询结果, 分类器不再需要直接指导原始的标签来源信息。

视频的自然消费模式,通常会导致非常不对等的共同观看概率。 情节系列通常是按照顺序观看的, 用户如果发现某个流派中比较小众的艺术家通常是从流行的艺术家开始的。 因此,我们发现预测用户的下一个观看视频比随机预测留下出的视频性能要好。 许多协同过滤系统通过保持一个随机项目并且从用户历史中的其他项目预测 这个留出项来隐含地选择标签和上下文。这种方式泄露了未来的信息, 并且忽略了推荐中的过去和未来的非对称消费模式。

相比之下,我们通过随机地选择一个观看的视频来回溯用户的观影历史, 并且只输入用户在观看视频之前的动作。

3.5 Experiments with Features and Depth

根据图6中的结果,添加特征和神经网络的深度明显地提高了模型的精度。 在实验中,我们采用了1百万的词汇表和1百万的查询token进行实验, 并且嵌入到256维的浮点向量。在用户行为的选取中, 最大选用了最近的50个观影观影历史和50个检索的token。 在softmax层输出一个相同长度为1百万维度的视频分类输出一个256维的多项分布, (这个可以看成是独立的输出向量)。模型在YouTube所有的用户数据上进行多轮的迭代, 直到模型发生收敛。网络结构遵循一种常见的“塔”型结构,其中网络的底部最宽, 每个连续的隐藏层将单元数量减半。网络结构遵循常见的塔型结构, 最顶层实际上是一个高效的线性因子分解方案,执行了一个和之前的系统类似的操作。 随着网络的宽度和深度不断增加,直到对增量的效益减少,收敛变得十分困难为止。

  • Depth 0: A linear layer simply transforms the concatenation layer to match the softmax dimension of 256

  • Depth 1: 256 ReLU

  • Depth 2: 512 ReLU -> 256 ReLU

  • Depth 3: 1024 ReLU -> 512 ReLU -> 256 ReLU

  • Depth 4: 2048 ReLU -> 1024 ReLU -> 512 ReLU -> 256 ReLU

4. RANKING

排序阶段的主要作用是采用用户感兴趣的数据来校准特定用户界面的候选集排序。 例如,用户通常有比较高的可能性观看某个给定的视频,但是在主页的缩略图场景下, 用户对这个视频的点击并不是很强烈。在排序阶段, 我们可以使用更多描述用户与视频关系的特征。 因为这个时候只有少量的视频需要被打分而不是候选集生成阶段百万量级的视频被打分。 排序对于集合分数不可以直接比较的不同候选集源也至关重要。

我们使用和候选集生成阶段相同的网络结构使用LR对每个视频给予单独的得分。 然后这个视频列表通过排序打分并且返回给用户。 我们最终的排序目标是根据线上A/B测试的结果来调整的, 但是基本上是一个关于每个视频预期观看时间的简单函数。 通过点击率进行排序通常会促进用户点击那些欺骗性(标题党和封面党)的视频, 但是观看时间更能捕捉到用户的参与度。

4.1 Feature Representation

我们的特征和传统的分类特征和连续特征有比较大的区分。 我们使用的分类特征基数差异很大,有些是二进制的(例如,用户是否登录) 而另外一些是有数百万个可能的值(例如,用户的最后一个查询结果。) 这些特征根据他们贡献了多个值还是一个制分为多阶特征和一阶特征。 一个很明显的一阶特征就是这个视频是否被点击, 而对应的多阶特征就是用户最后观看的 $N$ 个视频ID。 我们还根据特征是否描述了物品的属性或者用户属性和场景属性来进行区分。 查询特征需要在每次请求的时候都计算一次,并且需要针对每个物品计算物品得分。

  • Feature Engineering

一般来看我们在我们的排序模型里使用上百个的特征, 这些特征大致地分为分类特征和连续特征。 虽然深度学习有望减轻手工特征工程的负担, 但是原始数据的性质并不容易直接输入到前馈神经网络当中去。

我们仍然需要做大量工程转化来讲用户和视频数据转化成有用的特征。 这个问题的主要困难在于如何表示一个时间序列, 并且如何表示这些行为和被评分视频的关系。

我们观察到那些最重要的特征信号就是描述了用户之前与这个item有关交互的其他相似信号。 和他们在广告排序当中的经验相类似。例如考虑用户光影历史里面的类别, 这个用户过去观看了多少和这个视频相同的类别。上一次用户观看这个类别的时间。 这些描述用户在相关项目上的连续特征是非常强大的, 因为这些特征可以很好地在不同的项目之间进行归纳。 同样的我们发现从候选集将特征传播到排序阶段是非常重要的。 例如:例如哪些召回源召回了这个候选集,对应的召回源对着个视频给了多少打分。

描述过去观看了的视频频率的特征在推荐系统引入变动的时候也是至关重要的 (连续的请求不同返回相同的结果)。如果一个用户最近被推荐了某个视频, 但是没有被观看,那么这个模型自然会在下一次加载页面的时候降低这种推荐现象。 如何使用最新的印象和观影历史本身是一个很大的工程,这已经超出了本文的讨论范围。 但是这对于产品响应来说至关重要。

  • Embedding Categorical Features

与候选集生成阶段相似,对于稀疏特征我们将这些特征映射到一个稠密的向量表示当中。 在ID空间(也就是词汇表中)每一个ID学习到一个对应维度的向量, 向量的维度随着特征维数的增加而增加大概是维数值的对数成比例。 这些查找表是在训练前一次性传入数据的简单查找表。 基数非常大的ID空间(例如,视频的ID或者查询标识)会按照频次排序, 然后对整个表示空间截取前面top N个标识。至于超出词汇表的部分, embedding都设置成为0。与候选集生成网络相同, 多阶特征在被送入到神经网络当中之前通过加权求平均处理。

重要的是,同一个ID空间中的分类特征也共享相同的embedding。 例如:一个单独的在全局视频ID嵌入可以在许多特征中被使用。 (用户观看、最后一次观看的视频ID等等。) 尽管共享了embedding,但是每个特征都是单独fed到网络当中去的, 因此神经网络可以单独地学习到每个特征的专门表示。 共享嵌入对于提高泛化能力,加快训练速度和减低内存使用都非常重要。 对于排序和召回模型而言绝大多数参数都在这些具有较高基数的嵌入向量当中, 例如:一百万的ID嵌入到一个32维的空间向量当中的参数是一个 有2048个神经元的全连接层参数的7倍。

  • Normalizing Continuous Features

众所周知神经网络对数据的缩放和分布十分敏感, 而其他的方法对于特征的缩放是不变的, 例如决策树相关的算法。 我们发现对于连续特征适当地做一些归一化对于模型的收敛来说是至关重要的。 在排序模型中,我们将原本具有连续分布$f$的特征 $x$转化为 $\widetilde{x} = \int_{-\infty}^{x} df$ 在开始训练之前, 通过对特征进行预处理并采用欧冠线性插值近似地计算每个特征值对应的积分。

除了在模型中输入$\widetilde{x}$的值之外, 我们还使用了$\widetilde{x}^2$和$\sqrt{\widetilde{x}}$, 作为输入使得网络具有更强的表征能力能够建立这个关于这个特征次线性和亚线性的函数。

4.2 Modeling Expected Watch Time

在排序阶段,我们的模型预测的不是正例(视频曝光并且被点击) 和负例(视频曝光但是没有被点击)。在排序阶段, 正例用用户观看的时间长短来表示。为了预测可能的播放时长, 我们使用加权求平均的回归方法。如图7所示, 模型使用交叉熵作为损失函数。但是正向的曝光通过播放时长进行加权。 负向的曝光使用单位权重。这样的话通过逻辑回归得到的odd结果是一个 $\frac{\sum T_i}{N - k}$,其中N是训练的样本个数, K是正样本曝光,其中$T_i$是第$i$个曝光的播放时长。 假设正样例曝光很小(在我们的case中是true),近似等于 $E[T](1+P)$。 其中 $E[T]$ 表示预期的播放时长,其中 $P$ 表示点击率的概率。 因此如果当点击概率非常小的时候,乘积的结果接近播放时长。 在推断过程中,我们使用指数函数 $e^x$ 做最终的激活函数来生成这些odds,来逼近预估的播放时长。

4.3 Experiments with Hidden Layers

表1展示了我们在流出数据上,使用不同隐藏层结构得到结果。 在对应的每一个配置下给出的值(“weight, per-user loss”) 被视为吧所有的正样本(点击了的视频)和负样本(没有点击的视频) 给用户曝光展示在一个页面里面。 我们首先通过我们的模型对两种曝光的物品进行打分。 如果负样本比正样本拿到了更高的得分, 我们就认为正样本预期的观看时间被预测错误。 Weight, per-user loss是所有预测错误的视频的总体播放时长作为分子, 所有曝光视频的播放时长作为分母。 结果表明增加隐藏层的宽度和深度会改善召回的结果。 最后权衡了推断的CPU耗时,采用了1024->512->256的ReLU作为模型结构, 保持最好的结果,同时使得我们的CPU耗时保持在预算内。

在1024->512->256的网络结构里面, 我们尝试只用归一化的特征而不用他们的平方的时候,增加了0.2%的损失。 在相同的参数配置下,我们还训练了一个正负样本具有相同权重的模型, 这个比使用播放时长加权的损失提升了4.1%。

5 CONCLUSIONS

6 ACKNOWLEDGMENTS

图1    模型训练中的不同状态
L1正则和L2正则的区别

目录

在面试中经常会被问到L1正则和L2正则的区别是什么,今天我这边就来做一个简单的梳理。

这篇文章主要解决以下几个问题:

  1. 什么是regularization(正则)?
  2. 什么是L1正则,什么是L2正则? 怎么样计算这两种正则方式?
  3. 这两种正则方式的区别?
  4. 什么时候使用L1什么时候使用L2,使用这两种正则方式会导致什么样的模型效果?

基于这几个基本问题,我们开始这篇文章的讨论。在这篇文章的第一小节中, 主要会回答问题1以及问题1,第二小节主要回答问题2。在第三小节中对问题3和问题4的内容 做一些深入的探讨。

1 正则化

1. 什么是正则化

在开始了解什么是正则化之前,我们可以先来看一下为什么需要正则化?

在聊为什么需要正则化之前,那么我们先来聊一聊过拟合。 在机器学习的训练中可能会出现这样的问题:模型在训练集上的效果非常好, 但是在测试集上的效果很差。 这种情况,我们称之为过拟合。

如图1所示,我们给出了模型训练中的三种可能状态分别为:

欠拟合: 学习到的模型过于简单,没有办法很好的拟合数据真实分布。

恰好拟合: 学习到的模型能够很好的拟合数据情况。

过拟合: 学习到的模型过于复杂,完全拟合了训练数据,甚至连噪声数据都拟合了,相当于背下了答案。 在训练集上具有较好的效果,在测试集上效果较差。

图1    模型训练中的不同状态

过拟合换句话说就是随着模型复杂程度的增加,模型的训练误差不断减小, 但是在测试集上的误差并不是不断减小的。随着模型复杂的增加,在测试集 和训练集上的误差变化整体来看如图2。

图3    训练误差、测试误差与模型复杂度的关系

那么也就是说,当我们构建一个复杂模型尤其是构建具有较大参数规模的神经网络的时候, 很容易出现过拟合的现象。

正则化:是一种通过对学习算法进行轻微修改加入额外信息, 从而使得模型具有更好的泛化能力,从而减少过拟合的方法。 通过正则化可以提高模型在不可见数据(训练数据外的数据)上的性能。

1.2 正则化如何减少过拟合

我们已经介绍了模型训练能够学习到的三种可能状态:欠拟合(high bias), 拟合,过拟合(high variance)。

可是,过拟合是怎么产生的为什么能够通过正则化来解决过拟合。

过拟合发生的本质原因是由于监督学习问题的不确定性,这种不确定性来源于:从n个(线性无关) 方程中可以解n个变量,解n+1个变量会解不出。 那么在监督学习中数据(对应了方程)远远少于模型空间(对应了变量)。

在这种情况下可以把过拟合问题做如下拆分:

  1. 有限的数据集不能完全反应一个模型的好坏,然而我们不得不在有限的数据上挑选模型。 因此,我们完全有可能挑选到在模型训练上表现好但是在测试数据上表现很差的模型。

  2. 如果模型空间太大,会有很多的模型可供挑选,那么挑选到对的模型的机会会变小。

  3. 与此同时,如果我们要在训练上数据表现足够好。最直接的方法,就是选用足够大的模型空间, 从足够大的模型空间中挑选模型。否则如果模型空间太小,就不存在能够拟合数据很好的模型。

因此,如果我们要更好的拟合训练数据,就要挑选足够大的模型空间。 可是挑选足够大的模型空间之后,挑选到的模型又不一定适用于测试数据。 因为模型空间太大学出来的模型可能千奇百怪。

那么在这种情况下,能够用来缓解过拟合的方法有这么几种:

  1. 增大数据量

  2. 控制模型空间的复杂度

  3. 使用多个模型融合的结果

使用正则化相当于给方程的求解施加了约束条件,减小了模型空间的复杂度。

2 L1正则和L2正则

2.1 L1正则化

在讨论L1正则的时候,我们需要考虑这么几个问题:

  • 1.什么是L1正则?
  • 2.L1正则有什么特点?
  • 3.什么情况下适合使用L1正则?

L1正则是指权值向量的绝对值之和,通常表示为 ${||w||}_1$ 在添加正则项的时候一般会在正则项前面添加一个系数$\alpha$

在使用L1做正则化的时候,更容易产生稀疏的权值矩阵, 也就是产生一个稀疏的模型,可以用来做特征选择。

在使用L1做正则化的时候,目标函数是非光滑的。对于非光滑的优化问题, 它的最优解要么是在导数为0的地方,要么在不可导的地方,也就是在各个角上。 对于对于L1正则而言,各个“角”对应的位置很多特征系数为0,所以L1会给出一个稀疏解。

根据L1正则化的特点,L0可以用来得到稀疏解,主要用来使得参数稀疏化做特征选择。

除了L1之外,我们在这里再聊一下L0,表示参数中非0值的个数,也能够使得参数 更为稀疏,但是在实际研究中由于L0很难求解,所以一般使用L1代替。

2.2 L2正则化

L2正则是指权值向量中各元素的平方和然后再求平方根, L2正则项通常表示为 $||w||_2$,L2正则通常用来防止过拟合。

那么为什么L2正则可以用来解决过拟合问题?

在拟合过程中,通常都倾向于让权值尽可能的小, 最后构造一个所有参数都比较小的模型。 一般认为参数值较小的模型比较简单,能适应不同的数据集, 也在一定程度上避免了过拟合现象。

例如,对于一个线性回归方程,若某个参数比较大, 只要稍微有一点点偏离就会对结果造成很大的影响, 但是如果参数足够小,数据的扰动带来的影响会小模型的健壮性会更强一下。

为什么L2可以获得很小的参数?

图4    L2正则为什么能使得参数变小

2.3 L1和L2正则的区别

  • 计算方式的不同

  • 求得参数矩阵权重的不同

  • 使用范围的不同

[1] An Overview of Regularization Techniques in Deep Learning (with Python code)

[2] What is the difference between L1 and L2 regularization? How does it solve the problem of overfitting? Which regularizer to use and when?

[3] 为什么L1和L2正则化可防止过拟合

[4] 深入理解L1、L2正则化

[5]

Wide & Deep Learning for Recommender Systems

摘要

具有非线性特征变换的一般线性模型被广泛用于具有稀疏特征输入的大规模回归和分类问题。 通过一个大的特征交叉乘积集合来记忆特征之间的交叉是非常有效并且可解释的,同时也意味着 我们需要做更多特征工程的工作。在使用更少的特征工程的情况下,深度神经网络一般可以通过从 稀疏的特征输入中学习低维的稠密embedding生成不可见的特征拼接。此外,通过Embedding,深度 神经网络可能过泛化,当用户和物品之间的交互非常稀疏并且秩较的情况下, 从而推荐出相关度较低的物品。

在本篇文章中,我们提出Wide&Deep学习模型,将训练一个宽的线性模型和深度神经网络相结合, 从而同时获得深度学习中的记忆和泛化的能力。我在一个具有上亿日活并且超过一百万的app 的商业手机软件Google Play上建立并且评估了我们的系统。 在线的结果显示相比于只使用wide模型和Deep模型的情况下, Wide&Deep明显地提高了app的购买率。当然,我们也有我们在TensorFlow中的模型实验。

介绍

一个推荐系统可以被看成是一个查询排序系统,系统的输入的查询序列是用户和场景信息的集合,输出 是被排序的物品的集合。给出一个Query,推荐系统的任务是找到数据库中相关的物品然后基于特定的 目标,例如购买和点击来对物品进行排序。

在推荐系统中面临的挑战和一般的搜索排序系统所要面对的问题是相同的,也就是我们需要如何实现记忆 和泛化。记忆可以松散地理解为学习物品之间的共同出现次数并且利用用户历史数据中的相关性。 另一反面泛化是基于相关地传递来探索以前从来没有出现或者是出现得很少的特征关联。 基于记忆的推荐通常来说更为热门并且一般来说跟用户的行为特性直接相关。与记忆相比泛化更趋向于 提高推荐结果的多样性。在这篇文章中,我们主要集中精力在Google play当中,但是我们的方法同样适用于 一般化的推荐系统。

对于工业界的大规模线上推荐和排序系统,广义的线性模型,如:逻辑回归等因其简单、可扩展并且可以解释 被大规模地使用。这样的模型通常在使用二进制编码的稀疏特征上进行训练。例如如果一个用户安装了Netflix 二进制特征user_installed_app=netflix的值等于1。记忆可以成功并且高效地利用稀疏特征上的交叉。 例如 AND(user_installed_app=netflix, impression_app=pandora)的值为1,表示这个用户安装了Netflix 然后给这个用户展示了了pandora。这解释了特征共现如何与目标的标签相关联。泛化可以通过使用更细粒度的特征 例如AND(user_installed_category=video, impression_category=music),但是这通常需要人工的特征工程。 特征交叉转换有一个缺陷就是没有办法查询在训练数据中没有出现的特征。

基于Embedding的模型例如因子分解机或者深度神经网络可以通过学习一个低维的稠密嵌入向量将 规律推广到没有出现过的query-item对,从而减少特征工程的工作量。然而,当query-item 的矩阵非常的稀疏并且维度很高的情况下,学习低维度的向量表示是非常困难的,例如具有特定偏好的用户 或者具有比较狭窄的用户兴趣的小众项目。在这种case的情况下,大部query-item的pairs里面基本 没有不会有交互信息,但是稠密的embedding对所有的query-items对会生成一个非0的结果。 因此这就会导致过度的一般化从而推荐出不相关的结果。同时在另外一方面,线性模型的特征交叉 转换可以通过更少的参数记住这些“期望的规则”。

在这篇文章中我们提出了Wide & Deep学习框架通过联合一个线性模型组件和一个深度模型组件 在一个模型里面实现模型的记忆和泛化,具体效果如图1所示。

在这篇文章中我们主要的贡献如下:

  • 对一般的具有稀疏特征的推荐系统我们提出了一种新的Wide & Deep学习框架, 在这种学习框架中我们将特征转换的线性模型和前馈神经网络的深度模型相结合。

  • 我们在Google Play这样具有上亿日活的app上实现并且评估了我们的Wide & Deep推荐系统。

  • 我们在TensorFlow的高阶API上有一个关于Wide & Deep的开源实现。

推荐系统回顾

在图2中给出了一个推荐系统APP的数据架构。当用户访问app store的时候, 在一个Query里面包含用户的场用户变量和场景特征。推荐系统返回一系列用户更有可能点击和购买 的app列表,用户关于这个queries的行为我们会在线上的日志里面记录下来,记录的日志也就是我们 模型训练过程中的数据。

因为在系统里面有上百万的app,在线上查询的时候对查询的性能有要求,没有办法对每一个结果 进行评分。因此,我们现需要执行一个检索操作。检索系统返回一个和Query最匹配的短列表。在 减小了候选池的大小之后,排序系统通过对所有物品进行打分然后对其进行排序。系统对物品的 打分为$p(y|x)$,表示给出用户特征(例如:国家,语言,人口统计特征)、场景特征(例如:设备、 小时,星期)和行为信息(例如:app使用年龄,一个app过去的行为统计历史)$x$的情况下,对用户 行为标签$y$的概率。在这篇文章中,我们主要将注意力集中在如何使用Wide & Deep Learning 构建排序模型。

广度 & 深度学习

广度组件

在模型框架中,广度组件就是一个广义的线性模型,形式为$y=\mathbb{w}^T\mathbb{x}+b$ , 如图1中左边的位置所示,$y$表示预测的结果,$\mathbb{x}=[x_1, x_2,…,x_d]$表示特征$d$ 维度的特征向量,$\mathbb{w}=[w_1, w_2, w_3,…,w_d]$表示参数,并且其中$b$表示偏置量。 特征集合包括原始的特征也包括转换之后的特征。最重要的特征转化就是使用$cross-product transformation$,具体的定义如下:

\[\phi(\mathbb{x}) = \prod_{i=1}^{d} x_i^{c_{k_i}} \;\;\;\;\;\;c_{k_i}\in\{0,1\}\]

##

系统实现

实验结果

相关工作

结论

推荐系统中的传统方法:奇异值分解

Alibaba DIN模型介绍

目录

在这篇文章中主要介绍了一下Alibaba的论文《Deep Interest Network for Click-Through Rate Prediction》

文章中心思想的部分内容主要包括三个部分:

  1. Attention在CTR预估任务中的使用

  2. 自适应的正则化方法

  3. Dice激活函数

在这篇文章中,我们主要从以下几个方面综合的概括一下《Deep Interest Network for Click-Through Rate Prediction》 这篇论文的中心思想以及这篇论文的启发。

在论文的第一小节中,我们主要介绍了Deep Interest Network引入Attention的动机和基本思想, 以及引入Attention之后神经网络的基本结构。 在第二节中,总结了自适应正则化和Dice激活函数在神经网络模型训练上的优化。 在第三节中,简要的介绍了一下Alibaba在DIN上的实验。 最后我们分析了一下阿里公开的Deep Interest Network的实现代码。

1. DIN的基本思想

1.1 模型结构

在了解Deep Interest Network的基本思想之前,我们首先需要明确Deep Interest Network 需要处理的问题是排序问题,以及排序系统在推荐系统中所处的阶段。

图1    推荐/搜索系统架构

在图1中给出了一个搜索系统的架构和搜索类似的,推荐过程和搜索过程的区别 在于推荐系统的召回阶段和排序阶段的输入是用户画像和场景信息,搜索系统输入 的是用户自主输入的Query信息。

在推荐系统中召回和排序阶段分别完成不同的工作:

Matching/Retrieval: 召回阶段根据对应的行为输入从大规模(百万甚至前往量级)的候选集中挑选出成百上千个 用户可能感兴趣的物品,然后送入到排序阶段。

Ranking: 排序阶段的输入是用户行为信息和召回阶段的结果,然后给召回阶段的结果准确的排序得分。

Deep Interest Network所需要解决的问题在图1中红框标识的Ranking阶段。 不同阶段的数据情况和目的(所要解决的问题)决定了我们能够使用什么样的方法。

排序阶段面临的候选集个数比较小,需要对candidate有准确的区分并且尽可能兼顾排序结果的多样性,但是 对于同一个用户而言,能够对于同类目的物品具有相近的排序得分的可能性较大,因为相近的物品具有相同的特征。

图2    Base Model模型结构

以Base Model的Embedding & MLP的网络结构为例,模型输入通过将用户特征和物品特征concat & flatten输入到 MLP的模型结构中。同一用户具有相同的用户向量的输入,那么模型对同类型的物品的得分可能具有相近的得分。

可是在实际情况当中用户可能具备多个兴趣并且需要模型能够捕获用户的多样兴趣,对多种不同品类的物品都能 给出较为合适的排序得分。

在Deep Interest Network中,举了一个年轻妈妈的例子:

例如一个年轻的妈妈最近购买了羊毛大衣、T恤、耳环、手提包、皮革手提包 以及儿童外套。 这些数据隐含了用户的行为兴趣。如果当这个年轻的妈妈访问推荐系统的时候,可能会给她 推荐一个新的手提包。可是这个推荐结果只是契合了这个年轻母亲的一部分兴趣。

为了使得模型能够捕获用户多样的兴趣,在模型结构中引入了Attention机制。

图3    DEEP INTEREST NETWORK

在DIN中为了捕获用户的不同兴趣,对Embedding & MLP的结构进行了一些修改。在embedding层 生成用户向量的时候,针对每一个candidate采用局部激活函数生成用户不同行为的权重,然后采用sum pooling生成用户的行为向量。

1.2 特征输入

在Alibaba的Deep Interest Network中输入的特征如表1所示。在排序模型中主要的输入为分组或者分类 信息包括: 用户画像特征(User Profile Features),用户行为特征(User Behavior Features), 广告特征(Ad Features) 以及场景特征(Context Features)。

这些不同特征通过one-hot或者multi-hot的方式的编码方式编码成为二进制的向量,作为模型的输入,如图4所示。

图4    用户特征的编码表示

1.3 局部激活单元

Deep Interest Network本质上是在Base Model上的优化。Base Model是一个常见的DNN模型,主要分为4个部分:

Embedding层: 对高维的二进制输入向量,embedding层将这些向量转成低维的稠密向量表示。

Pooling & Concat层: 因为不同的用户具有不同的行为,所以在multi-hot的向量里面值为1的位置数目是不同的。例如 一个用户有3次购买行为和5次购买行为的multi-hot为1的索引位置分别为3个和5个。这个时候不同的用户输入经过 embedding层得到的特征向量维度是不同的。Pooling & Concat层的目的就是为了对物品和用户得到固定长度的向量表示。

在Pooling操作中,常用的操作是sum pooling(对应维度求和)和average pooling(对应维度求均值)

MLP层: MLP层是对Pooling & Concat层给出的向量通过全连接层来捕获特征之间的交叉。

LOSS: 在Base Model中采用负对数似然函数作为损失函数,对损失的计算如下:

\[\begin{equation} L = - \frac{1}{N} \sum_{(x,y) \in S} (y\;log\;p(x) + (1-y)\;log\;(1-p(x))) \tag{1.1} \end{equation}\]

在Deep Interest Network的动机中认为Pooling & Concat通过简单的pooling操作和concat操作生成 固定长度的向量没有办法有效地捕获到多样的用户兴趣。DIN引入Attention机制做局部激活在对行为 特征进行sum pooling的时候不同行为设置了不同的权重。

假设个定一个用户$A$那么这个用户的向量表示如公式(1.2)

\[\begin{equation} V_U(A) = f(V_A, e_1, e_2,...,e_H) = \sum_{j=1}^H a(e_j, V_A)e_j = \sum_{j=1}^H w_j e_j \tag{1.2} \end{equation}\]

其中:

${e_1, e_2,…,e_H}$ 是用户 $U$ 的行为embedding向量列表,用户行为向量维度的大小为 $H$。

$V_A$ 是候选广告A的向量,$V_U(A)$ 是随着不同的候选广告变化的。

图2中的$a(\cdot)$ 是一个前馈神经网络,DIN将这个神经网络的输出作为用户行为向量激活的权重。 在 $a(\cdot)$ 除了将两个向量作为输入之外,模型还将这两个向量计算的结果输入到后续的相关性建模当中, 来引入隐含的额外信息来进行相关性建模。

2. 深度模型的训练技巧

在DIN中介绍了两种模型训练的技巧分别为:Mini-batch Aware RegularizationData Adaptive Activation Function。 我这里翻译为小批量感知正则化和局部自适应激活,在2.1小节和2.2小节中分别介绍一下这两种方法。

2.1 小批量感知正则化

在DIN中因为类似于物品id这样细粒度特征的引入很容易出现深度模型的过拟合,并且在文中 给出了图像证明,模型在经过一轮迭代训练后,训练集上的loss明显下降,测试集上loss和auc开始明显提升。

在传统的机器学习模型训练当中解决过拟合问题常用的方法是正则化。可是在深度模型中, 具有大规模的权重矩阵如果使用传统的正则化方式例如L1正则和L2正则会使得模型训练阶段的计算量大大增加。

以L2正则为例,在深度模型的训练中如果采用随机梯度下降作为优化器, 参数更新的时候只需要更新每一个mini-batch中稀疏特征非0值所对应的参数。 可是如果我们需要在模型训练过程中添加L2正则, 那么对每一个mini-batch的参数需要在全局参数上计算L2的模。 这样的计算量级在生产环境是没有办法被接受的。

在DIN的模型训练中引入了mini-batch感知正则的概念,只需要计算每个mini-batch稀疏特征L2 的模型,来减小计算量。

在深度网络的参数中实际上Embedding生成的特征向量占据了大部分参数。在一下描述中,我们使用 $W \in \mathbb{R}^{D \times K}$表示特征嵌入矩阵,其中D是嵌入后特征向量的维度, K表示输入到神经网络中特征的维度。这种情况下如果我们只是在样本上计算L2正则,那么计算公式如公式2.1

\[\begin{equation} L_2(W) = ||W||^2_2 = \sum_{j=1}^K ||w_j||^2_2 = \sum_{(x,y)\in S} \sum_{j=1}^K \frac{I(x_j\ne0)}{n_j}||w_j||^2_2 \tag{2.1} \end{equation}\]

其中 $w_j\in \mathbb{R}^D$ 是特征嵌入矩阵中的第j条向量,$I(x_j\ne0)$表示如果样本$x$是否有特征$j$, 其中$n_j$表示特征$j$ 在所有的样本中出现的次数。上面的这个方程可以转化为下面的这种形式:

\[\begin{equation} L_2(W) = \sum_{K}^{j=1}\sum_{B}^{m=1} \sum_{(x,y)\in \mathcal{B_{\mathbb{m}}}} \frac{I(x_j \ne 0)}{n_j} ||w_j||^2_2 \tag{2.2} \end{equation}\]

其中$B$ 表示mini-batch的大小,$\mathcal{B_{\mathrm{m}}}$ 表示第 $m$ 个mini-batch。在上面的公式中,我们让 $\alpha_{mj} = max_{(x,y)\in \mathcal{B_{\mathbb{m}}}}I(x_j\ne0)$ 表示在mini-batch $\mathcal{B_{\mathrm{m}}}$中 是否存在特征$j$。那么上面的方程可以大致简化为如下所示:

\[\begin{equation} L_2(W) \approx \sum_{j=1}^K \sum_{m=1}^B \frac{\alpha_{mj}}{n_j}||w_j||^2_2 \tag{2.3} \end{equation}\]

在这种情况下,我们推导出一个小批量感知的L2正则。这个时候对于第$m$个mini-batch,对特征$j$的权重更新公式如下。

\[\begin{equation} w_j \leftarrow w_j - \eta[\frac{1}{|\mathcal{B_\mathbb{m}}|} \sum_{(x,y)\in \mathcal{B_{\mathbb{m}}}}{\frac{\partial L(p(x),y)}{\partial w_j} + \lambda \frac{\alpha_{mj}}{n_j}w_j}] \tag{2.4} \end{equation}\]

在计算公式2.4中只要对第$m$个mini-batch中出现的特征参数进行正则化。

2.2 局部自适应激活

在alibaba的DIN模型中,基于PReLU对激活函数做了一些改进。 首先,我们来看一下PReLU作为激活函数的描述形式

\[\begin{equation} f(s)=\left\{ \begin{aligned} s & \,\,\,\,\,\,\,\,\mathbb{if} \,\,\,\,s > 0 \\ \alpha s & \,\,\,\,\,\,\,\,\mathbb{if} \,\,\,\, s\le 0. \end{aligned} = p(s) \cdot s + (1-p(s)) \cdot \alpha s \right. \tag{2.5} \end{equation}\]

其中,$s$ 是激活函数的一维输入, $p(s)$是一个控制单元用来选择激活函数使用$f(s) = s$和$f(s) = \alpha s$中的哪一个函数段。

在PReLU中$p(s)$作为控制函数有一个硬转折点, 在DIN中提出了一个新的激活函数Dice对控制单元$p(s)$做了一下优化, 使得激活函数能够自适应数据情况。Dice激活函数的具体描述如下:

\[\begin{equation} f(s)=p(s)\cdot s + (1-p(s))\cdot \alpha s, \,\,\, p(s)=\frac{1}{1+e^{\frac{s-E[s]}{\sqrt{Var[s] + \epsilon}}}} \tag{2.6} \end{equation}\]

这个时候$p(s)$由图5中的左子图的形式变成右边子图的形式。

图5     $p(s)$ 函数的转变

在Dice函数中的激活函数里面,训练阶段$E[s]$和$Var[s]$分别是每个输入的mini-batch的均值和方差。在测试阶段, $E[s]$和$Var[s]$通过计算整个数据集上的平均值得到。其中$\epsilon$是一个非常小的常量, 在我们训练的过程中被设置为$10^{-8}$

这里我们可以把Dice激活函数看成PReLU的一般形式。 当$E[s]=0$并且$Var[s]=0$的时候,这个激活函数退化成为PReLU的形式。

3. DIN上的实验

Alibaba在DIN上进行了公开数据集和Alibaba抽取数据集两部分数据的离线实验,以及线上的AB实验。 接下来,我们细化地讲一下论文中涉及到的实验方法,评估指标,数据处理技巧。

3.1 数据集、参数设置和评估指标

  • 数据集和参数设置
  1. 亚马逊Electronics子数据集: 这个数据集包含了19万的用户, 6万多的物品和801个类别以及168万个样本。 这个数据集具有丰富的行为,每个用户和物品都至少有5条评论。在实验阶段所有的模型都采用SGD作为优化器。优化器初始化的学习速率是1.0, 优化器的衰减速率是0.1。在训练过程中mini-batch的大小设置为32。

  2. movieLens数据集: movieLens数据集应该是推荐领域里面非常著名的数据集了。 在这个数据集里面包含了13万的用户,2.7万部电影以及21个类别和2000万条样本数据。 由于movieLens是评分数据。在实验阶段把这个数据集分成二分类的任务。 在原始得分里面,4分和5分的数据标注为正样本,其他的数据标注为负样本。 在预测任务中超过3分的预测为正样本。

  3. Alibaba数据集: 在Alibaba数据集抽取了线上系统两周的数据作为训练样本和测试数据。 其中训练数据为2亿条,测试数据为1400万条。 对于所有的深度模型,16组特征向量的维度设置为12维。 MLP网络采用$192\times200\times80\times2$ 的网络结构。由于数据量庞大我们将batch size设置为5000, 并且使用Adam作为优化器,起始学习率为0.001耍贱速率为0.9。

  • 评估指标

在排序模型的评估中,AUC是常见的评估方式。在Alibaba对模型的评估中, 参考了《Optimized Cost per Click in Taobao Display Advertising》 需要考虑到针对不同的用户和广告展示位置区别进行对待,DIN评估的时候采用了一种基于用户加权的变种AUC, 例如一个从来没有点击过任何广告的用户会使得AUC降低。 基于这一点,提出了一种新的AUC评估矩阵称之为GAUC,具体的计算方式如下:

\[\begin{equation} GAUC = \frac{\sum_{(u,p)}w_{(u,p)} * AUC(u,p)}{\sum_{(u,p)}w_{(u,p)}} \tag{3.1} \end{equation}\]

在公式3.1的计算中首先根据用户和广告位置对测试数据做一下聚合, 然后针对写一个分组(如果这个分组只有正样本或者负样本的话, 就将这个分组移除)计算AUC。最后将所有的auc加权求平均。权重$w(u,p)$和 这个分组中的点击次数或者是占用时间成正比。

在计算得到AUC的情况下,实验中通过RelaImpr计算绝对提升。

\[\begin{equation} RelaImpr = (\frac{AUC(measured model)-0.5}{AUC(base model)-0.5}-1) \times 100\% \tag{3.2} \end{equation}\]

3.3 模型对比结果

在实验中分别对比了以下几个模型的效果:

  • LR: LR是传统机器学习模型的老大哥,实验中作为一个weak baseline。

  • BaseModel: BaseModel是DIN的模型的基本结构,是DIN模型的strong baseline。

  • Wide&Deep: Wide&Deep也是排序里面非常出名的模型,分为Wide(LR)和Deep(DNN)两部分。 其中Wide部分用来记忆模型的频次,Deep部分用来学习新的模式。

  • PNN: PNN是BaseModel的加强版本,用于捕获高阶特征。

  • DeepFM: DeepFM采用FM替换了LR中的,减少了人工的特征工程操作。

最后,不同模型在数据集上的对比结果如下图所示:

图6     模型结果的对比效果

3.4 正则化的表现

在亚马逊和movieLens数据集上没有出现过拟合现象,所以没有使用正则化。

在Alibaba数据集上因为使用了大量的稀疏特征出现了过拟合现象, 接下来分别对比了一下几种防止过拟合的方式带来的效果:

  • Dropout: 针对每一个样本随机地丢弃50%的特征id。

  • Filter: 通过视频出现的频次对样本中的物品id做过滤只留下高频出现的物品。 在我们的设置中,选取出现频次最多的top 2000万的物品id。

  • Regularization in DiFacto: 大概是一种正则化方法, 想要了解细节可以参考《DiFacto: Distributed factorization machines.》

  • MBA: 就是上述提出的Mini-Batch Aware regularization方法。 对于DiFacto和MBA使用的参数$\lambda$都被设置为0.01。

实验中对比几种不同正则化策略的离线评估指标如图7所示。

图7     正则化效果对比

4. DIN的代码实现

在了解完代码原理之后,我们来看一下DIN源码的实现。这里就看一下Attention 部分的代码。

def attention(queries, keys, keys_length):
  '''
    queries:     [B, H]
    keys:        [B, T, H]
    keys_length: [B]
  '''
  queries_hidden_units = queries.get_shape().as_list()[-1]
  queries = tf.tile(queries, [1, tf.shape(keys)[1]])
  queries = tf.reshape(queries, [-1, tf.shape(keys)[1], queries_hidden_units])
  din_all = tf.concat([queries, keys, queries-keys, queries*keys], axis=-1)
  d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, name='f1_att', reuse=tf.AUTO_REUSE)
  d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att', reuse=tf.AUTO_REUSE)
  d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att', reuse=tf.AUTO_REUSE)
  d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(keys)[1]])
  outputs = d_layer_3_all 
  # Mask
  key_masks = tf.sequence_mask(keys_length, tf.shape(keys)[1])   # [B, T]
  key_masks = tf.expand_dims(key_masks, 1) # [B, 1, T]
  paddings = tf.ones_like(outputs) * (-2 ** 32 + 1)
  outputs = tf.where(key_masks, outputs, paddings)  # [B, 1, T]

  # Scale
  outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5)

  # Activation
  outputs = tf.nn.softmax(outputs)  # [B, 1, T]

  # Weighted sum
  outputs = tf.matmul(outputs, keys)  # [B, 1, H]

  return outputs

方法的入参为:

  • queries:表示待查询的candidate ad

其中queries是一个[B,T]的矩阵。

其中,B表示batch size,T表示向量的维度。

  • keys:表示用户的行为历史

keys是一个[B,T,H]的矩阵,T表示batch中最大的观影序列长度。

  • keys_length:表示用户行为历史的长度,是一个长度为B的向量。

1. queries的转换

通过上面的代码,我们首先来看一下针对queries的操作:

queries_hidden_units = queries.get_shape().as_list()[-1]
queries = tf.tile(queries, [1, tf.shape(keys)[1]])
queries = tf.reshape(queries, [-1, tf.shape(keys)[1], queries_hidden_units])
din_all = tf.concat([queries, keys, queries-keys, queries*keys], axis=-1)

整个上述的代码通过tile操作,复制queries先转换成了一个[T, B, H]的矩阵。

然后对[T, B, H]通过reshape转换成一个[B,T,H]的矩阵。

在完成queries的转换后通过tf.concat()对queries, keys, queries-keys, queries$\times$key 进行拼接, 拼接后得到的矩阵大小为:[B,T, 4$\times$H]。 这一部分应该就是原始论文里面说的除了使用了queries和keys向量之外, 还是用了out product。

2. 全连接层生成权重

在三个全连接层中,最后输出一个[B,T,1]的矩阵,重新reshape得到[B,1,T]

d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, name='f1_att', reuse=tf.AUTO_REUSE)
d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att', reuse=tf.AUTO_REUSE)
d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att', reuse=tf.AUTO_REUSE)
d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(keys)[1]])
outputs = d_layer_3_all 

3. mask操作

在mask操作里面,对Attention的paddings做了一下处理。这里Mask做之后输出的值需要为负无穷,最后softmax出来的结果为0。

key_masks = tf.sequence_mask(keys_length, tf.shape(keys)[1])   # [B, T]
key_masks = tf.expand_dims(key_masks, 1) # [B, 1, T]
paddings = tf.ones_like(outputs) * (-2 ** 32 + 1)
outputs = tf.where(key_masks, outputs, paddings)  # [B, 1, T]

4. 生成激活值并加权

在attention方法的最后结果,通过进行scale,然后对相应的权重做激活,激活后的权重对Key值做加权求平均。

# Scale
outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5)

# Activation
outputs = tf.nn.softmax(outputs)  # [B, 1, T]
# Weighted sum
outputs = tf.matmul(outputs, keys)  # [B, 1, H]

return outputs

5. 总结

合上论文想一下,整个论文最主要值得参考的点大概是Attention在排序中的使用。

除此之外的疑问和收获就是:

  • 每个candidate ad生成一个user embedding然后和item feature生成的embedding做一下concat,这种情况下模型计算的复杂度在随着candidate ads 个数的增大呈线性增长。例如在召回阶段如何使用Attention的方式来捕获多样兴趣?

  • Filter和Dropout Feature看来也是用来解决细粒度特征带来的噪声数据常用的数据处理方式。