🧭 Каталог вариантов усиления бота
О чём это
Полное меню направлений усиления игрового бота, составленное после KCP-эксперимента (см. 🏁 Результаты усиления модели и ростер oracle-ботов). Каждый вариант — с механикой, ценой стройки, ожидаемым приростом и рисками. Основано на сплошном обзоре четырёх репо (движок, ev, analytics, доки) 2026-07-11. Исходный план арки фич — План усиления value-модели.
Точка отсчёта: 2-ply rich = 57.8% (задеплоенный чемпион oracle-3), 1-ply KCP = 60.8% (сильнее, но ~6.6 с/ход — не live). Гейт = win-rate против
aggressive, seeded арена.
Три факта, которые определяют стратегию
- Генерация self-play почти бесплатна. 1-ply rich играет ~27 партий/сек на M3 Pro → 100 000 партий ≈ 1 час ≈ 1.66M обучающих строк. Кости дают встроенную эксплорацию: два одинаковых бота не повторяют партии, 56 мультисетов на ход диверсифицируют дебюты сами — никакой epsilon-greedy механики не нужно. Это условия TD-Gammon (тоже кости, тоже value-модель).
- Недостающий кирпич один — дампер обучающих строк.
BotMatchRunner.simulateGameвидит на каждом ходу всё (позиция до броска, кости, ходящий, выбранный путь), но возвращает только исход. Мини-экспортёр (callback per turn → буфер строк → штамп исхода → CSV в формате экспортёра) ≈ 0.5–1 день. Все примитивы есть:FenParser.serialize(DFEN с пулом костей), UCI-нотация вOpeningBookBot.uci, фичи через*Features.extract. При генерации 2-ply дампить и корневое значение поиска — оно уже посчитано, это бесплатные TreeStrap-метки. - Измерительный прибор надо усилить до итераций. Гейт на 400 партий шумит ±2.5пп (1σ) — принимать на нём 15–20 решений о промоушене нельзя (промоутится шум). Но 1-ply гейт на 10 000 партий стоит ~6 минут и даёт σ=0.5пп; парные сиды (кандидат и чемпион на одинаковых наборах костей) режут дисперсию разницы ещё в 2–3×.
Инварианты любого обучающего цикла
Три прибора, без которых цикл самообмана не заметит
- Парный 10k-гейт (1-ply, vs
aggressive, одинаковые сиды у кандидата и чемпиона) — рефери никогда не участвует в тренировочных данных. Промоушен только при ≥ чемпион + 1.5пп.- Второй замороженный рефери (например 1-ply material, 41.5%): если win-rate против основного рефери растёт, а против второго — нет, идёт стилевой оверфит.
- Held-out logloss на человеческих партиях: если гейт растёт, а human-logloss деградирует — модель усиливает собственные слепые пятна (амплификация ошибок поиска-учителя).
Milestone-замер: 2-ply гейт на 1600 партий (~72 мин) на каждом значимом промоушене.
Ось A — Самообучение
A1. Дистилляция KCP→rich — ✅ ВЫПОЛНЕН 07-11 (частичный успех, ёмкостный потолок найден)
Идея: никаких новых партий. У нас есть дорогой сильный учитель (KCP-модель, 60.8%) и дешёвый студент (rich-фичи, 9 штук). Перекладываем сигнал учителя в студента: новая метка = λ·предсказание учителя + (1−λ)·исход партии на существующих 1.3M человеческих строк (KCP-обогащение уже есть — training_data_kcp.csv.gz содержит оба набора фич).
Механика: учитель предсказывает на 13 фичах (секунды на 1.3M строк), студент учится на 9 rich-фичах с бленд-меткой; свип λ ∈ {0, 0.25, 0.5, 0.75, 1.0}; retrain 15 с; оценка на тесте против настоящих исходов. Лучшую λ — в ONNX и на арену.
Почему может сработать: 4 KCP-колонки несут ~12.8пп сигнала (60.8 vs 48.0 на 1-ply). Дистилляция просит mobility/king-safety выразить этот сигнал там, где они коррелируют. Прямая аналогия — NNUE-сети, обучаемые имитировать более медленный сильный эвалуатор.
Результат: offline — плоский ноль, арена — маленький, но реальный эффект
Offline-свип идеально плоский: AUC 0.7022–0.7025 на всех λ против контрольных 0.7023 (λ=0). Сигнал учителя (его собственный AUC 0.7109) не переносится — mobility/king-safety не могут реконструировать вероятность взятия короля. Ожидаемо задним числом: дистилляция помогает студенту, которому не хватает данных или оптимизации; здесь 9-фичевый GBDT на 1.2M строк упирается в ёмкость, а перелейблинг тех же фич ёмкости не добавляет.
Но парные seeded-арены (общие сиды кандидат/контроль) показали реальный, хоть и малый эффект: 1-ply, λ=0 vs λ=1 — 10k партий 50.0% vs 51.2%; 20k партий 50.0% vs 51.1% (10211W/9789L). Сглаживание меток (непрерывная вероятность учителя вместо шумных 0/1 исходов) даёт ~+1.1пп даже при неизменном AUC — это эффект регуляризации/сглаживания, отдельный от переноса нового сигнала. 2-ply парный гейт (проверка, доживает ли эффект до задеплоенного чемпиона) — см. PR.
Вывод для дорожной карты: узкое место — набор фич, не метки и не объём данных (третий раз подряд в истории проекта: все большие скачки давали именно фичи). Приоритет смещается на C2 (новые дешёвые фичи) и B1 (гибрид 2-ply+KCP-рескоринг) — эффект нужно вносить фичами, не переразметкой существующих.
ev PR #6 (Closes #5): distill.py — teacher-scoring + бленд-меток + свип, один проход извлечения фич на сплит (rich-матрица = срез kcp-матрицы → учитель и студент видят побайтово идентичные строки).
| Стройка | Факт | Итог |
|---|---|---|
| ~0.5 дня, свип < 1 часа (реально 31с) | offline: плоско; арена: +1.1пп при λ=1 (20k партий, парный сид) | Дёшевый информативный результат, как и планировалось — пошли дальше по каталогу с чётким приоритетом (фичи, не метки) |
A2. Flywheel на исходах (TD-Gammon)
Идея: классическое самообучение. Текущий бот играет сам с собой, позиции лейблятся исходом, модель переобучается, цикл повторяется. Каждая итерация — шаг policy iteration: выучили V^π, играем жадно по ней.
Механика:
- Генерация: 1-ply rich чекпойнты в пуле оппонентов: 50% текущий-vs-текущий, 30% против ~5 прошлых чекпойнтов, 20% против встроенных ботов кроме
aggressive(рефери остаётся внешним). 100k партий/итерацию ≈ 62 мин на ноуте, облако не нужно. - Метки: исход, mover-perspective (ничьих нет — чистые 0/1). Опционально лёгкое дисконтирование ранних ходов к 0.5 (дисперсия костей).
- Смесь: human:self-play = 1:1 в первой итерации → до 1:2; не ниже 25% human. Человеческие строки — якорь распределения состояний. ⚠️ ~16.6 строк партии делят один исход → эффективный размер выборки = партии, не строки; сабсэмплить ≤2 строк на фазу партии или взвешивать 1/длину.
- Цикл: генерация (~1 ч) → retrain (15 с) → парный 10k-гейт (6 мин). Итерация ≈ 1.5 ч → 4–5/день, ~15–20 итераций за неделю (можно ночами).
| Стройка | Ожидание | Риски |
|---|---|---|
| ~1 день (дампер + скрипт цикла) | +2–6пп к 1-ply, большинство в первых ~5 итерациях; 2-ply двигается примерно в тандеме | Коллапс распределения (митиграция: пул+human-якорь+кости); стилевой оверфит (приборы №2–3); плато ёмкости GBDT — история проекта говорит, что скачки давали фичи, не объём данных, так что честный нижний край = +1–2пп |
Бонус: лучший нарратив для презентации хакатона — «бот научился, играя сам с собой всю ночь». И дампер, построенный здесь, нужен всем остальным вариантам.
A3. TreeStrap — перемётка человеческих позиций поиском
Идея: метка позиции = не исход партии (шумный, один на 16.6 позиций), а значение 2-ply expectimax под текущей моделью (pre-roll, ожидание по 56 мультисетам). Это рецепт NNUE-сообщества (Stockfish-сети учатся на оценках поиска) и TreeStrap (Veness et al. — мастерский уровень в классических шахматах бутстрапом поиска).
Механика: eval_{t+1} = distill(search(eval_t)) — лестница. Каждая ступень: перелейблить подвыборку (300–500k строк, ~1–9 ч на 64 OCPU облака, <$10) → retrain → гейт. Метка = λ·search + (1−λ)·исход, λ≈0.5–0.75. Одна ступень в день, 2–3 ступени реалистично.
Про «потолок слабого учителя»
Студент может превысить учителя: учитель (57.8%) ограничивает одну ступень, а не лестницу — дистиллированного студента снова оборачивают в 2-ply поиск, и search(eval_{t+1}) > search(eval_t), пока дистилляция достаточно точна. Реальный потолок — представительная ёмкость фич, и он общий для всей оси A.
| Стройка | Ожидание | Риски |
|---|---|---|
| 1–1.5 дня (батч-лейблер на движке + облачный скрипт) | +4–6пп на 2-ply за 2–3 ступени | Цена 2-ply на строку не замерена (0.16–2 core-s — ×12 неопределённость, замерить до коммита); амплификация слепых пятен (прибор №3); стухание ступеней к 3-й |
A4. AlphaZero-lite (эскалация)
Идея: объединить A2 и A3 — self-play 2-ply ботом (сильнейшая доступная генерация) + бесплатные search-метки (корневое значение уже посчитано при выборе хода) + лига чекпойнтов.
Механика: лига {текущий, лучший, 2 прошлых, 1 встроенный}; 5–10k партий/итерация (2–4 ч на 64 OCPU); метка = 0.7·корневое значение + 0.3·исход; replay-окно последних ~3 итераций + human-строки с весом ~0.3; итерация 3–5 ч → 1–2 в день.
| Стройка | Ожидание | Риски |
|---|---|---|
| 2–3 дня до первой итерации; ~$30–100 облака | потолок самый высокий: 2-ply 62–66% | Все риски A2 + сильнее амплификация (и состояния, и метки от текущей модели); мало партий → шумный исход (несёт search-метка); оркестрация ест дефицитные дни |
Вердикт: только как эскалация второй недели, если A1+A2 застопорились — переиспользует их дампер и гейт-инфраструктуру.
Ось B — Поиск
B1. Гибрид: 2-ply rich + KCP-рескоринг корневых финалистов
Уже записан в 🎓 KCP-фича — тактика против глубины поиска (§6–7) как «концептуально сильнее чистого 1-ply+KCP». Механика: обычный 2-ply rich поиск даёт топ-K финалистов; затем KCP-модель оценивает только их — 8 × ~16 мс ≈ ~130 мс/ход, т.е. live-viable. Дизайн-вопрос: как смешивать 2-ply значение и 1-ply KCP-скор (бленд с весом / тайбрейк / вето на ходы с высоким king_danger). Стройка ~1–2 дня. Ожидание: забрать часть из +3пп KCP над 2-ply при живой скорости.
B2. Policy-модель для top-K ответов соперника
В ExpectimaxSearch.opponentMinValue ответы соперника сейчас не прунятся вовсе («сотни-тысячи ответов на бросок») — единственное непрунированное место и естественный слот для policy: дешёвая модель предвыбирает top-K ответов. Листья уже материализуются как Array[GameState] — батч-форма готова. Это и самостоятельное ускорение 2-ply, и пререквизит 3-ply. Корневой пре-ранкер тоже захардкожен на материал (SearchScoring.scorePath(…, evaluateMaterial)) — второй слот.
B3. 3-ply expectimax
Глубина захардкожена (2); naive 3-ply ≈ 8 × 56 × R × 56 × R′ ≈ 10⁶–10⁸ листьев/ход. Требует: B2 (прунинг ответов), дедлайн-чеки внутри chance node (сейчас проверяется только между кандидатами), дешёвый лист. Большая стройка, неизвестный прирост. После хакатона.
B4. Реанимация MCTS
История: UCT MCTS v1 проиграл flat-MC 30:70 (журнал турниров 06-22) — случайное расширение без прунинга тратит итерации впустую. Список фиксов записан в 03 Идеи по улучшению алгоритмов §1–4 (прунинг кандидатов, safe root fallback, weighted chance nodes, ранний обрыв роллаутов + maxPlies-баг). Код жив на ветке feat/392-opening-book-bot. Естественная форма реанимации — MCTS с value-моделью вместо роллаутов (закрывающий TIP в 🎓 2-ply Expectimax поверх value-модели). Спекулятивно; после хакатона.
B5. candidateLimit↑ (почти бесплатный замер)
Рычаг силы/времени: K=2→46.5%, K=8→55+%. K=16 не замерялся (у Monte-Carlo прецедент MaxCandidates=16). Стоимость замера ≈ 0 (конфиг + арена). Ожидание +1–2пп за ~2× времени хода — у 2-ply rich запас есть (~160 мс/решение).
Ось C — Оценка и фичи
C1. Удешевить KCP (engine epic #385)
Сделано: 1A (вынос ply-0) + 1B (int-кодирование пула костей) = 1.6–1.72×. В спеках: 1C (#390, bitmask-шорткат прямых взятий, «~1× на тихих позициях») и 2A (#391, mutable scratch board make/undo, «~2–3× на JVM, 5× оптимистично»,高 риск — вариант A уже был реверчен по регрессии). Реалистичный итог 3–5× от исходного, НЕ порядок. Следствие: 6.6 с/ход → ~1.5–2 с/ход — oracle-4 становится почти live (3+ мин контроли), гибрид B1 и TreeStrap A3 дешевеют пропорционально. Отвергнуто внутри эпика (не переделывать): прунинг мультисетов (неверен для AAA/AAB), кэш транспозиций (hit-rate≈0), внутривызовный параллелизм.
C2. Новые дешёвые фичи
Пешечная структура (сдвоенные/изолированные/проходные), укрытие короля, контроль центра, темпо-фичи — всё µs-класса, т.е. можно в листья 2-ply (в отличие от mobility=2 прохода movegen и KCP=216-DFS). История проекта: скачки давали именно фичи (+6.5пп rich, +12.8пп KCP на 1-ply). Паттерн готов: *Features в движке → enrichment → features.py → арена. ~1 день на набор.
C3. Модель посильнее (MLP вместо GBDT)
Если докажется потолок ёмкости GBDT (например, A1 покажет, что 9 фич не выражают KCP, и C2 не поможет). Сейчас пайплайн LightGBM-only: export_onnx завязан на convert_lightgbm + .txt-сериализацию; MLP потребует диспатч конвертера (skl2onnx или torch.onnx.export). Скала-сторона (FloatTensorType, вход “input”) не меняется. Средняя стройка; только по доказанной необходимости.
Ось D — Данные
D1. Больше человеческих партий + веса
Сейчас: 79k партий (оба ≥1800) из ~185k+ в БД. Расширение = чистая CLI-команда (mise run db:export-training out.csv.gz 1500 или 0 classic king_captured), рейтинги уже в CSV → взвешивание по силе игроков = один параметр sample_weight до LGBMRegressor.fit (мелкая правка ev). Риск: слабые игроки = шумные метки — потому и веса, а не просто больше данных.
D2. Книга дебютов для модельных ботов
Конвейер готов сегодня: mise run db:export-book (analytics) → JSON → OpeningBookBot.decorate(baseBot, book) (движок). Арена-валидация на крошечной книге уже дала +5пп aggressive-book vs aggressive (шумно, но позитивно). Требование: книга только от сильных игроков (≥2000), боты исключены (иначе петля «боты играют книгу → книга учится на ботах», см. Книга дебютов для роботов). ~0.5 дня на подключение к oracle-ботам.
Отвергнуто ранее (не переделывать)
| Идея | Результат | Где записано |
|---|---|---|
| Aggressive-преселекция кандидатов MC | 46.7% < 50.8% базы | 01 Анализ Монте-Карло против Aggressive, журнал Match 3 |
| Epsilon-greedy heavy rollouts (40% greedy) | 49.2% < 50.8%; «случайные роллауты работают лучше» | там же, Match 4 |
| UCT MCTS v1 | проиграл flat-MC 30:70 | 02 Журнал турниров 06-22 |
| Прунинг 56 мультисетов KCP | математически неверен (AAB/AAA, изломанные пути слайдеров) | epic #385 |
| Кэш транспозиций в KCP (plies≥1) | hit-rate ≈ 0 на случайных блужданиях | epic #385 |
| Dice-conditioned модель на корне | «лукахеда не даёт, слепоту к рекапче не лечит» | 🎓 2-ply Expectimax поверх value-модели §7 |
Рекомендованный план (12 дней до хакатона, с запасом на UI-работу)
- День 1: A1 (дистилляция) + парный 10k-гейт как инструмент. Даже нулевой результат дёшев и информативен — сразу меряет ёмкость rich-фич.
- День 2: дампер self-play строк (движок) — открывает A2/A3/A4; сразу с дампом корневого значения при 2-ply генерации.
- Дни 3–5: A2 flywheel (итерации фоном/ночами) с тремя приборами-инвариантами.
- Параллельно по желанию: B1 гибрид — самый вероятный путь донести силу KCP до живого бота; B5 — бесплатный замер.
- После хакатона: C1 (KCP 2A), B2→B3, D1/D2, при доказанном потолке — C3.
Честная сводная проекция по оси A: 2-ply live-бот 57.8% → ~61–65% к хакатону; ~50% уверенности в приросте >+3пп, ~20% риска упереться в потолок фич на +1–2пп.
Связанные заметки
- 🏁 Результаты усиления модели и ростер oracle-ботов — точка отсчёта, цифры всех арен.
- План усиления value-модели — исходный план арки фич (выполнен).
- 🎓 KCP-фича — тактика против глубины поиска — что такое KCP и гибрид B1.
- 🎓 2-ply Expectimax поверх value-модели — как устроен поиск чемпиона.
- 03 Идеи по улучшению алгоритмов — MCTS-фиксы для B4.
- Книга дебютов для роботов — D2.