BP神经网络入门级简介
2017-08-01 18:32
218 查看
1. BP神经网络概述
神经网络是一类能够模拟任何函数的一个非线性映射。先简单举个例子,假设有一个神经网络仅由一个神经元构成:神经网络采用的是给每个X1,X2,X3,X4加上一定的权值W1,W2,W3,W4,因此组合的线性输出为:
hW,b(x)=WTXW+b
hW,b(x)是一个标量
因此无论网络如何复杂,输出都会是一个关于输出的现象组合。为了模拟好非线性函数,需要加上一个激活函数:
f(z)=11+e−z
则:
f′(z)=e−z(1+e−z)2=f(z)(1−f(z))
假设我们的训练数据集为:
此时fW,b(x)=f(WTX+b),令g(x)=11+e−z,此时的损失函数为:
J(W,b)=12∑i=1n(fW,b(x(i))−y(i))2++λ2K∑k∈Kw2ji
分别对W,b求偏导,得:
∂J∂wj∂f∂bj=∑i=1ng(wix(i)+b−y(i))g(wix(i)+b)(1−g(wix(i)+b))⋅x(i)j+λKwji=∑i=1ng(wix(i)+b−y(i))g(wix(i)+b)(1−g(wix(i)+b))
后面的正则项是求所有权值的平方和的均值,K为权值的总的数目,根据梯度下降算法:
wj=wj−α∂J∂wjbj=bj−α∂J∂bj
代码的简单实现为:
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(l−1)ija(l−1)j+b(l−1)i=11+e−z(l)i
设最终的输出值为:h(W,b)(x),实际的值为y,则损失函数为:
J(W,b)=12(hW,b(x)−y)2+λ2K∑k∈Kw2ij
后面的为正则项,其中K表示所有权重的数目,正则项即时对所有权重求加权平方和的均值。
BP神经网络的步骤为:
1. 随机初始化权值W和b
2. 通过前向传播的网络,计算在当前权重和偏置的情况下的输出hw,b(x)以及中间每一层对应的输出a(l)
3. 对于输出层,定义
δ(L)i=∑j=1m∂12(a(L)i−y)2∂aLi=a(L)i−y
后文中,为了方便书写,去掉了求和符号,均假设只有一个样本。多个样本的话,只需叠加即可。
4,对于第L−1层,有:
δ(L−1)i=∂Jw,b(z(L−1)i)∂a(L−1)i=∑k=1m∑j=1n∂Jjw,b(aL−1i)∂aL−1i=∑k=1m∑j=1n∂[12(f(w(L−1)ijaL−1i+b(L)j+⋯)−yj]2∂a(L−1)i=∑k=1m∑j=1n[f(w(L−1)ijaL−1i+b(L)j+⋯)−y]⋅∂[f(w(L−1)ijaL−1i+b(L)j+⋯)−y]∂a(L−1)i=∑k=1m∑j=1n(a(L)j−yj)⋅f′(a(L)j)⋅w(L−1)ij=∑k=1m∑j=1n(a(L)j−yj)⋅a(L)j⋅(1−a(L)j)⋅wL−1ij=∑k=1m∑j=1nδ(L)j⋅a(L)j⋅(1−a(L)j)⋅wL−1ij
5, 更新第L−1层到第L层的权重w(L−1)
∂Jw,b(a(L−1)i)∂wji加上正则项以后的偏导数为:∂Jw,b(a(L−1)i)∂wjiw(L−1)ji=∑k=1n∂Jjw,b(a(L−1)i)∂a(L)j⋅∂a(L)j∂wji=(a(L)j−y)⋅a(L)j⋅(1−a(L)j)⋅a(L−1)i注意这里去掉了求和符号=δ(L)j⋅a(L)j⋅(1−a(L)j)⋅a(L−1)i=δ(L)j⋅a(L)j⋅(1−a(L)j)⋅a(L−1)i+λKwji=w(L−1)ji−α⋅∂Jw,b(a(L−1)i)∂wji
6,对于第l层和第l+1层之间:
∂J∂a(l)i=∑j=1n∂J∂a(l+1)j⋅∂a(l+1)j∂a(l)i
而
a(l+1)j=f(wjia(l)i+bj+...)=f(z(l+1)j+⋯)
因此
∂a(l+1)j∂a(l)i=f(z(l+1)j+⋯)⋅(1−f(z(l+1)j+⋯))⋅wji=a(l+1)j⋅(1−a(l+1)j)⋅wji
因此:
δ(L)i=∂J∂a(l)i=∑j=1n[∂J∂a(l+1)j⋅a(l+1)j⋅(1−a(l+1)j)⋅wji]=∑j=1n[δ(L+1)i⋅a(l+1)j⋅(1−a(l+1)j)⋅wji]
而对于最后一层:
∂J∂a(L)i=a(L)i−yii表示输出层的第i个节点
故:
∂J∂w(l)ji∂J∂b(l)iwjib(l)i=∂J∂a(l+1)j⋅a(l+1)j(1−a(l+1)j)a(l)i+λKwji=δ(l+1)j⋅a(l+1)j(1−a(l+1)j)a(l)i+λKwji=∂J∂a(l+1)j⋅a(l+1)j(1−a(l+1)j)=δ(l+1)j⋅a(l+1)j(1−a(l+1)j)=wji−α⋅∂J∂wji=b(l)i−α⋅∂J∂b(l)i
注意,这里:
δ(l)j=∂J∂a(l)j
由此可以看出,要想求出每一层的权重的偏导数∂J∂wji,需要先求出误差对于后面一层所有节点的的输出值a(l+1)j的偏导数,因此总共需要3个变量来分别存储a(l)i、∂J∂a(l)i、w(l)ji,其中a(l)i、∂J∂a(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. 初始化权重和偏置的时候,不能都设为一样的,应该要随机初始化,否则可能造成权重完全相同,例如:
这是截取的网络的一部分,此时如果输出层的两个神经元相等,则以后的迭代过程当中,这两个神经元相连的前面一层的权值也相等,如果往前回溯,由于:
∂J∂w(l)ji=δ(l+1)ja(l+1)j(1−a(l+1)j)a(l)i
会造成偏导相等,从而权值也会呈现对称的特征,因此可能导致网络性能受到影响。
2. 对于求导的检查
神经网络的计算过程中,求导过程比较复杂,为了检验是否出错,可以用如下方法检验:
对于其上某点(xi,yi)点的导数近似等于:
∂f(x)∂x≈f(x+ϵ)−f(x−ϵ)2ϵ
此处ϵ取尽可能小的值,例如10−6。因此对于神经网络,可以:
∂J(wji)∂wji≈J(wji+ϵ)−J(wji−ϵ)2ϵϵ=10−6
当发现精确值和近似值相近时,说明导数计算正确,解除检验操作即可。
相关文章推荐
- 最简单的Hibernate入门简介
- NSURLSession简介与入门
- C++模板元编程 入门简介
- [Kafka]Kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)
- kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)
- kafka入门:简介、使用场景、设计原理、主要配置及集群搭建
- 【Facebook的UI开发框架React入门之八】Image的使用简介(iOS平台)-goodmao
- Scala基础入门(一)Scala 简介
- Golang seelog 使用入门简介
- kafka入门:简介、使用场景、设计原理、主要配置及集群搭建
- kafka入门:简介、使用场景、设计原理、主要配置及集群搭建
- python入门书籍简介
- 01.ENVI产品简介与入门
- 业务规则入门简介
- Storm入门(十二)Twitter Storm: DRPC简介
- BT中的Tracker 简介 (P2P入门)
- PsychoPy入门_00_简介
- GBDT(MART) 迭代决策树入门教程 | 简介
- Kotlin 简介与入门指南
- Gradle Gradle入门系列(1):简介