四个基本公式
(1). 输出层错误量的等式:
(2).依据下一层错误量
获取错误量
的等式:
(3).网络的代价函数相对于偏置的改变速率的等式:
(4).网络的代价函数相对于权重的改变速率的等式:
示例代码
class Network(object):
...
def backprop(self, x, y):#x,y是矩阵形式,可查看numpy的array
"""建立临时变量nabla_b和nabla_w来保存偏置矩阵和权重举证初识都为0矩阵"""
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
#前向传播
activation = x #感知器的激活输出初始化为输入矩阵
activations = [x] # 存放各层激活输出值的列表,第一项存入输入矩阵
zs = [] # 存放各层加权输入的列表
for b, w in zip(self.biases, self.weights):#遍历各层权重和偏置
z = np.dot(w, activation)+b #加权输入矩阵
zs.append(z)#存入列表
activation = sigmoid(z)#激活输出矩阵
activations.append(activation)#存入列表
#反向传播
delta = self.cost_derivative(activations[-1], y) * \
sigmoid_prime(zs[-1])#此处delta为输出层的错误量根据公式(1)得出,为何是cost_derivative之后会解释
nabla_b[-1] = delta #根据公式(3)
nabla_w[-1] = np.dot(delta, activations[-2].transpose())#根据公式(4)
for l in xrange(2, self.num_layers):
z = zs[-l]
sp = sigmoid_prime(z)#对z求导,激活函数是sigmod
delta = np.dot(self.weights[-l+1].transpose(), delta) * sp#根据公式(2)
nabla_b[-l] = delta#公式(3)
nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
return (nabla_b, nabla_w)#公式(4)
def cost_derivative(self, output_activations, y):
"""Return the vector of partial derivatives \partial C_x /
\partial a for the output activations."""
return (output_activations-y)
def sigmoid(z):
"""The sigmoid function."""
return 1.0/(1.0+np.exp(-z))
def sigmoid_prime(z):#可查阅求导公式
"""Derivative of the sigmoid function."""
return sigmoid(z)*(1-sigmoid(z))
可以看出该代码充分利用了这4个公式来体现反向传播算法的过程,这个反向我觉得就是先利用公式(1)计算输出层的错误量,再利用公式(3)、(4)计算该层的偏置和权重的改变速率。有了最后一层的各项数据,再利用公式(2)反向传播(所以我觉得这个公式很重要,要不然为什么叫做反向传播)计算上一层一直到得到所有权重和偏置的更新量为止。值得注意的是这里cost_derivative为什么直接使用了激活输出减去目标值,其实这一项是代价函数的导数项,并且此处使用了平方代价函数。也就是说对于一个样本来说有
对激活输出a求导其实就是 也就是函数cost_derivative定义的那样
附sigmod函数求导