使用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)最后的幾個卷積層?
——卷積基中更靠底部的層編碼的是更加通用的可復(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ù)