推薦系統—基于物品的協同過濾(一)
前言
最近在工作當中接到一個關于推薦系統相關的需求,于是對推薦系統這一塊進行了學習和調研,于是想對推薦系統做一些記錄和總結。
什麼是推薦系統
随着移動網際網路的發展,我們進入了資訊爆炸的時代,面對海量的資訊使得使用者的選擇變得困難,同時使用者的需求也變得不那麼明确。如何在使用者需求不明确的情況下,從海量的曆史資料中尋找使用者感興趣的資訊,成為了推薦系統需要解決的主要問題。推薦系統的本質是在使用者需求不明确的情況下,通過機器學習、深度學習技術結合使用者曆史資料建構興趣模型,為使用者提供精準的個性化推薦幫助使用者減少使用者搜尋成本,提高資源配置效率。推薦系統也被稱為網際網路的“增長引擎”。
出現的原因
資訊過載;
個性化表達的需要;
特定場景下,個人對自己的需求不是那麼明顯。
作用意義
使用者角度(使用者體驗優化):推薦系統解決使用者在“資訊過載”的情況下,使用者如何高效獲得感興趣資訊的問題。
公司角度(公司商業利益):最大限度地吸引使用者、留存使用者、增加使用者黏性、提高使用者轉化率,進而達到商業目标連續增長、增加公司收益(新聞類:點選率,電商類:購買轉換率,視訊類:觀看時長等)。
發展曆程
協同過濾、邏輯回歸<2010年之前> =》因子分解、梯度提升樹、組合模型 =》深度學習推薦模型<2015年之後>。
邏輯架構
在已經擷取的“使用者資訊”、“物品資訊”,“場景資訊”資料基礎上,對于使用者U(user)在一個特定場景C(context)下,針對海量的物品資訊,建構一個函數f(U, I, C),用這個函數f預測使用者對特定候選物品I(items)的喜好程度,然後對喜好程度進行排序,選取喜好程度最大的Top(n)個候選物品作為推薦清單展示給使用者。
相關算法
協同過濾(Collaborative Filtering) 是一種在推薦系統廣泛使用的技術,2003年由Amazon發表的論文中提出,并迅速成為業界主流的推薦模型,盡管今天已經在推薦系統中引入了深度學習,但模型的基本原理還是沒有脫離經典協同過濾的思路。該技術主要通過分析使用者或者事務之間的相似性,來預測使用者可能感興趣的内容并将此内容推薦給使用者。這裡的相似可以是人口特征的相似,也可以是曆史浏覽人内容的相似,還可以是一個人通過一定機制給與某個事務的回應。假如有兩個人A和B是很好的朋友,并且都是人工智能方面的研發者,他們都喜歡看書,那麼協同過濾會認為他們兩個的相似度很高,會将A喜歡看的書而B沒有看的書推薦給B。
協同過濾的算法分以下幾種:
a.基于内容的協同過濾(ItemCF思想精髓物以類聚);
b.基于使用者的協同過濾(UserCF思想精髓人以群分);
c.矩陣分解模型LFM(Latent Factor Model,加入了隐向量的概念)。
邏輯回歸(Logistic Regression, LR) 協同過濾隻是應用使用者和物品之間的顯示資訊或者隐式資訊,LR可以利用和融合更多使用者、物品的上下文特征。LR模型是一種寬而不深的結構,模型優點是簡單、可控性好,但是效果的好壞直接依賴特征工程的程度,需要非常精細的連續型、離散型、實踐型等特征。
邏輯回歸的算法有以下幾種:
a.邏輯回歸(Logistic Regression,LR)
b.大規模分片線性模型(Large Scale Piece-wise Linear Model, LS-PLM)
因子分解機模型(Factorization Machines, FM) 考慮了特征間的交叉,對所有嵌套變量互動進行模組化,是以相比LR模型FM在資料非常稀疏的情況下,依然可以估計出可靠參數進行預測。此外FM也可以用線性時間來計算,同時還能夠與許多優秀的協同過濾方法相融合。FM在推薦系統和廣告領域關注的點選率(Click-through Rate,CTR)和轉化率(Conversion Rate,CVR)兩項名額上有着良好的表現。
FM模型算法有以下兩種:
a.因子分解機(Factorization Machines, FM)
b.域感覺因子分解機(Field-aware Factorization Machine,FFM)通過加入域的概念,進一步加強了FM的特征交叉的能力
組合模型 為了融合多個模型的優點,将不同的模型組合使用。像Facebook提出的梯度提升決策樹(Gradient Boosting Decision Tree,GBDT)+ 邏輯回歸模型(Logical Logistic Regression)是目前業界影響力最大的組合方式,還有結合深度學習算法的DeepFM和處于探索階段的NFM将FM進行神經網絡化嘗試等等。
協同過濾
協同大家的回報、評價和意見,一起對海量資訊進行過濾,從中篩選出目标使用者可能感興趣的資訊的推薦過程。通過研究搜集具有類似偏好或屬性的使用者資料來主動向使用者推薦最需要的資源。通用做法是收集和維護關于使用者喜好的資料,然後通過算法從資料中分析出具有相似口味的使用者,最後根據要推薦使用者的相似使用者的喜好向其推薦商品即可。
基于物品的協同過濾算法
本文主要是介紹基于物品的協同過濾算法的原理及代碼實作。ItemCF是目前業界使用最廣泛,同時也是最容易落地的算法之一,像亞馬遜、YouTube他們的推薦算法基礎都是基于ItemCF的。假設你是一個戶外運動愛好者,你在某個App上面買了一個登山杖,然後App給你推薦了一雙登山鞋,實作這樣一套邏輯的背後就是ItemCF在運作。在你購買登山杖的時候,ItemCF通過計算與登山杖相似物品的相似度,最後發現登山鞋與登山杖最相似,最後ItemCF把登山鞋推薦給了你。
根據上面的例子可知ItemCF算法的大緻思路如下,首先計算出物品的相似度,然後根據相似度的大小和使用者曆史行為資料生成推薦清單。那麼ItemCF是如何計算物品間的相似度,物品的形狀?大小?材質?它的工作原理又是怎樣的呢?
ItemCF的算法原理
當然ItemCF算法并不是直接去根據物品的屬性去直接去計算物品之間的相似度的,而是通過使用者的行為間接計算物品之間的相似度。我們換個角度看,假設很多使用者在買登山杖的同時也買了登山鞋,我們就可以認為這兩個物品非常相似,進而得到如下的計算相似度的模型(1):
W μ v = ∣ N ( μ ) ⋂ N ( v ) ∣ ∣ N ( μ ) ∣ (1) \tag{1}\Large{W_{\mu v}=\frac{|N(\mu)\bigcap{N(v)}|}{|N(\mu)|}} Wμv=∣N(μ)∣∣N(μ)⋂N(v)∣(1)
模型(1)中分子部分|N(μ)∩N(v)|表示的含義是同時購買了物品μ和v的使用者數量,如果購買了μ的同時都購買了v那麼物品μ和v的相似度為1,分母中|N(μ)|代表的是購買了商品μ的使用者總數。那麼模型(1)還存在什麼問題呢?我們假設商品v是一個非常熱門的一個商品,這個時候v和任何商品相似度都會很高,進而導緻ItemCF算法總是給使用者推薦熱門商品,是以我們需要對模型(1)進行優化,進而得到如下模型(2):
W μ v = ∣ N ( μ ) ⋂ N ( v ) ∣ ∣ N ( μ ) ∣ ∣ N ( v ) ∣ (2) \tag{2}\Large{W_{\mu v}=\frac{|N(\mu)\bigcap{N(v)}|}{\sqrt{|N(\mu)||N(v)|}}} Wμv=∣N(μ)∣∣N(v)∣
∣N(μ)⋂N(v)∣(2)
模型(2)中在母部分做了變更,分母中|N(μ)||N(v)|表示購買了商品μ的使用者數和購買了商品v的使用者數的乘積。模型(2)對物品v加了懲罰,當N(v)比較大時分母也會比較大,進而降低了熱門物品與其他物品相似的可能性。但是模型(2)依然存在一些問題,假設有一個使用者他是賣服裝的,他在App上買了200萬件服裝用于售賣,假設App平台有230萬件服裝,那麼這200萬件服裝之間就産生了相似度,進而導緻影響相似度的結果。我們可以認為購買物品的較多使用者比購買物品較少的使用者對相似度的貢獻要低,進而提出如下模型:
W μ v = ∑ i ∈ N ( μ ) ⋂ N ( v ) 1 l o g ( 1 + ∣ N ( i ) ∣ ) ∣ N ( μ ) ∣ ∣ N ( v ) ∣ (3) \tag{3}\Large{W_{\mu v}=\frac{\sum \limits_{i{{\in}N(\mu)\bigcap{N(v)}}}{}\frac{1}{log(1 + |N(i)|)}}{\sqrt{|N(\mu)||N(v)|}}} Wμv=∣N(μ)∣∣N(v)∣
i∈N(μ)⋂N(v)∑log(1+∣N(i)∣)1(3)
模型(3)中在模型(2)的基礎上對分子做了變更,N(μ)∩N(v)表示同時購買了μ和v的使用者的集合,N(i)代表使用者i購買的所有物品的總數,然後分别累加求和。最終我們得到優化的相似度計算模型,模型(3)被稱作IUF(Inverse User Frequence) 算法。
得到相似度計算模型後,我們要做的就是從多個相似的物品中挑選使用者最感興趣的物品。ItemCF主要是通過模型計算出使用者對物品的感興趣程度,計算模型如下:
P ( u , μ ) = ∑ v ∈ S ( μ , K ) ⋂ N ( u ) W μ v R u v \Large{P(u,\mu)=\sum_{v{\in}S(\mu,K){\bigcap}{N(u)}}W_{{\mu}v}R_{{u}v}} P(u,μ)=v∈S(μ,K)⋂N(u)∑WμvRuv
上式中N(u)表示使用者u喜歡的物品集合,S(μ,K)代表的是和物品μ最相似的k個物品的集合,Wμv表示物品μ和v的相似度,Ruv表示使用者u對物品v的興趣(假設購買過的Ruv=1,未購買過的Ruv=0)。最後根據P(u,μ)的大小進行逆序排列,取TopN作為推薦清單推薦給使用者。
相似度歸一化
假設現在有兩類商品A和B,A類物品之間的相似度為0.5,B類物品之間的相似度為0.7,A和B之間的相似度為0.2。在這樣一個前提下如果一個使用者喜歡了5個A類物品和5個B類物品,如果隻推薦5個商品的話,由于B類之間的相似度很大,最終通過ItemCF算法推薦的物品都是B類物品。但是如果對相似度進行歸一化之後,A類物品之間的相似度變成了1,B類物品之間的相似度也變成了1,這種情況下ItemCF算法推薦的A類物品和B類物品大緻相等。是以,通過歸一化可以提高推薦物品的多樣性,提高推薦系統的覆寫率。歸一化公式如下:
W i j ′ = w i j max j ( w i j ) \Large{W_{ij}' = \frac{w_{ij}}{\max \limits_{j}(w_{ij})}} Wij′=jmax(wij)wij
ItmeCF算法工作流程
我們假設有資料集如下,以cosine相似度計算為例:
物品 | |||||
使用者 | a | b | c | d | e |
U1 | 線性代數(a) | 機率論(b) | |||
U2 | 深度學習(c) | Python程式設計(e) | |||
U3 | 深度學習(c) | 數學之美(d) | |||
U4 | 機率論(b) | 數學之美(d) | Python程式設計(e) | ||
U5 | 線性代數(a) | 數學之美(d) |
a.統計使用者購買的清單
{U1: {‘線性代數’, ‘機率論’}, U2: {‘深度學習’, ‘Python程式設計’}, U3: {‘深度學習’, ‘數學之美’}, U4: {‘機率論’, ‘數學之美’, ‘Python程式設計’}, U5: {‘線性代數’, ‘數學之美’}}
b.統計每個物品購買的使用者數
{‘線性代數’: 2, ‘機率論’: 2, ‘深度學習’: 2, ‘數學之美’: 3, ‘Python程式設計’: 2}
c.求解整體共現矩陣
求解整體共現矩陣過程 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
d.計算相似矩陣
W a d = W d a = ∣ N ( a ) ⋂ N ( d ) ∣ ∣ N ( a ) ∣ ∣ N ( d ) ∣ = 1 2 ∗ 3 = 1 6 = 0.4 \Large{W_{ad} = W_{da} = \frac{|N{(a)}\bigcap{N(d)}|}{\sqrt{|N(a)||N(d)|}} = \frac{1}{\sqrt{2 * 3}} = \frac{1}{\sqrt{6}}} = 0.4 Wad=Wda=∣N(a)∣∣N(d)∣
∣N(a)⋂N(d)∣=2∗3
1=6
1=0.4
W b d = W d b = ∣ N ( b ) ⋂ N ( d ) ∣ ∣ N ( b ) ∣ ∣ N ( d ) ∣ = 1 2 ∗ 3 = 1 6 = 0.4 \Large{W_{bd} = W_{db} = \frac{|N{(b)}\bigcap{N(d)}|}{\sqrt{|N(b)||N(d)|}} = \frac{1}{\sqrt{2 * 3}} = \frac{1}{\sqrt{6}}} = 0.4 Wbd=Wdb=∣N(b)∣∣N(d)∣
∣N(b)⋂N(d)∣=2∗3
1=6
1=0.4
W c e = W e c = ∣ N ( c ) ⋂ N ( e ) ∣ ∣ N ( c ) ∣ ∣ N ( e ) ∣ = 1 2 ∗ 2 = 1 4 = 0.5 \Large{W_{ce} = W_{ec} = \frac{|N{(c)}\bigcap{N(e)}|}{\sqrt{|N(c)||N(e)|}} = \frac{1}{\sqrt{2 * 2}} = \frac{1}{\sqrt{4}}} = 0.5 Wce=Wec=∣N(c)∣∣N(e)∣
∣N(c)⋂N(e)∣=2∗2
1=4
1=0.5
W c d = W d c = ∣ N ( c ) ⋂ N ( d ) ∣ ∣ N ( c ) ∣ ∣ N ( d ) ∣ = 1 2 ∗ 3 = 1 6 = 0.4 \Large{W_{cd} = W_{dc} = \frac{|N{(c)}\bigcap{N(d)}|}{\sqrt{|N(c)||N(d)|}} = \frac{1}{\sqrt{2 * 3}} = \frac{1}{\sqrt{6}}} = 0.4 Wcd=Wdc=∣N(c)∣∣N(d)∣
∣N(c)⋂N(d)∣=2∗3
1=6
1=0.4
e.計算使用者對物品感興趣程度,以使用者U3為例,U3使用者購買了{深度學習(c),數學之美(d)},查找相似矩陣與用c相似的物品有{數學之美(d), Python程式設計(e)},與d物品相似的有{線性代數(a), 機率論(b), 深度學習(c), Python程式設計(e)},除去c和d最終推薦清單為{a, b, e}
P ( U 3 , a ) = W c a R c a + W d a R d a = W d a = 0.4 \Large{P({U3,a}) = W_{ca}R_{ca} + W_{da}R_{da}} = W_{da} = 0.4 P(U3,a)=WcaRca+WdaRda=Wda=0.4
P ( U 3 , b ) = W c b R c b + W d b R d b = W d b = 0.4 \Large{P({U3,b}) = W_{cb}R_{cb} + W_{db}R_{db}} = W_{db} = 0.4 P(U3,b)=WcbRcb+WdbRdb=Wdb=0.4
P ( U 3 , e ) = W c e R c e + W d e R d e = W c e + W d e = 0.5 + 0.4 = 0.9 \Large{P({U3,e}) = W_{ce}R_{ce} + W_{de}R_{de}} = W_{ce} + W_{de} = 0.5 + 0.4 = 0.9 P(U3,e)=WceRce+WdeRde=Wce+Wde=0.5+0.4=0.9
f.最後根據感興趣程度倒排,如果推薦一個商品的話,那麼最應該推的是商品Python程式設計(e)
代碼實作
代碼中為了提升效率,使用嵌套字典模拟實作物品的共現矩陣
import itertools
from collections import Counter
from operator import itemgetter
from math import log1p, sqrt
def preprocess(data):
"""
資料處理
:param data: DataFrame資料
:return:
"""
user_items = {}
items = data['item'].values
for user, item in zip(data['user'].values, items):
user_items.setdefault(user, set()).add(item)
return user_items, Counter(items)
def cos(data):
"""
計算餘弦相似矩陣分子部分
:param data:
:return:
"""
cos_mat = {}
cos_setdefault, cos_get = cos_mat.setdefault, cos_mat.get
for items in data.values():
for y in items:
cos_setdefault(y, {})
items_len = len(items)
if items_len > 1:
for y, x in itertools.permutations(items, 2):
cos_get(y).setdefault(x, 1) if cos_get(y).get(x) is None else cos_get(y).update(
{x: cos_get(y).get(x) + 1}
)
return cos_mat
def iuf(data):
"""
改進後的相似矩陣分子部分
:param data:
:return:
"""
iuf_mat = {}
iuf_setdefault, iuf_get = iuf_mat.setdefault, iuf_mat.get
for items in data.values():
for y in items:
iuf_setdefault(y, {})
items_len = len(items)
if items_len > 1:
for y, x in itertools.permutations(items, 2):
lg = 1. / log1p(items_len * 1.)
iuf_get(y).setdefault(x, lg) if iuf_get(y).get(x) is None else iuf_get(y).update(
{x: iuf_get(y).get(x) + lg}
)
return iuf_mat
def co_matrix(train_data, func):
"""
計算相似共現矩陣
:param train_data:
:param func:
:return:
"""
if func == 'iuf':
return iuf(train_data)
elif func == 'cos':
return cos(train_data)
class ItemCF(object):
def __init__(self, data, func='iuf', is_norm=False):
self._train_data, self._item_count = preprocess(data)
self._func = func
self._sim_matrix = {}
self._is_norm = is_norm
def train(self):
"""
計算相似度矩陣
:return:
"""
_train_data = self._train_data
_func = self._func
_item_count = self._item_count
_sim_matrix = self._sim_matrix
# 共現矩陣
co_mat = co_matrix(_train_data, _func)
# 計算相似度矩陣
for i, co_sum in co_mat.items():
_sim_matrix.setdefault(i, {})
for j, cji in co_sum.items():
_sim_matrix.get(i).setdefault(j, cji / sqrt(_item_count[i] * _item_count[j]))
if self._is_norm:
for i, relations in _sim_matrix.items():
max_num = relations[max(relations, key=relations.get)]
# 對字典進行歸一化操作後傳回新的字典
_sim_matrix[i] = {k: v / max_num for k, v in relations.items()}
def recommend(self, user, ton_n, scope):
"""
推薦
:param user: 需要推薦的使用者
:param ton_n: 推薦top_n清單
:param scope: 查找範圍
:return:
"""
recommend_list = {}
_sim_matrix = self._sim_matrix
items = self._train_data[user]
for item in items:
for i, sim in sorted(_sim_matrix[item].items(), key=itemgetter(1), reverse=True)[:scope]:
if i in items:
continue
val = recommend_list.get(i)
recommend_list.setdefault(i, sim) if val is None else recommend_list.update({i: val + sim})
return dict(sorted(recommend_list.items(), key=itemgetter(1), reverse=True)[:ton_n])
測試結果
編寫Test檔案
from dmp.recommand.cus.cf import ItemCF
import pandas as pd
if __name__ == '__main__':
good_label = pd.read_csv('data/test.csv')
itemCF = ItemCF(good_label, 'cos')
itemCF.train()
recommend_list = itemCF.recommend('U3', ton_n=5, scope=5)
print(recommend_list)
運作結果如下圖
總結
優點:
模型簡單,可解釋性強,通過對使用者和商品添加标簽可以獲得更好的精度
可以為具有特殊興趣愛好的使用者進行推薦
缺點:
物品屬性有限,難以區分商品資訊的品質
模型相似度衡量的标準隻考慮了物品本身,具有片面性
不能為使用者發現新的感興趣的産品
最後
本人想組建一個技術交流群,有興趣的可以掃碼加入,用于學習和交流,希望和大家一起學習一起進步。
QQ群:814507419
微信群:由于微信群二維碼會失效,大家可以加我微信,然後拉大家進群期待和大家一起進步一起學習。微信号18397716181