Чому A/B-тестування ML-моделей ламається в продакшені
Найдорожчий збій в A/B-тестуванні ML-моделей — не падіння сервісу, а впевнена відповідь не тією моделлю. У реєстрі лежить потрібна версія, але ознаки на вході збирає інший код, довідники оновлено окремо, і розбіжність версій моделі між контурами спливає через тижні після запуску.
Друга поломка — метрики. Тестування моделей у продакшені порівнює контроль та експеримент на різних зрізах: в одному контурі вікно агрегації ширше, в іншому викиди відфільтровано інакше.
Нестабільні метрики дають розкид більший за сам ефект моделі, і експеримент закривають за шумом, а не за результатом.
Третя — невідтворюваність. Через місяць уже ніхто не відновить, які ознаки, версії довідників і пороги стояли на момент заміру.
Навіть коли версії артефактів логуються в MLflow, у реєстрі зберігається модель, а не повний контекст її запуску — порівняти експерименти post factum нічим.
І четверта — відкат моделі. Якщо попередній артефакт не збережено разом із конфігурацією, повернення перетворюється на ручну збірку під тиском: якість падає просто зараз, а відновлення займає години. Ціна затримки вимірюється часткою зіпсованих рішень користувача за цей час.
Розбираємо, як це закривається: незмінні версії моделі та ознак, єдиний шар розрахунку метрик для обох контурів і відкат однією операцією.
Вимірювані ефекти: затримка, відкати, частка ручних перевірок
Керований контур змінює сам цикл: гіпотеза перевіряється на контрольній і тестовій групах до того, як версія поїде на всю аудиторію.
Перемикання частки трафіку стає операцією в налаштуваннях експерименту, а не окремим релізом, тому час від гіпотези до викатки моделі скорочується з днів до годин — узгодження замінює регламент.
Частка ручних звірок падає з тієї ж причини: метрики рахуються на одному розміченому зрізі для обох груп, значущість ефекту перевіряється автоматично, а аналітик дивиться лише спірні сегменти.
Те, що раніше потребувало щоденних вивантажень і зведення таблиць вручну, перетворюється на звіт, який оновлюється сам.
Окремо зникає клас відмов, який коштує найдорожче: «тиха» деградація на вузькому сегменті та викатка версії, навченої на застарілому зрізі.
Порогова метрика якості зупиняє розкатку і повертає попередню версію автоматично — час відновлення вимірюється хвилинами, а простій через помилку перестає бути подією на весь день.
MLflow у цій схемі відповідає за відтворюваність: кожна версія моделі прив'язана до параметрів, даних і метрики, тому результат експерименту можна переперевірити, а відкат — це перемикання на зафіксовану попередню версію, а не збірка заново. Ефект від експерименту стає вимірюваним до викатки, а не за фактом інциденту.
Варіанти розкатки: тіньовий запуск, канарковий, поетапний
Режим випуску обирають не за зручністю, а за тим, яку частку трафіку допустимо віддати новій моделі і наскільки дорого коштує помилка на живому запиті.
Тіньовий запуск відповідає на питання «чи збігається нова модель із поточною», канаркова розкатка — «чи не деградує продукт на реальних користувачах», поетапний випуск — «який із варіантів виграє за метрикою».
Обмеження режимів упираються в маршрутизацію запитів і в спосіб збору метрик. Поки метрика не прив'язана до версії моделі та до когорти, будь-яке порівняння підходів перетворюється на набір здогадів.
| Режим | Коли підходить | Обмеження |
|---|---|---|
| Тіньовий запуск | Перенесення логіки на нову модель, перевірка якості на реальному трафіку без впливу на відповідь | Варіант не впливає на видачу, тому продуктові метрики щодо нього не збираються; потрібен дублювальний контур і запас за ресурсами |
| Канаркова розкатка | Модель уже навчена, але потрібна страховка від деградації на живих запитах | Потребує автоматичного відкату й розмітки метрик за когортами; на малій частці трафіку статистика набирається повільно |
| Поетапний випуск | Потрібна перевірка ефекту за сегментами й порівняння версій із накопиченням даних | Триває довше; без контролю однорідності груп порівняння втрачає сенс |
На практиці режими комбінують: тіньовий запуск як допуск до живого трафіку, канаркова розкатка як страховка, поетапний випуск як спосіб зібрати статистику за сегментами.
Етапи впровадження: від аудиту до передачі в експлуатацію
Порядок робіт будуємо так, щоб помилку в схемі було видно до запуску на реальному трафіку, а не через тиждень розбору метрик. Кожен етап закінчується перевірюваним артефактом — тоді вмикання не перетворюється на «увімкнули й дивимося».
-
Аудит схеми експерименту. Дивимося, яка метрика вважається цільовою, де і на якому обсязі трафіку вона збирається, чи достатньо спостережень, щоб розрізнити версії.
Перевіряємо, чи не перетинаються групи з іншими запущеними експериментами і чи немає зміщення в тому, як події потрапляють у сховище.
-
Розмітка трафіку. Визначаємо одиницю рандомізації — користувач, сесія або запит — і спосіб закріплення: та сама ознака дає стабільну групу при повторних візитах. Фіксуємо частку трафіку на експеримент і правила виключення, щоб у порівняння не потрапили службові запити.
-
Налаштування контуру. Розгортаємо сервіс порівняння: він приймає запит, адресує його потрібній версії моделі, а передбачення та ознаку версії пише в лог. Тіньовий режим дає змогу прогнати нову версію на живому трафіку, не впливаючи на відповідь користувачу.
-
Контрольні перевірки. Проганяємо A/A-тест на однорідність груп, звіряємо розподіл ознак між гілками, перевіряємо поведінку при відмові однієї з версій і при недоступності сервісу порівняння.
Метрику на обох гілках рахуємо за одним і тим самим правилом — інакше різниця буде в розрахунку, а не в моделі.
-
Передача в експлуатацію. Віддаємо команді регламент запуску й зупинки, опис відкату, панель із метриками та налаштування оповіщень. Документуємо розмітку й конфігурацію контуру, щоб наступний експеримент запускався без нас.
Артефакти передачі: схеми, політики доступу, регламенти
Після здачі контуру в команди замовника залишається комплект артефактів, за яким нові A/B-тести запускаються самостійно — без відновлення логіки з листування та пам'яті інженерів. Нижче перелік того, що передаємо і навіщо кожен документ потрібен у роботі.
- Конфігурації контуру. Файли розгортання та параметри середовищ: тестове й продуктивне відрізняються лише значеннями, тому новий варіант моделі піднімається копіюванням конфігурації, а не збіркою заново.
- Схеми потоків даних. Шлях від запиту до запису метрики: де фіксується варіант, як рахується цільова метрика, де розходяться вибірки. За схемою новий інженер розбирається в контурі за годину.
- Політики доступу. Розмежування ролей: хто змінює частку трафіку на варіант, хто зупиняє тест, кому доступні сирі логи. Експеримент не можна зламати випадковою правкою.
- Регламент експерименту. Обов'язкові кроки: гіпотеза, цільова метрика, мінімальний обсяг вибірки, критерій зупинки та порядок відкату на контрольну модель.
- Шаблон звіту. Єдина форма фіксації результату: версії порівнюваних моделей, період, метрики за групами, висновок і рішення — продовжувати, доопрацьовувати або закривати.
- Інструкції та навчання. Розбір контуру з командою замовника та короткі пам'ятки з типових операцій: запуск тесту, зупинка, відкат, читання звіту, додавання нової метрики.
Такий комплект знімає залежність від підрядника: зміни в контур вносяться за документованими правилами, а не за усними домовленостями. Обговоримо задачу — повернемося з планом робіт і складом артефактів під ваш контур.
Кейс: прибрали розбіжність версій між контуром і продакшеном
Замовник викочував кілька моделей на місяць, а контур навчання й продакшен жили окремим життям. Артефакти переносили скриптами, ознаки перезбирали руками, і розбіжність версій між середовищами спливала вже на живому трафіку.
Сліпа викатка без розбиття аудиторії не давала помітити деградацію раніше, ніж просідали метрики продукту.
До перебудови дві-три ручні звірки на тиждень забирали в інженера по півдня, відновлення після інциденту розтягувалося на годину з гаком, а відкат доводилося запускати руками майже в чверті викаток.
Що змінили
Обидва середовища звели до єдиного реєстру моделей на MLflow: версія, набір ознак і якість на контрольній вибірці прив'язані до одного артефакту, а переведення в продакшен відбувається лише через автоматичну перевірку маніфесту.
Викатку перевели на розбиття трафіку 5/95 з автооткатом за ключовою метрикою замість сліпої заміни всієї моделі. Рішення логуються разом із версією, тому розбір збою починається з готової картини, а не з ручної звірки логів.
Через квартал ручні звірки скоротилися до однієї планової, розбіжності версій між контуром і продакшеном не відтворювалися жодного разу, а відновлення займало хвилини: відкат спрацьовував сам за порогом.
Частка шляхів, покритих автоматичними перевірками перед викаткою, зросла з третини до дев'яти десятих.
Що робити, якщо модель уже в продакшені, а розмітки немає?
Відсутність розмітки під експеримент блокує не порівняння версій, а відсутність керованого перемикача між ними. Якщо модель уже в продакшені, спочатку відокремлюємо її від коду сервісу: версія моделі та її параметри мають підставлятися з конфігурації, а не бути вшитими у виклик.
Далі на вході сервісу з'являється тонкий шар маршрутизації, який за однією ознакою запиту вирішує, яка версія його обробить.
Поки розмітки немає, спираємося на непрямі сигнали: логи рішень, відгук користувачів, частка ручних правок після відповіді моделі, розподіл передбачень за класами та зсув цього розподілу в часі.
Цього достатньо, щоб упіймати деградацію та розбіжність версій, а повноцінний набір перевіркових прикладів досоздається пізніше — уже під зібраний потік реальних запитів.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Тіньовий режим | Не можна ризикувати якістю відповіді користувачу | Дві версії рахуються паралельно, потрібен запас потужності |
| Канарковий запуск | Потрібно швидко впіймати явну відмову на малій частці трафіку | Показує збої, але майже не розрізняє якість |
| A/B за часткою трафіку | Метрика вимірювана й накопичується за дні, а не місяці | Потребує стійкого розподілу запитів за групами |
Перехід робимо поступовим: одиниці відсотків трафіку на нову версію, витримка на вибраній частці, потім крок угору.
На кожному кроці порівнюємо ті самі сигнали, що збирали до запуску, і тримаємо готовим відкат — повернення версії за конфігурацією, без перезбирання й зупинки сервісу.
Міграція без зупинки виходить саме з цього: маршрутизація живе окремо від моделі, тому підміна версії займає секунди і не перериває обробку запитів.
Поріг зупинки задаємо заздалегідь і у вимірюваних величинах: просідання цільової метрики, зростання частки помилок, розбіжність розподілів вище допустимого. Якщо поріг пройдено — трафік повертається на попередню версію автоматично, а не за дзвінком чергового.
Часті технічні сумніви замовників та інженерів
Сумніви на цьому етапі зазвичай технічні: чи вистачає даних, чи не спотворює картину сезонність, що робити з паралельними правками правил і моделі. Нижче — короткі відповіді про те, як ми закриваємо ці місця в розмітці експерименту.
Чи вистачить трафіку для достовірних метрик?
Рахуємо не загальний трафік, а кількість цільових подій у кожній групі. Мінімальний розмір вибірки визначаємо до старту — від очікуваного ефекту та розкиду метрики. Якщо подій менше, звужуємо метрику або подовжуємо вікно спостереження, а не підганяємо тест під красиву цифру.
Чи не спотворить картину сезонність?
Розбиття йде за стабільним ідентифікатором користувача, тому обидві групи потрапляють в одні й ті самі дні, акції та пікові години.
Порівняння йде паралельно в одному вікні, а результат переперевіряємо на сусідніх періодах — ефект, який тримається лише в одному тижні, у висновки не йде.
Що робити, якщо одночасно змінюються правила й модель?
Один експеримент — одна зміна. Якщо міняти обидва відразу, ефект не розділити: незрозуміло, що дало приріст, а що його з'їло. Розносимо правки на послідовні етапи або плануємо схему з перевіркою спільного впливу, щоб оцінити внесок кожного фактора.
Кому доступні результати і що з безпекою даних?
Звіти дивимося за ролями: продуктовій команді — зрізи за сегментами й метриками, інженерам — події у сховищі для розбору. Персональні дані у звітність не потрапляють, ідентифікатори псевдонімізовано, вивантаження обмежене політикою доступу й логується.
Обговоримо задачу: як ми почнемо роботу над контуром
Щоб контур A/B-тестування моделей не перетворився на череду переробок, починаємо з розбору того, що вже працює.
Від вас — доступ до репозиторію інфраструктури, до поточної схеми виведення моделей у продакшен і до вивантаження за цільовою метрикою за минулі періоди: без цього не порахувати розмір груп і тривалість вікна спостереження.
Порядок перших кроків:
- описуємо поточний шлях моделі: де зберігаються версії, хто перемикає трафік між ними, що вже логується;
- фіксуємо цільову метрику та метрики-обмежувачі, які не мають просісти за жодного результату тесту;
- вибираємо одиницю рандомізації — клієнт, сесія або запит — і спосіб закріпити її за групою, щоб користувач не перескакував між варіантами;
- узгоджуємо схему розрахунку: частки трафіку, мінімальний значущий ефект, критерій дострокової зупинки;
- проєктуємо відкат: однією командою весь трафік повертається на контрольну модель за лічені хвилини;
- додаємо спостереження за дрейфом вхідних даних і за розбіжністю розподілів між групами;
- розмічаємо обсяг робіт: де потрібні зміни в коді виведення, а де достатньо конфігурації.
За підсумками розбору повернемося з оцінкою та планом: склад етапів, строки за кожним, зона відповідальності нашої команди та вашої. Наступний крок — опишіть задачу, і ми призначимо розбір.







