From 4c5dead62b4d8465a5f5477c571c2965e5ab1e2a Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Fri, 25 Sep 2020 05:43:25 +0000 Subject: [PATCH 01/14] remove input requirment in dygraph Model --- python/paddle/hapi/model.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/python/paddle/hapi/model.py b/python/paddle/hapi/model.py index 53928ebed1b25f..f502a1dfc340de 100644 --- a/python/paddle/hapi/model.py +++ b/python/paddle/hapi/model.py @@ -792,14 +792,15 @@ class Model(object): switched by `paddle.disable_static()`. The usage is as follows. But note, the switching between dynamic and static should be before instantiating a Model. The input description, i.e, paddle.static.InputSpec, - must be required. + must be required for static graph. Args: network (paddle.nn.Layer): The network is an instance of paddle.nn.Layer. inputs (InputSpec|list|dict|None): `inputs`, entry points of network, could be a InputSpec instance, or lits of InputSpec instances, - or dict ({name: InputSpec}), and it couldn't be None. + or dict ({name: InputSpec}), and it couldn't be None in static + graph. labels (InputSpec|list|None): `labels`, entry points of network, could be a InputSpec instnace or lits of InputSpec instances, or None. For static graph, if labels is required in loss, @@ -848,9 +849,10 @@ def __init__(self, network, inputs=None, labels=None): self._optimizer = None self._test_dataloader = None - if not isinstance(inputs, (list, dict, Input)): - raise TypeError( - "'inputs' must be list or dict, and couldn't be None.") + if not in_dygraph_mode(): + if not isinstance(inputs, (list, dict, Input)): + raise TypeError( + "'inputs' must be list or dict, and couldn't be None.") self._inputs = self._verify_spec(inputs, True) self._labels = self._verify_spec(labels) @@ -1863,7 +1865,18 @@ def summary(self, input_size=None, dtype=None): def _verify_spec(self, specs, is_input=False): out_specs = [] - if isinstance(specs, dict): + if specs is None: + # Note(Aurelius84): If not specific specs of `Input`, using argument names of `forward` function + # to generate `Input`. But how can we know the actual shape of each input tensor? + if is_input: + out_specs = [ + Input( + name=n, shape=[None]) + for n in extract_args(self.network.forward) if n != 'self' + ] + else: + out_specs = to_list(specs) + elif isinstance(specs, dict): assert is_input == False out_specs = [specs[n] \ for n in extract_args(self.network.forward) if n != 'self'] From 5d2fad7b7938f4acf65988c07af02c260bbd34b2 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Fri, 25 Sep 2020 05:47:10 +0000 Subject: [PATCH 02/14] correct unittest --- python/paddle/tests/test_model.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python/paddle/tests/test_model.py b/python/paddle/tests/test_model.py index c89cbbbfbdae9e..b20dfa37b665a0 100644 --- a/python/paddle/tests/test_model.py +++ b/python/paddle/tests/test_model.py @@ -415,9 +415,7 @@ def test_dynamic_save_static_load(self): # dynamic saving device = paddle.set_device('cpu') fluid.enable_dygraph(device) - inputs = [InputSpec([None, 20], 'float32', 'x')] - labels = [InputSpec([None, 1], 'int64', 'label')] - model = Model(MyModel(classifier_activation=None), inputs, labels) + model = Model(MyModel(classifier_activation=None)) optim = fluid.optimizer.SGD(learning_rate=0.001, parameter_list=model.parameters()) model.prepare(optimizer=optim, loss=CrossEntropyLoss(reduction="sum")) From f7f4063521921ff1c2d5853337d0256734f4e419 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 06:13:44 +0000 Subject: [PATCH 03/14] upadte save inference model in dygraph without input --- python/paddle/hapi/model.py | 70 +++++++++++++++++++++++++------ python/paddle/tests/test_model.py | 44 +++++++++++++++---- 2 files changed, 95 insertions(+), 19 deletions(-) diff --git a/python/paddle/hapi/model.py b/python/paddle/hapi/model.py index f502a1dfc340de..d00cdf1044bdcd 100644 --- a/python/paddle/hapi/model.py +++ b/python/paddle/hapi/model.py @@ -598,6 +598,7 @@ def __init__(self, model): 'test_batch': 0 } + self._shapes = None if self._nranks > 1: stradegy = fluid.dygraph.parallel.ParallelStrategy() stradegy.nranks = ParallelEnv().nranks @@ -617,11 +618,13 @@ def mode(self, value): # TODO multi device in dygraph mode not implemented at present time def train_batch(self, inputs, labels=None): + assert self.model._optimizer, \ "model not ready, please call `model.prepare()` first" self.model.network.train() self.mode = 'train' inputs = to_list(inputs) + self._shapes = [list(input.shape) for input in inputs] labels = labels or [] labels = [to_variable(l) for l in to_list(labels)] @@ -656,6 +659,7 @@ def eval_batch(self, inputs, labels=None): self.model.network.eval() self.mode = 'eval' inputs = to_list(inputs) + self._shapes = [list(input.shape) for input in inputs] labels = labels or [] labels = [to_variable(l) for l in to_list(labels)] @@ -704,6 +708,7 @@ def test_batch(self, inputs): self.model.network.eval() self.mode = 'test' inputs = [to_variable(x) for x in to_list(inputs)] + self._shapes = [list(input.shape) for input in inputs] outputs = self.model.network.forward(*inputs) if self._nranks > 1 and isinstance(self.model._place, fluid.CUDAPlace): outputs = [_all_gather(o, self._nranks) for o in to_list(outputs)] @@ -778,7 +783,7 @@ def load(self, param_state_pairs, optim_state): if not hasattr(self.model._optimizer, 'set_state_dict'): warnings.warn( - "paddle.fluid.optimizer is deprecated in API 2.0, please use paddle.optimizer instead" + "paddle.fluid.optimizer is deprecated in API 2.0, please use paddle.optimizer instead." ) self.model._optimizer.set_dict(converted_state) else: @@ -846,14 +851,18 @@ def __init__(self, network, inputs=None, labels=None): self._loss = None self._loss_weights = None self._optimizer = None - self._optimizer = None + self._shapes = None + self._is_shape_inferred = False self._test_dataloader = None if not in_dygraph_mode(): if not isinstance(inputs, (list, dict, Input)): raise TypeError( "'inputs' must be list or dict, and couldn't be None.") - self._inputs = self._verify_spec(inputs, True) + elif inputs: + self._shapes = [list(input.shape) for input in inputs] + + self._inputs = self._verify_spec(inputs, is_input=True) self._labels = self._verify_spec(labels) # init backend @@ -905,7 +914,12 @@ def train_batch(self, inputs, labels=None): loss = model.train_batch([data], [label]) print(loss) """ - return self._adapter.train_batch(inputs, labels) + loss = self._adapter.train_batch(inputs, labels) + if fluid.in_dygraph_mode() and self._shapes is None: + self._shapes = self._adapter._shapes + self._is_shape_inferred = True + self._inputs = self._verify_spec(None, self._shapes, True) + return loss def eval_batch(self, inputs, labels=None): """ @@ -951,7 +965,12 @@ def eval_batch(self, inputs, labels=None): loss = model.eval_batch([data], [label]) print(loss) """ - return self._adapter.eval_batch(inputs, labels) + loss = self._adapter.eval_batch(inputs, labels) + if fluid.in_dygraph_mode() and self._shapes is None: + self._shapes = self._adapter._shapes + self._is_shape_inferred = True + self._inputs = self._verify_spec(None, self._shapes, True) + return loss def test_batch(self, inputs): """ @@ -988,7 +1007,12 @@ def test_batch(self, inputs): out = model.test_batch([data]) print(out) """ - return self._adapter.test_batch(inputs) + loss = self._adapter.test_batch(inputs) + if fluid.in_dygraph_mode() and self._shapes is None: + self._shapes = self._adapter._shapes + self._is_shape_inferred = True + self._inputs = self._verify_spec(None, self._shapes, True) + return loss def save(self, path, training=True): """ @@ -1671,6 +1695,14 @@ def get_inout_spec(all_vars, return_name=False): if fluid.in_dygraph_mode(): with fluid.framework._dygraph_guard(None): layer = self.network + if self._shapes is None: # No provided or inferred + raise RuntimeError( + "Saving inference model needs `inputs` or running before saving." + ) + if self._is_shape_inferred: + warnings.warn( + 'Saving actual input shapes only if `inputs` is provided, otherwise variable input dimension is immutable.' + ) layer.forward = paddle.jit.to_static( layer.forward, input_spec=self._inputs) @@ -1769,6 +1801,7 @@ def _run_one_epoch(self, data_loader, callbacks, mode, logs={}): data = flatten(data) # LoDTensor.shape is callable, where LoDTensor comes from # DataLoader in static graph + batch_size = data[0].shape()[0] if callable(data[ 0].shape) else data[0].shape[0] @@ -1811,6 +1844,14 @@ def _run_one_epoch(self, data_loader, callbacks, mode, logs={}): callbacks.on_batch_end(mode, step, logs) self._reset_metrics() + if self._shapes is None and mode == "train": + self._shapes = [ + data[i].shape() if callable(data[i].shape) else data[i].shape + for i in range(len(data) - 1) + ] + self._is_shape_inferred = True + self._inputs = self._verify_spec(None, self._shapes, True) + if mode == 'test': return logs, outputs return logs @@ -1862,18 +1903,23 @@ def summary(self, input_size=None, dtype=None): _input_size = self._inputs return summary(self.network, _input_size, dtype) - def _verify_spec(self, specs, is_input=False): + def _verify_spec(self, specs, shapes=None, is_input=False): out_specs = [] if specs is None: # Note(Aurelius84): If not specific specs of `Input`, using argument names of `forward` function # to generate `Input`. But how can we know the actual shape of each input tensor? + if is_input: - out_specs = [ - Input( - name=n, shape=[None]) - for n in extract_args(self.network.forward) if n != 'self' - ] + arg_names = extract_args(self.network.forward)[1:] + if shapes is not None and fluid.in_dygraph_mode(): + out_specs = [ + Input( + name=n, shape=shapes[i]) + for i, n in enumerate(arg_names) + ] + else: + out_specs = [Input(name=n, shape=[None]) for n in arg_names] else: out_specs = to_list(specs) elif isinstance(specs, dict): diff --git a/python/paddle/tests/test_model.py b/python/paddle/tests/test_model.py index b20dfa37b665a0..e88a4951ec39ae 100644 --- a/python/paddle/tests/test_model.py +++ b/python/paddle/tests/test_model.py @@ -552,6 +552,33 @@ def test_export_deploy_model(self): shutil.rmtree(save_dir) paddle.enable_static() + def test_export_deploy_model_without_inputs_in_dygraph(self): + mnist_data = MnistDataset(mode='train') + paddle.disable_static() + for initial in ["fit", "train_batch", "eval_batch", "test_batch"]: + save_dir = tempfile.mkdtemp() + if not os.path.exists(save_dir): + os.makedirs(save_dir) + net = LeNet() + model = Model(net) + model.prepare( + optimizer=optim, loss=CrossEntropyLoss(reduction="sum")) + if initial == "fit": + model.fit(mnist_data, batch_size=64, verbose=0) + else: + img = np.array( + np.random.random((1, 1, 28, 28)), dtype=np.float32) + label = np.array(np.random.rand(1, 1), dtype=np.int64) + if initial == "train_batch": + model.train_batch(img, label) + elif initial == "eval_batch": + model.eval_batch(img, label) + else: + model.test_batch(img) + + model.save(save_dir, training=False) + shutil.rmtree(save_dir) + class TestRaiseError(unittest.TestCase): def test_input_without_name(self): @@ -562,13 +589,16 @@ def test_input_without_name(self): with self.assertRaises(ValueError): model = Model(net, inputs, labels) - def test_input_without_input_spec(self): - for dynamic in [True, False]: - paddle.disable_static() if dynamic else None - net = MyModel(classifier_activation=None) - with self.assertRaises(TypeError): - model = Model(net) - paddle.enable_static() + def test_export_deploy_model_without_inputs_and_run_in_dygraph(self): + paddle.disable_static() + net = MyModel(classifier_activation=None) + save_dir = tempfile.mkdtemp() + if not os.path.exists(save_dir): + os.makedirs(save_dir) + with self.assertRaises(RuntimeError): + model = Model(net) + model.save(save_dir, training=False) + paddle.enable_static() if __name__ == '__main__': From 87a1b53bba2df3a76e3eb392aaad0227cf00e1c9 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 06:24:20 +0000 Subject: [PATCH 04/14] fix unittets for test_model.py --- python/paddle/tests/http.log | 0 python/paddle/tests/test_model.py | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 python/paddle/tests/http.log diff --git a/python/paddle/tests/http.log b/python/paddle/tests/http.log new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/python/paddle/tests/test_model.py b/python/paddle/tests/test_model.py index e88a4951ec39ae..2de39c709d899e 100644 --- a/python/paddle/tests/test_model.py +++ b/python/paddle/tests/test_model.py @@ -570,11 +570,11 @@ def test_export_deploy_model_without_inputs_in_dygraph(self): np.random.random((1, 1, 28, 28)), dtype=np.float32) label = np.array(np.random.rand(1, 1), dtype=np.int64) if initial == "train_batch": - model.train_batch(img, label) + model.train_batch([img], [label]) elif initial == "eval_batch": - model.eval_batch(img, label) + model.eval_batch([img], [label]) else: - model.test_batch(img) + model.test_batch([img]) model.save(save_dir, training=False) shutil.rmtree(save_dir) From 6396fceda48020676c8163591e35ec6bcee29762 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 06:28:09 +0000 Subject: [PATCH 05/14] solve conflicts --- python/paddle/tests/test_model.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/python/paddle/tests/test_model.py b/python/paddle/tests/test_model.py index 34e7065cd2391f..69a888de9c61fd 100644 --- a/python/paddle/tests/test_model.py +++ b/python/paddle/tests/test_model.py @@ -614,7 +614,6 @@ def test_input_without_name(self): with self.assertRaises(ValueError): model = Model(net, inputs, labels) -<<<<<<< HEAD def test_export_deploy_model_without_inputs_and_run_in_dygraph(self): paddle.disable_static() net = MyModel(classifier_activation=None) @@ -625,15 +624,6 @@ def test_export_deploy_model_without_inputs_and_run_in_dygraph(self): model = Model(net) model.save(save_dir, training=False) paddle.enable_static() -======= - def test_input_without_input_spec(self): - for dynamic in [True, False]: - paddle.disable_static() if dynamic else None - net = MyModel() - with self.assertRaises(TypeError): - model = Model(net) - paddle.enable_static() ->>>>>>> 6b727e08b1f38b3f4acc1708c163ed6ae5df8d58 if __name__ == '__main__': From 67bac4892822290a1bcc215ba12d4933d6c90f39 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 06:29:33 +0000 Subject: [PATCH 06/14] solve conflicts --- python/paddle/hapi/model.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/python/paddle/hapi/model.py b/python/paddle/hapi/model.py index fd6161f1cf8ec4..ea6d310ad53785 100644 --- a/python/paddle/hapi/model.py +++ b/python/paddle/hapi/model.py @@ -854,7 +854,6 @@ def __init__(self, network, inputs=None, labels=None): self._is_shape_inferred = False self._test_dataloader = None -<<<<<<< HEAD if not in_dygraph_mode(): if not isinstance(inputs, (list, dict, Input)): raise TypeError( @@ -863,13 +862,6 @@ def __init__(self, network, inputs=None, labels=None): self._shapes = [list(input.shape) for input in inputs] self._inputs = self._verify_spec(inputs, is_input=True) -======= - if not isinstance(inputs, (list, dict, Input)): - raise TypeError( - "'inputs' must be list or dict in static graph mode") - - self._inputs = self._verify_spec(inputs, True) ->>>>>>> 6b727e08b1f38b3f4acc1708c163ed6ae5df8d58 self._labels = self._verify_spec(labels) # init backend From 8dcc03ae1ae5f8bd0c7c92665fc496e8bd826da3 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 06:31:31 +0000 Subject: [PATCH 07/14] delete http.log --- python/paddle/tests/http.log | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 python/paddle/tests/http.log diff --git a/python/paddle/tests/http.log b/python/paddle/tests/http.log deleted file mode 100644 index e69de29bb2d1d6..00000000000000 From cc71c7b9ec58e4eeb9d6da95f73cdda264081685 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 07:33:44 +0000 Subject: [PATCH 08/14] fix test_model.py bug, correct initialization of MyModel --- python/paddle/tests/test_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/paddle/tests/test_model.py b/python/paddle/tests/test_model.py index 69a888de9c61fd..c5a0e820fe793f 100644 --- a/python/paddle/tests/test_model.py +++ b/python/paddle/tests/test_model.py @@ -616,7 +616,7 @@ def test_input_without_name(self): def test_export_deploy_model_without_inputs_and_run_in_dygraph(self): paddle.disable_static() - net = MyModel(classifier_activation=None) + net = MyModel() save_dir = tempfile.mkdtemp() if not os.path.exists(save_dir): os.makedirs(save_dir) From 7afd7d4b257a1efe93648d7295d2b4203c648634 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 08:53:35 +0000 Subject: [PATCH 09/14] fix unittests bugs --- python/paddle/tests/test_model.py | 70 ++++++++++--------------------- 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/python/paddle/tests/test_model.py b/python/paddle/tests/test_model.py index c5a0e820fe793f..6f5163f0583a95 100644 --- a/python/paddle/tests/test_model.py +++ b/python/paddle/tests/test_model.py @@ -66,34 +66,6 @@ def forward(self, inputs): return x -class LeNetDeclarative(fluid.dygraph.Layer): - def __init__(self, num_classes=10): - super(LeNetDeclarative, self).__init__() - self.num_classes = num_classes - self.features = Sequential( - Conv2d( - 1, 6, 3, stride=1, padding=1), - ReLU(), - Pool2D(2, 'max', 2), - Conv2d( - 6, 16, 5, stride=1, padding=0), - ReLU(), - Pool2D(2, 'max', 2)) - - if num_classes > 0: - self.fc = Sequential( - Linear(400, 120), Linear(120, 84), Linear(84, 10)) - - @declarative - def forward(self, inputs): - x = self.features(inputs) - - if self.num_classes > 0: - x = fluid.layers.flatten(x, 1) - x = self.fc(x) - return x - - class MnistDataset(MNIST): def __init__(self, mode, return_label=True, sample_num=None): super(MnistDataset, self).__init__(mode=mode) @@ -577,32 +549,34 @@ def test_export_deploy_model(self): shutil.rmtree(save_dir) paddle.enable_static() - def test_export_deploy_model_without_inputs_in_dygraph(self): + def test_dygraph_export_deploy_model_without_inputs(self): mnist_data = MnistDataset(mode='train') paddle.disable_static() for initial in ["fit", "train_batch", "eval_batch", "test_batch"]: save_dir = tempfile.mkdtemp() if not os.path.exists(save_dir): os.makedirs(save_dir) - net = LeNet() - model = Model(net) - model.prepare( - optimizer=optim, loss=CrossEntropyLoss(reduction="sum")) - if initial == "fit": - model.fit(mnist_data, batch_size=64, verbose=0) + net = LeNet() + model = Model(net) + optim = fluid.optimizer.Adam( + learning_rate=0.001, parameter_list=model.parameters()) + model.prepare( + optimizer=optim, loss=CrossEntropyLoss(reduction="sum")) + if initial == "fit": + model.fit(mnist_data, batch_size=64, verbose=0) + else: + img = np.array( + np.random.random((1, 1, 28, 28)), dtype=np.float32) + label = np.array(np.random.rand(1, 1), dtype=np.int64) + if initial == "train_batch": + model.train_batch([img], [label]) + elif initial == "eval_batch": + model.eval_batch([img], [label]) else: - img = np.array( - np.random.random((1, 1, 28, 28)), dtype=np.float32) - label = np.array(np.random.rand(1, 1), dtype=np.int64) - if initial == "train_batch": - model.train_batch([img], [label]) - elif initial == "eval_batch": - model.eval_batch([img], [label]) - else: - model.test_batch([img]) - - model.save(save_dir, training=False) - shutil.rmtree(save_dir) + model.test_batch([img]) + + model.save(save_dir, training=False) + shutil.rmtree(save_dir) class TestRaiseError(unittest.TestCase): @@ -614,7 +588,7 @@ def test_input_without_name(self): with self.assertRaises(ValueError): model = Model(net, inputs, labels) - def test_export_deploy_model_without_inputs_and_run_in_dygraph(self): + def test_save_infer_model_without_inputs_and_run_in_dygraph(self): paddle.disable_static() net = MyModel() save_dir = tempfile.mkdtemp() From 707a42135f259e136423b8d07f1df464399f06c4 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 09:21:51 +0000 Subject: [PATCH 10/14] set paddle manual seed for unittest --- python/paddle/hapi/model.py | 2 -- python/paddle/tests/test_model.py | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/python/paddle/hapi/model.py b/python/paddle/hapi/model.py index ea6d310ad53785..936bc77d491eed 100644 --- a/python/paddle/hapi/model.py +++ b/python/paddle/hapi/model.py @@ -618,7 +618,6 @@ def mode(self, value): # TODO multi device in dygraph mode not implemented at present time def train_batch(self, inputs, labels=None): - assert self.model._optimizer, \ "model not ready, please call `model.prepare()` first" self.model.network.train() @@ -1858,7 +1857,6 @@ def _run_one_epoch(self, data_loader, callbacks, mode, logs={}): ] self._is_shape_inferred = True self._inputs = self._verify_spec(None, self._shapes, True) - if mode == 'test': return logs, outputs return logs diff --git a/python/paddle/tests/test_model.py b/python/paddle/tests/test_model.py index 6f5163f0583a95..809b25db5e47f8 100644 --- a/python/paddle/tests/test_model.py +++ b/python/paddle/tests/test_model.py @@ -515,6 +515,7 @@ def test_summary_error(self): paddle.summary(nlp_net, (1, 1, 2)) def test_export_deploy_model(self): + self.set_seed() for dynamic in [True, False]: paddle.disable_static() if dynamic else None prog_translator = ProgramTranslator() From 7d8d7910505886761851ca88c63915153f028d81 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 11:40:02 +0000 Subject: [PATCH 11/14] fix Model bugs, because inputs can be list or dict when it is provided. --- python/paddle/hapi/model.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/paddle/hapi/model.py b/python/paddle/hapi/model.py index 936bc77d491eed..ef06e7e0b5b7a7 100644 --- a/python/paddle/hapi/model.py +++ b/python/paddle/hapi/model.py @@ -858,7 +858,10 @@ def __init__(self, network, inputs=None, labels=None): raise TypeError( "'inputs' must be list or dict, and couldn't be None.") elif inputs: - self._shapes = [list(input.shape) for input in inputs] + if isinstance(inputs, list): + self._shapes = [list(input.shape) for input in inputs] + elif isinstance(inputs, dict): + self._shapes = [list(inputs[name]) for name in inputs] self._inputs = self._verify_spec(inputs, is_input=True) self._labels = self._verify_spec(labels) From 3e56bfbfc534c946cda92583b746706fe167b80c Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Sun, 27 Sep 2020 12:33:11 +0000 Subject: [PATCH 12/14] add random seed for test_export_deploy_model --- python/paddle/tests/test_model.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/paddle/tests/test_model.py b/python/paddle/tests/test_model.py index 809b25db5e47f8..96c4483a35ba83 100644 --- a/python/paddle/tests/test_model.py +++ b/python/paddle/tests/test_model.py @@ -516,6 +516,7 @@ def test_summary_error(self): def test_export_deploy_model(self): self.set_seed() + np.random.seed(2020) for dynamic in [True, False]: paddle.disable_static() if dynamic else None prog_translator = ProgramTranslator() @@ -589,6 +590,12 @@ def test_input_without_name(self): with self.assertRaises(ValueError): model = Model(net, inputs, labels) + def test_static_without_inputs(self): + paddle.enable_static() + net = MyModel() + with self.assertRaises(TypeError): + model = Model(net) + def test_save_infer_model_without_inputs_and_run_in_dygraph(self): paddle.disable_static() net = MyModel() From 5ecba2acab18e2b89899eb0e6cdae7515fb52d83 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Mon, 28 Sep 2020 06:57:47 +0000 Subject: [PATCH 13/14] delete redundant codes, because calls --- python/paddle/hapi/model.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/python/paddle/hapi/model.py b/python/paddle/hapi/model.py index ef06e7e0b5b7a7..2fdb6be54f6602 100644 --- a/python/paddle/hapi/model.py +++ b/python/paddle/hapi/model.py @@ -861,7 +861,7 @@ def __init__(self, network, inputs=None, labels=None): if isinstance(inputs, list): self._shapes = [list(input.shape) for input in inputs] elif isinstance(inputs, dict): - self._shapes = [list(inputs[name]) for name in inputs] + self._shapes = [list(inputs[name].shape) for name in inputs] self._inputs = self._verify_spec(inputs, is_input=True) self._labels = self._verify_spec(labels) @@ -1853,13 +1853,6 @@ def _run_one_epoch(self, data_loader, callbacks, mode, logs={}): callbacks.on_batch_end(mode, step, logs) self._reset_metrics() - if self._shapes is None and mode == "train": - self._shapes = [ - data[i].shape() if callable(data[i].shape) else data[i].shape - for i in range(len(data) - 1) - ] - self._is_shape_inferred = True - self._inputs = self._verify_spec(None, self._shapes, True) if mode == 'test': return logs, outputs return logs From b151fba973e00942bd140f4b29e53a18b20c7606 Mon Sep 17 00:00:00 2001 From: LiuChiaChi <709153940@qq.com> Date: Mon, 28 Sep 2020 11:36:37 +0000 Subject: [PATCH 14/14] Code optimization, error information optimization --- python/paddle/hapi/model.py | 50 +++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/python/paddle/hapi/model.py b/python/paddle/hapi/model.py index 2fdb6be54f6602..8505544a71f58e 100644 --- a/python/paddle/hapi/model.py +++ b/python/paddle/hapi/model.py @@ -200,6 +200,15 @@ def _init_context(): return strategy +def _update_input_shapes(inputs): + shapes = None + if isinstance(inputs, list): + shapes = [list(input.shape) for input in inputs] + elif isinstance(inputs, dict): + shapes = [list(inputs[name].shape) for name in inputs] + return shapes + + class StaticGraphAdapter(object): """ Model traning/inference with a static graph. @@ -598,7 +607,7 @@ def __init__(self, model): 'test_batch': 0 } - self._shapes = None + self._input_shapes = None if self._nranks > 1: stradegy = fluid.dygraph.parallel.ParallelStrategy() stradegy.nranks = ParallelEnv().nranks @@ -623,7 +632,7 @@ def train_batch(self, inputs, labels=None): self.model.network.train() self.mode = 'train' inputs = to_list(inputs) - self._shapes = [list(input.shape) for input in inputs] + self._input_shapes = _update_input_shapes(inputs) labels = labels or [] labels = [to_variable(l) for l in to_list(labels)] @@ -658,7 +667,7 @@ def eval_batch(self, inputs, labels=None): self.model.network.eval() self.mode = 'eval' inputs = to_list(inputs) - self._shapes = [list(input.shape) for input in inputs] + self._input_shapes = _update_input_shapes(inputs) labels = labels or [] labels = [to_variable(l) for l in to_list(labels)] @@ -707,7 +716,7 @@ def test_batch(self, inputs): self.model.network.eval() self.mode = 'test' inputs = [to_variable(x) for x in to_list(inputs)] - self._shapes = [list(input.shape) for input in inputs] + self._input_shapes = _update_input_shapes(inputs) outputs = self.model.network.forward(*inputs) if self._nranks > 1 and isinstance(self.model._place, fluid.CUDAPlace): outputs = [_all_gather(o, self._nranks) for o in to_list(outputs)] @@ -849,7 +858,7 @@ def __init__(self, network, inputs=None, labels=None): self._loss = None self._loss_weights = None self._optimizer = None - self._shapes = None + self._input_shapes = None self._is_shape_inferred = False self._test_dataloader = None @@ -858,10 +867,7 @@ def __init__(self, network, inputs=None, labels=None): raise TypeError( "'inputs' must be list or dict, and couldn't be None.") elif inputs: - if isinstance(inputs, list): - self._shapes = [list(input.shape) for input in inputs] - elif isinstance(inputs, dict): - self._shapes = [list(inputs[name].shape) for name in inputs] + self._input_shapes = _update_input_shapes(inputs) self._inputs = self._verify_spec(inputs, is_input=True) self._labels = self._verify_spec(labels) @@ -915,10 +921,10 @@ def train_batch(self, inputs, labels=None): print(loss) """ loss = self._adapter.train_batch(inputs, labels) - if fluid.in_dygraph_mode() and self._shapes is None: - self._shapes = self._adapter._shapes + if fluid.in_dygraph_mode() and self._input_shapes is None: + self._input_shapes = self._adapter._input_shapes self._is_shape_inferred = True - self._inputs = self._verify_spec(None, self._shapes, True) + self._inputs = self._verify_spec(None, self._input_shapes, True) return loss def eval_batch(self, inputs, labels=None): @@ -965,10 +971,10 @@ def eval_batch(self, inputs, labels=None): print(loss) """ loss = self._adapter.eval_batch(inputs, labels) - if fluid.in_dygraph_mode() and self._shapes is None: - self._shapes = self._adapter._shapes + if fluid.in_dygraph_mode() and self._input_shapes is None: + self._input_shapes = self._adapter._input_shapes self._is_shape_inferred = True - self._inputs = self._verify_spec(None, self._shapes, True) + self._inputs = self._verify_spec(None, self._input_shapes, True) return loss def test_batch(self, inputs): @@ -1010,10 +1016,10 @@ def test_batch(self, inputs): print(out) """ loss = self._adapter.test_batch(inputs) - if fluid.in_dygraph_mode() and self._shapes is None: - self._shapes = self._adapter._shapes + if fluid.in_dygraph_mode() and self._input_shapes is None: + self._input_shapes = self._adapter._input_shapes self._is_shape_inferred = True - self._inputs = self._verify_spec(None, self._shapes, True) + self._inputs = self._verify_spec(None, self._input_shapes, True) return loss def save(self, path, training=True): @@ -1704,14 +1710,14 @@ def get_inout_spec(all_vars, return_name=False): if fluid.in_dygraph_mode(): with fluid.framework._dygraph_guard(None): layer = self.network - if self._shapes is None: # No provided or inferred + if self._input_shapes is None: # No provided or inferred raise RuntimeError( - "Saving inference model needs `inputs` or running before saving." + "Saving inference model needs 'inputs' or running before saving. Please specify 'inputs' in Model initialization or input training zqqdata and perform a training for shape derivation." ) if self._is_shape_inferred: warnings.warn( - 'Saving actual input shapes only if `inputs` is provided, otherwise variable input dimension is immutable.' - ) + "'inputs' was not specified when Model initialization, so the input shape to be saved will be the shape derived from the user's actual inputs. The input shape to be saved is %s. For saving correct input shapes, please provide 'inputs' for Model initialization." + % self._input_shapes) layer.forward = paddle.jit.to_static( layer.forward, input_spec=self._inputs)