forked from Dan4ick/Lidar_Muxa
резкость считывания 1.5: ложных на новой линии 23.5 -> 14.7 на км, память пересобрана на новом полу, new_data читается кусками из tar
This commit is contained in:
parent
6964e4cc4e
commit
28fd716a82
11 changed files with 474 additions and 55 deletions
24
README.md
24
README.md
|
|
@ -143,6 +143,19 @@ python tools/plot_benchmark.py # кривые и граф
|
||||||
python tools/compare_benchmark.py было.json стало.json # правка парно
|
python tools/compare_benchmark.py было.json стало.json # правка парно
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Большой бэг `new_data` (90 ГБ) не распаковывается целиком — инструменты
|
||||||
|
читают его кусками прямо из архива (`--tar`, по умолчанию
|
||||||
|
`../датасет/new_data` или `FLYGUARD_NEW_DATA`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python tools/make_training_set.py --new-data 0:110 --out data/cache/training_set_nd.npz
|
||||||
|
python tools/train_mbon.py --device cuda --data data/cache/training_set.npz --data data/cache/training_set_nd.npz --train-only new_data_ --out nd.npz
|
||||||
|
python tools/eval_new_data.py --shards 110: --readout было=artifacts/mbon_readout.npz --readout стало=nd.npz --out nd.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверка на второй половине честная: вставки идут только в первую, а
|
||||||
|
вторую модель не видит ни в каком виде (EXPERIMENTS п. 17.3).
|
||||||
|
|
||||||
Любое поле `Params` меняется без правки кода: `--set h_lo_core=0.28 --set
|
Любое поле `Params` меняется без правки кода: `--set h_lo_core=0.28 --set
|
||||||
k_sigma=0.75` у `make_benchmark.py`, `evaluate.py`, `make_training_set.py` и
|
k_sigma=0.75` у `make_benchmark.py`, `evaluate.py`, `make_training_set.py` и
|
||||||
`check_obstacle.py`. Два прогона полигона сравнивайте только парно —
|
`check_obstacle.py`. Два прогона полигона сравнивайте только парно —
|
||||||
|
|
@ -168,11 +181,12 @@ k_sigma=0.75` у `make_benchmark.py`, `evaluate.py`, `make_training_set.py` и
|
||||||
| Метрика | Значение | Чем измерено |
|
| Метрика | Значение | Чем измерено |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Реальный объект 0.67 × 1.35 м на 55 м | **99.5 %** кадров | `tools/check_obstacle.py` |
|
| Реальный объект 0.67 × 1.35 м на 55 м | **99.5 %** кадров | `tools/check_obstacle.py` |
|
||||||
| Ложные тревоги, leave-one-bag-out | **8.0 трека на км**, 13.8 % кадров | `tools/evaluate.py --mbon-dir` |
|
| Ложные тревоги, leave-one-bag-out | **5.7 трека на км**, 8.8 % кадров | `tools/evaluate.py --mbon-dir` |
|
||||||
| То же без обученного считывания | 13.6 на км, 28.1 % кадров | там же, без `--mbon-dir` |
|
| То же без обученного считывания | 11.9 на км, 20.3 % кадров | там же, без `--mbon-dir` |
|
||||||
| На незнакомой линии (памяти нет) | **23.5 на км**; без считывания 36.1 | `tools/evaluate.py --no-memory` |
|
| На незнакомой линии (памяти нет) | **14.7 на км**; без считывания 36.1 | `tools/evaluate.py --no-memory` |
|
||||||
| Дальность (полигон, 15 560 наблюдений) | человек стоя: рабочая дальность 80 м, P@50 = 0.71, P@100 = 0.48, **P@150 = 0.49** | `tools/plot_benchmark.py` |
|
| Вторая половина `new_data`: другой день, 3.27 км, не видена при обучении | **17.4 на км**, 31.4 % кадров | `tools/eval_new_data.py` |
|
||||||
| Человек, упавший на пути | P@50 = **0.50** (было 0.27), рабочая дальность 32 м | там же |
|
| Дальность (полигон, 15 560 наблюдений) | человек стоя: рабочая дальность **100 м**, P@50 = 0.71, P@100 = 0.50, P@150 = 0.46 | `tools/plot_benchmark.py` |
|
||||||
|
| Человек, упавший на пути | P@50 = **0.50** (было 0.27), рабочая дальность 20 м | там же |
|
||||||
| Обработка кадра | медиана 33 мс из бюджета 100 мс | `tools/run_pipeline.py` |
|
| Обработка кадра | медиана 33 мс из бюджета 100 мс | `tools/run_pipeline.py` |
|
||||||
| Разделение «знакомое / новое» (без учителя) | ROC AUC 0.905 | `tools/tune_memory.py` |
|
| Разделение «знакомое / новое» (без учителя) | ROC AUC 0.905 | `tools/tune_memory.py` |
|
||||||
| Считывание MBON «предмет / тоннель» (с учителем) | ROC AUC **0.986** | `tools/train_mbon.py` |
|
| Считывание MBON «предмет / тоннель» (с учителем) | ROC AUC **0.986** | `tools/train_mbon.py` |
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -3,31 +3,31 @@
|
||||||
"bag": "doubleT_obstacle",
|
"bag": "doubleT_obstacle",
|
||||||
"frames": 190,
|
"frames": 190,
|
||||||
"path_m": 0.0,
|
"path_m": 0.0,
|
||||||
"alarm_frames": 183,
|
"alarm_frames": 153,
|
||||||
"alarm_rate": 0.9631578947368421,
|
"alarm_rate": 0.8052631578947368,
|
||||||
"fp_objects": 522,
|
"fp_objects": 336,
|
||||||
"fp_tracks": 4,
|
"fp_tracks": 5,
|
||||||
"fp_per_km": NaN,
|
"fp_per_km": NaN,
|
||||||
"fp_median_d": 106.83192412328344,
|
"fp_median_d": 114.4002904954952,
|
||||||
"obj_rate": 0.9947368421052631,
|
"obj_rate": 0.9947368421052631,
|
||||||
"ms_p50": NaN,
|
"ms_p50": NaN,
|
||||||
"ms_p95": NaN,
|
"ms_p95": NaN,
|
||||||
"train_size": 36428
|
"train_size": 48221
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "doubleT_platform",
|
"bag": "doubleT_platform",
|
||||||
"frames": 239,
|
"frames": 239,
|
||||||
"path_m": 200.74905739412534,
|
"path_m": 200.74905739412534,
|
||||||
"alarm_frames": 48,
|
"alarm_frames": 31,
|
||||||
"alarm_rate": 0.200836820083682,
|
"alarm_rate": 0.1297071129707113,
|
||||||
"fp_objects": 66,
|
"fp_objects": 42,
|
||||||
"fp_tracks": 2,
|
"fp_tracks": 2,
|
||||||
"fp_per_km": 9.962686878640993,
|
"fp_per_km": 9.962686878640993,
|
||||||
"fp_median_d": 32.8633359224923,
|
"fp_median_d": 34.00916383990402,
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": NaN,
|
"ms_p50": NaN,
|
||||||
"ms_p95": NaN,
|
"ms_p95": NaN,
|
||||||
"train_size": 35491
|
"train_size": 46850
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "roundT_doubleT",
|
"bag": "roundT_doubleT",
|
||||||
|
|
@ -42,51 +42,51 @@
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": NaN,
|
"ms_p50": NaN,
|
||||||
"ms_p95": NaN,
|
"ms_p95": NaN,
|
||||||
"train_size": 35861
|
"train_size": 47438
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "roundT_pressureGate_roundT",
|
"bag": "roundT_pressureGate_roundT",
|
||||||
"frames": 239,
|
"frames": 239,
|
||||||
"path_m": 246.7637153487153,
|
"path_m": 246.7637153487153,
|
||||||
"alarm_frames": 25,
|
"alarm_frames": 18,
|
||||||
"alarm_rate": 0.10460251046025104,
|
"alarm_rate": 0.07531380753138076,
|
||||||
"fp_objects": 25,
|
"fp_objects": 18,
|
||||||
"fp_tracks": 2,
|
"fp_tracks": 1,
|
||||||
"fp_per_km": 8.104919303770778,
|
"fp_per_km": 4.052459651885389,
|
||||||
"fp_median_d": 160.95291512943237,
|
"fp_median_d": 160.20564869629789,
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": NaN,
|
"ms_p50": NaN,
|
||||||
"ms_p95": NaN,
|
"ms_p95": NaN,
|
||||||
"train_size": 36114
|
"train_size": 47718
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "roundT_squareT_pressureGate_squareT",
|
"bag": "roundT_squareT_pressureGate_squareT",
|
||||||
"frames": 239,
|
"frames": 239,
|
||||||
"path_m": 273.9439574444854,
|
"path_m": 273.9439574444854,
|
||||||
"alarm_frames": 6,
|
"alarm_frames": 4,
|
||||||
"alarm_rate": 0.02510460251046025,
|
"alarm_rate": 0.016736401673640166,
|
||||||
"fp_objects": 6,
|
"fp_objects": 4,
|
||||||
"fp_tracks": 2,
|
"fp_tracks": 1,
|
||||||
"fp_per_km": 7.3007633337022915,
|
"fp_per_km": 3.6503816668511457,
|
||||||
"fp_median_d": 161.27067444604893,
|
"fp_median_d": 162.18072351126122,
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": NaN,
|
"ms_p50": NaN,
|
||||||
"ms_p95": NaN,
|
"ms_p95": NaN,
|
||||||
"train_size": 35654
|
"train_size": 46546
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "squareT_platform_squareT_switch",
|
"bag": "squareT_platform_squareT_switch",
|
||||||
"frames": 239,
|
"frames": 239,
|
||||||
"path_m": 270.98139848673645,
|
"path_m": 270.98139848673645,
|
||||||
"alarm_frames": 86,
|
"alarm_frames": 52,
|
||||||
"alarm_rate": 0.3598326359832636,
|
"alarm_rate": 0.2175732217573222,
|
||||||
"fp_objects": 91,
|
"fp_objects": 56,
|
||||||
"fp_tracks": 4,
|
"fp_tracks": 3,
|
||||||
"fp_per_km": 14.761160811544727,
|
"fp_per_km": 11.070870608658545,
|
||||||
"fp_median_d": 96.62033146128638,
|
"fp_median_d": 90.95581811929335,
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": NaN,
|
"ms_p50": NaN,
|
||||||
"ms_p95": NaN,
|
"ms_p95": NaN,
|
||||||
"train_size": 33494
|
"train_size": 44039
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
Binary file not shown.
|
|
@ -299,6 +299,7 @@ $$q = \underbrace{\min\!\left(\frac{n_\text{лучей}}{n_\text{ожид}(d)},1
|
||||||
| `fov_deg` | шире сектор → лучше кривые и стрелки, дороже обработка |
|
| `fov_deg` | шире сектор → лучше кривые и стрелки, дороже обработка |
|
||||||
| `gain` / `leak` (CX) | быстрее подтверждение против устойчивости к шуму |
|
| `gain` / `leak` (CX) | быстрее подтверждение против устойчивости к шуму |
|
||||||
| `warn_evidence` | выше → меньше ложных тревог, позже обнаружение |
|
| `warn_evidence` | выше → меньше ложных тревог, позже обнаружение |
|
||||||
|
| `mbon_power` | вес наблюдения — вероятность обученного считывания в этой степени. Выше → строже: 1.0 → 1.5 срезает ложные на незнакомой линии с 23.5 до 14.7 на км ценой хвоста дальности за 130 м; 2.0 почти ничего не добавляет к ложным и теряет вдвое больше (EXPERIMENTS п. 17.4) |
|
||||||
| темп депрессии MB | выше → сильнее подавление знакомого, но риск заглушить и настоящее |
|
| темп депрессии MB | выше → сильнее подавление знакомого, но риск заглушить и настоящее |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -2420,4 +2420,181 @@ leave-one-bag-out.
|
||||||
собранных 19.09 при прежнем полу, и низких конструкций в колее не знает: на
|
собранных 19.09 при прежнем полу, и низких конструкций в колее не знает: на
|
||||||
знакомой линии их гасит только считывание. Выборку памяти надо собирать
|
знакомой линии их гасит только считывание. Выборку памяти надо собирать
|
||||||
проходом по 90 ГБ `new_data` — это следующий шаг, если понадобится ещё
|
проходом по 90 ГБ `new_data` — это следующий шаг, если понадобится ещё
|
||||||
снизить ложные на знакомой линии.
|
снизить ложные на знакомой линии. *Сделано — п. 17.1.*
|
||||||
|
|
||||||
|
## 17. После переноса: память на новом полу, край габарита, вторая половина `new_data`
|
||||||
|
|
||||||
|
Три хвоста п. 16, каждый мерился так же: полигон парно, ложные треки на двух
|
||||||
|
сценах, настоящий объект.
|
||||||
|
|
||||||
|
### 17.1. Память тоннеля на новом полу
|
||||||
|
|
||||||
|
Кэш кандидатов памяти был собран 19.09, при старом полу, и низких конструкций
|
||||||
|
между рельсами память не знала (п. 16.8). Пересобран целиком: шесть записей
|
||||||
|
(`tune_memory.py --collect-only`) и весь `new_data` (`stream_new_data.py`,
|
||||||
|
39 707 кандидатов против 30 902), память переобучена.
|
||||||
|
|
||||||
|
| | было | новая память |
|
||||||
|
|---|---:|---:|
|
||||||
|
| ложных треков/км, знакомая линия | 8.0 | **6.5** |
|
||||||
|
| кадров с тревогой | 13.8 % | **9.0 %** |
|
||||||
|
| посторонних на полигоне | 1226 | 1166 |
|
||||||
|
| полигон парно | | +78 / −95 |
|
||||||
|
| настоящий объект | 99.5 % | 99.5 % |
|
||||||
|
|
||||||
|
Незнакомой линии память не касается — там её нет по построению.
|
||||||
|
|
||||||
|
При резкости считывания 1.5, принятой в п. 17.4, разница между памятями
|
||||||
|
исчезает: на знакомой линии 5.7 и 5.7 ложных трека на км, полигон парно
|
||||||
|
+90 / −86, на записи с настоящим объектом кадров с посторонней тревогой
|
||||||
|
80.5 % против 96.3 %. Принята новая: она собрана на том же полу габарита,
|
||||||
|
что и детектор, и знает низкие конструкции между рельсами, которые детектор
|
||||||
|
теперь видит.
|
||||||
|
|
||||||
|
Все ближние потери — один сценарий: на `roundT_pressureGate_roundT` поезд
|
||||||
|
стоит в 14.1 м от сидящего человека или ящика у края габарита, рядом
|
||||||
|
гермозатвор, и предмет не подтверждается: 26 кадров подряд у сидящего, 10 у
|
||||||
|
ящика. Кандидат там —
|
||||||
|
слипшееся пятно (≈950 лучей, высота 0.40 м), считывание в нём не уверено
|
||||||
|
(p = 0.23–0.30), и держится он только на новизне: у старой памяти 0.27–0.28,
|
||||||
|
у новой 0.20–0.21. При нижнем пороге новизны 0.15 вес наблюдения падает в
|
||||||
|
2.4 раза, улика 0.36–0.39 против 0.04–0.05. Случай пограничный при любой
|
||||||
|
памяти, новая переводит его через край. У настоящего объекта новизна тоже
|
||||||
|
ниже (медиана 0.63 → 0.51), но выше 0.50, где множитель новизны уже равен
|
||||||
|
единице.
|
||||||
|
|
||||||
|
### 17.2. Край габарита у платформы
|
||||||
|
|
||||||
|
Шестнадцать ближних потерь после переобучения (п. 16.3) разобраны. У края
|
||||||
|
габарита (0.9 м) рядом с платформой нижняя часть человека слипается с
|
||||||
|
кромкой, и от кадра к кадру кандидат то слипшееся пятно (p ≈ 0.0–0.2 у любых
|
||||||
|
складок), то «верх»: обрывок 0.2 × 0.2 м на высоте 1.5–1.7 м, целиком в
|
||||||
|
габарите, с нулевым дефицитом до пола — по признакам навесное оборудование.
|
||||||
|
Такой верх старые складки оценивали в 0.74–0.99, новые — в 0.43–0.84. Улика
|
||||||
|
не добирает до порога 0.5, и решение приходит на 8.8–8.9 м вместо 10.7–17 м.
|
||||||
|
|
||||||
|
В среднем новые складки на ближних предметах почти так же уверены, как
|
||||||
|
старые (отложенные записи, 0–15 м: медиана p 0.993 против 0.996, доля
|
||||||
|
p ≥ 0.9 — 0.864 против 0.891), а уверенного фона на 60–120 м у них вшестеро
|
||||||
|
меньше (0.1 % против 0.7 %). Отсюда и падение ложных тревог: новое считывание
|
||||||
|
строже в целом и к нетипичному обрывку в частности.
|
||||||
|
|
||||||
|
Попытка лечения — положение 0.9 м в обучающей выборке: полигон его проверяет,
|
||||||
|
а в выборке были только 0, ±0.6 и ±1.2 м. Выборка 466 715 кандидатов, AUC
|
||||||
|
0.9849. **Край это не вылечило**: человек стоя на 0–15 м +5 / −5, сидя
|
||||||
|
+0 / −4. Зато считывание стало строже к фону:
|
||||||
|
|
||||||
|
| | было | с ±0.9 |
|
||||||
|
|---|---:|---:|
|
||||||
|
| ложных треков/км, незнакомая линия | 23.5 | **21.9** |
|
||||||
|
| то же, знакомая (новая память) | 6.5 | 6.5 |
|
||||||
|
| посторонних на полигоне | 1226 | 1175 |
|
||||||
|
| полигон парно | | +9 / −25 |
|
||||||
|
| настоящий объект | 99.5 % | 99.5 % |
|
||||||
|
|
||||||
|
На второй половине `new_data` (п. 17.3) — ничего: 23.9 и 23.9 ложных трека
|
||||||
|
на км. Не принято.
|
||||||
|
|
||||||
|
### 17.3. Считывание, обученное на `new_data`
|
||||||
|
|
||||||
|
Пять коротких записей сняты в один день (02.09) и вместе дают чуть больше
|
||||||
|
километра пути. `new_data` — двадцать минут непрерывной езды 17.09: 11 271
|
||||||
|
кадр в 221 шарде, 90 ГБ несжатого tar. Места на диске меньше архива, поэтому
|
||||||
|
он читается кусками прямо из tar: `tools/_new_data.py` распаковывает по пять
|
||||||
|
шардов во временный каталог и удаляет за собой.
|
||||||
|
|
||||||
|
Делить пришлось по времени, а не по кадрам: вставки в первую половину
|
||||||
|
(шарды 0–109) идут в обучение, ложные тревоги меряются на второй (шарды
|
||||||
|
110–220, 3.27 км пути), которую модель не видела ни в каком виде. Памяти
|
||||||
|
тоннеля в этой проверке нет, как на незнакомой линии. Выборка — прежние
|
||||||
|
333 739 кандидатов плюс 827 314 из 22 кусков первой половины, предметов
|
||||||
|
5.4 %, AUC складок 0.981.
|
||||||
|
|
||||||
|
| считывание | вторая половина `new_data`, ложных/км | кадров с тревогой | незнакомая линия | знакомая линия |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| прежнее | 23.9 | 38.5 % | 23.5 | 6.5 |
|
||||||
|
| с ±0.9 (п. 17.2) | 23.9 | 39.0 % | 21.9 | 6.5 |
|
||||||
|
| с `new_data` | **13.2** | **20.2 %** | **13.2** | 6.2 |
|
||||||
|
|
||||||
|
Ложных тревог почти вдвое меньше, и прокси незнакомой линии совпал с
|
||||||
|
честной проверкой. Но полигон парно — +39 / −259: у стоящего человека P@50
|
||||||
|
0.71 → 0.62, у лежачего P@100 0.22 → 0.11, у ящика 0.53 → 0.40.
|
||||||
|
|
||||||
|
Воспроизводится так:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python tools/make_training_set.py --new-data 0:110 --out data/cache/training_set_nd.npz
|
||||||
|
python tools/train_mbon.py --device cuda --data data/cache/training_set.npz --data data/cache/training_set_nd.npz --train-only new_data_ --save-folds artifacts/mbon_folds_nd --out artifacts/mbon_readout_nd.npz
|
||||||
|
python tools/eval_new_data.py --shards 110: --readout было=artifacts/mbon_readout.npz --readout nd=artifacts/mbon_readout_nd.npz --out nd_eval.json
|
||||||
|
```
|
||||||
|
|
||||||
|
`--train-only new_data_` — куски `new_data` только учат и складок не
|
||||||
|
получают: проверяются по-прежнему пять записей.
|
||||||
|
|
||||||
|
### 17.4. Строже или умнее: сравнение при равной строгости
|
||||||
|
|
||||||
|
Модель, которая вдвое реже тревожится и заметно реже видит предмет, может
|
||||||
|
быть не лучше, а просто строже. Это проверяется сравнением при одинаковой
|
||||||
|
строгости, и ручка для неё есть с п. 11: `mbon_power`, вес наблюдения
|
||||||
|
p^power. Все строки — с новой памятью (п. 17.1).
|
||||||
|
|
||||||
|
| считывание, резкость | вторая половина `new_data` | незнакомая | знакомая | посторонних на полигоне | полигон парно к первой строке |
|
||||||
|
|---|---:|---:|---:|---:|---:|
|
||||||
|
| прежнее, 1.0 | 23.9 | 23.5 | 6.5 | 1166 | |
|
||||||
|
| **прежнее, 1.5** | 17.4 | 14.7 | **5.7** | **793** | **+1 / −142** |
|
||||||
|
| прежнее, 2.0 | 15.0 | 14.3 | 5.7 | 628 | +0 / −268 |
|
||||||
|
| `new_data`, 1.0 | **13.2** | **13.2** | 6.2 | 822 | +39 / −259 |
|
||||||
|
| `new_data`, 0.7 | 24.8 | 16.4 | 7.7 | 927 | +72 / −184 |
|
||||||
|
|
||||||
|
* **Больше половины выигрыша — строгость.** Прежнее считывание при
|
||||||
|
резкости 1.5 срезает ложные на незнакомой линии с 23.5 до 14.7, а на
|
||||||
|
полигоне теряет 142 наблюдения из 7 600: 39 вблизи у края габарита (тот же
|
||||||
|
пограничный случай, что в п. 17.1–17.2) и 82 за 90 м.
|
||||||
|
* **Но не весь.** При сопоставимых ложных на второй половине `new_data`
|
||||||
|
(15.0 и 13.2) модель на `new_data` теряет на полигоне меньше, чем прежняя
|
||||||
|
при 2.0 (−220 против −268 в сумме). Другой день записи действительно
|
||||||
|
научил её тому, чего в пяти записях нет. Ослаблять её бесполезно: при 0.7
|
||||||
|
ложные возвращаются к 24.8.
|
||||||
|
* **Между «прежнее, 1.5» и «`new_data`, 1.0» — размен, а не доминирование.**
|
||||||
|
Вторая на четверть реже тревожится на данных своего дня, первая держит
|
||||||
|
дальность: P@50 у стоящего 0.71 против 0.62, P@100 у лежачего 0.22 против
|
||||||
|
0.11, у ящика 0.50 против 0.40. На пяти записях без памяти, которых при
|
||||||
|
проверке не видела ни одна из двух, они почти равны (14.7 и 13.2), на
|
||||||
|
знакомой линии прежняя лучше (5.7 и 6.2).
|
||||||
|
|
||||||
|
**Принято: прежнее считывание, резкость 1.5.** ТЗ просит баланс дальности и
|
||||||
|
ложных тревог, постановщик — прежде всего не видеть того, чего нет; резкость
|
||||||
|
1.5 даёт второе почти без цены в первом. Модель на `new_data` остаётся
|
||||||
|
рецептом из п. 17.3: если появятся ещё записи других дней, учить на них
|
||||||
|
выгоднее, чем ужесточать.
|
||||||
|
|
||||||
|
### 17.5. Итог
|
||||||
|
|
||||||
|
Новая память (п. 17.1), прежнее считывание, резкость 1.5 (п. 17.4) — против
|
||||||
|
опубликованного 23.09:
|
||||||
|
|
||||||
|
| | 23.09 | сейчас |
|
||||||
|
|---|---:|---:|
|
||||||
|
| ложных треков/км, знакомая линия | 8.0 (13.8 % кадров) | **5.7** (8.8 %) |
|
||||||
|
| то же, незнакомая линия | 23.5 (34.6 %) | **14.7** (25.9 %) |
|
||||||
|
| то же, вторая половина `new_data` | 23.9 (38.5 %) | **17.4** (31.4 %) |
|
||||||
|
| посторонних тревог на полигоне | 1226 | **793** |
|
||||||
|
| настоящий объект | 99.5 % | 99.5 % |
|
||||||
|
| человек стоя: рабочая дальность; P@50 / P@100 / P@150 | 80 м; 0.71 / 0.48 / 0.49 | **100 м**; 0.71 / 0.50 / 0.46 |
|
||||||
|
| человек сидя | 148 м; 0.67 / 0.52 / 0.49 | 122 м; 0.67 / 0.54 / 0.39 |
|
||||||
|
| человек лёжа | 32 м; 0.50 / 0.22 / 0.02 | 20 м; 0.50 / 0.22 / 0.00 |
|
||||||
|
| ящик | 20 м; 0.54 / 0.50 / 0.26 | 20 м; 0.57 / 0.50 / 0.22 |
|
||||||
|
| чемодан | 80 м; 0.61 / 0.45 / 0.24 | 80 м; 0.61 / 0.46 / 0.24 |
|
||||||
|
|
||||||
|
Ложных тревог примерно на треть меньше на всех трёх сценах, ближняя и
|
||||||
|
средняя дальность на месте. Цена — хвост за 130 м у сидящего человека
|
||||||
|
(P@150 0.49 → 0.39) и рабочая дальность лежачего 32 → 20 м: доля в полосе
|
||||||
|
20–32 м у него ходит у самого порога 0.5, и сдвинула её новая память, а не
|
||||||
|
резкость (P@50 тот же, 0.50). Без обученного считывания при тех же
|
||||||
|
настройках было бы 11.9 и 36.1 ложных трека на км.
|
||||||
|
|
||||||
|
Отдельно найдено при проверке сдачи: образ Docker **не загружал**
|
||||||
|
считывание — `mbon_path` был пустым, файла модели в образе не было, и узел
|
||||||
|
молча работал на ручной формуле. Все цифры выше замерены со считыванием,
|
||||||
|
образ теперь тоже с ним (`docker/Dockerfile`, `detect.launch.py`), а
|
||||||
|
дымовой тест образа проверяет, что модель на месте.
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,13 @@ class Params:
|
||||||
# веса улики в центральном комплексе там, где модель передана конвейеру;
|
# веса улики в центральном комплексе там, где модель передана конвейеру;
|
||||||
# без модели всё работает по-прежнему.
|
# без модели всё работает по-прежнему.
|
||||||
enable_mbon: bool = True
|
enable_mbon: bool = True
|
||||||
mbon_power: float = 1.0 # резкость: p**power перед смешиванием
|
# Резкость: вес наблюдения p**power. Рабочая точка 1.5 выбрана сравнением
|
||||||
|
# при равной строгости (EXPERIMENTS п. 17.4): ложных треков на незнакомой
|
||||||
|
# линии 23.5 → 14.7 на км, на знакомой 6.5 → 5.7, на второй половине
|
||||||
|
# new_data 23.9 → 17.4, а полигон теряет 142 наблюдения из 7 600: 39
|
||||||
|
# вблизи у края габарита (пограничный случай п. 17.1–17.2) и 82 за 90 м.
|
||||||
|
# 2.0 срезает ещё 127 наблюдений ради 0.4 трека на км.
|
||||||
|
mbon_power: float = 1.5
|
||||||
mbon_blend: float = 1.0 # 1 — только модель, 0 — только ручная формула
|
mbon_blend: float = 1.0 # 1 — только модель, 0 — только ручная формула
|
||||||
# Гашение знакомости с дальностью: за `nov_fade_from` её вклад падает
|
# Гашение знакомости с дальностью: за `nov_fade_from` её вклад падает
|
||||||
# до нуля к `nov_fade_to`. За 90 м новизна измеримо ВРЕДНА — у
|
# до нуля к `nov_fade_to`. За 90 м новизна измеримо ВРЕДНА — у
|
||||||
|
|
|
||||||
72
tools/_new_data.py
Normal file
72
tools/_new_data.py
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
"""Большой бэг `new_data` кусками прямо из tar-архива.
|
||||||
|
|
||||||
|
Это 90 ГБ в 221 шарде, а свободного места на диске меньше, чем весь архив.
|
||||||
|
Кусок из нескольких подряд идущих шардов распаковывается во временный
|
||||||
|
каталог, читается как обычный многошардовый бэг (`flyguard.bag.Bag`) и
|
||||||
|
удаляется. Шард достаётся по смещению в архиве, без повторного разбора
|
||||||
|
заголовков, поэтому куски можно распаковывать из нескольких процессов сразу.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Архив лежит там, куда его положили при скачивании датасета, а не в data/:
|
||||||
|
# распаковывать его целиком некуда. Путь переопределяется FLYGUARD_NEW_DATA
|
||||||
|
# или ключом --tar у инструментов.
|
||||||
|
DEFAULT_TAR = os.environ.get(
|
||||||
|
"FLYGUARD_NEW_DATA",
|
||||||
|
str(Path(__file__).resolve().parents[1] / "датасет" / "new_data"))
|
||||||
|
SHARD_RE = re.compile(r"_(\d+)\.db3$")
|
||||||
|
|
||||||
|
|
||||||
|
def shards(tar_path: str | Path) -> list[tuple[int, str, int, int]]:
|
||||||
|
"""(номер, имя файла, смещение данных, размер) всех шардов, по номеру."""
|
||||||
|
out = []
|
||||||
|
with tarfile.open(tar_path, "r:") as t:
|
||||||
|
for m in t:
|
||||||
|
mm = SHARD_RE.search(m.name)
|
||||||
|
if mm and m.isfile():
|
||||||
|
out.append((int(mm.group(1)), Path(m.name).name,
|
||||||
|
m.offset_data, m.size))
|
||||||
|
out.sort()
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def pick(members: list, spec: str) -> list:
|
||||||
|
"""Шарды по срезу номеров: `110:` — со 110-го до конца, `0:110` — первые 110."""
|
||||||
|
lo, _, hi = spec.partition(":")
|
||||||
|
lo_i = int(lo) if lo else 0
|
||||||
|
hi_i = int(hi) if hi else None
|
||||||
|
return [m for m in members if m[0] >= lo_i and (hi_i is None or m[0] < hi_i)]
|
||||||
|
|
||||||
|
|
||||||
|
def split(members: list, per_chunk: int) -> list[list]:
|
||||||
|
"""Подряд идущие шарды группами: каждая группа — одна «запись»."""
|
||||||
|
return [members[i:i + per_chunk] for i in range(0, len(members), per_chunk)]
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def chunk(tar_path: str | Path, members: list, workdir: str | None = None):
|
||||||
|
"""Распаковать шарды во временный каталог, отдать его путь, потом удалить."""
|
||||||
|
d = Path(tempfile.mkdtemp(prefix="fg_nd_", dir=workdir))
|
||||||
|
try:
|
||||||
|
with open(tar_path, "rb") as src:
|
||||||
|
for _, name, off, size in members:
|
||||||
|
src.seek(off)
|
||||||
|
with open(d / name, "wb") as dst:
|
||||||
|
left = size
|
||||||
|
while left:
|
||||||
|
buf = src.read(min(left, 1 << 22))
|
||||||
|
if not buf:
|
||||||
|
raise OSError(f"архив обрезан на {name}")
|
||||||
|
dst.write(buf)
|
||||||
|
left -= len(buf)
|
||||||
|
yield d
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(d, ignore_errors=True)
|
||||||
104
tools/eval_new_data.py
Normal file
104
tools/eval_new_data.py
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
"""Ложные тревоги на участке `new_data`, которого считывание не видело.
|
||||||
|
|
||||||
|
Прокси незнакомой линии в `evaluate.py --no-memory` — пять коротких записей,
|
||||||
|
вместе чуть больше километра пути, и все они сняты в один день. `new_data` —
|
||||||
|
двадцать минут непрерывной езды другим днём. Если учить считывание на
|
||||||
|
вставках в первую половину, а ложные тревоги мерить на второй, проверка
|
||||||
|
остаётся честной при любом ответе на вопрос, та ли это линия: второй половины
|
||||||
|
модель не видела ни в каком виде.
|
||||||
|
|
||||||
|
Вторая половина идёт одним непрерывным потоком — состояние конвейера (поза,
|
||||||
|
ось, треки) переносится между кусками, как в `stream_new_data.py`. Памяти
|
||||||
|
тоннеля нет. Несколько вариантов считывания гоняются по одним и тем же
|
||||||
|
кадрам, поэтому разница между ними — только от считывания. Вариант может
|
||||||
|
нести и свои настройки конвейера через `@`: так сравниваются модели при
|
||||||
|
одинаковой строгости (`@mbon_power=2`), а не только при умолчаниях.
|
||||||
|
|
||||||
|
python tools/eval_new_data.py --shards 110: \\
|
||||||
|
--readout было=artifacts/mbon_readout.npz --readout стало=new.npz \\
|
||||||
|
--readout строже=artifacts/mbon_readout.npz@mbon_power=2 \\
|
||||||
|
--out nd_eval.json
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
import _bootstrap as B # noqa: F401
|
||||||
|
import _new_data as ND
|
||||||
|
import _params as PS
|
||||||
|
from flyguard.bag import Bag
|
||||||
|
from flyguard.mbon_readout import MbonReadout
|
||||||
|
from flyguard.pipeline import FlyGuard, Params
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
|
ap.add_argument("--tar", default=ND.DEFAULT_TAR)
|
||||||
|
ap.add_argument("--shards", default="110:",
|
||||||
|
help="номера шардов срезом; по умолчанию вторая половина")
|
||||||
|
ap.add_argument("--per-chunk", type=int, default=5,
|
||||||
|
help="сколько шардов распаковывать за раз")
|
||||||
|
ap.add_argument("--readout", action="append", default=[],
|
||||||
|
metavar="ИМЯ=ПУТЬ[@ПОЛЕ=ЗНАЧЕНИЕ...]",
|
||||||
|
help="вариант считывания, после @ — свои поля Params; можно несколько")
|
||||||
|
ap.add_argument("--out", required=True)
|
||||||
|
PS.add_argument(ap)
|
||||||
|
args = ap.parse_args()
|
||||||
|
if not args.readout:
|
||||||
|
raise SystemExit("нужен хотя бы один --readout ИМЯ=ПУТЬ")
|
||||||
|
|
||||||
|
members = ND.pick(ND.shards(args.tar), args.shards)
|
||||||
|
pipes, stats = {}, {}
|
||||||
|
for item in args.readout:
|
||||||
|
name, _, rest = item.partition("=")
|
||||||
|
path, *own = rest.split("@")
|
||||||
|
params = Params(**PS.apply({}, args.set + own, Params))
|
||||||
|
pipes[name] = FlyGuard(params, memory=None, readout=MbonReadout.load(path))
|
||||||
|
stats[name] = dict(frames=0, path_m=0.0, back_m=0.0, alarm_frames=0,
|
||||||
|
fp_objects=0, tracks=set())
|
||||||
|
print(f"шарды {members[0][0]}–{members[-1][0]} ({len(members)} шт.), "
|
||||||
|
f"вариантов считывания {len(pipes)}")
|
||||||
|
|
||||||
|
t0 = time.time()
|
||||||
|
for part in ND.split(members, args.per_chunk):
|
||||||
|
with ND.chunk(args.tar, part) as d:
|
||||||
|
for _, pc in Bag(d).frames():
|
||||||
|
for name, fg in pipes.items():
|
||||||
|
res = fg.process(pc)
|
||||||
|
if res is None:
|
||||||
|
continue
|
||||||
|
s = stats[name]
|
||||||
|
s["frames"] += 1
|
||||||
|
ds = res.ego.ds if res.ego else 0.0
|
||||||
|
s["path_m"] += ds
|
||||||
|
s["back_m"] += max(-ds, 0.0)
|
||||||
|
objs = res.decision.objects
|
||||||
|
if objs:
|
||||||
|
s["alarm_frames"] += 1
|
||||||
|
s["fp_objects"] += len(objs)
|
||||||
|
s["tracks"].update(o.track_id for o in objs)
|
||||||
|
line = " | ".join(f"{n}: {len(s['tracks'])} треков на {s['path_m'] / 1000:.2f} км"
|
||||||
|
for n, s in stats.items())
|
||||||
|
print(f" шарды до {part[-1][0]:3d}: {line} | {(time.time() - t0) / 60:5.1f} мин",
|
||||||
|
flush=True)
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for name, s in stats.items():
|
||||||
|
km = max(s["path_m"] / 1000.0, 1e-6)
|
||||||
|
rows.append(dict(readout=name, frames=s["frames"], path_m=s["path_m"],
|
||||||
|
back_m=s["back_m"], alarm_frames=s["alarm_frames"],
|
||||||
|
alarm_rate=s["alarm_frames"] / max(s["frames"], 1),
|
||||||
|
fp_objects=s["fp_objects"], fp_tracks=len(s["tracks"]),
|
||||||
|
fp_per_km=len(s["tracks"]) / km))
|
||||||
|
print(f"{name:12s} кадров {s['frames']:6d}, путь {s['path_m'] / 1000:6.2f} км "
|
||||||
|
f"(назад {s['back_m']:.0f} м) | кадров с тревогой {rows[-1]['alarm_rate']:6.1%} | "
|
||||||
|
f"ложных треков {len(s['tracks']):4d} = {rows[-1]['fp_per_km']:5.1f} на км")
|
||||||
|
with open(args.out, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(rows, f, ensure_ascii=False, indent=1)
|
||||||
|
print("сохранено:", args.out)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -23,6 +23,11 @@
|
||||||
независимой проверкой того, что модель выучила предмет, а не «синтетику».
|
независимой проверкой того, что модель выучила предмет, а не «синтетику».
|
||||||
|
|
||||||
python tools/make_training_set.py --out data/cache/training_set.npz
|
python tools/make_training_set.py --out data/cache/training_set.npz
|
||||||
|
|
||||||
|
Выборку можно собрать и из большого бэга `new_data`, кусками прямо из архива
|
||||||
|
(`--new-data 0:110` — первые 110 шардов, по пять на «запись»): двадцать
|
||||||
|
минут езды другим днём дают считыванию обстановку, которой нет в пяти
|
||||||
|
коротких записях (EXPERIMENTS п. 17).
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
@ -31,6 +36,7 @@ import argparse
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import _bootstrap as B # noqa: F401
|
import _bootstrap as B # noqa: F401
|
||||||
|
import _new_data as ND
|
||||||
import _parallel as P
|
import _parallel as P
|
||||||
import _params as PS
|
import _params as PS
|
||||||
from flyguard.bag import Bag, find_bags
|
from flyguard.bag import Bag, find_bags
|
||||||
|
|
@ -43,9 +49,14 @@ MIN_OVERLAP = 0.5 # доля лучей ядра, пришедш
|
||||||
|
|
||||||
|
|
||||||
def _work(task):
|
def _work(task):
|
||||||
"""Одна задача — один бэг. Верхнего уровня: иначе не передать в процесс."""
|
"""Одна задача — один бэг или кусок `new_data`. Верхнего уровня: иначе не
|
||||||
path, params, limit, d_starts, laterals, seed = task
|
передать в процесс. Кусок распаковывается внутри воркера и удаляется за ним."""
|
||||||
return collect_bag(path, params, limit, d_starts, laterals, seed)
|
src, params, limit, d_starts, laterals, seed = task
|
||||||
|
if isinstance(src, tuple):
|
||||||
|
tar, members = src
|
||||||
|
with ND.chunk(tar, members) as d:
|
||||||
|
return collect_bag(d, params, limit, d_starts, laterals, seed)
|
||||||
|
return collect_bag(src, params, limit, d_starts, laterals, seed)
|
||||||
|
|
||||||
|
|
||||||
def ego_track(bag: Bag, params: Params, limit: int):
|
def ego_track(bag: Bag, params: Params, limit: int):
|
||||||
|
|
@ -141,6 +152,12 @@ def main() -> None:
|
||||||
# красивая и бессмысленная, а в тоннеле у оси полно штатных конструкций.
|
# красивая и бессмысленная, а в тоннеле у оси полно штатных конструкций.
|
||||||
ap.add_argument("--laterals", default="0,-0.6,0.6,-1.2,1.2")
|
ap.add_argument("--laterals", default="0,-0.6,0.6,-1.2,1.2")
|
||||||
ap.add_argument("--seed", type=int, default=20260921)
|
ap.add_argument("--seed", type=int, default=20260921)
|
||||||
|
ap.add_argument("--new-data", default="",
|
||||||
|
help="срез номеров шардов new_data, например 0:110: собирать "
|
||||||
|
"выборку из кусков большого бэга вместо записей --root")
|
||||||
|
ap.add_argument("--tar", default=ND.DEFAULT_TAR, help="архив new_data")
|
||||||
|
ap.add_argument("--per-chunk", type=int, default=5,
|
||||||
|
help="шардов в куске new_data; кусок считается одной записью")
|
||||||
P.add_argument(ap)
|
P.add_argument(ap)
|
||||||
PS.add_argument(ap)
|
PS.add_argument(ap)
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
@ -150,13 +167,20 @@ def main() -> None:
|
||||||
params = Params(**PS.apply({}, args.set, Params))
|
params = Params(**PS.apply({}, args.set, Params))
|
||||||
B.CACHE.mkdir(parents=True, exist_ok=True)
|
B.CACHE.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
if args.new_data:
|
||||||
|
chunks = ND.split(ND.pick(ND.shards(args.tar), args.new_data), args.per_chunk)
|
||||||
|
names = [f"new_data_{c[0][0]:03d}_{c[-1][0]:03d}" for c in chunks]
|
||||||
|
sources = [(args.tar, c) for c in chunks]
|
||||||
|
else:
|
||||||
bags = [p for p in find_bags(args.root) if p.name != HOLDOUT]
|
bags = [p for p in find_bags(args.root) if p.name != HOLDOUT]
|
||||||
tasks = [(p, params, args.limit, d_starts, laterals, args.seed) for p in bags]
|
names = [p.name for p in bags]
|
||||||
|
sources = bags
|
||||||
|
tasks = [(s, params, args.limit, d_starts, laterals, args.seed) for s in sources]
|
||||||
# Печатается по готовности, складывается по номеру задачи: порядок бэгов
|
# Печатается по готовности, складывается по номеру задачи: порядок бэгов
|
||||||
# в файле не должен зависеть от того, какой из них досчитался первым.
|
# в файле не должен зависеть от того, какой из них досчитался первым.
|
||||||
slots: list = [None] * len(tasks)
|
slots: list = [None] * len(tasks)
|
||||||
for i, task, got, secs in P.run(_work, tasks, args.jobs):
|
for i, task, got, secs in P.run(_work, tasks, args.jobs):
|
||||||
name = task[0].name
|
name = names[i]
|
||||||
if got is None:
|
if got is None:
|
||||||
print(f" {name:42s} пропущен")
|
print(f" {name:42s} пропущен")
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,17 @@ BANDS = ((0, 30), (30, 55), (55, 80), (80, 110), (110, 160), (160, 230))
|
||||||
|
|
||||||
|
|
||||||
def loo(names, X, Y, cfg, n_pn, args, keep_models=False):
|
def loo(names, X, Y, cfg, n_pn, args, keep_models=False):
|
||||||
"""Обучение на всех бэгах кроме проверяемого. Возвращает список результатов."""
|
"""Обучение на всех бэгах кроме проверяемого. Возвращает список результатов.
|
||||||
|
|
||||||
|
Проверяемыми бывают не все записи: куски `new_data` нужны только для
|
||||||
|
обучения, и отдельная складка на каждый из них — лишние полчаса
|
||||||
|
(`--train-only`).
|
||||||
|
"""
|
||||||
|
skip = tuple(p for p in args.train_only.split(",") if p)
|
||||||
out = []
|
out = []
|
||||||
for held in names:
|
for held in names:
|
||||||
|
if skip and held.startswith(skip):
|
||||||
|
continue
|
||||||
tr = [n for n in names if n != held]
|
tr = [n for n in names if n != held]
|
||||||
Xtr = np.concatenate([X[n] for n in tr])
|
Xtr = np.concatenate([X[n] for n in tr])
|
||||||
ytr = np.concatenate([Y[n] for n in tr])
|
ytr = np.concatenate([Y[n] for n in tr])
|
||||||
|
|
@ -53,7 +61,9 @@ def loo(names, X, Y, cfg, n_pn, args, keep_models=False):
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
ap = argparse.ArgumentParser(description=__doc__)
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
ap.add_argument("--data", default=str(B.CACHE / "training_set.npz"))
|
ap.add_argument("--data", action="append", default=None,
|
||||||
|
help="выборка make_training_set; можно несколько — записи "
|
||||||
|
"объединяются (по умолчанию data/cache/training_set.npz)")
|
||||||
ap.add_argument("--out", default=str(B.ARTIFACTS / "mbon_readout.npz"))
|
ap.add_argument("--out", default=str(B.ARTIFACTS / "mbon_readout.npz"))
|
||||||
ap.add_argument("--n-kc", type=int, default=0,
|
ap.add_argument("--n-kc", type=int, default=0,
|
||||||
help="ёмкость итоговой модели; 0 — взять лучшую из развёртки")
|
help="ёмкость итоговой модели; 0 — взять лучшую из развёртки")
|
||||||
|
|
@ -61,6 +71,9 @@ def main() -> None:
|
||||||
help="ёмкости для развёртки; пусто — не разворачивать")
|
help="ёмкости для развёртки; пусто — не разворачивать")
|
||||||
ap.add_argument("--active", type=int, default=100,
|
ap.add_argument("--active", type=int, default=100,
|
||||||
help="активных клеток после торможения APL")
|
help="активных клеток после торможения APL")
|
||||||
|
ap.add_argument("--train-only", default="",
|
||||||
|
help="префиксы записей через запятую, которые только учат и "
|
||||||
|
"своей складки не получают, например new_data_")
|
||||||
ap.add_argument("--epochs", type=int, default=60)
|
ap.add_argument("--epochs", type=int, default=60)
|
||||||
ap.add_argument("--lr", type=float, default=4.0)
|
ap.add_argument("--lr", type=float, default=4.0)
|
||||||
ap.add_argument("--l2", type=float, default=1e-5)
|
ap.add_argument("--l2", type=float, default=1e-5)
|
||||||
|
|
@ -73,12 +86,20 @@ def main() -> None:
|
||||||
"оценка нечестна — считывание увидит проверяемый бэг")
|
"оценка нечестна — считывание увидит проверяемый бэг")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
d = np.load(args.data, allow_pickle=True)
|
names, X, Y, D, feats = [], {}, {}, {}, None
|
||||||
names = [str(n) for n in d["names"]]
|
for path in args.data or [str(B.CACHE / "training_set.npz")]:
|
||||||
X = {n: d[f"X_{n}"].astype(np.float32) for n in names}
|
d = np.load(path, allow_pickle=True)
|
||||||
Y = {n: d[f"y_{n}"].astype(np.int8) for n in names}
|
f_here = [str(f) for f in d["features"]]
|
||||||
D = {n: d[f"d_{n}"].astype(np.float32) for n in names}
|
if feats is not None and f_here != feats:
|
||||||
feats = [str(f) for f in d["features"]]
|
raise SystemExit(f"{path}: другой набор признаков — выборки несовместимы")
|
||||||
|
feats = f_here
|
||||||
|
for n in (str(n) for n in d["names"]):
|
||||||
|
if n in X:
|
||||||
|
raise SystemExit(f"запись {n} встречается в двух выборках")
|
||||||
|
names.append(n)
|
||||||
|
X[n] = d[f"X_{n}"].astype(np.float32)
|
||||||
|
Y[n] = d[f"y_{n}"].astype(np.int8)
|
||||||
|
D[n] = d[f"d_{n}"].astype(np.float32)
|
||||||
n_pn = next(iter(X.values())).shape[1]
|
n_pn = next(iter(X.values())).shape[1]
|
||||||
tot = sum(v.shape[0] for v in X.values())
|
tot = sum(v.shape[0] for v in X.values())
|
||||||
pos = sum(int(v.sum()) for v in Y.values())
|
pos = sum(int(v.sum()) for v in Y.values())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue