PyTorch下神经网络构建的基本结构

本文主要参考李宏毅2021春机器学习课程中的 PyTorch 教学内容,详细内容可查看李宏毅2021春机器学习课程官网和李宏毅2021春机器学习课程视频

使用 PyTorch 构建神经网络模型主要分为几个部分,即导入数据、定义网络结构及损失函数等、训练模型、验证模型和测试模型[1]。下面通过代码的范例进行介绍

导入数据

通过 PyTorch 导入数据需要引入两个模块,即 DatasetDataLoader ,他们在 torch.utils.data 包下。一般我们会对导入的数据进行一定的处理,因此我们最好创建一个 Dataset 的子类以便自己的操作,实现这样一个子类我们需要完成如下的内容。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from torch.utils.data import Dataset, DataLoader

class MyDataset(Dataset):
    def __init__(self, file):
        self.data = ...

    def __getitem__(self, index):
        return self.data[index]

    def __len__(self):
        return len(self.data)

这样一个类仅为参考,包含几个函数的作用分别为:

  • __init__(self, file) :读取对应文件,并对文件中的数据进行一定的预处理。
  • __getitem__(self, index) :根据给定的 index 返回对应的数据。
  • __len__(self) :返回数据集的大小。

创建了这样一个数据集的类,我们需要将其导入到 DataLoader 中,相应的操作如下:

1
2
3
dataset = MyDataset(file)

dataloader = DataLoader(dataset, batch_size, shuffle=True)

DataLoader 中需要传入对应的 Dataset 对象,指定一个 batch_size ,由于在 PyTorch 中,训练模型时,并非将一整个数据集全部进行运算后在对模型进行优化,而是在数据集中分别多次取一个 batch_size 的数据进行预测,并进行后续的优化的,同时这样设计也更利于进行并行运算,以便使用 GPU 进行加速运算。shuffle 参数表明是否需要将数据打乱,一般而言在训练过程中,我们会要求打乱数据,而在测试过程中不进行打乱的操作,以防出现误差。

定义网络结构

在此处的定义网络结构是包含神经网络、损失函数和优化器的所有配置。范例代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import torch.nn as nn

class MyModel(nn.Module):
     def __init__(self):
         super(MyModel, self).__init__()
         # Define Neural Network
         self.net = nn.Sequential(            
         nn.Linear(10, 32),
         nn.Sigmoid(),
         nn.Linear(32, 1)
     )
     # Forward Pass
     def forward(self, x):                    
         return self.net(x)

# Contruct Model and Move to device (cpu/cuda)
model = MyModel().to(device)    

# Set Loss Function
criterion = nn.MSELoss()

# Set Optimizer
optimizer = torch.optim.SGD(model.parameters(), 0.1)    

首先,我们通过继承 nn.Module 类,定义一个网络结构,通过PyTorch文档[2]我们可知,主要需要构造的有两个函数,即 __init__forward 函数。我们需要在 __init__ 函数中通过在 nn.Sequential 中按需对网络层级结构进行添加;在 forward 函数中定义神经网络的前向传播,即通过模型进行预测的过程。定义完相关模型后,在后续我们需要调用这个模型,并转化为对应设备的模型(CPU或者CUDA)。损失函数方面此处我们使用了 nn.MSELoss() 均方损失函数,也可按需改成对应的损失函数。优化器则选定最为经典的随机梯度下降,在优化器中我们需要传入所有模型的参数以及学习率。

训练模型

训练模型的代码可如下:

1
2
3
4
5
6
7
8
9
for epoch in range(n_epoch):                # Iterate n_epochs
    model.train()                            # Set model to train model
    for x, y in tr_set:                        # Iterate through the dataloader
        optimizer.zero_grad()                # Set gradient to zero
        x, y = x.to(device), y.to(device)    # Move data to device (cpu/cuda)
        pred = model(x)                        # Forward pass (compute output)
        loss = criterion(pred, y)            # Compute loss
        loss.backward()                        # Compute gradient (backpropagation)
        optimizer.step()                    # Update model with optimizer

根据所需,设定需要跑几次 n_epoch ,将模型调至训练模型,并遍历训练集的数据,一般而言,我们是会循环训练数据集中 batch_size 个数据,将优化器的梯度重置为 0 ,以防上一次计算的梯度影响到本次计算;通过模型得到预测值,并与真实值相比,通过损失函数计算出损失,再通过调用 backward() 函数进行梯度的运算,最终通过优化器的 step() 函数将模型进行更新。

验证模型

验证模型的代码可如下:

1
2
3
4
5
6
7
8
9
model.eval()                                    # Set model to evaluation mode
total_loss = 0                            
for x, y in dv_set:                                # Iterate through the dataloader
    x, y = x.to(device), y.to(device)            # Move data to device (cpu/cuda)
    with torch.no_grad():                        # Disable gradient calculation
        pred = model(x)                            # Forward pass (compute output)        
        loss = criterion(pred, y)                # Compute loss
    total_loss += loss.cpu().item() * len(x)    # Accumulate loss
    avg_loss = total_loss / len(dv_set.dataset) # Compute averaged loss

首先需要把模型设定为评估状态,验证模型时,暂不需要对模型进行梯度操作,仅是评估当前状态下模型的优劣,因而模型只是进行预测并计算损失,最终计算出平均的损失以评价模型。

测试模型

测试模型方面则是在验证模型的代码下更为精简,无需统计平均损失(当然有需要的话也可以统计),范例代码如下:

1
2
3
4
5
6
7
model.eval()                                    # Set model to evaluation mode
total_loss = 0                            
for x, y in dv_set:                                # Iterate through the dataloader
    x, y = x.to(device), y.to(device)            # Move data to device (cpu/cuda)
    with torch.no_grad():                        # Disable gradient calculation
        pred = model(x)                            # Forward pass (compute output)        
        loss = criterion(pred, y)                # Collect prediction

存储/载入模型

  • 存储模型
1
torch.save(model.state_dict(), path)        # Path need to be specified
  • 载入模型
1
2
ckpt = torch.load(path)                        # Path need to be specified
model.load_state_dict(ckpt)