CNN-卷积神经网络

【代码】BP神经网络识别手写数字

作者 : 老饼 发表日期 : 2024-02-25 22:05:01 更新日期 : 2024-03-07 20:23:40
本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com



本文展示一个BP神经网络用于手写数字识别的例子,并用pytorch进行实现与训练

通过本文可以具体了解如何使用BP神经网络是如何解决手写数字识别问题的





    01. BP神经网络识别手写数字-数据介绍    




本节介绍手写数字样本数据,并针对手写数字预测问题设计CNN模型




      手写数字-数据介绍    


手写数字数据集MNIST是pytorch的自带数据之一,利用torchvision.datasets.MNIST就可以下载
手写数字数据集MNIST包含了10个手写数字(0-9)的7W个样本(训练样本6W个,测试样本1W个)
MNIST样本示例如下:
  
每个样本是28*28的单通道灰度图片







    02. BP神经网络识别手写数字      




本节使用pytorch来实现BP神经网络识别写手数字问题(借助pytorch的神经网络框架)




      BP神经网络识别手写数字-代码   


在使用BP神经网络预测手写数字时,需要先将图片输入进行展平,展平后共计28*28=784个输入
根据输入个数,不妨使用100个隐节点的三层BP神经网络模型对图片类别进行预测
  具体的BP神经网络识别手写数字代码如下:
import torch
from   torch import nn
from   torch.utils.data   import DataLoader
import torchvision
import numpy as np

#--------------------模型结构--------------------------------------------
# 定义神经网络的结构
class ConvNet(nn.Module):
    def __init__(self):
        super(ConvNet, self).__init__()
        self.nn_stack=nn.Sequential(
            nn.Flatten(),          # 对输入进行展平
            nn.Linear(28*28, 100),  
            nn.Tanh(),                                                      
            nn.Linear(100, 10)                       
            )
    def forward(self, x):
        y = self.nn_stack(x)
        return y

#-----------------------模型训练---------------------------------------
# 参数初始化函数
def init_param(model):
    # 初始化权重阈值                                                                         
    param_list = list(model.named_parameters())                                                # 将模型的参数提取为列表                      
    for i in range(len(param_list)):                                                           # 逐个初始化权重、阈值
        is_weight = i%2==0                                                                     # 如果i是偶数,就是权重参数,i是奇数就是阈值参数
        if is_weight:                                                                          
            torch.nn.init.normal_(param_list[i][1],mean=0,std=0.01)                            # 对于权重,以N(0,0.01)进行随机初始化
        else:                                                                                  
           torch.nn.init.constant_(param_list[i][1],val=0)                                     # 阈值初始化为0
  
# 训练函数                                                                                     
def train(dataloader,valLoader,model,optimizer,epochs,goal,device):                                      
    for epoch in range(epochs):                                                                
        err_num  = 0                                                                           # 本次epoch评估错误的样本
        eval_num = 0                                                                           # 本次epoch已评估的样本
        print('-----------当前epoch:',str(epoch),'----------------')                           
        for batch, (imgs, labels) in enumerate(dataloader):                                    
		    # -----训练模型-----                                                               
            x, y = imgs.to(device), labels.to(device)                                          # 将数据发送到设备
            optimizer.zero_grad()                                                              # 将优化器里的参数梯度清空
            py   = model(x)                                                                    # 计算模型的预测值   
            loss = lossFun(py, y)                                                              # 计算损失函数值
            loss.backward()                                                                    # 更新参数的梯度
            optimizer.step()                                                                   # 更新参数
			# ----计算错误率----                                                               
            idx      = torch.argmax(py,axis=1)                                                 # 模型的预测类别
            eval_num = eval_num + len(idx)                                                     # 更新本次epoch已评估的样本
            err_num  = err_num +sum(y != idx)                                                  # 更新本次epoch评估错误的样本
            if(batch%10==0):                                                                   # 每10批打印一次结果
                print('err_rate:',err_num/eval_num)                                            # 打印错误率
        # -----------验证数据误差---------------------------                                   
        model.eval()                                                                           # 将模型调整为评估状态
        val_acc_rate = calAcc(model,valLoader,device)                                          # 计算验证数据集的准确率
        model.train()                                                                          # 将模型调整回训练状态
        print("验证数据的准确率:",val_acc_rate)                                                # 打印准确率    
        if((err_num/eval_num)<=goal):                                                          # 检查退出条件
            break                                                                              
    print('训练步数',str(epoch),',最终训练误差',str(err_num/eval_num))                         

# 计算数据集的准确率                                                                           
def calAcc(model,dataLoader,device):                                                           
    py = np.empty(0)                                                                           # 初始化预测结果
    y  = np.empty(0)                                                                           # 初始化真实结果
    for batch, (imgs, labels) in enumerate(dataLoader):                                        # 逐批预测
        cur_py =  model(imgs.to(device))                                                       # 计算网络的输出
        cur_py = torch.argmax(cur_py,axis=1)                                                   # 将最大者作为预测结果
        py     = np.hstack((py,cur_py.detach().cpu().numpy()))                                 # 记录本批预测的y
        y      = np.hstack((y,labels))                                                         # 记录本批真实的y
    acc_rate = sum(y==py)/len(y)                                                               # 计算测试样本的准确率
    return acc_rate                                                                               

#--------------------------主流程脚本----------------------------------------------       
#-------------------加载数据--------------------------------
train_data = torchvision.datasets.MNIST(
    root       = 'D:\pytorch\data'                                                             # 路径,如果路径有,就直接从路径中加载,如果没有,就联网获取
    ,train     = True                                                                          # 获取训练数据
    ,transform = torchvision.transforms.ToTensor()                                             # 转换为tensor数据
    ,download  = True                                                                          # 是否下载,选为True,就下载到root下面
    ,target_transform= None)                                                                   
val_data = torchvision.datasets.MNIST(
    root       = 'D:\pytorch\data'                                                             # 路径,如果路径有,就直接从路径中加载,如果没有,就联网获取
    ,train     = False                                                                         # 获取测试数据
    ,transform = torchvision.transforms.ToTensor()                                             # 转换为tensor数据
    ,download  = True                                                                          # 是否下载,选为True,就下载到root下面
    ,target_transform= None)                                                                   
                                                                                               
#-------------------模型训练--------------------------------                                   
trainLoader = DataLoader(train_data, batch_size=1000, shuffle=True)                            # 将数据装载到DataLoader
valLoader   = DataLoader(val_data  , batch_size=1000)                                          # 将验证数据装载到DataLoader 
device      = torch.device('cuda' if torch.cuda.is_available() else 'cpu')                     # 设置训练设备  
model       = ConvNet().to(device)                                                             # 初始化模型,并发送到设备  
lossFun     = torch.nn.CrossEntropyLoss()                                                      # 定义损失函数为交叉熵损失函数
optimizer   = torch.optim.SGD(model.parameters(), lr=0.01,momentum =0.9)                       # 初始化优化器
train(trainLoader,valLoader,model,optimizer,1000,0.01,device)                                  # 训练模型,训练100步,错误低于1%时停止训练

# -----------模型效果评估--------------------------- 
model.eval()                                                                                   # 将模型切换到评估状态
train_acc_rate = calAcc(model,trainLoader,device)                                              # 计算训练数据集的准确率
print("训练数据的准确率:",train_acc_rate)                                                      # 打印准确率
val_acc_rate = calAcc(model,valLoader,device)                                                  # 计算验证数据集的准确率
print("验证数据的准确率:",val_acc_rate)                                                        # 打印准确率





     运行结果    


运行结果如下:
-----------当前epoch: 0 ----------------
err_rate: tensor(0.9100)                
err_rate: tensor(0.7925)                
err_rate: tensor(0.5954)                
err_rate: tensor(0.4929)                
err_rate: tensor(0.4363)                
err_rate: tensor(0.3978)                
验证数据的准确率: 0.8145                
-----------当前epoch: 1 ----------------
.........             
        .........                     
  -----------当前epoch: 249 ----------------
err_rate: tensor(0.0040)                
err_rate: tensor(0.0084)                
err_rate: tensor(0.0090)                
err_rate: tensor(0.0093)                
err_rate: tensor(0.0096)                
err_rate: tensor(0.0098)                
--------------------------------------- 
验证数据的准确率: 0.9761                 
  训练步数 249 ,最终训练误差 tensor(0.0099)  
  训练数据的准确率: 0.9902166666666666       
 验证数据的准确率: 0.9761    
                       
可以看到,验证数据(由于实际并未用于验证,所以其实是测试数据)的准确率已经达到97.6%,说明模型是有效的











 End 






联系老饼