DreamZero-DROID — NVIDIA GEAR Lab 的 14B 世界动作模型(World Action Model, WAM), 基于 Wan2.1-I2V-14B-480P 视频扩散骨干从头训练(仅用 DROID 数据集), 联合预测未来视频帧与机器人动作。本项目将其从 CUDA 环境迁移到 华为昇腾 Ascend910 NPU,并基于 FastAPI 提供服务化推理(HTTP 接口)。
| 项目 | 内容 |
|---|---|
| 模型 | DreamZero-DROID(GRoot VLA 自定义架构,22.92B 参数,61GB 权重) |
| 任务 | 视频+动作联合预测(世界动作模型):观测帧+语言指令+本体状态 → 24 步动作 |
| 输入 | 4 帧 480×640 观测(单帧自动复制)+ 英文指令 + 本体状态(≤64 维) |
| 输出 | action_pred (1,24,32) 动作序列 + video_pred 预测视频 latent |
| 硬件 | Ascend910(本环境 16 卡,部署使用物理 NPU1,bf16 占 47.5GB) |
| 软件 | CANN 8.5.1 / Python 3.11 / PyTorch 2.9.0 / torch_npu 2.9.0.post1 |
| 推理服务 | FastAPI + uvicorn(HTTP 服务化推理) |
| 测试结果 | 5/5 用例通过,单次推理约 9s |
DreamZero-DROID-npu/
├── inference.py # 服务化推理脚本(FastAPI HTTP 服务)★ 核心交付
├── test_inference.py # 测试用例(5 项,HTTP 模式)
├── requirements.txt # 运行环境依赖清单
├── README.md # 本文档
├── repo/ # GRoot 推理框架源码(已含全部 NPU 适配补丁)
│ ├── groot/ # VLA 模型实现(dreamzero 模块)
│ ├── eval_utils/ # 策略服务器接口
│ ├── socket_test_optimized_AR.py # 官方 WebSocket 服务器(多卡 DDP)
│ └── ...
├── test_results/
│ └── test_result.txt # 测试结果记录
└── assets/
├── umt5-xxl/ # umt5 分词器(sentencepiece,离线可用)
├── npu_device_call.png # 截图1:NPU 设备调用
├── model_result.png # 截图2:模型测试结果(动作热力图)
├── agent_workflow.png # 截图3:适配过程工作流
└── _make_screenshots.py # 截图生成脚本(可复现)| 依赖 | 版本 | 说明 |
|---|---|---|
| 操作系统 | openEuler / Ubuntu(aarch64) | 本机 openEuler 2203 SP4 |
| 昇腾硬件 | Ascend910(≥1 卡,≥64GB HBM) | bf16 权重 28GB + 激活 19GB ≈ 47.5GB |
| CANN | ≥ 8.0(本机 8.5.1) | /usr/local/Ascend/ascend-toolkit/set_env.sh |
| Python | 3.11 | — |
| PyTorch | 2.9.0 | 与 torch_npu 版本配套 |
| torch_npu | 2.9.0.post1 | 昇腾 NPU 运行时插件 |
| GRoot 框架 | 本目录 repo/(已打 NPU 补丁) | 通过 PYTHONPATH 引用,无需 pip 安装 |
模型权重:
/mnt/old_data/whl/models/6/GEAR-Dreams/DreamZero-DROID(61GB,10 分片)。 若分片缺失(如 model-00008),从 hf-mirror 补下:curl -L -o model-00008-of-00010.safetensors "https://hf-mirror.com/GEAR-Dreams/DreamZero-DROID/resolve/main/model-00008-of-00010.safetensors"
# 1) 加载 CANN 环境
source /usr/local/Ascend/ascend-toolkit/set_env.sh
# 2) 安装依赖(华为镜像源)
export PIP_INDEX_URL=https://repo.huaweicloud.com/repository/pypi/simple/
pip install -r requirements.txt
# 3) 校验 NPU 环境
npu-smi info
python -c "import torch, torch_npu; a=torch.randn(3,4).npu(); print(a+a)"DreamZero 为 NVIDIA GRoot 自定义 VLA 架构(非 transformers 标准模型),官方代码 为 CUDA/TensorRT 环境编写。迁移到昇腾 NPU 的适配点如下:
| # | 适配点 | 说明 |
|---|---|---|
| 1 | 权重分片补齐 | 原始下载缺 model-00008-of-00010.safetensors(4.9GB),从 hf-mirror 补下 |
| 2 | CUDA 硬编码 | 8 处 device='cuda'(VAE mean/std、DiT freqs、scheduler、噪声生成),transfer_to_npu 不覆盖 tensor 构造参数,全部改为 device='npu' |
| 3 | flash-attn → SDPA | 两个 attention 模块在无 flash-attn 时崩溃;wan2_1_attention.py 有现成 sdpa fallback(_gpu_supports_flash_attention 自动触发),attention.py 需设 ENABLE_TENSORRT=true 走 sdpa 分支;另修 2 处 assert q.device.type == 'cuda' → ('cuda','npu') |
| 4 | HF 组件下载绕过 | VAE/文本/图像编码器权重尝试从 HF 下载(ensure_file),离线环境崩溃;patch ensure_file 返回 None(三组件权重已含在主 checkpoint:vae 194 键 / model 1317 键 / text_encoder 242 键 / image_encoder 393 键),并在 config.json 设 skip_component_loading: true |
| 5 | tokenizer 兼容 | transformers T5Tokenizer(Fast) 与 umt5-xxl 的 spiece.model 不兼容(Cannot parse precompiled_charsmap,tokenizers 0.22 bug);改用 sentencepiece 直连编码 |
| 6 | 文本长度 | DiT text_len=512 固定,文本 token ids 需 pad/截断到 512(含 <|eos|>) |
| 7 | 输入维度 | state 需 64 维(state_encoder input_dim=max_state_dim=64);action 需 (1,24,32)(validate_inputs 校验) |
| 8 | torch.compile 移除 | scheduler 两个 @torch.compile 装饰器在 NPU 触发 dynamo 崩溃(get_device_capability() >= (9,0)),移除(NPU 无 Triton) |
| 9 | TensorRT 属性 | 推理路径检查 self.trt_engine,属性不存在崩溃;__init__ 补 self.trt_engine = None |
| 10 | 状态重置 | lazy_joint_video_action 跨调用保留 KV cache/语言状态,二次调用 500;每次推理前重置 kv_cache/clip_feas/ys/language/current_start_frame |
其他:官方推理服务器(socket_test_optimized_AR.py)依赖多卡 DDP + roboarena 协议 +
flash-attn,本次交付改为单卡 NPU 直接调用 lazy_joint_video_action + FastAPI 包装。
source /usr/local/Ascend/ascend-toolkit/set_env.sh
python inference.py \
--model-path /mnt/old_data/whl/models/6/GEAR-Dreams/DreamZero-DROID \
--host 0.0.0.0 --port 8008启动日志:
[inference] Loading model (61GB, bf16) -> npu:0 ...
[inference] Model loaded in 265.1s, device=npu:0, HBM used=47.5GB
INFO: Uvicorn running on http://0.0.0.0:8008| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /health | 健康检查,返回设备/参数/显存信息 |
| POST | /infer | 动作/视频预测(image + instruction + state) |
| POST | /v1/predict | OpenAI 风格预测接口 |
健康检查:
curl -s http://127.0.0.1:8008/health
# {"status":"ok","model":"DreamZero-DROID","architecture":"WAM (Wan2.1-I2V-14B backbone)","device":"npu:0","dtype":"torch.bfloat16","params_b":22.92}动作预测(Python 客户端):
import base64, requests
from PIL import Image
# 观测帧(服务自动复制为 4 帧)
img = Image.new("RGB", (640, 480), (200, 200, 200))
buf = ...
r = requests.post("http://127.0.0.1:8008/infer", json={
"image": "data:image/jpeg;base64,...",
"instruction": "pick up the red mug and place it on the table",
"state": [0.0] * 64,
}, timeout=1200)
print(r.json())返回示例:
{
"action_pred": [[0.02, -0.13, ...], ...], // 24×32 动作序列
"action_stats": {"shape": [1, 24, 32], "mean": 0.1091, "std": 1.1379},
"video_pred_shape": [1, 16, 3, 60, 80], // 预测视频 latent
"latency_ms": 9000.0
}python test_inference.py --url http://127.0.0.1:8008 # 完整模式(5 次推理)
python test_inference.py --url http://127.0.0.1:8008 --skip-range # 快速模式用例覆盖:
| 用例 | 内容 | 结果 |
|---|---|---|
| T1 | 健康检查 /health,device=npu、22.92B 参数 | ✅ PASS |
| T2 | 动作预测:action_pred (1,24,32) 且数值有限 | ✅ PASS |
| T3 | 输出统计:mean/std 合理范围 | ✅ PASS |
| T4 | OpenAI 风格接口:/v1/predict | ✅ PASS |
| T5 | 稳定性:连续两次调用均 200 | ✅ PASS |
完整测试结果见 test_results/test_result.txt,汇总:5 通过 / 0 失败。
| 截图 | 文件 | 说明 |
|---|---|---|
| NPU 设备调用 | assets/npu_device_call.png | npu-smi info + torch_npu 探针 + 部署摘要 |
| 测试结果 | assets/model_result.png | 动作序列热力图(24×32)+ 逐步范数 + 延迟指标 + 测试日志 |
| 适配过程 | assets/agent_workflow.png | 迁移适配工作流(10 步,含 7 个修复点) |
截图可通过 python assets/_make_screenshots.py 重新生成(需服务运行中,约 10s 一次推理)。
| 问题 | 原因 | 解决方案 |
|---|---|---|
加载报 LocalEntryNotFoundError | 组件尝试从 HF 下载 | 已 patch ensure_file 离线返回 None;确认 HF_HUB_OFFLINE=1 |
Torch not compiled with CUDA enabled | device='cuda' 硬编码 | 已全局替换为 device='npu' |
flash_attn 相关错误 | NPU 无 flash-attn | ENABLE_TENSORRT=true 走 sdpa;assert q.device.type 已放宽 |
Cannot parse precompiled_charsmap | tokenizers 0.22 与 umt5 spiece 不兼容 | 用 sentencepiece 直连编码 |
context.shape[1] == self.text_len 断言 | 文本未 pad | pad/截断到 512(服务已处理) |
| bmm 维度不匹配(32 vs 64) | state 维度错误 | state 需 64 维(服务已处理) |
| dynamo/Triton 相关崩溃 | @torch.compile 装饰器 | 已移除(scheduler 两处) |
has no attribute 'trt_engine' | TensorRT 分支检查 | 已补默认属性 |
| 第二次调用 500 | KV cache 跨调用残留 | 服务已内置状态重置 |
| 显存不足(OOM) | 与其他服务争卡 | 部署独立 NPU(本交付用物理 NPU1),单卡 ≥64GB |
repo/ 已含 NPU 补丁)