SAVING AND LOADING MODELS

本文档提供了有关PyTorch模型保存和加载的各种用例的解决方案。 您可以随意阅读整个文档,或者只是跳转到所需用例所需的代码。

在保存和加载模型时,有三个核心函数需要熟悉:

  1. torch.save:将序列化对象保存到磁盘。 该函数使用Python的pickle实用程序进行序列化。 使用此函数可以保存各种对象的模型,张量和词典。
  2. torch.load:使用pickle的unpickling工具将pickle对象文件反序列化到内存中。 此功能还有助于设备将数据加载到(请参阅跨设备保存和加载模型)。
  3. torch.nn.Module.load_state_dict:使用反序列化的state_dict加载模型的参数字典。有关state_dict的更多信息,请参阅什么是state_dict?

Contents:

What is a state_dict?

在PyTorch中,torch.nn.Module模型的可学习参数(即权重和偏差)包含在模型的参数中(使用model.parameters()访问)。 state_dict只是一个Python字典对象,它将每个图层映射到其参数张量。请注意,只有具有可学习参数(卷积层,线性层等)和已注册缓冲区(batchnorm的running_mean)的图层在模型的state_dict中具有条目。优化器对象(torch.optim)也有一个state_dict,它包含有关优化器状态的信息,以及使用的超参数

因为state_dict对象是Python字典,所以它们可以轻松保存,更新,更改和恢复,为PyTorch模型和优化器添加了大量模块化。

Example:

让我们看一下Training a classifier教程中使用的简单模型中的state_dict。

# Define model
class TheModelClass(nn.Module):
    def __init__(self):
        super(TheModelClass, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# Initialize model
model = TheModelClass()

# Initialize optimizer
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

# Print model's state_dict
print("Model's state_dict:")
for param_tensor in model.state_dict():
    print(param_tensor, "\t", model.state_dict()[param_tensor].size())

# Print optimizer's state_dict
print("Optimizer's state_dict:")
for var_name in optimizer.state_dict():
    print(var_name, "\t", optimizer.state_dict()[var_name])

Output:

Model's state_dict:
conv1.weight     torch.Size([6, 3, 5, 5])
conv1.bias   torch.Size([6])
conv2.weight     torch.Size([16, 6, 5, 5])
conv2.bias   torch.Size([16])
fc1.weight   torch.Size([120, 400])
fc1.bias     torch.Size([120])
fc2.weight   torch.Size([84, 120])
fc2.bias     torch.Size([84])
fc3.weight   torch.Size([10, 84])
fc3.bias     torch.Size([10])

Optimizer's state_dict:
state    {}
param_groups     [{'lr': 0.001, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [4675713712, 4675713784, 4675714000, 4675714072, 4675714216, 4675714288, 4675714432, 4675714504, 4675714648, 4675714720]}]

Saving & Loading Model for Inference

Save/Load state_dict (Recommended)

Save:

torch.save(model.state_dict(), PATH)

Load:

model = TheModelClass(*args, **kwargs)
model.load_state_dict(torch.load(PATH))
model.eval()

保存模型进行推理时,只需要保存训练模型的学习参数。使用torch.save()函数保存模型的state_dict将为您以后恢复模型提供最大的灵活性,这就是为什么它是保存模型的推荐方法。

常见的PyTorch约定是使用.pt或.pth文件扩展名保存模型。

请记住,在运行推理之前,必须调用model.eval()将dropout和批处理规范化层设置为评估模式。如果不这样做,将导致不一致的推理结果。

请注意,load_state_dict()函数接受字典对象,而不是保存对象的路径。 这意味着您必须在将已保存的state_dict传递给load_state_dict()函数之前对其进行反序列化。 例如,您无法使用model.load_state_dict(PATH)加载。

Save/Load Entire Model

Save:

torch.save(model, PATH)

Load:

# Model class must be defined somewhere
model = torch.load(PATH)
model.eval()

此保存/加载过程使用最直观的语法并涉及最少量的代码。 以这种方式保存模型将使用Python的pickle模块保存整个模块。 这种方法的缺点是序列化数据绑定到特定类以及保存模型时使用的确切目录结构。这是因为pickle不保存模型类本身。 相反,它会保存包含类的文件的路径,该文件在加载时使用。 因此,当您在其他项目中或在重构之后使用时,您的代码可能会以各种方式中断。

常见的PyTorch约定是使用.pt或.pth文件扩展名保存模型。

请记住,在运行推理之前,必须调用model.eval()将dropout和批处理规范化层设置为评估模式。 如果不这样做,将导致不一致的推理结果。

Saving & Loading a General Checkpoint for Inference and/or Resuming Training

Save:

torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
            'loss': loss,
            ...
            }, PATH)

Load:

model = TheModelClass(*args, **kwargs)
optimizer = TheOptimizerClass(*args, **kwargs)

checkpoint = torch.load(PATH)
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']

model.eval()
# - or -
model.train()

保存通用检查点时,要用于推理或恢复训练,您必须保存的不仅仅是模型的state_dict。 保存优化器的state_dict也很重要,因为它包含作为模型训练更新的缓冲区和参数。您可能想要保存的其他项目是您停止使用的epoch,最新记录的训练损失,外部torch.nn.Embedding层等。

要保存多个组件,请在字典中组织它们并使用torch.save()来序列化字典。 常见的PyTorch约定是使用.tar文件扩展名保存这些检查点。

要加载项目,首先初始化模型和优化器,然后使用torch.load()在本地加载字典。 从这里,您可以通过简单地查询字典来轻松访问已保存的项目。

请记住,在运行推理之前,必须调用model.eval()将dropout和批处理规范化层设置为评估模式。 如果不这样做,将导致不一致的推理结果。 如果您希望恢复训练,请调用model.train()以确保这些图层处于训练模式。

Saving Multiple Models in One File

Save:

torch.save({
            'modelA_state_dict': modelA.state_dict(),
            'modelB_state_dict': modelB.state_dict(),
            'optimizerA_state_dict': optimizerA.state_dict(),
            'optimizerB_state_dict': optimizerB.state_dict(),
            ...
            }, PATH)

Load:

modelA = TheModelAClass(*args, **kwargs)
modelB = TheModelBClass(*args, **kwargs)
optimizerA = TheOptimizerAClass(*args, **kwargs)
optimizerB = TheOptimizerBClass(*args, **kwargs)

checkpoint = torch.load(PATH)
modelA.load_state_dict(checkpoint['modelA_state_dict'])
modelB.load_state_dict(checkpoint['modelB_state_dict'])
optimizerA.load_state_dict(checkpoint['optimizerA_state_dict'])
optimizerB.load_state_dict(checkpoint['optimizerB_state_dict'])

modelA.eval()
modelB.eval()
# - or -
modelA.train()
modelB.train()

保存由多个torch.nn.Modules组成的模型时,例如GAN,序列到序列模型或模型集合,您可以采用与保存常规检查点时相同的方法。换句话说,保存每个模型的state_dict和相应的优化器的字典。 如前所述,您可以通过简单地将它们附加到字典中来保存任何其他可能有助于您恢复培训的项目。

常见的PyTorch约定是使用.tar文件扩展名保存这些检查点。

要加载模型,首先初始化模型和优化器,然后使用torch.load()在本地加载字典。 从这里,您可以通过简单地查询字典来轻松访问已保存的项目。

请记住,在运行推理之前,必须调用model.eval()将dropout和批处理规范化层设置为评估模式。 如果不这样做,将导致不一致的推理结果。 如果您希望恢复训练,请调用model.train()将这些图层设置为训练模式。

Warmstarting Model Using Parameters from a Different Model

Save:

torch.save(modelA.state_dict(), PATH)

Load:

modelB = TheModelBClass(*args, **kwargs)
modelB.load_state_dict(torch.load(PATH), strict=False)

在迁移学习或训练新的复杂模型时,部分加载模型或加载部分模型是常见的情况。 利用经过训练的参数,即使只有少数可用,也有助于热启动训练过程,并希望帮助您的模型比从头开始训练更快地收敛。

无论是从缺少某些键的部分state_dict加载,还是使用比正在加载的模型更多的键加载state_dict,都可以在load_state_dict()函数中将strict参数设置为False以忽略不匹配键。

如果要将参数从一个层加载到另一个层,但某些键不匹配,只需更改正在加载的state_dict中的参数键的名称,以匹配要加载到的模型中的键。

Saving & Loading Model Across Devices

Save on GPU, Load on CPU

Save:

torch.save(model.state_dict(), PATH)

Load:

device = torch.device('cpu')
model = TheModelClass(*args, **kwargs)
model.load_state_dict(torch.load(PATH, map_location=device))

在使用GPU训练的CPU上加载模型时,将torch.device('cpu')传递给torch.load()函数中的map_location参数。 在这种情况下,使用map_location参数将张量下的存储器动态地重新映射到CPU设备。

Save on GPU, Load on GPU

Save:

torch.save(model.state_dict(), PATH)

Load:

device = torch.device("cuda")
model = TheModelClass(*args, **kwargs)
model.load_state_dict(torch.load(PATH))
model.to(device)
# Make sure to call input = input.to(device) on any input tensors that you feed to the model

在GPU上训练并保存在GPU上的模型时,只需使用model.to(torch.device('cuda'))将初始化模型转换为CUDA优化模型。 此外,请务必在所有模型输入上使用.to(torch.device('cuda'))函数来为模型准备数据。请注意,调用my_tensor.to(device)会在GPU上返回my_tensor的新副本。 它不会覆盖my_tensor。 因此,请记住手动覆盖张量:my_tensor = my_tensor.to(torch.device('cuda'))。

Save on CPU, Load on GPU

Save:

torch.save(model.state_dict(), PATH)

Load:

device = torch.device("cuda")
model = TheModelClass(*args, **kwargs)
model.load_state_dict(torch.load(PATH, map_location="cuda:0"))  # Choose whatever GPU device number you want
model.to(device)
# Make sure to call input = input.to(device) on any input tensors that you feed to the model

在已训练并保存在CPU上的GPU上加载模型时,请将torch.load()函数中的map_location参数设置为cuda:device_id。 这会将模型加载到给定的GPU设备。 接下来,请务必调用model.to(torch.device('cuda'))将模型的参数张量转换为CUDA张量。 最后,确保在所有模型输入上使用.to(torch.device('cuda'))函数来为CUDA优化模型准备数据。请注意,调用my_tensor.to(device)会在GPU上返回my_tensor的新副本。 它不会覆盖my_tensor。 因此,请记住手动覆盖张量:my_tensor = my_tensor.to(torch.device('cuda'))。

Saving torch.nn.DataParallel Models

Save:

torch.save(model.module.state_dict(), PATH)

Load:

# Load to whatever device you want

torch.nn.DataParallel是一个模型包装器,支持并行GPU利用。 要一般性地保存DataParallel模型,请保存model.module.state_dict()。 这样,您就可以灵活地以任何方式加载模型到您想要的任何设备。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值