博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深度学习基础系列(七)| Batch Normalization
阅读量:6245 次
发布时间:2019-06-22

本文共 8281 字,大约阅读时间需要 27 分钟。

  Batch Normalization(批量标准化,简称BN)是近些年来深度学习优化中一个重要的手段。BN能带来如下优点:

  • 加速训练过程;
  • 可以使用较大的学习率;
  • 允许在深层网络中使用sigmoid这种易导致梯度消失的激活函数;
  • 具有轻微地正则化效果,以此可以降低dropout的使用。

  但为什么BN能够如此有效?让我们来一探究竟。

一、Covariate Shift

  Convariate shift是BN论文作者提出来的概念,其意是指具有不同分布的输入值对深度网络学习的影响。举个例子,假设我们有一个玫瑰花的深度学习网络,这是一个二分类的网络,1表示识别为玫瑰,0则表示非玫瑰花。我们先看看训练数据集的一部分:

  直观来说,玫瑰花的特征表现很明显,都是红色玫瑰花。 再看看训练数据集的另一部分:

  很明显,这部分数据的玫瑰花各种颜色都有,其特征分布与上述数据集是不一样的。通过下图我们可以再比较下:

  图中右侧部分绿色圆圈指的是玫瑰,红色打叉指的是非玫瑰,蓝色线为深度学习最后训练出来的边界。这张图可以更加直观地比较出两个数据集的特征分布是不一样的,这种不一样也就是所谓的covariate shift,而这种分布不一致将减缓训练速度。

  为什么这么说呢?输入值的分布不同,也可以理解为输入特征值的scale差异较大,与权重进行矩阵相乘后,会产生一些偏离较大地差异值;而深度学习网络需要通过训练不断更新完善,那么差异值产生的些许变化都会深深影响后层,偏离越大表现越为明显;因此,对于反向传播来说,这些现象都会导致梯度发散,从而需要更多的训练步骤来抵消scale不同带来的影响,也需要更多地步骤才能最终收敛。

  而BN的作用就是将这些输入值进行标准化,降低scale的差异至同一个范围内。这样做的好处在于一方面提高梯度的收敛程度,加快训练速度;另一方面使得每一层可以尽量面对同一特征分布的输入值,减少了变化带来的不确定性,也降低了对后层网路的影响,各层网路变得相对独立。

  也许上述的解释可能有些晦涩,让我们通过代码和图像来直观理解。

 二、Batch Normalization

  BN的计算公式如下图所示:

  简单地说,通过计算均值和方差后,mini-batch的数据进行标准化,再加上β和γ可以使数据进行移动和缩放。

  我们以某个CIFAR-10的数据为例,通过BN的转换来查看数据分布的前后变化,以下为示例代码:

import numpy as npimport matplotlib.pyplot as pltimport tensorflow.keras.backend as Kfrom tensorflow import keras(train_images, train_labels), (test_images, test_labels) = keras.datasets.cifar10.load_data()# 输入图片尺寸为(32, 32, 3),经flatten后大小为(3072, 1)x = train_images[0].reshape(-1) / 255print("x:", x.shape)# 假设我们的隐藏层第一层的输出为(1024, 1),则反推权重大小为(1024, 3072)w = K.eval(K.random_normal_variable(shape=(1024, 3072), mean=0, scale=1))print("w:", w.shape)# 进行矩阵乘法得到大小为(1024, 1)的集合zz = np.dot(w, x)print("z:", z.shape)a = K.constant(z)# 求均值mean = K.mean(a)print("mean:", K.eval(mean))var = K.var(a)# 求方差print("var:", K.eval(var))# 对z进行batch normalization,gamma为0表示不进行移动,beta为0.25表示将normal后的值压缩至1/4大小a = K.eval(K.batch_normalization(a, mean, var, 0, 0.25))# flatten normal值a = a.reshape(-1)print("batch_normal_a:", a.shape)#以图的方式直观展示without_BN和with_BN的区别p1 = plt.subplot(211)p1.hist(z, 50, density=1, facecolor='g', alpha=0.75)p1.set_title("data distribution without BN")p1.set_xlabel('data range')p1.set_ylabel('probability')p1.grid(True)#p1.axis([-4, 4, 0, 1])p2 = plt.subplot(212)p2.hist(a, 50, density=1, facecolor='g', alpha=0.75)p2.set_title("data distribution with BN")p2.set_xlabel('data range')p2.set_ylabel('probability')p2.grid(True)#p2.axis([-4, 4, 0, 1])plt.subplots_adjust(hspace=1)plt.show()

  其图像为:

 

  从图像分析可知,数据经过标准化后,其形状保持大致不变,但尺寸被我们压缩至(-1, 1)之间,而原尺寸在(-80,80)之间。

  通过平移和缩放,BN可以使数据被限定在我们想要的范围内,所以每层的输出数据都进行BN的话,可以使后层网络面对稳定的输入值,降低梯度发散的可能,从而加快训练速度;同时也意味着允许使用大点的学习率,加快收敛过程。

  被缩放的数据让使用sigmoid或tanh激活函数在深层网络变成可能,并且在实际应用中β和γ是可以学习的,下图是一个直观的解释图。

  

三、Batch Normalizaiotn的实际应用

  理论结合实践才能确定是否有用,让我们以keras举例,看看BN是否能提高效率。

  

  上图简要地绘制了BN在神经网络中的位置,在每层网络的激活函数前。与前述例子不同之处在于,数据不是单个进行标准化,而是以mini batch集合的方式进行标准化。

  我们通过下述代码来比较和观察without_BN模型和with_BN模型的差异:  

import kerasfrom keras.datasets import cifar10from keras.preprocessing.image import ImageDataGeneratorfrom keras.models import Sequentialfrom keras.layers import Dense, Dropout, Activation, Flatten, BatchNormalizationfrom keras.layers import Conv2D, MaxPooling2Dfrom matplotlib import pyplot as pltimport numpy as np# 为保证公平起见,两种方式都使用相同的随机种子np.random.seed(7)batch_size = 32num_classes = 10epochs = 100data_augmentation = True# The data, split between train and test sets:(x_train, y_train), (x_test, y_test) = cifar10.load_data()# Convert class vectors to binary class matrices.y_train = keras.utils.to_categorical(y_train, num_classes)y_test = keras.utils.to_categorical(y_test, num_classes)x_train = x_train.astype('float32')x_test = x_test.astype('float32')x_train /= 255x_test /= 255# without_BN模型的训练model_without_bn = Sequential()model_without_bn.add(Conv2D(32, (3, 3), padding='same',                            input_shape=x_train.shape[1:]))model_without_bn.add(Activation('relu'))model_without_bn.add(Conv2D(32, (3, 3)))model_without_bn.add(Activation('relu'))model_without_bn.add(MaxPooling2D(pool_size=(2, 2)))model_without_bn.add(Dropout(0.25))model_without_bn.add(Conv2D(64, (3, 3), padding='same'))model_without_bn.add(Activation('relu'))model_without_bn.add(Conv2D(64, (3, 3)))model_without_bn.add(Activation('relu'))model_without_bn.add(MaxPooling2D(pool_size=(2, 2)))model_without_bn.add(Dropout(0.25))model_without_bn.add(Flatten())model_without_bn.add(Dense(512))model_without_bn.add(Activation('relu'))model_without_bn.add(Dropout(0.5))model_without_bn.add(Dense(num_classes))model_without_bn.add(Activation('softmax'))opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)model_without_bn.compile(loss='categorical_crossentropy',                         optimizer=opt,                         metrics=['accuracy'])if not data_augmentation:    history_without_bn = model_without_bn.fit(x_train, y_train,                                          batch_size=batch_size,                                          epochs=epochs,                                          validation_data=(x_test, y_test),                                          shuffle=True)else:    # 使用数据增强获取更多的训练数据    datagen = ImageDataGenerator(width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True)    datagen.fit(x_train)    history_without_bn = model_without_bn.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size), epochs=epochs,                                  validation_data=(x_test, y_test), workers=4)# with_BN模型的训练model_with_bn = Sequential()model_with_bn.add(Conv2D(32, (3, 3), padding='same', input_shape=x_train.shape[1:]))model_with_bn.add(BatchNormalization())model_with_bn.add(Activation('relu'))model_with_bn.add(Conv2D(32, (3, 3)))model_with_bn.add(BatchNormalization())model_with_bn.add(Activation('relu'))model_with_bn.add(MaxPooling2D(pool_size=(2, 2)))model_with_bn.add(Conv2D(64, (3, 3), padding='same'))model_with_bn.add(BatchNormalization())model_with_bn.add(Activation('relu'))model_with_bn.add(Conv2D(64, (3, 3)))model_with_bn.add(BatchNormalization())model_with_bn.add(Activation('relu'))model_with_bn.add(MaxPooling2D(pool_size=(2, 2)))model_with_bn.add(Flatten())model_with_bn.add(Dense(512))model_with_bn.add(BatchNormalization())model_with_bn.add(Activation('relu'))model_with_bn.add(Dense(num_classes))model_with_bn.add(BatchNormalization())model_with_bn.add(Activation('softmax'))opt = keras.optimizers.rmsprop(lr=0.001, decay=1e-6)model_with_bn.compile(loss='categorical_crossentropy',                         optimizer=opt,                         metrics=['accuracy'])if not data_augmentation:    history_with_bn = model_without_bn.fit(x_train, y_train,                                              batch_size=batch_size,                                              epochs=epochs,                                              validation_data=(x_test, y_test),                                              shuffle=True)else:    # 使用数据增强获取更多的训练数据    datagen = ImageDataGenerator(width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True)    datagen.fit(x_train)    history_with_bn = model_with_bn.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size), epochs=epochs,                                             validation_data=(x_test, y_test), workers=4)# 比较两种模型的精确度plt.plot(history_without_bn.history['val_acc'])plt.plot(history_with_bn.history['val_acc'])plt.title('Model accuracy')plt.ylabel('Validation Accuracy')plt.xlabel('Epoch')plt.legend(['No Batch Normalization', 'With Batch Normalization'], loc='lower right')plt.grid(True)plt.show()# 比较两种模型的损失率plt.plot(history_without_bn.history['loss'])plt.plot(history_without_bn.history['val_loss'])plt.plot(history_with_bn.history['loss'])plt.plot(history_with_bn.history['val_loss'])plt.title('Model loss')plt.ylabel('Loss')plt.xlabel('Epoch')plt.legend(['Training Loss without BN', 'Validation Loss without BN', 'Training Loss with BN', 'Validation Loss with BN'], loc='upper right')plt.show()

  两种模型的代码差异主要为两点:

  • with_BN模型放弃了dropout函数,因为BN本身带有轻微地正则效果
  • with_BN的学习率较without_BN模型放大了10倍

  本模型中,我们使用了数据增强技术,我们来看看最终的比较图像:

 

  上图显示,测试数据集的精确度明显with_BN模型(87%)要高于without_BN模型(77%)。从训练速度来说,with_BN模型大概在第22代时已经很接近于最终收敛,而without_BN模型大概在第40代时接近于最终收敛,说明with_BN模型也会比较快。

  再比较看看损失度,明显可以看出无论是训练集还是测试集,with_BN模型要低于without_BN模型。

四、结论

  BN对于优化神经网络,加快训练速度甚至在提高准确度、降低损失度方面都能发挥积极作用,当然想要取得理想的效果也得需要反复地尝试各种组合(比如上述的例子,如果去掉数据增强技术,在我的测试结果,显示测试集的损失度反而更高,过拟合更严重)。

转载于:https://www.cnblogs.com/hutao722/p/9842199.html

你可能感兴趣的文章
市场营销进入大数据时代
查看>>
sudo命令:解决使用Linux命令行时出现的错误提示
查看>>
Linux的IRQ中断子系统分析
查看>>
使用FreeMarker替换JSP的10个理由
查看>>
阿里云创建E-MapReduce 2 创建集群
查看>>
白帽子认为2017年网络安全的头号威胁是大规模监控
查看>>
前端JS如何获取主域名(根域名)
查看>>
VR技术行业应用前景初探:技术创新定义精彩未来
查看>>
知识产权攻击是从哪冒出来的?
查看>>
宽带服务商设局,美国法律这么治
查看>>
混合IT架构的最佳实践
查看>>
一文详解神经网络 BP 算法原理及 Python 实现
查看>>
高通与联想达成新专利许可协议
查看>>
阿里科学家王刚、吴翰清同时入选MIT2017年度TR35 开创中国互联网企业先河
查看>>
继SDS之后又迎来CDM,存储行业真要变天了?
查看>>
美媒:联想电脑威胁五角大楼网络安全
查看>>
绿色智慧城市|城市增长边界的几个误区
查看>>
美国土安全部发警告:尽快卸载QuickTime for Windows
查看>>
《SEO的艺术(原书第2版)》——3.9 为客户挖掘和直接营销开展SEO
查看>>
运营商造梦“管道+内容”三十载不言放弃
查看>>