4. 多模态特征提取
多模态融合的第一步,就是把不同模态的数据变成机器能理解的向量。说白了,你得先把图片、文字、声音这些原始信号,统一映射到一个共享的语义空间里。
我刚开始做多模态项目时,踩过最大的坑就是——各模态的特征尺度不一致。图像特征动辄 2048 维,文本可能才 768 维,音频更是五花八门。直接拼起来训练,梯度震荡得根本收敛不了。所以,理解每种模态的特征提取器,是基本功。
4.1 图像特征提取:从 CNN 到 ViT
图像特征提取,传统上靠 CNN。ResNet、EfficientNet 这些 backbone,通过堆叠卷积层和池化层,把一张图逐步压缩成一个特征图。我个人习惯用 ResNet-50 作为基线,输出 2048 维的全局特征。
但 CNN 有个天然缺陷:感受野有限。它只能看到局部,很难捕捉全局依赖。比如一张图里,远处的狗和近处的猫,CNN 需要很多层才能建立关联。
ViT(Vision Transformer)的出现改变了这一点。它把图像切成 patch,像处理文本序列一样用 Transformer 编码。我在项目中试过 ViT-B/16,效果确实好,但训练成本也高。
| 模型 | 输出维度 | 优点 | 缺点 |
|---|---|---|---|
| ResNet-50 | 2048 | 成熟、轻量、易部署 | 局部感受野,长程依赖弱 |
| ViT-B/16 | 768 | 全局建模,效果好 | 需要大量数据,计算量大 |
| Swin-T | 768 | 层次化,兼顾局部与全局 | 实现复杂,调参麻烦 |
4.2 文本特征提取:BERT 与 的取舍
文本特征提取,现在基本是 Transformer 的天下。BERT 和 是两大流派。
BERT 是双向编码器,适合做理解任务。比如情感分析、文本分类。它输出的 [CLS] 向量,通常作为整句的表示。我一般取最后一层 hidden state,维度 768。
是单向解码器,适合做生成任务。但它的中间层也可以提取特征。不过要注意, 的注意力是因果的,只能看到上文。如果你做的是多模态融合,需要上下文双向信息,BERT 更合适。
我曾经在一个图文匹配项目里,用 提取文本特征,结果效果一直上不去。后来换成 BERT,直接涨了 3 个点。为什么?因为匹配任务需要理解整句话,而不是预测下一个词。
4.3 音频特征提取:Wav2Vec 与 Whisper
音频特征提取,以前靠手工特征(MFCC、Fbank)。现在端到端模型越来越流行。
Wav2Vec 2.0 是 Facebook 提出的自监督模型。它把原始音频波形直接编码成特征,不需要人工设计滤波器。输出维度通常是 768 或 1024。我习惯用它做通用音频特征提取,比如语音情感识别、说话人识别。
Whisper 是 的多任务模型。它不仅能做语音识别,还能输出丰富的语义特征。Whisper 的 encoder 输出,其实可以当作音频的语义表示。我在一个视频理解项目里,用 Whisper 的 encoder 特征代替传统声学特征,效果提升明显。
| 模型 | 输出维度 | 适用场景 | 注意事项 |
|---|---|---|---|
| Wav2Vec 2.0 | 768 | 通用音频理解 | 需要微调,对噪声敏感 |
| Whisper | 1024 | 语音识别、语义理解 | 模型大,推理慢 |
| HuBERT | 768 | 语音单元发现 | 训练成本高 |
4.4 特征对齐:维度与尺度的统一
三种模态的特征提取完了,问题来了——维度不一样,尺度也不一样。图像 2048,文本 768,音频 1024。直接拼?梯度会爆炸。
我常用的做法是加一个 线性投影层。把每个模态的特征投影到同一个维度,比如 512 或 768。然后做 LayerNorm,把尺度拉齐。
import torch.nn as nn
class FeatureProjector(nn.Module):
def __init__(self, in_dim, out_dim=512):
super().__init__()
self.proj = nn.Linear(in_dim, out_dim)
self.norm = nn.LayerNorm(out_dim)
def forward(self, x):
return self.norm(self.proj(x))
# 使用示例
image_proj = FeatureProjector(2048, 512)
text_proj = FeatureProjector(768, 512)
audio_proj = FeatureProjector(1024, 512)
下面这张图,展示了多模态特征提取与对齐的完整流程: