【Week-P2】CNN彩色图片分类-CIFAR10数据集

文章目录

  • 一、环境配置
  • 二、准备数据
  • 三、搭建网络结构
  • 四、开始训练
  • 五、查看训练结果
  • 六、总结
    • 3.1 ⭐ `torch.nn.Conv2d()`详解
    • 3.2 ⭐ `torch.nn.Linear()`详解
    • 3.3 ⭐`torch.nn.MaxPool2d()`详解
    • 3.4 ⭐ 关于卷积层、池化层的计算
    • 4.2.1 `optimizer.zero_grad()`说明
    • 4.2.2 `loss.backward()`说明
    • 4.2.3 `optimizer.step()`说明
    • 4.4.1 `model.train()`说明
    • 4.4.2 `model.eval()`说明

本文采用CIFAR10数据集,通过简单CNN来实现彩色图片识别。

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊 | 接辅导、项目定制

一、环境配置

# 1. 设置环境
import sys
from datetime import datetimeimport torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvisionprint("---------------------1.配置环境------------------")
print("Start time: ", datetime.today())
print("Pytorch version: " + torch.__version__)
print("Python version: " + sys.version)device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device

在这里插入图片描述

二、准备数据

导入数据的方式和【Week P1】中的方法是一致的,都是通过dataset下载数据集、通过dataloader加载数据集。

'''
2. 导入数据使用dataset下载CIFAR10数据集,并划分好训练集与测试集使用dataloader加载数据,并设置好基本的batch_size
'''
print("---------------------2.1 下载CIFAR10数据集,并划分训练集和测试集------------------")
train_ds = torchvision.datasets.CIFAR10('data', train=True, transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensordownload=True)test_ds  = torchvision.datasets.CIFAR10('data', train=False, transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensordownload=True)print("---------------------2.2 设置batch_size------------------")
batch_size = 32train_dl = torch.utils.data.DataLoader(train_ds, batch_size=batch_size, shuffle=True)test_dl  = torch.utils.data.DataLoader(test_ds, batch_size=batch_size)print("---------------------2.2.1 取一个批次查看数据格式------------------")
# 取一个批次查看数据格式
# 数据的shape为:[batch_size, channel, height, weight]
# 其中batch_size为自己设定,channel,height和weight分别是图片的通道数,高度和宽度。
imgs, labels = next(iter(train_dl))
imgs.shapeprint("---------------------2.3 数据可视化------------------")
import numpy as np# 指定图片大小,图像大小为20宽、5高的绘图(单位为英寸inch)
plt.figure(figsize=(20, 5)) 
for i, imgs in enumerate(imgs[:20]):# 维度缩减npimg = imgs.numpy().transpose((1, 2, 0))# 将整个figure分成2行10列,绘制第i+1个子图。plt.subplot(2, 10, i+1)plt.imshow(npimg, cmap=plt.cm.binary)plt.axis('off')#plt.show()  如果你使用的是Pycharm编译器,请加上这行代码

等待漫长的4h35min后:
在这里插入图片描述

三、搭建网络结构

对于一般的CNN网络来说,都是由特征提取网络和分类网络构成,其中特征提取网络用于提取图片的特征,分类网络用于将图片进行分类。

用到的运算主要有:卷积、池化

网络结构:
在这里插入图片描述

以下几点涉及到的内容,统一在文末说明:
3.1 ⭐ torch.nn.Conv2d()详解
3.2 ⭐ torch.nn.Linear()详解
3.3 ⭐torch.nn.MaxPool2d()详解
3.4 ⭐ 关于卷积层、池化层的计算

print("---------------------3.1 定义简单CNN网络,要点:卷积和池化运算------------------")
import torch.nn.functional as Fnum_classes = 10  # 图片的类别数class Model(nn.Module):def __init__(self):super().__init__()# 特征提取网络self.conv1 = nn.Conv2d(3, 64, kernel_size=3)   # 第一层卷积,卷积核大小为3*3self.pool1 = nn.MaxPool2d(kernel_size=2)       # 设置池化层,池化核大小为2*2self.conv2 = nn.Conv2d(64, 64, kernel_size=3)  # 第二层卷积,卷积核大小为3*3   self.pool2 = nn.MaxPool2d(kernel_size=2) self.conv3 = nn.Conv2d(64, 128, kernel_size=3) # 第二层卷积,卷积核大小为3*3   self.pool3 = nn.MaxPool2d(kernel_size=2) # 分类网络self.fc1 = nn.Linear(512, 256)          self.fc2 = nn.Linear(256, num_classes)# 前向传播def forward(self, x):x = self.pool1(F.relu(self.conv1(x)))     x = self.pool2(F.relu(self.conv2(x)))x = self.pool3(F.relu(self.conv3(x)))x = torch.flatten(x, start_dim=1)x = F.relu(self.fc1(x))x = self.fc2(x)return xprint("---------------------3.2 加载和打印网络结构------------------")
from torchinfo import summary
# 将模型转移到GPU中(我们模型运行均在GPU中进行)
model = Model().to(device)summary(model)

在这里插入图片描述

四、开始训练

4.2 编写训练函数中,用到的函数有:

  • optimizer.zero_grad()
  • loss.backward()
  • optimizer.step()

在文末说明每个函数的使用方法

4.3 编写测试函数中:

  • 测试函数和训练函数大致相同,但是由于不进行梯度下降对网络权重进行更新,所以不需要传入优化器

4.4 正式训练中,使用的训练方法包括:

  • model.train():作用是启用 Batch Normalization 和 Dropout
  • model.eval():作用是不启用 Batch Normalization 和 Dropout
# 4. 训练模型
print("---------------------4.1 设置超参数------------------")
loss_fn    = nn.CrossEntropyLoss() # 创建损失函数
learn_rate = 1e-2 # 学习率
opt        = torch.optim.SGD(model.parameters(),lr=learn_rate)print("---------------------4.2 编写训练函数-----------------")
# 训练循环
# 训练循环
def train(dataloader, model, loss_fn, optimizer):size = len(dataloader.dataset)  # 训练集的大小,一共60000张图片num_batches = len(dataloader)   # 批次数目,1875(60000/32)train_loss, train_acc = 0, 0  # 初始化训练损失和正确率for X, y in dataloader:  # 获取图片及其标签X, y = X.to(device), y.to(device)# 计算预测误差pred = model(X)          # 网络输出loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失# 反向传播optimizer.zero_grad()  # grad属性归零loss.backward()        # 反向传播optimizer.step()       # 每一步自动更新# 记录acc与losstrain_acc  += (pred.argmax(1) == y).type(torch.float).sum().item()train_loss += loss.item()train_acc  /= sizetrain_loss /= num_batchesreturn train_acc, train_lossprint("---------------------4.3 编写测试函数-----------------")
def test (dataloader, model, loss_fn):size        = len(dataloader.dataset)  # 测试集的大小,一共10000张图片num_batches = len(dataloader)          # 批次数目,313(10000/32=312.5,向上取整)test_loss, test_acc = 0, 0# 当不进行训练时,停止梯度更新,节省计算内存消耗with torch.no_grad():for imgs, target in dataloader:imgs, target = imgs.to(device), target.to(device)# 计算losstarget_pred = model(imgs)loss        = loss_fn(target_pred, target)test_loss += loss.item()test_acc  += (target_pred.argmax(1) == target).type(torch.float).sum().item()test_acc  /= sizetest_loss /= num_batchesreturn test_acc, test_lossprint("---------------------4.4 正式训练-----------------")
epochs     = 10
train_loss = []
train_acc  = []
test_loss  = []
test_acc   = []for epoch in range(epochs):model.train()epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)model.eval()epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))
print('Done')

在这里插入图片描述

五、查看训练结果

print("---------------------5. 查看训练结果-----------------")
import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率epochs_range = range(epochs)plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

在这里插入图片描述
可以看到,训练10个epoch后的效果是非常差的,训练准确率和测试准确率都不到60%。

六、总结

3.1 ⭐ torch.nn.Conv2d()详解

函数原型:

torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)

关键参数说明:

  • in_channels ( int ):输入图像中的通道数
  • out_channels ( int ) : 卷积产生的通道数
  • kernel_size ( int or tuple ) :卷积核的大小
  • stride ( int or tuple , optional ) :卷积的步长。默认值:1
  • padding ( int , tuple或str , optional ) : 添加到输入的所有四个边的填充。默认值:0
  • dilation (int or tuple, optional):膨胀操作,控制kernel点(卷积核点)的间距,默认值:1。
  • padding_mode (字符串,可选) : ‘zeros’, ‘reflect’, ‘replicate’或’circular’. 默认:‘zeros’
  • 关于dilation参数图解:
    在这里插入图片描述

3.2 ⭐ torch.nn.Linear()详解

函数原型:

torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

关键参数说明:

  • in_features:每个输入样本的大小
  • out_features:每个输出样本的大小

3.3 ⭐torch.nn.MaxPool2d()详解

函数原型:

torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

关键参数说明:

  • kernel_size:最大的窗口大小
  • stride:窗口的步幅,默认值为kernel_size(核的大小)
  • padding:填充值,默认为0
  • dilation:控制窗口中元素步长的参数

3.4 ⭐ 关于卷积层、池化层的计算

下面的网络数据shape变化过程为:

3, 32, 32(输入数据)→ 64, 30, 30(经过卷积层1)→ 64, 15, 15(经过池化层1)→ 64, 13, 13(经过卷积层2)→ 64, 6, 6(经过池化层2)→ 128, 4, 4(经过卷积层3) → 128, 2, 2(经过池化层3)→ 512 -> 256→ num_classes(10)

计算过程如下:
(1)卷积输出shape公式:
在这里插入图片描述
输入数据为:[3, 32, 32],即图片矩阵大小为32*32,卷积核大小为3,填充步长为默认值0,步长为默认值1,代入计算得到输出的大小为:30*30,输出通道不变,所以输入数据[3, 32, 32]经过Conv1层后得到的shape为·[64, 30, 30]·。

(2)池化输出公式:
在这里插入图片描述
输入的数据格式(从Conv1得到)是:[64, 30, 30] [C*Hin*Win],已知:Hin=30,padding=0,dilation=1,kernel_size=2,stride=2(即kernel_size),代入上述池化公式,可得Hout=15
同理,Wout=15,C保持不变,故而output.shape [64, 15, 15]
在这里插入图片描述

4.2.1 optimizer.zero_grad()说明

  • optimizer.zero_grad()函数会遍历模型的所有参数,通过内置方法截断反向传播的梯度流,再将每个参数的梯度值设为0,即上一次的梯度记录被清空。

4.2.2 loss.backward()说明

  • PyTorch的反向传播(即tensor.backward())是通过autograd包来实现的,autograd包会根据tensor进行过的数学运算来自动计算其对应的梯度。

  • 具体来说,torch.tensorautograd包的基础类,如果设置tensorrequires_gradsTrue,就会开始跟踪在这个tensor上的所有运算,如果做完运算后使用tensor.backward(),所有的梯度就会自动运算,tensor的梯度将会累加到它的.grad属性里面去。

  • 更具体地说,损失函数loss是由模型的所有权重w经过一系列运算得到的,若某个wrequires_gradsTrue,则w的所有上层参数(后面层的权重w)的.grad_fn属性中就保存了对应的运算,然后在使用loss.backward()后,会一层层的反向传播计算每个w的梯度值,并保存到该w.grad属性中。

  • 如果没有进行tensor.backward()的话,梯度值将会是None因此loss.backward()要写在optimizer.step()之前

4.2.3 optimizer.step()说明

  • step()函数的作用是执行一次优化步骤,通过梯度下降法来更新参数的值。因为梯度下降是基于梯度的,所以在执行optimizer.step()函数前应先执行loss.backward()函数来计算梯度。

  • 注意:optimizer只负责通过梯度下降进行优化,而不负责产生梯度,梯度是tensor.backward()方法产生的。

4.4.1 model.train()说明

  • model.train()的作用是:启用 Batch NormalizationDropout

  • 如果模型中有BN层(Batch Normalization)和Dropout,需要在训练时添加model.train()

  • model.train()是保证BN层能够用到每一批数据的均值和方差。

  • 对于Dropout,model.train()是随机取一部分网络连接来训练更新参数。

4.4.2 model.eval()说明

  • model.eval()的作用是:不启用 Batch NormalizationDropout

  • 如果模型中有BN层(Batch Normalization)和Dropout,在测试时添加model.eval()

  • model.eval()是保证BN层能够用全部训练数据的均值和方差,即测试过程中要保证BN层的均值和方差不变。

  • 对于Dropout,model.eval()是将所有网络连接都利用起来,即不进行随机舍弃神经元。

  • 训练完train样本后,生成的模型model要用来测试样本。在model(test)之前,需要加上model.eval(),否则的话,有输入数据,即使不训练,它也会改变权值。这是model中含有BN层和Dropout所带来的的性质。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重生奇迹MU剑士所有装备

1、普通装备:剑、盾、头盔、披风、手套、鞋子等。 2、精良装备:精良剑、精良盾、精良头盔、精良披风、精良手套、精良鞋子等。 3、稀有装备:稀有剑、稀有盾、稀有头盔、稀有披风、稀有手套、稀有鞋子等。 4、传说装备:传说剑、传…

数据安全扫描仪荣膺网络安全优秀创新成果大赛优胜奖 - 凸显多重优势

近日,由中国网络安全产业联盟(CCIA)主办、CCI数据安全工作委员会中国电子技术标准化研究院等单位承办的“2023年网络安全优秀创新成果大赛”获奖名单公布。天空卫士数据安全扫描仪(DSS)产品获得创新成果大赛优胜奖。 本…

图像处理—小波变换

小波变换 一维小波变换 因为存在 L 2 ( R ) V j 0 ⊕ W j 0 ⊕ W j 0 1 ⊕ ⋯ L^{2}(\boldsymbol{R})V_{j_{0}}\oplus W_{j_{0}}\oplus W_{j_{0}1}\oplus\cdots L2(R)Vj0​​⊕Wj0​​⊕Wj0​1​⊕⋯,所以存在 f ( x ) f(x) f(x)可以在子空间 V j 0 V_{j_0} Vj0…

Nginx快速入门:nginx各类转发、代理配置详解|location、proxy_pass参数详解(五)

0. 引言 咱们上节讲解了nginx的负载均衡配置,但是还有很多其他的转发情况,包括不同路径转发至不同的业务服务,通配符识别路径转发等。 今天一起来学习nginx的转发配置 1. location模块的匹配模式 首先我们要了解nginx进行转发代理的核心在…

c++学习笔记-提高篇-STL标准模板库3(stack容器、queue容器以及list容器)

目录 Stack容器 一、Stack容器介绍 二、stack常用接口 三、栈的示例 queue(队列)容器 一、queue容器介绍 二、queue常用接口 三、queue示例 list容器 一、list容器介绍 二、list常用接口及示例 (一)list构造函数 &am…

使用 Taro 开发鸿蒙原生应用 —— 探秘适配鸿蒙 ArkTS 的工作原理

背景 在上一篇文章中,我们已经了解到华为即将发布的鸿蒙操作系统纯血版本——鸿蒙 Next,以及各个互联网厂商开展鸿蒙应用开发的消息。其中,Taro作为一个重要的前端开发框架,也积极适配鸿蒙的新一代语言框架 —— ArkTS。 本文将…

html5 实现网页截屏 页面生成图片

效果 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"referrer" content"no-referrer" /><meta name"viewport"content"widthdevice-width,intial-sc…

Python整数常用的方法汇总与Python3 File(文件) 方法

Python整数常用的方法汇总 python&#xff13;基础之整数常用的方法整理 希望对大家学习或者使用python3能具有一定的参考价值。 __abs__ #返回一个数的绝对值 __add__ #两数相加 __and__ #两数按位与操作 __bool__ …

11种方法判断​软件的安全可靠性​

软件的安全可靠性是衡量软件好坏的一个重要标准&#xff0c;安全性指与防止对程序及数据的非授权的故意或意外访问的能力有关的软件属性&#xff0c;可靠性指与在规定的一段时间和条件下&#xff0c;软件 软件的安全可靠性是衡量软件好坏的一个重要标准&#xff0c;安全性指与防…

智能优化算法应用:基于蜜獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蜜獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蜜獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蜜獾算法4.实验参数设定5.算法结果6.参考文献7.MA…

centos(linux)安装jenkins

官网&#xff1a;https://pkg.jenkins.io/redhat/ jdk版本要和jenkins对上&#xff01; 安装官网进行操作&#xff1a; sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat/jenkins.reposudo rpm --import https://pkg.jenkins.io/redhat/jenkins.io-…

华为Harmony——ArkTs语言

文章目录 一、简单示例二、声明式UI描述创建组件无参有参数 配置属性配置事件配置子组件 三、自定义组件基本用法基本结构成员函数/变量 一、简单示例 我们以一个具体的示例来说明ArkTS的基本组成。如下图所示&#xff0c;当开发者点击按钮时&#xff0c;文本内容从“Hello Wo…

关于“Python”的核心知识点整理大全35

目录 13.3.4 重构 create_fleet() game_functions.py 13.3.5 添加行 game_functions.py alien_invasion.py 13.4 让外星人群移动 13.4.1 向右移动外星人 settings.py alien.py alien_invasion.py game_functions.py 13.4.2 创建表示外星人移动方向的设置 13.4.3 检…

转义字符使用详解【C语言】

目录 转义字符的概念 转义字符表 转义字符详解 和 实际使用示例 一、\a 二、\b 三、\f 四、\n 五、\r 六、\t 七、\v 八、\\ 九、\ 十、\" 十一、\? 十二、\0 十三、\ddd 十四、\xhh 总结—— 转义字符的概念 所有的 ASCII码都可以用“\加数字” 来表示…

【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化

引言 动态内存管理的函数有&#xff1a;malloc,calloc,ralloc,free,本文讲解动态内存函数和使用&#xff0c;如何进行动态内存管理,实现通讯录联系人容量的动态化&#xff0c;对常见动态内存错误进行总结。 ✨ 猪巴戒&#xff1a;个人主页✨ 所属专栏&#xff1a;《C语言进阶》…

C语言之输入输出和字符(2)

目录 缓冲和重定向 ▇缓冲 ▇重定向 字符 转义字符 \和\"……字符和字符" 字符串字面量的写法 字符常量的写法 八进制转义字符和十六进制转义字符 字符编码 在看本节之前&#xff0c;请先看下上一章&#xff0c;做到更好地衔接。https://blog.csdn.net/W061…

基于vue-cli快速发布vue npm 包

一、编写组件 1. 初始化项目并运行 vue create vue-digital-countnpm run serve2. 组件封装 新建package文件夹 ​ 因为我们可能会封装多个组件&#xff0c;所以在src下面新建一个package文件夹用来存放所有需要上传的组件。 ​ 当然&#xff0c;如果只有一个组件&#xff…

【科普知识】什么是步进驱动器、驱动模式及驱动的方法?

我们已经知道步进电机就是一种将电脉冲信号转化为角位移的执行机构。那么如何精准地控制电机的速度和位置呢&#xff1f;这就需要一种驱动器的装置。 1.什么是步进驱动器&#xff1f; 电机控制原理图 步进驱动器是一种能使步进电机运转的功率放大器&#xff0c;能把控制器发来…

Linux(二)常用命令

文章目录 一、文件管理命令1.1 chmod1.2 chown1.3 cat1.4 cp1.5 find1.6 head1.7 tail1.8 less1.9 more1.10 mv1.11 rm1.12 touch1.13 vim1.14 >和>>1.15 scp1.16 ln1.17 怎么用命令查看日志 二、文档管理命令2.1 grep2.2 wc2.3 echo 三、磁盘管理命令3.1 cd3.2 df3.3…

Observability:捕获 Elastic Agent 和 Elasticsearch 之间的延迟

在现代 IT 基础设施的动态环境中&#xff0c;高效的数据收集和分析至关重要。 Elastic Agent 是 Elastic Stack 的关键组件&#xff0c;通过促进将数据无缝摄取到 Elasticsearch 中&#xff0c;在此过程中发挥着至关重要的作用。 然而&#xff0c;显着影响此过程整体有效性的关…