Group Sequential Tests: очередное ускорение тестов
https://t.me/abba_testingНаивная Alpha-Spending
Для начала имеет смысл рассмотреть базовый наивный подход к снаряду, он нам нужен только для того, чтобы от него потом оттолкнуться в плане теории и не возвращаться!
В момент подглядывания (оно же промежуточный анализ, interim analysis), мы будем использовать более жесткую версию нашей запланированной на финальный расчет альфы, то есть скорректированную. Чтобы скорректировать альфу, мы можем использовать alpha-spending функцию - функция, которая "расходует", берет некоторую долю альфы на каждое подглядывание:

Декомпозируем:

Важное свойство: на момент старта наша альфа = 0, а на контрольный анализ согласно запланированной = 0.05:

Почему старт = (0) понятно, но почему конец = (1) ? Здесь 0 и 1 это отражение полноты от запланированного эксперимента, то есть на самом деле оперируют не столько кол-вом подглядыванием, сколько % размера выборки от запланированного (столько, сколько мы охватим при данном подглядывании). Поэтому вместо конкретного значения, какое это по счёту подглядывание, мы будем указывать долю аудитории на этот момент:
- подглядывание №1 - 0.2 (20%)
- подглядывание №2 - 0.4 (40%)
...
Посмотрим, какой будет результат, когда мы подставим альфа = 0.05, 5 раз будем смотреть в тест (4 раза подглядыванием + наш классичесий контрольный замер) и power, то есть степень, поставим сначала как 0.2:
[ 0 - 0.00, 1 - 0.03624, 2 - 0.04163, 3 - 0.04514, 4 - 0.04782, 5 - 0.05]
Изменим power на 0.5:
[ 0 - 0.00, 1 - 0.02236, 2 - 0.03162, 3 - 0.03873, 4 - 0.04472, 5 - 0.05]
Обратите внимание, что при power = 0.5 альфы жестче!
Применение:
- Мы можем либо заранее определиться с количеством подглядываний, и это предпочтительнее, так как это делает наш подход более строгим по процедуре. Определяемся - получаем на каждый день подглядывания альфу.
- Либо мы можем в момент, когда хотим подсмотреть тест, поставить % размера выборки от плана.
Вот, собственно, и всё!
Конечно, подводный камень, то есть наивность, тут в том, что на самом деле ошибка 1-го рода хотя бы 1 раз тут не 0.05, а больше: мы можем совершить ошибку или в первом тесте или во втором и пр. отсюда вероятность это сумма этих альф, для примера с power = 0.2 это 0.22...
Границы по Pocock
Когда слышишь "ошибка 1-го рода хотя бы 1 раз", то в голову может прийти классика множественного тестирования - какая-нибудь поправка, например, в самом классическом виде по Бонферрони это одна и та же ужесточенная альфа на все тесты.
Для наших 4+1 подглядываний, - или interim analysises + контрольный, - наша альфа по Pocock была бы 0.0158. На самом деле Pocock (1977) вычислял не столько альфу, сколько границы значимости, а уже от них можно было рассчитать область значимости - альфу.
Метод хорош только в случае, если все наши подглядывания равноценны, что не так, банально потому, что у нас идет накопление информации к моменту каждого нового подсмотра. Можно даже сказать так: всякий промежуточный тест это оценка по некоторой доле измерений от запланированной. Например, 1-ое подглядывание = 20%, 2-ое - 40%, ... финальное - 100%. И 80% как будто состоятельнее, чем 20%.
Чуть более официально эти доли называются как information fraction
Метод Lan, Kim и DeMets
На самом деле функции alpha-spending как раз и придумали исследователи Lan, Kim и DeMets в 1983 году. И их подход был кратно мудрее, чем наивный. Чтобы ошибка 1-го рода хотя бы 1 раз была согласно альфе в дизайне, скажем, 0.05, наши все подглядывания должны иметь такие альфы, чтобы в сумме давать 0.05!
Как этого добавиться?
Возьмём степень (power) у нашей базовой alpha-spending функции = 2:
[ 0 - 0.00, 1 - 0.002, 2 - 0.008, 3 - 0.018, 4 - 0.032, 5 - 0.05]
Далее нам нужно вычесть из каждой альфы предыдущую, то есть вот так:

Пример:
- α*(t) - α*(t-1) для t = 0.20 и t = 0.00 это будет 0.002
- α*(0.4) - α*(0.2) = 0.008 - 0.002 = 0.006
- α*(0.6) - α*(0.4) = 0.018 - 0.008 = 0.01
и тд.
Мы получим:
[0 - 0.00, 1 - 0.002, 2 - 0.006, 3 - 0.01, 4 - 0.014, 5 - 0.018]
Это и будут нашими новыми альфами для каждого промежуточного анализа и контрольного в том числе (0.018). В таком подходе у нас действительно будет растрата альфы в дизайне - при каждом подглядывании у нас теряется некоторая доля, прочая остается на все последующие.
Обратите внимание, что проведя тест за тестом c такими альфами, у нас ошибка 1-го рода в первом подглядывании или во втором или ... и так до контрольного будет равна альфе!

Так как альфа это источник критических значений (bounders - b), то

Вот наши b, границы значимости для двустороннего теста, когда у нас большие выборки и наш t-test свёлся к z-тесту:

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

В целом, тут все тот же мотив, который пронизываем многое в AB: всегда чем-то жертвуешь в угоду контроля некоторого параметра. Тут классика: хотим контролировать ошибку 1-го рода на все семейство тестов (FWER) - увеличиваем ошибку 2-го рода!
И последним не стоит пренебрегать! Есть такая ошибка как M-ошибка, ошибка эффекта. То есть у нас есть переоценка эффекта, в случае, если мы обнаружили значимость. Мощность с M-ошибкой напрямую связана, см. этот пост: вот примерная функциональная зависимость:

Если у вас стат. значимый эффект в момент подсмотра, то именно тут рекомендую оценить мощность и перевзвесить эффект (формулу выводили в уже указанном посте):

Поздравляю, мы на самом деле только что рассмотрели подход в ускорении наших тестов как Group Sequential Testing, также старый как мир
Дополнительно: функций alpha-spending много, есть и те, которые определяются через кумулятивное распределение (O'Brien-Fleming); правильный выбор я бы назвал таким, который как минимум корректно ведет себя в симуляции в плане мощности (а не как у меня) и который вы сами находите подходящим для вашего случая / эксперимента.
Futility
Но что если мы хотим останавливать тест не только, когда обнаружим стат. значимый результат, но и тогда, когда крайне маловероятно, что наш ожидаемый эффект вообще существует? Снова-таки чтобы не тратить зря времени!
Такой подход называется как futility, то есть обнаружение тщетности наших попыток. И здесь мы от alpha-spending просто переходим к beta-spending, правда не совсем так, как ожидаем.
Pampallona и Kim (2021)
Функция у нас может быть та же, что и для alpha-spending + тот же алгоритм вычета, просто в нее мы подставляем beta (классика = 0.2) и получаем, например, вот такие значения:
[0 - 0.00, 1 - 0.008, 2 - 0.024, 3 - 0.04, 4 - 0.056, 5 - 0.072], в сумме 0.2
Однако вопрос, а что с ними делать, куда прикладывать?
Если мы делаем все с умом, то у нас должен быть согласованный MDE, на который мы согласны, либо в худшем случае желаемый, экономически целесообразный эффект, под который мы собрали размер выборки.
MDE - это минимальный эффект, что мы можем обнаружить при нашей конфигурации теста, если он вообще есть! А значит, его может и не быть: тогда в каком случае мы могли бы отвернуть гипотезу о том, что он есть? Только если мы это сделаем относительно HA! Но как получить HA? На конкретный момент подглядывания мы можем свиднуть H0 на запланированный MDE/SE, где SE - это оценка отклонения разниц текущего подглядывания, если это two-sample test.
Обычно, все мыслят сдвиг вправо, некоторый прирост, у нас так же!
Выходит, наш список beta's - это список на самом деле альф для HA.
Но на самом деле все гораздо проще: нам нужно построить критическое значение согласно конкретной beta на H0 и сдвинуть имеено его на MDE/SE!
Важно:
- Мы не можем прибавить к Mu_H0 просто MDE, потому что мы оперируем стандартным распределением, значит должны именно получить z-score MDE относительно H0 в рамках конкретных выборочных данных на момент промежуточной оценки.
- Раз beta теперь альфа, то при проверке "Mu_B не равно Mu_A" мы обязаны использовать двусторонний тест, а значит каждую beta делить пополам. Возьмем beta №1 дает зону для HA cлева = 0.008 / 2 = 0.004, смотрим:

Интерпретация такая: если у нас значение меньше сплошной фиолетовой линии, то отклоняем гипотезу о таком MDE, тщетно (futilely) искать таковой!
Выходит, в рамках каждого теста у нас две ассиметричные границы - для стат. значимости и для futility:

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

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

2. Однако если эффект есть, у нас есть риск совершить ошибку, - не знаю, как верно назвать, - futility-error: не просто не обнаружить эффект как это есть при ошибке 2-го рода, а признать, что его нет вообще! Потому что именно так мы и будем думать и это будет выраженнее, чем просто при отсутствии стат. значимости. График похож на предыдущий, и он же может служить и оценкой fulfility-error:

Однако там, где у нас много подглядываний, там жестче границы, а поэтому много подглядывать для нас играет тут положительную роль.
В принципе, рассчитав MDE и зная поведение этой ошибки от кол-ва подсмотров, мы можем сделать и ее коррекцию, если только планируем (sic!) подглядывания. Например, пускай beta = alpha для beta-spending. Но как всегда, ценой увеличения того, что нам будет сложнее остановить тест, если эффекта а-ля MDE нет!
Заключение
На мой взгляд Group Sequential Testing через alpha/beta-spending куда как прямолинейнее и понятнее, чем mSPRT - сплошь привычные нам альфы и беты, просто под другим углом.
Это не серебряная пуля и нам будем чем жертвовать при его применении: мощностью и выводом/ошибкой про тщетность. Но с другой стороны, как уже и сказал, это весьма прозрачный метод в использовании с простыми концепциями под капотом. Легко объяснить, легко имплементировать.
Пользуйтесь!