精华内容
下载资源
问答
  • yolov5添加注意力机制--以EPSA为例

    千次阅读 热门讨论 2021-06-25 11:08:54
    修改comm.py,增加注意力机制的类 修改yaml文件,嵌入注意力机制 然后修改yolo.py,增加elif条件

    修改yaml文件,嵌入注意力机制

    一定要注意通道数,嵌入位置可以根据自己情况决定

    并不一定嵌入一个也不一定嵌入在我写的位置

    backbone:
      # [from, number, module, args]
      [[-1, 1, Focus, [64, 3]],  # 0-P1/2
       [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
       [-1, 3, BottleneckCSP, [128,3]],
       [-1, 1, PSAModule, [128,64]]
       [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
       [-1, 9, BottleneckCSP, [256]],
       [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
       [-1, 9, BottleneckCSP, [512]],
       [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
       [-1, 1, SPP, [1024, [5, 9, 13]]],
       [-1, 3, BottleneckCSP, [1024, False]],  # 9
      ]
    

    修改comm.py中,增加注意力机制的类,这个类可以去你选择的注意力机制代码中找,一般注意力机制都是随插随用

    # EPSANet
    def conv(in_planes, out_planes, kernel_size=3, stride=1, padding=1, dilation=1, groups=1):
        """standard convolution with padding"""
        return nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride,
                         padding=padding, dilation=dilation, groups=groups, bias=False)
    
    
    class SEWeightModule(nn.Module):
    
        def __init__(self, channels, reduction=16):
            super(SEWeightModule, self).__init__()
            self.avg_pool = nn.AdaptiveAvgPool2d(1)
            self.fc1 = nn.Conv2d(channels, channels // reduction, kernel_size=1, padding=0)
            self.relu = nn.ReLU(inplace=True)
            self.fc2 = nn.Conv2d(channels // reduction, channels, kernel_size=1, padding=0)
            self.sigmoid = nn.Sigmoid()
    
        def forward(self, x):
            out = self.avg_pool(x)
            out = self.fc1(out)
            out = self.relu(out)
            out = self.fc2(out)
            weight = self.sigmoid(out)
    
            return weight
    
    
    class PSAModule(nn.Module):
    
        def __init__(self, inplans, planes):
            super(PSAModule, self).__init__()
            conv_kernels = [3, 5, 7, 9]
            stride = 1
            conv_groups = [1, 4, 8, 16]
            self.conv_1 = conv(inplans, planes // 4, kernel_size=conv_kernels[0], padding=conv_kernels[0] // 2,
                               stride=stride, groups=conv_groups[0])
            self.conv_2 = conv(inplans, planes // 4, kernel_size=conv_kernels[1], padding=conv_kernels[1] // 2,
                               stride=stride, groups=conv_groups[1])
            self.conv_3 = conv(inplans, planes // 4, kernel_size=conv_kernels[2], padding=conv_kernels[2] // 2,
                               stride=stride, groups=conv_groups[2])
            self.conv_4 = conv(inplans, planes // 4, kernel_size=conv_kernels[3], padding=conv_kernels[3] // 2,
                               stride=stride, groups=conv_groups[3])
            self.se = SEWeightModule(planes // 4)
            self.split_channel = planes // 4
            self.softmax = nn.Softmax(dim=1)
    
        def forward(self, x):
            batch_size = x.shape[0]
            x1 = self.conv_1(x)
            x2 = self.conv_2(x)
            x3 = self.conv_3(x)
            x4 = self.conv_4(x)
    
            feats = torch.cat((x1, x2, x3, x4), dim=1)
            feats = feats.view(batch_size, 4, self.split_channel, feats.shape[2], feats.shape[3])
    
            x1_se = self.se(x1)
            x2_se = self.se(x2)
            x3_se = self.se(x3)
            x4_se = self.se(x4)
    
            x_se = torch.cat((x1_se, x2_se, x3_se, x4_se), dim=1)
            attention_vectors = x_se.view(batch_size, 4, self.split_channel, 1, 1)
            attention_vectors = self.softmax(attention_vectors)
            feats_weight = feats * attention_vectors
            for i in range(4):
                x_se_weight_fp = feats_weight[:, i, :, :]
                if i == 0:
                    out = x_se_weight_fp
                else:
                    out = torch.cat((x_se_weight_fp, out), 1)
    
            return out

    最后修改yolo.py,增加elif条件

    在yolo.py开头载入库的代码中添加common.py写好的注意力机制PSAModule

    from models.common import Conv, Bottleneck, SPP, DWConv, Focus, BottleneckCSP, Incep, BasicConv2d, Concat,PSAModule,MultiSpectralAttentionLayer

    在yolo.py代码,大概240行左右(具体情况视你自己的版本决定,我中间添加了一些东西记不清楚具体哪一行),在elif语句中添加注意力模块

            elif m is nn.BatchNorm2d:
                args = [ch[f]]
            #ori_yolo
            elif m is Concat:
               c2 = sum([ch[-1 if x == -1 else x + 1] for x in f])
            #modify_Bifpn
            #elif m is Concat:
             #c2 = max([ch[-1 if x == -1 else x + 1] for x in f])
            #elif m in [Inceptionv1, C3]:
                #args = [ch[f]]
            elif m is Detect:
                args.append([ch[x + 1] for x in f])
                if isinstance(args[1], int):  # number of anchors
                    args[1] = [list(range(args[1] * 2))] * len(f)
                #PSAM,attention
            elif m is PSAModule:
                channel, re = args[0], args[1]
                channel = make_divisible(channel * gw, 8) if channel != no else channel
                args = [channel, re]

    注意事项:添加注意力机制并不一定能提升效果,在添加过程中一定要注意yaml文件中的通道数是否匹配

    展开全文
  • yolov5-attention-源码

    2021-04-12 15:14:42
    2021年1月5日: :nn.SiLU()激活,记录, 集成。 2020年8月13日: :nn.Hardswish()激活,数据自动下载,本机AMP。 2020年7月23日: :改进了模型定义,培训和mAP。 2020年6月22日: 更新:新机头,减少了...
  • 这次比赛选择了官方提供的baseline yolov5进行训练,一开始使用的是yolov5s.yml配置文件进行训练的,并且数据也只是train2一小部分,由于笔者这里服务器只有一个1080Ti的可以使用,所以实验跑起来速度还是有点慢的,...

    这次比赛选择了官方提供的baseline yolov5进行训练,一开始使用的是yolov5s.yml配置文件进行训练的,并且数据也只是train2一小部分,由于笔者这里服务器只有一个1080Ti的可以使用,所以实验跑起来速度还是有点慢的,做的尝试也不是很多,下面是流水账。

    第一次成功提交

    第一次提交就是使用了train2部分数据集,设置了50个epoch,使用迁移学习,分辨率设置为500x500,花费大概2个小时训练完成。这个成绩的acc还不错,是因为conf thresh设置的值比较低,所以acc可以达到比较高的结果。但是mAP就很差,一方面是数据量不足导致的,另一方面是模型容量比较小。

    第二个比较不错的结果

    之后开始将train1部分的数据加进来,增大epoch个数到100,模型使用更大的yolov5x.yml,分辨率也提高到1000x1000,虽然有所提高,但是提高并不多。值得一提的是数据直接通过wget在linux中下载,并解压会出现错误,使用了论坛提供的tar方法也没有很好的解决。window上测试解压效果就很好,图片都没有损坏,不知道具体原因。但是如果从我本地window上传到服务器上,速度慢的简直不可忍受,所以就放弃了上传。采用了那些没有损坏的图片进行训练,尽管失去了一部分数据集,数据量还是很大的,训练yolov5x一般需要12-24个小时,时间比较久。

    后边怀疑可能是yolov5自带的mosic数据增强方法有问题,因为它会将四张图片组成一个进行训练,比较长的目标会有所损耗,所以关闭了这个数据增强方法。经过很长时间的训练,发现mosic还是有效果的,去掉了应该会掉点。

    去掉了mosic数据增强方法

    后边时间就到现在了,期间研究了一下yolov5的模型组织方式。因为之前笔者曾经用过yolov3, 那时候的数据组织方式是cfg文件,比较容易理解,但是也比较难改。在yolov5中使用了yaml文件进行组织,重复的模块可以通过number设置即可,降低了构建的难度。yolov5中也提供了多种多样的新模块,比如:CSP模块、SPP模块、GhostBottleneck模块、MixConv2d模块、CrossConv模块等等,这都是比较新的文章中提到的,方便进行实验。

    因为笔者之前研究过attention机制,也成功在yolov3中添加过attention模块,带来了一定的收益。所以之后的改进思路是添加SELayer,这个注意力模块的鼻祖。一般来说注意力模块作用是:增加模型的远距离依赖、增加模型复杂度、提高准确率(不绝对)等作用。这次也想在yolov5中研究添加SE的方法,这里做一个笔记总结。实验还在跑,后边会补充结果。

    先讲一下配置文件:以yolov5x.yaml为例:

    # parameters
    nc: 15  # number of classes
    depth_multiple: 1.33  # model depth multiple
    width_multiple: 1.25  # layer channel multiple
    
    # anchors
    anchors:
      - [10,13, 16,30, 33,23]  # P3/8
      - [30,61, 62,45, 59,119]  # P4/16
      - [116,90, 156,198, 373,326]  # P5/32
    
    # YOLOv5 backbone
    backbone:
      # [from, number, module, args]
      [[-1, 1, Focus, [64, 3]],  # 0-P1/2                 #1
       [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4              #2
       [-1, 3, C3, [128]],                                #3
       [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8              #4
       [-1, 9, C3, [256]],                                #5
       [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16             #6
       [-1, 9, C3, [512]],                                #7
       [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32            #8
       [-1, 1, SPP, [1024, [5, 9, 13]]],                  #9
       [-1, 3, C3, [1024, False]],  # 9                   #10
      ]
    
    # YOLOv5 head
    head:
      [[-1, 1, Conv, [512, 1, 1]],                        #11
       [-1, 1, nn.Upsample, [None, 2, 'nearest']],        #12
       [[-1, 6], 1, Concat, [1]],  # cat backbone P4      #13
       [-1, 3, C3, [512, False]],  # 13                   #14
    
       [-1, 1, Conv, [256, 1, 1]],                        #15
       [-1, 1, nn.Upsample, [None, 2, 'nearest']],        #16 
       [[-1, 4], 1, Concat, [1]],  # cat backbone P3      #17
       [-1, 3, C3, [256, False]],  # 17 (P3/8-small)      #18
    
       [-1, 1, Conv, [256, 3, 2]],                        #19
       [[-1, 14], 1, Concat, [1]],  # cat head P4         #20
       [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)    #21
    
       [-1, 1, Conv, [512, 3, 2]],                        #22
       [[-1, 11], 1, Concat, [1]],  # cat head P5         #23
       [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)    #24
    
       [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
      ]
    
    • nc : 代表class数目,目标检测一共有几个类

    • depth_multiple: 控制模型重复个数

    • width_multiple: 控制模型

    • anchors: 每一行对应一个detect,注意和head最后一行第一个参数相匹配。

    • backbone: 这个计数是从0开始的,不要被我后边写的数字误导。

      • 每行有四个参数:[from, number, module, args]
      • from: 代表连接哪一层,-1代表上一层。
      • number:这个模块的重复次数
      • module:模块的名称,一般在common.py和experimental.py中有这些模块的名称,也可以在里边添加新的模块。
      • args:模块需要的参数。
    • head:对yolov3熟悉的应该知道,这部分构建的是FPN, 其中最后一行就是检测头。

      • detect:最后一行是检测头,第一个参数代表是从哪一层添加检测头。

    如何添加注意力机制?这里提供其中一个改动方法,可能不是很科学,欢迎留言指出问题。

    ## author: pprp
    ## parameters
    nc: 15 # number of classes
    depth_multiple: 1 # model depth multiple
    width_multiple: 1 # layer channel multiple
    
    # anchors
    anchors:
      - [10, 13, 16, 30, 33, 23] # P3/8
      - [30, 61, 62, 45, 59, 119] # P4/16
      - [116, 90, 156, 198, 373, 326] # P5/32
    
    # YOLOv5 backbone
    backbone:
      # [from, number, module, args]
      [
        [-1, 1, Focus, [64, 3]], # 0-P1/2                 #1
        [-1, 1, Conv, [128, 3, 2]], # 1-P2/4              #2
        [-1, 3, C3, [128]], #3
        [-1, 1, Conv, [256, 3, 2]], # 3-P3/8              #4
        [-1, 9, C3, [256]], #5
        [-1, 1, Conv, [512, 3, 2]], # 5-P4/16             #6
        [-1, 9, C3, [512]], #7
        [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32            #8
        [-1, 1, SPP, [1024, [5, 9, 13]]], #9
        [-1, 3, C3, [1024, False]], # 9                   #10
        [-1, 1, SELayer, [1024, 4]], #10
      ]
    
    # YOLOv5 head
    head: [
        [-1, 1, Conv, [512, 1, 1]], #11 /32
        [-1, 1, nn.Upsample, [None, 2, "nearest"]], #12 /16
        [[-1, 6], 1, Concat, [1]], # cat backbone P4 /16       #13
        [-1, 3, C3, [512, False]], # 13 / 16                  #14
    
        [-1, 1, Conv, [256, 1, 1]], #15 /16 
        [-1, 1, nn.Upsample, [None, 2, "nearest"]], #16 /8
        [[-1, 4], 1, Concat, [1]], # cat backbone P3  /8    #17
        [-1, 3, C3, [256, False]], # 17 (P3/8-small) /8    #18
    
        [-1, 1, Conv, [256, 3, 2]], #19 /16
        [[-1, 6], 1, Concat, [1]], # cat head P4         #20
        [-1, 3, C3, [512, False]], # 20 (P4/16-medium)    #21
    
        [-1, 1, Conv, [512, 3, 2]], #22 /32
        [[-1, 8], 1, Concat, [1]], # cat head P5         #23
        [-1, 3, C3, [1024, False]], # 23 (P5/32-large)    #24
    
        [[18, 21, 24], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
      ]
    

    可以看出笔者在backbone最后一层添加了SELayer,这个类我已经在common.py中添加进来,如下所示:

    class SELayer(nn.Module):
        def __init__(self, c1, r=16):
            super(SELayer, self).__init__()
            self.avgpool = nn.AdaptiveAvgPool2d(1)
            self.l1 = nn.Linear(c1, c1//r, bias=False)
            self.relu = nn.ReLU(inplace=True)
            self.l2 = nn.Linear(c1//r, c1, bias=False)
            self.sig = nn.Sigmoid()
            
    
        def forward(self, x):
            b, c, _, _ = x.size()
            y = self.avgpool(x).view(b, c)
            y = self.l1(y)
            y = self.relu(y)
            y = self.l2(y)
            y = self.sig(y)
            y = y.view(b, c, 1, 1)
            return x * y.expand_as(x)
    

    还需要在yolo.py中添加这个改动,这里参考了yolo守望者的代码。

    for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']):
        m = eval(m) if isinstance(m, str) else m  # eval strings
        for j, a in enumerate(args):
            try:
                args[j] = eval(a) if isinstance(a, str) else a  # eval strings
            except:
                pass
        n = max(round(n * gd), 1) if n > 1 else n  # depth gain
        if m in [Conv, GhostConv, Bottleneck, GhostBottleneck, SPP,
                    DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP, 
                    C3]:
            c1, c2 = ch[f], args[0]
            if c2 != no:  # if not output
                c2 = make_divisible(c2 * gw, 8)
    
            args = [c1, c2, *args[1:]]
            if m in [BottleneckCSP, C3]:
                args.insert(2, n)  # number of repeats
                n = 1
        elif m is nn.BatchNorm2d:
            args = [ch[f]]
        elif m is Concat:
            c2 = sum([ch[x] for x in f])
        elif m is Detect:
            args.append([ch[x] for x in f])
            if isinstance(args[1], int):  # number of anchors
                args[1] = [list(range(args[1] * 2))] * len(f)
        elif m is Contract:
            c2 = ch[f] * args[0] ** 2
        elif m is Expand:
            c2 = ch[f] // args[0] ** 2
        elif m is SELayer: # 这里是修改的部分
            channel, re = args[0], args[1]
            channel = make_divisible(channel * gw, 8) if channel != no else channel 
            args = [channel, re]
        else:
            c2 = ch[f]
    

    这个方案目前还在运行中,等此次热身赛结束以后,会公开源码, 希望这个修改可以提升精度。


    2021.3.8 更新 已开源 https://github.com/pprp/datawhale_cv_competition

    展开全文
  • pytorch中加入注意力机制(CBAM),以yolov5为例

    千次阅读 热门讨论 2021-01-06 09:14:18
    代码实现:CBAM.PyTorch 参考文章:pytorch中加入注意力机制(CBAM),以ResNet为例 yolo理论解读:yolov5l.yaml ; yolo.py 主要改 .yaml, yolo.py, commom.py 这三个文件 ① .yaml yolov5提供了s、m、l、x四种,...

    代码实现:CBAM.PyTorch
    参考文章:pytorch中加入注意力机制(CBAM),以ResNet为例
    yolo理论解读:yolov5l.yamlyolo.py

    主要改 .yaml, yolo.py, commom.py 这三个文件

    ① .yaml
    yolov5提供了s、m、l、x四种,CBAM要加在backbone的位置。
    以我的代码为例,我在第一个卷积后加入了CBAM, 把相应的 ”Conv“ 改为了”Conv_CBAM“

    backbone:
      [[-1, 1, Focus, [64, 3]],  # 0-P1/2
       [-1, 1, Conv_CBAM, [128, 3, 2]],  # 1-P2/4 # add CBAM
       [-1, 3, BottleneckCSP, [128]],
       [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
       [-1, 9, BottleneckCSP, [256]],
       [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
       [-1, 9, BottleneckCSP, [512]],
       [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
       [-1, 1, SPP, [1024, [5, 9, 13]]],
       [-1, 3, BottleneckCSP, [1024, False]],  # 9
      ]
    

    ② yolo.py
    模型文件中的parse_model函数,用来读入模型yaml中的参数定义,代码更改如下:
    【原】

    from models.common import Conv, Bottleneck, SPP, DWConv, Focus, BottleneckCSP, Concat, NMS
    

    【改】

    from models.common import Conv, Bottleneck, SPP, DWConv, Focus, BottleneckCSP, Concat, NMS, Conv_CBAM
    

    【原】

    if m in [Conv, Bottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP, C3]:
    

    【改】

    if m in [Conv, Bottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP, C3, Conv_CBAM]:
    

    ③ commom.py
    yolo.py会调用commom.py里的函数,该部分是backbone各个模块参数讲解
    我们加入以下代码:
    【标准卷积层 + CBAM 模块】

    # 标准卷积层 + CBAM
    class Conv_CBAM(nn.Module):
        # Standard convolution
        def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
            super(Conv_CBAM, self).__init__()
            self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
            self.bn = nn.BatchNorm2d(c2)
            self.act = nn.Hardswish() if act else nn.Identity()
            self.ca = ChannelAttention(c2)
            self.sa = SpatialAttention()
    
        def forward(self, x):
            x = self.act(self.bn(self.conv(x)))
            x = self.ca(x) * x
            x = self.sa(x) * x
            return x
    
        def fuseforward(self, x):
            return self.act(self.conv(x))
    

    【CBAM定义模块】

    # add CBAM
    class ChannelAttention(nn.Module):
        def __init__(self, in_planes, ratio=16):
            super(ChannelAttention, self).__init__()
            self.avg_pool = nn.AdaptiveAvgPool2d(1)
            self.max_pool = nn.AdaptiveMaxPool2d(1)
    
            self.fc1   = nn.Conv2d(in_planes, in_planes // 16, 1, bias=False)
            self.relu1 = nn.ReLU()
            self.fc2   = nn.Conv2d(in_planes // 16, in_planes, 1, bias=False)
    
            self.sigmoid = nn.Sigmoid()
    
        def forward(self, x):
            avg_out = self.fc2(self.relu1(self.fc1(self.avg_pool(x))))
            max_out = self.fc2(self.relu1(self.fc1(self.max_pool(x))))
            out = avg_out + max_out
            return self.sigmoid(out)
    
    
    class SpatialAttention(nn.Module):
        def __init__(self, kernel_size=7):
            super(SpatialAttention, self).__init__()
    
            assert kernel_size in (3, 7), 'kernel size must be 3 or 7'
            padding = 3 if kernel_size == 7 else 1
    
            self.conv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
            self.sigmoid = nn.Sigmoid()
    
        def forward(self, x):
            avg_out = torch.mean(x, dim=1, keepdim=True)
            max_out, _ = torch.max(x, dim=1, keepdim=True)
            x = torch.cat([avg_out, max_out], dim=1)
            x = self.conv1(x)
            return self.sigmoid(x)
    
    展开全文
  • 提出了Attention-YOLO算法,该算法借鉴了基于项的注意力机制,将通道注意力及空间注意力机制加入特征提取网络之中,使用经过筛选加权的特征向量来替换原有的特征向量进行残差融合,同时添加二阶项来减少融合过程中的...
  • 在上一篇《【YOLOv4探讨 之七】利用Darknet YOLOv4在网络中添加注意力机制模块 系列之SE模块》https://blog.csdn.net/qq_41736617/article/details/118424585中,我们介绍了SE模块的添加方法,这一篇我们在Darknet中...


    在上一篇《【YOLOv4探讨 之七】利用Darknet YOLOv4在网络中添加注意力机制模块 系列之SE模块》 https://blog.csdn.net/qq_41736617/article/details/118424585中,我们介绍了SE模块的添加方法,这一篇我们在Darknet中增加了SAM模块。

    基本概念

    空间注意力机制使用SAM模块,在Darknet中,新添加的sam_layer层就是用于SAM模块,该层在darknet.h中的定义为sam. 其原理图如下:

    在这里插入图片描述其在网络中的部位仍然是RES残差模块中,首先对残差模块最后一个卷积模块输出分别求沿着通道方向的全局maxpool和全局avgpool,形成两个通道数为1的feature map,对两个feature map做containation,然后对这个2通道的输出做卷积,卷积完毕后使用Sigmoid激活函数确定空间平面上的权重,然后和残差模块最后一个卷积模块输出相乘。
    在这里插入图片描述
    该过程主要功能是提升目标定位效果,在空间上突出需要定位的目标打分权重。

    配置实现

    这里依然使用的是yolov3-tiny.cfg进行改造,添加RES和SAM模块需要在配置文件中增加####标注的内容:

    ......
    ......
    batch_normalize=1
    filters=256
    size=3
    stride=1
    pad=1
    activation=leaky
    
    [maxpool]
    size=2
    stride=2
    
    
    [convolutional]
    batch_normalize=1
    filters=128
    size=1
    stride=1
    pad=1
    activation=leaky
    
    #########新增的配置内容#######
    ####先对RES模块增加做准备,通道数一般往小设计,后续还要通过route层做containation###
    
    [route]
    layers = -2
    
    [convolutional]
    batch_normalize=1
    filters=128
    size=1
    stride=1
    pad=1
    activation=leaky
    
    ####两个RES模块######
    [convolutional]
    batch_normalize=1
    filters=128
    size=1
    stride=1
    pad=1
    activation=leaky
    
    [convolutional]
    batch_normalize=1
    filters=128
    size=3
    stride=1
    pad=1
    activation=leaky
    
    [shortcut]
    from=-3
    activation=linear
    
    [convolutional]
    batch_normalize=1
    filters=128
    size=1
    stride=1
    pad=1
    activation=leaky
    
    [convolutional]
    batch_normalize=1
    filters=128
    size=3
    stride=1
    pad=1
    activation=leaky
    
    ###SAM模块###
    #通道方向全局最大池化
    [maxpool]
    maxpool_depth = 1
    out_channels = 1
    #通道方向全局平均池化
    [route]
    layers = -2
    
    [avgpool]
    channelpool = 1
    #对两个1*H*W的池化层做containation
    [route]
    layers = -1, -3
    #对containation后的池化层进行卷积
    [convolutional]
    batch_normalize=1
    filters=128
    size=7
    stride=1
    pad=1
    activation=logistic#做Sigmoid
    #空间注意力加权
    [sam]
    from = -6
    activation= linear
    
    ###SAM模块结束####
    
    [shortcut]
    from=-9
    activation=linear
    ###RES模块结束####
    
    [convolutional]
    batch_normalize=1
    filters=128
    size=1
    stride=1
    pad=1
    activation=leaky
    
    [route]
    layers = -1,-16
    
    [convolutional]
    batch_normalize=1
    filters=256
    size=1
    stride=1
    pad=1
    activation=leaky
    #####新增的配置内容结束#####
    [convolutional]
    batch_normalize=1
    filters=512
    size=3
    stride=1
    pad=1
    activation=leaky
    
    [maxpool]
    size=2
    stride=1
    
    [convolutional]
    batch_normalize=1
    filters=1024
    size=3
    stride=1
    pad=1
    activation=leaky
    ......
    ......
    

    以上配置文件中,对containation后的池化层进行卷积这个过程这里直接进行128通道的卷积。严格按照原理图,卷积后为 1 × H × W 1\times H \times W 1×H×W,因为需要使用sam_layer和128通道的输入层进行相乘,这里需要进行128次containation。这个过程也可使用如下配置片段进行代替

    ......
    ......
    #对containation后的池化层进行卷积
    [convolutional]
    batch_normalize=1
    filters=1
    size=7
    stride=1
    pad=1
    activation=logistic#做Sigmoid
    #空间注意力加权
    [route]
    layers = -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,\
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
    
    [sam]
    from = -7
    activation= linear
    
    ###SAM模块结束####
    
    [shortcut]
    from=-10
    activation=linear
    ###RES模块结束####
    
    [convolutional]
    batch_normalize=1
    filters=128
    size=1
    stride=1
    pad=1
    activation=leaky
    
    [route]
    layers = -1,-17
    
    [convolutional]
    batch_normalize=1
    filters=256
    size=1
    stride=1
    pad=1
    activation=leaky
    #####新增的配置内容结束#####
    ......
    ......
    

    但是遇到一个最大的问题,训练一段时间后,会导致

    在这里插入图片描述网上都说是显存溢出在进行此操作时,同时监控显存,发现显存占用并没有爆。
    猜测有可能是因为128通道每个通道的内容相同,导致反响传播时候出现梯度爆炸,同时上面结果中也可看出loss = -nan。
    但在之前的测试中也出现过持续的nan之类,并没有因为loss = -nan或loss = nan程序立马崩溃,可见主要问题还是内存出错。因此更大的可能是因为反复堆叠feature map,内存不连贯,指针偶尔出错有关。
    为了实现128层的扩展,使用了128通道的卷积可以实现类似功能,但不会出现梯度爆炸等情况,这里就采用这种方式。

    源码修改与分析

    这里主要用到YOLOv4新增的sam_layer.c。
    由于Darknet中的avgpool_layer.c中没有通道方向的全局平均池化,本人在Darknet的代码中进行了修改,主要涉及parser.c,avgpool_layer.c,avgpool_layer.h和avgpool_layer_kennels.cu.
    废话不多说,放码过来。

    sam_layer

    • parser.c
    //parse_avgpool可以看出在Darknet框架中cfg文件中需要配置的参数为from和activation
    //from就是指定将当前的SAM权重map和哪个层的feature map相乘
    //activation默认为linear,同时不支持SWISH或MISH
    layer parse_sam(list *options, size_params params, network net)
    {
        char *l = option_find(options, "from");
        int index = atoi(l);
        if (index < 0) index = params.index + index;
    
        int batch = params.batch;
        layer from = net.layers[index];
    
        layer s = make_sam_layer(batch, index, params.w, params.h, params.c, from.out_w, from.out_h, from.out_c);
    
        char *activation_s = option_find_str_quiet(options, "activation", "linear");
        ACTIVATION activation = get_activation(activation_s);
        s.activation = activation;
        if (activation == SWISH || activation == MISH) {
            printf(" [sam] layer doesn't support SWISH or MISH activations \n");
        }
        return s;
    }
    
    • sam_layer.c
    void forward_sam_layer(const layer l, network_state state)
    {
        //计算输出feature map的尺寸
        int size = l.batch * l.out_c * l.out_w * l.out_h;
        
        float *from_output = state.net.layers[l.index].output;
    
        int i;
        #pragma omp parallel for
        for (i = 0; i < size; ++i) {
            //将SAM模块输出map和需要处理的feature map点乘
            //注意,输出的size设置为多大,SAM模块输出map有多少层,feature map就会选取的有多少层。另一层含义是和feature map相乘的SAM模块输出map要保持与之相同的size.
            l.output[i] = state.input[i] * from_output[i];
        }
    
        activate_array(l.output, l.outputs*l.batch, l.activation);
    }
    
    void backward_sam_layer(const layer l, network_state state)
    {
        gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta);
        //axpy_cpu(l.outputs*l.batch, 1, l.delta, 1, state.delta, 1);
        //scale_cpu(l.batch, l.out_w, l.out_h, l.out_c, l.delta, l.w, l.h, l.c, state.net.layers[l.index].delta);
    
        int size = l.batch * l.out_c * l.out_w * l.out_h;
        //int channel_size = 1;
        float *from_output = state.net.layers[l.index].output;
        float *from_delta = state.net.layers[l.index].delta;
    
        int i;
        #pragma omp parallel for
        for (i = 0; i < size; ++i) {
            //这个求微分的过程是分别求from_output和input的偏微分,分别反向传播使用
            state.delta[i] += l.delta[i] * from_output[i]; // l.delta * from  (should be divided by channel_size?)
    
            from_delta[i] = state.input[i] * l.delta[i]; // input * l.delta
        }
    }
    

    avgpool_layer

    考虑两种增加功能的思路,一种是增加一个新的函数,一种是增加配置参数进行选择,为尽可能避免给原框架造成过多的调整,考虑增加配置参数+原函数改造的办法。
    这里增加的参数定义为channelpool,如果需要从通道方向做池化,设置这个参数为1。

    parser.c代码修改

    //增加了channelpool,用于选择使用沿着通道方向的平均池化
    avgpool_layer parse_avgpool(list *options, size_params params)
    {
        int batch,w,h,c;
        //2021.07.04 add增加的参数,默认为0
        int channelpool = option_find_int(options, "channelpool",0);
        w = params.w;
        h = params.h;
        c = params.c;
        batch=params.batch;
        if(!(h && w && c)) error("Layer before avgpool layer must output image.");
        //2021.07.04 modify 调整make_avgpool_layer,增加参数channelpool
        avgpool_layer layer = make_avgpool_layer(batch,w,h,c,channelpool);
        return layer;
    }
    

    avgpool_layer.c代码修改

    //2021.07.04 modify
    avgpool_layer make_avgpool_layer(int batch, int w, int h, int c, int channelpool)
    {
    
        avgpool_layer l = { (LAYER_TYPE)0 };
        l.type = AVGPOOL;
        l.batch = batch;
        l.h = h;
        l.w = w;
        l.c = c;
        l.channelpool = channelpool;
        l.inputs = h*w*c;
        //根据配置设置输出尺寸
        if(!channelpool){
            l.out_w = 1;
            l.out_h = 1;
            l.out_c = l.c;
            fprintf(stderr, "avg                          %4d x%4d x%4d ->   %4d \n",  w, h, c, c);
        }else{
            l.out_w = l.w;//通道方向平均池化输出宽度为输入的宽度
            l.out_h = l.h;//通道方向平均池化输出高度为输入的高度
            l.out_c = 1;//通道方向平均池化输出通道数为1
            l.bflops = (l.c * l.out_h*l.out_w) / 1000000000.;
            //设置网络输出图
            fprintf(stderr, "avg                          %4d x%4d x%4d ->   %4d x%4d x%4d\n",  w, h, c, l.out_w, l.out_h, l.out_c, l.bflops);//
        }
        l.outputs = l.out_h * l.out_w * l.out_c;
        int output_size = l.out_h * l.out_w * l.out_c * batch;
        //int output_size = l.outputs * batch;
        l.output = (float*)xcalloc(output_size, sizeof(float));
        l.delta = (float*)xcalloc(output_size, sizeof(float));
        l.forward = forward_avgpool_layer;
        l.backward = backward_avgpool_layer;
        
        #ifdef GPU
        l.forward_gpu = forward_avgpool_layer_gpu;
        l.backward_gpu = backward_avgpool_layer_gpu;
        l.output_gpu  = cuda_make_array(l.output, output_size);
        l.delta_gpu   = cuda_make_array(l.delta, output_size);
        #endif 
    
        return l;
    }
    
    void resize_avgpool_layer(avgpool_layer *l, int w, int h)
    {
        l->w = w;
        l->h = h;
        l->inputs = h*w*l->c;
           
        if(l->channelpool){
        l->out_w = w;//根据载入resize图片的尺寸修改输出尺寸
        l->out_h = h;}
        //l->out_c = l->c;这一句不能添加,会导致通道数错误,也是段错误
        l->outputs = l->out_w * l->out_h * l->out_c;
        int output_size = l->outputs * l->batch;
    
        if (l->train)l->delta = (float*)xrealloc(l->delta, output_size * sizeof(float));
        l->output = (float*)xrealloc(l->output, output_size * sizeof(float));/**/
    
    //增加:申请GPU处理所需的显存空间
    #ifdef GPU
        CHECK_CUDA(cudaFree(l->output_gpu));
        l->output_gpu  = cuda_make_array(l->output, output_size);
    
        if (l->train) {
            CHECK_CUDA(cudaFree(l->delta_gpu));
            l->delta_gpu = cuda_make_array(l->delta, output_size);
        }
    
    #endif
    
    }
    
    //2021.07.04 modify
    void forward_avgpool_layer(const avgpool_layer l, network_state state)
    {
        int b,i,k;
        if(!l.channelpool)//如果不使用通道方向池化,处理方法不变
    	for(b = 0; b < l.batch; ++b){
    	    for(k = 0; k < l.c; ++k){
    		int out_index = k + b*l.c;
    		l.output[out_index] = 0;
    		for(i = 0; i < l.h*l.w; ++i){
    		    int in_index = i + l.h*l.w*(k + b*l.c);
    		    l.output[out_index] += state.input[in_index];
    		}
    		    l.output[out_index] /= l.h*l.w;
    	    }
    	}
        else{//如果使用通道方向池化,增加如下内容
            for(b = 0; b < l.batch; ++b){             
                for(i = 0; i < l.h*l.w; ++i){
                    int out_index = i + b*l.h*l.w;
    	        l.output[out_index] = 0;
                    for(k = 0; k < l.c; ++k){
    		    int in_index = k + l.c*(i + b*l.h*l.w);
    		    l.output[out_index] += state.input[in_index];           
                    }
    		    l.output[out_index] /= l.c;
                }
            }
        }
    
       
    }
    
    //2021.07.04 modify
    void backward_avgpool_layer(const avgpool_layer l, network_state state)
    {
        int b,i,k;
        if(!l.channelpool){//如果不使用通道方向池化,处理方法不变
            for(b = 0; b < l.batch; ++b){
                for(k = 0; k < l.c; ++k){
                    int out_index = k + b*l.c;
                    for(i = 0; i < l.h*l.w; ++i){
                        int in_index = i + l.h*l.w*(k + b*l.c);
                        state.delta[in_index] += l.delta[out_index] / (l.h*l.w);
                    }
                }
            }
        }
        else{//如果使用通道方向池化,增加如下内容
            for(b = 0; b < l.batch; ++b){
                for(i = 0; i < l.h*l.w; ++i){
                    int out_index = i + b*l.h*l.w;
                    for(k = 0; k < l.c; ++k){
    		    int in_index = k + l.c*(i + b*l.h*l.w);
                        state.delta[in_index] += l.delta[out_index] / (l.c);
                    }
                }
            }
        }
    
    }
    
    

    avgpool_layer_kernels.cu 代码修改

    __global__ void forward_avgpool_layer_kernel(int n, int w, int h, int c, float *input, float *output,int channelpool)
    {
        int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x;
        if(id >= n) return;
        if(!channelpool){//如果不使用通道方向池化,处理方法不变
    	    int k = id % c;
    	    id /= c;
    	    int b = id;
    
    	    int i;
    	    int out_index = (k + c*b);
    	    output[out_index] = 0;
    	    for(i = 0; i < w*h; ++i){
    		int in_index = i + h*w*(k + b*c);
    		output[out_index] += input[in_index];
    	    }
    	    output[out_index] /= w*h;
        }
        else{//如果使用通道方向池化,增加如下内容
                int size = w*h;
                int k = id % size;
    	    id /= size;
    	    int b = id;
    
    	    int i;
    	    int out_index = (k + size*b);
    	    output[out_index] = 0;
    	    for(i = 0; i < c; ++i){
    		int in_index = i + c*(k + b*size);
    		output[out_index] += input[in_index];
    	    }
    	    output[out_index] /= c;  
    
        }
    }
    
    __global__ void backward_avgpool_layer_kernel(int n, int w, int h, int c, float *in_delta, float *out_delta,int channelpool)
    {
        int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x;
        if(id >= n) return;
    
        if(!channelpool){//如果不使用通道方向池化,处理方法不变
    	    int k = id % c;
    	    id /= c;
    	    int b = id;
    
    	    int i;
    	    int out_index = (k + c*b);
    	    for(i = 0; i < w*h; ++i){
    		int in_index = i + h*w*(k + b*c);
    		in_delta[in_index] += out_delta[out_index] / (w*h);
    	    }
        }
        else{//如果使用通道方向池化,增加如下内容
                int size = w*h;
                int k = id % size;
    	    id /= size;
    	    int b = id;
    
    	    int i;
    	    int out_index = (k + size*b);
    	    for(i = 0; i < c; ++i){
    		int in_index = i + h*w*(k + b*size);
    		in_delta[in_index] += out_delta[out_index] / c;
    	    }
        }
    }
    
    extern "C" void forward_avgpool_layer_gpu(avgpool_layer layer, network_state state)
    {   
        size_t n = layer.c*layer.batch;
        forward_avgpool_layer_kernel<<<cuda_gridsize(n), BLOCK, 0, get_cuda_stream() >>>(n, layer.w, layer.h, layer.c, state.input, layer.output_gpu, layer.channelpool);//forward_avgpool_layer_kernel函数中增加layer.channelpool参数
        CHECK_CUDA(cudaPeekAtLastError());
    }
    
    extern "C" void backward_avgpool_layer_gpu(avgpool_layer layer, network_state state)
    {
        size_t n = layer.c*layer.batch;
    
        backward_avgpool_layer_kernel<<<cuda_gridsize(n), BLOCK, 0, get_cuda_stream() >>>(n, layer.w, layer.h, layer.c, state.delta, layer.delta_gpu, layer.channelpool);
        //backward_avgpool_layer_kernel函数中增加layer.channelpool参数
        CHECK_CUDA(cudaPeekAtLastError());
    }
    

    avgpool_layer.h代码修改

    只要修改如下内容即可

    avgpool_layer make_avgpool_layer(int batch, int w, int h, int c, int channelpool);
    

    maxpool_layer

    maxpool_layer内容比较多,这里重点分析和沿通道方向池化有关的代码

    • parser.c
    //Darknet中maxpooling处理方式比较多,参数也比较丰富
    //stride和size默认相等,表示池化的尺寸和池化窗口的平移量
    //stride_x,stride_y可以将池化平移量在X和Y方向设置成不同的数值
    //padding元素填充,一般用于feature map尺寸为奇数的情况
    //maxpool_depth表示沿通道方向最大池化
    //out_channels只有在maxpool_depth=1时候有用,表示输出层数量
    //antialiasing去混叠
    maxpool_layer parse_maxpool(list *options, size_params params)
    {
        int stride = option_find_int(options, "stride",1);
        int stride_x = option_find_int_quiet(options, "stride_x", stride);
        int stride_y = option_find_int_quiet(options, "stride_y", stride);
        int size = option_find_int(options, "size",stride);
        int padding = option_find_int_quiet(options, "padding", size-1);
        int maxpool_depth = option_find_int_quiet(options, "maxpool_depth", 0);
        int out_channels = option_find_int_quiet(options, "out_channels", 1);
        int antialiasing = option_find_int_quiet(options, "antialiasing", 0);
        const int avgpool = 0;
    
        int batch,h,w,c;
        h = params.h;
        w = params.w;
        c = params.c;
        batch=params.batch;
        if(!(h && w && c)) error("Layer before [maxpool] layer must output image.");
    
        maxpool_layer layer = make_maxpool_layer(batch, h, w, c, size, stride_x, stride_y, padding, maxpool_depth, out_channels, antialiasing, avgpool, params.train);
        return layer;
    }
    
    • maxpool_layer.c
    maxpool_layer make_maxpool_layer(int batch, int h, int w, int c, int size, int stride_x, int stride_y, int padding, int maxpool_depth, int out_channels, int antialiasing, int avgpool, int train)
    {
    ......
    ......
    
        l.batch = batch;
        l.h = h;
        l.w = w;
        l.c = c;
        l.pad = padding;
        l.maxpool_depth = maxpool_depth;
        l.out_channels = out_channels;
        //当使用通道方向最大池化时,设置输出的map尺寸
        if (maxpool_depth) {
            l.out_c = out_channels;
            l.out_w = l.w;
            l.out_h = l.h;
        }
        else {//当不使用通道方向最大池化时,设置输出的map尺寸,根据池化窗口和平移量进行缩减
            l.out_w = (w + padding - size) / stride_x + 1;
            l.out_h = (h + padding - size) / stride_y + 1;
            l.out_c = c;
        }
        l.outputs = l.out_h * l.out_w * l.out_c;
        l.inputs = h*w*c;
        l.size = size;
        l.stride = stride_x;
        l.stride_x = stride_x;
        l.stride_y = stride_y;
        ......
        ......
    }
    
    //maxpool前向传播
    void forward_maxpool_layer(const maxpool_layer l, network_state state)
    {
        if (l.maxpool_depth)
        {
            int b, i, j, k, g;
            for (b = 0; b < l.batch; ++b) {
                #pragma omp parallel for
                for (i = 0; i < l.h; ++i) {
                    for (j = 0; j < l.w; ++j) {
                        for (g = 0; g < l.out_c; ++g)
                        {
                            int out_index = j + l.w*(i + l.h*(g + l.out_c*b));
                            float max = -FLT_MAX;
                            int max_i = -1;
                            //当out_channels大于1,沿通道方向分段取最大值,最后保留out_channels个最大值
                            for (k = g; k < l.c; k += l.out_c)
                            {
                                int in_index = j + l.w*(i + l.h*(k + l.c*b));
                                float val = state.input[in_index];
    
                                max_i = (val > max) ? in_index : max_i;
                                max = (val > max) ? val : max;
                            }
                            l.output[out_index] = max;
                            if (l.indexes) l.indexes[out_index] = max_i;
                        }
                    }
                }
            }
            return;
        }
       ......
       ......
    }
    //maxpool反向传播
    //微分直接穿过,只要通道数正确即可
    void backward_maxpool_layer(const maxpool_layer l, network_state state)
    {
        int i;
        int h = l.out_h;
        int w = l.out_w;
        int c = l.out_c;
        #pragma omp parallel for
        for(i = 0; i < h*w*c*l.batch; ++i){
            int index = l.indexes[i];
            state.delta[index] += l.delta[i];
        }
    }
    
    
    • maxpool_layer_kernels.cu
    //比较简单,直接使用CUDA库中的函数,感兴趣的读者可以去英伟达官网看CUDA文档
    extern "C" void forward_maxpool_layer_gpu(maxpool_layer layer, network_state state)
    {
        if (layer.maxpool_depth) {
            int h = layer.out_h;
            int w = layer.out_w;
            int c = 1;// layer.out_c;
    
            size_t n = h*w*c*layer.batch;
    //如果Makefile中定义CUDA=1,直接使用CUDA库中的函数操作
            forward_maxpool_depth_layer_kernel << <cuda_gridsize(n), BLOCK, 0, get_cuda_stream() >> >(
                n, layer.w, layer.h, layer.c, layer.out_c, layer.batch, state.input, layer.output_gpu, layer.indexes_gpu);
            CHECK_CUDA(cudaPeekAtLastError());
    
            return;
        }
        ......
        ......
     }
    
    //CUDA库是一个专门的学习内容,实现功能使用下面代码段,这里不再详解
    extern "C" void backward_maxpool_layer_gpu(maxpool_layer layer, network_state state)
    {
    ......
    ......
        if (layer.maxpool_depth) {
            int h = layer.out_h;
            int w = layer.out_w;
            int c = layer.out_c;
    
            size_t n = h * w * c * layer.batch;
    
            backward_maxpool_depth_layer_kernel << <cuda_gridsize(n), BLOCK, 0, get_cuda_stream() >> >(n, layer.w, layer.h, layer.c, layer.batch, layer.delta_gpu, state.delta, layer.indexes_gpu);
            CHECK_CUDA(cudaPeekAtLastError());
            return;
        }
    
        size_t n = layer.h*layer.w*layer.c*layer.batch;
    
        backward_maxpool_layer_kernel<<<cuda_gridsize(n), BLOCK, 0, get_cuda_stream() >>>(n, layer.h, layer.w, layer.c, layer.stride_x, layer.stride_y, layer.size, layer.pad, layer.delta_gpu, state.delta, layer.indexes_gpu);
        CHECK_CUDA(cudaPeekAtLastError());
    }
    

    训练效果

    这里直接上图,具体调参这里省略,不具体比较不同网络结构之间的效果。主要证明改造成功。
    在这里插入图片描述训练2400次效果
    请添加图片描述
    训练3000次效果
    请添加图片描述

    小结

    在Darknet框架中使用SAM模块最大的挑战在于修改了avgpool_layer的实现代码,增加了沿通道方向的平均池化功能。Darknet框架并不是一个成熟的框架,很多细小的功能都需要修改代码进行添加,希望AlexyAB再辛苦一点,把该完善的功能都增加上,则各种新的tricks就可以直接修改cfg 文件实现了。
    这里抛砖引玉,小伙伴还有什么问题,可以给我留言相互交流哦。

    展开全文
  • YOLOV4 -- SE注意力机制

    2021-10-13 10:23:28
    YOLOV4 – SE注意力机制YOLOV4与YOLOV3的区别主要介绍了V3和V4之间的区别,但是仔细研究代码Tensorflow2.0—YOLO V4-tiny网络原理及代码解析(一)- 特征提取网络会发现其实在V4中还多出一个trick,那就是注意力...
  • 我想在yolo5加入注意力机制,怎么实现呢,懂得话联系我</p>
  • 两篇论文 《CBAM: Convolutional Block Attention Module》2018 ECCV 《BAM: Bottleneck Attention Module》2018 BWVC channel attention 通道注意力 spatial attention 空间注意力
  • yolov3注意力机制

    2021-10-06 15:56:14
    【从零开始学习YOLOv3】7. 教你在目标检测中添加Attention机制 pprp
  • YOLOv5结合BiFPN

    千次阅读 多人点赞 2021-04-28 22:44:04
    现在yolov5的neck用的是PANet,在efficient论文中提出了BiFPN结构,还有更加不错的性能。所以就尝试将yolov5中的PANet层改为BiFPN。 需要修改的地方 主要是修改yaml配置文件 我修改的是yolov5x.yaml,将concat层...
  • 利用Darknet在YOLOv4中添加注意力机制模块基本概念SE模块SAM模块CBAM模块源码分析配置实现 在论文《YOLOv4: Optimal Speed and Accuracy of Object Detectio》中,有一个重要的trick,就是注意力机制模块。而且在...
  • 前言 此篇文章转载于知乎,系列文章地址:...yolov4:YOLOv4: Optimal Speed and Accuracy of Object Detectio ...本文分析各种BN改进、网络感受野增强技巧、注意力机制和特征融合技巧。 1 常用归一化手段 1.1 BN、GN、IN
  • 前言:【从零开始学习YOLOv3】系列越写越多,本来安排的内容比较少,但是在阅读代码的过程中慢慢发掘了一些新的亮点,所以不断加入到这个系列中。之前都在读YOLOv3中的代码,已经学习了cfg文件、模型构建等内容。...
  • 前两篇文章《【YOLOv4探讨 之七】利用Darknet YOLOv4在网络中添加注意力机制模块 系列之SE模块》( https://blog.csdn.net/qq_41736617/article/details/118424585)和《【YOLOv4探讨 之八】(2)SAM模块 – 利用...
  • 注意力机制pytorch实现

    2021-03-21 21:35:48
    注意力机制pytorch实现 通道注意力机制 class ChannelAttention(nn.Module): def __init__(self, in_planes, ratio=16): super(ChannelAttention, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) ...
  • 点击上方“3D视觉工坊”,选择“星标”干货第一时间送达在2020年中,目标检测领域出现了许多优秀的工作,今天我们来聊一聊在当前热门的五大目标检测开源方案。1、五大改进,二十多项技巧实验,堪...
  • YOLOV4:您只看一次目标检测模型在pytorch当中的实现 2021年2月7日更新:加入letterbox_image的选项,关闭letterbox_image后网络的地图得到大幅度提升。 目录 性能情况 训练数据集 权值文件名称 测试数据集 输入图片...
  • (在embedding空间中,体现的是语义相似性,自己点积自己肯定相似性最高,设置qvk的目的应该是开一个新的空间,专门用于学习注意力机制,在这个空间里点积高表示注意力高,但不代语义相似度高,这样就强化了其它上...
  • yolov4初探

    2020-11-28 21:52:03
    因为不同位置GIOU和IOU相同,引入DIoU,就是两个框中心点的距离,C框对角线之间的距离加入到公式中,问题解决。最终用的CIoU,就是这个损失函数中加入了长宽比(真实和预测框的长宽比是否一致) 4、soft-nms 不符合...
  • 点击上方“3D视觉工坊”,选择“星标”干货第一时间送达本文在YOLOv5的基础上加入了一些新的技术,比如ViT、CBAM和一些Tricks(数据增广、多尺度测试等),最终命名为TPH-YO...
  • YOLOV5-5.x 源码讲解】整体项目文件导航

    千次阅读 多人点赞 2021-07-23 20:36:12
    这个项目是github的开源项目,YOLOV5:https://github.com/ultralytics/yolov5,目前已经有14.1k个Star 和 4.9k 个Folk了,非常的火。下面我会给大家逐个的文件介绍这个项目中的所有代码,希望能帮到大家。 我下的...
  • 本文在YOLOv5的基础上加入了一些新的技术,比如ViT、CBAM和一些Tricks(数据增广、多尺度测试等),最终命名为TPH-YOLOv5的目标检测器,特别擅长在无人机的目标捕捉。 工作单位: 北京航空航天大学 TPH-yolov5整体...

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 363
精华内容 145
关键字:

yolov5加入注意力机制