简单搭建卷积神经网络实现手写数字10分类

搭建卷积神经网络实现手写数字10分类

1.思路流程

1.导入minest数据集

2.对数据进行预处理

3.构建卷积神经网络模型

4.训练模型,评估模型

5.用模型进行训练预测

一.导入minest数据集

 

MNIST--->raw--->test-->(0,1,2...) 10个文件夹

MNIST--->raw--->train-->(0,1,2...) 10个文件夹

共60000张图片.可自己去网上下载

二.对数据进行预处理

----读取图片,将图片先转为张量。

img=cv2.imread(path)

----将图片进行归一化,即将像素值标准化到0-1之间

img_tensor=util.transforms_train(img)

----裁剪,翻转等,实现数据增强。

数据增强:通过对原始图像进行旋转、翻转等操作,可以增加数据的多样性。这有助于模型学习到更具泛化性的特征,减少对特定方向或位置的依赖,从而提高模型的鲁棒性和准确性

transforms_train=transforms.Compose([
    # transforms.CenterCrop(10),
    # transforms.PILToTensor(),
    transforms.ToTensor(),#归一化,转tensor
    transforms.Resize((28,28)),
    transforms.RandomVerticalFlip()
])

ps:为什么要归一化

  1. 消除量纲影响:不同图像的像素值范围可能差异很大。归一化可以将像素值范围统一到一个特定的区间,例如 [0, 1] 或 [-1, 1],消除不同图像之间因像素值范围差异带来的影响,使模型更关注图像的特征和结构,而不是像素值的绝对大小。

  2. 提高训练稳定性:有助于优化算法的收敛性和稳定性。如果像素值范围较大且分布不均匀,可能导致梯度计算不稳定,从而影响模型的训练效率和效果。

  3. 缓解过拟合:一定程度上可以减少数据中的噪声和异常值对模型的影响,降低模型对某些特定像素值的过度依赖,从而提高模型的泛化能力,减少过拟合的风险。

三.构建卷积神经网络模型

常见卷积神经网络(CNN),主要由卷积,池化,全连接组成。卷积核在输入图像上滑动,通过卷积运算提取局部特征。卷积核在整个图像上重复使用,大大减少了模型的参数数量,降低了计算复杂度,同时也增强了模型对平移不变性的鲁棒性。池化层对特征进行压缩,提取主要特征,减少噪声和冗余信息。

x=torch.randn(2,3,28,28)

用x表示初始图形的信息。为了简单理解,简单表述。其中

2--->两张图片

3--->图片的通道数是3个,即 RGB

28,28--->图片的宽高是28px 28px

采用以上的神经网络conv为卷积操作,maxpool为池化。Linear为全连接。relu为激活函数。

进入全连接层时需要将展平。torch.Size([2, 16, 5, 5])--->torch.Size([2, 400])

x=torch.flatten(x,1)

因为全连接是只进行的线性的变化。所以要把每张图片的维数参数降为1。

使用print(summary(net, x))可查看网络的层次结构。其中-1就表示自己算,是多少张图片就是多少

输入的的是x=torch.randn(2,3,28,28),最终输出的是(2,10)

四.训练模型,评估模型

需要初始化之前的数据和网络,然后选择合适的优化器和损失函数,学习率和加载图片的批次去训练模型。使用loss_avg和accurary来评估模型的性能。对于pytorch来说优化器可以实现自动梯度清0,自动更新参数。我们需要主要的是就实现其中的维度的转化。loss越小越接近真实值。其中计算精度的方法使用one-hot编码。其中0表示[0,0,0,0,0,0,0,0,0,0],1表示[0,1,0,0,0,0,0,0,0,0],2表示[0,0,1,0,0,0,0,0,0,0].。。。其他依次类推。我们把用网络得出的参数,类似[0.1,0.2,0.1,0.5,0,0,0,0,0,0](数据我随便写的),然后用Python的argmax去处最大值的索引与one-hot真实值的索引相比,如果相等就是正确的结果。

----本次实验使用的是MSE损失函数

----lr(学习率)设为0.01

----使用的优化器Adam ,其实其他优化器你也可以随便试试。

Adam 算法的主要优点包括:

  1. 自适应学习率:能够为每个参数自适应地调整学习率。

  2. 偏差校正:在初始阶段对梯度估计进行校正,加速初期的学习速率。

  3. 适应性强:在很多不同的模型和数据集上都表现出良好的性能。

  4. 实现简单,计算高效,对内存需求少。

使用tensorboard进行可视化

五.用模型进行训练预测

需要读取之前训练好的模型,然后用这个模型来实现预测一个自己手写的图片

    # 加载整个模型
    loaded_model = torch.load('whole_model.pth')
​
    # 保存模型参数
    torch.save(loaded_model.state_dict(),'model_params.pth')

代码附上:

dataset.py

import glob
import os.path
​
import cv2
import torch
import util
​
class DataAndLabel:
    def __init__(self,path='D:\\0MNIST\\raw',is_train=True):
        super().__init__()
        # 拼接路径
        #data里面是path,label
        clas='train' if is_train==True else 'test'
        path=os.path.join(path,clas)
        paths=glob.glob(os.path.join(path,'*','*'))
        # print(paths)
        # print(path)
        self.data=[]
        for path in paths:
            label=int(path.split('\\')[-2])
            self.data.append((path,label))
    def __getitem__(self, idx):
        #返回一个tensor,one-hot
        path,label =self.data[idx]
        img=cv2.imread(path)
        # cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
        img_tensor=util.transforms_train(img)
        one_hot=torch.zeros(10)
        one_hot[label]=1
        return img_tensor,one_hot
    def __len__(self):
        return len(self.data)
# if __name__ == '__main__':
#     data=DataAndLabel()
#     print(data[0])
#     print()

lenet5.py

import torch
import torch.nn as nn
from torchkeras import summary
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1=nn.Conv2d(3,6,5,1)
        self.maxpool1=nn.MaxPool2d(2)
        self.conv2=nn.Conv2d(6,16,3,1)
        self.maxpool2=nn.MaxPool2d(2)
        self.layer1=nn.Linear(16*5*5,10)
        self.layer2=nn.Linear(10,10)
        self.relu=nn.Softmax()
    def forward(self,x):
        x=self.conv1(x)
        x=self.relu(x)
        x=self.maxpool1(x)
        x=self.conv2(x)
        x=self.relu(x)
        x=self.maxpool2(x)
        # print(x.shape)
        x=torch.flatten(x,1)
        # print(x.shape)
​
        x=self.layer1(x)
        x=self.layer2(x)
        return x
if __name__ == '__main__':
    x=torch.randn(2,3,28,28)
    net=Net()
    out=net(x)
    # print(out.shape)
    # print(summary(net, x))

train_and_test

import torch
import tqdm
from torch.utils.data import Dataset, DataLoader
from torch.utils.tensorboard import SummaryWriter
from lenet5 import Net
import torch.nn as nn
from dataset import DataAndLabel
class TrainAndTest(Dataset):
    def __init__(self):
        super().__init__()
        # self.writer=SummaryWriter("logs")
        net=Net()
        self.net=net
        self.loss=nn.MSELoss()
        self.opt = torch.optim.Adam(net.parameters(), lr=0.1)
        self.train_data=DataAndLabel(is_train=True)
        self.test_data=DataAndLabel(is_train=False)
        self.train_loader=DataLoader(self.train_data,batch_size=100,shuffle=False)
        self.test_loader=DataLoader(self.test_data,batch_size=100,shuffle=False)
    # 拿到数据,网络
    def train(self,epoch):
        loss_sum = 0
        accurary_sum = 0
        for img_tensor, label in tqdm.tqdm(self.train_loader, desc='train...', total=len(self.train_loader)):
            out = self.net(img_tensor)
            loss = self.loss(out, label)
            self.opt.zero_grad()
            loss.backward()
            self.opt.step()
            loss_sum += loss.item()
            accurary_sum += torch.mean(
                torch.eq(torch.argmax(label, dim=1), torch.argmax(out, dim=1)).to(torch.float32)).item()
        loss_avg = loss_sum / len(self.train_loader)
        accurary_avg = accurary_sum / len(self.train_loader)
        print(f'train---->loss_avg={round(loss_avg, 3)},accurary_avg={round(accurary_avg, 3)}')
        # self.writer.add_scalars('loss',{'loss_avg':loss_avg},epoch)
    def train1(self):
        sum_loss = 0
        sum_acc = 0
        for img_tensors, targets in tqdm.tqdm(self.train_loader, desc="train...", total=len(self.train_loader)):
            out = self.net(img_tensors)
            loss = self.loss(out, targets)
            self.opt.zero_grad()
            loss.backward()
            self.opt.step()
            sum_loss += loss.item()
            pred_cls = torch.argmax(out, dim=1)
            target_cls = torch.argmax(targets, dim=1)
            accuracy =torch.mean(torch.eq(pred_cls, target_cls).to(torch.float32))
            sum_acc += accuracy.item()
        avg_loss = sum_loss / len(self.train_loader)
        avg_acc = sum_acc / len(self.train_loader)
        print(f'train:loss{round(avg_loss, 3)} acc:{round(avg_acc, 3)}')
​
​
    def run(self):
        for epoch in range(10):
            self.train1()
            # self.test(epoch)
if __name__ == '__main__':
    tt=TrainAndTest()
    tt.run()

util.py

from torchvision import transforms
​
transforms_train=transforms.Compose([
    # transforms.CenterCrop(10),
    # transforms.PILToTensor(),
    transforms.ToTensor(),#归一化,转tensor
    transforms.Resize((28,28)),
    transforms.RandomVerticalFlip()
])
transforms_test=transforms.Compose([
    transforms.ToTensor(),  # 归一化,转tensor
    transforms.Resize((28, 28)),
])

  • 40
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西柚与蓝莓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值