京东个性化向量召回算法DPSR

Posted zhiyong_will

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了京东个性化向量召回算法DPSR相关的知识,希望对你有一定的参考价值。

1. 概述

对于一个搜索系统来说,通常采用的召回都是基于倒排索引的召回,简单来说就是需要对item侧建立倒排索引,在检索的过程中,对query分词,根据分词结果去倒排索引中查找词匹配的item,简单的流程如下图所示:

在传统的检索过程中,通常存在语义上的问题,比如Query为“连衣裙”,此时根据词匹配就检索不到核心词为“长裙”的item,为了解决这样的问题,需要对Query进行扩展,将其扩展成相似的query,以此来实现语义匹配的问题。当然,词匹配的方式的一大优势就是相关性解决的比较好。为了能从算法上解决语义的gap,双塔的召回结构是现如今较多采用的一种方法。

双塔的召回有如下的一些优势:

  • item侧可离线计算,线上可采用基于ANN的向量匹配方式较快的查找到向量空间与Query相匹配的item
  • 基于模型的方法,可以融入更多的特征

基于这样的优势,京东也提出了自己的向量召回算法DPSR(Deep Personalized and Semantic Retrieval)[1],着重解决两个方面的问题,第一是语义相关而并非严格的词匹配;第二是提供更多的个性化。

2. DPSR的算法原理

2.1. DPSR的模型结构

DPSR的模型结构如下图所示:

从上面的DSPR模型结构来看,与传统的双塔结构并没有太多的不一致,只是在Query和User侧设计了多向量的结构,这一点与MIND[2]算法倒是有点类似,只是方法上略有区别,其主要的目的就是用户刻画用户的多兴趣表示。

假设用 Q ( q ) ∈ R d × m Q\\left ( q \\right )\\in \\mathbbR^d\\times m Q(q)Rd×m表示query侧的塔输出的 m m m d d d维的向量, S ( s ) ∈ R d × n S\\left ( s \\right )\\in \\mathbbR^d\\times n S(s)Rd×n表示的是item侧的塔输出的 n n n d d d维的向量,基于上图中,其中 n = 1 n=1 n=1。最终相似度的计算方法为:

f ( q , s ) = G ( Q ( q ) , S ( s ) ) = Q ( q ) T S ( s ) f\\left ( q,s \\right )=G\\left ( Q\\left ( q \\right ),S\\left ( s \\right ) \\right )=Q\\left ( q \\right )^TS\\left ( s \\right ) f(q,s)=G(Q(q),S(s))=Q(q)TS(s)

为了实现 k k k个向量的输出,这里采用了Projection层(实际上可以理解成 k k k个投影矩阵),与MIND[3]中使用动态路由的方式有点不同。

2.2. 模型的训练

假设query塔的输出 Q ( q ) Q\\left ( q \\right ) Q(q) e 1 , e 2 , ⋯   , e m \\left\\e_1,e_2,\\cdots ,e_m \\right\\ e1,e2,,em,其中 e i ∈ R d e_i\\in \\mathbbR^d eiRd,item侧塔的输出 S ( s ) S\\left ( s \\right ) S(s) g ∈ R d g\\in \\mathbbR^d gRd,两个塔之间的相似性计算为:

G ( Q ( q ) , S ( s ) ) = ∑ i = 1 m w i e i T g G\\left ( Q\\left ( q \\right ),S\\left ( s \\right ) \\right )=\\sum_i=1^mw_ie_i^Tg G(Q(q),S(s))=i=1mwieiTg

其中 w i w_i wi是一个权重的表示,其具体计算方法为:

w i = e x p ( e i T g / β ) ∑ j = 1 m e x p ( e j T g / β ) w_i=\\fracexp\\left ( e_i^Tg/\\beta \\right )\\sum_j=1^mexp\\left ( e_j^Tg/\\beta \\right ) wi=j=1mexp(ejTg/β)exp(eiTg/β)

其中 β \\beta β是一个温度参数。

温度参数 β \\beta β在较多计算softmax的场景下有涉及,如知识蒸馏[3]中,其主要作用是控制softmax的输出的“平滑程度”。简单对其分析可以发现,当 β \\beta β越大,结果就越平滑;反之,得到的概率分布就越尖锐。

这里的做法与Label-aware Attention的做法相似。除了上述的相似性的计算外,在DPSR中选择的样本为正负样本对的形式,即:

D = ( q i , s i + , N i ) ∣ i , r ( q i , s i + ) = 1 , r ( q i , s j − ) = 0    ∀ s j − ∈ N i D=\\left\\\\left ( q_i,s_i^+,N_i \\right )\\mid i, r\\left ( q_i,s_i^+ \\right )=1, r\\left ( q_i,s_j^- \\right )=0\\; \\forall s_j^-\\in N_i \\right\\ D=(qi,si+,Ni)i,r(qi,si+)=1,r(qi,sj)=0sjNi

对应的损失函数就是hinge loss:

L ( D ) = ∑ ( q i , s i + , N i ) ∈ D ∑ s j − ∈ N i m a x ( 0 , δ − f ( q i , s i + ) + f ( q i , s j − ) ) L\\left ( D \\right )=\\sum_\\left ( q_i,s_i^+,N_i \\right )\\in D\\sum_s_j^-\\in N_imax\\left ( 0,\\delta -f\\left ( q_i,s_i^+ \\right )+f\\left ( q_i,s_j^- \\right ) \\right ) L(D)=(qi,si+,Ni)DsjNimax(0,δf(qi,si+)+f(qi,sj))

2.3. 样本选择

负样本的选择一直是双塔召回模型中最重要的一块内容,对于正样本没有争议,通常选择点击样本作为正样本,而对于负样本通常有两种策略:

  • 根据频次从全库中随机采样
  • batch内的随机采样

这是随机样本的部分,通常还会增加一些困难样本,主要有人工的挖掘以及在线的困难样本挖掘。在DPSR中则是将负样本分成了两个部分,分别为随机负样本和batch负样本。

在DPSR中,随机负样本的选择就是简单的随机采样,并没有考虑到样本的频次问题。从全库的 N N N个item中得到随机的负样本 N i r a n d N_i^rand Nirand;Batch负样本是在Batch内通过重组得到负样本,在Batch内,本身是正样本的集合 B = ( q i , s i + , N i ) ∣ i B=\\left\\\\left ( q_i,s_i^+,N_i \\right )\\mid i \\right\\ B=

item2vec将用户的行为序列转化成item组成的句子,模仿word2vec训练word embedding将item embedding。基本思想是把原来高维稀疏的表示方式(one_hot)映射到低维稠密的向量空间中,这样我们就可以用这个低维向量来表示该项目(电影),进而通过计算两个低维向量之间的相似度来衡量两个项目之间的相似性。
embedding就是用一个低维的向量表示一个物体,可以是一个词,或是一个商品,或是一个电影等等。这个embedding向量的性质是能使距离相近的向量对应的物体有相近的含义
类似于Word2vec,item2vec有两种方式:CBOW和skip-gram模型。
CBOW使用的是词袋模型,模型的训练输入是某一个特征词的上下文相关的词对应的词向量,而输出就是这特定的一个词的词向量。Skip-Gram模型和CBOW的思路是反着来的,即输入是特定的一个词的词向量,而输出是特定词对应的上下文词向量。

主流程:

  • 从log中抽取用户行为序列
  • 将行为序列当成预料训练word2Vec得到item embedding
  • 得到item sim关系用于推荐

在代码中,我们直接用gensim库实现。在gensim中,word2vec 相关的API都在包gensim.models.word2vec中。和算法有关的参数都在类gensim.models.word2vec.Word2Vec中。算法需要注意的参数有:

  1. sentences: 我们要分析的语料,可以是一个列表,或者从文件中遍历读出。
  2. size: 词向量的维度,默认值是100。这个维度的取值一般与我们的语料的大小相关,如果是不大的语料,比如小于100M的文本语料,则使用默认值一般就可以了。如果是超大的语料,建议增大维度。
  3. window:即词向量上下文最大距离,window越大,则和某一词较远的词也会产生上下文关系。默认值为5。在实际使用中,可以根据实际的需求来动态调整这个window的大小。如果是小语料则这个值可以设的更小。对于一般的语料这个值推荐在[5,10]之间。
  4. sg: 即我们的word2vec两个模型的选择了。如果是0, 则是CBOW模型,是1则是Skip-Gram模型,默认是0即CBOW模型。
  5. hs: 即我们的word2vec两个解法的选择了,如果是0, 则是Negative Sampling,是1的话并且负采样个数negative大于0, 则是Hierarchical Softmax。默认是0即Negative Sampling。
  6. negative:即使用Negative Sampling时负采样的个数,默认是5。推荐在[3,10]之间。这个参数在我们的算法原理篇中标记为neg。
  7. cbow_mean: 仅用于CBOW在做投影的时候,为0,则算法中的\\(x_w\\)为上下文的词向量之和,为1则为上下文的词向量的平均值。在我们的原理篇中,是按照词向量的平均值来描述的。默认值也是1,不推荐修改默认值。
  8. min_count:需要计算词向量的最小词频。这个值可以去掉一些很生僻的低频词,默认是5。如果是小语料,可以调低这个值。
  9. iter: 随机梯度下降法中迭代的最大次数,默认是5。对于大语料,可以增大这个值。
  10. alpha: 在随机梯度下降法中迭代的初始步长。算法原理篇中标记为ηη,默认是0.025。
  11. min_alpha: 由于算法支持在迭代的过程中逐渐减小步长,min_alpha给出了最小的迭代步长值。随机梯度下降中每轮的迭代步长可以由iter,alpha, min_alpha一起得出。对于大语料,需要对alpha, min_alpha,iter一起调参,来选择合适的三个值。

训练完模型后,常见的用法如下:

#找出某一个词向量最相近的词集合
model.wv.similar_by_word(\'沙瑞金\'.decode(\'utf-8\'), topn =100)
#看两个词向量的相近程度
model.wv.similarity(\'沙瑞金\'.decode(\'utf-8\'), \'高育良\'.decode(\'utf-8\'))
#找出不同类的词
model.wv.doesnt_match(u"沙瑞金 高育良 李达康 刘庆祝".split())

全部代码如下所示:

#-*-coding:utf-8-*-
"""
author:jamest
date:20190405
CBOW function
"""
import pandas as pd
from gensim.models import Word2Vec
import multiprocessing
import os

class CBOW:
    def __init__(self,input_file):
        self.model = self.get_train_data(input_file)

    def get_train_data(self,input_file,L=100):
        if not os.path.exists(input_file):
            return
        score_thr = 4.0
        ratingsDF = pd.read_csv(input_file, index_col=None, sep=\'::\', header=None,
                                names=[\'user_id\', \'movie_id\', \'rating\', \'timestamp\'])
        ratingsDF = ratingsDF[ratingsDF[\'rating\']>score_thr]
        ratingsDF[\'movie_id\'] = ratingsDF[\'movie_id\'].apply(str)
        movie_list = ratingsDF.groupby(\'user_id\')[\'movie_id\'].apply(list).values
        print(\'training...\')
        model = Word2Vec(movie_list, size=L, window=5, sg=0, hs=0, min_count=1, workers=multiprocessing.cpu_count(),iter=10)
        return model

    def recommend(self,movieID,K):
        """
         Args:
             movieID:the movieID to find similar
             K:recom item num
         Returns:
             a dic,key:itemid ,value:sim score
         """
        movieID = str(movieID)
        rank = self.model.most_similar(movieID,topn=K)
        return rank

if __name__ == \'__main__\':
    moviesPath = \'../data/ml-1m/movies.dat\'
    ratingsPath = \'../data/ml-1m/ratings.dat\'
    usersPath = \'../data/ml-1m/users.dat\'

    rank = CBOW(ratingsPath).recommend(movieID=1,K=30)
    print(\'CBOW result\',rank)

参考:
推荐系统概述(一)
Github

以上是关于京东个性化向量召回算法DPSR的主要内容,如果未能解决你的问题,请参考以下文章

直播预告 | 1月12日下午,“搜索算法”专场①:淘宝搜索召回个性化搜索排序...

问答机器人召回优化

京东的个性化推荐系统

京东推荐广告算法彭长平:京东电商推荐系统做到60分容易,做到80分90分却很难

机器学习-支持向量机SVM算法

推荐系统[八]算法实践总结V1:淘宝逛逛and阿里飞猪个性化推荐:召回算法实践总结冷启动召回复购召回用户行为召回等算法实战