編輯:Happy
首發:AIWalker
Paper:https://arxiv.org/abs/2103.13634
Code:https://github.com/hellloxiaotian/ACNet
本文是哈工大左旺孟老師團隊在影像超分方面的最新工作,已被IEEE TSMC收錄。本文將ACNet中的非對稱卷積思想與LESRCNN進行組合,取得了更好的效能。由於作者尚未開源,故筆者進行了簡單的復現,復現還是挺容易的,哈哈。
Abstract
本文提出了一種非對稱CNN網路ACNet,它由非對稱模組(AB)、記憶力增強模組(MEB)、高頻特徵增強模組(HFFEB)構成。其中非對稱模組採用兩個一維卷積從水平和垂直方向對方框卷積進行增強,提升區域性顯著特徵的影響性因子;MEB則通過殘差連結方式對AB提取的低頻特徵進行融合,將低頻特徵變換到高頻特徵;HFFEB則融合低頻與高頻特徵得到更魯棒性的超分特徵。實驗結果表明:本文所提ACNet可以有效解決SISR、盲SISR以及噪聲不可知盲SISR等問題。
本文主要貢獻包含以下幾點:
- 提出一種多級特徵融合機制,通過對低頻特徵和高頻特徵融合,它可以很好的解決長期依賴問題,避免了上取樣機制導致效能退化問題。
- 提出一種非對稱架構增強區域性關鍵點的影響性以得到了魯邦的低頻特徵;
- 提出一種靈活的上取樣機制,它可以使得所提方案解決SISR等問題。
Method
上圖給出了本文所提非對稱卷積網路ACNet結構示意圖,它由三個模組構成:深度為17的AB模組、深度為1的MEB以及深度為5的HFFEB。
-
AB部分與2019年丁霄漢博士提出的ACNet中的ACB模組基本一致:訓練時三個分支:一個分支為$1\times 3$卷積,一個分支為$3\times 1$,一個分支為$3\times 3$卷積;推理時三個分支可以合併為單一卷積。
-
MEB則是對所有AB提取的特徵通過殘差學習方式進行融合,並將所提取的低頻特徵轉換為高頻特徵。注:該模組同時還進行了特徵上取樣。其中sub-conv部分的結構示意圖如下所示。這裡與EDSR中的上取樣基本一樣,略過咯。
-
HFFEB則對源自LR的低頻特徵以及上述融合高頻特徵進行融合處理得到更精確的超分特徵。這個地方是本文所提方法與之前常見網路不同之處:在更高解析度採用更多卷積進行增強以提升重建效能。
-
損失函式方面,本文選擇了MSE損失.
本文所設計的網路實在過於簡單,好像並沒有什麼需要深入介紹,看圖就夠了,或者看文末的參考實現亦可。
Experiments
在訓練方面,作者採用兩步法:(1) DIV2K的訓練集+驗證集同時參與訓練;(2) DIV2K的訓練集上進行微調。
直接上結果,下面兩個圖給出了Set5、Set14、B100、U100等資料的效能對比。
從上面四個表可以看到:在三個尺度上,本文所提ACNet均取得了還不錯的效能。
上表給出了不同噪聲強度下的影像超分效能對比。在計算複雜度、推理耗時、效能以及視覺效果方面,所提方法ACNet取得了比較好的均衡。
題外語
這篇論文整體看上去非常簡潔,思想也比較簡單。在網路結構方面,它與LESRCNN的結構上基本是一致的;而LESRNN與EDSR也是非常的相似。區別有這麼三點:
- 重建部分:EDSR在上取樣最終的解析度後直接採用一個卷積進行重建;而LESRCNN在上取樣到最終解析度後採用了5層卷積進行處理。LESRCNN的這種處理方法無疑會造成比較大的計算量。
- 殘差模組部分:EDSR中的殘差模組採用Conv-ReLU-Conv方式;而LESRCNN則採用ReLU-Conv-ReLU-Conv方式。
- 低頻特徵與高頻特徵融合部分:EDSR中用於融合的特徵並未經ReLU處理,先add再上取樣;而LESRCNN則用ReLU進行了處理,然而分別用sub-conv上取樣後再融合。
再回過來頭看LESRCNN與ACNet,則基本屬於將2019年提出的ACNet在LESCNN中的直接應用;事實上,ACNet與IMDN的組合的可行性在2020年的超分競賽中已得到了印證。所以,這篇文章的創新之處著實不多。
另外,需要吐槽的一點:這裡對比的都是一些比較早期的方法,像最近兩年的IMDN、RFANet、RFDN、PAN等優秀的輕量型模型均未進行對比。
不過這篇論文有一點值得稱讚:復現難度非常低。筆者粗看了下結構花了很少時間就完成了code的實現,並僅僅基於DIV2K訓練資料訓練了66W步。在BI退化方面,筆者訓練的模型效能要比作者文中列出來的指標高0.04-0.1dB不等。ACNet的參考實現code如下所示。
class MeanShift(nn.Conv2d):
def __init__(self,
rgb_range=1.0,
rgb_mean=(0.4488, 0.4371, 0.4040),
rgb_std=(1.0, 1.0, 1.0),
sign=-1):
super(MeanShift, self).__init__(3, 3, kernel_size=1)
std = torch.Tensor(rgb_std)
self.weight.data = torch.eye(3).view(3, 3, 1, 1) / std.view(3, 1, 1, 1)
self.bias.data = sign * rgb_range * torch.Tensor(rgb_mean) / std
self.requires_grad = False
class Upsampler(nn.Sequential):
def __init__(self,
scale,
channels,
bn=False,
act=False,
bias=True):
m = []
if (scale & (scale - 1)) == 0:
for _ in range(int(math.log(scale, 2))):
m.append(nn.Conv2d(channels, 4 * channels, 3, 1, 1, bias=bias))
m.append(nn.PixelShuffle(2))
if bn:
m.append(nn.BatchNorm2d(channels))
if act:
m.append(nn.ReLU(inplace=True))
elif scale == 3:
m.append(nn.Conv2d(channels, 9 * channels, 3, 1, 1, bias=bias))
m.append(nn.PixelShuffle(3))
if bn:
m.append(nn.BatchNorm2d(channels))
if act:
m.append(nn.ReLU(inplace=True))
else:
raise NotImplementedError
super().__init__(*m)
class ACBlock(nn.Module):
def __init__(self, in_channels, out_channels):
super(ACBlock, self).__init__()
self.conv1x3 = nn.Conv2d(in_channels, out_channels, (1, 3), 1, (0, 1))
self.conv3x1 = nn.Conv2d(in_channels, out_channels, (3, 1), 1, (1, 0))
self.conv3x3 = nn.Conv2d(in_channels, out_channels, (3, 3), 1, (1, 1))
def forward(self, x):
conv3x1 = self.conv3x1(x)
conv1x3 = self.conv1x3(x)
conv3x3 = self.conv3x3(x)
return conv3x1 + conv1x3 + conv3x3
class ACNet(nn.Module):
def __init__(self,
scale=2,
in_channels=3,
out_channels=3,
num_features=64,
num_blocks=17,
rgb_range=1.0):
super(ACNet, self).__init__()
self.scale = scale
self.num_blocks = num_blocks
self.num_features = num_features
# pre and post process
self.sub_mean = MeanShift(rgb_range=rgb_range, sign=-1)
self.add_mena = MeanShift(rgb_range=rgb_range, sign=1)
# AB module
self.blk1 = ACBlock(in_channels, num_features)
for idx in range(1, num_blocks):
self.__setattr__(f"blk{idx+1}", nn.Sequential(nn.ReLU(inplace=True), ACBlock(num_features, num_features)))
# MEB
self.lff = nn.Sequential(
nn.ReLU(inplace=False),
Upsampler(scale, num_features),
nn.Conv2d(num_features, num_features, 3, 1, 1),
nn.ReLU(inplace=True),
nn.Conv2d(num_features, num_features, 3, 1, 1)
)
self.hff = nn.Sequential(
nn.ReLU(inplace=False),
Upsampler(scale, num_features),
nn.Conv2d(num_features, num_features, 3, 1, 1),
nn.ReLU(inplace=True),
nn.Conv2d(num_features, num_features, 3, 1, 1)
)
# HFFEB
self.fusion = nn.Sequential(
nn.ReLU(inplace=False),
nn.Conv2d(num_features, num_features, 3, 1, 1),
nn.ReLU(inplace=True),
nn.Conv2d(num_features, num_features, 3, 1, 1),
nn.ReLU(inplace=True),
nn.Conv2d(num_features, out_channels, 3, 1, 1),
)
def forward(self, x):
inputs = self.sub_mean(x)
blk1 = self.blk1(inputs)
high = blk1
tmp = blk1
for idx in range(1, self.num_blocks):
tmp = self.__getattr__(f"blk{idx+1}")(tmp)
high = high + tmp
lff = self.lff(blk1)
hff = self.hff(high)
fusion = self.fusion(lff + hff)
output = self.add_mena(fusion)
return output
推薦閱讀
- 你的感知損失可能用錯了,沈春華團隊提出隨機權值廣義感知損失
- CVPR2021|超分效能不變,計算量降低50%,董超等人提出用於low-level加速的ClassSR
- SANet|融合空域與通道注意力,南京大學提出置換注意力機制
- GhostSR|針對影像超分的特徵冗餘,華為諾亞&北大聯合提出GhostSR
- 影像超分中的那些知識蒸餾
- ICLR2021 | 顯著提升小模型效能,亞利桑那州立大學&微軟聯合提出SEED
- RepVGG|讓你的ConVNet一卷到底,plain網路首次超過80%top1精度
- Transformer再下一城!low-level多個任務榜首被佔領
- 通道注意力新突破!從頻域角度出發,浙大提出FcaNet
- 無需額外資料、Tricks、架構調整,CMU開源首個將ResNet50精度提升至80%+新方法
- 46FPS+1080Px2超分+手機NPU,arm提出一種基於重引數化思想的超高效影像超分方案
- 動態卷積超進化!通道融合替換注意力,減少75%引數量且效能顯著提升 ICLR 2021
- CVPR2021|“無痛漲點”的ACNet再進化,清華大學&曠視科技提出Inception型別的DBB