Монте-Карло — що є дуже потужний інструментом у статистиці.
З його допомогою ми відповімо з вами на запитання:
Як перевірити ваш критерій? Валідний він на практиці чи ні?
Наприклад, чи працює \(t\)-test на малих розмірах вибірок?
У вас є 2 різні критерії. Як зрозуміти, який критерій краще підходить для вашої задачі?
Згадаємо \(t\)-критерій
маленька вибірка
велика вибірка
нормальний розподіл
t-test
t-test
будь-який розподіл
t-test
Що означає, що критерій коректний?
Критерій рівня значущості \(\alpha\) означає, що ймовірність невірно відкинути нульову гіпотезу \(\le \alpha\).
А це зі свого боку означає, що якщо нескінченно багато разів повторити один і той самий експеримент, у якому правильна нульова гіпотеза, генеруючи наново експеримент, то кількість хибнопозитивних спрацьовувань буде меншою за \(\le \alpha\) відсотків.
Процедура
Коротко:
Створюємо код критерію, який ми будемо перевіряти.
Генеруємо якомога більше експериментів, де вірна \(H_0\).
Проганяємо на них придуманий критерій.
Перевіряємо, чи правда, що тільки в \(\alpha\) відсотків випадків критерій відкидається?
Розгорнуто:
Насамперед треба вибрати розподіл, який буде описувати наші дані. Наприклад, якщо у нас метрика конверсії, то це бернуллівський розподіл, а якщо метрика — виторг, то краще використовувати експоненціальний розподіл як найпростіше наближення.
Завести лічильник bad_cnt = 0.
Далі в циклі розміру \(N\), де \(N\) — натуральне число від 1000 до нескінченності (чим воно більше, тим краще):
Симулювати створення вибірки з розподілу, обраного на першому кроці. Так, щоб вірною була \(H_0\).
А в разі AB-тесту симулювати треба не 1 вибірку, а 2: для тесту і контролю.
Запустити на згенерованих даних критерій, що перевіряється.
Далі перевірити, pvalue < alpha. Якщо так, то збільшити лічильник bad_cnt на 1. Тут ми перевіряємо, чи помилився критерій на поточній симуляції, чи ні.
Порахувати конверсію bad_cnt / N.
Якщо вона приблизно збігається з \(\alpha\), то все добре.
Якщо вона менша за \(\alpha\), то в принципі це адекватний критерій на практиці, просто він буде менш потужний, ніж критерій, що помиляється рівно в \(\alpha\) відсотку випадків.
Але на практиці варто перевірити: а теоретично така ситуація можлива? Чи це помилка в коді критерію?
Якщо критерій помиляється більше, ніж у \(\alpha\), то значить він некоректний і ним не можна користуватися. Використовуючи такий критерій, ви будете помилятися частіше, ніж треба, а отже, ваша компанія втрачатиме більше грошей.
\(t\)-test: мала вибірка, нормальний розподіл
numpy.random.seed(42)bad_cnt =0N =10000alpha =0.05sample_dist = norm(loc=2, scale=3)mu0=sample_dist.expect()for i inrange(N):# Генерую вибірку тесту і контролю test = sample_dist.rvs(5) control = sample_dist.rvs(5)# Запускаю критерій і рахую p-value pvalue = ttest_ind(test, control, alternative='two-sided').pvalue# Перевіряю, що pvalue < alpha bad_cnt += (pvalue < alpha)print(f"FPR: {round(bad_cnt / N, 4)}")
FPR: 0.0519
😩
😃Довірчий інтервал для FPR!
Порахувати отриманий FPR і побудувати довірчий інтервал для нього. Якщо \(\alpha\) лежить у ньому, значить усе добре, а інакше розбираємося, що пішло не так.
Довірчий інтервал можна побудувати різними способами: наприклад, використовуючи ідеї побудови довірчих інтервалів із другої лекції.
Але можна зробити простіше: довірчий інтервал Вілсона: він не такий точний, як ми виводили раніше, зате він швидший і працює з «коробки». Його не треба реалізовувати самому.
proportion_confint( count = bad_cnt, nobs = N, alpha=0.05, method='wilson' )
(0.04772180742973847, 0.05642233191006188)
\(t\)-test: мала вибірка, експ. розподіл
numpy.random.seed(42)bad_cnt =0N =10000alpha =0.05sample_dist = expon(scale=10)mu0=sample_dist.expect()for i inrange(N):# Генерую вибірку тесту і контролю test = sample_dist.rvs(5) control = sample_dist.rvs(5)# Запускаю критерій і рахую p-value pvalue = ttest_ind(test, control, alternative='two-sided').pvalue# Перевіряю, що pvalue < alpha bad_cnt += (pvalue < alpha)print(f"FPR: {round(bad_cnt / N, 4)}")
FPR: 0.0389
proportion_confint( count = bad_cnt, nobs = N, alpha=0.05, method='wilson' )
def check_criterion(test_dist, control_dist, sample_size, N_exps=10000, to_print=True):""" Функція для перевірки t-test критерію для AB-тесту Повертає довірчий інтервал для FPR, якщо прапор to_print = False. Інакше друкує результат. Параметри: - test_dist: Розподіл тестової вибірки в експерименті - control_dist: Розподіл контрольної вибірки в експерименті - sample_size: розмір вибірки тесту і контролю - N_exps: кількість експериментів, за якими потім рахується FPR - to_print: друкувати результат чи ні. Якщо ні, то функція повертає довірчий інтервал для FPR. """ numpy.random.seed(35) bad_cnt =0 alpha =0.05for i inrange(N_exps):# Генерую вибірку test = test_dist.rvs(sample_size) control = control_dist.rvs(sample_size)# Запускаю критерій і рахую p-value pvalue = ttest_ind(test, control, equal_var=False, alternative='two-sided').pvalue# Перевіряю, що pvalue < alpha bad_cnt += (pvalue < alpha)if to_print:print(f"FPR: {round(bad_cnt / N_exps, 4)}")print(f"CI={proportion_confint(count = bad_cnt, nobs = N_exps, alpha=0.05, method='wilson')}")else:return proportion_confint(count = bad_cnt, nobs = N_exps, alpha=0.05, method='wilson')
Якщо треба більша точність, то можна збільшити кількість експериментів N_exps.
Висновок
Щоб перевірити критерій, треба вміти багато разів проводити один і той самий експеримент.
Чи правильно реалізовано критерій?
Перевірте його! Можна на спеціально змодельованих даних.
Чи можна використовувати цей критерій для нашої задачі?
Перевірте його! Але тільки потрібно правильно згенерувати експеримент.
Як знайти мінімальний розмір вибірки у \(t\)-test?
Перевірте \(t\)-test на різних розмірах вибірки. З того моменту, як \(\alpha\) лежить у довірчому інтервалі — можемо вважати, що \(t\)-test буде працювати.
маленька вибірка
велика вибірка
нормальний розподіл
t-test
t-test
будь-який розподіл
Монте-Карло
t-test
Як імітувати експеримент?
Є 2 відповіді на це запитання:
Генерація тесту і контролю через штучне моделювання. За допомогою різних розподілів можна спробувати наблизити реальний розподіл на даних. Наприклад:
Для генерації виручки використовувати експоненціальний розподіл. Чим більша виручка від користувача — тим менше таких людей.
Для генерації конверсійних вибірок (наприклад, клікне/не клікне) використовувати бернулліївську вибірку.
Іноді можна брати суміш розподілів: нехай 90% користувачів нашого сайту приносять нульову виручку. Тоді можна перемножити бернуллівський розподіл на експоненціальний для моделювання виручки від користувача.
Також для перевірки критерію не обов’язково розподіли в тесті та в контролі мають збігатися. Вони можуть бути різними, але мат. очікування співпаде, як було в прикладі раніше.
Як імітувати експеримент?
Датасети на історичних даних компанії. У багатьох компаній є логування подій. Тоді ми зможемо прямо на реальних даних оцінити дієздатність критерію! І не потрапити в пастку того, що на штучних вибірках критерій валідний, а на реальних даних ні. Наприклад, у нас є дані про транзакції користувачів за кілька років. Це вже один готовий датасет: ви ділите всіх користувачів на тест та контроль і отримуєте один «експеримент» для перевірки вашого критерію.
Монте-Карло на історичних даних
Наші користувачі розміщують оголошення. Кожне оголошення відноситься тільки до однієї категорії товарів і розміщено тільки в одному регіоні. Звідси виникає нехитрий алгоритм:
Розіб’ємо всі розміщення користувачів на чотири (або N у загальному випадку) категорії: автомобілі, спецтехніка, послуги та нерухомість. Тепер наш датасет можна розбити на ці підкатегорії: наприклад, в одному датасеті дивитися виручку користувача тільки в цій підкатегорії.
Поділимо датасети за місяцями: датасет витрат користувача за листопад, за грудень і так далі.
Ще всі метрики можна поділити за суб’єктами країни або за групою суб’єктів.
Об’єднаємо всі 3 правила в одне. Наприклад: датасет витрат користувача за листопад у Києві.
Тепер у нас є велика кількість датасетів і в кожному з них є користувачі. Поділимо користувачів випадково на тест та контроль і отримаємо фінальні датасети для валідації придуманих статистичних критеріїв.
Який критерій краще?
Постановка
У нас є 2 критерії, які ми хочемо порівняти. Як вибрати кращий?
Порівняти потужність критеріїв!
генеруємо експерименти, де вірна \(H_1\);
рахуємо TPR для кожного критерію;
Порівняння критеріїв за потужністю
numpy.random.seed(42)rej_cnt =0N =10000alpha =0.05sample_dist = norm(loc=2, scale=3)mu = sample_dist.expect()for i inrange(N):# Генерую вибірку тесту і контролю test = sample_dist.rvs(15) control = sample_dist.rvs(15) *2# Запускаю критерій і рахую p-value pvalue = ttest_ind(test, control, equal_var=False, alternative='two-sided').pvalue# Перевіряю, що pvalue < alpha rej_cnt += (pvalue < alpha)print(f"TPR або потужність: {round(rej_cnt / N, 4)}")
TPR або потужність: 0.1938
Порівняння критеріїв за потужністю
numpy.random.seed(42)rej_cnt =0N =10000alpha =0.05sample_dist = norm(loc=2, scale=3)mu = sample_dist.expect()for i inrange(N):# Генерую вибірку тесту і контролю test = sample_dist.rvs(15) control = sample_dist.rvs(15) *3# Запускаю критерій і рахую p-value pvalue = ttest_ind(test, control, equal_var=False, alternative='two-sided').pvalue# Перевіряю, що pvalue < alpha rej_cnt += (pvalue < alpha)print(f"TPR або потужність: {round(rej_cnt / N, 4)}")
TPR або потужність: 0.343
Порівняння критеріїв за потужністю
numpy.random.seed(42)rej_cnt =0N =10000alpha =0.05sample_dist = norm(loc=2, scale=3)mu = sample_dist.expect()for i inrange(N):# Генерую вибірку тесту і контролю test = sample_dist.rvs(50) control = sample_dist.rvs(50) *3# Запускаю критерій і рахую p-value pvalue = ttest_ind(test, control, equal_var=False, alternative='two-sided').pvalue# Перевіряю, що pvalue < alpha rej_cnt += (pvalue < alpha)print(f"TPR або потужність: {round(rej_cnt / N, 4)}")