如何让 GridSearchCV 在我的管道中使用自定义转换器?
How do I make GridSeachCV work with a custom transformer in my pipeline?
如果我排除我的自定义转换器,GridSearchCV 运行正常,但是,它会出错。
这是一个假数据集:
import pandas
import numpy
from sklearn_pandas import DataFrameMapper
from sklearn_pandas import cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.grid_search import GridSearchCV
from sklearn.base import TransformerMixin
from sklearn.preprocessing import LabelBinarizer
from sklearn.ensemble import RandomForestClassifier
import sklearn_pandas
from sklearn.preprocessing import MinMaxScaler
df = pandas.DataFrame({"Letter":["a","b","c","d","a","b","c","d","a","b","c","d","a","b","c","d"],
"Number":[1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4],
"Label":["G","G","B","B","G","G","B","B","G","G","B","B","G","G","B","B"]})
class MyTransformer(TransformerMixin):
def transform(self, x, **transform_args):
x["Number"] = x["Number"].apply(lambda row: row*2)
return x
def fit(self, x, y=None, **fit_args):
return self
x_train = df
y_train = x_train.pop("Label")
mapper = DataFrameMapper([
("Number", MinMaxScaler()),
("Letter", LabelBinarizer()),
])
pipe = Pipeline([
("custom", MyTransformer()),
("mapper", mapper),
("classifier", RandomForestClassifier()),
])
param_grid = {"classifier__min_samples_split":[10,20], "classifier__n_estimators":[2,3,4]}
model_grid = sklearn_pandas.GridSearchCV(pipe, param_grid, verbose=2, scoring="accuracy")
model_grid.fit(x_train, y_train)
错误是
list indices must be integers, not str
当我的管道中有自定义转换器时,如何使 GridSearchCV 工作?
简短版本:pandas 和 scikit-learn 的 交叉验证方法 不喜欢那样说话(在我的版本中,0.15);这可以简单地通过将 scikit-learn 更新为 0.16/stable 或 0.17/dev 来解决。
GridSearchCV
class 验证数据并将其转换为数组(以便它可以正确执行 CV 拆分)。因此,您无法在内置交叉验证循环中使用 Pandas DataFrame 功能。
如果您想做这种事情,您将不得不制作自己的交叉验证例程,不执行验证。
编辑:这是我使用 scikit-learn 的交叉验证例程的经验。这就是 sklearn-pandas 提供 cross_val_score 的原因。但是,据我所知,GridSearchCV 并没有被 sklearn-pandas 专门化;您对它的导入不小心导入了默认的 sklearn 版本。因此,您可能必须使用 ParameterGrid 和 sklearn-pandas 的 cross_val_score.
来实现自己的网格搜索
我知道这个答案来得太晚了,但我遇到了与 sklearn 和 BaseSearchCV
导数 classes 相同的行为。这个问题实际上似乎源于 sklearn cross_validation 模块中的 _PartitionIterator
class,因为它假设每个 TransformerMixin
class 中发出的所有内容管道将是类似数组的,因此它会生成索引切片,用于以类似数组的方式索引传入的 X
args。这是 __iter__
方法:
def __iter__(self):
ind = np.arange(self.n)
for test_index in self._iter_test_masks():
train_index = np.logical_not(test_index)
train_index = ind[train_index]
test_index = ind[test_index]
yield train_index, test_index
并且 BaseSearchCV
网格搜索元 class 调用 cross_validation 的 _fit_and_score
,它使用名为 safe_split
的方法。这是相关的行:
X_subset = [X[idx] for idx in indices]
如果 X 是一个 pandas 数据帧,这绝对会产生意想不到的结果,你从你的 transform
函数发出。
我发现有两种方法可以解决此问题:
确保 return 来自你的转换器的数组:
return x.as_matrix()
这是一个黑客。如果转换器管道要求下一个转换器的输入是 DataFrame,就像我的情况一样,您可以编写一个与 sklearn grid_search
模块基本相同的实用程序脚本,但包括一些巧妙的验证方法在 BaseSearchCV
class:
的 _fit
方法中调用
def _validate_X(X):
"""Returns X if X isn't a pandas frame, otherwise
the underlying matrix in the frame. """
return X if not isinstance(X, pd.DataFrame) else X.as_matrix()
def _validate_y(y):
"""Returns y if y isn't a series, otherwise the array"""
if y is None:
return y
# if it's a series
elif isinstance(y, pd.Series):
return np.array(y.tolist())
# if it's a dataframe:
elif isinstance(y, pd.DataFrame):
# check it's X dims
if y.shape[1] > 1:
raise ValueError('matrix provided as y')
return y[y.columns[0]].tolist()
# bail and let the sklearn function handle validation
return y
如果我排除我的自定义转换器,GridSearchCV 运行正常,但是,它会出错。 这是一个假数据集:
import pandas
import numpy
from sklearn_pandas import DataFrameMapper
from sklearn_pandas import cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.grid_search import GridSearchCV
from sklearn.base import TransformerMixin
from sklearn.preprocessing import LabelBinarizer
from sklearn.ensemble import RandomForestClassifier
import sklearn_pandas
from sklearn.preprocessing import MinMaxScaler
df = pandas.DataFrame({"Letter":["a","b","c","d","a","b","c","d","a","b","c","d","a","b","c","d"],
"Number":[1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4],
"Label":["G","G","B","B","G","G","B","B","G","G","B","B","G","G","B","B"]})
class MyTransformer(TransformerMixin):
def transform(self, x, **transform_args):
x["Number"] = x["Number"].apply(lambda row: row*2)
return x
def fit(self, x, y=None, **fit_args):
return self
x_train = df
y_train = x_train.pop("Label")
mapper = DataFrameMapper([
("Number", MinMaxScaler()),
("Letter", LabelBinarizer()),
])
pipe = Pipeline([
("custom", MyTransformer()),
("mapper", mapper),
("classifier", RandomForestClassifier()),
])
param_grid = {"classifier__min_samples_split":[10,20], "classifier__n_estimators":[2,3,4]}
model_grid = sklearn_pandas.GridSearchCV(pipe, param_grid, verbose=2, scoring="accuracy")
model_grid.fit(x_train, y_train)
错误是
list indices must be integers, not str
当我的管道中有自定义转换器时,如何使 GridSearchCV 工作?
简短版本:pandas 和 scikit-learn 的 交叉验证方法 不喜欢那样说话(在我的版本中,0.15);这可以简单地通过将 scikit-learn 更新为 0.16/stable 或 0.17/dev 来解决。
GridSearchCV
class 验证数据并将其转换为数组(以便它可以正确执行 CV 拆分)。因此,您无法在内置交叉验证循环中使用 Pandas DataFrame 功能。
如果您想做这种事情,您将不得不制作自己的交叉验证例程,不执行验证。
编辑:这是我使用 scikit-learn 的交叉验证例程的经验。这就是 sklearn-pandas 提供 cross_val_score 的原因。但是,据我所知,GridSearchCV 并没有被 sklearn-pandas 专门化;您对它的导入不小心导入了默认的 sklearn 版本。因此,您可能必须使用 ParameterGrid 和 sklearn-pandas 的 cross_val_score.
来实现自己的网格搜索我知道这个答案来得太晚了,但我遇到了与 sklearn 和 BaseSearchCV
导数 classes 相同的行为。这个问题实际上似乎源于 sklearn cross_validation 模块中的 _PartitionIterator
class,因为它假设每个 TransformerMixin
class 中发出的所有内容管道将是类似数组的,因此它会生成索引切片,用于以类似数组的方式索引传入的 X
args。这是 __iter__
方法:
def __iter__(self):
ind = np.arange(self.n)
for test_index in self._iter_test_masks():
train_index = np.logical_not(test_index)
train_index = ind[train_index]
test_index = ind[test_index]
yield train_index, test_index
并且 BaseSearchCV
网格搜索元 class 调用 cross_validation 的 _fit_and_score
,它使用名为 safe_split
的方法。这是相关的行:
X_subset = [X[idx] for idx in indices]
如果 X 是一个 pandas 数据帧,这绝对会产生意想不到的结果,你从你的 transform
函数发出。
我发现有两种方法可以解决此问题:
确保 return 来自你的转换器的数组:
return x.as_matrix()
这是一个黑客。如果转换器管道要求下一个转换器的输入是 DataFrame,就像我的情况一样,您可以编写一个与 sklearn
的grid_search
模块基本相同的实用程序脚本,但包括一些巧妙的验证方法在BaseSearchCV
class:_fit
方法中调用def _validate_X(X): """Returns X if X isn't a pandas frame, otherwise the underlying matrix in the frame. """ return X if not isinstance(X, pd.DataFrame) else X.as_matrix() def _validate_y(y): """Returns y if y isn't a series, otherwise the array""" if y is None: return y # if it's a series elif isinstance(y, pd.Series): return np.array(y.tolist()) # if it's a dataframe: elif isinstance(y, pd.DataFrame): # check it's X dims if y.shape[1] > 1: raise ValueError('matrix provided as y') return y[y.columns[0]].tolist() # bail and let the sklearn function handle validation return y