目录标题
- 一、多层感知机
- 1.1 隐藏层
- 1.1.1 为什么需要隐藏层
- 1.1.2 在网络中加入隐藏层
- 1.1.3 从线性到非线性
- 1.2 激活函数
- 1.2.1 ReLU函数
- 1.2.2 sigmoid函数
- 1.2.3 tanh函数
最简单的深度神经网络成为多层感知机。多层感知机由多层神经元组成,每一层与它的上一层相连,从中接收输入;同时每一层也与它的下一层相连,影响当前层的神经元。
一、多层感知机
1.1 隐藏层
1.1.1 为什么需要隐藏层
简单的线性预处理不能解决比较复杂的问题。数据的特征之间有相关交互作用。对于深度神经网络,我们使用观测数据来联合学习隐藏层表示和应用于该表示的线性预测器。
1.1.2 在网络中加入隐藏层
我们可以通过在网络中加入一个或多个隐藏层来突破线性模型的限制,使其能处理更普遍的函数关系类型。最简单的方法是将许多全连接层堆叠在一起。每一层都输出到其上面的层,直到生成最后的输出。我们可以把前
L
−
1
L-1
L−1层看作表示,把最后一层看作线性预测器。这种架构通常称为多层感知机(multilayer perception , MLP) 。
上述图中的多层感知机有4个输入、3个输出,其隐藏层包含5个隐藏单元(即神经元)。输入层不涉及任何运算,因此,使用此网络生成输出只需要实现隐藏层和输出层的计算。
这个多层感知机中的层数为2,而且这两个层都是全连接的。每个输入都会影响隐藏层中的每个神经元,而隐藏层中的每个神经元又会影响输出层中的每个神经元。
1.1.3 从线性到非线性
通过矩阵
X
∈
R
n
×
d
\bm X \in \mathbb R^{n \times d}
X∈Rn×d来表示有
n
n
n个样本的小批量,其中每个样本具有
d
d
d个输入特征。对于具有
h
h
h个隐藏单元的单隐藏层多层感知机,用
H
∈
R
n
×
h
H \in \mathbb R^{n \times h}
H∈Rn×h表示隐藏层的输出,称为隐藏表示。
H
H
H也称为隐藏层变量。因为隐藏层和输出层都是全连接的,所以有隐藏层权重
W
(
1
)
∈
R
d
×
h
\bm W^{(1)} \in \mathbb R^{d \times h}
W(1)∈Rd×h和隐藏层偏置
b
(
1
)
∈
R
1
×
h
\bm b^{(1)} \in \mathbb R^{1 \times h}
b(1)∈R1×h以及输出层权重
W
(
2
)
∈
R
h
×
q
\bm W^{(2)} \in \mathbb R^{h \times q}
W(2)∈Rh×q和输出层偏置
b
(
2
)
∈
R
1
×
q
\bm b^{(2)} \in \mathbb R^{1 \times q}
b(2)∈R1×q。
因此,在形式上,按照如下方式计算单隐藏层多层感知机的输出
O
∈
R
n
×
q
\bm O \in \mathbb R^{n \times q}
O∈Rn×q
H
=
X
W
(
1
)
+
b
(
1
)
O
=
H
W
(
2
)
+
b
(
2
)
\bm {H=XW^{(1)}+b^{(1)}}\\ \bm {O=HW^{(2)}+b^{(2)}}
H=XW(1)+b(1)O=HW(2)+b(2)
上面的隐藏单元由输入的仿射函数给出,而输出(softmax操作前)只是隐藏单元的仿射函数。
仿射函数的仿射函数本身还是仿射函数(有点像绕口令,哈哈哈!!),之前的线性模型以及能够表示任何仿射函数。
我们可以用公式推导证明其等价性,即对于任意权重值,我们只需要合并隐藏层,便可产生具有参数
W
=
W
(
1
)
W
(
2
)
\bm {W=W^{(1)}W^{(2)}}
W=W(1)W(2)和
b
=
b
(
1
)
W
(
2
)
+
b
(
2
)
\bm {b=b^{(1)}W^{(2)}+b^{(2)}}
b=b(1)W(2)+b(2)的等价单层模型:
O
=
(
X
W
(
1
)
+
b
(
1
)
)
W
(
2
)
+
b
(
2
)
=
X
W
(
1
)
W
(
2
)
+
b
(
1
)
W
(
2
)
=
X
W
+
b
\bm { O= (XW^{(1)}+b^{(1)} ) W^{(2)} + b^{(2)} =XW^{(1)}W^{(2)}+ b^{(1)}W^{(2)} =XW+b }
O=(XW(1)+b(1))W(2)+b(2)=XW(1)W(2)+b(1)W(2)=XW+b
在此,需要引入额外的要素: 激活函数
在仿射变换之后对每个隐藏单元应用非线性的激活函数
σ
\sigma
σ,激活函数的输出称为激活值,一般来说,有了激活函数,多层感知机就不会退化成线性模型。
H
=
σ
(
X
W
(
1
)
+
b
(
1
)
)
O
=
H
W
(
2
)
+
b
(
2
)
\bm {H=\sigma (XW^{(1)}+b^{(1)}})\\ \bm {O=HW^{(2)}+b^{(2)}}
H=σ(XW(1)+b(1))O=HW(2)+b(2)
应用于隐藏层的激活函数通常按元素操作,在计算每一层的线性部分之后,可以计算每个激活值,而不需要查看其他隐藏单元所取的值。对于大多数激活函数都是如此。
激活函数最直观理解的作用在于:将线性转化为非线性
1.2 激活函数
激活函数通过计算加权合并加上偏置来确定神经元是否应该被激活,它们将输入信号转换为输出的可微运算。并且大多数激活函数都是非线性的。
1.2.1 ReLU函数
使用最广的激活函数是修正线性单元(rectified linear unit, ReLU),因为它实现简单,同时在各种预测任务中表现良好。
ReLU函数提供了一种非常简单的非线性变换。给定元素
x
x
x,ReLU函数被定义为该元素与0的最大值:
R
e
L
U
(
x
)
=
max
(
x
,
0
)
ReLU(x) = \max {(x,0)}
ReLU(x)=max(x,0)
ReLU函数通过将相应的激活值设为0,仅保留正元素舍去所有负元素。可以绘制出函数的曲线图,ReLU函数是分段呈线性的,但是整体是非线性的。
%matplotlib inline
import torch
from d2l import torch as d2l
x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = torch.relu(x)
d2l.plot(x.detach(), y.detach(), 'x', 'relu(x)', figsize=(5, 2.5))
x.detach()是为了阻止梯度回传到x。
在Pytorch中,detach()方法会返回一个新的张量,这个张量和原始张量只共享数据,但是没有任何的历史信息。
如果需要计算一个中间结果,但不希望这个结果对模型的梯度产生影响时,可以使用detach()
当输入为负时,ReLU函数的导数为0,而输入为正时,ReLU函数的导数为1,当输入值精确等于0时,ReLU函数不可导。此时,默认使用左侧的导数,即当输入为0时,导数为0,但是输入永远都不可能为0.
绘制ReLU函数的导数图像
y.backward(torch.ones_like(x), retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of relu', figsize=(5, 2.5))
使用ReLU函数最广泛的原因是,它的求导表现特别好:要么让参数消失,要么让参数通过。这使得优化表现更好,并且ReLU函数缓解了以往神经网络的梯度消失问题。
1.2.2 sigmoid函数
对于一个定义域在R上的输入,sigmoid函数将输入变换为区间[0,1]上的输出。因此sigmoid函数通常称为挤压函数:它将范围(-inf,inf),即负无穷到正无穷上的任意输入压缩到区间[0,1]上的某个值:
s
i
g
m
o
i
d
(
x
)
=
1
1
+
exp
(
−
x
)
sigmoid(x) = \frac 1 {1 + \exp{(-x)}}
sigmoid(x)=1+exp(−x)1
sigmoid是一个平滑、可微的阈值单元近似函数。当我们想要将输出视作二元分类问题的概率时,sigmoid仍然被广泛用作输出单元上的激活函数(sigmoid可以视为softmax的特例)。
但是,sigmoid在隐藏层已经较少使用,大多时候它被更简单、更容易训练的ReLUctant所取代。
当输入接近0时,sigmoid函数接近线性变换。
y = torch.sigmoid(x)
d2l.plot(x.detach(), y.detach(), 'x', 'sigmoid(x)', figsize=(5, 2.5))
sigmoid函数的导数是:
d
d
x
s
i
g
m
o
i
d
(
x
)
=
(
e
x
p
(
−
x
)
(
1
+
exp
(
−
x
)
)
2
=
s
i
g
m
o
i
d
(
x
)
(
1
−
s
i
g
m
o
i
d
(
x
)
)
\frac d {dx} sigmoid (x) = \frac {(exp(-x)} {(1 + \exp(-x))^2} = sigmoid(x) (1-sigmoid(x))
dxdsigmoid(x)=(1+exp(−x))2(exp(−x)=sigmoid(x)(1−sigmoid(x))
sigmoid函数的导数图像如下所示。 当输入为0时,sigmoid函数的导数达到最大值0.25; 而输入在任一方向上越远离0点时,导数越接近0。
# 清除以前的梯度
x.grad.data.zero_()
y.backward(torch.ones_like(x),retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of sigmoid', figsize=(5, 2.5))
1.2.3 tanh函数
与sigmoid函数类似,tanh(双曲正切)函数也能将其输入压缩转换到区间(-1,1)上。
tanh
(
x
)
=
1
−
exp
(
−
2
x
)
1
+
exp
(
−
2
x
)
\tanh(x) = \frac {1-\exp(-2x)} {1+\exp(-2x)}
tanh(x)=1+exp(−2x)1−exp(−2x)
绘制tanh函数的图像。当输入在0附近时,tanh函数接近线性变换。函数的形状类似于sigmoid函数,不同的是tanh函数关于坐标系原点中心对称。
y = torch.tanh(x)
d2l.plot(x.detach(), y.detach(), 'x', 'tanh(x)', figsize=(5, 2.5))
tanh函数的导数是:
d
d
x
tanh
(
x
)
=
1
−
tanh
2
(
x
)
\frac d {dx} \tanh(x) = 1-\tanh^2(x)
dxdtanh(x)=1−tanh2(x)
tanh函数的导数图像如下所示。 当输入接近0时,tanh函数的导数接近最大值1。 与在sigmoid函数图像中看到的类似, 输入在任一方向上越远离0点,导数越接近0。
# 清除以前的梯度
x.grad.data.zero_()
y.backward(torch.ones_like(x),retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of tanh', figsize=(5, 2.5))
总结:
1、多层感知机在输出层和输入层之间增加若干个全连接隐藏层,并通过激活函数转换隐藏层的输出。
2、 常用的激活函数 { 1 、 R e L U 函数(用得最多) 2 、 s i g m o i d 函数 3 、 t a n h 函数 常用的激活函数 \begin{cases} 1、ReLU函数(用得最多) \\ 2、sigmoid函数 \\ 3、tanh函数 \end{cases} 常用的激活函数⎩ ⎨ ⎧1、ReLU函数(用得最多)2、sigmoid函数3、tanh函数