如何將知識分離出來?
作者在論文中argue到,CNN在設計的過程中有一個固有的無效性,因為他們會將color,shape和紋理信息一起處理(感覺可以找個時間介紹一些,圖像中的color,shape或者texture信息對於圖像的特征提取有哪些幫助作用)。但是實際上這些不同的信息,比如color或者shape,texture對於識別來說的話,應該是包含不同的數量的信息的。作者舉了一個例子,一個人剛開始看一個物體的時候,需要看到完整地,細節地物體的邊界,從而能夠得到具有辨別性的shape編碼。但是color和texture就相對包含一些low-level的信息,意思是,對於物體的識別就沒有輪廓那么重要。這種思路實際上可以解釋為什么resnet需要residual skip來提升網絡的性能,或者是dense connection。以你為,通過添加這種additional connectivity能夠幫助不同類型的信息通過不同scale的深度進行融合流通(個人感覺這個說服力對於解釋為什么resnet work比較有說服力)。
這篇文章做了啥?
作者說到,在這篇文章中,他們提出了一個新的,two-stream CNN,能夠顯式的講shape information獨立成一個processing branch。兩個stream,分別是傳統cnn的stream,另外一個是shape stream,能夠並行的處理信息。除了非常頂層的layer,作者說不允許兩個stream信息的融合。
作者說到這篇文章的主要核心就是用一個門來控制兩路信息的交互。具體來講就是,作者利用傳統cnn stream的high-level的information來denoise前幾層shape stream的activations。這么一操作,shape stream能夠有效的處理相關的信息。而且只使用比較淺的網絡。為了能夠使得shape information,作者在shape stream上添加了語義邊界loss。我們更進一步的利用了一個新的loss function,來使得segmentation result和gt進行對齊。
並且作者還說道,他們的GSCNN是plug-and-play的,可以用在任何cnn上。作者做了大量的實驗,比deep lab-v3的結果在miou指標上高了1.5個百分點,在f-boundary指標上高了4%個百分點。而且作者說他們的實驗結果在一些很小的物體上的表現性能會更好,比如電線桿,交通標識或者交通燈等。
相關工作的介紹
語義分割進展
作者貌似說文獻6已經有人用邊界信息來refine實驗結果,但是和他們不一樣的是,作者是inject the learned boundary information到cnn的中間層,而不是最終的結果。作者還指出,之前文獻42也用了two stream network,但是他們是恢復由pooling降低的高分辨率的feature。文獻15,35,48提出了用於學習結構信息pixel級別的仿射信息,他們主要用來學習一個特定信息傳播模塊,作者提出的是學習高質量的shape information。
multitask learning
有一些工作是用來提出互補任務的學習。作者的目標並不是訓練一個多任務的網絡,而是能夠通過利用分割和邊界對偶性質來加強結構化信息的表示。文獻12,4能夠同時學習分割和邊界檢測的結果。31和40能夠學習intermediate的表示來輔助分割結果。但是這些工作對於邊界的約束只在loss function上,作者直接將邊界信息注入到網絡的中間層去,並且提出了一個對偶任務loss來同時refine 分割的mask和邊界預測的結果。
gated convolution
最近的在語言模型上的研究表明卷積上的gating mechanism是有效果的,比如文獻14提出了取代循環網絡中的循環connection的gated temporal convolution。文獻53提出了一個soft-gating的圖像不全的機制。文獻46提出了gated pixelcnn來做圖像生成。這里作者用gated covolution opreator來做語義分割以及控制兩個stream信息之間的流動。
Gated Shape CNN
相當於是作者把shape這一個分支獨立出來,因為考慮到shape對於分割而言是非常具有意義的,其實對於很多任務而言都是具有意義的,比如雙目深度估計或者是單目深度估計,都是比較有意義的。作者整體的pipline如下
作者整體的pipline如上圖,就是用backbone接出來幾路,用於做gate相關的東西,作者代碼里面寫的很詳細
我將作者代碼的detail繪制了一個圖,如下
最終網絡的輸出是一個edge map,一個是分割的結果。
關於邊緣圖,相當於作者是用一個branch,單獨輸出最終的邊緣圖,這種邊緣圖可以指導網絡分割的結果。
隨便可視化了一張圖片,原圖如下圖所示,
根據分割圖獲取的邊緣圖的ground truth如下圖所示
當然,下圖是作者對於上圖的gt增強的的結果。
作者將輸出的edge_out和candy算子得到的結果一起concate到aspp中,關於aspp,只deeplab-v3提及的一個模塊。下面是ASPP代碼的解析
ASPP代碼模塊解析
看了一下ASPP作者的代碼,當然在這里作者融合了edge的信息,代碼塊如下
class _AtrousSpatialPyramidPoolingModule(nn.Module):
'''
operations performed:
1x1 x depth
3x3 x depth dilation 6
3x3 x depth dilation 12
3x3 x depth dilation 18
image pooling
concatenate all together
Final 1x1 conv
'''
def __init__(self, in_dim, reduction_dim=256, output_stride=16, rates=[6, 12, 18]):
super(_AtrousSpatialPyramidPoolingModule, self).__init__()
# Check if we are using distributed BN and use the nn from encoding.nn
# library rather than using standard pytorch.nn
if output_stride == 8:
rates = [2 * r for r in rates]
elif output_stride == 16:
pass
else:
raise 'output stride of {} not supported'.format(output_stride)
self.features = []
# 1x1
self.features.append(
nn.Sequential(nn.Conv2d(in_dim, reduction_dim, kernel_size=1, bias=False),
Norm2d(reduction_dim), nn.ReLU(inplace=True)))
# other rates
for r in rates:
self.features.append(nn.Sequential(
nn.Conv2d(in_dim, reduction_dim, kernel_size=3,
dilation=r, padding=r, bias=False),
Norm2d(reduction_dim),
nn.ReLU(inplace=True)
))
self.features = torch.nn.ModuleList(self.features) # 經過計算,發現四者的size是一樣的
# img level features
self.img_pooling = nn.AdaptiveAvgPool2d(1)
self.img_conv = nn.Sequential(
nn.Conv2d(in_dim, reduction_dim, kernel_size=1, bias=False),
Norm2d(reduction_dim), nn.ReLU(inplace=True))
self.edge_conv = nn.Sequential(
nn.Conv2d(1, reduction_dim, kernel_size=1, bias=False),
Norm2d(reduction_dim), nn.ReLU(inplace=True))
def forward(self, x, edge):
x_size = x.size()
img_features = self.img_pooling(x)
img_features = self.img_conv(img_features)
img_features = F.interpolate(img_features, x_size[2:],
mode='bilinear',align_corners=True)
out = img_features
edge_features = F.interpolate(edge, x_size[2:],
mode='bilinear',align_corners=True)
edge_features = self.edge_conv(edge_features)
out = torch.cat((out, edge_features), 1)
for f in self.features:
y = f(x)
out = torch.cat((out, y), 1) # 二者concate在一起經過aspp
return out
ASPP是另外一篇文章提出來的,這里作者只是,將backnone和最后輸出的edge信息在一起輸入了aspp。aspp實際上是利用四路不同的空洞卷積並行處理輸入的feature map,不同的空洞卷積的dilation不同,感受野也不一樣,這對於提取不同尺度的信息。從上面的這個module可以看出,作者最后將上采樣得到的edge_features和out concate到一起,然后依次經過ASPP模塊,一個很自然的問題是,ASPP四路並行,雖然用不同的dilation和padding,盡管strid一樣,如何能夠保證輸出的feature map的尺寸一樣,使之能夠concate到一起?
根據輸出的feature map的尺寸計算公式
從上面這個公式可以看出,如果要保證\(W_{output}\)是一樣的話,\(s\)固定,則需要保證\(W_{in}+2*pading-dilation*(k-1)-1\)對於不同的dilation是一樣的,這就是ASPP實現的關鍵。因為\(W_{in}\)是固定的,所以就需要保證\(2*pading-dilation*(k-1)\)對於不同的dilation是一樣的,顯然,仔細觀察上面的self.features的卷積參數,作者第一個的卷積的padding默認是0,kernel size默認是1,所以20-1(1-1)=0。對於剩下的卷積里面的參數,padding都是為dilation,k為3,所以2*dilation-dilation(3-1)=0,可以看出,這樣就能夠保證不同dilation輸出的feature map的尺寸是一樣的,這樣后續的concate就會方便很多。
loss
作者定義了四個loss,對於分割或者是edge檢測的結果,分別定義了cross entropy loss。然后還定義了對偶loss,關於對偶loss,作者的兩個對偶loss分別定義如下
另外一個對偶loss,作者說探究了邊界以及分割的結果,也並沒有看太懂,但是一個值得學習的點就是作者用一個可微的函數替代了argmax操作,感覺這個還是挺值得學習的。
實驗結果
作者在cityspace數據集合上達到了比較好的實驗結果。並且作者做了ablation study,說不加gate或者不加candy得到的結果,結果時好時壞。
作者展示了一下,shape stream最終輸出的結果
以及作者展示了一下誤差圖
可以看出,deep-lab v3的誤差是比較小的。
btw,作者也可視化了一下三個gate的實驗結果,
越往后越具有高層次的信息。
根據分割的結果,作者能夠從分割的結果得到原圖的邊緣圖,可以看出,分割得到的圖的邊緣還是挺准的
總結一下
感覺這種inject這種low-level相關的信息還是挺值得學習的,就是單獨出來一個stream,這種stream與backbone有某種關聯,這樣可以強化分割得到的結果。這種low指導high level的方向感覺在很多任務中都會用到,也可能是未來的一個研究的方向。