复现何凯明Resnet论文正确率0.9+结果,但是正确率还是没有达到0.9以上,因为 101-layers 和 152-layers 的残差块结构和 34-layers 是有一些区别的。
import tensorflow as tf
import os
import numpy as np
import pickle
# 文件存放目录
CIFAR_DIR = "./cifar-10-batches-py"
def load_data(filename):
'''read data from data file'''
with open(filename, 'rb') as f:
data = pickle.load(f, encoding='bytes') # python3 需要添加上encoding='bytes'
return data[b'data'], data[b'labels'] # 并且 在 key 前需要加上 b
class CifarData:
def __init__(self, filenames, need_shuffle):
'''参数1:文件夹 参数2:是否需要随机打乱'''
all_data = []
all_labels = []
for filename in filenames:
# 将所有的数据,标签分别存放在两个list中
data, labels = load_data(filename)
all_data.append(data)
all_labels.append(labels)
# 将列表 组成 一个numpy类型的矩阵!!!!
self._data = np.vstack(all_data)
# 对数据进行归一化, 尺度固定在 [-1, 1] 之间
self._data = self._data / 127.5 - 1
# 将列表,变成一个 numpy 数组
self._labels = np.hstack(all_labels)
# 记录当前的样本 数量
self._num_examples = self._data.shape[0]
# 保存是否需要随机打乱
self._need_shuffle = need_shuffle
# 样本的起始点
self._indicator = 0
# 判断是否需要打乱
if self._need_shuffle:
self._shffle_data()
def _shffle_data(self):
# np.random.permutation() 从 0 到 参数,随机打乱
p = np.random.permutation(self._num_examples)
# 保存 已经打乱 顺序的数据
self._data = self._data[p]
self._labels = self._labels[p]
def next_batch(self, batch_size):
'''return batch_size example as a batch'''
# 开始点 + 数量 = 结束点
end_indictor = self._indicator + batch_size
# 如果结束点大于样本数量
if end_indictor > self._num_examples:
if self._need_shuffle:
# 重新打乱
self._shffle_data()
# 开始点归零,从头再来
self._indicator = 0
# 重新指定 结束点. 和上面的那一句,说白了就是重新开始
end_indictor = batch_size # 其实就是 0 + batch_size, 把 0 省略了
else:
raise Exception("have no more examples")
# 再次查看是否 超出边界了
if end_indictor > self._num_examples:
raise Exception("batch size is larger than all example")
# 把 batch 区间 的data和label保存,并最后return
batch_data = self._data[self._indicator:end_indictor]
batch_labels = self._labels[self._indicator:end_indictor]
self._indicator = end_indictor
return batch_data, batch_labels
# 拿到所有文件名称
train_filename = [os.path.join(CIFAR_DIR, 'data_batch_%d' % i) for i in range(1, 6)]
# 拿到标签
test_filename = [os.path.join(CIFAR_DIR, 'test_batch')]
# 拿到训练数据和测试数据
train_data = CifarData(train_filename, True)
test_data = CifarData(test_filename, False)
def residual_block(x, output_channel, is_training):
'''
定义残差块儿
:param x: 输入tensor
:param output_channel: 输出的通道数
:return: tensor
需要注意的是:每经过一个stage,通道数就要 * 2
在同一个stage中,通道数是没有变化的
'''
input_channel = x.get_shape().as_list()[-1] # 拿出 输入 tensor 的 最后一维:也就是通道数
if input_channel * 2 == output_channel:
increase_dim = True
strides = (2, 2) #
elif input_channel == output_channel:
increase_dim = False
strides = (1, 1)
else:
raise Exception("input channel can't match output channel")
conv1 = tf.layers.conv2d(x,
output_channel,
(3, 3),
strides = strides,
padding = 'same',
activation = None,
name = 'conv1'
)
bn = tf.layers.batch_normalization(conv1, training = is_training)
conv1 = tf.nn.relu(bn)
conv2 = tf.layers.conv2d(conv1,
output_channel,
(3, 3),
strides = (1, 1), # 因为 上一层 卷积已经进行过降采样,故这里不需要
padding = 'same',
activation = None,
name = 'conv2'
)
bn = tf.layers.batch_normalization(conv2, training = is_training)
conv2 = tf.nn.relu(bn)
if increase_dim: # 需要使用降采样
# pooled_x 数据格式 [ None, image_width, image_height, channel ]
# 要求格式 [ None, image_width, image_height, channel * 2 ]
pooled_x = tf.layers.average_pooling2d(x,
(2, 2), # size
(2, 2), # stride
padding = 'valid'
)
'''
如果输出通道数是输入的两倍的话,需要增加通道数量.
maxpooling 只能降采样,而不能增加通道数,
所以需要单独增加通道数
'''
padded_x = tf.pad(pooled_x, # 参数 2 ,在每一个通道上 加 pad
[
[ 0, 0 ],
[ 0, 0 ],
[ 0, 0 ],
[input_channel // 2, input_channel // 2] # 实际上就是 2倍input_channel,需要均分开
]
)
else:
padded_x = x
output_x = conv2 + padded_x # 就是 公式: H(x) = F(x) + x
return output_x
def res_net(x, num_residual_blocks, num_filter_base, class_num, is_training):
'''
残差网络主程序
:param x: 输入tensor
:param num_residual_blocks: 每一个stage有多少残差块儿 eg: list [3, 4, 6, 2] 及每一个stage上的残差块儿数量
:param num_filter_base: 最初的通道数
:param class_num: 所需要的分类数
:return: tensor
'''
num_subsampling = len(num_residual_blocks) # num_subsampling 为 stage 个数
layers = [] # 保存每一个残差块的输出
# x: [ None, width, height, channel] -> [width, height, channel]
input_size = x.get_shape().as_list()[1:]
# 首先,开始第一个卷积层
with tf.variable_scope('conv0'):
conv0= tf.layers.conv2d(x,
num_filter_base,
(3, 3),
strides = (1, 1),
padding = 'same',
name = 'conv0',
activation=None
)
bn = tf.layers.batch_normalization(conv0, training=is_training)
conv0 = tf.nn.relu(bn)
layers.append(conv0)
# 根据 模型,此处应有一个 pooling,但是 cifar-10 数据集很小,所以不再做 pool
# num_subsampling = 4, sample_id = [0, 1, 2, 3]
for sample_id in range(num_subsampling):
for i in range(num_residual_blocks[sample_id]):
with tf.variable_scope('conv%d_%d' % (sample_id, i)):
conv = residual_block(layers[-1],
num_filter_base * ( 2 ** sample_id ), # 每一个stage都是之前的2倍
is_training
)
layers.append(conv)
# 最后就到了 average pool, 1000维 全连接, 这一步
with tf.variable_scope('fc'):
# layer[-1].shape: [None, width, height, channel]
# kernal_size = image_width, image_height
global_pool = tf.reduce_mean(layers[-1], [1, 2]) # 求平均值函数,参数二 指定 axis
# global_pool的shape是(?, 128)
# 这里需要解释一下,对第二维,第三维求平均值,实际上就是对每一个feature map求一个平均值,一共有128个特征图.
# 所以维度从四维,降低到了两维
logits = tf.layers.dense(global_pool, class_num)
layers.append(logits)
return layers[-1]
# 设计计算图
# 形状 [None, 3072] 3072 是 样本的维数, None 代表位置的样本数量
x = tf.placeholder(tf.float32, [None, 3072])
# 形状 [None] y的数量和x的样本数是对应的
y = tf.placeholder(tf.int64, [None])
# 设立 batch_normalization 的 flag
is_training = tf.placeholder(tf.bool, [])
# [None, ], eg: [0, 5, 6, 3]
x_image = tf.reshape(x, [-1, 3, 32, 32])
# 将最开始的向量式的图片,转为真实的图片类型
x_image = tf.transpose(x_image, perm= [0, 2, 3, 1])
#########################################################
# 插入图像 增强
#########################################################
# 随机左右反转
#data_aug_1 = tf.image.random_flip_left_right(x_image)
# 增加亮度
#data_aug_2 = tf.image.random_brightness(x_image, max_delta = 63)
# 增加对比度
#data_aug_3 = tf.image.random_contrast(x_image, lower = 0.2, upper = 1.8)
y_ = res_net(x_image, [3, 4, 6, 3], 64, 10, is_training)
# 使用交叉熵 设置损失函数
loss = tf.losses.sparse_softmax_cross_entropy(labels = y, logits = y_)
# 该api,做了三件事儿 1. y_ -> softmax 2. y -> one_hot 3. loss = ylogy
# 预测值 获得的是 每一行上 最大值的 索引.注意:tf.argmax()的用法,其实和 np.argmax() 一样的
predict = tf.argmax(y_, 1)
# 将布尔值转化为int类型,也就是 0 或者 1, 然后再和真实值进行比较. tf.equal() 返回值是布尔类型
correct_prediction = tf.equal(predict, y)
# 比如说第一行最大值索引是6,说明是第六个分类.而y正好也是6,说明预测正确
# 将上句的布尔类型 转化为 浮点类型,然后进行求平均值,实际上就是求出了准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float64))
with tf.name_scope('train_op'): # tf.name_scope() 定义该变量的命名空间
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss) # 将 损失函数 降到 最低
# 初始化变量
init = tf.global_variables_initializer()
batch_size = 20
train_steps = 1000000
test_steps = 100
with tf.Session() as sess:
sess.run(init) # 注意: 这一步必须要有!!
# 开始训练
for i in range(train_steps):
# 得到batch
batch_data, batch_labels = train_data.next_batch(batch_size)
# 获得 损失值, 准确率
loss_val, acc_val, _ = sess.run([loss, accuracy, train_op], feed_dict={x:batch_data, y:batch_labels, is_training:True})
# 每 500 次 输出一条信息
if (i+1) % 500 == 0:
print('[Train] Step: %d, loss: %4.5f, acc: %4.5f' % (i+1, loss_val, acc_val))
# 每 5000 次 进行一次 测试
if (i+1) % 5000 == 0:
# 获取数据集,但不随机
test_data = CifarData(test_filename, False)
all_test_acc_val = []
for j in range(test_steps):
test_batch_data, test_batch_labels = test_data.next_batch(batch_size)
test_acc_val = sess.run([accuracy], feed_dict={ x:test_batch_data, y:test_batch_labels, is_training:False })
all_test_acc_val.append(test_acc_val)
test_acc = np.mean(all_test_acc_val)
print('[Test ] Step: %d, acc: %4.5f' % ((i+1), test_acc))
'''
使用 [3, 4, 6, 3], 64
=====================================================
[Test ] Step: 300000, acc: 0.88300
=====================================================
'''
基于Keras搭建ResNet:
"""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])