今天來介紹CNN的精髓,可以把卷積想像是一個特徵抓取器,抓出圖片裡的圓圈,特定角度的線等等。
回頭看我們在設定卷積層時所用到的參數,filters設定了我們在這一層裡要訓練出多少個抓取器,kernel_size代表讓抓取器偵測的圖片區域大小,strides設定了抓取器移動掃過整張圖片的步伐大小,padding有兩個參數,valid和same,決定輸出圖片的大小。
一個3X3的抓取器從左上角開始,從圖片拿取3X3大小的塊,抓取器每個值與塊對應位置的像素各自相乘,之後將他們加起來得到第1個值,之後向左移動stride所設定的大小,再拿出3X3的塊繼續上述步驟,直到圖片的最右側,現在我們有26個一條的像素,接著抓取器回到最左側,向下移動stride所設定的大小,然後周而復始,原始28X28的圖片經過3X3的抓取器後生成了一個26X26的圖片,padding參數使用same的話,keras會幫我們在圖的外圍填充數字0,這樣輸出的圖片大小可以保持不變,也能充分利用圖片邊緣的資訊。
以下範例的上半部是由抓取水平直線的抓取器產生,下半部則是由抓取垂直直線的抓取器產生,各位可以觀察一下區別,過程中使用到的scipy套件在安裝tensorflow時也會一併安裝,沒有的話請自行安裝。
from tensorflow.keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
from scipy.signal import convolve2d
import matplotlib.pyplot as plt
import numpy as np
from random import choice
hkernel = np.ones((6, 6))/24
hkernel[2:4] = 0
vkernel = np.ones((6, 6))/24
vkernel[:, 2:4] = 0
img = np.zeros((0, 448))
for i in range(10):
row = np.zeros((28, 0))
kernel = hkernel if i < 5 else vkernel
for j in range(8):
r = choice(x_train)
conv = np.array(convolve2d(r, kernel, mode='same'))
pair = np.hstack((r, conv.astype(np.uint8)))
row = np.hstack((row, pair))
img = np.vstack((img, row))
plt.figure(figsize=(16, 10), facecolor='w')
plt.imshow(img, plt.cm.gray)
plt.axis('off')
plt.show()