中野智文のブログ

データ・マエショリストのメモ

ValueError: y_true contains only one label (0). Please provide the true labels explicitly through the labels argument. というエラー

背景

GridSearchCV をすると、

ValueError: y_true contains only one label (0). Please provide the true labels explicitly through the labels argument.

というようなエラーに遭遇することがある。 これは「評価用のデータに1つの正解ラベルしか含まれていないから、複数の正解ラベルを labels 引数に入れてね」というメッセージなのだが、どこに入れるか分からない。

labels は score 関数のラベルに入れる

結論は表題の通りなのだが、scoring のパラメータに入れる関数のパラメータとなる。scoring パラメータは、string, list, dict, None の形式に対応しているが、 string や list は、score関数の文字列で、引数を指定できない。よってdict形式による関数の指定ということなる。 dict 関数の指定は、文字列と関数の二種類があるが、この例precision のようにstringで指定したら引数を指定できないので、 make_scorer(accuracy_score) のように指定する。この make_scorer は、 sklearn.metrics.make_scorer — scikit-learn 0.19.1 documentation のとおりだが、第一引数に、メトリックス関数を指定できるが、その他にそのメトリックス関数の引数(**kwargsと書かれている)を指定できる。

neg_log_loss に labels=[0, 1] に引数で与える場合。accuracy は一致をみるので、他のクラスの情報をしらなくても問題ないが、neg_log_loss は他に何クラスあるのか知っていないと計算できない模様。

from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, log_loss, make_scorer

...

    clf = GridSearchCV(
        LogisticRegression(penalty='l1')
        {'C': [0.01, 0.1, 1, 10, 100]},
        cv=KFold(n_splits=20),
        scoring={'neg_log_loss': make_scorer(log_loss, labels=[0, 1], greater_is_better=False), 
                 'accuracy': 'accuracy'},
        n_jobs=-1,
        refit='neg_log_loss'
    )
...

greater_is_better=False は、log_loss から、neg_log_loss に変換するときに必要。

まとめ

make_scorer 関数を使って、labels 引数を与えたものを scoring 引数のdict型で与える。

ちなみに、cross validation に、KFold を使わなかったり、cv数(n_splits)を小さくすれば、このエラーを回避することもできる場合もある(検証事例が単独のクラスになりくくなる)が、それは誤った解決法である。

参考

github.com