Модель у продакшені

Число на тесті — це обіцянка, а не властивість. Далі модель виходить до людей, світ починає змінюватись під нею, і головним питанням стає не «наскільки вона точна», а «коли ти помітиш, що вже ні».

У попередній темі ми довели детектор шахрайських оголошень до кінця: постановка, чесний поділ, базова лінія, ітерації, один-єдиний замір на тесті й обґрунтоване рішення запускати. Остання її фраза була про те, що модель у продакшні деградує. Ця тема — про те, як саме, як швидко і що з цим робити.

Дошка та сама: оголошення про вживані телефони, модель відповідає на питання, чи є оголошення шахрайським у мить публікації. Тільки тепер вона працює в бою. На день запуску ми поміряли її на 500 відкладених оголошеннях і отримали F1 = 0.843 (precision 0.854, recall 0.833). Це число ми записали у звіт. Далі буде видно, скільки воно проживе.

Кінець табличної частини курсу. Ця тема закриває блок «Підсумок» — сім блоків, у яких дані були таблицею. Далі починаються нейронні мережі — але жодне правило звідси там не скасовується, воно просто стає дорожчим.

ПерехідНавчання закінчилось — робота почалась

Модель у зошиті й модель у бою — це два різні інженерні обʼєкти, які випадково мають однакові ваги. У зошиті в тебе є таблиця: усі рядки одразу, усі колонки заповнені, усі правильні відповіді відомі, а часу порахувати — скільки завгодно. У бою немає жодної з цих чотирьох речей.

Дані приходять по одному. Не таблиця на тисячу рядків, а одне оголошення, яке щойно натиснули «Опублікувати». Усе, що ти рахував «по колонці» — медіану, середнє, квантилі для масштабування — у цю мить порахувати ні з чого. Значить, воно має бути вже пораховане й покладене поруч із моделлю. Ця думка проста, і саме на ній ламається більшість перших запусків.

Відповідь потрібна швидко. Людина натиснула кнопку й дивиться на екран. Якщо модель думає секунду, це вже помітно; якщо три — оголошення публікують без перевірки, бо ніхто не тримає користувача в очікуванні. Тобто в бою в тебе зʼявляється бюджет часу, якого в зошиті не існувало взагалі.

Ніхто не скаже, чи вона вгадала. Оце найважче. У зошиті колонка з правильними відповідями лежала поруч. У бою правильна відповідь на питання «чи це був шахрай» приходить тоді, коли покупець втратив передоплату й написав скаргу, — тобто через тижні. А для більшості оголошень не приходить ніколи: вони просто зникають із дошки, і ніхто не знає, чим вони були.

І світ не стоїть. Таблиця в зошиті — фотографія одного дня, продакшн — відео. Ціни падають, зʼявляються нові моделі телефонів, а шахраї змінюють поведінку саме тому, що їх почали ловити.

Життєвий цикл моделі

Тема 30 доводить до четвертого блоку й зупиняється. Усе, що після нього, — це кільце, яке крутиться, поки система жива.

Кільце, а не пряма. Проєкт із теми 30 — це перші три блоки й один перехід. Права й нижня частина схеми повторюються щомісяця весь час, поки модель комусь потрібна, і саме вони становлять більшу частину вартості системи.

У темі 29 ми міряли моделі не лише якістю, а й вартістю підтримки. Тепер видно, звідки та вартість береться: модель, яка щомісяця вимагає перенавчання, розбору скарг і звірки довідників, коштує стільки, скільки коштує людина, яка це робить, — і платять цю ціну щомісяця.

РежимиПакетно чи на запит

Перше архітектурне рішення після запуску — коли рахувати. Тут є дві незалежні осі, і їх постійно плутають в одну.

Вісь перша: коли навчається модель. Статичне навчання — навчили один раз, поклали файл із вагами в сервіс, він там і живе. Динамічне — модель перенавчається за розкладом чи за подією, і в бою щоразу опиняється нова.

Вісь друга: коли рахується відповідь. Статичний висновок (його ще звуть пакетним, batch) — раз на добу проганяємо всі активні оголошення й складаємо відповіді в таблицю; сервіс потім просто дістає готове. Динамічний висновок (онлайновий) — відповідь рахується в мить запиту.

Дві осі дають чотири комбінації, і в кожної своя ціна. Покрути перемикачі й подивись, що змінюється для нашої дошки.

Чотири способи запустити ту саму модель

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

навчання
висновок
затримка відповіді0 мс
свіжість прогнозудо 24 год
вік моделіросте без межі
вартість на місяць1 прогін/добу
Затримка й свіжість — це не одне й те саме. Пакетний висновок відповідає миттєво, бо відповідь уже лежить у таблиці, — але вона вчорашня, і на щойно опубліковане оголошення її просто немає. Онлайновий рахує довше нуля, зате відповідає саме про те оголошення, яке зараз перед ним.

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

SkewTraining/serving skew

А тепер головна пастка теми, і вона трапляється до будь-якого дрейфу — у перший же день роботи.

Training/serving skew (розбіжність між навчанням і бойовим застосуванням) — це коли одна й та сама ознака рахується двома різними способами: одним у зошиті, іншим у сервісі. Модель при цьому в бою бачить не ті числа, на яких її навчали, — і робить не ті висновки.

Це той самий механізм, що й витік через передобробку з теми 09, де медіана для заповнення пропусків рахувалась по всій таблиці. Різниця лише в тому, де це виявляється: витік підробляє число у звіті, а skew підробляє роботу системи. Перше коштує ніяковості на презентації, друге — грошей.

Наша ознака — price_deviation. Рахується вона так:

price_deviation = | ln( ціна ÷ типова ціна ) |

Словами: у скільки разів ціна цього оголошення відрізняється від типової — байдуже, в який бік. Розберемо знаки. Типова ціна — медіана цін усередині групи «модель телефона + рік випуску»; ці медіани заздалегідь порахували й склали в довідник. ln — натуральний логарифм: він перетворює «у скільки разів» на число, яке зручно порівнювати, і дає нуль там, де відношення дорівнює одиниці — ln(1) = 0, ln(2) ≈ 0.69, ln(0.5) ≈ −0.69. Дві вертикальні риски — модуль, тобто знак мінус ми відкидаємо.

Логарифм тут не прикраса. Без нього «удвічі дешевше за типову» дало б 0.5, а «удвічі дорожче» — 2.0, і однакові за силою відхилення в різні боки модель бачила б як різні за силою. З логарифмом обидва дають 0.69. А модуль потрібен тому, що підозріле і те, і те. Уся ознака тримається на одному числі ззовні — типовій ціні з довідника. І ось де починається різниця.

У зошиті ти сидиш із таблицею за дванадцять місяців і міряєш якість помісячно. Довідник ти щоразу будуєш із того самого місяця, який зараз оцінюєш, — він у тебе під рукою, і це виглядає природним. У бою в мить публікації оголошення сервіс має рівно один довідник: той, що поїхав разом із моделлю в день запуску. Оголошень цього місяця ще просто не існує.

Ось одне оголошення з десятого місяця: Beta 12, 2021 рік, ціна 2 790 грн, чесне.

як рахували в зошиті : типова 3 325 грн → відхилення 0.175 як доступно в бою : типова 4 990 грн → відхилення 0.581

Обидва числа рахуються тією самою формулою, тож перевіримо їх руками.

крок 1 · довідник із того самого місяця

Медіана групи «Beta 12, 2021» у десятому місяці — 3 325 грн.

2 790 ÷ 3 325 = 0.839 → ln(0.839) = −0.175 → модуль 0.175
крок 2 · довідник дня запуску

Та сама група, але медіана на десять місяців старша — 4 990 грн. Тоді ці телефони справді стільки коштували.

2 790 ÷ 4 990 = 0.559 → ln(0.559) = −0.581 → модуль 0.581

Одне оголошення, одна формула, один рядок коду — і число відрізняється в 3.3 раза. Для моделі 0.175 означає «звичайна ціна», а 0.581 — «підозріло дешево». Той самий чесний продавець у зошиті проходить, а в бою потрапляє в чергу до модератора.

Та сама ознака, порахована двома способами

Перемкни спосіб. Угорі — медіанне відхилення по місяцях, унизу — F1. Числа праворуч — за весь рік, усі 18 000 оголошень.

довідник типових цін
F1 за рік0.725
розрив—
відхилення, міс. 120.171
типова Beta 12 ʼ213 325 ₴
Перші місяці обидва способи дають майже те саме — і саме тому skew так довго не помічають. Розходитись вони починають тоді, коли ринок відʼїхав від дня запуску: у зошиті довідник їде разом із ринком, у бою — стоїть.

За рік зошит обіцяє F1 0.725, а система дає 0.614. Розрив 0.111 — і це не помилка вимірювання й не невдача моделі, а різниця між двома способами порахувати одне число.

Одну річ на цій фігурі легко прочитати неправильно. Падають обидві криві: навіть зі свіжим довідником F1 зʼїжджає з 0.889 у четвертому місяці до 0.434 у дванадцятому. Skew — це не падіння, а відстань між кривими. Падають вони з іншої причини, і саме про неї наступний розділ.

Найцікавіше — третій варіант. Довідник можна будувати з минулого місяця: ці оголошення в бою вже існують, правильні відповіді для них не потрібні, тож заборони немає жодної. Так система дає 0.730. Тобто зошит не брехав про досяжну якість — він брехав про ту конкретну систему, яку ми запустили.

Порівняй три числа між собою. Чесний, але недосяжний замір — 0.725. Досяжний і чесний — 0.730: різниця 0.005, тобто ніяка. А запущена система — 0.614, на 0.116 нижче за досяжне. Отже втрата взялась не з того, що ознака погана, і не з того, що свіжий довідник чимось кращий, а з того, що сервіс отримав інший довідник, ніж той, на якому міряли.

Звідси три речі, які це лікують, у порядку важливості. Перша — одна функція на обидва боки, і про неї нижче окремо. Друга — міряти так, як рахуватиме сервіс: якщо в бою довідник буде місячної давності, то й у зошиті будуй його з минулого місяця. Тоді число у звіті стає обіцянкою про систему, а не про формулу. Третя — записувати в журнал значення ознак у мить прогнозу: без цього skew неможливо ні знайти, ні довести, бо в бою не лишиться слідів того, що модель насправді бачила (про журнал — у розділі 6).

Єдиний надійний запобіжник — один шматок коду. Ознаку має рахувати одна функція, і зошит із сервісом мають викликати саме її. Щойно та сама формула живе у двох місцях, вони починають розходитись — не сьогодні, то через три місяці, коли хтось поправить одну з них і забуде про другу. Домовленості «ми пильнуватимемо» тут не працюють: skew не видно ні в тестах, ні в метриці, ні в код-ревʼю.

ДрейфТри різні дрейфи

Спершу розведімо дрейф зі skew, бо на нашій дошці вони зрослись: довідник стоїть, ринок їде — і розрив між зошитом і боєм із місяцями росте, наче він теж дрейф. Розводить їх одне питання: чи потрібен для цього час? Skew видно на одному оголошенні в один день: береш рядок, рахуєш ознаку двома шляхами й порівнюєш два числа, як ми щойно зробили. Дрейф на одному рядку не видно взагалі — він існує лише як різниця між двома моментами в часі. Skew — це вада системи, вона є в день запуску; дрейф — це властивість світу, він приходить сам.

Тепер про сам дрейф. Це слово в розмовах означає щонайменше три різні речі, і від того, яка саме трапилась, залежить, що робити. Розберемо їх на нашій дошці.

Дрейф даних (data drift) — поїхав розподіл ознак, а звʼязок із відповіддю лишився. Телефони дешевшають: за рік медіанна ціна на дошці впала з 3 980 до 2 250 грн, тобто на 43 %. Правило «ціна набагато нижча за типову — підозріло» при цьому не змінилось ні на йоту. Зламався не звʼязок, а довідник, яким ми рахуємо «типову».

Дрейф концепції (concept drift) — розподіл ознак може лишитись тим самим, але змінилось те, що ознака означає. Шахраї, яких почали ловити за дешевою приманкою, переходять на ціну біля типової: телефон коштує рівно скільки треба, а грошей покупець усе одно не побачить. Ознака та сама, значення те саме, відповідь інша. Цей дрейф найгірший, бо жоден нагляд за даними не зобовʼязаний його побачити.

Дрейф міток (label drift) — змінилась частка самих відповідей. Наприклад, шахраїв стало вдвічі більше через свято чи через дірку в реєстрації. Це найпростіший випадок: він видно одним числом, щойно зʼявляються мітки.

Три дрейфи й що саме в них рухається

Однакове слово, три різні поломки. Ліворуч рухається розподіл, посередині — межа рішення, праворуч — частка класів.

У нашій симуляції сталися перші два. Частка шахрайських оголошень трималась між 15.3 % і 17.6 % цілий рік — дрейфу міток не було зовсім. Стовпчики праворуч показують, як виглядав би цей дрейф, а не наш ряд. Модель усе одно розсипалась, і саме тому «частка шахраїв не змінилась» ніколи не є доказом, що все гаразд.

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

ЗатримкаКоли ти дізнаєшся

Тепер зберемо все разом. Ми взяли модель дня запуску, заморозили її разом із довідником — так, як це реально роблять — і прогнали через дванадцять місяців дошки, на якій ринок дешевшає, а шахраї з пʼятого місяця починають міняти схему.

Проблема в тому, що метрику якості в бою не видно. Щоб порахувати F1, потрібні правильні відповіді, а вони приходять тоді, коли шахрая викрили. Візьмімо затримку два місяці — це оптимістично. Отже, у сьомому місяці ти тримаєш у руках якість пʼятого.

А от розподіл ознак доступний одразу: щоб подивитись, як виглядає price_deviation у сьогоднішніх оголошеннях, мітки не потрібні взагалі. Найпоширеніша міра такого зсуву — PSI (population stability index, індекс стабільності популяції). Робить вона просту річ.

Візьми стару вибірку — ту, на якій навчались, — і поділи її на десять кошиків так, щоб у кожен потрапило рівно 10 % значень. Тепер розкидай по тих самих кошиках нові значення. Якщо нічого не змінилось, у кожному знову буде близько 10 %. Якщо розподіл поїхав, десь стане густо, десь порожньо. PSI підсумовує цю різницю по всіх кошиках:

PSI = Σ (ni − si) × ln( ni ÷ si )

Розберемо кожен знак. Σ (грецька «сигма») означає «склади все, що написано далі, по всіх десяти кошиках». si — частка старих значень у кошику номер i; за побудовою вона дорівнює 0.1. ni — частка нових. Різниця n − s каже, наскільки кошик змінився; ln — натуральний логарифм, він відповідає на питання «у скільки разів», а не «на скільки»: ln(1) = 0, ln(1.2) ≈ 0.18, ln(0.8) ≈ −0.22.

Порахуймо руками на двох кошиках замість десяти. Було порівну: 50 % і 50 %. Стало 60 % і 40 %.

крок 1 · перший кошик

Частка виросла з 0.50 до 0.60. Різниця +0.10, відношення 0.60 ÷ 0.50 = 1.2.

0.10 × ln(1.2) = 0.10 × 0.182 = 0.0182
крок 2 · другий кошик

Частка впала з 0.50 до 0.40. Різниця −0.10, відношення 0.40 ÷ 0.50 = 0.8. Логарифм числа, меншого за одиницю, відʼємний — і мінус на мінус дає плюс.

(−0.10) × ln(0.8) = (−0.10) × (−0.223) = 0.0223
крок 3 · складаємо

PSI = 0.0182 + 0.0223 = 0.041. Обидва доданки додатні — так і має бути: PSI не буває відʼємним, бо будь-яка зміна є зміною.

крок 4 · для порівняння

Якщо замість 60/40 стало 80/20, той самий рахунок дає 0.30 × ln(1.6) + (−0.30) × ln(0.4) = 0.141 + 0.275 = 0.416. Кошик поїхав утричі далі (0.30 замість 0.10), а PSI виріс уже вдесятеро: 0.416 проти 0.041. Так і має бути — PSI росте приблизно як квадрат зсуву, тож він навмисне мовчить на дрібних коливаннях і різко кричить на справжніх.

Усталені пороги: 0.1 — варто подивитись, 0.25 — зсув значний. Вони не закон природи, а звичка галузі; для конкретної системи їх підбирають на історії.

Поруч із PSI майже завжди рахують KS-тест (критерій Колмогорова–Смирнова). Він міряє те саме зміщення, але інакше: не по кошиках, а по накопичених частках.

KS = max | Fнов(x) − Fстар(x) |

Розберемо знаки. F(x) — накопичена частка: яка доля значень не перевищує x. Наприклад, якщо половина оголошень має відхилення до 0.2, то F(0.2) = 0.5. Fстар рахується на старій вибірці, Fнов — на новій, а max означає «пройди всю шкалу й візьми найбільшу різницю». Словами: знайди точку, у якій два розподіли розходяться найсильніше, і назви величину цього розходження.

Порахуймо на тих самих двох кошиках. Було 50 % і 50 %, тож накопичені частки старого розподілу — 0.50 на межі кошиків і 1.00 у кінці. Стало 60 % і 40 %: 0.60 і 1.00. Різниці — 0.10 і 0.00, найбільша з них 0.10. Це і є KS. Для 80/20 так само вийде 0.30. KS завжди між 0 і 1, і читається прямо: 0.30 означає, що в найгіршій точці шкали розподіли розходяться на 30 % вибірки.

Дві міри йдуть поруч, але поводяться по-різному, і це видно на нашому році: у пʼятому місяці PSI 0.108 і KS 0.113 — майже те саме, а в дванадцятому PSI 1.668 проти KS 0.495. Причина в тому, що KS упирається в одиницю, а PSI верхньої межі не має й на сильних зсувах росте набагато швидше. Тому пороги для них різні, і «0.1 і 0.25» — це пороги PSI, не KS.

А тепер — центральна картинка теми. Веди повзунок від першого місяця до дванадцятого.

Розрив між «зіпсувалась» і «дізнався»

Угорі — розподіл ознаки: сірий контур це день запуску, заливка це поточний місяць. Нижче — правда про якість і те, що ти реально бачиш. Внизу — PSI, який мітки не потребує.

PSI0.019
KS0.037
F1 насправді0.885
F1 на екрані—
Поломку й звістку про неї розділяють два місяці. 1 500 оголошень на місяць, 18 000 за рік. Мітки приходять із затримкою два місяці, тому в місяці m ти бачиш якість місяця m−2. Пунктир на панелі F1 — домовлена межа 0.80, на панелі PSI — пороги 0.10 і 0.25.

Ось що показує цей повзунок, якщо провести його цілком.

Модель фактично зіпсувалась у пʼятому місяці: F1 упав до 0.777, нижче домовлених 0.80. У той самий пʼятий місяць PSI вперше перетнув 0.1 — і це видно було того ж дня, без жодної мітки. А метрика якості за пʼятий місяць доїхала до тебе аж у сьомому. Два місяці детектор працював гірше, ніж обіцяв, а у звіті світилось зелене: у пʼятому місяці на екрані стояв F1 за третій — 0.879.

Цей розрив і є суттю теми. Не «модель погана» і не «дрейф існує», а те, що між поломкою і її виявленням лежить рівно стільки часу, скільки їде розмітка. Скоротити його можна двома способами: швидше отримувати мітки (кнопка «поскаржитись» на оголошенні дає розмітку за години, а не за місяці) або дивитись на те, що видно без міток.

Чого PSI і KS не міряють. Обидві дивляться тільки на вхід — на те, які числа приходять моделі. Ні про відповіді моделі, ні про правильні відповіді вони не знають нічого. Звідси три межі, про які треба памʼятати: Тому зсув розподілу — це привід піти й подивитись, а не вирок. Доказом лишається метрика на свіжій розмітці, яка приїде пізніше.

НаглядЩо моніторити

Найпоширеніша помилка — моніторити тільки якість. Її, як ми щойно бачили, часто не видно місяцями. Робочий набір сигналів вибудовується за іншим принципом: від того, що приходить швидко, до того, що приходить пізно. Метрика якості — остання в цьому списку, а не перша.

Що міряти, як часто й що робити при відхиленні

Порядок рядків — це порядок, у якому сигнали доходять до тебе. Верхні приходять за хвилини, нижні — за місяці.

що міряємояк частомежащо робити при відхиленні
Помилки сервісущохвилинипонад 0.1 % запитівбудити чергового: це не модель, це сервіс
Затримка відповідіщохвилинипонад 100 мс у 95 % запитівшукати повільний крок конвеєра, не міняти модель
Пропуски й типи в колонкахщогодини+5 пунктів до звичногоперевірити джерело: майже завжди зламався постачальник даних
Частка оголошень, які модель позначила тривогоющодня±20 % від стартових 16 %шукати причину; у нас за рік стало 33 % — удвічі більше, хоча самих шахраїв не побільшало
Розподіли ознак (PSI, KS)щотижняPSI 0.10 — дивитись, 0.25 — тривогазвірити довідники, порівняти з попереднім тижнем
Скарги від людейщоднязростання два тижні поспільчитати самі скарги: це найдешевша нова розмітка
Метрика якості на свіжій розмітціщомісяця, із затримкоюF1 нижче 0.80перенавчати, а відкочувати лише якщо перед падінням міняли модель (розділ 10)
Останній рядок приходить останнім. Коли він спрацював, поломці вже стільки місяців, скільки їде розмітка. Усі рядки вище — це спроба дізнатись раніше, і жоден із них не дає впевненості; вони дають привід піти й подивитись.

Дві поради з практики. По-перше, записуй те, з чим модель насправді працювала: не лише відповідь, а й значення ознак у мить прогнозу. Без цього skew із розділу 3 неможливо ні знайти, ні довести. По-друге, сигнал без порога — це не моніторинг: графік, на який ніхто не дивиться, дає лише відчуття контролю.

ТермінСкільки живе модель

Чесна відповідь: залежно від того, як швидко змінюється предметна область, і дізнатись це можна тільки заміром. Прогноз погоди старіє за години, розпізнавання рукописних цифр — за роки. Наш детектор шахрайства зістарився за пʼять місяців, бо в нього є противник, який навмисне змінює поведінку.

Ліки від старіння одні: навчити модель наново на свіжих даних. Далі це зветься перенавчанням — і не плутай його з перенавчанням у сенсі теми 17, де тим самим словом називають модель, яка завчила навчальну вибірку. Тут ідеться про повторне навчання: ті самі кроки, що й у день запуску, тільки на новішій таблиці.

Перенавчати можна двома способами, і вибір між ними — це вибір, хто ухвалює рішення.

За розкладом. Щомісяця, щотижня, щоночі — байдуже коли, головне завжди. Плюс у передбачуваності: команда знає, коли це станеться, і встигає підготуватись. Мінус у тому, що розклад не знає про світ: він перенавчить модель тоді, коли нічого не змінилось, і не перенавчить у тиждень, коли зламалось усе.

За тригером. Перенавчаємо, коли спрацював сигнал: PSI перейшов поріг, частка тривог виїхала за коридор, якість на свіжій розмітці впала. Плюс — реакція на подію, а не на календар. Мінус — тригер треба налаштувати, і хибне спрацювання коштує викату нової моделі, якої ніхто не просив.

«Налаштувати» тут означає одне число, і воно вирішує все. Візьми PSI нашого року з Інтерактиву 3: 0.019, 0.053, 0.041, 0.073, 0.108, 0.182, 0.344 — далі тільки більше. Поріг 0.25 уперше спрацював би аж у сьомому місяці, тобто рівно тоді, коли й так приїхала метрика за пʼятий: виграшу нуль, тригер спізнився разом із розміткою. Поріг 0.10 спрацював би в пʼятому — за два місяці до метрики, у той самий місяць, коли якість насправді провалилась. І за перші чотири місяці він не дав би жодної хибної тривоги: PSI там не піднімався вище 0.073.

Це і є ціна питання. Високий поріг перетворює тригер на дорогий спосіб дізнатись те саме й тоді ж; низький дає ті самі два місяці фори, але на спокійнішому році перетнувся б і без поломки, і команда викочувала б моделі, яких ніхто не просив. Тому поріг беруть не з таблиці, а зі своєї історії: такий, щоб на минулих даних він спрацьовував раніше за метрику й мовчав у місяці, коли все було гаразд. А поки такої історії немає — розклад чесніший за тригер, бо в нього немає числа, яке можна вгадати неправильно.

Порівняймо обидві дисципліни на нашій дошці. «Запустили й забули» — це модель дня запуску, яка працює весь рік без жодної зміни: її помісячна якість намальована на Інтерактиві 3. «Перенавчаємо щомісяця» — це щоразу нова модель, навчена на трьох останніх розмічених місяцях. Слово «розмічених» тут головне: мітки їдуть два місяці, тож на початку дванадцятого місяця остання повна розмітка — за десятий, і вчитись доводиться на місяцях 8-10.

середній F1 за місяці 5-12 запустили й забули 0.517 перенавчаємо щомісяця 0.647

Перше число можна перевірити просто на Інтерактиві 3: це середнє восьми помісячних значень із пʼятого місяця до дванадцятого — 0.777, 0.700, 0.608, 0.528, 0.463, 0.432, 0.297 і 0.334. Друге — те саме середнє, тільки щомісяця з новою моделлю.

Перенавчання працює: різниця між «детектор гальмує» і «детектор здався». Але подивись уважно на друге число. 0.647 — це теж далеко не 0.843 із дня запуску, і до дванадцятого місяця навіть свіжа модель дає близько 0.5. Причина не в алгоритмі: модель, навчена на місяцях 8-10, ловить шахраїв, які працювали у місяцях 8-10, а в дванадцятому схема вже інша. Поки мітки їдуть два місяці, модель приречена бігти позаду на два місяці.

Чому навіть свіжа модель відстає на два місяці

Початок дванадцятого місяця роботи. Зеленим — усе, на чому модель, яка працює просто зараз, узагалі могла навчитись.

Свіжа модель — це модель позаминулого місяця. Розмітка їде два місяці, тож найновіше, що можна показати моделі на початку дванадцятого місяця, — десятий. Ловити ж їй доводиться схему дванадцятого. Скоротити це відставання перенавчанням не можна взагалі: воно тримається не на розкладі навчання, а на швидкості розмітки.
Звідси несподіваний висновок про пріоритети. Якщо якість обмежена затримкою розмітки, то наступну годину вигідніше вкласти не в модель, а в кнопку «поскаржитись» і в процес, який швидко перетворює скаргу на мітку. Це та сама думка, що і в темі 30, де одна ознака дала більше, ніж уся сітка гіперпараметрів: найбільший приріст майже ніколи не лежить там, де найцікавіше його шукати.

ВерсіїВерсія всього

Питання, яке рано чи пізно ставлять усім: «Учора система пропустила ось це оголошення. Чому?» Відповісти на нього можна лише тоді, коли ти здатен відтворити вчорашній стан. А для цього версію мають мати три речі, а не одна.

Модель. Не «остання», а конкретний файл із конкретним номером, який лежить незмінним. Разом із ним — усе, що модель тягне за собою: довідник типових цін, список категорій, межі масштабування. Це один артефакт, а не файл із вагами плюс кілька змінних у чиємусь коді.

Дані. Не «таблиця оголошень», а зріз, на якому ця модель навчалась: які рядки, за який період, із якою розміткою. Таблиця в базі змінюється щодня; без зафіксованого зрізу навчання не відтворити навіть за тиждень.

Код. Той самий комміт, що готував ознаки й навчав. Разом із версіями бібліотек: результат OneHotEncoder чи порядок колонок можуть відрізнятись між версіями, і модель, навчена на одній, тихо помиляється на іншій.

Мінімальна форма, яка вже рятує: кожен прогноз у журналі має нести номер версії моделі. Одне поле — і питання «що змінилось між вівторком і середою» перетворюється з розслідування на запит.

Інструментів для цього багато — трекери експериментів, реєстри моделей, планувальники конвеєрів, сховища ознак. Називати конкретні тут немає сенсу: вони змінюються швидше за курс. Принцип же не змінюється: усе, що впливає на результат, має версію, а результат має посилання на неї.

ВикатЯк випускати нову

Нова модель краща за стару на крос-валідації. Це ще не привід міняти її в бою одним рухом: «краще на історії» і «краще на живих людях» — різні твердження, і друге перевіряють окремо.

Чотири щаблі викату

Стара модель лишається в бою, поки нова не довела своє. Червона стрілка знизу працює з будь-якого щабля.

Кожен щабель відповідає на своє питання. Тінь — «чи вона взагалі працює на живих даних»; канарка — «чи не ламає вона щось, чого ми не передбачили»; A/B — «чи справді вона краща». Пропустити щабель можна, але тоді на його питання відповідатимуть користувачі.

Тіньовий запуск (shadow mode). Нова модель отримує ті самі запити, що й стара, рахує відповідь і записує її в журнал — але рішення ухвалює стара. Ризику для людей нуль. Що це дає: ти бачиш реальну затримку, реальні збої на реальних даних і відсоток розбіжностей із чинною моделлю. Якщо нова модель позначає втричі більше оголошень, ти дізнаєшся про це до того, як черга модератора вибухне, а не після.

Канарковий викат (canary). Нова модель ухвалює рішення, але для маленької частки — скажімо, 5 % оголошень. Мета не поміряти якість (на 5 % за день її й не поміряєш), а спіймати грубу поломку на малій крові.

A/B. Дві моделі працюють паралельно на порівнянних частках трафіку, і ми міряємо не тільки метрику моделі, а й те, що важить для дошки: скільки скарг, скільки часу витратили модератори. У статистику тут не занурюємось; достатньо трьох правил. Ділити випадково й на рівні того, кого це стосується (у нас — оголошення). Дивитись результат один раз наприкінці домовленого періоду: щоденне підглядання рано чи пізно покаже «перемогу» на чистому шумі. Період — не коротший за тиждень, бо понеділок не схожий на суботу.

ВідкатПлан відкату

Найпрактичніша річ у всій темі, і вона вміщається в одне речення: план відкату має існувати до запуску, а не після поломки.

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

Мінімальний план — чотири пункти, і кожен має бути записаний до викату:

Тепер межа, яку варто провести до того, як припече: відкат лікує поганий викат, а не дрейф. Якщо якість упала одразу після того, як у бій пішла нова модель, — відкочуйся не роздумуючи: стара щойно працювала, і працюватиме знову. Якщо ж якість повзе вниз місяцями, а моделі ніхто не міняв, відкочуватись просто нікуди: попередня версія ще старша, тобто ще далі від сьогоднішнього світу. Тоді ліки інші — новий довідник, свіжа розмітка, перенавчання з розділу 7. Питання, яке розводить ці два випадки, одне: що змінилось останнім — наша система чи світ?

І одна порада, яку зазвичай ігнорують: відкат треба спробувати до того, як він знадобиться. Процедура, записана в документі й ніколи не виконана, — це не процедура, а намір.

РеальністьЩо ламається насправді

Уся ця тема була про модель. Тепер чесна поправка: у продакшні ламається переважно не модель, а конвеєр навколо неї. Сама модель якраз стабільна — та сама матриця ваг щодня дає ті самі числа. Псується те, що подає їй дані.

Типовий список, і в ньому немає нічого про машинне навчання:

Спільне в усіх шести: жодна не викидає помилку. Сервіс відповідає, графіки малюються, метрика мовчить, бо міток немає. Саме тому в таблиці моніторингу перші три рядки — не про модель, а про дані: частка пропусків у колонці й типи значень ловлять таку поломку за години, тоді як якість покаже її за місяці.

Практичний мінімум — перевірка схеми на вході. Перед тим як віддати рядок моделі, перевір: усі очікувані колонки на місці, типи ті самі, значення в межах, які бачила навчальна вибірка. Якщо ні — краще відмовитись відповідати й записати це в журнал, ніж відповісти впевнено й неправильно. Мовчазна помилка коштує дорожче за гучну.

ДаліЩо далі

На цьому таблична частина курсу закінчується. Озирнись: ти почав із питання «що таке машинне навчання», пройшов сім моделей і десяток метрик, зібрав наскрізний проєкт — а закінчуєш темою, у якій найважливіше число не F1, а кількість місяців між поломкою й виявленням.

Звідси варто забрати одну звичку. Число на тесті — це не властивість моделі, а обіцянка, дійсна доти, доки світ схожий на ту таблицю. Тому питання «яка в неї точність» неповне; повне звучить так: «яка в неї точність, як довго вона такою лишиться і як я дізнаюсь, що вже ні».

Далі — нейронні мережі. Вони починаються там, де таблиця закінчується: текст, звук, зображення. Усе, що ти щойно прочитав, там не скасовується, а дорожчає. Модель більша, навчання довше, відкат складніший, а ознаки модель робить собі сама — і це означає, що подивитись на них очима, як ми дивились на price_deviation, стане важче. Дисципліна з цієї теми там потрібніша, а не менш потрібна.