其他分享
首页 > 其他分享> > pytorch实现手写字识别

pytorch实现手写字识别

作者:互联网

使用Pytorch实现手写数字识别


目标


知道如何使用Pytorch完成神经网络的构建
知道Pytorch中激活函数的使用方法
知道Pytorch中torchvision.transforms中常见图形处理函数的使用
知道如何训练模型和如何评估模型

1. 思路和流程分析

流程:


准备数据,这些需要准备DataLoader
构建模型,这里可以使用torch构造一个深层的神经网络
模型的训练
模型的保存,保存模型,后续持续使用
模型的评估,使用测试集,观察模型的好坏

2. 准备训练集和测试集

准备数据集的方法前面已经讲过,但是通过前面的内容可知,调用MNIST返回的结果中图形数据是一个Image对象,需要对其进行处理

为了进行数据的处理,接下来学习torchvision.transfroms的方法


2.1 torchvision.transforms的图形数据处理方法


2.1.1 torchvision.transforms.ToTensor


把一个取值范围是[0,255]的PIL.Image或者shape为(H,W,C)的numpy.ndarray,转换成形状为[C,H,W]

其中(H,W,C)意思为(高,宽,通道数),黑白图片的通道数只有1,其中每个像素点的取值为[0,255],彩色图片的通道数为(R,G,B),每个通道的每个像素点的取值为[0,255],三个通道的颜色相互叠加,形成了各种颜色

示例如下:


    
  1. from torchvision import transforms
  2. import numpy as np
  3. data = np.random.randint( 0, 255, size= 12)
  4. img = data.reshape( 2, 2, 3)
  5. print(img.shape)
  6. img_tensor = transforms.ToTensor()(img) # 转换成tensor
  7. print(img_tensor)
  8. print(img_tensor.shape)
  9. 输出如下:
  10. shape:( 2, 2, 3)
  11. img_tensor:tensor([[[ 215, 171],
  12.                  [ 34,   12]],
  13.                 [[ 229,   87],
  14.                  [ 15, 237]],
  15.                 [[ 10,   55],
  16.                  [ 72, 204]]], dtype=torch.int32)
  17. new shape:torch.Size([ 3, 2, 2])

注意:

transforms.ToTensor对象中有__call__方法,所以可以对其示例能够传入数据获取结果


2.1.2 torchvision.transforms.Normalize(mean, std)


给定均值:mean,shape和图片的通道数相同(指的是每个通道的均值),方差:std,和图片的通道数相同(指的是每个通道的方差),将会把Tensor规范化处理。

即:Normalized_image=(image-mean)/std。

例如:


    
  1. from torchvision import transforms
  2. import numpy as np
  3. import torchvision
  4. data = np.random.randint( 0, 255, size= 12)
  5. img = data.reshape( 2, 2, 3)
  6. img = transforms.ToTensor()(img) # 转换成tensor
  7. print(img)
  8. print( "*"* 100)
  9. norm_img = transforms.Normalize(( 10, 10, 10), ( 1, 1, 1))(img) #进行规范化处理
  10. print(norm_img)
  11. 输出如下:
  12. tensor([[[ 177, 223],
  13.          [ 71, 182]],
  14.         [[ 153, 120],
  15.          [ 173,   33]],
  16.         [[ 162, 233],
  17.          [ 194,   73]]], dtype=torch.int32)
  18. ***************************************************************************************
  19. tensor([[[ 167, 213],
  20.          [ 61, 172]],
  21.         [[ 143, 110],
  22.          [ 163,   23]],
  23.         [[ 152, 223],
  24.          [ 184,   63]]], dtype=torch.int32)
  25. 注意:在sklearn中,默认上式中的std和me

an为数据每列的std和mean,sklearn会在标准化之前算出每一列的std和mean。

但是在api:Normalize中并没有帮我们计算,所以我们需要手动计算


当mean为全部数据的均值,std为全部数据的std的时候,才是进行了标准化。
如果mean(x)不是全部数据的mean的时候,std(y)也不是的时候,Normalize后的数据分布满足下面的关系

newmean=mean−xy,mean为原数据的均值,x为传入的均值x newstd=stdy,y为传入的标准差y newmean=mean−xy,mean为原数据的均值,x为传入的均值x newstd=stdy,y为传入的标准差y 
最后,会计算每个样本的损失,即上式的平均值

我们把softmax概率传入对数似然损失得到的损失函数称为交叉熵损失

在pytorch中有两种方法实现交叉熵损失

criterion = nn.CrossEntropyLoss()
loss = criterion(input,target)

#1. 对输出值计算softmax和取对数
output = F.log_softmax(x,dim=-1)
#2. 使用torch中带权损失
loss = F.nll_loss(output,target)

带权损失定义为:$l_n = -\sum w_{i} x_{i}$,其实就是把$log(P)$作为$x_i$,把真实值Y作为权重


4. 模型的训练

训练的流程:


实例化模型,设置模型为训练模式
实例化优化器类,实例化损失函数
获取,遍历dataloader
梯度置为0
进行向前计算
计算损失
反向传播
更新参数


    
  1. mnist_net = MnistNet()
  2. optimizer = optim.Adam(mnist_net.parameters(),lr= 0.001)
  3. def train(epoch):
  4.     mode = True
  5.     mnist_net.train(mode=mode) #模型设置为训练模型
  6.     train_dataloader = get_dataloader(train=mode) #获取训练数据集
  7.     for idx,(data,target) in enumerate(train_dataloader):
  8.         optimizer.zero_grad() #梯度置为0
  9.         output = mnist_net(data) #进行向前计算
  10.         loss = F.nll_loss(output,target) #带权损失
  11.         loss.backward()   #进行反向传播,计算梯度
  12.         optimizer.step() #参数更新
  13.         if idx % 10 == 0:
  14.             print( 'Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
  15.                 epoch, idx * len(data), len(train_dataloader.dataset),
  16.                         100. * idx / len(train_dataloader), loss.item()))

5. 模型的保存和加载


5.1 模型的保存

torch.save(mnist_net.state_dict(),"model/mnist_net.pt") #保存模型参数
torch.save(optimizer.state_dict(), 'results/mnist_optimizer.pt') #保存优化器参数

5.2 模型的加载

mnist_net.load_state_dict(torch.load("model/mnist_net.pt"))
optimizer.load_state_dict(torch.load("results/mnist_optimizer.pt"))

6. 模型的评估

评估的过程和训练的过程相似,但是:


不需要计算梯度
需要收集损失和准确率,用来计算平均损失和平均准确率
损失的计算和训练时候损失的计算方法相同
准确率的计算:


模型的输出为[batch_size,10]的形状
其中最大值的位置就是其预测的目标值(预测值进行过sotfmax后为概率,sotfmax中分母都是相同的,分子越大,概率越大)
最大值的位置获取的方法可以使用torch.max,返回最大值和最大值的位置
返回最大值的位置后,和真实值([batch_size])进行对比,相同表示预测成功


    
  1. def test():
  2.     test_loss = 0
  3.     correct = 0
  4.     mnist_net.eval()   #设置模型为评估模式
  5.     test_dataloader = get_dataloader(train= False) #获取评估数据集
  6.     with torch.no_grad(): #不计算其梯度
  7.         for data, target in test_dataloader:
  8.             output = mnist_net(data)
  9.             test_loss += F.nll_loss(output, target, reduction= 'sum').item()
  10.             pred = output.data.max( 1, keepdim= True)[ 1] #获取最大值的位置,[batch_size,1]
  11.             correct += pred.eq(target.data.view_as(pred)).sum()   #预测准备样本数累加
  12.     test_loss /= len(test_dataloader.dataset) #计算平均损失
  13.     print( '\nTest set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format(
  14.         test_loss, correct, len(test_dataloader.dataset),
  15.         100. * correct / len(test_dataloader.dataset)))
  16. 7. 完整的代码如下:
  17. import torch
  18. from torch import nn
  19. from torch import optim
  20. import torch.nn.functional as F
  21. import torchvision
  22. train_batch_size = 64
  23. test_batch_size = 1000
  24. img_size = 28
  25. def get_dataloader(train=True):
  26.     assert isinstance(train,bool), "train 必须是bool类型"
  27.     #准备数据集,其中0.1307,0.3081为MNIST数据的均值和标准差,这样操作能够对其进行标准化
  28.     #因为MNIST只有一个通道(黑白图片),所以元组中只有一个值
  29.     dataset = torchvision.datasets.MNIST( '/data', train=train, download= True,
  30.                                          transform=torchvision.transforms.Compose([
  31.                                          torchvision.transforms.ToTensor(),
  32.                                          torchvision.transforms.Normalize(( 0.1307,), ( 0.3081,)),]))
  33.     #准备数据迭代器
  34.     batch_size = train_batch_size if train else test_batch_size
  35.     dataloader = torch.utils.data.DataLoader(dataset,batch_size=batch_size,shuffle= True)
  36.     return dataloader
  37. class MnistNet(nn.Module):
  38.     def __init__(self):
  39.         super(MnistNet,self).__init__()
  40.         self.fc1 = nn.Linear( 28* 28* 1, 28)
  41.         self.fc2 = nn.Linear( 28, 10)
  42.     def forward(self,x):
  43.         x = x.view( -1, 28* 28* 1)
  44.         x = self.fc1(x) #[batch_size,28]
  45.         x = F.relu(x)   #[batch_size,28]
  46.         x = self.fc2(x) #[batch_size,10]
  47.         # return x
  48.         return F.log_softmax(x,dim= -1)
  49. mnist_net = MnistNet()
  50. optimizer = optim.Adam(mnist_net.parameters(),lr= 0.001)
  51. # criterion = nn.NLLLoss()
  52. # criterion = nn.CrossEntropyLoss()
  53. train_loss_list = []
  54. train_count_list = []
  55. def train(epoch):
  56.     mode = True
  57.     mnist_net.train(mode=mode)
  58.     train_dataloader = get_dataloader(train=mode)
  59.     print(len(train_dataloader.dataset))
  60.     print(len(train_dataloader))
  61.     for idx,(data,target) in enumerate(train_dataloader):
  62.         optimizer.zero_grad()
  63.         output = mnist_net(data)
  64.         loss = F.nll_loss(output,target) #对数似然损失
  65.         loss.backward()
  66.         optimizer.step()
  67.         if idx % 10 == 0:
  68.             print( 'Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
  69.                 epoch, idx * len(data), len(train_dataloader.dataset),
  70.                         100. * idx / len(train_dataloader), loss.item()))
  71.             train_loss_list.append(loss.item())
  72.             train_count_list.append(idx*train_batch_size+(epoch -1)*len(train_dataloader))
  73.             torch.save(mnist_net.state_dict(), "model/mnist_net.pkl")
  74.             torch.save(optimizer.state_dict(), 'results/mnist_optimizer.pkl')
  75. def test():
  76.     test_loss = 0
  77.     correct = 0
  78.     mnist_net.eval()
  79.     test_dataloader = get_dataloader(train= False)
  80.     with torch.no_grad():
  81.         for data, target in test_dataloader:
  82.             output = mnist_net(data)
  83.             test_loss += F.nll_loss(output, target, reduction= 'sum').item()
  84.             pred = output.data.max( 1, keepdim= True)[ 1] #获取最大值的位置,[batch_size,1]
  85.             correct += pred.eq(target.data.view_as(pred)).sum()
  86.     test_loss /= len(test_dataloader.dataset)
  87.     print( '\nTest set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format(
  88.         test_loss, correct, len(test_dataloader.dataset),
  89.         100. * correct / len(test_dataloader.dataset)))
  90. if __name__ == '__main__':
  91.     test()  
  92.     for i in range( 5): #模型训练5轮
  93.         train(i)
  94.         test()

 

标签:loss,dataloader,pytorch,train,mnist,test,识别,data,写字
来源: https://blog.csdn.net/weixin_44682222/article/details/111723024