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模型進行推薦,分佈是針對產品和用戶的推薦;