参考链接:
https://www.jianshu.com/p/964345dddb70
https://www.cnblogs.com/jsfantasy/p/12177216.html
花了一天学习反向传播,记录一下,以便后面回顾。
- 反向传播概念:前向传递输入信号直至输出产生误差,反向传播误差信息更新权重矩阵。类似于反馈系统,通过输出来影响网络各个层的权值和偏置。
- 数学约定:
按照图示数学符号网络的对网络进行描述,以便进行公示推到。下图展示了从输出层反向传播推导出第一层的权值,偏置也是类似的。
激活函数采用sigmoid函数,函数的定义与求导如图。
单个神经元推到后,我们要进行总结,以便编程使用。下图为总结的公式
BP1:
面向输出层,C为损失函数,前面部分代表损失函数对输出层输出值的求导,后面部分代表输出层输入激活函数的求导,两者在进行对应元素相乘
BP2:
面向隐藏层,前面部分代表后一层的权值于后一层的后一层的误差的矩阵乘法,后面部分代表该层输入激活函数的求导,两者在进行对应元素相乘
BP3:
表示第L层偏置的导数等于第L层的误差
BP4:
表示第L层权值的导数等于前一层输出值与第L层的矩阵乘法
注意区分矩阵乘法与对应元素相乘的区别
仔细观察,你会发现BP1与BP2相结合就能发挥出最大功效,可以计算出任意层的误差,只要首先利用BP1公式计算出输出层误差,然后利用BP2层层传递,就无敌了,这也正是误差反向传播算法的缘由吧。同时对于权重w以及偏置b我们就可以通过BP3和BP4公式来计算了。
梯度下降公式:
关键代码赏析(反向传播部分):
def backpropagation(self, X, y, learning_rate):
# 反向传播算法实现
output = self.feed_forward(X)# 向前计算,得到最终输出值
for i in reversed(range(len(self._layers))): # 反向循环
layer = self._layers[i]
if layer == self._layers[-1]: # 如果是输出层
layer.error = y - output # 这里直接用真实值减去预测值,没有求导
#对应公式BP1
layer.delta = layer.error * layer.apply_activation_derivative(output)
else: # 如果是隐藏层
next_layer = self._layers[i + 1]
layer.error = np.dot(next_layer.weights, next_layer.delta)
#对应公式BP2
layer.delta = layer.error * layer.apply_activation_derivative(layer.activation_output)
# 循环更新权值
for i in range(len(self._layers)):
layer = self._layers[i]
# o_i 为上一网络层的输出
o_i = np.atleast_2d(X if i == 0 else self._layers[i - 1].activation_output)
# 梯度下降公式+BP4公式,delta 是公式中的负数,故这里用加号
layer.weights += layer.delta * o_i.T * learning_rate