Docs: clarify dense tensor maps format and training usage

All modalities are stored as dense spatial tensor maps (not RGB renders):
- depth/edge/chm: [1,H,W] float16 continuous values
- segm: [1,H,W] uint8 dense class ID map (0-16)

Add training code examples: one-hot encoding, per-class embedding,
aux-encoder injection. Document why tensors over RGB renders (70% info
loss + OOD for DINOv3).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
pikaliov
2026-04-18 04:10:57 +03:00
parent 43c81ae8a4
commit 75502b481e
2 changed files with 40 additions and 28 deletions

View File

@@ -30,7 +30,7 @@ python -m pytest src/tests/ -v
## Ключевые решения
- **Формат выхода:** SafeTensors (zero-copy mmap, ~0.1ms чтение). Не NPY, не PNG, не `.pt`.
- **Формат выхода:** SafeTensors с **dense tensor maps** (zero-copy mmap, ~0.1ms). Все модальности — прямые тензоры (float16/uint8), не RGB-рендеры.
- **Структура директорий:** модальность = папка (`depth/`, `edge/`, `segm/`, `chm/`, `safetensors/`), не суффикс файла.
- **Стадии последовательно** — одна модель в GPU за раз (экономия VRAM).
- **Сегментация:** SegEarth-OV3 (SAM 3.1 + open-vocabulary prompts). **17 unified классов** для всех датасетов (единые ID для transfer learning).
@@ -66,6 +66,6 @@ docs/ — документация
- Не менять порядок/ID классов в `scripts/seg_classes.py` — все датасеты зависят от фиксированных ID.
- Не использовать `.pt` (torch.save) для хранения тензоров — pickle, медленно.
- Не рендерить depth/seg в RGB colormap для обучения — OOD для DINOv3, потеря информации.
- Не рендерить depth/seg в RGB colormap для обучения — OOD для DINOv3, потеря ~70% информации. Использовать dense tensor maps из SafeTensors.
- Не снижать threshold ниже 0.1 — увеличивает false positives без значимого улучшения recall.
- Не менять `dark_water_std_thr` (0.18) — калибровано на GTA-UAV ocean (std 0.10-0.15). Ниже — не ловит, выше — false positives на normal images.

View File

@@ -198,10 +198,10 @@ World-UAV-aug/
| Ключ | Dtype | Shape | Описание |
|:---|:---|:---|:---|
| `depth` | float16 | [1, H, W] | Карта глубины [0, 1] |
| `edge` | float16 | [1, H, W] | Границы (Sobel) [0, 1] |
| `chm` | float16 | [1, H, W] | Canopy height [0, 1] |
| `segm` | uint8 | [1, H, W] | Class IDs [0, 10] (World-UAV) / [0, 16] (unified) |
| `depth` | float16 | [1, H, W] | Dense depth map, непрерывная [0, 1], per-frame normalized |
| `edge` | float16 | [1, H, W] | Dense edge map (Sobel magnitude), [0, 1] |
| `chm` | float16 | [1, H, W] | Dense canopy height map, [0, 1], per-frame normalized |
| `segm` | uint8 | [1, H, W] | Dense class ID map, значения [0, 16] (17 unified классов) |
Преимущества SafeTensors:
- **Zero-copy mmap** -- тензор читается прямо с диска без копирования в RAM (~0.1ms)
@@ -242,42 +242,54 @@ World-UAV-aug/
## Использование для обучения
Все модальности хранятся как **dense tensor maps** — прямые тензоры, не RGB-рендеры. Это ключевое решение (см. [dialog_fusion_modalities](docs/segmentation_class_analysis.md)): тензоры сохраняют полную информацию без потерь при квантовании/colormapping и не являются OOD-входом для DINOv3.
### SafeTensors (рекомендуемый способ)
```python
from safetensors.torch import load_file
stem = "crop_12_4"
aug_root = Path("World-UAV-aug")
rel_parent = "Rot/SouthernSuburbs/DB/img"
import torch.nn.functional as F
# Zero-copy чтение всех модальностей за ~0.1ms
data = load_file(aug_root / "safetensors" / rel_parent / f"{stem}.safetensors", device="cpu")
data = load_file("World-UAV-aug/safetensors/Rot/.../crop_12_4.safetensors")
depth = data["depth"] # [1, 256, 256] float16, [0, 1]
edge = data["edge"] # [1, 256, 256] float16, [0, 1]
chm = data["chm"] # [1, 256, 256] float16, [0, 1]
segm = data["segm"] # [1, 256, 256] uint8, class IDs [0, 10]
# Для Teacher NADEZHDA: segm -> one-hot
import torch.nn.functional as F
segm_onehot = F.one_hot(segm.long().squeeze(0), num_classes=17) # [H, W, 17]
segm_onehot = segm_onehot.permute(2, 0, 1).float() # [17, H, W]
# Все модальности — dense spatial maps, готовые для injection в backbone
depth = data["depth"] # [1, H, W] float16, непрерывная глубина [0, 1]
edge = data["edge"] # [1, H, W] float16, Sobel magnitude [0, 1]
chm = data["chm"] # [1, H, W] float16, canopy height [0, 1]
segm = data["segm"] # [1, H, W] uint8, dense class ID map [0, 16]
```
### PNG fallback (для визуализации или legacy)
### Подача в Teacher NADEZHDA
Каждая модальность подаётся в свой lightweight aux-encoder, затем через FiLM/Conv1x1 injection в DINOv3 patch tokens:
```python
from PIL import Image
import numpy as np
# Depth / Edge / CHM → [B, 1, H, W] float → Conv aux-encoder → FiLM injection
# Прямые тензоры, НЕ RGB-рендеры (turbo colormap = потеря 70% информации + OOD)
aux_depth = depth_encoder(depth.float()) # [1, H, W] → [C, H, W]
aux_edge = edge_encoder(edge.float())
aux_chm = chm_encoder(chm.float())
# Depth / Edge / CHM -- grayscale float [0, 1]
depth = np.array(Image.open(aug_root / "depth" / rel_parent / f"{stem}.png")) / 255.0
edge = np.array(Image.open(aug_root / "edge" / rel_parent / f"{stem}.png")) / 255.0
chm = np.array(Image.open(aug_root / "chm" / rel_parent / f"{stem}.png")) / 255.0
# Segmentation → dense class ID map → per-class embedding → spatial feature map
# Вариант 1: one-hot → Conv
segm_onehot = F.one_hot(segm.long().squeeze(0), num_classes=17) # [H, W, 17]
segm_features = seg_conv(segm_onehot.permute(2, 0, 1).float()) # [17, H, W] → [C, H, W]
# Вариант 2: learned per-class embedding (SegAuxEncoder)
# seg_emb = nn.Embedding(17, 32)
# segm_features = seg_emb(segm.long().squeeze(0)).permute(2, 0, 1) # [H, W] → [32, H, W]
```
> PNG визуализации квантуют float16 в uint8 (256 уровней). Для обучения используйте SafeTensors.
### Почему тензоры, а не RGB-рендеры
| Формат | Пример depth | Потеря информации | Для DINOv3 |
|---|---|---|---|
| `float16` тензор (хранится) | `[0.4231, 0.4235, ...]` | ~0% | Прямой вход в aux-encoder |
| `uint8` grayscale PNG | `[108, 108, ...]` | ~0.4% | Приемлемо |
| `turbo colormap` RGB PNG | `[R=50, G=180, B=220]` | **~70%** | **OOD** — DINOv3 обучен на натуральных RGB |
> Для обучения **всегда** используйте SafeTensors. PNG визуализации — только для просмотра в Obsidian/файловом менеджере.
### Миграция со старого формата