Convolution Neural Network (卷積神經網路)
CNN一直以來是DL中最重要的一部份,CNN 在影像辨識中甚至可以超越人類辨識的精準度,把CNN的概念理解過一遍之後,會發現其實CNN是一個很直觀的演算法,而且仔細想想,其實跟人類用眼睛去辨識有87%的相似,接下來我用CNN始祖Model => LeNet 來介紹CNN是怎麼運作的,以下是LeNet的模型架構 ( 源自Yann LeCun 1998年論文 )
機器學習 Convolution Neural Network 卷積神經網路
LeNet Architecture, 1998可以從上面的LetNet架構中,對於輸入的圖片做了2次Convolutions(卷積),2次Subsampling(采樣),跟2次的Full connection(全連結)還有1次的Gaussian connections (高斯連結),中間的Convolution跟Subsampling在對圖片做特徵擷取的動作,最後把特徵擷取出來後,後面再展開用Full connection做分類,在這裡我就不介紹Full connection了,Full connection就是所謂的分類器,而他就是跟最原始DNN 分類的概念是一樣的,如果對DNN 逆向傳播不熟的可以參考這篇文章,下面我主要Focus在卷積層跟采樣層,也就是特徵擷取的那一段。 Convolution Layers (卷積層)
什麼是卷積呢?我們先來講一些computer vision的東西,我們都知道一張圖片都可以表示成由像素值(pixel)組成的矩陣。以下是一張長毛的皮卡丘的圖片(來自即將上映的神奇寶貝真人版電影,老實說,這隻真的很不討喜… 哈哈)
機器學習 Convolution Neural Network 卷積神經網路
大家應該都有用過美圖軟體,或是修圖軟體,有一些基本的特效像是”銳化”或是浮雕的特效,然而這些基本的特效跟美圖都是怎麼做到的呢? 沒錯,就是利用卷積(Convolution) 來達成的
機器學習 Convolution Neural Network 卷積神經網路
銳化特效,可以看出皮卡丘的邊緣跟毛都變得更細更清楚了,有一點點怪怪的XD
機器學習 Convolution Neural Network 卷積神經網路
浮雕特效,看得出來除了皮卡丘的邊緣以外都變成灰色的了,變成浮雕的感覺了這裡你可能會想,為什麼講CNN要講到美圖,而美圖跟類神經網路又有什麼關係呢?我相信大家對於”特徵”(Feature)這二個字應該都不陌生,在訓練ML的模型或是NN的模型時,我們需要不斷的擷取”資料”的特徵,最後再進行分類或是預估的動作。 ======================================== 之所以我們可以做過出不同的美圖效果,是在對圖片做不同的卷積(銳化和模糊或是邊緣增強等操作),我們用銳化來舉例好了,銳化過後的圖片,我們可以看出圖片裡面物件的”邊緣”被強化了,而”邊緣”就是圖片的其中一個有效的特徵,所以我們來思考一下,不同的卷積動作是不是就可以從圖片擷取出各種不同的特徵,像是”邊緣”,”曲線”等等的特徵呢?然後我們再去對特徵做選擇,並且利用特徵來做分類跟預估,沒錯!!所以這就是為什麼Convolution Neural Network的由來,卷積就是在對圖片去做擷取特徵的動作,找出最好的特徵最後再進行分類 ======================================== 那卷積又是怎麼達成的呢?圖片的卷積運算其實很簡單,只要你會小學教的加減乘除就可以做到,假設我們有張圖,5X5的像素圖,和一個3X3的矩陣
機器學習 Convolution Neural Network 卷積神經網路
接下來我再放上一張利用3X3矩陣做卷積的GIF圖
機器學習 Convolution Neural Network 卷積神經網路
基本卷積運算(來自網站)看完上面動圖後,可能有眼尖的人已經看出圖片卷積怎麼運作的了,沒錯,就是將3X3的矩陣在圖片上的像素一步一步移動(在卷積層中移動的步數稱為Stride步數),在每個位置的時候,計算兩個矩陣相對元素的乘積並相加,輸出一個值然後放在一個矩陣(右邊粉色的矩陣),這就是基本的卷積運算。 橘色的矩陣就是所謂的”卷積核(Kernel)”,也是所謂的Filter,然而美圖修修也就是用不同的Kernel做卷積所達成的,不過在這裡你可能會有一個問題? ★ 如果以上面那種運算方式,是不是每做一次卷積,出來後的特徵圖會越來越小呢?原本5X5的圖片跟3X3的矩陣做卷積出來後的圖變成3X3的了,如果不想經過卷積後圖變小的話,有個技術是Zero Padding,他可以將圖片向外擴張補0後再進行卷積,這樣卷積過後的特徵圖就會跟原本的圖一樣了,對於Padding有興趣的話,可以參考Tommy大寫的這篇文章,寫得非常好。 不同的卷積核對不同的圖片做出不同的效果,我們直接用openCV的code來實作一遍convolution
下面是我用不同的3X3 的卷積核做出來的效果
機器學習 Convolution Neural Network 卷積神經網路
現在大家應該都懂卷積在圖片上的原理了,而在CNN上卷積其實就是在對圖片做特徵擷取,那我們回到LeNet的架構圖上看一下,我們來看第一層的卷積層,Input為32x32的image,然而經過Convolutions的時候出現6張28X28的Feature map,這是怎麼做到的呢?
機器學習 Convolution Neural Network 卷積神經網路
其實很簡單的去想,就是用6個”不同的卷積核”去對Input 做卷積 ★深度 = 卷積核數量 第一個卷積層出來後的feature map為 6張,因為卷積核數量為6
第二個卷積層出來後的feature map為16張,因為卷積核數量為16
P.S. 這裡妳/你可能會想,第二次的卷積層出來的深度怎麼不是6*16呢?不是應該是每張圖做16個不同的卷積核卷積嗎?=>對於卷積核來說,是有深度的,看下面這張圖應該就很好理解,然而第二個卷積層就的卷積核就是有深度的,16個深度為6的卷積核,所以出來的feature map深度就是16
機器學習 Convolution Neural Network 卷積神經網路
來自此網站2. 那為什麼做完卷積後的每一個Feature map都是28X28呢? 讓我們從上面的那個簡單例子來思考,3X3的卷積核對5X5的圖片出來的Feature map是3X3,因為卷積核由最左邊到最右邊只能走3步,這是步數為1的情況,那如果步數為2的情況呢?出來的Feature map是2X2,我們可以推測出一個公式 ★Feature map width=[(Original width-Kernel width)/(Stride+1)]+1 所以5X5的Kernel,步數為1的情況出來的Feature map 就是28X28(沒有做Zero Padding的情況下) 接下來介紹什麼是Subsampling(采樣) Subsampling/ Pooling Layer(采樣/池化層)
Subsampling,也可以稱為Pooling(池化),這次我先直接來介紹池化的基本運算方式,Pooling常用的方式有二種,Max Pooling,Mean Pooling,我這裡介紹Max Pooling的運作方式,看完應該會馬上就可以理解Mean Pooling是怎麼做的。 池化層跟卷積層一樣,有個Kernel(大多為2X2),對卷積層出來的feature map做運算,我們來看下面這張圖:
機器學習 Convolution Neural Network 卷積神經網路
Max Pooling運作方式這張圖是利用2X2的Kernel 在image上用”步數”2(Stride)進行Pooling的基本運算,沒錯,就是很直觀也很簡單的,在Kernel經過的地方,取出最大值,就達成降維了。 看懂Pooling的基本運算後,我們來講一下為什麼要做池化層: - 對特徵圖(Feature map)降維,並且保留重要的特徵,參數減少,可防止Overfitting。
對於Overfitting不熟的人,可以參考我之前的寫的文章,我們知道,越複雜或是參數越多的模型容易造成Overfitting,Pooling層有效降低我們的參數,而且還可以保留重要的特徵,也可以使模型對圖像微小的變換或是一些失真變得更沉穩(因為我們取了Kernel的最大值,微小的失真並不會改變結果,最大值還是最大值)。 2. 卷積跟Pooling後可對”微小”的變化保持不變性(invariance), 旋轉、平移、伸縮等的不變性。 那這是什麼意思呢?這個討論版對這部份寫得非常好,大家參考看看
https://www.zhihu.com/question/36686900 LeNet(論文連結)實作
沒錯,LetNet就是所謂的CNN之父,前面也用了LetNet來介紹了卷積跟池化,然後LeNet整個架構就是利用了卷積跟池化把圖片特徵擷取出來後,並接上全連結層做訓練,看下面的架構圖就可以簡單的把LetNet實作出來,這裡我們用簡單Mnist data來實作LeNet 模型的分類器,
機器學習 Convolution Neural Network 卷積神經網路
LeNet Architecture, 1998
- import numpy as np
- import keras
- from keras.datasets import mnist
- from keras.utils import np_utils
- from keras.models import Sequential
- from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten
- from keras.optimizers import Adam
- #load the MNIST dataset from keras datasets
- (X_train, y_train), (X_test, y_test) = mnist.load_data()
- #Process data
- X_train = X_train.reshape(-1, 28, 28, 1) # Expend dimension for 1 cahnnel image
- X_test = X_test.reshape(-1, 28, 28, 1) # Expend dimension for 1 cahnnel image
- X_train = X_train / 255 # Normalize
- X_test = X_test / 255 # Normalize
- #One hot encoding
- y_train = np_utils.to_categorical(y_train, num_classes=10)
- y_test = np_utils.to_categorical(y_test, num_classes=10)
- #Build LetNet model with Keras
- def LetNet(width, height, depth, classes):
- # initialize the model
- model = Sequential()
- # first layer, convolution and pooling
- model.add(Conv2D(input_shape=(width, height, depth), kernel_size=(5, 5), filters=6, strides=(1,1), activation='tanh'))
- model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
- # second layer, convolution and pooling
- model.add(Conv2D(input_shape=(width, height, depth), kernel_size=(5, 5), filters=16, strides=(1,1), activation='tanh'))
- model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
- # Fully connection layer
- model.add(Flatten())
- model.add(Dense(120,activation = 'tanh'))
- model.add(Dense(84,activation = 'tanh'))
- # softmax classifier
- model.add(Dense(classes))
- model.add(Activation("softmax"))
- return model
- LetNet_model = LetNet(28,28,1,10)
- LetNet_model.summary()
- LetNet_model.compile(optimizer=Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08),loss = 'categorical_crossentropy',metrics=['accuracy'])
- #Strat training
- History = LetNet_model.fit(X_train, y_train, epochs=5, batch_size=32,validation_data=(X_test, y_test))
- #Plot Loss and accuracy
- import matplotlib.pyplot as plt
- plt.figure(figsize = (15,5))
- plt.subplot(1,2,1)
- plt.plot(History.history['accuracy'])
- plt.plot(History.history['val_accuracy'])
- plt.title('model accuracy')
- plt.ylabel('accuracy')
- plt.xlabel('epoch')
- plt.legend(['train', 'test'], loc='upper left')
- plt.subplot(1,2,2)
- plt.plot(History.history['loss'])
- plt.plot(History.history['val_loss'])
- plt.title('model loss')
- plt.ylabel('loss')
- plt.xlabel('epoch')
- plt.legend(['train', 'test'], loc='upper left')
- plt.show()
- plt.show()
複製代碼
機器學習 Convolution Neural Network 卷積神經網路
Train 5 個epoch就可以達到98.69的精確率
機器學習 Convolution Neural Network 卷積神經網路
Loss 跟 Accuracy最後附上一個很厲害的網站,模擬了LeNet所有層的輸出結果,超酷的! 3D Visualization of a Convolutional Neural NetworkEdit description
scs.ryerson.ca結論
CNN不外乎跟卷積還有池化脫離不了關係,卷積跟池化把一張圖片的特徵取出來後,就可以做很多的事情,不見得只能做分類,甚至可以把分類層改成Decoder進行圖像分割。CNN對於Deep learning 用在影像上面占了非常大的重要性,理解卷積跟池化會更容易理解CNN一直到現在的演化史,我也寫了另外一篇文章來簡單介紹CNN的演化史(分類的部份),並且利用Keras來簡易實作各個CNN的演化。
文章出處
|