深度学习(27)——YOLO系列(6)

深度学习(27)——YOLO系列(6)

咱就是说,需要源码请造访:Jane的GitHub:在这里等你哦
嗨,好久不见,昨天结束了yolov7的debug过程,真的在尽力句句理解,我想这应该是我更新的yolo系列的最后一篇,但是仅限于yolo,detect的话题还不会结束,还会继续进行,detect结束以后再说segmentation。和往常以一样的过程,从data开始。
注:在此不会句句讲解,只对我觉得很核心的内容展开,所以你懂吧,我能写进来的都是核心的核心,敲黑板!还有我一边debug一边写的草稿。足足有三页,这我怎么可以不出一篇blog和大家分享我的喜悦呢?

文章目录

  • 深度学习(27)——YOLO系列(6)
    • 1. 数据
      • 1.1 mosaic方法
    • 2. 模型
    • 3. 训练
    • 4. 推理

1. 数据

在这篇文章中用到的数据是东北大学发表的表面缺陷数据集(NEU-DET),已上传到资源,有需要自取。这个数据集收集了热轧钢带的六种典型表面缺陷,即轧制氧化皮(RS),斑块(Pa),开裂(Cr),点蚀表面( PS),内含物(In)和划痕(Sc)。该数据库包括1,800个灰度图像:六种不同类型的典型表面缺陷,每一类缺陷包含300个样本。对于缺陷检测任务,数据集提供了注释,指示每个图像中缺陷的类别和位置。对于每个缺陷,黄色框是指示其位置的边框,绿色标签是类别分数。
在training的配置中加载data.yaml 就OK

train: ..\NEU-DET\train.txt
val:..\NEU-DET\val.txt# number of classes
nc: 6# class names
names: ['crazing', 'inclusion', 'patches', 'pitted_surface', 'rolled-in_scale', 'scratches']

在这里插入图片描述
数据读入很简单,作者巧妙地运用了cache思想,数据只在第一次读入时需要很久,之后使用生成的cache加载数据。这里想说的是在数据增强部分的一个关键的方法mosaic方法。

1.1 mosaic方法

  • 核心思想: Mosaic数据增强 是一种在计算机视觉任务中常用的数据增强技术。它通过将多个不同样本的图像拼接成一个大的合成图像,然后将该合成图像用作训练数据来增加模型的鲁棒性和泛化能力

  • 使用步骤:

    • 随机选择四个不同的训练样本图像。【注:可以不是4个,比如9个,或者根据自己的想法拼接,但是一定要注意image和label要一起变化
    • 将这些选定的图像按照一定的比例拼接在一起,形成一个大的合成图像。
    • 对合成图像进行随机裁剪,以获得训练样本。
    • 对裁剪后的样本进行标签调整,以适应新的图像布局。
    • 使用裁剪后的样本作为训练数据,输入到模型中进行训练。
  • 使用该方法拟解决问题:引入更多的变化和复杂性,从而提高模型对于不同场景、不同尺度、不同物体相互间关系的识别和理解能力。通过将多个样本进行拼接,模型能够学习到更丰富的特征表示,并且在遇到类似的情况时更加稳定和可靠。

  • 适用场景:常用于目标检测、语义分割和图像分类等任务中,可以帮助模型更好地应对复杂场景、小目标、遮挡和视角变化等情况。

# 四个拼接
def load_mosaic(self, index):# loads images in a 4-mosaiclabels4, segments4 = [], []s = self.img_sizeyc, xc = [int(random.uniform(-x, 2 * s + x)) for x in self.mosaic_border]  # mosaic center x, yindices = [index] + random.choices(self.indices, k=3)  # 3 additional image indicesfor i, index in enumerate(indices):# Load imageimg, _, (h, w) = load_image(self, index)# place img in img4if i == 0:  # top leftimg4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tilesx1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc  # xmin, ymin, xmax, ymax (large image)x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h  # xmin, ymin, xmax, ymax (small image)elif i == 1:  # top rightx1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), ycx1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), helif i == 2:  # bottom leftx1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, w, min(y2a - y1a, h)elif i == 3:  # bottom rightx1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b]  # img4[ymin:ymax, xmin:xmax]padw = x1a - x1bpadh = y1a - y1b# Labelslabels, segments = self.labels[index].copy(), self.segments[index].copy()if labels.size:labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padw, padh)  # normalized xywh to pixel xyxy formatsegments = [xyn2xy(x, w, h, padw, padh) for x in segments]labels4.append(labels)segments4.extend(segments)# Concat/clip labelslabels4 = np.concatenate(labels4, 0)for x in (labels4[:, 1:], *segments4):np.clip(x, 0, 2 * s, out=x)  # clip when using random_perspective()# img4, labels4 = replicate(img4, labels4)  # replicate# Augment#img4, labels4, segments4 = remove_background(img4, labels4, segments4)#sample_segments(img4, labels4, segments4, probability=self.hyp['copy_paste'])img4, labels4, segments4 = copy_paste(img4, labels4, segments4, probability=self.hyp['copy_paste'])img4, labels4 = random_perspective(img4, labels4, segments4,degrees=self.hyp['degrees'],translate=self.hyp['translate'],scale=self.hyp['scale'],shear=self.hyp['shear'],perspective=self.hyp['perspective'],border=self.mosaic_border)  # border to removereturn img4, labels4

yolo-v7中使用的将9个拼接在一起

def load_mosaic9(self, index):# loads images in a 9-mosaiclabels9, segments9 = [], []s = self.img_sizeindices = [index] + random.choices(self.indices, k=8)  # 8 additional image indicesfor i, index in enumerate(indices):# Load imageimg, _, (h, w) = load_image(self, index)# place img in img9if i == 0:  # centerimg9 = np.full((s * 3, s * 3, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tilesh0, w0 = h, wc = s, s, s + w, s + h  # xmin, ymin, xmax, ymax (base) coordinateselif i == 1:  # topc = s, s - h, s + w, selif i == 2:  # top rightc = s + wp, s - h, s + wp + w, selif i == 3:  # rightc = s + w0, s, s + w0 + w, s + helif i == 4:  # bottom rightc = s + w0, s + hp, s + w0 + w, s + hp + helif i == 5:  # bottomc = s + w0 - w, s + h0, s + w0, s + h0 + helif i == 6:  # bottom leftc = s + w0 - wp - w, s + h0, s + w0 - wp, s + h0 + helif i == 7:  # leftc = s - w, s + h0 - h, s, s + h0elif i == 8:  # top leftc = s - w, s + h0 - hp - h, s, s + h0 - hppadx, pady = c[:2]x1, y1, x2, y2 = [max(x, 0) for x in c]  # allocate coords# Labelslabels, segments = self.labels[index].copy(), self.segments[index].copy()if labels.size:labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padx, pady)  # normalized xywh to pixel xyxy formatsegments = [xyn2xy(x, w, h, padx, pady) for x in segments]labels9.append(labels)segments9.extend(segments)# Imageimg9[y1:y2, x1:x2] = img[y1 - pady:, x1 - padx:]  # img9[ymin:ymax, xmin:xmax]hp, wp = h, w  # height, width previous# Offsetyc, xc = [int(random.uniform(0, s)) for _ in self.mosaic_border]  # mosaic center x, yimg9 = img9[yc:yc + 2 * s, xc:xc + 2 * s]# Concat/clip labelslabels9 = np.concatenate(labels9, 0)labels9[:, [1, 3]] -= xclabels9[:, [2, 4]] -= ycc = np.array([xc, yc])  # centerssegments9 = [x - c for x in segments9]for x in (labels9[:, 1:], *segments9):np.clip(x, 0, 2 * s, out=x)  # clip when using random_perspective()# img9, labels9 = replicate(img9, labels9)  # replicate# Augment#img9, labels9, segments9 = remove_background(img9, labels9, segments9)img9, labels9, segments9 = copy_paste(img9, labels9, segments9, probability=self.hyp['copy_paste'])img9, labels9 = random_perspective(img9, labels9, segments9,degrees=self.hyp['degrees'],translate=self.hyp['translate'],scale=self.hyp['scale'],shear=self.hyp['shear'],perspective=self.hyp['perspective'],border=self.mosaic_border)  # border to removereturn img9, labels9

从上面代码可以看到四个的时候是先确定中心点center,然后从左上到右下一次放图片。九个的时候是先将第一张图放在中心位置,然后从其上面逆时针一次是第二张到第八张图片。之后选择中心点进行裁剪。
为什么这样做呢?

  • 在数据加载的时候希望图片的大小是640,进入网络的图像希望是1280,如果是四张图片,直接根据center对原图裁剪或padding后就可以使用,并且有更多可能性。九张图片拼接在一起就是1920,随机选择一个中心点裁剪也不妨碍,这里可以自定义,没有必须怎么样,只要注意image和label的变化过程要完全对应,image和label对应即可!

2. 模型

模型的详细构造就不再赘述了,重点关注的是最后的detect,模型主要抽取了三个layer,三个layer是不同的网络深浅,网络结构越靠前学习到特征越是小细节的特征,越靠后越深层学习的特征越是全局特征。对于每一层的特征都会预测三个anchor,最终detect会得到一个长度为3的list,表示每个Layet的结果,其中每个元素的shape为【batch,anchor_no,character_size_width,character_size_height,11】,其中11=【x,y,w,h,置信度,one_hot_6_classes】
在这里插入图片描述

class Detect(nn.Module):stride = None  # strides computed during buildexport = False  # onnx exportend2end = Falseinclude_nms = Falseconcat = Falsedef __init__(self, nc=80, anchors=(), ch=()):  # detection layersuper(Detect, self).__init__()self.nc = nc  # number of classesself.no = nc + 5  # number of outputs per anchorself.nl = len(anchors)  # number of detection layersself.na = len(anchors[0]) // 2  # number of anchorsself.grid = [torch.zeros(1)] * self.nl  # init grida = torch.tensor(anchors).float().view(self.nl, -1, 2)self.register_buffer('anchors', a)  # shape(nl,na,2)self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2))  # shape(nl,1,na,1,1,2)self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch)  # output convdef forward(self, x):# x = x.copy()  # for profilingz = []  # inference outputself.training |= self.exportfor i in range(self.nl):x[i] = self.m[i](x[i])  # convbs, _, ny, nx = x[i].shape  # x(bs,255,20,20) to x(bs,3,20,20,85)x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()if not self.training:  # inferenceif self.grid[i].shape[2:4] != x[i].shape[2:4]:self.grid[i] = self._make_grid(nx, ny).to(x[i].device)y = x[i].sigmoid()if not torch.onnx.is_in_onnx_export():y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i]  # xyy[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # whelse:xy, wh, conf = y.split((2, 2, self.nc + 1), 4)  # y.tensor_split((2, 4, 5), 4)  # torch 1.8.0xy = xy * (2. * self.stride[i]) + (self.stride[i] * (self.grid[i] - 0.5))  # new xywh = wh ** 2 * (4 * self.anchor_grid[i].data)  # new why = torch.cat((xy, wh, conf), 4)z.append(y.view(bs, -1, self.no))if self.training:out = xelif self.end2end:out = torch.cat(z, 1)elif self.include_nms:z = self.convert(z)out = (z, )elif self.concat:out = torch.cat(z, 1)else:out = (torch.cat(z, 1), x)return out@staticmethoddef _make_grid(nx=20, ny=20):yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)])return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float()def convert(self, z):z = torch.cat(z, 1)box = z[:, :, :4]conf = z[:, :, 4:5]score = z[:, :, 5:]score *= confconvert_matrix = torch.tensor([[1, 0, 1, 0], [0, 1, 0, 1], [-0.5, 0, 0.5, 0], [0, -0.5, 0, 0.5]],dtype=torch.float32,device=z.device)box @= convert_matrix                          return (box, score)

3. 训练

训练过程和以往的以前一样,核心是:模型得到的predict和label之间计算loss。
loss由三部分组成:

1. target与predict的IOU
3. anchor的class label
  • 进入loss计算后要先对target进行处理build_target
    在这里插入图片描述

  • build target 中需要先进行正样本选择 find_positive
    在这里插入图片描述

  • 一共三层,每层的预测值都要与target做loss,每层都有三个anchor,三个anchor都有编号,需要先对其其进行编号与target拼接,从原来的(bcxywh)变为(bcxywh+no_anchor)
    在这里插入图片描述

  • 正样本选择中需要先比较groundtruth 和anchor之间的大小,需要两步筛选,第一步是筛除过大的groundtruth或者过小,groundtruth的h和w与anchor的hw比值控制在四分之一到四之间。在这之前要先将原来target还原为真实的坐标和长宽,原来是相对值,都在0-1之间,现在使用t = target* gain就是还原成这一层特征的位置,不是0-1之间的数了
    在这里插入图片描述

  • 这样就筛除了部分groundtruth,然后为了在原来的基础上增加一些新的点,使用g=0.5 ,相当于在原来的基础上增加了两个点对应的框,如果g=1就可以增加四个点对应的框【为了尽可能考虑多一点】
    在这里插入图片描述

  • 计算target和predict的IOU ,根据IOU计算iou的loss,取对数后取负值,IOU(<1)越大,对数值(<0)越大,取负数(>0)越小,使用topk(没有largest参数从小到大排序)取min(10,predict_num),将这些IOU累加确定最终选择每个正样本的anchor个数
    在这里插入图片描述

  • target的class类别是int,将其转换为one-hot类型,predict的class要和置信度做乘积,相当于加权。乘积后的值做变化与target的值做bce_with_logit损失
    在这里插入图片描述
    在这里插入图片描述

  • 最终的loss将IOU的loss和class的loss加权相加
    在这里插入图片描述

  • 根据前面确定的anchor个数得到最终target对应的预测值,matching_matrix大小是【target选择正样本个数*预测anchor个数】 在这里插入图片描述

  • 为了避免一个anchor被预测为多个正样本,对matching_matrix按列求和,大于1的,比较最终损失值cost,选择损失值最小的在这里插入图片描述
    复筛主要是根据target与predict的IOU以及预测class做的

4. 推理

在推理过程为了提高效率,速度更快:

  • 将BN与卷积权重参数融合
    在这里插入图片描述
  • 将1* 1卷积转换为3* 3卷积

今天先这样,饿了,去吃饭了,推理的BN和卷积合并下次上代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/20305.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为OD机试真题 Java 实现【不开心的小朋友】【2023 B卷 100分】,附详细解题思路

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 大家好&#xff0c;我是哪吒。 做技术&#xff0c;我是认真的&#xff0c;立志于打造最权威的华为OD机试真题专栏&#xff0c;帮助那些与我有同样需求的人&#xff…

免费系统维护清理工具:Onyx for Mac图文安装教程

OnyX 是一款适用于 macOS 的免费系统维护和优化工具。它由法国开发者 Jol Barrire&#xff08;也称为 Titanium&#xff09;创建&#xff0c;旨在帮助 macOS 用户管理和优化其计算机系统。 OnyX 提供了许多功能和工具&#xff0c;可以帮助用户执行各种系统维护任务。它是一个非…

阿里云服务器架构x86、GPU、ARM、裸金属和超级计算集群说明

阿里云服务器架构有什么区别&#xff1f;X86计算、ARM计算、GPU/FPGA/ASIC、弹性裸金属服务器、超级计算集群有什么区别&#xff1f;阿里云服务器网分享云服务器ECS架构详细说明&#xff1a; 目录 阿里云服务器ECS架构说明 X86计算 ARM计算 GPU/FPGA/ASIC 弹性裸金属服务…

火车头采集器下载中文图片地址报错:发生错误终止..

火车头采集器下载中文图片地址报错&#xff1a;发生错误终止.. 报错信息 该问题时网友发现的&#xff0c;采集的内容中图片URL地址包含中文字符。 然后在采集内容时火车头自动下载图片就提示&#xff1a;发生错误终止&#xff0c;远程服务器返回错误&#xff1a;&#xff08…

vue3的customRef

文章来源:我的博客,欢迎访问,不欢迎攻击,谁攻击谁儿子 customRef 作用:实现一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制 像是下面的代码一样: <template><div class"lim"><div class"btns"><el-input type"text…

优思学院|工厂如何从零开始开展TPM管理?

TPM管理的实施步骤&#xff0c;因应各企业的情况和特性&#xff0c;实施的方法会有点差异&#xff0c;但一般的基本步骤是以下表所表示。 这16个TPM管理的基本步骤&#xff0c;是将各企业之长处更加的发挥&#xff0c;且为了达成重点目标&#xff0c;组织全体的实施方法。 大…

【广州华锐互动】AR远程巡检系统在设备维修保养中的作用

随着科技的不断发展&#xff0c;AR(增强现实)远程巡检系统在设备检修中发挥着越来越重要的作用。这种系统可以将AR技术与远程通信技术相结合&#xff0c;实现对设备检修过程的实时监控和远程指导&#xff0c;提高设备检修的效率和质量。 首先&#xff0c;AR远程巡检系统可以帮助…

知行之桥 EDI 系统 XMLMap 操作指南

什么是XMLMap? XMLMap 的主要功能就是完成两个不同XML文件的关系映射&#xff0c;在知行之桥 EDI 系统中&#xff0c;将XMLMap 的全部功能都集成在了 XMLMap 端口中。 在正式使用XML Map 端口之前&#xff0c;我们先来了解一下此端口的内部构造&#xff0c;和其他端口类似&a…

基于单片机智能水杯 保温杯 定时提醒喝水 温度控制的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;LCD1602液晶显示当前水温&#xff0c;定时提醒&#xff0c;水量变化DS18B20检测当前水体温度&#xff1b;水位传感器检测当前水位&#xff1b;继电器驱动加热片进行水温加热&#xff1b;定时提醒喝水&#xff0c;蜂鸣器报警&#x…

【科研绘图】MacOS系统OmniGraffle实用指南

用过不少绘图软件&#xff0c;包括Visio (only for Windows)、ProcessOn、draw.io等主流软件&#xff0c;然后换Mac后尝试了实验室在用的OmniGraffle&#xff0c;才第一次感受到了绘图软件的人性化和强大&#xff01; 实用操作总结 按住Shift后调整元素位置或调整线段&#x…

LiveGBS流媒体平台GB/T28181功能-支持海康大华GB28181语音对讲需要的设备及服务准备

LiveGBS支持海康大华GB28181语音对讲需要的设备及服务准备 1、背景2、准备2.1、服务端必备条件&#xff08;注意&#xff09;2.2、准备语音对讲设备2.2.1、 大华摄像机2.2.1.1、 配置接入示例2.2.1.2、 配置音频通道编号 2.2.2、 海康摄像机2.2.2.1、 配置接入示例 3、开启音频…

Gradle下载和配置教程:Windows、Mac和Linux系统安装指南

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…