深度学习:神经网络--手写数字识别
目录
一、datasets
1.datasets简介
2.主要特点
二、MNIST
三、使用神经网络实现手写数字识别
1.创建数据加载器
2.判断是否使用GPU
3.创建神经网络
4.创建训练集模型
5.创建测试集模型
6.创建损失函数和优化器并训练
一、datasets
1.datasets简介
datasets是一个广泛使用的库,尤其在机器学习和自然语言处理领域,用于方便地加载和处理各种数据集。它提供了标准化的接口,使得数据集的访问、处理和分割变得更加简单。
2.主要特点
-
丰富的数据集库:
提供了数百个公开数据集,涵盖不同领域,如文本、图像、音频等。 -
简化数据加载:
只需几行代码即可加载数据集,支持多种格式(如 CSV、JSON、文本文件等)。 -
高效的数据处理:
提供数据预处理功能,如分词、编码、数据增强等,方便进行数据清洗和转换。 -
数据集分割:
支持轻松地将数据集分为训练集、验证集和测试集。 -
自定义数据集:
用户可以自定义数据集,方便地与现有数据集结合使用。 -
与深度学习框架的兼容性:
可以与 PyTorch、TensorFlow 等深度学习框架无缝集成,便于模型训练。
二、MNIST
- MNIST 是一个经典的手写数字识别数据集,广泛用于机器学习和计算机视觉领域的研究和教学。
- 它包含 70,000 张 28x28 像素的灰度图像,分为 60,000 张训练样本和 10,000 张测试样本,每张图像对应一个 0 到 9 的数字标签。
代码实现:
import torch
from torch import nn # 导入神经网络模块
from torch.utils.data import DataLoader # 数据包管理工具,打包数据
from torchvision import datasets # 封装了很多与图像相关的模型,数据集
from torchvision.transforms import ToTensor # 数据转换,张量,将其他类型的数据转换为tensor张量
'''下载训练数据集(包含训练图片和标签)'''
train_data = datasets.MNIST(
root='data'
, train=True
, download=True
, transform=ToTensor() # 张量,图片是不能直接传入神经网络模型
) # 对于pytorch库能够识别的数据一般是tensor张量.
'''下载测试数据集(包含训练图片和标签)'''
test_data = datasets.MNIST(
root='data'
, train=False
, download=True
, transform=ToTensor()
) # NumPy 数组只能在CPU上运行.Tensor可以在GPU上运行,这在深度学习应用中可以显著提高计算速度.
print(len(train_data))
'''展示手写数字图片'''
import matplotlib.pyplot as plt
figure = plt.figure()
for i in range(9):
img, label = train_data[i + 100]
figure.add_subplot(3, 3, i + 1)
plt.title(label)
plt.axis('off')
plt.imshow(img.squeeze(), cmap='gray')
a = img.squeeze()
plt.show()
输出:
三、使用神经网络实现手写数字识别
1.创建数据加载器
- 对数据进行打包
- 通过打包数据,可以提高工作效率,简化数据管理流程,并提升模型的训练和推理性能。
'''
创建数据DataLoader(数据加载器)
batch_size:将数据集分成多份,每一份为batch_size个数据.
优点:可以减少内存的使用,提高训练速度.
'''
train_dataloader = DataLoader(train_data, batch_size=64) # 64张图片为一个包
test_dataloader = DataLoader(test_data, batch_size=64)
for x, y in test_dataloader:
print(f"shape of x [N ,C,H,W]:{x.shape}")
print(f"shape of y :{y.shape} {y.dtype}")
break
输出:
- 第一行输出表示一个包64张一个通道的图片,每张图片像素大小为28*28
- 第二行输出表示包的大小为64,数据类型为torch里的int64
shape of x [N ,C,H,W]:torch.Size([64, 1, 28, 28])
shape of y :torch.Size([64]) torch.int64
2.判断是否使用GPU
'''判断当前设备是否支持GPU mps是苹果m系列芯片GPU'''
device = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_avaibale() else 'cpu'
print(f"using {device} device")
输出:
using cuda device
3.创建神经网络
'''创建神经网络类'''
class NeuralNetwork(nn.Module):
def __init__(self):
super().__init__() # 继承的父类初始化
self.flatten = nn.Flatten() # 展开,创建一个展开对象flatten
self.hidden1 = nn.Linear(28 * 28, 512) # 第1个参数:有多少个神经元传入进来,第2个参数:有多少个数据传出去 前一层神经元的个数,当前本层神经元个数
self.hidden2 = nn.Linear(512, 256) # 第二个隐藏层
self.hidden3 = nn.Linear(256, 128) # 第三个隐藏层
self.hidden4 = nn.Linear(128, 64) # 第四个隐藏层
self.out = nn.Linear(64, 10) # 输出必须和标签的类别相同,输入必须是上一层的神经元个数
def forward(self, x): # 前向传播,你得告诉它,数据的流向.是神经网络层连接起来,函数名称不能改.当你调用forward函数的时候,传入进来的图像数据
x = self.flatten(x) # 图像进行展开
x = self.hidden1(x)
x = torch.relu(x) # 激活函数,relu,tanh,sigmod relu没有梯度消失问题,且计算消耗大大降低
x = self.hidden2(x)
x = torch.relu(x)
x = self.hidden3(x)
x = torch.relu(x)
x = self.hidden4(x)
x = torch.relu(x)
x = self.out(x)
return x
model = NeuralNetwork().to(device) # 把刚刚创建的模型传入到GPU
print(model)
输出:
- 输出的就是神经网络的模型
- 输入层--隐藏层1--隐藏层2--隐藏层3--隐藏层4--输出层
- 每一层的输入神经元,输出神经元,偏置项
NeuralNetwork(
(flatten): Flatten(start_dim=1, end_dim=-1)
(hidden1): Linear(in_features=784, out_features=512, bias=True)
(hidden2): Linear(in_features=512, out_features=256, bias=True)
(hidden3): Linear(in_features=256, out_features=128, bias=True)
(hidden4): Linear(in_features=128, out_features=64, bias=True)
(out): Linear(in_features=64, out_features=10, bias=True)
)
4.创建训练集模型
def train(dataloader, model, loss_fn, optimizer):
model.train() # 告诉模型,我要开始训练,模型中w进行随机化操作,已经更新w.在训练过程中,w会被修改的
# pytorch提供2种方式来切换训练和测试的模式,分别是:model.train()和 model.eval().
# 一般用法是: 在训练开始之前写上model.trian(),在测试时写上model.eval().
batch_size_num = 1
for x, y in dataloader:
x, y = x.to(device), y.to(device) # 把训练数据集和标签传入CPU或GPU
pred = model.forward(x) # 向前传播
loss = loss_fn(pred, y) # 通过交叉熵损失函数计算损失值loss
optimizer.zero_grad() # 梯度值清零
loss.backward() # 反向传播计算得到每个参数的梯度值w
optimizer.step() # 根据梯度更新网络w参数
loss_value = loss.item() # 从tensor数据中提取数据出来,tensor获取损失值
if batch_size_num % 200 == 0:
print(f"loss:{loss_value:>7f} [number:{batch_size_num}]")
batch_size_num += 1
5.创建测试集模型
def test(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
model.eval() # 测试,w就不能再更新。
test_loss, correct = 0, 0
with torch.no_grad(): # 一个上下文管理器,关闭梯度计算。当你确认不会调用Tensor.backward()的时候。这可以减少计算所占用的消耗
for x, y in dataloader:
x, y = x.to(device), y.to(device)
pred = model.forward(x)
test_loss += loss_fn(pred, y).item() # test loss是会自动累加每一个批次的损失值
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
a = (pred.argmax(1) == y) # dim=1表示每一行中的最大值对应的索引号,dim=0表示每一列中的最大值对应的索引号
b = (pred.argmax(1) == y).type(torch.float)
test_loss /= num_batches # 能来衡量模型测试的好坏。
correct /= size # 平均的正确率
print(f"Test result: \n Accuracy: {(100 * correct)}%, Avg loss: {test_loss}")
6.创建损失函数和优化器并训练
loss_fn = nn.CrossEntropyLoss() # 处理多分类 损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.0012) #
# params:要训练的参数,一般我们传入的都是model.parameters()
# lr:leqrning rate学习率,也就是步长
epochs = 4 # 到底选择多少呢?
for t in range(epochs):
print(f"Epoch {t + 1}\n--------------")
train(train_dataloader, model, loss_fn, optimizer)
print("Done!")
test(test_dataloader, model, loss_fn)
输出:
- 可以看到每一轮之后模型损失值都在变小,说明模型在进行优化
Epoch 1
--------------
loss:0.459520 [number:200]
loss:0.307480 [number:400]
loss:0.213908 [number:600]
loss:0.196316 [number:800]
Epoch 2
--------------
loss:0.175202 [number:200]
loss:0.251854 [number:400]
loss:0.085344 [number:600]
loss:0.091463 [number:800]
Epoch 3
--------------
loss:0.050711 [number:200]
loss:0.184462 [number:400]
loss:0.043357 [number:600]
loss:0.056628 [number:800]
Epoch 4
--------------
loss:0.022849 [number:200]
loss:0.125797 [number:400]
loss:0.019434 [number:600]
loss:0.023895 [number:800]
Done!
Test result:
Accuracy: 96.58%, Avg loss: 0.14490024165602944