1+ {
2+ "nbformat" : 4 ,
3+ "nbformat_minor" : 0 ,
4+ "metadata" : {
5+ "colab" : {
6+ "name" : " FruitToEmoji-GIT.ipynb" ,
7+ "provenance" : [],
8+ "collapsed_sections" : [],
9+ "toc_visible" : true
10+ },
11+ "kernelspec" : {
12+ "name" : " python3" ,
13+ "display_name" : " Python 3"
14+ }
15+ },
16+ "cells" : [
17+ {
18+ "cell_type" : " markdown" ,
19+ "metadata" : {
20+ "id" : " f92-4Hjy7kA8" ,
21+ "colab_type" : " text"
22+ },
23+ "source" : [
24+ " <a href=\" https://www.arduino.cc/\" ><img src=\" https://raw.githubusercontent.com/sandeepmistry/aimldevfest-workshop-2019/master/images/Arduino_logo_R_highquality.png\" width=200/></a>\n " ,
25+ " # Tiny ML on Arduino\n " ,
26+ " ## Classify objects by color tutorial\n " ,
27+ " \n " ,
28+ " \n " ,
29+ " https://github.com/arduino/ArduinoTensorFlowLiteTutorials/"
30+ ]
31+ },
32+ {
33+ "cell_type" : " markdown" ,
34+ "metadata" : {
35+ "id" : " uvDA8AK7QOq-" ,
36+ "colab_type" : " text"
37+ },
38+ "source" : [
39+ " ## Setup Python Environment \n " ,
40+ " \n " ,
41+ " The next cell sets up the dependencies in required for the notebook, run it."
42+ ]
43+ },
44+ {
45+ "cell_type" : " code" ,
46+ "metadata" : {
47+ "id" : " Y2gs-PL4xDkZ" ,
48+ "colab_type" : " code" ,
49+ "colab" : {}
50+ },
51+ "source" : [
52+ " # Setup environment\n " ,
53+ " !apt-get -qq install xxd\n " ,
54+ " !pip install pandas numpy matplotlib\n " ,
55+ " %tensorflow_version 2.x\n " ,
56+ " !pip install tensorflow"
57+ ],
58+ "execution_count" : 0 ,
59+ "outputs" : []
60+ },
61+ {
62+ "cell_type" : " markdown" ,
63+ "metadata" : {
64+ "id" : " 9lwkeshJk7dg" ,
65+ "colab_type" : " text"
66+ },
67+ "source" : [
68+ " # Upload Data\n " ,
69+ " \n " ,
70+ " 1. Open the panel on the left side of Colab by clicking on the __>__\n " ,
71+ " 1. Select the Files tab\n " ,
72+ " 1. Drag `csv` files from your computer to the tab to upload them into colab."
73+ ]
74+ },
75+ {
76+ "cell_type" : " markdown" ,
77+ "metadata" : {
78+ "id" : " kSxUeYPNQbOg" ,
79+ "colab_type" : " text"
80+ },
81+ "source" : [
82+ " # Train Neural Network\n " ,
83+ " \n " ,
84+ " \n " ,
85+ " \n "
86+ ]
87+ },
88+ {
89+ "cell_type" : " markdown" ,
90+ "metadata" : {
91+ "id" : " Gxk414PU3oy3" ,
92+ "colab_type" : " text"
93+ },
94+ "source" : [
95+ " ## Parse and prepare the data\n " ,
96+ " \n " ,
97+ " The next cell parses the csv files and transforms them to a format that will be used to train the full connected neural network.\n " ,
98+ " \n "
99+ ]
100+ },
101+ {
102+ "cell_type" : " code" ,
103+ "metadata" : {
104+ "id" : " AGChd1FAk5_j" ,
105+ "colab_type" : " code" ,
106+ "colab" : {}
107+ },
108+ "source" : [
109+ " import matplotlib.pyplot as plt\n " ,
110+ " import numpy as np\n " ,
111+ " import pandas as pd\n " ,
112+ " import tensorflow as tf\n " ,
113+ " import os\n " ,
114+ " import fileinput\n " ,
115+ " \n " ,
116+ " print(f\" TensorFlow version = {tf.__version__}\\ n\" )\n " ,
117+ " \n " ,
118+ " # Set a fixed random seed value, for reproducibility, this will allow us to get\n " ,
119+ " # the same random numbers each time the notebook is run\n " ,
120+ " SEED = 1337\n " ,
121+ " np.random.seed(SEED)\n " ,
122+ " tf.random.set_seed(SEED)\n " ,
123+ " \n " ,
124+ " CLASSES = [];\n " ,
125+ " \n " ,
126+ " for file in os.listdir(\" /content/\" ):\n " ,
127+ " if file.endswith(\" .csv\" ):\n " ,
128+ " CLASSES.append(os.path.splitext(file)[0])\n " ,
129+ " \n " ,
130+ " CLASSES.sort()\n " ,
131+ " \n " ,
132+ " SAMPLES_WINDOW_LEN = 1\n " ,
133+ " NUM_CLASSES = len(CLASSES)\n " ,
134+ " \n " ,
135+ " # create a one-hot encoded matrix that is used in the output\n " ,
136+ " ONE_HOT_ENCODED_CLASSES = np.eye(NUM_CLASSES)\n " ,
137+ " \n " ,
138+ " inputs = []\n " ,
139+ " outputs = []\n " ,
140+ " \n " ,
141+ " # read each csv file and push an input and output\n " ,
142+ " for class_index in range(NUM_CLASSES):\n " ,
143+ " objectClass = CLASSES[class_index]\n " ,
144+ " df = pd.read_csv(\" /content/\" + objectClass + \" .csv\" )\n " ,
145+ " columns = list(df)\n " ,
146+ " # get rid of pesky empty value lines of csv which cause NaN inputs to TensorFlow\n " ,
147+ " df = df.dropna()\n " ,
148+ " df = df.reset_index(drop=True)\n " ,
149+ " \n " ,
150+ " # calculate the number of objectClass recordings in the file\n " ,
151+ " num_recordings = int(df.shape[0] / SAMPLES_WINDOW_LEN)\n " ,
152+ " print(f\"\\ u001b[32;4m{objectClass}\\ u001b[0m class will be output \\ u001b[32m{class_index}\\ u001b[0m of the classifier\" )\n " ,
153+ " print(f\" {num_recordings} samples captured for training with inputs {list(df)} \\ n\" )\n " ,
154+ " \n " ,
155+ " # graphing\n " ,
156+ " plt.rcParams[\" figure.figsize\" ] = (10,1)\n " ,
157+ " pixels = np.array([df['Red'],df['Green'],df['Blue']],float)\n " ,
158+ " pixels = np.transpose(pixels)\n " ,
159+ " for i in range(num_recordings):\n " ,
160+ " plt.axvline(x=i, linewidth=8, color=tuple(pixels[i]/np.max(pixels[i], axis=0)))\n " ,
161+ " plt.show()\n " ,
162+ " \n " ,
163+ " #tensors\n " ,
164+ " output = ONE_HOT_ENCODED_CLASSES[class_index]\n " ,
165+ " for i in range(num_recordings):\n " ,
166+ " tensor = []\n " ,
167+ " row = []\n " ,
168+ " for c in columns:\n " ,
169+ " row.append(df[c][i])\n " ,
170+ " tensor += row\n " ,
171+ " inputs.append(tensor)\n " ,
172+ " outputs.append(output)\n " ,
173+ " \n " ,
174+ " # convert the list to numpy array\n " ,
175+ " inputs = np.array(inputs)\n " ,
176+ " outputs = np.array(outputs)\n " ,
177+ " \n " ,
178+ " print(\" Data set parsing and preparation complete.\" )\n " ,
179+ " \n " ,
180+ " # Randomize the order of the inputs, so they can be evenly distributed for training, testing, and validation\n " ,
181+ " # https://stackoverflow.com/a/37710486/2020087\n " ,
182+ " num_inputs = len(inputs)\n " ,
183+ " randomize = np.arange(num_inputs)\n " ,
184+ " np.random.shuffle(randomize)\n " ,
185+ " \n " ,
186+ " # Swap the consecutive indexes (0, 1, 2, etc) with the randomized indexes\n " ,
187+ " inputs = inputs[randomize]\n " ,
188+ " outputs = outputs[randomize]\n " ,
189+ " \n " ,
190+ " # Split the recordings (group of samples) into three sets: training, testing and validation\n " ,
191+ " TRAIN_SPLIT = int(0.6 * num_inputs)\n " ,
192+ " TEST_SPLIT = int(0.2 * num_inputs + TRAIN_SPLIT)\n " ,
193+ " \n " ,
194+ " inputs_train, inputs_test, inputs_validate = np.split(inputs, [TRAIN_SPLIT, TEST_SPLIT])\n " ,
195+ " outputs_train, outputs_test, outputs_validate = np.split(outputs, [TRAIN_SPLIT, TEST_SPLIT])\n " ,
196+ " \n " ,
197+ " print(\" Data set randomization and splitting complete.\" )\n "
198+ ],
199+ "execution_count" : 0 ,
200+ "outputs" : []
201+ },
202+ {
203+ "cell_type" : " markdown" ,
204+ "metadata" : {
205+ "colab_type" : " text" ,
206+ "id" : " v8qlSAX1b6Yv"
207+ },
208+ "source" : [
209+ " ## Build & Train the Model\n " ,
210+ " \n " ,
211+ " Build and train a [TensorFlow](https://www.tensorflow.org) model using the high-level [Keras](https://www.tensorflow.org/guide/keras) API."
212+ ]
213+ },
214+ {
215+ "cell_type" : " code" ,
216+ "metadata" : {
217+ "id" : " kGNFa-lX24Qo" ,
218+ "colab_type" : " code" ,
219+ "colab" : {}
220+ },
221+ "source" : [
222+ " # build the model and train it\n " ,
223+ " model = tf.keras.Sequential()\n " ,
224+ " model.add(tf.keras.layers.Dense(8, activation='relu')) # relu is used for performance\n " ,
225+ " model.add(tf.keras.layers.Dense(5, activation='relu'))\n " ,
226+ " model.add(tf.keras.layers.Dense(NUM_CLASSES, activation='softmax')) # softmax is used, because we only expect one class to occur per input\n " ,
227+ " model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])\n " ,
228+ " history = model.fit(inputs_train, outputs_train, epochs=400, batch_size=4, validation_data=(inputs_validate, outputs_validate))\n " ,
229+ " \n "
230+ ],
231+ "execution_count" : 0 ,
232+ "outputs" : []
233+ },
234+ {
235+ "cell_type" : " markdown" ,
236+ "metadata" : {
237+ "id" : " guMjtfa42ahM" ,
238+ "colab_type" : " text"
239+ },
240+ "source" : [
241+ " ### Run with Test Data\n " ,
242+ " Put our test data into the model and plot the predictions\n "
243+ ]
244+ },
245+ {
246+ "cell_type" : " code" ,
247+ "metadata" : {
248+ "id" : " V3Y0CCWJz2EK" ,
249+ "colab_type" : " code" ,
250+ "colab" : {}
251+ },
252+ "source" : [
253+ " # use the model to predict the test inputs\n " ,
254+ " predictions = model.predict(inputs_test)\n " ,
255+ " \n " ,
256+ " # print the predictions and the expected ouputs\n " ,
257+ " print(\" predictions =\\ n\" , np.round(predictions, decimals=3))\n " ,
258+ " print(\" actual =\\ n\" , outputs_test)\n " ,
259+ " \n " ,
260+ " # Plot the predictions along with to the test data\n " ,
261+ " plt.clf()\n " ,
262+ " plt.title('Training data predicted vs actual values')\n " ,
263+ " plt.plot(inputs_test, outputs_test, 'b.', label='Actual')\n " ,
264+ " plt.plot(inputs_test, predictions, 'r.', label='Predicted')\n " ,
265+ " plt.show()"
266+ ],
267+ "execution_count" : 0 ,
268+ "outputs" : []
269+ },
270+ {
271+ "cell_type" : " markdown" ,
272+ "metadata" : {
273+ "id" : " j7DO6xxXVCym" ,
274+ "colab_type" : " text"
275+ },
276+ "source" : [
277+ " # Convert the Trained Model to Tensor Flow Lite\n " ,
278+ " \n " ,
279+ " The next cell converts the model to TFlite format. The size in bytes of the model is also printed out."
280+ ]
281+ },
282+ {
283+ "cell_type" : " code" ,
284+ "metadata" : {
285+ "id" : " 0Xn1-Rn9Cp_8" ,
286+ "colab_type" : " code" ,
287+ "colab" : {}
288+ },
289+ "source" : [
290+ " # Convert the model to the TensorFlow Lite format without quantization\n " ,
291+ " converter = tf.lite.TFLiteConverter.from_keras_model(model)\n " ,
292+ " tflite_model = converter.convert()\n " ,
293+ " \n " ,
294+ " # Save the model to disk\n " ,
295+ " open(\" gesture_model.tflite\" , \" wb\" ).write(tflite_model)\n " ,
296+ " \n " ,
297+ " import os\n " ,
298+ " basic_model_size = os.path.getsize(\" gesture_model.tflite\" )\n " ,
299+ " print(\" Model is %d bytes\" % basic_model_size)\n " ,
300+ " \n " ,
301+ " "
302+ ],
303+ "execution_count" : 0 ,
304+ "outputs" : []
305+ },
306+ {
307+ "cell_type" : " markdown" ,
308+ "metadata" : {
309+ "id" : " ykccQn7SXrUX" ,
310+ "colab_type" : " text"
311+ },
312+ "source" : [
313+ " ## Encode the Model in an Arduino Header File \n " ,
314+ " \n " ,
315+ " The next cell creates a constant byte array that contains the TFlite model. Import it as a tab with the sketch below."
316+ ]
317+ },
318+ {
319+ "cell_type" : " code" ,
320+ "metadata" : {
321+ "id" : " 9J33uwpNtAku" ,
322+ "colab_type" : " code" ,
323+ "colab" : {}
324+ },
325+ "source" : [
326+ " !echo \" const unsigned char model[] = {\" > /content/model.h\n " ,
327+ " !cat gesture_model.tflite | xxd -i >> /content/model.h\n " ,
328+ " !echo \" };\" >> /content/model.h\n " ,
329+ " \n " ,
330+ " import os\n " ,
331+ " model_h_size = os.path.getsize(\" model.h\" )\n " ,
332+ " print(f\" Header file, model.h, is {model_h_size:,} bytes.\" )\n " ,
333+ " print(\"\\ nOpen the side panel (refresh if needed). Double click model.h to download the file.\" )"
334+ ],
335+ "execution_count" : 0 ,
336+ "outputs" : []
337+ },
338+ {
339+ "cell_type" : " markdown" ,
340+ "metadata" : {
341+ "id" : " 1eSkHZaLzMId" ,
342+ "colab_type" : " text"
343+ },
344+ "source" : [
345+ " # Realtime Classification of Sensor Data on Arduino\n " ,
346+ " \n " ,
347+ " Now it's time to switch back to the tutorial instructions and run our new model on the [Arduino Nano 33 BLE Sense](https://www.arduino.cc/en/Guide/NANO33BLE)"
348+ ]
349+ }
350+ ]
351+ }
0 commit comments