1、PySpark 创建搜索推荐
1.1、常见推荐算法
- 常见推荐算法
算法 | 说明 |
---|---|
基于关系型规则的推荐(Association Rule) | 消费者购买产品A,那么他有多大机会购买产品B;<br> 购物车分析(啤酒和尿布) |
基于内容的推荐(Context-based) | 分析网页内容自动分类,在将用户自动分类;<br> 将新进分类的网页推荐给对该群感兴趣的用户 |
人口统计式的推荐(Demographic) | 将用户以个人属性(性别、年龄、教育背景、居住地、语言)作为分类指标; <br> 以此类作为推荐标准 |
协同过滤式推荐(Collaborative Filtering) | 通过观察所有用户对产品的评分来推断用户的喜好; <br> 找出对产品评分相近的其他用户,他们喜欢的产品当前用户多半也会喜欢 |
- 协同过滤式推荐(Collaborative Filtering)推荐的优缺点
有点 | 缺点 |
---|---|
可以达到个性化推荐;<br> 不需要内容分析;<br> 可以发现用户新的兴趣点; <br> 自动化程度高 | 冷启动问题(Cold-start):如果没有历史数据就没有办法分析; <br> 新用户问题:新用户没有数据,就不知道他的喜好; |
1.2、Spark MLlib ALS(Alternating Least Squares) 推荐算法
ALS(Alternating Least Squares) 推荐算法,交替最小二乘法。在机器学习中,特指使用最小二乘法的一种协同推荐算法;
网站上的设计经常会请用户对某个产品进行评分,如1-5分,可以整理如下图的矩阵;
- 显示评分
用户ID | 项目1 | 项目2 | 项目3 | 项目4 | 项目5 |
---|---|---|---|---|---|
user1 | 2 | 1 | 5 | ||
user2 | 1 | 3 | 1 | 1 | |
user3 | 3 | 4 | |||
user4 | 2 | 2 | 1 | 2 | |
user5 | 1 | 1 | 1 | 4 | 1 |
有些网站在设计可能并不会请用户进行评分,但是会记录用户是否选择了某个产品,如果用户选择了某个产品,就代表用户可能对该产品有兴趣,可用1表示,整理如下图的矩阵;
- 隐式评分
用户ID | 项目1 | 项目2 | 项目3 | 项目4 | 项目5 |
---|---|---|---|---|---|
user1 | 1 | 1 | 1 | ||
user2 | 1 | 1 | 1 | 1 | |
user3 | 1 | 1 | |||
user4 | 1 | 1 | 1 | 1 | |
user5 | 1 | 1 | 1 | 1 | 1 |
推荐算法就是要找出两个用户的相似性,如,user1有兴趣的项目为项目(1,2,3),user2有兴趣的项目为项目(1,2,3,4),user1比user2少了项目4;因此,当推荐算法要推荐项目给user1会推荐项目4;
- 稀疏矩阵
当用户与项目评分越来越多时,会出现大部分都是空白(大部分项目没有评分),这种矩阵称为稀疏矩阵;当矩阵非常大的时候,要计算这样的矩阵,非常浪费资源和时间;
- 矩阵分解
为了解决稀疏矩阵的问题,需要采用矩阵分解,将矩阵A(m X n)分解为X(m X rank)矩阵与Y(rank X n),如下图
1.3、ALS数据训练和推荐
1.3.1、数据准备
使用的数据主要有u.data(用户评分数据)和u.item(电影数据)
$ head data/u.{data,item}
==> data/u.data <==
196 242 3 881250949
186 302 3 891717742
22 377 1 878887116
244 51 2 880606923
166 346 1 886397596
298 474 4 884182806
115 265 2 881171488
253 465 5 891628467
305 451 3 886324817
6 86 3 883603013
==> data/u.item <==
1|Toy Story (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Toy%20Story%20(1995)|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0
2|GoldenEye (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?GoldenEye%20(1995)|0|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
3|Four Rooms (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Four%20Rooms%20(1995)|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
4|Get Shorty (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Get%20Shorty%20(1995)|0|1|0|0|0|1|0|0|1|0|0|0|0|0|0|0|0|0|0
5|Copycat (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Copycat%20(1995)|0|0|0|0|0|0|1|0|1|0|0|0|0|0|0|0|1|0|0
6|Shanghai Triad (Yao a yao yao dao waipo qiao) (1995)|01-Jan-1995||http://us.imdb.com/Title?Yao+a+yao+yao+dao+waipo+qiao+(1995)|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0
7|Twelve Monkeys (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Twelve%20Monkeys%20(1995)|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|1|0|0|0
8|Babe (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Babe%20(1995)|0|0|0|0|1|1|0|0|1|0|0|0|0|0|0|0|0|0|0
9|Dead Man Walking (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Dead%20Man%20Walking%20(1995)|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0
10|Richard III (1995)|22-Jan-1996||http://us.imdb.com/M/title-exact?Richard%20III%20(1995)|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|1|0
u.data包含4个字段:userid(用户id)、item id(项目id)、rating(评分)、timestamp(日期时间戳)
u.item有多个字段,在此取前两个:movie id(电影id)、movie title(电影片名)
1.3.2、程序思路
【u.data】 ——sc.textFile——> 【rawUserData】 ——map——> 【rawRatings】 ——map——> 【RatingsRDD】 ——ALS.train——> 【MatirixFactoization Model】
1.3.3、程序实现
- 设置文件读取路径
In [1]: Path = "file:/home/hadoop/pyspark/PythonProject/recommend/"
- 导入u.data数据,取前3个字段:用户id,产品id,评分
In [2]: rawUserData = sc.textFile(Path + "data/u.data")
In [3]: ratingsRDD = rawUserData.map(lambda line: line.split("\t")[:3] ).map(lambda x: (x[0],x[1],x[2]))
- 导入ALS进行模型训练
In [4]: from pyspark.mllib.recommendation import ALS
In [5]: model = ALS.train(ratingsRDD, 10, 10, 0.01)
- 导入电影数据u.item,取前两个字段:电影ID和电影名称
In [6]: itemRDD = sc.textFile(Path + "data/u.item")
In [7]: movieTitle = itemRDD.map(lambda line:line.split("|")).map(lambda a:(a[0],a[1])).collectAsMap()
- 使用模型针对用户进行产品推荐,针对用户id:100,推荐5部影片
In [8]: recommendMovie = model.recommendProducts(100,5)
In [9]: for rdm in recommendMovie:
...: print("针对用户ID: {0},推荐电影:{1} ,推荐评分:{2}".format(rdm[0], movieTitle.get(str(rdm[1])), rdm[2]))
...:
针对用户ID: 100,推荐电影:Angel Baby (1995) ,推荐评分:7.610337473154874
针对用户ID: 100,推荐电影:Pather Panchali (1955) ,推荐评分:6.615085728728855
针对用户ID: 100,推荐电影:Once Were Warriors (1994) ,推荐评分:6.417519553465505
针对用户ID: 100,推荐电影:Radioland Murders (1994) ,推荐评分:6.24059727584324
针对用户ID: 100,推荐电影:Crooklyn (1994) ,推荐评分:5.944125863246919
- 使用模型针对电影进行用户推荐,针对产品id:200,给5个用户推荐
In [10]: recommendUser = model.recommendUsers(200,5)
In [11]: for reu in recommendUser:
...: print("针对用户ID: {0},推荐电影:{1} ,推荐评分:{2}".format(reu[0], movieTitle.get(str(reu[1])), reu[2]))
...:
针对用户ID: 475,推荐电影:Shining, The (1980) ,推荐评分:7.06404094277568
针对用户ID: 762,推荐电影:Shining, The (1980) ,推荐评分:6.720699631364651
针对用户ID: 695,推荐电影:Shining, The (1980) ,推荐评分:6.221277830269441
针对用户ID: 810,推荐电影:Shining, The (1980) ,推荐评分:5.9598423486309535
针对用户ID: 917,推荐电影:Shining, The (1980) ,推荐评分:5.947701017469912
1.3.4、完整程序
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
进行训练存储模型,以便给推荐阶段使用
1.数据读取:读取u.data,经过处理后产生评分数据ratingsRDD;
2.训练阶段:评分数据ratingsRDD经过ALS.train训练产生模型 Model;
3.存储模型:存储模型Model在本地或者HDFS,作为后续推荐使用。
载入模型进行推荐
1.数据读取:读取u.item,经过数据处理后产生movieTitle(电影ID/名称 的字典)
2.载入模型:载入recommendTrain.py 中的存储模型Model;
3.推荐阶段:使用模型Model 进行推荐,使用movieTitle转换显示推荐的电影名称
"""
from pyspark.mllib.recommendation import ALS, MatrixFactorizationModel
from pyspark import SparkConf, SparkContext
import time
def CreateSparkContext(appName):
"""创建spark context 对象"""
conf = SparkConf().setAppName(appName).\
set("spark.ui.showConsoleProgress", "false")
sc = SparkContext(conf = conf)
return sc
def PreUdata(sc, Pathdir, udata):
"""载入训练数据"""
# 读取数据
rawUserData = sc.textFile(Pathdir + udata)
# 读取rawUserData 前3个字段,用户/产品/评价
ratingsRDD = rawUserData.map(lambda line: line.split("\t")[:3]).\
map(lambda x: (x[0],x[1],x[2]))
return ratingsRDD
def SaveModel(sc, Pathdir, ModelName):
"""保存训练好的模型"""
try:
model.save(sc, Pathdir + ModelName)
except Exception:
print("模型已经存在,请先删除...")
def preUitem(sc, Pathdir, uitem):
"""载入用户推荐的数据"""
print("开始读取电影ID与电影名称的字典...")
itemRDD = sc.textFile(Pathdir + uitem)
# 创建 电影ID 和 电影名称 的字典
movieTitle = itemRDD.map(lambda line:line.split("|"))\
.map(lambda a:(float(a[0]),a[1]))\
.collectAsMap()
return(movieTitle)
def loadModel(sc, Pathdir, ModelName):
""" 加载保存模型 """
try:
model = MatrixFactorizationModel.load(sc, Pathdir + ModelName)
except Exception:
print("找不到ALSModel模型,请先训练")
return model
def RecommendMovies(model, movieTitle, userID):
"""根据电影推荐"""
RecommendMovie = model.recommendProducts(userID, 10)
print("针对用户ID" + str(userID) + "推荐下列相关电影:\n")
for rdm in RecommendMovie:
print("针对用户ID: {0},推荐电影:{1} ,推荐评分:{2}".\
format(rdm[0], movieTitle.get(rdm[1]), rdm[2]))
def RecommendUsers(model, movieTitle, movieID):
"""根据用户推荐 """
RecommendUser = model.recommendUsers(movieID, 10)
print("针对电影ID: {0} 电影名:{1} 推荐给下列用户:\n".\
format(movieID, movieTitle[movieID]))
for rdu in RecommendUser:
print("针对用户ID: {0},推荐电影:{1} ,推荐评分:{2}".\
format(rdu[0], movieTitle.get(rdu[1]), rdu[2]))
if __name__ == "__main__":
# 设置应用名,工作路径..
appName = u"Recommend"
Pathdir = u"file:/home/hadoop/pyspark/PythonProject/recommend/"
udata = u"u.data"
uitem = u"u.item"
ModelName = u"ALSmodel"
print("初始化环境,创建应用,名字:Recommend")
sc = CreateSparkContext(appName)
print("==========数据准备阶段===========")
ratingsRDD = PreUdata(sc, Pathdir, udata)
print("==========训练阶段===============")
print("开始ALS训练,参数rank=5,iterations=10, lambda=0.01");
model = ALS.train(ratingsRDD, 5, 10, 0.01)
print("========== 存储Model============")
SaveModel(sc, Pathdir, ModelName)
# 停留5秒,开始执行推荐
time.sleep(5)
print("==========载入推荐数据==========")
movieTitle = preUitem(sc, Pathdir, uitem)
print("==========载入训练模型==========")
model = loadModel(sc, Pathdir, ModelName)
# 通过产品或者用户进行推荐
print("...............用户推荐...............")
movieID = 200
RecommendUsers(model, movieTitle, movieID)
print("...............电影推荐...............")
userID = 200
RecommendMovies(model, movieTitle, userID)
- 程序运行
$ spark-submit recommend.py
1.4、总结
使用 ALS 模块对影片的评价数据进行训练,训练后产生 MatrixFactorizationModel模型进行推荐,分布是针对产品和用户的推荐;
转载于:https://blog.51cto.com/balich/2134500