Spark推薦系統實踐

推薦系統是根據用戶的行爲、興趣等特徵,將用戶感興趣的信息、產品等推薦給用戶的系統,它的出現主要是爲了解決信息過載和用戶無明確需求的問題,根據劃分標準的不一樣,又分不少種類別:css

  1. 根據目標用戶的不一樣,可劃分爲基於大衆行爲的推薦引擎和個性化推薦引擎
  2. 根據數據之間的相關性,可劃分爲基於人口統計學的推薦和基於內容的推薦

    ......git

一般,咱們在討論推薦系統時主要是針對個性化推薦系統,由於它纔是更加智能的信息發現過程。在個性化推薦系統中,協同過濾算法是目前應用最成功也是最廣泛的算法,主要包括兩大類,基於用戶的協同過濾算法和基於物品的協同過濾算法。github

此外,在實際的推薦系統中,每每會針對不一樣的場景使用不一樣的策略以及多策略組合,從而達到最好的推薦效果。面試

本篇文章主要經過應用Spark KMeans、ALS以及基於內容的推薦算法來進行推薦系統的構建,具體涉及到的數據、表和代碼比較多,後續會在github上給出詳細說明。redis

首先看一下推薦系統的概況圖:算法

下面主要針對推薦算法的應用和推薦過程作詳細闡述。sql

1. 基於Spark KMeans實現對院校聚類性能優化

1.1 數據準備微信

經過院校信息的結構化數據school.txt和school_loca.txt,將兩個結構化文件加載到hive表中。app

(1)school.txt數據樣例

院校id 名稱 地址 類型 住宿方式  學費  備用金 運營歸屬  受權平臺 審覈狀態
1,諾丹姆吉本斯主教中學(Notre Dame-Bishop Gibbons School),紐約州-斯克內克塔迪,混校,寄宿家庭,283047,13289,UC獨家代理,校代網/微信,審覈經過
2,畢曉普馬金高中(Bishop Maginn High School),紐約州-奧爾巴尼,混校,寄宿家庭,277488,13028,UC獨家代理,校代網/微信,審覈經過

 

school.txt加載到hive表中的表結構信息:

schoolid        int         ##院校id
name    string            ##院校名稱
location        string      ##院校地址
type    string             ##學校類型
zhusu   string            ##住宿方式
fee     double            ##學費
byj     double             ##備用金
yygs    string              ##運營歸屬
sqpt    string              ##受權平臺
shzt    string               ##審覈狀態

 

(2)school_loca.txt數據樣例

地址id  地址名稱
1,加利福尼亞州-洛杉磯                                                             
2,紐約州-裏弗黑德
3,新澤西州-萊克伍德市
4,安大略省-鮑曼維爾市

 

school_loca.txt加載到hive表中的表結構信息

locationid      int       ##院校地址id
name    string          ##院校地址名稱

 

(3)對院校信息進行量化處理

sql語句示例:

select
 sd.schoolid,
 sd.name schoolname,
 sd.location,
 sl.locationid,
 (case
   when sd.type="混校" then "0"
   when sd.type="男校" then "1"
   when sd.type="女校" then "2"
 end) as school_type,
 (case
   when sd.zhusu="寄宿家庭" then "0"
   when sd.zhusu="學校宿舍" then "1"
   when sd.zhusu="男生學校宿舍/女生寄宿家庭" then "2"
   when sd.zhusu="學校宿舍/寄宿家庭" then "3"
   when sd.zhusu="學校宿舍-寄宿家庭" then "3"
 end) as zs,
 sd.fee,
 sd.byj
from
 school2_detail sd
join
 school2_location sl
on sd.location = sl.name;

 


提取出惟一描述一所院校的「特徵因子」:學校地址id(locationid)、學校類型(school_type)、住宿方式(zs)、學費(fee)、備用金(byj),並將這些"特徵因子"進行量化(除了locationid、fee、byj按照實際值進行量化,scool_type、zs量化標準參考上述sql語句)。

1.2 數據歸一化處理

首先了解一個概念,奇異樣本數據數據:指相對於其餘輸入樣本特別大或特別小的樣本矢量。奇異樣本數據數據的存在會引發訓練時間增大,並可能引發沒法收斂。因此在存在奇異樣本數據的狀況下,進行訓練以前最好進行歸一化,若是不存在奇異樣本數據,則能夠不用歸一化。

院校"特徵因子"具備不一樣的量綱,爲了消除指標之間的量綱影響,須要進行數據標準化處理,以解決數據指標之間的可比性。原始數據通過數據標準化處理後,各指標處於同一數量級,適合進行綜合對比評價。

本數據處理採起歸一化方式:最大—最小標準化。

最大—最小標準化是對原始數據進行線性變換,設MIN(A)和MAX(A)分別是屬性A的最小值和最大值,將A的一個原始值x經過最大—最小標準化映射到區間[0, 1]的值x’,那麼公式以下:x’ = (x - MIN(A)) / (MAX(A) - MIN(A))。

在院校信息中,school_type、zs每每取值較小,而fee、byj取值較大,量綱對數據分析時會產生必定影響,須要進行歸一化處理:

schoolId   schoolName   locationId  school_type   zs  fee  byj
歸一化前:1 諾丹姆吉本斯主教中學(Notre Dame-Bishop Gibbons School)      71     0       0    283047.0        13289.0

歸一化後:1 諾丹姆吉本斯主教中學(Notre Dame-Bishop Gibbons School)  0.693069306930693  0.0  0.0  0.4018890324907577 0.4000060201071579

 

歸一化處理sql語句示例:
create table school2_detail_number as
select
 sd.schoolid,
 sd.name schoolname,
 sl.locationid,
 (case
   when sd.type="混校" then "0"
   when sd.type="男校" then "1"
   when sd.type="女校" then "2"
 end) as school_type,
 (case
   when sd.zhusu="寄宿家庭" then "0"
   when sd.zhusu="學校宿舍" then "1"
   when sd.zhusu="男生學校宿舍/女生寄宿家庭" then "2"
   when sd.zhusu="學校宿舍/寄宿家庭" then "3"
   when sd.zhusu="學校宿舍-寄宿家庭" then "3"
 end) as zs,
 sd.fee,
 sd.byj
from
 school2_detail sd 
join 
 school2_location sl



= = = table school2_detail_number_gyh 最終歸一化結果表 = = = = 
create table school2_detail_number_gyh as
select 
 t1.schoolid,
 t1.name,
 (t1.locationid-t2.min_lid)/(t2.max_lid-t2.min_lid) as gyh_lid,
 (t1.school_type-t2.min_type)/(t2.max_type-t2.min_type) as gyh_school_type,
 (t1.zs-t2.min_zs)/(t2.max_zs-t2.min_zs) as gyh_zs,
 (t1.fee-t2.min_fee)/(t2.max_fee-t2.min_fee) as gyh_fee,
 (t1.byj-t2.min_byj)/(t2.max_byj-t2.min_byj) as gyh_byj
 from school2_detail_number t1 
 join 
 (select 
MAX(locationid) as max_lid,
MIN(locationid) as min_lid,
MAX(school_type) as max_type,
MIN(school_type) as min_type,
MAX(zs) as max_zs,
MIN(zs) as min_zs,
MAX(fee) as max_fee,
MIN(fee) as min_fee,
MAX(byj) as max_byj,
MIN(byj) as min_byj 
  from school2_detail_number) as t2;

 


2. 基於內容的推薦

2.1 基於內容推薦概述

基於內容的推薦(CB):主要是根據用戶過去喜歡的物品(item),爲用戶推薦和他過去喜歡的物品類似的物品,關鍵在於item類似度的度量。CB的過程通常包括如下三步:

1. 根據item的屬性抽取一些特徵來表示此item

2. 利用一個用戶過去喜歡(及不喜歡)的item特徵數據,來學習此用戶的喜愛特徵

3. 經過比較上一步獲得的用戶喜愛特徵與「候選」item特徵,爲此用戶推薦一組類似度較大的item

優勢:易於實現,不須要用戶數據所以不存在稀疏性和冷啓動問題;基於物品自己特徵推薦,所以不存在過分推薦熱門的問題。

缺點:抽取的特徵既要保證準確性又要具備必定的實際意義,不然很難保證推薦結果的相關性。

2.2 類似度算法描述

1. 歐幾里得距離

衡量空間各個點之間的絕對距離,跟各個點所在位置的座標直接相關。歐氏距離可以體現個體數值特徵的絕對差別,因此更多的用於須要從維度的數值大小中體現差別的分析,如使用用戶行爲指標分析用戶價值的類似度或差別。值域範圍[0,正無窮大]

2. 皮爾遜相關係數

強調的是空間中各點之間的線性相關關係。值域範圍[-1,1]。0表明無相關性,負值表明負相關,正值表明正相關

3. 餘弦類似度

衡量空間向量的夾角,主要體如今方向上的差別,而不是位置。好比A、B兩點:保持A點位置不變,B點朝原方向遠離座標軸原點,則兩者之間的餘弦距離是保持不變的(由於夾角沒有變化),但A、B兩點的距離明顯發生變化。餘弦距離更多的是從方向上區分差別,而對絕對的數值不敏感,更多的用於使用用戶對內容評分來區分興趣的類似度和差別,同時修正了用戶間可能存在的度量標準不統一的問題(由於餘弦距離對絕對數值不敏感)。值域範圍[-1,1]

2.3 數據準備和處理

同「基於Spark KMeans對院校進行聚類」中的數據準備

對於類似度算法實現,參考文章《Spark實現推薦系統中的類似度算法》

2.4 具體實現邏輯

待處理數據示例:

院校id  院校名稱  院校地址  院校地址id  學校類型  住宿方式  學費  備用金
1,諾丹姆吉本斯主教中學(Notre Dame-Bishop Gibbons School),紐約州-斯克內克塔迪,71,0,0,283047.0,13289.0
2,畢曉普馬金高中(Bishop Maginn High School),紐約州-奧爾巴尼,25,0,0,277488.0,13028.0
4,薩拉託加中央天主中學(Saratoga Central Catholic School),紐約州-薩拉託加斯普林斯,72,0,0,285705.0,13289.0
5,天主教中央中學(Catholic Central High School),紐約州-特洛伊,66,0,0,283047.0,13289.0

 

實現思路:根據上述得到院校詳細數據,提取出特徵因子,計算各個院校之間的類似度並根據類似度倒序排序,並計算每一個院校與它類似的院校取TopN存儲到reids中【注意:去掉基準院校】。

3. 基於SparkALS實現離線推薦

3.1 Spark基於模型協同過濾推薦算法ALS

Spark沒有像mahout那樣,嚴格區分基於物品的協同過濾推薦(ItemCF)和基於用戶的協同過濾推薦(UserCF)。只有基於模型的協同過濾推薦算法ALS(model-based CF)。

ALS經過數量相對少的未被觀察到的隱藏因子,來解釋大量用戶和物品之間潛在聯繫。ALS基於矩陣分解經過降維的方法來補全用戶-物品矩陣,對矩陣中沒有出現的值進行估計。

ALS基本假設:任何一個評分矩陣都可近似分解成兩個低維的用戶特徵矩陣和物品特徵矩陣。矩陣分解過程可理解成將用戶和物品均抽象的映射到相同的低維潛在特徵空間中。

3.2 具體實現邏輯

3.2.1 預處理日誌數據得到用戶對院校的「綜合分數」

經過處理採集到的日誌數據,獲得school_als.txt,示例以下:

用戶id,操做類型(瀏覽/收藏等),院校id

1,瀏覽,14

1,瀏覽,26

1,瀏覽,1

1,收藏,1

2,瀏覽,15

2,瀏覽,8

2,瀏覽,12

2,瀏覽,10

根據用戶id和院校id分組得到各個用戶對院校的操做類型的次數,從而計算最終對應院校的分值(score)【注意:這裏暫且將各個操做類型的基礎權重設爲1,瀏覽一次權重加0.1五、收藏一次權重加0.55,評論一次權重加0.3。後續具體根據業務來定具體的權重】。最終獲得的數據示例以下:

用戶id,院校id,分數,惟一標示id(方便後邊切分數據訓練ALS模型)
((1,16,1.15),16)
((1,3,1.15),87)
((1,26,2.3),88)
((1,34,1.15),154)

 

3.2.2 訓練Spark ALS模型,進行推薦

1. 準備數據

1)「歷史」綜合評分數據:將上一步處理獲得的數據,轉換成ALS的Rating格式數據,並將惟一標示id模於10(用於切分數據)

2)院校數據(school.txt)處理成(院校id -> 院校名稱)並轉成map

3)單一用戶「實時」綜合評分數據:用戶在頁面實時的行爲數據並處理成「用戶id,院校id,綜合分數」的格式

2. 切分數據

將「綜合評分數據」切分紅3個部分,60%用於訓練(加上單一用戶「實時」綜合評分數據),20%用於校驗,20%用於測試

3. 訓練模型

spark ALS訓練API主要有這幾個參數:

ratings:用戶評分數據RDD[Rating]

rank:隱因子個數,越大計算量越大,通常越精確

iterations:迭代次數

lambda:控制正則化過程,值越高正則化程度越高

計算不一樣參數下,根據訓練集得到的模型計算校驗集的RMSE(均方根偏差)RMSE最小的即爲最佳模型。

將用戶「實時」瀏覽的院校去掉,其餘院校做爲「候選」推薦院校,根據訓練的最優模型取TopN進行推薦。

4. 基於ALS和CB的業務角度分析

4.1 針對用戶是否產生實時行爲數據的不一樣處理

注意:因爲本推薦系統中基於CB的推薦,是基於院校類似度的推薦(不依賴於用戶偏好度數據),只要用戶產生瀏覽/收藏等行爲,就能基於瀏覽/收藏等的院校計算類似院校,因此不考慮新用戶「冷啓動」問題。

4.1.1 用戶產生實時行爲數據(瀏覽/收藏/評論等,設置不一樣權重)1)ALS

正常處理

2)CB

參考下方"基於CB的離線和實時推薦結果落地分析"

4.1.2 用戶沒有產生實時行爲數據

1)ALS老用戶:直接根據用戶歷史數據,按照ALS的正常處理邏輯進行處理

新用戶:能夠推薦一些熱門院校、須要推廣的院校等

2)CB參考下方"基於CB的離線和實時推薦結果落地分析"

4.2 推薦結果"落地"分析

注意:

1. 原始加載的院校數據是最基層、完整的數據(包括下線院校),因此推薦院校集要過濾掉已下線院校再進行推薦【也能夠在加載院校數據時經過sql語句過濾已下線院校,經過離線計算得到的推薦院校集也就不包含已下線院校;實時的推薦結果也會利用離線的推薦結果集因此得到推薦院校也不包含已下線院校。可是若是在離線結果已造成(當天或以前)或實時計算時下線院校更新而沒有及時更新相應的推薦數據會有必定延遲偏差】

2. 取TopN存儲到redis中,但實際推薦院校的時候只取TopN中的前幾個院校數據做爲推薦,爲了方便進行院校作CRUD處理時,redis中推薦數據的更新

3. 離線推薦結果和在線推薦結果進行彙總作最終推薦時,要過濾掉用戶已瀏覽的院校[根據業務具體需求看是否過濾掉近期已經推薦過的院校]

4. 最終推薦院校集數量可能不知足須要推薦的院校數量,能夠設置默認推薦集(如熱點院校)進行補全

4.2.1 離線結果"落地"分析

1. 基於CB的離線推薦結果"落地"

利用類似度算法,分別計算每一所院校與其餘院校的類似度(並根據類似度倒序排序) 

==> 若是沒有新增院校或已有院校屬性("特徵因子")不改變,只需計算一次。計算量:200+所院校,計算4W+次。

將每一個院校類似度計算結果,取TopN進行存儲(redis/HBase等)  

==> 如存儲到redis中:之前綴"recom:offlineCB:"和"基準院校id"拼接成的字符串爲key,以與基準院校」類似院校id:類似度」爲value("recom:offlineCB:"+baseSchoolId,"sid1:sims1,sid2:sims2,…")

2. 基於Spark ALS的離線推薦結果"落地"

因爲ALS須要訓練模型,若是每來一個用戶,產生了瀏覽/收藏等行爲,ALS模型就要實時從新訓練一次,會有必定的延遲;當用戶比較多時產生的數據量也比較大,延遲性會進一步加大。所以,採起根據前一天及以前的歷史數據,天天訓練一次ALS模型,取TopN結果進行存儲(redis/HBase等)

==> 如存儲到redis中:之前綴"recom:offlineALS:"和"用戶id"拼接成的字符串爲key,以"推薦給該用戶的院校id"爲value("recom:offlineALS:"+uId,"sid1,sid2,sid3,…")

注意:設置redis的key前綴是爲了區別不一樣推薦模型

4.2.2 實時結果"落地"分析

用戶瀏覽官網,spark-streaming從kafka消費數據進行處理,先將數據處理成標準化數據:「用戶id,操做類型,院校id」,再處理成「用戶id,院校id,score」格式。

1. 基於CB的實時推薦結果"落地"

根據院校id從Redis中獲取該院校基於CB的類似度推薦列表,自定義一個類CusItem.scala(屬性:院校id,weight),以「score*類似度」做爲最終權重weight並根據weight進行倒序排序,取TopN進行推薦(存儲到redis:以"recom:realCB:+userId"爲key,以推薦院校id列表[拼接成字符串]爲value)

2. 基於spark ALS的實時推薦結果"落地"

老用戶直接經過用戶id獲取redis中ALS離線推薦結果(存儲到redis中:以"recom:realALS:+userId"爲key,以推薦院校id列表[拼接成字符串]爲value)

4.3 基於院校"流行度"對實時和離線推薦結果的補充

主要是對熱門院校(好比點擊率高的、申請率高的)、須要推廣的院校等進行推薦。能夠根據PV、UV、日均PV或分享率等數據分析出熱門院校。可以解決新用戶冷啓動問題,同時根據不一樣的用戶特色推薦不一樣的院校

4.4 院校信息發生改變具體分析

當有「新增/刪除(包括院校下線)院校」或「已有院校屬性發生改變」時,後臺錄入對院校信息進行處理的同時,異步發送一條消息(如經過ActiveMQ,將院校id和對應的操做類型[add、delete、update])給「調整」計算院校類似度的程序,進行相應的處理。【注意:若是更新院校的屬性不是「特徵因子」,就不要發送信息了】

4.4.1 新增院校

   step1:以新增院校爲基準,計算其餘院校與該院校的類似度數據,並按照類似度數據進行倒序排序,取TopN存儲到redis【去掉基準院校】

   step2:以其餘院校爲基準,分別計算新增院校與其餘院校的類似度,用該類似度與其餘院校類似度數據中TopN院校最後一個院校的類似度數據比較,若是前者比後者小,不做任何操做;若是前者比後者大,根據TopN院校和新增院校的類似度數據進行倒序排序,去除類似度最小的,而後更新Redis中相應的數據

4.4.2 刪除院校(包括院校下線)

   step1:刪除Redis中對應刪除院校的CB類似度數據

   step2:兩種狀況 =>1)「刪除院校」不在其餘院校的TopN列表中,不更新Redis中其餘院校的類似度數據

2)「刪除院校」在其餘院校的TopN列表中,移除該院校[TopN列表移除完的狀況:實際業務場景可能性比較小,可不考慮]

4.4.3 已有院校屬性發生改變

1. 改變屬性不是"特徵因子"

不做任何處理[讓業務方那邊先判斷一下改變的屬性是否是「特徵因子」,若是不是就不要發消息了]。

特殊狀況:改變屬性是「上線狀態」,若是由下線改成上線,則調用‘新增院校’的處理方法

2. 改變屬性是"特徵因子"

step1:更新本身redis中類似度數據

step2:更新其餘院校[兩種方式:直接計算各個院校之間的類似度數據,但計算量爲200*200+;下面的方式麻煩一點但相對計算量會少不少]1) 改變院校在其餘院校redis中TopN列表計算該院校與其餘院校的類似度數據,將該值與TopN列表最後一個院校類似度數據進行比較:若是前者比後者大則TopN排序更新redis數據;若是前者比後者小則,從新計算其餘院校全部的類似度數據倒序取TopN2) 改變院校不在其餘院校redis中TopN列表

計算該院校與其餘院校的類似度數據,將該值與TopN列表最後一個院校類似度數據進行比較:若是前者比後者大,進行TopN類似度數據和改變院校類似度數據倒序排序,並移除最後一個;若是前者比後者小,不做處理。

對於上述的算法模型實現邏輯以及具體的數據處理、推薦處理等除了算法建模自己的考量,還要結合實際業務作相應調整,企業實際運用中要比上述複雜的多,包括推薦算法的種類、訓練模型、數據的標準化,業務的場景,等等。本文更可能是拋磚引玉,但願在你們作推薦系統的過程當中給出一個參考思路。

關聯文章:

Spark MLlib中KMeans聚類算法的解析和應用

Spark實現推薦系統中的類似度算法

 

Hive經常使用性能優化方法實踐全面總結

 

SparkSQL真的不支持存儲NullType類型數據到Parquet嗎?

 

經典的SparkSQL/Hive-SQL/MySQL面試-練習題

 

Spark在處理數據的時候,會將數據都加載到內存再作處理嗎?

 

關於HDFS應知應會的幾個問題

 

如何獲取流式應用程序中checkpoint的最新offset

關於一些技術點的隨筆記錄


 

關注微信公衆號:大數據學習與分享,獲取更對技術乾貨

相關文章
相關標籤/搜索