模型保存与载入¶
一、保存载入体系简介¶
1.1 基础API保存载入体系¶
飞桨框架2.1对模型与参数的保存与载入相关接口进行了梳理:对于训练调优场景,我们推荐使用paddle.save/load保存和载入模型;对于推理部署场景,我们推荐使用paddle.jit.save/load(动态图)和paddle.static.save/load_inference_model(静态图)保存载入模型。
飞桨保存载入相关接口包括:
paddle.static.save_inference_model
paddle.static.load_inference_model
各接口关系如下图所示:
二、训练调优场景的模型&参数保存载入¶
2.1 动态图参数保存载入¶
若仅需要保存/载入模型的参数,可以使用 paddle.save/load
结合Layer和Optimizer的state_dict达成目的,此处state_dict是对象的持久参数的载体,dict的key为参数名,value为参数真实的numpy array值。
结合以下简单示例,介绍参数保存和载入的方法,以下示例完成了一个简单网络的训练过程:
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, (1, )).astype('int64')
return image, label
def __len__(self):
return self.num_samples
class LinearNet(nn.Layer):
def __init__(self):
super(LinearNet, self).__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())))
# create network
layer = LinearNet()
loss_fn = nn.CrossEntropyLoss()
adam = opt.Adam(learning_rate=0.001, parameters=layer.parameters())
# create data loader
dataset = RandomDataset(BATCH_NUM * BATCH_SIZE)
loader = paddle.io.DataLoader(dataset,
batch_size=BATCH_SIZE,
shuffle=True,
drop_last=True,
num_workers=2)
# train
train(layer, loader, loss_fn, adam)
2.1.1 参数保存¶
参数保存时,先获取目标对象(Layer或者Optimzier)的state_dict,然后将state_dict保存至磁盘,示例如下(接前述示例):
# save
paddle.save(layer.state_dict(), "linear_net.pdparams")
paddle.save(adam.state_dict(), "adam.pdopt")
2.1.2 参数载入¶
参数载入时,先从磁盘载入保存的state_dict,然后通过set_state_dict方法配置到目标对象中,示例如下(接前述示例):
# load
layer_state_dict = paddle.load("linear_net.pdparams")
opt_state_dict = paddle.load("adam.pdopt")
layer.set_state_dict(layer_state_dict)
adam.set_state_dict(opt_state_dict)
2.2 静态图模型&参数保存载入¶
若仅需要保存/载入模型的参数,可以使用 paddle.save/load
结合Program的state_dict达成目的,此处state_dict与动态图state_dict概念类似,dict的key为参数名,value为参数真实的值。若想保存整个模型,需要使用``paddle.save``将Program和state_dict都保存下来。
结合以下简单示例,介绍参数保存和载入的方法:
import paddle
import paddle.static as static
paddle.enable_static()
# create network
x = paddle.static.data(name="x", shape=[None, 224], dtype='float32')
z = paddle.static.nn.fc(x, 10)
place = paddle.CPUPlace()
exe = paddle.static.Executor(place)
exe.run(paddle.static.default_startup_program())
prog = paddle.static.default_main_program()
2.2.1 静态图模型&参数保存¶
参数保存时,先获取Program的state_dict,然后将state_dict保存至磁盘,示例如下(接前述示例):
paddle.save(prog.state_dict(), "temp/model.pdparams")
如果想要保存整个静态图模型,除了state_dict还需要保存Program
paddle.save(prog, "temp/model.pdmodel")
2.2.2 静态图模型&参数载入¶
如果只保存了state_dict,可以跳过此段代码,直接载入state_dict。如果模型文件中包含Program和state_dict,请先载入Program,示例如下(接前述示例):
prog = paddle.load("temp/model.pdmodel")
参数载入时,先从磁盘载入保存的state_dict,然后通过set_state_dict方法配置到Program中,示例如下(接前述示例):
state_dict = paddle.load("temp/model.pdparams")
prog.set_state_dict(state_dict)
三、训练部署场景的模型&参数保存载入¶
3.1 动态图模型&参数保存载入(训练推理)¶
若要同时保存/载入动态图模型结构和参数,可以使用 paddle.jit.save/load
实现。
3.1.1 动态图模型&参数保存¶
模型&参数存储根据训练模式不同,有两种使用情况:
动转静训练 + 模型&参数保存
动态图训练 + 模型&参数保存
3.1.1.1 动转静训练 + 模型&参数保存¶
动转静训练相比直接使用动态图训练具有更好的执行性能,训练完成后,直接将目标Layer传入 paddle.jit.save
保存即可。:
一个简单的网络训练示例如下:
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, (1, )).astype('int64')
return image, label
def __len__(self):
return self.num_samples
class LinearNet(nn.Layer):
def __init__(self):
super(LinearNet, self).__init__()
self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)
@paddle.jit.to_static
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())))
# create network
layer = LinearNet()
loss_fn = nn.CrossEntropyLoss()
adam = opt.Adam(learning_rate=0.001, parameters=layer.parameters())
# create data loader
dataset = RandomDataset(BATCH_NUM * BATCH_SIZE)
loader = paddle.io.DataLoader(dataset,
batch_size=BATCH_SIZE,
shuffle=True,
drop_last=True,
num_workers=2)
# train
train(layer, loader, loss_fn, adam)
随后使用 paddle.jit.save
对模型和参数进行存储(接前述示例):
# save
path = "example.model/linear"
paddle.jit.save(layer, path)
通过动转静训练后保存模型&参数,有以下三项注意点:
Layer对象的forward方法需要经由
paddle.jit.to_static
装饰
经过 paddle.jit.to_static
装饰forward方法后,相应Layer在执行时,会先生成描述模型的Program,然后通过执行Program获取计算结果,示例如下:
import paddle
import paddle.nn as nn
IMAGE_SIZE = 784
CLASS_NUM = 10
class LinearNet(nn.Layer):
def __init__(self):
super(LinearNet, self).__init__()
self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)
@paddle.jit.to_static
def forward(self, x):
return self._linear(x)
若最终需要生成的描述模型的Program支持动态输入,可以同时指明模型的 InputSepc
,示例如下:
import paddle
import paddle.nn as nn
from paddle.static import InputSpec
IMAGE_SIZE = 784
CLASS_NUM = 10
class LinearNet(nn.Layer):
def __init__(self):
super(LinearNet, self).__init__()
self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)
@paddle.jit.to_static(input_spec=[InputSpec(shape=[None, 784], dtype='float32')])
def forward(self, x):
return self._linear(x)
请确保Layer.forward方法中仅实现预测功能,避免将训练所需的loss计算逻辑写入forward方法
Layer更准确的语义是描述一个具有预测功能的模型对象,接收输入的样本数据,输出预测的结果,而loss计算是仅属于模型训练中的概念。将loss计算的实现放到Layer.forward方法中,会使Layer在不同场景下概念有所差别,并且增大Layer使用的复杂性,这不是良好的编码行为,同时也会在最终保存预测模型时引入剪枝的复杂性,因此建议保持Layer实现的简洁性,下面通过两个示例对比说明:
错误示例如下:
import paddle
import paddle.nn as nn
IMAGE_SIZE = 784
CLASS_NUM = 10
class LinearNet(nn.Layer):
def __init__(self):
super(LinearNet, self).__init__()
self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)
@paddle.jit.to_static
def forward(self, x, label=None):
out = self._linear(x)
if label:
loss = nn.functional.cross_entropy(out, label)
avg_loss = nn.functional.mean(loss)
return out, avg_loss
else:
return out
正确示例如下:
import paddle
import paddle.nn as nn
IMAGE_SIZE = 784
CLASS_NUM = 10
class LinearNet(nn.Layer):
def __init__(self):
super(LinearNet, self).__init__()
self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)
@paddle.jit.to_static
def forward(self, x):
return self._linear(x)
如果你需要保存多个方法,需要用
paddle.jit.to_static
装饰每一个需要被保存的方法。
注解
只有在forward之外还需要保存其他方法时才用这个特性,如果仅装饰非forward的方法,而forward没有被装饰,是不符合规范的。此时 paddle.jit.save
的 input_spec
参数必须为None。
示例代码如下:
import paddle
import paddle.nn as nn
from paddle.static import InputSpec
IMAGE_SIZE = 784
CLASS_NUM = 10
class LinearNet(nn.Layer):
def __init__(self):
super(LinearNet, self).__init__()
self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)
self._linear_2 = nn.Linear(IMAGE_SIZE, CLASS_NUM)
@paddle.jit.to_static(input_spec=[InputSpec(shape=[None, IMAGE_SIZE], dtype='float32')])
def forward(self, x):
return self._linear(x)
@paddle.jit.to_static(input_spec=[InputSpec(shape=[None, IMAGE_SIZE], dtype='float32')])
def another_forward(self, x):
return self._linear_2(x)
inps = paddle.randn([1, IMAGE_SIZE])
layer = LinearNet()
before_0 = layer.another_forward(inps)
before_1 = layer(inps)
# save and load
path = "example.model/linear"
paddle.jit.save(layer, path)
保存的模型命名规则:forward的模型名字为:模型名+后缀,其他函数的模型名字为:模型名+函数名+后缀。每个函数有各自的pdmodel和pdiparams的文件,所有函数共用pdiparams.info。上述代码将在 example.model
文件夹下产生5个文件: linear.another_forward.pdiparams、 linear.pdiparams、 linear.pdmodel、 linear.another_forward.pdmodel、 linear.pdiparams.info
当使用
jit.save
保存函数时,jit.save
只保存这个函数对应的静态图 Program ,不会保存和这个函数相关的参数。如果你必须保存参数,请使用Layer封装这个函数。
示例代码如下:
def fun(inputs):
return paddle.tanh(inputs)
path = 'func/model'
inps = paddle.rand([3, 6])
origin = fun(inps)
paddle.jit.save(
fun,
path,
input_spec=[
InputSpec(
shape=[None, 6], dtype='float32', name='x'),
])
load_func = paddle.jit.load(path)
load_result = load_func(inps)
3.1.1.2 动态图训练 + 模型&参数保存¶
动态图模式相比动转静模式更加便于调试,如果你仍需要使用动态图直接训练,也可以在动态图训练完成后调用 paddle.jit.save
直接保存模型和参数。
同样是一个简单的网络训练示例:
import numpy as np
import paddle
import paddle.nn as nn
import paddle.optimizer as opt
from paddle.static import InputSpec
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, (1, )).astype('int64')
return image, label
def __len__(self):
return self.num_samples
class LinearNet(nn.Layer):
def __init__(self):
super(LinearNet, self).__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())))
# create network
layer = LinearNet()
loss_fn = nn.CrossEntropyLoss()
adam = opt.Adam(learning_rate=0.001, parameters=layer.parameters())
# create data loader
dataset = RandomDataset(BATCH_NUM * BATCH_SIZE)
loader = paddle.io.DataLoader(dataset,
batch_size=BATCH_SIZE,
shuffle=True,
drop_last=True,
num_workers=2)
# train
train(layer, loader, loss_fn, adam)
训练完成后使用 paddle.jit.save
对模型和参数进行存储:
# save
path = "example.dy_model/linear"
paddle.jit.save(
layer=layer,
path=path,
input_spec=[InputSpec(shape=[None, 784], dtype='float32')])
动态图训练后使用 paddle.jit.save
保存模型和参数注意点如下:
相比动转静训练,Layer对象的forward方法不需要额外装饰,保持原实现即可
与动转静训练相同,请确保Layer.forward方法中仅实现预测功能,避免将训练所需的loss计算逻辑写入forward方法
在最后使用
paddle.jit.save
时,需要指定Layer的InputSpec
,Layer对象forward方法的每一个参数均需要对应的InputSpec
进行描述,不能省略。这里的input_spec
参数支持两种类型的输入:
InputSpec
列表
使用InputSpec描述forward输入参数的shape,dtype和name,如前述示例(此处示例中name省略,name省略的情况下会使用forward的对应参数名作为name,所以这里的name为 x
):
paddle.jit.save(
layer=layer,
path=path,
input_spec=[InputSpec(shape=[None, 784], dtype='float32')])
Example Tensor 列表
除使用InputSpec之外,也可以直接使用forward训练时的示例输入,此处可以使用前述示例中迭代DataLoader得到的 image
,示例如下:
paddle.jit.save(
layer=layer,
path=path,
input_spec=[image])
3.1.2 动态图模型&参数载入¶
载入模型参数,使用 paddle.jit.load
载入即可,载入后得到的是一个Layer的派生类对象 TranslatedLayer
, TranslatedLayer
具有Layer具有的通用特征,支持切换 train
或者 eval
模式,可以进行模型调优或者预测。
注解
为了规避变量名字冲突,载入之后会重命名变量。
载入模型及参数,示例如下:
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
# load
path = "example.model/linear"
loaded_layer = paddle.jit.load(path)
载入模型及参数后进行预测,示例如下(接前述示例):
# inference
loaded_layer.eval()
x = paddle.randn([1, IMAGE_SIZE], 'float32')
pred = loaded_layer(x)
载入模型及参数后进行调优,示例如下(接前述示例):
# 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, (1, )).astype('int64')
return image, label
def __len__(self):
return self.num_samples
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())))
# fine-tune
loaded_layer.train()
dataset = RandomDataset(BATCH_NUM * BATCH_SIZE)
loader = paddle.io.DataLoader(dataset,
batch_size=BATCH_SIZE,
shuffle=True,
drop_last=True,
num_workers=2)
loss_fn = nn.CrossEntropyLoss()
adam = opt.Adam(learning_rate=0.001, parameters=loaded_layer.parameters())
train(loaded_layer, loader, loss_fn, adam)
# save after fine-tuning
paddle.jit.save(loaded_layer, "fine-tune.model/linear", input_spec=[x])
此外, paddle.jit.save
同时保存了模型和参数,如果你只需要从存储结果中载入模型的参数,可以使用 paddle.load
接口载入,返回所存储模型的state_dict,示例如下:
import paddle
import paddle.nn as nn
IMAGE_SIZE = 784
CLASS_NUM = 10
class LinearNet(nn.Layer):
def __init__(self):
super(LinearNet, self).__init__()
self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM)
@paddle.jit.to_static
def forward(self, x):
return self._linear(x)
# create network
layer = LinearNet()
# load
path = "example.model/linear"
state_dict = paddle.load(path)
# inference
layer.set_state_dict(state_dict, use_structured_name=False)
layer.eval()
x = paddle.randn([1, IMAGE_SIZE], 'float32')
pred = layer(x)
3.2 静态图模型&参数保存载入(推理部署)¶
保存/载入静态图推理模型,可以通过 paddle.static.save/load_inference_model
实现。示例如下:
import paddle
import numpy as np
paddle.enable_static()
# Build the model
startup_prog = paddle.static.default_startup_program()
main_prog = paddle.static.default_main_program()
with paddle.static.program_guard(main_prog, startup_prog):
image = paddle.static.data(name="img", shape=[64, 784])
w = paddle.create_parameter(shape=[784, 200], dtype='float32')
b = paddle.create_parameter(shape=[200], dtype='float32')
hidden_w = paddle.matmul(x=image, y=w)
hidden_b = paddle.add(hidden_w, b)
exe = paddle.static.Executor(paddle.CPUPlace())
exe.run(startup_prog)
3.2.1 静态图推理模型&参数保存¶
静态图导出推理模型需要指定导出路径、输入、输出变量以及执行器。 save_inference_model
会裁剪Program的冗余部分,并导出两个文件: path_prefix.pdmodel
、 path_prefix.pdiparams
。示例如下(接前述示例):
# Save the inference model
path_prefix = "./infer_model"
paddle.static.save_inference_model(path_prefix, [image], [hidden_b], exe)
3.2.2 静态图推理模型&参数载入¶
载入静态图推理模型时,输入给 load_inference_model
的路径必须与 save_inference_model
的一致。示例如下(接前述示例):
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(path_prefix, exe))
tensor_img = np.array(np.random.random((64, 784)), dtype=np.float32)
results = exe.run(inference_program,
feed={feed_target_names[0]: tensor_img},
fetch_list=fetch_targets)
四、旧保存格式兼容载入¶
如果你是从飞桨框架1.x切换到2.1,曾经使用飞桨框架1.x的fluid相关接口保存模型或者参数,飞桨框架2.1也对这种情况进行了兼容性支持,包括以下几种情况。
飞桨1.x模型准备及训练示例,该示例为后续所有示例的前序逻辑:
import numpy as np
import paddle
import paddle.fluid as fluid
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
# enable static mode
paddle.enable_static()
# 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, (1, )).astype('int64')
return image, label
def __len__(self):
return self.num_samples
image = fluid.data(name='image', shape=[None, 784], dtype='float32')
label = fluid.data(name='label', shape=[None, 1], dtype='int64')
pred = fluid.layers.fc(input=image, size=10, act='softmax')
loss = fluid.layers.cross_entropy(input=pred, label=label)
avg_loss = fluid.layers.mean(loss)
optimizer = fluid.optimizer.SGD(learning_rate=0.001)
optimizer.minimize(avg_loss)
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
# create data loader
dataset = RandomDataset(BATCH_NUM * BATCH_SIZE)
loader = paddle.io.DataLoader(dataset,
feed_list=[image, label],
places=place,
batch_size=BATCH_SIZE,
shuffle=True,
drop_last=True,
num_workers=2)
# train model
for data in loader():
exe.run(
fluid.default_main_program(),
feed=data,
fetch_list=[avg_loss])
4.1 从 paddle.fluid.io.save_inference_model
保存结果中载入模型&参数¶
同时载入模型和参数
使用 paddle.jit.load
配合 **configs
载入模型和参数。
如果你是按照 paddle.fluid.io.save_inference_model
的默认格式存储的,可以按照如下方式载入(接前述示例):
# save default
model_path = "fc.example.model"
fluid.io.save_inference_model(
model_path, ["image"], [pred], exe)
# enable dynamic mode
paddle.disable_static(place)
# load
fc = paddle.jit.load(model_path)
# inference
fc.eval()
x = paddle.randn([1, IMAGE_SIZE], 'float32')
pred = fc(x)
如果你指定了存储的模型文件名,可以按照以下方式载入(接前述示例):
# save with model_filename
model_path = "fc.example.model.with_model_filename"
fluid.io.save_inference_model(
model_path, ["image"], [pred], exe, model_filename="__simplenet__")
# enable dynamic mode
paddle.disable_static(place)
# load
fc = paddle.jit.load(model_path, model_filename="__simplenet__")
# inference
fc.eval()
x = paddle.randn([1, IMAGE_SIZE], 'float32')
pred = fc(x)
如果你指定了存储的参数文件名,可以按照以下方式载入(接前述示例):
# save with params_filename
model_path = "fc.example.model.with_params_filename"
fluid.io.save_inference_model(
model_path, ["image"], [pred], exe, params_filename="__params__")
# enable dynamic mode
paddle.disable_static(place)
# load
fc = paddle.jit.load(model_path, params_filename="__params__")
# inference
fc.eval()
x = paddle.randn([1, IMAGE_SIZE], 'float32')
pred = fc(x)
仅载入参数
如果你仅需要从 paddle.fluid.io.save_inference_model
的存储结果中载入参数,以state_dict的形式配置到已有代码的模型中,可以使用 paddle.load
配合 **configs
载入。
如果你是按照 paddle.fluid.io.save_inference_model
的默认格式存储的,可以按照如下方式载入(接前述示例):
model_path = "fc.example.model"
load_param_dict = paddle.load(model_path)
如果你指定了存储的模型文件名,可以按照以下方式载入(接前述示例):
model_path = "fc.example.model.with_model_filename"
load_param_dict = paddle.load(model_path, model_filename="__simplenet__")
如果你指定了存储的参数文件名,可以按照以下方式载入(接前述示例):
model_path = "fc.example.model.with_params_filename"
load_param_dict = paddle.load(model_path, params_filename="__params__")
注解
一般预测模型不会存储优化器Optimizer的参数,因此此处载入的仅包括模型本身的参数。
注解
由于 structured_name
是动态图下独有的变量命名方式,因此从静态图存储结果载入的state_dict在配置到动态图的Layer中时,需要配置 Layer.set_state_dict(use_structured_name=False)
。
4.2 从 paddle.fluid.save
存储结果中载入参数¶
paddle.fluid.save
的存储格式与2.x动态图接口paddle.save
存储格式是类似的,同样存储了dict格式的参数,因此可以直接使用paddle.load
载入state_dict,但需要注意不能仅传入保存的路径,而要传入保存参数的文件名,示例如下(接前述示例):
# save by fluid.save
model_path = "fc.example.model.save"
program = fluid.default_main_program()
fluid.save(program, model_path)
# enable dynamic mode
paddle.disable_static(place)
load_param_dict = paddle.load("fc.example.model.save.pdparams")
注解
由于 paddle.fluid.save
接口原先在静态图模式下的定位是存储训练时参数,或者说存储Checkpoint,故尽管其同时存储了模型结构,目前也暂不支持从 paddle.fluid.save
的存储结果中同时载入模型和参数,后续如有需求再考虑支持。
4.3 从 paddle.fluid.io.save_params/save_persistables
保存结果中载入参数¶
这两个接口在飞桨1.x版本时,已经不再推荐作为存储模型参数的接口使用,故并未继承至飞桨2.x,之后也不会再推荐使用这两个接口存储参数。
对于使用这两个接口存储参数兼容载入的支持,分为两种情况,下面以 paddle.fluid.io.save_params
接口为例介绍相关使用方法:
使用默认方式存储,各参数分散存储为单独的文件,文件名为参数名
这种存储方式仍然可以使用 paddle.load
接口兼容载入,使用示例如下(接前述示例):
# save by fluid.io.save_params
model_path = "fc.example.model.save_params"
fluid.io.save_params(exe, model_path)
# load
state_dict = paddle.load(model_path)
print(state_dict)
指定了参数存储的文件,将所有参数存储至单个文件中
将所有参数存储至单个文件中会导致存储结果中丢失Tensor名和Tensor数据之间的映射关系,因此这部分丢失的信息需要用户传入进行补足。为了确保正确性,这里不仅要传入Tensor的name列表,同时要传入Tensor的shape和dtype等描述信息,通过检查和存储数据的匹配性确保严格的正确性,这导致载入数据的恢复过程变得比较复杂,仍然需要一些飞桨1.x的概念支持。后续如果此项需求较为普遍,飞桨将会考虑将该项功能兼容支持到 paddle.load
中,但由于信息丢失而导致的使用复杂性仍然是存在的,因此建议你避免仅使用这两个接口存储参数。
目前暂时推荐你使用 paddle.static.load_program_state
接口解决此处的载入问题,需要获取原Program中的参数列表传入该方法,使用示例如下(接前述示例):
# save by fluid.io.save_params
model_path = "fc.example.model.save_params_with_filename"
fluid.io.save_params(exe, model_path, filename="__params__")
# load
import os
params_file_path = os.path.join(model_path, "__params__")
var_list = fluid.default_main_program().all_parameters()
state_dict = paddle.io.load_program_state(params_file_path, var_list)