链接
在本文中,我将回顾Ioffe和Svegedy的批量规范化的有用性。 我还将在Keras中实现批量标准化,并在训练性能方面取得实质性进展。

批量归一化的直观解释

训练中的问题

问题1:随着网络训练,早期层的权重发生变化,因此后期层的输入变化很大。 每层必须根据每批输入的不同分布重新调整其权重。 这减缓了模型训练。 如果我们可以在分布中使层输入更相似,那么网络可以专注于学习类之间的差异。
不同批次分布的另一个影响是消失的梯度。 消失的梯度问题是一个大问题,特别是对于S形激活函数。 如果g(x)表示sigmoid激活函数,则为| x | 增加,g’(x)趋于零。
认识和实现:批量标准化 - 图1
问题2.当输入分布变化时,神经元输出也会变化。 这导致神经元输出偶尔波动到S形函数的可饱和区域。 在那里,神经元既不能更新自己的权重,也不能将梯度传递回先前的层。 我们如何保持神经元输出不变为可饱和区域?

如果我们可以将神经元输出限制在零附近的区域,我们可以确保每个层在反向传播期间都会传回一个实质的梯度。 这将导致更快的训练时间和更准确的结果。
认识和实现:批量标准化 - 图2

批量标准作为解决方案。

批量标准化减轻了不同层输入的影响。 通过归一化神经元的输出,激活函数将仅接收接近零的输入。 这确保了非消失的梯度,解决了第二个问题。
认识和实现:批量标准化 - 图3
批量归一化将层输出转换为单位高斯分布。 当这些输出通过激活功能馈送时,层激活也将变得更加正常分布。
由于一层的输出是下一层的输入,因此层输入现在具有明显较小的批次间差异。 通过减少层输入的变化分布,我们解决了第一个问题。

数学解释

通过批量归一化,我们为每个激活函数寻找以零为中心的单位方差分布。 在训练期间,我们采用激活输入x并将其减去批次均值μ以实现零中心分布。
认识和实现:批量标准化 - 图4
接下来,我们取x并将其除以批量方差和一个小数,以防止除以零σ+ε。 这可确保所有激活输入分布都具有单位差异。
认识和实现:批量标准化 - 图5
最后,我们将x hat进行线性转换以缩放并移动批量标准化的输出。 尽管在反向传播期间网络发生了变化,但仍能确保保持这种正常化效果。
认识和实现:批量标准化 - 图6
在测试模型时,我们不使用批处理均值或方差,因为这会破坏模型。 (提示:单个观察的平均值和方差是多少?)相反,我们计算训练群体的移动平均值和方差估计值。 这些估计值是培训期间计算的所有批次平均值和方差的平均值。

批量标准化的好处

批量标准化的好处如下。
1.有助于防止具有可饱和非线性(sigmoid,tanh等)的网络中的消失梯度
通过批量标准化,我们确保任何激活函数的输入不会变为可饱和区域。 批量归一化将这些输入的分布转换为单位高斯(零中心和单位方差)。
2.规范模型
也许。 Ioffe和Svegeddy提出了这一主张,但没有就此问题进行广泛撰写。 也许这是归一化层输入的结果?
3.允许更高的学习率
通过在训练期间防止梯度消失的问题,我们可以设置更高的学习率。 批量标准化还降低了对参数标度的依赖性。 较大的学习速率可以增加层参数的规模,这导致梯度在反向传播期间回传时放大。 我需要阅读更多关于此的内容。

在Keras实施

引入

  1. import tensorflow as tf
  2. import numpy as np
  3. import os
  4. import keras
  5. from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
  6. import matplotlib.pyplot as plt
  7. import matplotlib.image as mpimg
  8. from keras.models import Model, Sequential
  9. from keras.layers import Input
  10. from keras.callbacks import ModelCheckpoint, EarlyStopping
  11. from keras.layers import BatchNormalization
  12. from keras.layers import GlobalAveragePooling2D
  13. from keras.layers import Activation
  14. from keras.layers import Conv2D, MaxPooling2D, Dense
  15. from keras.layers import MaxPooling2D, Dropout, Flatten
  16. import time

数据加载和预处理
在这笔记本中,我们使用Cifar 100数据集,因为它具有相当的挑战性,并且不会永远用于训练。 唯一的预处理是零中心和图像变化发生器。

  1. from keras.datasets import cifar100
  2. from keras.utils import np_utils
  3. (x_train, y_train), (x_test, y_test) = cifar100.load_data(label_mode='fine')
  4. #scale and regularize the dataset
  5. x_train = (x_train-np.mean(x_train))
  6. x_test = (x_test - x_test.mean())
  7. x_train = x_train.astype('float32')
  8. x_test = x_test.astype('float32')
  9. #onehot encode the target classes
  10. y_train = np_utils.to_categorical(y_train)
  11. y_test = np_utils.to_categorical(y_test)
  12. train_datagen = ImageDataGenerator(
  13. shear_range=0.2,
  14. zoom_range=0.2,
  15. horizontal_flip=True)
  16. train_datagen.fit(x_train)
  17. train_generator = train_datagen.flow(x_train,
  18. y = y_train,
  19. batch_size=80,)

在Keras中构建模型

我们的架构将包括堆叠的3x3卷积,然后是最大池化和dropout。 每个网络中有5个卷积块。 最后一层是一个完全连接的层,有100个节点和softmax激活。
我们将构建4个不同的卷积网络,每个网络都具有sigmoid或ReLU激活以及批量标准化或不标准化。 我们将比较每个网络的验证损失。

  1. def conv_block_first(model, bn=True, activation="sigmoid"):
  2. """
  3. The first convolutional block in each architecture. Only separate so we can specify the input shape.
  4. """
  5. #First Stacked Convolution
  6. model.add(Conv2D(60,3, padding = "same", input_shape = x_train.shape[1:]))
  7. if bn:
  8. model.add(BatchNormalization())
  9. model.add(Activation(activation))
  10. #Second Stacked Convolution
  11. model.add(Conv2D(60,3, padding = "same"))
  12. if bn:
  13. model.add(BatchNormalization())
  14. model.add(Activation(activation))
  15. model.add(MaxPooling2D())
  16. model.add(Dropout(0.15))
  17. return model
  18. def conv_block(model, bn=True, activation = "sigmoid"):
  19. """
  20. Generic convolutional block with 2 stacked 3x3 convolutions, max pooling, dropout,
  21. and an optional Batch Normalization.
  22. """
  23. model.add(Conv2D(60,3, padding = "same"))
  24. if bn:
  25. model.add(BatchNormalization())
  26. model.add(Activation(activation))
  27. model.add(Conv2D(60,3, padding = "same"))
  28. if bn:
  29. model.add(BatchNormalization())
  30. model.add(Activation(activation))
  31. model.add(MaxPooling2D())
  32. model.add(Dropout(0.15))
  33. return model
  34. def conv_block_final(model, bn=True, activation = "sigmoid"):
  35. """
  36. 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.
  37. """
  38. model.add(Conv2D(100,3, padding = "same"))
  39. if bn:
  40. model.add(BatchNormalization())
  41. model.add(Activation(activation))
  42. model.add(Conv2D(100,3, padding = "same"))
  43. if bn:
  44. model.add(BatchNormalization())
  45. model.add(Activation(activation))
  46. model.add(Flatten())
  47. return model
  48. def fn_block(model):
  49. """
  50. I'm not going for a very deep fully connected block, mainly so I can save on memory.
  51. """
  52. model.add(Dense(100, activation = "softmax"))
  53. return model
  54. def build_model(blocks=3, bn=True, activation = "sigmoid"):
  55. """
  56. Builds a sequential network based on the specified parameters.
  57. blocks: number of convolutional blocks in the network, must be greater than 2.
  58. bn: whether to include batch normalization or not.
  59. activation: activation function to use throughout the network.
  60. """
  61. model = Sequential()
  62. model = conv_block_first(model, bn=bn, activation=activation)
  63. for block in range(1,blocks-1):
  64. model = conv_block(model, bn=bn, activation = activation)
  65. model = conv_block_final(model, bn=bn, activation=activation)
  66. model = fn_block(model)
  67. return model
  68. def compile_model(model, optimizer = "rmsprop", loss = "categorical_crossentropy", metrics = ["accuracy"]):
  69. """
  70. Compiles a neural network.
  71. model: the network to be compiled.
  72. optimizer: the optimizer to use.
  73. loss: the loss to use.
  74. metrics: a list of keras metrics.
  75. """
  76. model.compile(optimizer = optimizer,
  77. loss = loss,
  78. metrics = metrics)
  79. return model
  80. #COMPILING THE 4 MODELS
  81. sigmoid_without_bn = build_model(blocks = 5, bn=False, activation = "sigmoid")
  82. sigmoid_without_bn = compile_model(sigmoid_without_bn)
  83. sigmoid_with_bn = build_model(blocks = 5, bn=True, activation = "sigmoid")
  84. sigmoid_with_bn = compile_model(sigmoid_with_bn)
  85. relu_without_bn = build_model(blocks = 5, bn=False, activation = "relu")
  86. relu_without_bn = compile_model(relu_without_bn)
  87. relu_with_bn = build_model(blocks = 5, bn=True, activation = "relu")
  88. relu_with_bn = compile_model(relu_with_bn)

模特训练

没有批量标准化的Sigmoid
训练陷入困境。 有100个课程,这个模型从未达到比随机猜测更好的性能(10%准确度)。

  1. history1 = sigmoid_without_bn.fit_generator(
  2. train_generator,
  3. steps_per_epoch=2000,
  4. epochs=20,
  5. verbose=0,
  6. validation_data=(x_test, y_test),
  7. callbacks = [model_checkpoint])

认识和实现:批量标准化 - 图7

具有批量标准化的Sigmoid

与没有批量标准化不同,该模型在训练期间开始实施。 这可能是批量标准化减轻消失梯度的结果。

  1. history2 = sigmoid_with_bn.fit_generator(
  2. train_generator,
  3. steps_per_epoch=2000,
  4. verbose=0,
  5. epochs=20,
  6. validation_data=(x_test, y_test),
  7. callbacks = [model_checkpoint])

认识和实现:批量标准化 - 图8

没有批量标准化的ReLU

在没有批量规范的情况下实施ReLU导致一些初始收益,然后收敛到非最优的局部最小值。

  1. history3 = relu_without_bn.fit_generator(
  2. train_generator,
  3. steps_per_epoch=2000,
  4. epochs=20,
  5. verbose=0,
  6. validation_data=(x_test, y_test),
  7. callbacks = [model_checkpoint])

认识和实现:批量标准化 - 图9
具有批量标准化的ReLU
与sigmoid模型一样,批量标准化提高了该网络的训练能力。

  1. history4 = relu_with_bn.fit_generator(
  2. train_generator,
  3. steps_per_epoch=2000,
  4. verbose=0,
  5. epochs=20,
  6. validation_data=(x_test, y_test),
  7. callbacks = [model_checkpoint])

认识和实现:批量标准化 - 图10

比较架构

我们在这里清楚地看到批量标准化的好处。 没有批量标准化的ReLU和S形模型都无法保持训练性能提升。 这可能是渐变消失的结果。 具有批量标准化的体系结构训练得更快,并且比没有批量标准化的体系结构表现更好。
认识和实现:批量标准化 - 图11

Conclusion

结论
批量标准化减少了训练时间并提高了神经网络的稳定性。 此效果适用于sigmoid和ReLU激活功能。 原帖可以在我的网站上找到,代码可以在我的GitHub上找到。

Resources

Further reading

Below are some more recent research papers that extend Ioffe and Svegedy’s work.
[1] How Does Batch Normalization Help Optimization? (No, It Is Not About Internal Covariate Shift)
[2] Batch Renormalization: Towards Reducing Minibatch Dependence in Batch-Normalized Models
[3] Layer Normalization
[4] Weight Normalization: A Simple Reparameterization to Accelerate Training of Deep Neural Networks
[5] Group Normalization