推荐系统的召回算法(一)—— 协同过滤法(基于用户)
??姍姍來遲的第二篇博客,最近在了解有關推薦系統方面的基本知識和算法,先總結其中一類經典常用的算法——協同過濾法。網上已有很多介紹其原理的好文章,所以本文用較多篇幅來寫一些自身對算法實現的理解和疑惑,希望有經驗的小伙伴能夠解惑,感激不盡!
??寫的比較倉促,如有描述或理解錯誤,敬請指出,望共同進步~
目錄
- 為何做推薦系統
- 召回和排序
- 協同過濾法—基于用戶(UserCF)
- UserCF原理
- 實現過程
- Python代碼
- 結語
為何做推薦系統
??先介紹下推薦系統中經常出現的兩個術語:User 和 Item,即用戶和物品,這里的物品可以是書籍、電影、新聞、視頻等。
??什么情況下需要推薦系統?——信息過載且用戶沒有明確的需求。簡單理解信息過載就是Item數量非常多,用戶一時半會兒不知道想要什么,這時候推薦系統就可以主動向用戶提供他們可能感興趣的物品。所以在做推薦系統前需要了解兩件事兒:
(1)平臺上Item的數量是否過多;如果數量不大,可以用分類目錄的形式對Item進行分門別類,更方便用戶找到適合自己的Item.
(2)用戶每次使用平臺時,自身的需求是否明確.
??以抖音app為例子,里面的短視頻千千萬,用戶一般并不清楚自己具體想看什么,推薦就成了留住用戶的最好方式,所以抖音的主界面就是個推薦場景(其實還有個搜索界面);還有淘寶app和云音樂app等,里面的推薦場景隨處可見。
召回和排序
??回歸正題,推薦系統一般包含兩個算法部分:召回階段(recall)和 排序階段(rank)。先說排序階段的作用,也是傳統機器學習、深度學習在推薦系統中發揮威力的地方,它們可以預測用戶對物品的點擊率、轉化率等(類似廣告中CTR模型),再以特定的規則進行排序后展示給用戶,更符合規則的物品做優先展示。但是不可能把所有的物品一次性喂入排序模型做預測排序(數量太多,算完用戶都跑了),所以召回階段的作用就是把所有物品做初步的篩選形成一個物品候選集,再進入排序階段。
??排序階段里往往都是高大上的模型,似乎直接決定推薦結果,其實召回階段產生的Item候選集也會對最終推薦結果產生很大影響。比如小明購買過一個水杯,然后召回階段產生的候選集幾乎由杯子這一類的物品組成,導致排序階段輸出的Top-N物品全是五花八門的杯子。。。顯然這樣的召回策略缺乏多樣性,使得排序模型無論多精確都無法擺脫只推薦杯子的結局。
??本文重點介紹召回階段最常見的一類算法——協同過濾法(Collaborative Filtering),做推薦的小伙伴們都應該很熟悉吧。相比ML、DL這些模型算法,CF幾乎沒有什么深奧的數學原理,注重的是背后原理和思想。
協同過濾法—基于用戶(UserCF)
??協同過濾法屬于召回階段的算法,簡單有效的特點使其仍未過時,現有的各大推薦場景中都能看到這類算法的影子?;舅枷刖褪腔?strong>用戶或物品的相似度做推薦,下面就介紹其中一種。
UserCF原理
??基于用戶的協同過濾法,基本思想是尋找用戶A的相似用戶B,把用戶B接觸過的物品b推薦給用戶A。借鑒《推薦系統實踐》一書中的說法,該算法主要包括兩個步驟:
??(1)找到和目標用戶A興趣相似的用戶集合;
??(2)找到這個集合中的用戶喜歡的,且目標用戶A沒有發生行為的物品推薦給目標用戶A。
??如何衡量用戶的相似度呢?首先確定計算相似度所需的用戶數據來源,我第一反應就是用戶的畫像數據(性別、身高、收入等),但是仔細想想畫像數據并不能完全反映一個用戶在平臺里的興趣偏好。相比之下,用戶在平臺上的行為數據更能體現其在平臺中的興趣偏好,也更適用于計算用戶相似度,比如抖音中的點贊、頁面停留時長;淘寶中的瀏覽、加購、購買、評分等行為數據。
??其次確定采用何種相似度指標,以下列舉幾個常見的相似度計算公式:
??(一)對于停留時長、評分等連續型行為數據
??假設用戶a和用戶b對所有Item的評分(停留時長)向量分別為 A 和 B,其中A=(a1,a2,...,an)A=(a_{1},a_{2},...,a_{n})A=(a1?,a2?,...,an?), B=(b1,b2,...,bn)B=(b_{1},b_{2},...,b_{n})B=(b1?,b2?,...,bn?),則有:
余弦相似度
cos?A,B?=A?B∣∣A∣∣×∣∣B∣∣=∑i=1naibi∑i=1n(ai)2×∑i=1n(bi)2cos\left \langle A,B \right \rangle={\frac {A \cdot B} {||A||\times||B||}}={\frac {\sum_{i=1}^{n}a_{i} b_{i}} {\sqrt {\sum_{i=1}^{n}\left (a_{i} \right )^2} \times \sqrt{\sum_{i=1}^{n}\left (b_{i} \right )^2}}}cos?A,B?=∣∣A∣∣×∣∣B∣∣A?B?=∑i=1n?(ai?)2?×∑i=1n?(bi?)2?∑i=1n?ai?bi??
??最初用于文本相似度的計算。從二維和三維的角度來看,該指標就是兩個向量間夾角的余弦值,取值范圍為[0,1],越接近1說明兩個向量越相似。
??公式中分母做了類似標準化(去量綱)的操作,圖中可看出該指標以向量間的夾角來衡量相似度,所以忽略了向量本身的模長。
歐式距離(Euclidean)
d(A,B)=(a1?b1)2+(a2?b2)2+...+(an?bn)2=(∑i=1n∣ai?bi∣2)1/2d(A,B)=\sqrt {(a_{1}-b_{1})^2+(a_{2}-b_{2})^2+...+(a_{n}-b_{n})^2}=(\sum_{i=1}^{n} {|a_{i}-b_{i}|^2})^{1/2}d(A,B)=(a1??b1?)2+(a2??b2?)2+...+(an??bn?)2?=(i=1∑n?∣ai??bi?∣2)1/2
??歐氏距離是非常直觀的距離指標,屬于閔可夫斯基距離(Minkowski)的一種特殊形式,類似的還有曼哈頓距離(Manhattan)、切比雪夫距離(Chebyshev),這些距離指標在K-means、KNN等算法中一般都有詳細介紹。
??可將歐氏距離做變型:11+d(A,B)\frac {1} {1+d(A,B)}1+d(A,B)1?,用該值衡量A,B向量的相似度更合適。相比余弦相似度,歐氏距離從向量各個分量間的差值體現兩者距離(差異)。
Pearson相關系數
cor(A,B)=E[(A?μA)(B?μB)]σA?σB=1n∑i=1n(ai?aˉ)(bi?bˉ)1n∑i=1n(ai?aˉ)2×1n∑i=1n(bi?bˉ)2cor\left( A,B\right)=\frac {E[(A-\mu_{A})(B-\mu_{B})]} {\sigma_{A}\cdot\sigma_{B}}=\frac {\frac {1} {n}\sum_{i=1}^{n}{(a_{i}-\bar{a})(b_{i}-\bar)}} {\sqrt{\frac {1} {n}\sum_{i=1}^{n}{(a_{i}-\bar{a})^2}}\times\sqrt{\frac {1} {n}\sum_{i=1}^{n}{(b_{i}-\bar)^2}}}cor(A,B)=σA??σB?E[(A?μA?)(B?μB?)]?=n1?∑i=1n?(ai??aˉ)2?×n1?∑i=1n?(bi??bˉ)2?n1?∑i=1n?(ai??aˉ)(bi??bˉ)?
其中aˉ=1n∑i=1nai\bar{a}=\frac {1} {n}\sum_{i=1}^{n}{a_{i}}aˉ=n1?∑i=1n?ai?,bˉ=1n∑i=1nbi\bar=\frac {1} {n}\sum_{i=1}^{n}{b_{i}}bˉ=n1?∑i=1n?bi? 。
??這是統計學中常用的相關性指標,用于描述兩個變量間的線性相關性。在余弦相似度的基礎上,對分子分母均做中心化(去均值)處理,所以也被稱為調整余弦相似度。
??個人對采用這個指標有些疑惑:首先相關系數取值為[-1,1],如果cor(A,B)<0cor(A,B)<0cor(A,B)<0,說明用戶A和用戶B對item的某行為數據呈負相關關系,假設該行為是瀏覽時長,然后把用戶B瀏覽時長短的item推給用戶A?還是只截取與用戶A相關系數為正的用戶,作為推薦給A的相似用戶集?
??(二)對于購買、瀏覽等離散型行為數據
??假設用戶A和用戶B的購買(瀏覽等)物品集合分別為N(a)和N(b),則有:
Jaccard相似度
wAB=∣N(a)∩N(b)∣∣N(a)∪N(b)∣w_{AB}=\frac {|N(a) \cap N(b)|} {|N(a) \cup N(b)|}wAB?=∣N(a)∪N(b)∣∣N(a)∩N(b)∣?
余弦相似度
wAB=∣N(a)∩N(b)∣∣N(a)∣∣N(b)∣w_{AB}=\frac {|N(a) \cap N(b)|} {\sqrt{|N(a)||N(b)|}}wAB?=∣N(a)∣∣N(b)∣?∣N(a)∩N(b)∣?
??以上兩個指標中,冷門物品和熱門物品對用戶相似度的權重是相同的,但實際上兩個用戶對冷門物品的行為更能說明他們興趣的相似度——《推薦系統實踐》,所以John S. Breese提出了一種改進的相似度指標。
改進的相似度
wAB=∑i∈N(a)∩N(b)1log(1+∣N(i)∣)∣N(a)∣∣N(b)∣w_{AB}=\frac {\sum_{i \in N(a)\cap N(b)}^{} \frac {1} {log\left (1+\left |N(i)\right | \right)}} {\sqrt {|N(a)||N(b)|}}wAB?=∣N(a)∣∣N(b)∣?∑i∈N(a)∩N(b)?log(1+∣N(i)∣)1??
??其中N(i)N(i)N(i)為對物品i有過購買(瀏覽等)行為的用戶集,1log(1+∣N(i)∣)\frac {1} {log(1+|N(i)|)}log(1+∣N(i)∣)1?表示物品i對相似度的影響,不難發現物品的購買人群越多(越熱門),其對相似度的影響越小,反之冷門物品的影響越大;所以改進后的指標更能刻畫用戶間的個人偏好相似度。
本節總結和思考
??1、一般平臺埋點數據返回的行為字段有很多,可以計算用戶間多個行為的相似度w1、w2...wNw_{1}、w_{2}...w_{N}w1?、w2?...wN?,比如購買行為的相似度、瀏覽時長的相似度等等,那么如何整合這些相似度來得到最終的候選集呢?
(a)利用每個相似度指標wi,i∈1,2,...Nw_{i},i \in {1,2,...N}wi?,i∈1,2,...N,產生NNN個候選集,取并集作為召回階段最終輸出的候選集;
(b)根據實際業務對每個相似度賦予合適的權重ri∈[0,1]r_{i}\in [0,1]ri?∈[0,1],比如認為購買行為比瀏覽行為更反應出用戶的興趣偏好,那么用購買行為數據算出的相似度指標權重應該更大,再通過加權平均的方式w=∑i=1nriwiw=\sum_{i=1}^{n}{r_{i}w_{i}}w=∑i=1n?ri?wi?作為最終用戶間的相似度,然后再得到輸出的候選集.
【第一種得到的候選集感覺更偏重多樣性,第二種做法中如果各權重定的好,找相似用戶似乎更準確;所以不知道用哪種方式整合兩種相似度,或者說實際應用中還有更好的方案.】
??2、各類指標各有優劣,可根據對具體業務的理解進行取舍,如果不同指標對最終的推薦結果影響不大,優先采取計算時間短的。(對于高維稀疏向量,余弦相似度的計算速度應該比較快.)
??3、實際中離散型的行為數據應該更常見吧,業務平臺初期可能沒有太多的行為數據埋點,但肯定有最基本的交易日志數據(買賣交易平臺),就可以從購買行為來計算用戶的相似度,后期再增加新的行為埋點數據。
??4、冷啟動問題,計算相似度的前提是用戶發生過行為,所以對于新用戶而言,該算法暫時會失效。列舉兩種常見的解決辦法:
(a)直接向新用戶推送近期的熱門物品。畢竟受大部分用戶喜愛的物品能大概率激發新用戶的興趣,或者說被新用戶厭惡的概率很低,但顯然缺乏個性化;
(b)當新用戶第一次進入平臺時,讓其選擇自身感興趣的Item類別。我記得CSDN就有這個功能,給出了Java開發、人工智能等幾個選項,然后根據新用戶的選項做博客推送。
【也有很多平臺會接入第三方的數據庫得到新用戶在其他平臺(如vx、微博等)的畫像數據,用于解決冷啟動問題?!?/p>
實現過程
??推薦系統需要具備一定的實時性,即用戶進入業務平臺后會立即給出對應的推薦結果,或者推薦結果隨著用戶的行為時時更新。比如在淘寶app中用戶剛瀏覽過某物品后,類似的商品下一秒就出現在推薦列表中,用戶不僅覺得體驗很棒,還會驚嘆這個系統好 “ 智能 ”的樣子 。
??為了盡量實現上述流程的實時性,一般會把算法中涉及到的復雜對象提前計算好,需要時再做調用可大大降低在線的推薦耗時。
用戶物品倒排表
??當給定一個用戶id時,如何快速獲取其近期發生過行為的Item集合?每次都用SQL語句獲取感覺效率不高,所以需提前離線構建一個用戶物品倒排表。
用戶相似矩陣
??輸入用戶id,都要獲取該用戶與所有其他用戶的相似度,為避免大量重復的計算,可以離線計算好用戶間的相似度矩陣,并設置合適的時間間隔進行更新。
步驟:
離線部分:
(1)構建用戶物品倒排表并進行存儲;
(2)計算好用戶的相似度矩陣并進行存儲;
在線部分:
輸入:用戶A的唯一id
(1)根據相似度矩陣找出用戶A對應的其他用戶相似度,排序并截取前k個相似用戶id;
(2)利用用戶物品倒排表找出k個相似用戶的物品集;
(3)對(2)中得到的物品集做進一步過濾,如刪除A已購的物品、已下架的物品等,得到最終候選集a;
輸出:物品候選集a
Python代碼
數據說明
??代碼所用數據是隨機產生的成交數據,字段包括用戶id(user_id)和物品id(item_id),所以只有一種行為數據。數據包含5000+個不同用戶,2100+個不同物品,成交記錄一共有8w條?!緮祿悬c簡潔,畢竟本文重點是了解算法應用的細節…】
用戶物品倒排表(user_item.py)
??這里我用字典(dict)來儲存用戶物品倒排表,形如:{‘user 1’:[‘item 1’,‘item2’,…],‘user 2’:[‘item 2’,‘item 3’,…],…} 。
用戶相似矩陣(user_similar.py)
??代碼里用了離散行為數據情況下的余弦相似度公式,以下兩類情況把相似度直接標0:(1)多數用戶間的已購物品無交集;(2)用戶間的相似度為1,說明兩者的已購物品集完全相同,沒有可以互相推薦的物品,后續通過對相似度進行排序查找相似用戶時應該排除此類用戶,所以提前把這類相似度標0。
【說實話,代碼中對用戶id做遍歷的兩層for循環讓我有點慌了,一時半會兒也想不到優化的方法…】
計算推薦物品候選集(User_CF.py)
??這里將UserCF寫成函數的形式進行調用,即輸入一個用戶后返回相應的推薦物品候選集?!緫弥鞒绦驅懗山涌谑遣皇歉奖闫渌Z言的調用?】
注:以上代碼分文件跑會報錯!
本節總結和思考
??1、顯然在主文件User_CF.py中,用戶物品倒排表user_item和用戶相似度矩陣user_similar作為其全局變量參與其中,那么3個文件的變量如何共享呢?
【目前本人只想到利用數據庫存儲和讀取user_item和user_similar的方式進行共享。但是用戶物品倒排表是字典的形式,不知道轉化成什么樣的表格形式存入數據庫?主程序讀取后還要轉回成字典形式,想想都覺得麻煩…】
??2、應用程序文件User_CF.py如何實現實時計算?
(a)把所有用戶的候選集提前算好存入數據庫,然后Java實時調用做推薦展示(暫時忽略排序階段);
(b)把python代碼寫成接口供Java調用,有需求時才調用計算推薦結果.
【第一種方案其實不是實時計算,提前一次性算好,而且感覺資源浪費,可能大部分用戶的推薦結果沒用到;第二種方案看上去更理想,不過對算法代碼的優化要求貌似更高,畢竟要控制計算耗時在ms級別,所以上線時一般會采用哪種方式呢或者有更好的方法?】
??3、用戶物品倒排表和用戶相似度矩陣能否做到實時更新?
【很多文章中都提到相似度矩陣是離線計算的,這就意味著如果用戶相似度矩陣不更新,UserCF計算得到的Item候選集就不會變,所以不能只用UserCF做召回?!?/p>
結語
??有很多指標可以評測召回算法的好壞,比如召回率、覆蓋率、準確率等,本文未作詳細介紹。最近處于剛接觸項目階段,所以文中內容更偏重算法的落地工程方面,沒有細究算法的質量和數學原理。
??另外文中很多本人沒想明白的地方希望有小伙伴能解惑,感激不盡!下一篇會介紹另一種常見的CF算法——基于Item的協同過濾,日期不定…
總結
以上是生活随笔為你收集整理的推荐系统的召回算法(一)—— 协同过滤法(基于用户)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python】【setFocus】焦点
- 下一篇: 初中教资计算机考试知识点,教资考试初中物