1. 引言

在这里插入图片描述

今天是学习视觉-语言-动作模型(Vision-Language-Action,VLA)的第 4 天。前三天分别了解了 VLA 的整体架构等,今天终于进入 VLA 中最核心的组件——Transformer。

Transformer 是当前几乎所有大模型的基础架构,VLA 模型同样离不开它。无论是处理视觉 token、语言 token,还是生成动作 token,Transformer 的自注意力机制都扮演着关键角色。今天的目标是:深入理解 Transformer 的核心原理,并动手复现一个可运行的简化版本

为什么 Transformer 如此重要?因为它解决了传统循环神经网络(RNN)的两个致命问题:一是无法并行计算,训练速度慢;二是难以捕捉长距离依赖关系,容易遗忘早期信息。Transformer 通过自注意力机制,让序列中的任意两个位置都能直接交互,既实现了并行计算,又完美解决了长距离依赖问题。这也是它能够成为大模型基石的根本原因。
在这里插入图片描述

2. Transformer 为什么对 VLA 如此重要

在 VLA 模型中,Transformer 承担着三个关键职责。这三个职责分别对应了 VLA 模型处理不同模态信息的能力,也是理解 VLA 工作原理的核心线索:

  • 跨模态融合:将视觉特征和语言特征在统一的 token 空间中进行交互,让模型理解"看到什么"与"指令说什么"之间的关系。
  • 序列建模:处理变长的输入序列(图像 patch 序列、文本 token 序列),捕捉长距离依赖关系。
  • 动作解码:自回归地生成动作 token 序列,将高层语义决策转化为具体的动作指令。

可以说,没有 Transformer,就没有今天的 VLA 模型。理解 Transformer 是复现和理解 VLA 的必经之路。

以 RT-2 为例,它本质上就是一个将视觉-语言模型(VLM)与机器人动作空间结合起来的 Transformer 模型。RT-2 将机器人动作表示为文本 token,从而可以直接利用互联网规模的图文数据进行预训练,再在机器人数据上进行微调。这种设计思路正是建立在 Transformer 强大的序列建模能力之上的。理解了 Transformer,你就能看懂 RT-2、OpenVLA 等主流 VLA 模型的内部机制。

下面是 Transformer 在 VLA 中承担的三个关键职责的示意图:

VLA 模型

跨模态融合

序列建模

动作解码

视觉特征 + 语言特征
统一 token 空间交互

图像 patch 序列
文本 token 序列
长距离依赖

自回归生成
动作 token 序列

3. Transformer 核心原理

3.1 整体架构

Transformer 采用编码器-解码器(Encoder-Decoder)结构,但在 VLA 中,我们更常使用编码器部分(如 ViT)或仅使用解码器部分(如 GPT 风格的自回归模型)。其核心组件包括:

原始 Transformer 论文(“Attention Is All You Need”)提出的架构包含两个部分:编码器负责将输入序列编码为上下文相关的特征表示,解码器则根据这些特征自回归地生成输出序列。在 VLA 模型中,这种结构被灵活运用:视觉编码器(如 ViT)使用 Transformer 编码器提取图像特征,而动作生成部分则常使用类似 GPT 的解码器结构,逐步预测动作 token。

无论使用哪种结构,Transformer 的核心组件都是相同的,它们共同构成了一个强大的特征提取和序列建模工具:

  • 自注意力机制(Self-Attention):让序列中的每个 token 都能关注到其他所有 token。
  • 多头注意力(Multi-Head Attention):将注意力拆分成多个子空间,捕捉不同类型的依赖关系。
  • 前馈网络(Feed-Forward Network):对每个 token 独立进行非线性变换。
  • 位置编码(Positional Encoding):为模型注入序列顺序信息。
  • 残差连接与层归一化(Residual Connection & LayerNorm):稳定训练,加速收敛。

3.2 自注意力机制

在这里插入图片描述

自注意力的核心思想是:对于输入序列中的每个 token,计算它与序列中所有 token 的相关性(注意力权重),然后加权聚合所有 token 的信息。

具体计算过程如下:

  1. 将输入 ( X ) 分别乘以三个权重矩阵 ( W_Q )、( W_K )、( W_V ),得到查询(Query)、键(Key)、值(Value)矩阵。
  2. 计算注意力分数:Attention(Q,K,V)=softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)VAttention(Q,K,V)=softmax(dk QKT)V
  3. 除以 ( \sqrt{d_k} ) 是为了防止点积结果过大导致 softmax 梯度消失。

下面是自注意力机制的计算流程示意图:

输入 X

W_Q 线性变换

W_K 线性变换

W_V 线性变换

Query 矩阵 Q

Key 矩阵 K

Value 矩阵 V

Q × K^T

除以 √d_k

Softmax 归一化

注意力权重

加权求和

输出

3.3 多头注意力

多头注意力将 Q、K、V 分别拆分成 ( h ) 个头,每个头独立计算注意力,最后拼接并经过一个线性变换。这样做的好处是:

  • 不同的头可以关注不同的关系模式(如语法关系、语义关系、位置关系)。
  • 在不增加计算量的情况下,增强了模型的表达能力。

下面是多头注意力的拆分与拼接流程:

Q、K、V 矩阵

拆分为 h 个头

头 1 注意力

头 2 注意力

头 h 注意力

拼接 Concat

线性变换 W_O

多头注意力输出

3.4 位置编码

由于自注意力机制本身不具备顺序感知能力,需要显式地注入位置信息。常用的方法有两种:

  • 正弦位置编码:使用不同频率的正弦和余弦函数生成位置向量。
  • 可学习位置编码:将位置编码作为可学习的参数,随模型一起训练。

下面是 Transformer 编码器的整体架构图,直观展示各组件之间的数据流向:

编码器层 × N

多头自注意力 Multi-Head Attention

残差连接 + LayerNorm

前馈网络 Feed-Forward

残差连接 + LayerNorm

输入 Token 序列

Embedding 嵌入层

位置编码 Positional Encoding

Dropout

输出特征序列

4. 从零复现 Transformer

下面用 PyTorch 从零实现一个简化但完整的 Transformer 编码器。这个实现将作为后续复现 VLA 模型的基础模块。

4.1 环境准备

pip install torch numpy

4.2 多头注意力实现

import torch
import torch.nn as nn
import math

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_heads, dropout=0.1):
        super().__init__()
        assert d_model % n_heads == 0, "d_model 必须能被 n_heads 整除"
        
        self.d_model = d_model
        self.n_heads = n_heads
        self.d_k = d_model // n_heads
        
        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)
    
    def forward(self, x, mask=None):
        batch_size, seq_len, _ = x.size()
        
        # 线性变换并拆分为多头
        Q = self.w_q(x).view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2)
        K = self.w_k(x).view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2)
        V = self.w_v(x).view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2)
        
        # 计算注意力分数
        scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
        
        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)
        
        attn = torch.softmax(scores, dim=-1)
        attn = self.dropout(attn)
        
        # 加权聚合
        context = torch.matmul(attn, V)
        context = context.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)
        
        return self.w_o(context)

4.3 前馈网络与编码器层

class FeedForward(nn.Module):
    def __init__(self, d_model, d_ff, dropout=0.1):
        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):
        return self.linear2(self.dropout(torch.relu(self.linear1(x))))

class EncoderLayer(nn.Module):
    def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
        super().__init__()
        self.self_attn = MultiHeadAttention(d_model, n_heads, dropout)
        self.feed_forward = FeedForward(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):
        # 自注意力子层(残差连接 + LayerNorm)
        attn_output = self.self_attn(x, mask)
        x = self.norm1(x + self.dropout1(attn_output))
        
        # 前馈子层(残差连接 + LayerNorm)
        ff_output = self.feed_forward(x)
        x = self.norm2(x + self.dropout2(ff_output))
        
        return x

4.4 位置编码与完整编码器

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=512):
        super().__init__()
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len).unsqueeze(1).float()
        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)
        self.register_buffer('pe', pe)
    
    def forward(self, x):
        return x + self.pe[:, :x.size(1)]

class TransformerEncoder(nn.Module):
    def __init__(self, vocab_size, d_model, n_heads, d_ff, n_layers, max_len, dropout=0.1):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, d_model)
        self.pos_encoding = PositionalEncoding(d_model, max_len)
        self.layers = nn.ModuleList([
            EncoderLayer(d_model, n_heads, d_ff, dropout) for _ in range(n_layers)
        ])
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x, mask=None):
        x = self.dropout(self.pos_encoding(self.embedding(x)))
        for layer in self.layers:
            x = layer(x, mask)
        return x

4.5 快速验证

# 参数设置
vocab_size = 1000
d_model = 128
n_heads = 8
d_ff = 512
n_layers = 4
max_len = 64
batch_size = 2
seq_len = 32

# 初始化模型
model = TransformerEncoder(vocab_size, d_model, n_heads, d_ff, n_layers, max_len)

# 随机输入
input_ids = torch.randint(0, vocab_size, (batch_size, seq_len))

# 前向传播
output = model(input_ids)
print(f"输出形状: {output.shape}")  # 期望: torch.Size([2, 32, 128])

运行上述代码,如果输出形状为 torch.Size([2, 32, 128]),说明我们的 Transformer 编码器已经可以正常工作了。

5. 从 Transformer 到 VLA 的桥梁

复现完 Transformer 编码器后,来看看它是如何融入 VLA 模型的。一个典型的 VLA 模型(如 RT-2、OpenVLA)通常包含以下流程:

图像输入

视觉编码器 ViT

语言指令

文本编码器

视觉 Token

语言 Token

Transformer 融合

动作 Token 解码

机器人动作输出

关键点在于:

  • 视觉 Token 化:将图像切分为 patch,通过 ViT 编码为视觉 token 序列。
  • Token 拼接:将视觉 token 和语言 token 拼接成统一序列,送入 Transformer。
  • 动作生成:在序列末尾添加动作 token,通过自回归方式逐步生成动作。

6. 今日总结与下一步计划

今天从零复现了 Transformer 编码器,包括多头注意力、前馈网络、位置编码和残差连接等核心组件。通过动手实现,对自注意力机制有了更直观的理解。

今日收获:

  • 理解了自注意力的计算流程和缩放点积注意力的原理。
  • 掌握了多头注意力的拆分与拼接逻辑。
  • 能够独立实现一个可运行的 Transformer 编码器。
  • 明白了 Transformer 在 VLA 中承担跨模态融合和动作解码的关键角色。

7. 参考资料

  • Vaswani et al., “Attention Is All You Need”, 2017
  • Dosovitskiy et al., “An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale”, 2020
  • Brohan et al., “RT-2: Vision-Language-Action Models Transfer Web Knowledge to Robotic Control”, 2023
  • Kim et al., “OpenVLA: An Open-Source Vision-Language-Action Model”, 2024
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐