Septeni Engineer's Blog

セプテーニエンジニアが綴る技術ブログ

scikit-learnを使ってみた

こんにちは、佐野です。

先日、機械学習を勉強する機会があり、手元でちょっと検証するときにscikit-learnを使ってみたのですが、とても使いやすく便利だったため、有名なライブラリですが紹介したいと思います。

scikit-learnとはPythonオープンソースライブラリで、クラス分類、回帰分析、クラスタリングなどのための様々なアルゴリズムを備えた機械学習ライブラリです。

Pythonを今まで書いたことがなかったし、機械学習も薄っぺらい知識しかなかった僕でもインターフェースがとてもシンプルな作りになっていたのですぐ馴染むことができました。

インストー

弊社ではMacを使用しており、MacにはPython2系がインストールされているのですが、Python3系がおすすめとゆうことで、Python3系のAnacondaをインストールします。

AnacondaとはPythonディストリビューションで、Pythonでよく使われるパッケージもデフォルトでインストールしてくれているのでこれを使いました。もちろんこの中にscikit-learnや数値計算ライブラリのnumpyや図描画用のmatplotlibなども含んでくれています。

Download Anaconda Now! | Continuum

データ読み込み

データの読み込みをcsvから行う場合は以下のように読み込めます。

入力データと真値を別々に読み込んでいます。

import numpy
input_path = "./sample_input.csv"
ground_truth_path = "./sample_ground_truth.csv"
input = numpy.loadtxt(input_path, delimiter=',')
ground_truth = numpy.loadtxt(ground_truth_path, delimiter=',')

今回はscikit-learnに付属している0~9の手書き文字のデータセットを用いてみたいと思います。

from sklearn.datasets import load_digits
input, ground_truth = load_digits(return_X_y=True)

このinput(入力)には8×8にリサイズされたグレースケール画像の明度値(これも0~16の整数にラベリングされている)が1797枚入っています。 ground_truth(真値)にはそれぞれの画像の正解となる数字が配列で格納されています。

画像で確認するとこんな感じです。

import matplotlib.pyplot as plt
plt.gray()
plt.matshow(digits.images[100])
plt.show()

f:id:m_sano:20170710143208p:plain:w200

まとめると、

説明変数が64個の各ピクセルの明度値(0~16)

目的変数が0~9のクラス分類というような言い方になるのではないでしょうか。

学習データとテストデータに分割

学習モデルを作成する際には、ある一定のサンプルデータにしか効果を発揮しないというような過学習を防ぐために、 手持ちのデータを学習データと検証用のテストデータに分割してモデル作成&検証を行います。

つまり学習用データでモデル作成を行い、残ったテストデータで作成されたモデルが過学習せず、汎用性を備えているかを検証するような流れになります。

分割の比率は学習:テストデータを2:1がよく用いられるようです。

from sklearn.model_selection import train_test_split
input_train, input_test, ground_truth_train, ground_truth_test = train_test_split(
  input, ground_truth, test_size=0.33
)

このようにscikit-learnで、データセットを分割するメソッドは用意されています。

学習

学習モデルの作成はとてもシンプルで、例えばランダムフォレストアルゴリズムを使おうとすると

from sklearn.ensemble import RandomForestClassifier
classifier = RandomForestClassifier()
classifier.fit(input_train, ground_truth_train)

たったこれだけでモデル作成をしてくれます。

実際にはハイパーパラメータとよばれるモデル作成に必要なパラメータがたくさんあるので、

classifier = RandomForestClassifier(
  n_estimators=10, criterion='gini', max_depth=None, min_samples_split=2, 
  min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features='auto', 
  max_leaf_nodes=None, min_impurity_split=1e-07, bootstrap=True, oob_score=False, 
  n_jobs=1, random_state=None, verbose=0, warm_start=False, class_weight=None
)

みたいにパラメータをコンストラクタで指定します。(値はデフォルト)

このパラメータの調整次第で精度がよくなったり、過学習したりするので、よく用いられるハイパーパラメータが設定されているとはいえ、データセットに合わせて学習器を選択し、パラメータの調整を行なっていくことが非常に重要になります。

scikit-learnの公式では学習器の選択に迷った時のチートシートを公開しているので参考にするとよいかもしれません。

Choosing the right estimator — scikit-learn 0.18.2 documentation

認識

上で作られたモデルを用いて、認識を行うには

predicted_input = classifier.predict(input_test)

で、分類の予測結果を得ることができます。

このpredicted_inputとground_truth_testを比較することで、テストデータでも正しく認識を行えていたのか検証するために使えると思われます。

もしくは、シンプルに認識精度のみを得たい場合は、

accuracy = classifier.score(input_test, ground_truth_test)

で得ることができます。

またランダムフォレストとはアンサンブル学習の一つでブートストラップサンプリングした学習データセットごとに決定木分析をかけてたくさんの決定木のなかから多数決をとる手法ですが、一つ一つの決定木がどのようなものなのかを視覚化することも可能です.

from sklearn import tree
i_tree = 0
for tree_in_forest in classifier.estimators_:
    with open('tree_' + str(i_tree) + '.dot', 'w') as my_file:
        my_file = tree.export_graphviz(tree_in_forest, out_file = my_file)
    i_tree = i_tree + 1

でdotファイルを出力し、これをGraphviz で開くと Graphviz | Graphviz - Graph Visualization Software f:id:m_sano:20170710144224j:plain 一番上の拡大図 f:id:m_sano:20170710144535p:plain

今回のデータセットだと何番目のピクセルが何とか以上の時は〜とか言われてもあまりピンときませんが、 説明変数の種類によっては、説明変数を再考したりする上で有効な機能かなと思います。

また、ランダムフォレストアルゴリズムでは各説明変数の重要度を算出することができます。

importances = classifier.feature_importances_
indices = numpy.argsort(importances)[::-1]
for f in range(input_train.shape[1]):
  print("%d. feature %d (%f)" % (f + 1, indices[f], importances[indices[f]]))

これにより何番めの説明変数がどんだけ重要度をもっているのかをランキングで算出しています。これもまた説明変数の分析に有用な方法であると思います。

グリッドサーチ

先ほどハイパーパラメータの選定がモデルを作成するためにとても重要だと言いましたが、ひとつひとつ値かけて分析して。。。はとても大変なのでGridSearchCVというものを使用します。

よくネットで見るサンプルコードでは非推奨のバージョンをインポートしてるので

from sklearn.grid_search import GridSearchCV

ではなく、

from sklearn.model_selection import GridSearchCV

をインポートしてください。

次に、使いたいパラメータのディクショナリを定義

parameters = {
    'n_estimators'      : [5, 10, 30, 50, 100, 500],
    'max_features'      : [3, 5, 10],
    'random_state'      : [0],
    'min_samples_split' : [3, 5, 10, 20, 50, 100],
    'max_depth'         : [3, 5, 10]
}

で以下のように学習器をラップすることで、parametersの組み合わせ全てを考慮したハイパーパラメータの最適化を行なってくれます。

classifier = GridSearchCV(RandomForestClassifier(), parameters)
classifier.fit(input_train, ground_truth_train)
print(classifier.best_estimator_)

best_estimatorにそのとき使ったパラメータが格納されています。

このようにしてハイパーパラメータを絞り込んでいくとよいと思います。

まとめ

scikit-learnではpython機械学習ともに初心者の僕でも、簡単につかえることがわかりました。

とくに学習器のインターフェースが全て同じなのでアルゴリズムを変えて見るときも、ちょっとコメントアウトを1行差し替えるだけで 変えられて便利に感じました。

ただ、ひとつひとつのパラメータ、アルゴリズムの意味を理解せず、むやみに学習器をかえて、グリッドサーチでたくさん試してってやるのも時間の浪費ですので、まず学習器の理論を学び、ある程度目処がついた段階でグリッドサーチなどでパラメータチューニングを行い、認識精度を安定、高めていく作業に取り掛かっていくとよいのかなと思いました。