1 /**
2     Contains an implementation of convolutional layers.
3     
4     Authors: Henry Gouk
5 */
6 module dopt.nnet.layers.conv;
7 
8 import dopt.core;
9 import dopt.nnet;
10 import dopt.nnet.layers.util;
11 import dopt.online;
12 
13 /**
14     Encapsulates the additional options for a $(D Layer) created with conv2D.
15 */
16 class Conv2DOptions
17 {
18     this()
19     {
20         _useBias = true;
21         _filterInit = heGaussianInit();
22         _biasInit = constantInit(0.0f);
23         _padding = [0, 0];
24         _stride = [1, 1];
25         _weightDecay = 0.0f;
26 
27     }
28 
29     mixin(dynamicProperties(
30         "size_t[]", "padding",
31         "size_t[]", "stride",
32         "ParamInitializer", "filterInit",
33         "ParamInitializer", "biasInit",
34         "Projection", "filterProj",
35         "Projection", "biasProj",
36         "float", "weightDecay",
37         "bool", "useBias"
38     ));
39 }
40 
41 ///
42 unittest
43 {
44     //Creates a Conv2DOptions object with the default parameter values
45     auto opts = new Conv2DOptions()
46                .padding([0, 0])
47                .stride([1, 1])
48                .filterInit(heGaussianInit())
49                .biasInit(constantInit(0.0f))
50                .filterProj(null)
51                .biasProj(null)
52                .weightDecay(0.0f)
53                .useBias(true);
54     
55     //The fields can also be accessed again later
56     assert(opts.padding == [0, 0]);
57     assert(opts.stride == [1, 1]);
58 }
59 
60 /**
61     Creates a convolutional layer typically found in a convnet used for image classification.
62 
63     Params:
64         input = The previous (i.e., input) layer.
65         outputChannels = The number of feature maps that this layer should produce.
66         filterDims = The size of the kernels that should be convolved with the inputs.
67         opts = Additional options, with sensible defaults.
68     
69     Returns:
70         The new convolutional $(D Layer).
71 */
72 Layer conv2D(Layer input, size_t outputChannels, size_t[] filterDims, Conv2DOptions opts = new Conv2DOptions())
73 {
74     auto padding = opts.padding;
75     auto stride = opts.stride;
76     auto filterInit = opts.filterInit;
77     auto biasInit = opts.biasInit;
78     auto filterProj = opts.filterProj;
79     auto biasProj = opts.biasProj;
80     auto weightDecay = opts.weightDecay;
81     auto useBias = opts.useBias;
82 
83     auto x = input.output;
84     auto xTr = input.trainOutput;
85 
86     auto filters = float32([outputChannels, x.shape[1]] ~ filterDims);
87     filterInit(filters);
88 
89     import std.math : isNaN;
90 
91     auto filterLoss = (weightDecay == 0.0f) ? null : (weightDecay * sum(filters * filters));
92 
93     Parameter[] params = [
94             Parameter(filters, filterLoss, filterProj)
95         ];
96 
97     auto y = x.convolution(filters, padding, stride);
98     auto yTr = xTr.convolution(filters, padding, stride);
99 
100     if(useBias)
101     {
102         auto biases = float32([outputChannels]);
103         biasInit(biases);
104 
105         y = y.addBias(biases);
106         yTr = yTr.addBias(biases);
107 
108         params ~= Parameter(biases, null, biasProj);
109     }
110 
111     return new Layer([input], y, yTr, params);
112 }