实操教程 | Pytorch Debug指南:15条重要建议
作者 | Coggle
来源 | Coggle数据科学
编辑 | 极市平台
极市导读
在使用Pytorch
时你或多或少会遇到各种bug,为了缓解你的痛苦😢,本文将对常见的错误进行解释,并说清楚来龙去脉 >>加入极市CV技术交流群,走在计算机视觉的最前沿
在使用Pytorch
时你或多或少会遇到各种bug,为了缓解你的痛苦😢,本文将对常见的错误进行解释,并说清楚来龙去脉。
细节就是魔鬼,虽然代码不报错但还是可能会对精度带来影响。如果本文对你有帮助,请收藏&转发!
CrossEntropyLoss和NLLLoss
最常见的错误是损失函数和输出激活函数之间的不匹配。nn.CrossEntropyLossPyTorch
中的损失模块执行两个操作:nn.LogSoftmax
和nn.NLLLoss
。
因此nn.CrossEntropyLossPyTorch
的输入应该是最后一个线性层的输出。不要在nn.CrossEntropyLossPyTorch
之前应用Softmax。 否则将对Softmax输出计算log-softmax,将会降低模型精度。
如果使用nn.NLLLoss
模块,则需要自己应用log-softmax。nn.NLLLoss
需要对数概率,而不是普通概率。因此确保应用nn.LogSoftmaxor nn.functional.log_softmax
,而不是nn.Softmax
。
Softmax的计算维度
注意Softmax的计算维度。通常是输出张量的最后一个维度,例如nn.Softmax(dim=-1)
。如果混淆了维度,模型最终会得到随机预测。
类别数据与嵌入操作
对于类别数据,常见的做法是进行数值编码。但对于深度学习而言,这并不是一个很好的操作,数值会带来大小关系,且会丢失很多信息。因此对于类别数据建议使用one-hot
或Embedding
操作,对于nn.Embedding
模块,你需要设置的参数包括:
num_embeddings
:数据类别的数量embedding_dim
:每个类别的嵌入维度padding_idx
:填充符号的索引
嵌入特征向量从随机初始化,不要用 Kaiming、Xavier初始化方法。因为标准差为1,初始化、激活函数等被设计为输入标准差为 1。nn.Embedding
模块的示例用法:
import torch
import torch.nn as nn
# Create 5 embedding vectors each with 32 features
embedding = nn.Embedding(num_embeddings=5,
embedding_dim=32)
# Example integer input
input_tensor = torch.LongTensor([[0, 4], [2, 3], [0, 1]])
# Get embeddings
embed_vectors = embedding(input_tensor)
print("Input shape:", input_tensor.shape)
print("Output shape:", embed_vectors.shape)
print("Example features:\n", embed_vectors[:,:,:2])
nn.LSTM 中 数据维度
默认情况下,PyTorch的nn.LSTM
模块假定输入维度为[seq_len, batch_size, input_size]
,所以确保不要混淆序列长度和批大小的次数。如果混淆LSTM仍然可以正常运行,但会给出错误的结果。
维度不匹配
如果Pytorch执行矩阵乘法,并两个矩阵出现维度不匹配,PyTorch会报错并抛出错误。但是也存在PyTorch不会抛出错误的情况,此时未对齐的维度具有相同的大小。建议使用多个不同的批量大小测试您的代码,以防止维度不对齐。
训练和评估模式
在PyTorch中,神经网络有两种模式:train
和train
。您可以使用model.eval()
和model.train()
对模型时进行切换。不同的模式决定是否使用dropout
,以及如何处理Batch Normalization
。常见的错误是在eval
后忘记将模型设置回train
模式,确定模型在预测阶段为eval
模式。
参数继承
PyTorch支持nn.Modules
,一个模块可以包含另一个模块,另一个模块又可以包含一个模块,依此类推。
当调用.parameters()
时,PyTorch会查找该模块内的所有模块,并将它们的参数添加到最高级别模块的参数中。
但是PyTorch不会检测列表、字典或类似结构中模块的参数。如果有一个模块列表,请确保将它们放入一个nn.ModuleList
或nn.Sequential
对象中。
参数初始化
正确初始化模型的参数非常重要。用标准正态分布初始化参数不是好的选择,推荐的方法有Kaiming
或Xavier
。
zero_grad()
请记住在执行loss.backward()
之前调用optimizer.zero_grad()
。如果在执行反向传播之前没有重置所有参数的梯度,梯度将被添加到上一批的梯度中。
指标计算逻辑
在怀疑自己或模型之前,请经常检查您的指标计算逻辑计算两次或更多次。像准确性这样的指标很容易计算,但在代码中添加错误也很容易。例如,检查您是否对批次维度进行了平均,而不是意外对类维度或任何其他维度进行平均。
设备不匹配
如果使用GPU可能会看到一个错误,例如:
Runtime Error: Input type (torch.FloatTensor) dand weigh type (torch.cuda.FloatTensor) should be on the same device.
此错误表示输入数据在CPU上,而权重在GPU上。确保所有数据都在同一设备上。这通常是GPU,因为它支持训练和测试加速。
nn.Sequential和nn.ModuleList
如果模型有很多层,推荐将它们汇总为一个nn.Sequential
或nn.ModuleList
对象。在前向传递中,只需要调用sequential,或者遍历模块列表。
class MLP(nn.Module):
def __init__(self, input_dims=64, hidden_dims=[128,256], output_dims=10):
super().__init__()
hidden_dims = [input_dims] + hidden_dims
layers = []
for idx in range(len(hidden_dims)-1):
layers += [
nn.Linear(hidden_dims[i], hidden_dims[i+1]),
nn.ReLU(inplace=True)
]
self.layers = nn.Sequential(*layers)
def forward(self, x):
return self.layers(x)
参数重复计算
在深度神经网络中,通常会有重复添加到模型中的块。如果这些块需要比更复杂的前向函数,建议在单独的模块中实现它们。例如,一个 ResNet 由多个具有残差连接的ResNet块组成。ResNet模块应用一个小型神经网络,并将输出添加回输入。最好在单独的类中实现这种动态,以保持主模型类小而清晰。
输入相同的维度
如果您有多个具有相同输入的线性层或卷积,则可以将它们堆叠在一起以提高效率。假设我们有:
虽然可以通过两个线性层来实现它,但您可以通过将两层堆叠为一层来获得完全相同的神经网络。单层效率更高,因为这代表单个矩阵运算,而不是GPU的两个矩阵运算,因此我们可以并行化计算。
x = torch.randn(2, 10)
# Implementation of separate layers:
y1_layer = nn.Linear(10, 20)
y2_layer = nn.Linear(10, 30)
y1 = y1_layer(x)
y2 = y2_layer(x)
# Implementation of a stacked layer:
y_layer = nn.Linear(10, 50)
y = y_layer(x)
y1, y2 = y[:,:20], y[:,20:50]
使用带logits的损失函数
分类损失函数(例如二元交叉熵)在PyTorch中有两个版本:nn.BCELoss
和nn.BCEWithLogitsLoss
,建议和推荐的做法是使用后者。这因为它在数值上更稳定,并在您的模型预测非常错误时防止出现任何不稳定性。
如果您不使用logit损失函数,则当模型预测不正确的非常高或非常低的值时,您可能会遇到问题。
如果觉得有用,就请分享到朋友圈吧!
公众号后台回复“CVPR21检测”获取CVPR2021目标检测论文下载~
# CV技术社群邀请函 #
备注:姓名-学校/公司-研究方向-城市(如:小极-北大-目标检测-深圳)
即可申请加入极市目标检测/图像分割/工业检测/人脸/医学影像/3D/SLAM/自动驾驶/超分辨率/姿态估计/ReID/GAN/图像增强/OCR/视频理解等技术交流群
每月大咖直播分享、真实项目需求对接、求职内推、算法竞赛、干货资讯汇总、与 10000+来自港科大、北大、清华、中科院、CMU、腾讯、百度等名校名企视觉开发者互动交流~