| |
| """ |
| 分析annotation质量 |
| 找出少于20字符的简单标注,以便调整prompt策略 |
| """ |
|
|
| import json |
| from pathlib import Path |
| from collections import defaultdict |
|
|
|
|
| class AnnotationAnalyzer: |
| """标注分析器""" |
| |
| def __init__(self, threshold_chars=20): |
| self.threshold = threshold_chars |
| self.stats = defaultdict(list) |
| |
| def analyze_annotation(self, anno_file: Path): |
| """分析单个annotation文件""" |
| with open(anno_file, 'r') as f: |
| data = json.load(f) |
| |
| case_id = data.get('id', anno_file.parent.name) |
| accident_type = data.get('accident_type', '') |
| |
| if not accident_type or accident_type.lower() in ['null', 'none', 'unknown', '']: |
| return None, 'empty' |
| |
| |
| accident_type = accident_type.strip() |
| char_count = len(accident_type) |
| word_count = len(accident_type.split()) |
| |
| |
| if char_count < self.threshold: |
| category = 'short' |
| else: |
| category = 'detailed' |
| |
| return { |
| 'case_id': case_id, |
| 'dataset': data.get('dataset', 'unknown'), |
| 'accident_type': accident_type, |
| 'char_count': char_count, |
| 'word_count': word_count, |
| 'category': category, |
| 'file': str(anno_file) |
| }, category |
| |
| def analyze_dataset(self, dataset_root: Path, dataset_name: str): |
| """分析整个数据集""" |
| print(f"\n分析 {dataset_name}...") |
| |
| anno_files = list(dataset_root.rglob("annotation.json")) |
| print(f"找到 {len(anno_files)} 个annotation文件") |
| |
| results = { |
| 'short': [], |
| 'detailed': [], |
| 'empty': [] |
| } |
| |
| for anno_file in anno_files: |
| try: |
| info, category = self.analyze_annotation(anno_file) |
| if info: |
| results[category].append(info) |
| except Exception as e: |
| print(f"处理失败 {anno_file}: {e}") |
| |
| return results |
| |
| def print_summary(self, results: dict, dataset_name: str): |
| """打印统计摘要""" |
| total = sum(len(results[cat]) for cat in ['short', 'detailed', 'empty']) |
| |
| print(f"\n{'='*70}") |
| print(f"{dataset_name} - 标注质量统计") |
| print("=" * 70) |
| print(f"总计: {total} 案例") |
| print(f" 简单标注 (<{self.threshold}字符): {len(results['short'])} ({len(results['short'])/total*100:.1f}%)") |
| print(f" 详细标注 (>={self.threshold}字符): {len(results['detailed'])} ({len(results['detailed'])/total*100:.1f}%)") |
| print(f" 空标注: {len(results['empty'])} ({len(results['empty'])/total*100:.1f}%)") |
| |
| def print_examples(self, results: dict, n=10): |
| """打印示例""" |
| print(f"\n{'='*70}") |
| print("简单标注示例 (前{}):".format(min(n, len(results['short'])))) |
| print("=" * 70) |
| |
| |
| short_sorted = sorted(results['short'], key=lambda x: x['char_count']) |
| |
| for i, item in enumerate(short_sorted[:n], 1): |
| print(f"\n{i}. [{item['char_count']}字符, {item['word_count']}词]") |
| print(f" 案例: {item['case_id']}") |
| print(f" 标注: \"{item['accident_type']}\"") |
| |
| print(f"\n{'='*70}") |
| print("详细标注示例 (前5):") |
| print("=" * 70) |
| |
| |
| detailed_sorted = sorted(results['detailed'], key=lambda x: x['char_count'], reverse=True) |
| |
| for i, item in enumerate(detailed_sorted[:5], 1): |
| print(f"\n{i}. [{item['char_count']}字符, {item['word_count']}词]") |
| print(f" 案例: {item['case_id']}") |
| print(f" 标注: \"{item['accident_type'][:100]}...\"" if len(item['accident_type']) > 100 |
| else f" 标注: \"{item['accident_type']}\"") |
| |
| def save_analysis(self, results: dict, output_file: Path): |
| """保存分析结果""" |
| analysis = { |
| 'threshold': self.threshold, |
| 'short_annotations': results['short'], |
| 'detailed_annotations': results['detailed'], |
| 'empty_annotations': results['empty'], |
| 'statistics': { |
| 'total': sum(len(results[cat]) for cat in ['short', 'detailed', 'empty']), |
| 'short_count': len(results['short']), |
| 'detailed_count': len(results['detailed']), |
| 'empty_count': len(results['empty']) |
| } |
| } |
| |
| with open(output_file, 'w') as f: |
| json.dump(analysis, f, indent=2) |
| |
| print(f"\n✓ 分析结果保存到: {output_file}") |
|
|
|
|
| def main(): |
| """主函数""" |
| print("=" * 70) |
| print("Annotation质量分析") |
| print("阈值: 20字符") |
| print("=" * 70) |
| |
| analyzer = AnnotationAnalyzer(threshold_chars=20) |
| |
| all_results = { |
| 'short': [], |
| 'detailed': [], |
| 'empty': [] |
| } |
| |
| |
| dada_root = Path("PROJECT_ROOT/data/dataset/pretrain/DADA-2000") |
| if dada_root.exists(): |
| dada_results = analyzer.analyze_dataset(dada_root, "DADA-2000") |
| analyzer.print_summary(dada_results, "DADA-2000") |
| |
| for cat in ['short', 'detailed', 'empty']: |
| all_results[cat].extend(dada_results[cat]) |
| |
| |
| nexar_root = Path("PROJECT_ROOT/data/dataset/pretrain/nexar") |
| if nexar_root.exists(): |
| nexar_results = analyzer.analyze_dataset(nexar_root, "NEXAR") |
| analyzer.print_summary(nexar_results, "NEXAR") |
| |
| for cat in ['short', 'detailed', 'empty']: |
| all_results[cat].extend(nexar_results[cat]) |
| |
| |
| analyzer.print_summary(all_results, "总体") |
| |
| |
| analyzer.print_examples(all_results, n=15) |
| |
| |
| output_dir = Path("PROJECT_ROOT/data/dataset/pretrain/train") |
| output_dir.mkdir(parents=True, exist_ok=True) |
| analyzer.save_analysis(all_results, output_dir / "annotation_analysis.json") |
| |
| |
| print("\n" + "=" * 70) |
| print("建议的Prompt策略") |
| print("=" * 70) |
| |
| print("\n简单标注 (<20字符) - 使用简单prompt:") |
| print(" - 'What object or vehicle was involved in this accident?'") |
| print(" - 'Identify the main entity in this traffic incident.'") |
| print(" - 'What type of collision is shown? (e.g., vehicle, pedestrian, bicycle)'") |
| |
| print("\n详细标注 (>=20字符) - 使用详细prompt:") |
| print(" - 'Describe the accident in this image. What happened and why?'") |
| print(" - 'Provide a detailed description of the traffic incident.'") |
| print(" - 'Explain what led to this accident and what occurred.'") |
| |
| print("\n" + "=" * 70) |
| print("✅ 分析完成!") |
| print("=" * 70) |
| print("\n下一步:") |
| print("1. 查看 annotation_analysis.json 了解详细情况") |
| print("2. 运行 prepare_pretrain_data_adaptive.py 生成自适应prompt数据") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|