source: src/ai/conv2d.c @ b6097ac

feature/cnnfeature/crepe
Last change on this file since b6097ac was b6097ac, checked in by Paul Brossier <piem@piem.org>, 2 years ago

[conv2d] set_stride accepts int[]

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/*
2  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
3
4  This file is part of aubio.
5
6  aubio is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10
11  aubio is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
18
19*/
20
21
22#include "aubio_priv.h"
23#include "fmat.h"
24#include "tensor.h"
25#include "conv2d.h"
26
27typedef enum
28{
29  PAD_SAME = 0, // TODO
30  PAD_VALID = 1,
31  //PAD_CAUSAL = 2, // TODO (1d only, for dilated convolution)
32} aubio_conv2d_padding_type;
33
34struct _aubio_conv2d_t {
35  // define internals here
36  uint_t n_filters;
37  uint_t kernel_shape[2];     // kernel sizes
38  uint_t stride_shape[2];     // stride sizes
39
40  aubio_conv2d_padding_type padding_mode;
41
42  // these will be set after calling get_output_shape
43  aubio_tensor_t *kernel;
44  fvec_t *bias;
45  uint_t output_shape[3];     // shape of output
46  uint_t padding_start[2];    // {top, left} padding
47};
48
49static void aubio_conv2d_debug(aubio_conv2d_t *c, aubio_tensor_t *input_tensor);
50
51aubio_conv2d_t *new_aubio_conv2d(uint_t n_filters, uint_t *kernel_shape)
52{
53  aubio_conv2d_t *c = AUBIO_NEW(aubio_conv2d_t);
54
55  // validate input parameters
56  AUBIO_GOTO_FAILURE((sint_t)n_filters >= 1);
57  AUBIO_GOTO_FAILURE((sint_t)kernel_shape[0] >= 1);
58  AUBIO_GOTO_FAILURE((sint_t)kernel_shape[1] >= 1);
59
60  // set internal variables
61  c->n_filters = n_filters;
62  c->kernel_shape[0] = kernel_shape[0];
63  c->kernel_shape[1] = kernel_shape[1];
64
65  // default to padding_mode="valid"
66  c->padding_mode = PAD_VALID;
67  // set default stride_shape to {1, 1}
68  {
69    uint_t default_stride[2] = {1, 1};
70    aubio_conv2d_set_stride(c, default_stride);
71  }
72
73  return c;
74
75failure:
76  del_aubio_conv2d(c);
77  return NULL;
78}
79
80void del_aubio_conv2d(aubio_conv2d_t *c)
81{
82  AUBIO_ASSERT(c);
83  // destroy internals here
84  if (c->kernel) {
85    del_aubio_tensor(c->kernel);
86  }
87  if (c->bias)
88    del_fvec(c->bias);
89  AUBIO_FREE(c);
90}
91
92
93uint_t aubio_conv2d_set_stride(aubio_conv2d_t *c,
94    uint_t stride[2])
95{
96  if ((sint_t)stride[0] < 1) return AUBIO_FAIL;
97  if ((sint_t)stride[1] < 1) return AUBIO_FAIL;
98  c->stride_shape[0] = stride[0];
99  c->stride_shape[1] = stride[1];
100  return AUBIO_OK;
101}
102
103uint_t *aubio_conv2d_get_stride(aubio_conv2d_t *c)
104{
105  return c->stride_shape;
106}
107
108uint_t aubio_conv2d_get_output_shape(aubio_conv2d_t *c,
109    aubio_tensor_t *input_tensor,
110    uint_t *shape)
111{
112  uint_t output_shape[3] = {0, 0, c->n_filters};
113  uint_t padding_start[2] = {0, 0};
114
115  // check input parameters
116  AUBIO_ASSERT(input_tensor);
117  AUBIO_ASSERT(shape);
118
119  // reset output array
120  shape[0] = 0;
121  shape[1] = 0;
122  shape[2] = 0;
123
124  switch (c->padding_mode) {
125    case PAD_SAME:
126      // compute output shape
127      output_shape[0] = (uint_t)CEIL(input_tensor->shape[0]
128          / (smpl_t)c->stride_shape[0]);
129      output_shape[1] = (uint_t)CEIL(input_tensor->shape[1]
130          / (smpl_t)c->stride_shape[1]);
131
132      uint_t padding_shape[2];  // total amount of padding
133      padding_shape[0] = (output_shape[0] - 1) * c->stride_shape[0]
134        + c->kernel_shape[0] - input_tensor->shape[0];
135      padding_shape[1] = (output_shape[1] - 1) * c->stride_shape[1]
136        + c->kernel_shape[1] - input_tensor->shape[1];
137
138      padding_start[0] = FLOOR(padding_shape[0] / 2);
139      padding_start[1] = FLOOR(padding_shape[1] / 2);
140
141      break;
142    case PAD_VALID:
143      output_shape[0] = (input_tensor->shape[0] - c->kernel_shape[0] + 1)
144        / c->stride_shape[0];
145      output_shape[1] = (input_tensor->shape[1] - c->kernel_shape[1] + 1)
146        / c->stride_shape[1];
147
148      padding_start[0] = 0;
149      padding_start[1] = 0;
150      break;
151    //case PAD_CAUSAL:
152    //  // TODO
153    //  return AUBIO_FAIL;
154    default:
155      return AUBIO_FAIL;
156  }
157
158  uint_t kernel_shape[4];
159  kernel_shape[0] = c->kernel_shape[0];
160  kernel_shape[1] = c->kernel_shape[1];
161  kernel_shape[2] = input_tensor->shape[2];
162  kernel_shape[3] = c->n_filters;
163
164  if (c->kernel) del_aubio_tensor(c->kernel);
165  if (c->bias) del_fvec(c->bias);
166
167  c->kernel = new_aubio_tensor(4, kernel_shape);
168  if (!c->kernel) return AUBIO_FAIL;
169  c->bias = new_fvec(c->n_filters);
170
171  // set internals upon success
172  c->output_shape[0] = output_shape[0];
173  c->output_shape[1] = output_shape[1];
174  c->output_shape[2] = output_shape[2];
175
176  c->padding_start[0] = padding_start[0];
177  c->padding_start[1] = padding_start[1];
178
179  // set output
180  shape[0] = output_shape[0];
181  shape[1] = output_shape[1];
182  shape[2] = output_shape[2];
183
184  aubio_conv2d_debug(c, input_tensor);
185
186  return AUBIO_OK;
187}
188
189void aubio_conv2d_debug(aubio_conv2d_t *c, aubio_tensor_t *input_tensor)
190{
191  // print some info
192  AUBIO_ASSERT(c);
193  uint_t n_params = (c->kernel->shape[0] * c->kernel->shape[2] + 1)
194    * c->kernel->shape[1] * c->kernel->shape[3];
195
196  AUBIO_DBG("conv2d:    %15s ¤ conv2d %s"
197      " : (%d, %d, %d)"
198      " (%d params, stride (%d, %d), pad_start [%d, %d])\n",
199    aubio_tensor_get_shape_string(input_tensor),
200    aubio_tensor_get_shape_string(c->kernel),
201    c->output_shape[0], c->output_shape[1], c->output_shape[2],
202    n_params,
203    c->stride_shape[0], c->stride_shape[1],
204    -c->padding_start[0], -c->padding_start[1]);
205}
206
207uint_t aubio_conv2d_check_output_shape(aubio_conv2d_t *c,
208    aubio_tensor_t *input_tensor,
209    aubio_tensor_t *activations)
210{
211  // fetch output_shape if it hasn't been done before
212  if (c->output_shape[0] == 0 ||
213      c->output_shape[1] == 0 ||
214      c->output_shape[2] == 0) {
215    if (!aubio_conv2d_get_output_shape(c, input_tensor, c->output_shape)) {
216      return AUBIO_FAIL;
217    }
218  }
219
220  // check we have as many filters as expected activation outputs
221  if (activations->shape[2] != c->n_filters) return AUBIO_FAIL;
222  if (activations->shape[2] != c->kernel->shape[3]) return AUBIO_FAIL;
223  if (input_tensor->shape[2] != c->kernel->shape[2]) return AUBIO_FAIL;
224
225  // check tensor activations has the expected sizes
226  if (c->output_shape[0] != activations->shape[0]) return AUBIO_FAIL;
227  if (c->output_shape[1] != activations->shape[1]) return AUBIO_FAIL;
228  if (c->output_shape[2] != activations->shape[2]) return AUBIO_FAIL;
229  return AUBIO_OK;
230}
231
232void aubio_conv2d_do(aubio_conv2d_t *c, aubio_tensor_t *input_tensor,
233    aubio_tensor_t *activations)
234{
235  uint_t i, j, k, l, a, b;
236  uint_t stride_a, stride_b;
237  sint_t x, y;
238  smpl_t s, w, bias, acc;
239  uint_t jj, ll, bb, yy;
240
241  uint_t k_stride1 = c->kernel->shape[3];
242  uint_t k_stride2 = c->kernel->shape[2] * k_stride1;
243
244  AUBIO_ASSERT(c && input_tensor && activations);
245  // check we have the correct output activation sizes
246  if (aubio_conv2d_check_output_shape(c, input_tensor, activations))
247  {
248    AUBIO_ERR("conv2d: check_output_shape failed\n");
249    return;
250  }
251
252  // for each kernel filter k
253  for (i = 0; i < activations->shape[2]; i++) {
254    // get bias
255    bias = c->bias->data[i];
256    stride_b = 0; // == j * c->stride_shape[1]
257    jj = 0; // == j * activations->shape[2]
258    // for each output y
259    for (j = 0; j < activations->shape[1]; j++) {
260      // for each output x
261      stride_a = 0; // k * c->stride_shape[0]
262      for (k = 0; k < activations->shape[0]; k++) {
263        // reset output
264        acc = 0;
265        // compute convolution for one kernel
266        for (a = 0; a < c->kernel_shape[0]; a++) {
267          x = stride_a + a - c->padding_start[0];
268          if ((x < 0) || (x > (sint_t)input_tensor->shape[0] - 1))
269            continue; // padding with 0.
270          bb = 0; // == b * k_stride2
271          for (b = 0; b < c->kernel_shape[1]; b++) {
272            y = stride_b + b - c->padding_start[1];
273            if ((y < 0) || (y > (sint_t)input_tensor->shape[1] - 1))
274              continue; // padding with 0.
275            yy = y * input_tensor->shape[2];
276            ll = bb + i; // + l * k_stride1
277            // for each input channel
278            for (l = 0; l < input_tensor->shape[2]; l++) {
279              // get kernel weight
280              w = c->kernel->data[a][ll];
281              // get input sample
282              s = input_tensor->data[x][yy + l];
283              acc += w * s;
284              ll += k_stride1;
285            }
286            bb += k_stride2;
287          }
288        }
289        stride_a += c->stride_shape[0];
290        // apply bias
291        acc += bias;
292        // compute RELU
293        activations->data[k][jj + i] = MAX(acc, 0);
294      }
295      stride_b += c->stride_shape[1];
296      jj += activations->shape[2];
297    }
298  }
299}
300
301void aubio_conv2d_do_backwards(aubio_conv2d_t *c,
302    /*aubio_tensor_t *old_gradients,*/
303    aubio_tensor_t *gradients)
304{
305  uint_t i, j, k, a, b;
306  AUBIO_ASSERT(c && gradients);
307  // TODO
308  // for each kernel filter k
309  for (i = 0; i < c->n_filters; i++) {
310    // for each input column
311    for (j = 0; j < gradients->shape[1]; j++) {
312      // for each input row
313      for (k = 0; k < gradients->shape[2]; k++) {
314        for (a = 0; a < c->kernel_shape[0]; a++) {
315          for (b = 0; b < c->kernel_shape[1]; b++) {
316#if 0
317            smpl_t grad = gradients->data[i]->data[a][b];
318            smpl_t oldgrad = old_gradients->data[i]->data[a][b];
319            smpl_t m = (grad - oldgrad * momentum);
320            w -= lr * m - lr * decay * w;
321#endif
322          }
323        }
324      }
325    }
326  }
327}
328
329uint_t aubio_conv2d_set_padding_mode(aubio_conv2d_t *c,
330    const char_t *padding_mode)
331{
332  AUBIO_ASSERT(c && padding_mode);
333  if (strncmp(padding_mode, "same", PATH_MAX) == 0) {
334    c->padding_mode = PAD_SAME;
335  } else if (strncmp(padding_mode, "valid", PATH_MAX) == 0) {
336    c->padding_mode = PAD_VALID;
337  } else {
338    return AUBIO_FAIL;
339  }
340  return AUBIO_OK;
341}
342
343uint_t aubio_conv2d_set_kernel(aubio_conv2d_t *c, aubio_tensor_t *kernel)
344{
345  uint_t i;
346  AUBIO_ASSERT(c && kernel);
347  for (i = 0; i < c->kernel->ndim; i++) {
348    AUBIO_ASSERT(c->kernel->shape[i] == kernel->shape[i]);
349  }
350  return AUBIO_OK;
351}
352
353aubio_tensor_t *aubio_conv2d_get_kernel(aubio_conv2d_t* c)
354{
355  AUBIO_ASSERT(c && c->kernel);
356  return c->kernel;
357}
358
359uint_t aubio_conv2d_set_bias(aubio_conv2d_t *c, fvec_t *bias)
360{
361  AUBIO_ASSERT(c && bias);
362  AUBIO_ASSERT(c->kernel_shape[1] == bias->length);
363  return AUBIO_OK;
364}
365
366fvec_t *aubio_conv2d_get_bias(aubio_conv2d_t* c)
367{
368  AUBIO_ASSERT(c && c->bias);
369  return c->bias;
370}
Note: See TracBrowser for help on using the repository browser.