Эксперименты

A/B-тестирование: как не обмануть себя результатом

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

A/B-тест — это контролируемый эксперимент, а не «сравнение до и после». Случайное распределение пользователей по вариантам в среднем уравнивает все факторы — и те, что вы измеряете, и те, о которых не догадываетесь. Именно поэтому разница средних по целевой метрике даёт несмещённую оценку причинного эффекта1, тогда как наблюдательное сравнение путает эффект изменения с трендом, сезонностью и сменой аудитории.

Главное: A/B-тест отвечает на вопрос, только если метрика, размер выборки и горизонт остановки зафиксированы до запуска. «Победа», найденная подглядыванием или перебором метрик и сегментов, — чаще всего статистический шум.

Что измеряет A/B-тест

Перед запуском фиксируют первичную метрику (OEC, overall evaluation criterion23) — ту единственную величину, по которой принимается решение, — и формулируют гипотезу. OEC должна отражать долгосрочную ценность, а не сиюминутный клик: метрика, которую легко «накрутить» изменением, но которая не связана с удержанием и выручкой, уводит в ложную оптимизацию. Рядом задают guardrail-метрики — показатели, которые изменение не должно ухудшить (скорость загрузки, отток, жалобы), даже если первичная метрика выросла.

Важная техническая тонкость: единица рандомизации должна совпадать с единицей анализа. Если делите по пользователям, а считаете метрику по сессиям или событиям, наблюдения внутри одного пользователя коррелированы, обычная формула дисперсии их недооценивает, а доверительные интервалы оказываются ложно узкими. Это завышает число «значимых» результатов ещё до всякого подглядывания.

Сначала мощность, потом запуск

Тест без достаточной мощности4 не способен обнаружить эффект, даже когда тот реален: он почти обречён вернуть «не значимо». Поэтому до запуска фиксируют три величины: уровень значимости (обычно двусторонний α = 5%), мощность (обычно 1 − β = 80%) и MDE — минимальный эффект, который вообще имеет смысл ловить. MDE — это бизнес-порог («какой прирост окупит внедрение»), а не «любой ненулевой».

1 000 10 000 100 000 1 000 000 1% 5% 10% 15% 20% 25% 30% ≈ 31 000/вариант Мощность: 80% 90% базовая конверсия 5%, двусторонний α = 5%. Меньше эффект — кратно больше выборка. минимально различимый эффект (относит. прирост конверсии) наблюдений на вариант (log)
Рис. 1. Размер выборки против минимально различимого эффекта (MDE). Чтобы надёжно поймать меньший эффект, нужна кратно большая выборка: зависимость обратно-квадратичная. При базовой конверсии 5% обнаружение относительного прироста в 10% требует ≈ 31 000 наблюдений на вариант, а 5% — уже вчетверо больше. Поэтому размер выборки и срок теста считают до запуска, а не подгоняют после. Аналитический расчёт по формуле для двух долей · α=5%, мощность 80/90%.

Зависимость размера выборки от MDE обратно-квадратичная: чтобы надёжно поймать вдвое меньший эффект, нужно вчетверо больше наблюдений. Для двух долей размер выборки на вариант считается напрямую:

import numpy as np
from scipy.stats import norm

def sample_size_per_arm(p0, mde_rel, alpha=0.05, power=0.8):
    p1 = p0
    p2 = p0 * (1 + mde_rel)          # MDE как относительный прирост
    z_a = norm.ppf(1 - alpha / 2)    # двусторонний
    z_b = norm.ppf(power)
    p_bar = (p1 + p2) / 2
    num = (z_a * np.sqrt(2 * p_bar * (1 - p_bar))
           + z_b * np.sqrt(p1 * (1 - p1) + p2 * (1 - p2))) ** 2
    return int(np.ceil(num / (p2 - p1) ** 2))

n = sample_size_per_arm(p0=0.05, mde_rel=0.10)   # конверсия 5%, MDE +10%
print(f"Нужно ≈ {n:,} наблюдений на вариант")     # ≈ 31 234

Если на разумный MDE выборки не хватит за приемлемый срок, тест бессмысленно запускать — это сигнал поменять метрику на более чувствительную, увеличить эффект изменения или снизить дисперсию (см. CUPED ниже), а не «посмотреть, что получится».

Проблема подглядывания

Самая частая и самая дорогая ошибка. Соблазн понятен: дашборд обновляется в реальном времени, и хочется остановить тест, как только увидел p < 0.05. Но многократная проверка накапливающихся данных раздувает вероятность ложного срабатывания: при фиксированном пороге она уходит далеко за номинальные 5%5. Под нулевой гипотезой (эффекта нет) z-статистика блуждает случайно и рано или поздно пересечёт порог — «значимость» гарантирована любому, кто готов ждать и подглядывать.

0% 5% 10% 20% 30% 40% 1 2 5 10 20 50 89 номинальные 5% (фикс. горизонт / always-valid) 37% наивный тест при каждом взгляде с поправкой Эффекта НЕТ (H₀), но при 89 проверках «значимость» находится в ~37% тестов. число промежуточных проверок «на значимость» (log) доля ложных «побед»
Рис. 2. Подглядывание раздувает ложные «победы». Эффекта нет (верна H₀), но если останавливать тест при первом же «p < 0.05», то с ростом числа промежуточных проверок вероятность ложного срабатывания уходит далеко за номинальные 5%. Лечится фиксированным горизонтом, групповыми последовательными границами (Покок, O’Brien–Fleming) или always-valid p-значениями. Симуляция Монте-Карло под H₀ (60 000 прогонов, фикс. seed) · для наглядности.
import numpy as np
from scipy.stats import norm

rng = np.random.default_rng(0)
z_crit = norm.ppf(0.975)                 # 1.96

def false_positive_rate(n_looks, n_max=10_000, sims=40_000):
    step = n_max / n_looks
    # под H0 z-статистика на n наблюдениях ~ случайное блуждание / sqrt(n)
    blocks = rng.normal(0, np.sqrt(step), size=(sims, n_looks))
    z = np.cumsum(blocks, axis=1) / np.sqrt(step * np.arange(1, n_looks + 1))
    reject = (np.abs(z) > z_crit).any(axis=1)   # остановка на первом p<0.05
    return reject.mean()

for k in (1, 5, 20, 89):
    print(f"{k:>3} проверок → ложных побед: {false_positive_rate(k):.1%}")
# 1 → 5.1% · 5 → 14.2% · 20 → 25.3% · 89 → 36.9%

Лечится это тремя способами. Первый — фиксированный горизонт: посчитать размер выборки заранее и не принимать решение до его достижения. Второй — последовательный анализ, восходящий к критерию отношения вероятностей Вальда6: групповые последовательные границы Покока7 (равномерно строже на каждой промежуточной проверке) и O’Brien–Fleming8 (очень строго в начале, почти номинальный порог к финалу) корректируют критическое значение так, чтобы суммарная ошибка осталась на уровне 5%. Третий — always-valid p-значения9 (и доверительные интервалы), которые позволяют смотреть на результат сколько угодно часто и останавливаться в любой момент, не раздувая ошибку. Если бизнесу нужен непрерывный мониторинг — это правильный инструмент, а не наивный ежедневный t-тест.

Множество метрик и сегментов

Перебор метрик — та же проблема подглядывания, только по другой оси. Проверяя 20 метрик при α = 5%, вы в среднем получите одну ложную «значимость» даже при полном отсутствии эффекта. Решения: заранее назначить одну первичную метрику (а остальные считать диагностическими), либо контролировать долю ложных открытий (FDR)10 поправкой на множественность.

То же касается сегментов. Если резать данные постфактум — по устройствам, странам, когортам — и искать, «где сработало», что-нибудь «значимое» найдётся всегда: это «сад расходящихся троп». Отдельная ловушка — парадокс Симпсона11: эффект, положительный в каждом сегменте по отдельности, при агрегации может стать отрицательным (и наоборот), если состав групп по сегментам несбалансирован. Поэтому сегментный разрез — инструмент для гипотез к следующему тесту, а не для выводов из текущего; набор сегментов фиксируют до запуска.

Доверять ли результату: читаем интервал

«Победа» — это не факт p < 0.05, а доверительный интервал прироста. Он сразу отвечает на два вопроса: отличается ли эффект от нуля и — что важнее — достаточно ли он велик, чтобы окупить внедрение.

-8% -6% -4% -2% +0% +2% +4% +6% +8% +10% +12% нет эффекта порог практич. значимости (+2%) Вариант A значимый рост Вариант B неубедительно (CI пересекает 0) Вариант C значим, но ниже порога Вариант D значимое падение Вариант E неубедительно (CI пересекает 0) оценка прироста и 95% доверительный интервал
Рис. 3. Как читать результат: значимость ≠ важность. «Победа» — это не сам факт p < 0.05, а доверительный интервал прироста. Вариант A — значимый рост; B и E — неубедительны (CI пересекает 0, эффект не доказан); C — статистически значим, но весь интервал ниже порога практической значимости (роста, который окупит внедрение, нет); D — значимое падение, катить нельзя. Условный пример · числа для наглядности.

Отсюда два правила, которые экономят больше всего ошибочных решений. Статистическая значимость — не то же самое, что практическая. На миллионной выборке «значимым» становится и прирост в 0.2%, который не покрывает стоимость внедрения; поэтому интервал сравнивают с порогом практической значимости, а не только с нулём. И обратное: отсутствие значимости — не доказательство отсутствия эффекта, особенно при недоборе мощности; широкий интервал, накрывающий и ноль, и крупный эффект, означает «данных не хватило», а не «эффекта нет».

Что ломает A/B на практике

Даже корректно спланированный тест может оказаться недействительным. Первое, что проверяют, — SRM12 (sample ratio mismatch): если деление задумано как 50/50, а наблюдаемые размеры групп значимо разошлись, значит сломано само назначение (баг разметки, бот-трафик, перекос редиректов), и любым метрикам верить нельзя. Простая проверка — критерий χ²:

from scipy.stats import chisquare

def srm_check(n_control, n_treatment, split=(0.5, 0.5)):
    total = n_control + n_treatment
    observed = [n_control, n_treatment]
    expected = [total * split[0], total * split[1]]
    stat, p = chisquare(observed, expected)
    return p

p = srm_check(50_800, 49_200)            # ожидали 50/50
print(f"SRM p-value: {p:.2e}")             # ≈ 4e-07 → деление сломано, тест недействителен

Дальше — типовые источники смещения. Метрики-отношения (клики на сессию), когда единица анализа крупнее единицы рандомизации, требуют дельта-метода для корректной дисперсии. Эффекты новизны и привыкания: поведение в первые дни нерепрезентативно — нужно смотреть динамику эффекта, а не только среднее. Интерференция: в соцсетях и маркетплейсах группы влияют друг на друга, и обычный пользовательский A/B смещён — нужен кластерный рандомизированный дизайн. Наконец, чувствительность теста можно повысить без увеличения выборки — метод CUPED13 снижает дисперсию метрики, вычитая предсказуемую часть по данным до эксперимента, и тем сокращает нужный срок при той же мощности.

Чек-лист перед запуском

  • Метрика и гипотеза зафиксированы. Одна первичная метрика (OEC) и guardrail-метрики выбраны до старта.
  • Мощность посчитана. Известны MDE, α, мощность и вытекающие из них размер выборки и срок.
  • Горизонт остановки назначен. Либо фиксированный размер, либо явный метод последовательного анализа — но не «смотрим, пока не станет значимо».
  • SRM под контролем. Проверка баланса групп включена в мониторинг.
  • План анализа сегментов задан заранее. Разрезы — для гипотез, а не для поиска значимости постфактум.
  • Решение принимается по интервалу. Сравнение с порогом практической значимости, а не только с нулём.

Как с этим работает StatGazer

A/B-тест — это не «поделить трафик пополам», а методологический выбор: метрика, мощность, горизонт, дизайн и план анализа. Мы проектируем эксперименты под конкретное решение на стороне бизнес-аналитики, а если тест уже проведён — выполняем его независимую проверку и валидацию: воспроизводим расчёт, проверяем подглядывание, SRM, множественность метрик и границы выводов. Никаких обещаний по исходу здесь нет — задача в том, чтобы решение опиралось на корректный эксперимент, а не на статистический шум. Описать задачу можно в контактах — ответим в течение 24 часов.

Источники

Ключевые работы по дизайну экспериментов, мощности, последовательному анализу и онлайн-A/B, упомянутые в статье. Номера-сноски в тексте ссылаются на этот список.

  1. Fisher, R. A. (1935). The Design of Experiments (1st ed.). Edinburgh: Oliver & Boyd. en.wikipedia.org. — вводит рандомизацию, повторность и блокирование как основу причинного вывода в эксперименте.
  2. Kohavi, R., Longbotham, R., Sommerfield, D., & Henne, R. M. (2009). Controlled Experiments on the Web: Survey and Practical Guide. Data Mining and Knowledge Discovery, 18(1), 140–181. doi:10.1007/s10618-008-0114-1. — обзор и практическое руководство по онлайн-A/B; понятие OEC и контроль качества эксперимента.
  3. Kohavi, R., Tang, D., & Xu, Y. (2020). Trustworthy Online Controlled Experiments: A Practical Guide to A/B Testing. Cambridge: Cambridge University Press. doi:10.1017/9781108653985. — каноническая монография по доверенным онлайн-экспериментам (A/B).
  4. Cohen, J. (1988). Statistical Power Analysis for the Behavioral Sciences (2nd ed.). Hillsdale, NJ: Lawrence Erlbaum Associates. doi:10.4324/9780203771587. — справочник по мощности, размерам эффекта и расчёту необходимого размера выборки.
  5. Armitage, P., McPherson, C. K., & Rowe, B. C. (1969). Repeated Significance Tests on Accumulating Data. Journal of the Royal Statistical Society: Series A, 132(2), 235–244. doi:10.2307/2343787. — показывает инфляцию ошибки I рода при повторных проверках накапливающихся данных — теория «подглядывания».
  6. Wald, A. (1945). Sequential Tests of Statistical Hypotheses. Annals of Mathematical Statistics, 16(2), 117–186. doi:10.1214/aoms/1177731118. — последовательный критерий отношения вероятностей (SPRT) — фундамент последовательного анализа.
  7. Pocock, S. J. (1977). Group Sequential Methods in the Design and Analysis of Clinical Trials. Biometrika, 64(2), 191–199. doi:10.1093/biomet/64.2.191. — групповые последовательные границы с равномерной корректировкой уровня значимости.
  8. O'Brien, P. C., & Fleming, T. R. (1979). A Multiple Testing Procedure for Clinical Trials. Biometrics, 35(3), 549–556. doi:10.2307/2530245. — консервативные границы O’Brien–Fleming: строго в начале, почти номинальный порог к финалу.
  9. Johari, R., Koomen, P., Pekelis, L., & Walsh, D. (2017). Peeking at A/B Tests: Why It Matters, and What to Do About It. KDD ’17, 1517–1525. doi:10.1145/3097983.3097992. — always-valid p-значения против подглядывания при непрерывном мониторинге A/B.
  10. Benjamini, Y., & Hochberg, Y. (1995). Controlling the False Discovery Rate: A Practical and Powerful Approach to Multiple Testing. Journal of the Royal Statistical Society: Series B, 57(1), 289–300. doi:10.1111/j.2517-6161.1995.tb02031.x. — контроль доли ложных открытий (FDR) при множественной проверке метрик.
  11. Simpson, E. H. (1951). The Interpretation of Interaction in Contingency Tables. Journal of the Royal Statistical Society: Series B, 13(2), 238–241. doi:10.1111/j.2517-6161.1951.tb00088.x. — парадокс Симпсона: разворот направления эффекта при агрегации или сегментации данных.
  12. Kohavi, R., Deng, A., Frasca, B., Walker, T., Xu, Y., & Pohlmann, N. (2013). Online Controlled Experiments at Large Scale. KDD ’13, 1168–1176. doi:10.1145/2487575.2488217. — A/B-эксперименты в масштабе; контроль качества и диагностика sample ratio mismatch (SRM).
  13. Deng, A., Xu, Y., Kohavi, R., & Walker, T. (2013). Improving the Sensitivity of Online Controlled Experiments by Utilizing Pre-Experiment Data. WSDM ’13, 123–132. doi:10.1145/2433396.2433413. — метод CUPED: снижение дисперсии метрик с помощью данных до эксперимента.

Нужна модель, а не статья?

Опишите задачу.
Ответим в течение 24 часов.

Мы не только пишем о методах — мы проектируем эксперименты и проверяем чужие для бизнеса, фондов и исследователей.

NDA до передачи данных · границы работ, KPI и сроки фиксируются до старта · hello@statgazer.ru