PySpark 創建搜索推薦

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),如下圖

PySpark 創建搜索推薦

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章