diff --git a/paddle/fluid/imperative/layer.cc b/paddle/fluid/imperative/layer.cc index 47488d4dea79f285769f29c93f7888a7f783f070..8f20f0c06e043ddc629e47c6e49280c5467b0e20 100644 --- a/paddle/fluid/imperative/layer.cc +++ b/paddle/fluid/imperative/layer.cc @@ -207,7 +207,7 @@ framework::LoDTensor& VarBase::GradValue() { std::map> OpBase::ApplyGrad() { if (grad_op_descs_.empty() && backward_id_ <= 0) { - LOG(WARNING) << "op with no grad: " << op_desc_->Type(); + VLOG(3) << "op with no grad: " << op_desc_->Type(); return {}; } diff --git a/python/paddle/fluid/imperative/layers.py b/python/paddle/fluid/imperative/layers.py index 71ff95bdea36967c1fa6b5c94cc7ca305e7a544a..59fe6bbf74b80c2260c5b4881fee8807482c9c68 100644 --- a/python/paddle/fluid/imperative/layers.py +++ b/python/paddle/fluid/imperative/layers.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import collections import contextlib import sys import numpy as np @@ -30,31 +31,45 @@ class Layer(core.Layer): def __init__(self, dtype=core.VarDesc.VarType.FP32, name=None): self._built = False self._dtype = dtype + self._parameters = collections.OrderedDict() + self._sub_layers = collections.OrderedDict() + + def parameters(self, include_sublayers=True): + """Returns a list of Parameters from current and sub-layers. + + Args: + include_sublayers: If true, also include the parameters from + sublayers. + + Returns a list of Parameters. + """ + ret = [p for p in self._parameters.values()] + if include_sublayers: + for l in self._sub_layers.values(): + for p in l.parameters(include_sublayers): + ret.append(p) + return ret + + def sublayers(self, include_sublayers=True): + """Returns a list of sub layers. - def parameters(self): - params = [] - for key in self.__dict__.keys(): - value = self.__dict__[key] - if isinstance(value, framework.Parameter): - params.append(value) - elif isinstance(value, core.Layer): - params.extend(value.parameters()) - elif isinstance(value, collections.Container): - if len(value) == 0: - continue - if isinstance(value[0], framework.Parameter): - params.extend(value) - elif isinstance(value[0], core.Layer): - for v in value: - params.extend(v.parameters()) - - return params + Args: + include_sublayers: If true, also include the layers from sublayers. + + Returns a list of sub layers. + """ + ret = [l for l in self._sub_layers.values()] + if include_sublayers: + for l in self._sub_layers.values(): + for sub_l in l.sublayers(include_sublayers): + ret.append(sub_l) + return ret def clear_gradients(self): for p in self.parameters(): p._clear_gradient() - def _build_once(self, inputs): + def _build_once(self, *args): pass def __call__(self, *inputs): @@ -71,6 +86,66 @@ class Layer(core.Layer): def backward(self, *inputs): raise ValueError("Layer shouldn't implement backward") + def add_sublayer(self, name, sublayer): + """Adds a sub Layer instance. + + Added sublayer can be access like self.name. + + Args: + name: name of this sublayer. + sublayer: an instance of Layer. + Returns: + the sublayer passed in. + """ + assert isinstance(sublayer, core.Layer) + self._sub_layers[name] = sublayer + return sublayer + + def add_parameter(self, name, parameter): + """Adds a Parameter instance. + + Added parameter can be access like self.name. + + Args: + name: name of this sublayer. + parameter: an instance of Parameter. + Returns: + the parameter passed in. + """ + assert isinstance(parameter, framework.Parameter) + self._parameters[name] = parameter + return parameter + + def __getattr__(self, name): + if name in self._parameters: + return self._parameters[name] + elif name in self._sub_layers: + return self._sub_layers[name] + + def __setattr__(self, name, value): + if isinstance(value, framework.Parameter): + params = self.__dict__.get('_parameters', None) + if params is None: + raise ValueError( + "super(YourLayer, self).__init__() should be called first") + params[name] = value + elif isinstance(value, core.Layer): + layers = self.__dict__.get('_sub_layers', None) + if layers is None: + raise ValueError( + "super(YourLayer, self).__init__() should be called first") + layers[name] = value + else: + object.__setattr__(self, name, value) + + def __delattr__(self, name): + if name in self._parameters: + del self._parameters[name] + elif name in self._sub_layers: + del self._sub_layers[name] + else: + object.__delattr__(self, name) + class PyLayer(core.PyLayer): """Layers composed of user-defined python codes.""" diff --git a/python/paddle/fluid/imperative/nn.py b/python/paddle/fluid/imperative/nn.py index 6c5961cc63d1c140e0a6f33aac054acdbbe8e8e0..c86a373ae4a92053538c93386003f9014c32841f 100644 --- a/python/paddle/fluid/imperative/nn.py +++ b/python/paddle/fluid/imperative/nn.py @@ -225,9 +225,6 @@ class FC(layers.Layer): act=act, name=name) - def parameters(self): - return [self._w, self._b] - def _build_once(self, input): input_shape = input.shape param_shape = [ @@ -478,9 +475,6 @@ class Embedding(layers.Layer): dtype=self._dtype, is_bias=False) - def parameters(self): - return [self._w] - def forward(self, input): out = self._helper.create_variable_for_type_inference(self._dtype) self._helper.append_op( diff --git a/python/paddle/fluid/tests/unittests/test_base_layer.py b/python/paddle/fluid/tests/unittests/test_base_layer.py new file mode 100644 index 0000000000000000000000000000000000000000..bf00698d63624d4e20a0853641219a2735d89d25 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/test_base_layer.py @@ -0,0 +1,82 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import numpy as np + +import paddle.fluid as fluid +from paddle.fluid.layer_helper import LayerHelper + + +class L1(fluid.imperative.Layer): + def __init__(self): + super(L1, self).__init__() + self._helper = LayerHelper( + 'MyLayer', + param_attr=fluid.ParamAttr( + initializer=fluid.initializer.Constant(value=0.1))) + + self.w1 = self._helper.create_parameter( + attr=self._helper.param_attr, + shape=[2, 2], + dtype='float32', + is_bias=False) + self.w2 = self._helper.create_parameter( + attr=self._helper.param_attr, + shape=[2, 2], + dtype='float32', + is_bias=False) + + def forward(self): + return self.w1 + self.w2 + + +class L2(fluid.imperative.Layer): + def __init__(self): + super(L2, self).__init__() + self.layer1 = L1() + self.layer2 = L1() + + def forward(self): + return self.layer1() + self.layer2() + + +class L3(fluid.imperative.Layer): + def __init__(self): + super(L3, self).__init__() + self.layer1 = L2() + self.layer2 = L2() + + def forward(self): + return self.layer1() + self.layer2() + + +class TestBaseLayer(unittest.TestCase): + def test_one_level(self): + with fluid.imperative.guard(): + l = L1() + ret = l() + self.assertEqual(l.w1.name, "MyLayer_0.w_0") + self.assertEqual(l.w2.name, "MyLayer_0.w_1") + self.assertTrue(np.allclose(ret._numpy(), 0.2 * np.ones([2, 2]))) + + def test_three_level(self): + with fluid.imperative.guard(): + l = L3() + ret = l() + self.assertTrue(np.allclose(ret._numpy(), 0.8 * np.ones([2, 2]))) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/paddle/fluid/tests/unittests/test_imperative.py b/python/paddle/fluid/tests/unittests/test_imperative.py index baaddf9f2e5b123300f1d083b33ea644665348fd..c54e998ea875e1bd27f9816f88db0e38bc488459 100644 --- a/python/paddle/fluid/tests/unittests/test_imperative.py +++ b/python/paddle/fluid/tests/unittests/test_imperative.py @@ -333,6 +333,18 @@ class TestImperative(unittest.TestCase): self.assertTrue(np.allclose(dy_out, static_out)) self.assertTrue(np.allclose(dy_grad, static_grad)) + params = mlp.parameters(True) + self.assertEqual("FC_0.w_0", params[0].name) + self.assertEqual("FC_0.b_0", params[1].name) + self.assertEqual("FC_1.w_0", params[2].name) + self.assertEqual("FC_1.b_0", params[3].name) + self.assertEqual(len(params), 4) + + sublayers = mlp.sublayers(True) + self.assertEqual(mlp._fc1, sublayers[0]) + self.assertEqual(mlp._fc2, sublayers[1]) + self.assertEqual(len(sublayers), 2) + def test_rnn(self): np_inp = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0], [10.0, 11.0, 12.0]]) diff --git a/python/paddle/fluid/tests/unittests/test_imperative_gan.py b/python/paddle/fluid/tests/unittests/test_imperative_gan.py index 681661bfc63db95653be371688a047efe96f3866..33c196d1ab52b393491561e75054e6c323fce18d 100644 --- a/python/paddle/fluid/tests/unittests/test_imperative_gan.py +++ b/python/paddle/fluid/tests/unittests/test_imperative_gan.py @@ -33,9 +33,6 @@ class Discriminator(fluid.imperative.Layer): self._fc1 = FC(size=32, act='elu', name="d_fc1") self._fc2 = FC(size=1, name="d_fc2") - def parameters(self): - return self._fc1.parameters() + self._fc2.parameters() - def forward(self, inputs): x = self._fc1(inputs) return self._fc2(x) @@ -48,10 +45,6 @@ class Generator(fluid.imperative.Layer): self._fc2 = FC(size=64, act='elu', name="g_fc2") self._fc3 = FC(size=1, name="g_fc3") - def parameters(self): - return self._fc1.parameters() + self._fc2.parameters( - ) + self._fc3.parameters() - def forward(self, inputs): x = self._fc1(inputs) x = self._fc2(x) diff --git a/python/paddle/fluid/tests/unittests/test_imperative_ptb_rnn.py b/python/paddle/fluid/tests/unittests/test_imperative_ptb_rnn.py index afe990e74ff96dfbca4f335b561f9bbe7d295246..82aff18b728f45388cd747e3bce19c1c9d6f91cc 100644 --- a/python/paddle/fluid/tests/unittests/test_imperative_ptb_rnn.py +++ b/python/paddle/fluid/tests/unittests/test_imperative_ptb_rnn.py @@ -75,16 +75,6 @@ class SimpleLSTMRNN(fluid.imperative.Layer): self.hidden_array.append(pre_hidden) self.cell_array.append(pre_cell) - def parameters(self): - parameters = list() - for param in self.weight_1_arr: - parameters.append(param) - for param in self.weight_2_arr: - parameters.append(param) - for bias in self.bias_arr: - parameters.append(bias) - return parameters - def forward(self, input_embedding, init_hidden=None, init_cell=None): res = [] for index in range(self._num_steps): @@ -177,12 +167,6 @@ class PtbModel(fluid.imperative.Layer): def _build_once(self, input, label, init_hidden, init_cell): pass - def parameters(self): - parameters = self.simple_lstm_rnn.parameters() + [ - self.softmax_weight, self.softmax_bias - ] + self.embedding.parameters() - return parameters - def forward(self, input, label, init_hidden, init_cell): init_h = fluid.layers.reshape( diff --git a/python/paddle/fluid/tests/unittests/test_imperative_resnet.py b/python/paddle/fluid/tests/unittests/test_imperative_resnet.py index c27fd0b8024a8fa3310a62de34299fb621e2902f..128d18621db8374c6c385dddbefc0d29e760a02f 100644 --- a/python/paddle/fluid/tests/unittests/test_imperative_resnet.py +++ b/python/paddle/fluid/tests/unittests/test_imperative_resnet.py @@ -21,7 +21,6 @@ import paddle import paddle.fluid as fluid from paddle.fluid import core from paddle.fluid.layer_helper import LayerHelper -from paddle.fluid.optimizer import SGDOptimizer from paddle.fluid.imperative.nn import Conv2D, Pool2D, BatchNorm, FC from paddle.fluid.imperative.base import to_variable from test_imperative_base import new_program_scope @@ -173,11 +172,13 @@ class ResNet(fluid.imperative.Layer): for block in range(len(depth)): shortcut = False for i in range(depth[block]): - bottleneck_block = BottleneckBlock( - num_channels=num_channels, - num_filters=num_filters[block], - stride=2 if i == 0 and block != 0 else 1, - shortcut=shortcut) + bottleneck_block = self.add_sublayer( + 'bb_%d_%d' % (block, i), + BottleneckBlock( + num_channels=num_channels, + num_filters=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + shortcut=shortcut)) num_channels = bottleneck_block._num_channels_out self.bottleneck_block_list.append(bottleneck_block) shortcut = True @@ -223,8 +224,7 @@ class TestImperativeResnet(unittest.TestCase): batch_size=batch_size) dy_param_init_value = {} - for param in fluid.default_main_program().global_block( - ).all_parameters(): + for param in resnet.parameters(): dy_param_init_value[param.name] = param._numpy() for batch_id, data in enumerate(train_reader()): @@ -247,16 +247,14 @@ class TestImperativeResnet(unittest.TestCase): dy_out = avg_loss._numpy() if batch_id == 0: - for param in fluid.default_main_program().global_block( - ).all_parameters(): + for param in resnet.parameters(): if param.name not in dy_param_init_value: dy_param_init_value[param.name] = param._numpy() avg_loss._backward() dy_grad_value = {} - for param in fluid.default_main_program().global_block( - ).all_parameters(): + for param in resnet.parameters(): if not param.stop_gradient: np_array = np.array(param._ivar._grad_ivar().value() .get_tensor()) @@ -267,8 +265,7 @@ class TestImperativeResnet(unittest.TestCase): resnet.clear_gradients() dy_param_value = {} - for param in fluid.default_main_program().global_block( - ).all_parameters(): + for param in resnet.parameters(): dy_param_value[param.name] = param._numpy() with new_program_scope(): @@ -349,6 +346,7 @@ class TestImperativeResnet(unittest.TestCase): self.assertTrue(np.allclose(static_out, dy_out)) self.assertEqual(len(dy_param_init_value), len(static_param_init_value)) + for key, value in six.iteritems(static_param_init_value): self.assertTrue(np.allclose(value, dy_param_init_value[key])) self.assertTrue(np.isfinite(value.all()))