はじめに
SVMで二クラス(0/1)の分類器を学習して、各データのクラス1への所属確率を出したい用ができたので sklearn.svm.SVC を見ていたら、predict_probaとかいうまさになやつを見つけたので、使って見たら ん?ってなった話。
ん?ってなったこと
predict(X)で予測した結果とpredict_probaで算出した確率の整合が取れてない(時がたまにあった)のです。
準備
いったんpredictとpredict_probaの仕様を確認。
predict(X)
[データ数]行 × [次元数]列の特徴量行列 X を引数にして、データ数分の予測ラベルを返すそうです。読む必要なかった。
predict_proba(X)
[データ数]行 × [次元数]列の特徴量行列 X を引数にして、各データがそれぞれのクラスに所属する確率を返す、って書いてあります。で、最後に「列はクラス名でソートされていて、そのクラス名は **classes_**で見れますよ」とも書いてあります。
上記をふまえると、例えば、X = [[-1 -1 0][-1- -2 -1][2 5 1]]
, y = ['0' '0' '1']
みたいなデータの場合、predict(X)は(全部正解なら)['0' '0' '1']
, predict_proba(X)は[[0.9 0.1][0.88 0.12][0.01,0.99]]
みたいになるってことですよね。
本題
なのに、predict(X)
['1' '1' '0' '1' '0' '0' ・・・
で、predict_proba(X)が
[[0.60069337 0.39930663]
[0.60064285 0.39935715]
[0.18527877 0.81472123]
[0.60069337 0.39930663]
[0.18525054 0.81474946]
[0.18532548 0.81467452]
・・・
のように逆になっているときが、数回に一回あったのです。念のため **.classes_**でクラスの順番を確認しましたが、こちらは何回実行しても
['0' '1']
のように常にクラス名順でした。predict_proba(X)だけがたまに逆になるのです。
いろいろ調べたら
このことについて質問したり説明してる人がいました。
- https://stackoverflow.com/questions/17017882/scikit-learn-predict-proba-gives-wrong-answers
- https://leck-tech.com/machine-learning/svm#Classification
端的に言うとpredictとpredict_probaは矛盾することがあるからdecision_function(X)使った方がいいよ、的なことが書いてありました。
*なんで矛盾するのとかそういう細かい話は面倒なので書きません。知りたければ "Platt Scaling" でググるか、元論を読んだりしてください。
じゃあどうしたらいいんだろ
いろいろ考えても正解は出なかったのですが、候補だけ挙げさせていただきます。
1. decision_function(X)を確率っぽい値に変換する
decision_function(X)を使うと、上の例だと
[ 1.00020435 0.99997659 -1.0001893 1.00020435 -1.0003867 -0.99986263
のような値がでます。(意味は省きますが)この値が正だとクラス1、負だとクラス0に分類されるので、この値をシグモイド関数に代入すれば確率っぽいものは出せますね。ただ、シグモイド関数の係数をどう設定すればいいんだっていう話と、その係数を最適化するのがPlatt Scalingみたいなのでもうあれです。
2. predict_probaが逆になっても大丈夫なようにコードを書く
「predict(X)でクラス1と予測されたデータのpredict_probaが大きい方の列がクラス1の列だよ」っていうのを学習時に記憶させておくとか。ただ、それが理論的にしていいことなのかわかりません。
3. SVMをあきらめる
あきらめることも大事。
おわりに
predict_probaはRandomForestとか他の手法にもあるので、そっちも同じ感じだと困るなーと。これはSVMだけの事象だよとかこうするほうがいいと思うよとかそういう意見を頂けたらとっても嬉しいです。