BP神经网络学习与实践

BP(back propagation)神经网络是1986年由Rumelhart和McClelland为首的科学家提出的概念,是一种按照误差逆向传播算法训练的多层前馈神经网络,是目前应用最广泛的神经网络。

目前,在人工神经网络的实际应用中,绝大部分的神经网络模型都采用BP网络及其变化形式。它也是前向网络的核心部分,体现了人工神经网络的精华。

先举一个简单的例子,带入数值演示反向传播法的过程,掌握BP神经网络。

第一层是输入层,包含两个神经元In1,In2,和截距项b1;第二层是隐含层,包含两个神经元H1、H2和截距项b2,第三层是输出O1、O2,每条线上标的wi是层与层之间连接的权重,激活函数我们默认为sigmoid函数。设输入分别为x1、x2,实际输出分别为y1、y2。
在这里插入图片描述

1. 前向传播

  1. 输入层Ini---->隐含层Hi:
      计算神经元H1、H2的输入加权和分别为h1、h2:
       h 1 = x 1 . w 1 + x 2 . w 3 + b 1 h_{1} = x_{1} .w_{1}+x_{2}. w_{3}+b_{1} h1=x1.w1+x2.w3+b1
       h 2 = x 1 . w 2 + x 2 . w 4 + b 2 h_{2} = x_{1} .w_{2}+x_{2} .w_{4}+b_{2} h2=x1.w2+x2.w4+b2
      神经元H1的输出到O1:(此处用到激活函数为sigmoid函数)、H2的输出到O2:
       h o 1 = 1 1 + e h 1 ho_{1}=\frac{1}{1+e^{h_{1}}} ho1=1+eh11
       h o 2 = 1 1 + e h 2 ho_{2}=\frac{1}{1+e^{h_{2}}} ho2=1+eh21
  2. 隐含层-Hi—>输出层Oi:
      计算输出层神经元o1和o2的值:
       o 1 = h o 1 . w 5 + h o 2 . w 7 + b 3 o_{1} = ho_{1}. w_{5}+ho_{2} .w_{7}+b_{3} o1=ho1.w5+ho2.w7+b3
       o 2 = h o 1 . w 6 + h o 2 . w 8 + b 4 o_{2} = ho_{1} .w_{6}+ho_{2}. w_{8}+b_{4} o2=ho1.w6+ho2.w8+b4
      神经元O1的输出oo1,神经元O2的输出oo2:
       o o 1 = 1 1 + e o 1 oo_{1}=\frac{1}{1+e^{o_{1}}} oo1=1+eo11
       o o 2 = 1 1 + e o 2 oo_{2}=\frac{1}{1+e^{o_{2}}} oo2=1+eo21

2. 反向传播

  1. 计算总误差
      使用均方误差为总误差(square error),设为E,分别计算O1和O2的误差,求和:
       E o 1 = 1 2 ( y 1 − o o 1 ) 2 Eo_{1}=\frac{1}{2}(y_{1}-oo_{1})^{2} Eo1=21(y1oo1)2
       E o 2 = 1 2 ( y 2 − o o 2 ) 2 Eo_{2}=\frac{1}{2}(y_{2}-oo_{2})^{2} Eo2=21(y2oo2)2
       E = E o 1 + E o 2 E = Eo_{1} + Eo_{2} E=Eo1+Eo2
  2. 隐含层 → \rightarrow 输出层的权值更新:
    以权重参数w5为例,如果我们想知道w5对整体误差产生了多少影响,可以用整体误差对w5求偏导求出:(链式法则)
       ∂ E ∂ w 5 = ∂ E ∂ o o 1 ⋅ ∂ o o 1 ∂ o 1 ⋅ ∂ o 1 ∂ w 5 \frac{\partial E}{\partial w_{5}}=\frac{\partial E}{\partial oo_{1}}\cdot \frac{\partial oo_{1}}{\partial o_{1}}\cdot \frac{\partial o_{1}}{\partial w_{5}} w5E=oo1Eo1oo1w5o1

如下面的图所示,可以更直观的看清楚使用链式求导,体现误差是怎样反向传播的:
在这里插入图片描述
现在我们来分别求导三者计算每个式子:
   E = 1 2 ( y 1 − o o 1 ) 2 + 1 2 ( y 2 − o o 2 ) 2 E=\frac{1}{2} \left (y_{1} - oo_{1}\right)^{2} + \frac{1}{2} \left (y_{2} - oo_{2}\right)^{2} E=21(y1oo1)2+21(y2oo2)2
   δ E δ o o 1 = 2 ⋅ 1 2 ( y 1 − o o 1 ) 2 − 1 ⋅ − 1 + 0 \frac{\delta E}{\delta oo_{1}}=2 \cdot \frac {1}{2} \left(y_{1} - oo_{1}\right)^{2-1}\cdot-1+0 δoo1δE=221(y1oo1)211+0
   δ E δ o o 1 = − ( y 1 − o o 1 ) \frac{\delta E}{\delta oo_{1}}=- \left(y_{1} - oo_{1}\right) δoo1δE=(y1oo1)
  已下中间求导过程略。
   δ o o 1 δ o 1 = o o 1 ( 1 − o o 1 ) \frac{\delta oo_{1}}{\delta o_{1}}=oo_{1} \left(1 - oo_{1}\right) δo1δoo1=oo1(1oo1)
   δ o 1 δ w 5 = h o 1 \frac{\delta o_{1}}{\delta w_{5}}=ho_{1} δw5δo1=ho1
  三者相乘的结果如下:
   ∂ E ∂ w 5 = − ( y 1 − o o 1 ) ⋅ o o 1 ⋅ ( 1 − o o 1 ) ⋅ h o 1 \frac{\partial E}{\partial w_{5}}=-(y_{1} - oo_{1}) \cdot oo_{1} \cdot (1-oo_{1})\cdot ho_{1} w5E=(y1oo1)oo1(1oo1)ho1

为了表达方便,用 δ o 1 \delta o_{1} δo1来表示输出层的误差:
   δ o 1 = ∂ E ∂ o o 1 ⋅ ∂ o o 1 ∂ h o 1 = ∂ E ∂ h o 1 \delta o_{1}= \frac{\partial E}{\partial oo_{1}}\cdot \frac{\partial oo_{1}}{\partial ho_{1}} = \frac{\partial E}{\partial ho_{1}} δo1=oo1Eho1oo1=ho1E
   δ o 1 = − ( y 1 − o o 1 ) ⋅ o o 1 ( 1 − o o 1 ) \delta o_{1}= -\left ( y_{1} - oo_{1} \right ) \cdot oo_{1}\left ( 1- oo_{1} \right ) δo1=(y1oo1)oo1(1oo1)
  因此,整体误差E对w5的偏导公式可以写成:
   ∂ E ∂ w 5 = δ o 1 ⋅ h o 1 \frac{\partial E}{\partial w_{5}} = \delta o_{1}\cdot ho_{1} w5E=δo1ho1

假如以 w 5 ( 1 ) w_{5}^{(1)} w5(1)表示反向传播更新w5的值。
   w 5 ( 1 ) = w 5 − η ⋅ ∂ E ∂ w 5 w_{5}^{(1)}=w_{5} - \eta \cdot \frac{\partial E}{\partial w_{5}} w5(1)=w5ηw5E

其中, η \eta η是学习速率。同理,可更新w6,w7,w8。

  1. 隐含层 → \rightarrow 隐含层的权值更新:
    推导过程方法与上面描述原理基本一致,但是有个点需是不一样的,在上文计算总误差对w5的偏导时,是从 o o 1 → h o 1 → w 5 oo_{1} \rightarrow ho_{1} \rightarrow w_{5} oo1ho1w5,但是在隐含层之间的权值更新时,过程是 h o 1 → h 1 → w 1 ho_{1}\rightarrow h_{1} \rightarrow w_{1} ho1h1w1,而 h o 1 ho_{1} ho1会接受 E o 1 Eo_{1} Eo1 E o 1 Eo_{1} Eo1两个地方传来的误差,如下图所示,因此,这个点两个都要计算。
    在这里插入图片描述
    首先,计算 ∂ E ∂ h o 1 \frac{\partial E}{\partial ho_{1}} ho1E
       ∂ E ∂ h o 1 = ∂ E o 1 ∂ h o 1 + ∂ E o 2 ∂ h o 1 \frac{\partial E}{\partial ho_{1}} = \frac{\partial Eo_{1}}{\partial ho_{1}} + \frac{\partial Eo_{2}}{\partial ho_{1}} ho1E=ho1Eo1+ho1Eo2

需要分别计算 ∂ E o 1 ∂ h o 1 \frac{\partial Eo_{1}}{\partial ho_{1}} ho1Eo1 ∂ E o 2 ∂ h o 1 \frac{\partial Eo_{2}}{\partial ho_{1}} ho1Eo2
   ∂ E o 1 ∂ h o 1 = ∂ E o 1 ∂ o 1 ⋅ ∂ o 1 ∂ h o 1 \frac{\partial Eo_{1}}{\partial ho_{1}}=\frac{\partial Eo_{1}}{\partial o_{1}}\cdot \frac{\partial o_{1}}{\partial ho_{1}} ho1Eo1=o1Eo1ho1o1
  其中, ∂ E o 1 ∂ o 1 = ∂ E o 1 ∂ o o 1 ⋅ ∂ o o 1 ∂ o 1 \frac{\partial Eo_{1}}{\partial o_{1}}=\frac{\partial Eo_{1}}{\partial oo_{1}}\cdot \frac{\partial oo_{1}}{\partial o_{1}} o1Eo1=oo1Eo1o1oo1
  并且,上式中的内容,在前面已经计算过来,直接引用。(注: ∂ E o 1 ∂ o o 1 \frac{\partial Eo_{1}}{\partial oo_{1}} oo1Eo1 ∂ E ∂ o o 1 \frac{\partial E}{\partial oo_{1}} oo1E的结果是一样的?因为对 o o 1 oo_{1} oo1求导,则涉及到 E o 2 Eo_{2} Eo2 o o 2 oo_{2} oo2部分都为0)
   o 1 = w 5 ⋅ h o 1 + w 6 ⋅ h o 2 + b 2 ⋅ 1 o_{1} = w_{5} \cdot ho_{1} + w_{6} \cdot ho_{2} + b_{2}\cdot 1 o1=w5ho1+w6ho2+b21
   ∂ o 1 ∂ h o 1 = w 5 \frac{\partial o_{1}}{\partial ho_{1}}=w_{5} ho1o1=w5
  
  同理,可以求得 ∂ E o 2 ∂ h o 1 = ∂ E o 2 ∂ o o 2 ⋅ ∂ o o 2 ∂ h o 1 \frac{\partial Eo_{2}}{\partial ho_{1}}=\frac{\partial Eo_{2}}{\partial oo_{2}}\cdot \frac{\partial oo_{2}}{\partial ho_{1}} ho1Eo2=oo2Eo2ho1oo2
  
  接着,计算 ∂ h o 1 ∂ h 1 \frac{\partial ho_{1}}{\partial h_{1}} h1ho1
   h o 1 = 1 1 + e − h 1 ho_{1}=\frac{1}{1+e^{ -h_{1}}} ho1=1+eh11
   ∂ h o 1 ∂ h 1 = h o 1 ⋅ ( 1 − h o 1 ) \frac{\partial ho_{1}}{\partial h_{1}}=ho_{1} \cdot (1 - ho_{1}) h1ho1=ho1(1ho1)
  
  接着,再计算 ∂ h 1 ∂ w 1 \frac{\partial h_{1}}{\partial w_{1}} w1h1
   h 1 = w 1 ⋅ i n 1 + w 2 ⋅ i n 2 + b 1 ⋅ 1 h_{1}=w_{1} \cdot in_{1} + w_{2} \cdot in_{2} + b_{1} \cdot 1 h1=w1in1+w2in2+b11
   ∂ h 1 ∂ w 1 = i n 1 \frac{\partial h_{1}}{\partial w_{1}}=in_{1} w1h1=in1
  最后,三者相乘:  
   ∂ E ∂ w 1 = ∂ E ∂ h o 1 ⋅ ∂ h o 1 ∂ h 1 ⋅ ∂ h 1 ∂ w 1 \frac{\partial E}{\partial w_{1}}=\frac{\partial E}{\partial ho_{1}}\cdot \frac{\partial ho_{1}}{\partial h_{1}}\cdot \frac{\partial h_{1}}{\partial w_{1}} w1E=ho1Eh1ho1w1h1
  为了简化公式,使用 s i g m a ( h 1 ) sigma(h_{1}) sigma(h1)表示隐含层单元h1的误差:
   ∂ E ∂ w 1 = ( ∑ o = 1 n ∂ E ∂ o o ⋅ ∂ o o ∂ o ⋅ ∂ o ∂ h o 1 ) ⋅ ∂ h o 1 ∂ h 1 ⋅ ∂ h 1 ∂ w 1 \frac{\partial E}{\partial w_{1}}= (\sum_{o=1}^{n} \frac{\partial E}{\partial oo} \cdot \frac{\partial oo}{\partial o} \cdot \frac{\partial o}{\partial ho_{1}}) \cdot \frac{\partial ho_{1}}{\partial h_{1}}\cdot \frac{\partial h_{1}}{\partial w_{1}} w1E=(o=1nooEoooho1o)h1ho1w1h1
   ∂ E ∂ w 1 = ( ∑ o = 1 n δ o ⋅ w h o ) ⋅ h o 1 ⋅ ( 1 − h o 1 ) ⋅ i n 1 \frac{\partial E}{\partial w_{1}}=(\sum_{o=1}^{n} \delta_o \cdot w_{ho})\cdot ho_{1} \cdot (1-ho1)\cdot in_{1} w1E=(o=1nδowho)ho1(1ho1)in1
   ∂ E ∂ w 1 = δ h 1 ⋅ i n 1 \frac{\partial E}{\partial w_{1}}=\delta_{h_{1}}\cdot in_{1} w1E=δh1in1
  
  通过上面的推导过程,更新w1的权值: 
   w 1 ( 1 ) = w 1 − η ⋅ ∂ E ∂ w 1 w_{1}^{(1)}=w_{1} - \eta \cdot \frac{\partial E}{\partial w_{1}} w1(1)=w1ηw1E
  同理,其他按此方式更w2、w3、w4。

3. BP神经网络代码(Tensorflow)

我们使用随机梯度下降算法和MNIST训练数据,来写一个能够学习识别手写数字的BP神经网络程序。首先我们需要获得MNIST数据。

import tensorflow as tf 
from  tensorflow.examples.tutorials.mnist  import  input_data
import numpy as np 

mnist = input_data.read_data_sets('./data/', one_hot = True)

num_classes = 10  # 输出大小
input_size = 784  # 输入大小
hidden_units_size = 30  # 隐藏层节点数量
batch_size = 100
training_iterations = 10000

X = tf.placeholder(tf.float32, shape = [None, input_size])
Y = tf.placeholder(tf.float32, shape = [None, num_classes])

W1 = tf.Variable(tf.random_normal ([input_size, hidden_units_size], stddev = 0.1))
B1 = tf.Variable(tf.constant (0.1), [hidden_units_size])
W2 = tf.Variable(tf.random_normal ([hidden_units_size, num_classes], stddev = 0.1))
B2 = tf.Variable(tf.constant (0.1), [num_classes])

hidden_opt = tf.matmul(X, W1) + B1  # 输入层到隐藏层正向传播
hidden_opt = tf.nn.relu(hidden_opt)  # 激活函数,用于计算节点输出值
final_opt = tf.matmul(hidden_opt, W2) + B2  # 隐藏层到输出层正向传播


# 对输出层计算交叉熵损失
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=Y, logits=final_opt))
# 梯度下降算法,这里使用了反向传播算法用于修改权重,减小损失
opt = tf.train.GradientDescentOptimizer(0.05).minimize(loss)
# 初始化变量
init = tf.global_variables_initializer()
# 计算准确率
correct_prediction =tf.equal (tf.argmax (Y, 1), tf.argmax(final_opt, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range (training_iterations) :
        batch = mnist.train.next_batch (batch_size)
        batch_input = batch[0]
        batch_labels = batch[1]
        # 训练
        training_loss = sess.run ([opt, loss], feed_dict = {X: batch_input, Y: batch_labels})
        if i % 1000 == 0 :
            train_accuracy = accuracy.eval (session = sess, feed_dict = {X: batch_input,Y: batch_labels})
            print ("step : %d, training accuracy = %g " % (i, train_accuracy))

总结

在一般的BP神经网络中,单个样本有m个输入和n个输出,在输入层和输出层之间还有若干个隐藏层h,实际上 1989年时就已经有人证明了一个万能逼近定理 :

在任何闭区间的连续函数都可以用一个隐藏层的BP神经网络进行任意精度的逼近。
所以说一个三层的神经网络就可以实现一个任意从m维到n维的一个映射,这三层分别是 输入层、隐藏层、输出层 。

一般来说,在BP神经网络中,输入层和输出层的节点数目都是固定的,关键的就是在于隐藏层数目的选择,隐藏层数目的选择决定了神经网络工作的效果。

这里有一个选择隐藏层数目的经验公式: h = ( m + n ) + a h=\sqrt{(m+n)+a} h=(m+n)+a

  • h 隐藏层
  • m 输入层
  • n 输出层
  • a 调节常数数(一般介于1~10之间,如果数据多的话我们可以设a稍微大一点,而数据不是太多的时候就设置的小一点防止过拟合。)

BP神经网络的优缺点,以及应用介绍:

  • 优点:具有任意复杂的模式分类能力和优良的多维函数映射能力,解决了简单感知器不能解决的异或或者一些其他的问题。从结构上讲,BP神经网络具有输入层、隐含层和输出层;从本质上讲,BP算法就是以网络误差平方目标函数、采用梯度下降法来计算目标函数的最小值。基本BP算法包括信号的前向传播和误差的反向传播两个过程。

  • 缺点:
    ①学习速度慢,即使是一个简单的过程,也需要几百次甚至上千次的学习才能收敛。
    ②容易陷入局部极小值。
    ③网络层数、神经元个数的选择没有相应的理论指导。
    ④网络推广能力有限。

  • 应用:
    ①函数逼近。
    ②模式识别。
    ③分类。
    ④数据压缩。

参考:
《A Step by Step Backpropagation Example》 Matt Mazur 2015.03
《一文弄懂神经网络中的反向传播法——BackPropagation》 Charlotte77 2016.06

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肖永威

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值