yolov7解析:yolov5的plus扩充 yoloV5代码如何变成V7!

yolov7来了,话不多说,强行回归,之前提到的一些nanodet/yolox的优化技巧,没想到V7也做了,这样挺好,减少了不少工作量,大体今天上午看了下主要是两部分核心:网络结构和辅助训练分支,从V7仓库适配了下代码,才发现缝合度极度严重,其实就是魔改版的V5,不过仍旧有值得学习的东西,同时想起V6(mt),让我明白一个道理:做事情一定要快!于是下午我就把V7之于V5的区别对比了下,并基于我自己的魔改版V5仓库进行了V7的添加,基本全程没什么大坑,还是比较容易的,分享在我的github上,代码是集成好的。目前V7其实是没有V5一些代码细节更好的,代码和文章持续更新请放心!还是有不少优化的地方,(还是老规矩:有问题留言或者私信,比较急的挂git issue,最近更新频繁,不管是GIT也好解析也罢,由于V7的作者也在不断更新修正代码,所以这也是不可避免的)。
基于最新的YOLOV5定期更新的,为什么要用yolov5修改?

1. 因为v7也是基于某一时间的V5代码上基础改的(因为这个作者的YOLOR当初也是基于V5做的添加),这样更利于快速上手,事实上从代码层面来讲本来也是基于v5的,很多共同的也没必要再复述
2. yolv5不断更新工程代码的规范,优化了不少小BUG和程序的问题
3. 学习进步
4. 同样参考基于yolov5的人体姿态关键点检测的Paper以及开源,估计最近V7仓库会放出来的。

common.py代码比较多,后续会规范整理!此时此刻,我差点想改个名字叫YOLOV5+7 =12? ,V7的yaml结构我放在了这里:

先补充下YoloV7的大致使用:
比如你想训练yolov7的P5-model直接run,注意官方将这个分离成了两部分,更直接一些。

	python train.py  --cfg  models/v7_cfg/training/yolov7.yaml  --weights yolov7.pt  --data (custom datasets)   

P6-modle ,run:

	python train.py  --cfg  models/v7_cfg/training/yolov7w6.yaml --imgsz 1280  --weights 'yolov7-w6_training.pt'  --data (custom datasets)  --aux_ota_loss  --hyp data/hyps/hyp.scratch-v7.custom.yaml

权重我挂在百度云上了!yolov7 预训练权重打包链接:yolov7 预训练权重打包链接 提取码:v7v7
请注意由于我删除了P6模型里的Reorg操作其实就是Focus(在我看来这个操作是没必要的),所以你需要重新训练或者微调下,如果你想使用V7原始权重,你只需要在YAML里改回去

使用需知:如何重参数化YOLOV7模型

这里以yolov7为例,我们看这段提供的代码,

  1. 训练training/下的yaml结构后,你的初始权重 xxx_training.pt会变成xxx.pt你需要加载训练好的权重yolov7xxx.pt,具体也可以参考重参数结构的脚本
  2. 然后使用deploy的模型去加载你训练的权重,改变层索引和结构,这样可以推理并且完成加速。

具体看官方tools/下的reparameterization.ipynb或者我的代码下的reparameterization.Py ,参考使用,后续会优化整理下便于使用。

nc=80
anchors=3
device = select_device('0', batch_size=1)
# model trained by cfg/training/*.yaml
# 注意这里官方给的yolov7.pt,这里会误导一些朋友出现问题,如果你用官方直接的yolov7.py是错误的,因为yolov7.pt是作者重参数的最终推理模型,头部的yoloR的key已经被移除了,你可以使用yolov7_traing.pt或者是你通过traing下的yaml所生成的权重,然后训练后会存为yolov7.pt,这个才是作者想表达的意思。
ckpt = torch.load('yolov7_training.pt', map_location=device) 
# reparameterized model in cfg/deploy/*.yaml
model = Model('models/v7_cfg/deploy/yolov7.yaml', ch=3, nc=80).to(device)
#print(model)

# copy intersect weights
state_dict = ckpt['model'].float().state_dict()
exclude = []
intersect_state_dict = {k: v for k, v in state_dict.items() if k in model.state_dict() and not any(x in k for x in exclude) and v.shape == model.state_dict()[k].shape}
model.load_state_dict(intersect_state_dict, strict=False)
model.names = ckpt['model'].names
model.nc = ckpt['model'].nc

for i in state_dict:
     print(i)
#print(intersect_state_dict)

# reparametrized YOLOR  将yolor头部的权重赋值
for i in range((model.nc+5)*anchors):
   
    model.state_dict()['model.105.m.0.weight'].data[i, :, :, :] *= state_dict['model.105.im.0.implicit'].data[:, i, : :].squeeze()
    model.state_dict()['model.105.m.1.weight'].data[i, :, :, :] *= state_dict['model.105.im.1.implicit'].data[:, i, : :].squeeze()
    model.state_dict()['model.105.m.2.weight'].data[i, :, :, :] *= state_dict['model.105.im.2.implicit'].data[:, i, : :].squeeze()
model.state_dict()['model.105.m.0.bias'].data += state_dict['model.105.m.0.weight'].mul(state_dict['model.105.ia.0.implicit']).sum(1).squeeze()
model.state_dict()['model.105.m.1.bias'].data += state_dict['model.105.m.1.weight'].mul(state_dict['model.105.ia.1.implicit']).sum(1).squeeze()
model.state_dict()['model.105.m.2.bias'].data += state_dict['model.105.m.2.weight'].mul(state_dict['model.105.ia.2.implicit']).sum(1).squeeze()
model.state_dict()['model.105.m.0.bias'].data *= state_dict['model.105.im.0.implicit'].data.squeeze()
model.state_dict()['model.105.m.1.bias'].data *= state_dict['model.105.im.1.implicit'].data.squeeze()
model.state_dict()['model.105.m.2.bias'].data *= state_dict['model.105.im.2.implicit'].data.squeeze()


# model to be saved
ckpt = {'model': deepcopy(model.module if is_parallel(model) else model).half(),
        'optimizer': None,
        'training_results': None,
        'epoch': -1}

# save reparameterized model
torch.save(ckpt, 'models/v7_cfg/deploy/yolov7.pt')

如果代码有BUG 。iussue上留言,今天被MT的V6刺激到黑化,程序是有CI 原则上不会有大问题,后续会完善额外的模型CI

一点就分享系列(实践篇5-上篇)

  • 一点就分享系列(实践篇5-上篇)[持续更新!全网首发]yolov7解析
  • 使用需知:如何重参数化YOLOV7模型
  • 一句话综述yolov7到底是什么!
  • 再次强调注意事项:
  • 一、模型结构—>保持性能,提升速度
  • 1. 核心改动一V7的核心结构-RepVgg
  • 2.核心改动一 辅助检测头分支—–提升训练性能
  • 2.1 模型训练设计思路和结构
  • IDetect 模块-yolor的模型
  • IBIN-Detect(这部分模型和训练 ,作者并未开源还,所以我后续会详细补充,核心就是该LOSS)
  • IAUX-Detect

一句话综述yolov7到底是什么!

千篇一律的东西我不想做,因为能看这个文章的人,基本都看过YOLOV5了,那么为什么我说yolov7只是yolov5的plus?
最直接的就是从git代码上可以看出基本是“”集百家之所长”,代码大概是今年5月份yolov5的版本基础上结合了自己的修改(别问我为什么知道,每周维护更新!),那么我很有底气的可以量化来说,同时也为后续大家看yolov7打通整体思路,一句话总结:

yolov7的代码= 5月之前的yolov5工程版本基础上+ v7作者YOLOR的改进(YOLOV4的AB佬)+总结新的重参数网络结构和算子/加入最近才开源的swinv2等算子结构+辅助检测分支/对应的LOSS标签匹配策略+模型结构的增加和解析引起的代码调整,包含初始化权重参数、优化器等

带来了性能的提升以及FLOPS的增加,最终综合来看还是一次进化,一些问题,比如调整NMS等一些参数和Trick在数据集上产生虚高精度,这里不过多讨论,本篇宗旨:抱着学习的态度去积累

通过看代码,可以看出V7还在做实验来验证一些结构,其次其实主体就是这些区别,接下来就是细化yolov7的各个细节,这部分可以慢慢在整理,梳理和总结学习。
yolov7存在过高的flops,但是仍旧具备不错的推理速度,完全依赖于重参数结构,让我们看看模型结构。

再次强调注意事项:

我的GIT加入的YOLOV7也是不断更新的,但是代码并不和V7完全一模一样,因为代码还在不断更新所以我举个例子:
比如V7的P6结构中,REORG我删除了,因为这FOCUS一样,所以我这部分还是使用YOLOV5的[-1, 1, Conv, [64, 6, 2, 2] 去替代Reorg,所以需要自己从新训练下模型。

一、模型结构—>保持性能,提升速度

1. 核心改动一V7的核心结构-RepVgg

设计初衷:结构重参数化,解耦训练和推理的逻辑,提升最终的推理速度和优化内存,先补一下regvgg的背景吧,其实这里也可以看出做轻量化模型设计的最近很火的一个思想就是降低mac!

  1. 3×3卷积非常快。在GPU上,3×3卷积的计算密度(理论运算量除以所用时间)可达1×1和5×5卷积的四倍。
  2. 单路架构非常快,因为并行度高。同样的计算量,“大而整”的运算效率远超“小而碎”的运算。
  3. 单路架构省内存。例如,ResNet的shortcut虽然不占计算量,却增加了一倍的显存占用。
  4. 单路架构灵活性更好,容易改变各层的宽度(如剪枝)。

训练时候是多分支,推理时候为单路结构,这样降低mac成本,再结合3X3卷积在GPU上的速度优势,RepVgg的block每层都加了平行的1×1卷积分支和恒等映射分支,这里和resnet不完全一样。

然后核心就是如何从多分支转为单路用于推理部署,来看看REPVGG怎么做的?

  1. conv和BN融合,这个老生常谈, 融合过程是conv带有bias的,目的就是推理时候进行加速.
  2. 1×1和3×3卷积融合,如何对齐?
    以3X3卷积为基准,因为1×1的Conv等价于卷积核多个0的3X3卷积,而indentity相当于单位矩阵的1×1卷积,那么同样等价于3×3卷积.
    只需要把1×1的卷积padding成3×3的卷积就行,这样就可以完成single_path
  3. 总体融合
    在3×3的卷积形式上对齐后,我们可以进行直接融合即对应相加。激活函数RELU因为涉及到数值精度,应该需要量化,所以没有融合激活函数。

其实以上思路就是跟tensorrt的网络加速优化有类似的地方,只不过trt做了更多的变换,包含激活函数的融合,垂直和水平融合,包含取消一些没必要的层比如concat。

总结repvgg,等效结构变换带来推理速度的质变,值得学习,参考TRT的融合或许可以做的更好。
yolov7的论文中对这个结构进行了再一次的调整,为了缓解dense网络的性能下降问题,如图:

使用(b)中的RepConv的Block,没有并行分支,作者团队发现有的identity 连接是不需要,并且不需要在conv和repconv直接进行indentity,这也算贡献吧,然后我们以yolov7中的几个yaml来熟悉下其结构,因为网络结构是IDetect ,是有yolor的结构
这里再概述下:YOLOV7的深层p6版本最后头部选用了基于yolor和aux辅助检测层的变体,来提升性能,【代码的话比较长!到现在了,我相信这种基础的模块代码大家都能看得明白,至于其中的SPPCSPC等结构的卷积、池化计算大家直接看源码】,直接上仓库的代码链接:common.py算子块,现在请大家仔细看下这个”训练版本”yaml的结构!

# parameters
nc: 80  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple

# anchors
anchors:
  - [12,16, 19,36, 40,28]  # P3/8
  - [36,75, 76,55, 72,146]  # P4/16
  - [142,110, 192,243, 459,401]  # P5/32

# yolov7 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [32, 3, 1]],  # 0
  
   [-1, 1, Conv, [64, 3, 2]],  # 1-P1/2      
   [-1, 1, Conv, [64, 3, 1]],
   
   [-1, 1, Conv, [128, 3, 2]],  # 3-P2/4  
   [-1, 1, Conv, [64, 1, 1]],
   [-2, 1, Conv, [64, 1, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [[-1, -3, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1]],  # 11
         
   [-1, 1, MP, []],
   [-1, 1, Conv, [128, 1, 1]],
   [-3, 1, Conv, [128, 1, 1]],
   [-1, 1, Conv, [128, 3, 2]],
   [[-1, -3], 1, Concat, [1]],  # 16-P3/8  
   [-1, 1, Conv, [128, 1, 1]],
   [-2, 1, Conv, [128, 1, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [[-1, -3, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [512, 1, 1]],  # 24
         
   [-1, 1, MP, []],
   [-1, 1, Conv, [256, 1, 1]],
   [-3, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [256, 3, 2]],
   [[-1, -3], 1, Concat, [1]],  # 29-P4/16  
   [-1, 1, Conv, [256, 1, 1]],
   [-2, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [[-1, -3, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [1024, 1, 1]],  # 37
         
   [-1, 1, MP, []],
   [-1, 1, Conv, [512, 1, 1]],
   [-3, 1, Conv, [512, 1, 1]],
   [-1, 1, Conv, [512, 3, 2]],
   [[-1, -3], 1, Concat, [1]],  # 42-P5/32  
   [-1, 1, Conv, [256, 1, 1]],
   [-2, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [[-1, -3, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [1024, 1, 1]],  # 50
  ]

# yolov7 head
head:
  [[-1, 1, SPPCSPC, [512]], # 51
  
   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [37, 1, Conv, [256, 1, 1]], # route backbone P4
   [[-1, -2], 1, Concat, [1]],
   
   [-1, 1, Conv, [256, 1, 1]],
   [-2, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1]], # 63
   
   [-1, 1, Conv, [128, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [24, 1, Conv, [128, 1, 1]], # route backbone P3
   [[-1, -2], 1, Concat, [1]],
   
   [-1, 1, Conv, [128, 1, 1]],
   [-2, 1, Conv, [128, 1, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [128, 1, 1]], # 75
      
   [-1, 1, MP, []],
   [-1, 1, Conv, [128, 1, 1]],
   [-3, 1, Conv, [128, 1, 1]],
   [-1, 1, Conv, [128, 3, 2]],
   [[-1, -3, 63], 1, Concat, [1]],
   
   [-1, 1, Conv, [256, 1, 1]],
   [-2, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1]], # 88
      
   [-1, 1, MP, []],
   [-1, 1, Conv, [256, 1, 1]],
   [-3, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [256, 3, 2]],
   [[-1, -3, 51], 1, Concat, [1]],
   
   [-1, 1, Conv, [512, 1, 1]],
   [-2, 1, Conv, [512, 1, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [512, 1, 1]], # 101
   
   [75, 1, RepConv, [256, 3, 1]],
   [88, 1, RepConv, [512, 3, 1]],
   [101, 1, RepConv, [1024, 3, 1]],

   [[102,103,104], 1, IDetect, [nc, anchors]],   # Detect(P3, P4, P5)
  ]

除此之外,在YOLOV7的仓库中还集成了swinv1、swinv2、yolor等模块构建模型,这里就不做过多介绍了,详情直接看官方或者我的仓库中的models/common.py。

2.核心改动一 辅助检测头分支—–提升训练性能

看过nano系列的Assign Guidance Module, 这一做法为了缓解轻量化目标检测网络的小检测头设计的训练不稳定问题,之前很多人问我怎么提升我也是说过加辅助分支这点,只是自己还没来得及想好怎么加和实现,正好来看看V7的做法:
由于检测头的深度降低后,模型性能会受到比较大的退化影响,正样本的质量很差,导致训练的MAP上不去,所以使用aux head帮助检测出一些好的正样本去匹配,辅助yolov7的head训练,这里叫Lead head。

上图(b)是带aux辅助分支的检测器结构,是比较常规的辅助结构。在深度网络的训练中,目前如yolov5等检测器,通过gt和预测的box计算CIOU等以IOU为基准的软标签分配来训练模型,一般的做法为:两端head单独去分配标签。



这里yolov7和naodet的作者设计的区别是:辅助aux Head是不如lead head的,也就是主检测的lead Head 的预测去引导辅助aux Head,也就是上图中利用aux head的大量低质量的正样本anchor进行一次coarse to fine ,因为aux召回率较高,找出更多的样本lead head再进行fine,来看一下,这里说下,yolov7在训练中主要引入了带有yolor的检测头有和aux的辅助检测头。

2.1 模型训练设计思路和结构

解耦化设计,因为这些组件是可以自己搭配的这才是最重要的,所以一定要清楚下面的设计结构。这里让我们分别看下,同时也是改动部分在yolo.py的几个检测头,YOLOV7中的模型结构简单规划为两大类基础(方便理解


这样你可以yaml中看出目前可以训练的版本是:
P5-model对应官方的yolov7、yolov7x、yolov7-tiny,训练头使用的是Idetect(YOLOR的内积算子);
P6-model对应官方训练的结构中除P5的模型,其余的yolov7-x6x.yaml的结构,head部分有IBINDetect、IAuxDetect等,也就是P6的网络才有辅助Aux-head部分的辅助分支。

IDetect 模块-yolor的模型

以V7作者的成名作YOLOR为基础的 Implicit加入,这里对于yolor不做过多解析(因为我也没仔细看过,有空补一下哈),简单来说YOLOR中的隐式知识结合卷积特征映射和乘法,代码比较简单,我对YOLOR的影响就是在深层模型上确实精度提高有效。即唯一区别:加入了Yolor的Head.

通过引入加法,可以使神经网络预测中心坐标的偏移量。还可以引入乘法来自动搜索锚点的超参数集,这是基于锚点的对象检测器经常需要的。此外,可以分别使用点乘和concat来执行多任务特征选择和为后续计算设置前提条件。

##### yolor #####

class ImplicitA(nn.Module):
    def __init__(self, channel, mean=0., std=.02):
        super(ImplicitA, self).__init__()
        self.channel = channel
        self.mean = mean
        self.std = std
        self.implicit = nn.Parameter(torch.zeros(1, channel, 1, 1))
        nn.init.normal_(self.implicit, mean=self.mean, std=self.std)

    def forward(self, x):
        return self.implicit + x
    

class ImplicitM(nn.Module):
    def __init__(self, channel, mean=0., std=.02):
        super(ImplicitM, self).__init__()
        self.channel = channel
        self.mean = mean
        self.std = std
        self.implicit = nn.Parameter(torch.ones(1, channel, 1, 1))
        nn.init.normal_(self.implicit, mean=self.mean, std=self.std)

    def forward(self, x):
        return self.implicit * x
class IDetect(nn.Module):
    stride = None  # strides computed during build
    export = False  # onnx export

    def __init__(self, nc=80, anchors=(), ch=()):  # detection layer
        super(IDetect, self).__init__()
        self.nc = nc  # number of classes
        self.no = nc + 5  # number of outputs per anchor
        self.nl = len(anchors)  # number of detection layers
        self.na = len(anchors[0]) // 2  # number of anchors
        self.grid = [torch.zeros(1)] * self.nl  # init grid
        a = 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 conv
        #self.ia就是与隐式表征相加
		#self.im就是与隐式表征相乘
        self.ia = nn.ModuleList(ImplicitA(x) for x in ch)  
        self.im = nn.ModuleList(ImplicitM(self.no * self.na) for _ in ch)
class IDetect(nn.Module):
    stride = None  # strides computed during build
    export = False  # onnx export

    def __init__(self, nc=80, anchors=(), ch=()):  # detection layer
        super(IDetect, self).__init__()
        self.nc = nc  # number of classes
        self.no = nc + 5  # number of outputs per anchor
        self.nl = len(anchors)  # number of detection layers
        self.na = len(anchors[0]) // 2  # number of anchors
        self.grid = [torch.zeros(1)] * self.nl  # init grid
        a = 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 conv
        
        self.ia = nn.ModuleList(ImplicitA(x) for x in ch)
        self.im = nn.ModuleList(ImplicitM(self.no * self.na) for _ in ch)

    def forward(self, x):
        # x = x.copy()  # for profiling
        z = []  # inference output
        self.training |= self.export
        for i in range(self.nl):
        """
        	对于每层FPN的输出每个通道先加上一个隐式表征,
        	然后经过预测头的卷积,再每个通道乘以一个隐式表征;
        	"""

            x[i] = self.m[i](self.ia[i](x[i]))  # conv
            x[i] = self.im[i](x[i])
            bs, _, 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:  # inference
                if 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()
                y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i]  # xy
                y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # wh
                z.append(y.view(bs, -1, self.no))

        return x if self.training else (torch.cat(z, 1), x)

IBIN-Detect(这部分模型和训练 ,作者并未开源还,所以我后续会详细补充,核心就是该LOSS)

在IDetect的基础上加入了SigmoidBIN 损失函数,这个结构目前在V7中没有使用(后续通过训练实验我会补充的更详细点),通过SigmoidBin这个函数,是 BCEWithLogitsLoss、 MSELoss组成的优化函数,通过该loss函数组合对w、h生成的softlabel去进行优化,在该函数里讲w和h的tensor长度定为22,那么整体就是额外的 44长度,加上NC和x、y,如果nc=80,那么每一个anchor预测的长度为:44+80+3=127.
这里我也有点小问题,正在等作者答复,他训练部分没有给出这个结构的训练样例,不过换汤不换药,我只是好奇为什么是21,V7作者的答复是开放性的选择:”可以将 bin _ count 更改为任何你想使用的值。在我们的实验中,21 ~ 41得到类似的结果,而 < 21在 数据集上得到的结果稍差一点。“

IAUX-Detect

waiting,明天继续更!

来源:啥都会一点的老程

物联沃分享整理
物联沃-IOTWORD物联网 » yolov7解析:yolov5的plus扩充 yoloV5代码如何变成V7!

发表评论