LeaveOneOutEncoder в sklearn.pipeline - PullRequest
       9

LeaveOneOutEncoder в sklearn.pipeline

0 голосов
/ 16 сентября 2018

Я создаю конвейер с помощью LeaveOneOutEncoder.Конечно, я использую игрушечный пример. Leave One Out предназначен для преобразования категориальных переменных

import pandas as pd
import numpy as np
from sklearn import preprocessing
import sklearn
from sklearn.pipeline import Pipeline
from sklearn.pipeline import FeatureUnion
from category_encoders import  LeaveOneOutEncoder
from sklearn import linear_model
from sklearn.base import BaseEstimator, TransformerMixin

df= pd.DataFrame({ 'y': [1,2,3,4,5,6,7,8], 'a': ['a', 'b','a', 'b','a', 'b','a', 'b' ], 'b': [5,5,3,4,8,6,7,3],})

class ItemSelector(BaseEstimator, TransformerMixin):
def __init__(self, key):
    self.key = key
def fit(self, x, y=None):
    return self
def transform(self, data_dict):
    return data_dict[self.key]

class MyLEncoder(BaseEstimator, TransformerMixin):
def transform(self, X, **fit_params):
    enc = LeaveOneOutEncoder()
    encc = enc.fit(np.asarray(X), y)
    enc_data = encc.transform(np.asarray(X))
    return enc_data
def fit_transform(self, X,y=None,  **fit_params):
    self.fit(X,y,  **fit_params)
    return self.transform(X)
def fit(self, X, y, **fit_params):
    return self


X = df[['a', 'b']]
y = df['y']

regressor = linear_model.SGDRegressor()

pipeline = Pipeline([

    # Use FeatureUnion to combine the features
    ('union', FeatureUnion(
        transformer_list=[


             # categorical
            ('categorical', Pipeline([
                ('selector', ItemSelector(key='a')),
                ('one_hot', MyLEncoder())

            ])),
             # year

        ])),
    # Use a regression
    ('model_fitting', linear_model.SGDRegressor()),
])

pipeline.fit(X, y)
pipeline.predict(X)

Это ВСЕ правильно, когда я использую его в данных поезда и теста!Но когда я пытаюсь предсказать новые данные, я получаю erorr

pipeline.predict(pd.DataFrame({ 'y': [3, 8], 'a': ['a', 'b' ], 'b': [3, 6],}))

, помогающую найти ошибку!Ошибка должна быть простой, но мои глаза плавают.И проблема должна быть в классе MyLEncoder.Что я должен изменить?

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Я сделал, как показано ниже

lb = df['a']
class MyLEncoder(BaseEstimator, TransformerMixin):
def transform(self, X, **fit_params):
    enc = LeaveOneOutEncoder()
    encc = enc.fit(np.asarray(lb), y)
    enc_data = encc.transform(np.asarray(X))

    return enc_data

def fit_transform(self, X,y=None,  **fit_params):
    self.fit(X,y,  **fit_params)
    return self.transform(X)

def fit(self, X, y, **fit_params):
    return self

Итак, я изменил X в строке encc = enc.fit(np.asarray(lb), y) на lb.

0 голосов
/ 19 сентября 2018

Вы звоните

encc = enc.fit(np.asarray(X), y)

в методе transform() из MyLEncoder.

Итак, здесь есть пара проблем:

1) Ваш LeaveOneOutEncoder запоминает только последние данные, переданные в transform из MyLEncoder, и забывает предыдущие данные.

2) Во время установки LeaveOneOutEncoder требуется наличие y.Но это не будет присутствовать во время предсказания, когда вызывается MyLEncoder transform().

3) В настоящее время ваша строка:

pipeline.predict(X)

работает просто по счастливой случайности, потому что ваш Xто же самое, и когда вызывается MyLEncoder transform(), вы уже определили y, поэтому он используется.Но это просто неправильно.

4) Несвязанная вещь (нельзя назвать это ошибкой).Когда вы делаете это:

pipeline.predict(pd.DataFrame({ 'y': [3, 8], 'a': ['a', 'b' ], 'b': [3, 6],}))

pipeline.predict() требует только X, а не y.Но вы также отправляете y.В настоящее время это не проблема, поскольку в конвейере вы используете только столбец a и выбрасываете всю информацию, но, возможно, в сложных настройках это может ускользнуть, и данные, представленные в столбце y, будут использоваться в качестве функций (X data), который затем даст вам неправильные результаты.

Чтобы решить эту проблему, измените MyLEncoder на:

class MyLEncoder(BaseEstimator, TransformerMixin):

    # Save the enc during fitting
    def fit(self, X, y, **fit_params):
        enc = LeaveOneOutEncoder()
        self.enc = enc.fit(np.asarray(X), y)

        return self

    # Here, no new learning should be done, so never call fit() inside this
    # Only use the already saved enc here
    def transform(self, X, **fit_params):

        enc_data = self.enc.transform(np.asarray(X))
        return enc_data

    # No need to define this function, if you are not doing any optimisation in it.
    # It will be automatically inherited from TransformerMixin
    # I have only kept it here, because you kept it.
    def fit_transform(self, X,y=None, **fit_params):
        self.fit(X, y, **fit_params)
        return self.transform(X)

Теперь, когда вы сделаете это:

pipeline.predict(pd.DataFrame({ 'y': [3, 8], 'a': ['a', 'b' ], 'b': [3, 6],}))

Вы не получите никакой ошибки, но все же, как сказано в пункте 4, я бы хотел, чтобы вы сделали что-то вроде этого:

new_df = pd.DataFrame({ 'y': [3, 8], 'a': ['a', 'b' ], 'b': [3, 6],})

new_X = new_df[['a', 'b']]
new_y = new_df['y']

pipeline.predict(new_X)

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

...