source: src/ai/conv1d.c @ be3164d

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

[ai] only compile _debug function in debug mode

  • Property mode set to 100644
File size: 9.7 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 "conv1d.h"
26
27typedef enum
28{
29  PAD_SAME = 0,
30  PAD_VALID = 1,
31  PAD_CAUSAL = 2, // TODO (1d only, for dilated convolution)
32} aubio_conv1d_padding_type;
33
34struct _aubio_conv1d_t {
35  // define internals here
36  uint_t n_filters;
37  uint_t kernel_shape;     // kernel sizes
38  uint_t stride_shape[1];  // stride sizes
39
40  aubio_conv1d_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[2];  // shape of output
46  uint_t padding_start;    // left padding
47
48#if defined(HAVE_BLAS)
49  aubio_tensor_t *padded_input;
50#endif
51};
52
53#if defined(DEBUG)
54static
55void aubio_conv1d_debug(aubio_conv1d_t *c, aubio_tensor_t *input_tensor);
56#endif
57
58aubio_conv1d_t *new_aubio_conv1d(uint_t n_filters, uint_t kernel_shape[1])
59{
60  aubio_conv1d_t *c = AUBIO_NEW(aubio_conv1d_t);
61
62  // validate input parameters
63  AUBIO_GOTO_FAILURE((sint_t)n_filters >= 1);
64  AUBIO_GOTO_FAILURE((sint_t)kernel_shape[0] >= 1);
65
66  // set internal variables
67  c->n_filters = n_filters;
68  c->kernel_shape = kernel_shape[0];
69
70  // default to padding_mode="valid"
71  c->padding_mode = PAD_VALID;
72  // set default stride_shape to (1)
73  uint_t stride_shape[1] = {1};
74  aubio_conv1d_set_stride(c, stride_shape);
75
76  return c;
77
78failure:
79  del_aubio_conv1d(c);
80  return NULL;
81}
82
83void del_aubio_conv1d(aubio_conv1d_t *c)
84{
85  AUBIO_ASSERT(c);
86  // destroy internals here
87  if (c->kernel) {
88    del_aubio_tensor(c->kernel);
89  }
90  if (c->bias)
91    del_fvec(c->bias);
92#if defined(HAVE_BLAS)
93  if (c->padded_input) del_aubio_tensor(c->padded_input);
94#endif
95  AUBIO_FREE(c);
96}
97
98
99uint_t aubio_conv1d_set_stride(aubio_conv1d_t *c, uint_t stride[1])
100{
101  if ((sint_t)stride[0] < 1) return AUBIO_FAIL;
102  c->stride_shape[0] = stride[0];
103  return AUBIO_OK;
104}
105
106uint_t *aubio_conv1d_get_stride(aubio_conv1d_t *c)
107{
108  return c->stride_shape;
109}
110
111uint_t aubio_conv1d_get_output_shape(aubio_conv1d_t *c,
112    aubio_tensor_t *input_tensor,
113    uint_t *shape)
114{
115  uint_t output_shape[2] = {0, c->n_filters};
116  uint_t padding_shape = 0;  // total amount of padding
117  uint_t padding_start = 0;
118
119  // check input parameters
120  AUBIO_ASSERT(input_tensor);
121  AUBIO_ASSERT(shape);
122
123  // reset output array
124  shape[0] = 0;
125  shape[1] = 0;
126
127  switch (c->padding_mode) {
128    case PAD_SAME:
129      // compute output shape
130      output_shape[0] = (uint_t)CEIL(input_tensor->shape[0]
131          / (smpl_t)c->stride_shape[0]);
132
133      padding_shape = (output_shape[0] - 1) * c->stride_shape[0] +
134        c->kernel_shape - input_tensor->shape[0];
135
136      padding_start = FLOOR(padding_shape / 2);
137      break;
138    case PAD_VALID:
139      output_shape[0] = (input_tensor->shape[0] - c->kernel_shape + 1)
140        / c->stride_shape[0];
141
142      padding_start = 0;
143      break;
144    case PAD_CAUSAL:
145      // TODO
146      return AUBIO_FAIL;
147    default:
148      return AUBIO_FAIL;
149  }
150
151  uint_t kernel_shape[3];
152  kernel_shape[0] = c->kernel_shape; // filter length
153  kernel_shape[1] = input_tensor->shape[1]; // channels
154  kernel_shape[2] = c->n_filters; // outputs
155
156  if (c->kernel) del_aubio_tensor(c->kernel);
157  if (c->bias) del_fvec(c->bias);
158
159  c->kernel = new_aubio_tensor(3, kernel_shape);
160  if (!c->kernel) return AUBIO_FAIL;
161  c->bias = new_fvec(c->n_filters);
162
163  // set internals upon success
164  c->output_shape[0] = output_shape[0];
165  c->output_shape[1] = output_shape[1];
166
167#if defined(HAVE_BLAS)
168  if (c->padded_input) del_aubio_tensor(c->padded_input);
169  uint_t padded_shape[2] = {input_tensor->shape[0] + padding_shape,
170    input_tensor->shape[1]};
171  c->padded_input = new_aubio_tensor(2, padded_shape);
172#endif
173
174  c->padding_start = padding_start;
175
176  // set output
177  shape[0] = output_shape[0];
178  shape[1] = output_shape[1];
179
180#if defined(DEBUG)
181  aubio_conv1d_debug(c, input_tensor);
182#endif
183
184  return AUBIO_OK;
185}
186
187#if defined(DEBUG)
188void aubio_conv1d_debug(aubio_conv1d_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];
194  AUBIO_DBG("conv1d:    %15s -> (%d, %d) (%d params)"
195      " (weigths=(%d, %d, %d), stride=(%d,), pad_start=(%d,))\n",
196    aubio_tensor_get_shape_string(input_tensor),
197    c->output_shape[0], c->output_shape[1],
198    n_params,
199    c->kernel->shape[0], c->kernel->shape[1], c->kernel->shape[2],
200    c->stride_shape[0],
201    -c->padding_start);
202}
203#endif
204
205uint_t aubio_conv1d_check_output_shape(aubio_conv1d_t *c,
206    aubio_tensor_t *input_tensor,
207    aubio_tensor_t *activations)
208{
209  // fetch output_shape if it hasn't been done before
210  if (c->output_shape[0] == 0 ||
211      c->output_shape[1] == 0) {
212    if (!aubio_conv1d_get_output_shape(c, input_tensor, c->output_shape)) {
213      return AUBIO_FAIL;
214    }
215  }
216
217  // check we have as many filters as expected activation outputs
218  if (activations->shape[1] != c->n_filters) return AUBIO_FAIL;
219  if (activations->shape[1] != c->kernel->shape[2]) return AUBIO_FAIL;
220  if (input_tensor->shape[1] != c->kernel->shape[1]) return AUBIO_FAIL;
221
222  // check tensor activations has the expected sizes
223  if (c->output_shape[0] != activations->shape[0]) return AUBIO_FAIL;
224  if (c->output_shape[1] != activations->shape[1]) return AUBIO_FAIL;
225  return AUBIO_OK;
226}
227
228#if !defined(HAVE_BLAS)
229void aubio_conv1d_do(aubio_conv1d_t *c, aubio_tensor_t *input_tensor,
230    aubio_tensor_t *activations)
231{
232  uint_t i, j, k, a;
233  uint_t stride_a, kk;
234  sint_t x;
235  smpl_t s, w, bias, acc;
236
237  AUBIO_ASSERT(c && input_tensor && activations);
238  // check we have the correct output activation sizes
239  if (aubio_conv1d_check_output_shape(c, input_tensor, activations))
240  {
241    AUBIO_ERR("conv1d: check_output_shape failed\n");
242    return;
243  }
244
245  // for each kernel filter k
246  for (i = 0; i < activations->shape[1]; i++) {
247    // get bias
248    bias = c->bias->data[i];
249    stride_a = 0; // j * c->stride_shape
250    // for each output
251    for (j = 0; j < activations->shape[0]; j++) {
252      // reset output
253      acc = 0;
254      // compute convolution for one kernel
255      for (a = 0; a < c->kernel_shape; a++) {
256        x = stride_a + a - c->padding_start;
257        if ((x > -1) && (x < (sint_t)input_tensor->shape[0])) {
258          kk = 0;
259          // for each input channel
260          for (k = 0; k < input_tensor->shape[1]; k++) {
261            // get kernel weight
262            w = c->kernel->data[a][kk + i];
263            // get input sample
264            s = input_tensor->data[x][k];
265            acc += w * s;
266            kk += c->kernel->shape[2];
267          }
268        }
269      }
270      stride_a += c->stride_shape[0];
271      // apply bias
272      activations->data[j][i] = acc + bias;
273    }
274  }
275}
276
277#else /* HAVE_BLAS */
278
279// blas implementation
280//
281//  uses gemv on the padded input to compute each output elements at once
282//
283// TODO
284//  - avoid copy when padding_start == 0
285//  - optimize copying using tensor helpers
286
287void aubio_conv1d_do(aubio_conv1d_t *c, aubio_tensor_t *input_tensor,
288    aubio_tensor_t *activations)
289{
290  uint_t i, j;
291
292  uint_t sdot_size = c->kernel->shape[0] * c->kernel->shape[1];
293  uint_t input_stride = c->stride_shape[0] * c->padded_input->shape[1];
294
295  AUBIO_ASSERT(c && input_tensor && activations);
296  if (aubio_conv1d_check_output_shape(c, input_tensor, activations))
297  {
298    AUBIO_ERR("conv1d: check_output_shape failed\n");
299    return;
300  }
301
302  // copy input to padded version
303  for (j = 0; j < input_tensor->shape[0]; j++) {
304    for (i = 0; i < input_tensor->shape[1]; i++) {
305      c->padded_input->data[j + c->padding_start][i] =
306        input_tensor->data[j][i];
307    }
308  }
309
310  // for each output
311  for (j = 0; j < activations->shape[0]; j++) {
312    // for each row of activation output
313    aubio_cblas__gemv(CblasRowMajor, CblasTrans,
314        sdot_size, c->kernel->shape[2], 1.,
315        c->kernel->buffer, c->kernel->shape[2],
316        c->padded_input->buffer + j  * input_stride, 1, 0.,
317        activations->buffer + j * activations->shape[1], 1);
318  }
319  for (j = 0; j < activations->shape[0]; j++) {
320    // for each kernel filter k
321    for (i = 0; i < activations->shape[1]; i++) {
322      activations->data[j][i] += c->bias->data[i];
323    }
324  }
325}
326#endif /* HAVE_BLAS */
327
328uint_t aubio_conv1d_set_padding_mode(aubio_conv1d_t *c,
329    const char_t *padding_mode)
330{
331  AUBIO_ASSERT(c && padding_mode);
332  if (strncasecmp(padding_mode, "same", PATH_MAX) == 0) {
333    c->padding_mode = PAD_SAME;
334  } else if (strncasecmp(padding_mode, "valid", PATH_MAX) == 0) {
335    c->padding_mode = PAD_VALID;
336  } else {
337    return AUBIO_FAIL;
338  }
339  return AUBIO_OK;
340}
341
342aubio_tensor_t *aubio_conv1d_get_kernel(aubio_conv1d_t* c)
343{
344  AUBIO_ASSERT(c && c->kernel);
345  return c->kernel;
346}
347
348uint_t aubio_conv1d_set_kernel(aubio_conv1d_t *c, aubio_tensor_t *kernel)
349{
350  AUBIO_ASSERT(c && kernel);
351  if (aubio_tensor_have_same_shape(c->kernel, kernel)) {
352    aubio_tensor_copy(kernel, c->kernel);
353    return AUBIO_OK;
354  }
355  return AUBIO_FAIL;
356}
357
358uint_t aubio_conv1d_set_bias(aubio_conv1d_t *c, fvec_t *bias)
359{
360  AUBIO_ASSERT(c && bias);
361  if (bias->length == c->bias->length) {
362    fvec_copy(bias, c->bias);
363    return AUBIO_OK;
364  }
365  return AUBIO_FAIL;
366}
367
368fvec_t *aubio_conv1d_get_bias(aubio_conv1d_t* c)
369{
370  AUBIO_ASSERT(c && c->bias);
371  return c->bias;
372}
Note: See TracBrowser for help on using the repository browser.