Масштабирование данных — необходимый и прямой процесс в самых простых ситуациях, но в жизни нет ничего по-настоящему простого. В этой статье будет показано, как масштабировать несколько типов данных при использовании перекрестной проверки. Давайте погрузимся!

Давайте сначала начнем с того, что такое масштабирование и зачем нам нужно масштабировать наши данные?

Масштабирование помещает значения наших данных в диапазон либо от 0 до 1, либо от -1 до +1, сохраняя при этом форму распределения. Модели машинного обучения получают множество чисел на основе данных, которые могут иметь совершенно разные единицы измерения.

Давайте возьмем простой пример — скажем, мы сравниваем блюда в ресторане быстрого питания, и у нас есть следующие точки данных для одного приема пищи:

  • Цена = $5,99
  • Калории = 990 ккал
  • Количество предметов = 3
  • Количество заказанных блюд в день (среднее) = 120
  • Дней в меню = 1395

Хотя алгоритмы машинного обучения невероятно умны, они пока не могут сравнить эти числа и расшифровать их вес. Итак, чтобы наша модель работала оптимально, нам нужно придать ей человеческий характер и передать числовые данные, с которыми она может работать.

Различные типы данных

Категорические данные Данные. В нашем примере категориальные данные могут быть типами продуктов в еде, таких как гамбургер, картофель фри и безалкогольный напиток. Мы можем закодировать это так, чтобы каждый из элементов стал своей собственной функцией (столбцом), где 1 означает наличие этого элемента, а 0 означает его отсутствие. Допустим, у нас есть 100 000 строк и 100 категориальных функций — тогда будет довольно сложно пометить их вручную. К счастью, есть несколько способов автоматизировать процесс, например функция Pandas get_dummies и OneHotEncoder от SKLearn.

Порядковые данные — например, группы дохода или возрастные группы, представляют заказ. Возможно, 1-й уровень дохода составляет ‹ 10 000 долларов США, 2-й уровень дохода — от 10 000 до 20 000 долларов США и так далее. Эти типы данных можно обрабатывать несколькими способами, но для простоты сегодняшней темы я решил включить их в категориальные переменные, недостатком которых является потеря чувства порядка.

Непрерывные данные — это числовые значения в наборе данных. В нашем примере все перечисленные выше переменные еды быстрого питания (цена, калории и т. д.) являются непрерывными переменными и должны быть масштабированы, чтобы модель могла выполнять соответствующие сравнения для модели, чтобы делать точные прогнозы.

Перекрестная проверка

Хотя обработка каждого из этих типов данных возможна, при использовании перекрестной проверки это становится затруднительным. Самый простой способ — масштабировать данные перед запуском перекрестной проверки, но на самом деле это ошибка, потому что это допускает утечку данных, поскольку набор проверки увидит обучающие данные до перекрестной проверки. Это означает, что каждая складка в рамках перекрестной проверки должна подбираться и преобразовываться отдельно. Мы хотим, чтобы модель полностью соответствовала обучающим данным и ТОЛЬКО тестировалась на данных проверки/тестирования.

Позвольте представить вам вашего нового лучшего друга!

Преобразователь столбцов SKLearn!

В этом примере я буду определять лучшие веса классов для модели логистической регрессии с использованием перекрестной проверки.

Первым делом нам нужно импортировать необходимые библиотеки:

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import (cross_val_score, GridSearchCV,
                                     StratifiedKFold)
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer

Набор данных, который я использовал, был Набор данных показателей здоровья сердечно-сосудистых заболеваний. Некоторые данные уже были в пригодном для использования формате (0 и 1), в то время как другие данные были числовыми, категориальными и порядковыми. Как указывалось ранее, я решил рассматривать свои порядковые данные как категориальные. Так, например, каждая выборка получала 1, если они попадали в определенную группу доходов, и 0, если не попадали.

Создание преобразователя столбцов (препроцессор)

Числовые функции обозначаются первыми в списке. Затем создается конвейер с использованием SimpleImputer и StandardScaler.

numeric_features = ['MentHlth', 'PhysHlth', 'BMI']
numeric_transformer = Pipeline(
    steps=[('imputer', SimpleImputer(strategy="median")), 
           ('scaler', StandardScaler())])

Затем категориальные функции необходимо преобразовать в числа с помощью OneHotEncoder.

categorical_features = ['Age', 'Education', 'Income', 'GenHlth', 'Diabetes'] 
categorical_transformer = OneHotEncoder(handle_unknown='ignore')

Затем мы можем поместить все это вместе в препроцессор:

preprocessor = ColumnTransformer(transformers=[
    ('num', numeric_transformer, numeric_features),
    ('cat', categorical_transformer, categorical_features)])

Теперь, когда препроцессор определен, мы можем вызывать его каждый раз при запуске модели.

lm = Pipeline(steps=[('preprocessor', preprocessor), ('classifier',    LogisticRegression(solver='liblinear'))])

Наконец, давайте поместим все это в GridSearch, чтобы найти оптимальные веса классов.

# Test different class weights
weights = np.linspace(0.0, 0.99, 100)
param_grid = {'classifier__class_weight': 
              [{0:x, 1:1.0-x} for x in weights]}
lm_grid_search = GridSearchCV(lm, param_grid, k=5, n_jobs=-1)
lm_grid_search.fit(X, y)
print(lm_grid_search.best_params_)

››› {‘classifier__class_weight’: {0: 0,09, 1: 0,91}}

Теперь мы действительно оптимизировали гиперпараметр без утечки данных! Хотя может показаться, что этот код требует довольно много шагов, он определенно лучше ручного масштабирования каждого из типов данных для каждой складки.

Надеюсь, это помогло упростить процесс. Удачного кодирования!

Спасибо за чтение! Пожалуйста, подпишитесь на меня, чтобы посмотреть, как я прохожу учебный курс Data Science Bootcamp в Metis.