(四)PyTorch——多分类问题及深层次神经网络(mnist数字识别问题)

MNIST是一个非常有名的手写数字识别数据集,在很多资料中,这个数据集都会被用作深度学习的入门案例。
MNIST数据集是NIST数据集的一个子集,它包含了60000张图片作为训练数据,10000张图片作为测试数据。在MNIST数据集中的每一张图片都代表了0~9中的一个数字。图片的大小都为28x28,且数字都会出现在图片的正中间。

数字图片及其像素矩阵:(MNIST数据集中图片的像素大小为28x28,为了更清楚的展示,图中显示的是14x14矩阵)

在这里插入图片描述

MNIST提供了四个文件:

train-images-idx3-ubyte.gz (训练图像数据60000个)

train-labels-idx1-ubyte.gz (训练图像数据标签60000个)

t10k-images-idx3-ubyte.gz (测试图像数据10000个)

t10k-labels-idx1-ubyte.gz (测试图像数据标签10000个)
 

PyTorch和TensorFlow都内置mnist数据集。

TensorFlow实现MNIST数字识别问题

多分类问题,loss函数使用一个更加复杂的函数,叫交叉熵。

softmax

sigmoid函数可以将任何一个值转化到0~1之间,对于一个二分类问题,这样就足够了,如果不属于第一类,那么必定属于第二类,所以只需要用一个值来表示其属于其中一类概率,但是对于多分类问题,需要知道其属于每一类的概率,这个时候需要softmax函数。

MNIST

import numpy as np
import torch
from torchvision.datasets import mnist#导入pytorch内置的mnist数据集
from torch import nn
from torch.autograd import Variable

#使用内置函数下载mnist数据集
train_set=mnist.MNIST('/data',train=True,download=True)
test_set=mnist.MNIST('/data',train=False,download=True)

#查看其中的一个数据集
a_data,a_label=train_set[0]
print(a_data)
print(a_label)

下载完后的文件:

 

#这里读入的数据是PIL库中的格式,可以非常方便地将其转化为numpy array
a_data=np.array(a_data,dtype='float32')
print(a_data.shape)
#我们可以看到这种图片的大小是28X28
print(a_data)

完整代码:

import numpy as np
import torch
from torchvision.datasets import mnist#导入pytorch内置的mnist数据集
from torch import nn
from torch.autograd import Variable
import matplotlib.pyplot as plt


#我们可以将数组展示出来,里面的0就表示黑色,255表示白色
#对于神经网络,我们第一层的输入就是28*28=784,所以必须将得到的数据做一个变换
#使用reshape将他们拉平成一个一维向量

def data_tf(x):
    x=np.array(x,dtype='float32')/255
    x=(x-0.5)/0.5#标准化
    x=x.reshape((-1,))#拉平
    x=torch.from_numpy(x)
    return x

train_set=mnist.MNIST('data',train=True,transform=data_tf,download=True)#重新载入数据集,申明定义的数据变换
test_set=mnist.MNIST('data',train=False,transform=data_tf,download=True)
a,a_label=train_set[0]
print(a.shape)
print(a_label)

#使用pytorch自带的DataLoader定义一个数据迭代器
from torch.utils.data import DataLoader
train_data=DataLoader(train_set,batch_size=64,shuffle=True)
test_data=DataLoader(test_set,batch_size=128,shuffle=False)

#使用这样的数据迭代器是非常有必要的,如果数据量太大,就无法一次将他们全部读入内存,所以需要使用Python迭代器,每次生成一个批次的数据
a,a_label=next(iter(train_data))
print(a.shape)
print(a_label.shape)

#使用Sequential定义4层神经网络
net=nn.Sequential(
    nn.Linear(784,400),
    nn.ReLU(),
    nn.Linear(400,200),
    nn.ReLU(),
    nn.Linear(200,100),
    nn.ReLU(),
    nn.Linear(100,10)
)

#交叉熵在pyTorch中已经内置了,交叉熵的数值稳定性更差,所以内置的函数
#帮我们解决了这个问题

#定义loss函数
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(net.parameters(),1e-1)#使用随机梯度下降,学习率0.1

#开始训练
losses=[]
acces=[]
eval_losses=[]
eval_acces=[]

for e in range(20):
    train_loss=0
    train_acc=0
    net.train()
    for im,label in train_data:
        im=Variable(im)
        label=Variable(label)
        #前向传播
        out=net(im)
        loss=criterion(out,label)
        #反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        #记录误差
        train_loss+=loss.item()
        #计算分类的准确率
        _,pred=out.max(1)
        num_correct=(pred==label).sum().item()
        acc=num_correct/im.shape[0]
        train_acc+=acc
    losses.append(train_loss/len(train_data))
    acces.append(train_acc/len(train_data))
    #在测试集上检验效果
    eval_loss=0
    eval_acc=0
    net.eval()#将模型改为预测模式
    for im,label in test_data:
        im=Variable(im)
        label=Variable(label)
        out=net(im)
        loss=criterion(out,label)
        #记录误差
        eval_loss+=loss.item()
        #记录准确率
        _,pred=out.max(1)
        num_correct=(pred==label).sum().item()
        acc=num_correct/im.shape[0]
        eval_acc+=acc
    eval_losses.append(eval_loss/len(test_data))
    eval_acces.append(eval_acc/len(test_data))
    print('epoch:{},Train Loss:{:.6f},Train Acc:{:.6f},Eval Loss:{:.6f},Eval ACC:{:.6f}'
          .format(e,train_loss/len(train_data),train_acc/len(train_data),
                  eval_loss/len(test_data),eval_acc/len(test_data)))

#画出loss曲线和准确率曲线
plt.title("train loss")
plt.plot(np.arange(len(losses)),losses)
plt.show()

plt.plot(np.arange(len(acces)),acces)
plt.title('train acc')
plt.show()

plt.plot(np.arange(len(eval_losses)),eval_losses)
plt.title('test loss')
plt.show()

plt.plot(np.arange(len(eval_acces)),eval_acces)
plt.title('test acc')
plt.show()


运行结果:

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/qq_41251963/article/details/104136614