1.说明
BP算法的理解可以去看这一篇博客,静下心去看绝对可以看懂
2.代码
结合着上面的博客看下面的代码
数学公式都在代码中有体现
如果觉得我写的注释有点看不懂,可以自己放到机器中debug跑一下
代码是《深度学习与TensorFlow实践》这本书第5章的思考与习题的第2题的代码
from math import exp
from random import seed
from random import random
# 初始化神经网络
def initialize_network(n_inputs, n_hidden, n_outputs):
network = list()
# 初始化权值
# 这里只有一个隐含层和一个输出层,
hidden_layer = [{'weights': [random() for i in range(n_inputs + 1)]} for i in range(n_hidden)]
network.append(hidden_layer)
output_layer = [{'weights': [random() for i in range(n_hidden + 1)]} for i in range(n_outputs)]
network.append(output_layer)
print(hidden_layer)
# 设置网络层
return network
# # 计算神经元的激活值(加权之和)
def activate(weights, inputs):
activation = weights[-1]
for i in range(len(weights) - 1):
activation += weights[i] * inputs[i]
return activation
# 定义激活函数
def transfer(activation):
return 1.0 / (1.0 + exp(-activation))
# 计算神经网络的正向传播
def forward_propagate(network, row):
inputs = row
for layer in network:
new_inputs = []
for neuron in layer:
# 计算权值与输入的函数值
activation = activate(neuron['weights'], inputs)
# 将函数值激活并保存
neuron['output'] = transfer(activation)
new_inputs.append(neuron['output'])
inputs = new_inputs
return inputs
# 计算激活函数的导数
def transfer_derivative(output):
return output * (1.0 - output)
# 反向传播误差信息,并将纠偏责任存储在神经元中
# 反向传播使用的是链式法则 + 梯度下降公式
# BP更新的公式
# 总误差对W的导数 = 总误差对当前层激活函数的导数 * 当前层激活函数的导数对当前层线性函数Z的导数 * 当前层线性函数Z对W的导数
# 第一步:总误差对当前层激活函数的导数
# 第二步:当前层激活函数的导数对当前层线性函数Z的导数
# 第三步:当前层线性函数Z对W的导数
# 输出层与隐含层的第一步不同
# 1.如果更新输出层上的权值W,则总误差就是全网络的误差,即误差函数的值
# 总误差对当前层激活函数的导数 = 总误差对当前层激活函数的导数
# 2.如果更新任意隐含层的权值W,则总误差指的是与当前层节点有联系的下一层节点的误差
# 总误差对当前层激活函数的导数 = 与当前层节点有联系的下一层节点1对当前层激活函数的导数 + 与当前层节点有联系的下一层节点2对当前层激活函数的导数 + ... +与当前层节点有联系的下一层节点n对当前层激活函数的导数
# 由于我们每一层只有两个节点,所以
# 总误差对当前层激活函数的导数 = 与当前层节点有联系的下一层节点1对当前层激活函数的导数 + 与当前层节点有联系的下一层节点2对当前层激活函数的导数
# 与当前层节点有联系的下一层节点1对当前层激活函数的导数 = 与当前层节点有联系的下一层节点1对与当前层节点有联系的下一层节点1的线性函数Z的导数 * 当前层节点有联系的下一层节点1的线性函数Z对当前层激活函数的导数
# 与当前层节点有联系的下一层节点1对与当前层节点有联系的下一层节点1的线性函数Z的导数 已经 在计算输出层权值时计算过并存到 neuron['responsibility'])
# 当前层节点有联系的下一层节点1的线性函数Z对当前层激活函数的导数 就是 下一层节点1的权值
def backward_propagate_error(network, expected):
for i in reversed(range(len(network))):
layer = network[i]
errors = list()
# 如果是隐含层
if i != len(network) - 1:
# 遍历隐含层的一层
# 开始遍历某一层隐含层的所有神经节点,从第0个开始,取出一个隐含层的神经节点
for j in range(len(layer)):
error = 0.0
# 第一步
# 遍历当前层节点有联系的下一层节点,因为下一层节点的对应误差已经计算过了(这就是反向传播的真谛!)
# 计算总误差对当前层激活函数的导数
for neuron in network[i + 1]:
error += (neuron['weights'][j] * neuron['responsibility'])
errors.append(error)
# 如果是输出层
else:
# 第一步
for j in range(len(layer)):
neuron = layer[j]
errors.append(expected[j] - neuron['output'])
# 第二步
# 计算当前层激活函数对当前层线性函数Z的导数,即transfer_derivative
for j in range(len(layer)):
neuron = layer[j]
neuron['responsibility'] = errors[j] * transfer_derivative(neuron['output'])
# 根据误差,更新网络权重
def update_weights(network, row, l_rate):
for i in range(len(network)):
inputs = row[:-1]
if i != 0:
inputs = [neuron['output'] for neuron in network[i - 1]]
# 第三步 + 梯度下降
for neuron in network[i]:
for j in range(len(inputs)):
neuron['weights'][j] += l_rate * neuron['responsibility'] * inputs[j]
neuron['weights'][-1] += l_rate * neuron['responsibility']
# 根据指定的训练周期训练网络
def train_network(network, train, l_rate, n_epoch, n_outputs):
for epoch in range(n_epoch):
sum_error = 0
for row in train:
outputs = forward_propagate(network, row)
# 期望数组初始化
expected = [0 for i in range(n_outputs)]
# 期望数组的索引0、1就是对应要输出的值,其中哪个索引被置为1,则代表着这个值就是输出值
# [1, 1, 0],预期输出应该是0,所以期望数组是expected[0] = 1
expected[row[-1]] = 1
sum_error += sum([(expected[i] - outputs[i]) ** 2 for i in range(len(expected))])
backward_propagate_error(network, expected)
update_weights(network, row, l_rate)
print('>周期=%d, 误差=%.3f' % (epoch, sum_error))
def predict(network, row):
outputs = forward_propagate(network, row)
return outputs.index(max(outputs))
if __name__ == '__main__':
dataset = [[1, 1, 0],
[1, 0, 1],
[0, 1, 1],
[0, 0, 0]]
n_inputs = len(dataset[0]) - 1
n_outputs = len(set([row[-1] for row in dataset]))
network = initialize_network(n_inputs, 2, n_outputs)
train_network(network, dataset, 0.5, 20000, n_outputs)
for layer in network:
print(layer)
for row in dataset:
prediction = predict(network, row)
print('预期值=%d, 实际输出值=%d' % (row[-1], prediction))