1. BP神经网络概述
神经网络是一类能够模拟任何函数的一个非线性映射。先简单举个例子,假设有一个神经网络仅由一个神经元构成:
神经网络采用的是给每个
X1,X2,X3,X4
加上一定的权值
W1,W2,W3,W4
,因此组合的线性输出为:
hW,b(x) 是一个标量
因此无论网络如何复杂,输出都会是一个关于输出的现象组合。为了模拟好非线性函数,需要加上一个激活函数:
则:
假设我们的训练数据集为:
此时
fW,b(x)=f(WTX+b)
,令
g(x)=11+e−z
,此时的损失函数为:
分别对 W,b 求偏导,得:
后面的正则项是求所有权值的平方和的均值, K 为权值的总的数目,根据梯度下降算法:
代码的简单实现为:
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
层,如
b(l)i 表示第 l=1 层第 i 个节点的偏置,定义
设最终的输出值为: h(W,b)(x) ,实际的值为 y ,则损失函数为:
后面的为正则项,其中 K 表示所有权重的数目,正则项即时对所有权重求加权平方和的均值。
BP神经网络的步骤为:
1. 随机初始化权值
2. 通过前向传播的网络,计算在当前权重和偏置的情况下的输出
3. 对于输出层,定义
后文中,为了方便书写,去掉了求和符号,均假设只有一个样本。多个样本的话,只需叠加即可。
4,对于第 L−1 层,有:
5, 更新第 L−1 层到第 L 层的权重
6,对于第 l 层和第
而
因此
因此:
而对于最后一层:
故:
注意,这里:
由此可以看出,要想求出每一层的权重的偏导数 ∂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. 初始化权重和偏置的时候,不能都设为一样的,应该要随机初始化,否则可能造成权重完全相同,例如:
这是截取的网络的一部分,此时如果输出层的两个神经元相等,则以后的迭代过程当中,这两个神经元相连的前面一层的权值也相等,如果往前回溯,由于:
会造成偏导相等,从而权值也会呈现对称的特征,因此可能导致网络性能受到影响。
2. 对于求导的检查
神经网络的计算过程中,求导过程比较复杂,为了检验是否出错,可以用如下方法检验:
对于其上某点 (xi,yi) 点的导数近似等于:
此处 ϵ 取尽可能小的值,例如 10−6 。因此对于神经网络,可以:
当发现精确值和近似值相近时,说明导数计算正确,解除检验操作即可。