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

Как определить процент потерянной дисперсии при использовании метода PCA?

1.0 Junior🔥 121 комментариев
#Статистика и A/B тестирование

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

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

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

Определение потери дисперсии при использовании PCA

PCA (Principal Component Analysis) — мощный метод снижения размерности, но он неизбежно приводит к потере информации. Правильное определение этой потери критично для выбора оптимального количества компонент. Расскажу о практических методах, которые использовал в различных проектах.

1. Основная концепция: Explained Variance Ratio

Каждый главный компонент объясняет определённый процент дисперсии данных. Потеря дисперсии = 100% минус объяснённая дисперсия.

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

# Загружаем данные
X = load_iris().data
print(f"Оригинальная размерность: {X.shape[1]}")

# Применяем PCA со всеми компонентами
pca_full = PCA()
pca_full.fit(X)

# Explained Variance Ratio
explained_variance = pca_full.explained_variance_ratio_
cumulative_variance = np.cumsum(explained_variance)

print("\nДисперсия, объяснённая каждым компонентом:")
for i, var in enumerate(explained_variance):
    print(f"PC{i+1}: {var:.4f} ({var*100:.2f}%)")

print("\nКумулятивная объяснённая дисперсия:")
for i, cum_var in enumerate(cumulative_variance):
    print(f"PC1-PC{i+1}: {cum_var:.4f} ({cum_var*100:.2f}%)")

# Потеря дисперсии
print(f"\nПотеря при использовании 2 компонент: {(1 - cumulative_variance[1])*100:.2f}%")

2. График "Scree Plot" для визуализации потери

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# График 1: Индивидуальная дисперсия
axes[0].bar(range(1, len(explained_variance) + 1), explained_variance, alpha=0.7, color='steelblue')
axes[0].plot(range(1, len(explained_variance) + 1), explained_variance, 'ro-', linewidth=2)
axes[0].set_xlabel('Номер компонента')
axes[0].set_ylabel('Объяснённая дисперсия')
axes[0].set_title('Scree Plot: Дисперсия по компонентам')
axes[0].grid(True, alpha=0.3)

# График 2: Кумулятивная дисперсия
axes[1].plot(range(1, len(cumulative_variance) + 1), cumulative_variance, 'bo-', linewidth=2, markersize=8)
axes[1].axhline(y=0.95, color='r', linestyle='--', label='95% порог')
axes[1].axhline(y=0.90, color='orange', linestyle='--', label='90% порог')
axes[1].set_xlabel('Количество компонент')
axes[1].set_ylabel('Кумулятивная объяснённая дисперсия')
axes[1].set_title('Кумулятивная дисперсия')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# Определение количества компонент для 95% дисперсии
n_components_95 = np.argmax(cumulative_variance >= 0.95) + 1
print(f"\nКоличество компонент для 95% дисперсии: {n_components_95}")
print(f"Потеря дисперсии: {(1 - cumulative_variance[n_components_95 - 1])*100:.2f}%")

3. Методы выбора оптимального числа компонент

Метод 1: Пороговое значение (90%, 95%, 99%)

def find_n_components(pca, threshold=0.95):
    """
    Находит количество компонент, необходимых для объяснения threshold% дисперсии
    """
    cumulative_var = np.cumsum(pca.explained_variance_ratio_)
    n_components = np.argmax(cumulative_var >= threshold) + 1
    actual_variance_retained = cumulative_var[n_components - 1]
    variance_lost = 1 - actual_variance_retained
    
    return n_components, actual_variance_retained, variance_lost

n_comp_90, var_90, loss_90 = find_n_components(pca_full, threshold=0.90)
n_comp_95, var_95, loss_95 = find_n_components(pca_full, threshold=0.95)
n_comp_99, var_99, loss_99 = find_n_components(pca_full, threshold=0.99)

print(f"90% дисперсии: {n_comp_90} компонент, потеря {loss_90*100:.2f}%")
print(f"95% дисперсии: {n_comp_95} компонент, потеря {loss_95*100:.2f}%")
print(f"99% дисперсии: {n_comp_99} компонент, потеря {loss_99*100:.2f}%")

Метод 2: Elbow Method (метод локтя)

# Находим "локоть" — точку, где прирост дисперсии замедляется
differences = np.diff(explained_variance)
print("\nПримерный прирост дисперсии между компонентами:")
for i, diff in enumerate(differences):
    print(f"PC{i+1} → PC{i+2}: {diff:.4f}")

# Визуально найти локоть
plt.figure(figsize=(10, 6))
plt.plot(range(1, len(explained_variance) + 1), explained_variance, 'o-', linewidth=2, markersize=8)
plt.xlabel('Номер компонента')
plt.ylabel('Объяснённая дисперсия')
plt.title('Метод локтя: где кривая замедляет рост?')
plt.grid(True, alpha=0.3)
plt.show()

# Автоматический поиск локтя
elbows = []
for i in range(1, len(differences)):
    if differences[i-1] - differences[i] > 0.02:  # Примерный порог
        elbows.append(i+1)
print(f"\nПредполагаемый локоть на компоненте: {elbows[0] if elbows else 'не найден'}")

Метод 3: Kaiser Criterion (собственные значения > 1)

# Для нормализованных данных
eigenvalues = pca_full.explained_variance_
kaisier_n_components = np.sum(eigenvalues > 1)

print(f"\nПо критерию Кайзера (eigenvalue > 1): {kaisier_n_components} компонент")

plt.figure(figsize=(10, 6))
plt.plot(range(1, len(eigenvalues) + 1), eigenvalues, 'o-', linewidth=2, markersize=8)
plt.axhline(y=1, color='r', linestyle='--', label='Kaiser Criterion (λ=1)')
plt.xlabel('Номер компонента')
plt.ylabel('Собственное значение')
plt.title('Критерий Кайзера')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

4. Практический пример: Полный анализ потери информации

from sklearn.datasets import load_digits
from sklearn.preprocessing import StandardScaler

# Загружаем данные
X = load_digits().data
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# PCA со всеми компонентами
pca = PCA()
pca.fit(X_scaled)

# Анализ потери информации
print("===== АНАЛИЗ ПОТЕРИ ИНФОРМАЦИИ =====")
print(f"Оригинальная размерность: {X.shape[1]}")
print(f"Количество выборок: {X.shape[0]}")

# Сценарии использования
scenarios = [
    ("Максимальное сжатие (50% дисперсии)", 0.50),
    ("Среднее сжатие (80% дисперсии)", 0.80),
    ("Минимальное сжатие (95% дисперсии)", 0.95),
    ("Практически без потерь (99% дисперсии)", 0.99),
]

for scenario_name, threshold in scenarios:
    n_comp, var_retained, var_lost = find_n_components(pca, threshold)
    print(f"\n{scenario_name}:")
    print(f"  Компонент: {n_comp} / {pca.n_components_}")
    print(f"  Объяснённая дисперсия: {var_retained*100:.2f}%")
    print(f"  Потеря дисперсии: {var_lost*100:.2f}%")
    print(f"  Сжатие: {X.shape[1] / n_comp:.1f}x")

5. Связь между потерей дисперсии и производительностью модели

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

# Загружаем данные с классами
X, y = load_digits(return_X_y=True)
X_scaled = StandardScaler().fit_transform(X)

# PCA
pca = PCA()
pca.fit(X_scaled)
cumulative_var = np.cumsum(pca.explained_variance_ratio_)

# Проверяем производительность для разного количества компонент
scores_pca = []
for n_comp in range(1, len(pca.explained_variance_ratio_) + 1, 5):
    pca_temp = PCA(n_components=n_comp)
    X_pca = pca_temp.fit_transform(X_scaled)
    
    rf = RandomForestClassifier(n_estimators=10, random_state=42)
    score = cross_val_score(rf, X_pca, y, cv=3, scoring='accuracy').mean()
    loss = (1 - cumulative_var[n_comp - 1]) * 100
    
    scores_pca.append((n_comp, score, loss))
    print(f"Компонент: {n_comp}, Accuracy: {score:.3f}, Потеря дисперсии: {loss:.2f}%")

# Визуализация
components = [x[0] for x in scores_pca]
accuracies = [x[1] for x in scores_pca]
losses = [x[2] for x in scores_pca]

fig, ax1 = plt.subplots(figsize=(10, 6))
ax1.plot(components, accuracies, 'b-o', linewidth=2, markersize=8, label='Accuracy')
ax1.set_xlabel('Количество компонент')
ax1.set_ylabel('Точность', color='b')
ax1.tick_params(axis='y', labelcolor='b')

ax2 = ax1.twinx()
ax2.plot(components, losses, 'r--o', linewidth=2, markersize=8, label='Потеря дисперсии (%)')
ax2.set_ylabel('Потеря дисперсии (%)', color='r')
ax2.tick_params(axis='y', labelcolor='r')

plt.title('Влияние потери дисперсии на производительность')
fig.tight_layout()
plt.show()

6. Практические рекомендации

  • Для EDA и визуализации: 95% дисперсии — хороший компромисс
  • Для снижения шума: 80-90% позволяет отфильтровать помехи
  • Для максимальной скорости: 50-70% при приемлемом качестве
  • Всегда проверяйте: Как потеря дисперсии влияет на метрику вашей задачи

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

Как определить процент потерянной дисперсии при использовании метода PCA? | PrepBro