概述与QA

本系列为我在vla上的第一个实践:基于lerobot做π0.5的推理 、微调  、仿真。本文主要介绍模型的微调、仿真与评估实现。

下面列出几个QA来引入:

Q:本章具体是要做什么事情?

A:本章要将之前做的LoRA微调、仿真结合起来,在基座模型上微调出一个新的模型,并通过libero的评估和MuJoCo仿真来看看模型训练的效果如何。


1. 基线评估

用下面的代码来进行pi05_libero_base基线模型的评估:

import os
os.environ["HF_HOME"] = "/root/autodl-tmp/huggingface"
os.environ["HF_HUB_OFFLINE"] = "1"
os.environ["MUJOCO_GL"] = "egl"
os.environ["PYOPENGL_PLATFORM"] = "egl"
os.environ["LIBERO_CONFIG_PATH"] = "/root/autodl-tmp/libero-config"
# 让子进程的输出不缓冲,实时打印日志
os.environ["PYTHONUNBUFFERED"] = "1"

# 解析命令行参数
import argparse
import json
# 把命令列表转换成可复制的 shell 字符串。
import shlex
import subprocess
import time
from datetime import datetime
from pathlib import Path
# 处理时区
from zoneinfo import ZoneInfo

from huggingface_hub import snapshot_download
# 注册一个参数,名字是 --control-mode,值只可选relative\absolute
parser = argparse.ArgumentParser()
parser.add_argument(
    "--control-mode",
    choices=["relative", "absolute"],
    default="relative",
)
args = parser.parse_args()

checkpoint = snapshot_download(
    "lerobot/pi05_libero_base",
    local_files_only=True,
)

start_time = datetime.now(ZoneInfo("Asia/Shanghai"))
output_dir = Path("/root/autodl-tmp/workspace/outputs") / (
    f"b1_base_{args.control_mode}_{start_time:%Y%m%d_%H%M%S}"
)
output_dir.mkdir(parents=True, exist_ok=False)

# 调用官方评估流程:
# 创建环境 → 加载模型与处理器 → 循环执行动作 → 保存结果
command = [
    "lerobot-eval",
    f"--policy.path={checkpoint}",
    "--policy.device=cuda",
    "--policy.dtype=bfloat16",
    "--policy.n_action_steps=10",
    '--policy.normalization_mapping={"VISUAL":"IDENTITY",'
    '"STATE":"MEAN_STD","ACTION":"MEAN_STD"}',
    "--env.type=libero",
    "--env.task=libero_spatial",
    "--env.task_ids=[1]",
    f"--env.control_mode={args.control_mode}",
    "--env.observation_height=256",
    "--env.observation_width=256",
    "--env.init_states=true",
    "--env.hard_reset=true",
    "--env.max_parallel_tasks=1",
    "--eval.batch_size=1",
    "--eval.n_episodes=2",
    "--seed=1000",
    f"--output_dir={output_dir}",
]

print("=== B1 原始模型闭环评估 ===")
print("开始时间:", start_time.isoformat())
print("控制模式:", args.control_mode)
print("输出目录:", output_dir)
print("完整命令:", shlex.join(command), flush=True)

start = time.perf_counter()

# 同时显示并保存日志,报错也会保留下来
with (output_dir / "run.log").open("w") as log:
    log.write(f"开始时间: {start_time.isoformat()}\n")
    log.write(f"完整命令: {shlex.join(command)}\n")
    log.flush()

    process = subprocess.Popen(
        command,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True,
        bufsize=1,
    )

    for line in process.stdout:
        print(line, end="", flush=True)
        log.write(line)
        log.flush()

    return_code = process.wait()

elapsed = time.perf_counter() - start
end_time = datetime.now(ZoneInfo("Asia/Shanghai"))

print("\n=== 运行结束 ===")
print("结束时间:", end_time.isoformat())
print("总运行耗时(含加载模型):", elapsed, "秒")
print("日志:", output_dir / "run.log")

if return_code != 0:
    raise SystemExit(f"评估失败,退出码: {return_code}")

with (output_dir / "eval_info.json").open() as file:
    result = json.load(file)

overall = result["overall"]
print("实际回合数:", overall["n_episodes"])
print("成功率:", overall["pc_success"], "%")
print("评估总耗时:", overall["eval_s"], "秒")
print("平均每回合评估耗时:", overall["eval_ep_s"], "秒")
print("视频路径:", overall["video_paths"])
print("B1_BASE_EVAL_OK")

做了10回合的小规模评估,结果全部失败。


2. 官方微调版本评估

pi05官方在libero上全量微调了6000步,得到了一个官方微调版本pi05_libero_finetuned_v044,用下面的指令对其进行评估:

import os
os.environ["HF_HOME"] = "/root/autodl-tmp/huggingface"
os.environ["HF_HUB_OFFLINE"] = "1"
os.environ["MUJOCO_GL"] = "egl"
os.environ["PYOPENGL_PLATFORM"] = "egl"
os.environ["LIBERO_CONFIG_PATH"] = "/root/autodl-tmp/libero-config"
# 让子进程的输出不缓冲,实时打印日志
os.environ["PYTHONUNBUFFERED"] = "1"

# 解析命令行参数
import argparse
import json
# 把命令列表转换成可复制的 shell 字符串。
import shlex
import subprocess
import time
from datetime import datetime
from pathlib import Path
# 处理时区
from zoneinfo import ZoneInfo

from huggingface_hub import snapshot_download
# 注册一个参数,名字是 --control-mode,值只可选relative\absolute
parser = argparse.ArgumentParser()
parser.add_argument(
    "--control-mode",
    choices=["relative", "absolute"],
    default="relative",
)
args = parser.parse_args()

checkpoint = snapshot_download(
    "lerobot/pi05_libero_finetuned_v044",
    local_files_only=True,
)

start_time = datetime.now(ZoneInfo("Asia/Shanghai"))
output_dir = Path("/root/autodl-tmp/workspace/outputs") / (
    f"b1_official_finetuned_{args.control_mode}_{start_time:%Y%m%d_%H%M%S}"
)
output_dir.mkdir(parents=True, exist_ok=False)

# 调用官方评估流程:
# 创建环境 → 加载模型与处理器 → 循环执行动作 → 保存结果
command = [
    "lerobot-eval",
    f"--policy.path={checkpoint}",
    "--policy.device=cuda",
    "--policy.dtype=bfloat16",
    "--policy.n_action_steps=10",
    "--policy.compile_model=false",
    '--policy.normalization_mapping={"VISUAL":"IDENTITY",'
    '"STATE":"MEAN_STD","ACTION":"MEAN_STD"}',
    "--env.type=libero",
    "--env.task=libero_spatial",
    "--env.task_ids=[0]",
    f"--env.control_mode={args.control_mode}",
    "--env.observation_height=256",
    "--env.observation_width=256",
    "--env.init_states=true",
    "--env.hard_reset=true",
    "--env.max_parallel_tasks=1",
    "--eval.batch_size=1",
    "--eval.n_episodes=10",
    "--seed=1000",
    f"--output_dir={output_dir}",
]

print("=== B1 官方微调模型闭环评估 ===")
print("开始时间:", start_time.isoformat())
print("控制模式:", args.control_mode)
print("输出目录:", output_dir)
print("完整命令:", shlex.join(command), flush=True)

start = time.perf_counter()

# 同时显示并保存日志,报错也会保留下来
with (output_dir / "run.log").open("w") as log:
    log.write(f"开始时间: {start_time.isoformat()}\n")
    log.write(f"完整命令: {shlex.join(command)}\n")
    log.flush()

    process = subprocess.Popen(
        command,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True,
        bufsize=1,
    )

    for line in process.stdout:
        print(line, end="", flush=True)
        log.write(line)
        log.flush()

    return_code = process.wait()

elapsed = time.perf_counter() - start
end_time = datetime.now(ZoneInfo("Asia/Shanghai"))

print("\n=== 运行结束 ===")
print("结束时间:", end_time.isoformat())
print("总运行耗时(含加载模型):", elapsed, "秒")
print("日志:", output_dir / "run.log")

if return_code != 0:
    raise SystemExit(f"评估失败,退出码: {return_code}")

with (output_dir / "eval_info.json").open() as file:
    result = json.load(file)

overall = result["overall"]
print("实际回合数:", overall["n_episodes"])
print("成功率:", overall["pc_success"], "%")
print("评估总耗时:", overall["eval_s"], "秒")
print("平均每回合评估耗时:", overall["eval_ep_s"], "秒")
print("视频路径:", overall["video_paths"])
print("B1_OFFICIAL_FINETUNED_EVAL_OK")

10回合评估结果为10/10,可见官方微调的版本效果很不错。结果也大致符合LeRobot框架下 Pi0.5 在 LIBERO 环境中的仿真复现与微调实践(含完整 Debug 记录)_lerobot pi0.5-CSDN博客

参考文章中记录的结果:


3. 自己微调一个版本,做同样的评估

用上之前的LoRA方法,训练5000步:

source /etc/network_turbo
source /root/miniconda3/etc/profile.d/conda.sh
conda activate lerobot

export HF_HOME=/root/autodl-tmp/huggingface
export HF_HUB_OFFLINE=0
export HF_HUB_DISABLE_XET=1

cd /root/autodl-tmp/workspace
mkdir -p logs

# 每次生成新名称,避免覆盖已有训练结果
B1_RUN_NAME="b1_black_bowl_lora_5000steps_$(date +%Y%m%d_%H%M%S)"

# 保留训练退出状态,同时将输出保存到日志
set -o pipefail

{
  time -p lerobot-train \
    --dataset.repo_id=lerobot/libero \
    --dataset.root=/root/autodl-tmp/huggingface/hub/datasets--lerobot--libero/snapshots/a1aaacb7f6cd6ee5fb43120f673cebb0cfea7dd4 \
    --dataset.episodes='[1272,1273,1275,1282,1300,1327,1330,1344,1347,1352,1368,1379,1382,1395,1397,1401,1410,1425,1449,1450,1491,1493,1500,1504,1506,1517,1527,1532,1560,1563,1567,1568,1574,1579,1583,1589,1597,1602,1615,1617,1642,1660,1669,1672,1679]' \
    --dataset.video_backend=pyav \
    --policy.type=pi05 \
    --policy.pretrained_path=/root/autodl-tmp/huggingface/hub/models--lerobot--pi05_libero_base/snapshots/a217bfd3b14673cf2ce597e69997ab21866438dd \
    --policy.normalization_mapping='{"ACTION":"MEAN_STD","STATE":"MEAN_STD","VISUAL":"IDENTITY"}' \
    --policy.n_action_steps=10 \
    --policy.empty_cameras=1 \
    --policy.freeze_vision_encoder=true \
    --policy.train_expert_only=true \
    --policy.gradient_checkpointing=true \
    --policy.compile_model=false \
    --policy.dtype=bfloat16 \
    --policy.device=cuda \
    --policy.push_to_hub=false \
    --policy.optimizer_lr=2.5e-5 \
    --policy.scheduler_warmup_steps=100 \
    --policy.scheduler_decay_steps=5000 \
    --peft.method_type=LORA \
    --peft.r=16 \
    --peft.lora_alpha=16 \
    --output_dir="outputs/$B1_RUN_NAME" \
    --job_name="$B1_RUN_NAME" \
    --batch_size=2 \
    --num_workers=0 \
    --steps=5000 \
    --log_freq=50 \
    --save_freq=1000 \
    --seed=1000 \
    --wandb.enable=false
} 2>&1 | tee "logs/$B1_RUN_NAME.log"

之后再用这个微调之后的版本跑评估:

source /root/miniconda3/etc/profile.d/conda.sh
conda activate lerobot

export HF_HOME=/root/autodl-tmp/huggingface
export HF_HUB_OFFLINE=1
export MUJOCO_GL=egl
export PYOPENGL_PLATFORM=egl
export LIBERO_CONFIG_PATH=/root/autodl-tmp/libero-config

cd /root/autodl-tmp/workspace

# 使用刚才训练的最终 checkpoint
# 如果换过终端,需要先设置 B1_RUN_NAME 为本次训练文件夹的实际名称
B1_CHECKPOINT="/root/autodl-tmp/workspace/outputs/b1_black_bowl_lora_5000steps_20261010_120335/checkpoints/last/pretrained_model"

B1_EVAL_NAME="b1_own_lora_relative_$(date +%Y%m%d_%H%M%S)"

mkdir -p logs
set -o pipefail

{
  time -p lerobot-eval \
    --policy.path="$B1_CHECKPOINT" \
    --policy.use_peft=true \
    --policy.device=cuda \
    --policy.dtype=bfloat16 \
    --policy.n_action_steps=10 \
    --policy.compile_model=false \
    --policy.normalization_mapping='{"VISUAL":"IDENTITY","STATE":"MEAN_STD","ACTION":"MEAN_STD"}' \
    --env.type=libero \
    --env.task=libero_spatial \
    --env.task_ids='[0]' \
    --env.control_mode=relative \
    --env.observation_height=256 \
    --env.observation_width=256 \
    --env.init_states=true \
    --env.hard_reset=true \
    --env.max_parallel_tasks=1 \
    --eval.batch_size=1 \
    --eval.n_episodes=10 \
    --seed=1000 \
    --output_dir="outputs/$B1_EVAL_NAME"
} 2>&1 | tee "logs/$B1_EVAL_NAME.log"

10回合评估结果为7/10,在原本的基础上有进度,较官方微调的版本相比还有差距,目前认为原因在于训练方法,LoRA微调的效果确实不如全量微调。


总结

本文介绍了官方基座、官方微调版本、自己的LoRA微调模型在libero数据集上的评估结果,由此验证LoRA微调的有效性。

文章仅做学习分享,如有谬误,欢迎指正。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐