model.train与model.val 和 Batch Normalization的关系

model.trainmodel.valBatch Normalization的关系

1、model.trainmodel.eval的区别(参考博客)

(1)model.train : model.train的作用是启用Batch NormalizationDropout。如果模型中有BN层和Dropout,则需要在模型训练时添加model.train(), 用来保证BN层能够学习到每一个Batch数据的均值与方差(具体计算过程见下文)。对于Dropout, model.train是随机取一部分网络连接来训练更新参数,从而增强网络的泛化能力。

(2)model.eval: model.eval的作用是不启用Batch NormalizationDropout。如果模型中有BN层和Dropout,则一般需要在模型测试或者验证的时候添加model.evalmodel.eval会将Batch Normalization 内继承至Moduleself.training参数置为false, 则保证BN层在测试的阶段不会再进行输入数据均值和方差的计算,即保证在测试过程中BN层所使用的均值和方差不变且为训练过程中保存的均值和方差。对于Dropout, model.eval是利用了全部的网络连接,即不会随机舍弃神经元。

对于Dropout,操作相对简单,不用过多解释。因为现在大多数的模型使用了BN层,则可以不用过多理会DropoutL2正则项参数的选择,甚至可以将这两项去除。

在使用BN的时候,要注意model.trainmodel.eval的区别,训练完train数据集样本后,在使用该模型参数进行测试前,需要加上model.eval(),否则的话,有输入数据,即使不进行训练,BN层的均值和方差也会改变,进而可能造成测试精度下降。但是也有部分情况会出现加上model.eval在训练过程中精度高而在验证或者测试的时候结果较差,甚至一团糟,这部分原因和解决方案可以参考第三节。

2、Batch Normalization

2.1 为什么需要使用Batch Normalization

主要参考这篇博客

随机梯度下降法对于训练深度网络简单高效,但是它有个毛病,就是需要我们人为的去选择参数,比如学习率、参数初始化、权重衰减系数、Dropout比例等。这些参数的选择对训练结果至关重要,以至于我们很多时间都浪费在这些的调参上。BN算法的强大之处在下面几个方面:

  • 可以选择较大的学习率,使得训练速度增长很快,具有快速收敛性。
  • 可以不去理会DropoutL2正则项参数的选择,如果选择使用BN,甚至可以去掉这两项。
  • 去掉局部响应归一化层(LRN)。(AlexNet中使用的方法,BN层出来之后这个就不再用了)
  • 可以把训练数据打乱,防止每批训练的时候,某一个样本被经常挑选到。(我的理解是使用BN层更多的是学习数据样本的分布,某一两个样本多次被挑选到不会影响整体的数据特征分布)

我们都知道在神经网络训练开始前,都要对输入数据做一个归一化处理,原因在于神经网络学习过程本质就是为了学习数据分布,一旦训练数据与测试数据的分布不同,那么网络的泛化能力也大大降低;另外一方面,一旦每批训练数据的分布各不相同(batch 梯度下降),那么网络就要在每次迭代都去学习适应不同的分布,这样将会大大降低网络的训练速度,这也正是为什么我们需要对数据都要做一个归一化预处理的原因。

对于深度网络的训练是一个复杂的过程,只要网络的前面几层发生微小的改变,那么后面几层就会被累积放大下去。一旦网络某一层的输入数据的分布发生改变,那么这一层网络就需要去适应学习这个新的数据分布,所以如果训练过程中,训练数据的分布一直在发生变化,那么将会影响网络的训练速度。

所以BN层就是要解决上述问题,通过在网络每一层输入的时候,又插入了一个归一化层,也就是先做一个归一化处理,然后再进入网络的下一层。然而其实实现起来并不是那么简单的。**如果是仅仅使用上面的归一化公式,对网络某一层A的输出数据做归一化,然后送入网络下一层B,这样是会影响到本层网络A所学习到的特征的。**打个比方,比如我网络中间某一层学习到特征数据本身就分布在 S 型激活函数的两侧,你强制把它给我归一化处理、标准差也限制在了1,把数据变换成分布于 s 函数的中间部分,这样就相当于我这一层网络所学习到的特征分布被你搞坏了,这可怎么办?于是BN作者使出了一招惊天地泣鬼神的招式:变换重构,引入了可学习参数 γ \gamma γ β \beta β ,这就是算法关键之处。通过参数 γ \gamma γ β \beta β 可以恢复出原始的某一层所学到的特征。

此外,这篇博客也讲到batch Normalization解决了梯度消失,梯度爆炸等问题。值得深入学习 

2.2 Batch Normalization基本原理:

Batch Normalization层需要计算一个miniBatch input feature(x)中所有元素的均值 μ \mu μ 和方差 σ \sigma σ ,然后对 x i x_i xi减去均值除以标准差,最后利用可学习的参数 γ \gamma γ β \beta β 进行仿射变换,即可得到最终的BN输出 y i y_i yi

公式如下:

①计算均值和方差:

μ B ← 1 m ∑ i = 1 m x i \mu _{B}\leftarrow \frac{1}{m}\sum_{i=1}^{m}x_{i} \\ μBm1i=1mxi
σ B 2 ← 1 m ∑ i = 1 m ( x i − μ B ) 2 \sigma _{B}^{2} \leftarrow \frac{1}{m} \sum_{i=1}^{m}\left ( x_{i}- \mu_{B} \right)^ 2 σB2m1i=1m(xiμB)2

②样本数据标准化处理:

x i ^ ← x i − μ B σ B 2 + ϵ \widehat{x_i}\leftarrow \frac{x_i-\mu_{B}}{\sqrt{\sigma_{B}^{2}+\epsilon }} xi σB2+ϵ xiμB
③进行平移和缩放处理,引入 γ \gamma γ β \beta β 两个可训练参数,使网络可以学习恢复出原始网络所要学习的数据特征分布。

y i ← γ ∗ x i ^ + β ≡ B N γ , β ( x i ) y_{i}\leftarrow \gamma \ast \widehat{x_{i}} + \beta \equiv BN_{\gamma,\beta}(x_{i}) yiγxi +βBNγ,β(xi)
其完整公式如下所示:

x ^ = γ ∗ ( x − μ ) σ 2 + ϵ + β \widehat{x} = \gamma \ast \frac{ \left ( x-\mu \right )}{\sqrt{\sigma^{2}+\epsilon }}+\beta x =γσ2+ϵ (xμ)+β

其代码实现如下:

m = K.mean(X, axis=-1, keepdims=True)#计算均值
std = K.std(X, axis=-1, keepdims=True)#计算标准差
X_normed = (X - m) / (std + self.epsilon)#归一化
out = self.gamma * X_normed + self.beta#重构变换

由上述公式可以看出,当训练时Batch Size设置较小的话,所计算出的均值 μ \mu μ 和方差 σ \sigma σ 不能较好的反应整个数据集的特征分布,因此,可学习的参数 γ \gamma γ β \beta β 不能够很好的恢复出原始的某一层所学到的特征,在测试时,如果是单张图片,使用model.eval固定均值 μ \mu μ 和方差 σ \sigma σ 时,不能很好的将单张图片进行归一化,进而使用参数 γ \gamma γ β \beta β 无法很好的进行重构变换(注意此处,有些博客会说使用model.eval固定的是可学习参数 γ \gamma γ β \beta β ,实际上这两个参数测试的时候因为没有反向传播,不管使用model.train还是model.eval都是不会变化的,只有在模型训练的时候有梯度反向传播才会进行学习训练)。

当训练时batch size设置比较大时,BN层中可学习的参数每次所使用的均值和方差更接近全局的数据,因此可以很好的反应全局的数据分布,使用单张图片测试时结果也是相差不大的。

1.尽量将batch size设置大一些(2的整数次幂),32,64,128,256,能多大就多大,但是一般不要超过512.
2.如果设备受限,导致batch size只能设置很小(1,2),那测试时可以把eval()去掉,前提是网络结构中并没有dropout层。这里可以理解为训练时batch size设置的很小,如1、2,其实每次minibatch太小,在训练时训练出的均值和方差其实与单张图片测试时相差很大,去掉eval()使用默认train()可以不固定均值和方差。

3、常见的问题及解决方案

(1)为什么有时测试时使用model.eval()会使得准确率降低?(原文链接

原因①:可能是由于batch size设置较小,如1,2,训练时针对BN层计算的均值和方差只使用很小的mini batch数据,测试时使用的是单张图片进行测试,使用model.eval()固定了均值和方差,所以不能很好的对该图片进行归一化,就会导致准确率下降。

原因②: 参考:https://www.jianshu.com/p/a29b86b6ab0b 。作者:ZhengHsin
这个错误可能是由于在batchnorm中的动量(momentum)设置而导致。在进行推理时,设置较大的动量值可以解决这个问题。如下:nn.BatchNorm2d(out_planes, eps=1e-3, momentum=1))

momentum作用是更新 running_meanrunning_var 时的动量,默认0.1。更新公式是:
x n e w = ( 1 − m o m e n t u m ) × x c u r + m o m e n t u m × x b a t c h x_{new}=(1-momentum)\times x_{cur}+momentum\times x_{batch} xnew=(1momentum)×xcur+momentum×xbatch
其中, x n e w x_{new} xnew代表更新后的running_meanrunning_var , x c u r x_{cur} xcur 表示更新前的running_meanrunning_var x b a t c h x_{batch} xbatch 表示当前batch的均值和无偏样本方差。

可能还有很多别的原因,欢迎讨论。

(2)Batch Normalization的均值和方差对谁求?
1、对feature mapchannel方向求均值和方差, 假设batch size=n, feature mapshape= (w, h, c), 其中cchannel个数, 则会对cn*w*h的特征分别求出c个均值和方差.
2、同样的,为了减少参数量, 一个channel(相当于一种卷积核提取的同一种特征)只对应一组可学习的参数γβ, 所以对于一层BN层, 可学习的参数为2*c个.

参考博客:
https://blog.csdn.net/qq_52852138/article/details/123769937
https://zhuanlan.zhihu.com/p/357075502
https://blog.csdn.net/weixin_44479045/article/details/124307200
https://blog.csdn.net/ygfrancois/article/details/90382459
https://blog.csdn.net/qq_52852138/article/details/123769937
https://blog.csdn.net/hjimce/article/details/50866313
https://zhuanlan.zhihu.com/p/337732517

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下代码出现input depth must be evenly divisible by filter depth: 1 vs 3错误是为什么,代码应该怎么改import tensorflow as tf from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D from keras.optimizers import SGD from keras.utils import np_utils from keras.preprocessing.image import ImageDataGenerator from keras.applications.vgg16 import VGG16 import numpy # 加载FER2013数据集 with open('E:/BaiduNetdiskDownload/fer2013.csv') as f: content = f.readlines() lines = numpy.array(content) num_of_instances = lines.size print("Number of instances: ", num_of_instances) # 定义X和Y X_train, y_train, X_test, y_test = [], [], [], [] # 按行分割数据 for i in range(1, num_of_instances): try: emotion, img, usage = lines[i].split(",") val = img.split(" ") pixels = numpy.array(val, 'float32') emotion = np_utils.to_categorical(emotion, 7) if 'Training' in usage: X_train.append(pixels) y_train.append(emotion) elif 'PublicTest' in usage: X_test.append(pixels) y_test.append(emotion) finally: print("", end="") # 转换成numpy数组 X_train = numpy.array(X_train, 'float32') y_train = numpy.array(y_train, 'float32') X_test = numpy.array(X_test, 'float32') y_test = numpy.array(y_test, 'float32') # 数据预处理 X_train /= 255 X_test /= 255 X_train = X_train.reshape(X_train.shape[0], 48, 48, 1) X_test = X_test.reshape(X_test.shape[0], 48, 48, 1) # 定义VGG16模型 vgg16_model = VGG16(weights='imagenet', include_top=False, input_shape=(48, 48, 3)) # 微调模型 model = Sequential() model.add(vgg16_model) model.add(Flatten()) model.add(Dense(256, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(7, activation='softmax')) for layer in model.layers[:1]: layer.trainable = False # 定义优化器和损失函数 sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True) model.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy']) # 数据增强 datagen = ImageDataGenerator( featurewise_center=False, featurewise_std_normalization=False, rotation_range=20, width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True) datagen.fit(X_train) # 训练模型 model.fit_generator(datagen.flow(X_train, y_train, batch_size=32), steps_per_epoch=len(X_train) / 32, epochs=10) # 评估模型 score = model.evaluate(X_test, y_test, batch_size=32) print("Test Loss:", score[0]) print("Test Accuracy:", score[1])
04-19

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值