← Назад к вопросам
Как работать с корреляцией в случае, если нужно предсказать класспо вещественным признакам?
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) | Менее критично, игнорировать мультиколлинеарность |
Итог
Оптимальный подход:
- Анализ — построить корреляционную матрицу и VIF
- Удаление — избавиться от явных дубликатов (корр > 0.9)
- Feature Selection — выбрать самые информативные признаки
- Регуляризация — использовать Ridge/Elastic Net для линейных моделей
- Сравнение — протестировать несколько подходов на валидации
Главное — мультиколлинеарность проблема не для tree-based моделей, но критична для линейных и интерпретируемых подходов.