CNNは画像認識の分野で驚異的な精度を誇るディープラーニングのアルゴリズムのひとつであるものの、ぱっと見がとても複雑な構造をしているため、実装するのも大変そうです。
実際、ネットや文献上で見られる多くのCNNの実装は、Theano (pythonのライブラリ)の自動微分機能を使っていたり、MATLABの組み込み関数を使っているものがほとんどです。
そのためか、きちんと forward propagation & backpropagation を数式で書き下している文献はないように思いました。(もちろん、楽に実装できるならばそれはそれで素晴らしいことです。)
そこで、どうすれば CNN を実装するための数式を書き下せるのか、レイヤーごとに分けて導出していきたいと思います。
まず、CNN がどんな層に分解できるのかについて。これは、下記の3つで表せるでしょう。
Convolution Layer: 畳み込みレイヤー
Activation Layer: 活性化レイヤー
MaxPooling Layer: プーリングレイヤー
この3層をひとつのセットとして、ディープラーニングの場合はこのセットを積み重ねていくのが基本形です。(プーリングレイヤーは、Max PoolingがデファクトスタンダードなのでMaxPooling Layerと書いています。また、モデルの最後に付く通常の多層パーセプトロン層はここでは省いています。)
しかし、この順番は特に決まっているわけではなく、研究によっては Convolution → MaxPooling → Activation で実験していたり、Convolution → Convolution → Activation → MaxPooling で実験していたりと様々です。
複雑に見えるCNNですが、この3つのレイヤーの feed-forward と backpropagation を数式で表してしまいさえすれば、どんな言語でも実装が可能になるはずです。
では、順番に見ていきましょう。まずは畳み込みレイヤーから。
まずはと言いつつ、この層はカーネルの学習があったり、複数チャネルの場合があったりと一番煩雑になるため、簡単のために最初は画像が1チャネル(グレースケール)の場合にどうなるかを考えてみます。
・Convolutional Layer (1 channel)
さて、理解しやすくするために、下の図に則って数式を表わしていくことにします。
\( M, N \) はインプット画像のサイズ、\( m, n \) はカーネル(畳み込みフィルター)のサイズ、 \( k \) はカーネルのインデックス番号を表しており、
また、 \( x \) は入力画像データ、 \( a^{\,(k)} \) は、畳み込みレイヤーを通した後の2次元データを表しています。理解のしやすさのために、 カーネル \( \, k \, \) に対応しているんだぞ、ということを明記しています。
すると、畳み込みは以下の式で表すことができます:
\[ a_{ij}^{(k)} = \sum_{s = 0}^{m - 1} \sum_{t = 0}^{n - 1} w_{s\,t}^{(k)} \,x_{(i + s)(j + t)} + b^{(k)} \]
ここで、\(w^{(k)}\) はカーネルを、\(b^{(k)}\)はバイアスを表しています。これで、畳み込みレイヤーの feed-forward は書き下すことができました。
続いて、backward (backpropagation) について考えてみます。学習すべきモデルのパラメータは \( w^{(k)} \)および\( b^{(k)} \)であるため、
誤差関数を\( E \) で表したとすると、それぞれの勾配は次のように書くことができます:
\[
\begin{align}
\frac{\partial E}{\partial w_{s\,t}^{(k)}} &= \sum_{i = 0}^{M-m} \sum_{j = 0}^{N-n} \frac{\partial E}{\partial a_{ij}^{(k)}} \frac{\partial a_{ij}^{(k)}}{\partial w_{s\,t}^{(k)}} \nonumber \\\
\nonumber \\\
&= \sum_{i = 0}^{M-m} \sum_{j = 0}^{N-n} \frac{\partial E}{\partial a_{ij}^{(k)}} \, x_{(i+s)(j+t)} \nonumber \\\
\nonumber \\\
\nonumber \\\
\frac{\partial E}{\partial b^{(k)}} &= \sum_{i = 0}^{M-m} \sum_{j = 0}^{N-n} \frac{\partial E}{\partial a_{ij}^{(k)}} \frac{\partial a_{ij}^{(k)}}{\partial b^{(k)}} \nonumber \\\
\nonumber \\\
&= \sum_{i = 0}^{M-m} \sum_{j = 0}^{N-n} \frac{\partial E}{\partial a_{ij}^{(k)}}
\end{align}
\]
ここで、バックプロパゲーションの誤差
\[ \delta_{ij}^{(k)} := \frac{\partial E}{\partial a_{ij}^{(k)}} \]
は、前のレイヤー(feed-forwardでは次のレイヤー)から逆伝播してきているはずなので、上式を用いてモデルパラメータを更新することができることになります。
Convolution Layer から更に逆伝播が必要なとき(すなわちディープな層になっているとき)は、 畳み込み層の誤差
\[ \frac{\partial E}{\partial x_{ij}{(k)}} \]
を求める必要があるので、こちらも書き下してみましょう。次のように表すことができます:
\[
\begin{align}
\frac{\partial E}{\partial x_{ij}} &= \sum_{s = 0}^{m - 1} \sum_{t = 0}^{n - 1} \frac{\partial E}{\partial a_{(i-s)(j-t)}^{(k)}} \frac{\partial a_{(i-s)(j-t)}^{(k)}}{\partial x_{ij}} \nonumber \\\
\nonumber \\\
&= \sum_{s = 0}^{m - 1} \sum_{t = 0}^{n - 1} \frac{\partial E}{\partial a_{(i-s)(j-t)}^{(k)}} \, w_{s\,t}^{(k)}
\end{align}
\]
ここで注意すべきなのは、\( i - s < 0 \) または \( j - t < 0 \) となり得る場合があるということです。このときは
\[ \frac{\partial E}{\partial a_{(i-s)(j-t)}^{(k)}} = 0 \]
として計算をすることになります。
誤差の式を見ると、カーネル \( w \) を flip した(行・列ともにひっくり返した、すなわち180°回転した)フィルターとの畳み込みになっていることがわかるでしょう。
畳み込みを逆伝播するのでflipの畳み込みになっている、という様に解釈できるでしょうか。これで、次のレイヤー(feed-forwardでは前のレイヤー)に伝播すべき誤差を求めることができます。
・Activation Layer
続いて、活性化レイヤーです。最近のトレンドとして、sigmoid関数やtanh関数を使うよりも、ReLU (Rectified Linear Unit) を使うことが多いので、ReLUでの式を書いてみます。
これは全く難しくありません。まずは feed-forward から:
\[ a_{ij} = {\rm ReLU}\,(x_{ij}) = {\rm max} \,(0, x_{ij}) \]
また、 backward は次のように表せます:
\[
\begin{eqnarray}
\frac{\partial E}{\partial x_{ij}}
=
\begin{cases}
\frac{\partial E}{\partial a_{ij}} & \, if \,\, a_{ij} \geq 0 \nonumber \\\
\, & \, \nonumber \\\
0 & \, otherwise
\end{cases}
\end{eqnarray}
\]
・MaxPooling Layer
Max Pooling については、フィルターが正方形でなくても適用はできるものの、
正方形にしてもしなくても大差はないので、計算が簡単になる正方形で表すのが普通です。
この層も、学習すべきパラメータはないので、feed-forward, backward ともに 前後のレイヤーにつなげる式を書くだけで終わりです。
feed-forward は次のようになります:
\[
a_{ij} = {\rm max}\, ( x_{(i+s)(j+t)} ) \,\, {\scriptstyle where \,\, s \in [0, \,k], \,\, t \in [0, \,k] }
\]
ここで、 \( k \) はフィルタのサイズを表しています。
また、backward (backpropagation) は次のようになります:
\[
\begin{eqnarray}
\frac{\partial E}{\partial x_{(i+s)(j+t)}}
=
\begin{cases}
\frac{\partial E}{\partial a_{ij}} & \, if \,\, a_{ij} = x_{(i+s)(j+t)} \nonumber \\\
\, & \, \nonumber \\\
0 & \, otherwise
\end{cases}
\end{eqnarray}
\]
これで3つの層の feed-forward, backpropagation の導出ができました。
しかし、カラーの画像やディープラーニングに対応するためには、畳み込みレイヤーで複数チャネルの場合を考えなくてはなりません。
こちらについても考えてみましょう。
・Convolutional Layer (multi-channel)
複数チャネルの場合を図にすると、下のように表すことができます。
1チャネルのときに比べ、新しくチャネル\(c\) というパラメータが増えました。
そのため、伝播の式もそれぞれチャネルの分、ループが増えることになります。
feed-forwardの式を書いてみると次のようになります:
\[
\begin{align}
a_{ij}^{(k)} &= \sum_{c} a_{ij}^{(k, c)}
\nonumber \\\
\nonumber \\\
&= \sum_{c} \, \sum_{s = 0}^{m - 1} \sum_{t = 0}^{n - 1} w_{s\,t}^{(k)} \,x_{(i + s)(j + t)}^{(\nonumber c)} + b^{(k)}
\end{align}
\]
ここで、カーネル(およびバイアス)は各チャネルで共有されるため、\(w^{(k)}\) および \(b^{(k)}\) は \( c \) に依存しないことに注意したい。
すると、モデルパラメータの更新式は下記のように表すことができます:
\[
\begin{align}
\frac{\partial E}{\partial w_{s\,t}^{(k)}} &= \sum_{i = 0}^{M-m} \sum_{j = 0}^{N-n} \, \sum_{c} \frac{\partial E}{\partial a_{ij}^{(k, c)}} \frac{\partial a_{ij}^{(k, c)}}{\partial w_{s\,t}^{(k)}}
\nonumber \\\
\nonumber \\\
&= \sum_{i = 0}^{M-m} \sum_{j = 0}^{N-n} \, \sum_{c} \frac{\partial E}{\partial a_{ij}^{(k, c)}} \, x_{(i+s)(j+t)}^{(\nonumber c)}
\nonumber \\\
\nonumber \\\
\nonumber \\\
\frac{\partial E}{\partial b^{(k)}} &= \sum_{i = 0}^{M-m} \sum_{j = 0}^{N-n} \, \sum_{c} \frac{\partial E}{\partial a_{ij}^{(k, c)}} \frac{\partial a_{ij}^{(k, c)}}{\partial b^{(k)}}
\nonumber \\\
\nonumber \\\
&= \sum_{i = 0}^{M-m} \sum_{j = 0}^{N-n} \, \sum_{c} \frac{\partial E}{\partial a_{ij}^{(k, c)}}
\end{align}
\]
見た目はだいぶごちゃついていますが、基本的にはチャネルごとの和をとる項が増えたにすぎません。
同様にバックプロパゲーションも下記のように導出することができます:
\[
\frac{\partial E}{\partial x_{ij}^{(\nonumber c)}}
= \sum_{s = 0}^{m - 1} \sum_{t = 0}^{n - 1} \frac{\partial E}{\partial a_{(i-s)(j-t)}^{(k, c)}} \, w_{s\,t}^{(k)}
\]
これで、複数チャネルの場合にも対応することができるようになりました。
\( \sum \)が多いことからも、実装するとforループが何重にもなることがわかります。
マシンの処理能力が高くないとCNNを走らせるのは難しいでしょう。
CNNの実装はまだしていませんが、できたらまた公開する予定です。
数式に誤りがありましたらご連絡ください。