今回、作ってみたCNNはCNNがレイヤ3層、全結語は2層のオーソドックスなタイプだ。
ただ、作っていく上でメモリリークとか起こしてしまったり、スタックオーバーフローしちゃうらしくCNNの深さは抑えている。
レイヤ | 深さ |
---|---|
入力 | RGBなので3 |
レイヤ0 | 2 |
レイヤ1 | 4 |
レイヤ2 | 8 |
図にするとこんな感じだな。
できあがって、50枚の猫ちゃんをOKデータ、50枚の犬ちゃんをNGデータとして学習してみた。
そうすると、3,740ターン目に誤差が0.001を切るようになったので学習は終了。
検証用データを学習用途は別に10枚の猫ちゃん、10枚の犬ちゃんを用意して確認してみると正解率80%になった。
それほど高くないけど、学習量とCNNの組み上げ方からすると十分な結果が得られた。
まぁ、正解率99%となると果てしなく険しい道のりになると思いますがね・・・
さて、CNNってどんなことするのかおさらいする。
畳み込みって、画像処理で言えばフィルタだよね。
下図は3x3のフィルタを示している。
フィルタをかけたい画素の周囲3x3をある係数で計算してひとつの出力を得る。
フィルタ係数によって、輪郭抽出であったり、平坦化などができたりするわけだよね。
難しく畳み込みでスライドでなんてダラダラ説明するから難しいわけで、下図のようにイメージすれば良いのかな?
ソースコードを極端に書けば、こんな感じ。
offset = y * width + x;
for(n = 0; n < 3; ++n){
for(m = 0 ; m < 3; ++m){
z += x[offset + (n - 1) * 3 + (m -1)] * w[n * 3 + m];
}
}
それを全画素やるわけだから下図のように進んでいって、
最終的には画像を舐めてくんだよね。
プーリングって、要は間引きだと思えばいいか。
下図じゃ2x2のプーリングを示している。
近隣画素の2x2で1画素を出力する。
この時の計算方法が平均であったり、最大値であったりするわけなんだな。
画像処理では最大値が使われることが多いらしい。
畳み込み+プーリングで1つのレイヤを示す。
今回は3つのレイヤがあるので(畳み込み+プーリング)×3の処理を行っている。
CNNの特徴的なのはレイヤーが進む毎に画角サイズが小さくなることです。
3x3のフィルタでは2ドット小さくなり、2x2のプーリングでは半分の画角になる。
今回のテストでは5x5のフィルタ、2x2のプーリングを使用して、入力画像は60x60にしたので次のようになる。
60x60 → ( 56x56 → 28x28 ) → ( 24x24 → 12x12 ) → ( 8x8 → 4x4 )
ここでの括弧はレイヤを示している。
括弧の中の左側は畳み込み、右側がプーリング後の画像のサイズ。
つまり、3つのレイヤで最後の画角は4x4になっている。
これでネコか犬か判断しろって言われてもできるわけがない。
そこで、レイヤ毎にたくさんのフィルタを適用していろんな特徴を抜き出す。
ここがCNNの面白いところ。
エッヂとか分かりやすい特徴ではなく、フィルタは別にランダム値で適当な特徴を取れればいいんだな。
たくさん、特徴があるほどなにかしら分かりやすくなるってのがCNNと思えばいい。
CNNで最後、4x4の画像ができあがる。
今回のテストでは4x4の画像に、フィルタが8つなのでCNNの最後には4x4x8のデータができあがる。
これを全結合の入力として、全入力の演算を行う。
下図は4入力、中間層が2、出力が1の全結合を示したものである。
今回のテストではこの入力層が4x4x8=128個あるということだ。
中間層は半分にしたので64個である。
この64個の根拠は全く何もない。
ただ、半分にしただけだ。
最後は1つだけ、今回、ここは「猫!」ってわかるものになっていればいい。
今回、できあがったのが最初に登場した下図である。
なんとなく、作ってみたけど成果率80%っていい線いってるじゃん♪
最初は60%ぐらいだったんけど、別途、与えたデータがまずかった。
ちゃんと揃えたら80%ってところだった。
今回、作成の目的は勉強用のCNNを作成することであった。
勢いで作ったのでいろいろ不具合があったけど、そのおかげでどこを勉強させれば効果的なのかがなんとなく、わかった。