Что такое LightGBM

LightGBM — это библиотека градиентного бустинга над деревьями решений (Gradient Boosted Decision Trees, GBDT), разработанная Microsoft. Чтобы понять, что это значит, разберём по частям.

Дерево решений — простейший блок: последовательность вопросов вида «material_diff > 3?» → «да → dice_q > 0?» → … → в конце листа число (предсказание). Одно дерево — слабый, грубый предсказатель.

Бустинг — идея не строить одно большое дерево, а много маленьких, каждое из которых исправляет ошибки предыдущих:

  1. Первое дерево делает грубое предсказание (условно, «всегда 50%»).
  2. Второе дерево не предсказывает исход напрямую — оно предсказывает остаток ошибки первого дерева (где оно ошиблось и на сколько).
  3. Третье исправляет остаток после первых двух. И так далее.
  4. Финальный прогноз = сумма вкладов всех деревьев (у нас — до 500 штук), прогнанная через сигмоиду, чтобы получить вероятность 0..1.

Это как консилиум врачей: первый ставит общий диагноз, каждый следующий смотрит именно на то, что предыдущие упустили.

«Light» — то, что отличает конкретно LightGBM от конкурента (XGBoost) и делает её быстрой:

  • Гистограммное разбиение: вместо перебора всех возможных порогов разбиения по непрерывному признаку, значения группируются в бины (гистограмму) — на порядок быстрее поиск лучшего разбиения.
  • Рост «по листьям», не «по уровням»: LightGBM на каждом шаге достраивает тот лист дерева, где ошибка максимальна, а не отращивает все листья одного уровня одновременно. Деревья получаются глубже и точнее при том же бюджете, но чуть более склонны к переобучению — отсюда важность ранней остановки (см. ниже).

Почему выбрали именно её для этой задачи

  1. Наши признаки табличные, не картинка/текст. У нас 12 чисел на строку (разница по типам фигур, состав костей) — классическая табличная задача. На таких данных GBDT — золотой стандарт, стабильно обходит нейросети, если только не вкладываться в сложные признаки (mobility, king-danger — мы их сознательно отложили, нет генератора ходов на Python).
  2. Обучается на CPU за минуты, GPU не нужен вообще для этого этапа — иронично, учитывая сколько сил мы вложили в добычу GPU-лимита у Oracle; он понадобится только на следующей ступени (CNN), не для этого бейслайна.
  3. Поддерживает наши «мягкие» метки. У нас исход хода — не строго 0/1 (выиграл/проиграл), а 0 / 0.5 / 1 (проигрыш/ничья/выигрыш). Обычный бинарный objective этого не понимает. Поэтому в baseline.py мы явно указали objective: "xentropy" — кросс-энтропию для непрерывных меток в [0,1], которая именно эту ситуацию и обрабатывает.
  4. Даёт объяснимость. Можно посмотреть feature importance — какие признаки модель реально использует (материал? какая именно фигура на костях?) — полезно и для доверия к модели, и для отладки.

Как это будет использоваться у нас — по шагам, через уже написанный код

Весь путь уже реализован в dicechess-ev, просто ещё не прогнан на реальных данных:

  1. data.py читает training_data.csv.gz (1.3M строк, которые мы только что выгрузили) и делит на train/val/test по партиям, не по отдельным ходам — критично, потому что все ходы одной партии делят один и тот же исход, и деление по ходам дало бы утечку данных и завышенную точность на валидации.

  2. features.py превращает каждую строку в 12 чисел с точки зрения того, кто ходит (не «белые минус чёрные», а «я минус соперник»): разница по пешкам/коням/слонам/ладьям/ферзям, суммарный материал, и состав выпавших костей (сколько из трёх — пешки, сколько — кони и т.д.).

  3. baseline.py передаёт это в LightGBM:

    num_leaves=31, learning_rate=0.05, n_estimators=500, min_child_samples=50
    

    — до 500 деревьев с максимум 31 листом каждое, но с ранней остановкой (early_stopping(30)): если 30 итераций подряд качество на валидации не растёт, обучение останавливается — защита от переобучения на наши ~79k независимых партий (напомню: значимо именно число уникальных партий, а не строк — все ходы одной партии не независимы друг от друга).

  4. 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 дело не дойдёт.