快速上手PyTorch——Quickstart in PyTorch

快速上手PyTorch——Quickstart in PyTorch

1. 简介

相信大家在学习Pytorch的过程中,对Pytorch官方网站教程的参考学习,是一个必不可少的环节,本文中作者将依据自己浅薄的理解,简要分析Pytorch/Tutorials/QuickStart,希望能为有需求的朋友们提供一些帮助,也欢迎大家批评指正。本文将依据Quickstart,使用FashionMNIST数据集,构建并训练一个简单的分类网络。

2. 代码分析

(1) 导入必要的库

在加载数据前,我们需要先导入需要用到的一些库

import torch
from torchvision import datasets
from torch.utils.data import DataLoader
from torch import nn
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt 	# visualize data (optional)

torch.utils.data.Datasetstorch.utils.data.DataLoader是Pytorch中处理数据的两个基本功能,前者包含了一些数据样本和对应的标签,后者则是把前者包裹成一个可迭代的对象,并将其传递给后续的 train 和 test 函数。

与此同时,Pytorch也提供一些具有针对性的库,如torchvisiontorchtexttorchaudio,它们包含了图片数据、文本信息、音频信息,本文中所采用的FashionMNIST数据集就是存储在torchvision下的datasets中。

torch.nn中提供了我们在构建神经网络中所需要的一系列层,因此在构建网络的时候,我们只需要简单地调用即可。

torchvision.transforms.ToTensor让我们能够将PIL图像转化为Tensor表征的图像。

(2) 加载数据

FashionMNIST包含有60,000个训练数据和10,000个测试数据,每一个数据都包含一个 28 p i x e l × 28 p i x e l 28 pixel \times 28 pixel 28pixel×28pixel 的灰度图像和一个代表样本类别的标签。我们需要将torchvision.datasets中的FashionMNIST导入进来,并使用DataLoader将其包裹成一个可迭代的对象。

# download training data from datasets
training_data = datasets.FashionMNIST(root='data',
                                      train=True,
                                      download=True,
                                      transform=ToTensor())

# download test data from datasets
test_data = datasets.FashionMNIST(root='data',
                                  train=False,
                                  download=True,
                                  transform=ToTensor())

root表示数据集的存储路径,若不存在指定的文件夹,将自行创建。

train表明该数据是数据集中的训练数据还是测试数据。

download表示是否下载,如果其值为True,那么将检测root下的文件夹是否含有数据集,如果没有,将下载数据集,如果有,不再重复下载;如果其值为False,不下载数据集。

transform指定了样本的转换格式。

target_transform指定了标签的转换格式(示例代码中并没有特殊指定)。

# define batch size
batch_size = 16
# create data loaders
train_dataloader = DataLoader(training_data, batch_size=batch_size, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=batch_size, shuffle=True)

batch_size表示在训练或者测试过程中,一次喂入的数据量,举个例子,对本例中的FashionMNIST来说,训练集中有60,000个样本数据,一次喂入16个样本,那么总共需要 60 , 000 ÷ 16 = 3750 60,000 \div 16 = 3750 60,000÷16=3750 个batch才能将训练数据全部喂进网络中。使用batch的一部分原因是,通过设置合理的batch_size值,我们可以提高内存的利用率,提高训练速度,并使梯度下降更为准确。

shuffle表示打乱数据集中的数据,通常针对一个数据集,我们会遍历多次在下文所述的优化器中进行迭代,每一次称为一个epoch,为了提高网络的泛用性,降低过拟合程度,我们需要将每一个epoch中的样本打乱。

(3) 构建网络

在导入数据后,我们就可以开始着手构建我们的网络了,本文中的神经网络结构较为简单,主要就是全连接层和激活函数ReLU的堆叠。

# chose CPU or GPU for training
if torch.cuda.is_available():
    device = 'cuda'
else:
    device = 'cpu'
print(f'You are currently using {device}.')

torch.cuda.is_available()判断cuda是否可用,进而选择使用GPU或CPU进行训练。

# define model
class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 512),
                                               nn.ReLU(),
                                               nn.Linear(512, 512),
                                               nn.ReLU(),
                                               nn.Linear(512, 10),
                                               nn.ReLU())

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)
print(model)

我们继承nn.Module并在__init__下定义自己的网络结构,如上代码所示,我们在__init__下定义了一个self.flatten和一个self.linear_relu_stack

self.flatten的作用为将 m × n m \times n m×n 的图像变为 1 × ( m ∗ n ) 1 \times (m*n) 1×(mn) 的图像,即将图像“摊平”,以送入全连接层。在本例中,即将 28 × 28 28 \times 28 28×28 的图像变为 1 × 784 1 \times 784 1×784 的图像。

self.linear_relu_stack中的结构非常简单:

784 × 512 − L i n e a r ⟹ R u L U ⟹ 512 × 512 − L i n e a r ⟹ R e L U ⟹ 512 × 10 − L i n e a r ⟹ R e L U 784 \times 512 - Linear \Longrightarrow RuLU \Longrightarrow 512 \times 512 - Linear \Longrightarrow ReLU \Longrightarrow 512 \times 10 - Linear \Longrightarrow ReLU 784×512LinearRuLU512×512LinearReLU512×10LinearReLU

因为最后FashionMNIST中有10个类,因此最后的输出也为10个数。

最后,在forward中,我们应用上述结构,构造一个前向通路。并根据先前选择的device部署模型,打印模型结构。

(4) 损失函数和优化器

Pytorch提供了丰富的损失函数和优化器,在这里我们使用nn.CrossEntropyLoss()作为损失函数,torch.optim.SGD()作为优化器。

# define loss function
loss_function = nn.CrossEntropyLoss()

# define optimizer
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) 

注意此处的lr表示SGD的学习率,可以根据实际情况进行修改。

(5) 训练与测试

冗长的准备工作已经完成,可以开始着手定义训练与测试了

# define train loop
def train(dataloader, model, loss_function, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):    # batch represents batch-th
        X, y = X.to(device), y.to(device)    # X represents img, while y represents label

        # Compute prediction error
        pred = model(X)
        loss = loss_function(pred, y)

        # BackPropagation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        # Oversee process
        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f'loss:{loss:>7f} [{current:>5d}/{size:>5d}]')

size获取数据集中所有样本的个数。

在for循环中,我们遍历dataloader中的所有数据,获取其索引及对应数据(注意dataloader中含有两项元素——样本及其对应的标签)。由于我们先前在train_dataloader中定义了batch_size,因此此处的batch代表的enumerate的索引值恰好表示了第几个batch。

接着我们调用loss_function计算loss,再利用优化器进行反向传播更新网络参数。

为了监督进程,对每100个batch,我们获取它当前的loss和当前进行到第几个数据,并打印出来。

# define test loop
def test(dataloader, model, loss_function):
    size = len(dataloader.dataset)    # numbers of the whole test images(10,000)
    num_batches = len(dataloader)    # 10,000 / batch size(16) = 625
    model.eval()
    sum_test_loss, sum_correct = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            sum_test_loss += loss_function(pred, y).item()
            sum_correct += (pred.argmax(1) == y).type(torch.float).sum().item()    
    test_loss = sum_test_loss / num_batches
    correct_ratio = 100 * (sum_correct / size)
    print(f'Test Error: \n Accuracy: {(correct_ratio):>0.1f}%, Avg loss: {test_loss:>8f} \n')

为了观察经过训练后的模型的表现,我们还需要测试集来评估模型的好坏。

size获取数据集中所有样本个数。

num_batches获取batch个数,由于FashionMNIST中的测试集有10,000个样本,且我们此前设置的batch_size为16,因此batch为 10 , 000 ÷ 16 = 625 10,000 \div 16 = 625 10,000÷16=625

model.eval()的作用像是一种开关,由于一些层(比如Dropouts Layers、BatchNorm Layers)在训练和测试中的行为是不一样的,因此在测试时,我们需要model.eval()将它们置为测试(非训练)状态,与之相对的则是model.train()——置为训练状态。由于作者水平有限,其在本例中的作用尚不明确,欢迎有知道的大佬补充。

What does model.eval() do in pytorch?

What does model.train() do in PyTorch?

注意到在测试中,梯度信息是不需要的,因此with torch.no_grad()可以帮助我们停止计算梯度以节省算力。

为了方便后续理解,在这里给出打印后pred的大致结构

tensor([[0.1421, 0.0000, 0.0000, 0.0000, 0.0413, 0.4401, 0.0000, 0.7510, 0.8822, 0.0000],
        [0.9166, 0.6223, 0.0000, 0.9044, 1.2210, 0.0000, 0.0000, 0.0000, 0.3413, 0.0000],
        [0.2356, 0.0000, 0.0000, 0.0826, 0.5078, 0.0776, 0.0000, 0.0544, 0.4084, 0.0000],
        [0.0000, 0.0000, 0.0000, 0.0000, 0.2197, 0.2384, 0.0000, 0.4583, 0.9327, 0.0000],
        [1.0815, 1.9916, 0.0000, 1.9508, 0.3624, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
        [0.0574, 0.0000, 0.0000, 0.0000, 0.1917, 0.2527, 0.0000, 0.2819, 0.7821, 0.0000],
        [1.8598, 2.3490, 0.0000, 2.5405, 0.7339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
        [0.7836, 1.3618, 0.0000, 0.9962, 0.2862, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
        [0.5627, 0.2218, 0.0000, 0.2929, 0.1805, 0.0201, 0.0000, 0.0000, 0.0482, 0.0000],
        [0.8809, 2.1661, 0.0000, 1.5107, 0.2718, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
        [0.0000, 0.0000, 0.0000, 0.0000, 0.3268, 0.5301, 0.0000, 1.5194, 1.8777, 0.0000],
        [0.1255, 0.0000, 0.0000, 0.0000, 1.2370, 0.0451, 0.0000, 0.0322, 0.8576, 0.0000],
        [0.7843, 0.2338, 0.0000, 0.6287, 0.6042, 0.0785, 0.0000, 0.0000, 0.2600, 0.0000],
        [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.2876, 0.0000, 0.6852, 0.5046, 0.0000],
        [0.4747, 0.0000, 0.0000, 0.0000, 0.6698, 0.1957, 0.0000, 0.2946, 0.9717, 0.0000],
        [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.3904, 0.0000, 1.1054, 0.5773, 0.0000]])

上图展示的是一个batch中的数据,总共有625个batch。

sum_test_loss表示所有的batch的loss的加和,一个batch的loss为该batch下16个loss的均值。

(pred.argmax(1) == y).type(torch.float)表示返回pred的每一行最大值的索引,再与标签进行比较,相等返回1,反之返回0

sum_correct则是对上述所有的1的加和,表征了在test中模型预测正确的样本个数。

test_loss表示平均损失,correct_ratio表示精确度。

(6) 开始训练

# start train
epochs = 5
for t in range(epochs):
    print(f'Epoch {t + 1}\n----------------------------------')
    train(train_dataloader, model, loss_function, optimizer)
    test(test_dataloader, model, loss_function)

为了节省时间,这里我们只训练5个epoch,实际情况下根据个人需求自行设置。根据我的个人测试,在训练了50个epoch左右时,精度能达到85%。

(7) 模型的存储、加载和预测

# save model parameters
torch.save(model.state_dict(), 'model.pth')
print('Save Pytorch Model State to model.pth')

模型训练结束后,我们可以通过上述代码将其保存为.pth文件,注意这里仅仅保存了网络参数,不包括网络结构,若想既包含网络参数又包含网络结构可以去官方文档查找(我懒得找了)。

# load model
model = NeuralNetwork()
model.load_state_dict(torch.load('model.pth'))

加载保存在model.pth中的网络参数。

# make predictions
classes = ['T-shirts/top', 'Trousers', 'Pullover', 'Dress', 'Coat',
           'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
model.eval()
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
    pred = model(x)
    predicted = classes[pred.argmax(1)]
    actual = classes[y]
    print(f'Predicted: "{predicted}", Actual: "{actual}"')

上述代码展示了FashionMNIST中包含的10类物体名称,并根据我们构建的网络进行预测,代码比较简单,这里不再赘述,这里我们简单挑选了FashionMNIST测试集中的第一组数据作为预测对象。

3. 主要参考资料

PyTorch/Tutorials

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值