layers.py 4.7 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
Xin Pan 已提交
37
    def parameters(self):
X
Xin Pan 已提交
38 39 40
        """Returns an OrderedDict with parameters from current and sub-layers.
        """
        return self._parameters
X
Xin Pan 已提交
41

X
Xin Pan 已提交
42 43
    def clear_gradients(self):
        for p in self.parameters():
M
minqiyang 已提交
44
            p._clear_gradient()
X
Xin Pan 已提交
45

46 47 48
    def _build_once(self, inputs):
        pass

49
    def __call__(self, *inputs):
X
Xin Pan 已提交
50
        if not self._built:
51 52
            self._build_once(*inputs)

53
        outputs = self.forward(*inputs)
X
Xin Pan 已提交
54
        self._built = True
M
minqiyang 已提交
55
        return outputs
M
minqiyang 已提交
56

57 58
    def forward(self, *inputs):
        raise NotImplementedError
X
Xin Pan 已提交
59 60 61 62

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

X
Xin Pan 已提交
63 64 65 66 67 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
    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 已提交
93

X
Xin Pan 已提交
94
class PyLayer(core.PyLayer):
X
Xin Pan 已提交
95 96
    """Layers composed of user-defined python codes."""

X
Xin Pan 已提交
97 98
    def __init__(self):
        super(PyLayer, self).__init__()
X
Xin Pan 已提交
99

X
Xin Pan 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    @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 已提交
119
    @staticmethod
M
minqiyang 已提交
120
    def forward(*inputs):
X
Xin Pan 已提交
121 122
        raise NotImplementedError

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

    @classmethod
M
minqiyang 已提交
128
    def __call__(cls, *inputs):
X
Xin Pan 已提交
129 130
        tracer = framework._imperative_tracer()
        block = framework.default_main_program().current_block()
M
minqiyang 已提交
131
        ivar_inputs = [x._ivar for x in inputs]
X
Xin Pan 已提交
132

X
polish  
Xin Pan 已提交
133 134
        if not hasattr(cls, 'forward_id'):
            cls.forward_id = core.PyLayer.num_funcs() + 1
X
Xin Pan 已提交
135
            PyLayer.register_func(cls.forward_id, cls._do_forward)
X
polish  
Xin Pan 已提交
136
            cls.backward_id = core.PyLayer.num_funcs() + 1
X
Xin Pan 已提交
137
            PyLayer.register_func(cls.backward_id, cls._do_backward)
X
Xin Pan 已提交
138 139

        iop = core.OpBase()
X
polish  
Xin Pan 已提交
140 141
        iop.forward_id = cls.forward_id
        iop.backward_id = cls.backward_id
X
Xin Pan 已提交
142
        block.ops.append(iop)
M
minqiyang 已提交
143
        ivars = tracer.py_trace(iop, ivar_inputs, False)
X
Xin Pan 已提交
144 145
        ret = []
        for ivar in ivars:
M
minqiyang 已提交
146
            tensor = ivar.value().get_tensor()
X
Xin Pan 已提交
147 148 149 150 151 152 153 154 155
            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