第2章 基础回顾:多模态模型基础(CLIP、ViLT)
好,咱们正式开始前,先聊点实在的。多模态模型这几年火得不行,但很多人一上来就扎进数据增强的细节里,反而把地基给忽略了。我个人习惯是,不管学什么新技术,先把它的「祖宗」搞清楚。CLIP 和 ViLT 就是多模态领域的两个关键里程碑。理解了它们,你才知道数据增强到底在「增强」什么。
2.1 模态对齐:让图像和文本「说同一种语言」
多模态模型的核心挑战是什么?说白了,就是让图像和文本这两种完全不同的数据,能在同一个语义空间里对话。
举个例子。你给模型看一张「猫坐在沙发上」的图片,再给它一段文字「a cat sitting on a sofa」。模型得知道,图片里的那个毛茸茸的东西,和文字里的「cat」是同一个概念。这就是模态对齐。
核心概念:模态对齐 ≠ 简单的特征拼接。它要求模型学习一个共享的嵌入空间,在这个空间里,语义相似的图像和文本,它们的特征向量距离很近;语义不相关的,距离很远。
我在项目中遇到过一个问题:直接用预训练的 CNN 提取图像特征,再用 BERT 提取文本特征,然后简单拼起来做分类。效果很差。为什么?因为两个特征空间压根没对齐。图像特征关注的是纹理、边缘,文本特征关注的是语法、词义,它们不在一个频道上。
2.2 CLIP:对比学习的标杆
CLIP(Contrastive Language-Image Pre-training)是 在 2021 年提出的。它的思路非常直接:用 4 亿个图文对,通过对比学习让模型学会对齐。
2.2.1 核心思想:对比学习
CLIP 的训练过程,你可以想象成一个「连连看」游戏。给你一个 batch 的图文对,比如 32 张图片和 32 段文字。模型的任务是:找出哪张图片和哪段文字是配对的。
- 正样本:正确的图文对(比如图片1 配 文本1)
- 负样本:错误的图文对(比如图片1 配 文本2、文本3...)
模型通过对比损失(Contrastive Loss),拉近正样本的距离,推远负样本的距离。就这么简单,但效果惊人。
我的经验:CLIP 的 batch size 非常关键。 用了 32768 的 batch size。为什么这么大?因为负样本越多,对比学习的效果越好。你想想看,如果 batch 里只有 2 个样本,负样本就 1 个,模型学不到什么东西。我自己的实验里,batch size 从 256 提到 1024,zero-shot 准确率直接涨了 5 个点。
2.2.2 双塔架构
CLIP 用了双塔结构:一个图像编码器(通常是 ViT 或 ResNet),一个文本编码器(通常是 Transformer)。两个塔各自提取特征,然后在对比损失里对齐。
# 伪代码:CLIP 的前向过程
image_features = image_encoder(image) # [batch_size, embed_dim]
text_features = text_encoder(text) # [batch_size, embed_dim]
# 归一化
image_features = normalize(image_features)
text_features = normalize(text_features)
# 计算相似度矩阵
logits = image_features @ text_features.T * temperature # [batch_size, batch_size]
# 对比损失(对称形式)
labels = torch.arange(batch_size)
loss_i = cross_entropy(logits, labels) # 图像到文本
loss_t = cross_entropy(logits.T, labels) # 文本到图像
loss = (loss_i + loss_t) / 2
这个架构有个好处:图像和文本的编码器是独立的。训练好后,你可以把图像编码器拆出来做图像检索,或者把文本编码器拆出来做文本分类。灵活性很高。
注意:CLIP 的双塔架构虽然高效,但有个天然缺陷:图像和文本只在最后的对比损失处交互,中间没有任何信息交换。这导致它很难处理细粒度的对齐任务,比如「找出图片里穿红色衣服的人」。这种任务需要图像局部和文本片段的交互,CLIP 做不到。
2.3 ViLT:单塔融合的尝试
ViLT(Vision-and-Language Transformer)是 2021 年提出的。它走了一条和 CLIP 完全不同的路:单塔架构。
2.3.1 核心思想:统一处理
ViLT 的想法很激进:为什么不把图像和文本直接扔进同一个 Transformer 里?它把图像 patch 和文本 拼成一个序列,然后用一个 Transformer 统一处理。
这样做的好处是:图像和文本可以在每一层 Transformer 里充分交互,实现真正的「深度融合」。
2.3.2 架构细节
ViLT 的输入处理非常轻量:
- 文本:用 BERT 的 tokenizer 转成 序列,加上 [CLS] 和 [SEP] 标记。
- 图像:直接把图像切成 patch(比如 16x16),然后线性投影成 embedding。没有用 CNN 做特征提取,也没有用目标检测器。这是 ViLT 最「狠」的地方——它把图像当成「另一种语言的 」来处理。
# 伪代码:ViLT 的输入构造
text_tokens = tokenizer("a cat sitting on a sofa") # [seq_len_text]
image_patches = patchify(image, patch_size=16) # [num_patches, patch_dim]
# 线性投影
text_embeds = text_embedding(text_tokens) # [seq_len_text, hidden_dim]
image_embeds = patch_projection(image_patches) # [num_patches, hidden_dim]
# 拼接
input_embeds = concat([text_embeds, image_embeds]) # [seq_len_text + num_patches, hidden_dim]
# 统一 Transformer
output = transformer(input_embeds)
关键区别:CLIP 是「先独立提取,再对比对齐」;ViLT 是「先拼接,再统一处理」。CLIP 擅长粗粒度的图文匹配,ViLT 擅长细粒度的跨模态理解。
2.4 模态融合:从浅层到深层
聊完两个代表性模型,咱们来总结一下模态融合的几种方式。我习惯把它们分成三个层次:
| 融合方式 | 代表模型 | 交互深度 | 计算开销 | 适用场景 |
|---|---|---|---|---|
| 早期融合(Early Fusion) | ViLT | 高(每层都交互) | 高 | 细粒度理解、VQA |
| 晚期融合(Late Fusion) | CLIP | 低(只在最后交互) | 低 | 粗粒度检索、zero-shot |
| 中间融合(Mid Fusion) | ALBEF、VLMO | 中(部分层交互) | 中 | 平衡场景 |
你可能会问:哪种最好?说实话,没有的好坏。我在做电商图文匹配时,用 CLIP 的晚期融合就够了,因为只需要判断「商品图」和「商品描述」是否匹配。但做视觉问答时,必须用 ViLT 或 ALBEF 这种早期融合,因为需要定位到图片的具体区域。
2.5 本章知识结构图
下面这张图,我把 CLIP 和 ViLT 的核心流程画出来了。你可以看到两种架构的对比:
2.6 为什么数据增强对多模态模型特别重要?
好,基础讲完了。现在回到咱们课程的主题:数据增强。
你可能会问:CLIP 和 ViLT 不是已经很强了吗?为什么还需要数据增强?
嗯,这里有个关键问题:多模态模型对数据分布的敏感性远超单模态模型。
- CLIP 的问题:它依赖海量的图文对,但互联网上的图文对质量参差不齐。比如「一张猫的图片,配文是 'cute animal'」,这种弱对齐的样本太多了。如果不做数据增强,模型很容易学到「猫 = cute animal」这种粗浅的关联,而忽略了「猫 = 有胡须、有尾巴、会喵喵叫」这些细粒度特征。
- ViLT 的问题:它把图像 patch 和文本 拼在一起,对位置信息非常敏感。如果训练数据里「猫」总是出现在图片的左上角,模型就会学到「左上角的 patch 是猫」这种错误的 bias。数据增强(比如随机裁剪、旋转)可以打破这种位置 bias。
避坑指南:我曾经在一个图文匹配项目里,直接用 CLIP 的预训练权重做 fine-tune,没做任何数据增强。结果在测试集上,模型把「白色汽车」和「白色沙发」搞混了。为什么?因为训练数据里白色汽车和白色沙发的背景太相似了。后来加了颜色抖动和背景替换的数据增强,准确率从 82% 提到了 91%。
所以,数据增强不是锦上添花,而是多模态模型泛化性的「刚需」。接下来的章节,我会带你深入各种数据增强技术,看看它们到底是怎么提升模型效果的。