首页
视频
资源
登录
原
Pytorch 识别手写数字(易化学习笔记三)
487
人阅读
2024/6/9 20:11
总访问:
2971613
评论:
0
收藏:
0
手机
分类:
python
![](https://img.tnblog.net/arcimg/hb/21f086c80c5d4afda1bc1029dadd8f3a.png) >#Pytorch 识别手写数字(易化学习笔记三) [TOC] 识别手写数字 ------------ ### LeNet-5 tn2>手写数字识别的非常高效的卷积神经网络。 高效原因: 1.用卷积核(局部连接,共享权重,平移不变性) -> 提取关键特征,参数大幅减少 2.不断降维后,仍能保留关键特征(识别成功) `64*28*28 -> 6*28*28 -> 6*14*14 -> 16*10*10 -> 16*5*5 -> 120 -> 84 -> 10` 用卷积 减少图片张数(64 -> 6) 用池化减少图片大小(28->14 -> 10 -> 5 -> 1) 用全连接层 把三维的矩阵降为 一维 (16*5*5 ->120) 两层全连接层,是借鉴 softmax 取10个中做大概率的。 ![](https://img.tnblog.net/arcimg/hb/864021b3897c44cbb540ffeee66407fc.jpeg) ### 训练模型 tn2>创建一个`q1.py`的文件。 ```python import torch import torchvision import torch.nn as nn # 超参数设置 # 训练轮数 EPOCH = 8 # 批处理尺寸(每批用64个图片) BATCH_SIZE = 64 # 学习率 LR = 0.001 ## 数据载入 # 归一化,转为[0,1.0] shape[C,H,W]的张量 # trans = torchvision.transforms.ToTensor() #(优化方式) 3.正则化 -> 加弹性余量 # 在输入数据的预处理里,加入正则化Normalize trans = torchvision.transforms.Compose( # 转换的集合 [ torchvision.transforms.ToTensor(), # 归一化,转为[0,1.0] shape[C,H,W]的张量 torchvision.transforms.Normalize([0.5], [0.5]) # 正则化(均值=0.5,标准差=0.5) ] ) # 获取手写数字的训练集(pytorch已整理好的,其中都是28*28的图片) train_data = torchvision.datasets.MNIST( root="./data", # 设置数据集的所在目录 train=True, # 是训练集 transform=trans, # 设置转换函数 download=True # True:无则下载数据集 ) # 获取手写数字的测试集 test_data = torchvision.datasets.MNIST( root="./data/", train=False, # 是测试集 transform=trans, download=True ) # 载入训练集 train_loader = torch.utils.data.DataLoader( dataset=train_data, # 指定数据集载入 batch_size=BATCH_SIZE, # 每批数目(每次从数据集中取出 送入模型的训练的样本数) shuffle=True) # 乱序(取样本时) # 载入测试集 test_loader = torch.utils.data.DataLoader( dataset=test_data, batch_size=BATCH_SIZE, shuffle=True) ## 构建模型 # 定义网络结构 class LeNet(nn.Module): def __init__(self): super(LeNet, self).__init__() # 卷积层: 由下面3层添加到顺序容器Sequential中而成 self.conv1 = nn.Sequential( nn.Conv2d(1, 6, 5, 1, 2), # /*2维卷积层: input_size=(1*28*28) -> 特征过滤(降维) # 右键可查看API,参数未输入的将采用默认值 # 如 class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True) # 输入通道: 1张灰度图 input_size=(1*28*28),图片需是28*28的 # 输出通道: 6张特征图 (从不同角度看,得到不同特征图,如从轮廓,明暗效果,纹理等,可定义许多个) # 一个卷积核 ,过滤出一张特征图,输出6张特征图,就对应6个卷积核 # 卷积核大小: 5x5 高宽相等可只写一个 # 滑动步长: 1步 # 填充: 2 -> 图边用2圈0填充(卷积后尺寸会变小,图边可用零填充,保证尺寸相同) # 注:为维持输出图片尺寸不变 由公式 O=(I-F+2P)/S+1=(28-5+2*2)/1+1 = 28 # 如是32*32图片 需公式来O=(I-F+2P)/S+1调整 变为Conv2d(1, 6, 5) # */ nn.ReLU(), # 激活层: input_size=(6*28*28) -> 引入非线性(可挑选信息) nn.MaxPool2d(kernel_size=2, stride=2),#/*池化层(最值): # output_size=(6*14*14) -> 舍去非显著特征,减少参数,缓解过拟合 # O=(I-F+2P)/S+1 = (28-2+2*0)/2 + 1 = 14 #*/ ) self.conv2 = nn.Sequential( nn.Conv2d(6, 16, 5), # input_size=(6*14*14) O=(I-F+2P)/S+1=(14-5+2*0)/1+1 = 10 nn.ReLU(), # input_size=(16*10*10) nn.MaxPool2d(2, 2) # output_size=(16*5*5) O=(I-F+2P)/S+1=(10-2+2*0)/2+1 = 5 ) # 全连接层 (in:16*5*5 out:120) self.fc1 = nn.Sequential( nn.Linear(16 * 5 * 5, 120), # (优化方案)2.加入dropout 随机断开 -> 增加随机性(防过拟合) nn.Dropout(0.5), # 随机50%的断开 nn.ReLU() ) self.fc2 = nn.Sequential( nn.Linear(120, 84), nn.ReLU() ) self.fc3 = nn.Linear(84, 10) # 前向传播函数: 一旦构建前向传播网络成功,反向传播函数也会自动生成(autograd) def forward(self, x): x = self.conv1(x) x = self.conv2(x) # print(x.shape) torch.Size([64, 16, 5, 5]) 每批64张图片 输出16张 5*5的特征图 # 扁平化:行64张 列-1自动推导,把16张 5*5的特征图压平为一维的点 -> 方便对接全连接层 x = x.view(x.shape[0], -1) x = self.fc1(x) x = self.fc2(x) x = self.fc3(x) return x # 没有gpu则使用cpu device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 新建模型到设备上(gpu/cpu) net = LeNet().to(device) # 使用交叉熵损失函数(通常用于多分类问题上) criterion = nn.CrossEntropyLoss() # 采用SGD的优化器 # 在优化器中加L2正则化(weight_decay=0.01 指权重衰减 惩罚%1) optimizer = torch.optim.Adam(net.parameters(), lr=LR) ## 训练评估 for epoch in range(EPOCH): sum_loss = 0.0 # enumerate 遍历列表对象,加上序号i for i, data in enumerate(train_loader): # 准备 # inputs:手写数字图片 labels:图片对应的数字标签 inputs, labels = data # 把数据传入设备中(如GPU) inputs, labels = inputs.to(device), labels.to(device) # 梯度清零: 每次迭代都需梯度清零,因pytorch默认会累积梯度 optimizer.zero_grad() # 向前传播 # 模型对输入进行预测 preds = net(inputs) # 计算损失 loss = criterion(preds, labels) # 向后传播 # 反向传播,计算梯度 loss.backward() # 按学习率减去少量梯度来调整权重 optimizer.step() # 每训练100个batch打印一次平均loss sum_loss += loss.item() if i % 100 == 99: print('[%d, %d] loss: %.03f'%(epoch + 1, i + 1, sum_loss / 100)) sum_loss = 0.0 # 每跑完一次epoch测试一下准确率 with torch.no_grad(): correct = 0 total = 0 for data in test_loader: images, labels = data images, labels = images.to(device), labels.to(device) outputs = net(images) # 取得分最高的那个类 _, predicted = torch.max(outputs.data, 1) # 标签总数: 累加每批取的标签数 total += labels.size(0) # 预测正确的样本数 correct += (predicted == labels).sum() # 统计正确率 print('第%d个epoch的识别准确率为:%d%%' % (epoch + 1, (100 * correct / total))) torch.save(net.state_dict(), "mnist_2d.pth") # 保存模型参数(推荐) # 优点: 速度快,占空间少, # 缺点: 重加载时需自定义model,且参数需与保存的模型的一致, # 可以只是部分网络,相对灵活,便于对网络进行修改 # torch.save(net, 'net_model.pth') # 可直接保存模型(不建议),这里略过 ``` ![](https://img.tnblog.net/arcimg/hb/56e71e34201b4d5fbb3c40fabee894c0.png) ### 数据预处理 tn2>从网上找一张图片,然后编写`q2.py`文件。 ![](https://img.tnblog.net/arcimg/hb/56bbbe3065b44cde92bf8937f19947f7.png) ```python ## 数据预处理 import cv2 # 导入opencv库 import matplotlib.pyplot as plt #pic ='8.bmp' # 待识别的图片名 pic ='4.jpeg' img = cv2.imread(pic) # 读取图片 # 数据预处理(转换为与训练集相同的格式) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 彩色图片转为灰度图片 img = 255 - img # 黑白反转(官方mnist集合里黑白是反的) img = cv2.resize(img,(28,28),interpolation = cv2.INTER_AREA) # 转换为与训练集图片相同的尺寸 img = img.reshape(1,28,28) # 转换为与训练集相同格式 1个 28*28像素的 测试图像 img =img/255.0 # 除以255为了归一化,加.0 转为浮点数 ->避免数据溢出 # 查看图片数据 print(img.shape) for i in range(28): for j in range(28): print(format(img[0][i][j],'.1f'),end="") # 0:0号图 i:图的行号 j:图的列号 end=""不换行 # .1f: 限制float小数点后的显示位数为1位 print('E') # 显示图片 plt.imshow(img[0], cmap=plt.get_cmap('gray')) # cmap: 颜色图谱 gray:黑白色 plt.show() ``` ![](https://img.tnblog.net/arcimg/hb/a1e82a5122934f1eb304643a4a800194.png) ### 预测数据 tn2>接着我们刚刚的代码添加如下代码进行预测数据。 ```python ## 载入模型参数 # 定义网络结构 class LeNet(nn.Module): def __init__(self): super(LeNet, self).__init__() self.conv1 = nn.Sequential( nn.Conv2d(1, 6, 5, 1, 2), nn.ReLU(), nn.MaxPool2d(kernel_size=2, stride=2), ) self.conv2 = nn.Sequential( nn.Conv2d(6, 16, 5), nn.ReLU(), nn.MaxPool2d(2, 2) ) self.fc1 = nn.Sequential( nn.Linear(16 * 5 * 5, 120), nn.Dropout(0.5), nn.ReLU() ) self.fc2 = nn.Sequential( nn.Linear(120, 84), nn.ReLU() ) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = self.conv1(x) x = self.conv2(x) x = x.view(x.shape[0], -1) x = self.fc1(x) x = self.fc2(x) x = self.fc3(x) return x # 没有gpu则使用cpu device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 新建模型到设备上(gpu/cpu) model = LeNet().to(device) # 加载已训练好的模型参数 model.load_state_dict(torch.load('mnist_2d.pth')) # 可用载入模型torch.load('模型名'),一般不推荐使用,略过 ## 预测评估 # 评估模式 model.eval() # 转为torch 的张量格式 img = torch.tensor(img) # 升维为 torch.Size([1, 1, 28, 28]) -> 满足model对输入格式要求 img = img.unsqueeze(0) # 需与模型的元素类型匹配 images =img.to(torch.float32) images = images.to(device) print("image shap=",images.shape) print("image dtype=",images.dtype) outputs = model(images) # 用模型预测, # 注:输入数据要求是4维,如 torch.Size([64, 1, 28, 28]) # 输出10个数字的概率 print(outputs) # 取概率最高的 _, predicted = torch.max(outputs.data, 1) # 预测值 print(predicted) ``` ![](https://img.tnblog.net/arcimg/hb/42ca208e87e04a6e8a9931138c82c4f7.png)
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739
👈{{preArticle.title}}
👉{{nextArticle.title}}
评价
{{titleitem}}
{{titleitem}}
{{item.content}}
{{titleitem}}
{{titleitem}}
{{item.content}}
尘叶心繁
这一世以无限游戏为使命!
博主信息
排名
6
文章
6
粉丝
16
评论
8
文章类别
.net后台框架
177篇
linux
18篇
linux中cve
1篇
windows中cve
0篇
资源分享
10篇
Win32
3篇
前端
28篇
传说中的c
5篇
Xamarin
9篇
docker
15篇
容器编排
101篇
grpc
4篇
Go
15篇
yaml模板
1篇
理论
2篇
更多
Sqlserver
4篇
云产品
39篇
git
3篇
Unity
1篇
考证
2篇
RabbitMq
23篇
Harbor
1篇
Ansible
8篇
Jenkins
17篇
Vue
1篇
Ids4
18篇
istio
1篇
架构
2篇
网络
7篇
windbg
4篇
AI
19篇
threejs
2篇
人物
1篇
嵌入式
20篇
python
20篇
HuggingFace
8篇
pytorch
10篇
opencv
6篇
Halcon
5篇
最新文章
最新评价
{{item.articleTitle}}
{{item.blogName}}
:
{{item.content}}
关于我们
ICP备案 :
渝ICP备18016597号-1
网站信息:
2018-2024
TNBLOG.NET
技术交流:
群号656732739
联系我们:
contact@tnblog.net
欢迎加群
欢迎加群交流技术