多模态融合的核心问题

多模态模型要解决一个根本问题:如何让模型理解不同模态(文本、图像、音频、视频)之间的语义关联。这包含两个子问题:

  1. 表征对齐:将不同模态的输入映射到统一的语义空间
  2. 推理融合:在统一空间中进行跨模态的推理与生成

多模态融合的三个阶段

阶段 时间 代表模型 核心方法
对比学习 2021-2022 CLIP, ALIGN 双塔编码器 + 对比损失
桥接融合 2023 BLIP-2, LLaVA 视觉编码器 + Q-Former/投影层 + LLM
原生多模态 2024-2026 GPT-4V, Gemini 端到端训练的多模态Transformer

第一阶段:对比学习对齐(CLIP)

CLIP的核心思想

CLIP(Contrastive Language-Image Pre-training)通过简单的对比学习,将图像和文本映射到同一语义空间:

import torch
import torch.nn as nn
import torch.nn.functional as F

class CLIPModel(nn.Module):
    def __init__(self, image_encoder, text_encoder, projection_dim=512):
        super().__init__()
        self.image_encoder = image_encoder  # ViT
        self.text_encoder = text_encoder    # Transformer
        self.image_projection = nn.Linear(
            image_encoder.dim, projection_dim
        )
        self.text_projection = nn.Linear(
            text_encoder.dim, projection_dim
        )
        self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1/0.07))
    
    def forward(self, images, texts):
        # 编码图像和文本
        image_features = self.image_encoder(images)      # (batch, img_dim)
        text_features = self.text_encoder(texts)          # (batch, txt_dim)
        
        # 投影到共享空间
        image_embeds = self.image_projection(image_features)  # (batch, proj_dim)
        text_embeds = self.text_projection(text_features)      # (batch, proj_dim)
        
        # L2归一化
        image_embeds = F.normalize(image_embeds, dim=-1)
        text_embeds = F.normalize(text_embeds, dim=-1)
        
        # 对比损失:对角线为正样本,其余为负样本
        logit_scale = self.logit_scale.exp()
        logits_per_image = logit_scale * image_embeds @ text_embeds.t()
        logits_per_text = logits_per_image.t()
        
        labels = torch.arange(len(images), device=images.device)
        
        loss_i2t = F.cross_entropy(logits_per_image, labels)
        loss_t2i = F.cross_entropy(logits_per_text, labels)
        loss = (loss_i2t + loss_t2i) / 2
        
        return loss

CLIP的局限

  • 表征能力有限:对比学习只学了"相似/不相似",无法做细粒度理解
  • 无法生成:CLIP只能做检索和分类,无法生成图像描述
  • 固定分辨率:ViT需要固定输入分辨率,处理高分辨率图像时信息丢失

第二阶段:桥接融合(BLIP-2 / LLaVA)

BLIP-2:Q-Former桥接

BLIP-2引入了Q-Former(Querying Transformer),用一组可学习的Query从视觉特征中提取与语言相关的信息:

class QFormer(nn.Module):
    """BLIP-2的Q-Former:从冻结的视觉编码器中提取语言相关信息"""
    def __init__(self, num_queries=32, d_model=768, num_heads=12):
        super().__init__()
        # 可学习的Query Tokens
        self.query_tokens = nn.Parameter(
            torch.randn(num_queries, d_model)
        )
        # Cross-Attention:Query关注视觉特征
        self.cross_attn = nn.MultiheadAttention(
            d_model, num_heads, batch_first=True
        )
        # Self-Attention:Query之间交互
        self.self_attn = nn.MultiheadAttention(
            d_model, num_heads, batch_first=True
        )
        # FFN
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_model * 4),
            nn.GELU(),
            nn.Linear(d_model * 4, d_model)
        )
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.norm3 = nn.LayerNorm(d_model)
    
    def forward(self, image_features, text_input=None):
        """
        image_features: (batch, num_patches, img_dim) 来自冻结的ViT
        text_input: (batch, seq_len, d_model) 可选的文本输入
        """
        batch_size = image_features.shape[0]
        queries = self.query_tokens.unsqueeze(0).expand(batch_size, -1, -1)
        
        # Self-Attention
        attn_out = self.self_attn(queries, queries, queries)[0]
        queries = self.norm1(queries + attn_out)
        
        # Cross-Attention(关注图像特征)
        cross_out = self.cross_attn(queries, image_features, image_features)[0]
        queries = self.norm2(queries + cross_out)
        
        # FFN
        queries = self.norm3(queries + self.ffn(queries))
        
        # 输出: (batch, num_queries, d_model) — 固定长度的视觉表示
        return queries

LLaVA:简单投影的力量

LLaVA证明了一个简单的线性投影层就能实现出色的多模态能力:

class LLaVA(nn.Module):
    """LLaVA: 最简洁的多模态融合方案"""
    def __init__(self, vision_encoder, llm, d_vision=1024, d_llm=4096):
        super().__init__()
        self.vision_encoder = vision_encoder  # 冻结的CLIP ViT
        self.llm = llm                        # 冻结的LLM
        
        # 核心就是一个线性投影层
        self.projection = nn.Linear(d_vision, d_llm)
    
    def forward(self, images, text_tokens, attention_mask):
        # 1. 提取视觉特征
        with torch.no_grad():
            image_features = self.vision_encoder(images)  # (batch, num_patches, d_vision)
        
        # 2. 投影到LLM的嵌入空间
        visual_embeds = self.projection(image_features)   # (batch, num_patches, d_llm)
        
        # 3. 将视觉嵌入与文本嵌入拼接
        text_embeds = self.llm.get_input_embeddings()(text_tokens)
        
        # [视觉Token] [文本Token] → 拼接输入
        combined_embeds = torch.cat([visual_embeds, text_embeds], dim=1)
        
        # 4. 送入LLM生成
        outputs = self.llm(inputs_embeds=combined_embeds, 
                          attention_mask=attention_mask)
        
        return outputs

LLaVA的成功说明:冻结的视觉编码器 + 简单投影 + 冻结的LLM,仅通过训练投影层就能实现强大的多模态能力。这极大降低了多模态训练的门槛。

第三阶段:原生多模态(GPT-4V / Gemini)

原生多模态的设计原则

GPT-4V和Gemini代表了多模态融合的最新范式——从训练第一天起就是多模态的

原生多模态训练流程:
  阶段1 - 预训练:
    数据: 图文对 + 纯文本 + 图像描述
    方法: 所有模态共享Transformer backbone
    特点: 视觉Token与文本Token在同一个序列中
    
  阶段2 - 指令微调:
    数据: 多模态指令对话数据
    方法: SFT + RLHF
    特点: 学习按指令理解图像、回答问题
    
  阶段3 - 能力增强:
    数据: OCR、图表理解、空间推理
    方法: 针对性数据增强
    特点: 细粒度视觉理解能力

视觉Token化方案

原生多模态模型需要将图像转化为Token序列。三种主流方案:

方案1:Patch Embedding(ViT风格)

class PatchEmbedding(nn.Module):
    """将图像分割为Patch并转化为Token"""
    def __init__(self, img_size=224, patch_size=16, d_model=4096):
        super().__init__()
        self.num_patches = (img_size // patch_size) ** 2
        self.proj = nn.Conv2d(
            3, d_model, 
            kernel_size=patch_size, stride=patch_size
        )
        # 位置编码
        self.pos_embed = nn.Parameter(
            torch.randn(1, self.num_patches, d_model)
        )
    
    def forward(self, images):
        # images: (batch, 3, 224, 224)
        patches = self.proj(images)  # (batch, d_model, 14, 14)
        patches = patches.flatten(2).transpose(1, 2)  # (batch, 196, d_model)
        return patches + self.pos_embed

方案2:多分辨率Token

class MultiResolutionTokenizer:
    """多分辨率视觉Token化"""
    def __init__(self, d_model):
        self.d_model = d_model
        # 不同分辨率的编码器
        self.encoders = nn.ModuleDict({
            'low': PatchEmbedding(224, 16, d_model),    # 196 tokens
            'mid': PatchEmbedding(448, 16, d_model),    # 784 tokens  
            'high': PatchEmbedding(896, 16, d_model),   # 3136 tokens
        })
    
    def forward(self, image, detail_level='auto'):
        if detail_level == 'auto':
            # 根据图像复杂度自动选择分辨率
            complexity = self._estimate_complexity(image)
            if complexity < 0.3:
                return self.encoders['low'](image)
            elif complexity < 0.7:
                return self.encoders['mid'](F.interpolate(image, size=448))
            else:
                return self.encoders['high'](F.interpolate(image, size=896))
        else:
            return self.encoders[detail_level](image)
    
    def _estimate_complexity(self, image):
        """简单的图像复杂度估计"""
        gray = image.mean(dim=1)
        edges = torch.abs(gray[:, :, 1:] - gray[:, :, :-1]).mean()
        return float(edges.clamp(0, 1))

方案3:动态Token数量

根据图像内容动态决定Token数量——简单图像用少量Token,复杂图像用更多Token。这在精度与效率之间取得平衡。

跨模态注意力机制

原生多模态模型中,视觉Token和文本Token在同一个注意力层中交互:

class MultiModalAttention(nn.Module):
    """支持模态感知的多模态注意力"""
    def __init__(self, d_model, num_heads):
        super().__init__()
        self.num_heads = num_heads
        self.d_head = d_model // num_heads
        
        # 共享的QKV投影
        self.q_proj = nn.Linear(d_model, d_model)
        self.k_proj = nn.Linear(d_model, d_model)
        self.v_proj = nn.Linear(d_model, d_model)
        self.o_proj = nn.Linear(d_model, d_model)
        
        # 模态偏置项(让模型区分视觉和文本Token)
        self.modal_bias = nn.ParameterDict({
            'VV': nn.Parameter(torch.zeros(num_heads)),  # 视觉→视觉
            'VT': nn.Parameter(torch.zeros(num_heads)),  # 视觉→文本
            'TV': nn.Parameter(torch.zeros(num_heads)),  # 文本→视觉
            'TT': nn.Parameter(torch.zeros(num_heads)),  # 文本→文本
        })
    
    def forward(self, x, modal_types):
        """
        x: (batch, seq_len, d_model)
        modal_types: (batch, seq_len) — 0=文本, 1=视觉
        """
        batch, seq_len, _ = x.shape
        
        Q = self.q_proj(x).view(batch, seq_len, self.num_heads, self.d_head)
        K = self.k_proj(x).view(batch, seq_len, self.num_heads, self.d_head)
        V = self.v_proj(x).view(batch, seq_len, self.num_heads, self.d_head)
        
        # 计算注意力分数
        scores = torch.einsum('bqhd,bkhd->bhqk', Q, K) / (self.d_head ** 0.5)
        
        # 添加模态偏置
        # 构造 (q_modal, k_modal) → bias 的映射
        q_modal = modal_types.unsqueeze(1)  # (batch, 1, seq)
        k_modal = modal_types.unsqueeze(2)  # (batch, seq, 1)
        
        for q_type in [0, 1]:
            for k_type in [0, 1]:
                key = ('T' if q_type == 0 else 'V') + ('T' if k_type == 0 else 'V')
                mask = (q_modal == q_type) & (k_modal == k_type)
                bias = self.modal_bias[key].view(1, -1, 1, 1)
                scores = scores + mask.unsqueeze(1) * bias
        
        # Softmax和加权求和
        attn = F.softmax(scores, dim=-1)
        out = torch.einsum('bhqk,bkhd->bqhd', attn, V)
        out = out.reshape(batch, seq_len, -1)
        
        return self.o_proj(out)

评估基准与性能

多模态理解能力对比(2026年)

模型 MMMU MMBench DocVQA ChartQA 视觉推理
GPT-4V 75.4 83.2 92.1 78.5 68.3
Gemini 2.0 73.8 81.5 90.3 80.1 65.7
Qwen-VL-Max 68.2 76.8 88.5 72.3 58.4
InternVL-2 65.5 74.3 85.2 70.8 55.1
LLaVA-1.6 52.1 62.5 70.4 55.2 42.3

从CLIP到GPT-4V的演进,本质上是从"表征对齐"到"推理融合"的跃迁。CLIP解决了"猫的图片"和"一只猫"这个文本在语义空间中对齐的问题;而GPT-4V则能理解"这张图中的猫为什么在打哈欠"这样的复杂跨模态推理。

未来方向

  1. 任意模态融合:不仅是图文,还包括音频、视频、3D、触觉的统一建模
  2. 实时多模态推理:视频流实时理解,延迟<100ms
  3. 多模态生成:从理解到生成,统一的"输入任意模态→输出任意模态"框架
  4. 高效视觉Token:将196+个视觉Token压缩到16个以内,同时保持理解能力
  5. 空间理解增强:精确的空间位置感知和几何推理能力

多模态融合正在从"图文检索"走向"通用多模态推理"。这条路上的每一步创新,都在推动AI从"语言智能"迈向"感知智能",最终走向"具身智能"。