密集连接层和卷积层的根本区别在于,Dense层,从输入特征空间中学到的是全局模式。(比如对于MNIST数字,全局模式就是涉及所有像素的模式),而卷积学到的是局部模式,这让卷积具备了两个性质。
猫狗分类的数据集不包含在Keras中,它是由2013年末公开并作为一项计算机视觉竞赛的一部分,当时卷积神经网络还不是主流算法。
数据集下载地址
https://www.kaggle.com/c/dogs-vs-cats/data
创建数据集,验证集,和测试集。并且在每个目录下分别创建猫和狗的图片保存路径。
import os, shutil
original_dataset_dir ='D:/DownloadEI/train'
base_dir = 'D:/严少青毕设/CNN-BiLSTM/cnn-bilstm/base_data'
if not os.path.exists(base_dir):
os.makedirs(base_dir)
train_dir = os.path.join(base_dir,'train')
if not os.path.exists(train_dir):
os.makedirs(train_dir)
validation_dir = os.path.join(base_dir,'validation')
if not os.path.exists(validation_dir):
os.makedirs(validation_dir)
test_dir = os.path.join(base_dir,'test')
if not os.path.exists(test_dir):
os.makedirs(test_dir)
train_cats_dir = os.path.join(train_dir,'cat')
if not os.path.exists(train_cats_dir):
os.makedirs(train_cats_dir)
train_dogs_dir = os.path.join(train_dir,'dog')
if not os.path.exists(train_dogs_dir):
os.makedirs(train_dogs_dir)
validation_cats_dir = os.path.join(validation_dir,'cat')
if not os.path.exists(validation_cats_dir):
os.makedirs(validation_cats_dir)
validation_dogs_dir = os.path.join(validation_dir,'dog')
if not os.path.exists(validation_dogs_dir):
os.makedirs(validation_dogs_dir)
test_cats_dir = os.path.join(test_dir,'cat')
if not os.path.exists(test_cats_dir):
os.makedirs(test_cats_dir)
test_dogs_dir = os.path.join(test_dir,'dog')
if not os.path.exists(test_dogs_dir):
os.makedirs(test_dogs_dir)
将下载数据集导入新建的文件目录下:
frames = ['cat.{}.jpg'.format(i) for i in range(1500,2000)]
for frame in frames:
src = os.path.join(original_dataset_dir,frame)
dst = os.path.join(test_cats_dir,frame)
shutil.copyfile(src,dst)
frames = ['dog.{}.jpg'.format(i) for i in range(1000)]
for frame in frames:
src = os.path.join(original_dataset_dir,frame)
dst = os.path.join(train_dogs_dir,frame)
shutil.copyfile(src,dst)
frames = ['dog.{}.jpg'.format(i) for i in range(1000,1500)]
for frame in frames:
src = os.path.join(original_dataset_dir,frame)
dst = os.path.join(validation_dogs_dir,frame)
shutil.copyfile(src,dst)
frames = ['dog.{}.jpg'.format(i) for i in range(1500,2000)]
for frame in frames:
src = os.path.join(original_dataset_dir,frame)
dst = os.path.join(test_dogs_dir,frame)
shutil.copyfile(src,dst)
在MINIST识别数字这篇博客中,构建了一个小型卷积神经网络。这里复用整体的结构,即卷积神经网络使用Conv2层(使用relu进行激活)和MaxPooling2D层交替堆叠构成。
但是这里处理的是更大的图像,需要相应的增加网络,即再增加一个Conv2D+MaxPooling2D的组合,这可以增加网络尺寸,降低特征图大小,使其连接Flatten时候尺寸不会太大。本例中输入尺寸是150*150,最后连接Flatten层大小为7*7。
因为这个问题是二分类问题,网络最后一层使用sigmoid层进行激活。
from keras import models
from keras import layers
import os
network = models.Sequential()
network.add(layers.Conv2D(32,(3,3),activation='relu',input_shape=(150,150,3)))
network.add(layers.MaxPool2D(2,2))
network.add(layers.Conv2D(64,(3,3),activation='relu'))
network.add(layers.MaxPool2D(2,2))
network.add(layers.Conv2D(128,(3,3),activation='relu'))
network.add(layers.MaxPool2D(2,2))
network.add(layers.Conv2D(128,(3,3),activation='relu'))
network.add(layers.MaxPool2D(2,2))
network.add(layers.Flatten())
network.add(layers.Dropout(0.5))
network.add(layers.Dense(512,activation='relu'))
network.add(layers.Dense(1,activation='sigmoid'))
定义优化器和损失函数
from keras import optimizers
network.compile(optimizer=optimizers.RMSprop(learning_rate=1e-4),
loss= 'binary_crossentropy',
metrics='accuracy')
数据预处理的步骤如下:
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(
'D:/严少青毕设/CNN-BiLSTM/cnn-bilstm/base_data/train',
target_size=(150,150),
batch_size=20,
class_mode='binary'
)
validation_generator = test_datagen.flow_from_directory(
'D:/严少青毕设/CNN-BiLSTM/cnn-bilstm/base_data/validation',
target_size=(150,150),
batch_size=20,
class_mode='binary'
)
它生成了150x150的RGB图像[形状为(20,150,150,3)]与二进制标签形状为【20,】组成的批量,生成器会不断生成这些批量,会不断循环目标文件夹的图像。
利用生成器我们可以让模型进行拟合,使用fit_generator方法进行拟合,它在数据生成器上效果与fit相同。它的第一个参数是Python生成器,可以不断的生成输入和目标批量。因为数据是不断生成的,每轮提取多少样本,这是steps_per_epoch参数的作用;运行了steps_per_epoch个梯度下降后,拟合进入下一轮次。验证集也是同理。
history = network.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=100,
validation_data=validation_generator,
validation_steps=50
)
在训练结束后保存模型
network.save('cats_dogs_small_1.h5')
如果按照上述方式训练网络,训练精度随着时间线性增加,知道接近百分之百,而验证集精度则停留在70-72%之间。验证损失仅在第五轮后就达到最小值,然后保持不变。这是因为训练样本相对较少。我们要使用dropout和L2正则化来解决过拟合问题。
数据增强是计算机视觉领域的一种新方法,在深度学习处理图像的时候几乎都会用到这种方法,它就是数据增强。
过拟合是因为学习样本太少,导致无法训练处能够泛化到新数据的模型。如果拥有无限数据,模型就能学习到数据分布的所有内容,永远不会过拟合。数据增强是从现有的训练样本中生成更多的训练数据,其方法是利用多种能生产可信图像的随机变换来增加数据内容,从而具有泛化能力。
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
)
开始训练
Found 1000 images belonging to 2 classes.
Epoch 1/100
100/100 [==============================] - 55s 538ms/step - loss: 0.6943 - accuracy: 0.5000 - val_loss: 0.6882 - val_accuracy: 0.5460