深度学习 Pytorch Mnist手写数字识别

Mnist手写数字识别

概要

使用torch的相关类实现神经网络各个部分,对手写数字数据集mnist_train_small.csv(需要可以私信qq1365157644发)进行手写数字识别训练。

具体内容

读取数据 生成对应的DataLoader

使用pandas读取数据集

mnist = pd.read_csv("sample_data/mnist_train_small.csv", header = None)

这个数据集总共20000张(1x28x28)的手写数字图片,在这个csv文件中,第一列表示的是各个样本的标签(0-9)其他784列是每个像素点的值(0-255)mnist_train_small.csv
可以通过matplootlib展示图片,这里展示的是第一个样本,从上图也可以看出第一个样本的标签为6。

plt.imshow(mnist.iloc[0,1:].values.reshape(28,28))  
plt.title(mnist.iloc[0,0])

第一个样本图片
通过实现torch.utils.data.Dataset抽象类,为mnist生成专属的dataset

class Mnist(Dataset):
  def __init__(self, mnist):
    self.mnist = mnist
  def __len__(self):
    return self.mnist.shape[0]
  def __getitem__(self,idx):
    img = self.mnist.iloc[idx,1:].values.astype(np.float32)
    label = self.mnist.iloc[idx,0]
    return img, label

这里重写了__len__,__getitem__方法,第一个方法返回一共多少个样本,第二样本就是通过idx依次返回每个样本的feature和label。这里要注意:当为其生成dataloader时,这里的numpy类型的数据也就变成了tensor类型,在tensor后面的计算中需要的数据类型是torch.float(float32),在numpy中float数据类型有三种表示方法float,float32,float64(这只是我知道的)其中第一种和第三种在转换成tensor类型后会变成double。

m_train = Mnist(mnist.iloc[:15000,:])
m_test = Mnist(mnist.iloc[15000:,:])
mnistTrain = DataLoader(m_train, batch_size = 100, num_workers=2)
mnistTest = DataLoader(m_test, batch_size = 100, num_workers=2)

这里选取了前面3/4的数据作为你测试集,后面1/4作为测试集,DataLoader其中的主要参数,可以参考dataloader参数

网络结构

class MnistNet(nn.Module):
  def __init__(self):
    super(MnistNet,self).__init__()
    self.fc1 = nn.Linear(784, 50)
    self.fc2 = nn.Linear(50, 10)

  def forward(self,input):
    x = self.fc1(input)
    x = F.relu(x)
    out = self.fc2(x)
    return F.log_softmax(out, dim = 1)

需要继承nn.Module类,重写forward类,这里使用了ANN,第一层784x50,输出层50x10(因为有10各类别)。最后这个log_softmax,由于要实现多分类,这里的log_softmax就是先进行了普通的softmax操作,将10个输出映射到0-1,并且10个输出加起来等于1,这样就可以用概率很好的解释这个样本的预测标签(自己感觉),在将这10个结果取对数(因为方便后面直接使用nll_loss直接求损失)这里也可以直接返回结果不用log_softmax,后面选择损失函数时直接使用交叉熵损失(CrossEntropyLoss)也能达到一样的效果。

优化器

optimizer = Adam(model.parameters(), lr = 0.001)

优化器也是使用torch.optim现成的Adam(还没学)

损失函数

交叉熵损失,由于需要实现多分类不能直接使用sigmoid函数(它输出一个0-1的概率值反映而分类中正类的概率)。(还没学)

训练模型

def train(epoch):
  model.train()
  for idx, (input, target) in enumerate(mnistTrain):
    optimizer.zero_grad()
    output = model(input)
    loss = F.nll_loss(output, target)
    loss.backward()
    optimizer.step()
    print('Train Epoch: {} [{}/{} ({:.0f}%)]\t\tLoss: {:.6f}'.format((epoch+1), (idx+1)*len(input), len(mnistTrain.dataset), 100.*(idx+1)/len(mnistTrain), loss.item()))

训练模型时要注意需要每次将梯度置0,不然它会叠加,调用backward计算梯度,optimizer.step根据梯度优化参数。

评估模型

def test():
  model.eval()
  loss_list = []
  acc_list = []
  for idx, (input, target) in enumerate(mnistTest):
    with torch.no_grad():
      output = model(input)
      cur_loss = F.nll_loss(output, target)
      loss_list.append(cur_loss)
      pred = output.max(dim = 1)[-1]
      cur_acc = pred.eq(target).float().mean()
      acc_list.append(cur_acc)
  print("====================\naccuracy:{}   loss:{}".format(np.mean(acc_list), np.mean(loss_list)))

评估模型不需要优化参数,所以优化器和backward方法都不需要调用,这里要算准确率,通过output找出最大值的位置(tensor.max()返回的就是最大值和最大值的位置),这个位置就是这次预测的预测标签再用eq方法与target对比,查看一个batch中有多少预测准确的(返回的是[True,Flase……])通过float转成0,1再用mean方法算平均值。

整体代码

import torch
import numpy as np
import pandas as pd 
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
from torch.nn import Linear
import torch.nn as nn
import torch.nn.functional as F
from torch.optim import Adam

mnist = pd.read_csv("sample_data/mnist_train_small.csv", header=None)

class Mnist(Dataset):
  def __init__(self, mnist):
    self.mnist = mnist
  def __len__(self):
    return self.mnist.shape[0]
  def __getitem__(self,idx):
    img = self.mnist.iloc[idx,1:].values.astype(np.float32)
    label = self.mnist.iloc[idx,0]
    return img, label
    
m_train = Mnist(mnist.iloc[:15000,:])
m_test = Mnist(mnist.iloc[15000:,:])
mnistTrain = DataLoader(m_train, batch_size = 100, num_workers=2)
mnistTest = DataLoader(m_test, batch_size = 100, num_workers=2)

class MnistNet(nn.Module):
  def __init__(self):
    super(MnistNet,self).__init__()
    self.fc1 = nn.Linear(784, 50)
    self.fc2 = nn.Linear(50, 10)

  def forward(self,input):
    x = self.fc1(input)
    x = F.relu(x)
    out = self.fc2(x)
    return F.log_softmax(out, dim = 1)

model = MnistNet()
optimizer = Adam(model.parameters(), lr = 0.001)

def train(epoch):
  model.train()
  for idx, (input, target) in enumerate(mnistTrain):
    optimizer.zero_grad()
    output = model(input)
    loss = F.nll_loss(output, target)
    loss.backward()
    optimizer.step()
    print('Train Epoch: {} [{}/{} ({:.0f}%)]\t\tLoss: {:.6f}'.format((epoch+1), (idx+1)*len(input), len(mnistTrain.dataset), 100.*(idx+1)/len(mnistTrain), loss.item()))


def test():
  model.eval()
  loss_list = []
  acc_list = []
  for idx, (input, target) in enumerate(mnistTest):
    with torch.no_grad():
      output = model(input)
      cur_loss = F.nll_loss(output, target)
      loss_list.append(cur_loss)
      pred = output.max(dim = 1)[-1]
      cur_acc = pred.eq(target).float().mean()
      acc_list.append(cur_acc)
  print("====================\naccuracy:{}   loss:{}".format(np.mean(acc_list), np.mean(loss_list)))

for i in range(3):
  train(i)
test()

学到了哪些

了解了sigmoid函数,softmax函数,交叉熵损失(一点浅显的东西),拉了一遍神经网络模型构建训练的流程,学会了使用dataset和dataloader定义自己简单的数据集。

总结

曾试图想着看dataset和dataloader的源码,但是去看了下后发现自己好像没学过python一样,给我一种就像换了语言写的感觉,看不懂!可能目前水平不够,还没资格看懂。
通过这次手写数字识别,发现自己太懒了,特别是这两天(虽然才开始)又不想学了,总会逃避一些问题,没有把整个流程走完(保存模型,加载模型,怎么转成onnx应用等等)
没完成上次说的了解tensor的一些操作(明天单独补上!)
加油加油!坚持!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值