PCA — метод головних компонент

Сім ознак не можна намалювати. Дві — можна. Питання лише в тому, які дві обрати — і чи обовʼязково це мають бути ознаки з таблиці.

Блок про дані закінчується там, де таблиця стає завеликою. У темі 09 ми чистили колонки, у темі 10 вигадували нові — і після цього стовпців стало більше, ніж було спочатку. Тепер зворотна дія: стиснути таблицю впоперек. Не викинути рядки, а зменшити кількість стовпців, і питання теми звучить так: скільки насправді чисел потрібно, щоб описати оголошення, якщо в таблиці їх сім?

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

Метод головних компонент (principal component analysis, PCA) робить рівно це: із семи колонок він виготовляє дві нові, намагаючись викинути якнайменше. Не вибирає дві з наявних — саме виготовляє нові, і ця різниця виявиться головною в усій темі.

МотивТри різні «навіщо»

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

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

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

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

Перевірити це можна за хвилину, і краще один раз побачити числа. Розкидаємо тисячу точок навмання по квадрату зі стороною 1, ставимо в цей же квадрат ще одну точку й питаємо дві речі: як далеко до найближчої з тисячі й як далеко до найдальшої. Для двох ознак виходить 0.015 і 1.047 — найдальша приблизно у сімдесят разів далі за найближчу, і слово «найближчий» тут справді щось означає. Повторюємо той самий дослід у десяти вимірах: 0.51 і 1.91, різниця вже лише вчетверо. У ста вимірах: 3.32 і 4.78 — різниця в півтора раза. Найближча точка стала майже такою ж далекою, як найдальша.

Це називають прокляттям розмірності (curse of dimensionality). Причина проста: кожна нова ознака додає до відстані ще один доданок, і чим більше доданків складається, тим менше окрема відмінність здатна змістити загальну суму. Точки розповзаються, і всі опиняються приблизно однаково далеко одна від одної. Страждає від цього не якийсь один метод, а будь-який, що міряє відстані або оцінює окремий параметр на кожен вимір: у високій розмірності йому потрібно в рази більше даних. У цьому списку й пошук найближчих сусідів, і кластеризація, до якої курс дійде в темах 26-27, — обидва тримаються саме на відстанях.

Навіщо зменшувати розмірність і чим це роблять

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

навіщощо це означає на практицічим це роблять
Побачитинамалювати всі ознаки на одній картинці й помітити групи, хвости, дивні точкиPCA — швидко й із формулою, яку можна застосувати до нових даних; t-SNE, UMAP — картинка красивіша, але одноразова
Прискоритисотні або тисячі стовпців перетворити на десятки, щоб навчання йшло хвилини, а не годиниPCA і його родичі; іноді достатньо просто викинути частину стовпців
Прибрати надлишковістьдві колонки говорять те саме — залишити один вимір замість двохPCA стискає їх в одну компоненту; відбір ознак просто викидає другу
Головна різниця в останній колонці. Відбір ознак (feature selection) залишає підмножину тих стовпців, що вже є, і після нього таблицю можна читати. PCA будує нові стовпці як суміші старих — після нього таблиця стає нечитабельною, зате коротшою. Це різні операції, і PCA не є способом «вибрати найкорисніші ознаки».

НадлишокОзнака, яка нічого не додає

Почнемо з найпростішого випадку, у якому зайвий вимір видно неозброєним оком. У нашій таблиці є дві колонки, що відповідають на одне питання «скільки цей телефон приблизно коштує»: типова_ціна — медіана реальних цін на дошці для пари «модель + рік», і оцінка_каталогу — ціна нового апарата з каталогу, помножена на 18 % зношення за кожен рік. Порахуємо їхню кореляцію (тема 08):

кореляція(типова_ціна, оцінка_каталогу) = 0.989

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

Що кореляція робить із формою хмари

Три пари колонок по 240 рядків. Ліворуч звʼязку немає, праворуч колонки майже дублюють одна одну. Зелена пряма — напрямок найбільшого розкиду.

Що сильніший звʼязок, то тонша хмара впоперек. При кореляції 0.03 обидва напрямки майже рівноцінні: перший забирає 56 % розкиду, другий — 44 %. При 0.99 хмара сплющується в лінію, і на другий напрямок лишається 0.6 %. Точки формально мають дві координати, але вздовж другої осі між ними майже немає різниці — саме таку пару колонок PCA і стискає в одну.

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

PCA — це машинка, яка знаходить такі напрямки автоматично й у будь-якій кількості вимірів, де малювати вже неможливо. У нашій семивимірній таблиці остання компонента виявиться саме різницею між цими двома колонками, і на неї припаде 0.15 % усього розкиду. Тобто одна з семи осей — це фактично шум округлення.

ГеометріяНапрямок найбільшого розкиду

Забудьмо на кілька абзаців про формули. PCA — геометрія, і починати треба з неї.

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

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

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

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

Що саме зберігається, а що втрачається

Три точки хмари, одна пряма через центр. Для кожної точки — координата вздовж прямої та перпендикуляр до неї.

Дві частини одного вектора. Вектор від центру хмари до точки розкладається на дві перпендикулярні частини: t — уздовж прямої, її ми зберігаємо, і d — упоперек, її ми викидаємо. За теоремою Піфагора t² + d² дорівнює квадрату довжини самого вектора, і ця довжина від кута прямої не залежить взагалі.
Пряму через хмару можна проводити по-різному. Зверни увагу, що відстань від точки до прямої ми міряємо перпендикуляром — найкоротшим шляхом. Її можна було б міряти й інакше: по вертикалі — тобто наскільки високо точка стоїть над прямою або нижче за неї, якщо рухатись строго вздовж осі y. Це не те саме, і на тій самій хмарі два способи дають дві різні прямі. Вертикальний спосіб має сенс тоді, коли осі нерівноправні: одна колонка щось пояснює, друга нею пояснюється, і ми хочемо вгадувати другу за першою. Саме так міряє помилку тема 12 «Лінійна регресія» — наступна в курсі, — і корисно буде памʼятати, що PCA робить інакше. Тут обидві осі рівні в правах: ніякої «правильної» колонки немає, є тільки хмара, яку треба покласти на пряму з найменшою втратою.

ЕквівалентністьДві задачі, одна відповідь

Ту саму задачу описують двома способами, і в підручниках вони трапляються порівну.

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

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

Ці дві постановки виглядають різними — одна про максимум, друга про мінімум, одна про розкид, друга про помилку. Насправді це буквально одна задача, і причина — теорема Піфагора зі схеми 2. Для кожної точки квадрат відстані до центру дорівнює t² + d². Складемо це по всіх точках. Значок Σ нижче — велика грецька літера «сигма», і читається вона просто як команда «додай по всіх точках»:

Σ t2  +  Σ d2  =  Σ (відстань до центру)2

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

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

Крути пряму й дивись на два числа

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

розкид уздовж0.00
втрачено0.00
сума32.00
збережено0 %
Сума в третій комірці не змінюється ніколи — вона дорівнює 32.00 при будь-якому куті. Максимум першого числа (28.00) і мінімум другого (4.00) припадають на один і той самий кут 26.6°. Криві внизу — ті самі дві величини як функції кута: одна крива є другою, перевернутою догори дриґом.

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

Два слова, які треба знати до розрахунку

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

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

Уся суть — у знаку добутку. Якщо рядок має обидва відхилення додатні (ціна вища за середню і оцінка вища за середню) — добуток додатний. Якщо обидва відʼємні — добуток теж додатний, бо мінус на мінус. А якщо відхилення різного знака (ціна вища за середню, а оцінка нижча) — добуток відʼємний. Тож додатна коваріація означає «колонки переважно відхиляються в один бік», відʼємна — «у протилежні», а близька до нуля — «ніякого звʼязку не видно».

Порахуймо на трьох рядках, щоб побачити, що там нема нічого, крім множення. Нехай ціна відхилилась від своєї середньої на −7, −3 і +5 тисяч, а оцінка каталогу — на −1, −4 і 0 тисяч відповідно:

рядок 1: (−7) × (−1) = +7 обидва нижчі за середнє рядок 2: (−3) × (−4) = +12 обидва нижчі за середнє рядок 3: (+5) × ( 0) = 0 оцінка рівно на середній сума = +19, і вона додатна: колонки ходять разом

Тепер коваріаційна матриця. Коли колонок дві, різних комбінацій усього чотири: перша з першою, перша з другою, друга з першою, друга з другою. Складемо ці чотири числа в квадратик 2×2 — це вона і є. Коваріація колонки самої з собою — це просто її дисперсія (відхилення множиться саме на себе, тобто підноситься до квадрата), тому по діагоналі стоять дисперсії, а поза діагоналлю — коваріації пар. Для семи колонок матриця буде 7×7, за тим самим правилом. Ніякої іншої суті в ній немає: це просто акуратно складена таблиця всіх «розкидів» і всіх «ходять разом».

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

Напрямок, який матриця лише розтягує

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

Десять напрямків із дванадцяти матриця повернула — рожева стрілка не лежить на сірій. Два не повернула: напрямок 26.6°, який вона розтягнула рівно в 28 разів, і перпендикулярний до нього 116.6°, розтягнутий у 4 рази. Це і є два власні вектори з власними числами 28 і 4 — ті самі числа, що вийдуть у наступному розділі з квадратного рівняння. Сума 28 + 4 = 32 — і це весь розкид хмари.

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

ЧислаПорахуймо руками

Візьмімо шість оголошень і дві ознаки: ціну, яку просить продавець, і оцінку каталогу. Обидві в тисячах гривень — тобто в тих самих одиницях, тому масштабувати нічого не треба (у розділі 7 буде видно, чому це важлива обмовка).

Крок 1 · центруємо

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

Крок 2 · коваріаційна матриця

Складаємо ту саму таблицю 2×2, про яку йшлося в розділі 4. По діагоналі — дисперсії кожної ознаки: наскільки сильно вона сама по собі розкидана. Поза діагоналлю — коваріація: наскільки ознаки розкидані разом. Спершу додаємо всі добутки відхилень, а потім ділимо суму на n−1 = 5 — не на 6. Мінус одиниця тут тому, що одне число ми вже витратили: середнє порахували з тих самих шести рядків, і після цього шосте відхилення вже не вільне (сума всіх відхилень мусить дати нуль). Так рахують і numpy, і scikit-learn, тож числа зійдуться з бібліотечними.

Sxx = 116 / 5 = 23.2 (дисперсія ціни) Syy = 44 / 5 = 8.8 (дисперсія оцінки) Sxy = 48 / 5 = 9.6 (коваріація) ⎡ 23.2 9.6 ⎤ C = ⎢ ⎥ ⎣ 9.6 8.8 ⎦

Матриця симетрична: коваріація ціни з оцінкою — те саме число, що й коваріація оцінки з ціною. Сума діагоналі, 23.2 + 8.8 = 32, — це весь розкид хмари, і саме її ми далі розкладемо на дві частини.

Крок 3 · рівняння на власне число

Шукаємо напрямок u = (u1, u2), який матриця лише розтягує, як на схемі 3: після множення стрілка дивиться туди ж, тільки стала в λ разів довшою. «Помножити матрицю на вектор» означає взяти кожен рядок матриці, почленно перемножити його з вектором і додати результати. Для нашої матриці 2×2 виходить два звичайні рівняння:

23.2 · u1 + 9.6 · u2 = λ · u1 9.6 · u1 + 8.8 · u2 = λ · u2

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

(23.2 − λ) · u1 + 9.6 · u2 = 0 9.6 · u1 + (8.8 − λ) · u2 = 0

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

Коли ж два рівняння виду a·u1 + b·u2 = 0 і c·u1 + d·u2 = 0 збігаються? Тоді, коли пара (a, b) пропорційна парі (c, d) — тобто коли a·d = b·c, або a·d − b·c = 0. Цей вираз, «добуток головної діагоналі мінус добуток другої», у лінійній алгебрі зветься визначником (determinant) матриці 2×2. Підставмо наші чотири числа — a = 23.2 − λ, b = 9.6, c = 9.6, d = 8.8 − λ:

(23.2 − λ)(8.8 − λ) − 9.6 · 9.6 = 0 204.16 − 32 λ + λ2 − 92.16 = 0 λ2 − 32 λ + 112 = 0

Вийшло звичайне квадратне рівняння — те саме, що розвʼязують у школі. Його називають характеристичним рівнянням матриці. Для семи ознак матриця була б 7×7, а рівняння — сьомого степеня; руками його ніхто не розвʼязує, це робить numpy.linalg.eigh. Але ідея та сама.

Крок 4 · власні числа

Дискримінант: 32² − 4·112 = 1024 − 448 = 576, а корінь із 576 — рівно 24.

λ1 = (32 + 24) / 2 = 28 λ2 = (32 − 24) / 2 = 4

Перевірка: 28 + 4 = 32 — рівно сума діагоналі C. Це не збіг, а загальне правило: власні числа ділять між собою весь розкид хмари й нічого не додають і не гублять. Перша компонента забирає 28 із 32, тобто 87.5 %, друга — решту 12.5 %.

Крок 5 · власний вектор

Тепер сам напрямок. Власне число ми знаємо, тож підставмо λ1 = 28 у перше з двох рівнянь кроку 3 (друге дасть те саме, бо при цьому λ вони збігаються — рівно на цьому й будувався розрахунок):

(23.2 − 28) · u1 + 9.6 · u2 = 0 −4.8 · u1 + 9.6 · u2 = 0 u1 = 2 · u2

Тобто напрямок (2, 1): на два кроки праворуч — один крок угору. Довжина такого вектора дорівнює √5 ≈ 2.236, а нам потрібен вектор довжини 1, тому ділимо на неї:

u = (2, 1) / √5 = (0.894, 0.447) кут = 26.6°
Крок 6 · проєкції й перевірка

Координата точки вздовж напрямку — це скалярний добуток центрованої точки на u: беремо кожну координату, множимо на відповідне число з u й додаємо. Для першого оголошення це (−7)·0.894 + (−1)·0.447 = −6.71.

А тепер головна перевірка всієї теми: порахуємо дисперсію цих шести проєкцій. Вона має дорівнювати λ1 = 28 — тому що власне число і є збереженим розкидом.

Той самий розрахунок повністю, у числах

Шість оголошень, центрування, проєкція на першу компоненту та відстань до неї.

оголошенняцінаоцінка ціна − 11оцінка − 9 t (уздовж)d (упоперек) t²d²
А48−7−1−6.71+2.24455
Б85−3−4−4.47−2.24205
В98−2−1−2.240.0050
Г1310+2+1+2.240.0050
Д169+50+4.47−2.24205
Е1614+5+5+6.71+2.24455
сума6654000.000.0014020
÷ (n−1)——————28.04.0
Останній рядок — уся суть методу. Дисперсія проєкцій дорівнює 28.0 — це рівно λ₁, порахований у кроці 4 через дискримінант. Середній квадрат перпендикулярів дорівнює 4.0 — це рівно λ₂. Разом 32.0, тобто весь розкид хмари, і жодне з цих трьох чисел ми не підбирали. Знак у колонці d показує лише те, з якого боку прямої лежить точка; у сумі квадратів він ролі не грає.

Прочитай таблицю ще раз і зістав із інтерактивом вище. Постав повзунок на 27 градусів — і в правій колонці інтерактиву, у рядку «сума», зʼявляться ті самі 140.0 і 20.0. На табло під картинкою вони поділені на n−1 = 5, тобто показані як 28.00 і 4.00: там дисперсія, а в таблиці — суми квадратів. Ніякої магії в PCA немає: центрування, три суми, квадратне рівняння й один скалярний добуток. Усе решта — та сама арифметика в більшій кількості вимірів, де замість квадратного рівняння працює numpy.linalg.eigh.

ДисперсіяПояснена дисперсія

У прикладі з шести точок перша компонента забрала 87.5 % розкиду. Ця частка й зветься поясненою дисперсією (explained variance): власне число компоненти, поділене на суму всіх власних чисел. Слово «пояснена» тут не про причини — воно означає тільки «утримана», «не втрачена при стисненні».

Практичне питання завжди одне: скільки компонент лишити. Відповідь дає накопичена частка. Береш перші дві — дивишся, скільки відсотків розкиду вони разом тримають. Береш три — дивишся знову. Зупиняєшся там, де цифра тебе влаштовує; типовий поріг у роботі — 90 або 95 %, але це домовленість, а не закон.

Порахуємо на нашій дошці. Сім стандартизованих ознак, 1 100 оголошень.

Скільки компонент лишити

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

накопичено55.8 %
втрачено44.2 %
внесок цієї15.9 %
відстань1.76
«Відстань» — середня відстань від оголошення до його відновленої копії у стандартизованих одиницях: беремо k координат, збираємо з них назад сім ознак і міряємо, наскільки схибили. При семи компонентах вона рівно нуль — нічого не втрачено, просто повернули систему координат.

Числа виявились не такими, як хотілося б. Перша компонента тримає 39.9 %, друга додає 15.9 % — разом лише 55.8 %. Щоб дійти до 90 %, потрібно пʼять компонент із семи (94.2 %); чотири дають 84.6 %. Наша таблиця стискається погано, і це нормальний результат, а не помилка розрахунку: сім ознак здебільшого справді розповідають про різні речі. Модель телефона, вік акаунта та стан корпусу між собою не повʼязані ніяк.

Єдиний вимір, який справді нічого не важить, — сьомий: 0.15 %. Подивись на його склад — це різниця між типова_ціна й оцінка_каталогу, тими самими двома колонками з кореляцією 0.989. PCA сам, без підказок, знайшов дублікат і склав його в один вимір із мізерною вагою.

Що робити з цим знанням. Якщо мета була «побачити», бери дві компоненти й памʼятай, що дивишся на 55.8 % розкиду — картинка приблизна за побудовою. Якщо мета була «прискорити», пʼять компонент замість семи економлять мало, і чесна відповідь — не стискати взагалі. PCA не зобовʼязаний давати виграш; коли не дає, про це треба сказати, а не витискати з нього результат.

МасштабБез масштабу все брехня

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

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

ціна 8 689 ₴ рік 2.31 типова_ціна 5 077 ₴ памʼять_гб 123.78 оцінка_каталогу 6 150 ₴ стан_бал 0.85 вік_акаунта 407 днів

Ціна розкидана на 8 689 «одиниць», стан — на 0.85, тобто приблизно в десять тисяч разів менше (8 689 ÷ 0.85 ≈ 10 222). А розкид, який максимізує PCA, — це квадрат стандартного відхилення, тож у дисперсіях розрив стає квадратом цього числа: 10 2222 ≈ сто мільйонів разів. Тому напрямок найбільшого розкиду майже напевно збігатиметься з ціною — не тому, що ціна важливіша, а тому, що вона виміряна в дрібніших одиницях. І це не гіпотеза: подивись на список відхилень вище й перепиши ціну не в гривнях, а в тисячах гривень. Її розкид одразу стає 8.689 замість 8 689, найбільшим у таблиці робиться вік акаунта (407, бо він у днях), і перша компонента з «ціни» перетворюється на «вік акаунта». Дані не змінились узагалі — змінився лише підпис одиниці. Метод, який залежить від того, у чому ти вимірював, у такому вигляді користі не має.

Ліки ті самі, що в темі 09: StandardScaler. Кожну колонку зводимо до середнього 0 і стандартного відхилення 1, після чого всі ознаки заходять у розрахунок на рівних. Подивись, що це міняє.

Одна кнопка, дві різні перші компоненти

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

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

У режимі «як є» на три цінові колонки припадає 99.9996 % квадрата довжини вектора. Рік, памʼять, стан і вік акаунта разом набирають 0.0000045 — їх у першій компоненті просто немає. Формально метод відпрацював; змістовно він побудував складну конструкцію, яка означає «ціна».

Правило без винятків для цієї теми. Якщо колонки виміряні в різних одиницях, стандартизація перед PCA обовʼязкова. Виняток один: коли всі ознаки вже в одних одиницях і різниця в їхньому розкиді змістовна (пікселі одного зображення, щомісячні продажі за рік). У таблиці, де поруч гривні, дні та бали, такого не буває.

МежіЧого PCA не вміє

Тепер нарешті намалюймо ту саму картинку, заради якої все й починалось: 1 100 оголошень у площині перших двох компонент.

Дошка в двох компонентах

Ті самі 1 100 оголошень двічі. Ліворуч колір — цінова сімʼя моделі (Alfa дешеві, Beta середні, Gamma дорогі). Праворуч ті самі точки пофарбовано за колонкою «шахрайське», якої PCA не бачив.

Ліворуч структура є, праворуч — немає. Медіана першої компоненти зростає рівно за ціновим класом моделі: Alfa A5 −1.16, Alfa A7 −0.71, Beta 12 −0.38, Beta 12 Pro +0.34, Gamma X +1.04, Gamma X Ultra +1.87. А медіана в шахрайських оголошень −0.38 проти −0.43 у чесних — тобто ніякої різниці.

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

Права картинка — чесний провал, і він повчальніший. Шахрайські оголошення розсипані по всій площині рівно так само, як чесні. Причини дві, і обидві варто памʼятати.

PCA не знає, що ти шукаєш. Він максимізує розкид, а не користь. Найрозкиданіший напрямок у цій таблиці — «дорогий телефон проти дешевого», і саме його метод і повернув. Ознака, за якою відрізняються шахраї, могла б жити в шостій компоненті з внеском 5.7 % — і, викинувши все, крім двох перших, ти б викинув її першою.

Компоненти лінійні. Шахрая на цій дошці видає не ціна, а відношення ціни до типової: 1 200 грн за телефон, що коштує 12 000. Відношення — дія нелінійна, і жодна сума виду a·ціна + b·типова її не відтворить. PCA будує тільки такі суми, тому ця структура для нього невидима в принципі. Дай методу колонку |ln(ціна ÷ типова)| з теми 10 — і все зміниться. Але придумав би цю колонку ти, а не PCA.

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

PC1 = 0.44·ціна + 0.57·типова + 0.57·каталог + 0.39·рік + 0.06·памʼять + 0.02·стан − 0.03·вік

Як це назвати? «Загальна дорожнеча оголошення» — приблизно, з натяжкою. У якій це одиниці? Ні в якій. Що означає, що в оголошення PC1 = 2.3? Нічого, що можна сказати продавцеві. Модель на компонентах може працювати чудово, але пояснити її рішення людині — а це буває вимогою — стає майже неможливо. Поки що порівняй із тим, як пояснив би рішення живий модератор: «ціна втричі нижча за типову, акаунт молодший за тиждень» — дві зрозумілі умови, кожна названа своїм імʼям. Тема 23 «Дерева рішень» дасть модель, яка пояснює себе саме так — ланцюжком простих умов. А тут показувати нічого: сім вихідних колонок змішані в одне число з вагами.

Коли потрібна саме картинка

Якщо мета була лише подивитись на дані, у PCA є сильніші конкуренти — t-SNE та UMAP. Вони нелінійні: намагаються зберегти не загальний розкид, а сусідство, тобто щоб точки, близькі у вихідному просторі, лишились близькими й на картинці. Групи на їхніх малюнках виглядають набагато виразніше.

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

ВитікВитік через PCA

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

Z = StandardScaler().fit_transform(усі_дані) # ← ось тут T = PCA(n_components=5).fit_transform(Z) # ← і тут навч, тест = train_test_split(T, ...)

Логіка здається бездоганною: PCA — метод без учителя, він таргета не бачить, отже відповіді підглянути не може. Логіка хибна. Витік — це не тільки підглядання відповідей, а будь-яке використання тестових рядків при підготовці даних (тема 09). Середні й стандартні відхилення для StandardScaler пораховані по всіх рядках, зокрема тестових. Коваріаційна матриця — так само. Напрямки компонент підігнані під ту хмару, у якій уже лежить тест. Тест перестав бути незнайомими даними ще до того, як зʼявилась перша модель.

Ефект можна поміряти. Візьмемо просте питання: яку частку розкиду тестових рядків утримують дві компоненти?

1 100 оголошень чесно 54.6 % з витоком 56.1 % 40 оголошень чесно 53.4 % з витоком 62.8 %

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

Правильно так. StandardScaler і PCA кладуться в Pipeline разом із моделлю. Тоді на кожному фолді крос-валідації середні, відхилення й компоненти перераховуються заново — і тільки на навчальній частині фолда (тема 09). Тестові рядки лише проходять через уже готове перетворення методом transform, і жодне число з них у нього не потрапляє.

ДаліЩо далі

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

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

І одне спостереження, яке дуже знадобиться далі в курсі. Стиснення до k компонент — це відновлення точки з неповних даних, і для кожного оголошення можна поміряти, наскільки погано воно відновилось. Для більшості ця відстань невелика: точка лежить там, де лежать усі. А от оголошення, яке PCA відновлює з великою похибкою, — це оголошення, не схоже на решту. Саме так із методу головних компонент виходить детектор дивних рядків, і на цьому будується пошук аномалій — тема 28 з блоку «Навчання без учителя». Там ця відстань стане не діагностикою, а самим прогнозом. А наступна тема курсу — 12 «Лінійна регресія»: там пряму крізь хмару теж проводитимуть, але міряти відстань до неї будуть по вертикалі, а не перпендикуляром, і саме через це вийде інша пряма.