# 1 先导入模块,并设置基本参数:
import torch
import torch.nn as nn # 神经网络
import torchvision # 数据集及深度学习模型
import torchvision.transforms as transforms # 用于将数据集转化为tensor
# 基本参数设置
# input_size = 784 # 输入变量个数
# num_classes = 10 # 分类数目
num_epochs = 5 # 迭代次数
batch_size = 100 # 训练模型时一次性输入的样本数目
learning_rate = 1e-3 # 学习率
# 2 数据预处理流程
transform = transforms.Compose([
transforms.Resize((32, 32)), # 将图片大小从28*28调整为32x32
transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为torch.Tensor
transforms.Normalize((0.5,), (0.5,)) # 归一化到[0,1]
])
# 2 下载并导入数据集
data_path = "./data/MNIST" # 在当前路径下创建MNIST文件夹
# 2.1 训练集设置(若没有下载,则下载之)
train_dataset = torchvision.datasets.MNIST(
root = data_path, # 数据集路径
train = True, # 是否为训练集,设置为True:即将数据设置为训练集
# transform = transforms.ToTensor(), # 将数据转化为张量
transform = transform,
download = True) # 下载数据集
# 2.2 测试集设置
test_dataset = torchvision.datasets.MNIST(
root = data_path, # 数据集路径
train = False, # 是否为训练集,设置为False:即为测试集
# transform = transforms.ToTensor(), # 将数据转化为张量
transform = transform,
download = True) # 下载数据集
# 2.3 构建训练集数据加载器
train_loader = torch.utils.data.DataLoader(
dataset = train_dataset, # 训练集数据
batch_size = batch_size, # 训练模型时一次性输入的样本数目
shuffle = True) # 打乱顺序
# 2.4 构建测试集数据加载器
test_loader = torch.utils.data.DataLoader(
dataset = test_dataset, # 测试集数据
batch_size = batch_size, # 训练模型时一次性输入的样本数目
shuffle = False) # 不打乱顺序
# 2.4 可视化数据集图像
import matplotlib.pyplot as plt
n = 10 # 展示10张图像
plt.figure(figsize=(10, 5))
for i in range(n):
images, labels = train_dataset[i]
plt.subplot(2, 5, i+1)
plt.imshow(images[0].view(32, 32), cmap='gray')
plt.title(f'Label: {labels}')
plt.show()
# 导入模块
import torch.nn as nn
import torch.nn.functional as F
# 构建一个LeNet类
class LeNet(nn.Module):
def __init__(self):
super(LeNet,self).__init__()
self.tmp_dict = {}
# 输入图片单通道:1;输出通道数:6;卷积核为5*5
self.conv1 = nn.Conv2d(1,6,5)
# 输入通道:6;输出通道数:16;卷积核为5*5
self.conv2 = nn.Conv2d(6,16,5)
# 全连接 y = wx+b
self.fc1 = nn.Linear(16*5*5,120)
self.fc2 = nn.Linear(120,84)
self.fc3 = nn.Linear(84,10)
def forward(self,x):
# 第一次卷积:1卷积-2激活-3池化:
x = self.conv1(x) # 卷积:输入图片单通道:1;输出通道数:6;卷积核为5*5
# print(x.size()) # 打印conv2层的输出形状
x = F.relu(x) # ReLU激活函数
x = F.max_pool2d(x,2) # 最大池化,模板2*2
# print(x.size()) # 打印conv2层的输出形状
# 第二次卷积:1卷积-2激活-3池化:
x = self.conv2(x) # 卷积:输入6通道:6;输出通道数:16;卷积核为5*5
# print(x.size()) # 打印conv2层的输出形状
x = F.relu(x) # ReLU激活函数
x = F.max_pool2d(x,2) # 最大池化,模板2*2
# print(x.size()) # 打印conv2层的输出形状
# 数据展平
x = x.view(x.size()[0],-1) # 展平之后,作为输入接入全连接网络
# 全连接网络1:
x = F.relu(self.fc1(x)) # 线性模型1
# 全连接网络2
x = F.relu(self.fc2(x)) # 线性模型2
return self.fc3(x) # 输出层
# 3 构建卷积神经网络
model = LeNet()
print(model) # 输出
# 4 模型训练
# 4.1 设置损失函数
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数,其内部集成了softmax()函数
# 4.2 设置优化方法:随机梯度下降法
# optimizer = torch.optim.SGD(model.parameters(),lr = learning_rate)
optimizer = torch.optim.Adam(model.parameters(),lr = learning_rate)
# 4.3 训练模型:遍历
for epoch in range(num_epochs):
for i, (images,labels) in enumerate(train_loader): # 从训练集中获取数据,包括序号,(特征数据,标签数据)
# 4.3.1 转换数据格式:28*28 ---> 1*784
# images = images.reshape(-1,28*28)
# 4.3.2 前向传播:导入数据,输出计算结果值
outputs = model(images)
# 4.3.3 计算损失值
loss = criterion(outputs,labels)
# 4.3.4 反向传播及优化
optimizer.zero_grad() # 初始化梯度
loss.backward() # 自动求导的函数,用于计算损失函数对模型参数的梯度,实现反向传播算法。
optimizer.step() # 更新梯度
# 4.3.5 打印日志
if (i+1)%300 ==0:
print(f"Epoch:{epoch+1},Step:{i+1},loss:{loss.item()}") # 即:打印第几轮中的第几步,当前loss是多少
# 5 模型测试
# 5.1 禁用梯度计算
# 解释:PyTorch默认每一次前向传播都会计算梯度
# with torch.no_grad()是PyTorch中的一个上下文管理器,用于在进入该上下文时,禁用梯度计算。
with torch.no_grad():
correct = 0 # 设置“正确数量”的初始值为0
total = 0 # 设置“总数”的初始值为0
for images,labels in test_loader: # 测试集加载器
# images = images.reshape(-1,28*28) # 转换数据格式:28*28 ---> 1*784,用于模型输入
outputs = model(images) # 将数据待入训练好的模型进行测试
_,predicted = torch.max(outputs,1) # 返回每一行中的最大值_及其对应的索引predicted。dim=1代表按行处理。
total = total + labels.size(0) # 累加每批次处理的测试样本数目,从而计算总的测试样本数目
# print(f"total:{total}")
correct += (predicted == labels).sum() # 先计算每批次的正确预测数目,并进行累加,从而计算总的测试样本数目
print(f"Accuracy of the model on the 10000 test images is :{100 * correct / total:.2f}%")