手写数字识别代码详解

文件目录如下,其中数据集data目录运行时在与手写数字识别同级目录自动生成,具体文件内代码见下文

一、conf.py文件

"""
项目配置
"""
import torch

train_batch_size = 128   # 训练批次大小,表示每次训练神经网络时每次使用的图像张量和标签张量的数量为128
test_batch_size = 1000   # 测试批次大小,即测试时为1000
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")   # 判断当前系统是否支持cuda来加速运算,后续用于设置模型和数据在何处运行

二、dataset.py文件

"""
准备数据集
"""
import torch
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
import torchvision
import conf

def mnist_dataset(train): #准备minist的dataset数据集,输入参数train,表示是否需要加载训练数据集,True则加载,否则加载测试数据集
    func = torchvision.transforms.Compose([   # 将多个数据处理函数组合在一起,以便对数据进行连续的转换操作,以下用来两个转换操作
        torchvision.transforms.ToTensor(),   # 将PIL image或numpy.ndarray的数据类型转为Tensor,并进行归一化处理(0~1之间)
        torchvision.transforms.Normalize(   # 对每个通道进行标准化,即先减均值再除以标准差
            mean=(0.1307,),
            std=(0.3081,)
        )]
    )

    # 1. 准备Mnist数据集
    return  MNIST(root="../data/mnist", train=train, download=True, transform=func)   # root为存储MNIST数据集的目录,train表示是否加载训练数据集,download表示是否从远程下载MNIST数据,transform表示对数据集进行的数据处理操作,最后返回处理后的MNIST数据集

def get_dataloader(train=True):   # 获取数据加载器,train=True表示是否是训练集
    mnist = mnist_dataset(train)   # 调用函数返回MNIST数据集,并将其赋值给mnist变量
    batch_size = conf.train_batch_size if train else conf.test_batch_size  # 根据是否为训练集确定批次大小
    return DataLoader(mnist,batch_size=batch_size,shuffle=True)  # 返回数据加载器对象,DataLoader是pytorch中用于数据加载的工具类,用于从给定的数据集中读取数据并返回迭代器
    # 此处将 MNIST 数据集传递给 DataLoader 对象,并指定批次大小和是否打乱数据顺序。这样,就可以在训练或测试神经网络模型时使用该迭代器来获取数据

if __name__ == '__main__':
    for (images,labels) in get_dataloader():   # 遍历数据加载器中的所有图像和标签数据,将每次循环得到的图像数据赋值给images变量,标签数据赋值给labels变量
        print(images.size())  # 打印图像数据的大小,即图形张量的形状,具体为形如(batch_size,channel,height,width)的4维张量,依次为批次大小、图像通道数、图像高度和宽度
        print(labels.size())   # 打印标签数据的大小,即标签张量的形状,具体为形如(batch_size,)的1维张量,表示批次大小
        break  # 查看第一个批次数据后停止循环,即输出第一个批次的图像和标签数据的大小

三、models.py文件

"""定义模型"""

import torch.nn as nn
import torch.nn.functional as F

class MnistModel(nn.Module):   # 定义神经网络模型,继承pytorch的nn.Module类
    def __init__(self):  # 初始化函数,用于定义网络结构和模型参数
        super(MnistModel,self).__init__()   # 继承nn.Module类的属性和方法
        self.fc1 = nn.Linear(1*28*28,100)   # 定义全连接层,包含100个神经元,即输出特征数量为100
        self.fc2 = nn.Linear(100,10)  # 定义全连接层,包含10个神经元,即输出特征数量为10,输入特征的数量为100

    def forward(self, image):   # 前向传播函数,用于进行模型推断操作,image是一个手写数字图像,
        image_viwed = image.view(-1,1*28*28) #[batch_size,1*28*28]  #形态转换,以便能够输入到全连接层
        fc1_out = self.fc1(image_viwed) #[batch_size,100]    # 将数据输入到第一个全连接层self.fc1中
        fc1_out_relu = F.relu(fc1_out) #[batch_siz3,100]   # ReLU激活函数
        out = self.fc2(fc1_out_relu) #[batch_size,10]   # 输入到第二个全连接层
        return F.log_softmax(out,dim=-1)  # 输出层使用该函数对得到的结果进行对数概率归一化处理,以便能够进行后续的损失计算和反向传播操作

四、train.py文件

"""
进行模型的训练
"""
from dataset import get_dataloader
from models import MnistModel
from torch import optim
import torch.nn.functional as F
import conf
from tqdm import tqdm
from test2 import eval
import numpy as np
import torch
import os

#1. 实例化模型,优化器,损失函数
model = MnistModel().to(conf.device)  # 创建一个MnistModel的实例,并将其移动到指定设备
optimizer = optim.Adam(model.parameters(),lr=1e-3)   # 使用Adam优化算法创建优化器,拥有优化神经网络模型中的参数,前者返回模型中所有需要训练的权重和偏置张量,后者指定学习率为0.001
                                                    # Adam优化算法是一种自适应梯度下降优化算法,可在高维空间中快速、稳定的优化神经网络模型

# if os.path.exists("./models/model.pkl"):
#     model.load_state_dict(torch.load("./models/model.pkl"))
#     optimizer.load_state_dict(torch.load("./models/optimizer.pkl"))


#2. 进行循环,进行训练
def train(epoch):   # 训练神经网络模型,epoch为训练的轮数
    train_dataloader = get_dataloader(train=True)   # 调用函数获取训练数据集的数据加载器
    bar = tqdm(enumerate(train_dataloader),total=len(train_dataloader))   # 使用tqdm()函数创建进度条,enumerate(train_dataloader)表示将数据加载器train_dataloader转换成可迭代的枚举对象,total=len(train_dataloader)表示进度条的总长度
    total_loss = []
    for idx,(input,target) in bar:   # 遍历数据加载器中的所有数据,并将其赋值给input和target
        input = input.to(conf.device)   # 将输入数据转移到指定的计算设备上,即GPU或CPU
        target = target.to(conf.device)  # 将目标数据转移到指定计算设备上
        optimizer.zero_grad()  # 将梯度清零,以避免梯度累加导致错误的梯度更新
        output = model(input)  # 将输入数据input输入到定义好的神经网络模型model中,得到输出预测结果output
        loss = F.nll_loss(output,target)   # 计算损失函数,使用负对数似然损失函数(Negative Log Likelihood Loss)进行分类问题的学习,该函数会将网络的输出结果output和目标数据target代入公式中计算出损失值并返回
        loss.backward()  # 计算反向传播,计算损失对网络参数的梯度
        total_loss.append(loss.item())   # 将计算得到的损失值添加到列表total_loss中
        optimizer.step()   # 根据梯度更新参数
        #打印数据
        if idx%10 ==0 :   # 每迭代10个批次就保存一下模型和优化器参数的状态
            bar.set_description("epcoh:{} idx:{},loss:{:.6f}".format(epoch,idx,np.mean(total_loss)))
            torch.save(model.state_dict(),"./models/model.pkl")   # # 将模型的参数保存到磁盘中
            torch.save(optimizer.state_dict(),"./models/optimizer.pkl")   # 将优化器的参数保存到磁盘中

if __name__ == '__main__':
    for i in range(10):  # 开启训练轮数为10轮的循环
        train(i)  # 调用train()函数开始训练模型
        eval()   # 训练完成后,调用eval()函数对模型进行评估

五、test2.py文件

"""
进行模型的评估
"""
from dataset import get_dataloader
from models import  MnistModel
from torch import optim
import torch.nn.functional as F
import conf
from tqdm import tqdm
import numpy as np
import torch
import os

def eval():   # 测试模型在测试集上的性能表现
    # 1. 实例化模型,优化器,损失函数
    model = MnistModel().to(conf.device)   # 实例化MnistModel的对象并将其移动到指定的设备上(CPU或GPU)

    if os.path.exists("./models/model.pkl"):  # 判断模型是否已保存到本地来恢复模型权重,若不存在,则根据MnistModel类中定义的初始化函数随机生成权重
        model.load_state_dict(torch.load("./models/model.pkl"))
    test_dataloader = get_dataloader(train=False)   # 获取测试数据集的数据加载器
    total_loss = []
    total_acc = []
    with torch.no_grad():
        for input,target in test_dataloader: #2. 进行循环,进行训练,每次使用一个批次的图像输入和对于的标签,并将数据一道指定设备上
            input = input.to(conf.device)
            target = target.to(conf.device)
            #计算得到预测值
            output = model(input)  # 使用模型前向传播获取模型输出
            #得到损失
            loss = F.nll_loss(output,target)   # 计算模型预测与真实标签间的损失loss
            #反向传播,计算损失
            total_loss.append(loss.item())   # 将该批次数据的loss值添加到数组中

            #计算准确率
            ###计算预测值
            pred = output.max(dim=-1)[-1]
            total_acc.append(pred.eq(target).float().mean().item())   # 计算该批次数据的准确率并添加到数组中
    print("test loss:{},test acc:{}".format(np.mean(total_loss),np.mean(total_acc)))   # 计算平均损失和平均准确率并打印

if __name__ == '__main__':
    # for i in range(10):
    #     train(i)
    eval()

六、运行train.py文件

输出如下

epcoh:0 idx:460,loss:0.321847: 100%|██████████| 469/469 [00:22<00:00, 21.20it/s]
test loss:0.1768832817673683,test acc:0.9481000065803528
epcoh:1 idx:460,loss:0.147730: 100%|██████████| 469/469 [00:26<00:00, 17.82it/s]
test loss:0.1185844399034977,test acc:0.9652999997138977
epcoh:2 idx:460,loss:0.101420: 100%|██████████| 469/469 [00:25<00:00, 18.67it/s]
test loss:0.09583198800683021,test acc:0.9711999952793121
epcoh:3 idx:460,loss:0.079119: 100%|██████████| 469/469 [00:25<00:00, 18.42it/s]
test loss:0.08689267784357071,test acc:0.9724000036716461
epcoh:4 idx:460,loss:0.061671: 100%|██████████| 469/469 [00:29<00:00, 15.77it/s]
test loss:0.0784779790788889,test acc:0.9767000019550324
epcoh:5 idx:460,loss:0.051475: 100%|██████████| 469/469 [00:30<00:00, 15.20it/s]
test loss:0.07767344787716865,test acc:0.975299996137619
epcoh:6 idx:460,loss:0.042745: 100%|██████████| 469/469 [00:29<00:00, 16.05it/s]
test loss:0.07635272592306137,test acc:0.9771999955177307
epcoh:7 idx:460,loss:0.034893: 100%|██████████| 469/469 [00:31<00:00, 14.98it/s]
test loss:0.0841908399015665,test acc:0.9741999983787537
epcoh:8 idx:460,loss:0.029657: 100%|██████████| 469/469 [00:31<00:00, 15.10it/s]
test loss:0.09327867105603219,test acc:0.9734000027179718
epcoh:9 idx:460,loss:0.023495: 100%|██████████| 469/469 [00:34<00:00, 13.76it/s]
test loss:0.08141878321766853,test acc:0.976800000667572

学习导航:https://www.xqnav.top/

猜你喜欢

转载自blog.csdn.net/qq_43874317/article/details/130054272
今日推荐