aifes_team
Published © GPL3+

AIfES - Inference Tutorial

This tutorial shows the different ways how to perform an inference in AIfES®.

AdvancedFull instructions provided1,775
AIfES - Inference Tutorial

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
Arduino or an Arduino compatible board
×1

Software apps and online services

AIfES®
Arduino IDE
Arduino IDE

Story

Read more

Code

AIfES_Inference_tutorial.ino

Arduino
This is the sample code for the AIfES® Inference Tutorial compatible with the Arduino® IDE.
  /*
  www.aifes.ai
  https://github.com/Fraunhofer-IMS/AIfES_for_Arduino
  Copyright (C) 2020-2021 Fraunhofer Institute for Microelectronic Circuits and Systems. All rights reserved.

  AIfES is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <https://www.gnu.org/licenses/>.

  AIfES_Inference_tutorial
  --------------------

  Versions:
    1.0.0   Initial version
  
  The sketch shows an example of how the inference of an already trained network is performed. 
  In the concrete example, a neural network was trained to map an XOR gate. 
  The neural network was trained in Keras and the configuration including the weights was imported into AIfES. 
  The network structure is 2-3(Sigmoid)-1(Sigmoid) and Sigmoid is used as activation function.
  The calculation is done in float 32.
  
  XOR truth table
  Input    Output
  0   0    0
  0   1    1
  1   0    1
  1   1    0
  
  Tested on:
    Arduino UNO
    Arduino Nano 33 BLE Sense
   
*/
#include <aifes.h>

#define INPUTS  2
#define NEURONS 3
#define OUTPUTS 1

#define DATA_COUNT 4

void setup() {
  Serial.begin(115200); //115200 baud rate (If necessary, change in the serial monitor)
  while (!Serial);

  Serial.println(F("AIfES inference tutorial"));
  Serial.println(F("Type >inference< to start"));

}

void loop() {

  while(Serial.available() > 0 ){
    String str = Serial.readString();
    if(str.indexOf("inference") > -1){        //Keyword "inference"
      
      Serial.println(F("Result LayeredWeights method"));
      Inference_LayeredWeights();
      
      Serial.println(F(""));
      Serial.println(F("Result FlatWeights method"));
      Inference_FlatWeights();

      Serial.println(F(""));
      Serial.println(F("Result FlatWeights bytes method"));
      Inference_FlatWeights_bytes();
    }
    else{
      Serial.println(F("unknown"));
      Serial.println(F("Type >inference<"));
    }
  }

}

void Inference_LayeredWeights(){

    //Tensor for the input data
    //Input data for the XOR ANN
    //float input_data[] = {0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f};  // 1D array example
    float input_data[DATA_COUNT][INPUTS] = {
        {0.0f, 0.0f},
        {0.0f, 1.0f},
        {1.0f, 0.0f},
        {1.0f, 1.0f}
    };

    //Tensor for the input data
    uint16_t input_shape[] = {DATA_COUNT, INPUTS};        // Definition of the input shape
    aitensor_t input_tensor;                              // Creation of the input AIfES tensor
    input_tensor.dtype = aif32;                           // Definition of the used data type, here float with 32 bits, different ones are available
    input_tensor.dim = 2;                                 // Dimensions of the tensor, here 2 dimensions, as specified at input_shape
    input_tensor.shape = input_shape;                     // Set the shape of the input_tensor
    input_tensor.data = input_data;                       // Assign the input_data array to the tensor. It expects a pointer to the array where the data is stored

    //Tensor for the output data
    float output_data[DATA_COUNT * OUTPUTS];              // Output data
    uint16_t output_shape[] = {DATA_COUNT, OUTPUTS};      // Definition of the output shape
    aitensor_t output_tensor_1;                           // Creation of the output AIfES tensor
    output_tensor_1.dtype = aif32;                        // Definition of the used data type, here float with 32 bits, different ones are available
    output_tensor_1.dim = 2;                              // Dimensions of the tensor, here 2 dimensions, as specified at input_shape
    output_tensor_1.shape = output_shape;                 // Set the shape of the output_tensor
    output_tensor_1.data = output_data;                   // Assign the output_data array to the tensor. It expects a pointer to the array where the data is stored

    // ---------------------------------- Layer definition ---------------------------------------

    // Input layer
    uint16_t input_layer_shape[] = {1, INPUTS};         // Definition of the input layer shape (The 1 must remain here regardless of the number of data sets)
    ailayer_input_t input_layer;                        // Creation of the AIfES input layer
    input_layer.input_dim = 2;                          // Definition of the input dimension (Must fit to the input tensor)
    input_layer.input_shape = input_layer_shape;        // Handover of the input layer shape

    // Dense layer (hidden layer)
    const float weights_hidden_layer[] PROGMEM = {-10.1164f, -8.4212f, 5.4396f, 7.297f, -7.6482f, -9.0155f};  //Hidden layer weights
    const float bias_weights_hidden_layer[] PROGMEM = {-2.9653f,  2.3677f, -1.5968f};                         //Hidden layer bias weights
    
    ailayer_dense_t hidden_layer;                                                                             // Creation of the AIfES hidden dense layer
    hidden_layer.neurons = NEURONS;                                                                           // Number of neurons
    hidden_layer.weights.data = (void*)weights_hidden_layer;                                                  // Passing the hidden layer weights. The (void*) is only needed if you use PROGMEM
    hidden_layer.bias.data = (void*)bias_weights_hidden_layer;                                                // Passing the hidden layer bias weights. The (void*) is only needed if you use PROGMEM


    // Sigmoid activation function
    ailayer_sigmoid_f32_t sigmoid_layer_1;

    /* // Alternative activation functions
    ailayer_relu_f32_t          relu_layer;
    ailayer_sigmoid_f32_t       sigmoid_layer;
    ailayer_tanh_f32_t          tanh_layer;
    ailayer_softsign_f32_t      softsign_layer;
    ailayer_leaky_relu_f32_t    leaky_relu_layer;
    ailayer_elu_f32_t           elu_layer;
    //Alpha values
    leaky_relu_layer.alpha = 0.01f;
    elu_layer.alpha = 1.0f;
    //Softmax
    ailayer_softmax_f32_t       softmax_layer; // Only for the output layer
    */

    // Output dense layer
    const float weights_output_layer[] PROGMEM = {12.0305f, -6.5858f, 11.9371f};  //Output dense layer weights
    const float bias_weights_output_layer[] PROGMEM = {-5.4247f};                 //Output dense layer bias weights
    
    ailayer_dense_t output_layer;                                                 // Creation of the AIfES ouput dense layer
    output_layer.neurons = OUTPUTS;                                               // Number of neurons
    output_layer.weights.data = (void*) weights_output_layer;                     // Passing the output layer weights. The (void*) is only needed if you use PROGMEM
    output_layer.bias.data = (void*) bias_weights_output_layer;                   // Passing the output layer bias weights. The (void*) is only needed if you use PROGMEM

    // Sigmoid activation function
    ailayer_sigmoid_f32_t sigmoid_layer_2;

    // --------------------------- Define the structure of the model ----------------------------

    aimodel_t model;  // AIfES model
    ailayer_t *x;     // Layer object from AIfES, contains the layers

    // Passing the layers to the AIfES model
    model.input_layer = ailayer_input_f32_default(&input_layer);
    x = ailayer_dense_f32_default(&hidden_layer, model.input_layer);
    x = ailayer_sigmoid_f32_default(&sigmoid_layer_1, x);
    x = ailayer_dense_f32_default(&output_layer, x);
    model.output_layer = ailayer_sigmoid_f32_default(&sigmoid_layer_2, x);

    aialgo_compile_model(&model); // Compile the AIfES model

    // -------------------------------- Allocate and schedule the working memory for inference ---------
    uint32_t memory_size = aialgo_sizeof_inference_memory(&model);
    void *memory_ptr = malloc(memory_size);
    // Here is an alternative if no "malloc" should be used
    //byte memory_ptr[memory_size];

    // Schedule the memory over the model
    aialgo_schedule_inference_memory(&model, memory_ptr, memory_size);

     // ------------------------------------- Run the inference ------------------------------------
    aialgo_inference_model(&model, &input_tensor, &output_tensor_1);

    // ------------------------------------- Print result ------------------------------------
    Serial.print("Output 0: ");
    Serial.println(output_data[0],6);
    Serial.print("Output 1: ");
    Serial.println(output_data[1],6);
    Serial.print("Output 2: ");
    Serial.println(output_data[2],6);
    Serial.print("Output 3: ");
    Serial.println(output_data[3],6);


    free(memory_ptr);

}

void Inference_FlatWeights(){

    //FlatWeights array
    const float FlatWeights[] PROGMEM = {-10.1164f, -8.4212f, 5.4396f, 7.297f, -7.6482f, -9.0155f, -2.9653f,  2.3677f, -1.5968f, 12.0305f, -6.5858f, 11.9371f,-5.4247f};

    //Tensor for the input data
    //float input_data[] = {0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f};    // Input data for the XOR ANN
    float input_data[DATA_COUNT][INPUTS] = {
        {0.0f, 0.0f},
        {0.0f, 1.0f},
        {1.0f, 0.0f},
        {1.0f, 1.0f}
    };

    //Tensor for the input data
    uint16_t input_shape[] = {DATA_COUNT, INPUTS};      // Definition of the input shape
    aitensor_t input_tensor;                            // Creation of the input AIfES tensor
    input_tensor.dtype = aif32;                         // Definition of the used data type, here float with 32 bits, different ones are available
    input_tensor.dim = 2;                               // Dimensions of the tensor, here 2 dimensions, as specified at input_shape
    input_tensor.shape = input_shape;                   // Set the shape of the input_tensor
    input_tensor.data = input_data;                     // Assign the input_data array to the tensor. It expects a pointer to the array where the data is stored

    //Tensor for the output data
    float output_data[DATA_COUNT * OUTPUTS];            // Output data
    uint16_t output_shape[] = {DATA_COUNT, OUTPUTS};    // Definition of the output shape
    aitensor_t output_tensor_1;                         // Creation of the output AIfES tensor
    output_tensor_1.dtype = aif32;                      // Definition of the used data type, here float with 32 bits, different ones are available
    output_tensor_1.dim = 2;                            // Dimensions of the tensor, here 2 dimensions, as specified at input_shape
    output_tensor_1.shape = output_shape;               // Set the shape of the output_tensor
    output_tensor_1.data = output_data;                 // Assign the output_data array to the tensor. It expects a pointer to the array where the data is stored

    // ---------------------------------- Layer definition ---------------------------------------

    // Input layer
    uint16_t input_layer_shape[] = {1, INPUTS};         // Definition of the input layer shape (The 1 must remain here regardless of the number of data sets)
    ailayer_input_t input_layer;                        // Creation of the AIfES input layer
    input_layer.input_dim = 2;                          // Definition of the input dimension (Must fit to the input tensor)
    input_layer.input_shape = input_layer_shape;        // Handover of the input layer shape

    // Dense layer (hidden layer)
    ailayer_dense_t hidden_layer;                       // Creation of the AIfES hidden dense layer
    hidden_layer.neurons = NEURONS;                     // Number of neurons

    // Sigmoid activation function
    ailayer_sigmoid_f32_t sigmoid_layer_1;

    /* // Alternative activation functions
    ailayer_relu_f32_t          relu_layer;
    ailayer_sigmoid_f32_t       sigmoid_layer;
    ailayer_tanh_f32_t          tanh_layer;
    ailayer_softsign_f32_t      softsign_layer;
    ailayer_leaky_relu_f32_t    leaky_relu_layer;
    ailayer_elu_f32_t           elu_layer;
    //Alpha values
    leaky_relu_layer.alpha = 0.01f;
    elu_layer.alpha = 1.0f;
    //Softmax
    ailayer_softmax_f32_t       softmax_layer; // Only for the output layer
    */

    // Output dense layer
    ailayer_dense_t output_layer;                       // Creation of the AIfES ouput dense layer
    output_layer.neurons = OUTPUTS;                     // Number of neurons

    // Sigmoid activation function
    ailayer_sigmoid_f32_t sigmoid_layer_2;

    // --------------------------- Define the structure of the model ----------------------------

    aimodel_t model;                                    // AIfES model
    ailayer_t *x;                                       // Layer object from AIfES, contains the layers

    // Passing the layers to the AIfES model
    model.input_layer = ailayer_input_f32_default(&input_layer);
    x = ailayer_dense_f32_default(&hidden_layer, model.input_layer);
    x = ailayer_sigmoid_f32_default(&sigmoid_layer_1, x);
    x = ailayer_dense_f32_default(&output_layer, x);
    model.output_layer = ailayer_sigmoid_f32_default(&sigmoid_layer_2, x);

    aialgo_compile_model(&model); // Compile the AIfES model

    // -------------------------------- Allocate and schedule the working memory for inference ---------

    // Parameter memory size in Byte
    uint32_t parameter_memory_size = aialgo_sizeof_parameter_memory(&model);
    
    // Calculate the number of float weights
    uint32_t FlatWeight_array_length = parameter_memory_size / sizeof(float);
    Serial.print(F("Length FlatWeight array: "));
    Serial.println(FlatWeight_array_length);
    
    //Check the array length
    if(FlatWeight_array_length != sizeof(FlatWeights)/sizeof(float))
    {
      Serial.println(F("ERROR!: Number of weights wrong"));
    }
    
    aialgo_distribute_parameter_memory(&model, (void*) FlatWeights, parameter_memory_size); //The (void*) is only needed if you use PROGMEM

    uint32_t memory_size = aialgo_sizeof_inference_memory(&model);
    void *memory_ptr = malloc(memory_size);
    // Here is an alternative if no "malloc" should be used
    //byte memory_ptr[memory_size];

    // Schedule the memory over the model
    aialgo_schedule_inference_memory(&model, memory_ptr, memory_size);

     // ------------------------------------- Run the inference ------------------------------------
    aialgo_inference_model(&model, &input_tensor, &output_tensor_1);

    // ------------------------------------- Print result ------------------------------------
    Serial.print("Output 0: ");
    Serial.println(output_data[0],6);
    Serial.print("Output 1: ");
    Serial.println(output_data[1],6);
    Serial.print("Output 2: ");
    Serial.println(output_data[2],6);
    Serial.print("Output 3: ");
    Serial.println(output_data[3],6);

    free(memory_ptr);

}

void Inference_FlatWeights_bytes(){

    //FlatWeights array
    float FlatWeights[] = {-10.1164f, -8.4212f, 5.4396f, 7.297f, -7.6482f, -9.0155f, -2.9653f,  2.3677f, -1.5968f, 12.0305f, -6.5858f, 11.9371f,-5.4247f};

    //Tensor for the input data
    //float input_data[] = {0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f};    // Input data for the XOR ANN
    float input_data[DATA_COUNT][INPUTS] = {
        {0.0f, 0.0f},
        {0.0f, 1.0f},
        {1.0f, 0.0f},
        {1.0f, 1.0f}
    };

    //Tensor for the input data
    uint16_t input_shape[] = {DATA_COUNT, INPUTS};      // Definition of the input shape
    aitensor_t input_tensor;                            // Creation of the input AIfES tensor
    input_tensor.dtype = aif32;                         // Definition of the used data type, here float with 32 bits, different ones are available
    input_tensor.dim = 2;                               // Dimensions of the tensor, here 2 dimensions, as specified at input_shape
    input_tensor.shape = input_shape;                   // Set the shape of the input_tensor
    input_tensor.data = input_data;                     // Assign the input_data array to the tensor. It expects a pointer to the array where the data is stored

    //Tensor for the output data
    float output_data[DATA_COUNT * OUTPUTS];            // Output data
    uint16_t output_shape[] = {DATA_COUNT, OUTPUTS};    // Definition of the output shape
    aitensor_t output_tensor_1;                         // Creation of the output AIfES tensor
    output_tensor_1.dtype = aif32;                      // Definition of the used data type, here float with 32 bits, different ones are available
    output_tensor_1.dim = 2;                            // Dimensions of the tensor, here 2 dimensions, as specified at input_shape
    output_tensor_1.shape = output_shape;               // Set the shape of the output_tensor
    output_tensor_1.data = output_data;                 // Assign the output_data array to the tensor. It expects a pointer to the array where the data is stored

    // ---------------------------------- Layer definition ---------------------------------------

    // Input layer
    uint16_t input_layer_shape[] = {1, INPUTS};         // Definition of the input layer shape (The 1 must remain here regardless of the number of data sets)
    ailayer_input_t input_layer;                        // Creation of the AIfES input layer
    input_layer.input_dim = 2;                          // Definition of the input dimension (Must fit to the input tensor)
    input_layer.input_shape = input_layer_shape;        // Handover of the input layer shape

    // Dense layer (hidden layer)
    ailayer_dense_t hidden_layer;                       // Creation of the AIfES hidden dense layer
    hidden_layer.neurons = NEURONS;                     // Number of neurons

    // Sigmoid activation function
    ailayer_sigmoid_f32_t sigmoid_layer_1;

    /* // Alternative activation functions
    ailayer_relu_f32_t          relu_layer;
    ailayer_sigmoid_f32_t       sigmoid_layer;
    ailayer_tanh_f32_t          tanh_layer;
    ailayer_softsign_f32_t      softsign_layer;
    ailayer_leaky_relu_f32_t    leaky_relu_layer;
    ailayer_elu_f32_t           elu_layer;
    //Alpha values
    leaky_relu_layer.alpha = 0.01f;
    elu_layer.alpha = 1.0f;
    //Softmax
    ailayer_softmax_f32_t       softmax_layer; // Only for the output layer
    */

    // Output dense layer
    ailayer_dense_t output_layer;                       // Creation of the AIfES ouput dense layer
    output_layer.neurons = OUTPUTS;                     // Number of neurons

    // Sigmoid activation function
    ailayer_sigmoid_f32_t sigmoid_layer_2;

    // --------------------------- Define the structure of the model ----------------------------

    aimodel_t model;                                    // AIfES model
    ailayer_t *x;                                       // Layer object from AIfES, contains the layers

    // Passing the layers to the AIfES model
    model.input_layer = ailayer_input_f32_default(&input_layer);
    x = ailayer_dense_f32_default(&hidden_layer, model.input_layer);
    x = ailayer_sigmoid_f32_default(&sigmoid_layer_1, x);
    x = ailayer_dense_f32_default(&output_layer, x);
    model.output_layer = ailayer_sigmoid_f32_default(&sigmoid_layer_2, x);

    aialgo_compile_model(&model); // Compile the AIfES model

    // -------------------------------- Allocate and schedule the working memory for inference ---------

    // Parameter memory size in Byte
    uint32_t parameter_memory_size = aialgo_sizeof_parameter_memory(&model);
    
    // Calculate the number of float weights
    uint32_t FlatWeight_array_length = parameter_memory_size / sizeof(float);
    Serial.print(F("Length FlatWeight array: "));
    Serial.println(FlatWeight_array_length);
    
    //Check the array length
    if(FlatWeight_array_length != sizeof(FlatWeights)/sizeof(float))
    {
      Serial.println(F("ERROR!: Number of weights wrong"));
    }

    // Array for the weights in uint8
    uint8_t FlatWeights_byte[parameter_memory_size];

    // Typecast uint_8 pointer
    uint8_t *FlatWeights_byte_ptr = (uint8_t *) FlatWeights;

    uint32_t i = 0;

    // Copy the values into the uint8 array
    for (i = 0; i < parameter_memory_size; i++) {
      FlatWeights_byte[i] = FlatWeights_byte_ptr[i];
    }
  
    aialgo_distribute_parameter_memory(&model, (void*) FlatWeights_byte, parameter_memory_size); //The (void*) is only needed if you use PROGMEM

    uint32_t memory_size = aialgo_sizeof_inference_memory(&model);
    void *memory_ptr = malloc(memory_size);
    // Here is an alternative if no "malloc" should be used
    //byte memory_ptr[memory_size];

    // Schedule the memory over the model
    aialgo_schedule_inference_memory(&model, memory_ptr, memory_size);

     // ------------------------------------- Run the inference ------------------------------------
    aialgo_inference_model(&model, &input_tensor, &output_tensor_1);

    // ------------------------------------- Print result ------------------------------------
    Serial.print("Output 0: ");
    Serial.println(output_data[0],6);
    Serial.print("Output 1: ");
    Serial.println(output_data[1],6);
    Serial.print("Output 2: ");
    Serial.println(output_data[2],6);
    Serial.print("Output 3: ");
    Serial.println(output_data[3],6);

    free(memory_ptr);

}

Credits

aifes_team
1 project • 18 followers
Contact

Comments

Please log in or sign up to comment.