当前位置: 首页 > news >正文

卷积神经网络参数解读

简单的卷积网络

  • 构建卷积神经网络
    • 读取数据
    • 卷积网络的介绍与构建
    • 准确率评估标准
  • 网络模型的训练和测试

在之前的分类任务中,我们讲述了全连接神经网络(FC)进行分类的方法,但是对于图像数据来说,全连接神经网络并不是一个合适的选择,因为在图像信息当中,每个像素点所代表的信息和其周围的像素点或多或少存在关系。应对这样的情况,卷积神经网络(CNN)是比较合理的选择。本节我们依然使用MNIS数据集来进行卷积神经网络的初步学习。

构建卷积神经网络

读取数据

我们依然分别构建训练集和测试集:

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import datasets,transforms 
import matplotlib.pyplot as plt
import numpy as np
# 定义超参数 
input_size = 28  # 图像的总尺寸28*28
num_classes = 10  # 标签的种类数
num_epochs = 3  # 训练的总循环周期,由于可供学习数据量不够,
                # 我们要重复使用训练集进行学习
batch_size = 64  #一个撮(批次)的大小,64张图片

# 训练集
train_dataset = datasets.MNIST(root='./data',  
                            train=True,   
                            transform=transforms.ToTensor(),  # 将读入数据转换成tensor
                            download=True) 
print(train_dataset) # 查看训练集基本信息
# 测试集
test_dataset = datasets.MNIST(root='./data', 
                           train=False, 
                           transform=transforms.ToTensor())
print(test_dataset) # 查看测试集基本信息
# 数据打包,64个一组
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, 
                                           batch_size=batch_size, 
                                           shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, 
                                           batch_size=batch_size, 
                                           shuffle=True)

在这里插入图片描述

卷积网络的介绍与构建

卷积神经网络和全连接神经网络在结构上有很多相似点,因此我们着重讲述一下这两个网络中一个比较明显的区别:卷积核。在全连接神经网络中,我们主要通过wx+b的方式得到特征,因此我们输入的是一维向量,输出的也是一维特征向量。但在卷积神经网络中则有所不同。卷积神经网络依靠内容不同的卷积核分别与图像进行卷积以获得特征,假如我们希望从一幅图像中获得16种特征(需要16个不同卷积核),那么每输入一张28 * 28的图像矩阵,将会输出16个28 * 28的图像矩阵。
不难看出,这数据量一下就多了很多,对于计算机来说,这并不是一件好事,因为如果我们设置的隐层很多,那么需要计算机处理的数据量就会爆炸式的增长。为了缓解这一问题,就需要使用池化。池化,就是将我们得到的特征图进行优化。以前文提到的16张28 * 28大小的特征图举例,我们将每一张图都单独取出来,然后每2 * 2个像素点中,保留下一个最大的值,组成新的特征图,这样一来,我们就得到了16张14*14大小的特征图,节约了很多算力。
下面我们用代码实现网络构建:

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        # 处理输入的第一层网络
        self.conv1 = nn.Sequential(         # 输入,大小为每一张图片的所有特征 (1, 28, 28)
            nn.Conv2d( # 2D卷积针对图像,1D卷积针对结构化数据,3D卷积针对视频
                in_channels=1,              # 灰度图,输入是1个channel
                out_channels=16,            # 卷积核个数,决定能得到多少特征图,以及下一层输入多少channel
                kernel_size=5,              # 卷积核大小,通常选3*3因为可以得到更多的特征
                stride=1,                   # 卷积核每次向右或下移动步长,步长越大特征越少
                # 添加边缘信息,确保原图的边缘新西也能多次参与卷积过程。这里是图像边缘加入两圈0
                padding=2,                  # 如果希望卷积后大小跟原来一样,需要设置padding=(kernel_size-1)/2 if stride=1
                                            # 从公式中可以看出padding要根据kernel来选择
                                            # 如果除不开会默认向下取整
            ),                              # 输出的特征图为 (16, 28, 28)
            nn.ReLU(),                      # relu层,每提取一次特征都需要做一次非线性映射
            nn.MaxPool2d(kernel_size=2),    # 进行池化操作(2x2 区域), 输出长度不变但图片维度被压缩
                                            # 此处的输出结果为: (16, 14, 14)
        )
        # 全连接层
        self.conv2 = nn.Sequential(         # 连多个隐层
            nn.Conv2d(16, 32, 5, 1, 2),     # 输出 (32, 14, 14)
            nn.ReLU(),                      # relu层
            nn.Conv2d(32, 32, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(2),                # 输出 (32, 7, 7)
        )
        # 紧邻输出的最后一层
        self.conv3 = nn.Sequential(         
            nn.Conv2d(32, 64, 5, 1, 2),     
            nn.ReLU(),                      # 输出 (64, 7, 7)
        )
        # 每张原图都会卷积出64个7*7的图片,因此共有64*7*7个特征
        self.out = nn.Linear(64 * 7 * 7, 10)   # 卷积得到的特征结果做一下全连接,分为10类

    def forward(self, x): # 前向传播会在nn.Module里自行使用,我们的代码不需要对它进行调用,如不写,
                          # 计算机就不知道如何进行前向传播
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        # 此时的x包含四个参数:batch,c,h,w
        x = x.view(x.size(0), -1)           # flatten操作,将batch_size与输出特征分开
                                            # -1为自动计算,结果为:(batch_size, 64 * 7 * 7)
        output = self.out(x)
        return output

这样我们的卷积网络模型就构建完毕了,代码中包括详细的注释,可以帮助大家理解卷积和池化。

准确率评估标准

之前我们已经讲到过,损失值不能直观的反应学习的好坏,因此我们需要自己拟定一个求取分类准确率的函数:

def accuracy(predictions, labels): # 预测值和真实值
    pred = torch.max(predictions.data, 1)[1] # 获得预测分类之中得分最大值的索引
                                             # 加入索引是8,那么分类结果就是数字8
    rights = pred.eq(labels.data.view_as(pred)).sum() # 判断正确的数量
    return rights, len(labels) 

torch.max可以帮我们找到目标数据中的最大值,当然我们还可以调整第二个参数得到多个次大值。

网络模型的训练和测试

这部分内容已经在本系列第一篇中详细提到过,因此不再赘述,代码中有详细的注释,不懂的小伙伴要认真学起来了:

# 实例化
net = CNN() 
#损失函数
criterion = nn.CrossEntropyLoss() 
#优化器
optimizer = optim.Adam(net.parameters(), lr=0.001) #定义优化器,普通的随机梯度下降算法

#开始训练循环
for epoch in range(num_epochs):
    #当前epoch的结果保存下来
    train_rights = [] 
    for batch_idx, (data, target) in enumerate(train_loader):  # train_loader将60000个数据分成938组,每组64个图
                                                               # 该循环会进行938次
        net.train()                             
        output = net(data) 
        loss = criterion(output, target) 
        optimizer.zero_grad() 
        loss.backward() 
        optimizer.step() 
        right = accuracy(output, target) 
        train_rights.append(right) 

    
        if batch_idx % 100 == 0:  # 每一百次循环进行一下测试,检测学习结果
            
            net.eval() 
            val_rights = [] 
            
            for (data, target) in test_loader: # test_loader将10000个数据分成157组,每组64个数据
                                               # 我们最多可以测试157次,这里只测了30次。相当于检测
                                               # 学习成果的试卷足够多。我们可以增加外循环次数或者增加
                                               # 每组测试集中的“试题量”让待测试的数据得到充分的利用
                output = net(data) 
                right = accuracy(output, target) 
                val_rights.append(right)
                
            #准确率计算
            # 将这一组的所有判断结果
            train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
            val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))

            print('当前epoch: {} [{}/{} ({:.0f}%)]\t损失: {:.6f}\t训练集准确率: {:.2f}%\t测试集正确率: {:.2f}%'.format(
                epoch, batch_idx * batch_size, len(train_loader.dataset),
                100. * batch_idx / len(train_loader), 
                loss.data, 
                100. * train_r[0].numpy() / train_r[1], 
                100. * val_r[0].numpy() / val_r[1]))

在这里插入图片描述
大家可以把这次的训练结果和全连接神经网络的训练结果做一下对比,就可以发现效果还是有不小的提升的~
可能有小伙伴还是对增加“试题量”不太理解,其实我们只需要把读取数据中部分代码做以下替换即可:

test_loader = torch.utils.data.DataLoader(dataset=test_dataset, 
                                           batch_size=330,  # 我们将10000道题打包成31份试卷,
                                                            # 取前等量的30份试卷作测试用,每张试卷有330道题
                                           shuffle=True)

至于说增加学习次数就更简单了,因为一共可以测试157次,一个小循环跑完会测试10次,也就是把num_epochs设置成15刚刚好~

相关文章:

  • IP 地址及其应用(计算机网络)
  • poi-tl(word模板渲染)
  • Java线程
  • 【Flink读写外部系统】Flink自定义kafka分区并输出
  • 【云原生】学习K8s的扩展技能(CRD)
  • Chapter05 修炼python基本功:条件语句和循环
  • 彻底掌握Makeifle(三)
  • 手机抓取蓝牙日志btsnoop的方法汇总(Android一直补充中)
  • 【Vue 开发实战】实战篇 # 30:实现一个可动态改变的页面布局
  • [单片机框架][drivers层][cw2015/ADC] fuelgauge 硬件电量计和软件电量计(一)
  • 【iVX 开发 - 入门】开发环境、应用对象树介绍(含操作演示)
  • CTFshow 代码审计
  • 19-Django REST framework-DRF工程搭建
  • CSP-S信息学奥赛考试大纲(提高级)
  • 电源硬件设计----降压-升压(Buck-Boost)变换器基础
  • [译] 怎样写一个基础的编译器
  • python 装饰器(一)
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • Twitter赢在开放,三年创造奇迹
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • Vue全家桶实现一个Web App
  • 多线程事务回滚
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 基于遗传算法的优化问题求解
  • 如何编写一个可升级的智能合约
  • 一些css基础学习笔记
  • 通过调用文摘列表API获取文摘
  • #define与typedef区别
  • (007)XHTML文档之标题——h1~h6
  • (4)logging(日志模块)
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (附源码)springboot教学评价 毕业设计 641310
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (原)本想说脏话,奈何已放下
  • (转)四层和七层负载均衡的区别
  • .Net - 类的介绍
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .NET CLR基本术语
  • .NET Core中的去虚
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NET简谈设计模式之(单件模式)
  • .NET开发人员必知的八个网站
  • .NET中统一的存储过程调用方法(收藏)
  • @RequestBody的使用
  • [ vulhub漏洞复现篇 ] struts2远程代码执行漏洞 S2-005 (CVE-2010-1870)
  • [20140403]查询是否产生日志
  • [20171113]修改表结构删除列相关问题4.txt
  • [Android] Android ActivityManager
  • [android] 手机卫士黑名单功能(ListView优化)
  • [Android]使用Android打包Unity工程