статистикамножественные сравненияA/B-тесты

Множественные сравнения: поправки Бонферрони и Бенджамини-Хохберга

2026-07-11 10 мин

Если в одном A/B-тесте ты смотришь не на одну метрику, а сразу на десять, то почти наверняка найдёшь «значимый» эффект там, где на самом деле ничего не изменилось. Это и есть проблема множественных сравнений: при фиксированном пороге alpha каждый новый тест добавляет свой шанс на ложное срабатывание, и суммарная вероятность ошибки растёт быстро. Лечится это поправками. Бонферрони действует в лоб — делит порог на число тестов и потому строг до консервативности. Бенджамини-Хохберг (BH) мягче: он контролирует не «хотя бы одну ошибку», а долю ошибок среди тех гипотез, что ты отверг. Ниже разберу, когда какую брать, и покажу на реальном наборе метрик.

Почему при множестве тестов растёт вероятность ложного открытия?

Одиночный тест на уровне alpha = 0.05 означает простую вещь: если верна нулевая гипотеза (эффекта нет), ты всё равно объявишь его существующим примерно в 5% случаев. Это встроенная плата за метод. Проблема в том, что 5% — это на один тест.

Проверяешь m независимых гипотез — и вероятность не ошибиться ни разу равна 0.95 в степени m. Значит, вероятность хотя бы одной ложной находки — это 1 - 0.95^m. Считается в две строчки:

alpha = 0.05
for m in [1, 5, 10, 20]:
    fwer = 1 - (1 - alpha) ** m
    print(m, round(fwer, 3))
# 1  0.05
# 5  0.226
# 10 0.401
# 20 0.642

Для 10 метрик это уже 40%, для 20 — около 64%. То есть на дашборде из двадцати метрик (от DAU до конверсии и выручки) почти наверняка что-то «позеленеет» просто от шума. И это ещё оптимистичная оценка: продуктовые метрики почти всегда скоррелированы — конверсия, ARPU и retention тянут друг друга, — поэтому реальная картина сложнее, но суть та же. Чем больше смотришь, тем выше риск принять шум за сигнал.

Что такое FWER и FDR и чем они отличаются?

Прежде чем выбирать поправку, нужно решить, что именно ты держишь под контролем. Есть две разные величины, и половина путаницы вокруг темы — от того, что их смешивают.

FWER (family-wise error rate) — вероятность совершить хотя бы одну ошибку первого рода во всём семействе тестов. Держишь FWER на уровне 0.05 — значит, с вероятностью 95% среди всех твоих выводов нет ни одной ложной находки. Гарантия строгая: одна ошибка на всю пачку уже считается провалом.

FDR (false discovery rate) — ожидаемая доля ложных открытий среди отвергнутых гипотез. Держишь FDR на 0.05 — значит, из тех метрик, что ты назвал значимыми, в среднем около 5% окажутся ложными. Здесь ты заранее миришься с небольшим процентом мусора ради того, чтобы не пропустить настоящие эффекты.

Разница чисто практическая. FWER уместен, когда цена ложного вывода высока и на каждом «открытии» строится решение. FDR — когда ты делаешь скрининг, гипотез много, и потерять реальный эффект хуже, чем протащить пару ложных. Запомнить легко: Бонферрони контролирует FWER, BH контролирует FDR.

Как работает поправка Бонферрони?

Самая простая поправка из всех. Хочешь удержать FWER на уровне alpha при m тестах — сравнивай каждый p-value не с alpha, а с alpha / m. Для пяти метрик и alpha = 0.05 порог становится 0.01. Эквивалентно можно умножить сами p-value на m и сравнивать с исходным alpha — это и есть «скорректированный» p-value.

Почему это работает: сумма вероятностей отдельных ошибок не превышает m * (alpha / m) = alpha. Метод не требует независимости тестов — он справедлив вообще всегда, при любой структуре зависимостей. В этом его сила и повод любить его за надёжность. Но за универсальность платишь мощностью: при большом m порог становится настолько жёстким, что реальные, но умеренные эффекты просто не проходят.

Когда Бонферрони слишком строгая?

Проблема вылезает, когда тестов десятки. Порог alpha / m не различает, скоррелированы гипотезы или нет, и потому переусердствует, если метрики связаны — а в продукте они связаны почти всегда. В итоге ты защищаешься от ложных находок ценой пропущенных настоящих: статистическая мощность падает, и часть реальных улучшений остаётся «незначимой». Для скрининга это плохой размен.

Если FWER всё-таки нужен, но Бонферрони душит, посмотри на метод Холма — это пошаговая (step-down) версия. p-value сортируются по возрастанию, самый маленький сравнивается с alpha / m, следующий с alpha / (m - 1), и так далее, пока не встретится первый непройденный порог. Холм даёт ровно ту же гарантию FWER, но отвергает не меньше гипотез, чем Бонферрони, а обычно больше. Если ты уже тянешься к Бонферрони — Холм почти всегда строго лучше и не стоит ничего лишнего.

Как устроена процедура Бенджамини-Хохберга?

BH меняет саму цель: не «ноль ошибок с вероятностью 95%», а «в среднем не больше 5% мусора среди находок». Алгоритм пошаговый и считается руками за минуту:

Ключевой момент — шаг 4: BH отвергает всё до найденной границы разом. Метод контролирует FDR при независимых тестах и при так называемой положительной зависимости — а именно она обычно и есть у продуктовых метрик. Поэтому на практике BH применяют смело и без лишней паранойи.

Пример: пять метрик в одном A/B-тесте

Возьмём один тест новой формы онбординга. Мы заранее договорились смотреть пять метрик и получили такие p-value:

Сами метрики по группам считаются обычным SQL — это удобно отрепетировать в SQL-тренажёре:

select
    variant,
    count(*)                        as users,
    avg((converted = 1)::int)       as conversion_rate,
    avg(revenue)                    as arpu
from experiment_users
group by variant;

А дальше сравним три подхода — без поправки, Бонферрони и BH. Руками считать необязательно, всё есть в statsmodels:

import pandas as pd
from statsmodels.stats.multitest import multipletests

pvals = pd.Series({
    'conversion_rate':  0.008,
    'arpu':             0.018,
    'retention_d7':     0.025,
    'avg_session_len':  0.028,
    'support_contacts': 0.620,
})

res = pd.DataFrame({'p': pvals})
for method in ['bonferroni', 'fdr_bh']:
    reject, p_adj, _, _ = multipletests(pvals, alpha=0.05, method=method)
    res[f'{method}_p_adj'] = p_adj.round(3)
    res[f'{method}_sig']   = reject

print(res)

Что получается:

Разница наглядная: на одних и тех же данных Бонферрони находит один эффект, BH — четыре. Кто «прав», зависит от контекста. Если это финальное решение о раскатке и ошибка дорога — жёсткость Бонферрони оправдана. Если это исследовательский прогон, и победителей ты всё равно перепроверишь отдельным подтверждающим тестом — BH даёт меньше ложных «незначимо» и не даёт выбросить реальные улучшения. Прокрутить этот расчёт на своих числах можно в Python-тренажёре.

Бонферрони или BH — что выбрать?

Короткое правило, которым пользуюсь сам:

Последний пункт важнее первых двух. Самая надёжная защита от проблемы множественных сравнений — не поправка, а дисциплина: одна главная метрика, объявленная до эксперимента. Поправки нужны там, где без множественности реально не обойтись, а не как способ задним числом спасти отчёт.

Частые ошибки при множественных сравнениях

Несколько граблей, на которые я насмотрелся:

Как закрепить это на практике?

Теорию про FWER и FDR любят спрашивать на собеседованиях аналитиков — формулировка «у тебя 20 метрик в тесте, как не обмануться» звучит почти дословно. Чтобы это осело, лучше один раз прогнать руками: посчитать метрики по группам в SQL-тренажёре, прокрутить multipletests на своих p-value, разобрать пару похожих ситуаций в кейсах и порешать задачи по статистике. Если хочется системно — от базовых запросов в курсе SQL до типичных вопросов на собеседованиях.

Полный доступ ко всем задачам, кейсам и AI-разбору собеседований открывается в Pro, но начать можно бесплатно — и Бонферрони с BH точно попадут в бесплатную часть тренажёра.

Готовься к собесу аналитика
Вопросы по статистике и A/B с разбором — попробуй бесплатно.
Банк вопросов →