协同过滤 & 矩阵分解

939 阅读4分钟

简介

本章主要介绍深度学习出现之前的几种经典算法,包括协同、矩阵分解和Logistic Regression等。这些类别的算法,在深度学习出来之前有着广泛的应用。而且,对于一般数据集的情况,这类方式也是简单易用,可解释性强。

协同过滤 Collaborative Filter

协同过滤算法是最早提出的推荐的算法类型,其核心的思想是,对于需要预测的向量xx的数据,我们选取和xx相似的top K个向量,来预测xx的值。

举个例子,我们有一个矩阵,行表示用户user,列表示商品item,元素的取值是0、1 or -1,-1表示不喜欢,0表表示没投票,1表示喜欢。之后,给出一个用户u,预测u对商品i是否是喜欢。

比如,下面的矩阵给出了一个简单的情况:

user \ itemitem0item1item2item3
user00110
user1101-1
user20-101

实际应用中,user和item的维度会很高。

假设我们现在有一个新的用户u,总共有nn种商品,mm个已经知道对商品评价的用户,而且也知道用户u对部分商品的评价,现在我们想知道u对于商品k的评价。

首先,我们需要定义函数sim(i,j)sim(\mathbf{i},\mathbf{j}),表示向量i\mathbf{i}j\mathbf{j}的相似度。数学上,有很多方式,给出几个例子:

余弦相似度:

sim(i,j)=cos(i,j)=ijijsim(\mathbf{i},\mathbf{j}) = cos(\mathbf{i}, \mathbf{j}) = \frac{\mathbf{i} \cdot \mathbf{j}}{\left\|\mathbf{i}\right\| \left\|\mathbf{j}\right\|}

欧几里得距离:

sim(x,y)i=1N(xiyi)2sim(\mathbf{x},\mathbf{y}) \sqrt{\sum_{i=1}^{N}(x_{i}-y_{i})^2}

还有其他的方式,比如皮尔逊相关系数等,这里不再一一列举。

算法步骤:

  1. 计算用户u和用户矩阵中,所有用户U的相似度
  2. 计算出u和所有用户的相似程度,并选择出top K个最相似的
  3. 根据加权公式,计算出Ru,pR_{u,p}Ru,pR_{u,p}表示用户u对于商品p的喜好程度

喜好程度的计算方法为

Ru,p=sS(sim(u,s)Rs,p)sSsim(u,s)R_{u,p} = \frac{\sum_{s \in S} (sim({\mathbf{u},\mathbf{s}})\cdot R_{\mathbf{s},\mathbf{p}})} {\sum_{s \in S}sim({\mathbf{u},\mathbf{s}})}

其中,SS 表示top K个和u相似的的用户集,Rs,pR_{\mathbf{s},\mathbf{p}}表示用户s对于商品p的喜好程度。

上面提到的CF算法,是基于用户的,因此又称为UserCF

如果用户数量远远大于商品数量的话,我们可以用商品向量来计算,思路是一样的,不过此时的计算方式是基于商品item的,即商品对于用户的映射关系。这种方式我们成为ItemCF

UserCF更适合于新闻推荐、发现热点和热点追踪等,因为该方法基于用户之间的相似度来计算的。

ItemCF更适合于稳定商品的情况,比如电视剧推荐等。

CF算法的不足之处在于,我们计算的时候,只考虑了单一维度的情况,比如好友的喜好,无法执行根据其他特征来计算推荐

矩阵分解

矩阵分解算法,就是为了解决CF中提到的特征不足的问题。矩阵分解的核心思想如下,为每个用户和商品创建一个kk维的隐向量;那么用户u对于商品p的喜好程度就是

ru,p=kukpr_{u, p} = \mathbf{k}_{u} \cdot \mathbf{k}_{p}

其中,ku\mathbf{k}_{u}表示用户u的隐向量,kp\mathbf{k}_{p}表示商品p的隐向量。

原来的User-Item矩阵:

R=UV\mathbf{R}=\mathbf{U}\mathbf{V}

其中,RRm×n\mathbf{R} \in \mathbb{R}^{m \times n}表示原来的User_Item矩阵, URm×k\mathbf{U} \in \mathbb{R}^{m \times k}表示用户矩阵,VRk×n\mathbf{V} \in \mathbb{R}^{k \times n}表示商品矩阵。

那么,R\mathbf{R}中的元素rui\mathbf{r}_{ui}表示用户u对于商品i的评分,计算方式为:

r^ui=xuTyi\hat{r}_{ui}=\mathbf{x}^\mathrm{T}_{u}\mathbf{y}_{i}

xuT\mathbf{x}^\mathrm{T}_{u}表示用户u的行向量,yi\mathbf{y}_{i}表示商品i的列向量。

在矩阵维度很高的情况下,不适合使用奇异值分解,这里我们统一使用梯度下降的方式来求解。

明确我们的求解目标,R\mathbf{R}中不为零的数据是已知的用户数据,用户矩阵U\mathbf{U}和商品矩阵V\mathbf{V}是需要学习的出来的数据。定义我们的求解目标:

minX,Yrui0(ruir^ui)2=minX,Yrui0(ruixuTyi)2\min_{X,Y}\sum_{r_{ui}\neq0}{(r_{ui}-\hat{r}_{ui})^2}=\min_{X,Y}\sum_{r_{ui}\neq0}{(r_{ui}-\mathbf{x}^\mathrm{T}_{u}\mathbf{y}_{i})^2}

为了防止求解过程中出现过拟合,我们需要加入正则化选项,因此最终的求解目标是:

minX,Y{rui0(ruixuTyi)2+λ(uxu22+iyi22)}\min_{X,Y}\left\{\sum_{r_{ui}\neq0}{(r_{ui}-\mathbf{x}^\mathrm{T}_{u}\mathbf{y}_{i})^2} +\lambda (\sum_{u}\parallel \mathbf{x}_u \parallel_2^2 +\sum_{i}\parallel \mathbf{y}_i \parallel_2^2)\right\}

其中,x\mathbf{x}y\mathbf{y}互相独立。

这里需要单独说明一个地方,我们所有的向量都是列向量,上文提到的xu\mathbf{x}_u表示用户对于商品评价的列向量。我们使用xuT\mathbf{x}^\mathrm{T}_{u}表示行向量,首先是从形态理解上,用户矩阵中,对于商品的评价,是按照行存储的;其次是为了向量内积,行向量于列向量的内积是个具体的数字。根据matrixcookbook手册 ,向量内积的求导的方式为:

xTax=aTxx=a\frac{\partial \mathbf{x}^\mathrm{T}\mathbf{a}}{\mathbf{x}} = \frac{\partial \mathbf{a}^\mathrm{T} \mathbf{x}}{\mathbf{x}} = \mathbf{a}

根据梯度下降的迭代法则,我们需要分别对对xu\mathbf{x}_uyi\mathbf{y}_i求偏导,并迭代跟新。xu\mathbf{x}_u的梯度函数为

xu=2(ruixuTyi)yi+2λxu\nabla_{\mathbf{x}_u} = -2(r_{ui}-\mathbf{x}^\mathrm{T}_{u}\mathbf{y}_{i})\mathbf{y}_{i}+2\lambda \mathbf{x}_u

更新应该是朝着梯度的反方向,因此xu\mathbf{x}_{u}的梯度更新方式为

xu=xuγ((ruixuTyi)yiλxu)\mathbf{x}_u = \mathbf{x}_u - \gamma\left((r_{ui}-\mathbf{x}^\mathrm{T}_{u}\mathbf{y}_{i})\mathbf{y}_{i} -\lambda \mathbf{x}_{u}\right)

γ\gamma是学习速率。这里是对向量求导。

x\mathbf{x}y\mathbf{y}是对称的,因此y\mathbf{y}的更新方式为:

yi=yiγ((ruixuTyi)xuλyi)\mathbf{y}_i = \mathbf{y}_i-\gamma((r_{ui}-\mathbf{x}^\mathrm{T}_{u}\mathbf{y}_{i})\mathbf{x}_{u}-\lambda \mathbf{y}_i)

如果新增用户了,我们一般不会重新训练整个数据集,这样复杂度太高了。正确方式应该是,利用用户的历史行为数据和已经训练好的物品矩阵,单独训练xu\mathbf{x}_u即可,此时不需要更新物品矩阵,只需要更新新增的用户的参数。

矩阵分解还有隐式分解的形式,基本流程一样,这里不再赘述。