From 521c1f2e38eb05bba6d9e9f08adc76cf6e2e8191 Mon Sep 17 00:00:00 2001 From: Pavlenko_B Date: Tue, 30 Jun 2026 15:44:41 +0300 Subject: [PATCH] Add protocol --- PROTOCOL_HPO_EdgeNeXt_NarutoSign.md | 307 ++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 PROTOCOL_HPO_EdgeNeXt_NarutoSign.md diff --git a/PROTOCOL_HPO_EdgeNeXt_NarutoSign.md b/PROTOCOL_HPO_EdgeNeXt_NarutoSign.md new file mode 100644 index 0000000..f224f6d --- /dev/null +++ b/PROTOCOL_HPO_EdgeNeXt_NarutoSign.md @@ -0,0 +1,307 @@ +--- +type: protocol +status: draft +date: 2026-06-26 +component: backbone +supervisor: "мнс Павленко Б.В." +task_type: student-research +topic: hyperparameter-optimization +model: EdgeNeXt +dataset: Naruto Sign +tools: [Optuna, timm, UMAP] +related: + - "[[B134_BB_2022_EdgeNeXt_Efficient_CNN-Transformer_Mobile_Vision]]" + - "[[PROTOCOL_text_encoder_benchmark_StripNet_GTA_UAV]]" + - "[[ПРАКТИКА_2026-07-01_задания_16_студентов]]" + - "[[HYP_SOFIA_v77_design]]" +tags: + - protocol + - methodology + - hpo + - optuna + - edgenext + - mona + - transfer-learning + - classification + - component/backbone + - task/experiment +author: claude +--- + +# PROTOCOL: Подбор гиперпараметров для классификации изображений (EdgeNeXt × Naruto Sign × Optuna) + +> Учебно-исследовательская методичка для студента под руководством **мнс Павленко Б.В.** +> Тема: **оптимизация/подбор гиперпараметров (HPO)** для задачи классификации изображений на наборе **Naruto Sign** с компактным энкодером визуальных признаков **EdgeNeXt**. Сквозные инструменты: **Optuna** (поиск), **timm** (модель), **UMAP** (анализ признаков). Дополнительно изучаются **заморозка / частичная заморозка / MONA-адаптер** как точки на оси «ёмкость адаптации ↔ риск переобучения», подбираемые наравне с прочими гиперпараметрами. + +--- + +## §0. TL;DR + +| Что | Решение | +| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **Цель** | Освоить и применить методы HPO: на малом датасете Naruto Sign подобрать гиперпараметры дообучения EdgeNeXt так, чтобы максимизировать **macro-F1** на валидации, и обосновать выбор экспериментально | +| **Энкодер (фикс. семейство)** | **EdgeNeXt** (timm): `edgenext_xx_small.in1k` (1.3M) / `edgenext_x_small.in1k` (2.3M) / `edgenext_small.usi_in1k` (5.6M), ImageNet-pretrained — вариант сам по себе является гиперпараметром | +| **Датасет** | **Naruto Sign** — классификация ручных печатей. Verified (сиблинг-трек, см. §3): **13 классов** (12 печатей + `zero`), **~2245 изобр.** (train 2159 / test 86, **val нет**), PNG+JPG, лицензия «Unknown». Источник — [Kaggle vikranthkanumuru](https://www.kaggle.com/datasets/vikranthkanumuru/naruto-hand-sign-dataset) (ImageFolder). Числа **пересчитать после скачивания** | +| **Режимы переноса** | `full` (full fine-tuning) · `partial` (разморозить последние `n` стадий, `n=0` ⇒ linear probing) · `mona` (frozen backbone + Conv-MONA адаптеры, PEFT) | +| **Поиск** | **Optuna**, default = `TPESampler(multivariate=True)` + `MedianPruner`/`HyperbandPruner`; затем multi-objective (`NSGAIISampler`): macro-F1 ↑ vs trainable-params ↓ | +| **Главная метрика** | **macro-F1** (валидация). Доп.: balanced accuracy, top-1, MCC, confusion matrix. При дисбалансе accuracy — вводит в заблуждение | +| **Анализ данных** | Статистика датасета + **UMAP** эмбеддингов EdgeNeXt, раскраска по классам, кластеризация на промежуточном (10–20-мерном) вложении, ARI/NMI/silhouette → предсказание «трудных» классов | +| **Гипотезы** | H1 (TPE+pruning ≫ random/grid при равном бюджете) · H2 (оптимум — partial freeze, не full FT и не linear probing) · H3 (MONA ≥ full FT при <5% параметров) · H4 (loss/sampler против дисбаланса ↑ macro-F1) · H5 (multi-objective находит Pareto-лучшие edge-конфигурации) · H6 (UMAP-перекрытие классов предсказывает ошибки) — см. §8 | +| **Численные эксперименты** | B0 baseline → E1 ablation режимов → E2 Optuna single-objective → E3 Optuna multi-objective → E4 UMAP-анализ → (опц.) E5 sampler/random vs TPE — см. §9 | +| **Сроки** | 14 дней (MVP ~8–10 дней): рабочий честный baseline > богатство экспериментов | +| **Compute** | 1×RTX 4090; EdgeNeXt малый → прогон минуты; десятки trial-ов Optuna реалистичны | + +--- + +## §1. Контекст, цель и зачем проекту + +### 1.1 Учебная цель +Студент осваивает полный цикл HPO на «чистой» автономной CV-задаче: (1) что такое кодирование визуальных признаков и почему backbone = feature encoder; (2) какие бывают методы подбора гиперпараметров и чем Bayesian/TPE лучше grid/random; (3) как устроена Optuna; (4) что подбирать в DL-классификации (lr, wd, loss, аугментации, **глубина заморозки**, PEFT); (5) как честно мерить качество при дисбалансе; (6) как анализировать признаковое пространство (UMAP) и превращать наблюдения в гипотезы; (7) как провести и описать численные эксперименты. + +### 1.2 Зачем это проекту MERIDIAN/SOFIA +Задача автономна (публичные данные, не трогает проприетарный Triple-Teacher), но методически прямо питает проект: +- **EdgeNeXt** — критически релевантный для SOFIA-Tiny edge-backbone (см. конспект [[B134_BB_2022_EdgeNeXt_Efficient_CNN-Transformer_Mobile_Vision]]: 256×256 вход, Jetson-деплой, 1.3–5.6M params, SDTA channel-attention). Этот эксперимент — песочница для SDTA/adaptive-kernels интуиций. +- **MONA** — у проекта уже есть **Conv-MONA** в text-encoder бенчмарке ([[PROTOCOL_text_encoder_benchmark_StripNet_GTA_UAV]] §2.1): здесь студент реализует и измеряет тот же класс PEFT на чистой классификации. +- **Optuna-методология** переиспользуема для всего проекта (E0–E9 recipe-tuning), а decision-rule-культура (равный бюджет, фикс. seed, p<0.05, честные caveat) — общая для практики ([[ПРАКТИКА_2026-07-01_задания_16_студентов]] §«Общие правила»). + +### 1.3 Что НЕ изучаем +- ❌ Детекцию/сегментацию печатей (только классификация, ImageFolder→softmax). +- ❌ Архитектурный NAS (варьируем только пресет EdgeNeXt XXS/XS/S, не перестраиваем блоки). +- ❌ Дистилляцию от Teacher (это E2 MERIDIAN, не эта задача). + +--- + +## §2. Карта теоретических глав (читать перед экспериментами) + +Подробная теория вынесена в самостоятельные главы (папка `_research/`). Каждая — самодостаточна, с формулами, таблицами и проверенными источниками: + +| Глава | Файл | О чём | +|:--|:--|:--| +| **Кодирование признаков и EdgeNeXt** | [[SECTION_edgenext]] | feature encoding, архитектура EdgeNeXt (stem/4 стадии/SDTA/adaptive kernels), варианты, **timm-практика** (`create_model`, `forward_features`, `forward_head(pre_logits=True)`, `resolve_model_data_config`) | +| **Методы HPO (таксономия)** | [[SECTION_hpo_methods]] | гиперпараметры vs параметры, grid/random ([Bergstra&Bengio 2012]), Bayesian (GP/TPE/SMAC, EI/UCB), CMA-ES/PBT, multi-fidelity (Successive Halving/Hyperband/ASHA/BOHB), фреймворки, методология, типичные ошибки | +| **Optuna (полный разбор)** | [[SECTION_optuna]] | Study/Trial/define-by-run, **samplers** (TPE/GP/CMA-ES/NSGA-II/III/QMC/**AutoSampler**), **pruners** (Median/ASHA/Hyperband/...), multi-objective+constraints, v4.5/v4.6, storage, визуализация, интеграция с PyTorch, шпаргалка «sampler+pruner» | +| **Заморозка / частичная заморозка** | [[SECTION_freezing]] | full FT vs linear probing vs partial freeze, **LP-FT** ([Kumar 2022]), механика `requires_grad`/`eval()`/FrozenBN, gradual unfreezing, discriminative LR, глубина заморозки как гиперпараметр | +| **MONA-layer (PEFT)** | [[SECTION_mona]] | adapter-PEFT, разбор Mona ([arXiv:2408.08345], CVPR 2025) по коду репозитория `Leiyi-Hu/mona`: scaled-LayerNorm + MonaOp (DW 3/5/7) + down/up-proj; «5%>100% full FT»; vs LoRA/Adapter; PyTorch-скелет | +| **UMAP (анализ признаков)** | [[SECTION_umap]] | интуиция, PCA/t-SNE/UMAP, параметры (`n_neighbors`/`min_dist`/`metric`), пайплайн EdgeNeXt→UMAP, кластеризация на промежуточном вложении, ARI/NMI/AMI/silhouette, предостережения | +| **Классификация: метрики/loss/sampling** | [[SECTION_classification]] | softmax/top-k, метрики (macro/micro/weighted-F1, balanced acc, MCC, ECE), loss (CE/LS/Focal/Class-Balanced/LDAM/KD), семплирование (WeightedRandomSampler, mixup/cutmix), связка loss×sampler | + +--- + +## §3. Датасет Naruto Sign — протокол подготовки + +Полное описание и источники — в [[SECTION_naruto_dataset]]. + +> [!info] Verified-факты (из параллельного студенческого трека под рук. Устенко В.Ю. — тот же датасет; зафиксированы в рабочей памяти проекта) +> Тот же Kaggle-набор `vikranthkanumuru/naruto-hand-sign-dataset` уже верифицирован параллельным треком: **13 классов** (12 печатей + `zero`/покой), **2245 изображений** (train 2159 / test 86, **val отсутствует**), 1797 PNG + 448 JPG, реальные фото рук, лицензия **«Unknown»**, числа из notebook 2020. Поэтому: `num_classes=13` по умолчанию; **val нет** → выделяется стратифицированно из train (реализовано в [`data.py`](code/src/data.py), ветка `use_predefined_split`); **не** переоткрывать эти факты заново — только пересчитать `dataset_stats.py` после скачивания (числа могли измениться). Документ трека Устенко может находиться в другой ветке репозитория. + +Ключевые решения протокола: + +1. **Источник по умолчанию:** Kaggle `vikranthkanumuru/naruto-hand-sign-dataset` (формат ImageFolder, заточен под классификацию). Roboflow-наборы — детекционные, использовать как запасные (crop по bbox). +2. **Сверить статистику после скачивания** скриптом [`dataset_stats.py`](code/src/dataset_stats.py): число классов (12 или 13?), всего изображений, per-class, разрешения, дисбаланс. Это **первый артефакт** студента — таблица статистики в отчёте. +3. **Стратифицированный split** (seed=42), доли 70/15/15. Реализация — [`data.py`](code/src/data.py) (`_stratified_indices`). +4. **Утечка из видео — главный риск честности.** Кадры извлечены из видео покадрово ⇒ соседние кадры почти идентичны. Случайный per-frame split **завышает** метрику. Если в именах файлов есть id видео/серии — делать **group-split** (`StratifiedGroupKFold`, см. [[SECTION_naruto_dataset]] §5). Если id нет — отметить риск в отчёте и сделать дедупликацию near-duplicate (perceptual hash). Базовый `data.py` делает per-frame split — это допустимо для учебного MVP, **но факт и риск утечки обязательно фиксируется в REPORT.md**. +5. **Размер входа** — 256×256 (дефолт EdgeNeXt), нормализация ImageNet (берётся из `timm.data.resolve_model_data_config`). + +> [!danger] Horizontal flip — отключён по умолчанию +> Печать — это **конкретная конфигурация левой/правой руки**; зеркалирование меняет семантику жеста ⇒ label noise. В [`data.py`](code/src/data.py) `use_hflip=False` зафиксировано и **не подаётся в Optuna**. См. [[SECTION_naruto_dataset]] §6. + +--- + +## §4. Задача классификации — метрики, loss, семплирование (решения) + +Теория — [[SECTION_classification]]. Решения протокола: + +| Аспект | Решение | Обоснование | +|:--|:--|:--| +| **Главная метрика (objective Optuna)** | **macro-F1** на валидации | равный вес классам; accuracy при дисбалансе вводит в заблуждение | +| Сопутствующие метрики | balanced accuracy, top-1, MCC, Cohen κ, per-class F1, confusion matrix | полная картина; confusion matrix — диагностика похожих печатей | +| Loss (категориальный гиперпараметр) | `{ce, ce_ls, weighted_ce, focal}` | от baseline до imbalance-aware; LS для калибровки, focal/weighted/CB для дисбаланса | +| Веса классов | effective-number ([Cui 2019], `cb_beta`) | мягче, чем грубая обратная частота | +| Семплирование | `WeightedRandomSampler` (вкл/выкл — гиперпараметр) | альтернатива loss-ребалансировке | +| ⚠️ Двойная компенсация | **не** включать `weighted_ce/focal` + `WeightedRandomSampler` одновременно с полной силой | переусиление minority → падение precision (см. [[SECTION_classification]] §5) | +| Аугментации (гиперпараметры) | RandomResizedCrop, лёгкий rotation, color jitter, RandAugment(M), mixup(α) | mixup/RandAugment — «умный oversampling»; **без hflip** | + +Реализация: [`metrics.py`](code/src/metrics.py) (sklearn), [`losses.py`](code/src/losses.py) (`FocalLoss`, `effective_number_weights`, `build_criterion`), [`data.py`](code/src/data.py) (`_make_sampler`, `compute_class_weights`). + +--- + +## §5. Модель и режимы переноса (заморозка / частичная / MONA) + +Теория — [[SECTION_edgenext]], [[SECTION_freezing]], [[SECTION_mona]]. Три режима реализованы в [`model.py`](code/src/model.py) (`build_model`, `ModelConfig.regime`): + +| Режим | Что обучается | Когда ожидаемо лучше | Параметризация в Optuna | +|:--|:--|:--|:--| +| `full` | весь backbone + голова (2 LR-группы) | данных достаточно / домен далёк | — | +| `partial` | последние `n_unfrozen_stages` стадий + голова; `n=0` ⇒ linear probing | малый датасет, нужна частичная адаптация | `n_unfrozen_stages ∈ [0,4]` | +| `mona` | frozen backbone + **Conv-MONA** адаптеры + голова (PEFT, <5% весов) | малый датасет, адаптация без переобучения | `mona_bottleneck`, `mona_kernels`, `mona_last_only` | + +Ключевые инженерные детали (см. [[SECTION_freezing]] §2): +- Замороженные параметры **исключаются** из optimizer (`build_param_groups` берёт только `requires_grad=True`). +- Замороженные **norm-слои переводятся в `eval()`** каждый train-шаг (`freeze_consistency`) — иначе running-статистика BN «уезжает». EdgeNeXt в основном на LayerNorm (этой ловушки нет), но правило соблюдаем для гибридных модулей. +- **Discriminative LR:** backbone получает `base_lr × backbone_lr_mult` (по умолчанию 0.1), голова/адаптеры — `base_lr`. + +MONA-адаптер ([`mona.py`](code/src/mona.py)) — conv-вариант (вход `[B,C,H,W]`), верный коду репозитория `Leiyi-Hu/mona`: scaled-LayerNorm (`γ·LN(x)+γx·x`) → down 1×1 → `MonaOp` (DW 3/5/7, два residual) → GELU → up 1×1 (zero-init ⇒ старт = identity). Вставляется forward-hook'ом после выбранных стадий (число каналов определяется dry-run'ом, без зависимости от `feature_info`). + +--- + +## §6. Анализ признаков через UMAP + +Теория и предостережения — [[SECTION_umap]]. Реализация — [`umap_analysis.py`](code/src/umap_analysis.py). + +Пайплайн: `forward_features` → `forward_head(pre_logits=True)` → L2-норма → матрица `[N,D]` → +(а) UMAP→**2D** (раскраска по истинному классу, `random_state=42`, `metric=cosine`) для визуализации; +(б) UMAP→**10–20D** для **кластеризации** (KMeans с `k`=число классов) → silhouette, **ARI/NMI** относительно истинных меток. + +Что извлечь: +- Какие печати образуют изолированные «острова» (легко различимы), какие перекрываются (Ram/Snake/Dragon — сложенные ладони). +- **Сверка с confusion matrix**: перекрытие в эмбеддинге ⇒ off-diagonal масса. Это валидирует, что ошибки идут от признаков, а не от головы. +- Выполнить анализ **дважды**: на frozen ImageNet-EdgeNeXt (до дообучения) и на лучшей дообученной модели (после) → показать, как дообучение «растягивает» классы. + +> [!warning] UMAP — инструмент генерации гипотез, не доказательство. Не интерпретировать абсолютные расстояния/размеры кластеров; фиксировать seed; выводы подтверждать метриками (§5 [[SECTION_umap]]). + +--- + +## §7. Выбор метода и настройка Optuna + +Обоснование выбора метода — [[SECTION_hpo_methods]]; разбор Optuna — [[SECTION_optuna]]. Решения: + +- **Single-objective (E2):** `TPESampler(multivariate=True, seed=42)` + `MedianPruner` (или `HyperbandPruner`, если бюджет на trial неясен). Прунинг по эпохам через `trial.report(macro_f1, epoch)` + `trial.should_prune()` — реализовано в [`train.py`](code/src/train.py). +- **Multi-objective (E3):** `NSGAIISampler` или `AutoSampler` (OptunaHub), `directions=["maximize","minimize"]` = macro-F1 ↑ vs trainable-params ↓ → Pareto-фронт edge-конфигураций. +- **Baseline для честного сравнения метода (E5, опц.):** `RandomSampler` при **равном** бюджете trial-ов и **том же** пространстве поиска. +- **Storage:** SQLite (`--storage`) для персистентности и дашборда; `gc_after_trial=True` (VRAM). +- **Пространство поиска** (define-by-run, [`optuna_search.py`](code/src/optuna_search.py) `suggest_configs`): `model_name`, `regime` (+условные `n_unfrozen`/MONA-параметры), `lr` (log), `weight_decay` (log), `optimizer`, `backbone_lr_mult` (log), `loss_name` (+условный `focal_gamma`), `label_smoothing`, `weighted_sampler`, `img_size`, `batch_size`, `mixup_alpha`, `rrc_scale_min`, `randaug_magnitude`, `drop_rate`, `drop_path_rate`. +- **После поиска:** `plot_optimization_history`, `plot_param_importances` (fANOVA — какие гиперпараметры реально важны), `plot_slice`, `plot_pareto_front`. + +> [!important] Лог-шкала для `lr`/`weight_decay` обязательна. Их эффект мультипликативен; линейный сэмплинг почти не пробует малые значения (см. [[SECTION_hpo_methods]] §8.1). + +--- + +## §8. Гипотезы + +Формат: «Если [метод], то [результат], потому что [обоснование]». Каждую проверяем численно (§9) при **равном бюджете** и фикс. seed; вывод — со стат-проверкой (bootstrap-CI / 3 seed), а не по разнице в 0.1%. + +| ID | Гипотеза | Проверка | Основание | +|:--|:--|:--|:--| +| **H1** | **Если** искать TPE+pruning, **то** при равном бюджете trial-ов он найдёт конфиг с macro-F1 не хуже (обычно лучше) random/grid, **потому что** TPE моделирует $\ell(\lambda)/g(\lambda)$ и концентрирует бюджет в перспективной зоне, а pruner отсеивает плохих рано | E2 vs E5 (random), та же сетка, тот же бюджет | [Bergstra 2011/2012], [Akiba 2019] — [[SECTION_hpo_methods]], [[SECTION_optuna]] | +| **H2** | **Если** варьировать глубину заморозки, **то** оптимум — **partial freeze** (разморозить 1–2 верхние стадии), а не full FT и не linear probing, **потому что** на малом датасете full FT переобучается/искажает признаки (feature distortion), а linear probing не хватает ёмкости адаптации | E1 ablation (`n_unfrozen ∈ {0,1,2,3,4}`) + важность параметра `regime`/`n_unfrozen` в E2 | [Kumar 2022 LP-FT], CS231n — [[SECTION_freezing]] | +| **H3** | **Если** применить **MONA** (frozen backbone + адаптеры, <5% параметров), **то** macro-F1 ≥ full FT при многократно меньшем числе обучаемых параметров, **потому что** multi-scale conv-фильтры дают vision-friendly адаптацию, а заморозка регуляризует на малых данных | E1 (`full` vs `partial` vs `mona`) при равном бюджете; E3 Pareto (F1 vs params) | [Yin 2024 Mona] — [[SECTION_mona]] | +| **H4** | **Если** включить imbalance-aware loss/sampler (weighted/focal/CB или WeightedRandomSampler), **то** вырастет **macro-F1 / balanced accuracy / recall редких классов** (ценой лёгкого падения overall accuracy), **потому что** редкие классы получают больший вклад в обучение | E2: важность `loss_name`/`weighted_sampler`; целевой контраст на самых редких классах | [Cui 2019], [Lin 2017] — [[SECTION_classification]] | +| **H5** | **Если** оптимизировать multi-objective (macro-F1 ↑, trainable-params ↓), **то** Pareto-фронт даст **edge-предпочтительные** конфигурации (например MONA/partial-малый вариант) при незначимой потере F1, **потому что** PEFT/малые варианты EdgeNeXt дают высокую точность при низкой ёмкости | E3 (`NSGAIISampler`/`AutoSampler`), анализ `plot_pareto_front` | [[SECTION_optuna]] §4, [[SECTION_edgenext]] §3 | +| **H6** | **Если** классы перекрываются в UMAP-эмбеддинге, **то** именно эти пары дают наибольшую off-diagonal массу в confusion matrix, **потому что** ошибки классификатора порождаются неразделимостью в признаковом пространстве | E4: сверка UMAP-перекрытий с confusion matrix лучшей модели; ARI/NMI | [McInnes 2018] — [[SECTION_umap]] | + +--- + +## §9. План численных экспериментов и decision-rules + +Все прогоны: seed=42 (финальные сравнения — 42/123/456), **равный бюджет** на сравниваемые конфигурации, меняется **только один фактор**. Эталон метрик — [`metrics.py`](code/src/metrics.py). + +```mermaid +flowchart TD + D[Naruto Sign
ImageFolder] --> S[dataset_stats.py
STATS.md] + D --> SP[Стратиф. split seed=42
data.py] + SP --> B0[B0 baseline
partial n=1, ce_ls] + B0 --> E1{E1 ablation режимов
full / partial 0..4 / mona} + E1 -->|лучший режим| E2[E2 Optuna single-obj
TPE + MedianPruner
objective = val macro-F1] + E2 -->|best_params| FT[Финал: 3 seed
+ test + bootstrap-CI] + E2 --> E3[E3 Optuna multi-obj
NSGA-II: F1 up vs params down
Pareto-front] + E2 -. контроль .-> E5[E5 random vs TPE
равный бюджет, H1] + FT --> E4[E4 UMAP до/после
2D + кластеризация
ARI/NMI] + E4 --> CM[Сверка с confusion matrix
H6] + E1 --> HYP[Гипотезы H2/H3] + E3 --> HYP5[Гипотеза H5] + E2 --> HYP4[Важность параметров fANOVA
H4] + HYP --> R[REPORT.md
вердикты H1-H6 + caveat] + HYP4 --> R + HYP5 --> R + CM --> R +``` + +| Этап | Что делаем | Команда | Артефакт | Decision-rule | +|:--|:--|:--|:--|:--| +| **B0** baseline | Один честный прогон `partial, n=1, ce_ls`, eval на test + confusion | `python -m src.run_baseline --data-root <...> --regime partial --n-unfrozen 1` | `report.json`, `confusion.png` | Зафиксировать baseline macro-F1, который HPO обязан побить | +| **E1** ablation режимов | `full` vs `partial(n=0..4)` vs `mona` при равном recipe/бюджете | `run_baseline` × режимы (скрипт-обёртка) | таблица режим×(macro-F1, params), кривые | H2/H3: лучший режим; если `mona`≥`full` при <5% params — H3 поддержана | +| **E2** Optuna single-obj | TPE+pruner, objective=val macro-F1, 40–80 trial | `python -m src.optuna_search --data-root <...> --sampler tpe --pruner median --n-trials 60 --storage` | `*_best.json`, `*_trials.csv`, importance/history/slice png | Лучший trial должен побить B0; зафиксировать best_params | +| **E3** Optuna multi-obj | NSGA-II/AutoSampler, F1↑ vs params↓, 60–100 trial | `python -m src.optuna_search --data-root <...> --multi-objective --sampler nsga --n-trials 80` | Pareto-front png + таблица недоминируемых | H5: выбрать edge-точку (max F1 при params ≤ бюджета) | +| **E4** UMAP-анализ | эмбеддинги до/после дообучения, 2D + кластеризация | `python -m src.umap_analysis --data-root <...> [--checkpoint best.pt]` | `umap_2d.png`, `cluster_report.json` | H6: сверить перекрытия с confusion matrix | +| **E5** (опц.) метод-контроль | random vs TPE при равном бюджете и пространстве | `--sampler random` vs `--sampler tpe` | кривые «best-so-far vs число trial» | H1: TPE ≥ random; вывод со стат-проверкой | + +**Финальная переоценка:** топ-конфиг(и) из E2/E3 переобучить на 3 seed (42/123/456), сравнить по среднему ± std, оценить на **test** (трогается один раз) + bootstrap-CI. Только после этого делать выводы. + +> Абсолютные числа могут быть скромными при малом/«грязном» датасете — оценивается **корректность методологии** (равный бюджет, фикс. seed, отсутствие утечки, честный eval, признание caveat), а не голый macro-F1. + +--- + +## §10. Воспроизводимость, честность, типичные ловушки + +- **Seed** 42 везде (`set_seed` в [`train.py`](code/src/train.py)); финал — 3 seed. +- **Test трогать один раз**, в самом конце; подбор — только на val (иначе утечка test в HPO). +- **Не сравнивать при разном бюджете** методы/конфиги; единое пространство поиска. +- **Лог-шкала** для lr/wd. +- **Заморозка BN** — `freeze_consistency` после каждого `model.train()`. +- **Двойная компенсация дисбаланса** — не включать sampler + weighted-loss одновременно на полную (см. [[SECTION_classification]] §5). +- **Утечка из видео** — главный риск Naruto Sign; зафиксировать в REPORT.md, при наличии id — group-split. +- **UMAP** — не «доказывать» кластерами; фиксировать seed, подтверждать метриками. +- **Атомарная запись** результатов (`os.replace`), `gc_after_trial` для VRAM. +- Отрицательные результаты и подтверждённые caveat фиксировать явно — это плюс. + +--- + +## §11. Что сдаёт студент + +1. **Код** (папка [`code/`](code/)) с `README.md` и `requirements.txt` — запускается на чужой машине по инструкции. +2. **`STATS.md`** — таблица статистики Naruto Sign по факту скачивания ([`dataset_stats.py`](code/src/dataset_stats.py)) + источник/версия/лицензия. +3. **`REPORT.md`** — результаты всех этапов: таблицы (B0/E1/E2/E3), кривые обучения, importance-график Optuna, Pareto-фронт, UMAP-картинки (до/после) + confusion matrix, проверка каждой гипотезы H1–H6 с вердиктом (поддержана/опровергнута/неубедительно), анализ ошибок (3–5 примеров), честные caveat. +4. **Артефакты:** `*_trials.csv`, `*_best.json`, `umap_2d.png`, `cluster_report.json`, лучший чекпойнт + config, папка PNG. + +**MVP (≈8–10 дней):** статистика + B0 baseline + E1 ablation 3 режимов + E2 Optuna (≥40 trial, TPE+pruner) с побитием baseline + E4 UMAP (2D + ARI) + REPORT с проверкой ≥3 гипотез. +**Stretch:** E3 multi-objective + Pareto-анализ; E5 random vs TPE со стат-проверкой; 3-seed переоценка + bootstrap-CI; MONA-абляция (bottleneck/ядра) внутри Optuna; group-split без утечки; AutoSampler. + +--- + +## §12. Календарь (14 дней) + +| Дни | Задача | +|:--|:--| +| 1–2 | Скачать датасет, `dataset_stats.py`, `STATS.md`; прочитать главы [[SECTION_edgenext]], [[SECTION_classification]] | +| 3 | Поднять окружение (`requirements.txt`), smoke-тест `build_model` для 3 режимов; прочитать [[SECTION_freezing]], [[SECTION_mona]] | +| 4 | **B0** baseline + confusion matrix | +| 5–6 | **E1** ablation режимов (full/partial 0–4/mona); таблица + кривые | +| 6 | Прочитать [[SECTION_hpo_methods]], [[SECTION_optuna]] | +| 7–9 | **E2** Optuna single-objective (TPE+pruner), importance/history/slice; побить B0 | +| 9 | **E4** UMAP (до/после), сверка с confusion matrix | +| 10 | Воспроизводимый прогон зафиксирован; черновик REPORT | +| 11–12 | **E3** multi-objective + Pareto (stretch); 3-seed переоценка | +| 13 | **E5** random vs TPE (stretch); анализ ошибок | +| 14 | Финализация REPORT.md/README.md, графики, выводы, caveat | + +--- + +## §13. Карта кода (`code/`) + +| Файл | Назначение | +|:--|:--| +| [`src/data.py`](code/src/data.py) | ImageFolder, стратифицированный split, transforms (hflip OFF), WeightedRandomSampler, class weights | +| [`src/model.py`](code/src/model.py) | EdgeNeXt (timm), режимы full/partial/mona, freeze-логика, param-groups (discriminative LR), `extract_features` | +| [`src/mona.py`](code/src/mona.py) | Conv-MONA адаптер (по коду `Leiyi-Hu/mona`), вставка forward-hook'ом после стадий | +| [`src/losses.py`](code/src/losses.py) | CE/LS/weighted/Focal, effective-number weights, `build_criterion` | +| [`src/metrics.py`](code/src/metrics.py) | macro-F1, balanced acc, top-k, MCC, κ, confusion (sklearn) | +| [`src/train.py`](code/src/train.py) | train/eval цикл, mixup (timm), early-stop, **Optuna pruning** (`report`/`should_prune`), VRAM-hygiene | +| [`src/optuna_search.py`](code/src/optuna_search.py) | пространство поиска (define-by-run), single/multi-objective, sampler+pruner, экспорт best/trials | +| [`src/umap_analysis.py`](code/src/umap_analysis.py) | эмбеддинги → UMAP 2D + кластеризация → silhouette/ARI/NMI + 2D-картинка | +| [`src/run_baseline.py`](code/src/run_baseline.py) | одиночный прогон + test-метрики + confusion-heatmap (B0/E1) | +| [`src/dataset_stats.py`](code/src/dataset_stats.py) | статистика ImageFolder (число классов, per-class, дисбаланс, разрешения) | + +> Конфигурация: задача управляется Optuna (define-by-run) + argparse/dataclass, поэтому здесь сознательное отступление от gin-strict (`.claude/rules/python-code.md`) — пространство гиперпараметров описывается в `suggest_configs`, а не в статическом `.gin`. Остальные конвенции (type hints, Google-docstrings, `from __future__ import annotations`, seeds, atomic writes, `inference_mode`) соблюдены. + +--- + +## §14. Чем задание выявляет сильного студента + +Слабый студент сделает MVP буквально (посчитает числа, прогонит Optuna). Сильный проявит **критическое мышление**: +- осознает и измерит **утечку из видео** (group-split vs per-frame), не примет завышенный R/F1 за чистую монету; +- проверит H2/H3 честно (равный бюджет) и свяжет вывод с теорией (LP-FT, «5%>100%»), а не просто выберет «что победило»; +- покажет **важность гиперпараметров** (fANOVA) и сузит пространство во второй итерации; +- сверит UMAP-перекрытия с confusion matrix (H6) — увяжет признаковый анализ с ошибками; +- докажет H1 со стат-проверкой (random vs TPE при равном бюджете), а не на одном seed; +- честно зафиксирует отрицательные результаты и caveat (hflip, двойная компенсация, малый/грязный датасет). +- **Сквозной маркер:** способность связать эксперимент с проектом (EdgeNeXt ↔ SOFIA-Tiny [[B134_BB_2022_EdgeNeXt_Efficient_CNN-Transformer_Mobile_Vision]]; Conv-MONA ↔ [[PROTOCOL_text_encoder_benchmark_StripNet_GTA_UAV]]) и сделать честный вывод с признанием caveat.