百度Apollo车道线分割比赛二(模型搭建)
百度Apollo车道线分割比赛一(数据处理)
百度Apollo车道线分割比赛三(模型训练)
本实现暂时实现两种常见的分割模型Unet,deeplabv3plus,最后在实现两个模型的融合(百度比赛主要看分割的miou不侧重模型的实时性),其中Unet的backbone采用resnet101,deeplabv3plus采用的backbone为Xcepthon65。backbone可以灵活选择,而且pytorch中有预训练好的权重,可以加载实现快速的训练。
一.Unet
Resnet系列参数如下图,在Unet模型分为 encoder block 和decoder block ,resnet去掉最后的全连接层作为Unet的backbone。另外decoder block上采样采用转置卷积,上采样止原图的1/4大小,最后计算loss时需要将最后输出双线性插值到原图大小再和label做loss。
resnet系列:
Unet结构:
处理文件为models.Unet_ResNet101.py,代码如下:
#coding:utf-8
#@author: Jiangnan He
#@date:2019.12.10 18:21
'''
resnet101-unet
1.上采样 还是采用 转置卷积
2.进行高低层特征融合时使用了torch.cat() 替换了 FCN 中的 "+"
3.resnet101 下采样1/32 所以进行了 5次下采样 本实现进行了 3次高低层特征融合 最后得到heatmap为原图的1/4
4.与label做loss 时直接将输出 heatmap 上采样到image尺寸
layer0 input
↓
layer1 conv3x3 ch=64 (1/2)
↓
layer2 reslayer(maxpool) ch=256 (1/4) --------------------------- 256+256---conv3 ch=256
↓ ↑upconv2
layer3 reslayer ch=512 (1/8) ------------------------------ 512+512---conv3 ch=512
↓ ↑upconv2
layer4 reslayer ch=1024 (1/16)---------------------------------- 1024+1024--conv3 ch=1024
↓ ↑upconv2
layer5 reslaeyr ch=2048 (1/32)-----------------------------------------
'''
import torch
import torch.nn as nn
from torch.autograd import Variable
from torchsummary import summary
from torch.nn import functional as F
class Block(nn.Module):
def __init__(self,in_ch,out_ch,ksize=3,stride=1,padding=1):
super(Block,self).__init__()
self.conv1=nn.Conv2d(in_ch,out_ch,kernel_size=ksize,stride=stride,padding=padding)
self.bn=nn.BatchNorm2d(out_ch)
self.relu=nn.ReLU(inplace=True)
def forward(self, x):
return self.relu(self.bn(self.conv1(x)))
def make_layers(in_channels, layer_list):
layers = []
for v in layer_list:
layers += [Block(in_channels, v)]
in_channels = v
return nn.Sequential(*layers)
#层
class Layer(nn.Module):
def __init__(self, in_channels, layer_list):
super(Layer, self).__init__()
self.layer = make_layers(in_channels, layer_list)
def forward(self, x):
out = self.layer(x)
return out
#残差块
class ResBlock(nn.Module):
def __init__(self,ch_list,downsample,Res):# ch_list=[in_ch,ch,out_ch]
super(ResBlock,self).__init__()
self.res=Res# 残差块 还是 瓶颈块的标志位
self.ds = downsample # 残差块时 是否下采样
#第一个1x1 卷积
self.firconv1x1=Block(ch_list[0],ch_list[1],1,1,0) #第一个1x1卷积 不下采样
self.firconv1x1d = Block(ch_list[0],ch_list[1], 1,2,0) #第一个1x1卷积 下采样
# 3x3卷积
self.conv3x3=Block(ch_list[1],ch_list[1],3,1,1)
#第二个1x1卷积
self.secconv1x1=Block(ch_list[1],ch_list[2],1,1,0)
#skip 卷积操作
self.resconv1x1d=Block(ch_list[0],ch_list[2],1,2,0) #skip下采样的1x1卷积
self.resconv1x1=Block(ch_list[0],ch_list[2],1,1,0) #skip下采样的1x1卷积
def forward(self,x):
if self.res==True:#此时为残差块
if self.ds==True:#skip有卷积操作需要下采样
residual=self.resconv1x1d(x)
f1=self.firconv1x1d(x)
else:#skip有卷积操作不需要下采样
residual = self.resconv1x1(x)
f1 = self.firconv1x1(x)
else:# 瓶颈块sikp无卷积操作
residual=x
f1 = self.firconv1x1(x)
f2=self.conv3x3(f1)
f3=self.secconv1x1(f2)
f3+=residual
return f3
#Res层
class ResLayer(nn.Module):
def __init__(self,ch_list1,ch_list2,downsample,numBotBlock):
super(ResLayer,self).__init__()
self.num = numBotBlock
self.resb=ResBlock(ch_list1,downsample,True)
self.botb=ResBlock(ch_list2,downsample,False)
self.BoB=self.make_layers()
def make_layers(self):
layers=[]
for i in range(self.num)