YOLOv6 Pro | YOLOv6 Network Magic Change (1) - RepGFPN Fusion Efficient Aggregation Network (ELAN) と再パラメータ化されたターゲット検出ネック (DAMO-YOLO より)

Ali Dharma Academy ICLR2022 が発行した論文「GiraffeDet: A Heavy-Neck Paradigm for Object Detection」では、非常に軽量なバックボーンと大きな計算ネックを備え、ネットワークが空間情報間の情報相互作用により重点を置く GiraffeDet を提案しました。高解像度の特徴マップと低解像度の特徴マップの意味情報。同時に、2022 年 11 月末のオープンソース DAMO YOLO では、GFPN のアイデアが再び使用され、クイーン融合 GFPN に基づいて、効率的なアグリゲーション ネットワーク (ELAN) のアイデアと、この記事では、熱を利用して新しいネック ネットワーク RepGFPN を形成するための再パラメータ化を行い、YOLOv6 Pro フレームワークの YOLOV6 ネック構造の RepGFPN を改善します。同様の改善は YOLOv5 でも達成できます。

いつものように、YOLOv6 Pro フレームワークを紹介します。

 · YOLOv6 Pro は、公式の全体的なアーキテクチャに基づいており YOLOv6 、 YOLOv5 ネットワーク構築手法 を使用してYOLOv6 、  backboneneckeffidehead 構造を含むネットワークを構築します。 ·ファイル内のモジュールを任意に変更または追加でき、変更された各ファイルは独立して実行可能です。目的は科学研究を促進することです
。 ·  今後  、モジュールに基づいてネットワーク構造の改善がさらに追加される予定です。· 事前にトレーニングされた重みは、一致することを保証するために公式の重みから変換されています。yaml
yolov5yoloair

・先行発売されたp6モデル(非公式)

· RepGFPN、FocalTransformer、RepGhost、CoAtNet など、その他のいくつかの改良されたモジュールが追加されました。

私たちが使用した yoloair フレームワークは、  IEEE UV 2022「Vision Meets Alage」物体検出コンペティションYOLOv6 pro で第 1 位を獲得しました。

プロジェクトリンク: GitHub - yang-0201/YOLOv6_pro: yolov6 がネットワーク構造を変更しやすくする

興味のある友人は、スターとフォークをクリックして、質問がある場合はすぐにフィードバックしてください。プロジェクトの開始時に、いくつかの機能的な提案が採用され、開発されます。志を同じくする友人も、共同で維持および開発するために PR を送信することを歓迎します。プロジェクトのフォローアップは継続的に更新および改善される予定ですので、ご期待ください。

タイトルに入ります!

DAMO YOLO: 論文アドレス https://arxiv.org/pdf/2211.15444.pdf

これは、神経構造探索 NAS 技術に基づいて取得されたMAE-NAS バックボーン ネットワーク、 RepGFPN、ZeroHead 構造を含む DAMO YOLO 全体のネットワーク構造図です。今日は主に RepGFPN 構造に焦点を当てます。 module は Fusion Block 構造です。

著者らは、GFPN が有効である主な理由の 1 つは、高レベルの意味情報と低レベルの空間情報を完全に交換できるためであると考えています。GFPN では、マルチスケール フィーチャは、前のレイヤーと現在のレイヤーの階層フィーチャの両方に融合されます。さらに重要なことは、log2(n) スキップ レイヤ接続により、より効率的な情報転送が実現され、より深いネットワークまで拡張できることです。

 同様に、現代のyoloシリーズモデルでもオリジナルのネック構造をGFPNに直接置き換えたところ、より高い精度が達成されました。(後で GFPN を置き換えることを試みることもできます) しかし、彼らが発見した問題は、GFPN ベースのモデルの遅延が改良されたパネットベースのモデルの遅延よりもはるかに大きいため、精度の向上はろうそくの価値がない可能性があるということです。次の理由を要約してください。

1. 異なるスケールの特徴マップは同じチャネル次元を持ちます。

2. Queen-fusion はリアルタイム検出モデルの要件を満たすことができません。

3. 畳み込みベースのクロススケール特徴融合は非効率的です。

我々は GFPN に基づいて、リアルタイムターゲット検出の設計を満たすための斬新で効率的な設計である RepGFPN を提案します。

1. 異なるスケールの特徴マップのフロップの差が大きいため、限られた計算コストの制約の下で、各スケールの特徴マップが共有する同じ数のチャネルを制御することは困難です。

したがって、著者のネック特徴融合では、異なるチャネル寸法の異なるスケール特徴マップの設定が使用されます。著者は、次の表に示すように、同じチャネルと異なるチャネルのパフォーマンスとネックの深さと幅のトレードオフを比較しています。

異なるスケールでチャネル数を柔軟に制御することで、すべてのスケールで同じ数のチャネルを共有するよりも高い精度を達成できることがわかります。深さが 3、幅が (96、192、384) の場合に最高のパフォーマンスが得られます。

2) GFPN は、 queen-fusionを通じて機能の相互作用を強化しますが、多くの追加のアップサンプリングおよびダウンサンプリング操作ももたらします。

著者はこれらのアップサンプリング操作とダウンサンプリング操作のパフォーマンスを比較し、結果を表に示します。

追加のアップサンプリング操作によりレイテンシが 0.6 ミリ秒増加する一方、精度の向上はわずか 0.3mAP であり、追加のダウンサンプリング操作によってもたらされるパフォーマンスの向上よりも大幅に低いことがわかります。したがって、リアルタイム検出の制約の下で、queen-fusionの余分なアップサンプリング操作を削除します。

3. 特徴融合ブロックでは、最初に元の 3x3 畳み込みベースの特徴融合を CSPNet に置き換え、4.2 mAP ゲインを取得します。その後、再パラメータ化メカニズムと ELAN (Efficient Layer Aggregation Network) の接続を組み合わせて CSPNet をアップグレードします。膨大な計算負荷を追加することなく、より高い精度を実現します。比較結果を表に示します。

論文で紹介した後、誰もがすでに RepGFPN のアイデアを理解していると思います。コードを見てみましょう。

DAMO YOLO のソース コードを参照し、例として YOLOv6l と YOLOv6t に追加された RepGFPN-T、RepGFPN-M、RepGFPN-S を含む RepGFPN 構造を YOLOv6 Pro のフレームワークに追加しました。

まず、yolov6l+RepGFPN-M 構造の yaml ファイルを確認します。

depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
backbone:
  # [from, number, module, args]
  [[-1, 1, ConvWrapper, [64, 3, 2]],  # 0-P1/2
   [-1, 1, ConvWrapper, [128, 3, 2]],  # 1-P2/4
   [-1, 1, BepC3, [128, 6, "ConvWrapper"]],
   [-1, 1, ConvWrapper, [256, 3, 2]],  # 3-P3/8
   [-1, 1, BepC3, [256, 12, "ConvWrapper"]],
   [-1, 1, ConvWrapper, [512, 3, 2]],  # 5-P4/16
   [-1, 1, BepC3, [512, 18, "ConvWrapper"]],
   [-1, 1, ConvWrapper, [1024, 3, 2]],  # 7-P5/32
   [-1, 1, BepC3, [1024, 6, "ConvWrapper"]],
   [-1, 1, SPPF, [1024, 5]]]  # 9
neck:
   [
    [ 6, 1,ConvBNAct,[ 256, 3, 2, silu ] ],
    [ [ -1, 9 ], 1, Concat, [ 1 ] ], # 768
    [ -1, 1, RepGFPN, [ 512, 1.5, 1.0, silu ] ],  #  8

    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ 4, 1,ConvBNAct,[ 128, 3, 2, silu ] ],
    [ [ -1, 6, 13 ], 1, Concat, [ 1 ] ], # 896
    [ -1, 1, RepGFPN, [ 256, 1.5, 1.0, silu ] ], # merge_4 12

    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ [ -1, 4 ], 1, Concat, [ 1 ] ], # 384
    [ -1, 1, RepGFPN, [ 128, 1.5, 1.0, silu ] ], # 512+256  merge_5  15  out

    [ -1, 1,ConvBNAct,[ 128, 3, 2, silu ] ],
    [ [ -1, 16 ], 1, Concat, [ 1 ] ], # 384
    [ -1, 1, RepGFPN, [ 256, 1.5, 1.0, silu ] ], # 512+256  merge_7  18  out

    [ 16, 1,ConvBNAct,[ 256, 3, 2, silu ] ],
    [ -2, 1,ConvBNAct,[ 256, 3, 2, silu ] ],
    [ [ -1, 12, -2 ], 1, Concat, [ 1 ] ], # 1024
    [ -1, 1, RepGFPN, [ 512, 1.5, 1.0, silu ] ], # 512+512+1024 merge_6 22  out
   ]

effidehead:
  [[19, 1,Head_out , [128, 16]],
  [22, 1, Head_out, [256, 16]],
  [26, 1, Head_out, [512, 16]],
  [[27, 28, 29], 1, Out, []]]


 元の画像を比較してください

さまざまな色のモジュールは ConvBNAct です。これは、単純な畳み込み、正規化、relu/silu 活性化関数モジュールです。

Fusion Block はメインの融合モジュールであり、yaml ファイルの RepGFPN に対応します。

 入力は 2 層または 3 層です. concat の後, 1x1 畳み込みを使用してチャネルを削減します. 以下は ELAN を模倣した特徴集約モジュールです. N Rep 3x3 畳み込みと 3x3 畳み込みで構成されます. 異なる層が同時に出力されます,次に concat を渡して最終出力を取得します。

 入力RepGFPNのパラメータの意味は[出力チャネル、深さ係数、中間層チャネルのスケーリングファクタ、使用される活性化関数の種類]ですが、重いNeckと軽いHeadの概念を維持するために、削除しました。 6 のデカップリング ヘッドを Head_out に置き換えた単純な出力

yolov6t+RepGFPN-T 構造の yaml ファイル:

depth_multiple: 0.33  # model depth multiple
width_multiple: 0.375  # layer channel multiple
backbone:
  # [from, number, module, args]
  [[-1, 1, RepVGGBlock, [64, 3, 2]],  # 0-P1/2
   [-1, 1, RepVGGBlock, [128, 3, 2]],  # 1-P2/4
   [-1, 6, RepBlock, [128]],
   [-1, 1, RepVGGBlock, [256, 3, 2]],  # 3-P3/8
   [-1, 12, RepBlock, [256]],
   [-1, 1, RepVGGBlock, [512, 3, 2]],  # 5-P4/16
   [-1, 18, RepBlock, [512]],
   [-1, 1, RepVGGBlock, [1024, 3, 2]],  # 7-P5/32
   [-1, 6, RepBlock, [1024]],
   [-1, 1, SimSPPF, [1024, 5]]]  # 9
neck:
   [
    [ 6, 1,ConvBNAct,[ 192, 3, 2 ] ],
    [ [ -1, 9 ], 1, Concat, [ 1 ] ], # 576
    [ -1, 1, RepGFPN, [ 384, 1.0, 1.0 ] ],  #  8

    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ 4, 1,ConvBNAct,[ 96, 3, 2 ] ],
    [ [ -1, 6, 13 ], 1, Concat, [ 1 ] ], # 672
    [ -1, 1, RepGFPN, [ 192, 1.0, 1.0 ] ], # merge_4 12

    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ [ -1, 4 ], 1, Concat, [ 1 ] ], # 288
    [ -1, 1, RepGFPN, [ 64, 1.0, 1.0 ] ], #  merge_5  15  out

    [ -1, 1,ConvBNAct,[ 64, 3, 2 ] ],
    [ [ -1, 16 ], 1, Concat, [ 1 ] ], # 256
    [ -1, 1, RepGFPN, [ 128, 1.0, 1.0 ] ], #   merge_7  18  out

    [ 16, 1,ConvBNAct,[ 192, 3, 2 ] ],
    [ -2, 1,ConvBNAct,[ 128, 3, 2 ] ],
    [ [ -1, 12, -2 ], 1, Concat, [ 1 ] ], # 704
    [ -1, 1, RepGFPN, [ 256, 1.0, 1.0 ] ], #  merge_6 22  out
   ]

effidehead:
  [[19, 1,Head_out , [170, 0]],  ##170 * 0.375 = 64
  [22, 1, Head_out, [341, 0]],   ##341 * 0.375 = 128
  [26, 1, Head_out, [682, 0]],   ##682 * 0.375 = 256
  [[27, 28, 29], 1, Out, []]]


追加する必要があるコードは、common.py に追加するか、RepGFPN.py ファイルを自分で作成してから、モジュール名を common.py にインポートすることです。

yolov6.layers.damo_yolo から ConvBNAct、RepGFPN をインポート
import numpy as np
import torch
import torch.nn as nn

class RepGFPN(nn.Module):
    def __init__(self,in_channels,out_channels,depth=1.0,hidden_ratio = 1.0,act = 'relu',block_name='BasicBlock_3x3_Reverse',spp = False):
        super(RepGFPN, self).__init__()


        self.merge_3 = CSPStage(block_name,
                                in_channels,
                                hidden_ratio,
                                out_channels,
                                round(3 * depth),
                                act=act)


    def forward(self,x):
        x = self.merge_3(x)
        return  x
class CSPStage(nn.Module):
    def __init__(self,
                 block_fn,
                 ch_in,
                 ch_hidden_ratio,
                 ch_out,
                 n,
                 act='swish',
                 spp=False):
        super(CSPStage, self).__init__()

        split_ratio = 2
        ch_first = int(ch_out // split_ratio)
        ch_mid = int(ch_out - ch_first)
        self.conv1 = ConvBNAct(ch_in, ch_first, 1, act=act)
        self.conv2 = ConvBNAct(ch_in, ch_mid, 1, act=act)
        self.convs = nn.Sequential()

        next_ch_in = ch_mid
        for i in range(n):
            if block_fn == 'BasicBlock_3x3_Reverse':
                self.convs.add_module(
                    str(i),
                    BasicBlock_3x3_Reverse(next_ch_in,
                                           ch_hidden_ratio,
                                           ch_mid,
                                           act=act,
                                           shortcut=True))
            else:
                raise NotImplementedError
            if i == (n - 1) // 2 and spp:
                self.convs.add_module(
                    'spp', SPP(ch_mid * 4, ch_mid, 1, [5, 9, 13], act=act))
            next_ch_in = ch_mid
        self.conv3 = ConvBNAct(ch_mid * n + ch_first, ch_out, 1, act=act)

    def forward(self, x):
        y1 = self.conv1(x)
        y2 = self.conv2(x)

        mid_out = [y1]
        for conv in self.convs:
            y2 = conv(y2)
            mid_out.append(y2)
        y = torch.cat(mid_out, axis=1)
        y = self.conv3(y)
        return y
class ConvBNAct(nn.Module):
    """A Conv2d -> Batchnorm -> silu/leaky relu block"""
    def __init__(
        self,
        in_channels,
        out_channels,
        ksize,
        stride=1,
        act='relu',
        groups=1,
        bias=False,
        norm='bn',
        reparam=False,
    ):
        super().__init__()
        # same padding
        pad = (ksize - 1) // 2
        self.conv = nn.Conv2d(
            in_channels,
            out_channels,
            kernel_size=ksize,
            stride=stride,
            padding=pad,
            groups=groups,
            bias=bias,
        )
        if norm is not None:
            self.bn = get_norm(norm, out_channels, inplace=True)
        if act is not None:
            self.act = get_activation(act, inplace=True)
        self.with_norm = norm is not None
        self.with_act = act is not None

    def forward(self, x):
        x = self.conv(x)
        if self.with_norm:
            x = self.bn(x)
        if self.with_act:
            x = self.act(x)
        return x

    def fuseforward(self, x):
        return self.act(self.conv(x))
class BasicBlock_3x3_Reverse(nn.Module):
    def __init__(self,
                 ch_in,
                 ch_hidden_ratio,
                 ch_out,
                 act='relu',
                 shortcut=True):
        super(BasicBlock_3x3_Reverse, self).__init__()
        assert ch_in == ch_out
        ch_hidden = int(ch_in * ch_hidden_ratio)
        self.conv1 = ConvBNAct(ch_hidden, ch_out, 3, stride=1, act=act)
        self.conv2 = RepConv(ch_in, ch_hidden, 3, stride=1, act=act)
        self.shortcut = shortcut

    def forward(self, x):
        y = self.conv2(x)
        y = self.conv1(y)
        if self.shortcut:
            return x + y
        else:
            return y
def get_norm(name, out_channels, inplace=True):
    if name == 'bn':
        module = nn.BatchNorm2d(out_channels)
    else:
        raise NotImplementedError
    return module
class SPP(nn.Module):
    def __init__(
        self,
        ch_in,
        ch_out,
        k,
        pool_size,
        act='swish',
    ):
        super(SPP, self).__init__()
        self.pool = []
        for i, size in enumerate(pool_size):
            pool = nn.MaxPool2d(kernel_size=size,
                                stride=1,
                                padding=size // 2,
                                ceil_mode=False)
            self.add_module('pool{}'.format(i), pool)
            self.pool.append(pool)
        self.conv = ConvBNAct(ch_in, ch_out, k, act=act)

    def forward(self, x):
        outs = [x]

        for pool in self.pool:
            outs.append(pool(x))
        y = torch.cat(outs, axis=1)

        y = self.conv(y)
        return y
import torch.nn.functional as F
class Swish(nn.Module):
    def __init__(self, inplace=True):
        super(Swish, self).__init__()
        self.inplace = inplace

    def forward(self, x):
        if self.inplace:
            x.mul_(F.sigmoid(x))
            return x
        else:
            return x * F.sigmoid(x)
class RepConv(nn.Module):
    '''RepConv is a basic rep-style block, including training and deploy status
    Code is based on https://github.com/DingXiaoH/RepVGG/blob/main/repvgg.py
    '''
    def __init__(self,
                 in_channels,
                 out_channels,
                 kernel_size=3,
                 stride=1,
                 padding=1,
                 dilation=1,
                 groups=1,
                 padding_mode='zeros',
                 deploy=False,
                 act='relu',
                 norm=None):
        super(RepConv, self).__init__()
        self.deploy = deploy
        self.groups = groups
        self.in_channels = in_channels
        self.out_channels = out_channels

        assert kernel_size == 3
        assert padding == 1

        padding_11 = padding - kernel_size // 2

        if isinstance(act, str):
            self.nonlinearity = get_activation(act)
        else:
            self.nonlinearity = act

        if deploy:
            self.rbr_reparam = nn.Conv2d(in_channels=in_channels,
                                         out_channels=out_channels,
                                         kernel_size=kernel_size,
                                         stride=stride,
                                         padding=padding,
                                         dilation=dilation,
                                         groups=groups,
                                         bias=True,
                                         padding_mode=padding_mode)

        else:
            self.rbr_identity = None
            self.rbr_dense = conv_bn(in_channels=in_channels,
                                     out_channels=out_channels,
                                     kernel_size=kernel_size,
                                     stride=stride,
                                     padding=padding,
                                     groups=groups)
            self.rbr_1x1 = conv_bn(in_channels=in_channels,
                                   out_channels=out_channels,
                                   kernel_size=1,
                                   stride=stride,
                                   padding=padding_11,
                                   groups=groups)

    def forward(self, inputs):
        '''Forward process'''
        if hasattr(self, 'rbr_reparam'):
            return self.nonlinearity(self.rbr_reparam(inputs))

        if self.rbr_identity is None:
            id_out = 0
        else:
            id_out = self.rbr_identity(inputs)

        return self.nonlinearity(
            self.rbr_dense(inputs) + self.rbr_1x1(inputs) + id_out)

    def get_equivalent_kernel_bias(self):
        kernel3x3, bias3x3 = self._fuse_bn_tensor(self.rbr_dense)
        kernel1x1, bias1x1 = self._fuse_bn_tensor(self.rbr_1x1)
        kernelid, biasid = self._fuse_bn_tensor(self.rbr_identity)
        return kernel3x3 + self._pad_1x1_to_3x3_tensor(
            kernel1x1) + kernelid, bias3x3 + bias1x1 + biasid

    def _pad_1x1_to_3x3_tensor(self, kernel1x1):
        if kernel1x1 is None:
            return 0
        else:
            return torch.nn.functional.pad(kernel1x1, [1, 1, 1, 1])

    def _fuse_bn_tensor(self, branch):
        if branch is None:
            return 0, 0
        if isinstance(branch, nn.Sequential):
            kernel = branch.conv.weight
            running_mean = branch.bn.running_mean
            running_var = branch.bn.running_var
            gamma = branch.bn.weight
            beta = branch.bn.bias
            eps = branch.bn.eps
        else:
            assert isinstance(branch, nn.BatchNorm2d)
            if not hasattr(self, 'id_tensor'):
                input_dim = self.in_channels // self.groups
                kernel_value = np.zeros((self.in_channels, input_dim, 3, 3),
                                        dtype=np.float32)
                for i in range(self.in_channels):
                    kernel_value[i, i % input_dim, 1, 1] = 1
                self.id_tensor = torch.from_numpy(kernel_value).to(
                    branch.weight.device)
            kernel = self.id_tensor
            running_mean = branch.running_mean
            running_var = branch.running_var
            gamma = branch.weight
            beta = branch.bias
            eps = branch.eps
        std = (running_var + eps).sqrt()
        t = (gamma / std).reshape(-1, 1, 1, 1)
        return kernel * t, beta - running_mean * gamma / std

    def switch_to_deploy(self):
        if hasattr(self, 'rbr_reparam'):
            return
        kernel, bias = self.get_equivalent_kernel_bias()
        self.rbr_reparam = nn.Conv2d(
            in_channels=self.rbr_dense.conv.in_channels,
            out_channels=self.rbr_dense.conv.out_channels,
            kernel_size=self.rbr_dense.conv.kernel_size,
            stride=self.rbr_dense.conv.stride,
            padding=self.rbr_dense.conv.padding,
            dilation=self.rbr_dense.conv.dilation,
            groups=self.rbr_dense.conv.groups,
            bias=True)
        self.rbr_reparam.weight.data = kernel
        self.rbr_reparam.bias.data = bias
        for para in self.parameters():
            para.detach_()
        self.__delattr__('rbr_dense')
        self.__delattr__('rbr_1x1')
        if hasattr(self, 'rbr_identity'):
            self.__delattr__('rbr_identity')
        if hasattr(self, 'id_tensor'):
            self.__delattr__('id_tensor')
        self.deploy = True
def get_activation(name='silu', inplace=True):
    if name is None:
        return nn.Identity()

    if isinstance(name, str):
        if name == 'silu':
            module = nn.SiLU(inplace=inplace)
        elif name == 'relu':
            module = nn.ReLU(inplace=inplace)
        elif name == 'lrelu':
            module = nn.LeakyReLU(0.1, inplace=inplace)
        elif name == 'swish':
            module = Swish(inplace=inplace)
        elif name == 'hardsigmoid':
            module = nn.Hardsigmoid(inplace=inplace)
        elif name == 'identity':
            module = nn.Identity()
        else:
            raise AttributeError('Unsupported act type: {}'.format(name))
        return module

    elif isinstance(name, nn.Module):
        return name

    else:
        raise AttributeError('Unsupported act type: {}'.format(name))
def conv_bn(in_channels, out_channels, kernel_size, stride, padding, groups=1):
    '''Basic cell for rep-style block, including conv and bn'''
    result = nn.Sequential()
    result.add_module(
        'conv',
        nn.Conv2d(in_channels=in_channels,
                  out_channels=out_channels,
                  kernel_size=kernel_size,
                  stride=stride,
                  padding=padding,
                  groups=groups,
                  bias=False))
    result.add_module('bn', nn.BatchNorm2d(num_features=out_channels))
    return result

yolo.pyを追加

elif m in [RepGFPN, ConvBnAct]:
            c1 = ch[f]
            c2 = args[0]
            args = [c1, c2, *args[1:]]

これで完了です。

おすすめ

転載: blog.csdn.net/qq_43000647/article/details/128293855