| |
| """ |
| 自适应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 = { |
| "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], []) |
| |
| |
| 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) |
| |
| |
| 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 = self.SYSTEM_PROMPTS[task_type] if self.use_system_prompt else "" |
| |
| |
| 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, |
| "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, |
| "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 = 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]}...") |
| |
| |
| 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✅ 数据集测试完成!") |
|
|