- nndl是什么?
- 反向传播算法简介
- 定义&公式
- 基本思想
- Back Propagation四个基本方程
- 算法表示
- Python实现
nndl是什么?
《神经网络与深度学习》(《Neural Network and Deep Learning》)是机器学习大神
Michael Nielsen介绍神经网络入门的一本教材,英文地址:http://neuralnetworksanddeeplearning.com/在此感谢译者Xiaohu Zhu和Freeman Zhang。有对机器学习和神经网络有关内容感兴趣的读者,点这里获取相关资源。
P.S.: 本文仅对该书第二章进行总结,其中的描述会不全面,想详细了解请点击链接下载学习。
反向传播算法简介
反向传播算法(Backpropagation Algorithm),最初在二十世纪70年代被提出,但真正得到重视是在1986年。反向传播被应用于计算损失函数(代价函数)的梯度,具有稳定、速度快等特点。
定义&公式
权重(Weight) 代表从
层的第
个神经元到
层的第
个神经元的连接上的权重(注意
和
代表的意义从位置上看是反过来的,这样做是为了方便矩阵乘积直接计算而不用进行转置操作,一开始不太好理解;还有上面的层数
指的是到达层的位置,故前一层
需要减去
)。
偏置(Bias) 表示在第
层第
带权输入 :中间变量,表示
层第
个神经元的带权输入,即:
误差(Error) 中间量,表示
层第
个神经元上的误差。
Hadamard 乘积 矩阵或向量的对应元素相乘,即
激活函数(Activation Function) Sigmoid函数
其导数可表示为:
激活值(Activation) 表示第
层第
个神经元的激活值,由激活函数定义可记为:
。
层的第
个神经元的激活值
和
层的激活值之间的联系:
二次代价函数(Cost Function) :
基本思想
反向传播的目标是计算代价函数分别关于权重
和偏置
的偏导
和
- 第一个假设:代价函数可以被写成一个在每个训练样本
上的代价函数
的均值
。其原因是反向传播实际上是对一个独立的训练样本计算
和
,然后我们通过在所有训练样本上进行平均化获得
和
- 第二个假设:代价函数可以写成神经网络输出的函数,例如:
Back Propagation四个基本方程
- 输出层的误差(注意输出层使用大写L记号)
矩阵表示:
- 使用下一层的误差
来表示当前层的误差
- 代价函数关于网络中任意偏置的改变率
- 代价函数关于任何一个权重的改变率
矩阵形式:
算法表示
- 输入
:为输入层设置激活值
;
- 前向传播:对每个
计算相应的
和
;
- 输出层误差
:计算向量
;
- 反向传播误差:对每一个
计算
- 输出:代价函数的梯度
Python实现
仅反向传播部分的代码,完整代码请见该书第二章。
import numpy as np
def sigmoid(z):
return 1. / (1. + np.exp(-z))
def sigmoid_prime(z):
return sigmoid(z) * (1 - sigmoid(z))
class Network(object):
"""
省略其余方法的实现
"""
def backprop(self, x, y):
# 初始化权重向量和偏置向量,一般为零矩阵
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
# 初始化激活值以及z向量(神经元的值z组成的向量)
activation = x
activations = [x]
z_vectors = []
# 开始循环计算z向量及激活向量
for b, w in zip(self.biases, self.weights):
z = np.dot(w, activation) + b
z_vectors.append(z)
activation = sigmoid(z)
activations.append(activation)
# 开始计算输出层的误差,应用第一个方程
delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(z_vectors[-1])
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].T)
# 开始向前计算误差并存入梯度b及梯度w,应用第二个方程
for l in range(2, self.num_layers):
z = z_vectors[-l]
delta = np.dot(self.weights[-l + 1].T, delta) * sigmoid_prime(z)
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta, activations[-l-1].T)
return nabla_b, nabla_w
def cost_derivative(self, output_activations, y):
# 定义代价函数的导数(梯度),本例中使用二次代价函数,故其导数为(输出层激活向量-y)
return output_activations -