(基于PyTorch)神经网络手写数字识别代码&解释

(基于PyTorch)神经网络手写数字识别代码&解释,第1张

依旧使用了数据集MNIST中的部分数据。


1、PyTorch基本知识

import torch
#简单的PyTorch张量
x=torch.tensor(3.5)
print(x)
#变量x的类型是PyTorch张量

 

tensor(3.5000)输出值的含义是,该变量的值是3.5000,同时它被包装在一个PyTorch张量中。


#简单的张量计算
y=x+3
print(y)
#生成的y也是一个PyTorch张量

 

x=torch.tensor(3.5,requires_grad=True)
#通过x的参数requires_grad=True告诉PyTorch我们希望得到一个关于x的梯度
print(x)

y=(x-1)*(x-2)*(x-3)
print(y)

#计算梯度
y.backward()
#x=3.5时的梯度
x.grad

#创建包含x y和z的计算图
x=torch.tensor(3.5,requires_grad=True)
y=x*x
z=2*y+3
#计算梯度
z.backward()
#当x=3.5时的梯度
x.grad

#创建包含x y和z的计算图
a=torch.tensor(2.0,requires_grad=True)
b=torch.tensor(1.0,requires_grad=True)

x=2*a+3*b
y=5*a*a+3*b*b*b
z=2*x+3*y

#计算梯度
z.backward()

#当a=2.0时的梯度
a.grad

#导入pandas库,用于读取csv文件
import pandas

#使用pandas将训练数据读取到一个DataFrame中
df=pandas.read_csv('mnist_train.csv',header=None)

#使用head()函数查看一个较大的DataFrame的前几行
df.head()

#使用info()函数查看DataFrame的概况
df.info()

 

 以上结果告诉我们,该DataFrame有200行,这对应200幅训练图像,同时,每行有785个值。


#导入pandas库用于读取csv文件
import pandas
#导入matplotlib用于绘图
import matplotlib.pyplot as plt

#从DataFrame读取数据
row=0
#row=0表示第一行
data=df.iloc[row]
#选择数据集的第一行并赋值给变量data

#第一个值是标签
label=data[0]

#图像是余下的784个值
img=data[1:].values.reshape(28,28)
#选取剩余的784个值,将他们重新映射成为一个28x28的正方形数组,并赋值给变量img
plt.title("label="+str(label))
#在标题显示之前提取的标签
plt.imshow(img,interpolation='none',cmap='Blues')
#pyplot无须平滑像素以及调色板的颜色为蓝色
plt.show()

 2、全部代码

2.1 导入包 

import numpy
import torch
import torch.nn as nn
#将torch.nn模块作为nn导入,是一种常见的命名方式
from torch.utils.data import Dataset
#将数据载入torch.utils.data.Dataset
import pandas
#导入pandas库用于读取csv文件
import matplotlib.pyplot as plt
#导入matplotlib用于绘图

 2.2 神经网络

class Classifier(nn.Module):
    #Classifier类继承了nn.Module
    def __init__(self):
        #__init__(self)是一个特殊的函数,当我们从一个类最终创建对象时需要调用它,它也被称为构造函数
        #初始化PyTorch父类,调用父类构造函数
        super().__init__()
        
        #定义神经网络层
        self.model=nn.Sequential(
            #nn.Sequential()提供了一个神经模块的列表
            nn.Linear(784,200),
            #nn.Linear(784,200)是一个从784个节点到200个节点的全连接映射。


这个模块包含节点之间链接的权重,在训练时会被更新 #nn.Sigmoid(), #nn.Sigmoid()将S型逻辑激活函数应用于前一个模块的输出,也就是本例中200个节点的输出 nn.LeakyReLU(0.02), #改进:将激活函数改为LeakReLU(0.02),其中0.02是函数左半边的梯度 nn.LayerNorm(200), #标准化,减少神经网络中参数和信号的取值范围,以及将均值转换为0 #在网络信号输出最终层之前使用nn.LayerNorm(200)将它们标准化 nn.Linear(200,10), #nn.Linear(200,10)是将200个节点映射到10个节点的全连接映射,它包含中间隐藏层与输出层10个节点之间链接的权重 nn.Sigmoid() #nn.Sigmoid()再将S型逻辑激活函数应用于10个节点的输出。


其结果就是网络的最终输出 #nn.LeakyReLU(0.02) #改进:将激活函数改为LeakReLU(0.02),其中0.02是函数左半边的梯度 ) #nn.Linear:当数值从输入端传递到输出端时,该模块对它们应用了Ax+B形式的线性函数。


这里,A为链接权重,B为偏差。


这两个参数都会在训练时被更新。


它们也被称为可学习参数。


#创建损失函数 #self.loss_function=nn.MSELoss() #均方误差:先计算每个输出节点的实际输出和预期输出之差的平方,再计算平均值,即torch.nn.MSELoss() #适合情况:回归任务,输入值设计为连续范围的值,如,一个预测温度的网络会输出0~100°C的任何值 self.loss_function=nn.BCELoss() #改进:对图像进行分类,分类任务(判断true/false或者0/1),常用二元交叉熵损失,它同时惩罚置信度高的错误输出和置信值低的正确输出,即nn.BCELoss() #但是BCE只能处理0~1的值,而Leaky ReLU有可能输出范围之外的值,则,可以在最终层之后保留一个S型函数,但是在隐藏层之后使用LeakyReLU #创建优化器,使用简单的梯度下降 #self.optimiser=torch.optim.SGD(self.parameters(),lr=0.01) #随机梯度下降(SGD),将学习率设置为0.01 #在上面的代码中,我们通过self.parameters()访问所有可学习参数,并将所有可学习参数都传递给SGD优化器 self.optimiser=torch.optim.Adam(self.parameters()) #随机梯度下降的缺点:会陷入损失函数的局部最小值;对所有可学习的参数都使用单一的学习率 #改进:Adam,利用动量的概念,减小陷入局部最小值的可能性,想象一下:一个沉重的球如何利用动量滚过一个小坑;对每个可学习参数使用单独的学习率,这些学习率随着每个参数在训练期间的变化而变化 #可视化训练,跟踪训练的一种方法是监控损失 #记录训练进展的计数器和列表 self.counter=0 self.progress=[] pass def forward(self,inputs): #通过forward()方法向网络传递信息 #直接运行模型 return self.model(inputs) #将输入传递给self.model(),它由nn.Sequential()定义,模型的输出直接返回给forward()的主调函数 def train(self,inputs,targets): #train()既需要网络的输出值,也需要预期的目标值,这样才可以与实际输出进行比较,并计算损失值 #计算网络的输出值 outputs=self.forward(inputs) #计算损失值 loss=self.loss_function(outputs,targets) #使用损失来更新网络的链接权重 #梯度归零,反向传播,并更新权重 self.optimiser.zero_grad() #optimiser.zero_grad()将计算图中的梯度全部归零 loss.backward() #loss.backward()从loss函数中计算网络中的梯度 #每次训练网络之前,需要将梯度归零,否则,每次loss.backward()计算出来的梯度会累积 self.optimiser.step() #optimiser.step()使用这些梯度来更新网络的可学习参数 #每隔10个训练样本增加一次计数器的值,并将损失值添加进列表的末尾 self.counter+=1 if(self.counter%10==0): self.progress.append(loss.item()) #item()函数是为了方便展开一个单值张量,获取里面的数字 pass if(self.counter%1000==0): #每1000次训练后打印计数器的值,这样可以了解训练进展的快慢 print("counter=",self.counter) pass def plot_progress(self): #将损失值绘制成图 df=pandas.DataFrame(self.progress,columns=['loss']) #将损失值列表progress转换为一个pandas DataFrame,便于绘图 df.plot(ylim=(0,1.0),figsize=(16,8),alpha=0.1,marker='.',grid=True,yticks=(0,0.25,0.5)) #使用plot()函数的选项,设计图的设计和风格 pass

2.3 数据集类

class MnistDataset(Dataset):
    def __init__(self,csv_file):
        self.data_df=pandas.read_csv(csv_file,header=None)
        #csv_file被读入一个名为data_df的pandas DataFrame中
        pass
    
    def __len__(self):
        #__len__()返回数据集中的项目总数
        return len(self.data_df)
        #通过len(mnist_dataset)获取数据集的大小
    
    def __getitem__(self,index):
        #__getitem__()通过所以返回数据集中的第n项,例如使用mnist_dataset[3]访问第4项
        #目标图像(标签)
        label=self.data_df.iloc[index,0]
        #第index项中提取标签label
        target=torch.zeros((10))
        #创建维度为10的张量变量target来表示神经网络的预期输出
        target[label]=1.0
        #除了与标签相对应的项是1之外,其他值皆为0,这种表示方法叫做独热编码
        #比如,标签0所对应的张量是[1,0,0,0,0,0,0,0,0,0],标签4所对应的张量是[0,0,0,0,1,0,0,0,0,0]
        
        #图像数据,取值范围是0~255,标准化为0~1
        image_values=torch.FloatTensor(self.data_df.iloc[index,1:].values)/255.0
        
        #返回标签、图像数据张量以及目标张量
        return label,image_values,target
    
    def plot_image(self,index):
        #绘图
        arr=self.data_df.iloc[index,1:].values.reshape(28,28)
        plt.title("label="+str(self.data_df.iloc[index,0]))
        plt.imshow(arr,interpolation='none',cmap='Blues')
    pass

2.4 使用训练数据训练神经网络

mnist_dataset=MnistDataset('mnist_train.csv')
#导入训练数据mnist_train.csv
mnist_dataset.plot_image(9)
#绘制第10幅图像,第10幅图像的索引是9,因为第一幅图像的索引是0

mnist_dataset[9]

%%time
#%%time可以用来记录本单元格运行所需时间
#创建神经网络
C=Classifier()

#在MNIST数据集训练神经网络
epochs=3

for i in range(epochs):
    print('training epoch',i+1,"of",epochs)
    for label,image_data_tensor,target_tensor in mnist_dataset:
        C.train(image_data_tensor,target_tensor)
    pass
pass

#绘制分类器损失值
C.plot_progress()
#损失值得下降意味着网络分类图像能力越来越好
#生成的图每次都会有点区别,因为神经网络本质是一个随机过程
#可以看出,改进之后的网络,损失值下降的速率要高于原始网络(如果我们考虑噪声在图中的密度,而不考虑高度,则损失值得噪声也比较少)

 2.5 测试数据

#加载MNIST测试数据
mnist_test_dataset=MnistDataset('mnist_test.csv')

#挑选一幅图像,选择索引为19的第20幅图像
record=19

#绘制图像和标签
mnist_test_dataset.plot_image(record)

#接下来看看神经网络如何判断这副图像
image_data=mnist_test_dataset[record][1]

#调用训练后的神经网络
output=C.forward(image_data)

#绘制输出张量
pandas.DataFrame(output.detach().numpy()).plot(kind='bar',legend=False,ylim=(0,1))
#输出被转换成一个简单的numpy数组,再被包装成一个DataFrame,以便绘制柱形图
#10条柱形分别对应10个神经网络输出节点的值,最大值对应4,也就是说我们的网络认为图像是4

#测试用训练数据训练后的网络

score=0
items=0

for label,image_data_tensor,target_tensor in mnist_test_dataset:
    answer=C.forward(image_data_tensor).detach().numpy()
    if(answer.argmax()==label):
        #answer.argmax()语句的作用是输出张量answer中最大值的索引。


如果第一个值是最大的,则argmax是0 score+=1 pass items+=1 pass print(score,items,score/items)

 准确率96%,感觉挺好~

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/567721.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-09
下一篇 2022-04-09

发表评论

登录后才能评论

评论列表(0条)

保存