Deep learning with Python 學習筆記(2)

本節介紹基於Keras的CNN html

卷積神經網絡接收形狀爲 (image_height, image_width, image_channels)的輸入張量(不包括批量維度),寬度和高度兩個維度的尺寸一般會隨着網絡加深而變小。通道數量由傳入 Conv2D 層的第一個參數所控制網絡

用卷積神經網絡對 MNIST 數字進行分類Demoide

from keras import layers
from keras import models
from keras.datasets import mnist
from keras.utils import to_categorical


def set_model():
    model = 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'))
    # 須要將 3D 輸出展平爲 1D,將(3, 3, 64)輸出展平爲(576, )
    model.add(layers.Flatten())
    model.add(layers.Dense(64, activation='relu'))
    model.add(layers.Dense(10, activation='softmax'))
    # 查看模型各層狀態
    model.summary()
    return model


(train_images, train_labels), (test_images, test_labels) = mnist.load_data(path='/home/fan/dataset/mnist.npz')
train_images = train_images.reshape((60000, 28, 28, 1))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28, 28, 1))
test_images = test_images.astype('float32') / 255
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

model = set_model()
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)

運行以後,顯示正確率爲0.9921,而以前使用的密集鏈接網絡的正確率爲0.9794,提升了0.0127學習

密集鏈接層和卷積層的根本區別在於, Dense 層從輸入特徵空間中學到的是全局模式,若是模式出如今新的位置,它只能從新學習這個模式,而卷積層學到的是局部模式,能夠在任何位置進行匹配
學習局部模式使得CNN具備如下性質:測試

  1. 卷積神經網絡學到的模式具備平移不變性(translation invariant)編碼

    卷積神經網絡在圖像右下角學到某個模式以後,它能夠在任何地方識別這個模式,好比左上角
    對於密集鏈接網絡來講,若是模式出如今新的位置,它只能從新學習這個模式lua

  2. 卷積神經網絡能夠學到模式的空間層次結構(spatial hierarchies of patterns)spa

    第一個卷積層將學習較小的局部模式(好比邊緣),第二個卷積層將學習由第一層特徵組成的更大的模式,以此類推。這使得卷積神經網絡能夠有效地學習愈來愈複雜、愈來愈抽象的視覺概念(由於視覺世界從根本上具備空間層次結構)3d

對於包含兩個空間軸(高度和寬度)和一個深度軸(也叫通道軸)的 3D 張量,其卷積也叫特徵圖(feature map)。卷積運算從輸入特徵圖中提取圖塊,並對全部這些圖塊應用相同的變換,生成輸出特徵圖(output feature map)。該輸出特徵圖還是一個 3D 張量,具備寬度和高度,其深度能夠任意取值,由於輸出深度是層的參數,深度軸的不一樣通道再也不像 RGB 輸入那樣表明特定顏色,而是表明過濾器(filter)。過濾器對輸入數據的某一方面進行編碼rest

上例中,模型定義了

model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))

該卷積層接收一個大小爲 28 * 28 * 1 的特徵圖,輸出一個 26 * 26 * 32 的特徵圖(26 = (28 -3) / 1 + 1),該26 * 26 * 32是過濾器對輸入的響應圖(response map),表示這個過濾器模式在輸入中不一樣位置的響應。這也是特徵圖這一術語的含義: 深度軸的每一個維度都是一個特徵(或過濾器),而 2D 張量 output[:, :, n]是這個過濾器在輸入上的響應的二維空間圖(map)

卷積由如下兩個關鍵參數所定義

  1. 從輸入中提取的圖塊尺寸: 這些圖塊的大小一般是 3×3 或 5×5
  2. 輸出特徵圖的深度:卷積所計算的過濾器的數量
    對於 Keras 的 Conv2D 層,這些參數都是向層傳入的前幾個參數: Conv2D(output_depth, (window_height, window_width))

卷積的工做原理
在 3D 輸入特徵圖上滑動(slide)這些 3×3 或 5×5 的窗口,在每一個可能的位置中止並提取周圍特徵的 3D 圖塊[形狀爲 (window_height, window_width, input_depth) ]。而後每一個 3D 圖塊與學到的同一個權重矩陣[叫做卷積核(convolution kernel)]作張量積,轉換成形狀爲 (output_depth,) 的 1D 向量。而後對全部這些向量進行空間重組,使其轉換爲形狀爲 (height, width, output_depth) 的 3D 輸出特徵圖。輸出特徵圖中的每一個空間位置都對應於輸入特徵圖中的相同位置

卷積計算

可見,當特徵圖經過卷積核以後,特徵圖的尺寸變小,具體變化爲
\[ outputSize = \frac {(inputSize - ConvSize + 2*padding)}{stride} \]
其中,outputSize 爲輸出尺寸,inputSize 爲輸入尺寸,ConvSize爲卷積核尺寸,padding 爲填充,stride 爲步幅

對於 Conv2D 層,能夠經過 padding 參數來設置填充,這個參數有兩個取值: "valid" 表示不使用填充(只使用有效的窗口位置);"same" 表示「填充後輸出的寬度和高度與輸入相同」。padding 參數的默認值爲 "valid"

最大池化一般使用 2×2 的窗口和步幅 2,其目的是將特徵圖下采樣 2 倍。與此相對的是,卷積一般使用 3×3 窗口和步幅 1
經過池化,咱們能夠減小參數數量,防止過擬合,同時可使得以後的卷積相對於以前的得到更大的視野,從而更好地學習特徵的空間層級結構

卷積神經網絡主要由 Conv2D 層(使用 relu 激活)和MaxPooling2D 層交替堆疊構成,當要處理更大的圖像和更復雜的問題時,須要相應的增大網絡,便可以再增長一個 Conv2D + MaxPooling2D 的組合。這既能夠增大網絡容量,也能夠進一步減少特徵圖的尺寸,使其在鏈接 Flatten 層時尺寸不會太大

在向網絡中輸入數據時,咱們首先須要將數據進行預處理,將其格式化爲浮點數張量,JPEG數據處理步驟以下

  1. 讀取圖像
  2. 將JPEG文件解碼爲RGB像素網絡
  3. 將像素網絡轉換爲浮點數張量
  4. 將像素值縮放到[0, 1]區間

當數據量較大時,咱們能夠採用生成器的方式將數據依次餵給網絡來進行擬合
Keras包含ImageDataGenerator 類,能夠快速建立 Python 生成器,可以將硬盤上的圖像文件自動轉換爲預處理好的張量批量
讓模型對數據擬合

model.fit_generator(train_generator, steps_per_epoch=100, epochs=30, 
                    validation_data=validation_generator, validation_steps=50)

第一個參數爲數據生成器,第二個參數表示從生成器中抽取 steps_per_epoch 個批量後(即運行了steps_per_epoch 次梯度降低),擬合過程將進入下一個輪次,第三個參數爲驗證數據,若是其爲一個數據生成器的話,須要指定validation_steps參數,來講明須要從驗證生成器中抽取多少個批次用於評估

Keras保存模型

model.save('\*\*\*.h5')

一個使用CNN的貓狗分類Demo
數據集下載
此處爲了快速獲得結果,使用貓狗各1000個圖像訓練,各500個驗證,各500個測試

from keras import layers
from keras import models
from keras import optimizers
import os
import matplotlib.pyplot as plt


def get_model():
    # 貓狗二分類
    model = models.Sequential()
    model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))
    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(128, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Flatten())
    model.add(layers.Dense(512, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))
    # 顯示模型各層信息
    model.summary()
    model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4), metrics=['acc'])
    return model


from keras.preprocessing.image import ImageDataGenerator


def data_preprocess(train_dir, validation_dir):
    # Python生成器會不斷循環目標文件夾中的圖像,從而會不停地生成批量
    # 將圖像乘1/255縮放
    train_datagen = ImageDataGenerator(rescale=1. / 255)
    test_datagen = ImageDataGenerator(rescale=1. / 255)
    # 將全部文件調整爲150 * 150
    train_generator = train_datagen.flow_from_directory(train_dir, target_size=(150, 150), batch_size=20, class_mode='binary')
    validation_generator = test_datagen.flow_from_directory(validation_dir, target_size=(150, 150), batch_size=20, class_mode='binary')
    return train_generator, validation_generator


base_dir = '/home/fan/dataset/dogVScat/testDogVSCat'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
train_generator, validation_generator = data_preprocess(train_dir, validation_dir)
model = get_model()
history = model.fit_generator(train_generator, steps_per_epoch=100, epochs=30, validation_data=validation_generator, validation_steps=50)


acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()

實驗結果
loss: 0.0304 - acc: 0.9925 - val_loss: 1.2209 - val_acc: 0.7010

從如上結果能夠看出,咱們的網絡過擬合了,可使用數據加強的方式來防止過擬合

數據加強是從現有的訓練樣本中生成更多的訓練數據,其方法是利用多種可以生成可信圖像的隨機變換來增長(augment)樣本。其目標是,模型在訓練時不會兩次查看徹底相同的圖像。這讓模型可以觀察到數據的更多內容,從而具備更好的泛化能力
在 Keras 中,這能夠經過對 ImageDataGenerator 實例讀取的圖像執行屢次隨機變換來實現
Demo

from keras.preprocessing.image import ImageDataGenerator  
datagen = ImageDataGenerator(rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True, fill_mode='nearest')

其中

rotation_range 是角度值(在 0~180 範圍內),表示圖像隨機旋轉的角度範圍
width_shift 和 height_shift 是圖像在水平或垂直方向上平移的範圍(相對於總寬度或總高度的比例)
shear_range 是隨機錯切變換的角度
zoom_range 是圖像隨機縮放的範圍
horizontal_flip 是隨機將一半圖像水平翻轉
fill_mode 是用於填充新建立像素的方法,這些新像素可能來自於旋轉或寬度 / 高度平移

使用數據加強的方法增長數據

from keras.preprocessing import image
import matplotlib.pyplot as plt
from keras.preprocessing.image import ImageDataGenerator
import numpy as np


img_path = '/home/fan/dataset/dogVScat/testDogVSCat/train/dogs/dog.77.jpg'
# 加載圖片並調整尺寸
img = np.asarray(image.load_img(img_path, target_size=(150, 150)))
datagen = ImageDataGenerator(rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True, fill_mode='nearest')
plt.imshow(img)
plt.title('original img')
plt.show()


img = img.reshape((1, ) + img.shape)
i = 0
for item in datagen.flow(img, batch_size=1):
    item = image.array_to_img(item[0])
    plt.subplot(2, 2, i+1)
    plt.imshow(item)
    i += 1
    plt.title('generated img ' + str(i))
    if i % 4 == 0:
        break
plt.show()

結果以下

爲了繼續下降過擬合,能夠再向網絡中添加dropout。Keras向網絡中添加dropout

model.add(layers.Dropout(0.5))

經過使用數據加強,正則化以及調節網絡參數能夠在必定程度上提升精度,可是由於數據較少,想要進一步提升精度就須要使用預訓練的模型

Deep learning with Python 學習筆記(3)
Deep learning with Python 學習筆記(1)

相關文章
相關標籤/搜索