基于CIFAR10(小批量图片)数据集训练ResNet(残差网络)
Keras实例目录
代码注释
"""Trains a ResNet on the CIFAR10 dataset.
基于CIFAR10(小批量图片)数据集训练ResNet(残差网络)
ResNet v1
[a] Deep Residual Learning for Image Recognition
残差学习在图像识别中的应用
https://arxiv.org/pdf/1512.03385.pdf
ResNet v2
[b] Identity Mappings in Deep Residual Networks
基于ResNet的恒等映射
https://arxiv.org/pdf/1603.05027.pdf
"""
from __future__ import print_function
import keras
from keras.layers import Dense, Conv2D, BatchNormalization, Activation
from keras.layers import AveragePooling2D, Input, Flatten
from keras.optimizers import Adam
from keras.callbacks import ModelCheckpoint, LearningRateScheduler
from keras.callbacks import ReduceLROnPlateau
from keras.preprocessing.image import ImageDataGenerator
from keras.regularizers import l2
from keras import backend as K
from keras.models import Model
from keras.datasets import cifar10
import numpy as np
import os
# Training parameters
# 训练参数
batch_size = 32 # orig paper trained all networks with batch_size=128 原论文batch_size=128
epochs = 200
data_augmentation = True
num_classes = 10
# Subtracting pixel mean improves accuracy
subtract_pixel_mean = True
# Model parameter 模型参数
# ----------------------------------------------------------------------------
# | | 200-epoch | Orig Paper| 200-epoch | Orig Paper| sec/epoch
# Model | n | ResNet v1 | ResNet v1 | ResNet v2 | ResNet v2 | GTX1080Ti
# |v1(v2)| %Accuracy | %Accuracy | %Accuracy | %Accuracy | v1 (v2)
# ----------------------------------------------------------------------------
# ResNet20 | 3 (2)| 92.16 | 91.25 | ----- | ----- | 35 (---)
# ResNet32 | 5(NA)| 92.46 | 92.49 | NA | NA | 50 ( NA)
# ResNet44 | 7(NA)| 92.50 | 92.83 | NA | NA | 70 ( NA)
# ResNet56 | 9 (6)| 92.71 | 93.03 | 93.01 | NA | 90 (100)
# ResNet110 |18(12)| 92.65 | 93.39+-.16| 93.15 | 93.63 | 165(180)
# ResNet164 |27(18)| ----- | 94.07 | ----- | 94.54 | ---(---)
# ResNet1001| (111)| ----- | 92.39 | ----- | 95.08+-.14| ---(---)
# ---------------------------------------------------------------------------
n = 3
# Model version
# 模型版本
# Orig paper: version = 1 (ResNet v1), Improved ResNet: version = 2 (ResNet v2)
# 原论文:版本 = 1 (ResNet v1),升级 ResNet:版本 =2 (ResNet v2)
version = 1
# Computed depth from supplied model parameter n
# 根据模型参数n计算深度
if version == 1:
depth = n * 6 + 2
elif version == 2:
depth = n * 9 + 2
# Model name, depth and version
# 模型名称,深度和版本
model_type = 'ResNet%dv%d' % (depth, version)
# Load the CIFAR10 data.
# 加载CIFAR10数据集
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
# Input image dimensions.
# 输入图片维度
input_shape = x_train.shape[1:]
# Normalize data.
# 归一化数据
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
# If subtract pixel mean is enabled
# 如果启用像素均值
if subtract_pixel_mean:
x_train_mean = np.mean(x_train, axis=0)
x_train -= x_train_mean
x_test -= x_train_mean
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
print('y_train shape:', y_train.shape)
# Convert class vectors to binary class matrices.
# 转换类向量为多分类矩阵
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
def lr_schedule(epoch):
"""Learning Rate Schedule
学习速率表
Learning rate is scheduled to be reduced after 80, 120, 160, 180 epochs.
Called automatically every epoch as part of callbacks during training.
在80, 120, 160、180个时期后,学习率降低。在训练期间自动调用每个时期作为回调的一部分。
# Arguments
参数
epoch (int): The number of epochs
epoch(int,整型):(训练)周期数
# Returns
返回
lr (float32): learning rate
lr(float32,浮点型):学习率
"""
lr = 1e-3 # 1e-3:1乘以10的-3次方
if epoch > 180:
lr *= 0.5e-3
elif epoch > 160:
lr *= 1e-3
elif epoch > 120:
lr *= 1e-2
elif epoch > 80:
lr *= 1e-1
print('Learning rate: ', lr)
return lr
def resnet_layer(inputs,
num_filters=16,
kernel_size=3,
strides=1,
activation='relu',
batch_normalization=True,
conv_first=True):
"""2D Convolution-Batch Normalization-Activation stack builder
2D卷积批处理归一化激活堆栈生成器
# Arguments
参数
inputs (tensor): input tensor from input image or previous layer
inputs(tensor,张量):来自输入图片或前一层输入的张量
num_filters (int): Conv2D number of filters
num_filters (int): 2维卷积过滤器数
kernel_size (int): Conv2D square kernel dimensions
kernel_size (int): 2维卷积过滤器平方核维度
strides (int): Conv2D square stride dimensions
strides (int):2维卷积平方步长维度
activation (string): activation name
activation (string): 激活函数名称
batch_normalization (bool): whether to include batch normalization
batch_normalization (bool): 是否包含批次归一化
conv_first (bool): conv-bn-activation (True) or
activation-bn-conv (False)
conv_first (bool): 卷积-批次归一化-激活 (True) or
激活-批次归一化-卷积 (False)
# Returns
# 返回
x (tensor): tensor as input to the next layer
x (tensor): 张量作为下一层的输入
"""
conv = Conv2D(num_filters,
kernel_size=kernel_size,
strides=strides,
padding='same',
kernel_initializer='he_normal',
kernel_regularizer=l2(1e-4))
x = inputs
if conv_first:
x = conv(x)
if batch_normalization:
x = BatchNormalization()(x)
if activation is not None:
x = Activation(activation)(x)
else:
if batch_normalization:
x = BatchNormalization()(x)
if activation is not None:
x = Activation(activation)(x)
x = conv(x)
return x
def resnet_v1(input_shape, depth, num_classes=10):
"""ResNet Version 1 Model builder [a]
ResNet版本1模型构建 [a]
Stacks of 2 x (3 x 3) Conv2D-BN-ReLU
Last ReLU is after the shortcut connection.
最后一个Relu是在快捷连接之后。
At the beginning of each stage, the feature map size is halved (downsampled)
by a convolutional layer with strides=2, while the number of filters is
doubled. Within each stage, the layers have the same number filters and the
same number of filters.
在每个阶段的开始,特征映射的大小被步幅为2的卷积层减半(下采样),而滤波器的数量增加了一倍。
在每个阶段,这些层具有相同数量的过滤器和相同数量的过滤器。
Features maps sizes:
特征映射大小:
stage 0: 32x32, 16
stage 1: 16x16, 32
stage 2: 8x8, 64
The Number of parameters is approx the same as Table 6 of [a]:
参数的数量与[a]表6大致相同:
ResNet20 0.27M
ResNet32 0.46M
ResNet44 0.66M
ResNet56 0.85M
ResNet110 1.7M
# Arguments
参数
input_shape (tensor): shape of input image tensor
input_shape (tensor): 输入图像张量形状
depth (int): number of core convolutional layers
depth (int):核卷积层的数量
num_classes (int): number of classes (CIFAR10 has 10)
num_classes (int): 类别数量 (CIFAR10(数据集)有10个)
# Returns
返回
model (Model): Keras model instance
模型(Model):Keras模型实例
"""
if (depth - 2) % 6 != 0:
raise ValueError('depth should be 6n+2 (eg 20, 32, 44 in [a])')
# Start model definition.
# 开始模型定义
num_filters = 16
num_res_blocks = int((depth - 2) / 6)
inputs = Input(shape=input_shape)
x = resnet_layer(inputs=inputs)
# Instantiate the stack of residual units
# 实例化残差单元堆栈
for stack in range(3):
for res_block in range(num_res_blocks):
strides = 1
if stack > 0 and res_block == 0: # first layer but not first stack 第一层而不是第一堆栈
strides = 2 # downsample 降低采样
y = resnet_layer(inputs=x,
num_filters=num_filters,
strides=strides)
y = resnet_layer(inputs=y,
num_filters=num_filters,
activation=None)
if stack > 0 and res_block == 0: # first layer but not first stack 第一层而不是第一堆栈
# linear projection residual shortcut connection to match
# 线性投影残差捷径连接匹配
# changed dims
# 改变维度
x = resnet_layer(inputs=x,
num_filters=num_filters,
kernel_size=1,
strides=strides,
activation=None,
batch_normalization=False)
x = keras.layers.add([x, y])
x = Activation('relu')(x)
num_filters *= 2
# Add classifier on top.
# 在顶部添加分类器。
# v1 does not use BN after last shortcut connection-ReLU
# V1 没有使用 批次归一化处理,在上一个连接ReLU激活后。
x = AveragePooling2D(pool_size=8)(x)
y = Flatten()(x)
outputs = Dense(num_classes,
activation='softmax',
kernel_initializer='he_normal')(y)
# Instantiate model.
# 初始化模型
model = Model(inputs=inputs, outputs=outputs)
return model
def resnet_v2(input_shape, depth, num_classes=10):
"""ResNet Version 2 Model builder [b]
ResNet版本2模型构建 [b]
Stacks of (1 x 1)-(3 x 3)-(1 x 1) BN-ReLU-Conv2D or also known as
bottleneck layer
First shortcut connection per layer is 1 x 1 Conv2D.
第一个短连接每层是 1 x 1 的2维卷积
Second and onwards shortcut connection is identity.
第二个和前一个短连接是一致的。
At the beginning of each stage, the feature map size is halved (downsampled)
by a convolutional layer with strides=2, while the number of filter maps is
doubled. Within each stage, the layers have the same number filters and the
same filter map sizes.
在每个阶段的开始,特征映射的大小被步幅为2的卷积层减半(下采样),而滤波器的数量增加了一倍。
在每个阶段,这些层具有相同数量的过滤器和相同数量的过滤器。
Features maps sizes:
特征映射的大小:
conv1 : 32x32, 16
stage 0: 32x32, 64
stage 1: 16x16, 128
stage 2: 8x8, 256
# Arguments
参数
input_shape (tensor): shape of input image tensor
input_shape (tensor): 输入图像张量形状
depth (int): number of core convolutional layers
depth (int): 核卷积层数量
num_classes (int): number of classes (CIFAR10 has 10)
num_classes (int): 类别数量 (CIFAR10(数据集)有10个)
# Returns
返回
model (Model): Keras model instance
模型(Model):Keras模型实例
"""
if (depth - 2) % 9 != 0:
raise ValueError('depth should be 9n+2 (eg 56 or 110 in [b])')
# Start model definition.
# 模型定义
num_filters_in = 16
num_res_blocks = int((depth - 2) / 9)
inputs = Input(shape=input_shape)
# v2 performs Conv2D with BN-ReLU on input before splitting into 2 paths
# V2在分裂成2条路径之前用BN-ReLU在输入端执行Conv2D
x = resnet_layer(inputs=inputs,
num_filters=num_filters_in,
conv_first=True)
# Instantiate the stack of residual units
# 实例化残差单元堆栈
for stage in range(3):
for res_block in range(num_res_blocks):
activation = 'relu'
batch_normalization = True
strides = 1
if stage == 0:
num_filters_out = num_filters_in * 4
if res_block == 0: # first layer and first stage 第一层、第一步
activation = None
batch_normalization = False
else:
num_filters_out = num_filters_in * 2
if res_block == 0: # first layer but not first stage 第一层非第一步
strides = 2 # downsample 降低采样
# bottleneck residual unit
# 瓶颈残差单元
y = resnet_layer(inputs=x,
num_filters=num_filters_in,
kernel_size=1,
strides=strides,
activation=activation,
batch_normalization=batch_normalization,
conv_first=False)
y = resnet_layer(inputs=y,
num_filters=num_filters_in,
conv_first=False)
y = resnet_layer(inputs=y,
num_filters=num_filters_out,
kernel_size=1,
conv_first=False)
if res_block == 0:
# linear projection residual shortcut connection to match
# changed dims
x = resnet_layer(inputs=x,
num_filters=num_filters_out,
kernel_size=1,
strides=strides,
activation=None,
batch_normalization=False)
x = keras.layers.add([x, y])
num_filters_in = num_filters_out
# Add classifier on top.
# 在顶部添加分类器。
# v2 has BN-ReLU before Pooling
# v2 在池化前用BN-ReLU激活处理
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = AveragePooling2D(pool_size=8)(x)
y = Flatten()(x)
outputs = Dense(num_classes,
activation='softmax',
kernel_initializer='he_normal')(y)
# Instantiate model.
# 初始化模型
model = Model(inputs=inputs, outputs=outputs)
return model
if version == 2:
model = resnet_v2(input_shape=input_shape, depth=depth)
else:
model = resnet_v1(input_shape=input_shape, depth=depth)
model.compile(loss='categorical_crossentropy',
optimizer=Adam(lr=lr_schedule(0)),
metrics=['accuracy'])
model.summary()
print(model_type)
# Prepare model model saving directory.
# 模型保存目录
save_dir = os.path.join(os.getcwd(), 'saved_models')
model_name = 'cifar10_%s_model.{epoch:03d}.h5' % model_type
if not os.path.isdir(save_dir):
os.makedirs(save_dir)
filepath = os.path.join(save_dir, model_name)
# Prepare callbacks for model saving and for learning rate adjustment.
# 保存模型和学习率调整回调
checkpoint = ModelCheckpoint(filepath=filepath,
monitor='val_acc',
verbose=1,
save_best_only=True)
lr_scheduler = LearningRateScheduler(lr_schedule)
lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1),
cooldown=0,
patience=5,
min_lr=0.5e-6)
callbacks = [checkpoint, lr_reducer, lr_scheduler]
# Run training, with or without data augmentation.
# 运行训练,有或没有数据扩大(增加数据集样本数量)
if not data_augmentation:
print('Not using data augmentation.')
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test),
shuffle=True,
callbacks=callbacks)
else:
print('Using real-time data augmentation.')
# This will do preprocessing and realtime data augmentation:
# 处理和实时数据(集)扩大
datagen = ImageDataGenerator(
# set input mean to 0 over the dataset
# 在数据集上设置输入平均值为0
featurewise_center=False,
# set each sample mean to 0
# 设置样本均值为0
samplewise_center=False,
# divide inputs by std of dataset
# 用数据集的std划分输入
featurewise_std_normalization=False,
# divide each input by its std
# 根据标准划分每个输入
samplewise_std_normalization=False,
# apply ZCA whitening # 应用ZCA白化处理
zca_whitening=False,
# randomly rotate images in the range (deg 0 to 180) # 在0到180度范围内,随机旋转图像
rotation_range=0,
# randomly shift images horizontally # 随机水平移动图像
width_shift_range=0.1,
# randomly shift images vertically # 随机垂直移动图像
height_shift_range=0.1,
# randomly flip images # 随机翻转图像
horizontal_flip=True,
# randomly flip images # 随机翻转图像
vertical_flip=False)
# Compute quantities required for featurewise normalization
# (std, mean, and principal components if ZCA whitening is applied).
# 特征归一化所需的计算量
# (标准均值,主ZCA白化应用)。
datagen.fit(x_train)
# Fit the model on the batches generated by datagen.flow().
# 拟合模型,该模型由datagen.flow()(函数)批量生成
model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size),
validation_data=(x_test, y_test),
epochs=epochs, verbose=1, workers=4,
callbacks=callbacks)
# Score trained model.
# 评估训练模型
scores = model.evaluate(x_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])
代码执行
Keras详细介绍
英文:https://keras.io/
中文:http://keras-cn.readthedocs.io/en/latest/
实例下载
https://github.com/keras-team/keras
https://github.com/keras-team/keras/tree/master/examples
完整项目下载
方便没积分童鞋,请加企鹅452205574,共享文件夹。
包括:代码、数据集合(图片)、已生成model、安装库文件等。