<この記事は「 Money Forward Advent Calendar 2015 」の5日目の記事です。遅れてすみません>
概要
- 機械学習におけるベクトル化およびその書き方について触れます
対象
- 機械学習初心者
- 行列のバックグラウンドがない・暫く使ってない人
やらないこと
- 行列、内積、転置といったものの説明
Intro
Googe, Microsoft, IBMなどのIT巨人たちが機械学習システムをオープンソース化するなど、この分野における盛り上がりが急加速している様に感じます。私的な意見(希望かもしれない)ですが、第3次AIブームは冬を迎えずに定着し、「機械学習を使えて当たり前 + それを差別化」するサービス作りを意識する段階にきてるのではないでしょうか。
なので、私は直近2・3ヶ月のインプット活動を、専らMOOCの受講やMLPシリーズの読み込みなどにわりあててきました。
その中で感じたことは数学を避けてきた社会人プログラマが機械学習の勉強を始める際の最短経路にもありましたが、「最低限行列とベクトルの扱いに慣れていないと、コース内でさらっと行列の操作があっても、今それ何やってるんでしたっけという状態になりやすい(引用)」です。私は大学で制御工学・メカトロニクスを専門にしていたので、それなりのバックグラウンドがあったのですが、それでも苦戦しました(4年のブランクがあったたから、と言い訳します)。そんな自分でも苦労したんだから、同じ立場もしくはバックグラウンド皆無の人については厳しいやろ!という若干上から目線で、ベクトル・行列を使った機械学習について例といっしょに書いてみようと思います。
なぜベクトル・行列を使うのか
単純に実行(学習)速度が早いからです。行列を使わない場合はfor-loopを使って各サンプル毎の学習をすることになるのですが、n > 1000ぐらいになると劇的に学習速度が失速します。これは、まあインタプリタ型言語であるOctaveやPythonだと、for分ごとにオーバーヘッドが発生してしまうため(らしい)です。従ってfor-loopより行列での学習が推奨されます。
参考までに、わたしがMLP(多層パーセプトロン)モデルのMNISTデータ(28x28画素)学習を行列版とfor文版の両方を実装しようとした時のパフォーマンスを紹介します。
- 学習用データセット: 14000サンプル
- 特徴量: 764
- 隠し層のユニット数: 100
- epoc: 100
- 実行環境
- 行列版にかかった学習時間: 38.1sec
- for文版にかかった学習時間: (体感)10min
- 正直実装に間違えがあるかもしれないのですが、デバッグがしんどいので検証してません
どうやってベクトル化するのか。
シンプルなロジスティック回帰で考えてみましょう。今回はzと勾配の微積分をベクトル化してみます。
State zのベクトル化
活性化関数に突っ込む前のベクトルz(の各要素)は下記のように計算できます。
上記をまずfor文で実装するのですが、それを行列を使った形に実装することを目標にします。
それはつまりzを一コマンドだけで以下の形にしたいと言う考えになります。
- x -> 入力サンプル
- theta -> 各データのパラメタ
- m -> データセット数
- n -> 次元
そして、上記の各カラムは以下の様に変形できます。そういうもの として認識ください。
- 詳しくは「内積」でググッてほしいのですが、ベクトルの内積では
どっちが先にきても要素ごとの足し算になる(theta0x0 + theta1x1 + …)ということです。
上記の通り、zの中の各要素はベクトルxとベクトルthetaの内積となっております。
これをfor文を使わずにxとthetaで表現するには、下記のように各ベクトルx(の転置)を行に重ねたようなXを作ります
で、そうすると以下のような、シンプルな式ができます。
実装
これをOctave/pythonで実装すると、下記のようにすっきりした形にできます。
z = X * theta;
# numpyを使った方法だと
np.dot(theta, X)
例2 勾配のベクトル化
最適なパラメタを見つけるには、評価関数の各パラメタに対する偏微分が0になる値、あるいは閾値より下回るようなパラメタをみつけたいので、偏微分の式を実装する必要があります。
$$\frac{\partial J}{\partial \theta_j} = \frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)}) - y^{(i)}) x_{j}^{(i)}$$
これをベクトル化すると
さて、これを変形するには、まず下記の法則が使えます。そういうものとして認識ください。
xがずらずらと並んでる行列は、zのベクトル化でも紹介したXの転置なので、
よって、下記のようにコスト関数の偏微分をベクトル化したものを書けます。
実装
以下の様に簡潔にかけます。
h = activate_function(z)
grad = 1 / m * (X'*(h-y))
h = activate_function(z)
grad = 1/m * np.dot(X.T, h-y)
総括
以上が、ベクトル化に関する説明となりました。これで初学者の心に対して添え木をできえれば、と思っております。ただ、どこかミスってる気がしてなりませんので、ご意見・コメントお待ちしております。
読んだ書籍・受けたコース
- 深層学習
- データサイエンティスト養成読本・機械学習入門編
- ITエンジニアのための機械学習入門
- Building Machine Learning Systems with Python
- Andrew Ng教授のMachine Learning
- ワシントン州立大学のMachine Learning
余談
- 初学者がどこで躓くか知りたい
- kobitoにlatex記述できるようにしてほしい(切実)
- Qiitaってもしかして、行列かけない...?
- 仕方なくgyazoったけど、絶対いいやりかたがあるはず
- 教科書作った人は凄い
- これを作ってる際に、私のこころが折れそうだった。