07-多模态学习¶
📌 本章定位:理论基础与算法原理
本章侧重多模态学习的数学原理和算法理论,包括: - 多模态表示学习与对齐的数学基础 - 融合策略的理论框架(早期/晚期/中间融合) - CLIP、BLIP等模型的核心算法原理与推导 - 对比学习(InfoNCE Loss)的数学理解
🔗 相关章节导航: | 侧重点 | 章节 | 说明 | |--------|------|------| | 理论原理 | 👉 本文档 | 数学推导、算法原理、融合策略理论 | | CV应用 | 计算机视觉/13-多模态学习 | VLM架构对比、实战项目、部署应用 | | 大模型 | LLM学习/多模态大模型 | GPT-4o/Gemini等多模态大模型 | | 具身智能 | 具身智能/VLA模型 | 视觉-语言-动作模型 |
学习时间: 约6-8小时 难度级别: ⭐⭐⭐⭐ 中高级 前置知识: CNN、Transformer、注意力机制、对比学习概念、PyTorch 学习目标: 理解多模态融合的核心方法,掌握CLIP、BLIP等视觉-语言模型的原理与使用
目录¶
- 1. 多模态学习概述
- 2. 多模态表示与对齐
- 3. 多模态融合策略
- 4. CLIP:对比语言-图像预训练
- 5. BLIP/BLIP-2:视觉-语言预训练
- 6. 视觉问答与图文生成
- 7. 多模态大模型(LMM)
- 8. 实战:CLIP零样本分类与检索
- 9. 面试高频题
- 10. 练习与自我检查
1. 多模态学习概述¶
1.1 什么是多模态¶
| 模态 | 示例 |
|---|---|
| 文本 | 文章、标题、评论 |
| 图像 | 照片、医学影像、卫星图 |
| 音频 | 语音、音乐、环境声 |
| 视频 | 电影、监控、短视频 |
| 结构化 | 表格、知识图谱 |
多模态学习:融合来自多种模态的信息,使模型获得更全面的理解能力。
1.2 核心挑战¶
| 挑战 | 说明 |
|---|---|
| 表示 (Representation) | 如何将不同模态映射到统一空间 |
| 对齐 (Alignment) | 如何找到跨模态的对应关系 |
| 融合 (Fusion) | 如何组合多模态信息 |
| 翻译 (Translation) | 如何从一种模态生成另一种 |
| 协同学习 | 一种模态的知识如何帮助另一种 |
1.3 应用全景¶
多模态应用
├── 理解类
│ ├── 图像描述生成 (Image Captioning)
│ ├── 视觉问答 (VQA)
│ └── 视频理解
├── 检索类
│ ├── 文搜图 / 图搜文
│ └── 跨模态检索
├── 生成类
│ ├── 文生图 (Text-to-Image)
│ ├── 文生视频
│ └── 语音合成
└── 交互类
├── 多模态对话
└── 具身智能 (Embodied AI)
2. 多模态表示与对齐¶
2.1 联合表示 vs 协同表示¶
| 类型 | 说明 | 方法 |
|---|---|---|
| 联合表示 | 将多模态特征投影到同一共享空间 | CLIP, VSE++ |
| 协同表示 | 各模态保留独立表示,通过约束保持关联 | CCA, 双塔模型 |
2.2 对齐方式¶
| 方式 | 粒度 | 示例 |
|---|---|---|
| 全局对齐 | 整张图 ↔ 整个句子 | CLIP |
| 局部对齐 | 图像区域 ↔ 词/短语 | SCAN, ALBEF |
| 时序对齐 | 视频帧 ↔ 字幕时间戳 | 视频描述生成 |
2.3 对比学习用于对齐¶
3. 多模态融合策略¶
3.1 三种融合时机¶
| 策略 | 位置 | 特点 |
|---|---|---|
| 早期融合 | 输入层拼接 | 简单,但难以处理异构输入 |
| 晚期融合 | 决策层融合 | 各模态独立编码,灵活 |
| 中间融合 | 中间层交互 | 平衡交互深度和模块化 |
3.2 注意力融合¶
| 方法 | 机制 |
|---|---|
| Cross-Attention | 一种模态作为Query,另一种作为Key/Value |
| Co-Attention | 两种模态互相关注 |
| Self-Attention | 拼接所有模态后统一自注意力 |
3.3 Cross-Attention 实现¶
import torch
import torch.nn as nn
class CrossAttention(nn.Module): # 继承nn.Module定义神经网络层
"""跨模态注意力:query来自模态A, key/value来自模态B"""
def __init__(self, dim, n_heads=8): # __init__构造方法,创建对象时自动调用
super().__init__() # super()调用父类方法
self.n_heads = n_heads
self.head_dim = dim // n_heads
self.q_proj = nn.Linear(dim, dim)
self.k_proj = nn.Linear(dim, dim)
self.v_proj = nn.Linear(dim, dim)
self.out_proj = nn.Linear(dim, dim)
self.scale = self.head_dim ** -0.5
def forward(self, query, context):
"""
query: [B, Lq, D] 模态A的序列
context: [B, Lk, D] 模态B的序列
"""
B, Lq, D = query.shape
Lk = context.size(1)
Q = self.q_proj(query).view(B, Lq, self.n_heads, self.head_dim).transpose(1, 2) # 链式调用,连续执行多个方法
K = self.k_proj(context).view(B, Lk, self.n_heads, self.head_dim).transpose(1, 2)
V = self.v_proj(context).view(B, Lk, self.n_heads, self.head_dim).transpose(1, 2)
attn = (Q @ K.transpose(-2, -1)) * self.scale
attn = attn.softmax(dim=-1)
out = (attn @ V).transpose(1, 2).reshape(B, Lq, D) # reshape重塑张量形状
return self.out_proj(out)
class MultimodalFusionBlock(nn.Module):
"""双向跨模态融合块"""
def __init__(self, dim):
super().__init__()
self.text_to_image = CrossAttention(dim)
self.image_to_text = CrossAttention(dim)
self.norm1 = nn.LayerNorm(dim)
self.norm2 = nn.LayerNorm(dim)
self.ffn_image = nn.Sequential(
nn.Linear(dim, dim * 4), nn.GELU(), nn.Linear(dim * 4, dim)
)
self.ffn_text = nn.Sequential(
nn.Linear(dim, dim * 4), nn.GELU(), nn.Linear(dim * 4, dim)
)
def forward(self, image_feat, text_feat):
# 图像关注文本
image_feat = image_feat + self.text_to_image(image_feat, text_feat)
image_feat = image_feat + self.ffn_image(self.norm1(image_feat))
# 文本关注图像
text_feat = text_feat + self.image_to_text(text_feat, image_feat)
text_feat = text_feat + self.ffn_text(self.norm2(text_feat))
return image_feat, text_feat
4. CLIP:对比语言-图像预训练¶
4.1 架构¶
CLIP架构
图像编码器(ViT/ResNet) 文本编码器(Transformer)
↓ ↓
图像嵌入 vᵢ 文本嵌入 tⱼ
↓ ↓
L2归一化 L2归一化
↘ ↙
相似度矩阵 S[i,j] = vᵢ·tⱼ / τ
↓
对比损失 (InfoNCE)
4.2 训练目标¶
给定 batch 中 \(N\) 对图文对,训练互信息最大化:
4.3 CLIP的能力¶
| 能力 | 说明 |
|---|---|
| 零样本分类 | 无需训练样本,通过文本描述分类 |
| 跨模态检索 | 文搜图 / 图搜文 |
| 开放词汇目标检测 | 检测任意文本描述的物体 |
| 图文相似度 | 评估图文匹配程度 |
4.4 零样本分类原理¶
输入图像 → 图像编码器 → 图像嵌入 v
候选标签: ["猫", "狗", "鸟"] → 文本模板: "一张{类别}的照片"
→ 文本编码器 → 文本嵌入 [t₁, t₂, t₃]
预测类别 = argmax(v · tᵢ)
5. BLIP/BLIP-2:视觉-语言预训练¶
5.1 BLIP的三个预训练任务¶
| 任务 | 目标 | 作用 |
|---|---|---|
| ITC | 图文对比学习 | 对齐表示 |
| ITM | 图文匹配判断 | 细粒度理解 |
| LM | 图像条件文本生成 | 生成能力 |
5.2 BLIP-2的Q-Former¶
引入Q-Former作为图像编码器与LLM之间的桥梁:
优势:将图像理解与语言生成解耦,大幅减少训练成本。
5.3 视觉-语言模型对比¶
| 模型 | 架构 | 预训练数据 | 关键创新 |
|---|---|---|---|
| CLIP | 双塔 | 4亿图文对 | 对比学习,零样本 |
| BLIP | 统一编解码 | 1.29亿 | 字幕生成+过滤去噪 |
| BLIP-2 | Q-Former + LLM | — | 桥接冻结视觉与语言模型 |
| LLaVA | ViT + LLM | 150K | 视觉指令调优 |
6. 视觉问答与图文生成¶
6.1 视觉问答(VQA)¶
6.2 图像描述生成(Image Captioning)¶
class SimpleCaptioner(nn.Module):
"""简化的图像描述生成模型"""
def __init__(self, visual_dim, vocab_size, embed_dim, hidden_dim):
super().__init__()
self.visual_proj = nn.Linear(visual_dim, embed_dim)
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.decoder = nn.TransformerDecoder(
nn.TransformerDecoderLayer(embed_dim, nhead=8),
num_layers=6
)
self.output_proj = nn.Linear(embed_dim, vocab_size)
def forward(self, visual_features, token_ids):
"""
visual_features: [B, N_patch, visual_dim] 图像特征
token_ids: [B, L] 文本token序列
"""
# 投影视觉特征
memory = self.visual_proj(visual_features) # [B, N_patch, embed_dim]
memory = memory.transpose(0, 1) # [N_patch, B, embed_dim]
# 文本嵌入
tgt = self.embedding(token_ids).transpose(0, 1) # [L, B, embed_dim]
# 因果掩码
L = tgt.size(0)
causal_mask = torch.triu(torch.ones(L, L), diagonal=1).bool()
# 解码
out = self.decoder(tgt, memory, tgt_mask=causal_mask)
return self.output_proj(out.transpose(0, 1)) # [B, L, vocab_size]
7. 多模态大模型(LMM)¶
7.1 架构范式¶
LMM通用架构:
视觉编码器 (ViT, 通常冻结)
↓
投影层 / 适配器(桥接视觉与语言)
↓
大语言模型 (LLM, 可能冻结/LoRA微调)
↓
多模态输出(文本回答、推理等)
7.2 代表模型¶
| 模型 | LLM基座 | 视觉编码器 | 特点 |
|---|---|---|---|
| GPT-4V | GPT-4 | 未公开 | 最强多模态理解 |
| LLaVA | Vicuna/Llama | CLIP ViT | 简洁高效,开源 |
| Qwen-VL | Qwen | ViT | 中文优秀 |
| InternVL | InternLM | InternViT | 动态分辨率 |
7.3 视觉指令调优¶
与纯文本指令调优类似,使用带图像的指令数据微调模型:
{
"image": "photo_001.jpg",
"conversations": [
{"from": "human", "value": "<image>\n请描述这张图片的内容。"},
{"from": "gpt", "value": "图片展示了一只金毛猎犬在草地上奔跑..."}
]
}
8. 实战:CLIP零样本分类与检索¶
import torch
import torch.nn.functional as F
from PIL import Image
# 使用HuggingFace的transformers加载CLIP
# pip install transformers
from transformers import CLIPProcessor, CLIPModel
def clip_zero_shot_classification(image_path, candidate_labels):
"""CLIP零样本图像分类"""
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
image = Image.open(image_path)
texts = [f"a photo of a {label}" for label in candidate_labels] # 列表推导式,简洁创建列表
inputs = processor(text=texts, images=image, return_tensors="pt", padding=True)
with torch.no_grad(): # 禁用梯度计算,节省内存
outputs = model(**inputs)
logits = outputs.logits_per_image # [1, n_labels]
probs = logits.softmax(dim=-1)
for label, prob in zip(candidate_labels, probs[0]): # zip按位置配对多个可迭代对象
print(f" {label}: {prob.item():.4f}") # .item()将单元素张量转为Python数值
predicted = candidate_labels[probs.argmax().item()]
return predicted
def clip_image_text_retrieval(image_paths, query_text):
"""CLIP文搜图检索"""
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
images = [Image.open(p) for p in image_paths]
inputs = processor(text=[query_text], images=images,
return_tensors="pt", padding=True)
with torch.no_grad():
outputs = model(**inputs)
similarities = outputs.logits_per_text[0] # [n_images]
ranked = similarities.argsort(descending=True)
print(f"查询: '{query_text}'")
for rank, idx in enumerate(ranked): # enumerate同时获取索引和元素
print(f" #{rank+1}: {image_paths[idx]} (score: {similarities[idx]:.3f})")
return ranked
# 使用示例
# clip_zero_shot_classification("cat.jpg", ["cat", "dog", "bird", "car"])
# clip_image_text_retrieval(["a.jpg", "b.jpg", "c.jpg"], "a dog playing frisbee")
9. 面试高频题¶
Q1: CLIP是如何实现零样本分类的?¶
答:CLIP通过对比学习在4亿图文对上训练图像编码器和文本编码器,将图像和文本映射到同一嵌入空间。零样本分类时,将候选类别通过文本模板(如"a photo of a {class}")编码为文本嵌入,计算输入图像嵌入与各文本嵌入的余弦相似度,选择相似度最高的类别。无需任何标注训练数据即可分类。
Q2: 多模态融合有哪几种策略?各有什么优缺点?¶
答:(1) 早期融合:输入层拼接,交互充分但难处理异构数据;(2) 晚期融合:各模态独立编码后在决策层合并,灵活但交互不够;(3) 中间融合:通过Cross-Attention等在中间层交互,平衡深度与模块化。实际中间融合最常用,如BLIP-2的Q-Former。
Q3: BLIP-2的Q-Former解决了什么问题?¶
答:Q-Former通过一组可学习的查询向量,在冻结的图像编码器和冻结的LLM之间搭建桥梁。它解决了两个问题:(1) 避免端到端训练大型视觉和语言模型的巨大计算开销;(2) 通过交叉注意力从图像中提取与语言相关的信息,过滤无关视觉信息。
Q4: 当前多模态大模型的通用架构是什么?¶
答:视觉编码器(通常是预训练的ViT/CLIP视觉部分) + 投影/适配层(如MLP或Q-Former) + 大语言模型。视觉编码器将图像转为patch特征序列,投影层将视觉特征映射到LLM的输入空间,LLM基于视觉信息进行推理和文本生成。
Q5: 跨模态对齐的挑战是什么?¶
答:(1) 语义鸿沟——不同模态的底层表示差异巨大(像素 vs 词向量);(2) 粒度不匹配——图像是2D空间信号,文本是1D序列,且描述粒度不同;(3) 多对多关系——一张图可对应多种描述,一段文本可匹配多张图;(4) 数据质量——网络爬取的图文对噪声大、配对不精确。
10. 练习与自我检查¶
编程练习¶
- 基础:使用HuggingFace加载CLIP模型,实现零样本图像分类
- 进阶:实现一个跨模态检索系统(文搜图 + 图搜文)
- 挑战:基于BLIP-2实现图像描述生成,并尝试视觉问答
检查清单¶
- 能说明多模态学习的五大挑战(表示、对齐、融合、翻译、协同学习)
- 理解早期/中间/晚期融合的区别
- 能实现Cross-Attention跨模态融合模块
- 理解CLIP的对比学习训练方式和零样本分类原理
- 了解BLIP-2的Q-Former架构和训练策略
- 知道当前多模态大模型的通用架构范式
- 能使用现有工具完成零样本分类或检索任务
- 了解视觉指令调优的概念
扩展阅读: - Radford et al., 2021: Learning Transferable Visual Models From Natural Language Supervision (CLIP) - Li et al., 2023: BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and LLMs - Liu et al., 2024: Visual Instruction Tuning (LLaVA) - Baltrušaitis et al., 2019: Multimodal Machine Learning: A Survey and Taxonomy