如有错误,恳请指出。


文章目录

  • 1. TTA概念介绍
  • 2. TTA代码实现
  • 3. TTA使用方法

一句话简单的介绍Test Time Augmentation(TTA)就是测试过程中也使用数据增强,官方教程介绍:Test-Time Augmentation (TTA) Tutorial


1. TTA概念介绍

在训练过程中数据增强是非常常用的一种手段,目的是为了提高模型的泛化能力,以免出现大小不一样,图像选择一下就分辨不出来的尴尬。那么TTA就是想在推理阶段也进行数据增强。不过不会太复杂,因为会增加额外的计算量,在打比赛的时候可能会用到,因为打比赛不在意你的推理时长是多久,所以可以尽情瞎造;但是在实际部署的情况下,因为推理速度减慢很可能会达不到实时监测的效果,所以实际是没有必要在推理也进行数据增强的,会降低监测速度。


2. TTA代码实现

知道了其原理是在推理阶段使用数据增强,那么很明显,其将在model中的前向传播过程中实现。在yolov5中,TTA 自动集成到所有YOLOv5 PyTorch Hub模型中。具体的解析我已经写在了注释中。

yolov5实现代码:

class Model(nn.Module):def __init__(self, cfg='yolov5s.yaml', ch=3, nc=None, anchors=None):  # model, input channels, number of classessuper().__init__()...# 如果直接传入的是dict则无需处理; 如果不是则使用yaml.safe_load加载yaml文件with open(cfg, errors='ignore') as f:self.yaml = yaml.safe_load(f)  # model dict...# 创建网络模型# self.model: 初始化的整个网络模型(包括Detect层结构)# self.save: 所有层结构中from不等于-1的序号,并排好序  [4, 6, 10, 14, 17, 20, 23]self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch])  # model, savelist...def forward(self, x, augment=False, profile=False, visualize=False):  # debug同样需要第三次才能正常跳进来if augment:     # use Test Time Augmentation(TTA), 如果打开会对图片进行scale和flipreturn self._forward_augment(x)  # augmented inference, Nonereturn self._forward_once(x, profile, visualize)  # single-scale inference, train# 使用TTA进行推理(当然还是会调用普通推理实现前向传播)def _forward_augment(self, x):img_size = x.shape[-2:]  # height, widths = [1, 0.83, 0.67]  # scalesf = [None, 3, None]  # flips (2-ud上下flip, 3-lr左右flip)y = []  # outputs# 这里相当于对输入x进行3次不同参数的测试数据增强推理, 每次的推理结构都保存在列表y中for si, fi in zip(s, f):# scale_img缩放图片尺寸# 通过普通的双线性插值实现,根据ratio来控制图片的缩放比例,最后通过pad 0补齐到原图的尺寸xi = scale_img(x.flip(fi) if fi else x, si, gs=int(self.stride.max()))yi = self._forward_once(xi)[0]  # forward:torch.Size([1, 25200, 25])# cv2.imwrite(f'img_{si}.jpg', 255 * xi[0].cpu().numpy().transpose((1, 2, 0))[:, :, ::-1])  # save# _descale_pred将推理结果恢复到相对原图图片尺寸, 只对坐标xywh:yi[..., :4]进行恢复# 如果f=2,进行上下翻转; 如果f=3,进行左右翻转yi = self._descale_pred(yi, fi, si, img_size)y.append(yi)    # [b, 25200, 25] / [b, 18207, 25] / [b, 12348, 25]# 把第一层的后面一部分的预测结果去掉, 也把最后一层的前面一部分的预测结果去掉# [b, 24000, 25] / [b, 18207, 25] / [b, 2940, 25]# 筛除的可能是重复的部分吧, 提高运行速度(有了解的朋友请告诉我一下)y = self._clip_augmented(y)  # clip augmented tailsreturn torch.cat(y, 1), None  # augmented inference, train# 普通推理def _forward_once(self, x, profile=False, visualize=False):# y列表用来保存中间特征图; dt用来记录每个模块执行10次的平均时长y, dt = [], []  # outputs# 对sequence模型进行遍历操作, 不断地对输入x进行处理, 中间结果需要保存的时候另外存储到列表y中for m in self.model:# 如果只是对前一个模块的输出进行操作, 则需要提取直接保存的中间特征图进行操作,# 一般是concat处理, 对当前层与之前曾进行一个concat再卷积; detect模块也需要提取3个特征层来处理if m.f != -1:  # if not from previous layerx = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f]  # from earlier layers# profile参数打开会记录每个模块的平均执行10次的时长和flops用于分析模型的瓶颈, 提高模型的执行速度和降低显存占用if profile:self._profile_one_layer(m, x, dt)# 使用当前模块对特征图进行处理# 如果是concat模块: 则x是一个特征图列表, 则对其进行拼接处理, 再交给下一个卷积模块;# 如果是C3, Conv等普通的模块: 则x是单一特征图# 如果是detct模块: 则x是3个特征图的列表 (训练与推理返回的内容不一样)x = m(x)  # run# self.save: 把所有层结构中from不是-1的值记下并排序 [4, 6, 10, 14, 17, 20, 23]y.append(x if m.i in self.save else None)  # save output# 特征可视化if visualize:feature_visualization(x, m.type, m.i, save_dir=visualize)return x# 翻转数据增强def _descale_pred(self, p, flips, scale, img_size):# de-scale predictions following augmented inference (inverse operation)if self.inplace:p[..., :4] /= scale  # de-scale xywh坐标缩放回原来大小# f=2,进行上下翻转if flips == 2:p[..., 1] = img_size[0] - p[..., 1]  # de-flip ud# f=3,进行左右翻转elif flips == 3:p[..., 0] = img_size[1] - p[..., 0]  # de-flip lrelse:x, y, wh = p[..., 0:1] / scale, p[..., 1:2] / scale, p[..., 2:4] / scale  # de-scaleif flips == 2:y = img_size[0] - y  # de-flip udelif flips == 3:x = img_size[1] - x  # de-flip lrp = torch.cat((x, y, wh, p[..., 4:]), -1)return p# 这里y的一个包含3个子列表的列表, 通过对输入图像x进行了3次不同尺度的变换, 所以得到了3个inference结构# 这里看不太懂, 不过大概做的事情就是对第一个列表与最后一个列表的结果做一些过滤处理# 把第一层的后面一部分的预测结果去掉, 也把最后一层的前面一部分的预测结果去掉, 然后剩下的concat为一个部分def _clip_augmented(self, y):# Clip YOLOv5 augmented inference tailsnl = self.model[-1].nl  # Detect(): number of detection layers (P3-P5)g = sum(4 ** x for x in range(nl))  # grid pointse = 1  # exclude layer counti = (y[0].shape[1] // g) * sum(4 ** x for x in range(e))  # indices: (25200 // 21) * 1 = 1200y[0] = y[0][:, :-i]  # large: (1,25200,25) -> (1,24000,25)i = (y[-1].shape[1] // g) * sum(4 ** (nl - 1 - x) for x in range(e))  # indices: (12348 // 21) * 16 = 9408y[-1] = y[-1][:, i:]  # small: (1,12348,25) -> (1,2940,25)return y...

这里部分的函数还会调用torch_utils来实现,比如scale_img通过双线性插值来实现图像的缩放(在通过pad0来补齐到原图的尺寸),这里额外贴上scale_img这个辅助函数:

scale_img函数:

# 通过普通的双线性插值实现,根据ratio来控制图片的缩放比例,最后通过pad 0补齐到原图的尺寸
def scale_img(img, ratio=1.0, same_shape=False, gs=32):  # img(16,3,256,416)# scales img(bs,3,y,x) by ratio constrained to gs-multipleif ratio == 1.0:return imgelse:h, w = img.shape[2:]s = (int(h * ratio), int(w * ratio))  # new sizeimg = F.interpolate(img, size=s, mode='bilinear', align_corners=False)  # resizeif not same_shape:  # pad/crop imgh, w = [math.ceil(x * ratio / gs) * gs for x in (h, w)]return F.pad(img, [0, w - s[1], 0, h - s[0]], value=0.447)  # value = imagenet mean

代码+注释有点长,具体就是通过双线性插值对图像进行缩放然后打上补丁到原图大小,推理完获取结果后再将推理结果恢复到相对原图图片尺寸, 只对坐标xywh:yi[…, :4]进行恢复。


3. TTA使用方法

官方教程链接:https://github.com/ultralytics/yolov5/issues/303

  • Test with TTA
python val.py --weights yolov5x.pt --data coco.yaml --img 832 --augment
  • Inference with TTA
python detect.py --weights yolov5s.pt --img 832 --source data/images --augment

官方文档指出,启用 TTA 的推理通常需要大约 2-3 倍的正常推理时间,因为图像正在左右翻转并以 3 种不同的分辨率进行处理,输出在 NMS 之前合并。同时这里为了避免太多的冗余结果,还已经通过了_clip_augmented函数去除了部分结果,然后再对3个不同分辨率处理后输出的结果进行合并再给nms进行后处理。速度下降的部分原因仅仅是图像尺寸较大(832 对 640),而部分原因是实际的 TTA 操作。因为,很显然,本来一个输入得多一个推理结果,而使用了TTA不仅使用了数据增强上下左右翻转(在源码中只对中间尺度进行左右翻转),同时还进行了多个尺度的输入处理获取了多个尺度的输出结果,这显然增加了3倍的工作量,所以启用 TTA 的推理通常需要大约 2-3 倍的正常推理时间,这是可以理解的。

此外,可以看见,其实使用TTA这个功能只需要设置augment这个参数,而这个参数在forward中进行控制。测试代码如下所示:

import torch# Model
model = torch.hub.load('ultralytics/yolov5', 'yolov5s')  # or yolov5m, yolov5x, custom# Images
img = 'https://ultralytics.com/images/zidane.jpg'  # or file, PIL, OpenCV, numpy, multiple# Inference
results = model(img, augment=True)  # <--- TTA inference# Results
results.print()  # or .show(), .save(), .crop(), .pandas(), etc.

此外,既然了解了原理,那么也可以自定义自己的TTA,可以执行设置自己推理阶段的数据增强,也就是需要改写_forward_augment函数即可:

def _forward_augment(self, x):img_size = x.shape[-2:]  # height, widths = [1, 0.83, 0.67]  # scalesf = [None, 3, None]  # flips (2-ud上下flip, 3-lr左右flip)y = []  # outputs# 这里相当于对输入x进行3次不同参数的测试数据增强推理, 每次的推理结构都保存在列表y中for si, fi in zip(s, f):# scale_img缩放图片尺寸# 通过普通的双线性插值实现,根据ratio来控制图片的缩放比例,最后通过pad 0补齐到原图的尺寸xi = scale_img(x.flip(fi) if fi else x, si, gs=int(self.stride.max()))yi = self._forward_once(xi)[0]  # forward:torch.Size([1, 25200, 25])# cv2.imwrite(f'img_{si}.jpg', 255 * xi[0].cpu().numpy().transpose((1, 2, 0))[:, :, ::-1])  # save# _descale_pred将推理结果恢复到相对原图图片尺寸, 只对坐标xywh:yi[..., :4]进行恢复# 如果f=2,进行上下翻转; 如果f=3,进行左右翻转yi = self._descale_pred(yi, fi, si, img_size)y.append(yi)    # [b, 25200, 25] / [b, 18207, 25] / [b, 12348, 25]y = self._clip_augmented(y)  # clip augmented tailsreturn torch.cat(y, 1), None  # augmented inference, train

总结yolov5实现的TTA手段:

  • 增大输入图像大小30%,如640 vs 832
  • left-right flipped
  • 3 different resolutions
  • the outputs merged before NMS

结果:和正常比慢 2-3X


参考资料:

1. Test-Time Augmentation (TTA) Tutorial

2. YOLOv5 Test-Time Augmentation (TTA) 教程:这篇是将Tutorial搬运下来的,所以本质上两个内容一样

YOLOv5的Tricks | 【Trick3】Test Time Augmentation(TTA)相关推荐

  1. Test time augmentation(TTA)

    Test time augmentation 数据扩充是模型训练期间通常使用的一种方法,它使用来自训练数据集的样本的修改副本来扩充训练集. 数据增强通常使用图像数据执行,其中使用一些执行的图像处理技术 ...

  2. YOLOv5的Tricks | 【Trick14】YOLOv5的val.py脚本的解析

    如有问题,恳请指出. 这篇可能是这个系列最后的一篇了,最后把yolov5的验证过程大致的再介绍介绍,基本上把yolov5的全部内容就稍微过了一遍了,也是我自己对这个项目学习的结束.(补充一下,这里我介 ...

  3. YOLOv5的Tricks | 【Trick11】在线模型训练可视化工具wandb(Weights Biases)

    如有错误,恳请指出. 与其说是yolov5的训练技巧,这篇博客更多的记录如何使用wandb这个在线模型训练可视化工具,感受到了yolov5作者对其的充分喜爱. 所以下面内容更多的记录下如何最简单的使用 ...

  4. YOLOv5的Tricks | 【Trick13】YOLOv5的detect.py脚本的解析与简化

    如有错误,恳请指出. 在之前介绍了一堆yolov5的训练技巧,train.py脚本也介绍得差不多了.之后还有detect和val两个脚本文件,还想把它们总结完. 在之前测试yolov5训练好的模型时, ...

  5. YOLOv5的Tricks | 【Trick7】指数移动平均(Exponential Moving Average,EMA)

    如有错误,恳请指出. 文章目录 1. 移动平均法 2. 指数移动平均 3. TensorFlow中的EMA使用 4. Yolov5中的EMA使用 这篇博客主要用于整理网上对EMA(指数移动平均)的介绍 ...

  6. YOLOv5的Tricks | 【Trick15】使用COCO API评估模型在自己数据集的结果

    如有错误,恳请指出. 在解析yolov5整个工程项目的时候要,已经对其detect.py脚本和val.py脚本进行分别的解析.其中,个人觉得detect脚本写得过于冗杂,所以分别为每个任务(图片推理, ...

  7. Kaggle小技巧:TTA(test time augmentation)测试时增强

    文章目录 1.原理简述 2.代码实现 1.原理简述 TTA:test time augmentation 顾名思义是测试时增强的意思,可将准确率提高若干个百分点,它就是测试时增强(test time ...

  8. tta部署_YOLOv5项目介绍

    YOLOv5 项目教程 作者:elfin  资料来源: 1.前言 ​项目自发布以来,直到现在仍然在不断改进模型.项目.作者的更新频率很大,很多问题都能够及时解决,当然问题也很多!到写稿此时,项目的de ...

  9. Ubuntu18下通过yolov5进行训练并预测

    一.环境 Ubuntu18.04         联想小新 yolov5s 二.前期准备 2.1.安装python3.7(python版本 > 3.6)_终端运行 sudo apt-get in ...

最新文章

  1. UriComponentsBuilder和UriComponents url编码
  2. Service Fabric 与Ocelot 的集成
  3. 信息学奥赛一本通 1178:成绩排序 | OpenJudge NOI 1.10 03:成绩排序
  4. java知识总结-24
  5. [USACO 1.5.1]数字金字塔
  6. JVM执行子系统探究——类文件结构初窥
  7. 探索Android FrameWork底层开发视频全套
  8. java mdb连接_[Java]如何连接一个带密码的mdb文件?
  9. html中字体 楷体_css怎么设置字体为楷体?
  10. trados 有道api_在Trados中使用微软机器翻译
  11. 日志:实现微信公众号自动问答机器人(待整理)
  12. 管理学研究中应用计算机仿真,计算机仿真在企业流程再造中应用研究.doc
  13. less 或 scss 覆盖UI组件样式并集选择器使用
  14. ESP8266_遥控小车网页版
  15. 共赴一场有温度的毕业典礼 风声家政商学院高级职业经理人研修班一期收官
  16. 【算法基础】DFS深度优先算法 —— AcWing 843. n-皇后问题 AcWing 842. 排列数字
  17. 二进制部署kubernetes 1.25.5(二)
  18. 深度分析蚂蚁金服RPC框架结构
  19. c语言中组件出现错误,错误消息:无法载入文件或组件 或其相依性的其中之一。 找到的组件资讯清单定义与组件参考不符。 (发生例外状况于 HRESULT: 0x80131040)...
  20. pandas之describe函数分析

热门文章

  1. 处理 ios端 输入框拉起键盘页面上移的问题
  2. C4D+OC渲染线框
  3. unicode转utf8
  4. 【译】Flutter 2 正式版的新功能,一睹为快
  5. OrbitControls缩放阻尼作用
  6. 通用渗透测试框架、简化渗透测试流程
  7. 实时数仓之 Kappa 架构与 Lambda 架构
  8. mac excel 正在计算机,在 Mac 上的 Numbers 表格中打开电子表格
  9. 多目标遗传算法NSGA
  10. JS 内存泄漏的几种情况以及解决方案