aboutsummaryrefslogtreecommitdiff
path: root/nerv/nn/layer_repo.lua
blob: 35ac10489f21b5f61e92f311b10916f0fd6809a8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
--- Implements a concecpt that stores a collection of layers.

--- The class for storing a collection of layers.
-- @type nerv.LayerRepo

local LayerRepo = nerv.class("nerv.LayerRepo")

--- The constructor.
-- @param layer_spec the *layer specification*, a declarative way of creating layers in the collection. The layer specification is structured as follow:
--
--    {
--        [<layer_typename1>] =
--        {
--            <layer_id1> = <layer_conf1>,
--            <layer_id2> = <layer_conf2>,
--            <layer_id3> = <layer_conf3>,
--            ...
--        },
--        [<layer_typename2>] =
--        {
--            ...
--        },
--        ...
--    }
-- To be short, the specification is a table containing pairs of a layer type
-- name string (such as `"nerv.AffineLayer"`) and a table which maps layer
-- identifiers to `layer_conf`. For `layer_conf`, see `nerv.Layer.__init` and
-- the `__init` doc for an individual layer type.
--
-- Here is an example:
--
--    {
--        ["nerv.AffineLayer"] =
--        {
--            affine0 = {dim_in = {429}, dim_out = {2048},
--                        params = {ltp = "affine0_ltp", bp = "affine0_bp"}},
--            affine1 = {dim_in = {2048}, dim_out = {2048},
--                        params = {ltp = "affine1_ltp", bp = "affine1_bp"}},
--            affine2 = {dim_in = {2048}, dim_out = {2048},
--                        params = {ltp = "affine2_ltp", bp = "affine2_bp"}},
--            affine3 = {dim_in = {2048}, dim_out = {2048},
--                        params = {ltp = "affine3_ltp", bp = "affine3_bp"}},
--            affine4 = {dim_in = {2048}, dim_out = {2048},
--                        params = {ltp = "affine4_ltp", bp = "affine4_bp"}},
--            affine5 = {dim_in = {2048}, dim_out = {2048},
--                        params = {ltp = "affine5_ltp", bp = "affine5_bp"}},
--            affine6 = {dim_in = {2048}, dim_out = {2048},
--                        params = {ltp = "affine6_ltp", bp = "affine6_bp"}},
--            affine7 = {dim_in = {2048}, dim_out = {3001},
--                        params = {ltp = "affine7_ltp", bp = "affine7_bp"}}
--        },
--        ["nerv.SigmoidLayer"] =
--        {
--            sigmoid0 = {dim_in = {2048}, dim_out = {2048}},
--            sigmoid1 = {dim_in = {2048}, dim_out = {2048}},
--            sigmoid2 = {dim_in = {2048}, dim_out = {2048}},
--            sigmoid3 = {dim_in = {2048}, dim_out = {2048}},
--            sigmoid4 = {dim_in = {2048}, dim_out = {2048}},
--            sigmoid5 = {dim_in = {2048}, dim_out = {2048}},
--            sigmoid6 = {dim_in = {2048}, dim_out = {2048}}
--        },
--        ["nerv.SoftmaxCELayer"] = -- softmax + ce criterion layer for finetune output
--        {
--            ce_crit = {dim_in = {3001, 1}, dim_out = {1}, compressed = true}
--        },
--        ["nerv.SoftmaxLayer"] = -- softmax for decode output
--        {
--            softmax = {dim_in = {3001}, dim_out = {3001}}
--        }
--    }
-- @param param_repo the default parameter repo to be used for binding parameters, if one layer
-- does not specify `pr` in its layer config `layer_conf`
-- @param global_conf a table describing the computation state and providing
-- with some global settings

function LayerRepo:__init(layer_spec, param_repo, global_conf)
    self.layers = {}
    self:add_layers(layer_spec, param_repo, global_conf)
end

--- Add more layers to the collection (repo).
-- @param layer_spec the *layer specification*, a declarative way of creating layers in the collection.
-- @param param_repo the default parameter repo to be used for binding parameters, if one layer
-- does not specify `pr` in its layer config `layer_conf`
-- @param global_conf a table describing the computation state and providing
-- with some global settings
--
-- Here is an example for adding graph layers based on the previous example:
--    layer_repo:add_layers(
--    {
--        ["nerv.GraphLayer"] =
--        {
--            global_transf = {
--                dim_in = {429}, dim_out = {429},
--                layer_repo = layer_repo,
--                connections = {
--                    {"<input>[1]", "blayer1[1]", 0},
--                    {"blayer1[1]", "wlayer1[1]", 0},
--                    {"wlayer1[1]", "blayer2[1]", 0},
--                    {"blayer2[1]", "wlayer2[1]", 0},
--                    {"wlayer2[1]", "<output>[1]", 0}
--                }
--            },
--            main = {
--                dim_in = {429}, dim_out = {3001},
--                layer_repo = layer_repo,
--                connections = {
--                    {"<input>[1]", "affine0[1]", 0},
--                    {"affine0[1]", "sigmoid0[1]", 0},
--                    {"sigmoid0[1]", "affine1[1]", 0},
--                    {"affine1[1]", "sigmoid1[1]", 0},
--                    {"sigmoid1[1]", "affine2[1]", 0},
--                    {"affine2[1]", "sigmoid2[1]", 0},
--                    {"sigmoid2[1]", "affine3[1]", 0},
--                    {"affine3[1]", "sigmoid3[1]", 0},
--                    {"sigmoid3[1]", "affine4[1]", 0},
--                    {"affine4[1]", "sigmoid4[1]", 0},
--                    {"sigmoid4[1]", "affine5[1]", 0},
--                    {"affine5[1]", "sigmoid5[1]", 0},
--                    {"sigmoid5[1]", "affine6[1]", 0},
--                    {"affine6[1]", "sigmoid6[1]", 0},
--                    {"sigmoid6[1]", "affine7[1]", 0},
--                    {"affine7[1]", "<output>[1]", 0}
--                }
--            }
--        }
--    }, param_repo, gconf)
--
-- To fully understand the example, please check the doc for `nerv.GraphLayer`,
-- and notice that `layer_repo` itself is passed to the graph layer config because
-- primitive layers such as `"affine0"` have been created by the layer
-- specification during the construction (see the example in `__init`).

function LayerRepo:add_layers(layer_spec, param_repo, global_conf)
    local layers = self.layers
    for ltype, llist in pairs(layer_spec) do
        local layer_type = nerv.get_type(ltype)
        if layer_type == nil then
            nerv.error('layer type `%s` not found', ltype)
        end
        for id, lconf in pairs(llist) do
            if type(lconf) ~= "table" then
                nerv.error("layer config table is need")
            end
            if lconf.pr == nil then
                lconf.pr = param_repo
            end
            if layers[id] ~= nil then
                nerv.error("a layer with id %s already exists", id)
            end
            nerv.info("create layer: %s", id)
            layers[id] = layer_type(id, global_conf, lconf)
        end
    end
end

--- Rebind the parameters.
-- @param param_repo the new parameter repo used for parameter rebinding

function LayerRepo:rebind(param_repo)
    if self.__rebinding then
        return
    end
    self.__rebinding = true
    for _, layer in pairs(self.layers) do
        if not layer.__already_rebound then
            layer.__already_rebound = true
            layer.lconf.pr = param_repo
            layer:bind_params()
        end
    end
    for _, layer in pairs(self.layers) do
        layer.__already_rebound = false
    end
    self.__rebinding = false
end

--- Get a layer from the collection (repo) by its identifier.
-- @param lid the layer id
function LayerRepo:get_layer(lid)
    local layer = self.layers[lid]
    if layer == nil then
        nerv.error("layer with id %s not found", lid)
    end
    return layer
end