shithub: opus

Download patch

ref: b9cd61be8b882d315182d224bd657b144a92bd25
parent: 8caaa5e91742059b95be5571a62fd716cb784458
author: Jean-Marc Valin <jmvalin@jmvalin.ca>
date: Fri Nov 23 14:43:58 EST 2018

Work in progress translation to C

--- /dev/null
+++ b/dnn/dump_lpcnet.py
@@ -1,0 +1,130 @@
+#!/usr/bin/python3
+
+import lpcnet
+import sys
+import numpy as np
+from keras.optimizers import Adam
+from keras.callbacks import ModelCheckpoint
+from keras.layers import Layer, GRU, CuDNNGRU, Dense, Conv1D, Embedding
+from ulaw import ulaw2lin, lin2ulaw
+from mdense import MDense
+import keras.backend as K
+import h5py
+import re
+
+def printVector(f, vector, name):
+    v = np.reshape(vector, (-1));
+    #print('static const float ', name, '[', len(v), '] = \n', file=f)
+    f.write('static const float {}[{}] = {{\n   '.format(name, len(v)))
+    for i in range(0, len(v)):
+        f.write('{}'.format(v[i]))
+        if (i!=len(v)-1):
+            f.write(',')
+        else:
+            break;
+        if (i%8==7):
+            f.write("\n   ")
+        else:
+            f.write(" ")
+    #print(v, file=f)
+    f.write('\n};\n\n')
+    return;
+
+def dump_layer_ignore(self, f, hf):
+    print("ignoring layer " + self.name + " of type " + self.__class__.__name__)
+    False
+Layer.dump_layer = dump_layer_ignore
+
+def dump_gru_layer(self, f, hf):
+    name = self.name
+    print("printing layer " + name + " of type " + self.__class__.__name__)
+    weights = self.get_weights()
+    printVector(f, weights[0], name + '_weights')
+    printVector(f, weights[1], name + '_recurrent_weights')
+    printVector(f, weights[-1], name + '_bias')
+    #activation = re.search('function (.*) at', str(layer.activation)).group(1).upper()
+    if hasattr(self, 'activation'):
+        activation = self.activation.__name__.upper()
+    else:
+        activation = 'TANH'
+    if hasattr(self, 'reset_after'):
+        reset_after = self.reset_after
+    else:
+        reset_after = 1
+    f.write('const GRULayer {} = {{\n   {}_bias,\n   {}_weights,\n   {}_recurrent_weights,\n   {}, {}, ACTIVATION_{}, {}\n}};\n\n'
+            .format(name, name, name, name, weights[0].shape[0], weights[0].shape[1]//3, activation, reset_after))
+    hf.write('#define {}_SIZE {}\n'.format(name.upper(), weights[0].shape[1]//3))
+    hf.write('extern const GRULayer {};\n\n'.format(name));
+    True
+CuDNNGRU.dump_layer = dump_gru_layer
+GRU.dump_layer = dump_gru_layer
+
+def dump_dense_layer(self, f, hf):
+    name = self.name
+    print("printing layer " + name + " of type " + self.__class__.__name__)
+    weights = self.get_weights()
+    printVector(f, weights[0], name + '_weights')
+    printVector(f, weights[-1], name + '_bias')
+    #activation = re.search('function (.*) at', str(layer.activation)).group(1).upper()
+    if hasattr(self, 'activation'):
+        activation = self.activation.__name__.upper()
+    else:
+        activation = 'TANH'
+    f.write('const DenseLayer {} = {{\n   {}_bias,\n   {}_weights,\n   {}, {}, ACTIVATION_{}\n}};\n\n'
+            .format(name, name, name, weights[0].shape[0], weights[0].shape[1], activation))
+    hf.write('#define {}_SIZE {}\n'.format(name.upper(), weights[0].shape[1]))
+    hf.write('extern const DenseLayer {};\n\n'.format(name));
+    False
+Dense.dump_layer = dump_dense_layer
+
+def dump_mdense_layer(self, f, hf):
+    name = self.name
+    print("printing layer " + name + " of type " + self.__class__.__name__)
+    weights = self.get_weights()
+    printVector(f, weights[0], name + '_weights')
+    printVector(f, weights[1], name + '_bias')
+    printVector(f, weights[1], name + '_factor')
+    #activation = re.search('function (.*) at', str(layer.activation)).group(1).upper()
+    if hasattr(self, 'activation'):
+        activation = self.activation.__name__.upper()
+    else:
+        activation = 'TANH'
+    f.write('const MDenseLayer {} = {{\n   {}_bias,\n   {}_weights,\n   {}_factor,\n   {}, {}, ACTIVATION_{}\n}};\n\n'
+            .format(name, name, name, name, weights[0].shape[0], weights[0].shape[1], activation))
+    hf.write('#define {}_SIZE {}\n'.format(name.upper(), weights[0].shape[0]))
+    hf.write('extern const MDenseLayer {};\n\n'.format(name));
+    False
+MDense.dump_layer = dump_mdense_layer
+
+
+model, _, _ = lpcnet.new_lpcnet_model(rnn_units1=640)
+model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['sparse_categorical_accuracy'])
+#model.summary()
+
+model.load_weights(sys.argv[1])
+
+f = open(sys.argv[2], 'w')
+hf = open(sys.argv[3], 'w')
+
+
+
+f.write('/*This file is automatically generated from a Keras model*/\n\n')
+f.write('#ifdef HAVE_CONFIG_H\n#include "config.h"\n#endif\n\n#include "nnet.h"\n#include "foo.h"\n\n')
+
+hf.write('/*This file is automatically generated from a Keras model*/\n\n')
+hf.write('#ifndef RNN_DATA_H\n#define RNN_DATA_H\n\n#include "{}"\n\n'.format(sys.argv[3]))
+
+layer_list = []
+for i, layer in enumerate(model.layers):
+    if layer.dump_layer(f, hf):
+        layer_list.append(layer.name)
+
+hf.write('struct RNNState {\n')
+for i, name in enumerate(layer_list):
+    hf.write('  float {}_state[{}_SIZE];\n'.format(name, name.upper())) 
+hf.write('};\n')
+
+hf.write('\n\n#endif\n')
+
+f.close()
+hf.close()
--- /dev/null
+++ b/dnn/nnet.c
@@ -1,0 +1,213 @@
+/* Copyright (c) 2018 Mozilla
+                 2008-2011 Octasic Inc.
+                 2012-2017 Jean-Marc Valin */
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include "opus_types.h"
+#include "arch.h"
+#include "common.h"
+#include "tansig_table.h"
+#include "nnet.h"
+
+static OPUS_INLINE float tansig_approx(float x)
+{
+    int i;
+    float y, dy;
+    float sign=1;
+    /* Tests are reversed to catch NaNs */
+    if (!(x<8))
+        return 1;
+    if (!(x>-8))
+        return -1;
+#ifndef FIXED_POINT
+    /* Another check in case of -ffast-math */
+    if (celt_isnan(x))
+       return 0;
+#endif
+    if (x<0)
+    {
+       x=-x;
+       sign=-1;
+    }
+    i = (int)floor(.5f+25*x);
+    x -= .04f*i;
+    y = tansig_table[i];
+    dy = 1-y*y;
+    y = y + x*dy*(1 - y*x);
+    return sign*y;
+}
+
+static OPUS_INLINE float sigmoid_approx(float x)
+{
+   return .5f + .5f*tansig_approx(.5f*x);
+}
+
+static OPUS_INLINE float relu(float x)
+{
+   return x < 0 ? 0 : x;
+}
+
+static void gemm_accum(float *out, const float *weights, int rows, int cols, int col_stride, const float *x)
+{
+   int i, j;
+   for (i=0;i<rows;i++)
+   {
+      for (j=0;j<cols;j++)
+         out[i] += weights[j*col_stride + i]*x[j];
+   }
+}
+
+void compute_activation(float *output, float *input, int N, int activation)
+{
+   int i;
+   if (activation == ACTIVATION_SIGMOID) {
+      for (i=0;i<N;i++)
+         output[i] = sigmoid_approx(input[i]);
+   } else if (activation == ACTIVATION_TANH) {
+      for (i=0;i<N;i++)
+         output[i] = tansig_approx(input[i]);
+   } else if (activation == ACTIVATION_RELU) {
+      for (i=0;i<N;i++)
+         output[i] = relu(input[i]);
+   } else if (activation == ACTIVATION_SOFTMAX) {
+      float sum = 0;
+      for (i=0;i<N;i++) {
+         output[i] = exp(input[i]);
+         sum += output[i];
+      }
+      sum = 1.f/(sum+1e-30);
+      for (i=0;i<N;i++)
+         output[i] = sum*output[i];
+   } else {
+      celt_assert(layer->activation == ACTIVATION_LINEAR);
+      for (i=0;i<N;i++)
+         output[i] = input[i];
+   }
+}
+
+void compute_dense(const DenseLayer *layer, float *output, const float *input)
+{
+   int i;
+   int N, M;
+   int stride;
+   celt_assert(layer->nb_neurons <= MAX_NEURONS);
+   M = layer->nb_inputs;
+   N = layer->nb_neurons;
+   stride = N;
+   for (i=0;i<N;i++)
+      output[i] = layer->bias[i];
+   gemm_accum(output, layer->input_weights, N, M, stride, input);
+   compute_activation(output, output, N, layer->activation);
+}
+
+void compute_gru(const GRULayer *gru, float *state, const float *input)
+{
+   int i;
+   int N, M;
+   int stride;
+   float tmp[MAX_NEURONS];
+   float z[MAX_NEURONS];
+   float r[MAX_NEURONS];
+   float h[MAX_NEURONS];
+   celt_assert(gru->nb_neurons <= MAX_NEURONS);
+   M = gru->nb_inputs;
+   N = gru->nb_neurons;
+   stride = 3*N;
+   /* Compute update gate. */
+   for (i=0;i<N;i++)
+      z[i] = gru->bias[i];
+   gemm_accum(z, gru->input_weights, N, M, stride, input);
+   gemm_accum(z, gru->recurrent_weights, N, N, stride, state);
+   for (i=0;i<N;i++)
+      z[i] = sigmoid_approx(z[i]);
+
+   /* Compute reset gate. */
+   for (i=0;i<N;i++)
+      r[i] = gru->bias[N + i];
+   gemm_accum(r, &gru->input_weights[N], N, M, stride, input);
+   gemm_accum(r, &gru->recurrent_weights[N], N, N, stride, state);
+   for (i=0;i<N;i++)
+      r[i] = sigmoid_approx(r[i]);
+
+   /* Compute output. */
+   for (i=0;i<N;i++)
+      h[i] = gru->bias[2*N + i];
+   if (gru->reset_after)
+   {
+      /* WARNING: The reset_after version was never tested. */
+      RNN_CLEAR(tmp, N);
+      gemm_accum(tmp, &gru->recurrent_weights[2*N], N, N, stride, state);
+      for (i=0;i<N;i++)
+         h[i] += tmp[i] * r[i];
+      gemm_accum(h, &gru->input_weights[2*N], N, M, stride, input);
+   } else {
+      for (i=0;i<N;i++)
+         tmp[i] = state[i] * r[i];
+      gemm_accum(h, &gru->input_weights[2*N], N, M, stride, input);
+      gemm_accum(h, &gru->recurrent_weights[2*N], N, N, stride, tmp);
+   }
+   for (i=0;i<N;i++)
+      h[i] = z[i]*state[i] + (1-z[i])*tansig_approx(h[i]);
+   for (i=0;i<N;i++)
+      state[i] = h[i];
+}
+
+void compute_conv1d(const Conv1DLayer *layer, float *output, float *mem, const float *input)
+{
+   int i;
+   int N, M;
+   int stride;
+   float tmp[MAX_CONV_INPUTS];
+   celt_assert(layer->nb_neurons <= MAX_NEURONS);
+   celt_assert(layer->nb_inputs*layer->kernel_size <= MAX_CONV_INPUTS);
+   M = layer->nb_inputs;
+   N = layer->nb_neurons;
+   RNN_COPY(tmp, mem, layer->nb_inputs*(layer->kernel_size-1));
+   RNN_COPY(tmp, input, layer->nb_inputs);
+   M = layer->nb_inputs;
+   N = layer->nb_neurons;
+   stride = N;
+   for (i=0;i<N;i++)
+      output[i] = layer->bias[i];
+   gemm_accum(output, layer->input_weights, N, M, stride, input);
+   compute_activation(output, output, N, layer->activation);
+   RNN_COPY(mem, &tmp[layer->nb_inputs], layer->nb_inputs*(layer->kernel_size-1));
+}
+
+void compute_embedding(const EmbeddingLayer *layer, float *output, int input)
+{
+   int i;
+   for (i=0;i<layer->dim;i++)
+   {
+      output[i] = layer->embedding_weights[input*layer->dim + i];
+   }    
+}
+
--- /dev/null
+++ b/dnn/nnet.h
@@ -1,0 +1,94 @@
+/* Copyright (c) 2018 Mozilla
+   Copyright (c) 2017 Jean-Marc Valin */
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _NNET_H_
+#define _NNET_H_
+
+#define MAX_NEURONS 1024
+#define MAX_CONV_INPUTS 1024
+
+#define ACTIVATION_LINEAR  0
+#define ACTIVATION_SIGMOID 1
+#define ACTIVATION_TANH    2
+#define ACTIVATION_RELU    3
+#define ACTIVATION_SOFTMAX 4
+
+typedef struct {
+  const float *bias;
+  const float *input_weights;
+  int nb_inputs;
+  int nb_neurons;
+  int activation;
+} DenseLayer;
+
+typedef struct {
+  const float *bias;
+  const float *input_weights;
+  const float *factor;
+  int nb_inputs;
+  int nb_neurons;
+  int nb_channels;
+  int activation;
+} MDenseLayer;
+
+typedef struct {
+  const float *bias;
+  const float *input_weights;
+  const float *recurrent_weights;
+  int nb_inputs;
+  int nb_neurons;
+  int activation;
+  int reset_after;
+} GRULayer;
+
+typedef struct {
+  const float *bias;
+  const float *input_weights;
+  int nb_inputs;
+  int kernel_size;
+  int nb_neurons;
+  int activation;
+} Conv1DLayer;
+
+typedef struct {
+  const float *embedding_weights;
+  int nb_inputs;
+  int dim;
+} EmbeddingLayer;
+
+void compute_activation(float *output, float *input, int N, int activation);
+
+void compute_dense(const DenseLayer *layer, float *output, const float *input);
+
+void compute_gru(const GRULayer *gru, float *state, const float *input);
+
+void compute_conv1d(const Conv1DLayer *layer, float *output, float *mem, const float *input);
+
+void compute_embedding(const EmbeddingLayer *layer, float *output, int input);
+
+
+#endif /* _MLP_H_ */
--- /dev/null
+++ b/dnn/tansig_table.h
@@ -1,0 +1,45 @@
+/* This file is auto-generated by gen_tables */
+
+static const float tansig_table[201] = {
+0.000000f, 0.039979f, 0.079830f, 0.119427f, 0.158649f,
+0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f,
+0.379949f, 0.413644f, 0.446244f, 0.477700f, 0.507977f,
+0.537050f, 0.564900f, 0.591519f, 0.616909f, 0.641077f,
+0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f,
+0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.821040f,
+0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f,
+0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.915420f,
+0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f,
+0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.961090f,
+0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f,
+0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f,
+0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f,
+0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.992020f,
+0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f,
+0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f,
+0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.997590f,
+0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f,
+0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f,
+0.999000f, 0.999076f, 0.999147f, 0.999213f, 0.999273f,
+0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f,
+0.999550f, 0.999585f, 0.999617f, 0.999646f, 0.999673f,
+0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f,
+0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f,
+0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f,
+0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f,
+0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f,
+0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.999970f,
+0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.999980f,
+0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f,
+0.999988f, 0.999989f, 0.999990f, 0.999990f, 0.999991f,
+0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f,
+0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f,
+0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f,
+0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f,
+0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f,
+0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f,
+0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f,
+1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f,
+1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f,
+1.000000f,
+};
--