每一次深度模型调参都是让人头疼的东西,总感觉有很多玄学的东西能用,但事实上最后效果都不会太好。这里专门记录一些可能会有影响的调参方法。所以这是一个累积更新的文章。希望之后碰到新的方法和雷区能在这里记下,减少调试时间。

有效措施

数据预处理

数据预处理的效果可能比模型的各种调参来得更加直接有效。对于 NLP 任务可能不那么明显,但是对于 CV 或者 DSP 任务却是立竿见影的。

数据增广

计算机视觉中常用的方法。基本是对于图片随机翻转、随机截取、添加高斯模糊、随机遮盖等等措施。其中随机截取和随机翻转是一般都有效的,随机遮盖等则不一定。数据增广可以在模型训练中完成,让模型实时更改数据集内容;也可以在训练前就完成增广;两种方法都用也没有问题。

对于图片的数据增广方法:

1
2
3
4
5
6
7
8
9
import torchvision.transforms as tfs
transform = tfs.Compose([
tfs.Resize((256, 256)), # 先调整图片大小至256x256
tfs.RandomCrop((224, 224)), # 再随机裁剪到224x224
tfs.RandomHorizontalFlip(), # 随机的图像水平翻转,通俗讲就是图像的左右对调
tfs.RandomRotation(5),
tfs.ToTensor(),
tfs.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.2225)), # 维度和图像的channel相关
])

调用时使用即可:

1
img = transform(img)

数据标准化/归一化

即使是非常简单的数据标准化/归一化,就能取得显著的 performance 提高以及训练速度的大幅度提升。对于图像信息,可以使用灰度图均衡化,而对于数字信号或者其他信息,则可以使用 MinMax,标准化众多方法。这个对于模型训练速度的提升可能是数量级上。

但无论如何,数据标准化几乎应当是数据预处理的必要操作,绝对不能跳过。

1
2
3
4
5
6
7
8
9
# 直方图均衡化
import cv2
img = cv2.equalizeHist(img)

# 标准化 - 对于一维数据
def std(data):
mu = np.mean(data)
sigma = np.std(data)
return (data - mu) / sigma

训练

损失函数权重

对于普通的损失函数,对于不同的类加以相同的权进行处理。但是对于类分别不均的训练集/测试集,可以轻微调整模型的损失函数权重,一般权重添加方法是按照训练集的比例强行加权。一般这样就能获得略好的效果。但是还有一定的调整空间。事实上使用这个方法对于一些数据分布不均的方法有非常明显的效果。不过如果实在分布不均的话,可以考虑数据预处理方法解决。

1
2
weight = torch.Tensor([1, 0.8])
lossfun = nn.CrossEntropyLoss(weight = weight)

如果使用 cuda 训练,还应该申明.cuda()

学习率衰减

原来习惯性用 Adam 也就没有再关心过学习率的问题,但事实上学习率仍然是有影响的。使用过小的学习率前期的训练时间过长而且容易进入一个不太好的局部最优解,相反使用大的学习率在前期能够势如破竹。(甚至是数量级的提升)但是训练到后期的模型 performan 却需要更小的学习率,所以这里引入学习率衰减的方法。

1
2
3
4
5
def adjust_learning_rate(optimizer, epoch):
lr = 1e-4 * (0.4 ** (epoch // 8))
for param_group in optimizer.param_groups:
param_group['lr'] = lr
adjust_learning_rate(optimizer, epoch)

传入的 optimizer 参数就是 PyTorch 的模型。对于 Adam 模型,建议使用 $1e^{-4}$ 作为起始训练速率。

不稳定措施

添加模型的全连接层

添加全连接层会大幅度增加模型的复杂度以及参数量,强制减低模型的训练速度,但而提高模型的过拟合能力。所以增加全连接层方法在数据量不足以支撑的情况下是绝对不推荐的。但是有的时候能发挥一定的作用。

Dropout

Dropout 不是万能的方法。例如对于最后的分类器叠加 Dropout 反而会使模型的表现变得滑稽。Dropout 的添加是需要通过反复实验的。不过建议在模型的一开始不要使用 Dropout,因为模型自身的能力可能就不够,不能够很好地拟合训练数据。而且增加 Dropout 增加了训练时间,对于判断模型的强度也是不利的。在实验的最后发现过拟合严重可以考虑尝试使用 Dropout 方法。

Multi-task

这是一个很不确定的东西。Multi-task 的效果随着设计的多任务而变化。事实上,在我使用 Multi-task 的情况下,几乎都没有得到明显的提升,有的时候甚至会拖后腿使模型的能力下降。所以 Multi-task 是需要仔细思考后使用的。

加载预训数据

对于 NLP 任务,使用预训参数是必要的,特别是对于 BERT 等模型,即使固定参数,只训练后面的部分也能获得很好的效果。但是对于 CV 任务这个效果却不是一定的。例如在 COVID-19 分类任务中,我使用了 ResNet18 作为一个模型,并加载了 PyTorch 的预训结果,获得了训练速度的显著提升;然而在语谱图分类时,使用预训的结果并没有明显的优势。

所以模型的训练对象和预训的数据集应该有一定的共同之处时才能够发挥作用。语谱图是完全和 ImageNet 不同的东西,所以不能发挥作用也是在预期之中的。

基本无效措施