松鼠乐园 松鼠乐园
  • 注册
  • 登录
  • 首页
  • 快捷入口
    • Vue
    • Tensorflow
    • Springboot
    • 语言类
      • CSS
      • ES5
      • ES6
      • Go
      • Java
      • Javascript
    • 工具类
      • Git
      • 工具推荐
    • 服务器&运维
      • Centos
      • Docker
      • Linux
      • Mac
      • MySQL
      • Nginx
      • Redis
      • Windows
    • 资源类
      • 论文
      • 书籍推荐
      • 后端资源
      • 前端资源
      • html网页模板
      • 代码
    • 性能优化
    • 测试
  • 重大新闻
  • 人工智能
  • 开源项目
  • Vue2.0从零开始
  • 广场
首页 › 人工智能 › 原理解释|直觉与实现:Batch Normalization

原理解释|直觉与实现:Batch Normalization

迦娜王
3年前人工智能
548 0 0

在本文中,会回顾一下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。

原理解释|直觉与实现:Batch Normalization

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

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

原理解释|直觉与实现:Batch Normalization

Batch Norm解决方案

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

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

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

数学解释

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

原理解释|直觉与实现:Batch Normalization

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

原理解释|直觉与实现:Batch Normalization

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

原理解释|直觉与实现: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 os
 
import keras
from keras.preprocessing.image import ImageDataGenerator, array_to_img,img_to_array, load_img
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
 
from keras.models import Model, Sequential
from keras.layers import Input
 
from 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, Flatten
 
import time

数据加载和预处理

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

 from keras.datasets import cifar100
 from keras.utils import np_utils
 
 (x_train, y_train), (x_test, y_test) = cifar100.load_data(label_mode=\\'fine\\')
 
 #scale and regularize the dataset
 x_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 classes
 y_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中构建模型

我们的网络结构由 3×3 的卷积层堆叠而成,卷积后面接最大化池化和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 Convolution
 model.add(Conv2D(60,3, padding = "same", input_shape = x_train.shape[1:]))
 if bn:
 model.add(BatchNormalization())
 model.add(Activation(activation))
 #Second Stacked Convolution
 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 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 model
 
def 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 model
 
 def 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 model
 
 def 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 model
 
 def 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 MODELS
 sigmoid_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])
原理解释|直觉与实现:Batch Normalization

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])
原理解释|直觉与实现:Batch Normalization

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])
原理解释|直觉与实现:Batch Normalization

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

不同结构的对比

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

原理解释|直觉与实现:Batch Normalization

结论

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

Batch Normalization
0
通用物体检测数据集 Objects365
上一篇
理解二分类交叉熵|可视化的方法解释对数损失
下一篇
评论 (0)

请登录以参与评论。

现在登录
聚合文章
Servicios profesionales Organizaciones
1年前
在Gitee收获近 5k Star,更新后的Vue版RuoYi有哪些新变化?
1年前
vue3.x reactive、effect、computed、watch依赖关系及实现原理
1年前
Vue 3 新特性:在 Composition API 中使用 CSS Modules
1年前
标签
AI AI项目 css docker Drone Elaticsearch es5 es6 Geometry Go gru java Javascript jenkins lstm mysql mysql优化 mysql地理位置索引 mysql索引 mysql规范 mysql设计 mysql配置文件 mysql面试题 mysql高可用 nginx Redis redis性能 rnn SpringBoot Tensorflow tensorflow2.0 UI设计 vue vue3.0 vue原理 whistle ZooKeeper 开源项目 抓包工具 日志输出 机器学习 深度学习 神经网络 论文 面试题
相关文章
我收集了12款自动生成器,无聊人士自娱自乐专用
输入一张图,就能让二次元老婆动起来,宛如3D:这全是为了科学啊
使用ONNX+TensorRT部署人脸检测和关键点250fps
基于 Keras 的烟火检测
松鼠乐园

资源整合,创造价值

小伙伴
墨魇博客 无同创意
目录
重大新闻 Centos CSS Docker ES5 ES6 Go Java Javascript Linux Mac MySQL Nginx Redis Springboot Tensorflow Vue Vue2.x从零开始 Windows 书籍推荐 人工智能 前端资源 后端资源 壁纸 开源项目 测试 论文
Copyright © 2018-2022 松鼠乐园. Designed by nicetheme. 浙ICP备15039601号-4
  • 重大新闻
  • Centos
  • CSS
  • Docker
  • ES5
  • ES6
  • Go
  • Java
  • Javascript
  • Linux
  • Mac
  • MySQL
  • Nginx
  • Redis
  • Springboot
  • Tensorflow
  • Vue
  • Vue2.x从零开始
  • Windows
  • 书籍推荐
  • 人工智能
  • 前端资源
  • 后端资源
  • 壁纸
  • 开源项目
  • 测试
  • 论文
热门搜索
  • jetson nano
  • vue
  • java
  • mysql
  • 人工智能
  • 人脸识别
迦娜王
坚持才有希望
1224 文章
35 评论
242 喜欢
  • 0
  • 0
  • Top