yolov5 a ajouté le mécanisme d'attention CBAM, SE, CA, ECA, code pur (22.3.1 également mis à jour)

 Le réseau yolov5 impliqué dans cet article est la version 5.0 et la version 6.0 sera mise à jour si nécessaire.

Attention 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.f1 = nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False)
#         self.relu = nn.ReLU()
#         self.f2 = nn.Conv2d(in_planes // ratio, in_planes, 1, bias=False)
#         # 写法二,亦可使用顺序容器
#         # self.sharedMLP = nn.Sequential(
#         # nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False), nn.ReLU(),
#         # nn.Conv2d(in_planes // rotio, in_planes, 1, bias=False))
#
#         self.sigmoid = nn.Sigmoid()
#
#     def forward(self, x):
#         avg_out = self.f2(self.relu(self.f1(self.avg_pool(x))))
#         max_out = self.f2(self.relu(self.f1(self.max_pool(x))))
#         out = self.sigmoid(avg_out + max_out)
#         return 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.conv = 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.conv(x)
#         return self.sigmoid(x)
#
#
# class CBAMC3(nn.Module):
#     # CSP Bottleneck with 3 convolutions
#     def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
#         super(CBAMC3, self).__init__()
#         c_ = int(c2 * e)  # hidden channels
#         self.cv1 = Conv(c1, c_, 1, 1)
#         self.cv2 = Conv(c1, c_, 1, 1)
#         self.cv3 = Conv(2 * c_, c2, 1)
#         self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
#         self.channel_attention = ChannelAttention(c2, 16)
#         self.spatial_attention = SpatialAttention(7)
#
#         # self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])
#
#     def forward(self, x):
#         out = self.channel_attention(x) * x
#         print('outchannels:{}'.format(out.shape))
#         out = self.spatial_attention(out) * out
#         return out

Mise à jour du code CBAM 2022.1.26

Instruit par le patron, il est souligné que le module cbam ci-dessus ne correspond pas au code du projet yolov5, et le code auquel yolov5 ajoute l'attention de cbam est soumis au code suivant : (Si ce code est utilisé, le CBAMC3 correspondant dans le Les fichiers yolo.py et yaml doivent également être remplacés par CBAM , il en va de même pour SE ci-dessous)

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.f1 = nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False)
        self.relu = nn.ReLU()
        self.f2 = nn.Conv2d(in_planes // ratio, in_planes, 1, bias=False)
        # 写法二,亦可使用顺序容器
        # self.sharedMLP = nn.Sequential(
        # nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False), nn.ReLU(),
        # nn.Conv2d(in_planes // rotio, in_planes, 1, bias=False))

        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        avg_out = self.f2(self.relu(self.f1(self.avg_pool(x))))
        max_out = self.f2(self.relu(self.f1(self.max_pool(x))))
        out = self.sigmoid(avg_out + max_out)
        return 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.conv = 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.conv(x)
        return self.sigmoid(x)


class CBAM(nn.Module):
    # CSP Bottleneck with 3 convolutions
    def __init__(self, c1, c2, ratio=16, kernel_size=7):  # ch_in, ch_out, number, shortcut, groups, expansion
        super(CBAM, self).__init__()
        # c_ = int(c2 * e)  # hidden channels
        # self.cv1 = Conv(c1, c_, 1, 1)
        # self.cv2 = Conv(c1, c_, 1, 1)
        # self.cv3 = Conv(2 * c_, c2, 1)
        # self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
        self.channel_attention = ChannelAttention(c1, ratio)
        self.spatial_attention = SpatialAttention(kernel_size)

        # self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])

    def forward(self, x):
        out = self.channel_attention(x) * x
        # print('outchannels:{}'.format(out.shape))
        out = self.spatial_attention(out) * out
        return out

 1. Voici le code pour l'attention de convolution. J'aime généralement l'ajouter après le module C3 de common.py, pas besoin de faire des changements, juste ctrl+c+v pour un imbécile.

2. Apportez des modifications dans yolo.py. Remplacez le code correspondant par le code suivant dans la fonction parse_model ou trompez ctrl+c+v.

 if m in [Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP,
                 C3, C3TR,CBAMC3]:
            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,CBAMC3]:
                args.insert(2, n)  # number of repeats
                n = 1

3. Apportez des modifications au fichier yaml. Par exemple, si vous souhaitez utiliser le réseau s, je l'ai modifié comme ceci : remplacez tous les modules C3 du réseau dorsal par le module CBAMC3 (il convient de noter ici que ce changement ne peut pas charger les poids de pré-entraînement, c'est-à-dire , votre modèle est à partir de zéro a commencé la formation). Si vous ne voulez pas trop changer, lisez la suite.

Ajouter un mécanisme d'attention (CBAM) à pytorch, en prenant yolov5 comme exemple_YY_172's blog-CSDN blog_yolov5 plus attention

C'est le premier blogueur qui a attiré l'attention de CBAM sur le réseau yolov5. J'ai également lu sa méthode et je l'ai supprimée.

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

 attention SE

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)

Mise à jour des codes 2022.1.26SE 

Corrigé par le même patron, le code se dans la partie ci-dessus ne correspond pas non plus au code du projet yolov5, et le code se modifié sera publié. Le code de se attention est soumis à ce qui suit :

class SE(nn.Module):
    def __init__(self, c1, c2, r=16):
        super(SE, 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):
        print(x.size())
        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)

1. Voici l'extrait de code pour l'attention SE, identique à l'ajout de l'attention précédente, j'aime l'ajouter après C3.

2. Apportez des modifications dans yolo.py.

def parse_model(d, ch):  # model_dict, input_channels(3)
    logger.info('\n%3s%18s%3s%10s  %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments'))
    anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple']
    na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors  # number of anchors
    no = na * (nc + 5)  # number of outputs = anchors * (classes + 5)

    layers, save, c2 = [], [], ch[-1]  # layers, savelist, ch out
    for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']):  # from, number, module, args
        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, C3TR, CoordAtt, SELayer, eca_layer, CBAM]:
            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, C3TR]:
                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
        else:
            c2 = ch[f]

3. Apportez des modifications au fichier yaml que vous souhaitez utiliser.

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

 Après avoir fonctionné avec succès, cela ressemble à ceci

 Vous devriez être en mesure de voir où cette attention est ajoutée, et c'est là qu'elle est utilisée.

C'est une autre méthode que j'utilise pour ajouter de l'attention, cette méthode charge des poids pré-formés, et je recommande cette méthode pour tout le monde. Puisque je recommande à tout le monde d'utiliser cette méthode, quel est le but de la méthode que je recommande d'ajouter à l'attention de CBAM ? Hahahaha baisse les yeux.

Concours Tianchi - Processus d'amélioration de la base de détection des défauts de tissu - Ajouter un mécanisme d'attention au blog du modèle yolov5_pprp - Blog CSDN_yolov5 Mécanisme d'attention

C'est le blog que j'ai vu qui a ajouté l'attention de SE au modèle yolov5. J'ai également cité la méthode du blogueur. Merci pour le partage, l'intrusion et la suppression.

 À l'attention de la CEA

# class eca_layer(nn.Module):
#     """Constructs a ECA module.
#     Args:
#         channel: Number of channels of the input feature map
#         k_size: Adaptive selection of kernel size
#     """
#     def __init__(self, channel, k_size=3):
#         super(eca_layer, self).__init__()
#         self.avg_pool = nn.AdaptiveAvgPool2d(1)
#         self.conv = nn.Conv1d(1, 1, kernel_size=k_size, padding=(k_size - 1) // 2, bias=False)
#         self.sigmoid = nn.Sigmoid()
#
#     def forward(self, x):
#         # feature descriptor on the global spatial information
#         y = self.avg_pool(x)
#
#         # Two different branches of ECA module
#         y = self.conv(y.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
#
#         # Multi-scale information fusion
#         y = self.sigmoid(y)
#         x=x*y.expand_as(x)
#
#         return x * y.expand_as(x)

1. Voici l'extrait de code d'attention. Insérez-le simplement dans votre propre script et annulez le commentaire. La position ajoutée est la même que ci-dessus, je n'en parlerai donc pas ici. 

2. Modifiez yolo.py. Voir l'extrait de code suivant.

      if m in [Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP,
                 C3, C3TR]:
            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,eca_layer]:
                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 eca_layer:
            channel=args[0]
            channel=make_divisible(channel*gw,8)if channel != no else channel
            args=[channel]
        else:
            c2 = ch[f]

 3. Modifiez le fichier yaml que vous souhaitez utiliser. Ici, je veux expliquer pourquoi j'ai expliqué deux méthodes d'ajout d'attention (la première : remplacer tout C3 dans le backbone ; la seconde : ajouter l'attention à la dernière couche du backbone et créer une couche de sortie). Le modèle de la deuxième méthode est toujours en cours d'exécution et il n'y a pas encore de résultat, mais les résultats du modèle peuvent également être devinés. Il y a une légère amélioration stable et l'effet de détection ne s'améliorera pas trop. J'utilise la première méthode pour prêter attention à ECA Lorsque toute la force remplace C3 dans la colonne vertébrale, le p, r et la carte du modèle chutent tous, d'environ un ou deux points, mais étonnamment, son effet de détection est très bon, et il peut y détecter existe de nombreuses cibles indétectables dans le modèle avant le changement. Bien sûr, il y aura plus de fausses détections et de détections manquées que dans le modèle d'origine. Après avoir modifié manuellement le seuil, c'est beaucoup mieux. Étant donné que l'ensemble de données implique des secrets d'entreprise, il ne sera pas être publié ici. Maintenant, ce que je fais est la détection des casques. Les étudiants intéressés peuvent essayer cette méthode d'ajout d'attention.

Découvrez l'un des résultats du test.

Si vous souhaitez simplement améliorer la précision du modèle, la deuxième méthode est recommandée.

 La prochaine étape est l'attention publiée sur le CVPR de cette année.

CoorAttention

# class h_sigmoid(nn.Module):
#     def __init__(self, inplace=True):
#         super(h_sigmoid, self).__init__()
#         self.relu = nn.ReLU6(inplace=inplace)
#
#     def forward(self, x):
#         return self.relu(x + 3) / 6
#
#
# class h_swish(nn.Module):
#     def __init__(self, inplace=True):
#         super(h_swish, self).__init__()
#         self.sigmoid = h_sigmoid(inplace=inplace)
#
#     def forward(self, x):
#         return x * self.sigmoid(x)


# class CoordAtt(nn.Module):
#     def __init__(self, inp, oup, reduction=32):
#         super(CoordAtt, self).__init__()
#         self.pool_h = nn.AdaptiveAvgPool2d((None, 1))
#         self.pool_w = nn.AdaptiveAvgPool2d((1, None))
#
#         mip = max(8, inp // reduction)
#
#         self.conv1 = nn.Conv2d(inp, mip, kernel_size=1, stride=1, padding=0)
#         self.bn1 = nn.BatchNorm2d(mip)
#         self.act = h_swish()
#
#         self.conv_h = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)
#         self.conv_w = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)
#
#     def forward(self, x):
#         identity = x
#
#         n, c, h, w = x.size()
#         x_h = self.pool_h(x)
#         x_w = self.pool_w(x).permute(0, 1, 3, 2)
#
#         y = torch.cat([x_h, x_w], dim=2)
#         y = self.conv1(y)
#         y = self.bn1(y)
#         y = self.act(y)
#
#         x_h, x_w = torch.split(y, [h, w], dim=2)
#         x_w = x_w.permute(0, 1, 3, 2)
#
#         a_h = self.conv_h(x_h).sigmoid()
#         a_w = self.conv_w(x_w).sigmoid()
#
#         out = identity * a_w * a_h
#
#         return out

 Ceci est l'extrait de code, ajouté après le module C3 de common.py

 Voici la partie de la modification de yolo.py. Enfin, les modifications apportées au fichier yaml ne seront pas mentionnées ici. Deux méthodes sont fournies à tout le monde. Vous pouvez choisir vous-même.

def parse_model(d, ch):  # model_dict, input_channels(3)
    logger.info('\n%3s%18s%3s%10s  %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments'))
    anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple']
    na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors  # number of anchors
    no = na * (nc + 5)  # number of outputs = anchors * (classes + 5)

    layers, save, c2 = [], [], ch[-1]  # layers, savelist, ch out
    for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']):  # from, number, module, args
        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, C3TR,CBAMC3,CoordAtt]:#
            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, C3TR]:
                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 eca_layer:
        #     channel=args[0]
        #     channel=make_divisible(channel*gw,8)if channel != no else channel
        #     args=[channel]   
        elif m is CoordAtt:
            inp,oup,re = args[0],args[1],args[2]
            oup = make_divisible(oup * gw, 8) if oup != no else oup
            args = [inp,oup,re]
        else:
            c2 = ch[f]

La méthode suivante pour ajouter l'attention ECA et CA consiste à prendre des photos des deux premiers blogueurs et à les exécuter plusieurs fois dans ma région. Ce ne sont que deux mots, faciles à utiliser, et une attention future peut également être ajoutée de cette manière.

Veuillez vous déplacer ici pour la méthode d'ajout d'attention de la version yolov5-6.0

Je connais toutes sortes de façons d'ajouter de l'attention et de travailler. En cas de besoin, des amis peuvent me contacter et gagner des frais de subsistance.


Mise à jour 2022.2.14 : J'ai utilisé densenet pour remplacer la mise au point, et la structure fpn dans le cou a été changée en code bi-fpn. Si vous en avez besoin, veuillez discuter en privé et gagner des frais de subsistance. L'astuce peut être utilisée pour l'obtention du diplôme et la publication de la thèse de maîtrise. 


Mes amis et moi avons créé un petit studio. Il y a dans l'équipe de nombreux étudiants dans le domaine de l'intelligence artificielle issus d'universités doubles de premier ordre, les cinq meilleurs leaders de la compétition nationale, des ingénieurs avec de nombreuses années d'expérience et des étudiants diplômés avec Les exigences de la thèse de fin d'études ou de la conception des cours seront prises en compte en conséquence ; pour les projets d'ingénierie, nous nous engageons à répondre à vos besoins dans les plus brefs délais. Bien sûr, si vous êtes un étudiant ou un étudiant diplômé et que vous estimez que vos capacités sont bonnes, nous vous fournirons également un revenu à temps partiel. Bienvenue à nous rejoindre. Le code QR de la discussion de groupe est le suivant, qui sera être mis à jour une fois par semaine. Merci pour votre soutien.

 Merci beaucoup, et enfin je souhaite à tous un salaire annuel d'un million.

Terminé.

Je suppose que tu aimes

Origine blog.csdn.net/zqt321/article/details/121772854
conseillé
Classement