Май 2019 — Заметка №5

Десять тысяч A/B тестов

Большой текст. Будет интересен тем, кто знает, что такое A/B тесты и статистическая значимость.

A/B тесты полезная штука для валидации гипотез, но не такая простая как кажется — легко ошибиться. Если о статистической значимости и p-значениях большинство знает, то вот о более хитрых возможностях ошибиться — нет.

Одна из проблем: repeated significance testing error (повторные ошибки тестирования значимости). Умные люди пишут (русский перевод), что выборка должна быть фиксированной, например “в тесте поучаствует 1000 посетителей”. После чего тест нужно обязательно завершить и подсчитать результаты.

Нельзя завершать тест досрочно, если он вдруг показал статистическую значимость раньше. Нельзя держать тест запущенным так долго, пока какой-то вариант не покажет статистическую значимость(без ограничения на размер выборки).

Эта мысль контринтуитивна для меня. Я знаю, что про это пишут люди, которые понимают в статистике больше чем я (я понимаю так себе). Но я не мог понять до конца и осознать по настоящему почему оно работает так. Мысли об этом меня преследовали и воскресной ночью я понял — надо сделать модель и просимулировать всё это!

Я сделал модель, которая симулирует множество A/B тестов, сравнивая две гипотезы с разной конверсией. В каждой симуляции трафик случайным образом распределяется между двумя версиями. Каждый симулированный посетитель случайным образом конверится, согласно конверсии у гипотезы. Мы собираем статистику и оцениваем:

Сам код можно найти на Гитхабе. Также я там подробнее описал подход к симуляции. Если вы увидели ошибку в коде или же расчетах — пожалуйста, расскажите мне (@qetzal в Телеграме) — я буду очень благодарен (я не настоящий программист и статистик.

Ниже я расскажу про результаты (много чисел). Я просимулировал три ситуации:

В каждой ситуации мы смотрим и на результат после фиксированной выборки (26000 посетитилей, то рекомендованное число для 3% baseline конверсии, обнаружения разницы в 20% и 95% стат. значимости). Каждая ситуация была просимулирована 10,000 раз — проценты ниже отображают ситуации от всех симуляций.

Ситуация первая — гипотезы сильно отличаются по конверсии

У контрольной версии конверсия 3%. У тестовой - 2.15%.

Вывод: Фиксированная выборка показала значимый и правильный результат в 99.6% случаев (ожидаемо).

Если мы завершаем тест раньше, как только получили значимость и версии сильно отличаются по конверсии, то в ~87% случаев можно это сделать и получить верный результат. Это может сэкономить время. Но в 13% случаев такой подход приводит к ошибке и неправильному выбору — даже при такой большой разнице в конверсиях.

Ситуация вторая — гипотезы отличаются по конверсии, но не сильно

У контрольной версии конверсия 3%. У тестовой - 2.8%.

Вывод: если версии не сильно отличаются по конверсии (но отличаются), то при недостаточно большой фиксированной выборке мы будем получать не статистически значимые результаты (в нашем случае 75% случаев). Значимые и правильные результаты были получены только в четверти случаев по результатам фиксированной выборки. Это ожидаемый результат — если нет уверенности в сильной разнице гипотез надо делать выборку больше.

Если мы завершаем тест раньше, как только получили значимость, и версии не сильно отличаются по конверсии, то в ~54% случаев можно это сделать и получить верный результат. В 15% можно подержать тест запущенным уже после того как прошла фиксированная выборка (которая не показала значимость) и все таки получить значимый и правильный результат.

Но вот уже в 29% случаев (каждый третий!) такой подход приводит к ошибке и неправильному выбору. Мы останавливаем тест раньше и получаем значимые результаты, что контрольная версия конвертит хуже (что не так).

Ситуация третья — гипотезы не отличаются по конверсии

У контрольной версии конверсия 3%. И у тестовой — 3%. (спойлер — тут начинается самое веселое)

Вывод: если версии не отличаются по конверсии и мы завершаем тест после фиксированной выборки, мы будем получать не статистически значимые результаты в большинстве случаев (в нашем случае 90% случаев). В 5% случаев мы получим значимые и неправильные результаты — процент таких ошибок ограничен сверху нашим “p-value”, мы можем его снижать (увеличивая выборку), но он не будет больше.

Если отличий в гипотезах нет и мы или завершаем тест раньше (до окончания фиксированной выборки) как только получили первый значимый результат или держим тест запущенным пока не получим значимый результат — мы получим значимые и неверные данные (что какая-то гипотеза отличается) в 91.5% случаев. 9 из 10 неверных ответов. Wow.

Выводы

TL;DR: Надо заранее выбирать размер выборки, останавливать тест когда он прошел и измерять значимость только в конце. Если тест не значимый — надо его перезапускать (а не продолжать). Если тест не значимый — возможно эффект изменений слишком мал для детектирования, надо увеличивать выборку.


P.S. Читатель Igor Yashkov прислал (спасибо большое!) еще интересных ссылок, которые открывают еще одну интересную штуку.

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

С другой стороны — это очевидно неудобно. Каждый тест надо держать запущенным до заранее определенного конца (при не таком частом событии, например апгрейд до платного плана и небольшой разнице в гипотезах это может занять недели и месяцы). Тест может не показать статистическую значимость и значит надо все или начинать заново (еще месяц!) или выкидывать сделанную работу.

Люди пытаются обойти эту проблему, используя другие подходы к расчетам, без p-value (то что все используют c p-value называют frequentist hypothesis testing).

Один из подходов это Bayesian A/B Testing. Очень подробно про него пишет VWO. При каждом свидетельстве (есть конверсии или нет) мы обновляем вероятности гипотезы. При таком подходе мы можем и остановить тест в любой момент и держать его запущенным так долго как хотим. VWO говорит, что это заметно ускоряет тесты. Это сильно уменьшает процент ошибок, но говорят не убирает их совсем.

Или вот еще один другой подход: frequentist approaches to sequential testing.

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

(в ссылках выше много математики — я сходу не понял все штуки, потребуется сесть и обдумать)