推荐算法实战-6-召回(2)-word2vec与双塔召回

 

本文是推荐算法实战系列第六篇文章。

前面文章包括:

  1. 推荐系统简介
  2. 特征工程
  3. embedding
  4. 精排
  5. 召回(1):传统召回以及召回中的loss设计

本文介绍三类召回模型:word2vec、FM、双塔。

3、Word2Vec召回

Word2Vec在给定的文本语料库中,通过自监督学习每个单词的word embedding。

它的思想与bert和GPT如出一辙。

它有skip-gram和cbow两种模式。以skip-gram为例,其原理为,给定一个中心词w,预测哪些词o能够出现在w的上下文中与w搭配使用

例如,给定一句话“the quick brown fox jumps over the lazy dog”,当我们选中fox作中心词,且取上下午窗口大小为1时,word2vec的训练目标是使“fox,brown”和“fox,jumps”这样的单词组合出现的概率越大越好。

word2vec的损失函数为:

\(L_{word2vec} = -\frac{1}{|B|}\sum_{({w_i},c_i) \in B} log \frac{exp(\pmb w_i \cdot \pmb c_i)}{\sum_{j \in V} exp(\pmb w_i \cdot \pmb c_j)}\)

  • 它是softmax loss。
  • B:batch,一条样本(wi、ci)由一个中心词wi和上下文ci组成。
  • V:所有单词集合,语料库的词典。
  • ${\pmb w_i}$、$ \pmb c_i $是wi和ci的word embedding。二者的点积越大,说明关联性越强,越有可能出现在同一个上下午中

每次计算都要遍历V中所有单词,计算量太大。按照上一节介绍的简化softmax公式,word2vec可以采用NEG loss训练:

\[L_{word2vec-NEG} = \frac{1}{|B|}\sum_{(w_i, c_i) \in B} \Big[log(1 + exp(-\pmb{w_i \cdot c_i})) + \sum_{j \in S_i} log(1 + exp(\pmb{w_i \cdot c_j})) \Big]\]
  • Si:给中心词wi随机采样一批单词作为它的负样本。
  • 与上一节U2I中的NEG loss形式几乎完全一样。

3.1、Item2Vec召回

将word2vec应用到推荐领域最直接的算法是Item2Vec。

将用户的某个行为序列(例如,用户在一个session内点击过的物料序列)当作一个句子,序列中的每个itemid当作一个单词。这种方式构造完数据集后,可以直接套用word2vec训练,得到每个item的embedding,可以用于I2I召回。

  • 正样本:认为同一个用户在同一个session内交互过的物料,彼此之间相似,它们的embedding也应该相似。考虑到序列内两两组合生成的正样本太多,item2vec照搬word2vec,也采用滑窗,即只在某个物料前后出现的其它物料才当作正样本。
  • 负样本:仿照word2vec,在整个物料库中随机采样一部分作为当前物料的负样本(备注:整个物料库随机采样or batch内负采样?)
  • 如何生成embedding:没有采用模型,只是定义了一个待学习的矩阵$V \in R^{ T \times d}$。其中, T 是所有物料的个数,d是embedding的维度大小。第i个物料的embedding就是V的第i行。
  • loss:NEG loss。

3.2、airbnb召回

Item2Vec直接把word2vec套用到用户行为序列组成的“句子”上,尽管简单,但是忽略了推荐领域与NLP的差异。例如,NLP认为一个单词只与它周围的单词相关性高;推荐领域中,一个session内第一个点击的物料与最后一个物料很有可能仍然高度相关。

airbnb结合“民宿中介”的业务特点,对word2vec进行了若干改进

3.2.1、airbnb的I2I召回

  • 正样本:按照item2vec和word2vec的思路,给定一个用户的房屋(listing,l)点击序列,采用滑窗后,认为某个房屋l只与窗口内的有限几个房屋c才是相似的。但是,如果一个点击序列最终导致某个房屋$l_b$转化(成功预定),$l_b$的业务信号非常强,必须保留。因此,额外增加了一批正样本,即点击序列中的每个l与最终的$l_b$是相似的
  • 负样本:根据召回的一贯原则,随机采样得到的其它房屋是负样本的主力。但是在“民宿中介”的业务场景中,每个点击序列的房屋基本是同城的,而随机采样的房屋很多是异地的。这样以来,模型可能学习到只使用“是否同城”来判断两个房屋是否相似,忽视了同城内房屋的差异。为了弥补随机负采样的不足,Airbnb为每个房屋在同城采样一部分房屋作为hard negative,引导模型关注“是否同城”之外的更多细节
  • embedding生成:类似Item2Vec,定义一个大矩阵V。第i个房屋的embedding就是V的第i行。
  • loss:与word2vec一样,采用NEG loss,但是增加了额外的正负样本。
\[L_{AirbnbI2I} = \frac{1}{|B|}\sum_{(l,c) \in B}\Big[log(1 + exp(-\pmb{v_l \cdot v_c})) + \sum_{nc \in N_l}log(1 + exp(\pmb(v_l \cdot v_{nc}))) + log(1 + exp(-\pmb{v_l \cdot v_{lb}})) + \sum_{(nc) \in N_{city}}log(1 + exp(\pmb {v_l \cdot v_{ncity}})) \Big]\]
  • $v$:房屋的embedding
  • $l$:当前房屋
  • $c$:某点击序列中,出现在l上下文的房屋,正例。
  • $nc$:随机采样的房屋,负例。
  • $l_b$:当前点击序列引导预定的房屋,正例。
  • $n_{city}$:l同城内随机采样的负例。

3.2.2、airbnb的U2I召回

Airbnb的第二个创新是将word2vec扩展到了U2I和冷启动领域。

希望从更重要的预定数据中学习用户和房屋的embedding。问题在于,大多数用户预订和大多数房屋的被预订记录非常有限。稀疏的预定数据难以学习高质量的用户和房屋embedding。

为此,airbnb的做法是,根据属性和人工规则,将用户和房屋归类。单个用户和单个房屋的预定记录稀疏,但是某类用户和某类房屋的预定记录就丰富许多,允许算法学习更高质量的某类用户和某类房屋的embedding。有助于新用户和新房屋的冷启动。

  • 正样本:如果某用户u预定过某房屋l,u所属的用户类别U和l所属的房屋类别L应该是相似的,属于正样本。
  • 负样本:对于一个用户类型U,随机采样一部分房屋类型作为主力负样本。此外,如果u被某个房东拒绝,该房屋的类型L就称为了U的hard negative
  • embdding生成:定义两个待学习的矩阵表示用户类型和房屋类型的embedding,$V_{UT} \in R^{ UT \times d}$和$V_{LT} \in R^{ LT \times d}$, UT 是所有用户类型的个数, LT 是所有房屋类型的个数。第i类用户的embedding就是$V_{UT}$的第i行,第i类房屋的embedding就是$V_{LT}$的第i行。
  • loss:和word2vec一样,采用NEGloss,只不过增加了“被房东拒绝”作为hard negative。
\[L_{AirbnbU2I} = \frac{1}{|B|} \sum_{(ut,lt) \in B} \Big[ log(1 + exp(-\pmb{v_{ut} \cdot v_{lt}})) + \sum_{(nlt) \in N_{ut}}log(1 + exp(\pmb{v_{ut} \cdot v_{nlt}})) + \sum_{nlt \in N_{rlt}} log(1 + exp(\pmb{v_{ut} \cdot v_{rlt}})) \Big]\]
  • ut:当前用户类型。
  • lt:被ut这一类用户(中的任意一个)预定过的房屋类型,正样本。
  • nlt:给ut随机采样的负样本房屋类型,easy negative。
  • rlt:拒绝过ut的房屋类型,hard negative。

3.3、阿里的EGES召回

阿里于2018年提出Enhanced Graph Embedding with Side Information(EGES)模型,是将word2vec移植到推荐领域的又一次改进。

3.3.1、正样本

无论是item2vec还是airbnb I2I都认为只有同一个用户在同一个session内点击过的物品才有相似性,才可以作为正样本。

这太狭隘了。如果一个用户点击了A和B,另一个用户点击了B和C。item2vec认为只有AB、BC之间才有相似性,但是,AC没有相似性吗?

ECGS认为应该把这种跨用户、跨session的相似性考虑进去,它借助图数据结构来实现。

  • 首先,根据用户行为序列,建立物料关系有向图item graph。图的节点表示item,边表示两个物料在某一个session序列内被顺序交互过。例如,用户U1在一个session内顺序点击过DAB,则有两条边D->A和A->B。节点i、j之间的边的权重$M_{ij}$=数据集中先点击i后点击j的次数
  • 沿着上述item graph 随机游走产生一批新的序列。随机游走时,节点i到j的转移概率$P(v_j v_i) = \frac{M_{ij}}{\sum_{j \in N_+(v_i)}M_{ij}}$。其中,$N_+(v_i)$表示由节点i出发的邻居节点的集合。
  • 在新序列上再套用word2vec,定义滑窗,窗口内的物料相似,是正样本。

3.3.2、负样本

word2vec中的随机负采样。

3.3.3、embedding生成

推荐系统与NLP的差别是,NLP中通常除了单词自身没有别的特征可用。推荐系统除了ID,还有很多其它特征,例如丰富的属性信息(side information),包括类别、品牌等等。

item2vec中只用了id信息,没有用其它特征。训练集中没有出现的ID无法获得其embedding,存在冷启动问题。

ECGS利用这些属性信息合成新物料的embedding,缓解冷启动问题

首先,给itemid和每个属性定义一个embedding矩阵,共1+n个embedding 矩阵

其次,将每个物料的id embedding和它的n个属性embedding融合成一个embedding Hv。

最简单的融合方法是average pooling:

\[H_v = \frac{1}{1+n}\sum_{s=0}^nW_v^s\]

复杂的方法是,允许每个物料给每个属性分配不同的融合权重,类似于attention机制:

\(H_v = \sum_{j=0}^n \Big[ \frac{exp(a_v^j)}{\sum_{j=0}^nexp(a_v^j)} W_v^j \Big]\)

  • v:第v个物料。
  • j:每个物料的第j个属性,j=0表示ID。
  • $a_v^j$:标量,第v个物料的第j个属性的重要性,通过训练学习得到。$exp(a_v^j)$是为了保证权重为正。分母是对所有(n+1)个属性的权重求和作为归一化因子。
  • $A$:所有的$a_v^j$构成一个待学习的权重矩阵$A \in R^{ V \times (1+n)}$。 V 是物料个数,n是属性个数,+1表示ID。A的第v行第j列即$a_v^j$。
  • $W_v^j$:表示物料v的属性j的embedding。(备注:单个物料的属性j的embedding是向量,所有V个物料的属性j的embedding是一个矩阵)

EGES和airbnb U2I,都将ID之外的属性信息引入word2vec,从而缓解冷启动、数据稀疏问题

二者引入属性信息的方法不同:

  • airbnb U2I通过人工先验规则将用户和物料分类,用类的embedding代替单个用户、物料的embedding
  • EGES将各类属性信息分别embedding。至于这些embedding是什么,如何与id embedding融合,都由算法学习。避免依赖人工规则,但增加了模型的参数量,需要更多的训练数据。

3.3.4、loss

采用word2vec的NEG loss。

4、FM召回-瑞士军刀

FM既可以用在精排,也可以用在召回,甚至粗排。

4.1、打压热门物料

FM召回主要用于U2I。很自然地,用户与他交互过的物料构成正样本,随机采样部分物料作为负样本。

看似简单,但是存在一个热门物料打压的问题,值得重视。

推荐系统存在“2-8”定律,即少量热门物料占据了绝大部分曝光点击。按照上述样本构造方式,正例数据被少数热门物料垄断。其后果是,训练时,模型会使用户向量靠拢热门物品向量。预测时召回大部分是热门物料,失去了个性化与多样性

视物料出现在正例还是负例中采取不同的打压策略。

4.1.1、热门物料当正例要降采样

可以参考word2vec过滤高频词的做法,定义物料ti能够被任何用户选为正样本的概率如下: \(P_{pos}(t_i) = \sqrt{\frac{\alpha}{f(t_i)}}\)

  • f(ti)是物料ti的曝光频率。ti曝光越频繁(越热门),它作为正样本的概率越低。
  • $\alpha$是超参数。可以理解称为“冷门物料”的门槛。如果$f(t_i) <= \alpha$还被点击过,那么它必须成为正样本(概率=1)。

4.1.2、热门物料当负例要过采样

负采样时对热门物料应该过采样:

  • 既然热门物料已经垄断正样本,我们需要提高热门物料在负样本中的比例,以抵消热门物料对损失函数的影响。
  • 如果负采样用uniform sampling,很有可能采样到冷门的、与用户“八杆子打不着”的easy negative。因为绝大多数用户喜欢热门,热门物料当负例能构成hard negative,提升模型分辨能力。

随机负采样时,一方面需要尽可能广泛地覆盖所有候选物料,另一方面需要多采样一些热门物料。为了平衡两方面需求,参考word2vec,定义负采样概率如下:

\(P_{neg}(t_i) = \frac{F(t_i)^b} { \sum_{t' \in V} F(t')^b }\)

  • V:所有候选物料
  • F(ti):第i个物料的曝光次数。
  • b:超参数。b=1时,负采样完全遵循物料热度,对热门物料打压最厉害,对所有候选物料的覆盖不足。b=0时,退化成uniform sampling,对所有候选物料覆盖度最高,但是热门物料无打压,每个物料采样概率一样,大部分采样到的是easy negative。根据word2vec的经验,b一般取0.75

4.2、增广embedding

不同于item2vec,airbnb召回只能使用userid、itemid特征,FM可以使用更丰富的特征。只是,FM召回不能使用user-item交叉特征。

FM的公式如下: \(FM(u,t) = b + \sum_{i \in I(u,t)}w_i + \sum_{i \in I(u,t)}\sum_{(j=i+1) \in I(u,t)} \pmb {v_i \cdot v_j}\)

  • 一条样本由一个用户u和一个物料t组成,I(u,t)表示这条样本中所有非零特征的集合
  • b:bias
  • wi:第i个特征的一阶权重
  • vi:第i个特征的embedding。

将第二、三项按照特征是用户特征还是物料特征进行拆解,得到: \(FM(u,t) = b + [W_u + W_t] + [V_{uu} + V_{tt} + V_{ut}]\)

  • Wu:用户特征的一阶权重和(or加权和?)。
  • Wt:物料特征的一阶权重和。
  • Vuu:用户特征内部两两二阶交叉权重和。
  • Vtt:物料特征内部两两二阶交叉权重和。
  • Vut:用户特征与物料特征两两二阶交叉和。(FM不能使用用户-物料二阶交叉?)

在给不同物料打分时,用户是固定的,Wu、Vuu对同一个用户的不同物料是相同的,可以去掉

\[FM(u,t) = b + W_t + V_{tt} + V_{ut}\]

将Vtt变形,降为线性复杂度,如下公式,其中reducesum表示一个向量所有位置上的元素求和: $$ \begin{aligned} V_{tt} &= \sum_{i \in I_t} \sum_{(j=i+1)\in I_t} \pmb {v_i \cdot v_j}
&= \frac{1}{2}reducesum \Big[ \Big( \sum_{i \in I_t} \pmb v_i \Big)^2 - \sum_{i \in I_t} \pmb{v_i^2}

\Big] \end{aligned} $$

再将Vut也拆解成两个向量点积的形式:

\(V_{ut} = \sum_{i \in I_u}\sum_{j \in I_t} \pmb {v_i \cdot v_j} = \Big( \sum_{i \in I_u} \pmb{v_i} \Big) \cdot \Big( \sum_{j \in I_t} \pmb v_j \Big)\) 代入FM(u,t),得到:

$$ \begin{aligned} FM(u,t) &= \pmb E_u \cdot \pmb E_t
E_u &= concat(1, \sum_{i \in I_u} \pmb v_i)
E_t &= concat(W_t + V_{tt}, \sum_{j \in I_t}\pmb v_j)

\end{aligned} $$

  • Eu:用户向量,召回时,线上实时计算。
  • Et:物料向量。离线提前计算好,并存入FAISS建立索引。
  • 该公式只针对预测阶段。

训练阶段,没必要将用户特征与物料特征拆开,假设采用BPR loss,FM召回的损失函数如下:

\(\begin{aligned} FM(u,t) &= b + \sum_{i \in I(u,t)}w_i + 1/2reducesum \Big[ \Big(\sum_{i \in I(u,t)} v_i \Big)^2 - \sum_{i \in I(u,t)} v_i^2 \Big] \\ L_{BPR} &= \frac{1}{|B|}\sum_{(u_i, t_{i+}, t_{i-}) \in B} log(1 + exp(FM(u_i, t_{i-}) - FM(u_i, t_{i+}))) \end{aligned}\)

  • B:batch。一条样本是一个三元组。
  • ti+:ui点击过的正样本。
  • ti-:给ui随机采样的负样本。

5、双塔模型:大厂主力

双塔模型由微软提出的DSSM模型演进而来。

5.1、正样本

双塔模型可以用在不同的召回场景,对应不同的正样本策略:

  • U2I:一个用户u与他交互过的物料t,相互匹配,构成正样本。
  • I2I:一个用户在同一个session内交互过的两个物料ti和tj相似,构成正样本。
  • U2U:一个用户的一半行为历史,与同一个用户的另一半行为相似,构成正样本。

    5.2、负样本

    负样本依赖负采样。

    5.2.1、In-batch负采样

    以点击场景的U2I为例:

  • 一个batch内,第i条样本由用户ui与他点击过的物料ti组成,是正样本。
  • 输入一个batch中的所有样本都是正样本,第i条样本为(ui, ti),没有标签y,因为默认都是正样本,负样本靠batch内采样得到。
  • 同一个batch内,除了ti之外,其余的正例物料都作为ui的负例,即(u_i, t_j)都是负样本。

训练时,batch内的每个用户向量ui,要与每个样本中的物料向量做点击。由于tj已经作为uj的正例计算过了,不用重复计算。

缺点是样本选择偏差SSB。召回的正样本来自点击,被点击的多是热门物料。再加上一个batch大小有限,其中的热门物料更加集中,与线上召回时的候选集差异较大。

换句话说,in-batch采样到的大都是hard negative,缺少与用户兴趣八竿子打不着的easy negative。

可以借助矩阵运算方便地实现in-batch负采样。

  • user矩阵:U[B,dim]
  • item矩阵:I[B,dim]
  • scores:匹配得分,user-item矩阵相乘得到:S[B, B],元素S[i,j]表示user-i对item-j的匹配得分。
  • labels:[B,B],对角线上全1,其余位置全0。
  • 对于第i条样本,只有labels[i,i]=1,scores[i,i]是正样本得分;label[i,j]=0,scores[i,j]是负样本得分。
  • 对labeles,scores计算softmax loss。计算loss时注意logits需要根据负样本采样概率修正。

5.2.2、混合负采样

为了缓解SSB,facebook、google、华为等大厂提出了混合负采样。

  • 额外建立一个向量缓存,存储物料塔在训练过程中得到的跨batch的物料向量
  • 训练时,先进行batch内负采样,得到hard negative。
  • 同时,从向量缓存中采样一些之前的batch内计算好的物料向量,作为easy negative。

尽管一个batch内,热门物料比较集中,但是向量缓存汇集了多个batch的物料,从中能够采样到一些小众、冷门物料作为easy negative,起到让召回模型“开眼界、见世面”的效果。

5.3、embedding生成:双塔结构特点

5.3.1、单塔可以很复杂

每个塔都是一个DNN,结构可以灵活设计:

  • 以U2I为例,用户特征喂入用户塔,得到用户向量;物料(正例和负例)特征喂入物料塔,输出物料向量。
  • 塔的底座宽,可以接收丰富的特征。用户侧特征包括:userid、画像、行为历史等。物料特征包括:itemid、属性、静态画像、动态画像,内容理解算法产生的多模态表征特征。
  • 塔身高。每层的结构可以很复杂。底层喂入的信息在沿着塔身向上流动的过程中可以完成充分的交叉。

总之,双塔模型表达能力比item2vec、FM大大增强。

5.3.2、双塔要解耦

召回双塔只允许最终生成的用户向量、物料向量通过点积交叉一次,在此之前,二者必须解耦,不允许信息垮塔流动,避免双塔变单塔

为了解耦,特征上,不能做特征交叉(用户-物料lookup特征),结构上,不能使用DIN那种以候选物料做query的target attention。

但是用户的行为序列是反应用户兴趣的最重要的信息来源。如何将它们接入用户塔,是业界研究的重要课题。

最简单的做法,如youtube,把用户过去观看的视频先embedding 再average pooling。

改进方案仍然是给不同的历史行为赋予不同的权重(恰似EGES做法),由于不能用候选物料做target attention,业界提出了一些替代方案:

  • 如果是搜索场景,用户输入的搜索文本最能反应意图,可以作为attention中的query。
  • 阿里的SDM召回中,使用用户画像当query,给各个历史行为打分。
  • 微信的CDR模型,认为用户最后点击的物料,反映用户最新的兴趣,可以当query衡量之前历史行为的重要性。

5.4、loss

双塔模型常采用in-batch sampled softmax loss,以U2I为例,公式如下: \(\begin{aligned} G(u,t) &= \frac{\pmb{u_i \cdot t_i}}{\tau} - logQ(t) \\ L_{tower} &= -\frac{1}{|B|}\sum_{(u_i,t_i) \in B} log \frac{exp(G(u_i,t_i))}{exp(G(u_i, t_i)) + \sum_{j \in B, j \neq i}exp(u_i, t_j)} \end{aligned}\)

  • B: batch, 第i条样本(ui,ti)表示用户ui和他点击过的物料ti。
  • 第i条正样本对应的负样本,由同一个batch内的(ui,tj)组成,即in-batch 负采样。
  • G(u,t):用户u与t的匹配度。

    5.4.1、L2正则化

在计算向量u和向量t的点积之前,可以先除以它们各自的L2 norm,转化成长度为1的向量,再做点积,等价于计算余弦相似度cosine(u,t)。

很多实践表明,使用余弦相似度比点积更好。毕竟两个向量是否相似,看它们的夹角即可,没必要让优化器浪费精力调整向量的长度。

5.4.2、温度系数

loss公式中的$\tau$是温度系数,$0 < \tau <= 1$。

softmax loss 的优化目标是使正例的概率$P_{pos} = \frac{exp(G(u_i,t_i))}{exp(G(u_i,t_i)) + \sum_j exp(G(u_i,t_j))}$尽可能大,要求分母中的负例得分越小越好

$\tau$起到放大器的作用,如果某个负例(ui,tj)没有训练好,导致cosine(ui,tj)>0,$1/\tau$能将这个错误放大很多倍,导致分母变大,损失增加。那个没有被训练好的负例就会被模型聚焦,重点关注(备注,对正例的影响呢?)。

温度系数可以用来平衡召回模型的记忆性和扩展性,或者说,精确性和多样性

  • 如果系数很小,它对错误的放大能力很强。只要tj没有被用户点击过,模型就会强力将它拉开,模型将会牢牢记住用户历史点击反映出的兴趣爱好,不敢偏离太远。召回结果精度较高,但是对用户潜在兴趣覆盖不足,容易陷入信息茧房。
  • 如果系数较大,它对错误的放大功能就弱。对负样本不会拉开太远,召回时仍有可能命中。好处是覆盖范围扩大,坏处是tj可能不是ui喜欢的,召回把关太松,有损用户体验。

5.4.3、采样概率修正

loss公式中的Q(t)表示负采样得到某个物料t的概率。

in-batch负采样时,Q(t)等于t在所有点击样本中的占比。Q可以离线、定时统计得到,或者在线流式计算得到。

如果采用混合负采样,损失函数如下:

\(L_{tower} = -\frac{1}{|B|}\sum_{(u_i,t_i) \in B} log \frac{exp(G(u_i,t_i))}{exp(G(u_i,t_i)) + \sum_{j \in (B+B'),j \neq i}exp(G(u_i,t_j))}\)

  • 与in-batch唯一的差别是,负样本来自B、B’两部分。B’来自之前batch的向量缓存。

Q也来自两部分,是一个组合概率: \(Q(t) = \frac{|B|}{|B| + |B'|}Q_{in-batch}(t) + \frac{|B'|}{|B| + |B'|}Q_{cache}(t)\)

  • $Q_{in-batch}(t)$是batch内负采样,等于物料t在所有点击样本中的占比。
  • $Q_{cache}(t)$是在向量缓存中采样到tj的概率。如果均匀采样,$Q_{in-batch}(t) = \frac{1}{CacheSize}$ 。注意,此处只与缓存大小有关,与点击占比无关。

本文总访问量