神经网络训练的一些技巧和方法

完整的内容见Numpy, Torch,cv近日小结

1.重要超参数的设定

1.1 batch size的设定

如果用了batch norm, 那么batch size不宜太小,最好要大于等于32.
一开始时可以设置为32, 之后x2逐步调整。

1.2 学习率设置与更新

1.2.0 warmup

warmup作用:由于刚开始训练时,模型的权重(weights)是随机初始化的,此时若选择一个较大的学习率,可能带来模型的不稳定(振荡),选择Warmup预热学习率的方式,可以使得开始训练的几个epoches或者一些steps内学习率较小,在预热的小学习率下,模型可以慢慢趋于稳定,等模型相对稳定后再选择预先设置的学习率进行训练,使得模型收敛速度变得更快,模型效果更佳。

在选择lr初值和更新方式的阶段,不要使用warmup。否则warmup可能会掩盖lr初值设置过大的问题。应该等lr, batch size, epoch等参数选定好后,再尝试使用warmup看效果。

1.2.1 学习率初值选定

当batachsize固定(如32),可以把学习率初值设为1e-3, 然后10x调整,最后再x2或者x5微调。

如果训练过程中准确率始终维持在一个很低的水平或者出现NAN, 那么准确率应该调小; 如果

1.2.2 学习率与batchsize的关系

对于收敛精度,由于增大了batch size使梯度估计相较于badeline的梯度更加准确,噪音减少,更容易收敛到附近的local minima,类似于GD的效果。batch size增大K倍,相当于将梯度的方差减少K倍,因此梯度更加准确。

如果要保持方差和原来SGD一样,相当于给定了这么大的方差带宽容量,那么就可以增大lr,充分利用这个方差容量。
因此可将lr增加sqrt(K)倍,以提高训练速度,这也是在linear scaling rule之前很多人常用的增大lr的方式。但在实际中,发现直接将lr增大K倍的效果较好。

1.2.3 学习率调整策略

常见的方式包括Linear, step, multistep, cosine等。

def get_scheduler(optimizer, opt) :
	"""Return a learning rate scheduler
		Parameters:
		optimizer -- 网络优化器
		opt.lr_policy -- 学习率scheduler的名称: linear | step | plateau | cosine
	"""

	def linear_rule(epoch) :
		lr_l = 1.0 - epoch / opt.epochs
		return lr_l
	def warmup_rule(epoch) :
		if epoch < opt.warmup_epoch :
			lr_l = epoch / opt.warmup_epoch
		else:
			T = epoch - opt.warmup_epoch
			total_epoch = opt.epochs - opt.warmup_epoch
			if opt.adjust_lr == 'cosine':
				lr_l = 0.5 * (1 + math.cos(T / total_epoch * math.pi))
			elif opt.adjust_lr == 'step':
				gamma = opt.step_gamma
				step_size = opt.step_size
				lr_l = gamma ** (T//step_size)
		return lr_l

	if opt.adjust_lr == 'linear' :
		scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=linear_rule)
	elif opt.adjust_lr == 'step' :
		# scheduler = lr_scheduler.StepLR(optimizer, step_size=100, gamma=0.5)
		scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=warmup_rule)
	# elif opt.adjust_lr == 'multistep' :
	# 	epochs = opt.epochs
	# 	milestones = [epochs // 2, epochs * 3 // 4]
	# 	scheduler = lr_scheduler.MultiStepLR(optimizer, milestones=milestones, gamma=0.1)
	elif opt.adjust_lr == 'plateau':
		scheduler = lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.2, threshold=0.01, patience=5)
	elif opt.adjust_lr == 'cosine':
		scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=warmup_rule)
	else :
		raise NotImplementedError('learning rate policy [%s] is not implemented', opt.adjust_lr)
	return scheduler

1.3 epoch的设置

lr的decay策略与epoch有关,因此要严格分析起来会比较复杂。
大体的原则是先设置一个比较大的epoch如100。然后观察准确率和train loss的变化.

如果刚开始准确率就上升比较快,但后面的绝大部分epoch准确率没有明显变化或者后面的绝大部分epoch上train Loss基本没有下降,就可以考虑减少epoch; 反之,如果epoch快结束时这两个还在变化,就考虑增加epoch。

1.4 optimizer选择

一般来说Adam是最优选择,其他的以后有机会再了解。

2. train network

2.1 如何防止过拟合

过拟合主要有两个原因造成的,数据太少和模型太复杂。对于模型复杂,最简单暴力的做法就是减少网络的层数和神经元的个数,但是一般不是很提倡这种做法,是因为人为并不能很好掌控删减的程度,因此下面介绍几种高效的方法。

  1. 正则化与权重衰减。 L2正则化也就是权重衰减。

一般,Weight Decay=0.001。可以根据实际训练效果,做x10倍的上下调整。

另外,pytorch里通过设置优化器的weight_decay来进行权重衰减,貌似没有分母的n(因为优化器是没有办法得知训练集的样本大小的)。

在这里插入图片描述

  1. 数据增强(crop, resize, noise, rotate, flip and etc). 需要注意的是标签可能也需要随之变化.
  2. 提前终止。 当testset上的准确率(或loss等)有下降的趋势,立刻停止。
  3. dropout与BatchNormalization。 根据一些最新的研究论文,相比于dropout,更推荐使用BN。
    需要注意的是,使用BN时batch size不能太小。一般可以选择32,再根据实际训练效果x2上调下调。
    注意: PyTorch设置 Dropout 时,torch.nn.Dropout(keepProb = 0.5), 这里的 keepProb是指该层(layer)的神经元在每次迭代训练时会被丢弃(失活)的可能性。其他的如TF等参数的意义到底是失活概率还是不失活概率,还需查阅文档。

PS:根据本人目前浅薄经验, 数据增强是最有效的(因为无需参数的设定,只要增强了数据,基本上都会起到一定的防止过拟合效果)。

2.2 train loss与test loss结果分析

train loss 不断下降,test loss不断下降,说明网络仍在学习;

train loss 不断下降,test loss趋于不变,说明网络过拟合;

train loss 趋于不变,test loss不断下降,说明数据集100%有问题;

train loss 趋于不变,test loss趋于不变,说明学习遇到瓶颈,需要减小学习率或批量数目;

train loss 不断上升,test loss不断上升,说明网络结构设计不当,训练超参数设置不当,数据集经过清洗等问题。

2.3 解决神经网络训练时train loss不下降的问题

训练集Loss不下降,说明欠拟合
总结的如下解决方案:

  1. 正则化过度,**开始训练时可以考虑一个小的正则化系数。**过拟合后,再根据训练情况进行调整。
  2. 选择合适的损失函数。损失函数相当于模型拟合程度的一个评价指标,如果损失函数选取不当,导致学习难度过大,就容易欠拟合。考虑降低损失函数的学习难度。
  3. 选择合适的优化器和学习速率。我们需要手动调整学习率,首先选择一个合适的初始学习率,当训练不动之后,稍微降低学习率,然后再训练一段时间,这时候基本上就完全收敛了。
  4. 训练时间不足
  5. 神经网络的拟合能力不够。增加神经网络的层数。

具体参考原文链接:
如何解决神经网络训练时loss不下降的问题
深度学习:欠拟合问题的几种解决方案

2.4 L1 Loss与L2 Loss的对比与选取

L1 Loss:

  • 以稳定速率收敛
  • 对输入数据中的离群点不敏感
  • 不光滑,存在不可导点

L2 Loss:

  • 随着误差的减小,梯度也在减小,这有利于收敛,即使使用固定的学习速率,也能较快的收敛到最小值。
  • 对离群点敏感
  • 光滑,处处可导

smooth L1 Loss:

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200729013125101.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzE0NjEy,size_16,color_FFFFFF,t_70

  • 当与 ground truth 差别过大(存在离群点)时,梯度值不至于过大而跑飞;
  • 当与 ground truth 差别很小时,梯度值足够小,较快收敛到最小值。

L1与L2 Loss的选择:

对于大多数CNN网络,我们一般是使用L2-loss而不是L1-loss,因为L2-loss的收敛速度要比L1-loss要快得多。

如果数据离群点较多,可以考虑使用L1 Loss.

2.5 网络结构和参数量估计

2.5.1 Vgg16

参考https://blog.csdn.net/hzhj2007/article/details/80164909

最后是三个全连接层

(7x7x512) x 4096=102M

4096x4096=17M

4096x1000=4M

最后全连接层共有123M parameters. 而整个Vgg16一共有138M parameters.

如果是用float32, 那么大小为138M x 4 = 500~MB.

2.6 同时使用多个数据库训练

2.6.1 使用一个dataloader

第一个方法是把所有的数据库拼接在一起。
参考:
https://zhuanlan.zhihu.com/p/222772996
https://pytorch.org/docs/stable/data.html?highlight=concatdataset#torch.utils.data.ConcatDataset

2.6.2 zip多个dataloader

zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象。

我们可以使用 list() 转换来输出列表。

如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。

如果两个dataloader的长度不一样, 使用cycle,我们将再次重复最小的数据集,直到迭代器查看最大数据集中的所有样本。

# len(dataloader1) == len(dataloader2)
for i,data in enumerate(zip(dataloader1, dataloader2)):
	(x1,y1),(x2,y2) = data

# len(dataloader1) < len(dataloader2)
from itertools import cycle
for i,data in enumerate(zip(cycle(dataloader1), dataloader2)):
	(x1,y1),(x2,y2) = data

2.7 训练加速

2.7.1 读取数据加速

  1. 服务器内存一般都很大(100GB级别),可以在训练之前把所有数据全部读入内存。
  2. 把num_worker设置为8或者16
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值