Фаза 01 · урок 10

Понижение размерности: PCA, t-SNE, UMAP

Цель урока: У вас есть набор данных с 784 признаками на образец. Возможно, это значения пикселей рукописных цифр. Возможно, уровни экспрессии генов. Возможно, сигналы поведения пользователей. Вы не можете визуализировать 784 измерения. Не можете их…

Текущий релиз AlexBred.com: первые 100 уроков русскоязычной программы.

Курс
AI Engineering from Scratch
Фаза
Математические основы
Чтение
14 мин.
Проверено
Содержание урока
  1. Цели обучения
  2. Проблема
  3. Концепция
  4. Проклятие размерности
  5. PCA: найдите важные направления
  6. Доля объяснённой дисперсии
  7. Выбор числа компонент
  8. t-SNE: сохраняйте соседства
  9. UMAP: быстрее, лучше глобальная структура
  10. Что когда использовать
  11. Ядерный PCA
  12. Ошибка восстановления
  13. Соберите сами
  14. Шаг 1: PCA с нуля
  15. Шаг 2: тест на синтетических данных
  16. Шаг 3: цифры MNIST в 2D
  17. Шаг 4: сравнение со sklearn
  18. Шаг 5: сравнение с UMAP
  19. Примените
  20. Подготовьте к использованию
  21. Упражнения
  22. Ключевые термины
  23. Дополнительные материалы

В высокоразмерных данных есть структура. Её находят, посмотрев под правильным углом.

Тип: Создание Язык: Python Предварительные требования: Фаза 1, уроки 01 («Интуитивное понимание линейной алгебры»), 02 («Векторы, матрицы и операции»), 03 («Собственные значения и собственные векторы»), 06 («Вероятность и распределения») Время: ~90 минут

Цели обучения

  • Реализовать PCA с нуля: центрировать данные, вычислить ковариационную матрицу, выполнить разложение по собственным значениям и спроецировать данные
  • Использовать долю объяснённой дисперсии и метод локтя, чтобы выбрать число главных компонент
  • Сравнить PCA, t-SNE и UMAP для визуализации цифр MNIST в 2D и объяснить их компромиссы
  • Применить ядерный PCA с RBF-ядром, чтобы разделить нелинейные структуры данных, с которыми не справляется стандартный PCA

Проблема

У вас есть набор данных с 784 признаками на образец. Возможно, это значения пикселей рукописных цифр. Возможно, уровни экспрессии генов. Возможно, сигналы поведения пользователей. Вы не можете визуализировать 784 измерения. Не можете их построить на графике. Вы даже не можете о них думать.

Но большинство из этих 784 признаков избыточны. Настоящая информация находится на гораздо меньшей поверхности. Рукописной «7» не нужно 784 независимых числа для описания. Ей достаточно нескольких: угла штриха, длины поперечной линии, наклона. Остальное — шум.

Понижение размерности находит эту меньшую поверхность. Оно берёт ваши 784-мерные данные и сжимает их до 2, 10 или 50 измерений, сохраняя важную структуру.

Концепция

Проклятие размерности

Высокоразмерные пространства неинтуитивны. С ростом числа измерений нарушаются три вещи.

Расстояние теряет смысл. В высокой размерности расстояние между любыми двумя случайными точками сходится к одному и тому же значению. Если каждая точка находится примерно на одинаковом расстоянии от всех остальных, поиск ближайших соседей перестаёт работать.

Dimension    Avg distance ratio (max/min between random points)
2            ~5.0
10           ~1.8
100          ~1.2
1000         ~1.02

Объём концентрируется в углах. Единичный гиперкуб в d измерениях имеет 2^d углов. В 100 измерениях почти весь объём находится в углах, далеко от центра. Точки данных расходятся к краям, а вашим моделям не хватает данных внутри пространства.

Вам нужно экспоненциально больше данных. Чтобы сохранить ту же плотность образцов в пространстве, переход от 2D к 20D означает, что вам потребуется в 10^18 раз больше данных. Их никогда не бывает достаточно. Понижение размерности возвращает плотность данных к пригодному для работы уровню.

PCA: найдите важные направления

Анализ главных компонент (Principal Component Analysis, PCA) находит оси, вдоль которых ваши данные изменяются сильнее всего. Он поворачивает систему координат так, что первая ось захватывает наибольшую дисперсию, вторая — следующую по величине, и так далее.

Алгоритм:

1. Center the data        (subtract the mean from each feature)
2. Compute covariance     (how features move together)
3. Eigendecomposition     (find the principal directions)
4. Sort by eigenvalue     (biggest variance first)
5. Project               (keep top k eigenvectors, drop the rest)

Почему разложение по собственным значениям? Ковариационная матрица симметрична и положительно полуопределена. Её собственные векторы — ортогональные направления в пространстве признаков. Собственные значения показывают, какую дисперсию захватывает каждое направление. Собственный вектор с наибольшим собственным значением указывает вдоль направления максимальной дисперсии.

Диаграмма к уроку «Понижение размерности: PCA, t-SNE, UMAP»

  • До PCA: облако данных распределено по диагонали вдоль обеих осей, x и y
  • После PCA: система координат повёрнута так, что PC1 совпадает с направлением максимальной дисперсии (вытянутый разброс), а PC2 — с направлением минимальной дисперсии (узкий разброс)
  • Понижение размерности: отбросив PC2, вы проецируете данные на PC1 и теряете очень мало информации

Доля объяснённой дисперсии

Каждая главная компонента захватывает часть общей дисперсии. Доля объяснённой дисперсии показывает, какую именно.

Component    Eigenvalue    Explained ratio    Cumulative
PC1          4.73          0.473              0.473
PC2          2.51          0.251              0.724
PC3          1.12          0.112              0.836
PC4          0.89          0.089              0.925
...

Когда накопленная объяснённая дисперсия достигает 0,95, вы знаете, что столько-то компонент захватывают 95% информации. Всё после этого в основном является шумом.

Выбор числа компонент

Три стратегии:

  1. Порог. Оставьте достаточно компонент, чтобы объяснить 90–95% дисперсии.
  2. Метод локтя. Постройте график объяснённой дисперсии для каждой компоненты. Ищите резкое снижение.
  3. Качество последующей задачи. Используйте PCA как предобработку. Переберите k и измерьте точность модели. Лучшее k находится там, где точность выходит на плато.

t-SNE: сохраняйте соседства

t-распределённое стохастическое вложение соседей (t-Distributed Stochastic Neighbor Embedding, t-SNE) предназначено для визуализации. Оно отображает высокоразмерные данные в 2D (или 3D), сохраняя то, какие точки находятся близко друг к другу.

Интуиция такова: в исходном пространстве вычислите распределение вероятностей по парам точек на основе расстояний между ними. Близкие точки получают высокую вероятность. Далёкие точки — низкую. Затем найдите расположение в 2D, в котором сохраняется то же распределение вероятностей. Точки, бывшие соседями в 784 измерениях, остаются соседями в 2D.

Ключевые свойства t-SNE:

  • Нелинейный. Он может развернуть сложные многообразия, с которыми PCA не справляется.
  • Стохастический. Разные запуски дают разные компоновки.
  • Параметр перплексии определяет, сколько соседей учитывать (типичный диапазон: 5–50).
  • Расстояния между кластерами в результате не имеют смысла. Значение имеют только сами кластеры.
  • Медленный на больших наборах данных. По умолчанию O(n^2).

UMAP: быстрее, лучше глобальная структура

Униформная аппроксимация и проекция многообразия (Uniform Manifold Approximation and Projection, UMAP) работает подобно t-SNE, но имеет два преимущества:

  • Быстрее. Он использует графы приближённых ближайших соседей вместо вычисления всех попарных расстояний.
  • Лучше сохраняет глобальную структуру. Относительные положения кластеров в результате обычно имеют больше смысла, чем в t-SNE.

UMAP строит взвешенный граф в высокоразмерном пространстве («нечёткое топологическое представление»), а затем находит низкоразмерную компоновку, которая как можно лучше сохраняет этот граф.

Ключевые параметры:

  • n_neighbors: сколько соседей определяют локальную структуру (аналогично перплексии). Более высокие значения сохраняют больше глобальной структуры.
  • min_dist: насколько плотно точки располагаются в результате. Меньшие значения создают более плотные кластеры.

Что когда использовать

Метод Сценарий использования Сохраняет Скорость
PCA Предобработка перед обучением Глобальную дисперсию Быстро (точно), работает на миллионах образцов
PCA Быстрая исследовательская визуализация Линейную структуру Быстро
t-SNE 2D-графики для публикаций Локальные соседства Медленно (идеально < 10 тыс. образцов)
UMAP Масштабируемая 2D-визуализация Локальную + часть глобальной структуры Средне (обрабатывает миллионы)
PCA Уменьшение признаков для моделей Признаки, ранжированные по дисперсии Быстро
t-SNE / UMAP Понимание структуры кластеров Разделение кластеров От средней до медленной

Практическое правило: используйте PCA для предобработки и сжатия данных. Используйте t-SNE или UMAP, когда нужно визуализировать структуру в 2D.

Ядерный PCA

Стандартный PCA находит линейные подпространства. Он поворачивает систему координат и отбрасывает оси. Но что, если данные лежат на нелинейном многообразии? Круг в 2D нельзя разделить никакой прямой. Стандартный PCA не поможет.

Ядерный PCA применяет PCA в высокоразмерном пространстве признаков, индуцированном ядерной функцией, не вычисляя явно координаты в этом пространстве. Это ядерный трюк — та же идея, что лежит в основе SVM.

Алгоритм:

  1. Вычислите ядерную матрицу K, где K_ij = k(x_i, x_j)
  2. Центрируйте ядерную матрицу в пространстве признаков
  3. Выполните разложение центрированной ядерной матрицы по собственным значениям
  4. Верхние собственные векторы (масштабированные на 1/sqrt(eigenvalue)) являются проекциями

Распространённые ядерные функции:

Ядро Формула Подходит для
RBF (гауссово) exp(-gamma * ||x - y||^2) Большинства нелинейных данных, гладких многообразий
Полиномиальное (x . y + c)^d Полиномиальных зависимостей
Сигмоидальное tanh(alpha * x . y + c) Отображений, похожих на нейронные сети

Когда использовать ядерный PCA вместо стандартного PCA:

Критерий Стандартный PCA Ядерный PCA
Структура данных Линейное подпространство Нелинейное многообразие
Скорость O(min(n^2 d, d^2 n)) O(n^2 d + n^3)
Интерпретируемость Компоненты — линейные комбинации признаков У компонент нет прямой интерпретации через признаки
Масштабируемость Работает на миллионах образцов Ядерная матрица имеет размер n x n, ограничена памятью
Восстановление Прямое обратное преобразование Требует приближения прообраза

Классический пример — концентрические окружности в 2D. Два кольца точек, одно внутри другого. Стандартный PCA проецирует оба на одну и ту же прямую — бесполезно для классификации. Ядерный PCA с RBF-ядром отображает внутреннюю и внешнюю окружности в разные области, делая их линейно разделимыми.

Ошибка восстановления

Насколько хорошо ваше понижение размерности? Вы сжали 784 измерения до 50. Что вы потеряли?

Измерьте ошибку восстановления:

  1. Спроецируйте данные в k измерений: X_reduced = X @ W_k
  2. Восстановите: X_hat = X_reduced @ W_k^T
  3. Вычислите MSE: mean((X - X_hat)^2)

Для PCA ошибка восстановления имеет простую связь с объяснённой дисперсией:

Reconstruction error = sum of eigenvalues NOT included
Total variance = sum of ALL eigenvalues
Fraction lost = (sum of dropped eigenvalues) / (sum of all eigenvalues)

Доля объяснённой дисперсии для каждой компоненты равна:

explained_ratio_k = eigenvalue_k / sum(all eigenvalues)

График накопленной объяснённой дисперсии в зависимости от числа компонент даёт кривую «локтя». Правильное число компонент находится там, где:

  • Кривая выравнивается (убывающая отдача)
  • Накопленная дисперсия пересекает ваш порог (обычно 0,90 или 0,95)
  • Качество последующей задачи выходит на плато

Ошибка восстановления полезна не только для выбора k. Её можно использовать для обнаружения аномалий: образцы с высокой ошибкой восстановления — это выбросы, которые не соответствуют выученному подпространству. На этом основано обнаружение аномалий на базе PCA в производственных системах.

pca-axes

Соберите сами

Шаг 1: PCA с нуля

import numpy as np

class PCA:
    def __init__(self, n_components):
        self.n_components = n_components
        self.components = None
        self.mean = None
        self.eigenvalues = None
        self.explained_variance_ratio_ = None

    def fit(self, X):
        self.mean = np.mean(X, axis=0)
        X_centered = X - self.mean

        cov_matrix = np.cov(X_centered, rowvar=False)

        eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)

        sorted_idx = np.argsort(eigenvalues)[::-1]
        eigenvalues = eigenvalues[sorted_idx]
        eigenvectors = eigenvectors[:, sorted_idx]

        self.components = eigenvectors[:, :self.n_components].T
        self.eigenvalues = eigenvalues[:self.n_components]
        total_var = np.sum(eigenvalues)
        self.explained_variance_ratio_ = self.eigenvalues / total_var

        return self

    def transform(self, X):
        X_centered = X - self.mean
        return X_centered @ self.components.T

    def fit_transform(self, X):
        self.fit(X)
        return self.transform(X)

Шаг 2: тест на синтетических данных

np.random.seed(42)
n_samples = 500

t = np.random.uniform(0, 2 * np.pi, n_samples)
x1 = 3 * np.cos(t) + np.random.normal(0, 0.2, n_samples)
x2 = 3 * np.sin(t) + np.random.normal(0, 0.2, n_samples)
x3 = 0.5 * x1 + 0.3 * x2 + np.random.normal(0, 0.1, n_samples)

X_synthetic = np.column_stack([x1, x2, x3])

pca = PCA(n_components=2)
X_reduced = pca.fit_transform(X_synthetic)

print(f"Original shape: {X_synthetic.shape}")
print(f"Reduced shape:  {X_reduced.shape}")
print(f"Explained variance ratios: {pca.explained_variance_ratio_}")
print(f"Total variance captured: {sum(pca.explained_variance_ratio_):.4f}")

Шаг 3: цифры MNIST в 2D

from sklearn.datasets import fetch_openml

mnist = fetch_openml("mnist_784", version=1, as_frame=False, parser="auto")
X_mnist = mnist.data[:5000].astype(float)
y_mnist = mnist.target[:5000].astype(int)

pca_mnist = PCA(n_components=50)
X_pca50 = pca_mnist.fit_transform(X_mnist)
print(f"50 components capture {sum(pca_mnist.explained_variance_ratio_):.2%} of variance")

pca_2d = PCA(n_components=2)
X_pca2d = pca_2d.fit_transform(X_mnist)
print(f"2 components capture {sum(pca_2d.explained_variance_ratio_):.2%} of variance")

Шаг 4: сравнение со sklearn

from sklearn.decomposition import PCA as SklearnPCA
from sklearn.manifold import TSNE

sklearn_pca = SklearnPCA(n_components=2)
X_sklearn_pca = sklearn_pca.fit_transform(X_mnist)

print(f"\nOur PCA explained variance:     {pca_2d.explained_variance_ratio_}")
print(f"Sklearn PCA explained variance: {sklearn_pca.explained_variance_ratio_}")

diff = np.abs(np.abs(X_pca2d) - np.abs(X_sklearn_pca))
print(f"Max absolute difference: {diff.max():.10f}")

tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X_mnist)
print(f"\nt-SNE output shape: {X_tsne.shape}")

Шаг 5: сравнение с UMAP

try:
    from umap import UMAP

    reducer = UMAP(n_components=2, n_neighbors=15, min_dist=0.1, random_state=42)
    X_umap = reducer.fit_transform(X_mnist)
    print(f"UMAP output shape: {X_umap.shape}")
except ImportError:
    print("Install umap-learn: pip install umap-learn")

Примените

PCA как предобработка перед классификатором:

from sklearn.decomposition import PCA as SklearnPCA
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

X_train, X_test, y_train, y_test = train_test_split(
    X_mnist, y_mnist, test_size=0.2, random_state=42
)

results = {}
for k in [10, 30, 50, 100, 200]:
    pca_k = SklearnPCA(n_components=k)
    X_tr = pca_k.fit_transform(X_train)
    X_te = pca_k.transform(X_test)

    clf = LogisticRegression(max_iter=1000, random_state=42)
    clf.fit(X_tr, y_train)
    acc = accuracy_score(y_test, clf.predict(X_te))
    var_captured = sum(pca_k.explained_variance_ratio_)
    results[k] = (acc, var_captured)
    print(f"k={k:>3d}  accuracy={acc:.4f}  variance={var_captured:.4f}")

Качество выходит на плато задолго до 784 измерений. Это плато — ваша рабочая точка.

Подготовьте к использованию

Этот урок создаёт:

  • outputs/skill-dimensionality-reduction.md — навык выбора подходящей техники понижения размерности для заданной задачи

Упражнения

  1. Измените класс PCA, чтобы он поддерживал inverse_transform. Восстановите цифры MNIST из 10, 50 и 200 компонент. Для каждого варианта выведите ошибку восстановления (среднеквадратическую разность с исходными данными).

  2. Запустите t-SNE на том же подмножестве MNIST со значениями перплексии 5, 30 и 100. Опишите, как меняется результат. Почему перплексия влияет на плотность кластеров?

  3. Возьмите набор данных с 50 признаками, из которых информативны только 5 (создайте его с помощью sklearn.datasets.make_classification). Примените PCA и проверьте, правильно ли кривая объяснённой дисперсии определяет, что данные фактически пятимерны.

Ключевые термины

Термин Как обычно говорят Что это на самом деле означает
Проклятие размерности «Слишком много признаков» С ростом размерности расстояния, объёмы и плотность данных ведут себя неинтуитивно. Моделям нужно экспоненциально больше данных, чтобы это компенсировать.
PCA «Уменьшить размерность» Поверните систему координат так, чтобы оси совпали с направлениями максимальной дисперсии, затем отбросьте оси с низкой дисперсией.
Главная компонента «Важное направление» Собственный вектор ковариационной матрицы. Направление в пространстве признаков, вдоль которого данные изменяются сильнее всего.
Доля объяснённой дисперсии «Сколько информации в этой компоненте» Часть общей дисперсии, захватываемая одной главной компонентой. Сложите верхние k долей, чтобы увидеть, сколько сохраняют k компонент.
Ковариационная матрица «Как признаки коррелируют» Симметричная матрица, в которой элемент (i,j) измеряет, как признак i и признак j изменяются вместе. Элементы диагонали — отдельные дисперсии.
t-SNE «Тот график кластеров» Нелинейный метод, который отображает высокоразмерные данные в 2D, сохраняя вероятности попарных соседств. Хорош для визуализации, а не для предобработки.
UMAP «Быстрый t-SNE» Нелинейный метод на основе топологического анализа данных. Сохраняет и локальную, и часть глобальной структуры. Масштабируется лучше t-SNE.
Перплексия «Ручка t-SNE» Управляет эффективным числом соседей, которое учитывает каждая точка. Низкая перплексия фокусируется на очень локальной структуре. Высокая захватывает более широкие закономерности.
Многообразие «Поверхность, на которой лежат данные» Низкоразмерная поверхность, вложенная в высокоразмерное пространство. Лист бумаги, смятый в 3D, — двумерное многообразие.

Дополнительные материалы


Источник: Dimensionality Reduction — оригинал Навигация: назад: 01.09 — Теория информации: энтропия, KL-дивергенция · Фаза 1 — Математические основы · Полный каталог · следующая статья: 01.11 — Сингулярное разложение.