6.1. Пайплайны и составные оценщики ¶
scikit-learn предоставляет библиотеку преобразователей, которые могут очищать (см. Предварительная обработка данных ), уменьшать (см. Неконтролируемое уменьшение размерности ), расширять (см. Аппроксимация ядра ) или генерировать (см. Извлечение функций ) представления функций.
Как и другие оценщики, они представлены классами с fit
методом, который изучает параметры модели (например, среднее значение и стандартное отклонение для нормализации) из обучающего набора, и transform
методом, который применяет эту модель преобразования к невидимым данным. fit_transform
может быть более удобным и эффективным для одновременного моделирования и преобразования обучающих данных.
Объединение таких трансформаторов в параллельном или последовательном режиме рассматривается в разделе «Пайплайны и составные оценщики» . Парные метрики, сходства и ядра охватывают преобразование пространств признаков в матрицы сходства, в то время как преобразование цели прогнозирования (y) рассматривает преобразования целевого пространства (например, категориальные метки) для использования в scikit-learn.
Преобразователи обычно комбинируются с классификаторами, регрессорами или другими оценщиками для построения составного оценщика. Самый распространенный инструмент — пайплайн (конвейер). Конвейер часто используется в сочетании с FeatureUnion, который объединяет выходные данные преобразователей в составное пространство функций. TransformedTargetRegressor занимается преобразованием цели (т. Е. Логарифмическим преобразованием y ). Напротив, конвейеры преобразуют только наблюдаемые данные ( X ).
6.1.1. Конвейер: объединение оценок
Pipeline
можно использовать для объединения нескольких оценщиков в одну. Это полезно, поскольку часто существует фиксированная последовательность шагов при обработке данных, например, выбор функций, нормализация и классификация. Pipeline
здесь служит нескольким целям:Удобство и герметичность
Вам нужно только один раз вызвать fit и predict свои данные, чтобы они соответствовали всей последовательности оценщиков.Совместный выбор параметров
Вы можете выполнять поиск по сетке сразу по параметрам всех оценщиков в конвейере.Безопасность
Конвейеры помогают избежать утечки статистики из ваших тестовых данных в обученную модель при перекрестной проверке, гарантируя, что одни и те же образцы используются для обучения преобразователей и предикторов.
Все оценщики в конвейере, кроме последнего, должны быть преобразователями (т. Е. Должны иметь метод transform ). Последний оценщик может быть любого типа (преобразователь, классификатор и т. д.).
6.1.1.1. Использование
6.1.1.1.1. Строительство
Построен Pipeline
с использованием списка (key, value) пар, где key это строка , содержащая имя , которое вы хотите дать этот шаг и value является объектом оценки:
>>> from sklearn.pipeline import Pipeline >>> from sklearn.svm import SVC >>> from sklearn.decomposition import PCA >>> estimators = [('reduce_dim', PCA()), ('clf', SVC())] >>> pipe = Pipeline(estimators) >>> pipe Pipeline(steps=[('reduce_dim', PCA()), ('clf', SVC())])
Функция полезности make_pipeline
— это сокращение для построения конвейеров; он принимает переменное количество оценщиков и возвращает конвейер, автоматически заполняя имена:
>>> from sklearn.pipeline import make_pipeline >>> from sklearn.naive_bayes import MultinomialNB >>> from sklearn.preprocessing import Binarizer >>> make_pipeline(Binarizer(), MultinomialNB()) Pipeline(steps=[('binarizer', Binarizer()), ('multinomialnb', MultinomialNB())])
6.1.1.1.2. Доступ к шагам
Оценщики конвейера хранятся в виде списка в steps
атрибуте, но могут быть доступны по индексу или имени путем индексации (с [idx]
) конвейера:
>>> pipe.steps[0] ('reduce_dim', PCA()) >>> pipe[0] PCA() >>> pipe['reduce_dim'] PCA()
Атрибут named_steps
конвейера позволяет получить доступ к шагам по имени с завершением табуляции в интерактивных средах:
>>> pipe.named_steps.reduce_dim is pipe['reduce_dim'] True
Подконвейер также может быть извлечен с использованием нотации срезов, обычно используемой для последовательностей Python, таких как списки или строки (хотя разрешен только шаг 1). Это удобно для выполнения только некоторых преобразований (или их обратных):
>>> pipe[:1] Pipeline(steps=[('reduce_dim', PCA())]) >>> pipe[-1:] Pipeline(steps=[('clf', SVC())])
6.1.1.1.3. Вложенные параметры
Доступ к параметрам оценщиков в конвейере можно получить с помощью <estimator>__<parameter>
синтаксиса:
>>> pipe.set_params(clf__C=10) Pipeline(steps=[('reduce_dim', PCA()), ('clf', SVC(C=10))])
Это особенно важно для поиска по сетке:
>>> from sklearn.model_selection import GridSearchCV >>> param_grid = dict(reduce_dim__n_components=[2, 5, 10], ... clf__C=[0.1, 10, 100]) >>> grid_search = GridSearchCV(pipe, param_grid=param_grid)
Отдельные шаги также можно заменить как параметры, а незавершенные шаги можно игнорировать, установив для них 'passthrough'
:
>>> from sklearn.linear_model import LogisticRegression >>> param_grid = dict(reduce_dim=['passthrough', PCA(5), PCA(10)], ... clf=[SVC(), LogisticRegression()], ... clf__C=[0.1, 10, 100]) >>> grid_search = GridSearchCV(pipe, param_grid=param_grid)
Оценщики конвейера можно получить по индексу:
>>> pipe[0] PCA()
или по имени:
>>> pipe['reduce_dim'] PCA()
Примеры:
Смотрите также:
6.1.1.2. Примечания
Вызов fit
конвейера — это то же самое, что вызов fit
каждого оценщика по очереди, transform
ввод и передача его следующему шагу. В конвейере есть все методы, которые есть у последнего оценщика в конвейере, т. е. Если последний оценщик является классификатором, его Pipeline
можно использовать в качестве классификатора. Если последний оценщик — трансформатор, опять же, конвейер.
6.1.1.3. Кэширующие трансформаторы: избегайте повторных вычислений
Установка трансформаторов может быть дорогостоящей в вычислительном отношении. С его memory
набором параметров Pipeline
кэширует каждый преобразователь после вызова fit
. Эта функция используется, чтобы избежать вычисления подходящих трансформаторов в пайплайне, если параметры и входные данные идентичны. Типичным примером является случай поиска в сети, в котором трансформаторы могут быть установлены только один раз и повторно использованы для каждой конфигурации.
Параметр memory
нужен для кеширования трансформаторов. memory
может быть либо строкой, содержащей каталог для кэширования преобразователей, либо объектом joblib.Memory :
>>> from tempfile import mkdtemp >>> from shutil import rmtree >>> from sklearn.decomposition import PCA >>> from sklearn.svm import SVC >>> from sklearn.pipeline import Pipeline >>> estimators = [('reduce_dim', PCA()), ('clf', SVC())] >>> cachedir = mkdtemp() >>> pipe = Pipeline(estimators, memory=cachedir) >>> pipe Pipeline(memory=..., steps=[('reduce_dim', PCA()), ('clf', SVC())]) >>> # Clear the cache directory when you don't need it anymore >>> rmtree(cachedir)
Предупреждение Побочный эффект кеширования трансформаторов
Используя Pipeline
без включенного кеша, можно проверить исходный экземпляр, например:
>>> from sklearn.datasets import load_digits >>> X_digits, y_digits = load_digits(return_X_y=True) >>> pca1 = PCA() >>> svm1 = SVC() >>> pipe = Pipeline([('reduce_dim', pca1), ('clf', svm1)]) >>> pipe.fit(X_digits, y_digits) Pipeline(steps=[('reduce_dim', PCA()), ('clf', SVC())]) >>> # The pca instance can be inspected directly >>> print(pca1.components_) [[-1.77484909e-19 ... 4.07058917e-18]]
Включение кэширования запускает клонирование трансформаторов перед установкой. Следовательно, экземпляр трансформатора, передаваемый в трубопровод, не может быть проверен напрямую. В следующем примере при доступе к PCA
экземпляру pca2
будет подниматься, AttributeError
поскольку pca2
трансформатор будет непригодным. Вместо этого используйте атрибут named_steps
для проверки оценщиков в конвейере:
>>> cachedir = mkdtemp() >>> pca2 = PCA() >>> svm2 = SVC() >>> cached_pipe = Pipeline([('reduce_dim', pca2), ('clf', svm2)], ... memory=cachedir) >>> cached_pipe.fit(X_digits, y_digits) Pipeline(memory=..., steps=[('reduce_dim', PCA()), ('clf', SVC())]) >>> print(cached_pipe.named_steps['reduce_dim'].components_) [[-1.77484909e-19 ... 4.07058917e-18]] >>> # Remove the cache directory >>> rmtree(cachedir)
6.1.2. Преобразование цели в регрессии
TransformedTargetRegressor
преобразует цели y
перед подгонкой регрессионной модели. Прогнозы отображаются обратно в исходное пространство с помощью обратного преобразования. Он принимает в качестве аргумента регрессор, который будет использоваться для прогнозирования, и преобразователь, который будет применен к целевой переменной:
>>> import numpy as np >>> from sklearn.datasets import fetch_california_housing >>> from sklearn.compose import TransformedTargetRegressor >>> from sklearn.preprocessing import QuantileTransformer >>> from sklearn.linear_model import LinearRegression >>> from sklearn.model_selection import train_test_split >>> X, y = fetch_california_housing(return_X_y=True) >>> X, y = X[:2000, :], y[:2000] # select a subset of data >>> transformer = QuantileTransformer(output_distribution='normal') >>> regressor = LinearRegression() >>> regr = TransformedTargetRegressor(regressor=regressor, ... transformer=transformer) >>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) >>> regr.fit(X_train, y_train) TransformedTargetRegressor(...) >>> print('R2 score: {0:.2f}'.format(regr.score(X_test, y_test))) R2 score: 0.61 >>> raw_target_regr = LinearRegression().fit(X_train, y_train) >>> print('R2 score: {0:.2f}'.format(raw_target_regr.score(X_test, y_test))) R2 score: 0.59
Для простых преобразований вместо объекта Transformer можно передать пару функций, определяющих преобразование и его обратное отображение:
>>> def func(x): ... return np.log(x) >>> def inverse_func(x): ... return np.exp(x)
Впоследствии объект создается как:
>>> regr = TransformedTargetRegressor(regressor=regressor, ... func=func, ... inverse_func=inverse_func) >>> regr.fit(X_train, y_train) TransformedTargetRegressor(...) >>> print('R2 score: {0:.2f}'.format(regr.score(X_test, y_test))) R2 score: 0.51
По умолчанию предоставленные функции проверяются при каждой подгонке как противоположные друг другу. Тем не менее, можно обойти эту проверку, установив check_inverse
на False
:
>>> def inverse_func(x): ... return x >>> regr = TransformedTargetRegressor(regressor=regressor, ... func=func, ... inverse_func=inverse_func, ... check_inverse=False) >>> regr.fit(X_train, y_train) TransformedTargetRegressor(...) >>> print('R2 score: {0:.2f}'.format(regr.score(X_test, y_test))) R2 score: -1.57
Примечание
Преобразование можно запустить, задав любую transformer
или пару функций func
и inverse_func
. Однако установка обоих параметров вызовет ошибку.
6.1.3. FeatureUnion: составные пространственные объекты
FeatureUnion
объединяет несколько объектов-преобразователей в новый преобразователь, который объединяет их выходные данные. FeatureUnion
принимает список объектов-преобразователей. Во время подгонки каждый из них подбирается к данным независимо. Трансформаторы применяются параллельно, а выводимые ими матрицы характеристик объединяются бок о бок в более крупную матрицу.
Если вы хотите применить различные преобразования к каждому полю данных, см. Соответствующий класс ColumnTransformer
(см. Руководство пользователя ).
FeatureUnion
служит тем же целям, что и Pipeline
— удобство и совместная оценка и проверка параметров.
FeatureUnion
и Pipeline
их можно комбинировать для создания сложных моделей.
(FeatureUnion
не имеет возможности проверить, могут ли два преобразователя создавать идентичные функции. Он создает объединение только в том случае, если наборы функций не пересекаются, и ответственность за их соответствие лежит на вызывающей стороне.)
6.1.3.1. Использование
Создается FeatureUnion
с использованием списка пар (key, value), где key- имя, которое вы хотите дать данному преобразованию (произвольная строка; она служит только идентификатором), и value является объектом оценки:
>>> from sklearn.pipeline import FeatureUnion >>> from sklearn.decomposition import PCA >>> from sklearn.decomposition import KernelPCA >>> estimators = [('linear_pca', PCA()), ('kernel_pca', KernelPCA())] >>> combined = FeatureUnion(estimators) >>> combined FeatureUnion(transformer_list=[('linear_pca', PCA()), ('kernel_pca', KernelPCA())])
Подобно конвейерам, объединения функций имеют вызываемый сокращенный конструктор make_union
, который не требует явного именования компонентов.
Например Pipeline
, отдельные шаги можно заменить с помощью set_params
и игнорировать, установив 'drop'
:
>>> combined.set_params(kernel_pca='drop') FeatureUnion(transformer_list=[('linear_pca', PCA()), ('kernel_pca', 'drop')])
6.1.4. ColumnTransformer для разнородных данных
Многие наборы данных содержат объекты разных типов, например текст, числа с плавающей запятой и даты, где каждый тип объекта требует отдельных этапов предварительной обработки или извлечения признаков. Часто проще всего предварительно обработать данные перед применением методов scikit-learn, например, с помощью pandas . Обработка ваших данных перед их передачей в scikit-learn может быть проблематичной по одной из следующих причин:
- Включение статистики из тестовых данных в препроцессоры делает оценки перекрестной проверки ненадежными (известными как утечка данных ), например, в случае масштабаторов или вменения пропущенных значений.
- Вы можете захотеть включить параметры препроцессоров в поиск параметров .
Помогает ColumnTransformer
выполнять различные преобразования для различных столбцов данных, в пределах , Pipeline
что является безопасным от утечки данных и которые могут быть параметризованы. ColumnTransformer
работает с массивами, разреженными матрицами и пандами DataFrames .
К каждому столбцу может быть применено различное преобразование, такое как предварительная обработка или определенный метод извлечения признаков:
>>> import pandas as pd >>> X = pd.DataFrame( ... {'city': ['London', 'London', 'Paris', 'Sallisaw'], ... 'title': ["His Last Bow", "How Watson Learned the Trick", ... "A Moveable Feast", "The Grapes of Wrath"], ... 'expert_rating': [5, 3, 4, 5], ... 'user_rating': [4, 5, 4, 3]})
Для этих данных мы можем захотеть закодировать 'city'
столбец как категориальную переменную, используя, OneHotEncoder
но применив CountVectorizer
к 'title'
столбцу. Поскольку мы можем использовать несколько методов извлечения признаков в одном столбце, мы даем каждому преобразователю уникальное имя, например 'city_category'
и 'title_bow'
. По умолчанию остальные столбцы рейтинга игнорируются ( remainder='drop'
):
>>> from sklearn.compose import ColumnTransformer >>> from sklearn.feature_extraction.text import CountVectorizer >>> from sklearn.preprocessing import OneHotEncoder >>> column_trans = ColumnTransformer( ... [('city_category', OneHotEncoder(dtype='int'),['city']), ... ('title_bow', CountVectorizer(), 'title')], ... remainder='drop') >>> column_trans.fit(X) ColumnTransformer(transformers=[('city_category', OneHotEncoder(dtype='int'), ['city']), ('title_bow', CountVectorizer(), 'title')]) >>> column_trans.get_feature_names() ['city_category__x0_London', 'city_category__x0_Paris', 'city_category__x0_Sallisaw', 'title_bow__bow', 'title_bow__feast', 'title_bow__grapes', 'title_bow__his', 'title_bow__how', 'title_bow__last', 'title_bow__learned', 'title_bow__moveable', 'title_bow__of', 'title_bow__the', 'title_bow__trick', 'title_bow__watson', 'title_bow__wrath'] >>> column_trans.transform(X).toarray() array([[1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0], [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1]]...)
В приведенном выше примере CountVectorizer
ожидается , что на входе будет одномерный массив, поэтому столбцы были указаны как строка ('title'
). Однако, OneHotEncoder
поскольку большинство других преобразователей ожидают 2D-данных, в этом случае вам необходимо указать столбец как список строк (['city']
).
Помимо скаляра или списка отдельных элементов, выбор столбца может быть указан как список из нескольких элементов, целочисленный массив, срез, логическая маска или с make_column_selector
. make_column_selector
используются для выбора колонок в зависимости от типа данных или имени столбца:
>>> from sklearn.preprocessing import StandardScaler >>> from sklearn.compose import make_column_selector >>> ct = ColumnTransformer([ ... ('scale', StandardScaler(), ... make_column_selector(dtype_include=np.number)), ... ('onehot', ... OneHotEncoder(), ... make_column_selector(pattern='city', dtype_include=object))]) >>> ct.fit_transform(X) array([[ 0.904..., 0. , 1. , 0. , 0. ], [-1.507..., 1.414..., 1. , 0. , 0. ], [-0.301..., 0. , 0. , 1. , 0. ], [ 0.904..., -1.414..., 0. , 0. , 1. ]])
Строки могут ссылаться на столбцы, если входом является DataFrame, целые числа всегда интерпретируются как позиционные столбцы.
Мы можем сохранить оставшиеся столбцы рейтинга, установив remainder='passthrough'
. Значения добавляются в конец преобразования:
>>> column_trans = ColumnTransformer( ... [('city_category', OneHotEncoder(dtype='int'),['city']), ... ('title_bow', CountVectorizer(), 'title')], ... remainder='passthrough') >>> column_trans.fit_transform(X) array([[1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, 4], [1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 3, 5], [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 4], [0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 5, 3]]...)
Параметр remainder
может быть установлен в качестве оценки для преобразования оставшегося рейтинга столбцов. Преобразованные значения добавляются в конец преобразования:
>>> from sklearn.preprocessing import MinMaxScaler >>> column_trans = ColumnTransformer( ... [('city_category', OneHotEncoder(), ['city']), ... ('title_bow', CountVectorizer(), 'title')], ... remainder=MinMaxScaler()) >>> column_trans.fit_transform(X)[:, -2:] array([[1. , 0.5], [0. , 1. ], [0.5, 0.5], [1. , 0. ]])
Функция make_column_transformer
доступна более легко создать ColumnTransformer
объект. В частности, имена будут даны автоматически. Эквивалент для приведенного выше примера:
>>> from sklearn.compose import make_column_transformer >>> column_trans = make_column_transformer( ... (OneHotEncoder(), ['city']), ... (CountVectorizer(), 'title'), ... remainder=MinMaxScaler()) >>> column_trans ColumnTransformer(remainder=MinMaxScaler(), transformers=[('onehotencoder', OneHotEncoder(), ['city']), ('countvectorizer', CountVectorizer(), 'title')])
6.1.5. Визуализация составных оценщиков
Оценщики могут отображаться в формате HTML при отображении в записной книжке jupyter. Это может быть полезно для диагностики или визуализации конвейера с помощью множества оценщиков. Эта визуализация активируется установкой display
параметра в set_config
:
>>> from sklearn import set_config >>> set_config(display='diagram') >>> # diplays HTML representation in a jupyter context >>> column_trans
Пример вывода HTML можно увидеть в представлении HTML раздела конвейера преобразователя столбцов со смешанными типами. В качестве альтернативы HTML можно записать в файл, используя estimator_html_repr
:
>>> from sklearn.utils import estimator_html_repr >>> with open('my_estimator.html', 'w') as f: ... f.write(estimator_html_repr(clf))