ESP32开发进阶: 训练神经网络
一、网络设定
我们设定一个简单的前馈神经网络,其结构如下:
-
输入层:节点数:2,接收输入数据,每个输入样本包含2个特征,例如
{1.0, 0.0},{0.0, 1.0}等。 -
隐藏层:节点数:2,处理和提取输入数据的特征,
-
激活函数:使用 Sigmoid 激活函数
sigmoid(x) = 1 / (1 + exp(-x)) -
权重矩阵(Weights from Input to Hidden):
float weights_input_hidden[INPUT_NODES][HIDDEN_NODES] = {{0.15, 0.25},{0.20, 0.30} };这是一个 2x2 的权重矩阵,用于连接输入层和隐藏层。
-
偏置(Biases for Hidden Layer):
float bias_hidden[HIDDEN_NODES] = {0.35, 0.35};这是一个包含2个偏置值的数组,分别对应每个隐藏层节点。
-
每个隐藏层节点计算如下:
其中
是输入节点值,
是权重,
是偏置。
-
-
输出层:节点数:1,给出最终的预测结果。
-
激活函数:使用 Sigmoid 激活函数
-
权重矩阵:
float weights_hidden_output[HIDDEN_NODES][OUTPUT_NODES] = {{0.40},{0.50} };这是一个 2x1 的权重矩阵,用于连接隐藏层和输出层。
-
偏置:
这是一个包含1个偏置值的数组,分别对应每个隐藏层节点。float bias_output[OUTPUT_NODES] = {0.60}; - 输出层节点计算如下:
其中
是隐藏层节点值,
是权重,
是偏置。
-
整体网络设定如下图所示:

二、Arduino端代码
首先,是初始化部分(权重和偏置的定义)
float weights_input_hidden[INPUT_NODES][HIDDEN_NODES] = {{0.15, 0.25},{0.20, 0.30}
};float weights_hidden_output[HIDDEN_NODES][OUTPUT_NODES] = {{0.40},{0.50}
};float bias_hidden[HIDDEN_NODES] = {0.35, 0.35};
float bias_output[OUTPUT_NODES] = {0.60};
接着,前向传播通过计算每一层的加权输入和激活输出来推断输入样本的预测值。
void forward_propagation(float input[]) {for (int i = 0; i < INPUT_NODES; i++) {input_layer[i] = input[i];}for (int j = 0; j < HIDDEN_NODES; j++) {hidden_layer[j] = 0;for (int i = 0; i < INPUT_NODES; i++) {hidden_layer[j] += input_layer[i] * weights_input_hidden[i][j];}hidden_layer[j] += bias_hidden[j];hidden_layer[j] = sigmoid(hidden_layer[j]);}for (int k = 0; k < OUTPUT_NODES; k++) {output_layer[k] = 0;for (int j = 0; j < HIDDEN_NODES; j++) {output_layer[k] += hidden_layer[j] * weights_hidden_output[j][k];}output_layer[k] += bias_output[k];output_layer[k] = sigmoid(output_layer[k]);}
}
最后,反向传播通过计算误差并根据误差调整权重和偏置,以最小化损失函数。
void backward_propagation(float input[], float target) {float output_error = target - output_layer[0];float output_delta = output_error * sigmoid_derivative(output_layer[0]);float hidden_error[HIDDEN_NODES];float hidden_delta[HIDDEN_NODES];for (int j = 0; j < HIDDEN_NODES; j++) {hidden_error[j] = output_delta * weights_hidden_output[j][0];hidden_delta[j] = hidden_error[j] * sigmoid_derivative(hidden_layer[j]);}for (int j = 0; j < HIDDEN_NODES; j++) {weights_hidden_output[j][0] += learning_rate * output_delta * hidden_layer[j];}bias_output[0] += learning_rate * output_delta;for (int i = 0; i < INPUT_NODES; i++) {for (int j = 0; j < HIDDEN_NODES; j++) {weights_input_hidden[i][j] += learning_rate * hidden_delta[j] * input_layer[i];}}for (int j = 0; j < HIDDEN_NODES; j++) {bias_hidden[j] += learning_rate * hidden_delta[j];}
}
定义四组输入样本及其目标输出,用于训练神经网络, 通过多次迭代训练神经网络,并在每次训练后输出当前的权重和F1-score。完整代码如下:
#include <Arduino.h>
#include <cmath>// 定义神经网络结构
#define INPUT_NODES 2
#define HIDDEN_NODES 2
#define OUTPUT_NODES 1// 定义神经网络参数
float input_layer[INPUT_NODES];
float hidden_layer[HIDDEN_NODES];
float output_layer[OUTPUT_NODES];float weights_input_hidden[INPUT_NODES][HIDDEN_NODES] = {{0.15, 0.25},{0.20, 0.30}
};float weights_hidden_output[HIDDEN_NODES][OUTPUT_NODES] = {{0.40},{0.50}
};float bias_hidden[HIDDEN_NODES] = {0.35, 0.35};
float bias_output[OUTPUT_NODES] = {0.60};float learning_rate = 0.1;// 激活函数和其导数(sigmoid)
float sigmoid(float x) {return 1.0 / (1.0 + exp(-x));
}float sigmoid_derivative(float x) {return x * (1.0 - x);
}// 计算预测值
void forward_propagation(float input[]) {for (int i = 0; i < INPUT_NODES; i++) {input_layer[i] = input[i];}for (int j = 0; j < HIDDEN_NODES; j++) {hidden_layer[j] = 0;for (int i = 0; i < INPUT_NODES; i++) {hidden_layer[j] += input_layer[i] * weights_input_hidden[i][j];}hidden_layer[j] += bias_hidden[j];hidden_layer[j] = sigmoid(hidden_layer[j]);}for (int k = 0; k < OUTPUT_NODES; k++) {output_layer[k] = 0;for (int j = 0; j < HIDDEN_NODES; j++) {output_layer[k] += hidden_layer[j] * weights_hidden_output[j][k];}output_layer[k] += bias_output[k];output_layer[k] = sigmoid(output_layer[k]);}
}// 更新权重和偏置
void backward_propagation(float input[], float target) {float output_error = target - output_layer[0];float output_delta = output_error * sigmoid_derivative(output_layer[0]);float hidden_error[HIDDEN_NODES];float hidden_delta[HIDDEN_NODES];for (int j = 0; j < HIDDEN_NODES; j++) {hidden_error[j] = output_delta * weights_hidden_output[j][0];hidden_delta[j] = hidden_error[j] * sigmoid_derivative(hidden_layer[j]);}for (int j = 0; j < HIDDEN_NODES; j++) {weights_hidden_output[j][0] += learning_rate * output_delta * hidden_layer[j];}bias_output[0] += learning_rate * output_delta;for (int i = 0; i < INPUT_NODES; i++) {for (int j = 0; j < HIDDEN_NODES; j++) {weights_input_hidden[i][j] += learning_rate * hidden_delta[j] * input_layer[i];}}for (int j = 0; j < HIDDEN_NODES; j++) {bias_hidden[j] += learning_rate * hidden_delta[j];}
}// 计算F1-score
float compute_f1_score(float tp, float fp, float fn) {float precision = tp / (tp + fp);float recall = tp / (tp + fn);return 2 * (precision * recall) / (precision + recall);
}void print_weights() {Serial.println("Weights Input-Hidden:");for (int i = 0; i < INPUT_NODES; i++) {for (int j = 0; j < HIDDEN_NODES; j++) {Serial.printf("w[%d][%d] = %f ", i, j, weights_input_hidden[i][j]);}Serial.println();}Serial.println("Weights Hidden-Output:");for (int j = 0; j < HIDDEN_NODES; j++) {Serial.printf("w[%d][0] = %f ", j, weights_hidden_output[j][0]);}Serial.println();
}void setup() {// 初始化串口Serial.begin(115200);while (!Serial) {}// 打印欢迎信息Serial.println("Hello, ESP32 Neural Network with Training!");
}void loop() {// 输入样本和目标float input[][INPUT_NODES] = {{1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}, {0.0, 0.0}};float target[] = {1.0, 1.0, 0.0, 0.0};// 初始化统计量float tp = 0, fp = 0, fn = 0;// 训练for (int epoch = 0; epoch < 1000; epoch++) {for (int i = 0; i < 4; i++) {forward_propagation(input[i]);backward_propagation(input[i], target[i]);// 更新统计量float prediction = output_layer[0] > 0.5 ? 1.0 : 0.0;if (prediction == 1.0 && target[i] == 1.0) {tp++;} else if (prediction == 1.0 && target[i] == 0.0) {fp++;} else if (prediction == 0.0 && target[i] == 1.0) {fn++;}}// 打印权重和F1-scoreSerial.printf("Epoch %d\n", epoch);print_weights();float f1_score = compute_f1_score(tp, fp, fn);Serial.printf("F1-Score: %f\n", f1_score);// 重置统计量tp = 0;fp = 0;fn = 0;// 延迟一段时间delay(100);}// 停止程序while (true) {}
}
部分打印结果如下:

相关文章:
ESP32开发进阶: 训练神经网络
一、网络设定 我们设定一个简单的前馈神经网络,其结构如下: 输入层:节点数:2,接收输入数据,每个输入样本包含2个特征,例如 {1.0, 0.0}, {0.0, 1.0} 等。 隐藏层:节点数:…...
全国区块链职业技能大赛国赛考题前端功能开发
任务3-1:区块链应用前端功能开发 1.请基于前端系统的开发模板,在登录组件login.js、组件管理文件components.js中添加对应的逻辑代码,实现对前端的角色选择功能,并测试功能完整性,示例页面如下: 具体要求如下: (1)有明确的提示,提示用户选择角色; (2)用户可看…...
直接插入排序算法详解
直接插入排序(Straight Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排…...
sql手动自增id
有时候在运维处理数据的时候,需要给某张表插入新的记录,那么需要知道最新插入数据的id,并在最新id的基础上加上id增长步长获取新的id,这个过程往往需要现将max出来加1,再手动补充到sql语句中,很麻烦,而且数据多的时候容易出错。有…...
10_TypeScript中的泛型
TypeScript中的泛型) 一、泛型的定义二、泛型函数三、泛型类:比如有个最小堆算法,需要同时支持返回数字和字符串两种类型。通过类的泛型来实现四、泛型接口五、泛型类 --扩展 把类作为参数类型的泛型类1、实现:定义一个 User 的类…...
Unity3D之TextMeshPro使用
文章目录 1. TextMeshPro简介2. TextMeshPro创建3. TextMeshPro脚本中调用4. TextMeshPro字体设置及中文支持过程中出现的一些问题 1. TextMeshPro简介 【官网文档】https://docs.unity.cn/cn/2020.3/Manual/com.unity.textmeshpro.html TextMeshPro 是 Unity 的最终文本解决…...
K8S 上部署 Prometheus + Grafana
文章目录 一、使用 Helm 安装 Prometheus1. 配置源2. 下载 prometheus 包3. 安装 prometheus4. 卸载 二、使用 Helm 安装 Grafana1. 配置源2. 安装 grafana3. 访问4. 卸载 一、使用 Helm 安装 Prometheus 1. 配置源 地址:https://artifacthub.io/packages/helm/pro…...
雷军的逆天改命与顺势而为
雷军年度演讲前,朋友李翔提了一个问题:雷军造车是属于顺势而为还是逆势而为?评论互动区有一个总结,很有意思,叫“顺势逆袭”。 大致意思是产业趋势下小米从手机到IOT再切入汽车,是战略的必然,不…...
Leetcode 11. 盛最多水的容器
Leetcode 11. 盛最多水的容器 Leetcode 11. 盛最多水的容器 一、题目描述二、我的想法 一、题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成…...
Java笔试分享
1、设计模式(写>3种常用的设计模式) 设计模式是在软件工程中解决常见问题的经验性解决方案。以下是一些常用的设计模式: 单例模式(Singleton): 意图:确保一个类只有一个实例,并…...
LeetCode:对称的二叉树(C语言)
1、问题概述:给一个二叉树,看是否按轴对称 2、示例 示例 1: 输入:root [1,2,2,3,4,4,3] 输出:true 示例 2: 输入:root [1,2,2,null,3,null,3] 输出:false 3、分析 (1&a…...
Postman中的API Schema验证:确保响应精准无误
Postman中的API Schema验证:确保响应精准无误 在API开发和测试过程中,验证响应数据的准确性和一致性是至关重要的。Postman提供了一个强大的功能——API Schema验证,它允许开发者根据预定义的JSON Schema来检查API响应。本文将详细介绍如何在…...
深入浅出WebRTC—GCC
GoogCcNetworkController 是 GCC 的控制中心,它由 RtpTransportControllerSend 通过定时器和 TransportFeedback 来驱动。GoogCcNetworkController 不断更新内部各个组件的状态,并协调组件之间相互配合,向外输出目标码率等重要参数࿰…...
leetcode日记(49)旋转链表
其实不难,就是根据kk%len判断需要旋转的位置,再将后半段接在前半段前面就行。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : …...
InteliJ IDEA最新2024版下载安装与快速配置激活使用教程+jdk下载配置
第一步:下载ideaIC-2024.1.4 方法1:在线链接 IntelliJ IDEA – the Leading Java and Kotlin IDE (jetbrains.com) 选择社区版进行下载 方法2:百度网盘 链接:https://pan.baidu.com/s/1ydS6krUX6eE_AdW4uGV_6w?pwdsbfm 提取…...
【23】Android高级知识之Window(四) - ThreadedRenderer
一、概述 在上一篇文章中已经讲了setView整个流程中,最开始的addToDisplay和WMS跨进程通信的整个过程做了什么。继文章Android基础知识之Window(二),这算是另外一个分支了,接着讲分析在performTraversals的三个操作中,最后触发pe…...
Java-根据前缀-日期-数字-生成流水号(不重复)
🎈边走、边悟🎈迟早会好 小伙伴们在日常开发时可能会遇到的业务-生成流水号,在企业中可以说是比较常见的需求, 可以采用"前缀日期数字"的方式(ps:此方式是需要用到缓存的)前缀:为了…...
跟李沐学AI:卷积层
从全连接层到卷积 多层感知机十分适合处理表格数据,其中行对应样本,列对应特征。但对于图片等数据,全连接层会导致参数过多。卷积神经网络(convolutional neural networks,CNN)是机器学习利用自然图像中一…...
使用RedisTemplate操作executePipelined
前言 RedisTemplate 是 Spring 提供的用于操作 Redis 的模板类,它封装了 Redis 的连接、连接池等管理,并提供了一系列的操作方法来简化 Redis 的使用。其中,executePipelined 方法是 RedisTemplate 中的一个高级特性,用于支持 Re…...
react-native从入门到实战系列教程一环境安装篇
充分阅读官网的环境配置指南,严格按照他的指导作业,不然你一直只能在web或沙箱环境下玩玩 极快的网络和科学上网,必备其中的一个较好的心理忍受能力,因为上面一点就可以让你放弃坚持不懈,努力尝试 成功效果 三大件 …...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
