Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

9.4.1 数据版本管理与元数据记录

在前面几节里,我们更多讨论“如何训练一个好模型”。从本节开始,我们关注的是另一半:如何保证这些训练过程在一年之后还能被别人、甚至是你自己可靠地重现 。对具身智能 / VLA 来说,这意味着要能追踪:某一次机器人“翻车”或表现很强,是用的哪一版数据、哪一版代码、哪一版模型。

【图 9-A 占位:一个“训练谱系”示意图——从“数据版本”→“预处理流水线”→“训练配置”→“模型版本”的有向图,用箭头连起来,表示可追溯关系】


9.4.1.1 数据版本

数据版本(Data Versioning) 的目标,是做到对数据“像对代码那样”进行版本管理:任何一次训练用的是哪一批轨迹、哪一批视频、哪一批语言指令,都可以精确地定位与恢复。(labelyourdata.com)

(1)为什么具身智能特别需要数据版本

对于 VLA / 机器人数据,这个需求比传统 CV/NLP 更强,原因包括:

  • 数据成本高:真实机器人收集一条轨迹的成本远高于从互联网抓一张图。删错一批、覆盖一批,等于烧了一堆硬件与人力。
  • 分布变动大:你会不断增加新场景(不同桌面、不同物体)、新机器人(不同自由度)、新任务,数据分布会明显随时间演化。
  • 安全与合规:一旦机器人产生危险行为,需要追溯“是哪一版数据教的”,版本可追踪是基本前提。(Labellerr)
(2)版本粒度与命名

实践中常见的版本粒度包括:

  • 数据集级:例如 openx_kit_real_v1.0lab_panda_drawer_v2.3,一版对应一次较大的采集 / 清洗周期。
  • 子集级:按机器人平台、任务、场景划分子版本,例如:
    • openx_kit_real_v1.0/panda/pick_place/…
    • openx_kit_real_v1.0/franka/drawer_open/…
  • 时间切片:按采集窗口切分,如 2025Q12025-10-week2,便于定位问题到某一批采集活动。

一个简单而实用的做法是:为每个可训练的数据集维护“语义化版本号”(如 SemVer:MAJOR.MINOR.PATCH),并且在任何训练配置里都写明“数据集名称 + 版本号”,而不是模糊的 “latest”。(labelyourdata.com)

(3)实现方式:从手工到专门工具
  1. 最基础的文件夹+README 做法

优点是门槛低;缺点是难以自动追踪“文件级别”的变化,也难以和实验管理工具自动打通。

  • 每个版本一个独立目录:
    • data/openx/v1.0/…
    • data/openx/v1.1/…
  • data/README.md 中记录各版本的:
    • 创建时间
    • 主要差异(新增任务、修复标注、过滤了哪些异常)
    • 对应的采集脚本 / 仿真配置哈希
  1. DVC / lakeFS / Pachyderm 等数据版本工具 这类工具本质上是“Git for data”,会为每次数据变更创建“提交(commit)”,存储在对象存储或分布式文件系统中,并保留可回滚的历史。(labelyourdata.com)

在具身智能项目中,一个常见模式是:

  • DVC:通过 .dvc 文件将数据的哈希写入 Git 仓库本身,数据本体存到 S3 / OSS / NFS 等;优点是与现有 Git 工作流兼容。
  • Pachyderm / lakeFS:直接把数据湖当成“可提交的仓库”,天然支持数据血缘和审计追踪,非常适合大型 MLOps 平台。
  • 原始轨迹(如 ROS bag)和视频存入对象存储;
  • 用 DVC / Pachyderm 对“已清洗 + 对齐 + 标注完成的训练子集”做版本管理;
  • 在训练代码里,仅通过逻辑名字引用数据版本,例如 dataset: openx_kit_real@2.1.0

【图 9-B 占位:对比“文件夹命名式版本管理”与“DVC/Pachyderm 提交式版本管理”的简化示意图】


9.4.1.2 元数据

元数据(Metadata)就是“关于数据的数据”:当你只看一条轨迹的图像与动作时,很难知道它是哪台机器人、在什么场景、执行什么任务、是否成功。元数据承担的就是这个“说明书”的角色。(Medium)

在具身智能场景中,元数据大致可以分为三层:

  1. 数据集层元数据(dataset-level) 描述某一版数据集的全局信息,可以用 YAML/JSON manifest 文件表示,例如 openx_kit_real_v1.2.0/meta.yaml
    • 基本信息:名称、版本号、维护者、发布时间、许可证;
    • 适用机器人:支持的机器人型号列表(如 franka_panda, ur5, aloha);
    • 任务分布:每种任务的轨迹数量、成功率大致统计;
    • 采集环境:真实/仿真、主要场景类型(桌面、货架、抽屉)、主要光照条件;
    • 预处理信息:是否进行过时间对齐、下采样、坐标变换、归一化等。
  2. 轨迹层元数据(episode-level / trajectory-level) 对于单条轨迹,典型的元数据字段包括:
    • trajectory_id:唯一 ID(通常是 UUID);
    • task_id / task_name:例如 pick_place_blockdrawer_open
    • robot_id 与硬件信息:机械臂型号、末端夹爪类型、固件版本;
    • env_params:仿真时的摩擦系数、物体质量范围,现实中可记录手眼标定版本号;
    • success:任务是否成功,以及失败原因标签(碰撞、抓取失败、超时等);
    • language_instruction_ids:该轨迹所关联的指令文本 ID,便于多语言指令对齐。
  3. 样本层元数据(step-level) 对于对时间非常敏感的任务,可以在每个 time step 记录:
    • 时间戳(与图像帧、力觉、关节状态统一);
    • 传感器同步状态(是否丢帧、是否插值);
    • 特殊事件标记(接触发生、抓取闭合、抽屉开启瞬间等)。

良好的元数据设计可以显著简化后续的数据筛选、子集构建与可视化分析,也是构建可靠审计追踪的前提。


9.4.1.3 审计追踪

审计追踪(Audit Trail) 的目标是:给定某个模型版本或机器人行为,你能一路“倒推”到:

哪次实验 → 用了哪份代码 → 哪份配置 → 哪一版数据 → 哪些预处理步骤。

这在金融、医疗等高监管行业已经是常态,在“会真的撞到人的机器人”上,只会更重要。(VerifyWise)

(1)审计追踪需要记录什么

一个最低限度的“可用审计条目”通常包含:

  • 模型信息:模型名称、版本号、训练时间、模型文件的哈希(如 SHA256);
  • 代码版本:Git 仓库 URL + commit hash + 分支 / tag;
  • 数据版本:数据集逻辑名 + 数据版本号(或 DVC/Pachyderm commit);(DagsHub)
  • 训练配置:完整的超参数配置文件快照;
  • 实验环境:主机类型、GPU 型号、深度学习框架版本、操作系统等;
  • 责任人:触发训练的用户、触发方式(手动运行 / CI 作业);
  • 关键训练指标:最终验证集指标、训练过程中的异常(NaN / 恶性发散等)。

这些信息通常由 实验追踪工具 + 数据版本工具 + Git 共同提供,稍后 9.4.2、9.4.3 会展开。

(2)如何落地“可查账”的链路

一个常用的工程实践是:

  1. 每一次实验 run 都生成一个全局唯一 run_id(例如 2025-10-20_23-15-42_franka_bc_v3 或直接 UUID)。
  2. 在实验追踪系统中,以 run_id 为主键,写入:
    • 训练脚本所在的 Git commit;
    • 使用的数据集逻辑名与数据版本号;
    • 完整的配置文件内容(或其哈希);
    • 模型 checkpoint 的存储路径和哈希。
  3. 在部署系统中,同样把 run_id 随模型一起记录下来:
    • 部署日志中记录“当前线上版本 model_deploy_id 对应 run_id”;
    • 机器人执行日志中写入“当前使用模型 run_id”。

这样,当现实世界中发生任何“异常行为”时,你就可以沿着 run_id → 训练记录 → 数据版本 → 原始轨迹 的路径逐步排查。

【图 9-C 占位:一个“从机器人行为日志回溯到数据与代码”的审计链路图,展示 run_id 在各系统间贯穿的示意】


9.4.2 大规模训练的参数配置与日志记录

大规模 VLA / 具身模型训练的一个典型特征是:配置项多到肉眼记不住,日志多到肉眼看不过来 。靠“记住我大概怎么设的学习率”已完全不现实,这一节的目标,是给出一套稳健的“配置 + 日志”体系。


9.4.2.1 超参数配置

超参数配置管理 的核心要求只有一句话:任何一次训练,必须能够被一份“可复用的配置文件 + 某个代码版本”完全描述 。(neptune.ai)

(1)配置内容的分层

建议把配置拆成几个层次(在 YAML / JSON 中体现):

  • data: 数据相关
    • 数据集名与版本号
    • 训练/验证/测试划分方式
    • 数据增强参数(随机裁剪、颜色扰动、域随机化范围等)
  • model: 模型结构
    • 视觉 backbone 类型(ResNet/ViT)及其预训练权重版本
    • 语言模型选择及其冻结/解冻策略
    • 动作解码器结构(自回归 / 并行、token 词表大小等)
  • train: 训练流程
    • 学习率、学习率调度策略
    • batch size、梯度累积步数
    • 优化器种类(AdamW / LAMB 等)、权重衰减
    • 训练轮数、warmup 步数、梯度裁剪阈值
  • rl: 若包含 RL 微调
    • 奖励系数、折扣因子
    • 收集样本的并行环境数
    • 每步更新的采样与更新频率等
  • env: 运行环境
    • GPU 数量、每块卡的 batch
    • 分布式后端(NCCL、Gloo)
    • 混合精度 / 全精度开关
(2)配置系统与工具

简单起步可以用 纯 YAML + argparse/JSON 解析 。在项目复杂之后,可以考虑使用专门的配置管理库,例如 Hydra、Sacred 等,它们支持:

  • 多个基础配置的组合(例如 robot=franka + task=drawer_open + backbone=vit);
  • 命令行覆写单个字段,同时自动记录覆写内容;
  • 为每次 run 自动生成一个“已展开的完整配置快照”。(GitHub)

这样可以极大降低“忘了某个参数到底是默认值还是手动改过”的风险。

(3)配置与实验追踪的绑定

最后一条实践经验是:把“配置文件快照”当作实验日志的一部分,而不是只写在磁盘上。多数实验管理工具(如 MLflow、Weights & Biases、Neptune 等)都支持将配置作为 JSON/YAML artifact 上传,并可在网页界面中直接浏览和对比不同实验的配置差异。(mlflow.org)


9.4.2.2 日志系统

日志系统(logging)承担两件事:

  1. 短期内帮助你 debug 与监控训练
  2. 长期为你提供可视化对比、审计追踪与论文作图素材
(1)记录什么

典型需要记录的内容包括:(JFrog)

  • 标量指标
    • 训练 loss、验证 loss;
    • 每个任务的成功率、平均回报;
    • 学习率、梯度范数、参数范数;
  • 直方图 / 分布
    • 模型权重分布、梯度分布(有助于发现梯度消失/爆炸);
  • 图像与视频(具身智能项目非常重要):
    • 训练过程中随机抽取的观察帧、分割结果、注意力热力图;
    • 每若干 steps 的 rollout 视频,展示机器人在仿真 / 现实中的行为;
  • 文本日志
    • 关键阶段的摘要(比如“第 1000 步成功率首次超过 80%”);
    • 异常信息(Loss 爆炸、NaN、训练重启等);
  • 系统指标
    • GPU 利用率、显存占用、CPU/内存压力,方便判断是否受限于 I/O 或算力瓶颈。
(2)工具选择

在工程实践中,常见的组合是:

  • 基础日志:用 logging 模块写入本地文件(如 train.log),用于快速 grep 与排错;
  • 可视化日志:使用 TensorBoard、MLflow、Weights & Biases、Comet 等工具记录标量、图像和模型参数,支持在网页中对比多次实验的曲线;(mlflow.org)
  • 基础设施监控:对于大型集群训练,引入 Prometheus + Grafana 等监控 GPU/节点状态,避免训练“悄悄挂掉”。
(3)日志频率与成本控制
  • 标量指标(loss、成功率)可以每几步记录一次;
  • 高成本对象(视频、整张图像)的记录频率要谨慎,一般每 N 个 step 或每轮训练只记录少量样本;
  • 日志文件要限制大小并定期归档或上传到对象存储,避免占满磁盘导致训练中断。

【图 9-D 占位:一个典型训练日志仪表盘截图示意——上方是 loss/成功率曲线,下方是若干机器人 rollout 视频缩略图】


9.4.2.3 模型检查点

模型检查点(checkpoint) 是大规模训练的“存档点”:既是故障恢复的安全带,也是模型选择的样本库。(mlflow.org)

(1)检查点内容

一个“完整”的 checkpoint 通常包含:

  • 模型参数(state_dict);
  • 优化器状态(包括动量、二阶矩估计等);
  • 学习率调度器状态(当前步数、当前学习率);
  • 训练进度(当前 epoch / step);
  • 重要随机数生成器状态(PyTorch、NumPy、Python random 等);
  • 若为 RL 训练,还可包括 replay buffer 片段、归一化参数等。

这样,在断电或 Crash 后,可以尽可能“无损地”恢复训练。

(2)命名与保留策略
  • 建议使用包含 run_id + step/epoch + 关键指标 的命名方式,例如: ckpt_run1234_step200k_valSucc0.81.pt
  • 一般会保留:
    • 最新 checkpoint(便于继续训练);
    • 若干个基于验证指标排序的“最优 checkpoint”(比如前 3 名);
    • 若干“里程碑 checkpoint”(例如每 100k steps 一份,用于分析训练过程)。

对于大模型,checkpoint 本身可能非常大(数十 GB),这就需要:

  • 使用压缩格式(如 fp16 权重、分 shard 存储);
  • 使用云端 / 对象存储,并在实验日志中记录其路径和哈希,以便后续校验完整性。

【图 9-E 占位:时间轴上按训练 step 排列的一系列 checkpoint 图标,其中部分被标记为“best”/“latest”/“milestone”】


9.4.3 实验管理工具(配置、对比实验、可视化)

如果说 9.4.2 讲的是“你在代码里手动 log 的东西”,那么本节的重点是:把这些日志、配置、结果,用一套统一的实验管理工具“捆绑成一个故事” 。这样,无论是自己复盘,还是给别人解释,都能“点开一个实验就看到全貌”。(JFrog)


9.4.3.1 配置管理

配置管理在这里的含义略有扩大:不仅是本地的 YAML 文件,还包括与实验追踪工具的联动。

(1)单一信息源(Single Source of Truth)

建议建立“单一信息源”的理念:

  • 所有实验都必须由配置文件驱动(即便是简单实验,最好也有一个最小配置);
  • 所有配置变化,要么通过修改配置文件 + Git 提交,要么通过命令行覆写并在实验追踪工具中自动记录;
  • 禁止将关键超参数硬编码在训练脚本中(magic numbers)。
(2)与实验追踪工具结合

以 Sacred、MLflow、Weights & Biases 等为例,常见做法是:

  • main() 函数入口处初始化实验追踪器;
  • 将配置对象完整地 log 进去;
  • 让工具自动为本次 run 建立目录 / 面板,后续的所有日志、checkpoint 路径都挂在这个 run 名下。(mlflow.org)

这样,当你浏览某个 run 时,可以随时查看其完整配置并与其他 run 做逐字段对比。


9.4.3.2 对比实验

对比实验(experiment comparison) 是整套体系的核心产出之一:你不是单独看一条曲线,而是同时比较“不同设计决策”对训练与最终性能的影响。

(1)变量控制与标签体系

要做可解释的对比实验,必须做到:

  • 每次 run 只改变少数几个超参数(如 backbone 从 ResNet50 换成 ViT-B);
  • 为 run 打上清晰的标签(如 robot=franka, task=drawer, algo=BC, backbone=ViT),方便后续在 UI 中筛选子集对比。

多数实验管理工具都原生支持为 run 添加 key–value 形式的标签,并提供基于标签的筛选与分组统计。(neptune.ai)

(2)典型对比维度(以 VLA 为例)
  • 架构对比:纯视觉→视觉+语言→完整 VLA 模型,观察各自对多任务成功率的提升;
  • 预训练策略对比:无预训练 vs 纯视觉预训练 vs 视觉-语言对比学习预训练;
  • 训练范式对比:纯 BC vs BC+RL 微调 vs BC+自监督预训练+RL;
  • 数据版本对比dataset_v1.0 vs dataset_v1.1(加入 domain randomization 或新增场景)对泛化性能的影响。

对比实验的结果,既是论文中消融实验(ablation study)的来源,也是工程决策(上线哪个模型版本)的依据。


9.4.3.3 可视化

在具身智能项目中,“看曲线”只是可视化的一部分,“看机器人行为” 同样重要。

(1)训练过程可视化

常规的训练曲线包括:

  • 全任务平均成功率 vs 训练步数;
  • 按任务拆分的成功率曲线(看是否某些任务明显学得更慢);
  • Loss、梯度范数随时间变化;

实验管理工具(如 MLflow、W&B、TensorBoard 等)都提供实时曲线绘制与导出功能。(mlflow.org)

(2)具身任务特有的可视化
  • rollout 视频墙:将不同模型版本在相同任务上的执行视频,拼成网格对比。可以直观看出模型是否学会了更“柔和”的操作、更稳健的路径。
  • 注意力热力图:对于 Transformer 类模型,可视化语言 token 对视觉 patch 的注意力,观察模型是否“看到了”正确的区域(比如“红色杯子”)。
  • 状态分布可视化:对于大量轨迹,可以用 PCA / t-SNE 对高维状态表示降维,绘制散点图,直观展示数据覆盖哪些状态区域。

【图 9-F 占位:一页“实验管理 dashboard”示意图,上方为按任务分组的成功率曲线,下方为不同模型版本在同一任务上的视频缩略图对比】


9.4.4 可复现性与代码结构规范

可复现性(Reproducibility) 是现代机器学习、尤其是具身智能研究的“硬门槛”:如果你自己在不同时间、不同机器上都复现不了结果,那无论模型多 fancy,都很难说服别人相信。(docs.pytorch.org)

本节从三个层面保障复现:随机性控制、代码版本控制、项目结构规范。


9.4.4.1 随机种子控制

深度学习中的“不确定性”来源很多,包括:

  • 参数初始化;
  • 数据打乱 / 数据增强;
  • mini-batch 分配;
  • GPU 并行的非确定性算子;
  • 仿真环境中的随机扰动(域随机化)。

完全做到“比特级稳定”在某些平台上并不现实,但可以做到主导因素可控 。(docs.pytorch.org)

(1)基础做法:统一设定随机种子

以 PyTorch 为例,典型的“set_seed” 函数会:

  • 设置 Python random 的种子;
  • 设置 NumPy 的随机种子;
  • 设置 PyTorch CPU/GPU 的随机种子;
  • 配置 DataLoader 的 worker_init_fngenerator,确保数据加载进程也使用可控种子;
  • 配置 cuDNN 的 deterministic / benchmark 选项,避免非确定性算子(以牺牲一些性能为代价)。

注意:PyTorch 官方文档明确指出,即便这样做也不能保证百分百确定性,尤其是在使用某些特定 GPU 算子或多机多卡训练时。你需要根据具体框架版本与硬件情况阅读最新的 reproducibility 指南。(docs.pytorch.org)

(2)仿真与域随机化的种子

在具身智能场景中,还有一层:仿真环境的随机数 。例如:

  • 把物体初始位置、摩擦系数、光照等做域随机化;
  • 将传感器噪声随机化。

这里的建议是:

  • 在训练时,保存每次 roll-out 所用的“环境随机种子”或“随机参数向量”,以便复现同一条轨迹;
  • 在分析某一失败案例时,可以用相同环境种子、相同 policy 与相同初始状态复现问题。

9.4.4.2 代码版本控制

代码版本控制的“默认选项”就是 Git,这里不再赘述用法,而是强调 与实验、数据、模型的联动 。(DagsHub)

(1)每个实验必须绑定一个 Git commit
  • 要求所有训练都在“干净的工作区”上运行(git status 无修改),否则很难知道实际运行的代码究竟是什么;
  • 在运行训练脚本时自动记录当前的 commit hash、分支与 tag;
  • 建议在针对论文或重要结果时,为对应 commit 打上 tag,例如 paper_icra25_v1
(2)Git + 数据 / 模型版本工具

对于大文件(数据与模型权重),有几种常见组合方式:

  • 代码在 Git 中管理,数据用 DVC / Pachyderm 等工具版本化,并在 Git 中记录数据指针;(Medium)
  • 模型参数可以:
    • 存在对象存储,比如 S3 s3://lab-models/vla/openx/v1/run1234.pt
    • 使用模型版本工具(如 MLflow Model Registry)维护“当前生产版本”和历史版本映射。

实验追踪工具一般会同时记录“Git commit + 数据版本 ID + 模型 artifact 路径”,形成完整链路。(mlflow.org)


9.4.4.3 项目结构规范

最后一块,是让整个项目看起来“不像一堆随手写的脚本” 。一个良好的项目结构能显著降低新成员上手成本,也让你在一年后回来看代码时不至于完全迷路。(DEV Community)

(1)典型目录结构

以一个中大型 VLA / 机器人项目为例,可以采用类似结构:

project_root/
  configs/          # 所有 YAML/JSON 配置(模型、数据、训练、部署等)
  src/
    data/           # 数据加载与预处理(含 ROS bag/视频解析等)
    models/         # 模型定义(编码器、解码器、VLA 主体架构)
    training/       # 训练循环、loss 计算、分布式封装
    eval/           # 评估脚本与指标实现
    deployment/     # 在线推理、与 ROS/机器人接口的封装
    utils/          # 通用工具函数(日志、seed、配置解析等)
  scripts/
    train_vla.py    # 训练入口脚本
    eval_vla.py     # 离线评估脚本
    collect_data.py # 数据采集或仿真数据生成脚本
  notebooks/
    exploration/    # 数据探索、可视化用 notebook
    reports/        # 生成图表与论文结果的 notebook
  data/
    raw/            # 原始数据(通常不直接进 Git,可由 DVC 管理)
    processed/      # 预处理后用于训练的数据
  experiments/      # 本地运行产生的日志、checkpoint(线上可迁移到远程存储)
  tests/            # 单元测试与简单集成测试
  docs/             # 使用说明、API 文档、设计文档
  requirements.txt / environment.yml / pyproject.toml
  README.md

在机器人项目里,还会额外有:

  • robot/:URDF/XACRO、机械臂/夹爪参数;
  • simulation/:仿真环境配置、场景模型、任务脚本;
  • ros_ws/:若使用 ROS/ROS2,对应的工作空间与包结构。
(2)环境与依赖管理

要实现可复现性,仅仅有源代码远远不够,你还需要描述清楚:

  • Python 依赖版本(requirements.txtpoetry/conda 环境);
  • C/C++/系统依赖(某些仿真库或驱动版本);
  • CUDA / cuDNN / GPU 驱动版本。

一个常见做法是使用 Docker / 容器来封装环境:为每个主要版本的系统构建一个基础镜像(如 vla-base:cuda12.2-py310),在镜像中固定所有依赖版本,再在容器内运行训练与评估脚本。(hackmd.io)


本节从数据、配置、日志、代码和项目结构五个维度,搭建了一套“能查、能复现、能对比”的工程框架。后续第 10 章在讨论部署与评测时,会在此基础上进一步要求:不仅训练过程可复现,** 线上行为与线下实验之间的差异也要尽量可解释和可控**。