備忘録とか日常とか

学んだこととかを書きます。

scikit-learnでsvm 基本的な使い方

今更だがsvmを使いたかったのでscikit-learnで使い方を調べた。
公式ドキュメントが整っているのでそっち見ただけでもわかる。
1.4. Support Vector Machines — scikit-learn 0.19.2 documentation


以下参考
Scikit-learnでハイパーパラメータのグリッドサーチ
scikit-learnによる多クラスSVM
2013.07.15 はじパタlt scikit-learnで始める機械学習

とりあえず使う

とりあえず使うというだけなら何も考えず

from sklearn import svm

clf = svm.SVC()
clf.fit(train_features, train_labels)

とすれば識別器の学習ができる。
その後テストデータに対して推定を行うには

test_pred = clf.predict(test_features)

でおけ。
変数test_predには推定ラベルが羅列したnumpy.ndarrayが入る。
これを真値と比較して結果を見るには、

from sklearn.metrics import classification_report, accuracy_score

print classification_report(test_labels, test_pred)
print accuracy_score(test_labels, test_pred)

とすれば、結果をF値と認識率で評価してくれる。
F値については以下参照
F値 - 機械学習の「朱鷺の杜Wiki」


いやそんなの興味ねーから、Confusion matrixくださいよって時は

from sklearn.metrics import confusion_matrix

print confusion_matrix(test_labels, test_pred)

で表示できる。F値とかは何か結果がごまかされてる感があるけど
Confusion matrix見せとけばとりあえず誰でも納得する、ような気がする


カーネルなどのパラメータを指定する場合は

clf = svm.SVC(kernel='rbf', C=1, gamma=0.1)

のように指定する。
デフォルトではRBFカーネルで、C=1.0, gamma= 1/特徴数 となっている。

チューニングする

svmの性能を引き出すには各種パラメータを調整する必要がある
まずカーネルだが、上で述べたようにsvm.SVCはデフォルトではRBFカーネルを使う
基本的にはRBF、特徴の次元数が大きい時は線形カーネルでもいいらしい
RBFカーネルでは調整するパラメータは{\gamma}{C}、線形は{C}だけ
これをグリッドサーチで探索する
(詳しいことは勉強中・・・こことかわかりやすいのかも)

なんとscikit-learnでは一行でグリッドサーチする関数が用意されている。

parameters = [{'kernel':('rbf'), 'C':numpy.logspace(-4, 4, 9), 'gamma':numpy.logspace(-4, 4, 9)},
              {'kearnel':('rbf'), 'C':numpy.logspace(-4, 4, 9)} ]
clf = grid_search.GridSearchCV(svm.SVC(), parameters, n_jobs = -1)
clf.fit(train_features, train_labels)

print clf.best_estimator_

parametersで指定したパラメータでグリッドサーチを行う。
n_jobsでCPUのコア数を指定して並列計算してくれる。(-1で自動決定)
ただしその分使用するメモリ量も大きくなる。当たり前だけど。
特徴量の次元が大きい場合、メモリが足りなくなってエラーになることがあるので注意。

print clf.best_estimator_でベストパラメータを表示できる。


ここでGridSearchCVcvという引数を整数で指定すると、
指定したfold数でCross-validationをやってくれる。もういたれりつくせりである。

結果はclf.grid_scores_に入っているので下みたいに表示できる。

print"mean score for cross-validation:\n"
for params, mean_score, all_scores in clf.grid_scores_:
    print "{:.3f} (+/- {:.3f}) for {}".format(mean_score, all_scores.std() / 2, params)

clf.grid_scores_には
params(cross-validationのパラメータ),
mean_score(cross_validation後の平均認識率),
all_score(各foldでの認識率)が格納されている。
ついでにall_score.std()標準偏差を求めて{\pm \sigma} を表示するようにしてる。
format関数って便利だね


結果はこんな感じ↓

mean score for cross-validation:

0.782 (+/- 0.004) for {'kernel': 'rbf', 'C': 0.0001, 'gamma': 0.0001}
0.854 (+/- 0.002) for {'kernel': 'rbf', 'C': 0.0001, 'gamma': 0.001}
0.786 (+/- 0.006) for {'kernel': 'rbf', 'C': 0.0001, 'gamma': 0.01}
0.798 (+/- 0.003) for {'kernel': 'rbf', 'C': 0.0001, 'gamma': 0.1}
...

パラメータによってはすごい数になる。

詳しくはドキュメント参照。
sklearn.grid_search.GridSearchCV — scikit-learn 0.19.2 documentation

テストデータの推定は上と同じようにできる。

One-versus-Rest か One-versus-One か

多クラス分類を行う場合、一つの特徴量がどのクラスに分類されるかを決定するのに2つの方法がある。

  • ある特定のクラスに入るか、それ以外に入るか(One-versus-Rest、以後ovr)
  • ある特定のクラスに入るか、別のある特定のクラスに入るか(One-versus-One、以後ovo)

{K}クラス分類問題を考えた時、
ovrでは{K}個の分類器を利用し、
ovoでは{K(K-1)/2}個の分類器を利用する、という違いがある。


svm.SVCdecision_function_shapeという引数でこれらを指定できる。
デフォルトでは'ovo'となっているが、'ovr'のほうが推奨されている(識別性能が良い?)
2016/2/20の時点ではバージョン1.7.0が最新だが、1.8ではデフォルトを'ovr'にすると公式で宣言されてるから
結構性能が違ってくるのかもしれない。

感想

アホでも使えるレベルにしてくれていて助かる。
あとは理論的なことを勉強せねば・・・