☰
多模态推荐工具箱MMRec实战:10+模型开箱即跑与Jupyter实验全流程
2026/10/25 14:14:50 网站建设 项目流程

简介:MMRec是一套面向推荐系统开发者与科研人员的多模态推荐工具箱,基于Python实现,旨在降低多模态推荐模型的研发与实验门槛。资源包共59个文件,以29个py源码、18个yaml配置、4个ipynb实验笔记为主,辅以md说明、png示意图与pdf报告,压缩包约446KB,结构紧凑、便于按模块查阅。工具箱集成协同过滤、矩阵分解及MTransE、CDAE、MMoE等10余个先进模型,覆盖从传统方法到深度学习架构,并支持Yelp、Amazon等公开数据集,方便开展基准测试与对比实验。通过Jupyter Notebook提供数据加载、模型训练、结果评估的完整流程模板,配合简洁API与可扩展设计,研究者可快速集成自有算法。内容还涉及特征编码、评分转交互、双图神经网络等预处理脚本,适合电商、社交媒体、内容推荐等场景的算法验证与项目落地。目前已有267人学习下载,可作为多模态推荐入门与进阶的实践参考。

1. 多模态推荐工具箱 MMRec:10 多个模型开箱即跑,Jupyter Notebook 里把实验流程走通

如果你正在做推荐系统相关的课程设计、毕设或者论文实验,大概率会遇到一个很现实的问题:单模态的协同过滤还没调明白,导师或者业务方已经开始要求「融合图像和文本特征」了。MMRec 这个工具箱就是冲着这个场景来的——它把 10 多个多模态推荐模型收进一个 Python 工程里,用 Jupyter Notebook 串起从评分转交互、特征编码到模型训练评估的完整链路。拿到手的是一个 zip 包,解压后能看到main.py、configs、models、data、utils这些目录,还有preprocessing下几个预处理 notebook。它适合两类人:一是想快速跑通多模态推荐 baseline 的研究生,二是不想从零搭数据管线的算法工程师。下面按「资源结构 → 数据预处理 → 模型训练 → 避坑 → 进阶」的顺序拆一遍。

2. 拆开 MMRec 的目录:从 main.py 到 preprocessing 的完整链路

2.1 工程骨架与各目录职责

解压之后先别急着跑代码,花五分钟把目录结构看清楚,后面调参和排错会省很多时间。MMRec 的顶层结构大致是这样:

路径作用
main.py训练入口,负责解析配置、构建模型、跑 epoch
configs/各模型的 YAML/JSON 配置文件,超参都在这里改
models/多模态推荐模型实现,每个模型一个文件
data/数据集存放与加载逻辑
utils/通用工具,含数据加载器、评估指标、日志
preprocessing/数据预处理 notebook,评分转交互、特征编码等
README.md项目说明与运行指引

main.py是整个工程的调度中心。它做的事情不复杂:读配置 → 加载数据 → 实例化模型 → 训练循环 → 评估。你不需要改main.py的主体逻辑,改配置文件和模型文件就够了。configs目录下每个模型通常对应一份配置,里面写着学习率、embedding 维度、batch size、训练轮数这些。我一般会先把配置复制一份改名,比如dualgnn_yelp.yaml,避免污染原始文件。

models目录是核心资产。协同过滤、矩阵分解、深度学习类的模型都在这里,每个文件基本遵循「继承基类 → 实现 forward → 实现 loss」的写法。如果你想加自己的模型,照着现有文件的接口抄一个就行,这是这个工具箱可扩展性的体现。

2.2 预处理 notebook 的执行顺序

preprocessing目录下的 notebook 不是随便跑的,有先后依赖。从文件名能看出大致顺序:

  1. 0rating2inter.ipynb:把评分数据转成隐式交互(0/1),这是推荐系统常见做法,显式评分转成「有没有交互」。
  2. 1splitting.ipynb:划分训练集、验证集、测试集。
  3. 2reindex-feat.ipynb:对特征和 ID 重新索引,保证从 0 开始连续。
  4. 3feat-encoder.ipynb:用预训练编码器把图像、文本特征编码成向量。
  5. dualgnn-gen-u-u-matrix.py:生成用户-用户关系矩阵,给 DualGNN 这类用图结构的模型准备输入。

常见做法是先跑0再跑1,因为划分要在交互转换之后做。2和3的顺序取决于你的特征来源——如果原始特征已经是 ID 对齐的,可以先3再2;如果特征文件里的 ID 和交互文件不一致,必须先2对齐再3编码。这个顺序搞反了,后面模型加载特征时会直接报 index out of range。

提示:每个 notebook 跑完检查一下输出目录,确认生成了对应的.inter、.feat文件再往下走,不要一口气全跑完再回头找问题。

2.3 配置文件里真正要改的参数

很多人跑不通 MMRec,不是代码有 bug,是配置文件没对上自己的数据。以一份典型的模型配置为例,需要关注的字段:

# configs/xxx.yaml 示例结构 dataset: yelp # 数据集名称,要和 data/ 下的目录名一致 data_path: ./data/ # 数据根路径 embedding_size: 64 # 嵌入维度,显存不够就降到 32 batch_size: 2048 # 批大小,图模型建议 1024~4096 lr: 0.001 # 学习率,图模型常用 1e-3 epochs: 100 # 训练轮数 reg_weight: 0.01 # 正则化权重,过拟合就调大

dataset字段必须和data/下实际目录名完全一致,大小写敏感。embedding_size和batch_size是最容易翻车的两个参数——图神经网络类的模型对显存占用比较敏感,如果你在本地跑,先把embedding_size设成 32、batch_size设成 1024 试一轮,确认能跑通再往上加。reg_weight是正则项系数,训练集 loss 一直降但验证集指标不涨的时候,把它从 0.01 调到 0.1 往往有效。

3. 跑通第一个模型:从数据加载到评估的完整命令

3.1 环境准备与依赖安装

MMRec 是 Python 工程,依赖主要是 PyTorch、NumPy、SciPy、Pandas 以及 Jupyter。建议用 conda 建一个独立环境,避免和系统 Python 打架:

# 创建并激活环境 conda create -n mmrec python=3.8 -y conda activate mmrec # 安装核心依赖(版本按你 CUDA 情况调整) pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install numpy scipy pandas scikit-learn jupyter pyyaml tqdm

Python 3.8 是比较稳的选择,很多推荐系统的老代码对新版本兼容性一般。PyTorch 的 CUDA 版本根据你显卡驱动来,没有 GPU 就用 CPU 版,只是训练会慢很多。装完之后python -c "import torch; print(torch.cuda.is_available())"确认一下 GPU 是否可用。

3.2 数据预处理的实际操作

假设你已经把数据集放到了data/下,接下来按顺序跑预处理。以 Jupyter Notebook 为例,启动之后逐个执行:

# 启动 Jupyter jupyter notebook # 在浏览器里打开 preprocessing/0rating2inter.ipynb # 修改里面的输入路径为你的数据集路径,然后 Run All

0rating2inter.ipynb的核心逻辑是把评分矩阵转成交互矩阵。代码里通常有一个阈值判断,比如评分大于 3 算正交互。这个阈值直接影响正负样本比例,阈值太高会导致正样本太少,模型学不到东西;阈值太低会引入噪声。常见做法是取数据集中位数或者 4 分(5 分制)。

# 评分转交互的核心逻辑示意 import pandas as pd import numpy as np df = pd.read_csv('data/yelp/ratings.csv') # 评分 >= 4 视为正交互 df = df[df['rating'] >= 4] # 重新索引 user 和 item,保证从 0 开始连续 user_ids = {uid: idx for idx, uid in enumerate(df['user_id'].unique())} item_ids = {iid: idx for idx, iid in enumerate(df['item_id'].unique())} df['user'] = df['user_id'].map(user_ids) df['item'] = df['item_id'].map(item_ids) df[['user', 'item']].to_csv('data/yelp/yelp.inter', sep=' ', index=False, header=False)

这段代码做了三件事:过滤低评分、重映射 ID、输出交互文件。重映射这步很关键,推荐系统的 embedding 层是按 ID 索引的,如果 ID 不连续或者不从 0 开始,embedding 查表会直接崩。1splitting.ipynb负责按用户或按时间划分数据集,通常是 8:1:1 或者 7:1:2。划分时要保证每个用户在训练集里至少有一条交互,否则该用户的 embedding 训不起来。

3.3 启动训练与日志解读

预处理跑完,回到工程根目录启动训练:

# 指定配置文件和 GPU python main.py --config configs/xxx.yaml --gpu 0

--config指向你要跑的模型配置,--gpu指定显卡编号,没有 GPU 就去掉这个参数。训练开始后终端会打印每个 epoch 的 loss 和评估指标。重点看两个东西:训练 loss 是否稳定下降,验证集的 Recall@20 和 NDCG@20 是否在涨。如果 loss 降但指标不涨,大概率是过拟合,调大reg_weight或者加 dropout。如果 loss 震荡厉害,把学习率降到 1e-4 试试。

评估指标一般在utils里实现,常见的是 Recall 和 NDCG。Recall@K 衡量的是「前 K 个推荐里命中了多少真实交互」,NDCG@K 额外考虑了命中位置,越靠前权重越高。这两个指标是多模态推荐论文里的标配,跑通之后可以直接拿来做对比实验。

4. 避坑与排查:多模态推荐实验里最容易翻车的五件事

4.1 特征文件和交互文件 ID 对不上

现象:模型加载特征时报IndexError: index out of range,或者训练 loss 一直是 NaN。

原因:2reindex-feat.ipynb没有跑,或者跑的时候用的 ID 映射和交互文件不一致。多模态推荐里图像/文本特征是按 item ID 索引的,如果特征文件里的 item ID 是原始 ID,而交互文件已经重映射过,两边就对不上。

解决:先跑2reindex-feat.ipynb,确保特征文件和交互文件用同一套 ID 映射。跑完之后检查两个文件的 item ID 范围是否一致,用wc -l看行数,用head看前几行 ID 是否从 0 开始。

4.2 显存不够导致训练中断

现象:跑图神经网络类模型时,几个 epoch 之后报CUDA out of memory。

原因:embedding_size或batch_size设太大,或者用户-用户关系矩阵太稀疏但维度太高。DualGNN 这类模型要构建邻接矩阵,显存占用比普通矩阵分解高不少。

解决:先把batch_size减半,再把embedding_size从 64 降到 32。如果还不行,检查dualgnn-gen-u-u-matrix.py生成的矩阵是不是太稠密,可以加一个相似度阈值过滤掉弱连接。

4.3 预处理 notebook 顺序跑反

现象:3feat-encoder.ipynb跑完生成的.feat文件加载时报维度不匹配。

原因:先跑了3再跑2,特征编码时用的 ID 和重新索引后的 ID 不一致。或者0和1的顺序反了,划分数据集时用的还是原始评分。

解决:严格按0 → 1 → 2 → 3的顺序执行。如果已经跑乱了,把输出目录清空重来。我一般会在每个 notebook 开头加一个检查 cell,确认上一步的输出文件存在再往下跑。

4.4 评估指标不涨但 loss 在降

现象:训练 loss 从 0.8 降到 0.1,但验证集 Recall@20 一直在 0.05 左右不动。

原因:过拟合,或者正负样本比例严重失衡。多模态推荐里如果正样本太少,模型会倾向于把所有样本预测为负,loss 很低但指标很差。

解决:调大reg_weight,加 dropout,或者对负样本做下采样。另外检查一下0rating2inter.ipynb里的评分阈值是不是设太高了,导致正样本过少。

4.5 配置文件 dataset 名称和目录不匹配

现象:启动训练直接报FileNotFoundError,找不到数据文件。

原因:configs里的dataset字段和data/下的实际目录名不一致,或者data_path写的是相对路径但你在别的目录下执行main.py。

解决:dataset字段和目录名逐字符核对,大小写、下划线都要一致。data_path建议用绝对路径,或者在工程根目录下执行命令。这个坑很低级但特别常见,血泪经验是每次换数据集先ls data/确认目录名。

5. 进阶用法:加自己的模型和做对比实验

5.1 照着现有模型接口扩展新模型

MMRec 的可扩展性体现在模型文件的接口统一。你打开models下任意一个模型文件,基本能看到这几个方法:__init__里定义 embedding 层和网络结构,forward里写前向传播,calculate_loss里写损失函数。要加自己的模型,复制一个结构最接近的文件改名,然后改这三处。

# models/my_model.py 骨架示意 import torch import torch.nn as nn from models.base import BaseModel class MyModel(BaseModel): def __init__(self, config, dataset): super().__init__(config, dataset) # 用户和物品的 embedding self.user_embedding = nn.Embedding(dataset.user_num, config['embedding_size']) self.item_embedding = nn.Embedding(dataset.item_num, config['embedding_size']) # 多模态特征投影层 self.feat_proj = nn.Linear(dataset.feat_dim, config['embedding_size']) def forward(self, user, item): u_emb = self.user_embedding(user) i_emb = self.item_embedding(item) # 融合多模态特征 feat = self.feat_proj(self.item_feat[item]) score = (u_emb * (i_emb + feat)).sum(dim=-1) return score def calculate_loss(self, interaction): user = interaction[0] item = interaction[1] score = self.forward(user, item) # BPR loss loss = -torch.log(torch.sigmoid(score)).mean() return loss

关键是继承基类并实现forward和calculate_loss。基类里通常已经处理了数据加载、优化器构建、评估这些通用逻辑。写完模型文件后,在configs下加一份对应配置,main.py就能识别到。注意dataset.feat_dim要和你的特征文件维度一致,否则投影层会报维度错误。

5.2 多模型对比实验的组织方式

做对比实验时,不要手动一个个跑然后抄结果,容易出错。我一般写一个 shell 脚本批量跑:

#!/bin/bash # run_all.sh 批量跑多个模型 for config in configs/*.yaml; do echo "Running $config" python main.py --config $config --gpu 0 2>&1 | tee "logs/$(basename $config).log" done

每个模型的日志单独存一份,跑完之后从日志里提取最好的 Recall@20 和 NDCG@20。这样既省事又可追溯。如果要做消融实验,比如去掉图像特征看指标掉多少,就在配置里加一个use_image: false的开关,在模型forward里根据这个开关决定是否融合图像特征。

5.3 验证实验可复现性的几个习惯

多模态推荐实验最怕结果不可复现。我的习惯是每次跑实验前固定随机种子,在main.py开头加:

import random import numpy as np import torch def set_seed(seed=42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = False

然后在配置文件里把seed作为一个参数传进去。这样同一份配置跑两次结果基本一致。另外,每次改完预处理或者模型代码,把对应的输出文件版本号记一下,不然过两天自己都忘了哪个结果对应哪版数据。从那以后我每次跑对比实验都强制走一遍「固定种子 → 记录配置 → 单独存日志」的流程,省了很多回头找结果的时间。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询