【转载】BP神经网络

原文地址: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:

[cpp] view plain copy

  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:

[cpp] view plain copy

  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:

[cpp] view plain copy

  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:

[cpp] view plain copy

  1. Test : BP.h BP.cpp Test.cpp
  2. g++ BP.cpp Test.cpp -o Test
  3. clean:
  4. rm Test
时间: 2024-10-01 16:15:57

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

转载——关于bp神经网络

一.BP神经网络的概念 BP神经网络是一种多层的前馈神经网络,其主要的特点是:信号是前向传播的,而误差是反向传播的.具体来说,对于如下的只含一个隐层的神经网络模型: (三层BP神经网络模型) BP神经网络的过程主要分为两个阶段,第一阶段是信号的前向传播,从输入层经过隐含层,最后到达输出层:第二阶段是误差的反向传播,从输出层到隐含层,最后到输入层,依次调节隐含层到输出层的权重和偏置,输入层到隐含层的权重和偏置. 二.BP神经网络的流程 在知道了BP神经网络的特点后,我们需要依据信号的前向传播和误差

BP神经网络

BP 神经网络中的 BP 为 Back  Propagation 的简写,最早它是由Rumelhart.McCelland等科学家于 1986 年提出来的,Rumelhart 并在Nature 上发表了一篇非常著名的文章 <Learning representations by back-propagating errors> .随着时代的迁移,BP神经网络理论不断的得到改进.更新,现在无疑已成为了应用最为广泛的神经网络模型之一.让我们一起来探索下 BP神经网络最初的 基本模型和概念! 从神经

题外:分类篇(音乐风格分类)基于BP神经网络

语音特征参数MFCC的提取及识别 (2012-09-07 20:24:03) 转载▼ 耳蜗实质上相当于一个滤波器组,耳蜗的滤波作用是在对数频率尺度上进行的,在1000HZ下,人耳的感知能力与频率成线性关系:而在1000HZ以上,人耳的感知能力与频率不构成线性关系,而更偏向于对数关系,这就使得人耳对低频信号比高频信号更敏感.Mel频率的提出是为了方便人耳对不同频率语音的感知特性的研究.频率与Mel频率的转换公式为: MFCC在一定程度上模拟了人耳对语音的处理特点,应用了人耳听觉感知方面的研究成果,

BP神经网络及matlab实现

本文主要内容包括: (1) 介绍神经网络基本原理,(2) AForge.NET实现前向神经网络的方法,(3) Matlab实现前向神经网络的方法 . 第0节.引例  本文以Fisher的Iris数据集作为神经网络程序的测试数据集.Iris数据集可以在http://en.wikipedia.org/wiki/Iris_flower_data_set  找到.这里简要介绍一下Iris数据集: 有一批Iris花,已知这批Iris花可分为3个品种,现需要对其进行分类.不同品种的Iris花的花萼长度.花萼

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

作者:李瞬生链接:https://www.zhihu.com/question/44328472/answer/128973724来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. BP Neural Network - 使用 Automatic Differentiation (Backpropagation) 进行导数计算的层级图模型 (layer-by-layer graphical model) 只要模型是一层一层的,并使用AD/BP算法,就能称作 BP Ne

字符识别OCR研究一(模板匹配&amp;amp;BP神经网络训练)

摘 要 在MATLAB环境下利用USB摄像头採集字符图像.读取一帧保存为图像.然后对读取保存的字符图像,灰度化.二值化,在此基础上做倾斜矫正.对矫正的图像进行滤波平滑处理,然后对字符区域进行提取切割出单个字符.识别方法一是採用模板匹配的方法逐个对字符与预先制作好的字符模板比較,假设结果小于某一阈值则结果就是模板上的字符:二是採用BP神经网络训练.通过训练好的net对待识别字符进行识别.最然后将识别结果通过MATLAB下的串口工具输出51单片机上用液晶显示出来. keyword: 倾斜矫正.字符切

BP神经网络的公式推导

如果感觉自己看不懂,那就看看我博客的梯度下降法,博文最后的感知机也算最简单的BP神经网络吧,用的也是反馈(w,b):典型梯度下降法 BP网络的结构 BP网络的结构如下图所示,分为输入层(Input),隐含层(Hidden),输出层(Output). 输入层的结点个数取决于输入的特征个数. 输出层的结点个数由分类的种类决定. 在输入层和输出层之间通常还有若干个隐含层,至于隐含层的个数以及每个隐含层的结点个数由训练工程师的经验来人为设定. 链接A曾提到由万能逼近定理,一般一个隐含层就足够了.且这个隐

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

神经网络曾经很火,有过一段低迷期,现在因为深度学习的原因继续火起来了.神经网络有很多种:前向传输网络.反向传输网络.递归神经网络.卷积神经网络等.本文介绍基本的反向传输神经网络(Backpropagation 简称BP),主要讲述算法的基本流程和自己在训练BP神经网络的一些经验. BP神经网络的结构 神经网络就是模拟人的大脑的神经单元的工作方式,但进行了很大的简化,神经网络由很多神经网络层构成,而每一层又由许多单元组成,第一层叫输入层,最后一层叫输出层,中间的各层叫隐藏层,在BP神经网络中,只有

BP神经网络-- 基本模型

转载:http://www.cnblogs.com/jzhlin/archive/2012/07/28/bp.html BP 神经网络中的 BP 为 Back  Propagation 的简写,最早它是由Rumelhart.McCelland等科学家于 1986 年提出来的,Rumelhart 并在Nature 上发表了一篇非常著名的文章 <Learning representations by back-propagating errors> .随着时代的迁移,BP神经网络理论不断的得到改进