原文地址:http://blog.csdn.net/acdreamers/article/details/44657439

今天来讲BP神经网络,神经网络在机器学习中应用比较广泛,比如函数逼近,模式识别,分类,数据压缩,数据

挖掘等领域。接下来介绍BP神经网络的原理及实现。

Contents

  1. BP神经网络的认识

  2. 隐含层的选取

  3. 正向传递子过程

  4. 反向传递子过程

  5. BP神经网络的注意点

  6. BP神经网络的C++实现

1. BP神经网络的认识

   BP(Back Propagation)神经网络分为两个过程

(1)工作信号正向传递子过程

(2)误差信号反向传递子过程

在BP神经网络中,单个样本有个输入,有个输出,在输入层和输出层之间通常还有若干个隐含层。实际

上,1989Robert Hecht-Nielsen证明了对于任何闭区间内的一个连续函数都可以用一个隐含层的BP网

络来逼近,这就是万能逼近定理。所以一个三层的BP网络就可以完成任意的维到维的映射。即这三层分

别是输入层(I),隐含层(H),输出层(O)。如下图示

2. 隐含层的选取

在BP神经网络中,输入层和输出层的节点个数都是确定的,而隐含层节点个数不确定,那么应该设置为多少

才合适呢?实际上,隐含层节点个数的多少对神经网络的性能是有影响的,有一个经验公式可以确定隐含层

节点数目,如下

其中为隐含层节点数目,为输入层节点数目,为输出层节点数目,之间的调节常数。

3. 正向传递子过程

现在设节点和节点之间的权值为,节点的阀值为,每个节点的输出值为,而每个节点的输出

值是根据上层所有节点的输出值、当前节点与上一层所有节点的权值和当前节点的阀值还有激活函数来实现

的。具体计算方法如下

其中为激活函数,一般选取S型函数或者线性函数。

正向传递的过程比较简单,按照上述公式计算即可。在BP神经网络中,输入层节点没有阀值。

4. 反向传递子过程

   在BP神经网络中,误差信号反向传递子过程比较复杂,它是基于Widrow-Hoff学习规则的。假设输出层

的所有结果为,误差函数如下

而BP神经网络的主要目的是反复修正权值和阀值,使得误差函数值达到最小。Widrow-Hoff学习规则

是通过沿着相对误差平方和的最速下降方向,连续调整网络的权值和阀值,根据梯度下降法,权值矢量

的修正正比于当前位置上E(w,b)的梯度,对于第个输出节点有

假设选择激活函数为

对激活函数求导,得到

那么接下来针对

其中有

同样对于

这就是著名的学习规则,通过改变神经元之间的连接权值来减少系统实际输出和期望输出的误差,这个规

则又叫做Widrow-Hoff学习规则或者纠错学习规则

上面是对隐含层和输出层之间的权值和输出层的阀值计算调整量,而针对输入层和隐含层和隐含层的阀值调

整量的计算更为复杂。假设是输入层第k个节点和隐含层第i个节点之间的权值,那么有

其中有

这样对学习规则理解更为深刻了吧。

有了上述公式,根据梯度下降法,那么对于隐含层和输出层之间的权值和阀值调整如下

而对于输入层和隐含层之间的权值和阀值调整同样有

至此BP神经网络的原理基本讲完。

5. BP神经网络的注意点

BP神经网络一般用于分类或者逼近问题。如果用于分类,则激活函数一般选用Sigmoid函数或者硬极限函

数,如果用于函数逼近,则输出层节点用线性函数,即

BP神经网络在训练数据时可以采用增量学习或者批量学习。

   增量学习要求输入模式要有足够的随机性,对输入模式的噪声比较敏感,即对于剧烈变化的输入模式,训

练效果比较差,适合在线处理。批量学习不存在输入模式次序问题,稳定性好,但是只适合离线处理。

   标准BP神经网络的缺陷:

(1)容易形成局部极小值而得不到全局最优值。

BP神经网络中极小值比较多,所以很容易陷入局部极小值,这就要求对初始权值和阀值有要求,要使

得初始权值和阀值随机性足够好,可以多次随机来实现。

(2)训练次数多使得学习效率低,收敛速度慢。

(3)隐含层的选取缺乏理论的指导。

(4)训练时学习新样本有遗忘旧样本的趋势。

   BP算法的改进:

(1)增加动量项

引入动量项是为了加速算法收敛,即如下公式

动量因子一般选取

(2)自适应调节学习率

(3)引入陡度因子

通常BP神经网络在训练之前会对数据归一化处理,即将数据映射到更小的区间内,比如[0,1]或[-1,1]。

6. BP神经网络的C++实现

   BP神经网络的C++文件如下

BP.h:

  1. #ifndef _BP_H_
  2. #define _BP_H_
  3. #include <vector>
  4. #define LAYER    3        //三层神经网络
  5. #define NUM      10       //每层的最多节点数
  6. #define A        30.0
  7. #define B        10.0     //A和B是S型函数的参数
  8. #define ITERS    1000     //最大训练次数
  9. #define ETA_W    0.0035   //权值调整率
  10. #define ETA_B    0.001    //阀值调整率
  11. #define ERROR    0.002    //单个样本允许的误差
  12. #define ACCU     0.005    //每次迭代允许的误差
  13. #define Type double
  14. #define Vector std::vector
  15. struct Data
  16. {
  17. Vector<Type> x;       //输入数据
  18. Vector<Type> y;       //输出数据
  19. };
  20. class BP{
  21. public:
  22. void GetData(const Vector<Data>);
  23. void Train();
  24. Vector<Type> ForeCast(const Vector<Type>);
  25. private:
  26. void InitNetWork();         //初始化网络
  27. void GetNums();             //获取输入、输出和隐含层节点数
  28. void ForwardTransfer();     //正向传播子过程
  29. void ReverseTransfer(int);  //逆向传播子过程
  30. void CalcDelta(int);        //计算w和b的调整量
  31. void UpdateNetWork();       //更新权值和阀值
  32. Type GetError(int);         //计算单个样本的误差
  33. Type GetAccu();             //计算所有样本的精度
  34. Type Sigmoid(const Type);   //计算Sigmoid的值
  35. private:
  36. int in_num;                 //输入层节点数
  37. int ou_num;                 //输出层节点数
  38. int hd_num;                 //隐含层节点数
  39. Vector<Data> data;          //输入输出数据
  40. Type w[LAYER][NUM][NUM];    //BP网络的权值
  41. Type b[LAYER][NUM];         //BP网络节点的阀值
  42. Type x[LAYER][NUM];         //每个神经元的值经S型函数转化后的输出值,输入层就为原值
  43. Type d[LAYER][NUM];         //记录delta学习规则中delta的值
  44. };
  45. #endif  //_BP_H_

BP.cpp:

  1. #include <string.h>
  2. #include <stdio.h>
  3. #include <math.h>
  4. #include <assert.h>
  5. #include "BP.h"
  6. //获取训练所有样本数据
  7. void BP::GetData(const Vector<Data> _data)
  8. {
  9. data = _data;
  10. }
  11. //开始进行训练
  12. void BP::Train()
  13. {
  14. printf("Begin to train BP NetWork!\n");
  15. GetNums();
  16. InitNetWork();
  17. int num = data.size();
  18. for(int iter = 0; iter <= ITERS; iter++)
  19. {
  20. for(int cnt = 0; cnt < num; cnt++)
  21. {
  22. //第一层输入节点赋值
  23. for(int i = 0; i < in_num; i++)
  24. x[0][i] = data.at(cnt).x[i];
  25. while(1)
  26. {
  27. ForwardTransfer();
  28. if(GetError(cnt) < ERROR)    //如果误差比较小,则针对单个样本跳出循环
  29. break;
  30. ReverseTransfer(cnt);
  31. }
  32. }
  33. printf("This is the %d th trainning NetWork !\n", iter);
  34. Type accu = GetAccu();
  35. printf("All Samples Accuracy is %lf\n", accu);
  36. if(accu < ACCU) break;
  37. }
  38. printf("The BP NetWork train End!\n");
  39. }
  40. //根据训练好的网络来预测输出值
  41. Vector<Type> BP::ForeCast(const Vector<Type> data)
  42. {
  43. int n = data.size();
  44. assert(n == in_num);
  45. for(int i = 0; i < in_num; i++)
  46. x[0][i] = data[i];
  47. ForwardTransfer();
  48. Vector<Type> v;
  49. for(int i = 0; i < ou_num; i++)
  50. v.push_back(x[2][i]);
  51. return v;
  52. }
  53. //获取网络节点数
  54. void BP::GetNums()
  55. {
  56. in_num = data[0].x.size();                         //获取输入层节点数
  57. ou_num = data[0].y.size();                         //获取输出层节点数
  58. hd_num = (int)sqrt((in_num + ou_num) * 1.0) + 5;   //获取隐含层节点数
  59. if(hd_num > NUM) hd_num = NUM;                     //隐含层数目不能超过最大设置
  60. }
  61. //初始化网络
  62. void BP::InitNetWork()
  63. {
  64. memset(w, 0, sizeof(w));      //初始化权值和阀值为0,也可以初始化随机值
  65. memset(b, 0, sizeof(b));
  66. }
  67. //工作信号正向传递子过程
  68. void BP::ForwardTransfer()
  69. {
  70. //计算隐含层各个节点的输出值
  71. for(int j = 0; j < hd_num; j++)
  72. {
  73. Type t = 0;
  74. for(int i = 0; i < in_num; i++)
  75. t += w[1][i][j] * x[0][i];
  76. t += b[1][j];
  77. x[1][j] = Sigmoid(t);
  78. }
  79. //计算输出层各节点的输出值
  80. for(int j = 0; j < ou_num; j++)
  81. {
  82. Type t = 0;
  83. for(int i = 0; i < hd_num; i++)
  84. t += w[2][i][j] * x[1][i];
  85. t += b[2][j];
  86. x[2][j] = Sigmoid(t);
  87. }
  88. }
  89. //计算单个样本的误差
  90. Type BP::GetError(int cnt)
  91. {
  92. Type ans = 0;
  93. for(int i = 0; i < ou_num; i++)
  94. ans += 0.5 * (x[2][i] - data.at(cnt).y[i]) * (x[2][i] - data.at(cnt).y[i]);
  95. return ans;
  96. }
  97. //误差信号反向传递子过程
  98. void BP::ReverseTransfer(int cnt)
  99. {
  100. CalcDelta(cnt);
  101. UpdateNetWork();
  102. }
  103. //计算所有样本的精度
  104. Type BP::GetAccu()
  105. {
  106. Type ans = 0;
  107. int num = data.size();
  108. for(int i = 0; i < num; i++)
  109. {
  110. int m = data.at(i).x.size();
  111. for(int j = 0; j < m; j++)
  112. x[0][j] = data.at(i).x[j];
  113. ForwardTransfer();
  114. int n = data.at(i).y.size();
  115. for(int j = 0; j < n; j++)
  116. ans += 0.5 * (x[2][j] - data.at(i).y[j]) * (x[2][j] - data.at(i).y[j]);
  117. }
  118. return ans / num;
  119. }
  120. //计算调整量
  121. void BP::CalcDelta(int cnt)
  122. {
  123. //计算输出层的delta值
  124. for(int i = 0; i < ou_num; i++)
  125. d[2][i] = (x[2][i] - data.at(cnt).y[i]) * x[2][i] * (A - x[2][i]) / (A * B);
  126. //计算隐含层的delta值
  127. for(int i = 0; i < hd_num; i++)
  128. {
  129. Type t = 0;
  130. for(int j = 0; j < ou_num; j++)
  131. t += w[2][i][j] * d[2][j];
  132. d[1][i] = t * x[1][i] * (A - x[1][i]) / (A * B);
  133. }
  134. }
  135. //根据计算出的调整量对BP网络进行调整
  136. void BP::UpdateNetWork()
  137. {
  138. //隐含层和输出层之间权值和阀值调整
  139. for(int i = 0; i < hd_num; i++)
  140. {
  141. for(int j = 0; j < ou_num; j++)
  142. w[2][i][j] -= ETA_W * d[2][j] * x[1][i];
  143. }
  144. for(int i = 0; i < ou_num; i++)
  145. b[2][i] -= ETA_B * d[2][i];
  146. //输入层和隐含层之间权值和阀值调整
  147. for(int i = 0; i < in_num; i++)
  148. {
  149. for(int j = 0; j < hd_num; j++)
  150. w[1][i][j] -= ETA_W * d[1][j] * x[0][i];
  151. }
  152. for(int i = 0; i < hd_num; i++)
  153. b[1][i] -= ETA_B * d[1][i];
  154. }
  155. //计算Sigmoid函数的值
  156. Type BP::Sigmoid(const Type x)
  157. {
  158. return A / (1 + exp(-x / B));
  159. }

Test.cpp:

  1. #include <iostream>
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include "BP.h"
  5. using namespace std;
  6. double sample[41][4]=
  7. {
  8. {0,0,0,0},
  9. {5,1,4,19.020},
  10. {5,3,3,14.150},
  11. {5,5,2,14.360},
  12. {5,3,3,14.150},
  13. {5,3,2,15.390},
  14. {5,3,2,15.390},
  15. {5,5,1,19.680},
  16. {5,1,2,21.060},
  17. {5,3,3,14.150},
  18. {5,5,4,12.680},
  19. {5,5,2,14.360},
  20. {5,1,3,19.610},
  21. {5,3,4,13.650},
  22. {5,5,5,12.430},
  23. {5,1,4,19.020},
  24. {5,1,4,19.020},
  25. {5,3,5,13.390},
  26. {5,5,4,12.680},
  27. {5,1,3,19.610},
  28. {5,3,2,15.390},
  29. {1,3,1,11.110},
  30. {1,5,2,6.521},
  31. {1,1,3,10.190},
  32. {1,3,4,6.043},
  33. {1,5,5,5.242},
  34. {1,5,3,5.724},
  35. {1,1,4,9.766},
  36. {1,3,5,5.870},
  37. {1,5,4,5.406},
  38. {1,1,3,10.190},
  39. {1,1,5,9.545},
  40. {1,3,4,6.043},
  41. {1,5,3,5.724},
  42. {1,1,2,11.250},
  43. {1,3,1,11.110},
  44. {1,3,3,6.380},
  45. {1,5,2,6.521},
  46. {1,1,1,16.000},
  47. {1,3,2,7.219},
  48. {1,5,3,5.724}
  49. };
  50. int main()
  51. {
  52. Vector<Data> data;
  53. for(int i = 0; i < 41; i++)
  54. {
  55. Data t;
  56. for(int j = 0; j < 3; j++)
  57. t.x.push_back(sample[i][j]);
  58. t.y.push_back(sample[i][3]);
  59. data.push_back(t);
  60. }
  61. BP *bp = new BP();
  62. bp->GetData(data);
  63. bp->Train();
  64. while(1)
  65. {
  66. Vector<Type> in;
  67. for(int i = 0; i < 3; i++)
  68. {
  69. Type v;
  70. scanf("%lf", &v);
  71. in.push_back(v);
  72. }
  73. Vector<Type> ou;
  74. ou = bp->ForeCast(in);
  75. printf("%lf\n", ou[0]);
  76. }
  77. return 0;
  78. }

Makefile:

  1. Test : BP.h BP.cpp Test.cpp
  2. g++ BP.cpp Test.cpp -o Test
  3. clean:
  4. rm Test

【转载】BP神经网络的更多相关文章

  1. [转载] bp神经网络推导

    https://blog.csdn.net/fanxin_i/article/details/80212906 看了这么多就这个推的清楚,转嘞

  2. 转载——关于bp神经网络

    一.BP神经网络的概念     BP神经网络是一种多层的前馈神经网络,其主要的特点是:信号是前向传播的,而误差是反向传播的.具体来说,对于如下的只含一个隐层的神经网络模型: (三层BP神经网络模型) ...

  3. BP神经网络—java实现(转载)

    神经网络的结构 神经网络的网络结构由输入层,隐含层,输出层组成.隐含层的个数+输出层的个数=神经网络的层数,也就是说神经网络的层数不包括输入层.下面是一个三层的神经网络,包含了两层隐含层,一个输出层. ...

  4. 数据挖掘系列(9)——BP神经网络算法与实践

    神经网络曾经很火,有过一段低迷期,现在因为深度学习的原因继续火起来了.神经网络有很多种:前向传输网络.反向传输网络.递归神经网络.卷积神经网络等.本文介绍基本的反向传输神经网络(Backpropaga ...

  5. bp神经网络及matlab实现

    本文主要内容包含: (1) 介绍神经网络基本原理,(2) AForge.NET实现前向神经网络的方法,(3) Matlab实现前向神经网络的方法 . 第0节.引例  本文以Fisher的Iris数据集 ...

  6. RBF神经网络和BP神经网络的关系

    作者:李瞬生链接:https://www.zhihu.com/question/44328472/answer/128973724来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  7. BP神经网络的公式推导

    如果感觉自己看不懂,那就看看我博客的梯度下降法,博文最后的感知机也算最简单的BP神经网络吧,用的也是反馈(w,b):典型梯度下降法 BP网络的结构 BP网络的结构如下图所示,分为输入层(Input), ...

  8. BP神经网络的数学原理及其算法实现

    什么是BP网络 BP网络的数学原理 BP网络算法实现 转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/44514073  上一篇 ...

  9. BP神经网络-- 基本模型

    转载:http://www.cnblogs.com/jzhlin/archive/2012/07/28/bp.html BP 神经网络中的 BP 为 Back  Propagation 的简写,最早它 ...

随机推荐

  1. 【Coursera】线性回归和逻辑回归

    一.线性回归 1.批量梯度下降法 每次对参数进行一次迭代时,都要扫描一遍输入全集 算法可以收敛到局部最优值 当迭代多次之后,每次迭代参数的改变越小 2.随机梯度下降法 对于一个输入样本,对参数进行一次 ...

  2. Internet History, Technology and Security (Week3)

    Week3. Welcome to week 3! This is our fourth and final week of History where we make the connection ...

  3. java集合LinkedList

    基于jdk_1.8.0 关于List,主要是有序的可重复的数据结构.jdk主要实现类有ArrayList(底层使用数组).LinkedList(底层使用双向链表) LinkedList: (一)继承关 ...

  4. mysql 性能分析及explain用法

    转载自http://blog.sina.com.cn/s/blog_4586764e0100o9s1.html 使用explain语句去查看分析结果 如   explain select * from ...

  5. angularJS1笔记-(19)-angular异步加载包的方式

    我们平时写的导入包的方式都是同步方式,有时候会显得过于卡顿,这样我们就可以使用异步加载的方式. script.js方式: 执行结果为: 异步加载还可以加载多个即为script([,,,],functi ...

  6. PAT 甲级 1126 Eulerian Path

    https://pintia.cn/problem-sets/994805342720868352/problems/994805349851185152 In graph theory, an Eu ...

  7. 抽奖系统 random()

    random() 方法可返回介于 0 ~ 1 之间的一个随机数. document.write(parseInt(10*Math.random())); //输出0-10之间的随机整数document ...

  8. ACM数论之旅13---容斥原理(一切都是命运石之门的选择(=゚ω゚)ノ)

    容斥原理我初中就听老师说过了,不知道你们有没有听过(/≧▽≦)/ 百度百科说: 在计数时,必须注意没有重复,没有遗漏. 为了使重叠部分不被重复计算,人们研究出一种新的计数方法. 这种方法的基本思想是: ...

  9. 关于AC自动机和DP的联系

    首先是描述个大概.不说一些特殊的DP 或者借用矩阵来状态转移 (这些本质都是一样的). 只讲AC自动机和DP的关系(个人理解). AC自动机 又可以叫做状态机. 我一开始的认为.AC 自动机提供了一些 ...

  10. 【刷题】洛谷 P3768 简单的数学题

    题目描述 由于出题人懒得写背景了,题目还是简单一点好. 输入一个整数n和一个整数p,你需要求出(\(\sum_{i=1}^n\sum_{j=1}^n ijgcd(i,j))~mod~p\),其中gcd ...