继搜索引擎以后,推荐系统改变了用户与网站之间的交互方式,在提升用户参与度和多样化推荐产品方面有重要的应用。亚马逊有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_title
和cosine_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
3.视频配套代码