人は学習した知見を脳に記憶する、機械学習では学習した知見をどう記憶すれば良いか

[前回](http://qiita.com/ynakayama/items/6a472e5ebbe9365186bd)は機械学習による未来予測の例として、決定木アルゴリズムで株価の予測をする話をしました。このように直近のポートフォリオの変化をあらわす数列の並びから次の数値の上げ下げを単純に予測するといった場面では、複雑なアルゴリズムに頼らずとも決定木のような単純明快な方法で低コストかつそこそこの精度による予測を実現することができます。

機械的な予測はたとえば短期のトレードにおいて威力を発揮するでしょう。週よりも日、日よりも時や分のように短いレンジのほうが向いているでしょうから、現物よりは信用取引などのほうが向いているかもしれません。もし中・長期的な投資をするならば、やはりファンダメンタルズが優良で PER が格安 ROE が良好な銘柄に投資するといった基本的な姿勢が大切になると思います。

[テクニカル指標の一覧](http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AF%E3%83%8B%E3%82%AB%E3%83%AB%E6%8C%87%E6%A8%99%E4%B8%80%E8%A6%A7)を見ればわかりますが、そもそも古くから伝わるこれらの計算式は決して複雑ではありません。またそのシグナルの判断基準もさほど難しいアルゴリズムではありません。このことを勘案すると、今日の機械学習を応用すれば、より正解率の高いアルゴリズムを考案したり、あるいはそれらを利用したシステムトレードも可能ではないかと思います。たとえば五年以内に機械学習アルゴリズムで武装した新世代の投資家が次々と市場に参入してくる、といったことも現実になりうるかもしれません。

## 学習した状態の分類器をシリアライズされたバイナリデータとして保存する

さて、一般的に教師あり機械学習では、教師データによって分類器を学習するわけですが、これらはどのように記憶されるのでしょうか。人間の場合は学習した記憶は脳細胞に記憶するわけですが、同様に機械においても学習した知見を蓄えておく必要があります。

教師データ全体を毎回フィッティングさせるとするとコストが高いので、できれば学習済みのインスタンスを次回以降も利用したいところです。そこで [pickle モジュール](http://docs.python.jp/3.4/library/pickle.html)を利用します。

[Python の pickle モジュール](http://diveintopython3-ja.rdy.jp/serializing.html)はオブジェクトをシリアライズします。これは Ruby でいう似たような動作をする [Marshal](http://ruby-doc.org/core-2.2.1/Marshal.html) モジュールに相当します。 pickle を利用することで、学習済みのインスタンスをシリアライズされたデータとして保存しておくことができます。

教師あり学習の動きを図にすると次のようになります。

![10.png](https://qiita-image-store.s3.amazonaws.com/0/38501/15349ecd-cf43-1d9e-36cd-543d379100dd.png)

[前回](http://qiita.com/ynakayama/items/6a472e5ebbe9365186bd)の記事で解説したとおり、機械学習ライブラリ scikit-learn では機械学習のクラスをもとにインスタンスを生成し、 .fit メソッドにて教師データへのフィッティング (= 学習) をおこないます。

```py3
# 機械学習のインスタンスを生成する (下の例では決定木による分類器)
from sklearn import tree
clf = tree.DecisionTreeClassifier()
clf.fit(features, labels) # 教師データで学習する
```

pickle モジュールは任意の Python オブジェクトを扱うことができ、これをバイトデータに変換として変換 (= シリアライズ) することができます。。

cPickle モジュールは pickle の C 言語による実装です。 cPickle は pickle と異なりサブクラス化することができません。 pickle よりはるかに高速なので基本的にはこちらを利用することを推奨します。テクニックとしては C 実装版が利用できる場合はそちらを、失敗したら通常の pickle をインポートするという手が使えます。

```py3
try:
    import cPickle as pickle
except:
    import pickle
```

次回以降の分類では、保存されたオブジェクトがある (= 記憶がある) 場合、たくわえた知見を呼び出して分類をおこなえば良いというわけです。

```py3
# インスタンスをファイルに書き込む
with open(filename, 'wb') as f:
    pickle.dump(clf, f)
```

たとえば日次更新のデータがありそれを教師データにする場合、ロードしたインスタンスにその日のぶんの教師データをフィッティングさせれば済むことになります。

また、知見がたくわえられていない場合のみ、新たにインスタンスを生成して教師データ全体を学習しなおすという作りにしておくと良いでしょう。

```py3
# ファイルが存在する場合のみ、インスタンスをロードする
if os.path.exists(filename):
    with open(filename, 'rb') as g:
        clf = pickle.load(g)
else:
    # ファイルが無ければ新規インスタンスを生成して学習しなおし
```

注意するべき点としては、インスタンスをシリアライズしたバイナリを他のホストで利用しようとしても、アーキテクチャが異なると正常に動作しません。複数の計算機で分析基盤を構築しているような場合は注意が必要です。また基礎となっている scikit-learn ライブラリのバージョンを変更する場合もそれまで蓄えた知見を一度破棄してもう一度いちから学習しなおしたほうが良いでしょう。

## データの傾向ごとに分類器を別々に用意することで汎化能力を高める

さらに[以前に説明したクラスタリングの方法](http://qiita.com/ynakayama/items/2efd2578fab73760c6e0)を応用するなどして、分類器のインスタンスを複数生成し、データの傾向によってそれに向いているインスタンスに予測させるというテクニックも使えます。

これも図にしてみましょう。

![11.png](https://qiita-image-store.s3.amazonaws.com/0/38501/3e843f70-7f54-ea4b-6616-b9c4852e927a.png)

教師なし学習である K 平均法クラスタリングによって、たとえば金融データであれば業種などに関係なく一定の類似度でデータがまとまります。こうしてまとまったクラスタごとに分類器のインスタンスを生成してフィッティングすれば、より汎化能力を高めた状態の分類器ができあがります。

K 平均によってクラスタリングしクラスタごとに学習すると、機械の知見の数 = k になります。

たとえば似た値動きをする銘柄同士を業種の垣根をこえてクラスタリングし、これらの経験から次の値動きを予測するといった応用方法が考えられます。

このようなときも複数のインスタンスをロード / アンロードするのに pickle モジュールが利用できます。

## まとめ

今回は機械学習における教師データの学習後の状態を pickle によってシリアライズして保存する (= 人間が脳の海馬に知見を記憶するのに相当) という話を説明しました。これは一例にすぎませんが scikit-learn の場合は使いやすいテクニックであると思います。