Pytorch入门:手写数字识别代码逐行解析

先上完整代码:

import torch
from torch import nn
from torch.utils.data import Dataset
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import torch.optim as optim

#定义神经网络类Network, 它将继承nn.Module类
class Network(nn.Module):
    # 神经网络中的神经元数量是固定的,所以init不用传入参数
    def __init__(self):
        super().__init__() # 调用了父类的初始化函数
        # layer1是输入层于隐藏层之间的线性层
        self.layer1 = nn.Linear(28 * 28, 256)
        # layer2是隐藏层和输出层之间的线性层
        self.layer2 = nn.Linear(256, 10)
    def forward(self, x):
        #将输入由二维转换为一维,便于输入到线性层中
        x = x.view(-1, 28*28)
        x = self.layer1(x) #输出layer1的输出结果
        x = torch.relu(x) #输出激活后的输出结果
        #计算layer2的结果,并返回
        return self.layer2(x)

device = torch.device("cuda:0")
# 加载MNIST数据集
train_dataset = datasets.MNIST(root='./data', train=True, transform=transforms.ToTensor(), download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transforms.ToTensor())

# 定义数据加载器
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=128, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=128, shuffle=False)


# 定义模型、损失函数和优化器

model = Network().to(device)

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters())


# 训练模型
for epoch in range(20):
    for i, (images, labels) in enumerate(train_loader):
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        
        if (i+1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(epoch+1, 20, i+1, len(train_loader), loss.item()))
            
            
            
# 测试模型
model.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print('Test Accuracy: {:.2f}%'.format(100 * correct / total))

下面开始逐行解析

1.

from torch import nn

导入pytorch中的nn,该类包含了构建模型所需要的组件

import torch.optim as optim

optim里有各种优化器

2.

#定义神经网络类Network, 它将继承nn.Module类
class Network(nn.Module):
    # 神经网络中的神经元数量是固定的,所以init不用传入参数
    def __init__(self):
        super().__init__() # 调用了父类的初始化函数
        # layer1是输入层于隐藏层之间的线性层
        self.layer1 = nn.Linear(28 * 28, 256)
        # layer2是隐藏层和输出层之间的线性层
        self.layer2 = nn.Linear(256, 10)
    def forward(self, x):
        #将输入由二维转换为一维,便于输入到线性层中
        x = x.view(-1, 28*28)
        x = self.layer1(x) #输出layer1的输出结果
        x = torch.relu(x) #输出激活后的输出结果
        #计算layer2的结果,并返回
        return self.layer2(x)

创建模型类

先在构造方法__init__()中创建搭建模型所需要的各种层。然后在forward()方法中搭建模型并实现前向传播的过程(即将数据输入到一层,再将该层的输出作为输入,输入到下一层),全连接层的输出先经过Relu激活函数再传入下一层。顺便说一句,BP神经网络的隐藏层最多两层,再增加层数并不会提高预测精度。

输入层为28 * 28,因为BP神经网络只支持一维数据,所以要将28 * 28大小的图片转换为28 * 28=784大小的一维向量。

隐藏层维数通常为2的幂次方,这里设为256。

输出层的维数为10,因为这是一个10分类问题,需要最后输出10个概率来判断输入的图片属于哪一类。

3.

device = torch.device("cuda:0")

创建指定设备类型的对象,这里指定设备为GPU。

4.

train_dataset = datasets.MNIST(root='./data', train=True, transform=transforms.ToTensor(), download=True)

加载MNIST数据集,root=" "指定数据集所在的根目录;train=True指定下载的是训练集;transform=transforms.ToTensor(),将图片转换为tensor类型;download=True,如果没有下载数据集就下载数据集。tensor是张量,形式类似于array。为什么要将array转换为tensor?因为要在GPU上加速,数据形式就必须转换为tensor。

test_dataset = datasets.MNIST(root='./data', train=False, transform=transforms.ToTensor())

train=False,表明下载的是测试集。这一句里没有指定download=True,因为pytorch内置了测试集,为了节省资源,不再下载测试集。

5.

train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=128, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=128, shuffle=False)

定义数据加载器,batch_size指定每一个batch的大小,shuffle=True将数据打乱,随机化。

此时,数据被分成了一个batch一个batch的形式,训练的时候也是以batch为单位送入模型。

6.

model = Network().to(device)

将模型转换为GPU形式

7.

criterion = nn.CrossEntropyLoss()

损失函数为交叉熵损失,通过使用该损失函数,可以顺便在输出层后面自动加softmax,使得最终的输出为每一类的概率。因此在构建模型的时候并没有显示地加入softmax层。

8.

optimizer = optim.Adam(model.parameters())

优化器使用Adam,可以自动调整学习率。什么是优化器?其实就是更新参数的方法,常用的优化器还有随机梯度下降法SGD。

9.

for epoch in range(20):
    for i, (images, labels) in enumerate(train_loader):
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        
        if (i+1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(epoch+1, 20, i+1, len(train_loader), loss.item()))

这是模型训练的代码。最外层的循环中range(20)表示总共训练20轮。

内层的循环中:

1)

for i, (images, labels) in enumerate(train_loader):

enumerate()是用来读取迭代器的,train_loader是DataLodaer的返回值,是一个以(输入数据,标签)为内容的迭代器。

i 是序号。(images,labels)是包含了图片和标签的元组。注意,这里的(images,labels)是一个batch,而不是单独的图片和标签。

2)

images, labels = images.to(device), labels.to(device)

将数据转换为GPU类型的,这样才能在GPU上运行。

3)

optimizer.zero_grad()

每一轮先将梯度清零,防止使用前几轮累计的梯度更新参数。

4)

outputs = model(images)

将图片输入到模型中,得到输出。

5)

loss = criterion(outputs, labels)

利用输出和label计算loss

6)

loss.backward()

反向传播。

7)

optimizer.step()

使用优化器进行参数更新。

8)

if (i+1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(epoch+1, 20, i+1, len(train_loader), loss.item()))

每经过100个batch,打印已完成的epoch/总的epoch已完成的batch数/总的batch数,经过的这100个batch中,最后一个batch的loss

10.

model.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print('Test Accuracy: {:.2f}%'.format(100 * correct / total))

这是预测的代码。

1)

使用model.eval()的原因是:在模型中,我们通常会加上Dropout层和batch normalization层,在模型预测阶段,我们需要将这些层设置到预测模式,model.eval()就是帮我们一键搞定的,如果在预测的时候忘记使用model.eval(),会导致不一致的预测结果。

2)

with语句相当于try-finally。torch.no_grad()用来设置在预测时不对梯度进行更新。

3)

correct = 0
total = 0

correct是预测正确的数量,total是总的数量。

4)

_, predicted = torch.max(outputs.data, 1)

torch.max()返回最大值和最大值的序号,这里不需要最大值,所以写_。使用torch.max()是要得出输出概率中的最大值对应的类别,1表示输出每行的最大值。

5)

total += labels.size(0)

计算总的图片数量。

6)

correct += (predicted == labels).sum().item()

计算预测正确的数量

7)

print('Test Accuracy: {:.2f}%'.format(100 * correct / total))

打印正确率

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MNIST是深度学习领域的一个经典数据集,包含了手写数字0-9的灰度图像,每张图像的尺寸为28×28像素。基于这个数据集,我们可以训练一个模型来实现手写数字识别。 下面是一个基于PyTorch实现的MNIST手写数字识别代码的分析: ```python import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms ``` 首先,我们需要导入PyTorch及其相关的库,其中包括了神经网络相关的模块、优化器模块、数据处理模块等。 ```python transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True, num_workers=2) testset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform) testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False, num_workers=2) ``` 接着,我们对MNIST数据集进行预处理,将其转化为一个可用于训练的数据集和测试集,并进行标准化处理。 ```python class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 10, kernel_size=5) self.conv2 = nn.Conv2d(10, 20, kernel_size=5) self.pool = nn.MaxPool2d(2) self.fc1 = nn.Linear(320, 50) self.fc2 = nn.Linear(50, 10) def forward(self, x): x = self.pool(torch.relu(self.conv1(x))) x = self.pool(torch.relu(self.conv2(x))) x = x.view(-1, 320) x = torch.relu(self.fc1(x)) x = self.fc2(x) return x ``` 然后,我们定义了一个卷积神经网络模型。这个模型包括了两个卷积层和两个全连接层,其中卷积层用于提取图像特征,而全连接层则用于分类任务。 ```python net = Net() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) ``` 接着,我们定义了损失函数和优化器,其中损失函数采用交叉熵损失,优化器采用随机梯度下降(SGD)方法。 ```python for epoch in range(10): running_loss = 0.0 for i, data in enumerate(trainloader, 0): inputs, labels = data optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() if i % 100 == 99: print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 100)) running_loss = 0.0 print('Finished Training') ``` 最后,我们使用训练集对模型进行训练,并在每个epoch结束时输出损失值。训练完成后,我们可以使用测试集来评估模型的性能。 这就是一个基于PyTorch实现的MNIST手写数字识别代码的分析。通过这个例子,我们可以了解到如何使用PyTorch来构建、训练和评估一个深度学习模型。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值