基于用户的协同过滤算法理解
基于用户的协同过滤算法详解
Date: October 29, 2025
Author: AI 算法助手
Tags: 推荐系统,协同过滤,机器学习,Python
目录
什么是协同过滤
协同过滤(Collaborative Filtering)是推荐系统中最经典的算法之一,其核心思想是 “相似的用户喜欢相似的物品”。
协同过滤的两种主要类型
-
基于用户的协同过滤(User-Based CF):找到与目标用户兴趣相似的其他用户,推荐这些相似用户喜欢的物品
-
基于物品的协同过滤(Item-Based CF):找到与目标用户喜欢的物品相似的其他物品进行推荐
本文将详细介绍基于用户的协同过滤算法。
基于用户的协同过滤原理
核心假设
“如果用户 A 和用户 B 有相似的兴趣偏好,那么用户 A 喜欢的物品很可能也会被用户 B 喜欢”
算法流程
基于用户的协同过滤推荐主要包含两个关键步骤:
-
计算用户相似度:找到与目标用户兴趣相似的其他用户
-
生成推荐列表:根据相似用户的喜好为目标用户推荐物品
相似度计算方法
在无评分的二值数据场景下(只有物品是否被拥有的信息),常用的相似度计算方法有三种:
1. Common Neighbors(共同邻居)相似度
定义:直接计算两个用户共同拥有的物品数量
def cn_similarity(items1: set, items2: set) -> int:
"""Common Neighbors相似度:共同物品的数量"""
return len(items1 & items2)
特点:计算简单,适用于二值数据,但缺乏归一化
2. Jaccard(杰卡德)相似度
定义:两个集合交集与并集的比例
def jaccard_similarity(items1: set, items2: set) -> float:
"""Jaccard相似度:交集/并集"""
intersection = len(items1 & items2)
union = len(items1 | items2)
return 0.0 if union == 0 else intersection / union
公式:
J(u, v) = |I(u) ∩ I(v)| / |I(u) ∪ I(v)|
特点:取值范围 [0,1],适合衡量集合相似程度
3. 余弦相似度
定义:将用户物品集合视为二进制向量,计算向量夹角的余弦值
import math
def cosine_similarity(items1: set, items2: set) -> float:
"""余弦相似度:共同物品数/(√物品数1 × √物品数2)"""
intersection = len(items1 & items2)
norm1 = math.sqrt(len(items1))
norm2 = math.sqrt(len(items2))
return 0.0 if (norm1 * norm2) == 0 else intersection / (norm1 * norm2)
公式:
cos(u, v) = |I(u) ∩ I(v)| / (√|I(u)| × √|I(v)|)
特点:对向量长度不敏感,注重方向相似性
算法实现步骤
步骤 1:准备实验数据
# 用户-物品关联数据
user_items = {
'A': {'b', 'd'}, # 用户A拥有物品b和d
'B': {'a', 'b', 'c'}, # 用户B拥有物品a、b和c
'C': {'a', 'b', 'd'}, # 用户C拥有物品a、b和d
'D': {'a', 'e'} # 用户D拥有物品a和e
}
步骤 2:实现推荐函数
from collections import defaultdict
def recommend_items(
target_user: str,
k: int = 2,
top_n: int = 2,
sim_method = jaccard_similarity
) -> list:
"""
为目标用户推荐物品
Args:
target_user: 目标用户ID
k: 参考的相似用户数量
top_n: 返回的推荐物品数量
sim_method: 相似度计算方法
Returns:
list: 推荐物品列表
"""
# 获取目标用户已拥有的物品
target_owned = user_items[target_user]
# 步骤1:计算与其他用户的相似度
similarities = []
for other_user, items in user_items.items():
if other_user == target_user:
continue
similarity = sim_method(target_owned, items)
if similarity > 0:
similarities.append((similarity, other_user))
# 步骤2:选择最相似的k个用户
similarities.sort(key=lambda x: x[0], reverse=True)
top_k_users = similarities[:k]
if not top_k_users:
return []
# 步骤3:计算物品推荐分数
item_scores = defaultdict(float)
for similarity, user in top_k_users:
# 相似用户拥有但目标用户没有的物品
candidate_items = user_items[user] - target_owned
for item in candidate_items:
item_scores[item] += similarity
# 步骤4:返回推荐分数最高的top_n个物品
sorted_items = sorted(item_scores.items(), key=lambda x: x[1], reverse=True)
return [item for item, score in sorted_items[:top_n]]
实验验证
实验设计
实验参数:
-
目标用户:A、D
-
相似用户数量 k:2、3
-
推荐物品数量 top_n:2
-
相似度算法:Jaccard、余弦、Common Neighbors
实验结果
用户 A 的推荐结果
# 为用户A推荐物品
print("用户A推荐结果(Jaccard):", recommend_items('A', k=2, sim_method=jaccard_similarity))
print("用户A推荐结果(余弦):", recommend_items('A', k=2, sim_method=cosine_similarity))
print("用户A推荐结果(CN):", recommend_items('A', k=2, sim_method=cn_similarity))
输出:
用户 A 推荐结果(Jaccard): [‘a’, ‘c’]
用户 A 推荐结果(余弦): [‘a’, ‘c’]
用户 A 推荐结果(CN): [‘a’, ‘c’]
用户 D 的推荐结果
# 为用户D推荐物品
print("用户D推荐结果(Jaccard):", recommend_items('D', k=2, sim_method=jaccard_similarity))
print("用户D推荐结果(余弦):", recommend_items('D', k=2, sim_method=cosine_similarity))
print("用户D推荐结果(CN):", recommend_items('D', k=2, sim_method=cn_similarity))
输出:
用户 D 推荐结果(Jaccard): [‘b’, ‘c’]
用户 D 推荐结果(余弦): [‘b’, ‘c’]
用户 D 推荐结果(CN): [‘b’, ‘c’]
性能评估
评估指标
def evaluate_recommendation(recommended_items, expected_items):
"""评估推荐结果的性能"""
recommended_set = set(recommended_items)
expected_set = set(expected_items)
intersection = recommended_set & expected_set
precision = len(intersection) / len(recommended_set) if recommended_set else 0
recall = len(intersection) / len(expected_set) if expected_set else 0
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) != 0 else 0
return {
'precision': precision,
'recall': recall,
'f1': f1,
'hit': intersection
}
评估结果
用户 A 的性能评估
# 预期推荐物品
expected_a = ['a', 'c']
result_a = recommend_items('A', k=2)
eval_a = evaluate_recommendation(result_a, expected_a)
print(f"用户A评估结果:")
print(f"推荐物品: {result_a}")
print(f"预期物品: {expected_a}")
print(f"命中物品: {eval_a['hit']}")
print(f"精确率: {eval_a['precision']:.4f}")
print(f"召回率: {eval_a['recall']:.4f}")
print(f"F1分数: {eval_a['f1']:.4f}")
输出:
用户 A 评估结果:
推荐物品: [‘a’, ‘c’]
预期物品: [‘a’, ‘c’]
命中物品: {‘a’, ‘c’}
精确率: 1.0000
召回率: 1.0000
F1 分数: 1.0000
用户 D 的性能评估
# 预期推荐物品
expected_d = ['b', 'c', 'd']
result_d = recommend_items('D', k=2)
eval_d = evaluate_recommendation(result_d, expected_d)
print(f"用户D评估结果:")
print(f"推荐物品: {result_d}")
print(f"预期物品: {expected_d}")
print(f"命中物品: {eval_d['hit']}")
print(f"精确率: {eval_d['precision']:.4f}")
print(f"召回率: {eval_d['recall']:.4f}")
print(f"F1分数: {eval_d['f1']:.4f}")
输出:
用户 D 评估结果:
推荐物品: [‘b’, ‘c’]
预期物品: [‘b’, ‘c’, ‘d’]
命中物品: {‘b’, ‘c’}
精确率: 1.0000
召回率: 0.6667
F1 分数: 0.8000
优缺点分析
优点
-
实现简单:算法原理直观,易于理解和实现
-
无需物品特征:只需要用户行为数据,不需要物品的内容特征
-
发现新兴趣:能够为用户推荐意想不到的兴趣物品
-
实时性好:当用户行为发生变化时,可以快速更新推荐结果
缺点
-
数据稀疏性:当用户 - 物品矩阵稀疏时,难以找到相似用户
-
冷启动问题:新用户或新物品缺乏行为数据,无法有效推荐
-
计算复杂度:随着用户数量增加,相似度计算复杂度呈 O (n²) 增长
-
推荐多样性:可能导致推荐结果缺乏多样性
总结与展望
实验结论
-
算法有效性:基于用户的协同过滤算法在无评分二值数据场景下表现良好
-
性能表现:用户 A 的 F1 分数达到 1.0,用户 D 达到 0.8,推荐效果理想
-
算法对比:三种相似度算法在小规模数据集上表现基本一致
-
参数影响:k 值(相似用户数量)对推荐结果影响较小
改进方向
-
混合推荐:结合基于物品的协同过滤或内容推荐,提高推荐效果
-
降维技术:使用 PCA、SVD 等降维技术降低计算复杂度
-
时间因素:考虑用户行为的时间衰减,近期行为给予更高权重
-
冷启动处理:结合物品特征信息处理新用户和新物品问题
-
多样性优化:在保证准确性的同时提高推荐结果的多样性
应用场景
基于用户的协同过滤算法适用于:
-
电商平台的商品推荐
-
音乐、视频、新闻等内容推荐
-
社交网络的好友推荐
-
在线教育的课程推荐
完整代码
import math
from collections import defaultdict
# 1. 实验数据
user_items = {
'A': {'b', 'd'},
'B': {'a', 'b', 'c'},
'C': {'a', 'b', 'd'},
'D': {'a', 'e'}
}
# 2. 相似度计算函数
def cn_similarity(items1: set, items2: set) -> int:
"""Common Neighbors相似度:共同物品的数量"""
return len(items1 & items2)
def jaccard_similarity(items1: set, items2: set) -> float:
"""Jaccard相似度:交集/并集"""
intersection = len(items1 & items2)
union = len(items1 | items2)
return 0.0 if union == 0 else intersection / union
def cosine_similarity(items1: set, items2: set) -> float:
"""余弦相似度:共同物品数/(√物品数1 × √物品数2)"""
intersection = len(items1 & items2)
norm1 = math.sqrt(len(items1))
norm2 = math.sqrt(len(items2))
return 0.0 if (norm1 * norm2) == 0 else intersection / (norm1 * norm2)
# 3. 推荐函数
def recommend_items(
target_user: str,
k: int = 2,
top_n: int = 2,
sim_method = jaccard_similarity
) -> list:
"""为目标用户推荐物品"""
target_owned = user_items[target_user]
# 计算与其他用户的相似度
similarities = []
for other_user, items in user_items.items():
if other_user == target_user:
continue
similarity = sim_method(target_owned, items)
if similarity > 0:
similarities.append((similarity, other_user))
# 选择最相似的k个用户
similarities.sort(key=lambda x: x[0], reverse=True)
top_k_users = similarities[:k]
if not top_k_users:
return []
# 计算物品推荐分数
item_scores = defaultdict(float)
for similarity, user in top_k_users:
candidate_items = user_items[user] - target_owned
for item in candidate_items:
item_scores[item] += similarity
# 返回推荐结果
sorted_items = sorted(item_scores.items(), key=lambda x: x[1], reverse=True)
return [item for item, score in sorted_items[:top_n]]
# 4. 评估函数
def evaluate_recommendation(recommended_items, expected_items):
"""评估推荐结果"""
recommended_set = set(recommended_items)
expected_set = set(expected_items)
intersection = recommended_set & expected_set
precision = len(intersection) / len(recommended_set) if recommended_set else 0
recall = len(intersection) / len(expected_set) if expected_set else 0
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) != 0 else 0
return {
'precision': precision,
'recall': recall,
'f1': f1,
'hit': intersection
}
# 5. 主函数
if __name__ == "__main__":
print("基于用户的协同过滤算法演示")
print("=" * 50)
# 为用户A推荐
print("n用户A推荐结果:")
a_recommendations = recommend_items('A', k=2)
print(f"推荐物品: {a_recommendations}")
# 为用户D推荐
print("n用户D推荐结果:")
d_recommendations = recommend_items('D', k=2)
print(f"推荐物品: {d_recommendations}")
# 评估结果
print("n性能评估:")
expected_a = ['a', 'c']
eval_a = evaluate_recommendation(a_recommendations, expected_a)
print(f"用户A F1分数: {eval_a['f1']:.4f}")
expected_d = ['b', 'c', 'd']
eval_d = evaluate_recommendation(d_recommendations, expected_d)
print(f"用户D F1分数: {eval_d['f1']:.4f}")
参考资料
-
Resnick, P., Iacovou, N., Suchak, M., Bergstrom, P., & Riedl, J. (1994). GroupLens: an open architecture for collaborative filtering of netnews.
-
Sarwar, B., Karypis, G., Konstan, J., & Riedl, J. (2001). Item-based collaborative filtering recommendation algorithms.
-
Su, X., & Khoshgoftaar, T. M. (2009). A survey of collaborative filtering techniques.
如果这篇文章对您有帮助,请点赞分享!如有疑问或建议,欢迎留言讨论。
更多推荐


所有评论(0)