实时语义分割之FarSeeNet(2020)模型解析代码复现及滑坡检测实战
论文《FarSee-Net: Real-Time Semantic Segmentation by Efficient Multi-scale Context Aggregation and Feature Space Super-resolution》原理解析及代码复现。
用自己的数据训练yolov11目标检测
实时语义分割之BiSeNetv2(2020)结构原理解析及建筑物提取实践
实时语义分割之Deep Dual-resolution Networks(DDRNet2021)原理解析及建筑物提取实践
SegFormer 模型解析:结合 Transformer 的高效分割框架原理及滑坡语义分割实战
文章目录
- 概要
- 理论知识
- 整体架构流程
- 前端网络(frontend_network):基于ResNet的特征提取
- 后端网络(backend_network):FASPP(基于空间金字塔池化的特征聚合)
- 模型实践
- 训练数据准备
- 模型训练
- 影像测试
- 结果示例
概要
提示: (1)论文地址:https://arxiv.org/abs/2003.03913 (2)本人打包好数据可运行代码:FarSeeNet.zip 链接: https://pan.baidu.com/s/1febtaxNsKVVURu03IIacYA?pwd=1pve 提取码: 1pve
FarSee-Net包括前端、后端两个部分,前端采用ResNet基础的骨干网络确保了鲁棒的特征提取;而后端网络的FASPP模块则通过有效结合高低层特征进行精准分割。网络围绕两个关键思想构建:
1、高效的多尺度上下文聚合:模型整合了多层次的上下文信息,提高了分割精度,尤其是在物体大小差异较大的复杂场景中。
2、特征空间超分辨率:通过使用超分辨率技术,FarSee-Net增强了特征表示,使得在不显著增加计算开销的情况下,能够实现更高像素级别的精度。
理论知识
整体架构流程
class FarSeeNet(nn.Module):
def __init__(self, num_class=1, n_channel=3, backbone_type='resnet18', act_type='relu'):
super().__init__()
if 'resnet' in backbone_type:
self.frontend_network = ResNet(backbone_type)
high_channels = 512 if backbone_type in ['resnet18', 'resnet34'] else 2048
low_channels = 256 if backbone_type in ['resnet18', 'resnet34'] else 1024
else:
raise NotImplementedError()
self.backend_network = FASPP(high_channels, low_channels, num_class, act_type)
def forward(self, x):
size = x.size()[2:]
_, _, x_low, x_high = self.frontend_network(x)
x = self.backend_network(x_high, x_low)
x = F.interpolate(x, size, mode='bilinear', align_corners=True)
return x
FarSeeNet 由两部分组成:
- frontend_network(主干网络):基于ResNet的特征提取。
- backend_network(解码头):基于FASPP模块通过有效结合高低层特征进行精准分割。
前端网络(frontend_network):基于ResNet的特征提取
self.frontend_network = ResNet(backbone_type)
high_channels = 512 if backbone_type in ['resnet18', 'resnet34'] else 2048
low_channels = 256 if backbone_type in ['resnet18', 'resnet34'] else 1024
FarSee-Net的前端网络采用了ResNet架构。ResNet是一种著名的深度卷积网络,通过残差连接克服了梯度消失问题。前端网络输出低层和高层特征(分别为x_low和x_high),并将其传递给后端网络进一步处理。
- 高层特征(深层):resnet18和resnet34为512个通道,较深的ResNet变体为2048个通道。
- 低层特征(浅层):resnet18和resnet34为256个通道,较深的变体为1024个通道。
后端网络(backend_network):FASPP(基于空间金字塔池化的特征聚合)
代码逻辑(简化示意)
class FASPP(nn.Module):
def __init__(self, high_channels, low_channels, num_class, act_type, dilations=[6,12,18], hid_channels=256):
super().__init__()
# 高层特征处理
self.conv_high = nn.ModuleList([ConvBNAct(high_channels, hid_channels, 1, act_type)])
for dt in dilations:
self.conv_high.append(nn.Sequential(ConvBNAct(high_channels, hid_channels, 1, act_type),
DWConvBNAct(hid_channels, hid_channels, 3, dilation=dt, act_type)))
self.sub_pixel_high = nn.Sequential(conv1x1(hid_channels*4, hid_channels*2*(2**2)), nn.PixelShuffle(2))
# 低层特征处理
self.conv_low_init = ConvBNAct(low_channels, 48, 1, act_type)
self.conv_low = nn.ModuleList([ConvBNAct(hid_channels*2+48, hid_channels//2, 1, act_type)])
for dt in dilations[:-1]:
self.conv_low.append(nn.Sequential(ConvBNAct(hid_channels*2+48, hid_channels//2, 1, act_type),
DWConvBNAct(hid_channels//2, hid_channels//2, 3, dilation=dt, act_type)))
self.conv_low_last = nn.Sequential(ConvBNAct(hid_channels//2*3, hid_channels*2, 1, act_type),
ConvBNAct(hid_channels*2, hid_channels*2, act_type))
self.sub_pixel_low = nn.Sequential(conv1x1(hid_channels*2, num_class*(4**2)), nn.PixelShuffle(4))
def forward(self, x_high, x_low):
# 处理高层特征
high_feats = [conv_high(x_high) for conv_high in self.conv_high]
x = torch.cat(high_feats, dim=1)
x = self.sub_pixel_high(x)
# 处理低层特征
x_low = self.conv_low_init(x_low)
x = torch.cat([x, x_low], dim=1)
low_feats = [conv_low(x) for conv_low in self.conv_low]
x = torch.cat(low_feats, dim=1)
x = self.conv_low_last(x)
x = self.sub_pixel_low(x)
return x
1、高层特征处理(High-level Features):
- self.conv_high:首先,使用一个 1x1 卷积层提取高层特征。然后,对于每个膨胀率 dilations 中的值,使用一个 1x1 卷积和一个带有不同膨胀率的深度可分离卷积(DWConv)来提取不同尺度的高层特征。
- self.sub_pixel_high:将所有提取的高层特征合并,并通过一个 1x1 卷积和 PixelShuffle 层进行上采样,提升图像分辨率。
2、低层特征处理(Low-level Features):
- self.conv_low_init:对低层输入特征图应用一个 1x1 卷积,初始化低层特征。
- self.conv_low:随后,低层特征与高层特征拼接,并使用多个 1x1 卷积和深度可分离卷积来进一步处理低层特征。
- self.conv_low_last:经过多个卷积处理后,最终的低层特征通过一个 1x1 卷积和卷积层进一步调整维度。
- self.sub_pixel_low:类似于高层特征的处理,将低层特征通过一个 1x1 卷积和 PixelShuffle 层进行上采样。
3、前向传播:
- 高层特征处理:通过 self.conv_high 进行一系列卷积操作,并将结果合并,最终通过 self.sub_pixel_high 层进行上采样。
- 低层特征处理:低层输入通过 self.conv_low_init 进行初始化,然后与高层特征拼接,经过多层卷积处理后,最终通过 self.sub_pixel_low 层进行上采样,生成最终输出。
模型实践
训练数据准备
提示:云盘代码已内置少量山体滑坡数据集
训练数据分为原始影像和标签(二值化,0和255),均位于Sample文件夹内,数据相对路径为:
Sample\landslide\train\ IMG_T1
------------------\ IMG_LABEL
-------------\val \ IMG_T1
------------------\IMG_LABEL
本示例中影像尺寸不一,在dp0_train.py文件parser参数项,crop_height 、crop_width设置为256、256,训练数据在CDDataset_Seg定义中进行了resize,模型训练中均为256*256。
模型训练
运行dp0_train.py,模型开始训练,核心参数包括:
parser参数 | 说明 |
---|---|
num_epochs | 训练批次 |
learning_rate | 初始学习率 |
batch_size | 单次样本数量 |
dataset | 数据集名字 |
crop_height | 训练时影像重采样尺度 |
数据结构CDDataset_Seg定义在utils文件夹dataset.py中,注意读取后进行了数据增强(随机翻转),灰度化,尺寸调整,标签归一化、 one-hot 编码,以及维度和数据类型的转换,最终得到适用于 PyTorch 模型训练的张量。
# 读取训练图片和标签图片
image_t1 = cv2.imread(image_t1_path,-1)
#image_t2 = cv2.imread(image_t2_path)
label = cv2.imread(label_path)
# 随机进行数据增强,为2时不做处理
if self.data_augment:
flipCode = random.choice([-1, 0, 1, 2])
if flipCode != 2:
# image_t1 = normalized(image_t1, 'tif')
image_t1 = self.augment(image_t1, flipCode)
#image_t2 = self.augment(image_t2, flipCode)
label = self.augment(label, flipCode)
label = cv2.cvtColor(label, cv2.COLOR_BGR2GRAY)
image_t1 = cv2.resize(image_t1, (self.img_h, self.img_w))
#image_t2 = cv2.resize(image_t2, (Config.img_h, Config.img_w))
label = cv2.resize(label, (self.img_h, self.img_w))
label = label/255
label = label.astype('uint8')
label = onehot(label, 2)
label = label.transpose(2, 0, 1)
label = torch.FloatTensor(label)
训练过程如下图所示,模型保存至checkpoints数据集同名文件夹内。
影像测试
运行dp0_AllPre.py,核心参数包括:
parser参数 | 说明 |
---|---|
Checkpointspath | 预训练模型位置名称 |
Dataset | 批量化预测数据文件夹 |
Outputpath | 输出数据文件夹 |
数据加载方式:
pre_dataset = CDDataset_Pre(data_path=pre_imgpath1,
transform=transforms.Compose([
transforms.ToTensor()]))
pre_dataloader = torch.utils.data.DataLoader(dataset=pre_dataset,
batch_size=1,
shuffle=False,
pin_memory=True,
num_workers=0)
需要注意,预测定义的数据结构CDDataset_Pre与训练时CDDataset_Seg有区别,此处将其resize回原始尺寸,后续使用自己的数据训练时注意调整。
结果示例