ML-пайплайни
ML-пайплайн — це автоматизований шлях від сирих даних до моделі, яку можна розгорнути. Натренована модель — це артефакт збірки, а не код. Вона постає з чотирьох входів — даних, тренувального коду, конфігурації та середовища, — і лише один із них лежить у вашому репозиторії. Саме тому клонування репозиторію й запуск скрипта дає вам іншу модель. Пайплайн існує, щоб усі чотири були записані, повторювані й доступні для перевірки. На цій сторінці — стадії та чому вони окремі, чому дані ламаються тихо, а код гучно, що таке skew, який робить чудову офлайн-модель марною онлайн, і коли ноутбука справді достатньо.
- Модель — це
- Артефакт, а не код
- Входів фіксувати
- Чотири, а не один
- Ламається тихо
- Завжди дані
Модель — це артефакт збірки, а не код
Під інженерією програмного забезпечення лежить зручне припущення: репозиторій — це істина, і кожен, хто його викачає й зібʼє, отримає ту саму програму. Машинне навчання це припущення тихо ламає. У вашому репозиторії лежить тренувальний код, але модель постала з цього коду плюс саме тих рядків даних, які він побачив, плюс гіперпараметрів, які хтось передав у командному рядку, плюс версій бібліотек і драйверів на машині, що це запустила. Змініть будь-що з цих чотирьох — і отримаєте іншу модель: зазвичай трохи іншу, іноді дуже.
Це не теоретичне занепокоєння, і воно приходить як цілком конкретний поганий день. Модель у проді починає поводитися дивно. Ви хочете порівняти її з попередньою, тож шукаєте, як зібрали попередню, — і знаходите ноутбук, коміт чотиримісячної давності й таблицю, у яку відтоді писали щодня. Перезібрати стару модель неможливо, тож і сказати, що змінилося, неможливо. Саме цей день і відвертає пайплайн, і це дешевша причина його побудувати, ніж будь-який аргумент про автоматизацію.
Стадії — і чому вони окремі
Пайплайн — це орієнтований граф кроків, де кожен крок читає названі входи й пише названі виходи. Розділення тут не заради охайності. Воно дозволяє кешувати крок замість перераховувати, дозволяє збою назвати себе й дозволяє порівнювати два запуски крок за кроком. Шість стадій покривають майже кожен тренувальний пайплайн у проді, і кожна існує тому, що щось ламається, коли її зливають із сусідньою.
1 · Забір
Забрати сирі дані й заморозити їх як адресований зріз — партиція, версія таблиці, маніфест хешів файлів. «Учорашній запит» — це не вхід; ідентифікатор зрізу — вхід.
2 · Валідація
Перевірити зріз проти очікуваної схеми й очікуваних розподілів, перш ніж на нього щось витрачати. Цю стадію пропускають — і саме вона зловила б інцидент.
3 · Ознаки
Перетворити рядки на числа, які споживає модель. Тримається окремо, бо це перетворення має виконуватися ідентично під час сервінгу, — саме розділення це й уможливлює.
4 · Тренування
Дорогий крок — і найменш цікавий. Він читає файл конфігурації, а не прапорці командного рядка, і пише артефакт плюс метадані запуску, що називають його чотири входи.
5 · Оцінка
Оцінити кандидата на відкладеній вибірці І проти моделі, що зараз у проді. Число, з яким нема чого порівняти, ще ніколи не спинило поганого релізу.
6 · Реєстрація
Записати артефакт, його оцінки й походження до реєстру під версією. Реєстрація — це не розгортання, а передача, що робить розгортання окремим і зворотним рішенням.
Дві речі про цей граф варто сказати прямо. Перше: оркестратор — найменш важливий вибір на цій сторінці. Airflow, Kubeflow, Dagster, Metaflow, Prefect або Makefile за вашим наявним CI виражають той самий граф, і жоден із них не полагодить неадресований вхід даних. Друге: кроки мають бути звичайним кодом, що виконується на вашому ноутбуці з малим зрізом. Пайплайн, який можна запустити лише пушем у кластер, — це пайплайн, який ніхто не ітеруватиме, і ціна цього приходить у вигляді повернення людей до ноутбука.
Код ламається гучно, дані — тихо
Коли ламається код, щось кидає виняток і збірка стає червоною. Коли ламаються дані, все успішно. Команда вище перейменувала колонку — і ваше зʼєднання дає порожні значення; поле валюти перейшло з копійок на гривні — і ознака тихо ділиться на сто; зміна в логуванні перестала слати подію — і категорія зникла з тренувальної вибірки. У кожному з цих випадків пайплайн зелений, модель тренується, метрики трохи рухаються, і ніхто не дивиться — бо ніщо не впало.
Ліки — зробити збої даних гучними, тобто записати очікування й дозволити пайплайну відмовлятися. Перевірки, які варто мати, нудні й нечисленні:
- Схема: колонки існують, із тими типами й допустимістю порожніх значень, які ви очікуєте. Дешево — і ловить перейменування.
- Обсяг: кількість рядків у діапазоні недавніх запусків. Зріз на 3% від учорашнього означає, що впала задача вище за течією, а не що попит обвалився.
- Діапазони й категорії: числові поля в межах правдоподібного, категорійні — з відомої множини. Ловить зміну одиниць і нове значення переліку.
- Пропуски: частка порожніх значень на колонку проти попередніх запусків, бо колонка, що тихо стає на 90% порожньою, — класичний тихий злам.
- Дрейф розподілу проти тренувального зрізу — спершу як звіт, а не як заборона: жорсткий барʼєр тут спрацює на «чорну пʼятницю» і буде вимкнений до березня.
- Витік: жодної ознаки, порахованої з інформації, якої немає на момент передбачення. Найдорожча для пізнього виявлення й найважча для автоматизації.
Skew між тренуванням і сервінгом — і навіщо сховище ознак
Ось найпоширеніша причина, чому модель із хорошими офлайн-оцінками погано працює онлайн, — і це взагалі не задача моделювання. Тренувальні ознаки рахуються в одному місці, у пакетній задачі, на Python, по всій таблиці. Сервінгові ознаки рахуються в іншому місці, на кожен запит, часто іншою мовою, по одному рядку. Дві реалізації однієї ідеї розходяться: інше правило округлення, інше значення за замовчуванням для пропуску, вікно, що означає останні 30 днів в одній і останні 30 календарних днів в іншій. Модель тоді бачить під час сервінгу входи, яких ніколи не бачила під час тренування, і деградує так, що жодне перетренування цього не полагодить.
| У чому різниця | Бік тренування | Бік сервінгу |
|---|---|---|
| Форма роботи | Одна пакетна задача на мільйони рядків, хвилини — нормально. | Один рядок на запит, у межах кількох мілісекунд. |
| Момент часу | Має реконструювати, що було відомо тоді, — зʼєднання на момент часу. | Читає те, що відомо зараз, — це простий випадок. |
| Хто це пише | Зазвичай дата-саєнтист, у тренувальному репозиторії. | Зазвичай бекенд-інженер, у сервісі. Ось де тріщина. |
| Як це ламається | Ніяк — офлайн-оцінка лишається чудовою. | Тихо — передбаченнями, гіршими за обіцяне тестовою вибіркою. |
Сховище ознак — це відповідь саме на це й ні на що інше, і це варто сказати, бо продають його як значно більше. Його справжня робота: ознака визначена один раз і подається двома способами — офлайн-сховище, що вміє відповісти «яким було це значення станом на ту мітку часу» для побудови тренувальних вибірок, і онлайн-сховище, що відповідає «яке це значення зараз» за одноцифрові мілісекунди. Купувати продукт необовʼязково; дисципліна — обовʼязкова. Спільна бібліотека, яку викликають обидва боки, з одним тестом, що стверджує однакове значення офлайн і онлайн для тієї самої сутності й часу, дає більшість користі за частку експлуатаційної ваги.
Реєстр — і чому зареєструвати не означає розгорнути
Остання стадія пише до реєстру моделей, і корисно думати про нього як про репозиторій артефактів, що випадково знає про моделі. Він зберігає бінарник під версією разом із метриками, які той набрав, чотирма входами, що його породили, і записом про те, яке середовище зараз запускає яку версію. Саме це останнє поле робить відкат зміною конфігурації, а не задачею перетренування.
Тримати реєстрацію окремо від розгортання — саме та частина, яку команди зливають, а потім шкодують. Пайплайн, що тренує й одразу підвищує до продакшену, не має місця для людського рішення, місця для порівняння кандидата з чинною моделлю й способу відповісти на «поверніть те, що було о девʼятій ранку», інакше ніж натренувати знову — з іншими даними, бо дані вже пішли далі. Розділення коштує одного поля конфігурації й купує дві речі, потрібні саме в найгіршу мить: порівняння і шлях назад.
- Версіонуйте артефакт і ніколи не перезаписуйте.
model-latest.pklу бакеті — це не реєстр, і через нього кожен інцидент лишається без відповіді. - Зберігайте оцінку поруч з артефактом, включно з тим, на якому наборі її отримано. Метрика без свого набору — не метрика.
- Записуйте походження ідентифікаторами, а не текстом: ідентифікатор зрізу, sha коміту, хеш конфігурації, дайджест образу.
- Робіть підвищення явним переходом із назвою, щоб «яка модель зараз жива» було запитом, а не археологією.
Перетренування — це розклад, а не аварія
Моделі псуються, бо світ рухається, а не тому, що ваги гниють. Ціни змінюються, конкурент запускається, приходить новий сегмент клієнтів — і залежність, яку модель вивчила пів року тому, більше не є залежністю в даних. Зріла відповідь — запланований запуск перетренування, результат якого оцінюють проти чинної моделі й підвищують, лише якщо він виграє. Це перетворює деградацію з інциденту на рутину, що здебільшого дає результат «без змін». Незріла відповідь — перетреновувати, коли хтось помітить, що щось не так; тоді пайплайн вправляється рідко й через це падає саме тоді, коли потрібен.
Як часто — це питання бізнесу з технічним тестом. Візьміть модель із проду, натренуйте її на даних, що закінчуються місяць тому, і оцініть обидві на останньому місяці. Якщо свіжа модель виразно виграє, ваші дані рухаються швидко й місяць — це нижня межа; якщо дві нерозрізненні, ви перетреновуєте частіше, ніж це виправдано світом, і платите за це обчисленнями й часом на ревʼю. Зробіть цей вимір один раз — і він закриє суперечку, яка інакше триває рік.
Антипатерни, які варто назвати
- Ноутбук, що став продакшеном: клітинки виконані в порядку, який ніхто не записав, проти таблиці, яка відтоді змінилася.
- Дані, адресовані як «таблиця», а не як зріз: той самий код за задумом дає щодня іншу модель.
- Платформа, збудована до появи другої моделі. Одній моделі не потрібен оркестратор — потрібні скрипт і зафіксоване середовище.
- Код ознак, продубльований між тренуванням і сервінгом, без жодного тесту, що стверджує їхню згоду.
- Кандидат, підвищений тому, що його оцінка побила минулотижневу, — і жодного разу не оцінений проти моделі, яка справді працює.
- Перетренування, запущене самим лише сигналом дрейфу: сезонний тиждень переписує модель, з якою все було гаразд.
Коли це застосовувати
Застосовуйте, коли
- Модель, чиї передбачення доходять до справжніх користувачів, де питання «яка версія це дала і з яких даних» зрештою поставить хтось інший, не ви.
- Дані, що змінюються за розкладом: тренування стає повторюваною задачею, а не одноразовим дослідженням.
- Більше ніж одна людина торкається тренувального коду: незаписаний крок — це крок, який знає лише хтось один.
- Будь-який контекст, де регулятор, аудитор чи клієнт може спитати, як було вироблено рішення щодо нього.
Уникайте, коли
- Разовий аналіз, чий результат — слайд, а не сервіс: зафіксуйте середовище, збережіть ноутбук і йдіть далі.
- Одна модель, яку двічі на рік перетреновує одна людина: оркестратор додає систему для підтримки й нічого не прибирає.
- Продукти цілком на сторонньому API моделі, де немає тренувального запуску, який відтворювати, — дивіться натомість LLMOps.
- Як заміну рішенню про власність над даними: пайплайн над джерелом, яке ніхто не підтримує, автоматизує проблему, а не розвʼязує її.
Було корисно?
Поділіться з тим, хто працює над тією ж задачею.