Index: src/ai/batchnorm.c
===================================================================
--- src/ai/batchnorm.c	(revision 7b2a58c41a206275eab1ab4953ce9aa35db17d2b)
+++ src/ai/batchnorm.c	(revision 7b2a58c41a206275eab1ab4953ce9aa35db17d2b)
@@ -0,0 +1,186 @@
+/*
+  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
+
+  This file is part of aubio.
+
+  aubio is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  aubio is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+#include "fmat.h"
+#include "tensor.h"
+#include "batchnorm.h"
+
+struct _aubio_batchnorm_t {
+  uint_t n_outputs;
+  fvec_t *gamma;
+  fvec_t *beta;
+  fvec_t *moving_mean;
+  fvec_t *moving_variance;
+};
+
+static void aubio_batchnorm_debug(aubio_batchnorm_t *c,
+    aubio_tensor_t *input_tensor);
+
+aubio_batchnorm_t *new_aubio_batchnorm(uint_t n_outputs)
+{
+  aubio_batchnorm_t *c = AUBIO_NEW(aubio_batchnorm_t);
+
+  AUBIO_GOTO_FAILURE((sint_t)n_outputs > 0);
+
+  c->n_outputs = n_outputs;
+
+  c->gamma = new_fvec(n_outputs);
+  c->beta = new_fvec(n_outputs);
+  c->moving_mean = new_fvec(n_outputs);
+  c->moving_variance = new_fvec(n_outputs);
+
+  return c;
+
+failure:
+  del_aubio_batchnorm(c);
+  return NULL;
+}
+
+void del_aubio_batchnorm(aubio_batchnorm_t* c) {
+  AUBIO_ASSERT(c);
+  if (c->gamma)
+    del_fvec(c->gamma);
+  if (c->beta)
+    del_fvec(c->beta);
+  if (c->moving_mean)
+    del_fvec(c->moving_mean);
+  if (c->moving_variance)
+    del_fvec(c->moving_variance);
+  AUBIO_FREE(c);
+}
+
+void aubio_batchnorm_debug(aubio_batchnorm_t *c, aubio_tensor_t *input_tensor)
+{
+  const char_t *shape_str = aubio_tensor_get_shape_string(input_tensor);
+  AUBIO_DBG("batchnorm: %15s ¤ (%d x4) -> %s (4 x %d params)\n",
+      shape_str, c->n_outputs, shape_str, c->n_outputs);
+}
+
+uint_t aubio_batchnorm_get_output_shape(aubio_batchnorm_t *c,
+    aubio_tensor_t *input, uint_t *shape)
+{
+  AUBIO_ASSERT(c && input && shape);
+
+  shape[0] = input->shape[0];
+  shape[1] = input->shape[1];
+  shape[2] = input->shape[2];
+
+  aubio_batchnorm_debug(c, input);
+
+  return AUBIO_OK;
+}
+
+void aubio_batchnorm_do(aubio_batchnorm_t *c, aubio_tensor_t *input_tensor,
+    aubio_tensor_t *activations)
+{
+  uint_t i, j, k;
+  uint_t jj;
+  smpl_t s;
+  AUBIO_ASSERT(c);
+  AUBIO_ASSERT_EQUAL_SHAPE(input_tensor, activations);
+  if (input_tensor->ndim == 3) {
+    for (i = 0; i < activations->shape[0]; i++) {
+      jj = 0;
+      for (j = 0; j < activations->shape[1]; j++) {
+        for (k = 0; k < activations->shape[2]; k++) {
+          s = input_tensor->data[i][jj + k];
+          s -= c->moving_mean->data[k];
+          s *= c->gamma->data[k];
+          s /= SQRT(c->moving_variance->data[k] + 1.e-4);
+          s += c->beta->data[k];
+          activations->data[i][jj + k] = s;
+        }
+        jj += activations->shape[2];
+      }
+    }
+  } else if (input_tensor->ndim == 2) {
+    for (i = 0; i < activations->shape[0]; i++) {
+      for (j = 0; j < activations->shape[1]; j++) {
+        s = input_tensor->data[i][j];
+        s -= c->moving_mean->data[j];
+        s *= c->gamma->data[j];
+        s /= SQRT(c->moving_variance->data[j] + 1.e-4);
+        s += c->beta->data[j];
+        activations->data[i][j] = s;
+      }
+    }
+  }
+}
+
+uint_t aubio_batchnorm_set_gamma(aubio_batchnorm_t *t, fvec_t *gamma)
+{
+  AUBIO_ASSERT(t && t->gamma);
+  AUBIO_ASSERT(gamma);
+  if (t->gamma->length != gamma->length) return AUBIO_FAIL;
+  fvec_copy(gamma, t->gamma);
+  return AUBIO_OK;
+}
+
+uint_t aubio_batchnorm_set_beta(aubio_batchnorm_t *t, fvec_t *beta)
+{
+  AUBIO_ASSERT(t && t->beta);
+  AUBIO_ASSERT(beta);
+  if (t->beta->length != beta->length) return AUBIO_FAIL;
+  fvec_copy(beta, t->beta);
+  return AUBIO_OK;
+}
+
+uint_t aubio_batchnorm_set_moving_mean(aubio_batchnorm_t *t, fvec_t *moving_mean)
+{
+  AUBIO_ASSERT(t && t->moving_mean);
+  AUBIO_ASSERT(moving_mean);
+  if (t->moving_mean->length != moving_mean->length) return AUBIO_FAIL;
+  fvec_copy(moving_mean, t->moving_mean);
+  return AUBIO_OK;
+}
+
+uint_t aubio_batchnorm_set_moving_variance(aubio_batchnorm_t *t, fvec_t *moving_variance)
+{
+  AUBIO_ASSERT(t && t->moving_variance);
+  AUBIO_ASSERT(moving_variance);
+  if (t->moving_variance->length != moving_variance->length) return AUBIO_FAIL;
+  fvec_copy(moving_variance, t->moving_variance);
+  return AUBIO_OK;
+}
+
+fvec_t *aubio_batchnorm_get_gamma(aubio_batchnorm_t *t)
+{
+  AUBIO_ASSERT(t && t->gamma);
+  return t->gamma;
+}
+
+fvec_t *aubio_batchnorm_get_beta(aubio_batchnorm_t *t)
+{
+  AUBIO_ASSERT(t && t->beta);
+  return t->beta;
+}
+
+fvec_t *aubio_batchnorm_get_moving_mean(aubio_batchnorm_t *t)
+{
+  AUBIO_ASSERT(t && t->moving_mean);
+  return t->moving_mean;
+}
+
+fvec_t *aubio_batchnorm_get_moving_variance(aubio_batchnorm_t *t)
+{
+  AUBIO_ASSERT(t && t->moving_variance);
+  return t->moving_variance;
+}
Index: src/ai/batchnorm.h
===================================================================
--- src/ai/batchnorm.h	(revision 7b2a58c41a206275eab1ab4953ce9aa35db17d2b)
+++ src/ai/batchnorm.h	(revision 7b2a58c41a206275eab1ab4953ce9aa35db17d2b)
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
+
+  This file is part of aubio.
+
+  aubio is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  aubio is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef AUBIO_BATCHNORM_H
+#define AUBIO_BATCHNORM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file
+
+ Batch normalization layer.
+
+ References
+ ----------
+
+ Ioffe, Sergey; Szegedy, Christian. "Batch Normalization: Accelerating Deep
+ Network Training by Reducing Internal Covariate Shift", available online
+ at https://arxiv.org/pdf/1502.03167.pdf
+
+*/
+
+typedef struct _aubio_batchnorm_t aubio_batchnorm_t;
+
+aubio_batchnorm_t *new_aubio_batchnorm(uint_t n_outputs);
+
+void aubio_batchnorm_do(aubio_batchnorm_t *t,
+        aubio_tensor_t *input_tensor,
+        aubio_tensor_t *activations);
+
+void aubio_batchnorm_train(aubio_batchnorm_t *t, aubio_tensor_t *input);
+
+uint_t aubio_batchnorm_set_gamma(aubio_batchnorm_t *t, fvec_t *gamma);
+uint_t aubio_batchnorm_set_beta(aubio_batchnorm_t *t, fvec_t *beta);
+uint_t aubio_batchnorm_set_moving_mean(aubio_batchnorm_t *t, fvec_t *moving_mean);
+uint_t aubio_batchnorm_set_moving_variance(aubio_batchnorm_t *t, fvec_t *moving_variance);
+
+fvec_t *aubio_batchnorm_get_gamma(aubio_batchnorm_t *t);
+fvec_t *aubio_batchnorm_get_beta(aubio_batchnorm_t *t);
+fvec_t *aubio_batchnorm_get_moving_mean(aubio_batchnorm_t *t);
+fvec_t *aubio_batchnorm_get_moving_variance(aubio_batchnorm_t *t);
+
+uint_t aubio_batchnorm_get_output_shape(aubio_batchnorm_t *t,
+        aubio_tensor_t *input, uint_t *shape);
+
+void del_aubio_batchnorm(aubio_batchnorm_t *t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUBIO_BATCHNORM_H */
