【入门】5分钟了解卷积神经网络CNN是什么

news2024/10/6 10:26:32

本文来自《老饼讲解-BP神经网络》https://www.bbbdata.com/

目录

  • 一、卷积神经网络的结构
    • 1.1.卷积与池化的作用
    • 2.2.全连接层的作用
  • 二、卷积神经网络的运算
    • 2.1.卷积层的运算
    • 2.2.池化的运算
    • 2.3.全连接层运算
  • 三、pytorch实现一个CNN例子
    • 3.1.模型的搭建
    • 3.2.CNN完整训练代码

CNN神经网络常用于图片识别,是深度学习中常用的模型。
本文简单快速了解卷积神经网络是什么东西,并展示一个简单的示例。

一、卷积神经网络的结构

一个经典的卷积神经网络的结构如下:
卷积神经网络
C代表卷积层,P代表池化层,F代表全连接层。
卷积神经网络主要的、朴素的用途是图片识别。即输入图片,然后识别图片的类别,例如输入一张图片,识别该图片是猫还是狗。

1.1.卷积与池化的作用

卷积层与池化层共同是卷积神经网络的核心,它用于将输入图片进行压缩,例如一张224x224的图片,经过卷积+池化后,可能得到的就是55x55的图片,也就是说,卷积与池化的目的就是使得输入图片变小,同时尽量不要损失太多与类别相关的信息。例如一张猫的图片经过卷积与池化之后,尽量减少图片的大小,但要尽可能地保留"猫"的信息。

2.2.全连接层的作用

全连接层主要用于预测图片的类别。全连接层实际可以看作一个BP神经网络模型, 使用"卷积+池化"之后得到的特征来拟合图片的类别。

二、卷积神经网络的运算

2.1.卷积层的运算

卷积层的运算如下:
卷积运算
卷积层中的卷积核就是一个矩阵,直观来看它就是一个窗口,卷积窗口一般为正方形,即长宽一致,
卷积运算通过从左到右,从上往下移动卷积核窗口,将窗口覆盖的每一小块输入进行加权,作为输出

2.2.池化的运算

池化层是通过一个池化窗口,对输入进行逐块扫描,每次将窗口的元素合并为一个元素,
池化层的运算如下:
池化运算
池化层一般分为均值池化与最大值池化,顾名思义,就是计算时使用均值还是最大值:
均值池化与最大值池化

2.3.全连接层运算

全连接层就相当于一个BP神经网络模型,即每一层与下一层都是全连接形式。
全连接层

假设前一层传过来的输入的是X,则当前层的输出是tanh(WX+b)

三、pytorch实现一个CNN例子

下面以手写数字识别为例,展示如何使用pytorch实现一个CNN
在这里插入图片描述

3.1.模型的搭建

如下所示,就搭建了一个CNN模型

# 卷积神经网络的结构
class ConvNet(nn.Module):
    def __init__(self,in_channel,num_classes):
        super(ConvNet, self).__init__()
        self.nn_stack=nn.Sequential(
            #--------------C1层-------------------
            nn.Conv2d(in_channel,6, kernel_size=5,stride=1,padding=2),
            nn.ReLU(inplace=True),  
            nn.AvgPool2d(kernel_size=2,stride=2),
            # 输出14*14
            #--------------C2层-------------------
            nn.Conv2d(6,16, kernel_size=5,stride=1,padding=2),
            nn.ReLU(inplace=True),
            nn.AvgPool2d(kernel_size=2,stride=2),
            # 输出7*7
            #--------------C3层-------------------
            nn.Conv2d(16,80,kernel_size=7,stride=1,padding=0),
            # 输出1*1*80
            #--------------全连接层F4----------
            nn.Flatten(),          # 对C3的结果进行展平
            nn.Linear(80, 120),  
            nn.ReLU(inplace=True),                                   
            #--------------全连接层F5----------                      
            nn.Linear(120, num_classes)                       
            )
    def forward(self, x):
        p = self.nn_stack(x)
        return p

从代码里可以看到,只需按自己所设定的结构进行随意搭建就可以了。
搭建了之后再使用数据进行训练可以了,然后就可以使用模型对样本进行预测。

3.2.CNN完整训练代码

完整的CNN训练代码示例如下:

import torch
from   torch import nn
from   torch.utils.data   import DataLoader
import torchvision
import numpy as np

#--------------------模型结构--------------------------------------------
# 卷积神经网络的结构
class ConvNet(nn.Module):
    def __init__(self,in_channel,num_classes):
        super(ConvNet, self).__init__()
        self.nn_stack=nn.Sequential(
            #--------------C1层-------------------
            nn.Conv2d(in_channel,6, kernel_size=5,stride=1,padding=2),
            nn.ReLU(inplace=True),  
            nn.AvgPool2d(kernel_size=2,stride=2),
            # 输出14*14
            #--------------C2层-------------------
            nn.Conv2d(6,16, kernel_size=5,stride=1,padding=2),
            nn.ReLU(inplace=True),
            nn.AvgPool2d(kernel_size=2,stride=2),
            # 输出7*7
            #--------------C3层-------------------
            nn.Conv2d(16,80,kernel_size=7,stride=1,padding=0),
            # 输出1*1*80
            #--------------全连接层F4----------
            nn.Flatten(),          # 对C3的结果进行展平
            nn.Linear(80, 120),  
            nn.ReLU(inplace=True),                                   
            #--------------全连接层F5----------                      
            nn.Linear(120, num_classes)                       
            )
    def forward(self, x):
        p = self.nn_stack(x)
        return p

#-----------------------模型训练---------------------------------------
# 参数初始化函数
def init_param(model):
    # 初始化权重阈值                                                                         
    param_list = list(model.named_parameters())                                                # 将模型的参数提取为列表                      
    for i in range(len(param_list)):                                                           # 逐个初始化权重、阈值
        is_weight = i%2==0                                                                     # 如果i是偶数,就是权重参数,i是奇数就是阈值参数
        if is_weight:                                                                          
            torch.nn.init.normal_(param_list[i][1],mean=0,std=0.01)                            # 对于权重,以N(0,0.01)进行随机初始化
        else:                                                                                  
           torch.nn.init.constant_(param_list[i][1],val=0)                                     # 阈值初始化为0
  
# 训练函数                                                                                     
def train(dataloader,valLoader,model,epochs,goal,device):                                      
    for epoch in range(epochs):                                                                
        err_num  = 0                                                                           # 本次epoch评估错误的样本
        eval_num = 0                                                                           # 本次epoch已评估的样本
        print('-----------当前epoch:',str(epoch),'----------------')                           
        for batch, (imgs, labels) in enumerate(dataloader):                                    
		    # -----训练模型-----                                                               
            x, y = imgs.to(device), labels.to(device)                                          # 将数据发送到设备
            optimizer.zero_grad()                                                              # 将优化器里的参数梯度清空
            py   = model(x)                                                                    # 计算模型的预测值   
            loss = lossFun(py, y)                                                              # 计算损失函数值
            loss.backward()                                                                    # 更新参数的梯度
            optimizer.step()                                                                   # 更新参数
			# ----计算错误率----                                                               
            idx      = torch.argmax(py,axis=1)                                                 # 模型的预测类别
            eval_num = eval_num + len(idx)                                                     # 更新本次epoch已评估的样本
            err_num  = err_num +sum(y != idx)                                                  # 更新本次epoch评估错误的样本
            if(batch%10==0):                                                                   # 每10批打印一次结果
                print('err_rate:',err_num/eval_num)                                            # 打印错误率
        # -----------验证数据误差---------------------------                                   
        model.eval()                                                                           # 将模型调整为评估状态
        val_acc_rate = calAcc(model,valLoader,device)                                          # 计算验证数据集的准确率
        model.train()                                                                          # 将模型调整回训练状态
        print("验证数据的准确率:",val_acc_rate)                                                # 打印准确率    
        if((err_num/eval_num)<=goal):                                                          # 检查退出条件
            break                                                                              
    print('训练步数',str(epoch),',最终训练误差',str(err_num/eval_num))                         

# 计算数据集的准确率                                                                           
def calAcc(model,dataLoader,device):                                                           
    py = np.empty(0)                                                                           # 初始化预测结果
    y  = np.empty(0)                                                                           # 初始化真实结果
    for batch, (imgs, labels) in enumerate(dataLoader):                                        # 逐批预测
        cur_py =  model(imgs.to(device))                                                       # 计算网络的输出
        cur_py = torch.argmax(cur_py,axis=1)                                                   # 将最大者作为预测结果
        py     = np.hstack((py,cur_py.detach().cpu().numpy()))                                 # 记录本批预测的y
        y      = np.hstack((y,labels))                                                         # 记录本批真实的y
    acc_rate = sum(y==py)/len(y)                                                               # 计算测试样本的准确率
    return acc_rate                                                                               

#--------------------------主流程脚本----------------------------------------------       
#-------------------加载数据--------------------------------
train_data = torchvision.datasets.MNIST(
    root       = 'D:\pytorch\data'                                                             # 路径,如果路径有,就直接从路径中加载,如果没有,就联网获取
    ,train     = True                                                                          # 获取训练数据
    ,transform = torchvision.transforms.ToTensor()                                             # 转换为tensor数据
    ,download  = True                                                                          # 是否下载,选为True,就下载到root下面
    ,target_transform= None)                                                                   
val_data = torchvision.datasets.MNIST(
    root       = 'D:\pytorch\data'                                                             # 路径,如果路径有,就直接从路径中加载,如果没有,就联网获取
    ,train     = False                                                                         # 获取测试数据
    ,transform = torchvision.transforms.ToTensor()                                             # 转换为tensor数据
    ,download  = True                                                                          # 是否下载,选为True,就下载到root下面
    ,target_transform= None)                                                                   
                                                                                               
#-------------------模型训练--------------------------------                                   
trainLoader = DataLoader(train_data, batch_size=1000, shuffle=True)                            # 将数据装载到DataLoader
valLoader   = DataLoader(val_data  , batch_size=100)                                           # 将验证数据装载到DataLoader 
device      = torch.device('cuda' if torch.cuda.is_available() else 'cpu')                     # 设置训练设备  
model       = ConvNet(in_channel =1,num_classes=10).to(device)                                 # 初始化模型,并发送到设备  
lossFun     = torch.nn.CrossEntropyLoss()                                                      # 定义损失函数为交叉熵损失函数
optimizer   = torch.optim.SGD(model.parameters(), lr=0.01,momentum =0.9,dampening=0.0005)      # 初始化优化器
train(trainLoader,valLoader,model,1000,0.01,device)                                            # 训练模型,训练100步,错误低于1%时停止训练

# -----------模型效果评估--------------------------- 
model.eval()                                                                                   # 将模型切换到评估状态(屏蔽Dropout)
train_acc_rate = calAcc(model,trainLoader,device)                                              # 计算训练数据集的准确率
print("训练数据的准确率:",train_acc_rate)                                                      # 打印准确率
val_acc_rate = calAcc(model,valLoader,device)                                                  # 计算验证数据集的准确率
print("验证数据的准确率:",val_acc_rate)                                                        # 打印准确率

运行结果如下:

-----------当前epoch: 0 ---------------- 
err_rate: tensor(0.7000)                 
验证数据的准确率: 0.3350877192982456     
-----------当前epoch: 1 ---------------- 
err_rate: tensor(0.6400)                 
验证数据的准确率: 0.3350877192982456     
-----------当前epoch: 2 ---------------- 
.......
.......
-----------当前epoch: 77 ----------------
err_rate: tensor(0.0100)                 
验证数据的准确率: 1.0                    
-----------当前epoch: 78 ----------------
err_rate: tensor(0.)                     
验证数据的准确率: 1.0                    
-----------当前epoch: 79 ----------------
err_rate: tensor(0.0200)                 
验证数据的准确率: 1.0                    
-----------当前epoch: 80 ----------------
err_rate: tensor(0.0100)                 
验证数据的准确率: 0.9982456140350877     
-----------------------------------------
训练步数 80 ,最终训练误差 tensor(0.0088) 
训练数据的准确率: 0.9982456140350877     
验证数据的准确率: 0.9982456140350877 

可以看到,识别效果达到了99.8%。CNN模型对图片的识别是非常有效的。


相关链接:

《老饼讲解-机器学习》:老饼讲解-机器学习教程-通俗易懂
《老饼讲解-神经网络》:老饼讲解-matlab神经网络-通俗易懂
《老饼讲解-神经网络》:老饼讲解-深度学习-通俗易懂

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

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

相关文章

Golang | Leetcode Golang题解之第203题移除链表元素

题目&#xff1a; 题解&#xff1a; func removeElements(head *ListNode, val int) *ListNode {dummyHead : &ListNode{Next: head}for tmp : dummyHead; tmp.Next ! nil; {if tmp.Next.Val val {tmp.Next tmp.Next.Next} else {tmp tmp.Next}}return dummyHead.Next …

有人物联的串口服务器USR-TCP232-410S基本测试通信和使用方案(485串口和232串口)

1.将 410S(USR-TCP232-410S&#xff0c;简称 410S 下同)的串口通过串口线(或USB 转串口线)与计算机相连接&#xff0c;通过网线将 410S 的网口 PC 的网口相连接&#xff0c;检测硬件连接无错误后&#xff0c;接入我们配送的电源适配器&#xff0c;给 410S 供电。观察指示灯状态…

MCU 是什么?一文了解MCU 产业

MCU&#xff08;Microcontroller Unit&#xff09;&#xff0c;中文名为“微控制器单元”、“单片微型计算机”。MCU 将中央处理器&#xff08;CPU&#xff09;、内存&#xff08;RAM&#xff09;、输入 / 输出界面&#xff08;I/O&#xff09;等等一大堆东西&#xff0c;全部整…

Qt:4.信号和槽

目录 1.信号源、信号和槽&#xff1a; 2.Qt类的继承关系&#xff1a; 3.自定义槽函数&#xff1a; 4.第一种信号和槽的连接的方法&#xff1a; 5.第二种信号和槽的连接的方法&#xff1a; 6.自定义信号&#xff1a; 7.发射信号&#xff1a; 8.信号和槽的传参&#xff1a;…

《数据仓库与数据挖掘》 总复习

试卷组成 第一章图 第二章图 第三章图 第四章图 第五章图 第六章图 第九章图 第一章 DW与DM概述 &#xff08;特点、特性&#xff09; DB到DW 主要特征 &#xff08;1&#xff09;数据太多&#xff0c;信息贫乏&#xff08;Data Rich&#xff0c; Information Poor)。 &a…

侯捷C++面向对象高级编程(上)-2-构造函数

1.inline函数 2.访问级别 3.构造函数 4.重载

【力扣高频题】004.两个正序数组的中位数

------------------ 长文警告 ------------------ 4.两个正序数组的中位数 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O ( l o g ( m n ) ) O(log(mn)) O…

【期末速成】计算机操作系统 EP03 | 学习笔记

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文&#xff1a;☀️☀️☀️2.1 考点五&#xff1a;进程的概念及特征2.1 考点六&#xff1a;进程的状态与切换 三、总结&#xff1a;&#x1f353;&#x1f353;&#x1f353; 一、前言&#x1f680;&#x1f6…

Unix/Linux shell实用小程序1:生字本

前言 在日常工作学习中&#xff0c;我们会经常遇到一些不认识的英语单词&#xff0c;于时我们会打开翻译网站或者翻译软件进行查询&#xff0c;但是大部分工具没有生词本的功能&#xff0c;而有生字本的软件又需要注册登陆&#xff0c;免不了很麻烦&#xff0c;而且自己的数据…

linux-内存映射MMAP-lseek-dup-fifo-通信-IO多路复用

1、内存映射MMap&#xff1a; DMA&#xff1a; 可以用*/[]取代read和write&#xff1b; 限制&#xff1a; 1、文件大小固定不能改变&#xff1b;&#xff08;ftruncate&#xff09; 2、只能是磁盘文件&#xff1b; 3、建立映射之前先open mmap函数&#xff1a; mmap第一个…

GAN论文阅读笔记(10)—— High-fidelity GAN Inversion with Padding Space

论文&#xff1a;High-fidelity GAN Inversion with Padding Space paper&#xff1a;136750036.pdf (ecva.net) code&#xff1a;EzioBy/padinv: [ECCV 2022] PadInv: High-fidelity GAN Inversion with Padding Space (github.com) 关键词&#xff1a;GAN, GAN 反演 ( GAN I…

MobPush HarmonyOS NEXT 版本集成指南

开发工具&#xff1a;DevEco Studio 集成方式&#xff1a;在线集成 HarmonyOS API支持&#xff1a;> 11 集成前准备 注册账号 使用MobSDK之前&#xff0c;需要先在MobTech官网注册开发者账号&#xff0c;并获取MobTech提供的AppKey和AppSecret&#xff0c;详情可以点击查…

React实战学习(一)_棋盘设计

需求&#xff1a; 左上侧&#xff1a;状态左下侧&#xff1a;棋盘&#xff0c;保证胜利就结束 和 下过来的不能在下右侧&#xff1a;“时光机”,保证可以回顾&#xff0c;索引 语法&#xff1a; 父子之间属性传递&#xff08;props&#xff09;子父组件传递&#xff08;写法上&…

第十三章 常用类

一、包装类 1. 包装类的分类 &#xff08;1&#xff09;针对八种基本数据类型相应的引用类型—包装类 &#xff08;2&#xff09;有了类的特点&#xff0c;就可以调用类中的方法。 2. 包装类和基本数据的转换 jdk5 前的手动装箱和拆箱方式&#xff0c;装箱&#xff1a;基本…

使用SpringBoot整合filter

SpringBoot整合filter&#xff0c;和整合servlet类似&#xff0c;也有两种玩儿法 1、创建一个SpringBoot工程&#xff0c;在工程中创建一个filter过滤器&#xff0c;然后用注解WebFilter配置拦截的映射 2、启动类还是使用ServletComponentScan注解来扫描拦截器注解WebFilter 另…

通过百度文心智能体创建STM32编程助手-实操

一、前言 文心智能体平台AgentBuilder 是百度推出的基于文心大模型的智能体&#xff08;Agent&#xff09;平台&#xff0c;支持广大开发者根据自身行业领域、应用场景&#xff0c;选取不同类型的开发方式&#xff0c;打造大模型时代的产品能力。开发者可以通过 prompt 编排的…

主从复制、哨兵以及Cluster集群

目录 1.Redis高可用 2.Redis主从复制 2.1 主从复制的作用 2.2 主从复制流程 2.3 搭建Redis主从复制 2.3.1 修改Redis配置文件&#xff08;Master节点操作&#xff09; 2.3.2 修改Redis配置文件&#xff08;Slave节点操作&#xff09; 2.3.2 验证主从复制结果 3.Redis哨…

Oracle新特性速递:未来数据库技术的无限可能

文章目录 一、自治数据库&#xff1a;智能化与自动化的革命二、机器学习集成&#xff1a;智能数据分析的新境界三、区块链技术&#xff1a;确保数据完整性与透明性四、云原生数据库&#xff1a;灵活扩展与快速部署五、人工智能优化器&#xff1a;智能查询执行计划《Oracle从入门…

Pow(x,n)快速冥算法

快速幂算法 快速幂算法是一种通过分治和递归的方式来计算幂运算的方法&#xff0c;其核心思想是利用分治和递归减少乘法的次数来显著提高效率。 基本原理&#xff1a; 给定 x 和 n&#xff0c;计算 x^n 的过程如下&#xff1a; 基本情况处理&#xff1a;如果指数 n 是 0&…

【STM32修改串口波特率】

STM32微控制器中的串口波特率调整通常涉及到USART&#xff08;通用同步接收器/发送器&#xff09;模块的配置。USART模块提供了多个寄存器来设置波特率&#xff0c;其中关键的寄存器包括BRR&#xff08;波特率寄存器&#xff09;和USART_CR1&#xff08;控制寄存器1&#xff09…