推荐系统系列教程之十二:Facebook是怎么为十亿人互相推荐好友的?

 
 

   编者按:之前推出了《推荐系统系列教程》,反响不错,前面已经推出了十一期,今天按约推出第十二期:Facebook是怎么为十亿人互相推荐好友的。希望朋友们多点“在看”,多多转发,我会在“在看”数超过20后推出下一篇教程。

640?wx_fmt=png


    早在前面几篇务虚的文章中,我们就聊过了推荐系统中的经典问题,其中有一类就是评分预测。

回顾矩阵分解 

    矩阵分解要将用户物品评分矩阵分解成两个小矩阵,一个矩阵是代表用户偏好 的用 户隐因子 向量 组成,另一 个矩阵是代表物品语义主题的隐因子 向量 组成。
    这两个小矩阵相乘后得到的矩阵,维度和原来的用户物品评分矩阵一模一样。 如原来矩阵维度是 m*n,其中 m 是用户数量,n 是物品数量,再假如分 解后的隐因子向量是 k 个,那么用户隐因子向量组成的矩阵就是 m*k,物品隐因子向量组成的矩阵就是 n * k。  
    得到的这两个矩阵有这么几个特点:
    1、每个用 户对应一 k 维向量 ,每个物品也对应一 k 维向量 ,就是所 谓的隐因子 向量 ,因为是无 中生 有变出来的,所以叫做“隐因子 ”;
    2、两个矩阵相乘后,就得到了任何一个用户对任何一个物品的预测评分, 具体这个评分靠不靠谱,那就是看功夫了。  
    所以矩阵分解,所做的事就是矩阵填充。 那到底怎么填充呢,换句话也就是说 两个小 矩阵怎么得到呢?
    按照机器学习的套路,就是使用优化算法求解下面这个损失函数:

640?wx_fmt=png

    这个公式依然由两部分构成: 加号左边是误差平方 和,加号右边是分解后参数 的平方
    这种模式可以套在几乎所有的机器学习训练中:就是一个负责衡量模型准不准, 另一个负责衡量模型稳不稳定。行话 是这样说的:一个衡量模型的偏差,一个 衡量模型的方差。 偏差大的模型欠拟合,方差大的模型过拟合。
    有了这个目标函数后,就要用到优化算法找到能使它最小的参数。 优化方法常用的选择有两个,一个是随机梯度下降(SGD),另一个是交替最小二乘 (ALS )。  
    在实际应用中, 交替最小二乘更常用一些 ,这也是社交巨头Facebook在他们 的推荐系统中选择的主要矩阵分解方法,今天,我就专门聊一聊交替最小二乘求矩阵分解。 
交替最小二乘原理(ALS) 
    交替最小二乘的核心是交替,什么意思呢?你的任务是找到两个矩阵 P 和 Q, 让它们相乘后约等于原矩阵 R: 

640?wx_fmt=png

    难就难在,P 和 Q 两个都是未知的,如果知道其中一个的话,就可以按照线  性代数标准解法求得,  比 如如果知道了  Q ,那么 P 就可以这样算

640?wx_fmt=png

    也就是 R 矩阵乘以 Q 矩阵的逆矩阵就得到了结果。  
    反之知道了 P 再求 Q 也一样。 交替最小二乘通过迭代的方式解决了这个鸡 生蛋蛋生鸡的难题: 
    1、初始化随机矩阵 Q里面 的元素值;
    2、把 Q 矩阵当做已知的,直接用 线性代数的方 法求得矩阵 P;
    3、得到了 矩阵 P 后,把 P 当做已知的,故技重施,回去求解矩阵 Q;
    4、上面两个过程交替进行,一直到误差可以接受为止。
    你看吧,机器就是这么单纯善良,先用一个假的结果让算法先运转起来,然后不断迭代最终得到想要的结果。 这和做互联网C2C平台的思路也一样,告诉 买家说:快来这里,我们是万能的,什么都能买到!
    买家来了 去告诉卖家们说: 快来这里 开店,我这里 掌握了 最多的剁手 党。 嗯,雪球就这样滚起来了
    交替最小二 乘有这么几 个好处 :
    1、在交替的其中一 步,也就是假设已知其中一 个矩阵求解另一 个时,要优 化的参数是很容易 并行 化的;
    2、在不那么稀疏的数据集合上,交替最小二乘通常比随机梯度下降要更快 地得到结果,事实上这一点就是我马上要说的,也就是关于隐式反馈的内容。  
隐式反馈 
    矩阵分解算法,是为解决评分预测问题而生的,比如说,预测用户会给商品打几 颗星,然后把用 户可能打高 星的商品推荐给用 户,然而 事实上却是,用  首 先必须先去浏览商品, 然后是购买,最后才可能打分。
    相比“预测用户会打多少分”,“预测用户会不会去浏览”更加有意义,而且,用户浏览数据远远多于打分评价数据。 也就是说,实际上推荐系统关注的是预测行为,行为也就是一再强调的隐式反馈。  
    那如何从解决评分预测问题转向解决预测行为上来呢?这就是另一类问题了,行话叫做 One-Class。  
    这是什么意思呢?如果把预测用户行为看成一个二分类问题,猜用户会不会做 某件事,但实际上收集到的数据只有明确的一类:用户干了某件事,而用户明 确“不干”某件事的数据却没有明确表达。 所以这就是 One-Class 的由来, One-Class 数据也是隐式反馈的通常特点。  
    对隐式反馈的矩阵分解,需要将交替最小二乘做一些改进,改进后的算法叫做加权交替最小二乘:Weighted-ALS 。  
    这个加权要从哪说起?用 户对物品的隐式反馈,通常是可以多次的,你有 心心念念的衣 服或者电子 产品,但是刚刚剁完手 的你正在吃土 买不 起,只能每天去 看一 眼。
    这样一来,后台就记录了你查看过这件商品多少次, 查看次数越多,就代表你越喜欢这个。也就是说,行为的次数是对行为的置信度反应,也就是所谓的加权。
    加权交替最小二乘这样对待隐式反馈:
    1、如果用 户对物品无 隐式反馈则认为评分 0 ;
    2、如果用 户对物品有至 少一 次隐式反馈则认为评分是 1 ,次数作为该评分 的置信度。  
    那现在的目标函数在原来的基础上变成这样:

640?wx_fmt=png        多出来的 Cui 就是置信度,在计算误差时考虑反馈次数,次数越多,就越可信。 置信度一般也不是直接等于反馈次数,根据一些经验,置信度 Cui 这样计算:

640?wx_fmt=png

    其中阿尔法是一个超参数,需要调教,默认值取 40 可以得到差不多的效果, 

C 就是次数了。  
    这里又引出另一个问题,那些没有反馈的缺失值,就是在我们的设定下,取值为 0 的评分就非常多,有两个原因导致在实际使用时要注意这个问题: 
    1、本身隐式反馈就只有正类别是确定的,负类别是我们假设的,你要知道, One-Class 并不是随便起的名字;
    2、这会导致正负类别样本非常不平衡,严重倾斜到 0 评分这边。  
    因此,不能一股脑儿使用所有的缺失值作为负类别,矩阵分解的初心就是要填充这些值,如果都假设他们为 0了,那就忘记初心了。 应对这个问题的做法就是负样本采样:挑一部分缺失值作为负类别样本即可。
    怎么挑?有两个 法: 
    1、随机均匀采样和正类别一样多;
    2、 按照物品的热门程度采样。
    请允许我直接说结论, 第一种不是很靠谱,第二种在实践中经过了检验
    还是回到初心来,你想一想,在理想情况下,什么样的样本最适合做负样本?
    就是展示给用户了,他也知道这个物品的存在了,但就是没有对其作出任何反 馈。 问题就是很多时候 知道到底是用户没有意识到物品的存在呢,还是知道 物品的存在而不感兴趣呢?
    因此按照物品热门程度采样的思想就是:一个越热门的物品,用户越可能知道 它的存在。 那这种情况下,用户还没对它有反馈就表明:这很可能就是真正的 负样本。
    按照热门程度采样来构建负样本,在实际中是一个很常用的技巧,我之前和你提到的文本算法 Word2Vec 学习过程,也用到了类似的负样本采样技巧。  
推荐计算
    在得到了分解后的矩阵后,相当于每个 户得到了隐因子向量,这是一个稠密 向量,用于代表他的兴趣。 同时每个物品也得到了一个稠密向量,代表它的语 义或主题。而 且可以认为这两者是一一对应的,用户的兴趣就是表现在物品的 语义维度上的。
    看上去,让用户和物品的隐因子向量两两相乘,计算点积就可以得到所有的推荐结果了。 但是实际上复杂度还是很高,尤其对于用户数量和物品数量都巨 大的应用,如 Facebook,就更不现实。 于是 Facebook 提出了两个办法得到真 正的推荐结果。  
    第一种,利用一些专门设计的数据结构存储所有物品的隐因子向量,从而实现通过一个用户向量可以返回最相似的 K 个物品。  
    Facebook 给出了自己的开源实现 Faiss,类似的开源实现还有 Annoy, KGraph,NMSLIB 。  
    其中 Facebook 开源的 Faiss 和 NMSLIB(Non-Metric Space Library)都用到了 ball tree 来存储物品向量。  
    如果需要动态增加新的物品向量到索引中,推荐使用Faiss,如果不是,推荐 使用NMSLIB 或者 KGraph。用 户向量则可以存在内存数据中,这样可以在用户访问时,实时产生推荐结果。  
    第二种,就是拿着物品的隐因子向量先做聚类,海量的物品会减少为少量的聚 类。 然后再逐一计算用户和每个聚类中心的推荐分数,给用户推荐物品就变成了 给用户推荐物品聚类。
    得到给用户推荐的聚类后,再从每个聚类中挑选少许几个物品作为最终推荐结 果。 这样做的好处除了大大减小推荐计算量之外,还可以控制推荐结果的多样 性,因为可以控制在每个类别中选择的物品数 。
总结 
    在真正的推荐系统的实际应用中,评分预测实际上场景很少,而且数据也很少。 因此,相比预测评分,预测“用户会对物品干出什么事”,会更加有效。  
    然而这就需要对矩阵分解做一些改进,加权交替最小二乘就是改进后的矩阵分解算法,被 Facebook 采用在了他们的推荐系统中,这篇文章里,我也详细地 解释了这一矩阵分解算法在落地时的步骤和注意事项。  
    其中,我们提到了针对 One-Class 这种数据集合,一种常用的负样本构建方法是根据物品的热门程度采样,你能想到还有哪些负样本构建方法吗?
附: 最后再唠叨两句,本系列教程全部免费,但希望大家每期都不要落下,这样可成体系,也希望各位粉丝朋友多多转发,并在看完后点个“在看”,以示鼓励。 我会在文章“在看”数超过20后推送出下一篇的教程。 希望大家都有所收获。
「 更多干货,更多收获 」

640?wx_fmt=gif

推荐系统教程之六:从文本到用户画像有多远