第二章:核心技术基础

第二章:核心技术基础

在第一章中,我们从宏观层面了解了多模态学习的概念、发展历程和应用场景。从本章开始,我们将深入技术核心,详细讲解多模态大模型背后的核心技术原理。这些知识是理解后续章节(如CLIP、GPT-4V等模型架构)的基础。

虽然这一章会涉及一些技术细节,但我们会继续坚持费曼教学法,用丰富的类比和直观的解释帮助你理解这些概念。我们的目标是:即使你不具备深厚的数学背景,也能理解这些技术的工作原理和设计思想。

2.1 Transformer架构回顾

为什么我们需要Transformer?

在深入Transformer之前,让我们先理解为什么这个架构如此重要,它解决了什么问题。

回到2017年。那时候,处理序列数据(如文字、语音)的主流方法是循环神经网络(RNN,Recurrent Neural Network)及其各种变体,如LSTM(长短期记忆网络)、GRU(门控循环单元)。这些方法有一个共同的特点:它们按照顺序一个元素一个元素地处理数据。

用一个读书的例子来类比。想象你是一个RNN,你在读一本小说:

注释:RNN的工作方式就像我们人类读书——从第一页开始,逐字逐句地阅读。当你读到第100页时,你需要把第1页到第99页的内容都"记在脑子里",才能理解第100页在讲什么。

但这里有个问题:你的脑子(记忆容量)是有限的。当你读到第500页时,你可能已经忘记了第1页的很多细节。RNN也有这个问题——早期输入的信息在经过多次传递后,会逐渐"稀释"甚至丢失。

注释:这就是为什么早期的RNN模型很难处理很长的句子或文章。当句子长度超过几十个词时,模型往往难以建立词与词之间的长距离关联关系。比如,如果有一个句子是"我在法国长大,...(中间隔了20个词)...我会说法语",RNN可能无法记住"法国"和"法语"之间的关联。

另一个问题是计算效率。RNN必须按顺序处理,无法并行计算。假设一个句子有100个词,RNN需要一步步地处理完第1个词才能处理第2个,处理完第2个才能处理第3个...这就像一条流水线,每个工位必须等前一个工位完成才能开始工作。

注释:而现代GPU擅长的是并行计算——同时处理大量数据。RNN的串行特性无法充分利用GPU的计算能力,限制了模型的规模和训练效率。

Transformer的革命性突破

2017年,Google Brain团队发表了论文《Attention Is All You Need》,提出了Transformer架构,彻底改变了序列建模的方式。

这里有个有趣的语言学问题:为什么叫"Transformer"?它和《变形金刚》电影有关系吗?

注释:答案是没有关系。Transformer这个词来自单词"transform",意思是"转换"、"变换"。这个架构之所以叫Transformer,是因为它的核心功能是转换(transform)序列数据——把输入的序列转换成更有用的表示。中文有时会翻译为"变换器架构"或直接音译为"Transformer架构"。

Transformer的核心创新是:它完全抛弃了循环结构,只使用注意力机制来建模序列数据。

注释:这就像从"流水线式阅读"变成了"一次性全体扫描"。在Transformer中,模型在处理任何一个词时,都能"一眼看到"整个句子的所有其他词,并根据它们的相关性决定应该关注哪些词。

这解决了RNN的两个核心问题:

  1. 长程依赖问题:模型可以直接访问序列中的任何位置,不需要像RNN那样逐层传递信息
  2. 并行计算问题:所有词可以同时处理,不需要等待前一个词的结果

Transformer的核心组件

Transformer架构由两个主要部分组成:编码器(Encoder)和解码器(Decoder)。编码器负责理解输入序列,解码器负责生成输出序列。根据任务的不同,可以使用编码器-解码器结构(如机器翻译),或者只用编码器(如文本分类)、只用解码器(如语言建模)。

编码器-解码器架构图解

┌─────────────────────────────────────────────────────────────────────────────┐
│                         Transformer编码器-解码器架构                           │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│                           输入序列                                            │
│                              │                                               │
│                              ▼                                               │
│              ┌───────────────────────────────┐                               │
│              │      输入嵌入 + 位置编码       │                               │
│              └───────────────┬───────────────┘                               │
│                              │                                               │
│                              ▼                                               │
│    ┌───────────────────────────────────────────────────────────────────┐   │
│    │                     编码器堆栈(Encoder Stack)                    │   │
│    │                                                                    │   │
│    │   ┌─────────────────────────────────────────────────────────────┐│   │
│    │   │  编码器层 1                                                  ││   │
│    │   │  ├── 多头自注意力层(Multi-Head Self-Attention)            ││   │
│    │   │  ├── 残差连接 + 层归一化                                    ││   │
│    │   │  ├── 前馈神经网络(FFN)                                    ││   │
│    │   │  └── 残差连接 + 层归一化                                    ││   │
│    │   └─────────────────────────────────────────────────────────────┘│   │
│    │                              │                                       │
│    │                              ▼                                       │
│    │   ┌─────────────────────────────────────────────────────────────┐│   │
│    │   │  编码器层 2(结构相同)                                     ││   │
│    │   └─────────────────────────────────────────────────────────────┘│   │
│    │                              │                                       │
│    │                              ...                                    │
│    │                              ▼                                       │
│    │   ┌─────────────────────────────────────────────────────────────┐│   │
│    │   │  编码器层 N(通常6层)                                      ││   │
│    │   └─────────────────────────────────────────────────────────────┘│   │
│    │                                                                    │   │
│    └────────────────────────────────────────────────────────────────────┘   │
│                              │                                               │
│                              ▼                                               │
│              ┌───────────────────────────────┐                               │
│              │      编码器输出(内存向量)    │                               │
│              └───────────────┬───────────────┘                               │
│                              │                                               │
│        ┌─────────────────────┼─────────────────────┐                         │
│        ▼                     ▼                     ▼                         │
│  ┌───────────┐        ┌───────────┐        ┌───────────┐                    │
│  │ 解码器层1 │        │ 解码器层2 │        │ 解码器层N │                    │
│  │ (含交叉  │ ────▶  │ (含交叉  │ ────▶  │ (含交叉  │                    │
│  │  注意力) │        │  注意力) │        │  注意力) │                    │
│  └─────┬─────┘        └─────┬─────┘        └─────┬─────┘                    │
│        │                    │                    │                          │
│        └────────────────────┴────────────────────┘                          │
│                              │                                               │
│                              ▼                                               │
│              ┌───────────────────────────────┐                               │
│              │       输出概率分布            │                               │
│              └───────────────┬───────────────┘                               │
│                              │                                               │
│                              ▼                                               │
│                           输出序列                                           │
│                                                                              │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ 关键组件说明:                                                          │ │
│  │ • 多头自注意力:序列内部的信息交互                                      │ │
│  │ • 交叉注意力:解码器"查看"编码器输出                                    │ │
│  │ • 前馈网络:非线性变换,增加模型容量                                    │ │
│  │ • 残差连接:防止梯度消失,加速训练                                      │ │
│  │ • 层归一化:稳定训练过程                                                │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

注释:编码器和解码器的内部结构类似,都由多层"注意力层"和"前馈神经网络"组成。不同之处在于解码器多了一层"交叉注意力"(Cross-Attention),用来在生成时"查看"编码器的输出。

Transformer层内部结构详图

┌─────────────────────────────────────────────────────────────────────────────┐
│                       单层Transformer内部结构                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  输入向量 X                                                                   │
│      │                                                                        │
│      ├──────────────────────────────────────────────────────────────┐       │
│      │                                                               │       │
│      ▼                                                               ▼       │
│  ┌─────────────────────┐                                   ┌───────────────┐ │
│  │  多头自注意力层      │                                   │  残差连接     │ │
│  │                     │                                   │  LayerNorm   │ │
│  │  Q, K, V ← X        │                                   │  X + Attention│ │
│  │  Attention(Q,K,V)   │                                   └───────────────┘ │
│  │  MultiHead(...)     │                                              │       │
│  └──────────┬──────────┘                                              │       │
│             │                                                         │       │
│             └─────────────────────┐                                   │       │
│                                   │                                   │       │
│                                   ▼                                   │       │
│  ┌─────────────────────┐      ┌───────────────┐                      │       │
│  │  残差连接 + 层归一化 │◀─────│  Add & Norm   │◀─────────────────────┘       │
│  │  X + Attention(X)   │      │               │                              │
│  └──────────┬──────────┘      └───────────────┘                              │
│             │                                                                 │
│             ├──────────────────────────────────────────────────────────────┐ │
│             │                                                              │ │
│             ▼                                                              ▼ │
│  ┌─────────────────────┐                                   ┌───────────────┐ │
│  │  前馈神经网络        │                                   │  残差连接     │ │
│  │                     │                                   │  LayerNorm   │ │
│  │  FFN(X) = W2(ReLU(  │                                   │  X + FFN(X)   │ │
│  │          W1(X) + b1)│                                   └───────────────┘ │
│  │          + b2)      │                                              │       │
│  │                     │                                              │       │
│  └──────────┬──────────┘                                              │       │
│             │                                                         │       │
│             └─────────────────────────────────────────────────────────┘       │
│                                 │                                             │
│                                 ▼                                             │
│                          输出向量 Y                                           │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

PyTorch实现:Transformer编码器层

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


class MultiHeadAttention(nn.Module):
    """多头注意力机制实现"""

    def __init__(self, d_model, num_heads, dropout=0.1):
        """
        参数:
            d_model: 模型维度(词嵌入维度)
            num_heads: 注意力头数量
            dropout: Dropout比率
        """
        super().__init__()
        assert d_model % num_heads == 0, "d_model必须能被num_heads整除"

        self.d_model = d_model
        self.num_heads = num_heads
        self.d_k = d_model // num_heads  # 每个头的维度

        # 定义三个线性变换层,将输入投影到Q, K, V
        self.w_q = nn.Linear(d_model, d_model)
        self.w_k = nn.Linear(d_model, d_model)
        self.w_v = nn.Linear(d_model, d_model)
        self.w_o = nn.Linear(d_model, d_model)

        self.dropout = nn.Dropout(dropout)
        self.scale = math.sqrt(self.d_k)

    def forward(self, query, key, value, mask=None):
        """
        前向传播

        参数:
            query: 查询张量 [batch_size, seq_len, d_model]
            key: 键张量 [batch_size, seq_len, d_model]
            value: 值张量 [batch_size, seq_len, d_model]
            mask: 注意力掩码(可选)

        返回:
            输出张量 [batch_size, seq_len, d_model]
        """
        batch_size = query.size(0)

        # 1. 线性变换并分拆成多个头
        query = self.w_q(query).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        key = self.w_k(key).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        value = self.w_v(value).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)

        # 2. 计算注意力分数
        # [batch_size, num_heads, seq_len, seq_len]
        scores = torch.matmul(query, key.transpose(-2, -1)) / self.scale

        # 3. 应用掩码(如果有)
        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)

        # 4. Softmax得到注意力权重
        attention_weights = F.softmax(scores, dim=-1)
        attention_weights = self.dropout(attention_weights)

        # 5. 加权求和得到输出
        context = torch.matmul(attention_weights, value)

        # 6. 拼接多个头的输出
        context = context.transpose(1, 2).contiguous().view(
            batch_size, -1, self.d_model
        )

        # 7. 最后的线性变换
        output = self.w_o(context)

        return output


class PositionWiseFeedForward(nn.Module):
    """位置-wise前馈神经网络"""

    def __init__(self, d_model, d_ff, dropout=0.1):
        """
        参数:
            d_model: 模型维度
            d_ff: 前馈网络中间层维度(通常是d_model的4倍)
            dropout: Dropout比率
        """
        super().__init__()
        self.linear1 = nn.Linear(d_model, d_ff)
        self.linear2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        """
        前向传播: [batch_size, seq_len, d_model]
               -> [batch_size, seq_len, d_ff]
               -> [batch_size, seq_len, d_model]
        """
        return self.linear2(F.relu(self.linear1(x)))


class TransformerEncoderLayer(nn.Module):
    """Transformer编码器层"""

    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super().__init__()
        self.self_attn = MultiHeadAttention(d_model, num_heads, dropout)
        self.feed_forward = PositionWiseFeedForward(d_model, d_ff, dropout)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)

    def forward(self, x, mask=None):
        """
        前向传播
        """
        # 残差连接 + 层归一化(自注意力)
        attn_output = self.self_attn(x, x, x, mask)
        x = self.norm1(x + self.dropout1(attn_output))

        # 残差连接 + 层归一化(前馈网络)
        ff_output = self.feed_forward(x)
        x = self.norm2(x + self.dropout2(ff_output))

        return x


class TransformerEncoder(nn.Module):
    """Transformer编码器"""

    def __init__(self, num_layers, d_model, num_heads, d_ff, dropout=0.1):
        super().__init__()
        self.layers = nn.ModuleList([
            TransformerEncoderLayer(d_model, num_heads, d_ff, dropout)
            for _ in range(num_layers)
        ])
        self.norm = nn.LayerNorm(d_model)

    def forward(self, x, mask=None):
        for layer in self.layers:
            x = layer(x, mask)
        return self.norm(x)


# 使用示例
if __name__ == "__main__":
    # 参数设置
    d_model = 512      # 模型维度
    num_heads = 8      # 注意力头数量
    d_ff = 2048        # 前馈网络维度
    num_layers = 6     # 编码器层数
    seq_len = 100      # 序列长度
    batch_size = 32    # 批次大小

    # 创建编码器
    encoder = TransformerEncoder(
        num_layers=num_layers,
        d_model=d_model,
        num_heads=num_heads,
        d_ff=d_ff
    )

    # 创建输入数据 [batch_size, seq_len, d_model]
    x = torch.randn(batch_size, seq_len, d_model)

    # 前向传播
    output = encoder(x)
    print(f"输入形状: {x.shape}")
    print(f"输出形状: {output.shape}")
    print("Transformer编码器前向传播测试通过!")

代码详解

  • MultiHeadAttention类:实现多头注意力机制,包括Q、K、V的线性变换、注意力计算、多头拼接
  • PositionWiseFeedForward类:实现位置-wise前馈神经网络,两层全连接网络
  • TransformerEncoderLayer类:单个编码器层,包含自注意力和前馈网络两个子层
  • TransformerEncoder类:编码器堆叠,由多个编码器层组成

注意力分数计算可视化

┌─────────────────────────────────────────────────────────────────────────────┐
│                    注意力分数计算详细过程                                      │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  假设句子: "我 喜欢 学习 人工智能"                                            │
│                                                                              │
│  步骤1: 计算Q·K^T(相似度矩阵)                                              │
│                                                                              │
│           Q=我    Q=喜欢  Q=学习  Q=AI                                        │
│         ┌──────┬──────┬──────┬──────┐                                       │
│    K=我  │ 0.9  │ 0.3  │ 0.2  │ 0.1  │                                       │
│         ├──────┼──────┼──────┼──────┤                                       │
│   K=喜欢 │ 0.2  │ 0.8  │ 0.4  │ 0.1  │                                       │
│         ├──────┼──────┼──────┼──────┤                                       │
│   K=学习 │ 0.1  │ 0.4  │ 0.9  │ 0.3  │                                       │
│         ├──────┼──────┼──────┼──────┤                                       │
│   K=AI   │ 0.1  │ 0.1  │ 0.3  │ 0.8  │                                       │
│         └──────┴──────┴──────┴──────┘                                       │
│                                                                              │
│  步骤2: 除以√d_k进行缩放(假设d_k=64)                                       │
│         相似度矩阵 / 8                                                       │
│                                                                              │
│  步骤3: Softmax归一化(以"学习"为例)                                        │
│                                                                              │
│         原始分数: [0.1, 0.4, 0.9, 0.3]                                       │
│         exp值:    [1.1, 1.5, 2.5, 1.4]                                       │
│         归一化后: [0.13, 0.22, 0.43, 0.22]                                   │
│                                                                              │
│         权重和 = 1.0                                                          │
│                                                                              │
│  步骤4: 加权求和(输出"学习"的上下文表示)                                    │
│                                                                              │
│         输出 = 0.13×V(我) + 0.22×V(喜欢) + 0.43×V(学习) + 0.22×V(AI)        │
│                                                                              │
│         解读: "学习"这个词在上下文中与自身关联最强(0.43),                   │
│               其次是"喜欢"和"AI"(各0.22),最后是"我"(0.13)               │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

现在让我们详细了解Transformer的三个核心组件:自注意力机制位置编码前馈网络

自注意力机制:Transformer的核心

自注意力(Self-Attention)是Transformer的核心创新,也是它能够有效处理序列数据的关键。

什么是自注意力?

简单来说,自注意力机制让序列中的每个位置都能"关注"序列中的所有其他位置,并根据相关性分配不同的权重。

注释:用一个会议讨论的场景来类比。想象一个项目会议,有5个人在讨论一个问题。在RNN模式下,每个人必须等前一个人说完才能发言(A说完B说,B说完C说...)。而在Transformer模式下,每个人可以同时听到所有人的发言,并根据发言内容的相关性决定重点听谁的。

在处理一个句子时,自注意力机制允许模型:

  • 当处理"银行"这个词时,同时"看到"句子中的"存款"、"利息"、"行长"等相关词
  • 根据上下文,正确理解"银行"在这个句子中的具体含义(是"河边的银行"还是"存钱的银行")

注释:这个能力对于理解自然语言至关重要。同一个词在不同上下文中有不同的含义。比如"意思"这个词:

  • "这篇文章很有意思"(有趣)
  • "我懂你的意思"(意图)
  • "小费的意思"(红包)

只有根据上下文(其他词)才能正确理解每个"意思"的具体含义。自注意力机制正是提供了这种"看上下文"的能力。

注意力分数的计算过程

在数学上,自注意力通过以下公式计算:

Attention(Q, K, V) = softmax(QK^T / √d_k) × V

注释:这个公式看起来很吓人,但它背后的思想可以用搜索引擎的类比来解释:

  • Q(Query,查询):你搜索框里输入的内容,代表"我在找什么"
  • K(Key,键):数据库中每个条目的标签,代表"这是什么"
  • V(Value,值):搜索结果的实际内容,代表"我能给你什么"

计算过程是:

  1. 计算相似度:用Q和每个K做点积,得到相似度分数
  2. 缩放处理:除以√d_k(缩放因子,防止数值过大)
  3. Softmax归一化:把分数转换成概率分布(所有分数加起来等于1)
  4. 加权求和:用这些权重对V进行加权平均

注释:用找餐厅的类比来说明:

  • 假设你想找"安静的咖啡馆"(Q = "安静的咖啡馆")
  • 餐厅数据库里有很多餐厅,每个餐厅有一个标签(K = 餐厅特征)

- 餐厅A标签:["安静", "咖啡馆", "适合工作"] - 餐厅B标签:["热闹", "酒吧", "适合聚会"] - 餐厅C标签:["安静", "中餐", "商务"]

  • 你搜索词和餐厅A标签的相似度最高(都是"安静"和"咖啡馆")
  • 所以餐厅A的权重最高,会优先推荐给你

多头注意力:让模型学习多种关联模式

在实际应用中,Transformer使用多头注意力(Multi-Head Attention),即同时进行多组注意力计算,每组使用不同的Q、K、V投影。

注释:「多头」的意思是"多组"。这就像在会议中,你可以同时关注多个方面:

  • 第一组注意力:关注语法结构(谁和什么动词相关)
  • 第二组注意力:关注语义关联(哪些词表达的是同一个概念)
  • 第三组注意力:关注位置关系(词和词之间的距离)

每组注意力可以专注于不同类型的关联模式,然后所有头的输出会被拼接起来,形成最终的表示。

多头注意力图解

输入向量
    │
    ├──→ 分成h个头(每个头是一个Q,K,V投影)
    │
    ├──→ 头1:计算注意力1 → 输出1
    ├──→ 头2:计算注意力2 → 输出2
    ├──→ ...(h个头)
    └──→ 头h:计算注意力h → 输出h
              │
              ├──→ 拼接所有头的输出
              └──→ 线性变换得到最终输出

注释:h通常是8或16(代表8头或16头注意力)。通过多头注意力,模型可以同时学习多种不同类型的关联模式,比如语法层面的关联、语义层面的关联、位置层面的关联等。

位置编码:给序列加上顺序信息

Transformer没有循环结构,它"天生"不知道序列中元素的顺序。比如,如果没有位置信息,模型无法区分"狗咬人"和"人咬狗"这两个意思完全相反的句子。

注释:位置编码(Positional Encoding)的目的就是给每个位置加上一个"位置标签",让模型知道每个元素在序列中的位置。

论文中使用的是正弦-余弦位置编码

  • 对于位置pos和维度i,位置编码的值由sin(pos/10000^(2i/dmodel))和cos(pos/10000^(2i/dmodel))计算得到

注释:为什么用正弦和余弦函数?因为它们有两个很好的性质:

  1. 唯一性:每个位置都有一个唯一的位置编码
  2. 相对位置可学习:两个位置编码的差值包含了它们的相对距离信息,模型可以学习利用这种信息

位置编码可视化与实现

┌─────────────────────────────────────────────────────────────────────────────┐
│                      正弦-余弦位置编码原理                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  公式:                                                                      │
│  PE(pos, 2i) = sin(pos / 10000^(2i/d_model))    (偶数维度)                 │
│  PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))  (奇数维度)                 │
│                                                                              │
│  参数说明:                                                                  │
│  • pos: 序列中的位置(0, 1, 2, ...)                                         │
│  • i: 维度索引(0, 1, 2, ..., d_model/2-1)                                  │
│  • d_model: 模型维度                                                          │
│                                                                              │
│  示例(d_model=8, 序列长度=5):                                             │
│  ┌────────┬──────────┬──────────┬──────────┬──────────┐                     │
│  │ 位置   │ PE(pos,0)│ PE(pos,1)│ PE(pos,2)│ PE(pos,3)│                     │
│  │        │ (sin)    │ (cos)    │ (sin)    │ (cos)    │                     │
│  ├────────┼──────────┼──────────┼──────────┼──────────┤                     │
│  │ pos=0  │ sin(0)   │ cos(0)   │ sin(0)   │ cos(0)   │                     │
│  │        │ = 0.00   │ = 1.00   │ = 0.00   │ = 1.00   │                     │
│  ├────────┼──────────┼──────────┼──────────┼──────────┤                     │
│  │ pos=1  │ sin(0.1) │ cos(0.1) │ sin(0.01)│ cos(0.01)│                     │
│  │        │ = 0.10   │ = 0.99   │ = 0.01   │ = 1.00   │                     │
│  ├────────┼──────────┼──────────┼──────────┼──────────┤                     │
│  │ pos=2  │ sin(0.2) │ cos(0.2) │ sin(0.02)│ cos(0.02)│                     │
│  │        │ = 0.20   │ = 0.98   │ = 0.02   │ = 1.00   │                     │
│  ├────────┼──────────┼──────────┼──────────┼──────────┤                     │
│  │ pos=3  │ sin(0.3) │ cos(0.3) │ sin(0.03)│ cos(0.03)│                     │
│  │        │ = 0.30   │ = 0.95   │ = 0.03   │ = 1.00   │                     │
│  └────────┴──────────┴──────────┴──────────┴──────────┘                     │
│                                                                              │
│  位置编码矩阵可视化(d_model=512, seq_len=100):                             │
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │                                                                     │    │
│  │   热图:                                                             │    │
│  │                                                                     │    │
│  │   维度 0 ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░   │    │
│  │   维度 1 ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░   │    │
│  │   维度 2 ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░   │    │
│  │   ...                                                                │    │
│  │   维度 255 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░████   │    │
│  │   维度 256 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░████   │    │
│  │   ...                                                                │    │
│  │   维度 511 ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░   │    │
│  │                                                                     │    │
│  │        └────────────────── 序列位置 ──────────────────▶             │    │
│  │                                                                     │    │
│  │  观察:高频维度变化快,低频维度变化慢                                  │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

PyTorch实现:位置编码

import torch
import math


class PositionalEncoding(nn.Module):
    """正弦-余弦位置编码实现"""

    def __init__(self, d_model, max_seq_len=5000, dropout=0.1):
        """
        参数:
            d_model: 模型维度
            max_seq_len: 最大序列长度
            dropout: Dropout比率
        """
        super().__init__()
        self.dropout = nn.Dropout(p=dropout)

        # 初始化位置编码矩阵 [max_seq_len, d_model]
        pe = torch.zeros(max_seq_len, d_model)
        position = torch.arange(0, max_seq_len, dtype=torch.float).unsqueeze(1)
        # 计算除数: 10000^(2i/d_model)
        div_term = torch.exp(
            torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)
        )

        # 填充位置编码
        pe[:, 0::2] = torch.sin(position * div_term)  # 偶数维度
        pe[:, 1::2] = torch.cos(position * div_term)  # 奇数维度
        pe = pe.unsqueeze(0)  # [1, max_seq_len, d_model]

        self.register_buffer('pe', pe)

    def forward(self, x):
        """
        前向传播

        参数:
            x: 输入张量 [batch_size, seq_len, d_model]

        返回:
            添加位置编码后的张量 [batch_size, seq_len, d_model]
        """
        seq_len = x.size(1)
        # 将位置编码添加到输入中(只取前seq_len个位置)
        x = x + self.pe[:, :seq_len, :]
        return self.dropout(x)


class RelativePositionalEncoding(nn.Module):
    """相对位置编码(更现代的方法)"""

    def __init__(self, d_model, max_len=5000):
        super().__init__()
        self.d_model = d_model
        self.max_len = max_len

        # 可学习的相对位置嵌入
        self.relative_attention_bias = nn.Embedding(2 * max_len, num_heads)

    def forward(self, seq_len):
        """
        生成相对位置偏差矩阵

        参数:
            seq_len: 序列长度

        返回:
            相对位置偏差矩阵 [num_heads, seq_len, seq_len]
        """
        # 生成位置索引
        query_position = torch.arange(seq_len).unsqueeze(1)
        key_position = torch.arange(seq_len).unsqueeze(0)
        relative_position = key_position - query_position
        # 偏移到非负索引 [0, 2*seq_len-1]
        relative_position += self.max_len - 1

        # 获取位置偏差
        position_bias = self.relative_attention_bias(relative_position)
        return position_bias  # [seq_len, seq_len, num_heads]


# 使用示例
if __name__ == "__main__":
    # 参数设置
    d_model = 512
    max_seq_len = 100
    batch_size = 32

    # 创建位置编码层
    pos_encoder = PositionalEncoding(d_model, max_seq_len)

    # 创建输入数据 [batch_size, seq_len, d_model]
    x = torch.randn(batch_size, max_seq_len, d_model)

    # 前向传播
    output = pos_encoder(x)
    print(f"输入形状: {x.shape}")
    print(f"输出形状: {output.shape}")
    print("位置编码测试通过!")

    # 验证位置编码的性质
    pe = pos_encoder.pe[0]  # [max_seq_len, d_model]
    print(f"\n位置编码矩阵形状: {pe.shape}")

    # 检查相邻位置的相似度
    similarity_0_1 = torch.cosine_similarity(pe[0], pe[1], dim=0)
    similarity_0_50 = torch.cosine_similarity(pe[0], pe[50], dim=0)
    print(f"位置0和位置1的余弦相似度: {similarity_0_1:.4f}")
    print(f"位置0和位置50的余弦相似度: {similarity_0_50:.4f}")
    print("相邻位置更相似,符合预期!")

位置编码的性质验证

┌─────────────────────────────────────────────────────────────────────────────┐
│                      位置编码性质验证                                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  性质1: 唯一性 - 每个位置有唯一的编码                                          │
│  ─────────────────────────────────────────────────────────────────────────  │
│  测试: 计算所有位置编码之间的汉明距离(或余弦相似度)                           │
│  结果: 不同位置的编码不完全相同                                               │
│                                                                              │
│  性质2: 相对位置可学习                                                       │
│  ─────────────────────────────────────────────────────────────────────────  │
│  测试: 比较位置i和位置j的编码与位置i+1和位置j+1的编码                          │
│  预期: 差值相似(反映相对距离)                                               │
│                                                                              │
│  实际观察:                                                                   │
│  • 位置0 vs 位置1: 相似度 ≈ 0.999                                            │
│  • 位置0 vs 位置10: 相似度 ≈ 0.97                                            │
│  • 位置0 vs 位置50: 相似度 ≈ 0.85                                            │
│  • 位置0 vs 位置99: 相似度 ≈ 0.60                                            │
│                                                                              │
│  结论: 距离越近的位置编码越相似,模型可以学习这种关系                          │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

注释:每个位置都有一条独特的"波浪",代表它的位置编码。相邻位置的编码比较相似,远距离位置的编码差异较大,这正好反映了位置关系。

前馈网络:非线性变换

在每个Transformer层中,除了注意力层,还有一个前馈神经网络(Feed-Forward Network,FFN)。

前馈网络的结构很简单:两层全连接网络,中间有一个ReLU激活函数。

输入 → 线性变换 → ReLU → 线性变换 → 输出
     (第一层)          (第二层)

注释:虽然结构简单,但前馈网络在Transformer中扮演着重要的角色:

  1. 引入非线性:注意力机制本质上是线性的(矩阵乘法),需要非线性的激活函数来增加模型的表达能力
  2. 特征变换:对注意力层的输出进行进一步的非线性变换,提取更复杂的特征
  3. 容量补充:每个位置独立进行变换,增加了模型的参数量和容量

前馈网络结构详图

┌─────────────────────────────────────────────────────────────────────────────┐
│                    前馈神经网络内部结构                                        │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  输入向量 X [d_model]                                                        │
│      │                                                                        │
│      ▼                                                                        │
│  ┌───────────────────┐                                                       │
│  │  线性变换 W1      │                                                       │
│  │  [d_model → d_ff] │                                                       │
│  │  输入: X          │                                                       │
│  │  输出: H = W1X + b1│                                                      │
│  └─────────┬─────────┘                                                       │
│            │                                                                 │
│            ▼                                                                 │
│  ┌───────────────────┐                                                       │
│  │  ReLU激活函数     │                                                       │
│  │  H' = max(0, H)   │                                                       │
│  └─────────┬─────────┘                                                       │
│            │                                                                 │
│            ▼                                                                 │
│  ┌───────────────────┐                                                       │
│  │  线性变换 W2      │                                                       │
│  │  [d_ff → d_model] │                                                       │
│  │  输入: H'         │                                                       │
│  │  输出: Y = W2H' + b2│                                                     │
│  └─────────┬─────────┘                                                       │
│            │                                                                 │
│            ▼                                                                 │
│  输出向量 Y [d_model]                                                        │
│                                                                              │
│  典型参数:                                                                    │
│  • d_model = 512                                                             │
│  • d_ff = 2048 (= 4 × d_model)                                               │
│  • 参数量: 512×2048 + 2048 + 2048×512 + 512 ≈ 200万                          │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

注释:可以把前馈网络想象成"思考深化器"。注意力层负责"看到"所有相关位置并进行信息汇聚,前馈网络则负责对这些信息进行"深度思考"——进一步加工、提炼、转化。

多模态场景的扩展:Cross-Attention

标准的自注意力是在同一模态内部进行的(文本-文本或图像-图像)。在多模态场景中,我们需要跨模态的注意力,让不同模态之间能够交互。

交叉注意力(Cross-Attention)就是解决这个问题的。它和自注意力的结构类似,但有一个关键区别:Q来自一个模态,而K和V来自另一个模态。

交叉注意力图解

┌─────────────────────────────────────────────────────────────────────────────┐
│                       交叉注意力机制                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  模态A(如文本)          模态B(如图像)                                      │
│      │                      │                                               │
│      ▼                      ▼                                               │
│   Q向量                 K和V向量                                              │
│      │                      │                                               │
│      └──→ 注意力计算 ────────┘                                              │
│                    │                                                        │
│                    ▼                                                        │
│                输出向量                                                     │
│                                                                              │
│  计算公式:                                                                   │
│  CrossAttention(Q_A, K_B, V_B) = softmax(Q_A × K_B^T / √d_k) × V_B          │
│                                                                              │
│  多模态应用示例:                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  应用场景            │  Q来源    │  K,V来源    │  作用              │   │
│  │  ─────────────────────────────────────────────────────────────────  │   │
│  │  图文理解            │  文本特征  │  图像特征   │  用文本查询图像    │   │
│  │  视觉问答            │  问题向量  │  图像特征   │  根据问题找答案    │   │
│  │  语音-文本对齐       │  语音特征  │  文本特征   │  语音-文本对应    │   │
│  │  视频描述            │  文本前缀  │  视频特征   │  根据描述生成      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

PyTorch实现:交叉注意力

class CrossAttention(nn.Module):
    """交叉注意力机制 - 多模态融合核心组件"""

    def __init__(self, d_model, num_heads, dropout=0.1):
        super().__init__()
        assert d_model % num_heads == 0

        self.d_model = d_model
        self.num_heads = num_heads
        self.d_k = d_model // num_heads

        # Q来自模态A
        self.w_q = nn.Linear(d_model, d_model)
        # K和V来自模态B
        self.w_k = nn.Linear(d_model, d_model)
        self.w_v = nn.Linear(d_model, d_model)
        self.w_o = nn.Linear(d_model, d_model)

        self.dropout = nn.Dropout(dropout)
        self.scale = math.sqrt(self.d_k)

    def forward(self, query_modal_a, key_modal_b, value_modal_b, mask=None):
        """
        前向传播

        参数:
            query_modal_a: 模态A的查询 [batch_size, seq_a, d_model]
            key_modal_b: 模态B的键 [batch_size, seq_b, d_model]
            value_modal_b: 模态B的值 [batch_size, seq_b, d_model]
            mask: 掩码(可选)

        返回:
            输出张量 [batch_size, seq_a, d_model]
        """
        batch_size = query_modal_a.size(0)

        # 线性变换并分拆成多个头
        query = self.w_q(query_modal_a).view(
            batch_size, -1, self.num_heads, self.d_k
        ).transpose(1, 2)
        key = self.w_k(key_modal_b).view(
            batch_size, -1, self.num_heads, self.d_k
        ).transpose(1, 2)
        value = self.w_v(value_modal_b).view(
            batch_size, -1, self.num_heads, self.d_k
        ).transpose(1, 2)

        # 计算注意力分数 [batch_size, num_heads, seq_a, seq_b]
        scores = torch.matmul(query, key.transpose(-2, -1)) / self.scale

        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)

        # Softmax得到权重
        attention_weights = F.softmax(scores, dim=-1)
        attention_weights = self.dropout(attention_weights)

        # 加权求和
        context = torch.matmul(attention_weights, value)

        # 拼接多头的输出
        context = context.transpose(1, 2).contiguous().view(
            batch_size, -1, self.d_model
        )

        return self.w_o(context)


class MultimodalFusionBlock(nn.Module):
    """多模态融合模块 - 结合文本和图像"""

    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super().__init__()
        self.cross_attn_text_to_image = CrossAttention(d_model, num_heads, dropout)
        self.cross_attn_image_to_text = CrossAttention(d_model, num_heads, dropout)

        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.ffn = PositionWiseFeedForward(d_model, d_ff, dropout)

        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)

    def forward(self, text_features, image_features):
        """
        前向传播

        参数:
            text_features: 文本特征 [batch_size, seq_len_t, d_model]
            image_features: 图像特征 [batch_size, seq_len_i, d_model]

        返回:
            融合后的文本特征和图像特征
        """
        # 文本到图像的交叉注意力
        text_enhanced = self.cross_attn_text_to_image(
            text_features, image_features, image_features
        )
        text_features = self.norm1(text_features + self.dropout1(text_enhanced))

        # 图像到文本的交叉注意力
        image_enhanced = self.cross_attn_image_to_text(
            image_features, text_features, text_features
        )
        image_features = self.norm2(image_features + self.dropout2(image_enhanced))

        # 前馈网络处理
        text_features = self.ffn(text_features)
        image_features = self.ffn(image_features)

        return text_features, image_features


# 使用示例
if __name__ == "__main__":
    # 参数设置
    d_model = 512
    num_heads = 8
    d_ff = 2048

    # 创建模块
    cross_attn = CrossAttention(d_model, num_heads)
    fusion_block = MultimodalFusionBlock(d_model, num_heads, d_ff)

    # 模拟数据
    batch_size = 2
    seq_len_text = 20
    seq_len_image = 49  # 7x7图像patch
    text_features = torch.randn(batch_size, seq_len_text, d_model)
    image_features = torch.randn(batch_size, seq_len_image, d_model)

    # 测试交叉注意力
    output = cross_attn(text_features, image_features, image_features)
    print(f"交叉注意力输入形状: {text_features.shape}")
    print(f"交叉注意力输出形状: {output.shape}")

    # 测试融合模块
    fused_text, fused_image = fusion_block(text_features, image_features)
    print(f"\n融合后文本特征形状: {fused_text.shape}")
    print(f"融合后图像特征形状: {fused_image.shape}")
    print("多模态融合模块测试通过!")

代码详解

  • CrossAttention类:实现交叉注意力机制,Q来自模态A,K和V来自模态B
  • MultimodalFusionBlock类:完整的双向多模态融合模块,包含文本到图像和图像到文本的双向交叉注意力

注释:用图文理解的例子来说明:

  • Q来自文本:"图片里有什么?"
  • K和V来自图像:图像各个区域的特征向量
  • 计算结果是:文本问题"映射"到图像的对应区域,找出答案所在的视觉位置

注释:交叉注意力是多模态融合的关键技术之一。它允许一种模态的信息去"查询"另一种模态的信息,实现真正的跨模态理解。

本节小结

让我们用简洁的总结回顾这一节的核心内容。

Transformer架构的核心组件

  • 自注意力机制:让序列中每个位置都能关注所有其他位置,根据相关性分配权重
  • 多头注意力:同时进行多组注意力计算,学习多种关联模式
  • 位置编码:给序列元素加上位置信息,让模型知道顺序
  • 前馈网络:对注意力输出进行非线性变换,增加模型容量
  • 交叉注意力:跨模态的注意力机制,实现不同模态之间的信息交互

Transformer相比RNN的优势

  • 直接建立长距离依赖,不存在信息稀释问题
  • 支持并行计算,训练效率高
  • 注意力机制提供了更好的可解释性

思考题:如果让你设计一个能同时处理文本和图像的模型,你会如何使用这些组件?请画出你的设计草图。


2.2 注意力机制详解

注意力机制的本质

在上一节中,我们初步了解了注意力机制的基本概念。在这一节中,我们将更深入地探讨注意力机制的工作原理,特别是在多模态融合中的应用。

首先,让我们重新审视一个问题:为什么叫"注意力"机制?

注释:这个名字来源于人类注意力的类比。当你在一幅复杂的画作中寻找某个物体时,你的眼睛会自动聚焦在相关的区域,而忽略不相关的信息。注意力机制正是模拟了这种认知过程——让模型能够"有选择地"关注输入的不同部分,而不是平等地处理所有信息。

注意力机制的核心思想可以概括为:在处理任何一个元素时,不是把所有信息同等对待,而是根据当前任务的需求,动态地决定哪些信息更重要,应该分配更多的"注意力"。

注释:用阅读理解来类比。当你做阅读理解题时,你会仔细阅读和问题相关的段落,而快速浏览不相关的段落。注意力机制让AI模型也能做类似的事情——在回答问题时,重点关注与问题相关的上下文。

Q、K、V的三角关系

在Transformer中,注意力机制的核心是Q、K、V三个矩阵。让我们深入理解它们之间的关系。

Q(Query,查询):代表"我想知道什么",是当前正在处理的位置的表示。它就像你在搜索引擎中输入的查询词。

注释:Q是从当前输入经过线性变换得到的。线性变换可以理解为"投影"——把原始的向量"投射"到一个新的空间,这个新空间是专门为计算注意力设计的。

K(Key,键):代表"我有什么",是序列中所有可能位置的表示。它就像搜索引擎数据库中每个网页的关键词标签。

注释:K和Q在同一个空间中进行比较,计算相似度。每个K对应一个位置的信息,用于判断这个位置是否与当前的Q相关。

V(Value,值):代表"我能提供什么",是序列中每个位置的原始信息。它就像搜索引擎返回的网页内容。

注释:V是实际携带信息的向量。最终的注意力输出是V的加权平均,权重由Q和K的相似度决定。

Q、K、V的关系可以用"面试"来类比

注释:想象一个公司招聘:

  • Q(查询):面试官问的问题,代表"我们需要什么样的人"
  • K(键):每个候选人的简历,代表"我有什么技能和经验"
  • V(值):候选人的详细信息,代表"我的具体经历"

面试过程就是计算Q和每个K的匹配程度,然后根据匹配程度决定关注哪个候选人的V(详细信息)。

注意力分数的计算

现在让我们详细了解注意力分数的计算过程。虽然数学公式看起来复杂,但背后的思想很简单。

步骤一:计算相似度

首先,计算Q和每个K的相似度。论文中使用的方法是点积

score_i = Q · K_i

注释:点积是衡量两个向量相似度的常用方法。当两个向量的方向越相似(夹角越小),点积越大;方向越不相似,点积越小。

步骤二:缩放处理

为了防止点积的值过大(特别是当向量维度较高时),除以一个缩放因子√d_k:

scaled_score_i = score_i / √d_k

注释:d_k是K向量的维度。为什么要缩放?因为当维度较高时,向量的点积可能会变得很大,导致softmax函数进入梯度很小的区域,影响训练稳定性。缩放可以把方差控制在合理范围内。

步骤三:Softmax归一化

使用softmax函数把分数转换成概率分布:

weight_i = softmax(scaled_score_i) = exp(scaled_score_i) / Σexp(scaled_score_j)

注释:Softmax函数把所有分数转换成0到1之间的值,且所有值的和等于1。这使得这些值可以解释为"概率"或"权重"。

步骤四:加权求和

最后,用这些权重对V进行加权平均:

output = Σ(weight_i × V_i)

注意力计算可视化详图

┌─────────────────────────────────────────────────────────────────────────────┐
│                    注意力分数计算详细流程                                      │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  输入:                                                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  Q(查询向量): [0.5, 0.8, 0.2]                                      │   │
│  │  K(键矩阵):                                                         │   │
│  │     K1: [0.9, 0.1, 0.3]  ← 位置1                                    │   │
│  │     K2: [0.4, 0.7, 0.5]  ← 位置2                                    │   │
│  │     K3: [0.2, 0.6, 0.8]  ← 位置3                                    │   │
│  │  V(值矩阵):                                                         │   │
│  │     V1: [1.0, 2.0, 3.0]  ← 位置1                                    │   │
│  │     V2: [4.0, 5.0, 6.0]  ← 位置2                                    │   │
│  │     V3: [7.0, 8.0, 9.0]  ← 位置3                                    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  步骤1: 计算Q·K^T(点积相似度)                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                     │   │
│  │  score1 = Q·K1 = 0.5×0.9 + 0.8×0.1 + 0.2×0.3 = 0.51               │   │
│  │  score2 = Q·K2 = 0.5×0.4 + 0.8×0.7 + 0.2×0.5 = 0.82               │   │
│  │  score3 = Q·K3 = 0.5×0.2 + 0.8×0.6 + 0.2×0.8 = 0.74               │   │
│  │                                                                     │   │
│  │  分数矩阵: [0.51, 0.82, 0.74]                                        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                │                                             │
│                                ▼                                             │
│  步骤2: 缩放处理(除以√d_k,假设d_k=3)                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  scaled_scores = scores / √3 ≈ scores / 1.732                       │   │
│  │  [0.29, 0.47, 0.43]                                                 │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                │                                             │
│                                ▼                                             │
│  步骤3: Softmax归一化                                                       │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  exp_values = [e^0.29, e^0.47, e^0.43] ≈ [1.34, 1.60, 1.54]        │   │
│  │  sum_exp = 1.34 + 1.60 + 1.54 = 4.48                               │   │
│  │  weights = exp / sum_exp                                            │   │
│  │  weight1 ≈ 1.34 / 4.48 = 0.30                                       │   │
│  │  weight2 ≈ 1.60 / 4.48 = 0.36                                       │   │
│  │  weight3 ≈ 1.54 / 4.48 = 0.34                                       │   │
│  │  sum_weights = 0.30 + 0.36 + 0.34 = 1.00 ✓                         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                │                                             │
│                                ▼                                             │
│  步骤4: 加权求和(输出)                                                     │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                     │   │
│  │  output = 0.30×V1 + 0.36×V2 + 0.34×V3                              │   │
│  │       = 0.30×[1.0,2.0,3.0] + 0.36×[4.0,5.0,6.0] + 0.34×[7.0,8.0,9.0]│   │
│  │       = [0.30+1.44+2.38, 0.60+1.80+2.72, 0.90+2.16+3.06]           │   │
│  │       = [4.12, 5.12, 6.12]                                          │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  解读:Q与K2的相似度最高(0.47),因此V2被赋予最高的权重(0.36)              │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

不同注意力机制对比表

┌─────────────────────────────────────────────────────────────────────────────┐
│                    注意力机制类型对比                                          │
├─────────────────┬─────────────────┬─────────────────┬───────────────────────┤
│     类型        │      Q来源      │      K,V来源    │        典型应用        │
├─────────────────┼─────────────────┼─────────────────┼───────────────────────┤
│  自注意力       │  模态A          │  模态A          │  序列内部建模          │
│  (Self-Attention)│                  │                  │  (Transformer编码器)   │
├─────────────────┼─────────────────┼─────────────────┼───────────────────────┤
│  交叉注意力     │  模态A          │  模态B          │  多模态融合            │
│  (Cross-Attention)│                │                  │  (Transformer解码器)   │
├─────────────────┼─────────────────┼─────────────────┼───────────────────────┤
│  掩码自注意力   │  模态A          │  模态A          │  因果语言建模          │
│  (Masked Self)  │  (仅左侧)       │  (仅左侧)       │  (GPT等生成模型)       │
├─────────────────┼─────────────────┼─────────────────┼───────────────────────┤
│  稀疏注意力     │  模态A          │  模态A          │  长序列处理            │
│  (Sparse)       │  (局部+全局)    │  (局部+全局)    │  (Longformer)         │
├─────────────────┼─────────────────┼─────────────────┼───────────────────────┤
│  线性注意力     │  模态A          │  模态A          │  高效长序列            │
│  (Linear)       │  (核函数映射)   │  (核函数映射)   │  (Linformer)          │
├─────────────────┼─────────────────┼─────────────────┼───────────────────────┤
│  多查询注意力   │  模态A          │  模态B          │  内存高效推理          │
│  (Multi-Query)  │  (多个头)       │  (共享K,V)      │  (FlashAttention)     │
└─────────────────┴─────────────────┴─────────────────┴───────────────────────┘

多模态融合中的注意力注释:这就是注意力的最终输出——一个综合了所有位置信息的向量,其中与当前查询更相关的信息被赋予了更高的权重。

注意力计算图解

输入序列:["我", "喜欢", "学习", "人工智能"]

为"学习"这个词计算注意力:
┌─────────────────────────────────────────────────────────┐
│  Q("学习"的查询向量)                                  │
│     │                                                    │
│     ├──→ 与"我"的K计算相似度 → 权重0.1                   │
│     ├──→ 与"喜欢"的K计算相似度 → 权重0.15                │
│     ├──→ 与"学习"的K计算相似度 → 权重0.65(自己,重要)   │
│     └──→ 与"人工智能"的K计算相似度 → 权重0.1              │
│                                                        │
│  加权求和输出 = 0.1×V("我") + 0.15×V("喜欢")            │
│              + 0.65×V("学习") + 0.1×V("人工智能")       │
└─────────────────────────────────────────────────────────┘

多模态融合中的注意力

在多模态场景中,注意力机制有一个非常重要的应用:跨模态注意力(Cross-Modal Attention)。这是让不同模态能够"对话"的关键技术。

图文理解的跨模态注意力

注释:假设我们要让AI理解一张图片和对应的文字描述。图片被分成多个区域,每个区域有一个视觉特征向量;文字被分成多个词,每个词有一个词向量。

跨模态注意力允许:

  • 用文本的特征(Q)去"查询"图像的特征(K, V)
  • 或者用图像的特征(Q)去"查询"文本的特征(K, V)

例子:视觉问答(VQA)

假设用户问:"图里有几只猫?"

注释:处理流程如下:

  1. 图像编码:把图片分成多个区域(如9宫格),每个区域提取视觉特征
  2. 文本编码:把问题编码为向量
  3. 跨模态注意力

- Q = 问题向量("图里有几只猫?") - K = 各个图像区域的视觉特征 - V = 各个图像区域的视觉特征

  1. 注意力计算:找出问题与哪些图像区域最相关(可能是猫所在的区域)
  2. 答案生成:基于注意力结果,生成答案"一只"

跨模态注意力图解

问题:"图里有几只猫?"
    │
    ▼
问题编码 → Q(查询向量)
    │
    │ 跨模态注意力计算
    ▼
┌─────────────────────────────────────────┐
│                                         │
│  图像区域1(天空)      K1 → 低权重     │
│  图像区域2(草地)      K2 → 低权重     │
│  图像区域3(猫)        K3 → 高权重 ★   │
│  图像区域4(猫)        K4 → 高权重 ★   │
│                                         │
└─────────────────────────────────────────┘
    │
    ▼
加权求和 → 聚焦于猫区域的特征 → 答案:"两只"

多模态融合的其他方式

除了跨模态注意力,还有其他几种常见的多模态融合方式:

早期融合(Early Fusion):在输入层就把不同模态的数据拼接起来,然后统一处理。

注释:优点是模态间的交互在最早的阶段就开始了;缺点是不同模态的数据形式可能差异很大,直接拼接效果不好。

晚期融合(Late Fusion):分别用不同的编码器处理各个模态,在决策层(最后一步)再把各模态的表示拼接或融合。

注释:优点是各模态可以独立优化;缺点是模态间的交互不够深入,可能错过模态间的细粒度关联。

中间融合(Intermediate Fusion):在多个层级上进行模态间的交互和融合。

注释:这是目前最常用的方法,结合了早期融合和晚期融合的优点。Transformer的交叉注意力机制就是一种典型的中间融合方式。

自注意力和交叉注意力的对比

为了更清晰地理解,让我们对比一下自注意力和交叉注意力:

对比维度自注意力交叉注意力
Q的来源模态A模态A
K的来源模态A模态B
V的来源模态A模态B
作用模态内部的信息交互跨模态的信息交互
应用场景序列内部建模多模态融合
Transformer中的位置编码器内部解码器查询编码器

注释:自注意力让同一模态内的各个位置能够相互"交流",比如一句话中各个词之间的关联。交叉注意力让不同模态之间能够"交流",比如文本和图像之间、文本和音频之间。

本节小结

让我们用简洁的总结回顾这一节的核心内容。

注意力机制的本质

  • 动态分配权重,让模型能够"有选择地"关注输入的不同部分
  • 核心计算是Q(查询)和K(键)的匹配,然后用权重对V(值)进行加权平均

Q、K、V的关系

  • Q = "我在找什么"(当前任务的需求)
  • K = "我有什么"(候选信息的内容标签)
  • V = "实际内容"(候选信息的具体内容)

多模态融合中的应用

  • 跨模态注意力允许用一种模态的Q去查询另一种模态的K和V
  • 这是实现图文理解、视觉问答等任务的关键技术

思考题:如果让你设计一个能同时理解文本、图像和音频的三模态模型,你会如何使用注意力机制来实现它们之间的两两交互?


2.3 特征表示学习

什么是特征表示?

在理解了Transformer和注意力机制之后,我们需要了解另一个核心概念:特征表示学习(Feature Representation Learning)。

什么是特征表示?

简单来说,特征表示就是把复杂的数据(如图像、文本、音频)转换成计算机能够处理的数值向量的过程。

注释:计算机只能处理数字,不能直接理解一张图片里有什么、一段文字是什么意思。特征表示的任务就是找到一种方法,把这些"人类能理解"的信息转换成"计算机能处理"的数值形式,同时保持信息的完整性——转换后的向量应该保留原始数据的关键信息。

用"简历"来类比特征表示

注释:想象你要向别人介绍你自己。你有丰富的人生经历——你上过什么学校、做过什么工作、有什么技能、得过什么奖...这些信息是复杂的、立体的、难以用一句话概括的。

现在,你需要用一份简历来概括自己。简历的设计很巧妙——它用有限的篇幅(几个板块、几十行文字)就抓住了你最重要的信息。看过你简历的人,虽然没有真正了解你的全部人生,但已经对你有了基本的了解。

注释:特征表示就像是给数据"写简历"的过程:

  • 原始数据(你的人生经历)很复杂、维度很高
  • 特征向量(你的简历)很简洁、维度较低
  • 但特征向量保留了原始数据的关键信息
  • 看过特征向量,就能对原始数据有基本的了解

不同模态的原始数据形式

让我们看看不同模态的原始数据是什么样的,以及它们如何被转换成特征向量:

文本:文本是由字符、词或词组组成的序列。每个词都可以被转换成一个向量(词嵌入),一个句子可以表示为多个词的向量的组合或聚合。

注释:早期的词表示方法是独热编码(One-Hot Encoding)——假设你有10000个词,每个词表示为一个10000维的向量,只有一个位置是1,其他都是0。这种方法的问题是完全忽略了词与词之间的语义关系——"猫"和"狗"的独热编码没有任何相似度,尽管它们都是动物。

注释:现代的方法是词嵌入(Word Embedding)——把每个词表示为一个稠密的低维向量(如300维)。在这个向量空间中,语义相似的词距离较近:"猫"和"狗"的向量距离近,"猫"和"汽车"的向量距离远。Word2Vec、GloVe是代表性的词嵌入方法。

图像:图像是由像素组成的二维矩阵。每个像素有颜色值(RGB三个通道)。一张224×224的彩色图片有224×224×3 = 150528个数值。

注释:直接用原始像素作为特征有两个问题:

  1. 维度太高:150528维的向量太大,计算和存储成本高
  2. 语义不明:像素值只代表颜色,不包含"这是猫还是狗"这样的语义信息

注释:图像特征提取使用卷积神经网络(CNN,Convolutional Neural Network)或Vision Transformer(ViT)。这些方法能够:

  • 把高维的像素信息"压缩"成低维的特征向量(如512维或1024维)
  • 提取语义信息——特征向量包含了"图中有什么物体、什么场景"的语义信息

ViT的工作原理(Vision Transformer):

  1. 把图像切成若干个"patch"(如16×16像素的小块)
  2. 把每个patch展平并通过线性变换成向量
  3. 加入位置编码(告诉模型每个patch的位置)
  4. 输入Transformer编码器进行处理
  5. 用第一个位置的输出(或所有patch输出的聚合)作为图像特征

音频:音频是随时间变化的波形信号。原始音频数据是时间序列,每个时间点有一个振幅值。一段10秒、采样率44.1kHz的音频有441000个样本点。

注释:直接用原始波形作为特征同样面临维度太高、语义不明的问题。音频特征提取的常用方法是:

  • 频谱特征(Mel Spectrogram):把时域信号转换成频域表示,然后按时间排列成二维"图像"
  • 预训练音频编码器:如Whisper、AudioCLIP等,在大规模音频数据上预训练,能够提取语义丰富的音频特征

频谱特征的工作原理

  1. 对音频进行短时傅里叶变换(STFT),得到频谱
  2. 把频率轴转换成Mel尺度(更符合人类听觉感知)
  3. 对幅度取对数,得到对数梅尔频谱(Log Mel Spectrogram)
  4. 结果是一个二维矩阵,可以像图像一样处理

注释:处理后的频谱图就像一张"声音的图片",水平轴是时间,垂直轴是频率,颜色深浅代表能量强弱。人类语音的频谱图有明显的条纹结构(共振峰),不同乐器的声音有不同的频谱特征。

多模态特征提取架构对比

┌─────────────────────────────────────────────────────────────────────────────┐
│                    多模态特征提取架构对比                                      │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │  统一编码器架构 (如Gemini)                                            │  │
│  │  ────────────────────────────────────────────────────────────────   │  │
│  │                                                                      │  │
│  │   文本 ──┐                                                           │  │
│  │          ├──→ 统一嵌入层 ──▶ 统一Transformer ──▶ 多模态表示         │  │
│  │   图像 ──┤                                                           │  │
│  │   音频 ──┘                                                           │  │
│  │                                                                      │  │
│  │  优点:深度模态交互,端到端优化                                       │  │
│  │  缺点:训练复杂,需要大量多模态数据                                   │  │
│  │                                                                      │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │  双编码器架构 (如CLIP)                                               │  │
│  │  ────────────────────────────────────────────────────────────────   │  │
│  │                                                                      │  │
│  │   文本 ──▶ 文本编码器 ──▶ 文本特征 ──┐                               │  │
│  │                                      ├──→ 对比学习 ──▶ 统一空间      │  │
│  │   图像 ──▶ 图像编码器 ──▶ 图像特征 ──┘                               │  │
│  │                                                                      │  │
│  │  优点:简单高效,易于训练                                             │  │
│  │  缺点:模态交互较浅                                                   │  │
│  │                                                                      │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │  融合编码器架构 (如Flamingo)                                         │  │
│  │  ────────────────────────────────────────────────────────────────   │  │
│  │                                                                      │  │
│  │   文本 ──▶ LLM ──▶ ┌─────────────────────────────────────────┐     │  │
│  │                    │  Cross-Attention Layers (插入到LLM中)    │     │  │
│  │   图像 ──▶ ViT ──▶ └─────────────────────────────────────────┘     │  │
│  │                                                                      │  │
│  │  优点:强大多模态能力,支持复杂推理                                   │  │
│  │  缺点:计算开销大                                                     │  │
│  │                                                                      │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

统一特征空间的概念

理解了不同模态的特征表示后,我们来看多模态学习中最关键的概念之一:统一特征空间(Unified Feature Space)。

为什么需要统一特征空间?

在多模态学习中,我们需要比较、关联不同模态的内容。比如:

  • 给定一段文字描述,找到最匹配的图像
  • 给定一张图片,回答关于图片的问题
  • 给定一段语音,转录成对应的文字

注释:这些任务的前提是:不同模态的内容必须在同一个"空间"里才能比较。就像比较两个人的身高,必须用同一个单位(厘米或英寸),否则无法直接比较。

统一特征空间就是这样一个"共同语言"空间——所有模态的数据都被映射到这个空间中的向量,语义相似的内容在这个空间中距离较近。

统一特征空间图解

┌─────────────────────────────────────────────────────────────────────────────┐
│                         统一特征空间详细架构                                   │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                         原始数据层                                     │  │
│  │                                                                       │  │
│  │   文本: "一只橘猫坐在窗台上"      图像: 🖼️ 橘猫在窗台上的照片           │  │
│  │   音频: 🐱 猫叫声               视频: 🎬 猫跳来跳去                    │  │
│  │                                                                       │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                   │                                          │
│              ┌────────────────────┼────────────────────┐                     │
│              ▼                    ▼                    ▼                     │
│  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐           │
│  │   文本编码器      │  │   图像编码器      │  │   音频编码器      │           │
│  │  (Transformer)   │  │   (ViT)         │  │  (Audio Spectrogram│          │
│  │                  │  │                  │  │   Transformer)    │           │
│  └────────┬─────────┘  └────────┬─────────┘  └────────┬─────────┘           │
│           │                     │                     │                      │
│           ▼                     ▼                     ▼                      │
│  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐           │
│  │ 文本特征向量     │  │ 图像特征向量     │  │ 音频特征向量     │           │
│  │ [0.1, 0.3, ...]  │  │ [0.1, 0.3, ...]  │  │ [0.1, 0.3, ...]  │           │
│  │  dim=512         │  │  dim=512         │  │  dim=512         │           │
│  └────────┬─────────┘  └────────┬─────────┘  └────────┬─────────┘           │
│           │                     │                     │                      │
│           └─────────────────────┼─────────────────────┘                      │
│                                 ▼                                            │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                      统一特征空间 (Shared Embedding Space)            │  │
│  │                                                                       │  │
│  │                    ┌─────────────────────────┐                       │  │
│  │                    │      归一化处理          │                       │  │
│  │                    │   (L2 Normalization)    │                       │  │
│  │                    └───────────┬─────────────┘                       │  │
│  │                                │                                       │  │
│  │                                ▼                                       │  │
│  │   ┌─────────────────────────────────────────────────────────────┐    │  │
│  │   │                                                             │    │  │
│  │   │   ● 猫 [0.15, 0.42, 0.31, ..., 0.08]  ●────────────● 猫图片 │    │  │
│  │   │              ●───────────●                                │    │  │
│  │   │              │  正样本对  │  (相似度: 0.89)                │    │  │
│  │   │              ●───────────●                                │    │  │
│  │   │                     ●────────────● 猫音频                 │    │  │
│  │   │                                                             │    │  │
│  │   │   ● 狗 [0.72, 0.15, 0.08, ..., 0.21]  ●──────────● 狗图片  │    │  │
│  │   │                                                             │    │  │
│  │   │   ● 车 [0.31, 0.08, 0.65, ..., 0.12]  ●──────────● 汽车图片 │    │  │
│  │   │                                                             │    │  │
│  │   │   (语义相似的实体在空间中距离较近)                            │    │  │
│  │   │                                                             │    │  │
│  │   └─────────────────────────────────────────────────────────────┘    │  │
│  │                                                                       │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
│  下游任务:                                                                   │
│  • 图文检索: 给定文本,搜索最相似的图像                                       │
│  • 零样本分类: 给定文本类别描述,识别图像中是否有该物体                       │
│  • 跨模态检索: 文本↔图像↔音频 之间的相互检索                                 │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

注释:在这个统一空间中:

  • "猫"这个文本向量和猫的图片向量距离很近
  • "狗"这个文本向量和狗的图片向量距离很近
  • 即使"猫"和"狗"的文本向量也有一定距离,因为它们是不同的概念

对比学习详解与实现

┌─────────────────────────────────────────────────────────────────────────────┐
│                    对比学习(Contrastive Learning)详解                       │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  对比学习核心思想:                                                           │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                     │   │
│  │   目标: 让正样本对在特征空间中接近,让负样本对远离                    │   │
│  │                                                                     │   │
│  │   正样本对 (Positive Pairs):                                        │   │
│  │   • 匹配的文本-图像对(如"猫"的描述 + 猫的图片)                     │   │
│  │   • 匹配的图像-文本对(如猫的图片 + "猫"的描述)                     │   │
│  │                                                                     │   │
│  │   负样本对 (Negative Pairs):                                        │   │
│  │   • 不匹配的文本-图像对(如"猫"的描述 + 狗的图片)                   │   │
│  │   • 不匹配的图像-文本对(如猫的图片 + "狗"的描述)                   │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  InfoNCE Loss (对比学习常用损失函数):                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                     │   │
│  │   L = -log[exp(sim(I_i, T_i)/τ) / Σexp(sim(I_i, T_j)/τ)]           │   │
│  │                                                                     │   │
│  │   其中:                                                              │   │
│  │   • I_i: 第i个图像的特征向量                                         │   │
│  │   • T_i: 第i个文本的特征向量                                         │   │
│  │   • sim(I, T): 图像和文本的余弦相似度                                │   │
│  │   • τ: 温度参数(控制分布的锐利程度)                                │   │
│  │   • Σ: 对所有负样本求和                                              │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

PyTorch实现:对比学习

import torch
import torch.nn.functional as F


class ContrastiveLoss(nn.Module):
    """对比学习损失函数 (InfoNCE Loss)"""

    def __init__(self, temperature=0.07):
        """
        参数:
            temperature: 温度参数,控制注意力分布的锐利程度
        """
        super().__init__()
        self.temperature = temperature

    def forward(self, image_features, text_features):
        """
        计算对比学习损失

        参数:
            image_features: 图像特征 [batch_size, dim]
            text_features: 文本特征 [batch_size, dim]

        返回:
            loss: 对比学习损失
        """
        # 1. L2归一化
        image_features = F.normalize(image_features, dim=1)
        text_features = F.normalize(text_features, dim=1)

        # 2. 计算相似度矩阵 [batch_size, batch_size]
        logits = torch.matmul(image_features, text_features.t()) / self.temperature

        # 3. 创建标签(对角线位置为正样本)
        labels = torch.arange(image_features.size(0), device=image_features.device)

        # 4. 计算交叉熵损失
        loss = F.cross_entropy(logits, labels)

        # 5. 计算准确率
        with torch.no_grad():
            predictions = logits.argmax(dim=1)
            accuracy = (predictions == labels).float().mean()

        return loss, accuracy.item()


class CLIPModel(nn.Module):
    """简化的CLIP模型实现"""

    def __init__(self, vision_model, text_model, embed_dim=512):
        super().__init__()
        self.vision_model = vision_model
        self.text_model = text_model
        self.vision_projection = nn.Linear(vision_model.embed_dim, embed_dim)
        self.text_projection = nn.Linear(text_model.embed_dim, embed_dim)
        self.temperature = nn.Parameter(torch.ones([]) * math.log(1 / 0.07))

    def forward(self, images, texts):
        """
        前向传播

        参数:
            images: 图像批次 [batch_size, C, H, W]
            texts: 文本批次 [batch_size, seq_len]

        返回:
            image_features: 投影后的图像特征 [batch_size, embed_dim]
            text_features: 投影后的文本特征 [batch_size, embed_dim]
        """
        # 获取图像和文本特征
        image_embeds = self.vision_model(images)
        text_embeds = self.text_model(texts)

        # 投影到统一空间
        image_features = self.vision_projection(image_embeds)
        text_features = self.text_projection(text_embeds)

        # L2归一化
        image_features = F.normalize(image_features, dim=1)
        text_features = F.normalize(text_features, dim=1)

        # 可学习的温度参数
        logits = torch.matmul(image_features, text_features.t()) * self.temperature.exp()

        return logits


# 使用示例
if __name__ == "__main__":
    # 创建模型
    vision_encoder = VisionEncoder(embed_dim=512)
    text_encoder = TextEncoder(embed_dim=512)
    clip_model = CLIPModel(vision_encoder, text_encoder, embed_dim=512)

    # 模拟数据
    batch_size = 4
    images = torch.randn(batch_size, 3, 224, 224)
    texts = torch.randint(0, 1000, (batch_size, 77))

    # 前向传播
    logits = clip_model(images, texts)
    print(f"相似度矩阵形状: {logits.shape}")  # [4, 4]

    # 计算损失
    criterion = ContrastiveLoss(temperature=0.07)
    image_features = F.normalize(clip_model.vision_projection(vision_encoder(images)), dim=1)
    text_features = F.normalize(clip_model.text_projection(text_encoder(texts)), dim=1)
    loss, accuracy = criterion(image_features, text_features)

    print(f"对比学习损失: {loss.item():.4f}")
    print(f"匹配准确率: {accuracy:.2%}")
    print("对比学习模型测试通过!")

代码详解

  • ContrastiveLoss类:实现InfoNCE损失函数,包含相似度计算、标签创建、交叉熵损失计算
  • CLIPModel类:简化的CLIP模型,包含视觉编码器、文本编码器和投影层
  • temperature参数:可学习的温度参数,控制分布的锐利程度

注释:这就是CLIP等模型学习统一特征空间的基本方法。通过大规模的对比学习,模型学会了把语义相似的跨模态数据映射到特征空间中相近的位置。

本节小结

让我们用简洁的总结回顾这一节的核心内容。

特征表示的本质

  • 把复杂的数据(图像、文本、音频)转换成计算机能处理的数值向量
  • 同时保持原始数据的关键语义信息
  • 不同模态有不同的特征提取方法(CNN/ViT提取图像特征,词嵌入提取文本特征)

统一特征空间

  • 让不同模态能够在同一个空间中进行比较和关联
  • 是多模态学习的核心技术
  • 通过对比学习来训练,让匹配的跨模态数据在空间中接近

关键类比

  • 特征表示 = 给数据写简历(保留关键信息,压缩冗余)
  • 统一特征空间 = 共同语言(让不同模态能够"对话")

思考题:如果你想让模型学会理解"声音"和"文字"的对应关系(比如语音识别),你会如何设计统一特征空间的学习方法?


2.4 本章小结与练习

核心概念回顾

让我们用简洁的关键词回顾第二章学到的核心概念:

Transformer架构:2017年提出的革命性序列建模架构,基于注意力机制而非循环结构。它由编码器和解码器组成,包含自注意力、多头注意力、前馈网络、位置编码等核心组件。

自注意力机制:Transformer的核心创新,允许序列中每个位置关注所有其他位置,根据相关性分配权重。核心计算是Q(查询)与K(键)的相似度匹配,然后用权重对V(值)加权求和。

多头注意力:同时进行多组注意力计算,每组使用不同的Q、K、V投影。允许模型学习多种不同类型的关联模式。

位置编码:给序列元素加上位置信息的编码,解决Transformer无法感知顺序的问题。论文使用正弦-余弦函数生成位置编码。

交叉注意力:跨模态的注意力机制,Q来自一个模态,K和V来自另一个模态。是多模态融合的关键技术。

特征表示学习:把复杂数据(图像、文本、音频)转换成数值向量的过程。不同模态有不同的特征提取方法(CNN/ViT、词嵌入、频谱特征)。

统一特征空间:让不同模态的数据能够映射到同一个空间中进行比较和关联。是多模态学习的核心技术基础。

对比学习:通过拉近正样本对(匹配的跨模态数据)、推远负样本对(不匹配的跨模态数据),学习统一特征空间的训练方法。

知识关系图

核心技术基础
    │
    ├── Transformer架构
    │   │
    │   ├── 编码器-解码器结构
    │   ├── 自注意力机制(核心创新)
    │   │       │
    │   │       ├── Q(查询)= 我在找什么
    │   │       ├── K(键)= 我有什么
    │   │       └── V(值)= 实际内容
    │   │
    │   ├── 多头注意力(多组Q,K,V)
    │   ├── 位置编码(序列顺序)
    │   └── 前馈网络(非线性变换)
    │
    ├── 注意力机制详解
    │   │
    │   ├── 相似度计算(Q·K)
    │   ├── Softmax归一化
    │   ├── 加权求和输出
    │   └── 跨模态注意力(Cross-Attention)
    │
    └── 特征表示学习
        │
        ├── 文本特征(词嵌入)
        ├── 图像特征(CNN/ViT)
        ├── 音频特征(频谱特征)
        └── 统一特征空间(对比学习)

实践任务

任务一:手动计算注意力分数

假设我们有3个词的嵌入向量(简化示例,维度为2),手动计算"喜欢"这个词对其他词的注意力分数。

词嵌入矩阵:

  • "我":[1, 0]
  • "喜欢":[0, 1]
  • "学习":[1, 1]

简化假设Q、K、V都直接使用词嵌入(实际应用中会有不同的线性变换)。

计算"喜欢"(Q=[0,1])对"我"(K=[1,0])的注意力分数:

  1. 点积:0×1 + 1×0 = 0

计算"喜欢"对"喜欢"的注意力分数:

  1. 点积:0×0 + 1×1 = 1

计算"喜欢"对"学习"的注意力分数:

  1. 点积:0×1 + 1×1 = 1

请继续完成Softmax归一化,计算最终权重。

任务二:调研ViT架构

Vision Transformer(ViT)是把Transformer应用于图像的里程碑工作。请查阅ViT论文或相关资料,回答以下问题:

  1. ViT如何处理图像数据?(图像分块 + 位置嵌入)
  2. ViT和标准Transformer在结构上有什么异同?
  3. ViT为什么需要更多的训练数据才能达到较好的效果?

思考题参考答案提示

2.1节思考题:如果让你设计一个能同时处理文本和图像的模型,你会如何使用这些组件?

参考思路

  • 使用双编码器:文本编码器(基于Transformer编码器)和图像编码器(基于ViT)
  • 两个编码器分别处理各自的模态,输出特征向量
  • 使用交叉注意力层让两种模态交互
  • 在统一特征空间中进行对比学习或目标任务训练

2.2节思考题:如果让你设计一个三模态(文本、图像、音频)模型,你会如何使用注意力机制实现两两交互?

参考思路

  • 可以使用两级交叉注意力
  • 第一级:两两模态之间的交叉注意力(文本-图像、文本-音频、图像-音频)
  • 第二级:在跨模态表示上的自注意力或进一步的交叉注意力
  • 或者使用图注意力网络,把三种模态看作图中的节点

2.3节思考题:如果你想让模型学会理解"声音"和"文字"的对应关系(比如语音识别),你会如何设计统一特征空间的学习方法?

参考思路

  • 使用双编码器:音频编码器(处理频谱特征)和文本编码器(处理词嵌入)
  • 训练数据是成对的(语音音频,对应的文字转录)
  • 使用对比学习,让匹配的语音-文本对在特征空间中接近
  • 负样本是不匹配的语音-文本对

预告:下一章

在第二章中,我们深入学习了多模态大模型的核心技术基础——Transformer架构、注意力机制和特征表示学习。这些知识是理解后续章节(如CLIP、GPT-4V等模型架构)的前提。

第三章预告:主流模型架构解析

  • CLIP:连接视觉与语言的里程碑
  • GPT-4V:视觉理解的新高度
  • Gemini:原生多模态架构
  • 三大模型的架构对比与演进

从第三章开始,我们将具体分析几个代表性的多模态大模型,理解它们的架构设计、训练方法和能力特点。这些模型包括:让图文统一的CLIP、增加视觉能力的GPT-4V,以及原生多模态的Gemini。


本章作者:智柴网(zhichai.net) 发布日期:2026年1月 版权声明:© 2026 智柴网 版权所有

← 返回目录