LightFM训练集与测试集需同规模吗?纯协同过滤模型报错排查
嘿,这个问题我之前踩过坑!咱们来捋清楚到底怎么回事,以及怎么解决它。
错误原因拆解
你碰到的"Incorrect number of features in user_features"错误,本质是纯协同过滤模式下,LightFM把用户ID当作唯一的特征维度——训练时模型会根据训练集的用户总数(也就是训练矩阵的行数)来初始化user_embeddings的维度。当测试矩阵的行数更少时,如果你直接用测试用户生成的特征矩阵,它的列数会等于测试用户的数量,这就和训练好的user_embeddings的行数(训练用户总数)不匹配,触发了lightfm.py第789行的维度检查。
你提到根源在第778行,那行应该是LightFM处理用户特征的逻辑:在纯协同过滤场景下,用户特征默认是一个单位矩阵(identity matrix),行数等于当前输入的用户数量。如果测试时用户数比训练少,这个矩阵的列数就会小于训练时的用户总数,自然过不了维度校验。
解决方案:统一用户特征维度
核心思路是:让测试集的用户特征矩阵的列数,严格等于训练集的用户总数,不管测试集中有多少用户。这里有两种可靠的实现方式:
1. 用LightFM的Dataset类统一管理特征
这是最省心的方法,Dataset会帮你维护全局的用户/物品索引,确保训练和测试的特征维度一致:
from lightfm import LightFM from lightfm.data import Dataset # 假设你的训练/测试交互数据是(user_id, item_id)或带评分的三元组 train_interactions = [(u1, i1), (u2, i2), ...] # 训练集交互 test_interactions = [(u1, i3), (u3, i2), ...] # 测试集用户都是训练子集 # 初始化Dataset,只基于训练集拟合用户和物品 dataset = Dataset() dataset.fit( (user for user, item in train_interactions), (item for user, item in train_interactions) ) # 构建训练/测试的交互矩阵 train_matrix, _ = dataset.build_interactions(train_interactions) test_matrix, _ = dataset.build_interactions(test_interactions) # 生成训练用的用户特征矩阵(纯协同过滤下是单位矩阵) train_user_features = dataset.build_user_features() # 训练模型 model = LightFM(loss="warp") model.fit(train_matrix, user_features=train_user_features, epochs=30) # 生成测试用的用户特征矩阵——关键!用同一个Dataset生成,维度和训练时一致 test_user_ids = [user for user, item in test_interactions] test_user_features = dataset.build_user_features( ((user_id,) for user_id in test_user_ids) ) # 现在预测就不会报错了 predictions = model.predict( test_user_ids, [item for user, item in test_interactions], user_features=test_user_features )
2. 手动维护用户索引映射
如果你不想用Dataset,也可以手动建立训练集用户到索引的映射,确保测试用户的特征向量长度等于训练用户总数:
import numpy as np from lightfm import LightFM # 提取训练集所有用户ID并建立映射 train_users = list(set(u for u, i in train_interactions)) user_id_to_idx = {user: idx for idx, user in enumerate(train_users)} num_train_users = len(train_users) # 生成训练用户特征矩阵(单位矩阵) train_user_features = np.eye(num_train_users) # 训练模型 model = LightFM(loss="warp") model.fit(train_matrix, user_features=train_user_features, epochs=30) # 生成测试用户特征矩阵:每个测试用户对应长度为num_train_users的one-hot向量 test_user_features = np.zeros((len(test_interactions), num_train_users)) for idx, (user, _) in enumerate(test_interactions): test_user_features[idx, user_id_to_idx[user]] = 1 # 预测 predictions = model.predict( [user_id_to_idx[u] for u, i in test_interactions], [i for u, i in test_interactions], user_features=test_user_features )
关键提醒
纯协同过滤本身无法处理训练集中没有出现过的新用户——如果你的测试集包含新用户,这个错误本质上是合理的,因为模型没有这些用户的任何历史数据可供学习。这种情况下你可能需要切换到带内容特征的混合模型,或者先处理新用户的冷启动问题。
内容的提问来源于stack exchange,提问作者Chris M

