GridSearchCV генерирует разные наилучшие параметры, только изменяя порядок настройки param_grid

#python #scikit-learn #gridsearchcv

#питон #scikit-учиться #gridsearchcv

Вопрос:

Я использую GridSearchCV, чтобы найти наилучшую настройку параметров моего оценщика sklearn.pipeline. Конвейер состоит из преобразования данных, уменьшения размера UMAP и кластеризации Kmeans. Окончательные результаты кластеризации Kmeans оцениваются с помощью silhouette_score. Я попытался проверить правильность работы всего конвейера / GridSearchCV, изменив только порядок параметров в param_grid (например, измените ‘уменьшить__n_соседей’: (5, 10) на ‘уменьшить__n_соседей’: (10, 5)). Я получил совершенно другие наилучшие параметры, хотя я ожидаю, что изменение порядка параметров не должно влиять на наилучшие параметры, определенные GridSearchCV.

Ниже приведен код. Класс Debug используется для сохранения выходных данных с шага «уменьшить». Этот сохраненный результат используется в cv_silhouette_scorer() для вычисления silhouette_score. Я подозреваю, что класс отладки и cv_silhouette_scorer() работали не так, как я ожидал.

Большое вам спасибо за вашу помощь.

 class Debug(BaseEstimator, TransformerMixin):
    def __init__(self):
        self.transX = None

    def transform(self, X):
        print(X)
        self.transX = X.copy()
        return X

    def fit(self, X, y=None, **fit_params):
        return self

def cv_silhouette_scorer(estimator, X):
#     estimator.fit(X)
    sdata = estimator.named_steps['debug'].transX
    cluster_labels = estimator.named_steps['cluster'].labels_
    num_labels = len(set(cluster_labels))
    num_samples = sdata.shape[0]
    if num_labels == 1 or num_labels == num_samples:
        return -1
    else:
        return silhouette_score(sdata, cluster_labels)


ohe = OneHotEncoder(drop='if_binary', dtype=np.float32)
ore = OrdinalEncoder(dtype=np.float32)
ctenc = ColumnTransformer(transformers=[('ohe', ohe, nom_vars), ('ore', ore, ord_vars)], 
                          remainder='passthrough')
nftr = FunctionTransformer(nominal_indicator_missing, check_inverse=False, 
                          kw_args={'feat_names': ohecols, 'orig_cols': nom_vars})
oftr = FunctionTransformer(ordinal_indicator_missing, check_inverse=False,
                            kw_args={'miss_value': 0.})

ctmiss = ColumnTransformer(transformers=[('nftr', nftr, slice(0, 19)), ('oftr', oftr, slice(19, 20)), ('drop_cols', 'drop' , slice(32, 36) )], remainder='passthrough')

mputer = IterativeImputer(random_state=RS, add_indicator=True, initial_strategy="most_frequent", skip_complete=True)


# Add below keep_vars transformer to drop all demographic columns before pass to UMAP

keep_cols = ColumnTransformer(transformers=[('keep_cols1', 'passthrough' , slice(17, 25) ), ('keep_cols2', 'passthrough' , slice(46, 54) )] )

scaler = StandardScaler()

trans = FunctionTransformer(np.transpose, check_inverse=False)

dreduce = umap.UMAP(random_state=RS)
knn = KMeans(random_state=RS)
pipe = Pipeline(steps=[
        ('enc', ctenc)
        , ('functr', ctmiss)
        , ('mpute', mputer)
        , ('keep_cols', keep_cols)
        , ('scale', scaler)
        , ('trans', trans)
        , ('reduce', dreduce)
        , ("debug", Debug())
        , ('cluster', knn)
    ]
)

parameters = {
    'mpute__max_iter': (15, 20),
    'reduce__n_neighbors': (5, 10),
    'reduce__min_dist': (0.02, 0.05),
    'reduce__n_components': (2, 3),
    'reduce__metric': ('euclidean', 'manhattan'),
    'cluster__n_clusters': (2, 3),
    'cluster__n_init': (10, 25)
}
# parameters = {
#     'mpute__max_iter': (20, 15),
#     'reduce__n_neighbors': (10, 5),
#     'reduce__min_dist': (0.05, 0.02),
#     'reduce__n_components': (3, 2),
#     'reduce__metric': ('manhattan', 'eucidean'),
#     'cluster__n_clusters': (3, 2),
#     'cluster__n_init': (25, 10)
# }

def cv_silhouette_scorer(estimator, X):
#     estimator.fit(X)
    sdata = estimator.named_steps['debug'].transX
    cluster_labels = estimator.named_steps['cluster'].labels_
    num_labels = len(set(cluster_labels))
    num_samples = sdata.shape[0]
    if num_labels == 1 or num_labels == num_samples:
        return -1
    else:
        return silhouette_score(sdata, cluster_labels)

gsearch3 = GridSearchCV(pipe, parameters, n_jobs=-1, scoring=cv_silhouette_scorer , cv=5, verbose=1)
gsearch3.fit(dfnew)