由于受到计算机性能的影响,虽然LeNet在图像分类中取得了较好的成绩,但是并没有引起很多的关注。 知道2012年,Alex等人提出的AlexNet网络在ImageNet大赛上以远超第二名的成绩夺冠,卷积神经网络乃至深度学习重新引起了广泛的关注。
AlexNet特点
AlexNet是在LeNet的基础上加深了网络的结构,学习更丰富更高维的图像特征。AlexNet的特点:
1.更深的网络结构
2.使用层叠的卷积层,即卷积层+卷积层+池化层来提取图像的特征
3.使用Dropout抑制过拟合
4.使用数据增强Data Augmentation抑制过拟合
5.使用Relu替换之前的sigmoid的作为激活函数
6.多GPU训练
7.ReLu作为激活函数
在最初的感知机模型中,输入和输出的关系如下:
𝑦=∑𝑖𝑤𝑖𝑥𝑖+𝑏
只是单纯的线性关系,这样的网络结构有很大的局限性:即使用很多这样结构的网络层叠加,其输出和输入仍然是线性关系,无法处理有非线性关系的输入输出。因此,对每个神经元的输出做个非线性的转换也就是,将上面就加权求和
∑𝑖𝑤𝑖𝑥𝑖+𝑏 的结果输入到一个非线性函数,也就是激活函数中。 这样,由于激活函数的引入,多个网络层的叠加就不再是单纯的线性变换,而是具有更强的表现能力。
image
在最初, 𝑠𝑖𝑔𝑚𝑜𝑖𝑑 和 𝑡𝑎𝑛ℎ函数最常用的激活函数。
image
在网络层数较少时,𝑠𝑖𝑔𝑚𝑜𝑖𝑑 函数的特性能够很好的满足激活函数的作用:它把一个实数压缩至0到1之间,当输入的数字非常大的时候,结果会接近1;当输入非常大的负数时,则会得到接近0的结果。这种特性,能够很好的模拟神经元在受刺激后,是否被激活向后传递信息(输出为0,几乎不被激活;输出为1,完全被激活)。𝑠𝑖𝑔𝑚𝑜𝑖𝑑一个很大的问题就是梯度饱和。 观察𝑠𝑖𝑔𝑚𝑜𝑖𝑑函数的曲线,当输入的数字较大(或较小)时,其函数值趋于不变,其导数变的非常的小。这样,在层数很多的的网络结构中,进行反向传播时,由于很多个很小的𝑠𝑖𝑔𝑚𝑜𝑖𝑑导数累成,导致其结果趋于0,权值更新较慢。
-
ReLu
image针对 𝑠𝑖𝑔𝑚𝑜𝑖𝑑 梯度饱和导致训练收敛慢的问题,在AlexNet中引入了ReLU。ReLU是一个分段线性函数,小于等于0则输出为0;大于0的则恒等输出。相比于 𝑠𝑖𝑔𝑚𝑜𝑖𝑑 ,ReLU有以下有点:
- 计算开销下。 𝑠𝑖𝑔𝑚𝑜𝑖𝑑 的正向传播有指数运算,倒数运算,而ReLu是线性输出;反向传播中, 𝑠𝑖𝑔𝑚𝑜𝑖𝑑 有指数运算,而ReLU有输出的部分,导数始终为1.
- 梯度饱和问题
- 稀疏性。Relu会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。
这里有个问题,前面提到,激活函数要用非线性的,是为了使网络结构有更强的表达的能力。那这里使用ReLU本质上却是个线性的分段函数,是怎么进行非线性变换的。
这里把神经网络看着一个巨大的变换矩阵 𝑀 ,其输入为所有训练样本组成的矩阵 𝐴 ,输出为矩阵 𝐵 。
𝐵=𝑀⋅𝐴
这里的 𝑀 是一个线性变换的话,则所有的训练样本 𝐴 进行了线性变换输出为 𝐵 。
那么对于ReLU来说,由于其是分段的,0的部分可以看着神经元没有激活,不同的神经元激活或者不激活,其神经玩过组成的变换矩阵是不一样的。
设有两个训练样本𝑎1,𝑎2 其训练时神经网络组成的变换矩阵为𝑀1,𝑀2 由于𝑀1变换对应的神经网络中激活神经元和𝑀2是不一样的,这样𝑀1,𝑀2实际上是两个不同的线性变换。也就是说,每个训练样本使用的线性变换矩阵𝑀𝑖 是不一样的,在整个训练样本空间来说,其经历的是非线性变换。**
简单来说,不同训练样本中的同样的特征,在经过神经网络学习时,流经的神经元是不一样的(激活函数值为0的神经元不会被激活)。这样,最终的输出实际上是输入样本的非线性变换。
单个训练样本是线性变换,但是每个训练样本的线性变换是不一样的,这样整个训练样本集来说,就是非线性的变换。
数据增强
神经网络由于训练的参数多,表能能力强,所以需要比较多的数据量,不然很容易过拟合。当训练数据有限时,可以通过一些变换从已有的训练数据集中生成一些新的数据,以快速地扩充训练数据。对于图像数据集来说,可以对图像进行一些形变操作:
- 翻转
- 随机裁剪
- 平移,颜色光照的变换
- ...
AlexNet中对数据做了以下操作:
- 随机裁剪,对256×256的图片进行随机裁剪到227×227,然后进行水平翻转。
- 测试的时候,对左上、右上、左下、右下、中间分别做了5次裁剪,然后翻转,共10个裁剪,之后对结果求平均。
- 对RGB空间做PCA(主成分分析),然后对主成分做一个(0, 0.1)的高斯扰动,也就是对颜色、光照作变换,结果使错误率又下降了1%。
层叠池化
在LeNet中池化是不重叠的,即池化的窗口的大小和步长是相等的,如下
image
在AlexNet中使用的池化(Pooling)却是可重叠的,也就是说,在池化的时候,每次移动的步长小于池化的窗口长度。AlexNet池化的大小为3×3的正方形,每次池化移动步长为2,这样就会出现重叠。重叠池化可以避免过拟合,这个策略贡献了0.3%的Top-5错误率。与非重叠方案𝑠=2,𝑧=2相比,输出的维度是相等的,并且能在一定程度上抑制过拟合。
局部相应归一化
ReLU具有让人满意的特性,它不需要通过输入归一化来防止饱和。如果至少一些训练样本对ReLU产生了正输入,那么那个神经元上将发生学习。然而,我们仍然发现接下来的局部响应归一化有助于泛化。𝑎𝑖𝑥,𝑦表示神经元激活,通过在(𝑥,𝑦)位置应用核𝑖,然后应用ReLU非线性来计算,响应归一化激活𝑏𝑖𝑥,𝑦通过下式给定:
𝑏𝑖𝑥,𝑦=𝑎𝑖𝑥,𝑦/(𝑘+𝛼∑𝑗=𝑚𝑎𝑥(0,𝑖−𝑛/2)𝑚𝑖𝑛(𝑁−1,𝑖+𝑛/2)(𝑎𝑗𝑥,𝑦)2)𝛽
其中,𝑁是卷积核的个数,也就是生成的FeatureMap的个数;𝑘,𝛼,𝛽,𝑛是超参数,论文中使用的值是𝑘=2,𝑛=5,𝛼=10−4,𝛽=0.75
输出𝑏𝑖𝑥,𝑦和输入𝑎𝑗𝑥,𝑦的上标表示的是当前值所在的通道,也即是叠加的方向是沿着通道进行。将要归一化的值𝑎𝑖𝑥,𝑦所在附近通道相同位置的值的平方累加起来∑𝑚𝑖𝑛(𝑁−1,𝑖+𝑛/2)𝑗=𝑚𝑎𝑥(0,𝑖−𝑛/2)(𝑎𝑗𝑥,𝑦)2
Dropout
这个是比较常用的抑制过拟合的方法了。
引入Dropout主要是为了防止过拟合。在神经网络中Dropout通过修改神经网络本身结构来实现,对于某一层的神经元,通过定义的概率将神经元置为0,这个神经元就不参与前向和后向传播,就如同在网络中被删除了一样,同时保持输入层与输出层神经元的个数不变,然后按照神经网络的学习方法进行参数更新。在下一次迭代中,又重新随机删除一些神经元(置为0),直至训练结束。
Dropout应该算是AlexNet中一个很大的创新,现在神经网络中的必备结构之一。Dropout也可以看成是一种模型组合,每次生成的网络结构都不一样,通过组合多个模型的方式能够有效地减少过拟合,Dropout只需要两倍的训练时间即可实现模型组合(类似取平均)的效果,非常高效。
如下图:
Alex网络结构
image上图中的输入是224×224,不过经过计算(224−11)/4=54.75并不是论文中的55×55,而使用227×227作为输入,则(227−11)/4=55
网络包含8个带权重的层;前5层是卷积层,剩下的3层是全连接层。最后一层全连接层的输出是1000维softmax的输入,softmax会产生1000类标签的分布网络包含8个带权重的层;前5层是卷积层,剩下的3层是全连接层。最后一层全连接层的输出是1000维softmax的输入,softmax会产生1000类标签的分布。
卷积层C1
该层的处理流程是: 卷积-->ReLU-->池化-->归一化。
卷积,输入是227×227,使用96个11×11×3的卷积核,得到的FeatureMap为55×55×96。
ReLU,将卷积层输出的FeatureMap输入到ReLU函数中。
池化,使用3×3步长为2的池化单元(重叠池化,步长小于池化单元的宽度),输出为27×27×96((55−3)/2+1=27)
局部响应归一化,使用𝑘=2,𝑛=5,𝛼=10−4,𝛽=0.75进行局部归一化,输出的仍然为27×27×96,输出分为两组,每组的大小为27×27×48
卷积层C2
该层的处理流程是:卷积-->ReLU-->池化-->归一化
卷积,输入是2组27×27×48。使用2组,每组128个尺寸为5×5×48的卷积核,并作了边缘填充padding=2,卷积的步长为1. 则输出的FeatureMap为2组,每组的大小为27×27 𝑡𝑖𝑚𝑒𝑠128. ((27+2∗2−5)/1+1=27)
ReLU,将卷积层输出的FeatureMap输入到ReLU函数中
池化运算的尺寸为3×3,步长为2,池化后图像的尺寸为(27−3)/2+1=13,输出为13×13×256
局部响应归一化,使用𝑘=2,𝑛=5,𝛼=10−4,𝛽=0.75进行局部归一化,输出的仍然为13×13×256,输出分为2组,每组的大小为13×13×128
卷积层C3
该层的处理流程是: 卷积-->ReLU
卷积,输入是13×13×256,使用2组共384尺寸为3×3×256的卷积核,做了边缘填充padding=1,卷积的步长为1.则输出的FeatureMap为13×13 𝑡𝑖𝑚𝑒𝑠384
ReLU,将卷积层输出的FeatureMap输入到ReLU函数中
卷积层C4
该层的处理流程是: 卷积-->ReLU
该层和C3类似。
卷积,输入是13×13×384,分为两组,每组为13×13×192.使用2组,每组192个尺寸为3×3×192的卷积核,做了边缘填充padding=1,卷积的步长为1.则输出的FeatureMap为13×13 𝑡𝑖𝑚𝑒𝑠384,分为两组,每组为13×13×192
ReLU,将卷积层输出的FeatureMap输入到ReLU函数中
卷积层C5
该层处理流程为:卷积-->ReLU-->池化
卷积,输入为13×13×384,分为两组,每组为13×13×192。使用2组,每组为128尺寸为3×3×192的卷积核,做了边缘填充padding=1,卷积的步长为1.则输出的FeatureMap为13×13×256
ReLU,将卷积层输出的FeatureMap输入到ReLU函数中
池化,池化运算的尺寸为3×3,步长为2,池化后图像的尺寸为 (13−3)/2+1=6,即池化后的输出为6×6×256
全连接层FC6
该层的流程为:(卷积)全连接 -->ReLU -->Dropout
卷积->全连接: 输入为6×6×256,该层有4096个卷积核,每个卷积核的大小为6×6×256。由于卷积核的尺寸刚好与待处理特征图(输入)的尺寸相同,即卷积核中的每个系数只与特征图(输入)尺寸的一个像素值相乘,一一对应,因此,该层被称为全连接层。由于卷积核与特征图的尺寸相同,卷积运算后只有一个值,因此,卷积后的像素层尺寸为4096×1×1,即有4096个神经元。
ReLU,这4096个运算结果通过ReLU激活函数生成4096个值
Dropout,抑制过拟合,随机的断开某些神经元的连接或者是不激活某些神经元
全连接层FC7
流程为:全连接-->ReLU-->Dropout
全连接,输入为4096的向量
ReLU,这4096个运算结果通过ReLU激活函数生成4096个值
Dropout,抑制过拟合,随机的断开某些神经元的连接或者是不激活某些神经元
输出层
第七层输出的4096个数据与第八层的1000个神经元进行全连接,经过训练后输出1000个float型的值,这就是预测结果。
AlexNet参数数量
卷积层的参数 = 卷积核的数量 * 卷积核 + 偏置
C1: 96个11×11×3的卷积核,96×11×11×3+96=34848
C2: 2组,每组128个5×5×48的卷积核,(128×5×5×48+128)×2=307456
C3: 384个3×3×256的卷积核,3×3×256×384+384=885120
C4: 2组,每组192个3×3×192的卷积核,(3×3×192×192+192)×2=663936
C5: 2组,每组128个3×3×192的卷积核,(3×3×192×128+128)×2=442624
FC6: 4096个6×6×256的卷积核,6×6×256×4096+4096=37752832
FC7: 4096∗4096+4096=16781312
output: 4096∗1000=4096000
卷积层 C2,C4,C5中的卷积核只和位于同一GPU的上一层的FeatureMap相连。从上面可以看出,参数大多数集中在全连接层,在卷积层由于权值共享,权值参数较少。
完整代码
# -- encoding:utf-8 --
Create on 19/5/25 10:06
import os
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 定义外部传入的参数
tf.app.flags.DEFINE_bool(flag_name="is_train",
default_value=True,
docstring="给定是否是训练操作,True表示训练,False表示预测!!")
tf.app.flags.DEFINE_string(flag_name="checkpoint_dir",
default_value="./mnist/models/models_alext",
docstring="给定模型存储的文件夹,默认为./mnist/models/models_alext")
tf.app.flags.DEFINE_string(flag_name="logdir",
default_value="./mnist/graph/graph_alext",
docstring="给定模型日志存储的路径,默认为./mnist/graph/graph_alext")
tf.app.flags.DEFINE_integer(flag_name="batch_size",
default_value=16,
docstring="给定训练的时候每个批次的样本数目,默认为16.")
tf.app.flags.DEFINE_integer(flag_name="store_per_batch",
default_value=100,
docstring="给定每隔多少个批次进行一次模型持久化的操作,默认为100")
tf.app.flags.DEFINE_integer(flag_name="validation_per_batch",
default_value=100,
docstring="给定每隔多少个批次进行一次模型的验证操作,默认为100")
tf.app.flags.DEFINE_float(flag_name="learning_rate",
default_value=0.01,
docstring="给定模型的学习率,默认0.01")
FLAGS = tf.app.flags.FLAGS
def create_dir_with_not_exits(dir_path):
"""
如果文件的文件夹路径不存在,直接创建
:param dir_path:
:return:
"""
if not os.path.exists(dir_path):
os.makedirs(dir_path)
def conv2d(name, net, filter_height, filter_width, output_channels, stride=1, pandding='SAME'):
with tf.variable_scope(name):
input_channels = net.get_shape()[-1]
filter = tf.get_variable(name='w', shape=[filter_height, filter_width, input_channels, output_channels])
bias = tf.get_variable(name='b', shape=[output_channels])
net = tf.nn.conv2d(input=net, filter=filter, strides=[1, stride, stride, 1], padding=pandding)
net = tf.nn.bias_add(net, bias)
return net
def relu(name, net):
with tf.variable_scope(name):
return tf.nn.relu(net)
def max_pool(name, net, height, width, stride, padding='SAME'):
with tf.variable_scope(name):
return tf.nn.max_pool(value=net, ksize=[1, height, width, 1],
strides=[1, stride, stride, 1], padding=padding)
def fc(name, net, units):
with tf.variable_scope(name):
input_units = net.get_shape()[-1]
w = tf.get_variable(name='w', shape=[input_units, units])
b = tf.get_variable(name='b', shape=[units])
net = tf.matmul(net, w) + b
return net
def create_model(input_x, show_image=False):
"""
构建模型
:param input_x: 占位符,格式为[None, 784]
:return:
"""
# 定义一个网络结构: input -> conv -> relu -> pooling -> conv -> relu -> pooling -> FC -> relu -> FC
with tf.variable_scope("net"):
with tf.variable_scope("Input"):
# 这里定义一些图像的处理方式,包括:格式转换、基础处理(大小、剪切...)
x = tf.reshape(input_x, shape=[-1, 28, 28, 1])
# 这里做resize只是为了符合论文中的输入大小
x = tf.image.resize_nearest_neighbor(x, size=(224, 224))
with tf.device('/GPU:0'), tf.variable_scope("net11"):
net1 = conv2d('conv1', x, 11, 11, 48, 4)
net1 = relu('relu1', net1)
net1 = max_pool('pool1', net1, 2, 2, 2)
net1 = conv2d('conv2', net1, 5, 5, 128, 1)
net1 = relu('relu2', net1)
net1 = max_pool('pool2', net1, 2, 2, 2)
with tf.device('/GPU:1'), tf.variable_scope("net21"):
net2 = conv2d('conv1', x, 11, 11, 48, 4)
net2 = relu('relu1', net2)
net2 = max_pool('pool1', net2, 2, 2, 2)
net2 = conv2d('conv2', net2, 5, 5, 128, 1)
net2 = relu('relu2', net2)
net2 = max_pool('pool2', net2, 2, 2, 2)
# 合并两个网络的输出
net = tf.concat([net1, net2], axis=-1)
with tf.device('/GPU:0'), tf.variable_scope("net12"):
net1 = conv2d('conv3', net, 3, 3, 192, 1)
net1 = relu('relu3', net1)
net1 = conv2d('conv4', net1, 3, 3, 192, 1)
net1 = relu('relu4', net1)
net1 = conv2d('conv5', net1, 3, 3, 128, 1)
net1 = relu('relu5', net1)
net1 = max_pool('pool3', net1, 2, 2, 2)
with tf.device('/GPU:1'), tf.variable_scope("net22"):
net2 = conv2d('conv3', net, 3, 3, 192, 1)
net2 = relu('relu3', net2)
net2 = conv2d('conv4', net2, 3, 3, 192, 1)
net2 = relu('relu4', net2)
net2 = conv2d('conv5', net2, 3, 3, 128, 1)
net2 = relu('relu5', net2)
net2 = max_pool('pool3', net2, 2, 2, 2)
# 合并两个网络的输出
net = tf.concat([net1, net2], axis=-1)
shape = net.get_shape()
net = tf.reshape(net, shape=[-1, shape[1] * shape[2] * shape[3]])
# 做全连接操作
with tf.device('/GPU:0'), tf.variable_scope("net13"):
net1 = fc('fc1', net, 2048)
net1 = relu('relu6', net1)
with tf.device('/GPU:1'), tf.variable_scope("net23"):
net2 = fc('fc1', net, 2048)
net2 = relu('relu6', net2)
# 合并两个网络的输出
net = tf.concat([net1, net2], axis=-1)
# 做全连接操作
with tf.device('/GPU:0'), tf.variable_scope("net14"):
net1 = fc('fc2', net, 2048)
net1 = relu('relu7', net1)
with tf.device('/GPU:1'), tf.variable_scope("net24"):
net2 = fc('fc2', net, 2048)
net2 = relu('relu7', net2)
# 合并两个网络的输出
net = tf.concat([net1, net2], axis=-1)
# 全连接
logits = fc('fc3', net, 10)
with tf.variable_scope("Prediction"):
# 每行的最大值对应的下标就是当前样本的预测值
predictions = tf.argmax(logits, axis=1)
return logits, predictions
def create_loss(labels, logits):
"""
基于给定的实际值labels和预测值logits进行一个交叉熵损失函数的构建
:param labels: 是经过哑编码之后的Tensor对象,形状为[n_samples, n_class]
:param logits: 是神经网络的最原始的输出,形状为[n_samples, n_class], 每一行最大值那个位置对应的就是预测类别,没有经过softmax函数转换。
:return:
"""
with tf.name_scope("loss"):
# loss = tf.reduce_mean(-tf.log(tf.reduce_sum(labels * tf.nn.softmax(logits))))
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits))
tf.summary.scalar('loss', loss)
return loss
def create_train_op(loss, learning_rate=0.01, global_step=None):
"""
基于给定的损失函数构建一个优化器,优化器的目的就是让这个损失函数最小化
:param loss:
:param learning_rate:
:param global_step:
:return:
"""
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
train_op = optimizer.minimize(loss, global_step=global_step)
return train_op
def create_accuracy(labels, predictions):
"""
基于给定的实际值和预测值,计算准确率
:param labels: 是经过哑编码之后的Tensor对象,形状为[n_samples, n_class]
:param predictions: 实际的预测类别下标,形状为[n_samples,]
:return:
"""
with tf.name_scope("accuracy"):
# 获取实际的类别下标,形状为[n_samples,]
y_labels = tf.argmax(labels, 1)
# 计算准确率
accuracy = tf.reduce_mean(tf.cast(tf.equal(y_labels, predictions), tf.float32))
tf.summary.scalar('accuracy', accuracy)
return accuracy
def train():
# 对于文件是否存在做一个检测
create_dir_with_not_exits(FLAGS.checkpoint_dir)
create_dir_with_not_exits(FLAGS.logdir)
with tf.Graph().as_default():
# 一、执行图的构建
# 0. 相关输入Tensor对象的构建
input_x = tf.placeholder(dtype=tf.float32, shape=[None, 784], name='input_x')
input_y = tf.placeholder(dtype=tf.float32, shape=[None, 10], name='input_y')
global_step = tf.train.get_or_create_global_step()
# 1. 网络结构的构建
logits, predictions = create_model(input_x)
# 2. 构建损失函数
loss = create_loss(input_y, logits)
# 3. 构建优化器
train_op = create_train_op(loss,
learning_rate=FLAGS.learning_rate,
global_step=global_step)
# 4. 构建评估指标
accuracy = create_accuracy(input_y, predictions)
# 二、执行图的运行/训练(数据加载、训练、持久化、可视化、模型的恢复....)
with tf.Session() as sess:
# 获取一个日志输出对象
train_logdir = os.path.join(FLAGS.logdir, 'train')
validation_logdir = os.path.join(FLAGS.logdir, 'validation')
train_writer = tf.summary.FileWriter(logdir=train_logdir, graph=sess.graph)
validation_writer = tf.summary.FileWriter(logdir=validation_logdir, graph=sess.graph)
# a. 创建一个持久化对象(默认会将所有的模型参数全部持久化,因为不是所有的都需要的,最好仅仅持久化的训练的模型参数)
var_list = tf.trainable_variables()
# 是因为global_step这个变量是不参与模型训练的,所以模型不会持久化,这里加入之后,可以明确也持久化这个变量。
var_list.append(global_step)
saver = tf.train.Saver(var_list=var_list)
# a. 变量的初始化操作(所有的非训练变量的初始化 + 持久化的变量恢复)
# 所有变量初始化(如果有持久化的,后面做了持久化后,会覆盖的)
sess.run(tf.global_variables_initializer())
# 做模型的恢复操作
ckpt = tf.train.get_checkpoint_state(FLAGS.checkpoint_dir)
if ckpt and ckpt.model_checkpoint_path:
print("进行模型恢复操作...")
# 恢复模型
saver.restore(sess, ckpt.model_checkpoint_path)
# 恢复checkpoint的管理信息
saver.recover_last_checkpoints(ckpt.all_model_checkpoint_paths)
# 获取所有的summary输出操作
summary = tf.summary.merge_all()
# b. 训练数据的产生/获取(基于numpy随机产生<可以先考虑一个固定的数据集>)
mnist = input_data.read_data_sets(
train_dir='../datas/mnist', # 给定本地磁盘的数据存储路径
one_hot=True, # 给定返回的数据中是否对Y做哑编码
validation_size=5000 # 给定验证数据集的大小
)
# c. 模型训练
batch_size = FLAGS.batch_size
step = sess.run(global_step)
vn_accuracy_ = 0
while True:
# 开始模型训练
x_train, y_train = mnist.train.next_batch(batch_size=batch_size)
_, loss_, accuracy_, summary_ = sess.run([train_op, loss, accuracy, summary], feed_dict={
input_x: x_train,
input_y: y_train
})
print("第{}次训练后模型的损失函数为:{}, 准确率:{}".format(step, loss_, accuracy_))
train_writer.add_summary(summary_, global_step=step)
# 持久化
if step % FLAGS.store_per_batch == 0:
file_name = 'model_%.3f_%.3f_.ckpt' % (loss_, accuracy_)
save_path = os.path.join(FLAGS.checkpoint_dir, file_name)
saver.save(sess, save_path=save_path, global_step=step)
if step % FLAGS.validation_per_batch == 0:
vn_loss_, vn_accuracy_, vn_summary_ = sess.run([loss, accuracy, summary],
feed_dict={
input_x: mnist.validation.images,
input_y: mnist.validation.labels
})
print("第{}次训练后模型在验证数据上的损失函数为:{}, 准确率:{}".format(step,
vn_loss_,
vn_accuracy_))
validation_writer.add_summary(vn_summary_, global_step=step)
# 退出训练(要求当前的训练数据集上的准确率至少为0.8,然后最近一次验证数据上的准确率为0.8)
if accuracy_ > 0.99 and vn_accuracy_ > 0.99:
# 退出之前再做一次持久化操作
file_name = 'model_%.3f_%.3f_.ckpt' % (loss_, accuracy_)
save_path = os.path.join(FLAGS.checkpoint_dir, file_name)
saver.save(sess, save_path=save_path, global_step=step)
break
step += 1
# 关闭输出流
train_writer.close()
validation_writer.close()
def prediction():
# TODO: 参考以前的代码自己把这个区域的内容填充一下。我下周晚上讲。
# 做一个预测(预测的评估,对mnist.test这个里面的数据进行评估效果的查看)
with tf.Graph().as_default():
pass
def main(_):
if FLAGS.is_train:
# 进入训练的代码执行中
print("开始进行模型训练运行.....")
train()
else:
# 进入测试、预测的代码执行中
print("开始进行模型验证、测试代码运行.....")
prediction()
print("Done!!!!")
if __name__ == '__main__':
# 默认情况下,直接调用当前py文件中的main函数
tf.app.run()
网友评论