摘要
本篇BLOG整合了各种调参技巧,
种子的选取(玄学)
推荐两个好用的种子
- random.seed(42) ,42是《银河系漫游指南》中的答案,社区反馈,这个种子确实在大多数任务下表现不错,有人说,这是因为光需要\(10^{-42}\)秒才能穿过质子的直径,也有人说,光通过水面折射42度形成彩虹。
- random.seed(3407),这个种子有文献支持,特别适用于CIFAR-10之类的中小数据集。(在CIFAR 10 数据集上,作者选取了10000个随机种子,每个随机种子进行30s的时间进行训练和测试,总耗时将近83小时。模型架构采用的是9层的ResNet,优化器SGD。为了保证部分实验中,模型是接近收敛的,因此作者对其中500次的结果训练时长延迟至1分钟。最后,总算力消耗为90小时的V100 GPU运行时间。)
其他可以试用的
- random.seed(999)因为这个是我的世界的一个神奇种子,开局有村庄和矿洞,附近还有村庄,水井底下有钻石矿,开局就能挖到钻石。
- random.seed(215) 太上老君是我国著名的炼丹大师,他的生日是农历二月十五日,所以这个种子也可以用。
数据预处理
- 调参是锦上添花的事,而底线取决于:模型的选择和数据的清洗。一定要进行数据归一化,把数据分布分散到均值为0,方差为1的区间,利于模型训练。
- RGB Norm:这种增强通过从每个通道的值中减去每个通道的平均值并除以通道的标准差来标准化图像的 RGB 通道。这有助于标准化图像中的值,并可以提高模型的性能。
- def rgb_norm(image):
- r, g, b = cv2.split(image)
- r = (r - np.mean(r)) / np.std(r)
- g = (g - np.mean(g)) / np.std(g)
- b = (b - np.mean(b)) / np.std(b)
- return cv2.merge((r, g, b))
复制代码
- 一个几乎必定涨点的神技巧:mixup(Zhang et al., 2017)是一种数据增强方法,通过线性插值生成新的训练样本。其核心思想是将两个样本及其标签进行线性组合:
\[\begin{aligned}x' &= \lambda x_1 + (1 - \lambda) x_2 \\y' &= \lambda y_1 + (1 - \lambda) y_2\end{aligned}\]
其中,\(x_1\)和\(x_2\)是两个输入样本,\(y_1\)和\(y_2\)是对应的标签,\(\lambda\)是一个在[0, 1]之间的超参数,用于控制混合的比例。通过这种方式,mixup可以生成新的训练样本,从而提高模型的泛化能力。在CV中几乎必定涨点。
- 除此之外,博客(https://zhuanlan.zhihu.com/p/592531559)中有将近20种图像处理相关的涨点技巧,都可供cv任务参考。
关于loss
类别不平衡
focal loss(Lin et al., 2017)是一种针对样本不均衡问题的损失函数,通过调整难易样本的权重来提高模型对难分类样本的关注度。其定义为:
\[\mathcal{L}(p_t) = -\alpha_t (1 - p_t)^\gamma \log(p_t)\]
其中,\(p_t\)是模型对正确类别的预测概率,\(\alpha_t\)是类别的权重,\(\gamma\)是调节难易样本的超参数。通过这种方式,focal loss可以有效缓解样本不均衡问题,提高模型对少数类的识别能力。
多个loss组合
学习任务中,loss可能包含多个成分,loss = a * loss1 + b * loss2 + c * loss3 怎么设置 ?这时候需要平衡各个loss的尺度。常见的做法有:
- 把所有 loss 放缩到差不多的尺度,设 a = 1,b 和 c 取 10^k,k 选完不管了;
- 如果有两项 loss,可以 loss = a * loss1 + (1 - a) * loss2,通过控制一个超参数 a 调整 loss;
- 玄学躺平做法 loss = loss1 / loss1.detach() + loss2 / loss2.detach() + loss3 /loss3.detach(),分母可能需要加 eps,相当于在每一个 iteration 选定超参数 a, b, c,使得多个 loss 尺度完全一致;进一步更科学一点就 loss = loss1 + loss2 / (loss2 / loss1).detach() + loss3 / (loss3 / loss1).detach()
- 多个loss引入Pareto优化理论。自动调整,可见nips2018论文:https://proceedings.neurips.cc/paper_files/paper/2018/file/432aca3a1e345e339f35a30c8f65edce-Paper.pdf
- 根据任务的优先级调整:如果在多任务学习中,主要希望主任务的效果好,辅助任务的效果可能不是很care,那么可以根据任务的优先级来调整loss的权重。
根据不同任务选择合适的loss
不同任务可能需要不同的损失函数。选择合适的损失函数可以帮助模型更好地学习各个任务的特征。
关于优化器
Adam和SGDM是最常用的两个,前者能快速收敛,后者收敛慢但最终精度更高。现在大家会先使用Adam快速收敛,后面再用SGDM提升精度。
代码分别如下:- # Adam
- optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
- # SGDM
- optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
复制代码 如果你在计算机视觉里用Adam之类的自适应优化器,得到的结果很有可能会离SGD的baseline差好几个点。主要原因是,自适应优化器容易找到sharp minima,泛化表现常常比SGD差得显著。如果你训练Transformer一类的模型,Adam优化得更快且更好。主要原因是,NLP任务的loss landscape有很多“悬崖峭壁”,自适应学习率更能处理这种极端情况,避免梯度爆炸。
关于学习率和batchsize
- 训练的最后阶段,如果不主动把learning rate降下去,loss根本就不会自己收敛到一个比较小的值.
- 一般来说,越大的batch-size使用越大的学习率。原理很简单,越大的batch-size意味着我们学习的时候,收敛方向的confidence越大,我们前进的方向更加坚定,而小的batch-size则显得比较杂乱,毫无规律性,因为相比批次大的时候,批次小的情况下无法照顾到更多的情况,所以需要小的学习率来保证不至于出错。可以看下图损失Loss与学习率Lr的关系:
- warmup and decay一起使用
- warmup:在训练初期使用较小的学习率,逐渐增加到预设值,以避免初始阶段的梯度爆炸。
- decay:在训练后期逐渐降低学习率,以便模型在接近收敛时进行更精细的调整。
实验证明,有了warmup之后模型能够学习的更稳定。可以直接调用库函数代码- from torch.optim.lr_scheduler import LambdaLR
- def lr_lambda(epoch):
- if epoch < 5:
- return 0.1
- else:
- return 0.95 ** (epoch - 5)
复制代码
- 学习率衰减策略,都可以考虑,通常先看自己领域一般用什么策略。比如nlp中warmup+cosine decay是常用的策略。其中余弦衰减率定义为
\[\eta_t=\frac{1}{2}\left(1+\cos\left(\frac{t\pi}{T}\right)\right)\eta\]
关于正则化
- 模型内部 dropout ratio 是一个很重要的参数,一般小于0.5。Dropout一般适合于全连接层部分,而卷积层由于其参数并不是很多,所以不需要dropout,加上的话对模型的泛化能力并没有太大的影响。
- L1,L2正则化的使用有不同场景,
- L1正则化适用于特征选择,能够使得部分权重变为0,从而实现特征稀疏化。
- L2正则化则更常用,能够防止过拟合,保持模型的平滑性。
- smooth label(Szegedy et al., 2016)是一种通过对标签进行平滑处理来提高模型鲁棒性的方法。其核心思想是将标签从one-hot编码转换为一个概率分布,从而减少模型对单一标签的过拟合。具体实现方式为:
我们不是要求我们的模型为正确的类别预测 1,而是要求它为正确的类别预测 1-ε,并将所有其他类别预测为 ε. 它被视为一种正则化技术,因为它限制了softmax 函数的最大概率使最大概率不会比其他标签大得多(防止模型过度自信)。- import torch.nn.functional as F
-
-
- def reduce_loss(loss, reduction='mean'):
- return loss.mean() if reduction=='mean' else loss.sum() if reduction=='sum' else loss
-
-
- class LabelSmoothingCrossEntropy(nn.Module):
- def __init__(self, epsilon:float=0.1, reduction='mean'):
- super().__init__()
- self.epsilon = epsilon
- self.reduction = reduction
-
- def forward(self, preds, target):
- n = preds.size()[-1]
- log_preds = F.log_softmax(preds, dim=-1)
- loss = reduce_loss(-log_preds.sum(dim=-1), self.reduction)
- nll = F.nll_loss(log_preds, target, reduction=self.reduction)
- return linear_combination(loss/n, nll, self.epsilon)
复制代码 关于权重初始化
- pre-training 预训练,指的是不用从零开始训练一个新模型,可以从在类似问题中训练过的模型入手,用公开的参数做初始化,相当于一种warm start。预训练模型可以是ImageNet等大规模数据集上训练的模型,或者是针对特定任务的预训练模型。
- Xavier 初始化:Xavier初始化的基本思想是,若对于一层网络的 输出和输出可以保持正态分布且方差相近,这样就可以避免输出趋向于0,从而避免梯度消失情况。
\[W \sim \mathcal{N}(0, \frac{2}{n_{in} + n_{out}})\]
\[W \sim \mathcal{N}(0, \frac{2}{n_{in} \cdot k^2})\]
其中,\(n_{in}\)和\(n_{out}\)分别是输入和输出的神经元数量,\(k\)是卷积核的大小。缺点:Xavier初始化有一定限制,其推导过程假设激活函数在零点附近接近线性函数,且激活值关于0对称。Relu函数很明显是不对称的。
- Kaiming 初始化:Kaiming初始化是针对ReLU激活函数设计的,其基本思想是保持每层的方差不变。其公式为:
\[W \sim \mathcal{N}(0, \frac{2}{n_{in}})\]
其中,\(n_{in}\)是输入神经元的数量。
- svd初始化:对RNN有比较好的效果。
- batchsize normalization的zero-\(\gamma\)初始化,指的是在Batch Normalization中,将\(\gamma\)的初始值设为0,以使得网络在训练初期不受缩放因子的影响。
- 使用权重衰减的时候不使用bias decay,偏置不作为权重衰减的对象。因为偏置通常不需要正则化,且其值通常较小,使用权重衰减可能会导致偏置过小,从而影响模型性能。
其他技巧
知识蒸馏
知识蒸馏(Knowledge Distillation)是一种通过大型模型(Teacher)指导小型模型(Student)学习的模型压缩技术,其核心思想是将复杂模型的知识“蒸馏”到轻量模型中,在保持性能的同时提升效率。
- 训练大型模型(Teacher Network)
目标:在数据集上训练一个高性能但复杂的模型(如ResNet、BERT等)。
- 计算软标签(Soft Labels)
通过温度参数(Temperature, T) 调整Softmax的输出平滑度:
\[\text{Softmax}(z_i) = \frac{e^{z_i / T}}{\sum_j e^{z_j / T}}\]
T的作用:
- T=1:标准Softmax,概率分布尖锐。
- T>1(如T=3):概率分布更平滑,凸显次要类别信息
举个例子,Teacher原始输出logits为[2.9, 0.1, 0.23]
- 若 Softmax(T=1):[0.885, 0.054, 0.061]
- 软化后(T=3):[0.554, 0.218, 0.228],突出了某种类别间关系。
- 训练学生模型(Student Network),Student的损失函数由两部分加权组成:
其一是蒸馏损失(Distillation Loss):此处使用KL散度(Kullback-Leibler Divergence)衡量Student输出与Teacher软标签的差异:
\[\mathcal{L}_{\text{distill}} = \text{KL}\left(\text{Softmax}(\text{Student}/T) \parallel \text{Softmax}(\text{Teacher}/T)\right)\]
其二是监督损失(Supervised Loss): 利用交叉熵度量与真实硬标签的误差:
\[\mathcal{L}_{\text{supervised}} = \text{CE}(\text{Student}, y_{\text{true}})\]
总损失可以表示为
\[\mathcal{L}_{\text{total}} = \alpha \cdot \mathcal{L}_{\text{distill}} + (1-\alpha) \cdot \mathcal{L}_{\text{supervised}}\]
其中,\(\alpha\) 控制软/硬标签的权重(通常\(\alpha=0.7\))
自动调参
- 如果用Grid Search. 这个是最常见的。具体说,就是每种参数确定好几个要尝试的值,然后像一个网格一样,把所有参数值的组合遍历一下。优点是实现简单暴力,如果能全部遍历的话,结果比较可靠。缺点是太费时间了,特别像神经网络,一般尝试不了太多的参数组合。推荐一下我们的工作-递归的SSR系列
- 使用NNI,NNI是微软开源的一个自动调参框架,支持多种调参算法,包括TPE、SMAC等。NNI可以通过配置文件定义搜索空间和调参算法,然后自动进行超参数优化。
- GluonCV,李沐推荐的,但是基于MXNet,可能不太适合现在的主流框架。
参考文献
- https://zhuanlan.zhihu.com/p/630152281 深度学习十五条调参经验
- https://www.zhihu.com/question/315772308/answer/1636730368 如何调整learning rate
- https://zhuanlan.zhihu.com/p/461855995 网络调参,warmup+cosine decay
- https://zhuanlan.zhihu.com/p/592531559 CV调参技巧
- https://zhuanlan.zhihu.com/p/376068083 2021炼丹指南
- https://zhuanlan.zhihu.com/p/433048073 标签平滑
- https://blog.csdn.net/qq_42103091/article/details/123287865 NNI自动调参
- https://arxiv.org/pdf/1812.01187 Bag of Tricks for Image Classification with Convolutional Neural Networks
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除 |