← К портфолио A/B Testing Methodology Toolkit preview
Ситуация
Метод A/B-теста хорош настолько, насколько верны его Type I error под нулём и мощность под эффектом
Задача
Сделать свойства методов проверяемыми: контроль ошибки подкреплён прогоном, а не ссылкой на литературу
Действия
Реализация методов из первичной литературы — SRM, CUPED, delta-method, mSPRT, HTE, BH — и калибровка каждого симуляцией
Результат
Type I error ≈ α у каждого метода; покрытие CI ≈ 95% для bootstrap; 15 модулей калибруются симуляцией
Стек
PythonNumPy / SciPypytestuv
Исходники Смотреть на GitHub → Обновлено: Sep 17, 2026
Содержание
  1. Ситуация
  2. Задача
  3. Действия
  4. Модули
  5. Сквозной пайплайн
  6. Запуск
  7. Результат
  8. Ограничения
  9. Документация

A/B Testing Methodology Toolkit

Ситуация

Метод A/B-теста хорош ровно настолько, насколько хороши его ошибка первого рода под нулевой гипотезой и мощность под реальным эффектом.

На практике эти обещания редко проверяют:

  • наивный peeking,
  • неправильный SE для ratio-метрик
  • и множественное тестирование молча ломают решения.

Тулкит реализует 15 методов из первичной литературы и калибрует каждый симуляцией — вместо веры в асимптотику.

Задача

От меня требовалось сделать свойства методов проверяемыми: чтобы заявление о контроле ошибки подкреплялось прогоном, а не ссылкой на литературу.

Действия

Модули

Модуль Метод Что показывает демо
srm_test.py Sample Ratio Mismatch (χ²) ловит бакинг-баги до любых downstream-тестов
sample_size.py Fixed-horizon sizing n/arm для пропорций и средних
delta_method_ratio.py Ratio-метрики (CTR, RPC) корректный SE для ΣY/ΣX; наивный per-unit t-test смещён
cuped.py Variance reduction дисперсия ↓ на ρ², SE — примерно в ρ раз, за счёт pre-period
group_sequential.py Alpha-spending границы Pocock/OBF держат Type I, пока наивный peeking его раздувает
msprt_always_valid.py Always-valid p-values mSPRT позволяет смотреть и останавливаться когда угодно
sequential_ratio.py Sequential ratio-метрики delta-method + mSPRT для CTR под мониторингом
sequential_ab_testing.py Sequential rule Эвана Миллера воспроизводит таблицу размеров, валидирует Type I/power, экономию выборки
bayesian_ab_test.py Analytic Bayesian A/B Beta-Binomial / Normal-Normal, P(B>A), expected loss, ROPE
bootstrap_ci.py Bootstrap CI percentile и BCa для скошенных метрик
heterogeneous_treatment_effects.py HTE по сегментам interaction-модель ловит Simpson-подобное зануление
multiple_comparisons.py Множественное тестирование Bonferroni (FWER) vs Benjamini-Hochberg (FDR)
novelty_primacy.py Time-varying effects treat×day interaction ловит novelty decay / primacy growth
switchback.py Cluster & switchback cluster-robust SE; carryover bias
test_simulator.py Generic calibration любой DGP + тест → эмпирические Type I и power

Сквозной пайплайн

scripts/run_full_pipeline.py связывает модули в один реалистичный сценарий на синтетических данных: SRM check → CUPED → delta-method CTR test → per-segment ATE с BH-коррекцией → novelty check → markdown-отчёт в outputs/report.md.

Запуск

uv sync --all-groups
uv run pytest                 # calibration test suite
uv run python scripts/run_full_pipeline.py   # end-to-end demo → outputs/report.md

Результат

Тестовый набор перепрогоняет каждую калибровку с ассертами:

  • Type I error ≈ α (± tolerance) для каждого метода под его нулевой гипотезой;
  • покрытие CI ≈ 95% для bootstrap;
  • наивный peeking раздувает Type I, always-valid / alpha-spending контролируют его;
  • наивный per-unit SE для ratio-метрик неточен, delta-method точен;
  • корректность на known-answer фикстурах (SRM splits, сегментные uplift-ы).

Ограничения

Числа получены на симуляциях с заданным DGP, а не на прод-трафике: калибровка подтверждает корректность реализации, но не гарантирует, что реальные данные удовлетворяют её допущениям.

Это набор инструментов, а не замена продуманному дизайну эксперимента.

Документация

Аналитика

Данные: github.com/NikitaBoyarkin/ab_test: numbers re-produced by running scripts/group_sequential.py, scripts/msprt_always_valid.py, scripts/sequential_ab_testing.py and scripts/run_full_pipeline.py under `uv run python` (reproducible; plots/ and outputs/ are gitignored)

Ошибка I рода при промежуточных просмотрах

Эмпирическая вероятность ложноположительного срабатывания за K=5 промежуточных проверок (40000 симуляций нулевой гипотезы). Наивный повторный z-тест с границей 1.96 на каждом просмотре против Pocock и O'Brien–Fleming, где границы калибруются на суммарный alpha=5%.

0 5 10 15 Naive peeking — Type I error: 14.1 14.1 Pocock — Type I error: 4.9 4.9 OBF — Type I error: 4.9 4.9 Naive peeking Pocock OBF Дизайн Эмпирическая ошибка I рода, %
Выводы
  • Проверка данных на каждом из 5 просмотров с границей 1.96 раздувает ошибку I рода до 14.1% — почти в 3 раза выше номинальных 5%.
  • Альфа-спендинг возвращает контроль: и Pocock, и OBF держат ошибку на 4.9%, ниже номинальных 5%.

Ложные срабатывания при непрерывном подглядывании

Вероятность хоть раз получить p ≤ 0.05 за поток наблюдений t=1..500 при нулевой гипотезе (2000 потоков). Обычный z-тест пересчитывается на каждом шаге; always-valid p-значение mSPRT (Johari et al. 2015) гарантирует корректность в любой момент остановки.

0 20 40 60 Naive z-test — False-positive rate: 47.8 47.8 mSPRT always-valid — False-positive rate: 3.2 3.2 Naive z-test mSPRT always-valid Метод P(хоть раз p ≤ 0.05), %
Выводы
  • Пересчёт наивного z-теста на каждом наблюдении даёт ложноположительное срабатывание в 47.8% потоков — это почти каждая вторая «победа» оказывается шумом.
  • Always-valid p-значение mSPRT снижает этот риск до 3.2%, то есть ниже номинальных 5%, и при этом позволяет остановиться в любой момент.

Средний размер выборки секвенциального теста

Правило Эвана Миллера (N=808 успехов, отрыв 56) против фиксированного горизонта: средний размер выборки секвенциального теста в % от фиксированного. По 1000 испытаний на кейс, базовый rate 1%, MDE +20%.

0 50 100 150 A/A (0%) — Avg sample size vs fixed: 118.6 118.6 A/B (+20%) — Avg sample size vs fixed: 76.3 76.3 Blockbuster (+40%) — Avg sample size vs fixed: 41.8 41.8 A/A (0%) A/B (+20%) Blockbuster (+40%) Тип испытания Средний размер выборки, % от фиксированного
Выводы
  • При реальном эффекте +20% секвенциальный тест завершается в среднем на 76.3% выборки фиксированного дизайна, а при +40% — всего на 41.8%.
  • При отсутствии эффекта (A/A) секвенциальный тест в среднем расходует 118.6% выборки — экономия появляется только при реальном эффекте.

Экономия выборки в зависимости от MDE

Экономия секвенциального правила по сравнению с фиксированным горизонтом при базовом rate 1% (сетка по MDE, 1000 испытаний на точку). Расчёт: (ожидаемые успехи фиксированного дизайна − средние успехи секвенциального) / фиксированные.

0 5 10 15 20 Savings 0.1 — Savings: 20 0.2 — Savings: 14 0.5 — Savings: 1 0.1 0.2 0.5 Минимальный детектируемый эффект (относительный) Экономия выборки, %
Выводы
  • При MDE 10% секвенциальный тест экономит 20% выборки, при MDE 20% — 14%, а при MDE 50% экономия падает до 1%.
  • Экономия монотонно убывает с размером эффекта: от 20% при MDE 10% до 1% при MDE 50% — правило выгодно для малых, долгих тестов, а не для крупных эффектов.

Смотрите также

Карта связей

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