1 /**
2     Contains an implementation of dense (i.e., fully connected) layers.
3     Authors: Henry Gouk
4 */
5 module dopt.nnet.layers.dense;
6 
7 import dopt.core;
8 import dopt.nnet;
9 import dopt.nnet.layers.util;
10 import dopt.online;
11 
12 /**
13     Encapsulates additional options for dense layers.
14 */
15 class DenseOptions
16 {
17     this()
18     {
19         _weightInit = heGaussianInit();
20         _biasInit = constantInit(0.0f);
21         _useBias = true;
22         _weightDecay = 0;
23     }
24 
25     mixin(dynamicProperties(
26         "ParamInitializer", "weightInit",
27         "ParamInitializer", "biasInit",
28         "Projection", "weightProj",
29         "Projection", "biasProj",
30         "float", "weightDecay",
31         "bool", "useBias"
32     ));
33 }
34 
35 ///
36 unittest
37 {
38     //Create a DenseOptions object with the default parameters
39     auto opts = new DenseOptions()
40                .weightInit(heGaussianInit())
41                .biasInit(constantInit(0.0f))
42                .weightProj(null)
43                .biasProj(null)
44                .weightDecay(0.0f)
45                .useBias(true);
46     
47     //Options can also be read back again later
48     assert(opts.weightDecay == 0.0f);
49     assert(opts.useBias == true);
50 }
51 
52 /**
53     Creates a fully connected (AKA, dense) layer.
54 
55     Params:
56         input = The previous layer in the network.
57         numOutputs = The number of units in this layer.
58         opts = Additional options with sensible default values.
59     
60     Returns:
61         The new layer.
62 */
63 Layer dense(Layer input, size_t numOutputs, DenseOptions opts = new DenseOptions())
64 {
65     auto x = input.output;
66     auto xTr = input.trainOutput;
67 
68     x = x.reshape([x.shape[0], x.volume / x.shape[0]]);
69     xTr = xTr.reshape([xTr.shape[0], xTr.volume / xTr.shape[0]]);
70 
71     auto weights = float32([numOutputs, x.shape[1]]);
72     opts._weightInit(weights);
73 
74     auto weightLoss = (opts.weightDecay == 0.0f) ? null : (opts.weightDecay * sum(weights * weights));
75 
76     Parameter[] params = [
77         Parameter(weights, weightLoss, opts.weightProj)
78     ];
79 
80     auto y = matmul(x, weights.transpose([1, 0]));
81     auto yTr = matmul(xTr, weights.transpose([1, 0]));
82 
83     if(opts.useBias)
84     {
85         auto bias = float32([numOutputs]);
86         opts._biasInit(bias);
87 
88         y = y + bias.repeat(y.shape[0]);
89         yTr = yTr + bias.repeat(yTr.shape[0]);
90 
91         params ~= Parameter(bias, null, opts.biasProj);
92     }
93 
94     return new Layer([input], y, yTr, params);
95 }