首先数据集下载:
http://files.grouplens.org/datasets/movielens/ml-100k.zip
下载好后解压,里面有几个比较重要
首先是u.user 记录着用户的信息
u.data记录着用户对其看过的电影的评价
u.item记录的便是电影的信息
----------------------------------------------------------------
首先看u.data里面数据的具体细节:
from pyspark import SparkContext
import matplotlib
#ALS is a matrix decomposition
from pyspark.mllib.recommendation import ALS
import os
os.environ['PYSPARK_PYTHON']='/usr/bin/python'
sc.stop()
sc = SparkContext("local[1]","My text")
data = sc.textFile("file:///usr/local/data/ml-100k/u.data")
print(data.first())
可以看到,其有四个字段,依次是用户ID,电影ID , 用户对其评价(1-5等级)等,之间是以制表符间隔的
加下来将其分隔开,并取前三个比较重要的字段:
rawRatings = data.map(lambda record :record.split('\t')).map(lambda record:(record[0],record[1],record[2]))
print(rawRatings.first())
这里用到了ALS这一矩阵分解进行的训练,选取分解后10个维度,10个迭代次数,控制正则化参数()就是控制过拟合参数)进行训练,默认训练方式是显式训练,除此之外还有就是trainImplicit隐式训练模式(它不是一种等级矩阵,而是用一种类似0/1的偏好矩阵)
#this train is the Explicit train ,another way is the Implicit trainImplicit
model = ALS.train(rawRatings,10,10,0.1)
print(model)
训练好模型后便可以进行一系列预测,比如预测666这个用户对957这个电影的评价
model.predict(666,957)
给666这个用户推荐5步电影
model.recommendProducts(666,5)
预测喜欢957这个电影的5个用户
model.recommendUsers(957,5)
为了结果更加直观,这里也将存储电影信息的u.item读入
itemRDD = sc.textFile("file:///usr/local/data/ml-100k/u.item")
print(itemRDD.first())
从上面可以看出它的数据格式,我们这里比较关心,第二个字段即电影名
movieTitle = itemRDD.map(lambda record:record.split('|')).map(lambda record:(int(record[0]),record[1])).collectAsMap()
print(movieTitle[2])
结合上面得到的电影名,就可以直观的看到给666这个用户推荐的具体5部电影名
movieIP = model.recommendProducts(666,5)
for ip in movieIP:
print("the movie name is :"+movieTitle[ip[1]]+"and the score is :"+str(ip[2])+"\n")
下面开始介绍一些指标用来评价训练好的模型性能
来看一下余弦相似度,它其实就是来看两个向量的夹角,0度为完全相似,180则认为二者偏差较大,可以通过提取训练好后具体某两个电影的向量来看这两个电影的相似度,就是我们常说的 “那些年,一起追过的女孩”和“小时代”都属于爱情片,二者计算出来的余弦相似度也许就比较大。
说了这么多,下面看一下,余弦相似度的定义,注意:本来余弦相似度的范围是【-1,1】,这里归一到【0,1】:
import numpy as np
#similar cosine
def Similarity(Vector1,Vector2):
Vec1 = np.mat(Vector1)
Vec2 = np.mat(Vector2)
num = float(Vec1*Vec2.T)
denom = np.linalg.norm(Vec1)*np.linalg.norm(Vec2)
cos = num/denom
return 0.5+0.5*cos
print(Similarity((1,1,0),(1,1,0)))
#Extraction vector factor,but I do not know how to get a specific vector
print(model.productFeatures)
下面来看一下其他的评价方式
为了后续工作,这里先将data做一些排序
ratings = data.map(lambda record :record.split('\t')).map(lambda record:(int(record[0]),int(record[1]),int(record[2]))).sortByKey(True,1)
print(ratings.take(5))
这里可以清楚的看到首先是1这个用户对一些电影的评价,紧接着就是2这个用户对一些列电影的评价,以此类推,,,
我们接下来要用700这个用户做为样本进行评价,首先筛选出用户700对电影评价的信息
#select all the movies which the 700 of users has seen
moviesForUser = ratings.filter(lambda record :record[0]==700)
print(moviesForUser.take(10))
可以看到其已经对很多电影进行了评价,我们接下来选取其评价最高的10部电影
#select all the top 10 movies which the 700 of users has seen
topMovies = moviesForUser.takeOrdered(10,key=lambda record:-record[2])
print(topMovies)
print('----------------')
for movieIP in topMovies:
print("the movie name is :"+movieTitle[movieIP[1]]+" and the score is "+str(movieIP[2])+"\n")
可以看到其对Star Wars(ID为50)即星球大战这部电影评价为5,很高的评价,那么我们用我们训练好的模型来看一下其对星球大战有多高的评价呢?
#To test the accuracy of the model,we can use the the 50 of moviesip to see the score that the model gives
#we can see the 700 user has given the movie 5 score, that is the movie is good
model.predict(700,50)
可以看到其评价也很高4.2吧,当然通过一些列优化模型的方法,我们让其结果更加准确即更加接近5。
接下来我们使用训练好的模型来给700这个用户推荐10部电影
#Next , we use our model that has been trained to predict the top 10 movies of the 700 user likesP
predictMovieIP = model.recommendProducts(700,10)
print(predictMovieIP)
print('---------------------------')
for ip in predictMovieIP:
print("the movie name is :"+movieTitle[ip[1]]+"and the score is :"+str(ip[2])+"\n")
对比上面其已经看过的电影的评价,再看一下推荐结果,我们大概就可以看出训练好的模型的好坏。
当然上面的这一评价模型好坏的方法还是比较感性,下面我们用数学的方法来量化评价
下面介绍均方差MSE和均方根误差RMSE,关于其概念很简单啦,不做过多介绍,还是给出具体实现代码吧
首先我们构造了两个List,一个是其最喜欢的10部电影的真实评价,另一个是通过模型来预测这10部电影的评价
#we will use "Mean Squared Error" (MSE) to evalute the model
#of course ,"the Root Mean Squared Error" can also been used.
#In fact ,there is no different !!!!!!!!!!!!!!!!!!!!!!!!
realRatings = []
predictRatings = []
for movieIP in topMovies:
realRatings.append(float(movieIP[2]))
predictRatings.append(model.predict(700,movieIP[1]))
print(realRatings)
print(predictRatings)
下面使用python 来写一下MSE,注意这里是传统的方法,并没有使用Spark
import math
def MSE(vect1,vect2):
num = len(vect1)
vect1 = np.mat(vect1)
vect2 = np.mat(vect2)
return np.sum(np.power((vect1-vect2),2))/num
resultMSE = MSE(realRatings,predictRatings)
resultRMSE = math.sqrt(resultMSE)
print("the MSE is :"+str(resultMSE))
print("the RMSE is :"+str(resultRMSE))
当然了,在spark环境中最好还是使用spark,比较分布式还是比较快:
毫无疑问,先要将其转化为RDD,才能使用spark的一些算子
#of course,we can use pyspark to make it!!!!!!!!!!!
realRatingsRDD = sc.parallelize(realRatings)
predictRatingsRDD = sc.parallelize(predictRatings)
predictAndRealRDD = predictRatingsRDD.zip(realRatingsRDD)
print(predictAndRealRDD.collect())
下面就是简单的利用了一些spark内置的算子实现了MSE和RMSE
resultMSE = predictAndRealRDD.map(lambda record:(record[0]-record[1])**2).mean()
resultRMSE =sc.parallelize([resultMSE]).map(lambda record :math.sqrt(record)).collect()
print("the MSE is :"+str(resultMSE))
print("the RMSE is :"+str(resultRMSE[0]))
当然了,spark中已经内置了一些评价函数,里面有很多的指标函数,其中就包括MSE和RMSE,我们直接调用即可
其输入参数要求的格式是(prediction1,label1),(prediction2,label2),(prediction3,label3),........
#There is also Build-in evalutation function in pyspark
#predictionAndLabel RDD of (predicted1 , label1) ,(predicted2 , label2),(predicted3 , label3),...
#so predicted.dimension must be equal to the label.dimension
from pyspark.mllib.evaluation import RegressionMetrics,RankingMetrics
metrics = RegressionMetrics(predictAndRealRDD)
print("the MSE is :"+str(metrics.meanSquaredError)+"\n")
print("the RMSE is :"+str(metrics.rootMeanSquaredError)+"\n")
通过上面的三种方法,我们可以看到均得到了相同的结果
下面介绍另一种评价指标MAP,K值平均准确率,它的原理就是:假设选取K部推荐给用户的电影(集合A),通过已有的数据集,然后看每一部是否在其感兴趣的电影内(集合B),如果是就加一分,当然推荐评分越高的电影如果在其感兴趣的范围内,其加一分所占的权值应该越大。APK所试图衡量的是模型对用户感兴趣和会去接触的物品的预测能力,当然在本例子中用MAP去衡量模型并不是太合适,因为我们得到的是777这个用户所有电影,里面有的电影该用户并不感兴趣,即集合B中有用户不感兴趣的电影,当然我们可以取其比如说前20评价较高的电影最为集合B,下面的代码笔者没有这样做,只是简单讲所有777这个用户看过的电影作为了集合B。
补充:MAP同样适用估隐式数据集上的推荐,而MSE和RMSE就不怎么适合
首先得到两个集合
#Next,we can also use the "Mean Average Precision" (MAP) to calculate
realmovies=moviesForUser.map(lambda record :record[1]).collect()
predictmovies = sc.parallelize(predictMovieIP).map(lambda record:record[1]).collect()
print(realmovies)
print(predictmovies)
下面就是MAP具体的细节算法
def MAP(pre,real,k):
prek = sc.parallelize(pre).take(k)
score = 0.0
numHits = 0.0
for (ip,movip) in enumerate(prek):
if movip in real:
numHits+=1.0
score+=numHits/float(ip+1.0)
if len(real)==0:
return 1.0
else:
return score/min(len(real),k)
resultMAP = MAP(predictmovies,realmovies,10)
print(resultMAP)
当然了spark当然已经内置了这一评价函数,这里笔者没有试验成功,按其官网API说明其输入参数是
(predict Array ,label Array) pairs这一形式
即predictmoviesAndRealmoviesRDD,但是笔者试了一下没有成功,这里先记着,后续再研究,,,,,,,待续
#Of course,there is also Build-in evalutation function in pyspark about MAP
#predictionAndLabel RDD of (predict Array ,label Array) pairs
#the dimension of predict does not have to be equal to the dimension of label
rankMetrics = RankingMetrics(predictmoviesAndRealmoviesRDD)
print("the MAP is:"+str(rankMetrics.meanAveragePrecision))
全部代码: