程序地带

手写数字识别


手写数字识别的简单demo

环境 python3.8 torch 1.5.1+cpu 数据集MINIST


步骤:

1.minist函数下载数据 2.预处理,创建一个迭代器DataLoder 3.可视化数据 4.nn工具箱创建网络 5.实例化模型 6.训练模型 7.可视化结果


结构:

2个hiddenlayer 激活函数relu 在这里插入图片描述


代码
1.准备数据
import numpy as np
import torch
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
from torchvision.datasets import mnist # 导入内置minist数据
#导入预处理模块
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
#导入nn和优化器
import torch.nn.functional as F
import torch.optim as optim
from torch import nn
#定义超参数,(训练、测试个数,学习率、迭代次数...)
train_batch_size=64
test_batch_size=128
learning_rate=0.01
num_epoches=20
lr=0.01
momentum=0.5

导入必要的模块和定义超参数 import os 是为了不让内核崩溃,必须加的一句话


2.下载数据和预处理
#定义预处理,依次放在Compose函数
#transforms.Compose把转换函数组合在一起,先转换为标量再Normalize([0.5],[0.5])对张量进行归一化,0.5是平均值和方差,灰色图像只有一个通道,彩色有三个Normalize([m1,m2,m3],[n1,n2,n3])
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.5],[0.5])])
#下载数据,download参数控制是否下载,./data目录下有MNIST,可选择False
train_dataset=mnist.MNIST('./data',train=True,transform=transform,download=True)
test_dataset=mnist.MNIST('./data',train=False,transform=transform)
#DataLoader得到生成器,可节省内存,是一个可迭代对象,可以使用迭代器一样使用,理解为数据集的调用
train_loader=DataLoader(train_dataset,batch_size=train_batch_size,shuffle=True)
test_loader=DataLoader(test_dataset,batch_size=test_batch_size,shuffle=False)

关于 transforms torchvision.transforms 参数解读/中文使用手册这个类将多个变换方式结合在一起


mnist torchvision.datasets.MNIST 参数解读/中文使用手册详细讲了各个参数的使用 DataLoderpytorch dataloader数据加载


3.可视化源数据
#可视化源数据,enumerate循环测试集
examples=enumerate(test_loader)
batch_idx,(example_data,example_targets)=next(examples)
fig=plt.figure() #创建一个图
for i in range(6):
plt.subplot(2,3,i+1) #子图位置
plt.tight_layout()
plt.imshow(example_data[i][0],cmap='gray',interpolation='none')
plt.title("ground truth:{}".format(example_targets[i]))
plt.xticks([])
plt.yticks([])
plt.show()

enumerate是一个循环方法可以同时获得索引和值,enumerate(test_loader)是一个可迭代访问的对象 next(iterator, default) default是迭代结尾的返回值,可写可不写 example_targets是图片实际对应的数字标签 example_data是图片 subplot 子图 plt.imshowmatplotlib.pyplot.imshow()函数的使用 注意第一个参数


4.构建网络
#构建模型
class Net (nn.Module) : #Net继承Module
"""
Sequential构建网络,函数功能是将网络的层组合到一起
"""
def __init__(self, in_dim, n_hidden_1, n_hidden_2,out_dim):
super(Net,self).__init__() #用Module方法初始化
self.layer1=nn.Sequential(nn.Linear(in_dim,n_hidden_1),nn.BatchNorm1d(n_hidden_1))
self.layer2 = nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2), nn.BatchNorm1d(n_hidden_2))
self.layer3 = nn.Sequential(nn.Linear( n_hidden_2, out_dim))
def forward(self,x):
x= F.relu(self.layer1(x))
x = F.relu(self.layer2(x))
x=self.layer3(x)
return x

构建网络层可以基于Module类或者nn.functional 主要区别是 nn.Module继承Module类 ,nn.functional更像纯函数 一般有可学习的层如卷积、全连接、dropout用nn.Module;激活函数、池化层用nn.functional


Net继承nn.Module类 并用父类方法初始化参数 构建层nn.Sequential方法介绍 nn.Linear(n_hidden_1, n_hidden_2) 参数是维度 nn.BatchNorm1d BatchNorm就是在深度神经网络训练过程中使得每一层神经网络的输入保持相同分布的nn.BatchNorm1d()


模型训练时,不需要使用forward,只要在实例化一个对象中传入对应的参数就可以自动调用 forward 函数 请注意,前向传递可以使用成员变量甚至数据本身来确定执行路径——它还可以使用多个参数!


5.实例化网络
#检查GPU
device=torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#实例化网络
model=Net(28*28,300,100,10) #初始化
model.to(device)
#定义损失函数优化器
criterion=nn.CrossEntropyLoss()
optimizer=optim.SGD(model.parameters(),lr=lr,momentum=momentum)

model=Net(28*28,300,100,10) 初始化,用了_init_函数 model.to(device)这行代码的意思是将所有最开始读取数据时的tensor变量copy一份到device所指定的GPU(CPU)上去,之后的运算都在GPU(CPU)上进行。


SGD随机梯度下降,最普通,没有加速效果,momentum是改良版本 优化算法与Torch.optim库


6.训练模型
#开始训练 ,训练损失、准确率,测试验证损失、准确率数组
losses=[]
acces=[]
eval_losses=[]
eval_acces=[]

定义训练 损失、准确率,测试验证损失、准确率数组,保留每次迭代的结果


for epoch in range(num_epoches):
train_loss=0
train_acc=0
model.train()
#动态修改学习率
if epoch%5==0:
optimizer.param_groups[0]['lr']*=0.1
for img, label in train_loader:
img=img.to(device)
label=label.to(device)
img=img.view(img.size(0),-1)
#前向传播
out=model(img)
loss=criterion(out,label)
#反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
#记录误差
train_loss+=loss.item()
#计算准确率
_,pred=out.max(1)
num_correct=(pred==label).sum().item()
acc =num_correct/img.shape[0]
train_acc+=acc
losses.append(train_loss/len(train_loader))
acces.append(train_acc / len(train_loader))

每次迭代前,将loss acc清零 model.train()训练模式model.train和model.eval用法及区别详解


1.model.train() 启用 BatchNormalization 和 Dropout。 2.model.eval() 不启用 BatchNormalization 和 Dropout。 外循环数据集循环一次为一次迭代 每五次迭代 调整学习率在训练中动态的调整学习率 optimizer.param_groups修改 或者新建 optimizer,但是后者会造成有momentum的sgd收敛中的震荡。 optimizer.param_groups [{}]长度为1的list, optimizer.param_groups[0]长度为6的字典,有lr momentum等参数 内循环一张张图片循环 img.view(img.size(0),-1)将高维数据 平铺为低维 对x.view(x.size(0), -1)的一些理解 -1是自动补齐的意思 前向传播 先将处理过后的数据集喂给model对象,自动执行forward, 得到结果out,再将out和标签label喂给criterion来计算损失函数 反向传播 使用optimizer.zero_grad()手动将梯度设置为零,因为PyTorch在默认情况下会累积梯度 loss.backward() 自动生成梯度 我们收集一组新的梯度,并使用optimizer.step(),执行优化器将其传播回每个网络参数,更新参数。 optimizer.step() 和loss.backward()和scheduler.step()的关系与区别


train_loss将每张图片的损失累加 _, pred = torch.max(out, 1) #按行获取最大值,并返回张量和索引值 torch.max()使用讲解 num_correct = (pred == label).sum().item() # item()将一个值的张量变为标量(元素值) train_acc也类似


每次迭代后,将结果存入losses acces数组(结果要记得除以长度)因为累计值不是平均值


7.测试集
#测试集效果
eval_loss = 0
eval_acc = 0
#更改模式为预测模式
model.eval()
for img, label in test_loader:
img = img.to(device)
label = label.to(device)
img = img.view(img.size(0), -1)
out=model(img)
loss=criterion(out,label)
#记录误差
eval_loss += loss.item()
# 计算准确率
_, pred = out.max(1)
num_correct = (pred == label).sum().item()
acc = num_correct / img.shape[0]
eval_acc += acc
eval_losses.append(eval_loss / len(test_loader))
eval_acces.append(eval_acc / len(test_loader))
print('epoch:{},train loss:{:.4f},test acc:{:.4f},test loss:{:.4f},test acc:{:.4f}'
.format(epoch,train_loss/len(train_loader),train_acc / len(train_loader),eval_loss / len(test_loader),eval_acc / len(test_loader)))

每次迭代前,将eval_loss eval_acc清零 model.eval()不启用 BatchNormalization 和 Dropout。 代码跟训练集类似,但是少了反向传播的过程,不用学习参数。只用前向传播来计算结果。


8.可视化训练及测试损失值
plt.title('trainloss')
plt.plot(np.arange(len(losses)),losses)
plt.legend(['trainloss'],loc='upper right')

plt.legend 加图例


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41321169/article/details/111246273

随机推荐

随笔4——fopen与fopen_s用法

VS2019中经常会遇到fopen打开文件错误,此时需要改为fopen_sfopen用法:FILE*fp;fp=fopen(filename,“w”);fopen_...

海云妹妹 阅读(748)

C++指针变量作函数参数接收数组地址

C++指针变量作函数参数接收数组地址在C++中,用指针变量指向数组元素时要注意:指针变量p可以指向有效的数组元素,实际上也可以...

C语言入门到精通 阅读(196)

使用动态分区分配方式的模拟

使用动态分区分配方式的模拟1、实验目的了解动态分区分配方式中使用的数据结构和分配算法,并进一步加深对动态分区存储管理方式及其实现过程的理解。2、实验内容(1)...

jiashaoniubi 阅读(905)

一键带你了解Yotta企业云盘大数据存储

一键带你了解Yotta企业云盘大数据存储

Yotta企业云盘不同于其他,Yotta云盘采用的是区块链存储,并且有加密去重技术。Yotta企业云盘为大数据时代数据安全高效存储添砖加瓦随着信息技术的普及,...

lqyanna 阅读(480)

多因子模型学习笔记

多因子模型学习笔记

一、简介多因子模型是风险——收益关系的定量表达,因子是不同类型风险的解释变量。多因子模型是由APT理论发展而来,一般表达式多因子模型本质是将对N只股票的收益——风险预测转变...

weixin_44357667 阅读(497)

Unity开发VR--VRTK与SteamVR版本对应问题

Unity开发VR--VRTK与SteamVR版本对应问题

Unity进行VR项目开发时候经常使用的插件为VRTK,作为小白一般之间在Unity商店下载VRTK,现在商店中对应的VRTK版本为3.3.0SteamVRUnity商店对...

liang_704959721 阅读(344)

es6新语法学习

es6新语法学习1、let2、解构表达式3、函数优化4、对象优化4、map和reducePromise模块化1、let<!DOCTYPEhtml><htmllang="...

weixin_43888133 阅读(591)

C语言:strncmp() 用法详解

一、strncmp()简介1.函数原型intstrncmp(constchar*str1,constchar*str2,size_tn);2.参数str1–要进行比较的第一个字符串。str2–要进行比...

根号五 阅读(695)