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))

打印正确率

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值