带有 SkLearn 管道的 GridSearch 无法正常工作
GridSearch with SkLearn Pipeline not working properly
我为列名以数字开头的特征子集实现了自定义 PCA,在 PCA 之后,将它们与其余特征连接起来。然后在网格搜索中实现一个 GBRT 模型作为 sklearn 管道。管道本身运行良好,但使用 GridSearch 时,它似乎每次都在获取数据的子集并发出错误。自定义 PCA 是:
class PartialPCA(BaseEstimator, TransformerMixin):
def __init__(self, n_components=0.9995, svd_solver='full', mask=None):
# mask should contain selected cols. Suppose it is boolean to avoid code overhead
self.n_components = n_components
self.svd_solver = svd_solver
self.mask = mask
def fit(self, X, y=None):
print(X.shape)
print(type(X))
X.to_csv('InitialX.csv')
print(X.isnull().values.any())
X.reset_index(inplace=True, drop=True)
self.remaining_cols = X[[i for i in X.columns if i[0].isdigit() is False]].copy()
self.pca = PCA(n_components=self.n_components, svd_solver=self.svd_solver)
mask = self.mask if self.mask is not None else slice(None)
self.pca.fit(X[mask])
return self
def transform(self, X, y=None):
mask = self.mask if self.mask is not None else slice(None)
pca_transformed = self.pca.transform(X[mask])
if self.mask is not None:
print(pca_transformed.shape)
col_no = pca_transformed.shape[1]
pca_transformed = pd.DataFrame(data=pca_transformed, columns=range(1, col_no + 1))
X = pd.concat(objs=(self.remaining_cols, pca_transformed), axis=1)
X.to_csv('X.csv')
print(X.isnull().values.any())
print(pca_transformed.isnull().values.any())
print(self.remaining_cols.isnull().values.any())
return X
else:
return pca_transformed
然后被
调用
mask = [i for i in trainPredTrans.columns if i[0].isdigit() is True]
pca = PartialPCA(n_components=0.9995, svd_solver='full', mask=mask)
print(pca)
gbrt = GradientBoostingRegressor(n_estimators=100, random_state=10)
pipe = Pipeline(steps=[('pca', pca), ('gbrt', gbrt)])
estimator = model_selection.GridSearchCV(pipe,param_grid=[dict(pca__svd_solver=['auto','full','arpack']),
dict(gbrt__learning_rate=[0.1, 0.2, 0.3, 0.4, 0.5],
gbrt__loss=["ls", "lad", "huber", "quantile"],
gbrt__max_depth=[3, 4, 5],
gbrt__min_samples_split=[2, 3, 4])])
print(estimator)
trainPredTrans.to_csv('trainPredTrans.csv')
estimator.fit(trainPredTrans, trainTarget.values.ravel())
输入列车数据帧的形状为 (1198, 1248) 但在函数内部,当我打印 X.shape 时,它是 (798, 1248) 并且在适合后它变成 (798, 97) 并且看起来再次迭代并给出一个错误,说输入有 nan 值,这是由于连接两个不同大小的数据帧而发生的(但 whch 应该具有相同的大小)。
我花了很多时间,但无法弄清楚问题所在以及为什么它在没有 gridsearch 的情况下似乎也能正常工作。似乎 Gridsearch 正在使用 gbrt 参数迭代 pca,这不应该发生
那是因为训练和测试数据的长度。 GridSearcCV 将根据 cv 参数将数据拆分为训练和测试。所以火车数据的长度会更多,保存到 self.remaining_cols
当测试数据到 transform()
时,您尝试将具有更多样本的原始 self.remaining_cols
附加到新数据因此新数据附加了 Nans
以匹配长度。
为了解决这个问题,我建议您将 self.remaining_cols
逻辑移至 transform()
而不是 fit()
。像这样:
...
...
def fit(self, X, y=None):
X.reset_index(inplace=True, drop=True)
self.pca = PCA(n_components=self.n_components, svd_solver=self.svd_solver)
mask = self.mask if self.mask is not None else slice(None)
self.pca.fit(X[mask])
return self
def transform(self, X, y=None):
mask = self.mask if self.mask is not None else slice(None)
X.reset_index(inplace=True, drop=True)
pca_transformed = self.pca.transform(X[mask])
if self.mask is not None:
col_no = pca_transformed.shape[1]
pca_transformed = pd.DataFrame(data=pca_transformed, columns=range(1, col_no + 1))
self.remaining_cols = X[[i for i in X.columns if i[0].isdigit() is False]].copy()
X = pd.concat(objs=(self.remaining_cols, pca_transformed), axis=1)
return X
else:
return pca_transformed
此外,要执行此类操作,其中仅选择列的子集进行某些处理,我建议您查看 FeatureUnion 和 ItemSelector,如本示例中所述:
注:我观察到你将参数space定义为两个字典。您不应该认为将字典列表发送到 GridSearchCV 会使它们成为排他性的。这意味着它们将单独计算,而不是相互结合。
我为列名以数字开头的特征子集实现了自定义 PCA,在 PCA 之后,将它们与其余特征连接起来。然后在网格搜索中实现一个 GBRT 模型作为 sklearn 管道。管道本身运行良好,但使用 GridSearch 时,它似乎每次都在获取数据的子集并发出错误。自定义 PCA 是:
class PartialPCA(BaseEstimator, TransformerMixin):
def __init__(self, n_components=0.9995, svd_solver='full', mask=None):
# mask should contain selected cols. Suppose it is boolean to avoid code overhead
self.n_components = n_components
self.svd_solver = svd_solver
self.mask = mask
def fit(self, X, y=None):
print(X.shape)
print(type(X))
X.to_csv('InitialX.csv')
print(X.isnull().values.any())
X.reset_index(inplace=True, drop=True)
self.remaining_cols = X[[i for i in X.columns if i[0].isdigit() is False]].copy()
self.pca = PCA(n_components=self.n_components, svd_solver=self.svd_solver)
mask = self.mask if self.mask is not None else slice(None)
self.pca.fit(X[mask])
return self
def transform(self, X, y=None):
mask = self.mask if self.mask is not None else slice(None)
pca_transformed = self.pca.transform(X[mask])
if self.mask is not None:
print(pca_transformed.shape)
col_no = pca_transformed.shape[1]
pca_transformed = pd.DataFrame(data=pca_transformed, columns=range(1, col_no + 1))
X = pd.concat(objs=(self.remaining_cols, pca_transformed), axis=1)
X.to_csv('X.csv')
print(X.isnull().values.any())
print(pca_transformed.isnull().values.any())
print(self.remaining_cols.isnull().values.any())
return X
else:
return pca_transformed
然后被
调用mask = [i for i in trainPredTrans.columns if i[0].isdigit() is True]
pca = PartialPCA(n_components=0.9995, svd_solver='full', mask=mask)
print(pca)
gbrt = GradientBoostingRegressor(n_estimators=100, random_state=10)
pipe = Pipeline(steps=[('pca', pca), ('gbrt', gbrt)])
estimator = model_selection.GridSearchCV(pipe,param_grid=[dict(pca__svd_solver=['auto','full','arpack']),
dict(gbrt__learning_rate=[0.1, 0.2, 0.3, 0.4, 0.5],
gbrt__loss=["ls", "lad", "huber", "quantile"],
gbrt__max_depth=[3, 4, 5],
gbrt__min_samples_split=[2, 3, 4])])
print(estimator)
trainPredTrans.to_csv('trainPredTrans.csv')
estimator.fit(trainPredTrans, trainTarget.values.ravel())
输入列车数据帧的形状为 (1198, 1248) 但在函数内部,当我打印 X.shape 时,它是 (798, 1248) 并且在适合后它变成 (798, 97) 并且看起来再次迭代并给出一个错误,说输入有 nan 值,这是由于连接两个不同大小的数据帧而发生的(但 whch 应该具有相同的大小)。 我花了很多时间,但无法弄清楚问题所在以及为什么它在没有 gridsearch 的情况下似乎也能正常工作。似乎 Gridsearch 正在使用 gbrt 参数迭代 pca,这不应该发生
那是因为训练和测试数据的长度。 GridSearcCV 将根据 cv 参数将数据拆分为训练和测试。所以火车数据的长度会更多,保存到 self.remaining_cols
当测试数据到 transform()
时,您尝试将具有更多样本的原始 self.remaining_cols
附加到新数据因此新数据附加了 Nans
以匹配长度。
为了解决这个问题,我建议您将 self.remaining_cols
逻辑移至 transform()
而不是 fit()
。像这样:
...
...
def fit(self, X, y=None):
X.reset_index(inplace=True, drop=True)
self.pca = PCA(n_components=self.n_components, svd_solver=self.svd_solver)
mask = self.mask if self.mask is not None else slice(None)
self.pca.fit(X[mask])
return self
def transform(self, X, y=None):
mask = self.mask if self.mask is not None else slice(None)
X.reset_index(inplace=True, drop=True)
pca_transformed = self.pca.transform(X[mask])
if self.mask is not None:
col_no = pca_transformed.shape[1]
pca_transformed = pd.DataFrame(data=pca_transformed, columns=range(1, col_no + 1))
self.remaining_cols = X[[i for i in X.columns if i[0].isdigit() is False]].copy()
X = pd.concat(objs=(self.remaining_cols, pca_transformed), axis=1)
return X
else:
return pca_transformed
此外,要执行此类操作,其中仅选择列的子集进行某些处理,我建议您查看 FeatureUnion 和 ItemSelector,如本示例中所述:
注:我观察到你将参数space定义为两个字典。您不应该认为将字典列表发送到 GridSearchCV 会使它们成为排他性的。这意味着它们将单独计算,而不是相互结合。