Files
PracticeClassif/_research/SECTION_mona.md
2026-06-30 15:17:40 +03:00

259 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mona-layer (Multi-cognitive Visual Adapter) — adapter-based PEFT для зрения
> Источники: статья [Yin, Hu, Li, Zhang, Yang — «5%>100%: Breaking Performance Shackles of Full Fine-Tuning on Visual Recognition Tasks», arXiv:2408.08345 (CVPR 2025)](https://arxiv.org/abs/2408.08345); официальная реализация [GitHub Leiyi-Hu/mona](https://github.com/Leiyi-Hu/mona).
> Все формулы блока и псевдокод ниже выверены по файлу [`swin_transformer_mona.py`](https://raw.githubusercontent.com/Leiyi-Hu/mona/master/Swin-Transformer-Classification/mmcls/models/backbones/swin_transformer_mona.py) (классы `Mona` и `MonaOp`).
---
## 1. Проблема: full fine-tuning против PEFT
Классическая схема переноса — *pre-train & fine-tune*: большой backbone обучается на крупном корпусе (ImageNet-21k, LVD-142M и т.п.), затем **все** его веса дообучаются на целевой задаче. На больших целевых датасетах это работает, но имеет два дефекта на **малых** данных:
- **Стоимость.** Для Swin-L это ~198M обучаемых параметров и отдельная полная копия весов на каждую задачу/чекпойнт ([Yin et al., 2024](https://arxiv.org/abs/2408.08345)).
- **Переобучение.** При малом числе примеров обновление всех параметров подгоняет backbone под шум обучающей выборки; обобщение падает.
**PEFT (parameter-efficient fine-tuning, она же delta-tuning)** решает обе проблемы: backbone **заморожен** ($\nabla_\theta = 0$), обучается лишь малая доля новых параметров $\Delta$ (обычно 0.15 %). Семейства PEFT:
| Семейство | Что обучается | Представитель |
|-----------|---------------|---------------|
| Bias-tuning | только bias-векторы | BitFit |
| Low-rank | низкоранговые добавки $BA$ к линейным слоям | LoRA |
| Adapter (последовательный) | bottleneck-MLP внутри блока | Houlsby Adapter, AdaptFormer |
| Prompt-tuning | обучаемые prompt-токены на входе | VPT |
| Side-tuning | внешняя «боковая» сеть | LST |
Главная претензия статьи Mona: до неё **ни один visual delta-tuning не превосходил full fine-tuning на сложных плотных задачах** (detection, segmentation). Mona — первый метод, который это делает на всех протестированных задачах ([arXiv:2408.08345, abstract](https://arxiv.org/abs/2408.08345)).
---
## 2. Что такое Mona-блок
Mona — это **последовательный adapter** (вставляется в residual-ветку трансформерного блока), но с двумя ключевыми отличиями от языковых адаптеров:
1. **Multi-cognitive фильтры** — внутри bottleneck стоят не линейные слои, а набор **depth-wise свёрток разных масштабов** (3×3, 5×5, 7×7), которые усредняются. Это даёт «vision-friendly» обработку (локальность, разные рецептивные поля).
2. **Scaled LayerNorm на входе** — нормализация с **двумя обучаемыми покомпонентными масштабами** $\gamma$ и $\gamma_x$, регулирующая распределение признаков перед визуальными фильтрами.
### 2.1. Структура (по реализации)
Внутренний модуль `MonaOp` (multi-scale conv-агрегатор), работает на тензоре $z \in \mathbb{R}^{B\times C_b\times H\times W}$ с $C_b = 64$ (bottleneck), все свёртки **depth-wise** (`groups = C_b`):
$$
\mathrm{MonaOp}(z) = z' + \mathrm{Proj}_{1\times1}(z'),\qquad
z' = \frac{\mathrm{DW}_{3}(z) + \mathrm{DW}_{5}(z) + \mathrm{DW}_{7}(z)}{3} + z
$$
где $\mathrm{DW}_{k}$ — depth-wise conv с ядром $k\times k$ и padding $k//2$, $\mathrm{Proj}_{1\times1}$ — pointwise conv $1\times1$. Обе агрегации обёрнуты в residual.
Внешний модуль `Mona` (вход — последовательность токенов $x \in \mathbb{R}^{B\times N\times C}$, $N = H\cdot W$):
$$
\hat{x} = \mathrm{LayerNorm}(x)\odot \gamma + x \odot \gamma_x \qquad\text{(scaled norm, обучаемые } \gamma,\gamma_x\in\mathbb{R}^{C})
$$
$$
u = W_{\text{down}}\,\hat{x} \in \mathbb{R}^{B\times N\times 64}\quad\text{(down-projection, Linear } C\to 64)
$$
$$
u' = \mathrm{MonaOp}\big(\mathrm{reshape}(u)\big)\quad\text{(токены} \to \text{карта } H\times W \to \text{токены)}
$$
$$
y = W_{\text{up}}\,\mathrm{Dropout}\big(\mathrm{GELU}(u')\big)\in\mathbb{R}^{B\times N\times C}\quad\text{(up-projection, Linear } 64\to C)
$$
$$
\boxed{\;\mathrm{Mona}(x) = x + y\;}\quad\text{(внешний residual)}
$$
Инициализация масштабов в коде: $\gamma = 10^{-6}\cdot\mathbf{1}$ (LayerNorm-ветка стартует почти «выключенной»), $\gamma_x = \mathbf{1}$ (тождественный проброс входа). Это делает Mona при инициализации **близкой к identity** — стабильный старт для дообучения.
> Замечание по терминологии: «scaled LayerNorm» здесь = `LayerNorm(x)*γ + x*γx`, то есть **выпуклоподобная** смесь нормированного и исходного признака с обучаемыми покомпонентными весами, а не нормировка с фиксированным множителем.
---
## 3. Куда вставляется и сколько добавляет
В Swin-блоке Mona вставляется **дважды на каждый трансформерный блок** — после attention-ветки и после FFN-ветки (см. `SwinTransformerBlock.forward`):
```
shortcut → (W-MSA/SW-MSA) → x = shortcut + drop_path(x)
x = Mona_1(x, (H,W)) # adapter после attention
identity = x; x = norm2(x); x = MLP(x)
x = identity + drop_path(x)
x = Mona_2(x, (H,W)) # adapter после FFN
```
- **Замороженным остаётся весь backbone** (attention, MLP, patch-merging, embeddings). Обучаются: параметры всех `Mona`-модулей (down/up Linear, 3 depth-wise conv + 1×1 projector, $\gamma$, $\gamma_x$) и task-head/decoder.
- **Доля обучаемых параметров backbone:** ~**2.56 %** на Swin-L и ~**4.67 %** на Swin-B (см. таблицы §4) ([arXiv:2408.08345, Tables](https://arxiv.org/abs/2408.08345)). Bottleneck фиксирован на 64 каналов, поэтому стоимость adapter не растёт квадратично с $C$.
---
## 4. Результаты статьи
Сравнение проведено на Swin-B/Swin-L (ImageNet-21k pre-train) против full fine-tuning и набора PEFT. Ниже — выверенные числа из [arXiv:2408.08345](https://arxiv.org/html/2408.08345v1).
### 4.1. COCO instance segmentation (Cascade Mask R-CNN, Swin-B)
| Метод | Обуч. параметры | % | AP$_{box}$ | AP$_{mask}$ |
|-------|------------------|------|-----------|-------------|
| Full fine-tuning | 89.14M | 100.0 | 52.40 | 45.10 |
| Fixed (frozen) | 0M | 0.0 | 48.00 | 41.60 |
| BitFit | 0.21M | 0.23 | 50.10 | 43.60 |
| LoRA | 3.06M | 3.43 | 50.40 | 43.90 |
| Adapter (Houlsby) | 3.19M | 3.58 | 52.10 | 45.00 |
| AdaptFormer | 1.60M | 1.79 | 51.70 | 44.60 |
| **Mona** | **4.16M** | **4.67** | **53.40** | **46.00** |
### 4.2. ADE20K semantic segmentation (UperNet, Swin-L)
| Метод | Обуч. параметры | % | mIoU |
|-------|------------------|------|------|
| Full fine-tuning | 198.58M | 100.0 | 51.18 |
| Fixed | 0M | 0.0 | 46.84 |
| BitFit | 0.30M | 0.15 | 48.37 |
| LoRA | 4.57M | 2.31 | 50.34 |
| Adapter | 4.61M | 2.33 | 50.78 |
| AdaptFormer | 2.34M | 1.18 | 50.83 |
| **Mona** | **5.08M** | **2.56** | **51.36** |
### 4.3. Pascal VOC detection (RetinaNet, Swin-L) и DOTA oriented (Oriented R-CNN, Swin-B)
| Метод | VOC AP$_{box}$ | DOTA mAP |
|-------|----------------|----------|
| Full fine-tuning | 83.70 | 78.31 |
| BitFit | 85.40 | 76.05 |
| LoRA | 85.40 | 75.91 |
| Adapter | 86.70 | 78.27 |
| AdaptFormer | 86.60 | 77.43 |
| **Mona** | **87.30** | **78.44** |
### 4.4. Image classification (Swin-L, усреднение по датасетам)
| Метод | Top-1 Acc | Top-5 Acc |
|-------|-----------|-----------|
| Full fine-tuning | 92.79 | 98.81 |
| BitFit | 93.51 | 99.69 |
| LoRA | 93.47 | 99.69 |
| Adapter | 93.99 | 99.61 |
| AdaptFormer | 93.70 | 99.65 |
| **Mona** | **94.04** | **99.76** |
### 4.5. Почему «5 % > 100 % full FT»
- На **плотных** задачах (detection, segmentation) full FT — сильный baseline, и линейные PEFT (LoRA, BitFit, AdaptFormer) его **не достают**. Mona обходит full FT на всех задачах: +1.0 AP$_{box}$ / +0.9 AP$_{mask}$ на COCO, +0.18 mIoU на ADE20K, +3.6 AP на VOC.
- Причина: **conv-фильтры разных масштабов** дают индуктивное смещение, подходящее под изображения (локальность, многомасштабность), которого нет у линейных адаптеров; а **заморозка backbone** регуляризует — на малых данных полная подгонка переобучается, а 5 % параметров «достаточно гибки, но не настолько, чтобы запомнить шум».
---
## 5. Mona vs LoRA vs обычный Adapter
| Критерий | LoRA | Adapter (Houlsby) | **Mona** |
|----------|------|-------------------|----------|
| Куда вставляется | добавка $\Delta W = \frac{\alpha}{r}BA$ к линейным слоям (q,v) | bottleneck-MLP внутри блока | bottleneck **с conv** в residual-ветке (после attn и после FFN) |
| Внутренние операторы | две линейные матрицы $A,B$ | down-Linear → нелинейность → up-Linear | down-Linear → **3× depth-wise conv (3/5/7) + 1×1** → GELU → up-Linear |
| Учёт пространства | нет (работает потокенно) | нет | **да** (reshape в карту $H\times W$, multi-scale свёртки) |
| Нормализация входа | нет | обычно нет | **scaled LayerNorm** с обучаемыми $\gamma,\gamma_x$ |
| Слияние в инференсе | можно слить в веса (zero-cost) | нет (доп. слой) | нет (доп. слой) |
| Сильная сторона | дёшево, мерджится | универсален | **vision-friendly, превосходит full FT на dense-задачах** |
| Доля параметров | ~23 % | ~2.33.6 % | ~2.54.7 % |
Ключевое отличие: LoRA и Houlsby-Adapter оперируют **только линейными** преобразованиями признаков и не используют 2D-структуру; Mona добавляет **визуальное индуктивное смещение** (multi-scale depth-wise conv) и **adaptive scaled norm**, поэтому выигрывает именно на изображениях.
---
## 6. Минимальный PyTorch-скелет Mona
Рабочий минимум, эквивалентный официальному коду (`bottleneck=64` по умолчанию, как в репозитории; здесь параметризован).
```python
import torch
import torch.nn as nn
import torch.nn.functional as F
class MonaOp(nn.Module):
"""Multi-cognitive conv aggregator: depthwise 3x3/5x5/7x7 + 1x1 projector."""
def __init__(self, dim: int):
super().__init__()
self.conv1 = nn.Conv2d(dim, dim, 3, padding=1, groups=dim) # depthwise 3x3
self.conv2 = nn.Conv2d(dim, dim, 5, padding=2, groups=dim) # depthwise 5x5
self.conv3 = nn.Conv2d(dim, dim, 7, padding=3, groups=dim) # depthwise 7x7
self.projector = nn.Conv2d(dim, dim, 1) # pointwise 1x1
def forward(self, x): # x: (B, dim, H, W)
identity = x
x = (self.conv1(x) + self.conv2(x) + self.conv3(x)) / 3.0 + identity
identity = x
return identity + self.projector(x) # second residual
class Mona(nn.Module):
"""Multi-cognitive visual adapter (Yin et al., CVPR 2025)."""
def __init__(self, in_dim: int, bottleneck: int = 64):
super().__init__()
self.norm = nn.LayerNorm(in_dim)
self.gamma = nn.Parameter(torch.ones(in_dim) * 1e-6) # scaled-norm branch
self.gammax = nn.Parameter(torch.ones(in_dim)) # identity branch
self.project1 = nn.Linear(in_dim, bottleneck) # down-projection
self.adapter_conv = MonaOp(bottleneck) # multi-scale conv
self.nonlinear = F.gelu
self.dropout = nn.Dropout(0.1)
self.project2 = nn.Linear(bottleneck, in_dim) # up-projection
def forward(self, x, hw_shape): # x: (B, N, C), hw_shape=(H, W)
identity = x
x = self.norm(x) * self.gamma + x * self.gammax # scaled LayerNorm
u = self.project1(x) # (B, N, bottleneck)
b, n, c = u.shape
h, w = hw_shape
u = u.reshape(b, h, w, c).permute(0, 3, 1, 2) # -> (B, c, H, W)
u = self.adapter_conv(u)
u = u.permute(0, 2, 3, 1).reshape(b, n, c) # -> (B, N, c)
u = self.dropout(self.nonlinear(u))
return identity + self.project2(u) # outer residual
```
Вставка в трансформерный блок (frozen backbone, обучается только Mona):
```python
# внутри SwinBlock.forward после attention и после FFN:
x = shortcut + drop_path(attn_out)
x = mona_after_attn(x, (H, W))
x = x + drop_path(mlp_out)
x = mona_after_ffn(x, (H, W))
```
---
## 7. Применимость к маленькому датасету Naruto Sign на EdgeNeXt
Naruto Sign (распознавание ручных «печатей»/жестов) — **малый** датасет, EdgeNeXt — компактный backbone. Здесь Mona особенно уместен:
- **Снижение переобучения.** Backbone EdgeNeXt замораживается (предобученные веса сохраняют общие визуальные признаки), обучается лишь ~2.55 % параметров — меньше степеней свободы → меньше шансов запомнить шум малой выборки.
- **Доменная адаптация через conv.** Жесты рук — локальные многомасштабные паттерны (пальцы, контуры). Multi-scale depth-wise свёртки (3/5/7) подстраивают предобученные признаки под этот домен лучше линейных адаптеров.
- **Дёшево по памяти.** Хранится один frozen backbone + лёгкие Mona-веса; для edge-сценария это критично.
> Замечание по совместимости: EdgeNeXt — гибрид conv+SDTA, а не чистый Swin. Mona-блок (`Mona`/`MonaOp`) переносим почти без изменений: ему нужны лишь токенизированные признаки `(B, N, C)` + `(H, W)` для reshape, либо его можно применять прямо к feature-map `(B, C, H, W)`, опустив permute. Точку вставки (после каждой стадии / после SDTA-блоков) выносим в гиперпараметры.
### Гиперпараметры для Optuna
| Гиперпараметр | Диапазон / варианты поиска | Комментарий |
|---------------|----------------------------|-------------|
| `bottleneck` (down-dim) | {16, 32, 64, 96} | меньше → сильнее регуляризация, дешевле |
| набор ядер conv | {(3), (3,5), (3,5,7), (5,7)} | trade-off рецептивное поле vs параметры |
| агрегация ветвей | {mean, learnable-weighted sum} | взвешенная сумма даёт ещё ~$k$ обучаемых скаляров |
| init `gamma` | {1e-6, 1e-4, 1e-2} | насколько «включена» scaled-norm-ветка на старте |
| init `gammax` | {1.0, 0.5} | сила identity-проброса |
| dropout в bottleneck | [0.0, 0.3] | регуляризация под малый датасет |
| стадии вставки | подмножество {stage1..stage4} | в ранние/поздние стадии или только в часть |
| позиции в блоке | {after-attn, after-ffn, both} | both = как в оригинале |
| LR для Mona-параметров | log-uniform [1e-4, 5e-3] | adapter-веса часто требуют LR выше backbone |
| `weight_decay` Mona | log-uniform [1e-5, 1e-2] | — |
Эмпирическое правило (из статьи): на малых/плотных задачах лучше вставлять Mona **во все стадии и обе позиции** (after-attn + after-ffn); сокращать набор ядер до (3,5) или bottleneck до 32 имеет смысл, если Optuna показывает переобучение или упор в бюджет параметров.
---
## Источники
- [Yin D., Hu L., Li B., Zhang Y., Yang X. «5%>100%: Breaking Performance Shackles of Full Fine-Tuning on Visual Recognition Tasks». arXiv:2408.08345, 2024 (CVPR 2025)](https://arxiv.org/abs/2408.08345) — [HTML-версия](https://arxiv.org/html/2408.08345v1).
- [Официальная реализация — GitHub Leiyi-Hu/mona](https://github.com/Leiyi-Hu/mona); исходник блока — [`swin_transformer_mona.py`](https://raw.githubusercontent.com/Leiyi-Hu/mona/master/Swin-Transformer-Classification/mmcls/models/backbones/swin_transformer_mona.py).
- PEFT-baselines: LoRA ([Hu et al., 2021, arXiv:2106.09685](https://arxiv.org/abs/2106.09685)); Adapter/Houlsby ([Houlsby et al., 2019, arXiv:1902.00751](https://arxiv.org/abs/1902.00751)); AdaptFormer ([Chen et al., 2022, arXiv:2205.13535](https://arxiv.org/abs/2205.13535)); VPT ([Jia et al., 2022, arXiv:2203.12119](https://arxiv.org/abs/2203.12119)); BitFit ([Zaken et al., 2021, arXiv:2106.10199](https://arxiv.org/abs/2106.10199)).