池化(Pooling)是卷積神經(jīng)網(wǎng)絡中的一個重要的概念荒勇,它實際上是一種形式的降采樣柒莉。有多種不同形式的非線性池化函數(shù),池化層會不斷地減小數(shù)據(jù)的空間大小沽翔,因此參數(shù)的數(shù)量和計算量也會下降兢孝,這在一定程度上也控制了過擬合窿凤。通常來說,CNN的卷積層之間都會周期性地插入池化層
一. 池化的目的及作用
池化層大大降低了網(wǎng)絡模型參數(shù)和計算成本跨蟹,也在一定程度上降低了網(wǎng)絡過擬合的風險雳殊。概括來說,池化層主要有以下五點作用:
- 增大網(wǎng)絡感受野
- 抑制噪聲窗轩,降低信息冗余
- 降低模型計算量夯秃,降低網(wǎng)絡優(yōu)化難度
- 防止網(wǎng)絡過擬合
- 使模型對輸入的特征位置變化更加魯棒
對于池化操作,大量常用的是Max_Pooling和Average_Pooling痢艺,但實際上卷積神經(jīng)網(wǎng)絡的池化方法還有很多仓洼,下文將對業(yè)界目前所出現(xiàn)的一些池化方法進行歸納總結:
二. 池化函數(shù)分類詳解
1. Max Pooling(最大池化)
最大池化(Max Pooling)是將輸入的矩陣劃分為若干個矩形區(qū)域,對每個子區(qū)域輸出最大值堤舒,其定義如下:
其中色建,表示與第
個特征圖有關的在矩形區(qū)域
的最大池化輸出值,
表示矩形區(qū)域
中位于(p,q)處的元素
對于最大池化操作舌缤,只選擇每個矩形區(qū)域中的最大值進入下一層箕戳,而其他元素將不會進入下一層。所以最大池化提取特征圖中響應最強烈的部分進入下一層国撵,這種方式摒棄了網(wǎng)絡中大量的冗余信息陵吸,使得網(wǎng)絡更容易被優(yōu)化。同時這種操作方式也常常丟失了一些特征圖中的細節(jié)信息卸留,所以最大池化更多保留些圖像的紋理信息
# Torch 實現(xiàn)
torch.nn.MaxPool1d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
2. Average Pooling(平均池化)
平均池化(Average Pooling)是將輸入的圖像劃分為若干個矩形區(qū)域,對每個子區(qū)域輸出所有元素的平均值椭豫,其定義如下:
其中耻瑟,表示與第
個特征圖有關的在矩形區(qū)域
的平均池化輸出值,
表示矩形區(qū)域
中位于(p,q)處的元素赏酥,
表示矩形區(qū)域
中元素個數(shù)
平均池化取每個矩形區(qū)域中的平均值喳整,可以提取特征圖中所有特征的信息進入下一層,而不像最大池化只保留值最大的特征裸扶,所以平均池化可以更多保留些圖像的背景信息
torch.nn.AvgPool1d(kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True)
3. Global Average Pooling(全局平均池化)
在卷積神經(jīng)網(wǎng)絡訓練初期框都,卷積層通過池化層后一般要接多個全連接層進行降維,最后再Softmax分類呵晨,這種做法使得全連接層參數(shù)很多魏保,降低了網(wǎng)絡訓練速度,且容易出現(xiàn)過擬合的情況摸屠。在這種背景下谓罗,M Lin等人提出使用全局平均池化
Global Average Pooling
來取代最后的全連接層。用很小的計算代價實現(xiàn)了降維季二,更重要的是GAP極大減少了網(wǎng)絡參數(shù)(CNN網(wǎng)絡中全連接層占據(jù)了很大的參數(shù))檩咱。
全局平均池化是一種特殊的平均池化揭措,只不過它不劃分若干矩形區(qū)域,而是將整個特征圖中所有的元素取平均輸出到下一層刻蚯。其定義如下:
其中绊含,表示與第
個特征圖的全局平均池化輸出值,
表示第
個特征圖區(qū)域
中位于(p,q)處的元素炊汹,
表示第
個特征圖全部元素的個數(shù)
作為全連接層的替代操作躬充,GAP對整個網(wǎng)絡在結構上做正則化防止過擬合,直接剔除了全連接層中黑箱的特征兵扬,直接賦予了每個channel實際的類別意義麻裳。除此之外,使用GAP代替全連接層器钟,可以實現(xiàn)任意圖像大小的輸入津坑,而GAP對整個特征圖求平均值,也可以用來提取全局上下文信息傲霸,全局信息作為指導進一步增強網(wǎng)絡性能
class GlobalAvgPool1d(nn.Module):
def __init__(self):
super(GlobalAvgPool1d,self).__init__()
def forward(self, x):
return nn.AvgPool1d(x,kernel_size=x.shape[2])
4. Mix Pooling(混合池化)
在模型訓練期間隨機采用了最大池化和平均池化方法疆瑰,并在一定程度上有助于防止網(wǎng)絡過擬合現(xiàn)象,其定義如下:
其中是0或1的隨機值昙啄,表示選擇使用最大池化或平均池化穆役,換句話說,混合池化以隨機方式改變了池調節(jié)的規(guī)則梳凛,這將在一定程度上解決最大池和平均池所遇到的問題
混合池化優(yōu)于傳統(tǒng)的最大池化和平均池化方法耿币,并可以解決過擬合問題來提高分類精度。此外該方法所需要的計算開銷可忽略不計韧拒,而無需任何超參數(shù)進行調整淹接,可被廣泛運用于CNN
將AvgPool1d與MaxPool1d加權求和即可
5. Stochastic Pooling(隨機池化)
隨機池化是Zeiler等人于ICLR2013提出的一種池化操作,隨機池化的計算過程如下:
- 先將矩陣中的元素同時除以它們的和sum叛溢,得到概率矩陣
- 按照概率隨機選中元素
- pooling得到的值就是方格位置的值
隨機池化只需對特征圖中的元素按照其概率值大小隨機選擇塑悼,即元素值大的被選中的概率也大,而不像max-pooling那樣楷掉,永遠只取那個最大值元素厢蒜,這使得隨機池化具有更強的泛化能力
class StochasticPool2DLayer(nn.Module):
def __init__(self, pool_size=2, maxpool=True, training=False, grid_size=None, **kwargs):
super(StochasticPool2DLayer, self).__init__(**kwargs)
self.pool_size = pool_size
self.maxpool_flag = maxpool
self.training = training
if grid_size:
self.grid_size = grid_size
else:
self.grid_size = pool_size
self.Maxpool = torch.nn.MaxPool2d(kernel_size=self.pool_size, stride=1)
self.Avgpool = torch.nn.AvgPool2d(kernel_size=self.pool_size, stride=self.pool_size, padding=self.pool_size//2,)
self.padding = nn.ConstantPad2d((0,1,0,1),0)
def forward(self, x, training=False, **kwargs):
if self.maxpool_flag:
x = self.Maxpool(x)
x = self.padding(x)
if not self.training:
x = self.Avgpool(x)
return x # [:, :, ::self.pool_size, ::self.pool_size]
else:
w, h = x.data.shape[2:]
n_w, n_h = w//self.grid_size, h//self.grid_size
n_sample_per_grid = self.grid_size//self.pool_size
idx_w = []
idx_h = []
if w>2 and h>2:
for i in range(n_w):
offset = self.grid_size * i
if i < n_w - 1:
this_n = self.grid_size
else:
this_n = x.data.shape[2] - offset
this_idx, _ = torch.sort(torch.randperm(this_n)[:n_sample_per_grid])
idx_w.append(offset + this_idx)
for i in range(n_h):
offset = self.grid_size * i
if i < n_h - 1:
this_n = self.grid_size
else:
this_n = x.data.shape[3] - offset
this_idx, _ = torch.sort(torch.randperm(this_n)[:n_sample_per_grid])
idx_h.append(offset + this_idx)
idx_w = torch.cat(idx_w, dim=0)
idx_h = torch.cat(idx_h, dim=0)
else:
idx_w = torch.LongTensor([0])
idx_h = torch.LongTensor([0])
output = x[:, :, idx_w.cuda()][:, :, :, idx_h.cuda()]
return output
6. Power Average Pooling(冪平均池化)
冪平均池化是基于平均池化和最大池化的結合,利用一個學習參數(shù)來確定這兩種方法的相對重要性烹植;當
時许饿,使用局部求和溯香,當
時创南,使用最大池化主之,其定義如下:
其中表示待池化區(qū)域中的像素值集
torch.nn.LPPool1d(norm_type, kernel_size, stride=None, ceil_mode=False)
7. Detail-Preserving Pooling(DPP池化)
為了降低隱藏層的規(guī)模或數(shù)量促绵,大多數(shù)CNN都會采用池化方式來減少參數(shù)數(shù)量攒庵,來改善某些失真的不變性并增加感受野的大小嘴纺。由于池化本質上是一個有損的過程,所以每個這樣的層都必須保留對網(wǎng)絡可判別性最重要的部分進行激活浓冒。但普通的池化操作只是在特征圖區(qū)域內進行簡單的平均或最大池化來進行下采樣過程栽渴,這對網(wǎng)絡的精度有比較大的影響∥壤粒基于以上幾點闲擦,F(xiàn)araz Saeedan等人提出一種自適應的池化方法-DPP池化,該池化可以放大空間變化并保留重要的圖像結構細節(jié)场梆,且其內部的參數(shù)可通過反向傳播加以學習墅冷。DPP池化主要受Detail-Preserving Image Downscaling
的啟發(fā)。
- Detail-Preserving Image Downscaling
- 其中
是原圖或油,
是output寞忿,[]表示取對于坐標像素值
- 其中
是施加到輸入隨后的下采樣,其隨后由一個近似的二維高斯濾波器平滑化的箱式濾波器的結果顶岸。如下展示了DPID的結構圖腔彰,
是用近似高斯分布的filter smooth后的圖像:
- 下圖展示了DPID的濾波圖,與普通雙邊濾波器不同辖佣,它獎勵輸入強度的差異霹抛,使得與
的差異較大的像素值貢獻更大
- Detail-Preserving Pooling
a. 將上部分中的L2Norm替換成一個可學習的generic scalar reward function:
b. 首先給出weight的表示:
c. 這里給出了兩種reward function:
d. 作者又補充了的生成:
DPP池化允許縮減規(guī)模以專注于重要的結構細節(jié),可學習的參數(shù)控制著細節(jié)的保存量卷谈,此外杯拐,由于細節(jié)保存和規(guī)范化相互補充,DPP可以與隨機合并方法結合使用世蔗,以進一步提高準確率
class DetailPooling(nn.Module):
def __init__(self, tensor_size, asymmetric=False, lite=True,
*args, **kwargs):
super(DetailPooling, self).__init__()
self._lambda = nn.Parameter(torch.Tensor(1))
self._lambda.data.mul_(0).add_(.6)
self._alpha = nn.Parameter(torch.Tensor(1))
self._alpha.data.mul_(0).add_(.1)
self.asymmetric = asymmetric
self.lite = lite
if self.lite:
self.weight = torch.FloatTensor([[[[1, 2, 1]]]])
self.weight = self.weight.expand((tensor_size[1], 1, 1, 3))
else:
self.weight = nn.Parameter(torch.rand(*(tensor_size[1], 1, 3, 3)))
self.weight = nn.init.xavier_normal_(self.weight, gain=0.01)
self.tensor_size = tensor_size[:2] + \
F.avg_pool2d(torch.rand(1, 1, tensor_size[2],
tensor_size[3]), (2, 2)).size()[2:]
def forward(self, tensor):
self._alpha.data.pow_(2).pow_(.5)
self._lambda.data.pow_(2).pow_(.5)
padded_tensor = F.pad(tensor, (1, 1, 1, 1), mode="replicate")
if self.lite:
if tensor.is_cuda and not self.weight.is_cuda:
self.weight = self.weight.cuda()
equation2 = F.conv2d(F.conv2d(padded_tensor, self.weight, groups=tensor.size(1)), self.weight.transpose(2, 3), groups=tensor.size(1)).div(16)
else:
equation2 = F.conv2d(padded_tensor, self.weight, groups=tensor.size(1))
eps = 1e-6
if self.asymmetric:
equation56 = equation2.mul(-1).add(tensor).clamp(0).pow(2)
equation56 = equation56.add(eps**2).pow(2).pow(self._lambda)
else:
equation56 = equation2.mul(-1).add(tensor).pow(2).add(eps**2)
equation56 = equation56.pow(2).pow(self._lambda)
equation4 = equation56.add(self._alpha)
equation7 = equation4.div(F.avg_pool2d(F.pad(equation4, (0, 1, 0, 1), mode="replicate"), (2, 2), (1, 1)).add(1e-8))
equation8 = F.avg_pool2d(tensor.mul(equation7), (2, 2))
return equation8
8. Local Importance Pooling(局部重要性池化)
CNN通常使用空間下采樣層來縮小特征圖端逼,以實現(xiàn)更大的接受場和更少的內存消耗,但對于某些任務而言凸郑,這些層可能由于不合適的池化策略而丟失一些重要細節(jié)裳食,最終損失模型精度矛市。為此芙沥,作者從局部重要性的角度提出了局部重要性池化,通過基于輸入學習自適應重要性權重浊吏,LIP可以在下采樣過程中自動增加特征判別功能
池化操作可歸納為如下公式:
其中的大小和特征
一致而昨,代表每個點的重要性。Local Aggregation and Normalization框架如下圖所示:
圖中分別對應了平均池化找田,最大池化和步長為2的卷積歌憨。首先最大池化對應的最大值不一定是最具區(qū)分力的特征,并且在梯度更新中也難以更新到最具區(qū)分力的特征墩衙,除非最大值被抑制掉务嫡。而步長為2的卷積問題主要在于固定的采樣位置甲抖。因此,合適的池化操作應該包含兩點:
- 下采樣的位置要盡可能非固定間隔
- 重要性的函數(shù)
需通過學習獲得
LIP首先在原特征圖上學習一個類似于注意力的特征圖心铃,然后再和原特征圖進行加權求均值准谚,公式可表述如下:
Local Importance Pooling可以學習自適應和可判別性的特征圖以匯總下采樣特征,同時丟棄無信息特征去扣。這種池化機制能極大保留物體大部分細節(jié)柱衔,對于一些細節(jié)信息異常豐富的任務至關重要
def lip2d(x, logit, kernel size=3, stride=2, padding=1):
weight = torch.exp(logit)
return F.avg pool2d(x?weight , kernel size, stride, padding)/F.avg pool2d(
weight, kernel size, stride, padding)
9. Soft Pooling(軟池化)
現(xiàn)有的一些池化方法大都基于最大池化和平均池化的不同組合,而軟池化****是基于softmax加權的方法來保留輸入的基本屬性愉棱,同時放大更大強度的特征激活唆铐。與maxpooling不同,softpool是可微的奔滑,所以網(wǎng)絡在反向傳播過程中為每個輸入獲得一個梯度艾岂,這有利于提高訓練效果。
SoftPool的計算流程如下:
- 特征圖透過滑動視窗來框選局部數(shù)值
- 框選的局部數(shù)值會先經(jīng)過指數(shù)計算档押,計算出的值為對應的特征數(shù)值的權重
- 將各自的特征數(shù)值與其相對應的權重相乘
- 最后進行加總
這樣的方式讓整體的局部數(shù)值都有所貢獻澳盐,重要的特征占有較高的權重。比Max pooling令宿、Average pooling能夠保留更多信息
SoftPool的數(shù)學定義如下:
計算特征數(shù)值的權重叼耙,其中
為框選的局部區(qū)域,
為特征數(shù)值
將相應的特征數(shù)值與權重相乘后做加總操作:
-
梯度計算: 下圖可以很清楚的指導使用SoftPool的Gradient計算流程粒没。與Max Pooling不同筛婉,SoftPool是可微的,因此在反向傳播至少會分配一個最小梯度值進行更新癞松。
作為一種新穎地池化方法爽撒,SoftPool可以在保持池化層功能的同時盡可能減少池化過程中帶來的信息損失,更好地保留信息特征并因此改善CNN中的分類性能响蓉。大量的實驗結果表明該算法的性能優(yōu)于原始的Avg池化與Max池化硕勿。隨著神經(jīng)網(wǎng)絡的設計變得越來越困難,而通過NAS等方法也幾乎不能大幅度提升算法的性能枫甲,為了打破這個瓶頸源武,從基礎的網(wǎng)絡層優(yōu)化入手,不失為一種可靠有效的精度提升手段
class SOFTPOOL1d(Function):
def forward(ctx, input, kernel=2, stride=None):
no_batch = False
if len(input.size()) == 2:
no_batch = True
input.unsqueeze_(0)
B, C, D = input.size()
kernel = _single(kernel)
if stride is None:
stride = kernel
else:
stride = _single(stride)
oD = (D-kernel[0]) // stride[0] + 1
output = input.new_zeros((B, C, oD))
softpool_cuda.forward_1d(input.contiguous(), kernel, stride, output)
ctx.save_for_backward(input)
ctx.kernel = kernel
ctx.stride = stride
if no_batch:
return output.squeeze_(0)
return output
def backward(ctx, grad_output):
grad_input = torch.zeros_like(ctx.saved_tensors[0])
saved = [grad_output.contiguous()] + list(ctx.saved_tensors) + [ctx.kernel, ctx.stride] + [grad_input]
softpool_cuda.backward_1d(*saved)
saved[-1][torch.isnan(saved[-1])] = 0
return saved[-1], None, None