共计 2135 个字符,预计需要花费 6 分钟才能阅读完成。
跨模态检索的挑战与机遇
在推荐系统和智能搜索场景中,我们常常需要处理图像和文本之间的关联匹配。传统方法依赖于手工设计的特征工程,比如用 SIFT 提取图像特征,用 TF-IDF 处理文本。这种方式存在明显的局限性:

- 手工特征难以捕捉高层次语义信息
- 不同模态的特征空间维度不一致,无法直接比较
- 特征提取流程与后续任务分离,无法端到端优化
CLIP 的创新设计
CLIP(Contrastive Language-Image Pretraining)通过对比学习解决了这些问题。相比 VSE++ 等早期方案,CLIP 的优势在于:
- 双塔结构 :
- 图像编码器(通常用 ResNet 或 ViT)
- 文本编码器(通常用 Transformer)
-
两个编码器并行处理不同模态数据
-
计算效率 :
- 离线计算特征向量,在线检索只需计算余弦相似度
-
适合大规模部署
-
对比学习目标 :
- 通过 InfoNCE 损失拉近正样本对距离
- 推开负样本对距离
PyTorch 实现详解
模型构建
import torch
import torch.nn as nn
from torchvision.models import resnet50
from transformers import AutoTokenizer, AutoModel
class ImageEncoder(nn.Module):
def __init__(self):
super().__init__()
self.model = resnet50(pretrained=True)
self.model.fc = nn.Identity() # 移除最后的全连接层
def forward(self, x):
return self.model(x)
class TextEncoder(nn.Module):
def __init__(self):
super().__init__()
self.tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
self.model = AutoModel.from_pretrained('bert-base-uncased')
def forward(self, text):
inputs = self.tokenizer(text, return_tensors='pt', padding=True, truncation=True)
outputs = self.model(**inputs)
return outputs.last_hidden_state[:,0,:] # 取 [CLS] token 作为句子表示
对比损失实现
InfoNCE 损失的数学表达:
$$
\mathcal{L} = -\log\frac{\exp(s_{i,j}/\tau)}{\sum_{k=1}^N \exp(s_{i,k}/\tau)}
$$
其中 $\tau$ 是温度系数,代码实现:
def contrastive_loss(image_emb, text_emb, temperature=0.07):
# 计算相似度矩阵
logits = image_emb @ text_emb.T / temperature
# 对角线是正样本对
labels = torch.arange(len(logits)).to(logits.device)
# 对称计算两个方向的损失
loss_i = nn.CrossEntropyLoss()(logits, labels)
loss_t = nn.CrossEntropyLoss()(logits.T, labels)
return (loss_i + loss_t) / 2
训练技巧与优化
批处理负采样
- 利用当前 batch 内的其他样本作为负样本
- 无需额外存储负样本队列
混合精度训练
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
with autocast():
image_emb = image_encoder(images)
text_emb = text_encoder(texts)
loss = contrastive_loss(image_emb, text_emb)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
特征归一化
# 在计算相似度前先做 L2 归一化
image_emb = nn.functional.normalize(image_emb, dim=-1)
text_emb = nn.functional.normalize(text_emb, dim=-1)
实践中的经验总结
- 数据预处理 :
- 图像缩放保持长宽比,用 letterbox 填充
-
文本统一小写处理
-
超参数调优 :
- 初始学习率建议 3e-5
-
batch size 尽可能大(至少 256)
-
调试技巧 :
- 用 t -SNE 可视化特征空间分布
- 定期检查 top- k 检索准确率
扩展与展望
- 视频文本检索 :
- 用 3D CNN 处理视频
-
时间维度上做 pooling
-
模型蒸馏 :
- 用大模型生成伪标签
- 训练轻量化的学生模型
完整代码已上传 Colab: 实践链接
通过 CLIP 的双塔结构,我们实现了高效的跨模态检索。这种设计不仅适用于图文场景,经过适当调整还能扩展到音频、视频等多模态领域,为构建更智能的推荐系统提供了坚实基础。
正文完
