2. 特征提取基础:图像特征(CNN/ViT)、文本特征(BERT/)、音频特征(MFCC/CLAP)
跨模态检索的核心,说白了就是让不同模态的数据在同一个特征空间里“对话”。而特征提取,就是给每种模态的数据“翻译”成机器能理解的向量。这一步做不好,后面的对齐和检索全是空中楼阁。我这些年调过的系统里,至少有一半的性能问题,根子都出在特征提取上。
2.1 图像特征:从 CNN 到 ViT
图像特征提取,过去十年基本是 CNN 的天下。但这两年,ViT 开始抢班夺权了。咱们得两手都抓。
2.1.1 CNN 特征:ResNet 的“残差”哲学
CNN 通过卷积核滑动窗口提取局部特征,层数越深,感受野越大。但层数一深,梯度消失问题就来了。ResNet 的解决方案很巧妙——引入残差连接。
核心公式:输出 = F(x) + x
让网络直接学习“残差”F(x),而不是完整的映射。这样梯度可以直通浅层,训练 100 层以上的网络也不怕。
我在项目中常用的是 ResNet-50 或 ResNet-101,在 ImageNet 上预训练后,去掉最后的全连接层,取倒数第二层的 2048 维向量作为图像特征。效果稳定,部署也方便。
我的经验:如果你做细粒度检索(比如不同款式的鞋子),建议用 ResNet-101。如果是粗粒度场景(比如区分猫和狗),ResNet-50 就够了,还能省一半显存。
2.1.2 ViT 特征:把图像当句子看
ViT 的思路很直接:把图像切成 16x16 的 patch,每个 patch 拉平后当成一个“词”,然后扔给 Transformer 处理。它天然能捕捉全局依赖,不像 CNN 要靠堆叠层数来扩大感受野。
ViT 输出的 [CLS] 对应的向量,通常作为整张图的全局特征。我个人习惯用 ViT-B/16 作为基线,特征维度 768,比 ResNet 的 2048 小不少,但检索精度往往更高。
| 模型 | 特征维度 | 参数量 | 适用场景 |
|---|---|---|---|
| ResNet-50 | 2048 | 25.6M | 通用检索、移动端部署 |
| ResNet-101 | 2048 | 44.5M | 细粒度分类、高精度检索 |
| ViT-B/16 | 768 | 86M | 全局语义理解、跨模态对齐 |
| ViT-L/16 | 1024 | 307M | 高精度、大算力场景 |
避坑指南:ViT 需要大量数据预训练才能发挥威力。如果你只有几千张图,直接用 ViT 效果可能还不如 ResNet。我曾经在一个小数据集上硬上 ViT,结果 mAP 掉了 8 个点。后来换成 ResNet-101 才救回来。
2.2 文本特征:BERT 与 的“双向”与“单向”
文本特征提取,现在基本是预训练语言模型的天下。BERT 和 是两大流派,各有各的脾气。
2.2.1 BERT:双向编码,理解为王
BERT 用 Masked Language Model 做预训练,随机遮住 15% 的词,让模型根据上下文去猜。这种双向机制让它特别擅长“理解”语义。
提取特征时,我通常取 BERT 最后一层的 [CLS] 向量,或者对所有 做平均池化。维度一般是 768(base 版本)。
代码示例:BERT 特征提取
from transformers import BertTokenizer, BertModel
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
text = "A dog is running in the park"
inputs = tokenizer(text, return_tensors='pt', padding=True, truncation=True)
outputs = model(**inputs)
# 取 [CLS] 向量作为文本特征
text_feat = outputs.last_hidden_state[:, 0, :] # shape: [1, 768]
2.2.2 :单向生成,表达为王
用自回归方式预训练,从左到右逐个预测下一个词。它擅长“生成”而不是“理解”。在跨模态检索里, 的特征通常用于生成式任务,比如图像描述生成。
提取特征时,取最后一层的最后一个 的隐状态。维度也是 768( base)。
| 模型 | 预训练方式 | 特征维度 | 擅长任务 |
|---|---|---|---|
| BERT-base | 双向 MLM | 768 | 文本理解、检索、分类 |
| BERT-large | 双向 MLM | 1024 | 高精度理解、NLI |
| 单向自回归 | 768 | 文本生成、图像描述 | |
| 单向自回归 | 12288 | 少样本生成、对话 |
我的建议:做文本到图像的跨模态检索,首选 BERT。做图像到文本的生成,可以考虑 。但如果你资源有限,BERT 是更稳妥的选择——它在下游任务上的泛化能力更强。
2.3 音频特征:MFCC 与 CLAP
音频特征提取,传统方法用 MFCC,深度学习时代用 CLAP 这类预训练模型。
2.3.1 MFCC:经典手工特征
MFCC 模拟人耳听觉特性,把音频信号转换成 13-40 维的系数序列。提取流程大致是:预加重 → 分帧 → 加窗 → FFT → Mel 滤波器组 → 对数运算 → DCT。
我一般取 40 维 MFCC,加上一阶和二阶差分,得到 120 维特征。然后做时间维度的平均池化,得到一个固定长度的向量。
MFCC 特征维度:40(静态)+ 40(一阶差分)+ 40(二阶差分)= 120 维
2.3.2 CLAP:跨模态音频预训练
CLAP 是 推出的音频-文本对比学习模型,类似 CLIP 但用于音频。它把音频和文本映射到同一个特征空间,直接输出 512 维或 1024 维的向量。
用 CLAP 提取音频特征,不需要手工设计滤波器,直接输入原始波形或 Mel 频谱图就行。效果比 MFCC 好一大截,尤其在跨模态检索场景下。
| 方法 | 特征维度 | 是否需要标注数据 | 检索精度 |
|---|---|---|---|
| MFCC + 均值池化 | 120 | 否 | 低 |
| CLAP (base) | 512 | 是(预训练) | 高 |
| CLAP (large) | 1024 | 是(预训练) | 很高 |
避坑指南:MFCC 对采样率敏感。我曾经用 16kHz 的音频提取 MFCC,换到 44.1kHz 后特征分布全变了,检索结果一塌糊涂。后来统一重采样到 16kHz 才解决问题。如果你用 CLAP,记得把音频重采样到 48kHz,这是它预训练时用的采样率。
2.4 本章知识结构图
下面这张图梳理了三种模态的特征提取路径,以及它们最终汇聚到统一特征空间的过程。
2.5 特征提取的通用原则
不管用哪种模型,有几个原则是通用的:
- 维度匹配:不同模态的特征最终要映射到同一维度,通常是 256、512 或 768。我习惯用 512 维,平衡了精度和计算开销。
- 归一化:特征向量一定要做 L2 归一化,否则检索时欧氏距离会被维度高的模态主导。我见过有人没做归一化,结果图像特征总是占主导,文本特征基本没起作用。
- 预训练权重:尽量用在大规模数据上预训练好的权重,不要从头训练。除非你的数据量超过百万级。
- 特征存储:提取好的特征建议用 FAISS 或 Milvus 建索引,不要每次检索都重新提取。离线提取、在线检索,这是工业界的标准做法。
我的习惯:每次提取特征后,我都会随机抽 1000 条做一次 t-SNE 可视化,看看不同模态的特征分布是否合理。如果同类样本在空间里散成一团,那说明特征提取有问题,得回去调模型。