手写数字识别实战

全部代码:

import matplotlib.pyplot
import torch
from torch import nn  # nn是完成神经网络相关的一些工作
from torch.nn import functional as F  # functional是常用的一些函数
from torch import optim  # 优化的工具包

import torchvision
from matplotlib import pyplot as plt
from utils import plot_images, plot_curve, one_hot

# step1  :  load dataset
# 指定了每次梯度更新时用于训练模型的数据样本数量
batch_size = 512     # 一次处理的图片数量  我们的处理的图片是28×28像素
train_loader = torch.utils.data.DataLoader(
    torchvision.datasets.MNIST('mnist_data', train=True, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),  # 把numpy格式转换为tensor
                                   torchvision.transforms.Normalize(   # 图像像素分布在0-1,所以要-0.1307,除以标准差0.3801,使得数据能够在0附近均匀分布
                                       (0.1307,), (0.3081,))
                               ])),
    # 1.torchvision.transforms.Normalize 是 PyTorch 中的一个非常有用的图像预处理转换(transform),
    # 它主要用于将图像数据标准化到特定的均值(mean)和标准差(std)上。这个转换通常用于训练深度学习模型之前,
    # 特别是卷积神经网络(CNN)模型,因为标准化有助于模型更快地收敛并提高模型的性能。

    # 2.这里,(0.1307,) 和 (0.3081,) 分别指定了用于标准化的均值和标准差。注意,虽然这两个元组只包含一个元素,
    # 但它们实际上是为每个通道(channel)指定的。在这个特定的例子中,由于这些值是针对MNIST数据集的,而MNIST数据集是灰度图像,所以只有一个通道。

    # 3.虽然这里直接给出了均值(0.1307)和标准差(0.3081),但在实际应用中,这些值通常是通过计算整个训练数据集的像素值的统计量来获得的。
    # 对于MNIST这样的灰度数据集,计算整个数据集的像素均值和标准差,然后用于所有图像的标准化。

    batch_size=batch_size, shuffle=True)   # batch_size一次行处理多少张图片,shuffle意味着加载时要做一个随机的打散


test_loader = torch.utils.data.DataLoader(
    torchvision.datasets.MNIST('mnist_data/', train=False, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,))
                               ])),
    batch_size=batch_size, shuffle=False)

x, y = next(iter(train_loader))
# x代表当前批次(batch)中的输入数据,即图像数据。对于MNIST数据集来说,x的形状通常是[batch_size, 1, 28, 28](如果数据没有被转换为灰度图并归一化,
# 则可能是[batch_size, 3, 28, 28],但MNIST是灰度图,所以通道数为1)。这里的batch_size是你在创建DataLoader时指定的每个批次中的样本数。

# y代表当前批次中每个输入数据对应的标签(label),即每个图像对应的数字(0-9之间的整数)。y的形状通常是[batch_size],表示每个样本的类别标签。
print(x.shape, y.shape, x.min(), x.max())
plot_images(x, y, 'image sample')

matplotlib.pyplot.show()

# step2  :  bulid a network
class Net(nn.Module):

    def __init__(self):
        super(Net,self).__init__()

        #wx+b
        self.fc1 = nn.Linear(28*28,256)  #,28*28是x的维度,256一般根据经验随机决定,大维变成小维
        self.fc2 = nn.Linear(256,64)     #第二层的输入与上一层的输出相同
        self.fc3 = nn.Linear(64,10)      #10分类,此处不是根据经验

    #计算过程
    def forward(self,x):
        # x: [b,1,28,28]
        # h1 =relu(xw1+b1)
        x = F.relu(self.fc1(x))
        # h2 = relu(h1w2+b2)
        x = F.relu(self.fc2(x))
        # h3 = h2w3+b3,最后一层看情况添加激活函数
        x = self.fc3(x)   # 激活函数加不加取决于你的任务

        return x

# step3  : 训练。 训练的逻辑是:每一次求导,然后再去更新
# net.parameters()返回[w1,b1,w2,b2,w3,b3],这就是我们要优化的; lr是学习步长 ;momentum帮助更好的优化
net = Net()
# 使用SGD优化器,学习率为0.01,动量为0.9
optimizer = optim.SGD(net.parameters(),lr=0.01,momentum=0.9)
# 把loss保存起来
train_loss = []

# epoch 是整个数据集的训练轮数。在这个例子中,数据集将被遍历3次。
# batch_idx 是当前批次的索引。
for epoch in range(3):
    for batch_idx, (x,y) in enumerate(train_loader):

        # x: [b,1,28,28]  y : [512]
        # 将图像数据从[b,1,28,28]打平成[b,feature],size(0)是batch,因为网络期望的输入是一个一维的特征向量。
        x = x.view(x.size(0),28*28)
        # [b,10]
        # one_hot是一个自定义函数,用于将类别标签转换为one-hot编码
        out = net(x)
        # [b,10],真实的y
        y_onehot= one_hot(y)
        # loss=mse(out,y_onehot),求其均方差
        loss = F.mse_loss(out,y_onehot)

        #清零梯度
        optimizer.zero_grad()
        #计算梯度
        loss.backward()
        # 更新梯度:w‘ = w-lr*grad
        optimizer.step()

        #进行梯度下降的可视化,把数据记录下来
        train_loss.append(loss.item())

        # 每隔10个批次,打印当前轮次、批次索引和损失值,以便于监控训练过程。
        if batch_idx % 10 == 0:
            print(epoch,batch_idx,loss.item())

# 将训练损失绘制成曲线图
plot_curve(train_loss)
#we can get optimal [w1,b1,w2,b2,w3,b3]


# step4  :  测试test
total_correct = 0
# 打印loss
for x, y in test_loader:
    x = x.view(x.size(0), 28 * 28)
    out = net(x)  # 得到网络的输出
    # out: [b, 10]  => pred: [b]
    pred = out.argmax(dim=1)
    correct = pred.eq(y).sum().float().item()  # item()取数值    当前batch正确的个数
    total_correct += correct
total_num = len(test_loader.dataset)  # 总的测试的数量
acc = total_correct / total_num  # 准确率
print('test acc:', acc)
x, y = next(iter(test_loader))
out = net(x.view(x.size(0), 28 * 28))
pred = out.argmax(dim=1)
plot_images(x, pred, 'test')
# 后期可进行的工作:
# def net()中增加网络层数
# def forward()中最后一层可以用softmax()
# loss:F.mse_loss()改成交叉熵函数

utils工具包:

# 四个步骤:load data; bulid model; train; test
import torch
from matplotlib import pyplot as plt


def plot_curve(data):  # 绘制loss下降的曲线图
    fig = plt.figure()
    plt.plot(range(len(data)), data, color='blue')
    plt.legend(['value'], loc='upper right')
    plt.xlabel('step')
    plt.ylabel('value')
    plt.show()


def plot_images(img, label, name):  # 画图片(因为这里涉及到一个图片的识别),这个地方可以方便地看到图片的识别结果
    fig = plt.figure()
    for i in range(6):
        plt.subplot(2, 3, i + 1)
        plt.tight_layout()
        plt.imshow(img[i][0] * 0.3081 + 0.1307, cmap='gray', interpolation='none')
        plt.title("{}:{}".format(name, label[i].item()))
        plt.xticks([])
        plt.yticks([])
    plt.show


def one_hot(label, depth=10):  # 需要通过scatter()完成one_hot编码
    out = torch.zeros(label.size(0), depth)
    idx = torch.LongTensor(label).view(-1, 1)
    out.scatter_(dim=1, index=idx, value=1)
    return out

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值