学習と検定


入力の準備

現状で三種類の特徴ベクトルを持った教師データが用意できたので、これを用いた分類を行います。分類はscikit-learnのClassifierを用いるので、これに入れるためには

  • X:(n_feature,n_sample)の行列
  • Y:(n_sample)で、東方に1、そうでない曲に0を振ったベクトル

が必要になります。というわけでまずこの形にデータを成形します。

データの均衡化

不均衡なデータセットで学習を行うと意図しない分類が行われてしまうので、データを何らかの方法で均衡化しなければなりません。この点で非常に参考になったのが、 この記事です。この記事では8つの方策が書いてあります。

具体的にどの手法がよいのか、どのぐらい均衡化すれば十分なのかは状況によりけりだと思うので、まず試して、十分な精度まで分類機が良くなれば重文みたいにアドホックにならざるを得ない感じがありますね。音楽の分類だと優先順位的には5>2>6>3>4>1>8>7かなと思います。今回は幸い普通にアンダーサンプリングするだけである程度結果が出ましたが、必要があればSMOTEとかでサンプルを増してみるのよいように見えます。SMOTEはpythonではimbalanced-learnというライブラリでできます。imblearnはnumpy1.11を要求しますが、windowsのanacondaではcondaでnumpyを更新するとmklのライブラリが見つからずnumpyのロード自体が出来なくなってしまうので、Unofficial windows binariesからnumpyを入れなおしましょう。

データの前処理

いくつかの学習モデルでは規格化、正規化、白色化あたりの前処理があると性能が改善します。SVMや多層ニューラルネットワークなどでは、standarizationとがなされていることが特に重要らしいです。例えば、代表的なモデルであるSVMではスケールの比率がそのまま重要度の比率になっているそうです。一方、Random Forestはstandarizationをしてもしなくても変わらないらしいです。Logistic regressionも必要ないそうです。

今回の分類はそれぞれの特徴の分散の小ささは基本的にその特徴の重要度の低さを表していると考えることにして分析をしました。というかこのあたりから身辺が忙しくなり試している暇がなかった

学習、検定、評価

scikit-learnでは、fitで学習し、predictで分類するだけです。楽勝ですね。一般的な検定では、train/validation/testの三つにデータを分割します。長らくvalidationとtestの違いをよく分かっていなかったのですが、恐らく

  • train : データxt,ytにフィットしたモデルSVM(C=0.1),SVM(C=1)...を作る。
  • validation : データxv,yvに対して評価してみると、モデルSVM(C=C_0)が一番いいことが分かる。
  • test : データxtest,ytestでSVM(C=C_0)の性能を測る

という手続きであるようです。つまり、trainではモデルの学習性能を評価し、validationではモデルの汎化性能を比較し、testではモデルの汎化性能を評価する、という感じだと思います。このあたりの手続きは全てscikit-learnに関数があるのでimportして呼ぶを繰り返すだけです。大雑把に書くとこんな感じです。

from sklearn.cross_validation import train_test_split
from sklearn.grid_search import GridSearchCV
from sklearn.metrics import classification_report
from sklearn.ensemble import RandomForestClassifier
from sklearn.utils import shuffle

# trainとtestに分割
X_true = load("hoge_true.pkl")
X_false = load("hoge_false.pkl")
X_true_train,X_true_test = train_test_split(X_true,test_size=0.25)
# falseの方が多い前提でアンダーサンプリングしています
X_false_train,X_false_test = train_test_split(X_false,train_size=len(X_true_train),test_size=len(X_false_test))

# ラベルを付けてまとめる
X_train = X_true_train + X_false_train
Y_train = [1]*len(X_true_train)+[0]*len(X_false_train)
X_test = X_true_test + X_false_test
Y_test = [1]*len(X_true_test)+[0]*len(X_false_test)
X_train,Y_train = shuffle(X_train,Y_train)
X_test,Y_test = shuffle(X_test,Y_test)

# 検定
param = { "n_estimators" : [5,10,50,100]}
clf = RandomForestClassifier()
gs = GridSearchCV(clf,param)
gs.fit(X_train,Y_train)

# 評価
bestClf = gs.best_estimator_
print(gs.best_score_,gs.best_params_)

Y_train_predict = bestClf.predict(X_train)
reportStrTrain = classification_report(Y_train,Y_train_predict)
print(reportStrTrain)

Y_test_predict = bestClf.predict(X_test)
reportStrTest = classification_report(Y_test,Y_test_predict)
print(reportStrTest)

例えばRandom Forestにはn_estimatorというハイパーパラメータがあり、こいつをGridSearchCVで調整することで汎化性能を調性することができます。あとはbest_paramsの具合を見ながら、ハイパーパラメータを絞っていきます。ハイパーパラメータが複数ある場合は調性していく定番の手順のようなものがあるようです。例えばXGBoostだとここが参考になりました。

ハイパーパラメータの最適化

最適化は基本的には手でレンジを設定しながらGridSearchを繰り返します。しかし、それなりにでかいデータセットになるとパラメータのメッシュを細かく切るとパラメータの種類の乗数で増えていくので、パラメータの微妙な違いに敏感になってくると結局相当な非線形最適化を人力でやるハメになり、かなり黒魔術的な最適化になってきます(なりました)。例えばXGBoostは大量にハイパーパラメータがあるので、割とやってられない感じになります。

そこで使えるのが、hyperoptというハイパーパラメータの最適化ライブラリです。scikit-learnでのハイパーパラメータ探索に使う方法はてれかさまのブログ記事 が参考になります。が、このツールの存在をこれ終わった後に知ったので、後から手元で試しはしましたが、記事のデータ出力では実質的に使われていないです。

results matching ""

    No results matching ""