VLAlert / training /pretrain /pretrain_dataset_adaptive.py
AsianPlayer's picture
Add VLAlert code
1e05592 verified
Raw
History Blame Contribute Delete
10.7 kB
#!/usr/bin/env python3
"""
自适应Prompt的数据集加载器
使用数据中的user_prompt字段,而不是固定的prompt模板
"""
import pickle
from pathlib import Path
from typing import Dict, List, Optional, Tuple
import torch
from torch.utils.data import Dataset
from PIL import Image
import random
class AdaptivePretrainDataset(Dataset):
"""
自适应Prompt预训练数据集
每个样本都有自己的user_prompt,根据annotation长度定制
Args:
data_file: pretrain_data_adaptive.pkl路径
split: 'train', 'val', 或 'test'
tasks: 任务列表
curriculum_stage: 0=easy, 1=medium, 2=hard, 3=all
use_system_prompt: 是否使用system prompt
"""
# System prompts (任务级别)
SYSTEM_PROMPTS = {
"scene_understanding": "You are an expert driving scene analyzer. Describe the environment accurately.",
"binary_detection": "You are a traffic safety AI. Detect abnormal driving situations.",
"accident_description": "You are an accident analysis AI. Answer based on the question asked.", # 更通用
"sequence_prediction": "You are a temporal driving AI. Analyze video sequences for accident prediction."
}
def __init__(
self,
data_file: str,
split: str = "train",
tasks: List[str] = None,
curriculum_stage: int = 3,
use_system_prompt: bool = True
):
self.split = split
self.tasks = tasks or ["task1", "task2", "task3", "task4"]
self.curriculum_stage = curriculum_stage
self.use_system_prompt = use_system_prompt
# 加载数据
with open(data_file, "rb") as f:
all_data = pickle.load(f)
split_data = all_data[split]
# 收集样本
self.samples = []
task_map = {
"task1": "task1_scene_understanding",
"task2": "task2_binary_detection",
"task3": "task3_accident_description",
"task4": "task4_sequence_prediction"
}
for task in self.tasks:
if task in task_map:
task_samples = split_data.get(task_map[task], [])
# Curriculum filtering
if curriculum_stage < 3:
difficulty_map = {0: "easy", 1: "medium", 2: "hard"}
target_difficulty = difficulty_map[curriculum_stage]
task_samples = [
s for s in task_samples
if s.get("difficulty", "easy") == target_difficulty
]
self.samples.extend(task_samples)
# Shuffle
if split == "train":
random.shuffle(self.samples)
print(f"{'='*70}")
print(f"数据集加载: {split}")
print(f"Curriculum Stage: {curriculum_stage} ({['easy', 'medium', 'hard', 'all'][curriculum_stage]})")
print(f"任务: {tasks}")
print(f"样本数: {len(self.samples)}")
# 统计
from collections import Counter
task_dist = Counter(s["task"] for s in self.samples)
print(f"\n任务分布:")
for task, count in task_dist.items():
print(f" {task}: {count}")
# 统计短/长标注
if curriculum_stage == 3 and ("task3" in self.tasks or "task4" in self.tasks):
short_count = sum(
1 for s in self.samples
if s["task"] in ["accident_description", "sequence_prediction"]
and s["metadata"].get("is_short_annotation", False)
)
detailed_count = sum(
1 for s in self.samples
if s["task"] in ["accident_description", "sequence_prediction"]
and not s["metadata"].get("is_short_annotation", False)
)
if short_count + detailed_count > 0:
print(f"\nAnnotation分布 (任务3&4):")
print(f" 短标注 (<20字符): {short_count}")
print(f" 详细标注 (>=20字符): {detailed_count}")
# 难度分布
if curriculum_stage == 3:
diff_dist = Counter(s.get("difficulty", "unknown") for s in self.samples)
print(f"\n难度分布:")
for diff, count in diff_dist.items():
print(f" {diff}: {count}")
print("=" * 70)
def __len__(self):
return len(self.samples)
def __getitem__(self, idx):
sample = self.samples[idx]
task_type = sample["task"]
# 获取system prompt (任务级别)
system_prompt = self.SYSTEM_PROMPTS[task_type] if self.use_system_prompt else ""
# 使用样本中的user_prompt (自适应)
user_prompt = sample.get("user_prompt", "")
if task_type in ["scene_understanding", "binary_detection", "accident_description"]:
# 单帧任务
image = Image.open(sample["image_path"]).convert("RGB")
return {
"task": task_type,
"subtask": sample.get("subtask", task_type),
"image": image,
"system_prompt": system_prompt,
"user_prompt": user_prompt, # 自适应prompt
"label": sample["label"],
"difficulty": sample.get("difficulty", "unknown"),
"metadata": sample["metadata"]
}
elif task_type == "sequence_prediction":
# 序列任务
images = []
for img_path in sample["image_sequence"]:
img = Image.open(img_path).convert("RGB")
images.append(img)
return {
"task": task_type,
"subtask": sample.get("subtask", task_type),
"image_sequence": images,
"system_prompt": system_prompt,
"user_prompt": user_prompt, # 自适应prompt
"label": sample["label"],
"difficulty": sample.get("difficulty", "unknown"),
"metadata": sample["metadata"]
}
else:
raise ValueError(f"未知任务类型: {task_type}")
def collate_fn_adaptive(batch):
"""
自适应collate函数
每个样本有自己的user_prompt
"""
single_frame_batch = []
sequence_batch = []
for item in batch:
if item["task"] in ["scene_understanding", "binary_detection", "accident_description"]:
single_frame_batch.append(item)
elif item["task"] == "sequence_prediction":
sequence_batch.append(item)
result = {}
# 单帧任务
if single_frame_batch:
result["single_frame"] = {
"task": [x["task"] for x in single_frame_batch],
"subtask": [x["subtask"] for x in single_frame_batch],
"images": [x["image"] for x in single_frame_batch],
"system_prompts": [x["system_prompt"] for x in single_frame_batch],
"user_prompts": [x["user_prompt"] for x in single_frame_batch], # 每个样本不同
"labels": [x["label"] for x in single_frame_batch],
"difficulties": [x["difficulty"] for x in single_frame_batch],
"metadata": [x["metadata"] for x in single_frame_batch]
}
# 序列任务
if sequence_batch:
result["sequence"] = {
"task": [x["task"] for x in sequence_batch],
"subtask": [x["subtask"] for x in sequence_batch],
"image_sequences": [x["image_sequence"] for x in sequence_batch],
"system_prompts": [x["system_prompt"] for x in sequence_batch],
"user_prompts": [x["user_prompt"] for x in sequence_batch], # 每个样本不同
"labels": [x["label"] for x in sequence_batch],
"difficulties": [x["difficulty"] for x in sequence_batch],
"metadata": [x["metadata"] for x in sequence_batch]
}
return result
# ============ 测试代码 ============
if __name__ == "__main__":
from torch.utils.data import DataLoader
data_file = "PROJECT_ROOT/data/dataset/pretrain/train/pretrain_data_adaptive.pkl"
print("\n" + "=" * 70)
print("测试自适应Prompt数据集")
print("=" * 70)
# 创建数据集
dataset = AdaptivePretrainDataset(
data_file=data_file,
split="train",
tasks=["task1", "task2", "task3", "task4"],
curriculum_stage=3
)
loader = DataLoader(
dataset,
batch_size=4,
shuffle=False,
num_workers=0,
collate_fn=collate_fn_adaptive
)
# 测试一个batch
batch = next(iter(loader))
print("\n" + "=" * 70)
print("Batch示例")
print("=" * 70)
if "single_frame" in batch:
sf = batch["single_frame"]
print(f"\n单帧任务: {len(sf['images'])} 样本")
for i in range(len(sf['task'])):
print(f"\n样本 {i+1}:")
print(f" 任务: {sf['task'][i]}")
print(f" 难度: {sf['difficulties'][i]}")
print(f" System: {sf['system_prompts'][i][:60]}...")
print(f" User Prompt: {sf['user_prompts'][i]}") # 注意每个都不同
print(f" Label: {sf['labels'][i][:60]}...")
# 如果是事故描述任务,显示annotation长度
if sf['task'][i] == 'accident_description':
is_short = sf['metadata'][i].get('is_short_annotation', False)
anno_len = sf['metadata'][i].get('annotation_length', 0)
print(f" Annotation: {'短' if is_short else '详细'} ({anno_len}字符)")
if "sequence" in batch:
seq = batch["sequence"]
print(f"\n序列任务: {len(seq['image_sequences'])} 样本")
for i in range(len(seq['task'])):
print(f"\n样本 {i+1}:")
print(f" 序列长度: {len(seq['image_sequences'][i])}")
print(f" 难度: {seq['difficulties'][i]}")
print(f" User Prompt: {seq['user_prompts'][i]}") # 注意每个都不同
print(f" Label: {seq['labels'][i][:60]}...")
is_short = seq['metadata'][i].get('is_short_annotation', False)
anno_len = seq['metadata'][i].get('annotation_length', 0)
print(f" Annotation: {'短' if is_short else '详细'} ({anno_len}字符)")
print("\n✅ 数据集测试完成!")