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 分布与训练时实际输入模型的分布不一致,可能导致归一化不正确。

相关推荐
触底反弹1 小时前
🔥 DeepSeek 560 万美金干翻 OpenAI?一文讲透「蒸馏」的来龙去脉
人工智能
私人珍藏库1 小时前
[Android] 会计快题库 -财会职称考试刷题学习
android·人工智能·学习·app·软件·多功能
Sirius Wu1 小时前
OpenClaw Skill:Matplotlib 可视化技能 + 沙箱双层隔离完整详解
服务器·网络·人工智能·安全·ai·架构·aigc
闻道且行之2 小时前
TurboOCR:基于PP-OCRv6的极速Windows离线OCR工具,深度解析3.4GB依赖背后的技术架构
c++·人工智能·python·qt·机器学习·ocr
We0 AI2 小时前
2026 年的 B2B 官网竞争,已经不只是页面竞争了
人工智能·aigc·#ai建站
橙臣程2 小时前
深度学习2——CNN与RNN概述
rnn·深度学习·cnn
冬奇Lab2 小时前
每日一个开源项目(第160篇):Destructive Command Guard - 在 AI Agent 运行 rm -rf 之前拦截它
人工智能·安全·开源
绝世番茄2 小时前
鸿蒙HarmonyOS ArkTS原生拖拽排序深度解析
深度学习·华为·list·harmonyos·鸿蒙
IvorySQL2 小时前
PG 日报|SQL/PGQ 图查询基于联接重写机制实现
数据库·人工智能·sql·postgresql·区块链·ivorysql