李沐《动手学深度学习》卷积神经网络 相关基础概念

系列文章

李沐《动手学深度学习》预备知识 张量操作及数据处理
李沐《动手学深度学习》预备知识 线性代数及微积分
李沐《动手学深度学习》线性神经网络 线性回归
李沐《动手学深度学习》线性神经网络 softmax回归
李沐《动手学深度学习》多层感知机 模型概念和代码实现
李沐《动手学深度学习》多层感知机 深度学习相关概念
李沐《动手学深度学习》深度学习计算

目录

  • 系列文章
  • 一、从全连接层到卷积
    • (一)全连接层
    • (二)卷积神经网络的空间不变性
    • (三)通道
  • 二、图像卷积
    • (一)互相关运算
    • (二)卷积层
    • (三)学习卷积核
    • (四)特征映射和感受野
  • 三、填充和步幅
    • (一)填充:在输入图像的边界填充元素(通常填充元素是0)
    • (二)步幅:每次滑动元素的数量
  • 四、多输入多输出通道
    • (一)多输入通道
    • (二)多输出通道
    • (三)1x1卷积层
  • 五、汇聚层


教材:李沐《动手学深度学习》

卷积神经网络(convolutional neural network,CNN)是一类强大的、为处理图像数据而设计的神经网络,是机器学习利用自然图像中一些已知结构的创造性方法。

一、从全连接层到卷积

(一)全连接层

  1. 全连接层网络的缺点:

    • 不能预先假设任何与特征交互相关的先验结构;
    • 全连接层往往意味着大量的参数,需要大量的GPU、分布式优化训练的经验和超乎常人的耐心。
  2. 全连接层的形式化表示:( X X X是输入的二维图像, H H H是其对应的隐藏表示, W W W是权重矩阵, U U U包含偏置参数)
    [ H ] i , j = [ U ] i , j + ∑ k ∑ l [ W ] i , j , k , l [ X ] k , l = [ U ] i , j + ∑ a ∑ b [ V ] i , j , a , b [ X ] i + a , j + b \begin{aligned} [H]_{i,j} &=[U]_{i,j}+\sum_{k}\sum_{l}[W]_{i,j,k,l}[X]_{k,l}\\ &=[U]_{i,j}+\sum_{a}\sum_{b}[V]_{i,j,a,b}[X]_{i+a,j+b} \end{aligned} [H]i,j=[U]i,j+kl[W]i,j,k,l[X]k,l=[U]i,j+ab[V]i,j,a,b[X]i+a,j+b
    k = i + a k=i+a k=i+a l = j + b l=j+b l=j+b,则有 [ V ] i , j , a , b = [ W ] i , j , i + a , j + b [V]_{i,j,a,b}=[W]_{i,j,i+a,j+b} [V]i,j,a,b=[W]i,j,i+a,j+b。索引 a a a b b b在正偏移和负偏移之间移动覆盖了整个图像。

(二)卷积神经网络的空间不变性

  1. 平移不变性: 不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应;(这意味着检测对象在输入 X X X中的平移仅导致隐藏表示 H H H中的平移,而 V V V U U U实际上不依赖于 ( i , j ) (i,j) (i,j)的值)
    [ H ] i , j = u + ∑ a ∑ b [ V ] a , b [ X ] i + a , j + b [H]_{i,j}=u+\sum_{a}\sum_{b}[V]_{a,b}[X]_{i+a,j+b} [H]i,j=u+ab[V]a,b[X]i+a,j+b
    这就是卷积: 使用系数 [ V ] a , b [V]_{a,b} [V]a,b对位置 ( i , j ) (i,j) (i,j)附近的像素 ( i + a , j + b ) (i+a,j+b) (i+a,j+b)进行加权得到 [ H ] i , j [H]_{i,j} [H]i,j,而且此时 [ V ] a , b [V]_{a,b} [V]a,b的系数比 [ V ] i , j , a , b [V]_{i,j,a,b} [V]i,j,a,b少很多,因为前者不再依赖于图像中的位置。

  2. 局部性: 神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系,这就是“局部性”原则。最终,可以聚合这些局部特征,以在整个图像级别进行预测。(在 ∣ a ∣ > Δ |a| > \Delta a>Δ ∣ b ∣ > Δ |b| > \Delta b>Δ的范围之外,可以设置 [ V ] a , b = 0 [V]_{a,b}=0 [V]a,b=0
    [ H ] i , j = u + ∑ a = − Δ Δ ∑ b = − Δ Δ [ V ] a , b [ X ] i + a , j + b [H]_{i,j}=u+\sum_{a=-\Delta}^\Delta\sum_{b=-\Delta}^\Delta[V]_{a,b}[X]_{i+a,j+b} [H]i,j=u+a=ΔΔb=ΔΔ[V]a,b[X]i+a,j+b
    这就是卷积层: V V V被称为卷积核或滤波器,也就是卷积层的权重。

  3. 卷积的本质是有效提取相邻像素间的相关特征。

(三)通道

实际上,图像不是二维张量,而是一个由高度、宽度和颜色组成的三维张量。

[ H ] i , j , d = ∑ a = − Δ Δ ∑ b = − Δ Δ ∑ c [ V ] a , b , c , d [ X ] i + a , j + b , c [H]_{i,j,d}=\sum_{a=-\Delta}^\Delta\sum_{b=-\Delta}^\Delta\sum_{c}[V]_{a,b,c,d}[X]_{i+a,j+b,c} [H]i,j,d=a=ΔΔb=ΔΔc[V]a,b,c,d[X]i+a,j+b,c
这是具有多个通道的卷积层,其中 V V V是该卷积层的权重。

二、图像卷积

(一)互相关运算

  • 在卷积层中,输入张量和核张量通过互相关运算产生输出张量;
  • 输出大小等于输入大小 n h × n w n_h\times n_w nh×nw减去卷积核大小 k h × k w k_h \times k_w kh×kw,即:
    ( n h − k h + 1 ) × ( n w − k w + 1 ) (n_h-k_h+1)\times(n_w-k_w+1) (nhkh+1)×(nwkw+1)
    李沐《动手学深度学习》卷积神经网络 相关基础概念_第1张图片

二维互相关运算的实现:

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

(二)卷积层

  1. 卷积层对输入和卷积核权重进行互相关运算,并在添加标量偏置之后产生输出。 所以,卷积层中的两个被训练的参数是卷积核权重和标量偏置。 在训练基于卷积层的模型时,会随机初始化卷积核权重。

二维卷积层的实现:

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
  1. 卷积层的一个简单应用:通过找到像素变化的位置,来检测图像中不同颜色的边缘,实现图像中目标的边缘检测。

(三)学习卷积核

先构造一个卷积层,并将其卷积核初始化为随机张量。接下来,在每次迭代中,我们比较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}')

(四)特征映射和感受野

  • 特征映射:卷积层有时被称为特征映射,因为它可以被视为一个输入映射到下一层的空间维度的转换器;
  • 感受野:在卷积神经网络中,对于某一层的任意元素 x x x,其感受野是指在前向传播期间可能影响计算的所有元素(来自所有先前层)。

三、填充和步幅

填充和步幅可用于有效地调整数据的维度

(一)填充:在输入图像的边界填充元素(通常填充元素是0)

  • 填充可以增加输出的高度和宽度,这常用来使输出与输入具有相同的高和宽;
  • 如果我们添加 p h p_h ph行填充(大约一半在顶部,一半在底部)和列填充 p w p_w pw(左侧大约一半,右侧一半),则输出形状将为
    ( n h + p h − k h + 1 ) × ( n w + p w − k w + 1 ) (n_h+p_h-k_h+1)\times(n_w+p_w-k_w+1) (nh+phkh+1)×(nw+pwkw+1)
    此时输出的高度和宽度将分别增加和 p h p_h ph p w p_w pw
  • 如果要使得输入和输出具有相同的高度和宽度,则应该设置 p h = k h − 1 p_h=k_h-1 ph=kh1 p w = k w − 1 p_w=k_w-1 pw=kw1,如果 k h k_h kh是奇数,则应该在高度的两侧填充 p h / 2 p_h/2 ph/2行,如果 k h k_h kh是偶数,则应该在输入的顶部填充 [ p h / 2 ] [p_h/2] [ph/2]行,在底部填充 [ p h / 2 ] [p_h/2] [ph/2]行。因此卷积神经网络中卷积核的高度和宽度通常为奇数,在保持空间维度的同时,可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列。

李沐《动手学深度学习》卷积神经网络 相关基础概念_第2张图片
卷积层高度和宽度均为3,所有侧边填充1个像素(padding=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

卷积层高度为5,宽度为3,高度和宽度两边的填充分别为2和1(padding=(2,1))

conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape

(二)步幅:每次滑动元素的数量

  • 步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的 1 n \frac{1}{n} n1(n是一个大于1的整数);
  • 当垂直步幅为 s h s_h sh、水平步幅为 s w s_w sw时,输出形状为:
    [ ( n h − k h + p h + s h ) / s h ] × [ n w − k w + p w + s w ) / s w ] [(n_h-k_h+p_h+s_h)/s_h]\times[n_w-k_w+p_w+s_w)/s_w] [(nhkh+ph+sh)/sh]×[nwkw+pw+sw)/sw]
    如果设置了 p h = k h − 1 p_h=k_h-1 ph=kh1 p w = k w − 1 p_w=k_w-1 pw=kw1,输出形状将简化为:
    [ ( n h + s h − 1 ) / s h ] × [ n w + s w − 1 ) / s w ] [(n_h+s_h-1)/s_h]\times[n_w+s_w-1)/s_w] [(nh+sh1)/sh]×[nw+sw1)/sw]
    如果输入的高度和宽度可以被垂直和步幅整除,则输出形状为:
    ( n h / s h ) × ( n w / s w ) (n_h/s_h)\times(n_w/s_w) (nh/sh)×(nw/sw)

将高度和宽度的步幅设置为2(stride=2):

conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
comp_conv2d(conv2d, X).shape

四、多输入多输出通道

(一)多输入通道

  1. 卷积核:与输入数据具有相同输入通道数
  2. 多输入通道互相关运算: 对每个通道执行互相关操作,然后将结果相加
    李沐《动手学深度学习》卷积神经网络 相关基础概念_第3张图片
    利用代码实现多输入通道的互相关计算:
import torch
from d2l import torch as d2l

def corr2d_multi_in(X, K):
    # 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起
    return sum(d2l.corr2d(x, k) for x, k in zip(X, K))

(二)多输出通道

  • 每个通道可以看作是对不同特征的响应。每个通道不是独立学习的,而是为了共同使用而优化的,因此多输出通道并不仅是学习多个单通道的检测器。

  • 为了获得多个通道的输出,可以为每个输出通道创建一个形状 c i × k h × k w c_i\times k_h \times k_w ci×kh×kw的卷积核张量,这样卷积核的形状是 c 0 × c i × k h × k w c_0\times c_i\times k_h \times k_w c0×ci×kh×kw。( c i c_i ci:输入通道数, c 0 c_0 c0:输出通道数, k h k_h kh:卷积核的高度, k w k_w kw:卷积核的宽度)

def corr2d_multi_in_out(X, K):
    # 迭代“K”的第0个维度,每次都对输入“X”执行互相关运算。
    # 最后将所有结果都叠加在一起
    return torch.stack([corr2d_multi_in(X, k) for k in K], 0)

(三)1x1卷积层

  • 1x1卷积失去了卷积层的特有能力——在高度和宽度维度上,识别相邻元素间相互作用的能力;
  • 1x1卷积层通常用于调整网络层的通道数量和控制模型复杂性
  • 1x1卷积的唯一计算发生在通道上;

当以每像素为基础应用时,可以将1x1卷积层看作是在每个像素位置应用的全连接层,用 c i c_i ci个输入值转换 c 0 c_0 c0个输出值;下图是使用1x1卷积核与3个输入通道和2个输出通道的互相关计算,这里的输入和输出具有相同的高度和宽度,输出中的每个元素都是从输入图像中同一位置的线性组合。

李沐《动手学深度学习》卷积神经网络 相关基础概念_第4张图片

使用全连接层实现1x1卷积:

def corr2d_multi_in_out_1x1(X, K):
    c_i, h, w = X.shape
    c_o = K.shape[0]
    X = X.reshape((c_i, h * w))
    K = K.reshape((c_o, c_i))
    # 全连接层中的矩阵乘法
    Y = torch.matmul(K, X)
    return Y.reshape((c_o, h, w))

五、汇聚层

当我们处理图像时,我们希望逐渐降低隐藏表示的空间分辨率、聚集信息,这样随着我们在神经网络中层叠的上升,每个神经元对其敏感的感受野(输入)就越大。通过逐渐聚合信息,生成越来越粗糙的映射,最终实现学习全局表示的目标,同时将卷积图层的所有优势保留在中间层。

  1. 汇聚(pooling)层的双重目的:
    • 降低卷积层对位置的敏感性;
    • 降低对空间降采样表示的敏感性。
  2. 汇聚层与卷积层的比较:
    • 相同点:都是由一个固定形状的窗口组成,根据步幅大小在输入区域上滑动
    • 不同点:卷积层对应互相关计算是有参数的;汇聚层的池运算是确定的,汇聚层不包含参数
  3. 汇聚层按照计算方法可以分为最大汇聚层和平均汇聚层,最大汇聚层返回汇聚窗口中所有元素的最大值,平均汇聚层返回汇聚窗口中所有元素的平均值;
  4. 与卷积层一样,汇聚层也可以改变输出形状,可以通过填充和步幅以获得所需的输出形状;
  5. 在处理多通道输入数据时,汇聚层在每个输入通道上单独运算,而不是像卷积层一样在通道上对输入进行汇总。 这意味着汇聚层的输出通道数与输入通道数相同

实现汇聚层的前向传播:

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

你可能感兴趣的:(李沐《动手学深度学习》学习笔记,深度学习,cnn,人工智能,pytorch,神经网络)