https://www.toutiao.com/a6707566287964340747/

作者:Harrison Jansma编译:ronghuaiyang

在本文中,我会回顾一下batch normalization的用处。我也会在Keras中实现一下batch normalization,并在训练中得到了实际的提升。代码可以在https://github.com/harrisonjansma/Research-Computer-Vision/tree/master/07-28-18-Implementing-Batch-Norm找到。

Batch Normalization的一个直觉的解释

训练中的问题

问题1:当网络在训练时,前一层的权值会变换,导致后面的层的输入也会变化的比较厉害。每一层都必须调整权值来适应每个batch的输入的分布。这会使得模型的训练变慢。如果我们可以让每一层的输入的分布变得相似,那么整个网络就会把精力集中在训练不同的类别上,而不是适应不同的分布上。

另外一个batch之间不同的分布的影响是梯度的消失。梯度消失问题是一个大问题,特别是对于sigmoid的激活函数。如果g(x)表示sigmoid激活函数,当|x| 增加,g′(x) 趋向于0。

问题1,当输入的分布变化时,神经网络的输出也在变化。这就导致了神经网络的输出偶尔会进入到sigmoid函数的饱和区域。一旦到了饱和区域,神经元就无法更新权值了,没有梯度回传到前面的层去。那么,我们如何防止神经元的输出变化到饱和区域呢?

如果我们可以限制神经元的输出在0的附近,我们可以确保每一层都可以通过反向传播回传比较大的梯度。这会使得训练速度加快,得到更加准确的结果。

Batch Norm解决方案

Batch normalization减轻了输入的变化的影响。通过对神经元的输出进行归一化,激活函数的输入都是在0附近的,这就保证了没有梯度的消失,解决了第二个问题。

Batch normalization将每一层的输出变换成一个单位的高斯分布。由于这些输出被输入到一个激活函数中,激活后的值也是一个正态的分布。

因为一层的输出是下一层的输入,每一层的输入的分布对于不同的batch来说就不会有太大的变化。通过减小输入层的分布的变化,我们解决了第一个问题。

数学解释

通过batch normalization,我们寻找一个以0为中心的,单位方差的分布作为每一层的激活函数的输入。在训练的时候,我们用激活的输入x减去这个batch中的均值μ来得到以0为中心的分布。

然后,我们用x除以这个batch的方差,这里需要一个很小的数来防止除0操作, 也就是σ+ϵ。这样确保了所有的激活函数的输入分布具有单位方差。

最后,我们将x通过一个线性变换,通过一个缩放和偏移,得到了 batch normalization的输出。确保这个归一化的作用会保持住,尽管网络在反向传播的时候会有变化。

当测试模型的时候,我们并不使用batch的均值和方差,因为这可能影响模型。而是计算均值和方差的移动平均来估计训练集的分布。这样的估计是在训练的过程中对所有的batch的均值和方差进行计算得到的。

Batch Normalization的好处

batch normalization的好处如下:

1. 帮助防止网络中的梯度消失线性,特别是使用饱和的非线性激活函数的时候(如sigmoid,tanh)

使用batch normalization,我们确保激活函数的输入不会落入到饱和区域。batch normalization将输入的分布变换到单位高斯分布(0均值,单位方差)。

2. 模型正则化

也许有,Ioffe和Svegeddy声称有这个作用,但是并没有在这个问题上展开说。也许这个效果来自于层的输入的归一化?

3. 允许更高的学习率

通过防止训练时候梯度消失的问题,我们可以使用更高的学习率。Batch normalization同样减少了对于参数尺度的依赖。大的学习了可以增加参数的尺度,从而在反向传播的时候造成梯度的放大,对于这,我需要更多了解一下。

Keras的实现

导入包

import tensorflow as tf
import numpy as np
import osimport keras
from keras.preprocessing.image import ImageDataGenerator, array_to_img,img_to_array, load_img
import matplotlib.pyplot as plt
import matplotlib.image as mpimgfrom keras.models import Model, Sequential
from keras.layers import Inputfrom keras.callbacks import ModelCheckpoint, EarlyStopping
from keras.layers import BatchNormalization
from keras.layers import GlobalAveragePooling2D
from keras.layers import Activation
from keras.layers import Conv2D, MaxPooling2D, Dense
from keras.layers import MaxPooling2D, Dropout, Flattenimport time

数据加载和预处理

在这里,我们使用了 Cifar 100的数据集,难度合理,不会训练很长时间。预处理只做了0均值的处理,以及一个图像的变换的生成器。

 from keras.datasets import cifar100from keras.utils import np_utils(x_train, y_train), (x_test, y_test) = cifar100.load_data(label_mode='fine')#scale and regularize the datasetx_train = (x_train-np.mean(x_train))x_test = (x_test - x_test.mean())x_train = x_train.astype('float32')x_test = x_test.astype('float32')#onehot encode the target classesy_train = np_utils.to_categorical(y_train)y_test = np_utils.to_categorical(y_test)train_datagen = ImageDataGenerator(shear_range=0.2,zoom_range=0.2,horizontal_flip=True)train_datagen.fit(x_train)train_generator = train_datagen.flow(x_train,y = y_train,batch_size=80,)

在Keras中构建模型

我们的网络结构由 3x3 的卷积层堆叠而成,卷积后面接最大化池化和dropout。每个网络中有5个卷积block。最后一层是全连接层,有100个节点,使用softmax作为激活函数。

我们构建了4个不同的卷积神经网络,每个或者使用sigmoid或者使用ReLU激活函数,或者使用了 batch normalization,或者没有。我们会对比每个网络的有效的loss。

def conv_block_first(model, bn=True, activation="sigmoid"):"""The first convolutional block in each architecture. Only separate so we can specify the input shape.""" #First Stacked Convolutionmodel.add(Conv2D(60,3, padding = "same", input_shape = x_train.shape[1:]))if bn:model.add(BatchNormalization())model.add(Activation(activation))#Second Stacked Convolutionmodel.add(Conv2D(60,3, padding = "same"))if bn:model.add(BatchNormalization())model.add(Activation(activation))model.add(MaxPooling2D())model.add(Dropout(0.15))return model
def conv_block(model, bn=True, activation = "sigmoid"):"""Generic convolutional block with 2 stacked 3x3 convolutions, max pooling, dropout, and an optional Batch Normalization."""model.add(Conv2D(60,3, padding = "same"))if bn:model.add(BatchNormalization())model.add(Activation(activation))model.add(Conv2D(60,3, padding = "same"))if bn:model.add(BatchNormalization())model.add(Activation(activation))model.add(MaxPooling2D())model.add(Dropout(0.15))return modeldef conv_block_final(model, bn=True, activation = "sigmoid"):"""I bumped up the number of filters in the final block. I made this separate so that I might be able to integrate Global Average Pooling later on. """model.add(Conv2D(100,3, padding = "same"))if bn:model.add(BatchNormalization())model.add(Activation(activation))model.add(Conv2D(100,3, padding = "same"))if bn:model.add(BatchNormalization())model.add(Activation(activation))model.add(Flatten())return modeldef fn_block(model):"""I'm not going for a very deep fully connected block, mainly so I can save on memory."""model.add(Dense(100, activation = "softmax"))return modeldef build_model(blocks=3, bn=True, activation = "sigmoid"):"""Builds a sequential network based on the specified parameters.blocks: number of convolutional blocks in the network, must be greater than 2.bn: whether to include batch normalization or not.activation: activation function to use throughout the network."""model = Sequential()model = conv_block_first(model, bn=bn, activation=activation)for block in range(1,blocks-1):model = conv_block(model, bn=bn, activation = activation)model = conv_block_final(model, bn=bn, activation=activation)model = fn_block(model)return modeldef compile_model(model, optimizer = "rmsprop", loss ="categorical_crossentropy", metrics = ["accuracy"]):"""Compiles a neural network.model: the network to be compiled.optimizer: the optimizer to use.loss: the loss to use.metrics: a list of keras metrics."""model.compile(optimizer = optimizer,loss = loss,metrics = metrics)return model#COMPILING THE 4 MODELSsigmoid_without_bn = build_model(blocks = 5, bn=False, activation ="sigmoid")sigmoid_without_bn = compile_model(sigmoid_without_bn)sigmoid_with_bn = build_model(blocks = 5, bn=True, activation = "sigmoid")sigmoid_with_bn = compile_model(sigmoid_with_bn)relu_without_bn = build_model(blocks = 5, bn=False, activation = "relu")relu_without_bn = compile_model(relu_without_bn)relu_with_bn = build_model(blocks = 5, bn=True, activation = "relu")relu_with_bn = compile_model(relu_with_bn)

Model训练

Sigmoid不使用Batch Normalization

训练卡住了,使用100个类,模型并不比随机猜好(10%的准确率)。

history1 = sigmoid_without_bn.fit_generator(train_generator,steps_per_epoch=2000,epochs=20,verbose=0,validation_data=(x_test, y_test),callbacks = [model_checkpoint])

Sigmoid使用Batch Normalization

和不用 batch normalization不一样,模型总算是有点起色了,这应该是 batch normalization的减轻了梯度消失的作用。

history2 = sigmoid_with_bn.fit_generator(train_generator,steps_per_epoch=2000,verbose=0,epochs=20,validation_data=(x_test, y_test),callbacks = [model_checkpoint])

ReLU不使用Batch Normalization

使用ReLU,不使用 batch normalization,有一点初始的提升,收敛到了一个局部最小中。

history3 = relu_without_bn.fit_generator(train_generator,steps_per_epoch=2000,epochs=20,verbose=0,validation_data=(x_test, y_test),callbacks = [model_checkpoint])

ReLU使用Batch Normalization

和sigmoid一样, batch normalization在训练中提高了网络的能力。

history4 = relu_with_bn.fit_generator(train_generator,steps_per_epoch=2000,verbose=0,epochs=20,validation_data=(x_test, y_test),callbacks = [model_checkpoint])

不同结构的对比

我们可以清楚的看到 batch normalization的好处。ReLU 和sigmoid 的模型在没有batch normalization的时候,都没有训练的很好。可能是梯度消失的原因。使用了batch normalization的模型训练的更快,而且效果更好。

结论

batch normalization减少了训练的时间,提高了神经网络的稳定性。对于sigmoid和ReLU都有效果。

原文链接:https://towardsdatascience.com/intuit-and-implement-batch-normalization-c05480333c5b

原理解释|直觉与实现:Batch Normalization相关推荐

  1. 五个角度解释深度学习中 Batch Normalization为什么效果好?

    https://www.toutiao.com/a6699953853724361220/ 深度学习模型中使用Batch Normalization通常会让模型得到更好表现,其中原因到底有哪些呢?本篇 ...

  2. 一文搞懂BN的原理及其实现过程(Batch Normalization)

    1. 在讲BN之前我要向大家提出几个问题,就是为什么要引入BN呢?在神经网络训练的时候输入图片不是引入了image normalization吗?所以BN到底是什么呢? Batch Normaliza ...

  3. Batch Normalization的作用及原理

    目录 声明 BN是什么[1] 为什么提出BN[1, 2] BN的作用及原理 加速训练,提高收敛速度[1] 缓解梯度消失(梯度爆炸)[3] 缓解过拟合[4] 其他相关问题 BN和激活函数的顺序问题[5] ...

  4. 卷积神经网络CNN(2)—— BN(Batch Normalization) 原理与使用过程详解

    前言 Batch Normalization是由google提出的一种训练优化方法.参考论文:Batch Normalization Accelerating Deep Network Trainin ...

  5. t-sne原理解释_T-SNE解释-数学与直觉

    t-sne原理解释 The method of t-distributed Stochastic Neighbor Embedding (t-SNE) is a method for dimensio ...

  6. Batch Normalization原理及pytorch的nn.BatchNorm2d函数

    下面通过举个例子来说明Batch Normalization的原理,我们假设在网络中间经过某些卷积操作之后的输出的feature map的尺寸为4×3×2×2,4为batch的大小,3为channel ...

  7. 批归一化作用_批归一化Batch Normalization的原理及算法

    一.BN提出的背景意义 本文的背景文献是:<Batch Normalization: Accelerating Deep Network Training by Reducing Interna ...

  8. Batch Normalization详解(原理+实验分析)

    Batch Normalization详解(原理+实验分析) 1. 计算过程 2. 前向传播过程 3. 反向传播过程 4. 实验分析 4.1 实验一:验证有没有BatchNorm下准确率的区别 4.2 ...

  9. 批标准化(Batch Normalization )最详细易懂的解释

    12. 批标准化(Batch Normalization ) 大纲:Tips for Training Deep Network Training Strategy: Batch Normalizat ...

最新文章

  1. 洛谷P1896 [SCOI2005]互不侵犯 状压dp+位运算
  2. 陆奇“入驻” YC,开启新征程
  3. php 5.3 construct_PHP 5.3新增魔术方法__invoke概述
  4. 因子分析——因子旋转
  5. CSDN开设博客专栏的方法
  6. “命令终端”的实现2-字符读取及按键控制
  7. 解决”不允许一个用户使用一个以上用户名与一个服务器或共享资源的多重连接“问题
  8. python 提示框如何顶层显示_python tkinter之顶层菜单、弹出菜单实例
  9. c++数学函数运算,浮点数据相等判断
  10. python 运行报错 Process finished with exit code -1073740791 (0xC0000409)
  11. python数据可视化学习之随机漫步
  12. 软件工程师必须掌握的知识结构
  13. HDU6598 Harmonious Army
  14. Ubuntu 12.04硬盘安装与U盘安装(图文)
  15. java二维数组添加数据_Java自学路线图
  16. 2018 Google 开发者大会终于来了!
  17. c语言如何清除scanf缓存,C语言如何清除scanf()缓存
  18. 是否允许应用获取设备信息_手机权限获取弹窗不断,隐私安全如何确保,这些权限需谨慎...
  19. 常用的CSS命名规范大总结
  20. 解锁ESP32-C3的GPIO11

热门文章

  1. 接受者操作特征曲线ROC
  2. C++ size_t 与 size_type区别
  3. JAVA-OPTS引发的思考
  4. 图灵奖得主门徒、RISC-V 创始成员领衔,睿思芯科获数千万美金融资 | AI 创业周报第6期...
  5. 如何设计一款暗度陈仓的反爬虫
  6. 自定义一个安全的rm指令
  7. 张立贤:积跬步至千里,我与地学大数据的探索之旅 | 提升之路系列(五)
  8. 人类高质量AI训练方式:精细化数据集管理颠覆唯SOTA论
  9. 权威解读 | 世界互联网大会蓝皮书
  10. 多语言互通:谷歌发布实体检索模型,涵盖超过100种语言和2000万个实体