You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何对LightFM电影推荐系统的用户-物品交互矩阵做交叉验证?

LightFM交叉验证与性能评估实操指南(带Item Features场景)

看起来你已经在LightFM的路上走了不少步了——用MovieLens数据构建带物品特征的模型,还做了基础的训练测试拆分。关于交叉验证和性能评估,我给你梳理一下实操方法和细节:

一、实现可靠的k折交叉验证

你目前用的random_train_test_split是单次拆分,要全面评估模型的稳定性和泛化能力,k折交叉验证是更好的选择。LightFM专门提供了适配稀疏交互矩阵的KFold工具,按用户维度拆分数据集(更贴合推荐系统评估逻辑),具体代码如下:

import numpy as np
from lightfm import LightFM
from lightfm.cross_validation import KFold
from lightfm.evaluation import precision_at_k, auc_score

# 定义交叉验证折数,比如5折
k_folds = 5
kf = KFold(n_splits=k_folds, random_state=42)  # 加random_state确保结果可复现

# 存储每折的评估结果
precision_scores = []
auc_scores = []

for train_idx, test_idx in kf.split(user_item):
    # 将索引拆分转换为稀疏矩阵格式
    train = user_item[train_idx].tocsr()
    test = user_item[test_idx].tocsr()
    
    # 训练模型(每次都初始化新模型,避免跨折污染)
    model = LightFM(loss='warp', learning_rate=0.01, k=10, random_state=42)
    model.fit(train, item_features=item_features, epochs=50)
    
    # 评估测试集性能
    test_precision = precision_at_k(
        model, test, k=10, 
        item_features=item_features, 
        train_interactions=train  # 排除用户已交互过的物品,避免作弊
    ).mean()
    test_auc = auc_score(
        model, test, 
        item_features=item_features, 
        train_interactions=train
    ).mean()
    
    precision_scores.append(test_precision)
    auc_scores.append(test_auc)

# 计算平均评估结果
mean_precision = np.mean(precision_scores)
mean_auc = np.mean(auc_scores)

print(f"5折交叉验证平均Precision@10: {mean_precision:.2f}")
print(f"5折交叉验证平均AUC: {mean_auc:.2f}")

关键说明

  • LightFM的KFold默认按用户拆分数据集,每一折测试集包含不同的用户子集,这样能评估模型对新用户的推荐能力,更符合真实业务场景。
  • 每次折都初始化新模型,避免上一折的训练结果影响当前折,保证验证的客观性。

二、模型性能评估的核心指标与细节

你已经用到了LightFM官方的precision_at_kauc_score,这两个是推荐系统的核心评估指标,再给你补充细节和扩展:

1. Precision@k

  • 含义:对每个用户,Top-k推荐列表中用户实际交互过的物品占比。数值越高,推荐的精准度越好。
  • 必加参数train_interactions:评估测试集时,这个参数会自动排除用户在训练集中已交互的物品,不会把用户已经看过/喜欢的电影算成“成功推荐”,完全贴合真实推荐场景。

2. AUC Score

  • 含义:对每个用户,随机选一个正样本(用户交互过的物品)和一个负样本(用户未交互的物品),模型给正样本打分高于负样本的概率。AUC越接近1,模型的排序能力越强。
  • 同样需要train_interactions:确保负样本是用户真正没接触过的物品,避免评估偏差。

3. 额外可选评估指标

如果需要更全面的评估,可以用以下指标,用法和上面一致:

from lightfm.evaluation import recall_at_k, ndcg_at_k

test_recall = recall_at_k(
    model, test, k=10, 
    item_features=item_features, 
    train_interactions=train
).mean()
test_ndcg = ndcg_at_k(
    model, test, k=10, 
    item_features=item_features, 
    train_interactions=train
).mean()

print(f"Test Recall@10: {test_recall:.2f}")
print(f"Test NDCG@10: {test_ndcg:.2f}")
  • Recall@k:Top-k推荐列表覆盖用户实际交互物品的比例,衡量推荐的全面性。
  • NDCG@k:考虑推荐顺序的归一化折扣累积增益,越靠前的正确推荐权重越高,更贴近用户对推荐列表的实际感知。

三、现有代码的优化建议

针对你现有的代码,有两个实用优化点:

1. 增加随机种子,确保结果可复现

在拆分数据集和初始化模型时加上random_state,这样每次运行代码的结果一致,方便调试和对比:

train, test = cross_validation.random_train_test_split(
    user_item, test_percentage=0.25, random_state=42
)
model_lightfm = LightFM(
    loss='warp', learning_rate=0.01, k=10, random_state=42
)

2. 推荐函数排除已交互物品

你的推荐函数目前没有排除用户已经看过的电影,可能会把用户已经喜欢的内容重复推荐。修改后更合理:

def recommend(model, user_id, user_item_matrix):
    n_users, n_items = user_item_matrix.shape
    # 获取用户已交互过的物品索引
    interacted_items = user_item_matrix[user_id].indices
    # 预测所有物品得分
    scores = model.predict(user_id, np.arange(n_items), item_features=item_features)
    # 将已交互物品的得分设为负无穷,确保不会被推荐
    scores[interacted_items] = -np.inf
    # 排序取Top10
    top_item_indices = np.argsort(-scores)[:10]
    top_items = metadata['title_clean'].iloc[top_item_indices].values
    
    # 获取用户喜欢的高分电影
    best_rated = ratings_df[(ratings_df.userId == user_id) & (ratings_df.rating >= 4.5)].movieId.values
    known_positives = metadata.loc[metadata['MOVIEID'].isin(best_rated)].title_clean.values
    
    print(f"User {user_id} likes:")
    for k in known_positives[:10]:
        print(k)
    print("\nRecommended:")
    for x in top_items:
        print(x)

# 调用时传入完整的用户-物品矩阵(或训练集,根据你想排除的交互范围)
recommend(model_lightfm, 10, user_item)

内容的提问来源于stack exchange,提问作者xxx

火山引擎 最新活动