# 32. Драбина втрат: донавчання одобрених мереж

| | |
| --- | --- |
| Пара | BTC_USDT + 5 монет |
| Клас | залежить від моделі: `клас210х14х4ц`, сім класів сітки 210 с, `клас210х10-1х7д/д` |
| Вкладка | [`/ladder`](http://localhost:8080/ladder) |
| Статус | етапи 1–11 підтверджені, 2026-09-17 |

## Ціль

У проєкті вже є одобрені мережі. Кожна з них має свою найкращу версію:
ваги, ручки й число на val, яким її вибирали. Питання цього дослідження
одне:

> **Чи можна взяти одобрену версію й донавчити її, міняючи функцію втрат
> між епохами, — так, щоб її власна нерухома міра стала кращою?**

І друге, службове: **чи всі вони доучені на повному train** — чи є серед
них такі, що спинились не тому, що більше нікуди, а тому, що скінчився
бюджет епох.

Відповідь має бути чесною в обидва боки. Драбина, яка «завжди
покращує», — це драбина, що міряє себе; тому міра фіксується заздалегідь
і від штрафу не залежить, а гірша епоха просто відкочується.

## Метод

### Драбина

```
одобрена версія  ->  замір на val  ->  це точка відліку
щабель 1         ->  епоха         ->  замір  ->  краще? лишаю : вертаю
щабель 2         ->  епоха         ->  замір  ->  ...
```

- Ваги стартують з **одобреної версії**, а не з нуля.
- На кожному щаблі — своя функція втрат, і штраф на щаблі **росте**.
- Після епохи міряється нерухома міра на val. Краще — ваги лишаються й
  наступний щабель іде від них. Гірше — ваги вертаються.
- Втрата міняється **між** епохами, за явним розкладом, а не від
  метрики. Втрата зі зворотним звʼязком від метрики оптимізує саму
  себе; розклад — ні.

### Нерухома міра

Втрата міняється на кожному щаблі, тому **крива втрати нічого не
каже**: число під пʼятим щаблем не порівнюється з числом під першим.
Тому в кожної моделі є міра, яка:

- стоїть на всьому прогоні й жодного разу не міняється;
- **не залежить** від того, чим зараз вчать — рангова або рахована
  сталими константами;
- це та сама міра, якою одобрену версію вибирали в її дослідженні.

| Модель | Міра | Куди краще |
| ------ | ---- | ---------- |
| `legleft` | коридор: частка видачі поза коридором довкола цілі плюс балакучість після вікна | менше |
| `legtip` | `AP` — середня влучність | більше |
| `hazard100` | широке AUC: середнє по шести монетах | більше |
| `hazard4` | те саме, ще й середнє по чотирьох горизонтах | більше |
| `zone` | `AUC(півот) + AUC(корекція) − плутанина`, з умовою допуску | більше |

Поруч із мірою в кожного прогону стоять **додаткові числа** (похибка в
подіях, AUC, влучність на найгучніших 5 %, плутанина). Вони рішення не
приймають — вони показують, **чим** заплачено за зміну міри.

### Мертва зона

Зміна, менша за `margin`, зміною не вважається. Без неї драбина ловила б
шум замірювання й «покращувала» модель туди-сюди на четвертому знаку.
`margin` стоїть у кожного адаптера свій — від 0.001 до 0.003.

### Коли гірше: перенавчання щабля

Гірша епоха не лишається ніколи. Робиться три речі одразу:

1. **Ваги вертаються** до найкращих.
2. **Оптимізатор збирається заново.** Моменти AdamW набрані під іншим
   штрафом — вони тягнуть туди, куди тягнув попередній щабель.
3. **Крок навчання ділиться навпіл**, і порядок батчів міняється.

Після цього той самий щабель учиться ще раз. Дві невдалі спроби —
щабель лишається невзятим, і наступний стартує з тих самих найкращих
ваг.

### Шум замірювання: чим читається виграш

Дві спроби того самого щабля — це **та сама модель, ті самі дані й той
самий штраф**; різниться лише порядок батчів і крок. Тому різниця між
їхніми мірами — це шум: скільки одна епоха може коштувати сама по собі,
без жодної зміни постановки.

Драбина міряє цей шум сама, по всіх парах спроб прогону, і виграш
читається **тільки поруч із ним**:

> виграш, менший за розкид двох спроб того самого щабля, — це вибір із
> шуму, а не покращення.

Це не дрібниця, а головна пастка всієї конструкції. Точка відліку —
одобрена версія — це **мінімум по власній кривій епох** свого
дослідження, тобто число, вже відібране з десятків спроб. Драбина ж
кидає по одній-дві спроби на щабель. Порівнювати відібраний мінімум із
одиничним кидком можна лише знаючи, наскільки широко той кидок
розкидається.

Тому у звіті кожної моделі стоїть окремий рядок: медіана розкиду по
парах спроб, найбільший розкид — і перелік щаблів, які побили одобрену
версію **більше ніж на цей розкид**. Якщо таких щаблів немає, то
покращення немає, хоч би яке додатне число стояло в колонці «зміна».

### Чому крок навчання спадає

Одобрена версія стоїть **у своєму оптимумі на val**: її епоху вибирали
саме за цим. Повна епоха тим самим кроком, яким її вчили, з цього
оптимуму просто виходить — і перший же щабель це показує. Тому крок
спадає вниз по драбині: перші щаблі йдуть кроком одобреної версії, далі
вдвічі й учетверо меншим.

Це не ручка, яку підбирають: це наслідок того, звідки драбина стартує.

## Дані

Драбина нічого не будує: вона бере ті самі датасети, на яких вчились
одобрені версії, і те саме розбиття. Нічого не перемішується, межа
train|val не рухається.

| Модель | Дані | Розбиття |
| ------ | ---- | -------- |
| `legleft`, `legtip` | `research/29-class7dd-legs/data/legleft_blocks4_gauss_clock.npz` — 554 ноги (train 451, val 103), 308 897 подій, 415 каналів семи класів сітки 210 с, чотири ноги в памʼяті, нормалізація `gauss` | за датою, val з 2026-08-14 |
| `hazard100`, `hazard4` | `data/datasets/<PAIR>/клас210х14х4ц` — шість монет одним потоком кожна, 336 471 подія train, 28 870 val | за датою, межа 2026-08-14 15:27 UTC |
| `zone` | `data/datasets/<PAIR>/class210x10m1x7dd` — рівні семи фіч у рангах плюс канал пропусків, розмітка зон `zones.py` | за датою, `cut` датасету |

Монети всюди одні й ті самі: BTC_USDT, ETH_USDT, SOL_USDT, XRP_USDT,
BNB_USDT, DOGE_USDT.

## Моделі під драбиною

### `legleft` — скільки подій до кінця ноги

- **Звідки**: [дослідження 29, етап 4](../29-class7dd-legs/29-class7dd-legs.md), ваги `research/29-class7dd-legs/models/legleft_blocks4_gauss_clock_band/`.
- **Що робить**: LSTM 32 з причинною увагою веде ногу зігзага 2.3 % на
  ема10 подія за подією і на кожній каже, скільки подій лишилось до
  півота. Перші 150 подій після півота відповідає, далі мовчить.
- **Одобрене число**: коридор `0.8373`, краща епоха 24 з 32.
- **Що дорожчає на щаблях**: ширина коридору втрати `0.90 → 0.97`, ціна
  заниження `80 → 400`, ціна балакучості `6 → 24`, крутість експоненти,
  стеля завищення, вага голови вікна, домішка Хубера в подіях і ширина
  самого вікна відповіді.

### `legtip` — сигнал кінця ноги

- **Звідки**: [дослідження 29, етап 5](../29-class7dd-legs/29-class7dd-legs.md), ваги `research/29-class7dd-legs/models/legtip_blocks4_gauss_clock_bce/`.
- **Що робить**: та сама мережа на тому самому вході, але питання
  двійкове — «ми вже в кінці?». Сигнал — останні 5 % подій ноги.
- **Одобрене число**: `AP 0.151` при рівні сигналу 0.05, краща епоха
  20 з 26.
- **Що дорожчає**: тиша на початку ноги (`×4 → ×16` на перших 50–100
  подіях), вага краю біля межі сигналу, фокус `γ1`/`γ2` і вага позитиву
  `5 → 30`.

### `hazard100` — півот у наступні 100 подій

- **Звідки**: [дослідження 20, розділ 12](../20-leg-loss/20-leg-loss.md), ваги `research/20-leg-loss/models/hazard100_win/`.
- **Що робить**: мережа з памʼяттю йде рядом монети подія за подією на
  `клас210х14х4ц` і каже калібровану ймовірність того, що нога
  скінчиться в наступні 100 подій. Памʼять рве сама — за перевищенням
  над базовою частотою.
- **Одобрене число**: широке AUC `0.7203`, епоха 8 з 11.
- **Що дорожчає**: вага рідкої мітки `×1.5 → ×3`, фокус, вага подій
  близько до півота, довжина вікна градієнта і поріг обнулення памʼяті.

### `hazard4` — чотири горизонти

- **Звідки**: [дослідження 20, розділ 12](../20-leg-loss/20-leg-loss.md), ваги `research/20-leg-loss/models/hazard4_win/`.
- **Що робить**: та сама мережа, але виходів чотири — `p₁₀`, `p₄₀`,
  `p₁₆₀`, `p₆₄₀`; крива не спадає за побудовою.
- **Одобрене число**: широке AUC `0.7423` — найкраще число дослідження
  20; епоха 5 з 8.
- **Що дорожчає**: те саме, що в `hazard100`.

### `zone` — зона півота, зона корекції, звичайна подія

- **Звідки**: [дослідження 31, етапи 5–7](../31-leg-common-part/31-leg-common-part.md).
- **Що робить**: LSTM у два шари йде рядом подій `клас210х10-1х7д/д` і
  на кожній каже, до якого з трьох видів вона належить.
- **Одобрене число**: AUC півота `0.753`, корекції `0.717` — найсильніший
  набір гріду етапу 7 (`hidden 128`).
- **Особливість**: ваг від того гріду не лишилось, тому драбина **спершу
  вчить одобрену версію заново** — тими самими ручками й на тому самому
  seed, вісім епох, — і кладе її окремим файлом `models/zone/base.pt`.
  Наступний прогін драбини бере вже його.
- **Умова допуску**: плутанину легко звести до нуля, зламавши канал
  корекції. Тому щабель, у якого AUC хоч одного каналу нижче `0.6`, не
  береться, хай би яка була його оцінка.
- **Дві точки відліку.** Локально навчена базова версія вийшла слабшою
  за той рядок гріду, який вона мала повторити: `0.7055` проти `1.053`
  (`0.753 + 0.717 − 0.417` з етапу 7). Вісім епох із того самого seed
  дали найкращою **першу**, далі канал корекції ламався. Тому виграш
  драбини читається проти **обох** чисел, і головне з них —
  опубліковане `1.053`, а не локальне `0.7055`.

### Чого на драбині немає

**Мережа сходинок дослідження 19** (розділ 9, `leg_end_steps_ema10_2_3`)
одобрена й недоучена — бюджет був пʼять епох, а краща епоха четверта, і
val ще йшов униз. Але її val — це **шість ніг** BTC після межі. Похибка
частки на шести ланках дорівнює `0.5 / √6 = 0.20`
(`rules/03-models.md` → Розмір вибірки в незалежних одиницях), тобто
будь-яка зміна, менша за 0.4, нічого не означає. Драбина, яка ухвалює
рішення «лишаю чи вертаю» за таким числом, вимірювала б шум, тому ця
мережа сюди не входить: їй потрібен ширший val, а це інша робота.

## Скрипти

| Скрипт | Що робить | Вихід |
| ------ | --------- | ----- |
| [`scripts/ladder.py`](scripts/ladder.py) | драбина: щабель за щаблем, замір, відкат і перенавчання; CLI на всі моделі | `results/ladder_<модель>.json`, `models/<модель>/model.pt` |
| [`scripts/ad_legleft.py`](scripts/ad_legleft.py) | адаптер «скільки подій до кінця ноги»: втрата `band` із параметрами щабля, міра-коридор | — |
| [`scripts/ad_legtip.py`](scripts/ad_legtip.py) | адаптер «сигнал кінця ноги»: тиша, край, фокус, вага позитиву; міра `AP` | — |
| [`scripts/hazard_common.py`](scripts/hazard_common.py) | спільна частина обох мереж дослідження 20: прохід потоком, втрата щабля, широке AUC | — |
| [`scripts/ad_hazard.py`](scripts/ad_hazard.py) | адаптер `p₁₀₀` | — |
| [`scripts/ad_hazard4.py`](scripts/ad_hazard4.py) | адаптер чотирьох горизонтів | — |
| [`scripts/ad_zone.py`](scripts/ad_zone.py) | адаптер зон: базове навчання, прицільний штраф, умова допуску | `models/zone/base.pt` |
| [`scripts/run_all.sh`](scripts/run_all.sh) | дві черги прогонів: MPS і CPU | логи в `results/` |
| [`scripts/report.py`](scripts/report.py) | зведення прогонів у таблиці цього файлу; нічого не рахує заново | markdown у stdout |
| [`scripts/val_series.py`](scripts/val_series.py) | ряд val BTC: обидві версії кожної мережі проходять val подія за подією, видача лягає на спільну вісь під ціну | `results/series_<модель>_BTC_USDT.json`, `results/val_series_BTC_USDT.json` |
| [`scripts/fake_pivot.py`](scripts/fake_pivot.py) | фальшивий півот: та сама `legleft` на ногах, нарізаних кожні 150 подій, у трьох постановках проти справжньої правди | `results/fake_pivot_BTC_USDT.json` |
| [`scripts/self_train.py`](scripts/self_train.py) | перенавчання `legleft` у власній рамці: межі ніг ставить сама мережа, епоха за епохою заново | `results/self_train.json`, `models/legleft_self/model.pt` |
| [`scripts/self_pivot.py`](scripts/self_pivot.py) | ряд val BTC у власній рамці: одобрені ваги й перенавчені, межі ніг і коридор проти справжньої правди | `results/self_pivot_BTC_USDT.json` |
| [`scripts/share_train.py`](scripts/share_train.py) | частка ноги без розмітки: рівні вікна ряду, лінія будується накопиченням із двох чисел мережі, безштрафова зона звужується з епохами | `results/share_train.json`, `models/legshare/model.pt` |
| [`scripts/share_series.py`](scripts/share_series.py) | ряд val BTC у частках: правда, лічильник розмітки межею знизу, мережа і її сигнал «обнулись»; правило нарізки береться з навчання | `results/share_series_BTC_USDT.json` |
| [`scripts/pivot_zone.py`](scripts/pivot_zone.py) | зона розвороту: кошик подій на відстані `d` від півота проти тіла ноги, логістична регресія на 410 каналах класу, AUC на val і підлога на перемішаних мітках; звідси береться безштрафова зона обнулення | `results/pivot_zone.json` |
| [`scripts/zone_train.py`](scripts/zone_train.py) | донавчання `legshare` драбиною промаху: штраф росте втричі на кожні пʼять подій повз зону, «пізно» множиться на мовчання, спуск став рішучим (`r²`) | `results/zone_train.json`, `models/legzone/model.pt` |
| [`scripts/zone_series.py`](scripts/zone_series.py) | ряд val BTC мережею `legzone`: пилка, сигнал «обнулись» і влучність у зону | `results/zone_series_BTC_USDT.json` |
| [`scripts/zone_length.py`](scripts/zone_length.py) | чи їде зона з довжиною ноги: та сама крива нарізно на коротких, середніх і довгих ногах, на двох осях — у подіях і в частках ноги; точка згасання надлишку | `results/zone_length.json` |
| [`scripts/zone_own.py`](scripts/zone_own.py) | своя межа кожній нозі за фічами самої ноги (метод дослідження 31), три способи рахувати несхожість і чотири перевірки методу: фальшива точка, вміст при рівному бюджеті, стійкість до порога, звʼязок із довжиною | `results/zone_own.json` |
| [`scripts/zone_false.py`](scripts/zone_false.py) | хибні розвороти: чи відрізняє клас кінець ноги від корекції (за глибиною, з підлогою) і куди лягають обнулення мережі — на розворот, на корекцію чи в порожнє, з контролем на покриття, за силою натиску й за висотою лінії | `results/zone_false.json` |
| [`scripts/rate_train.py`](scripts/rate_train.py) | одне обнулення на ногу: зона часткою ноги і ціна за натиск — рівна, бюджетом і бюджетом разом зі здешевленням мовчання; драбини ручок по епохах | `results/rate_train.json`, `rate_flat.json`, `rate_budget.json`, `rate_late.json`, `models/legrate/model.pt` |
| [`scripts/tooth_check.py`](scripts/tooth_check.py) | кандидати на роль сигналу зубців однією лінійкою: `AUC` зони і влучність при заданій частоті обнулень | `results/tooth_check.json` |
| [`scripts/tooth_train.py`](scripts/tooth_train.py) | мережа зубців: позитив максимумом по зоні ноги, негатив щільний, драбина місця; замір «найгучніша подія ноги — в зоні чи ні» | `results/tooth_train.json`, `models/legtooth/model.pt` |
| [`scripts/line_train.py`](scripts/line_train.py) | мережа частки: веде лінію приростом, а обнуляється чужим замороженим сигналом; у втраті немає жодного доданка про обнулення | `results/line_train.json`, `models/legline/model.pt` |
| [`scripts/split_series.py`](scripts/split_series.py) | ряд val BTC обома мережами поділу: сира видача зубців, той самий сигнал порогом, лінія поділу і стара одноголова версія на спільній осі | `results/split_series_BTC_USDT.json` |

```bash
# одна модель
python3 research/32-loss-ladder/scripts/ladder.py --model legleft \
    --minutes 115 --attempts 2 --force

# усі одобрені мережі, двома чергами
sh research/32-loss-ladder/scripts/run_all.sh

# таблиці розділу «Результат» — з файлів прогонів
python3 research/32-loss-ladder/scripts/report.py

# перелік адаптерів
python3 research/32-loss-ladder/scripts/ladder.py --list

# продовжити драбину, яка вперлась у стелю часу: ваги й хід беруться
# з її ж файлів, і прогін іде з того щабля, на якому спинився
python3 research/32-loss-ladder/scripts/ladder.py --model legleft \
    --minutes 120 --resume --force

# ряд val BTC: усі пʼять моделей по черзі, кожна своїм процесом, потім
# зшивання на спільну вісь подій
python3 research/32-loss-ladder/scripts/val_series.py
python3 research/32-loss-ladder/scripts/val_series.py --model zone
python3 research/32-loss-ladder/scripts/val_series.py --join

# фальшивий півот: скільки видачі `legleft` тримається на розмітці
python3 research/32-loss-ladder/scripts/fake_pivot.py
python3 research/32-loss-ladder/scripts/fake_pivot.py --step 300

# власна рамка: мережа сама називає довжину ноги і сама ставить її кінець
python3 research/32-loss-ladder/scripts/self_train.py --minutes 150 --force
python3 research/32-loss-ladder/scripts/self_pivot.py

# частка ноги без розмітки: перші епохи — тільки детектор кінця ноги
python3 research/32-loss-ladder/scripts/share_train.py \
    --epochs 40 --detect 6 --force

# доучити те, що вже лежить: старт із кращої епохи
python3 research/32-loss-ladder/scripts/share_train.py --epochs 10 --resume

# ряд val BTC: правило нарізки береться з share_train.json
python3 research/32-loss-ladder/scripts/share_series.py

# зона розвороту: скільки подій біля півота відрізняються від тіла ноги
python3 research/32-loss-ladder/scripts/pivot_zone.py

# драбина промаху: донавчання `legshare` з його ж ваг
python3 research/32-loss-ladder/scripts/zone_train.py --epochs 60 \
    --minutes 400
python3 research/32-loss-ladder/scripts/zone_train.py --epochs 20 --resume
python3 research/32-loss-ladder/scripts/zone_series.py

# етап 9: зона по нозі й хибні розвороти
python3 research/32-loss-ladder/scripts/zone_length.py
python3 research/32-loss-ladder/scripts/zone_own.py --limit 0.75
python3 research/32-loss-ladder/scripts/zone_false.py
python3 research/32-loss-ladder/scripts/zone_false.py --part split

# етап 10: одне обнулення на ногу (драбина мусить пройти весь хід)
python3 research/32-loss-ladder/scripts/rate_train.py \
    --epochs 45 --patience 0

# етап 11: дві мережі замість однієї
python3 research/32-loss-ladder/scripts/tooth_check.py
python3 research/32-loss-ladder/scripts/tooth_train.py \
    --epochs 45 --patience 0
python3 research/32-loss-ladder/scripts/line_train.py \
    --epochs 40 --patience 0 --aim 2.5
python3 research/32-loss-ladder/scripts/split_series.py
```

`--force` тут свідомий: на цій машині вільної памʼяті менше за межу
2400 МБ (`rules/03-models.md` → Навчання без свопа), і без ключа прогін
не почався б.

Зупинка на прохання: `kill -TERM <pid>` не вбиває прогін, а домальовує
поточний щабель і виходить із файлами.

Стеля часу `--minutes` — не рання зупинка, а межа прогону: драбина
довга, а машина одна. Прогін, що вперся в неї, продовжується ключем
`--resume` з того щабля, на якому спинився, — ваги беруться з
`models/<модель>/model.pt`, хід і точка відліку з `results/ladder_<модель>.json`.

## План роботи

| # | Етап | Статус |
| - | ---- | ------ |
| 1 | Драбина на мережах дослідження 29 — `legleft` і `legtip`: сімнадцять щаблів кожній, замір коридором і `AP`, видача на val по кожній монеті | підтверджено, 2026-09-16 |
| 2 | Драбина на мережах дослідження 20 — `hazard100` і `hazard4`: шістнадцять щаблів, замір широким AUC по шести монетах | підтверджено, 2026-09-16 |
| 3 | Драбина на мережі дослідження 31 — `zone`: базова версія навчена заново, тринадцять щаблів під умовою допуску | підтверджено, 2026-09-16 |
| 4 | Ряд val BTC: обидві версії кожної мережі проходять val подія за подією, видача лягає плашками під ціну на спільній осі; той самий прохід переміряє ваги й ловить різницю з числами прогону | підтверджено, 2026-09-16 |
| 5 | Фальшивий півот: `legleft` на ногах, нарізаних кожні 150 подій, — скільки з видачі тримається на класі, а скільки на чотирьох каналах розмітки; окремий графік ціни BTC і трьох постановок під нею | підтверджено, 2026-09-16 |
| 6 | Власна рамка: мережа сама називає довжину ноги середнім своїх перших `START` відповідей і сама ставить її кінець; перенавчання в цій рамці з одобрених ваг, межі ставляться заново кожної епохи, і замір — чи збіглись вони зі справжніми півотами | підтверджено, 2026-09-16 |
| 7 | Частка ноги без розмітки: відповідь у частці пройденої ноги — `0` на початку, `1` на кінці, і форма в часі — пилка, яка сама падає в нуль на розвороті. Розмітки на вході немає взагалі, ряд нарізаний рівними вікнами; лінія не передбачається, а будується з двох чисел мережі — «обнулись» і «підрости». Штраф за формою: не впасти там, де нога вже нова, — найдорожче. Пʼять ітерацій конструкції й заміри детектора на різних частотах обнулення | підтверджено, 2026-09-16 |
| 8 | Зона розвороту і драбина промаху: скільки подій біля півота відрізняються від тіла ноги (кошик проти середини ноги, `AUC` на val і підлога на перемішаних мітках) — звідки береться безштрафова зона обнулення в **подіях**, а не у відсотках ноги; донавчання мережі етапу 7 штрафом, який росте втричі на кожні пʼять подій промаху повз цю зону, де мовчання після розвороту коштує найдорожче; своя вкладка з кривою зони, кривою навчання і рядом val BTC | підтверджено, 2026-09-17 |
| 9 | Зона по нозі й хибні розвороти: чи їде зона з довжиною ноги (та сама крива нарізно на коротких, середніх і довгих ногах — і на двох осях, у подіях та в частках); своя межа кожній нозі за фічами самої ноги — метод дослідження 31 під чотирма перевірками (фальшива точка, вміст зони при рівному бюджеті подій, стійкість до порога, звʼязок із довжиною); і куди насправді лягають зайві зубці мережі — на корекції всередині ноги чи ні, з контролем на покриття, з розрізом за силою натиску й за висотою лінії | підтверджено, 2026-09-17 |
| 10 | Одне обнулення на ногу: зона безштрафовості стає часткою ноги, а за сам натиск платиться — трьома механізмами по черзі (рівна ціна, бюджет ноги, бюджет разом із здешевленням мовчання), драбини ручок проходять увесь хід без ранньої зупинки | підтверджено, 2026-09-17 |
| 11 | Поділ задачі на дві мережі: одна каже **де зубець** (позитив максимумом по зоні ноги, негатив щільний, драбина місця), друга веде **частку ноги** й обнуляється чужим замороженим сигналом; перед тим — порівняння готових кандидатів на роль зубців однією лінійкою, і перевірка, чи можна виправити зсув зубця сталою поправкою | підтверджено, 2026-09-17 |

## Приклади

Один рядок виводу — один щабель:

```
одобрена версія: міра 0.8373  коридор 8.2 % · широкий 25.5 % · похибка 313 подій
щабель  1 · той самий штраф   спроба 1 · втрата 1.7074 · міра 0.8551 · вертаю
щабель  1 · той самий штраф   спроба 2 · втрата 1.6903 · міра 0.8494 · вертаю
щабель  2 · заниження ×1.5    спроба 1 · втрата 2.1180 · міра 0.8302 · ЛИШАЮ
```

Читається так: перший щабель — та сама втрата, якою вчили одобрену
версію, — двічі зробив гірше (0.8551 і 0.8494 проти 0.8373), обидва рази
ваги повернулись. Другий щабель із дорожчим заниженням дав 0.8302 — це
краще, ваги лишились, і третій щабель піде вже від них.

## Результат

Пʼять мереж, 124 спроби щаблів, 6.9 години машинного часу. Таблиці нижче
зібрані зі самих файлів прогонів скриптом
[`report.py`](scripts/report.py) — нічого не переписано руками.

Це те, що зміряв **сам прогін**. Перш ніж читати числа `zone`, треба
прочитати розділ [«Перевірка ваг: замір наново»](#перевірка-ваг-замір-наново):
його виграш переміру не пережив.

| Модель | Міра | Одобрена | Драбина | Зміна | Шум замірювання | Щаблів узято | Спроб |
| ------ | ---- | -------: | ------: | ----: | --------------: | -----------: | ----: |
| `legleft` | коридор (менше) | 0.8373 | 0.8373 | 0.0000 — не краще | ±0.0069 (11 пар) | 0 | 23 |
| `legtip` | AP (більше) | 0.1515 | 0.1515 | 0.0000 — не краще | ±0.0030 (12 пар) | 0 | 25 |
| `hazard100` | широке AUC (більше) | 0.7204 | 0.7428 | +0.0224 — **краще** | ±0.0043 (13 пар) | 3 | 29 |
| `hazard4` | широке AUC (більше) | 0.7423 | 0.7423 | 0.0000 — не краще | ±0.0026 (11 пар) | 0 | 22 |
| `zone` | оцінка (більше) | 0.7055 | 1.1799 | +0.4744 — **краще** | ±0.0793 (8 пар) | 5 | 25 |

### `legleft` — Скільки подій до кінця ноги

Джерело: research/29-class7dd-legs/29-class7dd-legs.md → етап 4. Одобрені ваги: `research/29-class7dd-legs/models/legleft_blocks4_gauss_clock_band`.

**Нерухома міра — коридор.** частка видачі поза коридором довкола цілі плюс надбавка за балакучість після вікна відповіді; менше краще

Одобрена версія дала **0.8373**, жоден щабель її не побив.

Шум замірювання: дві спроби того самого щабля розходяться на 0.0069 (медіана 11 пар, найбільша 0.0404). Виграш драбини 0.0000 — **менший за шум**. Спроб, що побили одобрену версію більше ніж на шум: 0 з 23, тобто **жодної**.

По монетах: покращилось 0 з 6. Найбільше — DOGE_USDT (-0.0000), найгірше — BTC_USDT (-0.0000). Знак тут завжди «краще», хай куди дивиться сама міра.

#### Щаблі

| Щабель | Що дорожчає | Спроба | Втрата | коридор | Рішення | Ручки |
| -----: | ----------- | -----: | -----: | ----: | ------- | ----- |
| — | одобрена | — | 0.0000 | 0.8373 | точка відліку | hidden 32 · lr 0.001 · loss band · from research/29-class7dd-legs/models/legleft_blocks4_gauss_clock_band |
| 1 | той самий штраф | 1 | 1.7074 | 0.8551 | вертаю | lr 0.001 |
| 1 | той самий штраф | 2 | 1.5311 | 0.8737 | вертаю | lr 0.0005 |
| 2 | заниження ×1.5 | 1 | 2.5178 | 0.8588 | вертаю | lr 0.001 · down 120 · up 30 |
| 2 | заниження ×1.5 | 2 | 2.2525 | 0.8612 | вертаю | lr 0.0005 · down 120 · up 30 |
| 3 | коридор 0.93–1.07 | 1 | 1.7896 | 0.8601 | вертаю | lr 0.001 · band 0.93–1.07 |
| 3 | коридор 0.93–1.07 | 2 | 1.6919 | 0.8669 | вертаю | lr 0.0005 · band 0.93–1.07 |
| 4 | той самий штраф · крок ×0.3 | 1 | 1.5131 | 0.8626 | вертаю | lr 0.0003 |
| 4 | той самий штраф · крок ×0.3 | 2 | 1.4885 | 0.8695 | вертаю | lr 0.00015 |
| 5 | заниження ×2 · тиша ×1.5 | 1 | 2.9945 | 0.8698 | вертаю | lr 0.0003 · down 160 · up 40 · quiet 9 |
| 5 | заниження ×2 · тиша ×1.5 | 2 | 2.9313 | 0.8726 | вертаю | lr 0.00015 · down 160 · up 40 · quiet 9 |
| 6 | коридор 0.95–1.05 · ×2 | 1 | 3.0505 | 0.8635 | вертаю | lr 0.0003 · band 0.95–1.05 · down 160 · up 40 |
| 6 | коридор 0.95–1.05 · ×2 | 2 | 3.0048 | 0.8731 | вертаю | lr 0.00015 · band 0.95–1.05 · down 160 · up 40 |
| 7 | крутіша експонента | 1 | 1.1319 | 0.8725 | вертаю | lr 0.0003 · grow 7 |
| 7 | крутіша експонента | 2 | 1.1273 | 0.8778 | вертаю | lr 0.00015 · grow 7 |
| 8 | заниження ×3 · тиша ×2 | 1 | 4.4339 | 0.8529 | вертаю | lr 0.0003 · down 240 · up 60 · quiet 12 |
| 8 | заниження ×3 · тиша ×2 | 2 | 4.4218 | 0.8695 | вертаю | lr 0.00015 · down 240 · up 60 · quiet 12 |
| 9 | коридор 0.96–1.04 · ×3 | 1 | 4.6444 | 0.8853 | вертаю | lr 0.0003 · band 0.96–1.04 · down 240 · up 60 |
| 9 | коридор 0.96–1.04 · ×3 | 2 | 4.5812 | 0.8449 | вертаю | lr 0.00015 · band 0.96–1.04 · down 240 · up 60 |
| 10 | важча голова вікна | 1 | 1.7983 | 0.8688 | вертаю | lr 0.0001 · wake [0, 2.0]–[30, 1.0] |
| 10 | важча голова вікна | 2 | 1.8075 | 0.8617 | вертаю | lr 5e-05 · wake [0, 2.0]–[30, 1.0] |
| 11 | домішка Хубера в подіях | 1 | 1.6627 | 0.8619 | вертаю | lr 0.0001 · mix 0.3 |
| 11 | домішка Хубера в подіях | 2 | 1.6367 | 0.8608 | вертаю | lr 5e-05 · mix 0.3 |
| 12 | нижча стеля завищення | 1 | 3.2338 | 0.8993 | вертаю | lr 0.0001 · ceil 1.8 · up 60 |

#### Видача на val по монетах

| Монета | коридор: одобрена | драбина | зміна | похибка, подій: одобрена | драбина | зміна | у коридорі 0.9–1.1: одобрена | драбина | зміна | Подій val |
| ------ | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --------: |
| BTC_USDT | 0.7759 | 0.7759 | 0.0000 | 420.7412 | 420.7412 | 0.0000 | 9.9 | 9.9 | 0.0 | 9394 |
| ETH_USDT | 0.7272 | 0.7272 | 0.0000 | 325.6204 | 325.6204 | 0.0000 | 14.8 | 14.8 | 0.0 | 9475 |
| SOL_USDT | 0.8565 | 0.8565 | 0.0000 | 295.6693 | 295.6693 | 0.0000 | 7.9 | 7.9 | 0.0 | 9396 |
| XRP_USDT | 0.8930 | 0.8930 | 0.0000 | 238.2004 | 238.2004 | 0.0000 | 5.6 | 5.6 | 0.0 | 6925 |
| BNB_USDT | 0.8826 | 0.8826 | 0.0000 | 459.0175 | 459.0175 | 0.0000 | 4.5 | 4.5 | 0.0 | 6166 |
| DOGE_USDT | 0.8489 | 0.8489 | 0.0000 | 250.0699 | 250.0699 | 0.0000 | 7.5 | 7.5 | 0.0 | 9953 |

### `legtip` — Сигнал кінця ноги

Джерело: research/29-class7dd-legs/29-class7dd-legs.md → етап 5. Одобрені ваги: `research/29-class7dd-legs/models/legtip_blocks4_gauss_clock_bce`.

**Нерухома міра — AP.** середня влучність — площа під кривою влучність-повнота; рангова, тому не залежить від ваги позитиву. Рівень сигналу 0.05, більше краще

Одобрена версія дала **0.1515**, жоден щабель її не побив.

Шум замірювання: дві спроби того самого щабля розходяться на 0.0030 (медіана 12 пар, найбільша 0.0127). Виграш драбини 0.0000 — **менший за шум**. Спроб, що побили одобрену версію більше ніж на шум: 0 з 25, тобто **жодної**.

По монетах: покращилось 0 з 6. Найбільше — DOGE_USDT (0.0000), найгірше — BTC_USDT (0.0000). Знак тут завжди «краще», хай куди дивиться сама міра.

#### Щаблі

| Щабель | Що дорожчає | Спроба | Втрата | AP | Рішення | Ручки |
| -----: | ----------- | -----: | -----: | ----: | ------- | ----- |
| — | одобрена | — | 0.0000 | 0.1515 | точка відліку | hidden 32 · lr 0.001 · loss bce · pos_weight 19 · from research/29-class7dd-legs/models/legtip_blocks4_gauss_clock_bce |
| 1 | той самий штраф | 1 | 0.1563 | 0.1366 | вертаю | lr 0.001 |
| 1 | той самий штраф | 2 | 0.1351 | 0.1270 | вертаю | lr 0.0005 |
| 2 | тиша 50 подій ×4 | 1 | 0.2696 | 0.1337 | вертаю | lr 0.001 · hush 50 · hush_cost 4 |
| 2 | тиша 50 подій ×4 | 2 | 0.1526 | 0.1234 | вертаю | lr 0.0005 · hush 50 · hush_cost 4 |
| 3 | тиша ×8 | 1 | 0.2439 | 0.1098 | вертаю | lr 0.001 · hush 50 · hush_cost 8 |
| 3 | тиша ×8 | 2 | 0.1925 | 0.1225 | вертаю | lr 0.0005 · hush 50 · hush_cost 8 |
| 4 | той самий штраф · крок ×0.3 | 1 | 0.1036 | 0.1351 | вертаю | lr 0.0003 |
| 4 | той самий штраф · крок ×0.3 | 2 | 0.1150 | 0.1366 | вертаю | lr 0.00015 |
| 5 | тиша 80 подій ×8 | 1 | 0.1586 | 0.1421 | вертаю | lr 0.0003 · hush 80 · hush_cost 8 |
| 5 | тиша 80 подій ×8 | 2 | 0.1867 | 0.1429 | вертаю | lr 0.00015 · hush 80 · hush_cost 8 |
| 6 | вага краю ×2/×4 | 1 | 0.3345 | 0.1387 | вертаю | lr 0.0003 · close [0.8, 2.0]–[0.9, 4.0] |
| 6 | вага краю ×2/×4 | 2 | 0.3127 | 0.1362 | вертаю | lr 0.00015 · close [0.8, 2.0]–[0.9, 4.0] |
| 7 | тиша ×8 · край ×2/×4 | 1 | 0.3840 | 0.1389 | вертаю | lr 0.0003 · hush 50 · hush_cost 8 · close [0.8, 2.0]–[0.9, 4.0] |
| 7 | тиша ×8 · край ×2/×4 | 2 | 0.4709 | 0.1413 | вертаю | lr 0.00015 · hush 50 · hush_cost 8 · close [0.8, 2.0]–[0.9, 4.0] |
| 8 | фокус γ1 · тиша ×4 | 1 | 0.1307 | 0.1404 | вертаю | lr 0.0003 · gamma 1 · hush 50 · hush_cost 4 |
| 8 | фокус γ1 · тиша ×4 | 2 | 0.1005 | 0.1406 | вертаю | lr 0.00015 · gamma 1 · hush 50 · hush_cost 4 |
| 9 | вага позитиву 10 · тиша ×8 | 1 | 0.1308 | 0.1384 | вертаю | lr 0.0003 · pos 10 · hush 50 · hush_cost 8 |
| 9 | вага позитиву 10 · тиша ×8 | 2 | 0.1351 | 0.1459 | вертаю | lr 0.00015 · pos 10 · hush 50 · hush_cost 8 |
| 10 | тиша ×12 | 1 | 0.2132 | 0.1418 | вертаю | lr 0.0001 · hush 50 · hush_cost 12 |
| 10 | тиша ×12 | 2 | 0.2329 | 0.1480 | вертаю | lr 5e-05 · hush 50 · hush_cost 12 |
| 11 | фокус γ2 · тиша ×8 | 1 | 0.0993 | 0.1488 | вертаю | lr 0.0001 · gamma 2 · hush 50 · hush_cost 8 |
| 11 | фокус γ2 · тиша ×8 | 2 | 0.1159 | 0.1506 | вертаю | lr 5e-05 · gamma 2 · hush 50 · hush_cost 8 |
| 12 | вага позитиву 30 · тиша ×8 | 1 | 0.2161 | 0.1443 | вертаю | lr 0.0001 · pos 30 · hush 50 · hush_cost 8 |
| 12 | вага позитиву 30 · тиша ×8 | 2 | 0.2255 | 0.1478 | вертаю | lr 5e-05 · pos 30 · hush 50 · hush_cost 8 |
| 13 | край ×3/×6 · тиша ×8 | 1 | 0.5735 | 0.1449 | вертаю | lr 0.0001 · hush 50 · hush_cost 8 · close [0.8, 3.0]–[0.9, 6.0] |

#### Видача на val по монетах

| Монета | AP: одобрена | драбина | зміна | AUC: одобрена | драбина | зміна | влучність @5 %: одобрена | драбина | зміна | Подій val |
| ------ | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --------: |
| BTC_USDT | 0.1838 | 0.1838 | 0.0000 | 0.7250 | 0.7250 | 0.0000 | 26.2 | 26.2 | 0.0 | 9394 |
| ETH_USDT | 0.1216 | 0.1216 | 0.0000 | 0.6856 | 0.6856 | 0.0000 | 19.0 | 19.0 | 0.0 | 9475 |
| SOL_USDT | 0.1396 | 0.1396 | 0.0000 | 0.6722 | 0.6722 | 0.0000 | 21.3 | 21.3 | 0.0 | 9396 |
| XRP_USDT | 0.2828 | 0.2828 | 0.0000 | 0.8145 | 0.8145 | 0.0000 | 28.6 | 28.6 | 0.0 | 6925 |
| BNB_USDT | 0.0855 | 0.0855 | 0.0000 | 0.6621 | 0.6621 | 0.0000 | 12.0 | 12.0 | 0.0 | 6166 |
| DOGE_USDT | 0.1870 | 0.1870 | 0.0000 | 0.7451 | 0.7451 | 0.0000 | 28.7 | 28.7 | 0.0 | 9953 |

### `hazard100` — Півот у наступні 100 подій

Джерело: research/20-leg-loss/20-leg-loss.md → розділ 12. Одобрені ваги: `research/20-leg-loss/models/hazard100_win`.

**Нерухома міра — широке AUC.** середнє AUC по шести монетах після межі розбиття, по всіх горизонтах моделі; рангова міра, тому не залежить від ваги класу. Більше краще

Одобрена версія дала **0.7204**, драбина — **0.7428** (щабель 13 · вікно градієнта 40).

Шум замірювання: дві спроби того самого щабля розходяться на 0.0043 (медіана 13 пар, найбільша 0.0144). Виграш драбини 0.0224 — більший за шум. Спроб, що побили одобрену версію більше ніж на шум: 14 з 29 — той самий штраф · крок ×0.3, вага ×2 · фокус γ1, вікно градієнта 150, близькість ×4 (100 подій), вага ×3, фокус γ2, вікно градієнта 40, поріг обнулення ×2, поріг обнулення ×0.5.

По монетах: покращилось 4 з 6. Найбільше — BNB_USDT_fill (+0.0838), найгірше — ETH_USDT_fill (-0.0176). Знак тут завжди «краще», хай куди дивиться сама міра.

#### Щаблі

| Щабель | Що дорожчає | Спроба | Втрата | широке AUC | Рішення | Ручки |
| -----: | ----------- | -----: | -----: | ----: | ------- | ----- |
| — | одобрена | — | 0.0000 | 0.7204 | точка відліку | layers 2 · hidden 64 · head 128 · window 75 · cap 500 · lr 0.0003 · lift 10 · horizons 100 · from research/20-leg-loss/models/hazard100_win |
| 1 | той самий штраф | 1 | 1.1020 | 0.7127 | вертаю | lr 0.0003 |
| 1 | той самий штраф | 2 | 1.0891 | 0.7178 | вертаю | lr 0.00015 |
| 2 | вага мітки ×1.5 | 1 | 1.3290 | 0.7064 | вертаю | lr 0.0003 · wmul 1.5 |
| 2 | вага мітки ×1.5 | 2 | 1.3325 | 0.7208 | вертаю | lr 0.00015 · wmul 1.5 |
| 3 | фокус γ1 | 1 | 0.5494 | 0.7201 | вертаю | lr 0.0003 · gamma 1 |
| 3 | фокус γ1 | 2 | 0.5445 | 0.7191 | вертаю | lr 0.00015 · gamma 1 |
| 4 | вага мітки ×2 | 1 | 1.5339 | 0.7096 | вертаю | lr 0.0003 · wmul 2 |
| 4 | вага мітки ×2 | 2 | 1.5192 | 0.7233 | **лишаю** | lr 0.00015 · wmul 2 |
| 5 | близькість ×2 (100 подій) | 1 | 1.5102 | 0.7181 | вертаю | lr 0.0003 · near 100–2.0 |
| 5 | близькість ×2 (100 подій) | 2 | 1.4836 | 0.7223 | вертаю | lr 0.00015 · near 100–2.0 |
| 6 | той самий штраф · крок ×0.3 | 1 | 1.0745 | 0.7395 | **лишаю** | lr 0.0001 |
| 7 | вага ×2 · фокус γ1 | 1 | 0.7424 | 0.7357 | вертаю | lr 0.0001 · wmul 2 · gamma 1 |
| 7 | вага ×2 · фокус γ1 | 2 | 0.7463 | 0.7315 | вертаю | lr 5e-05 · wmul 2 · gamma 1 |
| 8 | вікно градієнта 150 | 1 | 1.0599 | 0.7318 | вертаю | lr 0.0001 · window 150 |
| 8 | вікно градієнта 150 | 2 | 1.0575 | 0.7361 | вертаю | lr 5e-05 · window 150 |
| 9 | близькість ×4 (100 подій) | 1 | 2.0030 | 0.7253 | вертаю | lr 0.0001 · near 100–4.0 |
| 9 | близькість ×4 (100 подій) | 2 | 2.0361 | 0.7178 | вертаю | lr 5e-05 · near 100–4.0 |
| 10 | вага ×3 | 1 | 1.7701 | 0.7273 | вертаю | lr 0.0001 · wmul 3 |
| 10 | вага ×3 | 2 | 1.7862 | 0.7254 | вертаю | lr 5e-05 · wmul 3 |
| 11 | фокус γ2 | 1 | 0.2718 | 0.7349 | вертаю | lr 0.0001 · gamma 2 |
| 12 | близькість ×4 · вага ×2 | 1 | 2.6457 | 0.6981 | вертаю | lr 0.0001 · near 100–4.0 · wmul 2 |
| 12 | близькість ×4 · вага ×2 | 2 | 2.7560 | 0.6898 | вертаю | lr 5e-05 · near 100–4.0 · wmul 2 |
| 13 | вікно градієнта 40 | 1 | 1.0579 | 0.7428 | **лишаю** | lr 3e-05 · window 40 |
| 14 | поріг обнулення ×2 | 1 | 1.0444 | 0.7322 | вертаю | lr 3e-05 · lift 20 |
| 14 | поріг обнулення ×2 | 2 | 1.0469 | 0.7364 | вертаю | lr 1.5e-05 · lift 20 |
| 15 | поріг обнулення ×0.5 | 1 | 1.0481 | 0.7356 | вертаю | lr 3e-05 · lift 5 |
| 15 | поріг обнулення ×0.5 | 2 | 1.0487 | 0.7351 | вертаю | lr 1.5e-05 · lift 5 |
| 16 | усе разом | 1 | 1.5263 | 0.7185 | вертаю | lr 3e-05 · wmul 2 · gamma 1 · near 200–3.0 · window 150 |
| 16 | усе разом | 2 | 1.5890 | 0.7196 | вертаю | lr 1.5e-05 · wmul 2 · gamma 1 · near 200–3.0 · window 150 |

#### Видача на val по монетах

| Монета | широке AUC: одобрена | драбина | зміна | схожість десятини: одобрена | драбина | зміна | своїх обнулень: одобрена | драбина | зміна | Подій val |
| ------ | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --------: |
| BTC_USDT_29 | 0.6463 | 0.6504 | +0.0042 | 0.3864 | 0.3861 | -0.0003 | 0.0000 | 0.0000 | 0.0000 | 3649 |
| ETH_USDT_fill | 0.7190 | 0.7014 | -0.0176 | 0.4319 | 0.4278 | -0.0041 | 0.0000 | 0.0000 | 0.0000 | 4612 |
| SOL_USDT_fill | 0.7410 | 0.7869 | +0.0459 | 0.1915 | 0.2131 | +0.0216 | 0.0000 | 0.0000 | 0.0000 | 4918 |
| XRP_USDT_fill | 0.8392 | 0.8341 | -0.0050 | 0.1885 | 0.1998 | +0.0113 | 0.0000 | 0.0000 | 0.0000 | 4919 |
| BNB_USDT_fill | 0.5552 | 0.6391 | +0.0838 | 0.3016 | 0.2420 | -0.0596 | 0.0000 | 0.0000 | 0.0000 | 4612 |
| DOGE_USDT_fill | 0.8217 | 0.8447 | +0.0230 | 0.2989 | 0.3005 | +0.0016 | 0.0000 | 0.0000 | 0.0000 | 4945 |

### `hazard4` — Чотири горизонти кінця ноги

Джерело: research/20-leg-loss/20-leg-loss.md → розділ 12. Одобрені ваги: `research/20-leg-loss/models/hazard4_win`.

**Нерухома міра — широке AUC.** середнє AUC по шести монетах після межі розбиття, по всіх горизонтах моделі; рангова міра, тому не залежить від ваги класу. Більше краще

Одобрена версія дала **0.7423**, жоден щабель її не побив.

Шум замірювання: дві спроби того самого щабля розходяться на 0.0026 (медіана 11 пар, найбільша 0.0120). Виграш драбини 0.0000 — **менший за шум**. Спроб, що побили одобрену версію більше ніж на шум: 0 з 22, тобто **жодної**.

По монетах: покращилось 0 з 6. Найбільше — DOGE_USDT_fill (0.0000), найгірше — BTC_USDT_29 (0.0000). Знак тут завжди «краще», хай куди дивиться сама міра.

#### Щаблі

| Щабель | Що дорожчає | Спроба | Втрата | широке AUC | Рішення | Ручки |
| -----: | ----------- | -----: | -----: | ----: | ------- | ----- |
| — | одобрена | — | 0.0000 | 0.7423 | точка відліку | layers 2 · hidden 64 · head 128 · window 75 · cap 500 · lr 0.0003 · lift 10 · horizons 10–40–160–640 · from research/20-leg-loss/models/hazard4_win |
| 1 | той самий штраф | 1 | 0.9125 | 0.7372 | вертаю | lr 0.0003 |
| 1 | той самий штраф | 2 | 0.9038 | 0.7346 | вертаю | lr 0.00015 |
| 2 | вага мітки ×1.5 | 1 | 1.1098 | 0.7360 | вертаю | lr 0.0003 · wmul 1.5 |
| 2 | вага мітки ×1.5 | 2 | 1.0974 | 0.7322 | вертаю | lr 0.00015 · wmul 1.5 |
| 3 | фокус γ1 | 1 | 0.4588 | 0.7367 | вертаю | lr 0.0003 · gamma 1 |
| 3 | фокус γ1 | 2 | 0.4550 | 0.7349 | вертаю | lr 0.00015 · gamma 1 |
| 4 | вага мітки ×2 | 1 | 1.2636 | 0.7341 | вертаю | lr 0.0003 · wmul 2 |
| 4 | вага мітки ×2 | 2 | 1.2529 | 0.7273 | вертаю | lr 0.00015 · wmul 2 |
| 5 | близькість ×2 (100 подій) | 1 | 1.2781 | 0.7346 | вертаю | lr 0.0003 · near 100–2.0 |
| 5 | близькість ×2 (100 подій) | 2 | 1.2664 | 0.7317 | вертаю | lr 0.00015 · near 100–2.0 |
| 6 | той самий штраф · крок ×0.3 | 1 | 0.9016 | 0.7372 | вертаю | lr 0.0001 |
| 6 | той самий штраф · крок ×0.3 | 2 | 0.9024 | 0.7394 | вертаю | lr 5e-05 |
| 7 | вага ×2 · фокус γ1 | 1 | 0.6310 | 0.7351 | вертаю | lr 0.0001 · wmul 2 · gamma 1 |
| 7 | вага ×2 · фокус γ1 | 2 | 0.6350 | 0.7361 | вертаю | lr 5e-05 · wmul 2 · gamma 1 |
| 8 | вікно градієнта 150 | 1 | 0.9030 | 0.7389 | вертаю | lr 0.0001 · window 150 |
| 8 | вікно градієнта 150 | 2 | 0.9044 | 0.7411 | вертаю | lr 5e-05 · window 150 |
| 9 | близькість ×4 (100 подій) | 1 | 1.8500 | 0.7310 | вертаю | lr 0.0001 · near 100–4.0 |
| 9 | близькість ×4 (100 подій) | 2 | 1.8634 | 0.7430 | вертаю | lr 5e-05 · near 100–4.0 |
| 10 | вага ×3 | 1 | 1.5018 | 0.7307 | вертаю | lr 0.0001 · wmul 3 |
| 10 | вага ×3 | 2 | 1.5098 | 0.7286 | вертаю | lr 5e-05 · wmul 3 |
| 11 | фокус γ2 | 1 | 0.2308 | 0.7382 | вертаю | lr 0.0001 · gamma 2 |
| 11 | фокус γ2 | 2 | 0.2332 | 0.7410 | вертаю | lr 5e-05 · gamma 2 |

#### Видача на val по монетах

| Монета | широке AUC: одобрена | драбина | зміна | схожість десятини: одобрена | драбина | зміна | своїх обнулень: одобрена | драбина | зміна | Подій val |
| ------ | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --------: |
| BTC_USDT_29 | 0.6065 | 0.6065 | 0.0000 | 0.3891 | 0.3891 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 3649 |
| ETH_USDT_fill | 0.7743 | 0.7743 | 0.0000 | 0.4267 | 0.4267 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 4612 |
| SOL_USDT_fill | 0.7375 | 0.7375 | 0.0000 | 0.2212 | 0.2212 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 4918 |
| XRP_USDT_fill | 0.8086 | 0.8086 | 0.0000 | 0.2354 | 0.2354 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 4919 |
| BNB_USDT_fill | 0.6752 | 0.6752 | 0.0000 | 0.2710 | 0.2710 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 4612 |
| DOGE_USDT_fill | 0.8517 | 0.8517 | 0.0000 | 0.3102 | 0.3102 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 4945 |

### `zone` — Зона півота і зона корекції

Джерело: research/31-leg-common-part/31-leg-common-part.md → етапи 5–7. Одобрені ваги: `research/31-leg-common-part → етап 7`.

**Нерухома міра — оцінка.** `AUC(півот) + AUC(корекція) − плутанина`; щабель, у якого AUC хоч одного каналу нижче 0.6, не береться взагалі — плутанину можна прибрати, зламавши канал. Більше краще

Одобрена версія дала **0.7055**, драбина — **1.1799** (щабель 8 · штраф 0.3).

Шум замірювання: дві спроби того самого щабля розходяться на 0.0793 (медіана 8 пар, найбільша 0.2355). Виграш драбини 0.4744 — більший за шум. Спроб, що побили одобрену версію більше ніж на шум: 20 з 25 — той самий штраф, штраф за плутанину 0.1, фокус γ2, штраф 0.2, стеля ваги класу 40, той самий штраф · крок ×0.3, фокус γ1 · штраф 0.1, штраф 0.3, стеля ваги класу 10, штраф 0.15 · стеля 40, штраф 0.5, усе разом.

По монетах: покращилось 3 з 3. Ще на 3 монетах міри немає — там канал зламаний на одному з боків порівняння: SOL_USDT, XRP_USDT, BNB_USDT. Найбільше — ETH_USDT (+0.6529), найгірше — BTC_USDT (+0.1550). Знак тут завжди «краще», хай куди дивиться сама міра.

#### Щаблі

| Щабель | Що дорожчає | Спроба | Втрата | оцінка | Рішення | Ручки |
| -----: | ----------- | -----: | -----: | ----: | ------- | ----- |
| — | одобрена | — | 0.0000 | 0.7055 | точка відліку | hidden 128 · layers 2 · lr 0.003 · loss bce · confuse 0 · memory увесь ряд · epochs 8 · trained_here True · from research/31-leg-common-part → етап 7, найсильніший набір гріду |
| 1 | той самий штраф | 1 | 0.8027 | канал зламано | вертаю | lr 0.003 |
| 1 | той самий штраф | 2 | 0.7275 | 1.0208 | **лишаю** | lr 0.0015 |
| 2 | штраф за плутанину 0.1 | 1 | 0.7757 | 0.8015 | вертаю | lr 0.003 · confuse 0.1 |
| 2 | штраф за плутанину 0.1 | 2 | 0.7588 | 1.0371 | **лишаю** | lr 0.0015 · confuse 0.1 |
| 3 | фокус γ2 | 1 | 0.1820 | 0.9710 | вертаю | lr 0.003 · loss focal |
| 3 | фокус γ2 | 2 | 0.1817 | 1.0653 | **лишаю** | lr 0.0015 · loss focal |
| 4 | штраф 0.2 | 1 | 0.8776 | канал зламано | вертаю | lr 0.003 · confuse 0.2 |
| 4 | штраф 0.2 | 2 | 0.7909 | 1.0558 | вертаю | lr 0.0015 · confuse 0.2 |
| 5 | стеля ваги класу 40 | 1 | 0.8291 | 0.8968 | вертаю | lr 0.003 · cap 40 |
| 5 | стеля ваги класу 40 | 2 | 0.8160 | 1.0415 | вертаю | lr 0.0015 · cap 40 |
| 6 | той самий штраф · крок ×0.3 | 1 | 0.6996 | 0.9766 | вертаю | lr 0.001 |
| 6 | той самий штраф · крок ×0.3 | 2 | 0.6942 | 1.0808 | **лишаю** | lr 0.0005 |
| 7 | фокус γ1 · штраф 0.1 | 1 | 0.3945 | 1.0678 | вертаю | lr 0.001 · loss focal · gamma 1 · confuse 0.1 |
| 7 | фокус γ1 · штраф 0.1 | 2 | 0.3920 | 1.0789 | вертаю | lr 0.0005 · loss focal · gamma 1 · confuse 0.1 |
| 8 | штраф 0.3 | 1 | 0.8187 | 1.1799 | **лишаю** | lr 0.001 · confuse 0.3 |
| 9 | стеля ваги класу 10 | 1 | 0.5098 | 1.0282 | вертаю | lr 0.001 · cap 10 |
| 9 | стеля ваги класу 10 | 2 | 0.5066 | 1.0679 | вертаю | lr 0.0005 · cap 10 |
| 10 | фокус γ2 · штраф 0.2 | 1 | 0.2576 | канал зламано | вертаю | lr 0.0003 · loss focal · confuse 0.2 |
| 10 | фокус γ2 · штраф 0.2 | 2 | 0.2598 | канал зламано | вертаю | lr 0.00015 · loss focal · confuse 0.2 |
| 11 | штраф 0.15 · стеля 40 | 1 | 0.8637 | 1.1190 | вертаю | lr 0.0003 · confuse 0.15 · cap 40 |
| 11 | штраф 0.15 · стеля 40 | 2 | 0.8634 | 1.0547 | вертаю | lr 0.00015 · confuse 0.15 · cap 40 |
| 12 | штраф 0.5 | 1 | 0.8609 | канал зламано | вертаю | lr 0.0003 · confuse 0.5 |
| 12 | штраф 0.5 | 2 | 0.8592 | 1.0484 | вертаю | lr 0.00015 · confuse 0.5 |
| 13 | усе разом | 1 | 0.4666 | 1.1141 | вертаю | lr 0.0003 · loss focal · gamma 1 · confuse 0.15 · cap 40 |
| 13 | усе разом | 2 | 0.4688 | 1.1002 | вертаю | lr 0.00015 · loss focal · gamma 1 · confuse 0.15 · cap 40 |

#### Видача на val по монетах

| Монета | оцінка: одобрена | драбина | зміна | AUC півот: одобрена | драбина | зміна | AUC корекція: одобрена | драбина | зміна | Подій val |
| ------ | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --------: |
| BTC_USDT | 1.0068 | 1.1618 | +0.1550 | 0.6453 | 0.6478 | +0.0025 | 0.8281 | 0.8251 | -0.0031 | 7746 |
| ETH_USDT | 0.7011 | 1.3540 | +0.6529 | 0.6425 | 0.7996 | +0.1571 | 0.8181 | 0.8266 | +0.0085 | 8655 |
| SOL_USDT | — | — | — | 0.5858 | 0.5579 | -0.0279 | 0.5561 | 0.5970 | +0.0409 | 7590 |
| XRP_USDT | — | — | — | 0.5000 | 0.6660 | +0.1660 | 0.4699 | 0.4439 | -0.0260 | 6914 |
| BNB_USDT | — | — | — | 0.5863 | 0.8197 | +0.2334 | 0.5297 | 0.5495 | +0.0198 | 6166 |
| DOGE_USDT | 0.7486 | 1.1536 | +0.4050 | 0.6528 | 0.7435 | +0.0908 | 0.6605 | 0.7434 | +0.0829 | 8894 |

## Перевірка ваг: замір наново

Числа розділу «Результат» — це те, що зміряв **сам прогін**. Ваги лежать
файлами, і той самий замір можна зробити ще раз. Скрипт
[`val_series.py`](scripts/val_series.py) веде обидві версії кожної мережі
по val BTC подія за подією і рахує її ж нерухому міру на самому BTC:

| Модель | Міра на BTC | У прогоні: одобрена → драбина | Переміряно: одобрена → драбина |
| ------ | ----------- | ----------------------------: | -----------------------------: |
| `legleft` | коридор (менше) | 0.7759 → 0.7759 | 0.7759 → 0.7759 |
| `legtip` | AP (більше) | 0.1838 → 0.1838 | 0.1838 → 0.1838 |
| `hazard100` | AUC (більше) | 0.6463 → 0.6504 | 0.6463 → 0.6504 |
| `hazard4` | AUC (більше) | 0.6065 → 0.6065 | 0.6065 → 0.6065 |
| `zone` | оцінка (більше) | 1.0068 → 1.1618 | **0.9162 → 1.0772** |

Чотири мережі з пʼяти повторюються **знак у знак**. Пʼята — `zone` — ні,
і причина не в драбині.

### Прискорювач `mps` лишає dropout увімкненим на замірі

Та сама вага, ті самі дані, той самий код — і дві різні відповіді
залежно від того, **чи був у цьому процесі зворотний прохід**:

```
процес, де спершу йшло навчання : оцінка 1.0068 (те, що записав прогін)
процес, де навчання не було     : оцінка 0.9162
той самий процес, dropout занулено вручну: оцінка 0.9162
```

На процесорі різниці немає: `cpu` в обох режимах дає 0.9162. Тобто після
зворотного проходу `mps` рахує `nn.LSTM` і `nn.Dropout` так, ніби модель
і далі вчиться, хоч `model.training` дорівнює `False`. Замір стає
заміром **зашумленої** моделі, а не тієї, яку зберегли.

Драбина `zone` вся пройшла в такому процесі: навчання й замір ідуть один
за одним, тому всі її числа — і точка відліку, і кожен щабель — зняті
з увімкненим dropout. Порівняння між щаблями від цього не розсипається
(всі міряні однаково), а от сам результат — так.

### Що дає `zone` на чесному замірі

Обидві версії, шість монет, `cpu`, dropout вимкнений:

| Версія | Оцінка | AUC півот | AUC корекція | Плутанина | BTC | ETH | DOGE | SOL · XRP · BNB |
| ------ | -----: | --------: | -----------: | --------: | --: | --: | ---: | --------------- |
| одобрена (`base.pt`) | **1.0471** | 0.7430 | 0.7046 | 0.4005 | 0.9162 | 1.2994 | 1.0855 | канал зламано |
| драбина (`model.pt`, щабель 8) | 0.9909 | 0.7425 | 0.7102 | **0.4617** | 1.0772 | 1.1575 | 0.8892 | канал зламано |

Висновок прогону перевертається: плутанина не впала з `0.417` до
`0.263`, а **зросла** з `0.400` до `0.462`, і оцінка стала гіршою на
`0.056`. Виграш `+0.4744` був виграшем над зашумленою точкою відліку —
дропаут бив по одобреній версії сильніше, ніж по тій, що вчилась під
прицільним штрафом. На самому BTC щабель 8 усе-таки кращий
(`0.9162 → 1.0772`), але на шести монетах — ні.

Ще одне число, яке видно тільки на чесному замірі: влучність драбини
`0.8936` дорівнює частці більшості `0.8936`. Тобто модель щабля 8 у
режимі `argmax` не каже «зона» ніколи — вона тримає ранги, але не
рішення.

## Фальшивий півот: скільки видачі тримається на розмітці

`legleft` не знаходить кінець ноги — їй кажуть, де нога **почалась**.
Півот приходить із розмітки зігзага 2.3 % на ема10 по всьому ряду, і з
нього ростуть чотири канали (`leg:up`, `leg:step`, `leg:guess`,
`leg:open`) плюс сама нарізка: памʼять LSTM обнуляється на першій події
ноги. Півот 2.3 % підтверджується, тільки коли ціна відійшла від нього
на 2.3 %, — на 95 півотах val медіанна затримка **80 подій** (розділ
«Скільки з цього дає розмітка» дослідження 29). Тобто в живому ряді нуль
лічильника стоїть у точці, про яку дізнаєшся сильно пізніше.

Замір простий: ряд val BTC ріжеться на рівні шматки по 150 подій, і
мережі кажуть, що кожен шматок — нова нога. Ваги ті самі, одобрені;
нічого не вчиться. Три постановки:

| постановка | що змінено |
| ---------- | ---------- |
| розмітка | справжні ноги зігзага — одобрена постановка |
| кожні 150 | півот кожні 150 подій, напрямок лишається справжнім |
| сліпий | те саме й без `leg:up`: від розмітки не лишилось нічого |

Вікно відповіді для міри скрізь **справжнє** — перші 150 подій після
справжнього півота. Інакше три постановки міряли б різні зрізи ряду.

| постановка | міра | коридор 0.9–1.1 | коридор 0.7–1.3 | похибка у вікні | медіана після вікна | у коридорі 0.7–1.3 після вікна |
| ---------- | ---: | ---: | ---: | ---: | ---: | ---: |
| розмітка | **0.7759** | 9.9 % | **36.0 %** | 221 | −0.4 | 0.2 % |
| кожні 150 | 5.7935 | **11.1 %** | 32.1 % | 282 | 501 | **14.4 %** |
| сліпий | 5.6035 | 10.3 % | 33.0 % | 277 | 482 | 14.2 % |

Міра в двох нижніх рядків гірша **механічно**: фальшивий півот не
закриває вікно ніколи, мережа говорить весь ряд, і надбавка за
балакучість становить 5.01 із 5.79. Читати треба коридори.

### Чотири канали розмітки не несуть нічого

Вузький коридор **не впав**: 9.9 % із справжніми ногами, 11.1 % із
фальшивими, 10.3 % без напрямку. Широкий просів на чверть (36.0 → 32.1 %),
медіанна похибка у вікні виросла з 221 до 282 подій — це є, але це не
обвал, якого чекаєш від мережі, у якої забрали орієнтир.

Три числа кажуть, що саме сталось:

| пара | кореляція |
| ---- | --------: |
| кожні 150 ↔ сліпий | **0.991** |
| розмітка ↔ кожні 150 | 0.278 |
| кожні 150 ↔ лічильник | **0.036** |

Канал напрямку не робить майже нічого — дві постановки дають ту саму
лінію. І видача **не є лічильником**: з базою «медіана залишку на цьому
номері кроку» вона не корелює зовсім. Мережа відповідає класом.

Більше того, медіана частки `сказано / правда` у вікні з фальшивим
півотом **ближча до одиниці**, ніж із справжнім: 0.987 проти 0.779.
Одобрена постановка систематично занижує; фальшива — ні.

### Що розмітка дає насправді — тишу

Різниця між рядками не в тому, що мережа каже, а в тому, **коли вона
мовчить**. З розміткою після вікна медіана видачі −0.4 події: тиша. З
фальшивим півотом вікно не закривається, і мережа каже 501 подію на
кожній події ряду. Половина нерухомої міри — саме про це.

І тиша не безкоштовна: після вікна фальшива постановка лягає в широкий
коридор на **14.4 %** подій, а одобрена — на 0.2 %, бо мовчить. Там, де
мережу примусили замовкнути, вона викидає відповідь, яка в одному
випадку з семи була б правильною.

### Чи мережа взагалі краща за сталу

Найкраща стала, підібрана **на самій val**, дає міру 0.7007 — краще за
0.7759 мережі. Але це вибір по відповіді. Чесно підібрана на train стала
— 300 подій — дає на BTC val **0.8727**, а train-медіана залишку у вікні
(362 події) — **0.8310**. Обидві гірші за мережу. Тобто `legleft`
справді щось знає, і знає це **з класу**, а не з півота.

### Межа заміру

Фальшива нога бере в памʼять чотири попередні фальшиві ноги — 600 подій,
тоді як справжня бере чотири справжні ноги, це близько 1570 подій, і
може дотягнутись у train. Частина розриву в широкому коридорі — звідси, а
не від каналів. Через діру в ряді фальшива нога не кладеться: ряд
ріжеться суцільними відрізками.

## Власна рамка: мережа сама ставить кінець ноги

Попередній розділ показав, що розмітка дає `legleft` головно **вікно
мовчання**. Тут вікно лишається, але ставить його сама мережа.

### Правило

    нога почалась  ->  перші 20 подій мережа відповідає
    N = round(mean(сказано на цих подіях))   ->  довжина ноги
    минуло N подій ->  сигнал «кінець ноги»: памʼять і лічильник з нуля

Розмітка в рамці не бере участі: `leg:up` погашено, `leg:step`,
`leg:guess` і `leg:open` рахуються від **власного** початку ноги. Ніщо
не заглядає вперед — відповідь на кроці `i` бачить події `0 … i`, а межа
ставиться на `N` кроків уперед від уже відомого початку. На відміну від
справжнього півота зігзага, який підтверджується через ~80 подій після
себе, ця рамка причинна цілком.

Від розмітки лишається тільки **ціль**: скільки подій до справжнього
півота. Інакше вчити нема на чому. Замикається це просто — перші
20 відповідей тягнуться втратою до справжнього залишку, а з них же
береться межа: вивчивши відповідь, мережа тим самим ставить межу на
місце півота.

### Чим міряємо

Коридор на подіях, де версія **відповідає**: у версії з розміткою це
перші 150 подій після півота зігзага, у власній рамці — перші 150 подій
своєї ноги. Правда скрізь одна — справжній залишок. Надбавки за
балакучість у мірі немає: вона міряє слухняність вікну, якого у власної
рамки немає. Замість неї поруч стоїть **покриття** — яку частку ряду
версія коментує: без нього можна влучати, мовчавши.

### Одобрені ваги рамки не тримають

Шість монет val: коридор **6.0 / 18.3 %** проти 8.2 / 25.5 % з
розміткою, 173 ноги медіаною 285 подій замість справжніх ~390, від
кожного справжнього півота до найближчої власної межі 55 подій. Мережа,
навчена в рамці розмітки, під власною ріже ногу втричі частіше, ніж
треба: вона систематично каже «нозі лишилось мало».

### Драбина форм втрати під цю рамку

Рамка інша, тому й втрата не зобовʼязана бути тією, якою вчили одобрену
версію. Вісім форм, кожна стартує з **тих самих** одобрених ваг і йде
по дві епохи; переможцю — ще чотири. Найкраща епоха кожної:

| форма | що питає | епоха | міра | коридор 0.9–1.1 | коридор 0.7–1.3 | покриття | ніг | медіана ноги | півот → межа |
| ----- | -------- | ----: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| **`down`** | той самий коридор, **заниження ×5** | 2 | **0.8364** | **8.3 %** | **24.4 %** | 34.5 % | 120 | **430** | 93 |
| `mae` | L1 на `log1p`: медіана залишку, без вікна і без часток | 2 | 0.8412 | 8.0 % | 23.7 % | 40.3 % | 141 | 346 | 66 |
| `ratio` | `\|log(сказано / правда)\|` — без мертвої зони коридору | 1 | 0.8433 | 7.6 % | 23.8 % | 45.4 % | 158 | 312 | 51 |
| `loose` | коридор без плати за балакучість | 1 | 0.8445 | 7.8 % | 23.3 % | 51.9 % | 183 | 256 | 61 |
| `tight` | вужчий коридор 0.95–1.05, дорожчі обидва краї | 1 | 0.8519 | 7.4 % | 22.3 % | 52.5 % | 185 | 246 | **47** |
| `band` | одобрена форма: коридор і мовчання після вікна | 1 | 0.8601 | 6.7 % | 21.3 % | 55.5 % | 199 | 239 | 50 |
| `head` | коридор, але перші 20 подій важать ×4 | 2 | 0.8602 | 7.0 % | 21.0 % | 49.7 % | 174 | 278 | 70 |
| `events` | Хубер на самому числі подій у сотнях | 2 | 0.8623 | 6.6 % | 21.0 % | 52.5 % | 186 | 258 | 55 |

Точка відліку — одобрені ваги в цій рамці: 0.8787.

**Форма втрати важить більше за епохи.** Відстань від `band` (0.8601) до
`down` (0.8364) більша, ніж усе, що дали додаткові епохи всередині
будь-якої форми: у переможця епохи 3–5 пішли назад (0.8559, 0.8466,
0.8581), і терпіння зупинило прогін.

**Таблиця читається однією віссю — ціною заниження.** Чим воно дорожче,
тим довша нога, тим краще число і тим менше покриття. `down` стоїть на
її кінці й дає коридор **8.3 / 24.4 %** проти 8.2 / 25.5 % у версії
**з розміткою** — тобто по самій відповіді зрівнявся з тією, якій півоти
дають готовими, не маючи жодного каналу розмітки.

Ціна в тому ж рядку: покриття 48.6 → 34.5 %, і межі стали **далі** від
справжніх півотів (55 → 93 події). Коротконогі форми тримають межу
ближче (`tight` — 47 подій), але гірше кажуть число. Одним прогоном
обидва кінці не беруться.

### На BTC окремо виграшу немає

| постановка | міра | коридор 0.9–1.1 | коридор 0.7–1.3 | покриття | ніг | медіана ноги | півот → межа |
| ---------- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| розмітка | **0.7707** | **9.9 %** | **36.0 %** | 16.0 % | 10 | 658 | — |
| сама собі · одобрені ваги | 0.9057 | 3.9 % | 15.0 % | 50.6 % | 32 | 289 | **89** |
| сама собі · перенавчена | 0.9130 | 4.3 % | 13.2 % | 30.3 % | 19 | 538 | 155 |

Перенавчання підтягло довжину ноги до справжньої (289 → 538 проти 658),
але коридор на BTC не зрушив, а межі відійшли від півотів. Виграш, який
видно на шести монетах, на цій одній монеті не тримається — той самий
ефект, що у висновку 4.

## Частка ноги: скільки її пройдено

Етап 7. Питання те саме — де ми в нозі, — але відповідь більше не в
подіях, а в частці, і головне тут **форма відповіді в часі**:

    0.0  нога щойно почалась
    0.5  пройдено половину
    1.0  нога скінчилась, і лінія сама падає в нуль

Тобто пилка. Розмітки на вході немає взагалі: чотири її канали
погашені, ряд ріжеться не по ногах, а рівними вікнами по `WIN` подій
(перші `WARM` з них — розігрів памʼяті, вони не міряються). Межа ноги
не приходить ні каналом, ні нарізкою — мережа має побачити кінець ноги
**сама, по фічах**. Памʼять LSTM саме для цього й потрібна.

Розмітка лишається тільки в цілі: частка справжньої ноги зігзага 2.3 %
на ема10. Поруч стоїть межа знизу — сам лічильник розмітки,
`крок / (крок + медіана залишку train)`, який мережа не бачить.

### Штраф за формою

| Доданок | Що питає | Ціна |
| ------- | -------- | ---- |
| `cliff` | видача висока там, де нога **вже нова** — тобто не впала | найдорожче, ×60 |
| `tail` | не доросла до одиниці в кінці ноги | заниження ×6 × (1 + 4) |
| `fall` | спуск там, де нога ще йде | ×120 на одиницю падіння |
| `reset` | сам сигнал «нога скінчилась», проти мітки перших `HOT` подій ноги | ×3, позитив важить ×10 |
| коридор | решта відхилень, експонента поза мертвою зоною | вниз ×6, вгору ×2 |

Спуск заборонений **не скрізь**, а рівно там, де правда росте: на
самому півоті падіння — це те, чого від мережі й хочуть. Два доданки
дивляться в різні боки й ніде не перетинаються.

Безштрафова зона звужується за прогін: 20 % на початку, 3 % у кінці.

### Чим міряємо

Нерухома міра — коридор довкола справжньої частки (`mark = 1 − (near +
wide) / 2`). Але сама вона форми не бачить: **рівна лінія посередині
дає пристойний коридор і жодної пилки**, і саме тому поруч стоять
числа форми:

    голова   медіана видачі на перших 10 % ноги      → має бути ~0
    хвіст    медіана видачі на останніх 10 %         → має бути ~1
    розмах   хвіст − голова                          → висота пилки
    обвал    медіана падіння видачі на справжньому півоті
    ріст     кореляція видачі з правдою всередині ноги
    часто    обнулень на 1000 подій (справжніх півотів — 2)
    влучила  частка справжніх півотів, біля яких обнулення є
    дарма    частка обнулень, біля яких півота немає

Краща епоха обирається не мірою, а оцінкою `mark + (1 − розмах)/2 +
(1 − обвал)/2`: точність і форма разом.

### Пʼять ітерацій

Кожна міняла одне й міряла те саме.

| # | Що змінилось | Розмах | Обвал | Обнулень / 1000 | Влучила |
| - | ------------ | -----: | ----: | --------------: | ------: |
| 1 | вільна сигмоїда, штраф за формою | **0.006** | 0.003 | — | — |
| 2 | лінія будується накопиченням: `s = (1 − r)(s + g)` | 0.14 | 0.027 | 34 | 36 % |
| 3 | пауза 60 подій після обнулення + фаза чистого детектора | 0.12 | 0.007 | 3.9 | 25 % |
| 4 | пауза 300 подій — частота з самих даних | 0.02 | 0.003 | **2.3** | 11 % |
| 5 | падіння накопиченням: за подію не більше чверті висоти | 0.08 | 0.013 | 112 | 65 % |

**Ітерація 1.** Мережа каже частку прямо сигмоїдою. Втрата падає
рівно (2.74 → 1.51), а форма вмирає: видача сходиться до **рівної
лінії на 0.45** і стоїть там. Це не збій навчання, а оптимум: правда
розподілена рівно по нозі, і за цього штрафу найдешевша стала
відповідь — 0.69 (якби заниження й завищення коштували однаково, було
б 0.50). Градієнт веде саме туди.

**Ітерація 2.** Форму вбудовано в конструкцію. Мережа більше не каже
частку — вона каже два числа на подію: `r` (обнулись) і `g`
(підрости), а лінія будується накопиченням `s = (1 − r)·min(s + g, 1)`.
Вниз вона тепер не може піти інакше, ніж через власний сигнал. Форма
зʼявилась (розмах 0.14), але кнопка тиснеться **34 рази на тисячу
подій** там, де справжніх розворотів два.

**Ітерація 3.** Пауза після обнулення (слід згасає геометрично за ~60
подій) плюс фаза, де перші епохи штрафується **тільки** детектор.
Частота стала майже правильною, а на фазі детектора влучність росла
17 → 35 → 39 → 48 → **54 %**. Фаза форми її зруйнувала назад до 15 %:
штраф за форму переписує тулуб під рівну лінію.

**Ітерація 4.** Пауза 300 подій — це число з даних, а не з підбору:
медіана ноги зігзага 2.3 % — сотні подій, двох розворотів поряд не
буває. Частота обнулень стала **правильною** (2.3 на тисячу проти
справжніх 2), і саме тут видно головне число всього етапу: на цій
частоті детектор влучає лише в **11–17 %** розворотів, а 88 % його
сигналів хибні.

**Ітерація 5.** Замір стелі детектора пояснив, чому: пущений на волю,
він бере **78 %** розворотів, але ціною 42 обнулень на тисячу подій.
Отже сигнал про кінець ноги в класі **розмазаний по десятках подій**,
а не стоїть у самому півоті. Тому лінія стала падати накопиченням: за
подію не більше чверті висоти, і щоб упасти в нуль, треба тримати
сигнал десяток подій поспіль. Детектор справді почав позначати 65–73 %
розворотів — але позначаючи при цьому кожну десяту подію ряду, і пилка
з цього не склалась (розмах 0.08).

### Що з цього виходить

Форма лінії більше не проблема: вона вбудована в конструкцію, і вниз
лінія не може піти інакше, ніж через власний сигнал. Проблема одна й
заміряна числом:

> **На правильній частоті обнулень клас `клас210х10-1х7д/д` знаходить
> один розворот із восьми, і сім із восьми його сигналів хибні.**

Пилка з такого детектора не складається ні за якої функції втрати:
щоб зронити лінію в правильному місці, треба знати місце. Це той самий
висновок, що в дослідженнях 22, 23 і 30 — зона розвороту слабко
відрізняється від решти ноги, — тільки тут він отриманий з іншого боку
й на одному числі.

## Зона розвороту і драбина промаху

Етап 7 упирався в детектор: форму лінії вбудували в конструкцію, а
місце розвороту мережа не бачила. Тут питання ставиться з іншого боку —
**де саме кінчається нога і скільки коштує промах**.

### Спершу замір: «5 % ноги» — не міра

Раніше край ноги брався у відсотках: перші й останні 5 %. Так не можна.
Пʼять відсотків ноги в 1200 подій — це 60 подій, а ноги в 120 подій —
шість. Якщо подія біля розвороту й справді інакша, ця різниця стоїть
**у подіях** від півота, а не в частках.

`pivot_zone.py` міряє її прямо. Кошик — події на відстані `d` подій від
розвороту; тіло — середина ноги (40…60 % її довжини); кошик і тіло
ніколи не перетинаються. На кожен кошик окремо вчиться логістична
регресія на 410 каналах класу (розмітка не заходить) на ногах train і
міряється `AUC` на ногах val: чи відрізнить вона подію кошика від події
тіла. Поруч — **підлога**: той самий прохід із перемішаними мітками.

Ноги довші за 200 подій: train 355, val 66.

| Подій від півота | AUC хвоста | надлишок | AUC голови | надлишок |
| ---------------- | ---------- | -------- | ---------- | -------- |
| 0…2 | 0.759 | 0.276 | 0.698 | 0.158 |
| 3…5 | 0.777 | 0.252 | 0.622 | 0.129 |
| 6…9 | 0.764 | 0.264 | 0.652 | 0.148 |
| 10…14 | 0.745 | 0.199 | 0.645 | 0.152 |
| 15…19 | 0.715 | 0.212 | 0.626 | 0.101 |
| 20…29 | 0.694 | 0.149 | 0.604 | 0.103 |
| 30…39 | 0.676 | 0.154 | 0.599 | 0.098 |
| 40…49 | 0.639 | 0.138 | 0.601 | 0.108 |
| 50…69 | 0.627 | 0.136 | 0.581 | 0.084 |
| 70…99 | 0.606 | 0.114 | 0.570 | 0.082 |
| 100…149 | 0.560 | 0.057 | 0.576 | 0.088 |
| 150…199 | 0.481 | −0.027 | 0.511 | −0.020 |
| 200…299 | 0.473 | −0.024 | 0.472 | −0.010 |

Читається це так:

- **різниця є, і вона сильна** — 0.78 проти підлоги 0.52 за три-пʼять
  подій до розвороту;
- **пік стоїть не в самому півоті**, а за 3…9 подій до нього;
- **обриву немає**: надлишок спадає полого й сідає в підлогу аж за
  150 подій. Тому межа береться не «різниці більше немає», а
  **половина висоти піка**: хвіст — 50 подій, голова — 150;
- голова слабша за хвіст удвічі (пік 0.158 проти 0.276): подія **перед**
  розворотом виглядає інакше сильніше, ніж подія після нього.

Для штрафу потрібна одна лінійка на обидва боки, тому безштрафова зона
береться спільним порогом — половина найбільшого надлишку (0.138),
смугою з двох сусідніх кошиків:

> **50 подій до розвороту і 15 після.** Усередині цієї зони обнулення
> мережі не штрафується взагалі: там подія й сама не відрізняється від
> сусідніх.

У частках ноги ці 50 подій — від 5 % до 30 % залежно від ноги. Саме
тому зона міряється в подіях.

### Драбина: втричі на кожні пʼять подій

`zone_train.py` бере мережу етапу 7 **з її ваг** і міняє тільки штраф на
сигнал обнулення:

| Доданок | Що питає | Ціна |
| ------- | -------- | ---- |
| `пізно` | нога скінчилась, сигналу немає | ×4, помножене на **мовчання** |
| `рано` | обнулення до зони | ×4 |
| драбина | кожні 5 подій промаху повз зону | ×3, стеля 243 |
| `cliff` | видача висока там, де нога вже нова | ×60 |
| `fall` | спуск там, де нога ще йде | ×120 |

Перші пʼять подій поза зоною — «стандартний» штраф (×3⁰), далі ×3, ×9,
×27. Стеля обовʼязкова обом бокам: без неї подія з середини ноги
коштувала б ×3⁶⁰.

Штраф за «пізно» помножений на **мовчання** — добуток `(1 − r)` від
самого розвороту. Поки мережа не натиснула, ціна росте; щойно
натиснула — доданок гасне сам. Тому градієнт тягне натиснути
**раніше**, а не сильніше.

### Перший прогін показав, чого драбині мало

| Епоха | Втрата | Розмах | Обвал | Спуски | Обнулень/1000 | У зоні | Без сигналу |
| ----- | ------ | ------ | ----- | ------ | ------------- | ------ | ----------- |
| 1 | 13.12 | 0.039 | 0.009 | 0.280 | 19.4 | 36 % | 44 % |
| 4 | 9.42 | 0.055 | 0.006 | 0.237 | 21.0 | 40 % | 44 % |
| 8 | 7.84 | 0.072 | 0.005 | 0.185 | 20.5 | 39 % | 44 % |

Втрата падає, а пилки немає: лінія сіла в рівну мушу біля 0.55 і йде
вниз на кожній пʼятій події. Причина не в драбині, а в трьох речах, і
всі три заміряні:

1. **Кволий сигнал точить лінію.** У конструкції етапу 7
   `s ← (1 − fade·r)·(s + g)`, і `r ≈ 0.1` скрізь — це мінус 2.5 % на
   кожній події. Рости лінії нема коли.
2. **Хибна тривога була вигідна.** «Рано» коштувало ×1 проти ×4 за
   «пізно», і мовчання після розвороту обходилось дорожче, ніж вісім
   хибних тривог за ногу.
3. **Драбина заглушила форму.** Її доданки сягали 972 на подію проти
   одиниць у коридора: мережі було байдуже, що малює лінія, поки вона
   вгадує момент.

Перша спроба вилікувати це степенем (`r²` у множнику спуску) дала
дзеркальну біду: лінія прилипла до одиниці й не спускалась узагалі
(голова 0.9998, хвіст 0.9999, обвал 0.0005). Слабкого сигналу мало,
щоб зрушити лінію, а сильного мережа не давала.

Тому спуск зроблено **рішучим через мертву зону**: у множник іде не
сам сигнал, а `(r − 0.20) / 0.80`, обрізане нулем і одиницею. Сигнал
слабший за 0.20 лінію не чіпає взагалі, сильний роняє її так само, як
раніше, і трохи швидше (`fade` 0.35 замість 0.25). Тим самим числом
говорить і штраф: інакше лишалась лазівка — тримати `r` трохи нижче
мертвої зони, лінію це не роняє, а штраф за мовчання гасить. Обидва
боки драбини вирівняні (×0.5 і ×0.5), стеля збита до 81: множники між
собою ті самі, ×3 на кожні пʼять подій, змінився тільки масштаб — щоб
форма й час розвороту важили одне проти одного, а не тонули.

## Висновки

### 1. Донавчання дає рівно там, де прогін не дійшов до кінця

Чотири одобрені мережі, і результат розкладається без винятків по одній
ознаці — **чим скінчився їхній власний прогін**:

| Модель | Краща епоха | Чим скінчився прогін | Драбина |
| ------ | ----------- | -------------------- | ------- |
| `hazard100` | 8 з 11 | `patience` на зростанні | **+0.0224**, 14 спроб із 29 поза шумом |
| `zone` | — | ваг не лишилось узагалі | на переміру гірше: `1.047 → 0.991` (розділ 5) |
| `legleft` | 24 з 32 | прогін дійшов, далі val ріс | 0 з 23 |
| `legtip` | 20 з 26 | прогін дійшов | 0 з 25 |
| `hazard4` | 5 з 8 | `patience` | 0 з 22 |

`hazard4` — єдиний, хто ламає просте правило «зупинив `patience` → є що
доучувати»: його теж спинила рання зупинка, але жодна з 22 спроб не
зрушила міру навіть на шум (`±0.0026`, найменший з усіх). Тобто питання
не в тому, скільки епох дали, а в тому, **чи міра ще рухалась**.

### 2. Виграє не форма штрафу, а крок навчання

Щаблі, які взяв `hazard100`, — це `той самий штраф · крок ×0.3` і
`вікно градієнта 40`. Жодна з нових форм втрати — ні фокус, ні вага
близькості до півота, ні потроєна вага мітки — не дала нічого, чого не
дав би просто менший крок на тій самій втраті. Це прямий висновок для
решти проєкту: **перш ніж вигадувати втрату, треба довчити на меншому
кроці.**

Щаблі `zone`, де новий штраф начебто дав стрибок, переміру не
пережили — розділ 5. Тобто винятку немає взагалі: жодна нова форма
втрати не виграла.

### 3. Одобрене число — це відібраний мінімум, а не рівень моделі

`legleft`: усі 23 спроби лягли в `0.85–0.90` при точці відліку `0.8373`
і шумі `0.0069`. Тобто одобрена версія — не те, що мережа вміє, а
**найкраща з 32 епох**, відібрана по тому самому val. Одна нова епоха
повертає модель на типовий рівень її ж кривої, і це не погіршення
навчання, а зникнення відбору.

Звідси правило для порівнянь у проєкті: відібраний мінімум не
порівнюється з одиничним кидком без міри розкиду цього кидка. Саме тому
в кожної моделі тут стоїть **шум замірювання**.

### 4. Середнє по монетах ховає монету

`hazard100` дав `+0.0224` середнього — але це `BNB +0.0838`,
`SOL +0.0459`, `DOGE +0.0230` і `BTC +0.0042` при `ETH −0.0176` і
`XRP −0.0050`. Виграш зібрано насамперед на найслабшій монеті: BNB
стояв на `0.5552`, тобто майже на випадковому рівні, і піднявся до
`0.6391`. Дві найсильніші монети при цьому трохи просіли. Для торгівлі це інша річ, ніж
«модель покращилась»: покращилось те, що було найгіршим.

### 5. Виграш `zone` не пережив переміру — і це головна знахідка

Прогін показав `0.7055 → 1.1799` і плутанину, зрізану з `0.417` до
`0.263`. Замір тих самих ваг наново дає `1.0471 → 0.9909` і плутанину,
що **зросла** з `0.400` до `0.462`.

Причина не в драбині, а в прискорювачі: після зворотного проходу `mps`
рахує `nn.Dropout` увімкненим і на замірі, хоч модель стоїть у режимі
`eval`. Одобрена версія від цього шуму втрачала більше, ніж версія, що
вчилась під прицільним штрафом, — звідси й весь «виграш» (розділ
«Перевірка ваг: замір наново»).

Що з цього випливає ширше: **будь-яке число, зняте в одному процесі з
навчанням на `mps`, треба переміряти окремим прогоном на `cpu`.** Це
стосується не тільки цього дослідження — грід етапу 7 дослідження 31
знімався так само.

Уточнення до етапу 7 дослідження 31, яке я зробив був із прогону
(«прицільний штраф ламає канал, який ще будується, і не ламає
побудований»), **знімається**: на чесному замірі штраф `0.3` плутанину
не зменшив.

### 6. Що з цим робити далі

- `hazard100` має кращу версію, ніж його одобрена: ваги в
  `models/hazard100/model.pt`, і переміряний BTC повторює число прогону
  знак у знак (`0.6463 → 0.6504`). Одобреною вона **не стає
  автоматично** — це рішення користувача (`rules/03-models.md` →
  Фінальна тестова модель).
- `zone` у роботу не йде: на чесному замірі драбина зробила гірше.
  Осмислено повторити її цілком на `cpu` — і базову версію, і щаблі.
- `legleft`, `legtip` і `hazard4` лишаються як є: доучувати їх нічим.
- Перевірити на `cpu` числа дослідження 31, знятих тим самим способом.

### 7. Замір мусить робитись окремим прогоном

Драбина міряла себе сама, у тому ж процесі, де вчилась, — і найгучніший
результат усього прогону виявився артефактом прискорювача. Замір
збережених ваг окремим процесом коштує хвилин і ловить те, чого не
ловить жодна крива навчання: криві в прогоні `zone` виглядали
бездоганно.

### 8. Розмітка дає `legleft` тишу, а не відповідь

Фальшивий півот кожні 150 подій не збив мережу: вузький коридор
9.9 → 11.1 %, і без каналу напрямку 10.3 %. Видача з фальшивими ногами
не корелює з лічильником (0.036) і майже тотожна видачі без напрямку
(0.991) — відповідає клас. Чотири канали розмітки й нарізка по ногах
дають головно **вікно мовчання**, а воно й тримає половину нерухомої
міри. При цьому мережа обходить чесно підібрану сталу (0.7759 проти
0.8310), тож знання в ній є.

Практичний бік: у живому ряді нуль лічильника доступний із затримкою
близько 80 подій, і ця робота показує, що `legleft` це переживе — але
разом із тишею пропаде і той механізм, яким її обирали за мірою. Міру
доведеться переписати під постановку без вікна.

### 9. Мережа може обійтись без півота, але платить покриттям

Власна рамка знімає з `legleft` останню непричинну підпору: півот
зігзага, який у живому ряді підтверджується через ~80 подій. Одобрені
ваги її не тримають (коридор 6.0 / 18.3 % проти 8.2 / 25.5 %), але
перенавчання під формою `down` доводить коридор до **8.3 / 24.4 %** —
рівня версії з розміткою.

Заплачено покриттям: ноги довші, вікно відповіді покриває 34.5 % ряду
замість 48.6 %, і власні межі стоять далі від справжніх півотів (93
події проти 55). Тобто мережа навчилась **краще казати число** і гірше
називати місце розвороту. Це різні задачі, і одна втрата обидві не
закриває: місце розвороту має своїх детекторів — `legtip` і `hazard`.

На BTC окремо виграшу немає зовсім, і це той самий висновок 4: середнє
по монетах ховає монету.

### 10. Форму можна вбудувати в конструкцію, місце розвороту — ні

Ціль «частка пройденої ноги» вимагає пилки: ріст до одиниці й обвал у
нуль, який мережа робить сама. Пʼять ітерацій показали, що ця задача
розпадається на дві, і вони різні за складністю.

**Форма — питання конструкції, і воно закрите.** Вільна сигмоїда під
будь-яким штрафом сходиться до рівної лінії: правда розподілена рівно
по нозі, і найдешевша стала відповідь (0.69 за цього штрафу, 0.50 за
симетричного) завжди б'є спробу вгадати. Щойно лінію перестали
передбачати й почали **будувати** — `s = (1 − r)·min(s + g, 1)`, де
`r` і `g` каже мережа, — вона стала монотонною за побудовою і впасти
може тільки через власний сигнал. Жодна функція втрати цього не дала
б: це властивість виходу, а не штрафу.

**Місце розвороту — питання даних, і воно відкрите.** На правильній
частоті обнулень (2.3 на тисячу подій проти справжніх 2) детектор
влучає в **11–17 %** розворотів, а 88 % його сигналів хибні. Пущений
на волю, він бере 78 % — ціною 42 сигналів на тисячу подій. Тобто
сигнал у класі є, але він розмазаний по десятках подій і не стоїть у
самому півоті.

Звідси й межа всього етапу: **пилку тримає детектор, а не втрата**.
Скільки не міняй форму штрафу, зронити лінію в правильному місці не
вийде, поки місце невідоме. Це той самий висновок, що в дослідженнях
22, 23 і 30, здобутий з іншого боку.

## Вкладка дашборду

[Драбина втрат](http://localhost:8080/ladder) — сім розділів:

| Розділ | Що показує |
| ------ | ---------- |
| Метод | що робить драбина, чим міряє і що робить, коли гірше |
| Моделі під драбиною | рядок на модель: нерухома міра, число одобреної версії, найкраще, що взяла драбина, зміна, скільки щаблів узято зі скількох спроб і час. Рядок відкриває свою драбину |
| Хід драбини | щабель за щаблем: що дорожчає, спроба, втрата, міра, рішення й ручки; під ним — **видача на val по кожній монеті**, одобрена проти драбини, з різницею на кожне число |
| Ряд val BTC | ціна BTC із зігзагом 2.3 % на ема10, під нею **плашка на кожну модель** на спільній осі X: суцільна лінія — драбина, пунктир — одобрена версія, смуга — правда, про яку питають. Під плашками таблиця двох замірів: число прогону і число, переміряне на процесорі |
| Фальшивий півот | окремий графік: ціна BTC і під нею видача `legleft` у трьох постановках — справжня розмітка, півот кожні 150 подій і той самий півот без каналу напрямку. Вертикалі — межі фальшивих ніг. Під графіком таблиця коридорів: чим міряти, коли мережа перестала мовчати |
| Сама собі півот | окремий графік: ціна BTC і під нею видача `legleft` у власній рамці — одобреними вагами і перенавченими в ній. Вертикалі — межі, які мережа поставила собі сама. Під графіком таблиця рамок із покриттям і відстанню «півот → найближча межа», а під нею крива перенавчання епоха за епохою |
| Частка ноги | окремий графік: ціна BTC і під нею **частка пройденої ноги** на спільній нерухомій шкалі 0…1 — правда (пилка), лічильник розмітки межею знизу, мережа і її власний сигнал «обнулись», яким вона роняє лінію. Під графіком таблиця з числами форми — голова, хвіст, розмах, обвал, ріст, — а під нею крива навчання з шириною безштрафової зони, частотою обнулень і оцінкою кожної епохи |

Колонка «втрата» стоїть заради повноти: на кожному щаблі вона рахується
іншою формулою, і між щаблями її число не читається. Читається тільки
міра.

Дані — `/api/ladder` (перелік моделей), `/api/ladder/<модель>` (хід
однієї драбини), `/api/ladder/series` (ряд val BTC) і
`/api/ladder/fake` (фальшивий півот), `/api/ladder/self` (власна рамка),
`/api/ladder/selftrain` (крива перенавчання), `/api/ladder/share` (ряд
частки ноги) і `/api/ladder/sharetrain` (криві обох рамок частки). Прогони йдуть один
за одним і лягають файлами по черзі, тому вкладка питає перелік, а не
зашиває його: готова модель зʼявляється сама, а та, що ще рахується,
стоїть рядком «ще рахується».

Плашки покривають різні відрізки val, і кожен стоїть числом у таблиці:
ноги дослідження 29 лежать у val цілком, навіть якщо почались до межі
(з 10.08); у дослідження 20 своя межа розбиття (14.08 15:27) і свій
кінець датасету (28.08); зони дослідження 31 ідуть від першого блока
val (14.08 14:29) до 04.09. Розрив у лінії — «модель тут не
відповідає», а не нуль.

### Що вийшло

Мережа етапу 7 донавчена цією втратою з її ж ваг, крок `1e-4`, 23 епохи
по хвилині, краща — 11-та (далі 12 епох без покращення). Міри — на
ногах val усіх шести монет, 102 розвороти.

| Епоха | Втрата | Голова | Хвіст | Розмах | Спуски | Обнулень/1000 | У зоні | Без сигналу | Оцінка |
| ----- | ------ | ------ | ----- | ------ | ------ | ------------- | ------ | ----------- | ------ |
| 1 | 2.99 | 0.943 | 1.000 | 0.057 | 6.5 % | 48.8 | 48 % | 31 % | 2.348 |
| 2 | 2.54 | 0.746 | 0.863 | 0.117 | 7.4 % | 52.0 | 53 % | 27 % | 2.267 |
| 5 | 2.01 | 0.602 | 0.759 | 0.157 | 7.7 % | 41.0 | 50 % | 29 % | 2.258 |
| **11** | **1.62** | **0.637** | **0.791** | **0.154** | **7.3 %** | **34.7** | **50 %** | **28 %** | **2.240** |
| 17 | 1.53 | 0.711 | 0.858 | 0.147 | 6.1 % | 29.4 | 49 % | 28 % | 2.264 |
| 23 | 1.43 | 0.627 | 0.708 | 0.081 | 6.2 % | 33.5 | 50 % | 28 % | 2.318 |

Проти етапу 7 на тому самому замірі:

| Число | Етап 7 (`legshare`) | Етап 8 (`legzone`) |
| ----- | ------------------- | ------------------ |
| спуски всередині ноги | 49 % подій | **7 %** |
| розмах голова→хвіст | 0.04 | **0.154** |
| розворотів із обнуленням у зоні | — | **50 %** |
| розворотів без жодного сигналу | — | 28 % |
| обнулень на 1000 подій | 20–30 | 34.7 |

Пилка з'явилась: лінія росте вгору і падає різко, а не сиплеться
щокроку. Половина розворотів має обнулення в межах заміряної зони, і
ще чверть — поза нею, але поруч. Ціна цього — 35 обнулень на тисячу
подій там, де справжніх розворотів 2.5: мережа роняє лінію **вчетверо
частіше**, ніж є ноги, тобто ставить по два-три зубці на одну справжню
ногу.

### На BTC окремо

Ряд val BTC — це 10 вікон, 8894 події й **девʼять** розворотів, і на
такій вибірці модель виглядає гірше за пул: у зоні 11 %, без сигналу
44 %, голова 0.75 проти хвоста 0.72 (форми немає взагалі), зсув +0.29.

Це не суперечність, а та сама межа заміру, що й в етапі 7: девʼять
розворотів — це девʼять кидків монети. Читати треба пул із 102
розворотів, а ряд BTC дивитись очима — і очима видно головне: **зубці
стали зубцями**, але їх утричі більше, ніж справжніх ніг, і стоять
вони не там.


## Зона по нозі й хибні розвороти

Статус: **підтверджено, 2026-09-17**.

Етап 8 поставив безштрафову зону обнулення одним числом на всі ноги: 50
подій до розвороту, 15 після. Тут три питання підряд: чи однакова ця
зона на різних ногах, чи можна дати кожній нозі свою межу, і куди
насправді падають зайві зубці мережі.

### 50 подій — це різна поблажка різним ногам

Ті самі 50 подій у частках ноги (з `pivot_zone.json`): медіана 8.9 %,
але 3.7 % на довгій нозі й 17.1 % на короткій, а на найкоротшій нозі
датасету — чверть її довжини. Виходить перекіс не на користь справи:
**коротка нога дістає більше безштрафових подій, ніж довга**, і саме
там, де подій на всю ногу мало.

### Замір: зона їде з довжиною ноги

[`scripts/zone_length.py`](scripts/zone_length.py) — та сама міра, що в
`pivot_zone.py` (кошик проти тіла ноги, логістична регресія на 410
каналах, `AUC` на ногах val, підлога на перемішаних мітках), але ноги
спершу діляться на три кошики за довжиною. Межі кошиків — терцилі ніг
train, довших за 200 подій: **380** і **780** подій.

Надлишок `AUC − підлога` перед розворотом, згрупований по відстані
(кошики складені по 2–4, бо окремий кошик на двох десятках ніг val
шумить на ±0.08):

| подій до розвороту | коротка (медіана 300) | середня (524) | довга (1086) |
| ------------------ | --------------------- | ------------- | ------------ |
| 0…9 | 0.166 | 0.210 | 0.266 |
| 10…29 | 0.145 | 0.101 | 0.220 |
| 30…49 | −0.047 | 0.095 | 0.157 |
| 50…99 | 0.011 | 0.019 | 0.186 |
| 100…199 | 0.022 | 0.021 | 0.082 |

Згасання (надлишок упав утричі проти піка свого кошика, дві групи
поспіль нижче порога):

| кошик | медіана ноги | зона згасає на | це у частках ноги |
| ----- | ------------ | -------------- | ----------------- |
| коротка | 300 подій | 30 подій | **10.0 %** |
| середня | 524 подій | 50 подій | **9.5 %** |
| довга | 1086 подій | 100 подій | **9.2 %** |

Розмах між кошиками: у подіях **3.3×**, у частках ноги **1.09×**.
Зона стоїть не в подіях, а в **частці**: приблизно десята частина
ноги, однаково на коротких і на довгих.

Поруч іде перевірка тією самою мірою, але з кошиками, нарізаними
одразу в частках ноги. Там видно те саме з іншого боку: на всіх трьох
довжинах надлишок тримається до 10 % ноги й падає в нуль на межі
10…20 % — 0.110 → −0.019 у коротких, 0.049 → 0.018 у середніх,
0.230 → −0.005 у довгих.

Голова ноги слабша й коротша: надлишок є тільки на першій десятці
подій (0.039 / 0.118 / 0.142) і гасне до 10…30 подій незалежно від
довжини ноги. Голову має сенс лишити в подіях.

### Своя межа кожній нозі: метод не пройшов перевірку

[`scripts/zone_own.py`](scripts/zone_own.py) переносить метод
дослідження 31: від розвороту йдемо в бік тіла ноги, поки подія
несхожа на свою ногу; несхожість — робастний `z` проти опори **своєї**
ноги, поріг — квантиль `d` самої ноги, а не стале число.

Перевірок чотири, і головна з них — **фальшива точка**: та сама
процедура з випадкової події тіла ноги. Якщо зона звідти така сама, як
із розвороту, метод описує не розворот.

| спосіб `d` | зона хвоста, медіана | довша за фальшиву | `AUC` проти фальшивої | стійкість до порога |
| ---------- | -------------------- | ----------------- | --------------------- | ------------------- |
| `plain` — медіана \|z\| по 410 каналах | 4 події | 48 % ніг | 0.6954 | 0.65…0.92 |
| `learned` — те саме на 40 найважчих каналах | 0 подій | 38 % ніг | 0.5747 | 0.66…0.96 |
| `score` — видача детектора, поріг персональний | **10 подій** | **62 % ніг** | **0.7642** | 0.82…0.95 |

Рецепт дослідження 31 у лоб (`plain`) тут **не працює**: межа з
розвороту виходить довшою за межу з випадкової точки лише на половині
ніг — тобто на монетку. Причина видно з самих чисел: `d` як медіана
\|z\| шукає **сплеск величини** в окремому каналі, а різниця біля
півота живе в **напрямку** — логістика бачить її з `AUC` 0.78, окремий
канал не бачить ніяк. Звуження до 40 найважчих каналів (`learned`)
робить гірше, а не краще.

Працює третій спосіб: за `d` береться сама видача детектора,
навченого на ногах train, а персональним лишається **поріг** —
квантиль видачі цієї ноги. Він дає 62 % проти монетки й тримає ранги
довжин при порозі від 0.5 до 0.8.

Вміст зони при **рівному бюджеті подій** (рівній межі віддається
стільки ж подій, скільки забрали персональні, — інакше менша вибірка
сама собою дає вищий `AUC`):

| межа | подій val | `AUC` | підлога | надлишок |
| ---- | --------- | ----- | ------- | -------- |
| персональна `score` | 1589 | 0.8519 | 0.4971 | **0.355** |
| рівна, той самий бюджет (2.65 % ноги) | 1269 | 0.7324 | 0.4882 | 0.244 |
| рівна, 9.5 % ноги | 4555 | 0.6699 | 0.5260 | 0.144 |

Персональна межа справді добірніша. Але ця перевірка **частково
замкнена**: межу малює детектор, а вміст міряє модель того самого
роду. Тому висновок спирається на фальшиву точку й на стійкість, а не
на цей рядок.

І головне обмеження: персональна зона — це медіана **10 подій**
(2.65 % ноги) там, де статистика каже 10 %, і вона майже не залежить
від довжини ноги (рангова кореляція 0.31, а для частки 0.07). Це не
кращий варіант правила «10 % ноги», а інше, набагато тісніше правило.

### Куди насправді падають зайві зубці

[`scripts/zone_false.py`](scripts/zone_false.py). Корекція — півот
**вкладеного** зігзага на тій самій ема10, усередині ноги, не ближче
за 40 подій до країв і **того самого боку**, що й кінець ноги: місце,
яке виглядає як її завершення. Що ціна датасету — саме та ема, на якій
стоять ноги, скрипт перевіряє першим ділом: усі **554 з 554** кінців
ніг лягли рівно на півоти зігзага 2.3 %.

**Чи розвʼязна задача взагалі.** Клас перед справжнім кінцем ноги
проти класу перед корекцією, причинне вікно 10 подій, навчання на
train, `AUC` на val, підлога на перемішаних мітках:

| глибина корекції | корекцій | `AUC` | підлога | надлишок |
| ---------------- | -------- | ----- | ------- | -------- |
| 0.5…0.8 % | 613 | 0.6651 | 0.4958 | **0.169** |
| 0.8…1.0 % | 220 | 0.5262 | 0.5295 | −0.003 |
| 1.0…1.5 % | 316 | 0.5273 | 0.5240 | 0.003 |
| від 1.5 % | 133 | 0.4856 | 0.4703 | 0.015 |

Дослідження 30 (етап 5) знайшло те саме на одному BTC, семи фічах і 63
півотах. Тут той самий результат на 410 каналах, шести монетах і 1282
корекціях: **клас відрізняє кінець ноги від дрібної корекції 0.5–0.8 %
і не відрізняє від корекції 0.8 % і глибшої взагалі**. Події перед
корекцією в 1 % не відрізняються від подій перед справжнім кінцем
ноги. Жодна функція втрат не навчить мережу розділити те, що на вході
однакове.

**Де стоять зубці мережі.** Прохід ваг `legzone` по всіх 58 вікнах val
(47 тис. подій, 103 розвороти), 1634 обнулення. Поруч із часткою
обов'язково стоїть **покриття** — яку частку ряду взагалі займають ці
вікна, бо інакше широке вікно саме собою дає високу частку:

| куди лягло | частка обнулень | покриття ряду | підйом над випадковим |
| ---------- | --------------- | ------------- | --------------------- |
| зона справжнього розвороту | 25.4 % | 13.3 % | **1.91** |
| корекція 0.3 %, ±10 подій | 17.6 % | 15.5 % | 1.14 |
| корекція 0.5 %, ±10 подій | 11.4 % | 9.3 % | 1.23 |
| корекція 0.8 %, ±20 подій | 14.0 % | 10.4 % | 1.34 |
| ні те, ні те | 53…68 % | — | — |

Мережа тисне на справжніх розворотах удвічі частіше за випадок, а на
корекціях — практично з випадковою частотою. **Корекції не є головним
джерелом хибних зубців**: більшість натисків стоїть у місцях, де немає
ні розвороту, ні корекції.

**Чи є сенс у силі натиску.** Те саме по зростаючому порогу:

| поріг | обнулень на 1000 | розворотів знайдено | підйом на розвороті |
| ----- | ---------------- | ------------------- | ------------------- |
| 0.30 | 71.4 | 63.1 % | 1.82 |
| 0.50 | 34.7 | 49.5 % | 1.91 |
| 0.70 | 11.9 | 30.1 % | 1.96 |
| 0.90 | 0.5 | 1.9 % | **0.94** |

Підйом стоїть на 1.9 від порога 0.3 до 0.7 і **падає до випадкового**
на 0.9. Найсильніші натиски мережі — шум. Підняти поріг не можна:
влучність не росте, а розвороти губляться пропорційно.

**Висота лінії на натиску** — і ось головне число:

| де натиск | медіана лінії |
| --------- | ------------- |
| на справжньому розвороті | **0.1313** |
| на корекції | 0.2234 |
| у порожньому місці | 0.1582 |

`AUC` висоти лінії як ознаки влучного натиску — **0.382**, тобто нижче
випадкового: там, де мережа тисне по ділу, лінія стоїть **нижче**, ніж
там, де вона тисне намарно. Пилка живе в діапазоні близько 0.15
замість 0…1: мережа тисне так часто (раз на 29 подій), що лінія просто
не встигає вирости.

### Що з цього випливає

1. Безштрафова зона хвоста має стояти **часткою ноги** (≈10 %), а не
   50 подіями: коротка нога зараз дістає 17 % безштрафових подій,
   довга — 4.6 %. Голова лишається в подіях.
2. Персональна межа за фічами додає небагато й тільки у формі `score`
   (62 % проти монетки); рецепт дослідження 31 у лоб тут не працює.
3. Мету «відрізняти кінець ноги від корекцій» **у лоб узяти не можна**:
   на корекціях глибших за 0.8 % вхід не містить різниці. Та й зайві
   зубці стоять не на корекціях.
4. Єдина ознака, яка могла б розділити кінець і корекцію, — **де в
   нозі** стоїть натиск, і носій цієї ознаки — висота власної лінії.
   Зараз вона мовчить (`AUC` 0.382), бо лінія не піднімається. Тому
   першим ділом треба збити частоту натиску до одного на ногу, і лише
   тоді висота лінії стане тим, до чого можна прив'язати ціну.


## Одне обнулення на ногу

Статус: **підтверджено, 2026-09-17**.

Етап 9 лишив мережу з 34.7 обнуленнями на 1000 подій там, де справжніх
розворотів 2.5. Тут перевіряється, чи можна збити цю частоту ціною у
функції втрат.

Спершу — **звідки береться спрей**. Порахуймо, що коштує кожна
стратегія у втраті етапу 8:

    мовчати після розвороту   до 0.5 × 81 = **40 за подію**, і так
                              до 150 подій поспіль
    хибно обнулитись          один спуск ≈ 0.13 висоти × 120 = **15**,
                              разово

Тиснути часто просто дешевше, ніж чекати. Заодно зона стала часткою
ноги (9.5 %, у межах 15…150 подій) — за етапом 9.

### Три механізми ціни, і два з них не працюють

[`scripts/rate_train.py`](scripts/rate_train.py), донавчання з ваг
`legzone`. Драбина ручок іде по епохах: одним числом з початку мережа
просто замовкає.

**Рівна ціна за натиск** — `price × pull` на кожній події поза зоною:

| епоха | ціна | розмах лінії | обнулень/1000 |
| ----- | ---- | ------------ | ------------- |
| 1 | 2.0 | **0.156** | 33.6 |
| 14 | 14.0 | 0.026 | 23.7 |
| 26 | 55.4 | **−0.011** | 28.2 |

Ціна виросла у 28 разів — частота стоїть, розмах у нулі. Причина
механічна: лінійна ціна керує **сумою** натиску, а не його кількістю, і
найдешевша відповідь на неї — стиснути всі зубці нарівно. Пилка стає
рівною смугою. Результат лишається як доказ: `results/rate_flat.json`.

**Бюджет ноги** — натиски складаються в межах своєї ноги (нога ділиться
за подіями народження), одне обнулення безплатне, перебір коштує
`price × (сума − 1)²`:

| | обнулень/1000 | розмах |
| --- | ------------- | ------ |
| етап 8 | 34.7 | 0.154 |
| рівна ціна | 23.7 | 0.026 |
| бюджет | **16.9** | 0.106 |

Бюджет прибирає зайві зубці й **не гладить решту** — це видно з того,
що розмах тримається. Але він упирається в рівновагу: ціна перебору
доходить до 88, частота стоїть на 17–21.

**Рівновага з мовчанням.** Бюджет тисне частоту вниз, а доданок
«мовчання після розвороту» — вгору. Тому третій прогін веде вниз і
його: `LATE` іде драбиною 0.5 → 0.02 разом із ростом ціни перебору.

### Драбина мусить пройти весь хід

Перші три прогони спинялись на 18-й епосі: терпіння 12 обривало їх,
коли драбина пройшла третину. Для драбинного прогону це неправильно —
сенс саме в тому, щоб ручки дійшли до верху, а краща епоха й так
зберігається окремо. Ключ `--patience 0` вимикає зупинку.

Повний прогін, 45 епох, обидві драбини до кінця:

| епоха | ціна перебору | ціна мовчання | обнулень/1000 | розмах |
| ----- | ------------- | ------------- | ------------- | ------ |
| 1 | 5 | 0.500 | 23.0 | 0.145 |
| 12 | 40 | 0.312 | 16.9 | 0.053 |
| 29 | 355 | 0.091 | **16.3** | 0.003 |
| 45 | 2000 | 0.020 | 21.5 | −0.013 |

**Ціна перебору виросла у 400 разів, ціна мовчання впала у 25 — частота
не зрушила.** Дно за весь прогін 16.3 при цілі 2.5, і розмах при цьому
з'їдається до нуля.

Висновок етапу: частота обнулень не є властивістю балансу втрати. Одна
голова, яка водночас шукає розворот і веде лінію, розмінює одне на
одне, і тиснути на цей розмін ручками більше нема куди.

## Дві мережі замість однієї

Статус: **підтверджено, 2026-09-17**.

Якщо одна голова не тягне обидві задачі, їх треба розділити:

    мережа зубців   каже, **де** кінець ноги — один сигнал на ногу
    мережа лінії    веде частку пройденої ноги й обнуляється чужим
                    сигналом, сама рішень про обнулення не приймає

### Готового сигналу на роль зубців немає

[`scripts/tooth_check.py`](scripts/tooth_check.py) міряє кандидатів
**однією лінійкою**: та сама стрічка val, та сама рамка (рухомі вікна,
розмітка забілена), і для кожної цілі «скільки обнулень на 1000 подій»
береться поріг, який саме стільки й дає.

При **одному обнуленні на ногу** (2.5 на 1000):

| кандидат | `AUC` зони | підйом | розворотів із сигналом | без сигналу |
| -------- | ---------- | ------ | ---------------------- | ----------- |
| `legzone` (етап 8) | **0.657** | 2.31 | 10.7 % | 59 % |
| `legrate` (етап 10) | 0.653 | 1.71 | 8.7 % | 54 % |
| `legtip` (дослідження 29) | 0.640 | **2.37** | 5.8 % | 88 % |

`legtip` не кращий за те, що вже є: при такій частоті він мовчить на
88 % розворотів. Усі троє — той самий слабкий детектор.

### Мережа зубців: позитив максимумом

[`scripts/tooth_train.py`](scripts/tooth_train.py). Ціль формулюється
інакше, ніж у `legtip`:

    позитив   найгучніша подія зони має бути гучною — **одна** на ногу
    негатив   кожна подія поза зоною має бути тихою — щільно

Максимум і є «натисни один раз»: коли одна подія зони вже гучна,
градієнт на решту подій зони не йде, і сигнал не розмазується на всі
сорок. Щільний негатив змушує платити за кожен зайвий натиск окремо.

Поруч — драбина на те, **де** в зоні лежить сигнал. Відстань тут
зважується самим сигналом, а не береться від найближчої події зони:
сам півот у зоні завжди, тому міра «найближча подія» дорівнює нулю на
кожній нозі й не каже нічого. Першу версію цього доданка саме так і
було написано, і тест це виявив — драбина місця тоді не працювала
зовсім.

Рамка — рухомі вікна, а не ноги-блоки: нарізка по ногах сама підказує
мережі, де кінець (етап 5).

Поруч із мірою при спільному порозі стоїть міра, яка відповідає задачі
буквально: **беремо найгучнішу подію кожної ноги** й дивимось, чи
влучила вона в зону. Спільний поріг тут вводить в оману — на легкій
нозі мережа тисне тричі, на важкій жодного разу.

45 епох, краща 12-та:

| число | значення | випадково |
| ----- | -------- | --------- |
| ніг, де найгучніша подія в зоні | **31.4 %** | 9.5 % |
| `AUC` зони | 0.669 | 0.5 |
| зсув найгучнішої події | −78 подій | — |
| розкид зсуву | 78 подій | — |

Це найкращі зубці в проєкті — утричі краще за випадок. Але `AUC`
уперлась у ту саму стелю 0.65–0.67, що й у всіх інших.

Полагоджена драбина місця результату **не змінила**: до полагодження
було 33.3 % при `AUC` 0.655, після — 31.4 % при 0.669, і зсув той самий
−78 подій. Доданок, який тягне сигнал до півота, не тягне його туди
навіть коли працює правильно: ознаки не підтримують пізнішого
розміщення.

### Зсув зубця виправити не можна

Медіана зсуву −78 подій виглядає як стала поправка, але розкид це
заперечує: квартилі −287…−17, децилі −455…−6. Сталий зсув назад робить
**гірше**:

| поправка | ніг у зоні |
| -------- | ---------- |
| без поправки | **0.333** |
| +20 подій | 0.157 |
| +78 подій (медіана) | 0.108 |
| +10 % ноги | 0.108 |

Тобто 33 % зубців влучні, а решта розкидана по всій нозі, а не зміщена.

### Мережа лінії: падіння приходить ззовні

[`scripts/line_train.py`](scripts/line_train.py). У втраті не лишилось
жодного доданка про обнулення — ні драбини промаху, ні ціни за натиск,
ні бюджету. Лишилась сама форма, і хитрувати мережі нема чим: купити
коридор частими падіннями вона не може, бо падіннями не керує.

Поріг чужого сигналу ставиться **на ногах train** і той самий іде на
val. Тут вилізло окреме число: поріг, що дає 2.5 обнулення на 1000 на
train, дає **12.4 на val** — мережа зубців на train самовпевненіша.

40 епох, краща 6-та: міра 0.8126, **розмах 0.0722**, обвал 0, ріст
0.5177 (лінія росте вздовж ноги), голова 0.44…0.52.

Голова ноги не падає в нуль, і це не налаштування, а конструкція:
`fade` 0.35 рахувався під власний сигнал, що тривав десяток подій
поспіль. Чуже рішення — одна подія, і третина висоти лишає лінію на
двох третинах. Повний спуск (`fade` 1.0) голови теж не дав: обнулення
лягають не там, де треба, і лінія втрачає фазу.

### Що з цього випливає

1. Поділ **знімає конфлікт у втраті**: мережі більше не розмінюють
   частоту на форму.
2. Але **стелю він не знімає**. Розмах лінії обмежений точністю зубців:
   33 % влучних дають розмах 0.072 проти 0.62 в розміткового лічильника.
3. Усі шляхи впираються в те саме число: **`AUC` 0.65** про те, чи
   стоїть подія біля розвороту. Воно однакове в `legzone`, `legrate`,
   `legtip` і в спеціально навченій мережі зубців, при чотирьох різних
   функціях втрат і трьох рамках. Це не властивість втрати й не
   властивість архітектури — це те, скільки про кінець ноги говорить
   сам вхід.
4. Те саме з іншого боку вже казав етап 9: клас **не відрізняє**
   справжній кінець ноги від корекції 0.8 % і глибшої.


## Вкладка «Зона розвороту і драбина промаху»

[/legzone](http://localhost:8080/legzone) — три розділи:

| Розділ | Що показує |
| ------ | ---------- |
| Зона розвороту | крива `AUC` кошик за кошиком для обох боків ноги: скільки подій біля півота відрізняються від тіла ноги. Поруч із кожним кошиком — підлога на перемішаних мітках і надлишок над нею смужкою. Під кривими — межа зони в подіях і те саме у відсотках ноги, щоб було видно, чому відсоток не міра |
| Навчання драбиною промаху | епоха за епохою: втрата, міра-коридор, числа форми, частота обнулень, **влучність у зону**, затримка сигналу в подіях і частка розворотів без сигналу взагалі. Краща епоха підсвічена |
| Ряд val BTC | ціна BTC із зігзагом 2.3 % на ема10, під нею частка пройденої ноги на нерухомій шкалі 0…1. Кнопки перемикають дві конструкції: **одна голова** (правда, лічильник, мережа `legzone` і її сигнал «обнулись») і **дві мережі** (правда, лічильник, сира видача зубців, той самий сигнал порогом, лінія поділу і стара одноголова версія поруч). Під кнопками — числа зубців: скільки ніг мають найгучнішу подію в зоні, зсув і частота. У легенді кожна лінія іде з описом, звідки її число (див. нижче) |

Описи ліній ряду (`LEGZONE_TELLS` у `dashboard/static/app.js`) — назви
самої по собі мало, бо з неї не видно, чи бачила лінія відповідь:

| Лінія | Що це насправді |
| ----- | --------------- |
| насправді | ціль, не вхід: частка ноги за розміткою зігзага 2.3 % — `0` на першій події після розвороту, `1` на останній перед наступним; проводиться заднім числом, коли нога вже скінчилась |
| лічильник розмітки | межа знизу без мережі: лічильник подій від розвороту, поділений на самого себе плюс медіанний залишок train на цьому кроці (канал розмітки `leg:guess`); мережа цього каналу не бачить |
| мережа | видача одноголової мережі: та сама частка, зібрана пилкою з двох її чисел — «підрости» веде вгору, «обнулись» роняє; вниз лінія може піти лише власним сигналом |
| сигнал «обнулись» | власний сигнал скидання: наскільки сильно мережа цієї події тисне «обнулись»; кожен зубець угору — місце, де вона вважає, що нога скінчилась |
| зубці (сира видача) | окрема мережа зубців до порога: одна відповідь на подію, «наскільки це кінець ноги»; училась максимумом по нозі — один натиск на ногу, найгучніша подія |
| лінія поділу | мережа частки з поділу: веде лінію вгору і рішення про кінець ноги не приймає взагалі — роняє її чужий сигнал зубців, пропущений через поріг |
| одна голова | стара одноголова версія на тому самому ряді: сама шукає кінець і сама веде лінію; лежить поруч, щоб різниця від поділу була видна очима |
| Теза 1: зона стоїть у частці ноги | точка згасання надлишку нарізно на коротких, середніх і довгих ногах — у подіях і в частках ноги поруч, плюс сама крива надлишку по групах відстані. Розмах у подіях проти розмаху в частках — те, чим доводиться вибір осі |
| Теза 2: рецепт дослідження 31 не проходить перевірку | три способи рахувати несхожість, і в кожного рядка — **фальшива точка**: на скількох ногах межа з розвороту довша за межу з випадкової події тіла. Поруч стійкість до порога і звʼязок із довжиною ноги |
| Теза 3: хибні зубці стоять не на корекціях | клас проти корекції за глибиною з підлогою; куди лягли обнулення — з **покриттям** поруч із кожною часткою й підйомом над випадковим; те саме по силі натиску; і висота лінії на влучних натисках проти решти |
| Теза 4: частоту обнулень ціною не збити | три механізми ціни поруч — рівна, бюджет ноги, бюджет разом зі здешевленням мовчання: верх ціни, найменша частота за прогін і розмах лінії на кращій епосі. Нижче — повний хід драбини без ранньої зупинки |
| Теза 5: задачі поділено, окрема мережа зубців | кандидати на роль зубців однією лінійкою при одному обнуленні на ногу, потім крива навчання мережі зубців із мірою «найгучніша подія ноги — в зоні чи ні» і зсувом |
| Теза 6: поділ зняв конфлікт, але не стелю | голова, хвіст і розмах лінії проти самої правди й проти розміткового лічильника; поруч названо спільне число всіх спроб — `AUC` 0.65 |

Дані — `/api/legzone/marks` (замір зони), `/api/legzone/train` (крива
навчання), `/api/legzone/series` (ряд val BTC), `/api/legzone/length`
(зона по довжині ноги), `/api/legzone/own` (перевірка персональної
межі), `/api/legzone/false` (хибні розвороти), `/api/legzone/rate`
(частота обнулень трьома механізмами), `/api/legzone/tooth` (кандидати
й мережа зубців) `/api/legzone/line` (мережа частки) і `/api/legzone/split`
(ряд val BTC обома мережами поділу). Кожен розділ
незалежний: немає файлу — стоїть рядок «запустіть такий-то скрипт», а
решта вкладки працює.

Три останні розділи написані **тезами**: спершу твердження з числом,
під ним таблиця, якою воно доводиться. Правило одне — жодна частка не
стоїть без свого контролю: частка обнулень іде поруч із покриттям ряду,
`AUC` — поруч із підлогою на перемішаних мітках, довжина зони — поруч
із довжиною зони з фальшивої точки.


## Тести

[`tests/test_loss_ladder.py`](../../tests/test_loss_ladder.py) — рішення
драбини й формули щаблів на синтетиці: напрямок міри й мертва зона,
спадання кроку вниз по драбині, коридор `band` (всередині штрафу немає,
вниз дорожче, після вікна — модуль видачі), тиша й край `legtip`, вага
рідкої мітки й близькість до півота в `hazard`, умова допуску `zone`,
продовження драбини й **шум замірювання** — медіана по парах спроб,
щабель зі зламаним каналом у пару не йде, і виграшем рахується тільки
те, що вийшло за шум у бік своєї міри. Ряд val — окремо:
`TestValSeriesShape` (плашка на модель, лінія на горизонт, міра BTC
знаходиться хай як зветься потік) і `TestValSeriesJoin` (спільна вісь,
подія іншої сітки лягає на найближчу, пропуск лишається пропуском,
вікно обрізається по відповідях, плашка несе вердикт своєї драбини). Фальшивий півот — `TestFakePivot`: ноги-сусіди
зшиваються, через діру в ряді нарізка не йде, хвіст відрізка лишається
коротшим, памʼять бере попередні фальшиві ноги й не виходить за край,
відповідь лічильника читається з даних, підробляються рівно чотири
канали розмітки і жоден інший, а вікно міри лишається справжнім. Власна рамка — `TestSelfFrame`
(відрізки не зшивають дві монети, ціль лишається справжнім залишком,
кроки шматка беруться з уже поставлених меж, памʼять не виходить за
стелю, вікно закривається за власним лічильником, підміняються рівно
чотири канали розмітки, довжина ноги дорівнює середньому перших
відповідей і не виходить за межі відрізка) і `TestSelfPivotSeries`
(правило довжини та обидві відстані між півотами й межами). Частка
ноги — `TestShareTruth` (частка йде від нуля до одиниці в кожній нозі,
лічильник сам по собі вже дає частку, гасяться рівно чотири канали
розмітки й клас лишається цілим), `TestShareWindows` (вікна стикуються
подія в подію без дір і перекриттів, перші події відрізка тільки
розігрівають памʼять, короткий хвіст відрізка вікна не дає, довге вікно
саме зменшує батч, безштрафова зона звужується за весь прогін),
`TestShareStep` (не впасти там, де нога вже нова, дорожче за все інше;
ціна цього гасне з віддаллю від півота; не дорости до одиниці в хвості
дорого; спуск усередині ноги карається, а на самому півоті — ні; на
першій події вікна падати нема з чого), `TestShareSaw` (без власного
сигналу лінія тільки росте, власний сигнал — єдиний шлях униз, лінія не
виходить за нуль-одиницю, одна подія роняє лише частку висоти, мітка
покриває перші події ноги, пропустити півот дорожче за хибний сигнал,
обнулення рахуються проти справжніх півотів), `TestShareMarks` (точна
відповідь дає нуль, широкий коридор ловить те, що вузький пропустив,
події поза заміряним відрізком не рахуються, спуски рахуються нога за
ногою, форма ідеальної пилки читається числами, рівна лінія дає нуль
форми, оцінка платить за форму, а не лише за коридор),
`TestShareSeries` і `TestShareSawDead` (кволий сигнал лінію не точить,
а сильний роняє її так само, як і без мертвої зони).

Зона розвороту й драбина промаху — `TestPivotZoneSpots` (кошик тримає
події рівно на заданій відстані від краю, кошик і тіло ноги не мають
спільних подій, коротка нога не дає ні кошика, ні тіла, вибірка
тримається під стелею), `TestPivotZoneAuc` (повний поділ — одиниця,
дзеркальний — нуль, одне число на всіх — монетка),
`TestPivotZoneEdge` (зона кінчається там, де надлишок падає вдвічі
проти піка; немає різниці — немає зони; безштрафове вікно береться
одним порогом на обидва боки), `TestZoneLadder` (ціна множиться втричі
на кожні пʼять подій і впирається в стелю), `TestZoneSilence`
(мовчання стоїть одиницею, поки мережа не натиснула, і новий розворот
починає його заново), `TestZoneStep` (усередині заміряної зони
обнулення безкоштовне, мовчання після ноги дорожчає втричі за кожні
пʼять подій, вчасне натискання гасить увесь доданок, обнулення з
середини ноги — найдорожче «рано») і `TestZoneTiming` (кроки рахують
події до обох країв ноги, обнулення в зоні — влучання, півот без
жодного обнулення поруч — промах, влучність у зону входить у вибір
епохи).

[`tests/test_dashboard.py`](../../tests/test_dashboard.py) — вкладка:
`TestLadderTab` (ендпоінти, вердикт читається напрямком міри, видача
розбита по монетах), `TestLadderWithoutResults` (без прогонів не падає),
`TestLadderMarkup` (таблиці моделей, щаблів і монет),
`TestLadderSeriesTab` (ряд val: вісь, півоти, кожна лінія плашки лежить
у відповіді й має довжину осі, два заміри поруч),
`TestLadderSeriesWithoutResults` і `TestLadderSeriesMarkup` (полотно на
кожну модель, таблиця двох замірів, легенда), `TestLadderFakeTab`
(маршрут не є моделлю «fake», лінії лежать на спільній осі, вікно міри
одне на три постановки, межі фальшивих ніг стоять на подіях осі),
`TestLadderFakeWithoutResults` і `TestLadderFakeMarkup` (два полотна на
спільній осі, таблиця трьох постановок, легенда каже, чому міра гірша
механічно), `TestLadderSelfTab` (маршрут не є моделлю «self», лінії
на спільній осі, розмітка стоїть поруч із власною рамкою, кожна
постановка звітує покриття), `TestLadderSelfWithoutResults` і
`TestLadderSelfMarkup` (полотна, таблиця рамок, крива перенавчання,
правило рамки написане словами), `TestLadderShareTab` (маршрут не є
моделлю «share», лінії на спільній осі, видача не виходить за межі
нуля-одиниці, лічильник стоїть межею знизу, кожна лінія звітує форму),
`TestLadderShareWithoutResults` і `TestLadderShareMarkup` (полотна,
таблиця з числами форми, крива з безштрафовою зоною й оцінкою, ціна
«не впасти» названа словами), `TestLegzoneTab` (зона заміряна на обидва
боки й кожен кошик має підлогу, безштрафове вікно не зʼїдає ногу, кожна
епоха звітує влучність у зону, лінії ряду лежать на спільній осі),
`TestLegzoneWithoutResults` і `TestLegzoneMarkup` (зона написана в
подіях, а не у відсотках, підлога й надлишок стоять поруч із кожним
кошиком, драбина ×3 і мовчання названі словами, краща епоха
підсвічена).

Етап 9 — `TestZoneLengthBuckets` (нога потрапляє тільки в свій кошик за
довжиною, коротша за межу не потрапляє нікуди, група складає свої
кошики, одна шумна група посеред кривої не обриває зону, а дві поспіль
обривають, вердикт називає ту вісь, на якій кошики ближчі, кошик у
частках бере ту саму частку з кожної ноги), `TestZoneOwnBorder`
(зростання межі зупиняють `patience` звичайних подій поспіль, одна
спокійна подія — ні, стеля тримає зону всередині ноги, рівна нога дає
нульову зону, а не зону на всю ногу, сплеск у кінці знаходиться й
виходить довшим за зону з фальшивої точки, бюджет — це частка ноги,
рівна межа витрачає той самий бюджет, персональна бере свою довжину
кожній нозі, рангова кореляція знає порядок і не знає масштабу) і
`TestZoneFalseMarks` (монета тримає свій відрізок ряду, вікно перед
кандидатом причинне, зона — частка ноги плюс стала голова, обнулення на
півоті рахується влучним, посеред ноги — порожнім, обнулення біля
корекції йде разом зі своїм покриттям, і висота лінії відрізняє влучні
натиски від решти).

[`tests/test_dashboard.py`](../../tests/test_dashboard.py) — тези етапу 9:
`TestLegzoneStageNine` (зона по довжині віддає і події, і частку, і вони
сходяться; кожен спосіб персональної межі звітує фальшиву точку; кожне
вікно корекції йде разом зі своїм покриттям і підйом дорівнює їхньому
відношенню; кожна глибина має підлогу; без файлів розділи не падають) і
`TestLegzoneStageNineMarkup` (перша теза ставить події поруч із часткою,
друга називає фальшиву точку й монетку, третя показує покриття біля
кожної частки, висота лінії названа нижчою за випадкову, а теза про клас
каже, що корекцію глибшу за 0.8 % він не відрізняє).

Етапи 10 і 11 на вкладці: `TestLegzoneSplit` (відповідь про частоту несе
всі три механізми поруч і не падає, коли двох перших прогонів немає;
відповідь про зубці склеює лінійку з кривою навчання, а самої лінійки
досить, щоб вкладка не була порожньою) і `TestLegzoneSplitMarkup` (усі
три механізми названі словами, сказано дно частоти й хід драбини,
кандидати стоять на одній лінійці, зсув названий невиправним, лінія
порівняна з розмітковим лічильником, а стеля названа числом).

Перемикач ряду: `TestLegzoneSeriesSwitch` (сам по собі ряд однієї
конструкції дає одну кнопку, дві конструкції — дві, перша активна,
поділ відкривається й без старої версії, кожна лінія поділу названа, а
рядок зубців каже, скільки ніг влучено і з яким зсувом; кожна лінія
несе опис того, звідки взялося її число, і той самий опис висить
підказкою на таблиці; без жодного ряду блок просить запустити скрипт).

## Файл дослідження

[`research/32-loss-ladder/32-loss-ladder.md`](32-loss-ladder.md)
