Penyetelan Halus Model Visi-Bahasa-Tindakan
Pendahuluan
Model Vision-Language-Action (VLA) merupakan terobosan dalam AI berwujud, yang menggabungkan persepsi visual, pemahaman bahasa, dan kontrol robotik ke dalam model terpadu yang dapat mengikuti instruksi bahasa alami untuk melakukan tugas fisik. Tutorial ini mencakup proses lengkap untuk menyetel model VLA sesuai dengan aplikasi robotik spesifik Anda.
Apa itu Model Visi-Bahasa-Aksi?
VLA menggabungkan visi komputer (menafsirkan gambar/video), pemrosesan bahasa alami (memahami dan menghasilkan teks), dan pelaksanaan tindakan (berinteraksi dengan lingkungan atau sistem). Ini memungkinkan mereka untuk mengamati, bernalar, dan bertindak berdasarkan input visual dan tekstual.
Jika kamu ingin melihat VLA beraksi, lihat Tur Laboratorium Robotika Google DeepMind bersama Hannah Fry.
Arsitektur VLA yang populer termasuk OpenVLA, RT-2 (Robotic Transformer 2), dan PaLM-E.
Poin Penting
- Model VLA mempersatukan visi, bahasa, dan tindakan dengan memetakan gambar kamera dan instruksi bahasa alami langsung ke tindakan robot dalam arsitektur ujung-ke-ujung
- Gunakan LoRA untuk fine-tuning yang efisien, melatih hanya 0,1-0,2% dari parameter alih-alih seluruh model, memungkinkan pelatihan pada satu GPU dengan VRAM 24GB
Prasyarat
Persyaratan Perangkat Keras
- GPU dengan VRAM minimal 24GB (RTX 3090/4090, A100, atau H100)
- Perangkat keras robot atau lingkungan simulasi
- Kamera untuk input visual saat menjalankan inferensi
- Perangkat keras robot atau lingkungan simulasi
Langkah 0: Siapkan GPU dan Jupyter Notebook
Siapkan Droplet GPU Linux-Console.net dan kemudian SSH root@[IPV4] di terminal Anda
Di Terminal:
# Core dependencies
pip install torch torchvision
pip install transformers accelerate
pip install datasets
pip install wandb # for experiment trackingpip install jupyter lab
jupyter labLangkah 1: Memahami Arsitektur VLA
Pemain kunci: Vision Encoder: Memproses gambar dari kamera menjadi embedding visual Language Encoder: Mengubah instruksi teks menjadi embedding bahasa Fusion Module: Menggabungkan informasi visual dan bahasa Action Decoder: Memprediksi tindakan robot dari representasi gabungan
Langkah 2: Mempersiapkan Dataset Anda
Pengumpulan Data
Dataset Anda harus berisi episode dengan:
- Gambar: Pengamatan kamera RGB pada setiap langkah waktu
- Bahasa: Deskripsi tugas bahasa alami
- Aksi: Urutan aksi robot (posisi sendi, kecepatan, atau pose end-effector)
- Metadata: Label kesuksesan, durasi episode, dan sebagainya.
Contoh Format Data
{
"episode_0": {
"images": [img_0, img_1, ..., img_T], # shape: (T, H, W, 3) where T is total frames
"language": "pick up the red block",
"actions": [act_0, act_1, ..., act_T], # shape: (T, action_dim)
"success": True
}
}Organisasi Dataset
from datasets import Dataset, DatasetDict
import numpy as np
def create_vla_dataset(episodes):
"""Convert robot episodes to HuggingFace dataset format"""
data = {
"images": [],
"language": [],
"actions": [],
"episode_id": []
}
for ep_id, episode in enumerate(episodes):
for t in range(len(episode['images'])):
data['images'].append(episode['images'][t])
data['language'].append(episode['language'])
data['actions'].append(episode['actions'][t])
data['episode_id'].append(ep_id)
return Dataset.from_dict(data)
# Split into train/val
dataset = create_vla_dataset(your_episodes)
dataset = dataset.train_test_split(test_size=0.1)Langkah 3: Menyiapkan Model Dasar
Memuat VLA yang Telah Dilatih
from transformers import AutoModel, AutoProcessor
import torch
# Load pre-trained VLA (example using OpenVLA)
model_name = "openvla/openvla-7b"
processor = AutoProcessor.from_pretrained(model_name)
model = AutoModel.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
device_map="auto"
)
# Freeze base model parameters (optional for LoRA)
for param in model.parameters():
param.requires_grad = FalseAdaptasi Ruang Aksi
Ruang tindakan robot Anda kemungkinan berbeda dari data pra-pelatihan:
from torch import nn
class ActionHead(nn.Module):
def __init__(self, hidden_dim, action_dim):
super().__init__()
self.fc = nn.Sequential(
nn.Linear(hidden_dim, 512),
nn.ReLU(),
nn.Dropout(0.1),
nn.Linear(512, action_dim)
)
def forward(self, x):
return self.fc(x)
# Add custom action head
model.action_head = ActionHead(
hidden_dim=model.config.hidden_size,
action_dim=7 # e.g., 6-DOF arm + gripper
)Langkah 4: Penyesuaian Halus yang Efisien dengan LoRA
Low-Rank Adaptation (LoRA) memungkinkan penyesuaian yang efisien dengan mengurangi jumlah parameter yang dapat dilatih:
from peft import LoraConfig, get_peft_model
# Configure LoRA
lora_config = LoraConfig(
r=16, # rank
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM"
)
# Apply LoRA to model
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
#Expected output: trainable params: 8.3M || all params: 7B || trainable%: 0.12%Langkah 5: Pemrosesan dan Augmentasi Data
Pralaku Gambar
from torchvision import transforms
image_transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
transforms.RandomHorizontalFlip(p=0.5),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])Normalisasi Aksi
def normalize_actions(actions, stats):
"""Normalize actions to [-1, 1] range"""
return (actions - stats['mean']) / (stats['std'] + 1e-8)
def denormalize_actions(normalized_actions, stats):
"""Convert back to original action space"""
return normalized_actions * (stats['std'] + 1e-8) + stats['mean']
# Calculate statistics from your dataset
action_stats = {
'mean': dataset['train']['actions'].mean(axis=0),
'std': dataset['train']['actions'].std(axis=0)
}Pengaturan DataLoader
from torch.utils.data import DataLoader
def collate_fn(batch):
"""Custom collate function for VLA data"""
images = torch.stack([processor.image_processor(b['images'])
for b in batch])
text = processor.tokenizer([b['language'] for b in batch],
padding=True, return_tensors="pt")
actions = torch.tensor([b['actions'] for b in batch],
dtype=torch.float32)
return {
'pixel_values': images,
'input_ids': text['input_ids'],
'attention_mask': text['attention_mask'],
'actions': actions
}
train_loader = DataLoader(
dataset['train'],
batch_size=32,
shuffle=True,
collate_fn=collate_fn,
num_workers=4
)Langkah 6: Loop Pelatihan
Fungsi Kerugian
import torch.nn.functional as F
def vla_loss(predicted_actions, target_actions, reduction='mean'):
"""Action prediction loss (MSE for continuous actions)"""
mse_loss = F.mse_loss(predicted_actions, target_actions, reduction=reduction)
return mse_loss
# Alternative: Action chunking for temporal coherence
def chunked_action_loss(pred_chunks, target_chunks, chunk_size=10):
"""Predict multiple future actions at once"""
loss = 0
for i in range(chunk_size):
loss += F.mse_loss(pred_chunks[:, i], target_chunks[:, i])
return loss / chunk_sizeSkrip Pelatihan
from accelerate import Accelerator
from tqdm import tqdm
import wandb
# Initialize accelerator for distributed training
accelerator = Accelerator(mixed_precision='bf16')
# Setup
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=0.01)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10000)
# Prepare for distributed training
model, optimizer, train_loader = accelerator.prepare(
model, optimizer, train_loader
)
# Initialize wandb
wandb.init(project="vla-finetuning", config={
"learning_rate": 1e-4,
"batch_size": 32,
"epochs": 10
})
# Training loop
global_step = 0
for epoch in range(10):
model.train()
epoch_loss = 0
for batch in tqdm(train_loader, desc=f"Epoch {epoch+1}"):
# Forward pass
outputs = model(
pixel_values=batch['pixel_values'],
input_ids=batch['input_ids'],
attention_mask=batch['attention_mask']
)
# Predict actions
predicted_actions = model.action_head(outputs.last_hidden_state[:, -1])
# Compute loss
loss = vla_loss(predicted_actions, batch['actions'])
# Backward pass
accelerator.backward(loss)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
scheduler.step()
optimizer.zero_grad()
# Logging
epoch_loss += loss.item()
global_step += 1
if global_step % 100 == 0:
wandb.log({
"loss": loss.item(),
"learning_rate": scheduler.get_last_lr()[0],
"epoch": epoch
})
print(f"Epoch {epoch+1} - Average Loss: {epoch_loss / len(train_loader):.4f}")
# Save checkpoint
if (epoch + 1) % 2 == 0:
accelerator.save_model(model, f"checkpoints/epoch_{epoch+1}")Langkah 7: Evaluasi
Evaluasi Simulasi
def evaluate_in_simulation(model, env, num_episodes=50):
"""Evaluate model in simulation environment"""
model.eval()
success_count = 0
with torch.no_grad():
for episode in range(num_episodes):
obs = env.reset()
instruction = env.get_task_instruction()
done = False
while not done:
# Process observation
image = torch.tensor(obs['image']).unsqueeze(0)
text_inputs = processor.tokenizer(instruction, return_tensors="pt")
# Predict action
outputs = model(
pixel_values=image.to(model.device),
input_ids=text_inputs['input_ids'].to(model.device)
)
action = model.action_head(outputs.last_hidden_state[:, -1])
# Denormalize and execute
action = denormalize_actions(action.cpu().numpy(), action_stats)
obs, reward, done, info = env.step(action[0])
if info['success']:
success_count += 1
success_rate = success_count / num_episodes
print(f"Success Rate: {success_rate*100:.1f}%")
return success_rateMetrik Kloning Perilaku
def evaluate_bc_metrics(model, val_loader):
"""Evaluate behavioural cloning performance"""
model.eval()
total_mse = 0
total_cosine_sim = 0
n_samples = 0
with torch.no_grad():
for batch in val_loader:
outputs = model(
pixel_values=batch['pixel_values'],
input_ids=batch['input_ids']
)
pred_actions = model.action_head(outputs.last_hidden_state[:, -1])
# MSE
mse = F.mse_loss(pred_actions, batch['actions'])
total_mse += mse.item() * len(batch['actions'])
# Cosine similarity
cosine_sim = F.cosine_similarity(pred_actions, batch['actions']).mean()
total_cosine_sim += cosine_sim.item() * len(batch['actions'])
n_samples += len(batch['actions'])
return {
'mse': total_mse / n_samples,
'cosine_similarity': total_cosine_sim / n_samples
}Langkah 8: Penerapan
Ekspor Model
# Save fine-tuned model
model.save_pretrained("./finetuned_vla")
processor.save_pretrained("./finetuned_vla")
# For deployment, merge LoRA weights
from peft import PeftModel
base_model = AutoModel.from_pretrained(model_name)
merged_model = PeftModel.from_pretrained(base_model, "./finetuned_vla")
merged_model = merged_model.merge_and_unload()
merged_model.save_pretrained("./deployed_model")Inferensi Waktu Nyata
class VLAController:
def __init__(self, model_path):
self.model = AutoModel.from_pretrained(model_path)
self.processor = AutoProcessor.from_pretrained(model_path)
self.model.eval()
self.model.to('cuda')
@torch.inference_mode()
def predict_action(self, image, instruction):
"""Real-time action prediction"""
# Preprocess
inputs = self.processor(
images=image,
text=instruction,
return_tensors="pt"
).to('cuda')
# Predict
outputs = self.model(**inputs)
action = self.model.action_head(outputs.last_hidden_state[:, -1])
return action.cpu().numpy()[0]
# Usage
controller = VLAController("./deployed_model")
action = controller.predict_action(camera_image, "pick up the cup")
robot.execute_action(action)Sumber Daya
OpenVLA
Makalah RT-2 dan situs web RT-2: Ini adalah karya yang mencetuskan istilah VLA (model Vision-Language-Action)
Perpustakaan PEFT
RoboMimic (untuk penanganan dataset)
-
OpenVLA: Presentasi Penelitian LeRobot #5 oleh Moo Jin Kim
Kesimpulan
Penyetelan model VLA memungkinkan robot untuk melakukan tugas khusus dengan kontrol bahasa alami. Kami berharap tutorial ini menunjukkan kepada Anda bagaimana Anda dapat menyesuaikan model yang telah dilatih sebelumnya dengan perangkat keras dan tugas spesifik Anda, memanfaatkan kekuatan pelatihan skala besar sekaligus menyesuaikannya untuk aplikasi Anda. Di sini, Anda disarankan untuk memulai dengan simulasi, beriterasi dengan cepat, dan secara bertahap beralih ke perangkat keras nyata seiring peningkatan model Anda.
Terima kasih telah belajar bersama Komunitas Linux-Console.net.
