Skip to content

Getting Started on SML

Bruno Dilly edited this page Jul 20, 2015 · 19 revisions

Overview

If you're new to Soletta Machine Learning (SML), here are a few steps to create a simple software using its C API.

See Soletta Machine Learning for a introduction to the project and learn a bit about machine learning.

For instructions on how to use machine learning on flows written with Soletta, see How to add machine learning to flows

Building

Start by cloning Soletta Machine Learning git repository:

$ git clone [email protected]:solettaproject/soletta-machine-learning.git

Install the dependencies: fuzzylite, fann and soletta

fuzzylite is fuzzy logic control library written in C++:

FANN is a neural network library.

Soletta is a framework for making IoT devices. Some Soletta's data types are used by SML. Also SML provides a module for Soletta that depends on it.

CMake building system is used to build SML, so it's required to install it.

After dependencies are properly installed, it's time to build SML:

$ cd soletta-machine-learning/
$ mkdir build/
$ cd build/
$ cmake ..
$ make

Modeling the solution

Before start coding, it's a good idea to think about the problem and try to answer a few questions:

  • What is it trying to learn?
  • What would be the input and output variables?
  • What engine is better to this specific problem? See this section to learn about their differences.
  • (For fuzzy only) How can these variables change? Take on consideration the type of variables, ranges, how should they be separated on terms.

Coding

Let's consider a very simple scenario, a product composed by a presence sensor, a switch and a light bulb. So when the user gets in the her room presence sensor reads "on", the user press the switch turning the light on. When the user is leaving the room she disable the switch, turning the lights out, and the presence sensor reads "off".

The SML main flow consist in the following steps:

Choose an engine

Let's use fuzzy engine for demonstration purposes, but both engines should be do fine for such scenario.

Start creating a file hello_world.c including general purpose header sml.h and sml_fuzzy specific for the chosen engine. sml_ann.h would be included if neural network would be used.

Also, create the main() function. For now, just create a sml instance and delete it later. This instance will be used to add variables, receive values, and process this data making it possible to predict output.

#include <sml.h>
#include <sml_fuzzy.h>
#include <stdio.h>

int 
main(int argc, char **argv) {
    Sml *sml;

    sml = sml_fuzzy_new();

    // TODO: everything else

    sml_free(sml);

    return 0;
}

So after compiling it:

$ gcc hello_world.c -o hello_world `pkg-config --cflags --libs sml`

You should be able to run it:

$ ./hello_world

but it won't do anything useful yet.

Add variables

In this scenario, there are only two variables:

  • one input variable, the presence sensor, that may be 0 or 1
  • one output variable, the light state, that also can be 0 or 1 (representing on / off)

So let's declare these variables:

Sml_Variable *sensor, *light;

And add them to our example, after the sml instance creation and before its deletion - where was marked with TODO. The range (from 0 to 1), and terms must to be defined.

Terms are a way to split the values in the range in meaningful parts. Fuzzy supports some functions to describe them, as ramps, triangles and others. Since they're binary variables, using two ramps, one starting on 0 and other ending on 1, with a small overlap between them, seems good enough. This way, it will try to predict the output for any possible value being more certain about it on extremities.

To learn more about terms, see https://github.com/solettaproject/soletta/wiki/Soletta-Machine-Learning#terms.

We don't need to explicitly delete this variables or terms later, since it'll be done when sml_free() is called.

sensor = sml_new_input(sml, "PresenceSensor");
sml_variable_set_range(sml, sensor, 0, 1);
sml_fuzzy_variable_add_term_ramp(sml, sensor, "Off", 0, 0.6, 1);
sml_fuzzy_variable_add_term_ramp(sml, sensor, "On", 0.4, 1.0, 1);

light = sml_new_output(sml, "Light");
sml_variable_set_range(sml, light, 0, 1);
sml_fuzzy_variable_add_term_ramp(sml, light, "Off", 0, 0.6, 1);
sml_fuzzy_variable_add_term_ramp(sml, light, "On", 0.4, 1.0, 1);

Register read callback

It's required to set a read callback. This function will be called at each processing cycle to update variables values. Inside main(), after creating sml instance.

sml_set_read_callback(sml, read_cb, NULL);

Third argument is a pointer for any data that may be needed by this function.

So in this function the values of presence sensor and light must to be fetched. It could be done via GPIO, OIC, or any other way, depending in your product. It may be synchronous or asynchronous. But it's out of scope for this tutorial.

To keep it simple, yet illustrative, we're going to simulate the values in a function read_cb() Variable sensor_state represents presense sensor reading, switch_state represents light state. To simulate a daily based used, a day would be a cycle of 15 readings, user will be present on last 5 readings of each "day". When she leaves she remembers to turn lights off... most of the time. This is simulated by off_count . When she forgets lights on and leaves, machine learning should suggest to turn lights off.

After states are "fetch", they're set on sml variables (input and output).

static bool
read_cb(Sml *sml, void *data)
{
    Sml_Variables_List *inputs, *outputs;
    Sml_Variable *sensor, *light;

    static int sensor_state = 0;
    static int switch_state = 0;
    static int off_count = 0;
    static unsigned int count = 0;

    // user is present after 10 reads
    if (count == 10) {
        sensor_state = 1;
        switch_state = 1;
        count++;
    }

    // and stay there for 5 more reads
    else if (count == 15) {
        off_count++;
        sensor_state = 0;
        // most of times she remembers to swith off lights when she leaves
        if (off_count % 4 > 0)
            switch_state = 0;
        count = 0;
    }

    // ... forever on this cycle
    else {
        count++;
    }

    inputs = sml_get_input_list(sml);
    light = sml_variables_list_index(sml, inputs, 0);
    sml_variable_set_value(sml, light, switch_state);

    outputs = sml_get_output_list(sml);
    sensor = sml_variables_list_index(sml, outputs, 0);
    sml_variable_set_value(sml, sensor, sensor_state);

    return true;
}

Register change callback

Call process()

Everything is set up, it's time to trigger the processing. It will be done in a simple loop, making 100 process() calls, but more elaborated ways to call process() can be implemented, using mainloops.

for (int i = 0; i < 100; i++)
{
    sml_process(sml);
}

Final code

After all these steps, code should looks like this:

Output

Fine tuning

SML supports a lot of customization, even some options specific for each engine.

To learn more about it, check the project's online API

If you prefer, it's possible to build it online

$ cd machine-learning/build/ 
$ make doc

It depends on Doxygen

Code samples

There is a directory inside soletta-machine-learning repository with a few code samples:

$ cd machine-learning/examples/

List: