Spark MLlib中实现了ALS(Alternating Least Squares)基于协同过滤的推荐算法。
MovieLens数据集收集了大量用户对不同电影的评分,详情见数据集官网http://grouplens.org/datasets/movielens。
下载ml-100k数据至工作目录中,终端输入命令:
mkdir -p ~/pythonwork/PythonProject/data
cd ~/pythonwork/PythonProject/data
wget http://files.grouplens.org/datasets/movielens/ml-100k.zip
unzip -j ml-100k
启动 Hadoop Muti Node Cluster,复制ml-100k至HDFS中
start-all.sh
hadoop fs -mkdir /user/yyf/data
hadoop fs -copyFromLocal -f ~/pythonwork/PythonProject/data /user/yyf/
hadoop fs -ls /user/yyf/data
终端输入命令:
cd ~/pythonwork/ipynotebook
PYSPARK_DRIVER_PYTHON=ipython PYSPARK_DRIVER_PYTHON_OPTS="notebook" HADOOP_CONF_IR=/usr/local/hadoop/etc/hadoop MASTER=yarn-client pyspark
ALS算法训练数据的格式是Rating RDD数据类型
1、配置文件读取路径(IPython/Jupyter Notebook中键入以下命令)
global Path
if sc.master[:5]=="local":
Path="file:/home/yyf/pythonwork/PythonProject"
else:
Path="hdfs://master:9000/user/yyf/"
2、导入ml-100k数据u.data
rawUserData=sc.textFile(Path+"data/u.data")
rawUserData.count()
上图,4个字段分别是:用户id、项目(电影)id、评分、评分日期
导入Rating模块,读取rawUserData前3个字段,按照用户id、电影id、用户对电影的评分来编写rawRatings
rawRatings=rawUserData.map(lambda line:line.split("\t")[:3])
## 取前5项
rawRatings.take(5)
3、准备ALS训练数据
ALS训练数据格式 Rating RDD数据类型定义如下:
Rating(user,product,rating)
其中,user字段是用户编号,product是产品编号,rating是用户对产品的评价
第一行表示:编号为196的用户对编号为242的电影的评分为3
1、导入ALS模块
from pyspark.mllib.recommendation import ALS
使用ALS.train进行训练,两种训练模式:显式评分训练与隐士评分训练:
使用实际的电影评分,ALS.train(ratings,rank,iterations=5,lambda_=0.01)
将实际的电影评分看作1(实际意义变成了用户对电影感不感兴趣的二分类标签问题),ALS.trainImplicit(ratings,rank,iterations=5,lambda_=0.01)
两种训练模式都会返回MatrixFactorizationModel模型,其中参数:
2、使用ASL.train命令进行训练
1、针对用户推荐电影
使用MatrixFactorizationModel.recommendProducts(user: Int, num: Int)方法,其中参数user为用户id,num为推荐的项数 。
上图推荐结果中,第一项表示系统对用户666首先推荐的电影id,推荐的评分约为5.70。返回的结果按照评分从高到低排列,评分越高说明用户可能越感兴趣。
2、预测用户对某部电影的评分
使用.predict(user: Int, product: Int)方法:
上图的运行结果表示:编号为666的用户对编号为957的电影的评分可能为2.72
3、针对电影推荐用户
给定某部电影,将其推荐给可能对它感兴趣的用户,使用.recommendUsers(product: Int, num: Int)方法,其中参数product表示电影id,num表示推荐的用户数。
运行结果返回5个用户(对957最感兴趣的)以及相应的评分。
建立Recommend项目,包含两个.py程序:
1、RecommendTrain.py
2、Recommend.py
RecommendTrain.py
# -*-coding: utf-8 -*-
from pyspark import SparkContext
from pyspark import SparkConf
from pyspark.mllib.recommendation import Rating
from pyspark.mllib.recommendation import ALS
def SetPath(sc):
"""定义全局变量Path,配置文件读取"""
global Path
if sc.master[0:5] == "local": # 当前为本地路径,读取本地文件
Path = "file:/home/yyf/pythonwork/PythonProject/"
else: # 不是本地路径,有可能是YARN Client或Spark Stand Alone必须读取HDFS文件
Path = "hdfs://master:9000/user/yyf/"
def CreateSparkContext():
"""定义CreateSparkContext函数便于创建SparkContext实例"""
sparkConf = SparkConf() \
.setAppName("Recommend") \
.set("spark.ui.showConsoleProgress","false")
sc = SparkContext(conf=sparkConf)
SetPath(sc)
print("master="+sc.master)
return sc
def PrepareData(sc):
"""数据预处理:读取u.data文件,转化为ratingsRDD数据类型"""
rawUserData = sc.textFile(Path + "data/u.data")
rawRatings = rawUserData.map(lambda line: line.split("\t")[:3])
ratingsRDD = rawRatings.map(lambda x: (x[0], x[1], x[2]))
return ratingsRDD
def SaveModel(sc):
"""存储模型"""
try:
model.save(sc, Path+"ALSmodel")
print("模型已存储")
except Exception:
print("模型已存在,先删除后创建")
if __name__ == "__main__":
sc = CreateSparkContext()
print("==========数据准备阶段==========")
ratingsRDD = PrepareData(sc)
print("========== 训练阶段 ============")
print(" 开始ALS训练,参数rank=5,iterations=10,lambda=0.1")
model = ALS.train(ratingsRDD, 5, 10, 0.1)
print("========== 存储model ==========")
SaveModel(sc)
Recommend.py
# -*-coding: utf-8 -*-
from pyspark import SparkContext
from pyspark import SparkConf
from pyspark.mllib.recommendation import MatrixFactorizationModel
import sys
def SetPath(sc):
"""定义全局变量Path,配置文件读取"""
global Path
if sc.master[0:5] == "local": # 当前为本地路径,读取本地文件
Path = "file:/home/yyf/pythonwork/PythonProject/"
else: # 不是本地路径,有可能是YARN Client或Spark Stand Alone必须读取HDFS文件
Path = "hdfs://master:9000/user/yyf/"
def CreateSparkContext():
"""定义CreateSparkContext函数便于创建SparkContext实例"""
sparkConf = SparkConf() \
.setAppName("Recommend") \
.set("spark.ui.showConsoleProgress","false")
sc = SparkContext(conf=sparkConf)
SetPath(sc)
print("master="+sc.master)
return sc
def loadModel(sc):
"""载入训练好的推荐模型"""
try:
model = MatrixFactorizationModel.load(sc, Path+"ALSmodel")
print("载入模型成功")
except Exception:
print("模型不存在, 请先训练模型")
return model
def PrepareData(sc):
"""数据准备:准备u.item文件返回电影id-电影名字典(对照表)"""
itemRDD = sc.textFile(Path+"data/u.item")
movieTitle = itemRDD.map(lambda line: line.split("|")) \
.map(lambda a: (int(a[0]), a[1])) \
.collectAsMap()
return movieTitle
def RecommendMovies(model,movieTitle,inputUserId):
RecommendMovie = model.recommendProducts(inputUserId, int(input[1]))
print("对用户ID为"+str(inputUserId)+"的用户推荐下列"+input[1]+"部电影:")
for p in RecommendMovie:
print("对编号为" + str(p[0]) + "的用户" + "推荐电影" + str(movieTitle[p[1]]) + "推荐评分为" + str(p[2]))
def RecommendUsers(model,movieTitle,inputMovieId):
RecommendUser = model.recommendUsers(inputMovieId, int(input[1]))
print("对电影ID为"+str(inputMovieId)+"的电影推荐给下列"+input[1]+"个用户:")
for p in RecommendUser:
print("对编号为" + str(p[0]) + "的用户" + "推荐电影" + str(movieTitle[p[1]]) + "推荐评分为" + str(p[2]))
def Recommend(model):
"""根据参数进行推荐"""
if input[0][0] == "U":
RecommendMovies(model, movieTitle, int(input[0][1:]))
if input[0][0] == "M":
RecommendUsers(model, movieTitle, int(input[0][1:]))
if __name__ == "__main__":
print("请输入2个参数, 第一个参数指定推荐模式(用户/电影), 第二个参数为推荐的数量如U666 10表示向用户666推荐10部电影")
input = [i for i in sys.stdin.readline().strip().split(" ")]
sc=CreateSparkContext()
print("==========数据准备==========")
movieTitle = PrepareData(sc)
print("==========载入模型==========")
model = loadModel(sc)
print("==========进行推荐==========")
Recommend(model)
运行结果:
请输入2个参数, 第一个参数指定推荐模式(用户/电影), 第二个参数为推荐的数量如U666 10表示向用户666推荐10部电影
U666 10
==========数据准备==========
==========载入模型==========
载入模型成功
==========进行推荐==========
对用户ID为666的用户推荐下列10部电影:
对编号为666的用户推荐电影Boys, Les (1997)推荐评分为4.88358730439
对编号为666的用户推荐电影Pather Panchali (1955)推荐评分为4.75295263874
对编号为666的用户推荐电影Some Mother's Son (1996)推荐评分为4.53178390243
对编号为666的用户推荐电影Anna (1996)推荐评分为4.50898184243
对编号为666的用户推荐电影Butcher Boy, The (1998)推荐评分为4.46272366026
对编号为666的用户推荐电影Spanish Prisoner, The (1997)推荐评分为4.46272366026
对编号为666的用户推荐电影Brothers in Trouble (1995)推荐评分为4.46272366026
对编号为666的用户推荐电影Butcher Boy, The (1998)推荐评分为4.46272366026
对编号为666的用户推荐电影They Made Me a Criminal (1939)推荐评分为4.39719073006
对编号为666的用户推荐电影Saint of Fort Washington, The (1993)推荐评分为4.37662670282