Практический фундамент машинного обучения
Инженерия признаков
От сырых данных к ценным признакам
Создание признаков, или инженерия признаков, — это процесс преобразования сырых данных в информативные предикторы, которые улучшают качество модели машинного обучения. Это больше искусство, чем точная наука, но существуют мощные методы, позволяющие систематизировать этот процесс. Вместо того чтобы просто удалять строки с пропусками или применять базовые замены, мы можем использовать более сложные подходы.
Продвинутая обработка пропусков
Когда в данных есть пропуски, замена средним или медианой — это лишь первый шаг. Для более точного восстановления данных существует итеративная импутация. Идея в том, чтобы рассматривать каждый признак с пропусками как целевую переменную, а остальные признаки — как предикторы. Модель обучается на известных значениях и предсказывает пропущенные.
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
import numpy as np
# Пример данных с пропусками
X = np.array([
[1, 2, np.nan],
[3, 4, 5],
[np.nan, 6, 7],
[8, 9, 10]
])
# Создаем объект импутера
imputer = IterativeImputer(max_iter=10, random_state=0)
# Обучаем и трансформируем данные
X_filled = imputer.fit_transform(X)
print(X_filled)
из Scikit-learn автоматизирует этот процесс. Он последовательно проходит по каждому признаку, предсказывая пропущенные значения на основе других, и повторяет этот цикл несколько раз, пока значения не стабилизируются. Этот метод часто дает более точные результаты, чем простая замена, так как учитывает взаимосвязи между признаками.
Другой подход — групповая импутация. Если у вас пропущен возраст клиента, разумнее заполнить его средним возрастом для его города или профессии, а не общим средним по всей выборке. Это требует хорошего понимания данных, но может значительно улучшить качество.
Кодирование категорий и утечка данных
Категориальные признаки с большим количеством уникальных значений (высокой кардинальностью), например, почтовые индексы или ID товаров, создают проблему. Стандартное One-Hot Encoding порождает тысячи новых столбцов, делая модель громоздкой и медленной. Здесь на помощь приходит целевое кодирование, или
Идея проста: каждая категория заменяется средним значением целевой переменной для этой категории. Например, если мы предсказываем отток клиентов, категорию «Город А» можно заменить на средний процент оттока в этом городе. Звучит отлично, но здесь кроется главная опасность: (data leakage).
Если вычислять среднее по всей выборке, а затем использовать его для обучения, модель получит информацию о целевой переменной напрямую. Чтобы этого избежать, всегда вычисляйте значения для кодирования только на обучающей выборке и применяйте их к валидационной и тестовой. Еще надежнее — использовать схемы с кросс-валидацией, где для каждой части (фолда) среднее вычисляется по остальным частям.
Автоматизация конвейеров обработки
В реальных наборах данных часто смешаны числовые и категориальные признаки, и к каждому типу нужно применять свою логику обработки: к числовым — масштабирование и импутацию, к категориальным — кодирование. Объединить эти шаги в единый конвейер помогают инструменты Scikit-learn.
ColumnTransformer — это мощный инструмент, который позволяет применять разные преобразования к разным столбцам датасета. Вы можете указать, что числовые столбцы нужно обработать одним пайплайном (например, импутация + масштабирование), а категориальные — другим (импутация + OneHotEncoder). Все это объединяется в один шаг, который легко встраивается в общий пайплайн модели.
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
# Определяем шаги для числовых признаков
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())])
# Определяем шаги для категориальных признаков
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore'))])
# Создаем препроцессор с ColumnTransformer
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, ['age', 'income']),
('cat', categorical_transformer, ['city', 'gender'])])
# Теперь preprocessor можно встроить в пайплайн с моделью
# model = Pipeline(steps=[('preprocessor', preprocessor),
# ('classifier', LogisticRegression())])
Помимо ColumnTransformer, существует и FeatureUnion. Он применяет несколько преобразователей к одному и тому же набору данных параллельно и объединяет их результаты. Это полезно, когда вы хотите извлечь из одних и тех же данных признаки разного типа, например, посчитать и TF-IDF, и длину текста для текстовых данных.
Создание таких конвейеров не только упорядочивает код, но и является ключевым элементом для предотвращения утечки данных. Все параметры преобразований (например, среднее для импутации, параметры масштабирования) вычисляются строго на обучающих данных (fit_transform) и затем применяются к тестовым (transform), что гарантирует честную оценку модели.
В чем заключается основной принцип работы итеративной импутации для заполнения пропущенных значений?
Какая основная опасность связана с использованием целевого кодирования (Target Encoding)?
Грамотная инженерия признаков — это то, что часто отличает хорошую модель от великолепной. Автоматизация этих шагов с помощью пайплайнов позволяет не только улучшить качество предсказаний, но и сделать процесс моделирования надежным и воспроизводимым.
