Attention-deficit/ hyperactivity disorder (ADHD) is a neurodevelopmental condition that can make it difficult for people to focus and this the most common neurodevelopmental condition in children.
However, children affected by ADHD can be taught in a rather interactive way. This project is a simple example of this solution. Children usually start to learn colors when they are a toddler and color coding plays a significant role in enhancing memory performance.
Overview - Hardware and SoftwareM5Stack Core 2 ESP32 IoT Development Kit (I will call it M5Stack from here) and Neuton TinyML made this project possible. By the way, though Neuton is not an open-source tool, all functions are free and its models can run on almost any MCU.
I focused on preparing a proper dataset. I was inspired to apply this idea after taking a look at the MNIST dataset. I will explain why as you read further.
I drew the initial of the color on the M5Stack and the handwritten letter would be predicted by the TinyML model and the model's output will be used to display the respective colour - using the RGB LEDs. I have programmed this prototype for fifteen colours. One drawback is that the LEDs are unable to display any dark colours.
MNIST Dataset & Its relevance to the ProjectThe MNIST (Modified National Institute of Standards and Technology) dataset is the "hello world" dataset of computer vision, sourced from the MNIST database which is a large collection of handwritten digits.
Each sample is a 28x28 grayscale image of a hand-drawn digit between 0 and 9. There are 784 pixels in total and each pixel value indicates the lightness/darkness of the respective pixel. A higher pixel value means the pixel is dark and if it's low – the pixel is light. The pixel value has a range of 0-255 (inclusive).
The pixel location of a pixel is calculated using the following formula:
x = i * 28 + j
x is the pixel location. I and j are integers between 0 and 27 (inclusive). I denotes the pixel's row while j denotes the pixel's column and the indexing is by zero.
I decided to create my own dataset based on this idea and create a dataset using letters instead of digits.
Data Collection: Preparing the Training and Test datasetsI used my M5Stack to collect the data and prepare the training and test datasets. I decided to assign 75% of the data as the training dataset and the remaining 25% as the test dataset.
The target variable of my training dataset will be the 'Label' variable and it will contain the initial of the color. The letters and corresponding colors are as follows:
- A - Aquamarine
- B - Blue
- C - Cyan
- F - Fluorescent Blue
- G - Green
- I - Indigo
- L - Lime
- M - Magenta
- O - Orange
- P - Pink
- R - Red
- S - Sky Blue
- V - Violet
- W - White
- Y - Yellow
I collected 400 samples for each letter, 300 of which belonged to the training dataset and 100 of which belonged to the test dataset. There will be 255 feature variables that contain the pixel values of the respective pixels. I verified the approximate number of pixels that had significant pixel values as I drew my digits and decided to use 255 feature variables.
The screen resolution of the M5Stack is 320 x 240 pixels. To calculate the pixel location, I used the following formula:
x = i * 320 + j
The factor by which you multiply the pixel's row number is basically the width of your touchscreen. To store the pixel values, I tried using the list method. I declared a list and then tried to append the pixel values within a for loop (with 255 iterations) but it was not successful as I kept getting an error saying that the pixel location did not have an appropriate data type. To save time, I decided to use the buffer method instead where I allocated memory to store pixel values.
Each person will have a different way to write a letter, so I made sure to draw out each letter in all possible ways and collect sufficient samples for each way.
Neuton TinyML requires the datasets in a CSV format so I prepared my training and test datasets as CSV files. Your dataset, both training, and test, will have to meet some other requirements as well, but don't worry, you can always view them in the Support Library on the platform.
The code for data collection is available in the Code section below.
The next step is model training.
Training the ModelVisit the Neuton TinyML's web page (neuton.ai) and click on ‘Get Started’. Click on the ‘Start for Free’ button and you will be redirected to the welcome page where you can sign in using your Google account and get started. Set up your CGP account and you will receive free credits to upload your own data and train your models. Subscribe to Neuton's Zero Gravity Plan and you are good to go!
Click on 'Add New Solution' and you will see something like this:
Once you are done, click ‘Next’ and you will be required to upload your training dataset. The dataset will be validated and if it meets all requirements, it will show a green tick and allow you to continue. You should not have duplicate rows or any missing values.
Click ‘OK’ and proceed to the next step. Choose your target variable which is the 'Label' variable and if you want to eliminate any other variables, you can also do that.
The next step will require you to specify the task type, the metric, and TinyML model settings. The platform can identify the target metric and task type itself but I will explain why I used the Classification task type.
This model should be able to classify the given input as a letter and this is supervised machine learning as we are training the model with the target and feature variables.
This is classification since we are not predicting a continuous dependent variable using independent variables like predicting the yearly income using the number of hours worked per week. There are two types of classification - binary and multi. Binary classification will classify the input into one of the two classes. But in this project, we will be classifying the input into one of the nine classes so the task type is Multi Classification in this case.
The target metric is Accuracy and you will eventually know why the platform chose it after your model's training is complete. The target metric calculates the error rate of the model predictions on the validation dataset and represents the model quality.
If you want to create tiny models for microcontrollers, enable the TinyML mode using the slider and set the model settings.
The input data type is FLOAT32 and the normalization type is 'Unified scale for all features'. You will need to choose this normalization type if the data from your feature variables are within the same range and doing this will also reduce the time required for training. Enable float datatype support and select 8 bits as the bit depth for calculations. Once you are done, click 'Start training' and the training process will start.
You can view the quality of your model, its accuracy, and other analytics once your training is complete.
Training resultsMy model had an accuracy of 97.2% and a model quality index of 97%. I am satisfied with the results!
I enabled prediction to see how well my model performed. For this, I used my test dataset.
The results were better than expected and I felt quite confident about my TinyML model. I downloaded the C library and got ready to deploy it on my M5Stack.
Embedding the TinyML Model on M5StackCreate an Arduino sketch file to deploy your model. I used Neuton TinyML to create the model. After downloading the C library which is available for download after the training's complete, extract the zipped folder and copy the contents into the folder with your sketch file. Read the README text file within the downloaded content to learn how to embed your model.
According to the README file, the two main functions are:
- neuton_model_set_inputs - to set input values
- neuton_model_run_inference - to make predictions
You will need to make an array with model inputs. In my case, I have used a buffer as my input data type was not suitable for an array. Please make sure that the input count and order are the same as in the training dataset. Pass this to neuton_model_set_inputs
function. The function will return 0 when the buffer is full and this indicates that the model is ready for prediction.
You should call neuton_model_run_inference
function with two arguments when your buffer is ready. These two arguments are:
- pointer to
index
of predicted class - pointer to neural net
outputs
As you can see in the code below, 0
is returned by neuton_model_run_inference
function when the prediction is successful.
if (neuton_model_set_inputs(inputs) == 0)
{
uint16_t index;
float* outputs;
if (neuton_model_run_inference(&index, &outputs) == 0)
{
// code for handling prediction result
}
}
After a successful prediction, classification takes place and the inference results are mapped on your classes. Note that the inference results are encoded (0..n). Use dictionaries binary_target_dict_csv.csv / multi_target_dict_csv.csv
for the mapping process.
I have uploaded the complete source code in the Code section for your convenience.
Final LookConclusionI had a nice experience while working on this project. I hope you liked my tutorial and found it helpful. I also hope that children are more intrigued by this project and show more interest while learning the colors. I'm always open to suggestions so please feel free to share your feedback below.
Comments