池化(Pooling)的种类与具体用法——基于Pytorch

  池化操作通常在卷积神经网络中使用,并且与卷积操作配合使用,可以起到调节数据维数,并且具有抑制噪声、降低信息冗余、降低模型计算量、防止过拟合等作用。池化没有可以学习的参数,所以某种程度上与激活函数较为相似,池化在一维或多维张量上的操作与卷积层也有很多相似之处。

一:.基本原理

  池化操作最初是用来减小数据大小,使模型更容易训练,这个过程即为下采样(downsampling),这里可以称为下池化。随着之后的发展,池化也可以增加数据大小,此时的池化操作为上采样(upsmapling)或上池化。

  数据的池化,根据被池化的张量维数的不同,一般可分为一维池化、二维池化与三维池化。池化操作都有一个固定的窗口,可以称为池化窗口,类似与卷积操作中的卷积核。

 对于池化,大多数都需要池化窗口kernel size、步长stride、填充padding、dilation这几个参数。池化窗口表示选取的数据范围,步长表示池化窗口每次沿某个方向的长度,padding表示对input池化前是否在周围补充0,dilation为膨胀率。  

 对于上图,红框即为池化窗口。

  对于上图,padding=1,stride=(1,1),kernel_size=(2,2),dilation=2。

(注:在Pytorch中,padding大小不能超过池化窗口的一半,否则报错)

二:下池化

1.最大池化:MaxPool

最大池化,即每次选取移动框内数的最大值。它可以分为一维、二维和三维张量数据的池化。

        1.1一维数据最大池化

 上图显示的便是kernel_size=3,stride=1,padding=0的一维最大池化。

import torch 
from torch import nn
input=torch.randint(0,2,size=(2,2,5),dtype=torch.float32)
maxpool1d=nn.MaxPool1d(kernel_size=3,stride=1,padding=0,dilation=1)
output=maxpool1d(input)
print(input,output)

        1.2二维数据与三维数据最大池化

 对于二维数据与三维数据的最大池化,与一维情况基本相同。只不过此时的池化窗口为2维张量或三维张量。移动步长也分为两个或三个方向。

 上图显示的是一个kernel_size为(3,3),stride=(1,1),padding=0的二维最大池化。

2.平均池化:AvgPool

 平均池化与前面最大池化所有的参数与意义完全相同,并且也分为1d、2d、3d池化。只不过它的输出是池化窗口所有数的平均值,而不是最大值。

import torch
from torch import nn
input=torch.randint(0,2,size=(2,2,5,4),dtype=torch.float32)
avgpool2d=nn.AvgPool2d(kernel_size=(3,3),stride=(2,1),padding=0)
output=avgpool2d(input)
print(input,output)

3.LP池化:LPPool

 LP池化是一类受视觉皮层内阶层结构启发建立的池化模型。它的输出为池化窗口内每个数的p次方后求和再求均值,最后均值的1/p次方即为该部分窗口的输出结果。所以该函数需要参数norm_type,来指定p值,如果p为1,即与均值池化相同;如果p为无穷,则与最大池化相同。

(注:该函数没有参数padding与dilation)

 在Pytorch中,LP池化只有一维与二维两种。

import torch
from torch import nn
input=torch.randint(0,2,size=(2,2,5,4),dtype=torch.float32)
lppool2d=nn.LPPool2d(kernel_size=(2,2),stride=1,norm_type=1.5)
output=lppool2d(input)
print(input,output)

4.自适应最大/平均池化:AdaptiveMaxPool与AdaptiveAvgPool

 自适应池化的特点是:无论输入数据的size是多少,最终输出的size始终是函数指定的size。而背后的原理是:该函数可以根据输入size与我们指定的输出size来自动调整池化窗口、步长等参数。所以该函数没有kernel_size,stride,padding等参数,只需要指定输出size大小即可。如果需要让某一维度为原来大小,可以直接指定该部分参数为None。

import torch
from torch import nn
input=torch.randint(0,2,size=(2,2,5,4),dtype=torch.float32)
adaptivemaxpool2d=nn.AdaptiveMaxPool2d((None,2))
output=adaptivemaxpool2d(input)

5.分数阶最大池化:FractionalMaxPool

 关于分数阶最大池化,兔兔发现它似乎并没有太多的实际用处。函数中仍需要kernel_size,如果指定output_size,它就可以输出指定大小的张量,类似于自适应最大池化;如果指定output_ratio,它就会根据输出大小与输入大小的比例output_ratio来决定输出大小,所以output_ratio为0~1。并且函数不能同时接受output_ratio与output_size,毕竟这两个参数都决定输出大小,同时使用势必会引起冲突。

input=torch.randint(0,20,size=(1,1,5,5),dtype=torch.float32)
pool=nn.FractionalMaxPool2d(kernel_size=(3,3),output_ratio=0.7)
out=pool(input)
print(out)

三:上池化

1.反最大池化 :MaxUnpool

反池化与前面的池化基本相同,参数一般仍然需要kernel_size,stride,padding等。对于反最大池化,我们还需要提供参数indices。indices表示下池化过程中池化窗口返回那个最大值的位置索引。事实上,在前面的最大池化中,参数return_indices即表示是否返回索引,只不过该函数默认为False。

input=torch.randint(0,2,size=(1,1,5,5),dtype=torch.float32)
pool=nn.MaxPool2d(kernel_size=3,stride=1,padding=0,return_indices=True)
out=pool(input)
print(input)
print(out[0]) #池化后结果
print(out[1]) #池化indice

 那么,现在对于反最大池化就比较好理解了,这个函数根据传入的参数indices,来使得上池化后该位置索引为池化窗口最大值,其余都为0。这里需要注意的是,indices不能传给nn.MaxUnpool,而是传给它的forward()函数中。

input=torch.randint(0,2,size=(1,1,5,5),dtype=torch.float32)
pool=nn.MaxPool2d(kernel_size=3,stride=1,padding=0,return_indices=True)
out,indices=pool(input)
unpool=nn.MaxUnpool2d(kernel_size=3,stride=1,padding=0)
out=unpool(out,indices=indices)
print(out)

四:其它

 对于前面的讲到的池化,实际的池化种类远远不止这些,但Pytorch中也并未提供其它种类的池化方法,所以此时需要我们能够根据池化原理来设计池化层。兔兔在这里以一种随机池化为例,来实现该方法。

 随机池化(Stochastic Pooling)是每次以一定的概率来选取池化窗口的值,并且窗口中的值越大,被选中作为输出的概率越大。所以需要用窗口中每一个数除以窗口中所有数的和,此时每个位置的值即为该位置被选中输出的概率。

import torch
from torch import nn
import numpy as np
class stochasticPool2d(nn.Module):
    def __init__(self,kernel_size,stride):
        super().__init__()
        self.kernel_size=kernel_size
        self.stride=stride
    def forward(self,input):
        b,c,h,w=input.shape
        output=[]
        for B in range(b):
            batch=[]
            for C in range(c):
                channel=[]
                for i in range(0,h-self.kernel_size[0]+1,self.stride[0]):
                    l=[]
                    for j in range(0,w-self.kernel_size[1]+1,self.stride[1]):
                        kernel=input[B,C,i:i+self.kernel_size[0],j:j+self.kernel_size[1]]
                        num=kernel.detach().numpy().reshape((1,-1))[0]
                        pro=(kernel/kernel.sum()).detach().numpy()
                        pro=pro.reshape((1,-1))[0]
                        out=np.random.choice(a=num,p=pro,replace=True)
                        l.append(out)
                    channel.append(l)
                batch.append(channel)
            output.append(batch)
        return torch.tensor(output,dtype=torch.float32)

if __name__=='__main__':
    input=torch.randint(0,2,size=(1,1,5,5))
    pool=stochasticPool2d(kernel_size=(3,3),stride=(1,1))
    print(pool(input))

四:总结

 池化作为深度学习中常用的一种方法,具有对于模型的优化具有重要意义。事实上,池化的种类是非常多的,但是在Pytorch中并未提供所有的方法。一方面我们可以根据Pytorch中已有的池化方法,调整其中的参数来实现我们想要的池化方法;另一方面,我们也应该学会在已有方案都无法实行的情况下,能够自己方法原理设计池化层。

猜你喜欢

转载自blog.csdn.net/weixin_60737527/article/details/127024456