广告召回论文阅读笔记(2)-从TDM到二向箔

4,789 阅读5分钟

在上一篇《广告召回论文阅读笔记(1)-双塔模型》中,笔者介绍了双塔模型在广告召回中的应用,即在搜索广告或推荐广告中,预先将关键词或广告通过广告塔转化为向量,并构建向量索引,再将广告请求中的搜索词或用户画像通过用户塔也转化为向量,从向量索引中检索和搜索词或用户画像向量相关的关键词或广告向量,从而将相应的广告(若返回的是关键词,则进一步召回圈选该关键词的广告主的广告)作为广告候选集之一,和其他路召回的广告候选集合并,输入后续流程。通过双塔模型,可以分别实现对用户和广告信息的深度挖掘和语义理解,并通过向量索引,实现语义层面和用户相关广告的快速召回,从而突破原有基于文本匹配或定向条件匹配的召回通道的性能天花板。

但双塔模型也存在以下不足:为了能够单独计算用户和广告向量、实现广告向量的预先计算和索引构建,模型中的用户塔和广告塔分别对用户和广告信息进行挖掘,而无法对两者信息进行交叉挖掘,另外,模型最后只能通过用户和广告向量的内积表达两者的相关性,以上在模型结构上的约束限制了模型的表达能力。因此,为突破双塔模型效果天花板,在保证低延时从海量数据实现召回的前提下,提升模型表达能力,近几年多篇论文在索引和模型的充分结合和联合优化上开展了大量工作,引入树、图等索引结构,通过模型实时计算用户和广告(商品)的相关性,从而突破以向量内积表达相关性的效果天花板,同时,通过模型和索引的充分结合和联合优化,使得模型计算的相关性能够满足索引结构的相关约束,从而能够通过索引实现高效剪枝和快速检索,避免因暴力穷举计算用户和所有广告(商品)的相关性而无法满足在线召回的低延时要求

本篇是对笔者之前分别梳理的《TDM(Tree-based Deep Model)论文阅读笔记》、《NANN论文阅读笔记》两篇文章的整合,并增加了对JTM的介绍,如有不足之处,请指正。

深度学习和树索引的结合-TDM

《Learning Tree-based Deep Model for Recommender Systems》是阿里妈妈算法团队于2018年发表的一篇论文,其中创新性地将树结构索引和深度神经网络结合,在推荐系统召回阶段,通过树结构索引实现海量商品的快速检索和高效剪枝

TDM

树结构

对于树结构,假设有NN个节点,N={n1,n2,,nN}N=\{n_1,n_2,\dots,n_N\},表示树中的叶子节点和非叶子节点,其中每个叶子节点与商品集合CC中的每个商品一一对应,而非叶子节点可以认为是描述其子节点下所有商品的一个分类,且分类粒度从下到上逐渐变粗,根节点就是整个商品集合。下图是一个树结构示例,共有8个叶子节点,每个对应到一个商品,节点8、9各表示一个个商品,节点4表示描述节点8、9这两个商品的一个分类,节点2表示描述节点8、9、10、11这四个商品的一个分类,节点1是整个商品集合。

图1 树结构

对于树中每个节点nn,可以为其训练一个分类器P(j)(nu)P^{(j)}(n|u)(其中uu表示用户,jj表示节点nn所在的树层级),用于计算用户uu对于节点nn(可能是某个商品分类,也可能是某个商品)的感兴趣概率。在此基础上,对于召回问题,其实就是要计算用户uu最感兴趣的kk个商品,可以从上到下逐层遍历树,在每一层查找该层用户uu最感兴趣的kk个节点,剪枝该层其他节点,然后再从这kk个节点下一层的子节点中查找用户uu最感兴趣的kk个节点,如此循环直至最下一层,查找到用户uu最感兴趣的kk个叶子节点(商品),实现召回。

因为树深是O(lnC)\text{O}(\ln{|C|})C|C|为商品总数),且在每层查找时均可以剪枝,所以上述查找过程是非常高效的,至多遍历2klogC2*k*\log{|C|}个节点,但需要树结构中的非叶子节点分类器满足以下条件:

P(j)(nu)=maxnc{n’s children nodes in level j+1}P(j+1)(ncu)α(j)(2)P^{(j)}(n|u)=\frac{\max_{n_c\in\{n\text{'s children nodes in level } j+1\}}P^{(j+1)(n_c|u)}}{\alpha^{(j)}} \tag{2}

即每个节点的概率等于其子节点的概率的最大值除以归一化项α(j)\alpha^{(j)},这样能保证在任何一层查找概率较大的kk个节点后,其下一层概率较大的kk个节点均属于上一层这kk个节点的子节点。而归一化项的引入,是用于调整P(j)(nu)P^{(j)}(n|u)的大小,保证同一层中各节点的概率和为1。论文中将满足这一特性的树结构称为最大堆树(Max-Heap Like Tree)。

论文中还提到,召回过程中实际并不需要计算出概率真实值,只需要计算出各节点概率的相对大小即可,因此论文使用用户和商品交互这类隐式反馈作为样本,使用一个深度神经网络进行训练,用作各节点的分类器,即全局所有分类器共用一个深度神经网络。

对于如何产出样本?假设用户uu和叶子节点ndn_d所对应的商品有交互,那么ndn_d对于用户uu是一个正样本,有:

P(m)(ndu)>P(m)(ntu)P^{(m)}(n_d|u)>P^{(m)}(n_t|u)

其中,mm表示叶子层级,ntn_t表示叶子节点除ndn_d外的其他节点,即与用户有交互的节点的概率应大于其他无交互的节点的概率。进一步,对于任一层级jj,令lj(nd)l_j(n_d)表示叶子节点ndn_d在层级jj的祖先节点,根据公式2,可以推导出:

P(j)(lj(nd)u)>P(j)(nqu)P^{(j)}(l_j(n_d)|u)>P^{(j)}(n_q|u)

其中,nqn_q是层级jj中除lj(nd)l_j(n_d)外的其他节点,即在层级jj中,lj(nd)l_j(n_d)的概率也应大于该层其他节点的概率。基于上述分析,论文使用负样本采样产出每个层级的正负样本。具体来说,与用户uu有交互的叶子节点及其祖先节点组成用户uu的正样本,再从每个层级中随机选择其他节点组成用户uu的负样本。例如,图1中对于用户uun13n_{13}及其祖先节点n1n_1n3n_3n6n_6是正样本,被标记为绿色,而对每层其他节点随机选择n2n_2n5n_5n9n_9作为负样本,被标记为红色。

在产出样本后,论文使用这些样本训练一个深度神经网络,用作各节点的分类器。假设Yu+\mathcal{Y}^+_uYu\mathcal{Y}^-_u分别是用户uu的正负样本集合,那么似然函数为:

u(nYu+P(y^u(n)=1n,u)nYuP(y^u(n)=0n,u))(3)\prod_{u}{\Big(\prod_{n\in\mathcal{Y}_u^+}{P(\hat{y}_u(n)=1|n,u)}\prod_{n\in\mathcal{Y}_u^-}{P(\hat{y}_u(n)=0|n,u)}\Big)} \tag{3}

其中,P(y^u(n)=1n,u)P(\hat{y}_u(n)=1|n,u)P(y^u(n)=0n,u)P(\hat{y}_u(n)=0|n,u)分别表示给定用户uu时对于节点nn由模型预估出的正(用户感兴趣)、负(用户不感兴趣)样本的概率,相对应的对数似然损失函数为:

unYu+Yuyu(n)logP(y^u(n)=1n,u)+(1yu(n))logP(y^u(n)=0n,u)(4)-\sum_u{\sum_{n\in \mathcal{Y}_u^+ \cup\mathcal{Y}_u^-}{y_u(n) \log{P(\hat{y}_u(n)=1|n,u)+(1-y_u(n))\log{P(\hat{y}_u(n)=0|n,u)}}}} \tag{4}

其中,yu(n)y_u(n)是给定用户uu时,节点nn的真实正负样本标识。

根据上述损失函数进行模型参数迭代训练。

深度神经网络

模型结构

具体的深度神经网络模型如图2所示。

图2 深度神经网络

其中,在输入层,使用用户行为表示用户特征,将用户行为划分为多个时间窗口,在每个时间窗口中,使用和用户有交互的多个商品作为输入,并引入Embedding层,将每个商品转化为其Embedding向量。而模型目标是预测给定用户时其对树中各节点(某类商品或某个商品)感兴趣的概率,因此将树中节点也作为输入,并引入Embedding层,将节点也转化为其Embedding向量。同时参考了DIN模型,也引入了注意力机制,为用户行为的每个商品设计了激活单元,用于计算用户行为中的商品和树中节点的相关性权重,使用权重对同一时间窗口中的多个商品Embedding向量进行加权平均池化。最后再对多个时间窗口输出的Embedding向量进行拼接作为最终的用户Embedding向量,将用户Embedding向量和树节点Embedding向量拼接在一起输入隐层。隐层是三层全连接网络,每层神经元个数分别是128、64和24,并采用和DIN模型相同的激活函数PReLU。输出层是一个二分类Softmax函数,输出用户对商品感兴趣和不感兴趣的概率。从模型结构可以看出,相比于对用户和商品向量作简单的内积,其通过多层网络,能充分表达用户和商品之间的相关性。另外,论文中还提到,除了图中标出的输入特征,还可以将用户画像和上下文等作为输入特征。

树的构建和模型训练

对于树的构建和模型训练,论文的整体方案是,先采用一定的方法初始化树,再按以下的步骤循环多次:

  • 基于已构建树和叶子节点样本数据,生成其他每层样本数据,然后输入上一节的深度神经网络进行模型训练;
  • 模型训练完成后,基于训练后所得叶子节点的Embedding向量重新构建新树;

最终得到线上服务使用的深度神经网络和树结构。

对于如何初始化树,论文的方案是对所有商品聚类排序,保证相同品类的商品排列在一起,但品类是随机乱序的,品类中的商品也是随机乱序的,然后将排序后的商品递归平均地划分为两个集合,直至最后划分出的集合中只包含一个商品,这样就能从上到下构建出一个近似完全二叉树。

对于如何基于训练后所得叶子节点的Embedding向量重新构建新树,论文的方案是对叶子节点的Embedding向量采用K均值聚类算法递归划分为两个集合,直至最后划分出的集合中只包含一个叶子节点,这样也能从上到下构建出一个二叉树。

在线服务

召回阶段的在线服务架构如图所示。

图3 在线服务架构

当用户在淘宝内产生点击、购买、加购等行为时,这些行为数据会进入实时特征服务,汇聚成用户历史行为特征。当用户再次发起页面浏览请求时,用户定向服务从实时特征服务获取用户历史行为特征,对树结构进行逐层遍历,在每一层,对于每个节点,将节点Emebedding向量和用户历史行为等特征输入深度神经网络,得到概率,然后进行排序、剪枝,直至最后一层,得到概率较大的若干个叶子节点作为召回的候选商品。

实验分析

论文使用了两个数据集进行实验:MovieLens-20M(用户对电影的评分数据)和UserBehavior(淘宝内的用户行为数据)。在指标上,令Pu\mathcal{P}_u表示为用户uu召回的商品集合,且Pu=M|\mathcal{P}_u|=MGu\mathcal{G}_u表示用户uu真实感兴趣的商品集合,则准确率和准召率如下:

Precision@M(u)=PuGuM,Recall@M(u)=PuGuGuPrecision@M(u)=\frac{|\mathcal{P}_u \cap \mathcal{G}_u|}{M}, Recall@M(u)=\frac{|\mathcal{P}_u \cap \mathcal{G}_u|}{|\mathcal{G}_u|}

FMeasure@M(u)F-Measure@M(u)如下:

FMeasure@M(u)=2Precision@M(u)Recall@M(u)Precision@M(u)+Recall@M(u)F-Measure@M(u)=\frac{2*Precision@M(u)*Recall@M(u)}{Precision@M(u)+Recall@M(u)}

另外,令Su\mathcal{S}_u表示已和用户uu有交互的商品,论文还添加了以下指标用于衡量推荐的新颖性(推荐商品与用户无历史交互的占比):

Novelty@M(u)=PuSuMNovelty@M(u)=\frac{|\mathcal{P}_u \setminus \mathcal{S}_u|}{M}

图4 实验结果

实验结果如图所示。其中分为无过滤和过滤用户已交互商品两种情况。在这两种情况下,TDM相比其他模型,在准确率和准召率上均有明显的提升,其次是YouTube的DNN模型,在之前的论文阅读笔记中,已介绍了该模型,通过深度神经网络训练得到用户和商品的Embedding向量,然后在内积向量空间中查找和用户相近的商品实现召回。

深度学习和树索引的联合优化-JTM

《Joint Optimization of Tree-based Index and Deep Model for Recommender Systems》是阿里妈妈算法团队于2019年发表的一篇论文。原TDM算法在模型训练和树索引构建时分别采用了不同的优化目标,模型训练的优化目标是最小化对数似然损失函数,而树索引构建的优化目标是K均值聚类算法中的根据最近距离选择样本类别、再根据样本均值选择聚类中心、如此循环直至样本类别不再变化,模型训练和树索引构建的优化目标不一致,导致整体TDM算法不易达成最优解,因此,在2019年的这篇论文中,阿里妈妈算法团队提出了一个新的联合优化框架,对原TDM算法中的模型训练和树索引构建,采用一致的优化目标——最小化对数似然损失函数(即模型预估用户偏好的准确度),并基于树索引对用户进行分层表征以更好地对用户不同粒度的兴趣进行建模,最后通过实验论证了上述方法能够有效提升推荐系统效果指标。论文中的联合优化框架被称为JTM(Joint Optimization of Tree-based Index and Deep Model)。

JTM

TDM

图5 TDM模型

论文首先回顾了TDM模型。TDM模型如图5所示,其包含两部分,第一部分为树索引T\mathcal{T},第二部分为用户偏好模型M\mathcal{M}。商品集合中的每个商品通过函数π()\pi(\cdot)映射到树索引中的一个叶子节点。树索引中的非叶子节点可以被认为是其所有子叶子节点所对应商品的一个聚类,且树索引满足以下最大堆(Max-Heap Like Tree)性质:

p(l)(nu)=maxnc{n’s children in level l+1}p(l+1)(ncu)α(l)p^{(l)}(n|u)=\frac{\max_{n_c\in\{n\text{'s children in level }l+1\}}p^{(l+1)(n_c|u)}}{\alpha^{(l)}}

其中,p(l)(nu)p^{(l)}(n|u)表示用户uu对节点nn感兴趣的真实概率,等于用户对其子节点感兴趣的真实概率的最大值除以归一化项α(j)\alpha^{(j)},这样能保证在任何一层查找概率较大的kk个节点后,其下一层概率较大的kk个节点均属于上一层这kk个节点的子节点。而归一化项的引入,是用于调整P(j)(nu)P^{(j)}(n|u)的大小,保证同一层中各节点的概率和为1。

基于树索引的这一性质,对于召回问题,其实就是要计算用户uu最感兴趣的kk个商品,可以从上到下逐层遍历树,在每一层查找该层用户uu最感兴趣的kk个节点,剪枝该层其他节点,然后再从这kk个节点下一层的子节点中查找用户uu最感兴趣的kk个节点。从子节点中查找用户uu最感兴趣的kk个节点,即对于每个子节点,将其和用户uu的特征一并输入用户偏好模型M\mathcal{M},由模型预测用户uu对该节点感兴趣的概率,然后,对子节点按照模型预测的用户感兴趣的概率从高到低进行排序,从中选择排序靠前的kk个节点,如此循环直至最下一层,查找到用户uu最感兴趣的kk个叶子节点(商品),实现召回。

使用上述召回方式,解决了双塔模型只能在最后阶段进行用户和商品表征向量的内积,而无法提前对用户和商品特征进行交叉和挖掘的问题,从而能够引入比较复杂的模型结构对海量商品集合进行用户偏好预测,召回更加准确的商品。

联合优化

从上述召回流程可以看出,召回的准确性同时依赖树索引T\mathcal{T}和用户偏好模型M\mathcal{M}。在原TDM的论文中,训练M\mathcal{M}和构造T\mathcal{T}相互独立,分别采用了不同的优化目标,模型训练的优化目标是最小化对数似然损失函数,而树索引构建的优化目标是K均值聚类算法中的根据最近距离选择样本类别、再根据样本均值选择聚类中心、如此循环直至样本类别不再变化,模型训练和树索引构建的优化目标不一致,导致整体TDM算法不易达成最优解。因此,在JTM的论文中,论文作者提出了一个新的联合优化框架,使用一个全局损失函数L(θ,π)\mathcal{L}(\theta,\pi)(其中,θ\theta是用户偏好模型M\mathcal{M}的参数,π\pi是将商品映射至树索引T\mathcal{T}的叶子节点的函数),同时训练M\mathcal{M}和构造T\mathcal{T},从而进一步提升召回的准确性。

首先介绍全局损失函数L(θ,π)\mathcal{L}(\theta,\pi)如何表示。令训练数据包含nn对正样本(ui,ci)(u_i,c_i),每对样本表示用户uiu_i对商品cic_i感兴趣。令π()\pi(\cdot)表示映射函数,将样本(ui,ci)(u_i,c_i)中的商品cic_i映射至树索引中的叶子节点π(ci)\pi(c_i)。令p(π(ci))ui;π)p(\pi(c_i))|u_i;\pi)表示用户uiu_i对叶子节点π(ci)\pi(c_i)(即商品cic_i)感兴趣的概率的真实值。令p^(π(ci))ui;θ,π)\hat{p}(\pi(c_i))|u_i;\theta,\pi)表示用户偏好模型在参数θ\theta下、用户uiu_i对叶子节点π(ci)\pi(c_i)(即商品cic_i)感兴趣的概率的预测值。若(ui,ci)(u_i,c_i)为正样本,则p(π(ci))ui;π)=1p(\pi(c_i))|u_i;\pi)=1,且根据最大堆性质,叶子节点π(ci)\pi(c_i)的所有父节点的感兴趣概率的真实值也为1,令bj()b_j(\cdot)表示求取叶子节点在树中第jj层的父节点(树的根节点层级为0,层级序号从上到下逐渐递增),则{p(bj(π(ci))ui;π)}j=0lmax=1\{p(b_j(\pi(c_i))|u_i;\pi)\}_{j=0}^{l_{max}}=1。基于上述推导,采用负对数似然函数的全局损失函数可由下式表示:

L(θ,π)=i=1nj=0lmaxlogp^(bj(π(ci))ui;θ,π)\mathcal{L}(\theta,\pi)=-\sum_{i=1}^{n}{\sum_{j=0}^{l_{max}}{\log{\hat{p}(b_j(\pi(c_i))|u_i;\theta,\pi)}}}

即求和所有正样本商品所对应叶子节点和其父节点由模型预测的概率的对数、并取负值。

基于上述全局损失函数,联合优化框架如图6所示,在每次优化迭代中,先优化模型,按梯度下降调节模型参数,最小化全局损失函数L(θ,π)\mathcal{L}(\theta,\pi),再按照图7所示的树索引优化算法优化商品和叶子节点的映射函数π()\pi(\cdot)、并重建树索引,最小化全局损失函数L(θ,π)\mathcal{L}(\theta,\pi)。每次迭代包含独立的两步、分别优化模型和索引的原因是映射函数π()\pi(\cdot)的优化属于组合优化(Combinational Optimization)问题,较难通过梯度下降同时优化模型参数和映射函数。

图6 联合优化框架

图7 商品和叶子节点的映射函数的优化算法

模型优化采样常规的梯度下降方法,论文也未过多介绍,只是简单说明在归一化项计算上的优化:在计算第ll层某个节点的概率p(l)(nu)p^{(l)}(n|u)时,需先计算该层的归一化项α(l)\alpha^{(l)},而计算归一化项α(l)\alpha^{(l)},则需先计算该层所有节点未归一化前的概率,由于每层节点数是随着层级指数级增长的,因此为了减少归一化项的计算量,论文采用了Noise Contrastive Estimation进行采样以减少归一化项的计算量。

论文主要介绍了映射函数如何优化。论文将映射函数π()\pi(\cdot)的优化等价于从商品集合C\mathcal{C}到树索引T\mathcal{T}的最优分配问题,即minπL(θ,π)\min_\pi\mathcal{L}(\theta,\pi)等价于加权二分图的最大权重分配问题。

等价的证明过程如下。 令第kk个商品ckc_k被分配至第mm个叶子节点nmn_m,即π(ck)=nm\pi(c_k)=n_m,则两者之间连接的权重可由下式计算:

Lck,nm=(u,c)Akj=0lmaxlogp^(bj(π(c))u;θ,π)\mathcal{L}_{c_k,n_m}=\sum_{(u,c)\in\mathcal{A}_k}{\sum_{j=0}^{l_{max}}{\log{\hat{p}(b_j(\pi(c))|u;\theta,\pi)}}}

其中,Ak\mathcal{A}_k是所有包含商品ckc_k的正样本集合,即用户uu和商品cc的配对中,商品ccckc_k。上述权重公式即求和所有包含商品ckc_k的正样本、商品ckc_k所对应叶子节点和其父节点由模型预测的概率的对数。将商品集合CC中的商品和树索引T\mathcal{T}中的叶子节点看作二分图的顶点,需要在商品和叶子节点之间建立全连接π()\pi(\cdot),使得任意一个商品ckc_k和一个叶子节点nmn_m一一对应,这样就构建了一个以Lck,nm\mathcal{L}_{c_k,n_m}为权重的加权二分图VV,且全局损失函数可进一步被改写成:

L(θ,π)=i=1CLci,π(ci)\mathcal{L}(\theta,\pi)=-\sum_{i=1}^{|C|}{\mathcal{L}_{c_i,\pi(c_i)}}

从中可以看出,最小化损失函数,即最大化加权二分图中所有边的权重和。

传统的解决加权二分图最大权重分配问题的算法有匈牙利算法(关于匈牙利算法的介绍可以参考一位知友的文章《算法学习笔记(5):匈牙利算法》),而匈牙利算法因计算量较大难以应用于大数据量场景。若采用贪心算法从所有未分配的商品和叶子节点中选择权重Lck,nm\mathcal{L}_{c_k,n_m}最大的边,则也需要预先计算C×2lmax|C|\times 2^{l_{max}}的权重矩阵,而商品数量较大,权重矩阵求解也会导致较大的计算量,因此,论文设计了分段的树索引学习算法(Segmented Tree Learning Algorithm),如图7所示,下面详细介绍该算法流程。

首先定义对于某个商品ckc_k、从第ss层到第dd层的权重,即对于商品ckc_k,通过映射函数π()\pi(\cdot)映射到叶子节点π(ck)\pi(c_k),只求和该叶子节点从第ss层到第dd层的父节点由模型预测的概率的对数,如下所示:

Lck,π(ck)s,d=(u,c)Akj=sdlogp^(bj(π(ck))u;θ,π)\mathcal{L}_{c_k,\pi(c_k)}^{s,d}=\sum_{(u,c)\in\mathcal{A}_k}{\sum_{j=s}^d{\log\hat{p}(b_j(\pi(c_k))|u;\theta,\pi)}}

分段的树索引学习算法分多次迭代,每次迭代将映射函数由πold()\pi_{old}(\cdot)更新至πnew()\pi_{new}(\cdot),且每次迭代中将整棵树从上到下按层级划分多段,逐段采用贪心算法进行优化,这样可以减少计算量。对于第一段,即从第1层(第0层为根节点)到第dd层,求解映射函数πnew()\pi_{new}(\cdot),将所有商品映射到第dd层的各节点中,从而最大化从第1层到第dd层的节点由模型预测的概率的对数的和,即i=1CLci,π(ci)1,d\sum_{i=1}^{|C|}{\mathcal{L}_{c_i,\pi(c_i)}^{1,d}},同时,还需满足对于层高为lmaxl_{max}的完全二叉树T\mathcal{T},第dd层的每个节点包含的商品数不超过2lmaxd2^{l_{max}-d}。以上第一段的求解问题也是一个带约束的二分图最大权重分配问题,可以采用贪心算法求解,但计算量相比原始问题下降较大,原始问题需将所有商品分配至叶子节点(叶子节点共2lmax2^{l_{max}}个),而新问题仅需将所有商品分配至第dd层节点(第dd层节点共2d2^d个)。在求解第一段的映射函数后,保留所有商品映射到第dd层的各节点的结果不变,即bd(π(c))b_d(\pi(c))不变,接着优化第二段的dd层,即对于第dd层的各节点,分别采用贪心算法进行优化,求解映射函数πnew()\pi_{new}(\cdot),将当前节点中的商品映射到第2×d2\times d层的各节点中,从而最大化从第d+1d+1层到第2×d2\times d层的节点由模型预测的概率的对数的和,(仍需满足映射结果可构造二叉树的约束)。如此循环不断往下,直至叶子节点。

图7算法的第5步具体描述了在某一段中如何调整映射结果以满足可构造二叉树的约束。在第ldl-d层到第ll层这一段,对于已分配至第ldl-d层中节点nin_i的商品cCnic\in\mathcal{C}_{n_i},进一步采用贪心算法,按照权重Lc,ld+1,l\mathcal{L}_{c,\cdot}^{l-d+1,l}的最大化将商品cc分配至节点nin_i在第ll层的某个子节点中。为了保证第ll层每个子节点分配的商品数不超过2lmaxl2^{l_{max}-l},对映射结果进行调整:对第ll层商品数超过限制的节点,首先保留在上次迭代中、采用旧映射函数仍映射至该节点(即bl(π(c))==bl(πold(c))b_l(\pi'(c))==b_l(\pi_{old}(c)))的商品不变,然后将剩余的商品按照权重值L,nld+1,l\mathcal{L}_{\cdot,n}^{l-d+1,l}的降序进行排序,尾部超过限制的每个商品再按照权重值Lc,ld+1,l\mathcal{L}_{c,\cdot}^{l-d+1,l}的降序进行排序、移至其他还有空间且权重尽量大的节点中。

分段的树索引学习算法避免了原始问题若采用贪心算法、求解权重矩阵计算量较大的问题,同时,通过分段拆分为多个子任务后,子任务之间可以并行运行,提升计算效率。

分层用户表征

在TDM中,召回是从上到下逐层遍历树节点的过程,而每个树节点表示商品的一个抽象类别,且类别粒度从上层到下层逐渐变细,直至叶子节点表示具体商品,因此,在逐层遍历、使用模型预测用户对各树节点感兴趣的概率时,模型实际预测的是用户对不同粒度的商品类别感兴趣的概率,这样就需要构造各层有差异的模型输入,提升模型预测的准确率。 而用户行为序列作为模型用户侧的特征,可以很好地表征用户兴趣。令用户行为序列为c={c1,c2,,cm}c=\{c_1,c_2,\cdots,c_m\},其中,cic_i为用户已交互的第ii个商品,当从树索引第ll层召回用户最感兴趣的节点时,则对用户行为序列中的商品,使用商品所对应叶子节点在第ll层的父节点进行替换,即用户行为序列可表示为cl={bl(π(c1)),bl(π(c2)),,bl(π(cm))}c^l=\{b_l(\pi(c_1)),b_l(\pi(c_2)),\cdots,b_l(\pi(c_m))\}。论文指出,不同层采用不同的用户表征,有以下好处:

  • 层独立性。模型在各层的目标不同,召回不同粒度的节点,而用户行为序列作为模型输入,若保持由商品构成不变,则经过Embedding层后、输出的Embedding向量也不变,和模型在各层目标的不同不一致,一种解决方案是为模型在各层设置不同的Embedding矩阵,用户行为序列经过Embedding层后、输出的Embedding向量随层变化,但这一方案会额外引入大量的模型参数,增加模型计算量,而采用分层用户表征,可以在保持模型参数不变的前提下,使得用户行为序列的Embedding向量随层变化(第ll层使用商品所对应叶子节点在第ll层的父节点的Embedding向量替换商品的Embedding向量),实现层独立性。
  • 准确建模。模型在各层召回不同粒度的节点,从上到下,粒度由粗到细,而在某层召回时,若使用该层节点替换商品表示用户行为序列,则用户兴趣表征的粒度和该层召回的目标节点一致,即用户兴趣表征表示用户对某种粒度的商品类别的兴趣,而目标节点也表示相同粒度的商品类别,从而在各层实现对用户兴趣表征的准确建模。

实验分析

论文进行了大量实验分析,下面仅介绍其中部分实验结果和结论。论文使用了两个数据集进行实验:Amazon Books(用户对书籍的评分数据)和UserBehavior(淘宝内的用户行为数据)。效果指标和原TDM论文一致,包括准确率、准召率和F值。实验结果如图8所示。

图8 实验结果

JTM在两个数据集上相比TDM、YouTube DNN均更优。

深度学习和图索引的结合-NANN(二向箔)

《Approximate Nearest Neighbor Search under Neural Similarity Metric for Large-Scale Recommendation》是阿里妈妈算法团队于2022年发表的一篇论文。这篇论文提出了一个新的召回方案,即在通过近似最近邻搜索算法快速查找和用户相近的若干个商品时,使用深度神经网络模型的计算输出作为用户和商品的距离度量表示其相关性,替代内积、余弦相似度等度量形式的用户和商品的向量距离。这样,既可以充分使用模型的表达能力保证用户和商品相关性的准确性,也可以通过近似最近邻搜索算法(论文中使用HNSW算法)保证结果的快速返回。论文将该方案称为NANN(Neural Approximate Nearest Neighbor Search)

相关工作

论文中也提到,这个方案的思路参考了百度于2020年发表的论文《Fast Item Ranking under Neural Netword based Measures》。百度的论文提出了基于图进行最近邻搜索、基于深度神经网络的输出作为距离度量的方案SL2G,而阿里妈妈的论文在此基础上,使用了HNSW算法(并在此基础上进行优化)进行最近邻搜索,并在网络结构、模型训练、工程实现等方面进行优化,取得了更好的效果。论文在效果评估中使用SL2G作为基线模型。

HNSW算法是基于图的最近邻搜索算法之一。《Approximate nearest neighbor algorithm based on navigable small world graphs》《Efficient and robust approximate nearest neighbor search using hierarchical navigable small world graphs》这两篇论文先后于2014年和2018年提出了NSW(Navigable Small World)算法和HNSW(Hierarchical Navigable Small World)算法进行图的构建和搜索,其中,HNSW在NSW的基础上,引入分层思想,效果更好,因此,论文中采用HNSW算法、并在此基础上进行优化,作为最近邻搜索算法。关于NSW算法和HNSW算法的详细介绍,可以阅读上述两篇文章。 另外,在这篇论文之前,阿里妈妈也曾提出TDM(Tree-based Deep Model)方案,在召回阶段引入深度神经网络。该方案通过树结构索引商品,因此将召回的时间复杂度降低至O(lnC)\text{O}(\ln{|C|})C|C|为商品总数),召回时从上到下逐层遍历树结构,通过深度神经网络计算用户和每个节点的相关性,查找Top的若干个商品类别(非叶子节点)或商品(叶子节点)并实现剪枝。但TDM方案存在以下不足:一是索引和模型的联合训练比较耗计算资源,二是树结构索引中的每个非叶子节点并不表示具体的某个商品(仅每个叶子节点表示具体的某个商品),因此在模型中,节点特征无法使用商品信息。而本篇论文提出的方案解决了上述的两个不足:一是在模型训练和图搜索上进行优化减少计算量,二是图中的节点均表示具体商品,可以充分使用商品信息。

具体方案

模型结构

模型结构如下图所示:

图9 NANN模型结构

遍历图时,将遍历的节点作为模型输入之一,模型的其他输入还包括用户信息、行为序列,输出是给定当前用户时,和该节点的相关性得分。模型结构包括5个部分:

  • Embedding层,将原始各特征转化为Embedding向量;
  • 商品网络,输入为图搜索当前遍历的节点(商品)各种特征的Embedding向量,输出为商品的Embedding向量;
  • 注意力网络,输入为行为序列中各商品各种特征的Embedding向量、以及商品网络输出的商品Embedding向量,通过注意力网络,计算行为序列中各商品和当前商品的相关性权重,然后对行为序列中各商品的Embedding向量进行加权求和作为输出;
  • 用户网络,输入为当前用户各种特征的Embedding向量,输出为用户的Embedding向量;
  • 评分网络,输入为上述商品网络、注意力网络、用户网络输出的拼接,输出为用户和商品的相关性得分。

而对于不同的数据集,模型结构细节稍有不同。论文使用了两个数据集:

  • 淘宝公开数据集(UserBehavior),涉及近百万用户、400余万商品的8000多万行为数据,每条数据包括用户id、商品id、商品类别id、行为类型、时间戳;
  • 淘宝线上数据集(Industry),涉及亿级用户、百余级商品的3亿余行为数据,其数据相比公共开数据集,包含更多的属性;

对于淘宝线上数据集(Industry),其模型结构如图所示:

图10 淘宝线上数据集模型结构

其中,商品网络为三层全连接网络,各层节点数分别为128、64、64,且前两层带有激活函数(PReLU)和批归一化,最后输出64维的商品向量;评分网络为四层全连接网络,各层节点数分别为512、256、128、1,且前三层带有激活函数(PReLU)和批归一化,最后输出用户和商品的相关性得分。

而对于注意力网络,注意力机制中对于表达自主性的查询和多个键、值对,通过注意力评分函数计算查询和各个键的权重,基于权重对相应的各个值进行加权求和,用公式描述如下:

f(q,(k_1,v_1),...,(k_m,v_m))=_i=1mα(q,k_i)v_iRvα(q,k_i)=softmax(a(q,k_i))=exp(a(q,k_i))_j=1mexp(a(q,k_j))R\begin{aligned} f(q,(k\_1,v\_1),...,(k\_m,v\_m))&=\sum\_{i=1}^{m}{\alpha(q,k\_i)v\_i} \in \mathbb{R}^v \\ \alpha(q,k\_i)=\text{softmax}(a(q,k\_i))=&\frac{\text{exp}(a(q,k\_i))}{\sum\_{j=1}^m{\text{exp}(a(q,k\_j))}} \in \mathbb{R} \end{aligned}

其中,a(q,k)a(q,k)为注意力评分函数,在本论文中,查询即当前商品,每个键、值对表示用户行为序列中的一个商品,注意力机制的目标即求解行为序列中每个商品和当前商品的相关性权重,若越相关,则权重越大,其特征加权求和后对模型输出的影响越大。具体实现时,注意力网络结构采用缩放点积注意力(Scaled Dot-Product Attention),其注意力评分函数公式为:

a(q,k)=qTkda(q,k)=\frac{q^\text{T}k}{\sqrt{d}}

其中,ddqqk\的长度。对照到模型结果图中,商品网络输出的商品Embedding向量经过两层全连接网络(第一层节点数为64并带有激活函数PReLU,第二层节点数为64)得到当前查询向量,行为序列中每个商品的Embedding向量作为值向量,并经过两层全连接网络(第一层节点数为32并带有激活函数PReLU,第二层节点数为64)得到键向量,通过点积、缩放和Softmax计算得到权重,再对每个商品的Embedding向量进行加权求和得到注意力网络的输出。

最后对于用户网络,采用Transformer模型的编码器(Encoder)部分,如图11所示:

图11 Transformer模型的编码器部分

图12 Encoder中的多头注意力层

编码器在Embedding和位置编码后,包含多个编码器层,而每个编码器层又包含两个子层:多头注意力层(Multi-Head Attention)和全连接网络层,并在每个子层周围有一个残差连接,然后进行层归一化。通过残差连接避免梯度消失问题。多头注意力层中每一层的核心结构也是缩放点积注意力,只是将上游的输出同时作为查询、键、值从而引入自注意力机制,同时通过不同的线性变换产生不同的注意力输入,这样能够自动挖掘原始输入中的深度信息。本篇论文将编码器层数和多头注意力层数均设置为1,用户网络的输入是用户各种特征的Embedding向量(共51维),通过编码器,输出为是16×5116\times51维的用户Embedding向量。

图13 淘宝公开数据集模型结构

对于淘宝公开数据集(UserBehavior),其模型结构如图所示,其商品网络、注意力网络、评分网络结构和线上数据集类似,只是用户网络,由于数据集中没有用户信息,因此改用行为序列中各商品各种特征的Embedding向量,进行求和池化,作为用户向量。

模型训练

模型训练,损失函数如下所示:

Lall=LNCE+LAUX(3)\mathcal{L}_{all}=\mathcal{L}_{NCE}+\mathcal{L}_{AUX} \tag{3}

包含两部分:

  • LNCE\mathcal{L}_{NCE},分类问题损失函数,正样本即用户和商品发生交互的历史记录,负样本基于NCE(Noise Contrastive Estimation)进行采样以减少计算量;
  • LAUX\mathcal{L}_{AUX},通过引入对抗样本(Adversarial Examples),采用对抗训练(Adversarial Training)提升模型鲁棒性;

论文在遍历图时,通过计算当前遍历节点(商品)和给定用户的模型输出su(ev)s_u(\text{e}_v)作为距离度量,而在构建图时,由于用户未知,所以仍是使用节点(商品)本身的向量ev\text{e}_v的L2距离作为距离度量,所以,若ev\text{e}_v作为模型输入,其微小变化引起输出su(ev)s_u(\text{e}_v)的巨大变化,会影响召回的准确度(论文通过实验也验证了这点),所以论文通过对抗训练提升模型鲁棒性,降低ev\text{e}_v的波动对输出的影响。对抗训练的损失函数如下所示:

LAUX=uvYusu(ev)logsu(ev)su(ev^)ev^=ev+Δ(5)\begin{aligned} \mathcal{L}_{AUX}&=\sum_u{\sum_{v \in \mathcal{Y}_u}{s_u(\text{e}_v)\text{log}\frac{s_u(\text{e}_v)}{s_u(\hat{\text{e}_v})}}} \\ \hat{\text{e}_v}&=\text{e}_v+\Delta \end{aligned} \tag5

使用FGSM算法产生对抗样本,通过以下公式在原始样本的基础上生成扰动:

Δ=ϵsign(evsu(ev))\Delta=\epsilon \text{sign}(\nabla_{\text{e}_v}s_u(\text{e}_v))

其中,evsu(ev)\nabla_{\text{e}_v}s_u(\text{e}_v)表示su(ev)s_u(\text{e}_v)的梯度。关于对抗样本和对抗训练的介绍,可以参考知乎的这篇文章

图的构建

对于图的构建,论文直接使用了HNSW算法,并使用商品向量的L2距离作为距离度量。

在线召回

在线召回用公式表达如下:

Bu=argTopkvVsu(ev)(4)\mathcal{B}_u=\text{argTopk}_{v\in \mathcal{V}}s_u(\text{e}_v) \tag4

其中,ev\text{e}_v表示在线召回时遍历图中节点的向量,求取模型输出su(ev)s_u(\text{e}_v)最大的kk个节点集合。具体遍历算法论文中称为Beam-Retrieval算法,基于HNSW算法进行分层遍历,如下所示:

图14 Beam-Retrieval算法

其中,令构建好的分层图为GG,图的层数为LL,用户为uu,要求返回KK个和用户最相关(距离最近)的节点,其集合为为WW。从上到下逐层搜索,在每层,调用SEARCH-LAYER算法,该算法本质是一个贪心算法,具体如下:

图15 每一层的SEARCH-LAYER算法

令当前层为lcl_c,当前层开始遍历的节点集合为epep(即上一层的输出),当前层搜索的步数为TcT_c,当前层已被访问的节点集合为SS,当前层待搜索的候选节点集合为CC,当前层和用户最相关(距离最近)的节点集合为WW。初始时,将epep赋值给集合SSCCWW。对于搜索的每一步:

  • 将候选节点集合CC的所有邻居节点赋值给集合NN
  • 从集合NN中减枝已被访问的节点集合SS
  • 对集合NN中减枝后余下的节点和当前层和用户最相关的节点集合WW取并集,并对这些节点求取s(u,v)s(u,v)最大(和用户最相关)的KK个节点,赋值给集合WW,即覆盖更新当前层和用户最相关的节点集合;
  • 对集合NN和集合WW取交集,作为下一步的候选节点集合CC
  • 对集合NN和集合SS取并集,即将本步中访问的节点标记为已被访问,用于下一步的剪枝;

上述算法,在原始HNSW算法的基础上,针对在线召回场景作了一些优化:

  • 通过for循环限制搜索的步数,控制算法计算耗时以满足在线召回的快速响应要求;
  • efl(l>0)ef_l(l>0)(即每层开始遍历的节点)可以是多个(原始HNSW算法限制最多一个),便于并行计算。

系统实现

如图16所示:

图16 系统实现

其中,模型和索引基于TensorFlow框架。用户行为通过特征服务实时生成用户特征。当用户发起访问请求时,由预测服务从特征服务获取用户特征,遍历索引,通过模型进行相关性计算,获取候选集合,再传给下游的服务进行排序。论文中也提到了很多工程方面的优化技巧以提升在线性能,着重提到的两点:

  • 使用Bitmap保存已访问节点并进行剪枝,相比于TensorFlow原生算子,QPS提升明显;
  • Dynamic Shape with XLA,XLA是Google研发的专用于深度学习的编译器,可以加速TensorFlow模型,但其在遇到dynamic shape时,会触发即时编译(JIT)造成线上服务性能抖动;论文中提出了解决方案——auto padding,在线召回时,对于dynamic shape,自动扩展到和最其最相近的已预编译好的shape,计算后再切分得到原来的shape。

效果评估

效果评估使用淘宝公开数据集(UserBehavior)和线上数据集(Industry)这两份数据。基于用户uu的行为序列bu1,...,buk,...,bun(b_{u_1},...,b_{u_k},...,b_{u_n}),根据第1次到第kk次的行为,预测第k+1k+1次的行为。

评估指标

查全率:

recall(Pu,Gu)@M(u)=PuGuGu\text{recall}(\mathcal{P}_u,\mathcal{G}_u)@M(u)=\frac{|\mathcal{P}_u \cap \mathcal{G}_u|}{|\mathcal{G}_u|}

其中,Pu\mathcal{P}_u是召回的商品集合,Gu\mathcal{G}_u是和用户uu真实相关的商品集合。

基于暴力扫描所有商品召回TopK的查全率:

recall-all@M(u)=recall(Bu,Gu)@M(u)\text{recall-all}@M(u)=\text{recall}(\mathcal{B}_u,\mathcal{G}_u)@M(u)

其中,Bu=argTopkvVsu(ev)\mathcal{B}_u=\text{argTopk}_{v\in \mathcal{V}}s_u(\text{e}_v),为暴力扫描召回的商品集合,该查全率用于评估模型本身计算相关性的性能。

基于图搜索召回TopK的查全率:

recall-retrieval@M(u)=recall(Ru,Gu)@M(u)\text{recall-retrieval}@M(u)=\text{recall}(\mathcal{R}_u,\mathcal{G}_u)@M(u)

其中,Ru\mathcal{R}_u为图搜索召回的商品集合。

通过以下公式衡量因为使用图搜索导致的查全率的损失:

recall-Δ@M(u)=recall-all@Mrecall-retrieval@Mrecall-all@M\text{recall-}\Delta @M(u)=\frac{\text{recall-all}@M - \text{recall-retrieval}@M}{\text{recall-all}@M}

通过以下公式衡量图搜索相比于暴力扫描召回结果的覆盖率:

coverage@M(u)=RuBuBu\text{coverage}@M(u)=\frac{|\mathcal{R}_u \cap \mathcal{B}_u|}{|\mathcal{B}_u|}

评估效果

图17 NANN、NANN-HNSW、SL2G比较

其中,横轴表示实验遍历商品在商品全集的占比,NANN曲线表示采用本论文设计的图搜索算法——Beam-retrieval算法,NANN-HNSW曲线表示采用原始的HNSW算法。从实验结果可以看出,NANN相比于SL2G,在查全率和覆盖率上均有较大的提升,同时,在线上数据集上,覆盖率能达到99%,说明召回结果已和暴力扫描相差不大,能够抵抗对抗攻击,另外,仅需要遍历1%-2%的商品,即可收敛到比较高的查全率和覆盖率。而论文设计的用于图搜索的Beam-retrieval算法相比于原始的HNSW算法,在查全率和覆盖率上一直较优。

在模型结构上,实验了3种结构:

  • DNN w/ attention:包含用户网络、注意力网络、商品网络、评分网络;
  • DNN w/o attention:去掉注意力网络,对行为序列中的商品进行Embedding计算后得到的向量列表进行加和池化,其他结构保持和DNN w/ attention相同;
  • two-sided:常用的双塔模型,用户侧,对行为序列中的商品进行Embedding计算后得到的向量列表进行加和池化,并和用户网络的输出拼接在一起,作为用户的Embedding向量;商品侧使用商品的Embedding向量,最后通过计算用户和商品向量的内积作为相关性度量输出。

实验结果如下所示: 图18 各模型结构比较 从中可以看到,从two-sided到DNN w/ attention,模型越复杂,召回率越高,但recall-Δ\text{recall-}\Delta也越高,说明图搜索造成的召回质量损失越大,通过引入对抗训练,能明显减小recall-Δ\text{recall-}\Delta,改善图搜索召回质量。

在淘宝展示广告中,通过A/B测试进一步验证了NANN的在线效果,基线模型采用TDM,评估指标是点击率CTR和千次展现广告收入RPM,NANN相比TDM,分别带来CTR增长2.4%和RPM增长3.1%的提升。

参考文献