成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

推薦系統-協同過濾在Spark中的實現

移動開發
本文以經典的協同過濾為切入點,重點介紹了被工業界廣泛使用的矩陣分解算法,從理論與實踐兩個維度介紹了該算法的原理,通俗易懂,希望能夠給大家帶來一些啟發。

作者 | vivo 互聯網服務器團隊-Tang Shutao

現如今推薦無處不在,例如抖音、淘寶、京東App均能見到推薦系統的身影,其背后涉及許多的技術。

本文以經典的協同過濾為切入點,重點介紹了被工業界廣泛使用的矩陣分解算法,從理論與實踐兩個維度介紹了該算法的原理,通俗易懂,希望能夠給大家帶來一些啟發。

筆者認為要徹底搞懂一篇論文,最好的方式就是動手復現它,復現的過程你會遇到各種各樣的疑惑、理論細節。

一、 背景

1.1 引言

在信息爆炸的二十一世紀,人們很容易淹沒在知識的海洋中,在該場景下搜索引擎可以幫助我們迅速找到我們想要查找的內容。

在電商場景,如今的社會物質極大豐富,商品琳瑯滿目,種類繁多。消費者很容易挑花眼,即用戶將會面臨信息過載的問題。

為了解決該問題,推薦引擎應運而生。例如我們打開淘寶App,JD app,B站視頻app,每一個場景下都有推薦的模塊。

那么此時有一個幼兒園小朋友突然問你,為什么JD給你推薦這本《程序員頸椎康復指南》?你可能會回答,因為我的職業是程序員。

接著小朋友又問,為什么《Spark大數據分析》這本書排在第6個推薦位,而《Scala編程》排在第2位?這時你可能無法回答這個問題。

為了回答該問題,我們設想下面的場景:

在JD的電商系統中,存在著用戶和商品兩種角色,并且我們假設用戶都會對自己購買的商品打一個0-5之間的分數,分數越高代表越喜歡該商品。

基于此假設,我們將上面的問題轉化為用戶對《程序員頸椎康復指南》,《Spark大數據分析》,《Scala編程》這三本書打分的話,用戶會打多少分(用戶之前未購買過這3本書)。因此物品在頁面的先后順序就等價于預測用戶對這些物品的評分,并且根據這些評分進行排序的問題。

為了便于預測用戶對物品的評分問題,我們將所有三元組(User, Item, Rating),即用戶User給自己購買的商品Item的評分為Rating,組織為如下的矩陣形式:

圖片

其中,表格包含 m 個用戶和n個物品,將表格定義為評分矩陣 Rm×nRm×n  ,其中的元素 ru,iru,i 表示第u個用戶對第i個物品的評分。

例如,在上面的表格中,用戶user-1購買了物品 item-1, item-3, item-4,并且分別給出了4,2,5的評分。最終,我們將原問題轉化為預測白色空格處的數值。

1.2 協同過濾

協同過濾,簡單來說是利用與用戶興趣相投、擁有共同經驗之群體的喜好來推薦給用戶感興趣的物品。興趣相投使用數學語言來表達就是相似度  (人與人,物與物)。因此,根據相似度的對象,協同過濾可以分為基于用戶的協同過濾和基于物品的協同過濾。

以評分矩陣為例,以行方向觀測評分矩陣,每一行代表每個用戶的向量表示,例如用戶user-1的向量為 [4, 0, 2, 5, 0, 0]。以列方向觀測評分矩陣,每一列表示每個物品的向量表示,例如物品item-1的向量為[4, 3, 0, 0, 5]。

基于向量表示,相似度的計算有多種公式,例如余弦相似度,歐氏距離,皮爾森。這里我們以余弦相似度為例,它是我們中學學過的向量夾角 (中學只涉及2維和3維) 的高維推廣,余弦相似度公式很容易理解和使用。給定兩個向量 A={a1,?,an}A={a1,?,an} 和?B={b1,?,bn}B={b1,?,bn} ,其夾角定義如下:

圖片

例如,我們計算user-3和user-4的余弦相似度,二者對應的向量分別為 [0, 2, 0, 3, 0, 4],[0, 3, 3, 5, 4, 0]

圖片

向量夾角的余弦值越接近1代表兩個物品方向越接近平行,也就是越相似,反之越接近-1代表兩個物品方向越接近反向,表示兩個物品相似度接近相反,接近0,表示向量接近垂直/正交,兩個物品幾乎無關聯。顯然,這和人的直覺完全一致。

例如,我們在視頻App中經常能看到"相關推薦"模塊,其背后用到的原理之一就是相似度計算,下圖展示了一個具體的例子。

我們用《血族第一部》在向量庫 (存儲向量的數據庫,該系統能夠根據輸入向量,用相似度公式在庫中進行檢索,找出TopN的候選向量) 里面進行相似度檢索,找到了前7部高相似度的影片,值得注意的是第一部是自己本身,相似度為1.0,其他三部是《血族》的其他3部同系列作品。

圖片

1.2.1 基于用戶的協同過濾 (UserCF)

基于用戶的協同過濾分為兩步

  1. 找出用戶相似度TopN的若干用戶。
  2. 根據TopN用戶評分的物品,形成候選物品集合,利用加權平均預估用戶u對每個候選物品的評分。

圖片

例如,由用戶u的相似用戶{u1, u3, u5, u9}可得候選物品為

圖片

我們現在預測用戶u對物品i1的評分,由于物品在兩個用戶{u1, u5}的購買記錄里,因此用戶u對物品i1的預測評分為:

圖片

其中sim(u,u1)sim(u,u1) 表示用戶 u與用戶 u1u1的相似度。

在推薦時,根據用戶u對所有候選物品的預測分進行排序,取TopM的候選物品推薦給用戶u即可。

1.2.2 基于物品的協同過濾 (ItemCF)

圖片

基于物品的協同過濾分為兩步

  1. 在用戶u購買的物品集合中,選取與每一個物品TopN相似的物品。
  2. TopN相似物品形成候選物品集合,利用加權平均預估用戶u對每個候選物品的評分。

例如,我們預測用戶u對物品i3的評分,由于物品i3與物品{i6, i1, i9}均相似,因此用戶u對物品i3的預測評分為:

圖片

其中 sim(i6,i3)sim(i6,i3)  表示物品 i6i6  與物品 i3的相似度,其他符號同理。

圖片

1.2.3 UserCF與ItemCF的比較

我們對ItemCF和UserCF做如下總結:

UserCF主要用于給用戶推薦那些與之有共同興趣愛好的用戶喜歡的物品,其推薦結果著重于反映和用戶興趣相似的小群體的熱點,更社會化一些,反映了用戶所在的小型興趣群體中物品的熱門程度。在實際應用中,UserCF通常被應用于用于新聞推薦。

ItemCF給用戶推薦那些和他之前喜歡的物品類似的物品,即ItemCF的推薦結果著重于維系用戶的歷史興趣,推薦更加個性化,反應用戶自己的興趣。在實際應用中,圖書、電影平臺使用ItemCF,比如豆瓣、亞馬遜、Netflix等。

除了基于用戶和基于物品的協同過濾,還有一類基于模型的協同過濾算法,如上圖所示。此外基于用戶和基于物品的協同過濾又可以歸類為基于鄰域 (K-Nearest Neighbor, KNN) 的算法,本質都是在找"TopN鄰居",然后利用鄰居和相似度進行預測。

二、矩陣分解

經典的協同過濾算法本身存在一些缺點,其中最明顯的就是稀疏性問題。我們知道評分矩陣是一個大型稀疏矩陣,導致在計算相似度時,兩個向量的點積等于0 (以余弦相似度為例)。為了更直觀的理解這一點,我們舉例如下:

rom sklearn.metrics.pairwise import cosine_similarity
a = [
[ 0, 0, 0, 3, 2, 0, 3.5, 0, 1 ],
[ 0, 1, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 1, 0, 0, 0, 0, 0, 0 ],
[4.1, 3.8, 4.6, 3.8, 4.4, 3, 4, 0, 3.6]
]
cosine_similarity(a)
# array([[1. , 0. , 0. , 0.66209271],
# [0. , 1. , 0. , 0.34101639],
# [0. , 0. , 1. , 0.41280932],
# [0.66209271, 0.34101639, 0.41280932, 1. ]])

圖片

我們從評分矩陣中抽取item1 - item4的向量,并且利用余弦相似度計算它們之間的相似度。

通過相似度矩陣,我們可以看到物品item-1, item-2, item-3的之間的相似度均為0,而且與item-1, item-2, item-3最相似的物品都是item-4,因此在以ItemCF為基礎的推薦場景中item-4將會被推薦給用戶。

但是,物品item-4與物品item-1, item-2, item-3最相似的原因是item-4是一件熱門商品,購買的用戶多,而物品item-1, item-2, item-3的相似度均為0的原因僅僅是它們的特征向量非常稀疏,缺乏相似度計算的直接數據。

綜上,我們可以看到經典的基于用戶/物品的協同過濾算法有天然的缺陷,無法處理稀疏場景。為了解決該問題,矩陣分解被提出。

2.1 顯示反饋

我們將用戶對物品的評分行為定義為顯示反饋?;陲@示反饋的矩陣分解是將評分矩陣Rm×n 用兩個矩陣 Xm×k和Yn×k的乘積近似表示,其數學表示如下:

圖片

其中, k?m/nk?m/n 表示隱性因子,以用戶側來理解,?k=2k=2 表示的就是用戶的年齡和性別兩個屬性。此外有個很好的比喻就是物理學的三棱鏡,白光在三棱鏡的作用下被分解為7種顏色的光,在矩陣分解算法中,分解的作用就類似于"三棱鏡",如下圖所示,因此,矩陣分解也被稱為隱語義模型。矩陣分解將系統的自由度從 O(mn) 降到了O((m+n)k,從而實現了降維的目的。

圖片

為了求解矩陣?Xm×kXm×k 和Yn×k,需要最小化平方誤差損失函數,來盡可能地使得兩個矩陣的乘積逼近評分矩陣 Rm×nRm×n ,即

圖片

其中, λ(∑uxTuxu+∑iyTiyi)λ(∑uxuTxu+∑iyiTyi) 為懲罰項,λ為懲罰系數/正則化系數,xu表示第u個用戶的k維特征向量,yiyi  表示第 i 個物品的k維特征向量。

圖片

全體用戶的特征向量構成了用戶矩陣 ?Xm×kXm×k ,全體物品的特征向量構成了物品矩陣 Yn×k。

圖片

我們訓練模型的時候,就只需要訓練用戶矩陣中的m×k個參數和物品矩陣中的n×k個參數。因此,協同過濾就成功轉化成了一個優化問題。

2.2 預測評分

通過模型訓練 (即求解模型系數的過程),我們得到用戶矩陣Xm×k 和物品矩陣 Yn×kYn×k,全部用戶對全部物品的評分預測可以通過  Xm×k(Yn×k)TXm×k(Yn×k)T 獲得。如下圖所示。

圖片

得到全部的評分預測后,我們就可以對每個物品進行擇優推薦。需要注意的是,用戶矩陣和物品矩陣的乘積,得到的評分預估值,與用戶的實際評分不是全等關系,而是近似相等的關系。如上圖中兩個矩陣粉色部分,用戶實際評分和預估評分都是近似的,有一定的誤差。

2.3 理論推導

矩陣分解ALS的理論推導網上也有不少,但是很多推導不是那么嚴謹,在操作向量導數時有的步驟甚至是錯誤的。有的博主對損失函數的求和項理解解出現錯誤,例如

圖片

但是評分矩陣是稀疏的,求和并不會貫穿整個用戶集和物品集。正確的寫法應該是

圖片

其中,(u,i) is known(u,i) is known表示已知的評分項。

我們在本節給出詳細的、正確的推導過程,一是當做數學小練習,其次也是對算法有更深層的理解,便于閱讀Spark ALS的源碼。

將?(u,i) is known(u,i) is known 使用數學語言描述,矩陣分解的損失函數定義如下:

圖片

其中 K 為評分矩陣中已知的(u,i) 集合。例如下面的評分矩陣對應的 K為

圖片

求解上述損失函數存在兩種典型的優化方法,分別為

  • 交替最小二乘 (Alternating Least Squares, ALS)
  • 隨機梯度下降 (Stochastic Gradient Descent, SGD)

交替最小二乘,指的是固定其中一個變量,利用最小二乘求解另一個變量,以此交替進行,直至收斂或者到達最大迭代次數,這也是“交替”一詞的由來。

隨機梯度下降,是優化理論中最常用的一種方式,通過計算梯度,然后更新待求的變量。

在矩陣分解算法中,Spark最終選擇了ALS作為官方的唯一實現,原因是ALS很容易實現并行化,任務之間沒有依賴。

下面我們動手推導一下整個計算過程,在機器學習理論中,微分的單位一般在向量維度,很少去對向量的分量為偏微分推導。

首先我們固定物品矩陣 Y,將物品矩陣 Y看成常量。不失一般性,我們定義用戶u 評分過的物品集合為  IuIu,利用損失函數對向量?xuxu 求偏導,并且令導數等于0可得:

圖片

因為向量 xuxu與求和符號 ∑i∈Iu∑i∈Iu無關,所有將其移出求和符號,因為 ?xTuyiyTixuTyiyiT 是矩陣相乘 (不滿足交換性),因此 xuxu  在左邊

圖片

等式兩邊取轉置,我們有

圖片

為了化簡 ∑i∈IuyiyTi∑i∈IuyiyiT   與   ∑i∈Iuru,iyi∑i∈Iuru,iyi,我們將 ?Iu 展開。

假設Iu={ic1,?,icN} , 其中N表示用戶u評分過的物品數量,iciici表示第 cici個物品對應的索引/序號,借助于 Iu ,我們有

圖片

其中,

YIuYIu 為以?Iu={ic1,?icN}Iu={ic1,?icN} 為行號在物品矩陣 Y 中選取的N個行向量形成的子矩陣

Ru,Iu為以Iu={ic1,?icN} 為索引,在評分矩陣 R的第u 行的行向量中選取的N 個元素,形成的子行向量

因此,我們有

圖片

網上的博客,許多博主給出類似下面形式的結論不是很嚴謹,主要是損失函數的理解不到位導致的。

圖片

同理,我們定義物品 i 被評分的用戶集合為 Ui={ud1,?udM}Ui={ud1,?udM}

根據對稱性可得

圖片

其中,

?XUiXUi 為以 ?Ui={ud1,?,udM}Ui={ud1,?,udM} 為行號在用戶矩陣X中選取的M個行向量形成的子矩陣

Ri,UiRi,Ui 為以 Ui={ud1,?,udM}Ui={ud1,?,udM} 為索引,在評分矩陣 R的第i列的列向量中選取的 M個元素,形成的子列向量

此外,IkIk 為單位矩陣?

如果讀者感覺上述的推導還是很抽象,我們也給一個具體實例來體會一下中間過程

圖片

注意到損失函數是一個標量,這里我們只展開涉及到x1,1,x1,2x1,1,x1,2的項,如下所示

圖片

讓損失函數對 x1,1,x1,2x1,1,x1,2  分別求偏導數可以得到

圖片

寫成矩陣形式可得

圖片

利用我們上述的規則,很容易檢驗我們導出的結論。

總結來說,ALS的整個算法過程只有兩步,涉及2個循環,如下圖所示:

圖片

算法使用RMSE(root-mean-square error)評估誤差。

圖片

當RMSE值變化很小時或者到達最大迭代步驟時,滿足收斂條件,停止迭代。

“Talk is cheap. Show me the code.” 作為小練習,我們給出上述偽代碼的Python實現。

import numpy as np
from scipy.linalg import solve as linear_solve
# 評分矩陣 5 x 6
R = np.array([[4, 0, 2, 5, 0, 0], [3, 2, 1, 0, 0, 3], [0, 2, 0, 3, 0, 4], [0, 3, 3,5, 4, 0], [5, 0, 3, 4, 0, 0]])
m = 5 # 用戶數
n = 6 # 物品數
k = 3 # 隱向量的維度
_lambda = 0.01 # 正則化系數
# 隨機初始化用戶矩陣, 物品矩陣
X = np.random.rand(m, k)
Y = np.random.rand(n, k)
# 每個用戶打分的物品集合
X_idx_dict = {1: [1, 3, 4], 2: [1, 2, 3, 6], 3: [2, 4, 6], 4: [2, 3, 4, 5], 5: [1, 3, 4]}
# 每個物品被打分的用戶集合
Y_idx_dict = {1: [1, 2, 5], 2: [2, 3, 4], 3: [1, 2, 4, 5], 4: [1, 3, 4, 5], 5: [4], 6: [2, 3]}

# 迭代10次
for iter in range(10):
for u in range(1, m+1):
Iu = np.array(X_idx_dict[u])
YIu = Y[Iu-1]
YIuT = YIu.T
RuIu = R[u-1, Iu-1]
xu = linear_solve(YIuT.dot(YIu) + _lambda * np.eye(k), YIuT.dot(RuIu))
X[u-1] = xu
for i in range(1, n+1):
Ui = np.array(Y_idx_dict[i])
XUi = X[Ui-1]
XUiT = XUi.T
RiUi = R.T[i-1, Ui-1]
yi = linear_solve(XUiT.dot(XUi) + _lambda * np.eye(k), XUiT.dot(RiUi))
Y[i-1] = yi

最終,我們打印用戶矩陣,物品矩陣,預測的評分矩陣如下,可以看到預測的評分矩陣非常逼近原始評分矩陣。

# X
array([[1.30678487, 2.03300876, 3.70447639],
[4.96150381, 1.03500693, 1.62261161],
[6.37691007, 2.4290095 , 1.03465981],
[0.41680155, 3.31805612, 3.24755801],
[1.26803845, 3.57580564, 2.08450113]])
# Y
array([[ 0.24891282, 1.07434519, 0.40258993],
[ 0.12832662, 0.17923216, 0.72376732],
[-0.00149517, 0.77412863, 0.12191856],
[ 0.12398438, 0.46163336, 1.05188691],
[ 0.07668894, 0.61050204, 0.59753081],
[ 0.53437855, 0.20862131, 0.08185176]])
# X.dot(Y.T) 預測評分
array([[4.00081359, 3.2132548 , 2.02350084, 4.9972158 , 3.55491072, 1.42566466],
[3.00018371, 1.99659282, 0.99163666, 2.79974661, 1.98192672, 3.00005934],
[4.61343295, 2.00253692, 1.99697545, 3.00029418, 2.59019481, 3.99911584],
[4.97591903, 2.99866546, 2.96391664, 4.99946603, 3.99816006, 1.18076534],
[4.99647978, 2.31231627, 3.02037696, 4.0005876 , 3.5258348 , 1.59422188]])
# 原始評分矩陣
array([[4, 0, 2, 5, 0, 0],
[3, 2, 1, 0, 0, 3],
[0, 2, 0, 3, 0, 4],
[0, 3, 3, 5, 4, 0],
[5, 0, 3, 4, 0, 0]])

三、Spark  ALS應用

Spark的內部實現并不是我們上面所列的算法,但是核心原理是完全一樣的,Spark實現的是上述偽代碼的分布式版本,具體算法參考Large-scale Parallel Collaborative Filtering for the Netflix Prize。其次,查閱Spark的官方文檔,我們也注意到,Spark使用的懲罰函數與我們上文的有細微的差別。

圖片

其中  nu,ninu,ni分別表示用戶u打分的物品數量和物品 i 被打分的用戶數量。即

圖片

本小節通過兩個案例來了解Spark ALS的具體使用,以及在面對互聯網實際工程場景下的應用。

3.1 Demo案例

以第一節給出的數據為例,將三元組(User, Item, Rating)組織為als-demo-data.csv,該demo數據集涉及5個用戶和6個物品。

userId,itemId,rating
1,1,4
1,3,2
1,4,5
2,1,3
2,2,2
2,3,1
2,6,3
3,2,2
3,4,3
3,6,4
4,2,3
4,3,3
4,4,5
4,5,4
5,1,5
5,3,3
5,4,4

使用Spark的ALS類使用非常簡單,只需將三元組(User, Item, Rating)數據輸入模型進行訓練。

import org.apache.spark.sql.SparkSession
import org.apache.spark.ml.recommendation.ALS
val spark = SparkSession.builder().appName("als-demo").master("local[*]").getOrCreate()
val rating = spark.read
.options(Map("inferSchema" -> "true", "delimiter" -> ",", "header" -> "true"))
.csv("./data/als-demo-data.csv")
// 展示前5條評分記錄
rating.show(5)
val als = new ALS()
.setMaxIter(10) // 迭代次數,用于最小二乘交替迭代的次數
.setRank(3) // 隱向量的維度
.setRegParam(0.01) // 懲罰系數
.setUserCol("userId") // user_id
.setItemCol("itemId") // item_id
.setRatingCol("rating") // 評分列
val model = als.fit(rating) // 訓練模型
// 打印用戶向量和物品向量
model.userFactors.show(truncate = false)
model.itemFactors.show(truncate = false)
// 給所有用戶推薦2個物品
model.recommendForAllUsers(2).show()

上述代碼在控制臺輸出結果如下:

+------+------+------+
|userId|itemId|rating|
+------+------+------+
| 1| 1| 4|
| 1| 3| 2|
| 1| 4| 5|
| 2| 1| 3|
| 2| 2| 2|
+------+------+------+
only showing top 5 rows
+---+------------------------------------+
|id |features |
+---+------------------------------------+
|1 |[-0.17339179, 1.3144133, 0.04453602]|
|2 |[-0.3189066, 1.0291641, 0.12700711] |
|3 |[-0.6425665, 1.2283803, 0.26179287] |
|4 |[0.5160747, 0.81320006, -0.57953185]|
|5 |[0.645193, 0.26639006, 0.68648624] |
+---+------------------------------------+
+---+-----------------------------------+
|id |features |
+---+-----------------------------------+
|1 |[2.609607, 3.2668495, 3.554771] |
|2 |[0.85432494, 2.3137972, -1.1198239]|
|3 |[3.280517, 1.9563107, 0.51483333] |
|4 |[3.7446978, 4.259611, 0.6640027] |
|5 |[1.6036265, 2.5602736, -1.8897828] |
|6 |[-1.2651576, 2.4723763, 0.51556784]|
+---+-----------------------------------+
+------+--------------------------------+
|userId|recommendations |
+------+--------------------------------+
|1 |[[4, 4.9791617], [1, 3.9998217]]| // 對應物品的序號和預測評分
|2 |[[4, 3.273963], [6, 3.0134287]] |
|3 |[[6, 3.9849386], [1, 3.2667015]]|
|4 |[[4, 5.011649], [5, 4.004795]] |
|5 |[[1, 4.994258], [4, 4.0065994]] |
+------+--------------------------------+

我們使用numpy來驗證Spark的結果,并且用Excel可視化評分矩陣。

import numpy as np
X = np.array([[-0.17339179, 1.3144133, 0.04453602],
[-0.3189066, 1.0291641, 0.12700711],
[-0.6425665, 1.2283803, 0.26179287],
[0.5160747, 0.81320006, -0.57953185],
[0.645193, 0.26639006, 0.68648624]])
Y = np.array([[2.609607, 3.2668495, 3.554771],
[0.85432494, 2.3137972, -1.1198239],
[3.280517, 1.9563107, 0.51483333],
[3.7446978, 4.259611, 0.6640027],
[1.6036265, 2.5602736, -1.8897828],
[-1.2651576, 2.4723763, 0.51556784]])
R_predict = X.dot(Y.T)
R_predict

輸出預測的評分矩陣如下:

array([[3.99982136, 2.84328038, 2.02551472, 4.97916153, 3.0030386,  3.49205357],
[2.98138452, 1.96660155, 1.03257371, 3.27396294, 1.88351875, 3.01342882],
[3.26670123, 2.0001004 , 0.42992289, 3.00003605, 1.61982132, 3.98493822],
[1.94325135, 2.97144913, 2.98550149, 5.011649 , 4.00479503, 1.05883274],
[4.99425778, 0.39883335, 2.99113433, 4.00659955, 0.41937014, 0.19627587]])

從Excel可視化的評分矩陣可以觀察到預測的評分矩陣非常逼近原始的評分矩陣,以user-3為例,Spark推薦的物品是item-6和item-1, [[6, 3.9849386], [1, 3.2667015]],這和Excel展示的預測評分矩陣完全一致。

從Spark函數recommendForAllUsers()給出的結果來看,Spark內部并沒有去除用戶已經購買的物品。

圖片

3.2 工程應用

在互聯網場景,用戶數 m(千萬~億級別) 和物品數 n (10萬~100萬級別) 規模很大,App的埋點數據一般會保存在HDFS中,以互聯網的長視頻場景為例,用戶的埋點信息最終聚合為用戶行為表  t_user_behavior。

行為表包含用戶的imei,物品的content-id,但是沒有直接的用戶評分,實踐中我們的解決方案是利用用戶的其他行為進行加權得出用戶對物品的評分。即

rating = w1 * play_time (播放時長) + w2 * finsh_play_cnt (完成的播放次數) + w3 * praise_cnt (點贊次數) + w4 * share_cnt (分享次數) + 其他適合于你業務邏輯的指標

其中, wi為每個指標對應的權重。

圖片

如下的代碼塊演示了工程實踐中對大規模用戶和商品場景進行推薦的流程。

import org.apache.spark.ml.feature.{IndexToString, StringIndexer}
// 從hive加載數據,并利用權重公式計算用戶對物品的評分
val rating_df = spark.sql("select imei, content_id, 權重公式計算評分 as rating from t_user_behavior group by imei, content_id")
// 將imei和content_id轉換為序號,Spark ALS入參要求userId, itemId為整數
// 使用org.apache.spark.ml.feature.StringIndexer
val imeiIndexer = new StringIndexer().setInputCol("imei").setOutputCol("userId").fit(rating_df)
val contentIndexer = new StringIndexer().setInputCol("content_id").setOutputCol("itemId").fit(rating_df)
val ratings = contentIndexer.transform(imeiIndexer.transform(rating_df))
// 其他code,類似于上述demo
val model = als.fit(ratings)
// 給每個用戶推薦100個物品
val _userRecs = model.recommendForAllUsers(100)
// 將userId, itemId轉換為原來的imei和content_id
val imeiConverter = new IndexToString().setInputCol("userId").setOutputCol("imei").setLabels(imeiIndexer.labels)
val contentConverter = new IndexToString().setInputCol("itemId").setOutputCol("content_id").setLabels(contentIndexer.labels)
val userRecs = imeiConverter.transform(_userRecs)
// 離線保存供線上調用
userRecs.foreachPartition {
// contentConverter 將itemId轉換為content_id
// 保存redis邏輯
}

值得注意的是,上述的工程場景還有一種解決方案,即隱式反饋。用戶給商品評分很單一,在實際的場景中,用戶未必會給物品打分,但是大量的用戶行為,同樣能夠間接反映用戶的喜好,比如用戶的購買記錄、搜索關鍵字,加入購物車,單曲循環播放同一首歌。我們將這些間接用戶行為稱之為隱式反饋,以區別于評分對應的顯式反饋。胡一凡等人在論文Collaborative filtering for implicit feedback datasets中針對隱式反饋場景提出了ALS-WR模型  (ALS with Weighted-λ-Regularization),并且Spark官方也實現了該模型,我們將在以后的文章中介紹該模型。

四、總結

本文從推薦的場景出發,引出了協同過濾這一經典的推薦算法,并且由此講解了被Spark唯一實現和維護的矩陣分解算法,詳細推導了顯示反饋下矩陣分解的理論原理,并且給出了Python版本的單機實現,能夠讓讀者更好的理解矩陣這一算法,最后我們以demo和工程實踐兩個實例講解了Spark ALS的使用,能夠讓沒有接觸過推薦算法的同學有個直觀的理解,用理論與實踐的形式明白矩陣分解這一推薦算法背后的原理。

參考文獻:

  1. 王喆, 深度學習推薦系統
  2. Hu, Yifan, Yehuda Koren, and Chris Volinsky. "Collaborative filtering for implicit feedback datasets." 2008 Eighth IEEE International Conference on Data Mining. IEEE, 2008.
  3. Zhou, Yunhong, et al. "Large-scale parallel collaborative filtering for the Netflix prize." International conference on algorithmic applications in management. Springer, Berlin, Heidelberg, 2008.

責任編輯:未麗燕 來源: vivo互聯網技術
相關推薦

2017-02-05 21:02:44

大數據深度學習推薦系統

2017-04-27 18:09:26

item embedd推薦系統算法

2020-06-28 07:30:00

推薦算法推薦系統

2023-10-31 16:46:45

2016-08-18 01:36:14

協同過濾推薦電影用戶

2021-11-15 12:45:44

協同過濾算法架構

2024-03-11 08:00:00

位置偏差算法矩陣分解算法

2017-02-08 09:25:16

Spark分解推薦

2018-05-21 08:22:14

自編碼器協同過濾深度學習

2018-03-23 11:33:56

協同過濾程序算法

2024-02-05 09:30:10

推薦算法深度學習內容過濾

2024-07-23 08:00:00

2024-10-29 09:00:00

2019-05-05 09:00:00

數據分析算法推薦系統

2020-06-29 07:00:00

推薦算法推薦系統

2017-06-29 09:15:36

推薦算法策略

2022-10-14 08:03:46

圖形數據庫系統

2018-03-23 11:56:09

相似性推薦推薦算法推薦

2017-07-26 17:25:22

機器學習SparkAPI

2024-06-26 19:18:53

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品视频一区二区三区 | 男女午夜激情视频 | 午夜丰满寂寞少妇精品 | 日本一道本视频 | 欧洲一级毛片 | 99精品国产一区二区三区 | 伊人影院99 | 久久久亚洲一区 | 国产一区二区三区在线免费观看 | 久草视 | 久久伊人精品一区二区三区 | 亚洲国产精品久久久久 | 黄色一级视频 | 午夜精品视频在线观看 | 精品久久亚洲 | 国产一区二区三区视频 | 在线91| 日韩中文字幕免费在线观看 | 亚洲成av人片在线观看 | 狠狠亚洲 | 欧美性jizz18性欧美 | 一级片在线观看 | 99久久婷婷国产综合精品首页 | 日本特黄a级高清免费大片 成年人黄色小视频 | 日韩在线观看视频一区 | 精品久久网| 欧美成人激情 | 亚洲一区二区三区在线 | 天天爽天天干 | 国产精品毛片一区二区在线看 | 国产精品久久久久国产a级 欧美日韩国产免费 | 中文字幕一区在线 | 国产视频2021 | 91.com在线观看 | 亚洲美女一区二区三区 | 蜜臀网站 | 欧美在线视频一区二区 | 亚洲三区视频 | 亚洲国产aⅴ成人精品无吗 综合国产在线 | 亚洲国产91| 成人免费影院 |