我想用sklearn创建一个管道,包括一些预处理步骤和最后一个步骤,其中包含适合数据的模型。我使用这个管道通过交叉验证获得分数。稍后,我想使用GridSearchCV
中的管道进行参数优化
到目前为止,预处理步骤包括:
ColumnsRemoval()
类删除一些列的一个步骤StandardScaler()
数字特征和OneHotEncoder()
分类特征李>问题是我得到的分数都是nan
。它运行得相当快,似乎传递到模型中的是空数组:
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.model_selection import cross_validate
import numpy as np
# Create random dataframe
num_data = np.random.random_sample((5,4))
cat_data = ['good','bad','fair','excellent','bad']
col_list_stack = ['SalePrice','Id','TotalBsmtSF','GrdLivArea']
data = pd.DataFrame(num_data, columns = col_list_stack)
data['Quality'] = cat_data
X_train = data.drop(labels = ['SalePrice'], axis = 1)
y_train = data['SalePrice']
#------------------------------------------------------------#
# create a custom transformer to remove columns
class ColumnsRemoval(BaseEstimator, TransformerMixin):
def __init__(self, skip = False, remove_cols = ['Id','TotalBsmtSF']):
self._remove_cols = remove_cols
self._skip = skip
def fit(self, X, y = None):
return self
def transform(self, X, y = None):
if not self._skip:
return X.drop(labels = self._remove_cols,axis = 1)
else:
return X
#------------------------------------------------------------#
# PIPELINE and cross-validation
# Preprocessing steps common to numerical and categorical data
preprocessor_common = Pipeline(steps=[
('remove_features', ColumnsRemoval())])
# Separated preprocessing steps
numeric_transformer = Pipeline(steps=[
('scaler', StandardScaler())])
categorical_transformer = Pipeline(steps=[
('onehot', OneHotEncoder(handle_unknown='ignore'))])
preprocessor_by_cat = ColumnTransformer(
transformers=[
('num', numeric_transformer, ['GrdLivArea']),
('cat', categorical_transformer, ['Quality'])], remainder = 'passthrough')
# Full pipeline with model
pipe = Pipeline(steps = [('preprocessor_common', preprocessor_common),
('preprocessor_by_cat', preprocessor_by_cat),
('model', LinearRegression())])
# Use cross validation to obtain scores
scores = cross_validate(pipe, X_train, y_train,
scoring = ["neg_mean_squared_error","r2"], cv = 4)
我尝试了以下方法:
preprocessor_by_cat
+model
步骤时,我会得到分数值。使用preprocessor_common
+model
步骤也会给出nan
分数preprocessor_common
+preprocessor_by_cat
)中执行两个预处理步骤,.fit_transform()
训练数据,然后将其发送到cross_validate(),大致如下:pipe = Pipeline(steps = [('preprocessor_common', preprocessor_common),
('preprocessor_by_cat', preprocessor_by_cat),
])
X_processed = pipe.fit_transform(X_train)
# Use cross validation to obtain scores
scores = cross_validate(LinearRegression(), X_processed, y_train,
scoring = ["neg_mean_squared_error","r2"], cv = 4)
根据我的理解,在管道中进行预处理或对管道进行预处理+模型是相同的,这就是为什么我认为获取NaN
值是一个问题
我希望问题很清楚,如果你能做到这一点,恭喜你:)
TL;DR
您需要重新定义自定义
ColumnsRemoval
的__init()__
函数,因为传递Python列表作为默认值将导致错误。一种可能的解决办法:这样,您的管道就可以按预期工作了
背景
我运行了您的MWE,发现以下错误:
它与您的自定义
ColumnsRemoval
的以下行相关:这引发了错误:
在将标准Python列表传递给
drop()
函数时,这似乎是一个已知的问题,本post将对此进行讨论。解决方案是传递numpy
数组或pandas
索引对象。我提出的另一个解决方案是,不要在函数定义中为remove_cols
设置默认值,而是在函数体中分配它。这同样有效似乎没有人真正知道为什么会发生这种情况。很抱歉,我无法详细说明实际原因(如果有人能补充,我会非常高兴)。但问题应该得到解决
我找到了问题所在。我已经做了一些进一步的测试,还使用了
float
而不是列表作为默认值如here所述,在实例部分下:
因此,我所做的是使用与在
__init__()
中传递的参数名称相同的对象属性名称,现在一切正常。例如:使用
self._threshold
(注意threshold
之前的_
)有一种奇怪的行为,在某些情况下,对象与提供的值(或默认值)一起使用,但在其他情况下self._threshold
被设置为None
。这也允许使用list
作为默认值来通过__init__()
(尽管应该避免使用list
作为默认值,有关详细信息,请参阅afsharov的回答)相关问题 更多 >
编程相关推荐