From 2058bab1c0d02bbf631615cc53579eea52a544ef Mon Sep 17 00:00:00 2001 From: Youwei Song Date: Tue, 29 Oct 2019 16:03:39 +0800 Subject: [PATCH] Add Sequential api (#20789) * add Sequential api test=develop * fix unittest test=develop * refine code sample * test=develop --- python/paddle/fluid/dygraph/__init__.py | 4 + python/paddle/fluid/dygraph/container.py | 85 +++++++++++++++++++ .../test_imperative_container_sequential.py | 63 ++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 python/paddle/fluid/dygraph/container.py create mode 100644 python/paddle/fluid/tests/unittests/test_imperative_container_sequential.py diff --git a/python/paddle/fluid/dygraph/__init__.py b/python/paddle/fluid/dygraph/__init__.py index 3d81db53ec..96bd0baf11 100644 --- a/python/paddle/fluid/dygraph/__init__.py +++ b/python/paddle/fluid/dygraph/__init__.py @@ -20,6 +20,9 @@ from .base import * from . import layers from .layers import * +from . import container +from .container import * + from . import nn from .nn import * @@ -41,6 +44,7 @@ from .backward_strategy import * __all__ = [] __all__ += layers.__all__ __all__ += base.__all__ +__all__ += container.__all__ __all__ += nn.__all__ __all__ += tracer.__all__ __all__ += parallel.__all__ diff --git a/python/paddle/fluid/dygraph/container.py b/python/paddle/fluid/dygraph/container.py new file mode 100644 index 0000000000..20731a66af --- /dev/null +++ b/python/paddle/fluid/dygraph/container.py @@ -0,0 +1,85 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .layers import Layer + +__all__ = ['Sequential'] + + +class Sequential(Layer): + """Sequential container. + Sub layers will be added to this container in the order of argument in the constructor. + The argument passed to the constructor can be iterable Layers or iterable name Layer pairs. + + Parameters: + name_scope(str): The name of this class. + layers(iterable): Iterable Layers or iterable name Layer pairs. + + Examples: + .. code-block:: python + + import paddle.fluid as fluid + import numpy as np + + data = np.random.uniform(-1, 1, [30, 10]).astype('float32') + with fluid.dygraph.guard(): + data = fluid.dygraph.to_variable(data) + # create Sequential with iterable Layers + model1 = fluid.dygraph.Sequential('model1', + fluid.FC('fc1', 2), + fluid.FC('fc2', 3) + ) + model1[0] # access fc1 layer + res1 = model1(data) # sequential execution + + # create Sequential with name Layer pairs + model2 = fluid.dygraph.Sequential('model2', + ('l1', fluid.FC('l1', 2)), + ('l2', fluid.FC('l2', 3)) + ) + model2['l1'] # access l1 layer + model2.add_sublayer('l3', fluid.FC('l3', 3)) # add sublayer + print([l.full_name() for l in model2.sublayers()]) # ['l1/FC_0', 'l2/FC_0', 'l3/FC_0'] + res2 = model2(data) # sequential execution + + """ + + def __init__(self, name_scope, *layers): + super(Sequential, self).__init__(name_scope) + if len(layers) > 0 and isinstance(layers[0], tuple): + for name, layer in layers: + self.add_sublayer(name, layer) + else: + for idx, layer in enumerate(layers): + self.add_sublayer(str(idx), layer) + + def __getitem__(self, name): + return self._sub_layers[str(name)] + + def __setitem__(self, name, layer): + assert isinstance(layer, Layer) + setattr(self, str(name), layer) + + def __delitem__(self, name): + name = str(name) + assert name in self._sub_layers + del self._sub_layers[name] + + def __len__(self): + return len(self._sub_layers) + + def forward(self, input): + for layer in self._sub_layers.values(): + input = layer(input) + return input diff --git a/python/paddle/fluid/tests/unittests/test_imperative_container_sequential.py b/python/paddle/fluid/tests/unittests/test_imperative_container_sequential.py new file mode 100644 index 0000000000..017116b216 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/test_imperative_container_sequential.py @@ -0,0 +1,63 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function + +import unittest +import paddle.fluid as fluid +import numpy as np + + +class TestImperativeContainerSequential(unittest.TestCase): + def test_sequential(self): + data = np.random.uniform(-1, 1, [5, 10]).astype('float32') + with fluid.dygraph.guard(): + data = fluid.dygraph.to_variable(data) + model1 = fluid.dygraph.Sequential('model1', + fluid.FC('fc1', 1), + fluid.FC('fc2', 2)) + res1 = model1(data) + self.assertListEqual(res1.shape, [5, 2]) + self.assertTrue('fc1' in model1[0]._full_name) + model1[1] = fluid.FC('fc2_new', 3) + res1 = model1(data) + self.assertListEqual(res1.shape, [5, 3]) + self.assertTrue('fc2_new' in name + for name in [p.name for p in model1.parameters()]) + loss1 = fluid.layers.reduce_mean(res1) + loss1.backward() + + model2 = fluid.dygraph.Sequential( + 'model2', ('l1', fluid.FC('l1', 1)), ('l2', fluid.FC('l2', 3))) + self.assertEqual(len(model2), 2) + res2 = model2(data) + self.assertTrue('l1' in model2.l1.full_name()) + self.assertListEqual(res2.shape, res1.shape) + self.assertEqual(len(model1.parameters()), len(model2.parameters())) + del model2['l2'] + self.assertEqual(len(model2), 1) + res2 = model2(data) + self.assertListEqual(res2.shape, [5, 1]) + model2.add_sublayer('l3', fluid.FC('l3', 3)) + model2.add_sublayer('l4', fluid.FC('l4', 4)) + self.assertEqual(len(model2), 3) + res2 = model2(data) + self.assertListEqual(res2.shape, [5, 4]) + + loss2 = fluid.layers.reduce_mean(res2) + loss2.backward() + + +if __name__ == '__main__': + unittest.main() -- GitLab