作者:章华燕
编辑:田 旭
前言
在第一篇文章《推荐算法综述》中我们说到,真正的推荐系统往往是多个推荐算法策略的组合使用,本文介绍的将会是推荐系统最古老的算法:基于内容的推荐算法(Content-Based Recommendations CB)。
CB是最早被使用的推荐算法,它的思想非常简单:根据用户过去喜欢的物品(本文统称为 item),为用户推荐和他过去喜欢的物品相似的物品。而关键就在于这里的物品相似性的度量,这才是算法运用过程中的核心。 CB最早主要是应用在信息检索系统当中,所以很多信息检索及信息过滤里的方法都能用于CB中。
举个简单的例子:在京东上购物的小伙伴们应该都知道,每当你进入任何一个物品页面的时候都会有一个“猜你喜欢”的栏目,这时候他就会根据你经常购买的物品给你推荐相似的物品。例如对我来说:我经常购买互联网类书籍,所以它就会给我推荐类似的书籍(当然这里只是举个例子,京东的推荐算法肯定不可能那么单一,但是可以肯定的是他肯定会用到最基础的CB推荐算法)。
CB的过程一般包括以下三步:
举个例子说明前面的三个步骤。随着今日头条的崛起,基于内容的文本推荐就盛行起来。在这种应用中一个item就是一篇文章。
接下来我们详细介绍下上面的三个步骤:
物品表示
真实应用中的item往往都会有一些可以描述它的属性。这些属性通常可以分为两种:结构化的(structured)属性与非结构化的(unstructured)属性。所谓结构化的属性就是这个属性的意义比较明确,其取值限定在某个范围;而非结构化的属性往往其意义不太明确,取值也没什么限制,不好直接使用。比如在交友网站上,item就是人,一个item会有结构化属性如身高、学历、籍贯等,也会有非结构化属性(如item自己写的交友宣言,博客内容等等)。对于结构化数据,我们自然可以拿来就用;但对于非结构化数据(如文章),我们往往要先把它转化为结构化数据后才能在模型里加以使用。真实场景中碰到最多的非结构化数据可能就是文章了(如个性化阅读中)。下面我们就详细介绍下如何把非结构化的一篇文章结构化。
如何代表一篇文章在信息检索中已经被研究了很多年了,下面介绍的表示技术其来源也是信息检索,其名称为向量空间模型(Vector Space Model,简称VSM)。
记我们要表示的所有文章集合为 D={d1,d2,...,dn},而所有文章中出现的词(对于中文文章,首先得对所有文章进行分词)的集合(也称为词典)为T={t1,t2,...,tn}。也就是说,我们有N篇要处理的文章,而这些文章里包含了n个不同的词。我们最终要使用一个向量来表示一篇文章,比如第j篇文章被表示为dj={w1j,w2j,...,wnj},其中wij表示第i个词ti在文章j中的权重,值越大表示越重要。dj中其他向量的解释类似。所以,为了表示第j篇文章,现在关键的就是如何计算dj各分量的值了。例如,我们可以选取wij为1,如果词ti出现在第 j 篇文章中;选取为0,如果ti未出现在第j篇文章中。我们也可以选取wij为词ti出现在第 j 篇文章中的次数(frequency)。但是用的最多的计算方法还是信息检索中常用的词频-逆文档频率(term frequency–inverse document frequency,简称tf-idf)。第j篇文章中与词典里第k个词对应的tf-idf为:
其中,TF(tk,dj) 是第k个词在文章j中出现的次数,而nk 是所有文章中包含第k个词的文章数量。
最终第k个词在文章j中对应的权重由如下公式获得:
做归一化的好处是不同文章之间的表示向量被归一到一个量级上,便于下面步骤的操作。
特征学习
假设用户u已经对一些item给出了他的喜好判断,喜欢其中的一部分item,不喜欢其中的另一部分。那么,这一步要做的就是通过用户u过去的这些喜好判断,为他产生一个模型。有了这个模型,我们就可以根据此模型来判断用户u是否会喜欢一个新的item。所以,我们要解决的是一个典型的有监督分类问题,理论上机器学习里的分类算法都可以照搬进这里。
下面我们简单介绍下CB里常用的一些学习算法:
1
最近邻方法(简称KNN)
对于一个新的item,最近邻方法首先找用户u已经评判过并与此新item最相似的k个item,然后依据用户u对这k个item的喜好程度来判断其对此新item的喜好程度。这种做法和CF中的item-based kNN很相似,差别在于这里的item相似度是根据item的属性向量计算得到,而CF中是根据所有用户对item的评分计算得到。
对于这个方法,比较关键的可能就是如何通过item的属性向量计算item之间的两两相似度。对于结构化数据,相似度计算使用欧几里得距离;而如果使用向量空间模型(VSM)来表示item的话,则相似度计算可以使用cosine。
2
Rocchio算法
Rocchio算法是信息检索中处理相关反馈(Relevance Feedback)的一个著名算法。比如你在搜索引擎里搜“苹果”,当你最开始搜这个词时,搜索引擎不知道你到底是要能吃的水果,还是要不能吃的苹果,所以它往往会尽量呈现给你各种结果。当你看到这些结果后,你会点一些你觉得相关的结果(这就是所谓的相关反馈了)。然后如果你翻页查看第二页的结果时,搜索引擎可以通过你刚才给的相关反馈,修改你的查询向量取值,重新计算网页得分,把跟你刚才点击的结果相似的结果排前面。比如你最开始搜索“苹果”时,对应的查询向量是{“苹果” : 1}。而当你点击了一些与Mac、iPhone相关的结果后,搜索引擎会把你的查询向量修改为{“苹果” : 1, “Mac” : 0.8, “iPhone” : 0.7},通过这个新的查询向量,搜索引擎就能比较明确地知道你要找的是不能吃的苹果了。Rocchio算法的作用就是用来修改你的查询向量的:{“苹果” : 1} –> {“苹果” : 1, “Mac” : 0.8, “iPhone” : 0.7}。
在CB里,我们可以类似地使用Rocchio算法来获得用户u的profile
其中 wj表示item j的属性,Ir与 Inr 分别表示已知的用户u喜欢与不喜欢的item集合;而 β 与 γ 为正负反馈的权重,它们的值由系统给定。
在获得
后,对于某个给定的item j,我们可以使用
和
的相似度来代表用户u对j的喜好度。
Rocchio算法的一个好处是
可以根据用户的反馈实时更新,其更新代价很小。
正如在本节开头所说,本节要解决的是一个典型的有监督分类问题。所以各种有效的分类机器学习算法都可以用到这里,下面列举几个常用的分类算法:
决策树算法(Decision Tree, 简称DT)
当item的属性较少而且是结构化属性时,决策树一般会是个好的选择。这种情况下决策树可以产生简单直观、容易让人理解的结果。而且我们可以把决策树的决策过程展示给用户u,告诉他为什么这些item会被推荐。但是如果item的属性较多,且都来源于非结构化数据(如item是文章),那么决策树的效果可能并不会很好。
线性分类算法(Linear Classifer, 简称LC)
对于我们这里的二类问题,线性分类器(LC)尝试在高维空间找一个平面,使得这个平面尽量分开两类点。也就是说,一类点尽可能在平面的某一边,而另一类点尽可能在平面的另一边。
仍以学习用户u的分类模型为例。
表示item j的属性向量,那么LC尝试在
空间中找平面
,使得此平面尽量分开用户u喜欢与不喜欢的item。其中的
就是我们要学习的参数了。最常用的学习
的方法就是梯度下降法了,其更新过程如下:
其中的上角标t表示第t次迭代,yuj表示用户u对item j的打分(例如喜欢则值为1,不喜欢则值为-1)。η 为学习率,它控制每步迭代变化多大,由系统给定。
和Rocchio算法一样,上面更新公式的好处就是它可以以很小的代价进行实时更新,实时调整用户u对应的 cu。
说到这里,很多童鞋可能会想起一些著名的线性分类器:Logistic Regression和Linear SVM等等,它们当然能胜任我们这里的分类任务。Linear SVM用在文本分类上能获得相当不错的效果。
如果item属性
的每个分量都是0/1取值的话(如item为文章,
的第k个分量为1表示词典中第k个词在item j中,为0表示第k个词不在item j中)。
素贝叶斯算法(Naive Bayes, 简称NB)
NB算法就像它的简称一样,牛逼!NB经常被用来做文本分类,它假设在给定一篇文章的类别后,其中各个词出现的概率相互独立。它的假设虽然很不靠谱,但是它的结果往往惊人地好。再加上NB的代码实现比较简单,所以它往往是很多分类问题里最先被尝试的算法。我们现在的profile learning问题中包括两个类别:用户u喜欢的item,以及他不喜欢的item。在给定一个item的类别后,其各个属性的取值概率互相独立。我们可以利用用户u的历史喜好数据训练NB,之后再用训练好的NB对给定的item做分类。
推荐列表的生成
如果上一步Profile Learning中使用的是分类模型(如DT、LC和NB),那么我们只要把模型预测的用户最可能感兴趣的n个item作为推荐返回给用户即可。而如果Profile Learning中使用的直接学习用户属性的方法(如Rocchio算法),那么我们只要把与用户属性最相关的n个item作为推荐返回给用户即可。其中的用户属性与item属性的相关性可以使用如cosine等相似度度量获得。
基于内容推荐的优缺点
下面说说基于内容推荐算法的优缺点。
CB的优点:
CB的缺点:
CB应该算是第一代的个性化应用中最流行的推荐算法了。但由于它本身具有某些很难解决的缺点(如上面介绍的第1点),再加上在大多数情况下其精度都不是最好的,目前大部分的推荐系统都是以其他算法为主(如CF),而辅以CB以解决主算法在某些情况下的不精确性(如解决新item问题)。但CB的作用是不可否认的,只要具体应用中有可用的属性,那么基本都能在系统里看到CB的影子。组合CB和其他推荐算法的方法很多(我很久以后会写一篇博文详细介绍之),最常用的可能是用CB来过滤其他算法的候选集,把一些不太合适的候选(比如不要给小孩推荐偏成人的书籍)去掉。