概述

如何用深度学习框架做分类,具体步骤应该是什么?应该怎么做?本文以keras(2.0版以上)为框架来具体讲解训练自己的网络来进行图片分类的步骤:
1.准备好自己的数据集,并按照train,test,validation来划分
2.阅读论文或者相关博客,搭建自己的模型。
3.根据经验进行超参选择(学习率,数据批量训练的个数等)和数据预处理(如归一化)
4.进行训练并分析结构,根据结果进行优化(如过拟合就增强数据集或者优化结构等)
5.(附加)可视化隐藏层输出结果以及权重,解释分类原理

注意:本博文使用的pycharm编译器。

数据集

实际工程中,大多情况下你可能都是用不多的数据来训练图像分类模型的。因此,这里使用小数据集作为例子。我们采用“猫狗数据集”来进行训练网络,它是由Kaggle.com在2013年年底作为计算机视觉竞赛的一部分提供的,下载地址是这里 ,不过注册挺麻烦的,下直接下载的朋友可以在这里进行下载。然后用一下脚本对文件夹中的“train”(我们这里只用这个文件)进行分类,代码如下:

import os, shutil

# dataset was uncompressed
original_dataset_dir = './train'

# The directory where we will
# store our smaller dataset
base_dir = './cats_and_dogs_small'
os.mkdir(base_dir)

# Directories for our training,
# validation and test splits
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)

# Directory with our training cat pictures
train_cats_dir = os.path.join(train_dir, 'cats')
os.mkdir(train_cats_dir)

# Directory with our training dog pictures
train_dogs_dir = os.path.join(train_dir, 'dogs')
os.mkdir(train_dogs_dir)

# Directory with our validation cat pictures
validation_cats_dir = os.path.join(validation_dir, 'cats')
os.mkdir(validation_cats_dir)

# Directory with our validation dog pictures
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.mkdir(validation_dogs_dir)

# Directory with our validation cat pictures
test_cats_dir = os.path.join(test_dir, 'cats')
os.mkdir(test_cats_dir)

# Directory with our validation dog pictures
test_dogs_dir = os.path.join(test_dir, 'dogs')
os.mkdir(test_dogs_dir)

# Copy first 1000 cat images to 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)

# Copy next 500 cat images to 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)

# Copy next 500 cat images to 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)

# Copy first 1000 dog images to 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)

# Copy next 500 dog images to 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)

# Copy next 500 dog images to 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(test_dogs_dir, fname)
    shutil.copyfile(src, dst)

其中original_dataset_dir=“ ”是放入下载原数据的地址,上述代码执行是按这样的位置放的:

用keras做分类的步骤总结
我们可以看到,cats_and_dogs_small是运行后产生的,里面有train,test,validation三个文件。
代码小结:一个非常方便的拷贝函数 shutil.copyfile(src, dst);

搭建自己的模型

这里使用简易的6层模型,4层卷积,2层全连接层:具体信息如下:
用keras做分类的步骤总结
代码如下:

from keras import layers
from keras import models
from keras import optimizers
from keras.preprocessing.image import ImageDataGenerator
import os
import matplotlib.pyplot as plt
base_dir = './cats_and_dogs_small'
train_dir = os.path.join(base_dir, 'train')
test_dir = os.path.join(base_dir, 'test')
validation_dir = os.path.join(base_dir, 'validation')

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'))
model.summary()
model.compile(loss='binary_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=['acc'])

数据预处理

在将数据馈送到我们的网络之前,应将数据格式化为适当的预处理浮点张量。 目前,我们把JPEG数据放入网络的步骤大致如下:
读取图片文件。
将JPEG内容解码为RBG像素网格。
将这些转换为浮点张量。
将像素值(0到255之间)重新缩放到[0,1]间隔(神经网络更喜欢处理小输入值)。
这可能看起来有点令人生畏,但Keras有实用工具自动处理这些步骤。 Keras有一个带有图像处理辅助工具的模块,位于keras.preprocessing.image。 特别是,它包含ImageDataGenerator类,它允许快速设置Python生成器,可以自动将磁盘上的图像文件转换为批处理的预处理张量。

from keras.preprocessing.image import ImageDataGenerator

# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory
        train_dir,
        # All images will be resized to 150x150
        target_size=(150, 150),
        batch_size=20,
        # Since we use binary_crossentropy loss, we need binary labels
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='binary')

运行代码并进行评估

我们使用生成器来训练模型。这里使用fit_generator来完成它。它的第一个参数是一个python生成器,这里是train_generator,因为数据是不断生成的,所以Keras要知道每一轮取多少样本,这里训练集2000个,而生成器是20一个batch,所以steps_per_epoch=100。对于验证集是一样的。

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

然后保存模型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(len(acc))

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

用keras做分类的步骤总结
可以看出明显过拟合,因此模型的正则化和数据增强很重要。

改进与提高

数据增强

在keras中可以通过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')

这里选了几个参数:
rotation_range :旋转角度
width_shift 和 height_shift :在水平或垂直方向平移百分比
fill_mode:填充新建像素的方法,这些像素可能来自旋转或者平移。

正则化模型

使用dropout:

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'])

用数据增强生成器训练新模型

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

# Note that the validation data should not be augmented!
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory
        train_dir,
        # All images will be resized to 150x150
        target_size=(150, 150),
        batch_size=32,
        # Since we use binary_crossentropy loss, we need binary labels
        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)

这里注意,测试集不用增强,结果如下
用keras做分类的步骤总结

比较可知,准确率由0.72提高到了0.82!

进一步提高

对于小型数据集,我们有三种策略——从头开始训练一个小型模型、用预训练的网络做特征提取、对预训练网络进行微调,本文只完成了从头开始训练一个小型模型,至于迁移学习(用预训练的网络做特征提取、对预训练网络进行微调)的用法请看下一篇文章

对于神经网路的可视化以及解释,请看这里。

推荐

关于时间序列分类的文章请点击这里

相关文章: