Full fix
This commit is contained in:
125
check_coverage.py
Normal file
125
check_coverage.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""Сравнение ПОКРЫТИЯ captions двух наборов (v1 vs v2) — отдельно от обучения.
|
||||
|
||||
Отвечает на один вопрос: одинаковый ли набор картинок покрыт текстом в v1 и v2?
|
||||
Если да — сравнение R@1 между версиями чистое. Если нет — разница R@1 может
|
||||
объясняться покрытием, а не качеством текста (см. протокол §5.4, §0.4).
|
||||
|
||||
Использует ТЕ ЖЕ функции чтения, что и обучение (load_text_descriptions,
|
||||
combine_text_levels), поэтому «есть текст» здесь означает ровно то же, что
|
||||
видит модель: непустой текст для выбранных text_levels, а не заглушку
|
||||
"No description available.".
|
||||
|
||||
Пример:
|
||||
python check_coverage.py \
|
||||
--data_root /media/servml/SSD_2_2TB/datasets/cvgl_datasets/GTA-UAV-LR \
|
||||
--captions_v1 ".../GTA-UAV-LR-captions_ v1" \
|
||||
--captions_v2 ".../GTA-UAV-LR-captions_v2" \
|
||||
--text_levels level1 level2
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from src.data.gta_uav import combine_text_levels, load_text_descriptions
|
||||
from src.data.gta_uav_eval import collect_gallery_names
|
||||
|
||||
|
||||
def _has_real_text(
|
||||
name: str,
|
||||
descriptions: dict[str, dict],
|
||||
text_levels: list[str],
|
||||
) -> bool:
|
||||
"""True, если для картинки есть непустой текст на выбранных уровнях.
|
||||
|
||||
Повторяет семантику обучения: пустой результат combine_text_levels →
|
||||
модель получит заглушку, т.е. текста фактически нет.
|
||||
"""
|
||||
desc = descriptions.get(name)
|
||||
if not desc:
|
||||
return False
|
||||
combined = combine_text_levels(desc, text_levels)
|
||||
return combined.strip() != "" and combined != "No description available."
|
||||
|
||||
|
||||
def _covered_set(
|
||||
names: list[str],
|
||||
descriptions: dict[str, dict],
|
||||
text_levels: list[str],
|
||||
) -> set[str]:
|
||||
"""Подмножество имён, у которых есть реальный текст."""
|
||||
return {n for n in names if _has_real_text(n, descriptions, text_levels)}
|
||||
|
||||
|
||||
def _report_side(
|
||||
title: str,
|
||||
names: list[str],
|
||||
desc_v1: dict[str, dict],
|
||||
desc_v2: dict[str, dict],
|
||||
text_levels: list[str],
|
||||
) -> None:
|
||||
"""Напечатать покрытие v1/v2 и пересечение для одной стороны."""
|
||||
total = len(names)
|
||||
cov_v1 = _covered_set(names, desc_v1, text_levels)
|
||||
cov_v2 = _covered_set(names, desc_v2, text_levels)
|
||||
both = cov_v1 & cov_v2
|
||||
only_v1 = cov_v1 - cov_v2
|
||||
only_v2 = cov_v2 - cov_v1
|
||||
|
||||
def pct(n: int) -> str:
|
||||
return f"{n:>6d} / {total} ({100.0 * n / max(total, 1):5.1f}%)"
|
||||
|
||||
print(f"\n── {title} (всего уникальных: {total}) " + "─" * 20)
|
||||
print(f" покрытие v1: {pct(len(cov_v1))}")
|
||||
print(f" покрытие v2: {pct(len(cov_v2))}")
|
||||
print(f" есть в ОБОИХ (пересеч): {pct(len(both))}")
|
||||
print(f" только v1: {len(only_v1)}")
|
||||
print(f" только v2: {len(only_v2)}")
|
||||
|
||||
if len(cov_v1) == len(cov_v2) == len(both):
|
||||
print(" ✅ наборы совпадают → покрытие не влияет на сравнение")
|
||||
else:
|
||||
diff = abs(len(cov_v1) - len(cov_v2))
|
||||
print(
|
||||
f" ⚠️ расхождение: |v1−v2| = {diff} картинок; "
|
||||
f"для честного сравнения оценивайте на пересечении ({len(both)})"
|
||||
)
|
||||
|
||||
|
||||
def main(args: argparse.Namespace) -> None:
|
||||
data_root = Path(args.data_root)
|
||||
text_levels = list(args.text_levels)
|
||||
|
||||
with open(data_root / args.test_meta) as f:
|
||||
pairs_meta = json.load(f)
|
||||
|
||||
# Дрон-запросы: уникальные имена картинок дрона в тесте.
|
||||
drone_names = sorted({e["drone_img_name"] for e in pairs_meta})
|
||||
# Спутниковая галерея: те же уникальные тайлы, что и в eval.
|
||||
gallery_names, _ = collect_gallery_names(pairs_meta)
|
||||
|
||||
print("=" * 64)
|
||||
print(f"COVERAGE CHECK | text_levels={text_levels} | {args.test_meta}")
|
||||
print("=" * 64)
|
||||
|
||||
for view, names in (("drone", drone_names), ("satellite", gallery_names)):
|
||||
desc_v1 = load_text_descriptions(args.captions_v1, view_type=view)
|
||||
desc_v2 = load_text_descriptions(args.captions_v2, view_type=view)
|
||||
_report_side(
|
||||
f"{view.upper()} {'queries' if view == 'drone' else 'gallery'}",
|
||||
names, desc_v1, desc_v2, text_levels,
|
||||
)
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description="Сравнить покрытие captions v1 vs v2")
|
||||
p.add_argument("--data_root", type=str, required=True)
|
||||
p.add_argument("--test_meta", type=str, default="cross-area-drone2sate-test.json")
|
||||
p.add_argument("--captions_v1", type=str, required=True)
|
||||
p.add_argument("--captions_v2", type=str, required=True)
|
||||
p.add_argument("--text_levels", nargs="+", default=["level1"])
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(parse_args())
|
||||
Reference in New Issue
Block a user