参考资料:
课程内容链接:https://github.com/datawhalechina/thorough-pytorch
教程视频链接:https://www.bilibili.com/video/BV1L44y1472Z
导入包的方式
import os
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import torch.optim as optimizer
超参数包括:
-
batch size
-
初始学习率(初始)
-
训练次数(max_epochs)
-
GPU配置
GPU设置的两种常见方式:
# 方案一:使用os.environ,这种情况如果使用GPU不需要设置
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
加载使用:
model.cuda()
# 方案二:使用“device”,后续对要使用GPU的变量用.to(device)即可
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
加载和使用:
data = data.to(device)
model = Model(…).to(device)
数据读入通过Dataset+DataLoader
Dataset类来实现灵活的数据读取,定义的类需要继承PyTorch自身的Dataset类。主要包含三个函数:
-
__init__
: 用于向类中传入外部参数,同时定义样本集 -
__getitem__
: 用于逐个读取样本集合中的元素,可以进行一定的变换,并将返回训练/验证所需的数据 -
__len__
: 用于返回数据集的样本数
构建好Dataset后,就可以使用DataLoader来按批次读入数据了
from torch.utils.data import DataLoader
train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=4, shuffle=True, drop_last=True)
val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size, num_workers=4, shuffle=False)
-
batch_size:样本是按“批”读入的,batch_size就是每次读入的样本数
-
num_workers:有多少个进程用于读取数据
-
shuffle:是否将读入的数据打乱
-
drop_last:对于样本最后一部分没有达到批次数的样本,使其不再参与训练
神经网络模型构建:
PyTorch中神经网络构造一般是基于 Module 类的模型来完成的,它让模型构造更加灵活。
Module 类是 nn 模块里提供的一个模型构造类,是所有神经⽹网络模块的基类,我们可以继承它来定义我们想要的模型。下面继承 Module 类构造多层感知机。这里定义的 MLP 类重载了 Module 类的 init 函数和 forward 函数。它们分别用于创建模型参数和定义前向计算。前向计算也即正向传播。
模型初始化:
mlp = MLP() ##模型
print(list(mlp.parameters()))
print("-------初始化-------")
initialize_weights(mlp)
print(list(mlp.parameters()))
损失函数以交叉熵损失函数为例:
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
主要参数:
weight
:每个类别的loss设置权值。
size_average
:数据为bool,为True时,返回的loss为平均值;为False时,返回的各样本的loss之和。
ignore_index
:忽略某个类的损失函数。
reduce
:数据类型为bool,为True时,loss的返回是标量。
训练和评估:
for epoch in range(1, epochs+1):
train(epoch)
val(epoch)
训练:
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))
评估:
def val(epoch):
model.eval()
val_loss = 0
gt_labels = []
pred_labels = []
with torch.no_grad():
for data, label in test_loader:
data, label = data.cuda(), label.cuda()
output = model(data)
preds = torch.argmax(output, 1)
gt_labels.append(label.cpu().data.numpy())
pred_labels.append(preds.cpu().data.numpy())
loss = criterion(output, label)
val_loss += loss.item()*data.size(0)
val_loss = val_loss/len(test_loader.dataset)
gt_labels, pred_labels = np.concatenate(gt_labels), np.concatenate(pred_labels)
acc = np.sum(gt_labels==pred_labels)/len(pred_labels)
print('Epoch: {} \tValidation Loss: {:.6f}, Accuracy: {:6f}'.format(epoch, val_loss, acc))
优化器:
-
optimizer在一个神经网络的epoch中需要实现下面两个步骤:
-
梯度置零
-
梯度更新
-
optimizer = torch.optim.SGD(net.parameters(), lr=1e-5)
for epoch in range(EPOCH):
...
optimizer.zero_grad() #梯度置零
loss = ... #计算loss
loss.backward() #BP反向传播
optimizer.step() #梯度更新
文章出处登录后可见!