使用卷積網路實現計算機圖像識別:卷積和max pooling操作介紹
深度學習在計算機圖像識別上的應用非常成功。利用深度學習,我們能夠對圖片進行高精度識別,實現這一功能的,主要依靠神經網路中的一種分支,名為卷積網路。卷積網路與我們前面實現的網路不通之處在於,它可以直接接受多維向量,而我們以前實現的網路只能接收一維向量。
我們在開始時,實現了一個能夠識別手寫數字圖片的網路,網路接收數據時,必須把一張28*28的灰度圖轉換為784長的一維向量。在深入解析卷積網路前,我們直接用代碼將其實現出來,通過卷積網路實現手寫數字識別功能,先獲得一個感性認識,為後續的深入研究打下基礎,我們看看一個能直接接收手寫數字圖片的卷積網路是什麼樣子的:
from keras import layersfrom keras import modelsmodel = models.Sequential()model.add(layers.Conv2D(32, (3,3), activation="relu", input_shape=(28,28,1)))model.add(layers.MaxPooling2D(2,2))model.add(layers.Conv2D(64, (3,3), activation="relu"))model.add(layers.MaxPooling2D((2,2)))model.add(layers.Conv2D(64, (3,3), activation="relu"))model.summary()
上面代碼運行後結果如下:
上面實現網路與以往不同在於,網路層使用了Conv2D和MaxPooling,而不是以往的Dense,同時Conv2D網路層可以直接接收二維向量(28,28,1),這對應的就是手寫數字灰度圖。卷積網路主要作用是對輸入數據進行一系列運算加工,它輸出的是中間形態的結果,該結果不能直接用來做最終結果,要得到最終結果,我們需要為上面的卷積網路添加一層輸出層,代碼如下:
model.add(layers.Flatten())model.add(layers.Dense(64, activation="relu"))model.add(layers.Dense(10, activation="softmax"))model.summary()
卷積網路在最後一層輸出的是(3,3,64)的二維向量,Flatten()把它壓扁成3364的一維向量,然後再傳入一個包含64個神經元的網路層,由於我們要識別圖片中的手寫數字,其對應的結果有10種,也就是0到9,因此最後我們還添加了一個含有10個神經元的網路層。
我們把圖片數據輸入網路,對網路進行訓練:
from keras.datasets import mnistfrom keras.utils import to_categorical(train_images, train_labels), (test_images, test_labels) = mnist.load_data()train_images = train_images.reshape((60000, 28, 28, 1))train_images = train_images.astype("float32") / 255test_images = test_images.reshape((10000, 28, 28, 1))test_images = test_images.astype("float32") / 255train_labels = to_categorical(train_labels)test_labels = to_categorical(test_labels)model.compile(optimizer="rmsprop", loss="categorical_crossentropy", metrics=["accuracy"])model.fit(train_images, train_labels, epochs = 5, batch_size=64)test_loss, test_acc = model.evaluate(test_images, test_labels)print(test_acc)
上面代碼運行後,輸出結果如下:
我們構造的卷積網路對手寫數字圖片的識別準確率為99%,而我們最開始使用的網路對圖片識別的準確率是97%,也就是說最簡單的卷積網路,對圖片的識別效果也要比普通網路好得多。能取得這種好效果,主要是網路進行了兩種特殊操作,分別是Conv2D和MaxPooling2D,接下來我們看看這兩種操作的細節。
卷積操作,其實是把一張大圖片分解成好多個小部分,然後一次對這些小部分進行識別,我們最開始實現的網路是一下子識別整張大圖片,這是兩種網路對圖片識別精確度不一樣的重要原因。通常我們會把一張圖片分解成多個3*3或5*5的」小片「,然後分別識別這些小片段,最後把識別的結果集合在一起輸出給下一層網路,例如下圖:
上圖中小方格圈中的區域就是我們摳出來的3*3小塊。這種做法其實是一種分而治之的策略,如果一個整體很難攻克,那麼我就把整體瓦解成多個弱小的局部,然後把每個局部攻克了,那麼整體就攻克了。這種做法在圖象識別中很有效就在於它能對不同區域進行識別,假設識別的圖片是貓臉,那麼我們就可以把貓臉分解成耳朵,嘴巴,眼睛,鬍子等多個部位去各自識別,然後再把各個部分的識別結果綜合起來作為對貓臉的識別。
每一小塊識別後,會得到一個結果向量,例如語句:
layers.Conv2D(32, (3,3), activation="relu", input_shape=(28,28,1))
它表示把一個28*28的灰度圖片,(1表示顏色深度,對於灰度圖其深度用一個數字就可以表示,對於RGB圖,顏色深度需要用3個數字[R,G,B]表示),分解成多個3*3的小塊,每個3*3小塊識別後輸出一個含有32個元素的一維向量。這個一維向量我們成為過濾器Filter,它蘊含著對圖片的識別信息,例如「這部分對應貓臉的嘴巴」。
對於2828的圖片,把它分解成33小塊時,這些小塊總共有26*26個,我們先看看分解方法,假定我有一個5*5的大圖片,那麼我們可以將它如下分解成多個3*3的小塊:
如果你看不出分解規律,我們把左邊大圖片的每個小格標號後就清楚了,左邊圖片編號如下:
1,2,3,4,56,7,8,9,1011,12,13,14,15,16,17,18,19,2021,22,23,24,25
於是右邊第一行第一小塊對應的編號為:
1,2,36,7,811,12,13
第一行第二小塊為:
2,3,47,8,912,13,14
以此類推,一個28*28的圖片可以分解成26個3*3小塊,每個小塊又計算出一個含有32個元素的向量,這個計算其實是將3*3矩陣乘以一個鏈路參數矩陣,它跟我們前面講過的數據層上一層網路經過神經元鏈路輸入下一層網路的原理是一樣的,於是第一層網路輸出結果是26*26*32的三維矩陣。我們可以用下圖形象的表示輸出結果:
上面描述的操作流程就叫卷積,接下來我們看看另一種操作:max pooling。從我們的代碼中看到,第一層網路叫Conv2D,第二層就是MaxPooling2D,max pooling 的目標是把卷積操作得到的結果進一步「擠壓」出更有用的信息,有點類似於用力擰毛巾,把不必要的水分給擠兌掉。max pooling 其實是把一個二維矩陣進行2*2的分塊,這部分跟前面描述的卷積很像,具體操作如下圖:
一定要注意上面分塊與卷積分塊的區別,上面分出的塊與塊之間是沒有重疊的,而卷積分出的3*3小塊之間是相互重疊的!2*2分塊後,把每塊中的最大值抽出來最後組合成右邊的小塊,注意看完成後矩陣的維度縮減了一半,原來4*4的矩陣變成了2*2的矩陣。
回到我們的代碼例子,第一層卷積網路輸出了26*26*32的結果,我們可以看成由32 個 26*26個二維矩陣的集合。每個26*26的二維矩陣都經過上面的max pooling處理變成13*13的二維矩陣,因此經過第二層max pooling後,輸出的結果是 13*13*32的矩陣集合,也就是下面代碼產生了32個13*13的矩陣集合:
layers.MaxPooling2D(2,2)
其他代碼以此類推。卷積操作產生了太多的數據,如果沒有max pooling對這些數據進行壓縮,那麼網路的運算量將會非常巨大,而且數據參數過於冗餘就非常容易導致過度擬合。
以上就是我們這節需要介紹的內容,有了卷積網路,我們可以通過很少的訓練數據就能教會計算機識別圖片裡面的物體,所以它的功能非常強大,下一節我們將運用卷積網路識別貓狗圖片。
※自製Monkey編程語言編譯器:增加數組操作API和Mapsh數據類型
TAG:Coding迪斯尼 |