You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Surprise的SVD模型为训练集外用户生成预测?

解决Surprise SVD模型对新用户生成推荐的问题

方法一:手动计算新用户的隐因子与偏差(高效推荐)

由于Surprise预训练模型无法直接处理训练集外的新用户,我们可以复用已训练好的物品参数(隐因子、偏差),通过最小二乘法求解新用户的专属参数,无需重新训练整个模型,适合在线部署场景。

步骤说明

  1. 提前保存训练完成的模型,在API服务启动时加载。
  2. 接收新用户的评分数据,过滤掉训练集中不存在的电影(模型无对应参数)。
  3. 构建线性方程组,求解新用户的偏差bu_new和隐因子pu_new。
  4. 利用求解出的参数,计算所有未评分电影的预估评分,排序后取TopN推荐。

代码实现

1. 保存/加载预训练模型

import pickle

# 保存训练好的最终模型
with open('svd_final_model.pkl', 'wb') as f:
    pickle.dump(final_algo, f)

# 在API服务中加载模型
with open('svd_final_model.pkl', 'rb') as f:
    final_algo = pickle.load(f)

2. 处理新用户评分并生成推荐

import numpy as np

def get_new_user_top_recommendations(new_user_ratings, algo, n=5):
    """
    为新用户生成TopN推荐
    Args:
        new_user_ratings: 列表,元素为(新用户ID, 电影原始ID, 评分)
        algo: 预训练好的Surprise SVD模型
        n: 推荐数量,默认5
    Returns:
        列表,元素为(电影原始ID, 预估评分),按评分降序排列
    """
    # 提取预训练模型的核心参数
    mean = algo.trainset.global_mean
    bi = algo.bi  # 物品偏差(内部ID索引)
    qi = algo.qi  # 物品隐因子矩阵(形状:n_items × n_factors)
    reg_bu = algo.reg_bu  # 用户偏差正则化系数
    reg_pu = algo.reg_pu  # 用户隐因子正则化系数
    n_factors = algo.n_factors

    # 转换新用户评分的电影ID为内部ID,过滤训练集中不存在的电影
    inner_ratings = []
    for _, iid_raw, r in new_user_ratings:
        try:
            iid_inner = algo.trainset.to_inner_iid(iid_raw)
            inner_ratings.append((iid_inner, r))
        except ValueError:
            # 跳过不在训练集中的电影
            continue

    if not inner_ratings:
        # 无有效评分时,可返回热门电影或随机推荐
        return []

    # 构建线性方程组的特征矩阵X和目标向量y
    X = []
    y = []
    for iid_inner, r in inner_ratings:
        # 每行特征:[1, 物品隐因子0, 物品隐因子1, ..., 物品隐因子n-1]
        row = [1.0] + qi[iid_inner].tolist()
        X.append(row)
        # 目标值:真实评分 - 全局均值 - 物品偏差
        y_val = r - mean - bi[iid_inner]
        y.append(y_val)

    # 添加正则化项,防止过拟合
    # 用户偏差的正则化行
    X.append([np.sqrt(reg_bu)] + [0.0] * n_factors)
    y.append(0.0)
    # 用户隐因子每个维度的正则化行
    for j in range(n_factors):
        reg_row = [0.0] * (n_factors + 1)
        reg_row[j+1] = np.sqrt(reg_pu)
        X.append(reg_row)
        y.append(0.0)

    # 转换为numpy数组并求解最小二乘
    X_np = np.array(X)
    y_np = np.array(y)
    params, _, _, _ = np.linalg.lstsq(X_np, y_np, rcond=None)

    # 提取新用户的参数
    bu_new = params[0]
    pu_new = params[1:]

    # 预测所有未评分电影的评分
    predictions = []
    rated_inner_iids = {iid for iid, _ in inner_ratings}
    for iid_inner in algo.trainset.all_items():
        if iid_inner in rated_inner_iids:
            continue
        # 计算预估评分
        est = mean + bu_new + bi[iid_inner] + np.dot(pu_new, qi[iid_inner])
        # 转换回原始电影ID
        iid_raw = algo.trainset.to_raw_iid(iid_inner)
        predictions.append((iid_raw, est))

    # 按预估评分降序排序,取TopN
    predictions.sort(key=lambda x: x[1], reverse=True)
    return predictions[:n]

# 示例使用
new_user_ratings = [("new_user_001", "movie_123", 4.0), ("new_user_001", "movie_456", 2.5), ("new_user_001", "movie_789", 5.0)]
top5_recs = get_new_user_top_recommendations(new_user_ratings, final_algo, n=5)
print(top5_recs)

方法二:合并新用户数据重新训练模型(适合小数据集)

如果数据集规模较小,可直接将新用户的评分合并到原数据中,重新构建训练集并训练模型。这种方法实现简单,但效率较低,不适合大数据量的在线场景。

代码实现

import pandas as pd
from surprise import Dataset, Reader

# 假设原数据存储在df中,列名:user_id, item_id, rating
new_user_ratings = [("new_user_001", "movie_123", 4.0), ("new_user_001", "movie_456", 2.5)]
new_user_df = pd.DataFrame(new_user_ratings, columns=["user_id", "item_id", "rating"])

# 合并原数据与新用户数据
combined_df = pd.concat([df, new_user_df], ignore_index=True)

# 重新构建训练集
reader = Reader(rating_scale=(1, 5))
data = Dataset.load_from_df(combined_df[["user_id", "item_id", "rating"]], reader)
trainset = data.build_full_trainset()

# 重新拟合模型(可复用原模型参数初始化,减少训练时间)
final_algo.fit(trainset)

# 构建新用户的anti-testset(未评分的电影)
new_user_raw_id = "new_user_001"
new_user_inner_id = trainset.to_inner_uid(new_user_raw_id)
anti_testset = []
for iid_inner in trainset.all_items():
    # 检查该电影是否已被用户评分
    if iid_inner not in [iid for (iid, _) in trainset.ur[new_user_inner_id]]:
        iid_raw = trainset.to_raw_iid(iid_inner)
        anti_testset.append((new_user_raw_id, iid_raw, trainset.global_mean))

# 预测并生成Top5推荐
predictions = final_algo.test(anti_testset)
top_n = get_top_n(predictions, n=5)
new_user_recs = top_n[new_user_raw_id]
print(new_user_recs)

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 15:55:18