史上最简洁实用人工神经元网络c++编写202301

这是史上最简单、清晰……
C++语言编写的 带正向传播、反向传播(Forward ……和Back Propagation)……任意Nodes数的人工神经元神经网络……。

大一学生、甚至中学生可以读懂。

适合于,没学过高数的程序员……照猫画虎编写人工智能、深度学习之神经网络……


著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

“我在网上看到过很多神经网络的实现方法,但这一篇是最简单、最清晰的。”

一位来自Umass的华人小哥Along Asong,写了篇神经网络入门教程,在线代码网站Repl.it联合创始人Amjad Masad看完以后,给予如是评价。

 这篇教程发布仅一天时间,就在Hacker News论坛上收获了574赞。程序员们纷纷夸赞这篇文章的代码写得很好,变量名很规范,让人一目了然。

下面就让我们一起从零开始学习神经网络吧:

 c++写一完整人工神经网络,要求输入层有9个Nodes,一个隐藏层有12个Nodes,输出层有5个Nodes,……含有反向传播、梯度下降法更新权重和偏置等。

  1. 神经网络的结构
  2. 前向传播(Forward Propagation)
  3. 反向传播(Back Propagation)
  4. 更新权重和偏置(梯度下降法)

下面是一个基本的实现:

// c++人工神经网络反向传播梯度下降更新权重偏置230810a18.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <vector>
#include <cmath>
#include <ctime>
#include <cstdlib>
#include <string>
#include <sstream>
#include <iomanip>  // 引入setprecisionint Ninpu9t = 9; //输入层Nodes数
int Nhidde12n = 12;//隐藏层Nodes数 4;// 11;
int nOutpu2t = 5;//输出层Nodes数 2;// 3;double Lost001 = 9.0;// 使用sigmoid作为激活函数
double sigmoid(double x) {return 1.0 / (1.0 + std::exp(-x));
}double sigmoid_derivative(double x) {double s = sigmoid(x);return s * (1 - s);
}class NeuralNetwork {
private:std::vector<std::vector<double>> weights1, weights2; // weightsstd::vector<double> bias1, bias2;                     // biasesdouble learning_rate;public:NeuralNetwork() : learning_rate(0.1) {  //01) {srand(time(nullptr));// 初始化权重和偏置weights1.resize(Ninpu9t, std::vector<double>(Nhidde12n));weights2.resize(Nhidde12n, std::vector<double>(nOutpu2t));bias1.resize(Nhidde12n);bias2.resize(nOutpu2t);for (int i = 0; i < Ninpu9t; ++i)for (int j = 0; j < Nhidde12n; ++j)weights1[i][j] = (rand() % 2000 - 1000) / 1000.0; // [-1, 1]for (int i = 0; i < Nhidde12n; ++i) {//for1100ibias1[i] = (rand() % 2000 - 1000) / 1000.0; // [-1, 1]for (int j = 0; j < nOutpu2t; ++j)weights2[i][j] = (rand() % 2000 - 1000) / 1000.0; // [-1, 1]}//for1100ifor (int i = 0; i < nOutpu2t; ++i)bias2[i] = (rand() % 2000 - 1000) / 1000.0; // [-1, 1]}std::vector<double> forward(const std::vector<double>& input) {std::vector<double> hidden(Nhidde12n);std::vector<double> output(nOutpu2t);for (int i = 0; i < Nhidde12n; ++i) {//for110ihidden[i] = 0;for (int j = 0; j < Ninpu9t; ++j){hidden[i] += input[j] * weights1[j][i];}hidden[i] += bias1[i];hidden[i] = sigmoid(hidden[i]);}//for110ifor (int i = 0; i < nOutpu2t; ++i) {//for220ioutput[i] = 0;for (int j = 0; j < Nhidde12n; ++j){output[i] += hidden[j] * weights2[j][i];}output[i] += bias2[i];output[i] = sigmoid(output[i]);}//for220ireturn output;}void train(const std::vector<double>& input, const std::vector<double>& target) {// Forwardstd::vector<double> hidden(Nhidde12n);std::vector<double> output(nOutpu2t);std::vector<double> hidden_sum(Nhidde12n, 0);std::vector<double> output_sum(nOutpu2t, 0);for (int i = 0; i < Nhidde12n; ++i) {for (int j = 0; j < Ninpu9t; ++j){hidden_sum[i] += input[j] * weights1[j][i];}hidden_sum[i] += bias1[i];hidden[i] = sigmoid(hidden_sum[i]);}//for110ifor (int i = 0; i < nOutpu2t; ++i) {//for220ifor (int j = 0; j < Nhidde12n; ++j)output_sum[i] += hidden[j] * weights2[j][i];    //注意 output_sum[]output_sum[i] += bias2[i];output[i] = sigmoid(output_sum[i]);     //激活函数正向传播}//for220i//反向传播Backpropagationstd::vector<double> output_errors(nOutpu2t);for (int i = 0; i < nOutpu2t; ++i) {//for2240ioutput_errors[i] = target[i] - output[i];//算损失综合总和:Lost001 = 0.0;Lost001 += fabs(output_errors[i]);}//for2240istd::vector<double> d_output(nOutpu2t);for (int i = 0; i < nOutpu2t; ++i)d_output[i] = output_errors[i] * sigmoid_derivative(output_sum[i]); //对output_sum[]做 激活函数的 导数 传播std::vector<double> hidden_errors(Nhidde12n, 0);for (int i = 0; i < Nhidde12n; ++i) {//for440ifor (int j = 0; j < nOutpu2t; ++j)hidden_errors[i] += weights2[i][j] * d_output[j];}//for440istd::vector<double> d_hidden(Nhidde12n);for (int i = 0; i < Nhidde12n; ++i)d_hidden[i] = hidden_errors[i] * sigmoid_derivative(hidden_sum[i]); //对 hidden_errors层 做激活函数的 导数 传播// Update weights and biasesfor (int i = 0; i < Nhidde12n; ++i) {//for66ifor (int j = 0; j < nOutpu2t; ++j)weights2[i][j] += learning_rate * d_output[j] * hidden[i]; //修改 隐藏层的 weights2}//for66ifor (int i = 0; i < nOutpu2t; ++i)bias2[i] += learning_rate * d_output[i];for (int i = 0; i < Ninpu9t; ++i) {//for990ifor (int j = 0; j < Nhidde12n; ++j)weights1[i][j] += learning_rate * d_hidden[j] * input[i];   //修改输入层的 weight1}//for990ifor (int i = 0; i < Nhidde12n; ++i)bias1[i] += learning_rate * d_hidden[i];}//void train(const std::vector<double>& input, const std::vector<double>& target
}; //class NeuralNetwork {int main() {NeuralNetwork nn;// Examplestd::vector<double> input[5];input[0] = { 0,1,0, 0,1,0, 0,1,0 };      //1“竖线”或 “1”字{ 1.0, 0.5, 0.25, 0.125 };input[1] = { 0,0,0, 1,1,1,0,0,0 };      //-“横线”或 “-”减号{ 1.0, 0.5, 0.25, 0.125 };input[2] = { 0,1,0, 1,1,1, 0,1,0 };      //+“+”加号{ 1.0, 0.5, 0.25, 0.125 };input[3] = { 0,1,0, 0,2, 0,  0,3, 0.12 };   // '1'或 '|'字型{ 1.0, 0.5, 0.25, 0.125 };input[4] = { 1,1,0, 9,0,9.8,  1,1,1 };      //“口”字型+{ 1.0, 0.5, 0.25, 0.125 };std::vector<double> target[5];target[0] = { 1.0, 0,0,0,0 };//1 , 0}; //0.0, 1.0, 0.5};      //{ 0.0, 1.0 };target[1] = { 0, 1.0 ,0,0,0};//- 91.0, 0};// , 0, 0}; //target[2] = { 0,0,1.0,0,0 };//+ 1.0, 0.5};target[3] = { 1.0 ,0,0, 0.5 ,0}; //1target[4] = { 0,0,0,0,1.0 }; //“口”// Trainingfor (int i = 0; i < 50000/*00 */; ++i) {//for220ifor (int jj = 0; jj < 4; ++jj) {nn.train(input[jj], target[jj]);}//for2230jjif (0 == i % 10000) {//if2250printf(".");std::cout << "[Lost:" << Lost001 << std::endl;Lost001 = 0;}//if2250
}//for220i// Testinput[0] = { 0,1,0, 0,1,0, 0,1,0 };      //1{ 1.0, 0.5, 0.25, 0.125 };std::vector<double> output = nn.forward(input[0]);for (auto& val : output)std::cout << val << " ";std::cout << std::endl;input[1] = { 0,0,0, 1,1,1, 0,0,0 };//std::vector<double> output = nn.forward(input[1]);for (auto& val : output)std::cout << val << " ";std::cout << std::endl;//-----------------------------------------------std::string S;//   int D[9];do {std::cout << std::endl << "请输入一个包含9个由逗号分隔的数字的字符串: ";std::getline(std::cin, S);std::stringstream ss(S);for (int i = 0; i < 9; ++i) {std::string temp;std::getline(ss, temp, ',');input[1][i] = (double)std::stof(temp); // 将字符串转化为整数}std::cout << "数字数组为: ";for (int i = 0; i < 9; ++i) {std::cout << input[1][i] << " ";}output = nn.forward(input[1]);std::cout << std::endl;for (auto& val : output)std::cout <<std::fixed<< std::setprecision(9)<< val << " ";} while (1 == 1);//====================================================return 0;
}//main

运行结果:

 

完整神经网络程序、和用法 可以私信或者 Email联系本人……

本人长期从事人工智能,神经网络,嵌入式 C/C++语言开发、培训……欢迎咨询!

41313989

41313989#QQ.com

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

plt绘制箱型图+散点图

import numpy as np import matplotlib.pyplot as plt# 创建示例数据 np.random.seed(1) data [np.random.normal(0, std, 100) for std in range(1, 4)]# 绘制箱型图 plt.boxplot(data, patch_artistTrue,zorder0)# 添加数据点的散点图&#xff0c;并设置参数以避免重叠 for …

6.Web后端开发【SpringBoot入门】

文章目录 1 SpringBoot快速入门1.1 Web分析 2. HTTP协议2.1 HTTP-概述2.1.1 介绍2.2.2 特点 2.2 HTTP-请求协议2.3 HTTP-响应协议2.3.1 格式介绍2.3.2 响应状态码 常见的相应状态码 3 WEB服务器3.1 服务器概述 1 SpringBoot快速入门 Spring的官网Spring Boot 可以帮助我们非常…

探秘Maven神奇力量:使用systemPath加载外部JAR包

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; 探秘Maven神奇力量&#xff1a;使用systemPath加载外部JAR包 ⏱️ 创作…

明星都偏爱的多燕瘦活酵素,不含非法添加事件更健康

不少瘦身人士信奉“运动就能瘦”的准则&#xff0c;每天坚持高强度运动&#xff0c;一段时间后却发现&#xff0c;不仅体重没有下降&#xff0c;甚至整个人看起来都变得更加壮实了&#xff0c;这是为什么&#xff1f; 首先&#xff0c;运动是分为减脂和增肌两种类型的&#xff…

拒绝摆烂!C语言练习打卡第四天

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;每日一练 &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、选择题 &#x1f4dd;1.第一题 &#x1f4dd;2.第二题 &#x1f4d…

远程线程注入(简单样例以及原理)

远程线程注入(简单样例以及原理) 注入的目标是将我们的代码注入到目标进程的地址空间中 注入通常可以根据注入的内容分为两种类型&#xff1a; shellcode注入 &#xff1a;这种注入是将我们的代码直接注入到目标内存中&#xff0c;这就要保证我们的代码在贴到其他地址上后仍…

【AI】文心一言的使用

一、获得内测资格&#xff1a; 1、点击网页链接申请&#xff1a;https://yiyan.baidu.com/ 2、点击加入体验&#xff0c;等待通过 二、获得AI伙伴内测名额 1、收到短信通知&#xff0c;点击链接 网页Link&#xff1a;https://chat.baidu.com/page/launch.html?fa&sourc…

SpringBoot + Vue 微人事权限组管理模块 (十四)

权限组前端页面制作 权限组管理角色和菜单之间关系&#xff0c;操作员管理着用户和角色之间的关系。 英文的输入框要有个前缀&#xff0c;SpringSecurity里角色英文名需要加一个ROLE_的前缀 上代码 <div><div class"permissManaTool"><el-input pla…

通过 kk 创建 k8s 集群和 kubesphere

官方文档&#xff1a;多节点安装 确保从正确的区域下载 KubeKey export KKZONEcn下载 KubeKey curl -sfL https://get-kk.kubesphere.io | VERSIONv3.0.7 sh -为 kk 添加可执行权限&#xff1a; chmod x kk创建 config 文件 KubeSphere 版本&#xff1a;v3.3 支持的 Kuber…

go语言中channel类型

目录 什么是channel 为什么要有channel channel操作使用 初始化&#xff1a; 操作&#xff1a; 单向channel 双向channel&#xff0c;可读可写&#xff1a; close下什么场景会出现panic 总结 什么是channel Channels are a typed conduit through which you can send …

【ROS】参数服务器--理论模型与参数操作(C++)

一、概念介绍 参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据。 作用&#xff1a;存储一些多节点…

Qt实现简单的漫游器

文章目录 Qt的OpenGL窗口GLSL的实现摄像机类的实现简单的漫游器 Qt的OpenGL窗口 Qt主要是使用QOpenGLWidget来实现opengl的功能。  QOpenGLWidget 提供了三个便捷的虚函数&#xff0c;可以重载&#xff0c;用来重新实现典型的OpenGL任务&#xff1a; paintGL&#xff1a;渲染…

Datawhale Django后端开发入门 Vscode TASK02 Admin管理员、外键的使用

一.Admin管理员的使用 1、启动django服务 使用创建管理员之前&#xff0c;一定要先启动django服务&#xff0c;虽然TASK01和TASK02是分开的&#xff0c;但是进行第二个流程的时候记得先启动django服务&#xff0c;注意此时是在你的项目文件夹下启动的&#xff0c;时刻注意要执…

基于CentOS7.9安装部署docker(简洁版)

安装部署 1基于官方脚本安装&#xff08;不推荐 不能自行选择版本&#xff09; 官方文档&#xff1a;https://docs.docker.com/engine/install/centos/ 2 使用yum安装 阿里云文档&#xff1a;docker-ce镜像_docker-ce下载地址_docker-ce安装教程-阿里巴巴开源镜像站 # ste…

matlab使用教程(19)—曲线拟合与一元方程求根

1.多项式曲线拟合 此示例说明如何使用 polyfit 函数将多项式曲线与一组数据点拟合。您可以按照以下语法&#xff0c;使用 polyfit 求出以最小二乘方式与一组数据拟合的多项式的系数 p polyfit(x,y,n), 其中&#xff1a; • x 和 y 是包含数据点的 x 和 y 坐标的向量 …

【Linux操作系统】Linux系统编程中的共享存储映射(mmap)

在Linux系统编程中&#xff0c;进程之间的通信是一项重要的任务。共享存储映射&#xff08;mmap&#xff09;是一种高效的进程通信方式&#xff0c;它允许多个进程共享同一个内存区域&#xff0c;从而实现数据的共享和通信。本文将介绍共享存储映射的概念、原理、使用方法和注意…

uni-app的Vue.js实现微信小程序的紧急事件登记页面功能

主要功能实现 完成发生时间选择功能&#xff0c;用户可以通过日期选择器选择事件发生的时间。实现事件类型选择功能&#xff0c;用户可以通过下拉选择框选择事件的类型。添加子养殖场编号输入框&#xff0c;用户可以输入与事件相关的子养殖场编号。完成事件描述输入功能&#…

C++头文件

C头文件 一般头文件特殊头文件windows.hbits/stdc.h 一般头文件 C头文件是一种包含预定义函数、类和变量声明的文件。它们通常用于在源代码文件中引入外部库或模块的功能。 头文件的作用是提供程序所需的声明信息&#xff0c;以便在源代码文件中使用这些声明。当你在源代码文…

Android Studio实现解析HTML获取图片URL,将URL存到list,进行列表展示

目录 效果build.gradle(app)添加的依赖(用不上的可以不加)AndroidManifest.xml错误代码activity_main.xmlitem_image.xmlMainActivityImage适配器ImageModel 接收图片URL效果 build.gradle(app)添加的依赖(用不上的可以不加) dependencies {implementation com.square…

使用pnpm workspace管理Monorepo架构

在开发项目的过程中&#xff0c;我们需要在一个仓库中管理多个项目&#xff0c;每个项目有独立的依赖、脚手架&#xff0c;这种形式的项目结构我们称之为Monorepo&#xff0c;pnpm workspace就是管理这类项目的方案之一。 一、pnpm简介 1、pnpm概述 pnpm代表performance npm…