ESP32神经网络初步使用

news2024/11/15 16:25:41

摘要

本文档描述了如何使用Python和TensorFlow训练一个简单的神经网络模型来预测正弦函数,并将其部署到ESP32微控制器上。

参考文章

使用Python和Arduino在ESP32上预测正弦函数 - Dapenson - 博客园 (cnblogs.com)

最简单体验TinyML、TensorFlow Lite——ESP32跑机器学习(全代码)-CSDN博客

安装tensorflow

在windows上安装python3.8

在cmd中输入

pip install tensorflow

安装图形显示库,方便直观观察数据

pip install matplotlib

编写训练脚本

首先准备数据的python代码

import math
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
# Matplotlib is a graphing library
import matplotlib.pyplot as plt


def get_model():
    SAMPLES = 1000
    np.random.seed(1337)
    x_values = np.random.uniform(low=0, high=2 * math.pi, size=SAMPLES)
    # shuffle and add noise
    np.random.shuffle(x_values)
    y_values = np.sin(x_values)
    y_values += 0.1 * np.random.randn(*y_values.shape)
    # Plot our data
    plt.plot(x_values, y_values, 'b.')
    plt.show()

    # split into train, validation, test
    TRAIN_SPLIT = int(0.6 * SAMPLES)
    TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT)
    x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT])
    y_train, y_test, y_validate = np.split(y_values, [TRAIN_SPLIT, TEST_SPLIT])

    # create a NN with 2 layers of 16 neurons
    model = tf.keras.Sequential()
    model.add(layers.Dense(16, activation='relu', input_shape=(1, )))
    model.add(layers.Dense(16, activation='relu'))
    model.add(layers.Dense(1))
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    model.fit(x_train,
              y_train,
              epochs=200,
              batch_size=16,
              validation_data=(x_validate, y_validate))
    return model

if __name__ == '__main__':
    model = get_model()

定义了一个名为get_model的函数,用于创建一个神经网络模型。以下是代码的详细解释:

首先,导入了所需的库和模块,如numpy、math、matplotlib.pyplot和tensorflow。

定义了一个名为SAMPLES的常量,表示数据集的大小。

使用numpy的random.seed函数设置随机数种子,以便结果可重复。

生成一个包含SAMPLES个均匀分布的随机数的数组x_values,范围在0到2π之间。

对x_values数组进行洗牌和添加噪声,使其更接近实际数据。

使用matplotlib.pyplot的plot函数绘制x_values和y_values的关系图。

将数据集划分为训练集、验证集和测试集。

创建一个具有两个隐藏层(每层16个神经元)的神经网络模型。

使用tensorflow的Sequential类创建一个神经网络模型。

添加两个全连接层,分别具有16个神经元和激活函数relu。

添加一个输出层,具有1个神经元和线性激活函数。

使用compile方法编译模型,指定优化器为rmsprop,损失函数为mse(均方误差),评估指标为mae(平均绝对误差)。

使用fit方法训练模型,传入训练数据x_train和y_train,以及训练参数,如迭代次数(epochs)、批次大小(batch_size)和验证数据(x_validate和y_validate)。

返回训练好的模型。

    # create a NN with 2 layers of 16 neurons
    model = tf.keras.Sequential()
    model.add(layers.Dense(16, activation='relu', input_shape=(1, )))
    model.add(layers.Dense(16, activation='relu'))
    model.add(layers.Dense(1))

这段代码是使用TensorFlow框架构建一个简单的全连接神经网络模型。以下是模型的详细信息:

model = tf.keras.Sequential():创建一个空的序列模型对象。Sequential是TensorFlow中用于构建神经网络的简单接口,可以堆叠多个层以创建神经网络模型。

model.add(layers.Dense(16, activation='relu', input_shape=(1, ))):向模型中添加第一个全连接层。layers.Dense表示全连接层,16表示该层的神经元数量,activation='relu'表示使用ReLU激活函数。input_shape=(1,)表示该层的输入维度为(1,),即每个输入样本只有一个特征。

model.add(layers.Dense(16, activation='relu')):向模型中添加第二个全连接层。该层的参数与第一个全连接层相同。

model.add(layers.Dense(1)):向模型中添加输出层。layers.Dense表示全连接层,1表示该层的神经元数量,即输出结果的维度为1。

    model.fit(x_train,
              y_train,
              epochs=200,
              batch_size=16,
              validation_data=(x_validate, y_validate))

一旦我们定义了模型,我们就可以使用数据来训练它。训练包括向神经网络传递一个x值,检查网络的输出与期望的y值偏离多少,调整神经元的权值和偏差,以便下次输出更有可能是正确的。训练在完整数据集上多次运行这个过程,每次完整的运行都被称为一个epoch。训练中要运行的epoch数是我们可以设置的参数。在每个epoch期间,数据在网络中以多个批次运行。每个批处理,几个数据片段被传递到网络,产生输出值。这些输出的正确性是整体衡量的,网络的权重和偏差是相应调整的,每批一次。批处理大小也是我们可以设置的参数。下面单元格中的代码使用来自训练数据的x和y值来训练模型。它运行200个epoch,每个批处理中有16条数据。我们还传入一些用于验证的数据。

生成随机数据的直观样式

下面通过模型生成tflite

if __name__ == '__main__':
    model = get_model()
    converter = tf.lite.TFLiteConverter.from_keras_model(model)
    converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]
    tflite_model = converter.convert()

这段代码是将一个TensorFlow模型转换为TensorFlow Lite格式。TensorFlow Lite是一种轻量级的TensorFlow库,专为移动和嵌入式设备优化。将TensorFlow模型转换为TensorFlow Lite格式可以使模型在移动和嵌入式设备上运行得更快。

tf.lite.TFLiteConverter 是 TensorFlow Lite 中的一个类,用于将 TensorFlow 模型转换为 TensorFlow Lite 格式。from_keras_model 方法将一个 TensorFlow 模型作为输入,并返回一个 TFLiteConverter 对象。这个对象可以用来将模型转换为 TensorFlow Lite 格式。

converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]:设置优化选项,以便在转换过程中减小模型大小。OPTIMIZE_FOR_SIZE表示在保持模型准确性的同时尽可能减小模型大小。

tflite_model = converter.convert():使用convert()方法将TensorFlow模型转换为TensorFlow Lite模型,并将结果存储在tflite_model变量中。

Epoch 195/200
38/38 [==============================] - 0s 1ms/step - loss: 0.0149 - mae: 0.0969 - val_loss: 0.0127 - val_mae: 0.0886
Epoch 196/200
38/38 [==============================] - 0s 1ms/step - loss: 0.0151 - mae: 0.0985 - val_loss: 0.0144 - val_mae: 0.0963
Epoch 197/200
38/38 [==============================] - 0s 1ms/step - loss: 0.0145 - mae: 0.0955 - val_loss: 0.0115 - val_mae: 0.0813
Epoch 198/200
38/38 [==============================] - 0s 1ms/step - loss: 0.0147 - mae: 0.0953 - val_loss: 0.0110 - val_mae: 0.0816
Epoch 199/200
38/38 [==============================] - 0s 1ms/step - loss: 0.0149 - mae: 0.0972 - val_loss: 0.0137 - val_mae: 0.0920
Epoch 200/200
38/38 [==============================] - 0s 1ms/step - loss: 0.0142 - mae: 0.0946 - val_loss: 0.0113 - val_mae: 0.0850

这些是训练过程中的日志输出,表示模型在每个训练周期(epoch)的损失(loss)和平均绝对误差(mae)。每个周期后,都会在验证数据集上评估模型性能,并报告验证损失和平均绝对误差。

Epoch 191/200表示当前周期是第191个周期,总共200个周期。

38/38 [==============================] - 0s 1ms/step表示在当前周期中,训练数据集的38个样本已经训练完毕,训练耗时0秒,每步训练耗时1毫秒。

- loss: 0.0149 - mae: 0.0961表示在当前周期中,训练数据集的损失为0.0149,平均绝对误差为0.0961。

- val_loss: 0.0149 - val_mae: 0.0982表示在当前周期中,验证数据集的损失为0.0149,平均绝对误差为0.0982。

以此类推,后面的每个周期都会输出类似的信息。

下面我们将tflite_model的TensorFlow Lite模型文件写入名为sine_model.tflite的文件中

if __name__ == '__main__':
    model = get_model()
    converter = tf.lite.TFLiteConverter.from_keras_model(model)
    converter.optimizations = [tf.lite.Optimize.DEFAULT]
    tflite_model = converter.convert()

    # # Save the model to disk
    open("sine_model.tflite", "wb").write(tflite_model)

执行之后会在运行目录下生成文件sine_model.tflite

下面将TensorFlow Lite模型(tflite_model)转换为一个C语言数组,并将结果写入一个名为sine_model_c.h的头文件中。

# Function: Convert some hex value into an array for C programming
def hex_to_c_array(hex_data, var_name):

    c_str = ''

    # Create header guard
    c_str += '#ifndef ' + var_name.upper() + '_H\n'
    c_str += '#define ' + var_name.upper() + '_H\n\n'

    # Add array length at top of file
    c_str += '\nunsigned int ' + var_name + '_len = ' + str(
        len(hex_data)) + ';\n'

    # Declare C variable
    c_str += 'unsigned char ' + var_name + '[] = {'
    hex_array = []
    for i, val in enumerate(hex_data):

        # Construct string from hex
        hex_str = format(val, '#04x')

        # Add formatting so each line stays within 80 characters
        if (i + 1) < len(hex_data):
            hex_str += ','
        if (i + 1) % 12 == 0:
            hex_str += '\n '
        hex_array.append(hex_str)

    # Add closing brace
    c_str += '\n ' + format(' '.join(hex_array)) + '\n};\n\n'

    # Close out header guard
    c_str += '#endif //' + var_name.upper() + '_H'

    return c_str


if __name__ == '__main__':
    model = get_model()
    converter = tf.lite.TFLiteConverter.from_keras_model(model)
    converter.optimizations = [tf.lite.Optimize.DEFAULT]
    tflite_model = converter.convert()

    # # Save the model to disk
    open("sine_model.tflite", "wb").write(tflite_model)

    # # Write TFLite model to a C source (or header) file
    c_model_name = 'sine_model'
    with open(c_model_name + '.h', 'w') as file:
        file.write(hex_to_c_array(tflite_model, c_model_name))

注意:每次训练后的模型都不同,因为每次生成的训练素材都是随机提供的,所以不要以为每次都会有相同的数组。

模型已经被我们训练好了,但一般来说正常训练好的DL模型不能被部署到单片机上,因为太大了,我们将使用TensorFlow Lite转换器。转换器以一种特殊的、节省空间的格式输出文件,以便在内存受限的设备上使用。由于这个模型将部署在一个微控制器上,我们希望它尽可能小!量化是一种减小模型尺寸的技术。它降低了模型权值的精度,节省了内存。

# Function: Post-conversion validation of the model
def validate_model():
    SAMPLES = 1000
    np.random.seed(1337)
    x_values = np.random.uniform(low=0, high=2 * math.pi, size=SAMPLES)
    # shuffle and add noise
    np.random.shuffle(x_values)
    y_values = np.sin(x_values)
    y_values += 0.1 * np.random.randn(*y_values.shape)

    # split into train, validation, test
    TRAIN_SPLIT = int(0.6 * SAMPLES)
    TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT)
    x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT])
    y_train, y_test, y_validate = np.split(y_values, [TRAIN_SPLIT, TEST_SPLIT])

    # create a NN with 2 layers of 16 neurons
    model = tf.keras.Sequential()
    model.add(layers.Dense(16, activation='relu', input_shape=(1, )))
    model.add(layers.Dense(16, activation='relu'))
    model.add(layers.Dense(1))
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    history_1 = model.fit(x_train,
              y_train,
              epochs=200,
              batch_size=16,
              validation_data=(x_validate, y_validate))
    # Make predictions based on our test dataset
    predictions = model.predict(x_test)

    # Convert the model to the TensorFlow Lite format without quantization
    converter = tf.lite.TFLiteConverter.from_keras_model(model)
    tflite_model = converter.convert()

    # Save the model to disk
    open("sine_model_new.tflite", "wb").write(tflite_model)

    # Convert the model to the TensorFlow Lite format with quantization
    converter = tf.lite.TFLiteConverter.from_keras_model(model)
    # Indicate that we want to perform the default optimizations,
    # which includes quantization
    converter.optimizations = [tf.lite.Optimize.DEFAULT]
    # Define a generator function that provides our test data's x values
    # as a representative dataset, and tell the converter to use it
    def representative_dataset_generator():
        for value in x_test:
            # Each scalar value must be inside of a 2D array that is wrapped in a list
            yield [np.array(value, dtype=np.float32, ndmin=2)]
    converter.representative_dataset = representative_dataset_generator
    # Convert the model
    tflite_model = converter.convert()

    # Save the model to disk
    open("sine_model_quantized.tflite", "wb").write(tflite_model)

    # Instantiate an interpreter for each model
    sine_model = tf.lite.Interpreter('sine_model_new.tflite')
    sine_model_quantized = tf.lite.Interpreter('sine_model_quantized.tflite')

    # Allocate memory for each model
    sine_model.allocate_tensors()
    sine_model_quantized.allocate_tensors()

    # Get indexes of the input and output tensors
    sine_model_input_index = sine_model.get_input_details()[0]["index"]
    sine_model_output_index = sine_model.get_output_details()[0]["index"]
    sine_model_quantized_input_index = sine_model_quantized.get_input_details()[0]["index"]
    sine_model_quantized_output_index = sine_model_quantized.get_output_details()[0]["index"]

    # Create arrays to store the results
    sine_model_predictions = []
    sine_model_quantized_predictions = []

    # Run each model's interpreter for each value and store the results in arrays
    for x_value in x_test:
        # Create a 2D tensor wrapping the current x value
        x_value_tensor = tf.convert_to_tensor([[x_value]], dtype=np.float32)
        # Write the value to the input tensor
        sine_model.set_tensor(sine_model_input_index, x_value_tensor)
        # Run inference
        sine_model.invoke()
        # Read the prediction from the output tensor
        sine_model_predictions.append(
            sine_model.get_tensor(sine_model_output_index)[0])
        # Do the same for the quantized model
        sine_model_quantized.set_tensor(sine_model_quantized_input_index, x_value_tensor)
        sine_model_quantized.invoke()
        sine_model_quantized_predictions.append(
            sine_model_quantized.get_tensor(sine_model_quantized_output_index)[0])


    # See how they line up with the data
    plt.clf()
    plt.title('Comparison of various models against actual values')
    plt.plot(x_test, y_test, 'bo', label='Actual')
    plt.plot(x_test, predictions, 'ro', label='Original predictions')
    plt.plot(x_test, sine_model_predictions, 'bx', label='Lite predictions')
    plt.plot(x_test, sine_model_quantized_predictions, 'gx', label='Lite quantized predictions')
    plt.legend()
    plt.show()

这个函数会重新进行训练两个完全一样的模型,然后其中一个进行转换,用未转换的和已转换的进行比较。

可见几乎没有损失。

生成编程使用的数组

我们可以使用xxd命令生成数组,但是不能生成完整的头文件,没有ifdefine

PS E:\tensorflow> & 'D:\Program Files\Git\usr\bin\xxd.exe' -i .\sine_model.tflite > sina_modelxxd.h

xxd是一个Linux命令,Windows安装了git就有,可使用everything自行搜索xxd.exe,将其所在目录添加到环境变量中即可使用xxd命令。

这将生成一个名为sine_modelxxd.h的文件,其中包含模型的C数组表示形式。

unsigned char __sine_model_tflite[] = {
  0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x14, 0x00, 0x20, 0x00,
  0x1c, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
  0x90, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x04, 0x07, 0x00, 0x00,
  ......
  ......
  ......
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
  0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
  0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09
};
unsigned int __sine_model_tflite_len = 3168;

在上一步中的python代码中,已添加python转化模型为c数组的功能,因此此步骤可选

使用模型

现在,我们将转向Arduino部分,将训练好的模型加载到ESP32上,并进行正弦函数的预测。

安装运行环境

示例程序具体分以下几个步骤:

引入库和头文件 在Arduino IDE中,我们需要引入EloquentTinyML库和模型头文件。这些库提供了在ESP32上加载和运行TensorFlow Lite模型的功能。

注意:要使用esp32通用芯片需要0.0.10版本

编写arduino文件

#include <Arduino.h>

// https://github.com/eloquentarduino/EloquentTinyML  Version:0.0.10
#include <EloquentTinyML.h>
// sine_model.h contains the array you exported from the previous step with xxd or tinymlgen
#include "sine_model.h"

#define NUMBER_OF_INPUTS 1
#define NUMBER_OF_OUTPUTS 1
// in future projects you may need to tweek this value: it's a trial and error process
#define TENSOR_ARENA_SIZE 2 * 1024

Eloquent::TinyML::TfLite<NUMBER_OF_INPUTS, NUMBER_OF_OUTPUTS, TENSOR_ARENA_SIZE> ml;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  ml.begin(sine_model);
}

void loop() {
  // pick up a random x and predict its sine
  float x = 3.14 * random(100) / 100;
  float y = sin(x);
  float input[1] = {x};
  float predicted = ml.predict(input);

  Serial.print("sin(");
  Serial.print(x);
  Serial.print(") = ");
  Serial.print(y);
  Serial.print("\t predicted: ");
  Serial.println(predicted);
  delay(100);
}

这段代码是ESP32的Arduino代码,用于加载和运行之前在Python中训练并转换为TensorFlow Lite格式的模型,以进行正弦函数预测。

以下是代码的详细解释:

引入必要的库和头文件:

#include <Arduino.h>
#include <EloquentTinyML.h>
#include "sine_model.h"

定义输入和输出的数量:

#define NUMBER_OF_INPUTS 1
#define NUMBER_OF_OUTPUTS 1

定义Tensor Arena的大小,用于分配内存空间给模型:

#define TENSOR_ARENA_SIZE 2 * 1024

创建TinyML对象并设置模型数据:

Eloquent::TinyML::TfLite<NUMBER_OF_INPUTS, NUMBER_OF_OUTPUTS, TENSOR_ARENA_SIZE> ml;

在setup()函数中初始化串口通信和TinyML模型:

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  ml.begin(sine_model);
}

在loop()函数中进行循环预测:

void loop() {
  // pick up a random x and predict its sine
  float x = 3.14 * random(100) / 100;
  float y = sin(x);
  float input[1] = {x};
  float predicted = ml.predict(input);

  Serial.print("sin(");
  Serial.print(x);
  Serial.print(") = ");
  Serial.print(y);
  Serial.print("\t predicted: ");
  Serial.print(predicted);
  Serial.print("\t ");
  Serial.print(abs(y-predicted)*100/y);
  Serial.print("%\n");
  delay(500);
}

运行如下

使用最新版本

如果需要使用最新版本需要使用ESP32S3模块

还需要安装依赖库

编写arduino文件

/**
 * Run a TensorFlow model to predict the IRIS dataset
 * For a complete guide, visit
 * https://eloquentarduino.com/tensorflow-lite-esp32
 */
// replace with your own model
// include BEFORE <eloquent_tinyml.h>!
#include "sine_model.h"
// include the runtime specific for your board
// either tflm_esp32 or tflm_cortexm
#include <tflm_esp32.h>
// now you can include the eloquent tinyml wrapper
#include <eloquent_tinyml.h>

// this is trial-and-error process
// when developing a new model, start with a high value
// (e.g. 10000), then decrease until the model stops
// working as expected
#define ARENA_SIZE 2000
#define TF_NUM_OPS 2

Eloquent::TF::Sequential<TF_NUM_OPS, ARENA_SIZE> tf;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(3000);
  Serial.println("__TENSORFLOW IRIS__");

  // configure input/output
  // (not mandatory if you generated the .h model
  // using the everywhereml Python package)
  tf.setNumInputs(1);
  tf.setNumOutputs(1);
  // add required ops
  // (not mandatory if you generated the .h model
  // using the everywhereml Python package)
  // tf.resolver.AddFullyConnected();
  // tf.resolver.AddSoftmax();

  while (!tf.begin(sine_model).isOk())
    Serial.println(tf.exception.toString());
}

void loop() {
  // pick up a random x and predict its sine
  float x = 3.14 * random(100) / 100;
  float y = sin(x);
  float input[1] = { x };

  Serial.print("sin(");
  Serial.print(x);
  Serial.print(") = ");
  Serial.print(y);
  if (!tf.predict(input).isOk()) {
    Serial.println(tf.exception.toString());
    return;
  }
  Serial.print("\t predicted: ");
  Serial.println(tf.classification);
  // 此处会取出运算结果中最大值,如果需要取出所有值,可以获取tf.outputs,这个是一个float*

  // how long does it take to run a single prediction?
  Serial.print("It takes ");
  Serial.print(tf.benchmark.microseconds());
  Serial.println("us for a single prediction");
  delay(100);
}

缺少运行截图,后续s3板子好后补充

进阶

下面对训练模型算法进行修正,让预测更加准确

对训练集、验证集和测试集画图

    # split into train, validation, test
    TRAIN_SPLIT = int(0.6 * SAMPLES)
    TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT)
    x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT])
    y_train, y_test, y_validate = np.split(y_values, [TRAIN_SPLIT, TEST_SPLIT])

    plt.plot(x_train, y_train, 'b.', label="Train")
    plt.plot(x_validate, y_validate, 'y.', label="Validate")
    plt.plot(x_test, y_test, 'r.', label="Test")
    plt.legend()
    plt.show()

在训练过程中,模型的性能不断地根据我们的训练数据和我们早先留出的验证数据进行测量。训练产生一个数据日志,告诉我们模型的性能在训练过程中是如何变化的。

    history_1 = model.fit(x_train,
              y_train,
              epochs=200,
              batch_size=16,
              validation_data=(x_validate, y_validate))
    
    # Draw a graph of the loss, which is the distance between
    # the predicted and actual values during training and validation.
    loss = history_1.history['loss']
    val_loss = history_1.history['val_loss']

    epochs = range(1, len(loss) + 1)

    plt.plot(epochs, loss, 'g.', label='Training loss')
    plt.plot(epochs, val_loss, 'b', label='Validation loss')
    plt.title('Training and validation loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    plt.show()

针对局部观察

    # Exclude the first few epochs so the graph is easier to read
    SKIP = 100

    plt.plot(epochs[SKIP:], loss[SKIP:], 'g.', label='Training loss')
    plt.plot(epochs[SKIP:], val_loss[SKIP:], 'b.', label='Validation loss')
    plt.title('Training and validation loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    plt.show()

下面我们将绘制平均绝对误差MAE,这是另一种衡量网络预测距离实际数字有多远的方法:

    # Draw a graph of mean absolute error, which is another way of
    # measuring the amount of error in the prediction.
    mae = history_1.history['mae']
    val_mae = history_1.history['val_mae']

    plt.plot(epochs[SKIP:], mae[SKIP:], 'g.', label='Training MAE')
    plt.plot(epochs[SKIP:], val_mae[SKIP:], 'b.', label='Validation MAE')
    plt.title('Training and validation mean absolute error')
    plt.xlabel('Epochs')
    plt.ylabel('MAE')
    plt.legend()
    plt.show()

可以看出,200次的训练最终MAE误差大约在30%左右。

我们画出拟合曲线看看:

    # Use the model to make predictions from our validation data
    predictions = model.predict(x_train)

    # Plot the predictions along with to the test data
    plt.clf()
    plt.title('Training data predicted vs actual values')
    plt.plot(x_test, y_test, 'b.', label='Actual')
    plt.plot(x_train, predictions, 'r.', label='Predicted')
    plt.legend()
    plt.show()

当我们把拟合次数改为1000后

可以看出,1000次的训练最终MAE误差大约在11%以下。

我们画出拟合曲线看看:

这张图清楚地表明,我们的网络已经学会了以一种非常有限的方式近似正弦函数。这些预测是高度线性的,只能非常粗略地符合数据。这种拟合的刚性表明,该模型没有足够的能力学习正弦波函数的全部复杂性,所以它只能以一种过于简单的方式近似它。把我们的模型做大,我们就能提高它的性能。

我们也可以将神经网络层数减少为两层试一下

    # create a NN with 2 layers of 16 neurons
    model = tf.keras.Sequential()
    model.add(layers.Dense(16, activation='relu', input_shape=(1, )))
    model.add(layers.Dense(1))

数据图会差很多

使用TensorFlowLite_ESP32库

安装库

需要使用ESP32-S3目标模块,并且修改代码TensorFlowLite_ESP32\src\tensorflow\lite\micro\compatibility.h

#ifdef TF_LITE_STATIC_MEMORY
#define TF_LITE_REMOVE_VIRTUAL_DELETE \
  // void operator delete(void* p) {}
#else
#define TF_LITE_REMOVE_VIRTUAL_DELETE
#endif

#endif  // TENSORFLOW_LITE_MICRO_COMPATIBILITY_H_

ino代码

#include <TensorFlowLite_ESP32.h>
#include <tensorflow/lite/micro/all_ops_resolver.h>

#include "sine_model.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/system_setup.h"
#include "tensorflow/lite/schema/schema_generated.h"

// This is a small number so that it's easy to read the logs
const int kInferencesPerCycle = 20;

// 定义了一个常量 kXrange,其值为 2 * π,用于表示模型训练时使用的 x 值的范围。这里用 2 * π 来近似 π
// 值,以避免引入额外的库。
const float kXrange = 2.f * 3.14159265359f;

// Globals, used for compatibility with Arduino-style sketches.
namespace
{
tflite::ErrorReporter* error_reporter = nullptr;
const tflite::Model* model = nullptr;
tflite::MicroInterpreter* interpreter = nullptr;
TfLiteTensor* input = nullptr;
TfLiteTensor* output = nullptr;
int inference_count = 0;

constexpr int kTensorArenaSize = 2000;
uint8_t tensor_arena[kTensorArenaSize];
}  // namespace

void setup()
{
    // 用于设置日志记录的。
    // NOLINTNEXTLINE(runtime-global-variables)是一个编译器指令,用于告诉Lint工具(如cpplint)不要警告全局变量的使用。
    // NOLINTNEXTLINE(runtime-global-variables)
    static tflite::MicroErrorReporter micro_error_reporter;
    error_reporter = &micro_error_reporter;

    // 用于将模型(sine_model)映射到一个可用的数据结构中。这是一个非常轻量级的操作,不涉及复制或解析。
    model = tflite::GetModel(sine_model);
    if (model->version() != TFLITE_SCHEMA_VERSION) {
        TF_LITE_REPORT_ERROR(error_reporter,
                             "Model provided is schema version %d not equal "
                             "to supported version %d.",
                             model->version(), TFLITE_SCHEMA_VERSION);
        return;
    }

    // 用于解析TensorFlow Lite操作实现。
    // NOLINTNEXTLINE(runtime-global-variables)
    static tflite::AllOpsResolver resolver;

    // 用于构建一个TensorFlow Lite模型解释器
    static tflite::MicroInterpreter static_interpreter(model, resolver, tensor_arena, kTensorArenaSize, error_reporter);
    interpreter = &static_interpreter;

    // 用于获取模型(interpreter)的输入和输出张量(tensors)的指针。
    // 这里使用了interpreter->input(0)和interpreter->output(0)来获取输入和输出张量,其中0表示张量的索引。
    input = interpreter->input(0);
    output = interpreter->output(0);

    // Keep track of how many inferences we have performed.
    inference_count = 0;
}

void loop()
{
    // 计算要输入到模型中的 x 值。我们将当前inference_count与每个周期的推理次数进行比较,以确定我们在模型训练的可能 x
    // 值范围内的位置,并使用它来计算值。
    float position = static_cast<float>(inference_count) / static_cast<float>(kInferencesPerCycle);
    float x = position * kXrange;

    // 通过除以输入张量的比例因子(scale)并加上零点(zero_point)将浮点数x转换为整数x_quantized。
    int8_t x_quantized = x / input->params.scale + input->params.zero_point;
    // 将x_quantized放入模型的输入张量input->data.int8[0]中
    input->data.int8[0] = x_quantized;

    // 执行模型推理, and report any error
    TfLiteStatus invoke_status = interpreter->Invoke();
    if (invoke_status != kTfLiteOk) {
        TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed on x: %f\n", static_cast<double>(x));
        return;
    }

    // 从模型输出的张量中获取量化后的输出值,存储在变量 y_quantized 中。
    int8_t y_quantized = output->data.int8[0];
    // 将 y_quantized 从整数转换为浮点数,并将结果存储在变量 y
    // 中。这里需要减去输出张量的零点(output->params.zero_point)并乘以输出张量的缩放因子(output->params.scale)。
    float y = (y_quantized - output->params.zero_point) * output->params.scale;

    // Output the results. A custom HandleOutput function can be implemented
    // for each supported hardware target.
    // Log the current X and Y values
    TF_LITE_REPORT_ERROR(error_reporter, "x_value: %f, y_value: %f\n", static_cast<double>(x), static_cast<double>(y));

    // Increment the inference_counter, and reset it if we have reached
    // the total number per cycle
    inference_count += 1;
    if (inference_count >= kInferencesPerCycle) inference_count = 0;
}

缺少运行截图,后续s3板子好后补充

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2057654.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Studio修改默认.m2与Gradle user home缓存位置

Android Studio修改默认.m2与Gradle user home缓存位置 1、修改Gradle user home的方法&#xff1a; android studio配置默认.gradle路径_android studio gradle在哪-CSDN博客文章浏览阅读2k次。当android studio新建一个项目时候&#xff0c;默认的.gradle路径均认为是在c盘的…

若依搭建实践

若依要求版本 JDK>1.8 MySQL>5.7 Maven>3.0 Node >12 Redis >3 一、环境下载及安装 我本地JDK1.8 MySQL9.0.1 Maven3.5.4 Node 20.12.2 Redis 5.0.14.1 在若依官网下载需要的版本&#xff0c;目前若依支持版本有四个&#xff0c;我们根据需要选择对应的版本…

集合及数据结构第五节————ArrayList的介绍和应用

系列文章目录 集合及数据结构第五节————ArrayList的介绍和应用 ArrayList的介绍和应用 什么是ArrayLisArrayList使用简单的洗牌算法杨辉三角 文章目录 系列文章目录集合及数据结构第五节————ArrayList的介绍和应用 ArrayList的介绍和应用 一、ArrayList1.什么是Arra…

鸿蒙 使用 expandSafeArea 实现顶部沉浸式导航

1&#xff0c; 先看效果&#xff1b; // 设置顶部绘制延伸到状态栏.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP]) 2&#xff0c; 直接cv 粘贴代码 // xxx.ets Entry Component struct Example {build() {Column() {Row() {Text().fontSize(40).textAlign(Text…

2_foc转速环_电磁转矩方程与运动方程的传递函数

转速环里面包含有电流环在内&#xff0c;当外面给定转速时&#xff0c;系统通过控制iq来控制电磁转矩&#xff0c;从而电机开始转动。电机在转动的过程&#xff0c;传感器检测到机械角度会改变&#xff0c;也就是说电角度也会改变&#xff0c;由电角度在单位时间的变化&#xf…

【C++ 第十五章】map 和 set 的封装(封装红黑树)

1. map 和 set 的介绍 ⭐map 与 set 分别是STL中的两种序列式容器; 它们是一种树形数据结构的容器&#xff0c;且其的底层构造为一棵红黑树; 而在上一篇文章中提到,其实红黑树本身就是一棵二叉搜索树,是基于二叉搜索树的性质对其增加了平衡的属性来提高其综合性能 ⭐当然也…

ip地址冲突的原因及其解决方法是什么

在当今的信息化时代&#xff0c;网络已成为我们生活和工作中不可或缺的一部分。然而&#xff0c;随着网络设备数量的不断增加&#xff0c;网络管理中的问题也日益凸显&#xff0c;其中IP地址冲突便是常见问题之一。IP地址冲突不仅会导致网络通信不稳定&#xff0c;甚至可能使设…

详解栈和队列

目录&#xff1a; 1.栈 2.队列 一、 栈&#xff08;Stack&#xff09; 1.1 概念&#xff1a; 栈是一种特殊的线性表&#xff0c;只允许在固定的一端进行插入和删除元素的操作。进行插入元素的一端叫做栈顶&#xff0c;另一端叫做栈底。从数据结构的角度出发&#xff0c;栈中…

k8s-deployment控制器

k8s-deployment控制器 1、yaml文件标签学习-指定pod在哪台节点上创建 强制指定pod在指定节点上创建&#xff1a; --- apiVersion: apps/v1 kind: Deployment metadata:name: my-deployment spec:replicas: 3selector:matchLabels:app: web1template:metadata:labels:app: web1…

流媒体服务器二 3学习 librtmp 库的配置使用

librtmp 库是个啥&#xff1f; librtmp是一个开源的基于C语言的库&#xff0c;提供了一个连接RTMP服务器&#xff0c;发送和接收RTMP流的API。 它可以用来开发流媒体播放器&#xff0c;网络直播等应用。它的主要特点是快速、稳定和低延迟。 librtmp支持RTMP&#xff0c;RTMPS…

超越 RAG 基础:AI 应用的高级策略

作者&#xff1a;来自 Elastic Platform Team 我们最近与 Cohere 举办的线上活动深入探讨了检索增强生成 (Retrieval Augmented Genereation - RAG) 的世界&#xff0c;重点讨论了在概念验证阶段之后构建 RAG 应用程序的关键注意事项。我们的演讲者是 Elastic 的首席解决方案架…

使用 Python和 FFmpeg 批量截图视频到各自文件夹中

在这篇博客中&#xff0c;我们将创建一个简单的图形用户界面 (GUI) 工具&#xff0c;利用 wxPython 和 FFmpeg 来从视频文件中批量生成截图。这个工具能够让用户选择一个文件夹&#xff0c;遍历其中的所有视频文件&#xff0c;按照视频长度将其分为四等分&#xff0c;然后为每个…

鸿蒙HarmonyOS之使用ArkTs语言实现自定义Tab菜单栏分页主页面UI

一、效果 显示为3个Tab菜单栏&#xff0c;中间可以滑动 二、实现步骤 1、Index.ets 示例代码中用到的颜色、文字、图片等资源可以自行替换 import { Tab_1 } from ./Tab_1; import { Tab_2 } from ./Tab_2; import { Tab_3 } from ./Tab_3;//主页面 Entry Component stru…

Excel数字中间指定位置插入符号——以120120加*为例

设置单元格格式——自定义 更多阅读Excel数字中间指定位置插入符号_哔哩哔哩_bilibili

B码对时案例分享,基于RK3568J+Logos-2,让电力设备轻松实现“高精度授时”!

本文主要介绍瑞芯微RK3568J紫光同创Logos-2的B码对时案例&#xff0c;开发环境如下&#xff1a; Windows开发环境&#xff1a;Windows 7 64bit、Windows 10 64bit Pango Design Suite(PDS)&#xff1a;PDS_2022.2-SP3 IRIG-B码对时典型应用 IRIG-B码对时可应用于继电保护装…

台球厅自动控制无人台球之智能开关-SAAS本地化及未来之窗行业应用跨平台架构

一、无人台球厅优点 1. 降低人力成本&#xff1a;无需雇佣大量员工&#xff0c;如收银员、服务员等&#xff0c;节省了工资、福利和管理成本。 2. 24 小时营业&#xff1a;不受人工营业时间限制&#xff0c;可以随时满足顾客的需求&#xff0c;增加营业时长和收入。 3. 便捷高…

不良图片检测

OpenNSFW模型简介 OpenNSFW是一个由 Yahoo 研究院开源的深度学习模型&#xff0c;用于识别和区分网络上的正常内容与不适宜内容&#xff08;Not Safe For Work&#xff09;。 项目地址&#xff1a;https://github.com/yahoo/open_nsfw OpenNSFW主要基于Caffe框架实现&#x…

邮票孔拼版制作方法

邮票孔拼版制作方法 拼版后的局部图:(中间用连接桥的方式&#xff0c;此方式能最少程度上减少残留) 2&#xff09;拼版后的效果图 3&#xff09;邮票孔拼版规则: 拼板与板间距1.2MM或者1.6MM 等邮票孔&#xff1a;8个0.55MM的孔,孔间距0.2MM加两排&#xff0c;邮票孔伸到…

KubeSphere核心实战_kubesphere部署redis01_为redis指定配置文件_指定存储卷_配置服务---分布式云原生部署架构搭建047

然后我们再来,部署一下redis,可以看到,首先去容器官网去找到对应的redis的镜像然后 可以看到镜像中都有说的,如何启动,以及 --appendonly yes 是指定持久化.然后 /data表示数据存储的位置. 可以看到数据存储位置 然后还有配置文件的位置. 可以看到,我们首先去创建配置文件,然后…

【专题】2024全数驱动 致胜未来-数字化敏捷银行白皮书报告合集PDF分享(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p37404 政策明确发展使命&#xff0c;新时代商业银行应坚持党建引领&#xff0c;秉持高质量发展理念。数字经济已成大势&#xff0c;商业银行需构建数字基础设施能力&#xff0c;强化顶层战略规划。当前商业银行数字化发展面临诸多挑…