-
Notifications
You must be signed in to change notification settings - Fork 6k
Update save inference model to support dygraph #25894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
guoshengCS
merged 51 commits into
PaddlePaddle:develop
from
LiuChiachi:update_save_inference_model_to_support_dygraph
Aug 25, 2020
Merged
Changes from 48 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
463359e
update save_inference_model for hapi
LiuChiachi 4709a25
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi b595db4
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi db663c4
update save_inference_model to support dygraph
LiuChiachi b7850ed
fix comments
LiuChiachi 314e3e2
fix comments
LiuChiachi 2ffcddc
test=develop
LiuChiachi 196df6a
test, test=develop
LiuChiachi 6caa61b
fix dim test, test=develop
LiuChiachi c625a50
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 8f6cb8f
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi c7cc35f
test, test=develop
LiuChiachi 753aef9
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 86adb71
add test_export_deploy_model_dynamic
LiuChiachi 43bd7d2
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 08688f5
fix unittest for hapi: save_inference_model
LiuChiachi c61da24
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 0939851
fix code style
LiuChiachi 25efe73
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi cca0c0a
accept review by guoshengCS
LiuChiachi 92ab5d9
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 87aaf3c
fix coverage rate
LiuChiachi 19f5ec1
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi d45488e
update doc for save_inference_model and copyright
LiuChiachi 4f6fda6
change test model back to LeNet() in test_export_deploy_model
LiuChiachi 7d3bf0f
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 743f249
copy jit.save, use LeNet() to test export deploy model
LiuChiachi 103f8bf
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 5777091
add return value for dygraph, and fix doc error
LiuChiachi 9d1be86
corrected the doc writing
LiuChiachi 5fef294
Delete redundant import and correct import order in sample code.
LiuChiachi 0ee2da7
remove 'fluid' and add prepare() and fit() in sample code
LiuChiachi f569de7
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 5cd1d8a
correct usage of API 2.0 in sample code
LiuChiachi 1453d03
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi a4e44af
solve conflicts and change name of save_inference_model for hapi
LiuChiachi fa11228
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi ead375a
fix sample code bugs
LiuChiachi d01442d
fix code style bugs
LiuChiachi eeb08bb
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 4f1223d
fix test_model.py bugs
LiuChiachi aa96150
set for_inference=True
LiuChiachi 869bc19
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 26fcde3
correct usage for static.InputSpec
LiuChiachi 2d29fac
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 04b2d5a
update doc for model.save
LiuChiachi 3777dc7
correct usage of API 2.0
LiuChiachi 3e6f384
Merge branch 'develop' into update_save_inference_model_to_support_dy…
LiuChiachi 9dffc17
rename param name for model.save
LiuChiachi 1e4aea5
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
LiuChiachi 92d280c
correct for_inference as training
LiuChiachi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| # Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. | ||
| # Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
|
|
@@ -26,19 +26,25 @@ | |
|
|
||
| import paddle | ||
| from paddle import fluid | ||
| from paddle.fluid import core | ||
| from paddle.fluid.framework import in_dygraph_mode, Variable, ParamBase, _current_expected_place | ||
| # Note: Use alias `Input` temporarily before releasing hapi feature. | ||
| from paddle.static import InputSpec as Input | ||
| from paddle.fluid.framework import in_dygraph_mode, Variable | ||
| from paddle.fluid.executor import global_scope | ||
| from paddle.fluid.io import is_belong_to_optimizer | ||
| from paddle.fluid.dygraph.base import to_variable | ||
| from paddle.fluid.dygraph.parallel import ParallelEnv | ||
| from paddle.fluid.dygraph.dygraph_to_static.program_translator import ProgramTranslator, FunctionSpec | ||
| from paddle.fluid.layers.utils import flatten | ||
| from paddle.fluid.incubate.fleet.collective import fleet, DistributedStrategy | ||
| from paddle.fluid.incubate.fleet.base import role_maker | ||
| from paddle.fluid.executor import scope_guard, Executor | ||
| from paddle.io import DataLoader, Dataset | ||
|
|
||
| from paddle.fluid.dygraph.layers import Layer | ||
| from paddle.metric import Metric | ||
|
|
||
|
|
||
| from .distributed import DistributedBatchSampler, _all_gather, prepare_distributed_context, _parallel_context_initialized | ||
| from .callbacks import config_callbacks | ||
| from .utils import to_list, to_numpy, flatten_list, restore_flatten_list, extract_args | ||
|
|
@@ -846,50 +852,80 @@ def forward(self, x): | |
| """ | ||
| return self._adapter.test_batch(inputs) | ||
|
|
||
| def save(self, path): | ||
| """ | ||
| This function saves parameters, optimizer infomation to path. | ||
| def save(self, path, for_inference=False): | ||
| """ | ||
| This function saves parameters, optimizer information or model and | ||
| paramters only for inference to path. It depends on the parameter | ||
| `for_inference`. | ||
|
|
||
| The parameters contains all the trainable Variable, will save to | ||
| a file with suffix ".pdparams". | ||
| If `for_inference` is set to False, the parameters saved contain all | ||
| the trainable Variable, will save to a file with suffix ".pdparams". | ||
| The optimizer information contains all the variable used by optimizer. | ||
| For Adam optimizer, contains beta1, beta2, momentum etc. All the | ||
| information will save to a file with suffix ".pdopt". (If the optimizer | ||
| have no variable need to save (like SGD), the fill will not generated). | ||
| This function will silently overwrite existing file at the target location. | ||
|
|
||
| This function will silently overwrite existing file | ||
| at the target location. | ||
| If `for_inference` is set to True, only inference model will be saved. It | ||
| should be noted that before using `save`, you should run the model, and | ||
| the shape of input you saved is as same as the input of its running. | ||
| `@paddle.jit.to_static` must be added on `forward` function of your layer | ||
| in dynamic mode now and these will be optimized later. | ||
|
|
||
| Args: | ||
| path (str): The file prefix to save model. The format is | ||
| 'dirname/file_prefix' or 'file_prefix'. if empty str. A exception | ||
| will be raised. | ||
| for_inference (bool, optional): Whether to save inference model only. | ||
| Default: False. | ||
|
|
||
| Returns: | ||
| None | ||
|
|
||
| Examples: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| import paddle | ||
| import paddle.incubate.hapi as hapi | ||
|
|
||
| class MyNet(paddle.nn.Layer): | ||
| def __init__(self): | ||
| super(MyNet, self).__init__() | ||
| self._fc = paddle.nn.Linear(784, 1, act='softmax') | ||
| import paddle | ||
| import paddle.incubate.hapi as hapi | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 后续需要按照我们的新目录更新下 |
||
| from paddle.nn import Linear | ||
| from paddle.incubate.hapi.datasets.mnist import MNIST as MnistDataset | ||
|
|
||
| class Mnist(paddle.nn.Layer): | ||
| def __init__(self): | ||
| super(MyNet, self).__init__() | ||
| self._fc = Linear(784, 1, act='softmax') | ||
|
|
||
| @paddle.jit.to_static # If save for inference in dygraph, need this | ||
| def forward(self, x): | ||
| y = self._fc(x) | ||
| return y | ||
|
|
||
| device = hapi.set_device('cpu') | ||
| paddle.disable_static(device) | ||
| model = hapi.Model(MyNet()) | ||
| model.save('checkpoint/test') | ||
|
|
||
| dynamic = True # False | ||
| device = hapi.set_device('cpu') | ||
| # if use static graph, do not set | ||
| paddle.disable_static(device) if dynamic else None | ||
|
|
||
| # inputs and labels are not required for dynamic graph. | ||
| input = hapi.Input([None, 784], 'float32', 'x') | ||
| label = hapi.Input([None, 1], 'int64', 'label') | ||
|
|
||
| model = hapi.Model(Mnist(), input, label) | ||
| optim = paddle.optimizer.SGD(learning_rate=1e-3, | ||
| parameter_list=model.parameters()) | ||
| model.prepare(optim, | ||
| paddle.nn.CrossEntropyLoss(), | ||
| hapi.metrics.Accuracy()) | ||
| mnist_data = hapi.datasets.MNIST(mode='train', chw_format=False) | ||
| model.fit(mnist_data, epochs=1, batch_size=32, verbose=0) | ||
| model.save('checkpoint/test') # save checkpoint | ||
| model.save('inference_model', True) # save for inference | ||
| """ | ||
|
|
||
| if ParallelEnv().local_rank == 0: | ||
| self._adapter.save(path) | ||
| if for_inference: | ||
| self._save_inference_model(path) | ||
| else: | ||
| self._adapter.save(path) | ||
|
|
||
| def load(self, path, skip_mismatch=False, reset_optimizer=False): | ||
| """ | ||
|
|
@@ -1474,13 +1510,17 @@ def __len__(self): | |
| cbks.on_end('test', logs) | ||
| return outputs | ||
|
|
||
| def save_inference_model(self, | ||
| save_dir, | ||
| model_filename=None, | ||
| params_filename=None, | ||
| model_only=False): | ||
| def _save_inference_model(self, | ||
| save_dir, | ||
| model_filename=None, | ||
| params_filename=None, | ||
| model_only=False): | ||
| """ | ||
| Save inference model must in static mode. | ||
| Save inference model can be in static or dynamic mode. | ||
| It should be noted that before using `save_inference_model`, you should | ||
| run the model, and the shape you saved is as same as the input of its | ||
| running. `@paddle.jit.to_static` must be added on `forward` function of | ||
| your layer in dynamic mode now and these will be optimized later. | ||
|
|
||
| Args: | ||
| save_dir (str): The directory path to save the inference model. | ||
|
|
@@ -1496,39 +1536,145 @@ def save_inference_model(self, | |
| Returns: | ||
| list: The fetch variables' name list | ||
|
|
||
|
|
||
| Examples: | ||
| .. code-block:: python | ||
| import numpy as np | ||
| import paddle | ||
| from paddle.static import InputSpec | ||
|
|
||
| import paddle.incubate.hapi as hapi | ||
|
|
||
| input = hapi.Input([-1, 1, 28, 28], 'float32', 'image') | ||
| model = hapi.Model(hapi.vision.LeNet(), input) | ||
| model.prepare() | ||
|
|
||
| from paddle.nn import Linear | ||
| from paddle.incubate.hapi.datasets.mnist import MNIST as MnistDataset | ||
|
|
||
| class Mnist(Layer): | ||
| def __init__(self, classifier_act=None): | ||
| super(Mnist, self).__init__() | ||
|
|
||
| self.fc = Linear(input_dim=784, output_dim=10, act="softmax") | ||
|
|
||
| @paddle.jit.to_static # In static mode, you need to delete this. | ||
| def forward(self, inputs): | ||
| outputs = self.fc(inputs) | ||
| return outputs | ||
|
|
||
| dynamic = True # False | ||
| device = hapi.set_device('gpu') | ||
|
|
||
| # if use static graph, do not set | ||
| paddle.disable_static(device) if dynamic else None | ||
|
|
||
| # inputs and labels are not required for dynamic graph. | ||
| input = InputSpec('x', [None, 784], 'float32') | ||
| label = InputSpec('label', [None, 1], 'int64') | ||
|
|
||
| model = hapi.Model(Mnist(), input, label) | ||
| optim = paddle.optimizer.SGD(learning_rate=1e-3, | ||
| parameter_list=model.parameters()) | ||
| model.prepare(optim, | ||
| paddle.nn.CrossEntropyLoss(), | ||
| hapi.metrics.Accuracy()) | ||
| mnist_data = hapi.datasets.MNIST(mode='train', chw_format=False) | ||
| model.fit(mnist_data, epochs=1, batch_size=32, verbose=0) | ||
| model.save_inference_model('inference_model') | ||
| """ | ||
| assert not fluid.in_dygraph_mode( | ||
| ), 'Save inference model must in static mode!' | ||
|
|
||
| prog = self._adapter._progs.get('test', None) | ||
| assert prog, \ | ||
| "Model is not ready, please call `model.prepare()` first" | ||
| def get_inout_spec(all_vars, return_name=False): | ||
| result_list = [] | ||
| valid_vars = [var for var in all_vars if isinstance(var, Variable)] | ||
| result_list = valid_vars | ||
| if return_name: | ||
| result_list = [var.name for var in result_list] | ||
|
|
||
| infer_prog = prog.clone(for_test=True) | ||
| return result_list | ||
|
|
||
| input_names = [v.name for v in self._adapter._input_vars['test']] | ||
| endpoints = self._adapter._endpoints['test']['output'] | ||
| # TODO: | ||
| # 1. Make it Unnecessary to run model before calling `save_inference_model` for users in dygraph. | ||
| # 2. Save correct shape of input, now the interface stores the shape that the user sent to | ||
| # the inputs of the model in running. | ||
| # 3. Make it Unnecessary to add `@paddle.jit.to_static` for users in dynamic mode. | ||
| if fluid.in_dygraph_mode(): | ||
LiuChiachi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| layer = self.network | ||
| fluid.disable_dygraph() | ||
|
|
||
| # 1. input check | ||
| prog_translator = ProgramTranslator() | ||
| if not prog_translator.enable_declarative: | ||
| raise RuntimeError( | ||
| "save_inference_model doesn't work when setting ProgramTranslator.enable=False." | ||
| ) | ||
| if not isinstance(layer, Layer): | ||
| raise TypeError( | ||
| "The input layer should be 'Layer', but received layer type is %s." | ||
| % type(layer)) | ||
|
|
||
| # 2. get program of declarative Layer.forward | ||
| prog_cache = prog_translator.get_program_cache() | ||
| # make dummy args & kwargs, to get excepted FunctionSpec | ||
| layer_func = FunctionSpec(type(layer).forward, [layer], {}) | ||
| concrete_program, _ = prog_cache.get_program(layer_func) | ||
|
|
||
| # NOTE: we maintain the mapping of variable name to | ||
| # structured name, the buffer variable (non-persistable) | ||
| # saved to inference program may not need by dygraph Layer, | ||
| # we only record the state_dict variable's structured name | ||
| state_names_dict = dict() | ||
| for structured_name, var in layer.state_dict().items(): | ||
| state_names_dict[var.name] = structured_name | ||
|
|
||
| # 3. share parameters from Layer to scope & record var info | ||
| scope = core.Scope() | ||
| extra_var_info = dict() | ||
| for param_or_buffer in concrete_program.parameters: | ||
| # share to scope | ||
| param_or_buffer_tensor = scope.var( | ||
| param_or_buffer.name).get_tensor() | ||
| src_tensor = param_or_buffer.value().get_tensor() | ||
| param_or_buffer_tensor._share_data_with(src_tensor) | ||
| # record var info | ||
| extra_info_dict = dict() | ||
| if param_or_buffer.name in state_names_dict: | ||
| extra_info_dict['structured_name'] = state_names_dict[ | ||
| param_or_buffer.name] | ||
| extra_info_dict['stop_gradient'] = param_or_buffer.stop_gradient | ||
| if isinstance(param_or_buffer, ParamBase): | ||
| extra_info_dict['trainable'] = param_or_buffer.trainable | ||
| extra_var_info[param_or_buffer.name] = extra_info_dict | ||
|
|
||
| # 4. build input & output spec | ||
| input_var_names = get_inout_spec(concrete_program.inputs, True) | ||
| output_vars = get_inout_spec(concrete_program.outputs) | ||
LiuChiachi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # 5. save inference model | ||
| with scope_guard(scope): | ||
| return fluid.io.save_inference_model( | ||
| dirname=save_dir, | ||
| feeded_var_names=input_var_names, | ||
| target_vars=output_vars, | ||
| executor=Executor(_current_expected_place()), | ||
| main_program=concrete_program.main_program.clone(), | ||
| model_filename=model_filename, | ||
| params_filename=params_filename, | ||
| program_only=model_only) | ||
|
|
||
| return fluid.io.save_inference_model( | ||
| save_dir, | ||
| input_names, | ||
| endpoints, | ||
| self._adapter._executor, | ||
| main_program=infer_prog, | ||
| model_filename=model_filename, | ||
| params_filename=params_filename, | ||
| program_only=model_only) | ||
| else: | ||
| prog = self._adapter._progs.get('test', None) | ||
| assert prog, \ | ||
| "Model is not ready, please call `model.prepare()` first" | ||
|
|
||
| infer_prog = prog.clone(for_test=True) | ||
|
|
||
| input_names = [v.name for v in self._adapter._input_vars['test']] | ||
| endpoints = self._adapter._endpoints['test']['output'] | ||
|
|
||
| return fluid.io.save_inference_model( | ||
| save_dir, | ||
| input_names, | ||
| endpoints, | ||
| self._adapter._executor, | ||
| main_program=infer_prog, | ||
| model_filename=model_filename, | ||
| params_filename=params_filename, | ||
| program_only=model_only) | ||
|
|
||
| def _run_one_epoch(self, data_loader, callbacks, mode, logs={}): | ||
| outputs = [] | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for_inference=False -> training=True
建议跟组网类的API的参数保持一致,用training这个参数标识训练和预测