</>
Topic 5 of 6

Course materials are currently available in Russian only.

Подготовка признаков

Многие модели чувствительны к тому, в каких числах записаны признаки. Метод ближайших соседей считает расстояние: если доход — сотни тысяч рублей, а возраст — десятки лет, разница в доходе на 1000 рублей «весит» больше, чем разница в 50 лет. А строковые признаки (город, марка машины) модели вообще не принимают — их надо превратить в числа.

Стандартизация: StandardScaler

Для каждого признака вычитаем среднее и делим на стандартное отклонение. После этого у каждого признака среднее 0 и разброс 1 — признаки становятся сравнимыми.

from sklearn.preprocessing import StandardScaler

X = [[150, 50000], [170, 80000], [190, 110000]]   # рост, доход
scaler = StandardScaler()
scaler.fit(X)                 # запоминает среднее и отклонение каждого столбца
print(scaler.mean_)           # [   170. 80000.]
print(scaler.transform(X))    # [[-1.22 -1.22] [0 0] [1.22 1.22]]

fit_transform(X) — то же, что fit(X), а затем transform(X).

Отклонение считается по формуле с делением на n (как numpy.std по умолчанию). Если столбец постоянный, отклонение нулевое — тогда StandardScaler делит на 1, и столбец превращается в нули.

Нормировка: MinMaxScaler

Другой способ — перевести каждый признак в отрезок от 0 до 1: минимум станет 0, максимум — 1.

from sklearn.preprocessing import MinMaxScaler
print(MinMaxScaler().fit_transform([[0], [5], [10]]))   # [[0. ] [0.5] [1. ]]

Утечка данных

Масштабирование — тоже «обучение»: оно запоминает средние и отклонения. Поэтому правило то же, что для модели: fit — только на обучающей выборке, а тестовую выборку и новые данные только transform:

scaler = StandardScaler().fit(X_train)
X_train_s = scaler.transform(X_train)
X_test_s = scaler.transform(X_test)     # те же среднее и отклонение, что у обучающей

Если обучить масштабирование на всех данных до разбиения, сведения о тестовой выборке «протекут» в обучение, и оценка качества окажется нечестной. И наоборот: если забыть преобразовать тестовую выборку, модель получит данные совсем в другом масштабе.

Категориальные признаки: OneHotEncoder

Строковый признак кодируют так: для каждого значения заводят свой столбец из нулей и единиц.

from sklearn.preprocessing import OneHotEncoder

enc = OneHotEncoder(handle_unknown="ignore")
enc.fit([["red"], ["green"], ["red"]])
print(enc.categories_)                               # [array(['green', 'red'], ...)]
print(enc.transform([["red"], ["blue"]]).toarray())  # [[0. 1.] [0. 0.]]
  • fit принимает таблицу — даже для одного признака это список списков;
  • значения в categories_ отсортированы;
  • transform возвращает разреженную матрицу, .toarray() превращает её в обычный массив;
  • без handle_unknown="ignore" незнакомое значение вызывает ошибку, с ним — даёт строку из нулей.

Почему не пронумеровать города числами 1, 2, 3? Тогда модель решит, что «город 3» втрое «больше» города 1, а это бессмыслица.

В pandas похожее делает pd.get_dummies(df["city"]), но он не запоминает список значений обучающей выборки, поэтому для моделей удобнее OneHotEncoder.

Конвейер: Pipeline

Чтобы не следить вручную, где fit, а где transform, шаги объединяют в конвейер:

from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

model = make_pipeline(StandardScaler(), LogisticRegression())
model.fit(X_train, y_train)          # обучает масштабирование и модель на X_train
print(model.score(X_test, y_test))   # масштабирует X_test теми же параметрами и считает accuracy

Конвейер ведёт себя как одна модель: у него есть fit, predict, score. Внутри fit каждый шаг, кроме последнего, делает fit_transform, а последний — fit. При predict все шаги делают только transform. Утечка данных исключена.

Если разные столбцы готовятся по-разному (числа — масштабировать, строки — кодировать), используют ColumnTransformer — он тоже кладётся в конвейер.

Итог

  • StandardScaler — среднее 0, отклонение 1; MinMaxScaler — отрезок [0, 1];
  • fit — только на обучающей выборке, transform — на всех;
  • OneHotEncoder(handle_unknown="ignore") — строковый признак → столбцы из нулей и единиц;
  • make_pipeline(шаг1, …, модель) — подготовка и модель как одно целое.