GridSearch с SkLearn Pipeline не работает должным образом - PullRequest
0 голосов
/ 03 июля 2018

Я реализовал пользовательский 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, которые произошли из-за конкатенации двух кадров данных разных размеров (но которые должны иметь одинаковый размер). Я провел много часов, но не мог понять проблему и почему она, казалось бы, работает без gridsearch. Похоже, что Gridsearch использует параметры gbrt для итерации по pca, что не должно происходить

1 Ответ

0 голосов
/ 03 июля 2018

Это из-за длины поезда и данных испытаний. 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, как описано в этом примере:

Примечание: Я заметил, что вы определили пространство параметров как два диктанта. Вам не следует, что отправка списка диктовок в GridSearchCV сделает их эксклюзивными. Это означает, что они будут рассчитываться отдельно, а не в сочетании друг с другом.

...