适用于YOLO模型的一些模块

1、RVBS

RVBS

SPD(Slicing)

python 复制代码
class RVBS(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1):
        super(RVBS, self).__init__()
        out_channels = out_channels // 4
        self.in_channels = in_channels
        self.nonlinearity = nn.SiLU()
        self.rbr_dense = self.conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
        self.rbr_1x1 = self.conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=stride, padding=padding - kernel_size // 2)
        self.rbr_identity = nn.BatchNorm2d(num_features=in_channels) if out_channels == in_channels and stride == 1 else None

    def conv_bn(self, in_channels, out_channels, kernel_size, stride, padding):
        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, bias=False))
        result.add_module('bn', nn.BatchNorm2d(num_features=out_channels))
        return result

    def _pad_1x1_to_3x3_tensor(self, kernel1x1):
        if kernel1x1 is None:
            return 0
        else:
            return F.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'):
                kernel_value = np.zeros((self.in_channels, self.in_channels, 3, 3), dtype=np.float32)
                for i in range(self.in_channels):
                    kernel_value[i, i, 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 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 forward(self, inputs):
        if self.rbr_identity is None:
            id_out = 0
        else:
            id_out = self.rbr_identity(inputs)
        x = self.nonlinearity(self.rbr_dense(inputs) + self.rbr_1x1(inputs) + id_out)
        return torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)

    def fusevggforward(self, inputs):
        x = self.nonlinearity(self.rbr_dense(inputs))
        return torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)

2、ASDCN

ASDCN

python 复制代码
class ASDCN(nn.Module):
    def __init__(self, in_channels, out_channels, conv2d_bias=True, reduce_gamma=False):
        super().__init__()
        out_channels = out_channels // 4
        self.square_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=3, stride=1, padding=1, bias=conv2d_bias)
        self.square_bn = nn.BatchNorm2d(num_features=out_channels)
        self.ver_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=(3, 1), stride=1, padding=(1, 0), bias=conv2d_bias)
        self.ver_bn = nn.BatchNorm2d(num_features=out_channels)
        self.hor_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=(1, 3), stride=1, padding=(0, 1), bias=conv2d_bias)
        self.hor_bn = nn.BatchNorm2d(num_features=out_channels)
        self.act = nn.SiLU()
        if reduce_gamma:
            self.init_gamma(1.0 / 3)

    def init_gamma(self, gamma_value):
        init.constant_(self.square_bn.weight, gamma_value)
        init.constant_(self.ver_bn.weight, gamma_value)
        init.constant_(self.hor_bn.weight, gamma_value)

    def _fuse_bn_tensor(self, conv, bn):
        std = (bn.running_var + bn.eps).sqrt()
        t = (bn.weight / std).reshape(-1, 1, 1, 1)
        conv_bias = conv.bias if conv.bias is not None else torch.zeros_like(bn.running_mean)
        return conv.weight * t, bn.bias + (conv_bias - bn.running_mean) * bn.weight / std

    def _add_to_square_kernel(self, square_kernel, asym_kernel):
        asym_h = asym_kernel.size(2)
        asym_w = asym_kernel.size(3)
        square_h = square_kernel.size(2)
        square_w = square_kernel.size(3)
        square_kernel[:, :, square_h // 2 - asym_h // 2: square_h // 2 - asym_h // 2 + asym_h, square_w // 2 - asym_w // 2: square_w // 2 - asym_w // 2 + asym_w] += asym_kernel

    def get_equivalent_kernel_bias(self):
        hor_k, hor_b = self._fuse_bn_tensor(self.hor_conv, self.hor_bn)
        ver_k, ver_b = self._fuse_bn_tensor(self.ver_conv, self.ver_bn)
        square_k, square_b = self._fuse_bn_tensor(self.square_conv, self.square_bn)
        self._add_to_square_kernel(square_k, hor_k)
        self._add_to_square_kernel(square_k, ver_k)
        return square_k, hor_b + ver_b + square_b

    def fuse_convs(self):
        deploy_k, deploy_b = self.get_equivalent_kernel_bias()
        self.fused_conv = nn.Conv2d(in_channels=self.square_conv.in_channels,
                                    out_channels=self.square_conv.out_channels,
                                    kernel_size=self.square_conv.kernel_size,
                                    stride=self.square_conv.stride,
                                    padding=self.square_conv.padding,
                                    bias=True).requires_grad_(False)
        self.fused_conv.weight.data = deploy_k
        self.fused_conv.bias.data = deploy_b
        for para in self.parameters():
            para.detach_()
        self.__delattr__('square_conv')
        self.__delattr__('square_bn')
        self.__delattr__('hor_conv')
        self.__delattr__('hor_bn')
        self.__delattr__('ver_conv')
        self.__delattr__('ver_bn')

    def forward(self, input):
        square_outputs = self.square_bn(self.square_conv(input))
        vertical_outputs = self.ver_bn(self.ver_conv(input))
        horizontal_outputs = self.hor_bn(self.hor_conv(input))
        result = self.act(square_outputs + vertical_outputs + horizontal_outputs)
        return torch.cat([result[..., ::2, ::2], result[..., 1::2, ::2], result[..., ::2, 1::2], result[..., 1::2, 1::2]], 1)

    def forward_fuse(self, input):
        result = self.act(self.fused_conv(input))
        return torch.cat([result[..., ::2, ::2], result[..., 1::2, ::2], result[..., ::2, 1::2], result[..., 1::2, 1::2]], 1)

3、 DBPA-C3k2

DBPA-C3k2

DBB

PPA

Attention

python 复制代码
# --------------------------------------------------DBPA_C3k2 begin--------------------------------------------------
class DBPA_C3k2(nn.Module):
    def __init__(self, c1, c2, n=1, c3k=False, e=0.5, front=False, middle=False, back=False, attention=None, single_init=True, shortcut=True):
        super().__init__()
        self.c = int(c2 * e)
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)
        self.cv2 = Conv((2 + n) * self.c, c2, 1)
        self.m = nn.ModuleList(nn.Sequential(ADBC3k(self.c, self.c, 2, middle, single_init, shortcut) if c3k else ADBBottleneck(self.c, self.c, middle, single_init, shortcut), DBB(self.c, self.c, single_init=single_init)) for _ in range(n))
        self.front = front
        self.back = back
        if self.front:
            # self.attention = LSKblock(c1)
            # self.attention = CoordAtt(c1, c1)
            # self.attention = SE(c1)
            # self.attention = CBAM(c1)
            # self.attention = OECA()
            self.attention = PPA(c1, c1)
        if self.back:
            # if attention == 'LSKblock':
            #     self.attention = LSKblock(c2)
            # if attention == 'CoordAtt':
            #     self.attention = CoordAtt(c2, c2)
            # if attention == 'SE':
            #     self.attention = SE(c2)
            # if attention == 'CBAM':
            #     self.attention = CBAM(c2)
            # if attention == 'OECA':
            #     self.attention = OECA()
            if attention is None:
                self.attention = PPA(c2, c2)

    def forward(self, x):
        y = list(self.cv1(self.attention(x) if self.front else x).chunk(2, 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.attention(self.cv2(torch.cat(y, 1))) if self.back else self.cv2(torch.cat(y, 1))


class ADBC3k(nn.Module):
    def __init__(self, c1, c2, n=1, middle=False, single_init=True, shortcut=True, e=0.5):
        super().__init__()
        c_ = int(c2 * e)
        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(*(ADBBottleneck(c_, c_, middle, single_init, shortcut, e=1.0) for _ in range(n)))

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))


class ADBBottleneck(nn.Module):
    def __init__(self, c1, c2, middle=False, single_init=True, shortcut=True, e=0.5):
        super().__init__()
        c_ = int(c2 * e)
        self.cv1 = DBB(c1, c_, single_init=single_init)
        self.cv2 = DBB(c_, c2, single_init=single_init)
        self.middle = middle

        if self.middle:
            # self.attention = LSKblock(c2)
            # self.attention = CoordAtt(c2, c2)
            # self.attention = SE(c2)
            # self.attention = CBAM(c2)
            # self.attention = OECA()
            self.attention = PPA(c2, c2)
        self.add = shortcut and c1 == c2

    def forward(self, x):
        if self.middle:
            return x + self.attention(self.cv2(self.cv1(x))) if self.add else self.attention(self.cv2(self.cv1(x)))
        else:
            return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))


# --------------------------------------------------DBPA_C3k2 end--------------------------------------------------


# ****************************************DBB begin****************************************
class DBB(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1, single_init=True):
        super().__init__()
        self.kernel_size = kernel_size
        self.out_channels = out_channels
        self.dbb_1x1 = conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=stride, padding=0)
        self.dbb_1x1_kxk = nn.Sequential()
        self.dbb_1x1_kxk.add_module('idconv1', IdentityBasedConv1x1(channels=in_channels))
        self.dbb_1x1_kxk.add_module('bn1', BNAndPadLayer(pad_pixels=padding, num_features=in_channels))
        self.dbb_1x1_kxk.add_module('conv2', nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride, padding=0, bias=False))
        self.dbb_1x1_kxk.add_module('bn2', nn.BatchNorm2d(out_channels))
        self.dbb_avg = nn.Sequential()
        self.dbb_avg.add_module('conv', nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=1, padding=0, bias=False))
        self.dbb_avg.add_module('bn', BNAndPadLayer(pad_pixels=padding, num_features=out_channels))
        self.dbb_avg.add_module('avg', nn.AvgPool2d(kernel_size=kernel_size, stride=stride, padding=0))
        self.dbb_avg.add_module('avgbn', nn.BatchNorm2d(out_channels))
        self.dbb_origin = conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
        self.nonlinear = nn.SiLU()
        if single_init:
            self.single_init()

    def single_init(self):
        self.init_gamma(0.0)
        init.constant_(self.dbb_origin.bn.weight, 1.0)

    def init_gamma(self, gamma_value):
        init.constant_(self.dbb_origin.bn.weight, gamma_value)
        init.constant_(self.dbb_1x1.bn.weight, gamma_value)
        init.constant_(self.dbb_avg.avgbn.weight, gamma_value)
        init.constant_(self.dbb_1x1_kxk.bn2.weight, gamma_value)

    def get_equivalent_kernel_bias(self):
        k_origin, b_origin = transI_fusebn(self.dbb_origin.conv.weight, self.dbb_origin.bn)
        k_1x1, b_1x1 = transI_fusebn(self.dbb_1x1.conv.weight, self.dbb_1x1.bn)
        k_1x1 = transVI_multiscale(k_1x1, self.kernel_size)
        k_1x1_kxk_first = self.dbb_1x1_kxk.idconv1.get_actual_kernel()
        k_1x1_kxk_first, b_1x1_kxk_first = transI_fusebn(k_1x1_kxk_first, self.dbb_1x1_kxk.bn1)
        k_1x1_kxk_second, b_1x1_kxk_second = transI_fusebn(self.dbb_1x1_kxk.conv2.weight, self.dbb_1x1_kxk.bn2)
        k_1x1_kxk_merged, b_1x1_kxk_merged = transIII_1x1_kxk(k_1x1_kxk_first, b_1x1_kxk_first, k_1x1_kxk_second, b_1x1_kxk_second, groups=1)
        k_avg = transV_avg(self.out_channels, self.kernel_size, 1)
        k_1x1_avg_second, b_1x1_avg_second = transI_fusebn(k_avg.to(self.dbb_avg.avgbn.weight.device), self.dbb_avg.avgbn)
        k_1x1_avg_first, b_1x1_avg_first = transI_fusebn(self.dbb_avg.conv.weight, self.dbb_avg.bn)
        k_1x1_avg_merged, b_1x1_avg_merged = transIII_1x1_kxk(k_1x1_avg_first, b_1x1_avg_first, k_1x1_avg_second, b_1x1_avg_second, groups=1)
        return transII_addbranch((k_origin, k_1x1, k_1x1_kxk_merged, k_1x1_avg_merged), (b_origin, b_1x1, b_1x1_kxk_merged, b_1x1_avg_merged))

    def fuse_convs(self):
        kernel, bias = self.get_equivalent_kernel_bias()
        self.dbb_reparam = nn.Conv2d(in_channels=self.dbb_origin.conv.in_channels,
                                     out_channels=self.dbb_origin.conv.out_channels,
                                     kernel_size=self.dbb_origin.conv.kernel_size,
                                     stride=self.dbb_origin.conv.stride,
                                     padding=self.dbb_origin.conv.padding,
                                     bias=True).requires_grad_(False)
        self.dbb_reparam.weight.data = kernel
        self.dbb_reparam.bias.data = bias
        for para in self.parameters():
            para.detach_()
        self.__delattr__('dbb_origin')
        self.__delattr__('dbb_avg')
        self.__delattr__('dbb_1x1')
        self.__delattr__('dbb_1x1_kxk')

    def forward(self, inputs):
        return self.nonlinear(self.dbb_origin(inputs) + self.dbb_1x1(inputs) + self.dbb_avg(inputs) + self.dbb_1x1_kxk(inputs))

    def forward_fuse(self, inputs):
        return self.nonlinear(self.dbb_reparam(inputs))


def conv_bn(in_channels, out_channels, kernel_size, stride, padding):
    conv_layer = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride, padding=padding, bias=False)
    bn_layer = nn.BatchNorm2d(num_features=out_channels)
    se = nn.Sequential()
    se.add_module('conv', conv_layer)
    se.add_module('bn', bn_layer)
    return se


class IdentityBasedConv1x1(nn.Conv2d):
    def __init__(self, channels):
        super().__init__(in_channels=channels, out_channels=channels, kernel_size=1, stride=1, padding=0, bias=False)
        id_value = np.zeros((channels, channels, 1, 1))
        for i in range(channels):
            id_value[i, i, 0, 0] = 1
        self.id_tensor = torch.from_numpy(id_value).type_as(self.weight)
        init.zeros_(self.weight)

    def forward(self, input):
        kernel = self.weight + self.id_tensor.to(self.weight.device)
        if input.dtype != kernel.dtype:
            kernel = kernel.to(input.dtype)
        result = F.conv2d(input, kernel, None, stride=1, padding=0, dilation=self.dilation, groups=self.groups)
        return result

    def get_actual_kernel(self):
        return self.weight + self.id_tensor.to(self.weight.device)


class BNAndPadLayer(nn.Module):
    def __init__(self, pad_pixels, num_features):
        super().__init__()
        self.bn = nn.BatchNorm2d(num_features)
        self.pad_pixels = pad_pixels

    def forward(self, input):
        output = self.bn(input)
        if self.pad_pixels > 0:
            pad_values = self.bn.bias.detach() - self.bn.running_mean * self.bn.weight.detach() / torch.sqrt(self.bn.running_var + self.bn.eps)
            output = F.pad(output, [self.pad_pixels] * 4)
            pad_values = pad_values.view(1, -1, 1, 1)
            output[:, :, 0:self.pad_pixels, :] = pad_values
            output[:, :, -self.pad_pixels:, :] = pad_values
            output[:, :, :, 0:self.pad_pixels] = pad_values
            output[:, :, :, -self.pad_pixels:] = pad_values
        return output

    @property
    def weight(self):
        return self.bn.weight

    @property
    def bias(self):
        return self.bn.bias

    @property
    def running_mean(self):
        return self.bn.running_mean

    @property
    def running_var(self):
        return self.bn.running_var

    @property
    def eps(self):
        return self.bn.eps


def transI_fusebn(kernel, bn):
    gamma = bn.weight
    std = (bn.running_var + bn.eps).sqrt()
    return kernel * ((gamma / std).reshape(-1, 1, 1, 1)), bn.bias - bn.running_mean * gamma / std


def transII_addbranch(kernels, biases):
    return sum(kernels), sum(biases)


def transIII_1x1_kxk(k1, b1, k2, b2, groups):
    if groups == 1:
        k = F.conv2d(k2, k1.permute(1, 0, 2, 3))
        b_hat = (k2 * b1.reshape(1, -1, 1, 1)).sum((1, 2, 3))
    else:
        k_slices = []
        b_slices = []
        k1_T = k1.permute(1, 0, 2, 3)
        k1_group_width = k1.size(0) // groups
        k2_group_width = k2.size(0) // groups
        for g in range(groups):
            k1_T_slice = k1_T[:, g * k1_group_width:(g + 1) * k1_group_width, :, :]
            k2_slice = k2[g * k2_group_width:(g + 1) * k2_group_width, :, :, :]
            k_slices.append(F.conv2d(k2_slice, k1_T_slice))
            b_slices.append((k2_slice * b1[g * k1_group_width:(g + 1) * k1_group_width].reshape(1, -1, 1, 1)).sum((1, 2, 3)))
        k, b_hat = transIV_depthconcat(k_slices, b_slices)
    return k, b_hat + b2


def transIV_depthconcat(kernels, biases):
    return torch.cat(kernels, dim=0), torch.cat(biases)


def transV_avg(channels, kernel_size, groups):
    input_dim = channels // groups
    k = torch.zeros((channels, input_dim, kernel_size, kernel_size))
    k[np.arange(channels), np.tile(np.arange(input_dim), groups), :, :] = 1.0 / kernel_size ** 2
    return k


def transVI_multiscale(kernel, target_kernel_size):
    H_pixels_to_pad = (target_kernel_size - kernel.size(2)) // 2
    W_pixels_to_pad = (target_kernel_size - kernel.size(3)) // 2
    return F.pad(kernel, [H_pixels_to_pad, H_pixels_to_pad, W_pixels_to_pad, W_pixels_to_pad])


# ****************************************DBB end******************************************

# ****************************************PPA begin****************************************
class SpatialAttentionModule(nn.Module):
    def __init__(self):
        super(SpatialAttentionModule, self).__init__()
        self.conv2d = nn.Conv2d(
            in_channels=2, out_channels=1, kernel_size=7, stride=1, padding=3
        )
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        avgout = torch.mean(x, dim=1, keepdim=True)
        maxout, _ = torch.max(x, dim=1, keepdim=True)
        out = torch.cat([avgout, maxout], dim=1)
        out = self.sigmoid(self.conv2d(out))
        return out * x


class LocalGlobalAttention(nn.Module):
    def __init__(self, output_dim, patch_size):
        super().__init__()
        self.output_dim = output_dim
        self.patch_size = patch_size
        self.mlp1 = nn.Linear(patch_size * patch_size, output_dim // 2)
        self.norm = nn.LayerNorm(output_dim // 2)
        self.mlp2 = nn.Linear(output_dim // 2, output_dim)
        self.conv = nn.Conv2d(output_dim, output_dim, kernel_size=1)
        self.prompt = torch.nn.parameter.Parameter(
            torch.randn(output_dim, requires_grad=True)
        )
        self.top_down_transform = torch.nn.parameter.Parameter(
            torch.eye(output_dim), requires_grad=True
        )

    def forward(self, x):
        x = x.permute(0, 2, 3, 1)
        B, H, W, C = x.shape
        P = self.patch_size

        # Local branch
        local_patches = x.unfold(1, P, P).unfold(2, P, P)  # (B, H/P, W/P, P, P, C)
        local_patches = local_patches.reshape(B, -1, P * P, C)  # (B, H/P*W/P, P*P, C)
        local_patches = local_patches.mean(dim=-1)  # (B, H/P*W/P, P*P)

        local_patches = self.mlp1(local_patches)  # (B, H/P*W/P, input_dim // 2)
        local_patches = self.norm(local_patches)  # (B, H/P*W/P, input_dim // 2)
        local_patches = self.mlp2(local_patches)  # (B, H/P*W/P, output_dim)

        local_attention = F.softmax(local_patches, dim=-1)  # (B, H/P*W/P, output_dim)
        local_out = local_patches * local_attention  # (B, H/P*W/P, output_dim)

        cos_sim = F.normalize(local_out, dim=-1) @ F.normalize(
            self.prompt[None, ..., None], dim=1
        )  # B, N, 1
        mask = cos_sim.clamp(0, 1)  # B, N, 1
        local_out = local_out * mask  # (B, H/P*W/P, output_dim)
        local_out = local_out @ self.top_down_transform  # (B, H/P*W/P, output_dim)

        # Restore shapes
        local_out = local_out.reshape(
            B, H // P, W // P, self.output_dim
        )  # (B, H/P, W/P, output_dim)
        local_out = local_out.permute(0, 3, 1, 2)
        local_out = F.interpolate(
            local_out, size=(H, W), mode="bilinear", align_corners=False
        )  # (B, output_dim,H, W, )
        output = self.conv(local_out)  # (B, output_dim,H, W, )

        return output


class ECA(nn.Module):
    def __init__(self, in_channel, gamma=2, b=1):
        super(ECA, self).__init__()
        k = int(abs((math.log(in_channel, 2) + b) / gamma))
        kernel_size = k if k % 2 else k + 1
        padding = kernel_size // 2
        self.pool = nn.AdaptiveAvgPool2d(output_size=1)
        self.conv = nn.Sequential(
            nn.Conv1d(
                in_channels=1,
                out_channels=1,
                kernel_size=kernel_size,
                padding=padding,
                bias=False,
            ),
            nn.Sigmoid(),
        )

    def forward(self, x):
        out = self.pool(x)
        out = out.view(x.size(0), 1, x.size(1))
        out = self.conv(out)
        out = out.view(x.size(0), x.size(1), 1, 1)
        return out * x


class conv_block(nn.Module):
    def __init__(
            self,
            in_features,
            out_features,
            kernel_size=(3, 3),
            stride=(1, 1),
            padding=(1, 1),
            dilation=(1, 1),
            norm_type="bn",
            activation=True,
            use_bias=True,
            groups=1,
    ):
        super().__init__()
        self.conv = nn.Conv2d(
            in_channels=in_features,
            out_channels=out_features,
            kernel_size=kernel_size,
            stride=stride,
            padding=padding,
            dilation=dilation,
            bias=use_bias,
            groups=groups,
        )

        self.norm_type = norm_type
        self.act = activation

        if self.norm_type == "gn":
            self.norm = nn.GroupNorm(
                32 if out_features >= 32 else out_features, out_features
            )
        if self.norm_type == "bn":
            self.norm = nn.BatchNorm2d(out_features)
        if self.act:
            # self.relu = nn.GELU()
            self.relu = nn.ReLU(inplace=False)

    def forward(self, x):
        x = self.conv(x)
        if self.norm_type is not None:
            x = self.norm(x)
        if self.act:
            x = self.relu(x)
        return x


class PPA(nn.Module):
    def __init__(self, in_features, filters) -> None:
        super().__init__()

        self.skip = conv_block(
            in_features=in_features,
            out_features=filters,
            kernel_size=(1, 1),
            padding=(0, 0),
            norm_type="bn",
            activation=False,
        )
        self.c1 = conv_block(
            in_features=in_features,
            out_features=filters,
            kernel_size=(3, 3),
            padding=(1, 1),
            norm_type="bn",
            activation=True,
        )
        self.c2 = conv_block(
            in_features=filters,
            out_features=filters,
            kernel_size=(3, 3),
            padding=(1, 1),
            norm_type="bn",
            activation=True,
        )
        self.c3 = conv_block(
            in_features=filters,
            out_features=filters,
            kernel_size=(3, 3),
            padding=(1, 1),
            norm_type="bn",
            activation=True,
        )
        self.sa = SpatialAttentionModule()
        self.cn = ECA(filters)
        self.lga2 = LocalGlobalAttention(filters, 2)
        self.lga4 = LocalGlobalAttention(filters, 4)

        self.bn1 = nn.BatchNorm2d(filters)
        self.drop = nn.Dropout2d(0.1)
        self.relu = nn.ReLU()

        self.gelu = nn.GELU()

    def forward(self, x):
        x_skip = self.skip(x)
        x_lga2 = self.lga2(x_skip)
        x_lga4 = self.lga4(x_skip)
        x1 = self.c1(x)
        x2 = self.c2(x1)
        x3 = self.c3(x2)
        x = x1 + x2 + x3 + x_skip + x_lga2 + x_lga4
        x = self.cn(x)
        x = self.sa(x)
        x = self.drop(x)
        x = self.bn1(x)
        x = self.relu(x)
        return x

# ****************************************PPA****************************************

4、 FI-WIoUv2

python 复制代码
class WIoU_Scale1:
    ''' if monotonous = None , v1
        if monotonous = True  , v2
        if monotonous = False , v3
    '''
    iou_mean = 1.
    monotonous = None

    _momentum = 1 - 0.5 ** (1 / 7000)
    _is_train = True

    def __init__(self, iou):
        self.iou = iou
        self._update(self)

    @classmethod
    def _update(cls, self):
        if cls._is_train: cls.iou_mean = (1 - cls._momentum) * cls.iou_mean + cls._momentum * self.iou.detach().mean().item()

    @classmethod
    def _scaled_loss(cls, self, gamma=1.9, delta=3):
        if isinstance(self.monotonous, bool):
            if self.monotonous:
                return (self.iou.detach() / self.iou_mean).sqrt()
            else:
                beta = self.iou.detach() / self.iou_mean
                alpha = delta * torch.pow(gamma, beta - delta)
                return beta / alpha
        return 1


class WIoU_Scale2:
    ''' if monotonous = None , v1
        if monotonous = True  , v2
        if monotonous = False , v3
    '''
    iou_mean = 1.
    monotonous = True

    _momentum = 1 - 0.5 ** (1 / 7000)
    _is_train = True

    def __init__(self, iou):
        self.iou = iou
        self._update(self)

    @classmethod
    def _update(cls, self):
        if cls._is_train: cls.iou_mean = (1 - cls._momentum) * cls.iou_mean + \
                                         cls._momentum * self.iou.detach().mean().item()

    @classmethod
    def _scaled_loss(cls, self, gamma=1.9, delta=3):
        if isinstance(self.monotonous, bool):
            if self.monotonous:
                return (self.iou.detach() / self.iou_mean).sqrt()
            else:
                beta = self.iou.detach() / self.iou_mean
                alpha = delta * torch.pow(gamma, beta - delta)
                return beta / alpha
        return 1


class WIoU_Scale3:
    ''' if monotonous = None , v1
        if monotonous = True  , v2
        if monotonous = False , v3
    '''
    iou_mean = 1.
    monotonous = False

    _momentum = 1 - 0.5 ** (1 / 7000)
    _is_train = True

    def __init__(self, iou):
        self.iou = iou
        self._update(self)

    @classmethod
    def _update(cls, self):
        if cls._is_train: cls.iou_mean = (1 - cls._momentum) * cls.iou_mean + \
                                         cls._momentum * self.iou.detach().mean().item()

    @classmethod
    def _scaled_loss(cls, self, gamma=1.9, delta=3):
        if isinstance(self.monotonous, bool):
            if self.monotonous:
                return (self.iou.detach() / self.iou_mean).sqrt()
            else:
                beta = self.iou.detach() / self.iou_mean
                alpha = delta * torch.pow(gamma, beta - delta)
                return beta / alpha
        return 1


def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, EIoU=False, SIoU=False, MPDIoU1=False, MPDIoU2=False, MPDIoU3=False, WIoU1=False, WIoU2=False, WIoU3=False, scale=False, Focaler=False, Focal=False, Inner=False, ratio=1.0, d=0.00, u=0.95, eps=1e-7):
    """
    Calculate Intersection over Union (IoU) of box1(1, 4) to box2(n, 4).

    Args:
        box1 (torch.Tensor): A tensor representing a single bounding box with shape (1, 4).
        box2 (torch.Tensor): A tensor representing n bounding boxes with shape (n, 4).
        xywh (bool, optional): If True, input boxes are in (x, y, w, h) format. If False, input boxes are in
                               (x1, y1, x2, y2) format. Defaults to True.
        GIoU (bool, optional): If True, calculate Generalized IoU. Defaults to False.
        DIoU (bool, optional): If True, calculate Distance IoU. Defaults to False.
        CIoU (bool, optional): If True, calculate Complete IoU. Defaults to False.
        eps (float, optional): A small value to avoid division by zero. Defaults to 1e-7.

    Returns:
        (torch.Tensor): IoU, GIoU, DIoU, or CIoU values depending on the specified flags.
    """
    # Get the coordinates of bounding boxes
    if xywh:  # transform from xywh to xyxy
        (x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)
        w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2
        b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_
        b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_
    else:  # x1, y1, x2, y2 = box1
        b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)
        b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)
        w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps
        w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps
        if Inner:
            x1 = (b1_x1 + b1_x2) / 2
            y1 = (b1_y1 + b1_y2) / 2
            x2 = (b2_x1 + b2_x2) / 2
            y2 = (b2_y1 + b2_y2) / 2
            w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2
            inner_b1_x1, inner_b1_x2, inner_b1_y1, inner_b1_y2 = x1 - w1_ * ratio, x1 + w1_ * ratio, y1 - h1_ * ratio, y1 + h1_ * ratio
            inner_b2_x1, inner_b2_x2, inner_b2_y1, inner_b2_y2 = x2 - w2_ * ratio, x2 + w2_ * ratio, y2 - h2_ * ratio, y2 + h2_ * ratio
            inner_inter = (torch.min(inner_b1_x2, inner_b2_x2) - torch.max(inner_b1_x1, inner_b2_x1)).clamp(0) * (torch.min(inner_b1_y2, inner_b2_y2) - torch.max(inner_b1_y1, inner_b2_y1)).clamp(0)
            inner_union = w1 * ratio * h1 * ratio + w2 * ratio * h2 * ratio - inner_inter + eps
            inner_iou = inner_inter / inner_union

    # Intersection area
    inter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp_(0) * (b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp_(0)

    # Union Area
    union = w1 * h1 + w2 * h2 - inter + eps

    # IoU
    iou = inter / union

    if scale:
        if Focaler:
            if Inner:
                iou = ((inner_iou - d) / (u - d)).clamp(0, 1)  # default d=0.00,u=0.95
            else:
                iou = ((iou - d) / (u - d)).clamp(0, 1)  # default d=0.00,u=0.95
        if WIoU1:
            self = WIoU_Scale1(1 - iou)
        elif WIoU2:
            self = WIoU_Scale2(1 - iou)
        else:
            self = WIoU_Scale3(1 - iou)

    if CIoU or DIoU or GIoU or EIoU or SIoU or WIoU1 or WIoU2 or WIoU3 or MPDIoU1 or MPDIoU2 or MPDIoU3:
        cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1)  # convex (smallest enclosing box) width
        ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1)  # convex height
        if CIoU or DIoU or EIoU or SIoU or WIoU1 or WIoU2 or WIoU3 or MPDIoU1 or MPDIoU2 or MPDIoU3:  # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
            c2 = cw.pow(2) + ch.pow(2) + eps  # convex diagonal squared
            rho2 = (
                           (b2_x1 + b2_x2 - b1_x1 - b1_x2).pow(2) + (b2_y1 + b2_y2 - b1_y1 - b1_y2).pow(2)
                   ) / 4  # center dist**2
            if CIoU:  # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
                v = (4 / math.pi ** 2) * ((w2 / h2).atan() - (w1 / h1).atan()).pow(2)
                with torch.no_grad():
                    alpha = v / (v - iou + (1 + eps))
                if Focaler:
                    if Inner:
                        iou = ((inner_iou - d) / (u - d)).clamp(0, 1)  # default d=0.00,u=0.95
                    else:
                        iou = ((iou - d) / (u - d)).clamp(0, 1)  # default d=0.00,u=0.95
                if Focal:
                    return iou - (rho2 / c2 + v * alpha), torch.pow(iou, 0.5)
                else:
                    return iou - (rho2 / c2 + v * alpha)  # CIoU
            if EIoU:
                rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)).pow(2)
                rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)).pow(2)
                cw2 = cw.pow(2) + eps
                ch2 = ch.pow(2) + eps
                if Focaler:
                    iou = ((iou - d) / (u - d)).clamp(0, 1)
                if Focal:
                    return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2), torch.pow(iou, 0.5)
                else:
                    return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2)
            if SIoU:
                s_cw = (b2_x1 + b2_x2 - b1_x1 - b1_x2) * 0.5 + eps
                s_ch = (b2_y1 + b2_y2 - b1_y1 - b1_y2) * 0.5 + eps
                sigma = torch.pow(s_cw ** 2 + s_ch ** 2, 0.5)
                sin_alpha_1 = torch.abs(s_cw) / sigma
                sin_alpha_2 = torch.abs(s_ch) / sigma
                threshold = pow(2, 0.5) / 2
                sin_alpha = torch.where(sin_alpha_1 > threshold, sin_alpha_2, sin_alpha_1)
                angle_cost = torch.cos(torch.arcsin(sin_alpha) * 2 - math.pi / 2)
                rho_x = (s_cw / cw) ** 2
                rho_y = (s_ch / ch) ** 2
                gamma = angle_cost - 2
                distance_cost = 2 - torch.exp(gamma * rho_x) - torch.exp(gamma * rho_y)
                omiga_w = torch.abs(w1 - w2) / torch.max(w1, w2)
                omiga_h = torch.abs(h1 - h2) / torch.max(h1, h2)
                shape_cost = torch.pow(1 - torch.exp(-1 * omiga_w), 4) + torch.pow(1 - torch.exp(-1 * omiga_h), 4)
                if Focaler:
                    iou = ((iou - d) / (u - d)).clamp(0, 1)
                if Focal:
                    return iou - 0.5 * (distance_cost + shape_cost), torch.pow(iou, 0.5)
                else:
                    return iou - 0.5 * (distance_cost + shape_cost)
            if WIoU1:
                if scale:
                    return getattr(WIoU_Scale1, '_scaled_loss')(self), (1 - iou) * torch.exp((rho2 / c2)), iou  # WIoU https://arxiv.org/abs/2301.10051
                else:
                    return iou, torch.exp((rho2 / c2))  # WIoU v1
            if WIoU2:
                if scale:
                    return getattr(WIoU_Scale2, '_scaled_loss')(self), (1 - iou) * torch.exp((rho2 / c2)), iou  # WIoU https://arxiv.org/abs/2301.10051
                else:
                    return iou, torch.exp((rho2 / c2))  # WIoU v2
            if WIoU3:
                if scale:
                    return getattr(WIoU_Scale3, '_scaled_loss')(self), (1 - iou) * torch.exp((rho2 / c2)), iou  # WIoU https://arxiv.org/abs/2301.10051
                else:
                    return iou, torch.exp((rho2 / c2))  # WIoU v3
            if MPDIoU1:
                cw2 = cw.pow(2) + eps
                ch2 = ch.pow(2) + eps
                d12 = ((b2_x1 - b1_x1) - (b2_y1 - b1_y1)) ** 2
                d22 = ((b2_x2 - b1_x2) - (b2_y2 - b1_y2)) ** 2
                if Focaler:
                    iou = ((iou - d) / (u - d)).clamp(0, 1)
                if Focal:
                    return iou - ((d12 + d22) / (cw2 + ch2)), torch.pow(iou, 0.5)
                else:
                    return iou - ((d12 + d22) / (cw2 + ch2))
            if MPDIoU2:
                cw2 = cw.pow(2)
                ch2 = ch.pow(2)
                d12 = (b2_x1 - b1_x1) ** 2 + (b2_y1 - b1_y1) ** 2
                d22 = (b2_x2 - b1_x2) ** 2 + (b2_y2 - b1_y2) ** 2
                if Focaler:
                    iou = ((iou - d) / (u - d)).clamp(0, 1)
                if Focal:
                    return iou - ((d12 + d22) / (cw2 + ch2 + eps)), torch.pow(iou, 0.5)
                else:
                    return iou - ((d12 + d22) / (cw2 + ch2 + eps))
            if MPDIoU3:
                sq_sum = (640 ** 2) + (640 ** 2)
                d12 = (b2_x1 - b1_x1) ** 2 + (b2_y1 - b1_y1) ** 2
                d22 = (b2_x2 - b1_x2) ** 2 + (b2_y2 - b1_y2) ** 2
                if Focaler:
                    iou = ((iou - d) / (u - d)).clamp(0, 1)
                if Focal:
                    return iou - ((d12 + d22) / sq_sum), torch.pow(iou, 0.5)
                else:
                    return iou - ((d12 + d22) / sq_sum)
            if Focaler:
                iou = ((iou - d) / (u - d)).clamp(0, 1)
            if Focal:
                return iou - rho2 / c2, torch.pow(iou, 0.5)
            else:
                return iou - rho2 / c2  # DIoU
        c_area = cw * ch + eps  # convex area
        if Focaler:
            iou = ((iou - d) / (u - d)).clamp(0, 1)
        if Focal:
            return iou - (c_area - union) / c_area, torch.pow(iou, 0.5)
        else:
            return iou - (c_area - union) / c_area  # GIoU https://arxiv.org/pdf/1902.09630.pdf
    return iou  # IoU
相关推荐
阿里巴巴首席技术官11 小时前
目标检测基础
笔记·yolo
jay神2 天前
深度学习为什么需要反向传播
人工智能·深度学习·yolo·目标检测·cnn·毕业设计
YOLO数据集集合2 天前
一站式AI数据自动化标注与训练平台:零门槛玩转YOLO全系列模型
人工智能·深度学习·yolo·ai·自动化·数据集·标注软件
探物 AI3 天前
yolo目标检测中的激活函数对比
人工智能·yolo·目标检测
jay神4 天前
深度学习的正确调参顺序
人工智能·深度学习·yolo·计算机视觉·课程设计
雨晨源码(同名B站)5 天前
【2027届人工智能专业选题】基于yolov8的农业病虫害图像识别与分类系统 |深度学习 计算机视觉
人工智能·深度学习·yolo·计算机视觉·分类
fl1768316 天前
无人机视角低空拍摄的土豆马铃薯幼苗与杂草检测数据集VOC+YOLO格式5266张6类
yolo·无人机
qq_25294131686 天前
列车车轮缺陷智能检测数据集:800张图像、4大类别,助力铁路安全运维
运维·人工智能·安全·yolo·目标检测·计算机视觉·视觉检测
雨晨源码(同名B站)6 天前
基于深度学习YoloV11农业病害虫害检测系统 智慧农业信息化综合管理平台 (附源码+lw文档+ppt)
数据库·人工智能·深度学习·yolo·信息可视化