Torcheck —— 测试你的 PyTorch 模型
引言
你是否有过这样的经历:长时间训练 PyTorch 模型,结果发现在模型的 forward 方法中输入了一行错误?你是否曾经遇到过这样的情况:你从模型中获得了一些合理的输出,但是不确定这是否表明你构建的模型是正确的,或者这只是因为深度学习是如此强大,即使是错误的模型架构也会产生下降的结果。
就我个人而言,测试深度学习模型有时会让我抓狂。最突出的痛点是:
它的黑盒特性使它很难测试。即使不是不可能,也需要很多专业知识来理解中间结果
长的训练时间大大减少了迭代次数
没有专门的工具。通常,您希望在一个小样本数据集上测试模型,这需要重复编写样板代码,以便进行设置优化、计算损失和反向传播
为了减少这种开销,我之前做了一些研究。这里有一篇详细文章:https://thenerdstation.medium.com/how-to-unit-test-machine-learning-code-57cf6fd81765,其核心思想是,我们永远不能百分之百确定我们的模型是正确的,但至少它应该能够通过一些合理性检验。换句话说,这些合理性检验是必要的,但可能还不够。
为了节省您的时间,以下是他提出的所有合理性检验的摘要:
如果一个模型参数在训练过程中没有被故意冻结,那么它应该在训练过程中不断变化。这可以是 PyTorch 线性层的张量
模型参数在训练过程中不应该改变,如果它被冻结。这可能是一个你不想更新的预训练好的层
根据您的模型属性,模型输出的范围应该服从某些条件。例如,如果它是一个分类模型,它的输出不应该都在范围(0,1)内。否则,很有可能您在计算损失之前错误地将 softmax 最大激活函数应用于输出
(这实际上不是来自那篇文章,而是一个常见的问题)在大多数情况下,模型参数不应该包含 NaN 或 Inf(infinite number),这同样适用于模型输出
除了提出这些检查,他还构建了一个 Python 包来实现这些检查。这是一个很好的包,但仍然有未解决的痛点。这个包是几年前创建的,现在已经不再维护了。
因此,受这种合理性检验思想的启发,目标是创建一个易于使用的 Python 包,创建 torcheck!其主要创新包括:
不再需要额外的测试代码。只需添加几行代码指定训练前的检查,torcheck 将在训练发生时执行检查,并在检查失败时提出信息性错误消息
可以在不同的级别检查模型。你可以指定子模块、线性层甚至权重张量的检查,而不是检验整个模型!这样就可以对复杂体系结构的检查进行更多的自定义
接下来,我们会给你一个关于 torcheck 的快速教程。
假设我们已经编写了一个 ConvNet 模型来对 MNIST 数据集进行分类:
# model
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 1, kernel_size=1, stride=1)
self.conv2 = nn.Conv2d(1, 8, kernel_size=3, stride=1, padding=1)
self.conv3 = nn.Conv2d(8, 16, kernel_size=3, stride=1, padding=1)
self.relu = nn.ReLU()
self.maxpool = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(16 * 7 * 7, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
output = self.relu(self.conv1(x))
output = self.relu(self.conv2(x))
output = self.maxpool(output)
output = self.relu(self.conv3(output))
output = self.maxpool(output)
output = output.view(output.size()[0], -1)
output = self.relu(self.fc1(output))
output = self.fc2(output)
return output
# training routine
model = CNN()
optimizer = optim.Adam(model.parameters(), lr=0.001)
for epoch in range(num_epochs):
for x, y in dataloader:
y_pred = model(x)
loss = F.cross_entropy(y_pred, y)
loss.backward()
optimizer.step()
optimizer.zero_grad()
在模型代码中实际上有一个细微的错误。你们中的一些人可能已经注意到了:在第16行中,我们不小心把 x 放在了右边,而应该是 output。
现在让我们看看 torcheck 如何帮助我们检验这个隐藏的错误!
步骤0:安装
在我们开始之前,首先用一行代码安装软件包。
pip install torcheck
步骤1: 添加 torcheck 代码
接下来我们将添加代码。Torcheck 代码总是驻留在循环训练之前,在模型和优化器实例化之后,如下所示:
# model and optimizer instantiation
model = CNN()
optimizer = optim.Adam(model.parameters(), lr=0.001)
###########################
# torcheck code goes here #
###########################
# training routine
for epoch in range(num_epochs):
for x, y in dataloader:
y_pred = model(x)
loss = F.cross_entropy(y_pred, y)
loss.backward()
optimizer.step()
optimizer.zero_grad()
步骤1.1:注册优化器
首先,用 torcheck 注册你的优化器:
torcheck.register(optimizer)
步骤1.2:增加合理性检验
接下来,添加要在四个类别中执行的所有检验。
1. 参数改变/不改变
对于我们的例子,我们希望在训练过程中更改所有的模型参数:
# check all the model parameters will change
# module_name is optional, but it makes error messages more informative when checks fail
torcheck.add_module_changing_check(model, module_name="my_model")
附注
为了演示 torcheck 的全部功能,让我们假设稍后你已经冻结了卷积层,只想微调线性层。在这种情况下将会像这样:
# check the first convolutional layer's parameters won't change
torcheck.add_module_unchanging_check(model.conv1, module_name="conv_layer_1")
# check the second convolutional layer's parameters won't change
torcheck.add_module_unchanging_check(model.conv2, module_name="conv_layer_2")
# check the third convolutional layer's parameters won't change
torcheck.add_module_unchanging_check(model.conv3, module_name="conv_layer_3")
# check the first linear layer's parameters will change
torcheck.add_module_changing_check(model.fc1, module_name="linear_layer_1")
# check the second linear layer's parameters will change
torcheck.add_module_changing_check(model.fc2, module_name="linear_layer_2")
2. 输出范围检查
因为我们的模型是一个分类模型,所以我们想要添加前面提到的检验:模型输出不应该都在范围(0,1)内。
# check model outputs are not all within (0, 1)
# aka softmax hasn't been applied before loss calculation
torcheck.add_module_output_range_check(
model,
output_range=(0, 1),
negate_range=True,
)
negate_range = True 参数带有“ not all”的含义。如果您只是想检查模型输出都在某个范围内,只需删除该参数。
尽管 torcheck 不适用于我们的示例,但是它还允许你检查子模块的中间输出。
3. NaN 检查
我们当然希望确保模型参数在训练期间不会变成 NaN,并且模型输出不包含 NaN。添加 NaN 检查很简单:
# check whether model parameters become NaN or outputs contain NaN
torcheck.add_module_nan_check(model)
4. Inf 检查
类似地,添加 Inf 检查:
# check whether model parameters become infinite or outputs contain infinite value
torcheck.add_module_inf_check(model)
在添加了所有感兴趣的检验之后,最终的训练代码如下:
model = CNN()
optimizer = optim.Adam(model.parameters(), lr=0.001)
torcheck.register(optimizer)
torcheck.add_module_changing_check(model, module_name="my_model")
torcheck.add_module_output_range_check(model, output_range=(0, 1), negate_range=True)
torcheck.add_module_nan_check(model)
torcheck.add_module_inf_check(model)
for epoch in range(num_epochs):
for x, y in dataloader:
y_pred = model(x)
loss = F.cross_entropy(y_pred, y)
loss.backward()
optimizer.step()
optimizer.zero_grad()
步骤2:训练和修复
现在让我们像往常一样进行训练,看看会发生什么:
$ python run.py
Traceback (most recent call last):
(stack trace information here)
RuntimeError: The following errors are detected while training:
Module my_model's conv1.weight should change.
Module my_model's conv1.bias should change.
砰!我们立即得到一个错误消息,说我们的模型的 conv1.weight 和 conv1.bias 不会改变。一定是 model.conv1出了什么问题。
正如预期的那样,我们转向模型代码,注意错误,修复它,并重新运行训练。
(可选)步骤3:关闭检验
耶! 我们的模型通过了所有的检验。最后,我们可以对其进行关闭:
torcheck.disable()
当你想要在验证集上运行模型,或者只想从你的模型训练中消除检查耗时,这是非常有用的。
如果你还想继续使用,只需要:
torcheck.enable()
个人微信(如果没有备注不拉群!) 请注明:地区+学校/企业+研究方向+昵称
下载1:何恺明顶会分享
在「AI算法与图像处理」公众号后台回复:何恺明,即可下载。总共有6份PDF,涉及 ResNet、Mask RCNN等经典工作的总结分析
下载2:终身受益的编程指南:Google编程风格指南
在「AI算法与图像处理」公众号后台回复:c++,即可下载。历经十年考验,最权威的编程规范!
下载3 CVPR2021 在「AI算法与图像处理」公众号后台回复:CVPR,即可下载1467篇CVPR 2020论文 和 CVPR 2021 最新论文
点亮 ,告诉大家你也在看