8000 grid_search: feeding parameters to scorer functions · Issue #8158 · scikit-learn/scikit-learn · GitHub
[go: up one dir, main page]

Skip to content

grid_search: feeding parameters to scorer functions #8158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
xavierfontaine opened this issue Jan 5, 2017 · 8 comments · Fixed by #27058
Closed

grid_search: feeding parameters to scorer functions #8158

xavierfontaine opened this issue Jan 5, 2017 · 8 comments · Fixed by #27058

Comments

@xavierfontaine
Copy link

Description

GridSearchCV and RandomizedSearchCV do not allow for passing parameters to the scorer function. This could be made possible by adding an extra scorer_params, similar to the fit_params argument.

For consistency with fit_params, special care would have to be paid to sample weights. Weights fed through fit_params are currently well-distributed across training folds. Similarly, weights fed through scorer_params would have to be distributed across validation folds.

Is adding this feature under consideration?

If not, should I give it a try?

(Nota: follow-up issue to the closed #8156. I believed the change in focus required a new thread.)

@amueller
Copy link
Member
amueller commented Jan 5, 2017

Also see #4632.

What other use-cases are there apart from sample_weights?

@xavierfontaine
Copy link
Author
xavierfontaine commented Jan 6, 2017

Good question. A list of use-cases would be:

  • Some scorer functions from sklearn.metrics take additional arguments. For instance, the multioutput argument which appears in several regression metrics (e.g. explained_variance_score), the averageargument in several classification scoring functions (e.g. precision_score), or the beta parameter that appears in fbeta_score.
  • Most (all?) functions from sklearn.metrics have an optional sample_weight argument.
  • Custom metrics may take any arbitrary number of arguments, depending on the user's need.

Thanks for pointing out the related thread #4632. Answering your last point on that thread, I believe a scorer_params argument would be an unambiguous way to feed weights to the scorer function. Indeed, fit_params already does the same for the training algorithm. Consequently, as far as I can tell:

  • the functionalities of fit_params and scorer_params would be consistent
  • The argument names would be consistent and clear
  • It would be possible to unambiguously weight either the training data, the validation data, or both.

@jnothman
Copy link
Member
jnothman commented Jan 7, 2017 via email

@xavierfontaine
Copy link
Author
xavierfontaine commented Jan 9, 2017

Fair point, I guess only the use of data-dependent arguments in cross-validation would require an evolution of sklearn's code.

Then I guess we are back to the first issue I opened (#8156): what would be the best way to allow for validation sample weights in grid_search?

I do not feel qualified to express an opinion on whether implementing sample props (#4497) should be done before tackling this issue, or whether the proposal I made above (or something else entirely) would be a way to go in the meanwhile.

@jnothman
Copy link
Member
jnothman commented Jan 9, 2017 via email

@jyn514
Copy link
jyn514 commented Mar 31, 2018

I would be interested in this. I am running a wrapper using Keras, and it shows a progress bar even when I set verbose to 0, because _fit_and_score does not pass the parameters to scorer.

Sample code:

from sklearn.model_selection import GridSearchCV
from keras.wrappers.scikit_learn import KerasClassifier
from keras.models import Sequential

def configure(nodes=10):
    model = Sequential()
    model.add(layers.Dense(nodes))
    model.add(layers.Dense(2))
    model.compile()
    return model

hyperparams = {'nodes': [20, 40, 60, 80, 100, 128]}
GridSearchCV(estimator=KerasClassifier(configure),
                        param_grid=hyperparams).fit(data, labels, verbose=0)

Output:

4358/4358 [==============================] - 0s 90us/step
7232/8714 [=======================>......] - ETA: 0s

The reason I think the problem is _fit_and_score:

File "/usr/local/lib/python2.7/dist-packages/sklearn/externals/joblib/parallel.py", line 131, in __call__
    return [func(*args, **kwargs) for func, args, kwargs in self.items]
File "/usr/local/lib/python2.7/dist-packages/sklearn/model_selection/_validation.py", line 492, in _fit_and_score
    is_multimetric)
File "/usr/local/lib/python2.7/dist-packages/sklearn/model_selection/_validation.py", line 523, in _score
    return _multimetric_score(estimator, X_test, y_test, scorer)
File "/usr/local/lib/python2.7/dist-packages/sklearn/model_selection/_validation.py", line 553, in _multimetric_score
    score = scorer(estimator, X_test, y_test)
File "/usr/local/lib/python2.7/dist-packages/sklearn/metrics/scorer.py", line 244, in _passthrough_scorer
    return estimator.score(*args, **kwargs)

Notice how kwargs are passed before and after.

@stmax82
Copy link
stmax82 commented Oct 10, 2018

I just got a task where I have to implement a custom metric that needs additional data per sample, like this:

X, y = make_classification()

k = np.random.rand(len(X)) # additional data that's required to calculate the custom metric

def my_metric(y, y_pred, k):
    return np.average(y*y_pred*k) # just an example...

Using this in a GridSearchCV:

gridsrch = GridSearchCV(estimator  = LogisticRegression(),
                        param_grid = {"C": [0.1, 1, 10]},
                        scoring    = make_scorer(my_metric, k=k), # passing k to the scorer...
                        cv         = 3)

gridsrch.fit(X, y)

... fails with the following error in line np.average(y*y_pred*k) because k is longer than y and y_pred during cross validation:

ValueError: operands could not be broadcast together with shapes (34,) (100,)

I actually found a workaround by using pandas data frames / series and abusing their indexes... the following works as expected:

# convert X, y and k to pandas data frames / series
df_X = pd.DataFrame(X)
df_y = pd.Series(y)

df_k = pd.Series(k)

def my_metric(y, y_pred, k):
    # abuse y's index to grab the right elements from k
    return np.average(y*y_pred*k.loc[y.index])

gridsrch = GridSearchCV(estimator  = LogisticRegression(),
                        param_grid = {"C": [0.1, 1, 10]},
                        scoring    = make_scorer(my_metric, k=df_k),
                        cv         = 3)

gridsrch.fit(df_X, df_y)

... but it'd be great if this also worked without pandas data frames somehow.

@amueller
Copy link
Member

@stmax82 this is #4497. It's a known issue that's on the long-term roadmap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
0