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