CNN#

畳み込みを使うネットワーク

全結合層の問題点#

画像は縦・横・チャンネル方向の3次元あるが、全結合層に入力するときは1次元にすることになり、形状・位置関係を上手く扱えない。

畳み込み(convolution)層 は形状を維持する

計算#

畳み込み層#

チャネルごとに、カーネル(フィルター) と呼ばれる行列を画像の行列と要素ごとに積をとり、総和をとる

import numpy as np

img = np.array([
    [1, 2, 3, 0],
    [0, 1, 2, 3],
    [3, 0, 1, 2],
    [2, 3, 0, 1],
])

ker = np.array([
    [2, 0, 1],
    [0, 1, 2],
    [1, 0, 2],
])

H, W = img.shape
K = ker.shape[0]

conv = np.zeros((H - K + 1, W - K + 1))
for i in range(H - K + 1):
    for j in range(W - K + 1):
        conv[i, j] = (img[i:i+K, j:j+K] * ker).sum()
conv
array([[15., 16.],
       [ 6., 15.]])

パディング#

畳み込みを行う際に、入力データの周囲に固定のデータ(例えば0など)を埋めることがある。これを パディング(padding) という。

パディングにより畳み込み後の行列のサイズを調整できる

import numpy as np

# padding
img = np.array([
    [1, 2, 3, 0],
    [0, 1, 2, 3],
    [3, 0, 1, 2],
    [2, 3, 0, 1],
])

H, W = img.shape
img_pad = np.zeros((H+2, W+2))
img_pad[1:-1, 1:-1] = img
img_pad
array([[0., 0., 0., 0., 0., 0.],
       [0., 1., 2., 3., 0., 0.],
       [0., 0., 1., 2., 3., 0.],
       [0., 3., 0., 1., 2., 0.],
       [0., 2., 3., 0., 1., 0.],
       [0., 0., 0., 0., 0., 0.]])
# 畳み込み後も 4 x 4 を維持
img = img_pad
H, W = img.shape
conv = np.zeros((H - K + 1, W - K + 1))
for i in range(H - K + 1):
    for j in range(W - K + 1):
        conv[i, j] = (img[i:i+K, j:j+K] * ker).sum()
conv
array([[ 7., 12., 10.,  2.],
       [ 4., 15., 16., 10.],
       [10.,  6., 15.,  6.],
       [ 8., 10.,  4.,  3.]])

プーリング層#

プーリング(pooling) は縦方向・横方向の空間を小さくする演算

例えば、特定の範囲における最大値を取得する max poolingがある

# 2 x 2 の範囲で最大値をとる
img = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
])
K = 2  # 範囲
H, W = img.shape

# 1ずつずらして最大値をとっていく
pool = np.zeros((H - K + 1, W - K + 1))
for i in range(H - K + 1):
    for j in range(W - K + 1):
        pool[i, j] = np.max(img[i:i+K, j:j+K])
pool
array([[5., 6.],
       [8., 9.]])