Основы машинного обучения
Подготовка данных
Подготовка — ключ к успеху
Модели машинного обучения подобны двигателям: они могут быть невероятно мощными, но качество их работы напрямую зависит от качества «топлива», которым вы их снабжаете. В нашем случае топливо — это данные. Необработанные, «сырые» данные часто содержат ошибки, пропуски и форматы, которые алгоритмы просто не могут понять. Процесс преобразования этих данных в чистый, структурированный вид называется подготовкой данных, и это, возможно, самый важный этап в построении эффективной модели.
Мы будем работать с гипотетическим набором данных о недвижимости. Наша цель — подготовить его для модели, которая будет предсказывать стоимость дома. Для всех операций мы будем использовать две основные библиотеки экосистемы Python: Pandas для манипуляции данными и Scikit-Learn для их предобработки.
Первый взгляд с Pandas
Прежде чем что-то менять, нужно понять, с чем мы имеем дело. Pandas — идеальный инструмент для загрузки и первичного анализа данных. Давайте загрузим наш датасет и посмотрим на его структуру.
import pandas as pd
import numpy as np
# Создаем гипотетический DataFrame
data = {
'Площадь': [120, 150, 90, np.nan, 200, 135],
'Кол-во комнат': [3, 4, 2, 3, 5, 3],
'Район': ['Центр', 'Север', 'Юг', 'Центр', 'Север', np.nan],
'Состояние': ['Отличное', 'Хорошее', 'Требует ремонта', 'Хорошее', 'Отличное', 'Хорошее'],
'Цена': [150000, 210000, 95000, 165000, 250000, 170000]
}
df = pd.DataFrame(data)
# Выводим основную информацию
print('Информация о DataFrame:')
df.info()
print('\nОписательная статистика:')
print(df.describe())
Метод .info() показывает типы данных каждого столбца и количество непропущенных значений. Мы сразу видим пропуски в столбцах «Площадь» и «Район». Метод .describe() дает нам базовую статистику (среднее, стандартное отклонение, медиану и т.д.) только для числовых признаков. Эта информация — наша отправная точка для дальнейшей очистки.
Чистка и преобразование
Теперь, когда у нас есть общая картина, приступим к исправлению проблем. Начнем с пропущенных значений.
Пропущенные значения (NaN) необходимо обработать, потому что большинство алгоритмов машинного обучения не умеют с ними работать и выдадут ошибку.
Самый простой способ — удалить строки с пропусками, но это может привести к потере ценной информации. Лучше заполнить их, используя разумную стратегию. Для числовых признаков, как «Площадь», часто используют среднее или медианное значение. Для категориальных, как «Район», — самое частое значение (моду). В Scikit-Learn для этого есть удобный инструмент — SimpleImputer.
from sklearn.impute import SimpleImputer
# Заполнение пропусков в числовом столбце медианой
num_imputer = SimpleImputer(strategy='median')
df['Площадь'] = num_imputer.fit_transform(df[['Площадь']])
# Заполнение пропусков в категориальном столбце модой
cat_imputer = SimpleImputer(strategy='most_frequent')
df['Район'] = cat_imputer.fit_transform(df[['Район']])
print(df.info())
Отлично, с пропусками разобрались. Следующий шаг — преобразование категориальных признаков в числа. Модели понимают только цифры, поэтому текстовые описания вроде «Центр» или «Отличное» нужно закодировать.
Здесь важно различать два типа признаков:
- Номинальные: Категории, не имеющие внутреннего порядка. Например, «Район» ('Центр', 'Север', 'Юг'). Нельзя сказать, что 'Север' > 'Центр'.
- Порядковые: Категории с четко определенным порядком. Например, «Состояние» ('Требует ремонта' < 'Хорошее' < 'Отличное').
Для номинальных признаков идеально подходит OneHotEncoder, который создает новый бинарный столбец для каждой категории. Для порядковых — OrdinalEncoder, который присваивает каждой категории целое число в соответствии с ее порядком.
from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder
# One-Hot кодирование для номинального признака 'Район'
ohe = OneHotEncoder(sparse_output=False, handle_unknown='ignore')
area_encoded = ohe.fit_transform(df[['Район']])
area_df = pd.DataFrame(area_encoded, columns=ohe.get_feature_names_out(['Район']))
# Ordinal кодирование для порядкового признака 'Состояние'
condition_order = ['Требует ремонта', 'Хорошее', 'Отличное']
ord_encoder = OrdinalEncoder(categories=[condition_order])
df['Состояние_кодированное'] = ord_encoder.fit_transform(df[['Состояние']])
# Объединяем результаты и удаляем исходные столбцы
df_processed = pd.concat([df.drop(['Район', 'Состояние'], axis=1), area_df], axis=1)
print(df_processed.head())
Разделение и масштабирование
Перед тем как обучать модель, абсолютно необходимо разделить данные на обучающую и тестовую выборки. Обучающая выборка используется для «тренировки» модели, а тестовая — для независимой оценки ее качества на данных, которые она никогда не видела. Это помогает убедиться, что модель не просто запомнила ответы, а действительно научилась находить закономерности.
Этот шаг предотвращает так называемую — одну из самых коварных ошибок в машинном обучении.
В Scikit-Learn это делается одной строкой с помощью train_test_split.
from sklearn.model_selection import train_test_split
X = df_processed.drop('Цена', axis=1)
y = df_processed['Цена']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)
Последний шаг — масштабирование признаков. Такие признаки, как «Площадь» (значения от 90 до 200) и «Кол-во комнат» (от 2 до 5), имеют совершенно разные масштабы. Для алгоритмов, основанных на расстоянии (например, k-NN или SVM), это может стать проблемой: признак с большим диапазоном значений будет оказывать непропорционально большое влияние на результат. Чтобы это исправить, признаки приводят к одному масштабу.
| Метод | Описание | Когда использовать |
|---|---|---|
| Стандартизация (StandardScaler) | Преобразует данные так, чтобы их среднее было равно 0, а стандартное отклонение — 1. | Наиболее распространенный метод. Хорошо работает, когда данные имеют распределение, близкое к нормальному. |
| Нормализация (MinMaxScaler) | Масштабирует данные в заданный диапазон, обычно [0, 1]. | Полезно, когда алгоритм требует данных в определенном диапазоне, или когда распределение не является нормальным. Чувствителен к выбросам. |
Важно: скалер (например, StandardScaler) нужно обучать (fit) только на обучающей выборке, а затем применять (transform) и к обучающей, и к тестовой.
from sklearn.preprocessing import StandardScaler
# Выбираем только числовые столбцы для масштабирования
numeric_features = ['Площадь', 'Кол-во комнат']
scaler = StandardScaler()
# Обучаем скалер на обучающих данных и трансформируем их
X_train[numeric_features] = scaler.fit_transform(X_train[numeric_features])
# Применяем тот же скалер к тестовым данным
X_test[numeric_features] = scaler.transform(X_test[numeric_features])
print('Масштабированная обучающая выборка:')
print(X_train.head())
Теперь наши данные полностью готовы. Они очищены, закодированы, разделены и отмасштабированы. Этот подготовленный набор можно смело подавать на вход любой модели машинного обучения.
Какова основная цель разделения данных на обучающую и тестовую выборки?
У вас есть признак «Состояние» со значениями «Требует ремонта», «Хорошее» и «Отличное». Какой тип кодировщика из Scikit-Learn лучше всего подходит для преобразования этого признака в числовой формат?
