基于物品的协同过滤(ItemCF)核心原理: 通过分析用户行为数据来发现物品间的相似性关系。其核心假设是:被相同用户群体偏好的物品具有相似性。
标准实现流程:
- 构建物品相似度矩阵:基于用户-物品交互数据计算物品间相似度
- 生成候选集:针对用户已有偏好的物品,寻找与其相似的其他物品
- 推荐排序:综合用户历史偏好程度与物品相似度进行评分预测
典型应用场景:
- 电商平台的"相关推荐"(如"购买了该商品的用户还买了...")
- 个性化推荐模块(如"猜你喜欢")
核心价值主张: "物以类聚"——基于您已表现出兴趣的物品,推荐与其属性相似的其他物品。
1. 余弦相似度计算
核心思想:将两个物品的评分看作多维空间中的两个向量,通过计算它们之间的夹角余弦值来判断相似度。夹角越小(余弦值越接近1),说明越相似。
#余弦相似度计算
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity # 从sklearn导入余弦相似度计算工具
def calculate_item_similarity(item1, item2, user_ratings):
# 1. 找出对 item1 评分过的所有用户ID
users_with_item1 = set(uid for uid in user_ratings if item1 in user_ratings[uid])
# 2. 找出对 item2 评分过的所有用户ID
users_with_item2 = set(uid for uid in user_ratings if item2 in user_ratings[uid])
# 3. 取交集:找到同时对这两个物品都评分过的用户
common_users = users_with_item1 & users_with_item2
# 4. 边界处理:如果没有共同评分用户,相似度返回0
if len(common_users) == 0:
return 0.0
# 5. 构建向量:提取共同用户对 item1 和 item2 的评分
vec1 = np.array([user_ratings[uid].get(item1, 0) for uid in common_users]).reshape(1, -1)
vec2 = np.array([user_ratings[uid].get(item2, 0) for uid in common_users]).reshape(1, -1)
# 6. 计算余弦相似度
return cosine_similarity(vec1, vec2)[0][0]
适用场景:适合关注用户绝对评分偏好的场景。但它的缺点是:如果有个用户习惯打高分(比如全打5星),另一个习惯打低分(全打3星),即使他们对两个物品的相对喜欢程度一样,余弦相似度也会认为他们不相似。
2. 皮尔逊相关系数计算
核心思想:可以看作是“中心化”后的余弦相似度。它在计算前会先减去用户的平均评分,因此它衡量的是两个物品在评分趋势上的线性相关性,而不是绝对数值。
#皮尔逊相关系数
from scipy.stats import pearsonr # 导入scipy中的皮尔逊相关系数计算工具
def calculate_pearson_similarity(item1, item2, user_ratings):
# 1 & 2. 找出对 item1 和 item2 评分过的用户(这里用的是列表推导式)
users_with_item1 = [uid for uid in user_ratings if item1 in user_ratings[uid]]
users_with_item2 = [uid for uid in user_ratings if item2 in user_ratings[uid]]
# 3. 转换为集合后取交集,找到共同用户
common_users = set(users_with_item1) & set(users_with_item2)
# 4. 边界处理:皮尔逊相关系数要求【至少有2个共同用户】才能计算(1个点无法形成趋势线)
if len(common_users) < 2:
return 0.0
# 5. 构建评分向量
ratings1 = [user_ratings[uid][item1] for uid in common_users]
ratings2 = [user_ratings[uid][item2] for uid in common_users]
# 6. 计算皮尔逊相关系数
# pearsonr 返回两个值:第一个是相关系数,第二个是p-value(显著性)
correlation, _ = pearsonr(ratings1, ratings2)
return correlation
与余弦的区别:
- 皮尔逊的门槛更高:要求至少2个共同用户。
- 皮尔逊能消除用户评分尺度差异(比如上文提到的“严师”和“慈母”打分现象)。在推荐系统中,皮尔逊通常比余弦表现更好。
3. 矩阵分解计算
核心思想:不直接计算物品间的相似度,而是将巨大的、稀疏的“用户-物品评分矩阵”分解为两个小且稠密的矩阵:用户隐向量矩阵 和 物品隐向量矩阵。通过隐向量点积来预测评分。
#矩阵分解SVD
import numpy as np
import pandas as pd
from sklearn.decomposition import TruncatedSVD # 导入sklearn的SVD工具
def build_rating_matrix(user_ratings):
"""
将嵌套字典形式的 user_ratings 转换为二维 numpy 矩阵,
并返回矩阵以及用户和物品的ID列表,用于后续的索引映射。
"""
# 将字典转换为 pandas DataFrame (行为用户,列为物品)
df = pd.DataFrame.from_dict(user_ratings, orient='index')
# 用 0 填充没有评分的空值
df = df.fillna(0)
# 保存用户和物品的ID顺序,这个顺序对应矩阵的行和列
user_ids = df.index.tolist()
item_ids = df.columns.tolist()
return df.values, user_ids, item_ids
def train_svd_model(user_ratings, n_components=50):
# 构建用户-景点评分矩阵,并获取ID列表
rating_matrix, user_ids, item_ids = build_rating_matrix(user_ratings)
# SVD分解
svd = TruncatedSVD(n_components=n_components)
user_factors = svd.fit_transform(rating_matrix)
item_factors = svd.components_.T
# 同时返回分解后的矩阵和ID列表
return user_factors, item_factors, user_ids, item_ids
def predict_svd_rating(user_id, item_id, user_factors, item_factors, user_ids, item_ids):
# 使用列表的 index 方法找到 user_id 和 item_id 在矩阵中的位置索引
try:
user_idx = user_ids.index(user_id)
item_idx = item_ids.index(item_id)
except ValueError:
# 如果用户或物品不在训练数据中,返回一个默认值(例如0或平均分)
print(f"警告:用户 {user_id} 或 物品 {item_id} 不在训练数据中。")
return 0.0
# 获取用户和物品的隐向量
user_vec = user_factors[user_idx]
item_vec = item_factors[item_idx]
# 预测评分
return np.dot(user_vec, item_vec)
适用场景:这是工业界最常用的推荐算法基础(如隐语义模型、深度学习推荐系统的底层思想)。它能极大地缓解数据稀疏性问题(即使两个物品没有共同评分用户,也能通过隐特征算出关联),计算效率高,扩展性极强。
对比与总结
| 算法 | 解决的问题 | 核心机制 | 主要优缺点 |
|---|---|---|---|
| 余弦相似度 | 物品相似度评估 | 基于评分向量的夹角计算 | 速度快;但易受用户打分绝对值影响(存在"严师效应") |
| 皮尔逊相关系数 | 物品趋势相似度评估 | 基于均值调整后的向量夹角计算 | 比余弦更准确,消除个人偏好;但需至少2个共同评分,数据稀疏时效果欠佳 |
| 矩阵分解(SVD) | 用户评分预测 | 将评分矩阵分解为用户/物品特征向量 | 性能最优,能挖掘深层关联,泛化能力强;缺点是模型解释性较差(难以解释提取的50个特征的具体含义) |
5022



被折叠的 条评论
为什么被折叠?



