Line data Source code
1 : #include "Conv.h"
2 : #include "TypeCasts.hpp"
3 :
4 : namespace elsa::ml
5 : {
6 : template <typename data_t>
7 2 : ConvBase<data_t>::ConvBase(LayerType layerType, index_t numberOfFilters,
8 : const VolumeDescriptor& filterDescriptor, Activation activation,
9 : index_t strides, Padding padding, bool useBias,
10 : Initializer kernelInitializer, Initializer biasInitializer,
11 : const std::string& name, int requiredNumberOfDimensions)
12 : : Trainable<data_t>(layerType, activation, useBias, kernelInitializer, biasInitializer,
13 : name, requiredNumberOfDimensions),
14 : numberOfFilters_(numberOfFilters),
15 : filterDescriptor_(filterDescriptor.clone()),
16 : strides_(strides),
17 2 : padding_(padding)
18 : {
19 2 : }
20 :
21 : template <typename data_t>
22 3 : index_t ConvBase<data_t>::getNumberOfFilters() const
23 : {
24 3 : return numberOfFilters_;
25 : }
26 :
27 : template <typename data_t>
28 4 : VolumeDescriptor ConvBase<data_t>::getFilterDescriptor() const
29 : {
30 4 : if (!filterDescriptor_)
31 0 : throw std::logic_error("Filter descriptor not set");
32 :
33 : // Downcast to VolumeDescriptor
34 4 : return downcast<VolumeDescriptor>(*filterDescriptor_);
35 : }
36 :
37 : template <typename data_t>
38 3 : index_t ConvBase<data_t>::getStrides() const
39 : {
40 3 : return strides_;
41 : }
42 :
43 : template <typename data_t>
44 1 : Padding ConvBase<data_t>::getPadding() const
45 : {
46 1 : return padding_;
47 : }
48 :
49 : template <typename data_t>
50 2 : Conv<data_t>::Conv(index_t convolutionDimensions, index_t numberOfFilters,
51 : const VolumeDescriptor& filterDescriptor, Activation activation,
52 : index_t strides, Padding padding, bool useBias,
53 : Initializer kernelInitializer, Initializer biasInitializer,
54 : const std::string& name)
55 : : ConvBase<data_t>(
56 : convolutionDimensions == 1
57 : ? LayerType::Conv1D
58 : : (convolutionDimensions == 2 ? LayerType::Conv2D : LayerType::Conv3D),
59 : numberOfFilters, filterDescriptor, activation, strides, padding, useBias,
60 : kernelInitializer, biasInitializer, name,
61 : /* required number of input dims */ static_cast<int>(convolutionDimensions) + 1),
62 2 : convolutionDimensions_(convolutionDimensions)
63 : {
64 2 : }
65 :
66 : template <typename data_t>
67 1 : IndexVector_t Conv<data_t>::getPaddingSizes() const
68 : {
69 : // We pad spatial dimensions only
70 1 : IndexVector_t paddingSize(asUnsigned(this->convolutionDimensions_));
71 :
72 1 : switch (this->getPadding()) {
73 : // Valid padding means no padding at all
74 0 : case Padding::Valid:
75 0 : paddingSize.setZero();
76 0 : break;
77 : // Same padding means we pad the input such that the output has unchanges spatial
78 : // dimensions. In theory we wouldn't need to do anything here since we already
79 : // know the dimensions of the output descriptor (same as the ones of the input
80 : // descriptor), however, the backend won't understand magic terms such as *Valid* or
81 : // *Same* and needs bare padding numbers.
82 : //
83 : // Since the spatial output dimensions of a convolution operations
84 : // calculates as
85 : // o = (i-p+k)/s+1
86 : // we calculate same padding as
87 : // p = s(i-1)-1-k
88 1 : case Padding::Same:
89 3 : for (int dim = 0; dim < paddingSize.size(); ++dim) {
90 2 : const index_t inputDim =
91 2 : this->getInputDescriptor().getNumberOfCoefficientsPerDimension()[dim];
92 2 : const index_t kernelDim =
93 2 : this->getFilterDescriptor().getNumberOfCoefficientsPerDimension()[dim];
94 2 : paddingSize[dim] = this->getStrides() * (inputDim - 1) - inputDim + kernelDim;
95 : }
96 1 : break;
97 : }
98 1 : return paddingSize;
99 : }
100 :
101 : template <typename data_t>
102 1 : void Conv<data_t>::computeOutputDescriptor()
103 : {
104 1 : if (this->inputDescriptors_.size() != 1) {
105 : throw std::invalid_argument(
106 0 : "Cannot compute output descriptor without input descriptor");
107 : }
108 :
109 2 : if (this->inputDescriptors_.front()->getNumberOfCoefficientsPerDimension().tail(1)(0)
110 1 : != this->filterDescriptor_->getNumberOfCoefficientsPerDimension().tail(1)(0))
111 0 : throw std::invalid_argument("Input and filter channels must match");
112 :
113 2 : IndexVector_t dims(convolutionDimensions_ + 1);
114 2 : auto padding = getPaddingSizes();
115 3 : for (index_t idx = 0; idx < convolutionDimensions_; ++idx) {
116 : // output = (input - kernel + 2 * padding) / stride + 1
117 4 : dims[idx] = (this->inputDescriptors_.front()->getNumberOfCoefficientsPerDimension()[idx]
118 4 : - this->filterDescriptor_->getNumberOfCoefficientsPerDimension()[idx]
119 2 : + padding[idx])
120 2 : / this->strides_
121 2 : + 1;
122 : }
123 1 : dims(convolutionDimensions_) = this->numberOfFilters_;
124 1 : VolumeDescriptor desc(dims);
125 1 : this->outputDescriptor_ = desc.clone();
126 1 : this->numberOfTrainableParameters_ =
127 2 : this->getNumberOfFilters() * this->getFilterDescriptor().getNumberOfCoefficients()
128 1 : + (this->useBias_ ? this->getNumberOfFilters() : 0);
129 1 : }
130 :
131 : template <typename data_t>
132 1 : Conv1D<data_t>::Conv1D(index_t numberOfFilters, const VolumeDescriptor& filterDescriptor,
133 : Activation activation, index_t strides, Padding padding, bool useBias,
134 : Initializer kernelInitializer, Initializer biasInitializer,
135 : const std::string& name)
136 : : Conv<data_t>(1, numberOfFilters, filterDescriptor, activation, strides, padding, useBias,
137 1 : kernelInitializer, biasInitializer, name)
138 : {
139 1 : }
140 :
141 : template <typename data_t>
142 0 : Conv1D<data_t>::Conv1D(index_t numberOfFilters, const std::array<index_t, 2>& filterSize,
143 : Activation activation, index_t strides, Padding padding, bool useBias,
144 : Initializer kernelInitializer, Initializer biasInitializer,
145 : const std::string& name)
146 : : Conv<data_t>(1, numberOfFilters,
147 0 : VolumeDescriptor(IndexVector_t{
148 : {/* width */ filterSize[0], /* channels */ filterSize[1]}}),
149 : activation, strides, padding, useBias, kernelInitializer, biasInitializer,
150 0 : name)
151 : {
152 0 : }
153 :
154 : template <typename data_t>
155 0 : Conv2D<data_t>::Conv2D(index_t numberOfFilters, const VolumeDescriptor& filterDescriptor,
156 : Activation activation, index_t strides, Padding padding, bool useBias,
157 : Initializer kernelInitializer, Initializer biasInitializer,
158 : const std::string& name)
159 : : Conv<data_t>(2, numberOfFilters, filterDescriptor, activation, strides, padding, useBias,
160 0 : kernelInitializer, biasInitializer, name)
161 : {
162 0 : }
163 :
164 : template <typename data_t>
165 1 : Conv2D<data_t>::Conv2D(index_t numberOfFilters, const std::array<index_t, 3>& filterSize,
166 : Activation activation, index_t strides, Padding padding, bool useBias,
167 : Initializer kernelInitializer, Initializer biasInitializer,
168 : const std::string& name)
169 : : Conv<data_t>(
170 : 2, numberOfFilters,
171 3 : VolumeDescriptor(IndexVector_t{{/* width */ filterSize[0], /* height */ filterSize[1],
172 : /* channels */ filterSize[2]}}),
173 1 : activation, strides, padding, useBias, kernelInitializer, biasInitializer, name)
174 : {
175 1 : }
176 :
177 : template <typename data_t>
178 0 : Conv3D<data_t>::Conv3D(index_t numberOfFilters, const VolumeDescriptor& filterDescriptor,
179 : Activation activation, index_t strides, Padding padding, bool useBias,
180 : Initializer kernelInitializer, Initializer biasInitializer,
181 : const std::string& name)
182 : : Conv<data_t>(3, numberOfFilters, filterDescriptor, activation, strides, padding, useBias,
183 0 : kernelInitializer, biasInitializer, name)
184 : {
185 0 : }
186 :
187 : template <typename data_t>
188 0 : ConvTranspose<data_t>::ConvTranspose(index_t convolutionDimensions, index_t numberOfFilters,
189 : const VolumeDescriptor& filterDescriptor,
190 : Activation activation, index_t strides, Padding padding,
191 : bool useBias, Initializer kernelInitializer,
192 : Initializer biasInitializer, const std::string& name)
193 : : ConvBase<data_t>(
194 : convolutionDimensions == 2 ? LayerType::Conv2DTranspose : LayerType::Conv3DTranspose,
195 : numberOfFilters, filterDescriptor, activation, strides, padding, useBias,
196 : kernelInitializer, biasInitializer, name,
197 : /* required number of input dims */ static_cast<int>(convolutionDimensions) + 1),
198 0 : convolutionDimensions_(convolutionDimensions)
199 : {
200 0 : }
201 :
202 : template <typename data_t>
203 0 : void ConvTranspose<data_t>::computeOutputDescriptor()
204 : {
205 : // Spatial dimension + channels
206 0 : IndexVector_t dims(convolutionDimensions_ + 1);
207 :
208 : // using (width x height x channels) we get
209 :
210 : // TODO(tellenbach): Add padding
211 0 : for (index_t idx = 0; idx < convolutionDimensions_; ++idx) {
212 0 : dims[idx] =
213 0 : this->strides_
214 0 : * (this->inputDescriptors_.front()->getNumberOfCoefficientsPerDimension()[idx]
215 0 : - 1)
216 0 : + this->filterDescriptor_
217 0 : ->getNumberOfCoefficientsPerDimension()[idx]; //- 2 * padding[idx];
218 : }
219 0 : dims(convolutionDimensions_) = this->numberOfFilters_;
220 :
221 0 : this->outputDescriptor_ = VolumeDescriptor(dims).clone();
222 0 : }
223 :
224 : template <typename data_t>
225 0 : Conv2DTranspose<data_t>::Conv2DTranspose(index_t numberOfFilters,
226 : const VolumeDescriptor& filterDescriptor,
227 : Activation activation, index_t strides,
228 : Padding padding, bool useBias,
229 : Initializer kernelInitializer,
230 : Initializer biasInitializer, const std::string& name)
231 : : ConvTranspose<data_t>(2, numberOfFilters, filterDescriptor, activation, strides, padding,
232 0 : useBias, kernelInitializer, biasInitializer, name)
233 : {
234 0 : }
235 :
236 : template class ConvBase<float>;
237 : template class Conv<float>;
238 : template struct Conv1D<float>;
239 : template struct Conv2D<float>;
240 : template struct Conv3D<float>;
241 : template class ConvTranspose<float>;
242 : template struct Conv2DTranspose<float>;
243 :
244 : } // namespace elsa::ml
|