layers.py 5.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# 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.

X
Xin Pan 已提交
15
import collections
16 17 18
import contextlib
import sys
import numpy as np
M
minqiyang 已提交
19
import collections
20 21 22 23 24

from paddle.fluid import core
from paddle.fluid import framework
from paddle.fluid.imperative import base

X
Xin Pan 已提交
25
__all__ = ['Layer', 'PyLayer']
26 27


X
Xin Pan 已提交
28 29 30
class Layer(core.Layer):
    """Layers composed of operators."""

M
minqiyang 已提交
31
    def __init__(self, dtype=core.VarDesc.VarType.FP32, name=None):
X
Xin Pan 已提交
32
        self._built = False
M
minqiyang 已提交
33
        self._dtype = dtype
X
Xin Pan 已提交
34 35
        self._parameters = collections.OrderedDict()
        self._sub_layers = collections.OrderedDict()
36

X
polish  
Xin Pan 已提交
37 38
    def parameters(self, include_sublayers=True):
        """Returns a list of Parameters from current and sub-layers.
X
Xin Pan 已提交
39
        """
X
polish  
Xin Pan 已提交
40 41 42 43 44 45
        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
X
Xin Pan 已提交
46

X
Xin Pan 已提交
47 48
    def clear_gradients(self):
        for p in self.parameters():
M
minqiyang 已提交
49
            p._clear_gradient()
X
Xin Pan 已提交
50

X
polish  
Xin Pan 已提交
51
    def _build_once(self, *args):
52 53
        pass

54
    def __call__(self, *inputs):
X
Xin Pan 已提交
55
        if not self._built:
56 57
            self._build_once(*inputs)

58
        outputs = self.forward(*inputs)
X
Xin Pan 已提交
59
        self._built = True
M
minqiyang 已提交
60
        return outputs
M
minqiyang 已提交
61

62 63
    def forward(self, *inputs):
        raise NotImplementedError
X
Xin Pan 已提交
64 65 66 67

    def backward(self, *inputs):
        raise ValueError("Layer shouldn't implement backward")

X
Xin Pan 已提交
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    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)

X
Xin Pan 已提交
98

X
Xin Pan 已提交
99
class PyLayer(core.PyLayer):
X
Xin Pan 已提交
100 101
    """Layers composed of user-defined python codes."""

X
Xin Pan 已提交
102 103
    def __init__(self):
        super(PyLayer, self).__init__()
X
Xin Pan 已提交
104

X
Xin Pan 已提交
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
    @classmethod
    def _do_forward(cls, inputs):
        return cls._to_tuple(cls.forward(inputs))

    @classmethod
    def _do_backward(cls, inputs):
        return cls._to_tuple(cls.backward(inputs))

    @staticmethod
    def _to_tuple(inputs):
        if not isinstance(inputs, list) and not isinstance(inputs, tuple):
            inputs = [inputs]
        ret = []
        for inp in inputs:
            tensor = core.LoDTensor()
            tensor.set(inp, core.CPUPlace())
            ret.append(tensor)
        return tuple(ret)

X
Xin Pan 已提交
124
    @staticmethod
M
minqiyang 已提交
125
    def forward(*inputs):
X
Xin Pan 已提交
126 127
        raise NotImplementedError

X
Xin Pan 已提交
128
    @staticmethod
M
minqiyang 已提交
129
    def backward(*douts):
X
Xin Pan 已提交
130
        raise NotImplementedError
X
Xin Pan 已提交
131 132

    @classmethod
M
minqiyang 已提交
133
    def __call__(cls, *inputs):
X
Xin Pan 已提交
134 135
        tracer = framework._imperative_tracer()
        block = framework.default_main_program().current_block()
M
minqiyang 已提交
136
        ivar_inputs = [x._ivar for x in inputs]
X
Xin Pan 已提交
137

X
polish  
Xin Pan 已提交
138 139
        if not hasattr(cls, 'forward_id'):
            cls.forward_id = core.PyLayer.num_funcs() + 1
X
Xin Pan 已提交
140
            PyLayer.register_func(cls.forward_id, cls._do_forward)
X
polish  
Xin Pan 已提交
141
            cls.backward_id = core.PyLayer.num_funcs() + 1
X
Xin Pan 已提交
142
            PyLayer.register_func(cls.backward_id, cls._do_backward)
X
Xin Pan 已提交
143 144

        iop = core.OpBase()
X
polish  
Xin Pan 已提交
145 146
        iop.forward_id = cls.forward_id
        iop.backward_id = cls.backward_id
X
Xin Pan 已提交
147
        block.ops.append(iop)
M
minqiyang 已提交
148
        ivars = tracer.py_trace(iop, ivar_inputs, False)
X
Xin Pan 已提交
149 150
        ret = []
        for ivar in ivars:
M
minqiyang 已提交
151
            tensor = ivar.value().get_tensor()
X
Xin Pan 已提交
152 153 154 155 156 157 158 159 160
            py_var = framework.Variable(
                block,
                type=core.VarDesc.VarType.LOD_TENSOR,
                name=None,
                shape=tensor.shape(),
                dtype=tensor._dtype(),
                ivar=ivar)
            ret.append(py_var)
        return ret