Skip to content

Commit 5e7af83

Browse files
GregorRjmvalin
authored andcommitted
Neural network model files
Extending the neural network dumper to dump to a simple text file format, and adding reader functions to read a neural network description from a FILE *.
1 parent f30741b commit 5e7af83

File tree

4 files changed

+200
-10
lines changed

4 files changed

+200
-10
lines changed

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ librnnoise_la_SOURCES = \
2222
src/denoise.c \
2323
src/rnn.c \
2424
src/rnn_data.c \
25+
src/rnn_reader.c \
2526
src/pitch.c \
2627
src/kiss_fft.c \
2728
src/celt_lpc.c

include/rnnoise.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
#ifndef RNNOISE_H
2929
#define RNNOISE_H 1
3030

31+
#include <stdio.h>
32+
33+
3134
#ifndef RNNOISE_EXPORT
3235
# if defined(WIN32)
3336
# if defined(RNNOISE_BUILD) && defined(DLL_EXPORT)
@@ -42,7 +45,6 @@
4245
# endif
4346
#endif
4447

45-
4648
typedef struct DenoiseState DenoiseState;
4749
typedef struct RNNModel RNNModel;
4850

@@ -56,4 +58,8 @@ RNNOISE_EXPORT void rnnoise_destroy(DenoiseState *st);
5658

5759
RNNOISE_EXPORT float rnnoise_process_frame(DenoiseState *st, float *out, const float *in);
5860

61+
RNNOISE_EXPORT RNNModel *rnnoise_model_from_file(FILE *f);
62+
63+
RNNOISE_EXPORT void rnnoise_model_free(RNNModel *model);
64+
5965
#endif

src/rnn_reader.c

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/* Copyright (c) 2018 Gregor Richards */
2+
/*
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions
5+
are met:
6+
7+
- Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
10+
- Redistributions in binary form must reproduce the above copyright
11+
notice, this list of conditions and the following disclaimer in the
12+
documentation and/or other materials provided with the distribution.
13+
14+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15+
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
18+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*/
26+
27+
#ifdef HAVE_CONFIG_H
28+
#include "config.h"
29+
#endif
30+
31+
#include <stdio.h>
32+
#include <stdlib.h>
33+
#include <sys/types.h>
34+
35+
#include "rnn.h"
36+
#include "rnn_data.h"
37+
#include "rnnoise.h"
38+
39+
/* Although these values are the same as in rnn.h, we make them separate to
40+
* avoid accidentally burning internal values into a file format */
41+
#define F_ACTIVATION_TANH 0
42+
#define F_ACTIVATION_SIGMOID 1
43+
#define F_ACTIVATION_RELU 2
44+
45+
RNNModel *rnnoise_model_from_file(FILE *f)
46+
{
47+
int i, in;
48+
49+
if (fscanf(f, "rnnoise-nu model file version %d\n", &in) != 1 || in != 1)
50+
return NULL;
51+
52+
RNNModel *ret = calloc(1, sizeof(RNNModel));
53+
if (!ret)
54+
return NULL;
55+
56+
#define ALLOC_LAYER(type, name) \
57+
type *name; \
58+
name = calloc(1, sizeof(type)); \
59+
if (!name) { \
60+
rnnoise_model_free(ret); \
61+
return NULL; \
62+
} \
63+
ret->name = name
64+
65+
ALLOC_LAYER(DenseLayer, input_dense);
66+
ALLOC_LAYER(GRULayer, vad_gru);
67+
ALLOC_LAYER(GRULayer, noise_gru);
68+
ALLOC_LAYER(GRULayer, denoise_gru);
69+
ALLOC_LAYER(DenseLayer, denoise_output);
70+
ALLOC_LAYER(DenseLayer, vad_output);
71+
72+
#define INPUT_VAL(name) do { \
73+
if (fscanf(f, "%d", &in) != 1 || in < 0 || in > 128) { \
74+
rnnoise_model_free(ret); \
75+
return NULL; \
76+
} \
77+
name = in; \
78+
} while (0)
79+
80+
#define INPUT_ACTIVATION(name) do { \
81+
int activation; \
82+
INPUT_VAL(activation); \
83+
switch (activation) { \
84+
case F_ACTIVATION_SIGMOID: \
85+
name = ACTIVATION_SIGMOID; \
86+
break; \
87+
case F_ACTIVATION_RELU: \
88+
name = ACTIVATION_RELU; \
89+
break; \
90+
default: \
91+
name = ACTIVATION_TANH; \
92+
} \
93+
} while (0)
94+
95+
#define INPUT_ARRAY(name, len) do { \
96+
rnn_weight *values = malloc((len) * sizeof(rnn_weight)); \
97+
if (!values) { \
98+
rnnoise_model_free(ret); \
99+
return NULL; \
100+
} \
101+
name = values; \
102+
for (i = 0; i < (len); i++) { \
103+
if (fscanf(f, "%d", &in) != 1) { \
104+
rnnoise_model_free(ret); \
105+
return NULL; \
106+
} \
107+
values[i] = in; \
108+
} \
109+
} while (0)
110+
111+
#define INPUT_DENSE(name) do { \
112+
INPUT_VAL(name->nb_inputs); \
113+
INPUT_VAL(name->nb_neurons); \
114+
ret->name ## _size = name->nb_neurons; \
115+
INPUT_ACTIVATION(name->activation); \
116+
INPUT_ARRAY(name->input_weights, name->nb_inputs * name->nb_neurons); \
117+
INPUT_ARRAY(name->bias, name->nb_neurons); \
118+
} while (0)
119+
120+
#define INPUT_GRU(name) do { \
121+
INPUT_VAL(name->nb_inputs); \
122+
INPUT_VAL(name->nb_neurons); \
123+
ret->name ## _size = name->nb_neurons; \
124+
INPUT_ACTIVATION(name->activation); \
125+
INPUT_ARRAY(name->input_weights, name->nb_inputs * name->nb_neurons * 3); \
126+
INPUT_ARRAY(name->recurrent_weights, name->nb_neurons * name->nb_neurons * 3); \
127+
INPUT_ARRAY(name->bias, name->nb_neurons * 3); \
128+
} while (0)
129+
130+
INPUT_DENSE(input_dense);
131+
INPUT_GRU(vad_gru);
132+
INPUT_GRU(noise_gru);
133+
INPUT_GRU(denoise_gru);
134+
INPUT_DENSE(denoise_output);
135+
INPUT_DENSE(vad_output);
136+
137+
return ret;
138+
}
139+
140+
void rnnoise_model_free(RNNModel *model)
141+
{
142+
#define FREE_MAYBE(ptr) do { if (ptr) free(ptr); } while (0)
143+
#define FREE_DENSE(name) do { \
144+
if (model->name) { \
145+
free((void *) model->name->input_weights); \
146+
free((void *) model->name->bias); \
147+
free((void *) model->name); \
148+
} \
149+
} while (0)
150+
#define FREE_GRU(name) do { \
151+
if (model->name) { \
152+
free((void *) model->name->input_weights); \
153+
free((void *) model->name->recurrent_weights); \
154+
free((void *) model->name->bias); \
155+
free((void *) model->name); \
156+
} \
157+
} while (0)
158+
159+
if (!model)
160+
return;
161+
FREE_DENSE(input_dense);
162+
FREE_GRU(vad_gru);
163+
FREE_GRU(noise_gru);
164+
FREE_GRU(denoise_gru);
165+
FREE_DENSE(denoise_output);
166+
FREE_DENSE(vad_output);
167+
free(model);
168+
}

training/dump_rnn.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,45 @@
1212
import re
1313
import numpy as np
1414

15-
def printVector(f, vector, name):
15+
def printVector(f, ft, vector, name):
1616
v = np.reshape(vector, (-1));
1717
#print('static const float ', name, '[', len(v), '] = \n', file=f)
1818
f.write('static const rnn_weight {}[{}] = {{\n '.format(name, len(v)))
1919
for i in range(0, len(v)):
2020
f.write('{}'.format(min(127, int(round(256*v[i])))))
21+
ft.write('{}'.format(min(127, int(round(256*v[i])))))
2122
if (i!=len(v)-1):
2223
f.write(',')
2324
else:
2425
break;
26+
ft.write(" ")
2527
if (i%8==7):
2628
f.write("\n ")
2729
else:
2830
f.write(" ")
2931
#print(v, file=f)
3032
f.write('\n};\n\n')
33+
ft.write("\n")
3134
return;
3235

33-
def printLayer(f, layer):
36+
def printLayer(f, ft, layer):
3437
weights = layer.get_weights()
35-
printVector(f, weights[0], layer.name + '_weights')
38+
activation = re.search('function (.*) at', str(layer.activation)).group(1).upper()
3639
if len(weights) > 2:
37-
printVector(f, weights[1], layer.name + '_recurrent_weights')
38-
printVector(f, weights[-1], layer.name + '_bias')
40+
ft.write('{} {} '.format(weights[0].shape[0], weights[0].shape[1]/3))
41+
else:
42+
ft.write('{} {} '.format(weights[0].shape[0], weights[0].shape[1]))
43+
if activation == 'SIGMOID':
44+
ft.write('1\n')
45+
elif activation == 'RELU':
46+
ft.write('2\n')
47+
else:
48+
ft.write('0\n')
49+
printVector(f, ft, weights[0], layer.name + '_weights')
50+
if len(weights) > 2:
51+
printVector(f, ft, weights[1], layer.name + '_recurrent_weights')
52+
printVector(f, ft, weights[-1], layer.name + '_bias')
3953
name = layer.name
40-
activation = re.search('function (.*) at', str(layer.activation)).group(1).upper()
4154
if len(weights) > 2:
4255
f.write('static const GRULayer {} = {{\n {}_bias,\n {}_weights,\n {}_recurrent_weights,\n {}, {}, ACTIVATION_{}\n}};\n\n'
4356
.format(name, name, name, name, weights[0].shape[0], weights[0].shape[1]/3, activation))
@@ -67,18 +80,20 @@ def mean_squared_sqrt_error(y_true, y_pred):
6780
weights = model.get_weights()
6881

6982
f = open(sys.argv[2], 'w')
83+
ft = open(sys.argv[3], 'w')
7084

7185
f.write('/*This file is automatically generated from a Keras model*/\n\n')
72-
f.write('#ifdef HAVE_CONFIG_H\n#include "config.h"\n#endif\n\n#include "rnn.h"\n\n')
86+
f.write('#ifdef HAVE_CONFIG_H\n#include "config.h"\n#endif\n\n#include "rnn.h"\n#include "rnn_data.h"\n\n')
87+
ft.write('rnnoise-nu model file version 1\n')
7388

7489
layer_list = []
7590
for i, layer in enumerate(model.layers):
7691
if len(layer.get_weights()) > 0:
77-
printLayer(f, layer)
92+
printLayer(f, ft, layer)
7893
if len(layer.get_weights()) > 2:
7994
layer_list.append(layer.name)
8095

81-
f.write('const struct RNNModel rnnoise_model_{} = {{\n'.format(sys.argv[3]))
96+
f.write('const struct RNNModel rnnoise_model_{} = {{\n'.format(sys.argv[4]))
8297
for i, layer in enumerate(model.layers):
8398
if len(layer.get_weights()) > 0:
8499
structLayer(f, layer)

0 commit comments

Comments
 (0)