Машинне навчання · Блок 6 · Тема 29

Проєкт від початку до кінця

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

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

Задача одна на всю тему: детектор шахрайських оголошень на тій самій дошці про вживані телефони, що і в темі 08. 1 200 оголошень, вісім колонок, 16 % шахрайських. Ми пройдемо шлях від сирої таблиці до рішення, чи можна цю модель випускати до людей — з усіма числами й з усіма помилками, які на цьому шляху легко зробити.

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

01 / ПостановкаПостановка перед кодом

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

Чотири питання, на які треба відповісти до першого import.

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

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

Скільки коштує помилка кожного типу. Ось де ця задача перестає бути симетричною.

Таблиця 1 · Ціна двох помилок

Обидві помилки коштують грошей, але платять за них різні люди й у різний спосіб.

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

Яка з цього метрика. Точність (accuracy) тут не годиться взагалі: шахрайських оголошень 16 %, тож константа «всі чесні» дає 84 % і не знаходить нікого (тема 05). Нам потрібні дві величини одночасно — скільки шахраїв упіймали (recall) і яка частка тривог виявилась справжньою (precision). Щоб порівнювати кроки одним числом, беремо F1 — їхнє гармонічне середнє.

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

02 / ПоділПоділ — перша дія

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

Чому саме тут, а не після розвідки? Бо розвідка змінює тебе. Ти побачив, що ціна має довгий хвіст — і вирішив узяти логарифм. Побачив чотири дивні рядки — і вирішив їх прибрати. Кожне таке рішення підігнане під ті дані, які ти бачив. Якщо серед них був і тест, то тест перестав бути незалежним ще до першого навчання (тема 03). Модель нічого не підглядала, зате підглянув ти — і результат той самий.

навч_X, тест_X, навч_y, тест_y = train_test_split( усі_ознаки, таргет, test_size=0.25, random_state=42, stratify=таргет)

stratify тут не прикраса. Шахрайських оголошень 195 із 1 200; без стратифікації їхня частка в тесті гуляє на кілька відсотків просто від жеребкування, і різниця між двома моделями потоне в цьому шумі. З нею частка однакова в обох частинах: 16.2 % і 16.3 %.

Дві речі можна зробити до поділу, бо вони не рахують із даних жодного числа: привести памʼять до одного запису (128 замість 128 ГБ) і прибрати повні дублікати рядків. Дублікати — саме до поділу: якщо той самий рядок потрапить і в навчальну, і в тестову частину, тест перевірятиме модель на тому, що вона вже бачила напамʼять. А от медіани, межі викидів і заповнення пропусків — тільки після (тема 09).

Схема 1 · Порядок дій і три місця, де ховається витік

Вісім кроків проєкту в порядку виконання. Тестова частина замкнена від кроку 2 до кроку 7.

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

03 / РозвідкаРозвідка: три відповіді

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

Перша: де пропуски й чи вони випадкові. Ціни немає у 72 рядках, стану — у 30. І пропуск у ціні аж ніяк не випадковий: серед рядків без ціни шахрайських 54.2 %, серед рядків із ціною — 12.9 %. Отже, ці рядки не викидають: відсутність ціни сама по собі є сигналом, і їй потрібен окремий прапорець (тема 09).

Друга: чи немає колонки, якої в момент прогнозу ще не існує. Це питання ставлять числом. Кореляція кожної колонки з таргетом:

ціна −0.054 вік_акаунта −0.099 рік +0.035 памʼять_гб +0.113 скарг +0.891

Колонка скарг має лякати, а не радувати. Скарги зʼявляються після публікації, коли шахрая вже викрили; у мить, коли модель має відповісти, там нуль у всіх. Це витік, і коштує він дорого: модель зі скарг дає F1 0.974 на крос-валідації і нуль у бою. Колонку викидаємо назавжди — і з навчальної частини, і з тестової.

Третя: що з викидами. Найдорожче оголошення в навчальній частині — 95 000 грн. Це не помилка даних у чистому вигляді: чотири рядки — колекційні Gamma X 2017 року, а ще два — одруки із зайвим нулем. Ми їх поки не чіпаємо й побачимо у розділі 7, у що це вилилось.

04 / ВідлікБазова лінія

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

accuracy = 0.838    F1 = 0.000

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

Тепер уяви звіт «наша модель має точність 94 %». Без другої половини — «а базова лінія дає 84 %» — перша не читається. Правильна форма звіту завжди подвійна (тема 28).

05 / Перша модельПерша чесна модель

Не найкраща — перша. Логістична регресія (тема 17) на вихідних стовпцях, усередині конвеєра, з пʼятиразовою крос-валідацією.

Конвеєр (Pipeline) тут не для краси. Усередині нього закриваються пропуски, масштабуються числа й кодуються категорії — і все це рахується заново на кожному згині крос-валідації, окремо для навчальної частини згину. Якщо винести підготовку назовні, медіана для заповнення пропусків буде порахована по всіх 900 рядках, зокрема й по тих, на яких модель зараз перевіряють. Число підросте, а причини цього в коді буде не видно (тема 09).

F1 = 0.013    базова лінія: 0.000

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

А ми знаємо, чого саме немає. «Ціна 1 310 грн» не означає нічого, поки не сказано, скільки коштує такий самий телефон (тема 10). Будуємо цю ознаку — медіанну ціну всередині групи «модель + рік», а далі модуль логарифма відношення:

відхилення = |ln(ціна ÷ типова ціна)|

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

06 / ІтераціїІтерації, зокрема невдалі

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

Інтерактив 1 · Шлях проєкту з числами

Проведи повзунок від першого кроку до останнього. Один зі стовпчиків нижчий за попередній, а останній — нижчий за передостанній. Обидва провали справжні.

F10.000
зміна F1
precision0.000
recall0.000
Числа з practice.ipynb. Кроки 0-4 — середнє по пʼяти згинах крос-валідації на 900 навчальних оголошеннях. Крок 5 — єдиний замір на 300 відкладених. Пунктир на висоті 0.844 — найкраще, що обіцяла крос-валідація.

Розберемо, що сталось на кожному кроці.

Крок 2, ознака: 0.013 → 0.835. Один стовпчик, зроблений із двох чисел, які вже були в таблиці. Жодних нових даних, жодної складнішої моделі. Це найбільший приріст усього проєкту, і так буває майже завжди.

Крок 3, інша модель: 0.835 → 0.804. Хід був очевидний: випадковий ліс бачить нелінійності й взаємодії, працює зі значеннями за замовчуванням, не боїться масштабу. Мав виграти — і програв. Причина видима, коли її вже знаєш: розділювальна межа тут майже пряма («відхилення більше за поріг — шахрай»), а ламана межа з дерев таку пряму лише наслідує, щоразу трохи промахуючись.

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

Крок 4, налаштування: 0.835 → 0.844. Шість поєднань C і class_weight перебором (тема 24). Приріст є — і він майже в сто разів менший за той, що дала одна ознака. Це нормальна пропорція, і вона підказує, куди вкладати наступну годину.

07 / ПомилкиПомилки очима

Метрика каже, скільки помилок. Вона ніколи не каже, які. Тому після кожного серйозного кроку варто відкрити самі рядки, на яких модель помилилась.

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

Інтерактив 2 · Що спільного в помилок

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

група
оголошень24
без ціни12
мед. відхилення0.62
мед. вік акаунта143
Вертикальна вісь — вік акаунта в днях, шкала логарифмічна, бо вік змінюється від 5 днів до майже десяти років. Горизонтальна — відхилення ціни від типової; нуль означає «ціна рівно типова». Оголошення без ціни винесені в окрему смугу ліворуч, бо для них ця величина не існує.

Обидві групи помилок помітно зміщені в смугу ліворуч від риски. Порахуємо це прямо: помилок усього 45, з них 28 (62 %) — на оголошеннях без ціни. А самих таких оголошень 72 з 900, тобто 8 %. Вісім відсотків даних дають шістдесят два відсотки помилок.

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

Друга знахідка — у хибних тривогах, де ціна таки є. Їх пʼять, і три ми вже бачили: два Gamma X 2017 року за 82 і 88 тисяч (ті самі колекційні з теми 08) і Gamma X Ultra за 76 000 грн — одрук із зайвим нулем, справжня ціна 7 600. Модель робить рівно те, чого ми її навчили: бачить ціну в 12-17 разів вищу за типову й кричить «шахрай».

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

08 / ФіналФінальний замір

Ознаки обрано, модель обрано, гіперпараметри підібрано, поріг визначено. Тепер навчаємо на всіх 900 рядках і міряємо на 300 відкладених. Один раз.

крос-валідація F1 = 0.844 тест F1 = 0.796 precision 0.841 recall 0.755

Тест нижчий на 0.048, і це не збіг і не невдача. Ми обирали ознаку, модель, гіперпараметри й поріг за числом крос-валідації — а те, що обирали максимізуванням, завжди виходить трохи завищеним (тема 24). Шість поєднань у сітці — шість спроб, і найкраще з шести чисел уже містить дрібку везіння. Тест цього відбору не бачив, тому й показує менше.

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

«А якщо тільки подивитись, нічого не змінюючи?» Не буває. Ти подивився й побачив, що модель провалюється на дорогих телефонах. Далі ти або зміниш щось (і тест зіпсовано), або не зміниш — і тоді навіщо дивився. Єдиний спосіб мати незалежний тест — тримати його закритим до самого кінця, а для експериментів мати крос-валідацію.

09 / ЗапускЧи можна це запускати

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

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

Інтерактив 3 · Поріг рішення в наслідках, а не в метриках

Дві криві: скільки шахраїв проходить повз і скільки чесних продавців даремно потурбовано. Проведи повзунок від краю до краю.

пропущено шахраїв24
хибних тривог21
precision0.853
recall0.836
900 навчальних оголошень, 146 шахрайських. Числа пораховані на прогнозах крос-валідації, а не на тесті — поріг теж є рішенням, і обирати його на тесті означає підглянути. У проєкті ми лишили 0.5: він дає найбільший F1 (0.844), а формулювати ціну помилки в гривнях у нас поки нема на чому.

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

оголошення з ціною 272 шт. F1 = 0.900 precision 0.964 оголошення без ціни 28 шт. F1 = 0.606

Отже рішення про запуск не одне, а два. Оголошення з ціною — 91 % потоку — модель вирішує сама: з 28 піднятих тривог 27 виявились справжніми. Оголошення без ціни йдуть у чергу до модератора без моделі; не тому, що модель погана, а тому, що в цих рядках немає з чого робити висновок. Розділити задачу — теж інженерне рішення, і часто краще за спроби витиснути ще 0.01 з метрики.

Далі — чотири питання, які вирішує не той, хто пише код.

Таблиця 2 · Рішення про запуск і на що воно впливає

Жодне з цих рішень не міняє F1. Кожне міняє те, чим модель обернеться для людей.

рішенняваріантина що впливає
Що система робить із тривогоюблокувати · показати попередження · підняти в черзі модератораціну хибної тривоги — від «нічого» до «чесний продавець пішов на іншу дошку»
Хто перевіряє чергу і скільки встигаєодин модератор · команда · автоматично понад порогом 0.95поріг: пропускна здатність людей задає, скільки тривог має сенс піднімати
Як оскаржити рішеннякнопка «це помилка» · лист у підтримку · ніякі довіру, і дані: скарги на хибні тривоги — це нова розмітка
Як міряємо якість після запускущотижневий замір на свіжій розмітці · дивимось на скарги · ніякчи помітимо взагалі, що модель зіпсувалась
Останній рядок найважливіший і найчастіше порожній. Без заміру після запуску модель мовчки псується, і перший, хто про це дізнається, — користувач.

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

10 / ЧеклістЧекліст проєкту

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

Схема 2 · Чекліст наскрізного проєкту

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

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

11 / ДаліЩо далі

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

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

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

Далі в практиці. У practice.ipynb цей проєкт пройдено повністю й кожен крок друкує своє число: поділ, розвідка на train, базова лінія, конвеєр, крос-валідація, чотири ітерації (зокрема невдала), розбір помилок, таблиця порогів і фінальний замір на тесті. Зошит запускається згори вниз і відтворює всі числа цієї лекції.

Далі в темі

Теорію прочитано. Тепер закріпи її на практиці.