推荐系统


本文结构:

  1. 推荐系统
  2. 常用方法
    1. 简介
    2. 模型 cost, gradient 表达式
    3. 代码实现
  3. 应用实例

参考:
Coursera-Andrew Ng 的 Machine Learning
Sirajology 的 Recommendation Systems - Learn Python for Data Science


1. 推荐系统

根据用户的兴趣特点和购买行为,向用户推荐用户感兴趣的信息和商品。
为用户节省时间,还能挖掘可能用户自己都不知道的潜在兴趣点。

生活中的例子:喜马拉雅上根据我听过的书推荐相关的内容,效果不错,推荐的很多我都会订阅。Youtube上根据我看过的视频推荐内容,如果我在追剧,它会把最新的剧集放在我首页,还有我可能感兴趣的电影。

还有很多例子和方法,以及冷启动等关键问题,推荐大家看《推荐系统实战》这本书,之前去听新浪微博的分享,这本书是他们推荐系统部门的必备材料。


2. 常用模型

方法有基于内容,基于物品,基于用户和协同过滤等。

基于内容,物品,用户的推荐就是把相关的特征表达为向量形式后,计算它们之间的距离,根据相似度高的来为你推荐。

基于内容,例如,要判断文章间的相似性,就可以把每篇文章,按照它里面用到的单词,把一篇文章表达为向量的形式,然后计算文章之间的距离。

如果我们有一个用户对物品的打分矩阵,那么通过计算行向量间的距离,可以计算出物品之间的相似性,计算列向量的距离,可以得到用户间的相似性。

根据距离的定义公式,计算出向量间的距离,找到最相近的几个对象,再取平均值就可以作为预测值。

协同过滤

我们要解决一个推荐问题时,很自然的可以想到,用户为什么喜欢这些物品,应该是因为这些物品具有某些特点,而用户刚好对这些特点感兴趣。

但具体是哪些特点呢,每个特点又该用什么值来量化呢,这一步可能并不好做。协同过滤就可以提供一个解决方案,即使你并不知道这些特点都有什么,即使你并不知道各个特点都是多少值,你仍然可以得到预测结果。

它的模型表示为,x\_i 为电影 i 具有的特点向量,y\_j 为用户 j 对所有特点的偏好值向量。二者相乘就得到用户 j 对电影 i 的打分,然后可以用来和实际分数进行比较:


  1. 写出所有用户对所有电影的预测分数与实际分数的平方差公式,再加上 theta 和 x 的正则项,就得到了目标函数,我们要使这个函数达到最小值。


  2. 然后计算 cost function 对 theta 和 x 的偏导:


  3. 接着可以用梯度下降法迭代求 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后全职主妇
喜欢人工智能,行动派
创造力,思考力,学习力提升修炼进行中
欢迎您的喜欢,关注和评论!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容