手写数字识别

# 手写数字识别
import torch
import torchvision  #是Pytorch的一个图形库,服务于pytorch深度学习的框架,构建计算机视觉模型,做常见的图形变换
import torch.nn as nn
#显示进度条库
from tqdm import tqdm
import torch.optim as optim
import torch.nn.functional as F

import torchvision.datasets as dsets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
device = "cuda:0" if torch.cuda.is_available() else "cpu"
#定义一些训练需要用到的超参数
image_size=28 #图片的总尺寸28*28
num_classes=10 #标签的种类数
num_epochs=20 #训练的总循环周期
batch_size = 64 #一个训练批次的大小,64张图片

#这个函数包括了两个操作:将图片转换为张量,以及将图片进行归一化处理
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor(),
                                torchvision.transforms.Normalize(mean = [0.5],std = [0.5])])

#加载数据集
train_dataset= dsets.MNIST(root='./data', #文件存储路径
                           train=True, #提取训练集
                           transform=transforms.ToTensor(), #将图像转化为张量
                            download=True )#找不到文件时候,自动下载

#加载测试集
test_dataset= dsets.MNIST(root='./data',train=False
                        ,transform=transforms.ToTensor())

#训练集的加载器,自动将数据切分成批,顺序随机打乱
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,batch_size=batch_size,shuffle=True)#shuffle=True意思是顺序随机打乱

#首先,定义下标数组indices,它相当于对所有的test_dataset中的数据编码
#然后,定义下表indices_val表示校验集数据的下表。indices_test表示测试集的下标
indices=range(len(test_dataset))
indices_val=indices[:5000]
indices_test=indices[5000:]

#根据下标构造的两个数据集的SubsetRandomSampler采样器,他会对下标进行采样
sampler_val=torch.utils.data.sampler.SubsetRandomSampler(indices_val)
sampler_test=torch.utils.data.sampler.SubsetRandomSampler(indices_test)

#根据两个采样器定义加载器
# 注意将sampler_val和sampler_test分别赋值给了validation_loader和test_loader
validation_loader=torch.utils.data.DataLoader(dataset=test_dataset,batch_size=batch_size,shuffle=False,sampler=sampler_val)
test_loader=torch.utils.data.DataLoader(dataset=test_dataset,batch_size=batch_size,shuffle=False,sampler=sampler_test)

# #随便从数据集读取一张图片,并且绘制出来
#
# idx=100
# muteimg=train_dataset[idx][0].numpy()
#
# plt.imshow(muteimg[0,...])
# plt.show()
# print('标签是:',train_dataset[idx][1])

#构建网络
#构造一个Conv类,重写__ini__函数和forwa函数,__init__函数是构造函数,每当调用Conv类的时候具体化一个实例,都会调用构造函数,forward函数在进行正向神经网络时候被自动调用,且构造计算图
#再写一个retrieve_features函数,可以提取网络中各个卷积层的权重
#定义卷积神经网络:4和8为人为指定的两个卷积层的厚度
depth=[4,8]
class ConvNet(nn.Module):
    def __init__(self):
        super(ConvNet,self).__init__()
        #定义一个卷积层,输入通道为1,输出通道为4,窗口大小为5,padding为2
        self.conv1=nn.Conv2d(1,4,5,padding=2)
        #定义一个池化层,一个窗口为2*2的池化运算
        self.pool1=nn.MaxPool2d(2,2)
        #第二层卷积,输入通道为depth[0],输出通道为depth[1],窗口为5,padding为2
        self.conv2=nn.Conv2d(depth[0],depth[1],5,padding=2)

        #一个线性连接层,输入尺寸为后一层立方体的线性平铺,输出层512个节点
        self.fc1=nn.Linear(image_size//4*image_size//4*depth[1],512)  #除以4是因为经过了一个池化层2*2,大小变为原来的1/4
        self.fc2=nn.Linear(512,num_classes) #最后一层线性分类单元,输入为512,输出为要分类的类别数,也就是10(0-9)
    def forward(self,x): #该函数完成神经网络的前向运算,在这里把各个组件进行实际的拼装
        #x的size
        x=self.conv1(x) # 第一层卷积
        x=F.relu(x) # 激活函数用Relu,防止过拟合
        # 第二层池化层,将图像缩小
        x=self.pool1(x)
        #第三层卷积层
        x=self.conv2(x) #窗口为5,输入输出通道为4,8

        #第四层池化
        x=self.pool1(x)
        #此时x的size(batch_size,depth[1],image_width/4,image_height/4

        #将三维向量打成一个一维向量 view函数可以将一个tensor按指定方式重新排布
        x=x.view(-1,image_size//4*image_size//4*depth[1])
        x=F.relu(self.fc1(x)) #第五层为全连接层,使用relu激活函数

        #x的size (batch_size,512)

        #以默认0.5的概率对这一层进行dropout的操作,防止过拟合?
        x=F.dropout(x,training=self.training)
        x=self.fc2(x)#全连接
        #x的尺寸:(batch_size,num_classes)


        #输出曾为log_softmax,
        x=F.log_softmax(x,dim=1)
        return x
    def retrieve_dfeatures(self,x):
        #该函数用于提取卷积神经网络的特征图,返回feature_map1
        #feature_map2为前两层卷积层的特征图
        feature_map1=F.relu(self.conv1(x)) #完成第一层卷积
        x=self.pool1(feature_map1) # 完成第一层池化
        #第二层卷积,两层特征图都存储到了 1 2 中
        feature_map2=F.relu(self.conv2(x))
        return (feature_map1,feature_map2)




#运行模型
net=ConvNet() # 新建一个卷积神经网络的实例,此时构造函数init会自动执行
# net.to(device)
criterion=nn.CrossEntropyLoss() # loss函数的定义,交叉熵
optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
#定义优化器,普通的随机梯度下降法

record=[] # 记录准确率等数值的容器
weights=[] # 每若干步记录一次卷积核
#开始循环训练
def rightness(output, target):
    preds = output.data.max(dim=1, keepdim=True)[1]
    return preds.eq(target.data.view_as(preds)).cpu().sum(), len(target)



for epoch in range(num_epochs):
    train_rights=[] # 记录训练集上准确率的容器
    for batch_idx,(data,target) in enumerate(train_loader): #对容器中的没一个批次进行循环
        data,target=data.clone().requires_grad_(True),target.clone().detach()
        #给网络模型做标记,标记着模型在训练集上训练
        #这种区分主要是为了打开/关闭net的training标志,从而决定是否运行dropout
        net.train()

        output=net(data) #神经网络完成一次前馈啥的计算过程,得到预测输出output
        loss=criterion(output,target) #将output与标签target比较,计算loss
        optimizer.zero_grad() #清空梯度
        loss.backward()
        optimizer.step() #一步随机梯度下降算法
        right=rightness(output,target) #计算准确率所需的数值,返回数值为(正确数,总样本数)
        train_rights.append(right)
        if batch_idx %100 ==0:
            net.eval() #给网络模型做标记,标志着模型在训练集上训练
            val_rights=[] # 将校验集上的准确率的容器

            # 开始在校验集上做循环 计算校验集上的准确率
            for(data,target) in validation_loader:
                data,target=data.clone().requires_grad_(True),target.clone().detach()
                #完成一次前馈计算过程,得到目前训练的模型net在校验集上的表现
                output=net(data)
                #计算准确率所需的数值,返回数值为(正确样例数,总样本数)
                right=rightness(output,target)
                val_rights.append(right)
                train_r=(sum([tup[0] for tup in train_rights]),sum([tup[1] for tup in train_rights]))
                val_r=(sum([tup[0] for tup in val_rights]),sum([tup[1] for tup in val_rights]))
            print('训练周期:{}[{}/{}({:.0f}%)]\t,Loss:{:.6f}\t,训练准确率:{:.2f}%\t,校验准确率:{:.2f}%'.format(epoch,batch_idx*len(data),len(train_loader.dataset)
                                                                                                                   ,100.*batch_idx/len(train_loader),loss.data,
                                                                                                                   100.*train_r[0]/train_r[1],
                                                                                                                   100.*val_r[0]/val_r[1]
                                                                                                                   ))
            record.append((100-100.*train_r[0]/train_r[1],100-100.*val_r[0]/val_r[1]))
            weights.append([net.conv1.weight.data.clone(),net.conv1.bias.data.clone(),
                               net.conv2.weight.data.clone(),net.conv2.bias.data.clone()])
#在测试集上分批运行并计算总的准确率
net.eval() #标志着模型为运行阶段
vals=[] # 记录准确率所用的列表
#测试
for data,target in test_loader:
    data,target=data.clone().requires_grad_(True),target.clone().detach()
    output=net(data)
    val=rightness(output,target)
    vals.append(val)
#计算准确率
plt.figure(figsize=(10,7))
plt.plot(record)
plt.xlabel('Steps')
plt.ylabel('Error rate')



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值