source: src/ai/conv2d.c @ 0d16cf9

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

[conv2d] add simple blas version (im2row + gemm)

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