Pi0 (Physical Intelligence Zero) 模型架构

1. 整体架构概览

Pi0 是 Physical Intelligence 提出的视觉-语言-动作 (VLA) 模型,采用 PaliGemma (SigLIP + Gemma 2B) 作为视觉语言主干网络,配合独立的 Gemma 300M Action Expert 进行动作生成。其核心创新在于 双流 Transformer 注意力机制——VLM 前缀序列与动作专家后缀序列共享同一组 Transformer 层,通过精心设计的混合注意力掩码实现信息的单向流动;动作生成采�� Flow Matching 而非标准扩散模型,通过欧拉积分将高斯噪声迭代映射为动作轨迹。

graph TB subgraph Input["输入"] IMG["相机图像
(多视角支持)
[B, C, 224, 224]"] TXT["语言指令
(Tokenized)
[B, max_len=48]"] STATE["机器人状态
[B, state_dim]
(填充至 max_state_dim=32)"] end subgraph VLM["PaliGemma 视觉语言主干 (Prefix Stream)"] SIGLIP["SigLIP 视觉编码器"] PROJ_V["视觉投影层"] EMB_L["Gemma 2B 词元嵌入
embed_dim * sqrt(dim)"] MERGE["拼接: 图像嵌入 + 语言嵌入
(双向注意力)"] end subgraph Expert["Gemma 300M Action Expert (Suffix Stream)"] S_PROJ["状态投影
Linear(32 → 1024)"] A_PROJ["动作投影
Linear(32 → 1024)"] T_EMB["时间步编码
正弦位置编码"] FUSE["动作-时间融合 MLP
Concat → SiLU → Linear"] end subgraph DualStream["双流 Transformer (18层共享)"] DS["Prefix (VLM, 2048维) ‖ Suffix (Expert, 1024维)
共享注意力层 + 混合注意力掩码
RoPE 位置编码"] end subgraph FlowMatch["Flow Matching 输出"] OUT_PROJ["动作输出投影
Linear(1024 → 32)"] EULER["欧拉积分去噪
(10步迭代)"] end subgraph Output["输出"] ACT["预测动作轨迹
[B, chunk_size=50, action_dim]"] end IMG --> SIGLIP --> PROJ_V --> MERGE TXT --> EMB_L --> MERGE MERGE -->|"前缀嵌入
[B, prefix_len, 2048]"| DS STATE --> S_PROJ S_PROJ --> FUSE A_PROJ --> FUSE T_EMB --> FUSE FUSE -->|"后缀嵌入
[B, 1+chunk_size, 1024]"| DS DS -->|"后缀输出"| OUT_PROJ OUT_PROJ --> EULER --> ACT style Input fill:#e8f4fd,stroke:#2196F3 style VLM fill:#fff3e0,stroke:#FF9800 style Expert fill:#f3e5f5,stroke:#9C27B0 style DualStream fill:#e8f5e9,stroke:#4CAF50 style FlowMatch fill:#fff9c4,stroke:#FFC107 style Output fill:#fce4ec,stroke:#E91E63

2. 核心组件详解

2.1 PaliGemma 视觉语言模型 (SigLIP + Gemma 2B)

PaliGemma 是 Google 提出的视觉语言模型,由 SigLIP 视觉编码器Gemma 2B 语言模型 组成。在 Pi0 中,PaliGemma 的作用是将图像和语言指令编码为统一的视觉语言特征序列,作为双流 Transformer 的前缀 (Prefix) 输入。

graph LR subgraph SigLIP["SigLIP 视觉编码器 (vision_tower)"] PIX["pixel_values
[B, C, 224, 224]
归一化至 [-1, 1]"] --> PATCH["Patch 嵌入
+ 位置嵌入"] PATCH --> VIT["Vision Transformer
多层 Self-Attention"] VIT --> VFEAT["图像特征
[B, num_patches, vision_dim]"] end subgraph VisionProj["视觉投影 (multi_modal_projector)"] VFEAT --> MPROJ["线性投影
vision_dim → 2048"] end subgraph GemmaLM["Gemma 2B 语言模型 (language_model)"] TK["语言 Token
[B, seq_len]"] --> EMBT["词元嵌入
embed_tokens"] EMBT --> SCALE["缩放: emb * sqrt(dim)"] MPROJ -->|"图像嵌入"| CONCAT["拼接至序列"] SCALE -->|"语言嵌入"| CONCAT CONCAT --> GEMMA["Gemma 2B
18层 Transformer
hidden_size=2048
heads=8, kv_heads=1
head_dim=256
mlp_dim=16384"] end GEMMA --> PFEAT["前缀特征
[B, prefix_len, 2048]"] style SigLIP fill:#e3f2fd,stroke:#2196F3 style VisionProj fill:#f3e5f5,stroke:#9C27B0 style GemmaLM fill:#e8f5e9,stroke:#4CAF50

关键细节:


2.2 Action Expert (Gemma 300M)

Action Expert 是一个独立的 Gemma 300M 模型,负责处理机器人状态、含噪动作和时间步信息。它与 VLM 共享 Transformer 层的注意力计算,但拥有独立的参数(自己的 Q/K/V 投影、MLP 等)。

graph TB subgraph StateEnc["状态编码"] ST["机器人状态
[B, state_dim]"] --> PAD_S["零填充至 max_state_dim=32"] PAD_S --> SP["state_proj
Linear(32 → 1024)"] SP --> SE["状态嵌入
[B, 1, 1024]"] end subgraph TimeEnc["时间步编码"] T["时间步 t
[B]"] --> SIN["正弦-余弦位置编码
create_sinusoidal_pos_embedding
dim=1024
period: [4e-3, 4.0]"] SIN --> TE["时间嵌入
[B, 1024]
→ 扩展至 [B, chunk_size, 1024]"] end subgraph ActionEnc["动作编码 + 时间融合"] NA["含噪动作 x_t
[B, chunk_size, 32]"] --> AP["action_in_proj
Linear(32 → 1024)"] AP --> AE["动作嵌入
[B, chunk_size, 1024]"] AE --> CAT["Concat(动作嵌入, 时间嵌入)
[B, chunk_size, 2048]"] TE --> CAT CAT --> MLP_IN["action_time_mlp_in
Linear(2048 → 1024)"] MLP_IN --> SILU["SiLU 激活"] SILU --> MLP_OUT["action_time_mlp_out
Linear(1024 → 1024)"] MLP_OUT --> ATE["融合嵌入
[B, chunk_size, 1024]"] end subgraph SuffixBuild["后缀序列构建"] SE --> SCAT["Concat(状态, 融合嵌入)
[B, 1+chunk_size, 1024]"] ATE --> SCAT end subgraph ExpertModel["Gemma 300M 参数"] SPEC["hidden_size=1024
depth=18层
mlp_dim=4096
heads=8, kv_heads=1
head_dim=256
无 embed_tokens (设为 None)"] end SCAT --> SUFFIX_OUT["后缀嵌入 → 双流 Transformer"] style StateEnc fill:#e3f2fd,stroke:#2196F3 style TimeEnc fill:#fff3e0,stroke:#FF9800 style ActionEnc fill:#f3e5f5,stroke:#9C27B0 style SuffixBuild fill:#e8f5e9,stroke:#4CAF50 style ExpertModel fill:#fff9c4,stroke:#FFC107

关键细节:


2.3 双流 Transformer 注意力机制

Pi0 的核心创新是双流 Transformer:VLM 前缀序列 (Prefix)Action Expert 后缀序列 (Suffix) 共享同一组 18 层 Transformer,但通过混合注意力掩码实现差异化的信息流动。

graph TB subgraph DualStreamLayer["单层双流 Transformer 计算"] subgraph PrefixPath["Prefix 路径 (VLM, 2048维)"] P_IN["prefix_embeds
[B, prefix_len, 2048]"] P_LN["input_layernorm"] P_QKV["VLM 的 Q/K/V 投影
(Gemma 2B 参数)"] end subgraph SuffixPath["Suffix 路径 (Expert, 1024维)"] S_IN["suffix_embeds
[B, suffix_len, 1024]"] S_LN["input_layernorm"] S_QKV["Expert 的 Q/K/V 投影
(Gemma 300M 参数)"] end subgraph SharedAttn["共享注意力计算"] CATQ["Concat Q: [prefix_Q | suffix_Q]"] CATK["Concat K: [prefix_K | suffix_K]"] CATV["Concat V: [prefix_V | suffix_V]"] ROPE["RoPE 旋转位置编码
(共享 cos/sin)"] ATTN["联合注意力计算
+ 混合注意力掩码"] end subgraph SplitOut["分割输出"] SPLIT["按位置分割注意力输出"] P_OUT["prefix: o_proj (VLM)
→ 残差 → MLP → 残差"] S_OUT["suffix: o_proj (Expert)
→ 残差 → MLP → 残差"] end end P_IN --> P_LN --> P_QKV --> CATQ & CATK & CATV S_IN --> S_LN --> S_QKV --> CATQ & CATK & CATV CATQ & CATK & CATV --> ROPE --> ATTN ATTN --> SPLIT --> P_OUT & S_OUT style PrefixPath fill:#fff3e0,stroke:#FF9800 style SuffixPath fill:#f3e5f5,stroke:#9C27B0 style SharedAttn fill:#e8f5e9,stroke:#4CAF50 style SplitOut fill:#e3f2fd,stroke:#2196F3

混合注意力掩码

注意力掩码通过 att_masks 整数向量和 make_att_2d_masks 函数构建,实现以下规则:

掩码规则:

注意力矩阵示意:

Q \ K img lang state act_0 act_1 ...
img
lang
state
act_0
act_1
...

实现方式:


2.4 Flow Matching 扩散机制

Pi0 采用 Flow Matching(流匹配)而非标准扩散模型来生成动作。

直觉理解:从噪声到动作的"导航"

想象你站在一片浓雾(随机噪声)中,你的目标是走到一个精确的目的地(真实的机器人动作)。Flow Matching 要做的事情就是:训练一个"导航仪",告诉你在雾中的每一个位置应该朝哪个方向、以多快的速度走,才能最终到达目的地。

具体来说,分三步理解:

第一步——定义一条"直线公路"(线性插值路径)

在噪声和真实动作之间,画一条最简单的直线路径。用时间 $t$ 来标记你在这条路上的位置:

$$x_t = t \times \text{noise} + (1 - t) \times \text{actions}$$

这条路径是人为规定的,不需要学习——它只是告诉我们"从噪声到动作,中间状态长什么样"。

第二步——计算"速度场"(训练目标)

既然路径是一条直线,那沿着这条路走的"速度"(方向 + 快慢)在任何位置都是恒定的:

$$u_t = \frac{d\,x_t}{d\,t} = \text{noise} - \text{actions}$$

这就是速度场——它的含义是"从动作指向噪声的方向"。反过来说,如果我们沿 $-u_t$ 方向走,就是从噪声走向动作。训练时,我们让模型去学习预测这个速度场 $u_t$。

第三步——推理时"逆向行走"(欧拉积分)

推理时,我们从纯噪声 $x_1$ 出发,用模型预测的速度场一步一步走回 $x_0$(干净动作):

$$x_{t - \Delta t} = x_t + \Delta t \times v_t \quad (\Delta t = -0.1, \text{共走 10 步})$$

其中 $v_t$ 是模型对速度场的预测。每走一步,动作就变得更"清晰"一点,10 步之后就从一团噪声变成了合理的动作轨迹。

类比总结: 标准扩散模型像是在迷宫里摸索(随机微分方程,路径弯曲复杂,需要几百步),而 Flow Matching 像是在笔直的公路上开车(常微分方程,路径简单直接,10 步就到)。公路是人为修好的(线性插值),模型只需要学会"沿着公路开"(预测速度场)。

graph TB subgraph FlowMatchTheory["Flow Matching 理论"] direction TB THEORY["线性路径定义:
x_t = t * noise + (1 - t) * actions

速度场目标:
u_t = noise - actions

t = 0 时: x_0 = actions (干净动作)
t = 1 时: x_1 = noise (纯噪声)

模型学习预测 u_t, 使得:
v_t ≈ u_t = d(x_t)/dt"] end subgraph TimeSampling["训练时间步采样"] BETA["t ~ Beta(alpha=1.5, beta=1.0)
偏向大 t 值 (靠近噪声端)"] SCALE["缩放: t = t * 0.999 + 0.001
避免 t=0 和 t=1 的极端值"] end subgraph NoiseProcess["加噪过程"] ACT_GT["真实动作 actions
[B, 50, 32]"] NOISE["高斯噪声 noise
~ N(0, 1)
[B, 50, 32]"] NOISY["含噪动作 x_t
= t * noise + (1-t) * actions"] TARGET["速度目标 u_t
= noise - actions"] end BETA --> SCALE SCALE -->|"t"| NOISY ACT_GT --> NOISY NOISE --> NOISY ACT_GT --> TARGET NOISE --> TARGET subgraph LossComp["损失计算"] PRED["模型预测 v_t
(双流 Transformer 输出)"] LOSS["Loss = MSE(u_t, v_t)
对所有维度取均值"] end TARGET --> LOSS PRED --> LOSS style FlowMatchTheory fill:#e3f2fd,stroke:#2196F3 style TimeSampling fill:#fff3e0,stroke:#FF9800 style NoiseProcess fill:#f3e5f5,stroke:#9C27B0 style LossComp fill:#fce4ec,stroke:#E91E63

与标准扩散模型的区别:

特性 标准扩散 (DDPM) Flow Matching (Pi0)
噪声调度 固定余弦/线性调度 线性插值路径
预测目标 预测噪声 epsilon 预测速度场 u_t
推理步数 通常 50-1000 步 仅 10 步
时间步采样 均匀采样 Beta(1.5, 1.0) 分布
数学基础 随机微分方程 (SDE) 常微分方程 (ODE)

3. 训练流水线

训练时,模型接收图像、语言、状态和真实动作,通过 Flow Matching 加噪后预测速度场,以 MSE 损失进行优化。

graph TB subgraph DataPrep["数据准备"] IMG_RAW["原始图像
[B, C, H, W]"] --> IMG_PROC["预处理
resize_with_pad → 224x224
归一化至 [-1, 1]"] LANG_RAW["语言指令"] --> LANG_PROC["Tokenize
max_length=48"] STATE_RAW["状态向量"] --> STATE_PROC["零填充至
max_state_dim=32
MEAN_STD 归一化"] ACT_RAW["动作轨迹"] --> ACT_PROC["零填充至
max_action_dim=32
MEAN_STD 归一化"] end subgraph NoiseSample["Flow Matching 采样"] ACT_PROC --> FLOW NOISE_S["noise ~ N(0,1)
[B, 50, 32]"] --> FLOW TIME_S["t ~ Beta(1.5, 1.0)
缩放至 [0.001, 1.0]"] --> FLOW FLOW["x_t = t * noise + (1-t) * actions
u_t = noise - actions"] end subgraph Embedding["嵌入构建"] IMG_PROC --> EMBED_P["embed_prefix()
SigLIP 编码图像
+ 嵌入语言 token"] LANG_PROC --> EMBED_P EMBED_P --> PREFIX["前缀嵌入 + pad_mask + att_mask"] STATE_PROC --> EMBED_S["embed_suffix()
状态投影 + 动作时间融合"] FLOW -->|"x_t, t"| EMBED_S EMBED_S --> SUFFIX["后缀嵌入 + pad_mask + att_mask"] end subgraph MaskBuild["注意力掩码构建"] PREFIX -->|"pad_mask, att_mask"| MASK["make_att_2d_masks()
→ 4D 注意力掩码"] SUFFIX -->|"pad_mask, att_mask"| MASK end subgraph Forward["双流前向传播"] PREFIX -->|"prefix_embeds"| DUAL["PaliGemmaWithExpertModel.forward()
18层共享注意力计算"] SUFFIX -->|"suffix_embeds"| DUAL MASK --> DUAL DUAL -->|"suffix_out[:, -chunk_size:]"| OUT_P["action_out_proj
Linear(1024 → 32)"] OUT_P --> V_T["预测速度 v_t"] end subgraph LossCalc["损失计算"] FLOW -->|"u_t"| MSE["MSE Loss
= mean((u_t - v_t)^2)
截断至 original_action_dim"] V_T --> MSE MSE --> BACKWARD["反向传播"] end style DataPrep fill:#e8f4fd,stroke:#2196F3 style NoiseSample fill:#fff3e0,stroke:#FF9800 style Embedding fill:#f3e5f5,stroke:#9C27B0 style MaskBuild fill:#e0f7fa,stroke:#00BCD4 style Forward fill:#e8f5e9,stroke:#4CAF50 style LossCalc fill:#fce4ec,stroke:#E91E63

4. 推理流水线

推理时,模型采用 KV Cache 优化:先对前缀(图像+语言)做一次前向传播并缓存 Key/Value,后续 10 步去噪仅需处理后缀(状态+动作),大幅减少计算量。

graph TB subgraph PrefixCache["第一阶段: 前缀编码 (仅执行一次)"] IMG_I["图像"] --> EP["embed_prefix()"] LANG_I["语言指令"] --> EP EP --> PFX["前缀嵌入"] PFX --> VLM_FWD["PaliGemma 前向传播
inputs_embeds=[prefix, None]
use_cache=True"] VLM_FWD --> KVC["past_key_values
(KV Cache)"] end subgraph Init["初始化"] NOISE_I["x_0 = noise ~ N(0,1)
[B, 50, 32]"] DT["dt = -1/10 = -0.1
从 t=1.0 → t=0.0"] end subgraph DenoiseLoop["第二阶段: 迭代去噪 (10步欧拉积分)"] direction TB STEP["对于 step = 0, 1, ..., 9:
t = 1.0 + step * dt"] subgraph SingleStep["单步去噪 (denoise_step)"] X_T["当前 x_t"] --> ES["embed_suffix(state, x_t, t)
构建后缀嵌入"] ES --> ATT_BUILD["构建注意力掩码
suffix → prefix (KV Cache) 可见
suffix 内部因果"] ATT_BUILD --> EXP_FWD["Expert 前向传播
inputs_embeds=[None, suffix]
past_key_values=KV Cache"] EXP_FWD --> SOUT["suffix_out[:, -chunk_size:]"] SOUT --> AOUT["action_out_proj → v_t"] end AOUT --> EULER["欧拉更新:
x_{t+dt} = x_t + dt * v_t"] EULER -->|"循环"| X_T end subgraph FinalOut["输出"] EULER --> FINAL["最终动作轨迹
x_final = x_0 (去噪后)
[B, 50, action_dim]
截断至 original_action_dim"] end KVC --> EXP_FWD NOISE_I --> X_T style PrefixCache fill:#e3f2fd,stroke:#2196F3 style Init fill:#fff3e0,stroke:#FF9800 style DenoiseLoop fill:#e8f5e9,stroke:#4CAF50 style SingleStep fill:#e0f2f1,stroke:#009688 style FinalOut fill:#fce4ec,stroke:#E91E63

欧拉积分细节 -- 单步展开

graph LR XT["x_t
(当前含噪动作)"] --> EMBED["embed_suffix()
状态 + 动作时间融合"] STATE_I["state"] --> EMBED T_I["timestep t"] --> EMBED EMBED --> EXPERT["Gemma 300M Expert
(使用 KV Cache)"] KV["prefix KV Cache"] --> EXPERT EXPERT --> VT["预测速度 v_t"] XT --> EULER_S["x_{t+dt} = x_t + dt * v_t
dt = -0.1"] VT --> EULER_S EULER_S --> XT1["x_{t+dt}"] style EXPERT fill:#e8f5e9,stroke:#4CAF50 style EULER_S fill:#fff9c4,stroke:#FFC107

推理优化要点:


5. 关键超参数表

模型结构参数

参数 说明
paligemma_variant gemma_2b VLM 主干变体
action_expert_variant gemma_300m 动作专家变体
VLM hidden_size 2048 Gemma 2B 隐藏维度
VLM depth 18 Gemma 2B Transformer 层数
VLM mlp_dim 16384 Gemma 2B MLP 中间维度
VLM num_heads / kv_heads 8 / 1 Gemma 2B 注意力头数 (GQA)
VLM head_dim 256 每个注意力头的维度
Expert hidden_size 1024 Gemma 300M 隐藏维度
Expert depth 18 Gemma 300M Transformer 层数
Expert mlp_dim 4096 Gemma 300M MLP 中间维度
Expert num_heads / kv_heads 8 / 1 Gemma 300M 注意力头数 (GQA)
Expert head_dim 256 每个注意力头的维度
vocab_size 257152 词表大小
image_resolution (224, 224) 输入图像分辨率
hidden_activation gelu_pytorch_tanh 激活函数

动作生成参数

参数 说明
chunk_size 50 预测的动作步数 (action_horizon)
n_action_steps 50 执行的动作步数
max_state_dim 32 状态向量最大维度 (零填充)
max_action_dim 32 动作向量最大维度 (零填充)

Flow Matching 参数

参数 说明
num_inference_steps 10 推理去噪步数
time_sampling_beta_alpha 1.5 Beta 分布 alpha 参数
time_sampling_beta_beta 1.0 Beta 分布 beta 参数
time_sampling_scale 0.999 时间步缩放因子
time_sampling_offset 0.001 时间步偏移量
min_period 4e-3 正弦编码最小周期
max_period 4.0 正弦编码最大周期

训练参数

参数 说明
optimizer_lr 2.5e-5 峰值学习率
optimizer_betas (0.9, 0.95) AdamW beta 参数
optimizer_weight_decay 0.01 权重衰减
optimizer_grad_clip_norm 1.0 梯度裁剪范数
scheduler_warmup_steps 1000 学习率预热步数
scheduler_decay_steps 30000 余弦衰减总步数
scheduler_decay_lr 2.5e-6 最终学习率
tokenizer_max_length 48 语言 token 最大长度
dtype float32 / bfloat16 训练精度
freeze_vision_encoder False 是否冻结视觉编码器
train_expert_only False 是否仅训练 Expert
gradient_checkpointing False 梯度检查点 (省显存)

6. 关键源文件表

组件 类名 文件路径
策略入口 PI0Policy lerobot/policies/pi0/modeling_pi0.py:926
核心模型 PI0Pytorch lerobot/policies/pi0/modeling_pi0.py:536
双流模型 PaliGemmaWithExpertModel lerobot/policies/pi0/modeling_pi0.py:330
模型配置 PI0Config lerobot/policies/pi0/configuration_pi0.py:31
Gemma 配置 GemmaConfig / get_gemma_config lerobot/policies/pi0/modeling_pi0.py:294
注意力掩码 make_att_2d_masks lerobot/policies/pi0/modeling_pi0.py:103
层计算 compute_layer_complete lerobot/policies/pi0/modeling_pi0.py:221
正弦位置编码 create_sinusoidal_pos_embedding lerobot/policies/pi0/modeling_pi0.py:75
Beta 采样 sample_beta lerobot/policies/pi0/modeling_pi0.py:95
图像处理 resize_with_pad_torch lerobot/policies/pi0/modeling_pi0.py:146
预训练加载 PaliGemmaForConditionalGeneration transformers (HuggingFace)
动作专家 GemmaForCausalLM transformers (HuggingFace)