Влияние экспериментов друг на друга

Stats&Data ninja
11 min readJun 27, 2023

--

Пересечение А/В-тестов, многофакторный дизайн и тд.

Мотивация

Понятно, что подразумевается под влиянием экспериментов. Простой пример — в одном А/В-тесте меняется цвет фона (пусть будет на синий), а в другом цвет текста (также на синий). У пользователей, попавших в таргетные группы обоих тестов, окажется очень негативный клиентский опыт. При этом, если бы эксперименты запускались не параллельно, такого бы не наблюдалось. Есть и обратный эффект, т.н. симбиоз, когда 2 фичи вместе приносят больше, чем суммарно каждая из них по отдельности. Подобные примеры показывали Lyft и Statsig.

В англоязычном мире существует термин interactions. Также, влияние тестов часто называют пересечением. Здесь не стоит путать с другим, более общим пересечением, т.е. фактом того что пользователь участвует сразу в нескольких экспериментах (в англоязычной литературе используется термин overlapping). Такое пересечение помогает увеличить пропускную способность в А/В-тестах (запускать их больше), и не является проблемой до тех пор пока нет влияния тестов.

Для того, чтобы избежать влияния тестов, их нужно вынести друг от друга, т.е. убрать пересечение в широком смысле. Для этого существуют т.н. слои (Google , Microsoft, Яндекс, Avito и др), т.е. набор всех экспериментов (например маркетинговые акции), и каждый пользователь может одновременно быть только в одном тесте из слоя.

Математика влияния

Для визуального представления пересекающихся тестов используются т.н. таблицы сопряженности. Это таблица, в который за строки отвечает одна переменная (группа в первом тесте), за столбцы вторая (группа во втором тесте). Их можно использовать для двух вещей: проверка SRM и проверка аддитивности эффекта.

1. Проблема сплитования и SRM

Начнет с контроля SRM. О том что это такое — в моей прошлой статье. На первой картинке видно, что всего в тесте участвует 200 человек, причем первый тест разбивает их в пропорции 50/50, а второй 20/80. Если мы возьмем какую либо из групп первого теста, например Контроль 1, то второй тест разбивает ее в той же самой пропорции (20/80), что и всю выборку. То же самое и для Таргета 1. В этом и есть смысл ортогонального сплитования.

Картинка 1. Таблица сопряжённости двух тестов.

Если пропорции не соблюдаются, то возникает проблема Experiment Interference SRM. Мы обсуждали в прошлой статье, что SRM является проблемой используемой хэш-функции. Но если в случае одного теста SRM из-за хэш-функции обычно не искажает результаты (т.к. хэш обычно никак не связан с параметрами пользователя), то SRM в разрезе другого теста влияет очень сильно, если второй тест имеет эффект.

Например, рассмотрим картинку 2. На ней первый тест все также бьет общую выборку 50/50, а второй 20/80. Но при этом, в разрезе групп первого (в каждой из групп) второй тест сплитуется уже не 20/80, а по-другому.

Картинка 2. Пример Experiment Interference SRM.

Почему это проблема: если первый эксперимент тестирует хорошое изменение (имеет положительный эффект), то пользователи в таргете 1 становятся более лояльными и имеют более высокие метрики чем контроль 1. Если мы посмотрим на группы второго теста, то доля таких более лояльных пользователей в контроле 2 (25/40 = 62.5%) более высокая чем в таргете 2 (75/160=46.8%), а значит группы заведомо будут расходиться (т.н. contingency). Здесь доля таргета 1 является конфаундером для второго теста. О том что это такое я рассказывал в своей статье про causal inference.

Это причина, по которой Yahoo отказался от хэш-функции FNV в пользу Murmur3 и Bob Jenkins. В целом, многие встроенные в ЯП хэш-функции работают плохо. Microsoft отмечали, что необходимо использовать именно криптографические хэш-функции (MD5 работает неплохо, SHA256 похуже).

Также, подобная проблема может возникнуть, если используется двойной посол для корректного перемешивания юзеров между тестами, но лишь с небольшим количеством seed’ов (пример из статьи). Например, может случиться парадокс близнецов: для 363 рандомных seed’ов, необходимо только 23 эксперимента, чтобы с 50% вероятностью кто-то из них шарил одинаковый seed. Поэтому я советую брать уникальную соль для каждого эксперимента, например завязанную на его уникальное название или id.

Расбалансировку выборок между экспериментами можно измерить количественно с помощью коэффициента Фи. Это аналог корреляции Пирсона, но для категориальных данных (доля таргетной группы в одном из тестов в зависимости от группы в другом тесте). Также, как и с SRM в одном тесте, мы можем вычислить стат-значимость взаимосвязи с помощью того же критерий Хи-квадрат, только теперь немного в другом виде. Вместо того, чтобы проверять для разбиение для каждой группы внутри другого теста, мы проверим сразу по всей таблице сопряженности. Для примера сгенирим 10к id клиентов и раздадим рандомные группы в двух А/В-тестах, а затем проверим ортогональность.

import numpy as np
import pandas as pd


N = 10_000
df = pd.DataFrame(data={'id':range(1,N+1)})
df['label1'] = np.random.choice(2, N) # 0 значит контроль, 1 значит таргет
df['label2'] = np.random.choice(2, N) # 0 значит контроль, 1 значит таргет

# собираем таблицу сопряженности
data = []
for i in df.label1.unique().tolist():
data.append(df[df.label1==i].groupby(['label2']).id.count().tolist())
print(data)


# через statsmodels
import statsmodels.api as sm
table = sm.stats.Table(data)
res = table.test_nominal_association()
print(res.pvalue)

# через scipy
from scipy.stats import chi2_contingency
stat, p_value, dof, expected = chi2_contingency(data)
print(p_value)
# результаты разные, т.к.scipy использует the continuity correction
# При указании correction=False в методе выше рез-ты одинаковые

Надо понимать, что вышеупомянутый критерий работает только в случае, когда кол-во клиентов в каждой ячейке таблицы сопряженности больше 5. Вряд ли в А/В-тестах вы получите меньшие значения, но все же при разной сегментации пользователей и небольшом перекрытии тестов такое можно случится. В таком случае нужно использовать точный тест Фишера или тест Барнарда.

Таким образом, с учетом этой и предыдущей статьи хэш-функция должна иметь 2 свойства. Первое — отсутствие т.н. the funnels (т.е. когда соседние id получают одинаковый хэш), и тогда распределение по группам будет равномерным. Второе — отсутствие characteristics (когда изменение соли приводит к прогнозируемому изменению хэша), и тогда не возникает корреляций между экспериментами.

Есть еще одно ограничение на пересекающиеся тесты. Я уже рассказывал, что одно из предположений А/В-тестирования — рандомизация, нужна для того чтобы сбалансировать группы по всем параметрам. Это значит, что при достаточной больших выборках для каждого юнита в контрольной группе должен найтись примерно такой же в таргетной. Теперь мы знаем, что сбалансированым должен быть также такой параметр, как группы в других тестах. Т.е., если в КГ у нас есть клиент с каким-либо набором комбинаций групп в других тестах, в ТГ должен найтись клиент с таким же набором комбинаций. Т.е. должно найтись минимум 2 клиента (если групп всего 2), для каждого набора комбинаций. Число таких комбинаций равно 2 в степени, равной числу других тестов, в простом случае если во всех экспериментах только 2 группы. Значит число клиентов должно быть как минимум 2 в степени общее кол-во тестов. Например, для 20 ортогональных тестов желательно иметь не менее 1 млн клиентов. Более того, ввиду того что хэш-функции не идеальны, я бы брал число клиентов на порядок больше.

Даже если количество клиентов в каждой из групп сбалансировано, могут не сходиться метрики. По идее, перед А/В иногда проводят А/А-тест, а также сравнивают различные параметры аудитории между группами (например демографию). Собственно то же самое можно делать и в разрезе групп другого теста с помощью таблиц сопряженности и используя тесты хи-квадрат.

2. Аддитивность

Мы обсудили проблемы сплитования, т.е. технические, без решения которых нет смысла смотреть продуктовые пересечения. Собственно мы и начинали с примера про синий цвет фона и шрифта (т.н. contamination). Продуктовые пересечения — это когда результат одного эксперимента зависит от группы в другом тесте по продуктовым причинам. Покажем на примере той же таблицы, только теперь в ячейках будет указано не кол-во клиентов, а значение метрики:

Картинка 3. Аддитивность эффектов в двух экспериментах.

Пусть пользователи, попавшие в группы контролей обоих тестов среднее значение метрики равно C0. Клиенты, которым досталась таргетная группа в первом/втором и контрольная в другом тесте, будут иметь Average Treatment Effect соответственно ATE1/ATE2, и их средние значения метрики C0+ATE1 и С0+ATE2. В таком случае, если эксперименты не влияют друга на друга, то счастливчики попавшие сразу в 2 таргетные группы должны иметь среднее значение метрики C0+ATE1+ATE2. Это называется аддитивностью эффекта.

В кейсе с одинаковым цветом фона и шрифта пользователи, попавшие в 2 таргетные группы, столкнулись бы с резко негативным пользовательским опытом, а значит их метрики были бы даже меньше чем C0, хотя по отдельности эффекты могли бы быть положительными. Бывает и наоборот — по отдельности фичи дают небольшой эффект, но вместе получается вау-эффект.

Картинка 4. Отсутствие аддитивности

Почему это важно и нужно проверять аддитивность эффектов у тестов, которые потенциально могут влиять друг на друга:

Если эксперименты идут одновременно и нет аддитивности, то мы должны принимать решение не по каждому тесту отдельно, а совместное.

Влияние экспериментов — еще одна причина, по которой нельзя обобщать результаты проведенных экспериментов и почему иногда нужно их повторять. Действительно, помимо сезонности и действий конкурентов, на обобщаемость результатов теста влияют другие проведенные тесты и запущенные фичи. Покрас кнопки в синий цвет мог давать положительные результаты несколько лет назад, но недавно запустили синий цвет фона и сейчас наша фича при запуске даст отрицательный эффект.

Один из способов проверить аддитивность — просто проверить суммы эффектов (см. картинку ниже). Так делает Microsoft, ежедневно запуская попарные сравнения экспериментов. Добавив дов. интервалы к формуле, мы уберем шум и сделаем такое сравнение “научным”.

Простое объяснение можно дать и через регрессию. Как я уже рассказывал, простые эксперименты и статистические критерии можно представить в виде линейной модели. Простой А/В-тест можно представить как линейную модель, в которой зависимой переменной является метрика одного юнита, а независимой — его группа в тесте (0 если контроль и 1 для таргета):

Картинка 5. А/В-тест как линейная модель

Коэффициент этой модели перед независимой переменной и есть average treatment effect. Если пользователь одновременно участвует в двух параллельных тестах, то его метрика определяется двумя переменными-группами:

Картинка 6. Двухфакторная линейная модель

К сожалению, такая форма является ошибочной когда эксперименты не ортогональны. При влиянии группы одного эксперимента на эффект другого добавляется еще переменная взаимодействия:

Картинка 7. Двухфакторный А/В-тест с взаимодействием

В таком случае полный эффект складывается из двух составляющих: эффект в отсутствии другого теста ATE1/ATE2 (т.н. main effect) и эффект взаимодействия β. Поясняющую картинку можно найти в статье. Более подробный математический разбор можно найти здесь. Соответственно для проверки ортогональности тестов мы должны сравнить β с нулем.

Картинка 8. Визуализация взаимодействия

Для этого:

  • Решаем линейную регрессию и тестируем значимость коэффициентов через корреляционный тест, который в случае одной переменной совпадет с t-критерием. В регрессии группы в тестах являются dummy (индикаторными) переменными. Все это я рассказывал в вышеупомянутое статье, также это можно найти здесь.
  • Другой, более простой метод, создан специально для проверки аддитивности эффектов. Это стат-критерий 2-way Anova. По-другому, Tukey’s test of additivity. Пример кода можно найти здесь.

Оба метода хороши тем, что в случае несбалансированных выборок можно добавлять ковариаты, превращая тем самым Anova в Ancova. Третий, простой способ — построить графики метрик одного теста в зависимости от группы другого теста. Если линии не параллельны, значит нет аддитивности.

Главная проблема такого подхода — наличие большого кол-ва сравнений, из-за чего возникается проблема множественного тестирования. Одним из способов (использует Microsoft) снизить кол-во ложных прокрасов — это проверка FDR через байесовское множественное тестирование (статья).

Множество пересекающихся тестов

Однако не стоит забывать что обычно одновременно идут не два, а десятки, сотни и даже тысячи экспериментов! Bing еще 10 лет назад заявлял о 200 одновременных тестах, Statsig сообщает о тысячах экспериментах, Facebook и Google пересекают тысячи тестов. На самом деле все тесты можно добавить в уравнение регресии, и посчитать не только попарное влияние, но и допустим симбиоз 3х и т.д. тестов.

Проблема, связанная с таким расчетом, очевидна— слишком много параметров, многие из которых могут быть мультиколлинеарны (переменная взаимодействия 100 тестов скорее всего не отличается от переменной взаимодействия 101 теста), а также размеры выборок будут просто недостаточны для получения стат-значимости нужных параметров. Проблема не только в большом количестве тестов, но и в синхронизации их стартов и окончаний. Вполне возможно, что тесты будут пересекаться лишь на пару дней и действовать только на определенные сегменты пользователей, а значит нужная выборка будет очень маленькой даже для оценки только попарного влияния.

В реальности, часто не стоит переживать по этому поводу. Еще в 2009 году опрос от Рона Кохави и др. про А/В-тестирование в разных компаниях показал, что влияние одного эксперимента на другой довольно редко случается на практике. Даже если случился interaction, скорее всего он не изменит цвет прокрасса (красний/зеленый), а лишь немного сместит оценку эффекта. Поэтому пересчитывать все возможные варианты пересечений тестов не стоит, т.к. скорее всего большинство найденных взаимодействий окажутся просто false-positive. Statsig подтверждает это, говоря о том что обычно пересечения эксперименты не влияют на решение катить/не катить, а лишь дают не очень точную оценку эффекта.

Обычно тесты на разные части продукта не влияют друга на друга, а взаимодействие экспериментов происходит только в случае тестов одной команды в одном под-продукте. Обычно запускающие менеджеры и так в курсе, какие тесты потенциально могут оказывать влияние друг на друга, а значит выбрать один из множества вариантов: запустить последовательно, разделить трафик, ортогонально пересечь и затем проверить влияние, или же что еще лучше — сделать полно-факторный дизайн через MVT тестирование (о чем поговорим ниже). Для разделения трафика как раз и были придуманы слои у Google или numberline у Microsoft. Команда, запускающая тесты в свой части продукта, сама решает запускать их ортогонально или в своем слое. Однако, если есть беспокойство по поводу пересечения экспериментов, то я бы не советовал использовать слои, как уже было сказано желательно принимать по экспериментам совместное решение, а не независимое. Лучше запустить одновременно и проверить пересечение или использовать факторный дизайн.

Полно-факторный дизайн

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

И здесь мы сталкиваемся с проблемой недостатка мощности. Дело в том, что изначально мы рассчитывали размеры выборок для полных групп каждого эксперимента, а теперь разбиваем группы на подгруппы.

Если мы изначально сконцентрировали наше внимание на взаимодействии экспериментов и синхронизировали их запуски, то лучше провести полно-факторный эксперимент. Если каждый из предполагаемых тестов имел по 2 группы, то в таком тесте общее кол-во групп равно 2 в степени n. Для каждой комбинации факторов создается своя группа. Для двух фич будет контрольная группа, по таргетной группе для каждой фичи и группа с двумя фичами.

В целом, не всегда используется все 2**n групп, т.к. уже для 5 тестов понадобится 32 группы, что само по себе значительно снижает мощность эксперимента по сравнению с параллельными тестами, а с учетом поправок на множественного тестирования делает такой запуск бессмысленным. Поэтому иногда берут лишь подмножество всех необходимых групп, т.н. частичный факторный дизайн. При этом учитывается разреженность эффектов, т.е. тот факт что заведомо часть коэффициентов в модели равна нулю с высокой вероятностью. Для анализа подобных экспериментов разработана методология, называемая анализом Йейтса.

Комбинировать между собой тесты можно по-разному, не только полным факторным дизайном. Для этого создан целый раздел в науке об экспериментах, называемый комбинаторным дизайном.

Итог

  • Влияние тестов друга на друга приводит к не-аддитивности эффектов, отчего нельзя сделать вывод на одном тесте, его результат зависит от того, какой вариант другого теста раскатиться.
  • Если сплитование (хэш-функцией или как-то еще) не умеет в ортогональность (например, не используется двойной посол в бакетах), то в результатах будет неаддитивность даже если продуктово тесты не влияют друг на друга, поэтому в первую очередь проверье ваши хэш-функцию.
  • Влияние тестов может быть не только попарным, но и множественным.
  • На практике влияние тестов довольно редко, и оно лишь немного модифицирует эффект вместо того чтобы красить результаты в другую сторону
  • Если тесты могут повлиять друг на друга, то лучше запустить их в одном слое (разделение трафика с общим контролем), а еще лучше — запустить факторный эксперимент и оценить влияние. Полное пространство вариантов можно найти в статье от StatSig:
  • Более математические статьи можно найти тут и тут.

P.S.: Если тебе понравилась стать, в качестве благодарности можешь купить мне кофе на https://www.donationalerts.com/r/stats_data_ninja или https://buymeacoffee.com/koch.

--

--

Stats&Data ninja
Stats&Data ninja

No responses yet