chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,946 @@
|
||||
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import errno
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
import paddle
|
||||
from paddle import pir
|
||||
from paddle.autograd.backward_utils import (
|
||||
ValueSet,
|
||||
get_real_op_inputs,
|
||||
get_real_op_outputs,
|
||||
some_in_set,
|
||||
)
|
||||
from paddle.base import (
|
||||
core,
|
||||
default_main_program,
|
||||
)
|
||||
from paddle.base.executor import Executor, global_scope
|
||||
from paddle.base.framework import (
|
||||
dygraph_not_support,
|
||||
static_only,
|
||||
)
|
||||
from paddle.base.log_helper import get_logger
|
||||
from paddle.framework.io_utils import (
|
||||
_pack_loaded_dict,
|
||||
_pickle_loads_mac,
|
||||
_unpack_saved_dict,
|
||||
)
|
||||
|
||||
from .io_utils import (
|
||||
_check_args,
|
||||
_check_vars,
|
||||
_get_valid_program,
|
||||
_normalize_path_prefix,
|
||||
_safe_load_pickle,
|
||||
)
|
||||
|
||||
_logger = get_logger(
|
||||
__name__, logging.INFO, fmt='%(asctime)s-%(levelname)s: %(message)s'
|
||||
)
|
||||
|
||||
|
||||
def get_pir_parameters(program):
|
||||
"""
|
||||
Get parameters and optimizer variables from program.
|
||||
Args:
|
||||
program(Program): The program to get parameters and optimizer variables.
|
||||
"""
|
||||
params = []
|
||||
opts = []
|
||||
for var in program.list_vars():
|
||||
if (
|
||||
var.is_parameter
|
||||
or var.get_defining_op().name() == "builtin.parameter"
|
||||
):
|
||||
params.append(var)
|
||||
elif var.persistable and var.get_defining_op().name() == "pd_op.data":
|
||||
opts.append(var)
|
||||
return params, opts
|
||||
|
||||
|
||||
def get_pir_feed_and_fetch(program):
|
||||
feed_name_list = []
|
||||
fetch_targets = []
|
||||
for op in program.global_block().ops:
|
||||
if op.name() == "pd_op.data" or op.name() == "pd_op.feed":
|
||||
feed_name_list.append(op.attrs()["name"])
|
||||
if op.name() == "pd_op.fetch":
|
||||
fetch_targets.extend(op.operands_source())
|
||||
return feed_name_list, fetch_targets
|
||||
|
||||
|
||||
def set_var(name, ndarray):
|
||||
t = global_scope().find_var(name).get_tensor()
|
||||
p = t._place()
|
||||
if p.is_cpu_place():
|
||||
place = paddle.base.CPUPlace()
|
||||
elif p.is_cuda_pinned_place():
|
||||
place = paddle.base.CUDAPinnedPlace()
|
||||
elif p.is_xpu_place():
|
||||
p = paddle.base.core.Place()
|
||||
p.set_place(t._place())
|
||||
place = paddle.base.XPUPlace(p.xpu_device_id())
|
||||
elif p.is_custom_place():
|
||||
p = paddle.base.core.Place()
|
||||
p.set_place(t._place())
|
||||
place = paddle.base.CustomPlace(
|
||||
paddle.device.get_device().split(':')[0], p.custom_device_id()
|
||||
)
|
||||
else:
|
||||
p = paddle.base.core.Place()
|
||||
p.set_place(t._place())
|
||||
place = paddle.base.CUDAPlace(p.gpu_device_id())
|
||||
|
||||
t.set(ndarray, place)
|
||||
|
||||
|
||||
def append_pir_feed_ops(program, feed_vars):
|
||||
"""
|
||||
Append feed ops to the program.
|
||||
Args:
|
||||
program(Program): Specify a program you want to append fetch op.
|
||||
feed_vars(Value | list[Value]): Values should be feed.
|
||||
Returns:
|
||||
modify program
|
||||
"""
|
||||
for i, var in enumerate(feed_vars):
|
||||
orig_op = var.get_defining_op()
|
||||
if orig_op.name() != 'pd_op.feed' and orig_op.name() != 'pd_op.data':
|
||||
value = paddle._pir_ops.data(
|
||||
"feed_name_" + str(i),
|
||||
var.shape,
|
||||
var.dtype,
|
||||
paddle.base.core.Place(),
|
||||
)
|
||||
var.replace_all_uses_with(value)
|
||||
value.get_defining_op().move_before(orig_op)
|
||||
|
||||
for i, var in enumerate(feed_vars):
|
||||
orig_op = var.get_defining_op()
|
||||
if orig_op.name() != 'pd_op.feed' and orig_op.name() != 'pd_op.data':
|
||||
orig_op.get_parent_block().remove_op(orig_op)
|
||||
|
||||
|
||||
def append_pir_fetch_ops(program, fetch_name_var_maps):
|
||||
"""
|
||||
Append fetch ops to the program.
|
||||
Args:
|
||||
program(Program): Specify a program you want to append fetch op.
|
||||
fetch_vars(Tensor | list[Tensor]): Values returned by inference.
|
||||
Returns:
|
||||
modify program
|
||||
"""
|
||||
for i, (var, name) in enumerate(fetch_name_var_maps):
|
||||
out = paddle._pir_ops.fetch(var, name, i)
|
||||
out.persistable = True
|
||||
|
||||
|
||||
def pir_prune_with_input(program, feed_vars, target_vars):
|
||||
"""
|
||||
Prune a program according to feed_vars and target_vars.
|
||||
Args:
|
||||
program(Program): Specify a program you want to prune.
|
||||
feed_vars(Tensor | list[Tensor]): Values needed by inference.
|
||||
target_vars(Tensor | list[Tensor]): Values returned by inference.
|
||||
Returns
|
||||
modify program
|
||||
"""
|
||||
if not isinstance(program, paddle.static.Program):
|
||||
raise TypeError(
|
||||
f"program type must be `paddle.static.Program`, but received `{type(program)}`"
|
||||
)
|
||||
|
||||
total_ops = program.global_block().ops
|
||||
intersection_op_flags = [True] * len(total_ops)
|
||||
|
||||
# from output to input
|
||||
target_vars_ = ValueSet(target_vars)
|
||||
for i, op in reversed(list(enumerate(total_ops))):
|
||||
if some_in_set(get_real_op_outputs(op), target_vars_):
|
||||
for operand in get_real_op_inputs(op):
|
||||
target_vars_.add(operand)
|
||||
else:
|
||||
intersection_op_flags[i] = False
|
||||
|
||||
for i, op in reversed(list(enumerate(total_ops))):
|
||||
if not intersection_op_flags[i]:
|
||||
if some_in_set(get_real_op_outputs(op), ValueSet(feed_vars)):
|
||||
raise ValueError(
|
||||
f"The feed_var create by: '{op.name()}' is not involved in the target_vars calculation"
|
||||
f"Please remove it from feed_vars ."
|
||||
)
|
||||
program.global_block().remove_op(op)
|
||||
|
||||
|
||||
def _inference_optimize(program, prune_read_op=True):
|
||||
"""
|
||||
This method will create a new program and do following adjustments on it:
|
||||
1. Remove all reader variables and their creator ops if exist.
|
||||
|
||||
2. Remove the :code:`read_op` if exists.
|
||||
|
||||
3. change the :code:`is_test`
|
||||
attribute of operators to :code:`True`. All the :code:`Parameter`
|
||||
information will be lost.
|
||||
|
||||
Args:
|
||||
prune_read_op(bool): remove the read ops that are added by py_reader
|
||||
for cpp inference library
|
||||
|
||||
Notes: This API is a very low level API. Use
|
||||
:code:`Program.clone(for_test=True)` instead.
|
||||
|
||||
Returns:
|
||||
Program: The new program.
|
||||
"""
|
||||
|
||||
# remove all readers and the read_op if exist
|
||||
if prune_read_op:
|
||||
pass
|
||||
|
||||
# change all `is_test` attributes to True
|
||||
for block in program.blocks:
|
||||
for op in block.ops:
|
||||
if op.has_attr("is_test"):
|
||||
op.set_bool_attr("is_test", True)
|
||||
if op.name() == "pd_op.batch_norm":
|
||||
# Remove the output ReserveSpace of batch_norm if exists.
|
||||
pass
|
||||
|
||||
|
||||
def normalize_pir_program(program, feed_vars, fetch_vars, **kwargs):
|
||||
"""
|
||||
|
||||
Normalize/Optimize a program according to feed_vars and fetch_vars.
|
||||
|
||||
Args:
|
||||
program(Program): Specify a program you want to optimize.
|
||||
feed_vars(Tensor | list[Tensor]): Values needed by inference.
|
||||
fetch_vars(Tensor | list[Tensor]): Values returned by inference.
|
||||
kwargs: Supported keys including ``skip_prune_program``.
|
||||
- skip_prune_program(bool): whether to skip pruning program. Defaults to False.
|
||||
|
||||
Returns:
|
||||
Program: Normalized/Optimized program.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> paddle.enable_static()
|
||||
|
||||
>>> path_prefix = "./infer_model"
|
||||
|
||||
# User defined network, here a softmax regression example
|
||||
>>> image = paddle.static.data(name='img', shape=[None, 28, 28], dtype='float32')
|
||||
>>> label = paddle.static.data(name='label', shape=[None, 1], dtype='int64')
|
||||
>>> predict = paddle.static.nn.fc(image, 10, activation='softmax')
|
||||
|
||||
>>> loss = paddle.nn.functional.cross_entropy(predict, label)
|
||||
|
||||
>>> exe = paddle.static.Executor(paddle.CPUPlace())
|
||||
>>> exe.run(paddle.static.default_startup_program())
|
||||
|
||||
# normalize main program.
|
||||
>>> program = paddle.static.default_main_program()
|
||||
>>> normalized_program = paddle.static.normalize_program(program, [image], [predict])
|
||||
|
||||
"""
|
||||
if not isinstance(program, paddle.static.Program):
|
||||
raise TypeError(
|
||||
f"program type must be `paddle.static.Program`, but received `{type(program)}`"
|
||||
)
|
||||
if not isinstance(feed_vars, list):
|
||||
feed_vars = [feed_vars]
|
||||
if not all(isinstance(v, pir.Value) for v in feed_vars):
|
||||
raise TypeError("feed_vars type must be a Value or a list of Value.")
|
||||
if not isinstance(fetch_vars, list):
|
||||
fetch_vars = [fetch_vars]
|
||||
if not all(isinstance(v, pir.Value) for v in fetch_vars):
|
||||
raise TypeError("fetch_vars type must be a Value or a list of Value.")
|
||||
|
||||
if len(program.global_block().ops) == 0:
|
||||
raise ValueError(
|
||||
"program must not be empty. at least one operator is required!"
|
||||
)
|
||||
|
||||
# remind users to set auc_states to 0 if auc op were found.
|
||||
for op in program.global_block().ops:
|
||||
if op.name() == 'pd_op.auc':
|
||||
warnings.warn(
|
||||
"Be sure that you have set auc states to 0 before saving inference model."
|
||||
)
|
||||
break
|
||||
|
||||
# serialize program
|
||||
value_map = paddle.pir.IrMapping()
|
||||
copy_program = program.clone(value_map)
|
||||
global_block = copy_program.global_block()
|
||||
clone_feed_vars = [value_map.look_up(v) for v in feed_vars]
|
||||
clone_fetch_vars = [value_map.look_up(v) for v in fetch_vars]
|
||||
|
||||
for op in global_block.ops:
|
||||
# can not delete feed op because it's output used by other op.
|
||||
if op.name() == "pd_op.fetch":
|
||||
global_block.remove_op(op)
|
||||
|
||||
skip_prune_program = kwargs.get('skip_prune_program', False)
|
||||
# if feed var is not connect with target_vars, it will be delete.
|
||||
if not skip_prune_program:
|
||||
pir_prune_with_input(copy_program, clone_feed_vars, clone_fetch_vars)
|
||||
_inference_optimize(copy_program, prune_read_op=True)
|
||||
|
||||
fetch_vars_tuple = []
|
||||
for i, var in enumerate(clone_fetch_vars):
|
||||
scale_op = var.get_defining_op()
|
||||
orig_var = var
|
||||
if scale_op.name() == "pd_op.scale":
|
||||
full_op = scale_op.operand_source(1).get_defining_op()
|
||||
if full_op.has_attr("value") and full_op.attrs()['value'] == 1.0:
|
||||
orig_var = scale_op.operand_source(0)
|
||||
if orig_var.has_name:
|
||||
fetch_vars_tuple.append((orig_var, orig_var.name))
|
||||
else:
|
||||
fetch_vars_tuple.append((var, "fetch_name_" + str(i)))
|
||||
with paddle.static.program_guard(copy_program):
|
||||
append_pir_feed_ops(copy_program, clone_feed_vars)
|
||||
append_pir_fetch_ops(copy_program, fetch_vars_tuple)
|
||||
|
||||
return copy_program
|
||||
|
||||
|
||||
@dygraph_not_support
|
||||
def save_vars_pir(
|
||||
dirname,
|
||||
main_program=None,
|
||||
vars=None,
|
||||
filename=None,
|
||||
):
|
||||
"""
|
||||
Save specific variables in the `Program` to files.
|
||||
|
||||
There are two ways to specify the variables to be saved: set variables in
|
||||
a list and assign it to the `vars`, or use the `predicate` function to select
|
||||
variables that make `predicate(variable) == True`. The first way has a higher priority.
|
||||
|
||||
The `dirname` is used to specify the folder where to save variables.
|
||||
If you prefer to save variables in separate files in the `dirname` folder,
|
||||
do not set `filename`. If you prefer to save all variables in a single file,
|
||||
use `filename` to specify it.
|
||||
|
||||
Args:
|
||||
dirname(str, optional): The folder to save variables.
|
||||
When you need to save the parameter to the memory, set it to None.
|
||||
main_program(Program, optional): The program whose variables will be saved.
|
||||
If it is None, the default main program will
|
||||
be used automatically.
|
||||
Default: None
|
||||
vars(list[Variable], optional): The list contains all variables to be saved.
|
||||
Default: None
|
||||
filename(str, optional): If you prefer to save all variables in a single file,
|
||||
use `filename` to specify it. Otherwise, let `filename` be None.
|
||||
Default: None
|
||||
|
||||
Returns:
|
||||
str: When saving parameters to a file, returns None.
|
||||
When saving parameters to memory, returns a binary string containing parameters.
|
||||
"""
|
||||
|
||||
save_to_memory = False
|
||||
if dirname is None and filename is None:
|
||||
save_to_memory = True
|
||||
|
||||
main_program = _get_valid_program(main_program)
|
||||
|
||||
if vars is None:
|
||||
param, opt = get_pir_parameters(main_program)
|
||||
vars_list = param + opt
|
||||
return save_vars_pir(
|
||||
main_program=main_program,
|
||||
dirname=dirname,
|
||||
vars=[var for var in vars_list if var.persistable],
|
||||
filename=filename,
|
||||
)
|
||||
else:
|
||||
params_var_name = "saved_params"
|
||||
# give warning when there is no var in model
|
||||
if len(list(vars)) == 0:
|
||||
warnings.warn(
|
||||
"no variable in your model, please ensure there are any variables in your model to save"
|
||||
)
|
||||
return None
|
||||
|
||||
save_var_map = {}
|
||||
for v in vars:
|
||||
var = global_scope().find_var(v.name)
|
||||
# TODO(chenzhiyang): deal with RAW type and sparse
|
||||
if filename is None and save_to_memory is False:
|
||||
save_file_path = os.path.join(os.path.normpath(dirname), v.name)
|
||||
core.save_func(
|
||||
var.get_tensor(), v.name, save_file_path, True, False
|
||||
)
|
||||
else:
|
||||
save_var_map[v.name] = var.get_tensor()
|
||||
|
||||
if filename is not None or save_to_memory:
|
||||
save_var_list = []
|
||||
save_var_names = []
|
||||
for name in sorted(save_var_map.keys()):
|
||||
save_var_list.append(save_var_map[name])
|
||||
save_var_names.append(name)
|
||||
|
||||
save_path = ''
|
||||
if save_to_memory is False:
|
||||
save_path = os.path.join(os.path.normpath(dirname), filename)
|
||||
core.save_combine_func(
|
||||
save_var_list,
|
||||
save_var_names,
|
||||
save_path,
|
||||
True,
|
||||
False,
|
||||
save_to_memory,
|
||||
)
|
||||
|
||||
if save_to_memory:
|
||||
return global_scope().find_var(params_var_name).get_bytes()
|
||||
|
||||
|
||||
def load_vars_pir(
|
||||
executor,
|
||||
dirname,
|
||||
main_program=None,
|
||||
vars=None,
|
||||
filename=None,
|
||||
):
|
||||
"""
|
||||
:api_attr: PIR Static Graph
|
||||
|
||||
This API loads variables from files by C++ function.
|
||||
|
||||
There are two ways to specify the variables to be loaded: the first way, set
|
||||
variables in a list and assign it to the `vars`; the second way, use the
|
||||
`predicate` function to select variables that make `predicate(variable) == True`.
|
||||
The first way has a higher priority.
|
||||
|
||||
The `dirname` is used to specify the folder where to load variables.
|
||||
If variables were saved in separate files in the folder `dirname`,
|
||||
set `filename` None. If all variables were saved in a single file,
|
||||
use `filename` to specify it.
|
||||
|
||||
Args:
|
||||
executor(Executor): The executor to create variables in scope.
|
||||
dirname(str): The folder where to load the variables.
|
||||
main_program(Program, optional): The program whose variables will be loaded.
|
||||
If it is None, the default main program will
|
||||
be used automatically.
|
||||
Default: None
|
||||
vars(list[Variable], optional): The list that contains all variables to be loaded.
|
||||
Default: None
|
||||
filename(str, optional): The file which saved all required variables. If variables
|
||||
were saved in separate files, set it to be None.
|
||||
Default: None
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
assert executor is None or isinstance(executor, Executor)
|
||||
|
||||
vars_from_memory = False
|
||||
if dirname is not None:
|
||||
dirname = os.path.normpath(dirname)
|
||||
# TODO(chenzhiyang): vars_from_memory
|
||||
|
||||
if filename == '':
|
||||
filename = None
|
||||
|
||||
if vars is None:
|
||||
if main_program is None:
|
||||
main_program = default_main_program()
|
||||
|
||||
if not isinstance(main_program, paddle.static.Program):
|
||||
raise TypeError(
|
||||
f"The type of input main_program is invalid, expected type is paddle.static.Program, but received {type(main_program)}"
|
||||
)
|
||||
param, opt = get_pir_parameters(main_program)
|
||||
vars = param + opt
|
||||
paddle.base.libpaddle.pir.create_loaded_parameter(
|
||||
vars, global_scope(), executor._default_executor
|
||||
)
|
||||
load_vars_pir(
|
||||
executor,
|
||||
dirname=dirname,
|
||||
main_program=main_program,
|
||||
vars=[var for var in vars if var.persistable],
|
||||
filename=filename,
|
||||
)
|
||||
else:
|
||||
if main_program is None:
|
||||
main_program = default_main_program()
|
||||
|
||||
if not isinstance(main_program, paddle.static.Program):
|
||||
raise TypeError(
|
||||
f"The type of input main_program is invalid, expected type is paddle.static.Program, but received {type(main_program)}"
|
||||
)
|
||||
|
||||
# TODO(chenzhiyang):save origin param shape, check vars
|
||||
load_var_map = {}
|
||||
|
||||
for v in vars:
|
||||
var = global_scope().find_var(v.name)
|
||||
assert isinstance(var, paddle.base.libpaddle.Variable)
|
||||
if filename is None:
|
||||
if dirname is None:
|
||||
raise ValueError(
|
||||
"The directory path and params cannot be None at the same time."
|
||||
)
|
||||
file_path = os.path.join(dirname, v.name)
|
||||
core.load_func(
|
||||
file_path,
|
||||
-1,
|
||||
[],
|
||||
False,
|
||||
var.get_tensor(),
|
||||
executor._default_executor.get_place(),
|
||||
)
|
||||
else:
|
||||
load_var_map[v.name] = var
|
||||
|
||||
if filename is not None:
|
||||
load_var_list = []
|
||||
load_var_names = []
|
||||
for name in sorted(load_var_map.keys()):
|
||||
load_var_list.append(load_var_map[name].get_tensor())
|
||||
load_var_names.append(name)
|
||||
|
||||
if vars_from_memory is False:
|
||||
filename = os.path.join(dirname, filename)
|
||||
|
||||
core.load_combine_func(
|
||||
filename,
|
||||
load_var_names,
|
||||
load_var_list,
|
||||
False,
|
||||
executor._default_executor.get_place(),
|
||||
)
|
||||
for name, var in zip(load_var_names, load_var_list):
|
||||
set_var(name, np.array(var))
|
||||
|
||||
|
||||
@static_only
|
||||
def save_pir(program, model_path, protocol=4, **configs):
|
||||
"""
|
||||
This function saves parameters, optimizer information and network description to model_path.
|
||||
|
||||
The parameters contain all the trainable Tensor, and save to a file with suffix ".pdparams".
|
||||
The optimizer information contains all the Tensor used by optimizer. For Adam optimizer, contains beta1, beta2, momentum etc. All the information will be saved to a file with suffix ".pdopt". (If the optimizer has no Tensor to save (like SGD), the file will not be generated).
|
||||
The network description is the description of the program. It's only used for deployment. The description will be saved to a file with a suffix ".pdmodel".
|
||||
|
||||
Args:
|
||||
program(Program) : The program to be saved.
|
||||
model_path(str): The file prefix to save the program. The format is "dirname/file_prefix". If file_prefix is an empty str, an exception will be raised.
|
||||
protocol(int, optional): The protocol version of pickle module must be greater than 1 and less than 5.
|
||||
Default: 4
|
||||
configs(dict, optional) : Optional keyword arguments.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
base_name = os.path.basename(model_path)
|
||||
assert base_name != "", (
|
||||
"The input model_path MUST be format of dirname/filename [dirname\\filename in Windows system], but received model_path is empty string."
|
||||
)
|
||||
if 'pickle_protocol' in configs:
|
||||
protocol = configs['pickle_protocol']
|
||||
warnings.warn(
|
||||
"'pickle_protocol' is a deprecated argument. Please use 'protocol' instead."
|
||||
)
|
||||
|
||||
if not isinstance(protocol, int):
|
||||
raise ValueError(
|
||||
f"The 'protocol' MUST be `int`, but received {type(protocol)}"
|
||||
)
|
||||
|
||||
if protocol < 2 or protocol > 4:
|
||||
raise ValueError(
|
||||
f"Expected 1<'protocol'<5, but received protocol={protocol}"
|
||||
)
|
||||
|
||||
dir_name = os.path.dirname(model_path)
|
||||
if dir_name and not os.path.exists(dir_name):
|
||||
os.makedirs(dir_name)
|
||||
|
||||
def get_tensor(var):
|
||||
t = global_scope().find_var(var.name).get_tensor()
|
||||
return np.array(t)
|
||||
|
||||
# get parameters and optimizer variables
|
||||
parameter_list, optimizer_param_list = get_pir_parameters(program)
|
||||
param_dict = {
|
||||
var.name: get_tensor(var) for var in parameter_list if var.persistable
|
||||
}
|
||||
opt_dict = {
|
||||
var.name: get_tensor(var)
|
||||
for var in optimizer_param_list
|
||||
if var.persistable
|
||||
}
|
||||
|
||||
# save parameters
|
||||
param_dict = _unpack_saved_dict(param_dict, protocol)
|
||||
|
||||
# When value of dict is lager than 4GB ,there is a Bug on 'MAC python3'
|
||||
if sys.platform == 'darwin' and sys.version_info.major == 3:
|
||||
pickle_bytes = pickle.dumps(param_dict, protocol=protocol)
|
||||
with open(model_path + ".pdparams", 'wb') as f:
|
||||
max_bytes = 2**30
|
||||
f.writelines(
|
||||
pickle_bytes[i : i + max_bytes]
|
||||
for i in range(0, len(pickle_bytes), max_bytes)
|
||||
)
|
||||
else:
|
||||
with open(model_path + ".pdparams", 'wb') as f:
|
||||
pickle.dump(param_dict, f, protocol=protocol)
|
||||
|
||||
# save optimizer parameters
|
||||
with open(model_path + ".pdopt", 'wb') as f:
|
||||
pickle.dump(opt_dict, f, protocol=protocol)
|
||||
|
||||
# save program
|
||||
paddle.core.serialize_pir_program(program, model_path + ".json")
|
||||
|
||||
|
||||
@static_only
|
||||
def load_pir(program, model_prefix, executor=None, var_list=None):
|
||||
"""
|
||||
:api_attr: PIR Static Graph
|
||||
|
||||
This function gets parameters and optimizer information from program, and then gets corresponding value from file.
|
||||
An exception will be thrown if shape or dtype of the parameters does not match.
|
||||
|
||||
This function can also load model file saved with [ save_params, save_persistables, save_vars ].
|
||||
var_list can not be None when loading a single model file
|
||||
( filename is not None when save_params, save_persistables or save_vars is called ).
|
||||
|
||||
Args:
|
||||
program(Program): The program to be loaded
|
||||
model_prefix(str): The file prefix to store the program
|
||||
executor(Executor, optional): The executor used for initializing the parameter
|
||||
when startup program is not run.
|
||||
var_list(list|tuple, optional): The Tensor list/tuple to load a single model file saved with
|
||||
[ save_params, save_persistables, save_vars ].
|
||||
Default: None
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
assert executor is None or isinstance(executor, Executor)
|
||||
|
||||
parameter_file_name = model_prefix + ".pdparams"
|
||||
|
||||
# TODO(chenzhiyang):if not os.path.exists(parameter_file_name): load_vars
|
||||
|
||||
parameter_list, optimizer_param_list = get_pir_parameters(program)
|
||||
|
||||
with open(parameter_file_name, 'rb') as f:
|
||||
# When value of dict is lager than 4GB ,there is a Bug on 'MAC python3'
|
||||
if sys.platform == 'darwin' and sys.version_info.major == 3:
|
||||
load_dict = _pickle_loads_mac(parameter_file_name, f)
|
||||
else:
|
||||
load_dict = _safe_load_pickle(f, encoding='latin1')
|
||||
load_dict = _pack_loaded_dict(load_dict)
|
||||
for var in parameter_list:
|
||||
if var.persistable:
|
||||
assert var.name in load_dict, (
|
||||
f"Can not find [{var.name}] in model file [{parameter_file_name}]"
|
||||
)
|
||||
set_var(var.name, load_dict[var.name])
|
||||
|
||||
if len(optimizer_param_list) > 0:
|
||||
opt_file_name = model_prefix + ".pdopt"
|
||||
assert os.path.exists(opt_file_name), (
|
||||
f"Optimizer file [{opt_file_name}] not exits"
|
||||
)
|
||||
|
||||
if executor:
|
||||
paddle.base.libpaddle.pir.create_loaded_parameter(
|
||||
optimizer_param_list, global_scope(), executor._default_executor
|
||||
)
|
||||
|
||||
with open(opt_file_name, 'rb') as f:
|
||||
load_dict = _safe_load_pickle(f, encoding='latin1')
|
||||
for var in optimizer_param_list:
|
||||
if var.persistable:
|
||||
assert var.name in load_dict, (
|
||||
f"Can not find [{var.name}] in model file [{opt_file_name}]"
|
||||
)
|
||||
set_var(var.name, load_dict[var.name])
|
||||
|
||||
|
||||
@static_only
|
||||
def save_inference_model_pir(
|
||||
path_prefix, feed_vars, fetch_vars, executor, **kwargs
|
||||
):
|
||||
"""
|
||||
Save current model and its parameters to given path. i.e.
|
||||
Given ``path_prefix = "PATH/modelname"``, after invoking
|
||||
``save_inference_model(path_prefix, feed_vars, fetch_vars, executor)``,
|
||||
you will find two files named ``modelname.pdmodel`` and ``modelname.pdiparams``
|
||||
under ``PATH``, which represent your model and parameters respectively.
|
||||
|
||||
Args:
|
||||
path_prefix(str): Directory path to save model + model name without suffix.
|
||||
feed_vars(Tensor | list[Tensor]): Variables needed by inference.
|
||||
fetch_vars(Tensor | list[Tensor]): Variables returned by inference.
|
||||
executor(Executor): The executor that saves the inference model. You can refer
|
||||
to :ref:`api_guide_executor_en` for more details.
|
||||
kwargs: Supported keys including 'program' and "clip_extra". Attention please, kwargs is used for backward compatibility mainly.
|
||||
|
||||
- program(Program): specify a program if you don't want to use default main program.
|
||||
|
||||
- clip_extra(bool): the flag indicating whether to clip extra information for every operator. Default: True.
|
||||
|
||||
- legacy_format(bool): whether to save inference model in legacy format. Default: False.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
# check path_prefix, set model_path and params_path
|
||||
path_prefix = _normalize_path_prefix(path_prefix)
|
||||
try:
|
||||
# mkdir may conflict if pserver and trainer are running on the same machine
|
||||
dirname = os.path.dirname(path_prefix)
|
||||
os.makedirs(dirname)
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise
|
||||
|
||||
model_path = path_prefix + ".json"
|
||||
params_path = path_prefix + ".pdiparams"
|
||||
if os.path.isdir(model_path):
|
||||
raise ValueError(f"'{model_path}' is an existing directory.")
|
||||
if os.path.isdir(params_path):
|
||||
raise ValueError(f"'{params_path}' is an existing directory.")
|
||||
|
||||
# verify feed_vars
|
||||
_check_vars('feed_vars', feed_vars)
|
||||
# verify fetch_vars
|
||||
_check_vars('fetch_vars', fetch_vars)
|
||||
|
||||
program = _get_valid_program(kwargs.get('program', None))
|
||||
# serialize and save program
|
||||
program = normalize_pir_program(
|
||||
program,
|
||||
feed_vars,
|
||||
fetch_vars,
|
||||
skip_prune_program=kwargs.get('skip_prune_program', False),
|
||||
)
|
||||
|
||||
readable = kwargs.get('readable', False)
|
||||
trainable = kwargs.get('trainable', True)
|
||||
paddle.core.serialize_pir_program(
|
||||
program,
|
||||
(
|
||||
os.path.join(os.path.dirname(model_path), "__model__.json")
|
||||
if kwargs.get('separate_parameters', False)
|
||||
else model_path
|
||||
),
|
||||
True,
|
||||
readable,
|
||||
trainable,
|
||||
)
|
||||
|
||||
# serialize and save params
|
||||
save_dirname = os.path.dirname(params_path)
|
||||
params_filename = os.path.basename(params_path)
|
||||
save_vars_pir(
|
||||
dirname=save_dirname,
|
||||
main_program=program,
|
||||
filename=(
|
||||
None
|
||||
if kwargs.get('separate_parameters', False)
|
||||
else params_filename
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@static_only
|
||||
def load_inference_model_pir(path_prefix, executor, **kwargs):
|
||||
"""
|
||||
|
||||
Load inference model from a given path. By this API, you can get the model
|
||||
structure(Inference Program) and model parameters.
|
||||
|
||||
Args:
|
||||
path_prefix(str | None): One of the following:
|
||||
- Directory path to save model + model name without suffix.
|
||||
- Set to None when reading the model from memory.
|
||||
executor(Executor): The executor to run for loading inference model.
|
||||
See :ref:`api_guide_executor_en` for more details about it.
|
||||
kwargs: Supported keys including 'model_filename', 'params_filename'. Attention please, kwargs is used for backward compatibility mainly.
|
||||
|
||||
- model_filename(str): specify model_filename if you don't want to use default name.
|
||||
|
||||
- params_filename(str): specify params_filename if you don't want to use default name.
|
||||
|
||||
Returns:
|
||||
list: The return of this API is a list with three elements:
|
||||
(program, feed_target_names, fetch_targets). The `program` is a
|
||||
``Program`` (refer to :ref:`api_guide_Program_en`), which is used for inference.
|
||||
The `feed_target_names` is a list of ``str``, which contains names of variables
|
||||
that need to feed data in the inference program. The `fetch_targets` is a list of
|
||||
``Variable`` (refer to :ref:`api_guide_Program_en`). It contains variables from which
|
||||
we can get inference results.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> 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)
|
||||
|
||||
# Save the inference model
|
||||
>>> path_prefix = "./infer_model"
|
||||
>>> paddle.static.save_inference_model(path_prefix, [image], [hidden_b], exe)
|
||||
|
||||
>>> [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,
|
||||
... )
|
||||
|
||||
# In this example, the inference program was saved in file
|
||||
# "./infer_model.pdmodel" and parameters were saved in file
|
||||
# " ./infer_model.pdiparams".
|
||||
# By the inference program, feed_target_names and
|
||||
# fetch_targets, we can use an executor to run the inference
|
||||
# program to get the inference result.
|
||||
"""
|
||||
# check kwargs
|
||||
supported_args = ('model_filename', 'params_filename')
|
||||
deprecated_args = ('pserver_endpoints',)
|
||||
caller = inspect.currentframe().f_code.co_name
|
||||
_check_args(caller, kwargs, supported_args, deprecated_args)
|
||||
|
||||
# load from memory
|
||||
if path_prefix is None:
|
||||
_logger.warning(
|
||||
"Load inference model from memory is deprecated. Please specify path_prefix."
|
||||
)
|
||||
model_filename = kwargs.get('model_filename', None)
|
||||
params_filename = kwargs.get('params_filename', None)
|
||||
if params_filename is None:
|
||||
raise ValueError(
|
||||
"params_filename cannot be None when path_prefix is None."
|
||||
)
|
||||
|
||||
# deserialize bytes to program
|
||||
program = paddle.static.Program()
|
||||
paddle.base.core.deserialize_pir_program(model_filename, program)
|
||||
|
||||
params, opts = get_pir_parameters(program)
|
||||
vars = params + opts
|
||||
vars = [var for var in vars if var.persistable]
|
||||
if len(vars) > 0:
|
||||
load_vars_pir(
|
||||
# load from memory, dirname is None
|
||||
executor,
|
||||
dirname=None,
|
||||
main_program=program,
|
||||
filename=params_filename,
|
||||
)
|
||||
# load from file
|
||||
else:
|
||||
# check and norm path_prefix
|
||||
path_prefix = _normalize_path_prefix(path_prefix)
|
||||
dir_path = os.path.dirname(path_prefix)
|
||||
if not os.path.isdir(dir_path):
|
||||
raise ValueError(f"There is no directory named {dir_path}")
|
||||
# set model_path and params_path in new way,
|
||||
# path_prefix represents a file path without suffix in this case.
|
||||
if not kwargs:
|
||||
model_path = path_prefix + ".json"
|
||||
params_path = path_prefix + ".pdiparams"
|
||||
# set model_path and params_path in old way for compatible,
|
||||
# path_prefix represents a directory path.
|
||||
else:
|
||||
model_filename = kwargs.get('model_filename', None)
|
||||
params_filename = kwargs.get('params_filename', None)
|
||||
# set model_path
|
||||
if model_filename is None:
|
||||
model_path = os.path.join(path_prefix, "__model__")
|
||||
else:
|
||||
model_path = os.path.join(path_prefix, model_filename + ".json")
|
||||
|
||||
if not os.path.exists(model_path):
|
||||
model_path = os.path.join(path_prefix, model_filename)
|
||||
# set params_path
|
||||
if params_filename is None:
|
||||
params_path = os.path.join(path_prefix, "")
|
||||
else:
|
||||
params_path = os.path.join(
|
||||
path_prefix, params_filename + ".pdiparams"
|
||||
)
|
||||
if not os.path.exists(params_path):
|
||||
params_path = os.path.join(path_prefix, params_filename)
|
||||
_logger.warning(
|
||||
"The old way to load inference model is deprecated. Please specify path_prefix."
|
||||
f" model path: {model_path}, params path: {params_path}"
|
||||
)
|
||||
|
||||
# deserialize bytes to program
|
||||
program = paddle.static.Program()
|
||||
paddle.base.core.deserialize_pir_program(model_path, program)
|
||||
# load parameters
|
||||
params, opts = get_pir_parameters(program)
|
||||
vars = params + opts
|
||||
vars = [var for var in vars if var.persistable]
|
||||
if len(vars) > 0:
|
||||
load_dirname = os.path.dirname(params_path)
|
||||
params_filename = os.path.basename(params_path)
|
||||
|
||||
load_vars_pir(
|
||||
executor,
|
||||
dirname=load_dirname,
|
||||
main_program=program,
|
||||
filename=params_filename,
|
||||
)
|
||||
|
||||
feed_names, fetch_targets = get_pir_feed_and_fetch(program)
|
||||
return [program, feed_names, fetch_targets]
|
||||
Reference in New Issue
Block a user