There is no doubt that health is our main and most valuable resource, which requires constant and timely monitoring. There are many modern technologies that help in diagnosing all kinds of diseases, but often such diagnostic methods are quite expensive so patients turn to hospitals when the disease is already actively progressing.
And then I thought, what if we can create simple, but effective diagnostic gadgets using TinyML technologies? In this case, it would be possible to create simple and inexpensive devices that would serve the benefit of many people and help to prevent the most terrible diagnoses.
Diabetes Case
For my experiment, I chose one of the most common diseases of the 21st century, often caused by a sedentary lifestyle, poor diet, and bad habits – diabetes. It’s a chronic disease that causes a lot of complications. Statistics say that by 2040 the global diabetes pandemic will reach 642 million people, which means that one of every ten adults will suffer from this disease. Horrible figures indeed…Type 2 diabetes accounts for about 90% of cases of diabetes, with the remaining 10% mainly due to Type 1 diabetes mellitus and gestational diabetes. Obesity is considered the main cause of Type 2 diabetes in subjects who are genetically predisposed to the disease. The purpose of this tutorial is to visually demonstrate how you can create a simple device to recognize the probability of diabetes. Such a device can be easily made at home and used for regular monitoring of your health without frequent visits to the clinic. Moreover, if there is a high risk of developing diabetes, the patient will be able to seek medical advice in time.
DatasetTo create a machine learning model, I will use the free platform, Neuton TinyML. Also, I found an open-source dataset on Kaggle for model training. You can download it here: https://www.kaggle.com/datasets/mathchi/diabetes-data-set
This original dataset has been provided by the National Institute of Diabetes and Digestive and Kidney Diseases. The dataset consists of health data of female patients, aged 21 years old and older. The medical predictor variables include the number of pregnancies the patient has had, their BMI, insulin level, age, and so on.
Features of the dataset:The detailed description of all the features is as follows:
- Pregnancies: indicates the number of pregnancies
- Glucose: indicates the plasma glucose concentration
- Blood Pressure: indicates diastolic blood pressure in mm/Hg
- Skin Thickness: indicates triceps skinfold thickness in mm
- Insulin: indicates insulin in U/mL
- BMI: indicates the body mass index in kg/m2
- Diabetes Pedigree Function: indicates the function which scores the likelihood of diabetes based on family history
- Age: indicates the age of the person
- Outcome: indicates if the patient had a diabetes or not (1 = yes, 0 = no)
This is a binary classification task as we need to predict whether a patient has diabetes.
Step 1: Model CreationSo what do you need to create a TinyML model? Here’s the step-by-step process:
1. Create a new solution and upload a training dataset (the one from Kaggle or you can use your own). Selected a target variable, here 'Diabetes'.
2. Select the target metric as 'Accuracy' and enable the TinyML mode with FLOAT32 support.
Once your dataset is loaded and training has started, you can explore the data set to get a feel of a data specialist what it looks like and get some insights about it ;)
Exploratory Data Analysis:Let’s start by finding the correlation of every pair of features (and the outcome variable), and visualize the correlations using a heatmap.
In the above heatmap, darker colours indicate more correlation. As we can see from the heatmap, glucose levels, age, BMI and number of pregnancies all have a significant correlation with the outcome variable. Also notice the correlation between pairs of features, like age and pregnancies, or insulin and skin thickness.
Let’s also look at how many people in the dataset are diabetic and how many are not. Below is the barplot of the same:
It is also helpful to visualize relations between a single variable and the outcome. Below, we’ll see the relationship between age and outcome. You can similarly visualize other features. The figure is a plot of the mean age for each of the output classes. We can see that the mean age of people having diabetes is higher.
Once the training is complete we can check the metrics, in our case our accuracy was 0.768852. There are also other algorithms present with the Neuton platform which will list out multiple performance metrics, as you can see below, AUC performed the best with the highest accuracy.
We can also understand what happened with our model using the generated Feature Importance Matrix, which tells us which features were used and their greater effect on the outcome.
From the above figure, we can draw the following conclusions.
- Glucose level, BMI, diabetes pedigree function and age have a significant influence on the model, especially glucose level and BMI. It is good to see our machine learning model match what we have been hearing from doctors our entire lives!
Download the Neuton model for your tiny board.
Once you have downloaded it, we are ready to embed this on our device. For this project, we intended to use, Particle IoT boards. There are several reasons to use these cool boards:
- The board comes with different options for network connectivity (LTE, 2G/3G, Wifi, Ethernet, BLE)
- Equipped with the Nordic nRF52840 and Espressif ESP32 processors, they are really secure and powerful,
- Sadly, no TinyML projects are out on these boards yet.
Our idea is to use a mobile app to take all the features as input from the patient and send it to the Particle device for prediction instead of any cloud AI platform (Yeah! the cool thing about TinyML).
To build the app we have used MIT app inventor, it's a free, quick, easy, drag-and-drop no-code tool aimed at learning app development basics.
To set up a Firebase Database, follow this guide: https://community.appinventor.mit.edu/t/firebase-db-with-app-inventor-tutorial/43872
Visit this MIT App Inventor Link https://appinventor.mit.edu/ and create an account and a new project. After it is created, follow as shown below:
Our Homepage contains fields to input the features which we had used for our model training.
Here are the code blocks for our home page,
Our other page is the results page, where the user/patient would see the prediction results for her input data.
We have completed all the steps for building our app, now build a.apk file which is an installable file type for android phone
You can now install that on your Android Phone.
Particle Webhooks for Firebase:
Webhooks are user-defined HTTP callbacks. They are triggered by some event in a web application and can facilitate integrating different applications or third-party APIs. Particle has really got cool areas around Webhook Integration, follow as shown below:
- Create a new integration on the Particle IoT console (we have already created one for our project)
- For our first integration, GET, request type, add the webhook template as shown below,
- Our second integration, PUT, request type, add the template as shown below (this integration is used to store inference results received after Neuton TinyML operation on the Particle device for the app side)
- Lastly, you can test whether you made a webhook correctly or not using the test button on the integration info page
Let us jump to the codes, firstly, place your downloaded model C files into the Particle Workbench project directory,
C:.
│ project.properties
│ README.md
├───.vscode
│ launch.json
│ settings.json
├───lib
│ └───ArduinoJson
├───src
│ │ DiabetesHealthDevice.cpp
│ │ DiabetesHealthDevice.ino
│ │ neuton.c
│ │ neuton.h
│ │ README.md
│ ├───model
│ │ model.h
│ ├───preprocessing
│ │ └───blocks
│ │ └───normalize
│ │ normalize.c
│ │ normalize.h
│ └───target_values
│ binary_target_dict_csv.csv
└───target
There are two important functions which are used to handle webhooks and pass data to our neuton model:
// Forward declarations
void getDataHandler(const char *topic, const char *data);
void publishPredictionData(uint16_t index, unsigned long inference_time, float* outputs);
We first read the model input features from the Firebase DB (assuming the user has input the data and it is stored successfully) and then we feed those inputs to our Neuton inference function. The resulting output is sent to the webhook again to store in Firebase DB to show it to the user on the app.
void getDataHandler(const char *topic, const char *data)
{
StaticJsonDocument<256> doc;
payload = data;
payload.replace("\\""", "");
payload.replace("\"\"", "\"");
payload.trim();
snprintf(text, sizeof(text)-1, payload.c_str());
//strcpy(text, (const char*)payload);
// Serial.println(text);
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, text);
// Test if parsing succeeds.
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
return;
}
else
{
Serial.println(F("deserializeJson() succeeded."));
// Get the value of the element in the document, in exact same order we used while crating neuton model.
inputs[0] = atof(doc["preg"]);
inputs[1] = atof(doc["glucose"]);
inputs[2] = atof(doc["bp"]);
inputs[3] = atof(doc["skin"]);
inputs[4] = atof(doc["insulin"]);
inputs[5] = atof(doc["bmi"]);
inputs[6] = atof(doc["dpf"]);
inputs[7] = atof(doc["age"]);
if (neuton_model_set_inputs(inputs) == 0)
{
uint16_t index;
float* outputs;
start = millis();
if (neuton_model_run_inference(&index, &outputs) == 0)
{
stop = millis();
inference_time = stop - start;
Particle.publish("Inference time (ms): ", String(inference_time));
// code for handling prediction result
Serial.println("Prediction result:");
Serial.println(index);
Particle.publish("Prediction result: ", index==1?"diabetes yes":"diabetes no");
// publish the output to the firebase integration
publishPredictionData(index, inference_time, outputs);
}
}
}
}
void publishPredictionData(uint16_t index, unsigned long inference_time, float* outputs)
{
snprintf(text, sizeof(text), "{\"prediction\":%d,\"inferencetime\":%lu,\"confidence\":%.2f}", index, inference_time, outputs[index]);
Serial.printlnf("publishing %s", text);
Particle.publish(PUBLISH_EVENT_NAME, text, PRIVATE);
}
Compile and flash it to your device,
after flashing you could see the Published and Retrieved messages in the Particle Cloud Console Terminal and Device Logs,
If you notice your firebase real-time database, it should update accordingly.
We also tested the model upon the CSV dataset using our CSV upload utility (In my previous projects I have shown in detail how to use the CSV upload utility).
Here are the model characteristics,
Such tiny devices can make home-based prognosis solutions affordable and reliable with efficient, secure and global network connectivity. Particle and Neuton truly show what best can IoT devices leverage in 2022 and beyond!
Comments