目录
二、模型加载
三、一些注意的情况
1.保存加载用于推理的常规Checkpoint/或继续训练
2.加载pytorch预训练模型
3.保存多个模型到一个文件
4.关于torch.nn.DataParallel
pytorch有两种模型保存方式:
1.保存整个神经网络的的结构信息和模型参数信息,save的对象是网络net
# 保存和加载整个模型
torch.save(model_object, 'resnet.pth')
model = torch.load('resnet.pth')
2.只保存神经网络的训练模型参数,save的对象是net.state_dict()
# 将my_resnet模型储存为my_resnet.pth
torch.save(my_resnet.state_dict(), "my_resnet.pth")
# 加载resnet,模型存放在my_resnet.pth
my_resnet.load_state_dict(torch.load("my_resnet.pth"))
1.加载整个模型
# 模型类必须在别的地方定义
model = torch.load(PATH)
model.eval()
这种保存/加载模型的过程使用了最直观的语法,所用代码量少。这使用Python的pickle保存所有模块。这种方法的缺点是,保存模型的时候,序列化的数据被绑定到了特定的类和确切的目录。这是因为pickle不保存模型类本身,而是保存这个类的路径,并且在加载的时候会使用。因此,当在其他项目里使用或者重构的时候,加载模型的时候会出错。
一般来说,PyTorch的模型以.pt或者.pth文件格式保存。
一定要记住在评估模式的时候调用model.eval()
来固定dropout和批次归一化。否则会产生不一致的推理结果。
2.加载 state_dict(推荐)
model = TheModelClass(*args, **kwargs)#先定义模型
model.load_state_dict(torch.load(PATH))#加载参数
model.eval()
保存:
torch.save({
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': loss,
...
}, PATH)
加载:
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()
# - 或者 -
model.train()
在保存用于推理或者继续训练的常规检查点的时候,除了模型的state_dict之外,还必须保存其他参数。保存优化器的state_dict也非常重要,因为它包含了模型在训练时候优化器的缓存和参数。除此之外,还可以保存停止训练时epoch数,最新的模型损失,额外的torch.nn.Embedding
层等。
要保存多个组件,则将它们放到一个字典中,然后使用torch.save()
序列化这个字典。一般来说,使用.tar文件格式来保存这些检查点。
加载各个组件,首先初始化模型和优化器,然后使用torch.load()
加载保存的字典,然后可以直接查询字典中的值来获取保存的组件。
同样,评估模型的时候一定不要忘了调用model.eval()
。
1)加载预训练模型和参数
# PyTorch中的torchvision里有很多常用的模型,可以直接调用:
import torchvision.models as models
resnet101 = models.resnet18()
alexnet = models.alexnet()
squeezenet = models.squeezenet1_0()
densenet = models.densenet_161()
2)只加载模型,不加载预训练参数
# 导入模型结构
resnet18 = models.resnet18(pretrained=False)
# 加载预先下载好的预训练参数到resnet18
resnet18.load_state_dict(torch.load('resnet18-5c106cde.pth'))
3)加载部分预训练模型
resnet152 = models.resnet152(pretrained=True)
pretrained_dict = resnet152.state_dict()
"""加载torchvision中的预训练模型和参数后通过state_dict()方法提取参数
也可以直接从官方model_zoo下载:
pretrained_dict = model_zoo.load_url(model_urls['resnet152'])"""
model_dict = model.state_dict()
# 将pretrained_dict里不属于model_dict的键剔除掉
pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
# 更新现有的model_dict
model_dict.update(pretrained_dict)
# 加载我们真正需要的state_dict
model.load_state_dict(model_dict)
保存的模型包含多个torch.nn.Modules
时,比如GAN,一个序列-序列模型,或者组合模型,使用与保存常规检查点的方式来保存模型。也就是说,保存每个模型的state_dict和对应的优化器到一个字典中。我们可以保存任何能帮助我们继续训练的东西到这个字典中
保存:
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)
加载:
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()
# - 或者 -
modelA.train()
modelB.train()
在多卡的GPU服务器,当我们在上面跑程序的时候,当迭代次数或者epoch足够大的时候,我们通常会使用nn.DataParallel函数来用多个GPU来加速训练。
虽然我们可以直接保存DataParallel, 在load的时候直接对DataParallel进行load,但是这样的话模型就不能转移到单GPu上使用。所以我们统一对module部分load和save,这样模型既可以在单GPU也可以在多GPU上使用。
# save: 我们保存的是DataParallel的module(也就是我们真正要使用的神经网络)
model = resnet101()
model = torch.nn.DataParallel(model)
model = model.module()
torch.save(model.state_dict(), 'state')
# load: 我们对.module进行load, 直接对DataParallel进行load的话会报错,因为网络层的名称不同(parallel的网络层名称多了个.module)
model2 = resnet101()
model2 = torch.nn.DataParallel(model2)
model2 = model2.module()
model2.load_state_dict(torch.load('state'))
2)在训练代码中:若采用多卡并行训练,model = nn.DataParallel(train_model)
若比如测试时想调用backbone之后的结果, x=model.backbone(feats)
则会报错:AttributeError: ‘DistributedDataParallel’ object has no attribute ‘backbone’
此时,model下的子网络结构backbone、layer1、layer2的调用方法应该改变,如下所示
x=model.module.backbone(feats),直接调用(不加module)适合单卡训练的情况