← Назад к вопросам

Как работает PCA?

1.0 Junior🔥 181 комментариев
#Машинное обучение#Статистика и A/B тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Основная идея PCA

PCA преобразует исходные n признаков в m новых признаков (m < n), сохраняя максимальное количество информации:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris

# Загружаем датасет
iris = load_iris()
X = iris.data  # 150 samples, 4 features
y = iris.target

print(f"Исходная форма: {X.shape}")

# Шаг 1: Нормализация (КРИТИЧНО!)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Шаг 2: Применяем PCA
pca = PCA(n_components=2)  # Сводим к 2 компонентам
X_pca = pca.fit_transform(X_scaled)

print(f"Форма после PCA: {X_pca.shape}")

# Сколько дисперсии объясняют компоненты?
print(f"\nОбъясненная дисперсия каждой компоненты: {pca.explained_variance_ratio_}")
print(f"Всего объясненной дисперсии: {pca.explained_variance_ratio_.sum():.4f}")

Математика PCA

Шаг 1: Центрирование данных

# Вычитаем среднее (пока данные уже нормализованы)
X_centered = X_scaled - X_scaled.mean(axis=0)

Шаг 2: Вычисление матрицы ковариации

# Матрица ковариации показывает, как изменяются признаки вместе
cov_matrix = np.cov(X_centered.T)  # (4, 4) для iris
print(f"Матрица ковариации:\n{cov_matrix}")
print(f"Форма: {cov_matrix.shape}")

Шаг 3: Собственные значения и собственные векторы

PCA находит собственные векторы матрицы ковариации:

# Собственные значения и собственные векторы
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)

# Сортируем по убыванию собственных значений
idx = eigenvalues.argsort()[::-1]
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]

print(f"Собственные значения (дисперсии ПК): {eigenvalues}")
print(f"\nГлавные компоненты (собственные векторы):\n{eigenvectors}")

# Объясненная дисперсия
explained_variance_ratio = eigenvalues / eigenvalues.sum()
print(f"\nОбъясненная дисперсия каждой ПК: {explained_variance_ratio}")
print(f"Кумулятивная: {np.cumsum(explained_variance_ratio)}")

Шаг 4: Проекция на новые оси

# Новые координаты = исходные данные * собственные векторы
X_pca_manual = X_centered @ eigenvectors[:, :2]  # Первые 2 компоненты

print(f"Форма после проекции: {X_pca_manual.shape}")

# Сравниваем с scikit-learn
print(f"Совпадают? {np.allclose(X_pca, X_pca_manual)}")

Визуализация PCA

plt.figure(figsize=(12, 5))

# График 1: PCA проекция
plt.subplot(1, 2, 1)
colors = ["red", "green", "blue"]
labels = ["Setosa", "Versicolor", "Virginica"]
for target, color, label in zip([0, 1, 2], colors, labels):
    indices = y == target
    plt.scatter(X_pca[indices, 0], X_pca[indices, 1], 
                c=color, label=label, s=50, alpha=0.7)
plt.xlabel(f"PC1 ({pca.explained_variance_ratio_[0]:.2%} дисперсии)")
plt.ylabel(f"PC2 ({pca.explained_variance_ratio_[1]:.2%} дисперсии)")
plt.title("PCA проекция Iris датасета")
plt.legend()
plt.grid(True, alpha=0.3)

# График 2: Cumulative explained variance
plt.subplot(1, 2, 2)
pca_full = PCA()
pca_full.fit(X_scaled)
cumsum = np.cumsum(pca_full.explained_variance_ratio_)
plt.plot(range(1, len(cumsum) + 1), cumsum, "bo-")
plt.axhline(y=0.95, color="r", linestyle="--", label="95% дисперсии")
plt.xlabel("Число компонент")
plt.ylabel("Кумулятивная объясненная дисперсия")
plt.title("Выбор числа компонент")
plt.grid(True, alpha=0.3)
plt.legend()

plt.tight_layout()
plt.show()

Практическое применение

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

# Метод 1: Удержать 95% дисперсии
pca_95 = PCA(n_components=0.95)
X_pca_95 = pca_95.fit_transform(X_scaled)
print(f"Компонент для 95% дисперсии: {pca_95.n_components_}")

# Метод 2: Локоть (elbow method)
pca_full = PCA()
pca_full.fit(X_scaled)
cumsum = np.cumsum(pca_full.explained_variance_ratio_)

# Находим точку перелома
differences = np.diff(cumsum)
elbw = np.argmax(np.diff(differences)) + 1  # Приблизительно
print(f"\nЛокоть метод предлагает: {elbw + 1} компонент")

# Метод 3: Kaiser rule (собственные значения > 1)
pca_test = PCA()
pca_test.fit(X_scaled)
print(f"Kaiser rule: {(pca_test.explained_variance_ > 1).sum()} компонент")

2. Интерпретация главных компонент

# Какие исходные признаки важны для каждой ПК?
loadings = pca.components_.T * np.sqrt(pca.explained_variance_)

feature_names = iris.feature_names
plt.figure(figsize=(10, 4))

for i in range(2):
    plt.subplot(1, 2, i + 1)
    plt.barh(feature_names, loadings[:, i])
    plt.xlabel(f"Нагрузка (loading) на ПК{i+1}")
    plt.title(f"Главная компонента {i+1}")
    plt.grid(True, alpha=0.3, axis="x")

plt.tight_layout()
plt.show()

3. Инверсия проекции

# Восстанавливаем исходные данные (с потерей информации)
X_reconstructed = pca.inverse_transform(X_pca)

# Реконструкционная ошибка
reconstruction_error = np.mean((X_scaled - X_reconstructed) ** 2)
print(f"Среднеквадратичная ошибка реконструкции: {reconstruction_error:.4f}")

Когда использовать PCA

Плюсы:

  • Снижение размерности → быстрее обучение
  • Удаление мультиколлинеарности
  • Визуализация высокомерных данных
  • Шумоподавление (сохраняем только значимые компоненты)

Минусы:

  • Потеря интерпретируемости (новые оси — линейные комбинации)
  • Работает только с числовыми признаками
  • Предполагает линейные зависимости (для нелинейности есть t-SNE, UMAP)
# Пример: PCA для классификации
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

pipeline = Pipeline([
    ("scaler", StandardScaler()),
    ("pca", PCA(n_components=0.95)),
    ("classifier", RandomForestClassifier(random_state=42))
])

scores = cross_val_score(pipeline, X, y, cv=5, scoring="accuracy")
print(f"Accuracy: {scores.mean():.4f} +/- {scores.std():.4f}")

Альтернативы PCA

from sklearn.manifold import TSNE
from sklearn.decomposition import FactorAnalysis
from sklearn.preprocessing import KernelCenterer
from sklearn.decomposition import KernelPCA

# t-SNE для визуализации (нелинейная)
tsne = TSNE(n_components=2, random_state=42)
X_tsne = tsne.fit_transform(X_scaled)

# Kernel PCA для нелинейных данных
kpca = KernelPCA(n_components=2, kernel="rbf", random_state=42)
X_kpca = kpca.fit_transform(X_scaled)

# Factor Analysis (учитывает шум в каждом признаке)
fa = FactorAnalysis(n_components=2, random_state=42)
X_fa = fa.fit_transform(X_scaled)

Ключевые выводы

  1. PCA находит новые оси, максимизирующие дисперсию
  2. Работает через собственные значения и вектора матрицы ковариации
  3. ВСЕГДА нормализуйте данные перед PCA
  4. Выбирайте компоненты через 95% дисперсии или Kaiser rule
  5. PCA линейна — для нелинейности используйте t-SNE или UMAP
  6. Теряется интерпретируемость — главные компоненты это комбинации исходных признаков

PCA — базовый навык Data Scientist'а, особенно полезен для работы с высокомерными данными.