基本配置
以下的超参数可以统一配置,方便后续修改:
- batch_size
- 初始学习率
- 训练次数(max_epochs)
- GPU配置
batch_size=16
lr=1e4
max_epochs=100
device=torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
数据读入
pytorch是通过Dataset和DataLoader的形式完成数据读入的。其中两个类分别有如下作用:
- Dataset:定义好数据的格式和数据变换形式。
- DataLoader:用iterative的方式不断地读入批次数据。
Dataset
pytorch预定义了很多数据集,定义在了torchvision.datasets包中,常用的MNIST、CIFAR、ImageNet等数据都有,但是在实际项目中,
我们大多数情况都是使用的自定义的数据集,这时候急需要继承torch.utils.data.Dataset,下面我们将演示一下,如何实现自定义数据集。
通用的Dataset模板
1 2 3 4 5 6 7 8 9 10 11
| class MyDataset(Dataset): def __init__(self, ...): ...
def __len__(self): ... return len
def __getitem__(self, index): ... return image, label
|
通用模板包含3个方法:
__init__:数据集的初始化,可传入一些必要的参数
__len__:返回数据集个数
__getitem__:传入index,返回第index个数据和标签值
对于以上3个方法的实现比较灵活,一般包含以下两种方式:
- 在
__init__初始化时,将所有数据读入,__len__返回数据集长度,__getitem__直接通过索引返回数据即可。
- 在
__init__只给出本地文件地址,在__getitem__中现场读取相应数据和标签。
自定义Dataset示例
一个简单的实例,该数据集包含从1-1000的整数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from torch.utils.data import Dataset
class NumbersDataset(Dataset): def __init__(self): self.samples = list(range(1, 1001))
def __len__(self): return len(self.samples)
def __getitem__(self, idx): return self.samples[idx]
if __name__ == '__main__': dataset = NumbersDataset() print(len(dataset)) print(dataset[100]) print(dataset[122:361])
|
高级Dataset示例
该示例来自pytorch官方网站。
该数据集返回Fashion-MNIST
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import os import pandas as pd from torchvision.io import read_image
class CustomImageDataset(Dataset): def __init__(self, annotations_file, img_dir, transform=None, target_transform=None): self.img_labels = pd.read_csv(annotations_file) self.img_dir = img_dir self.transform = transform self.target_transform = target_transform
def __len__(self): return len(self.img_labels)
def __getitem__(self, idx): img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0]) image = read_image(img_path) label = self.img_labels.iloc[idx, 1] if self.transform: image = self.transform(image) if self.target_transform: label = self.target_transform(label) return image, label
|
应当注意:自定义的数据集应当只包含Train数据或者Test数据,然后通过传入Train数据和Test数据构成不同的数据集。
DataLoader
1 2 3 4 5 6 7 8 9
| from torch.utils.data import DataLoader
train_data = CustomImageDataset(train_path, transform=data_transform) val_data = CustomImageDataset(val_path, transform=data_transform)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=16, num_workers=4, shuffle=True, drop_last=True) val_loader = torch.utils.data.DataLoader(val_data, batch_size=16, num_workers=4, shuffle=False)
|
其中:
- batch_size:样本是按“批”读入的,batch_size就是每次读入的样本数,以上示例每次读入16个样本。
- num_workers:有多少个进程用于读取数据,Windows下该参数设置为0,Linux下常见的为4或者8,根据自己的电脑配置来设置
- shuffle:是否将读入的数据打乱,一般在训练集中设置为True,验证集中设置为False
- drop_last:对于样本最后一部分没有达到批次数的样本,使其不再参与训练
PyTorch中的DataLoader的读取可以使用next和iter来完成,使用如下代码查看dataloader读入的数据:
1
| images, labels = next(iter(val_loader))
|
模型构建
神经网络构建
通用的神经网络模板
Module 类是 torch.nn
模块里提供的一个模型构造类,是所有神经网络模块的基类,我们可以继承它来定义我们想要的模型。
1 2 3 4 5 6 7 8 9 10
| import torch from torch import nn
class CustomModule(nn.Module): def __init__(self, **kwargs): ··· def forward(self, x): ···
|
定义模型时,无需定义反向传播函数,系统将通过计算图,自动生成反向传播所需的backward函数。
简单的MLP网络
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import torch from torch import nn
class MLP(nn.Module): def __init__(self, **kwargs): super(MLP, self).__init__(**kwargs) self.hidden = nn.Linear(784, 256) self.act = nn.ReLU() self.output = nn.Linear(256,10) def forward(self, x): o = self.act(self.hidden(x)) return self.output(o)
|
使用网络:
1 2 3
| X = torch.rand(batch_size,784) net = MLP() net(X)
|
神经网络中常见的层
损失函数
训练与评估
训练
一个完成的训练过程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def train(epoch): model.train() train_loss = 0 for data, label in train_loader: data, label = data.cuda(), label.cuda() optimizer.zero_grad() output = model(data) loss = criterion(output, label) loss.backward() optimizer.step() train_loss += loss.item()*data.size(0)
train_loss = train_loss/len(train_loader.dataset) print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss))
|
验证
对应的,一个完整的验证过程为:
1 2 3 4 5 6 7 8 9 10 11 12 13
| def val(epoch): model.eval() val_loss = 0 with torch.no_grad(): for data, label in val_loader: data, label = data.cuda(), label.cuda() output = model(data) preds = torch.argmax(output, 1) loss = criterion(output, label) val_loss += loss.item()*data.size(0) running_accu += torch.sum(preds == label.data) val_loss = val_loss/len(val_loader.dataset) print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, val_loss))
|