衡量推荐系统预测评分的准确性,包括:
Precision@K(前 K 个推荐的准确率):
Recall@K(前 K 个推荐的召回率):
适用场景:用于 Top-K 推荐,比如推荐 10 部电影给用户,看有多少部是用户喜欢的。
计算方式:
适用场景:用于衡量推荐列表的排序质量,排名越靠前的正确推荐权重越高。
定义:衡量推荐系统覆盖的物品范围,即推荐的不同物品数量占总物品数的比例:
目的:防止推荐系统只推荐热门物品,确保长尾物品(冷门物品)也能被推荐。
定义:衡量推荐是否包含用户不常见或未见过的物品。
常见度衡量:可以用推荐物品的流行度(受欢迎程度)来反向衡量:
目的:鼓励推荐系统挖掘冷门物品,提升探索性。
适用场景:
定义:衡量推荐列表中不同类型物品的丰富程度。
目的:避免推荐过于集中在某个品类(比如用户喜欢科幻电影,但系统全推荐科幻片)。
适用场景:
指标 | 作用 | 适用场景 |
---|---|---|
RMSE | 预测评分误差,惩罚大误差 | 评分预测任务(电影评分) |
MAE | 预测评分误差,均等权重 | 评分预测任务(电影评分) |
Precision@K | 前 K 个推荐的准确率 | Top-K 推荐(电影、新闻等) |
Recall@K | 前 K 个推荐的召回率 | Top-K 推荐(电影、新闻等) |
NDCG | 考虑排序的推荐质量 | 需要精准排序的推荐 |
Coverage | 物品覆盖率 | 避免热门物品垄断 |
Novelty | 推荐新奇度 | 提高探索性,避免千篇一律 |
Diversity | 推荐多样性 | 防止推荐单一类型物品 |
计算效率 | 训练时间、预测时间 | 大规模数据集上的推荐 |
不同指标适用于不同的推荐系统任务,准确性指标通常与多样性、新颖度相冲突,在设计推荐系统时需要权衡取舍。
user obj tag dnn rnn
RMSE 0.9753 0.9978 0.9977 0.8297 0.8465
MAE 0.7703 0.7788 0.7791 0.6572 0.6667
Coverage 0.9281 0.9272 0.9262 0.9983 0.9971
NDCG@10
0.9373 0.8988 0.8999 0.9091 0.9163
Precision@10
0.6663 0.6565 0.6567 0.7005 0.6917
Recall@10
0.628 0.6207 0.6175 0.648 0.6452
算法 | RMSE |
---|---|
基于用户的协同过滤 | 0.9753 |
基于物品的协同过滤 | 0.9978 |
基于标签的推荐 | 0.9977 |
DNN | 0.8297 |
RNN | 0.8465 |
分析:
算法 | MAE |
---|---|
基于用户的协同过滤 | 0.7703 |
基于物品的协同过滤 | 0.7788 |
基于标签的推荐 | 0.7791 |
DNN | 0.6572 |
RNN | 0.6667 |
分析:
算法 | Coverage |
---|---|
基于用户的协同过滤 | 0.9281 |
基于物品的协同过滤 | 0.9272 |
基于标签的推荐 | 0.9262 |
DNN | 0.9983 |
RNN | 0.9971 |
分析:
算法 | NDCG@10 |
---|---|
基于用户的协同过滤 | 0.9373 |
基于物品的协同过滤 | 0.8988 |
基于标签的推荐 | 0.8999 |
DNN | 0.9091 |
RNN | 0.9163 |
分析:
算法 | Precision@10 |
---|---|
基于用户的协同过滤 | 0.6663 |
基于物品的协同过滤 | 0.6565 |
基于标签的推荐 | 0.6567 |
DNN | 0.7005 |
RNN | 0.6917 |
分析:
算法 | Recall@10 |
---|---|
基于用户的协同过滤 | 0.628 |
基于物品的协同过滤 | 0.6207 |
基于标签的推荐 | 0.6175 |
DNN | 0.648 |
RNN | 0.6452 |
分析:
,如根据物品相似性进行推荐的场景。
好的,我们结合每个推荐算法的特点来分析实验结果出现这些表现的原因。不同的推荐算法有不同的工作原理、优缺点以及适用场景,这些都会直接影响它们的性能表现。
确实,可以通过结合多种推荐算法的优点来创建一个复合推荐系统(Ensemble Recommendation System)。复合推荐系统旨在利用不同推荐算法在不同场景下的优势,以期获得比单一算法更好的推荐质量。
通过为不同算法分配权重,根据它们在不同指标上的表现来加权组合推荐结果。例如,可以根据每个算法的 RMSE、NDCG 等指标表现分配权重,将更优秀的算法给与更高的权重。
公式:
[
\hat{r}{u,i} = w_1 \cdot \hat{r}{u,i}^{CF} + w_2 \cdot \hat{r}{u,i}^{SVD} + w_3 \cdot \hat{r}{u,i}^{DNN} + \dots
]
其中,(\hat{r}_{u,i}) 表示用户 (u) 对物品 (i) 的预测评分,(w_1, w_2, w_3) 是权重系数,CF、SVD、DNN 等分别表示基于协同过滤、矩阵分解、DNN 的推荐。
优点:
缺点:
对每个算法推荐的物品进行排序,将所有推荐结果按某种规则(如基于算法得分、算法性能等)进行融合和排序,最后给用户提供排名前 K 的推荐项。
常见的排序方法有 Borda Count、Position-Weighted 等:
优点:
缺点:
混合策略是通过不同算法的组合实现更加多样化的推荐,结合每种算法的优势。例如,基于用户协同过滤(UserCF)可能提供较好的 NDCG、Precision@K,而 SVD 和 DNN 则在 RMSE 和 MAE 方面表现较好。可以通过如下方式来实现混合:
优点:
缺点:
这种方法将多个推荐算法的模型结合,形成一个新的联合模型。例如,可以将协同过滤、SVD 和 DNN 的预测结果作为输入,训练一个联合模型(如回归模型、决策树、神经网络等)来预测最终的推荐结果。
优点:
缺点:
根据你的需求,可以选择合适的复合推荐算法策略:
对于精准推荐和排名较为重视的场景(如电影、商品推荐等),可以考虑 加权平均 或 排名级联,这种方法能有效平衡准确性和排序质量。
对于多样性和覆盖度要求较高的场景(如新闻推荐、内容发现等),可以使用 混合策略 和 基于模型的混合。这种方式不仅能提高准确性,还能增加推荐结果的多样性和新颖性。
对于大规模数据集,考虑到计算开销和复杂性,加权平均 或 排名级联 是较为可行的选择,而对于数据较为复杂或需要动态调整的系统, 混合策略 会更加灵活。
针对电影推荐系统的复合推荐策略,需要考虑电影推荐的特性:用户偏好的多样性、电影内容的多样性、冷启动问题(新用户和新电影的推荐)、以及用户的长期行为模式等。因此,复合推荐算法的设计应该尽量综合不同算法的优点,解决不同情境下的局限性。
基于上述挑战,结合不同推荐算法的优缺点,我们可以选择以下几种复合推荐策略:
适用场景:对于推荐系统中各种算法的平衡使用,可以将 基于用户的协同过滤、基于物品的协同过滤、SVD 和 DNN 等算法的推荐结果进行加权平均。
如何加权:可以根据每个算法在特定指标上的表现(如 RMSE、NDCG、Precision@K 等)进行加权。例如,基于协同过滤的算法在 NDCG 和 Precision@K 上表现好,而 SVD 和 DNN 在 RMSE 和 Coverage 上表现更优。
例如,假设:
适用场景:当我们不想过多依赖于某一算法,而是希望通过融合不同算法的推荐结果来得到综合性的排名时,排名级联是一种很好的策略。通过对不同推荐算法的推荐结果进行排序,并根据加权得分(或物品出现的次数)进行融合,最终为用户提供排名前 K 的推荐。
这种方法能够平衡各算法在推荐准确性、排序质量、覆盖度等方面的优势。例如:
Borda Count 排名方法可以应用于此场景,每个算法根据排名给物品打分,并将这些得分加总,最后排名分数高的电影进入最终推荐。
适用场景:当我们希望综合多个推荐算法的优势,并且在不同情境下做出动态调整时,可以使用 混合策略。例如,可以在不同用户群体或不同电影类型上选择不同的推荐算法:
这种方法的关键是通过动态选择不同算法为不同的用户群体提供个性化的推荐。例如,可以根据用户的历史行为来确定其是“活跃用户”还是“冷启动用户”,并根据此选择相应的推荐算法。
对于电影推荐系统,建议采用 混合策略,结合 基于内容的推荐、协同过滤、SVD、DNN 和 RNN 等算法,通过 加权平均、排名级联 和 基于模型的混合 等方法实现复合推荐。不同算法的组合能够有效应对电影推荐中的冷启动问题、准确度、覆盖度以及多样性等挑战,同时能更好地平衡短期和长期兴趣,提供更加个性化和精准的推荐。
你观察到的现象是,由于混合模型(例如线性回归或神经网络)将多个算法的预测结果组合成一个新的预测,它有时会表现为“取中间值”或“折衷”,从而没有显著提高性能。这种情况通常发生在以下几个原因:
调整混合模型的权重:
使用更复杂的混合模型:
增加训练数据和多样化算法:
调整各个算法的超参数:
尝试不同的组合方式:
一种简单的改进方法是给每个模型的输出添加不同的权重,来调整它们对最终预测的贡献。
# 假设你已经获得了各个模型的预测结果 user_cf_preds, item_cf_preds, svd_preds
# 将这些模型的预测结果和训练数据结合,在回归模型中赋予不同权重
from sklearn.linear_model import Ridge
# 训练加权回归模型
# 假设我们使用 Ridge 回归(也可以使用其他回归模型),并调整 alpha 参数来控制正则化
X_train = np.array([user_cf_preds, item_cf_preds, svd_preds]).T # 注意这里是转置,因为每行对应一个样本的所有预测
y_train = np.array(y_train)
ridge_model = Ridge(alpha=1.0) # alpha 是正则化参数,可以调节
ridge_model.fit(X_train, y_train)
# 对测试集进行预测
X_test = np.array([user_cf_preds_test, item_cf_preds_test, svd_preds_test]).T
hybrid_preds = ridge_model.predict(X_test)
# 评估混合模型
rmse = np.sqrt(np.mean((hybrid_preds - y_test) ** 2))
mae = np.mean(np.abs(hybrid_preds - y_test))
如果你遇到了混合模型性能没有显著提升的问题,通常是由于模型间差异不大、模型权重分配不当或数据问题等。通过调整模型权重、尝试不同的组合方法以及使用更复杂的模型(如深度学习或集成学习),可以帮助提升混合模型的性能。
为了提高你的混合模型的性能,我们可以尝试一些优化方法,具体可以从以下几个方面入手:
增加模型的多样性:除了现有的协同过滤和矩阵分解(SVD),可以增加一些其他类型的推荐算法(如基于内容的推荐、基于标签的推荐、神经网络等),使得模型的多样性更高。
调整权重:我们可以使用加权回归或更复杂的回归模型(如 Ridge、Lasso 或 XGBoost)来调整每个模型的权重,从而避免简单的线性组合可能出现的“中间值”现象。
模型集成方法(Stacking):我们可以尝试集成多个模型的输出,使用一个学习算法来训练它们的加权组合。Stacking 方法可以有效结合不同模型的优势。
调参:优化每个单独模型的参数,确保每个算法在单独执行时表现最好。例如,调整 SVD 的秩、KNN 的邻居数等。
下面我将基于你的现有代码,展示如何对模型进行优化,主要通过权重加权回归(Ridge)、Stacking 和进一步的超参数调整来提升性能。
import os
import zipfile
import urllib.request
import pandas as pd
from surprise import Dataset, Reader, KNNBasic, SVD, accuracy
from surprise.model_selection import train_test_split
from sklearn.linear_model import Ridge
from sklearn.model_selection import train_test_split as sklearn_train_test_split
import numpy as np
from collections import defaultdict
import matplotlib.pyplot as plt
# 数据集下载链接
dataset_url = "https://files.grouplens.org/datasets/movielens/ml-1m.zip"
dataset_path = "./ml-1m"
# 检查数据集是否存在,如果不存在则下载
if not os.path.exists(dataset_path):
print("正在下载 MovieLens 1M 数据集...")
urllib.request.urlretrieve(dataset_url, "ml-1m.zip")
print("下载完成,正在解压...")
with zipfile.ZipFile("ml-1m.zip", "r") as zip_ref:
zip_ref.extractall(".")
print("数据集解压完成!")
# 读取 MovieLens 1M 数据集
file_path = os.path.join(dataset_path, "ratings.dat")
columns = ["user_id", "item_id", "rating", "timestamp"]
df = pd.read_csv(file_path, sep="::", names=columns, engine='python')
# 使用 Surprise 读取数据
reader = Reader(line_format='user item rating timestamp', sep='::', rating_scale=(1, 5))
data = Dataset.load_from_df(df[['user_id', 'item_id', 'rating']], reader)
trainset, testset = train_test_split(data, test_size=0.2)
# 训练基于用户的协同过滤模型
sim_options = {
'name': 'cosine', # 余弦相似度
'user_based': True # 基于用户的协同过滤
}
algo_user_cf = KNNBasic(sim_options=sim_options)
algo_user_cf.fit(trainset)
# 训练基于物品的协同过滤模型
sim_options['user_based'] = False # 基于物品的协同过滤
algo_item_cf = KNNBasic(sim_options=sim_options)
algo_item_cf.fit(trainset)
# 训练矩阵分解模型(SVD)
algo_svd = SVD()
algo_svd.fit(trainset)
# 获取每个模型的预测评分
def get_predictions(algo, testset):
predictions = algo.test(testset)
return {f"{uid}_{iid}": est for uid, iid, _, est, _ in predictions}
# 获取每个模型的预测评分
user_cf_preds = get_predictions(algo_user_cf, testset)
item_cf_preds = get_predictions(algo_item_cf, testset)
svd_preds = get_predictions(algo_svd, testset)
# 准备混合模型的训练数据
X_train = []
y_train = []
for uid, iid, true_r in testset: # 只解包成三个值
key = f"{uid}_{iid}"
if key in user_cf_preds and key in item_cf_preds and key in svd_preds:
X_train.append([user_cf_preds[key], item_cf_preds[key], svd_preds[key]])
y_train.append(true_r)
X_train = np.array(X_train)
y_train = np.array(y_train)
# 训练加权回归模型(Ridge)
ridge_model = Ridge(alpha=1.0) # alpha 是正则化参数,可以调节
ridge_model.fit(X_train, y_train)
# 对测试集进行预测
X_test = []
y_test = []
for uid, iid, true_r in testset: # 只解包成三个值
key = f"{uid}_{iid}"
if key in user_cf_preds and key in item_cf_preds and key in svd_preds:
X_test.append([user_cf_preds[key], item_cf_preds[key], svd_preds[key]])
y_test.append(true_r)
X_test = np.array(X_test)
y_test = np.array(y_test)
# 使用混合模型进行预测
hybrid_preds = ridge_model.predict(X_test)
# 评估混合模型
rmse = np.sqrt(np.mean((hybrid_preds - y_test) ** 2))
mae = np.mean(np.abs(hybrid_preds - y_test))
# 计算覆盖度(Coverage)
unique_items_train = set([i for _, i, _ in trainset.all_ratings()])
unique_items_test = set([i for _, i, _ in testset])
coverage = len(unique_items_test.intersection(unique_items_train)) / len(unique_items_test)
# 计算多样性(基于相似度的平均值)
sims_matrix = algo_user_cf.sim
avg_similarity = np.mean(sims_matrix)
# 计算 NDCG(归一化折损累积增益)
def dcg_at_k(r, k):
r = np.asfarray(r)[:k]
return np.sum(r / np.log2(np.arange(2, r.size + 2)))
def ndcg_at_k(recommended, relevant, k=10):
idcg = dcg_at_k(sorted(relevant, reverse=True), k)
if idcg == 0:
return 0
return dcg_at_k(recommended, k) / idcg
# 计算 Precision@K 和 Recall@K
def precision_recall_at_k(predictions, true_ratings, k=10, threshold=3.5):
user_est_true = defaultdict(list)
# 从 testset 中获取 uid 和 iid
for (uid, iid, true_r), est in zip(testset, predictions): # 用 zip 来配对 testset 和预测结果
user_est_true[uid].append((est, true_r))
precisions = []
recalls = []
for uid, ratings in user_est_true.items():
ratings.sort(reverse=True, key=lambda x: x[0])
recommended = [r[0] for r in ratings[:k]]
relevant = [r[1] >= threshold for r in ratings]
precision = sum(relevant[:k]) / k
recall = sum(relevant[:k]) / sum(relevant) if sum(relevant) != 0 else 0
precisions.append(precision)
recalls.append(recall)
avg_precision = np.mean(precisions)
avg_recall = np.mean(recalls)
return avg_precision, avg_recall
# 计算推荐的 NDCG
user_est_true = defaultdict(list)
for (uid, iid, true_r), est in zip(testset, hybrid_preds): # 用 zip 来配对 testset 和预测结果
user_est_true[uid].append((est, true_r))
ndcg_scores = []
for uid, ratings in user_est_true.items():
ratings.sort(reverse=True, key=lambda x: x[0])
recommended = [r[0] for r in ratings]
relevant = [r[1] for r in ratings]
ndcg_scores.append(ndcg_at_k(recommended, relevant, k=10))
average_ndcg = np.mean(ndcg_scores)
# 计算 Precision@10 和 Recall@10
precision, recall = precision_recall_at_k(hybrid_preds, y_test, k=10)
# 输出结果
print(f"RMSE: {rmse:.4f}")
print(f"MAE: {mae:.4f}")
print(f"Coverage: {coverage:.4f}")
print(f"Average Similarity: {avg_similarity:.4f}")
print(f"NDCG@10: {average_ndcg:.4f}")
print(f"Precision@10: {precision:.4f}")
print(f"Recall@10: {recall:.4f}")
加权回归(Ridge):使用 Ridge 回归 来组合不同推荐算法的输出。与普通回归相比,Ridge 回归在处理多重共线性时更为鲁棒,可以提高预测性能。
ridge_model = Ridge(alpha=1.0) # alpha 是正则化参数,可以调节
ridge_model.fit(X_train, y_train)
Stacking(堆叠模型):我们通过组合多个推荐算法的预测结果作为输入特征,通过加权回归模型来进行最终的预测。这样的做法使得混合模型能够更好地利用不同推荐算法的优势。
模型评估:我们继续使用常见的评估指标,包括 RMSE、MAE、Coverage、NDCG@10、Precision@10 和 Recall@10,来全面评估混合模型的表现。
并无提高
你的混合推荐系统(结合了基于协同过滤、SVD 和 DNN)中,Coverage
可能有所提升,但其他指标(如 RMSE
、MAE
、Precision
、Recall
等)未能显著提升,原因可能有以下几个方面:
目前,你使用了基于 线性回归(LinearRegression
)来融合传统推荐算法(协同过滤、SVD)和 DNN 模型的结果。线性回归本质上是一个线性模型,它可能无法充分捕捉不同模型间的复杂非线性关系,尤其是对于推荐系统中的数据和特征,可能不足以提升性能。
建议:
虽然引入了 DNN 模型,但 DNN 的效果可能没有很明显的提升,可能有以下原因:
LinearRegression
来将不同模型(基于协同过滤、SVD、DNN)的输出融合。LinearRegression
的预测能力较弱,它可能无法充分利用 DNN 和 SVD 等模型的复杂性。1-5
)。不一致的评分尺度可能会影响模型融合的效果。RMSE
和 MAE
是基于数值误差的评估指标,但它们可能无法全面反映推荐系统的性能。特别是在推荐系统中,用户可能并不关心预测评分与真实评分的具体差距,而是更关注 Precision
和 Recall
,这些指标能够反映模型的推荐质量。在设计基于 DNN 的推荐系统时,通常会使用 嵌入层(Embedding layers) 来表示用户和物品,DNN 模型通常有以下优势:
然而,DNN 的性能很大程度上取决于其架构和超参数的选择。如果没有合理的结构设计或者足够的训练,DNN 可能不会比传统的协同过滤和 SVD 方法带来显著提升。
希望这些思路能帮助你进一步优化混合推荐系统。如果你有更详细的实验设置或数据特征,也可以进一步讨论。
无明显提升
如果在使用 加权回归(Ridge 回归) 和 堆叠模型 后没有明显提升,可能是以下几个原因:
alpha
是正则化的超参数。如果这个参数未调优,可能会导致回归模型欠拟合或过拟合,影响模型的性能。GridSearchCV
或 RandomizedSearchCV
)来找到最优的 alpha
值。目前你使用的是 加权回归(Ridge 回归)作为融合模型,但有时通过简单的加权回归可能无法充分利用各个模型的优势。
堆叠模型(Stacking)通常涉及多个不同的基学习器(如决策树、支持向量机、神经网络等),这些基学习器的预测结果被用作新的特征输入到一个元学习器(如回归模型、逻辑回归等)中。在你的实现中,可能还可以进一步改进模型融合策略,尝试不同的融合方式,例如:
alpha
。调优 Ridge 回归的 alpha
:尝试调整 alpha
参数来避免过拟合或欠拟合。
from sklearn.model_selection import GridSearchCV
# 设置参数网格
param_grid = {'alpha': [0.01, 0.1, 1, 10, 100]}
ridge_grid = GridSearchCV(Ridge(), param_grid, cv=5)
ridge_grid.fit(X_train, y_train)
best_alpha = ridge_grid.best_params_['alpha']
ridge_model = Ridge(alpha=best_alpha)
ridge_model.fit(X_train, y_train)
加入更多样化的基础模型:考虑加入更多的基于内容的推荐算法,或者基于深度学习的模型。比如加入基于用户或物品嵌入的神经网络。
使用更强的堆叠元模型:尝试使用 XGBoost 或 LightGBM 等强大的树模型作为堆叠的元模型,这样能更好地学习各基础模型的加权关系。
更多特征工程:除了用户和物品的 ID,还可以尝试加入一些外部特征(如用户的行为模式、物品的类别等)。
增加模型的多样性:尝试不同类型的基础模型(如基于深度学习的模型、矩阵分解模型、树模型等),然后将它们的预测结果作为输入进行融合。
堆叠模型的多阶段训练:你可以将堆叠模型划分为多个阶段,比如:
堆叠模型和加权回归可能未能带来明显的提升,主要可能是由于基础模型表现已经接近最优、模型融合方式过于简单、缺乏足够的模型多样性等原因。可以通过调整模型、增强特征、多样化模型选择、使用更强的元模型等方式进一步改进系统的性能。