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

Как работать с корреляцией в случае, если нужно предсказать класспо вещественным признакам?

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

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

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

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

Работа с корреляцией при классификации по вещественным признакам

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

Фаза 1: Анализ корреляционной матрицы

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import spearmanr, pearsonr

# Корреляция признаков между собой
corr_matrix = X_train.corr(method='pearson')

# Визуализация
plt.figure(figsize=(12, 10))
sns.heatmap(corr_matrix, annot=True, fmt='.2f', cmap='coolwarm', center=0, 
            square=True, cbar_kws={'label': 'Correlation'})
plt.title('Feature Correlation Matrix')
plt.tight_layout()
plt.savefig('correlation_matrix.png', dpi=300)
plt.show()

# Поиск пар с высокой корреляцией (> 0.7)
high_corr_pairs = []
for i in range(len(corr_matrix.columns)):
    for j in range(i+1, len(corr_matrix.columns)):
        if abs(corr_matrix.iloc[i, j]) > 0.7:
            high_corr_pairs.append({
                'feature1': corr_matrix.columns[i],
                'feature2': corr_matrix.columns[j],
                'correlation': corr_matrix.iloc[i, j]
            })

print("\n=== High Correlation Pairs (> 0.7) ===")
for pair in sorted(high_corr_pairs, key=lambda x: abs(x['correlation']), reverse=True):
    print(f"{pair['feature1']} <-> {pair['feature2']}: {pair['correlation']:.4f}")

Фаза 2: Корреляция признаков с целевой переменной

# Корреляция каждого признака с целевой переменной
target_corr = pd.DataFrame({
    'feature': X_train.columns,
    'correlation_with_target': [X_train[col].corr(y_train) for col in X_train.columns]
}).sort_values('correlation_with_target', ascending=False, key=abs)

print("\n=== Correlation with Target ===")
print(target_corr)

# Point-biserial correlation для классификации (если классов 2)
from scipy.stats import pointbiserialr

if len(np.unique(y_train)) == 2:
    print("\n=== Point-Biserial Correlation (специально для классификации) ===")
    for col in X_train.columns:
        corr, p_value = pointbiserialr(y_train, X_train[col])
        print(f"{col}: {corr:.4f} (p-value: {p_value:.4e})")

Фаза 3: Обработка мультиколлинеарности (VIF)

from statsmodels.stats.outliers_influence import variance_inflation_factor

# VIF показывает, насколько признак линейно зависит от других
vif_data = pd.DataFrame()
vif_data['feature'] = X_train.columns
vif_data['VIF'] = [variance_inflation_factor(X_train.values, i) 
                   for i in range(X_train.shape[1])]

print("\n=== Variance Inflation Factor ===")
print(vif_data.sort_values('VIF', ascending=False))

# VIF > 10 — сильная мультиколлинеарность
# VIF > 5 — умеренная, требует внимания
high_vif = vif_data[vif_data['VIF'] > 5]
if not high_vif.empty:
    print("\nWARNING: Признаки с высокой мультиколлинеарностью:")
    print(high_vif)

Фаза 4: Стратегии обработки

Стратегия 1: Удаление дублирующихся признаков

# Удаляем один из пары сильно коррелированных признаков
features_to_drop = []

for pair in high_corr_pairs:
    if pair['feature1'] not in features_to_drop and pair['feature2'] not in features_to_drop:
        # Оставляем признак с большей корреляцией с целевой переменной
        corr1 = abs(X_train[pair['feature1']].corr(y_train))
        corr2 = abs(X_train[pair['feature2']].corr(y_train))
        
        if corr1 > corr2:
            features_to_drop.append(pair['feature2'])
        else:
            features_to_drop.append(pair['feature1'])

X_train_reduced = X_train.drop(columns=features_to_drop)
X_val_reduced = X_val.drop(columns=features_to_drop)
X_test_reduced = X_test.drop(columns=features_to_drop)

print(f"\nДалось удалить {len(features_to_drop)} признаков: {features_to_drop}")
print(f"Осталось {X_train_reduced.shape[1]} признаков из {X_train.shape[1]}")

Стратегия 2: PCA (Principal Component Analysis)

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

# Нормализуем данные
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_val_scaled = scaler.transform(X_val)
X_test_scaled = scaler.transform(X_test)

# PCA: сохраняем 95% дисперсии
pca = PCA(n_components=0.95)
X_train_pca = pca.fit_transform(X_train_scaled)
X_val_pca = pca.transform(X_val_scaled)
X_test_pca = pca.transform(X_test_scaled)

print(f"\nИсходных признаков: {X_train_scaled.shape[1]}")
print(f"После PCA: {X_train_pca.shape[1]}")
print(f"Объяснённая дисперсия: {pca.explained_variance_ratio_.sum():.4f}")

# Visualize explained variance
plt.figure(figsize=(10, 5))
plt.plot(np.cumsum(pca.explained_variance_ratio_), 'bo-')
plt.xlabel('Number of Components')
plt.ylabel('Cumulative Explained Variance')
plt.axhline(y=0.95, color='r', linestyle='--', label='95% threshold')
plt.grid(True)
plt.legend()
plt.show()

Стратегия 3: Регуляризация (Ridge/Elastic Net)

from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier

# Логистическая регрессия с L2 регуляризацией (Ridge)
model_ridge = LogisticRegression(
    penalty='l2',
    C=1.0,  # обратный параметр регуляризации (меньше C = больше регуляризация)
    solver='lbfgs',
    max_iter=1000
)

# Elastic Net: комбинация L1 и L2
model_elastic = LogisticRegression(
    penalty='elasticnet',
    C=1.0,
    solver='saga',
    l1_ratio=0.5,  # 0.5 = 50% L1 и 50% L2
    max_iter=1000
)

# Tree-based модели менее чувствительны к мультиколлинеарности
model_gb = GradientBoostingClassifier(
    n_estimators=100,
    learning_rate=0.1,
    max_depth=5
)

model_ridge.fit(X_train, y_train)
model_elastic.fit(X_train, y_train)
model_gb.fit(X_train, y_train)

print("Ridge Validation F1:", f1_score(y_val, model_ridge.predict(X_val), average='weighted'))
print("Elastic Net Validation F1:", f1_score(y_val, model_elastic.predict(X_val), average='weighted'))
print("GradBoost Validation F1:", f1_score(y_val, model_gb.predict(X_val), average='weighted'))

Стратегия 4: Feature Selection (выбор признаков)

from sklearn.feature_selection import SelectKBest, f_classif, mutual_info_classif
from sklearn.ensemble import RandomForestClassifier

# Метод 1: SelectKBest с ANOVA F-test
selector = SelectKBest(score_func=f_classif, k=10)
X_train_selected = selector.fit_transform(X_train, y_train)
selected_features = X_train.columns[selector.get_support()]

print(f"\n=== SelectKBest (k=10) ===")
print(f"Выбранные признаки: {list(selected_features)}")

# Метод 2: Feature importance из Random Forest
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)

feature_importance = pd.DataFrame({
    'feature': X_train.columns,
    'importance': rf.feature_importances_
}).sort_values('importance', ascending=False)

print(f"\n=== Random Forest Feature Importance ===")
print(feature_importance.head(10))

# Берём top k признаков
top_features = feature_importance.head(10)['feature'].tolist()
X_train_top = X_train[top_features]
X_val_top = X_val[top_features]
X_test_top = X_test[top_features]

Фаза 5: Сравнение подходов

from sklearn.metrics import f1_score, roc_auc_score

models = {
    'Original': (model_gb, X_train, X_val, X_test),
    'Reduced (no corr)': (GradientBoostingClassifier(n_estimators=100), X_train_reduced, X_val_reduced, X_test_reduced),
    'PCA': (GradientBoostingClassifier(n_estimators=100), X_train_pca, X_val_pca, X_test_pca),
    'Top 10 Features': (GradientBoostingClassifier(n_estimators=100), X_train_top, X_val_top, X_test_top),
}

results = []
for name, (model, X_tr, X_va, X_te) in models.items():
    model.fit(X_tr, y_train)
    f1 = f1_score(y_val, model.predict(X_va), average='weighted')
    auc = roc_auc_score(y_val, model.predict_proba(X_va)[:, 1], labels=[0, 1])
    results.append({
        'Model': name,
        'F1': f1,
        'AUC': auc,
        'Features': X_tr.shape[1]
    })

results_df = pd.DataFrame(results)
print("\n=== Comparison of Approaches ===")
print(results_df)

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

СитуацияРекомендация
Корреляция < 0.5Игнорировать, оставить признаки
0.5 < Корреляция < 0.7Следить, может быть нормально
Корреляция > 0.7Удалить один из пары или использовать PCA
Много признаков (>100)Использовать Feature Selection
Линейная модель (LR, SVM)Регуляризация + Feature Selection
Tree-based (RF, GB)Менее критично, игнорировать мультиколлинеарность

Итог

Оптимальный подход:

  1. Анализ — построить корреляционную матрицу и VIF
  2. Удаление — избавиться от явных дубликатов (корр > 0.9)
  3. Feature Selection — выбрать самые информативные признаки
  4. Регуляризация — использовать Ridge/Elastic Net для линейных моделей
  5. Сравнение — протестировать несколько подходов на валидации

Главное — мультиколлинеарность проблема не для tree-based моделей, но критична для линейных и интерпретируемых подходов.

Как работать с корреляцией в случае, если нужно предсказать класспо вещественным признакам? | PrepBro