Байесовские бенчмарки со смыслом
ESS/сек vs реальное время (wall-time) — строгие протоколы сравнения со Stan и PyMC.
2026-02-08 · 7 мин. чтения
Байесовские бенчмарки коварны: они сводят многомерный объект (апостериорное распределение + алгоритм сэмплирования) к одному числу. Это может быть полезно — но только если вы полностью контролируете постановку эксперимента.
В этом посте описано, как мы собираемся бенчмаркировать байесовский вывод с помощью метрики 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)
python3 benchmarks/nextstat-public-benchmarks/suites/bayesian/suite.py \
--deterministic --out-dir out/bayesianЗапуск с опциональными бэкендами (best-effort)
python3 benchmarks/nextstat-public-benchmarks/suites/bayesian/suite.py \
--deterministic --out-dir out/bayesian \
--backends nextstat,cmdstanpy,pymcЕсли cmdstanpy или pymc не установлены, кейсы не «исчезают» — они публикуются как warn с диагностируемой причиной.
Артефакты и схемы
| Артефакт | Схема | Назначение |
|---|---|---|
| out/bayesian/cases/*.json | nextstat.bayesian_benchmark_result.v1 | Один кейс: wall-time, ESS/сек (bulk/tail), R̂, divergence/treedepth, метаданные бэкенда. |
| out/bayesian/bayesian_suite.json | nextstat.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.Что важно вынести
Когда мы публикуем байесовские числа производительности, вы должны уметь ответить: «какое именно апостериорное распределение сэмплировалось?», «в каких именно настройках?», «насколько здоровыми были диагностики?» — и воспроизвести тот же харнесс на своём железе.
