《Dual Attention Network for Scene Segmentation》
文章:https://arxiv.org/pdf/1812.03904.pdf
代碼:https://github.com/junfu1115/DANet/
存在的問題
爲提高像素級識別特徵表示的辨別能力:①利用多尺度上下文融合,結合不同的膨脹卷積和池化操作,來聚合多尺度上下文。②使用分解結構增大卷積核尺寸或在網絡頂部引入有效的編碼層,來捕獲更豐富的全局信息。③編碼器-解碼器結構來融合中級和高級語義特徵。④使用RNN捕捉長程依賴關係,從而提高分割精度。
而①②③中,雖然上下文融合有助於捕獲不同比例的對象,但卻無法利用全局視圖中對象之間的關係。④中,使用RNN隱含地捕捉全局關係,但其有效性很大程度上依賴於長期記憶的學習結果。
本文提出的解決方案
爲解決上述問題,本文提出了雙注意力網絡(DANet),基於自注意力機制來分別捕獲空間維度和通道維度中的特徵依賴關係。具體而言,本文在dilated FCN上附加了2種注意力模塊,分別對空間維度和通道維度上的語義依賴關係進行建模。
- 其中position attention module使用自注意力機制捕獲特徵圖在任意兩個位置之間的空間依賴關係,通過加權求和對所有位置的特徵進行聚合更新,權重是由對應兩個位置的特徵相似性決定的。
- Channel attention module使用自注意力機制來捕獲任意兩個通道圖之間的通道依賴關係,並使用所有通道圖的加權,和更新每個通道圖。
- 這兩個注意力模塊的輸出進行融合,進一步增強了特徵表示。
因此本文方法更有效更靈活:①選擇性地聚合不起眼物體的相似特徵,突出它們的特徵表示,避免了突出物體的影響。②從全局的角度自適應地集成任何尺度上地相似特徵。③明確地考慮空間和通道的關係,這樣場景理解可以利用長期依賴關係。
DANet
因爲卷積操作產生的是局部感受野,導致相同標籤的像素對應特徵可能不同,這種差異會進而導致類內的不一致性,影響識別的準確率。所以本文提出:在特徵,之間使用注意力機制建立關聯以獲取全局上下文信息。
(1)Position Attention Module
觀察:傳統FCNs生成的特徵會導致對物體的錯誤分類。
解決:引入位置注意模塊在局部特徵上建立豐富的上下文關係,將更廣泛的上下文信息編碼爲局部特徵,進而增強他們的表示能力。
Spatial attention map的計算:,sji度量第i個位置對第j個位置的影響,也就是第i個位置和第j個位置之間的關聯程度/相關性,越大越相似。
Output的計算:,α表示尺度係數,初始化爲0,並逐漸地學習分配到更大的權重。每個位置的結果特徵E,是所有位置和原始位置的加權和。因此它具有全局上下文視圖,並能根據空間注意力圖有選擇地聚合上下文。
(2)Channel Attention Module
觀察:每個high level特徵的通道圖都可以看作是一個特定於類的響應,通過挖掘通道圖之間的相互依賴關係,可以突出相互依賴的特徵圖,提高特定語義的特徵表示。
解決:建立一個通道注意力模塊來顯式地建模通道之間的依賴關係。
Channel attention map的計算:,xji度量第i個通道對第j個通道的影響。
Output的計算:,β表示尺度係數,初始化爲0,並逐漸地學習分配到更大的權重。每個通道的結果特徵E,是所有通道特徵和原始特徵的加權和。實現了對特徵圖之間的長程語義依賴關係建模,有助於提高特徵的辨別性。
(3)Attention Module Embedding with Networks
爲了充分利用長程上下文信息,所以將這2個注意力模塊的特徵進行了聚合。即通過卷積層對兩個注意力模塊的輸出進行轉換,並執行一個element-wise的求和來實現特徵融合。最後接一個卷積得到最後的預測特徵圖。
該注意力模塊很簡單,可以直接插入到現在的FCN中。而且它們不會增加太多參數,還能有效地增強特徵表示。
實驗分析
實驗表明:①位置注意模塊能夠捕捉到清晰的語義相似性和長程關係。②當通道注意模塊增強後,特定語義的響應是明顯的。③DANet使用注意力模塊,可以更有效地捕獲全局依賴關係和長程上下文信息,在場景分割中學到更好的特徵表示。
總結
優點:DANet使用2個注意力模塊,可以更有效地捕獲全局依賴關係和長程上下文信息,在場景分割中學到更好的特徵表示,使得分割結果更加準確。
缺點:矩陣計算使得算法的計算複雜度較高?模型的魯棒性?缺少和DeepLab v3+的比較(可能沒有DeepLab v3+的效果好)?
【猜測】selectively和adaptively應該體現在:公式(2)和公式(4)中,S和X矩陣表示的權重上,softmax後大的值作用更大,對乘積的貢獻也就越大。
代碼閱讀
兩個注意力模塊的代碼在:DANet-master\encoding\nn\attention.py
###########################################################################
# Created by: CASIA IVA
# Email: [email protected]
# Copyright (c) 2018
###########################################################################
import numpy as np
import torch
import math
from torch.nn import Module, Sequential, Conv2d, ReLU,AdaptiveMaxPool2d, AdaptiveAvgPool2d, \
NLLLoss, BCELoss, CrossEntropyLoss, AvgPool2d, MaxPool2d, Parameter, Linear, Sigmoid, Softmax, Dropout, Embedding
from torch.nn import functional as F
from torch.autograd import Variable
torch_ver = torch.__version__[:3]
__all__ = ['PAM_Module', 'CAM_Module']
class PAM_Module(Module):
""" Position attention module"""
#Ref from SAGAN
def __init__(self, in_dim):
super(PAM_Module, self).__init__()
self.chanel_in = in_dim
# 先經過3個卷積層生成3個新特徵圖B C D (尺寸不變)
self.query_conv = Conv2d(in_channels=in_dim, out_channels=in_dim//8, kernel_size=1)
self.key_conv = Conv2d(in_channels=in_dim, out_channels=in_dim//8, kernel_size=1)
self.value_conv = Conv2d(in_channels=in_dim, out_channels=in_dim, kernel_size=1)
self.gamma = Parameter(torch.zeros(1)) # α尺度係數初始化爲0,並逐漸地學習分配到更大的權重
self.softmax = Softmax(dim=-1) # 對每一行進行softmax
def forward(self, x):
"""
inputs :
x : input feature maps( B × C × H × W)
returns :
out : attention value + input feature
attention: B × (H×W) × (H×W)
"""
m_batchsize, C, height, width = x.size()
# B -> (N,C,HW) -> (N,HW,C)
proj_query = self.query_conv(x).view(m_batchsize, -1, width*height).permute(0, 2, 1)
# C -> (N,C,HW)
proj_key = self.key_conv(x).view(m_batchsize, -1, width*height)
# BC,空間注意圖 -> (N,HW,HW)
energy = torch.bmm(proj_query, proj_key)
# S = softmax(BC) -> (N,HW,HW)
attention = self.softmax(energy)
# D -> (N,C,HW)
proj_value = self.value_conv(x).view(m_batchsize, -1, width*height)
# DS -> (N,C,HW)
out = torch.bmm(proj_value, attention.permute(0, 2, 1)) # torch.bmm表示批次矩陣乘法
# output -> (N,C,H,W)
out = out.view(m_batchsize, C, height, width)
out = self.gamma*out + x
return out
class CAM_Module(Module):
""" Channel attention module"""
def __init__(self, in_dim):
super(CAM_Module, self).__init__()
self.chanel_in = in_dim
self.gamma = Parameter(torch.zeros(1)) # β尺度係數初始化爲0,並逐漸地學習分配到更大的權重
self.softmax = Softmax(dim=-1) # 對每一行進行softmax
def forward(self,x):
"""
inputs :
x : input feature maps( B × C × H × W)
returns :
out : attention value + input feature
attention: B × C × C
"""
m_batchsize, C, height, width = x.size()
# A -> (N,C,HW)
proj_query = x.view(m_batchsize, C, -1)
# A -> (N,HW,C)
proj_key = x.view(m_batchsize, C, -1).permute(0, 2, 1)
# 矩陣乘積,通道注意圖:X -> (N,C,C)
energy = torch.bmm(proj_query, proj_key)
# 這裏實現了softmax用最後一維的最大值減去了原始數據,獲得了一個不是太大的值
# 沿着最後一維的C選擇最大值,keepdim保證輸出和輸入形狀一致,除了指定的dim維度大小爲1
# expand_as表示以複製的形式擴展到energy的尺寸
energy_new = torch.max(energy, -1, keepdim=True)[0].expand_as(energy)-energy
attention = self.softmax(energy_new)
# A -> (N,C,HW)
proj_value = x.view(m_batchsize, C, -1)
# XA -> (N,C,HW)
out = torch.bmm(attention, proj_value)
# output -> (N,C,H,W)
out = out.view(m_batchsize, C, height, width)
out = self.gamma*out + x
return out
'''
if __name__ == '__main__':
module = CAM_Module()
in_data = torch.randint(0, 255, (2, 3, 7, 7), dtype=torch.float32)
print(module(in_data).size())
'''
模塊融合的代碼在:DANet-master\encoding\models\danet.py
class DANetHead(nn.Module):
def __init__(self, in_channels, out_channels, norm_layer):
super(DANetHead, self).__init__()
inter_channels = in_channels // 4 # in_channels=2018,通道數縮減爲512
self.conv5a = nn.Sequential(nn.Conv2d(in_channels, inter_channels, 3, padding=1, bias=False), norm_layer(inter_channels), nn.ReLU())
self.conv5c = nn.Sequential(nn.Conv2d(in_channels, inter_channels, 3, padding=1, bias=False), norm_layer(inter_channels), nn.ReLU())
self.sa = PAM_Module(inter_channels) # 空間注意力模塊
self.sc = CAM_Module(inter_channels) # 通道注意力模塊
self.conv51 = nn.Sequential(nn.Conv2d(inter_channels, inter_channels, 3, padding=1, bias=False), norm_layer(inter_channels), nn.ReLU())
self.conv52 = nn.Sequential(nn.Conv2d(inter_channels, inter_channels, 3, padding=1, bias=False), norm_layer(inter_channels), nn.ReLU())
# nn.Dropout2d(p,inplace):p表示將元素置0的概率;inplace若設置爲True,會在原地執行操作。
self.conv6 = nn.Sequential(nn.Dropout2d(0.1, False), nn.Conv2d(512, out_channels, 1)) # 輸出通道數爲類別的數目
self.conv7 = nn.Sequential(nn.Dropout2d(0.1, False), nn.Conv2d(512, out_channels, 1))
self.conv8 = nn.Sequential(nn.Dropout2d(0.1, False), nn.Conv2d(512, out_channels, 1))
def forward(self, x):
# 經過一個1×1卷積降維後,再送入空間注意力模塊
feat1 = self.conv5a(x)
sa_feat = self.sa(feat1)
# 先經過一個卷積後,再使用有dropout的1×1卷積輸出指定的通道數
sa_conv = self.conv51(sa_feat)
sa_output = self.conv6(sa_conv)
# 經過一個1×1卷積降維後,再送入通道注意力模塊
feat2 = self.conv5c(x)
sc_feat = self.sc(feat2)
# 先經過一個卷積後,再使用有dropout的1×1卷積輸出指定的通道數
sc_conv = self.conv52(sc_feat)
sc_output = self.conv7(sc_conv)
feat_sum = sa_conv+sc_conv # 兩個注意力模塊結果相加
sasc_output = self.conv8(feat_sum) # 最後再送入1個有dropout的1×1卷積中
output = [sasc_output]
output.append(sa_output)
output.append(sc_output)
return tuple(output) # 輸出模塊融合後的結果,以及兩個模塊各自的結果
補充
這一段來自https://www.cnblogs.com/ziytong/p/10685860.html
文中的注意力模塊中的位置注意力模塊, 基本上與何凱明的<X. Wang, R. Girshick, A. Gupta, and K. He. Non-Local Neural Networks. In CVPR , 2018>提出的Non-Local結構是一致的。
而該自注意力的思路主要來源於google的一份工作(Attention Is All You Need): https://arxiv.org/abs/1706.03762
在Goodfellow的這篇文章中使用了 <H. Zhang, I. J. Goodfellow, D. N. Metaxas, and A. Odena. Self-attention generative adversarial networks. CoRR, abs/1805.08318, 2018> 類似的結構.https://arxiv.org/abs/1805.08318
其他參考來源
:
https://blog.csdn.net/mieleizhi0522/article/details/83111183
https://zhuanlan.zhihu.com/p/48056789