Pytorch 0.3 调参指南&Debug大法&迷人的坑(持续更新中)
1.调参指南:
通常我们需要调整的部分有:优化器optim、学习率、batch_sizes、
(1)optimizer:
通常我们使用的是:朴素的SGD、Adagrad、Adam,后两者训练速度很快相同学习率下收敛更快,但不适宜于精细调参,为了迅速查看某方法是否work可以采用后两者,对lr依赖较小,但不一定能够找到最优点。
SGD:
optimizer_SGD = optim.SGD(model.parameters(), lr = 0.001, momentum = 0.9, weight_decay=5e-4)
Adagrad:
optimizer_Adag = optim.Adagrad(model.parameters(), lr = 0.001, lr_decay = 0, weight_decay=5e-4
weight_decay是Adagrad对于weight的一个L2惩罚项,一般默认为0,如果发现权值变化过大,可以适当加一个值。
Adam:
class torch.optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
betas (Tuple[float, float], 可选) – 用于计算梯度以及梯度平方的运行平均值的系数(默认:0.9,0.999)eps (float, 可选) – 为了增加数值计算的稳定性而加到分母里的项(默认:1e-8)
(2)learning rate:
对于一个learning from scratch的网络,建议lr设置从0.01开始;如果fine-tuning则可以设置小一些从0.001开始。
具体来判断学习率有一个很简单但是方便的思路:
我们在网络输入训练后,会得到一个loss,然后用于更新;完毕以后我们再讲此输入放入网络中得到更新后的网络对于同样一个样本的loss变化情况,如果基本呈下降趋势则可以适当调大,直到loss变化开始发生明显的振荡,表明到极限了,之前最大的能够确保下降的学习率就是最合适的学习率:
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
print('loss_befotr:', loss)
loss.backward()
optimizer.step
# test learning rate
output_new = net(input)
loss_new = criterion(output_new, target)
(3)Batch_size:
在显存允许的条件下选择合适的就好,太小了会容易过拟合。
2.Debug大法:
出现比如loss、acc之类的问题,有以下可以去检查的地方:
(1)数据集部分:
是否有预处理?是否有归一(均值方差)?是否有Shuffle?在打label的时候是否出错?
dataloader有没有问题?里面的transform函数有没有不妥的地方?传进网络的时候有没有出错?
(2)网络部分:
网络结构有没有搭错?初始化正确了吗?参数赋值(pre-train)的时候有没有对应赋正确?拿全0数据作为输入或者debug中间变量看是否有问题?
(3)训练部分:
梯度正常回传了吗?参数正常更新了吗?loss函数有没有写错?Acc的测试写对了吗?打印一下中间变量来看看呢?训练需要写net.train(),测试需要写net.eval()哦?
全局微调:最后一层随机初始化,前面所有层为最后一层学习率的1/10。
局部微调:最后一层随机初始化,前面所有层参数锁死。
————————————————————————————————————————————————————
一些可以再调整的地方:
(1)检查权重初始化
如果不确定,请使用 Xavier 或 He 初始化。同样,初始化也许会给你带来坏的局部最小值,因此尝试不同的初始化,看看是否有效。
(2)改变超参数
(3) 减少正则化
太多的正则化会导致网络严重地欠拟合。减少正则化,比如 dropout、批归一、权重/偏差 L2 正则化等。
(4) 从训练模式转换为测试模式
一些框架有批归一化层、Dropout层,而其他的层在训练和测试时表现并不同。转换到适当的模式有助于网络更好地预测。
(4)可视化训练
• 监督每层的**值、权重和更新。确保它们的大小匹配。例如,参数更新的大小幅度(权重和偏差)应该是 1-e3。
• 考虑可视化库,例如 Tensorboard 和 Crayon 。紧要时你也可以打印权重、偏差或**值。
• 寻找平均值远大于 0 的层**。尝试批归一化层或者ELU单元。
• Deeplearning4j 指出了权重和偏差柱状图的期望值应该是什么样的:
对于权重,一段时间之后这些柱状图应该有一个近似高斯的(正态)分布。对于偏差,这些柱状图通常会从 0 开始,并经常以近似高斯(LSTM是例外情况)结束。留意那些向正无穷或负无穷发散的参数。留意那些变得很大的偏差。这有可能发生在分类网络的输出层,如果类别的分布不均匀。
• 检查层更新,它们应该呈高斯分布。
(5)尝试不同的优化器
优化器的选择不应当妨碍网络的训练,除非你选择了特别糟糕的超参数。但是,选择一个合适的优化器非常有助于在最短的时间内获得最多的训练结果。描述算法的论文应该指定了优化器,如果没有,我倾向于选择Adam或者带有动量的朴素SGD。
关于梯度下降的优化器可以参考Sebastian Ruder的 博文。
(6)梯度爆炸、梯度消失
• 检查隐藏层的更新情况,过大的值说明可能出现了梯度爆炸。这时,梯度截断(Gradient clipping)可能会有所帮助。
• 检查隐藏层的**值。Deeplearning4j 中有一个很好的指导方针:“一个好的**值标准差大约在 0.5 到 2.0 之间。明显超过这一范围可能就代表着**值消失或爆炸。”
(7)增加、减少学习速率
低学习速率将会导致你的模型收敛很慢。高学习速率将会在开始阶段减少你的损失,但是可能会导致你很难找到一个好的解决方案。试着把你当前的学习速率乘以 0.1 或 10然后进行循环。
(8) 克服NaN
在训练 RNNs 时得到 NaN(Non-a-Number,非数)是一个很大的问题。一些解决它的方法:
• 减小学习速率,尤其是如果你在前 100 次迭代中就得到了NaN。
• NaNs 的出现可能是由于用零作了除数,或用零或负数作了自然对数。
• Russell Stewart 在《如何处理NaN》中分享了很多心得。
• 尝试逐层评估你的网络,这样就会看见NaN到底出现在了哪里。
附:完整可参考博文:http://imgtec.eetrend.com/blog/10381
3.迷人的坑:
在近日的学习中遇到了一些奇奇怪怪的关于pytorch的问题:
(1)对于一些中间变量我们在处理的时候最好不要自己单另一个temp,尽管初始temp为Variable但是对于梯度部分很有可能传导紊乱,最好是让其用.**函数来直接对tensor进行操作稳妥第一。
(2)这个问题就很bug了,原本目的是要给两个结构网路model1、model2赋值,如果是只实例化了一次:
pre_vgg = torchvision.models.vgg16(pretrained=True)
发现在赋值的时候会让其指向同一个内存空间,更保险的做法就是实例化的时候实例两个:
pre_vgg1 = torchvision.models.vgg16(pretrained=True)
pre_vgg2 = torchvision.models.vgg16(pretrained=True)
在赋值的时候最好分开赋值,这样能够保证是在两个不同的内存空间,使用.weight传递某层的参数:
model.features1 = pre_vgg1.features1
model.fc1[0].weight = pre_vgg1.classifier[0].weight
model.fc1[0].bias = pre_vgg1.classifier[0].bias
注意:在传参数的时候可以print(net)看一下对应sequential里面的层数的数值,赋weights或者bias是分开的,以列表形式存储,不要赋错了。
下一篇: Tomcat6配置日志