source: src/ai/conv2d.c @ b2e2cd0

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

[conv2d] remove relu

  • Property mode set to 100644
File size: 10.5 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  if (c->kernel)
84    del_aubio_tensor(c->kernel);
85  if (c->bias)
86    del_fvec(c->bias);
87  AUBIO_FREE(c);
88}
89
90
91uint_t aubio_conv2d_set_stride(aubio_conv2d_t *c,
92    uint_t stride[2])
93{
94  if ((sint_t)stride[0] < 1) return AUBIO_FAIL;
95  if ((sint_t)stride[1] < 1) return AUBIO_FAIL;
96  c->stride_shape[0] = stride[0];
97  c->stride_shape[1] = stride[1];
98  return AUBIO_OK;
99}
100
101uint_t *aubio_conv2d_get_stride(aubio_conv2d_t *c)
102{
103  return c->stride_shape;
104}
105
106uint_t aubio_conv2d_get_output_shape(aubio_conv2d_t *c,
107    aubio_tensor_t *input_tensor,
108    uint_t *shape)
109{
110  uint_t output_shape[3] = {0, 0, c->n_filters};
111  uint_t padding_start[2] = {0, 0};
112
113  // check input parameters
114  AUBIO_ASSERT(input_tensor);
115  AUBIO_ASSERT(shape);
116
117  // reset output array
118  shape[0] = 0;
119  shape[1] = 0;
120  shape[2] = 0;
121
122  switch (c->padding_mode) {
123    case PAD_SAME:
124      // compute output shape
125      output_shape[0] = (uint_t)CEIL(input_tensor->shape[0]
126          / (smpl_t)c->stride_shape[0]);
127      output_shape[1] = (uint_t)CEIL(input_tensor->shape[1]
128          / (smpl_t)c->stride_shape[1]);
129
130      uint_t padding_shape[2];  // total amount of padding
131      padding_shape[0] = (output_shape[0] - 1) * c->stride_shape[0]
132        + c->kernel_shape[0] - input_tensor->shape[0];
133      padding_shape[1] = (output_shape[1] - 1) * c->stride_shape[1]
134        + c->kernel_shape[1] - input_tensor->shape[1];
135
136      padding_start[0] = FLOOR(padding_shape[0] / 2);
137      padding_start[1] = FLOOR(padding_shape[1] / 2);
138
139      break;
140    case PAD_VALID:
141      output_shape[0] = (input_tensor->shape[0] - c->kernel_shape[0] + 1)
142        / c->stride_shape[0];
143      output_shape[1] = (input_tensor->shape[1] - c->kernel_shape[1] + 1)
144        / c->stride_shape[1];
145
146      padding_start[0] = 0;
147      padding_start[1] = 0;
148
149      break;
150    //case PAD_CAUSAL:
151    //  // TODO
152    //  return AUBIO_FAIL;
153    default:
154      return AUBIO_FAIL;
155  }
156
157  uint_t kernel_shape[4];
158  kernel_shape[0] = c->kernel_shape[0];
159  kernel_shape[1] = c->kernel_shape[1];
160  kernel_shape[2] = input_tensor->shape[2];
161  kernel_shape[3] = c->n_filters;
162
163  if (c->kernel) del_aubio_tensor(c->kernel);
164  if (c->bias) del_fvec(c->bias);
165
166  c->kernel = new_aubio_tensor(4, kernel_shape);
167  if (!c->kernel) return AUBIO_FAIL;
168  c->bias = new_fvec(c->n_filters);
169
170  // set internals upon success
171  c->output_shape[0] = output_shape[0];
172  c->output_shape[1] = output_shape[1];
173  c->output_shape[2] = output_shape[2];
174
175  c->padding_start[0] = padding_start[0];
176  c->padding_start[1] = padding_start[1];
177
178  // set output
179  shape[0] = output_shape[0];
180  shape[1] = output_shape[1];
181  shape[2] = output_shape[2];
182
183  aubio_conv2d_debug(c, input_tensor);
184
185  return AUBIO_OK;
186}
187
188void aubio_conv2d_debug(aubio_conv2d_t *c, aubio_tensor_t *input_tensor)
189{
190  // print some info
191  AUBIO_ASSERT(c);
192  uint_t n_params = (c->kernel->shape[0] * c->kernel->shape[2] + 1)
193    * c->kernel->shape[1] * c->kernel->shape[3];
194
195  const char_t *tensor_str = aubio_tensor_get_shape_string(input_tensor);
196  //AUBIO_DBG("conv2d: kernel_shape_str %s\n", kernel_shape_str);
197  AUBIO_DBG("conv2d:    %15s -> (%d, %d, %d)",
198    tensor_str,
199    c->output_shape[0], c->output_shape[1], c->output_shape[2]);
200  tensor_str = aubio_tensor_get_shape_string(c->kernel);
201  AUBIO_DBG(" (n_params=%d, kernel_shape=(%d, %d),"
202      " weigths=%s, stride (%d, %d), pad_start [%d, %d]"
203      " pad_end [%d, %d])\n",
204    n_params, c->kernel_shape[0], c->kernel_shape[1],
205    tensor_str,
206    c->stride_shape[0], c->stride_shape[1],
207    -c->padding_start[0], -c->padding_start[1]);
208}
209
210uint_t aubio_conv2d_check_output_shape(aubio_conv2d_t *c,
211    aubio_tensor_t *input_tensor,
212    aubio_tensor_t *activations)
213{
214  // fetch output_shape if it hasn't been done before
215  if (c->output_shape[0] == 0 ||
216      c->output_shape[1] == 0 ||
217      c->output_shape[2] == 0) {
218    if (!aubio_conv2d_get_output_shape(c, input_tensor, c->output_shape)) {
219      return AUBIO_FAIL;
220    }
221  }
222
223  // check we have as many filters as expected activation outputs
224  if (activations->shape[2] != c->n_filters) return AUBIO_FAIL;
225  if (activations->shape[2] != c->kernel->shape[3]) return AUBIO_FAIL;
226  if (input_tensor->shape[2] != c->kernel->shape[2]) return AUBIO_FAIL;
227
228  // check tensor activations has the expected sizes
229  if (c->output_shape[0] != activations->shape[0]) return AUBIO_FAIL;
230  if (c->output_shape[1] != activations->shape[1]) return AUBIO_FAIL;
231  if (c->output_shape[2] != activations->shape[2]) return AUBIO_FAIL;
232  return AUBIO_OK;
233}
234
235void aubio_conv2d_do(aubio_conv2d_t *c, aubio_tensor_t *input_tensor,
236    aubio_tensor_t *activations)
237{
238  uint_t i, j, k, l, a, b;
239  uint_t stride_a, stride_b;
240  sint_t x, y;
241  smpl_t s, w, bias, acc;
242  uint_t jj, ll, bb, yy;
243
244  uint_t k_stride1 = c->kernel->shape[3];
245  uint_t k_stride2 = c->kernel->shape[2] * k_stride1;
246
247  AUBIO_ASSERT(c && input_tensor && activations);
248  // check we have the correct output activation sizes
249  if (aubio_conv2d_check_output_shape(c, input_tensor, activations))
250  {
251    AUBIO_ERR("conv2d: check_output_shape failed\n");
252    return;
253  }
254
255  // for each kernel filter k
256  for (i = 0; i < activations->shape[2]; i++) {
257    // get bias
258    bias = c->bias->data[i];
259    stride_b = 0; // == j * c->stride_shape[1]
260    jj = 0; // == j * activations->shape[2]
261    // for each output y
262    for (j = 0; j < activations->shape[1]; j++) {
263      // for each output x
264      stride_a = 0; // k * c->stride_shape[0]
265      for (k = 0; k < activations->shape[0]; k++) {
266        // reset output
267        acc = 0;
268        // compute convolution for one kernel
269        for (a = 0; a < c->kernel_shape[0]; a++) {
270          x = stride_a + a - c->padding_start[0];
271          if ((x < 0) || (x > (sint_t)input_tensor->shape[0] - 1))
272            continue; // padding with 0.
273          bb = 0; // == b * k_stride2
274          for (b = 0; b < c->kernel_shape[1]; b++) {
275            y = stride_b + b - c->padding_start[1];
276            if ((y < 0) || (y > (sint_t)input_tensor->shape[1] - 1))
277              continue; // padding with 0.
278            yy = y * input_tensor->shape[2];
279            ll = bb + i; // + l * k_stride1
280            // for each input channel
281            for (l = 0; l < input_tensor->shape[2]; l++) {
282              // get kernel weight
283              w = c->kernel->data[a][ll];
284              // get input sample
285              s = input_tensor->data[x][yy + l];
286              acc += w * s;
287              ll += k_stride1;
288            }
289            bb += k_stride2;
290          }
291        }
292        stride_a += c->stride_shape[0];
293        // apply bias
294        acc += bias;
295        // set output activation
296        activations->data[k][jj + i] = acc;
297      }
298      stride_b += c->stride_shape[1];
299      jj += activations->shape[2];
300    }
301  }
302}
303
304void aubio_conv2d_do_backwards(aubio_conv2d_t *c,
305    /*aubio_tensor_t *old_gradients,*/
306    aubio_tensor_t *gradients)
307{
308  uint_t i, j, k, a, b;
309  AUBIO_ASSERT(c && gradients);
310  // TODO
311  // for each kernel filter k
312  for (i = 0; i < c->n_filters; i++) {
313    // for each input column
314    for (j = 0; j < gradients->shape[1]; j++) {
315      // for each input row
316      for (k = 0; k < gradients->shape[2]; k++) {
317        for (a = 0; a < c->kernel_shape[0]; a++) {
318          for (b = 0; b < c->kernel_shape[1]; b++) {
319#if 0
320            smpl_t grad = gradients->data[i]->data[a][b];
321            smpl_t oldgrad = old_gradients->data[i]->data[a][b];
322            smpl_t m = (grad - oldgrad * momentum);
323            w -= lr * m - lr * decay * w;
324#endif
325          }
326        }
327      }
328    }
329  }
330}
331
332uint_t aubio_conv2d_set_padding_mode(aubio_conv2d_t *c,
333    const char_t *padding_mode)
334{
335  AUBIO_ASSERT(c && padding_mode);
336  if (strncmp(padding_mode, "same", PATH_MAX) == 0) {
337    c->padding_mode = PAD_SAME;
338  } else if (strncmp(padding_mode, "valid", PATH_MAX) == 0) {
339    c->padding_mode = PAD_VALID;
340  } else {
341    return AUBIO_FAIL;
342  }
343  return AUBIO_OK;
344}
345
346uint_t aubio_conv2d_set_kernel(aubio_conv2d_t *c, aubio_tensor_t *kernel)
347{
348  uint_t i;
349  AUBIO_ASSERT(c && kernel);
350  for (i = 0; i < c->kernel->ndim; i++) {
351    AUBIO_ASSERT(c->kernel->shape[i] == kernel->shape[i]);
352  }
353  return AUBIO_OK;
354}
355
356aubio_tensor_t *aubio_conv2d_get_kernel(aubio_conv2d_t* c)
357{
358  AUBIO_ASSERT(c && c->kernel);
359  return c->kernel;
360}
361
362uint_t aubio_conv2d_set_bias(aubio_conv2d_t *c, fvec_t *bias)
363{
364  AUBIO_ASSERT(c && bias);
365  AUBIO_ASSERT(c->kernel_shape[1] == bias->length);
366  return AUBIO_OK;
367}
368
369fvec_t *aubio_conv2d_get_bias(aubio_conv2d_t* c)
370{
371  AUBIO_ASSERT(c && c->bias);
372  return c->bias;
373}
Note: See TracBrowser for help on using the repository browser.