推荐系统(Recommender systems)
这里讨论推荐系统有两个深层原因或者说动机,第一个原因在于它是机器学习的一个重要应用,在过去的几年中在参观硅谷的各种科技类公司中,经常在那些公司里与开发机器学习应用的人交流,谈及什么才是机器学习最重要的应用,或者什么样的机器学习的应用是最想让它的表现得到改进的,最常听到的回答其中之一就是现在硅谷有好多个团队正试图建立更好的推荐系统。比如亚马逊、Netflix、eBay或者苹果公司的iTunes Genius做的事情,有很多网站或者很多系统试图向用户推荐新产品,比如说亚马逊会推荐新书,Netflix推荐新电影等等。这些推荐系统可能会看看你以前购买过什么书或者以前你给哪些电影进行过评分,这些系统贡献了现今亚马逊收入的相当大一部分,而对像Netflix这样的公司,他们向用户推荐的电影占了用户观看的电影的相当大一部分,于是一个推荐系统其表现的一些改进就能带来显著且即刻产生的影响这种影响关系到许多公司的最终业绩。
第二个动机是我们已经在前面章节中看到对于机器学习来说特征量(features)的重要性,所选择的特征对于学习算法的表现有很大影响,在机器学习领域有这么一个宏大的想法就是对于一些问题而言存在一些算法能自动地替代人类学习到一组优良的特征量,即有些情形下能够采用一种算法来学习到使用什么特征量,而推荐系统就是这种情形的一个例子,还有其他很多例子但通过学习推荐系统我们将能够对这种学习特征量的想法有一点理解。
1.预测电影评分
假想有一个影评网站,比方说允许评分在0颗星至5颗星之间,这里有5部电影《爱到最后》、《浪漫永远》、《小爱犬》、《无尽狂飙》还有《剑与空手道》,我们有4位用户名叫Alice,Bob,Carol和Dave。或称他们用户1、2、3和4,比方说Alice她非常喜欢《爱到最后》把它评为5颗星,她还喜欢《浪漫永远》也把它评为5颗星,她没看过《小爱犬》也就没评分这样我们没有这个评分数据,Alice很不喜欢《无尽狂飙》或是《剑与空手道》把它们评为0颗星。其他3位用户的的评价如下:
(n_u):表示用户的数目 |
---|
(n_m):表示电影的数目 |
(r(i,j)=1):表示用户j对电影i做了评分(否则即为做评分时为零) |
(y^{(i,j)}):用户j对电影i的评分(前提做了评分) |
首先介绍第一种构造推荐系统的方法,这种方法叫做“基于内容的推荐”(content-based recommendations)。应该如何预测这些缺少的问号评分值呢?假设对这些电影的每一部,都用一些特征来描述,具体来讲,假设每部电影有两种特征分别用x1和x2代表,x1表示这部电影属于爱情电影的程度,x2表示这部电影是动作电影的程度,因此对于电影《爱到最后》那么这部电影是爱情电影的比率为0.9,但是它属于动作电影的比率为0,《浪漫永远》爱情比率1.0,动作比率0.01,等等如下所示:
我们可以把对每个观众打分的预测当成一个独立的线性回归问题,具体来说,比如每一个用户j,我们都学习出一个参数( heta^{(j)})(这里是一个三维向量),然后要根据参数向量θ与特征(x^{(i)})的内积来预测用户j对电影i的评分即(( heta^{(j)})^Tx^{(i)})。
看一个具体的例子,对于用户1即Alice对应的参数向量就应该是( heta^{(1)}),现在假如我们想预测Alice对电影《小爱犬》是如何评价的那么这部电影,有一个参数向量(x^{(3)})等于[1,0.99,0],其中1是截距项,另外两个是特征0.99和0。假如说对于这个例子,已经知道Alice的参数向量( heta^{(1)})(后面会详细讲到如何获得这个参数)等于[0,5,0]。因此这两个向量的内积就等于5×0.99结果为4.95,故我们预测Alice得评分结果将为4.95。
现在可以把这个问题写成如下更正式一些,用r(i,j)=1来表示用户j对电影i进行了评分,y(i,j)则表示用户j对电影i的评分值,( heta^{(j)})表示用户j对应的参数向量,(x^{(i)})是某部电影i的特征向量,那么某个用户对某部电影的评分就是(( heta^{(j)})^Tx^{(i)})。
现在临时介绍一个额外的表示符号(m^{(j)}),用来表示用户j评价过的电影数量,预测评分是一个基本的线性回归问题,因此我们要做的就是选择一个参数向量( heta^{(j)}),使得预测的结果尽可能接近我们在训练集中的观测值。因此我们要关于( heta^{(j)})最小化下面这个求和值:(min_{ heta^{(j)}}left[frac{1}{2m^{(j)}}Sigma_{i:r(i,j)=1}(( heta^{(j)})^T(x^{(i)})-y^{(i,j)})^2+frac{lambda}{2m^{(j)}}Sigma_{k=1}^n( heta_k^{(j)})^2 ight])
对于推荐系统,这里把符号稍微简化一下,去掉常数项(m^(j)),去掉这一项不改变θ(j)的最优化结果,(min_{ heta^{(j)}}left[frac{1}{2}Sigma_{i:r(i,j)=1}(( heta^{(j)})^T(x^{(i)})-y^{(i,j)})^2+frac{lambda}{2}Sigma_{k=1}^n( heta_k^{(j)})^2 ight]) 。
在构建推荐系统的时候我们也不想只对某一个用户学习出参数向量,我们想对所有的用户都学习出θ,因为有(n_u)个用户,所以希望学习出所有的参数,那么要做的是将上面这个最优化目标再加上一个求和:(min_{ heta^{(j)}}left[frac{1}{2}Sigma_{j=1}^{n_u}Sigma_{i:r(i,j)=1}(( heta^{(j)})^T(x^{(i)})-y^{(i,j)})^2+frac{lambda}{2}Sigma_{j=1}^{n_u}Sigma_{k=1}^n( heta_k^{(j)})^2 ight]) ,这里把想要最优化的项称为:(J( heta^{(1)},..., heta^{(n_u)})),同样地J还是我们要最小化的最优化目标函数。
如果想要用梯度下降来求出这个J的最小值,可能会用到这些式子:
2.协同过滤(collaborative filtering)
协同滤波算法能够自行学习所要使用的特征,在上一小节中的基于内容的推荐中,每一部电影都有明确的浪漫指数是多少,动作指数是多少,但细想可以知道这样做难度很大,花费时间很多,要让每个人看完每一部电影后说出每一部电影有多浪漫多动作是一件不容易的事情,而且通常除这两个特征之外的其他指数有很多。
所以让我们转移一下问题,假如我们有一个数据集,但并不知道特征的值是多少,当我们得到一些不同用户对电影的评分,我们并不知道每部电影到底有多少浪漫的成分,或动作成分,于是把所有的特征都打上问号,如下图所示:
更一般的,假设用户告诉了我们的偏好,即已经给我们提供了( heta^{(1)})到( heta^{(n_u)})的值,而我们想知道电影i的特征向量(x^{(i)}),我们可以列出以下的最优化的问题:
我们要做的是学习出所有电影的所有特征,所以现在要做的是在此加上另外的一个求和,即对所有的电影n_m求和,然后最小化整个这个目标函数,针对所有的电影这样就会得到如下的最优化的问题:
上一小节“基于内容的推荐”算法讲的是,若有对一系列电影的评分,那么根据不同电影的特征(x^{(1)},..., x^{(n_m)}),我们可以得到参数θ。本节中我们主要讲的是如果用户愿意提供参数θ,那么可以为不同的电影估计特征。
有点像先有鸡还是先有蛋的问题,如果我们能知道θ就能学习到x,相对的如果我们知道x也会学出θ,所以实际上应该随机猜θ的值,接着基于一开始随机猜测出的θ的值继而学习出不同电影的特征,给出已有的一些电影的原始特征,再运用上一个小节中讨论过的方法,得到对参数θ的更好估计,为用户提供更好的参数θ集,接着继续迭代,不停重复优化θ、x、θ、x、θ。算法将会收敛到一组合理的特征以及一组合理的用户参数的估计。
具体算法实现
如果给出电影的几个特征,我们可以使用这些资料去获得用户的参数数据,如果给出用户的参数数据,那么可以使用这些资料去获得电影的特征,我们将它们合并成协同过滤算法(Collaborative Filtering Algorithm)。即不停地重复下面的式子的计算,随机地初始化这些参数,然后解出θ,解出x,解出θ,解出x...实际上存在一个更有效率的算法,不再需要像这样不停地计算x和θ,而是能够将x和θ同时计算出来。
此外,当我们以这样的方法学习特征量时,之前的前提是我们所使用的特征x0(即截距量)等于1,当我们以这种形式去学习特征量时,我们必须要去掉这个截距量,我们这里移除的理由是:我们没有必要去将这个等于一的特征值固定,因为如果算法想要的话,可以将特征值x1设为1,所以没有必要去将1这个特征定死,这样算法有了灵活性去自行学习。
总结一下,首先我们会把(x^{(1)},...,x^{(n_m)})和( heta^{(1)},..., heta^{(n_u)})初始为小的随机值,接下来我们要用梯度下降或者某些其他的高级优化算法把这个代价函数(J(x^{(1)},...,x^{(n_m)}, heta^{(1)},..., heta^{(n_u)}))最小化,梯度下降法写出来的更新公式如下:
3.协同过滤算法的向量化实现
我们有五部电影的数据集(如下图),首先将要做的是将这些用户的电影评分进行分组并存到一个矩阵中,我们共有五部电影以及四位用户,那么这个矩阵Y就是一个5行4列的矩阵,它将这些电影的用户评分数据都存在矩阵里,包括问号标注出的。
本节最后谈一谈均值归一化(Mean Normalization),它可以让协同滤波算法算法运行得更好。
首先我们了解均值归一化这个想法的动机,还是之前的电影评分的例子,考虑一下有一个用户没有给任何电影评分,即第五个用户Eve她没有给任何电影评分,我们来看看协同过滤算法会对这个用户做什么。
假如n等于2,即我们要学习两个特征变量还要学习出一个参数向量( heta^{(5)}),任然提醒一下,这个向量是n维的而不是n+1维的,根据优化函数,用户Eve没给任何电影打过分,所以对用户Eve来说没有电影满足r(i,j)=1这个条件,所以优化函数中的第一项(与实际评分差)完全不影响( heta^{(5)})的值,所以影响( heta^{(5)})值的唯一一项是最后的正则化项,优化的目的是尽可能地让正则化项小,换句话说我们想要最小化(frac{lambda}{2}[( heta_1^{(5)})^2+( heta_2^{(5)})^2]),所以最终得到的就会是( heta^{(5)})=[0;0]。
因为正则化项会让你的参数接近0,如果没有数据能够使得参数远离0,那么就会得到( heta^{(5)})等于零向量。所以当我们要预测用户5给电影打分结果,有(( heta^{(5)})^Tx^{(i)}),易见对任意i结果都会等于0,因此我们根据结果会预测Eve给所有电影的评分都是零星,这个结果我们还是没有任何好方法来把电影推荐给她,因为预测结果是所有这些电影都会被Eve给出一样的评分,没有一部电影拥有高一点儿的预测评分让我们能推荐给她。
均值归一化的想法可以让我们解决这个问题,把所有这些评分全部整合到矩阵Y中,全部是问号的这列对应着Eve没有给任何电影评分。为了实现均值归一化,首先计算每个电影所得评分的均值,把它们存在一个向量µ中,接着重新定义Y矩阵,将Y的每一列减去向量µ重新更新每一列,这样把每个电影都归一化为平均评分为零,当然未打分的这些问号没变。
因为Eve从来没有给任何电影打分,所以学习到的用户5的参数仍然还是零向量,这样我们预测Eve的评分是(( heta^{(5)})^Tx^{(i)}+mu_i),所以如果学习的等于0的话,对电影i的评分我们最终会预测为µi,即对于电影1我们会预测Eve对它的评分是2.5,对于电影2我们会预测Eve给它2.5星,对于电影3我们会预测Eve给它2星等等。我们就对这个新的观影用户Eve一无所知,我们预测她对每个电影的评分就是这些电影所得的平均评分,就是这些电影所得的平均评分。