在小型数据集上从头开始训练一个卷积神经网络

记:开学这一段时间以来没怎么学习,终于忙完了所有开学的事物,安心坐下来写一篇博客。今天这篇博客是关于卷积神经网络,它与密集链接模型相比可以提高在相当程度上提高精度,结果还是不错的。使用卷积神经网络之前还要了解一些卷积神经网络相关的基础知识,下面简单介绍一下再进行代码编写。

1、密集连接层(Dense层)和卷积层的区别:

  • Dense层从输入特征空间中学到的是全局模式(涉及所有像素的模式),而卷积层学到的是局部模式,卷积神经网络学到的模式具有平移不变形且卷积神经网络可以学到模式的空间层次结构。

2、特征图:包含两个空间轴(高度和宽度)和一个深度轴(也叫通道轴)的3D张量,其卷积也叫特征图。

      输出特征图:仍然是一个3D张量,具有宽度和高度,其深度可以任意取值,且深度轴的不同通道不再像RGB输入那样代表特定  颜色,而是代表过滤器。

3、卷积的两个参数定义:

  • 从输入中提取的图块尺寸:这些图块的大小通常是3x3,或者5x5。
  • 输出特征图的深度:即上面所说的卷积所计算的过滤器的数量

4、卷积的工作原理图:

  • 3D 输入特征图上滑动slide)这些 3×3 5×5 的窗口,在每个可能 的位置停止并提取周围特征的 3D 图块[形状为 (window_height, window_width, input_ depth)]。然后每个 3D 图块与学到的同一个权重矩阵[叫作卷积核convolution kernel)]做 张量积,转换成形状为 (output_depth,) 1D 向量。然后对所有这些向量进行空间重组, 使其转换为形状为 (height, width, output_depth) 3D 输出特征图。

          

5、最大池化的作用:对特征图进行下采样,与步进卷积类似,最大池化会将特征图的尺寸减半。最大池化通常使用2x2的窗口和步幅2,目的是将特征图下采样2倍,而卷积通常使用3x3窗口和步幅1。

6、使用下采样的原因:一是减少需要处理的特征图的元素个数,二是通过让连续 卷积层的观察窗口越来越大(即窗口覆盖原始输入的比例越来越大),从而引入空间过滤器的层级结构。

7、dogs-vs-cats数据集:是kaggle竞赛经常使用的一个数据集,包含25000张猫和狗的图片,每个类别都有12500张。如果在官网下载的话需要注册账号,而且由于某些原因不一定能下载下来,博主无意间发现了一个连接,非常不错。https://www.microsoft.com/en-us/download/details.aspx?id=54765。我们在下载数据集解压之后需要创建一个新的数据集,其中包含三个子集:每个类别各 1000 个样本的训练集、每个类别各 500 个样本的验证集和每个类别各 500 个样本的测试集。结构如下图所示:

              

8、具体代码以及部分注释(可以根据自己文件下载的位置更改路径)

import os,shutil

#原始数据集解压目录
original_dataset_cats_dir='E:\Google_download\kagglecatsanddogs_3367a\PetImages\Cat'
original_dataset_dogs_dir='E:\Google_download\kagglecatsanddogs_3367a\PetImages\Dog'

#创建新的数据集,将包含三个子集
base_dir='E:\jupyter_notebooks\cats_and_dogs_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)

#将前1000张猫的图像复制到train_cats_dir
fnames = ['{}.jpg'.format(i) for i in range(1000)] 
for fname in fnames:
    src = os.path.join(original_dataset_cats_dir, fname)
    dst = os.path.join(train_cats_dir, fname)
    shutil.copyfile(src, dst)

#接下来分别将500张猫的图像复制到validation_cats_dir和test_cats_dir,代码同上
fnames = ['{}.jpg'.format(i) for i in range(1000,1500)] 
for fname in fnames:
    src = os.path.join(original_dataset_cats_dir, fname)
    dst = os.path.join(validation_cats_dir, fname)
    shutil.copyfile(src, dst)
    
fnames = ['{}.jpg'.format(i) for i in range(1500,2000)] 
for fname in fnames:
    src = os.path.join(original_dataset_cats_dir, fname)
    dst = os.path.join(test_cats_dir, fname)
    shutil.copyfile(src, dst)
    
    
#接下来是按照上面复制猫的图像方法把狗的图像划分开
fnames = ['{}.jpg'.format(i) for i in range(1000)] 
for fname in fnames:
    src = os.path.join(original_dataset_dogs_dir, fname)
    dst = os.path.join(train_dogs_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['{}.jpg'.format(i) for i in range(1000,1500)] 
for fname in fnames:
    src = os.path.join(original_dataset_dogs_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    shutil.copyfile(src, dst)
    
fnames = ['{}.jpg'.format(i) for i in range(1500,2000)] 
for fname in fnames:
    src = os.path.join(original_dataset_dogs_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)
#构建网络,实例化猫狗分类的小卷积神经网络
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'))

现在来看一下每一层的形状大小:

       

#配置模型用于训练
from keras import optimizers
model.compile(loss='binary_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=['acc'])

#数据预处理
#数据以 JPEG 文件的形式保存在硬盘中,所以数据预处理步骤大致如下。
#(1) 读取图像文件。
#(2) 将 JPEG 文件解码为 RGB 像素网格。
#(3) 将这些像素网格转换为浮点数张量。
#(4) 将像素值(0~255 范围内)缩放到 [0, 1] 区间(正如你所知,神经网络喜欢处理#较小的输
#入值)。
#keras可以自动完成这些处理步骤,在keras.preprocessing.image的ImageDataGenerator类中

from keras.preprocessing.image import ImageDataGenerator

train_datagen=ImageDataGenerator(rescale=1./255)
test_datagen=ImageDataGenerator(rescale=1./255)#将所有图像缩小255倍,在0-1之间

train_generator=train_datagen.flow_from_directory(train_dir,#目标目录
                                                  target_size=(150,150),#调整图像大小为150*150
                                                  batch_size=20,
                                                  class_mode='binary')#使用了binary_corssentropy损失,所以用二进制标签

validation_generator=test_datagen.flow_from_directory(validation_dir,#目标目录
                                                  target_size=(150,150),#调整图像大小为150*150
                                                  batch_size=20,
                                                  class_mode='binary')#使用了binary_corssentropy损失,所以用二进制标签

#利用批量生成器拟合模型

history=model.fit_generator(
                        train_generator,
                        steps_per_epoch=100,#从生成器中抽取steps_per_epoch个批量后(即运行了steps_per_epoch次梯度下降),拟合进入到下一轮次
                        epochs=30,
                        validation_data=validation_generator,
                        validation_steps=50)#从验证生成器中抽取多少个批次用于评估


#保存模型
model.save('cats_and_dogs_small_1.h5')

#绘制训练过程中的损失曲线和精度曲线

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()

结果如下图:随着轮次往下进行训练精度越来越高,几乎达到100%,而验证精度在第5次往后基本达到最大,才70%左右。训练损失函数值也越来越小,几乎达到了0,而验证损失函数值在第10次左右达到最低点后就不再减小,说明训练数据出现了过拟合的现象,下面就用数据增强以及添加dropout层的方法来降低过拟合的发生。

9、数据增强可以利用ImageDataGenerator来设置,代码如下:

#利用ImageDataGenerator来设置数据增强
from keras.preprocessing.image import ImageDataGenerator
datagen=ImageDataGenerator(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')

#显示几个随机增强后的训练图像
from keras.preprocessing import image
import matplotlib.pyplot as plt

fnames=[os.path.join(train_dogs_dir,fname) for fname in os.listdir(train_cats_dir)]
img_path=fnames[4]
img=image.load_img(img_path,target_size=(150,150))#读取图像并调整图像大小
x=image.img_to_array(img)#将其转换为形状(150,150,3)的numpy数组
x=x.reshape((1,)+x.shape)#将其形状改变为(1,150,150,3)

i=0
for batch in datagen.flow(x,batch_size=2):
    plt.figure(i)
    print(batch[0].shape)
    imgplot=plt.imshow(image.array_to_img(batch[0]))
    i+=1
    if i %6==0:
        break
plt.show()

进行数据增强后的图像结果如下所示:

对于本例来说,我们可以把上面的小型卷积神经网络的构建网络代码,以及数据预处理代码换成下面的代码即可

#定义一个包含dropout的新卷积神经网络
from keras import layers
from keras import models

model2=models.Sequential()
model2.add(layers.Conv2D(32,(3,3),activation='relu',input_shape=(150,150,3)))
model2.add(layers.MaxPooling2D((2,2)))
model2.add(layers.Conv2D(64,(3,3),activation='relu'))
model2.add(layers.MaxPooling2D((2,2)))
model2.add(layers.Conv2D(128,(3,3),activation='relu'))
model2.add(layers.MaxPooling2D((2,2)))
model2.add(layers.Conv2D(128,(3,3),activation='relu'))
model2.add(layers.MaxPooling2D((2,2)))
model2.add(layers.Flatten())
model2.add(layers.Dropout(0.5))
model2.add(layers.Dense(512,activation='relu'))
model2.add(layers.Dense(1,activation='sigmoid'))



from keras import optimizers

model2.compile(loss='binary_crossentropy',
               optimizer=optimizers.RMSprop(lr=1e-4),
               metrics=['acc'])


#使用数据增强,只对训练数据进行增强,测试数据不进行增强
from keras.preprocessing.image import ImageDataGenerator

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)#将所有图像缩小255倍,在0-1之间

train_generator=train_datagen.flow_from_directory(train_dir,#目标目录
                                                  target_size=(150,150),#调整图像大小为150*150
                                                  batch_size=32,
                                                  class_mode='binary')#使用了binary_corssentropy损失,所以用二进制标签

validation_generator=test_datagen.flow_from_directory(validation_dir,#目标目录
                                                  target_size=(150,150),#调整图像大小为150*150
                                                  batch_size=32,
                                                  class_mode='binary')#使用了binary_corssentropy损失,所以用二进制标签



history2=model2.fit_generator(
                        train_generator,
                        steps_per_epoch=100,#从生成器中抽取steps_per_epoch个批量后(即运行了steps_per_epoch次梯度下降),拟合进入到下一轮次
                        epochs=30,
                        validation_data=validation_generator,
                        validation_steps=50)#从验证生成器中抽取多少个批次用于评估

model2.save('cats_and_dogs_small_2.h5')

结果如下图所示:由于博主的电脑配置不太可以,把轮次设置的比较小,可以尝试把epochs设置为100。虽然设置的比较小,但是还是可以看出来使用数据增强之后的效果的,过拟合的现象明显降低。

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.无名之辈

1毛也是爱~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值