神经元模型
神经网络 (neural networks)是由具有适应性的简单单元组成的广泛并行互连的网络,它的组织能够模拟生物神经系统对真实世界物体所作出的交互反应。
我们在机器学习中谈论神经网络时指的是「神经网络学习」,或者说,是机器学习与神经网络这两个学科领域的交叉部分。
神经网络中最基本的成分是神经元 (neuron, a.k.a. unit)模型,即上述定义中的「简单单元」。
在生物神经网络中,每个神经元与其他神经元相连,当它「兴奋」时,就会向相连的神经元发送化学物质,从而改变这些神经元内的电位;如果某神经元的电位超过了一个「阈值」(threshold, a.k.a. bias),那么它就会被激活,即「兴奋」起来,向其他神经元发送化学物质。
将上述情形抽象化,得到沿用至今的 M-P 神经元模型 。在这个模型中,神经元接收到来自几个其他神经元传递过来的输入信号,这些输入信号通过带权重的连接 (connection)进行传递,神经元接收到的总输入值将与神经元的阈值进行比较,然后通过「激活函数」(activation function, 亦称激活函数)处理以产生神经元的输出。
理想的激活函数是阶跃函数,将输入值映射为输出值 0 \ 1 0 \backslash 1 0\1 ,分别对应于神经元抑制和兴奋。但是阶跃函数有不连续、不光滑等不太好的性质,常用 Sigmoid 函数作为激活函数。
常用的 Sigmoid 函数
sigmoid ( x ) = 1 1 + e − x \operatorname{sigmoid}(x) = \dfrac{1}{1 + \e^{-x}}
sigmoid ( x ) = 1 + e − x 1
它把可能在较大范围内变化的输入值挤压到 ( 0 , 1 ) (0, 1) ( 0 , 1 ) 输出值范围内,有时也被称为「挤压函数」(squashing function)。
把许多个这样的神经元按一定的层次结构连接起来,就得到了神经网络。
计算机科学的角度看,只需将一个神经网络视为包含了许多参数的数学模型,这个模型是若干个函数(例如 y j = f ( ∑ i w i x i − θ j ) y_{j} = f\left(\sum_i w_i x_i - \theta_{j}\right) y j = f ( ∑ i w i x i − θ j ) )相互(嵌套)代入而得。
感知机与多层网络
感知机
感知机 (perceptron)由两层神经元组成,如下图所示:
输入层 接收外界输入信号后传递给输出层,输出层 是 M-P 神经元,亦称「阈值逻辑单元」(threshold logic unit)。
感知机能容易地实现逻辑与、或、非等运算,注意到 y = f ( ∑ i w i x i − θ ) y = f(\sum_i w_i x_i - \theta) y = f ( ∑ i w i x i − θ ) ,假设 f f f 是阶跃函数,有
与 (x 1 ∧ x 2 x_1 \land x_2 x 1 ∧ x 2 ):令 w 1 = w 2 = 1 , θ = 2 w_1 = w_2 = 1, \theta = 2 w 1 = w 2 = 1 , θ = 2 ,则 y = f ( x 1 + x 2 − 2 ) y = f(x_1 + x_2 - 2) y = f ( x 1 + x 2 − 2 ) ,当且仅当 x 1 = x 2 = 1 x_1 = x_2 = 1 x 1 = x 2 = 1 时 y = 1 y = 1 y = 1 ,否则 y = 0 y = 0 y = 0 ;
或 (x 1 ∨ x 2 x_1 \lor x_2 x 1 ∨ x 2 ):令 w 1 = w 2 = 1 , θ = 0.5 w_1 = w_2 = 1, \theta = 0.5 w 1 = w 2 = 1 , θ = 0.5 ,则 y = f ( x 1 + x 2 − 0.5 ) y = f(x_1 + x_2 - 0.5) y = f ( x 1 + x 2 − 0.5 ) ,当且仅当 x 1 = 1 x_1 = 1 x 1 = 1 或 x 2 = 1 x_2 = 1 x 2 = 1 时 y = 1 y = 1 y = 1 ,否则 y = 0 y = 0 y = 0 ;
非 (¬ x 1 \lnot x_1 ¬ x 1 ):令 w 1 = − 0.6 , w 2 = 0 , θ = − 0.5 w_1 = -0.6, w_2 = 0, \theta = -0.5 w 1 = − 0.6 , w 2 = 0 , θ = − 0.5 ,则 y = f ( − 0.6 x + 0.5 ) y = f(-0.6x + 0.5) y = f ( − 0.6 x + 0.5 ) ,当 x 1 = 1 x_1 = 1 x 1 = 1 时 y = 0 y = 0 y = 0 ,当 x 1 = 0 x_1 = 0 x 1 = 0 时 y = 1 y = 1 y = 1 。
更一般地,给定训练数据集,权重 w i w_i w i 以及阈值 θ \theta θ 可通过学习得到。阈值 θ \theta θ 可看作一个固定输入为 − 1.0 -1.0 − 1.0 的哑结点 (dummy node)所对应的连接权重 w n + 1 w_{n+1} w n + 1 。这样,权重和阈值的学习就可统一为权重的学习。
对训练样例 ( x , y ) (\bm{x}, y) ( x , y ) ,若当前感知机的输出为 y ^ \hat{y} y ^ ,则感知机权重将这样调整
w i ← w i + Δ w i Δ w i = η ( y − y ^ ) x i \begin{aligned}
w_i &\gets w_i + \Delta w_i \\
\Delta w_i &= \eta(y - \hat{y})x_i
\end{aligned}
w i Δ w i ← w i + Δ w i = η ( y − y ^ ) x i
其中 η ∈ ( 0 , 1 ) \eta \in (0, 1) η ∈ ( 0 , 1 ) 是学习率 (learning rate)。
可以看出来,若感知机对样例 ( x , y ) (\bm{x}, y) ( x , y ) 预测正确,即 y ^ = y \hat{y} = y y ^ = y ,则感知机不发生变化,否则将根据错误的程度进行权重调整。
感知机只有输出层神经元进行激活函数处理,即只用有一层功能神经元 (functional neuron),其学习能力非常有限。
上述与、或、非问题都是线性可分 (linearly separable)的问题。
可证明若两类模式是线性可分的,即存在一个线性超平面能将它们分开,则感知机的学习过程一定会收敛(converge)而求得适当的权向量 w = ( w 1 ; w 2 ; … ; w n + 1 ) \bm{w} = (w_1; w_2; \dots; w_{n+1}) w = ( w 1 ; w 2 ; … ; w n + 1 ) ;否则感知机学习过程将会发生振荡(fluctuation),w \bm{w} w 难以稳定下来,不能求得合适解。
例如感知机甚至无法解决异或这样简单的非线性可分问题。
多层网络
要解决非线性可分问题,需考虑使用多层功能神经元。例如下图 中这个简单的两层感知机就能解决异或问题。
在下图 (a) 中,输出层与输入层之间的一层神经元,被称为隐层 或隐含层 (hidden layer),隐含层和输出层神经元都是拥有激活函数的功能神经元,亦称功能单元 (functional unit)。因此这通常被称为「两层网络」或「单隐层网络」(避免歧义的名称)。
更一般的,常见的神经网络是形如下图所示的层级结构,每层神经元与下一层神经元全互连,神经元之间不存在同层连接,也不存在跨层连接。
这样的神经网络结构通常称为「多层前馈神经网络」(multi-layer feedforward neural networks),其中输入层神经元接收外界输入,隐层与输出层神经元对信号进行加工,最终结果由输出层神经元输出。
换言之,输入层神经元仅是接受输入,不进行函数处理,隐层与输出层包含功能神经元。
只需包含隐层,即可称为多层网络。
神经网络的学习过程,就是根据训练数据来调整神经元之间的即神经元连接的权重。「连接权」(connection weight)以及每个功能神经元的阈值。
换言之,神经网络「学」到的东西,蕴涵在连接权 与阈值 中。
多层前馈网络有强大的表示能力,即「万有逼近性」。
只需包含足够多神经元的隐层,多层前馈网络就能以任意精度逼近任意复杂度的连续函数。但如何设置隐层神经元数仍是未决问题(open problem),实际常用「试错法」(trial-by-error)。
误差逆传播算法
标准 BP 算法
误差逆传播 (error BackPropagation, BP)算法是迄今最成功的神经网络学习算法。
给定训练集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯ , ( x m , y m ) } , x i ∈ R d , y i ∈ R l D = \left\lbrace (\bm{x}_1, \bm{y}_1),\, (\bm{x}_2, \bm{y}_2),\, \cdots,\, (\bm{x}_m, \bm{y}_m) \right\rbrace,\, \bm{x}_i \in \R^d,\, \bm{y}_i \in \R^l D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯ , ( x m , y m ) } , x i ∈ R d , y i ∈ R l ,即输入示例由 d d d 个属性描述,输出 l l l 维实值向量。
如下图所示,给出了一个拥有 d d d 个输入神经元、l l l 个输出神经元、q q q 个隐层神经元的多层前馈神经网络:
输出层第 j j j 个神经元的阈值用 θ j \theta_{j} θ j 表示,隐层第 h h h 个神经元的阈值用 γ h \gamma_{h} γ h 表示。
输入层第 i i i 个神经元与隐层第 h h h 个神经元之间的连接权为 v i h v_{ih} v ih ,隐层第 h h h 个神经元与输出层第 j j j 个神经元之间的连接权为 w h j w_{hj} w hj 。
记隐层第 h h h 个神经元接收到的输入为 α h = ∑ i = 1 d v i h x i \alpha_h = \sum_{i=1}^d v_{ih}x_i α h = ∑ i = 1 d v ih x i ,输出层第 j j j 个神经元接收到的输入为 β j = ∑ h = 1 q w h j b h \beta_j = \sum_{h=1}^q w_{hj}b_h β j = ∑ h = 1 q w hj b h 。其中 b h b_h b h 为隐层第 h h h 个神经元的输出。假设隐层和输出层神经元都使用 Sigmoid 函数作为激活函数。
对训练例 ( x k , y k ) (\bm{x}_{k}, \bm{y}_{k}) ( x k , y k ) ,假定神经网络的输出为 y ^ k = ( y ^ 1 k , y ^ 2 k , ⋯ , y ^ n k ) \hat{\bm{y}}_{k} = (\hat{y}^{k}_1, \hat{y}^{k}_2, \cdots, \hat{y}^{k}_n) y ^ k = ( y ^ 1 k , y ^ 2 k , ⋯ , y ^ n k ) ,即
y ^ j k = f ( β j − θ j ) \hat{y}^{k}_j = f(\beta_j - \theta_{j})
y ^ j k = f ( β j − θ j )
则网络在训练例 ( x k , y k ) (\bm{x}_{k}, \bm{y}_{k}) ( x k , y k ) 上的均方误差为
E k = 1 2 ∑ j = 1 l ( y ^ j k − y j k ) 2 E_{k} = \dfrac{1}{2} \sum_{j=1}^l (\hat{y}^{k}_j - y^{k}_j)^2
E k = 2 1 j = 1 ∑ l ( y ^ j k − y j k ) 2
图所示的网络有 ( d + l + 1 ) q + l (d + l + 1)q + l ( d + l + 1 ) q + l 个参数需确定:
输入层到隐层的 d × q d \times q d × q 个权值
隐层到输出层的 q × l q \times l q × l 个权值
隐层的 q q q 个阈值
输出层的 l l l 个阈值
BP 是一个迭代学习算法,在迭代的每一轮中采用广义的感知机学习规则对参数进行更新估计,任意参数 v v v 的更新估计式为
v ← v + Δ v v \gets v + \Delta v
v ← v + Δ v
以隐层到输出层的连接权 w h j w_{hj} w hj 为例进行推导:
BP 算法基于「梯度下降」(gradient descent)策略,以目标的负梯度方向对参数进行调整,对上面的误差 E k E_{k} E k ,给定学习率 η \eta η ,有
Δ w h j = − η ∂ E k ∂ w h j \Delta w_{hj} = - \eta \dfrac{\partial E_k}{\partial w_{hj}}
Δ w hj = − η ∂ w hj ∂ E k
注意到 w h j w_{hj} w hj 先影响到第 j j j 个输出层神经元的输入值 β j \beta_{j} β j ,再影响到其输出值 y ^ j k \hat{y}^{k}_j y ^ j k ,最终影响到误差 E k E_{k} E k ,因此(链式法则)
∂ E k ∂ w h j = ∂ E k ∂ y ^ j k ⋅ ∂ y ^ j k ∂ β j ⋅ ∂ β j ∂ w h j \dfrac{\partial E_{k}}{\partial w_{hj}} = \dfrac{\partial E_{k}}{\partial \hat{y}^{k}_j} \cdot \dfrac{\partial \hat{y}^{k}_j}{\partial \beta_j} \cdot \dfrac{\partial \beta_j}{\partial w_{hj}}
∂ w hj ∂ E k = ∂ y ^ j k ∂ E k ⋅ ∂ β j ∂ y ^ j k ⋅ ∂ w hj ∂ β j
根据 β j \beta_{j} β j 的定义,有
∂ β j ∂ w h j = b h \dfrac{\partial \beta_{j}}{\partial w_{hj}} = b_h
∂ w hj ∂ β j = b h
Sigmoid 函数有这样的性质:f ′ ( x ) = f ( x ) ( 1 − f ( x ) ) f'(x) = f(x)(1 - f(x)) f ′ ( x ) = f ( x ) ( 1 − f ( x )) 。
简短的证明
f ′ ( x ) = ( 1 1 + e − x ) ′ = e − x ( 1 + e − x ) 2 = 1 1 + e − x ⋅ e − x 1 + e − x = f ( x ) ( 1 − f ( x ) ) \begin{aligned}
f'(x) &= \left( \dfrac{1}{1 + \e^{-x}} \right)'\\
&= \dfrac{\e^{-x}}{(1 + \e^{-x})^2}\\
&= \dfrac{1}{1 + \e^{-x}} \cdot \dfrac{\e^{-x}}{1 + \e^{-x}}\\
&= f(x)(1 - f(x))
\end{aligned}
f ′ ( x ) = ( 1 + e − x 1 ) ′ = ( 1 + e − x ) 2 e − x = 1 + e − x 1 ⋅ 1 + e − x e − x = f ( x ) ( 1 − f ( x ))
于是由上面的 y ^ j k , E k \hat{y}_{j}^{k}, E_{k} y ^ j k , E k ,可得输出层神经元梯度项 g j g_{j} g j 为
g j = − ∂ E k ∂ y ^ j k ⋅ ∂ y ^ j k ∂ β j = − ( y ^ j k − y j k ) f ′ ( β j − θ j ) = y ^ j k ( 1 − y ^ j k ) ( y j k − y ^ j k ) \begin{aligned}
g_{j} &= - \dfrac{\partial E_{k}}{\partial \hat{y}_{j}^{k}} \cdot \dfrac{\partial \hat{y}_{j}^{k}}{\partial \beta_{j}}\\
&= - (\hat{y}_{j}^{k} - y_{j}^{k}) f'(\beta_{j} - \theta_{j})\\
&= \hat{y}_{j}^{k} (1 - \hat{y}_{j}^{k}) (y_{j}^{k} - \hat{y}_{j}^{k})
\end{aligned}
g j = − ∂ y ^ j k ∂ E k ⋅ ∂ β j ∂ y ^ j k = − ( y ^ j k − y j k ) f ′ ( β j − θ j ) = y ^ j k ( 1 − y ^ j k ) ( y j k − y ^ j k )
再将这个式子和 ∂ β j ∂ w h j = b h \dfrac{\partial \beta_{j}}{\partial w_{hj}} = b_h ∂ w hj ∂ β j = b h 代入上面的链式法则,再代入 Δ w h j = − η ∂ E k ∂ w h j \Delta w_{hj} = - \eta \dfrac{\partial E_{k}}{\partial w_{hj}} Δ w hj = − η ∂ w hj ∂ E k ,可得
Δ w h j = η g j b h \Delta w_{hj} = \eta g_{j} b_h
Δ w hj = η g j b h
类似地有
Δ θ j = − η g j Δ v i h = η e h x i Δ γ h = − η e h \begin{aligned}
\Delta \theta_{j} &= - \eta g_{j}\\
\Delta v_{ih} &= \eta e_h x_i\\
\Delta \gamma_h &= - \eta e_h
\end{aligned}
Δ θ j Δ v ih Δ γ h = − η g j = η e h x i = − η e h
其中隐层神经元梯度项 e h e_h e h 为
e h = − ∂ E k ∂ b h ⋅ ∂ b h ∂ α h = − ∑ j = 1 l ∂ E k ∂ β j ⋅ ∂ β j ∂ b h f ′ ( α h − γ h ) = ∑ j = 1 l w h j g j f ′ ( α h − γ h ) = b h ( 1 − b h ) ∑ j = 1 l w h j g j \begin{aligned}
e_h &= - \dfrac{\partial E_{k}}{\partial b_h} \cdot \dfrac{\partial b_h}{\partial \alpha_h}\\
&= - \sum_{j=1}^l \dfrac{\partial E_{k}}{\partial \beta_j} \cdot \dfrac{\partial \beta_j}{\partial b_h} f'(\alpha_h - \gamma_h)\\
&= \sum_{j=1}^l w_{hj} g_{j} f'(\alpha_h - \gamma_h)\\
&= b_h(1 - b_h) \sum_{j=1}^l w_{hj} g_{j}
\end{aligned}
e h = − ∂ b h ∂ E k ⋅ ∂ α h ∂ b h = − j = 1 ∑ l ∂ β j ∂ E k ⋅ ∂ b h ∂ β j f ′ ( α h − γ h ) = j = 1 ∑ l w hj g j f ′ ( α h − γ h ) = b h ( 1 − b h ) j = 1 ∑ l w hj g j
诺奖趣闻
10.9 上课继续记笔记。2024 年诺贝尔物理学奖 于 10.8 颁给了 John J. Hopfield 与 Geoffrey E. Hinton,以表彰他们在使用人工神经网络进行机器学习的基础性发现和发明(for foundational discoveries and inventions that enable machine learning with artificial neural networks)。
这两位大佬在课件上介绍神经网络历史中有出现,只是我没留意 。
学习率 η ∈ ( 0 , 1 ) \eta \in (0, 1) η ∈ ( 0 , 1 ) 控制着算法每一轮迭代中的更新步长。若太大则容易振荡,太小则收敛速度又会过慢。
BP 算法工作流程:对于每个训练样例
先将输入示例提供给输入层神经元
逐层将信号前传,直到产生输出层的结果
计算输出层的误差(4~5 行)
再将误差逆向传播至隐层神经元(6 行)
最后根据隐层神经元的误差来对连接权和阈值进行调整(7 行)
迭代过程循环进行,直到达到某些停止条件,例如训练误差已达到一个很小的值,为止。
停止条件也与缓解 BP 过拟合的策略有关。
自推导
感觉上面写得不是太好,打算用矩阵重新推导一遍试试。
给定训练集 D = { ( x i , y i ) } i = 1 m , x i ∈ R d , y i ∈ R l D = \left\lbrace (\bm{x}_i, \bm{y}_i)\right\rbrace_{i=1}^m,\, \bm{x}_i \in \R^d,\, \bm{y}_i \in \R^l D = { ( x i , y i ) } i = 1 m , x i ∈ R d , y i ∈ R l ,即输入示例由 d d d 个属性描述,输出 l l l 维实值向量。
如下图所示,给出了一个拥有 d d d 个输入神经元、l l l 个输出神经元、q q q 个隐层神经元的多层前馈神经网络:
假设权重矩阵 W 1 ∈ R q × d , W 2 ∈ R l × q \bm{W}_{1} \in \R^{q \times d}, \bm{W}_{2} \in \R^{l \times q} W 1 ∈ R q × d , W 2 ∈ R l × q ,分别是是输入层到隐层的权重矩阵与隐层到输出层的权重矩阵;偏置向量 θ 1 ∈ R q × 1 , θ 2 ∈ R l × 1 \bm{\theta}_{1} \in \R^{q \times 1}, \bm{\theta}_{2} \in \R^{l \times 1} θ 1 ∈ R q × 1 , θ 2 ∈ R l × 1 。
为了一般化,考虑输入层到隐层的激活函数和隐层到输出层的激活函数分别为 f 1 , f 2 f_1, f_2 f 1 , f 2 。
则对于样本 ( x , y ) (\bm{x}, \bm{y}) ( x , y ) ,隐层的输入为(这里偏置是加,与上面也不一致)
z 1 = W 1 x + θ 1 \bm{z}_{1} = \bm{W}_{1} \bm{x} + \bm{\theta}_{1}
z 1 = W 1 x + θ 1
输出为
a 1 = f 1 ( z 1 ) \bm{a}_1 = f_1(\bm{z}_{1})
a 1 = f 1 ( z 1 )
这也能看出第一个权重矩阵为何是 q × d q \times d q × d ,也就是有「隐层神经元个数」行、「输入神经元个数」列,是反过来的。因为样本输入是 x ∈ R d × 1 \bm{x} \in \R^{d \times 1} x ∈ R d × 1 为列向量。
类似地,有输出层的输入为
z 2 = W 2 a 1 + θ 2 \bm{z}_{2} = \bm{W}_{2} \bm{a}_1 + \bm{\theta}_{2}
z 2 = W 2 a 1 + θ 2
输出为
y ^ = a 2 = f 2 ( z 2 ) \hat{\bm{y}} = \bm{a}_2 = f_2(\bm{z}_{2})
y ^ = a 2 = f 2 ( z 2 )
上面的损失函数为
E = 1 2 ∥ y ^ − y ∥ 2 E = \dfrac{1}{2} \|\hat{\bm{y}} - \bm{y}\|^2
E = 2 1 ∥ y ^ − y ∥ 2
其实还有别的损失函数,例如交叉熵 (cross entropy)损失函数
E = − ∑ j = 1 l y j log y ^ j E = - \sum_{j=1}^l y_j \log \hat{y}_j
E = − j = 1 ∑ l y j log y ^ j
为了一般化,下面就直接用 E E E 表示损失函数,并记 δ i = ∂ E ∂ z i \bm{\delta}_i = \dfrac{\partial E}{\partial \bm{z}_i} δ i = ∂ z i ∂ E 。
反向传播实际上就是利用「链式法则」,将损失函数对权重和偏置的梯度反向传播,然后根据梯度下降法更新权重和偏置。
因此我们需要计算
∂ E ∂ W 1 , ∂ E ∂ θ 1 , ∂ E ∂ W 2 , ∂ E ∂ θ 2 \dfrac{\partial E}{\partial \bm{W}_{1}},\, \dfrac{\partial E}{\partial \bm{\theta}_{1}},\, \dfrac{\partial E}{\partial \bm{W}_{2}},\, \dfrac{\partial E}{\partial \bm{\theta}_{2}}
∂ W 1 ∂ E , ∂ θ 1 ∂ E , ∂ W 2 ∂ E , ∂ θ 2 ∂ E
链式法则有
∂ E ∂ W 2 = ∂ E ∂ z 2 ∂ z 2 ∂ W 2 = ( ∂ E ∂ y ^ ∂ y ^ ∂ z 2 ) a 1 ⊺ = ( E ′ f 2 ′ ( z 2 ) ) a 1 ⊺ = δ 2 a 1 ⊺ \begin{aligned}
\dfrac{\partial E}{\partial \bm{W}_{2}} &= \dfrac{\partial E}{\partial \bm{z}_{2}} \dfrac{\partial \bm{z}_{2}}{\partial \bm{W}_{2}}\\
&= \left( \dfrac{\partial E}{\partial \hat{\bm{y}}} \dfrac{\partial \hat{\bm{y}}}{\partial \bm{z}_2} \right) \bm{a}_1^\intercal\\
&= \left( E' f_2'(\bm{z}_2) \right) \bm{a}_1^\intercal\\
&= \bm{\delta}_{2} \bm{a}_1^\intercal
\end{aligned}
∂ W 2 ∂ E = ∂ z 2 ∂ E ∂ W 2 ∂ z 2 = ( ∂ y ^ ∂ E ∂ z 2 ∂ y ^ ) a 1 ⊺ = ( E ′ f 2 ′ ( z 2 ) ) a 1 ⊺ = δ 2 a 1 ⊺
与
∂ E ∂ θ 2 = ∂ E ∂ z 2 ∂ z 2 ∂ θ 2 = δ 2 \begin{aligned}
\dfrac{\partial E}{\partial \bm{\theta}_{2}} &= \dfrac{\partial E}{\partial \bm{z}_{2}} \dfrac{\partial \bm{z}_{2}}{\partial \bm{\theta}_{2}}\\
&= \bm{\delta}_2
\end{aligned}
∂ θ 2 ∂ E = ∂ z 2 ∂ E ∂ θ 2 ∂ z 2 = δ 2
类似地有
∂ E ∂ W 1 = ∂ E ∂ z 1 ∂ z 1 ∂ W 1 = ( ∂ E ∂ z 1 ) x ⊺ = ( ∂ E ∂ a 1 ∂ a 1 ∂ z 1 ) x ⊺ = ( W 2 ⊺ δ 2 ∘ f 1 ′ ( z 1 ) ) x ⊺ = δ 1 x ⊺ \begin{aligned}
\dfrac{\partial E}{\partial \bm{W}_{1}} &= \dfrac{\partial E}{\partial \bm{z}_{1}} \dfrac{\partial \bm{z}_{1}}{\partial \bm{W}_{1}}\\
&= \left( \dfrac{\partial E}{\partial \bm{z}_{1}} \right) \bm{x}^\intercal\\
&= \left( \dfrac{\partial E}{\partial \bm{a}_1} \dfrac{\partial \bm{a}_1}{\partial \bm{z}_{1}} \right) \bm{x}^\intercal\\
&= \left( \bm{W}_{2}^\intercal \delta_2 \circ f_1'(\bm{z}_{1}) \right) \bm{x}^\intercal\\
&= \bm{\delta}_1 \bm{x}^\intercal
\end{aligned}
∂ W 1 ∂ E = ∂ z 1 ∂ E ∂ W 1 ∂ z 1 = ( ∂ z 1 ∂ E ) x ⊺ = ( ∂ a 1 ∂ E ∂ z 1 ∂ a 1 ) x ⊺ = ( W 2 ⊺ δ 2 ∘ f 1 ′ ( z 1 ) ) x ⊺ = δ 1 x ⊺
上面的带有矩阵的偏导运算,需要后面补一下数学知识。
【待补充】
使用梯度下降法,对于每个样本 ( x , y ) (\bm{x}, \bm{y}) ( x , y ) 与学习率 η \eta η ,更新参数
{ W 1 ← W 1 − η ∂ E ∂ W 1 θ 1 ← θ 1 − η ∂ E ∂ θ 1 W 2 ← W 2 − η ∂ E ∂ W 2 θ 2 ← θ 2 − η ∂ E ∂ θ 2 \left\lbrace\begin{aligned}
\bm{W}_{1} &\gets \bm{W}_{1} - \eta \dfrac{\partial E}{\partial \bm{W}_{1}}\\
\bm{\theta}_{1} &\gets \bm{\theta}_{1} - \eta \dfrac{\partial E}{\partial \bm{\theta}_{1}}\\
\bm{W}_{2} &\gets \bm{W}_{2} - \eta \dfrac{\partial E}{\partial \bm{W}_{2}}\\
\bm{\theta}_{2} &\gets \bm{\theta}_{2} - \eta \dfrac{\partial E}{\partial \bm{\theta}_{2}}
\end{aligned}\right.
⎩ ⎨ ⎧ W 1 θ 1 W 2 θ 2 ← W 1 − η ∂ W 1 ∂ E ← θ 1 − η ∂ θ 1 ∂ E ← W 2 − η ∂ W 2 ∂ E ← θ 2 − η ∂ θ 2 ∂ E
其中有
{ δ 2 = E ′ f 2 ′ ( z 2 ) δ 1 = W 2 ⊺ δ 2 ∘ f 1 ′ ( z 1 ) ∂ E ∂ W 1 = δ 1 x ⊺ ∂ E ∂ θ 1 = δ 1 ∂ E ∂ W 2 = δ 2 a 1 ⊺ ∂ E ∂ θ 2 = δ 2 \left\lbrace\begin{aligned}
\bm{\delta}_2 &= E' f_2'(\bm{z}_2)\\
\bm{\delta}_1 &= \bm{W}_{2}^\intercal \bm{\delta}_2 \circ f_1'(\bm{z}_1)\\
\dfrac{\partial E}{\partial \bm{W}_1} &= \bm{\delta}_1 \bm{x}^\intercal\\
\dfrac{\partial E}{\partial \bm{\theta}_{1}} &= \bm{\delta}_1\\
\dfrac{\partial E}{\partial \bm{W}_2} &= \bm{\delta}_2 \bm{a}_1^\intercal\\
\dfrac{\partial E}{\partial \bm{\theta}_{2}} &= \bm{\delta}_2
\end{aligned}\right.
⎩ ⎨ ⎧ δ 2 δ 1 ∂ W 1 ∂ E ∂ θ 1 ∂ E ∂ W 2 ∂ E ∂ θ 2 ∂ E = E ′ f 2 ′ ( z 2 ) = W 2 ⊺ δ 2 ∘ f 1 ′ ( z 1 ) = δ 1 x ⊺ = δ 1 = δ 2 a 1 ⊺ = δ 2
代码实现
下面使用 Python 代码实现一个简单的 BP 算法。初始权重与偏置使用了 [ − 0.5 , 0.5 ] [-0.5, 0.5] [ − 0.5 , 0.5 ] 上的均匀分布随机初始化,隐层采用了 ReLU 激活函数,输出层采用了 Sigmoid 激活函数,使用了交叉熵损失函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import numpy as npclass NeuralNetwork : def __init__ (self, input_size, hidden_size, output_size, learning_rate=0.01 ): self.input_size = input_size self.hidden_size = hidden_size self.output_size = output_size self.learning_rate = learning_rate self.W1 = np.random.uniform(-0.5 , 0.5 , (self.hidden_size, self.input_size)) self.b1 = np.random.uniform(-0.5 , 0.5 , (self.hidden_size, 1 )) self.W2 = np.random.uniform(-0.5 , 0.5 , (self.output_size, self.hidden_size)) self.b2 = np.random.uniform(-0.5 , 0.5 , (self.output_size, 1 )) def sigmoid (self, z ): return 1 / (1 + np.exp(-z)) def relu (self, z ): return np.maximum(0 , z) def forward (self, X ): self.z1 = self.W1 @ X.T + self.b1 self.a1 = self.relu(self.z1) self.z2 = self.W2 @ self.a1 + self.b2 self.a2 = self.sigmoid(self.z2) return self.a2.T def compute_loss (self, y_true, y_pred ): loss = -np.mean(y_true * np.log(y_pred + 1e-8 ) + (1 - y_true) * np.log(1 - y_pred + 1e-8 )) return loss def backward (self, X, y ): m = X.shape[0 ] dz2 = self.a2 - y.reshape(1 , -1 ) dW2 = dz2 @ self.a1.T / m db2 = np.sum (dz2, axis=1 , keepdims=True ) / m dz1 = self.W2.T @ dz2 * (self.z1 > 0 ) dW1 = dz1 @ X / m db1 = np.sum (dz1, axis=1 , keepdims=True ) / m self.W2 -= self.learning_rate * dW2 self.b2 -= self.learning_rate * db2 self.W1 -= self.learning_rate * dW1 self.b1 -= self.learning_rate * db1 def train (self, X, y, epochs=1000 ): losses = [] accuracies = [] for epoch in range (epochs): y_pred = self.forward(X) loss = self.compute_loss(y, y_pred) losses.append(loss) accuracy = np.mean((y_pred > 0.5 ) == y.reshape(-1 , 1 )) accuracies.append(accuracy) self.backward(X, y) if epoch % 100 == 0 : print (f'Epoch {epoch} , Loss: {loss:.4 f} , Accuracy: {accuracy:.4 f} ' ) return losses, accuracies
测试示例
工具函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import matplotlib.pyplot as pltdef plot_decision_boundary (model, X, y ): x_min, x_max = X[:, 0 ].min () - 0.1 , X[:, 0 ].max () + 0.1 y_min, y_max = X[:, 1 ].min () - 0.1 , X[:, 1 ].max () + 0.1 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01 ), np.arange(y_min, y_max, 0.01 )) Z = model.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) plt.contourf(xx, yy, Z, alpha=0.3 , cmap=plt.cm.Spectral) plt.scatter(X[:, 0 ], X[:, 1 ], c=y, s=40 , cmap=plt.cm.Spectral) plt.xlabel("Feature 1" ) plt.ylabel("Feature 2" ) plt.title("Decision Boundary" ) plt.show() def plot_training_process (losses, accuracies ): fig, ax1 = plt.subplots() ax1.set_xlabel('Epochs' ) ax1.set_ylabel('Loss' , color='tab:red' ) ax1.plot(losses, color='tab:red' , label='Loss' ) ax1.tick_params(axis='y' , labelcolor='tab:red' ) ax2 = ax1.twinx() ax2.set_ylabel('Accuracy' , color='tab:blue' ) ax2.plot(accuracies, color='tab:blue' , label='Accuracy' ) ax2.tick_params(axis='y' , labelcolor='tab:blue' ) fig.tight_layout() plt.title("Training Loss and Accuracy" ) plt.show()
以 moons 二分类数据集为例进行测试,输入层有 2 个神经元,隐层有 5 个神经元,输出层有 1 个神经元。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from sklearn.datasets import make_moonsfrom sklearn.model_selection import train_test_splitif __name__ == "__main__" : X, y = make_moons(n_samples=1000 , noise=0.1 , random_state=42 ) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2 , random_state=42 ) nn = NeuralNetwork(input_size=2 , hidden_size=5 , output_size=1 , learning_rate=0.5 ) losses, accuracies = nn.train(X_train, y_train, epochs=1000 ) plot_decision_boundary(nn, X_train, y_train) plot_decision_boundary(nn, X_test, y_test) plot_training_process(losses, accuracies) y_test_forward = nn.forward(X_test) y_test_pred = y_test_forward > 0.5 test_accuracy = np.mean(y_test_pred == y_test.reshape(-1 , 1 )) print (f"Test Accuracy: {test_accuracy:.4 f} " )
训练过程中损失与准确率:
1 2 3 4 5 6 7 8 9 10 Epoch 0, Loss: 0.7006, Accuracy: 0.5000 Epoch 100, Loss: 1.5489, Accuracy: 0.9000 Epoch 200, Loss: 1.6234, Accuracy: 0.9200 Epoch 300, Loss: 1.7023, Accuracy: 0.9500 Epoch 400, Loss: 1.8975, Accuracy: 0.9775 Epoch 500, Loss: 2.1299, Accuracy: 0.9900 Epoch 600, Loss: 2.3492, Accuracy: 0.9912 Epoch 700, Loss: 2.5403, Accuracy: 0.9925 Epoch 800, Loss: 2.7056, Accuracy: 0.9950 Epoch 900, Loss: 2.8573, Accuracy: 0.9962
训练集、测试集上的决策边界:
训练过程中的损失与准确率:
测试集上的准确率:100%。
代码有部分细节需要解释一下。
22 行中有 self.W1 @ X.T + self.b1
,这里 W1
是 q × d q \times d q × d 的矩阵,X
是 m × d m \times d m × d 的矩阵(m m m 个样本),b1
是 q × 1 q \times 1 q × 1 的矩阵,@
是矩阵乘法,+
是广播机制。这样得到的 z1
是 q × m q \times m q × m 的矩阵,每一列是一个样本的隐层输入。
ChatGPT 关于广播机制的说明
在这段代码中:
self.z1 = self.W1 @ X.T + self.b1
假设 W1
是 h × n h \times n h × n 的矩阵,X.T
是 n × m n \times m n × m 的矩阵,那么 W1 @ X.T
的结果是一个 h × m h \times m h × m 的矩阵。这里的 self.b1
是一个 h × 1 h \times 1 h × 1 的列向量。
为什么 numpy
可以进行这样的求和?
NumPy 在遇到不同形状的数组进行相加时,会自动应用 广播机制 (broadcasting)。在广播机制下,NumPy 会尝试扩展较小的数组,使其维度与较大数组兼容,前提是它们满足某些条件。
在这里:
W1 @ X.T
是 h × m h \times m h × m 的矩阵。
self.b1
是 h × 1 h \times 1 h × 1 的列向量。
根据广播规则,NumPy 会把 self.b1
自动扩展成 h × m h \times m h × m 的矩阵,每一列都重复 self.b1
的值,然后再进行逐元素相加操作。
Broadcasting 机制的具体规则
在 NumPy 中,广播的规则是这样的:
如果两个数组的维度不同,NumPy 会将较小维度的数组在前面补 1
。
然后从最后一维开始比较维度:
如果两个维度相等,或者其中一个为 1
,则认为它们是兼容的。
否则,维度不兼容,无法进行广播。
在兼容的情况下,数组会在维度为 1
的轴上被扩展,复制数据以匹配较大数组的形状。
具体来说,以下代码演示了广播机制:
1 2 3 4 5 6 import numpy as npW1_XT = np.random.randn(3 , 5 ) b1 = np.random.randn(3 , 1 ) z1 = W1_XT + b1
在这个例子中,b1
会沿列方向扩展为 3 × 5 3 \times 5 3 × 5 的矩阵,与 W1 @ X.T
的每个元素对应相加,结果是一个 3 × 5 3 \times 5 3 × 5 的矩阵。这就是广播机制在神经网络计算中常用的方式,简化了偏置项的加法操作。
在这个例子当中,交叉熵损失的计算公式如下
E = − ( y log y ^ + ( 1 − y ) log ( 1 − y ^ ) ) E = -(y \log \hat{y} + (1 - y) \log (1 - \hat{y}))
E = − ( y log y ^ + ( 1 − y ) log ( 1 − y ^ ))
29 行交叉熵损失函数的计算中,加上了一个很小的数 1 × 1 0 − 8 1 \times 10^{-8} 1 × 1 0 − 8 ,是为了避免出现 log(0)
的情况。
35 行中的 dz2
,也就是 δ 2 \bm{\delta}_2 δ 2 实际上经过了这样的化简:
δ 2 = ∂ E ∂ y ^ sigmoid ′ ( z 2 ) = − ( y y ^ − 1 − y 1 − y ^ ) ⋅ sigmoid ( z 2 ) ( 1 − sigmoid ( z 2 ) ) = − ( y − y ^ y ^ ( 1 − y ^ ) ) ⋅ y ^ ( 1 − y ^ ) = y ^ − y \begin{aligned}
\bm{\delta}_2 &= \dfrac{\partial E}{\partial \hat{\bm{y}}} \operatorname{sigmoid}'(\bm{z}_2)\\
&= -\left( \dfrac{\bm{y}}{\hat{\bm{y}}} - \dfrac{1-\bm{y}}{1-\hat{\bm{y}}} \right) \cdot \operatorname{sigmoid}(\bm{z}_2) (1 - \operatorname{sigmoid}(\bm{z}_2))\\
&= -\left( \dfrac{\bm{y} - \hat{\bm{y}}}{\hat{\bm{y}}(1-\hat{\bm{y}})} \right) \cdot \hat{\bm{y}} (1 - \hat{\bm{y}})\\
&= \hat{\bm{y}} - \bm{y}
\end{aligned}
δ 2 = ∂ y ^ ∂ E sigmoid ′ ( z 2 ) = − ( y ^ y − 1 − y ^ 1 − y ) ⋅ sigmoid ( z 2 ) ( 1 − sigmoid ( z 2 )) = − ( y ^ ( 1 − y ^ ) y − y ^ ) ⋅ y ^ ( 1 − y ^ ) = y ^ − y
也即 self.a2 - y.reshape(1, -1)
。y.reshape(1, -1)
将 y
转为行向量。
dW2
, db2
, dW1
, db1
要除以样本数 m m m ,这是为了求平均。
39 行也是类似的,只不过没能像上面一样化简,而 ReLU 的导数为 f 1 ′ ( z 1 ) = I ( z 1 > 0 ) f_1'(\bm{z}_1) = \mathbb{I}(\bm{z}_1 > 0) f 1 ′ ( z 1 ) = I ( z 1 > 0 ) 。
实际作业中做的是「小批量」梯度下降,即 train
函数还有一个参数 batch_size
,实际上是类似的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def train (self, X, y, epochs=1000 , batch_size=32 ): losses = [] accuracies = [] for epoch in range (epochs): permutation = np.random.permutation(X.shape[0 ]) X_shuffled = X[permutation] y_shuffled = y[permutation] for i in range (0 , X.shape[0 ], batch_size): X_batch = X_shuffled[i:i+batch_size] y_batch = y_shuffled[i:i+batch_size] y_pred = self.forward(X_batch) self.backward(X_batch, y_batch) loss = self.compute_loss(y, self.forward(X)) losses.append(loss) accuracy = np.mean((self.forward(X) > 0.5 ) == y.reshape(-1 , 1 )) accuracies.append(accuracy) if epoch % 100 == 0 : print (f'Epoch {epoch} , Loss: {loss:.4 f} , Accuracy: {accuracy:.4 f} ' ) return losses, accuracies
使用了 np.random.permutation
进行打乱,然后按照 batch_size
进行小批量梯度下降,而非整体梯度下降。
累积 BP 算法
BP 算法的目的是要最小化训练集 D D D 上的累积误差 E = 1 m ∑ k = 1 m E k E = \dfrac{1}{m}\displaystyle \sum_{k=1}^{m}E_{k} E = m 1 k = 1 ∑ m E k 。但是上面介绍的「标准 BP 算法」每次仅针对一个训练样例更新连接权和阈值。即上图中的算法更新规则是基于单个的 E k E_{k} E k 推导而得。可类似推导出基于累计误差最小化的更新规则,就得到了累积误差逆传播 (accumulated error backpropagation)算法。
标准 BP 算法每次更新只针对单个样例,参数更新得非常频
繁,而且对不同样例进行更新的效果可能出现「抵消」现象。因此,为了达到同样的累积误差极小点,标准 BP 算法往往需进行更多次数的迭代。
累积 BP 算法直接针对累积误差最小化,它在读取整个训练集 D D D 一遍后才对参数进行更新,其参数更新的频率低得多。但在很多任务中,累积误差下降到一定程度之后,进一步下降会非常缓慢,这时标准 BP 往往会更快获得较好的解,尤其是在训练集。非常大时更明显。
总结为
标准 BP 算法
每次针对单个训练样例更新权值与阈值
参数更新频繁,不同样例可能抵消,需要多次迭代
累积 BP 算法
优化目标是最小化整个训练集上的累积误差
读取整个训练集一遍才对参数进行更新,参数更新频率较低
过拟合
正是由于其强大的表示能力,BP 神经网络经常遭遇过拟合,其训练误差持续降低,但测试误差却可能上升。
有两种策略常用来缓解 BP 网络的过拟合:
早停 (early stopping)
若训练误差连续 a a a 轮的变化小于某个阈值 b b b ,则停止训练。
使用验证集:若训练误差降低、验证误差升高,则停止训练。
正则化 (regularization):基本思想是在误差目标函数中增加一个用于描述网络复杂度的部分(例如权值和阈值的平方和)。
对于正则化策略,仍令 E k E_{k} E k 表示第 k k k 个训练样例上的误差,w i w_i w i 表示权值和阈值,则(累积)误差目标函数改变为
E = λ 1 m ∑ k = 1 m E k + ( 1 − λ ) ∑ i w i 2 E = \lambda \dfrac{1}{m} \sum_{k=1}^{m} E_{k} + (1-\lambda) \sum_i w_i^2
E = λ m 1 k = 1 ∑ m E k + ( 1 − λ ) i ∑ w i 2
其中 λ ∈ ( 0 , 1 ) \lambda \in (0, 1) λ ∈ ( 0 , 1 ) 用于对经验误差与网络复杂度这两项进行折中。常通过交叉验证法进行估计。
全局最小与局部最小
若用 E E E 表示神经网络在训练集上的误差,则它显然是关于连接权 w \bm{w} w 和阈值 θ \theta θ 的函数。此时,神经网络的训练过程可看作一个参数寻优过程,即在参数空间中,寻找一组最优参数使得 E E E 最小。
有两种「最优」:局部极小 (local minimum)和全局最小 (global minimum)。
对 w ∗ \bm{w}^{*} w ∗ 和 θ ∗ \theta^{*} θ ∗ ,若存在 ϵ \epsilon ϵ 使得
∀ ( w ; θ ) ∈ { ( w ; θ ) ∣ ∥ ( w ; θ ) − ( w ∗ ; θ ∗ ) ∥ < ϵ } \forall (\bm{w}; \theta) \in \left\lbrace (\bm{w}; \theta) \mid \|(w; \theta) - (\bm{w}^{*}; \theta^{*})\| < \epsilon \right\rbrace
∀ ( w ; θ ) ∈ { ( w ; θ ) ∣ ∥ ( w ; θ ) − ( w ∗ ; θ ∗ ) ∥ < ϵ }
都有 E ( w ; θ ) ⩾ E ( w ∗ ; θ ∗ ) E(\bm{w}; \theta) \ge E(\bm{w}^{*}; \theta^{*}) E ( w ; θ ) ⩾ E ( w ∗ ; θ ∗ ) ,则称 ( w ∗ ; θ ∗ ) (\bm{w}^{*}; \theta^{*}) ( w ∗ ; θ ∗ ) 是局部极小解。
若对参数空间中的任意 ( w ; θ ) (\bm{w}; \theta) ( w ; θ ) ,都有 E ( w ; θ ) ⩾ E ( w ∗ ; θ ∗ ) E(\bm{w}; \theta) \ge E(\bm{w}^{*}; \theta^{*}) E ( w ; θ ) ⩾ E ( w ∗ ; θ ∗ ) ,则称 ( w ∗ ; θ ∗ ) (\bm{w}^{*}; \theta^{*}) ( w ∗ ; θ ∗ ) 是全局最小解。
参数空间内梯度为零的点,只要其误差函数小于邻点的误差函数值,就是局部极小点。
基于梯度的搜索是使用最为广泛的参数寻优方法。在此类方法中,我们从某些初始解出发,迭代寻找最优参数值。每次迭代中,我们先计算误差函数在当前点的梯度,然后根据梯度确定搜索方向。
例如,由于负梯度方向是函数值下降最快的方向,因此「梯度下降法」就是沿着负梯度方向搜索最优解。
然而,如果误差函数具有多个局部极小,则不能保证找到的解是全局最小。对这种情形,我们称此时参数寻优「陷入了局部极小」。
现实任务中常用以下策略试图「跳出」局部极小,从而进一步接近全局最小(可参考《人工智能导论》课程教材):
以多组不同参数值初始化多个神经网络,按标准方法训练后,取其中误差最小的解作为最终参数。
这相当于从多个不同的初始点开始搜索,这样就可能陷入不同的局部极小,从中进行选择有可能获得更接近全局最小的结果。
模拟退火 (simulated annealing)技术。
模拟退火在每一步都以一定的概率接受比当前解更差的结果,从而有助于「跳出」局部极小。在每步迭代过程中,接受「次优解」的概率要随着时间的推移而逐渐降低,从而保证算法稳定。
也会造成「跳出」全局最小。
随机梯度下降
随机梯度下降法在计算梯度时加入了随机因素。于是,即便陷入局部极小点,它计算出的梯度仍可能不为零,这样就有机会跳出局部极小继续搜索。
上述用于跳出局部极小的技术大多是启发式,理论上尚缺乏保障。
其他常见神经网络
RBF 网络
径向基函数 (Radial Basis Function, RBF)网络是一种单隐层前馈神经网络,它使用径向基函数作为隐层神经元激活函数,而输出层则是对隐层神经元输出的线性组合。
假定输入为 d d d 维向量 x \bm{x} x ,输出为实值,则 RBF 网络可表示为
φ ( x ) = ∑ i = 1 q w i ρ ( x , c i ) \varphi(\bm{x}) = \sum_{i=1}^{q} w_i \rho(\bm{x}, \bm{c}_i)
φ ( x ) = i = 1 ∑ q w i ρ ( x , c i )
其中 q q q 为隐层神经元个数,c i \bm{c}_{i} c i 和 w i w_{i} w i 分别是第 i i i 个隐层神经元所对应的中心和权重,ρ ( x , c i ) \rho\left(x, c_{i}\right) ρ ( x , c i ) 是径向基函数,常用的高斯径向基函数形如
ρ ( x , c i ) = exp ( − β i ∥ x − c i ∥ 2 ) \rho(\bm{x}, \bm{c}_{i}) = \exp\left( - \beta_i\|\bm{x} - \bm{c}_{i}\|^2 \right)
ρ ( x , c i ) = exp ( − β i ∥ x − c i ∥ 2 )
具有足够多隐层神经元的 RBF 网络能以任意精度逼近任意连续函数。
训练 RBF 网络过程:
确定神经元中心 c i \bm{c}_i c i
利用 BP 算法等确定参数 w i , β i w_i, \beta_i w i , β i
ART 网络
竞争型学习 (competitive learning)是神经网络中一种常用的无监督学习策略。在使用该策略时,网络的输出神经元相互竞争,每一时刻仅有一个竞争获胜的神经元被激活,其他神经元的状态被抑制。这种机制亦称「胜者通吃」(winner-take-all)原则。
自适应共振理论 (Adaptive Resonance Theory, ART)网络是一种竞争型神经网络。该网络由比较层、识别层、识别
阈值和重置模块构成:
比较层负责接收输入样本,并将其传递给识别层神经元
识别层每个神经元对应一个模式类,神经元数目可在训练过程中动态增长以增加新的模式类
步骤:
匹配
比较
搜索
学习
在接收到比较层的输入信号后,识别层神经元之间相互竞争以产生获胜神经元。
竞争的最简单方式:计算输入向量与每个识别层神经元所对应的模式类的代表向量之间的距离,距离最小者胜。
获胜神经元将向其他识别层神经元发送信号,抑制其激活。若输入向量与获胜神经元所对应的代表向量之间的相似度
大于识别阈值:则当前输入样本将被归为该代表向量所属类别,同时,网络连接权将会更新,使得以后在接收到相似输入样本时该模式类会计算出更大的相似度,从而使该获胜神经元有更大可能获胜;
不大于识别阈值:则重置模块将在识别层增设一个新的神经元,其代表向量就设置为当前输入向量。
显然,识别阈值对 ART 网络的性能有重要影响:
识别阈值较高时:输入样本将会被分成比较多、比较精细的模式类;
识别阈值较低时:输入样本将会被分成比较少、比较粗略的模式类。
ART 比较好地缓解了竞争型学习中的「可塑性-稳定性窘境」(stabilityplasticity dilemma),可塑性 是(plasticity)指神经网络要有学习新知识的能力 ,而稳定性 (stability)则是指神经网络在学习新知识时要保持对旧知识的记忆 。
这就使得 ART 网络具有一个很重要的优点:可进行增量学习(incremental learning)或在线学习(online learning)。
SOM 网络
自组织映射 (Self-Organizing Map, SOM)网络是一种竞争学习型的无监督神经网络,它能将高维输入数据映射到低维空间(通常为二维),同时保持输入数据在高维空间的拓扑结构,即将高维空间中相似的样本点映射到网络输出层中的邻近神经元。
如图所示,SOM 网络中的输出层神经元以矩阵方式排列在二维空间中,每个神经元都拥有一个权向量,网络在接收输入向量后,将会确定输出层获胜神经元,它决定了该输入向量在低维空间中的位置。
SOM 的训练目标就是为每个输出层神经元找到合适的权向量,已达到保持拓扑结构的目的。
SOM 的训练过程:
在接收到一个训练样本后,每个输出层神经元会计算该样本与自身携带的权向量之间的距离,距离最近的神经元成为竞争获胜者,称为最佳匹配单元 (best matching unit)。
网络接受输入样本后,将会确定输出层的「获胜」神经元(优胜邻域)。
最佳匹配单元及其邻近神经元的权向量将被调整,以使得这些权向量与当前输入样本的距离缩小。
上述过程不断迭代,直至收敛。
级联相关网络
一般的神经网络模型通常假定网络结构是事先固定的,训练的目的是利用训练样本来确定合适的连接权、阈值等参数。与此不同,结构自适应网络 则将网络结构也当作学习的目标之一,并希望能在训练过程中找到最符合数据特点的网络结构。
级联相关 (Cascade-Correlation)网络是结构自适应网络的重要代表。
级联相关网络有两个主要成分:「级联」和「相关」。
「级联」是指建立层次连接的层级结构。在开始训练时,网络只有输入层和输出层,处于最小拓扑结构;随着训练的进行,新的隐层神经元逐渐加入,从而创建起层级结构。当新的隐层神经元加入时,其输入端连接权值是冻结固定的。
「相关」是指通过最大化新神经元的输出与网络误差之间的相关性(correlation)来训练相关的参数。
与一般的前馈神经网络相比,级联相关网络无需设置网络层数、隐层神经元数目,且训练速度较快,但其在数据较小时易陷入过拟合.
这是 2024 年诺贝尔物理学奖的成果。
根据神经网络运行过程中的信息流向,可分为前馈式和反馈式两种基本类型。
John J. Hopfield 1982 年提出了一种单层反馈神经网络,后来人们将这种反馈网络称为 Hopfield 网络。
Hopfield 是一种反馈性神经网络,包括离散型 Hopfield 网络(DHNN)和连续型 Hopfield 网络(CHNN)。
神经元的输出称为网络的「状态」。
转移函数
x i = sign ( n e t i ) = { 1 , n e t i ⩾ 0 − 1 , n e t i < 0 x_i = \operatorname{sign}(\mathrm{net}_i) = \begin{cases}
1, & \mathrm{net}_i \ge 0\\
-1, & \mathrm{net}_i < 0
\end{cases}
x i = sign ( net i ) = { 1 , − 1 , net i ⩾ 0 net i < 0
反馈网络稳定时,每个神经元的状态都不再改变,此时的稳定状态就是网络输出 。
网络的稳定性由吸引子和能量函数 (energy function)来描述。
Hopfield 网络主要用于存储和检索记忆。
Elman 网络
与前馈神经网络不同,「递归神经网络」(recurrent neural networks)允许网络中出现环形结构,从而可让一些神经元的输出反馈回来作为输入信号。这样的结构与信息反馈过程,使得网络在 t t t 时刻的输出状态不仅与 t t t 时刻的输入有关,还与 t − 1 t - 1 t − 1 时刻的网络状态有关,从而能处理与时间有关的动态变化。
Elman 网络是最常用的递归神经网络之一。
它的结构与多层前馈网络很相似,但隐层神经元的输出被反馈回来,与下一时刻输入层神经元提供的信号一起,作为隐层神经元在下一时刻的输入。隐层神经元通常采用 Sigmoid 激活函数,而网络的训练则常通过推广的 BP 算法进行。
Boltzmann 机
神经网络中有一类模型是为网络状态定义一个「能量」(energy),能量最小化时网络达到理想状态,而网络的训练就是在最小化这个能量函数。
Boltzmann 机就是一种「基于能量」(energy-based)的模型。其神经元分为两层:
显层 用于表示数据的输入与输出;
隐层 则被理解为数据的内在表达。
Boltzmann 机中的神经元都是布尔型的,只能取 { 0 , 1 } \left\lbrace 0, 1 \right\rbrace { 0 , 1 } ,状态 1 表示激活,状态 0 表示抑制。
令向量 s ∈ { 0 , 1 } n \bm{s} \in \left\lbrace 0, 1 \right\rbrace^n s ∈ { 0 , 1 } n 表示 n n n 个神经元的状态,w i j w_{ij} w ij 表示神经元 i , j i, j i , j 之间的连接权,θ i \theta_i θ i 表示神经元 i i i 的阈值,则状态函数 s \bm{s} s 所对应的 Boltzmann 机能量定义为
E ( s ) = − ∑ i = 1 n − 1 ∑ j = i + 1 n w i j s i s j − ∑ i = 1 n θ i s i E(\bm{s}) = - \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} w_{ij}s_i s_j - \sum_{i=1}^{n} \theta_i s_i
E ( s ) = − i = 1 ∑ n − 1 j = i + 1 ∑ n w ij s i s j − i = 1 ∑ n θ i s i
其中 w i j w_{ij} w ij 表示两个神经元之间的连接权值,θ i \theta_i θ i 表示神经元 i i i 的阈值。
网络中的神经元以任意不依赖与输入值的顺序进行更新,则网络最终将达到 Boltzmann 分布 ,此时状态向量出现的概率将仅由其能量与所有可能状态向量的能量确定:
P ( s ) = exp ( − E ( s ) ) ∑ t exp ( − E ( t ) ) P(\bm{s}) = \dfrac{\exp(-E(\bm{s}))}{\sum_{\bm{t}}\exp(-E(\bm{t}))}
P ( s ) = ∑ t exp ( − E ( t )) exp ( − E ( s ))
Boltzmann 机训练:
将每个训练样本视为一个状态向量,使其出现的概率尽可能大
标准的 Boltzmann 机 是一个全连接图,训练网络的复杂度很高,这使其难以用于解决现实任务
现实中常用受限 Boltzmann 机(Restricted Boltzmann Machine, RBM)。RBM 仅保留显层与隐层之间的连接,从而将 Boltzmann 机结构有完全图简化为二部图
RBM 常用对比散度 (contrastive divergence, CD)算法进行训练:
假定网络中有 d d d 个显层神经元 q q q 个隐层神经元,令 v , h \bm{v}, \bm{h} v , h 分别是显层与隐层的状态向量,由于同一层内不存在连接,有
P ( v ∣ h ) = ∏ i = 1 d P ( v i ∣ h ) P ( h ∣ v ) = ∏ j = 1 q P ( h j ∣ v ) \begin{aligned}
P(\bm{v} \mid \bm{h}) &= \prod_{i=1}^{d} P(v_i \mid \bm{h})\\
P(\bm{h} \mid \bm{v}) &= \prod_{j=1}^{q} P(h_j \mid \bm{v})
\end{aligned}
P ( v ∣ h ) P ( h ∣ v ) = i = 1 ∏ d P ( v i ∣ h ) = j = 1 ∏ q P ( h j ∣ v )
CD 算法对每个训练样本 v \bm{v} v ,先计算出隐层神经元状态的概率分布,然后根据这个概率分布采样得到 h \bm{h} h ;类似的方法从 h \bm{h} h 中产生 v ′ \bm{v}' v ′ ,再从 v ′ \bm{v}' v ′ 中产生 h ′ \bm{h}' h ′ ;连接权重的更新公式为
Δ w i j = ϵ ( v h ⊺ − v ′ h ′ ⊺ ) \Delta w_{ij} = \epsilon (\bm{v} \bm{h}^\intercal - \bm{v}' \bm{h}'^\intercal)
Δ w ij = ϵ ( v h ⊺ − v ′ h ′ ⊺ )
深度学习
理论上来说,参数越多的模型复杂度越高、 「容量」(capacity)越大,这意味着它能完成更复杂的学习任务。但一般情形下,复杂模型的训练效率低,易陷入过拟合,因此难以受到人们青睐。
而随着云计算、大数据时代的到来,计算能力的大幅提高可缓解训练低效性,训练数据的大幅增加则可降低过拟合风险, 因此,以 「深度学习」(deep learning)为代表的复杂模型开始受到人们的关注。
典型的深度学习模型就是很深层的神经网络。对神经网络模型,提高容量的一个简单办法是增加隐层的数目。隐层多了,相应的神经元连接权、阈值等参数就会更多。
从增加模型复杂度的角度来看,增加隐层的数目比增加隐层神经元的数目更有效,因为增加隐层数不仅增加了拥有激活函数的神经元数目,还增加了激活函数嵌套的层数。
然而,多隐层神经网络难以直接用经典算法(例如标准 BP 算法)进行训练,因为误差在多隐层内逆传播时,往往会「发散」(diverge)而不能收敛到稳定状态。
重要诀窍(tricks):
无监督逐层训练(预训练 + 微调)
预训练:监督逐层训练,每次训练一层隐结点
微调:预训练全部完成后,对全网络进行微调训练
可视为将大量参数分组,对每组先找到较好的局部配置,再全局寻优
权共享(weight-sharing)
Dropout
在每轮训练时随机选择一些参数令其不被更新
减低 Rademacher 复杂度
ReLU (Rectified Linear Units)
将 Sigmoid 激活函数修改为修正线性函数 f ( x ) = max ( 0 , x ) f(x) = \max(0, x) f ( x ) = max ( 0 , x )
求导容易;缓解梯度消失现象
交叉熵(cross-entropy)
BP 算法中以交叉熵 − 1 m ∑ i = 1 m y i log y ^ i -\dfrac{1}{m}\displaystyle \sum_{i=1}^{m}y_i \log \hat{y}_i − m 1 i = 1 ∑ m y i log y ^ i 代替均方误差 E = 1 m ∑ i = 1 m ( y i − y ^ i ) 2 E = \dfrac{1}{m}\displaystyle \sum_{i=1}^{m}(y_i - \hat{y}_i)^2 E = m 1 i = 1 ∑ m ( y i − y ^ i ) 2
更能体现分类任务的特性
特征工程(feature engineering):特征工程由人类专家根据现实任务来设计,「特征提取」和「识别」是单独的两个阶段。
特征学习(feature learning):特征学习通过深度学习技术,自动产生有益于分类的特征,是一个端到端的学习框架。
补充内容
上面提及的神经网络基本都是上个世纪的产物,下面是一些 21 世纪的神经网络模型:
CNN 卷积神经网络
RNN 循环神经网络
GNN 图神经网络
Transformer 自注意力神经网络