池化(Pooling)是卷積神經(jīng)網(wǎng)絡(luò)中的一個重要的概念,它實(shí)際上是一種形式的降采樣。有多種不同形式的非線性池化函數(shù),池化層會不斷地減小數(shù)據(jù)的空間大小,因此參數(shù)的數(shù)量和計(jì)算量也會下降,這在一定程度上也控制了過擬合。通常來說,CNN的卷積層之間都會周期性地插入池化層
一. 池化的目的及作用
池化層大大降低了網(wǎng)絡(luò)模型參數(shù)和計(jì)算成本,也在一定程度上降低了網(wǎng)絡(luò)過擬合的風(fēng)險(xiǎn)。概括來說,池化層主要有以下五點(diǎn)作用:
- 增大網(wǎng)絡(luò)感受野
- 抑制噪聲,降低信息冗余
- 降低模型計(jì)算量,降低網(wǎng)絡(luò)優(yōu)化難度
- 防止網(wǎng)絡(luò)過擬合
- 使模型對輸入的特征位置變化更加魯棒
對于池化操作,大量常用的是Max_Pooling和Average_Pooling,但實(shí)際上卷積神經(jīng)網(wǎng)絡(luò)的池化方法還有很多,下文將對業(yè)界目前所出現(xiàn)的一些池化方法進(jìn)行歸納總結(jié):
二. 池化函數(shù)分類詳解
1. Max Pooling(最大池化)
最大池化(Max Pooling)是將輸入的矩陣劃分為若干個矩形區(qū)域,對每個子區(qū)域輸出最大值,其定義如下:
其中,表示與第
個特征圖有關(guān)的在矩形區(qū)域
的最大池化輸出值,
表示矩形區(qū)域
中位于(p,q)處的元素
對于最大池化操作,只選擇每個矩形區(qū)域中的最大值進(jìn)入下一層,而其他元素將不會進(jìn)入下一層。所以最大池化提取特征圖中響應(yīng)最強(qiáng)烈的部分進(jìn)入下一層,這種方式摒棄了網(wǎng)絡(luò)中大量的冗余信息,使得網(wǎng)絡(luò)更容易被優(yōu)化。同時(shí)這種操作方式也常常丟失了一些特征圖中的細(xì)節(jié)信息,所以最大池化更多保留些圖像的紋理信息
# Torch 實(shí)現(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ū)域輸出所有元素的平均值,其定義如下:
其中,表示與第
個特征圖有關(guān)的在矩形區(qū)域
的平均池化輸出值,
表示矩形區(qū)域
中位于(p,q)處的元素,
表示矩形區(qū)域
中元素個數(shù)
平均池化取每個矩形區(qū)域中的平均值,可以提取特征圖中所有特征的信息進(jìn)入下一層,而不像最大池化只保留值最大的特征,所以平均池化可以更多保留些圖像的背景信息
torch.nn.AvgPool1d(kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True)
3. Global Average Pooling(全局平均池化)
在卷積神經(jīng)網(wǎng)絡(luò)訓(xùn)練初期,卷積層通過池化層后一般要接多個全連接層進(jìn)行降維,最后再Softmax分類,這種做法使得全連接層參數(shù)很多,降低了網(wǎng)絡(luò)訓(xùn)練速度,且容易出現(xiàn)過擬合的情況。在這種背景下,M Lin等人提出使用全局平均池化
Global Average Pooling來取代最后的全連接層。用很小的計(jì)算代價(jià)實(shí)現(xiàn)了降維,更重要的是GAP極大減少了網(wǎng)絡(luò)參數(shù)(CNN網(wǎng)絡(luò)中全連接層占據(jù)了很大的參數(shù))。
全局平均池化是一種特殊的平均池化,只不過它不劃分若干矩形區(qū)域,而是將整個特征圖中所有的元素取平均輸出到下一層。其定義如下:
其中,表示與第
個特征圖的全局平均池化輸出值,
表示第
個特征圖區(qū)域
中位于(p,q)處的元素,
表示第
個特征圖全部元素的個數(shù)
作為全連接層的替代操作,GAP對整個網(wǎng)絡(luò)在結(jié)構(gòu)上做正則化防止過擬合,直接剔除了全連接層中黑箱的特征,直接賦予了每個channel實(shí)際的類別意義。除此之外,使用GAP代替全連接層,可以實(shí)現(xiàn)任意圖像大小的輸入,而GAP對整個特征圖求平均值,也可以用來提取全局上下文信息,全局信息作為指導(dǎo)進(jìn)一步增強(qiáng)網(wǎng)絡(luò)性能
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(混合池化)
在模型訓(xùn)練期間隨機(jī)采用了最大池化和平均池化方法,并在一定程度上有助于防止網(wǎng)絡(luò)過擬合現(xiàn)象,其定義如下:
其中是0或1的隨機(jī)值,表示選擇使用最大池化或平均池化,換句話說,混合池化以隨機(jī)方式改變了池調(diào)節(jié)的規(guī)則,這將在一定程度上解決最大池和平均池所遇到的問題
混合池化優(yōu)于傳統(tǒng)的最大池化和平均池化方法,并可以解決過擬合問題來提高分類精度。此外該方法所需要的計(jì)算開銷可忽略不計(jì),而無需任何超參數(shù)進(jìn)行調(diào)整,可被廣泛運(yùn)用于CNN
將AvgPool1d與MaxPool1d加權(quán)求和即可
5. Stochastic Pooling(隨機(jī)池化)
隨機(jī)池化是Zeiler等人于ICLR2013提出的一種池化操作,隨機(jī)池化的計(jì)算過程如下:
- 先將矩陣中的元素同時(shí)除以它們的和sum,得到概率矩陣
- 按照概率隨機(jī)選中元素
- pooling得到的值就是方格位置的值
隨機(jī)池化只需對特征圖中的元素按照其概率值大小隨機(jī)選擇,即元素值大的被選中的概率也大,而不像max-pooling那樣,永遠(yuǎn)只取那個最大值元素,這使得隨機(jī)池化具有更強(qiáng)的泛化能力
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(冪平均池化)
冪平均池化是基于平均池化和最大池化的結(jié)合,利用一個學(xué)習(xí)參數(shù)來確定這兩種方法的相對重要性;當(dāng)
時(shí),使用局部求和,當(dāng)
時(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ù)量,來改善某些失真的不變性并增加感受野的大小。由于池化本質(zhì)上是一個有損的過程,所以每個這樣的層都必須保留對網(wǎng)絡(luò)可判別性最重要的部分進(jìn)行激活。但普通的池化操作只是在特征圖區(qū)域內(nèi)進(jìn)行簡單的平均或最大池化來進(jìn)行下采樣過程,這對網(wǎng)絡(luò)的精度有比較大的影響?;谝陨蠋c(diǎn),F(xiàn)araz Saeedan等人提出一種自適應(yīng)的池化方法-DPP池化,該池化可以放大空間變化并保留重要的圖像結(jié)構(gòu)細(xì)節(jié),且其內(nèi)部的參數(shù)可通過反向傳播加以學(xué)習(xí)。DPP池化主要受Detail-Preserving Image Downscaling的啟發(fā)。
- Detail-Preserving Image Downscaling
- 其中
是原圖,
是output,[]表示取對于坐標(biāo)像素值
- 其中
是施加到輸入隨后的下采樣,其隨后由一個近似的二維高斯濾波器平滑化的箱式濾波器的結(jié)果。如下展示了DPID的結(jié)構(gòu)圖,
是用近似高斯分布的filter smooth后的圖像:
- 下圖展示了DPID的濾波圖,與普通雙邊濾波器不同,它獎勵輸入強(qiáng)度的差異,使得與
的差異較大的像素值貢獻(xiàn)更大
- Detail-Preserving Pooling
a. 將上部分中的L2Norm替換成一個可學(xué)習(xí)的generic scalar reward function:
b. 首先給出weight的表示:
c. 這里給出了兩種reward function:
d. 作者又補(bǔ)充了的生成:
DPP池化允許縮減規(guī)模以專注于重要的結(jié)構(gòu)細(xì)節(jié),可學(xué)習(xí)的參數(shù)控制著細(xì)節(jié)的保存量,此外,由于細(xì)節(jié)保存和規(guī)范化相互補(bǔ)充,DPP可以與隨機(jī)合并方法結(jié)合使用,以進(jìn)一步提高準(zhǔn)確率
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通常使用空間下采樣層來縮小特征圖,以實(shí)現(xiàn)更大的接受場和更少的內(nèi)存消耗,但對于某些任務(wù)而言,這些層可能由于不合適的池化策略而丟失一些重要細(xì)節(jié),最終損失模型精度。為此,作者從局部重要性的角度提出了局部重要性池化,通過基于輸入學(xué)習(xí)自適應(yīng)重要性權(quán)重,LIP可以在下采樣過程中自動增加特征判別功能
池化操作可歸納為如下公式:
其中的大小和特征
一致,代表每個點(diǎn)的重要性。Local Aggregation and Normalization框架如下圖所示:

圖中分別對應(yīng)了平均池化,最大池化和步長為2的卷積。首先最大池化對應(yīng)的最大值不一定是最具區(qū)分力的特征,并且在梯度更新中也難以更新到最具區(qū)分力的特征,除非最大值被抑制掉。而步長為2的卷積問題主要在于固定的采樣位置。因此,合適的池化操作應(yīng)該包含兩點(diǎn):
- 下采樣的位置要盡可能非固定間隔
- 重要性的函數(shù)
需通過學(xué)習(xí)獲得
LIP首先在原特征圖上學(xué)習(xí)一個類似于注意力的特征圖,然后再和原特征圖進(jìn)行加權(quán)求均值,公式可表述如下:
Local Importance Pooling可以學(xué)習(xí)自適應(yīng)和可判別性的特征圖以匯總下采樣特征,同時(shí)丟棄無信息特征。這種池化機(jī)制能極大保留物體大部分細(xì)節(jié),對于一些細(xì)節(jié)信息異常豐富的任務(wù)至關(guān)重要
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加權(quán)的方法來保留輸入的基本屬性,同時(shí)放大更大強(qiáng)度的特征激活。與maxpooling不同,softpool是可微的,所以網(wǎng)絡(luò)在反向傳播過程中為每個輸入獲得一個梯度,這有利于提高訓(xùn)練效果。
SoftPool的計(jì)算流程如下:
- 特征圖透過滑動視窗來框選局部數(shù)值
- 框選的局部數(shù)值會先經(jīng)過指數(shù)計(jì)算,計(jì)算出的值為對應(yīng)的特征數(shù)值的權(quán)重
- 將各自的特征數(shù)值與其相對應(yīng)的權(quán)重相乘
- 最后進(jìn)行加總
這樣的方式讓整體的局部數(shù)值都有所貢獻(xiàn),重要的特征占有較高的權(quán)重。比Max pooling、Average pooling能夠保留更多信息
SoftPool的數(shù)學(xué)定義如下:
計(jì)算特征數(shù)值的權(quán)重,其中
為框選的局部區(qū)域,
為特征數(shù)值
將相應(yīng)的特征數(shù)值與權(quán)重相乘后做加總操作:
-
梯度計(jì)算: 下圖可以很清楚的指導(dǎo)使用SoftPool的Gradient計(jì)算流程。與Max Pooling不同,SoftPool是可微的,因此在反向傳播至少會分配一個最小梯度值進(jìn)行更新。
作為一種新穎地池化方法,SoftPool可以在保持池化層功能的同時(shí)盡可能減少池化過程中帶來的信息損失,更好地保留信息特征并因此改善CNN中的分類性能。大量的實(shí)驗(yàn)結(jié)果表明該算法的性能優(yōu)于原始的Avg池化與Max池化。隨著神經(jīng)網(wǎng)絡(luò)的設(shè)計(jì)變得越來越困難,而通過NAS等方法也幾乎不能大幅度提升算法的性能,為了打破這個瓶頸,從基礎(chǔ)的網(wǎng)絡(luò)層優(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


