欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > 跟着李沐老师学习深度学习(十)

跟着李沐老师学习深度学习(十)

2025/2/20 20:25:33 来源:https://blog.csdn.net/weixin_53318497/article/details/145602183  浏览:    关键词:跟着李沐老师学习深度学习(十)

卷积层

从全连接层到卷积

例子

在这里插入图片描述
从这张PPT讲起,比如有一个猫狗分类的例子,比如一张图片的输入是36M像素,那么它的参数就有3600万;我们这里使用一个100大小的单隐藏层的MLP,那么此时整个模型就有100 * 36M的像素(即:36亿个参数);如果我们考虑多层感知机,在考虑大量数据的时候,那么此时无法处理了;说明:全连接层处理图片的时候会遇到参数过多、模型过大的问题。

不变性

根据“沃尔多在哪里” 可得到两个不变性:
1.平移不变性(translation invariance):不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应,即为“平移不变性”
2.局部性(locality):神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系,这就是“局部性”原则。最终,可以聚合这些局部特征,以在整个图像级别进行预测。
在这里插入图片描述

多层感知机局限性——破局

  • 首先补充一些关于 “卷积” 的知识

  • 连续域的卷积与离散域的卷积
    首先聊聊卷积(这部分可以直接跳过,影响不大)。卷积大家一般都是聊连续域比较多啊,其定义为
    在这里插入图片描述结果被称之为f(x)与g(x)的卷积。
    而离散域与之类似,为
    在这里插入图片描述注意,离散域中n的取值为整数,其他则同理,不再赘述。
    举个例子:记x(n),y(n)满足如下关系:

    • x(1)=1,x(2)=-1,当n取其他值时,x(n)=0;
    • y(1)=1, y(2)=2,当n取其他值时,y(n)=0;
      那么:
      在这里插入图片描述很自然的h(1)=0;
      其他同理。那么到此为止,我们卷积部分算是差不多讲完了。

在这里插入图片描述

  • 如果还是使用全连接层解决以上例子的问题,为了使每个隐藏神经元都能接收到每个输入像素的信息,我们将参数从权重矩阵(如同先前在多层感知机中所做的那样)替换为四阶权重张量。可以得到上图中公式1的式子;W → V做了形式上的变换(k=i+a, l=j+b)
  • 在此基础上,首先引入“平移不变性”: 检测对象在输入X中的平移,应该仅导致隐藏表示H中的平移,因此U和V不依赖于 (i, j)的值;可以将公式转变成上图中的公式(2);此时看公式可以得出V就是我们所说的卷积;
  • 然后,在引入“局部性”:收集[H]i, j的参数不应该偏离到(i, j)很远的地方,也就是说,可以规定一个范围▲;使得**|a| <= ▲ 并且 |b| <= ▲** ,此时公式又能化简为公式(3);此时V被称为卷积核(convolution kernel)或者滤波器(filter);

经过以上的转变,当图像处理的局部区域很小时,卷积神经网络与多层感知机的训练差异可能是巨大的:以前,多层感知机可能需要数十亿个参数来表示网络中的一层,而现在卷积神经网络通常只需要几百个参数,而且不需要改变输入或隐藏表示的维数

CNN中的关键操作:

  • 卷积
    “卷积” 说法其实是错误的,这里表达的运算其实是互相关运算(cross-correlation),而不是卷积运算;真正的卷积运算:需先将滤波器(核)水平和垂直翻转,再在输入信号或图像上滑动并计算点积;而相互关不需要翻转,直接在输入信号或图像上滑动并计算点积。
    在这里插入图片描述
    在这里插入图片描述

  • 总结

    • 卷积层将输入和核矩阵进行交叉相关,加上偏移后得到输
    • 核矩阵和偏移是可学习的参数
    • 核矩阵的大小是超参数
  • 代码

# 相互关运算
import torch
from torch import nn
from d2l import torch as d2ldef corr2d(X, K):"""计算二维相互关运算"""h, w = K.shape# 所以,输出大小等于输入大小 𝑛ℎ×𝑛𝑤, 减去卷积核大小 𝑘ℎ×𝑘𝑤# 即:(𝑛ℎ−𝑘ℎ+1)×(𝑛𝑤−𝑘𝑤+1).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)# 实现二维卷积层
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 froward(self, x):return corr2d(x, self.weight) + self.bias# 简单应用:检测图像中不同颜色的边缘
X = torch.ones((6, 8))
X[:, 2:6] = 0
XK = torch.tensor([[1.0, -1.0]])# 输出Y中的1代表从白色到黑色的边缘,-1代表从黑色到白色的边缘
Y = corr2d(X, K)
Y# 卷积核K只能检验垂直边缘
corr2d(X.t(), K)# 学习由X生成的Y的卷积核
conv2d = nn.Conv2d(1, 1, kernel_size=(1, 2), bias=False)X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))for i in range(10):Y_hat = conv2d(X)l = (Y_hat - Y)**2conv2d.zero_grad()l.sum().backward()conv2d.weight.data[:] -= 3e-2 * conv2d.weight.gradif (i + 1) % 2 == 0:print(f'batch{i+1}, loss {l.sum():.3f}')conv2d.weight.data.reshape((1, 2))
  • 填充和步幅
    在这里插入图片描述
    这里不多说,就用这个PPT覆盖,这两种操作都是为了调整数据的维度。
    代码:

    # 填充和步幅
    # 在所有侧边填充1个像素import torch
    from torch import nndef comp_conv2d(conv2d, X):# 将输入张量 X 的形状进行调整,在原形状前添加两个维度,使其变为 (1, 1, 8, 8)X = X.reshape((1, 1) + X.shape)Y = conv2d(X)# 将卷积后的输出张量 Y 的形状进行调整,去除前两个维度(批量大小和通道数),只保留高度和宽度维度return Y.reshape(Y.shape[2:])conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)
    X = torch.rand(size=(8, 8))
    comp_conv2d(conv2d, X).shape# 填充不同的高度和宽度
    conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))
    comp_conv2d(conv2d, X).shape# 步幅的例子
    # 将高度和宽度的步幅设置为2
    conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
    comp_conv2d(conv2d, X).shape# 稍微复杂的例子
    conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
    comp_conv2d(conv2d, X).shape
    # 高:(8 + 2 * 0 - 3)/ 3 + 1
    # 宽:(8 + 2 * 1 - 5)/ 4 + 1
    # 一般使用填充和步幅是对称的。
    

    Q:核大小、填充、步幅 重要程度
    A:填充一般是核大小 - 1;步幅:通常为1更好,不选1的情况是因为计算量太大了。

  • 多输入和多输出通道
    到目前为止,仅展示了单个输入和单个输出通道的简化例子。 这使得我们可以将输入、卷积核和输出看作二维张量。
    在这里插入图片描述
    在实际中,处理多输出多输出通道的例子比较多,例如彩色图像具有标准的RGB通道来代表红、绿和蓝。
    代码:

# 多输入多输出通道
# 实现多输入通道互相关运算:
import torch
from d2l import torch as d2ldef corr2d_multi_in(X, K):# zip:对最外面的通道进行遍历;将输入数据 X 和卷积核 K 按通道进行配对,每一对 (x, k) 分别表示输入数据的一个通道和对应的卷积核通道。# d2l.corr2d(x, k):对每一对通道进行二维单通道的互相关运算,得到该通道的运算结果。# sum(...):将所有通道的运算结果进行逐元素相加,得到最终的多输入通道互相关运算结果。return sum(d2l.corr2d(x, k) for x, k in zip(X, K))# 验证互相关运算的输出:
# 输入数据 X:形状为 (2, 3, 3),表示有 2 个输入通道,每个通道的大小为 3x3。
# 卷积核 K:形状为 (2, 2, 2),表示有 2 个卷积核通道,每个通道的大小为 2x2。
X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]],[[1.0, 2.0], [3.0, 4.0]]])corr2d_multi_in(X, K)
# 计算多个通道的输出的互相关函数
# X 3d, K:4d
def corr2d_multi_in_out(X, K):# torch.stack 函数用于在指定维度上对张量进行拼接。这里将列表中的多个运算结果张量在第 0 维上进行拼接,形成一个新的张量。return torch.stack([corr2d_multi_in(X, k) for k in K], 0)# 通过对原始卷积核 K 进行简单的数值变换(分别加 0、加 1、加 2),生成多个不同的卷积核,然后将这些卷积核在第 0 维上进行堆叠,以模拟多个输出通道对应的卷积核。
K = torch.stack((K, K + 1, K + 2), 0)
K.shapecorr2d_multi_in_out(X, K)# 1 * 1 卷积
def corr2d_multi_in_out_1x1(X, K):c_i, h, w = X.shapec_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))X = torch.normal(0, 1, (3, 3, 3))
K = torch.normal(0, 1, (2, 3, 1, 1))Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
# assert ... < 1e6:使用 assert 语句进行断言,如果 Y1 和 Y2 的差值之和小于 \(10^6\),则断言通过,说明两种卷积方式得到的结果在一定误差范围内是等价的
assert float(torch.abs(Y1 - Y2).sum()) < 1e6
  • 汇聚层(池化层)
    卷积神经网络中,通常会在相邻的卷积层之间加入一个池化层,池化层可以有效的缩小参数矩阵的尺寸,从而减少最后连接层的中的参数数量。所以加入池化层可以加快计算速度和防止过拟合的作用。

    • 池化的原理或过程:pooling是在不同的通道上分开执行的(就是池化操作不改变通道数),且不需要参数控制。然后根据窗口大小进行相应的操作,一般有max pooling、average pooling等。
  • 池化的作用:

    • 特征不变性:池化操作是模型更加关注是否存在某些特征而不是特征具体的位置。其中不变形性包括平移不变性、旋转不变性和尺度不变性。
    • 特征降维(下采样):池化相当于在空间范围内做了维度约减,从而使模型可以抽取更加广范围的特征。同时减小了下一层的输入大小,进而减少计算量和参数个数。
    • 在一定程度上防止过拟合。(类似ReLU)
  • 汇聚层中的相关操作在这里插入图片描述
    代码:

    # 实现池化层的正向传播
    import torch
    from torch import nn
    from d2l import torch as d2ldef pool2d(X, pool_size, mode='max'):p_h, p_w = pool_sizeY = 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')# 填充和步幅
    X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
    X# 深度学习框架(pytorch)中的步幅与池化窗口的大小相同
    pool2d = nn.MaxPool2d(3)
    pool2d(X)# 填充和步幅可以指定
    pool2d = nn.MaxPool2d(3, padding=1, stride=2)
    pool2d(X)# 设定一个任意大小的矩形池化窗口,并分别设定填充和步幅的高度和宽度
    pool2d = nn.MaxPool2d((2, 3), padding=(1, 1), stride=(2, 3))
    pool2d(X)# 在每个输入通道上单独运算
    X = torch.cat((X, X + 1), 1)
    Xpool2d = nn.MaxPool2d(3, padding=1, stride=2)
    pool2d(X)
    

CNN的基本构造

以上所有部分是CNN中的组件,CNN可以概括为以下部分:
1 输入层
输入层接收原始图像数据。图像通常由三个颜色通道(红、绿、蓝)组成,形成一个二维矩阵,表示像素的强度值。

2 卷积和激活
卷积层将输入图像与卷积核进行卷积操作,然后,通过应用激活函数(如ReLU)来引入非线性。这一步使网络能够学习复杂的特征。

3 池化层
通过减小特征图的大小来减少计算复杂性(选择池化窗口内的最大值或平均值来实现)这有助于提取最重要的特征。

4 多层堆叠
CNN通常由多个卷积和池化层的堆叠组成,以逐渐提取更高级别的特征。深层次的特征可以表示更复杂的模式。

5 全连接和输出
最后,全连接层将提取的特征映射转化为网络的最终输出。这可以是一个分类标签、回归值或其他任务的结果。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词