原文:https://www.shanyemangfu.com/bp-c.html
人工智能现在还是十分火热。说到人工智能,那就必须提到AlphaGo的事情。这样就可以引出神经网络了。BP神经网络是最简单的也是最早的人工神经网络,这是最基本的网络,以后所有的网络都是以此改进而来。当然学习神经网络要从学习BP的原理学起。
正好有个课程报告,让实现BP人工神经网络。大部分同学们都是使用的Python。但是不知道我怎么产生了可怕的想法,非得要使用C语言搞一波。当然一般情况,网上有的话就不重复造轮子了。但是我百度了很多,都是把网络写死了,根本没有办法扩展。不符合我的需求,于是我就产生了用C语言一个可以拓展的BP神经网络的想法。说干就干,但是现实没有那么好办。我前后写了三版。一开始使用随机梯度下降法,而且没有加上偏置。后来想使用批量梯度下降法,加上偏置。但是批量也挺麻烦,转头一想,为什么不使用小批量梯度下降呢。因为这个工程量差不多。于是最终第三版,写成了一个小批量梯度下降法的BP。
至于原理和公式推导什么的,我就不放了。因为实在太麻烦了。但是如果你不懂原理的话,或者想要推导过程。这儿推荐一个大佬写的文章,非常详细。这个程序也是根据这篇文章编写的。 https://blog.csdn.net/u014303046/article/details/78200010/

然后再说下程序出现的问题吧。
1.我本来想加上批规范化的。就是Batch Normalization。可以把每一层的输出搞到同一个分布。但是技术有限,感觉太难写了,懒得动弹就没写。以后有时间再说吧。
2.程序也添加了relu激活函数,但是效果不好,很多时候无法收敛,根本达不到想象中relu激活函数的优化效果。我暂时没找到原因。有时间再调试吧。
3.样本数大的时候,容易发生梯度消失,误差小到一定程度就不再降低。
4.用了c++的一些语法,比cout,因为写着方便。
5.其他的基本都写到注释里了,不懂再在评论区问吧。
/*
Author:山野莽夫
Web:https://www.shanyemangfu.com
version:3.0
*/
#include <iostream>
#include<math.h>
#include<fstream>
#include<algorithm>
#include<ctime>
#include <iomanip>
using namespace std;
//定义函数
double f(double x, int kind);//激活函数
double df(double x, int kind);//激活函数的导数
void readdata();//读取数据
void initial();//初始化
void save();//保存模型
void load();//加载模型
void normalall();//全部数据归一化
bool dobatchtrain(int traintime, double acc, int& times);//
double batchtrain(); //小批量随机梯度下降,单次更新
void batchpos(int begin, int batchsize);//小批量正向计算
void batchnega(int begin, int batchsize);//小批量反向更新
void btest();//测试
//参数
constexpr int hidenum = 2;//隐藏层层数;
constexpr int num[hidenum + 1 + 1 + 1] = { 5,4,5,5,3 };//各层神经元个数0位置存储最大值
constexpr double e = 2.718281828459;
constexpr int tnum = 150;//训练样本数目
constexpr double tau = 0.001;//学习率
constexpr int trainnum = 90;//训练样本数目
constexpr int batchsize = 10;//批大小,要设置为可以被样本数目整除,虽说可以代码处理,但是懒得处理。
//变量
double x[num[1] + 1][tnum + 1];//输入
double y[num[hidenum + 2] + 1][tnum + 1];//期望输出
double nmx[num[1] + 1][tnum + 1];//归一化后的输入
double nmy[num[hidenum + 2] + 1][tnum + 1];//归一化后的期望输出
double w[hidenum + 2][num[0] + 1][num[0] + 1];//层数/k层顺序/k-1层顺序 |权重
double bb[hidenum + 2 + 1][num[0] + 1];//偏置
double bd[hidenum + 2 + 1][num[0] + 1][batchsize + 1];//误差
double by[num[hidenum + 2] + 1][batchsize + 1];//预测输出值
double bz[hidenum + 2 + 1][num[0] + 1][batchsize + 1];//净输出
double ba[hidenum + 2 + 1][num[0] + 1][batchsize + 1];//输出