diff --git a/python/paddle/__init__.py b/python/paddle/__init__.py index 3c52bbdcccaf820dc7cf4fa30a718a2d6e0256b5..0af32da4e690b24719c8d96ddb084cf121cca88e 100755 --- a/python/paddle/__init__.py +++ b/python/paddle/__init__.py @@ -235,7 +235,6 @@ from .framework import grad #DEFINE_ALIAS from .framework import no_grad #DEFINE_ALIAS from .framework import save #DEFINE_ALIAS from .framework import load #DEFINE_ALIAS -from .framework import SaveLoadConfig #DEFINE_ALIAS from .framework import DataParallel #DEFINE_ALIAS from .framework import NoamDecay #DEFINE_ALIAS diff --git a/python/paddle/fluid/contrib/slim/tests/test_imperative_qat.py b/python/paddle/fluid/contrib/slim/tests/test_imperative_qat.py index df505cf2435e73d4c30f641451fb1225a21816c6..eb924e13a7e4fe6229b8dab7ebf10993023d4523 100644 --- a/python/paddle/fluid/contrib/slim/tests/test_imperative_qat.py +++ b/python/paddle/fluid/contrib/slim/tests/test_imperative_qat.py @@ -31,6 +31,7 @@ from paddle.fluid.dygraph.nn import Conv2D from paddle.fluid.dygraph.nn import Pool2D from paddle.fluid.dygraph.nn import Linear from paddle.fluid.log_helper import get_logger +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX paddle.enable_static() @@ -231,10 +232,11 @@ class TestImperativeQat(unittest.TestCase): before_save = lenet(test_img) # save inference quantized model - path = "./mnist_infer_model" + path = "./qat_infer_model/lenet" + save_dir = "./qat_infer_model" paddle.jit.save( layer=lenet, - model_path=path, + path=path, input_spec=[ paddle.static.InputSpec( shape=[None, 1, 28, 28], dtype='float32') @@ -245,12 +247,12 @@ class TestImperativeQat(unittest.TestCase): else: place = core.CPUPlace() exe = fluid.Executor(place) - [inference_program, feed_target_names, fetch_targets] = ( - fluid.io.load_inference_model( - dirname=path, - executor=exe, - model_filename="__model__", - params_filename="__variables__")) + [inference_program, feed_target_names, + fetch_targets] = fluid.io.load_inference_model( + dirname=save_dir, + executor=exe, + model_filename="lenet" + INFER_MODEL_SUFFIX, + params_filename="lenet" + INFER_PARAMS_SUFFIX) after_save, = exe.run(inference_program, feed={feed_target_names[0]: test_data}, fetch_list=fetch_targets) @@ -339,7 +341,7 @@ class TestImperativeQat(unittest.TestCase): paddle.jit.save( layer=lenet, - model_path="./dynamic_mnist", + path="./dynamic_mnist/model", input_spec=[ paddle.static.InputSpec( shape=[None, 1, 28, 28], dtype='float32') diff --git a/python/paddle/fluid/contrib/slim/tests/test_imperative_qat_channelwise.py b/python/paddle/fluid/contrib/slim/tests/test_imperative_qat_channelwise.py index 80d388ac0da6219bda8e485aabaaf7fea44f6cd0..ddf37a0ebf8c2dc1d0dc959a66da5e2a25bd06a4 100644 --- a/python/paddle/fluid/contrib/slim/tests/test_imperative_qat_channelwise.py +++ b/python/paddle/fluid/contrib/slim/tests/test_imperative_qat_channelwise.py @@ -31,6 +31,7 @@ from paddle.fluid.dygraph.nn import Conv2D from paddle.fluid.dygraph.nn import Pool2D from paddle.fluid.dygraph.nn import Linear from paddle.fluid.log_helper import get_logger +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX paddle.enable_static() @@ -231,10 +232,11 @@ class TestImperativeQat(unittest.TestCase): before_save = lenet(test_img) # save inference quantized model - path = "./mnist_infer_model" + path = "./qat_infer_model/mnist" + save_dir = "./qat_infer_model" paddle.jit.save( layer=lenet, - model_path=path, + path=path, input_spec=[ paddle.static.InputSpec( shape=[None, 1, 28, 28], dtype='float32') @@ -245,12 +247,12 @@ class TestImperativeQat(unittest.TestCase): else: place = core.CPUPlace() exe = fluid.Executor(place) - [inference_program, feed_target_names, fetch_targets] = ( - fluid.io.load_inference_model( - dirname=path, - executor=exe, - model_filename="__model__", - params_filename="__variables__")) + [inference_program, feed_target_names, + fetch_targets] = fluid.io.load_inference_model( + dirname=save_dir, + executor=exe, + model_filename="mnist" + INFER_MODEL_SUFFIX, + params_filename="mnist" + INFER_PARAMS_SUFFIX) after_save, = exe.run(inference_program, feed={feed_target_names[0]: test_data}, fetch_list=fetch_targets) @@ -339,7 +341,7 @@ class TestImperativeQat(unittest.TestCase): paddle.jit.save( layer=lenet, - model_path="./dynamic_mnist", + path="./dynamic_mnist/model", input_spec=[ paddle.static.InputSpec( shape=[None, 1, 28, 28], dtype='float32') diff --git a/python/paddle/fluid/dygraph/checkpoint.py b/python/paddle/fluid/dygraph/checkpoint.py index f4ea4d670e6006c86cd9ca8a15723e5a62e5c0d9..fb87ea4455d346c89949ef85a627ea4d124322af 100644 --- a/python/paddle/fluid/dygraph/checkpoint.py +++ b/python/paddle/fluid/dygraph/checkpoint.py @@ -24,8 +24,8 @@ from . import learning_rate_scheduler import warnings from .. import core from .base import guard -from paddle.fluid.dygraph.jit import SaveLoadConfig, deprecate_save_load_configs -from paddle.fluid.dygraph.io import _construct_program_holders, _construct_params_and_buffers, EXTRA_VAR_INFO_FILENAME +from paddle.fluid.dygraph.jit import _SaveLoadConfig +from paddle.fluid.dygraph.io import _construct_program_holders, _construct_params_and_buffers __all__ = [ 'save_dygraph', @@ -33,35 +33,23 @@ __all__ = [ ] -# NOTE(chenweihang): deprecate load_dygraph's argument keep_name_table, -# ensure compatibility when user still use keep_name_table argument -def deprecate_keep_name_table(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - def __warn_and_build_configs__(keep_name_table): - warnings.warn( - "The argument `keep_name_table` has deprecated, please use `SaveLoadConfig.keep_name_table`.", - DeprecationWarning) - config = SaveLoadConfig() - config.keep_name_table = keep_name_table - return config - - # deal with arg `keep_name_table` - if len(args) > 1 and isinstance(args[1], bool): - args = list(args) - args[1] = __warn_and_build_configs__(args[1]) - # deal with kwargs - elif 'keep_name_table' in kwargs: - kwargs['config'] = __warn_and_build_configs__(kwargs[ - 'keep_name_table']) - kwargs.pop('keep_name_table') - else: - # do nothing - pass +def _parse_load_config(configs): + supported_configs = ['model_filename', 'params_filename', 'keep_name_table'] + + # input check + for key in configs: + if key not in supported_configs: + raise ValueError( + "The additional config (%s) of `paddle.fluid.load_dygraph` is not supported." + % (key)) - return func(*args, **kwargs) + # construct inner config + inner_config = _SaveLoadConfig() + inner_config.model_filename = configs.get('model_filename', None) + inner_config.params_filename = configs.get('params_filename', None) + inner_config.keep_name_table = configs.get('keep_name_table', None) - return wrapper + return inner_config @dygraph_only @@ -132,12 +120,12 @@ def save_dygraph(state_dict, model_path): pickle.dump(model_dict, f, protocol=2) +# NOTE(chenweihang): load_dygraph will deprecated in future, we don't +# support new loading features for it # TODO(qingqing01): remove dygraph_only to support loading static model. # maybe need to unify the loading interface after 2.0 API is ready. # @dygraph_only -@deprecate_save_load_configs -@deprecate_keep_name_table -def load_dygraph(model_path, config=None): +def load_dygraph(model_path, **configs): ''' :api_attr: imperative @@ -152,10 +140,13 @@ def load_dygraph(model_path, config=None): Args: model_path(str) : The file prefix store the state_dict. (The path should Not contain suffix '.pdparams') - config (SaveLoadConfig, optional): :ref:`api_imperative_jit_saveLoadConfig` - object that specifies additional configuration options, these options - are for compatibility with ``jit.save/io.save_inference_model`` formats. - Default None. + **configs (dict, optional): other save configuration options for compatibility. We do not + recommend using these configurations, if not necessary, DO NOT use them. Default None. + The following options are currently supported: + (1) model_filename (string): The inference model file name of the paddle 1.x ``save_inference_model`` + save format. Default file name is :code:`__model__` . + (2) params_filename (string): The persistable variables file name of the paddle 1.x ``save_inference_model`` + save format. No default file name, save variables separately by default. Returns: state_dict(dict) : the dict store the state_dict @@ -196,8 +187,7 @@ def load_dygraph(model_path, config=None): opti_file_path = model_prefix + ".pdopt" # deal with argument `config` - if config is None: - config = SaveLoadConfig() + config = _parse_load_config(configs) if os.path.exists(params_file_path) or os.path.exists(opti_file_path): # Load state dict by `save_dygraph` save format @@ -246,7 +236,6 @@ def load_dygraph(model_path, config=None): persistable_var_dict = _construct_params_and_buffers( model_prefix, programs, - config.separate_params, config.params_filename, append_suffix=False) @@ -255,9 +244,9 @@ def load_dygraph(model_path, config=None): for var_name in persistable_var_dict: para_dict[var_name] = persistable_var_dict[var_name].numpy() - # if __variables.info__ exists, we can recover structured_name - var_info_path = os.path.join(model_prefix, - EXTRA_VAR_INFO_FILENAME) + # if *.info exists, we can recover structured_name + var_info_filename = str(config.params_filename) + ".info" + var_info_path = os.path.join(model_prefix, var_info_filename) if os.path.exists(var_info_path): with open(var_info_path, 'rb') as f: extra_var_info = pickle.load(f) diff --git a/python/paddle/fluid/dygraph/io.py b/python/paddle/fluid/dygraph/io.py index 4a3dacbd1acae59d4b668ad57b68b04c10e8b55c..a10adeb14aa7dd7cc5534f86d6af74ba59e3ccb4 100644 --- a/python/paddle/fluid/dygraph/io.py +++ b/python/paddle/fluid/dygraph/io.py @@ -31,8 +31,10 @@ from paddle.fluid.dygraph.base import switch_to_static_graph __all__ = ['TranslatedLayer'] -VARIABLE_FILENAME = "__variables__" -EXTRA_VAR_INFO_FILENAME = "__variables.info__" +INFER_MODEL_SUFFIX = ".pdmodel" +INFER_PARAMS_SUFFIX = ".pdiparams" +INFER_PARAMS_INFO_SUFFIX = ".pdiparams.info" + LOADED_VAR_SUFFIX = "load" PARAMETER_NAME_PREFIX = "param" BUFFER_NAME_PREFIX = "buffer" @@ -424,11 +426,8 @@ def _load_persistable_vars_by_program(model_path, return load_var_dict -def _load_persistable_vars(model_path, - var_info_path, - program_holder, - separate_params=False, - params_filename=None): +def _load_persistable_vars(model_path, var_info_path, program_holder, + params_filename): # 1. load extra var info with open(var_info_path, 'rb') as f: extra_var_info = pickle.load(f) @@ -464,33 +463,22 @@ def _load_persistable_vars(model_path, new_var = framework._varbase_creator( name=new_name, persistable=True) - # load separate vars - if separate_params is True: - framework._dygraph_tracer().trace_op( - type='load', - inputs={}, - outputs={'Out': new_var}, - attrs={'file_path': os.path.join(model_path, name)}) - new_var.stop_gradient = extra_var_info[name]['stop_gradient'] load_var_dict[new_name] = new_var load_var_list.append(new_var) # 3. load all vars - if separate_params is False: - if params_filename is not None: - var_file_path = os.path.join(model_path, params_filename) - else: - var_file_path = os.path.join(model_path, VARIABLE_FILENAME) - if not os.path.exists(var_file_path): - if len(extra_var_info) != 0: - raise ValueError("The model to be loaded is incomplete.") - else: - framework._dygraph_tracer().trace_op( - type='load_combine', - inputs={}, - outputs={'Out': load_var_list}, - attrs={'file_path': var_file_path}) + assert params_filename is not None, "params_filename should not be None." + var_file_path = os.path.join(model_path, params_filename) + if not os.path.exists(var_file_path): + if len(extra_var_info) != 0: + raise ValueError("The model to be loaded is incomplete.") + else: + framework._dygraph_tracer().trace_op( + type='load_combine', + inputs={}, + outputs={'Out': load_var_list}, + attrs={'file_path': var_file_path}) return load_var_dict @@ -532,14 +520,13 @@ def _construct_program_holders(model_path, model_filename=None): def _construct_params_and_buffers(model_path, programs, - separate_params=False, params_filename=None, append_suffix=True): - var_info_path = os.path.join(model_path, EXTRA_VAR_INFO_FILENAME) + var_info_filename = str(params_filename) + ".info" + var_info_path = os.path.join(model_path, var_info_filename) if os.path.exists(var_info_path): var_dict = _load_persistable_vars(model_path, var_info_path, - programs['forward'], separate_params, - params_filename) + programs['forward'], params_filename) else: var_dict = _load_persistable_vars_by_program( model_path, programs['forward'], params_filename) @@ -700,18 +687,16 @@ class TranslatedLayer(layers.Layer): raise ValueError("There is no directory named '%s'" % model_path) model_filename = None params_filename = None - separate_params = False if configs is not None: model_filename = configs.model_filename params_filename = configs.params_filename - separate_params = configs.separate_params # 1. load program desc & construct _ProgramHolder programs = _construct_program_holders(model_path, model_filename) # 2. load layer parameters & buffers - persistable_vars = _construct_params_and_buffers( - model_path, programs, separate_params, params_filename) + persistable_vars = _construct_params_and_buffers(model_path, programs, + params_filename) # 3. construct TranslatedLayer object translated_layer = TranslatedLayer(programs, persistable_vars) diff --git a/python/paddle/fluid/dygraph/jit.py b/python/paddle/fluid/dygraph/jit.py index 194ebafb08eefde4b08e503786f06c6dd06caa3b..6cdd13fba82ac21f637c790ad3259e1235c62bfa 100644 --- a/python/paddle/fluid/dygraph/jit.py +++ b/python/paddle/fluid/dygraph/jit.py @@ -29,7 +29,7 @@ from paddle.fluid.dygraph.base import program_desc_tracing_guard, switch_to_stat from paddle.fluid.dygraph.dygraph_to_static import logging_utils from paddle.fluid.dygraph.dygraph_to_static.logging_utils import set_code_level, set_verbosity from paddle.fluid.dygraph.dygraph_to_static.program_translator import ProgramTranslator, StaticFunction, unwrap_decorators -from paddle.fluid.dygraph.io import EXTRA_VAR_INFO_FILENAME, VARIABLE_FILENAME, TranslatedLayer +from paddle.fluid.dygraph.io import TranslatedLayer, INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX, INFER_PARAMS_INFO_SUFFIX from paddle.fluid.dygraph.layers import Layer from paddle.fluid.executor import Executor, scope_guard from paddle.fluid.framework import Block, ParamBase, Program, Variable @@ -39,7 +39,7 @@ from paddle.fluid.wrapped_decorator import wrap_decorator __all__ = [ 'TracedLayer', 'declarative', 'dygraph_to_static_func', 'set_code_level', - 'set_verbosity', 'save', 'load', 'SaveLoadConfig' + 'set_verbosity', 'save', 'load' ] @@ -228,73 +228,7 @@ def declarative(function=None, input_spec=None): return decorated -class SaveLoadConfig(object): - """ - The additional configuration options may be used in function - ``paddle.jit.save/load`` and ``paddle.load`` . - - Examples: - 1. Using ``SaveLoadConfig`` when saving model - - .. code-block:: python - - import paddle - import paddle.nn as nn - import paddle.optimizer as opt - - class SimpleNet(nn.Layer): - def __init__(self, in_size, out_size): - super(SimpleNet, self).__init__() - self._linear = nn.Linear(in_size, out_size) - - @paddle.jit.to_static - def forward(self, x): - y = self._linear(x) - z = self._linear(y) - return z - - # enable dygraph mode - paddle.disable_static() - - # train model - net = SimpleNet(8, 8) - adam = opt.Adam(learning_rate=0.1, parameters=net.parameters()) - x = paddle.randn([4, 8], 'float32') - for i in range(10): - out = net(x) - loss = paddle.tensor.mean(out) - loss.backward() - adam.step() - adam.clear_grad() - - # use SaveLoadconfig when saving model - model_path = "simplenet.example.model" - config = paddle.SaveLoadConfig() - config.model_filename = "__simplenet__" - paddle.jit.save( - layer=net, - model_path=model_path, - config=config) - - 2. Using ``SaveLoadConfig`` when loading model - - .. code-block:: python - - import paddle - - # enable dygraph mode - paddle.disable_static() - - # use SaveLoadconfig when loading model - model_path = "simplenet.example.model" - config = paddle.SaveLoadConfig() - config.model_filename = "__simplenet__" - infer_net = paddle.jit.load(model_path, config=config) - # inference - x = paddle.randn([4, 8], 'float32') - pred = infer_net(x) - """ - +class _SaveLoadConfig(object): def __init__(self): self._output_spec = None self._model_filename = None @@ -316,335 +250,105 @@ class SaveLoadConfig(object): @property def output_spec(self): - """ - Selects the output targets of the saved model ( ``paddle.jit.TranslatedLayer`` ). - By default, all return variables of original Layer's forward function - are kept as the output of the saved TranslatedLayer. - - The ``output_spec`` type should be list[Variable]. If the provided ``output_spec`` - list is not all output variables, the saved model will be pruned according to the - given ``output_spec`` list. - - .. note:: - The ``output_spec`` is only used when saving model. - - Examples: - .. code-block:: python - - import paddle - import paddle.nn as nn - import paddle.optimizer as opt - - class SimpleNet(nn.Layer): - def __init__(self, in_size, out_size): - super(SimpleNet, self).__init__() - self._linear = nn.Linear(in_size, out_size) - - @paddle.jit.to_static - def forward(self, x): - y = self._linear(x) - z = self._linear(y) - loss = paddle.tensor.mean(z) - return z, loss - - # enable dygraph mode - paddle.disable_static() - - # train model - net = SimpleNet(8, 8) - adam = opt.Adam(learning_rate=0.1, parameters=net.parameters()) - x = paddle.randn([4, 8], 'float32') - for i in range(10): - out, loss = net(x) - loss.backward() - adam.step() - adam.clear_grad() - - # use SaveLoadconfig.output_spec - model_path = "simplenet.example.model.output_spec" - config = paddle.SaveLoadConfig() - config.output_spec = [out] - paddle.jit.save( - layer=net, - model_path=model_path, - config=config) - - infer_net = paddle.jit.load(model_path) - x = paddle.randn([4, 8], 'float32') - pred = infer_net(x) - """ return self._output_spec @output_spec.setter def output_spec(self, spec): + if spec is None: + return if not isinstance(spec, list): raise TypeError( - "The SaveLoadConfig.output_spec should be 'list', but received input type is %s." + "The config `output_spec` should be 'list', but received input type is %s." % type(input)) for var in spec: if not isinstance(var, core.VarBase): raise TypeError( - "The element in SaveLoadConfig.output_spec list should be 'Variable', but received element's type is %s." + "The element in config `output_spec` list should be 'Variable', but received element's type is %s." % type(var)) self._output_spec = spec @property def model_filename(self): - """ - The name of file to save the translated program of target Layer. - Default filename is :code:`__model__` . - - Examples: - .. code-block:: python - - import paddle - import paddle.nn as nn - import paddle.optimizer as opt - - class SimpleNet(nn.Layer): - def __init__(self, in_size, out_size): - super(SimpleNet, self).__init__() - self._linear = nn.Linear(in_size, out_size) - - @paddle.jit.to_static - def forward(self, x): - y = self._linear(x) - z = self._linear(y) - return z - - # enable dygraph mode - paddle.disable_static() - - # train model - net = SimpleNet(8, 8) - adam = opt.Adam(learning_rate=0.1, parameters=net.parameters()) - x = paddle.randn([4, 8], 'float32') - for i in range(10): - out = net(x) - loss = paddle.tensor.mean(out) - loss.backward() - adam.step() - adam.clear_grad() - - # saving with configs.model_filename - model_path = "simplenet.example.model.model_filename" - config = paddle.SaveLoadConfig() - config.model_filename = "__simplenet__" - paddle.jit.save( - layer=net, - model_path=model_path, - config=config) - - # loading with configs.model_filename - infer_net = paddle.jit.load(model_path, config=config) - x = paddle.randn([4, 8], 'float32') - pred = infer_net(x) - """ return self._model_filename @model_filename.setter def model_filename(self, filename): + if filename is None: + return if not isinstance(filename, six.string_types): raise TypeError( - "The SaveLoadConfig.model_filename should be str, but received input's type is %s." + "The config `model_filename` should be str, but received input's type is %s." % type(filename)) if len(filename) == 0: - raise ValueError( - "The SaveLoadConfig.model_filename is empty string.") + raise ValueError("The config `model_filename` is empty string.") self._model_filename = filename @property def params_filename(self): - """ - The name of file to save all persistable variables in target Layer. - Default file name is :code:`__variables__` . - - Examples: - .. code-block:: python - - import paddle - import paddle.nn as nn - import paddle.optimizer as opt - - class SimpleNet(nn.Layer): - def __init__(self, in_size, out_size): - super(SimpleNet, self).__init__() - self._linear = nn.Linear(in_size, out_size) - - @paddle.jit.to_static - def forward(self, x): - y = self._linear(x) - z = self._linear(y) - return z - - # enable dygraph mode - paddle.disable_static() - - # train model - net = SimpleNet(8, 8) - adam = opt.Adam(learning_rate=0.1, parameters=net.parameters()) - x = paddle.randn([4, 8], 'float32') - for i in range(10): - out = net(x) - loss = paddle.tensor.mean(out) - loss.backward() - adam.step() - adam.clear_grad() - - model_path = "simplenet.example.model.params_filename" - config = paddle.SaveLoadConfig() - config.params_filename = "__params__" - - # saving with configs.params_filename - paddle.jit.save( - layer=net, - model_path=model_path, - config=config) - - # loading with configs.params_filename - infer_net = paddle.jit.load(model_path, config=config) - x = paddle.randn([4, 8], 'float32') - pred = infer_net(x) - """ return self._params_filename @params_filename.setter def params_filename(self, filename): + if filename is None: + return if not isinstance(filename, six.string_types): raise TypeError( - "The SaveLoadConfig.params_filename should be str, but received input's type is %s." + "The config `params_filename` should be str, but received input's type is %s." % type(filename)) if len(filename) == 0: - raise ValueError( - "The SaveLoadConfig.params_filename is empty string.") + raise ValueError("The config `params_filename` is empty string.") self._params_filename = filename - # NOTE: [why not use params_filename=None control params saved separately] - # The new save interface does not recommend parameters to be saved separately. - # Here, the concept should be separated as clearly as possible. - # Setting params_filename=None only means that the saved file name is set - # and without any other meaning. New separate_params control for file saved - # separately can makes the concept clearer. - @property - def separate_params(self): - """ - Configure whether to save the Layer parameters as separete files. - (In order to be compatible with the behavior of ``paddle.static.save_inference_model`` ) - - If True, each parameter will be saved to a file separately, the file name is the parameter name, - and the SaveLoadConfig.params_filename configuration will not take effect. Default False. - - .. note:: - Only used for ``paddle.jit.save`` . - - Examples: - .. code-block:: python - - import paddle - import paddle.nn as nn - import paddle.optimizer as opt - - class SimpleNet(nn.Layer): - def __init__(self, in_size, out_size): - super(SimpleNet, self).__init__() - self._linear = nn.Linear(in_size, out_size) - - @paddle.jit.to_static - def forward(self, x): - y = self._linear(x) - z = self._linear(y) - return z - - # enable dygraph mode - paddle.disable_static() - - # train model - net = SimpleNet(8, 8) - adam = opt.Adam(learning_rate=0.1, parameters=net.parameters()) - x = paddle.randn([4, 8], 'float32') - for i in range(10): - out = net(x) - loss = paddle.tensor.mean(out) - loss.backward() - adam.step() - adam.clear_grad() - - model_path = "simplenet.example.model.separate_params" - config = paddle.SaveLoadConfig() - config.separate_params = True - - # saving with configs.separate_params - paddle.jit.save( - layer=net, - model_path=model_path, - config=config) - # [result] the saved model directory contains: - # linear_0.b_0 linear_0.w_0 __model__ __variables.info__ - - # loading with configs.params_filename - infer_net = paddle.jit.load(model_path, config=config) - x = paddle.randn([4, 8], 'float32') - pred = infer_net(x) - """ - return self._separate_params - - @separate_params.setter - def separate_params(self, value): - if not isinstance(value, bool): - raise TypeError( - "The SaveLoadConfig.separate_params should be bool value, but received input's type is %s." - % type(value)) - self._separate_params = value - @property def keep_name_table(self): - """ - Configures whether keep ``structured_name -> parameter_name`` dict in loaded state dict. - This dict is the debugging information saved when call ``paddle.save`` . - It is generally only used for debugging and does not affect the actual training or inference. - By default, it will not be retained in ``paddle.load`` result. Default: False. - - .. note:: - Only used for ``paddle.load`` . - - Examples: - .. code-block:: python - - import paddle - - paddle.disable_static() - - linear = paddle.nn.Linear(5, 1) - - state_dict = linear.state_dict() - paddle.save(state_dict, "paddle_dy.pdparams") - - config = paddle.SaveLoadConfig() - config.keep_name_table = True - para_state_dict = paddle.load("paddle_dy.pdparams", config) - - print(para_state_dict) - # the name_table is 'StructuredToParameterName@@' - # {'bias': array([0.], dtype=float32), - # 'StructuredToParameterName@@': - # {'bias': u'linear_0.b_0', 'weight': u'linear_0.w_0'}, - # 'weight': array([[ 0.04230034], - # [-0.1222527 ], - # [ 0.7392676 ], - # [-0.8136974 ], - # [ 0.01211023]], dtype=float32)} - """ return self._keep_name_table @keep_name_table.setter def keep_name_table(self, value): + if value is None: + return if not isinstance(value, bool): raise TypeError( - "The SaveLoadConfig.keep_name_table should be bool value, but received input's type is %s." + "The config `keep_name_table` should be bool value, but received input's type is %s." % type(value)) self._keep_name_table = value +def _parse_save_configs(configs): + supported_configs = ['output_spec'] + + # input check + for key in configs: + if key not in supported_configs: + raise ValueError( + "The additional config (%s) of `paddle.jit.save` is not supported." + % (key)) + + # construct inner config + inner_config = _SaveLoadConfig() + inner_config.output_spec = configs.get('output_spec', None) + + return inner_config + + +def _parse_load_config(configs): + supported_configs = ['model_filename', 'params_filename'] + + # input check + for key in configs: + if key not in supported_configs: + raise ValueError( + "The additional config (%s) of `paddle.jit.load` is not supported." + % (key)) + + # construct inner config + inner_config = _SaveLoadConfig() + inner_config.model_filename = configs.get('model_filename', None) + inner_config.params_filename = configs.get('params_filename', None) + + return inner_config + + def _get_input_var_names(inputs, input_spec): name_none_error = "The %s's name is None. " \ "When using jit.save, please set InputSepc's name in " \ @@ -712,47 +416,88 @@ def _get_output_vars(outputs, output_spec): return result_list -# NOTE(chenweihang): change jit.save/load argument `configs` to `config` -def deprecate_save_load_configs(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - if 'configs' in kwargs: - kwargs['config'] = kwargs['configs'] - kwargs.pop('configs') - return func(*args, **kwargs) +# NOTE(chenweihang): [ Handling of use cases of API paddle.jit.load ] +# `paddle.jit.load` may be used to load saved results of: +# 1. Expected cases: +# - paddle.jit.save +# - paddle.static.save_inference_model +# - paddle.fluid.io.save_inference_model +# 2. Error cases: +# - paddle.save: no .pdmodel for prefix +# - paddle.static.save: no .pdiparams but .pdparams exists +# - paddle.fluid.io.save_params/save_persistables: no __model__ +# TODO(chenweihang): polish error message in above error cases +def _build_load_path_and_config(path, config): + # NOTE(chenweihang): If both [prefix save format] and [directory save format] exist, + # raise error, avoid confusing behavior + prefix_format_path = path + INFER_MODEL_SUFFIX + prefix_format_exist = os.path.exists(prefix_format_path) + directory_format_exist = os.path.isdir(path) + if prefix_format_exist and directory_format_exist: + raise ValueError( + "The %s.pdmodel and %s directory exist at the same time, " + "don't know which one to load, please make sure that the specified target " + "of ``path`` is unique." % (path, path)) + elif not prefix_format_exist and not directory_format_exist: + raise ValueError("The ``path`` (%s) to load model not exists." % path) + else: + if prefix_format_exist: + file_prefix = os.path.basename(path) + model_path = os.path.dirname(path) + if config.model_filename is not None: + warnings.warn( + "When loading the result saved with the " + "specified file prefix, the ``model_filename`` config does " + "not take effect.") + config.model_filename = file_prefix + INFER_MODEL_SUFFIX + if config.params_filename is not None: + warnings.warn( + "When loading the result saved with the " + "specified file prefix, the ``params_filename`` config does " + "not take effect.") + config.params_filename = file_prefix + INFER_PARAMS_SUFFIX + else: + # Compatible with the old save_inference_model format + model_path = path - return wrapper + return model_path, config -@deprecate_save_load_configs @switch_to_static_graph -def save(layer, model_path, input_spec=None, config=None): +def save(layer, path, input_spec=None, **configs): """ - Saves input declarative Layer as :ref:`api_imperative_TranslatedLayer` + Saves input Layer as ``paddle.jit.TranslatedLayer`` format model, which can be used for inference or fine-tuning after loading. It will save the translated program and all related persistable - variables of input declarative Layer to given ``model_path``. + variables of input Layer to given ``path``. - The default saved translated program file name is ``__model__``, - and the default saved persistable variables file name is ``__variables__``, - and it also saved some additional variable description information to file - ``__variables.info__``, these additional information is used in fine-tuning. + ``path`` is the prefix of saved objects, and the saved translated program file + suffix is ``.pdmodel``, the saved persistable variables file suffix is ``.pdiparams``, + and here also saved some additional variable description information to a file, + its suffix is ``.pdiparams.info``, these additional information is used in fine-tuning. The saved model can be loaded by follow APIs: - - :ref:`api_imperative_jit_load` - - :ref:`api_fluid_io_load_inference_model` (need pass ``params_filename='__variables__'``) + - ``paddle.jit.load`` + - ``paddle.static.load_inference_model`` - Other C++ inference APIs Args: - layer (Layer): the Layer to be saved. The Layer should be decorated by `@declarative`. - model_path (str): the directory to save the model. - input_spec (list[Variable], optional): Describes the input of the saved model. + layer (Layer): the Layer to be saved. The Layer should be decorated by `@paddle.jit.to_static`. + path (str): The path prefix to save model. The format is ``dirname/file_prefix`` or ``file_prefix``. + input_spec (list[InputSpec|Tensor], optional): Describes the input of the saved model. It is the example inputs that will be passed to saved TranslatedLayer's forward function. If None, all input variables of the original Layer's forward function would be the inputs of the saved model. Default None. - config (SaveLoadConfig, optional): :ref:`api_imperative_jit_saveLoadConfig` object - that specifies additional configuration options. Default None. + **configs (dict, optional): other save configuration options for compatibility. We do not + recommend using these configurations, they may be removed in the future. If not necessary, + DO NOT use them. Default None. + The following options are currently supported: + (1) output_spec (list[Tensor]): Selects the output targets of the saved model. + By default, all return variables of original Layer's forward function are kept as the + output of the saved model. If the provided ``output_spec`` list is not all output variables, + the saved model will be pruned according to the given ``output_spec`` list. + Returns: None @@ -804,10 +549,6 @@ def save(layer, model_path, input_spec=None, config=None): print("Epoch {} batch {}: loss = {}".format( epoch_id, batch_id, np.mean(loss.numpy()))) - # enable dygraph mode - place = paddle.CPUPlace() - paddle.disable_static(place) - # 1. train & save model. # create network @@ -818,7 +559,6 @@ def save(layer, model_path, input_spec=None, config=None): # create data loader dataset = RandomDataset(BATCH_NUM * BATCH_SIZE) loader = paddle.io.DataLoader(dataset, - places=place, batch_size=BATCH_SIZE, shuffle=True, drop_last=True, @@ -828,11 +568,11 @@ def save(layer, model_path, input_spec=None, config=None): train(layer, loader, loss_fn, adam) # save - model_path = "linear.example.model" - paddle.jit.save(layer, model_path) + path = "example_model/linear" + paddle.jit.save(layer, path) """ - # 1. input check + # 1. input build & check prog_translator = ProgramTranslator() if not prog_translator.enable_to_static: raise RuntimeError( @@ -843,9 +583,17 @@ def save(layer, model_path, input_spec=None, config=None): "The input layer of paddle.jit.save should be 'Layer', but received layer type is %s." % type(layer)) - configs = config - if configs is None: - configs = SaveLoadConfig() + # path check + file_prefix = os.path.basename(path) + if file_prefix == "": + raise ValueError( + "The input path MUST be format of dirname/file_prefix " + "[dirname\\file_prefix in Windows system], but received " + "file_prefix is empty string.") + + dirname = os.path.dirname(path) + if dirname and not os.path.exists(dirname): + os.makedirs(dirname) # avoid change user given input_spec inner_input_spec = None @@ -866,6 +614,9 @@ def save(layer, model_path, input_spec=None, config=None): "The element in input_spec list should be 'Variable' or `paddle.static.InputSpec`, but received element's type is %s." % type(var)) + # parse configs + configs = _parse_save_configs(configs) + # 2. get program from Layer # TODO(chenweihang): add support for other method, not only forward if isinstance(layer.forward, StaticFunction): @@ -927,9 +678,12 @@ def save(layer, model_path, input_spec=None, config=None): # 5. save inference model from paddle.fluid.io import save_inference_model - # VARIABLE_FILENAME keep nameing style consistent with '__model__' - if configs.params_filename is None: - configs.params_filename = VARIABLE_FILENAME + # construct new save_inference_model arguments + model_path = dirname + # NOTE(chenweihang): because prefix contains model and params filename, + # so we don't support set model_filename & params_filename + model_filename = file_prefix + INFER_MODEL_SUFFIX + params_filename = file_prefix + INFER_PARAMS_SUFFIX with scope_guard(scope): save_inference_model( @@ -938,9 +692,8 @@ def save(layer, model_path, input_spec=None, config=None): target_vars=output_vars, executor=Executor(_current_expected_place()), main_program=concrete_program.main_program.clone(), - model_filename=configs.model_filename, - params_filename=None - if configs.separate_params else configs.params_filename, + model_filename=model_filename, + params_filename=params_filename, export_for_deployment=configs._export_for_deployment, program_only=configs._program_only) @@ -958,23 +711,23 @@ def save(layer, model_path, input_spec=None, config=None): # Due to compatibility issues, we cannot change the original storage structure, # but we can save these information in `jit.save` without changing the original # storage to improve user experience. So we save extra information into - # file `__variables.info__` - extra_var_info_path = os.path.join(model_path, EXTRA_VAR_INFO_FILENAME) + # file `***.pdiparams.info` + extra_var_info_path = path + INFER_PARAMS_INFO_SUFFIX with open(extra_var_info_path, 'wb') as f: pickle.dump(extra_var_info, f, protocol=2) -@deprecate_save_load_configs @dygraph_only -def load(model_path, config=None): +def load(path, **configs): """ :api_attr: imperative - Load model saved by :ref:`api_imperative_jit_save` or :ref:`api_fluid_io_save_inference_model` - as :ref:`api_imperative_TranslatedLayer`, then performing inference or fine-tune training. + Load model saved by ``paddle.jit.save`` or ``paddle.static.save_inference_model`` or + paddle 1.x API ``paddle.fluid.io.save_inference_model`` as ``paddle.jit.TranslatedLayer``, + then performing inference or fine-tune training. .. note:: - For some historical reasons, if you load model saved by :ref:`api_fluid_io_save_inference_model`, + If you load model saved by ``paddle.static.save_inference_model`` , there will be the following limitations when using it in fine-tuning: 1. Imperative mode do not support LoDTensor. All original model's feed targets or parametars that depend on LoD are temporarily unavailable. 2. All saved model's feed targets need to be passed into TranslatedLayer's forward function. @@ -982,15 +735,23 @@ def load(model_path, config=None): 4. The parameter's ``trainable`` information is lost and can not be recovered. Args: - model_path (str): The directory path where the model is saved. - config (SaveLoadConfig, optional): :ref:`api_imperative_jit_saveLoadConfig` object that specifies - additional configuration options. Default None. + path (str): The path prefix to load model. The format is ``dirname/file_prefix`` or ``file_prefix``. + **configs (dict, optional): other load configuration options for compatibility. We do not + recommend using these configurations, they may be removed in the future. If not necessary, + DO NOT use them. Default None. + The following options are currently supported: + (1) model_filename (string): The inference model file name of the paddle 1.x + ``save_inference_model`` save format. Default file name is :code:`__model__` . + (2) params_filename (string): The persistable variables file name of the paddle 1.x + ``save_inference_model`` save format. No default file name, save variables separately + by default. + Returns: TranslatedLayer: A Layer object can run saved translated model. Examples: - 1. Load model saved by :ref:`api_imperative_jit_save` then performing inference and fine-tune training. + 1. Load model saved by ``paddle.jit.save`` then performing inference and fine-tune training. .. code-block:: python @@ -1039,10 +800,6 @@ def load(model_path, config=None): print("Epoch {} batch {}: loss = {}".format( epoch_id, batch_id, np.mean(loss.numpy()))) - # enable dygraph mode - place = paddle.CPUPlace() - paddle.disable_static(place) - # 1. train & save model. # create network @@ -1053,7 +810,6 @@ def load(model_path, config=None): # create data loader dataset = RandomDataset(BATCH_NUM * BATCH_SIZE) loader = paddle.io.DataLoader(dataset, - places=place, batch_size=BATCH_SIZE, shuffle=True, drop_last=True, @@ -1063,13 +819,13 @@ def load(model_path, config=None): train(layer, loader, loss_fn, adam) # save - model_path = "linear.example.model" - paddle.jit.save(layer, model_path) + path = "example_model/linear" + paddle.jit.save(layer, path) # 2. load model # load - loaded_layer = paddle.jit.load(model_path) + loaded_layer = paddle.jit.load(path) # inference loaded_layer.eval() @@ -1082,15 +838,17 @@ def load(model_path, config=None): train(loaded_layer, loader, loss_fn, adam) - 2. Load model saved by :ref:`api_fluid_io_save_inference_model` then performing and fine-tune training. + 2. Load model saved by ``paddle.fluid.io.save_inference_model`` then performing and fine-tune training. .. code-block:: python import numpy as np import paddle import paddle.fluid as fluid + import paddle.static as static import paddle.nn as nn import paddle.optimizer as opt + import paddle.nn.functional as F BATCH_SIZE = 16 BATCH_NUM = 4 @@ -1112,18 +870,18 @@ def load(model_path, config=None): 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) + image = static.data(name='image', shape=[None, 784], dtype='float32') + label = static.data(name='label', shape=[None, 1], dtype='int64') + pred = static.nn.fc(input=image, size=10, act='softmax') + loss = F.cross_entropy(input=pred, label=label) + avg_loss = paddle.mean(loss) - optimizer = fluid.optimizer.SGD(learning_rate=0.001) + optimizer = paddle.optimizer.SGD(learning_rate=0.001) optimizer.minimize(avg_loss) - place = fluid.CPUPlace() - exe = fluid.Executor(place) - exe.run(fluid.default_startup_program()) + place = paddle.CPUPlace() + exe = static.Executor(place) + exe.run(static.default_startup_program()) # create data loader dataset = RandomDataset(BATCH_NUM * BATCH_SIZE) @@ -1138,7 +896,7 @@ def load(model_path, config=None): # 1. train and save inference model for data in loader(): exe.run( - fluid.default_main_program(), + static.default_main_program(), feed=data, fetch_list=[avg_loss]) @@ -1179,6 +937,10 @@ def load(model_path, config=None): print("Epoch {} batch {}: loss = {}".format( epoch_id, batch_id, np.mean(loss.numpy()))) """ + # 1. construct correct config + config = _parse_load_config(configs) + model_path, config = _build_load_path_and_config(path, config) + return TranslatedLayer._construct(model_path, config) diff --git a/python/paddle/fluid/dygraph/static_runner.py b/python/paddle/fluid/dygraph/static_runner.py index d482077cd4f2aa5bf1cc30e4c71eac6e9bb7752f..e8738da07e99352f07688dd8ddde83bd31c6e1fa 100644 --- a/python/paddle/fluid/dygraph/static_runner.py +++ b/python/paddle/fluid/dygraph/static_runner.py @@ -14,7 +14,7 @@ from __future__ import print_function -from paddle.fluid.dygraph.jit import SaveLoadConfig +from paddle.fluid.dygraph.jit import _SaveLoadConfig from paddle.fluid.dygraph.io import TranslatedLayer @@ -31,7 +31,7 @@ class StaticModelRunner(object): """ def __new__(cls, model_dir, model_filename=None, params_filename=None): - configs = SaveLoadConfig() + configs = _SaveLoadConfig() if model_filename is not None: configs.model_filename = model_filename if params_filename is not None: diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/predictor_utils.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/predictor_utils.py index ba0adaf32e15db71162aed71c042100a0cd50e26..63edd35f59bd4a062b496ee798e5ef7a20f02980 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/predictor_utils.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/predictor_utils.py @@ -28,11 +28,12 @@ class PredictorTools(object): Paddle-Inference predictor ''' - def __init__(self, model_path, params_file, feeds_var): + def __init__(self, model_path, model_file, params_file, feeds_var): ''' __init__ ''' self.model_path = model_path + self.model_file = model_file self.params_file = params_file self.feeds_var = feeds_var @@ -43,7 +44,7 @@ class PredictorTools(object): ''' if os.path.exists(os.path.join(self.model_path, self.params_file)): config = AnalysisConfig( - os.path.join(self.model_path, "__model__"), + os.path.join(self.model_path, self.model_file), os.path.join(self.model_path, self.params_file)) else: config = AnalysisConfig(os.path.join(self.model_path)) diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_bert.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_bert.py index f105dd5e94744ecca96ee0282432ff4946ab5e04..6c26189a4adb37579bb2916a82a00df81f96a125 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_bert.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_bert.py @@ -12,13 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import time import unittest - import numpy as np + +import paddle import paddle.fluid as fluid from paddle.fluid.dygraph.dygraph_to_static import ProgramTranslator -from paddle.fluid.dygraph.io import VARIABLE_FILENAME +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX from bert_dygraph_model import PretrainModelLayer from bert_utils import get_bert_config, get_feed_data_reader @@ -31,7 +33,10 @@ place = fluid.CUDAPlace(0) if fluid.is_compiled_with_cuda() else fluid.CPUPlace( SEED = 2020 STEP_NUM = 10 PRINT_STEP = 2 -MODEL_SAVE_PATH = "./bert.inference.model" +MODEL_SAVE_DIR = "./inference" +MODEL_SAVE_PREFIX = "./inference/bert" +MODEL_FILENAME = "bert" + INFER_MODEL_SUFFIX +PARAMS_FILENAME = "bert" + INFER_PARAMS_SUFFIX DY_STATE_DICT_SAVE_PATH = "./bert.dygraph" @@ -85,7 +90,7 @@ def train(bert_config, data_reader, to_static): step_idx += 1 if step_idx == STEP_NUM: if to_static: - fluid.dygraph.jit.save(bert, MODEL_SAVE_PATH) + fluid.dygraph.jit.save(bert, MODEL_SAVE_PREFIX) else: fluid.dygraph.save_dygraph(bert.state_dict(), DY_STATE_DICT_SAVE_PATH) @@ -104,11 +109,15 @@ def train_static(bert_config, data_reader): def predict_static(data): + paddle.enable_static() exe = fluid.Executor(place) # load inference model [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model( - MODEL_SAVE_PATH, executor=exe, params_filename=VARIABLE_FILENAME) + MODEL_SAVE_DIR, + executor=exe, + model_filename=MODEL_FILENAME, + params_filename=PARAMS_FILENAME) pred_res = exe.run(inference_program, feed=dict(zip(feed_target_names, data)), fetch_list=fetch_targets) @@ -143,7 +152,7 @@ def predict_dygraph(bert_config, data): def predict_dygraph_jit(data): with fluid.dygraph.guard(place): - bert = fluid.dygraph.jit.load(MODEL_SAVE_PATH) + bert = fluid.dygraph.jit.load(MODEL_SAVE_PREFIX) bert.eval() src_ids, pos_ids, sent_ids, input_mask, mask_label, mask_pos, labels = data @@ -155,7 +164,8 @@ def predict_dygraph_jit(data): def predict_analysis_inference(data): - output = PredictorTools(MODEL_SAVE_PATH, VARIABLE_FILENAME, data) + output = PredictorTools(MODEL_SAVE_DIR, MODEL_FILENAME, PARAMS_FILENAME, + data) out = output() return out diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_bmn.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_bmn.py index af7e73c41464dbd26c476f20d4a1533e37d34ce3..f54f70e4b854bc0b44e2985d31f620a4d65915d5 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_bmn.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_bmn.py @@ -21,7 +21,7 @@ import paddle.fluid as fluid from paddle.fluid import ParamAttr from paddle.fluid.dygraph import to_variable from paddle.fluid.dygraph import ProgramTranslator -from paddle.fluid.dygraph.io import VARIABLE_FILENAME +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX from predictor_utils import PredictorTools @@ -422,7 +422,10 @@ class Args(object): prop_boundary_ratio = 0.5 num_sample = 2 num_sample_perbin = 2 - infer_dir = './bmn_infer_model' + model_save_dir = "./inference" + model_save_prefix = "./inference/bmn" + model_filename = "bmn" + INFER_MODEL_SUFFIX + params_filename = "bmn" + INFER_PARAMS_SUFFIX dy_param_path = './bmn_dy_param' @@ -620,7 +623,7 @@ def train_bmn(args, place, to_static): if batch_id == args.train_batch_num: if to_static: - fluid.dygraph.jit.save(bmn, args.infer_dir) + fluid.dygraph.jit.save(bmn, args.model_save_prefix) else: fluid.dygraph.save_dygraph(bmn.state_dict(), args.dy_param_path) @@ -735,13 +738,15 @@ class TestTrain(unittest.TestCase): return pred_res def predict_static(self, data): + paddle.enable_static() exe = fluid.Executor(self.place) # load inference model [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model( - self.args.infer_dir, + self.args.model_save_dir, executor=exe, - params_filename=VARIABLE_FILENAME) + model_filename=self.args.model_filename, + params_filename=self.args.params_filename) pred_res = exe.run(inference_program, feed={feed_target_names[0]: data}, fetch_list=fetch_targets) @@ -750,7 +755,7 @@ class TestTrain(unittest.TestCase): def predict_dygraph_jit(self, data): with fluid.dygraph.guard(self.place): - bmn = fluid.dygraph.jit.load(self.args.infer_dir) + bmn = fluid.dygraph.jit.load(self.args.model_save_prefix) bmn.eval() x = to_variable(data) @@ -760,7 +765,9 @@ class TestTrain(unittest.TestCase): return pred_res def predict_analysis_inference(self, data): - output = PredictorTools(self.args.infer_dir, VARIABLE_FILENAME, [data]) + output = PredictorTools(self.args.model_save_dir, + self.args.model_filename, + self.args.params_filename, [data]) out = output() return out diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_lac.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_lac.py index 4d735b565ddbcd0bea4e879f0ae5881e459c8f1d..d8cb3854d3e23a422377d03effbd6a69aa9d45a9 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_lac.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_lac.py @@ -26,7 +26,7 @@ import paddle.fluid as fluid from paddle.fluid.dygraph import to_variable from paddle.fluid.dygraph import Embedding, Linear, GRUUnit from paddle.fluid.dygraph import declarative, ProgramTranslator -from paddle.fluid.dygraph.io import VARIABLE_FILENAME +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX from predictor_utils import PredictorTools @@ -395,7 +395,10 @@ class Args(object): base_learning_rate = 0.01 bigru_num = 2 print_steps = 1 - model_save_dir = "./lac_model" + model_save_dir = "./inference" + model_save_prefix = "./inference/lac" + model_filename = "lac" + INFER_MODEL_SUFFIX + params_filename = "lac" + INFER_PARAMS_SUFFIX dy_param_path = "./lac_dy_param" @@ -498,13 +501,11 @@ def do_train(args, to_static): step += 1 # save inference model if to_static: - configs = fluid.dygraph.jit.SaveLoadConfig() - configs.output_spec = [crf_decode] fluid.dygraph.jit.save( layer=model, - model_path=args.model_save_dir, + path=args.model_save_prefix, input_spec=[words, length], - configs=configs) + output_spec=[crf_decode]) else: fluid.dygraph.save_dygraph(model.state_dict(), args.dy_param_path) @@ -573,13 +574,15 @@ class TestLACModel(unittest.TestCase): LAC model contains h_0 created in `__init__` that is necessary for inferring. Load inference model to test it's ok for prediction. """ + paddle.enable_static() exe = fluid.Executor(self.place) # load inference model [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model( self.args.model_save_dir, executor=exe, - params_filename=VARIABLE_FILENAME) + model_filename=self.args.model_filename, + params_filename=self.args.params_filename) words, targets, length = batch pred_res = exe.run( @@ -592,7 +595,7 @@ class TestLACModel(unittest.TestCase): def predict_dygraph_jit(self, batch): words, targets, length = batch with fluid.dygraph.guard(self.place): - model = fluid.dygraph.jit.load(self.args.model_save_dir) + model = fluid.dygraph.jit.load(self.args.model_save_prefix) model.eval() pred_res = model(to_variable(words), to_variable(length)) @@ -602,8 +605,9 @@ class TestLACModel(unittest.TestCase): def predict_analysis_inference(self, batch): words, targets, length = batch - output = PredictorTools(self.args.model_save_dir, VARIABLE_FILENAME, - [words, length]) + output = PredictorTools(self.args.model_save_dir, + self.args.model_filename, + self.args.params_filename, [words, length]) out = output() return out diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_mnist.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_mnist.py index bd600d2f2dbd6341ff7a83d6636047d01cae7859..8a21c4cfd0eca87ca2326e2b5eeb755a835f8de9 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_mnist.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_mnist.py @@ -25,7 +25,7 @@ from paddle.fluid.dygraph.base import switch_to_static_graph from paddle.fluid.dygraph import to_variable from paddle.fluid.dygraph.nn import Conv2D, Linear, Pool2D from paddle.fluid.optimizer import AdamOptimizer -from paddle.fluid.dygraph.io import VARIABLE_FILENAME +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX from paddle.fluid.dygraph.dygraph_to_static import ProgramTranslator from predictor_utils import PredictorTools @@ -218,34 +218,39 @@ class TestMNISTWithToStatic(TestMNIST): def check_jit_save_load(self, model, inputs, input_spec, to_static, gt_out): if to_static: infer_model_path = "./test_mnist_inference_model_by_jit_save" - configs = fluid.dygraph.jit.SaveLoadConfig() - configs.output_spec = [gt_out] + model_save_dir = "./inference" + model_save_prefix = "./inference/mnist" + model_filename = "mnist" + INFER_MODEL_SUFFIX + params_filename = "mnist" + INFER_PARAMS_SUFFIX fluid.dygraph.jit.save( layer=model, - model_path=infer_model_path, + path=model_save_prefix, input_spec=input_spec, - configs=configs) + output_spec=[gt_out]) # load in static mode static_infer_out = self.jit_load_and_run_inference_static( - infer_model_path, inputs) + model_save_dir, model_filename, params_filename, inputs) self.assertTrue(np.allclose(gt_out.numpy(), static_infer_out)) # load in dygraph mode dygraph_infer_out = self.jit_load_and_run_inference_dygraph( - infer_model_path, inputs) + model_save_prefix, inputs) self.assertTrue(np.allclose(gt_out.numpy(), dygraph_infer_out)) # load in Paddle-Inference predictor_infer_out = self.predictor_load_and_run_inference_analysis( - infer_model_path, inputs) + model_save_dir, model_filename, params_filename, inputs) self.assertTrue(np.allclose(gt_out.numpy(), predictor_infer_out)) @switch_to_static_graph - def jit_load_and_run_inference_static(self, model_path, inputs): + def jit_load_and_run_inference_static(self, model_path, model_filename, + params_filename, inputs): + paddle.enable_static() exe = fluid.Executor(self.place) [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model( dirname=model_path, executor=exe, - params_filename=VARIABLE_FILENAME) + model_filename=model_filename, + params_filename=params_filename) assert len(inputs) == len(feed_target_names) results = exe.run(inference_program, feed=dict(zip(feed_target_names, inputs)), @@ -258,8 +263,10 @@ class TestMNISTWithToStatic(TestMNIST): pred = infer_net(inputs[0]) return pred.numpy() - def predictor_load_and_run_inference_analysis(self, model_path, inputs): - output = PredictorTools(model_path, VARIABLE_FILENAME, inputs) + def predictor_load_and_run_inference_analysis( + self, model_path, model_filename, params_filename, inputs): + output = PredictorTools(model_path, model_filename, params_filename, + inputs) out = output() return out diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_mobile_net.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_mobile_net.py index a377075062b268723aaa3cb17bfa25d6b181798d..a086bf1455a814549a40c2fee179741178aac912 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_mobile_net.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_mobile_net.py @@ -20,7 +20,7 @@ from paddle.fluid.initializer import MSRA from paddle.fluid.param_attr import ParamAttr from paddle.fluid.dygraph.nn import Conv2D, Pool2D, BatchNorm, Linear from paddle.fluid.dygraph import declarative, ProgramTranslator -from paddle.fluid.dygraph.io import VARIABLE_FILENAME +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX import unittest @@ -439,7 +439,10 @@ class Args(object): train_step = 10 place = fluid.CUDAPlace(0) if fluid.is_compiled_with_cuda( ) else fluid.CPUPlace() - model_save_path = model + ".inference.model" + model_save_dir = "./inference" + model_save_prefix = "./inference/" + model + model_filename = model + INFER_MODEL_SUFFIX + params_filename = model + INFER_PARAMS_SUFFIX dy_state_dict_save_path = model + ".dygraph" @@ -504,7 +507,7 @@ def train_mobilenet(args, to_static): t_last = time.time() if batch_id > args.train_step: if to_static: - fluid.dygraph.jit.save(net, args.model_save_path) + fluid.dygraph.jit.save(net, args.model_save_prefix) else: fluid.dygraph.save_dygraph(net.state_dict(), args.dy_state_dict_save_path) @@ -514,11 +517,15 @@ def train_mobilenet(args, to_static): def predict_static(args, data): + paddle.enable_static() exe = fluid.Executor(args.place) # load inference model [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model( - args.model_save_path, executor=exe, params_filename=VARIABLE_FILENAME) + args.model_save_dir, + executor=exe, + model_filename=args.model_filename, + params_filename=args.params_filename) pred_res = exe.run(inference_program, feed={feed_target_names[0]: data}, @@ -545,7 +552,7 @@ def predict_dygraph(args, data): def predict_dygraph_jit(args, data): with fluid.dygraph.guard(args.place): - model = fluid.dygraph.jit.load(args.model_save_path) + model = fluid.dygraph.jit.load(args.model_save_prefix) model.eval() pred_res = model(data) @@ -554,7 +561,8 @@ def predict_dygraph_jit(args, data): def predict_analysis_inference(args, data): - output = PredictorTools(args.model_save_path, VARIABLE_FILENAME, [data]) + output = PredictorTools(args.model_save_dir, args.model_filename, + args.params_filename, [data]) out = output() return out @@ -565,7 +573,9 @@ class TestMobileNet(unittest.TestCase): def train(self, model_name, to_static): self.args.model = model_name - self.args.model_save_path = model_name + ".inference.model" + self.args.model_save_prefix = "./inference/" + model_name + self.args.model_filename = model_name + INFER_MODEL_SUFFIX + self.args.params_filename = model_name + INFER_PARAMS_SUFFIX self.args.dy_state_dict_save_path = model_name + ".dygraph" out = train_mobilenet(self.args, to_static) return out @@ -579,7 +589,9 @@ class TestMobileNet(unittest.TestCase): def assert_same_predict(self, model_name): self.args.model = model_name - self.args.model_save_path = model_name + ".inference.model" + self.args.model_save_prefix = "./inference/" + model_name + self.args.model_filename = model_name + INFER_MODEL_SUFFIX + self.args.params_filename = model_name + INFER_PARAMS_SUFFIX self.args.dy_state_dict_save_path = model_name + ".dygraph" local_random = np.random.RandomState(SEED) image = local_random.random_sample([1, 3, 224, 224]).astype('float32') diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_resnet.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_resnet.py index 203c8ddb3488c0fef9a0a590378505e5b61233cf..095940d79eac60a8b151cc3eb97a514482ab6038 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_resnet.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_resnet.py @@ -24,7 +24,7 @@ import paddle import paddle.fluid as fluid from paddle.fluid.dygraph import declarative, ProgramTranslator from paddle.fluid.dygraph.nn import BatchNorm, Conv2D, Linear, Pool2D -from paddle.fluid.dygraph.io import VARIABLE_FILENAME +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX from predictor_utils import PredictorTools @@ -38,7 +38,11 @@ batch_size = 2 epoch_num = 1 place = fluid.CUDAPlace(0) if fluid.is_compiled_with_cuda() \ else fluid.CPUPlace() -MODEL_SAVE_PATH = "./resnet.inference.model" + +MODEL_SAVE_DIR = "./inference" +MODEL_SAVE_PREFIX = "./inference/resnet" +MODEL_FILENAME = "resnet" + INFER_MODEL_SUFFIX +PARAMS_FILENAME = "resnet" + INFER_PARAMS_SUFFIX DY_STATE_DICT_SAVE_PATH = "./resnet.dygraph" program_translator = ProgramTranslator() @@ -261,7 +265,7 @@ def train(to_static): total_acc1.numpy() / total_sample, total_acc5.numpy() / total_sample, end_time-start_time)) if batch_id == 10: if to_static: - fluid.dygraph.jit.save(resnet, MODEL_SAVE_PATH) + fluid.dygraph.jit.save(resnet, MODEL_SAVE_PREFIX) else: fluid.dygraph.save_dygraph(resnet.state_dict(), DY_STATE_DICT_SAVE_PATH) @@ -287,10 +291,14 @@ def predict_dygraph(data): def predict_static(data): + paddle.enable_static() exe = fluid.Executor(place) [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model( - MODEL_SAVE_PATH, executor=exe, params_filename=VARIABLE_FILENAME) + MODEL_SAVE_DIR, + executor=exe, + model_filename=MODEL_FILENAME, + params_filename=PARAMS_FILENAME) pred_res = exe.run(inference_program, feed={feed_target_names[0]: data}, @@ -301,7 +309,7 @@ def predict_static(data): def predict_dygraph_jit(data): with fluid.dygraph.guard(place): - resnet = fluid.dygraph.jit.load(MODEL_SAVE_PATH) + resnet = fluid.dygraph.jit.load(MODEL_SAVE_PREFIX) resnet.eval() pred_res = resnet(data) @@ -310,7 +318,8 @@ def predict_dygraph_jit(data): def predict_analysis_inference(data): - output = PredictorTools(MODEL_SAVE_PATH, VARIABLE_FILENAME, [data]) + output = PredictorTools(MODEL_SAVE_DIR, MODEL_FILENAME, PARAMS_FILENAME, + [data]) out = output() return out diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_resnet_v2.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_resnet_v2.py index 75c251253c05afeebecfdb064058dd7222f4beba..a8cfeb90bd81403d21b08fd96091cd5c69418c32 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_resnet_v2.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_resnet_v2.py @@ -34,7 +34,11 @@ batch_size = 2 epoch_num = 1 place = paddle.CUDAPlace(0) if paddle.is_compiled_with_cuda() \ else paddle.CPUPlace() -MODEL_SAVE_PATH = "./resnet_v2.inference.model" + +MODEL_SAVE_DIR = "./inference" +MODEL_SAVE_PREFIX = "./inference/resnet_v2" +MODEL_FILENAME = "resnet_v2" + paddle.fluid.dygraph.io.INFER_MODEL_SUFFIX +PARAMS_FILENAME = "resnet_v2" + paddle.fluid.dygraph.io.INFER_PARAMS_SUFFIX DY_STATE_DICT_SAVE_PATH = "./resnet_v2.dygraph" program_translator = paddle.jit.ProgramTranslator() @@ -255,7 +259,7 @@ def train(to_static): total_acc1.numpy() / total_sample, total_acc5.numpy() / total_sample, end_time-start_time)) if batch_id == 10: if to_static: - paddle.jit.save(resnet, MODEL_SAVE_PATH) + paddle.jit.save(resnet, MODEL_SAVE_PREFIX) else: paddle.fluid.dygraph.save_dygraph(resnet.state_dict(), DY_STATE_DICT_SAVE_PATH) @@ -289,9 +293,10 @@ def predict_static(data): exe = paddle.static.Executor(place) [inference_program, feed_target_names, fetch_targets] = paddle.static.load_inference_model( - MODEL_SAVE_PATH, + MODEL_SAVE_DIR, executor=exe, - params_filename=paddle.fluid.dygraph.io.VARIABLE_FILENAME) + model_filename=MODEL_FILENAME, + params_filename=PARAMS_FILENAME) pred_res = exe.run(inference_program, feed={feed_target_names[0]: data}, @@ -302,7 +307,7 @@ def predict_static(data): def predict_dygraph_jit(data): paddle.disable_static(place) - resnet = paddle.jit.load(MODEL_SAVE_PATH) + resnet = paddle.jit.load(MODEL_SAVE_PREFIX) resnet.eval() pred_res = resnet(data) @@ -313,8 +318,8 @@ def predict_dygraph_jit(data): def predict_analysis_inference(data): - output = PredictorTools(MODEL_SAVE_PATH, - paddle.fluid.dygraph.io.VARIABLE_FILENAME, [data]) + output = PredictorTools(MODEL_SAVE_DIR, MODEL_FILENAME, PARAMS_FILENAME, + [data]) out = output() return out diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_save_inference_model.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_save_inference_model.py index cf7708c675aa9c1fb8faf5f8585b458be88b6c83..b431d5ae048a9a9b30d961b8a1d754d38e521457 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_save_inference_model.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_save_inference_model.py @@ -16,14 +16,14 @@ from __future__ import print_function import os import unittest - import numpy as np -import paddle.fluid as fluid +import paddle +import paddle.fluid as fluid from paddle.fluid.dygraph.dygraph_to_static import ProgramTranslator from paddle.fluid.dygraph.jit import declarative from paddle.fluid.dygraph.dygraph_to_static.partial_program import partial_program_from -from paddle.fluid.dygraph.io import EXTRA_VAR_INFO_FILENAME +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX, INFER_PARAMS_INFO_SUFFIX SEED = 2020 @@ -66,14 +66,13 @@ class TestDyToStaticSaveInferenceModel(unittest.TestCase): adam.minimize(loss) layer.clear_gradients() # test for saving model in dygraph.guard - infer_model_dir = "./test_dy2stat_save_inference_model_in_guard" - configs = fluid.dygraph.jit.SaveLoadConfig() - configs.output_spec = [pred] + infer_model_prefix = "./test_dy2stat_inference_in_guard/model" + infer_model_dir = "./test_dy2stat_inference_in_guard" fluid.dygraph.jit.save( layer=layer, - model_path=infer_model_dir, + path=infer_model_prefix, input_spec=[x], - configs=configs) + output_spec=[pred]) # Check the correctness of the inference dygraph_out, _ = layer(x) self.check_save_inference_model(layer, [x_data], dygraph_out.numpy()) @@ -91,30 +90,30 @@ class TestDyToStaticSaveInferenceModel(unittest.TestCase): expected_persistable_vars = set([p.name for p in model.parameters()]) - infer_model_dir = "./test_dy2stat_save_inference_model" - configs = fluid.dygraph.jit.SaveLoadConfig() - if fetch is not None: - configs.output_spec = fetch - configs.separate_params = True + infer_model_prefix = "./test_dy2stat_inference/model" + infer_model_dir = "./test_dy2stat_inference" + model_filename = "model" + INFER_MODEL_SUFFIX + params_filename = "model" + INFER_PARAMS_SUFFIX fluid.dygraph.jit.save( layer=model, - model_path=infer_model_dir, + path=infer_model_prefix, input_spec=feed if feed else None, - configs=configs) - saved_var_names = set([ - filename for filename in os.listdir(infer_model_dir) - if filename != '__model__' and filename != EXTRA_VAR_INFO_FILENAME - ]) - self.assertEqual(saved_var_names, expected_persistable_vars) + output_spec=fetch if fetch else None) # Check the correctness of the inference - infer_out = self.load_and_run_inference(infer_model_dir, inputs) + infer_out = self.load_and_run_inference(infer_model_dir, model_filename, + params_filename, inputs) self.assertTrue(np.allclose(gt_out, infer_out)) - def load_and_run_inference(self, model_path, inputs): + def load_and_run_inference(self, model_path, model_filename, + params_filename, inputs): + paddle.enable_static() exe = fluid.Executor(place) [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model( - dirname=model_path, executor=exe) + dirname=model_path, + executor=exe, + model_filename=model_filename, + params_filename=params_filename) results = exe.run(inference_program, feed=dict(zip(feed_target_names, inputs)), fetch_list=fetch_targets) diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_se_resnet.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_se_resnet.py index 8f11a585884636f4dea711d0ea07e197b3856a19..15cff501838a1485dc1f69326fb55d4dce8f85cc 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_se_resnet.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_se_resnet.py @@ -24,7 +24,7 @@ from paddle.fluid.dygraph.base import to_variable from paddle.fluid.dygraph.nn import BatchNorm, Conv2D, Linear, Pool2D from paddle.fluid.dygraph import declarative from paddle.fluid.dygraph import ProgramTranslator -from paddle.fluid.dygraph.io import VARIABLE_FILENAME +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX from predictor_utils import PredictorTools @@ -35,7 +35,10 @@ BATCH_SIZE = 8 EPOCH_NUM = 1 PRINT_STEP = 2 STEP_NUM = 10 -MODEL_SAVE_PATH = "./se_resnet.inference.model" +MODEL_SAVE_DIR = "./inference" +MODEL_SAVE_PREFIX = "./inference/se_resnet" +MODEL_FILENAME = "se_resnet" + INFER_MODEL_SUFFIX +PARAMS_FILENAME = "se_resnet" + INFER_PARAMS_SUFFIX DY_STATE_DICT_SAVE_PATH = "./se_resnet.dygraph" place = fluid.CUDAPlace(0) if fluid.is_compiled_with_cuda() \ @@ -383,10 +386,10 @@ def train(train_reader, to_static): step_idx += 1 if step_idx == STEP_NUM: if to_static: - configs = fluid.dygraph.jit.SaveLoadConfig() - configs.output_spec = [pred] - fluid.dygraph.jit.save(se_resnext, MODEL_SAVE_PATH, - [img], configs) + fluid.dygraph.jit.save( + se_resnext, + MODEL_SAVE_PREFIX, [img], + output_spec=[pred]) else: fluid.dygraph.save_dygraph(se_resnext.state_dict(), DY_STATE_DICT_SAVE_PATH) @@ -414,10 +417,14 @@ def predict_dygraph(data): def predict_static(data): + paddle.enable_static() exe = fluid.Executor(place) [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model( - MODEL_SAVE_PATH, executor=exe, params_filename=VARIABLE_FILENAME) + MODEL_SAVE_DIR, + executor=exe, + model_filename=MODEL_FILENAME, + params_filename=PARAMS_FILENAME) pred_res = exe.run(inference_program, feed={feed_target_names[0]: data}, @@ -428,7 +435,7 @@ def predict_static(data): def predict_dygraph_jit(data): with fluid.dygraph.guard(place): - se_resnext = fluid.dygraph.jit.load(MODEL_SAVE_PATH) + se_resnext = fluid.dygraph.jit.load(MODEL_SAVE_PREFIX) se_resnext.eval() pred_res = se_resnext(data) @@ -437,7 +444,8 @@ def predict_dygraph_jit(data): def predict_analysis_inference(data): - output = PredictorTools(MODEL_SAVE_PATH, VARIABLE_FILENAME, [data]) + output = PredictorTools(MODEL_SAVE_DIR, MODEL_FILENAME, PARAMS_FILENAME, + [data]) out = output() return out diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_transformer.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_transformer.py index 4fc8d27d30cb8f67c30bbcd8dcd30938f906462d..6721e7a51d2bc55696157d2686915355b56940f4 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/test_transformer.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/test_transformer.py @@ -32,6 +32,7 @@ STEP_NUM = 10 def train_static(args, batch_generator): + paddle.enable_static() paddle.manual_seed(SEED) paddle.framework.random._manual_program_seed(SEED) train_prog = fluid.Program() diff --git a/python/paddle/fluid/tests/unittests/dygraph_to_static/transformer_util.py b/python/paddle/fluid/tests/unittests/dygraph_to_static/transformer_util.py index 8ebb99fda660eae6802ef79c033f14b7e6425974..e264a300d8c186487225f6ded00141c34a770e21 100644 --- a/python/paddle/fluid/tests/unittests/dygraph_to_static/transformer_util.py +++ b/python/paddle/fluid/tests/unittests/dygraph_to_static/transformer_util.py @@ -277,7 +277,8 @@ def load_dygraph(model_path, keep_name_table=False): To load python2 saved models in python3. """ try: - para_dict, opti_dict = fluid.load_dygraph(model_path, keep_name_table) + para_dict, opti_dict = fluid.load_dygraph( + model_path, keep_name_table=keep_name_table) return para_dict, opti_dict except UnicodeDecodeError: warnings.warn( @@ -287,7 +288,7 @@ def load_dygraph(model_path, keep_name_table=False): if six.PY3: load_bak = pickle.load pickle.load = partial(load_bak, encoding="latin1") - para_dict, opti_dict = fluid.load_dygraph(model_path, - keep_name_table) + para_dict, opti_dict = fluid.load_dygraph( + model_path, keep_name_table=keep_name_table) pickle.load = load_bak return para_dict, opti_dict diff --git a/python/paddle/fluid/tests/unittests/test_directory_migration.py b/python/paddle/fluid/tests/unittests/test_directory_migration.py index 2f35b45aa670c1d8e2caa7fe2ecf4e06b9884899..7d48f2c419085ff7ea66076bfc561aa7655d41c6 100644 --- a/python/paddle/fluid/tests/unittests/test_directory_migration.py +++ b/python/paddle/fluid/tests/unittests/test_directory_migration.py @@ -43,15 +43,14 @@ class TestDirectory(unittest.TestCase): 'paddle.distributed.prepare_context', 'paddle.DataParallel', 'paddle.jit', 'paddle.jit.TracedLayer', 'paddle.jit.to_static', 'paddle.jit.ProgramTranslator', 'paddle.jit.TranslatedLayer', - 'paddle.jit.save', 'paddle.jit.load', 'paddle.SaveLoadConfig', - 'paddle.NoamDecay', 'paddle.PiecewiseDecay', - 'paddle.NaturalExpDecay', 'paddle.ExponentialDecay', - 'paddle.InverseTimeDecay', 'paddle.PolynomialDecay', - 'paddle.CosineDecay', 'paddle.static.Executor', - 'paddle.static.global_scope', 'paddle.static.scope_guard', - 'paddle.static.append_backward', 'paddle.static.gradients', - 'paddle.static.BuildStrategy', 'paddle.static.CompiledProgram', - 'paddle.static.ExecutionStrategy', + 'paddle.jit.save', 'paddle.jit.load', 'paddle.NoamDecay', + 'paddle.PiecewiseDecay', 'paddle.NaturalExpDecay', + 'paddle.ExponentialDecay', 'paddle.InverseTimeDecay', + 'paddle.PolynomialDecay', 'paddle.CosineDecay', + 'paddle.static.Executor', 'paddle.static.global_scope', + 'paddle.static.scope_guard', 'paddle.static.append_backward', + 'paddle.static.gradients', 'paddle.static.BuildStrategy', + 'paddle.static.CompiledProgram', 'paddle.static.ExecutionStrategy', 'paddle.static.default_main_program', 'paddle.static.default_startup_program', 'paddle.static.Program', 'paddle.static.name_scope', 'paddle.static.program_guard', @@ -104,9 +103,7 @@ class TestDirectory(unittest.TestCase): 'paddle.imperative.TracedLayer', 'paddle.imperative.declarative', 'paddle.imperative.ProgramTranslator', 'paddle.imperative.TranslatedLayer', 'paddle.imperative.jit.save', - 'paddle.imperative.jit.load', - 'paddle.imperative.jit.SaveLoadConfig', - 'paddle.imperative.NoamDecay' + 'paddle.imperative.jit.load', 'paddle.imperative.NoamDecay' 'paddle.imperative.PiecewiseDecay', 'paddle.imperative.NaturalExpDecay', 'paddle.imperative.ExponentialDecay', diff --git a/python/paddle/fluid/tests/unittests/test_imperative_save_load.py b/python/paddle/fluid/tests/unittests/test_imperative_save_load.py index bee53fd10f5feef911ce8ed105c7792cbd664a15..45709a358635ca212d3b52808ec5712bf9c76248 100644 --- a/python/paddle/fluid/tests/unittests/test_imperative_save_load.py +++ b/python/paddle/fluid/tests/unittests/test_imperative_save_load.py @@ -917,11 +917,6 @@ class TestDygraphPtbRnn(unittest.TestCase): state_dict = emb.state_dict() fluid.save_dygraph(state_dict, os.path.join('saved_dy', 'emb_dy')) - para_state_dict, opti_state_dict = fluid.load_dygraph( - os.path.join('saved_dy', 'emb_dy'), True) - self.assertTrue(para_state_dict != None) - self.assertTrue(opti_state_dict == None) - para_state_dict, opti_state_dict = fluid.load_dygraph( os.path.join('saved_dy', 'emb_dy'), keep_name_table=True) self.assertTrue(para_state_dict != None) diff --git a/python/paddle/fluid/tests/unittests/test_jit_save_load.py b/python/paddle/fluid/tests/unittests/test_jit_save_load.py index 99404246185040e35c59ab5cf374e1948870b2bb..71ec1271a041edddd66d55b3cbfb2070231ea8de 100644 --- a/python/paddle/fluid/tests/unittests/test_jit_save_load.py +++ b/python/paddle/fluid/tests/unittests/test_jit_save_load.py @@ -23,7 +23,7 @@ from paddle.static import InputSpec import paddle.fluid as fluid from paddle.fluid.dygraph import Linear from paddle.fluid.dygraph import declarative, ProgramTranslator -from paddle.fluid.dygraph.io import EXTRA_VAR_INFO_FILENAME, VARIABLE_FILENAME +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX, INFER_PARAMS_INFO_SUFFIX BATCH_SIZE = 32 BATCH_NUM = 10 @@ -127,8 +127,8 @@ class MultiLoadingLinearNet(fluid.dygraph.Layer): def __init__(self, size, model_path): super(MultiLoadingLinearNet, self).__init__() self._linear = Linear(size, size) - self._load_linear1 = fluid.dygraph.jit.load(model_path) - self._load_linear2 = fluid.dygraph.jit.load(model_path) + self._load_linear1 = paddle.jit.load(model_path) + self._load_linear2 = paddle.jit.load(model_path) @declarative def forward(self, x): @@ -218,23 +218,20 @@ def train_with_label(layer, input_size=784, label_size=1): class TestJitSaveLoad(unittest.TestCase): def setUp(self): - self.model_path = "model.test_jit_save_load" + self.model_path = "test_jit_save_load/model" # enable dygraph mode fluid.enable_dygraph() # config seed paddle.manual_seed(SEED) paddle.framework.random._manual_program_seed(SEED) - def train_and_save_model(self, model_path=None, configs=None): + def train_and_save_model(self, model_path=None): layer = LinearNet(784, 1) example_inputs, layer, _ = train(layer) final_model_path = model_path if model_path else self.model_path orig_input_types = [type(x) for x in example_inputs] - fluid.dygraph.jit.save( - layer=layer, - model_path=final_model_path, - input_spec=example_inputs, - configs=configs) + paddle.jit.save( + layer=layer, path=final_model_path, input_spec=example_inputs) new_input_types = [type(x) for x in example_inputs] self.assertEqual(orig_input_types, new_input_types) return layer @@ -243,13 +240,10 @@ class TestJitSaveLoad(unittest.TestCase): # train and save model train_layer = self.train_and_save_model() # load model - program_translator = ProgramTranslator() - program_translator.enable(False) - loaded_layer = fluid.dygraph.jit.load(self.model_path) + loaded_layer = paddle.jit.load(self.model_path) self.load_and_inference(train_layer, loaded_layer) self.load_dygraph_state_dict(train_layer) self.load_and_finetune(train_layer, loaded_layer) - program_translator.enable(True) def load_and_inference(self, train_layer, infer_layer): train_layer.eval() @@ -274,7 +268,7 @@ class TestJitSaveLoad(unittest.TestCase): # construct new model new_layer = LinearNet(784, 1) orig_state_dict = new_layer.state_dict() - load_state_dict, _ = fluid.dygraph.load_dygraph(self.model_path) + load_state_dict = paddle.load(self.model_path) for structured_name in orig_state_dict: self.assertTrue(structured_name in load_state_dict) new_layer.set_state_dict(load_state_dict) @@ -286,20 +280,24 @@ class TestJitSaveLoad(unittest.TestCase): np.array_equal(train_layer(x).numpy(), new_layer(x).numpy())) def test_load_dygraph_no_path(self): - model_path = "model.test_jit_save_load.no_path" - new_layer = LinearNet(784, 1) + model_path = "test_jit_save_load.no_path/model_path" with self.assertRaises(ValueError): model_dict, _ = fluid.dygraph.load_dygraph(model_path) def test_jit_load_model_incomplete(self): - model_path = "model.test_jit_save_load.remove_variables" - self.train_and_save_model(model_path=model_path) - # remove `__variables__` - var_path = os.path.join(model_path, VARIABLE_FILENAME) + model_path = "test_jit_save_load.remove_variables/model" + self.train_and_save_model(model_path) + # remove `.pdiparams` + var_path = model_path + INFER_PARAMS_SUFFIX os.remove(var_path) with self.assertRaises(ValueError): paddle.jit.load(model_path) + def test_jit_load_no_path(self): + path = "test_jit_save_load.no_path/model_path" + with self.assertRaises(ValueError): + loaded_layer = paddle.jit.load(path) + class TestSaveLoadWithInputSpec(unittest.TestCase): def setUp(self): @@ -313,8 +311,7 @@ class TestSaveLoadWithInputSpec(unittest.TestCase): net.forward, input_spec=[InputSpec( [None, 8], name='x')]) - model_path = "model.input_spec.output_spec" - configs = fluid.dygraph.jit.SaveLoadConfig() + model_path = "input_spec.output_spec/model" # check inputs and outputs self.assertTrue(len(net.forward.inputs) == 1) input_x = net.forward.inputs[0] @@ -322,11 +319,11 @@ class TestSaveLoadWithInputSpec(unittest.TestCase): self.assertTrue(input_x.name == 'x') # 1. prune loss - configs.output_spec = net.forward.outputs[:1] - fluid.dygraph.jit.save(net, model_path, configs=configs) + output_spec = net.forward.outputs[:1] + paddle.jit.save(net, model_path, output_spec=output_spec) # 2. load to infer - infer_layer = fluid.dygraph.jit.load(model_path, configs=configs) + infer_layer = paddle.jit.load(model_path) x = fluid.dygraph.to_variable( np.random.random((4, 8)).astype('float32')) pred = infer_layer(x) @@ -334,8 +331,7 @@ class TestSaveLoadWithInputSpec(unittest.TestCase): def test_multi_in_out(self): net = LinearNetMultiInput(8, 8) - model_path = "model.multi_inout.output_spec1" - configs = fluid.dygraph.jit.SaveLoadConfig() + model_path = "multi_inout.output_spec1/model" # 1. check inputs and outputs self.assertTrue(len(net.forward.inputs) == 2) input_x = net.forward.inputs[0] @@ -344,11 +340,11 @@ class TestSaveLoadWithInputSpec(unittest.TestCase): self.assertTrue(input_y.shape == (-1, 8)) # 2. prune loss - configs.output_spec = net.forward.outputs[:2] - fluid.dygraph.jit.save(net, model_path, configs=configs) + output_spec = net.forward.outputs[:2] + paddle.jit.save(net, model_path, output_spec=output_spec) # 3. load to infer - infer_layer = fluid.dygraph.jit.load(model_path, configs=configs) + infer_layer = paddle.jit.load(model_path) x = fluid.dygraph.to_variable( np.random.random((4, 8)).astype('float32')) y = fluid.dygraph.to_variable( @@ -357,11 +353,11 @@ class TestSaveLoadWithInputSpec(unittest.TestCase): pred_x, pred_y = infer_layer(x, y) # 1. prune y and loss - model_path = "model.multi_inout.output_spec2" - configs.output_spec = net.forward.outputs[:1] - fluid.dygraph.jit.save(net, model_path, [input_x], configs) + model_path = "multi_inout.output_spec2/model" + output_spec = net.forward.outputs[:1] + paddle.jit.save(net, model_path, [input_x], output_spec=output_spec) # 2. load again - infer_layer2 = fluid.dygraph.jit.load(model_path, configs=configs) + infer_layer2 = paddle.jit.load(model_path) # 3. predict pred_xx = infer_layer2(x) @@ -377,44 +373,6 @@ class TestJitSaveLoadConfig(unittest.TestCase): paddle.manual_seed(SEED) paddle.framework.random._manual_program_seed(SEED) - def basic_save_load(self, layer, model_path, configs): - # 1. train & save - example_inputs, train_layer, _ = train(layer) - fluid.dygraph.jit.save( - layer=train_layer, - model_path=model_path, - input_spec=example_inputs, - configs=configs) - # 2. load - infer_layer = fluid.dygraph.jit.load(model_path, configs=configs) - train_layer.eval() - # 3. inference & compare - x = fluid.dygraph.to_variable( - np.random.random((1, 784)).astype('float32')) - self.assertTrue( - np.array_equal(train_layer(x).numpy(), infer_layer(x).numpy())) - - def test_model_filename(self): - layer = LinearNet(784, 1) - model_path = "model.save_load_config.output_spec" - configs = fluid.dygraph.jit.SaveLoadConfig() - configs.model_filename = "__simplenet__" - self.basic_save_load(layer, model_path, configs) - - def test_params_filename(self): - layer = LinearNet(784, 1) - model_path = "model.save_load_config.params_filename" - configs = fluid.dygraph.jit.SaveLoadConfig() - configs.params_filename = "__params__" - self.basic_save_load(layer, model_path, configs) - - def test_separate_params(self): - layer = LinearNet(784, 1) - model_path = "model.save_load_config.separate_params" - configs = fluid.dygraph.jit.SaveLoadConfig() - configs.separate_params = True - self.basic_save_load(layer, model_path, configs) - def test_output_spec(self): train_layer = LinearNetReturnLoss(8, 8) adam = fluid.optimizer.AdamOptimizer( @@ -427,27 +385,47 @@ class TestJitSaveLoadConfig(unittest.TestCase): adam.minimize(loss) train_layer.clear_gradients() - model_path = "model.save_load_config.output_spec" - configs = fluid.dygraph.jit.SaveLoadConfig() - configs.output_spec = [out] - fluid.dygraph.jit.save( + model_path = "save_load_config.output_spec" + output_spec = [out] + paddle.jit.save( layer=train_layer, - model_path=model_path, + path=model_path, input_spec=[x], - configs=configs) + output_spec=output_spec) train_layer.eval() - infer_layer = fluid.dygraph.jit.load(model_path, configs=configs) + infer_layer = paddle.jit.load(model_path) x = fluid.dygraph.to_variable( np.random.random((4, 8)).astype('float32')) self.assertTrue( np.array_equal(train_layer(x)[0].numpy(), infer_layer(x).numpy())) + def test_save_no_support_config_error(self): + layer = LinearNet(784, 1) + path = "no_support_config_test" + with self.assertRaises(ValueError): + paddle.jit.save(layer=layer, path=path, model_filename="") + + def test_load_empty_model_filename_error(self): + path = "error_model_filename_test" + with self.assertRaises(ValueError): + paddle.jit.load(path, model_filename="") + + def test_load_empty_params_filename_error(self): + path = "error_params_filename_test" + with self.assertRaises(ValueError): + paddle.jit.load(path, params_filename="") + + def test_load_with_no_support_config(self): + path = "no_support_config_test" + with self.assertRaises(ValueError): + paddle.jit.load(path, separate_params=True) + class TestJitMultipleLoading(unittest.TestCase): def setUp(self): self.linear_size = 4 - self.model_path = "model.jit_multi_load" + self.model_path = "jit_multi_load/model" # enable dygraph mode fluid.enable_dygraph() # config seed @@ -459,8 +437,8 @@ class TestJitMultipleLoading(unittest.TestCase): def train_and_save_orig_model(self): layer = LinearNet(self.linear_size, self.linear_size) example_inputs, layer, _ = train(layer, self.linear_size, 1) - fluid.dygraph.jit.save( - layer=layer, model_path=self.model_path, input_spec=example_inputs) + paddle.jit.save( + layer=layer, path=self.model_path, input_spec=example_inputs) def test_load_model_retransform_inference(self): multi_loaded_layer = MultiLoadingLinearNet(self.linear_size, @@ -475,7 +453,7 @@ class TestJitMultipleLoading(unittest.TestCase): class TestJitPruneModelAndLoad(unittest.TestCase): def setUp(self): self.linear_size = 4 - self.model_path = "model.jit_prune_model_and_load" + self.model_path = "jit_prune_model_and_load/model" # enable dygraph mode fluid.enable_dygraph() # config seed @@ -494,13 +472,12 @@ class TestJitPruneModelAndLoad(unittest.TestCase): adam.minimize(loss) train_layer.clear_gradients() - configs = fluid.dygraph.jit.SaveLoadConfig() - configs.output_spec = [hidden] - fluid.dygraph.jit.save( + output_spec = [hidden] + paddle.jit.save( layer=train_layer, - model_path=self.model_path, + path=self.model_path, input_spec=[x], - configs=configs) + output_spec=output_spec) return train_layer @@ -508,7 +485,7 @@ class TestJitPruneModelAndLoad(unittest.TestCase): train_layer = self.train_and_save() train_layer.eval() - infer_layer = fluid.dygraph.jit.load(self.model_path) + infer_layer = paddle.jit.load(self.model_path) x = fluid.dygraph.to_variable( np.random.random((4, 8)).astype('float32')) @@ -519,7 +496,7 @@ class TestJitPruneModelAndLoad(unittest.TestCase): self.train_and_save() # chage extra var info - var_info_path = os.path.join(self.model_path, EXTRA_VAR_INFO_FILENAME) + var_info_path = self.model_path + INFER_PARAMS_INFO_SUFFIX with open(var_info_path, 'rb') as f: extra_var_info = pickle.load(f) extra_var_info.clear() @@ -527,7 +504,7 @@ class TestJitPruneModelAndLoad(unittest.TestCase): pickle.dump(extra_var_info, f, protocol=2) with self.assertRaises(RuntimeError): - fluid.dygraph.jit.load(self.model_path) + paddle.jit.load(self.model_path) class TestJitSaveMultiCases(unittest.TestCase): @@ -561,7 +538,7 @@ class TestJitSaveMultiCases(unittest.TestCase): train(layer) - model_path = "test_no_prune_to_static_after_train" + model_path = "test_no_prune_to_static_after_train/model" paddle.jit.save(layer, model_path) self.verify_inference_correctness(layer, model_path) @@ -569,7 +546,7 @@ class TestJitSaveMultiCases(unittest.TestCase): def test_no_prune_to_static_no_train(self): layer = LinearNetWithInputSpec(784, 1) - model_path = "test_no_prune_to_static_no_train" + model_path = "test_no_prune_to_static_no_train/model" paddle.jit.save(layer, model_path) self.verify_inference_correctness(layer, model_path) @@ -579,7 +556,7 @@ class TestJitSaveMultiCases(unittest.TestCase): train(layer) - model_path = "test_no_prune_no_to_static_after_train" + model_path = "test_no_prune_no_to_static_after_train/model" paddle.jit.save( layer, model_path, @@ -593,16 +570,15 @@ class TestJitSaveMultiCases(unittest.TestCase): example_inputs, _, _ = train(layer) - model_path = "test_no_prune_no_to_static_after_train_with_examples" - fluid.dygraph.jit.save( - layer=layer, model_path=model_path, input_spec=example_inputs) + model_path = "test_no_prune_no_to_static_after_train_with_examples/model" + paddle.jit.save(layer=layer, path=model_path, input_spec=example_inputs) self.verify_inference_correctness(layer, model_path) def test_no_prune_no_to_static_no_train(self): layer = LinearNetNotDeclarative(784, 1) - model_path = "test_no_prune_no_to_static_no_train" + model_path = "test_no_prune_no_to_static_no_train/model" paddle.jit.save( layer, model_path, @@ -616,9 +592,7 @@ class TestJitSaveMultiCases(unittest.TestCase): out = train_with_label(layer) - model_path = "test_prune_to_static_after_train" - configs = paddle.SaveLoadConfig() - configs.output_spec = [out] + model_path = "test_prune_to_static_after_train/model" paddle.jit.save( layer, model_path, @@ -626,18 +600,17 @@ class TestJitSaveMultiCases(unittest.TestCase): InputSpec( shape=[None, 784], dtype='float32', name="image") ], - configs=configs) + output_spec=[out]) self.verify_inference_correctness(layer, model_path, True) def test_prune_to_static_no_train(self): layer = LinerNetWithLabel(784, 1) - model_path = "test_prune_to_static_no_train" - configs = paddle.SaveLoadConfig() + model_path = "test_prune_to_static_no_train/model" # TODO: no train, cannot get output_spec var here # now only can use index - configs.output_spec = layer.forward.outputs[:1] + output_spec = layer.forward.outputs[:1] paddle.jit.save( layer, model_path, @@ -645,7 +618,7 @@ class TestJitSaveMultiCases(unittest.TestCase): InputSpec( shape=[None, 784], dtype='float32', name="image") ], - configs=configs) + output_spec=output_spec) self.verify_inference_correctness(layer, model_path, True) @@ -654,7 +627,7 @@ class TestJitSaveMultiCases(unittest.TestCase): train(layer) - model_path = "test_no_prune_input_spec_name_warning" + model_path = "test_no_prune_input_spec_name_warning/model" paddle.jit.save( layer, model_path, @@ -675,18 +648,16 @@ class TestJitSaveMultiCases(unittest.TestCase): train(layer) - model_path = "test_not_prune_output_spec_name_warning" - configs = paddle.SaveLoadConfig() + model_path = "test_not_prune_output_spec_name_warning/model" out = paddle.to_tensor(np.random.random((1, 1)).astype('float')) - configs.output_spec = [out] - paddle.jit.save(layer, model_path, configs=configs) + paddle.jit.save(layer, model_path, output_spec=[out]) self.verify_inference_correctness(layer, model_path) def test_prune_input_spec_name_error(self): layer = LinerNetWithLabel(784, 1) - model_path = "test_prune_input_spec_name_error" + model_path = "test_prune_input_spec_name_error/model" with self.assertRaises(ValueError): paddle.jit.save( layer, @@ -707,10 +678,8 @@ class TestJitSaveMultiCases(unittest.TestCase): train_with_label(layer) - model_path = "test_prune_to_static_after_train" - configs = paddle.SaveLoadConfig() + model_path = "test_prune_to_static_after_train/model" out = paddle.to_tensor(np.random.random((1, 1)).astype('float')) - configs.output_spec = [out] with self.assertRaises(ValueError): paddle.jit.save( layer, @@ -719,12 +688,12 @@ class TestJitSaveMultiCases(unittest.TestCase): InputSpec( shape=[None, 784], dtype='float32', name="image") ], - configs=configs) + output_spec=[out]) class TestJitSaveLoadEmptyLayer(unittest.TestCase): def setUp(self): - self.model_path = "model.jit_save_load_empty_layer" + self.model_path = "jit_save_load_empty_layer/model" # enable dygraph mode paddle.disable_static() @@ -740,7 +709,7 @@ class TestJitSaveLoadEmptyLayer(unittest.TestCase): class TestJitSaveLoadNoParamLayer(unittest.TestCase): def setUp(self): - self.model_path = "model.jit_save_load_no_param_layer" + self.model_path = "jit_save_load_no_param_layer/model" # enable dygraph mode paddle.disable_static() diff --git a/python/paddle/fluid/tests/unittests/test_load_state_dict_from_old_format.py b/python/paddle/fluid/tests/unittests/test_load_state_dict_from_old_format.py index fdc1e6b52aba1d5189a759d748ad198c080b4609..35ad6fdb30e7ba8792633934c39736f78b62ffeb 100644 --- a/python/paddle/fluid/tests/unittests/test_load_state_dict_from_old_format.py +++ b/python/paddle/fluid/tests/unittests/test_load_state_dict_from_old_format.py @@ -63,6 +63,8 @@ class TestLoadStateDictFromSaveInferenceModel(unittest.TestCase): self.epoch_num = 1 self.batch_size = 128 self.batch_num = 10 + # enable static mode + paddle.enable_static() def train_and_save_model(self, only_params=False): with new_program_scope(): @@ -136,13 +138,12 @@ class TestLoadStateDictFromSaveInferenceModel(unittest.TestCase): self.params_filename = None orig_param_dict = self.train_and_save_model() - config = paddle.SaveLoadConfig() - config.separate_params = True - config.model_filename = self.model_filename - load_param_dict, _ = fluid.load_dygraph(self.save_dirname, config) + load_param_dict, _ = fluid.load_dygraph( + self.save_dirname, model_filename=self.model_filename) self.check_load_state_dict(orig_param_dict, load_param_dict) - new_load_param_dict = paddle.load(self.save_dirname, config) + new_load_param_dict = paddle.load( + self.save_dirname, model_filename=self.model_filename) self.check_load_state_dict(orig_param_dict, new_load_param_dict) def test_load_with_param_filename(self): @@ -151,12 +152,12 @@ class TestLoadStateDictFromSaveInferenceModel(unittest.TestCase): self.params_filename = "static_mnist.params" orig_param_dict = self.train_and_save_model() - config = paddle.SaveLoadConfig() - config.params_filename = self.params_filename - load_param_dict, _ = fluid.load_dygraph(self.save_dirname, config) + load_param_dict, _ = fluid.load_dygraph( + self.save_dirname, params_filename=self.params_filename) self.check_load_state_dict(orig_param_dict, load_param_dict) - new_load_param_dict = paddle.load(self.save_dirname, config) + new_load_param_dict = paddle.load( + self.save_dirname, params_filename=self.params_filename) self.check_load_state_dict(orig_param_dict, new_load_param_dict) def test_load_with_model_and_param_filename(self): @@ -165,13 +166,16 @@ class TestLoadStateDictFromSaveInferenceModel(unittest.TestCase): self.params_filename = "static_mnist.params" orig_param_dict = self.train_and_save_model() - config = paddle.SaveLoadConfig() - config.params_filename = self.params_filename - config.model_filename = self.model_filename - load_param_dict, _ = fluid.load_dygraph(self.save_dirname, config) + load_param_dict, _ = fluid.load_dygraph( + self.save_dirname, + params_filename=self.params_filename, + model_filename=self.model_filename) self.check_load_state_dict(orig_param_dict, load_param_dict) - new_load_param_dict = paddle.load(self.save_dirname, config) + new_load_param_dict = paddle.load( + self.save_dirname, + params_filename=self.params_filename, + model_filename=self.model_filename) self.check_load_state_dict(orig_param_dict, new_load_param_dict) def test_load_state_dict_from_save_params(self): diff --git a/python/paddle/framework/__init__.py b/python/paddle/framework/__init__.py index 2ce442add2e02b5b1b869b4b233e734bc29bdea9..7e2f0eb2fb8bb14b9ebc6a023bb68283b25a5e33 100644 --- a/python/paddle/framework/__init__.py +++ b/python/paddle/framework/__init__.py @@ -20,8 +20,8 @@ __all__ = [ ] __all__ += [ - 'grad', 'LayerList', 'load', 'save', 'SaveLoadConfig', 'to_variable', - 'no_grad', 'DataParallel' + 'grad', 'LayerList', 'load', 'save', 'to_variable', 'no_grad', + 'DataParallel' ] __all__ += [ @@ -50,7 +50,6 @@ from ..fluid.dygraph.base import to_variable #DEFINE_ALIAS from ..fluid.dygraph.base import grad #DEFINE_ALIAS from .io import save from .io import load -from ..fluid.dygraph.jit import SaveLoadConfig #DEFINE_ALIAS from ..fluid.dygraph.parallel import DataParallel #DEFINE_ALIAS from ..fluid.dygraph.learning_rate_scheduler import NoamDecay #DEFINE_ALIAS diff --git a/python/paddle/framework/io.py b/python/paddle/framework/io.py index 7175f3101448f3ee239c641fe0142ef211510a72..c196c1d689bfe2f8bdc4a2b0434e864d5fcf2d75 100644 --- a/python/paddle/framework/io.py +++ b/python/paddle/framework/io.py @@ -26,7 +26,9 @@ import paddle from paddle import fluid from paddle.fluid import core from paddle.fluid.framework import Variable, _varbase_creator, _dygraph_tracer -from paddle.fluid.dygraph.io import _construct_program_holders, _construct_params_and_buffers, EXTRA_VAR_INFO_FILENAME +from paddle.fluid.dygraph.jit import _SaveLoadConfig +from paddle.fluid.dygraph.io import _construct_program_holders, _construct_params_and_buffers +from paddle.fluid.dygraph.io import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX, INFER_PARAMS_INFO_SUFFIX __all__ = [ 'save', @@ -55,19 +57,16 @@ def _load_state_dict_from_save_inference_model(model_path, config): # 2. load layer parameters & buffers with fluid.dygraph.guard(): persistable_var_dict = _construct_params_and_buffers( - model_path, - programs, - config.separate_params, - config.params_filename, - append_suffix=False) + model_path, programs, config.params_filename, append_suffix=False) # 3. construct state_dict load_param_dict = dict() for var_name in persistable_var_dict: load_param_dict[var_name] = persistable_var_dict[var_name].numpy() - # if __variables.info__ exists, we can recover structured_name - var_info_path = os.path.join(model_path, EXTRA_VAR_INFO_FILENAME) + # if *.info exists, we can recover structured_name + var_info_filename = str(config.params_filename) + ".info" + var_info_path = os.path.join(model_path, var_info_filename) if os.path.exists(var_info_path): with open(var_info_path, 'rb') as f: extra_var_info = pickle.load(f) @@ -116,12 +115,99 @@ def _load_state_dict_from_save_params(model_path): return load_param_dict +# NOTE(chenweihang): [ Handling of use cases of API paddle.load ] +# `paddle.load` may be used to load saved results of: +# 1. Expected cases: +# - need [full filename] when loading +# - paddle.save +# - paddle.static.save +# - paddle.fluid.save_dygraph +# - need [prefix] when loading [compatible for paddle 2.x] +# - paddle.jit.save +# - paddle.static.save_inference_model +# - need [directory] when loading [compatible for paddle 1.x] +# - paddle.fluid.io.save_inference_model +# - paddle.fluid.io.save_params/save_persistable +# 2. Error cases: +# - no error case +def _build_load_path_and_config(path, config): + # NOTE(chenweihang): If both [prefix save format] and [directory save format] exist, + # raise error, avoid confusing behavior + prefix_format_path = path + INFER_MODEL_SUFFIX + prefix_format_exist = os.path.exists(prefix_format_path) + directory_format_exist = os.path.isdir(path) + if prefix_format_exist and directory_format_exist: + raise ValueError( + "The %s.pdmodel and %s directory exist at the same time, " + "don't know which one to load, please make sure that the specified target " + "of ``path`` is unique." % (path, path)) + elif not prefix_format_exist and not directory_format_exist: + error_msg = "The ``path`` (%s) to load model not exists." + # if current path is a prefix, and the path.pdparams or path.pdopt + # is exist, users may want use `paddle.load` load the result of + # `fluid.save_dygraph`, we raise error here for users + params_file_path = path + ".pdparams" + opti_file_path = path + ".pdopt" + if os.path.exists(params_file_path) or os.path.exists(opti_file_path): + error_msg += " If you want to load the results saved by `fluid.save_dygraph`, " \ + "please specify the full file name, not just the file name prefix. For " \ + "example, it should be written as `paddle.load('model.pdparams')` instead of " \ + "`paddle.load('model')`." + raise ValueError(error_msg % path) + else: + if prefix_format_exist: + file_prefix = os.path.basename(path) + model_path = os.path.dirname(path) + if config.model_filename is not None: + warnings.warn( + "When loading the result saved with the " + "specified file prefix, the ``model_filename`` config does " + "not take effect.") + config.model_filename = file_prefix + INFER_MODEL_SUFFIX + if config.params_filename is not None: + warnings.warn( + "When loading the result saved with the " + "specified file prefix, the ``params_filename`` config does " + "not take effect.") + config.params_filename = file_prefix + INFER_PARAMS_SUFFIX + else: + # Compatible with the old save_inference_model format + model_path = path + + return model_path, config + + +def _parse_load_config(configs): + supported_configs = ['model_filename', 'params_filename', 'keep_name_table'] + + # input check + for key in configs: + if key not in supported_configs: + raise ValueError( + "The additional config (%s) of `paddle.load` is not supported." + % key) + + # construct inner config + inner_config = _SaveLoadConfig() + inner_config.model_filename = configs.get('model_filename', None) + inner_config.params_filename = configs.get('params_filename', None) + inner_config.keep_name_table = configs.get('keep_name_table', None) + + return inner_config + + def save(obj, path): ''' Save an object to the specified path. .. note:: Now only supports save ``state_dict`` of Layer or Optimizer. + + .. note:: + ``paddle.save`` will not add a suffix to the saved results, + but we recommend that you use the following paddle standard suffixes: + 1. for ``Layer.state_dict`` -> ``.pdparams`` + 2. for ``Optimizer.state_dict`` -> ``.pdopt`` Args: obj(Object) : The object to be saved. @@ -178,7 +264,7 @@ def save(obj, path): pickle.dump(saved_obj, f, protocol=2) -def load(path, config=None): +def load(path, **configs): ''' Load an object can be used in paddle from specified path. @@ -186,21 +272,39 @@ def load(path, config=None): Now only supports load ``state_dict`` of Layer or Optimizer. .. note:: - ``paddle.load`` supports loading ``state_dict`` from the result of several - paddle1.x save APIs in static mode, but due to some historical reasons, - if you load ``state_dict`` from the saved result of - ``paddle.static.save_inference_model/paddle.fluid.io.save_params/paddle.fluid.io.save_persistables`` , + ``paddle.load`` supports loading ``state_dict`` of Layer or Optimizer from + the result of other save APIs except ``paddle.load`` , but the argument + ``path`` format is different: + 1. loading from ``paddle.static.save`` or ``paddle.Model().save(training=True)`` , + ``path`` needs to be a complete file name, such as ``model.pdparams`` or + ``model.pdopt`` ; + 2. loading from ``paddle.jit.save`` or ``paddle.static.save_inference_model`` + or ``paddle.Model().save(training=False)`` , ``path`` need to be a file prefix, + such as ``model/mnist``, and ``paddle.load`` will get information from + ``mnist.pdmodel`` and ``mnist.pdiparams`` ; + 3. loading from paddle 1.x APIs ``paddle.fluid.io.save_inference_model`` or + ``paddle.fluid.io.save_params/save_persistables`` , ``path`` need to be a + directory, such as ``model`` and model is a directory. + + .. note:: + If you load ``state_dict`` from the saved result of + ``paddle.static.save`` or ``paddle.static.save_inference_model`` , the structured variable name will cannot be restored. You need to set the argument ``use_structured_name=False`` when using ``Layer.set_state_dict`` later. Args: path(str) : The path to load the target object. Generally, the path is the target - file path, when compatible with loading the saved results of - ``paddle.jit.save/paddle.static.save_inference_model`` , the path is a directory. - config (SaveLoadConfig, optional): :ref:`api_imperative_jit_saveLoadConfig` - object that specifies additional configuration options, these options - are for compatibility with ``paddle.jit.save/paddle.static.save_inference_model`` - formats. Default None. + file path. When compatible with loading the saved results other APIs, the path + can be a file prefix or directory. + **configs (dict, optional): other load configuration options for compatibility. We do not + recommend using these configurations, they may be removed in the future. If not necessary, + DO NOT use them. Default None. + The following options are currently supported: + (1) model_filename (string): The inference model file name of the paddle 1.x + ``save_inference_model`` save format. Default file name is :code:`__model__` . + (2) params_filename (string): The persistable variables file name of the paddle 1.x + ``save_inference_model`` save format. No default file name, save variables separately + by default. Returns: Object(Object): a target object can be used in paddle @@ -227,26 +331,9 @@ def load(path, config=None): load_layer_state_dict = paddle.load("emb.pdparams") load_opt_state_dict = paddle.load("adam.pdopt") ''' - # 1. input check - if not os.path.exists(path): - error_msg = "The path `%s` does not exist." - # if current path is a prefix, and the path.pdparams or path.pdopt - # is exist, users may want use `paddle.load` load the result of - # `fluid.save_dygraph`, we raise error here for users - params_file_path = path + ".pdparams" - opti_file_path = path + ".pdopt" - if os.path.exists(params_file_path) or os.path.exists(opti_file_path): - error_msg += " If you want to load the results saved by `fluid.save_dygraph`, " \ - "please specify the full file name, not just the file name prefix. For " \ - "example, it should be written as `paddle.load('model.pdparams')` instead of " \ - "`paddle.load('model')`." - raise ValueError(error_msg % path) - - if config is None: - config = paddle.SaveLoadConfig() - - # 2. load target load_result = None + config = _parse_load_config(configs) + if os.path.isfile(path): # we think path is file means this file is created by paddle.save with open(path, 'rb') as f: @@ -255,16 +342,15 @@ def load(path, config=None): if not config.keep_name_table and "StructuredToParameterName@@" in load_result: del load_result["StructuredToParameterName@@"] - elif os.path.isdir(path): - # we think path is directory means compatible with loading - # store results of static mode related save APIs - + else: + # file prefix and directory are compatible cases + model_path, config = _build_load_path_and_config(path, config) # check whether model file exists if config.model_filename is None: model_filename = '__model__' else: model_filename = config.model_filename - model_file_path = os.path.join(path, model_filename) + model_file_path = os.path.join(model_path, model_filename) if os.path.exists(model_file_path): # Load state dict by `jit.save/io.save_inference_model` save format @@ -274,7 +360,7 @@ def load(path, config=None): # `save_inference_model` not save structured name, we need to remind # the user to configure the `use_structured_name` argument when `set_state_dict` # NOTE(chenweihang): `jit.save` doesn't save optimizer state - load_result = _load_state_dict_from_save_inference_model(path, + load_result = _load_state_dict_from_save_inference_model(model_path, config) else: # load state dict by `io.save_params/persistables` save format @@ -283,9 +369,6 @@ def load(path, config=None): # mapping info will lost, so users need to give variable list, but users build # variable list in dygraph mode is difficult, we recommend users to use # paddle.static.load_program_state in this case - load_result = _load_state_dict_from_save_params(path) - else: - raise ValueError( - "Unsupported path format, now only supports file or directory.") + load_result = _load_state_dict_from_save_params(model_path) return load_result