【Keras篇】---利用keras改寫VGG16經典模型在手寫數字識別體中的應用

LHBlog發表於2018-03-30

一、前述

VGG16是由16層神經網路構成的經典模型,包括多層卷積,多層全連線層,一般我們改寫的時候卷積層基本不動,全連線層從後面幾層依次向前改寫,因為先改引數較小的。

二、具體

1、因為本文中程式碼需要依賴OpenCV,所以第一步先安裝OpenCV

因為VGG要求輸入244*244,而資料集是28*28的,所以需要通過OpenCV在程式碼裡去改變。

2、把模型下載後離線放入使用者的管理目錄下面,這樣訓練的時候就不需要從網上再下載了

3、我們保留的是除了全連線的所有層。

4、選擇資料生成器,在真正使用的時候才會生成資料,載入到記憶體,前面yield只是做了一個標記

 

 程式碼:

# 使用遷移學習的思想,以VGG16作為模板搭建模型,訓練識別手寫字型
# 引入VGG16模組
from keras.applications.vgg16 import VGG16

# 其次載入其他模組
from keras.layers import Input
from keras.layers import Flatten
from keras.layers import Dense
from keras.layers import Dropout
from keras.models import Model
from keras.optimizers import SGD

# 載入字型庫作為訓練樣本
from keras.datasets import mnist

# 載入OpenCV(在命令列中視窗中輸入pip install opencv-python),這裡為了後期對影象的處理,
# 大家使用pip install C:\Users\28542\Downloads\opencv_python-3.4.1+contrib-cp35-cp35m-win_amd64.whl
# 比如尺寸變化和Channel變化。這些變化是為了使影象滿足VGG16所需要的輸入格式
import cv2
import h5py as h5py
import numpy as np

# 建立一個模型,其型別是Keras的Model類物件,我們構建的模型會將VGG16頂層(全連線層)去掉,只保留其餘的網路
# 結構。這裡用include_top = False表明我們遷移除頂層以外的其餘網路結構到自己的模型中
# VGG模型對於輸入影象資料要求高寬至少為48個畫素點,由於硬體配置限制,我們選用48個畫素點而不是原來
# VGG16所採用的224個畫素點。即使這樣仍然需要24GB以上的記憶體,或者使用資料生成器
model_vgg = VGG16(include_top=False, weights='imagenet', input_shape=(48, 48, 3))#輸入進來的資料是48*48 3通道
#選擇imagnet,會選擇當年大賽的初始引數
#include_top=False 去掉最後3層的全連線層看原始碼可知
for layer in model_vgg.layers:
    layer.trainable = False#別去調整之前的卷積層的引數
model = Flatten(name='flatten')(model_vgg.output)#去掉全連線層,前面都是卷積層
model = Dense(4096, activation='relu', name='fc1')(model)
model = Dense(4096, activation='relu', name='fc2')(model)
model = Dropout(0.5)(model)
model = Dense(10, activation='softmax')(model)#model就是最後的y
model_vgg_mnist = Model(inputs=model_vgg.input, outputs=model, name='vgg16')
#把model_vgg.input  X傳進來
#把model Y傳進來 就可以訓練模型了

# 列印模型結構,包括所需要的引數
model_vgg_mnist.summary()


#以下是原版的模型結構 224*224
model_vgg = VGG16(include_top=False, weights='imagenet', input_shape=(224, 224, 3))
for layer in model_vgg.layers:
    layer.trainable = False#別去調整之前的卷積層的引數
model = Flatten()(model_vgg.output)
model = Dense(4096, activation='relu', name='fc1')(model)
model = Dense(4096, activation='relu', name='fc2')(model)
model = Dropout(0.5)(model)
model = Dense(10, activation='softmax', name='prediction')(model)
model_vgg_mnist_pretrain = Model(model_vgg.input, model, name='vgg16_pretrain')

model_vgg_mnist_pretrain.summary()

# 新的模型不需要訓練原有卷積結構裡面的1471萬個引數,但是注意引數還是來自於最後輸出層前的兩個
# 全連線層,一共有1.2億個引數需要訓練
sgd = SGD(lr=0.05, decay=1e-5)#lr 學習率 decay 梯度的逐漸減小 每迭代一次梯度就下降 0.05*(1-(10的-5))這樣來變
#隨著越來越下降 學習率越來越小 步子越小
model_vgg_mnist.compile(loss='categorical_crossentropy',
                                 optimizer=sgd, metrics=['accuracy'])

# 因為VGG16對網路輸入層需要接受3通道的資料的要求,我們用OpenCV把影象從32*32變成224*224,把黑白影象轉成RGB影象
# 並把訓練資料轉化成張量形式,供keras輸入
(X_train, y_train), (X_test, y_test) = mnist.load_data("../test_data_home")
X_train, y_train = X_train[:1000], y_train[:1000]#訓練集1000條
X_test, y_test = X_test[:100], y_test[:100]#測試集100條
X_train = [cv2.cvtColor(cv2.resize(i, (48, 48)), cv2.COLOR_GRAY2RGB)
           for i in X_train]#變成彩色的
#np.concatenate拼接到一起把
X_train = np.concatenate([arr[np.newaxis] for arr in X_train]).astype('float32')

X_test = [cv2.cvtColor(cv2.resize(i, (48, 48)), cv2.COLOR_GRAY2RGB)
          for i in X_test]
X_test = np.concatenate([arr[np.newaxis] for arr in X_test]).astype('float32')

print(X_train.shape)
print(X_test.shape)

X_train = X_train / 255
X_test = X_test / 255


def tran_y(y):
    y_ohe = np.zeros(10)
    y_ohe[y] = 1
    return y_ohe


y_train_ohe = np.array([tran_y(y_train[i]) for i in range(len(y_train))])
y_test_ohe = np.array([tran_y(y_test[i]) for i in range(len(y_test))])

model_vgg_mnist.fit(X_train, y_train_ohe, validation_data=(X_test, y_test_ohe),
                             epochs=100, batch_size=50)

 結果:

 自定義的網路層:

 

相關文章