BN层参数详解(1,2)
- 一般来说pytorch中的模型都是继承
nn.Module
类的,都有一个属性trainning
指定是否是训练状态,训练状态与否将会影响到某些层的参数是否是固定的,比如BN
层(对于BN层测试的均值和方差是通过统计训练的时候所有的batch的均值和方差的平均值)或者Dropout
层(对于Dropout层在测试的时候所有神经元都是激活的)。通常用model.train()
指定当前模型model
为训练状态,model.eval()
指定当前模型为测试状态。 - 同时,
BN
的API中有几个参数需要比较关心的,一个是affine
指定是否需要仿射,还有个是track_running_stats
指定是否跟踪当前batch
的统计特性。容易出现问题也正好是这三个参数:trainning
,affine
,track_running_stats
。 - 其中的
affine
指定是否需要仿射,也就是是否需要上面算式的第四个,如果affine=False
则γ=1,β=0 \gamma=1,\beta=0γ=1,β=0
,并且不能学习被更新。一般都会设置成affine=True
。(这里是一个可学习参数,) trainning
和track_running_stats
,track_running_stats=True
表示跟踪整个训练过程中的batch的统计特性,得到方差和均值,而不只是仅仅依赖与当前输入的batch
的统计特性(意思就是说新的batch依赖于之前的batch的均值和方差这里使用momentum参数,参考了指数移动平均的算法EMA)。相反的,如果track_running_stats=False
那么就只是计算当前输入的batch的统计特性中的均值和方差了。当在推理阶段的时候,如果track_running_stats=False
,此时如果batch_size
比较小,那么其统计特性就会和全局统计特性有着较大偏差,可能导致糟糕的效果。
应用技巧:(1,2)
通常pytorch都会用到optimizer.zero_grad() 来清空以前的batch所累加的梯度,因为pytorch中Variable计算的梯度会进行累计,所以每一个batch都要重新清空一次梯度,原始的做法是下面这样的:
问题:参数non_blocking,以及pytorch的整体框架??
代码(1)
for index,data,target in enumerate(dataloader):
data = data.cuda(non_blocking=True)
target = torch.from_numpy(np.array(target)).float().cuda(non_blocking = Trye)
output = model(data)
loss = criterion(output,target)
#清空梯度
optimizer.zero_grad()
loss.backward()
optimizer.step()
而这里为了模仿minibacth,我们每次batch不清0,累积到一定次数再清0,再更新权重:
for index, data, target in enumerate(dataloader):
#如果不是Tensor,一般要用到torch.from_numpy()
data = data.cuda(non_blocking = True)
target = torch.from_numpy(np.array(target)).float().cuda(non_blocking = True)
output = model(data)
loss = criterion(data, target)
loss.backward()
if index%accumulation == 0:
#用累积的梯度更新权重
optimizer.step()
#清空梯度
optimizer.zero_grad()
虽然这里的梯度是相当于原来的accumulation倍,但是实际在前向传播的过程中,对于BN几乎没有影响,因为前向的BN还是只是一个batch的均值和方差,这个时候可以用pytorch中BN的momentum参数,默认是0.1,BN参数如下,就是指数移动平均
x_new_running = (1 - momentum) * x_running + momentum * x_new_observed. momentum
参考链接:https://www.jianshu.com/p/a646cbc913b4,https://www.zhihu.com/question/303070254/answer/573037166