推荐系统是现代互联网应用中不可或缺的一部分。无论是电子商务、社交媒体还是内容平台,都在使用各种推荐算法来为用户提供个性化的体验。今天,我们将深入探讨一种简单而有效的推荐算法——简单算法推荐 (Simple Algorithm for Recommendation, SAR),并通过在著名的 MovieLens 数据集上的实践来展示其应用。
SAR 算法简介
SAR 是一种基于用户交互历史的快速、可扩展的个性化推荐算法。它的主要特点包括:
- 生成易于解释和理解的推荐结果
- 能够处理"冷启动物品"和"半冷启动用户"的场景
- 属于基于邻域的算法,旨在为每个用户推荐最合适的物品
SAR 的核心思想是推荐与用户已有偏好相似的物品。这里的"相似"指的是:如果用户与物品 A 交互,那么他们也很可能与物品 B 交互;而"偏好"则指用户在过去与某个物品有过交互。
SAR 的优势
- 高准确度:虽然算法简单,但能达到很好的推荐效果
- 训练速度快:只需要进行简单的计数就可以构建预测时使用的矩阵
- 预测速度快:预测过程仅涉及相似度矩阵与偏好向量的乘法运算
SAR 的使用注意事项
- 由于不使用物品或用户特征,在某些场景下可能不如使用这些特征的算法
- 内存消耗大:需要创建一个 m×m 的稀疏方阵 (m 为物品数量)
- SAR 更适合隐式评分场景,不适合预测具体评分值
数据准备
我们使用 MovieLens 数据集来演示 SAR 算法的应用。 MovieLens 是一个包含用户对电影评分的数据集,非常适合用来测试推荐算法。
首先,我们加载所需的库和数据:
import sys
import logging
import numpy as np
import pandas as pd
from recommenders.datasets import movielens
from recommenders.models.sar import SAR
from recommenders.datasets.python_splitters import python_stratified_split
# 加载 MovieLens 100k 数据集
data = movielens.load_pandas_df(size="100k")
data["rating"] = data["rating"].astype(np.float32)
接下来,我们将数据集分为训练集和测试集:
train, test = python_stratified_split(data, ratio=0.75, col_user="userID", col_item="itemID", seed=42)
这里我们使用了分层抽样的方法,保证每个用户的 75% 评分进入训练集,25% 进入测试集。
模型训练与预测
现在,我们可以开始训练 SAR 模型了:
model = SAR(
col_user="userID",
col_item="itemID",
col_rating="rating",
col_timestamp="timestamp",
similarity_type="jaccard",
time_decay_coefficient=30,
timedecay_formula=True,
normalize=True
)
model.fit(train)
在这个过程中,SAR 会计算物品之间的共现矩阵,然后基于 Jaccard 相似度计算物品相似度矩阵。同时,它还会计算用户-物品亲和度矩阵,捕捉用户与物品之间关系的强度。
训练完成后,我们可以为测试集中的用户生成 Top-K 推荐:
TOP_K = 10
top_k = model.recommend_k_items(test, top_k=TOP_K, remove_seen=True)
模型评估
为了评估 SAR 的性能,我们使用了几个常见的排序指标:
- 平均精度均值 (MAP)
- 归一化折损累积增益 (NDCG)
- 准确率 (Precision)
- 召回率 (Recall)
同时,我们还计算了一些评分指标:
- 均方根误差 (RMSE)
- 平均绝对误差 (MAE)
- R 方 (R-squared)
- 可解释方差 (Explained Variance)
eval_map = map(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K. eval_ndcg = ndcg_at_k(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K)✅
eval_precision = precision_at_k(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K. eval_recall = recall_at_k(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K)✅
eval_rmse = rmse(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
eval_mae = mae(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
eval_rsquared = rsquared(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
eval_exp_var = exp_var(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
这些指标能够全面地评估 SAR 算法在 MovieLens 数据集上的表现。
结论
通过在 MovieLens 数据集上的实验,我们展示了 SAR 算法的实际应用。 SAR 作为一种简单而有效的推荐算法,在许多场景下都能取得不错的效果。它的快速训练和预测特性使其特别适合需要实时推荐的应用场景。
然而,SAR 也有其局限性。例如,它不能利用物品或用户的特征信息,这在某些场景下可能会限制其性能。此外,对于大规模数据集,其内存消耗可能会成为一个挑战。
总的来说,SAR 是推荐系统工具箱中的一个有力工具。在实际应用中,我们需要根据具体的业务需求和数据特征,选择适合的算法或者多种算法的组合。
参考文献
- Recommenders contributors. (2023). SAR Single Node on MovieLens (Python, CPU). GitHub. https://github.com/recommenders-team/recommenders/blob/main/examples/00_quick_start/sar_movielens.ipynb
- Aggarwal, C. C. (2016). Recommender Systems: The Textbook. Springer International Publishing.✅
- Harper, F. M., & Konstan, J. A. (2015). The MovieLens Datasets: History and Context. ACM Transactions on Interactive Intelligent✅
这篇文章将介绍如何使用简单算法推荐 (SAR) 模型对 MovieLens 数据集进行协同过滤推荐。 SAR 是一种基于用户交互历史的快速可扩展的个性化推荐算法, 具有易于解释和处理冷启动问题的优势。让我们一步步来看 SAR 模型的实现过程。
1. 数据准备
首先, 我们加载 MovieLens 数据集并进行预处理:
data = movielens.load_pandas_df(size="100k")
data["rating"] = data["rating"].astype(np.float32)
这里我们使用了 MovieLens 100k 数据集, 包含 10 万条用户对电影的评分记录。
接下来, 我们将数据集分为训练集和测试集:
train, test = python_stratified_split(data, ratio=0.75, col_user="userID", col_item="itemID", seed=42)
我们使用了分层抽样的方法, 保留 75% 的数据作为训练集,25% 作为测试集。这种分割方法可以确保每个用户在训练集和测试集中都有数据。
2. SAR 模型训练
SAR 模型的核心思想是基于用户的历史交互来推荐相似的物品。我们首先实例化 SAR 模型:
model = SAR(
col_user="userID",
col_item="itemID",
col_rating="rating",
col_timestamp="timestamp",
similarity_type="jaccard",
time_decay_coefficient=30,
timedecay_formula=True,
normalize=True
)
这里我们使用了 Jaccard 相似度, 并启用了时间衰减和归一化。
然后我们开始训练模型:
model.fit(train)
SAR 模型的训练过程主要包括以下步骤:
- 计算物品共现矩阵
- 基于共现矩阵计算物品相似度矩阵
- 计算用户-物品亲和度矩阵
训练完成后, 我们可以为测试集中的用户生成 Top-K 推荐:
top_k = model.recommend_k_items(test, top_k=TOP_K, remove_seen=True)
3. 模型评估
为了评估 SAR 模型的性能, 我们使用了多个常用的推荐系统评估指标:
eval_map = map(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K. eval_ndcg = ndcg_at_k(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K)✅
eval_precision = precision_at_k(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K. eval_recall = recall_at_k(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K)✅
这些指标包括平均精度均值 (MAP) 、归一化折损累积增益 (NDCG) 、精确率和召回率。
此外, 我们还计算了一些评分预测相关的指标:
eval_rmse = rmse(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
eval_mae = mae(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
eval_rsquared = rsquared(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
eval_exp_var = exp_var(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
这些指标包括均方根误差 (RMSE) 、平均绝对误差 (MAE) 、 R 方和解释方差。
4. 结果分析
在 MovieLens 100k 数据集上,SAR 模型的表现如下:
- MAP: 0.106959
- NDCG: 0.379533
- Precision@K. 0.331071✅
- Recall@K. 0.176837✅
- RMSE: 1.229246
- MAE: 1.033912
这些结果表明,SAR 模型在推荐相关性和排序质量方面表现不错, 但在评分预测方面还有提升空间。
我们还可以查看特定用户的推荐结果:
user_id = 54
ground_truth = test[test["userID"] == user_id].sort_values(by="rating", ascending=False)[:TOP_K]
prediction = model.recommend_k_items(pd.DataFrame(dict(userID=[user_id])), remove_seen=True)
df = pd.merge(ground_truth, prediction, on=["userID", "itemID"], how="left")
这让我们能够直观地比较模型的推荐结果和用户的实际偏好。
5. 总结与展望
SAR 模型作为一种简单高效的协同过滤算法, 在 MovieLens 数据集上展现了不错的性能。它的优势在于:
- 训练和预测速度快
- 可解释性强
- 能够处理冷启动问题
然而,SAR 模型也存在一些局限性:
- 不使用用户和物品特征, 可能在某些场景下处于劣势
- 对于大规模数据集, 内存消耗较大
未来的改进方向可以包括:
- 结合内容特征, 构建混合推荐系统
- 使用更高级的相似度计算方法
- 探索模型集成, 如将 SAR 与矩阵分解方法结合
总的来说,SAR 模型为协同过滤推荐系统提供了一个简单而有效的 baseline, 值得在实际应用中尝试和优化。
简单算法推荐 (SAR) 在 MovieLens 数据集上的应用
简单算法推荐 (SAR) 是一种快速且可扩展的个性化推荐算法,它基于用户交易历史进行推荐。 SAR 生成的推荐易于解释和理解,并能处理 「冷启动物品」 和 「半冷启动用户」 场景。 SAR 属于一种基于邻域的算法,如 Aggarwal 的推荐系统 中所述,旨在为每个用户排名最顶级的物品。有关 SAR 的更多详细信息,请参阅 深入研究笔记本。
SAR 推荐与用户已有 亲和力 的物品最 相似 的物品。如果与一个物品交互的用户也可能与另一个物品交互,则这两个物品是 相似 的。如果用户过去与某个物品交互过,则该用户对该物品具有 亲和力。
SAR 的优势:
- 对于易于训练和部署的算法,具有很高的准确性。
- 快速训练,只需要简单的计数来构建预测时使用的矩阵。
- 快速评分,只需要将相似度矩阵与亲和度向量相乘。
正确使用 SAR 的注意事项:
- 由于它不使用物品或用户特征,因此在与使用特征的算法相比可能处于劣势。
- 它需要大量的内存,需要创建一个 $mxm$ 的稀疏方阵 (其中 $m$ 是物品的数量) 。这对于许多矩阵分解算法来说也是一个问题。
- SAR 倾向于隐式评分场景,它不预测评分。
本篇文章将以 Python 在 CPU 上为例,展示如何使用和评估 SAR 。
1. 加载数据
SAR 旨在用于以下模式的交互:< 用户 ID>, < 物品 ID>,< 时间>,[< 事件类型>], [< 事件权重>]
。
每行代表用户和物品之间的一次交互。这些交互可能是电子商务网站上不同类型的事件,例如用户点击查看物品、将物品添加到购物车、点击推荐链接等等。每个事件类型可以分配不同的权重,例如,我们可以将 「购买」 事件的权重分配为 10,而 「查看」 事件的权重可能只有 1 。
MovieLens 数据集格式良好,包含用户对电影的评分 (电影评分用作事件权重)——我们将使用它作为本篇文章的示例。
1.1 下载并使用 MovieLens 数据集
# top k 个推荐的物品
TOP_K = 10
# 选择 MovieLens 数据集大小:100k 、 1m 、 10m 或 20m
MOVIELENS_DATA_SIZE = "100k"
# 下载并加载 MovieLens 数据集
data = movielens.load_pandas_df(
size=MOVIELENS_DATA_SIZE
)
# 将浮点精度转换为 32 位以减少内存消耗
data["rating"] = data["rating"].astype(np.float32)
1.2 使用实用程序提供的 Python 随机拆分器拆分数据
我们将完整数据集拆分为 train
和 test
数据集,以评估算法在训练期间未见过的保留集上的性能。由于 SAR 根据用户偏好生成推荐,因此测试集中所有用户也必须存在于训练集中。在这种情况下,我们可以使用提供的 python_stratified_split
函数,该函数从每个用户中保留一定比例 (在本例中为 25%) 的物品,但确保所有用户都存在于 train
和 test
数据集中。 dataset.python_splitters
模块中提供了其他选项,可以更详细地控制拆分方式。
train, test = python_stratified_split(data, ratio=0.75, col_user="userID", col_item="itemID", seed=42)
2. 训练 SAR 模型
2.1 实例化 SAR 算法并设置索引
我们将使用 SAR 的单节点实现,并指定列名以匹配我们的数据集 (时间戳是一个可选列,如果您的数据集不包含它,可以删除) 。
其他选项用于控制算法的行为,如 深入研究笔记本 中所述。
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)-8s %(message)s')
model = SAR(
col_user="userID",
col_item="itemID",
col_rating="rating",
col_timestamp="timestamp",
similarity_type="jaccard",
time_decay_coefficient=30,
timedecay_formula=True,
normalize=True
)
2.2 在我们的训练数据上训练 SAR 模型,并为我们的测试数据获取 top-k 推荐
SAR 首先计算一个物品到物品的 共现矩阵。共现表示两个物品在任何给定用户中同时出现的次数。一旦我们有了共现矩阵,我们就可以通过给定指标 (本例中为 Jaccard 相似度) 重新缩放共现来计算 物品相似度矩阵。
我们还计算一个 亲和度矩阵 来捕获每个用户与每个物品之间关系的强度。亲和度由不同的类型 (如 评分 或 观看 电影) 以及事件发生的时间驱动。
推荐是通过将亲和度矩阵 $A$ 与相似度矩阵 $S$ 相乘来实现的。结果是一个 推荐评分矩阵 $R$。我们在下面看到的 recommend_k_items
函数中计算了 test
数据集中每个用户的 top-k 结果。
SAR 算法的完整演练可以在这里找到 here 。
with Timer() as train_time:
model.fit(train)
print("Took {} seconds for training.".format(train_time.interval))
with Timer() as test_time:
top_k = model.recommend_k_items(test, top_k=TOP_K, remove_seen=True)
print("Took {} seconds for prediction.".format(test_time.interval))
2.3 评估 SAR 的性能
我们评估 SAR 在 python_evaluation
模块中提供的几个常见排名指标上的性能。我们将考虑平均平均精度 (MAP) 、归一化折损累积增益 (NDCG) 、精确度和召回率,这些指标是针对我们使用 SAR 计算的每个用户的 top-k 物品。用户、物品和评分列名在每个评估方法中都已指定。
# 排名指标
eval_map = map(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K. eval_ndcg = ndcg_at_k(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K)✅
eval_precision = precision_at_k(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K. eval_recall = recall_at_k(test, top_k, col_user="userID", col_item="itemID", col_rating="rating", k=TOP_K)✅
# 评分指标
eval_rmse = rmse(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
eval_mae = mae(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
eval_rsquared = rsquared(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
eval_exp_var = exp_var(test, top_k, col_user="userID", col_item="itemID", col_rating="rating")
positivity_threshold = 2
test_bin = test.copy()
test_bin["rating"] = binarize(test_bin["rating"], positivity_threshold)
top_k_prob = top_k.copy()
top_k_prob["prediction"] = minmax_scale(top_k_prob["prediction"].astype(float))
eval_logloss = logloss(
test_bin, top_k_prob, col_user="userID", col_item="itemID", col_rating="rating"
)
print("Model:\t",
"Top K. \t%d" % TOP_K,✅
"MAP:\t%f" % eval_map,
"NDCG:\t%f" % eval_ndcg,
"Precision@K. \t%f" % eval_precision,✅
"Recall@K. \t%f" % eval_recall,✅
"RMSE:\t%f" % eval_rmse,
"MAE:\t%f" % eval_mae,
"R2:\t%f" % eval_rsquared,
"Exp var:\t%f" % eval_exp_var,
"Logloss:\t%f" % eval_logloss,
sep='\n')
# 现在让我们看看特定用户的结果
user_id = 54
ground_truth = test[test["userID"] == user_id].sort_values(
by="rating", ascending=False
)[:TOP_K]
prediction = model.recommend_k_items(
pd.DataFrame(dict(userID=[user_id])), remove_seen=True
)
df = pd.merge(ground_truth, prediction, on=["userID", "itemID"], how="left")
df.head(10)
上面,我们看到测试集中评分最高的物品之一被模型的 top-k 推荐所恢复,但其他物品则没有。离线评估很困难,因为它们只能使用测试集中以前看到的内容,可能无法代表用户在整个物品集中的实际偏好。对数据拆分方式、算法使用方式和超参数的调整可以改善这里的结果。
参考文献
希望这篇文章能帮助您更好地理解 SAR 算法及其在 MovieLens 数据集上的应用。