PyCon 2018: SVD推荐系统在Python中的实践

引言

继搜索引擎以后,推荐系统改变了用户与网站之间的交互方式,在提升用户参与度和多样化推荐产品方面有重要的应用。亚马逊有35%的利润来源于它的推荐系统,Netflix有75%的用户根据推荐系统选择电影。html

推荐系统是一个很是大的话题,本文介绍一种经常使用的基于模型的协同过滤算法——SVD(奇异值分解),在python中的使用。python

假设咱们用m个用户,n个商品,每一个用户对每一个商品的评分能够组成一个m*n的二维矩阵。固然,这个矩阵中会有很是多的值是不知道的,多是用户没有用过这个商品,也有可能用户使用后没有进行评分。以下图所示git

图中空白位置即未知的值。接下来,咱们须要作的是根据这个残缺的二维矩阵中已知的值,预测出未知的值,即预测出每个用户对每个商品的评分。github

能够想象,当矩阵被预测值补充完整以后,矩阵的每一行即表示一个用户对全部商品的评分,能够从这些评分中提取评分最高的几个商品推荐给用户,这样咱们就完成了一个推荐系统模型。算法

接下来,就是如何经过已知值预测未知值的问题了,这里咱们采用矩阵分解的方式,如图所示bash

中间矩阵能够拆分为左边和上边两个矩阵的乘积,这就是奇异值分解,一个矩阵老是能够拆分红两个矩阵相乘,SVD的原理能够见这篇博客,SVD方法在推荐系统中应用的原理能够参考这篇博客,下面,咱们主要来说讲如何在python中使用。ide

初始化及创建模型

首先须要安装surprise库,用下面这条命令函数

pip install scikit-surprise
复制代码

假设咱们如今有这样的数据(注:load_movielens函数不是库内置的,须要拿数据文件并本身定义,详情见文末连接中的做者源码)测试

movielens_df: pd.DataFrame = load_movielens()
movielens_df.head(5)

        user_id	    movie_title	            rating
36649	User 742	Jerry Maguire (1996)	4
2478	User 908	Usual Suspects, The (1995)	3
82838	User 758	Real Genius (1985)	4
69729	User 393	Things to Do in Denver when You're Dead (1995) 3 36560 User 66 Jerry Maguire (1996) 4 复制代码

即用户与电影之间的评分对应关系,下面导入须要的模块网站

from surprise import SVD
from surprise import Dataset, Reader
from surprise.model_selection import cross_validate, train_test_split
复制代码

下面开始正式建模

第一步:初始化reader,指定评分范围为1分到5分

reader = Reader(rating_scale=(1, 5))
复制代码

第二步:初始化数据,传入的数据只能有3列,必须按照这样的顺序[user_id, product_id, rating]

data = Dataset.load_from_df(movielens_df, reader)
复制代码

这里须要注意:data变量已经不是DataFrame类型了,而是surprise库中的一种数据类型。从上面movielens_df的结果能够看出,咱们的数据不是本文最初提到的矩阵形式,因此这一步转换就会将数据转化成surprise库须要的形式,便于以后的算法求解。

第三步:拆分训练集与测试集,75%的样本做为训练集,25%的样本做为测试集

trainset, testset = train_test_split(data, test_size=.25)
复制代码

这里的trainset的类型是surprise.dataset.Trainset类型,咱们能够查看数据的基本信息

trainset.n_users # 943
trainset.n_items # 596
复制代码

这说明咱们要用于训练的样本共有943个用户,596个商品。

第四步:训练模型,指定有100个隐含特征,使用训练集进行训练

model = SVD(n_factors=100)
model.fit(trainset)
复制代码

这里须要说明一下,100个隐含特征是指,本来943*596的矩阵会被拆分红943*100和100*596的两个矩阵乘积,n_factors值能够任意指定只要不超过596便可,可是设置不一样的值将会拟合出不一样的模型,须要选择使结果较优的值。

咱们也能够查看拆分出来的两个矩阵

model.pu.shape # (943, 100)
model.qi.shape # (596, 100)
复制代码

根据模型结果进行推荐

预测一个用户对一个电影的评分

指定用户和电影名便可

a_user = "User 196"
a_product = "Toy Story (1995)"
model.predict(a_user, a_product)

# Prediction(uid='User 196', iid='Toy Story (1995)', r_ui=None, est=3.93380711688207, details={'was_impossible': False})
复制代码

电影之间相关性

这里咱们须要写get_vector_by_movie_titlecosine_distance函数(详情见文末连接中做者源码)

以后咱们就能够实现输入两个电影名称,便可得到他们之间的相关性

toy_story_vec = get_vector_by_movie_title('Toy Story (1995)', model)
wizard_of_oz_vec = get_vector_by_movie_title('Wizard of Oz, The (1939)', model)

similarity_score = cosine_distance(toy_story_vec, wizard_of_oz_vec)
similarity_score
# 0.9461284008856982
复制代码

这是彻底不考虑导演等电影特征计算出来的电影类似度,由于咱们只使用了评分数据。

寻找与一个电影最类似的电影

首先须要实现get_top_similarities函数,得到最类似的五个电影,最后效果以下

get_top_similarities('Star Wars (1977)', model)

	vector cosine distance	movie title
0	0.000000	            Star Wars (1977)
1	0.262668	            Empire Strikes Back, The (1980)
2	0.295667	            Return of the Jedi (1983)
3	0.435423	            Raiders of the Lost Ark (1981)
复制代码

参考资料

1.视频 Daniel Pyrathon - A practical guide to Singular Value Decomposition in Python - PyCon 2018

2.surprise帮助文档

3.视频配套代码

相关文章
相关标签/搜索