动手学深度学习—卷积神经网络(原理解释+代码详解)

news2024/11/16 15:43:16

目录

  • 1. 从全连接层到卷积层
  • 2. 图像卷积
    • 2.1 互相关运算
    • 2.2 卷积层
    • 2.3 图像中目标的边缘检测
    • 2.4 学习卷积核
    • 2.5 特征映射和感受野
  • 3. 填充和步幅
    • 3.1 填充
    • 3.2 步幅
  • 4. 多输入多输出通道
    • 4.1 多输入通道
    • 4.2 多输出通道
    • 4.3 1×1卷积核
  • 5. 汇聚层
    • 5.1 最大汇聚层和平均汇聚层
    • 5.2 填充和步幅
    • 5.3 多个通道

1. 从全连接层到卷积层

  • 多层感知机对图像处理是百万维度,模型不可实现。
  • 如果要在图片中找到某个物体,寻找方法应该和物体位置无关。

适合计算机视觉的神经网络架构:

  1. 平移不变性:不管检测对象出现在图像中的哪个位置,神经网络前几层应该对相同图像区域有相似的反应。
  2. 局部性:神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系。

2. 图像卷积

2.1 互相关运算

严格来说,卷积层所表达的运算其实是互相关运算。

不同颜色所选的区域与同一个卷积核做互相关运算,最后得到输出。
在这里插入图片描述在这里插入图片描述
同理,卷积核滑动进行互相关运算。最终得到高度为2,宽度为2的输出。
在这里插入图片描述

输出大小:在这里插入图片描述

"""
    定义corr2d函数:
    1、该函数接受输入张量X和卷积核张量K,并返回输出张量Y
    2、输出大小 = 输入大小n(k)×n(w) - 卷积核大小k(h)×k(w)
    3、即:(n(k)-k(h)+1) × (n(w)-k(w)+1 )
"""
import torch
from torch import nn
from d2l import torch as d2l

def corr2d(X, K): #@save
    """计算二维互相关运算"""
    # 卷积核的高度h和宽度w,K指卷积核Kernel
    h, w = K.shape
    
    # 设置输出Y的大小,用0进行填充
    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

对上图进行验证
在这里插入图片描述

2.2 卷积层

定义卷积层Conv2D:

  1. 卷积层对输入和卷积核权重进行互相关运算;
  2. 并在添加标量偏置之后产生输出。
"""
    定义卷积层Conv2D:
    1、卷积层对输入和卷积核权重进行互相关运算;
    2、并在添加标量偏置之后产生输出。
"""
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))
     
    # corr2d(X, K)
    def forward(self, x):
        return corr2d(x, self.weight) + self.bias

2.3 图像中目标的边缘检测

# 卷积层的一个简单应用:通过找到像素变化的位置,来检测图像中不同颜色的边缘。
# 1、构造一个6×8像素的黑白图像
X = torch.ones((6, 8))
X[:, 2:6] = 0
X

在这里插入图片描述

# 2、我们构造一个高度为1、宽度为2的卷积核K(水平相邻元素相同输出为0)
K = torch.tensor([[1.0, -1.0]])

Y = corr2d(X, K)
Y

在这里插入图片描述

2.4 学习卷积核

"""
    由X生成Y的卷积核:
    1、构造一个卷积层,并将其卷积核初始化为随机张量;
    2、在每次迭代中,比较Y与卷积层输出的平方误差,然后计算梯度来更新卷积核;
    3、使用内置的二维卷积层,并忽略偏置。
"""
# 构造一个二维卷积层,它具有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 # 学习率

# 进行训练,轮数为10。计算梯度并进行更新,输出loss
for i in range(10):
    # 定义损失函数:交叉熵损失
    Y_hat = conv2d(X)
    l = (Y_hat - Y) ** 2
    
    # 梯度置零
    conv2d.zero_grad()
    l.sum().backward()
    # 迭代卷积核
    # 梯度更新:w = w - lr * w'
    conv2d.weight.data[:] -= lr * conv2d.weight.grad
    # 每隔2轮输出一次
    if(i + 1) % 2 ==0:
        print(f'epoch{i+1}, loss{l.sum():.3f}')

这里可以看到学习的卷积核接近之前边缘检测的卷积核。
在这里插入图片描述

2.5 特征映射和感受野

  1. 输出的卷积层有时被称为特征映射。
  2. 在卷积神经网络中,对于某一层的任意元素x,其感受野是指在前向传播期间可能影响x计算的所有元素。

3. 填充和步幅

  • 问题一:应用了连续卷积,最终得到的输出远小于输入大小,使得原始图像的边界丢失了许多有用信息,我们希望输入大小和输出大小相同?
    解决填充:在输入图像的边界填充元素(通常填充元素是0)
  • 问题二:有时原始的输入分辨率十分冗余,我们可能希望大幅降低图像的宽度和高度?
    解决步幅:设置卷积核滑动的步幅来减少采样次数
  • 问题三:卷积核为什么一般选择奇数
    解决:保持空间维度的同时,我们可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列。

3.1 填充

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

在这里插入图片描述在这里插入图片描述

"""
    填充:
    1、输入给定8×8,输出要求8×8
    2、卷积核的大小为3×3,所有侧边填充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)
    
    # 省略前两个维度:批量大小和通道(1, 1, 8, 8)
    return Y.reshape(Y.shape[2:])

conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)
X = torch.rand(size=(8, 8))
print(X)
print(conv2d)
print(comp_conv2d(conv2d, X))
comp_conv2d(conv2d, X).shape

在这里插入图片描述

# 卷积核为5×3时,为了使输入和输出相同,高度填充2,宽度填充1
conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape

在这里插入图片描述

3.2 步幅

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

在这里插入图片描述

"""
    步幅:
    1、每次滑动元素的数量;
    2、为了高效计算或是缩减采样次数,卷积窗口可以跳过中间位置,每次滑动多个元素。
"""
# 将高度和宽度的步幅设置为2,从而将输入的高度和宽度减半
# (8 + 2 - 3) / 2 = 4
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
comp_conv2d(conv2d, X).shape

在这里插入图片描述

4. 多输入多输出通道

4.1 多输入通道

当输入包含多个通道时,需要构造一个与输入数据具有相同输入通道数的卷积核,以便与输入数据进行互相关运算。

在这里插入图片描述

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

# 构造输入张量X和核张量K,以验证互相关运算的输出
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_muti_in(X, K)

在这里插入图片描述

4.2 多输出通道

多输出通道:

  1. 在最流行的神经网络架构中,随着神经网络层数的加深,我们常会增加输出通道的维数,通过减少空间分辨率以获得更大的通道深度。
  2. 将每个通道看作对不同特征的响应。
# 实现一个计算多个通道的输出的互相关函数
def corr2d_multi_in_out(X, K):
    # 迭代“K”的第0个维度,每次都对输入“X”执行互相关运算。
    # 最后将所有结果都叠加在一起
    return torch.stack([corr2d_multi_in(X, k) for k in K], 0)

# 通过将核张量K与K+1(K中每个元素加1)和K+2连接起来,构造了一个具有3个输出通道的卷积核。
K = torch.stack((K, K + 1, K + 2), 0)

# (输出通道数,输入通道数,高度,宽度)
K.shape

在这里插入图片描述

4.3 1×1卷积核

  • 1×1卷积核被经常用来改变通道,相当于全连接层
  • 可以对输入和输出的形状进行调整
# 使用全连接层实现1×1卷积
def corr2d_multi_in_out_1x1(X, K):
    c_i, h, w = X.shape
    # K:(输出通道,输入通道,高度,宽度)
    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))

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 float(torch.abs(Y1 - Y2).sum()) < 1e-6
print(Y1)
print(Y2)

在这里插入图片描述

5. 汇聚层

通过逐渐聚合信息,生成越来越粗糙的映射,最终实现学习全局表示的目标,同时将卷积图层的所有优势保留在中间层。
汇聚(pooling)层(也叫做池化层):

  1. 降低卷积层对位置的敏感性
  2. 降低对空间降采样表示的敏感性

5.1 最大汇聚层和平均汇聚层

汇聚层与卷积层的原理大体相似,只不过把互相关运算换成求最大值或者求平均值

在这里插入图片描述
在这里插入图片描述

# 最大汇聚层和平均汇聚层
"""
    定义汇聚层:
    1、设置汇聚层与输出的大小
    2、设置模式:大汇聚层和平均汇聚层
"""
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的高度和宽度
    Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
    
    # 进行遍历,相当于对矩阵的局部区域[i:i+p_h, j:j+p_w]求最大值/平均值
    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()
            if mode == 'avg':
                Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
    return Y

# 构建输入张量X,验证二维最大汇聚层输出
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
print(X)
print('最大汇聚层:\n', pool2d(X, (2, 2)))
print('平均汇聚层:\n', pool2d(X, (2, 2), 'avg'))
# print(f'最大汇聚层:{'\n'+pool2d(X, (2, 2))}')
# print(f'平均汇聚层:{pool2d(X, (2, 2), 'avg')}') 

在这里插入图片描述

5.2 填充和步幅

与卷积层一样,汇聚层也可以改变输出形状

# 构造了一个输入张量X,它有四个维度,其中样本数和通道数都是1
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
X

在这里插入图片描述

5.3 多个通道

多个通道:

  1. 在处理多通道输入数据时,汇聚层在每个输入通道上单独运算,而不是像卷积层一样在通道上对输入进行汇总
  2. 汇聚层的输出通道数与输入通道数相同。
"""
    多个通道:
    1、在处理多通道输入数据时,汇聚层在每个输入通道上单独运算,而不是像卷积层一样在通道上对输入进行汇总。
    2、汇聚层的输出通道数与输入通道数相同。
"""
X = torch.cat((X, X + 1), 1)
X
pool2d = nn.MaxPool2d(3, padding=1, stride=2)
pool2d(X)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/832123.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Stable Diffusion - SDXL 模型测试 (DreamShaper 和 GuoFeng v4) 与全身图像参数配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132085757 图像来源于 GuoFeng v4 XL 模型&#xff0c;艺术风格是赛博朋克、漫画、奇幻。 全身图像是指拍摄对象的整个身体都在画面中的照片&…

Bean的实例化方法

目录 1.工厂模式通常有三种形态&#xff1a; 2.简单工厂 2.1 静态工厂 2.1通过factory-bean实例化 2.3通过FactoryBean接口实例化 3.测试 关于容器的使用 3.1获得spring文件方式 3.2getBean方式 4.关闭容器 1.工厂模式通常有三种&#xff1a; 第一种&#xff1a;简单工…

二叉树题目:叶子相似的树

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;叶子相似的树 出处&#xff1a;872. 叶子相似的树 难度 3 级 题目描述 要求 考虑一个二叉树上所有的叶子&…

消息队列项目(1)

概念 这里的消息队列, 大致上就是一个生产者消费者模型. 我这个消息队列是仿照 RabbitMQ 的实现原理来进行编写的 需求分析 有几个核心的概念: 生产者(Producer)消费者(Consumer)中间人(Broker)发布(Publish) :生产者向中间人投递消息的过程订阅(Subcribe) :记录哪些消费者…

Java面试题 如何提高自己的算法?

练习一&#xff1a;飞机票 需求:机票价格按照淡季旺季、头等舱和经济舱收费、输入机票原价、月份和头等舱或经济舱。 按照如下规则计算机票价格&#xff1a;旺季&#xff08;5-10月&#xff09;头等舱9折&#xff0c;经济舱8.5折&#xff0c;淡季&#xff08;11月到来年4月&a…

【Java可执行命令】(十五)Java进程状态信息获取工具 jps:获取和监控Java进程的状态信息 ~

Java可执行命令之jps 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 语法格式3.2 可选参数&#xff1a;-q3.3 可选参数&#xff1a;-m3.4 可选参数&#xff1a;-l3.5 可选参数&#xff1a;-v3.6 可选参数&#xff1a;-V 4️⃣ 应用场景&#x1f33e; 总结 1️⃣ 概念 JPS&#xff…

【BEV感知】1-BEV感知算法介绍

1-BEV感知算法介绍 1 什么是BEV感知算法&#xff1f;1.1 什么是BEV&#xff1f;1.2 什么是感知&#xff1f;1.3 什么是算法&#xff1f;1.4 什么是BEV感知&#xff1f; 1 什么是BEV感知算法&#xff1f; 1.1 什么是BEV&#xff1f; Bird’s-Eye-View&#xff0c;尺度变化小、…

客户端电脑使用 FTP的Cadence_CIS库方法说明 (下)

简介&#xff1a;随着企业的规模扩大&#xff0c;硬件工程师的增多&#xff0c;使用统一服务器上的库管理&#xff0c;可以减少设计错误&#xff0c;提高效率。 使用在FTP上布局Cadence_CIS库&#xff0c;是目前的主流的做法之一&#xff1b; 本文方法&#xff0c;用于已经配置…

【网络基础进阶之路】一文弄懂TCP的三次握手与四次断开

系列文章&#xff1a; 【网络基础进阶之路】路由器间的静态综合详解 文章目录&#xff1a; 一、TCP协议 二、三次握手——建立连接 三、四次断开——结束连接 一、TCP协议 TCP是一种面向广域网的通信协议&#xff0c;目的是在跨越多个网络通信时&#xff0c;为两个通信端…

如何利用闭环思维解决企业营销问题

彼得圣吉在他的畅销书《第五项修炼》一书中主要介绍了“系统思考”的第五项修炼&#xff0c;系统思考的方法有三个基本元件&#xff1a;不断增强的回馈&#xff0c;反复调节的回馈&#xff0c;和时间滞延。其不断增强的回馈其实就是增强闭环理论&#xff0c;其在企业管理中有很…

嘿嘿嘿~卷王来喽~今天我被卷s了,谁也白想活zhuo!

前言 嘿嘿嘿~热乎乎的卷王来喽&#xff0c;卷呀&#xff01;你们怎么不卷&#xff01;&#xff08;啪&#xff01;电脑一合&#xff09;&#xff0c;好&#xff01;今天&#xff0c;我卷了&#xff0c;我得卷s&#xff0c;但是&#xff01;我被卷了&#xff0c;你们也白想活着…

极狐GitLab 10 年开源研发管理实践:4 个核心步骤突破效能瓶颈

目录 研发效能实践的痛点 4 步击破痛点&#xff0c;提升研发效能 1. 明确目标 2. 优化研发流程 ➤ 2.1 文档先行 ➤ 2.2 可追踪的任务 ➤ 2.3 自动化工作流 3. 注重代码质量 ➤ 3.1 代码门禁&#xff1a;严格要求&#xff0c;提前报错 ➤ 3.2 合并请求&#xff1a;代…

Netty 入门指南

文章目录 前言Netty介绍Netty发展历程Netty核心组件实现HTTP服务器总结 前言 上文《BIO、NIO、IO多路复用模型详细介绍&Java NIO 网络编程》介绍了几种IO模型以及Java NIO&#xff0c;了解了在网络编程时使用哪种模型可以提高系统性能及效率。即使Java NIO可以帮助开发人员…

【java】【maven】【高级】MAVEN聚合继承属性等

目录 1、模块开发与设计 2、聚合 2、继承 3、属性 4、版本管理 5、资源配置 6、多环境配置 7、多环境开发配置 8、跳过测试 9、私服 前言&#xff1a;maven的高级使用包含分模块开发与设计、聚合、继承、属性、版本管理、资源配置、多环境配置、多环境开发配置、跳过…

软件测试缺陷报告

缺陷报告是描述软件缺陷现象和重现步骤地集合。软件缺陷报告Software Bug Report&#xff08;SBR&#xff09;或软件问题报告Software Problem Report&#xff08;SPR&#xff09; 作用&#xff1a;缺陷报告是软件测试人员的工作成果之一&#xff0c;体现软件测试的价值缺陷报…

如何知道企业是否办理过等保备案?哪里可以查询?

对于等保政策细节&#xff0c;大家还存在很多疑问&#xff0c;例如有人在问&#xff0c;如何知道企业是否办理过等保备案&#xff1f;哪里可以查询&#xff1f;今天我们就来简单聊聊&#xff0c;仅供参考。 如何知道企业是否办理过等保备案&#xff1f; 一般企业办理过等保备案…

RISC-V基础指令之shift移动指令slli、srli、srai、sll、srl、sra

RISC-V的shift指令是用于对一个寄存器或一个立即数进行位移运算&#xff0c;并将结果存放在另一个寄存器中的指令。位移运算就是把一个操作数的每一位向左或向右移动一定的位数&#xff0c;得到一个新的位。RISC-V的shift指令有以下几种&#xff1a; slli&#xff1a;左逻辑位…

8.3 作业

整理思维导图 2. 递归实现&#xff0c;输入一个数&#xff0c;输出这个数的每一位 #include <myhead.h> void fun(int t) {if(t 0) return;fun(t/10);printf("%d\n",t%10); } int main(int argc,const char *argv[]) {int t1623809; fun(t);return 0; } 3.递…

虹科方案 | 虹科AR助力汽车产业降本增效!

虹科AR远程解决方案 将高性能的Vuzix AR眼镜与工业远程软件相结合&#xff0c;一线员工使用AR眼镜呼叫专家&#xff0c;由远程专家进行诊断并给出建议&#xff0c;支持一线员工与远程专家实时语音视频交互、AR标注指引、发送文件图片并进行会议录制&#xff0c;帮助一线员工解放…

Linux--对同一个文件分别执行r和w指令,其得到的fd一样吗?

代码&#xff1a; #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main() {int fdopen("log.txt",O_WRONLY|O_CREAT|O_TRUNC);printf("fd_w: %d\n",fd);fdopen("log.txt",O_…