推荐系统与协同过滤、奇异值分解

原做者:邓旭东html

原文发于做者我的微信公众号:大邓和他的Python(微信号:python

DaDengAndHisPython),点击查看原文,掘金已得到转载受权。再次感谢做者。算法


昨天我从Youtube上把PyCon2018和PyData2018两个会议对本身比较有用的视频下载下来,昨天分享的《使用pandas作更好的数据科学》来自PyData2018。受到该演讲者内容启发写了本文。
bash

视频地址:v.qq.com/x/page/i067…
微信

Daniel Pyrathon - A practical guide to Singular Value Decomposition in Python PyCon2018
奇异值分解Python实操

复制代码

1、预备知识

1.1 协同过滤

平常生活中,像亚马逊、淘宝、京东、今日头条等各大互联网公司会无时不刻的收集咱们的网络用户行为数据,并根据积累的历史行为数据对咱们推送推荐内容或者推荐商品。这就是咱们未曾感觉到存在的推荐算法所起到的做用,这之中比较常见的实现方式是协同过滤(Collaberative Filtering)。数据设计到用户、产品及产品评价三种信息,数据相似于下图网络


1.2 类似的人更容易作类似的事

协同过滤的核心想法是类似的人每每会作类似的事情。好比,A 和 B 是两个崇尚科技的人(类似信息源于大量的观影数据),而 B 喜欢 看科幻片 ,那么咱们猜想 A 也喜欢 科幻片。
机器学习



1.3 问题提出

上面咱们展现的用户电影可视化图,实际上就是推荐算法中常常用到的用户-评价矩阵,ide

  • 那么咱们如何对矩阵进行计算,才能获取类似性信息?学习

  • 有了类似性信息咱们又如何去利用类似性信息去作产品推荐?测试

  • 咱们知道两个向量经过余弦类似计算就能够得出两个向量的近似程度,那么这些向量咱们又该如何从用户-评价矩阵提取呢?

1.4 奇异值分解SVD

这就用到奇异值分解(Singular Value Decompositon),简称SVD。具体怎么提取不是咱们本文的重点,Python都帮咱们实现了,咱们只须要稍微了解下SVD,就直接上手用。

好比咱们如今有了用户-评价矩阵


给定一个矩阵,咱们均可以分解获得两种矩阵,一种是用户信息矩阵,一种是评价信息(产品)矩阵。这两种矩阵在本例中使用了n_features = 2,即对于用户向量或者产品评价向量长度均为2,实际上也能够为其余数字(好比3,4。。)


那么User1对于蓝色电影的喜欢程度是能够经过向量计算得出3.52


1.5 用户类似性

以下图,在二维坐标中咱们能够看出不一样用户间的类似度。


2、项目实战

咱们将使用Python的surprise库,对MovieLens数据集构建一个简单的协同过滤推荐系统。

安装方法:

pip3 install scikit-surprise
复制代码

若是你的anaconda自带jupyter notebook。那么你可能须要使用下面的安装方法

conda install -c conda-forge scikit-surprise
复制代码

从安装名咱们发现其他scikit的特殊关系,因此熟悉scikit的同窗看本文会比较轻松。

2.1 准备数据

MovieLens数据集含有1000个用户的100000个观影评分记录。其中咱们只须要使用该数据集中的u.data文件,该文件以行存储,每一行包括userID itemID rating timestamp,且各个字段之间以\t间隔。部分数据以下

['196\t242\t3\t881250949\n', 
'186\t302\t3\t891717742\n', 
'22\t377\t1\t878887116\n', 
'244\t51\t2\t880606923\n', 
'166\t346\t1\t886397596\n']
复制代码

2.2 切割数据

在surprise库中咱们能够建立读取器Reader的格式。在本例中,咱们使用\t将每行数据分隔后分配给

user item rating timestamp

定义好Reader格式后,咱们使用Dataset对象对数据进行读取操做。

from surprise import Reader, Dataset

#定义数据格式
reader = Reader(line_format='user item rating timestamp', sep='\t')

#使用reader格式从u.data文件中读取数据
data = Dataset.load_from_file('u.data', reader=reader)
复制代码

2.3 交叉检验

surprise提供了交叉验证(crossvalidation)的接口,crossvalidation是啥?

咱们先看图解释下


一份数据平均的分红5份,若是4份作训练集,1份作测试集。那么当咱们训练模型的时候有1/5的数据咱们的模型是没法学习的,这就浪费了20%。

可是咱们又不能拿把全部的数据通过一次训练,再拿其中训练过的数据去作预测。由于这样会致使准确率a很是高,但放到实践中这个模型的预测准确率其实是低于a的。

因此就有了crossvalidation交叉检验。咱们一份数据训练5次,每次完整的数据分红4份训练1份测试。这样就解决了上面遇到的问题。以下图

#n_folds=5是指数据分红5份,作5次训练预测
data.split(n_folds=5)
复制代码

2.4 最优化Optimization

训练怎么达到最优,那就要有Optimization,也就是要有一个可供参考的标准。

训练的方式与其余机器学习方法相似,要使得一种算法试图优化其预测值尽量接近真实值。在协做过滤应用中,咱们的算法将尝试预测某个用户-电影组合的评级,并将该预测值真实值进行比较。 使用经典偏差测量如均方根偏差(Root mean squared error,RMSE)和平均绝对偏差(Mean absolute error,MAE)来测量预测值和真实值之间的差别。

在surprise库中,咱们有普遍的算法可供选择,并为每种算法(SVD,NMF,KNN)提供多种参数选择。 就咱们的例子而言,咱们将使用SVD算法。 优化目标measures采用RMSE', 'MAE

from surprise import SVD, evaluate

#至关于scikit的机器学习算法的初始化
svd = SVD()

#至关于scikit中的score,模型评估
evaluate(svd, data, measures=['RMSE', 'MAE'])
复制代码

运行

Evaluating RMSE, MAE of algorithm SVD.
    ------------
    Fold 1
    RMSE: 0.9324
    MAE:  0.7346
    ------------
    Fold 2
    RMSE: 0.9422
    MAE:  0.7423
    ------------
    Fold 3
    RMSE: 0.9367
    MAE:  0.7398
    ------------
    Fold 4
    RMSE: 0.9310
    MAE:  0.7323
    ------------
    Fold 5
    RMSE: 0.9393
    MAE:  0.7422
    ------------
    ------------
    Mean RMSE: 0.9363
    Mean MAE : 0.7382
    ------------
    ------------
复制代码

从上面运行结果看,optimizer选用RMSE后,5次训练的平均准确率高达93.63%。

2.5 预测

最后咱们仍是很想看看训练出模型,其预测能力到底结果怎么样?

此次咱们就作交叉验证了,省事点直接所有丢给SVD去训练

from surprise import SVD
from surprise import Reader, Dataset

#读取数据
reader = Reader(line_format='user item rating timestamp', sep='\t')
data = Dataset.load_from_file('u.data', reader=reader)
data = data.build_full_trainset() 

#初始化svd模型,用data训练模型
svd =SVD()
svd.fit(data)
复制代码

上面的代码

data = data.build_full_trainset()
复制代码

这一行原本我没有写,可是当我注释掉这一行。出现下面的错误,

DatasetAutoFolds' object has no attribute 'global_mean' on python surprise 复制代码

最后在stackoverflow中找到解决办法,须要将data转化为surprise可以用的trainset类。

https://stackoverflow.com/questions/49263964/datasetautofolds-object-has-no-attribute-global-mean-on-python-surprise
复制代码

下面继续咱们的预测,userid为196,itemid为302, 其真实评分为4。

userid = str(196)
itemid = str(302)
actual_rating = 4
print(svd.predict(userid, 302, 4))
复制代码

运行

user: 196  item: 302   r_ui = 4.00   est = 3.41   {'was_impossible': False}
复制代码

预测值为3.41, 真实值为4。仍是相对靠谱的。

相关文章
相关标签/搜索