川雲さんの分析ブログ

興味のあることの分かりにくい要約です。最近は機械学習、データ分析など。

「ツールボックスとしての機械学習」のアプローチ

1. 機械学習の二つのアプローチ

最近読んだベイズ推論による機械学習入門の中で、機械学習の代表的なアプローチが二つ紹介されていました。

1) ツールボックスとしての機械学習

既存のアルゴリズムにデータを与えて予測するものです。
汎用的なアルゴリズムを様々なデータに使いまわせるので、比較的簡単にモデルを構築することが出来ます。
一方で高性能なモデルを手に入れるには、特徴量抽出に精を出すことや、様々なアルゴリズムを試して最も性能の良いものを選択するなどの作業が必要です。
scikit-learnなどのライブラリに含まれるサポートベクターマシン、ブースティング、ランダムフォレストがその代表です。

2) モデリングとしての機械学習

データに関するモデルを事前に構築して、モデルの含むパラメータや構造をデータから学習するものです。
解きたい問題毎に異なるモデルを構築するので、ツールボックスを使うときよりも高性能なモデルが期待できるそうです。
一方でこのアプローチを使いこなすには、ある程度の数学の知識が必要であることや、計算時間やメモリコストの問題があるそうです。
時系列モデル、隠れマルコフモデル、線形動的システムや深層学習がこちらに含まれます。

因みに、著者である須山さんのブログには4つと書かれていました。
興味がある方はどうぞ。

machine-learning.hatenablog.com

この本は後者の「モデリングとしての機械学習」を解説した書籍ですが、今回は前者をまとめます。
後者はまた追々に機会があれば。

2. ツールボックスとしてのscikit-learnの使い方

今や機械学習の最も有名なツールの一つがscikit-learnです。
大抵の機械学習の書籍にはこのツールの使い方が書かれていると思います。
私もPythonで始める機械学習という本で勉強しました。
大雑把な使い方は以前まとめましたが、あまりに説明が少ないと思いましたのでもう一度記します。

rio-cloud.hatenablog.com

2.1. scikit-learnのインポート

前回と同様、有名なデータセットであるirisデータをSupport Vector Machineで分類します。
他にも様々なデータセットがscikit-learnには含まれています。

5. Dataset loading utilities — scikit-learn 0.19.1 documentation

# Data set
from sklearn.datasets import load_iris

# Model
from sklearn.svm import SVC

# Utilities
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import GridSearchCV, train_test_split

2.2. データ準備

次に、データを読み込みます。
まずすべきことは、データを訓練とテスト用に分割することです。

これは、過学習を防ぐために必要なことです。
例えば、受験勉強の場合を考えてみましょう。
よく赤本などで大学の過去問を勉強しますが、その目的は他に応用できる普遍的な何かを学び取ること、つまり「汎化」することです。
もし過去問しか解けないのであれば、それは過去問に「過学習」していると言えます。
では、過学習しているか否かをどう判断するかというと、まだ見たことのない新しい問題を解けるか試せばよいのです。
この「新しい問題」のことをテストデータと呼びます。

# Data preparation
iris = load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y)

2.3. スケーリングとパイプライン

次に、モデルの準備をします。
モデルの準備にも色々ありますが、今回はスケーリングだけを扱います。

2.3.1. スケーリング

スケーリングとは、データのスケール(値の大きさ)を揃える作業です。
例えば、身長と体重から血圧を予測する回帰式を仮定しましょう(そんなことは出来ないでしょうけれど)。 一人を例にとると、120(mmHg)=w165(kg)+w21.75(m)のような回帰式になります。
この式を見ると、「単位」と「値の大きさ」が揃っていない所が気になります。
数式だけを考えた場合、単位はどうでもよいことです(数式の解釈が難しいだけ)。 一方で、値の大きさや範囲、すなわちスケールが異なることは回帰式にとって重大なマイナスポイントとなります。

値のスケールが異なることは、例えば正規化項をモデルに導入したときに問題を生じさせます。
正規化とは、モデルのバリアンスを下げるために目的関数に導入されるものです。
例えば、有名なL2ノルム正則化では、パラメータのL2ノルム(ベクトルの大きさ)を小さくする項を導入します。

||w||2=(w12+w22)1/2

ここで重要なことは、w1w2の値の大きさが揃っていないと[tex: ||w||2]が変わってしまうことです。
例えば先程の血圧の例ですと、仮にw1=1とした場合はw2=31.43となり、従って[tex: ||w||
2 = 31.44]となります。
つまり、L2ノルムのほとんどを[tex: ||w||2]が占めているので、L2ノルムを小さくするには[tex: ||w||2]だけを小さくすればよいことになります。
従って、身長は係数が小さいために殆ど考慮されず体重のみで血圧を予測するようになるのです。

このように、スケールが異なると正しい正則化が困難になります。
ですから、スケーリングして各値の大きさと範囲を揃えるのです。

スケーリングにも幾つか種類があり、代表的なものを挙げます。
名前はscikit-learnに実装されているクラス名と対応しています。

  1. Standard Scaler: 標準正規分布に従うように値を変換します。具体的には、平均で引いた後に標準偏差で割ります。z値などとも呼ばれています。
  2. MinMax Scaler: 値を[0, 1]の範囲に収めます。計算式は(x - min) / (max - min)です。
  3. Normalizer: ベクトルのノルムを1にします。例えば、L2ノルム(ベクトルの長さ)を1にすると、全ての点が球面上に張り付きます。

どれを使うかは、モデルがどのような仮定を置いているかに依存すると思われます。
例えば、線形回帰ではデータが正規分布に従うと仮定しているので、Standard Scalerが良いでしょう。 今回扱うサポートベクターマシンにはMinMax Scalerが良いそうです。
その理由を私なりに解釈すると、サポートベクターマシンは各データ点間の距離を元にして識別関数をチューニングするので、全てのデータ点がある一定の範囲内に収まってくれると距離の値が大きくなりすぎず、計算に都合が良いのでしょう。

2.3.2. パイプライン

スケーリングとセットで使うのがパイプラインです。
パイプラインとは、一連の処理を一纏めにしたオブジェクトのことです。
今回は、スケーリングとモデルを一纏めにしたパイプラインを作成します。

何故パイプラインが必要なのでしょうか?
それは、「情報の漏洩」を防ぐためです。
「情報の漏洩」とは、テストデータの値を使って訓練データを加工することを指します。
今回ですと、テストデータを含んだデータの分布を使って訓練データをスケーリングすると、訓練データはテストデータを「知っている」ことになります。
これがどのような弊害をもたらすのかは、今回の参考資料である「Pythonで始める機械学習」に興味深い例題が掲載されているのでそちらをご参照ください。

# Pipeline
pipe = Pipeline([('scaler', MinMaxScaler()), ('clf', SVC())])

2.4. グリッドサーチと交差検証

そしていよいよモデルを学習していきます。
ここでは、グリッドサーチと交差検証という二つのテクニックを用います。

2.4.1. グリッドサーチ

グリッドサーチとは、ハイパーパラメータを調整するためのテクニックです。
そもそもモデルには、パラメータとハイパーパラメータという二つの自由に設定できる値があります。

  • パラメータ: データから学習する定数(例:線形回帰の係数)
  • ハイパーパラメータ: モデルに対して人が任意に与える定数(例:最近傍法の近傍数)

ハイパーパラメータは決め打ちで何か与えないといけないのですが、この値がまずいとモデルの精度が出ません。
そこで、このハイパーパラメータを調整するテクニックがグリッドサーチなのです。
具体的には、ハイパーパラメータを数パターン用意してそれぞれのモデルを作成し、最も性能の良いモデルを採用します。
当然ですが、パターンが多いとより詳細にハイパーパラメータを計算できますが、計算時間もかかります。
一般的なハイパーパラメータの調整方法があるので、それを参考にして効率よく計算したいものです。

2.4.2. 交差検証(Cross Validation)

そして、「モデルの性能」をより正確に見積もるためのテクニックが交差検証です。
交差検証では、訓練データを更に二分して、「既知のデータ(訓練データ)」と「未知のデータ(評価データ)」に分割します。
この分け方を何通りも行うのが交差検証です。
例えば訓練データを5分割した場合、4グループ(全データの80%)を訓練データにしてモデルを学習させ、残りの1グループ(20%)を評価データとして性能を評価します。
このプロセスを、評価データに選ばれる1グループを入れ替えながら5回行えば全てのデータを使って評価できます。
このように、限りあるデータ数を減らさずに効率よくかつ精密にモデルの評価を行う手法が交差検証です。

以下のプログラムでは、サポートベクターマシンのハイパーパラメータであるCとgammaの値をグリッドサーチで調べています。
さらに、GridSearchCVで一つのモデルを5分割の交差検証で評価しています。
つまり、ハイパーパラメータ二つを各7パターン、かつそれぞれ5分割交差検証ということで、7 * 7 * 5 = 245個のモデルを作成しています。
このように、計算時間との兼ね合いでハイパーパラメータの候補数、交差検証の回数を決めるとよいでしょう。

# Grid search
param_grid = {'clf__C': 10 ** np.arange(-3, 3, 1.),
              'clf__gamma': 10 ** np.arange(-3, 3, 1.)}
grid = GridSearchCV(pipe, param_grid, cv=5)

# Model fitting
grid.fit(X_train, y_train)

2.5. 予測

これで最適なモデルが出来ました。
grid.fit()関数は、ハイパーパラメータのグリッドサーチが終了すると最も性能の良かったハイパーパラメータを採用し、全ての訓練データを使ってモデルを学習してくれます。
つまり、現段階でgridは最高のモデルであるはずです。
このモデルを使って最初に分割して残しておいたtestデータを評価します。
testデータは最後の最後に汎化性能を測るのがセオリーだそうです。
これで、スコアが0.92と出ました。

# Predict
grid.score(X_test, y_test)

ちなみに、データ分析コンペのKaggleではテストデータを更に二つ(publicとprivate)に分割します。
コンペの開催期間中はpublicのtestデータで性能を評価し、この値を良くするようにモデルを改善します。
そして、コンペの終了後にprivateのtestデータでもう一度モデルの汎化性能を評価するのです。
これは、テストデータに対する識別性能を良くしようとするがために、テストデータに過学習するのを防ぐための措置です。

3. 最後に

これでscikit-learnの基本的な使い方は述べたつもりです。
しかし、教師無し学習などは触れていません。
中々評価しにくいですからね。

今後は、「ベイズ推論による機械学習入門」の本題である「モデリングとしての機械学習」もまとめたいと思います。

以上です。