数字识别(训练MNIST数据集实例)

数字识别

以下是个图片数字识别的例子,数据集来自torchvision的MNIST,使用交叉熵计算损失,使用两层的全连接进行训练。

1. 数据集的获取

获取MNIST的数据集:

dset.MNIST(root, train=True, transform=None, target_transform=None, download=False)

参数说明: - root : processed/training.ptprocessed/test.pt 的主目录 - train : True = 训练集, False = 测试集 - download : True = 从互联网上下载数据集,并把数据集放在root目录下. 如果数据集之前下载过,将处理过的数据(minist.py中有相关函数)放在processed文件夹下。

所有代码演示

def get_dataloader(train= True, batch_size=BATCH_SIZE):
    transformFn = Compose([
        ToTensor(),
        Normalize(mean=(0.1307,), std=(0.0381,))
    ])

    dataset = MNIST(root="./dataset", train=train, transform=transformFn)
    data_loader = DataLoader(dataset, batch_size=batch_size, shuffle= True) ##将获取的数据集实例化一个数据加载器

    return data_loader
dataloader = get_dataloader()	

2. 模型的损失函数

当输出只有2个分类时,我们使用sigmoid计算对数似然损失

  • 在2分类时只有正类和负类,正类概率为P(x) = 1 1 + e − x \frac {1} {1+e^{-x}} 1+ex1,那负类概率即为1-P(x)
  • 接下来通过 $-\sum yln(P(x)) $(交叉熵,经常用于评估分类的预测效果)

当输出为多分类时:

  • 跟2分类唯一区别就是不能用sigoid函数计算样本的概率,这时候改为用softmax函数。
  • softmax需要计算每类样本的概率,而sigmoid只需要一次

softmax的公式为:

P ( x i ) = e x i ∑ e x j , j = 1 , 2... n P(x_i) = \frac {e^{x_i}}{\sum e^{x_j}}, j=1,2...n P(xi)=exjexi,j=1,2...n

对于softmax的结果,属于[0, 1]区间

从而通过交叉熵函数计算损失
l o s s = − ∑ Y l o g ( P ) , P = ∑ i = 1 n P ( x i ) loss = -\sum Ylog(P)\quad ,P=\sum_{i=1}^n P(x_i) \\ loss=Ylog(P),P=i=1nP(xi)
实现交叉熵损失的函数方式:

## 1. 直接创建交叉熵损失计算器
    criterion = nn.CrossEntropyLoss()
    loss = criterion(input, target)
    
## 2. 直接自己计算
	## 计算softmax并取对数
	output = F.log_softmax(x, dim=-1)
    # 计算loss带全损失
    # F.nll_loss(x, y) =  -∑ yixi,此处也即是将log(P)作为xi,真实值当做yi
    loss = F.nll_loss(output, target)
 

3. 构建模型

class MnistMdl(nn.Module):
    def __init__(self):
        super().__init__()
        self.md1 = nn.Linear(1*28*28, 28)
        # self.bn1 = nn.BatchNorm1d(28)
        self.md2 = nn.Linear(28, 10)

    def forward(self, input):
        """
        :param input: [batch_size, 1, 28, 28]
        :return:
        """
        # 1.修改输入的形状
        x = input.view([-1, 1*28*28])
        # 2.进行全连接
        x = self.md1(x)
        # x = self.bn1(x)
        # 3.激活函数处理
        x = F.relu(x)
        # 4.输出层
        out = self.md2(x)
        return F.log_softmax(out, -1)   # out为128*10的矩阵, dim=-1即对10列的数据进行概率计算(详见下的损失函数)

4. 模型的保存与加载

模型的加载

if os.path.exists("./dataset/module/model.pkl"):
    print("loading the history file!!!")
    model.load_state_dict(torch.load("./dataset/module/model.pkl"))
    optimizer.load_state_dict(torch.load("./dataset/module/optimizer.pkl"))

模型的保存

torch.save(model.state_dict(), "./dataset/module/model.pkl")
torch.save(optimizer.state_dict(), "./dataset/module/optimizer.pkl")

5. 模型的测试与评估

  1. 测试无需对算式进行追踪:with torch.no_grad():

  2. 准确率 :

    a. 获取预测值:tensor.max(dim=-1)[-1]

    b. tensor.eq(tensor2).float().mean()

6. 程序代码演示:

from torch.optim import Adam
import numpy as np
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision.transforms import Compose, ToTensor, Normalize
from torch import nn
import torch.nn.functional as F
import torch
import os

BATCH_SIZE = 128
TEST_SIZE = 1000

# 1.获取数据集
def get_dataloader(train= True, batch_size=BATCH_SIZE):
    transformFn = Compose([
        ToTensor(),
        Normalize(mean=(0.1307,), std=(0.0381,))
    ])

    dataset = MNIST(root="./dataset", train=train, transform=transformFn)
    data_loader = DataLoader(dataset, batch_size=batch_size, shuffle= True)

    return data_loader
dataloader = get_dataloader()

# 1. 构建模型
class MnistMdl(nn.Module):
    def __init__(self):
        super().__init__()
        self.md1 = nn.Linear(1*28*28, 28)
        # self.bn1 = nn.BatchNorm1d(28)
        self.md2 = nn.Linear(28, 10)

    def forward(self, input):
        """
        :param input: [batch_size, 1, 28, 28]
        :return:
        """
        # 1.修改输入的形状
        x = input.view([-1, 1*28*28])
        # 2.进行全连接
        x = self.md1(x)
        # x = self.bn1(x)
        # 3.激活函数处理
        x = F.relu(x)
        # 4.输出层
        out = self.md2(x)
        return F.log_softmax(out, -1)   # out为128*10的矩阵, dim=-1即对10列的数据进行概率计算

model = MnistMdl()
optimizer = Adam(model.parameters(), lr=0.001)
if os.path.exists("./dataset/module/model.pkl"):
    print("loading the history file!!!")
    model.load_state_dict(torch.load("./dataset/module/model.pkl"))
    optimizer.load_state_dict(torch.load("./dataset/module/optimizer.pkl"))

def train(epoch):
    """实现训练的过程"""
    dataloader = get_dataloader()
    for idx, (input, target) in enumerate(dataloader):
        optimizer.zero_grad()   # 梯度清零
        output = model(input)
        loss = F.nll_loss(output, target) # 加权平均获得损失
        loss.backward()
        optimizer.step()
        if idx%50 == 0:
            # print(epoch, idx, loss.data)
            print("Train Epoch : ", epoch, ", 已完成 ", idx,
                  "(",'%.2f'%(100.*idx/len(dataloader)),"%)",
                  " ,损失为 : " , '%.3f'%loss.item())
        if idx%100 == 0:
            torch.save(model.state_dict(), "./dataset/module/model.pkl")
            torch.save(optimizer.state_dict(), "./dataset/module/optimizer.pkl")

def test():
    """ 测试的过程"""
    test_dataloader = get_dataloader(False, TEST_SIZE)
    loss_list = []
    acc_list = []

    for idx, (input, target) in enumerate(test_dataloader):
        with torch.no_grad():
            output = model(input)
            # 计算交叉熵损失
            loss = F.nll_loss(output, target)
            loss_list.append(loss)

            # 计算准确率
            # output:[batch_size, 10]  target:[batch_size]
            y_pred = output.max(dim=-1)[-1] # -1取该数字的索引也就是位置
            acc = y_pred.eq(target).float().mean()
            acc_list.append(acc)

    print("平均精确率为 : ", np.mean(acc_list)," ,而平均损失为: ", np.mean(loss_list) )

if __name__ == '__main__':
    test()
    for i in range(3):
        train(i)
    test()

本文仅供自己参考学习使用,如有侵权立删

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值