BP神经网络入门级简介

1. BP神经网络概述

神经网络是一类能够模拟任何函数的一个非线性映射。先简单举个例子,假设有一个神经网络仅由一个神经元构成:

这里写图片描述

神经网络采用的是给每个 X1,X2,X3,X4 加上一定的权值 W1,W2,W3,W4 ,因此组合的线性输出为:

hW,b(x)=WTXW+b

hW,b(x) 是一个标量

因此无论网络如何复杂,输出都会是一个关于输出的现象组合。为了模拟好非线性函数,需要加上一个激活函数:

f(z)=11+ez

则:
f(z)=ez(1+ez)2=f(z)(1f(z))

假设我们的训练数据集为:
这里写图片描述

此时 fW,b(x)=f(WTX+b) ,令 g(x)=11+ez ,此时的损失函数为:

J(W,b)=12i=1n(fW,b(x(i))y(i))2++λ2KkKw2ji

分别对 W,b 求偏导,得:
Jwjfbj=i=1ng(wix(i)+by(i))g(wix(i)+b)(1g(wix(i)+b))x(i)j+λKwji=i=1ng(wix(i)+by(i))g(wix(i)+b)(1g(wix(i)+b))

后面的正则项是求所有权值的平方和的均值, K 为权值的总的数目,根据梯度下降算法:
wj=wjαJwjbj=bjαJbj

代码的简单实现为:

from numpy import exp, array, random, dot


class NeuralNetwork():
    def __init__(self):
        random.seed(1)
        # gengerate initial random W
        self.synaptic_weights = 2 * random.random((3, 1)) - 1

    def _sigmoid(self, x):
        return 1/(1+exp(-x))

    # gradient of the sigmod curve
    def _sigmoid_derivative(self, x):
        return x * (1-x)

    def train(self, training_set_inputs, train_set_outputs, number_of_training_iterations):
        for iteration in range(number_of_training_iterations):
            output = self.predict(training_set_inputs)
            error = training_set_outputs - output
            adjustment = dot(training_set_inputs.T, error * self._sigmoid_derivative(output))
            # update the W value
            self.synaptic_weights += adjustment

    def predict(self, inputs):
        return self._sigmoid(dot(inputs, self.synaptic_weights))

if __name__ == "__main__":
    neural_network = NeuralNetwork()

    print("Random starting synaptic weights:")
    print(neural_network.synaptic_weights)

    training_set_inputs = array([[0, 0, 1], [1, 1, 1], [1, 0, 1], [0, 1, 1]])
    training_set_outputs = array([[1, 1, 1, 0]]).T

    neural_network.train(training_set_inputs, training_set_outputs, 10000)

    print("New synaptic weights after training")
    print(neural_network.synaptic_weights)

    print("predicting:")
    print(neural_network.predict(array([1, 0, 0])))

以上就是BP神经网络的简单介绍。

2. BP神经网络里面的权值迭代

假设有下面这样一个4层的神经网络模型(作为多个隐藏层的代表):
这里写图片描述
先给定下面几个字母代号:
Ln 表示第 n 层,如L1表示输入层。 W(l)ij 表示第 l 层的第j个节点到第 l+1 层的第 i 个节点之间的权值。zli表示第 l 层的第i个节点经过上层所有连向该节点的加权求和, a(l)i 表示第 l 层的第i个节点经过 Activationfunction 之后的输出值,即:

a(l)i=f(z(l)i)=sigmod(z(l)i)

b(l)i 表示第 l=1 层第 i 个节点的偏置,定义δ(l)i为以 a(l)i 作为输入的第 l 层的第i个节点对总体误差的影响,则有:
z(l)ia(l)i=j=1nw(l1)ija(l1)j+b(l1)i=11+ez(l)i

设最终的输出值为: h(W,b)(x) ,实际的值为 y ,则损失函数为:
J(W,b)=12(hW,b(x)y)2+λ2KkKw2ij

后面的为正则项,其中 K 表示所有权重的数目,正则项即时对所有权重求加权平方和的均值。
BP神经网络的步骤为:
1. 随机初始化权值W b
2. 通过前向传播的网络,计算在当前权重和偏置的情况下的输出hw,b(x)以及中间每一层对应的输出 a(l)
3. 对于输出层,定义
δ(L)i=j=1m12(a(L)iy)2aLi=a(L)iy

后文中,为了方便书写,去掉了求和符号,均假设只有一个样本。多个样本的话,只需叠加即可。
4,对于第 L1 层,有:
δ(L1)i=Jw,b(z(L1)i)a(L1)i=k=1mj=1nJjw,b(aL1i)aL1i=k=1mj=1n[12(f(w(L1)ijaL1i+b(L)j+)yj]2a(L1)i=k=1mj=1n[f(w(L1)ijaL1i+b(L)j+)y][f(w(L1)ijaL1i+b(L)j+)y]a(L1)i=k=1mj=1n(a(L)jyj)f(a(L)j)w(L1)ij=k=1mj=1n(a(L)jyj)a(L)j(1a(L)j)wL1ij=k=1mj=1nδ(L)ja(L)j(1a(L)j)wL1ij

5, 更新第 L1 层到第 L 层的权重w(L1)
Jw,b(a(L1)i)wjiJw,b(a(L1)i)wjiw(L1)ji=k=1nJjw,b(a(L1)i)a(L)ja(L)jwji=(a(L)jy)a(L)j(1a(L)j)a(L1)i=δ(L)ja(L)j(1a(L)j)a(L1)i=δ(L)ja(L)j(1a(L)j)a(L1)i+λKwji=w(L1)jiαJw,b(a(L1)i)wji

6,对于第 l 层和第l+1层之间:
Ja(l)i=j=1nJa(l+1)ja(l+1)ja(l)i


a(l+1)j=f(wjia(l)i+bj+...)=f(z(l+1)j+)

因此
a(l+1)ja(l)i=f(z(l+1)j+)(1f(z(l+1)j+))wji=a(l+1)j(1a(l+1)j)wji

因此:
δ(L)i=Ja(l)i=j=1n[Ja(l+1)ja(l+1)j(1a(l+1)j)wji]=j=1n[δ(L+1)ia(l+1)j(1a(l+1)j)wji]

而对于最后一层:
Ja(L)i=a(L)iyiii

故:
Jw(l)jiJb(l)iwjib(l)i=Ja(l+1)ja(l+1)j(1a(l+1)j)a(l)i+λKwji=δ(l+1)ja(l+1)j(1a(l+1)j)a(l)i+λKwji=Ja(l+1)ja(l+1)j(1a(l+1)j)=δ(l+1)ja(l+1)j(1a(l+1)j)=wjiαJwji=b(l)iαJb(l)i

注意,这里:
δ(l)j=Ja(l)j

由此可以看出,要想求出每一层的权重的偏导数 Jwji ,需要先求出误差对于后面一层所有节点的的输出值 a(l+1j 的偏导数,因此总共需要3个变量来分别存储 a(l)i Ja(l)i w(l)ji ,其中 a(l)i Ja(l)i 需要一个2维数组来存, w(l)ji 需要一个3维度数组来存。

这里有一个简单的例子,很直观。

4. TensorFlow搭建一个简单的BP神经网络

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

mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
n_nodes_hl1 = 500
n_nodes_hl2 = 500
n_nodes_hl3 = 500

n_classes = 10
batch_size = 100

x = tf.placeholder("float", [None, 784])
y = tf.placeholder("float", [None, n_classes])


def neural_network_model(data):
    # 创建第一个隐藏层节点500个
    hidden_1_layer = {"weight": tf.Variable(tf.random_normal([784, n_nodes_hl1])),
                      "biases": tf.Variable(tf.random_normal([n_nodes_hl1]))}
    # 创建第二个隐藏层节点500个
    hidden_2_layer = {"weight": tf.Variable(tf.random_normal([n_nodes_hl1, n_nodes_hl2])),
                      "biases": tf.Variable(tf.random_normal([n_nodes_hl2]))}
    # 创建第三个隐藏层节点500个
    hidden_3_layer = {"weight": tf.Variable(tf.random_normal([n_nodes_hl2, n_nodes_hl3])),
                      "biases": tf.Variable(tf.random_normal([n_nodes_hl3]))}
    # 创建输出层节点10个
    output_layer = {"weight": tf.Variable(tf.random_normal([n_nodes_hl3, n_classes])),
                      "biases": tf.Variable(tf.random_normal([n_classes]))}

    # 连接输入层
    l1 = tf.add(tf.matmul(data, hidden_1_layer["weight"]), hidden_1_layer["biases"])
    l1 = tf.nn.relu(l1)

    # 连接hidden layer 1和hidden layer 2
    l2 = tf.add(tf.matmul(l1, hidden_2_layer["weight"]), hidden_2_layer["biases"])
    l2 = tf.nn.relu(l2)

    # 连接hidden layer 2和hidden layer 3
    l3 = tf.add(tf.matmul(l2, hidden_3_layer["weight"]), hidden_3_layer["biases"])
    l3 = tf.nn.relu(l3)

    # 连接hidden layer 3和output layer
    output = tf.add(tf.matmul(l3, output_layer["weight"]), output_layer["biases"])
    # output = tf.nn.softmax(output)

    return output


def train(x):
    # 获取输出值
    predict = neural_network_model(x)
    # 定义对数损失函数
    cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=predict, labels=y))
    # 定义最优化损失的计算方法
    optimizer = tf.train.AdamOptimizer().minimize(cost)

    hm_epochs = 10

    with tf.Session() as sess:  # 真正运行在这里面
        sess.run(tf.initialize_all_variables())

        for epoch in range(hm_epochs):
            epoch_loss = 0
            for _ in range(int(mnist.train.num_examples / batch_size)):
                epoch_x, epoch_y = mnist.train.next_batch(batch_size)
                _, c = sess.run([optimizer, cost], feed_dict={x: epoch_x, y: epoch_y})
                epoch_loss += c  # 叠加所有样本的损失
            print("Epoch", epoch, "completed out of ", hm_epochs, "loss: ", epoch_loss)

        correct = tf.equal(tf.arg_max(predict, 1), tf.arg_max(y, 1))

        accuracy = tf.reduce_mean(tf.cast(correct, "float"))  # 所有元素取均值
        print("Accuracy:", accuracy.eval({x: mnist.test.images, y: mnist.test.labels}))

train(x)

注意事项:
1. 初始化权重和偏置的时候,不能都设为一样的,应该要随机初始化,否则可能造成权重完全相同,例如:
这里写图片描述
这是截取的网络的一部分,此时如果输出层的两个神经元相等,则以后的迭代过程当中,这两个神经元相连的前面一层的权值也相等,如果往前回溯,由于:

Jw(l)ji=δ(l+1)ja(l+1)j(1a(l+1)j)a(l)i

会造成偏导相等,从而权值也会呈现对称的特征,因此可能导致网络性能受到影响。
2. 对于求导的检查
神经网络的计算过程中,求导过程比较复杂,为了检验是否出错,可以用如下方法检验:
这里写图片描述
对于其上某点 (xi,yi) 点的导数近似等于:
f(x)xf(x+ϵ)f(xϵ)2ϵ

此处 ϵ 取尽可能小的值,例如 106 。因此对于神经网络,可以:
J(wji)wjiJ(wji+ϵ)J(wjiϵ)2ϵϵ=106

当发现精确值和近似值相近时,说明导数计算正确,解除检验操作即可。

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值