我使用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)
这就是我在火车和测试数据上使用它的全部正确方法!但是当我尝试预测新数据时,我得到了错误答案
pipeline.predict(pd.DataFrame({ 'y': [3, 8], 'a': ['a', 'b' ], 'b': [3, 6],}))
帮助发现错误!错误一定很简单,但是我的眼睛在游泳。问题必须出在MyLEncoder类中。我必须改变什么?
答案 0 :(得分:1)
您正在呼叫
encc = enc.fit(np.asarray(X), y)
在transform()
的{{1}}方法中。
所以这里有两个问题:
1)您的MyLEncoder
只记住传递到LeaveOneOutEncoder
的{{1}}的最后数据,而忘记先前的数据。
2)在拟合期间,transform
要求MyLEncoder
出现。但这在调用LeaveOneOutEncoder
y
的预测过程中不会出现。
3)当前您所在的行:
MyLEncoder
运气不错,因为您的transform()
是相同的,并且在调用pipeline.predict(X)
X
时,您已经定义了MyLEncoder
以便使用。但这就是错误的。
4)无关的事物(可能不会将此称为错误)。当您这样做时:
transform()
y
仅需要pipeline.predict(pd.DataFrame({ 'y': [3, 8], 'a': ['a', 'b' ], 'b': [3, 6],}))
,而不需要pipeline.predict()
。但是您也在其中发送X
。当前这不是问题,因为在管道中您仅使用y
列并丢弃所有信息,但是在复杂的设置中,这可能会漏掉,而y
列中的数据将用作功能(a
数据)会给您错误的结果。
要解决此问题,请将您的y
更改为:
X
现在,当您执行此操作时:
MyLEncoder
您不会收到任何错误,但是仍然要注意第4点,我希望您这样做:
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)
使训练时间中使用的X和预测时间中使用的new_X出现相同。
答案 1 :(得分:1)
我已经完成了以下操作
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
的{{1}}行中更改了encc = enc.fit(np.asarray(lb), y)
。