NS-VLA v2 là một hướng rất đáng học nếu bạn đang fine-tune Vision-Language-Action model cho manipulation nhưng thấy policy thuần neural dễ lẫn lộn thứ tự hành động. Thay vì để VLA trực tiếp dự đoán một chuỗi action liên tục từ ảnh và câu lệnh, paper tách task thành neuro-symbolic primitives: move, pick, place, push, open, close, release, kèm object argument và một low-level solver sinh action chunk. Cách tách này làm policy giống một robot engineer có checklist: trước hết chọn primitive đúng, sau đó mới điều khiển tay máy theo primitive đó.
Paper gốc là NS-VLA: Neuro-Symbolic Vision-Language-Action Model for Embodied Reasoning and Manipulation — arXiv v2, 2026. Code và checkpoint nằm ở repo Zuzuzzy/NS-VLA, model ở Hugging Face Zuzuzzy/NS-VLA, dataset ở Zuzuzzy/NS-VLA-Dataset. Bài này đi theo hướng thực hành: hiểu ý tưởng, đọc kiến trúc, chuẩn bị môi trường, chạy BC warmup, fine-tune bằng GRPO/AWR trên LIBERO, rồi inference và đọc kết quả.
Nếu bạn mới bước vào VLA, nên đọc trước OpenVLA: VLA mở cho robot để nắm input ảnh + instruction + action vector. Nếu đã quen RL cho VLA, bài TGRPO: Fine-tune VLA với Trajectory GRPO sẽ giúp đặt NS-VLA v2 vào cùng bức tranh.
NS-VLA giải quyết vấn đề gì?
VLA manipulation thường thất bại ở ba điểm. Một là long-horizon reasoning: câu lệnh như "đặt bát bên phải vào khay rồi đóng ngăn kéo" có nhiều bước, nhưng action vector từng frame không nói rõ bước hiện tại thuộc ý định nào. Hai là object binding: model nhìn nhiều vật thể giống nhau và nhầm "bowl bên trái" với "bowl bên phải". Ba là RL credit assignment: reward cuối episode chỉ nói thành công/thất bại, không nói primitive nào sai.
NS-VLA đặt một lớp symbolic ở giữa. Thay vì học thẳng:
image + language + proprioception -> continuous action
model học:
image + language + state -> symbolic primitive + argument
symbolic primitive + argument + state -> action chunk
Primitive không phải planner cổ điển cứng nhắc. Nó vẫn được chọn bởi neural policy từ ảnh và ngôn ngữ. Điểm khác là output ở high-level có cấu trúc, nên dễ supervise bằng trace, dễ kiểm tra, và dễ fine-tune bằng RL. Với beginner, hãy xem primitive như "macro action" có tên rõ ràng: pick(red_mug), place(red_mug, tray), open(drawer). Low-level solver mới chịu trách nhiệm biến macro đó thành delta pose, gripper command và action chunk cho robot.

Hình pipeline của project cho thấy hai tầng chính. Encoder đọc observation và instruction để tạo latent state. Monotone pointer chọn primitive theo thứ tự tiến triển của task. Primitive solver nhận primitive, target object và robot state để dự đoán hành động liên tục. Đây là lý do paper gọi mô hình là neuro-symbolic: phần nhận thức và chọn primitive là neural, còn cấu trúc primitive/argument tạo ra ràng buộc symbolic.
Kiến trúc: encoder, pointer, solver
Ở mức triển khai, NS-VLA v2 có ba module cần hiểu.
1. Multi-modal encoder. Encoder nhận RGB image, text instruction và proprioception. Tùy config, backbone vision-language có thể dựa trên VLA pretrained như OpenVLA hoặc một encoder VLM tương thích. Mục tiêu của encoder không chỉ là nhận biết vật thể mà còn tạo embedding đủ giàu để tách "đang làm bước nào" trong task dài. Nếu dataset có camera cố định và wrist camera, bạn nên giữ nguyên thứ tự camera như code yêu cầu; đổi thứ tự camera là một lỗi rất khó debug vì model vẫn chạy nhưng attention học sai viewpoint.
2. Symbolic primitive pointer. Paper dùng cơ chế pointer đơn điệu để map progress của episode vào danh sách primitive. "Monotone" ở đây rất quan trọng: robot thường không nên quay ngược từ place về pick nếu task đã đặt xong vật thể. Pointer vì vậy đóng vai trò như một progress tracker. Nó giảm không gian search so với việc để policy tự do sinh mọi primitive ở mọi thời điểm.
3. Low-level primitive solver. Solver nhận primitive hiện tại và sinh action chunk. Với manipulation trong LIBERO, action thường là delta end-effector pose cộng gripper command. Action chunk giúp inference ổn định hơn: model không phải gọi lại full VLA cho từng bước cực nhỏ, mà dự đoán một đoạn ngắn, ví dụ chunk_H=8, rồi môi trường thực thi dần. Khi có sai lệch, vòng inference kế tiếp quan sát lại ảnh và cập nhật primitive.
Bạn có thể hình dung một episode "pick the red mug and place it in the cabinet" như sau:
Instruction: "Put the red mug in the cabinet"
Step 0-18: move(red_mug)
Step 19-35: pick(red_mug)
Step 36-70: move(cabinet)
Step 71-92: place(red_mug, cabinet)
Step 93-110: release(red_mug)
Với behavior cloning thuần túy, model chỉ thấy state-action. Với NS-VLA, training thấy thêm nhãn primitive nên biết đoạn 19-35 là "đang pick", không chỉ là "đang đổi tọa độ end-effector".
Stage I: BC warmup để học primitive trace
Training bắt đầu bằng behavior cloning. Đây là bước không nên bỏ qua. GRPO/AWR chỉ ổn khi policy ban đầu đã đủ tốt để tạo rollout có ý nghĩa. Nếu policy ngẫu nhiên, rollout toàn thất bại, advantage nhiễu, update RL sẽ làm model lệch khỏi demonstration.
Trong repo, quy trình chuẩn là chuẩn bị dữ liệu 1-shot từ LIBERO:
git clone https://github.com/Zuzuzzy/NS-VLA.git
cd NS-VLA
conda create -n nsvla python=3.10 -y
conda activate nsvla
pip install -e .
pip install -r requirements.txt
python data/prepare_1shot.py \
--suite libero_spatial \
--output_dir data/libero_1shot
Bạn cần cài LIBERO và MuJoCo theo hướng dẫn của repo/paper. Trên máy local, hãy xác nhận trước:
python -c "import mujoco, libero; print('env ok')"
python -c "import torch; print(torch.cuda.is_available())"
Với beginner, lỗi hay gặp nhất không nằm ở model mà ở dataset path. Hãy đặt biến môi trường rõ ràng:
export LIBERO_DATA_DIR=/path/to/LIBERO/datasets
export NSVLA_DATA_DIR=$PWD/data/libero_1shot
export WANDB_MODE=offline
Sau đó chạy BC warmup theo script của repo:
bash scripts/train.sh \
--stage bc \
--suite libero_spatial \
--data_dir $NSVLA_DATA_DIR \
--output_dir runs/ns-vla-bc-spatial \
--batch_size 8 \
--epochs 10
Tên flag có thể thay đổi theo commit, nên nguyên tắc là đọc scripts/train.sh và config đi kèm trước khi chạy trên cluster. Điều quan trọng về mặt kỹ thuật là checkpoint BC phải học được hai loss: primitive classification/pointer loss và action regression loss. Nếu primitive accuracy thấp, RL sau đó không cứu được nhiều vì policy không biết đang ở giai đoạn nào của task.
Stage II: GRPO cho high-level, AWR cho low-level
Điểm mới đáng chú ý của NS-VLA v2 là cách fine-tune RL tách theo tầng. High-level primitive được tối ưu bằng GRPO vì nó là quyết định rời rạc, có thể so sánh nhiều rollout trong cùng một nhóm. Low-level action solver được tối ưu bằng AWR vì action liên tục cần update mềm, ổn định và ít phá checkpoint BC.
GRPO, viết đầy đủ là Group Relative Policy Optimization, tính advantage tương đối trong một nhóm rollout. Thay vì cần critic riêng như PPO, GRPO so sánh reward của từng sample với trung bình/độ lệch chuẩn của nhóm. Trong robotics, điều này hữu ích khi reward noisy nhưng bạn có thể chạy nhiều rollout cùng task. Repo/paper dùng tinh thần cấu hình như group_size=8: cùng một task, cùng một initial seed hoặc cùng distribution, sinh 8 rollout rồi hỏi rollout nào tốt hơn.
AWR, Advantage Weighted Regression, dùng advantage để cân trọng số behavior cloning. Nếu một action chunk trong rollout dẫn đến tiến triển tốt, loss clone action đó mạnh hơn. Nếu chunk dẫn đến kẹt hoặc rơi vật thể, trọng số giảm. Vì AWR vẫn là regression quanh dữ liệu policy đã sinh, nó ít "giật" hơn policy gradient trực tiếp trên action liên tục.
Một cách đọc pipeline Stage II:
1. Load BC checkpoint.
2. Với mỗi LIBERO task, sinh group 8 rollout.
3. Tính reward theo success, subgoal progress và primitive completion.
4. GRPO update primitive pointer/high-level decision.
5. AWR update solver/action chunk.
6. Giữ KL anchor để model không trôi quá xa checkpoint BC.
7. Eval định kỳ trên LIBERO-Spatial/Object/Goal/Long.
Lệnh chạy có dạng:
bash scripts/train.sh \
--stage rl \
--suite libero_spatial \
--ckpt runs/ns-vla-bc-spatial/checkpoint-last \
--output_dir runs/ns-vla-rl-spatial \
--group_size 8 \
--chunk_H 8 \
--algo grpo_awr \
--kl_coef 0.02
Nếu bạn chỉ có một GPU consumer, hãy giảm batch size và số environment song song trước, không giảm group_size quá thấp. GRPO cần so sánh trong group; group quá nhỏ làm advantage kém ổn định. Nếu GPU memory vẫn thiếu, dùng gradient accumulation, LoRA/QLoRA hoặc train từng suite. Với bài toán production, tôi thường tách ba run: một run debug 100 episode, một run reproduce theo config paper, một run ablation cho task riêng.

Reward và logging cần theo dõi
Đừng chỉ nhìn success rate cuối cùng. Với neuro-symbolic policy, bạn nên log ít nhất sáu nhóm chỉ số:
| Nhóm metric | Ý nghĩa |
|---|---|
success_rate |
Tỷ lệ hoàn thành task trong LIBERO |
primitive_accuracy |
Pointer chọn đúng primitive theo trace hoặc heuristic |
primitive_switch_count |
Số lần đổi primitive trong một episode |
subgoal_completion |
Primitive có đạt trạng thái con hay không |
action_l2 |
Action solver còn bám demonstration không |
kl_to_bc |
RL có làm policy trôi khỏi BC quá mạnh không |
Nếu success rate tăng nhưng primitive_switch_count dao động rất lớn, policy có thể đang "spam" primitive. Nếu kl_to_bc tăng vọt trong vài nghìn update đầu, giảm learning rate hoặc tăng KL coefficient. Nếu primitive_accuracy tốt nhưng action L2 xấu, lỗi nằm ở solver/action representation, không phải high-level reasoning.
Một mẹo thực tế: lưu video rollout theo từng failure mode. Với LIBERO, bạn sẽ nhanh chóng thấy bốn nhóm lỗi: không tìm đúng object, chạm object nhưng không grasp, grasp được nhưng đặt sai target, hoặc làm đúng primitive nhưng timeout. Mỗi lỗi này cần fix khác nhau. Thêm data object view giúp lỗi nhận diện; chỉnh gripper threshold giúp lỗi grasp; tăng horizon/chunk hoặc reward subgoal giúp lỗi timeout.
Inference: từ câu lệnh tới action
Khi đã có checkpoint RL, inference không phức tạp nhưng phải giữ đúng preprocessing. Flow cơ bản:
from nsvla.policy import NSVLAPolicy
from libero.envs import make_libero_env
policy = NSVLAPolicy.from_pretrained(
"runs/ns-vla-rl-spatial/checkpoint-best",
device="cuda",
)
env = make_libero_env("libero_spatial", task_id=0)
obs = env.reset()
instruction = env.get_language_instruction()
done = False
while not done:
action_chunk, info = policy.predict_action_chunk(
obs=obs,
instruction=instruction,
horizon=8,
)
for action in action_chunk:
obs, reward, done, env_info = env.step(action)
if done:
break
Đây là skeleton để hiểu flow, không phải API cố định cho mọi commit. Khi áp dụng repo thật, hãy dùng script eval của project trước, sau đó mới wrapper lại thành service riêng. Điều bạn cần bảo toàn là: normalize ảnh đúng kích thước, normalize proprioception đúng mean/std, giữ action scale giống training, và không đổi language template giữa train/eval.
Nếu deploy robot thật, thêm safety layer ở dưới policy: giới hạn velocity, workspace bounds, collision checker nhẹ và emergency stop. NS-VLA có primitive rõ ràng nên dễ debug hơn policy end-to-end, nhưng nó vẫn là learned policy. Đừng để action chunk chạy 8 bước mù nếu force/torque hoặc camera cho thấy robot đã va chạm.
Kết quả trên LIBERO và robot thật
Theo paper/project, NS-VLA v2 đạt kết quả mạnh trên LIBERO và có demo real-world manipulation. Con số nổi bật từ project page là 98.6% trên LIBERO full benchmark và 79.4% trên LIBERO-Plus, còn paper v2 trình bày thêm kết quả theo chế độ 1-shot, ablation và real-world. Điểm nên hiểu là NS-VLA không chỉ tăng success rate nhờ backbone lớn hơn; contribution chính nằm ở cấu trúc primitive giúp reasoning theo bước rõ hơn và RL tách high-level/low-level.

Với benchmark như LIBERO-Spatial, Object, Goal và Long, hãy so kết quả theo từng suite thay vì chỉ lấy trung bình. LIBERO-Long thường lộ lỗi ordering và memory tốt hơn; LIBERO-Object lộ lỗi grounding vật thể; LIBERO-Spatial lộ lỗi quan hệ không gian. Nếu mô hình của bạn chỉ tốt ở Object nhưng kém ở Long, thêm primitive trace và curriculum task dài quan trọng hơn là tăng số epoch BC.
Checklist reproduce cho beginner
- Clone repo, tạo môi trường Python/CUDA đúng version.
- Cài LIBERO, MuJoCo, PyTorch và dependency của repo.
- Tải dataset/checkpoint từ Hugging Face.
- Chạy một eval script có sẵn để chắc environment hoạt động.
- Chuẩn bị dữ liệu 1-shot hoặc full demonstration.
- Train BC warmup cho tới khi primitive loss và action loss ổn định.
- Chạy RL GRPO/AWR với group rollout, logging video và KL.
- Eval mỗi suite riêng, không chỉ nhìn average.
- Phân loại failure bằng video.
- Chỉ sau khi ổn định trong sim mới nghĩ tới real robot.
Nếu bạn đã từng làm VLA-Adapter: Train VLA 0.5B với 9.6GB VRAM, hãy để ý khác biệt: VLA-Adapter tập trung làm model nhỏ vẫn mạnh; NS-VLA tập trung thêm cấu trúc symbolic để long-horizon manipulation dễ học và dễ debug hơn.
Khi nào nên dùng NS-VLA?
Dùng NS-VLA nếu task của bạn có nhiều bước, nhiều object và cần reasoning theo thứ tự. Ví dụ: mở ngăn kéo, lấy vật, đặt vào hộp, đóng lại. Những task này hợp với primitive vì failure thường nằm ở một bước rõ ràng. NS-VLA cũng phù hợp khi team cần giải thích log cho kỹ sư vận hành: "policy sai ở primitive place" dễ xử lý hơn "action dimension 4 lệch".
Không nên bắt đầu bằng NS-VLA nếu bạn chỉ có task pick-and-place một bước, dataset nhỏ và chưa có pipeline eval ổn định. Khi đó, một baseline behavior cloning hoặc OpenVLA fine-tune đơn giản có thể đủ. Neuro-symbolic layer thêm complexity: bạn phải quản lý primitive vocabulary, trace alignment, reward shaping và hai tầng training. Complexity này đáng trả khi task dài hoặc khi bạn cần debug nghiêm túc.
Kết luận
NS-VLA v2 là một ví dụ tốt cho xu hướng mới của robot learning: không còn tin rằng một VLA end-to-end lớn sẽ tự học mọi thứ từ action token. Paper đưa symbolic primitives trở lại, nhưng không quay về planner cổ điển; primitives được chọn bằng neural encoder và được tinh chỉnh bằng RL. BC warmup tạo policy ban đầu, GRPO cải thiện quyết định high-level, AWR làm low-level solver tốt hơn mà vẫn ổn định.
Với người mới, cách học tốt nhất là reproduce trên LIBERO trước: chạy eval, nhìn video, đọc primitive trace, rồi mới chỉnh reward hoặc action horizon. Khi bạn thấy một failure và có thể nói "sai primitive", "sai object argument" hay "solver đặt tay quá thấp", bạn đã đạt mục tiêu quan trọng nhất của NS-VLA: biến VLA manipulation từ hộp đen thành pipeline có thể phân tích.



