在这篇文章中,准备用 Python 从头开始实现一个全连接的神经网络。你可能会问,为什么需要自己实现,有很多库和框架可以为我们做这件事,比如 Tensorflow、Pytorch 等。这里只想说只有自己亲手实现了,才是自己的。
想到今天自己从接触到从事与神经网络相关工作已经多少 2、3 年了,其中也尝试用 tensorflow 或 pytorch 框架去实现一些经典网络。不过对于反向传播背后机制还是比较模糊。
梯度
梯度是函数上升最快方向,最快的方向也就是说这个方向函数形状很陡峭,那么也是函数下降最快的方向。
虽然关于一些理论、梯度消失和结点饱和可以输出一个 1、2、3 但是深究还是没有底气,毕竟没有自己动手去实现过一个反向传播和完整训练过程。所以感觉还是浮在表面,知其所以然而。
因为最近有一段空闲时间、所以利用这段休息时间将要把这部分知识整理一下、深入了解了解
类型 | 符号 | 说明 | 表达式 | 维度 |
---|---|---|---|---|
标量 | n^L n L | 表示第 L 层神经元的数量 | ||
向量 | B^L B L | 表示第 L 层偏置 | n^L \times 1 1 | |
矩阵 | W^L W L | 表示第 L 层的权重 | n^L \times n^L n L | |
向量 | Z^L Z L | 表示第 L 层输入到激活函数的值 | ZL=WLA^{(L-1)} + B^L W L A ( L − 1 ) + B L | n^L \times 1 1 |
向量 | A^L A L | 表示第 L 层输出值 | A^L = \sigma(Z^L) σ ( Z L ) | n^L \times 1 1 |
我们大家可能都了解训练神经网络的过程,就是更新网络参数,更新的方向是降低损失函数值。也就是将学习问题转换为了一个优化的问题。那么如何更新参数呢?我们需要计算参与训练参数相对于损失函数的导数,然后求解梯度,然后使用梯度下降法来更新参数,迭代这个过程,可以找到一个最佳的解决方案来最小化损失函数。
我们知道反向传播主要就是用来结算损失函数相对于权重和偏置的导数
可能已经听到或读到了,很多关于在网络通过反向传播来传递误差的信息。然后根据神经元的 w 和 b 对偏差贡献的大小。也就是将误差分配到每一个神经元上。 但这里的误差(error)是什么意思呢?这个误差的确切的定义又是什么?答案是这些误差是由每一层神经网络所贡献的,而且某一层的误差是后继层误差基础上分摊的,网络中第 l l 层的误差用 \delta^l δ l 来表示。
反向传播是基于 4 个基本方程的,通过这些方程来计算误差 \delta^L δ L 和损失函数,这里将这 4 个方程一一列出
\delta^{(L)} = \nabla_a C \odot \sigma{\prime}(zL) \tag{BP1}∇ aC ⊙ σ ′ ( z L ) ( B P 1 )
\delta^l = ((wl)T \delta^{l+1}) \odot \sigma{\prime}(zl) \tag{BP1}( ( w l ) T δ l + 1 ) ⊙ σ ′ ( z l ) ( B P 1 )
\frac{\partial C}{\partial b_{j}^l} = \delta_j^l \tag{BP3}∂ b j l∂ C= δ j l( B P 3 )
\frac{\partial C}{\partial w_{jk}^l} = a_k{l-1}\delta_jl \tag{BP4}∂ w j k l∂ C= a k l − 1δ j l( B P 4 )
关于如何解读这个 4 个方程,随后想用一期分享来说明。
class NeuralNetwork(object):
def __init__(self):
pass
def forward(self,x):
# 返回前向传播的 Z 也就是 w 和 b 线性组合,输入激活函数前的值
# 返回激活函数输出值 A
# z_s , a_s
pass
def backward(self,y,z_s,a_s):
#返回前向传播中学习参数的导数 dw db
pass
def train(self,x,y,batch_size=10,epochs=100,lr=0.001):
pass
我们都是神经网络学习过程,也就是训练过程。主要分为两个阶段 前向传播 和 后向传播
- 在前向传播函数中,主要计算传播的 Z 和 A,关于 Z 和 A 具体是什么请参见前面表格
- 在反向传播中计算可学习变量 w 和 b 的导数
def __init__(self,layers = [2 , 10, 1], activations=['sigmoid', 'sigmoid']):
assert(len(layers) == len(activations)+1)
self.layers = layers
self.activations = activations
self.weights = []
self.biases = []
f