source: src/ai/conv2d.c @ 1b9fe24

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

[conv2d] simplify padding modes enum, use _t suffix for type

  • Property mode set to 100644
File size: 14.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,   // same, aka half mode
30  PAD_VALID = 1   // valid, aka no padding
31} aubio_conv2d_padding_t;
32
33struct _aubio_conv2d_t {
34  // define internals here
35  uint_t n_filters;
36  uint_t kernel_shape[2];     // kernel sizes
37  uint_t stride_shape[2];     // stride sizes
38
39  aubio_conv2d_padding_t padding_mode;
40
41  // these will be set after calling get_output_shape
42  aubio_tensor_t *kernel;
43  fvec_t *bias;
44  uint_t output_shape[3];     // shape of output
45  uint_t padding_start[2];    // {top, left} padding
46
47#if defined(HAVE_BLAS)
48  aubio_tensor_t *padded_input;
49#endif
50};
51
52static void aubio_conv2d_debug(aubio_conv2d_t *c, aubio_tensor_t *input_tensor);
53
54aubio_conv2d_t *new_aubio_conv2d(uint_t n_filters, uint_t *kernel_shape)
55{
56  aubio_conv2d_t *c = AUBIO_NEW(aubio_conv2d_t);
57
58  // validate input parameters
59  AUBIO_GOTO_FAILURE((sint_t)n_filters >= 1);
60  AUBIO_GOTO_FAILURE((sint_t)kernel_shape[0] >= 1);
61  AUBIO_GOTO_FAILURE((sint_t)kernel_shape[1] >= 1);
62
63  // set internal variables
64  c->n_filters = n_filters;
65  c->kernel_shape[0] = kernel_shape[0];
66  c->kernel_shape[1] = kernel_shape[1];
67
68  // default to padding_mode="valid"
69  c->padding_mode = PAD_VALID;
70  // set default stride_shape to {1, 1}
71  {
72    uint_t default_stride[2] = {1, 1};
73    aubio_conv2d_set_stride(c, default_stride);
74  }
75
76  return c;
77
78failure:
79  del_aubio_conv2d(c);
80  return NULL;
81}
82
83void del_aubio_conv2d(aubio_conv2d_t *c)
84{
85  AUBIO_ASSERT(c);
86  if (c->kernel)
87    del_aubio_tensor(c->kernel);
88  if (c->bias)
89    del_fvec(c->bias);
90#if defined(HAVE_BLAS)
91  if (c->padded_input)
92    del_aubio_tensor(c->padded_input);
93#endif
94  AUBIO_FREE(c);
95}
96
97
98uint_t aubio_conv2d_set_stride(aubio_conv2d_t *c,
99    uint_t stride[2])
100{
101  if ((sint_t)stride[0] < 1) return AUBIO_FAIL;
102  if ((sint_t)stride[1] < 1) return AUBIO_FAIL;
103  c->stride_shape[0] = stride[0];
104  c->stride_shape[1] = stride[1];
105  return AUBIO_OK;
106}
107
108uint_t *aubio_conv2d_get_stride(aubio_conv2d_t *c)
109{
110  return c->stride_shape;
111}
112
113uint_t aubio_conv2d_get_output_shape(aubio_conv2d_t *c,
114    aubio_tensor_t *input_tensor,
115    uint_t *shape)
116{
117  uint_t output_shape[3] = {0, 0, c->n_filters};
118  uint_t padding_start[2] = {0, 0};
119  // total amount of padding
120  uint_t padding_shape[2] = {0, 0};
121
122  // check input parameters
123  AUBIO_ASSERT(input_tensor);
124  AUBIO_ASSERT(shape);
125
126  // reset output array
127  shape[0] = 0;
128  shape[1] = 0;
129  shape[2] = 0;
130
131  switch (c->padding_mode) {
132    case PAD_SAME:
133      // compute output shape
134      output_shape[0] = (uint_t)CEIL(input_tensor->shape[0]
135          / (smpl_t)c->stride_shape[0]);
136      output_shape[1] = (uint_t)CEIL(input_tensor->shape[1]
137          / (smpl_t)c->stride_shape[1]);
138
139      padding_shape[0] = (output_shape[0] - 1) * c->stride_shape[0]
140        + c->kernel_shape[0] - input_tensor->shape[0];
141      padding_shape[1] = (output_shape[1] - 1) * c->stride_shape[1]
142        + c->kernel_shape[1] - input_tensor->shape[1];
143
144      padding_start[0] = FLOOR(padding_shape[0] / 2);
145      padding_start[1] = FLOOR(padding_shape[1] / 2);
146
147      break;
148    case PAD_VALID:
149      output_shape[0] = (input_tensor->shape[0] - c->kernel_shape[0] + 1)
150        / c->stride_shape[0];
151      output_shape[1] = (input_tensor->shape[1] - c->kernel_shape[1] + 1)
152        / c->stride_shape[1];
153
154      padding_start[0] = 0;
155      padding_start[1] = 0;
156
157      break;
158    //case PAD_CAUSAL:
159    //  // TODO
160    //  return AUBIO_FAIL;
161    default:
162      return AUBIO_FAIL;
163  }
164
165  uint_t kernel_shape[4];
166  kernel_shape[0] = c->kernel_shape[0];
167  kernel_shape[1] = c->kernel_shape[1];
168  kernel_shape[2] = input_tensor->shape[2];
169  kernel_shape[3] = c->n_filters;
170
171  if (c->kernel) del_aubio_tensor(c->kernel);
172  if (c->bias) del_fvec(c->bias);
173
174  c->kernel = new_aubio_tensor(4, kernel_shape);
175  if (!c->kernel) return AUBIO_FAIL;
176  c->bias = new_fvec(c->n_filters);
177
178  // set internals upon success
179  c->output_shape[0] = output_shape[0];
180  c->output_shape[1] = output_shape[1];
181  c->output_shape[2] = output_shape[2];
182
183  c->padding_start[0] = padding_start[0];
184  c->padding_start[1] = padding_start[1];
185
186  // set output
187  shape[0] = output_shape[0];
188  shape[1] = output_shape[1];
189  shape[2] = output_shape[2];
190
191
192#if defined(HAVE_BLAS)
193  // im2col padding
194  padding_shape[0] = output_shape[0] * output_shape[1];
195  padding_shape[1] = c->kernel_shape[0] * c->kernel_shape[1]
196    * input_tensor->shape[2];
197  c->padded_input = new_aubio_tensor(2, padding_shape);
198  if (!c-> padded_input) {
199    AUBIO_MSG("conv2d: failed creating padded_input with shape (%d, %d, %d)\n",
200        padding_shape);
201    return AUBIO_FAIL;
202  }
203#endif
204
205  aubio_conv2d_debug(c, input_tensor);
206
207  return AUBIO_OK;
208}
209
210void aubio_conv2d_debug(aubio_conv2d_t *c, aubio_tensor_t *input_tensor)
211{
212  // print some info
213  AUBIO_ASSERT(c);
214  uint_t n_params = (c->kernel->shape[0] * c->kernel->shape[2] + 1)
215    * c->kernel->shape[1] * c->kernel->shape[3];
216
217  const char_t *tensor_str = aubio_tensor_get_shape_string(input_tensor);
218  //AUBIO_DBG("conv2d: kernel_shape_str %s\n", kernel_shape_str);
219  AUBIO_DBG("conv2d:    %15s -> (%d, %d, %d)",
220    tensor_str,
221    c->output_shape[0], c->output_shape[1], c->output_shape[2]);
222  tensor_str = aubio_tensor_get_shape_string(c->kernel);
223  AUBIO_DBG(" (n_params=%d, kernel_shape=(%d, %d),"
224      " weigths=%s, stride (%d, %d), pad_start [%d, %d])\n",
225    n_params, c->kernel_shape[0], c->kernel_shape[1],
226    tensor_str,
227    c->stride_shape[0], c->stride_shape[1],
228    -c->padding_start[0], -c->padding_start[1]);
229}
230
231uint_t aubio_conv2d_check_output_shape(aubio_conv2d_t *c,
232    aubio_tensor_t *input_tensor,
233    aubio_tensor_t *activations)
234{
235  // fetch output_shape if it hasn't been done before
236  if (c->output_shape[0] == 0 ||
237      c->output_shape[1] == 0 ||
238      c->output_shape[2] == 0) {
239    if (!aubio_conv2d_get_output_shape(c, input_tensor, c->output_shape)) {
240      return AUBIO_FAIL;
241    }
242  }
243
244  // check we have as many filters as expected activation outputs
245  if (activations->shape[2] != c->n_filters) return AUBIO_FAIL;
246  if (activations->shape[2] != c->kernel->shape[3]) return AUBIO_FAIL;
247  if (input_tensor->shape[2] != c->kernel->shape[2]) return AUBIO_FAIL;
248
249  // check tensor activations has the expected sizes
250  if (c->output_shape[0] != activations->shape[0]) return AUBIO_FAIL;
251  if (c->output_shape[1] != activations->shape[1]) return AUBIO_FAIL;
252  if (c->output_shape[2] != activations->shape[2]) return AUBIO_FAIL;
253  return AUBIO_OK;
254}
255
256#if !defined(HAVE_BLAS)
257void aubio_conv2d_do(aubio_conv2d_t *c, aubio_tensor_t *input_tensor,
258    aubio_tensor_t *activations)
259{
260  uint_t i, j, k, l, a, b;
261  uint_t stride_a, stride_b;
262  sint_t x, y;
263  smpl_t s, w, bias, acc;
264  uint_t jj, ll, bb, yy;
265
266  uint_t k_stride1 = c->kernel->shape[3];
267  uint_t k_stride2 = c->kernel->shape[2] * k_stride1;
268
269  AUBIO_ASSERT(c && input_tensor && activations);
270  // check we have the correct output activation sizes
271  if (aubio_conv2d_check_output_shape(c, input_tensor, activations))
272  {
273    AUBIO_ERR("conv2d: check_output_shape failed\n");
274    return;
275  }
276
277  // for each kernel filter k
278  for (i = 0; i < activations->shape[2]; i++) {
279    // get bias
280    bias = c->bias->data[i];
281    stride_b = 0; // == j * c->stride_shape[1]
282    jj = 0; // == j * activations->shape[2]
283    // for each output y
284    for (j = 0; j < activations->shape[1]; j++) {
285      // for each output x
286      stride_a = 0; // k * c->stride_shape[0]
287      for (k = 0; k < activations->shape[0]; k++) {
288        // reset output
289        acc = 0;
290        // compute convolution for one kernel
291        for (a = 0; a < c->kernel_shape[0]; a++) {
292          x = stride_a + a - c->padding_start[0];
293          if ((x < 0) || (x > (sint_t)input_tensor->shape[0] - 1))
294            continue; // padding with 0.
295          bb = 0; // == b * k_stride2
296          for (b = 0; b < c->kernel_shape[1]; b++) {
297            y = stride_b + b - c->padding_start[1];
298            if ((y < 0) || (y > (sint_t)input_tensor->shape[1] - 1))
299              continue; // padding with 0.
300            yy = y * input_tensor->shape[2];
301            ll = bb + i; // + l * k_stride1
302            // for each input channel
303            for (l = 0; l < input_tensor->shape[2]; l++) {
304              // get kernel weight
305              w = c->kernel->data[a][ll];
306              // get input sample
307              s = input_tensor->data[x][yy + l];
308              acc += w * s;
309              ll += k_stride1;
310            }
311            bb += k_stride2;
312          }
313        }
314        stride_a += c->stride_shape[0];
315        // apply bias
316        acc += bias;
317        // set output activation
318        activations->data[k][jj + i] = acc;
319      }
320      stride_b += c->stride_shape[1];
321      jj += activations->shape[2];
322    }
323  }
324}
325
326#else /* HAVE_BLAS */
327
328void aubio_conv2d_copy_to_padded(aubio_conv2d_t *o,
329    aubio_tensor_t *input_tensor, aubio_tensor_t *padded_input)
330{
331  // naive implementation of im2col
332  uint_t i, j, k, l, m;
333  uint_t stride_4 = o->kernel->shape[2];
334  uint_t stride_3 = o->kernel->shape[1] * stride_4;
335  uint_t stride_2 = o->kernel->shape[0] * stride_3;
336  uint_t stride_1 = o->output_shape[1] * stride_2;
337  uint_t stride_in_2 = input_tensor->shape[2];
338  uint_t stride_in_1 = input_tensor->shape[1] * stride_in_2;
339
340  AUBIO_ASSERT(padded_input->size ==
341      o->output_shape[0] * o->output_shape[1]
342      * o->kernel_shape[0] * o->kernel_shape[1]
343      * input_tensor->shape[2]);
344  AUBIO_ASSERT(input_tensor->shape[2] == o->kernel->shape[2]);
345
346  for (i = 0; i < o->output_shape[0]; i++)
347  {
348    for (j = 0; j <  o->output_shape[1]; j++)
349    {
350      for (k = 0; k < o->kernel->shape[0]; k++)
351      {
352        for (l = 0; l < o->kernel->shape[1]; l++)
353        {
354          for (m = 0; m < o->kernel->shape[2]; m++)
355          {
356            uint_t read_i = i * o->stride_shape[0] + k;
357            uint_t read_j = j * o->stride_shape[1] + l;
358            if (read_i < o->padding_start[0])
359              continue;
360            else if (read_i - o->padding_start[0] >= input_tensor->shape[0])
361              continue;
362            if (read_j < o->padding_start[1])
363              continue;
364            else if (read_j - o->padding_start[1] >= input_tensor->shape[1])
365              continue;
366
367            sint_t idx =
368              ((read_i - o->padding_start[0])) * stride_in_1
369              + ((read_j - o->padding_start[1])) * stride_in_2
370              + m;
371            padded_input->buffer[i * stride_1
372              + j * stride_2
373              + k * stride_3
374              + l * stride_4
375              + m]
376              = input_tensor->buffer[idx];
377          }
378        }
379      }
380    }
381  }
382}
383
384void aubio_conv2d_do(aubio_conv2d_t *o, aubio_tensor_t *input_tensor,
385    aubio_tensor_t *activations)
386{
387  uint_t i, j;
388  smpl_t bias;
389  aubio_tensor_t *padded_input = o->padded_input;
390  aubio_tensor_t *kernel = o->kernel;
391
392  AUBIO_ASSERT(o && input_tensor && activations);
393  // check we have the correct output activation sizes
394  if (aubio_conv2d_check_output_shape(o, input_tensor, activations))
395  {
396    AUBIO_ERR("conv2d: check_output_shape failed\n");
397    return;
398  }
399
400  uint_t M = padded_input->shape[0];
401  uint_t K = padded_input->size/padded_input->shape[0];
402  uint_t N = kernel->size / K;
403
404  // check sizes
405  AUBIO_ASSERT(M * K == padded_input->size);
406  AUBIO_ASSERT(N * K == kernel->size);
407  AUBIO_ASSERT(M * N == activations->size);
408
409  // copy input to im2col sliding window version
410  aubio_conv2d_copy_to_padded(o, input_tensor, padded_input);
411
412  aubio_cblas__gemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
413      M,                    // M
414      N,                    // N
415      K,                    // K
416      1.F,                  // alpha
417      padded_input->buffer, // M x K matrix
418      K,                    // K (2nd dim of A)
419      kernel->buffer,       // K x N matrix
420      N,                    // N
421      0.F,                  // beta
422      activations->buffer,  // M x N matrix
423      N);                   // N (2nd dim of C)
424
425
426  // apply bias
427  for (i = 0; i < activations->shape[2]; i++) {
428    bias = o->bias->data[i];
429    for (j = 0; j < activations->shape[0] * activations->shape[1]; j++)
430    {
431      activations->buffer[j * activations->shape[2] + i] += bias;
432    }
433  }
434}
435#endif
436
437void aubio_conv2d_do_backwards(aubio_conv2d_t *c,
438    /*aubio_tensor_t *old_gradients,*/
439    aubio_tensor_t *gradients)
440{
441  uint_t i, j, k, a, b;
442  AUBIO_ASSERT(c && gradients);
443  // TODO
444  // for each kernel filter k
445  for (i = 0; i < c->n_filters; i++) {
446    // for each input column
447    for (j = 0; j < gradients->shape[1]; j++) {
448      // for each input row
449      for (k = 0; k < gradients->shape[2]; k++) {
450        for (a = 0; a < c->kernel_shape[0]; a++) {
451          for (b = 0; b < c->kernel_shape[1]; b++) {
452#if 0
453            smpl_t grad = gradients->data[i]->data[a][b];
454            smpl_t oldgrad = old_gradients->data[i]->data[a][b];
455            smpl_t m = (grad - oldgrad * momentum);
456            w -= lr * m - lr * decay * w;
457#endif
458          }
459        }
460      }
461    }
462  }
463}
464
465uint_t aubio_conv2d_set_padding_mode(aubio_conv2d_t *c,
466    const char_t *padding_mode)
467{
468  AUBIO_ASSERT(c && padding_mode);
469  if (strncmp(padding_mode, "same", PATH_MAX) == 0) {
470    c->padding_mode = PAD_SAME;
471  } else if (strncmp(padding_mode, "valid", PATH_MAX) == 0) {
472    c->padding_mode = PAD_VALID;
473  } else {
474    return AUBIO_FAIL;
475  }
476  return AUBIO_OK;
477}
478
479uint_t aubio_conv2d_set_kernel(aubio_conv2d_t *c, aubio_tensor_t *kernel)
480{
481  uint_t i;
482  AUBIO_ASSERT(c && kernel);
483  for (i = 0; i < c->kernel->ndim; i++) {
484    AUBIO_ASSERT(c->kernel->shape[i] == kernel->shape[i]);
485  }
486  return AUBIO_OK;
487}
488
489aubio_tensor_t *aubio_conv2d_get_kernel(aubio_conv2d_t* c)
490{
491  AUBIO_ASSERT(c && c->kernel);
492  return c->kernel;
493}
494
495uint_t aubio_conv2d_set_bias(aubio_conv2d_t *c, fvec_t *bias)
496{
497  AUBIO_ASSERT(c && bias);
498  AUBIO_ASSERT(c->kernel_shape[1] == bias->length);
499  return AUBIO_OK;
500}
501
502fvec_t *aubio_conv2d_get_bias(aubio_conv2d_t* c)
503{
504  AUBIO_ASSERT(c && c->bias);
505  return c->bias;
506}
Note: See TracBrowser for help on using the repository browser.