A/B-тестыбайесовский подходстатистика

Байесовский A/B-тест vs частотный: в чём разница на практике

2026-07-11 12 мин

Короткий ответ: частотный тест отвечает на вопрос «насколько странными были бы мои данные, если бы B на самом деле не отличался от A» и выдаёт p-value. Байесовский отвечает прямо на тот вопрос, который задаёт продакт: «какова вероятность, что B лучше A, и сколько я теряю, если ошибусь». Это две разные логики, а не «новая и старая». Ниже разберу обе руками, посчитаю конверсию через Beta-Binomial и покажу, где какая удобнее.

В чём разница между частотным и байесовским A/B-тестом?

Частотный подход считает истинную конверсию фиксированным, но неизвестным числом. Данные — случайны, гипотеза — нет. Ты формулируешь нулевую гипотезу «разницы нет», собираешь данные и спрашиваешь: если бы разницы правда не было, как часто случайность нарисовала бы отличие не меньше моего? Это и есть p-value. Маленькое p-value — «такое совпадение было бы редким, значит скорее всего разница есть», и ты отвергаешь H0.

Байесовский подход переворачивает картину. Здесь неизвестная конверсия — это распределение (наша неуверенность), а данные — то, что уже случилось и зафиксировано. Ты берёшь начальное убеждение (приор), обновляешь его данными и получаешь апостериорное распределение конверсии. По нему считаешь напрямую: P(конверсия B > конверсия A) и ожидаемые потери от каждого решения.

Разница не косметическая. Частотный тест не может сказать «вероятность, что B лучше — 96%», хотя именно так его сплошь и рядом трактуют. Байесовский — может, потому что для него вероятность гипотезы это законная величина.

Почему p-value почти все понимают неправильно?

Классическая ошибка: «p-value = 0.03, значит вероятность, что B лучше — 97%». Это неверно. P-value — это P(данные такие или ещё более крайние | H0 истинна), а не P(H0 ложна | данные). Условие стоит не с той стороны.

Ещё p-value ничего не говорит о размере эффекта. При выборке в миллион человек статистически значимым станет прирост конверсии на 0.01 процентного пункта, который бизнесу безразличен. И наоборот — на маленькой выборке реальный, но скромный по данным эффект даст p-value 0.2, и ты его «не заметишь».

И третье, самое дорогое в проде: p-value честен ровно один раз — на заранее зафиксированном размере выборки. Если подглядывать каждый день и останавливаться, как только увидел p < 0.05, доля ложных срабатываний уползает с обещанных 5% к 20-30%. Про эту ловушку я подробнее писал в разборе A/B через scipy.stats — там же лежат задачи на t-тест, хи-квадрат и бутстрап, чтобы прочувствовать это на данных.

Что такое апостериорное распределение простыми словами?

Представь, что до эксперимента ты честно говоришь: «конверсия лендинга где-то между 2% и 8%, но точнее не знаю». Это твой приор — распределение возможных значений. Потом приходят данные: 4820 визитов, 241 конверсия. Данные сдвигают и сужают это распределение — теперь ты уверен, что конверсия около 5%, плюс-минус чуть-чуть. Вот это обновлённое распределение и есть апостериорное (posterior).

Апостериорное распределение — это полный ответ байесовского теста. Не одно число, а вся кривая: где конверсия скорее всего, а где почти наверняка нет. Из неё уже вытягиваешь что угодно — среднее, 95%-й интервал, вероятность превысить порог. В отличие от точечной оценки 241/4820, апостериорное сразу несёт в себе неопределённость: на 100 наблюдениях кривая широкая, на 100 000 — узкий пик.

Beta-Binomial на пальцах: считаем конверсию

Для бинарного исхода (конвертнулся / нет) есть подарок судьбы — сопряжённость Beta и биномиального распределения. Если приор для конверсии задан как Beta(alpha, beta), а данные — это s успехов и f провалов, то апостериорное распределение считается без интегралов, в одну строку: Beta(alpha + s, beta + f).

Интуиция за параметрами простая: alpha ведёт себя как «сколько конверсий мы видели», beta — «сколько неконверсий». Приор Beta(1, 1) — это ровная линия от 0 до 1, то есть «любая конверсия равновероятна, ничего не знаю». Наблюдал 241 конверсию из 4820 — апостериор становится Beta(1+241, 1+4579), узкий колокол около 5%.

import numpy as np
from scipy import stats

# Данные эксперимента: визиты и конверсии по двум вариантам
n_a, conv_a = 4820, 241   # контроль
n_b, conv_b = 4790, 289   # новый вариант

# Приор Beta(1, 1) — равномерный, "заранее ничего не знаем"
alpha0, beta0 = 1, 1

# Апостериорные распределения конверсии
post_a = stats.beta(alpha0 + conv_a, beta0 + n_a - conv_a)
post_b = stats.beta(alpha0 + conv_b, beta0 + n_b - conv_b)

print(f"A: конверсия ~ {post_a.mean():.4f}, "
      f"95% интервал {post_a.ppf(0.025):.4f}–{post_a.ppf(0.975):.4f}")
print(f"B: конверсия ~ {post_b.mean():.4f}, "
      f"95% интервал {post_b.ppf(0.025):.4f}–{post_b.ppf(0.975):.4f}")

Уже на этом шаге видно перекрытие интервалов: контроль около 5.0%, вариант около 6.0%, но хвосты налезают друг на друга. Частотный тест здесь сказал бы «разница есть, p чуть меньше 0.05» — а байесовский честно покажет, насколько мы всё ещё не уверены.

Как узнать вероятность, что B лучше A?

Вот ради чего всё затевалось. У нас есть два апостериорных распределения. Вопрос «какова вероятность, что B лучше A» — это P(конверсия_B > конверсия_A). Аналитически это интеграл, но проще и нагляднее посчитать симуляцией: натянуть много точек из обоих распределений и посмотреть, в какой доле случаев B оказался выше.

rng = np.random.default_rng(42)

# Тянем по 200 000 сэмплов из каждого апостериорного распределения
samples_a = post_a.rvs(200_000, random_state=rng)
samples_b = post_b.rvs(200_000, random_state=rng)

# Вероятность, что B лучше A
prob_b_better = (samples_b > samples_a).mean()
print(f"P(B > A) = {prob_b_better:.3f}")

# Насколько именно лучше — распределение прироста
uplift = (samples_b - samples_a) / samples_a
print(f"Медианный относительный прирост = {np.median(uplift)*100:.1f}%")

На этих данных получается примерно P(B > A) = 0.96. Читается это ровно так, как звучит: с вероятностью 96% новый вариант конвертит лучше. Никаких «отвергаем нулевую гипотезу на уровне значимости», никакого объяснения продакту, что такое ошибка первого рода. Просто «на 96% уверены, что выкатывать B — правильно».

Заметь второй кусок кода: мы получаем не только «лучше / хуже», но и распределение самого прироста. Можно спросить P(прирост > 5%) или P(прирост > 0) — байесовский аппарат отвечает на любой из этих вопросов из того же самого posterior. Для сравнения, частотный тест дал бы одно p-value и один доверительный интервал, а всё остальное пришлось бы досчитывать отдельно.

Что такое ожидаемые потери и почему это удобнее p-value?

P(B > A) = 0.96 звучит убедительно, но 96% — это не 100%. Остаётся 4% миров, где B на самом деле хуже, и мы, выкатив его, теряем деньги. Байесовский подход умеет оценить, сколько именно мы теряем в среднем. Это ожидаемые потери (expected loss).

Логика: если мы выберем B, но истина в том, что A лучше, наша потеря равна конверсия_A − конверсия_B. Если B и правда лучше — потеря ноль (мы приняли верное решение). Усредняем эту «потерю» по всему апостериорному распределению:

# Ожидаемые потери, если выкатим B вместо A (в процентных пунктах)
loss_b = np.maximum(samples_a - samples_b, 0).mean()
# Ожидаемые потери, если оставим A
loss_a = np.maximum(samples_b - samples_a, 0).mean()

print(f"Ожидаемые потери от выбора B = {loss_b*100:.4f} п.п.")
print(f"Ожидаемые потери от выбора A = {loss_a*100:.4f} п.п.")

Тут вся прелесть. Ожидаемые потери — в тех же единицах, что и метрика (процентные пункты конверсии), их видит и понимает не только аналитик. Правило остановки становится бизнесовым, а не статистическим: «останавливаем тест, когда ожидаемые потери от выкатки лучшего варианта меньше порога» — например, меньше 0.01 п.п. Ты сам решаешь, какой риск приемлем, вместо того чтобы молиться на магическое 0.05. Если хочешь связать это с деньгами — умножь ожидаемые потери на трафик и средний чек и получишь потерю в рублях. Как раз такой мостик от статистики к деньгам ждут на собеседовании — набор типичных вопросов есть в разделе вопросов по A/B и статистике.

Можно ли останавливать байесовский тест раньше?

Это главный практический плюс. У частотного теста, как я говорил, подглядывание ломает контроль ошибок — нужны поправки вроде sequential testing или заранее посчитанный фиксированный размер выборки. Байесовская величина P(B > A) и ожидаемые потери такого запрета не накладывают: они не про «как часто ошибётся процедура при повторении эксперимента», а про «что говорят данные, которые уже есть». Посмотреть на них можно хоть каждый час.

Оговорка, чтобы не переврать: байесовский тест не отменяет статистику полностью. Если останавливаться ровно в тот момент, когда P(B > A) первый раз перепрыгнуло 95%, доля ложных выкаток тоже подрастёт по сравнению с фиксированной выборкой — просто не так драматично, и её поведение управляется приором и порогом потерь. Практический рецепт: не гнаться за первым касанием порога, а требовать, чтобы ожидаемые потери устойчиво держались ниже границы. Тогда ранняя остановка работает честно и экономит недели трафика на очевидно выигрышных и очевидно проигрышных вариантах.

В чём минусы байесовского подхода?

Первый и главный — приор. Ты обязан что-то заявить о конверсии до эксперимента, и этот выбор влияет на результат, особенно на малых данных. Beta(1, 1) кажется нейтральным, но на выборке в 50 человек он всё равно тянет оценку к 50%, что для конверсии абсурдно. На больших выборках приор быстро «вымывается» данными и почти ни на что не влияет, а вот на маленьких может стать причиной спора «а почему ты взял именно такой приор». Ответ — брать слабо информативный приор из исторических данных (средняя конверсия по продукту) и проверять устойчивость: посчитать результат с парой разных приоров и убедиться, что вывод не переворачивается.

Второй минус — вычисления и объяснимость внутри команды. Beta-Binomial для конверсий считается элементарно, но стоит перейти к выручке, ARPU или времени на сайте — сопряжённость ломается, и нужны либо численные методы (MCMC), либо бутстрап-обёртки. Плюс коллеги, привыкшие к p-value, поначалу не доверяют «вероятности гипотезы» и просят «а покажи всё-таки значимость». Это вопрос культуры, а не математики, но время он отнимает.

Третий момент — легко себя обмануть ощущением уверенности. P(B > A) = 0.96 подкупает простотой, и хочется остановиться пораньше. Дисциплина с порогом ожидаемых потерь тут обязательна, иначе получишь те же грабли подглядывания, только с другой стороны.

Когда выбирать байесовский подход, а когда частотный?

Частотный подход бери, когда у тебя фиксированный размер выборки, культура компании завязана на классические A/B-платформы, и нужен воспроизводимый, всеми принятый протокол — «посчитали MDE, набрали выборку, проверили гипотезу». Он же проще защищается перед аудитом и регуляторикой: методология стандартная, ошибки контролируются по учебнику. Для базового t-теста и хи-квадрата на бинарных и непрерывных метриках его за глаза хватает.

Байесовский бери, когда важна ранняя остановка и экономия трафика, когда решение принимает бизнес и ему нужен ответ в терминах «вероятность и потери в рублях», а не «p-value 0.04». Он особенно хорош для многоруких бандитов, для последовательного набора данных и для ситуаций, где эффект скромный и нужно честно показать неопределённость, а не прятать её за бинарным «значимо / незначимо».

На практике я держу оба в голове: считаю классический z-тест как sanity-check и рядом — P(B > A) с ожидаемыми потерями для разговора с продактом. Они почти всегда сходятся в выводе, а расходятся ровно там, где интересно — на границе, где одно число решает выкатку.

Хочешь отработать оба подхода на реальных данных, а не на теории — в Python-тренажёре есть блок задач на A/B и статистику с автопроверкой кода, а быстрые формулы конверсии и агрегатов удобно подсмотреть в Python-справочнике. Метрики, по которым обычно и гоняют тесты — DAU и конверсию — я разбираю с формулами отдельно, а типовые продуктовые ситуации с A/B лежат в кейсах и тестовых заданиях. Первые задачи в каждом разделе открыты бесплатно; полный доступ ко всем задачам, кейсам и AI-разбору собеседований — в Pro.

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