Проблема с иммутацией внутри пайплайна в Sklearn [закрыто]

Вопрос или проблема

Я пытался решить Серию игр Kaggle 2024 и столкнулся с некоторыми столбцами с пропущенными значениями. Для этого я попытался выполнить имитацию внутри конвейера. Но имитация не работает, я проверял это несколько раз, но не могу понять, почему это происходит.
Не могли бы вы помочь мне с этим?

import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OrdinalEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split

data = pd.read_csv('/kaggle/input/playground-series-s4e8/train.csv')
data.drop([
    'stem-root',
    'veil-type',
    'veil-color',
    'spore-print-color',
], axis = 1, inplace = True) #более 80% null
length = data.shape[0]

acceptable_cols = [col for col in data.columns
            if data[col].isnull().sum()/data.shape[0]*100 <= 1]
trainSet = data[acceptable_cols].copy().sample(frac = 0.1, random_state = 0)
X = trainSet.drop(['class', 'id'], axis = 1)
y = trainSet['class']

cat_cols = X.select_dtypes('object').columns
num_cols = list(set(X.columns) - set(cat_cols))  ###################Выбор числовых столбцов
transformations = ColumnTransformer(transformers = [
    ('oe', OrdinalEncoder(), cat_cols),
    ('impute & scale', Pipeline([
        ('impute', SimpleImputer()),   ############################# Импутер
        ('scaler', StandardScaler())
    ]), num_cols)
])

pipeline = Pipeline([
    ('transformations', transformations),
    ('gbr', GradientBoostingClassifier(verbose = True))
])

X_train, X_val, y_train, y_val = train_test_split(X, y)
pipeline.fit(X_train, y_train) ###########################строка, где возникает ошибка
pipeline.score(X_val, y_val)

Всегда отображается ошибка:

Input X содержит NaN.
GradientBoostingClassifier не принимает пропущенные значения, закодированные как NaN. Для контролируемого обучения вы можете рассмотреть возможность использования sklearn.ensemble.HistGradientBoostingClassifier и Regressor, которые принимают пропущенные значения, закодированные как NaN. В противном случае возможно предварительно обработать данные, например, используя трансформер импутера в конвейере или удалив образцы с пропущенными значениями. См. https://scikit-learn.org/stable/modules/impute.html Вы можете найти список всех оценщиков, которые обрабатывают значения NaN, на следующей странице: https://scikit-learn.org/stable/modules/impute.html#estimators-that-handle-nan-values

Спасибо заранее за помощь.

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

ohe = ColumnTransformer([('категориальный', OrdinalEncoder(), cat_cols)], remainder="passthrough")
simple_imputer = SimpleImputer()
std_scaler = ColumnTransformer([('числовой', StandardScaler(), num_cols)], remainder="passthrough")
clf = GradientBoostingClassifier(verbose=True)
pipe = Pipeline(steps = [('encoder', ohe),
                         ('imputer', simple_imputer),
                         ('scaler', std_scaler),
                         ('model', clf)])

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

std_scaler = ColumnTransformer([('числовой', StandardScaler(), [1, 7, 8])], remainder="passthrough")

где вы масштабируете столбцы с индексами 1, 7 и 8. Я не знаю конкретные индексы столбцов, которые вы хотите закодировать или масштабировать, поэтому вам просто нужно будет вставить их.

Ответ или решение

Проблема с потерянными значениями в конвейере sklearn может возникнуть по нескольким причинам, когда вы пытаетесь использовать имputation (восстановление) в процессе обработки данных. Давайте тщательно разберемся в вашем коде, чтобы понять, что именно могло пойти не так.

Основная проблема:

Ошибка, с которой вы столкнулись:

Input X contains NaN.
GradientBoostingClassifier does not accept missing values encoded as NaN natively...

указывает на то, что ваш конвейер не очищает данные от NaN-значений должным образом до передачи в классификатор.

Анализ вашего кода:

  1. Создание преобразователя столбцов (ColumnTransformer):
    В вашем коде вы используете ColumnTransformer, чтобы применить OrdinalEncoder к категориальным данным, а затем применяете SimpleImputer и StandardScaler в другой пайплайне для числовых данных. Однако вызов SimpleImputer не осуществляется правильно, так как у вас есть небольшой синтаксический вакуум в некоторой логике.

  2. Не совсем корректное использование Pipeline:
    Ваша текущая структура создает Pipeline внутри ColumnTransformer, что может усложнить процесс. Также у вас есть ошибка с использованием &amp;, что, скорее всего, является результатом неаккуратного копирования кода из HTML. Вам следует использовать просто &, если нужно.

Как исправить:

  1. Убедитесь, что применяете Импьютер перед передачей в классификатор. Импьюта должна выполняться для всех недостающих значений, прежде чем передавать данные для обучения модели.
  2. Измените ваш ColumnTransformer, чтобы использовать SimpleImputer правильно. Вместо использования вложенного пайплайна необходимо обработать численные и категориальные данные отдельно и затем соединить их.

Вот оптимизированная версия вашего кода:

import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OrdinalEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split

# Загрузка и предварительная обработка данных
data = pd.read_csv('/kaggle/input/playground-series-s4e8/train.csv')
data.drop(columns=['stem-root', 'veil-type', 'veil-color', 'spore-print-color'], inplace=True)

acceptable_cols = [col for col in data.columns if data[col].isnull().sum()/data.shape[0]*100 <= 1]
trainSet = data[acceptable_cols].copy().sample(frac=0.1, random_state=0)
X = trainSet.drop(['class', 'id'], axis=1)
y = trainSet['class']

# Определение столбцов
cat_cols = X.select_dtypes('object').columns.tolist()
num_cols = [col for col in X.columns if col not in cat_cols]

# Создание конвейера
pipeline = Pipeline(steps=[
    ('preprocessing', ColumnTransformer(transformers=[
        ('cat', Pipeline(steps=[
            ('imputer', SimpleImputer(strategy='most_frequent')),
            ('encoder', OrdinalEncoder())
        ]), cat_cols),

        ('num', Pipeline(steps=[
            ('imputer', SimpleImputer(strategy='mean')),
            ('scaler', StandardScaler())
        ]), num_cols)
    ])),
    ('classifier', GradientBoostingClassifier(verbose=True))
])

# Разделение на тренировочные и валидационные данные
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=0)

# Обучение модели
pipeline.fit(X_train, y_train)
score = pipeline.score(X_val, y_val)

print(f'Accuracy: {score:.2f}')

Объяснение изменений:

  1. Структура пайплайна: Теперь у вас есть отдельный пайплайн для обработки категориальных и числовых данных в ColumnTransformer.
  2. Импьютация: SimpleImputer теперь обрабатывает недостающие значения в отдельных пайплайнах для категориальных и числовых данных.
  3. Метод обработки: Подобраны стратегии импьютации, что может быть полезно в зависимости от типа ваших данных. Например, для категориальных значений используется стратегия 'most_frequent', а для числовых - 'mean'.

Заключение:

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

Оцените материал
Добавить комментарий

Капча загружается...