深度学习——卷积神经网络(CNN)

学习目标:

1. 理解有关卷积神经网络的一些概念和构成所有卷积⽹络主⼲的基本元素。包括卷积层本⾝、填充(padding)和步幅 (stride)的基本细节、⽤于在相邻区域汇聚信息的汇聚层(pooling)、在每⼀层中多通道(channel)的使⽤, 以及有关现代卷积⽹络架构的仔细讨论。

2.了解卷积神经网络的原理


学习内容:

         介绍:卷积神经网络(convolutional neural network,CNN)是⼀类强⼤的、为处理图像数据⽽设计的 神经⽹络。卷积神经⽹络需要的参数少于全连接架 构的⽹络,⽽且卷积也很容易⽤GPU并⾏计算。因此卷积神经⽹络除了能够⾼效地采样从⽽获得精确的模型, 还能够⾼效地计算。

1.从全连接层到卷积

      多层感知机⼗分适合处理表格数据,其中⾏对应样本,列对应特征,然⽽对于⾼维感知数据,这种缺少结构的⽹络可能会变得不实⽤。于是便诞生了利用卷积神经网络用来处理高维数据。

     多层感知机的限制:多层感知机的输⼊是⼆维图像X,其隐藏表⽰H在数学上是⼀个矩阵,在代码中表⽰为⼆维张量。其 中X和H具有相同的形状。当图像的位置进行移动或者只对局部进行检测时,多层感知机检测的结果会与标签有着非常大的相差。

     卷积神经网络的特点:

     (1) 平移不变性(translation invariance):不管检测对象出现在图像中的哪个位置,神经⽹络的前⾯⼏层 应该对相同的图像区域具有相似的反应,即为“平移不变性”。

      (2)局部性(locality):神经⽹络的前⾯⼏层应该只探索输⼊图像中的局部区域,⽽不过度在意图像中相隔 较远区域的关系,这就是“局部性”原则。最终,可以聚合这些局部特征,以在整个图像级别进⾏预测。

卷积的定义:在数学中,两个函数(⽐如f, g :R d → R)之间的“卷积”被定义为:

                                      (f ∗ g)(x) =∫f(z)g(x − z)dz.

也就是说,卷积是当把⼀个函数“翻转”并移位x时,测量f和g之间的重叠。当为离散对象时,积分就变成求 和。例如,对于由索引为Z的、平⽅可和的、⽆限维向量集合中抽取的向量,我们得到以下定义:                          (f ∗ g)(i) = ∑f(a)g(i − a).

对于⼆维张量,则为f的索引(a, b)和g的索引(i − a, j − b)上的对应加和:

                                     (f ∗ g)(i, j) = ∑∑f(a, b)g(i − a, j − b).

2.图像卷积

在卷积层中,输⼊张量和核张量通过互相关运算产⽣输出张量。

输入张量其实就是一个大的矩阵(假设为M*N);核张量就是核函数,也就是一个小的矩阵(假设为m*n);则输出张量也为一个矩阵(其大小为(M-m+1)*(N-n+1));

import torch
from torch import nn
from d2l import torch as d2l
def corr2d(X, K): #@save

#"""计算⼆维互相关运算"""
h, w = K.shape
Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
for i in range(Y.shape[0]):
   for j in range(Y.shape[1]):
      Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
return Y
#输入张量
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
#核函数
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
corr2d(X, K)

卷积层:卷积层对输⼊和卷积核权重进⾏互相关运算,并在添加标量偏置之后产⽣输出。所以,卷积层中的两个被训 练的参数是卷积核权重和标量偏置。⾼度和宽度分别为h和w的卷积核可以被称为h × w卷积或h × w卷积核。我们也将带有h × w卷积核的卷积层 称为h × w卷积层。

特征映射:输出的卷积层

感受野:卷积核的大小

class Conv2D(nn.Module):

   def __init__(self, kernel_size):

     super().__init__()

     self.weight = nn.Parameter(torch.rand(kernel_size))

     self.bias = nn.Parameter(torch.zeros(1))

   def forward(self, x):

     return corr2d(x, self.weight) + self.bias

学习卷积核:通过仅查看“输⼊-输出”对来学习由X⽣成Y的卷积核。先构造⼀个卷积层,并 将其卷积核初始化为随机张量。接下来,在每次迭代中,我们⽐较Y与卷积层输出的平⽅误差,然后计算梯度 来更新卷积核。当误差越来越小时,说明以及越来越接近理想的卷积核。

# 构造⼀个⼆维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)
# 这个⼆维卷积层使⽤四维输⼊和输出格式(批量⼤⼩、通道、⾼度、宽度),
# 其中批量⼤⼩和通道数都为1
X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))
lr = 3e-2 # 学习率

for i in range(10):
   Y_hat = conv2d(X)
   l = (Y_hat - Y) ** 2

   conv2d.zero_grad()
   l.sum().backward()

# 迭代卷积核

  conv2d.weight.data[:] -= lr * conv2d.weight.grad

  if (i + 1) % 2 == 0:

    print(f'epoch {i+1}, loss {l.sum():.3f}')

3.图像填充

       在应⽤多层卷积时,我们常常丢失边缘像素。由于我们通常使⽤⼩卷积核,因此对于任何单个卷 积,我们可能只会丢失⼏个像素。但随着我们应⽤许多连续卷积层,累积丢失的像素数就多了。解决这个问 题的简单⽅法即为填充(padding):在输⼊图像的边界填充元素(通常填充元素是0)。

       通常,如果我们添加ph⾏填充(⼤约⼀半在顶部,⼀半在底部)和pw列填充(左侧⼤约⼀半,右侧⼀半),则 输出形状将为:(nh和nw为输入图像的大小,kh和kw为卷积核的大小);

    输出图像的大小为            (nh − kh + ph + 1) × (nw − kw + pw + 1)

这意味着输出的⾼度和宽度将分别增加ph和pw,当我们设置ph = kh − 1和pw = kw − 1,使输⼊和输出具有相同的⾼度和宽度。

import torch
from torch import nn
# 为了⽅便起⻅,我们定义了⼀个计算卷积层的函数。
# 此函数初始化卷积层权重,并对输⼊和输出提⾼和缩减相应的维数
def comp_conv2d(conv2d, X):
# 这⾥的(1,1)表⽰批量⼤⼩和通道数都是1

  X = X.reshape((1, 1) + X.shape)
  Y = conv2d(X)
  # 省略前两个维度:批量⼤⼩和通道
  return Y.reshape(Y.shape[2:])
# 请注意,这⾥每边都填充了1⾏或1列,因此总共添加了2⾏或2列
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)
X = torch.rand(size=(8, 8))
comp_conv2d(conv2d, X).shape

步幅:卷积核每次移动的大小,步幅太小会导致需要的时间很长,步幅太大会有可能丢失一些特征。所以要选择适当的步幅大小。

当垂直步幅为sh、⽔平步幅为sw时,输出形状为(nh和nw为输入图像的大小,kh和kw为卷积核的大小)

                     ⌊(nh − kh + ph + sh)/sh⌋ × ⌊(nw − kw + pw + sw)/sw⌋.

如果我们设置了ph = kh − 1和pw = kw − 1,则输出形状将简化为⌊(nh + sh − 1)/sh⌋ × ⌊(nw + sw − 1)/sw⌋。 更进⼀步,如果输⼊的⾼度和宽度可以被垂直和⽔平步幅整除,则输出形状将为(nh/sh) × (nw/sw)。

4.多输入多输出通道 

多输入:

       当输⼊包含多个通道时,需要构造⼀个与输⼊数据具有相同输⼊通道数的卷积核,以便与输⼊数据进⾏互相 关运算。假设输⼊的通道数为ci,那么卷积核的输⼊通道数也需要为ci。如果卷积核的窗⼝形状是kh ×kw,那 么当ci = 1时,我们可以把卷积核看作形状为kh × kw的⼆维张量。

对于多输入,我们可以对每个通道输⼊的⼆维张量和 卷积核的⼆维张量进⾏互相关运算,再对通道求和(将ci的结果相加)或者选取最大值或最小值的方法得到⼆维张量 。这就是多通道输⼊和多 输⼊通道卷积核之间进⾏⼆维互相关运算的结果。

多输出:

       在最流⾏的神经⽹络架构中,随着神经⽹络层数的加深,我们常会增加输 出通道的维数,通过减少空间分辨率以获得更⼤的通道深度。直观地说,我们可以将每个通道看作对不同特 征的响应。⽽现实可能更为复杂⼀些,因为每个通道不是独⽴学习的,⽽是为了共同使⽤⽽优化的。因此,多 输出通道并不仅是学习多个单通道的检测器。

5.汇聚层(降低卷积层对位置的敏感性,同时降低对空间降采样表示的敏感性)

        通常当我们处理图像时,我们希望逐渐降低隐藏表⽰的空间分辨率、聚集信息,这样随着我们在神经⽹络中 层叠的上升,每个神经元对其敏感的感受野(输⼊)就越⼤。

       与卷积层类似,汇聚层运算符由⼀个固定形状的窗⼝组成,该窗⼝根据其步幅⼤⼩在输⼊的所有区域上滑动, 为固定形状窗⼝(有时称为汇聚窗⼝)遍历的每个位置计算⼀个输出

        但是,不同于卷积层中的输⼊与卷积 核之间的互相关计算,汇聚层不包含参数。相反,池运算是确定性的,我们通常计算汇聚窗⼝中所有元素的 最⼤值或平均值。这些操作分别称为最⼤汇聚层(maximum pooling)和平均汇聚层(average pooling)。 在这两种情况下,与互相关运算符⼀样,汇聚窗⼝从输⼊张量的左上⻆开始,从左往右、从上往下的在输⼊ 张量内滑动。在汇聚窗⼝到达的每个位置,它计算该窗⼝中输⼊⼦张量的最⼤值或平均值。计算最⼤值或平 均值是取决于使⽤了最⼤汇聚层还是平均汇聚层。

import torch
from torch import nn
from d2l import torch as d2l
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]])
pool2d(X, (2, 2))
pool2d(X, (2, 2), 'avg')


6.卷积神经网络(LeNet)

        卷积神经网络,它是最早发布的卷积神经⽹络之⼀,因其在计算机视觉任务中的⾼效性能⽽受到⼴泛关 注。这个模型是由AT&T⻉尔实验室的研究员Yann LeCun在1989年提出的(并以其命名),⽬的是识别图像(LeCun et al., 1998)中的⼿写数字。当时,Yann LeCun发表了第⼀篇通过反向传播成功训练卷积神经⽹络的 研究,这项⼯作代表了⼗多年来神经⽹络研究开发的成果。

总体来看,LeNet(LeNet-5)由两个部分组成:

     • 卷积编码器:由两个卷积层组成;

     • 全连接层密集块:由三个全连接层组成。

每个卷积块中的基本单元是⼀个卷积层、⼀个sigmoid激活函数和平均汇聚层。请注意,虽然ReLU和最⼤汇 聚层更有效,但它们在20世纪90年代还没有出现。每个卷积层使⽤5 × 5卷积核和⼀个sigmoid激活函数。这 些层将输⼊映射到多个⼆维特征输出,通常同时增加通道的数量。第⼀卷积层有6个输出通道,⽽第⼆个卷 积层有16个输出通道。每个2 × 2池操作(步幅2)通过空间下采样将维数减少4倍。卷积的输出形状由批量⼤ ⼩、通道数、⾼度、宽度决定。

import torch
from torch import nn
from d2l import torch as d2l
net = nn.Sequential(
   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))

#验证
X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)
for layer in net:
   X = layer(X)
   print(layer.__class__.__name__,'output shape: \t',X.shape)


学习时间:

提示:这里可以添加计划学习的时间

例如:

  • 周一至周五晚上 7 点—晚上9点
  • 周六上午 9 点-上午 11 点
  • 周日下午 3 点-下午 6 点

学习产出:

提示:这里统计学习计划的总量

例如:

  • 技术笔记 2 遍
  • 优快云 技术博客 3 篇
  • 习的 vlog 视频 1 个
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值