ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CentOS 7.9多模态实战:TensorFlow与PyTorch环境配置与模型融合

CentOS 7.9多模态实战:TensorFlow与PyTorch环境配置与模型融合 先说个结论在 CentOS 7.9 上同时跑 TensorFlow 和 PyTorch 做多模态深度学习研究这件事本身不难难的是版本矩阵的匹配以及两个框架的数据接口怎么在设计上打通。很多人在第一步装驱动和 CUDA 时就栽了跟头后面全都白搭。这篇把我自己踩过的坑、实测可用的方案、以及多模态数据从预处理到融合训练的完整套路一次性写清楚希望能帮后来的人少走弯路。1. 环境准备与版本选型1.1 为什么选 CentOS 7.9 和这套版本组合CentOS 7.9 虽然已经进入 EOL 阶段但在不少企业的 GPU 服务器上仍然是主力系统尤其是那些跑着存量业务、不方便迁移内核的老机器。深度学习开发环境在这类系统上的核心矛盾是系统自带的 gcc、glibc 版本偏老而新版本 TensorFlow 和 PyTorch 对系统库的要求越来越高。我实测下来最稳的组合是NVIDIA 驱动 550.144.03 CUDA 11.2 cuDNN 8.1 TensorFlow 2.5.0 PyTorch 1.9.0。有人会问驱动明明是 550 系列为什么不用 CUDA 12这里有个关键知识点NVIDIA 驱动的版本只决定它兼容的最高 CUDA runtime 版本而 CUDA Toolkit 本身是可以向下兼容的也就是说你完全可以在新版驱动上装旧版 CUDA Toolkit。TensorFlow 2.5.0 官方支持的是 CUDA 11.2 和 cuDNN 8.1这是它能不能正常调用 GPU 的硬性要求所以就算驱动再新也没用TensorFlow 编译时的版本依赖就锁死在那里。再说 PyTorch。1.9.0 官方提供了 cu111 的预编译包对应 CUDA 11.1实测在 CUDA 11.2 的 runtime 下能正常跑torch.cuda.is_available()返回 True矩阵乘法结果也没问题。如果你不想用 1.9.0也可以选 1.10.0 或 1.12.0但注意别直接上 2.x因为新版 PyTorch 默认用 cu118 或 cu121需要额外处理编译环境没必要给自己找麻烦。1.2 驱动安装与 nouveau 黑名单装驱动之前必须先禁用系统自带的 nouveau 开源驱动否则 NVIDIA 官方驱动根本装不进去。步骤如下# 检查 nouveau 是否加载 lsmod | grep nouveau # 编辑内核模块黑名单 vim /etc/modprobe.d/blacklist.conf # 添加以下三行 blacklist nouveau options nouveau modeset0 # 重建 initramfs mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.bak dracut /boot/initramfs-$(uname -r).img $(uname -r) # 重启后确认不再加载 reboot lsmod | grep nouveau确认 nouveau 已经不再加载后开始安装 NVIDIA 驱动chmod x NVIDIA-Linux-x86_64-550.144.03.run ./NVIDIA-Linux-x86_64-550.144.03.run --no-opengl-files --silent这里有个非常重要的细节一定要加--no-opengl-files。服务器通常没有桌面环境还好一旦有图形界面或者需要在上面跑 OpenGL 相关任务不加这个参数可能会导致安装后重启黑屏或者循环登录。这个坑我踩过不止一次尤其是在带 X11 的机器上。装完驱动后用nvidia-smi验证能看到 GPU 型号、显存、驱动版本和 CUDA 版本就说明成功了。注意nvidia-smi显示的 CUDA 版本是驱动支持的最高版本不是你实际安装的 Toolkit 版本这两个概念不要混淆。1.3 CUDA Toolkit 与 cuDNN 的安装细节CUDA Toolkit 安装推荐用 runfile 方式不用 rpm因为 rpm 方式经常会把/usr/local/cuda链接指向默认路径而你后面可能需要在多个 CUDA 版本之间切换。wget https://developer.download.nvidia.com/compute/cuda/11.2.2/local_installers/cuda_11.2.2_460.32.03_linux.run sh cuda_11.2.2_460.32.03_linux.run --toolkit --silent --override安装完成后需要手动配置环境变量建议写到/etc/profile.d/cuda.sh里这样所有用户都能生效export PATH/usr/local/cuda-11.2/bin:$PATH export LD_LIBRARY_PATH/usr/local/cuda-11.2/lib64:$LD_LIBRARY_PATH然后安装 cuDNN。这里要下载和 CUDA 11.2 匹配的 cuDNN 8.1.x 版本解压后把 include 和 lib64 目录的内容复制到 CUDA 安装目录中tar -xzvf cudnn-11.2-linux-x64-v8.1.1.33.tgz cp cuda/include/cudnn*.h /usr/local/cuda-11.2/include/ cp cuda/lib64/libcudnn* /usr/local/cuda-11.2/lib64/ chmod ar /usr/local/cuda-11.2/include/cudnn*.h /usr/local/cuda-11.2/lib64/libcudnn*验证 cuDNN 是否就绪cat /usr/local/cuda-11.2/include/cudnn_version.h | grep CUDNN_MAJOR -A 2这里输出的版本号必须包含 8 开头否则后面 TensorFlow 会报错找不到或不匹配的 cuDNN。1.4 Python 虚拟环境与两套框架共存的坑我用的是 Miniconda 创建独立虚拟环境这是目前处理 TensorFlow 和 PyTorch 共存时最省心的方案。两个框架的依赖经常打架比如protobuf版本冲突就是最常见的问题TensorFlow 2.5.0 要求protobuf3.9.2而 PyTorch 1.9.0 对 protobuf 的版本没有那么挑剔但如果环境混装pip 经常会把 protobuf 升级到 3.20 以上TensorFlow 直接挂掉。conda create -n mm python3.8 conda activate mm # 安装 TensorFlow 2.5.0 pip install tensorflow2.5.0 # 安装 PyTorch 1.9.0 cu111 pip install torch1.9.0cu111 torchvision0.10.0cu111 -f https://download.pytorch.org/whl/torch_stable.html # 安装辅助库 pip install numpy pandas scikit-learn pillow opencv-python transformers装完验证是不是真的能调 GPU# 验证 TensorFlow import tensorflow as tf print(tf.__version__) print(tf.config.list_physical_devices(GPU)) # 验证 PyTorch import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))实测下来TensorFlow 2.5.0 在 Python 3.8 下运行稳定PyTorch 1.9.0 也兼容 Python 3.8。这里建议不要用 Python 3.9 或更高版本TensorFlow 2.5.0 在 3.9 上虽然也能跑但某些 ops 会有 warning既然是要做研究稳定第一。2. 多模态数据集成方案设计2.1 什么是多模态为什么要把它们统一处理多模态数据指的是来自不同来源或不同类型的信息组合。做深度学习的都知道单一模态的信息往往是有边界的比如只靠商品标题文本做分类很难捕捉到商品外观、色泽这些视觉特征只靠图片做分类又可能被背景干扰。把文本、图像、数值特征放在一个模型里联合学习往往能显著提升效果。但在实际工程落地的时候多模态数据的统一处理才是真正的重点。不同模态的数据格式不同、量纲不同、长度不同如果不能在一个框架内进行统一的 token 化、对齐和 batch 化模型训练就会因为数据 pipeline 的问题反复报错。2.2 数据预处理与对齐的完整流程我以电商商品多模态分类为例一条样本包含三个模态——商品标题文本、商品主图图像、价格和销量数值特征。目标是根据这些信息预测商品的类目。这是多模态任务里最经典也最难的设计方案之一。预处理的核心思路分三路进行# 图像模态统一缩放到固定尺寸归一化 from PIL import Image import numpy as np def process_image(img_path, img_size224): img Image.open(img_path).convert(RGB) img img.resize((img_size, img_size)) img_array np.array(img) / 255.0 return img_array # 文本模态tokenize 后统一长度 from transformers import BertTokenizer tokenizer BertTokenizer.from_pretrained(bert-base-chinese) def process_text(text, max_len128): tokens tokenizer(text, paddingmax_length, truncationTrue, max_lengthmax_len, return_tensorsnp) return tokens[input_ids].flatten(), tokens[attention_mask].flatten() # 数值模态标准化 from sklearn.preprocessing import StandardScaler scaler StandardScaler() # price 和 sales 两列做标准化fit 时只用训练集 numeric_features scaler.fit_transform(X_train[[price, sales]])这里有一个关键的工程细节三个模态的样本数量必须对齐。很多人做多模态数据 pipeline 时容易忽略这个问题比如图像加载失败后没有做丢弃处理导致 image 列表比 text 列表少了几条训练时 TensorFlow 会报ValueError: Data cardinality is ambiguousPyTorch 的DataLoader会直接把所有 batch 搞乱。我的做法是写一个统一的数据类在加载阶段就做对齐校验def load_multimodal_data(img_paths, texts, nums, labels): images [] valid_texts [] valid_nums [] valid_labels [] for idx in range(len(texts)): try: img process_image(img_paths[idx]) except Exception: continue # 加载失败直接跳过这条样本 images.append(img) valid_texts.append(process_text(texts[idx])) valid_nums.append(nums[idx]) valid_labels.append(labels[idx]) return np.array(images), np.array(valid_texts), np.array(valid_nums), np.array(valid_labels)这样处理后四个返回的数组长度完全一致后面训练阶段的 batch 问题就少了一大半。2.3 数据增强策略对多模态模型的影响单一模态模型的数据增强思路大家都比较熟悉图像做随机裁剪、翻转、色彩抖动文本做同义词替换、随机删除。但在多模态场景下增强策略不能简单地各做各的否则模态之间的对应关系会被破坏。举个例子一条样本的文本描述是红色连衣裙图像是一张红色连衣裙的图片。如果你只对文本做同义词替换把红色换成了黑色但图像没变模型就会学到错误的相关性。所以多模态增强的核心原则是常见的增强操作要同步施加在多个模态上或者只做单模态内部且不会破坏语义一致性的增强。我常用的做法是图像增强随机裁剪、水平翻转、色彩抖动这些不会改变商品的本质属性文本增强不改变核心名词只对修饰词做同义替换比如连衣裙保持不变时尚可换成潮流数值增强对价格/销量做小范围的正态扰动噪声标准差取原始值的 5% 以内当然如果你的任务不需要额外增强就能达到不错的效果不要强行加增强有时候过度增强反而会降低泛化性能。3. TensorFlow 侧的多模态模型实现3.1 TensorFlow 函数式 API 搭建多输入模型TensorFlow 2.5.0 的多模态建模最推荐用 Keras 函数式 API。Sequential 只能处理单输入单输出而多模态场景天然就是多输入。函数式 API 的好处是可以灵活定义输入分支、共享层和融合层。先看完整的代码结构import tensorflow as tf from tensorflow.keras import layers, Model # 定义三个输入分支 image_input tf.keras.Input(shape(224, 224, 3), nameimage) text_input tf.keras.Input(shape(128,), nametext) num_input tf.keras.Input(shape(2,), namenumeric) # 图像分支用轻量 CNN 提取特征 x_img layers.Conv2D(32, (3, 3), activationrelu)(image_input) x_img layers.MaxPooling2D((2, 2))(x_img) x_img layers.Conv2D(64, (3, 3), activationrelu)(x_img) x_img layers.MaxPooling2D((2, 2))(x_img) x_img layers.Conv2D(128, (3, 3), activationrelu)(x_img) x_img layers.GlobalAveragePooling2D()(x_img) # 文本分支Embedding BiLSTM x_text layers.Embedding(21128, 128, input_length128)(text_input) x_text layers.Bidirectional(layers.LSTM(64, return_sequencesFalse))(x_text) # 数值分支直接接 Dense x_num layers.Dense(16, activationrelu)(num_input) # 融合层Concat 后接全连接 concat layers.Concatenate()([x_img, x_text, x_num]) x layers.Dense(128, activationrelu)(concat) x layers.Dropout(0.5)(x) output layers.Dense(10, activationsoftmax)(x) model Model(inputs[image_input, text_input, num_input], outputsoutput) model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy])这里有几个需要注意的地方Embedding 层的 vocab_size 参数我用的是 BERT 中文词表所以直接填 21128。如果用的是自己的 tokenizer要根据实际词表大小来设置不然后面训练会报IndexError或无法加载预训练权重LSTM 的return_sequences要根据后续连接的结构来选择。这里直接接全连接层所以设为 False只取最后一个时间步的隐状态Concatenate 层的维度一致性三个分支的输出维度分别是 128图像 GlobalAveragePooling 后、128BiLSTM concat 后、16数值 Dense 后拼接后是 272所以第一层全连接输入维度写 128 没问题如果你有 GPU 显存不足的困扰可以换成更轻量的图像特征提取器比如 MobileNetV2 或 EfficientNetB0把include_topFalse后的全局池化输出作为图像特征。3.2 TensorFlow 训练策略与 checkpoint 保存多模态模型训练比单模态更容易过拟合因为模态越多模型容量越大可记忆的虚假相关性就越多。所以训练策略上我强烈建议开启早停EarlyStopping加上自适应学习率调度ReduceLROnPlateaufrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint callbacks [ EarlyStopping(monitorval_loss, patience8, restore_best_weightsTrue), ReduceLROnPlateau(monitorval_loss, factor0.5, patience3, min_lr1e-6), ModelCheckpoint(best_model_tf.h5, monitorval_accuracy, save_best_onlyTrue, verbose1) ] history model.fit( x{image: X_img_train, text: X_text_train, numeric: X_num_train}, yy_train, validation_data({image: X_img_val, text: X_text_val, numeric: X_num_val}, y_val), epochs50, batch_size32, callbackscallbacks )ReduceLROnPlateau的factor0.5表示验证损失不再下降时学习率减半patience3表示连续 3 个 epoch 没改善才减学习率。这个组合配合早停通常能比固定学习率多提升 2-3 个百分点的准确率而且能有效避免训练后期 loss 震荡。4. PyTorch 侧的多模态实现与跨框架协同4.1 PyTorch 自定义 Dataset 与 DataLoaderPyTorch 的建模流程和 TensorFlow 差异很大需要自己定义 Dataset 类和模型 forward 过程。先看 Dataset 的实现import torch from torch.utils.data import Dataset, DataLoader class MultimodalDataset(Dataset): def __init__(self, img_data, text_data, num_data, labels): # 假设 img_data 已经是 npy 格式预处理好的数组 self.img_data torch.FloatTensor(img_data) self.text_data torch.LongTensor(text_data) self.num_data torch.FloatTensor(num_data) self.labels torch.LongTensor(labels) def __len__(self): return len(self.labels) def __getitem__(self, idx): return { image: self.img_data[idx], text: self.text_data[idx], numeric: self.num_data[idx] }, self.labels[idx] dataset MultimodalDataset(X_img_train, X_text_train, X_num_train, y_train) dataloader DataLoader(dataset, batch_size32, shuffleTrue, num_workers4)num_workers4在多模态任务中尤其重要因为数据加载涉及图像解码和文本 tokenize是纯 CPU 密集操作。如果你不开多进程加载GPU 会疯狂空转等数据训练速度直接慢 3-4 倍。4.2 PyTorch 多模态融合模型实现import torch.nn as nn class MultimodalNet(nn.Module): def __init__(self): super().__init__() self.cnn nn.Sequential( nn.Conv2d(3, 32, kernel_size3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size3, padding1), nn.ReLU(), nn.MaxPool2d(2), ) self.cnn_pool nn.AdaptiveAvgPool2d((1, 1)) self.embedding nn.Embedding(21128, 128) self.lstm nn.LSTM(128, 64, batch_firstTrue, bidirectionalTrue) self.num_fc nn.Linear(2, 16) self.fusion_fc nn.Sequential( nn.Linear(64 * 2 64 * 2 16, 128), nn.ReLU(), nn.Dropout(0.5), nn.Linear(128, 10) ) def forward(self, image, text, numeric): # 图像分支 img_feat self.cnn(image) img_feat self.cnn_pool(img_feat).flatten(1) # 64 维 # 文本分支 text_emb self.embedding(text) text_out, _ self.lstm(text_emb) text_feat text_out[:, -1, :] # 取最后时间步双向 128 维 # 数值分支 num_feat torch.relu(self.num_fc(numeric)) # 16 维 # 多模态融合 concat_feat torch.cat([img_feat, text_feat, num_feat], dim1) output self.fusion_fc(concat_feat) return output这个模型的 forward 流程和前面 TensorFlow 的结构是对齐的方便交叉验证两种框架的结果。注意 PyTorch 里 LSTM 双向的输出维度是hidden_size * 2 128所以融合层的第一维是64 128 16 208写代码时别算错。4.3 TensorFlow 与 PyTorch 跨框架协同方案同时装两个框架除了做对比实验更常见的场景是你用 PyTorch 训练了一个特征提取器想把它的特征传给 TensorFlow 的模型。比如用 PyTorch 加载预训练 BERT把文本编码成向量再用 TensorFlow 做图像文本融合分类。这种搭配在很多工业项目中很常见因为 HuggingFace 生态在 PyTorch 侧最成熟而 TensorFlow 的生产部署链路更完善。具体实现很简单用 PyTorch 提取特征后保存成 npy 文件再用 TensorFlow 加载# PyTorch 侧提取文本特征并保存 text_features [] model_bert.eval() with torch.no_grad(): for batch in text_dataloader: outputs bert_model(**batch) text_features.append(outputs.last_hidden_state[:, 0, :].cpu().numpy()) np.save(text_features.npy, np.concatenate(text_features, axis0)) # TensorFlow 侧加载 feature 作为输入分支 text_feat_input tf.keras.Input(shape(768,), nametext_feat)这样做的好处是解耦了框架依赖两边训练、推理互不干扰。但要注意一旦用 PyTorch 提取了特征TensorFlow 侧的文本分支就变成固定的输入了无法再端到端反向传播到 BERT 层。如果需要端到端训练还是得在一个框架内完成或者用 ONNX 打通。如果你需要把两个框架的模型统一到一条推理链路上ONNX 是最成熟的选择。把 TensorFlow 模型转成 ONNX 用tf2onnx.convertPyTorch 转 ONNX 用torch.onnx.export然后用onnxruntime统一加载推理。这里给一个 PyTorch 转 ONNX 的示例dummy_image torch.randn(1, 3, 224, 224) dummy_text torch.ones(1, 128, dtypetorch.long) dummy_num torch.randn(1, 2) torch.onnx.export( model_pytorch.cpu().eval(), (dummy_image, dummy_text, dummy_num), multimodal_torch.onnx, input_names[image, text, numeric], output_names[probs], opset_version11, dynamic_axes{image: {0: batch}, text: {0: batch}, numeric: {0: batch}} )dynamic_axes参数很关键如果你不设置它导出的模型 batch size 是固定的部署阶段换一个 batch 大小就会报错。我见过很多人因为少了这行代码卡在推理环境部署上。5. 性能提升与泛化能力优化5.1 正则化L2 正则在 PyTorch 中的实现细节多模态模型一旦参数规模上来过拟合几乎是必然的。L2 正则化是最基础也最有效的防过拟合手段。PyTorch 里实现 L2 正则有两种方式一种是在optimizer里直接设置weight_decay另一种是手动计算正则项加到 loss 上。第一种方式最简洁optimizer torch.optim.Adam(model.parameters(), lr1e-3, weight_decay1e-4)第二种方式适合你想对不同层设置不同正则强度的场景def l2_regularizer(model, lambda_l21e-4): reg_loss 0.0 for param in model.parameters(): if param.dim() 1: # 只对权重矩阵做正则不惩罚 bias 和 BN 的 scale reg_loss torch.norm(param, 2) ** 2 return lambda_l2 * reg_loss # 训练循环内 loss criterion(output, target) l2_regularizer(model)这里有一个实操细节建议跳过 bias 项和 BatchNorm 的权重参数因为对它们做 L2 正则不会提升泛化能力反而会导致训练不稳定。判断方式是看param.dim() 1一维的参数通常是 bias 或 BN scale不应该参与正则。这也是为什么手动实现比直接用weight_decay更精细的原因。在 TensorFlow 侧L2 正则通过 Keras 的regularizers参数实现from tensorflow.keras import regularizers Dense(128, activationrelu, kernel_regularizerregularizers.l2(1e-4))5.2 数据增强策略对多模态模型的影响多模态模型训练的另一个重要问题是显存占用。图像分支和文本分支同时计算显存消耗不是简单相加而是近似相乘。我的处理经验优先用混合精度训练。TensorFlow 2.5 和 PyTorch 1.9 都支持自动混合精度。TensorFlow 侧在compile时通过tf.keras.mixed_precision.set_global_policy(mixed_float16)开启PyTorch 侧用torch.cuda.amp.autocast()包裹前向和反向过程。混合精度能把显存占用降低约 50%同时训练速度提升 1.5-2 倍。PyTorch 混合精度的标准写法scaler torch.cuda.amp.GradScaler() for batch in dataloader: optimizer.zero_grad() with torch.cuda.amp.autocast(): output model(**batch) loss criterion(output, target) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()用GradScaler是因为 float16 的梯度值范围很小直接backward()容易下溢成 0导致模型完全学不动。这个坑是混合精度训练最常见的错误来源新手基本都会踩。5.3 多模态融合策略的实战对比多模态融合不是简单地Concat就完事。我的实测经验是融合方式对最终效果的影响非常明显不同任务适合的方案不同融合策略实现方式适用场景我的实测效果早期融合输入阶段就拼接特征模态间关联极强的任务如音画同步数据量不足时容易过拟合晚期融合各模态独立建模后再融合模态独立性强的任务稳定但特征交互信息利用不足注意力加权融合用注意力机制动态分配模态权重模态重要性动态变化的任务效果最好但计算开销大具体到代码实现注意力加权融合是我推荐优先尝试的方案。比如给两个模态各学一个权重分数然后加权求和# PyTorch 实现简单的注意力融合 class AttentionFusion(nn.Module): def __init__(self, feat_dim): super().__init__() self.attention nn.Sequential( nn.Linear(feat_dim * 2, 64), nn.ReLU(), nn.Linear(64, 2), nn.Softmax(dim1) ) def forward(self, img_feat, text_feat): combined torch.cat([img_feat, text_feat], dim1) weights self.attention(combined) # (batch, 2) weighted weights[:, 0:1] * img_feat weights[:, 1:2] * text_feat return weighted这个方案比硬拼接多了可学习的权重分配在模态间差异比较大的场景下确实能带来明显的提升。6. 常见问题与排查技巧实录6.1 环境安装阶段的典型问题第一个高频问题TensorFlow 报错Could not load dynamic library libcudnn.so.8。原因是 LD_LIBRARY_PATH 没有指向 cuDNN 实际安装的目录或者 cuDNN 文件权限不对。排查命令ldconfig -p | grep cudnn如果输出为空说明动态库没被系统找到。把/usr/local/cuda-11.2/lib64加入LD_LIBRARY_PATH后执行ldconfig刷新缓存即可。第二个高频问题PyTorch 的torch.cuda.is_available()返回 False。这个大概率是你装的不是 cu111 版本而是 CPU 版本。用pip list | grep torch查看安装的版本号正确的输出应该是1.9.0cu111如果显示的是1.9.0cpu说明安装命令里的-f参数没生效重新执行安装命令。第三个问题驱动装好后nvidia-smi报 Unknown Error。多半是显卡被系统的其他进程占用了或者内核模块加载失败。先dmesg | grep nvidia看内核日志如果是权限问题检查 secure boot 是否开启CentOS 7.9 开启 secure boot 时 NVIDIA 驱动需要签名否则会拒绝加载。6.2 训练阶段的问题与调试经验训练阶段最常见的问题是OOM显存不足。多模态模型显存消耗大我遇到 OOM 时通常按以下顺序排查先看是不是 batch size 太大64 改 32不行再改 16再看是不是数据加载阶段把整个数据集都放到了 GPU 上最后考虑混合精度或者梯度累积torch.utils.data.DataLoader配合accumulation_steps实现PyTorch 的显存不释放也是老问题。在训练循环里加torch.cuda.empty_cache()或者del掉不再需要的中间变量能缓解但不能根治。根治方法是避免在图构建阶段把不必要的历史叶子节点保留必要时用with torch.no_grad()包裹验证和测试流程。还有一个多模态特有的问题数据加载阶段报IndexError: index out of range in self。这通常是因为不同模态的数据长度不一致比如图像数据 9999 条文本数据 10000 条PyTorch 的 Dataset 在__len__时取了最大值__getitem__时索引就越界了。我前面介绍的对齐校验函数就是为了解决这个问题。这类 bug 很隐蔽训练开始时报错还好排查怕的是某些 batch 恰好没取到越界样本训练几个小时之后才崩前功尽弃。一定要在训练前单独跑一次数据完整性校验打印各模态样本数确认一致再开始。7. 一点额外的建议最后说一个不是所有人都认同、但我觉得很重要的体会不要迷信多模态一定比单模态好先做 ablation study再加模态。我见过很多人一上来就堆了图像、文本、音频、数值四个模态模型是挺复杂的但因为某个模态的数据质量差反而把其他模态的预测结果带偏了。正确的做法是先用单模态建立 baseline然后一个模态一个模态地加进去看每次加的边际收益。如果加了某个模态后验证集指标没有提升就要反思是模态本身的信息量不够还是融合方式不对。环境搭建部分如果不想自己装驱动和 CUDA可以考虑用 Docker 镜像NVIDIA 官方提供了 NGC 容器里面 TensorFlow 和 PyTorch 的版本、CUDA、cuDNN 都匹配好了能省掉很多环境折腾的时间。直接拉下来就能跑比自己手动安装省心很多。这个项目后面其实还能继续扩展比如把两个框架训练出来的模型做集成、用半监督方法利用海量无标注多模态数据、或者在融合层引入更复杂的跨模态注意力机制。但目前这套方案已经足够支撑你在 CentOS 7.9 上把多模态研究的全流程跑通并且得到一个能够稳定提升效果的基线了。
返回列表