Расширенный CUPAC для снижения дисперсии в A/B экспериментах

Задача снижения дисперсии при проведении A/B экспериментов, кажется, никогда не перестанет быть актуальной. Хочу поделиться небольшим разбором свежей статьи, в которой авторы предлагают еще один довольно-таки провокационный метод для снижения дисперсии.

  • Можем ли мы использовать для снижения дисперсии данные во время эксперимента?

  • Может ли метод быть сильно лучше CUPAC и что значит «лучше»?

  • При применении методов снижения дисперсии мне приходится сталкиваться с проблемой большого количества пропущенных данных до эксперимента

Если вас зацепил какой-нибудь из этих тезисов / вопросов, то, возможно, вам будет интересна эта статья. Меня зовут Артем Пономарев, я занимаюсь анализом данных, и часть из этих вопросов показалась мне любопытной, поэтому я решил рассказать о прочитанной статье, которая в каком-то смысле дает свои ответы на эти вопросы.

Мем для привлечения внимания

Мем для привлечения внимания

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

Зачем мы хотим снижать дисперсию?

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

Каждый эксперимент хочется проводить быстрее и в каждом эксперименте хочется находить небольшие изменения метрик, то есть делать используемый критерий более чувствительным. Если мы посмотрим на формулу для расчета размера выборки, которая состоит из заданных ошибок I рода, II рода, минимального детектируемого эффекта (MDE) и дисперсии, для случая использования t-test критерия, то становится понятно, что, как аналитики, мы можем повлиять только на дисперсию.

N > \frac{(Z_\frac{\alpha}{2} + Z_\beta)^2(2*\sigma^2)}{e^2}» src=«https://habrastorage.org/getpro/habr/upload_files/dde/ce4/576/ddece4576cff89bff084243184f5eb39.svg» /></p>

<p>Можно, конечно, пытаться искать эффекты большей величины, чтобы уменьшить время проведения эксперимента или, напротив, увеличить объем выборки, чтобы искать эффекты меньшей величины, но это сделать мы всегда успеем при необходимости.</p>

<p>Итак, если мы научимся снижать дисперсию, то несложные математические приведения формулы выше показывают, что нам понадобится меньше данных для эксперимента или мы будем находить меньшие эффекты при том же объеме данных. Это-то нам и нужно! </p>

<h2>Как можно снижать дисперсию? </h2>

<p>Сейчас существует множество методов снижения дисперсии, о которых многие, как минимум, слышали.</p>

<ul><li><p><em>CUPED (Controlled-experiment Using Pre-Experiment Data)</em></p></li><li><p><em>CUPAC (Controlled-experiment Using Prediction As Covariate)</em></p></li><li><p><em>CUNOPAC (Controlled-experiment Using Not Overfitted Prediction As Covariate)</em></p></li><li><p><em>Uplift-критерий</em></p></li><li><p><em>Методы Causal Inference</em></p></li><li><p>…</p></li></ul>

<p>Каждый из этих методов довольно подробно разобран, и существует множество примеров, как их использовать на практике. Но для контекста мы остановимся на основных идеях <em>CUPED</em> и <em>CUPAC</em>.</p>

<h2>CUPED</h2>

<p>Один из самых популярных и простых методов снижения дисперсии. Метод использует предэкспериментальные данные в качестве ковариаты для построения несмещенной и скорректированной метрики результата.</p>

<p>Основная идея классического <em>CUPED</em> в том, чтобы найти скоррелированную с нашей результирующей метрикой <img alt=ковариату Xи использовать ее для получения новой метрики Y_{\text{cuped}}:

Y_{\text{cuped}} = Y - \theta  X\theta_0 = \frac{\mathrm{cov}(Y, X)}{\mathrm{Var}(X)}

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

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

CUPAC

Также довольно популярный метод в индустрии и, как правило, более мощный, чем CUPED. По сути является модификацией CUPED с добавлением ML.

Идея метода CUPAC в использовании предсказания целевой метрики Yв качестве ковариаты. Необходимо выбрать набор ковариат, которые скоррелированы с результирующей метрикой, обучить ML модель на предэкспериментальных данных и затем использовать полученное предсказание в качестве ковариаты в CUPED.

Идея метода, предложенного авторами статьи

Существующие популярные методы (например, рассмотренные выше CUPED, CUPAC) используют вспомогательную информацию, которая коррелирует с результатом, чтобы уменьшить неопределенность в A/B экспериментах, и тем самым делают оценку эффекта более точной. При этом такие методы используют, как правило, данные до эксперимента. И эффективность таких методов зависит от корреляции между предэкспериментальными данными и результатом (целевой метрикой). Но зачастую данные во время эксперимента сильнее коррелируют с результатом, а значит, потенциально могут быть более эффективны в решении проблемы.

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

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

Теперь чуть подробней про сам метод. На схеме 1 изображены отношения между следующими переменными для случая использования данных до эксперимента:

Так как мы учитываем ковариаты до эксперимента Xс помощью, например, регрессионной модели, которая использует Xв качестве признаков и предсказывает целевую метрику Y, то мы можем лучше объяснять полученный эффект на Y, тем самым снижая неопределенность и дисперсию Y.

Например, пусть целевая метрика Y— конверсия в покупку. Мы используем знания о конверсии в покупку пользователей, участвующих в эксперименте, на данных до эксперимента (то есть ту же самую метрику на предэкспериментальном периоде), чтобы лучше объяснить изменчивость целевой переменной Y.

Более подробно:

На схеме 2 изображены отношения между следующими переменными:


Y— целевая метрика
W— тритмент
X— предэкспериментальные ковариаты
U — ненаблюдаемые ковариаты, случайные ошибки
Z— внутриэкспериментальные ковариаты

Схема 2. Можем избавиться от косвенного влияния U на Y через Z, но не затрагивая косвенного влияния W на Y через Z

Схема 2. Можем избавиться от косвенного влияния U на Y через Z, но не затрагивая косвенного влияния W на Y через Z

На схеме 2 у нас появляется новая переменная Z— внутриэкспериментальные ковариаты. Здесь важно понять, что воздействие от тритмента (нашей тестируемой фичи) Wвлияет не только на целевую метрику Y, но и на другие метрики / ковариаты Z, потому что мы их наблюдаем уже после влияния нашего воздействия фичи W. Но в то же время ненаблюдаемые ковариаты и случайные ошибки Uтакже влияют на Z, а соответственно через Zкосвенно и на целевую метрику Y.

Если мы будем просто учитывать данные во время эксперимента Zтакже как раньше учитывали данные до эксперимента X, то внесем смещение в полученную оценку эффекта. Поэтому ключевая задача — избавиться от косвенного влияния ненаблюдаемых ковариат Uна целевую метрику Y, но при этом важно не затронуть косвенное влияние тритмента Wна Yчерез внутриэкспериментальные ковариаты Z.

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

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

В контексте A/B экспериментов тритмент может влиять на результат только через некоторые внутриэкспериментальные ковариаты. Ключевая идея для объединения данных до эксперимента и во время эксперимента заключается в:

  • выборе тех внутриэкспериментальных ковариат, которые не оказывают косвенного влияния на результат

  • включении их в структуру CUPAC посредством регрессионной корректировки

Предположение об отсутствии косвенного влияния соответствует предположению об эквивалентности среднего в методе CUPED и предположению об эквивалентности распределения в CUPAC. Авторы предлагают фокусироваться на предположении CUPED, поскольку оно слабее, что позволяет нам выбирать больше внутриэкспериментальных ковариат, которые удовлетворяют условию. В частности, мы предполагаем, что все внутриэкспериментальные ковариаты Zудовлетворяют предположению:

E[Z \mid W = 0] = E[Z \mid W = 1]

Для применения предложенного метода, необходимо выполнить шаги:

  1. Используем предэкспериментальные данные для прогноза целевой метрики Yс помощью ковариат X, как в CUPAC

  2. Считаем полученные остатки, которые собирают информацию из предэкспериментальных данных, которые не объясняются X
    Ri = Yi - f(Xi)

  3. Регрессируем R_iна Z_i, чтобы скорректировать различия, связанные с экспериментальными ковариатами (получаем вектор \hat{\gamma}^\)

  4. Окончательная оценка определяется следующим образом (в коде это три строчки, если что):

\begin{align*} \hat{\tau} &= \frac{1}{n_1} \sum_{W_i = 1} \left( \hat{R}_i - \hat{\gamma}^\top Z_i \right)  - \frac{1}{n_0} \sum_{W_i = 0} \left( \hat{R}_i - \hat{\gamma}^\top Z_i \right) \\ &= \frac{1}{n_1} \sum_{W_i = 1}  \left( Y_i - \hat{f}(X_i) - \hat{\gamma}^\top Z_i \right)  - \frac{1}{n_0} \sum_{W_i = 0}  \left( Y_i - \hat{f}(X_i) - \hat{\gamma}^\top Z_i \right) \end{align*}Код для применения предложенного метода

def check_improved_cupac(
    df_control: pd.DataFrame,
    df_pilot: pd.DataFrame,
    pre_experiment_covariates: List[str],
    in_experiment_covariates: List[str]
) -> float:
    """"""
    
    df = pd.concat([df_control, df_pilot])
    
    model_cupac = LinearRegression()
    model_cupac.fit(df[pre_experiment_covariates], df["Y"])
    df["Y_pred"] = model_cupac.predict(df[pre_experiment_covariates])

    df["R"] = df["Y"] - df["Y_pred"]

    model = LinearRegression()
    model.fit(df[in_experiment_covariates], df["R"])
    gamma_hat = model.coef_
    
    df["R_adj"] = df["R"] - df[in_experiment_covariates].dot(gamma_hat)
    
    df_control = df[df["T"] == 0]
    df_pilot = df[df["T"] == 1]

    _, pvalue = stats.ttest_ind(df_control["R_adj"], df_pilot["R_adj"])

    
    return pvalue
  

Получаем асимптотически несмещенную оценку (теоретическое доказательство есть в статье) со сниженной дисперсией.

Очевидно, применение метода имеет смысл только в случаях, когда:

  • у нас существуют внутриэкспериментальные ковариаты, для которых выполняется предположения об эквивалентности математических ожиданий

  • корреляция выбранных ковариат с Yсуществует

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

Выбор внутриэкспериментальных ковариат

Стоит отдельно затронуть выбор внутриэкспериментальных ковариат для применения метода. Это принципиально важный момент для получения корректной и несмещенной оценки.

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

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

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

Эмпирические исследования эффективности метода

Авторы статьи применили предложенный метод к 29 A/B экспериментам, которые были проведены в Etsy. Затем они сравнили эффективность метода с методом CUPAC.

Для применения CUPAC были использованы 117 предэкспериментальных ковариат, и была использована модель LightGBM. При использовании предложенного метода авторы отфильтровали внутриэкспериментальные ковариаты, выбрав только те, у которых было достаточно ненулевых значений. Затем они использовали критерий Манна-Уитни для оценки эквивалентности распределений между экспериментальной и контрольной группами, выбрав таким образом 23 внутрикспериментальных ковариаты.

Затем они использовали две метрики для оценки уровня снижения дисперсии, достигнутого предложенным методом. Первая метрика — разница значенийR^2между комбинированной моделью из предложенного методом и моделью CUPAC, которая предсказывает значение целевой метрики Y, гдеR^2это коэффициент детерминации модели. Эта метрика отражает улучшение предсказательной точности комбинированной модели в предложенном методе по сравнению с моделью CUPAC, которая использует только предэкспериментальные ковариаты X.

Результаты по первой метрике

Результаты по первой метрике

Результаты показывают, что улучшение по величине R^2варьируется от 0.02 до ~0.14 в зависимости от эксперимента.

Другая метрика — отношений асимптотических дисперсий.

Результаты по второй метрике

Результаты по второй метрике

График иллюстрирует вторую метрику. Синие столбцы показывают уменьшение дисперсии, достигнутое методом CUPAC по сравнению с методом без применения дополнительных корректировок по формуле:

1 - \frac{\sigma_{\text{CUPAC}}^2}{\sigma_{\text{DIFF}}^2}

Оранжевые столбцы показывают дополнительное уменьшение дисперсии, которое достигается предложенным методом по сравнению с CUPAC по формуле:

1 - \frac{\sigma^2}{\sigma_{\text{CUPAC}}^2}

Результаты показывают, что предложенный метод достигает большего или сопоставимого уменьшения дисперсии при использовании меньшего числа ковариат: 23 внутриэкспериментальных ковариаты в предложенном методе по сравнению с 117 предэкспериментальными ковариатами в CUPAC.

Симуляции

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

Для начала cгенерируем синтетические данные следующим образом:

Генерация данных

def generate_data_with_correlation(
    size: int,
    ate: float,
    pre_corr: float,
    exp_corr: float,
    y_before_corr: float,
    x_corr: List[float],
    seed: int = None
) -> pd.DataFrame:
    """"""
    
    if seed is not None:
        np.random.seed(seed)
    
    X = np.random.multivariate_normal(
        mean=[0, 0, 0], 
        cov=[[1, pre_corr, pre_corr], 
        [pre_corr, 1, pre_corr], 
        [pre_corr, pre_corr, 1]], 
        size=size
    )
    
    X1, X2, X3 = X[:, 0], X[:, 1], X[:, 2]

    T = np.random.binomial(n=1, p=0.5, size=size)
    
    Y_before = np.random.normal(0, 1, size)

    W_group_0 = np.random.multivariate_normal(
        mean=[0, 0, 0], 
        cov=[[1, exp_corr, exp_corr], 
        [exp_corr, 1, exp_corr], 
        [exp_corr, exp_corr, 1]], 
        size=size // 2
    )
    
    W_group_1 = np.random.multivariate_normal(
        mean=[0.1, -0.1, 0.2], 
        cov=[[1, exp_corr, exp_corr], 
        [exp_corr, 1, exp_corr], 
        [exp_corr, exp_corr, 1]], 
        size=size // 2
    )

    W = np.vstack((W_group_0, W_group_1))

    np.random.shuffle(W)

    W1, W2, W3 = W[:, 0], W[:, 1], W[:, 2]

    Y = Y_before * y_before_corr + T * ate + 0.4 * W1 + 0.3 * W2 + 0.3 * W3 + x_corr[0] * X1 + x_corr[1] * X2 + x_corr[2] * X3

    group_0_W = W[T == 0]
    group_1_W = W[T == 1]

    data = pd.DataFrame({
        "Y": Y,
        "Y_before": Y_before,
        "T": T,
        "X1": X1,
        "X2": X2,
        "X3": X3,
        "W1": W1,
        "W2": W2,
        "W3": W3
    })

    return data

  
  data = generate_data_with_correlation(
        size=10000, # sample size
        ate=0.01, # added effect size
        pre_corr=0.5,
        exp_corr=0.3,
        y_before_corr=0.2, # correlations of Y_before with the outcome variable
        x_corr=[0.4, 0.3, 0.1], # correlations of X with the outcome variable
        seed=111,
    )
  

Далее проведем множество симуляций оценки экспериментов и сравним чувствительность тестов с использованием методов CUPED, CUPAC, метода из статьи (improved CUPAC), а также случай отсутствия каких-либо корректировок. Для этого построим распределение p-value по результатам симуляций.

Симуляции A/B экспериментов

dict_pvalues = defaultdict(list)

for _ in tqdm(range(5000)):
    data = generate_data_with_correlation(
        size=2000,
        ate=0.04,
        pre_corr=0.5,
        exp_corr=0.3,
        y_before_corr=0.9,
        x_corr=[0.3, 0.5, 0.2],
    )
    
    df_control = data[data["T"] == 0]
    df_pilot = data[data["T"] == 1]

    dict_pvalues["cuped A/B"].append(check_cuped(df_control, df_pilot, "Y_before"))
    dict_pvalues["cupac A/B"].append(check_cupac(df_control, df_pilot, ["Y_before", "X1", "X2", "X3"]))
    dict_pvalues["ttest A/B"].append(check_ttest(df_control, df_pilot))
    dict_pvalues["improved cupac A/B"].append(check_improved_cupac(df_control, df_pilot, ["Y_before", "X1", "X2", "X3"], ["W1"]))

plot_pvalue_distribution(dict_pvalues)

Распределение p-value при использовании разных методов

Распределение p-value при использовании разных методов

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

Jupyter notebook с кодом симуляций можно найти тут: GitHub

Выводы

Авторы статьи предлагают метод снижения дисперсии, который предполагает использование как предэкспериментальных данных, так и внутриэкспериментальных (которые удовлетворяют предположениям метода) для бОльшего снижения дисперсии. Судя по практическим данным от Etsy, такой подход действительно помогает получить большее снижение дисперсии по сравнению с методом CUPAC, если существуют ковариаты во время эксперимента, удовлетворяющие предположениям метода и скоррелированные с результатом.

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

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

Но в то же время метод в некотором смысле «опасный»: нужно быть очень осторожным при его применении, потратить время на исследования взаимосвязей между метриками, чтобы получить корректную оценку.

А еще я пишу про данные в Telegram-канал, заходите почитать :)

Использованные источники

© Habrahabr.ru