aboutsummaryrefslogblamecommitdiff
path: root/nerv/nn/layer_repo.lua
blob: 35ac10489f21b5f61e92f311b10916f0fd6809a8 (plain) (tree)
1
2
3
4
5
6
7




                                                             

                                              



































































                                                                                                                                                        
                                                              



                                                        




















































                                                                                                       

                                                                  

                                               


                                                          

                                          

                                                        

                                     
               

                                                                   
               

                                                           

           

   


                                                                        
                                     

                            
       











                                           

   

                                                             






                                                     
--- 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