基于内容的推荐系统 (CBRS)
首先介绍一下最简单的一个推荐算法模型CBRS。在这个模型中我们用线性回归的基本思路拟合出每个用户对每个电影的评分向量,预测出用户没有评分的电影并进行推荐。
假设我们有4个Users和5个Movies,有一些用户给电影打了分,有一些没有打。电影的评分是从0-5,没有评分的项在表格中将用?来表示。我们设定每个电影有 X1 X_1和 X2 X_2两个属性,分别是浪漫和动作,并手动给每个电影进行 X1 X_1和 X2 X_2的设置。
MoviesUser | User 1 | User 2 | User 3 | User 4 | X1 X_1(romance) | X2 X_2(action) |
---|---|---|---|---|---|---|
Movie 1 | 5 | 5 | 0 | 0 | 0.9 | 0 |
Movie 2 | 5 | ? | ? | 0 | 1 | 0.01 |
Movie 3 | ? | 4 | 0 | ? | 0.99 | 0 |
Movie 3 | 0 | 0 | 5 | 4 | 0.1 | 1 |
Movie 3 | 0 | 0 | 5 | ? | 0 | 0.9 |
接下来引入一些标记: nu n_u 表示User的数量 nm n_m 表示Movie的数量 r(i,j) 表示User i 是否给 Movie j 评过分,有则=1,无则=0 y(i,j) y^{(i,j)} 表示User i 给 Movie j 的评分 mj m_j 表示User j 评过分的电影总数
我们对以上特征来构建一个线性回归模型的算法,针对每个用户j都有一个 θj θ^j参数向量(可以理解为对电影的各个属性喜爱程度),并用 xi x^i表示电影i的特征向量。对于用户j对电影i的评分,预测值为 (θj)Txi (θ^j)^Tx^i。
根据以上我们可以构建出代价函数Cost function J =
∑i:r(i,j)=1((θj)Txi−y(i,j))2
sum_{i:r(i,j)=1} ((θ^j)^Tx^i-y^{(i,j)})^2
根据线性回归模型,我们需要最小化代价函数,并加上正则化项:
min12∑i:r(i,j)=1((θj)Txi−y(i,j))2 λ2∑k=1n(θjk)2
minfrac{1}{2} sum_{i:r(i,j)=1} ((θ^j)^Tx^i-y^{(i,j)})^2 frac{λ}{2}sum_{k=1}^n(θ^j_k)^2
其中 i:r(i,j)表示我们只计算那些用户 j 评过分的电影。在一般的线性回归模型中,误差项和正则项应该都是乘以 1/2m,在这里我们将 m 去掉。并且我们不对方差项 θ0θ_0 进行正则化处理。
上面的操作都是对单个用户线性回归模型,对于所有用户,我们有:
minθ1..θnu12∑j=1nu∑i:r(i,j)=1((θj)Txi−y(i,j))2 λ2∑j=1nu∑k=1n(θjk)2
min_{θ_1..θ_{n^u}}frac{1}{2} sum_{j=1}^{n^u}sum_{i:r(i,j)=1} ((θ^j)^Tx^i-y^{(i,j)})^2 frac{λ}{2}sum_{j=1}^{n^u}sum_{k=1}^n(θ^j_k)^2
如果使用梯度下降来求解,根据Cost function代价函数求偏导可以得出θ的更新公式,其中a表示学习速率:
θjk:=θjk−a∑i:r(i,j)=1((θj)Txi−y(i,j))xik(k=0)
θ^j_k:=θ^j_k - asum_{i:r(i,j)=1} ((θ^j)^Tx^i-y^{(i,j)})x_k^i (k=0)
θjk:=θjk−a(∑i:r(i,j)=1((θj)Txi−y(i,j))xik λθjk)(k!=0)
θ^j_k:=θ^j_k - a(sum_{i:r(i,j)=1} ((θ^j)^Tx^i-y^{(i,j)})x_k^i λ θ^j_k) (k!=0)
当然此模型可能过于简单,除了每个电影的属性很少,而且人们喜爱一个电影的因素也不只是因为电影的分类,电影的演员,导演,甚至是上映时间都可以成为观众喜爱一部电影的重要因素。
Collaborative Filtering 协同过滤算法
在之前基于内容的推荐系统中,我们必须要有电影的特征向量才能求出每个用户的参数向量,但是这样会带来很大的麻烦,原因是每个人对电影的分类概念都不同,而且需要手动定义每个电影的特征向量将会降低许多效率。 不如我们换个思路,可否通过用户参数向量来拟合出电影的特征向量呢?当然是可以的:
minx1..xnm12∑i=1nu∑j:r(i,j)=1((θj)Txi−y(i,j))2 λ2∑i=1nm∑k=1n(xik)2
min_{x_1..x_{n^m}}frac{1}{2} sum_{i=1}^{n^u}sum_{j:r(i,j)=1} ((θ^j)^Tx^i-y^{(i,j)})^2 frac{λ}{2}sum_{i=1}^{n^m}sum_{k=1}^n(x^i_k)^2
和之前作对比两个最小化代价函数都如此相近,那么将用户参数与电影的向量都作为代价函数J的输入值,再分别对x于θ分别求偏导就可以解决实际应用中用户评分与电影特征向量都不全的问题了。
CostFunctionJ(x,θ)=12∑(i,j):r(i,j)=1((θj)Txi−y(i,j))2 λ2∑i=1nm∑k=1n(xik)2 λ2∑j=1nu∑k=1n(θjk)2
Cost Function J(x,θ) = frac{1}{2}sum_{(i,j):r(i,j)=1} ((θ^j)^Tx^i-y^{(i,j)})^2 frac{λ}{2}sum_{i=1}^{n^m}sum_{k=1}^n(x^i_k)^2 frac{λ}{2}sum_{j=1}^{n^u}sum_{k=1}^n(θ^j_k)^2
再对x与θ分别求偏导,并用梯度下降求局部最小值
θjk:=θjk−a(∑i:r(i,j)=1((θj)Txi−y(i,j))xik λθjk)
θ^j_k:=θ^j_k - a(sum_{i:r(i,j)=1} ((θ^j)^Tx^i-y^{(i,j)})x_k^i λ θ^j_k)
xik:=xik−a(∑j:r(i,j)=1((θj)Txi−y(i,j))θjk λxik)
x^i_k:=x^i_k - a(sum_{j:r(i,j)=1} ((θ^j)^Tx^i-y^{(i,j)})θ_k^j λ x^i_k)
注:在协同过滤从算法中,我们通常不使用方差项,如果需要的话,算法会自动学得。
协同过滤算法使用步骤如下:
- 初始 x(1),x(2),…,x( nm n_m),θ(1),θ(2),…,θ( nu n_u)为一些随机小值
- 使用梯度下降算法最小化代价函数
- 在训练完算法后,我们预测 (θ(j))Tx(i) (θ(j))^Tx(i)为用户 j 给电影 i 的评分从而进行推荐
- 均值化处理(Mean Normalization) 现在假设新来了一名用户,她从来没有看过任何电影,我们该如何给该用户推荐电影呢?首先我们可以先得出矩阵Y表示用户与评分:最有一列为该用户的评分 Y=⎡⎣⎢⎢⎢⎢⎢⎢55?005?4000?05500?40?????⎤⎦⎥⎥⎥⎥⎥⎥ Y=begin{bmatrix} 5 & 5 & 0 & 0 & ?\ 5 & ? & ? & 0 & ?\ ? & 4 & 0 & ? & ?\ 0 & 0 & 5 & 4 & ?\ 0 & 0 & 5 & 0 & ? end{bmatrix} 我们首先需要对结果 Y 矩阵进行均值归一化处理,将每一个用户对某一部电影的评分减去所有用户对该电影评分的平均值: Y=⎡⎣⎢⎢⎢⎢⎢⎢2.52.5?−2.25−1.252.5?2−2.25−1.25−2.5?−22.753.75−2.5−2.5?1.75−1.25?????⎤⎦⎥⎥⎥⎥⎥⎥ Y=begin{bmatrix} 2.5 & 2.5 & -2.5 & -2.5 & ?\ 2.5 & ? & ? & -2.5 & ?\ ? & 2 & -2 & ? & ?\ -2.25 & -2.25 & 2.75 & 1.75 & ?\ -1.25 & -1.25 & 3.75 & -1.25 & ? end{bmatrix} 然后我们利用这个新的 Y 矩阵来训练算法。如果我们要用新训练出的算法来预测评分,则需要将平均值重新加回去,预测 (θ(j))T(x(i)) μi (θ(j))^T(x(i)) μi 对于新用户,我们的新模型会认为她给每部电影的评分都是该电影的平均分。
Matlab代码参考
cofiCostFunc.m
代码语言:javascript复制function [J, grad] = cofiCostFunc(params, Y, R, num_users, num_movies, ...
num_features, lambda)
% Collaborative filtering cost function
% Unfold the U and W matrices from params
X = reshape(params(1:num_movies*num_features), num_movies, num_features);
Theta = reshape(params(num_movies*num_features 1:end), ...
num_users, num_features);
J = 0;
X_grad = zeros(size(X));
Theta_grad = zeros(size(Theta));
M=(X*Theta'-Y).^2;
J=1/2*sum(sum(R.*M));
J=J lambda/2*sum(sum(Theta.^2)) lambda/2*sum(sum(X.^2));
for k=1:num_features
for i=1:num_movies
for j=1:num_users
X_grad(i,k)=X_grad(i,k) (X(i,:)*Theta(j,:)'-Y(i,j))*Theta(j,k).*R(i,j);
end
end
end
X_grad=X_grad lambda*X;
for k=1:num_features
for j=1:num_users
for i=1:num_movies
Theta_grad(j,k)=Theta_grad(j,k) (X(i,:)*Theta(j,:)'-Y(i,j))*X(i,k).*R(i,j);
end
end
end
Theta_grad=Theta_grad lambda*Theta;
grad = [X_grad(:); Theta_grad(:)];
end