Chapter 06
2.1.3. UserCF:基于用户相似度的协同过滤
2.1.3. UserCF:基于用户相似度的协同过滤¶
基于用户的协同过滤(UserCF) (Resnick et al., 1994) 的核心思想是:具有相似历史行为的用户,未来偏好也相似。它先找到与目标用户偏好最接近的“邻居用户”,再基于这些邻居的行为来预测目标用户的兴趣。如 图2.1.5 所示,左侧用户和右侧用户都喜欢T恤和Polo衫,说明两人爱好相似。右侧用户还喜欢夹克,因此系统推测左侧用户也可能喜欢夹克,将其作为推荐结果。
图2.1.5 UserCF 原理示意图¶
2.1.3.1. UserCF核心算法¶
2.1.3.1.1. 用户相似度计算¶
假设用户 (u) 和用户 (v) 分别对应物品集合 (N(u)) 和 (N(v))(即他们各自有过行为的物品),常用的相似度矩阵的计算方法有以下三种。
杰卡德相似系数:当系统只记录用户是否对物品有过行为(比如是否点击、购买),而没有具体的评分时,可以用杰卡德系数来计算用户间的相似度 (w_{uv}):
(2.1.15)¶[w_{uv} = \frac{|N(u) \cap N(v)|}{|N(u) \cup N(v)|}] 这个公式可以直观理解为:分子是两人共同喜欢的物品数量,分母是两人喜欢的物品总数(去重后)。
余弦相似度:将每个用户的行为记录看作一个向量,通过向量夹角的余弦值来度量用户间的相似度 (w_{uv}):
(2.1.16)¶[w_{uv} = \frac{|N(u) \cap N(v)|}{\sqrt{|N(u)|\cdot|N(v)|}}] 余弦相似度考虑了用户活跃度的差异。
皮尔逊相关系数:当系统拥有具体的评分数据时(比如5星评分),可以利用用户的评分向量计算皮尔逊相关系数,作为相似度 (w_{uv}):
(2.1.17)¶[w_{uv} = \frac{\sum_{i \in I}(r_{ui} - \bar{r}u)(r{vi} - \bar{r}v)}{\sqrt{\sum{i \in I}(r_{ui} - \bar{r}u)^2}\sqrt{\sum{i \in I}(r_{vi} - \bar{r}v)^2}}] 这里(r{ui})表示用户(u)对物品(i)的评分,(\bar{r}_u)是用户(u)的平均评分,(I)是两个用户都评价过的物品集合。皮尔逊系数通过中心化处理,有效消除了个人评分习惯的差异。有些人习惯给高分(“好人”),有些人比较严格,通过减去各自的平均分,我们关注的是评分的相对变化趋势,而不是绝对数值。
2.1.3.1.2. 候选物品推荐¶
计算完相似度矩阵后,对目标用户 (u) 选择相似度最高的 (K) 个用户作为邻居集合 (\mathcal{S}_u),再基于这些邻居的已知评分来预测目标用户对未交互物品的兴趣。
简单加权平均:最直接的方法是用相似度作为权重,对邻居的评分进行加权平均:
(2.1.18)¶[\hat{r}{u,p} = \frac{\sum{v \in S_u} w_{uv} , r_{v,p}}{\sum_{v \in S_u} w_{uv}}] 这里(\hat{r}{u,p})是预测的用户(u)对物品(p)的评分,(S_u)是邻居用户集合,(w{uv})是相似度权重,(r_{v,p})是邻居(v)对物品(p)的实际评分。
考虑评分偏置的版本:为了进一步消除个人评分习惯的影响,我们可以加入偏置修正:
(2.1.19)¶[\hat{r}{u,p} = \bar{r}{u} + \frac{\sum_{v \in S_u} w_{uv} , (r_{v,p} - \bar{r}{v})}{\sum{v \in S_u} w_{uv}}] 这里(\bar{r}_u)和(\bar{r}_v)分别是用户(u)和(v)的平均评分。这个公式的思路是:先看邻居们对这个物品的评分相比他们平均水平如何,然后根据目标用户的平均评分水平进行调整。
2.1.3.1.3. 计算效率优化¶
直接计算所有用户对相似度的复杂度为 (O(|U|^2)),但很多用户对之间没有共同行为的物品,相似度必然为 0。基于物品倒排表可以大幅提速:为每个物品维护一个用户列表,遍历时将列表中的用户两两配对,累加共现矩阵 (C[u][v]),再除以 (\sqrt{|N(u)||N(v)|}) 得到余弦相似度,只需计算真正有共同行为的用户对。
线上推荐时,为目标用户找到最相似的 K 个用户,收集他们交互过的物品作为候选集,计算目标用户 (u) 对候选物品 (i) 的兴趣分数:
(2.1.20)¶[p(u, i) = \sum_{v \in S_u \cap N(i)} w_{uv} \cdot r_{vi}] 其中(S_u)是与用户(u)最相似的K个用户集合,(N(i))是对物品(i)有过行为的用户集合,(w_{uv})是用户相似度,(r_{vi})表示用户(v)对物品(i)的兴趣强度(可以是简单的1,也可以根据评分、交互时间等设置权重)。
系统对候选物品按兴趣分数排序,选择 Top-N 作为推荐结果。优化后的复杂度约为 (O(R \cdot \bar{n}))((R) 为总交互数,(\bar{n}) 为每个物品的平均用户数),在稀疏数据场景下远低于原来的 (O(|U|^2))。
2.1.3.2. UserCF评分预测示例¶
表格 表2.1.2 里是5个用户对5个物品的评分数据,可以理解为用户对物品的偏好程度。
表2.1.2 用户评分数据¶
物品1
物品2
物品3
物品4
物品5
用户1
5
3
4
4
?
用户2
3
1
2
3
3
用户3
4
3
4
3
5
用户4
3
3
1
5
4
用户5
1
5
5
2
1
基于皮尔逊相关系数,计算用户1 与其他用户(以用户2为例)的相似度:
-
计算用户1与用户2的皮尔逊相关系数: (\bar{r}{user1}=4,\ \bar{r}{user2}=2.25)。向量减去均值: (\text{user1}:(1,-1, 0,0) \quad \text{user2}: (0.75,-1.25,-0.25,0.75))
-
计算这俩新向量的余弦相似度,得到皮尔逊相似度 (0.852)。
根据相似用户,计算用户1对物品5的最终得分。用户2对物品5的评分是3, 用户3对物品5的打分是5, 那么根据上面的计算公式 (2.1.17),可以计算出 用户1 对物品5的最终得分是:
(2.1.21)¶[P_{user1, item5}=\bar{r}{user1}+\frac{\sum{k=1}^{2}\left(w_{user1,user k}\left(r_{userk, item5}-\bar{r}{userk}\right)\right)}{\sum{k=1}^{2} w_{user1, userk}}=4+\frac{0.85*(3-2.4)+0.7*(5-3.8)}{0.85+0.7}=4.87]
2.1.3.3. 代码实践¶
import numpy as np user_data = { 'user1': {'item1': 5, 'item2': 3, 'item3': 4, 'item4': 4}, 'user2': {'item1': 3, 'item2': 1, 'item3': 2, 'item4': 3, 'item5': 3}, 'user3': {'item1': 4, 'item2': 3, 'item3': 4, 'item4': 3, 'item5': 5}, 'user4': {'item1': 3, 'item2': 3, 'item3': 1, 'item4': 5, 'item5': 4}, 'user5': {'item1': 1, 'item2': 5, 'item3': 5, 'item4': 2, 'item5': 1}, }
这里使用字典来建立用户-物品的交互表:
-
字典users的键表示不同用户的名字,值为一个评分字典,评分字典的键值对表示某物品被当前用户的评分。
-
由于现实场景中,用户对物品的评分比较稀疏。如果直接使用矩阵进行存储,会存在大量空缺值,故此处使用了字典。
基于皮尔逊相关系数,计算用户相似性矩阵:
初始化相似性矩阵
import pandas as pd similarity_matrix = pd.DataFrame( np.identity(len(user_data)), index=user_data.keys(), columns=user_data.keys(), )
遍历每条用户-物品评分数据
for u1, items1 in user_data.items(): for u2, items2 in user_data.items(): if u1 == u2: continue vec1, vec2 = [], [] for item, rating1 in items1.items(): rating2 = items2.get(item, -1) if rating2 == -1: continue vec1.append(rating1) vec2.append(rating2) # 计算不同用户之间的皮尔逊相关系数 similarity_matrix[u1][u2] = np.corrcoef(vec1, vec2)[0][1]
print(similarity_matrix)
计算与用户1最相似的n个用户:
target_user = 'user1' num = 2
由于最相似的用户为自己,去除本身
sim_users = similarity_matrix[target_user].sort_values(ascending=False)[1:num+1].index.tolist() print(f'与用户{target_user}最相似的{num}个用户为:{sim_users}')
预测用户1对物品5的评分:
weighted_scores = 0. corr_values_sum = 0.
target_item = 'item5'
基于皮尔逊相关系数预测用户评分
for user in sim_users: corr_value = similarity_matrix[target_user][user] user_mean_rating = np.mean(list(user_data[user].values()))
weighted_scores += corr_value * (user_data[user][target_item] - user_mean_rating)
corr_values_sum += corr_valuetarget_user_mean_rating = np.mean(list(user_data[target_user].values())) target_item_pred = target_user_mean_rating + weighted_scores / corr_values_sum print(f'用户{target_user}对物品{target_item}的预测评分为:{target_item_pred:.4f}')
训练和评估完整的UserCF模型:
from funrec import run_experiment
run_experiment('user_cf')
+---------------+--------------+----------------+---------------+ | hit_rate@10 | hit_rate@5 | precision@10 | precision@5 | +===============+==============+================+===============+ | 0.6912 | 0.5927 | 0.1643 | 0.2063 | +---------------+--------------+----------------+---------------+
Table Of Contents
-
2.1.3. UserCF:基于用户相似度的协同过滤 2.1.3.1. UserCF核心算法 2.1.3.1.1. 用户相似度计算
-
2.1.3.1.2. 候选物品推荐
-
2.1.3.1.3. 计算效率优化
-
2.1.3.2. UserCF评分预测示例
-
2.1.3.3. 代码实践
codePrevious 2.1.2. Swing:面向工业场景的相似度优化 Next 2.1.4. 矩阵分解:从相似度计算到向量表示学习
