Spec-Zone.ru › scikit-learn

Примечание

Перейти в конец, чтобы скачать полный код примера или запустить этот пример в браузере с помощью JupyterLite или Binder.

Графики частичной зависимости и индивидуального условного математического ожидания

Графики частичной зависимости показывают зависимость целевой функции [2] от набора интересующих признаков, усредняя по значениям всех остальных признаков (дополняющих признаков). Из-за ограничений человеческого восприятия набор интересующих признаков должен быть небольшим (обычно один или два), поэтому их, как правило, выбирают среди наиболее важных признаков.

Аналогичным образом график индивидуального условного математического ожидания (ICE) [3] показывает зависимость целевой функции от интересующего признака. Однако, в отличие от графиков частичной зависимости, которые показывают усреднённый эффект интересующих признаков, графики ICE визуализируют зависимость предсказания от признака отдельно для каждого образца: для каждого образца строится отдельная линия. Для графиков ICE поддерживается только один интересующий признак.

В этом примере показано, как построить графики частичной зависимости и ICE для MLPRegressor и HistGradientBoostingRegressor, обученных на наборе данных о совместном использовании велосипедов. Пример основан на работе [1].

[1]

Молнар, Кристоф. «Интерпретируемое машинное обучение. Руководство по объяснению моделей «чёрного ящика», 2019.

[2]

Для классификации можно считать это оценкой регрессии до применения функции связи.

[3]

Голдстайн, А., Капелнер, А., Блайх, Дж. и Питкин, Э. (2015). «Заглядывая внутрь чёрного ящика: визуализация статистического обучения с помощью графиков индивидуального условного математического ожидания». Journal of Computational and Graphical Statistics, 24(1): 44–65

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

Предварительная обработка набора данных о совместном использовании велосипедов

Мы будем использовать набор данных о совместном использовании велосипедов. Цель — предсказать число аренд велосипедов, используя данные о погоде и сезоне, а также информацию о дате и времени.

from sklearn.datasets import fetch_openml

bikes = fetch_openml("Bike_Sharing_Demand", version=2, as_frame=True)
# Make an explicit copy to avoid "SettingWithCopyWarning" from pandas
X, y = bikes.data.copy(), bikes.target

# We use only a subset of the data to speed up the example.
X = X.iloc[::5, :]
y = y[::5]

Признак "weather" имеет особенность: категория "heavy_rain" встречается редко.

X["weather"].value_counts()
weather
clear         2284
misty          904
rain           287
heavy_rain       1
Name: count, dtype: int64

Из-за редкости этой категории мы объединяем её с "rain".

X["weather"] = (
    X["weather"]
    .astype(object)
    .replace(to_replace="heavy_rain", value="rain")
    .astype("category")
)

Теперь подробнее рассмотрим признак "year":

X["year"].value_counts()
year
1    1747
0    1729
Name: count, dtype: int64

Видно, что данные охватывают два года. Для обучения модели используем первый год, а для её тестирования — второй.

mask_training = X["year"] == 0.0
X = X.drop(columns=["year"])
X_train, y_train = X[mask_training], y[mask_training]
X_test, y_test = X[~mask_training], y[~mask_training]

Из информации о наборе данных видно, что в нём представлены различные типы данных. Для предварительной обработки разных столбцов необходимо выбрать соответствующие способы.

X_train.info()
<class 'pandas.DataFrame'>
RangeIndex: 1729 entries, 0 to 8640
Data columns (total 11 columns):
 #   Column      Non-Null Count  Dtype
---  ------      --------------  -----
 0   season      1729 non-null   category
 1   month       1729 non-null   int64
 2   hour        1729 non-null   int64
 3   holiday     1729 non-null   category
 4   weekday     1729 non-null   int64
 5   workingday  1729 non-null   category
 6   weather     1729 non-null   category
 7   temp        1729 non-null   float64
 8   feel_temp   1729 non-null   float64
 9   humidity    1729 non-null   float64
 10  windspeed   1729 non-null   float64
dtypes: category(4), float64(4), int64(3)
memory usage: 101.5 KB

Исходя из приведённой выше информации, мы будем считать столбцы category номинальными категориальными признаками. Кроме того, информацию о дате и времени также будем рассматривать как категориальные признаки.

Мы вручную определяем столбцы с числовыми и категориальными признаками.

numerical_features = [
    "temp",
    "feel_temp",
    "humidity",
    "windspeed",
]
categorical_features = X_train.columns.drop(numerical_features)

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

Построим график среднего числа аренд велосипедов, сгруппировав данные по сезонам и годам.

from itertools import product

import matplotlib.pyplot as plt
import numpy as np

days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
hours = tuple(range(24))
xticklabels = [f"{day}\n{hour}:00" for day, hour in product(days, hours)]
xtick_start, xtick_period = 6, 12

fig, axs = plt.subplots(nrows=2, figsize=(8, 6), sharey=True, sharex=True)
average_bike_rentals = bikes.frame.groupby(
    ["year", "season", "weekday", "hour"], observed=True
).mean(numeric_only=True)["count"]
for ax, (idx, df) in zip(axs, average_bike_rentals.groupby("year")):
    df.groupby("season", observed=True).plot(ax=ax, legend=True)

    # decorate the plot
    ax.set_xticks(
        np.linspace(
            start=xtick_start,
            stop=len(xticklabels),
            num=len(xticklabels) // xtick_period,
        )
    )
    ax.set_xticklabels(xticklabels[xtick_start::xtick_period])
    ax.set_xlabel("")
    ax.set_ylabel("Average number of bike rentals")
    ax.set_title(
        f"Bike rental for {'2010 (train set)' if idx == 0.0 else '2011 (test set)'}"
    )
    ax.set_ylim(0, 1_000)
    ax.set_xlim(0, len(xticklabels))
    ax.legend(loc=2)
Bike rental for 2010 (train set), Bike rental for 2011 (test set)

Первое заметное различие между обучающей и тестовой выборками состоит в том, что в тестовой выборке число аренд велосипедов выше. Поэтому неудивительно, если модель машинного обучения будет занижать число аренд. Мы также замечаем, что весной велосипедов арендуют меньше. Кроме того, в рабочие дни наблюдается характерная картина: около 6–7 часов утра и 17–18 часов вечера возникают пики числа аренд велосипедов. Мы можем учитывать эти наблюдения, чтобы понять график частичной зависимости.

Предварительная обработка для моделей машинного обучения

Поскольку далее мы используем две разные модели — MLPRegressor и HistGradientBoostingRegressor, — мы создаём два разных предварительных обработчика, каждый из которых предназначен для своей модели.

Предварительная обработка для модели нейронной сети

Для масштабирования числовых признаков мы используем QuantileTransformer, а категориальные признаки кодируем с помощью OneHotEncoder.

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, QuantileTransformer

mlp_preprocessor = ColumnTransformer(
    transformers=[
        ("num", QuantileTransformer(n_quantiles=100), numerical_features),
        ("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features),
    ]
)
mlp_preprocessor
ColumnTransformer(transformers=[('num', QuantileTransformer(n_quantiles=100),
                                 ['temp', 'feel_temp', 'humidity',
                                  'windspeed']),
                                ('cat', OneHotEncoder(handle_unknown='ignore'),
                                 Index(['season', 'month', 'hour', 'holiday', 'weekday', 'workingday',
       'weather'],
      dtype='str'))])
В среде Jupyter повторно выполните эту ячейку, чтобы отобразить представление HTML, или доверьте записной книжке.
На GitHub представление HTML отобразить невозможно. Попробуйте открыть эту страницу с помощью nbviewer.org.
Параметры
transformers transformers: список кортежей

Список кортежей (name, transformer, columns), задающих
объекты преобразователей, которые следует применить к подмножествам данных.

name : str
Как и в Pipeline и FeatureUnion, это позволяет задавать преобразователь и
его параметры с помощью ``set_params`` и искать их при поиске по сетке.
transformer : {'drop', 'passthrough'} или оцениватель
Оцениватель должен поддерживать :term:`fit` и :term:`transform`.
Также допускаются специальные строки 'drop' и 'passthrough',
указывающие соответственно на удаление столбцов или их передачу без
преобразования.
columns : str, массивоподобный объект из str, int, массивоподобный объект из int, массивоподобный объект из bool, срез или вызываемый объект
Индексирует данные по второй оси. Целые числа интерпретируются как
позиционные номера столбцов, а строки позволяют обращаться к столбцам DataFrame
по имени. Если ``transformer`` ожидает на входе X одномерный массивоподобный объект (вектор),
следует использовать скалярную строку или целое число; в противном случае
преобразователю будет передан двумерный массив.
Вызываемому объекту передаются входные данные `X`, и он может вернуть любой
из перечисленных выше вариантов. Чтобы выбрать несколько столбцов по имени или типу данных, можно использовать
:obj:`make_column_selector`.
[('num', ...), ('cat', ...)]
remainder remainder: {'drop', 'passthrough'} или оцениватель, по умолчанию='drop'

По умолчанию преобразуются и объединяются в выходных данных только столбцы,
указанные в `transformers`; остальные столбцы удаляются (значение по умолчанию — ``'drop'``).
Если указать ``remainder='passthrough'``, все оставшиеся столбцы, которые
не были указаны в `transformers`, но присутствовали в данных, переданных
в `fit`, будут автоматически переданы дальше. Это подмножество столбцов
объединяется с результатом преобразователей. Для фреймов данных
дополнительные столбцы, отсутствовавшие при вызове `fit`, не попадут в результат
`transform`.
Если задать для ``remainder`` оцениватель, к оставшимся
неуказанным столбцам будет применяться этот оцениватель. Он должен поддерживать
:term:`fit` и :term:`transform`.
Обратите внимание: для использования этой возможности порядок столбцов DataFrame,
передаваемых в :term:`fit` и :term:`transform`, должен совпадать.
'drop'
sparse_threshold sparse_threshold: float, по умолчанию=0.3

Если результаты разных преобразователей содержат разреженные матрицы,
они будут объединены в разреженную матрицу, если общая плотность
меньше этого значения. Укажите ``sparse_threshold=0``, чтобы всегда возвращать
плотную матрицу. Если преобразованные данные полностью состоят из плотных данных,
результат объединения будет плотным, а этот параметр будет проигнорирован.
0.3
n_jobs n_jobs: int, по умолчанию=None

Число задач для параллельного выполнения.
``None`` означает 1, если только не используется контекст :obj:`joblib.parallel_backend`.
``-1`` означает использование всех процессоров. Подробнее см. :term:`Глоссарий <n_jobs>`.
None
transformer_weights transformer_weights: dict, по умолчанию=None

Множители для признаков, обрабатываемых каждым преобразователем. Результат
преобразователя умножается на соответствующий множитель. Ключи — имена преобразователей,
значения — множители.
None
verbose verbose: bool, по умолчанию=False

Если значение True, по завершении обучения каждого преобразователя
будет выводиться затраченное время.
False
verbose_feature_names_out verbose_feature_names_out: bool, str или Callable[[str, str], str], по умолчанию=True

- Если значение True, :meth:`ColumnTransformer.get_feature_names_out` будет добавлять к именам всех признаков префикс
с именем преобразователя, создавшего признак. Это эквивалентно заданию
`verbose_feature_names_out="{transformer_name}__{feature_name}"`.
- Если значение False, :meth:`ColumnTransformer.get_feature_names_out` не будет
добавлять префиксы к именам признаков и вызовет ошибку, если имена признаков не уникальны.
- Если задано ``Callable[[str, str], str]``,
:meth:`ColumnTransformer.get_feature_names_out` переименует все признаки
с помощью имени преобразователя. Первый аргумент вызываемого объекта —
имя преобразователя, второй — имя признака. Возвращённая
строка станет новым именем признака.
- Если задано ``str``, это должна быть строка, готовая к форматированию. Переданная строка будет
отформатирована с использованием двух имён полей: ``transformer_name`` и ``feature_name``.
например, ``"{feature_name}__{transformer_name}"``. Подробнее см. метод
стандартной библиотеки :meth:`str.format`.

.. versionadded:: 1.0

.. versionchanged:: 1.6
`verbose_feature_names_out` может быть вызываемым объектом или строкой для форматирования.
True
['temp', 'feel_temp', 'humidity', 'windspeed']
Параметры
n_quantiles n_quantiles: int, по умолчанию=1000 или n_samples

Число вычисляемых квантилей. Оно соответствует числу
опорных точек, используемых для дискретизации функции распределения.
Если n_quantiles превышает число образцов, n_quantiles устанавливается
равным числу образцов, поскольку большее число квантилей не обеспечивает
более точной аппроксимации функции распределения.
100
output_distribution output_distribution: {'uniform', 'normal'}, по умолчанию='uniform'

Маргинальное распределение преобразованных данных. Возможные варианты:
'uniform' (по умолчанию) или 'normal'.
'uniform'
ignore_implicit_zeros ignore_implicit_zeros: bool, по умолчанию=False

Применяется только к разреженным матрицам. Если значение True, разреженные элементы
матрицы отбрасываются при вычислении статистики квантилей. Если значение False,
эти элементы считаются нулями.
False
subsample subsample: int или None, по умолчанию=10_000

Максимальное число образцов, используемых для оценки квантилей
с целью повышения вычислительной эффективности. Обратите внимание, что процедура подвыборки может
отличаться для разреженных и плотных матриц с одинаковыми значениями.
Отключите подвыборку, задав `subsample=None`.

.. versionadded:: 1.5
Добавлена возможность отключить подвыборку, указав `None`.
10000
random_state random_state: int, экземпляр RandomState или None, по умолчанию=None

Определяет генерацию случайных чисел для подвыборки и шума
сглаживания.
Подробнее см. параметр ``subsample``.
Передайте целое число, чтобы получать воспроизводимые результаты при нескольких вызовах функции.
См. :term:`Глоссарий <random_state>`.
None
copy copy: bool, по умолчанию=True

Задайте False, чтобы выполнить преобразование на месте и избежать копирования (если
входные данные уже являются массивом numpy).
True
Index(['season', 'month', 'hour', 'holiday', 'weekday', 'workingday',
       'weather'],
      dtype='str')
Параметры
handle_unknown handle_unknown: {'error', 'ignore', 'infrequent_if_exist', 'warn'}, по умолчанию='error'

Задаёт способ обработки неизвестных категорий во время :meth:`transform`.

- 'error' : вызвать ошибку, если при преобразовании встретится неизвестная категория.
- 'ignore' : если при преобразовании встретится неизвестная категория,
соответствующие ей столбцы one-hot-кодирования будут заполнены нулями. При обратном преобразовании неизвестная категория
будет обозначена как None.
- 'infrequent_if_exist' : если при преобразовании встретится неизвестная категория,
соответствующие ей столбцы one-hot-кодирования будут сопоставлены с редкой категорией, если она существует. Редкая категория
будет помещена в последнюю позицию кодировки. При обратном преобразовании неизвестная категория будет
сопоставлена с категорией, обозначенной как `'infrequent'`, если она существует. Если категория `'infrequent'` отсутствует,
то :meth:`transform` и :meth:`inverse_transform` будут обрабатывать неизвестную категорию так же, как при
`handle_unknown='ignore'`. Редкие категории определяются параметрами
`min_frequency` и `max_categories`. Подробнее см. в
:ref:`Руководстве пользователя <encoder_infrequent_categories>`.
- 'warn' : если при преобразовании встретится неизвестная категория,
будет выдано предупреждение, после чего кодирование продолжится так, как описано для
`handle_unknown="infrequent_if_exist"`.

.. versionchanged:: 1.1
Добавлено значение `'infrequent_if_exist'` для автоматической обработки неизвестных
и редких категорий.

.. versionadded:: 1.6
В версии 1.6 добавлено значение `"warn"`.
'ignore'
categories categories: 'auto' или список массивоподобных объектов, по умолчанию='auto'

Категории (уникальные значения) для каждого признака:

- 'auto' : автоматически определить категории по обучающим данным.
- list : ``categories[i]`` содержит категории, ожидаемые в i-м
столбце. Переданный список категорий не должен содержать одновременно строки и числовые
значения для одного признака; числовые значения должны быть отсортированы.

Использованные категории можно найти в атрибуте ``categories_``.

.. versionadded:: 0.20
'auto'
drop drop: {'first', 'if_binary'} или массивоподобный объект формы (n_features,), по умолчанию=None

Задаёт способ удаления одной из категорий для каждого
признака. Это полезно в ситуациях, когда идеально коллинеарные
признаки создают проблемы, например при передаче полученных данных
в нерегуляризованную модель линейной регрессии.

Однако удаление одной категории нарушает симметрию исходного
представления и поэтому может привести к смещению в последующих моделях,
например в регуляризованных моделях линейной классификации или регрессии.

- None : сохранить все признаки (значение по умолчанию).
- 'first' : удалить первую категорию каждого признака. Если присутствует только одна
категория, признак будет удалён полностью.
- 'if_binary' : удалить первую категорию каждого признака с двумя
категориями. Признаки с 1 или более чем 2 категориями
останутся без изменений.
- array : ``drop[i]`` — категория признака ``X[:, i]``, которую
следует удалить.

Если для группировки редких категорий настроены параметры `max_categories` или `min_frequency`,
удаление выполняется после группировки.

.. versionadded:: 0.21
Параметр `drop` добавлен в версии 0.21.

.. versionchanged:: 0.23
Значение `drop='if_binary'` добавлено в версии 0.23.

.. versionchanged:: 1.1
Добавлена поддержка удаления редких категорий.
None
sparse_output sparse_output: bool, по умолчанию=True

Если значение ``True``, возвращается разреженная матрица/массив SciPy
в формате «Compressed Sparse Row» (CSR).

.. versionadded:: 1.2
Параметр `sparse` переименован в `sparse_output`
True
dtype dtype: числовой тип, по умолчанию=np.float64

Требуемый тип данных выходных значений.
<class 'numpy.float64'>
min_frequency min_frequency: int или float, по умолчанию=None

Задаёт минимальную частоту, ниже которой категория будет
считаться редкой.

- Если задан `int`, редкими будут считаться категории с меньшей
частотой.

- Если задан `float`, редкими будут считаться категории с частотой меньше
`min_frequency * n_samples`.

.. versionadded:: 1.1
Подробнее см. в :ref:`Руководстве пользователя <encoder_infrequent_categories>`.
None
max_categories max_categories: int, по умолчанию=None

Задаёт верхний предел числа выходных признаков для каждого входного
признака с учётом редких категорий. Если есть редкие категории, `max_categories` включает категорию,
представляющую редкие категории, наряду с частыми. Если задано `None`,
число выходных признаков не ограничено.

.. versionadded:: 1.1
Подробнее см. в :ref:`Руководстве пользователя <encoder_infrequent_categories>`.
None
feature_name_combiner feature_name_combiner: "concat" или вызываемый объект, по умолчанию="concat"

Вызываемый объект с сигнатурой `def callable(input_feature, category)`, возвращающий
строку. Он используется для создания имён признаков, возвращаемых методом
:meth:`get_feature_names_out`.

`"concat"` объединяет закодированное имя признака и категорию в строку
`feature + "_" + str(category)`. Например, для признака X со значениями 1, 6, 7 создаются
имена признаков `X_1, X_6, X_7`.

.. versionadded:: 1.3
'concat'


Предобработчик для модели градиентного бустинга

Для модели градиентного бустинга мы оставляем числовые признаки без изменений и кодируем только категориальные признаки с помощью OrdinalEncoder.

from sklearn.preprocessing import OrdinalEncoder

hgbdt_preprocessor = ColumnTransformer(
    transformers=[
        ("cat", OrdinalEncoder(), categorical_features),
        ("num", "passthrough", numerical_features),
    ],
    sparse_threshold=1,
    verbose_feature_names_out=False,
).set_output(transform="pandas")
hgbdt_preprocessor
ColumnTransformer(sparse_threshold=1,
                  transformers=[('cat', OrdinalEncoder(),
                                 Index(['season', 'month', 'hour', 'holiday', 'weekday', 'workingday',
       'weather'],
      dtype='str')),
                                ('num', 'passthrough',
                                 ['temp', 'feel_temp', 'humidity',
                                  'windspeed'])],
                  verbose_feature_names_out=False)
В среде Jupyter повторно выполните эту ячейку, чтобы отобразить HTML-представление, или доверьте доступ к записной книжке.
На GitHub HTML-представление не отображается. Попробуйте открыть эту страницу на nbviewer.org.
Параметры
transformers transformers: список кортежей

Список кортежей (name, transformer, columns), задающих
объекты-преобразователи, которые применяются к подмножествам данных.

name : str
Как и в Pipeline и FeatureUnion, это позволяет задавать преобразователь и
его параметры с помощью ``set_params`` и выполнять их поиск в
поиске по сетке.
transformer : {'drop', 'passthrough'} или оцениватель
Оцениватель должен поддерживать :term:`fit` и :term:`transform`.
Также принимаются специальные строки 'drop' и 'passthrough',
указывающие соответственно на удаление столбцов или их передачу
без преобразования.
columns : str, массивоподобный объект из str, int, массивоподобный объект из int, массивоподобный объект из bool, срез или вызываемый объект
Индексирует данные по второй оси. Целые числа трактуются как
позиционные индексы столбцов, а строки могут ссылаться на столбцы DataFrame
по имени. Если ``transformer`` ожидает, что X будет одномерным массивоподобным объектом (вектором),
следует использовать скалярную строку или целое число;
в противном случае преобразователю будет передан двумерный массив.
Вызываемому объекту передаются входные данные `X`, и он может вернуть любое из
перечисленного выше. Чтобы выбрать несколько столбцов по имени или типу данных, можно использовать
:obj:`make_column_selector`.
[('cat', ...), ('num', ...)]
sparse_threshold sparse_threshold: число с плавающей точкой, по умолчанию 0.3

Если выходные данные разных преобразователей содержат разреженные матрицы,
они будут объединены в разреженную матрицу, если общая плотность
ниже этого значения. Используйте ``sparse_threshold=0``, чтобы всегда возвращать
плотную матрицу. Если преобразованные выходные данные состоят только из плотных данных,
результат объединения будет плотным, а этот параметр будет проигнорирован.
1
verbose_feature_names_out verbose_feature_names_out: bool, str или Callable[[str, str], str], по умолчанию True

- Если True, :meth:`ColumnTransformer.get_feature_names_out` будет добавлять в начало
всех имен признаков имя преобразователя, создавшего данный
признак. Это эквивалентно заданию
`verbose_feature_names_out="{transformer_name}__{feature_name}"`.
- Если False, :meth:`ColumnTransformer.get_feature_names_out` не будет добавлять
префикс к именам признаков и вызовет ошибку, если имена признаков не являются
уникальными.
- Если ``Callable[[str, str], str]``,
:meth:`ColumnTransformer.get_feature_names_out` переименует все признаки,
используя имя преобразователя. Первый аргумент вызываемого объекта — это
имя преобразователя, а второй — имя признака. Возвращаемая
строка станет новым именем признака.
- Если ``str``, это должна быть строка, готовая к форматированию. Указанная строка будет
отформатирована с использованием двух имен полей: ``transformer_name`` и ``feature_name``.
например, ``"{feature_name}__{transformer_name}"``. Дополнительные сведения см. в описании метода
:meth:`str.format` из стандартной библиотеки.

.. добавлено в версии:: 1.0

.. изменено в версии:: 1.6
`verbose_feature_names_out` может быть вызываемым объектом или строкой для форматирования.
False
remainder remainder: {'drop', 'passthrough'} или оцениватель, по умолчанию 'drop'

По умолчанию преобразуются и объединяются в выходных данных только указанные в `transformers` столбцы,
а неуказанные столбцы удаляются (значение по умолчанию — ``'drop'``).
Если задать ``remainder='passthrough'``, все оставшиеся столбцы, которые
не указаны в `transformers`, но присутствуют в данных, переданных
в `fit`, будут автоматически передаваться без изменений. Это подмножество столбцов
объединяется с выходными данными преобразователей. Для фреймов данных
дополнительные столбцы, отсутствовавшие во время `fit`, будут исключены из выходных данных
`transform`.
Если задать для ``remainder`` оцениватель, оставшиеся
неуказанные столбцы будут обрабатываться этим оценивателем. Оцениватель
должен поддерживать :term:`fit` и :term:`transform`.
Обратите внимание: для использования этой возможности порядок столбцов DataFrame,
передаваемых в :term:`fit` и :term:`transform`, должен совпадать.
'drop'
n_jobs n_jobs: int, по умолчанию None

Количество задач, выполняемых параллельно.
``None`` означает 1, если только не используется контекст :obj:`joblib.parallel_backend`.
``-1`` означает использование всех процессоров. Дополнительные сведения см. в :term:`глоссарии <n_jobs>`.
None
transformer_weights transformer_weights: dict, по умолчанию None

Множители для признаков каждого преобразователя. Выходные данные
преобразователя умножаются на эти веса. Ключи — имена преобразователей,
значения — веса.
None
verbose verbose: bool, по умолчанию False

Если True, по завершении обучения каждого преобразователя будет
выведено затраченное время.
False
Index(['season', 'month', 'hour', 'holiday', 'weekday', 'workingday',
       'weather'],
      dtype='str')
Параметры
categories categories: 'auto' или список массивоподобных объектов, по умолчанию 'auto'

Категории (уникальные значения) для каждого признака:

- 'auto' : автоматически определять категории по обучающим данным.
- list : ``categories[i]`` содержит категории, ожидаемые в i-м
столбце. Переданные категории не должны смешивать строки и числовые
значения и должны быть отсортированы, если они числовые.

Используемые категории доступны в атрибуте ``categories_``.
'auto'
dtype dtype: числовой тип, по умолчанию np.float64

Желаемый тип данных выходных значений.
<class 'numpy.float64'>
handle_unknown handle_unknown: {'error', 'use_encoded_value'}, по умолчанию 'error'

Если задано значение 'error', при наличии неизвестного
категориального признака во время преобразования будет вызвана ошибка. Если задано
'use_encoded_value', неизвестным категориям будет присвоено
закодированное значение, указанное параметром `unknown_value`. В
:meth:`inverse_transform` неизвестная категория будет обозначена как None.

.. добавлено в версии:: 0.24
'error'
unknown_value unknown_value: int или np.nan, по умолчанию None

Если параметру handle_unknown присвоено значение 'use_encoded_value',
этот параметр обязателен и задает закодированное значение для неизвестных
категорий. Оно должно отличаться от значений, используемых для кодирования любых
категорий в `fit`. Если задано np.nan, параметр `dtype` должен иметь
тип с плавающей точкой.

.. добавлено в версии:: 0.24
None
encoded_missing_value encoded_missing_value: int или np.nan, по умолчанию np.nan

Закодированное значение пропущенных категорий. Если задано `np.nan`, параметр `dtype`
должен иметь тип с плавающей точкой.

.. добавлено в версии:: 1.1
nan
min_frequency min_frequency: int или float, по умолчанию None

Задает минимальную частоту, ниже которой категория будет
считаться редкой.

- Если задано `int`, редкими будут считаться категории с меньшим количеством
элементов.

- Если задано `float`, редкими будут считаться категории, количество которых меньше
`min_frequency * n_samples`.

.. добавлено в версии:: 1.3
Подробнее см. в :ref:`руководстве пользователя <encoder_infrequent_categories>`.
None
max_categories max_categories: int, по умолчанию None

Задает верхний предел количества выходных категорий для каждого входного
признака с учетом редких категорий. Если есть редкие
категории, `max_categories` включает категорию, представляющую
редкие категории, вместе с частыми категориями. Если задано `None`,
количество выходных признаков не ограничено.

`max_categories` **не** учитывает пропущенные или неизвестные
категории. Если задать для `unknown_value` или `encoded_missing_value` целое число,
количество уникальных целочисленных кодов увеличится на единицу для каждого параметра.
В результате может быть до `max_categories + 2` целочисленных кодов.

.. добавлено в версии:: 1.3
Подробнее см. в :ref:`руководстве пользователя <encoder_infrequent_categories>`.
None
['temp', 'feel_temp', 'humidity', 'windspeed']


Частичная зависимость по одному признаку для разных моделей

В этом разделе мы вычислим частичную зависимость по одному признаку для двух разных моделей машинного обучения: (i) многослойного перцептрона и (ii) модели градиентного бустинга. На примере этих двух моделей мы покажем, как вычислять и интерпретировать графики частичной зависимости (PDP) как для числовых, так и для категориальных признаков, а также условное математическое ожидание для отдельных наблюдений (ICE).

Многослойный перцептрон

Обучим MLPRegressor и построим графики частичной зависимости для отдельных переменных.

from time import time

from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline

print("Training MLPRegressor...")
tic = time()
mlp_model = make_pipeline(
    mlp_preprocessor,
    MLPRegressor(
        hidden_layer_sizes=(30, 15),
        learning_rate_init=0.01,
        early_stopping=True,
        random_state=0,
    ),
)
mlp_model.fit(X_train, y_train)
print(f"done in {time() - tic:.3f}s")
print(f"Test R2 score: {mlp_model.score(X_test, y_test):.2f}")
Training MLPRegressor...
done in 0.745s
Test R2 score: 0.61

Мы настроили конвейер с помощью предобработчика, специально созданного для нейронной сети, и подобрали размер нейронной сети и скорость обучения, чтобы найти приемлемый компромисс между временем обучения и качеством предсказаний на тестовой выборке.

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

Более крупная нейронная сеть могла бы обеспечить еще более высокое качество предсказаний, но ее обучение обошлось бы значительно дороже.

Обратите внимание: прежде чем строить график частичной зависимости, важно убедиться, что модель достаточно точна на тестовой выборке. Иначе объяснять влияние признака на функцию предсказания модели с низким качеством предсказаний было бы мало смысла. В этом отношении наша модель MLP работает достаточно хорошо.

Построим график усредненной частичной зависимости.

import matplotlib.pyplot as plt

from sklearn.inspection import PartialDependenceDisplay

common_params = {
    "subsample": 50,
    "n_jobs": 2,
    "grid_resolution": 20,
    "random_state": 0,
}

print("Computing partial dependence plots...")
features_info = {
    # features of interest
    "features": ["temp", "humidity", "windspeed", "season", "weather", "hour"],
    # type of partial dependence plot
    "kind": "average",
    # information regarding categorical features
    "categorical_features": categorical_features,
}
tic = time()
_, ax = plt.subplots(ncols=3, nrows=2, figsize=(9, 8), constrained_layout=True)
display = PartialDependenceDisplay.from_estimator(
    mlp_model,
    X_train,
    **features_info,
    ax=ax,
    **common_params,
)
print(f"done in {time() - tic:.3f}s")
_ = display.figure_.suptitle(
    (
        "Partial dependence of the number of bike rentals\n"
        "for the bike rental dataset with an MLPRegressor"
    ),
    fontsize=16,
)
Partial dependence of the number of bike rentals for the bike rental dataset with an MLPRegressor
Computing partial dependence plots...
done in 0.956s

Градиентный бустинг

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

from sklearn.ensemble import HistGradientBoostingRegressor

print("Training HistGradientBoostingRegressor...")
tic = time()
hgbdt_model = make_pipeline(
    hgbdt_preprocessor,
    HistGradientBoostingRegressor(
        categorical_features=categorical_features,
        random_state=0,
        max_iter=50,
    ),
)
hgbdt_model.fit(X_train, y_train)
print(f"done in {time() - tic:.3f}s")
print(f"Test R2 score: {hgbdt_model.score(X_test, y_test):.2f}")
Training HistGradientBoostingRegressor...
done in 0.118s
Test R2 score: 0.62

Здесь мы использовали гиперпараметры модели градиентного бустинга по умолчанию и не выполняли предобработку, поскольку модели на основе деревьев по своей природе устойчивы к монотонным преобразованиям числовых признаков.

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

Построим графики частичной зависимости для некоторых числовых и категориальных признаков.

print("Computing partial dependence plots...")
tic = time()
_, ax = plt.subplots(ncols=3, nrows=2, figsize=(9, 8), constrained_layout=True)
display = PartialDependenceDisplay.from_estimator(
    hgbdt_model,
    X_train,
    **features_info,
    ax=ax,
    **common_params,
)
print(f"done in {time() - tic:.3f}s")
_ = display.figure_.suptitle(
    (
        "Partial dependence of the number of bike rentals\n"
        "for the bike rental dataset with a gradient boosting"
    ),
    fontsize=16,
)
Partial dependence of the number of bike rentals for the bike rental dataset with a gradient boosting
Computing partial dependence plots...
done in 1.421s

Анализ графиков

Сначала рассмотрим графики PDP для числовых признаков. Для обеих моделей общий тренд PDP для температуры показывает, что число аренд велосипедов растет с повышением температуры. Аналогичный анализ для влажности показывает противоположный тренд: число аренд велосипедов уменьшается с повышением влажности. Наконец, мы видим такую же тенденцию для скорости ветра: в обеих моделях число аренд велосипедов снижается при увеличении скорости ветра. Мы также видим, что предсказания MLPRegressor значительно более плавные, чем предсказания HistGradientBoostingRegressor.

Теперь рассмотрим графики частичной зависимости для категориальных признаков.

Для признака «сезон» мы видим, что самый низкий столбец соответствует весне. Для признака «погода» самый низкий столбец соответствует дождю. Для признака «час» видны два пика — около 7 утра и 6 вечера. Эти результаты согласуются с выводами, сделанными нами ранее при анализе набора данных.

Однако стоит отметить, что при наличии корреляции между признаками мы можем создавать потенциально бессмысленные синтетические наблюдения.

ICE и PDP

PDP представляет собой усреднение маргинальных эффектов признаков. Мы усредняем отклики всех наблюдений из предоставленного набора. В результате некоторые эффекты могут оставаться незаметными. Для этого можно построить график отклика каждого отдельного наблюдения. Такое представление называется графиком индивидуальных эффектов (ICE). На графике ниже показаны ICE для 50 случайно выбранных наблюдений по признакам температуры и влажности.

print("Computing partial dependence plots and individual conditional expectation...")
tic = time()
_, ax = plt.subplots(ncols=2, figsize=(6, 4), sharey=True, constrained_layout=True)

features_info = {
    "features": ["temp", "humidity"],
    "kind": "both",
    "centered": True,
}

display = PartialDependenceDisplay.from_estimator(
    hgbdt_model,
    X_train,
    **features_info,
    ax=ax,
    **common_params,
)
print(f"done in {time() - tic:.3f}s")
_ = display.figure_.suptitle("ICE and PDP representations", fontsize=16)
ICE and PDP representations
Computing partial dependence plots and individual conditional expectation...
done in 0.607s

ICE для признака температуры дает нам дополнительную информацию: некоторые линии ICE горизонтальны, а на других видно снижение зависимости от температуры выше 35 градусов Цельсия. Для признака влажности наблюдается похожая картина: некоторые линии ICE резко снижаются, когда влажность превышает 80%.

Не все линии ICE параллельны — это указывает на то, что модель обнаруживает взаимодействия между признаками. Повторим эксперимент, ограничив модель градиентного бустинга так, чтобы она не учитывала взаимодействия между признаками, с помощью параметра interaction_cst:

from sklearn.base import clone

interaction_cst = [[i] for i in range(X_train.shape[1])]
hgbdt_model_without_interactions = (
    clone(hgbdt_model)
    .set_params(histgradientboostingregressor__interaction_cst=interaction_cst)
    .fit(X_train, y_train)
)
print(f"Test R2 score: {hgbdt_model_without_interactions.score(X_test, y_test):.2f}")
Test R2 score: 0.38
_, ax = plt.subplots(ncols=2, figsize=(6, 4), sharey=True, constrained_layout=True)

features_info["centered"] = False
display = PartialDependenceDisplay.from_estimator(
    hgbdt_model_without_interactions,
    X_train,
    **features_info,
    ax=ax,
    **common_params,
)
_ = display.figure_.suptitle("ICE and PDP representations", fontsize=16)
ICE and PDP representations

Графики взаимодействия двух признаков

PDP для двух интересующих нас признаков позволяют визуализировать взаимодействия между ними. Однако построить и интерпретировать ICE для двух признаков непросто. Мы покажем вариант представления, доступный в from_estimator: это двумерная тепловая карта.

print("Computing partial dependence plots...")
features_info = {
    "features": ["temp", "humidity", ("temp", "humidity")],
    "kind": "average",
}
_, ax = plt.subplots(ncols=3, figsize=(10, 4), constrained_layout=True)
tic = time()
display = PartialDependenceDisplay.from_estimator(
    hgbdt_model,
    X_train,
    **features_info,
    ax=ax,
    **common_params,
)
print(f"done in {time() - tic:.3f}s")
_ = display.figure_.suptitle(
    "1-way vs 2-way of numerical PDP using gradient boosting", fontsize=16
)
1-way vs 2-way of numerical PDP using gradient boosting
Computing partial dependence plots...
done in 9.457s

График частичной зависимости по двум признакам показывает зависимость числа аренд велосипедов от совместных значений температуры и влажности. Мы отчетливо видим взаимодействие между этими признаками. При температуре выше 20 градусов Цельсия влажность влияет на число аренд велосипедов, причем это влияние, по-видимому, не зависит от температуры.

С другой стороны, при температуре ниже 20 градусов Цельсия и температура, и влажность непрерывно влияют на число аренд велосипедов.

Кроме того, наклон гребня, соответствующего порогу в 20 градусов Цельсия, сильно зависит от уровня влажности: в сухих условиях гребень крутой, а при влажности выше 70% и более влажных условиях — гораздо более пологий.

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

print("Computing partial dependence plots...")
features_info = {
    "features": ["temp", "humidity", ("temp", "humidity")],
    "kind": "average",
}
_, ax = plt.subplots(ncols=3, figsize=(10, 4), constrained_layout=True)
tic = time()
display = PartialDependenceDisplay.from_estimator(
    hgbdt_model_without_interactions,
    X_train,
    **features_info,
    ax=ax,
    **common_params,
)
print(f"done in {time() - tic:.3f}s")
_ = display.figure_.suptitle(
    "1-way vs 2-way of numerical PDP using gradient boosting", fontsize=16
)
1-way vs 2-way of numerical PDP using gradient boosting
Computing partial dependence plots...
done in 9.690s

Одномерные графики частичной зависимости для модели, которой запрещено учитывать взаимодействия признаков, показывают отдельные локальные всплески для каждого признака, особенно для признака «влажность». Эти всплески могут отражать ухудшение поведения модели, пытающейся компенсировать запрещенные взаимодействия за счет переобучения на отдельных обучающих наблюдениях. Обратите внимание, что качество предсказаний этой модели, измеренное на тестовой выборке, значительно хуже, чем у исходной модели без ограничений.

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

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

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

print("Computing partial dependence plots...")
features_info = {
    "features": ["season", "weather", ("season", "weather")],
    "kind": "average",
    "categorical_features": categorical_features,
}
_, ax = plt.subplots(ncols=3, figsize=(14, 6), constrained_layout=True)
tic = time()
display = PartialDependenceDisplay.from_estimator(
    hgbdt_model,
    X_train,
    **features_info,
    ax=ax,
    **common_params,
)

print(f"done in {time() - tic:.3f}s")
_ = display.figure_.suptitle(
    "1-way vs 2-way PDP of categorical features using gradient boosting", fontsize=16
)
1-way vs 2-way PDP of categorical features using gradient boosting
Computing partial dependence plots...
done in 0.534s

Трехмерное представление

Построим такой же график частичной зависимости для взаимодействия двух признаков, но на этот раз в трех измерениях.

# unused but required import for doing 3d projections with matplotlib < 3.2
import mpl_toolkits.mplot3d  # noqa: F401
import numpy as np

from sklearn.inspection import partial_dependence

fig = plt.figure(figsize=(5.5, 5))

features = ("temp", "humidity")
pdp = partial_dependence(
    hgbdt_model, X_train, features=features, kind="average", grid_resolution=10
)
XX, YY = np.meshgrid(pdp["grid_values"][0], pdp["grid_values"][1])
Z = pdp.average[0].T
ax = fig.add_subplot(projection="3d")
fig.add_axes(ax)

surf = ax.plot_surface(XX, YY, Z, rstride=1, cstride=1, cmap=plt.cm.BuPu, edgecolor="k")
ax.set_xlabel(features[0])
ax.set_ylabel(features[1])
fig.suptitle(
    "PD of number of bike rentals on\nthe temperature and humidity GBDT model",
    fontsize=16,
)
# pretty init view
ax.view_init(elev=22, azim=122)
clb = plt.colorbar(surf, pad=0.08, shrink=0.6, aspect=10)
clb.ax.set_title("Partial\ndependence")
plt.show()
PD of number of bike rentals on the temperature and humidity GBDT model, Partial dependence

Пользовательские точки для анализа

Ни в одном из предыдущих примеров не указывалось, _какие именно_ точки используются для построения графиков частичной зависимости. По умолчанию мы используем процентили, определенные по входному набору данных. В некоторых случаях полезно задать точные точки, для которых нужно получить предсказания модели. Например, если пользователь хочет проверить поведение модели на данных, отличающихся от обучающих, или сравнить две модели, обученные на немного разных данных. Параметр custom_values позволяет передать значения, для которых нужно получить предсказания модели. Он переопределяет параметры grid_resolution и percentiles. Вернемся к рассмотренному выше примеру с градиентным бустингом, но зададим пользовательские значения.

print("Computing partial dependence plots with custom evaluation values...")
tic = time()
_, ax = plt.subplots(ncols=2, figsize=(6, 4), sharey=True, constrained_layout=True)

features_info = {
    "features": ["temp", "humidity"],
    "kind": "both",
}

display = PartialDependenceDisplay.from_estimator(
    hgbdt_model,
    X_train,
    **features_info,
    ax=ax,
    **common_params,
    # we set custom values for temp feature -
    # all other features are evaluated based on the data
    custom_values={"temp": np.linspace(0, 40, 10)},
)
print(f"done in {time() - tic:.3f}s")
_ = display.figure_.suptitle(
    (
        "Partial dependence of the number of bike rentals\n"
        "for the bike rental dataset with a gradient boosting"
    ),
    fontsize=16,
)
Partial dependence of the number of bike rentals for the bike rental dataset with a gradient boosting
Computing partial dependence plots with custom evaluation values...
done in 0.728s

Общее время выполнения скрипта: (0 минут 30.470 секунд)

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_partial_dependence.ipynb

Download Python source code: plot_partial_dependence.py

Download zipped: plot_partial_dependence.zip

Похожие примеры

Расширенное построение графиков с частичной зависимостью

Создание признаков, связанных со временем

Основные нововведения scikit-learn 0.24

Основные нововведения scikit-learn 1.2

© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/inspection/plot_partial_dependence.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API