CNN で特徴的な動作に畳み込みっちゅう処理がある。
画像には局所性(隣接するピクセルの影響を受ける事)があるので、それを利用して画像の特徴を強調したりできる。
やってることはフィルタを用意して、画像に畳み込み計算を行う事。
以下の例みたいに、左上からフィルタを適用して 1 セル生成。したら、右に 1 セルずらして…と繰り返す。
見ての通り画像自体は小さくなる。
因みにこのずらす間隔をストライドと呼ぶ。
ストライド幅が大きいほど画像サイズは小さくなり、特徴が見逃されやすくなる。
で、この計算するとしたときに普通の画像に対して処理するのがしんどいなんで整形するのが一般的なのだそうだ。
im2col
画像に対して畳み込みを行うには形状がめんどくさいので以下みたいに配置しなおす。
これを im2col と呼ぶっぽい。
例えば 2x2 のフィルタで 3x2 のデータに適用するとき、まずは im2col でこんな風にしてしまう
こう変換すれば、フィルタをドット積で一括計算できる。
で、実際に変換と逆変換をやってみた。
画像は こちら のを使わせていただいた。
import numpy as np import matplotlib.pyplot as plt %matplotlib inline # 読み込んで表示しただけ img = plt.imread('himawari.png') plt.imshow(img) plt.show()
import statistics # カラー情報を捨ててモノクロに arr_monochro = [[statistics.mean(cell) for cell in y] for y in img] plt.imshow(arr_monochro, 'gray', vmin = 0, vmax = 1) plt.show()
changed = np.array(arr_monochro) changed.shape
(512, 512)
画像サイズは 512x512 に変更している。
これを変換していく
フィルタサイズは 3x3を想定して、スライド1 なら最終的な画像サイズは -2px で 510
def im2col(image, flt_h, flt_w, out_h, out_w): img_h, img_w = image.shape cols = np.zeros((flt_h, flt_w, out_h, out_w)) for h in range(flt_h): h_lim = h + out_h for w in range(flt_w): w_lim = w + out_w cols[h, w, :, :] = image[h:h_lim, w:w_lim] cols = cols.reshape(flt_h*flt_w, out_h*out_w) return cols cols = im2col(changed, 3, 3, 510, 510) cols.shape
(9, 260100)
予想通りになっている。
今度はこれを逆変換する(画像に戻す)。手順は前回と逆手順。
def col2im(cols, img_shape, flt_h, flt_w, out_h, out_w): img_h, img_w = img_shape cols = cols.reshape(flt_h, flt_w, out_h, out_w).transpose(0, 1, 2, 3) images = np.zeros((img_h, img_w)) for h in range(flt_h): h_lim = h + out_h for w in range(flt_w): w_lim = w + out_w images[h:h_lim, w:w_lim] += cols[h, w] return images / (flt_h * flt_w) rev_img = col2im(cols, (512, 512), 3, 3, 510, 510) plt.imshow(rev_img, 'gray', vmin = 0, vmax = 1) plt.show()
重ね合わせ部分ができるので、そのあたりが大分明るくなるので、フィルタサイズで最終的に割ってる。
ほぼほぼ元の画像に戻せることも確認した。
これで行列のドット積でフィルタを掛けれるようになった。