深度学习基础:MLP、BP、CNN、RNN
1.摘要
本篇博客参考《Deep Learning》的第二部分对深度学习的基础知识进行总结,主要包括:前馈神经网络、反向传播算法、卷积神经网络和循环神经网络,以便加深理解和记忆
2.前馈神经网络与反向传播算法
2.1前馈神经网络与反向传播算法简介
1)前馈神经网络模型是由1个输入层,1个输出层,0到多个隐藏层构成的前馈神经网络,每层包含多个MP神经元模型。从层的角度看,每层都可以看作是一个关于输入数据的复合函数;从表示学习的角度看,每一层都为输入数据提供了一个新的表示;从每层的神经元看,每个神经元又可看作独立并行的计算单元,进行分布式处理。
2)由万能近似定理可知,MLP可看作一般函数的通用近似器,由多个线性模型经过激活函数的非线性处理拟合出Borel可测的复杂函数。
3)通过纵深成网络可以减少MP单元的数量,提高可实现性,提高泛化能力,同时也蕴含着联结主义和大型系统由简单构件组成的思想。
4)MLP通过在经验数据上学习,提高计算机程序处理任务的性能度量。其中,学习到的知识就存储在每个互连的MP模型的边上和阈值中。
5)模型学习之初为每个MP模型的边权和阈值设成小随机值,通过模型当前对经验数据的预测值与标记值的均方误差作为损失函数调整各个MP单元的边权和阈值,以达到学习的目的。
6)优化的方法则是采用梯度下降的方法,这时就需要计算损失函数对各个参数(每个MP模型的边权和阈值)的梯度,计算的方法就是反向传播算法backprop,简称BP
2.2.微分链式法则与计算图
1)微分链式法则
反向传播算法的数学基础就是函数微分的链式法则,是复合函数求微分的方法,这里不多做介绍
2)计算图
计算图语言是描述神经网络更加精确、将计算形式化为图形的一种工具,我们将代数和计算图的表达式称为符号表示
- 图节点:表示一个变量,该变量可以是标量、向量、张量
- 操作:有一个或多个输入,一个输出的函数(不失一般性,该输出可以是多条目的向量、张量)
- 通过多个操作符合在一起描述复杂函数
这里给出一个简单示例
![](https://img-blog.csdnimg.cn/d85a155509f542048647d38db3371742.png)
2.3.前馈神经网络求参数梯度闭式解的示例
![](https://img-blog.csdnimg.cn/437dda85d1dc4e0ca1307cc896c3bc5c.png)
①激活函数:σ,设每个MP单元的激活函数都是σ
②节点:N(i,j),表示第i层第j个MP单元,其中,
N(i,j)节点的输入:
N
(
i
,
j
)
i
n
N(i,j)_{in}
N(i,j)in,
N
(
i
,
j
)
i
n
=
∑
w
(
i
−
1
,
,
j
)
T
N
o
u
t
(
i
−
1
,
)
−
θ
(
i
,
j
)
N(i,j)_{in} = \sum w(i-1,,j)^T N_{out}(i-1,) - θ(i,j)
N(i,j)in=∑w(i−1,,j)TNout(i−1,)−θ(i,j)
N(i,j)节点的输出:
N
(
i
,
j
)
o
u
t
N(i,j)_{out}
N(i,j)out,
N
(
i
,
j
)
o
u
t
=
σ
(
∑
w
(
i
−
1
,
,
j
)
T
N
o
u
t
(
i
−
1
,
)
−
θ
(
i
,
j
)
)
N(i,j)_{out} = σ(\sum w(i-1,,j)^T N_{out}(i-1,) - θ(i,j))
N(i,j)out=σ(∑w(i−1,,j)TNout(i−1,)−θ(i,j))
当N(i,j)节点为输出层单元时,其标签值为:
N
′
(
i
,
j
)
o
u
t
N'(i,j)_{out}
N′(i,j)out
③阈值:θ(i,j),表示第i层第i个MP单元的阈值
④边权:w(i,j,k),表示第i层第j个MP单元与第i+1层第k个MP单元之间的边权
⑤损失函数:L(w,θ)
⑥经验数据:
X
=
{
x
1
,
x
2
,
.
.
.
,
x
n
}
X =\{x_1,x_2,...,x_n \}
X={x1,x2,...,xn},
x
i
x_i
xi是二维向量
1)计算参数数量
参数数
量
w
,
θ
=
(
2
∗
3
+
3
∗
2
+
2
∗
3
)
+
(
2
+
3
+
2
+
3
)
=
28
参数数量_{w,θ} = ( 2 * 3 + 3* 2 + 2 * 3) + ( 2 + 3 + 2 + 3) = 28
参数数量w,θ=(2∗3+3∗2+2∗3)+(2+3+2+3)=28
2)计算w(3,1,1)的梯度(图中红色边权)
![](https://img-blog.csdnimg.cn/1d183d595335482189c24a1b5c310e27.png)
d
L
(
w
,
θ
)
d
w
(
3
,
1
,
1
)
=
d
L
(
w
,
θ
)
d
N
(
4
,
1
)
o
u
t
∗
d
N
(
4
,
1
)
o
u
t
d
N
(
4
,
1
)
i
n
∗
d
N
(
4
,
1
)
i
n
d
w
(
3
,
1
,
1
)
\frac {d L(w,θ)} {d w(3,1,1)} = \frac {d L(w,θ)} {d N(4,1)_{out}} * \frac {d N(4,1)_{out}} {d N(4,1)_{in}} * \frac {d N(4,1)_{in}} {d w(3,1,1)}
dw(3,1,1)dL(w,θ)=dN(4,1)outdL(w,θ)∗dN(4,1)indN(4,1)out∗dw(3,1,1)dN(4,1)in
更具体地,将每个微分表达式求出,并假设激活函数是sigmoid函数(
f
′
(
x
)
=
f
(
x
)
(
1
−
f
(
x
)
)
f'(x) = f(x)(1-f(x))
f′(x)=f(x)(1−f(x))),则
d
L
(
w
,
θ
)
d
w
(
3
,
1
,
1
)
=
(
N
(
4
,
1
)
o
u
t
−
N
′
(
4
,
1
)
o
u
t
)
∗
N
(
4
,
1
)
o
u
t
∗
(
1
−
N
(
4
,
1
)
o
u
t
)
∗
N
(
3
,
1
)
o
u
t
\frac {d L(w,θ)} {d w(3,1,1)} =(N(4,1)_{out} - N'(4,1)_{out}) * N(4,1)_{out} *(1-N(4,1)_{out}) * N(3,1)_{out}
dw(3,1,1)dL(w,θ)=(N(4,1)out−N′(4,1)out)∗N(4,1)out∗(1−N(4,1)out)∗N(3,1)out
3)计算w(2,1,1)的梯度(图中红色边权、黄色边表示可以影响到欲求边权的中间路径)
![](https://img-blog.csdnimg.cn/831a7f074f8c41f2a75575fb427aae54.png)
d L ( w , θ ) d w ( 2 , 1 , 1 ) = ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ d N ( 4 , i ) i n d N ( 3 , 1 ) o u t ∗ d N ( 3 , 1 ) o u t d N ( 3 , 1 ) i n ∗ d N ( 3 , 1 ) i n d w ( 2 , 1 , 1 ) = d N ( 3 , 1 ) o u t d N ( 3 , 1 ) i n ∗ d N ( 3 , 1 ) i n d w ( 2 , 1 , 1 ) ∗ ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ d N ( 4 , i ) i n d N ( 3 , 1 ) o u t \frac {d L(w,θ)} {d w(2,1,1)} =\sum_{i}^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \frac {d N(4,i)_{in}} {d N(3,1)_{out}} * \frac {d N(3,1)_{out}} {d N(3,1)_{in}} * \frac {d N(3,1)_{in}} {d w(2,1,1)} \\ = \frac {d N(3,1)_{out}} {d N(3,1)_{in}} * \frac {d N(3,1)_{in}} {d w(2,1,1)} * \sum_{i}^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \frac {d N(4,i)_{in}} {d N(3,1)_{out}} \\ dw(2,1,1)dL(w,θ)=i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗dN(3,1)outdN(4,i)in∗dN(3,1)indN(3,1)out∗dw(2,1,1)dN(3,1)in=dN(3,1)indN(3,1)out∗dw(2,1,1)dN(3,1)in∗i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗dN(3,1)outdN(4,i)in
同2)将微分表达式代入更具体的表示,并假设激活函数是sigmoid函数,则
d
L
(
w
,
θ
)
d
w
(
2
,
1
,
1
)
=
N
(
3
,
1
)
o
u
t
∗
(
1
−
N
(
3
,
1
)
o
u
t
)
∗
N
(
2
,
1
)
o
u
t
∑
i
3
(
N
(
4
,
i
)
o
u
t
−
N
′
(
4
,
i
)
o
u
t
)
∗
N
(
4
,
1
)
o
u
t
∗
(
1
−
N
(
4
,
1
)
o
u
t
)
∗
w
(
3
,
1
,
i
)
\frac {d L(w,θ)} {d w(2,1,1)} = N(3,1)_{out} *(1-N(3,1)_{out})*N(2,1)_{out} \sum_{i}^3 (N(4,i)_{out} - N'(4,i)_{out})*N(4,1)_{out} *(1-N(4,1)_{out})*w(3,1,i)
dw(2,1,1)dL(w,θ)=N(3,1)out∗(1−N(3,1)out)∗N(2,1)outi∑3(N(4,i)out−N′(4,i)out)∗N(4,1)out∗(1−N(4,1)out)∗w(3,1,i)
下面再求梯度时不再代入更为具体的表示形式
4)计算w(1,1,1)的梯度(图中红色边权、黄色边表示可以影响到欲求边权的中间路径)
![](https://img-blog.csdnimg.cn/f9c85ebe47aa48e9b35bda8f84af414d.png)
d L ( w , θ ) d w ( 1 , 1 , 1 ) = ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ ∑ j 2 d N ( 4 , i ) i n d N ( 3 , j ) o u t ∗ d N ( 3 , j ) o u t d N ( 3 , j ) i n ∗ d N ( 3 , j ) i n d N ( 2 , 1 ) o u t ∗ d N ( 2 , 1 ) o u t d N ( 2 , 1 ) i n t ∗ d N ( 2 , 1 ) i n d w ( 1 , 1 , 1 ) = d N ( 2 , 1 ) o u t d N ( 2 , 1 ) i n ∗ d N ( 2 , 1 ) i n d w ( 1 , 1 , 1 ) ∗ ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ ∑ j 2 d N ( 4 , i ) i n d N ( 3 , j ) o u t ∗ d N ( 3 , j ) o u t d N ( 3 , j ) i n ∗ d N ( 3 , j ) i n d N ( 2 , 1 ) o u t \frac {d L(w,θ)} {d w(1,1,1)} =\sum_{i}^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \sum_{j}^2 \frac {d N(4,i)_{in}} {d N(3,j)_{out}} * \frac {d N(3,j)_{out}} {d N(3,j)_{in}} * \frac {d N(3,j)_{in}} {d N(2,1)_{out}} * \frac {d N(2,1)_{out}} {d N(2,1)_{int}} * \frac {d N(2,1)_{in}} {d w(1,1,1)} \\ = \frac {d N(2,1)_{out}} {d N(2,1)_{in}} * \frac {d N(2,1)_{in}} {d w(1,1,1)} * \sum_{i}^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \sum_{j}^2 \frac {d N(4,i)_{in}} {d N(3,j)_{out}} * \frac {d N(3,j)_{out}} {d N(3,j)_{in}} * \frac {d N(3,j)_{in}} {d N(2,1)_{out}} dw(1,1,1)dL(w,θ)=i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗j∑2dN(3,j)outdN(4,i)in∗dN(3,j)indN(3,j)out∗dN(2,1)outdN(3,j)in∗dN(2,1)intdN(2,1)out∗dw(1,1,1)dN(2,1)in=dN(2,1)indN(2,1)out∗dw(1,1,1)dN(2,1)in∗i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗j∑2dN(3,j)outdN(4,i)in∗dN(3,j)indN(3,j)out∗dN(2,1)outdN(3,j)in
5)计算θ(4,1)的梯度(绿色为阈值θ对应的MP单元)
![](https://img-blog.csdnimg.cn/f2848e661adb43aea2add59be7a3dafc.png)
d L ( w , θ ) d θ ( 4 , 1 ) = d L ( w , θ ) d N ( 4 , 1 ) o u t ∗ d N ( 4 , 1 ) o u t d N ( 4 , 1 ) i n ∗ d N ( 4 , 1 ) i n d θ ( 4 , 1 ) \frac {d L(w,θ)} {d θ(4,1)} = \frac {d L(w,θ)} {d N(4,1)_{out}} * \frac {d N(4,1)_{out}} {d N(4,1)_{in}} * \frac {d N(4,1)_{in}} {d θ(4,1)} dθ(4,1)dL(w,θ)=dN(4,1)outdL(w,θ)∗dN(4,1)indN(4,1)out∗dθ(4,1)dN(4,1)in
6)计算θ(3,1)的梯度(绿色为阈值θ对应的MP单元,黄色边表示为可以影响到欲求阈值的中间路径)
![](https://img-blog.csdnimg.cn/da871709baa04306bf708e4c60ed613b.png)
d L ( w , θ ) d θ ( 3 , 1 ) = ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ d N ( 4 , i ) i n d N ( 3 , 1 ) o u t ∗ d N ( 3 , 1 ) o u t d N ( 3 , 1 ) i n ∗ d N ( 3 , 1 ) i n d θ ( 3 , 1 ) = d N ( 3 , 1 ) o u t d N ( 3 , 1 ) i n ∗ d N ( 3 , 1 ) i n d θ ( 3 , 1 ) ∗ ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ d N ( 4 , i ) i n d N ( 3 , 1 ) o u t \frac {d L(w,θ)} {d θ(3,1)} = \sum_i^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \frac {d N(4,i)_{in}} {d N(3,1)_{out}} * \frac {d N(3,1)_{out}} {d N(3,1)_{in}} * \frac {d N(3,1)_{in}} {d θ(3,1)} \\ = \frac {d N(3,1)_{out}} {d N(3,1)_{in}} * \frac {d N(3,1)_{in}} {d θ(3,1)} * \sum_i^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \frac {d N(4,i)_{in}} {d N(3,1)_{out}} dθ(3,1)dL(w,θ)=i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗dN(3,1)outdN(4,i)in∗dN(3,1)indN(3,1)out∗dθ(3,1)dN(3,1)in=dN(3,1)indN(3,1)out∗dθ(3,1)dN(3,1)in∗i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗dN(3,1)outdN(4,i)in
7)计算θ(2,1)的梯度(绿色为阈值θ对应的MP单元,黄色边表示为可以影响到欲求阈值的中间路径)
![](https://img-blog.csdnimg.cn/a56821b97bac45ccb9550fa1d34e6057.png)
d L ( w , θ ) d θ ( 2 , 1 ) = ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ ∑ j 2 d N ( 4 , i ) i n d N ( 3 , j ) o u t ∗ d N ( 3 , j ) o u t d N ( 3 , j ) i n ∗ d N ( 3 , j ) i n d N ( 2 , 1 ) o u t ∗ d N ( 2 , 1 ) o u t d N ( 2 , 1 ) i n ∗ d N ( 2 , 1 ) i n d θ ( 2 , 1 ) = d N ( 2 , 1 ) o u t d N ( 2 , 1 ) i n ∗ d N ( 2 , 1 ) i n d θ ( 2 , 1 ) ∗ ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ ∑ j 2 d N ( 4 , i ) i n d N ( 3 , j ) o u t ∗ d N ( 3 , j ) o u t d N ( 3 , j ) i n ∗ d N ( 3 , j ) i n d N ( 2 , 1 ) o u t \frac {d L(w,θ)} {d θ(2,1)} = \sum_i^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \sum_j^2 \frac {d N(4,i)_{in}} {d N(3,j)_{out}} * \frac {d N(3,j)_{out}} {d N(3,j)_{in}} * \frac {d N(3,j)_{in}} {d N(2,1)_{out}} * \frac {d N(2,1)_{out}} {d N(2,1)_{in}} * \frac {d N(2,1)_{in}} {d θ(2,1)} \\ = \frac {d N(2,1)_{out}} {d N(2,1)_{in}} * \frac {d N(2,1)_{in}} {d θ(2,1)} * \sum_i^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \sum_j^2 \frac {d N(4,i)_{in}} {d N(3,j)_{out}} * \frac {d N(3,j)_{out}} {d N(3,j)_{in}} * \frac {d N(3,j)_{in}} {d N(2,1)_{out}} dθ(2,1)dL(w,θ)=i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗j∑2dN(3,j)outdN(4,i)in∗dN(3,j)indN(3,j)out∗dN(2,1)outdN(3,j)in∗dN(2,1)indN(2,1)out∗dθ(2,1)dN(2,1)in=dN(2,1)indN(2,1)out∗dθ(2,1)dN(2,1)in∗i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗j∑2dN(3,j)outdN(4,i)in∗dN(3,j)indN(3,j)out∗dN(2,1)outdN(3,j)in
8)计算θ(1,1)的梯度(绿色为阈值θ对应的MP单元,黄色边表示为可以影响到欲求阈值的中间路径)
![](https://img-blog.csdnimg.cn/bc924102ab3141468f9bf6db64110ae9.png)
d L ( w , θ ) d θ ( 1 , 1 ) = ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ ∑ j 2 d N ( 4 , i ) i n d N ( 3 , j ) o u t ∗ d N ( 3 , j ) o u t d N ( 3 , j ) i n ∗ ∑ k 3 d N ( 3 , j ) i n d N ( 2 , k ) o u t ∗ d N ( 2 , k ) o u t d N ( 2 , k ) i n ∗ d N ( 2 , k ) i n d N ( 1 , 1 ) o u t ∗ d N ( 1 , 1 ) o u t d N ( 1 , 1 ) i n ∗ d N ( 1 , 1 ) i n d θ ( 1 , 1 ) = d N ( 1 , 1 ) o u t d N ( 1 , 1 ) i n ∗ d N ( 1 , 1 ) i n d θ ( 1 , 1 ) ∗ ∑ i 3 d L ( w , θ ) d N ( 4 , i ) o u t ∗ d N ( 4 , i ) o u t d N ( 4 , i ) i n ∗ ∑ j 2 d N ( 4 , i ) i n d N ( 3 , j ) o u t ∗ d N ( 3 , j ) o u t d N ( 3 , j ) i n ∗ ∑ k 3 d N ( 3 , j ) i n d N ( 2 , k ) o u t ∗ d N ( 2 , k ) o u t d N ( 2 , k ) i n ∗ d N ( 2 , k ) i n d N ( 1 , 1 ) o u t \frac {d L(w,θ)} {d θ(1,1)} = \sum_i^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \sum_j^2 \frac {d N(4,i)_{in}} {d N(3,j)_{out}} * \frac {d N(3,j)_{out}} {d N(3,j)_{in}} * \sum_k^3 \frac {d N(3,j)_{in}} {d N(2,k)_{out}} * \frac {d N(2,k)_{out}} {d N(2,k)_{in}} * \frac {d N(2,k)_{in}} {d N(1,1)_{out}} * \frac {d N(1,1)_{out}} {d N(1,1)_{in}} * \frac {d N(1,1)_{in}} {d θ(1,1)} \\ =\frac {d N(1,1)_{out}} {d N(1,1)_{in}} * \frac {d N(1,1)_{in}} {d θ(1,1)} * \sum_i^3 \frac {d L(w,θ)} {d N(4,i)_{out}} * \frac {d N(4,i)_{out}} {d N(4,i)_{in}} * \sum_j^2 \frac {d N(4,i)_{in}} {d N(3,j)_{out}} * \frac {d N(3,j)_{out}} {d N(3,j)_{in}} * \sum_k^3 \frac {d N(3,j)_{in}} {d N(2,k)_{out}} * \frac {d N(2,k)_{out}} {d N(2,k)_{in}} * \frac {d N(2,k)_{in}} {d N(1,1)_{out}} dθ(1,1)dL(w,θ)=i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗j∑2dN(3,j)outdN(4,i)in∗dN(3,j)indN(3,j)out∗k∑3dN(2,k)outdN(3,j)in∗dN(2,k)indN(2,k)out∗dN(1,1)outdN(2,k)in∗dN(1,1)indN(1,1)out∗dθ(1,1)dN(1,1)in=dN(1,1)indN(1,1)out∗dθ(1,1)dN(1,1)in∗i∑3dN(4,i)outdL(w,θ)∗dN(4,i)indN(4,i)out∗j∑2dN(3,j)outdN(4,i)in∗dN(3,j)indN(3,j)out∗k∑3dN(2,k)outdN(3,j)in∗dN(2,k)indN(2,k)out∗dN(1,1)outdN(2,k)in
2.4.归纳前馈神经网络求参数梯度闭式解的通式
通过上述的示例,我们求出了4层网络中每层对应的4组阈值之一和3组边权之一,这意味着我们可以求出上述示例MLP中任意参数的梯度。
实际上,如果为了推导出全连接MLP任意参数求梯度的通式,仅通过边权和阈值的两组具体求解,我们就已经可以求出通式。费力地写出全部梯度表达式,不仅能更容易归纳,还可以清楚地表明反向传播逐步传播的过程(这与正向传播的顺序恰好相反)
另外,我们发现了一个问题,为何4层网络对应了4组阈值,但只对应了3组边权?实际上,这个问题是因为输出层直接将输出层MP单元的输出值直接参与损失函数的运算,这意味着最后一步输出作为输入的函数及其参数是固定的,不需要再调整了,故只有3组边权。
下面给出任意MLP前馈神经网络求任意参数闭式解的通式
由于我们这里不再限制MLP网络的层数和每层的MP单元数,在4中定义的符号的基础上,我们需增设两个符号定义:
⑦最大层数:m
⑧每层最大单元数:
n
i
,
i
∈
{
1
,
2
,
.
.
m
}
n^i,i∈\{1,2,..m\}
ni,i∈{1,2,..m}
1)求w(i,j,k)的梯度
d
L
(
w
,
θ
)
d
w
(
i
,
j
,
k
)
=
d
N
(
i
+
1
,
k
)
o
u
t
d
N
(
i
+
1
,
k
)
i
n
∗
d
N
(
i
+
1
,
1
)
i
n
d
w
(
i
,
j
,
k
)
∗
∑
l
m
n
m
d
L
(
w
,
θ
)
d
N
(
m
,
l
m
)
o
u
t
∗
d
N
(
m
,
l
m
)
o
u
t
d
N
(
m
,
l
m
)
i
n
∗
∑
l
m
−
1
n
m
−
1
d
N
(
m
,
l
m
)
i
n
d
N
(
m
−
1
,
l
m
−
1
)
o
u
t
∗
d
N
(
m
−
1
,
l
m
−
1
)
o
u
t
d
N
(
m
−
1
,
l
m
−
1
)
i
n
∗
.
.
.
∗
∑
l
i
+
2
n
i
+
2
d
N
(
i
+
3
,
l
i
+
3
)
i
n
d
N
(
i
+
2
,
l
i
+
2
)
o
u
t
∗
d
N
(
i
+
2
,
l
i
+
2
)
o
u
t
d
N
(
i
+
2
,
l
i
+
2
)
i
n
∗
d
N
(
i
+
2
,
l
i
+
2
)
i
n
d
N
(
i
+
1
,
k
)
o
u
t
\frac {d L(w,θ)} {d w(i,j,k)} = \frac {d N(i+1,k)_{out}} {d N(i+1,k)_{in}} * \frac {d N(i+1,1)_{in}} {d w(i,j,k)} * \sum_{l^m}^{n^m} \frac {d L(w,θ)} {d N(m,{l^m})_{out}} * \frac {d N(m,{l^m})_{out}} {d N(m,{l^m})_{in}} * \sum_{l^{m-1}}^{n^{m-1}} \frac {d N(m,{l^m})_{in}} {d N(m-1,{l^{m-1}})_{out}} * \frac {d N(m-1,{l^{m-1}})_{out}} {d N(m-1,{l^{m-1}})_{in}} * ... * \sum_{l^{i+2}}^{n^{i+2}} \frac {d N(i+3,{l^{i+3}})_{in}} {d N(i+2,{l^{i+2}})_{out}} * \frac {d N(i+2,{l^{i+2}})_{out}} {d N(i+2,{l^{i+2}})_{in}} * \frac {d N(i+2,{l^{i+2}})_{in}} {d N(i+1,k)_{out}}
dw(i,j,k)dL(w,θ)=dN(i+1,k)indN(i+1,k)out∗dw(i,j,k)dN(i+1,1)in∗lm∑nmdN(m,lm)outdL(w,θ)∗dN(m,lm)indN(m,lm)out∗lm−1∑nm−1dN(m−1,lm−1)outdN(m,lm)in∗dN(m−1,lm−1)indN(m−1,lm−1)out∗...∗li+2∑ni+2dN(i+2,li+2)outdN(i+3,li+3)in∗dN(i+2,li+2)indN(i+2,li+2)out∗dN(i+1,k)outdN(i+2,li+2)in
2)求θ(i,j)的梯度
d
L
(
w
,
θ
)
d
θ
(
i
,
j
)
=
d
N
(
i
,
j
)
o
u
t
d
N
(
i
,
j
)
i
n
∗
d
N
(
i
,
j
)
i
n
d
θ
(
i
,
j
)
∗
∑
l
m
n
m
d
L
(
w
,
θ
)
d
N
(
m
,
l
m
)
o
u
t
∗
d
N
(
m
,
l
m
)
o
u
t
d
N
(
m
,
l
m
)
i
n
∗
∑
l
m
−
1
n
m
−
1
d
N
(
m
,
l
m
)
i
n
d
N
(
m
−
1
,
l
m
−
1
)
o
u
t
∗
d
N
(
m
−
1
,
l
m
−
1
)
o
u
t
d
N
(
m
−
1
,
l
m
−
1
)
i
n
∗
.
.
.
∗
∑
l
i
+
1
n
i
+
1
d
N
(
i
+
2
,
l
i
+
2
)
i
n
d
N
(
i
+
1
,
l
i
+
1
)
o
u
t
∗
d
N
(
i
+
1
,
l
i
+
1
)
o
u
t
d
N
(
i
+
1
,
l
i
+
1
)
i
n
∗
d
N
(
i
+
1
,
l
i
+
1
)
i
n
d
N
(
i
,
j
)
o
u
t
\frac {d L(w,θ)} {d θ(i,j)} = \frac {d N(i,j)_{out}} {d N(i,j)_{in}} * \frac {d N(i,j)_{in}} {d θ(i,j)} * \sum_{l^m}^{n^m} \frac {d L(w,θ)} {d N(m,{l^m})_{out}} * \frac {d N(m,{l^m})_{out}} {d N(m,{l^m})_{in}} * \sum_{l^{m-1}}^{n^{m-1}} \frac {d N(m,{l^m})_{in}} {d N(m-1,{l^{m-1}})_{out}} * \frac {d N(m-1,{l^{m-1}})_{out}} {d N(m-1,{l^{m-1}})_{in}} * ... * \sum_{l^{i+1}}^{n^{i+1}} \frac {d N(i+2,{l^{i+2}})_{in}} {d N(i+1,{l^{i+1}})_{out}} * \frac {d N(i+1,{l^{i+1}})_{out}} {d N(i+1,{l^{i+1}})_{in}} * \frac {d N(i+1,{l^{i+1}})_{in}} {d N(i,j)_{out}}
dθ(i,j)dL(w,θ)=dN(i,j)indN(i,j)out∗dθ(i,j)dN(i,j)in∗lm∑nmdN(m,lm)outdL(w,θ)∗dN(m,lm)indN(m,lm)out∗lm−1∑nm−1dN(m−1,lm−1)outdN(m,lm)in∗dN(m−1,lm−1)indN(m−1,lm−1)out∗...∗li+1∑ni+1dN(i+1,li+1)outdN(i+2,li+2)in∗dN(i+1,li+1)indN(i+1,li+1)out∗dN(i,j)outdN(i+1,li+1)in
2.5.其他补充
1) 通过微分的链式法则可以轻易地得到上述MLP模型中任意参数梯度的闭解,可以计算每一个参数的梯度值,但是在软件的实际实现中每次都重复计算会造成指数级的时间消耗。通过观察,在梯度逆向传播的过程中有许多重复的子表达式,以及重复的正向传播时的计算值。在反向计算梯度时可以将这些值保存下来,通过动态规划减小时间复杂度。
2)反向传播算法实际上不止适用于全连接的MLP网络图,对于任何连接方式的图都可以进行计算。计算时可以通过符号到数值(计算图与表达式)进行计算也可以通过符号到符号(计算图再添加额外的节点)计算。实际上,符号到符号的方法的描述包含了符号到数值的方法,符号到数值的方法可以理解为执行了与符号到符号的方法中构建图的过程中完全相同的计算,只是符号到数值的方法不能显示出计算图。
3)反向传播算法是深度学习计算参数梯度的一种实用方法,但不是唯一方法也不是最优方法。
3.卷积神经网络
3.1.卷积神经网络的定义
卷积神经网络(卷积网络CNN)是一种专门用来处理具有网格结构数据的神经网络,它在神经网络中的至少一层上使用卷积运算来代替一般的矩阵乘法运算。
3.2.卷积运算
3.2.1卷积运算的定义
卷积运算是一种特殊的线性运算,是两个实值函数相乘的一种数学运算
设自变量与x有关的函数 f ( x ) g ( x ) f(x)g(x) f(x)g(x),其中 f ( x ) f(x) f(x)称为输入, g ( x ) g(x) g(x)称为核函数,卷积结果有时称为特征映射,则
f f f与 g g g的卷积运算可抽象表示为: ( f ∗ g ) ( x ) (f*g)(x) (f∗g)(x)
连续型卷积: ( f ∗ g ) ( x ) = ∫ − ∞ ∞ f ( x ) g ( n − x ) d x (f*g)(x)=\int_{-∞}^∞ f(x)g(n-x)dx (f∗g)(x)=∫−∞∞f(x)g(n−x)dx
离散型卷积: ( f ∗ g ) ( x ) = ∑ − ∞ ∞ f ( x ) g ( n − x ) d x (f*g)(x)=\sum_{-∞}^∞ f(x)g(n-x)dx (f∗g)(x)=∑−∞∞f(x)g(n−x)dx
3.2.2卷积运算的现实例子
1)离散型卷积:抛筛子
抛两次筛子,求两次筛子点数之和为4的概率:
P ( n = 4 ) = f ( 1 ) ∗ g ( 3 ) + f ( 2 ) ∗ g ( 2 ) + f ( 3 ) ∗ g ( 1 ) = ∑ 1 3 f ( x ) g ( 4 − x ) = ( f ∗ g ) ( x ) P (n=4) = f(1)*g(3) + f(2)*g(2) + f(3)*g(1)=\sum_1^3 f(x)g(4-x) = (f*g)(x) P(n=4)=f(1)∗g(3)+f(2)∗g(2)+f(3)∗g(1)=∑13f(x)g(4−x)=(f∗g)(x)
2)连续型卷积:进出水问题
有一个水桶(假设水桶足够大)有两个孔,一个进水,一个出水,进水量是关于时间x的函数f(x);进入的水经过出水,剩余水量的比例也是关于时间x的函数g,求从x=0时刻开始,时刻x=t时水桶中的水的量
V ( t ) = ∫ 0 t f ( x ) g ( t − x ) d x V(t) = \int_0^t f(x)g(t-x) dx V(t)=∫0tf(x)g(t−x)dx
在上述两个实际的例子中,我们将问题域以外的函数值都设为0,那么整个实数域上的卷积运算则可缩小至在有限问题域上的运算
3.2.3将卷积运算扩展到二维
上述卷积的定义包括卷积的例子都是一维的(Toeplitz矩阵),这一维度往往是时间/决策步,将运算扩展到2维的矩阵(双重块循环矩阵)时,常用于对图像(像素矩阵)的处理
S ( i , j ) = ( I , K ) ( i , j ) = ∑ m ∑ n I ( m , n ) K ( i − m , j − n ) S(i,j) = (I,K)(i,j) = \sum_m \sum_n I(m,n)K(i-m,j-n) S(i,j)=(I,K)(i,j)=∑m∑nI(m,n)K(i−m,j−n)
给出具体的计算示例
![](https://img-blog.csdnimg.cn/41f08026f9b54a569cc61eadc93f548c.png)
![](https://img-blog.csdnimg.cn/57b5e057506b478989f12d8e122babd0.gif)
3.2.4二维灰度图卷积的示例:平滑化
有一张图像上有许多噪点(高频信号就如同平地中耸立的山峰)为了去除这些噪点,使得图像更加平滑(铲平山峰)可以通过取局部平均的方式(将这些山峰的土挖掉均匀铺在平地上)
设核矩阵为: g = [ 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 ] g=\begin{bmatrix} \frac 1 9 & \frac 1 9 & \frac 1 9 \\ \frac 1 9 & \frac 1 9 & \frac 1 9 \\ \frac 1 9 & \frac 1 9 & \frac 1 9 \end{bmatrix} g= 919191919191919191
令图像的所有像素点与核矩阵做卷积运算,即可实现平滑化(这里为简单起见使用的是算数平均,而非高斯分布矩阵)
![](https://img-blog.csdnimg.cn/4b60021f3b1b406a8efc00849f3a748d.png)
平滑后得到:
![](https://img-blog.csdnimg.cn/feb041c8f4994932904701d61f66a7e5.png)
3.2.5卷积翻转与互相关函数
1)翻转
卷积运算具有可交换性,体现在对输入数据的翻转(实际仅是输入的计算顺序互逆)
S ( i , j ) = ( I , K ) ( i , j ) = ∑ m ∑ n I ( m , n ) K ( i − m , j − n ) ≌ ∑ m ∑ n I ( i − m , j − n ) K ( m , n ) = ( K , I ) ( i , j ) S(i,j) = (I,K)(i,j) = \sum_m \sum_n I(m,n)K(i-m,j-n) ≌ \sum_m \sum_n I(i-m,j-n)K(m,n) = (K,I)(i,j) S(i,j)=(I,K)(i,j)=∑m∑nI(m,n)K(i−m,j−n)≌∑m∑nI(i−m,j−n)K(m,n)=(K,I)(i,j)
2)互相关函数
与卷积运算几乎一样,但并不翻转核,许多机器学习库中的卷积由它代替,且经常也被称为卷积运算
S ( i , j ) = ( I , K ) ( i , j ) = ∑ m ∑ n I ( i + m , j + n ) K ( m , n ) S(i,j) = (I,K)(i,j) = \sum_m \sum_n I(i+m,j+n)K(m,n) S(i,j)=(I,K)(i,j)=∑m∑nI(i+m,j+n)K(m,n)
3.3. 神经网络中为何用卷积运算代替矩阵乘法运算(动机、特点)
3.3.1.稀疏交互(稀疏连接、稀疏权重)
核的规模远小于输入的规模,这意味着相比于全连接的MLP而言,我们需要的参数更少,这可以理解为我们限制了该层输出与输入的连接数,不仅减小了模型的存储需求也提高了统计效率。
1)从输入元素的角度看,每个输入元素影响的输出更少
![](https://img-blog.csdnimg.cn/476e8a71a6d540b893253ce2df11f2eb.png)
![](https://img-blog.csdnimg.cn/4f4e4b7fb88d49acbc071d7412b28540.png)
3)处于卷积浅层的输出单元受输入单元影响的个数会减小,但处于更深层的单元,它们受卷积层输入单元的影响的个数并不会减少。若网络还包含类似步幅卷积或池化的结构,这种现象会加强
![](https://img-blog.csdnimg.cn/36e4eebd6533491c8e32b9f7b46678f9.png)
3.3.2参数共享
1)卷积层单元的多个函数使用相同的参数,参数共享保证我们只需要学习一个参数集合,而非每一个单元都要学习一组单独的参数。这没有减少传播的速度,但是减少了参数的存储消耗
2)右图中由于卷积的参数共享,3元素核的中间参数被同时影响了所有输出MP,左图全连接中的一个参数只能影响一个输出MP
![](https://img-blog.csdnimg.cn/58252d932ad048e9a984933e43acd882.png)
3.3.3平移等变
1)等变表示:若一个函数满足输入改变,输出也以同样方式改变的性质,该函数就是等变的。特别地,若函数 f ( x ) , g ( x ) f(x),g(x) f(x),g(x)满足: f ( g ( x ) ) = g ( f ( x ) ) f(g(x)) = g(f(x)) f(g(x))=g(f(x)),我们就说函数 f ( x ) f(x) f(x)对于变换g具有等变性
2)对于卷积而言,令g是输入的任意平移函数,那么卷积函数对于g是等变的,即对矩阵和卷积核先卷积运算再平移与先平移再卷积等价
3)卷积对其他的一些变换不是天然的,如对图像尺寸和角度的变换,需要一些其他的机制处理这些变换
3.4.在神经网络中应用卷积运算
在神经网路中应用卷积运算与数学上直接进行的卷积运算略有所不同
3.4.1提取多种特征
每当像素矩阵与卷积核进行一次卷积运算时,可以看作对图像提取了一种类型的特征。实际应用中,我们希望在神经网络的一层里能够提取多种不同类型的特征。这时每一次卷积操作都包含了像素矩阵同多个卷积核进行一次卷积运算。
3.4.2多通道卷积
1)多道输入描述
对于灰度图而言,像素矩阵的每一个位置都是一个0-255的标量,但是对于彩色图而言,像素矩阵的每一个位置都是RGB三通道的向量,因而卷积计算的方式会发生一定改变。具体而言,可以将彩色图的像素矩阵以颜色通道的维度分成3个矩阵,这时描述单张图像的输入数据则变为了3维张量
另外,由于深度学习的训练往往使用batch或minibatch的方式,每次训练会选择多张图像,由此可以增加第四维
故对输入的描述是4维的:
①通道索引
②某通道像素矩阵的x索引
③某通道像素矩阵的y索引
④图像索引
2)多道卷积核描述
为了适应多通道的卷积操作,卷积核也需要是多通道的,且输入与卷积核的通道数相等(这时卷积运算不一定是可交换的)
另外,我们会在卷积操作的线性乘法之后增加一个偏置,该偏置可以是每个卷积核使用一个共享的偏置;可以是以通道为维度划分,每个通道共享一个偏置或是对应于每一个输出位置都设置一个单独的偏置。
因而对卷积核的描述也是4维的:
①通道索引
②某通道卷积核矩阵的x索引
③某通道卷积核矩阵的y索引
④偏置索引
3)多核卷积运算规则(这里不给出解析描述,只通过易于理解的图像计算示例)
![](https://img-blog.csdnimg.cn/acd2bb1a07944534bd856aa74260d597.png)
如上述所示,左侧零填充后的3个蓝色矩阵分别为图像3个通道的像素矩阵,中间2组红色矩阵是2个卷积核,每个卷积核都有对应于输入通道的的3个滤波器。另外在2组卷积核最后还有以卷积核为维度划分的卷积核共享的2个偏置。
计算方法为:每个通道的像素矩阵分别与对应通道的滤波器矩阵相乘并在同一位置进行相加,最后再加上偏置得到一个仿射输出
4)下采样卷积
有时在进行卷积运算时为了降低计算开销(相应的代价是提取的特征没有之前那么好了)会在对像素矩阵进行逐块卷积时,跳过一些块,跳过的块数称为下采样卷积的步幅,当步幅为1则与常规的卷积运算相同。
![](https://img-blog.csdnimg.cn/c8e70b906a454e86a459d310b69e7f03.png)
如图,下图为运算时步幅为1时但输出步幅为2,上图为在运算时就设置了步幅为2的下采样卷积,每隔一块做一次卷积运算
5)不同维度和结构的输入数据进行卷积运算的任务
![](https://img-blog.csdnimg.cn/0d1bcf60257640a28f792a5545c07d11.png)
3.4.3零填充
1)为何进行零填充
如果不进行零填充,表示的宽度在每一层就会缩减,缩小后输出的长为:M-m +1,宽为:N-n+1,对输入填充0允许我们对核的宽度和输出的大小进行独立的控制。若不进行0填充,我们就只能让空间宽度的快速缩减,或只能选择一个很小的核
2)填充方法
①有效卷积valid:不填0,输出像素的表示更加规范,但输出的大小会在每一层缩减,若卷积核的规模很大,缩减速度会显著增加,这限制了网络的层数,当层数增加时,网格的空间维度最终会缩减到1×1而不能再进行有意义的卷积了
②相同卷积same:填充足左右、上下m-1,n-1个来保持输出和输入有相同的大小,这种情况下,网络能够包含任意多的卷积层。但是输出像素靠近边界的部分相比于中间部分对输出像素的影响更小,这可能会导致边界像素存在一定程度的欠表示。
③全卷积full:它进行了足够多的0填充使得每个像素在每个方向上被访问了k次,最终输出图像的宽度为m+k-1。这种填充方法会使得输出像素中靠近边界的部分相比于中间部分是更少像素的函数,这将导致学得一个在卷积特征映射的所有位置都表现不错的单核更为困难。
通常零填充的最优数量在valid和same之间
3.4.4局部连接(非共享卷积)
两个网络层之间采用局部连接而不采用全连接,这符合卷积层稀疏连接的特点。但与之不同的是,卷积层不仅采用了稀疏连接,还采用了参数共享,而局部连接则没有采用共享的参数,在局部连接之间使用各自独立的边权。
![](https://img-blog.csdnimg.cn/6aecbcd9f1f647b5bb220b4e5963aaab.png)
这样做的动机是:当我们知道每一个特征都是一小部分空间的函数而不是整个空间的特征时,局部连接层是有用的,例如对象识别中我们判断一张图片是否为人脸时只需要寻找嘴巴是否在图像下部分的中央即可。
3.4.5拼贴卷积
从参数共享上,对卷积层和局部连接层进行折中,拼贴卷积学习一组核卷积,在进行卷积运算时循环使用每个核卷积。
相比于局部连接的独立边权,增加了参数共享;
相比于卷积层增加了参数集合的独立参数个数,削弱了共享性。
![](https://img-blog.csdnimg.cn/62898c4df58e48cbb8b6fa285524eb74.png)
3.4.6其他卷积相关的运算
1)通过卷积定义的矩阵转置的乘法,可以用于计算通过卷积层反向传播误差的导数,还可以用于从隐藏层重构可视化单元。其大小取决于三个方面:零填充策略、前向传播的步幅、前向传播的输出大小
2)从隐藏层单元重构可视化单元,包括自编码器、RBM和稀疏编码等
3)训练任意深度的前馈卷积网络、训练带有(基于卷积转置的)重构函数网络(具体推导解析式略)
需要的算法:卷积、从输出到权重的反向传播、从输出到输入的反向传播
3.4.7高效的卷积算法
1)Fourier变换
2)通过组合方法使用可分离核的外积执行一个n维的卷积运算
3.4.8无监督学习卷积层
在包含了卷积层和全连接层的卷积神经网络中,计算代价更多在于卷积层的提取特征,而非全连接层学习映射函数。原因是经过卷积和池化在全连接层的特征维度已经缩小了很多,但是在卷积层则需要面对大维度的卷积核学习。
1)为了减少卷积层的学习成本,可以采用一些无监督的方法来获得卷积核(卷积层的参数学习)
①简单地随机初始化卷积核(随机过滤器,具有天然的频率选择和平移等变)
②手动设计卷积核,如设计卷积核为在特定方向或尺度来检测边缘
③通过无监督的方法学习卷积核,如将k聚类算法用于小图像块,然后用学得的中心作为卷积核
2)中间方法:介于监督学习和无监督学习之间的方法。不需要在每个梯度步骤中都进行完整的前向和反向传播,利用逐层贪心式预训练,独立地训练第一层,然后从第一层独立地提取所有特征一次,将这些特征隔离地训练第二层。
3)无监督地学习卷积层的好处难以说清,可认为无监督预学习相对于监督学习提供了一定的正则化,它直观地减少了计算成本允许我们训练更大的结构。
3.5.池化
3.5.1池化的概念
池化函数使用某一位置的相邻输出的总体统计特征来代替网络在该位置的输出
3.5.2池化的实现方式
最大池化、平均池化、L2范数池化、依靠距中心像素距离的加权平均池化
3.5…3池化的作用
1)池化综合了全部邻居的反馈,使得池化单元可以少于探测单元,因而我们可以通过综合池化区域k个像素的统计特征而不是单个像素来实现,这提高了网络的统计效率也降低了对参数的存储需求
2)池化对于处理不同大小的输入具有重要作用,如分类层的输入必须是固定大小的,但输入图像的大小不一,通过动态调整池化区域与输入矩阵相适应可以使得分类层总能接收到相同数量的统计特征而不论图像最初的输入大小了。
3)池化可能会使得一些自顶向下信息的神经网络结构变得复杂(如玻尔兹曼机和自编码器)
3.5.4池化的平移不变性
1)无论采用什么样的池化函数,当输入作少量平移时,池化能够帮助我们实现表示的近似不变,这种近似不变是指天然的平移不变性,即将输入平移一个微小的量,大多数通过池化函数的输出值不会发生改变。
如下图,当输入图像向右平移一个单位时,输入数据在每个位置都发生了改变,但是通过最大池化函数,输出结果仅有一半发生了改变
![](https://img-blog.csdnimg.cn/c75c28eef8414967a45c13ee30055ddb.png)
局部平移不变性很重要,尤其在关注某个特征是否出现而不关心它具体的位置时。但在一些领域,保存特征的具体位置却很重要。
3.5.5通过池化学习对其他变换的不变性
池化函数对局部平移具有天然的不变性,但对其他的变换则没有,如果想模型对输入的某些变换具有不变性,则需要通过多道方法:使用分离的参数学得多个特征,再利用池化函数进行池化。
如下图,使用三个学得的滤波器(卷积运算的提取特征)和一个池化函数可以学得对旋转变换的不变性,三个滤波器尝试匹配不同方向的5,任意一个过滤器匹配到5都会在探测单元(激活函数)中引起大的激活,然后无论哪个探测单元被激活,最大池化单元都有大的激活
![](https://img-blog.csdnimg.cn/b3318882b1f344f8a651b3788fc6861c.png)
3.5.6池化层在神经网络中的位置如何
卷积神经网络的卷积层通常有三级:
①第一级中,卷积层并行地进行多个卷积运算来产生一组线性激活函数
②第二级中,非线性的激活函数(如ReLU)作用在第一级的每个线性输出上,这一级也称探测级
③第三级中,使用池化函数进一步调整卷积层的输出
![](https://img-blog.csdnimg.cn/f691efe9ddc042a0bedf6e98fc3aaba0.png)
3.6.将卷积和池化看作一种无限强的先验
3.6.1先验的强弱
先验的强弱取决于概率密度的集中程度
1)弱先验:有较高的熵(不确定性强,可视为我们对这种先验的把握不大,它更容易随着观察数据而改变)
2)强先验:有较低的熵(确定性强,可视为我们对这种先验的把握很大,它不容易随着观察数据而改变)
3.6.2卷积
我们可以将卷积看作在MLP层中增加了无限强的先验概率分布,即:
①一个隐藏单元的权值必须和它邻居的权值相等,但在空间中改变(参数共享)
②除了那些处在隐藏空间连续的小的接受域内的权值以外,其余的权值均为零(稀疏连接)
该先验使该层学得的函数只包含局部连接关系,并对平移具有等变性。
3.6.3池化
池化也可以看作是一种无限强的先验:每一个单元都具有少量的平移不变性
3.6.4观察卷积神经网络的工作特点
将卷积神经网络看作无限强先验的MLP会导致极大的计算浪费,但可以帮助观察卷积神经网络是如何工作的
1)卷积和池化可能会导致欠拟合:如果一项任务依赖于保存精确的空间信息,那么在所有特征上使用池化会增大训练误差
2)当我们比较卷积的表现时,只能以基准中的其他卷积模型作为比较的对象
3.7. 结构化输出
3.7.1结构化输出的概念
卷积神经网络不仅可以预测分类和回归任务,还可以用于输出高维的结构化对象,通常这个结构化对象是一个张量,由标准卷积层产生。如张量S,其元素 S i , j , k S_{i,j,k} Si,j,k表示输入像素(i,j)属于种类k的概率。这允许模型标记图像中的每个像素,并绘制沿着单个对象轮廓的精确掩模。
3.7.2如何实现
先产生图像标签的原始猜测,再用相邻像素之间的交互来修正该原始猜测。重复这个修正步骤相当于在每一步使用相同的卷积,该卷积在深层网络的最后几层之间共享权重,这形成了一种特殊的循环卷积网络结构
3.7.3进一步处理全部像素的预测
对全部像素预测的进一步处理,可以获得图像在区域上的分割。一般的想法是假设大片相连的像素倾向于相同的标签,图模型可以描述相邻像素间的概率关系。卷积网络还可以被训练用于最大化近似图模型的训练目标
3.7.4问题与解决
由于大步幅的池化,输出矩阵比输入矩阵小
解决方法:
①避免把池化放在一起
②不做处理,产生一张低分辨率的标签网格
③使用单步幅的池化操作
4.循环神经网络
4.1.循环神经网络
1)概念
循环神经网络RNN:是一类用于处理序列数据( x 1 , x 2 , . . . , x r x^1,x^2,...,x^r x1,x2,...,xr)的神经网络
2)动机/特点
[1]从MLP发展到RNN的动机:参数共享,参数共享使模型能够扩展到不同形式的样本并进行泛化,当信息的特定部分会在序列内多个位置出现时,这样的共享尤为重要(如若在每个时间点都有一个单独的参数,我们则无法泛化到训练时没有见到的序列长度,也不能在时间上共享不同序列长度和不同位置的统计强度)
[2]减少参数数目的代价是优化参数可能更加困难,因为参数共享依赖于相同参数可用于不同时间步的假设:即假设给定时刻t的变量后,时刻t+1变量条件分布是平稳的。原则上可以使用t作为每个时间步的额外输入,并让学习器在发现任何时间依赖性的同时,不同时间步间尽可能多地共享
3)时延神经网络与循环神经网络
[1]时延神经网络:使用一维时间序列的卷积,参数共享的方式是使用相同的卷积核
[2]循环神经网络:输出的每一项是输出前一项的函数(前一项的输出作为后一项的一个输入)参数共享的方式是:后一项的输出和前一项的输出使用相同的更新规则(函数)
这种循环导致参数通过很深的计算图进行共享,周期代表变量自身的值在未来某一时间步对自身的影响
4)计算图
在多层前馈神经网络中,对计算图进行了介绍,计算图实际是形式化一组计算结构的方式。
一般可用循环图和展开图对RNN隐藏层的计算进行描述
[1]循环计算图
对应的函数表达: h t = f ( h t − 1 , x t ; θ ) h^t = f(h^{t-1},x^t;θ) ht=f(ht−1,xt;θ)
对应的循环计算图:
![](https://img-blog.csdnimg.cn/ca20cba260f34ff0838f3fcca51af19a.png)
[2]展开计算图
将循环结构线性展开,得到的重复结构称为事件链,展开循环图导致深度网络结构中的参数共享
对应的函数表达展开式: h t = f ( h t − 1 , x t ; θ ) = g t ( x t , x t − 1 , . . . , x 1 ) h^t = f(h^{t-1},x^t;θ) = g^t(x^t,x^{t-1},...,x^1) ht=f(ht−1,xt;θ)=gt(xt,xt−1,...,x1), g t g^t gt表示经t步展开的循环。
该状态包含了整个过去序列的信息,但有时我们根据不同的训练准则,只选择性地保留过去序列的部分影响(如 { y t − k , . . . , y t − 1 } → y t ) \{y^{t-k},...,y^{t-1}\}→y^t) {yt−k,...,yt−1}→yt)
对应的展开计算图:
![](https://img-blog.csdnimg.cn/a779a42eecff4cc993ba7e22c2e546ce.png)
[3]循环图与展开图的优点
①循环图:更加简洁
②展开图:能够描述计算流程,可以通过显式的信息流动帮助计算向前传播和向后传播。
同时也展现出RNN的两个特点:模型拥有相同的输入大小;在一个序列中的每个时间步使用相同参数的相同函数W,U,V
5)RNN的几种设计模式
U为输入样本X的权重矩阵,W为上一时间步单元隐藏层的输出或输出层的输出对应的权重矩阵,b为隐藏层的阈值,σ为隐藏层的激活函数,V为输出层的权重矩阵,c为输出层的阈值,输出层的激活函数是softmax函数(获取输出概率)
我们通常希望将RNN的输出解释为一个概率分布,且通常使用真实输出y与预测输出O之间的交叉熵作为损失函数
[1]每个时间步都有输出,且隐藏层之间有循环连接
![](https://img-blog.csdnimg.cn/61ad07329cd442c1a00e359c521c95b4.png)
前向传播:
h
t
=
σ
(
U
x
t
+
W
h
t
−
1
+
b
)
o
t
=
V
h
t
+
c
y
t
=
s
o
f
t
m
a
x
(
o
t
)
L
(
{
x
1
,
x
2
,
.
.
.
,
x
t
}
,
{
y
1
,
y
2
,
.
.
.
,
y
t
}
)
=
∑
t
L
t
=
−
∑
t
l
o
g
p
m
o
d
e
l
(
y
t
∣
x
1
,
x
2
,
.
.
.
,
x
t
)
h^t = σ(Ux^t + Wh^{t-1} + b) \\ o^t = Vh^t + c \\ y^t = softmax(o^t) \\ L(\{x^1,x^2,...,x^t\},\{y^1,y^2,...,y^t\}) = \sum_t L^t =- \sum_t log_{p_{model}}(y^t|{x^1,x^2,...,x^t})
ht=σ(Uxt+Wht−1+b)ot=Vht+cyt=softmax(ot)L({x1,x2,...,xt},{y1,y2,...,yt})=t∑Lt=−t∑logpmodel(yt∣x1,x2,...,xt)
[2]每个时间步都有输出,当前时刻的输出与下一时刻的隐藏单元之间有循环连接
![](https://img-blog.csdnimg.cn/39fa32cc8a1f45a485ef0ff8221e1a91.png)
前向传播:
h
t
=
σ
(
U
x
t
+
W
o
t
−
1
+
b
)
o
t
=
V
h
t
+
c
y
t
=
s
o
f
t
m
a
x
(
o
t
)
L
(
{
x
1
,
x
2
,
.
.
.
,
x
t
}
,
{
y
1
,
y
2
,
.
.
.
,
y
t
}
)
=
∑
t
L
t
=
−
∑
t
l
o
g
p
m
o
d
e
l
(
y
t
∣
x
1
,
x
2
,
.
.
.
,
x
t
)
h^t = σ(Ux^t + Wo^{t-1} + b) \\ o^t = Vh^t + c \\ y^t = softmax(o^t) \\ L(\{x^1,x^2,...,x^t\},\{y^1,y^2,...,y^t\}) = \sum_t L^t =- \sum_t log_{p_{model}}(y^t|{x^1,x^2,...,x^t})
ht=σ(Uxt+Wot−1+b)ot=Vht+cyt=softmax(ot)L({x1,x2,...,xt},{y1,y2,...,yt})=t∑Lt=−t∑logpmodel(yt∣x1,x2,...,xt)
该模式没有从h前向传播的直接连接,o通常缺乏过去的信息
[3]隐藏单元之间存在循环连接,待读取整个序列后产生单个输出的循环网络
![](https://img-blog.csdnimg.cn/06d29e5330134c619a22c0d055627a8c.png)
前向传播:
h
t
=
σ
(
U
x
t
+
W
h
t
−
1
+
b
)
o
t
=
V
h
t
+
c
y
t
=
s
o
f
t
m
a
x
(
o
t
)
L
(
{
x
1
,
x
2
,
.
.
.
,
x
t
}
,
y
t
)
=
L
t
=
−
l
o
g
p
m
o
d
e
l
(
y
t
∣
x
1
,
x
2
,
.
.
.
,
x
t
)
h^t = σ(Ux^t + Wh^{t-1} + b) \\ o^t = Vh^t + c \\ y^t = softmax(o^t) \\ L(\{x^1,x^2,...,x^t\},y^t) = L^t =-log_{p_{model}}(y^t|{x^1,x^2,...,x^t})
ht=σ(Uxt+Wht−1+b)ot=Vht+cyt=softmax(ot)L({x1,x2,...,xt},yt)=Lt=−logpmodel(yt∣x1,x2,...,xt)
[4]Teacher Forcing
Teacher Forcing用以解决上述三种(前后时间步隐藏层/输出层之间循环连接)导致的计算效率低下的问题,由于后一个时间步的输入依赖于前一个时间步的输出,因而只能线性地进行前向和反向计算,无法进行并行计算。
Teacher Forcing则是用每个时间步的真实输出代替该时间步的预测输出作为下一个时间步的输入,这样则可以解决前后时间步计算时的依赖问题,就可以进行并行计算。
![](https://img-blog.csdnimg.cn/c26128b102f74130b40f0f888b1510f2.png)
前向传播:
h
t
=
σ
(
U
x
t
+
W
y
t
−
1
+
b
)
h^t = σ(Ux^t + Wy^{t-1} + b) \\
ht=σ(Uxt+Wyt−1+b)
问题①:该网络缺少隐藏层之间的循环连接,导致它无法通过之前的隐藏单元来获取过去的信息。而每个时间步的真实值又不太能携带关于过去历史的必要信息,除非用户知道每个时间步的真实输出如何描述系统的全貌,并将它作为训练目标的一部分
问题②:该网络在训练时,历史信息通过前一个时间步的真实输出获取,在测试和应用时,历史信息则通过前一个时间步的输出值O来获取。这将造成训练期间该网络看到的输入与测试期间该网络看到的输入有很大的不同。一种缓解方法是同时使用Teacher Forcing和自由运行的输入进行训练
[5]输出序列的每个元素 y t y^t yt同时作用于当前时间步的输入和训练期间的目标(前一个时间步)
可用于图注等多种任务
![](https://img-blog.csdnimg.cn/0682e08107994d82905dfe79870db115.png)
[6]可变长度的序列映射到相同长度的输出序列
前一个时间步的真实输出也作为当前时间步隐藏层的一个输入
![](https://img-blog.csdnimg.cn/2e8e41430acc4dba80ccdc825ac20b7a.png)
6)通过时间反向传播BPTT
通过MLP的BP算法可以计算展开图的参数梯度,不需要特殊化的算法
7)如何确定序列长度
[1]设置序列终结符:如在词汇表输出中增加一个对应序列末尾的特殊符号
[2]在模型中每一个时间步引入一个额外的二项输出,表示在每个时间步决定继续或停止
[3]在模型中增加一个额外的输出r表示序列长度
8)作为有向图模型的循环网络(表示y的联合分布)、基于上下文的RNN序列建模(上下文即输入序列X,表示给定x,y的条件分布)
4.2.双向RNN
1)动机
单向RNN,在一个序列中的每个时间步上考虑了当前时间步以及过去历史对现今的影响。但在许多应用中预测输出可能依赖于整个输入序列,如语音识别:由于协同发音,当前声音作为音素的正确解释不仅取决于前一时间步的发音还取决于未来时间步的发音。
2)概念
双向RNN结合了一个序列从起点开始向着终点方向移动与从终点开始向着起点方向移动的两个方向,这能够使某一时间步的输出同时依赖于过去和未来的表示,而不必指定固定的大小窗口
![](https://img-blog.csdnimg.cn/56ef8d9eb0574df2a350abbe30c8636c.png)
3)扩展
该思想可以扩展到2维输入,如图像,由4个子RNN组成,沿着:上、下、左、右四个方向。如果RNN能够学习到承载长期依赖的信息,那么在2维网格的每个点的输出就能捕获到大多局部信息并依赖于长期输入的表示,对于这种RNN前向传播可以写成卷积的形式,自底向上的计算每一层的输入。相对于一般CNN,应用于图像的RNN通常代价更高。
4.3.基于编码-解码的序列到序列架构
1)动机
设输入序列长度为 n x n^x nx,输出序列长度为 n y n^y ny,先前给出的RNN网络总是有 n x = n y n^x=n^y nx=ny或 n x → n y = 1 n^x → n^y = 1 nx→ny=1的限制
基于编码-解码的序列到序列架构解决输入序列长度与输出序列长度不等的问题,如语音识别、机器翻译、问答等问题的输入输出序列通常不等
2)概念
[1]编码器/读取器(如向量到序列的RNN):输入RNN的输入序列X,输出上下文C(一般为最终隐藏状态的简单函数, n y = 1 n^y = 1 ny=1)
[2]解码器/写入器:输入上下文C,输出固定长度的输出序列Y
![](https://img-blog.csdnimg.cn/4b81d665c0be49c9a4b1e4844be95b1e.png)
3)问题
由于上下文C是一个向量 n C = 1 n^C = 1 nC=1的维度太小,它难以适当地概括一个长序列。一种解决方法是让C成为一个可变长度的序列,而不是一个固定大小的向量,另外还引入了上下文C元素与输出序列元素相关联的注意机制(以后再总结)
- 递归神经网络
介绍:递归神经网络是RNN的一个扩展,它被构造成深的树状结构而非链式结构,在“学习推论”上有很大的用途,在NLP、CV领域有很好的应用
特点/优势:是对于相同长度r的序列,通过树状结构的网络可以将计算复杂度从O®减小到O(log r),这有助于解决长期依赖问题
如何选择树结构:①选择不依赖于数据的树结构,如平衡二叉树;②外部方法为选择树结构提供借鉴:如使用句子语法分析树
![](https://img-blog.csdnimg.cn/1f3adae97766465a93a3939dc00b9b16.png)
4.4.长期依赖
1)长期依赖
RNN在每个时间步上使用同一矩阵W,因此会产生很深的计算图,长期依赖于同一W会导致梯度消失或梯度爆炸
如简单考虑计算图中存在一条反复与矩阵W相乘的路径,将W特征分解为: W = V d i a g ( λ ) V − 1 W=Vdiag(λ)V^{-1} W=Vdiag(λ)V−1,第k步后的矩阵为: W t = ( V d i a g ( λ ) V − 1 ) t = V d i a g ( λ ) t V − 1 W^t = (Vdiag(λ)V^{-1})^t=Vdiag(λ)^tV^{-1} Wt=(Vdiag(λ)V−1)t=Vdiag(λ)tV−1,当特征值λ不在1附近时,大于1会造成梯度爆炸,小于1会造成梯度消失。梯度爆炸会使学习变得不稳定,梯度消失会造成无法优化。从这个观点看, x T W t x^TW^t xTWt最终会丢失x中所有与W主特征向量正交的成分
2)优化长期依赖
机器学习中的一个主题是:设计一个易于优化的模型通常比设计出更强大的优化算法更容易
[1]梯度截断:梯度截断用于解决梯度爆炸的问题,设置梯度阈值,在梯度更新前丢掉饱和部分。具体有对于参数向量逐元素截断,还有对梯度范数进行截断两种方法
[2]引导信息流的正则化:用于解决梯度消失问题,在目标函数中引入正则化项 Ω = ∑ t ( ∣ ∣ ( ▽ h t L ) d h t d h t − 1 ∣ ∣ ∣ ∣ ▽ h t L ∣ ∣ ) 2 Ω=\sum_t(\frac {|| (▽_{h^t}L)\frac {dh^t} {dh^{t-1}} ||} {|| ▽_{h^t}L ||})^2 Ω=∑t(∣∣▽htL∣∣∣∣(▽htL)dht−1dht∣∣)2,要使 ( ▽ h t L ) d h t d h t − 1 (▽_{h^t}L)\frac {dh^t} {dh^{t-1}} (▽htL)dht−1dht与 ▽ h t L ▽_{h^t}L ▽htL一样大。计算该正则化项可能会出现困难,一种做法是在前向传播时将其设为近似的常值
4.5.回声状态网络
1)动机
从输入层到隐藏层以及隐藏层之间的权重映射是RNN网络中最难学的参数,那么一个避免这种困难的方法是手动设定循环隐藏单元,使网络能够很好的捕捉输入历史,且只用学习输出层权重
2)方法
[1]回声状态网络ESN:使用连续的隐藏单元,如简单地固定权重使其具有常值(近1或远大于1如3)的谱半径(特征值谱: J t = d s t d s t − 1 J^t=\frac {d s^t}{ds^{t-1}} Jt=dst−1dst,谱半径:定义为特征值的最大绝对值)信息通过时间向前传播过程中由于非线性单元的饱和而不会产生爆炸
[2]流体状态机:与ESN类似,只是使用二值型的隐藏单元
[3]储层计算:ESN和流体状态机都被称为储层计算,因为隐藏单元形成了可能捕获输入历史不同方面的临时特征池。储层计算网络类似于核机器,它将任意长度的序列映射为一个长度固定的向量,之后可以施加一个线性预测算子(通常是一个线性回归)来解决问题
4.6.多时间尺度的策略
1)多时间尺度策略:是处理长期依赖的一种方法,它设计工作在多个时间尺度(即每次输入不是单个时间步,而是多个)使模型的某些部分在细粒度的时间尺度(单时间步)上操作并能处理细节,而其他部分在粗时间粒度(多个时间步一起作输入)操作并能把遥远过去的信息有效地传递过来
2)几种多时间尺度策略
[1]时间维度的跳跃连接
增加从遥远过去的变量到目前变量的连接(即每次输入不是当前时间步还包括多个过去时间步)以构造较长的延迟循环网络
引入了d延迟的循环网络导数指数减小的速度与 r d \frac r d dr而非r有关,可以减轻循环依赖的梯度爆炸问题
[2]渗漏单元
对某些参数更新时积累一个平滑的梯度: v → u t = α u t − 1 + ( 1 − α ) v t v → u^t = αu^{t-1} + (1-α)v^t v→ut=αut−1+(1−α)vt, u t u^t ut是滑动平均值,这是一个从 u t − 1 u^{t-1} ut−1到 u t u^t ut线性自连接的例子。当α接近1时,滑动平均值能记录很长一段时间的信息,当α接近0时,过去的信息会被丢弃。这种线性自连接的隐藏单元可以模拟滑动平均的行为,被称为渗漏单元
[3]删除连接
删除连接主动删除长度为1的连接并用更长的连接替换他们,这种修改方式迫使单元在长时间尺度上运作。一种具体方法是使循环单元变为渗漏单元,但不同的单元组关联不同的固定时间尺度;另一种是使显式且离散的更新发生在不同的时间,不同的单元组有不同的频率
4.7.门控RNN
门控RNN是解决长期依赖的一种RNN模型,是实际应用中非常有效的序列模型,渗漏单元通过手动设置权重矩阵,而门控RNN则是希望网络能够学会在不同时间步动态地遗忘(丢弃)历史信息和记忆(保留)历史信息以及保留信息的大小,门控则是起到遗忘和记忆的作用
1)长短期记忆模型LSTM
[1]相比于传统的RNN,对于隐藏层而言,对于每个时间步也是使用相同的隐藏层单元进行循环连接,但是每个隐藏单元的内部引入了门控控制,进行了更加复杂的计算。
![](https://img-blog.csdnimg.cn/9e6e24af7e9e43e99be1df960312d4ef.png)
[2]相对于保留记忆的历史时间步的信息 h t − 1 h^{t-1} ht−1被传递外,还增加了状态单元 s t − 1 s^{t-1} st−1进行传递。这时上一步的输出信息实际变成了原始的输入,称为短时记忆;而状态单元实际成为了模型处理后的保留历史信息的具体,称为长时记忆
[3]LSTM隐藏层的具体计算方法如下:
![](https://img-blog.csdnimg.cn/5ff0f708c03749c19ed732fa42f86f8b.png)
①遗忘门:控制状态单元保留的历史信息(长时记忆)的多少(也可看作遗忘了多少,当sigmoid函数输出0时,则表明完全丢弃历史信息)
②输入门:控制本时间步对整个序列上贡献度的多少,有多少值增加到以往的状态单元中(长时记忆)
③输出门:控制本时间步的具体输出(短期记忆)与状态单元的最终值(长期记忆)
对于一个训练好的LSTM而言,门的值绝大多数都非常接近0或1,其余的值很少
2)门控循环单元GRU
[1]GRU同LSTM一样用于解决长期依赖问题,它与LSTM在很多时候的表现相当。它相比于LSTM舍弃了状态单元 s t s^t st,仅使用某一时间步的输入X和上一时间步的输出 h t − 1 h^{t-1} ht−1进行计算。它的计算相比于LSTM的没有其分工明确,但更加得一气呵成,这使得它从形式上来说没有LSTM清晰,但好处就是减少了计算量,可以提高计算效率
![](https://img-blog.csdnimg.cn/4de4fea28ac2406a9e88e396f847d8ba.png)
[2]计算
①重置门: R t = σ ( W r X t + U r h t − 1 + b t ) R_t = σ(W_rX_t+U_rh_{t-1} + b_t) Rt=σ(WrXt+Urht−1+bt),重置门有助于捕获短期记忆
②更新门: Z t = σ ( W z X t + U z h t − 1 + b z ) Z_t = σ(W_zX_t+U_zh_{t-1} + b_z) Zt=σ(WzXt+Uzht−1+bz),更新门有助于捕获长期记忆
③使用重置门计算重置后的数据: h t ′ = t a n h ( W c X t + U ( R t ⊙ h t − 1 ) ) h'_t = tanh(W_cX_t + U(R_t ⊙h_{t-1})) ht′=tanh(WcXt+U(Rt⊙ht−1))
④使用更新门和重置数据计算当前时间步的输出: h t = Z t ⊙ h t − 1 + ( 1 − Z t ) ⊙ h t ′ h_t=Z_t⊙h_{t-1} + (1-Z_t)⊙h'_t ht=Zt⊙ht−1+(1−Zt)⊙ht′
[3]作用
①复位和更新门能独立地 ‘‘忽略’’ 状态向量的一部分
②更新门像条件渗漏累积器一样 可以线性门控任意维度,从而选择将它复制(在sigmoid的一个极端)或完全由新的 ‘‘目标状态’’ 值(朝向渗漏累积器的收敛方向)替换并完全忽略它(在另一个极端)
③复位门控制当前状态中哪些部分用于计算下一个目标状态,在过去状态和未来状态之间引入了附加的非线性效
4.8.LSTM 示例程序
最后给出一个应用LSTM模型基于时间序列的股票预测模型的示例程序
1)数据集下载
使用上海证券从1990年12月20日到2021年3月31日每天的股票数据
2)程序代码
"""
:description:This script used LSTM model to predict time sequence data:shanghai stock exchange
:dataset:1990.12.20-2021.03.31 shanghai stock exchange's data
:debugging-time:2022.11.09
"""
import keras
from pandas import read_csv
from keras import optimizers
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from keras.callbacks import LearningRateScheduler
import keras.backend as K
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
import numpy
import matplotlib.pyplot as plt
import pickle
import math
def create_dataset(_dataset, _look_back, _predict_step):
"""
:description:创建训练数据:将时间序列划分成_look_back一组的输入数据,预测下一_predict_step的值
:param _dataset: 数据集
:param _look_back: 输入时间步
:param _predict_step: 预测时间步
:return:
"""
dataX, dataY = [], []
i = 0
while i < len(_dataset) - _look_back - _predict_step:
x = _dataset[i:(i + _look_back), :]
y = _dataset[i + _look_back:i + _look_back + _predict_step, :]
dataX.append(x)
dataY.append(y)
i = i + _predict_step
return numpy.array(dataX), numpy.array(dataY)
def scheduler(epoch):
"""
:description:设置自适应调整学习率规则:每隔10个epoch,学习率按训练轮数增长指数差值递减
:param epoch: 训练轮数
:return:
"""
# 改进点2:这里可以调训练轮数的参数,可以更换自适应学习率规则
if epoch % 100 == 0 and epoch != 0:
lr = K.get_value(model.optimizer.lr)
K.set_value(model.optimizer.lr, lr * 0.95 ** (epoch / 10))
print("lr changed to {}".format(lr * 0.95 ** (epoch / 10)))
return K.get_value(model.optimizer.lr)
if __name__ == '__main__':
# -----------CA-GrQc.txt.读入数据集-----------
fund_code = '000001'
dataframe = read_csv(fund_code + '.csv', encoding='gb2312')
# 数据预处理:dataframe切片,取全部样本第3列以后的列切片 -> 逆置(以时间升序) -> 取值 -> 转float32型
# https://blog.csdn.net/weixin_46649052/article/details/112462702
dataset = dataframe.iloc[:, 3:].iloc[::-1].values.astype('float32')
# --------2.划分原始的训练集和测试集---------
# 这里是80%做训练,20%做预测
train_size = int(len(dataset) * 0.8)
test_size = len(dataset) - train_size
# 分割原始的训练集和测试集
# 改进点1:划分训练集和测试集的方法是采用前80%的时间和后20%的时间。可以对时间按时间段抽样划分!
train, test = dataset[0:train_size, :], dataset[train_size:len(dataset), :]
# 归一化:将训练集和测试集数据进行归一化,并映射到[0,CA-GrQc.txt]区间
# https://blog.csdn.net/GentleCP/article/details/109333753
scaler = MinMaxScaler(feature_range=(0, 1))
train = scaler.fit_transform(train)
test = scaler.transform(test)
#
with open('scaler_for_' + fund_code + '.pickle', 'wb') as f:
pickle.dump(scaler, f)
# ------3.根据原始的数据构造模型输入的数据----------
look_back = time_step = 20 # 时间步长:可以调参
predict_step = 1 # 预测步数:可以调参
trainX, trainY = create_dataset(train, look_back, predict_step)
testX, testY = create_dataset(test, look_back, predict_step)
_, plt_data = create_dataset(dataset, look_back, predict_step)
# 重新整形模型输入数据的数据结构:[输入,时间步,真实输出]
trainX = numpy.reshape(trainX, (-1, time_step, trainX.shape[2]))
trainY = trainY.reshape(-1, predict_step * trainX.shape[2])
testX = numpy.reshape(testX, (-1, time_step, trainX.shape[2]))
testY = testY.reshape(-1, predict_step * trainX.shape[2])
# --------4.构建LSTM模型--------
# 4.1设置minibatch大小
batch_size = 32
# 4.2使用adam优化器
# https://blog.csdn.net/qq_32931827/article/details/122909624
optimizers.adam_v2.Adam(learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-8)
# 4.3设置自适应学习率
reduce_lr = LearningRateScheduler(scheduler)
# 4.4设置模型结构
model = Sequential()
model.add(LSTM(32, input_shape=(time_step, trainX.shape[2]), return_sequences=True))
# model.add(Dropout(rate=0.2))
model.add(LSTM(18, return_sequences=False))
# model.add(Dropout(rate=0.3))
model.add(Dense(predict_step * trainX.shape[2]))
# 模型编译
model.compile(loss='mse', optimizer='adam')
# 模型数据输入并训练
model.fit(trainX, trainY, epochs=50, batch_size=batch_size, verbose=2, callbacks=[reduce_lr])
# 保存模型
model.save('model_' + fund_code + '.h5')
# ----------5.进行预测并调整数据结构-----------
model = keras.models.load_model('model_000001.h5')
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
trainPredict = trainPredict.reshape(-1, trainX.shape[2])
trainY = trainY.reshape(-1, trainX.shape[2])
testPredict = testPredict.reshape(-1, trainX.shape[2])
testY = testY.reshape(-1, trainX.shape[2])
plt_data = plt_data.reshape(-1, trainX.shape[2])
# -------------6.计算平均平方损失------------
trainScore = math.sqrt(mean_squared_error(trainY[:, :], trainPredict[:, :]))
print('Train Score: %.4f RMSE' % trainScore)
testScore = math.sqrt(mean_squared_error(testY[:], testPredict[:, :]))
print('Test Score: %.4f RMSE' % testScore)
testScore = mean_squared_error(testY[:], testPredict[:, :])
print('Test Score: %.4f MSE' % testScore)
R_2 = r2_score(testY, testPredict)
print('Model R^2 = %.3f' % R_2)
title = fund_code + ' time_step = ' + str(time_step) + ' R^2 = {:.2f}'.format(R_2)
# -------------7.反归一化:恢复原本的数据形式--------------
trainPredict = scaler.inverse_transform(trainPredict)
testPredict = scaler.inverse_transform(testPredict)
trainY = scaler.inverse_transform(trainY)
testY = scaler.inverse_transform(testY)
# --------------8.结果可视化----------------
# 蓝色为原数据,绿色为训练集的预测值,值红色为测试集的预测
# 8.1将训练集预测结果转为折线图
trainPredict = trainPredict.reshape(-1, trainX.shape[2])
testPredict = testPredict.reshape(-1, trainX.shape[2])
plt_data = plt_data.reshape(-1, trainX.shape[2])
trainPredictPlot = numpy.empty_like(plt_data)
trainPredictPlot[:] = numpy.nan
trainPredictPlot[0:len(trainPredict)] = trainPredict[:]
# 8.2将测试集预测结果转为折线图输出的数据结构
testPredictPlot = numpy.empty_like(plt_data)
testPredictPlot[:] = numpy.nan
testPredictPlot[len(trainPredict) + look_back + predict_step:] = testPredict[:]
# 8.3绘制训练集预测值和真实值的折线图
plt.figure(1)
plt.plot(plt_data[:, 0])
plt.plot(trainPredictPlot[:, 0])
plt.plot(testPredictPlot[:, 0])
plt.title(title)
plt.savefig('train_result.png')
# 8.4绘制测试集近100天的预测数据
plt.figure(2)
plt.plot(testY[-100:, 0])
plt.plot(testPredict[-100:, 0])
plt.title('test_set last 100 day')
plt.savefig('test_last_100.png')
plt.show()
3)运行结果
[1]真实输出、训练集上预测和测试集上预测输出
![](https://img-blog.csdnimg.cn/cfe8917819ee4de6af774cce39d6746a.png)
![](https://img-blog.csdnimg.cn/332954f1a4f1423ebd85887e7b715fba.png)