• 李沐d2l(十)--卷积层Ⅱ


    一、池化层

    1 二维最大池化

    与卷积层类似,不过每次只需要返回滑动窗口的最大值

    image-20220811094201223

    当我们在进行图片颜色垂直边缘检测时,进行的卷积输出对位置非常敏感。看下面例子,真实图片的边缘位置不一定每次都在很好的地方,但是卷积输出Y只要有1像素的移位,就会输出0,所以我们需要一定程度的平移不变性去解决这个问题。

    image-20220811094701054

    当我们做了最大池化操作,就会容忍一个像素的移位

    image-20220811094903448

    2 填充、步幅和多个通道

    池化层与卷积层类似,都具有填充和步幅。
    没有可学习的参数(kernel)
    子啊每个输入通道应用池化层以获得相应的输出通道
    输出通道数 = 输入通道数

    3 平均池化层

    最大池化层:检测每个窗口最强的模式信号
    平均池化层:将最大池化层的“取最大值操作”替换为“求平均值”

    image-20220811095455980

    总结

    • 池化层返回窗口中最大或平均值
    • 缓解卷积层位置的敏感性

    4 代码

    最大和平均池化层

    def pool2d(X, pool_size, mode='max'):
        p_h, p_w = pool_size
        Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
        for i in range(Y.shape[0]):
            for j in range(Y.shape[1]):
                if mode == 'max':
                    Y[i, j] = X[i:i + p_h, j:j + p_w].max()
                elif mode == 'avg':
                    Y[i, j] = X[i:i + p_h, j:j + p_w].mean()
        return Y
    
    X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
    # 最大池化层
    print(pool2d(X, (2, 2)))
    '''
    tensor([[4., 5.],
            [7., 8.]])
    '''
    # 平均池化层
    print(pool2d(X, (2, 2), 'avg'))
    '''
    tensor([[2., 3.],
            [5., 6.]])
    '''
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    填充和步幅

    X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
    '''
    tensor([[[[ 0.,  1.,  2.,  3.],
              [ 4.,  5.,  6.,  7.],
              [ 8.,  9., 10., 11.],
              [12., 13., 14., 15.]]]])
    '''
    pool2d = nn.MaxPool2d(3) # 默认步幅与池化窗口的大小相同
    print(pool2d(X)) # tensor([[[[10.]]]])
    
    # 自定义填充和步幅
    pool2d = nn.MaxPool2d(3, padding=1, stride=2)
    
    # 自定义任意大小的矩阵池化窗口
    pool2d = nn.MaxPool2d((2, 3), padding=(1, 1), stride=(2, 3))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    汇聚层在每个输入通道上单独运算

    X = torch.cat((X, X + 1), 1)
    '''
    tensor([[[[ 0.,  1.,  2.,  3.],
              [ 4.,  5.,  6.,  7.],
              [ 8.,  9., 10., 11.],
              [12., 13., 14., 15.]],
    
             [[ 1.,  2.,  3.,  4.],
              [ 5.,  6.,  7.,  8.],
              [ 9., 10., 11., 12.],
              [13., 14., 15., 16.]]]])
    '''
    pool2d = nn.MaxPool2d(3, padding=1, stride=2)
    print(pool2d(X))
    '''
    tensor([[[[ 5.,  7.],
              [13., 15.]],
    
             [[ 6.,  8.],
              [14., 16.]]]])
    '''
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    二、LeNet

    先使用卷积层来学习图片空间信息,然后使用全连接层来转换到类别空间。

    image-20220811102205284

    代码

    class Reshape(torch.nn.Module):
        def forward(self, x):
            return x.view(-1, 1, 28, 28)
    
    net = torch.nn.Sequential(Reshape(), nn.Conv2d(1, 6, kernel_size=5,
                                                   padding=2), nn.Sigmoid(),
                              nn.AvgPool2d(kernel_size=2, stride=2),
                              nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),
                              nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
                              nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
                              nn.Linear(120, 84), nn.Sigmoid(), nn.Linear(84, 10))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    三、AlexNet

    1 与LeNet的区别

    AlexNet使用了更大的池化窗口,并且使用最大化池化层,由于图片更大,核窗口和步长也变得更大

    image-20220812092016705

    AlexNet新增了3个卷积层,并且有着更多的输出通道

    image-20220812092313194

    AlexNet和LeNet都用了两个隐藏层,但是AlexNet的大小增加了很多。

    image-20220812092431225

    2 其它改进

    • 激活函数从sigmoid变成了ReLu
    • 隐藏全连接层后加入了丢弃层
    • 数据增强:一张图片在读取之前,先对图片进行处理,比如随机截取图片、更改图片整体颜色等操作来得到更多的图片。

    3 代码

    import torch
    from torch import nn
    from d2l import torch as d2l
    
    net = nn.Sequential(
        nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
        nn.MaxPool2d(kernel_size=3, stride=2),
        nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
        nn.MaxPool2d(kernel_size=3, stride=2),
        nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(),
        nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
        nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
        nn.MaxPool2d(kernel_size=3, stride=2), nn.Flatten(),
        nn.Linear(6400, 4096), nn.ReLU(), nn.Dropout(p=0.5),
        nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(p=0.5),
        nn.Linear(4096, 10))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    四、使用块的网络VGG

    AlexNet比LeNet更深更大来得到更好的精度,VGG的思想就是能不能在AlexNet的基础上更深和更大。有以下三种选择

    1. 更多的全连接层(太贵)
    2. 更多的卷积层,因为AlexNet使用了3个连续的卷积层,在这个地方改进的话如果是继续在增加卷积层的数量就得不到最终一个很好的设计
    3. 将卷积层组合成块

    1 VGG块

    使用3 x 3卷积(n 层,m通道):在设计前比较了使用5 x 5卷积和3 x 3卷积的效果,发现在相同计算开销的情况下,使用更多的3 x 3的效果比使用少一点(5 x 5因为计算量大,所以不能做得很深)的5 x 5效果更好

    2 x 2最大池化层, 步幅2

    image-20220812095020558

    VGG与AlexNet主要区别就是把AlexNet不规则的卷积层设计抽取出来成为一个可以重复使用的卷积块(VGG块),然后可以重复的拼接块来得到不同的架构。

    image-20220812095940226

    2 代码

    import torch
    from torch import nn
    from d2l import torch as d2l
    
    def vgg_block(num_convs, in_channels, out_channels): # 参数1:需要的卷积层 参数2:输入通道数 参数3:输出通道数
        layers = []
        for _ in range(num_convs):
            layers.append(
                nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))
            layers.append(nn.ReLU())
            in_channels = out_channels
        layers.append(nn.MaxPool2d(kernel_size=2, stride=2))
        return nn.Sequential(*layers)
    
    conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
    
    def vgg(conv_arch):
        conv_blks = []
        in_channels = 1
        for (num_convs, out_channels) in conv_arch:
            conv_blks.append(vgg_block(num_convs, in_channels, out_channels))
            in_channels = out_channels
    
        return nn.Sequential(*conv_blks, nn.Flatten(),
                             nn.Linear(out_channels * 7 * 7, 4096), nn.ReLU(),
                             nn.Dropout(0.5), nn.Linear(4096, 4096), nn.ReLU(),
                             nn.Dropout(0.5), nn.Linear(4096, 10))
    
    net = vgg(conv_arch)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    五、NiN

    LeNet、AlexNet和VGG都有一个共同的特点:它们通过一系列的卷积层和汇聚层来提取空间结构特征后,再通过全连接层对特征的表征进行处理。但是全连接层存在一个问题,它会占用比较多的参数空间,可能会带来过拟合、占用大量内存以及占用很多计算带宽等影响。

    NiN的提出就是为了解决这些问题。它的思想是完全不需要全连接层,在每个像素的通道上分别使用多层感知机,也就是在每个像素位置,应用一个全连接层。这样将权重连接到了每个空间位置,可以将其视为1 * 1的卷积层,或者看作在每个像素位置上独立作用的全连接层,从另一个角度讲,就是将空间维度中的每个像素视为单个样本,将通道维度视为不同特征。

    1 NiN块

    NiN块由一个卷积层和两个全连接层(实际上是窗口大小为1 * 1、步幅为1、无填充的卷积层)组成(因为1 * 1的卷积层等价于全连接层)

    NiN块中两个1 * 1的卷积层其实是起到了全连接层的作用,这两个卷积层充当了带有ReLu激活函数的逐像素全连接层。

    NiN块可以认为是一个简单的神经网络:一个卷积层+两个全连接层。唯一的不同是这里的全连接层是对每一个像素做全连接,对于每个输入的每个像素的权重都是一样的,不会根据输入的具体情况发生变化,所以可以认为是按照输入的每个像素逐一做全连接

    image-20220812143621256

    image-20220812143935219

    2 NiN架构

    • 无全连接层
    • 交替使用NiN块和步幅为2的最大池化层,逐步减小高宽和增大通道数
    • 最后使用全局平均池化层得到输出,其输入通道数是标签类别数。

    3 VGG & NIN Networks

    VGG块是由多个卷积层和一个最大池化层组成。而NiN块由一个卷积层和两个大的全连接层,最后通过一个输出通道数为1000的全连接层得到1000类

    VGG网络由四个VGG块和两个大的全连接层,最后通过一个输出通道数为1000的全连接层得到1000类。NiN网络由一个NiN块和一个步幅为2、3 * 3的最大池化层不断重复,如果将最后重复部分的NiN块的通道数设置为标签类别的数量的话,则最后就可以用全局平均池化层替全连接层来得到输出。

    image-20220812144612863

    总结

    NiN完全取消了全连接层,这样做的好处是减少了模型所需参数的数量,但是在实践中,这种设计有时会增加模型的训练时间。

    4 代码

    import torch
    from torch import nn
    from d2l import torch as d2l
    
    # NiN块
    def nin_block(in_channels, out_channels, kernel_size, strides, padding):
        return nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size, strides, padding),
            nn.ReLU(), nn.Conv2d(out_channels, out_channels, kernel_size=1),
            nn.ReLU(), nn.Conv2d(out_channels, out_channels, kernel_size=1),
            nn.ReLU())
     # NiN模型
     net = nn.Sequential(
        nin_block(1, 96, kernel_size=11, strides=4, padding=0),
        nn.MaxPool2d(3, stride=2),
        nin_block(96, 256, kernel_size=5, strides=1, padding=2),
        nn.MaxPool2d(3, stride=2),
        nin_block(256, 384, kernel_size=3, strides=1, padding=1),
        nn.MaxPool2d(3, stride=2), nn.Dropout(0.5),
        nin_block(384, 10, kernel_size=3, strides=1, padding=1),
        nn.AdaptiveAvgPool2d((1, 1)),
        nn.Flatten())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    Pytest接口自动化测试实战演练
    JAVA基础(JAVA SE)学习笔记(八)面向对象编程(高级)
    NodeJS校园快递智能互助平台-计算机毕业设计源码58554
    间隔一分钟按某个按键一次——VBS脚本
    Spring Security认证架构介绍
    Performance Improvements in .NET 8 & 7 & 6 -- String【翻译】
    数据大屏方案 : 实现数据可视化的关键一环_光点科技
    32.企业快速开发平台Spring Cloud+Spring Boot+Mybatis之Highcharts 固定布局柱形图
    【深度学习】Pytorch面试题:什么是 PyTorch?PyTorch 的基本要素是什么?Conv1d、Conv2d 和 Conv3d 有什么区别?
    阿里云金融创新峰会云原生分论坛圆满举办,加速金融行业落地云原生
  • 原文地址:https://blog.csdn.net/qq_18824403/article/details/126304912