增强
色彩操作
-
颜色偏移
这种增强通过将每个通道乘以随机选择的系数来随机调整图像的色调、饱和度和亮度。系数的选择尽量在[0:6; 1:4]内选择,以确保图像不会偏离得太严重。def color_skew(image): h, s, v = cv2.split(image) h = h * np.random.uniform(low=0, high=6) s = s * np.random.uniform(low=1, high=4) v = v * np.random.uniform(low=0, high=6) return cv2.merge((h, s, v))
-
RGB通道规范化
这种增强通过从每个通道的值中减去每个通道的平均值并除以该通道的标准偏差来规范化图像的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))
-
灰度变换
这种增强通过灰度变换将图像转换为黑白图像。cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
-
Ben Graham:灰度+高斯模糊
这种增强将图像转换为灰度图,并应用高斯模糊来平滑图像中的任何噪声或细节。def ben_graham(image): image = cv2.cvtColor(image, cv2.COLOR_RGB2HSV) image = cv2.GaussianBlur(image, (5, 5), 0) return image
-
色调饱和度
这种增强将图像转换为HLS颜色空间,HLS颜色空间将图像分离为其色相、饱和度和亮度通道。cv2.cvtColor(image, cv2.COLOR_RGB2HLS)
-
LUV色彩空间
这种增强将图像转换为LUV色彩空间,该空间被设计为感知上均匀的,并实现更准确的颜色比较。cv2.cvtColor(image, cv2.COLOR_RGB2LUV)
-
alpha通道
这种增强为图像添加了alpha通道,可用于透明效果。cv2.cvtColor(image, cv2.COLOR_RGB2RGBA)
-
XYZ色彩空间
这种增强将图像转换为XYZ颜色空间,这是一个与设备无关的颜色空间,允许更准确的颜色表示。cv2.cvtColor(image, cv2.COLOR_RGB2XYZ)
-
色度亮度调整
这种增强将图像转换为YCrCb颜色空间,它将图像分离为亮度(亮度)和色度(颜色)通道。cv2.cvtColor(image, cv2.COLOR_RGB2YCrCb)
-
CIE Lab
这种增强将图像转换为CIE Lab颜色空间,该空间被设计为感知上均匀的,并实现更准确的颜色比较。cv2.cvtColor(image, cv2.COLOR_RGB2Lab)
-
YUV色彩空间
这种增强将图像转换为YUV颜色空间,YUV颜色空间将图像分离为亮度(亮度)和色度(颜色)通道。cv2.cvtColor(image, cv2.COLOR_RGB2YUV)
-
颜色抖动
这种增强会随机调整图像的亮度、对比度、饱和度和色调。transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5)
几何操作
-
中心裁剪(Center Crop)
这种增强随机裁剪一个长宽比为[3/4,4/3]的矩形区域,然后按[8%,100%]之间的因子随机缩放裁剪,最后将裁剪调整为 i m g _ s i z e × i m g _ s i z e img\_size\times img\_size img_size×img_size的正方形。每批测试都是随机进行的。transforms.CenterCrop((100, 100))
-
翻转(Flippings)
这种增强为图像增加了随机水平翻转的概率。例如,在概率为0.5的情况下,图像有50%的几率水平翻转。def flippings(image): if np.random.uniform() < 0.5: image = cv2.flip(image, 1) return image
-
随机裁剪
这种增强从图像中随机裁剪一个矩形区域。transforms.RandomCrop((100, 100))
-
随机调整大小裁剪
这种增强从图像中随机调整大小和裁剪矩形区域。transforms.RandomResizedCrop((100, 100))
-
随机仿射映象
这种增强随机地对图像应用仿射变换,包括旋转、缩放和剪切。transforms.RandomAffine(degrees=45, translate=(0.1, 0.1), scale=(0.5, 2.0), shear=45)
-
随机水平翻转
以0.5的概率随机水平翻转图像。transforms.RandomHorizontalFlip()
-
随机垂直翻转
这种增强以0.5的概率随机地垂直翻转图像。transforms.RandomVerticalFlip()
-
随机透视
这种增强随机地对图像应用透视转换。transforms.RandomPerspective()
-
随机旋转
这种增强以给定的角度范围随机旋转图像。transforms.RandomRotation(degrees=45)
-
随机反转
这种增强随机地颠倒了图像的颜色。transforms.RandomInvert()
-
随机色调分离
这种增强随机地减少了用于表示每个像素值的比特数,从而产生了偏置效果。transforms.RandomPosterize(bits=4)
-
随机过曝
这种增强随机地对图像应用过曝效应,其中超过一定强度阈值的像素被反转。transforms.RandomSolarize(threshold=128)
-
随机自动对比度
这种增强通过将强度值拉伸到完整的可用范围来随机调整图像的对比度。transforms.RandomAutocontrast()
-
随机均衡
这种增强随机地均衡图像的直方图,从而增加对比度。transforms.RandomEqualize()
高级增强
-
自动增广
它使用强化学习为给定的数据集搜索最优的增强策略。它已被证明可以提高图像分类模型的性能。from autoaugment import AutoAugment auto_augment = AutoAugment() image = auto_augment(image)
-
快速自动增广
快速自动增广是自动增广方法的更快实现。它使用神经网络来预测给定数据集的最佳增强策略。from fast_autoaugment import FastAutoAugment fast_auto_augment = FastAutoAugment() image = fast_auto_augment(image)
-
Augmix
Augmix是一种增强方法,它将多个增强图像组合在一起,创建一个单一的、更多样化和逼真的图像。研究表明,该方法可以提高图像分类模型的鲁棒性和泛化能力。from augmix import AugMix aug_mix = AugMix() image = aug_mix(image)
-
Mixup/Cutout
Mixup是一种通过线性插值两个图像的像素值来组合图像的增强方法。裁剪是一种从图像中随机移除矩形区域的增强方法。这些方法已被证明可以提高图像分类模型的鲁棒性和泛化能力。
例如:你拍一张猫的照片,然后在上面加上一些“透明的狗”。透明度的大小是一个超参数。x=lambda*x1+(1-lambda)x2 y=lambda*x1+(1-lambda)y2
验证上的增强
添加验证集数据增强将提高模型在测试时间增强上的性能,从而提高绝对性能。但如果寻求获得更好的性能,则使用一些验证集进行数据增强实验或在训练期间进行数据增强实验,并使用另一部分增加性能参数,这将减少模型过的拟合。
- 可以通过多次运行相同的模型来增加测试时间。
- 同时运行许多验证数据增强实验,以找到对模型性能影响最大的实验。
- 实使用实验:验和尝试vanilla模型,因为它们仍然显示出微妙的性能迹象。
测试上的增强
增强不仅在训练中有用,在测试时也有用。简单地将它们应用于预测和平均结果。
- 在不同的图像尺度上预测:尝试在不同的尺度上预测相同的图像。
- 颜色标准化:如上所述标准化。
预训练模型后添加隐藏层
添加更多的层是有益的,因为可以使用它们来学习更高级的功能,同时它也可以对大型预训练模型进行微调。
一层一层解冻
-
一个简单的技巧可以让你有一个微小的改善,那就是随着训练的进行,解冻你预先训练过的主干网络。
-
增加更多的层,并且冻结其他的层:事实证明,许多解决方案甚至可以通过在预先训练的模型训练后加入另一个训练阶段来进一步改进!这可以通过冻结预训练的模型并在其之后添加密集层来实现。
-
在PyTorch中冻结和解冻权重
# Weight freezing for param in model.parameters(): param.requires_grad = False # unfreeze weights in at all
# Weight unfreezing for param in model.parameters(): param.requires_grad = True # unfreeze weights in at all
-
TensorFlow中的冻结和解冻权重
# Weight freezing layer.trainable = False
# Weight unfreezing layer.trainable = True
Learning-rates和LR Shedulers
学习率和学习率调度器会影响模型的训练性能。改变学习率会对性能和训练收敛产生很大的影响。
Learning rate schedulers
最近,单周期余弦计划已经证明可以在多个NLP任务中提供更好的结果,可以这样使用它:
pytorch中一个余弦周期
from torch.optim.lr_scheduler import CosineAnnealingLR
optimizer = torch.optim.AdamW(optimizer_grouped_parameters, lr=args.learning_rate, eps=args.adam_epsilon)
scheduler = CosineAnnealingLR(optimizer, T_max=num_train_optimization_steps)
num_training_steps = num_train_optimization_steps / args.gradient_accumulation_steps
# Update the scheduler
scheduler.step()
# step the learning rate scheduler here, you will want to step the learning rate scheduler only once per optimizer step nothing more nothing less. So in this case, it should be called before you expect the gradients to be applied.
tensorflow中一个余弦周期
optimizer = tf.keras.optimizers.Adam(learning_rate)
scheduler = tf.keras.optimizers.schedules.CosineDecay(learning_rate, decay_steps=num_training_steps)
一些Learning Rate Schedulers技巧
- 使用
Triangular
或One Cyclic
方法进行学习率调度可以提供微妙但显著的改进,这些智能学习率调度方法可以克服一些批处理大小问题。 - 花点时间为你的任务和你正在使用的模型研究最佳的学习率调度方法,这是你的模型如何收敛的一个非常重要的部分。
- 学习率计划可用于训练具有较低批量大小或多个学习率或它们的任何组合的模型。
- 先尝试低学习率,看看大幅提高学习率对你的模型的表现是有利还是有害的。
- 在训练的后期阶段提高学习率或多个学习率或大的批处理大小或梯度积累或学习率调度器有时会帮助你的模型更好地收敛,这是一种高级技术,因为有时它会损害性能,但你可以测试给它一个很大的值的结果来判断。
- 损失缩放可以帮助减少损失方差,并在使用梯度积累或多个学习率或大的批处理大小时改善梯度流,但如果您试图通过增加批处理大小来解决这个问题,请尝试增加学习率,因为它有时可以产生更好的性能。
- 最近,单周期学习率调度方法占据了领先地位。
优化器的超参数
如果你想从Adam优化器中获得最佳性能,有几件事你需要知道:
- 找到最好的权重衰减值可能是棘手的,实验(和运气)将是你最好的朋友。
- 另一个重要的超参数是Adam优化器中使用的
beta1
和beta2
,选择最佳值取决于您的任务和数据。许多新任务可以从较低的beta1
和较高的beta2
中受益,而对于已建立的任务,它们将执行相反的操作。 - 在Adam优化器的世界里,第一条规则是不要低估优化器
ε
值的重要性。寻找最佳权值衰减超参数的原理同样适用于这里。 - 不要过度使用梯度截断规范,当你的梯度爆炸时,它可能会有帮助,而相反的情况也是如此——它会阻止某些任务的收敛。
- 梯度积累仍然提供一些微妙的好处,我通常积累梯度大约2步,但如果你的GPU没有耗尽内存,你可以推动到8个梯度积累步骤。在使用混合精度时,梯度积累也很有用。
过拟合和正则化
- 使用
dropout
!在层之间添加层dropout
通常会产生更多的训练稳定性和更健壮的结果,而且应该在隐藏层中使用。 - Dropout也可以用来小幅度提高性能,在训练任务和模型前尝试设置Dropout层。
-
正则化:当你的神经网络过拟合或欠拟合时,正则化可以提供极大的性能提升,对于正常的机器学习模型,
L1
或L2
正则化是可以的,但也可以尝试使用添加层和隐藏层。 - 多验证:可以通过使用多个验证来提高模型对过拟合的鲁棒性。然而,这是以计算时间为代价的。
优化器
现在每个人都在用Adam或AdamW。但是需要考虑的是,如果你的SGD有足够的动量进行超参数搜索,你可能会得到更好的结果。但这同样需要大量的调整。
有几个值得注意的重要优化器需要了解:
- AdamW:这是对Adam算法的扩展,可以防止模型权重在外层的指数级衰减,以及鼓励低于默认权重的惩罚超容量。
- Adafactor:它被设计为具有低内存使用量并且是可扩展的。这个优化器可以使用多个gpu提供显著的优化性能(见下文)。
- Novograd:基本上是另一个类似Adam的优化器,但具有更好的属性。它是用于训练超大模型的优化器之一。
- Ranger:Ranger优化器是一个非常有趣的优化器,在性能优化方面有很好的结果,但它显然不太为人所知或支持。
- Lamb:GPU优化可重用Adam优化器由GLUE和QQP竞赛的优胜者开发。
- Lookahead:一个流行的优化器,您可以在其他优化器之上使用,它将为您提供一些性能提升。
标签平滑
基本上是一个简单的技巧: y _ t r u e = y _ t r u e ∗ ( 1.0 − ε ) + 0.5 ∗ ε [ e x a m p l e : ε = 0.001 ] y\_true=y\_true*(1.0-\varepsilon)+0.5*\varepsilon [example: \varepsilon = 0.001] y_true=y_true∗(1.0−ε)+0.5∗ε[example:ε=0.001],通常效果很好。
Tensorflow
loss = BinaryCrossentropy(label_smoothing = label_smoothing)
pytorch
from torch.nn.modules.loss import _WeightedLoss
class SmoothBCEwLogits(_WeightedLoss):
def __init__(self, weight = None, reduction = 'mean', smoothing = 0.0, pos_weight = None):
super().__init__(weight=weight, reduction=reduction)
self.smoothing = smoothing
self.weight = weight
self.reduction = reduction
self.pos_weight = pos_weight
@staticmethod
def _smooth(targets, n_labels, smoothing = 0.0):
assert 0 <= smoothing < 1
with torch.no_grad(): targets = targets * (1.0 - smoothing) + 0.5 * smoothing
return targets
def forward(self, inputs, targets):
targets = SmoothBCEwLogits._smooth(targets, inputs.size(-1), self.smoothing)
loss = F.binary_cross_entropy_with_logits(inputs, targets,self.weight, pos_weight = self.pos_weight)
if self.reduction == 'sum': loss = loss.sum()
elif self.reduction == 'mean': loss = loss.mean()
return loss
高级技巧
知识蒸馏
用一个大的教师网来指导一个小的教师网的学习。
- 训练大模型:在数据上训练一个大型模型。
- 计算soft标签:利用训练好的大模型计算软标签。即大模型“软化”后的softmax输出
- 训练学生模型:在大模型的基础上,训练一个基于教师输出的学生模型作为额外的软目标损失函数,并通过插值调整两个损失函数的比例。
伪标签
使用一个模型来标记未标记的数据(例如测试数据),然后使用新的标记数据来训练模型。
- 训练教师模型:用你拥有的数据训练一个模型。
- 计算软标签:利用训练好的大模型对未标记数据进行软目标计算。
- 重要的:只使用你的模型“确定”的目标只使用最高置信度的OOF预测作为伪标签,以尽可能避免错误。(如果你不这样做,它可能不会起作用。)
- 训练学生模型:用你拥有的新标记数据训练一个学生模型。
如何进行CV伪标注?简单:将未标记的数据连接到OOF,并像使用堆叠一样简单地使用它们。
误差分析
一个可以为您节省大量时间的重要实践是使用您的模型来查找更难或破碎的数据样本。
对于你的模型来说,一个图像“更难”的原因有很多,例如,小的目标对象,不同的颜色,被切断的目标,无效的注释等等。
有错误反而是好消息!
如果你很难解释你的模型发生了什么,看看你的模型遇到的验证样本可能是个好主意。
发现模型的错误
发现错误最简单的方法是根据模型的置信度评分对验证样本进行排序,并查看哪些样本被预测的置信度最低。
mistakes_idx = [img_idx for img_idx in range(len(train)) if int(pred[img_idx] > 0.5) != target[img_idx]]
mistakes_preds = pred[mistakes_idx]
sorted_idx = np.argsort(mistakes_preds)[:20]
# Show the images of the sorted idx here..