PIDNet是2023年發(fā)表在CVPR上的實(shí)時(shí)語(yǔ)義分割網(wǎng)絡(luò),在推理速度和準(zhǔn)確性之間實(shí)現(xiàn)了最佳平衡,其中該系列的PIDNet-S在Cityscapes 測(cè)試集上達(dá)到93.2 FPS + 78.6% mIOU。
論文和開(kāi)源代碼在這里。
解決的問(wèn)題:傳統(tǒng)雙分支網(wǎng)絡(luò)低層的細(xì)節(jié)信息和高層語(yǔ)義信息直接融合,會(huì)導(dǎo)致細(xì)節(jié)特征很容易被上下文信息淹沒(méi),即文中的overshoot。
思路:提出一種三分支網(wǎng)絡(luò)架構(gòu),分別解析細(xì)節(jié)、上下文和邊界信息,并設(shè)計(jì)邊界注意力引導(dǎo)融合模塊(Bag)融合三個(gè)分支的特征。
為了在推理速度和準(zhǔn)確度之間取得最佳平衡,研究人員投入了大量精力來(lái)重新設(shè)計(jì)網(wǎng)絡(luò)架構(gòu),可以概括為:輕量級(jí)編碼器和解碼器(卷積分解及分組卷積)、多尺度輸入以及雙分支網(wǎng)絡(luò)。具體而言,SwiftNet使用一個(gè)低分辨率輸入來(lái)獲得高級(jí)語(yǔ)義,使用另一個(gè)高分辨率輸入來(lái)為其輕量級(jí)解碼器提供足夠的細(xì)節(jié)。DFANet通過(guò)修改基于深度可分離卷積的Xception的架構(gòu),引入了一種輕量級(jí)主干,并縮減了輸入大小以提高推理速度。ShuffleSeg采用Shuffle Net作為其主干,它結(jié)合了通道混洗和分組卷積,以降低計(jì)算成本。然而,這些網(wǎng)絡(luò)中的大多數(shù)仍然是編碼器-解碼器架構(gòu)的形式,并且需要信息流通過(guò)深層編碼器,然后反向通過(guò)解碼器,給這些模型帶來(lái)了很大的延遲。此外,由于GPU上深度可分離卷積的優(yōu)化還不成熟,傳統(tǒng)卷積盡管具有更多FLOP和參數(shù),但速度更快。
在語(yǔ)義分割任務(wù)中,上下文依賴性可以通過(guò)大的感受野來(lái)提取,而精確的邊界和小范圍物體識(shí)別則依賴于空間細(xì)節(jié)信息。在雙分支網(wǎng)絡(luò)如DDRNet中,細(xì)節(jié)分支的輸出分辨率為上下文分支的8倍(BiSeNet中為4倍),它們的直接融合將不可避免地導(dǎo)致過(guò)沖現(xiàn)象(overshoot),即物體邊界很容易被其周?chē)南袼匮蜎](méi),小規(guī)模物體可能被相鄰的大物體淹沒(méi)。文中用下圖來(lái)解釋過(guò)沖現(xiàn)象:
PID控制器包含3個(gè)具有互補(bǔ)功能的組件:比例(P)控制器表示當(dāng)前誤差,積分(I)控制器累積先前誤差,微分(D)控制器預(yù)測(cè)未來(lái)誤差變化。在雙分支網(wǎng)絡(luò)中,上下文分支通過(guò)級(jí)聯(lián)卷積層或池化層(其實(shí)也就是下采樣操作)不斷聚合從局部到全局的語(yǔ)義信息,以解析像素之間的長(zhǎng)距離依賴關(guān)系,而細(xì)節(jié)分支特征圖維持高分辨率以保留單個(gè)像素的位置信息。因此細(xì)節(jié)分支和上下文分支可以被視為空間域中的比例和積分控制器,這解釋了分割任務(wù)中存在過(guò)沖問(wèn)題的原因。
PIDNet網(wǎng)絡(luò)結(jié)構(gòu)
PIDNet主要包含以下結(jié)構(gòu):
- 綠色的比例(P)分支解析并保存高分辨率特征圖中的細(xì)節(jié)信息;
- 藍(lán)色的積分(I)分支聚合上下文信息,以解析遠(yuǎn)程依賴關(guān)系;
- 灰色的導(dǎo)數(shù)(D)分支提取高頻特征以預(yù)測(cè)邊界區(qū)域。
PIDNet的骨干網(wǎng)絡(luò)采用級(jí)聯(lián)的殘差塊,也就是ResNet中的BasicBlock和Bottleneck,首先我們介紹P分支中的Pag模塊。
Pag:選擇性學(xué)習(xí)高級(jí)語(yǔ)義
在PIDNet中,I分支提供的豐富和準(zhǔn)確的語(yǔ)義信息對(duì)于P分支的細(xì)節(jié)解析至關(guān)重要,P分支包含相對(duì)較少的層和通道。因此,可以將I分支作為其他兩個(gè)分支的備份,使其能夠向它們提供所需的信息。
如上圖,先對(duì)I分支和P分支的輸入y和x分別做1*1卷積和BN,再將y上采樣(因?yàn)樘卣鲌Dy的大小為原圖的1/16,x為1/8),兩者相乘后將結(jié)果做逐通道求和,再經(jīng)過(guò)sigmoid得到結(jié)果σ,得到Pag的輸出為σ * y + (1 - σ) *x
代碼實(shí)現(xiàn):
class PagFM(nn.Module):
def __init__(self, in_channels, mid_channels, after_relu=False, with_channel=False, BatchNorm=nn.BatchNorm2d):
super(PagFM, self).__init__()
self.with_channel = with_channel
self.after_relu = after_relu
self.f_x = nn.Sequential(
nn.Conv2d(in_channels, mid_channels,
kernel_size=1, bias=False),
BatchNorm(mid_channels)
)
self.f_y = nn.Sequential(
nn.Conv2d(in_channels, mid_channels,
kernel_size=1, bias=False),
BatchNorm(mid_channels)
)
if with_channel:
self.up = nn.Sequential(
nn.Conv2d(mid_channels, in_channels,
kernel_size=1, bias=False),
BatchNorm(in_channels)
)
if after_relu:
self.relu = nn.ReLU(inplace=True)
def forward(self, x, y):
input_size = x.size()
if self.after_relu:
y = self.relu(y)
x = self.relu(x)
y_q = self.f_y(y)
y_q = F.interpolate(y_q, size=[input_size[2], input_size[3]],
mode='bilinear', align_corners=False)##上采樣到與x分辨率相同
x_k = self.f_x(x)
if self.with_channel:
sim_map = torch.sigmoid(self.up(x_k * y_q))
else:
sim_map = torch.sigmoid(torch.sum(x_k * y_q, dim=1).unsqueeze(1))
##dim=1:逐通道相加,假設(shè)x_k * y_q的shape為[4, 32, 32, 64],相加后shape變?yōu)閇4, 32, 64],再通過(guò)unsqueeze(1)升維為[4, 1, 32, 64]
y = F.interpolate(y, size=[input_size[2], input_size[3]],
mode='bilinear', align_corners=False)##上采樣到與x分辨率相同
x = (1-sim_map)*x + sim_map*y
return x
PAPPM:上下文特征快速聚合
作者改進(jìn)了DDRNet中用于聚合不同尺度上下文信息的DAPPM模塊,改變DAPPM中的連接并使其并行化,同時(shí)縮減了每個(gè)尺度的通道數(shù)以提高推理速度,提出一種新的上下文信息聚合模塊,稱為并行聚合PPM(PAPPM)。
代碼實(shí)現(xiàn):
class PAPPM(nn.Module):
def __init__(self, inplanes, branch_planes, outplanes, BatchNorm=nn.BatchNorm2d):
super(PAPPM, self).__init__()
bn_mom = 0.1
self.scale1 = nn.Sequential(nn.AvgPool2d(kernel_size=5, stride=2, padding=2),
BatchNorm(inplanes, momentum=bn_mom),
nn.ReLU(inplace=True),
nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
)
self.scale2 = nn.Sequential(nn.AvgPool2d(kernel_size=9, stride=4, padding=4),
BatchNorm(inplanes, momentum=bn_mom),
nn.ReLU(inplace=True),
nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
)
self.scale3 = nn.Sequential(nn.AvgPool2d(kernel_size=17, stride=8, padding=8),
BatchNorm(inplanes, momentum=bn_mom),
nn.ReLU(inplace=True),
nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
)
self.scale4 = nn.Sequential(nn.AdaptiveAvgPool2d((1, 1)),
BatchNorm(inplanes, momentum=bn_mom),
nn.ReLU(inplace=True),
nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
)##全局平均池化
self.scale0 = nn.Sequential(
BatchNorm(inplanes, momentum=bn_mom),
nn.ReLU(inplace=True),
nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
)##scale0不做池化
self.scale_process = nn.Sequential(
BatchNorm(branch_planes*4, momentum=bn_mom),
nn.ReLU(inplace=True),
nn.Conv2d(branch_planes*4, branch_planes*4, kernel_size=3, padding=1, groups=4, bias=False),
)
self.compression = nn.Sequential(
BatchNorm(branch_planes * 5, momentum=bn_mom),
nn.ReLU(inplace=True),
nn.Conv2d(branch_planes * 5, outplanes, kernel_size=1, bias=False),
)
self.shortcut = nn.Sequential(
BatchNorm(inplanes, momentum=bn_mom),
nn.ReLU(inplace=True),
nn.Conv2d(inplanes, outplanes, kernel_size=1, bias=False),
)
def forward(self, x):
width = x.shape[-1]
height = x.shape[-2]
scale_list = []
x_ = self.scale0(x)
scale_list.append(F.interpolate(self.scale1(x), size=[height, width],
mode='bilinear', align_corners=algc)+x_)
scale_list.append(F.interpolate(self.scale2(x), size=[height, width],
mode='bilinear', align_corners=algc)+x_)
scale_list.append(F.interpolate(self.scale3(x), size=[height, width],
mode='bilinear', align_corners=algc)+x_)
scale_list.append(F.interpolate(self.scale4(x), size=[height, width],
mode='bilinear', align_corners=algc)+x_)
scale_out = self.scale_process(torch.cat(scale_list, 1))
out = self.compression(torch.cat([x_,scale_out], 1)) + self.shortcut(x)
return out
Bag:平衡細(xì)節(jié)和上下文
作者設(shè)計(jì)了一個(gè)邊界注意力引導(dǎo)融合模塊(Bag)來(lái)融合三個(gè)分支的特征,以邊界信息指導(dǎo)細(xì)節(jié)分支§和上下文分支(I)的融合。上下文分支可以提供準(zhǔn)確的語(yǔ)義信息,但丟失了很多空間和幾何細(xì)節(jié),尤其是邊界區(qū)域和小物體,而細(xì)節(jié)分支更好的保留了空間細(xì)節(jié)信息,Bag模塊使得模型沿著邊界區(qū)域更加信任細(xì)節(jié)分支,在對(duì)象內(nèi)部區(qū)域則更信任上下文特征。
當(dāng)σ > 0.5時(shí),模型更信任細(xì)節(jié)特征,小于0.5時(shí)更信任上下文特征。
代碼實(shí)現(xiàn):
##Bag:
class Bag(nn.Module):
def __init__(self, in_channels, out_channels, BatchNorm=nn.BatchNorm2d):
super(Bag, self).__init__()
self.conv = nn.Sequential(
BatchNorm(in_channels),
nn.ReLU(inplace=True),
nn.Conv2d(in_channels, out_channels,
kernel_size=3, padding=1, bias=False)
)
def forward(self, p, i, d):
edge_att = torch.sigmoid(d)
return self.conv(edge_att*p + (1-edge_att)*i)
##Light-Bag:
class Light_Bag(nn.Module):
def __init__(self, in_channels, out_channels, BatchNorm=nn.BatchNorm2d):
super(Light_Bag, self).__init__()
self.conv_p = nn.Sequential(
nn.Conv2d(in_channels, out_channels,
kernel_size=1, bias=False),
BatchNorm(out_channels)
)
self.conv_i = nn.Sequential(
nn.Conv2d(in_channels, out_channels,
kernel_size=1, bias=False),
BatchNorm(out_channels)
)
def forward(self, p, i, d):
edge_att = torch.sigmoid(d)
p_add = self.conv_p((1-edge_att)*i + p)
i_add = self.conv_i(i + edge_att*p)
return p_add + i_add
分割頭S/B-Head
分割頭的結(jié)構(gòu)比較簡(jiǎn)單,主要作用是計(jì)算輔助損失,文中損失函數(shù)的定義如下:
Loss = λ?l? + λ?l? + λ?l? + λ?l?
其中l(wèi)?是額外的語(yǔ)義損失,l?是加權(quán)二元交叉熵?fù)p失,l?和l?是邊界感知CE loss,在文中設(shè)置的權(quán)重為λ? = 0.4,λ? = 20, λ? = λ? = 1
分割頭的代碼實(shí)現(xiàn):
class segmenthead(nn.Module):
def __init__(self, inplanes, interplanes, outplanes, scale_factor=None):
super(segmenthead, self).__init__()
self.bn1 = BatchNorm2d(inplanes, momentum=bn_mom)
self.conv1 = nn.Conv2d(inplanes, interplanes, kernel_size=3, padding=1, bias=False)
self.bn2 = BatchNorm2d(interplanes, momentum=bn_mom)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(interplanes, outplanes, kernel_size=1, padding=0, bias=True)
self.scale_factor = scale_factor
def forward(self, x):
x = self.conv1(self.relu(self.bn1(x)))
out = self.conv2(self.relu(self.bn2(x)))
if self.scale_factor is not None:
height = x.shape[-2] * self.scale_factor
width = x.shape[-1] * self.scale_factor
out = F.interpolate(out,
size=[height, width],
mode='bilinear', align_corners=algc)
return out
訓(xùn)練和結(jié)果
由于論文中的訓(xùn)練的代碼比較復(fù)雜,博主功力不夠看著比較費(fèi)勁,就沒(méi)有用論文中的訓(xùn)練策略,而是參考一位大佬的語(yǔ)義分割系列25-BiSeNetV2(pytorch實(shí)現(xiàn))和語(yǔ)義分割系列26-VIT+SETR——Transformer結(jié)構(gòu)如何在語(yǔ)義分割中大放異彩來(lái)進(jìn)行訓(xùn)練和推理可視化的。論文中采用的是yacs庫(kù)以yaml格式的文件來(lái)配置模型的各種參數(shù),包括訓(xùn)練和測(cè)試等等的參數(shù),為模型復(fù)現(xiàn)、調(diào)整參數(shù)提供了很多便利,大伙可以一起學(xué)習(xí)學(xué)習(xí),yacs傳送門(mén)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-402774.html
下面是博主用PIDNet在camvid數(shù)據(jù)集上的分割結(jié)果,訓(xùn)練時(shí)沒(méi)有使用輔助損失,所以分割精度也不是特別高。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-402774.html
到了這里,關(guān)于實(shí)時(shí)語(yǔ)義分割---PIDNet論文筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!