Skip to content

Commit cfd0a84

Browse files
committed
add predictClass + python scripts to generate nn
1 parent 680700d commit cfd0a84

File tree

7 files changed

+194
-14
lines changed

7 files changed

+194
-14
lines changed

examples/DigitsExample/DigitsExample.ino

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ void loop() {
2828
int y_test = 8;
2929

3030
uint32_t start = micros();
31+
3132
ml.predict(x_test, y_pred);
33+
3234
uint32_t timeit = micros() - start;
35+
3336
Serial.print("It took ");
3437
Serial.print(timeit);
3538
Serial.println(" micros to run inference");
@@ -43,18 +46,10 @@ void loop() {
4346
Serial.print(i == 9 ? '\n' : ',');
4447
}
4548

46-
uint8_t pred_class = 0;
47-
float max_proba = y_pred[0];
48-
49-
for (int i = 1; i < 10; i++) {
50-
if (y_pred[i] > max_proba) {
51-
pred_class = i;
52-
max_proba = y_pred[i];
53-
}
54-
}
55-
56-
Serial.print("Predicted output is: ");
57-
Serial.println(pred_class);
49+
Serial.print("Predicted class is: ");
50+
Serial.println(ml.probaToClass(y_pred));
51+
Serial.print("Sanity check: ");
52+
Serial.println(ml.predictClass(x_test));
5853

5954
delay(1000);
6055
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import math
2+
import numpy as np
3+
from sklearn.datasets import load_digits
4+
import tensorflow as tf
5+
from tensorflow.keras import layers
6+
from tinymlgen import port
7+
8+
9+
def get_data():
10+
np.random.seed(1337)
11+
x_values, y_values = load_digits(return_X_y=True)
12+
x_values /= x_values.max()
13+
# reshape to (8 x 8 x 1)
14+
x_values = x_values.reshape((len(x_values), 8, 8, 1))
15+
16+
# split into train, validation, test
17+
TRAIN_SPLIT = int(0.6 * len(x_values))
18+
TEST_SPLIT = int(0.2 * len(x_values) + TRAIN_SPLIT)
19+
x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT])
20+
y_train, y_test, y_validate = np.split(y_values, [TRAIN_SPLIT, TEST_SPLIT])
21+
22+
return x_train, x_test, x_validate, y_train, y_test, y_validate
23+
24+
def get_model():
25+
x_train, x_test, x_validate, y_train, y_test, y_validate = get_data()
26+
27+
# create a CNN
28+
model = tf.keras.Sequential()
29+
model.add(layers.Conv2D(8, (3, 3), activation='relu', input_shape=(8, 8, 1)))
30+
# model.add(layers.MaxPooling2D((2, 2)))
31+
# model.add(layers.Conv2D(64, (3, 3), activation='relu'))
32+
# model.add(layers.MaxPooling2D((2, 2)))
33+
# model.add(layers.Conv2D(64, (3, 3), activation='relu'))
34+
model.add(layers.Flatten())
35+
# model.add(layers.Dense(16, activation='relu'))
36+
model.add(layers.Dense(len(np.unique(y_train))))
37+
38+
model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=['accuracy'])
39+
model.fit(x_train, y_train, epochs=50, batch_size=16,
40+
validation_data=(x_validate, y_validate))
41+
return Exp
42+
43+
44+
def test_model(model, x_test, y_test):
45+
x_test = (x_test / x_test.max()).reshape((len(x_test), 8, 8, 1))
46+
y_pred = model.predict(x_test).argmax(axis=1)
47+
print('ACCURACY', (y_pred == y_test).sum() / len(y_test))
48+
exit()
49+
50+
51+
if __name__ == '__main__':
52+
model, x_test, y_test = get_model()
53+
test_model(model, x_test, y_test)
54+
c_code = port(model, variable_name='digits_model', pretty_print=True)
55+
print(c_code)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import math
2+
import numpy as np
3+
import tensorflow as tf
4+
from tensorflow.keras import layers
5+
from tinymlgen import port
6+
7+
8+
def get_model():
9+
SAMPLES = 1000
10+
np.random.seed(1337)
11+
x_values = np.random.uniform(low=0, high=2*math.pi, size=SAMPLES)
12+
# shuffle and add noise
13+
np.random.shuffle(x_values)
14+
y_values = np.vstack((np.sin(x_values), np.cos(x_values))).T
15+
y_values += 0.1 * np.random.randn(*y_values.shape)
16+
17+
# split into train, validation, test
18+
TRAIN_SPLIT = int(0.6 * SAMPLES)
19+
TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT)
20+
x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT])
21+
y_train, y_test, y_validate = np.split(y_values, [TRAIN_SPLIT, TEST_SPLIT])
22+
23+
# create a NN with 2 layers of 16 neurons
24+
model = tf.keras.Sequential()
25+
model.add(layers.Dense(16, activation='relu', input_shape=(1,)))
26+
model.add(layers.Dense(16, activation='relu'))
27+
model.add(layers.Dense(2))
28+
model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
29+
model.fit(x_train, y_train, epochs=100, batch_size=16,
30+
validation_data=(x_validate, y_validate))
31+
return model
32+
33+
34+
def test_model(model, verbose=False):
35+
x_test = np.random.uniform(low=0, high=2*math.pi, size=100)
36+
y_test = np.vstack((np.sin(x_test), np.cos(x_test))).T
37+
y_pred = model.predict(x_test)
38+
if verbose:
39+
for i in range(y_pred.shape[1]):
40+
print('MAE[%d] = %.3f' % (i, np.abs(y_pred[:, i] - y_test[:, i]).mean()))
41+
42+
43+
if __name__ == '__main__':
44+
model = get_model()
45+
test_model(model, verbose=True)
46+
c_code = port(model, pretty_print=True)
47+
print(c_code)

examples/SineExample/sine_model.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import math
2+
import numpy as np
3+
import tensorflow as tf
4+
from tensorflow.keras import layers
5+
from tinymlgen import port
6+
7+
8+
def get_model():
9+
SAMPLES = 1000
10+
np.random.seed(1337)
11+
x_values = np.random.uniform(low=0, high=2*math.pi, size=SAMPLES)
12+
# shuffle and add noise
13+
np.random.shuffle(x_values)
14+
y_values = np.sin(x_values)
15+
y_values += 0.1 * np.random.randn(*y_values.shape)
16+
17+
# split into train, validation, test
18+
TRAIN_SPLIT = int(0.6 * SAMPLES)
19+
TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT)
20+
x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT])
21+
y_train, y_test, y_validate = np.split(y_values, [TRAIN_SPLIT, TEST_SPLIT])
22+
23+
# create a NN with 2 layers of 16 neurons
24+
model = tf.keras.Sequential()
25+
model.add(layers.Dense(8, activation='relu', input_shape=(1,)))
26+
model.add(layers.Dense(16, activation='relu'))
27+
model.add(layers.Dense(1))
28+
model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
29+
model.fit(x_train, y_train, epochs=200, batch_size=16,
30+
validation_data=(x_validate, y_validate))
31+
return model
32+
33+
34+
def test_model(model, verbose=False):
35+
x_test = np.random.uniform(low=0, high=2*math.pi, size=100)
36+
y_test = np.sin(x_test)
37+
y_pred = model.predict(x_test)
38+
print('MAE', np.abs(y_pred - y_test).mean())
39+
40+
41+
if __name__ == '__main__':
42+
model = get_model()
43+
test_model(model, verbose=True)
44+
c_code = port(model, pretty_print=True)
45+
print(c_code)

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"type": "git",
77
"url": "https://github.com/eloquentarduino/EloquentTinyML"
88
},
9-
"version": "0.0.4",
9+
"version": "0.0.5",
1010
"authors": {
1111
"name": "Simone Salerno",
1212
"url": "https://github.com/eloquentarduino"

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=EloquentTinyML
2-
version=0.0.4
2+
version=0.0.5
33
author=Simone Salerno,eloquentarduino@gmail.com
44
maintainer=Simone Salerno,eloquentarduino@gmail.com
55
sentence=An eloquent interface to Tensorflow Lite for Microcontrollers

src/EloquentTinyML.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ namespace Eloquent {
8989
return !failed;
9090
}
9191

92+
/**
93+
*
94+
* @param input
95+
* @param output
96+
* @return
97+
*/
9298
uint8_t predict(uint8_t *input, uint8_t *output = NULL) {
9399
// abort if initialization failed
94100
if (!initialized())
@@ -141,6 +147,38 @@ namespace Eloquent {
141147
return this->output->data.f[0];
142148
}
143149

150+
/**
151+
* Predict class
152+
* @param input
153+
* @return
154+
*/
155+
uint8_t predictClass(float *input) {
156+
float output[outputSize];
157+
158+
predict(input, output);
159+
160+
return probaToClass(output);
161+
}
162+
163+
/**
164+
* Get class with highest probability
165+
* @param output
166+
* @return
167+
*/
168+
uint8_t probaToClass(float *output) {
169+
uint8_t classIdx = 0;
170+
float maxProba = output[0];
171+
172+
for (uint8_t i = 1; i < outputSize; i++) {
173+
if (output[i] > maxProba) {
174+
classIdx = i;
175+
maxProba = output[i];
176+
}
177+
}
178+
179+
return classIdx;
180+
}
181+
144182
protected:
145183
bool failed;
146184
uint8_t tensorArena[tensorArenaSize];

0 commit comments

Comments
 (0)