1. 背景说明
如果模型中有BN层(Batch Normalization)和Dropout,需要在训练时添加model.train(),在测试时添加model.eval()。
BN层:
其作用对网络中间的每层进行归一化处理,并且使用变换重构保证每层提取的特征分布不会被破坏。训练时是针对每个mini-batch的,但是测试是针对单张图片的,即不存在batch的概念。由于网络训练完成后参数是固定的,每个batch的均值和方差是不变的,因此直接结算所有batch的均值和方差。所有Batch Normalization的训练和测试时的操作不同。
model.train(): 保证BN层用每一批数据的均值和方差;
model.eval(): 保证BN用全部训练数据的均值和方差。
Dropout层:
其作用克服Overfitting,在每个训练批次中,通过忽略一半的特征检测器,可以明显的减少过拟合现象。
model.train(): 随机取一部分网络连接来训练更新参数;
model.eval(): 利用到了所有网络连接。
2. model.train()
train模式下,dropout层会按照设定的参数p设置保留激活单元的概率; BN层会继续计算数据的mean和var等参数并更新。
3. model.eval()
在validation时,会使用model.eval()切换到测试模式,用于通知dropout层和BN层在train和val模式间切换。不启用BN层和Dropout,保证BN和dropout不发生变化,pytorch框架会自动把BN和Dropout固定住,不会取平均,而是用训练好的值,不然的话,一旦test的batch_size过小,很容易就会被BN层影响结果。
dropout层会让所有的激活单元都通过,而BN层会停止计算和更新mean和var,直接使用在训练阶段学到的mean和var值。该模式不会影响各层的gradient计算行为,即gradient计算和存储与training模式一样,只是不进行反传(backprobagation)。
此时,可使用with torch.no_grad()停止autograd模块的工作,以起到加速和节省显存的作用,具体行为就是停止gradient计算,从而节省了GPU算力和显存,但no_grad()本身并不会影响dropout和BN层的行为。
所以二者一般配合使用。即:
如果不在意显存大小和计算时间的话,仅仅使用model.eval()已足够得到正确的validation的结果;而with torch.zero_grad()则是更进一步加速和节省gpu空间(因为不用计算和存储gradient),从而可以更快计算,也可以跑更大的batch来测试。
4. 梯度是否更新
1. requires_grad
requires_grad是Variable变量中的一个属性,requires_grad的属性默认为False(即不需要计算梯度),若一个节点requires_grad被设置为True,那么所有依赖它的节点的requires_grad都为True,此时要求计算tensor的梯度。
2. model.eval()
数据不会进行反向传播,但仍然需要计算梯度;
3. with torch.no_grad()
torch.no_grad() 是一个上下文管理器,被该语句内部的语句将不会计算梯度,也不会进行反向传播。