VLM架构分类体系
视觉语言模型(Vision-Language Model,VLM)是当前多模态学习的核心研究方向,其架构设计直接影响模型的多模态理解能力。1 本篇系统性梳理VLM的架构分类体系,从融合方式、训练策略、应用场景三个维度进行分类,并深入分析各代表性架构的设计原理与演进路径。
VLM架构分类概述
分类体系
VLM架构可以从多个角度进行分类,形成一个多层次的分类体系:
┌─────────────────────────────────────────────────────────────────┐
│ VLM架构分类体系 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 按融合方式分类 │ │ 按训练策略分类 │ │ 按应用场景分类 │ │
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
│ │ • Prefix-based │ │ • 模态对齐预训练 │ │ • 视觉问答 │ │
│ │ • Gating-based │ │ • 指令微调 │ │ • 图文检索 │ │
│ │ • Cross-attention│ │ • 端到端训练 │ │ • 视觉对话 │ │
│ │ • 混合架构 │ │ • 轻量级适配 │ │ • 多图像理解 │ │
│ │ • 端到端架构 │ │ │ │ • 视频理解 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
按融合方式分类
| 融合方式 | 代表模型 | 核心特征 | 适用场景 |
|---|---|---|---|
| Prefix-based | LLaVA系列 | 视觉特征作为LLM的输入前缀 | 视觉问答、对话 |
| Gating-based | MiniGPT-4 | Gating机制控制信息流 | 轻量级多模态 |
| Cross-attention | InstructBLIP | 跨模态注意力交互 | 指令跟随任务 |
| 混合架构 | LEO、Florence-VL | 多编码器动态融合 | 高分辨率、复杂场景 |
| 端到端架构 | GPT-4V、Gemini | 原生多模态设计 | 全场景多模态理解 |
按训练策略分类
- 模态对齐预训练:冻结视觉编码器和LLM,仅训练模态融合模块
- 指令微调:在预训练基础上使用指令数据微调
- 端到端训练:所有组件联合训练
- 轻量级适配:使用LoRA、QLoRA等PEFT方法
架构演进时间线
2021 2023 2024-2025
│ │ │
▼ ▼ ▼
┌──────┐ ┌──────┬──────┬──────┐ ┌──────┬──────┬──────┐
│ CLIP │ │BLIP-2│LLaVA │MiniGPT│ │LLaVA-N│LEO │Florence│
│ 双编码│ ──────────▶ │Q-Former│Prefix│Gating│ │高分辨率│多编码器│VL │
└──────┘ └──────┴──────┴──────┘ └──────┴──────┴──────┘
│ │ │
▼ ▼ ▼
早期融合 中期融合 动态融合
(双塔结构) (桥梁连接) (专家混合)
┌─────────────────────────────────────┐
│ 原生多模态时代 │
│ GPT-4V ──── Gemini ──── Claude │
└─────────────────────────────────────┘
Prefix-based架构(LLaVA系列)
Prefix-based架构是当前最流行的VLM架构范式,其核心思想是将视觉编码器的输出作为语言模型的输入前缀,实现视觉到语言的转换。2
架构设计原理
核心思想
Prefix-based架构遵循”语言模型作为核心”的理念:
- 视觉编码器:使用预训练的CLIP ViT提取图像特征
- 投影层:将视觉特征映射到语言模型的输入空间
- 语言模型:冻结的LLM负责理解和生成
图像输入 投影层 LLM
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌─────────────────┐
│ CLIP │ │ 线性/MLP │ │ │
│ ViT-L/14│ ──────────▶ │ 投影 │ ──────────▶ │ Vicuna/LLaMA │
│ (冻结) │ │ (可训练) │ │ (冻结/微调) │
└─────────┘ └──────────┘ └─────────────────┘
│
▼
文本输出
数学表示
设视觉编码器为 ,其中 为patch数量, 为视觉特征维度。投影层 将视觉特征映射到语言空间。语言模型接收拼接后的输入:
其中 表示拼接操作。
LLaVA-1.0:开创性工作
LLaVA-1.0首次验证了视觉指令微调的可行性。2
class LLaVA_v1(nn.Module):
"""
LLaVA 1.0 架构实现
"""
def __init__(self, vision_encoder, language_model, config):
super().__init__()
self.vision_encoder = vision_encoder # CLIP ViT-L/14
self.language_model = language_model # Vicuna-7B/13B
# 简单的线性投影
vision_dim = vision_encoder.embed_dim # 1024
hidden_dim = language_model.config.hidden_size # 4096
self.projection = nn.Linear(vision_dim, hidden_dim)
def forward(self, images, input_ids, attention_mask=None, labels=None):
# 1. 视觉编码
image_features = self.vision_encoder(images)
# shape: (B, num_patches, 1024), num_patches = 256 for 224x224
# 2. 投影到语言空间
image_tokens = self.projection(image_features)
# shape: (B, num_patches, 4096)
# 3. 获取文本embedding
text_tokens = self.language_model.get_input_embeddings()(input_ids)
# 4. 将视觉tokens插入文本序列
inputs_embeds = self._merge_visual_tokens(image_tokens, text_tokens, input_ids)
# 5. LLM前向
outputs = self.language_model(
inputs_embeds=inputs_embeds,
attention_mask=attention_mask,
labels=labels
)
return outputs训练流程
LLaVA采用两阶段训练策略:
阶段1:预训练(特征对齐)
┌────────────┐ ┌────────────┐
│ CLIP ViT │ │ Vicuna │
│ (冻结) │ │ (冻结) │
└─────┬──────┘ └─────┬──────┘
│ │
▼ ▼
┌────────────────────────────────┐
│ 投影层W (可训练) │
│ 学习目标:图文匹配损失 │
└────────────────────────────────┘
训练数据:CC3M (595K图文对)
阶段2:指令微调
┌────────────┐ ┌────────────┐
│ CLIP ViT │ │ Vicuna │
│ (冻结) │ │ (微调) │
└─────┬──────┘ └─────┬──────┘
│ │
▼ ▼
┌────────────────────────────────┐
│ 投影层W (冻结) │
│ 学习目标:指令跟随损失 │
└────────────────────────────────┘
训练数据:GPT-4生成的视觉指令(158K)
LLaVA-1.5:能力增强
LLaVA-1.5在多个方面进行了改进:
- MLP投影:使用2层MLP替代线性投影,增强表达能力
- 更高分辨率:支持336x336和更高分辨率输入
- 数据质量提升:使用更优质的训练数据
class LLaVA_v15(nn.Module):
"""
LLaVA 1.5 架构实现
"""
def __init__(self, vision_encoder, language_model):
super().__init__()
self.vision_encoder = vision_encoder
self.language_model = language_model
vision_dim = vision_encoder.embed_dim
hidden_dim = language_model.config.hidden_size
# MLP投影:增强表达能力
self.projection = nn.Sequential(
nn.Linear(vision_dim, hidden_dim),
nn.GELU(),
nn.Linear(hidden_dim, hidden_dim)
)
# 额外的对齐损失
self.alignment_loss = nn.MSELoss()
def compute_alignment_loss(self, image_features, text_features):
"""
对比对齐损失:让视觉特征与对应文本特征接近
"""
# 归一化
image_features = F.normalize(image_features, dim=-1)
text_features = F.normalize(text_features, dim=-1)
# 计算相似度
similarity = torch.matmul(image_features, text_features.T)
return self.alignment_loss(similarity, torch.eye(similarity.size(0)).to(similarity.device))LLaVA-1.6 / LLaVA-NeXT:高分辨率处理
LLaVA-1.6引入AnyRes策略处理高分辨率图像,同时支持多图像和视频输入。
AnyRes策略
AnyRes的核心思想是将高分辨率图像分成多个低分辨率子图:
原始高分辨率图像 (672x672)
│
▼
┌───────────────────┐
│ 2x2 网格分割 │
├───────┬───────┬───┤
│ 子图1 │ 子图2 │ │
│(336x336)│(336x336)│ │
├───────┼───────┤ │
│ 子图3 │ 子图4 │ │
│(336x336)│(336x336)│ │
└───────┴───────┴───┘
│
▼
┌───────────────────┐
│ 全局特征 (1x1) │
│ + 局部特征 (2x2) │
│ = 5x patches │
└───────────────────┘
class AnyResStrategy:
"""
AnyRes高分辨率处理策略
"""
def __init__(self, grid_size=2, resolution=336):
self.grid_size = grid_size
self.resolution = resolution
def split_image(self, image):
"""
将高分辨率图像分割为多个子图
"""
B, C, H, W = image.shape
h, w = H // self.grid_size, W // self.grid_size
sub_images = []
for i in range(self.grid_size):
for j in range(self.grid_size):
sub_img = image[:, :, i*h:(i+1)*h, j*w:(j+1)*w]
sub_images.append(sub_img)
return sub_images
def encode(self, image, vision_encoder, projector):
# 全局特征
global_features = vision_encoder(image)
# 子图特征
sub_images = self.split_image(image)
sub_features = [vision_encoder(sub) for sub in sub_images]
# 拼接全局和局部特征
all_features = torch.cat([global_features] + sub_features, dim=1)
# 投影
return projector(all_features)LLaVA-1.5/1.6对比
| 特性 | LLaVA-1.0 | LLaVA-1.5 | LLaVA-1.6/NeXT |
|---|---|---|---|
| 投影层 | 线性 | MLP (2层) | MLP (2层) |
| 分辨率 | 224x224 | 336x336 | 可变(最高高分辨率) |
| 训练数据 | CC3M + GPT-4 | 558K + 过滤数据 | 1.2M多样化数据 |
| 多图像 | 不支持 | 不支持 | 支持 |
| 视频 | 不支持 | 不支持 | 支持(采样帧) |
| OCR能力 | 弱 | 中等 | 强 |
LLaVA-OneVision:统一架构
LLaVA-OneVision (2024) 是LLaVA系列的集大成者,实现了单图像、多图像、视频的统一处理能力。
class LLaVAOneVision(nn.Module):
"""
LLaVA-OneVision: 统一的多模态架构
"""
def __init__(self, vision_encoder, language_model):
super().__init__()
self.vision_encoder = vision_encoder
self.language_model = language_model
self.mm_projector = nn.Sequential(
nn.Linear(vision_encoder.embed_dim, language_model.config.hidden_size),
nn.GELU(),
nn.Linear(language_model.config.hidden_size, language_model.config.hidden_size)
)
def encode_multimodal(self, inputs):
"""
统一编码多模态输入
"""
if inputs.get("image") is not None:
return self.encode_image(inputs["image"])
if inputs.get("images") is not None:
return self.encode_multi_images(inputs["images"])
if inputs.get("video") is not None:
return self.encode_video(inputs["video"])
def encode_image(self, image):
"""单图像编码"""
features = self.vision_encoder(image)
return self.mm_projector(features)
def encode_multi_images(self, images):
"""多图像编码:拼接所有图像的特征"""
all_features = []
for img in images:
feat = self.vision_encoder(img)
all_features.append(feat)
# 拼接多图像特征
concatenated = torch.cat(all_features, dim=1)
return self.mm_projector(concatenated)
def encode_video(self, video_frames, num_frames=16):
"""视频编码:采样帧 + 时序聚合"""
# 采样帧
indices = torch.linspace(0, len(video_frames)-1, num_frames).long()
sampled_frames = video_frames[indices]
# 逐帧编码
frame_features = [self.vision_encoder(f) for f in sampled_frames]
# 时序拼接(可替换为Transformer)
temporal_features = torch.stack(frame_features, dim=1) # (B, T, N, D)
temporal_features = temporal_features.flatten(1, 2) # (B, T*N, D)
return self.mm_projector(temporal_features)Gating-based架构(MiniGPT-4)
Gating-based架构通过引入门控机制控制视觉信息流向语言模型,实现更精细的模态融合。3
核心思想
MiniGPT-4的核心洞察是:仅需要训练一个线性投影层就能实现视觉到语言的初步对齐,但为了获得更好的效果,需要更复杂的对齐策略。
Q-Former设计原理
Q-Former(Querying Transformer)是BLIP-2提出的关键组件,用于从视觉编码器中提取与语言相关的视觉表示。
视觉编码器输出 Q-Former LLM
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ CLIP ViT │ │ Query │ │ │
│ 特征输出 │ ──────────▶ │ Transformer │ ────────▶ │ LLaMA │
│ (257x768) │ │ (32 queries)│ │ │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ 门控机制 │
│ (可选) │
└─────────────┘
Q-Former结构
class QFormer(nn.Module):
"""
Q-Former: Querying Transformer for Vision-Language alignment
BLIP-2的核心组件
"""
def __init__(self, vision_dim=1024, num_queries=32, num_heads=8, num_layers=6):
super().__init__()
self.num_queries = num_queries
# 可学习的Query向量
self.query_tokens = nn.Parameter(torch.zeros(1, num_queries, vision_dim))
nn.init.normal_(self.query_tokens, std=0.02)
# 交叉注意力层
self.cross_attentions = nn.ModuleList([
nn.MultiheadAttention(vision_dim, num_heads, batch_first=True)
for _ in range(num_layers)
])
# 前馈网络
self.ffns = nn.ModuleList([
nn.Sequential(
nn.Linear(vision_dim, vision_dim * 4),
nn.GELU(),
nn.Linear(vision_dim * 4, vision_dim)
)
for _ in range(num_layers)
])
# Layer Norm
self.norms = nn.ModuleList([
nn.LayerNorm(vision_dim)
for _ in range(num_layers * 2)
])
def forward(self, vision_features, text_features=None):
"""
Args:
vision_features: (B, N, D_v) 来自视觉编码器
text_features: (B, L, D_v) 来自文本编码器(可选)
Returns:
query_output: (B, num_queries, D_v) 提取的查询特征
"""
B = vision_features.size(0)
# 初始化Query
queries = self.query_tokens.expand(B, -1, -1) # (B, num_queries, D)
for i in range(len(self.cross_attentions)):
# Cross-attention: Query attend to Vision
attn_out, _ = self.cross_attentions[i](
queries, vision_features, vision_features
)
queries = self.norms[i*2](queries + attn_out)
# FFN
ffn_out = self.ffns[i](queries)
queries = self.norms[i*2 + 1](queries + ffn_out)
return queriesMiniGPT-4架构
MiniGPT-4采用简化的两阶段对齐策略,仅训练极少的参数(投影层)。
class MiniGPT4(nn.Module):
"""
MiniGPT-4: 简化版VLM,仅训练投影层
"""
def __init__(self, config):
super().__init__()
# 冻结的视觉编码器
self.visual_encoder = CLIPVisionEncoder()
for param in self.visual_encoder.parameters():
param.requires_grad = False
# Q-Former: 学习视觉-语言对齐
self.q_former = QFormer(
vision_dim=self.visual_encoder.embed_dim,
num_queries=32
)
# 线性投影:Q-Former输出 → LLM空间
vision_dim = self.visual_encoder.embed_dim
llm_dim = config.llm_hidden_size # 4096
self.projection = nn.Sequential(
nn.Linear(vision_dim, 2048),
nn.GELU(),
nn.Linear(2048, llm_dim)
)
# 冻结的LLM
self.llm = VicunaLM(config)
for param in self.llm.parameters():
param.requires_grad = False
def forward(self, image, input_ids, attention_mask=None, labels=None):
# 1. 视觉编码
vision_features = self.visual_encoder(image)
# 2. Q-Former处理
query_features = self.q_former(vision_features)
# 3. 投影到LLM空间
visual_tokens = self.projection(query_features)
# 4. 与文本tokens合并
text_tokens = self.llm.get_input_embeddings()(input_ids)
inputs_embeds = self._merge_inputs(visual_tokens, text_tokens, input_ids)
# 5. LLM生成
outputs = self.llm(
inputs_embeds=inputs_embeds,
attention_mask=attention_mask,
labels=labels
)
return outputs与LLaVA的对比
| 特性 | LLaVA | MiniGPT-4 |
|---|---|---|
| 视觉编码器 | CLIP ViT (冻结) | CLIP ViT (冻结) |
| 对齐模块 | 线性/MLP投影 | Q-Former + 线性投影 |
| 可训练参数 | ~20M | ~10M |
| 训练策略 | 两阶段 | 两阶段 |
| 推理效率 | 高 | 中等(Q-Former额外计算) |
| 生成质量 | 较好 | 较好(对齐更精细) |
MiniGPT-4 vs MiniGPT-5
MiniGPT-5是对MiniGPT-4的改进,引入了Gating机制和生成式视觉编码器(LVG):
class MiniGPT5(nn.Module):
"""
MiniGPT-5: 引入Gating机制和生成式视觉编码
"""
def __init__(self, config):
super().__init__()
self.visual_encoder = CLIPVisionEncoder()
self.lvg_encoder = GenerativeVisionEncoder() # 新增:生成式视觉编码器
self.q_former = QFormer()
# Gating机制:控制信息流
self.gate = nn.Sequential(
nn.Linear(config.vision_dim * 2, config.vision_dim),
nn.Sigmoid()
)
self.projection = nn.Linear(config.vision_dim, config.llm_dim)
self.llm = LLaMA(config)
def forward(self, image, input_ids, ...):
# 视觉编码
clip_features = self.visual_encoder(image)
lvg_features = self.lvg_encoder(image)
# Gating: 自适应融合两种视觉特征
combined = torch.cat([clip_features, lvg_features], dim=-1)
gate_values = self.gate(combined)
gated_features = gate_values * clip_features + (1 - gate_values) * lvg_features
# Q-Former处理
query_features = self.q_former(gated_features)
# 投影并送入LLM
...Cross-attention架构(InstructBLIP)
Cross-attention架构通过跨模态注意力机制实现视觉和语言信息的深度交互,是目前最强大的VLM架构之一。4
指令感知的视觉特征提取
InstructBLIP的核心创新是指令感知的视觉特征提取:在提取视觉特征时,考虑当前指令的上下文信息。
指令文本 视觉编码器 Q-Former
│ │ │
▼ ▼ ▼
┌────────┐ ┌─────────────┐ ┌─────────────┐
│ BERT/ │ ──────────▶ │ CLIP ViT │ ────────▶ │ Cross- │
│ T5 │ │ (指令感知) │ │ Attention │
└────────┘ └─────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
文本 视觉特征 指令相关的
特征 (受指令引导) 视觉表示
跨模态注意力机制
InstructBLIP的Q-Former采用双向跨注意力:
class InstructBLIP_QFormer(nn.Module):
"""
InstructBLIP的Q-Former: 指令感知的视觉特征提取
"""
def __init__(self, config):
super().__init__()
self.num_query = 32
self.num_layer = 6
# 可学习的Query
self.query_tokens = nn.Parameter(torch.zeros(1, self.num_query, config.hidden_dim))
# 各层的Q-Former Block
self.layers = nn.ModuleList([
QFormerLayer(config) for _ in range(self.num_layer)
])
def forward(self, vision_features, text_features, attention_mask=None):
"""
Args:
vision_features: (B, N, D_v) 视觉编码器输出
text_features: (B, L, D_t) 文本特征(来自LLM或BERT)
Returns:
query_output: (B, num_query, D) 指令相关的视觉特征
"""
queries = self.query_tokens.expand(vision_features.size(0), -1, -1)
for layer in self.layers:
# 双向交叉注意力
queries = layer(
query=queries, # Query来自可学习向量
key_value_vision=vision_features, # Key/Value来自视觉
key_value_text=text_features, # 额外文本上下文
attention_mask=attention_mask
)
return queries
class QFormerLayer(nn.Module):
"""
Q-Former的单层结构
"""
def __init__(self, config):
super().__init__()
# Self-attention on queries
self.self_attn = nn.MultiheadAttention(config.hidden_dim, config.num_heads, batch_first=True)
# Cross-attention to vision
self.vision_cross_attn = nn.MultiheadAttention(config.hidden_dim, config.num_heads, batch_first=True)
# Cross-attention to text
self.text_cross_attn = nn.MultiheadAttention(config.hidden_dim, config.num_heads, batch_first=True)
# FFN
self.ffn = nn.Sequential(
nn.Linear(config.hidden_dim, config.intermediate_size),
nn.GELU(),
nn.Linear(config.intermediate_size, config.hidden_dim)
)
self.norms = nn.LayerNorm(config.hidden_dim)
def forward(self, query, key_value_vision, key_value_text, attention_mask):
# 1. Self-attention
residual = query
query = self.norms(query)
query, _ = self.self_attn(query, query, query)
query = residual + query
# 2. Vision Cross-attention
residual = query
query = self.norms(query)
query, _ = self.vision_cross_attn(query, key_value_vision, key_value_vision)
query = residual + query
# 3. Text Cross-attention
residual = query
query = self.norms(query)
query, _ = self.text_cross_attn(query, key_value_text, key_value_text, key_padding_mask=attention_mask)
query = residual + query
# 4. FFN
residual = query
query = self.norms(query)
query = residual + self.ffn(query)
return queryBLIP-2基础架构
InstructBLIP建立在BLIP-2的基础上,BLIP-2提出两阶段视觉-语言对齐:
阶段1:表示学习阶段
┌─────────────┐ ┌─────────────┐
│ 冻结的视觉 │ │ 冻结的图像 │
│ 编码器 │ ◀───── Q-Former ──│ 编码器 │
│ (CLIP ViT) │ │ (BERT) │
└─────────────┘ └─────────────┘
目标:学习与文本对齐的视觉表示
阶段2:生成学习阶段
┌─────────────┐ ┌─────────────┐
│ Q-Former │ │ 冻结的语言 │
│ (冻结) │ ◀───── Cross-attn ─│ 模型 │
│ │ │ (T5/Flan-T5)│
└─────────────┘ └─────────────┘
目标:训练Q-Former进行视觉到语言的转换
class BLIP2(nn.Module):
"""
BLIP-2: Bootstrapping Language-Image Pre-training
"""
def __init__(self, config):
super().__init__()
# 冻结的视觉编码器
self.visual_encoder = CLIPVisionEncoder()
for param in self.visual_encoder.parameters():
param.requires_grad = False
# Q-Former
self.q_former = QFormer(config)
# 线性投影:Q-Former → LLM
self.llm_proj = nn.Linear(config.q_former_dim, config.llm_dim)
# 冻结的LLM (T5或LLaMA)
if config.llm_type == "t5":
self.llm = T5Model.from_pretrained(config.llm_name)
else:
self.llm = LLaMAModel.from_pretrained(config.llm_name)
for param in self.llm.parameters():
param.requires_grad = False
def forward(self, image, input_ids, ...):
# 视觉编码
vision_embeds = self.visual_encoder(image)
# Q-Former处理
query_output = self.q_former(
vision_embeds,
text_ids=input_ids # 额外文本上下文
)
# 投影
query_output = self.llm_proj(query_output)
# 送入LLM
...Q-Former变体设计
不同的VLM架构采用不同的Q-Former变体:
| 变体 | 特点 | 代表模型 |
|---|---|---|
| 标准Q-Former | 固定数量Query | BLIP-2 |
| 指令感知Q-Former | 考虑文本指令上下文 | InstructBLIP |
| 动态Q-Former | Query数量可变 | 一些改进版本 |
| 分层Q-Former | 多尺度特征融合 | InternVL |
class DynamicQFormer(nn.Module):
"""
动态Q-Former: Query数量根据输入自适应
"""
def __init__(self, config):
super().__init__()
self.base_num_queries = config.num_queries
self.q_former = QFormer(config)
# 动态Query生成
self.query_predictor = nn.Sequential(
nn.Linear(config.vision_dim, 128),
nn.ReLU(),
nn.Linear(128, 1),
nn.Sigmoid()
)
def forward(self, vision_features, text_features=None):
# 预测每个patch的重要性
importance = self.query_predictor(vision_features) # (B, N, 1)
# 根据重要性选择top-k patches
k = min(self.base_num_queries, vision_features.size(1))
_, top_indices = torch.topk(importance.squeeze(-1), k, dim=1)
# 收集对应特征
B = vision_features.size(0)
selected_features = torch.zeros(B, k, vision_features.size(-1), device=vision_features.device)
for b in range(B):
selected_features[b] = vision_features[b, top_indices[b]]
# 使用选中的特征
return self.q_former(selected_features, text_features)混合架构(视觉专家混合)
混合架构通过组合多个视觉编码器或使用动态选择机制,处理不同类型的视觉输入。5
设计动机
单一视觉编码器难以同时处理:
- 高分辨率图像:需要更多patch或更大感受野
- 文本密集图像:需要OCR能力
- 场景理解图像:需要深度信息和语义信息
- 细粒度图像:需要局部特征
LEO:Mixture of Vision Encoders
LEO提出使用视觉专家混合机制,自适应地选择最合适的视觉编码器。
class LEO(nn.Module):
"""
LEO: Mixture of Vision Encoders
动态选择最合适的视觉编码器
"""
def __init__(self, config):
super().__init__()
# 多个视觉专家
self.vision_experts = nn.ModuleDict({
"high_res": HighResVisionEncoder(), # 高分辨率专家
"ocr": OCRVisionEncoder(), # OCR专家
"depth": DepthVisionEncoder(), # 深度估计专家
"clip": CLIPVisionEncoder(), # CLIP通用专家
})
# 路由网络:决定使用哪个专家
self.router = nn.Sequential(
nn.Linear(config.vision_dim * 4, config.router_hidden),
nn.ReLU(),
nn.Linear(config.router_hidden, len(self.vision_experts)),
nn.Softmax(dim=-1)
)
# 融合层
self.fusion = nn.ModuleList([
nn.Linear(config.vision_dim, config.output_dim)
for _ in self.vision_experts
])
self.llm = LLaMA(config)
def forward(self, image, input_ids, image_type=None):
# 1. 获取所有专家的特征
expert_features = {}
for name, encoder in self.vision_experts.items():
expert_features[name] = encoder(image)
# 2. 路由决策
if image_type is not None:
# 显式指定专家
weights = torch.zeros(len(self.vision_experts))
weights[list(self.vision_experts.keys()).index(image_type)] = 1.0
weights = weights.to(image.device)
else:
# 动态路由
combined_features = torch.cat([
feat.mean(dim=1) for feat in expert_features.values()
], dim=-1)
weights = self.router(combined_features)
# 3. 加权融合
fused_features = torch.zeros_like(list(expert_features.values())[0])
for i, (name, feat) in enumerate(expert_features.items()):
fused_features += weights[i] * self.fusion[i](feat)
# 4. 送入LLM
visual_tokens = self.project_to_llm(fused_features)
...Florence-VL:多编码器融合
Florence-VL采用多编码器融合策略,支持视觉和语言的多层次对齐。
class FlorenceVL(nn.Module):
"""
Florence-VL: 多编码器统一视觉语言表示
"""
def __init__(self, config):
super().__init__()
# 主干视觉编码器
self.vision_backbone = DaViT(config) # Data-efficient Vision Transformer
# 细粒度编码器
self.fine_encoder = FineGrainedEncoder()
# 语义编码器
self.semantic_encoder = SemanticEncoder()
# 多粒度融合
self.multi_granularity_fusion = MultiGranularityFusion(config)
# 统一投影
self.unified_proj = nn.Sequential(
nn.Linear(config.hidden_dim * 3, config.hidden_dim),
nn.LayerNorm(config.hidden_dim),
nn.GELU(),
nn.Linear(config.hidden_dim, config.llm_dim)
)
self.llm = LLaMA(config)
def forward(self, image, input_ids, return_features=False):
# 多尺度视觉编码
coarse_feat = self.vision_backbone(image) # 粗粒度
fine_feat = self.fine_encoder(image) # 细粒度
semantic_feat = self.semantic_encoder(image) # 语义级
# 跨粒度交互
fused = self.multi_granularity_fusion(
coarse_feat, fine_feat, semantic_feat
)
# 统一投影
visual_tokens = self.unified_proj(fused)
if return_features:
return visual_tokens, {
"coarse": coarse_feat,
"fine": fine_feat,
"semantic": semantic_feat
}
return self._merge_and_generate(visual_tokens, input_ids)动态视觉专家选择
动态选择机制可以根据输入内容自适应地选择处理路径:
class DynamicVisionSelector(nn.Module):
"""
动态视觉专家选择器
"""
def __init__(self, num_experts=4):
super().__init__()
self.experts = nn.ModuleList([
VisionExpert() for _ in range(num_experts)
])
# 门控网络
self.gate = nn.Sequential(
nn.AdaptiveAvgPool2d(1),
nn.Flatten(),
nn.Linear(512, 256),
nn.ReLU(),
nn.Linear(256, num_experts),
nn.Softmax(dim=1)
)
# 特征投影
self.proj = nn.Linear(512, 4096)
def forward(self, x):
# 1. 轻量级特征提取用于路由
routing_features = self._extract_routing_features(x)
# 2. 计算门控权重
gate_weights = self.gate(routing_features) # (B, num_experts)
# 3. 专家处理
expert_outputs = []
for expert in self.experts:
expert_outputs.append(expert(x))
# 4. 加权组合
outputs = torch.stack(expert_outputs, dim=0) # (num_experts, B, N, D)
gate_weights = gate_weights.unsqueeze(-1).unsqueeze(-1) # (B, num_experts, 1, 1)
fused_output = (outputs * gate_weights).sum(dim=0) # (B, N, D)
return self.proj(fused_output)端到端架构(商业模型)
端到端架构将视觉和语言建模统一在单一框架中,不使用独立的视觉编码器或采用原生多模态设计。
GPT-4V架构特点(推测)
OpenAI未公开GPT-4V的详细架构,但根据社区分析和论文推测:
| 推测特性 | 说明 |
|---|---|
| 视觉编码 | 可能使用改进的ViT或专用视觉Tokenizer |
| 模态融合 | 原生Transformer统一处理多模态 |
| 分辨率处理 | 智能预处理,支持可变分辨率 |
| 上下文理解 | 长序列处理能力 |
| 多语言 | 支持多语言视觉理解 |
class GPT4V_Architecture:
"""
GPT-4V架构(推测)
"""
def __init__(self):
# 推测:增强型视觉编码器
self.vision_encoder = "Enhanced ViT with dynamic resolution"
# 推测:统一的模态融合
self.fusion = "Unified multimodal transformer"
# 推测:GPT-4核心
self.language_model = "GPT-4 core capabilities"
# 推测特性
self.features = {
"resolution": "Variable, intelligent preprocessing",
"aspect_ratio": "Native support for flexible ratios",
"context_window": "Extended multimodal context",
"languages": "Multilingual visual understanding",
"image_tokenization": "Learned visual tokenizer"
}
def process_image(self, image):
"""
推测的图像处理流程
"""
# 1. 智能区域检测
regions = self._detect_regions(image)
# 2. 多尺度编码
features = []
for region in regions:
feat = self._encode_region(region)
features.append(feat)
# 3. 自适应聚合
return self._aggregate_features(features)
def preprocess_image(self, image):
"""
GPT-4V可能使用的高级预处理
"""
# 感兴趣区域检测
rois = self._find_rois(image)
# 保持原始宽高比
processed = []
for roi in rois:
# 智能缩放到合适大小
scaled = self._smart_resize(roi)
processed.append(scaled)
return processedGemini原生多模态设计
Google Gemini是原生多模态架构的代表,从一开始就将多模态建模作为核心设计目标。1
class GeminiArchitecture:
"""
Gemini: 原生多模态Transformer (M4T)
"""
def __init__(self):
# 统一的多模态Tokenizer
self.mm_tokenizer = MultimodalTokenizer()
# 原生多模态Transformer
self.model = MultimodalTransformer(
num_layers=32,
hidden_dim=4096,
num_heads=32,
max_length=32768
)
# 支持的模态
self.modalities = ["text", "image", "video", "audio"]
# 核心创新
self.innovations = {
"unified_tokenizer": "Text, vision, audio unified tokenization",
"native_cross_modal": "Cross-modal attention from layer 1",
"joint_pretraining": "End-to-end multimodal pretraining"
}
def encode_multimodal(self, inputs):
"""
统一处理多种模态输入
"""
encoded = {}
for modality in self.modalities:
if modality in inputs:
if modality == "image":
encoded[modality] = self.mm_tokenizer.tokenize_image(inputs[modality])
elif modality == "text":
encoded[modality] = self.mm_tokenizer.tokenize_text(inputs[modality])
elif modality == "audio":
encoded[modality] = self.mm_tokenizer.tokenize_audio(inputs[modality])
elif modality == "video":
encoded[modality] = self.mm_tokenizer.tokenize_video(inputs[modality])
# 拼接所有模态的tokens
return self.concat_multimodal_tokens(encoded)
def concat_multimodal_tokens(self, encoded):
"""
多模态token拼接
"""
all_tokens = []
modality_ids = []
for modality, tokens in encoded.items():
all_tokens.append(tokens)
modality_ids.extend([self.modality_to_id[modality]] * tokens.size(1))
concatenated = torch.cat(all_tokens, dim=1)
return concatenated
class MultimodalTransformer(nn.Module):
"""
Gemini的多模态Transformer核心
"""
def __init__(self, num_layers, hidden_dim, num_heads, max_length):
super().__init__()
self.layers = nn.ModuleList([
MultimodalTransformerLayer(
hidden_dim=hidden_dim,
num_heads=num_heads
)
for _ in range(num_layers)
])
# 模态嵌入
self.modality_embeddings = nn.Embedding(8, hidden_dim)
def forward(self, tokens, modality_ids=None):
"""
Args:
tokens: (B, L, D) 多模态tokens
modality_ids: (B, L) 各token的模态ID
"""
hidden_states = tokens
# 添加模态嵌入
if modality_ids is not None:
mod_emb = self.modality_embeddings(modality_ids)
hidden_states = hidden_states + mod_emb
# 通过Transformer层
for layer in self.layers:
hidden_states = layer(hidden_states)
return hidden_states
class MultimodalTransformerLayer(nn.Module):
"""
多模态Transformer层:支持所有模态间的注意力交互
"""
def __init__(self, hidden_dim, num_heads):
super().__init__()
# 通用自注意力
self.self_attn = nn.MultiheadAttention(hidden_dim, num_heads, batch_first=True)
# 模态间交叉注意力
self.cross_modal_attn = CrossModalAttention(hidden_dim, num_heads)
# 前馈网络
self.ffn = nn.Sequential(
nn.Linear(hidden_dim, hidden_dim * 4),
nn.GELU(),
nn.Linear(hidden_dim * 4, hidden_dim)
)
self.norms = nn.ModuleList([nn.LayerNorm(hidden_dim) for _ in range(3)])
def forward(self, x):
# Self-attention
residual = x
x = self.norms[0](x)
x, _ = self.self_attn(x, x, x)
x = residual + x
# Cross-modal attention
residual = x
x = self.norms[1](x)
x = self.cross_modal_attn(x, x, x) # 同构设计,支持任意模态
x = residual + x
# FFN
residual = x
x = self.norms[2](x)
x = residual + self.ffn(x)
return xClaude多模态能力
Anthropic的Claude 3系列通过集成视觉编码器实现了强大的多模态能力:
class ClaudeVision:
"""
Claude的多模态处理
"""
def __init__(self):
# 视觉编码器
self.vision_encoder = "Proprietary Vision Encoder"
# 文本模型
self.language_model = "Claude 3 Model"
# 处理能力
self.capabilities = {
"high_resolution": "支持高分辨率图像",
"document_understanding": "文档、表格、图表理解",
"flowchart": "流程图和架构图解析",
"handwriting": "手写内容识别",
"spatial": "空间关系和布局理解"
}
def process_vision(self, image):
"""
处理视觉输入
"""
# 图像预处理
processed = self.preprocess_image(image)
# 视觉编码
visual_features = self.vision_encoder(processed)
# 投影到语言空间
return self.project_to_llm(visual_features)架构对比与选型指南
综合对比
| 架构类型 | 代表模型 | 参数量 | 训练成本 | 推理速度 | 适用场景 |
|---|---|---|---|---|---|
| Prefix-based | LLaVA | 7B-13B | 低 | 快 | 通用视觉对话 |
| Gating-based | MiniGPT-4 | 7B | 低 | 中 | 轻量级部署 |
| Cross-attention | InstructBLIP | 7B-11B | 中 | 中 | 指令跟随任务 |
| 混合架构 | LEO, Florence-VL | 10B+ | 高 | 慢 | 高精度场景 |
| 端到端 | Gemini | 未公开 | 极高 | 中 | 全场景通用 |
架构演进趋势
┌────────────────────────────────────────────────────────────────┐
│ VLM架构演进 │
├────────────────────────────────────────────────────────────────┤
│ │
│ 2021-2022 2023 2024-2025│
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ CLIP │ │ LLaVA │ │ 原生 │ │
│ │ 双塔 │ ──────────▶ │ Prefix │ ──────────▶ │ 多模态 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 模态独立 轻量级融合 深度融合 │
│ 特征提取 桥梁连接 端到端学习 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 关键演进方向: │ │
│ │ • 冻结预训练模型 → 联合训练 │ │
│ │ • 单一视觉编码器 → 多专家混合 │ │
│ │ • 固定分辨率 → 动态分辨率 │ │
│ │ • 视觉-语言分离 → 原生多模态 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
选型建议
根据不同应用场景的架构选择:
| 场景 | 推荐架构 | 原因 |
|---|---|---|
| 学术研究 | LLaVA | 开源、代码完善、易于修改 |
| 轻量级部署 | MiniGPT-4 | 参数量小、训练简单 |
| 高质量对话 | InstructBLIP | 指令理解能力强 |
| 复杂视觉理解 | Florence-VL | 多粒度特征融合 |
| 企业级应用 | Gemini/Claude | 商业支持、能力全面 |
与现有内容的衔接
| 关联内容 | 说明 |
|---|---|
| LLaVA | Prefix-based架构的详细实现 |
| 多模态模型综述 | 商业模型与开源生态对比 |
| CLIP | 视觉编码器基础 |
| Transformer与注意力 | 多模态注意力的技术基础 |
| MoE | 混合架构中的专家选择机制 |
| PEFT | VLM的高效微调技术 |
| LoRA | VLM的参数高效微调实践 |
参考文献
Footnotes
-
Google DeepMind, Gemini: A Family of Highly Capable Multimodal Models, 2023 ↩ ↩2
-
Zhou et al., MiniGPT-4: Enhancing Vision Language Understanding with One Single Projection Layer, ICLR 2024 ↩
-
Li et al., InstructBLIP: Towards General-purpose Vision-Language Models with Instruction Tuning, NeurIPS 2023 ↩
-
Chen et al., Florence-VL: Enhanced Vision-Language Representation from Florence, CVPR 2025 ↩