Шпаргалка scikit-learn: функции для машинного обучения
В этой статье мы рассмотрим 50 наиболее полезных функций, предоставляемых Sci-kitlearn для задач машинного обучения. От предварительной обработки данных до выбора и оценки модели — эти функции охватывают широкий спектр методов и методологий для решения реальных задач. Мы будем использовать готовые наборы данных, чтобы проиллюстрировать применение каждой функции, чтобы вам было легче следовать и применять их в ваших собственных проектах.
Звучит фантастически? А теперь сюрприз: многие из этих функций просты в использовании и требуют для реализации всего несколько строк кода. Независимо от того, являетесь ли вы опытным специалистом по данным или только начинаете, эта памятка поможет вам лучше познакомиться с мощными инструментами, доступными в Sci-kit, и позволит вам ускорить свои проекты по науке о данных и машинному обучению.
Итак, возьмите свой любимый напиток, откиньтесь на спинку кресла и давайте погрузимся в мир Sci-kit Learn!
train_test_split
Эта функция используется для разделения набора данных на наборы для обучения и тестирования. Она принимает набор данных, целевую переменную и размер тестового набора в качестве параметров.
Использование методов fit_transform и transform
Функция score_dataset() возвращает среднюю абсолютную ошибку. Вопрос: Почему для данных обучения мы используем метод fit_transform() , а для тестовых данных просто transform() ? fit_transform() сначала вызывает метод fit потом transform , кого мы обучаем в этом случае? мы же просто создаем датафрейм.
Отслеживать
задан 13 мая 2020 в 15:30
583 6 6 серебряных знаков 19 19 бронзовых знаков
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Если вдруг этот вопрос ещё для кого-то актуален, хотел бы дополнить ответ уважаемого passant.
Вопрос: Почему для данных обучения мы используем метод fit_transform() , а для тестовых данных просто transform() ?
По-хорошему такой методики нужно придерживаться всегда. Обучение любых моделей, включая такие прострые штуки как скейлеры, импутеры, нужно проводить исключительно на тренировочных данных. Модель не должна видеть тестовой и валидационной части данных на этапе обучения. Даже если речь о заполнении пропусков или масштабировании. Любое нарушение этого правила рано или поздно закончится «утечкой данных», в результате чего модель будет показывать на тестовой/валидационной части данных результаты лучше, чем они есть на самом деле.
Ну и кроме того, как уже было упомянуто, даже скейлеры и импутеры обучаются. Скейлер может запомнить минимум и максимум значений каждой фичи, импутер может запомнить, например, медианы фич. Поэтому на тренировочных данных делаем fit_transform — одновременно и обучаемся и преобразуем тренировочные данные. А на тестовых/валидационных данных делаем уже только transform — преобразуем данные согласно ранее запомненным параметрам, полученным на тренировочной выборке.
Как писать преобразователи данных в Sklearn

Сегодня разбираемся, как создавать собственные преобразователи Sklearn, позволяющие интегрировать практически любую функцию или преобразование данных в классы конвейера Sklearn. Подробности под катом к старту флагманского курса по Data Science.
Зачем?
Только один вызов fit , и один — predict — насколько это было бы здорово? Вы получаете данные, обучаете конвейер единожды, и он заботится о предварительной обработке, инжиниринге признаков, моделировании. Всё, что нужно сделать вам, — это вызвать fit .
Какой же конвейер для этого достаточно мощный? В Sklearn много преобразователей, но не для всех ситуаций предварительной обработки. Итак, наш конвейер — несбыточная мечта, или нет? Точно нет.
Что такое конвейеры Sklearn?
Вот простой конвейер, который заполняет пропущенные значения числами, масштабирует их и обучает XGBRegressor на X , y :
from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.impute import SimpleImputer import xgboost as xgb xgb_pipe = make_pipeline( SimpleImputer(strategy='mean'), StandardScaler(), xgb.XGBRegressor() ) _ = xgb_pipe.fit(X, y)
В этом посте я в мельчайших подробностях рассказал о конвейерах Sklearn и об их преимуществах.
Самое заметное преимущество конвейеров — способность объединять все этапы предварительной обработки и моделирования в единственный оценщик, предотвращать утечку данных и не вызывать fit на наборах данных для валидации. А ещё конвейер — это бонус в виде краткого, воспроизводимого, модульного кода.
Но вся эта идея атомарных, аккуратных конвейеров ломается, как только нужно выполнять операции, которые не встроены в Sklearn как функции оценки, например:
- Извлечь шаблоны регулярных выражений для очистки текстовых данных нужно.
- Объединить существующие функции в одну, исходя из знаний предметной области.
Чтобы сохранить все преимущества конвейеров, нужен способ интегрировать в Sklearn пользовательскую предварительную обработку и логику инжиниринга признаков. Здесь и вступают в игру пользовательские преобразователи.
Интеграция простых функций через FunctionTransformer
В сентябрьском конкурсе TPS 2021 на Kaggle одна из идей — добавить количества пропущенных [в сроке данных] значений как новый признак — значительно повысила производительность модели. Эта операция не реализована в Sklearn, поэтому напишем функцию, которая отработает после импорта данных:
tps_df = pd.read_csv("data/train.csv") tps_df.head()

>>> tps_df.shape (957919, 120) >>> # Find the number of missing values across rows >>> tps_df.isnull().sum(axis=1) 0 1 1 0 2 5 3 2 4 8 .. 957914 0 957915 4 957916 0 957917 1 957918 4 Length: 957919, dtype: int64
Эта функция принимает DataFrame и реализует операцию:
def num_missing_row(X: pd.DataFrame, y=None): # Calculate some metrics across rows num_missing = X.isnull().sum(axis=1) num_missing_std = X.isnull().std(axis=1) # Add the above series as a new feature to the df X["#missing"] = num_missing X["num_missing_std"] = num_missing_std return X
Добавим в функцию в конвейер — передадим её в FunctionTransformer :
from sklearn.preprocessing import FunctionTransformer num_missing_estimator = FunctionTransformer(num_missing_row)
При передаче пользовательской функции в FunctionTransformer создаётся оценщик с методами fit , transform и fit_transform :
# Check number of columns before print(f"Number of features before preprocessing: ") # Apply the custom estimator tps_df = num_missing_estimator.transform(tps_df) print(f"Number of features after preprocessing: ") ------------------------------------------------------ Number of features before preprocessing: 120 Number of features after preprocessing: 122
Итак, у нас есть простая функция, поэтому нет необходимости вызывать fit : она просто возвращает нетронутую оценку. Единственное требование FunctionTransformer состоит в том, что передаваемая функция должна принимать данные в своём первом аргументе. При желании, если в функции нужен целевой массив, можно передать и его:
# FunctionTransformer signature def custom_function(X, y=None): . estimator = FunctionTransformer(custom_function) # no errors custom_pipeline = make_pipeline(StandardScaler(), estimator, xgb.XGBRegressor()) custom_pipeline.fit(X, y)
FunctionTransformer также принимает инверсию переданной функции на случай, если понадобится отменить изменения:
def custom_function(X, y=None): . def inverse_of_custom(X, y=None): . estimator = FunctionTransformer(func=custom_function, inverse_func=inverse_of_custom)
Подробности о других аргументах смотрите в документации.
Интеграция сложных шагов предварительной обработки
Один из самых распространённых вариантов масштабирования искажённых данных — это логарифмическое преобразование. Но если функция содержит хотя бы один 0, преобразование с помощью np.log или PowerTransformer вернёт ошибку.
Для обхода особенности особенности участники соревнований Kaggle ко всем образцам данных добавляют 1, и лишь затем применяют преобразование. Если преобразование выполняется в целевом массиве, то потребуется обратное преобразование, для которого после прогнозирования нужно использовать экспоненциальную функцию и вычесть 1:
y_transformed = np.log(y + 1) _ = model.fit(X, y_transformed) preds = np.exp(model.predict(X, y_transformed) - 1)
Работает, но осталась та же проблема — мы не можем включить код в конвейер из коробки. Конечно, можно обратиться к новому другу — FunctionTransformer , но он не подходит для сложных этапов предварительной обработки, таких как этот.
Вместо этого напишем собственный класс преобразователя и создадим функции fit , transform вручную. В конце концов у нас снова будет Sklearn-совместимый оценщик. Начнём:
from sklearn.base import BaseEstimator, TransformerMixin class CustomLogTransformer(BaseEstimator, TransformerMixin): pass
Сначала мы создаём класс, который наследуется от BaseEstimator и TransformerMixin из sklearn.base . Наследование этих классов позволяет конвейерам Sklearn распознавать классы как пользовательские оценщики.
Напишем метод __init__ , где инициализируем экземпляр PowerTransformer :
from sklearn.preprocessing import PowerTransformer class CustomLogTransformer(BaseEstimator, TransformerMixin): def __init__(self): self._estimator = PowerTransformer()
Напишем fit , где добавляем 1 ко всем признакам в данных и обучаем PowerTransformer :
class CustomLogTransformer(BaseEstimator, TransformerMixin): def __init__(self): self._estimator = PowerTransformer() def fit(self, X, y=None): X_copy = np.copy(X) + 1 self._estimator.fit(X_copy) return self
Метод fit должен возвращать сам преобразователь, это делается возвратом self . И проверим то, что мы написали:
custom_log = CustomLogTransformer() >>> custom_log.fit(tps_df) CustomLogTransformer()
Пока работает как положено.
У нас есть transform , где после добавления 1 к переданным данным вызывается transform из класса PowerTransformer :
class CustomLogTransformer(BaseEstimator, TransformerMixin): def __init__(self): self._estimator = PowerTransformer() def fit(self, X, y=None): X_copy = np.copy(X) + 1 self._estimator.fit(X_copy) return self def transform(self, X): X_copy = np.copy(X) + 1 return self._estimator.transform(X_copy)
Проверим его по-другому:
custom_log = CustomLogTransformer() custom_log.fit(tps_df) transformed_tps = custom_log.transform(tps_df) >>> transformed_tps[:5, :5] array([[ 0.48908946, -2.17126787, -1.79124946, -0.52828469, nan], [ 0.38660665, -0.29384644, 1.31313666, 0.1901713 , -0.34236997], [-0.04286469, -0.05047097, -1.16463754, 0.95459266, 1.71830766], [-0.584329 , -1.5743182 , -1.02444525, -0.15117546, 0.46952437], [-0.87027925, -0.13045462, -0.10489176, -0.36806683, 1.21317668]])
Работает как надо. Как я уже говорил, нам нужен метод отмены преобразования:
class CustomLogTransformer(BaseEstimator, TransformerMixin): def __init__(self): self._estimator = PowerTransformer() def fit(self, X, y=None): X_copy = np.copy(X) + 1 self._estimator.fit(X_copy) return self def transform(self, X): X_copy = np.copy(X) + 1 return self._estimator.transform(X_copy) def inverse_transform(self, X): X_reversed = self._estimator.inverse_transform(np.copy(X)) return X_reversed - 1
Вместо inverse_transform можно было воспользоваться np.exp . Теперь проведём окончательную проверку:
custom_log = CustomLogTransformer() tps_transformed = custom_log.fit_transform(tps_df) tps_inversed = custom_log.inverse_transform(tps_transformed)
Но подождите! Мы не писали _fit_transform_ — откуда она взялась?
Это просто — когда вы наследуетесь от _BaseEstimator_ и _TransformerMixin_ , то метод _fit_transform_ получаете просто так.
После обратного преобразования можно сравнить его с исходными данными:
>>> tps_df.values[:5, 5] array([0.35275, 0.17725, 0.25997, 0.4293 , 0.34079]) >>> tps_inversed[:5, 5] array([0.35275, 0.17725, 0.25997, 0.4293 , 0.34079])
Теперь у нас есть собственный преобразователь. Давайте соберём весь код воедино:
from sklearn.metrics import roc_auc_score from sklearn.model_selection import train_test_split from sklearn.pipeline import make_pipeline xgb_pipe = make_pipeline( FunctionTransformer(num_missing_row), SimpleImputer(strategy="constant", fill_value=-99999), CustomLogTransformer(), xgb.XGBClassifier( n_estimators=1000, tree_method="gpu_hist", objective="binary:logistic" ), ) X, y = tps_df.drop("claim", axis=1), tps_df[["claim"]].values.flatten() split = train_test_split(X, y, test_size=0.33, random_state=1121218) X_train, X_test, y_train, y_test = split xgb_pipe.fit(X_train, y_train) preds = xgb_pipe.predict_proba(X_test) >>> roc_auc_score(y_test, preds[:, 1]) 0.7986831816726399
Несмотря на то что преобразование нанесло вред модели, мы заставили наш конвейер работать!
Говоря коротко, сигнатура пользовательского класса преобразователя должна быть такой:
class CustomTransformer(BaseEstimator, TransformerMixin): def __init__(self): pass def fit(self): pass def transform(self): pass def inverse_transform(self): pass
Так вы получаете fit_transform без всяких усилий. Если не нужны методы __init__ , fit , transform или inverse_transform , не используйте их, родительские классы Sklearn позаботятся обо всём. Логика этих методов полностью зависит от ваших нужд.
А пока вы осваиваете преобразования в Sklearn, мы поможем вам прокачать навыки или с самого начала освоить профессию, востребованную в любое время:
- Профессия Data Scientist
- Профессия Fullstack-разработчик на Python
StandardScaler в машинном обучении
В машинном обучении StandardScaler (стандартный масштабатор) используется для изменения размера распределения значений так, чтобы среднее значение наблюдаемых значений было равно 0, а стандартное отклонение – 1. В этой статье я расскажу, как использовать StandardScaler в машинном обучении.
StandardScaler – это важный метод, который в основном выполняется как этап предварительной обработки перед многими моделями машинного обучения, чтобы стандартизировать диапазон функциональных возможностей входного набора данных.
Некоторые практики машинного обучения склонны слепо стандартизировать свои данные перед каждой моделью машинного обучения, не прилагая никаких усилий, чтобы понять, почему их следует использовать или даже нужно ли это или нет. Поэтому вам нужно понимать, когда следует использовать StandardScaler для масштабирования данных.
Когда и как использовать StandardScaler?
StandardScaler вступает в игру, когда характеристики входного набора данных сильно различаются между диапазонами или, когда они измеряются в разных единицах измерения.
StandardScaler удаляет среднее значение и масштабирует данные до единичной дисперсии. Однако выбросы оказывают существенное влияние при вычислении эмпирического среднего и стандартного отклонения, что сужает диапазон значений характеристик.
Эти различия в начальных функциях могут вызвать проблемы для многих моделей машинного обучения. Например, для моделей, основанных на вычислении расстояния, если одна из характеристик имеет широкий диапазон значений, расстояние будет регулироваться этой конкретной характеристикой.
Идея StandardScaler заключается в том, что переменные, которые измеряются в разных масштабах, не влияют в равной степени на соответствие модели и обучающую функцию модели и могут в конечном итоге создать систематическую ошибку.
Итак, чтобы справиться с этой потенциальной проблемой, нам необходимо стандартизировать данные (μ = 0, σ = 1), которые обычно используются, прежде чем мы интегрируем их в модель машинного обучения.
Теперь давайте посмотрим, как использовать StandardScaler с помощью Scikit-learn:
from sklearn.preprocessing import StandardScaler import numpy as np # 4 samples/observations and 2 variables/features X = np.array([[0, 0], [1, 0], [0, 1], [1, 1]]) # the scaler object (model) scaler = StandardScaler() # fit and transform the data scaled_data = scaler.fit_transform(X) print(X)
Результат:
[[0, 0],
[1, 0],
[0, 1],
[1, 1]])