点击上方“AI公园”,关注公众号,选择加“星标“或“置顶”


作者:Pavel Semkin

编译:ronghuaiyang

导读

通过实验验证了图像分类技能包中每种技巧是否有效。

介绍

图像分类是计算机视觉中的一个关键问题。

在图像分类任务中,输入是一幅图像,输出是通常描述图像内容的类标签(如“猫”、“狗”等)。

近十年来,神经网络在解决图像分类问题方面取得了很大进展。神经网络在分类问题上的应用始于2012年,由Alex Krizhevsky、Ilya Sutskever和Geoffrey Hinton引入AlexNet。他们的模型在ImageNet挑战中达到了63.3%的第一名精度。目前,(截至2020年8月)排名第一的是名为“FixEfficientNet-l2”的网络,其成绩为88.5%。

图1:ImageNet数据集的提升

人们对给图片分配标签的任务进行了详细的研究。通常,在GitHub上搜索一个模型(或自己实现它)并在数据上训练它就足够了。你得到了一个可以准确预测标签的解决方案。

然而,当你对结果不满意时,改进模型可能会很棘手。你可以尝试以下方法之一:

  • 使用另一个模型。例如,如果你使用ResNet系列,你可以尝试使用更大的模型或切换到最近的修改,如ResNeSt。然而,这并不总是可能的,因为你可能受到资源的限制(例如,如果你的目标部署在一个像Raspberry Pi这样的小设备),拥有数亿个参数的最先进的模型可能无法放入内存,或者推理可能太慢。此外,通常,我们使用的是预先训练好的模型来做迁移学习,你需要为你的模型找到权重,但如果你做一些自定义更新或在GitHub上找到一个模型,这可能是一个问题。这就是为什么有时你必须修复所选择的模型,并找到其他方法来提高质量。

  • 增加数据集大小。增加额外的样本可以提高质量。这是一个很明显的选择,它确实可以帮助模型更好地泛化,但也存在一些问题。首先,你需要标记新数据或找到标记良好的公共数据集。在分类任务中,标记通常被认为是简单的,但这在很大程度上取决于任务的细节。例如,医学图像可能很难获取,甚至更难标记。此外,还需要确保这些新数据具有类似的分布,不会干扰模型。

  • 微调超参数。神经网络自己会更新数百万个参数,但有几个超参数,如优化器参数,损失权重等,需要研究人员去设置。由于超参数有很多可能的组合,如果没有任何先验知识或直觉,可能很难找到最好的一个。

  • 使用一些“技巧”。它们是人们用来提高性能的最佳实践。这些技巧与超参数调优不同,因为你需要了解模型内部和训练过程中发生了什么。通过在训练期间更新一些参数(例如使用一个特定的学习率策略)或在模型权值初始化期间以一种特定的方式初始化,你可以使训练更加稳定并提高最后的结果。

今天我们要用最后最后一条,通过使用一些技巧来提升模型性能,这些技巧都在“Bag of Tricks for Image Classification with Convolutional Neural Networks”中被测试过,这里会重新验证一遍。

数据集

在我们的实验中,我们使用Food-101数据集。它可以在Kaggle上下载。Lukas Bossard、Matthieu Guillaumin和Luc Van Gool在food101 - Mining Discriminative Components with Random Forests中介绍了该数据集。

它包括101种食物。每个类包含1000个图像。因此,整个数据集包含101,000张图像,并被划分为train和test两个子集。train部分为每个类包含750个图像。然而,为了提高训练速度,我们将类别数量从101个减少到21个。

图2:来自Food-101 数据集的图像样本

请按照指示准备数据集:

  • 从Kaggle网站下载zip-archive:https://www.kaggle.com/dansbecker/food101/download

  • 解压缩数据

  • 使用split_food-101.py将Food-101分割为训练/测试文件夹。这个脚本会解析train.txttest.txt并复制图像到相应的子文件夹。注意,我们硬编码了将要使用的类。

基线

我们使用ResNet-18架构作为基线。为了提升结果,我们使用了一个预训练过的ImageNet模型,该模型使用Adam优化器和交叉熵损失函数。默认LR为1e-4,在epochs 15和30之后,每次乘以0.1。总的来说,模型在1个Nvidia 1080Ti GPU上训练了40个epoch,batch size大小为32。我们使用PyTorch-Lightning框架来组织我们的代码。

:为了使我们的结果更可靠,我们在每个实验中使用不同的种子启动3次,并提供平均结果。

由于我们的数据集很大而且很多样,所以我们使用一个简单的增强策略。在训练期间,我们使用:

  • RandomResizedCrop

  • HorizontalFlip

  • Normalization

def get_training_augmentation():augmentations_train = A.Compose([A.RandomResizedCrop(224, 224, scale=(0.8, 1.0)),A.HorizontalFlip(),A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),ToTensorV2(),],)return lambda img: augmentations_train(image=np.array(img))

在验证过程中,我们遵循作者的策略,将图像的短边调整为256,保持宽高比不变。然后使用中心裁剪,得到224×224的方形区域:

def get_test_augmentation():augmentations_val = A.Compose([A.SmallestMaxSize(256),A.CenterCrop(224, 224),A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),ToTensorV2(),],)return lambda img: augmentations_val(image=np.array(img))

图像分类的技巧

首先,让我们把技巧分成两类:

  1. 高效的训练技巧 — 硬件和模型相结合的技巧,可能提高性能

  2. 训练优化 — 进一步提高质量的几个有趣的方法。

让我们详细讨论每一个技巧。

高效的训练技巧

Trick #1: 大Batch训练

Batch大小是一个至关重要的训练参数,尽管Batch越大收敛速度越快,效果越好,但对于其最优值却不一定。这是一个有争议的,同时也是一个被广泛研究的话题。下面是一些处理这个问题的启发式方法。由于资源有限,我们试验了batch大小16、32、64、96。

当我们增加batch大小时,我们不改变随机梯度的期望,但减少噪音,因此,减少了方差。这意味着,batch越大,我们的学习效率就越高。一种流行的方法是在训练过程中线性缩放学习率。例如,假设我们选择1e-4作为batch大小为32的初始学习率。然后,通过改变batch大小,我们增加学习率为1e-4*b/32。然而,在我们的案例中,我们发现,Adam优化器1e-4的收敛性和稳定性更好,所以我们没有进行太多的线性缩放实验。

表1:使用不同的batch size训练的结果

batch越大,训练时间越短,训练精度越低。

Trick #2: LR Warm-up

遵循这一启发式,我们使用最初的几个epochs来“热身”学习率。在训练开始时(当所有的参数都远离最优参数时)使用较高的学习率可能会导致数值不稳定性的而导致质量下降。假设我们想要在前m个epochs上热身,使用初始学习率,在第i个epoch中,1≤i≤m,学习率为:

def optimizer_step(self, epoch, batch_idx, optimizer, *args, **kwargs):# Learning Rate warm-upif self.args.warmup != -1 and epoch < self.args.warmup:lr = self.args.lr * (epoch + 1) / self.args.warmupfor pg in optimizer.param_groups:pg["lr"] = lr

在那之后,我们可以使用任何策略(multi-step衰减,plateau衰减)。在我们的实验中,我们使用6 epochs 热身,直到学习率变为1e-4,然后在15和30个epochs上衰减到得1e-5,1e-6。

图3:学习率策略

总的来说,这个技巧提高了0.08%的准确率,不是很显著。

表2:学习率热身的结果

Trick #3: 混合精度

在常用框架(PyTorch、TensorFlow)中,我们用32位浮点精度格式(FP32)训练我们的模型。换句话说,所有的参数,梯度,算术运算的结果都以这种格式存储。然而,由于优化的逻辑单元,现代硬件在精度较低的数据类型上可能表现出更好的性能。文章的作者表示,他们的Nvidia V100在FP32上具有14个TFLOPS,而在FP16中具有100个TFLOPS。不幸的是,我们的GPU (Nvidia 1080Ti)FP16时速度较低,所以我们不会看到FP32和FP16性能的任何显著差异。

混合精度训练的结果

如你所见,FP16提高了所有batch大小(BS)设置的训练速度,而且也提高了准确度。我们使用了Nvidia apex库,其中有FP32的O0优化级别和FP16的O1优化级别。在 PyTorch-Lightning 中,可以通过在命令行参数中添加--amp_level [Opt_level]在FP32和FP16之间切换。

训练优化

Trick #4: 余弦学习率衰减

除了多步衰减学习率策略外,还有一些我们可以使用的策略。例如,我们可以应用一个余弦函数来将学习率从初始值降低到0。假设有T个epoch(忽略热身阶段),初始学习率为l,那么在epoch T时,学习率l~T~的计算为:

这样做的目的是为了平稳地降低学习率,与步进衰减策略相比,可以获得更好的训练效果。在余弦衰减过程中,我们在开始和结束时慢慢降低学习速率,而在中间,下降速率几乎是线性的。

图4:余弦学习率衰减

可以注意到,在我们的案例中,这种方法提高了准确率。此外,使用余弦策略的实验时间更短。

表4:使用余弦学习率策略的结果

Trick #5: 标签平滑

在图像分类中,我们通常使用交叉熵损失函数:

通过标签平滑,我们将二元指标yi替换为:

代码实现:

# Based on https://github.com/pytorch/pytorch/issues/7455
class LabelSmoothingLoss(nn.Module):def __init__(self, n_classes, smoothing=0.0, dim=-1):super(LabelSmoothingLoss, self).__init__()self.confidence = 1.0 - smoothingself.smoothing = smoothingself.cls = n_classesself.dim = dimdef forward(self, output, target, *args):output = output.log_softmax(dim=self.dim)with torch.no_grad():# Create matrix with shapes batch_size x n_classestrue_dist = torch.zeros_like(output)# Initialize all elements with epsilon / N - 1true_dist.fill_(self.smoothing / (self.cls - 1))# Fill correct class for each sample in the batch with 1 - epsilontrue_dist.scatter_(1, target.data.unsqueeze(1), self.confidence)return torch.mean(torch.sum(-true_dist * output, dim=self.dim))

对于独热编码,模型通常对其预测过于自信,因为这种方法迫使模型做出最大可能的logit差距。这意味着正确的类的logit和其他类别的logit之间的训练结果会有巨大的差异,同时也可能导致错误的类的logit彼此之间有很大的差异。

标签平滑的使用鼓励模型从全连接层产生有限的输出,这可能导致更好的泛化。它迫使模型将正确类的logit与其他类的logit之间的差异设置为依赖于ε的常数。

表5:使用标签平滑训练的结果

总的来说,标签平滑使我们的结果提高了0.9%,我们还减少了6分钟的训练时间。

Trick #6: 知识蒸馏

知识蒸馏,就是先训练一个复杂而重的模型(我们使用ResNet-50),即教师模型,然后在教师的帮助下训练一个较轻的模型(学生模型)。我们假设一个更复杂的模型应该具有更高的准确率,因此,理论上,它可以提高学生模型的结果,同时保持其简单性。学生试图复制老师的结果。

为了进行蒸馏,我们修改了损失函数。我们根据老师和学生的得分的差别来进行惩罚。我们的损失函数从交叉熵损失,变成:

代码实现:

# Based on https://github.com/peterliht/knowledge-distillation-pytorch/blob/master/model/net.py
class KnowledgeDistillationLoss(nn.Module):def __init__(self, alpha, T, criterion=nn.CrossEntropyLoss()):super().__init__()self.criterion = criterionself.KLDivLoss = nn.KLDivLoss(reduction="batchmean")self.alpha = alphaself.T = Tdef forward(self, input, target, teacher_target):loss = self.KLDivLoss(F.log_softmax(input / self.T, dim=1),F.softmax(teacher_target / self.T, dim=1),) * (self.alpha * self.T * self.T) + self.criterion(input, target) * (1.0 - self.alpha)return loss

我们使用ResNet-50作为教师模型。对模型进行了标签平滑、余弦退火LR和线性预热的训练,获得了92.18%的Top-1准确率。

表6:使用知识蒸馏的训练结果

我们在准确率上取得了显著的增长,但增加了训练时间,因为我们需要从老师那里得到预测。

Trick #7: Mix-up 增强

Mix-up是一种增强技术,构造一个新的图像作为两个其他的线性组合。假设我们有两个batch的样本(我们取当前的batch和早期迭代中的batch),我们所做的是随机洗牌第二个batch,并从这两个batch中创建一个线性组合图像:

作为目标,我们从这两个batch中取标签。我们计算每个标签的损失,并返还加权总和作为总损失:

λ是一个来自β分布的随机数。

此外,还可以为这个新样本创建一个增强目标,作为原始目标的线性组合(如果目标是one-ho编码或平滑的)。

这一技巧有助于减少高置信度预测的数量,并可以提高准确率,但对人类来说,可能很难判断增强的图片是什么。

图5:mix-up增强的例子

代码实现:

    def mixup_batch(self, x, y, x_previous, y_previous):lmbd = (np.random.beta(self.args.mixup_alpha, self.args.mixup_alpha)if self.args.mixup_alpha > 0else 1)if x_previous is None:x_previous = torch.empty_like(x).copy_(x)y_previous = torch.empty_like(y).copy_(y)batch_size = x.size(0)index = torch.randperm(batch_size)# If current batch size != previous batch size, we take only a part of the previous batchx_previous = x_previous[:batch_size, ...]y_previous = y_previous[:batch_size, ...]x_mixed = lmbd * x + (1 - lmbd) * x_previous[index, ...]y_a, y_b = y, y_previous[index]return x_mixed, y_a, y_b, lmbdclass MixUpAugmentationLoss(nn.Module):def __init__(self, criterion):super().__init__()self.criterion = criteriondef forward(self, input, target, *args):# Validation stepif isinstance(target, torch.Tensor):return self.criterion(input, target, *args)target_a, target_b, lmbd = targetreturn lmbd * self.criterion(input, target_a, *args) + (1 - lmbd) * self.criterion(input, target_b, *args)

应用该技术的结果如下表所示:

表7:使用mix-up增强的训练结果

通过平滑标签来进行两个batch之间的Mix-up增强,可以提高准确率,但需要更多的时间。

福利: 技巧组合

最后,我们将这些技巧结合在一起,重新进行了实验。总体来说,我们使用了:

  • 线性学习率热身

  • 余弦学习率策略

  • 标签平滑

  • 知识蒸馏

可以预料,这些技巧的组合会给我们带来强大的改进,因为我们结合了最好的技巧。这种设置可以得到性能的提升。总的来说,我们将基线准确率提高了1%。你可以看到汇总表如下:

表8:结果汇总

总结

如上所示,以不同的方式改变训练过程可以帮助你提高准确率,但它是依赖于任务和数据的。这就是为什么,在我们的案例中,改进并不是那么显著,因为基线模型已经能够达到很高的结果了。

—END—

英文原文:https://www.learnopencv.com/bag-of-tricks-for-image-classification/

请长按或扫描二维码关注本公众号

喜欢的话,请给我个在看吧

图像分类的技能包及每一项的实验验证相关推荐

  1. layui自带验证体系:手机号验证、邮箱验证、必填项非空验证、数字验证(含代码、案例)

    layui自带验证体系:手机号验证.邮箱验证.必填项非空验证.数字验证(含代码.案例) 案例 · 截图: 实例代码: <!DOCTYPE html> <html> <he ...

  2. 未能加载文件或程序集“Enyim.Caching”或它的某一个依赖项。未能验证强名称签名...

    from:http://www.mzwu.com/article.asp?id=3741 itHub下载Enyim项目,编译后引用程序运行出错: 引用内容 未能加载文件或程序集"Enyim. ...

  3. 未能加载文件或程序集“Microsoft.mshtml”或它的某一个依赖项。未能验证强名称签名。此程序集可能已被篡改,或者已被延迟签名,但没有用正确的私钥进行完全签名。 (异常来自 HRESULT:0

    未能加载文件或程序集"Microsoft.mshtml"或它的某一个依赖项.未能验证强名称签名.此程序集可能已被篡改,或者已被延迟签名,但没有用正确的私钥进行完全签名. (异常来自 ...

  4. 【揭秘】50K+验证工程师求职加分项——MCU芯片验证

    无论是在秋招.春招.还是社招,IC企业在招聘时,芯片验证的岗位的需求量往往都是排在前列.即便是在内卷比较严重的今年,验证工程师的需求量要是排名前三. 一款芯片从立项到流片生产需要经过层层自测和验证,否 ...

  5. YOLOv4重磅发布,五大改进,二十多项技巧实验,堪称最强目标检测万花筒

    今年2月22日,知名的 DarkNet 和 YOLO 系列作者 Joseph Redmon 宣布退出 CV 界面,这也就意味着 YOLOv3 不会再有官方更新了.但是,CV 领域进步的浪潮仍在滚滚向前 ...

  6. DataGirdView 编辑项时的验证

    dgvConfig.DataSource = CreateTable();dgvConfig.Columns["编号"].ReadOnly = true; //只读dgvConfi ...

  7. 安装sql server 2008时的错误,针对SQL Server 注册表项的一致性验证失败​

    解决方法: 这个问题是之前安装过相同版本的SQL,但是你没有卸载干净,导致SQL在监测的时候发现有相同版本的注册表项,所以不会通过验证.解决方法很简单,百度下载一个Windows Install Cl ...

  8. 【华为ICT大赛2022-2023-----云赛道】加分项-沙箱实验流程及实验步骤

    参加云赛道的小伙伴们可要记着报名加分项,可是200分啊,点击查看视频步骤,轻轻松松拿到手!为我们大赛助力加油!!! 报名点击下方链接 一.华为云大赛平台 点击下面链接观看视频 华为ICT大赛[2022 ...

  9. 内容推荐场景中自监督学习的应用

    本系列文章包含每平每屋过去一年在召回.排序和冷启动等模块中的一些探索和实践经验,本文为该专题的第七篇. 第一篇指路:冷启动系统优化与内容潜力预估实践 第二篇指路:GNN在轻应用内容推荐中的召回实践 第 ...

最新文章

  1. FPGA设计细节和实现(初学者)
  2. c语言坐标三角形判断,【C语言】判断三角形类型(示例代码)
  3. C#期中考试题人事系统篇
  4. docker容器状态跟踪及疑惑
  5. FastReport问题整理(转)
  6. 个人计算机有控制器和运算器吗,cpu是由控制器和运算器组成的对还是错
  7. Redis-6.2.5 安装 Linux环境(单机)
  8. inputstream示例_Java InputStream available()方法与示例
  9. php连接excel表格数据类型,TP5 中使用PHPExcel 导出导入数据到excel表格
  10. 设置调试PHP,debug php输出所有错误信息
  11. 华为上机试---购物单(算法:背包问题)
  12. python用于数据处理真的那么强大嘛_为什么有那么多人选择Python,真的有那么好吗?...
  13. LINQ语句之Select/Distinct和Count/Sum/Min/Max/Avg
  14. 用本地计算机做服务器提供外网访问:花生壳+tomcat
  15. 2021年研究生数学建模竞赛优秀论文汇总
  16. JDK与JRE各种版本下载地址
  17. linux学习日志,linux学习日记范文
  18. 中国冰淇淋市场深度评估及发展趋势预测报告(2022版)
  19. Clion安装教程(Linux)
  20. 一梦江湖网页提交问题服务器错误,一梦江湖4月3日更新内容详情一览

热门文章

  1. matlab 函数句柄@的介绍_什么是函数句柄(转)
  2. matlab扩展的欧几里得算法_最大公约数问题(欧几里得算法)
  3. 介词 before behind before beside between
  4. word如何设置上标形式_word怎样设置上标
  5. colorkey口红怎么样_colorkey口红推荐什么颜色
  6. 374. Guess Number Higher or Lower*
  7. 掌握这六步,搭建完美的机器学习项目
  8. linux软raid阅读笔记,linux_软RAID阅读笔记.pdf
  9. 【机器学习】孤立森林
  10. 【Python百日进阶-数据分析】Day325 - plotly.express.scatter_3d():3D散点图