Trong thế giới Vision-Language-Action (VLA) đang chạy đua scale — OpenVLA 7B, π0 3B, RDT 1B — thì OpenHelix Team đến từ Trung Quốc đăng lên arXiv một paper rất "ngược dòng": VLA-Adapter chỉ dùng backbone Qwen2.5-0.5B (nửa tỷ tham số, tức là nhỏ hơn 14× so với OpenVLA), không cần robotic pre-training, vẫn đạt 99.2% LIBERO-Object và 99.6% LIBERO-Spatial ở phiên bản Pro. Đặc biệt: train trong 8 giờ trên 1 con RTX 4090 hoặc thậm chí 9.6GB VRAM trên RTX 3060.
Đây là tin tốt cho mọi kỹ sư robotics Việt Nam đang muốn nhập cuộc VLA mà không có cluster A100. Bài này mình sẽ đi từ ý tưởng paper → kiến trúc Bridge Attention → cài đặt từng bước → train trên LIBERO → deploy thật lên Franka/UR-5.
Tại sao paper này quan trọng
Trước VLA-Adapter, ngành VLA đi theo "scaling hypothesis": muốn manipulation tốt thì phải có backbone lớn (≥7B) và phải pre-train trên hàng triệu trajectory robot (OXE dataset, DROID). Hệ quả:
- Chi phí training: OpenVLA cần ~8 ngày trên 64 A100. π0 cần TPU pod. Người chơi nhỏ chỉ có thể fine-tune, không thể tự train from scratch.
- VRAM inference: 7B model với fp16 = 14GB chỉ để load weights. Cộng thêm KV cache → khó deploy trên Jetson Orin (8-16GB).
- Latency: Action chunking trên 7B → 5-10Hz, giới hạn manipulation tốc độ cao.
OpenHelix đặt câu hỏi ngược lại: liệu chúng ta có thực sự cần backbone lớn không, hay chỉ cần biết cách "rút" condition từ VLM một cách thông minh?
Câu trả lời của paper: với một Policy module nhẹ có Bridge Attention, một backbone 0.5B đã đủ để vượt OpenVLA-OFT (7B) trên LIBERO. Paper gốc tại arXiv:2509.09372 — Wang et al., 2025.
Ý tưởng cốt lõi: Bridge Attention
Các VLA model truyền thống có 2 cách bridge giữa VLM và action head:
- Token-as-action (OpenVLA, VLA-0): Coi action như token đặc biệt, dùng autoregressive decoding. Chậm vì phải sinh từng token.
- Feature-as-condition (π0, RDT, OpenVLA-OFT): Lấy hidden state của VLM làm condition cho diffusion/flow policy. Nhanh hơn nhưng vẫn cần backbone lớn.
VLA-Adapter chọn hướng 2 nhưng có 3 cải tiến quan trọng:
1. ActionQuery tokens — Trainable queries
Tác giả thêm 64 learnable token (ActionQuery) vào input của VLM, train từ scratch. Các token này như "câu hỏi" mà policy module gửi cho VLM: "Cần action gì cho cảnh này?". VLM trả lời bằng cách update các token này qua self-attention layers.
2. Multi-layer feature injection
Khác với việc chỉ lấy hidden state ở layer cuối, VLA-Adapter inject feature ở nhiều layer khác nhau vào policy:
- Raw features (vision + language) lấy từ middle layers (semantic bias ít hơn deep layers).
- ActionQuery features lấy từ deep layers (đã được "filter" qua nhiều layer attention).
Ablation cho thấy multi-layer beat single-layer khoảng 2% success rate.
3. Bridge Attention với injection degree học được
Đây là innovation chính. Thay vì cộng trực tiếp condition vào policy, VLA-Adapter dùng cross-attention có gating:
# Pseudo-code Bridge Attention
gate_raw = sigmoid(W_g @ raw_features) # learnable injection ratio
gate_query = 1.0 # ActionQuery: full inject
policy_input = gate_raw * raw_features + gate_query * action_query_features
action = diffusion_head(policy_input, noisy_action)
Kết quả: Raw features cần inject có kiểm soát (vì có semantic bias từ pre-training image-text), còn ActionQuery features fully inject (vì đã được train riêng cho action).
Số liệu kết quả
| Suite | OpenVLA-OFT (7B) | π0 (3B) | VLA-Adapter (0.5B) | VLA-Adapter-Pro (0.5B) |
|---|---|---|---|---|
| LIBERO-Spatial | 97.6% | 96.8% | 97.8% | 99.6% |
| LIBERO-Object | 98.4% | 98.8% | 99.2% | 99.6% |
| LIBERO-Goal | 97.9% | 95.8% | 97.2% | 98.2% |
| LIBERO-Long | 94.5% | 85.2% | 95.0% | 96.4% |
| Average | 97.1% | 94.2% | 97.3% | 98.5% |
Điểm đáng kinh ngạc: với backbone đóng băng (frozen), VLA-Adapter vẫn đạt 86.4% LIBERO-Long, trong khi đối thủ chỉ 0%. Chứng tỏ Bridge Attention đã "rút" được condition đủ tốt, không cần fine-tune VLM.
Về inference speed, VLA-Adapter báo cáo fast inference speed so với mọi VLA cùng class, do chỉ chạy 1 forward pass qua 0.5B model + 8-step diffusion policy (97M params).
Cài đặt từ đầu
Yêu cầu hệ thống
- OS: Ubuntu 22.04 (mình test trên 20.04 cũng OK)
- CUDA: 11.8 hoặc 12.1
- GPU: Tối thiểu RTX 2080Ti 11GB (batch=1, LoRA rank=64)
- Disk: ~50GB cho dataset + checkpoint
Bước 1: Tạo conda env
conda create -n vla-adapter python=3.10.16 -y
conda activate vla-adapter
git clone https://github.com/OpenHelix-Team/VLA-Adapter.git
cd VLA-Adapter
pip install -e .
# Flash-attention — bản 2.5.5 đã được tác giả test
pip install "flash-attn==2.5.5" --no-build-isolation
Nếu pip install flash-attn fail (thường gặp với RTX 3060/3080), thử:
pip install ninja
MAX_JOBS=4 pip install "flash-attn==2.5.5" --no-build-isolation
Bước 2: Tải backbone Prismatic + Qwen2.5-0.5B
mkdir -p pretrained_models
cd pretrained_models
# Download từ HuggingFace
git lfs install
git clone https://huggingface.co/OpenHelix-Team/prism-qwen25-extra-dinosiglip-224px-0_5b
Backbone này dùng:
- Vision encoder: DINOv2 + SigLIP (concatenated patches)
- Resolution: 224×224
- Language model: Qwen2.5-0.5B-Instruct
- Projector: MLP 2-layer
Bước 3: Tải LIBERO dataset (~10GB)
cd VLA-Adapter
mkdir -p data && cd data
# Tải 4 suites
wget https://huggingface.co/datasets/openvla/modified_libero_rlds/resolve/main/libero_spatial.zip
wget https://huggingface.co/datasets/openvla/modified_libero_rlds/resolve/main/libero_object.zip
wget https://huggingface.co/datasets/openvla/modified_libero_rlds/resolve/main/libero_goal.zip
wget https://huggingface.co/datasets/openvla/modified_libero_rlds/resolve/main/libero_10.zip # = LIBERO-Long
for f in *.zip; do unzip "$f"; done
Train trên LIBERO-Object (đạt 99.2%)
Cấu hình theo VRAM
| VRAM | Batch size | LoRA rank | GPU phổ biến |
|---|---|---|---|
| 9.6GB | 1 | 64 | RTX 2080Ti 11GB, 3060 12GB |
| 24GB | 4 | 64 | RTX 3090, 4090 |
| 40-48GB | 8 | 64 | A100-40GB, RTX 5090 |
| ≥80GB | 16 | 64 | A100-80GB, H100 |
Command train (single GPU 24GB)
CUDA_VISIBLE_DEVICES=0 torchrun --standalone --nnodes 1 --nproc-per-node 1 \
vla-scripts/finetune.py \
--vlm_path pretrained_models/prism-qwen25-extra-dinosiglip-224px-0_5b \
--data_root_dir data \
--dataset_name libero_object \
--run_root_dir outputs \
--run_id LIBERO-Object-Pro \
--batch_size 4 \
--grad_accumulation_steps 1 \
--learning_rate 5e-4 \
--max_steps 200005 \
--save_freq 10000 \
--use_pro_version True \
--lora_rank 64 \
--image_aug True
Trên RTX 4090, 200k steps ≈ 8 giờ. Với batch=1 trên RTX 3060, ước tính ~30-36 giờ — vẫn khả thi để chạy qua đêm + 1 ngày.
Theo dõi quá trình train
tensorboard --logdir outputs/LIBERO-Object-Pro/runs --port 6006
Các metric quan trọng:
loss/action: Phải giảm từ ~0.6 xuống ~0.05 sau 50k steps.loss/diffusion: Tương tự, giảm liên tục.lr: Warmup 1000 steps → cosine decay.
Nếu loss plateau quá sớm (trước 30k steps), thường do:
- Image augmentation chưa bật → bật
--image_aug True. - LoRA rank quá thấp → thử rank 128.
- Learning rate quá thấp → tăng lên 1e-3.
Inference & Evaluation
Evaluate trên LIBERO simulator
CUDA_VISIBLE_DEVICES=0 python experiments/robot/libero/run_libero_eval.py \
--pretrained_checkpoint outputs/LIBERO-Object-Pro/checkpoint-200000 \
--task_suite_name libero_object \
--use_pro_version True \
--num_trials_per_task 50
Kỳ vọng: success rate ≈ 99.2-99.6%. Nếu thấp hơn 95%, check:
- Checkpoint có load đúng? Xem log có warning về key mismatch không.
- LIBERO version: dùng đúng commit
b5d8e0a(link trong README).
Real-time inference loop
from vla_adapter import VLAAdapter
# Load model
vla = VLAAdapter.from_pretrained(
"outputs/LIBERO-Object-Pro/checkpoint-200000",
device="cuda",
use_pro_version=True
)
# Inference
import torch
from PIL import Image
image = Image.open("camera_frame.jpg").resize((224, 224))
instruction = "pick up the red cup and place it on the plate"
with torch.no_grad():
action_chunk = vla.predict_action(
image=image,
instruction=instruction,
action_chunk_size=8 # predict 8 future actions
)
# action_chunk: shape [8, 7] for 7-DOF arm (xyz + rpy + gripper)
print(action_chunk)
Tốc độ inference trên RTX 4090: ~30-50Hz (action chunk 8) — đủ cho manipulation tốc độ trung bình.
Deploy thật lên Franka / UR-5 / ALOHA
Tháng 3/2026, tác giả thêm support Cobot Magic (ALOHA-style) cho deployment thật. Code nằm trong experiments/robot/aloha/. Workflow tương tự cho Franka/UR-5:
Bước 1: Calibrate camera
Đặt camera RGB ở góc workspace (top-down hoặc 45°). Resize input về 224×224. Quan trọng: giữ góc camera giống với data train, nếu lệch nhiều thì performance giảm mạnh.
Bước 2: Adapter cho action space
VLA-Adapter output là delta end-effector pose (7-DOF: xyz delta + axis-angle + gripper). Với Franka:
# Franka via libfranka
from franka_interface import FrankaArm
arm = FrankaArm()
current_pose = arm.get_pose() # 4x4 matrix
# Convert VLA delta to absolute target
delta_xyz = action_chunk[0][:3] * 0.05 # scale 5cm/step
delta_rot = action_chunk[0][3:6] # axis-angle
gripper_cmd = action_chunk[0][6] # 0=close, 1=open
target_pose = current_pose @ delta_to_homog(delta_xyz, delta_rot)
arm.move_to_pose(target_pose, duration=0.05)
arm.set_gripper(gripper_cmd)
Bước 3: Safety wrapper
Bắt buộc:
- Workspace bounds: Clip target pose trong vùng an toàn (vd: xy ∈ [-0.5, 0.5], z ∈ [0.05, 0.5]).
- Force/torque limit: Stop arm khi gặp lực >20N.
- Emergency stop: Hotkey ESC hoặc physical button.
Bước 4: Fine-tune trên data thật (nếu cần)
LIBERO checkpoint có thể zero-shot deploy được, nhưng để đạt success rate cao trên task mới, nên collect ~50-100 demo trajectory rồi fine-tune thêm 20k steps. Format data theo RLDS — xem hướng dẫn trong hướng dẫn LeRobot tutorial cho cách collect demo.
Tradeoffs & Pitfalls
Ưu điểm:
- Train 8 giờ trên 1 GPU consumer → ai cũng làm được.
- 0.5B model = deploy được trên Jetson Orin AGX (32GB).
- License MIT → dùng cho commercial OK.
Nhược điểm:
- Generalization OOD chưa được test rộng rãi — paper chủ yếu chạy LIBERO/CALVIN.
- Không có pre-training trên robotic data → có thể yếu hơn π0/RDT cho task chưa từng thấy.
- Real-world deployment chỉ verify trên Cobot Magic, chưa có benchmark Franka/UR-5 public.
Pitfall mình hay gặp:
- Flash-attention version sai → train chậm 5×. Bám đúng version 2.5.5.
- Quên
--use_pro_version True→ kết quả thấp hơn ~2%. Luôn dùng Pro. - Image resolution sai — phải đúng 224×224, không phải 256 hay 384.
- Dataset format — RLDS phải đúng commit của OpenVLA modified, không dùng LIBERO raw.
So sánh nhanh với các VLA khác
So với VLA-0 của NVIDIA (cũng dùng Qwen nhưng action-as-text), VLA-Adapter nhanh hơn vì không cần autoregressive decoding. So với OpenHelix Dual-System VLA (cũng cùng team), VLA-Adapter đơn giản hơn — single backbone thay vì fast+slow system. So với SimpleVLA-RL (RL fine-tune), VLA-Adapter dùng IL thuần — không cần reward design.
Nếu mục tiêu là prototype nhanh, train trên consumer GPU, deploy edge, VLA-Adapter hiện là lựa chọn hàng đầu. Nếu cần generalization cross-task tốt nhất, vẫn nên dùng π0/π0-FAST với backbone 3B.
Kết luận
VLA-Adapter chứng minh "scale không phải tất cả". Bằng cách thiết kế Bridge Attention thông minh, một model 0.5B có thể đánh bại 7B model trên LIBERO. Đây là bài học lớn cho ngành: trước khi scale lên 70B, hãy hỏi liệu kiến trúc hiện tại đã tận dụng hết feature của backbone chưa.
Với người Việt Nam, ý nghĩa thực tế là: bạn có thể bắt đầu nghiên cứu VLA chỉ với 1 con RTX 4090 (~50 triệu đồng) thay vì cluster A100 (~tỷ đồng). Ngưỡng nhập cuộc đã được kéo xuống rất thấp.