Edited at

[初心者向け]機械学習におけるベクトル化 入門

More than 3 years have passed since last update.

<この記事は「 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

  • 実行環境
    スクリーンショット 2015-12-06 13.48.35.png

  • 行列版にかかった学習時間: 38.1sec

  • for文版にかかった学習時間: (体感)10min


    • 正直実装に間違えがあるかもしれないのですが、デバッグがしんどいので検証してません




どうやってベクトル化するのか。

シンプルなロジスティック回帰で考えてみましょう。今回はzと勾配の微積分をベクトル化してみます。


State zのベクトル化

活性化関数に突っ込む前のベクトルz(の各要素)は下記のように計算できます。

上記をまずfor文で実装するのですが、それを行列を使った形に実装することを目標にします。

image

それはつまりzを一コマンドだけで以下の形にしたいと言う考えになります。


  • x -> 入力サンプル

  • theta -> 各データのパラメタ

  • m -> データセット数

  • n -> 次元

image

そして、上記の各カラムは以下の様に変形できます。そういうもの として認識ください。

* 詳しくは「内積」でググッてほしいのですが、ベクトルの内積では

どっちが先にきても要素ごとの足し算になる(theta0*x0 + theta1*x1 + …)ということです。

image

上記の通り、zの中の各要素はベクトルxとベクトルthetaの内積となっております。

これをfor文を使わずにxとthetaで表現するには、下記のように各ベクトルx(の転置)を行に重ねたようなXを作ります

image

で、そうすると以下のような、シンプルな式ができます。

image


実装

これをOctave/pythonで実装すると、下記のようにすっきりした形にできます。


octave.m

z = X * theta;



python.py

# 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)}$$

これをベクトル化すると

image

さて、これを変形するには、まず下記の法則が使えます。そういうものとして認識ください。

image

xがずらずらと並んでる行列は、zのベクトル化でも紹介したXの転置なので、

image

よって、下記のようにコスト関数の偏微分をベクトル化したものを書けます。

image


実装

以下の様に簡潔にかけます。


octave.m

h = activate_function(z)

grad = 1 / m * (X'*(h-y))


python.py

h = activate_function(z)

grad = 1/m * np.dot(X.T, h-y)


総括

以上が、ベクトル化に関する説明となりました。これで初学者の心に対して添え木をできえれば、と思っております。ただ、どこかミスってる気がしてなりませんので、ご意見・コメントお待ちしております。


読んだ書籍・受けたコース


余談


  • 初学者がどこで躓くか知りたい

  • kobitoにlatex記述できるようにしてほしい(切実)

  • Qiitaってもしかして、行列かけない...?


    • 仕方なくgyazoったけど、絶対いいやりかたがあるはず



  • 教科書作った人は凄い

  • これを作ってる際に、私のこころが折れそうだった。