paddlepaddle飞桨(学习笔记七——模型保存与加载)

模型训练后,训练好的模型参数保存在内存中,通常需要使用模型保存(save)功能将其持久化保存到磁盘文件中,并在后续需要训练调优或推理部署时,再加载(load)到内存中运行。本章详细介绍不同场景下模型保存与加载的方法。

1、训练调优场景

 

 1.1 使用基础API

  • paddle.save:使用 paddle.save保存模型,实际是通过 Python pickle 模块来实现的,传入要保存的数据对象后,会在指定路径下生成一个 pickle 格式的磁盘文件。

  • paddle.load:加载时还需要之前的模型组网代码,并使用paddle.load传入保存的文件路径,即可重新将之前保存的数据从磁盘文件中载入。

另外,paddle.save还支持直接保存 Tensor 数据,或者含 Tensor 的 list/dict 嵌套结构。所以动态图模式下,可支持保存和加载的内容包括:

  • 网络层参数: Layer.state_dict()

  • 优化器参数: Optimizer.state_dict()

  • Tensor 数据 :(如创建的 Tensor 数据、网络层的 weight 数据等)

  • 含 Tensor 的 list/dict 嵌套结构对象 (如保存 state_dict() 的嵌套结构对象:obj = {'model': layer.state_dict(), 'opt': adam.state_dict(), 'epoch': 100}

保存动态图模型
# 保存Layer参数
paddle.save(layer.state_dict(), "linear_net.pdparams")
# 保存优化器参数
paddle.save(adam.state_dict(), "adam.pdopt")
# 保存检查点checkpoint信息
paddle.save(final_checkpoint, "final_checkpoint.pkl")
  1. 对于 Layer.state_dict() (模型参数),推荐使用后缀 .pdparams ;

  2. 对于 Optimizer.state_dict() (优化器参数),推荐使用后缀 .pdopt 。

 加载动态图模型
# 载入模型参数、优化器参数和最后一个epoch保存的检查点
layer_state_dict = paddle.load("linear_net.pdparams")
opt_state_dict = paddle.load("adam.pdopt")
final_checkpoint_dict = paddle.load("final_checkpoint.pkl")

# 将load后的参数与模型关联起来
layer.set_state_dict(layer_state_dict)
adam.set_state_dict(opt_state_dict)

# 打印出来之前保存的 checkpoint 信息
print("Loaded Final Checkpoint. Epoch : {}, Loss : {}".format(final_checkpoint_dict["epoch"], final_checkpoint_dict["loss"].numpy()))

加载以后就可以继续对动态图模型进行训练调优(fine-tune),或者验证预测效果(predict)。

完整代码

注:在参数保存(三)完成后,可以直接将其注释掉,进行参数加载(四)

import numpy as np
import paddle
import paddle.nn as nn
import paddle.optimizer as opt

BATCH_SIZE = 16
BATCH_NUM = 4
EPOCH_NUM = 4

IMAGE_SIZE = 784
CLASS_NUM = 10

final_checkpoint = dict()

# 定义一个随机数据集
class RandomDataset(paddle.io.Dataset):
    def __init__(self, num_samples):
        self.num_samples = num_samples

    def __getitem__(self, idx):
        image = np.random.random([IMAGE_SIZE]).astype('float32')
        label = np.random.randint(0, CLASS_NUM - 1, (1, )).astype('int64')
        return image, label

    def __len__(self):
        return self.num_samples

class LinearNet(nn.Layer):
    def __init__(self):
        super().__init__()
        self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)

    def forward(self, x):
        return self._linear(x)

def train(layer, loader, loss_fn, opt):
    for epoch_id in range(EPOCH_NUM):
        for batch_id, (image, label) in enumerate(loader()):
            out = layer(image)
            loss = loss_fn(out, label)
            loss.backward()
            opt.step()
            opt.clear_grad()
            print("Epoch {} batch {}: loss = {}".format(
                epoch_id, batch_id, np.mean(loss.numpy())))
        # 最后一个epoch保存检查点checkpoint
        if epoch_id == EPOCH_NUM - 1:
            final_checkpoint["epoch"] = epoch_id
            final_checkpoint["loss"] = loss

if __name__ == '__main__':
    ####################!!一!!##############################
    #####################数据准备##############################
    # 创建网络、loss和优化器
    layer = LinearNet()
    loss_fn = nn.CrossEntropyLoss()
    adam = opt.Adam(learning_rate=0.001, parameters=layer.parameters())
    # 创建用于载入数据的DataLoader
    dataset = RandomDataset(BATCH_NUM * BATCH_SIZE)
    loader = paddle.io.DataLoader(dataset,
        batch_size=BATCH_SIZE,
        shuffle=True,
        drop_last=True,
        num_workers=2)
    ####################!!二!!##############################
    #####################模型训练##############################
    # 开始训练
    train(layer, loader, loss_fn, adam)
    ####################!!三!!##############################
    #####################参数保存##############################
    # 保存Layer参数
    paddle.save(layer.state_dict(), "linear_net.pdparams")
    # 保存优化器参数
    paddle.save(adam.state_dict(), "adam.pdopt")
    # 保存检查点checkpoint信息
    paddle.save(final_checkpoint, "final_checkpoint.pkl")
    ####################!!四!!##############################
    #####################参数加载##############################
    # 载入模型参数、优化器参数和最后一个epoch保存的检查点
    layer_state_dict = paddle.load("linear_net.pdparams")
    opt_state_dict = paddle.load("adam.pdopt")
    final_checkpoint_dict = paddle.load("final_checkpoint.pkl")

    # 将load后的参数与模型关联起来
    layer.set_state_dict(layer_state_dict)
    adam.set_state_dict(opt_state_dict)
    ###########################################################
    ###########################################################

1.2 使用高层API

保存动态图模型
  • 方式一:开启训练时调用的paddle.Model.fit函数可自动保存模型,通过它的参数 save_freq可以设置保存动态图模型的频率,即多少个 epoch 保存一次模型,默认值是 1。

  • 方式二:调用 paddle.Model.saveAPI。只需要传入保存的模型文件的前缀,格式如 dirname/file_prefix 或者 file_prefix ,即可保存训练后的模型参数和优化器参数,保存后的文件后缀名固定为 .pdparams 和.pdopt

#方式一:设置训练过程中保存模型
model.fit(data, epochs=1, batch_size=32, save_freq=1)

#方式二:设置训练后保存模型
model.save('checkpoint/test')  # save for training
加载动态图模型

高层 API 加载动态图模型所需要调用的 API 是 paddle.Model.load,从指定的文件中载入模型参数和优化器参数(可选)以继续训练。paddle.Model.load需要传入的核心的参数是待加载的模型参数或者优化器参数文件(可选)的前缀(需要保证后缀符合 .pdparams 和.pdopt)。

# 加载模型参数和优化器参数
model.load('checkpoint/test')
model.fit(data, epochs=1, batch_size=32, save_freq=1)

model.save('checkpoint/test_1')  # save for training
完整代码 

同上,Model.save或Model.fit后可以将其注释掉,直接Model.load

import paddle
from paddle.vision.transforms import Normalize

##################!!一!!#################################
##################数据准备##################################
transform = Normalize(mean=[127.5], std=[127.5], data_format='CHW')
# 下载数据集并初始化 DataSet
train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=transform)
test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=transform)

# 模型组网并初始化网络
lenet = paddle.vision.models.LeNet(num_classes=10)
model = paddle.Model(lenet)

##################!!二!!#################################
##################模型训练##################################
# 模型训练的配置准备,准备损失函数,优化器和评价指标
model.prepare(paddle.optimizer.Adam(parameters=model.parameters()),
              paddle.nn.CrossEntropyLoss(),
              paddle.metric.Accuracy())

# 模型训练
model.fit(train_dataset, epochs=5, batch_size=64, verbose=1)
# 模型评估
model.evaluate(test_dataset, batch_size=64, verbose=1)
##################!!三!!#################################
##################保存模型##################################
# 保存模型
model.save('./output/mnist')
##################!!四!!#################################
##################加载模型##################################
#加载模型,调优参数
model.load('output/mnist')
model.fit(test_dataset, epochs=1, batch_size=32, save_freq=1)
model.save('checkpoint/test_1')  # save for training

2、 推理部署场景

2.1 使用基础API

  • paddle.jit.save 保存模型
  • paddle.jit.load 加载模型

使用 paddle.jit.save 保存模型,通常是在后台执行了两个步骤:

  1. 先执行了动转静。当然如果前面已经执行了动转静训练,则跳过这一步。在处理逻辑上,主要包含两个主要模块:

    • 模型结构层面:将动态图模型中被 @paddle.jit.to_static 装饰的函数转化为完整的静态图 Program。

    • 模型参数层面:将动态图模型中的参数(Parameters 和 Buffers )转为 Persistable=True 的静态图模型参数 Variable。

  2. 再将静态图模型和参数导出为磁盘文件。Program 和 Variable 都可以直接序列化导出为磁盘文件,与前端代码完全解耦,导出的文件包括:

    • 后缀为 .pdmodel 的模型结构文件;

    • 后缀为 .pdiparams 的模型参数文件;

    • 后缀为 .pdiparams.info 的和参数状态有关的额外信息文件。

 动转静

 方式一:使用 @paddle.jit.to_static 装饰器

class LinearNet(nn.Layer):
    def __init__(self):
        super().__init__()
        self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)

    @paddle.jit.to_static       # <----在前向计算 forward 函数前添加一个装饰器
    def forward(self, x):
        return self._linear(x)

 方式二:使用 @paddle.jit.to_static 函数

# create network
layer = LinearNet()
layer = paddle.jit.to_static(layer) # <----通过函数式调用 paddle.jit.to_static(layer) 一键实现动转静
模型保存样例

动转静训练完成后,使用 paddle.jit.save 对模型和参数进行存储:

# 如果保存模型用于推理部署,则需切换 eval()模式
layer.eval()
# 使用 paddle.jit.save 保存训练好的静态图模型
path = "example.model/linear"
paddle.jit.save(layer, path)
 模型加载样例

动转静训练保存模型后,如果需要再加载用于训练调优或验证推理效果,可以选择使用 paddle.jit.load 或 paddle.load API。

  • 使用 paddle.jit.load 加载:该方式可以载入模型结构和参数,传入数据即可训练或推理。

  • 使用 paddle.load 加载:如果已有组网代码,则只传入模型参数也可再训练,因此也可以选择该方式加载。

# 载入 paddle.jit.save 保存的模型
path = "example.model/linear"
loaded_layer = paddle.jit.load(path)

# 执行预测
loaded_layer.eval()
x = paddle.randn([1, IMAGE_SIZE], 'float32')
pred = loaded_layer(x)
完整代码 

注意同上。

import numpy as np
import paddle
import paddle.nn as nn
import paddle.optimizer as opt

BATCH_SIZE = 16
BATCH_NUM = 4
EPOCH_NUM = 4

IMAGE_SIZE = 784
CLASS_NUM = 10

# define a random dataset
class RandomDataset(paddle.io.Dataset):
    def __init__(self, num_samples):
        self.num_samples = num_samples

    def __getitem__(self, idx):
        image = np.random.random([IMAGE_SIZE]).astype('float32')
        label = np.random.randint(0, CLASS_NUM, (1, )).astype('int64')
        return image, label

    def __len__(self):
        return self.num_samples

class LinearNet(nn.Layer):
    def __init__(self):
        super().__init__()
        self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)

    def forward(self, x):
        return self._linear(x)

def train(layer, loader, loss_fn, opt):
    for epoch_id in range(EPOCH_NUM):
        for batch_id, (image, label) in enumerate(loader()):
            out = layer(image)
            loss = loss_fn(out, label)
            loss.backward()
            opt.step()
            opt.clear_grad()
            print("Epoch {} batch {}: loss = {}".format(
                epoch_id, batch_id, np.mean(loss.numpy())))
if __name__ == '__main__':
    #####################!!一!!#######################################
    #####################定义模型#######################################
    # 定义模型
    layer = LinearNet()
    layer = paddle.jit.to_static(layer) # <----通过函数式调用 paddle.jit.to_static(layer) 一键实现动转静
    loss_fn = nn.CrossEntropyLoss()
    adam = opt.Adam(learning_rate=0.001, parameters=layer.parameters())
    #####################!!二!!#######################################
    #####################数据加载#######################################
    # 创建数据集
    dataset = RandomDataset(BATCH_NUM * BATCH_SIZE)
    loader = paddle.io.DataLoader(dataset,
        batch_size=BATCH_SIZE,
        shuffle=True,
        drop_last=True,
        num_workers=2)
    #####################!!三!!#######################################
    #####################模型训练#######################################
    # 模型训练
    train(layer, loader, loss_fn, adam)
    #####################!!四!!#######################################
    #####################参数保存#######################################
    # 如果保存模型用于推理部署,则需切换 eval()模式
    layer.eval()
    # 使用 paddle.jit.save 保存训练好的静态图模型
    path = "example.model/linear"
    paddle.jit.save(layer, path)
    #####################!!五!!#######################################
    #####################参数加载#######################################
    # 载入 paddle.jit.save 保存的模型
    path = "example.model/linear"
    loaded_layer = paddle.jit.load(path)
    #####################!!六!!#######################################
    #####################执行推理#######################################
    # 执行预测
    loaded_layer.eval()
    x = paddle.randn([1, IMAGE_SIZE], 'float32')
    pred = loaded_layer(x)
    print(pred)

2.2 使用高层API

paddle.Model.save的第一个参数需要设置为待保存的模型和参数等文件的前缀名,第二个参数 training 表示是否保存动态图模型以继续训练,默认是 True,这里需要设为 False,即保存推理部署所需的参数与文件。接前文高层 API 训练的示例代码,保存推理模型代码示例如下:

model.save('inference_model', False)  # save for inference

其他同训练调优场景

# 加载模型参数和优化器参数
model.load('checkpoint/test')
test_result = model.predict(test_dataset)

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一级piaopiao虎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值