我們上一篇已經講到如何建置自己的機器學習環境,但還沒給大家範例程式去測試自己的環境,而此篇將用手寫數字圖象辨識為範例帶著大家一步一步來建置自己的 Keras 神經網路

我們使用安裝 Anaconda 時也會一同安裝到的 Spyder 去編寫程式,首先打開 Anaconda 並選擇你創建的環境,並在環境中下載 Spyder 後開啟


資料讀取

首先,大家都知道訓練神經網路一定需要大量的資料,我們可以直接從 Keras 內建的手寫數字資料集去做這次練習,我們先導入需要的模組 from keras.datasets import mnist,然後再用這行程式碼 mnist.load_data() 讀取訓練和測試資料
from keras.datasets import mnist
from matplotlib.pyplot import imshow

(X_train,Y_train),(X_test,Y_test) = mnist.load_data()
print(X_train.shape)
print(Y_train.shape)
print(X_test.shape)
print(Y_test.shape)

num = 10000
imshow(X_train[num])
print(Y_train[num])

我們可以從 code 的輸出看出訓練資料是60000張28*28的圖片,而測試資料是10000張,隨機挑選一張圖片輸出可看出此資料集是手寫數字



In [1]:
(60000, 28, 28)
(60000,)
(10000, 28, 28)
(10000,)
3



資料預處理

架設網路模型之前,需要預處理訓練和測試資料,首先我們需要把圖片變成四維的張量,X_train = X_train[:, :, :, np.newaxis],X_test 也做相同的操作,原因是我們餵給網路的資料必須是(張數,長度,寬度,影像維度),若是彩色照片影像維度就是3,而因我們的訓練資料是一灰階影像,所以維度是1,在資料集中直接被省略,但在訓練之前須將此一維度新增回去(因有使用numpy套件,需先 import numpy as np)

from keras.datasets import mnist
import numpy as np
(X_train,Y_train),(X_test,Y_test) = mnist.load_data()
print(X_train.shape) X_train = X_train[:,:,:,np.newaxis] print(X_train.shape) ------------------------------------- In [2]: (60000, 28, 28) (60000, 28, 28, 1)

接著處理 Y,也就是資料的定義值做預處理,我們可以從輸出 X_train 的圖片和對應的 Y_train 值看出,Y_train 代表的是圖片的對應數字值,也就是我們的定義輸出,再將定義輸出餵給網路前,須將數值做 one-hot encode,意思是,若我今天有0,1,2三個數字,經過 one-hot encode 後,0會變成(1,0,0)這個數組,1會變成(0,1,0)以此類推,而我們今天影10個數字,表示我們的數組會有9個0,而此編碼可用 tenseorflow 內建的函式完成,首先一樣導入模組 import tensorflow as tf,接著使用下方的函式

from keras.datasets import mnist
import tensorflow as tf
def one_hot_matrix(labels, C):
    depth = tf.constant(C, name = "C")     one_hot_matrix = tf.one_hot(labels, depth, axis=0)     sess = tf.Session()     one_hot = sess.run(one_hot_matrix)     sess.close()     return one_hot
print(Y_train[num]) Y_train = one_hot_matrix(Y_train, 10) Y_train = Y_train.T print(Y_train[num]) -------------------------------------
In [3]:
3 [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]

資料都處理完後就可以開始架設神經網路模型啦~

神經網路建構
我們要使用 Keras 內建的函式去建構自己的神經網路,首先我們先將常用的函式導入
from keras.layers import Input, Dense, Activation, BatchNormalization, Flatten, Conv2D
from keras.layers import MaxPooling2D, Dropout
from keras.models import Model

自定義一個函式去創建自己的網路,並使用 Input() 這個函式去導入訓練的維度資訊
def model(input_shape):
    X_input = Input(input_shape)


接著我們一一介紹常用到的函式跟用法

卷積:

輸出 = Conv2D(輸出資訊厚度, (卷積核長度, 卷積核寬度), strides = (橫向步數, 直向步數), name = '自定義命名')(前一層輸入)

圖中的3、6、10是輸出資訊厚度,5*5是卷積核長度*卷積核寬度,而圖中輸入和兩層卷積可以下列 code 表示



X_input = Input(張數,32,32,3)
X = Conv2D(6, (5, 5), strides = (1, 1), name = 'conv0')(X_input)
X = Conv2D(10, (5, 5), strides = (1, 1), name = 'conv0')(X)

三層的維度各為
In [4]:
(張數,32,32,3)
(張數,28,28,6)
(張數,24,24,10)

我們可以將深度學習進程想像成在減少圖片長寬維度的同時去增加資料的厚度,詳細算法和圖解如附圖



最大池化:
輸出 = MaxPooling2D((池化核長度, 池化核寬度), strides = (橫向步數, 直向步數), name='自定義命名')(前一層輸入)

通常 strides 步數會省略不寫,會直接和池化核長寬同值,附圖為詳細圖解,此圖之最大池化層可用下列程式表示




X_input = Input(張數,5,5,1) X = MaxPooling2D((2, 2), name='mp0')(X_input)

輸入輸出的維度各為


In [5]: (張數,5,5,1) (張數,3,3,1)

標準化:
輸出 = BatchNormalization(axis = 需要標準化的軸, name = '自定義命名')(前一層輸入)

標準化是一個將特徵數據縮放平移到平均值為0標準差為1的數據組,而特徵表準化是有非常多優點的,下圖的藍色圓圈代表的是特徵的等高線,我們可從下圖觀察,發現左圖的特徵 X1, X2 區間相差非常大,所以對應的等高線非常尖,會導致在使用梯度下降法尋求最佳解時,需要很迭代多次才可以收斂,而經過適度縮放的右圖收斂則快速許多




在實際使用此函式時只需設定需要標準化的軸為何在此篇的例子便是資料厚度的對應維度,也就是軸3
X = BatchNormalization(axis = 3, name = 'bn0')(X)

激活函數:


輸出 = Activation('方程式名稱')(前一層輸入)

在神經網路中,單個神經元的激活函數定義了該節點在給定的輸入或輸入的集合下的輸出,附圖是單個神經元的基本結構,是由線性輸出 Z 和非線性輸出 A 兩部分組成



其中,f(x) 即為線性輸出 Z,g(x) 即為非線性輸出,g() 表示激活函數,通俗來說,激活函數一般是非線性函數,其作用是能夠給神經網絡加入一些非線性因素,使得神經網絡可以更好地解決較為複雜的問題,如下圖我們可以看出激活函數能夠幫助我們引入非線性因素,使得神經網絡能夠更好地解決更加複雜的問題。




此篇的例子我們會使用 Relu 激活函數
X = Activation('relu')(X)

另外在使用卷積時也可以指定其是否要有激活函數,若沒指定則是線性激活,如此篇例子便沒有指定,若要在卷積層中指定激活函數,可參考下列程式
X = Conv2D(64, (1, 1), strides = (1, 1), activation = 'relu', name = 'conv01')(X)

Dropout:
輸出 = Dropout(丟失比例)(前一層輸入)



何為 Dropout 下圖表示得十分清楚,Dropout 就是在每代訓練時隨機的讓指定比例的神經元失去功用(神經元輸出值變成0),以防止過度擬合(overfitting),因為隨機的停擺神經元可讓各個神經元對於前一層的輸入不會產生過度依賴的情況,因為並不是每一次訓練都會接收到該神經元的輸入,這使得神經網路會去學習尋找更具參考價值的特徵

實際的使用方法是將包含指定被 dropout 網路的代數作為輸入並指定比例
X = Dropout(0.2)(X)

展平:
輸出 = Flatten()(前一層輸入)

顧名思義就是將資料展平成一整條的數組,假設我將(10,4,4,128)的資料 Flatten,輸出的維度就會是10*4*4*128=20480,準備進行全連接並分類



全連接:
輸出 = Dense(輸出神經元數量, activation='方程式名稱', name='自定義命名')(前一層輸入)

全連接層的意義是將展平後的資訊整合成最後的分類,假設最後要區分成5種分類,那就要創建5個神經元去接收前一層的輸入並經激活函數後去輸出,最後的輸出層每顆神經元都有對應的分類,網路訓練完後,在預測時會比較最後一層的神經元輸出,輸出值最大的神經元就表示神經網路的預測結果是該神經元的對應類別



我們可以用程式實際表示上圖的網路連接
X = Dense(7, activation='relu', name='d0')(X) X = Dense(9, activation='relu', name='d0')(X) X = Dense(5, activation='relu', name='d0')(X)

介紹完後這邊讓大家看一下我自己簡單架構的網路
def model(input_shape):     X_input = Input(input_shape)     X = Conv2D(16, (5, 5), strides = (1, 1), name = 'conv0')(X_input)     X = BatchNormalization(axis = 3, name = 'bn0')(X)     X = Activation('relu')(X)
    X = MaxPooling2D((2, 2), name='max_pool0')(X)     X = Conv2D(32, (5, 5), strides = (1, 1), name = 'conv1')(X)     X = BatchNormalization(axis = 3, name = 'bn1')(X)     X = Activation('relu')(X)     X = MaxPooling2D((2, 2), name='max_pool1')(X)     X = Dropout(0.2)(X)     X = Flatten()(X)     X = Dense(128, activation='sigmoid', name='fc0')(X)     X = Dense(10, activation='softmax', name='fc1')(X)     model = Model(inputs = X_input, outputs = X, name='mnistModel')     return model

神經網路的架設就到這邊結束,大家可以根據上述工具的特性,去組合自己所需的神經網路以符合自己的訓練目標,而如何去開始訓練和如何去讀取訓練完的網路並預測,我們就留到本系列的最終篇再說囉~

Post a Comment

較新的 較舊