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

文章目录

    • 一、池化层
      • 1 二维最大池化
      • 2 填充、步幅和多个通道
      • 3 平均池化层
      • 4 代码
    • 二、LeNet
    • 三、AlexNet
      • 1 与LeNet的区别
      • 2 其它改进
      • 3 代码
    • 四、使用块的网络VGG
      • 1 VGG块
      • 2 代码
    • 五、NiN
      • 1 NiN块
      • 2 NiN架构
      • 3 VGG & NIN Networks
      • 4 代码

一、池化层

1 二维最大池化

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

李沐d2l(十)--卷积层Ⅱ_第1张图片

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

李沐d2l(十)--卷积层Ⅱ_第2张图片

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

李沐d2l(十)--卷积层Ⅱ_第3张图片

2 填充、步幅和多个通道

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

3 平均池化层

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

李沐d2l(十)--卷积层Ⅱ_第4张图片

总结

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

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.]])
'''

填充和步幅

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))

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

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.]]]])
'''

二、LeNet

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

李沐d2l(十)--卷积层Ⅱ_第5张图片

代码

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))

三、AlexNet

1 与LeNet的区别

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

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

李沐d2l(十)--卷积层Ⅱ_第6张图片

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

李沐d2l(十)--卷积层Ⅱ_第7张图片

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))

四、使用块的网络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

李沐d2l(十)--卷积层Ⅱ_第8张图片

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

李沐d2l(十)--卷积层Ⅱ_第9张图片

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)

五、NiN

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

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

1 NiN块

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

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

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

李沐d2l(十)--卷积层Ⅱ_第10张图片

李沐d2l(十)--卷积层Ⅱ_第11张图片

2 NiN架构

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

3 VGG & NIN Networks

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

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

李沐d2l(十)--卷积层Ⅱ_第12张图片

总结

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())

你可能感兴趣的:(深度学习,深度学习,python,人工智能)