scikit-learnの実装と設計|Pythonで学ぶシステム設計 #2

f:id:lib-arts:20190110181457p:plain

#1の記事ですが、システム設計の基礎的な話をするにあたって『オブジェクト指向のこころ』を参照し、概要についてまとめました。一冊ざっと読み切ってみて身近な具体例の方が良いということで、後ろの方でscikit-learnについて少し取り扱ってみました。

#1で取り扱ったのは簡単な読み方の流れのみだったので、#2ではscikit-learnの実装を改めて取りまとめ、考察していければと思います。

(必要以上に固く書きたくなく90%〜95%程度の確信度で書きますので、もし間違いなどがあれば気軽にご指摘いただけたらと思います。)

以下目次になります。

1. Pythonの実装の読み方の補足
2. #1の復習&sklearn.linear_model(回帰分析)の詳細
3. sklearn.dataset(irisなどのサンプルデータのAPI)の実装
4. 考察


1. Pythonの実装の読み方の補足
#1ではシステム設計の原理原則を抑えるためにPythonの実装の読解にそこまで重きを置いていませんでしたので、Pythonの読み方について補足させていただきます。
色々とやり方はあると思うのでベストなやり方かまではわからないですが、コード解析法はあくまで手段なので実際にソースコードの中身が理解できれば良いです。ですので参考程度に見ていただけたらと思います。

0. ライブラリが実現している機能の背景について学ぶ
-> scikit-learnであれば機械学習の仕組みetc、DjangoであればWeb開発の仕組みetcです。というのも読むにあたって、機能の実装はだいたいこうなっているというあたりをつけやすくなるからです。

1. ドキュメントで興味のある関数やメソッドの使い方を把握する(使用してみる)
-> まずは動かしてみると良いです。また、全体を読まなくても興味のある実装をいくつか読むだけでも全体の設計思想やコーディングの雰囲気についてはある程度ライブラリを読み込んでいけば理解することができます。ですので、興味のある機能について一旦注目すると良いです。

2. 把握した使用方法のライブラリの実装をたどってみる
-> 基本的にはライブラリはimport文を使って読み込む際には__init__.pyから読み込んでいます。ですのでここから実装本体を参照します。基本的にはimport元のディレクトリ内に本実装は用意されているのですが、場合によっては別のディレクトリに飛ばしているケースもあります。
-> 気になる機能の実装の本体について見つかればそれを読み進めます。

3. クラスの継承を行なっている際は元クラスに実装があるケースもあるので注意する
-> scikit-learnだと大体がfitとpredictのメソッドを探すことになるのですが、物によってはどちらかが見つからないというケースもあります。その際は大概継承元のクラスに実装されていますのでそちらも合わせて読むと良いです。

上記が解析の手順例です。よほど読みにくいソースでなければこの手順でだいたい読めると思います。ちなみに今回取り扱うscikit-learnはよく使われるライブラリである一方比較的実装が読みやすく、初めて読むにはちょうど良い難易度なのではと思います。Webが得意な方はDjangoもいくつか見た感じだと追いやすい印象だったのでDjangoも面白いのではと思います。


2. #1の復習&sklearn.linear_model(回帰分析)の詳細
#1では、
https://scikit-learn.org/stable/modules/linear_model.html
より得たサンプルコードを軽く見てみました。

>>> from sklearn import linear_model
>>> reg = linear_model.LinearRegression()
>>> reg.fit([[0, 0], [1, 1], [2, 2]], [0, 1, 2])
...
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None,
normalize=False)
>>> reg.coef_
array([0.5, 0.5])

実装元を追っていくことで、
https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/linear_model/base.py
までたどり着けたかと思います。

#2ではここから実際にscikit-learnがメイン処理でSciPyを用いているところまで見られればと思います。
サンプルコードではfitメソッドを使用した後最終的にreg.coef_で回帰分析の係数を取得しています。

class LinearRegression(LinearModel, RegressorMixin):
...
def fit(..):
....
return self


上記においてself.coef_に値を入れているところがメイン処理だと思われます。

1. out = sparse_lsqr(X, y)
    self.coef_ = out[0]

....

2. self.coef_, self._residues, self.rank_, self.singular_ = linalg.lstsq(X, y)

※ 1,2はそれぞれif-elseで書かれているのでどちらかしか実行されない

このとき上記のような実装が見つかるのですが、このlinalg.lstsqやsparse_lsqrはファイルの上の部分でimportされているSciPyの機能です。また、SciPyのドキュメントを検索するとこれは最適化のモジュールのようであるというのがわかります。

このことからscikit-learnはSciPyを裏では叩いているのではという仮説が立てられます。概要を把握して設計の理解の参考にするのが今回の目的だったので、一旦ここまでとしました。


3. sklearn.dataset(irisなどのサンプルデータのAPI)の実装
sklearn.linear_model以外にも読んでみたかったので、sklearn.datasetについても読んでみました。
https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_iris.html#sklearn.datasets.load_iris
2節と同様な流れで読んでいくために、上記のUser Guideからsklearn.datasets.load_irisのサンプルを引っ張ってきました。

>>> from sklearn.datasets import load_iris
>>> data = load_iris()
>>> data.target10, 25, 50
array([0, 0, 1])
>>> list(data.target_names)
['setosa', 'versicolor', 'virginica']


上記のimport関連を読むに、実際の実装をたどるにあたってはProjectルート/sklearn/datasets/__init__.pyを確認すれば良いことがわかります。
https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/datasets/__init__.py
上記を確認したところ、

from .base import load_iris

という行が見つかります。そのため、同じディレクトリ内のbase.pyを確認します。
https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/datasets/base.py
上記にはload_irisという関数があり、

data, target, target_names = load_data(module_path, 'iris.csv')

という行でファイルを指定して読み込みを行なっています。module_pathをたどっても良いですが、load_dataのコードも読まなくてはいけなく面倒だったためファイル名検索をかけて
https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/datasets/data/iris.csv
を見つけました。これが通常のirisデータと同様なデータのため、scikit-learnのdataset APIの仕様は基本的にはライブラリに置いてあるデータをロードしているというのが確認できました。

 

4. 考察

・各クラスのfitやpredictメソッドに責任の移行が行われており、制御プログラムは統一的に処理を記述できる
・__init__.pyに集約する形で書かれている(継承よりも集約を用いて実装すべき)
・基本はSciPyのラッパーで最適化処理などの処理はSciPyに投げている

基本的にはscikit-learnについての考察は前回まとめた通りです。だいたいの実装の雰囲気はつかめたので、より深く読むにあたっては機械学習のそれぞれのメソッドがどのような関係で書かれているかを確認すると良いのではと思っています。例えばRandomForestがDecisionTreeとどのような関係で実装されているのかなどを調べるともっと深いレベルでの理解が可能になりそうです。

他のライブラリの実装例も色々とまとめてみたいので、今回は一旦ここまでとできればと思います。
#3ではWebフレームワークについて見てみようということでDjangoを取り扱えればと思います。