目录
- 前期准备
- 一、制作数据集
- 1. excel表格数据
- 2. 代码
- 二、手写数字识别
- 1. 下载数据集
- 2. 搭建模型
- 3. 训练网络
- 4. 测试网络
- 5. 保存训练模型
- 6. 导入已经训练好的模型文件
- 7. 完整代码
前期准备
必须使用 3 个 PyTorch 内置的实用工具(utils):
⚫ DataSet 用于封装数据集;
⚫ DataLoader 用于加载数据不同的批次;
⚫ random_split 用于划分训练集与测试集。
一、制作数据集
在封装我们的数据集时,必须继承实用工具(utils)中的 DataSet 的类,这个过程需要重写__init__和__getitem__、__len__三个方法,分别是为了加载数据集、获取数据索引、获取数据总量。我们通过代码读取excel表格里面的数据作为数据集。
1. excel表格数据
2. 代码
为了简单演示,我们将表格的第0列作为输入特征,第1列作为输出特征。
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
from torch.utils.data import random_split
import matplotlib.pyplot as plt
# 制作数据集
class MyData(Dataset): """继承 Dataset 类"""
def __init__(self, filepath):
super().__init__()
df = pd.read_excel(filepath).values """ 读取excel数据"""
arr = df.astype(np.int32) """转为 int32 类型数组"""
ts = torch.tensor(arr) """数组转为张量"""
ts = ts.to('cuda') """把训练集搬到 cuda 上"""
self.X = ts[:, :1] """获取第0列的所有行做为输入特征"""
self.Y = ts[:, 1:2] """获取第1列的所有行为输出特征"""
self.len = ts.shape[0] """样本的总数"""
def __getitem__(self, index):
return self.X[index], self.Y[index]
def __len__(self):
return self.len
if __name__ == '__main__':
"""获取数据集"""
Data = MyData('label.xlsx')
print(Data.X[0]) """输出为:tensor([1020741172], device='cuda:0', dtype=torch.int32)"""
print(Data.Y[0]) """输出为:tensor([1], device='cuda:0', dtype=torch.int32) """
print(Data.__len__()) """输出为:233 """
"""划分训练集与测试集"""
train_size = int(len(Data) * 0.7) # 训练集的样本数量
test_size = len(Data) - train_size # 测试集的样本数量
train_Data, test_Data = random_split(Data, [train_size, test_size])
"""批次加载器"""
""" 第一个参数:表示要加载的数据集,即之前划分好的 train_Data或test_Data 。"""
""" 第二个参数:表示在每个 epoch(训练周期)开始之前是否重新洗牌数据。在训练过程中,通常会将数据进行洗牌,以确保模型能够学习到更加泛化的特征。而测试数据不需要重新洗牌,因为测试集仅用于评估模型的性能,不涉及模型参数的更新"""
""" 第三个参数:表示每个批次中的样本数量为 32。也就是说,每次迭代加载器时,它会从训练数据集中加载128个样本。"""
train_loader = DataLoader(train_Data, shuffle=True, batch_size=128)
test_loader = DataLoader(test_Data, shuffle=False, batch_size=64)
"""打印第一个批次的输入与输出特征"""
for inputs, targets in train_loader:
print(inputs)
print(targets)
二、手写数字识别
1. 下载数据集
在下载数据集之前,要设定转换参数:transform,该参数里解决两个问题:
⚫ ToTensor:将图像数据转为张量,且调整三个维度的顺序为 (C-W-H);C表示通道数,二维灰度图像的通道数为 1,三维 RGB 彩图的通道数为 3。
⚫ Normalize:将神经网络的输入数据转化为标准正态分布,训练更好;根据统计计算,MNIST 训练集所有像素的均值是 0.1307、标准差是 0.3081
"""数据转换为tensor数据"""
transform_data = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(0.1307, 0.3081)
])
"""下载训练集与测试集"""
train_Data = datasets.MNIST(
root = 'E:/Desktop/Document/4. Python/例程代码/dataset/mnist/', """下载路径"""
train = True, """训练集"""
download = True, """如果该路径没有该数据集,就下载"""
transform = transform_data """数据集转换参数"""
)
test_Data = datasets.MNIST(
root = 'E:/Desktop/Document/4. Python/例程代码/dataset/mnist_test/', """下载路径"""
train = False, """非训练集,也就是测试集"""
download = True, """如果该路径没有该数据集,就下载"""
transform = transform_data """数据集转换参数"""
)
"""批次加载器"""
train_loader = DataLoader(train_Data, shuffle=True, batch_size=64)
test_loader = DataLoader(test_Data, shuffle=False, batch_size=64)
2. 搭建模型
class DNN(nn.Module):
def __init__(self):
''' 搭建神经网络各层 '''
super(DNN,self).__init__()
self.net = nn.Sequential( # 按顺序搭建各层
nn.Flatten(), # 把图像铺平成一维
nn.Linear(784, 512), nn.ReLU(), # 第 1 层:全连接层
nn.Linear(512, 256), nn.ReLU(), # 第 2 层:全连接层
nn.Linear(256, 128), nn.ReLU(), # 第 3 层:全连接层
nn.Linear(128, 64), nn.ReLU(), # 第 4 层:全连接层
nn.Linear(64, 10) # 第 5 层:全连接层
)
def forward(self, x):
''' 前向传播 '''
y = self.net(x) # x 即输入数据
return y # y 即输出数据
3. 训练网络
"""实例化模型"""
model = DNN().to('cuda:0')
def train_net():
"""1.损失函数的选择"""
loss_fn = nn.CrossEntropyLoss() # 自带 softmax 激活函数
"""2.优化算法的选择"""
learning_rate = 0.01 # 设置学习率
optimizer = torch.optim.SGD(
model.parameters(),
lr=learning_rate,
momentum=0.5 # momentum(动量),它使梯度下降算法有了力与惯性
)
"""3.训练"""
epochs = 5
losses = [] """记录损失函数变化的列表"""
for epoch in range(epochs):
for (x, y) in train_loader: """从批次加载器中获取小批次的x与y"""
x, y = x.to('cuda:0'), y.to('cuda:0')
Pred = model(x) #将样本放入实例化的模型中,这里自动调用forward方法。
loss = loss_fn(Pred, y) # 计算损失函数
losses.append(loss.item()) # 记录损失函数的变化
optimizer.zero_grad() # 清理上一轮滞留的梯度
loss.backward() # 一次反向传播
optimizer.step() # 优化内部参数
"""4.画损失图"""
Fig = plt.figure()
plt.plot(range(len(losses)), losses)
plt.show()
损失图如下:
4. 测试网络
测试网络不需要回传梯度。
"""实例化模型"""
model = DNN().to('cuda:0')
def test_net():
correct = 0
total = 0
with torch.no_grad(): #该局部关闭梯度计算功能
for (x, y) in test_loader: #从批次加载器中获取小批次的x与y
x, y = x.to('cuda:0'), y.to('cuda:0')
Pred = model (x) #将样本放入实例化的模型中,这里自动调用forward方法。
_, predicted = torch.max(Pred.data, dim=1)
correct += torch.sum((predicted == y))
total += y.size(0)
print(f'测试集精准度: {100 * correct / total} %')
5. 保存训练模型
在保存模型前,必须要先进行训练网络去获取和优化模型参数。
if __name__ == '__main__':
model = DNN().to('cuda:0')
train_net()
torch.save(model,'old_model.pth')
6. 导入已经训练好的模型文件
导入训练好的模型文件,我们就不需要再进行训练网络,直接使用测试网络来测试即可。
new_model使用了原有模型文件,我们就需要在测试网络的前向传播中的模型修改为 new_model去进行测试。如下:
""" 假设我们之前保存好的模型文件为:'old_model.pth' """
def test_net():
correct = 0
total = 0
with torch.no_grad(): #该局部关闭梯度计算功能
for (x, y) in test_loader: #从批次加载器中获取小批次的x与y
x, y = x.to('cuda:0'), y.to('cuda:0')
Pred = new_model (x) #将样本放入实例化的模型中,这里自动调用forward方法。
_, predicted = torch.max(Pred.data, dim=1)
correct += torch.sum((predicted == y))
total += y.size(0)
print(f'测试集精准度: {100 * correct / total} %')
if __name__ == '__main__':
new_model = torch.load('old_model.pth')
test_net()
7. 完整代码
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets
import matplotlib.pyplot as plt
"""------------1.下载数据集----------"""
"""数据转换为tensor数据"""
transform_data = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(0.1307, 0.3081)
])
"""下载训练集与测试集"""
train_Data = datasets.MNIST(
root = 'E:/Desktop/Document/4. Python/例程代码/dataset/mnist/', """下载路径"""
train = True, """训练集"""
download = True, """如果该路径没有该数据集,就下载"""
transform = transform_data """数据集转换参数"""
)
test_Data = datasets.MNIST(
root = 'E:/Desktop/Document/4. Python/例程代码/dataset/mnist_test/', """下载路径"""
train = False, """非训练集,也就是测试集"""
download = True, """如果该路径没有该数据集,就下载"""
transform = transform_data """数据集转换参数"""
)
"""批次加载器"""
train_loader = DataLoader(train_Data, shuffle=True, batch_size=64)
test_loader = DataLoader(test_Data, shuffle=False, batch_size=64)
"""---------------2.定义模型------------"""
class DNN(nn.Module):
def __init__(self):
''' 搭建神经网络各层 '''
super(DNN,self).__init__()
self.net = nn.Sequential( # 按顺序搭建各层
nn.Flatten(), # 把图像铺平成一维
nn.Linear(784, 512), nn.ReLU(), # 第 1 层:全连接层
nn.Linear(512, 256), nn.ReLU(), # 第 2 层:全连接层
nn.Linear(256, 128), nn.ReLU(), # 第 3 层:全连接层
nn.Linear(128, 64), nn.ReLU(), # 第 4 层:全连接层
nn.Linear(64, 10) # 第 5 层:全连接层
)
def forward(self, x):
''' 前向传播 '''
y = self.net(x) # x 即输入数据
return y # y 即输出数据
"""-------------3.训练网络-----------"""
def train_net():
# 损失函数的选择
loss_fn = nn.CrossEntropyLoss() # 自带 softmax 激活函数
# 优化算法的选择
learning_rate = 0.01 # 设置学习率
optimizer = torch.optim.SGD(
model.parameters(),
lr=learning_rate,
momentum=0.5
)
epochs = 5
losses = [] # 记录损失函数变化的列表
for epoch in range(epochs):
for (x, y) in train_loader: # 获取小批次的 x 与 y
x, y = x.to('cuda:0'), y.to('cuda:0')
Pred = model(x) # 一次前向传播(小批量)
loss = loss_fn(Pred, y) # 计算损失函数
losses.append(loss.item()) # 记录损失函数的变化
optimizer.zero_grad() # 清理上一轮滞留的梯度
loss.backward() # 一次反向传播
optimizer.step() # 优化内部参数
"""Fig = plt.figure()"""
"""plt.plot(range(len(losses)), losses)"""
"""plt.show()"""
"""--------------------4.测试网络-----------"""
def test_net():
correct = 0
total = 0
with torch.no_grad(): #该局部关闭梯度计算功能
for (x, y) in test_loader: #获取小批次的 x 与 y
x, y = x.to('cuda:0'), y.to('cuda:0')
Pred = new_model(x) #一次前向传播(小批量)
_, predicted = torch.max(Pred.data, dim=1)
correct += torch.sum((predicted == y))
total += y.size(0)
print(f'测试集精准度: {100 * correct / total} %')
if __name__ == '__main__':
""" ------- 5.保存模型文件------"""
""" model = DNN().to('cuda:0') """
""" train_net() """
""" torch.save(model,'old_model.pth') """
""" ------- 6.加载模型文件 ----- """
new_model = torch.load('old_model.pth')
test_net()