04-Arduino板上的机器学习(AIfES for Arduino)

(AIfES for Arduino)–高效微控制器人工智能开源框架
原文链接:链接: AIfES for Arduino由Jean-Luc Aufranc撰写。
请添加图片描述

4-1 IoT简介(物联网简介)

在这里插入图片描述

4-2 点亮未来之路: AI

让电脑有能力自我学习,这就是机器学习。

4-3 传感器新浪潮:AI + IoT

①云端计算

在这里插入图片描述

②边缘计算

在这里插入图片描述

4-4 Arduino软件开发ESP32应用

(一) 准备一块ESP32开发板

ESP32是一个可以用于控制的AI硬件开发板,可借两侧的输出入(I/O)管脚控制外部电子元件。还具有WIFI连网接口传送数据的功能。
在这里插入图片描述

(二)安装Arduino开发环境

下载Arduino IDE开发环境(链接: 下载Arduino IDE
下载安装驱动程序链接:( 下载安装驱动程序链接

新增ESP32开发板到Arduino IDE
在Arduino IDE中执行‘文件’->‘首选项’
在这里插入图片描述
打开 Arduino 软件,点击 “文件”→“首选项”,在 “附加开发板管理器网址” 中输入 https://dl.espressif.com/dl/package_esp32_index.json(这是 ESP32 开发板的管理网址,其实不填也行,不影响下面的步骤)
在Arduino IDE中执行‘工具’->‘开发板管理器’
在这里插入图片描述
搜索输入框esp32,按此安装
在这里插入图片描述
因为要在国外的网站下载,所以会失败。可以转为手动下载方式。
手动下载方式:
进入镜像仓库网址链接: 下载 ESP32 的支撑包 https://codechina.youkuaiyun.com/mirrors/espressif/arduino-esp32 下载 ESP32 的支撑包。该仓库的下载速度相对较快,能有效解决从国外网站下载速度慢或无法访问的问题。这个过程会很花很长时间,加上安装时间,约1个多小时。
在这里插入图片描述

在这里插入图片描述
连接ESP32,选择所使用的开发板和串口号
在这里插入图片描述

4-5 什么是AI人工智能

图像辨识、语音识别、数据处理的问题,转成程序语言,形成解决问题的规则。 让电脑自己学习规则,这就是机器学习( Machine Learning , ML)

4-5-1 机器学习

监督式学习非监督式学习
监督式学习所使用的训练数据集合会事先给予标记 ,即准备一些问题和对应的答案给电脑后,通过合适的推理让它\ 自行找出其中的规则,并有能力针对类似的问题给出正确的回答,
常见的回归分析(Regression Analysis)与 统计分类(Classification)

4-5-2 神经网络

在机器学习中最主流的方法是类神经网络(Artificial Neural Network ,ANN,简称神经网络)
在这里插入图片描述
人工神经元
在这里插入图片描述
输入-就是问题
输出-就是答案
权重偏值 就是要自我学习的参数 可以表示成:
输出 = 输入 1 × 权重 1 + 输入 2 × 权重 2 + 输入 3 × 权重 3 + 偏值 输出 = 输入1×权重1+输入2×权重2+输入3×权重3+偏值 输出=输入1×权重1+输入2×权重2+输入3×权重3+偏值
如果只有一个输入的神经元:
在这里插入图片描述
输出 = 输入 × 权重 + 偏值 输出=输入×权重+偏值 输出=输入×权重+偏值
回归问题
指的是找到两组数据之间的对应关系
资料如下:
在这里插入图片描述
在这里插入图片描述

有1条线能大致连接: y = 0.8337 x − 81.331 y=0.8337x-81.331 y=0.8337x81.331

其实就是一个函数 体重 = 身高 × 0.8337 − 81.331 体重=身高×0.8337-81.331 体重=身高×0.833781.331

在这里插入图片描述
用直线表示关系,不是很贴合。`(>﹏<)′
用分段的折线来解决非线性问题。( ̄︶ ̄)↗ 
采用 激活函数(activation function) 便能 在神经元输出之前进行非线性计算,再将值输出:
在这里插入图片描述
最常用的激活函数是 ReLU函数,小于转折点的输出数值都等于0
在这里插入图片描述
如果更贴合数据,就要导入更多非线性度,做法
将多个神经元连接在一起:
上下并排的神经元合称 神经层
在这里插入图片描述
由于每个神经元共用一个激活函数,多个
激活函数 的神经元,等同提供更多非线性度,(ReLU 就是一次转折),所以生成函数更贴近数据:

神经网络
为了预测更加准确,可以加入更多输入数据,这些数据又称为-特征(Feature)

在这里插入图片描述
完整的神经网络又可以称为 - 模型 (model)

在这里插入图片描述
神经元的学习过程

神经元怎么学习?
一开始神经元什么都不会,因此 权重偏值
都是乱猜的,
所以输出的答案也是不对的,不过它会比对你给
的数据来调整,直到它的输出与你给的资料一致:
在这里插入图片描述
损失函数 -计算预测值和标签误差的函数, 计算出来的值称为-损失值(loss) ,越大代表误差越多,
神经元有这个数值后就知道怎么调整它的参数,
不同问题会搭配不同损失函数,例如,回归问题就会使用

  • 均方误差(Mean Squared Error,MSE)
    每笔标签减预测值(即误差),取平方,再取平均值。
    标签值: y 1 、 y 2 、 y 3 、 y 4 、 y 5 、 . . . 、 y n 、 标签值:y_1、y_2、y_3、y_4、y_5、...、y_n、 标签值:y1y2y3y4y5...yn
    预测值: y 1 ^ 、 y 2 ^ 、 y 3 ^ 、 y 4 ^ 、 y 5 ^ 、 . . . 、 y n ^ 、 预测值:\hat{y_1}、\hat{y_2}、\hat{y_3}、\hat{y_4}、\hat{y_5}、...、\hat{y_n}、 预测值:y1^y2^y3^y4^y5^...yn^
    均方差( M S E ) = 1 n ∑ i = 1 n ( y i − y i ^ ) 2 均方差(MSE)=\frac{1}{n} \sum_{i=1}^{n}(y_i-\hat{y_i})^2 均方差(MSE=n1i=1n(yiyi^)2
    优化器(optimizer) 利用损失值来更新 权重偏值
    调整神经元,让损失值降低,这个学习过程称为 - 训练
    由于更新的方向由后面层向前面层,又被称为 - 反向传播(BackPropagation,BP)
    使用 梯度下降(Gradient descent) 法
    朝向更小损失值的方向,修正参数,就能靠近最小损失值。
    在这里插入图片描述
    自适应调整学习率-η,加快训练速度。
    加上动量因素冲出区域最低,而到达全域最低。 因此,优化器也各有不同。

4-6 机器学习函数库

安装AIfES for Arduino

AIfES(Artificial Inteligence for Embedded System)是
一个用C语言编写的 机器学习函数库 ,能在嵌入式设备中进行神经网络的训练,训练后的参数可以建立神经网络模型,能够直接载入训练好的权重参数进行预测。
在这里插入图片描述搜索框中输入-AIfES
在这里插入图片描述
安装后…
在这里插入图片描述

4-7 学习更多的AIfES for Arduino的项目

链接: AIfES项目学习网址
链接1: wokwi上面的仿真项目链接1-(https://wokwi.com/projects/339063088500179539)
链接2: wokwi上面的仿真项目链接2-(https://wokwi.com/projects/326198897167827538)
实验耗材:Arduino 或 Arduino 兼容板

(一)实验目的

Wokwi 硬件模拟用ESP32开发板:
该示例展示了如何在 AIfES-Express 中使用训练数据从头开始训练神经网络。
使用神经网络学习一个异或门。
这里将异或门的四种不同状态作为训练数据输入。
网络结构是
输入层 2 - 中间层 3(Sigmoid 函数)- 输出层 1(Sigmoid 函数),并且使用 Sigmoid 函数作为激活函数。

在这里插入图片描述
计算使用 32 位浮点数进行。
异或的真值表 - 用于训练模型的数据集:

输入1输入2输出
000
011
101
110

在以下设备上进行了测试:
ESP32 开发套件(Wokwi 模拟)

(二)硬件连线

在这里插入图片描述

(三)程序代码

以下是带有注释的程序代码1:

/*
  www.aifes.ai
  https://github.com/Fraunhofer-IMS/AIfES_for_Arduino
  Copyright (C) 2020-2022  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 Affero 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 Affero General Public License for more details.
  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see <https://www.gnu.org/licenses/>.
  AIfES-Express ESP32 dual core demo
  ONLY FOR ESP32!!!!!!!
 AIfES-Express is a simplified API for AIfES, which is directly integrated. So you can simply decide which variant you want to use.
  
  The sketch shows an example of how a neural network is trained from scratch in AIfES-Express using training data.
  An XOR gate is mapped here using a neural network. 
  The 4 different states of an XOR gate are fed in as training data here. 
  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 / training data
  Input    Output
  0   0    0
  0   1    1
  1   0    1
  1   1    0
 You can find more AIfES tutorials here:
  https://create.arduino.cc/projecthub/aifes_team
  */

// 定义训练数据集数量、输入数量和输出数量
#define DATASETS        4
#define INPUTS          2
#define OUTPUTS         1

// 定义用于多核任务处理的任务句柄
TaskHandle_t  Core0TaskHnd;

// 定义内置 LED 的引脚编号
const int led = LED_BUILTIN;

// 标记神经网络是否已训练的布尔变量
bool NET_TRAINED = false;

// 定义输入数据的二维数组,存储 XOR 门的四种输入状态
float input_data[DATASETS][INPUTS] = {
        {0.0f, 0.0f},                    // Input data
        {0.0f, 1.0f},
        {1.0f, 0.0f},
        {1.0f, 1.0f}
        };

// 定义目标输出数据,对应 XOR 门的输出
float target_data[] = {0.0f, 1.0f, 1.0f, 0.0f}; // Target Data

// 定义输出数据数组,用于存储神经网络的计算结果
float output_data[DATASETS];

// 用于循环的变量
uint32_t i;
// 用于存储串口读取的字符串
String str;

void setup() 
{
  // 初始化串口通信,波特率为 115200
  Serial.begin(115200);  
  // 设置内置 LED 引脚为输出模式
  pinMode(led, OUTPUT);

  // 打印程序信息
  Serial.println ("AIfES - ESP32 dual core demo");
  Serial.print ("Core ");
  Serial.print (xPortGetCoreID());
  Serial.println (": I am responsible for the AI and read the UART commands");
  // 调用构建 AIfES 模型的函数(此处函数未给出具体实现)
  build_AIfES_model();
  // 创建一个任务并将其固定到核心 0,任务名为"CPU_0",任务栈大小为 1000,无参数传递,优先级为 1,任务句柄为 Core0TaskHnd,核心编号为 0
  xTaskCreatePinnedToCore(CoreTask0,"CPU_0",1000,NULL,1,&Core0TaskHnd,0);
  // IMPORTANT
  // AIfES 要求训练时使用随机权重
  // 这里通过模拟引脚 A5 的噪声生成随机种子
  srand(analogRead(A5));
}

void loop() 
{
    // 读取串口数据并存储为字符串
    str = Serial.readString();
    // 如果字符串中包含"training"
    if(str.indexOf("training") > -1)
    {
      // 打印训练信息
      Serial.println("Training");
      // 调用训练 AIfES 模型的函数,传入输入数据、目标数据和输出数据的指针
      train_AIfES_model((float*)input_data,(float*)target_data,(float*)output_data);
      // 设置已训练标志为真
      NET_TRAINED = true;
      
      Serial.println(F(""));
      Serial.println(F("Results:"));
      Serial.println(F("input 1:\tinput 2:\treal output:\tcalculated output:"));
      // 遍历数据集,打印输入数据、目标输出和计算得到的输出
      for (i = 0; i < DATASETS; i++) 
      {
        Serial.print (input_data[i][0]);
        Serial.print (F("\t\t"));
        Serial.print (input_data[i][1]);
        Serial.print (F("\t\t"));
        Serial.print (target_data[i]);
        Serial.print (F("\t\t"));
        Serial.println(output_data[i], 5);
      }
      Serial.println(F("A learning success is not guaranteed"));
      Serial.println(F("The weights were initialized randomly"));
      Serial.println(F("You can repeat the training with >training<\n"));
      Serial.println(F("Type >inference< to perform the inference"));
     
    }

    // 如果字符串中包含"inference"
    if(str.indexOf("inference") > -1)
    {
        // 如果网络已训练
        if(NET_TRAINED == true)
        {
          // 打印推理信息
          Serial.println("inference");
          // 初始化输出数据数组为 0
          output_data[0] = 0.0f;
          output_data[1] = 0.0f;
          output_data[2] = 0.0f;
          output_data[3] = 0.0f;
          // 调用推理函数,传入输入数据和输出数据的指针
          inference((float*)input_data,(float*)output_data);
          
          Serial.println(F(""));
          Serial.println(F("Inference results:"));
          Serial.println(F("input 1:\tinput 2:\tcalculated output:"));
          
          // 遍历数据集,打印输入数据和推理得到的输出
          for (i = 0; i < DATASETS; i++) 
          {
            Serial.print (input_data[i][0]);
            Serial.print (F("\t\t"));
            Serial.print (input_data[i][1]);
            Serial.print (F("\t\t"));
            Serial.println(output_data[i], 5);
          }
          
        }
        else
        {
            // 如果网络未训练,打印提示信息
            Serial.println("Net not trained");
        }
    }

  }

// 核心 0 的任务函数
void CoreTask0( void * parameter ) 
{ 
  Serial.print ("Core ");
  Serial.print (xPortGetCoreID());
  Serial.println(" (Task0): I switch the internal LED on and off");
  Serial.println("");
  Serial.println("Type >training< to start training");
  Serial.println("Type >inference< to peform the inference");
  for (;;) 
  { 
    // 点亮内置 LED
    digitalWrite(led, HIGH);
    delay (1000);
    // 熄灭内置 LED
    digitalWrite(led, LOW);
    delay (1000);
    // 让出处理器时间
    yield();
  } 
} 

以下是程序代码2:

/*
  www.aifes.ai
  https://github.com/Fraunhofer-IMS/AIfES_for_Arduino
  Copyright (C) 2020-2022  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 Affero 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 Affero General Public License for more details.
  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see <https://www.gnu.org/licenses/>.

  
  Short tutorial for Wokwi:
  - Press the green "play" button to start the simulation 
  - After a short waiting time the program will show the following text: 
     AIfES-Express XOR training demo 
     Type >training< to start 
  - Type the word "training" in the window below the output and confirm with Enter 
  - The UART simulation and processing of the input takes some time 
  - Then the online training with AIfES starts 
  - A training success is of course not guaranteed, because the weights are randomly chosen 
  - If it takes too long (max. 1000 epochs) you can also stop and restart the simulation 
  - To train anew type "training" again 
  
  Of course, you can also create your own project. 
  To use AIfES in Wokwi you have to include the AIfES for Arduino library. 
  - Click on the "Library Manager" and then on the purple "plus" icon. 
  - Search for "aifes" and add the library. 

  --------------------
  AIfES-Express XOR training demo
  --------------------
    Versions:
    1.0.0   Initial version
  AIfES-Express is a simplified API for AIfES, which is directly integrated. So you can simply decide which variant you want to use.
  
  The sketch shows an example of how a neural network is trained from scratch in AIfES-Express using training data.
  As in the example "0_AIfES-Express_XOR_Inference", an XOR gate is mapped here using a neural network. 
  The 4 different states of an XOR gate are fed in as training data here. 
  The network structure is 2-3(Sigmoid)-1(Sigmoid) and Sigmoid is used as activation function.
  In the example, the weights are initialized randomly in a range of values from -2 to +2. The Gotrot initialization was inserted as an alternative and commented out.
  For the training the ADAM Optimizer is used, the SGD Optimizer was commented out. 
  The optimizer runs a batch traininig over a maximum of 1000 epochs. 
  The early stopping is activated and stops the training when a desired target loss is reached.
  The calculation is done in float 32.
  
  XOR truth table / training data
  Input    Output
  0   0    0
  0   1    1
  1   0    1
  1   1    0

  You can find more AIfES tutorials here:
  https://create.arduino.cc/projecthub/aifes_team
  */

#include <aifes.h>                  // include the AIfES libary

#define DATASETS        4
#define FNN_3_LAYERS    3
#define PRINT_INTERVAL  10

// 定义全局变量,用于记录训练的 epoch 次数
uint32_t global_epoch_counter = 0;

// 自定义的打印损失函数
void printLoss(float loss)
{
    // 增加 epoch 计数器
    global_epoch_counter = global_epoch_counter + 1;
    Serial.print(F("Epoch: "));
    // 打印当前 epoch 次数乘以打印间隔
    Serial.print(global_epoch_counter * PRINT_INTERVAL);
    Serial.print(F(" / Loss: "));
    // 打印损失值,保留 5 位小数
    Serial.println(loss, 5);
    
}

void setup() {
  // 初始化串口通信,波特率为 115200(如果需要,可以在串口监视器中更改)
  Serial.begin(115200); 
  // 等待串口连接
  while (!Serial);
  
  // IMPORTANT
  // AIfES 要求训练时使用随机权重
  // 这里通过模拟引脚 A5 的噪声生成随机种子
  srand(analogRead(A5));

  // 打印程序名称
  Serial.println(F("AIfES-Express XOR training demo"));
  Serial.println(F("Type >training< to start"));
 
}

void loop() {

  // 当串口有数据可读时
  while(Serial.available() > 0 ){
    // 读取串口数据并存储为字符串
    String str = Serial.readString();
    // 如果字符串中包含"training"
    if(str.indexOf("training") > -1)       //Keyword "training"
     {
        
        Serial.println(F("AIfES:"));
        Serial.println(F(""));
        Serial.println(F("rand test"));
        // 打印随机数生成器生成的随机数
        Serial.println(rand());

        // 重置 epoch 计数器
        global_epoch_counter = 0;

        uint32_t i;
       
        // -------------------------------- describe the feed forward neural network ----------------------------------
        // neurons each layer
        // FNN_structure[0] = input layer with 2 inputs
        // FNN_structure[1] = hidden (dense) layer with 3 neurons
        // FNN_structure[2] = output (dense) layer with 1 output
        uint32_t FNN_structure[FNN_3_LAYERS] = {2,3,1};
    
        // 设置密集层的激活函数
        AIFES_E_activations FNN_activations[FNN_3_LAYERS - 1];
        FNN_activations[0] = AIfES_E_sigmoid; // Sigmoid for hidden (dense) layer
        FNN_activations[1] = AIfES_E_sigmoid; // Sigmoid for output (dense) layer
    
        /* possible activation functions
        AIfES_E_relu
        AIfES_E_sigmoid
        AIfES_E_softmax
        AIfES_E_leaky_relu
        AIfES_E_elu
        AIfES_E_tanh
        AIfES_E_softsign
        AIfES_E_linear
        */
    
        // AIfES Express 函数:计算所需的权重数量
        uint32_t weight_number = AIFES_E_flat_weights_number_fnn_f32(FNN_structure,FNN_3_LAYERS);
    
        Serial.print(F("Weights: "));
        Serial.println(weight_number);
    
        // FlatWeights 数组
        //float FlatWeights[weight_number];

        // 替代的权重数组
        float *FlatWeights;
        // 动态分配内存以存储权重数组
        FlatWeights = (float *)malloc(sizeof(float)*weight_number); 
    
    
        // 填充 AIfES Express 结构体
        AIFES_E_model_parameter_fnn_f32 FNN;
        FNN.layer_count = FNN_3_LAYERS;
        FNN.fnn_structure = FNN_structure;
        FNN.fnn_activations = FNN_activations;
        FNN.flat_weights = FlatWeights;
    
        // -------------------------------- create the tensors ----------------------------------
             
        // 定义输入数据,模拟 XOR 门的四种输入状态
        float input_data[4][2] = {
        {0.0f, 0.0f},                                                                       // Input data
        {0.0f, 1.0f},
        {1.0f, 0.0f},
        {1.0f, 1.0f}
        };
        // 定义输入张量的形状
        uint16_t input_shape[] = {DATASETS, (uint16_t)FNN_structure[0]};                     // Definition of the input shape
        // 创建输入张量,AITENSOR_2D_F32 是一个宏,用于简单地创建一个 float32 张量
        aitensor_t input_tensor = AITENSOR_2D_F32(input_shape, input_data);                 // Macro for the simple creation of a float32 tensor. Also usable in the normal AIfES version
    
        // 定义目标输出数据,对应 XOR 门的输出
        float target_data[] = {0.0f, 1.0f, 1.0f, 0.0f};                                     // Target Data
        // 定义目标张量的形状
        uint16_t target_shape[] = {DATASETS, (uint16_t)FNN_structure[FNN_3_LAYERS - 1]};     // Definition of the target shape
        // 创建目标张量
        aitensor_t target_tensor = AITENSOR_2D_F32(target_shape, target_data);              // Macro for the simple creation of a float32 tensor. Also usable in the normal AIfES version
       
        // 定义输出数据数组,用于存储神经网络的计算结果
        float output_data[DATASETS];                                                        // Output data
        // 定义输出张量的形状
        uint16_t output_shape[] = {DATASETS, (uint16_t)FNN_structure[FNN_3_LAYERS - 1]};     // Definition of the output shape
        // 创建输出张量
        aitensor_t output_tensor = AITENSOR_2D_F32(output_shape, output_data);              // Macro for the simple creation of a float32 tensor. Also usable in the normal AIfES version
        
        // -------------------------------- init weights settings ----------------------------------
    
        AIFES_E_init_weights_parameter_fnn_f32  FNN_INIT_WEIGHTS;
        FNN_INIT_WEIGHTS.init_weights_method = AIfES_E_init_uniform;
    
        /* init methods
            AIfES_E_init_uniform
            AIfES_E_init_glorot_uniform
            AIfES_E_init_no_init        //If starting weights are already available or if you want to continue training
        */
    
        FNN_INIT_WEIGHTS.min_init_uniform = -2; // only for the AIfES_E_init_uniform
        FNN_INIT_WEIGHTS.max_init_uniform = 2;  // only for the AIfES_E_init_uniform
        // -------------------------------- set training parameter ----------------------------------
        AIFES_E_training_parameter_fnn_f32  FNN_TRAIN;
        FNN_TRAIN.optimizer = AIfES_E_adam;
        /* optimizers
            AIfES_E_adam
            AIfES_E_sgd
        */
        FNN_TRAIN.loss = AIfES_E_mse;
        /* loss
            AIfES_E_mse,
            AIfES_E_crossentropy
        */
        FNN_TRAIN.learn_rate = 0.05f;                           // Learning rate is for all optimizers
        FNN_TRAIN.sgd_momentum = 0.0;                           // Only interesting for SGD
        FNN_TRAIN.batch_size = DATASETS;                        // Here a full batch
        FNN_TRAIN.epochs = 1000;                                // Number of epochs
        FNN_TRAIN.epochs_loss_print_interval = PRINT_INTERVAL;  // Print the loss every x times
    
        // 自定义的打印损失函数
        // it must look like this: void YourFunctionName(float x)
        FNN_TRAIN.loss_print_function = printLoss;

        // 启用早停机制,当达到目标损失时自动停止学习
        FNN_TRAIN.early_stopping = AIfES_E_early_stopping_on;
        /* early_stopping
            AIfES_E_early_stopping_off,
            AIfES_E_early_stopping_on
        */
        // 定义目标损失
        FNN_TRAIN.early_stopping_target_loss = 0.004;

        int8_t error = 0;
    
        // -------------------------------- do the training ----------------------------------
        // 在训练函数中,设置 FNN,初始化权重并执行训练
        error = AIFES_E_training_fnn_f32(&input_tensor,&target_tensor,&FNN,&FNN_TRAIN,&FNN_INIT_WEIGHTS,&output_tensor);

        // 处理训练中的错误
        error_handling_training(error); 
    
        // -------------------------------- do the inference ----------------------------------
        // AIfES Express 函数:执行推理
        error = AIFES_E_inference_fnn_f32(&input_tensor,&FNN,&output_tensor);
        
        // 处理推理中的错误
        error_handling_inference(error);    
        
        // -------------------------------- print the results ----------------------------------
   

        Serial.println(F(""));
        Serial.println(F("Results:"));
        Serial.println(F("input 1:\tinput 2:\treal output:\tcalculated output:"));
        
        // 遍历数据集,打印输入数据、目标输出和计算得到的输出
        for (i = 0; i < 4; i++) {
          Serial.print (input_data[i][0]);
          //Serial.print(((float* ) input_tensor.data)[i]); //Alternative print for the tensor
          Serial.print (F("\t\t"));
          Serial.print (input_data[i][1]);
          Serial.print (F("\t\t"));
          Serial.print (target_data[i]);
          Serial.print (F("\t\t"));
          Serial.println(output_data[i], 5);
          //Serial.println(((float* ) output_tensor.data)[i], 5); //Alternative print for the tensor
        }

        Serial.println(F(""));
        Serial.println(F("A learning success is not guaranteed"));
        Serial.println(F("The weights were initialized randomly"));
        Serial.println(F("You can repeat the training with >training<"));

     }
    else{
      // 如果输入不是"training",打印"unknown"
      Serial.println(F("unknown"));
    }
  }

}

// 处理训练中的错误函数
void error_handling_training(int8_t error_nr){
  switch(error_nr){
    case 0:
      //Serial.println(F("No Error :)"));
      break;    
    case -1:
      Serial.println(F("ERROR! Tensor dtype"));
      break;
    case -2:
      Serial.println(F("ERROR! Tensor shape: Data Number"));
      break;
    case -3:
      Serial.println(F("ERROR! Input tensor shape does not correspond to ANN inputs"));
      break;
    case -4:
      Serial.println(F("ERROR! Output tensor shape does not correspond to ANN outputs"));
      break;
    case -5:
      Serial.println(F("ERROR! Use the crossentropy as loss for softmax"));
      break;
    case -6:
      Serial.println(F("ERROR! learn_rate or sgd_momentum negative"));
      break;
    case -7:
      Serial.println(F("ERROR! Init uniform weights min - max wrong"));
      break;
    case -8:
      Serial.println(F("ERROR! batch_size: min = 1 / max = Number of training data"));
      break;
    case -9:
      Serial.println(F("ERROR! Unknown activation function"));
      break;
    case -10:
      Serial.println(F("ERROR! Unknown loss function"));
      break;
    case -11:
      Serial.println(F("ERROR! Unknown init weights method"));
      break;
    case -12:
      Serial.println(F("ERROR! Unknown optimizer"));
      break;
    case -13:
      Serial.println(F("ERROR! Not enough memory"));
      break;
    default :
      Serial.println(F("Unknown error"));
  }
}

// 处理推理中的错误函数
void error_handling_inference(int8_t error_nr){
  switch(error_nr){
    case 0:
      //Serial.println(F("No Error :)"));
      break;    
    case -1:
      Serial.println(F("ERROR! Tensor dtype"));
      break;
    case -2:
      Serial.println(F("ERROR! Tensor shape: Data Number"));
      break;
    case -3:
      Serial.println(F("ERROR! Input tensor shape does not correspond to ANN inputs"));
      break;
    case -4:
      Serial.println(F("ERROR! Output tensor shape does not correspond to ANN outputs"));
      break;
    case -5:
      Serial.println(F("ERROR! Unknown activation function"));
      break;
    case -6:
      Serial.println(F("ERROR! Not enough memory"));
      break;
    default :
      Serial.println(F("Unknown error"));
  }
}

(四)程序运行结果

程序代码2运行的结果:

Epoch(训练周期):320次
Loss(损失函数值):0.00385,接近于零👌
在这里插入图片描述
@所有人 
【全新视频教程】正点原子手把手教你学Arduino ESP32入门课程来啦!课程内容系统、全面且实用,覆盖了从基础到进阶,再到综合应用的完整学习路径!对于入门物联网和嵌入式开发的学习者来说是一个非常好的选择!【点击观看】
【正点原子】手把手教你快速入门Arduino ESP32
(目前上传60讲,后续课程将持续更新!)【资料下载】视频置顶评论区可下载视频配套资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值