Principal Component Analysis
Открой любой ноутбук с разведочным анализом многомерных данных — датасет с сотней колонок, эмбеддинги из нейросети на 512 или 768 чисел, геном с десятками тысяч признаков экспрессии генов — и рано или поздно ты упрёшься в одну и ту же стену: человеческий глаз способен воспринять двумерный или в лучшем случае трёхмерный график, а признаков в данных на порядки больше. Практически в каждом таком ноутбуке находится ячейка с from sklearn.decomposition import PCA, за которой следует pca.fit_transform(X) и диаграмма рассеяния (scatter-plot), на которой сотни признаков внезапно укладываются в двумерную плоскость, а скрытая структура данных — кластеры, выбросы, тренды — становится видна невооружённым глазом. Это и есть метод главных компонент (Principal Component Analysis, PCA) — вероятно, самый широко используемый инструмент снижения размерности во всём машинном обучении.
В уроке 244 про ковариацию и корреляцию, ближе к самому концу, было явно анонсировано: собственные значения и собственные векторы ковариационной матрицы данных лежат в основе метода главных компонент. Там же был разобран небольшой пример — ковариационная матрица площади и цены квартир $\Sigma = \begin{pmatrix}250 & 32{,}5\\ 32{,}5 & 4{,}3\end{pmatrix}$, для которой были даны собственные значения $\lambda_1 \approx 254{,}226$ и $\lambda_2 \approx 0{,}074$, и было показано, что первая главная компонента объясняет примерно $99{,}97\%$ дисперсии этой пары признаков. Этот урок — прямое продолжение и развёрнутое выполнение того обещания: мы вернёмся к тому самому примеру, посчитаем не только собственные значения, но и собственные векторы явно, разберём полный алгебраический вывод того, почему направления максимальной дисперсии данных вообще оказываются собственными векторами, и пройдём весь алгоритм PCA от сырых чисел до готовой проекции.
Есть и вторая линия, которая сходится здесь же. В университетском курсе линейной алгебры урок 172 про собственные векторы и собственные значения заканчивается ровно тем же самым сюжетом — абстрактным уравнением $Av = \lambda v$, применённым к ковариационной матрице данных. Там эта конструкция разбирается как чистая алгебра: характеристическое уравнение, спектр матрицы, теорема о вещественности собственных значений симметричных матриц. Здесь та же самая алгебра надевает практический костюм: $A$ становится ковариационной матрицей признаков датасета, собственные векторы — новыми осями координат, вдоль которых данные разбросаны сильнее всего, а собственные значения — точным числом, показывающим, сколько дисперсии несёт каждая такая ось. Если до сих пор собственные векторы казались красивой, но абстрактной конструкцией линейной алгебры, этот урок — момент, когда она превращается в конкретный, ежедневно используемый инструмент дата-сайентиста.
Ты узнаешь, как формально ставится задача снижения размерности и почему она сводится к поиску направления максимальной дисперсии; как из этой задачи оптимизации напрямую выводится уравнение $\Sigma w = \lambda w$ — то есть почему главные компоненты обязаны быть собственными векторами ковариационной матрицы, а не каким-то приближённым или эвристическим выбором; как выглядит полный алгоритм PCA на реальных числах, от центрирования данных до итоговой проекции; как выбрать, сколько компонент оставить, через долю объяснённой дисперсии; почему перед PCA признаки почти всегда нужно масштабировать — ровно та же история, что и с $k$ ближайшими соседями в уроке 314; и где PCA реально применяется в индустрии — от визуализации данных до сжатия изображений и борьбы с проклятием размерности.
История
Как и многое в этом курсе, метод главных компонент был изобретён дважды — сначала геометрически, потом алгебраически, с разницей больше чем в тридцать лет. Первым был Карл Пирсон — тот самый статистик, который дал миру коэффициент корреляции (урок 244) и термин «маргинальное распределение». В 1901 году он опубликовал статью «On Lines and Planes of Closest Fit to Systems of Points in Space» («О прямых и плоскостях, наилучшим образом приближающих системы точек в пространстве»). Пирсон рассматривал задачу чисто геометрически: дано облако точек в многомерном пространстве, нужно найти прямую (или плоскость), которая проходит через это облако так, чтобы сумма квадратов расстояний от точек до этой прямой была минимальной — задача, внешне похожая на линейную регрессию, но принципиально отличающаяся тем, что минимизируется перпендикулярное расстояние до прямой, а не вертикальное отклонение по одной выделенной оси $y$. Пирсон показал, что искомая прямая проходит через центр масс облака точек, но не довёл идею до вычислительного алгоритма и не связал её явно с собственными значениями матриц — в 1901 году аппарат линейной алгебры для этого ещё не был так стандартизирован, как сегодня.
Настоящее оформление метода в узнаваемом сегодня виде принадлежит Гарольду Хотеллингу (Harold Hotelling), американскому статистику и экономисту, который в 1933 году опубликовал работу «Analysis of a Complex of Statistical Variables into Principal Components» («Разложение комплекса статистических переменных на главные компоненты»). Именно Хотеллинг ввёл сам термин «главные компоненты» (principal components) и, что важнее, переформулировал задачу Пирсона на языке максимизации дисперсии: вместо «найти прямую, минимизирующую расстояния до точек» — «найти направление, вдоль которого дисперсия проекции данных максимальна». Как будет показано дальше в этом уроке, обе формулировки математически эквивалентны (по теореме Пифагора сумма квадратов расстояния до прямой и квадрата проекции на прямую постоянна, так что минимизация первого равносильна максимизации второго), но именно формулировка Хотеллинга оказалась удобнее для алгебраического решения через собственные векторы ковариационной матрицы — и именно она стала стандартной.
Следующие несколько десятилетий PCA оставался в первую очередь инструментом статистиков и психометристов (Хотеллинг много работал над факторным анализом тестов интеллекта), пока рост вычислительных мощностей во второй половине XX века не сделал вычисление собственных векторов больших матриц рутинной операцией. Настоящий взрыв популярности PCA в прикладных инженерных задачах пришёлся на 1980-1990-е годы — классический пример «Eigenfaces» (1991 год, работа Мэттью Тёрка и Алекса Пентланда) применил PCA к распознаванию лиц, представив каждое лицо как взвешенную комбинацию небольшого числа «типовых лиц»-компонент. Сегодня sklearn.decomposition.PCA — одна из самых часто импортируемых функций в экосистеме Data Science на Python, а сама идея — искать направления максимальной изменчивости данных через собственные векторы ковариационной (или, что то же самое после нормировки, корреляционной) матрицы — легла в основу целого семейства методов, от факторного анализа до латентного семантического анализа текстов.
Задача снижения размерности: направление максимальной дисперсии
Интуиция
Представь датасет о квартирах с двумя признаками — площадью и ценой — из урока 244. Мы уже знаем, что коэффициент корреляции между ними там оказался около $0{,}99$: облако точек на графике «площадь – цена» выглядит не как размытое пятно, а как узкая, почти одномерная колбаса, вытянутая по диагонали. Если тебе нужно описать положение каждой квартиры одним-единственным числом вместо двух, интуитивно понятно, что стоит сделать: провести ось вдоль этой «колбасы» и указывать положение точки вдоль неё, а не отдельно площадь и отдельно цену. Ты почти ничего не потеряешь, потому что вся содержательная изменчивость данных и так сосредоточена вдоль этого одного направления, а поперёк колбасы точки почти не разбросаны.
Это и есть суть снижения размерности через PCA в одном предложении: найти такие новые оси координат, вдоль которых данные разбросаны максимально сильно, и описывать точки координатами вдоль этих осей вместо исходных признаков. Формально: имея $n$ признаков, PCA ищет $n$ новых, взаимно перпендикулярных направлений, упорядоченных по убыванию дисперсии данных вдоль каждого из них, — и если несколько первых направлений уже забирают почти всю дисперсию, остальные можно просто отбросить, сократив число признаков с минимальной потерей информации.
Определение
Определение: Пусть $\mathbf{X}$ — центрированная матрица данных размера $n \times p$ ($n$ наблюдений, $p$ признаков, каждый столбец имеет нулевое среднее). Первой главной компонентой называется единичный вектор $w_1 \in \mathbb{R}^p$ ($\|w_1\|=1$), вдоль которого дисперсия проекции данных $\mathbf{X}w_1$ максимальна:
$$w_1 = \arg\max_{\|w\|=1} \mathrm{Var}(\mathbf{X}w).$$Вторая главная компонента $w_2$ — единичный вектор, ортогональный $w_1$ ($w_2^Tw_1=0$), максимизирующий дисперсию проекции среди всех направлений, перпендикулярных первому. Аналогично строятся все последующие компоненты $w_3, w_4, \dots$: каждая следующая максимизирует оставшуюся дисперсию, будучи ортогональной всем предыдущим. Значения проекций $\mathbf{X}w_1, \mathbf{X}w_2, \dots$ называют главными компонентами данных (или счётами, scores), а сами направления $w_1, w_2, \dots$ — компонентами нагрузок (loadings).
Примеры с разбором
Пример 1 (лёгкий). Для датасета площади и цены квартир из урока 244 ($\Sigma = \begin{pmatrix}250 & 32{,}5\\32{,}5 & 4{,}3\end{pmatrix}$, $\lambda_1 \approx 254{,}226$) первая главная компонента объясняет $\lambda_1/(\lambda_1+\lambda_2) \approx 99{,}97\%$ суммарной дисперсии. Что это означает содержательно для задачи снижения размерности?
Это означает, что если заменить два признака (площадь и цену) одним новым числом — координатой вдоль первой главной компоненты, — теряется в среднем лишь около $0{,}03\%$ разброса исходных данных. Вместо двумерной таблицы «площадь, цена» можно работать с одномерным «индексом квартиры» — одним числом на объект, — который почти полностью сохраняет всю содержательную изменчивость исходных двух признаков. Именно такая ситуация (сильно коррелированные признаки) и есть тот случай, где PCA работает эффектнее всего.
Пример 2 (средний). Возьмём датасет из пяти клиентов интернет-магазина с признаками $X_1$ — среднее время сессии в минутах, $X_2$ — число просмотренных страниц за сессию: $A(2,3)$, $B(3,4)$, $C(4,5)$, $D(5,6)$, $E(6,5)$. Проверим определение из этого раздела напрямую: сравним дисперсию проекции данных на ось $X_1$ (направление $w=(1,0)$), на ось $X_2$ (направление $w=(0,1)$) и на диагональное направление $w=(1,1)/\sqrt2 \approx (0{,}707;\,0{,}707)$.
Центрируем данные: средние $\overline{X_1}=4$, $\overline{X_2}=4{,}6$, отклонения — $A(-2;-1{,}6)$, $B(-1;-0{,}6)$, $C(0;0{,}4)$, $D(1;1{,}4)$, $E(2;0{,}4)$ (проверка суммы: $-2-1+0+1+2=0$ и $-1{,}6-0{,}6+0{,}4+1{,}4+0{,}4=0$, обе координаты центрированы верно). Проекция на ось $X_1$ ($w=(1,0)$) — это просто сами отклонения $X_1$: $-2,-1,0,1,2$, дисперсия $=(4+1+0+1+4)/4=2{,}5$. Проекция на ось $X_2$ ($w=(0,1)$) — отклонения $X_2$: $-1{,}6,-0{,}6,0{,}4,1{,}4,0{,}4$, дисперсия $=(2{,}56+0{,}36+0{,}16+1{,}96+0{,}16)/4=1{,}3$. Проекция на диагональ $w=(0{,}707;0{,}707)$: для точки $A$ это $0{,}707\cdot(-2)+0{,}707\cdot(-1{,}6)=-2{,}545$, аналогично для остальных: $B\to-1{,}131$, $C\to0{,}283$, $D\to1{,}697$, $E\to1{,}697$. Сумма квадратов: $6{,}477+1{,}279+0{,}080+2{,}880+2{,}880=13{,}596$, дисперсия $=13{,}596/4\approx3{,}399$.
Диагональное направление даёт заметно бо́льшую дисперсию проекции ($3{,}399$), чем чистые оси $X_1$ ($2{,}5$) или $X_2$ ($1{,}3$) — но, забегая вперёд (это будет явно вычислено в следующем разделе), даже это диагональное направление ещё не оптимально: настоящая первая главная компонента этих данных даёт дисперсию около $3{,}52$, чуть больше, чем случайно выбранная диагональ $(1,1)/\sqrt2$. Это иллюстрирует суть задачи оптимизации: PCA не перебирает направления наугад, а находит математически точное направление максимума.
Пример 3 (сложный). Изображение рукописной цифры из классического датасета MNIST — это матрица $28 \times 28$ пикселей, то есть вектор из $784$ признаков (яркость каждого пикселя). Визуализировать такие данные напрямую невозможно — человек не видит 784-мерное пространство. Как PCA решает эту проблему, и почему разумно ожидать, что первые несколько компонент уже объяснят заметную долю дисперсии?
PCA находит в 784-мерном пространстве пикселей несколько направлений максимальной дисперсии среди тысяч изображений цифр и проецирует каждое изображение на первые две (для визуализации на плоскости) или несколько десятков (для реальной работы модели) таких направлений. Ожидание, что уже небольшое число компонент объяснит заметную долю дисперсии, оправдано тем, что соседние пиксели рукописных цифр сильно коррелированы между собой (тёмный пиксель почти всегда окружён другими тёмными пикселями той же линии штриха) — точно та же логика, что и с площадью и ценой квартиры: сильная корреляция признаков означает, что «истинная» размерность данных заметно ниже числа исходных признаков. На практике для MNIST первые 50 главных компонент из 784 обычно объясняют свыше $80\text{-}90\%$ суммарной дисперсии — а первые две-три компоненты, хотя и объясняют куда меньшую долю, уже достаточно хороши, чтобы на плоскости стало видно, как разные цифры образуют более или менее различимые скопления точек. Именно этот сценарий — двумерная визуализация многомерных данных — подготовит следующий урок про t-SNE, метод, который решает ту же задачу визуализации, но с другим математическим аппаратом, лучше приспособленным именно для сохранения локальной структуры кластеров, а не глобальной дисперсии.
Почему это важно
Формулировка «найти направление максимальной дисперсии» — не абстрактная математическая прихоть, а прямое следствие того, что такое информация в данных. Если признак (или направление в пространстве признаков) почти не меняется от объекта к объекту, он почти ничего не говорит о различиях между объектами — вся содержательная информация, которая различает один объект от другого, живёт именно в направлениях максимального разброса. Отбрасывая направления с маленькой дисперсией, PCA отбрасывает именно ту часть данных, которая меньше всего помогает отличить один объект от другого — иными словами, действует так же, как сжатие изображения: убирает то, что почти не меняется, и сохраняет то, что несёт основную содержательную нагрузку.
Главные компоненты — это собственные векторы ковариационной матрицы
Интуиция
В предыдущем разделе задача была сформулирована как оптимизация: найти единичный вектор $w$, максимизирующий дисперсию проекции $\mathbf{X}w$. Это звучит как задача, для которой нужно перебирать направления или запускать градиентный спуск. На деле она решается точно и аналитически — и решение оказывается ровно той конструкцией, которую ты уже видел в университетском курсе: уравнением $Av = \lambda v$. Разница лишь в том, что вместо произвольной матрицы линейного преобразования здесь стоит ковариационная матрица данных, а собственные векторы приобретают конкретный смысл — осей наибольшего разброса облака точек.
Определение
Определение: Пусть $\Sigma$ — ковариационная матрица признаков (симметричная и положительно полуопределённая, урок 244). Главные компоненты — это собственные векторы $v_1, v_2, \dots, v_p$ матрицы $\Sigma$, упорядоченные по убыванию соответствующих собственных значений $\lambda_1 \ge \lambda_2 \ge \dots \ge \lambda_p \ge 0$. Собственный вектор $v_1$, отвечающий наибольшему собственному значению $\lambda_1$, — это первая главная компонента, а само $\lambda_1$ равно дисперсии данных вдоль этого направления. В общем случае $k$-я главная компонента $v_k$ — направление максимальной дисперсии среди всех направлений, ортогональных первым $k-1$ компонентам, а $\mathrm{Var}(\mathbf{X}v_k) = \lambda_k$.
Докажем это утверждение — вывод короткий и опирается ровно на тот аппарат условной оптимизации с ограничением, который уже встречался в курсе (урок 279, условия Каруша-Куна-Таккера). Нужно максимизировать $w^T\Sigma w = \mathrm{Var}(\mathbf{X}w)$ при ограничении $w^Tw=1$. Составляем функцию Лагранжа:
$$\mathcal{L}(w,\lambda) = w^T\Sigma w - \lambda(w^Tw - 1).$$Берём градиент по $w$ и приравниваем к нулю (используя, что $\Sigma$ симметрична, так что $\nabla_w(w^T\Sigma w) = 2\Sigma w$):
$$\frac{\partial \mathcal{L}}{\partial w} = 2\Sigma w - 2\lambda w = 0 \quad\Longrightarrow\quad \Sigma w = \lambda w.$$Это в точности уравнение на собственные векторы и собственные значения из урока 172. Иными словами, любая точка, в которой достигается локальный экстремум дисперсии проекции при ограничении единичной длины вектора, обязана быть собственным вектором ковариационной матрицы — никаких других кандидатов на экстремум попросту не существует. Осталось понять, какой из собственных векторов даёт максимум, а не минимум или седловую точку. Подставим найденное соотношение обратно в исходную целевую функцию: если $w=v_i$ — собственный вектор с собственным значением $\lambda_i$, то
$$w^T\Sigma w = v_i^T(\Sigma v_i) = v_i^T(\lambda_i v_i) = \lambda_i (v_i^Tv_i) = \lambda_i \cdot 1 = \lambda_i.$$Значение дисперсии проекции в точности равно собственному значению. Поскольку у симметричной матрицы $\Sigma$ все собственные значения вещественны (следствие из урока 172, применённое к ковариационным матрицам), а сама $\Sigma$ положительно полуопределена, значит $\lambda_i \ge 0$ для всех $i$ — и максимум среди всех $p$ возможных значений целевой функции $w^T\Sigma w = \lambda_i$ достигается ровно на наибольшем собственном значении $\lambda_1$. Значит первая главная компонента — это собственный вектор, отвечающий наибольшему $\lambda$. А поскольку симметричная матрица допускает набор из $p$ взаимно ортогональных собственных векторов (спектральная теорема, урок 172-173), задача нахождения второй компоненты — «максимизировать дисперсию среди направлений, ортогональных первому» — автоматически решается следующим по величине собственным значением $\lambda_2$ и его собственным вектором, и так далее по убыванию спектра.
Примеры с разбором
Пример 1 (лёгкий, полный численный расчёт). Вернёмся к датасету клиентов интернет-магазина из предыдущего раздела: $A(2,3)$, $B(3,4)$, $C(4,5)$, $D(5,6)$, $E(6,5)$ с центрированными отклонениями $A(-2;-1{,}6)$, $B(-1;-0{,}6)$, $C(0;0{,}4)$, $D(1;1{,}4)$, $E(2;0{,}4)$. Построим ковариационную матрицу, найдём её собственные значения и собственные векторы, и спроецируем данные на первую главную компоненту.
Шаг 1. Ковариационная матрица. Дисперсии уже найдены в предыдущем разделе: $\Sigma_{11}=2{,}5$, $\Sigma_{22}=1{,}3$. Ковариацию найдём как сумму произведений отклонений, делённую на $n-1=4$: произведения $(-2)(-1{,}6)=3{,}2$, $(-1)(-0{,}6)=0{,}6$, $0\cdot0{,}4=0$, $1\cdot1{,}4=1{,}4$, $2\cdot0{,}4=0{,}8$, сумма $=6{,}0$, значит $\Sigma_{12}=6{,}0/4=1{,}5$. Итого:
$$\Sigma = \begin{pmatrix}2{,}5 & 1{,}5\\ 1{,}5 & 1{,}3\end{pmatrix}.$$Шаг 2. Собственные значения. След матрицы $\mathrm{tr}(\Sigma)=2{,}5+1{,}3=3{,}8$, определитель $\det(\Sigma)=2{,}5\cdot1{,}3-1{,}5^2=3{,}25-2{,}25=1{,}0$. Характеристическое уравнение: $\lambda^2 - 3{,}8\lambda + 1{,}0=0$. Дискриминант $D=3{,}8^2-4\cdot1{,}0=14{,}44-4=10{,}44$, $\sqrt{D}\approx3{,}231$. Корни:
$$\lambda_1 = \frac{3{,}8+3{,}231}{2} \approx 3{,}516, \qquad \lambda_2 = \frac{3{,}8-3{,}231}{2} \approx 0{,}284.$$Проверка: сумма $3{,}516+0{,}284=3{,}800$ ✓ (равна следу), произведение $3{,}516\cdot0{,}284\approx0{,}999\approx1{,}0$ ✓ (равно определителю, с округлением сходится).
Шаг 3. Собственный вектор для $\lambda_1$. Решаем $(\Sigma-\lambda_1 I)v=0$:
$$\begin{pmatrix}2{,}5-3{,}516 & 1{,}5\\ 1{,}5 & 1{,}3-3{,}516\end{pmatrix}v = \begin{pmatrix}-1{,}016 & 1{,}5\\ 1{,}5 & -2{,}216\end{pmatrix}v = 0.$$Из первой строки: $-1{,}016\,v_1 + 1{,}5\,v_2 = 0 \Rightarrow v_1 = 1{,}477\,v_2$. Возьмём $v_2=1$, тогда $v_1\approx1{,}477$, длина вектора $\sqrt{1{,}477^2+1^2}=\sqrt{3{,}181}\approx1{,}784$. Нормируем:
$$v_1 \approx (0{,}828;\ 0{,}561).$$Шаг 4. Проекция данных на первую главную компоненту. Для каждой точки берём скалярное произведение отклонения на $v_1$: $A: -2\cdot0{,}828-1{,}6\cdot0{,}561\approx-2{,}554$; $B: -1\cdot0{,}828-0{,}6\cdot0{,}561\approx-1{,}165$; $C: 0+0{,}4\cdot0{,}561\approx0{,}224$; $D: 1\cdot0{,}828+1{,}4\cdot0{,}561\approx1{,}613$; $E: 2\cdot0{,}828+0{,}4\cdot0{,}561\approx1{,}880$.
Сумма проекций $\approx -2{,}554-1{,}165+0{,}224+1{,}613+1{,}880 = -0{,}002 \approx 0$ ✓ (проекция центрированных данных остаётся центрированной, с точностью до округления). Дисперсия проекций: сумма квадратов $\approx 6{,}523+1{,}357+0{,}050+2{,}602+3{,}534=14{,}066$, делим на $n-1=4$: $14{,}066/4\approx3{,}517$ — практически в точности совпадает с $\lambda_1\approx3{,}516$, как и должно быть по доказанному выше равенству $\mathrm{Var}(\mathbf{X}v_1)=\lambda_1$.
Пример 2 (средний). Найдём второй собственный вектор $v_2$ для того же датасета и проверим его ортогональность к $v_1$.
Решаем $(\Sigma-\lambda_2 I)v=0$ при $\lambda_2\approx0{,}284$: $\begin{pmatrix}2{,}5-0{,}284 & 1{,}5\\1{,}5 & 1{,}3-0{,}284\end{pmatrix}=\begin{pmatrix}2{,}216 & 1{,}5\\1{,}5 & 1{,}016\end{pmatrix}$. Из первой строки: $2{,}216\,v_1+1{,}5\,v_2=0 \Rightarrow v_1=-0{,}677\,v_2$. При $v_2=1$: $v_1\approx-0{,}677$, длина $\sqrt{0{,}677^2+1^2}=\sqrt{1{,}458}\approx1{,}207$, нормированный вектор $v_2\approx(-0{,}561;\ 0{,}828)$.
Проверка ортогональности: $v_1\cdot v_2 = 0{,}828\cdot(-0{,}561)+0{,}561\cdot0{,}828 \approx -0{,}4645+0{,}4645=0{,}0000$ ✓ — векторы строго перпендикулярны, что подтверждает общее свойство: собственные векторы симметричной матрицы, отвечающие разным собственным значениям, всегда ортогональны (та же теорема из университетского курса, урок 172-173, применённая здесь напрямую к ковариационной матрице).
Пример 3 (сложный, обещанный явный расчёт для квартир из урока 244). В уроке 244 для датасета площади и цены квартир была дана ковариационная матрица $\Sigma=\begin{pmatrix}250 & 32{,}5\\32{,}5 & 4{,}3\end{pmatrix}$ и собственные значения $\lambda_1\approx254{,}226$, $\lambda_2\approx0{,}074$, но собственные векторы там не вычислялись. Найдём собственный вектор $v_1$, отвечающий $\lambda_1$.
$$\Sigma - \lambda_1 I = \begin{pmatrix}250-254{,}226 & 32{,}5\\32{,}5 & 4{,}3-254{,}226\end{pmatrix} = \begin{pmatrix}-4{,}226 & 32{,}5\\32{,}5 & -249{,}926\end{pmatrix}.$$Из первой строки: $-4{,}226\,v_1+32{,}5\,v_2=0 \Rightarrow v_1 = 7{,}693\,v_2$. При $v_2=1$: $v_1\approx7{,}693$, длина $\sqrt{7{,}693^2+1^2}=\sqrt{60{,}18}\approx7{,}758$, нормированный вектор:
$$v_1 \approx (0{,}992;\ 0{,}129).$$Это и есть тот самый явный результат, который был обещан в уроке 244. Обрати внимание на числа: первая координата ($0{,}992$) почти в восемь раз больше второй ($0{,}129$) — первая главная компонента направлена почти строго вдоль оси площади и лишь совсем немного отклоняется в сторону цены. Причина не в том, что площадь «важнее» цены для описания квартиры, а в том, что дисперсия площади в исходных единицах ($250$ кв. м²) на два порядка больше дисперсии цены в исходных единицах ($4{,}3$ млн²) — собственный вектор ковариационной матрицы неизбежно вытягивается в сторону признака с бо́льшим числовым разбросом. Этот эффект — прямая подготовка к разделу про масштабирование признаков дальше в этом уроке: сырая (нестандартизированная) ковариационная матрица заставляет PCA непропорционально ориентироваться на признаки с большим масштабом значений, а не на признаки с реально более сильной структурной связью.
Почему это важно
Доказательство $\Sigma w=\lambda w$ — не просто красивая алгебра ради алгебры. Оно объясняет, почему PCA вообще решается точно и быстро, а не приближённо через итеративную оптимизацию: задача нахождения всех главных компонент сводится к одной операции линейной алгебры — разложению матрицы по собственным векторам (eigendecomposition), — для которой существуют быстрые и численно устойчивые алгоритмы. Это же объясняет, почему sklearn.decomposition.PCA().fit(X).components_ возвращает именно строки, ортогональные друг другу, а explained_variance_ — именно собственные значения ковариационной матрицы: библиотека не изобретает магию, а решает ровно то уравнение, которое было выведено в этом разделе.
Алгоритм PCA: четыре шага от сырых данных до новых признаков
Интуиция
Весь предыдущий раздел был про то, почему главные компоненты — это собственные векторы ковариационной матрицы. Теперь соберём это знание в конкретный пошаговый рецепт, который можно применить к любому датасету: от таблицы клиентов интернет-магазина до матрицы пикселей изображений. Рецепт состоит всего из четырёх шагов, и все четыре ты уже видел по отдельности в предыдущих разделах — здесь они просто выстраиваются в правильном порядке.
Определение
Определение (алгоритм PCA):
Центрирование. Из каждого признака вычесть его среднее значение по выборке, чтобы каждый столбец данных имел нулевое среднее — без этого шага ковариационная матрица (и вся дальнейшая математика) вычисляется некорректно.
Вычисление ковариационной матрицы. Построить матрицу $\Sigma$ размера $p \times p$ (или, что почти всегда эквивалентно на практике, работать напрямую с сингулярным разложением центрированной матрицы данных — этот технический нюанс разобран в примере 3 ниже).
Нахождение собственных значений и собственных векторов. Разложить $\Sigma$ на собственные векторы $v_1,\dots,v_p$ с собственными значениями $\lambda_1\ge\dots\ge\lambda_p$ и упорядочить их по убыванию $\lambda$.
Проекция на первые $k$ компонент. Выбрать число компонент $k \le p$ (по критерию из следующего раздела) и получить новые координаты каждого объекта как $\mathbf{X}_{\text{new}} = \mathbf{X}\,[v_1\ v_2\ \dots\ v_k]$ — матрицу центрированных данных, умноженную на матрицу из первых $k$ собственных векторов, поставленных столбцами.
Примеры с разбором
Пример 1 (лёгкий, полный проход по датасету). Применим все четыре шага к датасету клиентов $A(2,3)$, $B(3,4)$, $C(4,5)$, $D(5,6)$, $E(6,5)$ из предыдущих разделов, выбрав $k=1$.
Шаг 1 (центрирование) уже сделан: отклонения $A(-2;-1{,}6)$, $B(-1;-0{,}6)$, $C(0;0{,}4)$, $D(1;1{,}4)$, $E(2;0{,}4)$. Шаг 2 (ковариация): $\Sigma=\begin{pmatrix}2{,}5&1{,}5\\1{,}5&1{,}3\end{pmatrix}$. Шаг 3 (собственные значения и векторы): $\lambda_1\approx3{,}516$ с $v_1\approx(0{,}828;\,0{,}561)$, $\lambda_2\approx0{,}284$ с $v_2\approx(-0{,}561;\,0{,}828)$ — всё это уже вычислено в предыдущем разделе. Шаг 4 (проекция при $k=1$): используем только $v_1$, получая одномерное представление каждого клиента: $A\to-2{,}554$, $B\to-1{,}165$, $C\to0{,}224$, $D\to1{,}613$, $E\to1{,}880$.
Пять клиентов, изначально описанных двумя признаками (время сессии, число страниц), теперь описываются одним числом каждый — «общий индекс вовлечённости», упорядочивающий клиентов от наименее до наиболее активных, — и, как было проверено ранее, это одно число сохраняет $3{,}516/3{,}800\approx92{,}5\%$ исходной дисперсии данных.
Пример 2 (средний, выбор $k=1$ против $k=2$). Для того же датасета сравним результат при $k=1$ (одна компонента) и $k=2$ (обе компоненты, полная замена координат без потери информации).
При $k=2$ проекция на обе компоненты $v_1$ и $v_2$ даёт для каждого клиента пару чисел вместо одного, например для $A$: первая координата $-2{,}554$ (как в примере 1), вторая — проекция на $v_2\approx(-0{,}561;0{,}828)$: $-2\cdot(-0{,}561)+(-1{,}6)\cdot0{,}828\approx1{,}122-1{,}325=-0{,}203$. Такая проекция при $k=p=2$ (число компонент равно числу исходных признаков) — это просто поворот системы координат: суммарная дисперсия сохраняется полностью ($\lambda_1+\lambda_2=3{,}8$, в точности равно сумме исходных дисперсий $2{,}5+1{,}3=3{,}8$), никакая информация не теряется, но зато новые оси выстроены так, что вдоль первой сосредоточена почти вся изменчивость ($92{,}5\%$), а вдоль второй — совсем немного ($7{,}5\%$). Именно поэтому переход к $k
Пример 3 (сложный, код и техническая деталь про SVD). Реализуем те же четыре шага в sklearn и сверим результат с ручным расчётом.
import numpy as np
from sklearn.decomposition import PCA
X = np.array([[2, 3], [3, 4], [4, 5], [5, 6], [6, 5]])
pca = PCA(n_components=2)
X_new = pca.fit_transform(X)
print(pca.explained_variance_) # [3.516..., 0.284...]
print(pca.explained_variance_ratio_) # [0.925..., 0.075...]
print(pca.components_) # строки ~ [0.828, 0.561] и [-0.561, 0.828] (с точностью до знака)
Числа explained_variance_ и components_ практически совпадают с $\lambda_1,\lambda_2$ и $v_1,v_2$, найденными руками выше — с одной оговоркой: знак собственного вектора, который выдаёт библиотека, может отличаться (например, sklearn может вернуть $(-0{,}828;\,-0{,}561)$ вместо $(0{,}828;\,0{,}561)$) — направление прямой определено однозначно, а вот в какую сторону вдоль неё «смотрит» вектор, зависит от деталей численного алгоритма и может меняться между запусками или версиями библиотеки. Технически стоит также знать: sklearn.decomposition.PCA не строит явно ковариационную матрицу $\Sigma$ и не решает уравнение $\Sigma w=\lambda w$ напрямую, а применяет сингулярное разложение (SVD) к центрированной матрице данных $\mathbf{X}$ — это математически эквивалентно (собственные векторы $\mathbf{X}^T\mathbf{X}$ совпадают с правыми сингулярными векторами $\mathbf{X}$), но заметно более устойчиво численно, особенно когда число признаков велико, а ковариационная матрица плохо обусловлена.
Почему это важно
Именно то, что PCA сводится к жёсткому, детерминированному алгоритму из четырёх шагов, а не к эвристике с множеством настроек, делает его настолько предсказуемым и воспроизводимым инструментом — при одинаковых входных данных (с точностью до знака векторов) любые две правильные реализации PCA обязаны дать одинаковый результат, в отличие, скажем, от кластеризации k-means (урок 320), где результат зависит от случайной инициализации центроидов. Понимание этих четырёх шагов также объясняет, что в реальном пайплайне PCA почти никогда не применяется «в вакууме»: шаг 1 (центрирование) sklearn делает автоматически внутри PCA.fit(), но шаг «масштабирование до центрирования» — центральная тема одного из следующих разделов — библиотека за тебя не делает никогда, и про него легко забыть.
Доля объяснённой дисперсии и выбор числа компонент
Интуиция
Алгоритм из предыдущего раздела оставляет один открытый вопрос: сколько компонент $k$ на самом деле оставлять? Для визуализации ответ почти всегда очевиден — $k=2$ или $k=3$, потому что больше на плоскости или в 3D всё равно не нарисовать. Но если PCA применяется не для картинки, а для сжатия данных или как шаг предобработки перед обучением другой модели, нужен количественный критерий: сколько «сигнала» теряется при отбрасывании каждой следующей компоненты.
Определение
Определение: Долей объяснённой дисперсии (explained variance ratio) $k$-й главной компоненты называется отношение её собственного значения к сумме всех собственных значений:
$$\text{EVR}_k = \frac{\lambda_k}{\sum_{i=1}^p \lambda_i} = \frac{\lambda_k}{\mathrm{tr}(\Sigma)}.$$Накопленной долей объяснённой дисперсии для первых $k$ компонент называется $\sum_{i=1}^{k}\text{EVR}_i$. Число компонент $k$ обычно выбирают одним из способов: фиксируют порог накопленной доли (часто $90\%$, $95\%$ или $99\%$), либо строят «scree plot» — график $\lambda_i$ в порядке убывания — и ищут точку излома («локоть»), после которой собственные значения перестают заметно убывать.
Примеры с разбором
Пример 1 (лёгкий). Для датасета клиентов $\lambda_1\approx3{,}516$, $\lambda_2\approx0{,}284$. Найди $\text{EVR}_1$, $\text{EVR}_2$ и накопленную долю после каждой компоненты.
$\text{EVR}_1 = 3{,}516/3{,}800 \approx 0{,}925$ ($92{,}5\%$), $\text{EVR}_2 = 0{,}284/3{,}800 \approx 0{,}075$ ($7{,}5\%$). Накопленная доля после первой компоненты — $92{,}5\%$, после второй — ровно $100\%$ (как и должно быть: сумма всех $p$ собственных значений всегда даёт полную дисперсию данных). Если задать порог $90\%$, достаточно оставить $k=1$ компоненту; для порога $95\%$ пришлось бы оставить обе.
Пример 2 (средний, график собственных значений — scree plot — на пяти компонентах). Пусть для датасета из пяти признаков собственные значения ковариационной матрицы оказались (уже упорядочены по убыванию): $\lambda_1=8{,}2$, $\lambda_2=3{,}1$, $\lambda_3=1{,}4$, $\lambda_4=0{,}9$, $\lambda_5=0{,}4$. Построй таблицу накопленной доли объяснённой дисперсии и определи минимальное $k$, достаточное для порога $95\%$.
Сумма всех $\lambda_i = 8{,}2+3{,}1+1{,}4+0{,}9+0{,}4=14{,}0$.
k EVR_k накопленная доля
1 0.586 58.6%
2 0.221 80.7%
3 0.100 90.7%
4 0.064 97.1%
5 0.029 100.0%
Порог $95\%$ впервые превышается при $k=4$ ($97{,}1\%$) — значит для этого датасета нужно оставить четыре из пяти компонент, а сжатие с пяти признаков до четырёх экономит лишь один признак при заметной сохранённой полноте. Обрати внимание на разницу с примером 1: там уже одна компонента почти полностью описывала данные, здесь дисперсия распределена куда равномернее между компонентами, а значит и снижение размерности здесь даёт куда более скромный выигрыш — сам по себе PCA не гарантирует сильного сжатия, оно зависит от того, насколько признаки исходно коррелированы.
Пример 3 (сложный, реалистичный ML-масштаб). Для датасета MNIST (784 признака-пикселя) типичная картина такая: первые 10 главных компонент объясняют около $48\%$ суммарной дисперсии, первые 50 — около $82\%$, первые 150 — около $95\%$, а все 784 — очевидно, $100\%$. Обсуди, почему для визуализации имеет смысл использовать $k=2$, а для предобработки перед обучением классификатора — $k=50$ или больше, хотя обе задачи называются «PCA на одних и тех же данных».
Для визуализации цель — не максимально точно сохранить данные, а получить картинку, на которой видна структура (скопления похожих цифр), которую человек способен воспринять глазами; двумерная проекция, объясняющая всего $20\text{-}30\%$ дисперсии, всё равно может визуально разделить классы, потому что даже неполная информация о главных направлениях разброса часто уже достаточна для грубой визуальной кластеризации. Для предобработки перед обучением модели цель другая — минимизировать потерю информации, которая могла бы понадобиться классификатору для точного разделения похожих цифр (например, отличить рукописную «3» от «8»), поэтому здесь оправдано держать порог накопленной дисперсии высоким ($90\text{-}99\%$), даже если итоговое число компонент (полсотни-сотня) уже не нарисуешь на одном графике. Это ключевое практическое различие: число компонент для визуализации и число компонент для предобработки — два разных вопроса с разными критериями «сколько достаточно», даже когда речь идёт об одном и том же датасете.
Почему это важно
Доля объяснённой дисперсии — единственный количественный ответ на вопрос «а не потеряли ли мы слишком много, сжав данные?» без этого числа выбор $k$ превращается в гадание. На практике PCA(n_components=0.95) в sklearn принимает не целое число компонент, а именно долю дисперсии — библиотека сама подбирает минимальное $k$, обеспечивающее заданный порог, что превращает разобранный здесь ручной расчёт в одну строку кода. Но важно помнить: высокая накопленная доля объяснённой дисперсии не гарантирует, что оставленные компоненты полезны для конкретной последующей задачи (классификации, кластеризации) — PCA максимизирует именно дисперсию данных, а не разделимость классов, и в редких случаях направление с наибольшей дисперсией может оказаться как раз тем направлением, вдоль которого классы перепутаны сильнее всего.
Масштабирование признаков перед PCA
Интуиция
Ты уже сталкивался с этой проблемой в уроке 314 про $k$ ближайших соседей: доход в рублях (диапазон — десятки и сотни тысяч) и возраст в годах (диапазон — пара десятков) в сыром виде несопоставимы по масштабу, и любой алгоритм, работающий с расстояниями или дисперсиями, будет искажённо отдавать предпочтение признаку с большим числовым разбросом просто из-за единиц измерения, а не из-за реальной значимости. PCA устроен именно на дисперсиях — а значит подвержен ровно той же болезни, причём даже острее, чем kNN: там масштаб искажал расстояния, здесь он напрямую искажает сами направления главных компонент.
Определение
Определение: Перед вычислением ковариационной матрицы для PCA признаки почти всегда предварительно стандартизируют (приводят к нулевому среднему и единичной дисперсии, $z = (x-\mu)/\sigma$,
StandardScaler). Ковариационная матрица уже стандартизированных признаков в точности совпадает с корреляционной матрицей исходных признаков (урок 244) — на диагонали единицы, вне диагонали коэффициенты корреляции $\rho_{ij}$. PCA, применённый к стандартизированным данным, называют иногда «PCA по корреляционной матрице», в противоположность «PCA по ковариационной матрице» на сырых, немасштабированных признаках.
Примеры с разбором
Пример 1 (лёгкий, продолжение примера с квартирами). В предыдущем разделе для квартир (площадь, цена) на сырых данных был получен собственный вектор первой компоненты $v_1\approx(0{,}992;\,0{,}129)$ — почти полностью совпадающий с осью площади. Найдём первую компоненту для тех же данных, но после стандартизации, зная, что коэффициент корреляции между площадью и ценой $\rho\approx0{,}991$ (посчитан в уроке 244).
После стандартизации ковариационная матрица становится корреляционной: $R=\begin{pmatrix}1 & 0{,}991\\0{,}991 & 1\end{pmatrix}$. Для матрицы такого вида ($1$ на диагонали, $\rho$ вне диагонали) собственные значения находятся мгновенно: $\lambda_1=1+\rho\approx1{,}991$, $\lambda_2=1-\rho\approx0{,}009$, а собственные векторы — ровно диагональные направления $(1,1)/\sqrt2\approx(0{,}707;\,0{,}707)$ и $(1,-1)/\sqrt2\approx(0{,}707;\,-0{,}707)$ (проверка: подставь $w=(0{,}707;0{,}707)$ в $Rw$ — получится $(0{,}707(1+\rho);\,0{,}707(1+\rho))=(1+\rho)\cdot w$, то есть собственный вектор с собственным значением $1+\rho$, что и требовалось).
Результат разительно отличается от сырого расчёта: на стандартизированных данных первая компонента $\approx(0{,}707;\,0{,}707)$ — равный вклад площади и цены, — тогда как на сырых данных получалось почти $(0{,}992;\,0{,}129)$ — подавляющий перевес в сторону площади. Оба результата математически верны, но отвечают на разные вопросы: сырой PCA отвечает «вдоль какого направления абсолютный числовой разброс данных в квадратных метрах и миллионах рублей максимален» (и там неизбежно побеждает признак с большей абсолютной дисперсией просто по масштабу единиц), а PCA на стандартизированных данных отвечает «вдоль какого направления максимален разброс данных, если оба признака сначала уравнять по масштабу» — то есть более осмысленный вопрос, когда признаки измерены в принципиально разных единицах.
Пример 2 (средний, три признака с сильно разными масштабами). В уроке 244 был датасет из трёх признаков клиентов: возраст $X_1$ ($\mathbb{D}X_1=64$), доход $X_2$ ($\mathbb{D}X_2=90\,000$), стаж работы $X_3$ ($\mathbb{D}X_3=9$). Покажем, что на сырых данных первая компонента почти полностью определяется доходом, сравнив дисперсию проекции на «чистую ось дохода» $w=(0,1,0)$ и на «равновесное» направление $w=(1,1,1)/\sqrt3$.
Проекция на чистую ось дохода даёт дисперсию, в точности равную $\mathbb{D}X_2=90\,000$. Проекция на равновесное направление $w=(1/\sqrt3,1/\sqrt3,1/\sqrt3)$ даёт $w^T\Sigma w = \tfrac13(\Sigma_{11}+\Sigma_{22}+\Sigma_{33}) + \tfrac23(\Sigma_{12}+\Sigma_{13}+\Sigma_{23})$; используя $\Sigma_{11}=64$, $\Sigma_{22}=90\,000$, $\Sigma_{33}=9$, $\Sigma_{12}=1200$, $\Sigma_{13}=18$, $\Sigma_{23}=150$ из урока 244: $\tfrac13(64+90\,000+9) + \tfrac23(1200+18+150) = \tfrac13\cdot90\,073 + \tfrac23\cdot1368 \approx 30\,024{,}3+912{,}0=30\,936{,}3$.
Даже направление, равномерно взвешивающее все три признака, даёт дисперсию проекции всего $\approx30\,936$ — заметно меньше, чем $90\,000$ вдоль чистой оси дохода. Значит истинная первая главная компонента (максимум по всем направлениям, а не только по этим двум кандидатам) окажется ещё ближе к чистой оси дохода, чем равновесное направление, — просто потому, что доход измерен в тысячах рублей с огромной абсолютной дисперсией, а возраст и стаж — в единицах, где абсолютные числа малы. PCA на сырых данных здесь фактически «не заметит» возраст и стаж вообще, хотя содержательно они могут быть не менее важны для описания клиента.
Пример 3 (сложный, код пайплайна). Как и в уроке 314 для kNN, правильная защита от утечки статистик масштабирования из теста в обучение — обернуть StandardScaler и PCA в единый Pipeline.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
pipe = Pipeline([
("scaler", StandardScaler()),
("pca", PCA(n_components=0.95)),
])
X_train_reduced = pipe.fit_transform(X_train)
X_test_reduced = pipe.transform(X_test)
StandardScaler обучается (считает $\mu$ и $\sigma$) только на X_train, а к X_test применяется уже готовое преобразование — точно так же, как для StandardScaler перед KNeighborsClassifier в уроке 314. PCA(n_components=0.95) внутри того же пайплайна автоматически подбирает число компонент, обеспечивающее $95\%$ накопленной объяснённой дисперсии на обучающих данных, и применяет ровно те же самые направления к тестовым.
Почему это важно
Масштабирование перед PCA — не факультативная рекомендация, а системное требование почти для любого реального датасета, где признаки измерены в разных единицах: без него направления главных компонент почти гарантированно окажутся смещены в сторону признаков с наибольшим абсолютным числовым разбросом, вне зависимости от их реальной информативности, — ровно то, что было явно продемонстрировано на примере с квартирами и на примере с доходом клиентов. Единственное разумное исключение — ситуация, когда все признаки заведомо измерены в одних и тех же единицах и их относительный масштаб содержательно важен (например, интенсивность пикселей изображения от 0 до 255 по всем каналам) — тогда стандартизация иногда, наоборот, может стереть содержательно важную информацию о том, что одни области изображения варьируются сильнее других.
Применения PCA в машинном обучении
Интуиция
Всё, что было разобрано до этого раздела, — чистая математика снижения размерности. Но PCA не был бы одним из самых используемых инструментов Data Science, если бы не был полезен для конкретных практических задач. Разберём три главных сценария применения — они частично уже упоминались по ходу урока, здесь собраны вместе и развёрнуты подробнее.
Определение
Ключевые сценарии применения PCA: визуализация многомерных данных в 2D/3D для исследовательского анализа; сжатие данных и снижение требований к памяти и вычислениям; предобработка признаков перед обучением других моделей — борьба с мультиколлинеарностью и проклятием размерности (урок 314), удаление шума из данных за счёт отбрасывания компонент с маленькой дисперсией.
Примеры с разбором
Пример 1 (визуализация). Датасет Iris (классический учебный набор из 150 цветков ириса, 4 признака — длины и ширины лепестков и чашелистиков, 3 вида) спроецирован через PCA на первые две главные компоненты. На получившемся двумерном графике один из трёх видов (Iris setosa) образует чётко отделённое скопление точек, а два других вида частично перекрываются. Это типичный сценарий разведочного анализа: даже без обучения какой-либо модели классификации, один взгляд на PCA-проекцию сразу подсказывает аналитику, что задача различения setosa от остальных видов будет лёгкой, а различение двух оставшихся видов друг от друга — заметно сложнее, потому что их облака точек на графике накладываются друг на друга.
Пример 2 (сжатие и удаление шума). Классический пример сжатия изображений через PCA: фотография лица размером $100\times100$ пикселей — это вектор из $10\,000$ признаков. Применение PCA к базе из тысяч таких фотографий (метод «Eigenfaces», 1991) и сохранение первых $100\text{-}150$ компонент вместо всех $10\,000$ пикселей даёт сжатие примерно в 70-100 раз при сохранении визуально узнаваемого лица — потому что почти вся содержательная изменчивость человеческих лиц (форма носа, расположение глаз, освещение) укладывается в относительно небольшое число главных направлений, а случайный пиксельный шум камеры, наоборот, равномерно размазан по всем направлениям с маленькой дисперсией и отбрасывается вместе с отброшенными компонентами. Реконструкция изображения по $k$ главным компонентам ($\hat{\mathbf{x}} \approx \overline{\mathbf{x}} + \sum_{i=1}^k (\mathbf{x}\cdot v_i)v_i$) даёт эффект «удаления шума» практически бесплатно, как побочный эффект сжатия.
Пример 3 (предобработка перед другой моделью). В датасете со 100 признаками, среди которых десятки пар сильно коррелированных между собой (например, «площадь кухни» и «общая площадь квартиры»), линейная регрессия (урок 310) может столкнуться с мультиколлинеарностью — неустойчивыми, взаимно компенсирующими друг друга коэффициентами, о которой шла речь в уроке 244. Применение PCA перед регрессией (получившийся подход называют регрессией на главных компонентах, principal component regression) заменяет коррелированные исходные признаки на некоррелированные по построению главные компоненты (собственные векторы симметричной матрицы всегда ортогональны — значит и проекции на них всегда некоррелированы), полностью устраняя саму возможность мультиколлинеарности в новом признаковом пространстве. Похожим образом PCA часто применяют и как шаг предобработки перед кластеризацией методом k-means (урок 320) или DBSCAN (урок 322) на данных высокой размерности — оба этих алгоритма опираются на расстояния между точками, а в высокой размерности расстояния теряют содержательный смысл из-за проклятия размерности, ровно как и для kNN в уроке 314; PCA, снижая размерность до нескольких десятков осмысленных компонент, возвращает расстояниям различимость.
Почему это важно
Три сценария применения PCA — визуализация, сжатие, предобработка — формально решаются одним и тем же алгоритмом из четырёх шагов, но с разными критериями выбора $k$ и разными ожиданиями от результата: для визуализации $k$ жёстко ограничено двумя-тремя из соображений человеческого восприятия, для сжатия $k$ выбирается по балансу между экономией места и приемлемым качеством реконструкции, а для предобработки — по доле объяснённой дисперсии, достаточной, чтобы не потерять сигнал, нужный последующей модели. Понимание, что за всеми тремя сценариями стоит одна и та же математика собственных векторов ковариационной матрицы, а различаются лишь практические критерии выбора числа компонент, — это ровно тот момент, когда абстрактная линейная алгебра из университетского курса окончательно превращается в повседневный инженерный инструмент.
Практика: 30 заданий
Базовые задания (1–10)
Задание 1. Датасет из трёх точек по одному признаку $X$: $2, 4, 6$. Найди среднее и центрированные (после вычитания среднего) значения.
Задание 2. Ковариационная матрица $\Sigma=\begin{pmatrix}9 & 0\\0 & 4\end{pmatrix}$. Найди её собственные значения, не решая характеристическое уравнение — используя структуру матрицы.
Задание 3. Для матрицы из задания 2 найди долю объяснённой дисперсии первой главной компоненты.
Задание 4. Ковариационная матрица $\Sigma=\begin{pmatrix}5 & 2\\2 & 5\end{pmatrix}$. Найди след и определитель.
Задание 5. Даны собственные значения ковариационной матрицы четырёх признаков: $\lambda_1=6$, $\lambda_2=3$, $\lambda_3=0{,}8$, $\lambda_4=0{,}2$. Найди суммарную дисперсию данных (используя свойство следа матрицы).
Задание 6. Для данных из задания 5 найди накопленную долю объяснённой дисперсии после первых двух компонент.
Задание 7. Вектор $w=(0{,}6;\,0{,}8)$ предлагается как собственный вектор. Проверь, что он единичной длины (нормирован).
Задание 8. Для ковариационной матрицы клиентов интернет-магазина из этого урока $\Sigma=\begin{pmatrix}2{,}5&1{,}5\\1{,}5&1{,}3\end{pmatrix}$ найди дисперсию проекции данных на направление $w=(1,0)$ (чистая ось первого признака), не выполняя проекцию точек вручную — используя формулу $w^T\Sigma w$.
Задание 9. Объясни своими словами, почему первая главная компонента и направление, минимизирующее сумму квадратов перпендикулярных расстояний до прямой (задача Пирсона 1901 года), — это одно и то же направление.
Задание 10. Датасет содержит признаки «рост в сантиметрах» и «рост в дюймах» (то есть один и тот же признак дважды, в разных единицах, $1$ дюйм $\approx2{,}54$ см). Сколько ненулевых собственных значений будет у ковариационной матрицы этих двух «признаков» и почему?
Средние задания (11–20)
Задание 11. Используя след ($10$) и определитель ($21$) матрицы $\Sigma=\begin{pmatrix}5&2\\2&5\end{pmatrix}$ из задания 4, найди оба собственных значения.
Задание 12. Для матрицы из задания 11 найди собственный вектор, отвечающий $\lambda_1=7$.
Задание 13. Датасет из четырёх точек с одним признаком $X$: $1, 2, 3, 10$. Вычисли выборочную дисперсию и объясни, как выброс (точка $10$) влияет на дисперсию и, следовательно, на PCA.
Задание 14. Матрица $\Sigma=\begin{pmatrix}4&0&0\\0&1&0\\0&0&0{,}25\end{pmatrix}$ (уже диагональная, три признака). Найди долю объяснённой дисперсии для каждой из трёх компонент.
Задание 15. Найди дисперсию проекции данных с $\Sigma=\begin{pmatrix}2{,}5&1{,}5\\1{,}5&1{,}3\end{pmatrix}$ на направление $w=(0{,}6;\,0{,}8)$ (единичный вектор) через формулу $w^T\Sigma w$, и сравни с максимальной дисперсией $\lambda_1\approx3{,}516$, найденной в этом уроке.
Задание 16. Матрица $\Sigma=\begin{pmatrix}3&0\\0&3\end{pmatrix}$ (равные дисперсии, нулевая ковариация). Что можно сказать о главных компонентах такой матрицы?
Задание 17. Корреляционная матрица двух стандартизированных признаков $R=\begin{pmatrix}1&0{,}5\\0{,}5&1\end{pmatrix}$. Используя формулу $\lambda=1\pm\rho$ (выведенную в этом уроке), найди собственные значения и долю объяснённой дисперсии первой компоненты.
Задание 18. В коде ниже есть логическая ошибка:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
X_reduced = pca.fit_transform(X_raw)
Известно, что X_raw содержит признаки «годовой доход в рублях» (диапазон — сотни тысяч) и «число детей» (диапазон — 0-5). Найди проблему.
Задание 19. Для датасета клиентов из этого урока при проекции на $v_1\approx(0{,}828;\,0{,}561)$ получены значения $A\approx-2{,}554$, $B\approx-1{,}165$, $C\approx0{,}224$, $D\approx1{,}613$, $E\approx1{,}880$. Найди среднее этих проекций и объясни, почему оно обязано быть близко к нулю.
Задание 20. Ковариационная матрица трёх признаков имеет собственные значения $\lambda_1=100$, $\lambda_2=0{,}01$, $\lambda_3=0{,}001$. Оцени, сколько «эффективных» измерений на самом деле содержат эти данные, и что это говорит о возможной мультиколлинеарности исходных признаков.
Продвинутые задания (21–30)
Задание 21. Датасет содержит 200 признаков, из которых 190 — почти дублирующие друг друга сенсорные показания (сильно коррелированные между собой), а 10 — независимые друг от друга и от первой группы шумовые признаки с относительно небольшой, но ненулевой дисперсией. Опиши качественно, как будет выглядеть scree plot (график собственных значений по убыванию) для такого датасета.
Задание 22. Собственные векторы $v_1=(0{,}6;\,0{,}8)$ и $v_2=(0{,}8;\,-0{,}6)$ найдены для некоторой ковариационной матрицы. Проверь, что они действительно ортогональны и оба единичной длины.
Задание 23. Объясни, почему знак собственного вектора, возвращаемого sklearn.decomposition.PCA, не гарантирован и может отличаться от запуска к запуску, и почему это не является ошибкой.
Задание 24. Восстанови (реконструируй) приближённое значение центрированной точки $A=(-2;\,-1{,}6)$ из датасета клиентов, используя только её проекцию на первую компоненту $A\to-2{,}554$ и сам вектор $v_1\approx(0{,}828;\,0{,}561)$. Оцени ошибку реконструкции.
Задание 25. Перед PCA данные были центрированы, но не стандартизированы, хотя признаки измерены в существенно разных единицах (метры и килограммы). Аналитик утверждает: «Центрирование — это то же самое, что стандартизация, я уже всё сделал правильно». Опровергни это утверждение.
Задание 26. В задаче регрессии на главных компонентах (principal component regression) — то есть после применения PCA — с $k=5$ компонентами из исходных $50$ признаков качество модели на тестовых данных заметно ухудшилось по сравнению с обучением на всех 50 исходных признаках. Предложи возможное объяснение.
Задание 27. Сравни вычислительную сложность вычисления полного разложения по собственным векторам ковариационной матрицы $p\times p$ ($O(p^3)$) с ситуацией, когда число признаков $p$ значительно больше числа наблюдений $n$ (типично для геномных данных: тысячи генов, десятки образцов). Почему в таком случае sklearn предпочитает работать через SVD матрицы данных, а не через собственное разложение матрицы $\Sigma$?
Задание 28. Данные лежат строго на окружности единичного радиуса с центром в начале координат (например, точки $(\cos\theta,\sin\theta)$ для равномерно распределённых углов $\theta$). Предскажи, что покажет PCA для такого датасета, и объясни, почему это ожидаемый, а не проблемный результат.
Задание 29. Объясни своими словами разницу между PCA как методом снижения размерности и t-SNE (метод, с которым ты познакомишься в следующем уроке), опираясь только на то, что PCA максимизирует дисперсию проекции глобально по всем точкам сразу.
Задание 30. Датасет клиентов интернет-магазина из этого урока содержит признаки, измеренные без масштабирования: «время сессии в минутах» (значения от 2 до 6) и «число просмотренных страниц» (значения от 3 до 6) — оба признака уже в сопоставимом числовом диапазоне. Нужно ли в этом конкретном случае обязательно стандартизировать данные перед PCA, и почему ответ на этот вопрос неочевиден в общем случае?
Частые ошибки
Ошибка 1: забыть масштабировать признаки перед PCA. Самая частая и самая разрушительная по последствиям ошибка. Как было явно продемонстрировано на примере квартир (площадь против цены) и клиентов (доход против возраста и стажа), PCA на нестандартизированных признаках почти неизбежно выстраивает первую компоненту вдоль признака с наибольшей абсолютной дисперсией — часто просто из-за единиц измерения, а не реальной значимости.
Ошибка 2: интерпретировать высокую объяснённую дисперсию как гарантию полезности компонент для конкретной задачи. PCA максимизирует дисперсию исходных признаков, а не разделимость классов или предсказательную силу для целевой переменной регрессии (задание 26). Компоненты с высокой объяснённой дисперсией не обязаны быть теми же компонентами, что несут наибольшую пользу для последующей модели.
Ошибка 3: применять PCA к данным с сильными выбросами без предварительной проверки. Как показано в задании 13, PCA явно и напрямую опирается на дисперсию, а дисперсия крайне чувствительна к выбросам — единственная аномальная точка может «перетянуть» на себя направление первой главной компоненты, исказив картину для основной массы данных.
Ошибка 4: путать знак собственного вектора с содержательным различием. Знак направления главной компоненты не определён однозначно (задание 23) — сравнение результатов PCA между разными запусками или библиотеками «по значению координаты» без учёта возможной инверсии знака — источник ложных выводов о том, что «результат изменился», хотя геометрически направление осталось тем же самым.
Ошибка 5: считать, что PCA — это отбор признаков. PCA не выбирает подмножество исходных признаков, а строит совершенно новые признаки — линейные комбинации всех исходных. После PCA исходные признаки как таковые исчезают, и интерпретация «что означает первая компонента» требует явного анализа весов (нагрузок) внутри собственного вектора, а не простого указания на один исходный столбец данных.
Ошибка 6: применять PCA для нелинейно структурированных данных и удивляться плохому результату. PCA ищет только линейные направления максимальной дисперсии. Если истинная структура данных нелинейна (например, данные лежат на изогнутой поверхности, как в задании 28 с окружностью), PCA либо не сможет её содержательно упростить, либо исказит структуру — для таких случаев существуют нелинейные методы снижения размерности, включая t-SNE из следующего урока.
Главное запомнить
-
Метод главных компонент (PCA) ищет новые, взаимно ортогональные направления в пространстве признаков, вдоль которых дисперсия данных максимальна — эти направления и есть главные компоненты.
-
Главные компоненты — это в точности собственные векторы ковариационной матрицы данных, упорядоченные по убыванию соответствующих собственных значений; это строго доказывается через метод множителей Лагранжа, примененный к задаче максимизации $w^T\Sigma w$ при $\|w\|=1$.
-
Собственное значение $\lambda_k$, отвечающее $k$-й главной компоненте, равно в точности дисперсии данных, спроецированных на эту компоненту.
-
Алгоритм PCA состоит из четырёх шагов: центрирование данных, вычисление ковариационной матрицы, разложение по собственным векторам, проекция на первые $k$ компонент.
-
Доля объяснённой дисперсии $\lambda_k/\sum\lambda_i$ и её накопленная сумма — количественный критерий выбора числа компонент $k$, обычно по порогу накопленной доли ($90$-$99\%$) или по точке излома на графике собственных значений (scree plot).
-
Масштабирование признаков перед PCA обязательно для датасетов с разными единицами измерения — без него направления главных компонент искажаются в сторону признаков с наибольшим числовым разбросом, ровно как и в случае с kNN (урок 314).
-
Собственные векторы симметричной ковариационной матрицы всегда ортогональны, а значит главные компоненты данных всегда некоррелированы между собой — это делает PCA удобным способом устранить мультиколлинеарность перед линейной регрессией.
-
Знак направления собственного вектора не определён однозначно — практический вывод: сравнивать результаты PCA между запусками нужно с учётом возможной инверсии знака.
-
PCA применяется для визуализации многомерных данных в 2D/3D, для сжатия данных с удалением шума и как шаг предобработки перед другими моделями, включая борьбу с проклятием размерности перед kNN и кластеризацией.
-
PCA — линейный метод, максимизирующий глобальную дисперсию, а не сохраняющий локальную структуру соседей; для задач, где важна именно локальная кластерная структура, лучше подходят нелинейные методы вроде t-SNE.
Связь с темами курса
Этот урок — прямое и явное выполнение обещания из урока 244 про ковариацию и корреляцию: там было анонсировано, что собственные значения и собственные векторы ковариационной матрицы лежат в основе PCA, и был дан пример с площадью и ценой квартир, где посчитаны только собственные значения ($\lambda_1\approx254{,}226$, $\lambda_2\approx0{,}074$). Здесь этот же пример доведён до конца: явно найден собственный вектор первой компоненты $v_1\approx(0{,}992;\,0{,}129)$ на сырых данных и $v_1\approx(0{,}707;\,0{,}707)$ на стандартизированных данных — и на контрасте этих двух результатов показано, почему масштабирование признаков перед PCA не опционально. Оттуда же, из урока 244, пришла идея корреляционной матрицы как ковариационной матрицы стандартизированных признаков — здесь эта идея напрямую конвертирована в различие между «PCA по ковариационной матрице» и «PCA по корреляционной матрице».
Столь же прямая связь — с университетским уроком 172 про собственные векторы и собственные значения. Уравнение $Av=\lambda v$, характеристическое уравнение $\det(A-\lambda I)=0$, теорема о вещественности спектра симметричных матриц и о попарной ортогональности их собственных векторов — весь этот аппарат используется здесь без изменений, только матрица $A$ конкретизирована до ковариационной матрицы признаков. Урок 172 там же, ближе к концу, кратко анонсирует PCA как одно из приложений собственных векторов — этот урок разворачивает то приложение в полноценную методику с выводом, алгоритмом и численными примерами. Метод множителей Лагранжа, использованный здесь для вывода $\Sigma w=\lambda w$ из задачи максимизации дисперсии, — прямое применение аппарата условной оптимизации из урока 279 про условия Каруша-Куна-Таккера.
Внутри самого ml-course PCA закрывает целую линию тем. Проблема масштабирования признаков перед PCA — дословное повторение проблемы, впервые явно разобранной для k ближайших соседей (урок 314): там расстояния искажались признаком с большим диапазоном значений, здесь ровно тот же эффект искажает направления главных компонент. Устранение мультиколлинеарности через PCA перед линейной регрессией напрямую перекликается с обсуждением мультиколлинеарности и регуляризации Ridge/Lasso (уроки 244 и 312). А использование PCA как шага предобработки перед иерархической кластеризацией (урок 321) и DBSCAN (урок 322) на данных высокой размерности замыкает линию, начатую в уроке 314: проклятие размерности разрушает не только понятие «ближайшего соседа», но и понятие «плотного скопления точек», от которого напрямую зависит DBSCAN, — и PCA — стандартный практический ответ на эту проблему. В следующем уроке ты познакомишься с t-SNE — методом, решающим похожую задачу визуализации, но принципиально по-другому: не через глобальную максимизацию дисперсии, а через сохранение локальной структуры соседства точек.
Интересные факты
-
Между геометрической формулировкой Пирсона (1901) и алгебраической формулировкой Хотеллинга (1933), которая стала стандартной, прошло более тридцати лет — редкий случай в истории математики, когда одна и та же по сути идея была открыта дважды с разницей в поколение, прежде чем обрела своё нынешнее название и вычислительно удобную форму.
-
Классический метод «Eigenfaces» для распознавания лиц (1991, Тёрк и Пентланд) буквально назван в честь собственных векторов (eigenvectors): каждое лицо в базе представлялось как взвешенная сумма небольшого числа «типовых лиц»-компонент, полученных через PCA по тысячам фотографий, а само слово «eigenface» стало устойчивым термином в компьютерном зрении.
-
Хотя в этом уроке PCA всегда описывался как метод, работающий с ковариационной матрицей, на практике
sklearn.decomposition.PCAвообще не строит эту матрицу явно для большинства случаев — используется сингулярное разложение (SVD) матрицы данных, математически эквивалентное, но заметно быстрее и устойчивее при большом числе признаков (задание 27). -
В анализе финансовых кривых доходности облигаций первые три главные компоненты традиционно и удивительно устойчиво интерпретируются финансистами содержательно, а не как абстрактные оси: первая — «параллельный сдвиг» всей кривой доходности, вторая — «наклон» (изменение разницы между короткими и длинными ставками), третья — «изгиб» кривой; вместе эти три компоненты почти всегда объясняют свыше $95\%$ движения кривой целиком.
Лайфхаки
-
Всегда оборачивай
StandardScalerиPCAв единыйsklearn.pipeline.Pipeline— это не просто защищает от утечки статистик масштабирования из теста в обучение, но и делает пайплайн воспроизводимым при повторном применении к новым данным. -
Для выбора числа компонент используй
PCA(n_components=0.95)вместо ручного подбора целого $k$ — sklearn сам найдёт минимальное число компонент, обеспечивающее заданный порог накопленной объяснённой дисперсии, что избавляет от ручного построения таблицы, подобной той, что разобрана в этом уроке. -
Перед тем как доверять двумерной PCA-визуализации, всегда проверяй
explained_variance_ratio_для первых двух компонент — если их сумма составляет всего $15$-$20\%$, картинка показывает лишь малую и не обязательно репрезентативную часть реальной структуры данных, и делать выводы по ней стоит с большой осторожностью. -
Если PCA применяется как шаг предобработки перед моделью с учителем (регрессией или классификацией), сравнивай качество модели на полном наборе признаков и на PCA-сжатых признаках через кросс-валидацию (урок 306) — иногда снижение размерности улучшает качество за счёт удаления шума, а иногда ухудшает его из-за потери полезного сигнала (задание 26); заранее угадать результат без эксперимента почти невозможно.
-
Не забывай, что
pca.components_даёт направления в исходном признаковом пространстве — можно явно посмотреть, какие исходные признаки вносят наибольший вклад (по модулю коэффициента) в каждую главную компоненту, и часто это даёт содержательную интерпретацию новой оси («первая компонента — это в основном общий размер квартиры», «вторая компонента — это соотношение цены и удалённости от центра»), а не оставлять компоненты абстрактными безымянными числами. -
При работе с данными очень высокой размерности и относительно малой выборкой ($p \gg n$, как в геномике или текстовых эмбеддингах) не пытайся строить ковариационную матрицу вручную — доверься реализации
sklearnна основе SVD, которая специально спроектирована для эффективной работы именно в таком режиме (задание 27).
Собственные векторы и собственные значения выглядели, вероятно, самой абстрактной темой во всём университетском курсе линейной алгебры — уравнение $Av=\lambda v$, характеристический многочлен, спектр матрицы. PCA — момент, когда эта абстракция становится инструментом, который ты будешь вызывать одной строкой кода почти в каждом втором проекте по анализу данных: для визуализации многомерных датасетов, для сжатия эмбеддингов, для устранения мультиколлинеарности перед регрессией, для борьбы с проклятием размерности перед кластеризацией. Ты теперь знаешь не просто «что вызывать», а откуда берётся то, что вызывается — от задачи максимизации дисперсии через множители Лагранжа до уравнения на собственные векторы и обратно к конкретным числам в матрице данных. В следующем уроке ты познакомишься с t-SNE — методом, который решает похожую задачу визуализации совершенно другим путём, специально ориентированным на сохранение локальной, а не глобальной структуры данных.
Понял тему? Закрепи в боте! 🚀
Попрактикуйся на задачах и получи персональные рекомендации от AI
💪 Начать тренировку