source: src/ai/conv1d.c @ f335a9b

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

[conv1d] improve debug string

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