layer.py 7.3 KB
Newer Older
Q
qiaolongfei 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
# Copyright (c) 2016 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.
14
"""
Y
Yu Yang 已提交
15 16 17
`paddle.v2.layer` is a part of model config packages in paddle.v2. In API v2,
we want to make Paddle a plain Python package. The model config package defined
the way how to configure a neural network topology in Paddle Python code.
18

Y
Yu Yang 已提交
19
The primary usage shows below.
20

Y
Yu Yang 已提交
21
..  code-block:: python
22

Y
Yu Yang 已提交
23
    import paddle.v2 as paddle
24

Y
Yu Yang 已提交
25 26 27 28
    img = paddle.layer.data(name='img', type=paddle.data_type.dense_vector(784))
    hidden = paddle.layer.fc(input=img, size=200)
    prediction = paddle.layer.fc(input=hidden, size=10,
                                 act=paddle.activation.Softmax())
29

Y
Yu Yang 已提交
30 31
    # use prediction instance where needed.
    parameters = paddle.v2.parameters.create(cost)
32
"""
Y
Yu Yang 已提交
33
from config_base import Layer, __convert_to_v2__
Q
qiaolongfei 已提交
34 35 36
import paddle.trainer_config_helpers as conf_helps
from paddle.trainer_config_helpers.config_parser_utils import \
    parse_network_config as __parse__
D
dangqingqing 已提交
37

Q
qiaolongfei 已提交
38
from paddle.trainer_config_helpers.default_decorators import wrap_name_default
39
from paddle.trainer_config_helpers.default_decorators import wrap_act_default
Y
Yu Yang 已提交
40 41
from paddle.trainer_config_helpers.default_decorators import \
    wrap_bias_attr_default
42
from paddle.trainer_config_helpers.layers import layer_support
Q
qiaolongfei 已提交
43 44

import data_type
L
Luo Tao 已提交
45
import activation
Y
Yu Yang 已提交
46 47

__all__ = ['parse_network', 'data']
Q
qiaolongfei 已提交
48

D
dangqingqing 已提交
49 50 51 52 53 54 55
__projection_names__ = filter(lambda x: x.endswith('_projection'),
                              dir(conf_helps))
__all__ += __projection_names__

__operator_names__ = filter(lambda x: x.endswith('_operator'), dir(conf_helps))
__all__ += __operator_names__

Q
qiaolongfei 已提交
56

Q
qiaolongfei 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
def parse_network(*outputs):
    """
    parse all output layers and then generate a model config proto.
    :param outputs:
    :return:
    """

    def __real_func__():
        context = dict()
        real_output = [each.to_proto(context=context) for each in outputs]
        conf_helps.outputs(real_output)

    return __parse__(__real_func__)


Q
qiaolongfei 已提交
72 73 74 75 76 77 78
"""
Some layer may need some special config, and can not use __convert_to_v2__ to convert.
So we also need to implement some special LayerV2.
"""


class DataLayerV2(Layer):
Q
qiaolongfei 已提交
79
    def __init__(self, name, type, **kwargs):
80
        assert isinstance(type, data_type.InputType)
Q
qiaolongfei 已提交
81

Q
qiaolongfei 已提交
82
        self.type = type
Q
qiaolongfei 已提交
83 84
        self.__method_name__ = 'data_layer'
        self.__kwargs__ = kwargs
Q
qiaolongfei 已提交
85 86 87 88 89

        super(DataLayerV2, self).__init__(name=name, parent_layers=dict())

    def to_proto_impl(self, **kwargs):
        args = dict()
Q
qiaolongfei 已提交
90
        args['size'] = self.type.dim
Q
qiaolongfei 已提交
91 92
        for each in kwargs:
            args[each] = kwargs[each]
Q
qiaolongfei 已提交
93 94
        for each in self.__kwargs__:
            args[each] = self.__kwargs__[each]
Q
qiaolongfei 已提交
95 96 97
        return getattr(conf_helps, self.__method_name__)(name=self.name, **args)


98 99 100 101 102 103 104 105 106 107
class MixedLayerV2(Layer):
    """
    This class is use to support `with` grammar. If not, the following code
    could convert mixed_layer simply.

        mixed = __convert_to_v2__(
            'mixed_layer', name_prefix='mixed', parent_names=['input'])
    """

    class AddToSealedMixedLayerExceptionV2(Exception):
D
dangqingqing 已提交
108
        pass
109 110 111 112 113 114 115 116 117 118

    def __init__(self,
                 size=0,
                 input=None,
                 name=None,
                 act=None,
                 bias_attr=None,
                 layer_attr=None):
        self.__method_name__ = 'mixed_layer'
        self.finalized = False
D
dangqingqing 已提交
119
        self.__inputs__ = []
120
        if input is not None:
D
dangqingqing 已提交
121
            self.__inputs__ = input
122

D
dangqingqing 已提交
123 124
        other_kwargs = dict()
        other_kwargs['name'] = name
125 126 127 128 129
        other_kwargs['size'] = size
        other_kwargs['act'] = act
        other_kwargs['bias_attr'] = bias_attr
        other_kwargs['layer_attr'] = layer_attr

D
dangqingqing 已提交
130 131
        parent_layers = {"input": self.__inputs__}
        super(MixedLayerV2, self).__init__(name, parent_layers)
132 133 134 135
        self.__other_kwargs__ = other_kwargs

    def __iadd__(self, other):
        if not self.finalized:
D
dangqingqing 已提交
136
            self.__inputs__.append(other)
137 138 139 140 141
            return self
        else:
            raise MixedLayerTypeV2.AddToSealedMixedLayerExceptionV2()

    def __enter__(self):
D
dangqingqing 已提交
142
        assert len(self.__inputs__) == 0
143 144 145 146 147 148 149 150 151 152 153
        return self

    def __exit__(self, *args, **kwargs):
        self.finalized = True

    def to_proto_impl(self, **kwargs):
        args = dict()
        for each in kwargs:
            args[each] = kwargs[each]
        for each in self.__other_kwargs__:
            args[each] = self.__other_kwargs__[each]
D
dangqingqing 已提交
154
        return getattr(conf_helps, self.__method_name__)(**args)
155 156 157


@wrap_name_default("mixed")
D
dangqingqing 已提交
158
@wrap_act_default(act=activation.Linear())
159 160 161 162 163 164 165 166 167 168 169
@wrap_bias_attr_default(has_bias=False)
@layer_support(conf_helps.layers.ERROR_CLIPPING, conf_helps.layers.DROPOUT)
def mixed(size=0,
          name=None,
          input=None,
          act=None,
          bias_attr=False,
          layer_attr=None):
    return MixedLayerV2(size, input, name, act, bias_attr, layer_attr)


Q
qiaolongfei 已提交
170
LayerV2 = Layer
Q
qiaolongfei 已提交
171
data = DataLayerV2
L
Luo Tao 已提交
172 173 174
AggregateLevel = conf_helps.layers.AggregateLevel
ExpandLevel = conf_helps.layers.ExpandLevel

Y
Yu Yang 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219

def __layer_name_mapping__(inname):
    if inname in ['data_layer', 'memory', 'mixed_layer']:
        # Do Not handle these layers
        return
    elif inname == 'maxid_layer':
        return 'max_id'
    elif inname.endswith('memory') or inname.endswith(
            '_seq') or inname.endswith('_sim') or inname == 'hsigmoid':
        return inname
    elif inname in [
            'cross_entropy', 'multi_binary_label_cross_entropy',
            'cross_entropy_with_selfnorm'
    ]:
        return inname + "_cost"
    elif inname.endswith('_cost'):
        return inname
    elif inname.endswith("_layer"):
        return inname[:-len("_layer")]


def __layer_name_mapping_parent_names__(inname):
    all_args = getattr(conf_helps, inname).argspec.args
    return filter(
        lambda x: x in ['input1', 'input2','label', 'input', 'a', 'b', 'expand_as',
                        'weights', 'vectors', 'weight', 'score', 'left', 'right'],
        all_args)


def __convert_layer__(_new_name_, _old_name_, _parent_names_):
    global __all__
    __all__.append(_new_name_)
    globals()[new_name] = __convert_to_v2__(_old_name_, _parent_names_)


for each_layer_name in dir(conf_helps):
    new_name = __layer_name_mapping__(each_layer_name)
    if new_name is not None:
        parent_names = __layer_name_mapping_parent_names__(each_layer_name)
        assert len(parent_names) != 0, each_layer_name
        __convert_layer__(new_name, each_layer_name, parent_names)

del parent_names
del new_name
del each_layer_name
Q
qiaolongfei 已提交
220

221
# convert projection
D
dangqingqing 已提交
222
for prj in __projection_names__:
L
Luo Tao 已提交
223 224
    globals()[prj] = __convert_to_v2__(
        prj, parent_names=['input'], is_default_name=False)
225 226 227 228 229 230 231 232

# convert operator
operator_list = [
    # [V1_method_name, parent_names],
    ['dotmul_operator', ['a', 'b']],
    ['conv_operator', ['img', 'filter']]
]
for op in operator_list:
L
Luo Tao 已提交
233 234
    globals()[op[0]] = __convert_to_v2__(
        op[0], parent_names=op[1], is_default_name=False)