[深度学习]卷积神经网络的概念,入门构建(代码实例)

# 不再任何人,任何组织的身上倾注任何的感情,或许这就是能活得更开心的办法 

0.写在前面:

卷积神经网络的部分在之前就已经有所接触,这里重新更全面地总结一下关于深度学习中卷积神经网络的部分.并且在这里对如何构建代码,一些新的思想和网络做出一点点补充,同时会持续更新一些注入残差网络等现代的卷积网络部分.

简言之,入门看这篇就行.........

1.关于卷积网络的概念:

在一些图片处理之类的问题中,我们不可避免地会碰到一些环境监测的工作,其实正常来说,神经网络读取的数据肯定是矩阵,张量,为了保证图像的严整性,我们需要把每个像素都抽象化为一个具体的数字,这就会带来两个问题:

1.对于图像数据来说,需要考虑的是一个上下文的环境,换句话说,例如对人物,车辆的识别工作,不可能对像素进行处理.

2.另一个角度来说,我们在之前的多层感知机中使用了展平层来处理三维数据,这样子处理以后不仅不符合逻辑,计算量也很大,因此要引入卷积神经网络的概念.

并且不只是图像,对于一些大预言模型的处理,我们需要对传输数据进行一定的画框,然后分批次进行读取. 另外就是我最近在座的生物信息分析的数据,更需要考虑到其中一个上下文的环境

例如有一段生物信息代码:
ACTUTCCTAA

例如这段生物信息为例,我们在做数据分析的时候不能盲目地转化为[0,1,0,1,1,3....]这样子的数组

比如这段信息,我们倾向于对每条数据划分,在检测某个位点的时候,需要考虑到前后文关系,比如我们考虑第三个碱基C,我们需要传递近来计算的特征则是[ACT].

好吧我得承认这个说法仍然足够抽象,用图形的方式来理解,就显得比较容易了

1.1.什么卷积层:

卷积层的这个卷积,在计算机图形学中的应用同样很广泛,而且概念上也是一样的.

其实严格来说,我们这个操作并不能称之为卷积操作,因为数学论文中的卷积操作需要额外的一个步骤就是倒转卷积核,在这里我们仅仅是对固定的层数和区域做运算而已.

在大一的时候,我们接触过滑动窗口算法,用来进行平滑的操作,,或者就像是我们在ps中进行图像处理的时候,所用到的污点修复工具, 也是一种卷积.

这其实就是卷积神经网络中的一部分,只不过严格来说这个操作被称之为pooling(池化)

首先我们要介绍一下卷积核 / 过滤器  / convolution kernel / filtering

就是一个小型矩阵,深度和被卷积对象匹配(为什么这里说被卷积对象而不是输入,这个和我们后面的一个叫做分离卷积的东西有关,当然这个是后话了)

过滤器是怎么样工作的,或者说卷积的数学原理是什么,差不多就这样,那个小的过滤矩阵被称之为核

然后类似滑动窗口的操作,只不过这次窗口被严格限制在了原本张量范围内,窗口的每个计算,对应着目标中一个数字的输出
(图片其实贴在上面了)

例如在这张图中,涉及到了边缘检测的一个简单示例

很好地展现了一个垂直的边缘检测器,这个检测器也是我们常用的类型了

还有一些其他的核

但是迄今为止,我们说到的东西仅仅止步于数学和图形学,神经网络呢???

别急,核心的数目很多,种类很多,能达到的效果都不一样,如果能检测到更适合我们手中样本的数据,就是训练呗.

比如这样一个9*9大小的卷积核心,我们可以设置参数w1--w9, 后面如果有需要还可以使用bias还有激活函数之类的东西进行处理,紧接着根据参数开始训练,这就是我们的一个核心思路.

1.2.卷积层的相关参数

里我们展示一个比较完整的卷积神经网路的图像.

我们可以看到,现在我们的卷积是在三维上进行处理的,每个核的卷积结果都是一个二维矩阵.

如图所示

而具体的计算方法可以是这样子的

在这种比较正常的卷积操作中,我们设置一些相关的参数,并且将他们的名称作为约定俗成

有关参数
n:输入的长宽度

f:卷积核的长宽

s;卷积运算的移动步长

p:填充的长度(注意这个填充长度,不同的材料中表示的含义不太一样)

num:卷积核的数目,卷积核的数目在一般情况下决定了输出的三维张量的频道数量(频道是深度的另一种称呼)

这是一张纯三维的展示图像,途中我们可以看到这里又不止一个卷积核,每个卷积核都能得到一个结果.这里其实存在一个物理意义,每个卷积核都是提取自己需要的特征,比如A获取的是垂直边缘,B获取的是水平边缘.叠加起来就是我们需要的东西(比如轮廓图)

填充和步长
在进行卷积的时候,可以注意到一个问题,随着卷积的增加,虽然特征提取了,但是输出的长宽是肉眼可见的不断变小的.并且步长也会直接影响输出的因素.

而且,卷积也会导致对于边缘部分的采集不充分,所以对于这种情况,我们的处理方法是在原本的输入基础上,在四周填充长度为p的部分,使得卷积出来的结果大小发生一些变化

如图所示

步长

这里还有一个计算公式,通过这个计算公式我们可以得到关于输入和输出的尺寸的区别

1.3. 什么是池化层

池化层的翻译怎么说呢....汇聚层这个翻译其实更符合语意的

池化层其实个人感觉和卷积没有本质上的区别,都是对局部特征进行提取,然后够获取特征部分,压缩输入,节约计算.或者一些其他的奇奇怪怪的功能.

但是真说计算上的区别,那么就是池化层不需要任何训练参数,比较常用的两个池化核心是最大池化和平均池化. 另外,池化层的特点是,每个池化核心只有一层,这就代表了池化层是不会对输入的通道产生影响,而至少影响尺寸的大小

(另外在这里补充一点通常我们在说有关卷积神经网络的时候,所说的"尺寸"指的是二维尺寸,对于第三维度的高度(我们俗称的Z轴),我们称之为通道数目)

如果说在实际运行代码上的区别,我还记得一个特点就是如果不指定,那么池化层默认自己的步长和核心的尺寸大小一致

如图所示,这个就是池化层中被称之为平均池化层的操作,将自己所在"窗口"内的部分进行加和求平均,剩下最大池化层是干什么应该就不用我多说了.

并且在实际应用中,卷积和池化层中的尺寸计算通常会向下取整,以保持输出结果的整数尺寸

这是因为在卷积和池化操作中,输入特征图的尺寸往往无法被池化窗口或卷积核完全整除。为了保持输出特征图的尺寸与输入特征图的对应关系,并避免信息丢失或产生不连续的特征图,通常会将计算结果向下取整到最接近的整数

1.4,关于CNN的代码简单展示

我们就不使用具体的pytorch的函数和方法来进行过多的展示了,这里直接使用Sequential容器来进行一点简单的展示

# padding如果没有默认为0 ,stride如果不存在默认为1
net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5, padding=2),  输出结果为[6,27,27]
    nn.Sigmoid(),                               进行sigmoid操作,将范围固定在0-1的范围之中
    nn.AvgPool2d(kernel_size=2, stride=2),      输出结果为[6,13,13](原本计算结果为13.5,但是向下取整)
    nn.Conv2d(6, 16, kernel_size=5),            输出结果为[16,.....]
    nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),      输出结果为[16,.....] 嘛.计算结果差不多就这样不算了
    nn.Flatten(),  #------------------自此开始重新将数据进行展平(如果是大批量数据会保留第一个维度的存在)
    nn.Linear(16 * 5 * 5, 120), 
    nn.Sigmoid(),
    nn.Linear(120, 84), 
    nn.Sigmoid(),
    nn.Linear(84, 10))

2.关于pytorch中的一些实现代码,参数,以及注意问题

2.1关于具体的神经网络层的api

首先说明一下构建各种类型的卷积层的方法

# 2d的卷积层
nn.Conv2d(in_channels, out_channels, kernel_size=?,padding=?,stride=?)

# 2d的最大池化层
nn.MaxPool2d( kernel_size=?,padding=?,stride=?)

# 2d的平均池化层
nn.AvgPool2d( kernel_size=?,padding=?,stride=?)

上面所展示的内容全部都是单一维度的2d,其实还有其他的维度卷积,这里就不加一解释了

里面的参数就不用我多说了 

另外这里要单独说明另一个api:ModuleList

2.2关于神经网络层的初始化

神经网络的初始化这里其实要说明一点,就算我们不进行初始化操作,pytorch也会自动进行一些小范围的初始化操作.不过为减少迭代和训练次数,还是有必要的.

因为上面我们引入了有关于ModuleList的东西,所以对于这个使用方法更新的方式,我们需要一点更新,这个是一个具体案例,负责初始化一个携带有ModuleList的块.

def init_weight(m):
            if type(m)==nn.Linear or type(m)==nn.Conv2d:
                torch.nn.init.constant_(m.bias,0.1)
                torch.nn.init.normal_(m.weight,std=0.01)
            elif type(m)==nn.ModuleList:
                for i,subLayer in enumerate(m):
                    torch.nn.init.constant_(subLayer.bias,0.1)
                    torch.nn.init.normal_(subLayer.weight,std=0.01)
        self.apply(init_weight)

 如图所示(注意,无论是sequential容器构建的神经网络,还是通过class类进行构建的,只要是成功继承了nn.Module的神经网络结构部分, 都可以使用这种apply内置接口来对模型,都可以用这种方式来初始化函数,并且对每个模型都进行操作)

对于携带ModuleList的内部类中,我们需要enmuerate函数遍历的操作,然后对内部进行重新的修整.

2.3需要注意的问题

需要注意的问题就是:(长期补充)

1.ModuleLis虽然也是一种容器,但是它真的只是一个存储容器,因此我们无法通过ModuleList进行直接的先前传播计算

这些块在计算的时候会被认为是注册了的层,所以反向传播是可以正常训练的

但是正向转播的时候,这个容器本身是不能进行任何前向传播计算的,需要单独拆出来进行遍历

至于为什么pytorch总能找到哪些神经网络层参与了计算

底层来说,是反向传播机制. 代码上说, pytorch中特有的跟踪机制吧.....

3.关于一些现代神经网络还有内部的实现

其实关于卷积层和卷积层构建的神经网络,其实和密集层,激活函数本质上没什么区别.

但是随着现代神经网络和科学的发展,无论是神经网络本身还是更多的一些需求,我们都扩展出了一些现代的概念和函数架构,在这里我们会简单展示"VGG","NiN","GoogleNet"这三个块形式的结构,并且会在后面拓展一些关于残差等等的东西.

(1)VGG,以及块级别操作的接口

VGG这个名字感觉好怪每次都读成VCC了,.......

其实VGG块没什么太多好讲的,其实本质就是一个简单的卷积神经网络块,通过参数调整来保证无论是输入还输出部分,都能保证通道数目一致.

代码实例如图所示

def vgg_block(num_convs, in_channels, out_channels):
    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)

但是这里主要需要解释的就是块级别的网络层,也就是block,这个思想其实就是前向传播实现的,也就是实现了forward接口来实现的结果.至于好处我个人认为有以下两种

  1. 模块化和可复用性:通过将一组层组织成块的形式,可以将网络的结构划分为更小、更易于管理的模块。这种模块化的设计使得网络的构建更加清晰和可复用。每个块都可以被视为一个独立的单元,可以在不同的网络架构中使用,从而提高代码的可重用性。

  2. 实现复杂网络架构:块结构可以用于实现一些复杂的网络架构,如残差网络(ResNet)、Inception 网络、U-Net 等。这些网络往往由多个重复的块组成,通过简单的块结构的组合和堆叠,可以构建出具有强大表达能力的网络架构。

(2)NiN

NiN块(network in network)神经网络,其实说真我自己都没有太好的理解

# NiN和其他的网络有一点小小的区别

# 一般的NiN卷积层是一个普通卷积层,一个池化层,两个1*1但是不改变最终尺寸的卷积层

# 前两个就是正常的卷积操作,最后两个池化层的作用就是增加更多的非线性变换

# 提升特征的可提取度

# 卷积层举例:
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())

(3)GoogleNet,以及并行处理,还有inception函数

在进行最后的神经网络计算的时候,我们首先要介绍一个概念,并行操作

其实对于这个操作,其实源自于某一个科研项目(这里指的是我自己),需要处理一个很有特点的数据

一个批次(张量的数据)一共有四个特征,差不多是这个格式

[
[1,2,3,4,5,6,7,8,9,1,2,3,4],[1,2,3,4,5,6,7,8,9,1,2,3,4],
[1,2,3,4,5,6,7,8,9,1,2,3,4],[1,2,3,4,5,6,7,8,9,1,2,3,4]
]

每个特征都有自己的特征,而且科研内容的背景环境是完全不同的,所以我们不能使用2d来进行操作

我们需要实现的核心思路就是

首先 , 遍历得到里面的每一个子模块

然后每个子模块计算输入的一部分(张量高级语法),得到一个单独的输出结果

再把这些单独的输出结果重新拼合到张量,作为下一个输入

首先我有一个自己的处理函数写的比较蠢

for i in range(4):
            output.append(self.Convs1[i](X[:,i:i+1,:].float()))
        X=torch.cat(output,dim=1) #这个dim等于1的压缩机制是啥来着???

通过这种方式,我们尝试实现了最简单的Inception模块

(不过在这里,我们的实现方式是ModuleList模块)

class MyNet(nn.Module):
    def __init__(self):
        super().__init__()

        #创建一个多输入的卷积层
        self.Convs1=nn.ModuleList()
        #内部传入四个卷积层,卷积以后对每一个层的结果是一个[4,9]
        self.Convs1.append(nn.Conv1d(1,1,padding=0,kernel_size=5,stride=1))
        self.Convs1.append(nn.Conv1d(1,1,padding=0,kernel_size=5,stride=1))
        self.Convs1.append(nn.Conv1d(1,1,padding=0,kernel_size=5,stride=1))
        self.Convs1.append(nn.Conv1d(1,1,padding=0,kernel_size=5,stride=1))
        # 13 - x +1=9
        #创建一个多输入的密集层
        self.Convs2=nn.ModuleList()
        #内部传入四个密集层           密集计算以后每一个层的结果都是[4,3]
        self.Convs2.append(nn.Linear(9,3))
        self.Convs2.append(nn.Linear(9,3))
        self.Convs2.append(nn.Linear(9,3))
        self.Convs2.append(nn.Linear(9,3))

        #展平层  [4,3]---->[12]
        self.Flatten=nn.Flatten()
        #最后一个密集层做处理
        self.Linear1=nn.Linear(12,1)
        
        self.init()
   #13 - 5 +c1 = 9
    #向前传播计算逻辑
    def forward(self,X):
        output=[]

        for i in range(4):
            output.append(self.Convs1[i](X[:,i:i+1,:].float()))
        X=torch.cat(output,dim=1) #这个dim等于1的压缩机制是啥来着???

        output=[]
        for i  in range(4):
            output.append(self.Convs2[i](X[:,i:i+1,:].float()))
        X=torch.cat(output,dim=1) #这个dim等于1的压缩机制是啥来着???

        X=self.Flatten(X)

        X=self.Linear1(X)
        return (X)
    
    # 权重初始化函数
    def init(self):
        def init_weight(m):
            if type(m)==nn.Linear or type(m)==nn.Conv2d:
                torch.nn.init.constant_(m.bias,0.1)
                torch.nn.init.normal_(m.weight,std=0.01)
            elif type(m)==nn.ModuleList:
                for i,subLayer in enumerate(m):
                    torch.nn.init.constant_(subLayer.bias,0.1)
                    torch.nn.init.normal_(subLayer.weight,std=0.01)
        self.apply(init_weight)

net=MyNet()

(电脑没电了,未完,有空补充)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mac 安装 protobuf 和Android Studio 使用

1. 安装,执行命令 brew install protoc 2. Mac 错误提示:zsh: command not found: brew解决方法 解决方法:mac 安装homebrew, 用以下命令安装,序列号选择中科大(1)或 阿里云 /bin/zsh -c "$(curl…

掌握Shell:从新手到编程大师的Linux之旅

1 shell介绍 1.1 shell脚本的意义 1.记录命令执行的过程和执行逻辑,以便以后重复执行 2.脚本可以批量处理主机 3.脚本可以定时处理主机 1.2 脚本的创建 #!/bin/bash # 运行脚本时候执行的环境1.3 自动添加脚本说明信息 /etc/vimrc # vim主配置文件 ~/.vimrc # 该…

C语言开发者的利器:gcc编译命令指南

本文主要介绍gcc编译c语言过程,以及常用命令 文章目录 C语言编译过程1. 预处理(Preprocessing):2. 编译(Compiling):3. 汇编(Assembling):4. 链接&#xff08…

适用于 Windows 的 10 个最佳视频转换器:快速转换高清视频

您是否遇到过由于格式不兼容而无法在您的设备上播放视频或电影的情况?您想随意播放从您的相机、GoPro 导入的视频,还是以最合适的格式将它们上传到媒体网站?您的房间里是否有一堆 DVD 光盘,想将它们转换为数字格式以便于播放&…

深度模型压缩研究回顾

深度模型压缩研究回顾 作者:安静到无声 个人主页 目录 深度模型压缩研究回顾推荐专栏 在本节中,主要介绍了目前主流的深度神经网络压缩与加速方法,主要包括轻量化网络设计、参数量化、知识蒸馏、模型剪枝和硬件加速等,其中模型剪…

Linux系统中sh脚本编写

文章目录 Linux系统中sh脚本编写1.在编写sh脚本前了解一下基本语法1.1 if语句单分支双分支多分枝 1.2 for语法 2. 自己写的demo :自动部署前端项目 (自动拉取代码,打包,部署nginx)3.定时执行 shell脚本 Linux系统中sh脚…

性能分析工具的使用

文章目录 1. 数据库服务器调优的步骤2. 查看系统性能参数3. 统计SQL的查询成本:last_query_cost4. 定位执行慢的SQL:慢查询日志4.1 开启slow_query_log4.2 修改long_query_time阈值4.3 查看慢查询数目4.4 慢查询日志分析工具:mysqldumpslow 5…

work环境配置

1.计算机右键找到属性 2.配置环境变量 3.新加环境变量 4.修改环境变量path .bat文件内容 php ApplicationsChatstart_register.php ApplicationsChatstart_gateway.php ApplicationsChatstart_businessworker.php pause

App测试入门

App测试基础知识 App测试,是指对移动应用软件(如手机app、平板app等)进行全面和系统的测试,以确保其功能、性能、安全性、稳定性、兼容性等方面能满足用户的使用需求和期望。 App常见运行系统 IOS系统: IOS系统是苹果公…

MLC-LLM 支持RWKV-5推理以及对RWKV-5的一些思考

自从2023年3月左右,chatgpt火热起来之后,我把关注的一些知乎帖子都记录到了这个markdown里面,:https://github.com/BBuf/how-to-optim-algorithm-in-cuda/tree/master/large-language-model-note ,从2023年3月左右到现…

编写程序,要求输入x的值,输出y的值。分别用(1)不嵌套的if语句(2)嵌套的if语句(3)if-else语句(4)switch语句。

编写程序,要求输入x的值,输出y的值。分别用(1)不嵌套的if语句(2)嵌套的if语句(3)if-else语句(4)switch语句。 选择结构是编程语言中常用的一种控制结构&…

OpenGL 的学习之路-4(变换)

三大变换:平移、缩放、旋转(通过这三种变换,可以将图像移动到任意位置) 其实,这背后对应的数学在 闫令琪 图形学课程 中有过一些了解,所以,理解起来也不觉得很困难。看程序吧。 1.画三角形&am…

量化交易:公司基本面的量化

公司的基本面因素一直具备滞后性,令基本面的量化出现巨大困难。而从上市公司的基本面因素来看,一般只有每个季度的公布期才会有财务指标的更新,而这种财务指标的滞后性对股票表现是否有影响呢?如何去规避基本面滞后产生的风险呢&a…

网站SEO优化

网站SEO优化 浏览722 一、合理的title、description、keywords 搜索对着三项的权重逐个减小,title值强调重点即可;description把页面内容高度概括,不可过分堆砌关键词;keywords列举出重要关键词。 1、title title,…

【教3妹学编程-java基础6】详解父子类变量、代码块、构造函数执行顺序

-----------------第二天------------------------ 本文先论述父子类变量、代码块、构造函数执行顺序的结论, 然后通过举例论证,接着再扩展,彻底搞懂静态代码块、动态代码块、构造函数、父子类、类加载机制等知识体系。 温故而知新&#xff…

ZYNQ_project:LCD

模块框图: 时序图: 代码: /* // 24h000000 4324 9Mhz 480*272 // 24h800000 7084 33Mhz 800*480 // 24h008080 7016 50Mhz 1024*600 // 24h000080 4384 33Mhz 800*480 // 24h800080 1018 70Mhz 1280*800 */ module rd_id(i…

【MySQL】InnoDB和MyISAM区别详解(MySQL专栏启动)

📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于…

OpenCV C++ 图像 批处理 (批量调整尺寸、批量重命名)

文章目录 图像 批处理(调整尺寸、重命名)图像 批处理(调整尺寸、重命名) 拿着棋盘格,对着相机变换不同的方角度,采集十张以上(以10~20张为宜);或者棋盘格放到桌上,拿着相机从不同角度一通拍摄。 以棋盘格,第一个内焦点为坐标原点,便于计算世界坐标系下三维坐标; …

【代码随想录】算法训练计划24

回溯模板: 1、77. 组合 题目: 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 思路: 很经典的回溯,也是回溯中模板的经典应用,因而是回溯中的简单题…

97.qt qml-自定义Table之实现ctrl与shift多选

我们之前实现了:93.qt qml-自定义Table优化(新增:水平拖拽/缩放自适应/选择使能/自定义委托)-CSDN博客 实现选择使能的时候,我们只能一行行去点击选中,非常麻烦,所以本章我们实现ctrl多选与shift多选、 所以在Table控件新增两个属性: 1.实现介绍 ctrl多选实现原理:当我…
最新文章