TensorFlow Specialization Course 2 Week 1&2&3
Week1主要讲了在数据集比较小的情况下很容易过拟合的问题。
Week2给出解决过拟合问题的一种解决方法:数据增强。
Week3主要讲迁移学习,同时使用数据增强,另外介绍了另一种解决过拟合问题的方法:Dropout。
每一周的内容都比较少,所以放在一起总结。
我们下面使用前三周所讲的知识(迁移学习+数据增强+Dropout)来完成Kaggle比赛的狗猫分类。
下面代码全部运行在Colab上。
导入必要的包
import os
import zipfile
import random
import tensorflow as tf
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from shutil import copyfile
下载数据集
!wget --no-check-certificate \
"https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_3367a.zip" \
-O "/tmp/cats-and-dogs.zip"
local_zip = '/tmp/cats-and-dogs.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall('/tmp')
zip_ref.close()
print(len(os.listdir('/tmp/PetImages/Cat/')))
print(len(os.listdir('/tmp/PetImages/Dog/')))
# Expected Output:
# 12501
# 12501
try:
os.mkdir('/tmp/cats-v-dogs')
os.mkdir('/tmp/cats-v-dogs/training')
os.mkdir('/tmp/cats-v-dogs/testing')
os.mkdir('/tmp/cats-v-dogs/training/cats')
os.mkdir('/tmp/cats-v-dogs/training/dogs')
os.mkdir('/tmp/cats-v-dogs/testing/cats')
os.mkdir('/tmp/cats-v-dogs/testing/dogs')
except OSError:
pass
创建数据集目录,下一步将图像复制到对应文件夹中。需要注意的是,公开数据集中的数据并不是完美的,例如本次使用的数据集中存在文件大小为空的图像,需要把他们去除。
def split_data(SOURCE, TRAINING, TESTING, SPLIT_SIZE):
files = []
for filename in os.listdir(SOURCE):
file = SOURCE + filename
if os.path.getsize(file) > 0:
files.append(filename)
else:
print(filename + " is zero length, so ignoring.")
training_length = int(len(files) * SPLIT_SIZE)
testing_length = int(len(files) - training_length)
shuffled_set = random.sample(files, len(files))
training_set = shuffled_set[0:training_length]
testing_set = shuffled_set[-testing_length:]
for filename in training_set:
this_file = SOURCE + filename
destination = TRAINING + filename
copyfile(this_file, destination)
for filename in testing_set:
this_file = SOURCE + filename
destination = TESTING + filename
copyfile(this_file, destination)
CAT_SOURCE_DIR = "/tmp/PetImages/Cat/"
TRAINING_CATS_DIR = "/tmp/cats-v-dogs/training/cats/"
TESTING_CATS_DIR = "/tmp/cats-v-dogs/testing/cats/"
DOG_SOURCE_DIR = "/tmp/PetImages/Dog/"
TRAINING_DOGS_DIR = "/tmp/cats-v-dogs/training/dogs/"
TESTING_DOGS_DIR = "/tmp/cats-v-dogs/testing/dogs/"
split_size = .9
split_data(CAT_SOURCE_DIR, TRAINING_CATS_DIR, TESTING_CATS_DIR, split_size)
split_data(DOG_SOURCE_DIR, TRAINING_DOGS_DIR, TESTING_DOGS_DIR, split_size)
# Expected output
# 666.jpg is zero length, so ignoring
# 11702.jpg is zero length, so ignoring
使用迁移学习。
from tensorflow.keras import layers
from tensorflow.keras import Model
from tensorflow.keras.optimizers import RMSprop
!wget --no-check-certificate \
https://storage.googleapis.com/mledu-datasets/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5 \
-O /tmp/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5
from tensorflow.keras.applications.inception_v3 import InceptionV3
local_weights_file = '/tmp/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5'
pre_trained_model = InceptionV3(input_shape=(150, 150, 3),
input_top=False, # False则表示只使用卷积层,去掉InceptionV3的全连接层
weights=None) # None为随机初始化 如选'imagenet'表示在ImageNet上预训练的权重
# 载入我们下载好的预训练权重
pre_trained_model.load_weights(local_weights_file)
# 先让所有的卷积层冻结(参数不可训练),因为前面的卷积层的参数已经训练好,我们不希望他们变化
for layer in pre_trained_model.layers:
layer.trainable = False
# pre_trained_model.summary()
# 可能最后几层卷积层学习检测的特征也比较专门化,我们可以多弃几层。比如下面我们直接使用mixed7层的输出接我们的全连接层
last_layer = pre_trained_model.get_layer('mixed7')
print('last layer output shape: ', last_layer.output_shape)
last_output = last_layer.output
x = layers.Flatten()(last_output)
x = layers.Dense(1024, activation='relu')(x)
# 使用Dropout来防止过拟合。Dropout作用于上面的的层。
x = layers.Dropout(0.2)(x)
x = layers.Dense(1, activation='sigmoid')(x)
# Model这里接受两个参数 模型的输入和输出
model = Model(pre_trained_model.input, x)
model.compile(optimizer=RMSprop(lr=0.0001), loss='binary_crossentropy', metrics=['acc'])
和之前一样使用ImageDataGenerator
训练数据预处理,不同的是,我们在训练集上使用数据增强来扩充数据集从而防止过拟合。在ImageDataGenerator
方法中添加数据增强的参数。
TRAINING_DIR = "/tmp/cats-v-dogs/training/"
# train_datagen = ImageDataGenerator(rescale=1./255.)
# 数据增强
train_datagen = ImageDataGenerator(
rescale=1./255.,
# 随机旋转图像,角度取值范围(0-180),
rotation_range=40,
# 上下或左右平移的范围,0.2为图像大小的20%。
width_shift_range=0.2,
height_shift_range=0.2,
# 水平或垂直投影变换
shear_range=0.2,
# Randomly zooming inside pictures.
zoom_range=0.2,
# 水平翻转
horizontal_flip=True,
# 用于填充旋转或水平/垂直移动后填补像素 (什么时候用?什么时候不用?)
fill_mode='nearest')
train_generator = train_datagen.flow_from_directory(
TRAINING_DIR,
batch_size=100,
class_mode='binary',
target_size=(150, 150))
# 数据增强需要根据数据集特点来调整
# 例如之前的人-马数据集中 人和马都是直立着的。如果使用了图像旋转的方法,则验证集的准确率比较低且波动很大。因为验证集中并没有平躺的人或马。
VALIDATION_DIR = "/tmp/cats-v-dogs/testing/"
validation_datagen = ImageDataGenerator(rescale=1.0/255.)
validation_generator = validation_datagen.flow_from_directory(VALIDATION_DIR,
batch_size=100,
class_mode='binary',
target_size=(150, 150))
# Expected Output:
# Found 22498 images belonging to 2 classes.
# Found 2500 images belonging to 2 classes.
开始训练,将训练的数据存入history。
history = model.fit_generator(train_generator,
epochs=50,
verbose=1,
validation_data=validation_generator)
可视化训练过程中的loss和accuracy变化。
%matplotlib inline
import matplotlib.image as mpimg
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(len(acc)) # Get number of epochs
# 展示每一次迭代训练集和验证集的accuracy
plt.plot(epochs, acc, 'r', "Training Accuracy")
plt.plot(epochs, val_acc, 'b', "Validation Accuracy")
plt.title('Training and validation accuracy')
plt.figure()
# 展示每一次迭代训练集和验证集的loss
plt.plot(epochs, loss, 'r', "Training Loss")
plt.plot(epochs, val_loss, 'b', "Validation Loss")
plt.figure()
Kaggle链接:https://www.kaggle.com/c/dogs-vs-cats
参考:
<https://github.com/lmoroney/dlaicourse/blob/master/Course 2 - Part 6 - Lesson 3 - Notebook.ipynb