Блок навчання без учителя почався з кластеризації: ми групували рядки, не маючи жодної правильної відповіді. Тепер друга задача того самого блоку, і вона дзеркальна — стискати треба не рядки, а стовпці. Замість «які оголошення схожі між собою» питання звучить так: скільки насправді чисел потрібно, щоб описати оголошення, якщо в таблиці їх сім?
Приклад на всю тему — та сама дошка про вживані телефони, що і в темі 08. Беремо 1 100 оголошень із відомою ціною і сім числових ознак: ціна, типова ціна для цієї моделі й року, оцінка з каталогу, рік випуску, памʼять, стан у балах від 1 до 4 і вік акаунта продавця. Сім чисел на оголошення. Побудувати з них одну картинку неможливо — людське око читає два виміри, з натяжкою три.
Метод головних компонент (principal component analysis, PCA) робить рівно це: із семи колонок він виготовляє дві нові, намагаючись викинути якнайменше. Не вибирає дві з наявних — саме виготовляє нові, і ця різниця виявиться головною в усій темі.
01 / МотивТри різні «навіщо»
«Зменшити розмірність» звучить як одне бажання, а насправді їх три, і вони вимагають різних інструментів. Плутанина між ними — джерело половини невдалих спроб застосувати PCA.
Перше: щоб побачити. Найчесніша й найчастіша причина. У тебе сім колонок, ти хочеш подивитись на дані очима — а очі працюють із площиною. Можна малювати всі пари колонок підряд, але для семи ознак це двадцять одна картинка, і жодна з них не показує сім вимірів одночасно. Тут зменшення розмірності потрібне рівно для однієї хвилини перегляду, і моделі воно не стосується взагалі.
Друге: щоб прискорити. Менше стовпців — менше обчислень і менше памʼяті. Для семи ознак це смішно, для семи тисяч (наприклад, зображення, розгорнуте в рядок пікселів) — вирішально. Тут важлива не картинка, а те, що після стиснення модель навчається за хвилини замість годин.
Третє: щоб прибрати шум і надлишковість. Якщо дві колонки говорять майже те саме, друга не додає інформації — лише зайвий вимір, у якому модель має щось оцінювати. А зайві виміри коштують дорого — і найдивніше те, що разом із ними ламається саме поняття «схожий».
Перевірити це можна за хвилину, і краще один раз побачити числа. Розкидаємо тисячу точок навмання по квадрату зі стороною 1, ставимо в цей же квадрат ще одну точку й питаємо дві речі: як далеко до найближчої з тисячі й як далеко до найдальшої. Для двох ознак виходить 0.015 і 1.047 — найдальша приблизно у сімдесят разів далі за найближчу, і слово «найближчий» тут справді щось означає. Повторюємо той самий дослід у десяти вимірах: 0.51 і 1.91, різниця вже лише вчетверо. У ста вимірах: 3.32 і 4.78 — різниця в півтора раза. Найближча точка стала майже такою ж далекою, як найдальша.
Це називають прокляттям розмірності (curse of dimensionality). Причина проста: кожна нова ознака додає до відстані ще один доданок, і чим більше доданків складається, тим менше окрема відмінність здатна змістити загальну суму. Точки розповзаються, і всі опиняються приблизно однаково далеко одна від одної. Страждає від цього не якийсь один метод, а будь-який, що міряє відстані або оцінює окремий параметр на кожен вимір: у високій розмірності йому потрібно в рази більше даних. Кластеризація з двох попередніх тем — теж у цьому списку.
Таблиця 1 · Навіщо зменшувати розмірність і чим це роблять
Три різні мети. Інструмент під кожну свій, і плутати їх дорого.
| навіщо | що це означає на практиці | чим це роблять |
|---|---|---|
| Побачити | намалювати всі ознаки на одній картинці й помітити групи, хвости, дивні точки | PCA — швидко й із формулою, яку можна застосувати до нових даних; t-SNE, UMAP — картинка красивіша, але одноразова |
| Прискорити | сотні або тисячі стовпців перетворити на десятки, щоб навчання йшло хвилини, а не години | PCA і його родичі; іноді достатньо просто викинути частину стовпців |
| Прибрати надлишковість | дві колонки говорять те саме — залишити один вимір замість двох | PCA стискає їх в одну компоненту; відбір ознак просто викидає другу |
02 / НадлишокОзнака, яка нічого не додає
Почнемо з найпростішого випадку, у якому зайвий вимір видно неозброєним оком. У нашій
таблиці є дві колонки, що відповідають на одне питання «скільки цей телефон приблизно
коштує»: типова_ціна — медіана реальних цін на дошці для пари «модель + рік»,
і оцінка_каталогу — ціна нового апарата з каталогу, помножена на 18 %
зношення за кожен рік. Порахуємо їхню кореляцію
(тема 08):
Це майже одиниця. Знаючи одну колонку, ти передбачаєш другу з похибкою в кілька відсотків. Якщо намалювати оголошення в осях «типова ціна — оцінка каталогу», хмара виявиться не хмарою, а майже прямою лінією: точки розкидані вздовж діагоналі й майже не розкидані впоперек.
Тепер найважливіше спостереження всієї теми. Ця хмара живе у двох вимірах лише формально. Насправді, щоб описати положення точки з непоганою точністю, вистачить одного числа — того, наскільки далеко вона зайшла вздовж діагоналі. Другий вимір існує, але в ньому майже нічого не відбувається.
PCA — це машинка, яка знаходить такі напрямки автоматично й у будь-якій кількості вимірів, де малювати вже неможливо. У нашій семивимірній таблиці остання компонента виявиться саме різницею між цими двома колонками, і на неї припаде 0.15 % усього розкиду. Тобто одна з семи осей — це фактично шум округлення.
03 / ГеометріяНапрямок найбільшого розкиду
Забудьмо на кілька абзаців про формули. PCA — геометрія, і починати треба з неї.
Уяви хмару точок на площині. У неї є центр — точка, координати якої дорівнюють середнім по кожній осі. І в неї є форма: у якомусь напрямку хмара витягнута сильніше, ніж у решті. Візьми олівець, приклади до хмари й покрути навколо центру: є положення, у якому олівець «протикає» хмару вздовж, і положення, у якому він лежить упоперек. Напрямок, у якому хмара витягнута найсильніше, і є перша головна компонента. Ніякої іншої означеної суті тут немає — це просто напрямок найбільшої витягнутості.
Далі — друга компонента. Вона обирається за тим самим правилом (найбільший розкид), але з обмеженням: вона мусить бути перпендикулярною до першої. Чому перпендикулярною — питання не естетичне. Якби другий напрямок міг бути будь-яким, він просто зліг би з першим і повторив ту саму інформацію. Перпендикулярність гарантує, що нова компонента розповідає про хмару щось таке, чого перша не сказала.
Для семи ознак усе так само, просто хмара живе в семивимірному просторі: перша компонента — напрямок найбільшого розкиду, друга — найбільшого з того, що лишилось після першої, і так далі, аж поки напрямків не набереться стільки, скільки було ознак.
Тепер про те, що означає «стиснути». Візьмімо одну точку й одну пряму, що проходить через центр хмари. Опустимо з точки перпендикуляр на пряму — отримаємо проєкцію. Проєкція описується одним числом: наскільки далеко вздовж прямої вона стоїть від центру. Це число ми залишаємо. Довжина самого перпендикуляра — це те, що ми втратили: після стиснення точку вже не відрізнити від будь-якої іншої з таким самим положенням уздовж прямої.
Схема 1 · Що саме зберігається, а що втрачається
Три точки хмари, одна пряма через центр. Для кожної точки — координата вздовж прямої та перпендикуляр до неї.
04 / ЕквівалентністьДві задачі, одна відповідь
Ту саму задачу описують двома способами, і в підручниках вони трапляються порівну.
Постановка перша, «зберегти». Знайти напрямок, проєкції на який розкидані найсильніше. Логіка: якщо після стиснення точки злипнуться в одну купу, ми втратили все, що їх розрізняло; отже, шукаємо напрямок, де вони розрізняються найбільше.
Постановка друга, «не втратити». Знайти пряму, до якої точки в сумі лежать найближче — тобто мінімізувати суму квадратів перпендикулярів. Логіка: стиснення замінює кожну точку її проєкцією, тож помилка стиснення — це і є довжина перпендикуляра.
Ці дві постановки виглядають різними — одна про максимум, друга про мінімум, одна про розкид, друга про помилку. Насправді це буквально одна задача, і причина — теорема Піфагора зі схеми вище. Для кожної точки квадрат відстані до центру дорівнює t² + d². Складемо це по всіх точках:
Права частина не містить кута прямої. Скільки не крути олівець, сума квадратів відстаней від точок до центру хмари не змінюється — це властивість самої хмари. Отже, ліва частина — стала. А якщо сума двох доданків стала, то збільшити перший можна рівно на стільки, на скільки зменшиться другий.
Максимум збереженого розкиду й мінімум втраченого — це та сама точка на шкалі кутів. Не «приблизно те саме» й не «зазвичай збігається»: строго те саме, з арифметики. Найкраще це видно, коли крутиш пряму сам.
Інтерактив 1 · Крути пряму й дивись на два числа
Шість оголошень, дві ознаки — ціна й оцінка каталогу, обидві в тисячах гривень. Проведи повзунок від 0 до 180 градусів і стеж за двома величинами внизу: вони дзеркальні.
І ось тепер — назва. Напрямок, який максимізує розкид проєкцій, у лінійній алгебрі зветься власним вектором коваріаційної матриці, а сам максимальний розкид — її власним числом. Це не інше означення PCA й не глибша суть: це просто те, як математики називають цей напрямок, і зручний спосіб його порахувати без перебору кутів. Далі ми зробимо це руками на шести точках.
05 / ЧислаПорахуймо руками
Візьмімо шість оголошень і дві ознаки: ціну, яку просить продавець, і оцінку каталогу. Обидві в тисячах гривень — тобто в тих самих одиницях, тому масштабувати нічого не треба (у розділі 7 буде видно, чому це важлива обмовка).
Середня ціна — 11, середня оцінка — 9. Віднімаємо середні від кожної точки: тепер центр хмари стоїть у нулі, і всі подальші формули стають коротшими. Центрування — обовʼязкова перша дія PCA, бо всі напрямки ми відлічуємо саме від центру.
Це таблиця 2×2 із трьох чисел. По діагоналі — дисперсії кожної ознаки: наскільки
сильно вона сама по собі розкидана. Поза діагоналлю — коваріація: наскільки ознаки
розкидані разом. Ділимо на n−1 = 5, як це роблять numpy і
scikit-learn.
Матриця симетрична: коваріація ціни з оцінкою — те саме число, що й коваріація оцінки з ціною. Сума діагоналі, 23.2 + 8.8 = 32, — це весь розкид хмари, і саме її ми далі розкладемо на дві частини.
Власне число λ — це таке значення, при якому матриця C − λ·I перестає бути оборотною, тобто її визначник дорівнює нулю. Для матриці 2×2 визначник — це «добуток діагоналі мінус добуток антидіагоналі», і рівняння виходить звичайним квадратним:
Дискримінант: 32² − 4·112 = 1024 − 448 = 576, а корінь із 576 — рівно 24.
Перевірка: 28 + 4 = 32 — рівно сума діагоналі C. Це не збіг, а загальне правило: власні числа ділять між собою весь розкид хмари й нічого не додають і не гублять. Перша компонента забирає 28 із 32, тобто 87.5 %, друга — решту 12.5 %.
Тепер напрямок. Підставляємо λ1 = 28 у рівняння (C − λI)·u = 0 і беремо перший рядок:
Тобто напрямок (2, 1): на два кроки праворуч — один крок угору. Довжина такого вектора дорівнює √5 ≈ 2.236, а нам потрібен вектор довжини 1, тому ділимо на неї:
Координата точки вздовж напрямку — це скалярний добуток центрованої точки на u: беремо кожну координату, множимо на відповідне число з u й додаємо. Для першого оголошення це (−7)·0.894 + (−1)·0.447 = −6.71.
А тепер головна перевірка всієї теми: порахуємо дисперсію цих шести проєкцій. Вона має дорівнювати λ1 = 28 — тому що власне число і є збереженим розкидом.
Таблиця 2 · Той самий розрахунок повністю, у числах
Шість оголошень, центрування, проєкція на першу компоненту та відстань до неї.
| оголошення | ціна | оцінка | ціна − 11 | оцінка − 9 | t (уздовж) | d (упоперек) | t² | d² |
|---|---|---|---|---|---|---|---|---|
| А | 4 | 8 | −7 | −1 | −6.71 | +2.24 | 45 | 5 |
| Б | 8 | 5 | −3 | −4 | −4.47 | −2.24 | 20 | 5 |
| В | 9 | 8 | −2 | −1 | −2.24 | 0.00 | 5 | 0 |
| Г | 13 | 10 | +2 | +1 | +2.24 | 0.00 | 5 | 0 |
| Д | 16 | 9 | +5 | 0 | +4.47 | −2.24 | 20 | 5 |
| Е | 16 | 14 | +5 | +5 | +6.71 | +2.24 | 45 | 5 |
| сума | 66 | 54 | 0 | 0 | 0.00 | 0.00 | 140 | 20 |
| ÷ (n−1) | — | — | — | — | — | — | 28.0 | 4.0 |
Прочитай таблицю ще раз і зістав із інтерактивом вище. Ці ж 140 і 20 стоять на табло,
коли повзунок показує 27 градусів. Ніякої магії в PCA немає: центрування, три суми,
квадратне рівняння й один скалярний добуток. Усе решта — та сама арифметика в більшій
кількості вимірів, де замість квадратного рівняння працює
numpy.linalg.eigh.
06 / ДисперсіяПояснена дисперсія
У прикладі з шести точок перша компонента забрала 87.5 % розкиду. Ця частка й зветься поясненою дисперсією (explained variance): власне число компоненти, поділене на суму всіх власних чисел. Слово «пояснена» тут не про причини — воно означає тільки «утримана», «не втрачена при стисненні».
Практичне питання завжди одне: скільки компонент лишити. Відповідь дає накопичена частка. Береш перші дві — дивишся, скільки відсотків розкиду вони разом тримають. Береш три — дивишся знову. Зупиняєшся там, де цифра тебе влаштовує; типовий поріг у роботі — 90 або 95 %, але це домовленість, а не закон.
Порахуємо на нашій дошці. Сім стандартизованих ознак, 1 100 оголошень.
Інтерактив 2 · Скільки компонент лишити
Стовпчики — внесок кожної з семи компонент, лінія — накопичена частка. Нижче видно, скільки розкиду кожної вихідної ознаки переживає стиснення.
Числа виявились не такими, як хотілося б. Перша компонента тримає 39.9 %, друга додає 15.9 % — разом лише 55.8 %. Щоб дійти до 90 %, потрібно пʼять компонент із семи (94.2 %); чотири дають 84.6 %. Наша таблиця стискається погано, і це нормальний результат, а не помилка розрахунку: сім ознак здебільшого справді розповідають про різні речі. Модель телефона, вік акаунта та стан корпусу між собою не повʼязані ніяк.
Єдиний вимір, який справді нічого не важить, — сьомий: 0.15 %. Подивись на його
склад — це різниця між типова_ціна й оцінка_каталогу, тими самими
двома колонками з кореляцією 0.989. PCA сам, без підказок, знайшов дублікат і склав його
в один вимір із мізерною вагою.
07 / МасштабБез масштабу все брехня
Тепер про обмовку, яку я зробив у розділі 5. Там обидві ознаки були в тисячах гривень, і масштабувати не було потреби. У справжній таблиці одиниці різні — і це ламає PCA сильніше, ніж будь-яку іншу модель курсу.
Причина проста, якщо памʼятати означення. PCA шукає напрямок найбільшого розкиду, а розкид міряється у квадратах одиниць. Ось стандартні відхилення наших семи ознак:
Ціна розкидана на 8 689 «одиниць», стан — на 0.85. У квадратах різниця стає стотисячною. Тому напрямок найбільшого розкиду майже напевно збігатиметься з ціною — не тому, що ціна важливіша, а тому, що вона виміряна в дрібніших одиницях. Перепиши ціну в тисячах гривень — і перша компонента миттєво стане іншою, хоча дані не змінились. Метод, який залежить від вибору одиниць вимірювання, у такому вигляді користі не має.
Ліки ті самі, що в темі 09:
StandardScaler. Кожну колонку зводимо до середнього 0 і стандартного
відхилення 1, після чого всі ознаки заходять у розрахунок на рівних. Подивись, що це
міняє.
Інтерактив 3 · Одна кнопка, дві різні перші компоненти
Стовпчики — ваги семи ознак у першій компоненті. Перемкни режим і подивись, що лишається від решти ознак, коли ціна заходить у сирих гривнях.
У режимі «як є» на три цінові колонки припадає 99.9996 % квадрата довжини вектора. Рік, памʼять, стан і вік акаунта разом набирають 0.0000045 — їх у першій компоненті просто немає. Формально метод відпрацював; змістовно він побудував складну конструкцію, яка означає «ціна».
08 / МежіЧого PCA не вміє
Тепер нарешті намалюймо ту саму картинку, заради якої все й починалось: 1 100 оголошень у площині перших двох компонент.
Схема 2 · Дошка в двох компонентах
Ті самі 1 100 оголошень двічі. Ліворуч колір — цінова сімʼя моделі (Alfa дешеві, Beta середні, Gamma дорогі). Праворуч ті самі точки пофарбовано за колонкою «шахрайське», якої PCA не бачив.
Ліва картинка — успіх: PCA сам, без жодної підказки, вишикував телефони за ціновим класом. Колонки «модель» у нього не було взагалі, і все ж перша компонента впорядкувала оголошення саме за нею, бо ціна, типова ціна й оцінка каталогу разом і є ціновим класом. Це і є те, заради чого метод застосовують: знайти головну вісь варіації, коли ти сам не знаєш, що шукати.
Права картинка — чесний провал, і він повчальніший. Шахрайські оголошення розсипані по всій площині рівно так само, як чесні. Причини дві, і обидві варто памʼятати.
PCA не знає, що ти шукаєш. Він максимізує розкид, а не користь. Найрозкиданіший напрямок у цій таблиці — «дорогий телефон проти дешевого», і саме його метод і повернув. Ознака, за якою відрізняються шахраї, могла б жити в шостій компоненті з внеском 5.7 % — і, викинувши все, крім двох перших, ти б викинув її першою.
Компоненти лінійні. Шахрая на цій дошці видає не ціна, а відношення ціни до типової: 1 200 грн за телефон, що коштує 12 000. Відношення — дія нелінійна, і жодна сума виду a·ціна + b·типова її не відтворить. PCA будує тільки такі суми, тому ця структура для нього невидима в принципі. Дай методу колонку |ln(ціна ÷ типова)| з теми 10 — і все зміниться. Але придумав би цю колонку ти, а не PCA.
Третя втрата помітна на самому вигляді результату: компоненти нічого не означають. Ось перша, повністю:
Як це назвати? «Загальна дорожнеча оголошення» — приблизно, з натяжкою. У якій це одиниці? Ні в якій. Що означає, що в оголошення PC1 = 2.3? Нічого, що можна сказати продавцеві. Модель на компонентах може працювати чудово, але пояснити її рішення людині — а це буває вимогою — стає майже неможливо. Поки що порівняй із тим, як пояснив би рішення живий модератор: «ціна втричі нижча за типову, акаунт молодший за тиждень» — дві зрозумілі умови, кожна названа своїм імʼям. Далі в курсі буде модель, яка пояснює себе саме так, ланцюжком простих умов. А тут показувати нічого: сім вихідних колонок змішані в одне число з вагами.
Коли потрібна саме картинка
Якщо мета була лише подивитись на дані, у PCA є сильніші конкуренти — t-SNE та UMAP. Вони нелінійні: намагаються зберегти не загальний розкид, а сусідство, тобто щоб точки, близькі у вихідному просторі, лишились близькими й на картинці. Групи на їхніх малюнках виглядають набагато виразніше.
Ціна за це висока й часто не проговорена. По-перше, вони не дають перетворення: PCA видає сім чисел на компоненту, які можна застосувати до будь-якого нового оголошення хоч через рік, а t-SNE просто розкладає наявні точки й для нової доведеться рахувати все заново. По-друге, відстані між далекими групами на таких картинках нічого не значать: дві купки поруч не означають, що вони схожі. По-третє, результат залежить від налаштувань, і на тих самих даних із різними параметрами виходять різні картинки. Тому правило просте: t-SNE та UMAP — для очей, PCA — для конвеєра.
09 / ВитікВитік через PCA
Тепер помилка, яку роблять майже всі, і майже завжди — з добрих міркувань. Виглядає вона так:
Логіка здається бездоганною: PCA — метод без учителя, він таргета не бачить, отже
відповіді підглянути не може. Логіка хибна. Витік — це не тільки підглядання відповідей,
а будь-яке використання тестових рядків при підготовці даних
(тема 09). Середні й стандартні
відхилення для StandardScaler пораховані по всіх рядках, зокрема тестових.
Коваріаційна матриця — так само. Напрямки компонент підігнані під ту хмару, у якій уже
лежить тест. Тест перестав бути незнайомими даними ще до того, як зʼявилась перша
модель.
Ефект можна поміряти. Візьмемо просте питання: яку частку розкиду тестових рядків утримують дві компоненти?
На великій вибірці різниця півтора відсотка — не катастрофа, але вона є, і вона завжди в один бік, бо PCA припасувався до тесту. На сорока рядках розрив уже девʼять із половиною: чим менше даних, тим більше метод підганяється під конкретні рядки. І це та сама історія, що з масштабуванням у темі 09: помилка непомітна в коді, не викидає жодного попередження й завищує результат.
StandardScaler і
PCA кладуться в Pipeline разом із моделлю. Тоді на кожному згині
крос-валідації середні, відхилення й компоненти перераховуються заново — і тільки на
навчальній частині згину
(тема 09). Тестові рядки лише
проходять через уже готове перетворення методом transform, і жодне число
з них у нього не потрапляє.10 / ДаліЩо далі
Підсумок теми в чотирьох рядках. PCA шукає напрямки найбільшого розкиду й будує з них нові координати. Перша компонента — напрямок, у якому хмара витягнута найсильніше; максимум збереженого розкиду й мінімум втрат — це одна й та сама задача. Скільки компонент лишити, вирішує накопичена пояснена дисперсія. Перед PCA стандартизація обовʼязкова, а сам PCA живе всередині конвеєра, а не до поділу даних.
І одне спостереження, яке дуже знадобиться в наступній темі. Стиснення до k компонент — це відновлення точки з неповних даних, і для кожного оголошення можна поміряти, наскільки погано воно відновилось. Для більшості ця відстань невелика: точка лежить там, де лежать усі. А от оголошення, яке PCA відновлює з великою похибкою, — це оголошення, не схоже на решту. Саме так з методу головних компонент виходить детектор дивних рядків, і на цьому будується пошук аномалій — наступна тема блоку. Там ця відстань стане не діагностикою, а самим прогнозом.
practice.ipynb ми рахуємо PCA
двічі: спершу вручну на numpy — центрування, коваріаційна матриця,
linalg.eigh — а потім бібліотечним PCA, і звіряємо числа до
знака. Далі — пояснена дисперсія, картинка у двох компонентах, вироджена перша
компонента без масштабування й замір витоку в цифрах.Далі в темі
Теорію прочитано. Тепер закріпи її на практиці.