pythonによる機械学習[概要&最小二乗法編]

※当記事は、以下の書籍を参考にしてかかせていただきました。

自分の勉強のために気軽に書いているので記述ミスなどあるかもしれません。もし見つけた場合はご連絡ください><

さて、ということで。

最近機械学習という言葉がバスワードになってますね。

機械学習とは、データサイエンスの観点から見ると

「過去のデータを分析して未来を予測する際に役立つ判断ルールを見つける仕組み」

という感じのものです。

主な機械学習アルゴリズム

まず、主にどんな機械学習アルゴリズムがあるのか、ざっくりと説明していきます。 

分類をするアルゴリズム

複数のクラスに分類されたデータを分析して、新規のデータがどのクラスに分類されるかを予測するルールを導きます。

パーセプトロンやロジスティック回帰があります。

未来の数値を予測するアルゴリズム

過去のデータから関数を導き、その関数から未来の数値を予測します。

最小二乗法や最尤推定法などがあります。

グループ化を行うアルゴリズム

クラスタリングと呼ばれていて、データをなんらかの類似性を元に自然に分類します。

教師なし学習という答えの与えられていないものを学習させる手法で、例えば手書き文字の認識などに使われています。      

今回は、一番学びやすいと言われている、未来の数値を予測するアルゴリズムである最小二乗法の理論を説明し、pythonで実装してみたいと思います。

最小二乗法

最小二乗法は、回帰分析の基礎となるアルゴリズムです。

与えられたデータがとある多項式f(x)から生まれていると仮定して、多項式から得られる値と実際のデータの値の誤差を最小になるように多項式の係数を決定します。

理論

一つずつ式の意味を理解して追っていくと、すんなりと読むことができると思います。(逆に言うと、飛ばし飛ばしで読むと数学なので理解しづらいです)

まず、特徴変数と呼ばれる変数をx,目的変数と呼ばれる変数をtとします。

例えば、お店の売り上げを考えてみます。「◯◯日の売り上げは◯◯円でした」という風に決まる時、日付が特徴変数で売り上げが目的変数です。

何個かの(x,t)をデータで持っていて、新しくxが与えられた際にtの値を推定するのが最小二乗法の目標です。

実際に持っているN個のデータを{ \displaystyle {(x_n,t_n)}_{n=1}^{N} }とします。

データから得られる関数関係を推測するために、とりあえず以下のような多項式f(x)を考えます。

{ \displaystyle
f(x)=w_0+w_1 x+w_2 x^{2}+\cdots+w_M x^{M}
    =\sum_{m=0}^{M} w_m x^{m}\tag{1}
}

Mは、次数を示しています。実際に計算する際は何次式にするかを決めてから計算するので固定された値となります。(Mの決め方は後ほど)

この時、実際に観測された{ \displaystyle {(x_n,t_n)} }の値との差の2乗和をEと置くと、

{ \displaystyle
E=\{f(x_1)-t_1\}^{2}+\{f(x_2)-t_2\}^{2}+\{f(x_3)-t_3\}^{2}+\cdots+\{f(x_N)-t_N\}^{2}
}

となります。(のちの計算の便宜上、{\frac{1}{2}}をかけていますが、誤差の指標としては問題なく扱えます。)
この式は、以下のように表せます。

{ \displaystyle
E=\frac{1}{2} \sum_{n=1}^{N} \{f(x_n)-t_n\}^{2}\tag{2}
}

(2)式に、(1)式を代入すると、

{ \displaystyle
E=\frac{1}{2} \sum_{n=1}^{N} \left( \sum^{M}_{m=0} w_{m} x_{n}^{m}-t_{n}  \right)^{2}\tag{3}
}

となります。

このEを、最小にするのが最小二乗法です。

(3)の式で、具体的な値がわからないのは{ \displaystyle \{w_0 \cdots w_M\}}ですね。

Eが最小のとき、(3)を{ \displaystyle \{w_0 \cdots w_M\}}の関数ととらえた際の偏微分係数が0になるという条件があります。

これを式で表すと、

 {\displaystyle
\frac{\partial E}{\partial w_m}=0 \; (m=0, \cdots ,M) \tag{4}
}

となります。

(4)に(3)を代入して偏微分をすると、以下の式(5)が得られます。

 {\displaystyle
\sum_{n=1}^{N} \left(\sum_{m'=0}^{M} w_{m'} x_{n}^{m'}-t_{n} \right) x_{n}^{m}=0\tag{5}
}

(5)を、以下のように式変形します。

 {\displaystyle
\sum_{m'=0}^{M} w_{m'} \sum_{n=1}^{N} x_{n}^{m'} x_{n}^{m}-\sum_{n=1}^{N} t_{n} x_{n}^{m}=0\tag{6}
}

ここで、 {w=(w_0, \cdots ,w_M)}をまとめてベクトル{\mathbf{w}=(w_0, \cdots ,w_M)^{T}}とします。

※当ブログでは縦ベクトルを基準としています。

また、目的変数を並べたベクトルを{\mathbf{t}=(t_1, \cdots ,t_N)^{T}}とし、{x_{m}^{n}}を以下のようなN×(M+1)行列とします。

{\displaystyle
\Phi=
\left( \begin{array}{rrcr}
x_{1}^{0}   & x_{1}^{1}   & \cdots   & x_{1}^{M}\\\
x_{2}^{0}   & x_{2}^{1}   & \cdots   & x_{2}^{M}\\\
\vdots       & \vdots       & \ddots   & \vdots\\\
x_{N}^{0}   & x_{N}^{1}   & \cdots   & x_{N}^{M}
\end{array}\right)\tag{7}
}

数式が少しややこしくなってきました…

(7)を使うと、(6)を以下のように書き換えることができます。

{\mathbf{w}^{T} \Phi^{T} \Phi-\mathbf{t}^{T} \Phi=0\tag{8}}

すごくきれいになりました。

あとは、(8)を行列を変形して{\mathbf{w}= }に書きなおすだけです。

{\displaystyle
\mathbf{w}=(\Phi^{T} \Phi)^{-1} \Phi^{T} t\tag{9}
}

これで、{\mathbf{w}=}の形に直せました。

本当は逆行列の存在などをヘッセ行列の性質を用いたりして確認しなければならないのですが、かなり脱線してしまうので今回は割愛させていただきます。

(9)に具体的な値を入れることで、(1)の式が完成し、与えられたデータから関数関係を導き出せます。

そして、目標の新しく与えられたxに対応するtの値を(1)式から推測することができ、目標達成です!

実装

サンプルとして、sin関数に正規分布標準偏差0.7の雑音を付加したデータを作り、最小二乗法で近い多項式を求めてグラフにするプログラムを作成しました。

少し長くなってしまったのもあり、gistに貼り付けました。

使ったpythonのライブラリは、numpy,pandas,matplotlibです。

それぞれpipなどで導入できます(macはmatplotlibだけconfigファイルみたいなの設定するので調べてみるといいかも)

理論のお話と照らし合わせると読みやすいかもです。

python2.7.10です。

これを使って、学習データ数Nを10,多項式の次数Mを1, 3, 7, 9とした時のグラフは次のようになりました。

f:id:palloc:20160111094454p:plain

次数が1のときは1次方程式で直線になっていることがわかります。次数がどんどん上がってくる(自由度が高くなる)と、平方平均二乗誤差RMSが下がってきて、次数が9、すなわち多項式の自由度が10のときRMSが0になっていることがわかります。

誤差を最小に近づけるのが最小二乗法の手法でしたが、どのグラフが一番元の{\sin(2\pi x)}に近いかを見ると、M=3の時ですよね。

M=9の時のような状態を、過学習やオーバーフィッティングと呼びます。

与えられたデータが持っているノイズに、自由度の高い多項式は引きずられてしまうというイメージです。

過学習を抑えたい場合は、学習用データをたくさん集めることがとりあえずは有効でしょう。

先ほどのプログラムで、学習用データを100個用意した場合のM=1, 3, 7, 9のグラフを見てみましょう。

f:id:palloc:20160111111414p:plain

だいぶ過学習が抑制されていますね。

また、ベイズ的アプローチをするといいのですが、それはまたの機会に。

汎化

様々な次数のグラフから、汎化(要するに最適なMを見つける)する手法としては、ホールドアウト法やクロスバリデーションなどがあります。

ホールドアウト法

現在持っているデータを、学習用(トレーニングセットと呼ばれる)とテスト用(バリデーションセットやホールドアウトセットと呼ばれる)に分けて、学習用で得られた式がテスト用でどれだけ当てはまるか(誤差が少ないか)を確認することで、汎化したグラフを得ます。

クロスバリデーション

ざっくり言うと、ホールドアウト法を何回も行う方法です。

まず、データを例えば5パートに分けてみましょう。

そのうちの1つをテスト用、残りの4つを学習用として使い、ホールドアウト法を行います。

そして、得られた結果を記録して、テスト用にするパートを入れ替えてまたホールドアウト法を…という作業を5回繰り返します。

5個、検証結果が得られると思うので、それらから最適な次数Mを決めます。

これがクロスバリデーションと呼ばれる汎化の方法です。

まとめ

未来が予測できるよ!すごい!(こなみ

すごいざっくりと機械学習入門してみました。

最尤推定法とかで誤差の予測とかもできるので、時間があれば追加で記事書きたいと思ってます。

セキュリティ界隈にずっといて機械学習とは無縁だったのですが、とあるきっかけで触る機会があり、ハマってしまいましたね…(特に数学的に)

最初に書いた参考書籍には本当にいつもお世話になっています。ありがとうございます。