简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!
2.本篇文章对Pointnet++特征提取模块进行改进,提升性能。
3.专栏持续更新,紧随最新的研究内容。
目录
1.步骤一
2.步骤二
3.步骤三
论文介绍:
摘要。我们提出了卷积块注意模块(CBAM),这是一种简单而有效的前馈卷积神经网络注意模块。给定一个中间特征映射,我们的模块沿着两个独立的维度依次推断注意力映射,通道和空间,然后将注意力映射乘以输入特征映射以进行自适应特征细化。因为CBAM是一个轻量级的通用模块,它可以无缝地集成到任何CNN架构中,开销可以忽略不计,并且可以与基础CNN一起进行端到端训练。我们通过在ImageNet-1K、MS COCO检测和VOC 2007检测数据集上进行大量实验来验证我们的CBAM。
我们的实验表明,各种模型在分类和检测性能上都有一致的提高,证明了CBAM的广泛适用性。代码和模型将是公开的。
新建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
在models/pointnet2_utils.py文件中加入如下代码:
PointNetSetAbstraction结构图:
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
#################
在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))