item2vec召回
item2vec啊,其实就是word2vec,把word换成了item嘛!1. 知乎大佬的分享 2. 又一位大佬的分享 3. word2vec的分享
简单来说呢,CBOW是用周围词(item)预测中间词,skip-gram是用中间词预测周围词。经验上讲skip-gram效果好一些。但真说起来我还确实没有自己实现过... 牛逼又清楚的论文
先说skip-gram吧,也就是中心词预测周围词。怎么表示词呢?one-hot encoder,就是对应的位置是1,其他是0,这样一种编码。然后模型结构图可以理解为:
看图,输入x,接一层hidden,然后输出y,示意的其实是一个词预测下一个词(多个的后面再说)。hidden的激活函数其实是没做处理,为什么?
w2v是比youtube-dnn更早的模型,在youtube-dnn模型里我们的emb是look up出来的...看出来了吧?one-hot-encoder接一层线性hidden,其实相当于在W里loop up了一个emb,变成了hidden。
那问题来了,还记得我们在youtube-dnn模型里把hidden到输出前的W'当做了item的emb,这里是不是也可以呢?我个人感觉是都可以的,但实践中一般是用的输入矩阵,原因是含义上如此,这里的W'跟youtube的W'理解起来是不一样的。不过其实可以直接用W的转置当做W',感觉没啥问题。
一对一的看懂了,一对多也就好懂了:
啊,原来就是换成多个y嘛!于是问题又来了,loss怎么算?或者说,要怎么训练?
我们先从一对一开始看。其实细节在上面提到的“牛逼又清楚的论文”里有的,github的md不识别公式,偷懒贴点图了。
这个很好理解了,其实最后是一种交叉熵形式嘛,可以梯度更新,不用细说了。
一对多的形式是这样的:
再简单一说CBOW,其实就是skip-gram倒过来嘛。对于sg来说是一对多,所以output是多个word,而对于cbow来说是多对一,所以output是一个但是hidden是一串。它的loss形式是这样的:
注:有的资料示意图上面画的是c个word的sum,而上面图中公式是average。这里我认为average更合理一些,不然随着c的变化,h变化太多。也可以参考youtube-dnn中对input emb的处理,是直接avg。
现在我们已经大概清楚了模型结构,那有没有什么问题?
没错,问题跟youtube-dnn时的问题一样,word太多啦,算loss要疯掉了。
有两种优化的方法(其实youtube-dnn也是一样),分别叫做Hierarchical Softmax 和 Negative Sampling。
Hierarchical Softmax呢,我也没细研究...大概是说,用Huffman树把N分类的问题变成了log(N)次二分类,所以简化了softmax计算。实践中从来没用过,因为这玩意计算量比负采样大。
负采样好啊,跟youtube-dnn的理解一致了,其实就是每次只算了一个子集内的loss。
# 候选采样(采出来是样本)
tf.nn.uniform_candidate_sampler
tf.nn.log_uniform_candidate_sampler
# 候选采样loss(返回是loss)
tf.nn.sampled_softmax_loss
tf.nn.nce_loss
关于这个负采样我也只浮于表面,不够深入,如果以后有机会再补上吧。其实这里有很多问题...可以多看看别人的分享学习一下: 1. 关于nce_loss: Noise Contrastive Estimation 的简书分享 2. nce_loss的知乎问题 3. 知乎大佬的负采样分享 道听途说:nce可以多label,而sample_softmax只能one label,其实我们训练的时候one label就可以吧?假如一个中心词预测c个周围词,那就相当于产生了c个样本。
好了,懂了w2v,把w换成i就是i2v了。有点小区别就是,w2v里样本是“句子”产生的,所以是有顺序的,i2v里的item其实是set,可以认为无序。也就是说,一个用户的所有观看(举例而言的,其实有时候观看完成度太低是不算正样本的,而且会有长度限制、时效限制)构成set,set里item两两互为正样本。
实践中实现方式多种多样,可以用tf,见文件item2vec-tf-code.py,也可以用gensim库gensim链接:gensim.models.Word2Vec(user_sentences, size=args.dimension, workers=10)
item2vec怎么召回呢?简单,都有embedding了,cos相似度走起。做倒排,不用说了。实际推荐效果是不错的,占比也不低。但据说node2vec、graph emb等效果都会更好,这也不敢打包票,得看具体业务和实现吧。
加个问题,cbow和skip-gram的区别是什么?或者优缺点?因为本质上训练的时候,都是one-label的,所以对于cbow来说,过一遍文档(假设n个词)其实更新了n次;而对于skip-gram来说,因为是中心词预测周围词(假设C个),所以过一遍文档是更新了C*n次。所以sg比cbow时间复杂度高一些,训练起来慢,但是呢,更新次数多,也使emb更精细,更准确,所以效果好,尤其对生僻词更友好。通俗的理解就是,cbow是一个老师教c个学生,而sg是c个老师教一个学生。