Arduino前馈反向传播神经网络

2023-11-05

本文介绍了为Arduino Uno微控制器板开发的人工神经网络。 这里描述的网络是前馈反向传播网络,可能是最常见的类型。 它被认为是有监督或无监督学习的良好通用网络。 该项目的代码以Arduino Sketch的形式提供。 它是即插即用的-您可以将其上传到Uno并运行它,并且有一部分配置信息可用于快速构建和训练自定义网络。 这里提供的文章概述了人工神经网络,Sketch的详细信息,并介绍了前馈网络和反向传播算法中使用的一些基本概念。

自1980年代中期以来,一直在使用反向传播神经网络。 反向传播的基本概念非常简单,尽管算法本身包含一些高阶数学,但不必完全了解如何导出方程式即可应用它们。 在非常小的系统上实现网络存在一些挑战,而在较早的廉价微控制器和业余爱好者主板上,这些挑战是巨大的。 但是,Arduinos和今天的许多开发板一样,实际上使这项工作非常短。 此处使用的Arduino Uno基于Atmel的ATmega328微控制器。 它的2K SRAM足以用于具有7个输入和4个输出的示例网络,并且由于Arduino的GCC语言支持多维数组和浮点数学运算,因此编程工作非常容易管理。

神经网络已用于从自动驾驶汽车控制到游戏,面部识别到股票市场分析的各种应用中。 大多数应用程序将涉及某种类型的模式匹配,在这种情况下,系统的确切输入将是未知的,并且可能缺少或多余的信息。 考虑识别手写字符的问题。 字母表的一般形状可以提前知道,但是实际输入将始终变化。 当然,在ATmega328上建立的小型网络并不能完全满足面部识别的任务,但是可以掌握很多的机器人控制和机器学习实验。

顾名思义,人工神经网络(通常缩写为ANN)是受自然启发的计算模型。 这是在某种程度上模仿大脑存储信息并对各种输入做出反应的方式的尝试。 在自然界中,神经系统的基本组成部分是一种特殊类型的细胞,称为神经元。

将神经元可视为一个微小的电化学开关可能会很方便,它在受刺激时会打开。 神经元在广阔的网络中相互连接。 当神经元被刺激激发并变得活跃时,它会沿着该网络发送少量电荷,进而使网络中的其他神经元变得活跃。 一个神经元将有多个神经元馈入其中,并且这些连接的强度也会变化。 如果输入有很强的联系,它将提供很多刺激。 较弱的连接将提供较少的连接。 在非常真实的意义上,神经元可以被认为是将所有这些具有不同强度的输入相加,并基于总和产生输出。

在基于软件的人工神经网络中,神经元及其连接被构建为数学关系。 当软件具有输入模式时,它将通过网络馈入该模式,系统地将输入添加到每个神经元,计算该神经元的输出,并使用该输出将适当的输入馈送到网络中的其他神经元。

确定神经元之间的连接强度(也称为权重)成为神经网络应用程序中的首要任务。 在反向传播算法中,最初使用随机权重初始化网络。 然后为网络提供输入和输出的训练集。 当输入通过系统馈送时,会将实际输出与所需输出进行比较,并计算误差。 然后,该错误会通过网络反馈,并且权重会根据学习算法进行增量调整。 在许多周期(通常是数千个周期)中,最终将对网络进行训练,并在出现输入时给出正确的输出。

在我们在此处构建的前馈网络中,神经元分为三层,分别称为输入层,隐藏层和输出层。 一层中的所有神经元都连接到下一层中的所有神经元。 下面是这种关系的经典图形表示。

隐藏层在前馈网络中起着至关重要的作用。 在早期的神经网络模型中,输入神经元直接连接到输出神经元,并且网络可以实现的解决方案范围极为有限。 两层模型无法解决的此类问题之一是“异或”或-通常表示为XOR的逻辑。 在布尔逻辑中,当两个输入中的任何一个为true时,XOR关系将为true,但是当两个输入均为true时,则为false。 XOR的真值表如下图所示。

通过在输入和输出之间增加一层,网络可以解决XOR等问题。 一些理论认为,在优化网络的其他条件的情况下,三层网络将能够解决任何真值表。 解决XOR是对新网络的良好测试。 您会在示例中看到它经常使用,并且它通常被称为神经网络的“ Hello World”程序。

本文随附的Sketch中实现的网络只是一个演示,实际上并没有执行任何实际功能。 Sketch包括一组训练输入和输出,并且将网络训练到该集合,直到达到预定的准确度为止。 此时,Sketch宣告胜利,然后重新开始。 在此过程中,培训结果会定期发送到使用Arduino IDE或任何其他终端程序的串行监视器进行监视的串行端口。 (请注意,使用Arduino IDE时,在加载Sketch后有必要在“工具”菜单中启动串行监视器。)

该程序的结构使得通过简单地在Sketch开始处更改配置部分中的值就可以非常快速地组装网络和训练集。 这种设置可以在不必了解所有潜在细微差别的情况下进行网络实验。 \

配置部分包括两个数据数组Input和Target,它们共同构成训练集的真值表。 就其价值而言,Sketch中设置的训练是一个真值表,该真值表将led数字显示(0-9)的七个段映射为二进制数(0000-1001)。 您可能会认为这是光学字符识别问题的基本表示。 如果研究数组,您会发现它们在输入和输出的映射中提供了丰富的组合,并为网络可以学习解决相当困难的问题提供了很好的概念证明。

要将网络修改为新的训练集,必须在输入和目标数组中输入适当的真值表值,并且还必须在配置部分中调整相应的参数以匹配新的真值表:

  • PatternCount:真值表中训练项目或行的数量
  • InputNodes:输入神经元的数量
  • OutputNodes:输出神经元的数量

您可以在本节中配置一些其他项目进行实验。

  • HiddenNodes:隐藏神经元的数量
  • LearningRate:调整实际反向传播该错误的数量
  • Momentum:调整前一次迭代的结果对当前迭代的影响程度
  • InitialWeightMax:设置重量的最大起始值
  • Success:网络解决了训练集的错误阈值

作为一个一般概念,HiddenNodes,LearningRate,Momentum和InitialWeightMax一起工作以优化网络以提高学习效率和速度,同时最大程度地减少神经网络设计中遇到的陷阱。

较低的LearningRate值会导致训练过程变慢,但会降低网络陷入振荡的可能性,在这种情况下,网络会持续过度解决训练问题的解决方案,并且永远不会达到成功阈值。 在我们的演示中,LearningRate设置为3.。 对于大型,非常复杂的网络(比我们在Arduino Uno上构建的网络大得多),该值通常设置得非常低-约为0.01。

Momentum通过将先前反向传播的一部分注入当前反向传播来平滑训练过程。 Momentum有助于防止网络收敛于一个好的但不是最好的解决方案的现象,也称为收敛于局部最小值。 Momentum值必须在0到1之间。

隐藏神经元的数量会影响训练网络的速度,网络可以解决的问题的复杂性,并有助于防止收敛于局部最小值。 您将需要至少具有与输出神经元一样多的隐藏神经元,并且可能需要更多。 大量隐藏神经元的缺点是需要存储大量权重。

初始随机权重应该相对较小。Sketch随附的配置中的InitialWeightMax值为.5。这会将所有初始权重设置为-0.5至.5之间,这是一个很好的起点。

这些参数的理想值根据训练数据的不同而有很大的不同,实际上没有直接的最佳选择方法。经验与反复试验相结合似乎是方法。

在配置部分中的最终值“成功”将在系统认为学习训练集时设置系统中错误的阈值级别。 它是一个非常小的数字,大于零。 这种类型的网络的本质是系统中的总误差将接近零,但从未真正达到零。

请注意,具有7个输入,8个隐藏的神经元和4个输出的示例网络大约可以在Arduino Uno的2K SRAM上运行。 不幸的是,如果您没有在Arduino上用完内存,则不会发出警告,Sketch的行为只会变得不稳定。 好消息是,SRAM分配大于2K的Arduino和Arduino兼容系统的列表一直在增长。 如果您成为一名成熟的神经网络实验者,您将有很多选择。

至此,我们已经为您提供了足够的基础,可以将示例网络的代码复制到您自己的计算机上,将其上传到Arduino,并尝试各种设置。

在配置部分之外,我们现在转到Sketch本身。 将神经网络实现为C程序的基本策略是建立一个数据数组框架,以保持权重并跟踪通过信号前馈和错误后馈累加的累加总数。 嵌套的FOR循环序列迭代这些数组,从而在执行反向传播算法时进行所需的各种计算。 数组,其他变量和常量的名称已与其网络中的功能相对应; 随着其余说明的进行,这些名称将变得更加清晰。

尽管代码不是绝对的初学者,但是如果您熟悉数组和循环的概念,那么您应该应该能够通读本文随附的Sketch并遵循逻辑。 这是一个高级细分:

  • 初始化数组。权重设置为随机数,并且两个保存反向传播中使用的更改值的其他数组设置为零。
  • 开始一个大循环,通过完整的训练数据集运行系统
  • 随机化训练集在每次迭代中运行的顺序,以减少局部最小值的振荡或收敛
  • 计算隐藏层激活,输出层激活和错误
  • 反向传播错误到隐藏层
  • 更新权重
  • 如果系统错误大于成功阈值,则运行训练数据的另一次迭代
  • 如果系统错误小于成功阈值,则中断,声明成功,然后将数据发送到串行终端
  • 每1000个周期将训练集的测试运行结果发送到串行终端

除了编程逻辑外,还有三个要理解的网络基本概念:激活函数,梯度适当和偏置。

激活函数根据馈入该神经元的加权连接的总和来计算该神经元的输出。 尽管有所不同,但此Sketch用了最常见的激活函数,称为“ Sigmoid函数”,这是因为其独特的形状,如下图所示。

该函数的关键特征是无论输入如何,输出都将落在0到1之间。此功能在编码神经网络时非常方便,因为神经元的输出始终可以表示为全开和全关l之间的范围。 激活函数可以在代码中采用通用形式的几个地方看到:

output = 1.0/(1.0 + exp(-Accum)) 

输出是表示要激活的神经元输出的数组变量,而Accum是该神经元加权输入的总数。 特定公式的复杂性并不重要,除了它们可以方便地产生S型输出。

梯度下降是反向传播的秘密武器。 这是一种数学方法,它使我们能够计算每个输出神经元的误差幅度,确定与该神经元的每个连接对误差的贡献程度,并对这些连接的权重进行增量调整,以系统地减少误差。

梯度下降的第一步是为每个神经元计算一个称为增量的值。 增量反映了误差的大小-神经元的目标值与其实际输出之间的差异越大,则增量越大。 在输出层,增量计算很简单:

delta = (target - actual) * actual * (1 - actual)

用代码中表示为

 OutputDelta[i] = (Target[p][i] - Output[i]) * Output[i] * (1.0 - Output[i]) ;

由于没有可测量的目标,因此计算隐藏层的增量将变得更加复杂。 取而代之的是,每个隐藏神经元的误差大小是根据权重与针对输出层计算的增量之间的关系得出的。 对于每个隐藏的神经元,代码逐步遍历所有输出连接,将权重乘以增量,并保持累加总数:

Accum += OutputWeights[i][j] * OutputDelta[j] ;

然后,通过将存储在Accum中的值替换为在第一次计算中看到的公式中的Target [p] [i]-Output [i]值来计算内层增量:

HiddenDelta[i] = Accum * Hidden[i] * (1.0 - Hidden[i]) ;

在计算出两层的增量值之后,下一步就是实际操作并调整权重。 在这里,我们看到学习率和动量的值如何改变权重的变化。 对于每个重量,要更改的量由以下公式确定:

change = (learning rate * weight * delta) + (momentum * previous change)

然后通过将旧权重添加到更改值中来找到新权重:

weight = weight + change

对于内层和隐藏层之间的权重,公式在代码中显示为:

ChangeHiddenWeights[j][i] = LearningRate * Input[p][j] * HiddenDelta[i] + Momentum * ChangeHiddenWeights[j][i];
  HiddenWeights[j][i] += ChangeHiddenWeights[j][i] ;

对于隐藏层和输出层之间的权重,公式在代码中显示为:

ChangeOutputWeights[j][i] = LearningRate * Hidden[j] * OutputDelta[i] + Momentum * ChangeOutputWeights[j][i] ;
  OutputWeights[j][i] += ChangeOutputWeights[j][i] ;

最后,我们开始偏差,这是一个相对简单的概念,但是当不理解它时,可能会使代码有些混乱。 输入层和隐藏层每个都包含一个始终触发的额外神经元(换句话说,它始终隐含激活为“ 1”)。 偏差值对网络有几个积极影响。 它增加了稳定性并扩展了可能的解决方案的数量。 最重要的是,它消除了所有输入均为零且因此没有信号通过网络传播的可能性。 如果查看包含权重和变化值的数组的声明,则会看到额外的神经元。 此外,您还将在处理激活和更新功能的嵌套循环中看到,不依赖输入值的偏向神经元有单独的计算。

详情参阅 - 亚图跨际

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Arduino前馈反向传播神经网络 的相关文章

  • Arduino Nano 上的 WiFi

    我无法找到的虚拟问题 我用来将 WiFi 802 11b g n 添加到 Raspberry Pi 的扩展板也可以在 Nano 上使用吗 换句话说 向 Arduino Nano 板添加 WiFi 有多容易 可行 Thanks Arduino
  • 如何在观察者中处理具有不同状态值类型的 Observables

    首先是上下文和问题 框架代码在帖子底部 我们正在创建并实现一个 C 框架 以便在 Arduino 等环境中使用 为此 我想使用观察者模式 其中任何对传感器状态变化感兴趣的组件 Observables 可以注册自己 并且它将通过 Observ
  • Arduino Python3 脚本

    我正在尝试使用 Python3 脚本来控制 Arduino Mega 这是一个简单的脚本 用于从键盘获取一行并通过 Arduino 回显它 我从一个可用的 Python 2 脚本开始http petrimaki wordpress com
  • printf 的包装

    我在Arduino下编码 我想开发串行打印格式化功能 所以我尝试使用sprintf未知大小的缓冲区 基本上 我们可以避免谈论 Arduino 及其串行输出 并考虑将文本写入缓冲区 然后使用printf 我试过这个 include
  • 读取串行端口 - 忽略在一定时间内写入串行端口的部分数据

    我想定期读取串行端口上的数据和Arduino 所以本质上是这样的 读一读 Wait 读一读 Wait Take etc 我面临的问题是端口将缓冲其信息 因此一旦我调用等待函数 串行端口上的数据就会开始缓冲 等待函数完成后 我尝试再次读取数据
  • Arduino I2S 正弦波

    我正在开发一个项目 我想通过组合不同的正弦波来生成 简单 声音 我使用的是 arduino mkrZero 因为它内置了 I2S 接口 并且似乎有足够的处理能力来满足我的需求 I have wired my system exactly l
  • Arduino:使用串口和软件串口与蓝牙模块

    我的目的是使用 Arduino 通过 HC 05 蓝牙模块在 PC 和 Android 设备之间建立通信 我使用 PC 和 Arduino 串行监视器 之间的 USB 通信以及 SoftwareSerial 来连接到 HC 05 我的问题是
  • Android BLE 扫描与 UUID 过滤器列表混淆

    我正在 Android 移动设备 A 上执行 UUID 过滤扫描 并且有两个测试用例 一个人在另一台 Android 设备 移动设备 b 上使用名为 BLE Scanner 的应用程序来创建具有两个测试服务 每个服务都有自己唯一的 UUID
  • Python自动选择串口(适用于Arduino)

    目前 Python 程序必须知道设备 Arduino 位于哪个端口 然后 Python 才能与设备进行通信 Problem 每当设备拔出并重新插入时 其 COM 端口都会发生变化 因此必须再次向 Python 提供正确的串行端口 以便它找到
  • Arduino从SD卡读取最后一行

    我对 Arduino 业务还很陌生 如何从 SD 卡读取最后一行 通过以下代码片段 我可以读取第一行 n 之前的所有字符 现在我想添加一个 向后 声明 或其他内容 到目前为止我的代码 include
  • 终止导致设备或资源繁忙的进程:“/dev/ttyUSB0”?

    我使用以下 Python 代码连接到我的 Arduino 板 device glob glob dev ttyUSB 0 time sleep 1 arduino serial Serial device 115200 timeout 5
  • C#读取Arduino

    我正在尝试制作一个从 Arduino 读取传出信号的应用程序 但我无法使其在 C 中工作Windows 窗体 http en wikipedia org wiki Windows Forms 仅在控制台中 我的 C Windows 窗体代码
  • Arduino:连接字符串时崩溃和错误

    我尝试将 AES 256 加密的输出连接到一个字符串 将此字符串与从 Android 手机发送的加密字符串进行比较 基本上 连接似乎有效 但在几次运行后会出现错误 不可读的字符 字符串变得更短而不是更长 或崩溃 它是可重现的 重启后在同一点
  • pySerial 与 python 2.7 和 3.4 的差异

    我正在开发一个项目 需要通过串口将一些数字从 Windows 10 中的 python 发送到 arduino uno 作为一个简单的测试 我只想通过发送 2 来打开 LED 并通过从命令提示符发送 4 来关闭 LED 尽管我希望最终能够将
  • 如何在 Windows 上获取 Arduino 草图的汇编语言列表?

    我希望能够看到我的 Arduino 草图的汇编语言列表 我怎样才能实现这个目标 Update 我正在 Windows 机器上运行 Arduino 软件 一种方法是使用avr objdump on the elf构建创建的文件 例如 在 OS
  • 使用 Ardulink 命令 Arduino Uno

    我试图在我的 JAVA 应用程序中使用 Ardulink 库来命令我的 Arduino Uno 但没有成功 我不知道我错过了什么 这是我的代码 Link link Link getDefaultInstance boolean connec
  • Arduino C++ 代码:可以使用虚函数和异常吗?

    跟进这条评论 https stackoverflow com questions 452139 writing firmware assembly or high level 452401从问题中编写固件 汇编还是高级 https stac
  • 使用 Arduino 来操作 Android [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 有人尝试过让 Android 和 Arduino 相互通信吗 我在网上找到了几个项目 Amarino http www amarino tool
  • Arduino 高低

    我有一个 Arduino 我想知道到底是什么HIGH and LOW意思是 就实际值而言 它们是否有符号ints unsigned ints unsigned char是 他们的价值观是什么 我猜HIGH and LOW可能是unsigne
  • Arduino 串行输出丢弃字符

    当我尝试为我的 Arduino Uno 编写一些代码时 我遇到了一个奇怪的串行输出 我有这个原始代码 MyClass myclass void setup Serial Begin 9600 Serial println Starting

随机推荐

  • Git分布式版本控制系统

    摘要 Git管理挖掘图 较为全面的查看Git的工作流程 由于网页显示限制 建议下载放大查看 Git 常用命令 Git 是一个很强大的分布式版本控制系统 它不但适用于管理大型开源软件的源代码 管理私人的文档和源代码也有很多优势 Git常用操作
  • 大数据架构:Kafka

    Kafka 是一个高吞吐 分布式 基于发布订阅的消息系统 利用Kafka技术可在廉价PC Server上搭建起大规模消息系统 Kafka具有消息持久化 高吞吐 分布式 多客户端支持 实时等特性 适用于离线和在线的消息消费 KAFKA 分布式
  • 服务器环境搭建——安装mysql

    环境 操作系统 64位的Centos7 6 mysql 8 0 25 步骤 下载rpm并上传到服务器 去官网下载对应操作系统的rpm包 我是centos7 选择第二个 当然有了链接也可以使用wget指令 避免上传的步骤 安装RPM安装包 y
  • ZABBIX实践(一) 服务端部署和安装

    zabbix 一款非常强大的监控软件 不仅可以基于SNMP监控各种网络设备 而且还可以监控linux win等各版本操作系统的状态 1 本次实践的目标 1 搭建服务端和agent 实现监控 2 摸索对于VMWare的监控 2 安装环境 se
  • Java代码重构的几种模式

    主要来源 面向对象设计原理与模式 Java版 Object Oriented Design Using Java Written by Dale Skrien Java代码的重构模式主要有三种 重命名方法重构模式 引入解释性变量重构模式 以
  • Vue项目中处理key=value格式的数据-案例

    返回值 qrCode expiredAt 1693821779265 token 449d599830b8486a9c7b15e0bc3f036c listenUri wss ws abcdtest link token0 f63c6488
  • SpringBoot Datahub DatahubException InjectionManagerFactory not found 暨 datahub中可能遇到的问题及解法

    产生原因 依赖使用的是 compile group com aliyun datahub name aliyun sdk datahub version 2 12 0 public 然后和之前自己加的包依赖产生冲突 看了下是sf4j log
  • matlab中eig用法,MATLAB中eig的用法

    在MATLAB中 计算矩阵A的特征值和特征向量的函数是eig A 常用的调用格式有 5种 1 E eig A 求矩阵A的全部特征值 构成向量E 2 V D eig A 求矩阵A的全部特征值 构成对角阵D 并求A的特征向量构成 V的列向量 3
  • Git提交指定文件

    Git提交指定文件步骤 1 git status 查看修改的所有内容 或者git status s 2 git add 引号中间放上文件名 执行上一步后 会显示文件名跟目录 3 git stash u k 忽略其他文件 很重要的一步 4 g
  • Div 高度、滚动条距 Div 顶部偏移量、Div 中文档总高度

    版权声明 分享是一种品质 开源是一种精神 https blog csdn net wangmx1993328 article details 84560051
  • 【ABviewer从零开始教学查看器篇③】打开文件之缩略图菜单

    ABViewer是一款高质量 高效率 低成本的多功能设计及工程文档管理工具 能为您提供全面的专业的浏览及编辑功能 同时支持30多种光栅和矢量图形格式 在小编看来 ABViewer是一款非常简单且实用的CAD文档查看与编辑器 对于使用小白可能
  • 【图像处理】MATLAB:亮度变换

    亮度变换 函数imadjust f imread breast digital Xray tif g1 imadjust f 0 1 1 0 阴暗反转图像 负片图像 等同于 g1 imcomplement f g2 imadjust f 0
  • 服务器重启后,Tomcat首页可以访问,具体项目访问不了,报Lifecycle error.Unable to connect to Redis server: /localhost:6379

    10 47 18 main ERROR standard Lifecycle error Unable to connect to Redis server localhost 6379 com fr third org redisson
  • uniapp图片上传

    首先 在页面中创建一个按钮 并绑定点击事件 用于选择图片
  • 手把手搭建k8s集群

    目录 集群机器配置 1 安装Docker 1 1 安装persistent data 和 lvm2 1 2 修改docker安装源以及安装docker 1 3 启动docker 1 4 切换docker镜像源 2 安装k8s 2 1 安装v
  • 数据库题目汇总(下)

    文章目录 第一题 第二题 第三题 第四题 第五题 第六题 第七题 第八题 第九题 第十题 第一题 编写一个sql语句 查询每一个部门中薪水最高的职工 结果返回部门编号dno 薪水最高的职工工号eno和薪水salary 以dno升序排列 se
  • 常用jar包用途说明

    jar包 用途 axis jar SOAP引擎包 commons discovery 0 2 jar 用来发现 查找和实现可插入式接口 提供一些一般类实例化 单件的生命周期管理的常用方法 jaxrpc jar Axis运行所需要的组件包 s
  • 使用vue上传或下载excel文件

    真实vue项目中使用的案例 excel的上传文件 安装axios
  • git远程分支代码拉取

    1 远程拉取gitlab 工程分支 并在本地建立分支 具体过程 新建一个空文件 初始化 git init 自己要与origin master建立连接 下划线远程仓库链接 git remote add origin http 192 168
  • Arduino前馈反向传播神经网络

    本文介绍了为Arduino Uno微控制器板开发的人工神经网络 这里描述的网络是前馈反向传播网络 可能是最常见的类型 它被认为是有监督或无监督学习的良好通用网络 该项目的代码以Arduino Sketch的形式提供 它是即插即用的 您可以将