如何基于截断SVD计算MovieLens测试集的RMSE?
问题描述
我正在尝试对拆分后的MovieLens数据集使用截断SVD计算RMSE。原始数据集是610用户×9724电影的评分矩阵(评分范围1-5),用model_selection.train_test_split拆分后,训练集维度为610×9379,验证集维度为603×3653。对训练集执行SVD后,能计算训练矩阵与预测矩阵A=U·S·Vᵀ的RMSE,但测试集的维度和电影集合与训练集不同,不知道怎么计算预测评分与测试集的RMSE。
解决思路
核心问题是训练集和验证集的用户、电影集合不完全重叠,直接用矩阵运算会出现维度不匹配。推荐从原始长表(而非宽表)入手,对验证集的每条评分条目单独生成预测值,再计算RMSE,这种方式更高效且避免维度对齐的麻烦:
步骤说明
- 保留索引映射:从训练宽表中提取
userId到行索引、movieId到列索引的字典映射,方便快速查找训练集中的用户和电影位置。 - 构建嵌入矩阵:将
U与Sigma的对角矩阵相乘得到用户嵌入矩阵,将VT转置得到电影嵌入矩阵,两者的点积即可还原预测评分。 - 生成验证集预测:遍历验证集的每条评分记录,对存在于训练集中的用户和电影,计算对应嵌入的点积作为预测值;对未出现过的用户/电影,用训练集的全局评分均值填充(避免无预测值的情况)。
- 计算RMSE:收集所有真实评分和预测评分,直接调用
mean_squared_error计算并开平方得到RMSE。
修改后的代码
import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.utils.extmath import randomized_svd from sklearn.metrics import mean_squared_error # 读取数据 df = pd.read_csv('data/ml-latest-small/ratings.csv') # 拆分训练集和验证集 df_train, df_valid = train_test_split(df, test_size=0.1, random_state=42) # 构建训练宽表并填充0 train_data = df_train.pivot_table(index='userId', columns='movieId', values='rating').fillna(0) # 获取用户和电影的索引映射 user_to_idx = {user: idx for idx, user in enumerate(train_data.index)} movie_to_idx = {movie: idx for idx, movie in enumerate(train_data.columns)} # 执行截断SVD U, Sigma, VT = randomized_svd(np.array(train_data), n_components=15, n_iter=5, random_state=None) # 构建用户嵌入和电影嵌入矩阵 user_embeddings = U @ np.diag(Sigma) # shape: (610, 15) movie_embeddings = VT.T # shape: (9379, 15) # 计算训练集全局均值,用于处理未见过的用户/电影 train_global_mean = df_train['rating'].mean() # 生成验证集的预测值 y_true = [] y_pred = [] for _, row in df_valid.iterrows(): user_id = row['userId'] movie_id = row['movieId'] true_rating = row['rating'] # 检查用户和电影是否在训练集中 if user_id in user_to_idx and movie_id in movie_to_idx: user_idx = user_to_idx[user_id] movie_idx = movie_to_idx[movie_id] # 计算点积得到预测评分 pred_rating = user_embeddings[user_idx] @ movie_embeddings[movie_idx] # 限制评分在1-5范围内(可选,符合原始评分范围) pred_rating = np.clip(pred_rating, 1, 5) else: # 用训练集全局均值替代 pred_rating = train_global_mean y_true.append(true_rating) y_pred.append(pred_rating) # 计算RMSE rmse = np.sqrt(mean_squared_error(y_true, y_pred)) print(f"验证集RMSE: {rmse:.4f}")
内容的提问来源于stack exchange,提问作者Bothurin
相关产品推荐
相关产品推荐

