能否使用Surprise无需重新训练即可实时预测新用户的评分?
问题解答
原理可行性结论
需求在原理上完全可行。
基于矩阵分解的SVD推荐模型训练完成后,核心得到三类固定参数:全局评分均值、所有已纳入训练的物品偏置、所有已纳入训练的物品隐向量。新用户提交少量评分后,只需要固定上述已经训练好的物品侧参数,仅针对新用户的用户偏置、用户隐向量做极小量的优化求解即可,不需要重新训练整个模型的全部参数,计算成本极低,可以做到实时响应。
Surprise库原生支持情况
Surprise官方没有直接提供新用户增量适配的接口,原生的predict方法遇到不在训练集的新用户时,会默认把用户偏置、用户隐向量设为0,最终给出的预测值仅为全局均值加对应物品的偏置,完全没有用到新用户提交的个性化评分数据,达不到个性化推荐的要求。
但你可以直接调用训练好的SVD模型的公开参数,手动实现新用户参数的求解逻辑,实现成本很低,具体实现方案如下:
手动实现代码示例
首先你训练好的SVD模型可以直接取出所有需要的固定参数:
from surprise import SVD, Dataset, Reader import numpy as np # 你的原有训练逻辑 data = Dataset.load_from_df(your_df, Reader(rating_scale=(1,5))) trainset = data.build_full_trainset() svd = SVD(verbose=True, n_epochs=10, reg_all=0.02) svd.fit(trainset) # 取出模型固定参数 mu = svd.mu bi = svd.bi qi = svd.qi reg_bu = svd.reg_bu reg_pu = svd.reg_pu n_factors = svd.n_factors lr = svd.lr_all
然后拿到新用户的评分数据后,求解新用户的个性化参数:
def get_new_user_params(new_user_ratings): # new_user_ratings 格式:[(原始物品id, 评分), ...] rated_iids = [] ratings = [] for raw_iid, r in new_user_ratings: # 过滤掉训练集中不存在的物品 if trainset.knows_item(raw_iid): inner_iid = trainset.to_inner_iid(raw_iid) rated_iids.append(inner_iid) ratings.append(r) if not rated_iids: # 新用户没有任何有效评分,返回默认值 return 0, np.zeros(n_factors) rated_iids = np.array(rated_iids) ratings = np.array(ratings) # 初始化新用户参数 bu_new = 0.0 pu_new = np.random.normal(0, 0.1, n_factors) # 少量梯度迭代即可收敛,迭代20次完全足够 for _ in range(20): error = ratings - (mu + bu_new + bi[rated_iids] + pu_new @ qi[rated_iids].T) # 更新用户偏置 bu_new += lr * (np.sum(error) - reg_bu * bu_new) # 更新用户隐向量 pu_new += lr * (error @ qi[rated_iids] - reg_pu * pu_new) return bu_new, pu_new
最后就可以用求解得到的参数做预测、生成推荐:
def predict_new_user(bu_new, pu_new, raw_iid): if not trainset.knows_item(raw_iid): # 物品不在训练集,返回全局均值 return mu inner_iid = trainset.to_inner_iid(raw_iid) pred = mu + bu_new + bi[inner_iid] + pu_new @ qi[inner_iid].T # 截断到你的评分区间,示例为1-5分 return max(1, min(5, pred))
注意事项
- 新用户的有效评分数量建议不低于3个,求解得到的用户隐向量会更准确,推荐效果更好
- 训练集不存在的冷启动物品,这个方案也无法给出个性化预测,需要单独处理物品冷启动的逻辑
内容的提问来源于stack exchange,提问作者Nik
相关产品推荐
相关产品推荐

