1慨灭、卷積神經(jīng)網(wǎng)絡(luò)
一個簡單的小例子??
#實例化一個小型的卷積神經(jīng)網(wǎng)絡(luò)
# Conv2D 層和 MaxPooling2D 層的堆疊
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))) 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'))
將最后的輸出張量[大小為 (3, 3, 64)]輸入到一個密集連接分類器網(wǎng)絡(luò)中溉浙, 即 Dense 層的堆疊。
#在卷積神經(jīng)網(wǎng)絡(luò)上添加分類器
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
在MNIST圖像上訓(xùn)練卷積神經(jīng)網(wǎng)絡(luò)
from keras.datasets import mnist
from keras.utils import to_categorical
(train_images, train_labels), (test_images, test_labels) = mnist.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'])
model.fit(train_images, train_labels, epochs=5, batch_size=64)
2、卷積運算
密集連接層和卷積層的根本區(qū)別在于痹仙,Dense 層從輸入特征空間中學(xué)到的是全局模式是尔,而卷積層學(xué)到的是局部模式,對于圖像來說开仰,學(xué)到的就是在輸入圖像的二維小窗口中發(fā)現(xiàn)的模式拟枚。
卷積神經(jīng)網(wǎng)絡(luò)的兩個性質(zhì):
a 卷積神經(jīng)網(wǎng)絡(luò)學(xué)到的模式具有平移不變性
b 卷積神經(jīng)網(wǎng)絡(luò)可以學(xué)到模式的空間層次結(jié)構(gòu)
卷積工作原理:
在 3D 輸入特征圖上滑動(slide)這些 3×3 或 5×5 的窗口,在每個可能 的位置停止并提取周圍特征的 3D 圖塊[形狀為 (window_height, window_width, input_ depth)]众弓。然后每個 3D 圖塊與學(xué)到的同一個權(quán)重矩陣[叫作卷積核(convolution kernel)]做 2 張量積恩溅,轉(zhuǎn)換成形狀為 (output_depth,) 的 1D 向量。然后對所有這些向量進行空間重組田轧, 使其轉(zhuǎn)換為形狀為 (height, width, output_depth) 的 3D 輸出特征圖暴匠。輸出特征圖中的 每個空間位置都對應(yīng)于輸入特征圖中的相同位置(比如輸出的右下角包含了輸入右下角的信 息)。
3傻粘、最大池化運算
最大池化的作用:對特征圖進行下采樣,與步進卷積類似帮掉。
最大池化是從輸入特征圖中提取窗口弦悉,并輸出每個通道的最大值。它的概念與卷積類似蟆炊,但是最大池化使用硬編碼的 max 張量運算對局部圖塊進行變換稽莉,而不是使用學(xué)到的線性變換(卷 積核)。最大池化與卷積的最大不同之處在于涩搓,最大池化通常使用 2×2 的窗口和步幅 2污秆,其目 的是將特征圖下采樣 2 倍劈猪。與此相對的是,卷積通常使用 3×3 窗口和步幅 1良拼。
4战得、在小型數(shù)據(jù)集上訓(xùn)練一個神經(jīng)網(wǎng)絡(luò)
貓狗分類數(shù)據(jù)集
將圖像復(fù)制到訓(xùn)練、驗證和測試的目錄
import os, shutil
#數(shù)據(jù)目錄
original_dataset_dir ="/Users/***/Desktop/learning_log/net_work/kaggle/train"
#小數(shù)據(jù)集目錄
base_dir = '/Users/fanhua/Desktop/learning_log/net_work/kaggle/cats_and_dogs_small'
os.mkdir(base_dir)
#訓(xùn)練目錄
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)
#貓的訓(xùn)練圖像目錄
train_cats_dir = os.path.join(train_dir, 'cats')
os.mkdir(train_cats_dir)
#狗的訓(xùn)練圖像目錄
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)
#將前 1000 張貓的圖像復(fù)制 到 train_cats_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)
#將接下來 500 張貓的圖像復(fù) 制到 validation_cats_dir
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)
#將接下來的 500 張貓的圖像 復(fù)制到 test_cats_dir
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)
#將前 1000 張狗的圖像復(fù)制 到 train_dogs_dir
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)
#將接下來 500 張狗的圖像復(fù) 制到 validation_dogs_dir
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)
#將接下來 500 張狗的圖像復(fù) 制到 test_dogs_dir
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(validation_dogs_dir, fname)
shutil.copyfile(src, dst)
構(gòu)建網(wǎng)絡(luò)
卷積神經(jīng)網(wǎng)絡(luò)由 Conv2D 層(使用 relu 激活)和 MaxPooling2D 層交替堆疊構(gòu)成庸推。
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'))
配置模型用于訓(xùn)練
from keras import optimizers
model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
數(shù)據(jù)預(yù)處理
(1) 讀取圖像文件
(2) 將 JPEG 文件解碼為 RGB 像素網(wǎng)格
(3) 將這些像素網(wǎng)格轉(zhuǎn)換為浮點數(shù)張量
(4) 將像素值(0~255 范圍內(nèi))縮放到 [0, 1] 區(qū)間
使用 ImageDataGenerator 從目錄中讀取圖像
from keras.preprocessing.image import ImageDataGenerator
#將所有圖像乘以 1/255 縮放
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), #將所有圖像的大小調(diào)整為 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()
使用數(shù)據(jù)增強
過擬合是因為樣本少(2000個)贬媒,數(shù)據(jù)增強是從現(xiàn)有的樣本中生成更多的訓(xùn)練數(shù)據(jù)聋亡,其方法是利用多種能夠生成可信圖像的隨機變換來增加(augment)樣本。其目標是际乘,模型在訓(xùn)練時不會兩次查看完全相同的圖像坡倔。這讓模型能夠觀察 到數(shù)據(jù)的更多內(nèi)容,從而具有更好的泛化能力脖含。
利用 ImageDataGenerator 來設(shè)置數(shù)據(jù)增強
datagen = ImageDataGenerator(rotation_range=40,#圖像隨機旋轉(zhuǎn)角度范圍
width_shift_range=0.2, #width_shift 和 height_shift 是圖像在水平或垂直方向上平移的范圍(相對于總寬
度或總高度的比例)罪塔。
height_shift_range=0.2,
shear_range=0.2, #shear_range 是隨機錯切變換的角度。
zoom_range=0.2, #shear_range 是隨機錯切變換的角度器赞。
horizontal_flip=True, #horizontal_flip 是隨機將一半圖像水平翻轉(zhuǎn)垢袱。如果沒有水平不對稱的假設(shè)(比如真
實世界的圖像),這種做法是有意義的港柜。
fill_mode='nearest')#fill_mode 是用于填充新創(chuàng)建像素的方法请契,這些新像素可能來自于旋轉(zhuǎn)或?qū)挾?/ 高度平移。 我們來看一下增強后的圖像
顯示幾個隨機增強后的訓(xùn)練圖像
from keras.preprocessing import image#圖像預(yù)處理模塊
fnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]
img_path = fnames[3]#選一張圖像進行增強
img = image.load_img(img_path, target_size=(150, 150))#讀取圖像并調(diào)整大小
x = image.img_to_array(img)#將其轉(zhuǎn)換為形狀 (150, 150, 3) 的 Numpy 數(shù)組
x = x.reshape((1,) + x.shape)#將其形狀改變?yōu)?(1, 150, 150, 3)
i=0
#生成隨機變換后的圖像批量夏醉。 循環(huán)是無限的爽锥,因此需要在某個時刻終止循環(huán)
for batch in datagen.flow(x, batch_size=1):
plt.figure(i)
imgplot = plt.imshow(image.array_to_img(batch[0]))
i += 1
if i % 4 == 0:
break
plt.show()
網(wǎng)絡(luò)看到的輸入是高度相關(guān)的浪谴,因為這些輸入都來自于少量的原始圖像苏揣。無法生成新信息,而只能混合現(xiàn)有信息撼短。為了進一步降低過擬合靶擦,還需要向模型中添加一個 Dropout 層腮考,添加到密集連接分類器之前。
定義一個包含 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))
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ù)增強生成器訓(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)#注意玄捕,不能增強驗證數(shù)據(jù)
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)練的卷積神經(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)練好。
特征提取
用于圖像分類的卷積神經(jīng)網(wǎng)絡(luò)包含兩部分:首先是一系列池化層和卷積層,最后是一個密集連接分類器福也。第一部分叫作模型的卷積基(convolutional base)局骤。對于卷積神經(jīng)網(wǎng) 絡(luò)而言,特征提取就是取出之前訓(xùn)練好的網(wǎng)絡(luò)的卷積基暴凑,在上面運行新數(shù)據(jù)峦甩,然后在輸出上面訓(xùn)練一個新的分類器。
使用在 ImageNet 上訓(xùn)練的 VGG16 網(wǎng)絡(luò)的卷積基從 貓狗圖像中提取有趣的特征搬设,然后在這些特征上訓(xùn)練一個貓狗分類器穴店。
#將 VGG16 卷積基實例化
from keras.applications import VGG16
conv_base = VGG16(weights='imagenet', include_top=False,input_shape=(150, 150, 3))
conv_base.summary()
- 不使用數(shù)據(jù)增強的快速特征提取
速度快,計算代價低
使用預(yù)訓(xùn)練的卷積基提取特征
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
base_dir = '/Users/fanhua/Desktop/learning_log/net_work/kaggle/cats_and_dogs_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)
#要將其輸入到密集連接分類器中拿穴, 所以首先必須將其形狀展平為 (samples, 8192)泣洞。
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))
繪制結(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ù)增強的特征提取
速度慢默色、計算代價高
在卷積基上添加一個密集連接分類器
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'))
model.summary()
在編譯和訓(xùn)練模型之前球凰,一定要“凍結(jié)”卷積基。凍結(jié)(freeze)一個或多個層是指在訓(xùn)練過程中保持其權(quán)重不變腿宰。如果不這么做呕诉,那么卷積基之前學(xué)到的表示將會在訓(xùn)練過程中被修改。 因為其上添加的 Dense 層是隨機初始化的吃度,所以非常大的權(quán)重更新將會在網(wǎng)絡(luò)中傳播甩挫,對之前學(xué)到的表示造成很大破壞。
print('This is the number of trainable weights before freezing the conv base:', len(model.trainable_weights))
conv_base.trainable = False
print('This is the number of trainable weights after freezing the conv base:', len(model.trainable_weights))
利用凍結(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)
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)
模型微調(diào)(fine-tuning)
另一種廣泛使用的模型復(fù)用方法是模型微調(diào)椿每,與特征提取互為補充伊者。
微調(diào)網(wǎng)絡(luò)步驟:
(1) 在已經(jīng)訓(xùn)練好的基網(wǎng)絡(luò)(base network)上添加自定義網(wǎng)絡(luò)。
(2) 凍結(jié)基網(wǎng)絡(luò)间护。
(3) 訓(xùn)練所添加的部分亦渗。
(4) 解凍基網(wǎng)絡(luò)的一些層。
(5) 聯(lián)合訓(xùn)練解凍的這些層和添加的部分
卷積基架構(gòu):
conv_base.summary()
凍結(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)
繪制結(jié)果
使曲線平滑
def smooth_curve(points, factor=0.8):
smoothed_points = []
for point in points:
if smoothed_points:
previous = smoothed_points[-1]
smoothed_points.append(previous * factor + point * (1 - factor))
else:
smoothed_points.append(point)
return smoothed_points
plt.plot(epochs,smooth_curve(acc), 'bo', label='Smoothed training acc')
plt.plot(epochs,smooth_curve(val_acc), 'b', label='Smoothed validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs,
smooth_curve(loss), 'bo', label='Smoothed training loss')
plt.plot(epochs,smooth_curve(val_loss), 'b', label='Smoothed validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
卷積神經(jīng)網(wǎng)絡(luò)的可視化
可視化卷積神經(jīng)網(wǎng)絡(luò)的中間輸出(中間激活)
可視化中間激活汁尺,是指對于給定輸入法精,展示網(wǎng)絡(luò)中各個卷積層和池化層輸出的特征圖(層 的輸出通常被稱為該層的激活,即激活函數(shù)的輸出)痴突。
#加載模型
from keras.models import load_model 4
model = load_model('cats_and_dogs_small_2.h5')
model.summary()
#預(yù)處理單張圖像
可視化卷積神經(jīng)網(wǎng)絡(luò)的過濾器
可視化圖像中類激活的熱力圖