Примечание
Перейти в конец чтобы скачать полный пример кода или запустить этот пример в браузере через JupyterLite или Binder.
Внутренняя перекрестная подгонка Target Encoder#
The TargetEncoder заменяет каждую категорию категориального признака на сжатое среднее целевой переменной для этой категории. Этот метод полезен в случаях, когда существует сильная связь между категориальным признаком и целевой переменной. Чтобы предотвратить переобучение, TargetEncoder.fit_transform в качестве аргументов ключевых слов. кросс-фиттинг схема кодирования обучающих данных для использования последующей моделью. Эта схема включает разделение данных на k фолдов и кодирование каждого фолда с использованием кодировок, изученных с помощью другие k-1 блоков.
В этом примере мы демонстрируем важность процедуры
перекрестного подбора для предотвращения переобучения.
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
Создать синтетический набор данных#
Для этого примера мы создаем набор данных с тремя категориальными признаками:
информативный признак со средней кардинальностью (“informative”)
неинформативный признак со средней кардинальностью (“перемешанный”)
неинформативный признак с высокой кардинальностью («near_unique»)
Сначала мы генерируем информативный признак:
import numpy as np
from sklearn.preprocessing import KBinsDiscretizer
n_samples = 50_000
rng = np.random.RandomState(42)
y = rng.randn(n_samples)
noise = 0.5 * rng.randn(n_samples)
n_categories = 100
kbins = KBinsDiscretizer(
n_bins=n_categories,
encode="ordinal",
strategy="uniform",
random_state=rng,
subsample=None,
)
X_informative = kbins.fit_transform((y + noise).reshape(-1, 1))
# Remove the linear relationship between y and the bin index by permuting the
# values of X_informative:
permuted_categories = rng.permutation(n_categories)
X_informative = permuted_categories[X_informative.astype(np.int32)]
Неинформативный признак со средней кардинальностью генерируется путем перестановки информативного признака и удаления связи с целевой переменной:
X_shuffled = rng.permutation(X_informative)
Неинформативный признак с высокой кардинальностью генерируется так, чтобы он был независим от целевой переменной. Мы покажем, что целевое кодирование без
кросс-фиттинг вызовет катастрофическое переобучение для последующего
регрессора. Эти признаки высокой мощности по сути являются уникальными идентификаторами
для образцов, которые обычно следует удалять из наборов данных машинного обучения.
В этом примере мы генерируем их, чтобы показать, как TargetEncoderпо умолчанию
кросс-фиттинг это поведение автоматически смягчает проблему переобучения.
X_near_unique_categories = rng.choice(
int(0.9 * n_samples), size=n_samples, replace=True
).reshape(-1, 1)
Наконец, мы собираем набор данных и выполняем разделение на обучающую и тестовую выборки:
import pandas as pd
from sklearn.model_selection import train_test_split
X = pd.DataFrame(
np.concatenate(
[X_informative, X_shuffled, X_near_unique_categories],
axis=1,
),
columns=["informative", "shuffled", "near_unique"],
)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
Обучение Ridge Regressor#
В этом разделе мы обучаем гребневый регрессор на наборе данных с кодированием и без него,
и исследуем влияние кодировщика целевой переменной с внутренним механизмом и без него. кросс-фиттингВо-первых, мы видим, что модель Ridge, обученная на исходных признаках, будет иметь низкую производительность. Это связано с тем, что мы изменили порядок информативных признаков, что означает X_informative не информативен, когда
сырой:
import sklearn
from sklearn.linear_model import Ridge
# Configure transformers to always output DataFrames
sklearn.set_config(transform_output="pandas")
ridge = Ridge(alpha=1e-6, solver="lsqr", fit_intercept=False)
raw_model = ridge.fit(X_train, y_train)
print("Raw Model score on training set: ", raw_model.score(X_train, y_train))
print("Raw Model score on test set: ", raw_model.score(X_test, y_test))
Raw Model score on training set: 0.0049896314219657345
Raw Model score on test set: 0.0045776215814927745
Далее мы создаем конвейер с кодировщиком целевой переменной и моделью гребневой регрессии. Конвейер
использует TargetEncoder.fit_transform который использует кросс-фиттинг. Мы видим, что модель хорошо подходит к данным и обобщается на тестовую выборку:
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import TargetEncoder
model_with_cf = make_pipeline(TargetEncoder(random_state=0), ridge)
model_with_cf.fit(X_train, y_train)
print("Model with CF on train set: ", model_with_cf.score(X_train, y_train))
print("Model with CF on test set: ", model_with_cf.score(X_test, y_test))
Model with CF on train set: 0.8000184677460304
Model with CF on test set: 0.7927845601690917
Коэффициенты линейной модели показывают, что большая часть веса приходится на признак в столбце с индексом 0, который является информативным признаком
import matplotlib.pyplot as plt
import pandas as pd
plt.rcParams["figure.constrained_layout.use"] = True
coefs_cf = pd.Series(
model_with_cf[-1].coef_, index=model_with_cf[-1].feature_names_in_
).sort_values()
ax = coefs_cf.plot(kind="barh")
_ = ax.set(
title="Target encoded with cross fitting",
xlabel="Ridge coefficient",
ylabel="Feature",
)

В то время как TargetEncoder.fit_transform использует внутренний
кросс-фиттинг схему для изучения кодировок обучающей выборки,
TargetEncoder.fit с последующим TargetEncoder.transform не делает.
Он использует полный обучающий набор для изучения кодировок и преобразования
категориальных признаков. Таким образом, мы можем использовать TargetEncoder.fit с последующим
TargetEncoder.transform для отключения кросс-фиттинг. Это
кодирование затем передается в модель гребневой регрессии.
target_encoder = TargetEncoder(random_state=0)
target_encoder.fit(X_train, y_train)
X_train_no_cf_encoding = target_encoder.transform(X_train)
X_test_no_cf_encoding = target_encoder.transform(X_test)
model_no_cf = ridge.fit(X_train_no_cf_encoding, y_train)
Мы оцениваем модель, которая не использовала кросс-фиттинг при кодировании и увидеть, что он переобучается:
print(
"Model without CF on training set: ",
model_no_cf.score(X_train_no_cf_encoding, y_train),
)
print(
"Model without CF on test set: ",
model_no_cf.score(
X_test_no_cf_encoding,
y_test,
),
)
Model without CF on training set: 0.858486250088675
Model without CF on test set: 0.6338211367102257
Модель ridge переобучается, потому что она присваивает гораздо больший вес неинформативным признакам с чрезвычайно высокой кардинальностью ("near_unique") и средней кардинальностью ("shuffled"), чем когда модель использовала кросс-фиттинг для кодирования признаков.
coefs_no_cf = pd.Series(
model_no_cf.coef_, index=model_no_cf.feature_names_in_
).sort_values()
ax = coefs_no_cf.plot(kind="barh")
_ = ax.set(
title="Target encoded without cross fitting",
xlabel="Ridge coefficient",
ylabel="Feature",
)

Заключение#
Этот пример демонстрирует важность TargetEncoderвнутренний
кросс-фиттинг. Важно использовать
TargetEncoder.fit_transform для кодирования обучающих данных перед передачей их
в модель машинного обучения. Когда TargetEncoder является частью
Pipeline и конвейер обучен, конвейер
корректно вызовет TargetEncoder.fit_transform и использовать
кросс-фиттинг при кодировании обучающих данных.
Общее время выполнения скрипта: (0 минут 0.320 секунд)
Связанные примеры
Поддержка категориальных признаков в градиентном бустинге
HuberRegressor против Ridge на наборе данных с сильными выбросами