hamidkm9/sefr

1.0.1

Latest
uploaded 6 hours ago
Machine learning (TinyML) classifier that trains on the ESP32 itself, in one pass over the data. SEFR (arXiv:2006.04620) learns a linear multiclass model from streamed sensor samples, with an optional LDA refining pass. Header-only, no dynamic memory.

Readme

# SEFR for Arduino

SEFR is a machine learning (TinyML) classifier that trains on the microcontroller itself. It reads each training sample once, keeps only a running sum per class and the range of each feature, and can then classify new samples with one dot product per class. There is no learning rate, no iterations and no dynamic memory, so it runs on boards with 2 KB of RAM such as the Arduino Uno.

STMicroelectronics ships SEFR in [NanoEdge AI Studio](https://dev.st.com/edge-ai-docs/general/latest/en/docs/misc/nanoedge-ai-index.html), its AutoML tool for STM32, where it is recommended for multi-class classification with an ultra-low footprint.

## Install

In the Arduino IDE, open **Library Manager**, search for **SEFR** and click Install. With the command line:

```sh
arduino-cli lib install SEFR
```

With [PlatformIO](https://registry.platformio.org/libraries/hamid9/SEFR), add it to `platformio.ini`:

```ini
lib_deps = hamid9/SEFR
```

With [ESP-IDF](https://components.espressif.com/components/hamidkm9/sefr), add it from the ESP Component Registry and `#include "SEFR.h"`:

```sh
idf.py add-dependency "hamidkm9/sefr"
```

To start from the ESP-IDF example, which trains on the chip and prints accuracy and timings:

```sh
idf.py create-project-from-example "hamidkm9/sefr:train_on_device"
```

## Example

```cpp
#include <SEFR.h>

SEFR<3, 4> model;           // 3 features, 4 classes

void setup() {
  float sample[3] = {120, 80, 40};
  model.learn(sample, 2);   // add a sample of class 2; repeat for every sample
  model.train();            // compute the weights

  float reading[3] = {118, 82, 39};
  uint16_t label = model.predict(reading);
}
```

Training can continue: call `learn()` with more samples and `train()` again at any time.

For multiclass problems, an optional second pass usually improves accuracy a lot. After `train()`, call `beginRefine()`, pass every training sample to `refine()` once more, and call `endRefine()`. It fits linear discriminant analysis on the class scores and folds it into the same weights, so prediction stays exactly as fast.

## Examples

| Example | What it shows |
|---|---|
| `IrisTrainOnDevice` | Trains on the board from data stored in flash, then tests on held-out samples, with and without the refining pass. Fits on an Uno. |
| `SerialTrainer` | Teach the board new samples over the Serial Monitor, then ask it to classify. A starting point for training on sensor readings. |
| `PretrainedModel` | Loads weights trained elsewhere with `setModel()`. |

## API

| Method | Description |
|---|---|
| `SEFR<N_FEATURES, N_CLASSES> model;` | Creates a model. All memory is allocated statically. |
| `bool learn(const float *x, uint16_t label)` | Adds one sample. Labels go from 0 to `N_CLASSES - 1`. |
| `bool train()` | Computes weights from the samples so far. Needs samples of at least two classes. |
| `bool beginRefine()`, `bool refine(x, label)`, `bool endRefine()` | Optional second pass over the same samples. Needs samples of every class. |
| `uint16_t predict(const float *x)` | Returns the predicted class. |
| `uint16_t scores(const float *x, float *out)` | Writes one score per class and returns the best class. |
| `void setModel(const float *weights, const float *biases)` | Loads weights (`N_CLASSES` rows of `N_FEATURES`) and biases trained elsewhere. |
| `weights(c)`, `bias(c)`, `count(c)` | Read the weights, bias and number of samples of class `c`. |
| `reset()` | Forgets everything. |

## Memory

A model takes about `4 × (2·N_CLASSES·N_FEATURES + 2·N_FEATURES + 2·N_CLASSES² + 4·N_CLASSES)` bytes. For example, 4 features and 3 classes take about 250 bytes, and 64 features and 10 classes take about 6.6 KB.

## How it works

For each class, SEFR scales every feature to [0, 1], compares the average of the class with the average of all other classes, and sets the weight of each feature to their normalised difference. The threshold sits between the average scores of the two groups, weighted by their sizes. The paper describes the binary version, and this library uses one-vs-rest for more classes. The scaling is folded into the weights, so raw sensor values can be passed to `predict()`.

The library is tested against an independent NumPy implementation on the Iris, Wine, Breast Cancer and Digits datasets; see `extras/test`.

## Reference

Hamidreza Keshavarz, Mohammad Saniee Abadeh, Reza Rawassizadeh. *SEFR: A Fast Linear-Time Classifier for Ultra-Low Power Devices.* [arXiv:2006.04620](https://arxiv.org/abs/2006.04620), 2020.

## License

MIT

Links

Supports all targets

To add this component to your project, run:

idf.py add-dependency "hamidkm9/sefr^1.0.1"

download archive

Stats

  • Archive size
    Archive size ~ 9.64 KB
  • Downloaded in total
    Downloaded in total 0 times
  • Downloaded this version
    This version: 0 times

Badge

hamidkm9/sefr version: 1.0.1
|