
协同过滤是推荐系统领域最为经典和广泛应用的技术之一,其基本思想源于我们日常生活中的决策方式。当人们面临选择时,往往会参考与自己品味或需求相似的其他人的意见。比如选择餐厅时,我们会相信美食家的推荐;选购书籍时,我们会关注与自己阅读兴趣相近的朋友的书单。协同过滤正是将这种社会化的决策过程数字化和自动化。
协同过滤基于一个核心假设:用户偏好具有持久性,过去有相似偏好的用户在未来也会表现出相似的偏好。这个假设虽然简单,但在大多数场景下都被证明是有效的。协同过滤的核心思想可以用一句话概括:物以类聚,人以群分。它不需要了解物品的任何属性信息,仅依靠用户与物品的交互历史(如评分、购买、点击等)发现用户或物品之间的相似性,从而预测用户可能喜欢的内容,就能产生推荐,与基于物品本身属性(如电影类型、商品类别)的推荐不同,协同过滤完全依赖于用户的实际行为,不需要对物品内容进行复杂理解。
协同过滤简单理解:

工作原理可以分解为三个基本步骤:
这种方法的优势在于其直观性和有效性。它不需要领域专业知识,能够发现复杂的、非直观的关联关系。例如,协同过滤可能会发现喜欢购买咖啡机的人往往也喜欢购买特定品牌的咖啡豆,这种关联可能连商家自己都没有意识到。
相似度计算是协同过滤的核心技术环节,其准确性直接影响到推荐系统的效果。不同的相似度度量方法从不同角度衡量用户或物品之间的相似性,各有其适用场景和特点。
余弦相似度是一种衡量两个向量方向相似程度的度量方法,它将用户评分看作向量空间中的向量,并通过计算两个向量夹角的余弦值来评估它们的相似性。
它的取值范围在-1到1之间,值越接近1表示两个向量越相似,值越接近-1表示两个向量越相反,值为0表示两个向量正交,没有关联。
在协同过滤中,我们可以把每个用户看作一个高维空间中的向量,每个维度代表一个物品的评分。余弦相似度帮助我们找到方向相似的用户,即品味相似的用户。余弦相似度的优点是不受向量长度影响,只关注方向,适合处理用户评分尺度不一致的情况。
直观理解:
计算公式:

其中:
基础示例:简单二维向量
假设有两个向量: 向量A = [3, 4] 向量B = [1, 2]
计算过程推理:
用户示例:用户评分向量
假设两个用户对三部电影的评分:
计算过程推理:
代码演示:
import numpy as np
def cosine_similarity_basic(vec_a, vec_b):
"""
计算两个向量的余弦相似度
参数:
vec_a: 第一个向量
vec_b: 第二个向量
返回:
余弦相似度值
"""
# 转换为numpy数组
a = np.array(vec_a)
b = np.array(vec_b)
# 计算点积
dot_product = np.dot(a, b)
# 计算模长
norm_a = np.linalg.norm(a)
norm_b = np.linalg.norm(b)
# 计算余弦相似度
if norm_a == 0 or norm_b == 0:
return 0 # 避免除以零
return dot_product / (norm_a * norm_b)
# 测试
vector1 = [3, 4]
vector2 = [1, 2]
similarity = cosine_similarity_basic(vector1, vector2)
print(f"向量{vector1}和{vector2}的余弦相似度: {similarity:.3f}")
# 用户评分示例
user_a = [5, 3, 0]
user_b = [4, 2, 5]
user_similarity = cosine_similarity_basic(user_a, user_b)
print(f"用户A{user_a}和用户B{user_b}的余弦相似度: {user_similarity:.3f}")输出结果:
向量[3, 4]和[1, 2]的余弦相似度: 0.984 用户A[5, 3, 0]和用户B[4, 2, 5]的余弦相似度: 0.665
皮尔逊相关系数衡量的是两个变量之间的线性相关程度。在协同过滤中,它测量的是两个用户评分模式的一致性,而不是评分的绝对值。皮尔逊相关系数的取值范围也是[-1, 1],正值表示正相关,负值表示负相关。
与余弦相似度不同,皮尔逊相关系数考虑了用户平均评分的影响,因此能够缓解用户评分严格度不同带来的偏差,能更准确地反映用户品味的相似性。例如,有些用户习惯打高分,有些用户习惯打低分,皮尔逊相关系数能够消除这种个体差异的影响。
计算公式:

其中:
取值范围和解释:
评分过程推理:
假设我们有两个用户(用户A和用户B)对5部电影的评分情况:
电影 | 用户A评分 | 用户B评分 |
|---|---|---|
电影1 | 5 | 4 |
电影2 | 4 | 5 |
电影3 | 3 | 2 |
电影4 | 2 | 1 |
电影5 | 1 | 3 |
第一步:计算平均评分
首先计算每个用户的平均评分:
第二步:计算评分与平均分的差值
计算每个评分与其相应用户平均分的差值:
电影 | 用户A评分 | 用户B评分 | A评分差值 | B评分差值 |
|---|---|---|---|---|
电影1 | 5 | 4 | 5-3 = 2 | 4-3 = 1 |
电影2 | 4 | 5 | 4-3 = 1 | 5-3 = 2 |
电影3 | 3 | 2 | 3-3 = 0 | 2-3 = -1 |
电影4 | 2 | 1 | 2-3 = -1 | 1-3 = -2 |
电影5 | 1 | 3 | 1-3 = -2 | 3-3 = 0 |
第三步:计算分子部分
计算分子:每部电影的差值的乘积之和
电影 | A评分差值 | B评分差值 | 乘积 |
|---|---|---|---|
电影1 | 2 | 1 | 2×1 = 2 |
电影2 | 1 | 2 | 1×2 = 2 |
电影3 | 0 | -1 | 0×(-1) = 0 |
电影4 | -1 | -2 | (-1)×(-2) = 2 |
电影5 | -2 | 0 | (-2)×0 = 0 |
分子总和 = 2 + 2 + 0 + 2 + 0 = 6
第四步:计算分母部分
分母由两部分组成:
第一部分:取A用户评分差值的平方之和的平方跟
电影 | A评分差值 | A差值的平方 |
|---|---|---|
电影1 | 2 | 4 |
电影2 | 1 | 1 |
电影3 | 0 | 0 |
电影4 | -1 | 1 |
电影5 | -2 | 4 |
总和 = 4 + 1 + 0 + 1 + 4 = 10 平方根 = √10 ≈ 3.162 (√为平方根符号)
第二部分:取B用户评分差值的平方之和的平方跟
电影 | B评分差值 | B差值的平方 |
|---|---|---|
电影1 | 1 | 1 |
电影2 | 2 | 4 |
电影3 | -1 | 1 |
电影4 | -2 | 4 |
电影5 | 0 | 0 |
总和 = 1 + 4 + 1 + 4 + 0 = 10 平方根 = √10 ≈ 3.162 (√为平方根符号) 分母 = 3.162 × 3.162 = 10.000
第五步:计算皮尔逊相关系数
现在我们可以计算最终的皮尔逊相关系数:
r= 分子/分母 = 6/10 = 0.6
结果评估:
与余弦相似度的对比
为了对比,我们计算一下相同数据的余弦相似度:
余弦相似度计算:
对比分析:
差异原因:
因为余弦相似度没有考虑用户的平均评分习惯。用户A和用户B的平均评分都是3,但他们的评分模式有所不同。皮尔逊相关系数通过中心化处理,更好地反映了评分模式的一致性。
代码示例:
import numpy as np
from scipy.stats import pearsonr
# 用户评分数据
userA_ratings = [5, 4, 3, 2, 1]
userB_ratings = [4, 5, 2, 1, 3]
# 手动计算皮尔逊相关系数
def manual_pearson(x, y):
# 计算平均值
mean_x = np.mean(x)
mean_y = np.mean(y)
# 计算分子
numerator = np.sum((np.array(x) - mean_x) * (np.array(y) - mean_y))
# 计算分母
denominator = np.sqrt(np.sum((np.array(x) - mean_x)**2)) * np.sqrt(np.sum((np.array(y) - mean_y)**2))
return numerator / denominator
# 使用scipy计算
scipy_pearson, _ = pearsonr(userA_ratings, userB_ratings)
print(f"手动计算皮尔逊系数: {manual_pearson(userA_ratings, userB_ratings):.3f}")
print(f"Scipy计算皮尔逊系数: {scipy_pearson:.3f}")
# 计算余弦相似度
from sklearn.metrics.pairwise import cosine_similarity
cosine_sim = cosine_similarity([userA_ratings], [userB_ratings])[0][0]
print(f"余弦相似度: {cosine_sim:.3f}")输出结果:
手动计算皮尔逊系数: 0.600 Scipy计算皮尔逊系数: 0.600 余弦相似度: 0.927
调整余弦相似度是针对余弦相似度的改进版本,它从每个评分中减去相应用户的平均评分,从而消除用户评分偏差。这是因为不同的用户可能有不同的评分习惯:有些用户倾向于打高分,有些用户则倾向于打低分。这种方法结合了余弦相似度和皮尔逊相关系数的优点,在实践中表现出更好的性能。
计算公式:

其中:
评分过程推理:
假设我们有3个用户对3个物品的评分:
用户 | 物品A | 物品B | 物品C | 平均分 |
|---|---|---|---|---|
用户1 | 5 | 3 | 2 | 3.33 |
用户2 | 4 | 2 | 1 | 2.33 |
用户3 | 3 | 4 | 5 | 4.00 |
计算物品A和物品B的调整余弦相似度
第一步: 计算调整后的评分
第二步: 计算分子
第三步: 计算分母
第四步: 计算相似度
代码示例:
import numpy as np
import pandas as pd
def adjusted_cosine_similarity(item_i_ratings, item_j_ratings, user_means):
"""
计算两个物品之间的调整余弦相似度
参数:
item_i_ratings: 物品i的评分列表
item_j_ratings: 物品j的评分列表
user_means: 相应用户的平均评分列表
返回:
调整余弦相似度
"""
# 转换为numpy数组
ratings_i = np.array(item_i_ratings)
ratings_j = np.array(item_j_ratings)
means = np.array(user_means)
# 只考虑两个物品都被评分的用户
common_ratings = np.where((ratings_i > 0) & (ratings_j > 0))[0]
if len(common_ratings) < 2:
return 0 # 需要至少2个共同评分用户
ratings_i_common = ratings_i[common_ratings]
ratings_j_common = ratings_j[common_ratings]
means_common = means[common_ratings]
# 计算调整后的评分
adjusted_i = ratings_i_common - means_common
adjusted_j = ratings_j_common - means_common
# 计算分子和分母
numerator = np.sum(adjusted_i * adjusted_j)
denominator = np.sqrt(np.sum(adjusted_i**2)) * np.sqrt(np.sum(adjusted_j**2))
# 避免除以零
if denominator == 0:
return 0
return numerator / denominator
# 示例使用
itemA_ratings = [5, 4, 3] # 三个用户对物品A的评分
itemB_ratings = [3, 2, 4] # 三个用户对物品B的评分
user_means = [3.33, 2.33, 4.00] # 三个用户的平均评分
# 计算余弦相似度
from sklearn.metrics.pairwise import cosine_similarity
cosine_sim = cosine_similarity([itemA_ratings], [itemB_ratings])[0][0]
print(f"余弦相似度: {cosine_sim:.3f}")
adj_cosine_sim = adjusted_cosine_similarity(itemA_ratings, itemB_ratings, user_means)
print(f"调整余弦相似度: {adj_cosine_sim:.3f}")输出结果:
余弦相似度: 0.919 调整余弦相似度: -0.921
通过结果看出两中评分的差异还是很大的,调整余弦相似度就是要消除评分偏差,比传统余弦相似度更能反映物品之间的真实相似性,特别适合物品数量少于用户数量的场景。
协同过滤主要分为两大类型:基于内存的方法和基于模型的方法。基于内存的方法直接使用整个用户-物品交互数据集进行预测,而基于模型的方法则先从数据中学习一个预测模型,再用这个模型进行预测。
生活化例子:
假设你和朋友小明都喜欢《星际穿越》《盗梦空间》《信条》这三部诺兰电影,且都打了5星。系统发现你们的观影偏好高度相似(相似度90%),而小明最近给《变形金刚》打了5星,那么系统就会把《变形金刚》推荐给你。
实现步骤:
第一步:构建用户-物品评分矩阵
假设有3个用户和4部电影,评分范围1-5(0表示未评分):
用户/电影 | 星际穿越 | 盗梦空间 | 信条 | 变形金刚 |
|---|---|---|---|---|
你 | 5 | 5 | 5 | 0 |
小明 | 5 | 4 | 5 | 5 |
小红 | 2 | 3 | 2 | 0 |
第二步:计算用户相似度
系统会对比你和其他用户的评分向量,计算“相似度分数”。最常用的方法是余弦相似度,可以理解为“两个用户偏好向量之间的夹角大小”——夹角越小,偏好越相似。
在上面的例子中,你和小明的相似度远高于你和小红(接近90% vs 约30%)。
第三步:找到邻居用户并生成推荐
选择与你最相似的K个用户(比如K=10),将他们喜欢但你未评分的物品,按相似度加权平均后推荐给你。
示例:计算用户相似度矩阵
def calculate_pearson_similarity_matrix(ratings_df):
"""
计算所有用户之间的皮尔逊相关系数矩阵
参数:
ratings_df: 用户-物品评分DataFrame
返回:
皮尔逊相似度矩阵DataFrame
"""
n_users = ratings_df.shape[1] # 用户数量
similarity_matrix = np.zeros((n_users, n_users))
# 获取用户列表
users = ratings_df.columns.tolist()
# 计算每对用户之间的相似度
for i in range(n_users):
for j in range(i, n_users): # 只需计算上三角矩阵
if i == j:
similarity_matrix[i, j] = 1.0 # 用户与自己的相似度为1
else:
user_i_ratings = ratings_df[users[i]].values
user_j_ratings = ratings_df[users[j]].values
similarity = pearson_similarity(user_i_ratings, user_j_ratings)
similarity_matrix[i, j] = similarity
similarity_matrix[j, i] = similarity # 对称矩阵
# 转换为DataFrame
similarity_df = pd.DataFrame(
similarity_matrix,
index=users,
columns=users
)
return similarity_df
# 创建示例数据
ratings_data = {
'用户1': [5, 4, 0, 0, 3],
'用户2': [4, 0, 5, 0, 2],
'用户3': [0, 3, 4, 5, 0],
'用户4': [2, 0, 0, 4, 5],
'用户5': [0, 5, 3, 0, 4]
}
movies = ['电影A', '电影B', '电影C', '电影D', '电影E']
ratings_df = pd.DataFrame(ratings_data, index=movies)
# 计算皮尔逊相似度矩阵
pearson_sim_df = calculate_pearson_similarity_matrix(ratings_df)
print("皮尔逊相似度矩阵:")
print(pearson_sim_df)输出结果:
皮尔逊相似度矩阵: 用户1 用户2 用户3 用户4 用户5 用户1 1.0 1.0 0.0 -1.0 1.0 用户2 1.0 1.0 0.0 -1.0 -1.0 用户3 0.0 0.0 1.0 0.0 -1.0 用户4 -1.0 -1.0 0.0 1.0 0.0 用户5 1.0 -1.0 -1.0 0.0 1.0
生活化例子:
如果你买了《哈利波特与魔法石》,电商平台会推荐《哈利波特与密室》。这不是因为系统知道它们都是“奇幻小说”(基于内容的推荐),而是因为数据显示:购买《魔法石》的用户中,有80%也会购买《密室》——系统通过用户行为发现了物品之间的隐藏关联。
实现步骤:
第一步:构建物品-用户评分矩阵
以电影为行,用户为列:
电影 | 你 | 小明 | 小红 |
|---|---|---|---|
星际穿越 | 5 | 5 | 2 |
盗梦空间 | 5 | 4 | 3 |
信条 | 5 | 5 | 2 |
变形金刚 | 0 | 5 | 0 |
第二步:计算物品相似度
分析哪些电影经常被同一批用户喜欢。例如,《星际穿越》和《信条》的评分向量高度相似(你和小明都打了5星,小红打了2星),因此它们的相似度很高。
第三步:生成推荐
对于你喜欢的电影(如《星际穿越》),找出与其最相似的K部电影(如《信条》《奥本海默》),推荐给你。 示例:计算物品相似度矩阵
def calculate_adjusted_cosine_matrix(ratings_df):
"""
计算所有物品之间的调整余弦相似度矩阵
参数:
ratings_df: 用户-物品评分DataFrame(行:物品, 列:用户)
返回:
调整余弦相似度矩阵
"""
n_items = ratings_df.shape[0] # 物品数量
items = ratings_df.index.tolist()
# 计算每个用户的平均评分
user_means = ratings_df.mean(axis=0)
# 初始化相似度矩阵
similarity_matrix = np.zeros((n_items, n_items))
# 计算每对物品之间的相似度
for i in range(n_items):
for j in range(i, n_items): # 只需计算上三角矩阵
if i == j:
similarity_matrix[i, j] = 1.0 # 物品与自身的相似度为1
else:
item_i_ratings = ratings_df.iloc[i].values
item_j_ratings = ratings_df.iloc[j].values
sim = adjusted_cosine_similarity(
item_i_ratings,
item_j_ratings,
user_means.values
)
similarity_matrix[i, j] = sim
similarity_matrix[j, i] = sim # 对称矩阵
# 转换为DataFrame
similarity_df = pd.DataFrame(
similarity_matrix,
index=items,
columns=items
)
return similarity_df
# 创建示例数据
ratings_data = {
'用户1': [5, 4, 0, 0, 3],
'用户2': [4, 0, 5, 0, 2],
'用户3': [0, 3, 4, 5, 0],
'用户4': [2, 0, 0, 4, 5],
'用户5': [0, 5, 3, 0, 4]
}
movies = ['电影A', '电影B', '电影C', '电影D', '电影E']
ratings_df = pd.DataFrame(ratings_data, index=movies)
# 计算调整余弦相似度矩阵
adj_cosine_sim_df = calculate_adjusted_cosine_matrix(ratings_df)
print("调整余弦相似度矩阵:")
print(adj_cosine_sim_df)输出结果:
物品调整余弦相似度矩阵: 电影A 电影B 电影C 电影D 电影E 电影A 1.000000 0.000000 0.000000 0.0 0.070364 电影B 0.000000 1.000000 0.552674 0.0 0.981455 电影C 0.000000 0.552674 1.000000 0.0 0.086630 电影D 0.000000 0.000000 0.000000 1.0 0.000000 电影E 0.070364 0.981455 0.086630 0.0 1.000000
维度 | 基于用户的协同过滤 | 基于物品的协同过滤 |
|---|---|---|
核心对象 | 用户相似度 | 物品相似度 |
适用场景 | 用户少、物品多(如小众社区) | 物品少、用户多(如电商平台) |
稳定性 | 较差(用户偏好易变化) | 较好(物品属性相对稳定) |
推荐解释性 | 难(“和你相似的人喜欢”) | 易(“你喜欢的物品类似”) |
计算复杂度 | 高(用户增长快) | 低(物品更新慢) |
通过机器学习算法从数据中学习一个预测模型。常见的模型包括矩阵分解、聚类模型、深度学习模型等。其中,矩阵分解是最为经典的模型方法,它将用户-物品评分矩阵分解为用户特征矩阵和物品特征矩阵的乘积,通过潜在特征向量来表示用户和物品。
大模型的结合可以很好的解决新用户或新物品缺乏历史数据的问题,也正好解决了疑难的冷启动的问题。
示例:基于大语言模型的混合推荐系统
示例结合了传统的协同过滤方法和现代的大语言模型技术。系统首先使用传统的用户-物品评分矩阵,然后通过大语言模型分析物品的文本描述来计算语义相似度,最终将两种相似度结合来生成更准确的推荐。
2.1.1 数据准备
# 创建示例数据:用户对电影的评分
# 行代表用户,列代表电影,数值代表评分(0表示未评分)
ratings_data = {
'用户1': [5, 4, 3, 0, 0],
'用户2': [4, 5, 0, 2, 1],
'用户3': [3, 0, 4, 5, 2],
'用户4': [0, 3, 5, 4, 3],
'用户5': [2, 1, 0, 3, 4]
}
movies = ['电影A', '电影B', '电影C', '电影D', '电影E']
# 创建评分矩阵
ratings_df = pd.DataFrame(ratings_data, index=movies)
print("用户-电影评分矩阵:")
print(ratings_df)意图说明:
2.1.2 文本相似度计算器
class TextSimilarityCalculator:
"""基于大模型的文本相似度计算器"""
def __init__(self, model_name='D:/modelscope/hub/models/sentence-transformers/all-MiniLM-L6-v2'):
# 初始化代码...
def encode_texts(self, texts):
# 编码文本...
def calculate_similarity(self, text1, text2):
# 计算两个文本的相似度...
def batch_similarity_matrix(self, texts):
# 批量计算相似度矩阵...意图说明:

2.1.3 混合推荐系统
class HybridRecommender:
"""结合传统协同过滤和大模型的混合推荐系统"""
def __init__(self, ratings_df, text_model_name='D:/modelscope/hub/models/sentence-transformers/all-MiniLM-L6-v2'):
# 初始化代码...
def _precompute_item_embeddings(self):
# 预计算物品嵌入...
def calculate_semantic_similarity(self, item_id1, item_id2):
# 计算语义相似度...
def hybrid_similarity(self, item_id1, item_id2, alpha=0.7):
# 计算混合相似度...
def _calculate_cf_similarity(self, item_id1, item_id2):
# 计算协同过滤相似度...
def generate_recommendations(self, target_user_id, top_n=5, alpha=0.7):
# 生成推荐...
def _predict_rating(self, user_id, item_id, alpha):
# 预测评分...意图说明:
用户-电影评分矩阵:
用户1 用户2 用户3 用户4 用户5
电影A 5 4 3 0 2
电影B 4 5 0 3 1
电影C 3 0 4 5 0
电影D 0 2 5 4 3
电影E 0 1 2 3 4
加载模型: D:/modelscope/hub/models/sentence-transformers/all-MiniLM-L6-v2
电影描述语义相似度矩阵:
[[1.0000004 0.78310543 0.8368864 0.7532436 ]
[0.78310543 0.9999992 0.79229146 0.6913045 ]
[0.8368864 0.79229146 0.9999998 0.70960337]
[0.7532436 0.6913045 0.70960337 0.9999997 ]]
电影相似度: 0.791
加载模型: D:/modelscope/hub/models/sentence-transformers/all-MiniLM-L6-v2
推荐: 电影D, 预测评分: 2.10
推荐: 电影E, 预测评分: 1.952.3.1 语义相似度计算
系统使用大语言模型将物品的文本描述转换为高维向量,然后通过计算向量间的余弦相似度来量化物品之间的语义相似性。这种方法能够捕捉到传统协同过滤无法发现的深层语义关系。
2.3.2 混合相似度计算
系统将传统的协同过滤相似度和大语言模型计算的语义相似度进行加权融合:
混合相似度 = α × 协同过滤相似度 + (1-α) × 语义相似度
2.3.3 评分预测
对于目标用户未评分的物品,系统基于用户已评分的相似物品进行评分预测:
预测评分 = 用户平均评分 + ∑[相似度 × (评分 - 物品平均评分)] / ∑|相似度|
这种方法既考虑了用户的整体评分习惯,也考虑了物品的相对受欢迎程度。
2.3.4 基于大模型的相似度计算为推荐系统做了进一步的优化改进:
每种类型的协同过滤都有其适用场景和优缺点。
1. 冷启动问题
2. 数据稀疏性
3. 可扩展性
协同过滤作为推荐系统的经典方法,在处理用户行为数据方面具有不可替代的价值。而大模型的出现为推荐系统带来了语义理解、内容生成和跨模态处理等新能力。两者的结合代表了推荐系统发展的新方向:
传统协同过滤的价值:
大模型的贡献:
随着大模型技术的不断发展和优化,以及计算资源的更加普及,基于协同过滤和大模型的混合推荐系统将成为主流。这种系统不仅能够提供更准确的推荐,还能够提供更加人性化和可解释的推荐体验,真正实现懂你所需、荐你所想的个性化服务。
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import torch
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
# 创建示例数据:用户对电影的评分
# 行代表用户,列代表电影,数值代表评分(0表示未评分)
ratings_data = {
'用户1': [5, 4, 3, 0, 0],
'用户2': [4, 5, 0, 2, 1],
'用户3': [3, 0, 4, 5, 2],
'用户4': [0, 3, 5, 4, 3],
'用户5': [2, 1, 0, 3, 4]
}
movies = ['电影A', '电影B', '电影C', '电影D', '电影E']
# 创建评分矩阵
ratings_df = pd.DataFrame(ratings_data, index=movies)
print("用户-电影评分矩阵:")
print(ratings_df)
class TextSimilarityCalculator:
"""基于大模型的文本相似度计算器"""
def __init__(self, model_name='D:/modelscope/hub/models/sentence-transformers/all-MiniLM-L6-v2'):
"""
初始化文本相似度计算器
参数:
model_name: 预训练模型名称
- 'all-MiniLM-L6-v2': 轻量级通用模型
- 'all-mpnet-base-v2': 高质量通用模型
- 'multi-qa-mpnet-base-dot-v1': 问答优化模型
"""
self.model = SentenceTransformer(model_name)
print(f"加载模型: {model_name}")
def encode_texts(self, texts):
"""
将文本列表编码为向量表示
参数:
texts: 文本列表
返回:
文本向量矩阵
"""
return self.model.encode(texts, convert_to_tensor=True)
def calculate_similarity(self, text1, text2):
"""
计算两个文本之间的相似度
参数:
text1: 第一个文本
text2: 第二个文本
返回:
相似度分数 (0-1)
"""
# 编码文本
embeddings = self.encode_texts([text1, text2])
# 计算余弦相似度
similarity = cosine_similarity(
embeddings[0].cpu().numpy().reshape(1, -1),
embeddings[1].cpu().numpy().reshape(1, -1)
)[0][0]
return similarity
def batch_similarity_matrix(self, texts):
"""
计算文本集合的相似度矩阵
参数:
texts: 文本列表
返回:
相似度矩阵
"""
embeddings = self.encode_texts(texts)
similarity_matrix = cosine_similarity(embeddings.cpu().numpy())
return similarity_matrix
# 使用示例
text_calculator = TextSimilarityCalculator()
# 电影描述示例
movie_descriptions = [
"一部讲述黑客与反乌托邦系统斗争的赛博朋克惊悚片",
"一部设定在未来世界、科技先进的科幻冒险片",
"一部讲述两人在纽约坠入爱河的浪漫喜剧",
"一部以二战为背景的历史剧"
]
# 计算相似度矩阵
similarity_matrix = text_calculator.batch_similarity_matrix(movie_descriptions)
print("电影描述语义相似度矩阵:")
print(similarity_matrix)
# 计算特定文本对相似度
text1 = "一部关于人工智能和机器人的电影"
text2 = "一部探索人类与机器之间关系的电影"
similarity = text_calculator.calculate_similarity(text1, text2)
print(f"\n电影相似度: {similarity:.3f}")
print("=" * 50)
print("混合推荐系统")
print("=" * 50)
class HybridRecommender:
"""结合传统协同过滤和大模型的混合推荐系统"""
def __init__(self, ratings_df, text_model_name='D:/modelscope/hub/models/sentence-transformers/all-MiniLM-L6-v2'):
"""
初始化混合推荐系统
参数:
ratings_df: 用户-物品评分DataFrame
text_model_name: 文本编码模型名称
"""
self.ratings_df = ratings_df
self.text_calculator = TextSimilarityCalculator(text_model_name)
# 预计算物品文本特征
self.item_descriptions = {} # 假设有物品描述数据
self.item_embeddings = self._precompute_item_embeddings()
def _precompute_item_embeddings(self):
"""预计算所有物品的文本嵌入"""
item_ids = self.ratings_df.index.tolist()
descriptions = [self.item_descriptions.get(item_id, "") for item_id in item_ids]
return self.text_calculator.encode_texts(descriptions)
def calculate_semantic_similarity(self, item_id1, item_id2):
"""计算两个物品的语义相似度"""
idx1 = self.ratings_df.index.get_loc(item_id1)
idx2 = self.ratings_df.index.get_loc(item_id2)
emb1 = self.item_embeddings[idx1].cpu().numpy()
emb2 = self.item_embeddings[idx2].cpu().numpy()
return cosine_similarity([emb1], [emb2])[0][0]
def hybrid_similarity(self, item_id1, item_id2, alpha=0.7):
"""
计算混合相似度(协同过滤 + 语义相似度)
参数:
item_id1: 物品1ID
item_id2: 物品2ID
alpha: 协同过滤权重 (1-alpha为语义相似度权重)
"""
# 计算传统协同过滤相似度(基于评分)
cf_similarity = self._calculate_cf_similarity(item_id1, item_id2)
# 计算语义相似度
semantic_similarity = self.calculate_semantic_similarity(item_id1, item_id2)
# 混合相似度
hybrid_sim = alpha * cf_similarity + (1 - alpha) * semantic_similarity
return hybrid_sim
def _calculate_cf_similarity(self, item_id1, item_id2):
"""计算基于评分的协同过滤相似度"""
ratings1 = self.ratings_df.loc[item_id1].values
ratings2 = self.ratings_df.loc[item_id2].values
# 只考虑共同评分的用户
common_ratings = np.where((ratings1 > 0) & (ratings2 > 0))[0]
if len(common_ratings) < 2:
return 0
# 使用皮尔逊相关系数
from scipy.stats import pearsonr
corr, _ = pearsonr(ratings1[common_ratings], ratings2[common_ratings])
return 0 if np.isnan(corr) else corr
def generate_recommendations(self, target_user_id, top_n=5, alpha=0.7):
"""生成混合推荐"""
# 获取目标用户未评分的物品
user_ratings = self.ratings_df[target_user_id]
unrated_items = user_ratings[user_ratings == 0].index
predictions = []
for item_id in unrated_items:
# 计算预测评分
predicted_rating = self._predict_rating(target_user_id, item_id, alpha)
predictions.append((item_id, predicted_rating))
# 返回topN推荐
predictions.sort(key=lambda x: x[1], reverse=True)
return predictions[:top_n]
def _predict_rating(self, user_id, item_id, alpha):
"""预测用户对物品的评分"""
# 基于相似物品的评分进行预测
user_mean = self.ratings_df[user_id].mean()
numerator = 0
denominator = 0
# 找到用户已评分的物品
rated_items = self.ratings_df[user_id][self.ratings_df[user_id] > 0].index
for rated_item in rated_items:
similarity = self.hybrid_similarity(item_id, rated_item, alpha)
rating = self.ratings_df.loc[rated_item, user_id]
item_mean = self.ratings_df.loc[rated_item].mean()
numerator += similarity * (rating - item_mean)
denominator += abs(similarity)
if denominator == 0:
return user_mean
predicted_rating = user_mean + (numerator / denominator)
return max(1, min(5, predicted_rating)) # 确保评分在1-5范围内
# 使用示例
# 假设已有ratings_df和物品描述
hybrid_rec = HybridRecommender(ratings_df)
recommendations = hybrid_rec.generate_recommendations('用户1')
for item_id, rating in recommendations:
print(f"推荐: {item_id}, 预测评分: {rating:.2f}")原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。