【RT-1论文阅读】: VLA领域的开创性工作,让机器人学会“举一反三“的Transformer大模型
论文信息
- 标题:RT-1: ROBOTICS TRANSFORMER FOR REAL-WORLD CONTROL AT SCALE
- 会议:arXiv preprint (2022)
- 单位:Robotics at Google, Everyday Robots, Google Research, Brain Team
- 代码:github.com/google-research/robotics_transformer
- 论文:https://arxiv.org/pdf/2212.06817
一、引言:机器人学习的"数据困境"
想象一下,如果你想教一个机器人学会"把可乐罐放进冰箱",你需要怎么做?在传统的机器人学习方法中,你可能需要收集成百上千次这个特定任务的演示,然后训练一个专门的模型。但如果下次你想让它"把苹果放进碗里",你又得重新收集数据、重新训练模型。这就像教一个孩子学英语,每次只教他一个单词,而且他永远不会举一反三。
这就是机器人学习领域长期面临的"数据困境":真实世界的机器人数据收集极其昂贵且耗时。与计算机视觉和自然语言处理领域动辄数十亿的数据集相比,机器人数据集的规模通常只有几千甚至几百个样本。
那么,我们能不能像NLP和CV领域那样,训练一个通用的机器人大模型,让它能够从大量多样化的数据中学习通用的技能,然后零样本或少样本地泛化到新任务上呢?这正是RT-1(Robotics Transformer 1)想要解决的问题。
二、RT-1整体架构:高效的多模态Transformer
RT-1是一个专为真实世界机器人控制设计的Transformer模型。它的核心设计理念是:在保证实时控制速度的前提下,最大化模型的容量和泛化能力。
图1:RT-1的整体架构概览(来源:论文Figure 1)
从图1可以看出,RT-1的输入是图像序列和自然语言指令,输出是离散化的机器人动作。整个架构由三个主要部分组成:
- FiLM条件化的EfficientNet:用于处理图像和语言指令的早期融合
- TokenLearner:用于压缩视觉token数量,提高推理速度
- Decoder-only Transformer:用于序列建模和动作预测
通俗易懂的解释:你可以把RT-1想象成一个"机器人大脑"。EfficientNet是它的"眼睛",负责看清楚周围的环境;FiLM层是它的"注意力开关",根据语言指令告诉"眼睛"应该关注什么;TokenLearner是它的"信息筛选器",只把最重要的视觉信息传递给大脑;最后的Transformer就是它的"大脑皮层",负责思考和决策,告诉机器人应该做什么动作。
三、核心技术细节
3.1 输入与输出表示
3.1.1 输入表示
RT-1接收两种输入:
- 图像序列:最近6帧300×300的RGB图像
- 自然语言指令:描述任务的文本,如"pick up the coke can"
3.1.2 输出表示
RT-1输出一个11维的离散化动作向量:
- 7个手臂维度:x, y, z(位置),roll, pitch, yaw(姿态),gripper(夹爪开合度)
- 3个底盘维度:x, y(平移),yaw(旋转)
- 1个模式维度:控制手臂、控制底盘或终止任务
每个维度都被离散化为256个bin。这种离散化的表示方式有两个主要优点:
- 能够表示复杂的多模态动作分布
- 与Transformer的token化输入输出更加匹配
3.2 图像与语言的早期融合:FiLM-EfficientNet
RT-1没有采用常见的"图像编码器+语言编码器+后期融合"的架构,而是使用了FiLM(Feature-wise Linear Modulation)技术实现了图像和语言的早期融合。
FiLM层的数学公式如下:
FiLM(F,γ,β)=γ⋅F+β\text{FiLM}(F, \gamma, \beta) = \gamma \cdot F + \betaFiLM(F,γ,β)=γ⋅F+β
其中:
- FFF:输入特征图
- γ\gammaγ:缩放因子,由语言指令嵌入通过全连接层得到
- β\betaβ:偏移因子,同样由语言指令嵌入通过全连接层得到
通俗易懂的解释:FiLM层就像一个"调光器"。对于不同的语言指令,它会对图像特征图的不同通道进行不同程度的"调亮"或"调暗"。例如,当指令是"pick up the red apple"时,FiLM层会调亮与红色和苹果形状相关的特征通道,让模型更加关注这些信息。
RT-1使用了预训练的EfficientNet-B3作为图像编码器,并在每个MBConv块之后插入了FiLM层。为了避免破坏预训练权重,FiLM层的权重被初始化为:
γ=1,β=0\gamma = 1, \quad \beta = 0γ=1,β=0
这样,在训练初期,FiLM层相当于一个恒等映射,不会影响预训练EfficientNet的功能。随着训练的进行,模型会逐渐学习到如何根据语言指令调整图像特征。
3.3 视觉token压缩:TokenLearner
Transformer的自注意力机制的计算复杂度是O(n2)O(n^2)O(n2),其中nnn是token的数量。如果直接将EfficientNet输出的9×9×512特征图展平成81个token,那么自注意力的计算量会非常大,无法满足实时控制的要求。
为了解决这个问题,RT-1使用了TokenLearner模块来压缩视觉token的数量。TokenLearner的核心思想是:学习一个注意力权重,将大量的输入token加权求和得到少量的输出token。
TokenLearner的数学公式如下:
Ti=∑j=1Nai,j⋅VjT_i = \sum_{j=1}^{N} a_{i,j} \cdot V_jTi=j=1∑Nai,j⋅Vj
其中:
- TiT_iTi:第iii个输出token
- ai,ja_{i,j}ai,j:第iii个输出token对第jjj个输入token的注意力权重
- VjV_jVj:第jjj个输入token
- NNN:输入token的数量
RT-1使用TokenLearner将81个视觉token压缩到了8个,这使得自注意力的计算量减少了一个数量级以上。
通俗易懂的解释:TokenLearner就像一个"新闻编辑"。它从81条"新闻"(视觉token)中挑选出最重要的8条,然后将它们整合成一篇"摘要",传递给Transformer进行处理。这样不仅大大提高了处理速度,还能让模型更加关注最重要的信息。
3.4 Transformer骨干网络
经过TokenLearner压缩后,每帧图像得到8个token。RT-1使用最近6帧图像,因此总共有6×8=486 \times 8 = 486×8=48个视觉token。这些token被加上位置编码后,输入到一个decoder-only Transformer中。
RT-1的Transformer骨干网络有8个自注意力层,总共约19M参数。整个RT-1模型的总参数量约为35M,这在Transformer模型中算是非常小的,但它却能在真实世界机器人上以3Hz的频率运行。
3.5 训练目标
RT-1使用行为克隆(Behavioral Cloning)方法进行训练,训练目标是最小化预测动作与专家演示动作之间的交叉熵损失:
L=−∑t=0T∑d=0Dlogp(at,d∣i,x0:t)\mathcal{L} = -\sum_{t=0}^{T} \sum_{d=0}^{D} \log p(a_{t,d} | i, x_{0:t})L=−t=0∑Td=0∑Dlogp(at,d∣i,x0:t)
其中:
- TTT:轨迹长度
- DDD:动作维度数(11)
- at,da_{t,d}at,d:第ttt步第ddd维的真实动作
- iii:语言指令
- x0:tx_{0:t}x0:t:从第0步到第ttt步的图像序列
四、大规模数据集:130k演示,700+任务
RT-1的成功离不开大规模、多样化的训练数据集。谷歌的研究人员用了17个月的时间,使用13台Everyday Robots移动机械臂,收集了超过130k个成功的演示轨迹,涵盖了744个不同的任务指令。
这些任务可以分为以下几类:
| 技能类别 | 数量 | 描述 | 示例指令 |
|---|---|---|---|
| Pick Object | 130 | 从表面拿起物体 | pick iced tea can |
| Move Object Near Object | 337 | 将一个物体移到另一个物体附近 | move pepsi can near rxbar blueberry |
| Place Object Upright | 8 | 将细长物体竖直放置 | place water bottle upright |
| Knock Object Over | 6 | 推倒细长物体 | knock redbull can over |
| Open / Close Drawer | 6 | 打开或关闭橱柜抽屉 | open the top drawer |
| Place Object into Receptacle | 84 | 将物体放入容器 | place brown chip bag into white bowl |
| Pick Object from Receptacle and Place on Counter | 162 | 从容器中取出物体并放在柜台上 | pick green jalapeno chip bag from paper bowl and place on counter |
| Additional tasks | 9 | 其他真实场景任务 | pull napkin out of dispenser |
| Total | 744 |
表1:RT-1训练数据集的任务分布(来源:论文Table 1)
有趣的案例:在数据收集过程中,研究人员发现机器人学会了一些非常有趣的"人类习惯"。例如,当人类演示者在打开抽屉时会稍微向后退一步,机器人也学会了这个动作。这说明模型不仅学习了任务本身,还学习了人类完成任务的方式。
五、实验结果与分析
5.1 整体性能对比
研究人员将RT-1与两个最先进的基线模型进行了对比:
- Gato:DeepMind提出的通用多模态智能体
- BC-Z:谷歌之前提出的多任务机器人学习模型,用于SayCan系统
对比结果如下:
| Model | Seen Tasks | Unseen Tasks | Distractors | Backgrounds |
|---|---|---|---|---|
| Gato | 65 | 52 | 47 | 35 |
| BC-Z | 56 | 43 | 23 | 41 |
| BC-Z XL | 72 | 56 | 43 | 41 |
| RT-1 (ours) | 97 | 76 | 83 | 59 |
表2:RT-1与基线模型的整体性能对比(来源:论文Table 2)
从表2可以看出,RT-1在所有评估维度上都显著优于基线模型:
- 已见任务:97%的成功率,比BC-Z高25%,比Gato高32%
- 未见任务:76%的成功率,比次优基线高24%
- 抗干扰能力:83%的成功率,比次优基线高36%
- 背景鲁棒性:59%的成功率,比次优基线高18%
通俗易懂的解释:这意味着RT-1不仅能很好地完成它见过的任务,还能很好地完成它从未见过的新任务。即使在有很多干扰物体或者背景完全不同的情况下,它也能保持很高的成功率。
5.2 真实厨房场景泛化
为了测试RT-1在真实世界中的泛化能力,研究人员在两个完全不同的真实办公室厨房中进行了测试。这些厨房与训练环境在布局、光照、背景等方面都有很大差异。
测试结果如下:
| Model | L1 (Layout & Lighting) | L2 (+ Distractors) | L3 (+ New Objects & Settings) |
|---|---|---|---|
| Gato | 75 | 50 | 0 |
| BC-Z | 80 | 60 | 20 |
| BC-Z XL | 85 | 65 | 25 |
| RT-1 (ours) | 95 | 85 | 60 |
表3:RT-1在真实厨房场景中的泛化性能(来源:论文Table 3)
L1、L2、L3代表不同难度的泛化级别:
- L1:仅泛化到新的布局和光照条件
- L2:在L1的基础上增加了未见的干扰物体
- L3:在L2的基础上增加了全新的任务设置和物体
从表3可以看出,RT-1在所有难度级别上都表现最好。特别是在最困难的L3级别,RT-1的成功率达到了60%,而Gato的成功率为0%。
5.3 长时程任务性能
RT-1的高成功率和强泛化能力使得它非常适合用于长时程任务。研究人员将RT-1与SayCan系统结合,在真实厨房中执行了一系列长时程任务,如"Bring me two different sodas"、"Throw away all the items on the table"等。
这些任务平均需要9.6步才能完成,涉及平均2.4个操作技能。测试结果如下:
| Model | Kitchen1 Execution | Kitchen2 Execution |
|---|---|---|
| Original SayCan | 47 | - |
| SayCan w/ Gato | 33 | 0 |
| SayCan w/ BC-Z | 53 | 13 |
| SayCan w/ RT-1 (ours) | 67 | 67 |
表4:RT-1在长时程任务中的性能(来源:论文Table 6)
令人惊讶的是,RT-1在Kitchen1和Kitchen2中的执行成功率完全相同,都是67%。这说明RT-1具有非常强的环境泛化能力。在补充视频中,研究人员展示了SayCan-RT1系统能够执行长达50步的超长时间任务。
5.4 数据消融实验
为了研究数据量和数据多样性对RT-1性能的影响,研究人员进行了一系列数据消融实验:
| Model | % Data | % Tasks | Seen Tasks | Unseen Tasks | Distractors | Backgrounds |
|---|---|---|---|---|---|---|
| RT-1 | 100 | 100 | 97 | 76 | 83 | 59 |
| RT-1 | 51 | 100 | 97 | 67 | 42 | 53 |
| RT-1 | 37 | 100 | 97 | 55 | 35 | 47 |
| RT-1 | 22 | 100 | 73 | 46 | 29 | 41 |
| RT-1 | 97 | 75 | 86 | 54 | 42 | 53 |
表5:数据消融实验结果(来源:论文Table 7)
从表5可以得出一个非常重要的结论:数据多样性比数据量更重要。减少25%的任务(同时保留97%的数据)对泛化性能的影响,与减少49%的数据(同时保留100%的任务)相当。
5.5 跨域数据吸收能力
RT-1的一个非常强大的特性是它能够吸收来自不同域的数据,包括仿真数据和来自其他机器人的数据。
5.5.1 吸收仿真数据
研究人员在真实数据的基础上,添加了518k个仿真轨迹,这些轨迹包含了一些在真实世界中从未见过的物体。测试结果如下:
| Model | Training Data | Real Objects (Seen Skill) | Sim Objects (Seen Skill) | Sim Objects (Unseen Skill) |
|---|---|---|---|---|
| RT-1 | Real Only | 92 | 23 | 7 |
| RT-1 | Real + Sim | 92 | 87 | 33 |
表6:吸收仿真数据的实验结果(来源:论文Table 9)
从表6可以看出,添加仿真数据后:
- 真实物体的性能保持不变(92%)
- 仿真物体的性能从23%提升到了87%
- 仿真物体上未见技能的性能从7%提升到了33%
这说明RT-1能够有效地从仿真数据中学习,并且不会忘记在真实数据中学到的技能。
5.5.2 吸收其他机器人的数据
研究人员还尝试将来自Kuka IIWA机械臂的209k个抓取轨迹与Everyday Robots的数据混合训练。测试结果如下:
| Model | Training Data | Classroom eval | Bin-picking eval |
|---|---|---|---|
| RT-1 | EDR only | 92 | 22 |
| RT-1 | Kuka only | 0 | 0 |
| RT-1 | EDR + Kuka | 90 | 39 |
表7:吸收其他机器人数据的实验结果(来源:论文Table 5)
从表7可以看出:
- 仅用Kuka数据训练的模型在EDR机器人上完全无法工作(0%)
- 混合训练后,原始任务的性能仅下降了2%(从92%到90%)
- bin-picking任务的性能提升了17%(从22%到39%),几乎翻了一倍
这说明RT-1能够从其他机器人的经验中学习,并且能够将这些经验迁移到自己的身体上。
5.6 模型消融实验
为了研究RT-1各个组件的重要性,研究人员进行了一系列模型消融实验:
| Model | Seen Tasks | Unseen Tasks | Distractors | Backgrounds | Inference Time (ms) |
|---|---|---|---|---|---|
| RT-1 (full) | 97 | 76 | 83 | 59 | 15 |
| RT-1 w/o big model | 89 (-8) | 62 (-14) | 77 (-6) | 53 (-6) | 13.5 |
| RT-1 w/o pre-training | 84 (-13) | 43 (-33) | 60 (-23) | 41 (-18) | 15 |
| RT-1 w/ continuous actions | 43 (-54) | 30 (-46) | 48 (-35) | 35 (-24) | 16 |
| RT-1 w/o Transformer | 85 (-12) | 71 (-5) | 67 (-16) | 59 (0) | 5.9 |
| RT-1 w/ auto-regressive actions | 82 (-15) | 62 (-14) | 67 (-16) | 59 (0) | 26 |
| RT-1 w/o history | 86 (-11) | 62 (-14) | 50 (-33) | 59 (0) | 14 |
表8:模型消融实验结果(来源:论文Table 13)
从表8可以得出以下结论:
- ImageNet预训练非常重要:移除预训练后,未见任务的性能下降了33%
- 离散动作表示非常重要:使用连续动作后,所有维度的性能都大幅下降
- 历史信息对抗干扰能力很重要:移除历史信息后,抗干扰能力下降了33%
- 自回归动作表示没有帮助:不仅没有提升性能,还使推理速度慢了一倍
六、核心代码实现
下面是RT-1模型的核心代码实现(基于PyTorch):
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision.models import efficientnet_b3
from transformers import UniversalSentenceEncoder
class FiLMLayer(nn.Module):
"""Feature-wise Linear Modulation layer"""
def __init__(self, num_features, embedding_dim=512):
super().__init__()
self.gamma = nn.Linear(embedding_dim, num_features)
self.beta = nn.Linear(embedding_dim, num_features)
# Initialize to identity
nn.init.zeros_(self.gamma.weight)
nn.init.zeros_(self.gamma.bias)
nn.init.zeros_(self.beta.weight)
nn.init.zeros_(self.beta.bias)
def forward(self, x, embedding):
"""
Args:
x: (batch_size, num_features, height, width)
embedding: (batch_size, embedding_dim)
Returns:
modulated_x: (batch_size, num_features, height, width)
"""
gamma = self.gamma(embedding).unsqueeze(-1).unsqueeze(-1) + 1.0
beta = self.beta(embedding).unsqueeze(-1).unsqueeze(-1)
return gamma * x + beta
class TokenLearner(nn.Module):
"""TokenLearner module for compressing visual tokens"""
def __init__(self, in_channels, num_tokens=8, hidden_dim=128):
super().__init__()
self.num_tokens = num_tokens
self.attention = nn.Sequential(
nn.Conv2d(in_channels, hidden_dim, kernel_size=3, stride=1, padding=1),
nn.GELU(),
nn.Conv2d(hidden_dim, num_tokens, kernel_size=3, stride=1, padding=1)
)
def forward(self, x):
"""
Args:
x: (batch_size, in_channels, height, width)
Returns:
tokens: (batch_size, num_tokens, in_channels)
"""
batch_size, channels, height, width = x.shape
# Compute attention weights: (batch_size, num_tokens, height, width)
attn = self.attention(x)
attn = attn.view(batch_size, self.num_tokens, -1)
attn = F.softmax(attn, dim=-1)
# Reshape input: (batch_size, channels, height*width)
x_flat = x.view(batch_size, channels, -1)
# Compute tokens: (batch_size, num_tokens, channels)
tokens = torch.bmm(attn, x_flat.transpose(1, 2))
return tokens
class RT1(nn.Module):
"""Robotics Transformer 1 model"""
def __init__(self, num_actions=11, num_bins=256, num_frames=6, num_tokens_per_frame=8):
super().__init__()
self.num_actions = num_actions
self.num_bins = num_bins
self.num_frames = num_frames
self.num_tokens_per_frame = num_tokens_per_frame
# Language encoder
self.use = UniversalSentenceEncoder.from_pretrained("google/universal-sentence-encoder-large-5")
# Image encoder with FiLM layers
self.efficientnet = efficientnet_b3(pretrained=True)
self.film_layers = nn.ModuleList()
# Add FiLM layers after each MBConv block
for i in range(len(self.efficientnet.features)):
if isinstance(self.efficientnet.features[i], nn.Sequential):
for j in range(len(self.efficientnet.features[i])):
if hasattr(self.efficientnet.features[i][j], 'block'):
out_channels = self.efficientnet.features[i][j].block[-1][0].out_channels
self.film_layers.append(FiLMLayer(out_channels))
# TokenLearner
self.token_learner = TokenLearner(in_channels=1536, num_tokens=num_tokens_per_frame)
# Transformer decoder
self.transformer = nn.TransformerDecoder(
nn.TransformerDecoderLayer(
d_model=1536,
nhead=8,
dim_feedforward=4096,
dropout=0.1,
activation='gelu',
batch_first=True
),
num_layers=8
)
# Action head
self.action_head = nn.Linear(1536, num_actions * num_bins)
# Positional encoding
self.pos_encoding = nn.Parameter(torch.randn(1, num_frames * num_tokens_per_frame, 1536))
def forward(self, images, instructions):
"""
Args:
images: (batch_size, num_frames, 3, 300, 300)
instructions: list of strings
Returns:
action_logits: (batch_size, num_actions, num_bins)
"""
batch_size = images.shape[0]
# Encode language instructions
lang_embedding = self.use(instructions) # (batch_size, 512)
# Process each frame
all_tokens = []
film_idx = 0
for t in range(self.num_frames):
x = images[:, t] # (batch_size, 3, 300, 300)
# Forward through EfficientNet with FiLM layers
for i in range(len(self.efficientnet.features)):
x = self.efficientnet.features[i](x)
# Apply FiLM layer after each MBConv block
if isinstance(self.efficientnet.features[i], nn.Sequential):
for j in range(len(self.efficientnet.features[i])):
if hasattr(self.efficientnet.features[i][j], 'block'):
x = self.film_layers[film_idx](x, lang_embedding)
film_idx += 1
# Apply TokenLearner
tokens = self.token_learner(x) # (batch_size, num_tokens_per_frame, 1536)
all_tokens.append(tokens)
# Concatenate tokens from all frames
all_tokens = torch.cat(all_tokens, dim=1) # (batch_size, num_frames*num_tokens_per_frame, 1536)
# Add positional encoding
all_tokens = all_tokens + self.pos_encoding
# Forward through Transformer decoder
transformer_output = self.transformer(all_tokens, all_tokens) # (batch_size, seq_len, 1536)
# Take the last token for action prediction
last_token = transformer_output[:, -1] # (batch_size, 1536)
# Predict action logits
action_logits = self.action_head(last_token) # (batch_size, num_actions*num_bins)
action_logits = action_logits.view(batch_size, self.num_actions, self.num_bins)
return action_logits
# Example usage
if __name__ == "__main__":
model = RT1()
# Dummy input
batch_size = 2
num_frames = 6
images = torch.randn(batch_size, num_frames, 3, 300, 300)
instructions = ["pick up the coke can", "move the apple to the bowl"]
# Forward pass
action_logits = model(images, instructions)
print(f"Action logits shape: {action_logits.shape}") # Should be (2, 11, 256)
# Get predicted actions
predicted_actions = torch.argmax(action_logits, dim=-1)
print(f"Predicted actions: {predicted_actions}")
七、结论与展望
RT-1是机器人学习领域的一个重要里程碑。它证明了:
- Transformer架构可以有效地用于真实世界机器人控制
- 大规模、多样化的数据集是实现通用机器人学习的关键
- 数据多样性比数据量更重要
- 模型可以有效地吸收来自不同域的数据,包括仿真数据和其他机器人的数据
RT-1的成功为通用机器人学习指明了方向。未来的研究方向包括:
- 扩大模型规模和数据集规模
- 加入更多的模态,如触觉、声音等
- 实现更好的跨机器人迁移
- 结合强化学习进一步提升性能
有趣的展望:想象一下,未来你可以用自然语言告诉你的家庭机器人"帮我准备早餐",它会自动打开冰箱,拿出牛奶、面包和鸡蛋,然后为你煎鸡蛋、烤面包、倒牛奶。这一切都不需要你为每个单独的任务收集数据和训练模型。RT-1让我们离这个梦想又近了一步。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)