深度学习_CNN _图像数据分类_transfer learning实例
Transfer_learning背景知识介绍
- 介绍
简单地说,转移学习是“利用通过对一项任务进行训练获得的神经网络架构与属性应用于另一项任务。”
根据应用程序的不同,可以采用不同的策略来执行迁移学习。
例如,如果目标是构建一个图像分类应用程序,其中要分类的目标图像与已建立的数据集中的图像相似。我们就可以在这些数据集上使用预先训练过的神经网络模型进行训练,而不是从头开始。
在keras中我们可以用许多它提供的已经训练好CNN模型例如:
- VGG16,
- VGG19
- Inception V3
- ResNet50
- MobileNet
这些模型具体的准确率如下图所示我们可以去keras官网了解更详细的参数:
2.策略
迁移学习主要有两种方式:
1)特征提取(feature extractor)也称冻结(frezzing):
导入已经训练好的模型的权重与训练参数并冻结
(冻结指参数保持不变,不参加训练),只根据此次要训练的数据class类别数更改最后全连接网络输出层的参数。
2) 调整(fine tune):
微调意味着允许weight的更新。只冻结模型最开始对general 特征的训练权重以及参数,根据情况调节后面层的参数并进行适当的更改。这样的好处是这些层现在将用预先训练好的神经网络模型的权值进行初始化,而不是从一些随机初始化器/零的权值开始。这很重要,因为正确的权值集会影响收敛,而如果从零开始训练网络,则不会出现这种情况。
- 应用情况
所以什么时候用特征提取,什么时候用微调的方法呢?针对新的待训练的数据集的不同一般有以下几种情况:
1)当新的数据集比较小且和原数据集相似时采用特征提取的方法。因为新数据集比较小(比如<5000),如果fine-tune可能会过拟合;又因为新旧数据集类似,我们期望他们高层特征类似,可以使用预训练网络当做特征提取器,用提取的特征训练线性分类器。
2)新数据集大且和原数据集相似时采用调整的方法。因为新数据集足够大(比如>10000),可以fine-tune整个网络。
3)新数据集小且和原数据集不相似。新数据集小,最好不要fine-tune,和原数据集不类似,最好也不使用高层特征。这时可使用前面层的特征来训练SVM分类器。
4)新数据集大且和原数据集不相似。因为新数据集足够大,可以重新训练。但是实践中fine-tune预训练模型还是有益的。新数据集足够大,可以fine-tine整个网络。
项目介绍
利用transfer_learning技术对给定的四类图像数据集合进行分类识别训练,并进行调优。
数据集合
图像数据目录已经处理和分类好四类类图像数据(cat,dog,car,motorbike)
数据集来自Kaggle: https://www.kaggle.com/c/dogs-vs-cats/data.
图像数据分类处理
图像数据的分类处理示例代码如下。因为数据都存在一个文件夹下,为了适应keras的模型训练需要的数据格式,需要将图像按照类别分别存储在对应的子文件夹中。
示例代码完成的内容:
选取Kaggle数据集合的2000张猫和狗的图片,1000 张用来训练,500张用来验证,剩下500张用来测试。因为在接下来的图像处理中我们需要采用keras的ImageData Generator 来实时分批处理图像数据。而ImageData Generator对输入的数据格式有要求。所以现在我们就要先把图像整理成要求的格式,即将训练,测试,验证数据集分成k个子目录,k是图像的类别数。每个子目录下应该根据类别存放对应的图片。
# we assume the filenames of all images with cats (resp.dogs) are cat.i.jpg, where i is an integer.
import os, shutil
#创建好分类目录
base_dir = '../dogs-vs-cats'
original_dir='../dogs-vs-cats/trainAll'
train_dir = os.path.join(base_dir, 'trainS')
val_dir = os.path.join(base_dir, 'valS')
test_dir = os.path.join(base_dir, 'testS')
train_cats_dir=os.path.join(train_dir, 'cats')
train_dogs_dir=os.path.join(train_dir, 'dogs')
test_cats_dir=os.path.join(test_dir, 'cats')
test_dogs_dir=os.path.join(test_dir, 'dogs')
val_cats_dir=os.path.join(val_dir, 'cats')
val_dogs_dir=os.path.join(val_dir, 'dogs')
#the following script copy the first 1000 cat images to the training directoy.
fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
src = os.path.join(original_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_dir, fname)
dst = os.path.join(test_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_dir, fname)
dst = os.path.join(val_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_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_dir, fname)
dst = os.path.join(test_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_dir, fname)
dst = os.path.join(val_dogs_dir, fname)
shutil.copyfile(src, dst)
训练框架
实现神经网络的训练流程如下:
- 图像数据的预处理
- weight初始化
- 决定层数以及网络架构
- 健全性检查
- 观察训练结果以调整
数据预处理
图像预处理需完成以下几个任务:
- 将jpg/png图像数据转成tensor格式
- 统一调整图像的大小(rescale),数据的标准化。
- 图像数据的 zero-centering 即归一化。计算图像数据均值后对每个数据减去均值使得输入数据的均值为0,这样有利于训练时候梯度的快速收敛。
这里我们采用ImageData Generator来实现
具体代码如下:
# train_dir="../sample1000/train"
# validation_dir="../sample1000/val"
from keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(rescale=1./255) #rescale the tensor values to [0,1] rescale将 每个数据乘参数
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') #we only have two classes
validation_generator = test_datagen.flow_from_directory(
val_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
这里需要理解的几点
1.图像数据的格式
一张图片由像素组成,每个像素值的大小在[0,255],彩色图像有三个图像通道(RGB)对图像数据格式的讲解可看这个链接图像数据格式解释
2.generator
可以理解成一个将数据按照一定大小(batch size)一批批循环输入进行相应的处理然后再一批批输出的生成器。这里我们每批次从对应的目录里读入20个数据,并将每个图像转换成(height,width)=(150,150)的图像尺寸。class_mode选择binary 因为我们的数据只要两个类别分别是猫和狗。如果是多个类别,选择categorical,详细的 keras 的ImageDataGenerator可参考keras imagedatagenerator
训练一个简单的CNN
我们首先自己构建一个cnn的架构进行简单的训练以和后面用模型trasfer learning形成对比。
- 构建网络
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))) #we need to specify the size of images, 150 x 150 in our case
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')) #alternatively we could use the softmax activation function, in which case you should change the learning rate
cnn的神经网络层激活函数一般用relu的效果会更好,和sigmoid以及tanh相比,在正值没有梯度消失现象并且收敛的更快,在CNN中广泛使用。
- 配置模型训练参数。
from keras import optimizers
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
这里的lr是指learning rate。优化器我们选择RMSprop。损失函数采用二进制的交叉熵。交叉熵函数的理解可参考简单的交叉熵损失函数,你真的懂了吗?
- 训练并保存模型
history = model.fit_generator(
train_generator,
steps_per_epoch=100,#一次epoch 处理100个sample
epochs=30,#对于同一组数据循环处理30次epoch
validation_data=validation_generator,#验证集合
validation_steps=50)#,每次epoch之后验证一次拿出50个sample验证
#saving the model
model.save('cats_and_dogs_small_1.h5')
- 画出accuracy以及loss
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()
最后的图象示意
观察图像,我们从训练和验证的准确性之间的巨大差距和验证的损失开始增加可以看出是明显的过拟合现象。这是因为我们有少量的数据,容易过度拟合!
针对过拟合现象,我们可以采用图像增强的手段以及增加dropout层的方式改造原来的结构,这里就不详细叙述了,我们主要叙述transfer_learning的模型。图像增强和加了dropout层的代码以及效果如图。