fix
This commit is contained in:
@@ -4,24 +4,32 @@
|
|||||||
не от обучения. Поэтому процент обрезанных сэмплов можно посчитать по готовым
|
не от обучения. Поэтому процент обрезанных сэмплов можно посчитать по готовым
|
||||||
данным — результат идентичен тому, что было во время обучения.
|
данным — результат идентичен тому, что было во время обучения.
|
||||||
|
|
||||||
Для каждой из 6 комбинаций уровней и каждого набора (v1/v2) считает:
|
Считается ОТДЕЛЬНО по каждому split (cross-area train/test) и по каждой стороне
|
||||||
|
(drone queries / satellite gallery), чтобы процент отражал ровно те сэмплы, что
|
||||||
|
реально идут в обучение и в eval, а не весь датасет целиком (ср. check_coverage.py).
|
||||||
|
|
||||||
|
Для каждой из 6 комбинаций уровней считает:
|
||||||
- сколько сэмплов превышает 248 токенов (обрезается)
|
- сколько сэмплов превышает 248 токенов (обрезается)
|
||||||
- средняя/максимальная длина в токенах
|
- средняя/максимальная длина в токенах
|
||||||
- средний «перебор» у обрезанных (на сколько токенов текст длиннее 248)
|
- средний «перебор» у обрезанных (на сколько токенов текст длиннее 248)
|
||||||
|
|
||||||
Запуск:
|
Запуск:
|
||||||
python measure_truncation.py \
|
python measure_truncation.py \
|
||||||
|
--data_root "/media/servml/SSD_2_2TB/datasets/cvgl_datasets/GTA-UAV-LR" \
|
||||||
--descriptions_path "/media/servml/SSD_2_2TB/datasets/cvgl_datasets/GTA-UAV-LR-captions_ v1" \
|
--descriptions_path "/media/servml/SSD_2_2TB/datasets/cvgl_datasets/GTA-UAV-LR-captions_ v1" \
|
||||||
--version v1
|
--version v1
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import json
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
sys.path.insert(0, ".")
|
sys.path.insert(0, ".")
|
||||||
|
|
||||||
from src.data.gta_uav import load_text_descriptions, combine_text_levels
|
from src.data.gta_uav import load_text_descriptions, combine_text_levels
|
||||||
|
from src.data.gta_uav_eval import collect_gallery_names
|
||||||
from src.models.dgtrs.model import tokenize_dgtrs
|
from src.models.dgtrs.model import tokenize_dgtrs
|
||||||
|
|
||||||
CONTEXT_LENGTH = 248
|
CONTEXT_LENGTH = 248
|
||||||
@@ -40,24 +48,30 @@ COMBINATIONS = [
|
|||||||
def count_tokens(text: str) -> int:
|
def count_tokens(text: str) -> int:
|
||||||
"""Реальное число ненулевых токенов ДО обрезки.
|
"""Реальное число ненулевых токенов ДО обрезки.
|
||||||
|
|
||||||
tokenize с truncate=False даёт полную длину; если функция не поддерживает
|
Токенизируем с большим запасом (context_length=1024), чтобы увидеть полную
|
||||||
truncate=False, считаем через увеличенный context_length.
|
длину без потери токенов на лимите 248.
|
||||||
"""
|
"""
|
||||||
if not text.strip():
|
if not text.strip():
|
||||||
return 0
|
return 0
|
||||||
# токенизируем с большим запасом, чтобы увидеть полную длину без обрезки
|
|
||||||
toks = tokenize_dgtrs(text, context_length=1024, truncate=True)
|
toks = tokenize_dgtrs(text, context_length=1024, truncate=True)
|
||||||
return int((toks != 0).sum())
|
return int((toks != 0).sum())
|
||||||
|
|
||||||
|
|
||||||
def measure(descriptions: dict, combo: list[str]) -> dict:
|
def measure(names: list[str], descriptions: dict, combo: list[str]) -> dict:
|
||||||
"""Посчитать статистику обрезки для одной комбинации уровней."""
|
"""Посчитать статистику обрезки для одной комбинации уровней.
|
||||||
|
|
||||||
|
Учитываются только имена из `names` (сэмплы конкретного split/стороны),
|
||||||
|
у которых есть непустой склеенный текст.
|
||||||
|
"""
|
||||||
n_total = 0
|
n_total = 0
|
||||||
n_truncated = 0
|
n_truncated = 0
|
||||||
lengths = []
|
lengths = []
|
||||||
overflows = []
|
overflows = []
|
||||||
|
|
||||||
for img_name, desc in descriptions.items():
|
for name in names:
|
||||||
|
desc = descriptions.get(name)
|
||||||
|
if not desc:
|
||||||
|
continue
|
||||||
text = combine_text_levels(desc, combo)
|
text = combine_text_levels(desc, combo)
|
||||||
if not text.strip():
|
if not text.strip():
|
||||||
continue
|
continue
|
||||||
@@ -84,36 +98,83 @@ def measure(descriptions: dict, combo: list[str]) -> dict:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def _report_side(title: str, names: list[str], descriptions: dict) -> None:
|
||||||
args = parse_args()
|
"""Напечатать таблицу обрезки по всем комбинациям для одной стороны."""
|
||||||
|
print(f"\n── {title} (всего уникальных: {len(names)}) " + "─" * 20)
|
||||||
descriptions = load_text_descriptions(args.descriptions_path, view_type="drone")
|
|
||||||
print(f"Загружено {len(descriptions)} описаний ({args.version})\n")
|
|
||||||
|
|
||||||
print(f"{'Комбинация':<26} {'Всего':>7} {'Обрезано':>9} {'%':>7} "
|
print(f"{'Комбинация':<26} {'Всего':>7} {'Обрезано':>9} {'%':>7} "
|
||||||
f"{'СрДлина':>8} {'МаксДлина':>10} {'СрПеребор':>10}")
|
f"{'СрДлина':>8} {'МаксДлина':>10} {'СрПеребор':>10}")
|
||||||
print("-" * 82)
|
print("-" * 82)
|
||||||
|
|
||||||
rows = []
|
|
||||||
for combo in COMBINATIONS:
|
for combo in COMBINATIONS:
|
||||||
r = measure(descriptions, combo)
|
r = measure(names, descriptions, combo)
|
||||||
rows.append(r)
|
|
||||||
print(f"{r['combo']:<26} {r['n_total']:>7} {r['n_truncated']:>9} "
|
print(f"{r['combo']:<26} {r['n_total']:>7} {r['n_truncated']:>9} "
|
||||||
f"{r['pct_truncated']:>6.1f}% {r['avg_len']:>8.0f} "
|
f"{r['pct_truncated']:>6.1f}% {r['avg_len']:>8.0f} "
|
||||||
f"{r['max_len']:>10} {r['avg_overflow']:>10.0f}")
|
f"{r['max_len']:>10} {r['avg_overflow']:>10.0f}")
|
||||||
|
|
||||||
print("-" * 82)
|
print("-" * 82)
|
||||||
print(f"\nЛимит контекста: {CONTEXT_LENGTH} токенов")
|
|
||||||
print("Обрезано = число сэмплов, где склеенный текст длиннее лимита "
|
|
||||||
|
def _report_split(
|
||||||
|
data_root: Path,
|
||||||
|
meta_name: str,
|
||||||
|
desc_by_view: dict[str, dict],
|
||||||
|
) -> None:
|
||||||
|
"""Обрезка v для одного split-файла (train или test), обе стороны."""
|
||||||
|
meta_path = data_root / meta_name
|
||||||
|
if not meta_path.exists():
|
||||||
|
print(f"\n⚠️ Пропущен (нет файла): {meta_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(meta_path) as f:
|
||||||
|
pairs_meta = json.load(f)
|
||||||
|
|
||||||
|
# Дрон-запросы: уникальные имена картинок дрона в split.
|
||||||
|
drone_names = sorted({e["drone_img_name"] for e in pairs_meta})
|
||||||
|
# Спутниковая галерея: те же уникальные тайлы, что и в eval.
|
||||||
|
gallery_names, _ = collect_gallery_names(pairs_meta)
|
||||||
|
|
||||||
|
print("\n" + "=" * 64)
|
||||||
|
print(f"SPLIT: {meta_name} ({len(pairs_meta)} пар)")
|
||||||
|
print("=" * 64)
|
||||||
|
|
||||||
|
for view, names in (("drone", drone_names), ("satellite", gallery_names)):
|
||||||
|
_report_side(
|
||||||
|
f"{view.upper()} {'queries' if view == 'drone' else 'gallery'}",
|
||||||
|
names, desc_by_view[view],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
data_root = Path(args.data_root)
|
||||||
|
|
||||||
|
print("=" * 64)
|
||||||
|
print(f"TRUNCATION CHECK | version={args.version} | лимит={CONTEXT_LENGTH} токенов")
|
||||||
|
print("=" * 64)
|
||||||
|
|
||||||
|
# Описания не зависят от split — грузим один раз на каждую сторону.
|
||||||
|
desc_by_view = {
|
||||||
|
view: load_text_descriptions(args.descriptions_path, view_type=view)
|
||||||
|
for view in ("drone", "satellite")
|
||||||
|
}
|
||||||
|
for view, desc in desc_by_view.items():
|
||||||
|
print(f"Загружено {len(desc)} описаний ({view}, {args.version})")
|
||||||
|
|
||||||
|
# Обрезка важна и на train (что модель УЧИТ), и на test (что видит eval).
|
||||||
|
for meta_name in (args.train_meta, args.test_meta):
|
||||||
|
_report_split(data_root, meta_name, desc_by_view)
|
||||||
|
|
||||||
|
print("\nОбрезано = число сэмплов, где склеенный текст длиннее лимита "
|
||||||
"(хвост, включая level3-якорь, отсекается).")
|
"(хвост, включая level3-якорь, отсекается).")
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
p = argparse.ArgumentParser(description="Измерение обрезки по комбинациям уровней")
|
p = argparse.ArgumentParser(description="Измерение обрезки по комбинациям уровней и split")
|
||||||
|
p.add_argument("--data_root", type=str, required=True)
|
||||||
p.add_argument("--descriptions_path", type=str, required=True)
|
p.add_argument("--descriptions_path", type=str, required=True)
|
||||||
p.add_argument("--version", type=str, default="v1")
|
p.add_argument("--version", type=str, default="v1")
|
||||||
|
p.add_argument("--train_meta", type=str, default="cross-area-drone2sate-train.json")
|
||||||
|
p.add_argument("--test_meta", type=str, default="cross-area-drone2sate-test.json")
|
||||||
return p.parse_args()
|
return p.parse_args()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class GatedFusion(nn.Module):
|
|||||||
"доверяем картинке", σ(α) → 0 — "доверяем тексту".
|
"доверяем картинке", σ(α) → 0 — "доверяем тексту".
|
||||||
|
|
||||||
Картинка и текст приходят из РАЗНЫХ энкодеров (StripNet 1024-d,
|
Картинка и текст приходят из РАЗНЫХ энкодеров (StripNet 1024-d,
|
||||||
DGTRS 768-d), поэтому сначала проецируются в общий fused_dim и
|
DGTRS 512-d), поэтому сначала проецируются в общий fused_dim и
|
||||||
L2-нормируются — чтобы выпуклая комбинация была осмысленной
|
L2-нормируются — чтобы выпуклая комбинация была осмысленной
|
||||||
(комбинируем единичные векторы, как в общем пространстве CLIP
|
(комбинируем единичные векторы, как в общем пространстве CLIP
|
||||||
первоисточника).
|
первоисточника).
|
||||||
|
|||||||
Reference in New Issue
Block a user