source: src/ai/conv1d.c @ 3ff9654

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

[conv1d] add set_{kernel,bias}

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