Pointnet++改进:在特征提取模块加入CBAM注意力机制

简介:
1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!
2.本篇文章对Pointnet++特征提取模块进行改进,提升性能。
3.专栏持续更新,紧随最新的研究内容。

目录

1.步骤一

2.步骤二

3.步骤三

论文介绍:

Pointnet++改进:在特征提取模块加入CBAM注意力机制_第1张图片

摘要。我们提出了卷积块注意模块(CBAM),这是一种简单而有效的前馈卷积神经网络注意模块。给定一个中间特征映射,我们的模块沿着两个独立的维度依次推断注意力映射,通道和空间,然后将注意力映射乘以输入特征映射以进行自适应特征细化。因为CBAM是一个轻量级的通用模块,它可以无缝地集成到任何CNN架构中,开销可以忽略不计,并且可以与基础CNN一起进行端到端训练。我们通过在ImageNet-1K、MS COCO检测和VOC 2007检测数据集上进行大量实验来验证我们的CBAM。

我们的实验表明,各种模型在分类和检测性能上都有一致的提高,证明了CBAM的广泛适用性。代码和模型将是公开的。

1.步骤一

新建CBAM.py,加入如下代码


import torch
import torch.nn as nn

########CBAM
class ChannelAttentionModule(nn.Module):
    def __init__(self, c1, reduction=16):
        super(ChannelAttentionModule, self).__init__()
        mid_channel = c1 // reduction
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.max_pool = nn.AdaptiveMaxPool2d(1)

        self.shared_MLP = nn.Sequential(
            nn.Linear(in_features=c1, out_features=mid_channel),
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(in_features=mid_channel, out_features=c1)
        )
        self.act = nn.Sigmoid()
        # self.act=nn.SiLU()

    def forward(self, x):
        avgout = self.shared_MLP(self.avg_pool(x).view(x.size(0), -1)).unsqueeze(2).unsqueeze(3)
        maxout = self.shared_MLP(self.max_pool(x).view(x.size(0), -1)).unsqueeze(2).unsqueeze(3)
        return self.act(avgout + maxout)


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.act = 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.act(self.conv2d(out))
        return out


class CBAM(nn.Module):
    def __init__(self, c1):
        super(CBAM, self).__init__()
        self.channel_attention = ChannelAttentionModule(c1)
        self.spatial_attention = SpatialAttentionModule()

    def forward(self, x):
        out = self.channel_attention(x) * x
        out = self.spatial_attention(out) * out
        return out
##############CBAM















2.步骤二

在models/pointnet2_utils.py文件中加入如下代码:

PointNetSetAbstraction结构图:

Pointnet++改进:在特征提取模块加入CBAM注意力机制_第2张图片

class Conv(nn.Module):
    # Standard convolution
    def __init__(self, c1, c2, k=1):  # ch_in, ch_out, kernel, stride, padding, groups
        super(Conv, self).__init__()
        self.conv = nn.Conv2d(c1, c2, k)
        self.bn = nn.BatchNorm2d(c2)
        #self.act = nn.SiLU()
        #self.act = nn.LeakyReLU(0.1)
        self.act = nn.ReLU()
        #self.act = MetaAconC(c2)
        #self.act = AconC(c2)
        #self.act = Mish()
        #self.act = Hardswish()
        #self.act = FReLU(c2)
    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

    def fuseforward(self, x):
        return self.act(self.conv(x))


class PointNetSetAbstractionAttention(nn.Module):
    def __init__(self, npoint, radius, nsample, in_channel, mlp, group_all):
        super(PointNetSetAbstractionAttention, self).__init__()
        self.npoint = npoint
        self.radius = radius
        self.nsample = nsample
        #self.mlp_convs = nn.ModuleList()

        self.mlp_conv1 = Conv(in_channel,mlp[0],1)
        self.mlp_attention = CBAM(mlp[0])
        self.mlp_conv2 = Conv(mlp[0],mlp[1],1)
        self.mlp_conv3 = Conv(mlp[1],mlp[2],1)

        self.group_all = group_all

    def forward(self, xyz, points):
        """
        Input:
            xyz: input points position data, [B, C, N]
            points: input points data, [B, D, N]
        Return:
            new_xyz: sampled points position data, [B, C, S]
            new_points_concat: sample points feature data, [B, D', S]
        """
        xyz = xyz.permute(0, 2, 1)
        if points is not None:
            points = points.permute(0, 2, 1)

        if self.group_all:
            new_xyz, new_points = sample_and_group_all(xyz, points)
        else:
            new_xyz, new_points = sample_and_group(self.npoint, self.radius, self.nsample, xyz, points)
        # new_xyz: sampled points position data, [B, npoint, C]
        # new_points: sampled points data, [B, npoint, nsample, C+D]
        new_points = new_points.permute(0, 3, 2, 1)  # [B, C+D, nsample,npoint]

        new_points=self.mlp_conv1(new_points)
        new_points = self.mlp_attention(new_points)
        new_points = self.mlp_conv2(new_points)
        new_points = self.mlp_conv3(new_points)

        new_points = torch.max(new_points, 2)[0]
        new_xyz = new_xyz.permute(0, 2, 1)
        return new_xyz, new_points


########
class PointNetSetAbstractionMsgAttention(nn.Module):
    def __init__(self, npoint, radius_list, nsample_list, in_channel, mlp_list):
        super(PointNetSetAbstractionMsgAttention, self).__init__()
        self.npoint = npoint
        self.radius_list = radius_list
        self.nsample_list = nsample_list

        self.mlp_conv00 = Conv(in_channel+3,mlp_list[0][0],1)
        self.mlp_attention = CBAM(mlp_list[0][0])
        self.mlp_conv01 = Conv(mlp_list[0][0],mlp_list[0][1],1)
        self.mlp_conv02 = Conv(mlp_list[0][1],mlp_list[0][2],1)

        self.mlp_conv10 = Conv(in_channel+3,mlp_list[1][0],1)
        self.mlp_conv11 = Conv(mlp_list[1][0],mlp_list[1][1],1)
        self.mlp_conv12 = Conv(mlp_list[1][1],mlp_list[1][2],1)
        
    def forward(self, xyz, points):
        """
        Input:
            xyz: input points position data, [B, C, N]
            points: input points data, [B, D, N]
        Return:
            new_xyz: sampled points position data, [B, C, S]
            new_points_concat: sample points feature data, [B, D', S]
        """
        xyz = xyz.permute(0, 2, 1)
        if points is not None:
            points = points.permute(0, 2, 1)

        B, N, C = xyz.shape
        S = self.npoint
        new_xyz = index_points(xyz, farthest_point_sample(xyz, S))
        new_points_list = []
        for i, radius in enumerate(self.radius_list):
            K = self.nsample_list[i]
            group_idx = query_ball_point(radius, K, xyz, new_xyz)
            grouped_xyz = index_points(xyz, group_idx)
            grouped_xyz -= new_xyz.view(B, S, 1, C)
            if points is not None:
                grouped_points = index_points(points, group_idx)
                grouped_points = torch.cat([grouped_points, grouped_xyz], dim=-1)
            else:
                grouped_points = grouped_xyz

            grouped_points = grouped_points.permute(0, 3, 2, 1)  # [B, D, K, S]
            if i==0:
               grouped_points =self.mlp_conv00(grouped_points)
               grouped_points =self.mlp_attention(grouped_points)
               grouped_points = self.mlp_conv01(grouped_points)
               grouped_points = self.mlp_conv02(grouped_points)
            else:
                grouped_points = self.mlp_conv10(grouped_points)
                grouped_points = self.mlp_conv11(grouped_points)
                grouped_points = self.mlp_conv12(grouped_points)
            # for j in range(len(self.conv_blocks[i])):
            #     conv = self.conv_blocks[i][j]
            #     bn = self.bn_blocks[i][j]
            #     grouped_points =  F.relu(bn(conv(grouped_points)))
            new_points = torch.max(grouped_points, 2)[0]  # [B, D', S]
            new_points_list.append(new_points)

        new_xyz = new_xyz.permute(0, 2, 1)
        new_points_concat = torch.cat(new_points_list, dim=1)
        return new_xyz, new_points_concat
#################

3.步骤三

在models/pointnet2_sem_seg.py文件中修改引用即可,可以根据自己的需要添加一个或者多个,然后训练即可。分类和分割模型除了最后的检测头,结构基本一致。

import torch.nn as nn
import torch.nn.functional as F
# from models.pointnet2_utils import PointNetSetAbstraction, PointNetFeaturePropagation, PointNetSetAbstractionKPconv, \
#     PointNetSetAbstractionAttention
from models.pointnet2_utils import *

class get_model(nn.Module):
    def __init__(self, num_classes):
        super(get_model, self).__init__()

        self.sa1 = PointNetSetAbstractionAttention(1024, 0.1, 32, 9 + 3, [32, 32, 64], False)
        self.sa2 = PointNetSetAbstraction(256, 0.2, 32, 64 + 3, [64, 64, 128], False)
        self.sa3 = PointNetSetAbstraction(64, 0.4, 32, 128 + 3, [128, 128, 256], False)
        self.sa4 = PointNetSetAbstraction(16, 0.8, 32, 256 + 3, [256, 256, 512], False)
        self.fp4 = PointNetFeaturePropagation(768, [256, 256])
        self.fp3 = PointNetFeaturePropagation(384, [256, 256])
        self.fp2 = PointNetFeaturePropagation(320, [256, 128])
        self.fp1 = PointNetFeaturePropagation(128, [128, 128, 128])
        self.conv1 = nn.Conv1d(128, 128, 1)
        self.bn1 = nn.BatchNorm1d(128)
        self.drop1 = nn.Dropout(0.5)
        self.conv2 = nn.Conv1d(128, num_classes, 1)

    def forward(self, xyz):
        l0_points = xyz
        l0_xyz = xyz[:,:3,:]

        l1_xyz, l1_points = self.sa1(l0_xyz, l0_points)
        l2_xyz, l2_points = self.sa2(l1_xyz, l1_points)
        l3_xyz, l3_points = self.sa3(l2_xyz, l2_points)
        l4_xyz, l4_points = self.sa4(l3_xyz, l3_points)

        l3_points = self.fp4(l3_xyz, l4_xyz, l3_points, l4_points)
        l2_points = self.fp3(l2_xyz, l3_xyz, l2_points, l3_points)
        l1_points = self.fp2(l1_xyz, l2_xyz, l1_points, l2_points)
        l0_points = self.fp1(l0_xyz, l1_xyz, None, l1_points)

        x = self.drop1(F.relu(self.bn1(self.conv1(l0_points))))
        x = self.conv2(x)
        x = F.log_softmax(x, dim=1)
        x = x.permute(0, 2, 1)
        return x, l4_points


class get_loss(nn.Module):
    def __init__(self):
        super(get_loss, self).__init__()
        self.gamma=2
    def forward(self, pred, target, trans_feat, weight):#pred: 模型预测的输出   target: 真实的标签或数据,用于计算损失
        total_loss = F.nll_loss(pred, target, weight=weight)

        return total_loss


if __name__ == '__main__':
    import  torch
    model = get_model(13)
    xyz = torch.rand(6, 9, 2048)
    (model(xyz))

你可能感兴趣的:(Pointnet++改进专栏,深度学习,人工智能,3d,python)