感知机是一个有若干输入和一个输出的模型:
z = ∑ i = 1 m w i x i + b z=\sum\limits_{i=1}^mw_ix_i + b z=i=1∑mwixi+b
接着是一个神经元激活函数:
s i g n ( z ) = { − 1 z < 0 1 z ≥ 0 sign(z)= \begin{cases} -1& {z<0}\\ 1& {z\geq 0} \end{cases} sign(z)={−11z<0z≥0
从而得到我们想要的输出结果1或者-1。
所以MLP模型只能用于二元分类,且无法学习比较复杂的非线性模型,因此在工业界无法使用。而神经网络则在感知机的模型上做了扩展来解决这个问题。
逻辑回归对于如今越来越复杂的任务效果越来越差,主要是难以处理线性不可分的数据,LR处理线性不可分,一般是特征变换和特征组合,将低维空间线性不可分的数据在高维空间中线性可分
改良方式有几种,本质上都是对原始输入特征做文章。但都是针对特定场景设计。如果实际场景中特征组合在设计之外,模型无能为力
构建BP神经网络(也叫MLP多层感知器),用线性变换+非线性函数激活的方式进行特征变换。以分类为目的进行学习的时候,网络中的参数会以分类正确为目的自行调整,也就是自动提取合适的特征。(回归问题,去掉最后一层的激活函数就行。输出层激活函数只是为了结果有概率意义)神经网络最大的惊喜就是自动组合特征。
参考《速通8-DNN神经网络学习笔记》
a、b两类二分类时:
s o f t m a x ( x = a ) = e a e a + e b = 1 1 + e b − a = 1 1 + e − d softmax(x=a)=\frac{e^{a}}{e^{a}+e^{b}}=\frac{1}{1+e^{b-a}}=\frac{1}{1+e^{-d}} softmax(x=a)=ea+ebea=1+eb−a1=1+e−d1
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1.0/(1+np.exp(-x))
sigmoid_inputs = np.arange(-10,10)
sigmoid_outputs=sigmoid(sigmoid_inputs)
print("Sigmoid Function Input :: {}".format(sigmoid_inputs))
print("Sigmoid Function Output :: {}".format(sigmoid_outputs))
plt.plot(sigmoid_inputs,sigmoid_outputs)
plt.xlabel("Sigmoid Inputs")
plt.ylabel("Sigmoid Outputs")
plt.show()
def softmax(x):
orig_shape=x.shape
if len(x.shape)>1:
#Matrix
#shift max whithin each row
constant_shift=np.max(x,axis=1).reshape(1,-1)
x-=constant_shift
x=np.exp(x)
normlize=np.sum(x,axis=1).reshape(1,-1)
x/=normlize
else:
#vector
constant_shift=np.max(x)
x-=constant_shift
x=np.exp(x)
normlize=np.sum(x)
x/=normlize
assert x.shape==orig_shape
return x
softmax_inputs = np.arange(-10,10)
softmax_outputs=softmax(softmax_inputs)
print("Sigmoid Function Input :: {}".format(softmax_inputs))
print("Sigmoid Function Output :: {}".format(softmax_outputs))
# 画图像
plt.plot(softmax_inputs,softmax_outputs)
plt.xlabel("Softmax Inputs")
plt.ylabel("Softmax Outputs")
plt.show()
第二点从泰勒公式来说明:
泰勒公式:
任意函数可以写成多项式的和。 x 0 x_0 x0是函数定义域上的任意一点,x在a的附近。 R n R_n Rn是补偿,n越大 R n R_n Rn越小。n→∞, R n → 0 R_n→0 Rn→0。
为什么不用 e x e^x ex做激活函数,因为其各阶导数都一样。即各阶特征组合权重都很大,没有衰减过程。而好的特征我们希望组合特征的越高阶,对应权重越低,否则过拟合。不衰减,模型会认为必须满足所有高阶特征才预测出正确结果,泛化能力很弱。所以希望模型可以捕获高阶特征,但是不能过于依赖高阶特征。
没有激活函数的两层线性变换的意义:
1. Self-Attention模型的作用是提取语义级别的信息(不存在长距离依赖),而FFNN是在各个时序上对特征进行非线性变换,提高网络表达能力。
FFNN有两层,是将attention层输出先扩维4倍再降维。为什么这么做?神经网络中线性连接可以写成 d l = W l ⋅ x d^l=W^{l}\cdot x dl=Wl⋅x。其中三者维度分别是m×1、m×n、n×1。
线性代数参考《线性代数一(基本概念)》
例如标量z对向量X求导,就是看X各元素变化对z的影响。所以结果是z对X各元素求导,结果也是一个同尺寸向量。
列向量对行向量求导,结果是一个矩阵,方便后面进行链式求导中的导数连乘。(向量既可以写成列的形式,也可以写成行的形式。列向量可以直接对列向量求导,但是维数太多不便于操作,而且没法进行连乘,一般没人这么干。)
向量可以看成某一维为1的矩阵(行为1或列为1),同理,标量对m×n维矩阵求导,是对矩阵中每个元素求导,结果也是一个m×n维的结果。
对于 x 3 = W x 2 x_{3}=Wx_{2} x3=Wx2
展开之后有:
( x 31 x 32 . . . x 3 m ) = ( m 11 m 12 . . . m 1 n m 21 m 22 . . . m 2 n . . . . . . . . . . . . m m 1 m m 2 . . . m m n ) × ( x 21 x 22 . . . x 2 n ) \begin{pmatrix} x_{31}\\ x_{32}\\ ...\\ x_{3m}\end{pmatrix}=\begin{pmatrix} m_{11} & m_{12} &... &m _{1n} \\ m_{21} &m _{22} &... &m_{2n} \\ ...& ... & ... &... \\ m_{m1}&m_{m2} &... & m_{mn} \end{pmatrix}\times \begin{pmatrix} x_{21}\\ x_{22}\\ ...\\ x_{2n}\end{pmatrix} ⎝ ⎛x31x32...x3m⎠ ⎞=⎝ ⎛m11m21...mm1m12m22...mm2............m1nm2n...mmn⎠ ⎞×⎝ ⎛x21x22...x2n⎠ ⎞
所以 ∂ x 3 ∂ x 2 T = W \frac{\partial x_{3}}{\partial x_{2}^{T}}=W ∂x2T∂x3=W, ∂ x 3 ∂ W = x 2 T \frac{\partial x_{3}}{\partial W}=x_{2}^{T} ∂W∂x3=x2T
公式1——标量对向量求导链式法则:
向量 x 1 . . . x n x_{1}...x_{n} x1...xn为神经网络各层的输入,最终输出结果是标量z。存在依赖关系: x 1 → x 2 → x 3 . . . → x n → z x_{1}\rightarrow x_{2}\rightarrow x_{3}...\rightarrow x_{n}\rightarrow z x1→x2→x3...→xn→z,则有:
∂ z ∂ x 1 = ( ∂ x n ∂ x n − 1 T ⋅ ∂ x n − 1 ∂ x n − 2 T . . . ∂ x 2 ∂ x 1 T ) T ∂ z ∂ x n \frac{\partial z}{\partial x_{1}}=(\frac{\partial x_{n} }{\partial x_{n-1}^{T}}\cdot \frac{\partial x_{n-1} }{\partial x_{n-2}^{T}}...\frac{\partial x_{2} }{\partial x_{1}^{T}})^{T}\frac{\partial z}{\partial x_{n}} ∂x1∂z=(∂xn−1T∂xn⋅∂xn−2T∂xn−1...∂x1T∂x2)T∂xn∂z
公式2——标量对矩阵求导链式法则:
W为矩阵, x x x和 y y y是向量,有 y = W x y=Wx y=Wx。且标量 z = f ( y ) z=f(y) z=f(y),则:
∂ z ∂ W = ∂ z ∂ y ⋅ x T \frac{\partial z}{\partial W}=\frac{\partial z }{\partial y}\cdot x^{T} ∂W∂z=∂y∂z⋅xT
参照上面写的: ∂ x 3 ∂ W = x 2 T \frac{\partial x_{3}}{\partial W}=x_{2}^{T} ∂W∂x3=x2T
Loss对任意层 W k W^k Wk求导有:
∂ L o s s ∂ W k = ∂ L o s s ∂ d j L ⋅ ∂ d j L ∂ W k \frac{\partial Loss}{\partial W^{k}}=\frac{\partial Loss}{\partial d_{j}^{L}}\cdot \frac{\partial d_{j}^{L}}{\partial W^{k}} ∂Wk∂Loss=∂djL∂Loss⋅∂Wk∂djL
对于一个L层的神经网络,j表示L层第j维数据(也就是第j个神经元)
第一项 ∂ L o s s ∂ d j L = y j ′ − y j \frac{\partial Loss}{\partial d_{j}^{L}}=y_{j}'-y_{j} ∂djL∂Loss=yj′−yj,是一个标量。(即loss对最后一层某个神经元导数为一个标量,推导见P119)
又因为:
d k = W k ⋅ a k − 1 d^{k}=W^{k}\cdot a^{k-1} dk=Wk⋅ak−1
a k − 1 = f ( d l − 1 + w 0 k − 1 ) a^{k-1}=f(d^{l-1}+w_{0}^{k-1}) ak−1=f(dl−1+w0k−1)
后一项是标量对矩阵求导,根据公式2有:
∂ d j L ∂ W k = ∂ d j L ∂ d k ⋅ ( a k − 1 ) T \frac{\partial d_{j}^{L}}{\partial W^{k}}= \frac{\partial d_{j}^{L}}{\partial d^{k}}\cdot (a^{k-1})^{T} ∂Wk∂djL=∂dk∂djL⋅(ak−1)T
将 d j L d_{j}^{L} djL看做是由向量 d L d^{L} dL映射成标量
上式右边第一项是标量对向量求导,根据公式1有:
∂ d j L ∂ d k = ∂ d j L ∂ a L − 1 ⋅ ( ∂ a L − 1 ∂ ( d L − 1 ) T ⋅ ∂ d L − 1 ∂ ( a L − 2 ) T . . . ∂ d k + 1 ∂ ( a k ) T ⋅ ∂ a k ∂ ( d k ) T ) T \frac{\partial d_{j}^{L}}{\partial d^{k}}=\frac{\partial d_{j}^{L}}{\partial a^{L-1}}\cdot (\frac{\partial a^{L-1}}{\partial (d^{L-1})^{T}}\cdot \frac{\partial d^{L-1}}{\partial (a^{L-2})^{T}}...\frac{\partial d^{k+1}}{\partial (a^{k})^{T}}\cdot \frac{\partial a^{k}}{\partial (d^{k})^{T}})^T ∂dk∂djL=∂aL−1∂djL⋅(∂(dL−1)T∂aL−1⋅∂(aL−2)T∂dL−1...∂(ak)T∂dk+1⋅∂(dk)T∂ak)T
依次求三项导数有:
∂ d j L ∂ a L − 1 = W L \frac{\partial d_{j}^{L}}{\partial a^{L-1}}=W^L ∂aL−1∂djL=WL
∂ a m ∂ ( d m ) T = f ′ ( d m ) \frac{\partial a^{m}}{\partial (d^{m})^{T}}=f'(d^m) ∂(dm)T∂am=f′(dm)
∂ d m + 1 ∂ ( a m ) T = W m + 1 \frac{\partial d^{m+1}}{\partial (a^{m})^{T}}=W^{m+1} ∂(am)T∂dm+1=Wm+1
将以上结果联合起来就是:
∂ L o s s ∂ W k = ∂ L o s s ∂ d j L ⋅ ∂ d j L ∂ W k = ( y j ′ − y j ) ⋅ ( a k − 1 ) T ⋅ W L ⋅ f ′ ( d L − 1 ) ⋅ W L − 1 ⋅ f ′ ( d L − 2 ) . . . W k + 1 ⋅ f ′ ( d k ) \frac{\partial Loss}{\partial W^{k}}=\frac{\partial Loss}{\partial d_{j}^{L}}\cdot \frac{\partial d_{j}^{L}}{\partial W^{k}}=(y'_{j}-y_{j})\cdot (a^{k-1})^{T}\cdot W^{L}\cdot f'(d^{L-1})\cdot W^{L-1}\cdot f'(d^{L-2})... W^{k+1}\cdot f'(d^{k}) ∂Wk∂Loss=∂djL∂Loss⋅∂Wk∂djL=(yj′−yj)⋅(ak−1)T⋅WL⋅f′(dL−1)⋅WL−1⋅f′(dL−2)...Wk+1⋅f′(dk)
第m层激活函数得导数有:
a m = f ( d m + w 0 m ) a^{m}=f(d^{m}+w_{0}^{m}) am=f(dm+w0m)
展开后为:
( a 1 a 2 . . . a M ) = ( f ( d 1 + w 0 1 ) f ( d 2 + w 0 2 ) . . . f ( d M ) + w 0 M ) \begin{pmatrix} a_{1}\\ a_{2}\\ ...\\ a_{M}\end{pmatrix}=\begin{pmatrix} f(d_{1}+w_{0}^{1})\\ f(d_{2}+w_{0}^{2})\\ ...\\ f(d_{M})+w_{0}^{M}\end{pmatrix} ⎝ ⎛a1a2...aM⎠ ⎞=⎝ ⎛f(d1+w01)f(d2+w02)...f(dM)+w0M⎠ ⎞
其实是省去了上标层数m,其中第m层共有M个神经元。相当于向量d数乘之后得到向量a,每个元素按位操作。 a 1 = f ( d 1 + w 0 1 ) a_{1}=f(d_{1}+w_{0}^{1}) a1=f(d1+w01), a 1 a_{1} a1只和 d 1 d_{1} d1有关,和向量d其它元素无关,对d其它分量结果为0。
∂ a m ∂ ( d m ) T = f ′ ( d m ) \frac{\partial a^{m}}{\partial (d^{m})^{T}}=f'(d^m) ∂(dm)T∂am=f′(dm)
列向量对行向量求导,结果是一个矩阵。所以有:
f ′ ( d m ) = ( f ′ ( d 1 + w 0 1 ) 0 . . . 0 0 f ′ ( d 2 + w 0 2 ) . . . 0 0 . . . . . . . . . 0 0 . . . f ′ ( d M + w 0 M ) ) f'(d^m)=\begin{pmatrix} f'(d_{1}+w_{0}^{1}) &0 &... &0 \\ 0& f'(d_{2}+w_{0}^{2}) & ... & 0\\ 0 &... & ... &... \\ 0& 0 & ... & f'(d_{M}+w_{0}^{M}) \end{pmatrix} f′(dm)=⎝ ⎛f′(d1+w01)0000f′(d2+w02)...0............00...f′(dM+w0M)⎠ ⎞
sigmoid函数: s i g m o i d ( d ) = 1 1 + e − d sigmoid(d)=\frac{1}{1+e^{-d}} sigmoid(d)=1+e−d1导数为:
f ′ = f ( 1 − f ) = 0.25 − ( f − 0.5 ) 2 f'=f(1-f)=0.25-(f-0.5)^2 f′=f(1−f)=0.25−(f−0.5)2,后一项非负,当f=0.5时有最大值0.25。所以其值域为(0,0.25)
softmax函数及其导数,参考《Softmax函数及其导数》
relu函数: f ( x ) = m a x ( 0 , x ) f(x)=max(0,x) f(x)=max(0,x)。其导数w为:
f ′ ( x ) = { 0 , x < 0 1 , x > 0 f'(x)=\left\{\begin{matrix} 0 ,x<0\\ 1,x>0\end{matrix}\right. f′(x)={0,x<01,x>0
Tanh函数
f ( d ) = T a n h ( d ) = e d − e − d e d + e − d = s i g m o i d ( 2 d − 1 ) f(d)=Tanh(d)=\frac{e^{d}-e^{-d}}{e^{d}+e^{-d}}=sigmoid(2d-1) f(d)=Tanh(d)=ed+e−ded−e−d=sigmoid(2d−1)
导数为: ∂ a ∂ d = 1 − f 2 \frac{\partial a}{\partial d}=1-f^{2} ∂d∂a=1−f2
Tanh值域(-1,1),导数值域(0,1)。
海量的数据和强大的算力为深度学习的发展提供了条件。但是也带来一些新的问题:
合适的激活函数需要满足的条件
参考《从ReLU到GELU,一文概览神经网络的激活函数》
线性整流函数Relu: f ( x ) = m a x ( 0 , x ) f(x)=max(0,x) f(x)=max(0,x)
神经元真死和假死:
真死:
假死:饱和形式之一
LRelu: d<0时,f(d)=k,k为超参数,在0-1之间
PRelu: d<0时,f(d)=k,k是可学习的参数。
Maxout: w j ( l ) ( 1 ) w_{j}^{(l)}(1) wj(l)(1)到 w j ( l ) ( n ) w_{j}^{(l)}(n) wj(l)(n)有多个参数,分别和上一层输出相乘,即每个神经元节点j有多个输入,最终输出选最大值。(P133-134)
参考《超越ReLU却鲜为人知,3年后被挖掘》
参考《GELU 论文》
BERT、RoBERTa、ALBERT 等目前业内顶尖的 NLP 模型都使用了这种激活函数。另外,在 OpenAI 声名远播的无监督预训练模型 GPT-2 中,研究人员在所有编码器模块中都使用了 GELU 激活函数。在计算机视觉、自然语言处理和自动语音识别等任务上,使用 GELU 激活函数比使用ReLU 或 ELU 效果更好。
随着网络深度的不断增加,利用 Sigmoid 激活函数来训练被证实不如非平滑、低概率性的 ReLU 有效(Nair & Hinton, 2010),因为 ReLU 基于输入信号做出门控决策。
深度学习中为了解决过拟合,会随机正则化(如在隐层中加入噪声)或采用 dropout 机制。这两个选择是和激活函数割裂的。非线性和 dropout 共同决定了神经元的输出,而随机正则化在执行时与输入无关。
由此提出高斯误差线性单元(Gaussian Error Linear Unit,GELU)。GELU 与随机正则化有关,因为它是自适应 Dropout 的修正预期(Ba & Frey, 2013)。这表明神经元输出的概率性更高。
Dropout、ReLU 等机制都希望将「不重要」的激活信息规整为零。即对于输入的值,我们根据它的情况乘上 1 或 0。或者说,对输入x乘上一个伯努利分布 Bernoulli(Φ(x)),其中Φ(x) = P(X ≤ x)。(x服从于标准正态分布 N(0, 1))
对于一部分Φ(x),它直接乘以输入 x,而对于另一部分 (1 − Φ(x)),它们需要归零。随着 x 的降低,它被归零的概率会升高。对于 ReLU 来说,这个界限就是 0。
我们经常希望神经网络具有确定性决策,这种想法催生了 GELU 激活函数的诞生。具体来说可以表示为:Φ(x) × Ix + (1 − Φ(x)) × 0x = xΦ(x)。可以理解为,不太严格地说,上面这个表达式可以按当前输入 x 比其它输入大多少来缩放 x。
高斯概率分布函数通常根据损失函数计算,因此研究者定义高斯误差线性单元(GELU)为:
G E L U ( x ) = x P ( X ⩽ x ) = x Φ ( x ) GELU(x)=xP(X\leqslant x)=x\Phi (x) GELU(x)=xP(X⩽x)=xΦ(x)
上面这个函数是无法直接计算的,因此可以通过另外的方法来逼近这样的激活函数,研究者得出来的表达式为:
G E L U ( x ) = 0.5 x ( 1 + t a n h [ 2 π ( x + 0.044175 x 3 ) ] ) GELU(x)=0.5x(1+tanh[\sqrt{\frac{2}{\pi }}(x+0.044175x^{3})]) GELU(x)=0.5x(1+tanh[π2(x+0.044175x3)])
或: G E L U ( x ) = x σ ( 1.702 x ) GELU(x)=x\sigma (1.702x) GELU(x)=xσ(1.702x)
其中 σ() 是标准的 sigmoid 函数
当 x 大于 0 时,输出为 x;但 x=0 到 x=1 的区间除外,这时曲线更偏向于 y 轴。
没能找到该函数的导数,所以我使用了 WolframAlpha 来微分这个函数。结果如下:
微分的GELU函数:
GELU 的近似实现方式有两种,借助 tanh() 和借助σ()。我们在 GPT-2 的官方代码中也发现,更多研究者采用了 tanh() 的实现方式尽管它看起来要比 xσ(1.702x) 复杂很多。
# GPT-2 的 GELU 实现
def gelu(x):
return 0.5*x*(1+tf.tanh(np.sqrt(2/np.pi)*(x+0.044715*tf.pow(x, 3))))
参考《苏神文章解析(6篇)》
《浅谈Transformer的初始化、参数化与标准化》
《从几何视角来理解模型参数的初始化策略》