[Model] LeNet-5 by Keras

典型的卷積神經網絡。python

 

數據的預處理

  • Keras傻瓜式讀取數據:自動下載,自動解壓,自動加載。
  • # X_train:
array([[[[ 0., 0., 0., ..., 0., 0., 0.],
         [ 0., 0., 0., ..., 0., 0., 0.],
         [ 0., 0., 0., ..., 0., 0., 0.],
         ..., 
         [ 0., 0., 0., ..., 0., 0., 0.],
         [ 0., 0., 0., ..., 0., 0., 0.],
         [ 0., 0., 0., ..., 0., 0., 0.]]],

       ..., 

       [[[ 0., 0., 0., ..., 0., 0., 0.],
         [ 0., 0., 0., ..., 0., 0., 0.],
         [ 0., 0., 0., ..., 0., 0., 0.],
         ..., 
         [ 0., 0., 0., ..., 0., 0., 0.],
         [ 0., 0., 0., ..., 0., 0., 0.],
         [ 0., 0., 0., ..., 0., 0., 0.]]]], dtype=float32)
  • # y_train:
array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)

但須要二值化做爲output:np_utils.to_categorical(y_train, nb_classes)數組

  • # Y_train:
Y_train[0]
Out[56]: array([ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.])

Y_train[1]
Out[57]: array([ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])

Y_train[2]
Out[58]: array([ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.])

 

Code: 圖片--> Matrix 

#coding:utf-8

import os
from PIL import Image
import numpy as np

#讀取文件夾mnist下的42000張圖片,圖片爲灰度圖,因此爲1通道,
#若是是將彩色圖做爲輸入,則將1替換爲3,而且data[i,:,:,:] = arr改成data[i,:,:,:] = [arr[:,:,0],arr[:,:,1],arr[:,:,2]]
def load_data():
    data = np.empty((42000,1,28,28),dtype="float32")
    label = np.empty((42000,),dtype="uint8")

    imgs = os.listdir("./mnist")
    num = len(imgs)
    for i in range(num):
        img = Image.open("./mnist/"+imgs[i])
        arr = np.asarray(img,dtype="float32")
        data[i,:,:,:] = arr
        label[i] = int(imgs[i].split('.')[0])
    return data,label
原始圖片二維數組化

 

如何建模

Figure, LeNet-5的網絡結構網絡

 

Model結構說明:http://blog.csdn.net/strint/article/details/44163869dom

LeNet-5中主要的有:卷積層、下抽樣層、全鏈接層3中鏈接方式。ide

 

  • 卷積層

採用的都是5x5大小的卷積核,且卷積核每次滑動一個像素,一個特徵圖譜使用同一個卷積核(即特徵圖譜內卷積核共享參數)。函數

每一個上層節點的值乘以鏈接上的參數,把這些乘積及一個偏置參數相加獲得一個和,把該和輸入激活函數,激活函數的輸出便是下一層節點的值。ui

卷積核有5x5個鏈接參數加上1個偏置共26個訓練參數。編碼

【32*32 卷積(5*5)後獲得28*28,以下Figure 6】lua

 

  

Figure 6 卷積神經網絡鏈接與矩陣卷積的對應關係idea

 

  • 下抽樣層

採用的是2x2的輸入域,即上一層的4個節點做爲下一層1個節點的輸入,且輸入域不重疊,即每次滑動2個像素,下抽樣節點的結構見Figure 7。

每一個下抽樣節點的4個輸入節點求和後取平均,均值乘以一個參數加上一個偏置參數做爲激活函數的輸入,激活函數的輸出便是下一層節點的值。

一個下抽樣節點只有2個訓練參數。

 

Figure 7 一個下抽樣節點的鏈接方式

  • 全鏈接層

(略)

 

 

紀要:

輸入層是32x32像素的圖片,比數據集中最大的的字符(最大致積是20x20像素的字符位於28x28像素區域的中心)大不少。這樣作的緣由是能使潛在的特徵好比邊緣的端點、拐角可以出如今最高層次的特徵解碼器的接收域的中心。

LeNet-5的最後一個卷積層(C3,見後面)的接收域的中心與輸入的32x32的圖像的中心的20x20的區域相連。

輸入的像素值被標準化爲背景色(白色)值爲 -0.1、前景色(黑色)值爲1.175:這樣使得輸入的均值大體爲0、方差大體爲1,從而有利於加快訓練的速度,相似矩陣的歸一化。

在後面的描述中,卷積層用Cx標記,子抽樣層用Sx標記,全鏈接層用Fx標記,其中x表示該層的是LeNet的第x層。 

 

C1層是卷積層,造成6個特徵圖譜。

特徵圖譜中的每一個單元與輸入層的一個5x5的相鄰區域相連,即卷積的輸入區域大小是5x5,每一個特徵圖譜內參數共享,即每一個特徵圖譜內只使用一個共同卷積核,卷積核有5x5個鏈接參數加上1個偏置共26個參數。

卷積區域每次滑動一個像素,這樣卷積層造成的特徵圖譜每一個的大小是28x28。

C1層共有(5x5+1)x6=156個訓練參數,有156x28x28=122304個鏈接。

Figure 8 C1層的結構

 

S2層是一個下抽樣層。

C1層的6個28x28的特徵圖譜分別進行以2x2爲單位的下抽樣獲得6個14x14的圖。

每一個特徵圖譜使用一個下抽樣核,每一個下抽象核有兩個訓練參數,因此:

共有2x6=12個訓練參數,可是有(2x2+1)x14x14x6=5880個鏈接。

Figure 9 S2層的網絡結構

 

C3層是一個卷積層,卷積和和C1相同,不一樣的是C3的每一個節點與S2中的多個圖相連。

C3層有16個10x10的圖,每一個圖與S2層的鏈接的方式如Table 1 所示。

C3與S2中前3個圖相連的卷積結構見Figure 10。這種不對稱的組合鏈接的方式有利於提取多種組合特徵。

改爲有:

  (5x5x3+1)x6  C3的0與S2的0,1,2鏈接; C3的1與S2的1,2,3鏈接; ...

+ (5x5x4+1)x6  

+ (5x5x4+1)x3  

+ (5x5x6+1)x1  

= 1516個訓練參數,

共有1516x10x10=151600個鏈接。

 

Table 1 C3與S2的鏈接關係

 

 

Figure 10 C3與S2中前3個圖相連的卷積結構

 

S4是一個下采樣層。

C3層的16個10x10的圖分別進行以2x2爲單位的下抽樣獲得16個5x5的圖。鏈接的方式與S2層相似。

共有2x16=32個訓練參數,5x5x5x16=2000個鏈接。

 

C5層是一個卷積層。

因爲S4層的16個圖的大小爲5x5,與卷積核的大小相同,因此卷積後造成的圖的大小爲1x1

這裏造成120個卷積結果。每一個都與上一層的16個圖相連。

共有(5x5x16+1)x120 = 48120個參數,一樣有48120個鏈接。

卷積核種類:120?

Figure 11 C5層的鏈接方式

 

F6層是全鏈接層。

F6層有84個節點,對應於一個7x12的比特圖:-1表示白色,1表示黑色,這樣每一個符號的比特圖的黑白色就對應於一個編碼。

該層的訓練參數和鏈接數是(120+1)x84=10164。

Figure 12 編碼的比特圖

 

Figure 13 F6層的鏈接方式

 

Output層也是全鏈接層

共有10個節點,分別表明數字0到9,且若是節點i的值爲0,則網絡識別的結果是數字i

採用的是徑向基函數(RBF)的網絡鏈接方式。假設x是上一層的輸入,y是RBF的輸出,則RBF輸出的計算方式是:

的值由i的比特圖編碼肯定。越接近於0,則越接近於,即越接近於i的比特圖編碼,表示當前網絡輸入的識別結果是字符i。

該層有84x10=840個設定的參數和鏈接。

 

Figure 14 Output層的網絡鏈接方式

 

理解難點:http://blog.csdn.net/u010555688/article/details/24848367

Output層由歐式徑向基函數(Euclidean Radial Basis Function)單元組成,每類一個單元,每一個有84個輸入。換句話說,每一個輸出RBF單元計算輸入向量和參數向量之間的歐式距離。輸入離參數向量越遠,RBF輸出的越大。一個RBF輸出能夠被理解爲衡量輸入模式和與RBF相關聯類的一個模型的匹配程度的懲罰項。用機率術語來講,RBF輸出能夠被理解爲F6層配置空間的高斯分佈的負log-likelihood。給定一個輸入模式,損失函數應能使得F6的配置與RBF參數向量(即模式的指望分類)足夠接近。這些單元的參數是人工選取並保持固定的(至少初始時候如此)。這些參數向量的成分被設爲-1或1。雖然這些參數能夠以-1和1等機率的方式任選,或者構成一個糾錯碼,可是被設計成一個相應字符類的7*12大小(即84)的格式化圖片。這種表示對識別單獨的數字不是頗有用,可是對識別可打印ASCII集中的字符串頗有用。

用這種分佈編碼而非更經常使用的「1 of N」編碼用於產生輸出的另外一個緣由是,當類別比較大的時候,非分佈編碼的效果比較差。緣由是大多數時間非分佈編碼的輸出必須爲0。這使得用sigmoid單元很難實現。另外一個緣由是分類器不只用於識別字母,也用於拒絕非字母。使用分佈編碼的RBF更適合該目標。由於與sigmoid不一樣,他們在輸入空間的較好限制的區域內興奮,而非典型模式更容易落到外邊。

RBF參數向量起着F6層目標向量的角色。須要指出這些向量的成分是+1或-1,這正好在F6 sigmoid的範圍內,所以能夠防止sigmoid函數飽和。實際上,+1和-1是sigmoid函數的最大彎曲的點處。這使得F6單元運行在最大非線性範圍內。必須避免sigmoid函數的飽和,由於這將會致使損失函數較慢的收斂和病態問題。

 

以上是LeNet-5的卷積神經網絡的完整結構,共約有60,840個訓練參數,340,908個鏈接。一個數字識別的效果如Figure 15所示。

 

Figure 15 LeNet-5識別數字3的過程

 

經過對LeNet-5的網絡結構的分析,能夠直觀地瞭解一個卷積神經網絡的構建方法,爲分析、構建更復雜、更多層的卷積神經網絡作準備。

 

a Convolutional Neural Network - 精簡版的LeNet-5

一個示例:Code

#Evaluate how the model does on the test set
score = model.evaluate(X_test, Y_test, verbose=0) print('Test score:', score[0]) print('Test accuracy:', score[1]) 

Result

 

另外一個示例:

#coding:utf-8
 
''' GPU run command: THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python cnn.py CPU run command: python cnn.py '''
#導入各類用到的模塊組件
from __future__ import absolute_import from __future__ import print_function from keras.preprocessing.image import ImageDataGenerator from keras.models import Sequential from keras.layers.core import Dense, Dropout, Activation, Flatten from keras.layers.advanced_activations import PReLU from keras.layers.convolutional import Convolution2D, MaxPooling2D from keras.optimizers import SGD, Adadelta, Adagrad from keras.utils import np_utils, generic_utils from six.moves import range from data import load_data import random import numpy as np np.random.seed(1024)  # for reproducibility
 
#加載數據
data, label = load_data()
#打亂數據 index = [i for i in range(len(data))] random.shuffle(index) data = data[index] label = label[index] print(data.shape[0], ' samples') #label爲0~9共10個類別,keras要求格式爲binary class matrices,轉化一下,直接調用keras提供的這個函數 label = np_utils.to_categorical(label, 10) ############### #開始創建CNN模型 ############### #生成一個model model = Sequential() #第一個卷積層】4個卷積核,每一個卷積核大小5*51表示輸入的圖片的通道,灰度圖爲1通道 #border_mode能夠是valid或者full,參見這裏:http://blog.csdn.net/niuwei22007/article/details/49366745 #激活函數用tanh #你還能夠在model.add(Activation('tanh'))後加上dropout的技巧: model.add(Dropout(0.5)) model.add(Convolution2D(4, 5, 5, border_mode='valid',input_shape=(1,28,28))) model.add(Activation('tanh')) #第二個卷積層】8個卷積核,每一個卷積核大小3*3。4表示輸入的特徵圖個數,等於上一層的卷積核個數 #激活函數用tanh #採用maxpooling,poolsize爲(2,2) model.add(Convolution2D(8, 3, 3, border_mode='valid')) model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size
=(2, 2))) #第三個卷積層】16個卷積核,每一個卷積核大小3*3 #激活函數用tanh #採用maxpooling,poolsize爲(2,2) model.add(Convolution2D(16, 3, 3, border_mode='valid')) model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size
=(2, 2)))
#全鏈接層】,先將前一層輸出的二維特徵圖flatten爲一維的。 #Dense就是隱藏層。16就是上一層輸出的特徵圖個數。4是根據每一個卷積層計算出來的:(28-5+1)獲得24,(24-3+1)/2獲得11,(11-3+1)/2獲得4 #全鏈接有128個神經元節點,初始化方式爲normal model.add(Flatten()) model.add(Dense(128, init='normal')) model.add(Activation('tanh')) #Softmax分類】,輸出是10類別 model.add(Dense(10, init='normal')) model.add(Activation('softmax')) ############# #開始訓練模型 ############## #使用SGD + momentum #model.compile裏的參數loss就是損失函數(目標函數) sgd = SGD(lr=0.05, decay=1e-6, momentum=0.9, nesterov=True) model.compile(loss='categorical_crossentropy', optimizer=sgd,metrics=["accuracy"]) #調用fit方法,就是一個訓練過程. 訓練的epoch數設爲10,batch_size爲100. #數據通過隨機打亂shuffle=True。verbose=1,訓練過程當中輸出的信息,0、一、2三種方式均可以,可有可無。show_accuracy=True,訓練時每個epoch都輸出accuracy。 #validation_split=0.2,將20%的數據做爲驗證集。 model.fit(data, label, batch_size=100, nb_epoch=10,shuffle=True,verbose=1,validation_split=0.2)
相關文章
相關標籤/搜索