Chapter 04
2.1.1. ItemCF:基于物品相似度的协同过滤
2.1.1. ItemCF:基于物品相似度的协同过滤¶
基于物品的协同过滤(ItemCF) (Linden et al., 2003) 的核心思想是:用户的兴趣具有连贯性,喜欢某个物品的用户往往也会对相似的物品感兴趣。ItemCF关注的问题是“和你喜欢的物品相似的还有什么”,通过计算物品之间的相似度来生成推荐结果。
图2.1.1 ItemCF 原理示意图¶
从 图2.1.1 可以看到,当我们要给左侧用户推荐物品时,ItemCF会分析T恤和夹克之间的相似性。由于右侧三个用户都同时喜欢这两种服装,系统判断它们具有较高的相似性。既然左方用户喜欢T恤,那么夹克就成为了一个不错的推荐选择。
2.1.1.1. ItemCF核心算法¶
2.1.1.1.1. 物品相似度计算¶
在大多数实际场景中,只有用户是否对物品有过交互行为的数据(如点击、购买、收藏等),而没有具体的评分信息。ItemCF 使用余弦相似度量化物品间的相似程度:
(2.1.1)¶[w_{ij} = \frac{\boldsymbol{C}[i][j]}{\sqrt{|\mathcal{N}(i)| \cdot |\mathcal{N}(j)|}}] 其中 (|\mathcal{N}(i)|) 表示与物品 (i) 有交互的用户总数,(\boldsymbol{C}[i][j]) 是两个物品的共现次数(同时对两个物品有过行为的用户数)。分母对共现次数进行标准化,防止热门商品占据绝对优势。
2.1.1.1.2. 候选物品推荐¶
有了物品相似度矩阵,ItemCF 的线上推荐流程如下:
首先,系统会选取用户最近交互的物品作为种子物品(通常是几百个),然后为每个种子物品找到最相似的若干个候选物品(如Top-10),这样可以快速生成大量候选集合。接下来计算用户(u)对候选物品(i)的兴趣分数:
(2.1.2)¶[p(u, i) = \sum_{j \in \mathcal{N}(u)} w_{ij} \cdot r_{uj}] 其中(\mathcal{N}(u))是用户(u)交互过的物品集合,(w_{ij})是物品(i)和(j)的相似度,(r_{uj})表示用户对物品(j)的兴趣强度(可以是简单的1,也可以根据交互时间、类型等设置不同权重)。
最终,系统对所有候选物品按兴趣分数排序,选择Top-N物品作为ItemCF通道的推荐结果。
2.1.1.1.3. 计算效率优化¶
直接计算所有物品对相似度的复杂度为 (O(|\mathcal{I}|^2 \cdot |\mathcal{U}|)),但大多数物品对之间没有共同的用户交互,相似度必然为 0。基于用户-物品倒排表可以大幅提速:为每个用户维护一个物品交互列表,遍历时将列表中的物品两两配对,累加共现矩阵 (\boldsymbol{C}[i][j]),再除以 (\sqrt{|\mathcal{N}(i)||\mathcal{N}(j)|}) 得到余弦相似度,只需计算真正有共同用户的物品对。
优化后的复杂度约为 (O(R \cdot \bar{m}))((R) 为总交互数,(\bar{m}) 为用户平均交互物品数),在稀疏数据场景下由于 (\bar{m} \ll |\mathcal{I}|),效率远高于暴力计算。
2.1.1.1.4. 处理评分数据的相似度计算¶
前面的余弦相似度适用于隐式反馈场景(如点击、浏览),当有显式评分数据(如5星评分)时,皮尔逊相关系数能更好地捕获物品间的相似性模式 (Sarwar et al., 2001):
(2.1.3)¶[w_{ij} = \frac{\sum_{u \in \mathcal{U}{ij}}(r{ui} - \bar{r}i)(r{uj} - \bar{r}j)}{\sqrt{\sum{u \in \mathcal{U}{ij}}(r{ui} - \bar{r}i)^2}\sqrt{\sum{u \in \mathcal{U}{ij}}(r{uj} - \bar{r}j)^2}}] 其中(\mathcal{U}{ij})表示同时评价过物品(i)和(j)的用户集合,(r_{ui})是用户(u)对物品(i)的评分,(\bar{r}_i)是物品(i)的平均评分。
皮尔逊相关系数通过中心化处理,消除了不同物品评分分布的差异,关注评分的相对趋势而非绝对值。基于它可以预测用户对未接触物品的评分:
(2.1.4)¶[\hat{r}{u,j} = \bar{r}{j} + \frac{\sum_{k \in \mathcal{S}j} w{jk},\left( r_{u,k} - \bar{r}{k} \right)}{\sum{k \in \mathcal{S}j} w{jk}}] 这个公式以物品 (j) 的平均评分为基准,根据用户对相似物品的评分偏好进行调整。在大规模推荐系统中,出于计算复杂度和数据稀疏性的考虑,多数系统仍倾向于使用余弦相似度,辅以加权或归一化来处理评分差异。
2.1.1.2. ItemCF评分预测示例¶
表2.1.1 展示了一组用户评分数据,包含 5 位用户对 5 个物品的评分,其中用户 1 对物品 5 的评分未知,是我们需要预测的目标。
表2.1.1 用户评分数据¶
用户1
用户2
用户3
用户4
用户5
物品1
5
3
4
3
1
物品2
3
1
3
3
5
物品3
4
2
4
1
5
物品4
4
3
3
5
2
物品5
?
3
5
4
1
接下来以物品 5 和物品 1 为例,利用前述皮尔逊相关系数公式计算它们之间的相似度。首先计算两个物品的平均评分(仅使用用户 2~5 的已知评分):
(2.1.5)¶[\bar{r}_5 = (3+5+4+1)/4 = 3.25, \quad \bar{r}_1 = (3+4+3+1)/4 = 2.75] 将每位用户的评分减去对应物品的均值,得到中心化评分向量:
(2.1.6)¶[\text{物品5}: (-0.25,; 1.75,; 0.75,; -2.25), \quad \text{物品1}: (0.25,; 1.25,; 0.25,; -1.75)] 皮尔逊相关系数等价于中心化向量的余弦相似度,代入计算:
(2.1.7)¶[w_{5,1} = \cos\big((-0.25,; 1.75,; 0.75,; -2.25),;(0.25,; 1.25,; 0.25,; -1.75)\big) = 0.969] 根据皮尔逊相关系数,可以找到与物品5最相似的两个物品是物品1和物品4。基于相似物品,根据上面的计算公式 (2.1.4),可以计算出 用户1 对物品5的最终得分是:
(2.1.8)¶[\begin{split}\begin{aligned} \hat{r}{user1,item5}&=\bar{r}{item5}+\frac{\sum_{k=1}^{2}\left(w_{item5,itemk}\left(r_{user1,itemk}-\bar{r}{itemk}\right)\right)}{\sum{k=1}^{2} w_{item5,itemk}}\ &=\frac{13}{4}+\frac{0.97*(5-3.2)+0.58*(4-3.4)}{0.97+0.58}\ &=4.6 \end{aligned}\end{split}]
2.1.1.3. 代码实践¶
下面用 Python 复现上述 ItemCF 评分预测流程。首先构造 表2.1.1 中的评分数据:
import numpy as np item_data = { 'item1': {'user1': 5, 'user2': 3, 'user3': 4, 'user4': 3, 'user5': 1}, 'item2': {'user1': 3, 'user2': 1, 'user3': 3, 'user4': 3, 'user5': 5}, 'item3': {'user1': 4, 'user2': 2, 'user3': 4, 'user4': 1, 'user5': 5}, 'item4': {'user1': 4, 'user2': 3, 'user3': 3, 'user4': 5, 'user5': 2}, 'item5': {'user2': 3, 'user3': 5, 'user4': 4, 'user5': 1}, }
计算物品间相似度矩阵的代码如下所示:
import pandas as pd similarity_matrix = pd.DataFrame( np.identity(len(item_data)), index=item_data.keys(), columns=item_data.keys(), )
遍历每条物品-用户评分数据
for i1, users1 in item_data.items(): for i2, users2 in item_data.items(): if i1 == i2: continue vec1, vec2 = [], [] for user, rating1 in users1.items(): rating2 = users2.get(user, -1) if rating2 == -1: continue vec1.append(rating1) vec2.append(rating2) similarity_matrix[i1][i2] = np.corrcoef(vec1, vec2)[0][1]
print(similarity_matrix)
从user1交互过的物品中,找到与item5最相似的2个物品:
target_user = 'user1' target_item = 'item5' num = 2
sim_items = [] sim_items_list = similarity_matrix[target_item].sort_values(ascending=False).index.tolist() for item in sim_items_list: # 如果target_user对物品item评分过 if target_user in item_data[item]: sim_items.append(item) if len(sim_items) == num: break print(f'与物品{target_item}最相似的{num}个物品为:{sim_items}')
预测用户1对物品5的评分:
target_user_mean_rating = np.mean(list(item_data[target_item].values())) weighted_scores = 0. corr_values_sum = 0.
for item in sim_items: corr_value = similarity_matrix[target_item][item] user_mean_rating = np.mean(list(item_data[item].values()))
weighted_scores += corr_value * (item_data[item][target_user] - user_mean_rating)
corr_values_sum += corr_valuetarget_item_pred = target_user_mean_rating + weighted_scores / corr_values_sum print(f'用户{target_user}对物品{target_item}的预测评分为:{target_item_pred}')
以上代码逐步演示了单个用户-物品对的预测过程。
接下来调用封装好的实验框架,在完整数据集上运行 ItemCF 并查看评估指标:
from funrec import run_experiment
run_experiment('item_cf')
+---------------+--------------+----------------+---------------+ | hit_rate@10 | hit_rate@5 | precision@10 | precision@5 | +===============+==============+================+===============+ | 0.6594 | 0.5459 | 0.1444 | 0.1826 | +---------------+--------------+----------------+---------------+
Table Of Contents
-
2.1.1. ItemCF:基于物品相似度的协同过滤 2.1.1.1. ItemCF核心算法 2.1.1.1.1. 物品相似度计算
-
2.1.1.1.2. 候选物品推荐
-
2.1.1.1.3. 计算效率优化
-
2.1.1.1.4. 处理评分数据的相似度计算
-
2.1.1.2. ItemCF评分预测示例
-
2.1.1.3. 代码实践
codePrevious 2.1. 协同过滤 Next 2.1.2. Swing:面向工业场景的相似度优化
