版本对应:

本网页讨论的机器学习内容,为特定的机器学习内容(并未与CFD结合)。在《无痛苦NS方程笔记》中, 特定的将CFD与机器学习互相结合起来,无普适性机器学习内容。

ML: 手算反向传播与自动微分

在阅读本文之前,请阅读《无痛苦NS方程笔记》中的监督学习的数学算法一节。在构建神经网络后,通常采取反向传播的方法来计算对权重以及偏置的梯度。本文以一个实例来解释反向传播的梯度计算方法。

反向传播

_images/backpropagation.JPG

在上图中,假定输入层有两个变量\(x,y\),每个变量仅仅存在一个特征,\(x=3,y=4\)。隐藏层存在2个ReLU神经元。输出层的值是yIter。同时,每个神经元的权重、偏置已经给出。下面我们要通过反向传播的方法来更新权重与偏置。

首先计算神经元\(a_1\)的值:

(1)\[ a_1=\max (0, w_{11}x+w_{21}y+b_1)=\max (0, 0.9\cdot 3+0.7\cdot 4+0.5)=6 \]

然后计算神经元\(a_2\)的值:

(2)\[ a_1=\max (0, w_{12}x+w_{22}y+b_2)=\max (0, 0.8\cdot 3+0.6\cdot 4+0.5)=5.3 \]

计算预测的yIter的值:

(3)\[ y_{iter}=\max (0, w'_{1}a_1+w'_{2}a_2+b')= 2.96 \]

假定真实的\(y\)值是1,目前可以计算损失:

(4)\[ loss=\frac{1}{2}(y-y_{iter})^2=1.92 \]

反向传播主要是通过算法求解出\(\frac{\p loss}{\p w'_1}\),然后更新\(w'_1\)。这里的\(w'_1\)只是一个具体的例子,对于所有的权重都需要这样去更新。那么如何求解出\(\frac{\p loss}{\p w'_1}\)?反向传播主要使用的数学思想是链式法则:

(5)\[ \frac{\p loss}{\p w'_1}=\frac{\p loss}{\p y_{iter}} \frac{\p y_{iter}}{\p w'1} \]

从方程(4)中可以看出:

(6)\[ \frac{\p loss}{\p y_{iter}} = y_{iter}-y=1.96 \]

从方程(3)中可以看出:

(7)\[ \frac{\p y_{iter}}{\p w'1}= a_1=5.3 \]

因此有:

(8)\[ \frac{\p loss}{\p w'_1}=1.96\cdot 5.3 = 10.388 \]

类似的,针对\(w_{11}\),有:

(9)\[ \frac{\p loss}{\p w_{11}}=\frac{\p loss}{\p y_{iter}} \frac{\p y_{iter}}{\p a_1} \frac{\p a_1}{\p w_{11}} \]

其中

(10)\[ \frac{\p y_{iter}}{\p a_1} =w'_1=0.3 \]
(11)\[ \frac{\p a_1}{\p w_{11}}=x=3 \]

于是有:

(12)\[ \frac{\p loss}{\p w_{11}}=1.96\cdot 0.3 \cdot 3 = 1.764 \]

Warning

在这里要注意,方程(12)在计算损失的时候,乘以了\(w'_1\)的数值。也就是说计算前一层的梯度的时候,需要调用后一层的权重值。在极端情况下,比如\(w'_1=0\),那么这个导数就成为了\(0\)。也即梯度消失。

同理,针对\(w_{12}\),有:

(13)\[ \frac{\p loss}{\p w_{12}}=\frac{\p loss}{\p y_{iter}} \frac{\p y_{iter}}{\p a_1} \frac{\p a_1}{\p w_{12}}=1.96\cdot 0.3 \cdot 4=2.352 \]

其他权重以及偏置的计算方法完全相同。本算例中的参数向量假定为\(\boldsymbol{\theta}\)

(14)\[\begin{split} \boldsymbol{\theta}= \begin{bmatrix} w_{11}\\ w_{12}\\ w_{21}\\ w_{22}\\ w'_{1}\\ w'_{2}\\ b_1\\ b_2\\ b' \end{bmatrix} \end{split}\]

梯度下降方法需要计算出所有的导数:

(15)\[\begin{split} \frac{\p loss}{\p \boldsymbol{\theta}}= \begin{bmatrix} \frac{\p loss}{\p w_{11}}\\ \frac{\p loss}{\p w_{12}}\\ \frac{\p loss}{\p w_{21}}\\ \frac{\p loss}{\p w_{22}}\\ \frac{\p loss}{\p w'_{1}}\\ \frac{\p loss}{\p w'_{2}}\\ \frac{\p loss}{\p b_1}\\ \frac{\p loss}{\p b_2}\\ \frac{\p loss}{\p b'} \end{bmatrix} \end{split}\]

在上文中,我们已经演示了反向传播计算导数的方法,假定方程(15)中的导数已经全部计算出。下一步更新梯度的时候,就成为:

(16)\[\begin{split} \boldsymbol{\theta}^{new}= \begin{bmatrix} w_{11}\\ w_{12}\\ w_{21}\\ w_{22}\\ w'_{1}\\ w'_{2}\\ b_1\\ b_2\\ b' \end{bmatrix} -\alpha \begin{bmatrix} \frac{\p loss}{\p w_{11}}\\ \frac{\p loss}{\p w_{12}}\\ \frac{\p loss}{\p w_{21}}\\ \frac{\p loss}{\p w_{22}}\\ \frac{\p loss}{\p w'_{1}}\\ \frac{\p loss}{\p w'_{2}}\\ \frac{\p loss}{\p b_1}\\ \frac{\p loss}{\p b_2}\\ \frac{\p loss}{\p b'} \end{bmatrix} \end{split}\]

计算导数与更新参数:

方程(15)对应伪代码loss.backward();方程(15)对应伪代码adam->step()

其中\(\alpha\)是学习率,是一个超参数(用户自定义参数)。有两点需要注意:

  • 最初始的参数向量一般是随机数,因此可能会出现每一次训练结果导致不同的情况。设置随机种子保证每次的初始权重与偏置相同,则可以保证每次训练的模型相同;

  • 梯度下降的方法对参数向量的更新通通在最后一步,而不是更新一个参数然后用这个参数去更新后面的参数。这有点类似稀疏线性系统的雅可比迭代求解器,而不是高斯赛德尔迭代法;

自动微分

_images/backpropagation.JPG

现在将上图复制一遍贴在这里,还是以这个图来距离。一些情况下(例如如果使用PINN求解PDE的情况下),不仅需要求对权重的导数,还需要求对\(x\)\(y\)的导数。类似上文讨论的反向传播,预测值对\(x\)的导数可以这样求:

(17)\[ \frac{\p y_{iter}}{\p x}=\frac{\p y_{iter}}{\p a_1} \frac{\p a_1}{\p x} + \frac{\p y_{iter}}{\p a_2} \frac{\p a_2}{\p x}=0.3\cdot 0.9+0.2\cdot0.8=0.43 \]

方程(17)很明显是先计算后面的导数然后再往前计算,因此也可以近似理解为后向传播。不过在对变量求导数的时候,这种求法一般被称之为后向模式。另一方面,其还有另外一种求法。这一种方法来起来是从前向后来计算的。从公式上来看也比较好理解:

(18)\[\begin{split} \frac{\p y_{iter}}{\p x}= \left( \frac{\p a_1 }{\p x } \frac{\p x }{\p x } + \frac{\p a_1}{\p y} \frac{\p y}{\p x} \right)\frac{\p y_{iter}}{\p a_1} + \left( \frac{\p a_2 }{\p x } \frac{\p x }{\p x } + \frac{\p a_2}{\p y} \frac{\p y}{\p x} \right)\frac{\p y_{iter}}{\p a_2} \\\\ =\left( 0.9 \cdot 1 + 0.7 \cdot 0 \right)\cdot 0.3 + \left( 0.8\cdot 1 + 0.6 \cdot 0 \right)\cdot 0.2 \\\\ =0.43 \end{split}\]

方程(18)在计算的时候,需要从\( \frac{\p a_1 }{\p x } \)开始,逐步的向后来计算,最终得到一个\(\frac{\p y_{iter}}{\p x}\)。这是另外一种方法,被称之为前向模式。不管是后向模式还是 前向模式,都是计算对\(x\)导数的一种方法,这种方法就是自动微分方法。

针对自动微分的后向模式与前向模式,很明显后向模式需要调用更少的计算次数。因此通常自动微分采用后向模式来进行计算。后向模式自动微分与后向传播的差异更小。后向模式自动微分更像是一种数学方法。后向传播通常值得是针对损失标量,求取对参数向量的梯度的过程,其会调用后向模式自动微分这种数学方法来进行计算。

_images/autogradback.JPG

下面进行一个复杂点的计算。给定上图这个神经网络,如何通过后向模式自动微分来计算\( \frac{\p \alpha}{\p x_1} \)(在PINN中这种求导很常见)。首先对于\(x_1,x_2,x_3\)\(y_1,y_2\)的关系,有转置的雅可比矩阵为:

(19)\[\begin{split} {\mathbf J}^T({\mathbf y})=\begin{bmatrix} \frac{\p y_1}{\p x_{1}},\frac{\p y_2}{\p x_{1}}\\ \frac{\p y_1}{\p x_{2}},\frac{\p y_2}{\p x_{2}}\\ \frac{\p y_1}{\p x_{3}},\frac{\p y_2}{\p x_{3}} \end{bmatrix} \end{split}\]

对于\(y_1,y_2\)\(z_1,z_2\)的关系,有转置的雅可比矩阵为:

(20)\[\begin{split} {\mathbf J}^T({\mathbf z})=\begin{bmatrix} \frac{\p z_1}{\p y_{1}},\frac{\p z_2}{\p y_{1}}\\ \frac{\p z_1}{\p y_{2}},\frac{\p z_2}{\p y_{2}} \end{bmatrix} \end{split}\]

对于\(z_1,z_2\)\(\alpha\)的关系,有转置的雅可比矩阵为:

(21)\[\begin{split} {\mathbf J}^T({\mathbf \alpha})=\begin{bmatrix} \frac{\p \alpha}{\p z_{1}}\\ \frac{\p \alpha}{\p z_{2}} \end{bmatrix} \end{split}\]

如果进行这样的计算:

(22)\[\begin{split} \left( {\mathbf J}^T({\mathbf y}) \cdot {\mathbf J}^T({\mathbf z}) \right) \cdot {\mathbf J}^T({\mathbf \alpha}) = \left( \begin{bmatrix} \frac{\p y_1}{\p x_{1}},\frac{\p y_2}{\p x_{1}}\\ \frac{\p y_1}{\p x_{2}},\frac{\p y_2}{\p x_{2}}\\ \frac{\p y_1}{\p x_{3}},\frac{\p y_2}{\p x_{3}} \end{bmatrix} \cdot \begin{bmatrix} \frac{\p z_1}{\p y_{1}},\frac{\p z_2}{\p y_{1}}\\ \frac{\p z_1}{\p y_{2}},\frac{\p z_2}{\p y_{2}} \end{bmatrix} \right) \cdot \begin{bmatrix} \frac{\p \alpha}{\p z_{1}}\\ \frac{\p \alpha}{\p z_{2}} \end{bmatrix} \\\\ = \left( \begin{bmatrix} \frac{\p y_1}{\p x_{1}}\frac{\p z_1}{\p y_{1}}+\frac{\p y_2}{\p x_{1}}\frac{\p z_1}{\p y_{2}},\frac{\p y_1}{\p x_{1}}\frac{\p z_2}{\p y_{1}}+\frac{\p y_2}{\p x_{1}}\frac{\p z_2}{\p y_{2}}\\ \frac{\p y_1}{\p x_{2}}\frac{\p z_1}{\p y_{1}}+\frac{\p y_2}{\p x_{2}}\frac{\p z_1}{\p y_{2}},\frac{\p y_1}{\p x_{2}}\frac{\p z_2}{\p y_{1}}+\frac{\p y_2}{\p x_{2}}\frac{\p z_2}{\p y_{2}}\\ \frac{\p y_1}{\p x_{3}}\frac{\p z_1}{\p y_{1}}+\frac{\p y_2}{\p x_{3}}\frac{\p z_1}{\p y_{2}},\frac{\p y_1}{\p x_{3}}\frac{\p z_2}{\p y_{1}}+\frac{\p y_2}{\p x_{3}}\frac{\p z_2}{\p y_{2}} \end{bmatrix} \right) \cdot \begin{bmatrix} \frac{\p \alpha}{\p z_{1}}\\ \frac{\p \alpha}{\p z_{2}} \end{bmatrix} \\\\ = \begin{bmatrix} \frac{\p z_1}{\p x_{1}},\frac{\p z_2}{\p x_{1}}\\ \frac{\p z_1}{\p x_{2}},\frac{\p z_2}{\p x_{2}}\\ \frac{\p z_1}{\p x_{3}},\frac{\p z_2}{\p x_{3}} \end{bmatrix} \cdot \begin{bmatrix} \frac{\p \alpha}{\p z_{1}}\\ \frac{\p \alpha}{\p z_{2}} \end{bmatrix} =\begin{bmatrix} \frac{\p \alpha}{\p x_{1}} \\ \frac{\p \alpha}{\p x_{2}} \\ \frac{\p \alpha}{\p x_{3}} \end{bmatrix} \end{split}\]

可以看出,方程(22)就是\(\alpha\)\(\mathbf x\)的导数。

(23)\[ \frac{\p \alpha}{\p \mathbf x} = \left( {\mathbf J}^T({\mathbf y}) \cdot {\mathbf J}^T({\mathbf z}) \right) \cdot {\mathbf J}^T({\mathbf \alpha}) \]

方程(23)就是自动微分计算导数的方法。