1654 字
8 分钟
深度神经网络 - DNN

前言#

深度神经网络(DNN)12 是最基本的神经网络之一,人工神经网络(ANN)BP神经网络全连接神经网络 都在它在不同时期、不同领域等方面下的别称(主要是从不同实现方法上进行命名的,本质都差不多)。

本次任务是使用Python还原深度神经网络,并且不使用任何深度学习框架,只使用numpy包进行矩阵运算。


训练数据与处理#

这次使用的训练数据是 某地区20年的公路运量相关数据3,经典。

任务是根据人数、机动车辆、公路面积对公路客运量和运货量进行预测。

# 年份
years = [1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009]
# 人数(万人)
population = [20.55, 22.44, 25.37, 27.13, 29.45, 30.10, 30.96, 34.06, 36.42, 38.09, 39.13, 39.99, 41.93, 44.59, 47.30, 52.89, 55.73, 56.76, 59.17, 60.63]
# 机动车数(万辆)
vehicle = [0.6, 0.75, 0.85, 0.9, 1.05, 1.35, 1.45, 1.6, 1.7, 1.85, 2.15, 2.2, 2.25, 2.35, 2.5, 2.6, 2.7, 2.85, 2.95, 3.1]
# 公路面积(万平方公里)
roadarea = [0.09, 0.11, 0.11, 0.14, 0.20, 0.23, 0.23, 0.32, 0.32, 0.34, 0.36, 0.36, 0.38, 0.49, 0.56, 0.59, 0.59, 0.67, 0.69, 0.79]
# 公路客运量(万人)
passengertraffic = [5126, 6217, 7730, 9145, 10460, 11387, 12353, 15750, 18304, 19836, 21024, 19490, 20433, 22598, 25107, 33442, 36836, 40548, 42927, 43462]
# 公路货运量(万吨)
freighttraffic = [1237, 1379, 1385, 1399, 1663, 1714, 1834, 4322, 8132, 8936, 11099, 11203, 10524, 11115, 13320, 16762, 18673, 20724, 20803, 21804]

将数据进行 最大最小归一法 处理,将离散数据限定在一个范围,使数据更规范,提高 梯度下降算法 的效率,同时避免 数据溢出

x=xmin(x)max(x)min(x)x{'}=\frac{x-min(x)}{max(x)-min(x)}


网络结构#

如图所示,网络分为输入层、隐藏层和输出层。

输入层 是对数据的一个简单调用过程,很多时候算网络层的时候会进行忽略。我这个网络单次输入需要人数、机动车辆、公路面积3个数据,所有包含有3个节点。

隐藏层 是对数据进行复杂运算的过程,通过权重与偏置,经过多层复杂运算,提取出在数据中的特征。这里我只设立了1层隐藏层,该隐藏层中包含有8个节点。

输出层 是对隐藏层运算结果的输出过程,需要将运算结果输出到指定格式。这里我图上标了2个节点,这两个节点生成的结果就是网络根据输入获得的预测结果。


网络推导过程#

前向传播#

前向传播 就是将输入参数按网络的构造顺序逐步进行计算,最后得到的结果就是网络的预测或判断结果,简单来说就是一个通过输入参数进行结果预测的过程。

输入层#

确定输入向量 a0a_0 = [[20.55], [0.6], [0.09]]

  • a0a_0 既可以看作是输入层的输入,也可以看作是输入层的输出。

隐藏层#

STEP1: 确定净输入(未经激活):

z1=w1a0+b1z_1=w_1 \cdot a_0+b_1

  • w1w_1 是隐藏层的权重矩阵 weight,矩阵大小为(8,3)
  • b1b_1 是隐藏层的偏置矩阵 bias,矩阵大小为(8,1)
  • z1z_1 是隐藏层的净输入,矩阵大小为(8,1)

从这里可以看出,其实BP神经网络中每个节点就是一个 线性回归(Linear Regression) 问题,即:

y=w1x1+w2x2+...+by=w_1x_1+w_2x_2+...+b

STEP2: 确定隐藏层的输出,即通过 激活函数 进行激活,我选择的是 Sigmoid函数(逻辑函数)

x=11+exx{'}=\frac{1}{1+e^{-x}}

激活方式则如下所示:

a1=Sigmoid(z1)a_1=Sigmoid(z_1)

  • a1a_1 是隐藏层通过激活函数后的输出,矩阵大小为(8,1)

在我的理解中,激活函数主要功能有两个,分别是局限或限制住输出大小保留有效信息

输出层#

这里我没有再添加激活函数,所有输出层的净输入就是输出:

z2=w2a1+b2z_2=w_2 \cdot a_1+b_2

  • w2w_2 是输出层的权重矩阵,矩阵大小为(2,8)
  • b2b_2 是输出层的偏置矩阵,矩阵大小为(2,1)
  • z2z_2 是输出层的净输入和实际输出,矩阵大小为(2,1)

损失函数/代价函数#

训练过程中,在完成推理后,需要计算 预期值实际值 之间的误差,也被称为 损失函数4,这里使用的是最基础的方法,L2 Loss(最小二乘法)

e=1n×in(yi^yi)2e = \frac{1}{n} \times \sum_i^n({\hat{y_i} - y_i})^2

  • nn 是样本量
  • y^\hat{y} 是预期值/期望值
  • yy 是实际值
  • ee 是预期值与实际值之间的误差

其他常见的损失函数有 绝对值损失log对数损失指数损失交叉熵损失 等等。

反向传播#

梯度下降#

反向传播 是训练过程中的一个步骤,根据损失函数计算不同层之间的梯度,然后通过梯度更新之前的权重和偏置,而 梯度计算 的方法就是 链式求导,整个过程也被称为 反向传播梯度下降

更新权重和偏置 的方法如下:

λ=λ+δλ\lambda{'}= \lambda + {\delta \lambda}

由于 反向传播的目的是使损失函数最小,所以从负方向出发梯度下降速率是最快的,即:

δλ=ζ×eλ=ζ×ey^×y^λ{\delta \lambda} = -\zeta \times \frac{\partial{e}}{\partial{\lambda}} = -\zeta \times \frac{\partial{e}}{\partial{\hat{y}}} \times \frac{\partial{\hat{y}}}{\partial{\lambda}}

  • ζ\zeta 是学习率
  • λ\lambda 是权重或偏置

ey^\frac{\partial{e}}{\partial{\hat{y}}},是损失函数对预期值进行求导,即:

\begin{equation} \frac{\partial{e}}{\partial{\hat{y}}} = \hat{y} - y \end{equation}

注意:L2Loss 中原先系数是 1n\frac{1}{n},得到结果应该是 2n(y^y)\frac{2}{n}(\hat{y}-y),但我了解到,大多数时候为了运算方便,会使 n=2n=2,使系数为 11,只会对下降速度有所影响,等于是只改变了学习率。

剩下的就是,开导!

权重和偏置#

需要更新的是每层的 权重矩阵偏置矩阵,因此需要计算 Ez\frac{\partial{E}}{\partial{z}}Eb\frac{\partial{E}}{\partial{b}},即实际结果与各层权重矩阵和偏置矩阵的梯度(导数)。

z2z_2的输出梯度变化Ez2\frac{\partial{E}}{\partial{z_2}},需要输出损失对 y^\hat{y} 的偏导:

Ez2=Lossz2=y^y\frac{\partial{E}}{\partial{z_2}} = \frac{\partial{Loss}}{\partial{z_2}} = \hat{y} - y

w2w_2的梯度变化Ew2\frac{\partial{E}}{\partial{w_2}}

Ew2=Ez2×z2w2=Ez2a1T\frac{\partial{E}}{\partial{w_2}} = \frac{\partial{E}}{\partial{z_2}} \times {\frac{\partial{z_2}}{\partial{w_2}}} = \frac{\partial{E}}{\partial{z_2}} \cdot {a_1}^T

b2b_2的梯度变化Eb2\frac{\partial{E}}{\partial{b_2}}

Eb2=Ez2×z2b2=Ez2×1\frac{\partial{E}}{\partial{b_2}} = \frac{\partial{E}}{\partial{z_2}} \times {\frac{\partial{z_2}}{\partial{b_2}}} = {\frac{\partial{E}}{\partial{z_2}}} \times 1

z1z_1的输出梯度变化Ez1\frac{\partial{E}}{\partial{z_1}},中间还需要求一层 Sigmoid 的导数:

Sigmoid(x)x=x×(1x)\frac{\partial{Sigmoid(x)}}{\partial{x}}= x \times (1 - x)

Ez1=Ez2×z2a1×a1z1=w2TEz2Sigmoid(z1)z1\frac{\partial{E}}{\partial{z_1}} = \frac{\partial{E}}{\partial{z_2}} \times {\frac{\partial{z_2}}{\partial{a_1}}} \times {\frac{\partial{a_1}}{\partial{z_1}}} = {w_2}^T \cdot \frac{\partial{E}}{\partial{z_2}} * \frac{\partial{Sigmoid(z_1)}}{\partial{z_1}}

w1w_1的梯度变化Ew1\frac{\partial{E}}{\partial{w_1}}

Ew1=Ez1×z1w1=Ez1a0T\frac{\partial{E}}{\partial{w_1}} = \frac{\partial{E}}{\partial{z_1}} \times {\frac{\partial{z_1}}{\partial{w_1}}} = \frac{\partial{E}}{\partial{z_1}} \cdot {a_0}^T

b1b_1的梯度变化Eb1\frac{\partial{E}}{\partial{b_1}}

Eb1=Ez1×z1b1=Ez1×1\frac{\partial{E}}{\partial{b_1}} = \frac{\partial{E}}{\partial{z_1}} \times {\frac{\partial{z_1}}{\partial{b_1}}} = \frac{\partial{E}}{\partial{z_1}} \times 1

以上就是各层梯度变化的推导过程!

Footnotes#

  1. 神经网络基础原理 - 08 其他网络结构

  2. BP神经网络原理及编程实现

  3. 预测分析 Python BP神经网络模型 公路运量预测(学习笔记)

  4. 【线性回归、代价函数、损失函数】动画讲解

深度神经网络 - DNN
https://fuwari.vercel.app/posts/人工智能/神经网络/深度神经网络-dnn/
作者
Asuwee
发布于
2022-06-03
许可协议
CC BY-NC-SA 4.0