pytorch简明教程
官网文档,没有梯子的小伙伴浏览官网可能比较慢。
基本实验流程
深度学习的实验流程如上图,顺序按照红色标号,其中第五步Training(训练)时会周期性的进行第六步Validation(验证),直到满足训练结束条件,到第七步Testing(测试)来测试训练出来的模型的最终效果。
而pytorch等深度学习框架主要可以帮我们做的是第1-4步,数据集管理与加载、定义神经网络(定义、保存、加载等)、定义损失函数、选择优化器(计算梯度、更新参数等)。在第五步训练中,我们可以通过定义好的网络模型、损失函数、优化器与准备好的数据,简单的使用框架计算网络结果、损失函数值、根据损失函数反向传播更新网络参数,实现模型训练。
主要组件
Tensor 张量
Tensor:张量,也就是多维向量,可以存整形和浮点型
-
shape:
一个n维向量,(, , ... , ),n表示张量的维度,表示第i维的数量,高维(逻辑上的最高维度)在前。如一个shape为 (4, 5, 3) 的张量,从高维到低维进行说明,高(三维)有4层,每层(二维)有5条标准单位宽度的线,每条线(一维)长度为3。
但注意,这里逻辑上最高维的 dim = 0,是按照数组的下标表示的。比如下面的例子中,dim=0的维度的长度为4,dim=2的维度的长度为3。
-
pytorch创建tensor方法
torch.randn:创建指定shape的随机值tensor
-
tensor常见运算
-
squeeze / unsqueeze
squeeze可消除某长度为1的维度,是一种等价变换。如 (1, 2, 3),可消除其 dim=0的维度,变为 (2, 3)。
unsqueeze与squeeze相反,是在某处添加一个长度为1的维度,也是等价变换。
-
transpose
张量转置,类似矩阵的转置,但数据的相对位置不变。如 (1, 2, 3) -> (3, 2, 1)。
-
cat
按顺序拼接shape只有一个维度不同的多个tensor为一个tensor
-
调用GPU资源
-
tensor或module默认使用CPU进行计算,我们可以修改使用的硬件资源,如:x.to('cpu') 或者 x.to('cuda')
-
检查cuda
torch.cuda.is_available() -
多个GPU资源
指定 cuda:0、cuda:1等
计算梯度
处理数据集
我们需要继承pytorch的数据集处理类来实现自定义的数据集加载,基本框架如下:
from torch.utils.data import Dataset, DataLoader
class MyDataset(Dataset):
def __init__(self, file):
# 可做一些数据预加载、预处理的事情
self.data = file.read()
def __getitem__(self, index):
# 根据index读取对应的一条数据
return self.data[index]
def __len__(self):
# 返回数据总数
return len(self.data)
自定义了数据集,我们就可以通过下面的方法使用数据集:
dataset = MyDataset(file)
# shuffle设置是否打乱数据集,一般trainning为True,Testing为False
dataloader = DataLoader(dataset, batch_size, shuffle=True)
数据集的加载过程如下图:
最后,dataLoader会根据我们设置的batch_size将多条数据组成一组,进行统一运算,所以每条数据的真实结果(lable,标签)也会被组成一个向量进行统一处理。
定义网络
一个神经网络由多个不同种类的网络层组成,最基本的网络层为全连接层,也就是上面我们讲到的用激活函数多层拟合的网络,定义方法如下:
import torch.nn as nn
# Linear Layer,全连接层
nn.Linear(in_features, out_features)
全连接层示意图:
上面是我们之前学到的网络状的拟合函数(神经网络)的描述,把它用神经网络单层(包含输入维度和输出维度)描述如下:
接下来,我们定义一个完整的网络:
import torch.nn as nn
class MyModel(nn.Module):
def __init(self):
super(MyModel, self).__init__()
# 网络结构:全连接层(10, 32) + sigmoid + 全连接层(32, 1),输入维度为10,输出维度为1
self.net = nn.Sequential(
nn.Linear(10, 32),
nn.Sigmoid(),
nn.Linear(32, 1)
)
def forward(self, x):
return self.net(x)
激活函数
import torch.nn as nn
nn.Sigmoid()
nn.ReLU()
定义损失函数
import torch.nn as nn
// 平均方差
nn.MSELoss()
// 交叉熵,多用于分类问题
nn.CrossEntropyLoss()
优化器
torch.optim.SGD(params, lr, momentum = 0)
# params = model.parameters()
# lr,学习率
训练/验证/测试步骤
# 创建数据集对象
dataset = MyDataset(file)
# 创建数据加载对象
tr_set = DataLoader(dataset, 16, shuffle=True)
# 定义模型并声明运算使用的硬件位置
model = MyModel().to(device)
# 定义损失函数
criterion = nn.MSELoss()
# 定义优化器
optimizer = torch.optim.SGD(model.parameters(), 0.1)
# 训练模型 train
def train():
# 训练 n_epochs 轮
for epoch in range(n_epochs):
# 进入训练模式,这样才能更新参数
model.train()
# 每次取出 batch_size 组的数据,每组数据n维,一整个batch会在n+1维被合并为一个tensor统一训练
for x, y in tr_set:
# 初始化,将优化器里的gradient设成0
optimizer.zero_grad()
# 将数据移到目标运算设备上
x, y = x.to(device), y.to(device)
# 前向传播,根据feature x计算网络预测的结果值pred
pred = model(x)
# 计算预测结果的损失函数值
loss = criterion(pred, y)
# 反向传播,计算损失函数的梯度
loss.backward()
# 使用计算的梯度更新model的参数
optimizer.step()
# 验证模型 validation
def validation():
# 将模型设置为验证模式
model.eval()
total_loss = 0
# 从数据集中取出一个 batch 的数据
for x, y in dv_set:
# 将数据移到目标运算设备上
x, y = x.to(device), y.to(device)
# 设置一个scope,该scope内不计算梯度,会提升计算速度
with torch.no_grad():
# 前向传播,根据feature x计算网络预测的结果值pred
pred = model(x)
# 计算预测结果的损失函数值
loss = criterion(pred, y)
# 累计总loss
total_loss += loss.cpu().item() * len(x)
# 计算loss平均值,根据该值确定模型是否更优,是否要保存该模型
avg_loss = total_loss / len(dv_set.dataset)
# 最终测试模型 testing
def testing():
# 将模型设置为验证模式
model.eval()
preds = []
# 从数据集中取出一个 batch 的数据
for x in tt_set:
# 将数据移到目标运算设备上
x = x.to(device)
# 设置一个scope,该scope内不计算梯度,会提升计算速度
with torch.no_grad():
# 前向传播,根据feature x计算网络预测的结果值pred
pred = model(x)
# 保存预测结果
pred.append(pred.cpu())
# 存储模型
def save_model(path):
torch.save(model.state_dict(), path)
# 加载模型
def load_model(path):
ckpt = torch.load(path)
model.load_state_dict(ckpt)
常见问题
-
内存溢出 / 显卡内存移除
如 cuda 内存溢出:
CUDA out of memory. Tried to allocate 350.00 MiB (GPU O: 14.76 GiB total capacity; 11.94 GiB already allocated: 123.75 MiB free: 13.71 GiB reserved in total by PyTorch)在加载图片的时候,由于batch_size过大,可能会超出内存或者显卡内存的大小,导致数据无法一次性加载到内存而导致内存溢出,可以适当修改batch_size或者减小图片分辨率等方法来解决。
-
tensor的类型或者shape不匹配
先检查这样的转换是否符合预期,若符合预期,shape不匹配可以变换tensor形状,类型不匹配可以调整某个数据的类型或者显式转换。
其他功能
- 处理语音(speech/audio processing):torchaudio
- 处理NLP(natural language processing):torchtext
- 处理图像(computer vision):torchvision
- pytorch的补充 scikit-learn:skorch