【推薦系統】{1} —— 基於用戶的協同過濾算法

協同過濾(英語:Collaborative Filtering,簡稱CF),簡單來說是利用某興趣相投、擁有共同經驗之羣體的喜好來推薦用戶感興趣的信息,個人透過合作的機制給予信息相當程度的迴應(如評分)並記錄下來以達到過濾的目的進而幫助別人篩選信息,迴應不一定侷限於特別感興趣的,特別不感興趣信息的紀錄也相當重要。——維基百科


基於用戶的協同過濾算法(簡稱UserCF)

在一個在線個性化推薦系統中,當一個用戶A需要個性化推薦時,可以先找到和他有相似興趣的其他用戶,然後把那些用戶喜歡的、而用戶A沒有聽說過的物品推薦給A。這種方法稱爲基於用戶的協同過濾算法。

舉個例子:

用戶/物品 物品A 物品B 物品C 物品D
用戶A 推薦給用戶A
用戶B
用戶C

要實現基於用戶的協同過濾,需要的步驟:

  1. 收集用戶偏好
  2. 通過計算相似度找到與目標用戶相似的用戶
  3. 將相似用戶喜歡的、且目標用戶沒有聽說過的物品推薦給目標用戶

一、計算兩個用戶的興趣相似度

其中關鍵的一步就是計算兩個用戶的興趣相似度。

協同過濾算法主要利用行爲的相似度計算興趣的相似度。給定用戶 uu 和用戶 vv ,令 N(u)N(u) 表示用戶 uu 曾經有過正反饋的物品集合,令 N(v)N(v) 爲用戶 vv 曾經有過正反饋的物品集合。則可以通過計算餘弦相似度簡單地計算 uuvv 的興趣相似度:wuv=N(u)N(v)N(u)N(v)w_{uv} = \frac{|N(u)\cap{N(v)|}}{\sqrt{|N(u)\cup{N(v)|}}}


例子:

圖片來源

如圖,AABBCCDD 爲用戶,aabbccddee 爲物品。

利用餘弦相似度公式計算他們之間的興趣相似度分別爲:wAB={a,b,d}{a,c}{a,b,d}  {a,c}=16w_{AB} = \frac{|{\{ a,b,d\}}\cap\{{a,c}\}|}{\sqrt{|\{a,b,d\}|\,\,|\{a,c\}}|}=\frac{1}{\sqrt{6}}

wAC={a,b,d}{b,e}{a,b,d}  {b,e}=16w_{AC} = \frac{|{\{ a,b,d\}}\cap\{{b,e}\}|}{\sqrt{|\{a,b,d\}|\,\,|\{b,e\}}|}=\frac{1}{\sqrt{6}}

wAC={a,b,d}{c,d,e}{a,b,d}  {c,d,e}=13w_{AC} = \frac{|{\{ a,b,d\}}\cap\{{c,d,e}\}|}{\sqrt{|\{a,b,d\}|\,\,|\{c,d,e\}}|}=\frac{1}{3}
但事實上,很多用戶相互之間並沒有對同樣的物品產生過行爲,即 N(u)N(v)=0|N(u)\cap{N(v)}|=0


因此換一種思路,我們可以首先計算出 N(u)N(v)0|N(u)\cap{N(v)}|≠0 的用戶對 (u,v)(u,v) ,然後再對這種情況除以分母 N(u)N(v)\sqrt{{|N(u)\cup{N(v)|}}}

爲此,可以首先建立 >物品->用戶 的倒排表。令稀疏矩陣 C[u][v]=N(u)N(v)C[u][v]=|N(u)\cap{N(v)}| 。因此,可以遍歷倒排表中每個物品對應的用戶列表,將用戶列表中的兩兩用戶對應的 C[u][v]C[u][v] 同時加 11,最終就可以得到所有用戶之間不爲 00C[u][v]C[u][v]

代碼實現如下:

def UserSimilarity(train):
	# 建立倒排表
	item_users = dict()
	for u, items in train.items():
		for i in items.keys():
			if i not in item_users:
				item_users[i] = set()
			item_users[i].add(u)
	
	# 修改相似度矩陣
	C = dict()
	N = dict()
		for i, users in item_users.items():
			for u in users:
				N[u] += 1
				for V in users:
					if u == v:
						continue
					C[u][v] += 1
	
	# 計算相似度矩陣
	W = dict()
	for u, related_users in C.items():
		for V, cuv in related_users.items():
			W[u][v] = cuv / math.sqrt(N[u] * N[v])
	return W

以上圖的用戶行爲解釋上述算法。首先,需要建立 >物品->用戶 的倒排表(如圖1)。然後建立一個 4×44×4 的用戶相似度矩陣 WW,對於物品 aa,將 W[A][B]W[A][B]W[B][A]W[B][A]11,對於物品 bb,將 W[A][C]W[A][C]W[C][A]W[C][A]11,以此類推(如圖2)。掃描完所有物品後,我們可以得到最終的 WW 矩陣。 此處 WW 是餘弦相似度中的分子部分,將 WW 除以分母即可得到最終的用戶興趣相似度(如圖3)。

圖片來源


二、進行推薦

得到用戶之間的興趣相似度後,UserCFUserCF 算法會給用戶推薦和他興趣最相似的 KK 個用戶喜歡的物品。以下公式度量了 UserCFUserCF 算法中用戶 uu 對物品 ii 的感興趣程度: p(u,i)=vS(u,K)N(i)wuvrvip(u,i)=\displaystyle \sum_{v\in{S(u,K)\cap{N(i)}}}w_{uv}r_{vi}

K是 UserCFUserCF 算法的一個重要參數

其中,S(u,K)S(u,K) 包含和用戶 uu 興趣最接近的 KK 個用戶,N(i)N(i) 是對物品 ii 有過行爲的用戶集合,wurw_{ur}
是用戶 uu 和用戶 vv 的興趣相似度,rvir_{vi} 代表用戶 vv 對物品 ii 的興趣,因爲使用的是單一行爲的隱反饋數據(否則使用評分等指標),所以所有的 rvi=1r_{vi}=1

評分可以經過歸一化處理

代碼如下:

def Recommend(user, train, W):
	rank = dict()
	interacted_items = train[user]
	for v, wuv in sorted (W[u].items, key=itemgetter(1), reverse=True)[0:K]:
		for i, rvi in train[v].items:
			if i in interacted_items:
				# 過濾互動過的項目
				continue
			rank[i] += wuv * rvi
	return rank

利用上述 UserCFUserCF 算法,可以給上述圖1、2、3的用戶 AA 進行推薦。選取 K=3K=3,用戶 AA 對物品 ccee 沒有過行爲,因此可以把這兩個物品推薦給用戶 AA。相似用戶則是 BBCCDD,他們喜歡過並且 AA 沒有喜歡過的物品有 ccee。根據 UserCFUserCF 算法,用戶 AA 對物品 ccee 的興趣是:p(A,c)=wAB×1+wAD×1=16+19=0.7416p(A,c)=w_{AB} ×1+ w_{AD} ×1= \frac{1}{\sqrt{6}}+\frac{1}{\sqrt{9}}=0.7416p(A,e)=wAC×1+wAD×1=16+19=0.7416p(A,e)=w_{AC}×1+ w_{AD} ×1= \frac{1}{\sqrt{6}}+\frac{1}{\sqrt{9}}=0.7416


用戶相似度計算的改進:

兩個用戶對冷門物品採取過同樣的行爲比熱門物品更能說明他們興趣的相似度。因此,爲了防止熱門物品的影響,提出如下公式計算用戶的興趣相似度:wuv=iN(u)N(v)1log1+N(i)N(u)N(v)w_{uv}=\frac{\displaystyle \sum_{i\in{N(u)\cap{N(v)}}}\frac{1}{log1+|N(i)|}}{\sqrt{|N(u)||N(v)|}}

上述公式通過 1log1+N(i)\frac{1}{log1+|N(i)|} 懲罰了用戶 uu 和用戶 vv 共同興趣列表中熱門物品對他們相似度的影響。

基於上述用戶相似度公式的算法記爲 UserIIFUser-IIF 算法。

代碼如下:

def UserSimilarity(train):
	# 建立倒排表
	item_users = dict()
	for u, items in train.items():
		for i in items.keys():
			if i not in item_users:
				item_users[i] = set()
			item_users[i].add(u)
	# 統計相似度矩陣
	C = dict()
	N = dict()
	for i, users in item_users.items():
		for u in users:
			N[u] += 1
			for v in users: 
				if u == v:
					continue
				C[u][v] += 1 / math.1og(1 + len(users))
	# 計算相似度矩陣
	W = dict()
	for u, related_users in C.items():
		for v, cuv in related.users.items():
			W[u][v] = cuv / math.sqrt(N[u] * N[v])
	return W

UserCFUserCF 算法存在的問題:

  • 對於一個新用戶,很難找到相似用戶。
  • 對於一個物品,所有相似用戶都在其上沒有多少行爲。
  • 矩陣稀疏。
  • 用戶量越來越大
  • 人類善變

參考資料:《推薦系統實踐》

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