使用Keras構(gòu)建卷積神經(jīng)網(wǎng)絡(luò)模型

使用Keras能夠很方便地構(gòu)建神經(jīng)網(wǎng)絡(luò)模型,本文從弗朗索瓦·肖萊(Keras之父)所著的《Deep Learning with Python》一書中,整理了若干個構(gòu)建好的用于計算機(jī)視覺的神經(jīng)網(wǎng)絡(luò)模型。希望通過這些案例,能對深度學(xué)習(xí)(即卷積神經(jīng)網(wǎng)絡(luò))有更為直觀的理解。

目錄

一、訓(xùn)練一個普通神經(jīng)網(wǎng)絡(luò)(全連接)來識別MNIST數(shù)據(jù)集
二、在MNIST數(shù)據(jù)集上實現(xiàn)一個小型卷積神經(jīng)網(wǎng)絡(luò)
三、在dogs_vs_cats小型數(shù)據(jù)集上從頭開始訓(xùn)練一個卷積神經(jīng)網(wǎng)絡(luò)
四、使用數(shù)據(jù)增強(qiáng)在dogs_vs_cats小型數(shù)據(jù)集上從頭開始訓(xùn)練一個卷積神經(jīng)網(wǎng)絡(luò)
五、使用預(yù)訓(xùn)練方法在dogs_vs_cats小型數(shù)據(jù)集上從頭開始訓(xùn)練一個卷積神經(jīng)網(wǎng)絡(luò)
六、可視化卷積神經(jīng)網(wǎng)絡(luò)

1.訓(xùn)練一個普通神經(jīng)網(wǎng)絡(luò)(全連接)來識別MNIST數(shù)據(jù)集

#from keras.datasets import mnist   加載Keras中的MNIST數(shù)據(jù)集,由于Mnist數(shù)據(jù)集被墻,可以在https://s3.amazonaws.com/img-datasets/mnist.npz網(wǎng)址下載

def load_data(): #讀取下載的數(shù)據(jù)

    """Loads the MNIST dataset.

    # Arguments

        path: path where to cache the dataset locally

            (relative to ~/.keras/datasets).

    # Returns

        Tuple of Numpy arrays: `(x_train, y_train), (x_test, y_test)`.

        """

    path='C://Users//Administrator//Desktop//mnist.npz'

    f = np.load(path)

    x_train, y_train = f['x_train'], f['y_train']

    x_test, y_test = f['x_test'], f['y_test']

    f.close()

    return (x_train, y_train), (x_test, y_test)


import numpy as np


(train_images, train_labels), (test_images, test_labels) = load_data()

#查看數(shù)據(jù)樣式

train_images.shape

len(train_labels)

train_labels

#設(shè)置網(wǎng)絡(luò)架構(gòu)

from keras import models

from keras import layers


network = models.Sequential() #定義模型有兩種方法:一種是使用 Sequential 類(僅用于層的線性堆疊,這是目前最常見的網(wǎng)絡(luò)架構(gòu)),另一種是函數(shù)式 API(functional API,用于層組成的有向無環(huán)圖,讓你可以構(gòu)建任意形式的架構(gòu))

network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))#只接受第一個維度為28*28的2D張量,具有512個輸出單元,該層后面只能連接一個512維向量作為輸入的層;第0軸是批量維度,其大小沒有指定,可取任意值

network.add(layers.Dense(10, activation='softmax'))

#網(wǎng)絡(luò)編譯

network.compile(optimizer='rmsprop',

        loss='categorical_crossentropy',

            metrics=['accuracy'])#包括:優(yōu)化器、損失函數(shù)和在訓(xùn)練和測試過程中需要監(jiān)控的指標(biāo);對于二分類問題,可以使用二元交叉熵?fù)p失函數(shù);對于多分類問題,可用分類交叉熵;對于回歸問題,可用均方誤差。



#準(zhǔn)備圖像數(shù)據(jù)

train_images = train_images.reshape((60000, 28 * 28))#3d張量變?yōu)?d

train_images = train_images.astype('float32') / 255#數(shù)據(jù)歸一化

test_images = test_images.reshape((10000, 28 * 28))

test_images = test_images.astype('float32') / 255

#對標(biāo)簽進(jìn)行分類編碼

from keras.utils import to_categorical


train_labels = to_categorical(train_labels)#將labels轉(zhuǎn)為float32

test_labels = to_categorical(test_labels)

#訓(xùn)練網(wǎng)絡(luò)

network.fit(train_images, train_labels, epochs=5, batch_size=128)

#模型在測試集上的性能

test_loss, test_acc = network.evaluate(test_images, test_labels)

#顯示mnist數(shù)字集

digit = train_images[4]


import matplotlib.pyplot as plt

plt.imshow(digit, cmap=plt.cm.binary)

plt.show()

2.在MNIST數(shù)據(jù)集上實現(xiàn)一個小型卷積神經(jīng)網(wǎng)絡(luò)

#設(shè)置卷積、池化層架構(gòu)
from keras import layers
from keras import models

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))) #卷積神經(jīng)網(wǎng)絡(luò)接收形狀為 (image_height, image_width, image_channels)的輸入張量(不包括批量維度);每個 Conv2D 層和 MaxPooling2D 層的輸出都是一個形狀為 (height, width, channels) 的 3D 張量。寬度和高度兩個維度的尺寸通常會隨著網(wǎng)絡(luò)加深而變小。通道數(shù)量由傳入 Conv2D 層的第一個參數(shù)所控制(32 或 64)。
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'))

#model.summary()  查看模型結(jié)構(gòu)

#在卷積神經(jīng)網(wǎng)絡(luò)上添加分類器
model.add(layers.Flatten())   #將 3D 輸出展平為 1D
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

#在 MNIST 圖像上訓(xùn)練卷積神經(jīng)網(wǎng)絡(luò)
    #讀取、處理數(shù)據(jù)
def load_data():
    """Loads the MNIST dataset.

    # Arguments
        path: path where to cache the dataset locally
            (relative to ~/.keras/datasets).

    # Returns
        Tuple of Numpy arrays: `(x_train, y_train), (x_test, y_test)`.
        """
    path='C://Users//Administrator//Desktop//mnist.npz'
    f = np.load(path)
    x_train, y_train = f['x_train'], f['y_train']
    x_test, y_test = f['x_test'], f['y_test']
    f.close()
    return (x_train, y_train), (x_test, y_test)

import numpy as np
(train_images, train_labels), (test_images, test_labels) = load_data()

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.compile(optimizer='rmsprop',
        loss='categorical_crossentropy',
            metrics=['accuracy'])

    #模型訓(xùn)練
model.fit(train_images, train_labels, epochs=5, batch_size=64)

    #模型測試評估
test_loss, test_acc = model.evaluate(test_images, test_labels)

3.在dogs_vs_cats小型數(shù)據(jù)集上從頭開始訓(xùn)練一個卷積神經(jīng)網(wǎng)絡(luò)

所使用的貓狗分類數(shù)據(jù)集可以從網(wǎng)站 https://www.kaggle.com/c/dogs-vs-cats/data (Kaagle比賽網(wǎng)站)下載。該數(shù)據(jù)集包含 25 000 張貓狗圖像(每個類別都有 12 500 張),大小為 543MB(壓縮后)。下載數(shù)據(jù)并解壓之后,你需要創(chuàng)建一個新數(shù)據(jù)集,其中包含三個子集:每個類別各 1000 個樣本的訓(xùn)練集、每個類別各 500 個樣本的驗證集和每個類別各 500 個樣本的測試集。

#將圖像復(fù)制到訓(xùn)練、驗證和測試的目錄
import os, shutil

original_dataset_dir = 'E:\\dogs_vs_cats'

base_dir = 'E:\\dogs_vs_cats_small'
os.mkdir(base_dir)

train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)

train_cats_dir = os.path.join(train_dir, 'cats')
os.mkdir(train_cats_dir)

train_dogs_dir = os.path.join(train_dir, 'dogs')
os.mkdir(train_dogs_dir)

validation_cats_dir = os.path.join(validation_dir, 'cats')
os.mkdir(validation_cats_dir)

validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.mkdir(validation_dogs_dir)

test_cats_dir = os.path.join(test_dir, 'cats')
os.mkdir(test_cats_dir)

test_dogs_dir = os.path.join(test_dir, 'dogs')
os.mkdir(test_dogs_dir)

fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_cats_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_cats_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_cats_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_dogs_dir, fname)
    shutil.copyfile(src, dst)
    
fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    shutil.copyfile(src, dst)
    
fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)

#構(gòu)建網(wǎng)絡(luò)
from keras import layers
from keras import models

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'))

#網(wǎng)絡(luò)編譯
from keras import optimizers

model.compile(loss='binary_crossentropy',
    optimizer=optimizers.RMSprop(lr=1e-4),
        metrics=['acc'])

#數(shù)據(jù)預(yù)處理
    #使用 ImageDataGenerator 讀取圖像
from keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

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')

    #利用批量生成器擬合模型
history = model.fit_generator(
    train_generator,
    steps_per_epoch=100,
    epochs=30,
    validation_data=validation_generator,
    validation_steps=50)

#保存模型
model.save('cats_and_dogs_small_1.h5')

#繪制訓(xùn)練過程中的損失曲線和精度曲線
import matplotlib.pyplot as plt

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()

4.使用數(shù)據(jù)增強(qiáng)在dogs_vs_cats小型數(shù)據(jù)集上從頭開始訓(xùn)練一個卷積神經(jīng)網(wǎng)絡(luò)

該模型讀取數(shù)據(jù)的環(huán)節(jié)與上述的3相同,故不贅述。

在神經(jīng)網(wǎng)絡(luò)中,存在幾種常見的降低過擬合的技巧,比如 dropout 和權(quán)重衰減(L2 正則化)?,F(xiàn)在我們將使用一種針對于計算機(jī)視覺領(lǐng)域的新方法,在用深度學(xué)習(xí)模型處理圖像時幾乎都會用到這種方法,它就是數(shù)據(jù)增強(qiáng)(data augmentation)。數(shù)據(jù)增強(qiáng)是從現(xiàn)有的訓(xùn)練樣本中生成更多的訓(xùn)練數(shù)據(jù),其方法是利用多種能夠生成可信圖像的隨機(jī)變換來增加(augment)樣本。其目標(biāo)是,模型在訓(xùn)練時不會兩次查看完全相同的圖像。這讓模型能夠觀察到數(shù)據(jù)的更多內(nèi)容,從而具有更好的泛化能力。

#定義一個包含 dropout 的新卷積神經(jīng)網(wǎng)絡(luò)
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.Dropout(0.5)) #使用數(shù)據(jù)增強(qiáng)并無法生成新信息,而只能混合現(xiàn)有信息。為了進(jìn)一步降低過擬合,可向模型中添加一個 Dropout 層,添加到密集連接分類器之前。
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
        optimizer=optimizers.RMSprop(lr=1e-4),
          metrics=['acc'])
        
#利用數(shù)據(jù)增強(qiáng)生成器訓(xùn)練卷積神經(jīng)網(wǎng)絡(luò)
train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,)
    
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
        train_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')
        
validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')
        
history = model.fit_generator(
        train_generator,
        steps_per_epoch=100,
        epochs=100,
        validation_data=validation_generator,
        validation_steps=50) 
     
#保存模型
model.save('cats_and_dogs_small_2.h5')

5.使用預(yù)訓(xùn)練方法在dogs_vs_cats小型數(shù)據(jù)集上從頭開始訓(xùn)練一個卷積神經(jīng)網(wǎng)絡(luò)

想要將深度學(xué)習(xí)應(yīng)用于小型圖像數(shù)據(jù)集,一種常用且非常高效的方法是使用預(yù)訓(xùn)練網(wǎng)絡(luò)。預(yù)訓(xùn)練網(wǎng)絡(luò)(pretrained network)是一個保存好的網(wǎng)絡(luò),之前已在大型數(shù)據(jù)集(通常是大規(guī)模圖像分類任務(wù))上訓(xùn)練好。如果這個原始數(shù)據(jù)集足夠大且足夠通用,那么預(yù)訓(xùn)練網(wǎng)絡(luò)學(xué)到的特征的空間層次結(jié)構(gòu)可以有效地作為視覺世界的通用模型,因此這些特征可用于各種不同的計算機(jī)視覺問題,即使這些新問題涉及的類別和原始任務(wù)完全不同。舉個例子,你在 ImageNet 上訓(xùn)練了一個網(wǎng)絡(luò)(其類別主要是動物和日常用品),然后將這個訓(xùn)練好的網(wǎng)絡(luò)應(yīng)用于某個不相干的任務(wù),比如在圖像中識別家具。這種學(xué)到的特征在不同問題之間的可移植性,是深度學(xué)習(xí)與許多早期淺層學(xué)習(xí)方法相比的重要優(yōu)勢,它使得深度學(xué)習(xí)對小數(shù)據(jù)問題非常有效。

VGG16 架構(gòu)是由 Karen Simonyan 和 Andrew Zisserman 在2014年開發(fā)的。對于ImageNet數(shù)據(jù)集,它是一種簡單而又廣泛使用的卷積神經(jīng)網(wǎng)絡(luò)架構(gòu)。

使用預(yù)訓(xùn)練網(wǎng)絡(luò)有兩種方法:特征提取(feature extraction)和微調(diào)模型(fine-tuning)。

5.1 使用特征提取的預(yù)訓(xùn)練方法

特征提取是使用之前網(wǎng)絡(luò)學(xué)到的表示來從新樣本中提取出有趣的特征。然后將這些特征輸入一個新的分類器,從頭開始訓(xùn)練。一般來說,僅重復(fù)使用卷積基,因其學(xué)到的表示可能更加通用,更適合重復(fù)使用。而密集連接層的表示不再包含物體在輸入圖像中的位置信息,其舍棄了空間的概念,而物體位置信息仍然由卷積特征圖所描述。如果物體位置對于問題很重要,那么密集連接層的特征在很大程度上是無用的。

另外,某個卷積層提取的表示的通用性(以及可復(fù)用性)取決于該層在模型中的深度。模型中更靠近底部的層提取的是局部的、高度通用的特征圖(比如視覺邊緣、顏色和紋理),而更靠近頂部的層提取的是更加抽象的概念(比如“貓耳朵”或“狗眼睛”)。 因此,如果你的新數(shù)據(jù)集與原始模型訓(xùn)練的數(shù)據(jù)集有很大差異,那么最好只使用模型的前幾層來做特征提取,而不是使用整個卷積基。

選定了預(yù)訓(xùn)練的VGG16卷積基后,接下來,下一步有兩種方法可供選擇。

(1)不使用數(shù)據(jù)增強(qiáng)的快速特征提取

在數(shù)據(jù)集上運(yùn)行卷積基,將輸出保存成硬盤中的 Numpy 數(shù)組,然后用這個數(shù)據(jù)作為輸入,輸入到獨立的密集連接分類器中。這種方法速度快,計算代價低,但不允許使用數(shù)據(jù)增強(qiáng)。

#將 VGG16 卷積基實例化
from keras.applications import VGG16

conv_base = VGG16(weights='imagenet',
        include_top=False,
        input_shape=(150, 150, 3))

#不使用數(shù)據(jù)增強(qiáng)的快速特征提取
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator

base_dir = 'E:\\dogs_vs_cats_small'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')

datagen = ImageDataGenerator(rescale=1./255)
batch_size = 20

def extract_features(directory, sample_count):
    features = np.zeros(shape=(sample_count, 4, 4, 512))
    labels = np.zeros(shape=(sample_count))
    generator = datagen.flow_from_directory(
        directory,
        target_size=(150, 150),
        batch_size=batch_size,
        class_mode='binary')
    i = 0
    for inputs_batch, labels_batch in generator:
        features_batch = conv_base.predict(inputs_batch)  #調(diào)用 conv_base 模型的 predict 方法來從這些圖像中提取特征。
        features[i * batch_size : (i + 1) * batch_size] = features_batch
        labels[i * batch_size : (i + 1) * batch_size] = labels_batch
        i += 1
        if i * batch_size >= sample_count:
            break
    return features, labels
    
train_features, train_labels = extract_features(train_dir, 2000)
validation_features, validation_labels = extract_features(validation_dir, 1000)
test_features, test_labels = extract_features(test_dir, 1000)

train_features = np.reshape(train_features, (2000, 4 * 4 * 512))
validation_features = np.reshape(validation_features, (1000, 4 * 4 * 512))
test_features = np.reshape(test_features, (1000, 4 * 4 * 512))

#定義并訓(xùn)練密集連接分類器
from keras import models
from keras import layers
from keras import optimizers

model = models.Sequential()
model.add(layers.Dense(256, activation='relu', input_dim=4 * 4 * 512))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))

model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
    loss='binary_crossentropy',
    metrics=['acc'])
    
history = model.fit(train_features, train_labels,
    epochs=30,
    batch_size=20,
    validation_data=(validation_features, validation_labels))#訓(xùn)練速度非常快,因為只需處理兩個 Dense 層。即使在 CPU 上運(yùn)行,每輪的時間也不到一秒鐘。
    
#繪制結(jié)果
import matplotlib.pyplot as plt

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()
(2)使用數(shù)據(jù)增強(qiáng)的快速特征提取

在頂部添加 Dense 層來擴(kuò)展已有模型(即 conv_base),并在輸入數(shù)據(jù)上端到端地運(yùn)行整個模型。這樣可以使用數(shù)據(jù)增強(qiáng),因為每個輸入圖像進(jìn)入模型時都會經(jīng)過卷積基。但出于同樣的原因,這種方法的計算代價比不使用數(shù)據(jù)增強(qiáng)的要高很多。

該方法計算代價很高,只在有 GPU 的情況下才能嘗試運(yùn)行,在 CPU 上是絕對難以運(yùn)行的。如果無法在 GPU 上運(yùn)行代碼,那么就采用第一種方法。

在編譯和訓(xùn)練模型之前,一定要“凍結(jié)”卷積基。 凍結(jié)(freeze)一個或多個層是指在訓(xùn)練過程中保持其權(quán)重不變。如果不這么做,那么卷積基之前學(xué)到的表示將會在訓(xùn)練過程中被修改。因為其上添加的 Dense 層是隨機(jī)初始化的,所以非常大的權(quán)重更新將會在網(wǎng)絡(luò)中傳播,對之前學(xué)到的表示造成很大破壞。

#將 VGG16 卷積基實例化
from keras.applications import VGG16

conv_base = VGG16(weights='imagenet',
        include_top=False,
        input_shape=(150, 150, 3))
        
#在卷積基上添加一個密集連接分類器
from keras import models
from keras import layers

model = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))        

conv_base.trainable = False #凍結(jié)網(wǎng)絡(luò)
        
#利用凍結(jié)的卷積基端到端地訓(xùn)練模型
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers

train_datagen = ImageDataGenerator(
    rescale=1./255,
    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')
    
test_datagen = ImageDataGenerator(rescale=1./255)  #注意,不能增強(qiáng)驗證數(shù)據(jù)

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')
    
model.compile(loss='binary_crossentropy',
    optimizer=optimizers.RMSprop(lr=2e-5),
    metrics=['acc'])
    
history = model.fit_generator(
    train_generator,
    steps_per_epoch=100,
    epochs=30,
    validation_data=validation_generator,
    validation_steps=50)
5.2 使用模型微調(diào)的預(yù)訓(xùn)練方法

對于用于特征提取的凍結(jié)的模型基,微調(diào)是指將其頂部的幾層“解凍”,并將這解凍的幾層和新增加的部分(本例中是全連接分類器)聯(lián)合訓(xùn)練。下圖中,block5_conv1、 block5_conv2 和 block5_conv3 三層是可訓(xùn)練的。

微調(diào) VGG16 網(wǎng)絡(luò)的最后一個卷積塊.png

為何通常微調(diào)最后的幾個卷積層?
——卷積基中更靠底部的層編碼的是更加通用的可復(fù)用特征,而更靠頂部的層編碼的是更專業(yè)化的特征。微調(diào)這些更專業(yè)化的特征更加有用,因為它們需要在你的新問題上改變用途。微調(diào)更靠底部的層,得到的回報會更少。

#將 VGG16 卷積基實例化
from keras.applications import VGG16

conv_base = VGG16(weights='imagenet',
        include_top=False,
        input_shape=(150, 150, 3))

#不使用數(shù)據(jù)增強(qiáng)的快速特征提取
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator

base_dir = 'E:\\dogs_vs_cats_small'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')

datagen = ImageDataGenerator(rescale=1./255)
batch_size = 20

def extract_features(directory, sample_count):
    features = np.zeros(shape=(sample_count, 4, 4, 512))
    labels = np.zeros(shape=(sample_count))
    generator = datagen.flow_from_directory(
        directory,
        target_size=(150, 150),
        batch_size=batch_size,
        class_mode='binary')
    i = 0
    for inputs_batch, labels_batch in generator:
        features_batch = conv_base.predict(inputs_batch)
        features[i * batch_size : (i + 1) * batch_size] = features_batch
        labels[i * batch_size : (i + 1) * batch_size] = labels_batch
        i += 1
        if i * batch_size >= sample_count:
            break
    return features, labels
    
train_features, train_labels = extract_features(train_dir, 2000)
validation_features, validation_labels = extract_features(validation_dir, 1000)
test_features, test_labels = extract_features(test_dir, 1000)

train_features = np.reshape(train_features, (2000, 4 * 4 * 512))
validation_features = np.reshape(validation_features, (1000, 4 * 4 * 512))
test_features = np.reshape(test_features, (1000, 4 * 4 * 512))

#凍結(jié)直到某一層的所有層
conv_base.trainable = True

set_trainable = False
for layer in conv_base.layers:
    if layer.name == 'block5_conv1':
        set_trainable = True
    if set_trainable:
        layer.trainable = True
    else:
        layer.trainable = False
        
#微調(diào)模型
model.compile(loss='binary_crossentropy',
    optimizer=optimizers.RMSprop(lr=1e-5),
    metrics=['acc'])

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

#在測試數(shù)據(jù)上最終評估這個模型   
test_generator = test_datagen.flow_from_directory(
        test_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='binary')
test_loss, test_acc = model.evaluate_generator(test_generator, steps=50)
print('test acc:', test_acc)

6.可視化卷積神經(jīng)網(wǎng)絡(luò)

未完待續(xù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容