批归一化-在TensorFlow中

woshigzp 2018-06-04

在之前的文章中,我介绍了批归一化,并希望对BN(Batch Normalization)进行粗略的了解。在这里我们将看到BN如何被纳入你的模型。我使用Tensorflow作为我的平台。

Tensorflow提供了很多灵活性和易用性。它提供了高级API和低级API。Estimator API提供超快速的方式来创建,训练和测试模型。Tensorflow还提供了一款交互式可视化工具TensorBoard。它有助于查看图或模型图。

数据集

我选择使用的数据集是CIFAR 10,这是一个著名的图像识别数据集。图像大小为32x32,有10个分类类别。它有5万张图片需要训练,1万张图片需要测试。一个简单的神经网络,包含3个conv层和2个密集层,就足够学习数据集了。

批归一化-在TensorFlow中

卷积层

谷歌Colaboratory

Colaboratory是一个谷歌研究项目,旨在帮助传播机器学习教育和研究。这是一个Jupyter notebook 环境,不需要任何设置就可以使用,完全在云中运行。它甚至提供了一个GPU加速器来加速训练。所有这些都是免费的,如果你有谷歌账户。

这里做的模型可以在这里作为Colab笔记本。当你阅读这篇文章时,你可以尝试使用notebook 。启动时,进入编辑->笔记本设置->选择Accelerator -> GPU,使训练进行得更快。

如果您想在本地系统中运行笔记本,请克隆此https://github.com/ilango100/cifar。

定义模型

该模型首先被定义为通用的convnet规范,Python代码如下:

import tensorflow as tf

def model(features, labels, mode, params):

net = features['images']

for filt, kern, stride in zip(params['filters'], params['kern'], params['strides']):

if params['with_bn']:

net = tf.layers.batch_normalization(net,

training= mode==tf.estimator.ModeKeys.TRAIN)

net = tf.layers.conv2d(net, filt,

kern, stride, activation=tf.nn.relu)

net = tf.layers.flatten(net)

for units in params['dense']:

if params['with_bn']:

net = tf.layers.batch_normalization(net,

training= mode==tf.estimator.ModeKeys.TRAIN)

net = tf.layers.dense(net, units,

activation=tf.nn.relu)

if params['with_bn']:

net = tf.layers.batch_normalization(net,

training= mode==tf.estimator.ModeKeys.TRAIN)

logits = tf.layers.dense(net, params['n_classes'])

cls = tf.argmax(logits, -1)

if mode == tf.estimator.ModeKeys.PREDICT:

return tf.estimator.EstimatorSpec(mode, predictions={

"class": cls,

"score": tf.nn.softmax(logits)

})

loss = tf.losses.sparse_softmax_cross_entropy(labels, logits)

if mode == tf.estimator.ModeKeys.EVAL:

return tf.estimator.EstimatorSpec(mode, loss=loss, eval_metric_ops={

"accuracy": tf.metrics.accuracy(labels, cls)

})

adam = tf.train.AdamOptimizer()

if params['with_bn']:

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

with tf.control_dependencies(update_ops):

opt = adam.minimize(loss, global_step=tf.train.get_global_step())

else:

opt = adam.minimize(loss, global_step=tf.train.get_global_step())

return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=opt)

在第43-46行,这是我们告诉Tensorflow在训练时更新均值和方差的移动平均值的地方。包含这一点很重要,很多人往往会忘记。确保您包含在训练期间更新移动平均数。

由于这个模型是通用的,所以它建立了一个卷积神经网络,给定超参数为参数。对于我的模型,我已经给出了超参数:

hparams = {

"filters": [30, 50, 60],

"kern": [[3, 3]]*3,

"strides": [[2, 2], [1, 1], [1, 1]],

"dense": [3500, 700],

"n_classes": 10,

"with_bn": True

}

您可以调整参数,如滤波器,内核,步幅,甚至在conv层之后的密集图层和单元的数量。试试这些值,看看你能找到什么。另外,'with_bn'可以改变为包含BatchNorm或不包含。首先,我们禁用它并训练数据集。然后我们启用BatchNorm并训练来比较结果。

激活之前或之后的BatchNorm

在之前的文章中,我们看到BatchNorm可以在非线性之前或之后应用,这仍然是一个争论的问题。在当前的模型中,我决定在激活之后使用BatchNorm,更具体地说,在输入每层之前,因为引入了BatchNorm来减少内部协变量移位。至于是否使用缩放和移位,我决定现在就使用它们。

池化层

当图像的大小很大并且需要缩小时,池化层主要用于卷积网络。它还提供平移和旋转不变性,即,如果旋转特征映射的区域,则会提供相同的输出。但在大多数情况下,可以通过增加conv层的大小来实现尺寸缩小。本文研究了这些模型,并表明根本不需要池化层。

一些图像识别模型使用池化,有些仅使用大于1的步幅。在我的模型中,我决定不使用池化层。相反,我在第一层中使用了2x2的步幅,以降低分辨率和连续层数的1x1步幅。

我们开始训练吧

我强烈建议使用Google Colab和GPU加速器进行培训,除非系统中有强大的GPU。让我们开始训练模型。由于使用了Estimator API,因此学习的参数会在训练时自动保存。训练图可以在TensorBoard上查看。

没有BN的版本运行400步,而BN只运行到100步。对于每两个训练周期,都要进行一次测试。结果如下:

批归一化-在TensorFlow中

Loss without BatchNorm

批归一化-在TensorFlow中

Loss with BatchNorm

正如我们所看到的,没有BN的模型达到了350步,而BN达到了70!以BN进行训练的速度要快5倍。批次归一化后全部工作!

这个模型只有65 - 70%的准确率。许多模型采用数据增强来提高准确性。

过度拟合

正如我们在图中看到的,在训练中测试损失(蓝色)会上升——这是明显的过拟合。目前,我只专注于知道BN是如何将常规网络连接起来的。我没有使用正则化。

BN也提供了一种弱的正则化形式。但是,由于它不能防止过度拟合,因此应该与任何其他正则化层(如dropout)一起使用。

相关推荐