技术笔记
本博客记录踩坑脱坑、早年知识归纳. 新博客常常更新: https://hanxinle.github.io

导航

 

动机,其实自己是非常喜欢写作的,那些年中二年纪写了很多自己生活的方方面面,与那时候在QQ空间和新浪博客相比,现在自己算是弃笔了一般。想把自己看到的东西慢慢总结一下,以前中二幼稚起来觉得这些都太简单,不要写了,会丢人,其实根本没什么人光顾我的博客的。更根本的原因是健忘又懒惰,离不开烂笔头的帮助,很多知识与知识之间也不是具有逻辑链条,可以一条一条逐条推出来,就像捡贝壳一样,还是要自己一个一个捡起来才放心和靠谱,所以自己就在这里记下来,凡是因为这个目的而记录的,都写到《游记》里面去。

 

看了基本机器学习的书,觉得还是周志华老师讲得最好,把老师的思路总结起来呢,就是 这篇在知乎的文章讲得内容,方便了理解了老师书后复习用,这里引用下:

原文:  BP神经网原理及C语言实现  配套代码  其它 :BPNN-Face-Recognition-For-Parallel

其它:....我觉得也不错,适合我这个MPI初学者。 

BP神经网络及其C语言实现

BP神经网络是目前为止最为成功的神经网络模型之一,本文首先介绍BP神经网络的基本概念和理论推导,最后给出具有训练、仿真及实际拟合功能的C语言实现。
本文的理论部分全部来源于周志华《机器学习》,P97-P106,如需详细了解,请查阅本书。

1. 基本概念

  • 神经网络:由简单的神经元组成的广泛互联的网络,其具有适应性,可以模拟生物神经系统对真实世界所做出的交互反应。
  • 神经元模型:神经元模型是神经网络中的基本模型,单个神经元可以接收网络中其他神经元的信息,如果接收的信息超过阈值,则此神经元被激活,接着向其他神经元发送信息。
  • M-P神经元模型:M-P神经元模型是典型的神经元模型,其基本结构如图1所示。

图1 M-P神经元模型

在这个模型中,神经元接收来自n个其他神经元传递过来的输入信号,这些输入信号通过带权重的连接进行传递,神经元接收到的总输入值将与神经元的阈值进行比较,然后通过“激活函数”处理产生神经元的输出。

  • Sigmoid函数:sigmoid是最常用的激活函数,如图2所示。

图2 sigmoid函数

其函数表达式为sigmoid(x) = \frac{1}{1+e^{-x} }

  • 多层前馈神经网络(multi-layer feedforward neural networks):基本结构如图3所示,此种结构的圣经网络,每层神经元与下一层神经元全连接,神经元之间不存在同层连接,也不存在跨层连接。其网络层次包含3层,其中输入层神经元用于接收外界输入,隐层与输出层神经元对信号进行加工,最终结果由输出层神经元输出。

图3 多层前馈神经网络

  • 神经网络的学习过程:根据训练数据来调整神经元之间的连接权以及每个功能神经元的阈值,换言之,神经网络学习到的东西蕴含在连接权与阈值中。

2. BP神经网络理论推导

2.1 BP神经网络结构

BP神经网络是目前为止最为成功的神经网络算法之一,其学习方式采用标准梯度下降的误差逆传播(error BackPropagation)的方式,以下介绍的基本BP神经网络为3层前馈神经网络。

图4 BP神经网络模型

对于BP神经网络,我们需要使用训练数据集对其进行参数训练,然后使用测试机检验训练结果,如果训练效果达标,则可使用训练出的数据应用于实际使用场景,对于神经网络其训练过程最为主要,以下我们主要阐述其训练方法。

对于图4中的神经网络模型,我们做如下定义:

  • 给定训练集D = \left\{ (x_1,y_1),(x_2,y_2),...,(x_m,y_m) \right\} ,x_i\epsilon R^{d},y_i\epsilon R^{l} ,即输入数据维度为d,输出数据维度为l
  • 图4中,假设神经网络是输入节点为d个,输出节点为l个,隐层有q个的多层前向反馈神经网络,输出层第j个神经元阈值为\theta_j,第h个隐层神经元阈值为\gamma _h.输入层第i个节点与隐层第h个节点之间的权重为\nu _{ih},隐层第h个节点与输出层第j个节点的权重为w_{hj}.

根据以上假设可以有如下公式:

  1. 激活函数为f(x) = sigmoid(x)
  2. 隐层第h个神经元接收到的输入为\alpha _h=\sum_{i=1}^{d}{v_{ih}x_i}
  3. 隐层第h个神经元的输出为b_h=f(\alpha _h-\gamma _h)
  4. 输出层第j个神经元接收到的输入为\beta _j=\sum_{h=1}^{q}{w_{hj}b_h}
  5. 输出层第j个神经元的输出为y_j=f(\beta _j-\theta _j)

由以上5个公式可知,神经网络之中只要以下(d+l+1)q+l个参数确定,则就可以由输入计算出输出,这些参数分别为

  1. 输入层到隐层权重dq个,隐层到输出层权重ql
  2. 隐层神经元阈值q个,输出层神经元阈值l

那么我们如何得到这么多个参数呢?BP神经网络算法使用误差逆向传播进行训练。

2.2 BP神经网络的训练

神经网络的初始参数为[0,1]内随机数,假设某次训练过程中神经网络的输入的某个训练数据为(x,y),经过神经网络的输出为\tilde{y} =\left\{  \tilde{y_1}, \tilde{y_2},..., \tilde{y_l} \right\} ,其中\tilde{y_j} = f( \beta _j-\theta _j)

6. 对于训练数据集中的单个数据其误差E_k=\frac{1}{2} \sum_{j=1}^{l}{(\tilde{y_j}-y_j)^2 }

我们采用梯度下降法根据误差对神经网络中的参数进行反馈学习,神经网络中参数更新的公式为p \leftarrow p + \Delta p,

此处以隐层到输入层权重w_{hj}为例对神经网络参数更新公式进行推导:

我们以误差的负梯度方向对参数进行更新,\eta 为学习率,有如下公式:

7. 参数更新公式w_{hj}\leftarrow w_{hj}+\Delta w_{hj}

8. 参数调整力度\Delta w_{hj}=-\eta \frac{\partial E_k}{\partial w_{hj}}

9. 注意到,w_{hj}先影响到第j个输出神经元的输入值\beta _j,再影响到其输出值\tilde{y_j}, 根据偏导数的链式法则有\frac{\partial E_k}{\partial w_{hj}}=\frac{\partial E_k}{\partial \tilde{y_j} } \cdot  \frac{\partial \tilde{y_j}}{\partial {\beta _j}} \cdot \frac{\partial {\beta _j}}{\partial w_{hj}}

10. 根据\beta _j的定义显然有\frac{\partial \beta_j}{\partial w_{hj}}=b_h

11. 由于sigmoid函数的导数具有特殊性质f'(x) = f(x)(1-f(x))

12. 则g_j = - \frac{\partial E_k}{\partial \tilde{y_j}} \cdot \frac{\partial \tilde{y_j}}{\partial \beta_j}=-(\tilde{y_j}-y_j)f'(\beta_j-\theta_j)=\tilde{y_j}(1-\tilde{y_j})(y_j-\tilde{y_j})

按照以上推导方法有

13. \Delta w_{hj} = \eta g_j b_h
14. \Delta \theta_j = -\eta g_j
15. 同理,\Delta v_{ih} = \eta e_h x_i
16. \Delta \gamma _h = -\eta e_h
17. 在15.16式中e_h = - \frac{\partial E_k}{\partial b_h} \cdot \frac{\partial b_h}{\partial \alpha_h},而b_h影响l\beta _j,\beta _j又影响E,所以有
e_h = -(\sum_{j=1}^{l}{\frac{\partial E_k}{\partial \beta_j} \cdot \frac{\partial \beta_j}{\partial b_h}} ) \cdot f'(\alpha_h - \gamma_h)=-\sum_{j=1}^{l}{w_{hj}g_j} \cdot f'(\alpha_h - \gamma_h) = b_n(1-b_n) \sum_{j=1}^{l}{w_{hj} g_j}

注意在公式13.14.15.16中\eta控制着每一轮迭代中的更新步长,若太大,则容易振荡,太小则学习过程收敛很慢,为了细微调节,13.14中的学习率可以和15.16中的不一样。

18. 使用训练集上的累积误差衡量训练质量E = \frac{1}{m} \sum_{k=1}^{m}{E_k}

2.3 BP神经网络的训练过程

  • (0,1)内随机初始化神经网络中的连接权重和阈值;
  • repeat
  • for 遍历训练集中的每一个样本
  • 根据当前参数按照公式1.2.3.4.5计算样本的b_h
y_j.
  • 根据公式12.17计算g_j
e_h.
  • 根据公式13.14.15.16更新w_{hj}, v_{ih}, \theta_j, \gamma _h.
  • end for
  • until (迭代若干次或者累积误差E小于特定值)
  • 得到BP神经网络的参数

3. C语言实现

C语言的实现就很简单了,BP神经网络分为两个部分:

1. 训练模块,包含训练以及验证过程,注意此神经网络的输入输出都需要预先进行归一化处理。

 1 /**
 2  * To fetch test_set.
 3  * The length of in is IN_N, the length of out is OUT_N.
 4  */
 5 typedef bool (*test_set_get_t)(double *in, double *out);
 6 
 7 /**
 8  * Reset test_set fetch process.
 9  */
10 typedef bool (*test_set_init_t)(void);
11 
12 /**
13  * Init bpnn module.
14  */
15 void bpnn_init(void);
16 
17 /**
18  * Train bpnn module and produce parameter file.
19  * @param f_get To get test data in stream.
20  */
21 void bpnn_train(test_set_get_t f_get, test_set_init_t f_init);
22 
23 /**
24  * Test result of bpnn train parameter.
25  * @param f_get
26  */
27 void bpnn_sim(test_set_get_t f_get);

 

2. 拟合模块;

#define T bpnn_t
typedef struct T *T;

/**
 * Init bpnn module.
 * @return
 */
T bpnn_fit_new(void);

/**
 * Using bpnn fit in to out.
 * @param bpnn
 * @param in
 * @param out
 */
void bpnn_fit(T bpnn, double *in, double *out);

/**
 * Uninit bpnn.
 * @param bpnn
 */
void bpnn_fit_free(T *bpnn);

 

详细的实现请访问github:,https://github.com/ThreeClassMrWang/c-bpnn

实现里教会了神经网络进行(a+b+c)/3的计算,详细请见github,谢谢!

再次声明,这个文章是 知乎王小军 的,看原文请点击文章开头处的链接,可以关注下这位大佬在做什么,共同进步。

posted on 2017-11-21 16:09  九品加  阅读(292)  评论(0编辑  收藏  举报