深度學習 實驗五 基於Keras卷積神經網絡實現

深度學習 實驗五 基於Keras卷積神經網絡實現

一、問題描述

掌握基於TensorFlow的高級API框架Keras的基本用法,仍然通過MNIST手寫數字體數據集,學會搭建給予Keras API的卷積神經網絡,並用來識別手寫數字體。

二、設計簡要描述

1. 準備工作

1.1 導包

從TensorFlow中導入Keras的相關包,包含MNIST的數據集mnist模塊、model和layer層的相關模塊。

1.2 載入數據

利用mnist模塊的input_data自動下載導入數據集(已下載則直接導入)。

1.3 配置神經網絡

對於神經網絡中的所需的圖像的維度信息進行提前設置確定,方便之後直接統一調用。

1.4 繪製圖像的輔助函數

對輸入的圖像進行打印顯示,同步顯示該圖像的標籤值和預測值(預測值可選),默認輸出前9張圖片信息供參考。

1.5 繪製錯誤分類圖像的輔助函數

在預測過程中存在錯誤分類的圖像,設置函數可以打印該部分圖像,同步顯示圖像、真實標籤值、錯誤預測值。

2. 序列模型

2.1 搭建模型框架

利用Keras的序列模型逐步搭建模型框架,包含輸入層、卷積層、池化層、完全連接層和輸出層及相關參數設置。

2.2 模型編譯

利用Keras中Model的編譯函數compile對模型進行編譯,選擇設置好優化器、學習率和損失函數等參數。

2.3 模型訓練

利用Model的fit函數對模型進行訓練,同時將數據集的驗證集一併加入,查看訓練結果。

2.4 評估與性能指標

利用Model的evaluate函數對測試集進行測試,評估模型的精確率。

2.5 預測

利用Model的predict函數取一部分測試集進行預測,再利用在準備工作中定義好的繪製圖像的輔助函數進行打印輸出方便查看結果。

2.6 錯分類的圖片

收集對測試集預測後的結果中錯誤分類,然後調用準備工作中定義好的繪製錯誤分類的輔助函數進行打印錯誤分類的圖像信息,包含圖像、真實標籤值、錯誤的預測值。

因爲預測結果和測試集的真實標籤是獨熱編碼數組,所有需要先將預測結果和測試集的標籤數組轉換爲整數值,然後進行對比找出預測錯誤的圖像下標,再進行函數調用。

3. 功能模型

序列模型相對比較簡單,Keras同時提供了可以構建更爲複雜的功能模型。

3.1 搭建模型框架

定義模型的輸入層和中間的卷積層、池化層、完全連接層和輸出層的相關參數設置。

3.2 模型編譯

將定義好的模型框架賦值給新的model,設置優化器、損失函數等參數信息,再調用Model的compile進行編譯,可以使用summary函數對模型的的具體信息進行查看。

3.3 訓練

利用model的fit函數進行模型訓練,同時添加驗證集數據,進一步評估模型性能。

3.4 評估

利用model的evaluate函數對測試集進行評估,打印查看精確率。

3.5 錯分類圖片

對於模型進行測試集的圖像預測,其中錯誤分類的圖像可以進行打印查看。先是需要將得到的獨熱編碼的數組轉換爲整數型數組,再對比收集分類的數據,最後調用定義好的繪製錯誤分類的圖像的函數即可。

4. 保存和加載模型

4.1 保存Keras模型

設置模型的保存路徑和名稱,直接嗲用model.sava()即可保存模型。

4.2 刪除模型

Keras中可以直接使用 del 模型名 進行刪除模型。

4.3 加載模型

使用Model中的load_model函數可以直接加載保存爲文件的模型,加載之前保存的功能模型,用於測試。

4.4 用加載的模型來預測測試集的前9張圖片

調用model的predic函數對測試集進行預測,對結果使用定義好的繪製圖像函數plot_images進行打印查看。

5. 權重和輸出的可視化

5.1 定義畫卷積權重的輔助函數

5.2 得到層

利用summary函數查看加載的model2的所有層信息,獲取輸入層、第一層卷積層、第二層卷積層。

5.3 卷積權重

通過layer.get_weights()獲取各層權重,然後再通過plot_conv_weights函數來繪製權重圖。

5.4 繪製出卷積層輸出的幫助函數

5.5 輸入圖像

完成plot_image(image)函數(參數image表示圖像)並繪製測試集的第一張圖片。

5.6 卷積層輸出之方法一

5.6.1 基於K函數的模型轉換函數

使用K函數來獲取圖層輸出(參數分別是輸入層的輸入和卷積層1的輸出),並賦值給output_conv1。

5.6.2 獲取卷積層1的輸出

5.6.3 繪製輸出

利用plot_conv_output函數繪製得到卷積層1的輸出。

5.7 卷積層輸出之方法二

5.7.1 基於Model函數的圖層輸出獲取

使用與原始模型相同的輸入創建另一個功能模型,但輸出取自目前所需的的卷積層。

5.7.2 獲取卷積層2的輸出

調用output_conv2的predict()函數得到卷積層2的輸出,賦值給layer_output2並查看其形狀。

5.7.3 繪製輸出

最後,可以繪製卷積層的所有36個通道的輸出,使用plot_conv_output函數繪製卷積層2的輸出。

三、程序清單

# 基於Keras卷積神經網絡實現MNIST

# 1. 準備工作
# 1.1 導包
# 從Keras的datasets模塊中導入數據集mnist模塊,從layer.core模塊導入Dense和Activation模塊,
# 從optimizers 模塊導入SGD模塊,從utils模塊導入np_utils模塊
get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import math
import h5py

# InputLayer,Input,Reshape,MaxPooing2D,Conv2D,Dense,Flatten
from tensorflow.python.keras.models import Sequential,Model
from tensorflow.python.keras.layers import InputLayer, Input, Reshape, MaxPooling2D, Conv2D, Dense, Flatten

# 引入數據導入模塊
from tensorflow.examples.tutorials.mnist import input_data

# 1.2 載入數據
#從MNIST_data/中讀取MNIST數據,這條語句在數據不存在是,會自動執行下載
data = input_data.read_data_sets("MNIST_data/", one_hot=True)

# 1.3 配置神經網絡
# 圖像的每個維度中的像素數 28
train_img_size = 28
img_size = train_img_size

# 圖像存儲在一維矩陣中的總長度  784
img_size_flat = data.train.images[0].shape[0]

# 用來重塑圖像的高度和寬度的元組。 (28, 28)
img_shape = np.zeros((28, 28), dtype=float).shape

# 用來重塑圖像的高度,寬度和深度的元組。
# 這用於在Keras重塑。  (28, 28 ,1)
img_shape_full = np.zeros((28, 28, 1), dtype=float).shape

#類別數量  10
num_classes = 10

#  圖像的通道數  1
num_channels = 1

#打印顯示變量
print(img_size)
print(img_size_flat)
print(img_shape)
print(img_shape_full)
print(num_classes)
print(num_channels)

# 1.4 繪製圖像的輔助函數
def plot_images(images, cls_true, cls_pred=None):
    assert len(images) == len(cls_true) == 9
    
    # 創建3x3子圖.
    fig, axes = plt.subplots(3, 3)
    fig.subplots_adjust(hspace=0.3, wspace=0.3)

    for i, ax in enumerate(axes.flat):
        # 畫圖.
        ax.imshow(images[i].reshape(img_shape), cmap='binary')

        # 顯示真正的預測的類別.
        if cls_pred is None:
#             xlabel = "True: {0}".format(cls_true[i])
            xlabel = "True: {0}".format(int(np.nonzero(cls_true[i])[0]))
        else:
#             xlabel = "True: {0}, Pred: {1}".format(cls_true[i], cls_pred[i])
            xlabel = "True: {0}, Pred: {1}".format(int(np.nonzero(cls_true[i])[0]), np.argmax(cls_pred[i]))
        #將類別作爲x軸的標籤
        ax.set_xlabel(xlabel)
        
        # 去除圖中的刻度線.
        ax.set_xticks([])
        ax.set_yticks([])
    
plt.show()

plot_images(data.train.images[0:9], data.train.labels[0:9])

# 1.5 繪製錯誤分類圖像的輔助函數
def plot_example_errors(cls_pred, correct):
    # 此函數從下面的print_test_accuracy()調用
    # cls_pred包含了測試集所有圖像的預測類別
    # correct是布爾數組,表示預測的類是否等於測試集中每個圖像的真是類別.
    # 布爾數組中未正確分類的圖像
    incorrect = (correct == False)
    
    # 從測試集中獲取未正確分類的圖片
    images = data.test.images[incorrect]
    
    # 獲取這些圖像的預測列表
    cls_pred = cls_pred[incorrect]

    # 獲取這些圖像的正確類別
    cls_true = data.test.labels[incorrect]
    
    # 畫出前9張圖像
    plot_images(images=images[0:9],
                cls_true=cls_true[0:9],
                cls_pred=cls_pred[0:9])

# 2.序列模型

# 2.1 模型框架
# 開始構建Keras 序列模型。
model = Sequential()
# 添加一個輸入層,類似於TensorFlow中的feed_dict。
#輸入形狀input-shape 必須是包含圖像大小image-size_flat的元組。
model.add(InputLayer(input_shape=(img_size_flat,)))

# 輸入是一個包含784個元素的扁平數組,
# 但卷積層期望圖像形狀是(28,28,1)
model.add(Reshape(img_shape_full))

# 具有ReLU激活和最大池化的第一個卷積層。
model.add(Conv2D(kernel_size=5, strides=1, filters=16, padding='same',
                 activation='relu', name='layer_conv1'))
model.add(MaxPooling2D(pool_size=2, strides=2))

# 具有ReLU激活和最大池化的第二個卷積層
model.add(Conv2D(kernel_size=5, strides=1, filters=36, padding='same',
                 activation='relu', name='layer_conv2'))
model.add(MaxPooling2D(pool_size=2, strides=2))

# 將卷積層的4級輸出展平爲2級,可以輸入到完全連接/密集層。
model.add(Flatten())

# 具有ReLU激活的第一個完全連接/密集層。
model.add(Dense(128, activation='relu'))

# 最後一個完全連接/密集層,具有softmax激活功能,用於分類
model.add(Dense(num_classes, activation='softmax'))  

model.summary()

# 2.2 模型編譯
# 從tensorflow.python.keras.optimizers包中導入Adam,並設置優化器optimizer的學習率爲1e-3,然後編譯模型,
# 將參數optimizer設置爲optimizer, loss設置爲'categorical_crossentropy',矩陣 metrics設置爲'accuracy'
from tensorflow.keras.optimizers import Adam

optimizer = Adam(lr=1e-3)

model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])

# 2.3 訓練
# epochs設置爲1,batch_size設置爲128
history = model.fit(data.train.images, data.train.labels, epochs=1, batch_size=128,
                   verbose = 1, validation_data=[data.validation.images, data.validation.labels])

# 2.4 評估與性能指標
result = model.evaluate(data.test.images, data.test.labels, verbose = 2)

# 2.5 預測
cls_pred = model.predict(data.test.images[0:9], verbose=0)

plot_images(data.test.images[0:9], data.test.labels[0:9], cls_pred)

# 2.6 錯分類的圖片
# 從測試集中繪製錯誤分類圖像的一些示例。
# 從測試集中得到所有圖像的預測類,參數返回給y_pred,
# 然後將預測的類數從One-Hot編碼數組轉換爲整數賦值給cls_pred,
# 最後調用plot_example_errors繪製y_pred的前九張錯分類的圖像
y_pre = model.predict(data.test.images, verbose=0)

# 由one-hot轉換爲普通np數組
cls_pred = np.arange(0, len(y_pre))
for i in range(len(y_pre)):
    cls_pred[i] = np.argmax(y_pre[i])

y_test = np.arange(0, len(data.test.labels))
for i in range(len(data.test.labels)):
    y_test[i] = np.argmax(data.test.labels[i])

# correct是布爾數組,表示預測的類是否等於測試集中每個圖像的真是類別.
correct = np.arange(0, len(cls_pred))
for i in range(len(cls_pred)):
    if cls_pred[i] == y_test[i]:
        correct[i] = True
    else:
        correct[i] = False

plot_example_errors(y_pre, correct)

# 3. 功能模型

# 3.1 模型框架
# 創建一個輸入層,類似於TensorFlow中的feed_dict。.
#輸入形狀input-shape 必須是包含圖像大小image_size_flat的元組。.
inputs = Input(shape=(img_size_flat,))

#用於構建神經網絡的變量。
net = inputs

# 輸入是一個包含784個元素的扁平數組
#  但卷積層期望圖像形狀是(28,28,1)
net = Reshape(img_shape_full)(net)
#  具有ReLU激活和最大池化的第一個卷積層。
net = Conv2D(kernel_size=5, strides=1, filters=16, padding='same',
             activation='relu', name='layer_conv1')(net)
net = MaxPooling2D(pool_size=2, strides=2)(net)

#  具有ReLU激活和最大池化的第二個卷積層.
net = Conv2D(kernel_size=5, strides=1, filters=36, padding='same',
             activation='relu', name='layer_conv2')(net)
net = MaxPooling2D(pool_size=2, strides=2)(net)

# 將卷積層的4級輸出展平爲2級,可以輸入到完全連接/密集層。
net = Flatten()(net)

# 具有ReLU激活的第一個完全連接/密集層。
net = Dense(128, activation='relu')(net)

#  最後一個完全連接/密集層,具有softmax激活功能,用於分類
net = Dense(num_classes, activation='softmax')(net)

#神經網絡輸出
outputs = net

# 3.2 模型編譯
# 把模型賦值給model2,完成model.compile的配置,其中optimizer選擇rmsprop,其餘不變
from tensorflow.keras.models import Model

model2 = Model(inputs=inputs, outputs=outputs)

from tensorflow.keras.optimizers import RMSprop

rmsprop = RMSprop(lr=0.001, rho=0.9, epsilon=1e-08, decay=0.0)
model2.compile(optimizer=rmsprop, loss='categorical_crossentropy', metrics=['accuracy'])

model2.summary()

# 3.3 訓練
# 使用和序列模型相同的fit()函數進行訓練,此函數採用numpy數組作爲輸入。其中epoches=1,batch_siez = 128。
history2 = model2.fit(data.train.images, data.train.labels, epochs=1, batch_size=128,
                   verbose = 1, validation_data=[data.validation.images, data.validation.labels])

# 3.4 評估
# 參照2.4請用model.evaluate方法來評估其在測試集上的性能,並將結果返回給result,
# 然後查看result的性能指標,result的返回值分別對應的名稱是model.metrics_names。
# 最後,請查看直接打印出準確率的結果
result = model2.evaluate(data.test.images, data.test.labels, verbose = 2)

# 3.5 錯分類的圖片
y_pre2 = model2.predict(data.test.images, verbose=0)

# 由one-hot轉換爲普通np數組
cls_pred2 = np.arange(0, len(y_pre2))
for i in range(len(y_pre2)):
    cls_pred2[i] = np.argmax(y_pre2[i])

y_test2 = np.arange(0, len(data.test.labels))
for i in range(len(data.test.labels)):
    y_test2[i] = np.argmax(data.test.labels[i])

# correct是布爾數組,表示預測的類是否等於測試集中每個圖像的真是類別.
correct2 = np.arange(0, len(cls_pred2))
for i in range(len(cls_pred2)):
    if cls_pred2[i] == y_test2[i]:
        correct2[i] = True
    else:
        correct2[i] = False

plot_example_errors(y_pre2, correct2)

# 4. 保存和加載模型

# 4.1 保存Keras模型
# 序列模型保存
# 設置模型的保存路徑
path_model = 'model.keras'
# 簡單的使用model.save()保存模型
model.save(path_model)

# 功能模型保存
path_model2 = 'model2.keras'
model2.save(path_model2)

# 4.2 刪除模型
del model2

# 4.3 加載模型
from tensorflow.python.keras.models import load_model
# 請加載上述保存的模型然後賦值給model3
model3 = load_model(path_model)
model4 = load_model(path_model2)

# 4.4 用加載的模型來預測測試集的前9張圖片
cls_pred3 = model3.predict(data.test.images[0:9], verbose=0)
plot_images(data.test.images[0:9], data.test.labels[0:9], cls_pred3)

cls_pred4 = model4.predict(data.test.images[0:9], verbose=0)
plot_images(data.test.images[0:9], data.test.labels[0:9], cls_pred4)

# 5. 權重和輸出的可視化

# 5.1畫卷積權重的輔助函數
def plot_conv_weights(weights, input_channel=0):
    # 獲取權重的最低值和最高值
    # 這用於校正圖像的顏色強度,以便可以相互比較.
    w_min = np.min(weights)
    w_max = np.max(weights)
    # 卷積層中的卷積核數量
    num_filters = weights.shape[3]
    #要繪製的網格數.
    # 卷積核的平方根.
    num_grids = math.ceil(math.sqrt(num_filters))    
    #創建帶有網格子圖的圖像.
    fig, axes = plt.subplots(num_grids, num_grids)
    #  繪製所有卷積核的權重
    for i, ax in enumerate(axes.flat):
        #  僅繪製有限的卷積核權重
        if i<num_filters:
            #  獲取輸入通道的第i個卷積核的權重
            #   有關於4維張量格式的詳細信息請參閱new_conv_layer() 
            img = weights[:, :, input_channel, i]
            # 畫圖
            ax.imshow(img, vmin=w_min, vmax=w_max,
                      interpolation='nearest', cmap='seismic')        
        # 去除刻度線.
        ax.set_xticks([])
        ax.set_yticks([])  
    plt.show()

# 5.2 得到層
# 調用summary方法查看model4的所有層的信息
# 並把輸入層賦值給layer_input
# 第一層卷積層賦值給layer_conv1
# 第二層卷積層輔助給layer_conv2
model4.summary()
layer_input = model4.layers[0]
layer_conv1 = model4.layers[2]
layer_conv2 = model4.layers[4]

# 5.3 卷積權重
# 通過layer.get_weights()獲取各層權重,然後再通過plot_conv_weights函數來繪製權重圖
weights_conv1 = layer_conv1.get_weights()[0]
plot_conv_weights(weights=weights_conv1, input_channel=0)
weights_conv2 = layer_conv2.get_weights()[0]
plot_conv_weights(weights=weights_conv2, input_channel=0)

# 5.4 繪製出卷積層輸出的幫助函數
# 其中參數values就代表了卷積層
def plot_conv_output(values):
    # 卷積層中的卷積核數量
    num_filters = values.shape[3]
    # 要繪製的網格數
    # 卷積核的平方根
    num_grids = math.ceil(math.sqrt(num_filters))    
    # 創建帶有網格子圖的圖像
    fig, axes = plt.subplots(num_grids, num_grids)
    #畫出所有卷積核的輸出圖像
    for i, ax in enumerate(axes.flat):
        # 僅畫出有效卷積核圖像
        if i<num_filters:
            # 獲取第i個卷積核的輸出圖像
            img = values[0, :, :, i]
            # 畫圖e.
            ax.imshow(img, interpolation='nearest', cmap='binary')        
        # 移除刻度線
        ax.set_xticks([])
        ax.set_yticks([])    
    plt.show()

# 5.5 輸入圖像
def plot_image(image):
    plt.imshow(image.reshape(img_shape),
               interpolation='nearest',
               cmap='binary')
    plt.show()

image1 = data.test.images[0]
plot_image(image1

# 5.6 卷積層輸出之方法一
# 5.6.1 基於K函數的模型轉換函數
from tensorflow.python.keras import backend as K
# 使用K函數來獲取圖層輸出(參數分別是輸入層的輸入和卷積層1的輸出),並賦值給output_conv1
output_conv1 = K.function(inputs=[layer_input.input], outputs=[layer_conv1.output])

# 5.6.2 獲取卷積層1的輸出
layer_output1 = output_conv1(np.array([image1]))[0]
layer_output1.shape

# 5.6.3 繪製輸出
plot_conv_output(values=layer_output1)

# 5.7 卷積層輸出之方法二
# 5.7.1 基於Model函數的圖層輸出獲取
output_conv2 = Model(inputs=layer_input.input, outputs=layer_conv2.output)

# 5.7.2 獲取卷積層2的輸出
# 調用output_conv2的predict()函數得到卷積層2的輸出,賦值給layer_output2並查看其形狀
layer_output2 = output_conv2.predict(np.array([image1]))
layer_output2.shape

# 5.7.3 繪製輸出
# 最後,可以繪製卷積層的所有36個通道的輸出,使用plot_conv_output函數繪製卷積層2的輸出:
plot_conv_output(values=layer_output2)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章