Что такое LightGBM
LightGBM — это библиотека градиентного бустинга над деревьями решений (Gradient Boosted Decision Trees, GBDT), разработанная Microsoft. Чтобы понять, что это значит, разберём по частям.
Дерево решений — простейший блок: последовательность вопросов вида «material_diff > 3?» → «да → dice_q > 0?» → … → в конце листа число (предсказание). Одно дерево — слабый, грубый предсказатель.
Бустинг — идея не строить одно большое дерево, а много маленьких, каждое из которых исправляет ошибки предыдущих:
- Первое дерево делает грубое предсказание (условно, «всегда 50%»).
- Второе дерево не предсказывает исход напрямую — оно предсказывает остаток ошибки первого дерева (где оно ошиблось и на сколько).
- Третье исправляет остаток после первых двух. И так далее.
- Финальный прогноз = сумма вкладов всех деревьев (у нас — до 500 штук), прогнанная через сигмоиду, чтобы получить вероятность 0..1.
Это как консилиум врачей: первый ставит общий диагноз, каждый следующий смотрит именно на то, что предыдущие упустили.
«Light» — то, что отличает конкретно LightGBM от конкурента (XGBoost) и делает её быстрой:
- Гистограммное разбиение: вместо перебора всех возможных порогов разбиения по непрерывному признаку, значения группируются в бины (гистограмму) — на порядок быстрее поиск лучшего разбиения.
- Рост «по листьям», не «по уровням»: LightGBM на каждом шаге достраивает тот лист дерева, где ошибка максимальна, а не отращивает все листья одного уровня одновременно. Деревья получаются глубже и точнее при том же бюджете, но чуть более склонны к переобучению — отсюда важность ранней остановки (см. ниже).
Почему выбрали именно её для этой задачи
- Наши признаки табличные, не картинка/текст. У нас 12 чисел на строку (разница по типам фигур, состав костей) — классическая табличная задача. На таких данных GBDT — золотой стандарт, стабильно обходит нейросети, если только не вкладываться в сложные признаки (mobility, king-danger — мы их сознательно отложили, нет генератора ходов на Python).
- Обучается на CPU за минуты, GPU не нужен вообще для этого этапа — иронично, учитывая сколько сил мы вложили в добычу GPU-лимита у Oracle; он понадобится только на следующей ступени (CNN), не для этого бейслайна.
- Поддерживает наши «мягкие» метки. У нас исход хода — не строго 0/1 (выиграл/проиграл), а 0 / 0.5 / 1 (проигрыш/ничья/выигрыш). Обычный бинарный objective этого не понимает. Поэтому в
baseline.pyмы явно указалиobjective: "xentropy"— кросс-энтропию для непрерывных меток в [0,1], которая именно эту ситуацию и обрабатывает. - Даёт объяснимость. Можно посмотреть feature importance — какие признаки модель реально использует (материал? какая именно фигура на костях?) — полезно и для доверия к модели, и для отладки.
Как это будет использоваться у нас — по шагам, через уже написанный код
Весь путь уже реализован в dicechess-ev, просто ещё не прогнан на реальных данных:
-
data.pyчитаетtraining_data.csv.gz(1.3M строк, которые мы только что выгрузили) и делит на train/val/test по партиям, не по отдельным ходам — критично, потому что все ходы одной партии делят один и тот же исход, и деление по ходам дало бы утечку данных и завышенную точность на валидации. -
features.pyпревращает каждую строку в 12 чисел с точки зрения того, кто ходит (не «белые минус чёрные», а «я минус соперник»): разница по пешкам/коням/слонам/ладьям/ферзям, суммарный материал, и состав выпавших костей (сколько из трёх — пешки, сколько — кони и т.д.). -
baseline.pyпередаёт это в LightGBM:num_leaves=31, learning_rate=0.05, n_estimators=500, min_child_samples=50— до 500 деревьев с максимум 31 листом каждое, но с ранней остановкой (
early_stopping(30)): если 30 итераций подряд качество на валидации не растёт, обучение останавливается — защита от переобучения на наши ~79k независимых партий (напомню: значимо именно число уникальных партий, а не строк — все ходы одной партии не независимы друг от друга). -
evaluate.pyсчитает log-loss (главная метрика — насколько калиброваны вероятности), AUC/accuracy (только на решённых партиях — ничьи не имеют смысла для бинарной точности), и калибровочную таблицу.
Что происходит с обученной моделью дальше
Это самое интересное — куда это всё ведёт:
- Бот через expectimax в 1 полуход. Для каждого хода-кандидата считаем итоговую позицию, прогоняем через модель, получаем EV. Кости соперника непредсказуемы — 56 равновероятных вариантов, поэтому берём математическое ожидание по ним (для этого модель тренируется ещё и с «dropout» на костях — периодически кости на входе обнуляются, чтобы одна и та же модель работала и «кости известны», и «кости неизвестны для листовой оценки»). Выбираем ход с максимальным EV.
- Grading ходов / поиск ошибок. Разница
EV(лучший доступный ход) − EV(сыгранный ход)— это и есть «на сколько игрок ошибся», прямая связь с идеей аналитики (оценка качества ходов игроков), которую мы обсуждали раньше. - Перенос в Scala. LightGBM-модель нужно будет как-то исполнять из движка — либо экспортировать в формат, читаемый из JVM (текстовый дамп дерева — по сути просто вложенные if/else, несложно интерпретировать вручную), либо через ONNX. Важный нюанс на будущее: код извлечения признаков должен точно совпадать между Python (при обучении) и Scala (при игре) — иначе классическая проблема train/serve skew, когда модель в проде получает признаки, слегка отличающиеся от тех, на которых училась, и работает хуже, чем показывали метрики.
- Проверка в арене. Прогоняем получившегося бота через
TimedArenaRunnerпротив Aggressive(L5) — наш гейт «≥55% побед на 400 партиях».
Честный прогноз результата
Точность (accuracy) будет скромной — ориентир ~65-70%, и это нормально: три случайные кости плюс шум от слабых игроков создают реальную, неустранимую неопределённость на уровне отдельного хода в миттельшпиле. Оценивать модель нужно не по accuracy, а по log-loss и калибровке — насколько честно откалиброваны вероятности (если модель говорит «70% на победу», действительно ли такие позиции побеждаются в ~70% случаев). Плюс у нас уже есть бесплатная внешняя проверка калибровки: сравнить прогнозы модели с эмпирическим winrate по частым дебютным позициям из opening book — если совпадает, значит кодировка (перспектива хода, порядок костей) верна.
Это первая, «страховочная» ступень лестницы моделей — работающий, объяснимый бейслайн, который можно показать на демо, даже если до CNN дело не дойдёт.