Пользовательская функция оценки в GridSearchCV: доступ к немасштабированным функциям, а не к модели

Я использую пакет GridSearchCV из sklearn в Python, и я хотел бы использовать его с настраиваемой функцией оценки. Пользовательской функции скоринга потребуется доступ к переменным, которых нет в модели. Дело в том, что я не могу получить доступ к немасштабированным / неизмененным переменным из обучающего набора, поскольку они не включены в модель, которая использует масштабированные данные, и поскольку gridsearch выбирает строки случайным образом для каждого пакета. Ты хоть представляешь, как я могу с этим справиться?

Я попытался создать функцию оценки, которая принимает в качестве параметра исходный (немасштабированный, неизмененный) обучающий набор. Это работает, но поскольку gridsearch берет только подмножества обучающего набора, а строки перемешиваются, я не могу «связать» каждую строку с соответствующим ей значением в исходном обучающем наборе. Я попытался отмасштабировать данные, включенные в обучающий набор, но это не сработало. Я подумал о добавлении немасштабированного столбца, который я хочу, в масштабированный обучающий набор, но как я могу исключить его из модели?

# building pipelines
from sklearn.preprocessing import OneHotEncoder

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

num_pipeline = Pipeline([
    ('std_scaler', StandardScaler()),
],verbose=True)
cat_pipeline = Pipeline([
    ('one_hot_enc',OneHotEncoder(sparse=False,handle_unknown='ignore')),
],verbose=True)

from sklearn.compose import ColumnTransformer
full_pipeline = ColumnTransformer([
    ("num", num_pipeline, df_num_reg_attributes),
    ("cat", cat_pipeline, df_cat_attributes)
])

# fitting pipelines
X_train_prepared_reg = full_pipeline.fit_transform(X_res_df)
listColPrepared=np.concatenate((df_num_reg_attributes,full_pipeline.named_transformers_['cat'].named_steps['one_hot_enc'].get_feature_names()))
scalerX_train = full_pipeline.named_transformers_['num'].named_steps['std_scaler']
X_test_prepared_reg = full_pipeline.transform(X_test)
y_train = y_balanced

# scorer
def my_scorer(clf, X, y_true):
    DCWorkCost = 5.00
    OPWorkCost = 2.50
    mergedDataset = pd.DataFrame(data=X,index=np.arange(0,len(X)),columns=listColPrepared)
### this is the column I want -- I tried to unscale the data to access the column but it did not work    
    mergedDataset['Margin'] = scalerX_train.inverse_transform(mergedDataset['Margin'])
    mergedDataset['True'] = y_true
    mergedDataset['Pred'] = clf.predict(X)
 # rest of the scorer.........
    return revenue

# grid search
sgd_clf_cv = SGDClassifier(max_iter=5,tol=-np.infty, random_state=42)
parameters = {'class_weight':({0:.1,1:.9},{0:.2,1:.8},{0:.3,1:.7},{0:.25,1:.75},{0:.15,1:.85},{0:.35,1:.65},{0:.4,1:.6})}
grid = GridSearchCV(estimator=sgd_clf_cv, param_grid=parameters, scoring=my_scorer,verbose=10)
grid.fit(X_train_prepared_reg, y_train)
grid.best_estimator_

При попытке немасштабировать данные, как показано в коде, я получаю сообщение об ошибке о несоответствующих формах.


person damsc69    schedule 06.07.2019    source источник
comment
Вы можете scalerX_train в качестве параметра для sgd_clf_cv, и тогда он должен быть доступен в my_scorer, например sgd_clf_cv.scalerX = scalerX_train и в my_scorer используйте mergedDataset['Margin'] = clf.scalerX.inverse_transform(mergedDataset['Margin'])   -  person Maximilian Peters    schedule 07.07.2019
comment
Это очень помогло, спасибо. Мне удалось добавить средство масштабирования как свойство модели, но по какой-то причине я не могу получить к нему доступ в счетчике: он говорит, что объект SGDClassifier не имеет атрибута scalerX. Однако я могу получить доступ к счетчику за пределами счетчика.   -  person damsc69    schedule 08.07.2019


Ответы (1)


Два шага необходимы, чтобы иметь вашу собственную функцию оценки, которая также имеет доступ к другому постоянному объекту.

  1. Ваша пользовательская функция оценки должна быть передана в make_scorer. Формат функции подсчета очков должен быть def f(y_true, y_predicted).
  2. Для вашей функции оценки нужен третий именованный параметр, в котором вы можете разместить свой дополнительный объект.

В вашем случае код должен выглядеть примерно так

def my_scorer(y_true, y_pred, scaler=None):
    DCWorkCost = 5.00
    OPWorkCost = 2.50
    mergedDataset = pd.DataFrame(data=X, index=np.arange(0, len(y_true)), columns=listColPrepared)
    ### this is the column I want -- I tried to unscale the data to access the column but it did not work    
    mergedDataset['Margin'] = scaler.inverse_transform(mergedDataset['Margin'])
    mergedDataset['True'] = y_true
    mergedDataset['Pred'] = y_pred
   # rest of the scorer.........

    return revenue

...
scalerX_train = full_pipeline.named_transformers_['num'].named_steps['std_scaler']
...
sgd_clf_cv = SGDClassifier(max_iter=5,tol=-np.infty, random_state=42)
...
custom_score = make_scorer(my_scorer, scaler=scalarX_train)
...
grid = GridSearchCV(estimator=sgd_clf_cv, param_grid=parameters, scoring=custom_score, verbose=10)
person Maximilian Peters    schedule 08.07.2019
comment
Спасибо за вашу помощь, в конце концов я сам написал GridSearch, чтобы заставить его работать так, как я хочу. Однако с вашим кодом будет ли работать скейлер? Потому что я пробовал раньше, и он сказал, что не соответствующие формы. Я считаю, что он ожидает массив того же размера, что и тот, на котором он был обучен, и этого не будет с GridSearch, поскольку он просто берет подмножество строк ... - person damsc69; 09.07.2019
comment
Пожалуйста, добавьте код в качестве ответа, возможно, он поможет другим людям, которые ищут ту же проблему. - person Maximilian Peters; 10.07.2019