本文结构:
- 推荐系统
- 常用方法
- 简介
- 模型 cost, gradient 表达式
- 代码实现
- 应用实例
参考:
Coursera-Andrew Ng 的 Machine Learning
Sirajology 的 Recommendation Systems - Learn Python for Data Science
1. 推荐系统
根据用户的兴趣特点和购买行为,向用户推荐用户感兴趣的信息和商品。
为用户节省时间,还能挖掘可能用户自己都不知道的潜在兴趣点。
生活中的例子:喜马拉雅上根据我听过的书推荐相关的内容,效果不错,推荐的很多我都会订阅。Youtube上根据我看过的视频推荐内容,如果我在追剧,它会把最新的剧集放在我首页,还有我可能感兴趣的电影。
还有很多例子和方法,以及冷启动等关键问题,推荐大家看《推荐系统实战》这本书,之前去听新浪微博的分享,这本书是他们推荐系统部门的必备材料。
2. 常用模型
方法有基于内容,基于物品,基于用户和协同过滤等。
基于内容,物品,用户的推荐就是把相关的特征表达为向量形式后,计算它们之间的距离,根据相似度高的来为你推荐。
基于内容,例如,要判断文章间的相似性,就可以把每篇文章,按照它里面用到的单词,把一篇文章表达为向量的形式,然后计算文章之间的距离。
如果我们有一个用户对物品的打分矩阵,那么通过计算行向量间的距离,可以计算出物品之间的相似性,计算列向量的距离,可以得到用户间的相似性。
根据距离的定义公式,计算出向量间的距离,找到最相近的几个对象,再取平均值就可以作为预测值。
协同过滤
我们要解决一个推荐问题时,很自然的可以想到,用户为什么喜欢这些物品,应该是因为这些物品具有某些特点,而用户刚好对这些特点感兴趣。
但具体是哪些特点呢,每个特点又该用什么值来量化呢,这一步可能并不好做。协同过滤就可以提供一个解决方案,即使你并不知道这些特点都有什么,即使你并不知道各个特点都是多少值,你仍然可以得到预测结果。
它的模型表示为,x\_i 为电影 i 具有的特点向量,y\_j 为用户 j 对所有特点的偏好值向量。二者相乘就得到用户 j 对电影 i 的打分,然后可以用来和实际分数进行比较:
-
写出所有用户对所有电影的预测分数与实际分数的平方差公式,再加上 theta 和 x 的正则项,就得到了目标函数,我们要使这个函数达到最小值。
-
然后计算 cost function 对 theta 和 x 的偏导:
接着可以用梯度下降法迭代求 cost function 的最小值。
最后可以用计算得到的 theta 和 x 相乘,得到打分矩阵中未知的部分。
3. 代码实现
下面是用 matlab 实现的协同过滤。
其中的计算大多用矩阵表达,这样比写循环要快而且简洁,代码很简单,也可以很容易地用python写出来。
1. 引入数据
图中可见用户对电影的评分热点图。
% Load data
load ('ex8_movies.mat');
imagesc(Y);
ylabel('Movies');
xlabel('Users');
2. 写出 cost function 和 gradient 的表达式
其中 J 为 cost function,X\_grad,Theta\_grad 为相应的梯度。
先写了没有正则项的形式,在这基础上又加上了正则项,当然可以合二为一直接写带有正则项的。
需要注意的是,在数据矩阵中,我们只将有打分的地方拿来计算,
所以在代码中我们用 R 点乘误差矩阵,这个 R 的意思,R(i, j) = 1 时,说明用户 j 对电影 i 有评分,为 0 时就是没有打分。
function [J, grad] = cofiCostFunc(params, Y, R, num_users, num_movies, ...
num_features, lambda)
% 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);
% ========= without Regularization
M = X*Theta'-Y;
Z = R.*M;
J = 0.5 * sum( sum(Z.*Z) );
M_grad = X*Theta'-Y;
Z_grad = R.* M_grad;
X_grad = ( Z_grad )*Theta;
Theta_grad = ( Z_grad )'*X;
% ========= Regularization
Theta_regu = sum( sum(Theta.*Theta) )*lambda/2;
X_regu = sum( sum(X.*X) )*lambda/2;
J = J + Theta_regu + X_regu;
X_grad = X_grad + lambda*X;
Theta_grad = Theta_grad + lambda*Theta;
% Fold the U and W matrices to params
grad = [X_grad(:); Theta_grad(:)];
end
3.用随机梯度下降来训练模型
先随机初始化 X 和 Theta。
定义 'MaxIter' 迭代次数和 'lambda' 正则项之后,
调用 'fmincg' 训练模型,得到 cost function 达到最小时对应的 X 和 Theta。
% Set Initial Parameters (Theta, X)
X = randn(num_movies, num_features);
Theta = randn(num_users, num_features);
initial_parameters = [X(:); Theta(:)];
% Set options for fmincg
options = optimset('GradObj', 'on', 'MaxIter', 100);
% Set Regularization
lambda = 10;
theta = fmincg (@(t)(cofiCostFunc(t, Y, R, num_users, num_movies, ...
num_features, lambda)), ...
initial_parameters, options);
% Unfold the returned theta back into U and W
X = reshape(theta(1:num_movies*num_features), num_movies, num_features);
Theta = reshape(theta(num_movies*num_features+1:end), ...
num_users, num_features);
4.得到预测值
用 X * Theta' 得到预测值,其中第一列是目标用户的向量。
再根据打分进行排序,输出前10个推荐影片。
p = X * Theta';
my_predictions = p(:,1) + Ymean;
movieList = loadMovieList();
[r, ix] = sort(my_predictions, 'descend');
fprintf('\\nTop recommendations for you:\\n');
for i=1:10
j = ix(i);
fprintf('Predicting rating %.1f for movie %s\\n', my_predictions(j), ...
movieList{j});
end
4. 用 python 的 lightFM 库实现
完整代码见 Sirajology 的 Github
今天会用到三个库, Numpy, Scipy, LightFM。
先从 lightfm 这个库里引入需要的数据,还有 LightFM 用来构建模型。
import numpy as np
from lightfm.datasets import fetch_movielens
from lightfm import LightFM
接下来导入数据。
它有1k的用户对1700部电影的100k的评分数据。每个用户对二十个左右的电影进行评分,分数由1到5,以字典的形式存储。
#fetch data and format it
data = fetch_movielens(min_rating=4.0)
接下来建立模型
定义它的损失函数为 wrap,即给定一个用户来找到与他相似的用户,并且得到他们喜欢的电影的排序,用这个排序作为对新用户的预测。用 fit 函数来训练模型。
#create model
model = LightFM(loss='warp')
#train model
model.fit(data['train'], epochs=30, num_threads=2)
接下来就是建立推荐的函数 sample_recommendation
先从训练数据中得到我们有多少个用户和电影。
对每个用户,取到他们已经喜欢的电影的前三个,
再取 model.predict 得到的预测值中,排名前三的 top_items。
打印出来可以观察一下。
def sample_recommendation(model, data, user_ids):
#number of users and movies in training data
n_users, n_items = data['train'].shape
#generate recommendations for each user we input
for user_id in user_ids:
#movies they already like
known_positives = data['item_labels'][data['train'].tocsr()[user_id].indices]
#movies our model predicts they will like
scores = model.predict(user_id, np.arange(n_items))
#rank them in order of most liked to least
top_items = data['item_labels'][np.argsort(-scores)]
#print out the results
print("User %s" % user_id)
print(" Known positives:")
for x in known_positives[:3]:
print(" %s" % x)
print(" Recommended:")
for x in top_items[:3]:
print(" %s" % x)
在调用函数时只需要传入 model,data 和 user_ids 即可。
sample_recommendation(model, data, [3, 25, 450])
我是 不会停的蜗牛 Alice
85后全职主妇
喜欢人工智能,行动派
创造力,思考力,学习力提升修炼进行中
欢迎您的喜欢,关注和评论!