Как отсеять лишние признаки при создании модели машинного обучения?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как отсеять лишние признаки при создании ML-модели
Отбор признаков (Feature Selection) — критический этап, который влияет на качество, скорость и интерпретируемость модели. Разберу все основные методы.
Почему нужно отбирать признаки
- Переобучение — лишние признаки добавляют шум
- Производительность — меньше признаков = быстрее обучение и inference
- Интерпретируемость — модель с 10 важными признаками понятнее, чем с 1000
- Затраты на features — если признак дорогой в получении, не включаем его
Метод 1: Filter-based (Статистический анализ)
Удаляем признаки с низкой статистической связью с целевой переменной.
Для классификации — Mutual Information:
from sklearn.feature_selection import mutual_info_classif, SelectKBest
import pandas as pd
# Вычисляем mutual information между каждым признаком и целевой переменной
mi_scores = mutual_info_classif(X, y, random_state=42)
# Выбираем top-10 признаков
selector = SelectKBest(mutual_info_classif, k=10)
X_selected = selector.fit_transform(X, y)
# Смотрим, какие признаки выбрались
selected_features = X.columns[selector.get_support()].tolist()
print(f"Selected features: {selected_features}")
Для регрессии — Корреляция:
# Вычисляем корреляцию с целевой переменной
corr_with_target = X.corr(y).abs()
# Выбираем признаки с корреляцией > 0.1
selected_features = corr_with_target[corr_with_target > 0.1].index.tolist()
# Удаляем мультиколлинеарные признаки (корреляция > 0.95)
corr_matrix = X.corr().abs()
upper_triangle = corr_matrix.where(
np.triu(np.ones(corr_matrix.shape), k=1).astype(bool)
)
to_drop = [col for col in upper_triangle.columns if any(upper_triangle[col] > 0.95)]
X_cleaned = X.drop(columns=to_drop)
Преимущества:
- Быстро
- Не зависит от модели
- Хорошо работает для начального отбора
Недостатки:
- Не учитывает взаимодействие признаков
- Не учитывает нелинейные зависимости
Метод 2: Wrapper-based (Поиск с моделью)
Итеративно выбираем/удаляем признаки на основе качества модели.
Forward Selection (постепенное добавление):
from sklearn.feature_selection import SequentialFeatureSelector
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=100, random_state=42)
# Добавляем признаки по одному
selector = SequentialFeatureSelector(
model,
n_features_to_select=10, # Хотим 10 признаков
direction='forward' # Добавляем
)
X_selected = selector.fit_transform(X, y)
# Лучшие признаки
best_features = X.columns[selector.get_support()].tolist()
print(f"Best features: {best_features}")
Backward Elimination (постепенное удаление):
selector = SequentialFeatureSelector(
model,
n_features_to_select=10,
direction='backward' # Удаляем
)
X_selected = selector.fit_transform(X, y)
Рекурсивное исключение (RFE):
from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression
model = LinearRegression()
# Рекурсивно удаляем самые неважные признаки
rfe = RFE(estimator=model, n_features_to_select=10, step=1)
X_selected = rfe.fit_transform(X, y)
# Ранг важности
print(f"Feature ranking: {rfe.ranking_}")
selected_features = X.columns[rfe.support_].tolist()
Преимущества:
- Учитывает взаимодействие признаков
- Работает лучше, чем filter методы
Недостатки:
- Медленно (нужно переобучать модель много раз)
- Зависит от выбранной модели
Метод 3: Embedded (Встроенный отбор в модель)
Сама модель во время обучения выясняет важность признаков.
Tree-based importance (Decision Tree, Random Forest, XGBoost):
from sklearn.ensemble import RandomForestClassifier
import numpy as np
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X, y)
# Важность признаков
feature_importance = model.feature_importances_
# Выбираем top-15
top_15_idx = np.argsort(feature_importance)[-15:][::-1]
best_features = X.columns[top_15_idx].tolist()
# Визуализация
import matplotlib.pyplot as plt
plt.barh(X.columns[top_15_idx], feature_importance[top_15_idx])
plt.xlabel('Importance')
plt.show()
Коэффициенты линейной модели:
from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
# Нормализуем признаки (важно для Lasso)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Lasso обнуляет малозначимые коэффициенты
lasso = Lasso(alpha=0.01) # Увеличиваем alpha -> больше нулевых коэффициентов
lasso.fit(X_scaled, y)
# Признаки с ненулевыми коэффициентами
selected_features = X.columns[lasso.coef_ != 0].tolist()
print(f"Selected features: {selected_features}")
print(f"Number of features: {len(selected_features)}")
Permutation Importance (для любой модели):
from sklearn.inspection import permutation_importance
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)
# Перемешиваем каждый признак и смотрим, насколько упадет качество
result = permutation_importance(
model, X_test, y_test,
n_repeats=10,
random_state=42
)
# Признаки с низкой важностью можно удалить
importances = result.importances_mean
selected_idx = np.where(importances > 0.01)[0]
selected_features = X.columns[selected_idx].tolist()
Метод 4: L1-регуляризация (Sparse models)
from sklearn.linear_model import LogisticRegression
# L1 регуляризация обнуляет малозначимые коэффициенты
model = LogisticRegression(
penalty='l1',
solver='liblinear',
C=0.1 # Меньше C = сильнее регуляризация = больше нулей
)
model.fit(X, y)
# Выбираем признаки с ненулевыми коэффициентами
selected_features = X.columns[model.coef_[0] != 0].tolist()
print(f"Selected {len(selected_features)} features")
Практический пример: Комбинированный подход
from sklearn.feature_selection import (
SelectKBest, f_classif,
SequentialFeatureSelector
)
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
# Шаг 1: Filter — быстрый отбор top-30
filter_selector = SelectKBest(f_classif, k=30)
X_filtered = filter_selector.fit_transform(X, y)
filtered_features = X.columns[filter_selector.get_support()].tolist()
X_filtered = X[filtered_features]
# Шаг 2: Tree importance
rf = RandomForestClassifier(n_estimators=100)
rf.fit(X_filtered, y)
importance_threshold = np.percentile(rf.feature_importances_, 50)
tree_selected_idx = np.where(rf.feature_importances_ > importance_threshold)[0]
X_tree_selected = X_filtered.iloc[:, tree_selected_idx]
# Шаг 3: Forward selection для финализации
model = RandomForestClassifier(n_estimators=100)
selector = SequentialFeatureSelector(
model,
n_features_to_select=15,
direction='forward'
)
X_final = selector.fit_transform(X_tree_selected, y)
final_features = X_tree_selected.columns[selector.get_support()].tolist()
print(f"Final selected features: {final_features}")
Когда использовать какой метод
| Метод | Когда использовать | Плюсы | Минусы |
|---|---|---|---|
| Filter | Большой датасет, быстрый отбор | Быстро, просто | Не учитывает модель |
| Wrapper | Средний датасет, нужна точность | Хорошее качество | Медленно, переобучение |
| Embedded | Любой размер | Эффективно, быстро | Зависит от модели |
| L1 регуляризация | Линейные модели | Встроено в обучение | Только для линейных |
Итоговый чеклист
- Удалить очевидный мусор: null/constant признаки
- Анализ корреляции: удалить мультиколлинеарные
- Статистический отбор: SelectKBest с mutual_info
- Модель-зависимый отбор: feature_importances_
- Валидация: проверить качество на test set
- Итеративное уточнение: если нужно лучше