GridSearchCV
возвращает только баллы для каждой параметризации, и я хотел бы также увидеть кривую Рока, чтобы лучше понять результаты. Чтобы сделать это, я хотел бы взять модель GridSearchCV
с GridSearchCV
и воспроизвести те же результаты, но кэшировать вероятности. Вот мой код
import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.feature_selection import SelectFromModel
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import StratifiedKFold
from sklearn.pipeline import Pipeline
from tqdm import tqdm
import warnings
warnings.simplefilter("ignore")
data = make_classification(n_samples=100, n_features=20, n_classes=2,
random_state=1, class_sep=0.1)
X, y = data
small_pipe = Pipeline([
('rfs', SelectFromModel(RandomForestClassifier(n_estimators=100))),
('clf', LogisticRegression())
])
params = {
'clf__class_weight': ['balanced'],
'clf__penalty' : ['l1', 'l2'],
'clf__C' : [0.1, 0.5, 1.0],
'rfs__max_features': [3, 5, 10]
}
key_feats = ['mean_train_score', 'mean_test_score', 'param_clf__C',
'param_clf__penalty', 'param_rfs__max_features']
skf = StratifiedKFold(n_splits=5, random_state=0)
all_results = list()
for _ in tqdm(range(25)):
gs = GridSearchCV(small_pipe, param_grid=params, scoring='roc_auc', cv=skf, n_jobs=-1);
gs.fit(X, y);
results = pd.DataFrame(gs.cv_results_)[key_feats]
all_results.append(results)
param_group = ['param_clf__C', 'param_clf__penalty', 'param_rfs__max_features']
all_results_df = pd.concat(all_results)
all_results_df.groupby(param_group).agg(['mean', 'std']
).sort_values(('mean_test_score', 'mean'), ascending=False).head(20)
Вот моя попытка воспроизвести результаты
small_pipe_w_params = Pipeline([
('rfs', SelectFromModel(RandomForestClassifier(n_estimators=100), max_features=3)),
('clf', LogisticRegression(class_weight='balanced', penalty='l2', C=0.1))
])
skf = StratifiedKFold(n_splits=5, random_state=0)
all_scores = list()
for _ in range(25):
scores = list()
for train, test in skf.split(X, y):
small_pipe_w_params.fit(X[train, :], y[train])
probas = small_pipe_w_params.predict_proba(X[test, :])[:, 1]
# cache probas here to build an Roc w/ conf interval later
scores.append(roc_auc_score(y[test], probas))
all_scores.extend(scores)
print('mean: {:<1.3f}, std: {:<1.3f}'.format(np.mean(all_scores), np.std(all_scores)))
Я запускаю вышеупомянутые несколько раз, так как результаты кажутся нестабильными. Я создал сложный набор данных, так как мой собственный набор данных одинаково труден для изучения. Группировка предназначена для того, чтобы взять все итерации GridSearchCV
и усреднить & std показатели поезда и теста для стабилизации результатов. Затем я выбираю модель с наилучшими характеристиками (C = 0,1, штраф = l2 и max_features = 3 в моей последней модели) и пытаюсь воспроизвести те же самые результаты, когда я намеренно ввожу эти параметры.
Модель GridSearchCV
дает среднее значение 0,63 и 0,042 стандартного значения, тогда как моя собственная реализация получает среднее значение 0,59 и стандартное значение 0,131. Результаты поиска по сетке значительно лучше. Если я проведу этот эксперимент за 100 итераций как для GSCV, так и для моей собственной, результаты будут аналогичными.
Почему эти результаты не совпадают? Они оба внутренне используют StratifiedKFold()
когда передается целое число для cv... и, возможно, GridSearchCV
взвешивает результаты по размеру сгиба? Я не уверен в этом, это будет иметь смысл, хотя. Моя реализация имеет недостатки?
edit: random_state
добавлено в SKFold
Если вы установите набор random_state для RandomForestClassifier
, различия между различными girdsearchCV
будут устранены.
Для упрощения я установил n_estimators = 10 и получил следующий результат
mean_train_score mean_test_score
param_clf__C param_clf__penalty param_ rfs_max_features mean std mean std
1.0 l2 5 0.766701 0.000000 0.580727 0.0 10 0.768849 0.000000 0.577737 0.0
Теперь, если вы видите производительность на каждом разделении (путем удаления фильтрации key_feats
) лучших гипер-параметров, используя
all_results_df.sort_values(('mean_test_score'), ascending=False).head(1).T
мы получим
16
mean_fit_time 0.228381
mean_score_time 0.113187
mean_test_score 0.580727
mean_train_score 0.766701
param_clf__C 1
param_clf__class_weight balanced
param_clf__penalty l2
param_rfs__max_features 5
params {'clf__class_weight': 'balanced', 'clf__penalt...
rank_test_score 1
split0_test_score 0.427273
split0_train_score 0.807051
split1_test_score 0.47
split1_train_score 0.791745
split2_test_score 0.54
split2_train_score 0.789243
split3_test_score 0.78
split3_train_score 0.769856
split4_test_score 0.7
split4_train_score 0.67561
std_fit_time 0.00586908
std_score_time 0.00152781
std_test_score 0.13555
std_train_score 0.0470554
давайте воспроизвести это!
skf = StratifiedKFold(n_splits=5, random_state=0)
all_scores = list()
scores = []
weights = []
for train, test in skf.split(X, y):
small_pipe_w_params = Pipeline([
('rfs', SelectFromModel(RandomForestClassifier(n_estimators=10,
random_state=0),max_features=5)),
('clf', LogisticRegression(class_weight='balanced', penalty='l2', C=1.0,random_state=0))
])
small_pipe_w_params.fit(X[train, :], y[train])
probas = small_pipe_w_params.predict_proba(X[test, :])
# cache probas here to build an Roc w/ conf interval later
scores.append(roc_auc_score(y[test], probas[:,1]))
weights.append(len(test))
print(scores)
print('mean: {:<1.6f}, std: {:<1.3f}'.format(np.average(scores, axis=0, weights=weights), np.std(scores)))
[0,42727272727272736, 0,47, 0,54, 0,78, 0,7]
среднее: 0,580727, стандартное: 0,135
Примечание: mean_test_score
- это не просто среднее значение, это взвешенное среднее. Причина, по которой iid
param
Из документации:
iid: boolean, default = warn Если True, вернуть среднюю оценку по сгибам, взвешенную по количеству выборок в каждом наборе тестов. В этом случае предполагается, что данные одинаково распределены по складкам, а минимизированными потерями являются общие потери по образцу, а не средние потери по складкам. Если False, вернуть среднюю оценку по сгибам. По умолчанию True, но изменится на False в версии 0.21, чтобы соответствовать стандартному определению перекрестной проверки.
Изменено в версии 0.20. Параметр iid по умолчанию изменится с True на False в версии 0.22 и будет удален через 0.24.
random_state
.np.random.seed(1)
? Случайное состояние устанавливается в вызовеmake_classification
. Используя кусочек семян, я все еще получаю разницу в 0,5 между GSCV и моим собственным методом.