NextStatNextStat

Байесовские бенчмарки со смыслом

ESS/сек vs реальное время (wall-time) — строгие протоколы сравнения со Stan и PyMC.

БайесNUTSESS/секStanPyMC

2026-02-08 · 7 мин. чтения


Серия «Наступление доверия»:Оглавление·Предыдущий: Дифференцируемый HistFactory·Следующий: Фарма-бенчмарки

Байесовские бенчмарки коварны: они сводят многомерный объект (апостериорное распределение + алгоритм сэмплирования) к одному числу. Это может быть полезно — но только если вы полностью контролируете постановку эксперимента.

В этом посте описано, как мы собираемся бенчмаркировать байесовский вывод с помощью метрики ESS/сек так, чтобы сравнения были воспроизводимыми и сопоставимыми между фреймворками.

Аннотация. Мы рассматриваем числа производительности байесовского вывода как научные утверждения: фиксируем апостериорное распределение и протокол вывода, публикуем эффективность сэмплирования (ESS/сек; центральная часть (bulk) и хвосты (tail)) и метрики «здоровья» (divergences, насыщение по treedepth, R̂, E-BFMI), а также артефакты, чтобы независимый исследователь мог воспроизвести запуск. Сегодня seed-харнесс всегда запускает nextstat-бэкенд и генерирует эти метрики и артефакты под зафиксированными схемами. Бэкенды cmdstanpy и pymc поддерживаются best-effort: если зависимости отсутствуют, всё равно публикуется артефакт со статусом warn и причиной (это часть контракта воспроизводимости). Дальше мы усиливаем паритет кросс-фреймворк: одинаковые модели, согласованные настройки и единая политика диагностик. Текст и артефактный контракт ниже соответствуют NextStat 0.9.0.

0.Репликабельность: минимальный запуск и артефакты

Этот пост опирается на seed-харнесс байесовского сьюта в репозитории публичных бенчмарков. Он устроен как публикационный протокол: запуск → JSON-артефакты со схемой, манифестом окружения, конфигурацией сэмплера и диагностиками.

Минимальный запуск (NextStat-only)

bash
python3 benchmarks/nextstat-public-benchmarks/suites/bayesian/suite.py \
  --deterministic --out-dir out/bayesian

Запуск с опциональными бэкендами (best-effort)

bash
python3 benchmarks/nextstat-public-benchmarks/suites/bayesian/suite.py \
  --deterministic --out-dir out/bayesian \
  --backends nextstat,cmdstanpy,pymc

Если cmdstanpy или pymc не установлены, кейсы не «исчезают» — они публикуются как warn с диагностируемой причиной.

Артефакты и схемы

АртефактСхемаНазначение
out/bayesian/cases/*.jsonnextstat.bayesian_benchmark_result.v1Один кейс: wall-time, ESS/сек (bulk/tail), R̂, divergence/treedepth, метаданные бэкенда.
out/bayesian/bayesian_suite.jsonnextstat.bayesian_benchmark_suite_result.v1Индекс всех кейсов + sha256 + сводка по статусам.

Важная деталь протокола: в каждом кейсе явно публикуются настройки сэмплера (например,n_chains, warmup, samples,seed, max_treedepth,target_accept, init_jitter_rel) и версия NextStat в meta.nextstat_version.

Формально, контракт артефакта для одного кейса фиксирует:

  • Идентификатор входа. dataset.id и dataset.sha256(для сгенерированных датасетов — хэш канонического JSON).
  • Конфигурацию NUTS. В JSON это поля config.n_warmup и config.n_samples(CLI использует флаги --warmup и --samples).
  • Статус и причину. status ∈ failed и опционально reason (например, отсутствующая зависимость у опционального бэкенда).

1.Почему ESS/сек — правильная первая метрика (и когда это не так)

Для градиентных сэмплеров реальное время на итерацию (wall-time) — не вся история. Эффективный размер выборки (ESS) учитывает и вычислительную стоимость, и эффективность сэмплирования.

Но ESS/сек имеет смысл только когда:

  • Модель одна и та же
  • Параметризация одна и та же (центрированная vs нецентрированная может доминировать по влиянию)
  • Диагностика считается согласованно и по одной и той же методике

2.Что мы публикуем: ESS/сек + метрики «здоровья»

ESS/сек можно «накрутить» патологическими запусками. Поэтому как минимум мы публикуем:

  • ESS/сек по центральной части (bulk) и хвостам (tail), по группам параметров
  • Долю дивергенций (divergences)
  • Долю насыщения по максимальной глубине дерева (treedepth)
  • Максимальное R̂ по параметрам
  • Минимальное ESS (bulk + tail) по параметрам
  • Минимальное E-BFMI по цепям

Это не «дополнительная диагностика» — это часть того, что значит «засчитанный запуск».

В опорном харнессе ESS/сек публикуется как производная от диагностики и wall-time:timing.ess_bulk_per_sec.min = min(ESS_bulk) / wall_time_sи аналогично для median (если она определена). То же самое делается для tail‑ESS. Это специально делает метрику «пессимистичной»: один плохо смешавшийся параметр снижает итоговую оценку.


3.Протокол бенчмарка (что нужно зафиксировать)

Чтобы не получить «театр бенчмарков», мы фиксируем:

  • Определение модели и априоры
  • Параметризацию (явно)
  • Длину прогрева (warmup) и длину основной выборки
  • Настройки адаптации: целевая доля принятия (target_accept), политика адаптации шага и матрицы масс (mass matrix) и расписание обновлений
  • Политику инициализации ГСЧ (seed) и требования к детерминизму

Если что-либо из этого отличается, мы не называем это «сравнением» — это другой эксперимент.

3.1 Начальный набор публичных базовых линий (рекомендуется)

Для первых публичных байесовских снимков мы рекомендуем небольшой набор базовых линий, который покрывает разные режимы NUTS:

  • Простая HistFactory (~8 параметров) — быстро и «похоже на инференс»; проверяет базовое поведение ESS/сек и R̂
  • Логистическая регрессия (GLM) — классический бенчмарк; легко воспроизводим в Stan/PyMC
  • Иерархическая модель со случайными эффектами (нецентрированная) — упражняет геометрию «воронки» и чувствительность к параметризации

Большой воркспейс ФВЭ (например, tHu, 184 параметра) полезен, но обычно слишком медленный для ночных прогонов; держите его за флагом slow/release-only и публикуйте как редкий «контрольный прогон на масштабе».


4.Гейты корректности: числа производительности должны быть «допущены к существованию»

Мы разделяем корректность апостериорного распределения от производительности и публикуем оба.

4.1 Контрольный набор качества NUTS (быстро)

Запуск Apex2 «NUTS quality» ловит катастрофические регрессии в преобразованиях параметров, стабильности HMC/NUTS и вычислении диагностик:

PYTHONPATH=bindings/ns-py/python ./.venv/bin/python \
  tests/apex2_nuts_quality_report.py \
  --deterministic \
  --out tmp/apex2_nuts_quality_report.json

Включает небольшие интерпретируемые кейсы: среднее нормального распределения (Gaussian mean), апостериорное распределение со строгими априорами, стресс‑кейс Neal's funnel и мини‑фикстуру HistFactory.

Пороговые значения публикуются как часть отчёта. По умолчанию (smoke‑режим) используются:

  • rhat_max = 1.10
  • divergence_rate_max = 0.05
  • max_treedepth_rate_max = 0.10
  • ess_bulk_min = 10, ess_tail_min = 10, ebfmi_min = 0.20

Флаг --strict ужесточает требования до стандарто‑подобныхR̂ < 1.01 и divergence_rate < 0.01, а также автоматически увеличивает длину прогрева/выборки, чтобы такой режим был практически проходим.

4.2 Калибровка на основе симуляций (SBC) (медленно, но убедительнее)

SBC валидирует апостериорное распределение более напрямую: для синтетических наборов данных, сгенерированных из априора, ранги истинных параметров в апостериоре должны быть равномерными.

NS_RUN_SLOW=1 NS_SBC_RUNS=20 NS_SBC_WARMUP=200 NS_SBC_SAMPLES=200 \
  PYTHONPATH=bindings/ns-py/python ./.venv/bin/python \
  tests/apex2_sbc_report.py \
  --deterministic \
  --out tmp/apex2_sbc_report.json

Это не бенчмарк производительности — это проверка корректности, которая делает сравнения производительности осмысленными.

4.3 Политика диагностики: считаем в Rust, валидируем снаружи

Чтобы опорный харнесс оставался лёгким, NextStat считает ключевые диагностики в Rust (split rank-normalized folded R̂,bulk/tail ESS по оценкам Гейера и E-BFMI) и экспортирует их в JSON. Для кросс-фреймворк сравнений мы считаем «как именно считаются диагностики» частью зафиксированного протокола: либо все используют одну и ту же библиотеку диагностик, либо мы публикуем пакет валидации, который пересчитывает метрики стандартным инструментом (например, ArviZ) и проверяет паритет.


5.Что мы публикуем (артефакты)

Для каждого снимка бенчмарка:

  • Манифест бейзлайна (версии, железо, настройки)
  • Сырые измерения времени
  • ESS‑метрики (bulk + tail) по параметрам и сводка ESS/сек:ess_bulk_per_sec и ess_tail_per_sec(поля min и median), нормированные наtiming.wall_time_s.
  • Ключевые диагностики «здоровья» (в схемах — агрегированная сводка):divergence_rate, max_treedepth_rate,max_r_hat, min_ess_bulk,min_ess_tail, min_ebfmi.

Если запуск патологичен (divergences, срыв адаптации), мы публикуем это как результат, а не прячем в сноску.

Спецификация публикации: публичные бенчмарки.


6.Известные ловушки (мы фиксируем их явно)

A) Параметризация доминирует

Одна и та же модель в центрированной и нецентрированной форме может отличаться по ESS/сек на порядки. Мы считаем параметризацию частью входа бенчмарка.

B) Разные значения по умолчанию — разные эксперименты

У Stan, PyMC и кастомных реализаций разные значения по умолчанию для адаптации. Бенчмарки должны либо согласовать эти настройки, либо публиковать их и интерпретировать различия.

C) Расчёт ESS должен быть согласованным

ESS зависит от метода и версии инструмента диагностики. Мы публикуем точную политику расчёта и версию.


7.Как запустить локальный микробенчмарк (уже сегодня)

Для микробенчмарка только на Rust для NUTS по реальному времени (wall-time) с фиксированным NutsConfig:

cargo bench -p ns-inference --bench nuts_benchmark

Это покрывает небольшой Normal mean и мини‑фикстуру HistFactory; полезно для обнаружения регрессий, но не для «побед» между фреймворками.


8.Что важно вынести

Когда мы публикуем байесовские числа производительности, вы должны уметь ответить: «какое именно апостериорное распределение сэмплировалось?», «в каких именно настройках?», «насколько здоровыми были диагностики?» — и воспроизвести тот же харнесс на своём железе.


Связанное чтение