lerobot中use_relative_actions=True需要重新计算meta/stats.json等信息

目录

  • [1 问题说明](#1 问题说明)
  • [2 recompute_stats的具体计算逻辑](#2 recompute_stats的具体计算逻辑)
    • [2.1 统计信息的存储位置](#2.1 统计信息的存储位置)
    • [2.2 默认计算路径](#2.2 默认计算路径)
    • [2.3 relative action 的定义](#2.3 relative action 的定义)
    • [2.4 合法 chunk 的筛选](#2.4 合法 chunk 的筛选)
    • [2.5 `relative_exclude_joints`](#2.5 relative_exclude_joints)
    • [2.6 为什么同一个action会被多次统计?](#2.6 为什么同一个action会被多次统计?)
    • [2.7 计算哪些统计量?](#2.7 计算哪些统计量?)
    • [2.8 为什么普通路径要跳过action?](#2.8 为什么普通路径要跳过action?)
    • [2.9 参数要求](#2.9 参数要求)

1 问题说明

默认情况下, π 0.5 \pi_{0.5} π0.5输出的是绝对动作。启用relative actions后,模型将预测相对于机器人当前状态的动作增量,而不是绝对目标动作。在某些任务或机器人配置下,这种方式能够提升训练的稳定性。

如果要使用相对动作,首先需要使用命令行工具(CLI)重新计算数据集在相对动作空间下的统计信息。

bash 复制代码
lerobot-edit-dataset \
    --repo_id your_dataset \
    --operation.type recompute_stats \
    --operation.relative_action true \
    --operation.chunk_size 50 \
    --operation.relative_exclude_joints "['gripper']" \
    --push_to_hub true

或者,使用 Python 的等效实现如下:

python 复制代码
from lerobot.datasets import LeRobotDataset, recompute_stats

dataset = LeRobotDataset("your_dataset")
recompute_stats(dataset, relative_action=True, chunk_size=50, relative_exclude_joints=["gripper"])
dataset.push_to_hub()

chunk_size应与策略模型的chunk_size参数保持一致( π 0.5 \pi_{0.5} π0.5的默认值为 50)。relative_exclude_joints用于指定哪些关节仍采用绝对动作表示,而不转换为相对动作(例如夹爪的开合控制)。如果需要将更新后的数据集统计信息上传到Hugging Face Hub,请添加参数--push_to_hub true

然后,在训练时启用相对动作:

bash 复制代码
lerobot-train \
    --dataset.repo_id=your_dataset \
    --policy.type=pi05 \
    --policy.use_relative_actions=true \
    --policy.relative_exclude_joints='["gripper"]' \
    ...

2 recompute_stats的具体计算逻辑

代码位置:src/lerobot/datasets/dataset_tools.py中的recompute_stats()函数。它用于重新计算 LeRobot 数据集的全局统计信息,并写入:

text 复制代码
meta/stats.json

它不会修改原始数据文件,也不会更新:

text 复制代码
data/**/*.parquet
meta/episodes/**/*.parquet

2.1 统计信息的存储位置

数据集中有两类 stats:

文件 内容 主要用途
meta/stats.json 整个数据集的全局统计量 训练时 action/state 的归一化
meta/episodes/**/*.parquet 每个 episode 的局部统计量 数据集录制、聚合、子集等元数据操作

训练实际读取的是 dataset.meta.stats,其来源为 meta/stats.json

因此,recompute_stats() 的目标是修正或重建全局训练统计,而不是回写 episode 元数据中的历史 stats。


2.2 默认计算路径

函数先从 dataset.meta.features 筛选需要统计的数值特征:

python 复制代码
numeric_features = {
    k: v
    for k, v in features.items()
    if v["dtype"] not in ["image", "video", "string"]
    and k not in {"index", "episode_index", "task_index", "frame_index", "timestamp"}
}

默认 skip_image_video=True,因此只重新计算数值特征,例如:

text 复制代码
action
observation.state
其他数值 observation

随后它会:

  1. 读取 data/**/*.parquet
  2. episode_index 切分每个 episode
  3. 为每个 feature 计算 episode 内的 min/max/mean/std/quantiles
  4. 将所有 episode 的统计聚合为数据集全局统计
  5. 写入 meta/stats.json

对于普通 absolute action,逻辑相当于:

text 复制代码
stats["action"] = statistics(all absolute actions)

relative_action=True 的计算逻辑

当以下条件都成立时,函数会改用相对动作的方式计算 action stats

python 复制代码
relative_action is True
ACTION in features
OBS_STATE in features

也就是说,数据集至少必须同时包含:

text 复制代码
action
observation.state

2.3 relative action 的定义

对于一个 action chunk,其起始帧为 t,长度为 K

text 复制代码
action chunk:
[a_t, a_(t+1), ..., a_(t+K-1)]

current state:
s_t

相对动作定义为:

text 复制代码
relative_action(t, j) = action(t + j) - state(t)

其中:

text 复制代码
j = 0, 1, ..., K - 1

重点是:chunk 内所有未来 action 都减去 chunk 起始帧的 state。

它不是:

text 复制代码
action(t + j) - state(t + j)

而是:

text 复制代码
action(t + j) - state(t)

这与 action-chunk policy 的训练输入一致:模型基于当前状态 state(t),预测未来连续 K 步动作。


2.4 合法 chunk 的筛选

假设:

text 复制代码
chunk_size = 3

episode 0 的帧为:

text 复制代码
0, 1, 2, 3, 4

则合法 chunk 为:

text 复制代码
起点 0 -> [0, 1, 2]
起点 1 -> [1, 2, 3]
起点 2 -> [2, 3, 4]

如果 episode 1 从帧 5 开始,则下面的 chunk 无效:

text 复制代码
起点 3 -> [3, 4, 5]
起点 4 -> [4, 5, 6]

因为它们跨越了 episode 边界。

实现仅比较 chunk 首尾帧的 episode_index

python 复制代码
valid = episode_indices[starts] == episode_indices[starts + chunk_size - 1]

一个长度为 L 的 episode 可提供的合法 chunk 数量为:

text 复制代码
max(L - chunk_size + 1, 0)

2.5 relative_exclude_joints

默认情况下:

python 复制代码
relative_exclude_joints = ["gripper"]

这意味着名称中包含 gripper 的 action 维度不做相对化,仍保留绝对值。

例如:

text 复制代码
action_names = ["shoulder", "elbow", "wrist", "gripper"]
relative_mask = [1, 1, 1, 0]

含义:

text 复制代码
1 -> action - state
0 -> 保持原 action

若:

text 复制代码
state(t) = [10, 20, 30, 0.4]

原始 action chunk 为:

text 复制代码
a_t     = [11, 19, 31, 0.5]
a_(t+1) = [13, 18, 34, 0.6]
a_(t+2) = [16, 17, 38, 0.7]

转换后:

text 复制代码
relative a_t     = [1, -1, 1, 0.5]
relative a_(t+1) = [3, -2, 4, 0.6]
relative a_(t+2) = [6, -3, 8, 0.7]

前三个关节维度减去起始 state;gripper 维度保持绝对值。

若 action metadata 中没有 action.names,代码无法按名称识别 gripper,此时掩码会默认使全部维度相对化。


2.6 为什么同一个action会被多次统计?

chunk 使用滑动窗口,因此相邻 chunk 会重叠。

例如帧 2 会出现在:

text 复制代码
chunk [0, 1, 2]: action(2) - state(0)
chunk [1, 2, 3]: action(2) - state(1)
chunk [2, 3, 4]: action(2) - state(2)

虽然是同一个绝对 action,但参考 state 不同,所以它们是不同的相对动作样本。

最终参与统计的样本数量是:

text 复制代码
合法 chunk 数 × chunk_size

这刻意模拟了训练中所有可能的「当前 state + 未来 action chunk」组合。


2.7 计算哪些统计量?

所有相对 action chunk 会被展平为:

text 复制代码
(num_valid_chunks × chunk_size, action_dim)

并逐维计算:

text 复制代码
min
max
mean
std
count
q01
q10
q50
q90
q99

其中 quantile 使用运行中的直方图近似计算。

最终结果写入:

text 复制代码
meta/stats.json -> action

2.8 为什么普通路径要跳过action?

普通统计路径会针对原始数据计算:

text 复制代码
statistics(absolute action)

但启用相对动作训练后,模型接收到的是:

text 复制代码
statistics(action - chunk_start_state)

这两个分布通常不同,不能混用。

因此在相对 action stats 计算完成后,代码会执行:

python 复制代码
features_to_compute.pop(ACTION, None)

避免后续普通的逐 episode absolute-action 统计覆盖相对 action 的结果。

最后结构为:

text 复制代码
meta/stats.json:
  observation.state:
    # 原始绝对 state 的统计量

  action:
    # 相对 action 的统计量
    # relative_exclude_joints 的维度仍是绝对 action 统计量

2.9 参数要求

chunk_size 应与训练 policy 的 action chunk 长度一致:

python 复制代码
recompute_stats(
    dataset,
    relative_action=True,
    chunk_size=policy.chunk_size,
)

若两者不同,计算 stats 时的相对 action 分布与训练时实际输入模型的分布不一致,可能导致归一化不正确。

相关推荐
孙启超2 小时前
【AI应用开发】怎么降低 Agent 幻觉?有几种可行方案?
大数据·人工智能·llm·agent·rag·幻觉·ai应用开发
丘丘用户思思澪2 小时前
生产环境下优化 Agent Token 成本的系统化实战指南
人工智能
我是大卫2 小时前
图解RAG,一张图理解检索增强生成
人工智能
淡忘suuda2 小时前
生产级 AI Agent 评测体系实战:从数据集构建到自动回归与质量门禁
人工智能·数据挖掘·回归
spider_xcxc2 小时前
生产级AI智能体的可观测性实战:从指标监控到LLM评估
大数据·人工智能
高洁012 小时前
VLA:驱动具身智能迈向通用的关键引擎
人工智能·python·深度学习·transformer·知识图谱
数智化管理手记2 小时前
元数据和业务数据有什么区别?元数据价值体现在什么地方?
大数据·人工智能·数据挖掘·云计算
西安小哥2 小时前
AI时代技术工程师的"道法术器势":学习图谱、成长规划与角色重塑
人工智能·设计模式·程序员
还是鼠鼠2 小时前
【Spring AI智能体实战 02】Spring Boot 3.4整合Spring AI:项目创建与依赖配置
java·人工智能·spring boot·maven·spring ai
武子康2 小时前
GPT-5.6 Sol 的 ARC-AGI-3 分数为何翻近三倍:Agent 评测必须记录整套运行合同
人工智能·chatgpt·agent