ラビットチャレンジ - 深層学習 Day2 Section4 畳み込みニューラルネットワークの概念


0.概要

本記事は日本ディープラーニング協会認定の講座プログラムである「ラビット・チャレンジ」が提供している科目の1つである深層学習のレポートである。
記事タイトルに記載のとおり、Day2 Section4 畳み込みニューラルネットワークの概念について以下にまとめる。

1.畳み込みニューラルネットワーク(CNN)

CNNはよく画像認識等で使用されているニューラルネットワーク。
ただ画像だけにしか使えないわけではなく、1次元CNNを使うことで時系列データを扱うことも出来る。

例えば画像の場合、縦、横、チャンネル(RGBやグレースケール)について学習できる。

1.1.畳み込み層

画像とフィルタ(カーネル)の積の総和にバイアスを加えることで算出することで特徴を抽出する。
例えば横線を抽出するフィルタに対する畳み込みの結果が大きければ横線の特徴を抽出(横線が画像に存在する)し、小さければ横線がないということになる。

フィルタは3x3, 5x5等のサイズ。

活性化関数に負数を0にできるReLUを使うことで、あきらかにそのフィルタに対する特徴を持っていない箇所をないものとして扱うことができ、学習に悪影響を与えることを防げる。

1.1.1.パディング

画像の周囲に固定値(0を使うことが多い)を埋め込むこと。
パディングなしで畳み込みをすると元の画像より小さくなるが、パディングをすることでサイズを維持することが出来る。

また、パディングなしの場合、画像の端の方は他の部分と比べて畳み込みに使われる回数が少なくなり、特徴として抽出されにくいが、パディングをすることによってより端の方も特徴を抽出できるようになる。

パディングに使う値は0でなくとも良いが、学習に影響を与える可能性があるため0とすることが一般的。

1.1.2.ストライド

フィルタをどれだけずらして畳み込みを行うかということ。
画像に対してフィルタを少しずつずらしながら畳み込みは行っていくが、この時、ストライドを1とした場合、次の畳み込みは1ピクセルずらして行うことになる。
ストライドが2なら2ピクセルずらすこととなる。

1.1.3.チャンネル

空間的な奥行き。
チャンネルが1つだと例えばカラー画像でもRGBの違いを区別せずに畳み込みすることになり、正確な結果とならない。
例えばカラー画像の場合はRGBの3チャンネルに分けて畳み込みを行う。

1.2.プーリング層

対象領域の中から1つの値を取得する層。
畳み込んだ後に行うことでそれらしい特徴を持った値のみを抽出できる。

1.2.1.最大値プーリング

3x3等の対象領域の中で最大値を取得する。

1.2.2.平均値プーリング

3x3等の対象領域の中で平均値を取得する。

2.確認テスト

2.1.確認テスト1

サイズ6x6の入力画像をサイズ2x2のフィルタで畳み込んだ時の出力画像のサイズを答えよ。
なおストライドとパディングは1とする。

回答:

高さ(または幅)+2パディング(左右または上下)-フィルタの高さ(または幅)をストライドで割った値に1を足す。
(6+2*1-2/1)+1=7
高さ、幅ともに同じため7x7

3.Jupter演習

3.1.im2col

指定した順に軸を入れ替える下記処理をコメントアウトするとどうなるか。

col = col.transpose(0, 4, 5, 1, 2, 3)

初期値のフィルタ3x3、ストライド1の実行結果(入力値は同じものを使用)。

入力値

========== input_data ===========
[[[[ 2. 37. 55. 64.]
[95. 62. 76. 99.]
[24. 66. 60. 6.]
[29. 75. 15. 22.]]]

[[[86. 2. 76. 73.]
[48. 59. 78. 45.]
[92. 96. 0. 35.]
[17. 31. 57. 35.]]]]

コメントアウト前

============= col ==============
[[ 2. 37. 55. 95. 62. 76. 24. 66. 60.]
[37. 55. 64. 62. 76. 99. 66. 60. 6.]
[95. 62. 76. 24. 66. 60. 29. 75. 15.]
[62. 76. 99. 66. 60. 6. 75. 15. 22.]
[86. 2. 76. 48. 59. 78. 92. 96. 0.]
[ 2. 76. 73. 59. 78. 45. 96. 0. 35.]
[48. 59. 78. 92. 96. 0. 17. 31. 57.]
[59. 78. 45. 96. 0. 35. 31. 57. 35.]]

コメントアウト後

============= col ==============
[[ 2. 37. 95. 62. 37. 55. 62. 76. 55.]
[64. 76. 99. 95. 62. 24. 66. 62. 76.]
[66. 60. 76. 99. 60. 6. 24. 66. 29.]
[75. 66. 60. 75. 15. 60. 6. 15. 22.]
[86. 2. 48. 59. 2. 76. 59. 78. 76.]
[73. 78. 45. 48. 59. 92. 96. 59. 78.]
[96. 0. 78. 45. 0. 35. 92. 96. 17.]
[31. 96. 0. 31. 57. 0. 35. 57. 35.]]

コメントアウト前は正しく3x3で元の配列から各畳込みで対象となる値を行へ抽出していっていることが分かるが、コメントアウト後はズレてしまっている。

パラメータを変えて実行してみる。
今回は端の値がパディングしなかった場合よりパディングしたことでより使われることを確認する。

========== input_data ===========
[[[[ 2. 37. 55. 64.]
[95. 62. 76. 99.]
[24. 66. 60. 6.]
[29. 75. 15. 22.]]]

[[[86. 2. 76. 73.]
[48. 59. 78. 45.]
[92. 96. 0. 35.]
[17. 31. 57. 35.]]]]

============= col ==============
[[ 0. 0. 0. 0. 2. 37. 0. 95. 62.]
[ 0. 0. 0. 2. 37. 55. 95. 62. 76.]
[ 0. 0. 0. 37. 55. 64. 62. 76. 99.]
[ 0. 0. 0. 55. 64. 0. 76. 99. 0.]
[ 0. 2. 37. 0. 95. 62. 0. 24. 66.]
[ 2. 37. 55. 95. 62. 76. 24. 66. 60.]
[37. 55. 64. 62. 76. 99. 66. 60. 6.]
[55. 64. 0. 76. 99. 0. 60. 6. 0.]
[ 0. 95. 62. 0. 24. 66. 0. 29. 75.]
[95. 62. 76. 24. 66. 60. 29. 75. 15.]
[62. 76. 99. 66. 60. 6. 75. 15. 22.]
[76. 99. 0. 60. 6. 0. 15. 22. 0.]
[ 0. 24. 66. 0. 29. 75. 0. 0. 0.]
[24. 66. 60. 29. 75. 15. 0. 0. 0.]
[66. 60. 6. 75. 15. 22. 0. 0. 0.]
[60. 6. 0. 15. 22. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 86. 2. 0. 48. 59.]
[ 0. 0. 0. 86. 2. 76. 48. 59. 78.]
[ 0. 0. 0. 2. 76. 73. 59. 78. 45.]
[ 0. 0. 0. 76. 73. 0. 78. 45. 0.]
[ 0. 86. 2. 0. 48. 59. 0. 92. 96.]
[86. 2. 76. 48. 59. 78. 92. 96. 0.]
[ 2. 76. 73. 59. 78. 45. 96. 0. 35.]
[76. 73. 0. 78. 45. 0. 0. 35. 0.]
[ 0. 48. 59. 0. 92. 96. 0. 17. 31.]
[48. 59. 78. 92. 96. 0. 17. 31. 57.]
[59. 78. 45. 96. 0. 35. 31. 57. 35.]
[78. 45. 0. 0. 35. 0. 57. 35. 0.]
[ 0. 92. 96. 0. 17. 31. 0. 0. 0.]
[92. 96. 0. 17. 31. 57. 0. 0. 0.]
[96. 0. 35. 31. 57. 35. 0. 0. 0.]
[ 0. 35. 0. 57. 35. 0. 0. 0. 0.]]

パディングありで実行した結果、パディングなしの時は端の値が1回しか使われていなかったが、パディングすることでより使われるようになっていることが分かる。

3.2.col2im

print('========== col ===========\n', col)
print('==============================')
filter_h = 3
filter_w = 3
stride = 1
pad = 1
col2 = col2im(col, input_data.shape, filter_h=filter_h, filter_w=filter_w, stride=stride, pad=pad)
print('============= col2 ==============\n', col2)
print('==============================')

colはim2colで2次元配列化した値。

実行結果

========== col ===========
[[ 0. 0. 0. 0. 2. 37. 0. 95. 62.]
[ 0. 0. 0. 2. 37. 55. 95. 62. 76.]
[ 0. 0. 0. 37. 55. 64. 62. 76. 99.]
[ 0. 0. 0. 55. 64. 0. 76. 99. 0.]
[ 0. 2. 37. 0. 95. 62. 0. 24. 66.]
[ 2. 37. 55. 95. 62. 76. 24. 66. 60.]
[37. 55. 64. 62. 76. 99. 66. 60. 6.]
[55. 64. 0. 76. 99. 0. 60. 6. 0.]
[ 0. 95. 62. 0. 24. 66. 0. 29. 75.]
[95. 62. 76. 24. 66. 60. 29. 75. 15.]
[62. 76. 99. 66. 60. 6. 75. 15. 22.]
[76. 99. 0. 60. 6. 0. 15. 22. 0.]
[ 0. 24. 66. 0. 29. 75. 0. 0. 0.]
[24. 66. 60. 29. 75. 15. 0. 0. 0.]
[66. 60. 6. 75. 15. 22. 0. 0. 0.]
[60. 6. 0. 15. 22. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 86. 2. 0. 48. 59.]
[ 0. 0. 0. 86. 2. 76. 48. 59. 78.]
[ 0. 0. 0. 2. 76. 73. 59. 78. 45.]
[ 0. 0. 0. 76. 73. 0. 78. 45. 0.]
[ 0. 86. 2. 0. 48. 59. 0. 92. 96.]
[86. 2. 76. 48. 59. 78. 92. 96. 0.]
[ 2. 76. 73. 59. 78. 45. 96. 0. 35.]
[76. 73. 0. 78. 45. 0. 0. 35. 0.]
[ 0. 48. 59. 0. 92. 96. 0. 17. 31.]
[48. 59. 78. 92. 96. 0. 17. 31. 57.]
[59. 78. 45. 96. 0. 35. 31. 57. 35.]
[78. 45. 0. 0. 35. 0. 57. 35. 0.]
[ 0. 92. 96. 0. 17. 31. 0. 0. 0.]
[92. 96. 0. 17. 31. 57. 0. 0. 0.]
[96. 0. 35. 31. 57. 35. 0. 0. 0.]
[ 0. 35. 0. 57. 35. 0. 0. 0. 0.]]

============= col2 ==============
[[[[ 8. 222. 330. 256.]
[570. 558. 684. 594.]
[144. 594. 540. 36.]
[116. 450. 90. 88.]]]

[[[344. 12. 456. 292.]
[288. 531. 702. 270.]
[552. 864. 0. 210.]
[ 68. 186. 342. 140.]]]]

im2colで2次元化する前の多次元配列とは値が違うことが分かる。
col2imは形状は復元するが、値そのものまで復元するわけではないため、im2colの結果から画像をそのまま復元することには使うことが出来ないと言える。

X.ラビットチャレンジとは

ラビットチャレンジとは、日本ディープラーニング協会認定の講座プログラムの1つ。
E資格を受験するためにはこのラビットチャレンジ等、いずれかの講座プログラムを修了しなければならない。

ラビットチャレンジの特徴は「現場で潰しが効くディープラーニング講座」の通学講座録画ビデオを編集した教材を使用した自習スタイルであるという点。
サポートは他の講座より少なく、受け身ではなく自主的に学んでいく姿勢でなければ進められないが、その分、他の講座に比べると安価であり、手が出しやすい。
ある程度知識がある人、自力で頑張るぞというガッツのある人向けではないかと感じる。