LSTM
写在前面
本文记录笔者在学习LSTM时的记录,相信读者已经在网上看过许多的LSTM博客与视频,与其他博客不同的是,本文会从数学公式的角度,剖析LSTM模型中各个部分的模型输入输出等维度信息,帮助初学者在公式层面理解LSTM模型,并且给出了相关计算的例子代入股票预测场景,并给出参考代码。
模型结构
LSTM的模型结构如下图所示。它由若干个重复的LSTM单元组成,每个单元内部包含遗忘门、输入门和输出门,以及当前时刻的单元状态和输出状态。
模型输入
LSTM模型,通常是处理一个序列(比如文本序列或时间序列)
X
=
(
x
1
,
x
2
,
…
,
x
t
,
…
)
T
X = (x_1,x_2,\dots,x_t,\dots)^T
X=(x1,x2,…,xt,…)T ,每个时间步的输入可以表示为
x
t
x_t
xt,我们使用滑动窗口将序列分为若干个窗口大小为
L
L
L的窗口,步长为
s
t
e
p
step
step,当数据划分到最后,若不足为
L
L
L不能构成窗口时,缺少的数据使用pad填充,通常为0填充或使用最近数据填充。例如,假设我们有
29
29
29个时间步骤的输入,即
x
⃗
=
(
x
0
,
x
1
,
…
,
x
28
)
T
\vec{x} = (x_0,x_1,\dots,x_{28})^T
x=(x0,x1,…,x28)T,且假设窗口大小为
10
10
10,步长
s
t
e
p
step
step也为
10
10
10我们将数据分成三个窗口,即分为
x
1
⃗
=
(
x
0
,
x
1
,
…
,
x
9
)
T
\vec{x_1} = (x_0,x_1,\dots,x_{9})^T
x1=(x0,x1,…,x9)T
x
2
⃗
=
(
x
10
,
x
11
,
…
,
x
19
)
T
\vec{x_2} = (x_{10},x_{11},\dots,x_{19})^T
x2=(x10,x11,…,x19)T
x
3
⃗
=
(
x
20
,
x
21
,
…
,
x
28
,
x
29
)
T
\vec{x_3} = (x_{20},x_{21},\dots,x_{28},x_{29})^T
x3=(x20,x21,…,x28,x29)T
由于
x
29
x_{29}
x29的值不存在,我们将其值设为
0
0
0或者
x
28
x_{28}
x28的值,即
x
3
⃗
=
(
x
20
,
x
21
,
…
,
x
28
,
0
)
T
\vec{x_3} = (x_{20},x_{21},\dots,x_{28}, 0)^T
x3=(x20,x21,…,x28,0)T或者
x
3
⃗
=
(
x
20
,
x
21
,
…
,
x
28
,
x
28
)
T
\vec{x_3} = (x_{20},x_{21},\dots,x_{28},x_{28})^T
x3=(x20,x21,…,x28,x28)T。
当步长
s
t
e
p
step
step为
1
1
1时,通常不会出现上面的情况,这也是我们使用的最多的一种滑动窗口划分方案。
例如,对于一个时序序列
X
=
{
x
1
,
x
2
,
…
,
x
10
}
X = \{x_1, x_2, \ldots, x_{10}\}
X={x1,x2,…,x10},窗口大小
L
=
3
L = 3
L=3,滑动步长
s
t
e
p
=
1
step = 1
step=1,滑动窗口划分结果为:
x
1
⃗
=
(
x
1
,
x
2
,
x
3
)
x
2
⃗
=
(
x
2
,
x
3
,
x
4
)
x
3
⃗
=
(
x
3
,
x
4
,
x
5
)
x
4
⃗
=
(
x
4
,
x
5
,
x
6
)
x
5
⃗
=
(
x
5
,
x
6
,
x
7
)
x
6
⃗
=
(
x
6
,
x
7
,
x
8
)
x
7
⃗
=
(
x
7
,
x
8
,
x
9
)
x
8
⃗
=
(
x
8
,
x
9
,
x
10
)
\begin{aligned} \vec{x_1} & = (x_1, x_2, x_3) \\ \vec{x_2} & = (x_2, x_3, x_4) \\ \vec{x_3} & = (x_3, x_4, x_5) \\ \vec{x_4} & = (x_4, x_5, x_6) \\ \vec{x_5} & = (x_5, x_6, x_7) \\ \vec{x_6} & = (x_6, x_7, x_8) \\ \vec{x_7} & = (x_7, x_8, x_9) \\ \vec{x_8} & = (x_8, x_9, x_{10}) \end{aligned}
x1x2x3x4x5x6x7x8=(x1,x2,x3)=(x2,x3,x4)=(x3,x4,x5)=(x4,x5,x6)=(x5,x6,x7)=(x6,x7,x8)=(x7,x8,x9)=(x8,x9,x10)
LSTM 单元的输入包含当前时刻的输入 x t ⃗ \vec{x_t} xt、上一时刻的输出状态 h t − 1 h_{t-1} ht−1以及上一时刻的单元状态 c t − 1 c_{t-1} ct−1。在进行运算第一层LSTM单元时,我们会手动初始化 h 0 h_0 h0、 c 0 c_0 c0,而在后面的LSTM的单元中 h t − 1 h_{t-1} ht−1和 c t − 1 c_{t-1} ct−1,都可以由上一次的LSTM单元获得。 x t ⃗ \vec{x_t} xt、 h t − 1 h_{t-1} ht−1、 c t − 1 c_{t-1} ct−1分别代表当前时刻的输入信息、上一时刻的输出信息以及上一时刻的记忆信息。其中, x t ⃗ ∈ R m × 1 \vec{x_t} \in \mathbb{R}^{m \times 1} xt∈Rm×1, m m m是输入序列处理后的窗口大小(长度), h t − 1 h_{t-1} ht−1上一时刻的输出状态,形状为 h t − 1 ∈ R d × 1 h_{t-1} \in \mathbb{R}^{d \times 1} ht−1∈Rd×1, d d d是LSTM单元的隐藏状态大小, c t − 1 c_{t-1} ct−1是上一时刻的单元状态,形状为 c t − 1 ∈ R d × 1 c_{t-1} \in \mathbb{R}^{d \times 1} ct−1∈Rd×1,与 h t − 1 h_{t-1} ht−1具有相同的形状。
我们通常会把 h t − 1 h_{t-1} ht−1和 x t ⃗ \vec{x_t} xt拼在一起形成更长的向量 y t ⃗ \vec{y_t} yt,我们通常竖着拼,即 y t ⃗ ∈ R ( d + m ) × 1 \vec{y_t} \in \mathbb{R}^{(d + m) \times 1} yt∈R(d+m)×1 ,如公式下所示,然后 y t ⃗ \vec{y_t} yt会传入各个门。当采用多批次时, y t ⃗ ∈ R ( d + m ) × n \vec{y_t} \in \mathbb{R}^{(d + m) \times n} yt∈R(d+m)×n。
y t ⃗ = [ h t − 1 ; x t ⃗ ] = [ h t − 1 x t ⃗ ] \vec{y_t} = [h_{t-1}; \vec{x_t}] = \left[{\begin{matrix} h_{t-1} \\ \vec{x_t} \end{matrix}}\right] yt=[ht−1;xt]=[ht−1xt]
遗忘门
遗忘门的输入为我们在模型输入中处理得到的
X
t
′
X_t'
Xt′。我们将
X
t
′
X_t'
Xt′与遗忘门中的权重矩阵
W
f
W_f
Wf相乘再加上置偏值
b
f
b_f
bf,得到结果
M
f
M_f
Mf。然后对
M
f
M_f
Mf取Sigmoid,得到遗忘门的输出
f
t
f_t
ft,其形状与单元状态
c
t
c_t
ct相同,即
f
t
∈
R
d
×
1
f_t \in \mathbb{R}^{d \times 1}
ft∈Rd×1,表示遗忘的程度。具体的计算公式如(\ref{LSTME02})所示。
M
f
=
W
f
y
t
⃗
+
b
f
M_f = W_f\vec{y_t} + b_f
Mf=Wfyt+bf
f
t
=
σ
(
M
f
)
=
1
1
+
e
−
(
W
f
y
t
⃗
+
b
f
)
f_t = \sigma(M_f) = \frac{1}{1 + e^{-(W_f\vec{y_t} + b_f)}}
ft=σ(Mf)=1+e−(Wfyt+bf)1
其中,
y
t
⃗
∈
R
(
d
+
m
)
×
1
\vec{y_t} \in \mathbb{R}^{(d + m) \times 1}
yt∈R(d+m)×1,
W
f
∈
R
d
×
(
d
+
m
)
W_f \in \mathbb{R}^{d \times (d + m)}
Wf∈Rd×(d+m),
b
f
∈
R
d
×
1
b_f \in \mathbb{R}^{d \times 1}
bf∈Rd×1,
f
t
∈
R
d
×
1
f_t \in \mathbb{R}^{d \times 1}
ft∈Rd×1。
在LSTM的许多门中,都使用Sigmoid函数,Sigmoid函数的绝大部分的值的取值范围为 ( 0 , 1 ) (0, 1) (0,1),这可以很有效的表示在Sigmoid函数的输入中哪些数据需要记忆,哪些数据需要遗忘的过程。当Sigmoid函数只越接近 0 0 0时表示遗忘,当接近 1 1 1时表示需要记忆。
输入门
输入门的输入为我们在模型输入中处理得到的
y
t
⃗
\vec{y_t}
yt,且
y
t
⃗
∈
R
(
d
+
m
)
×
1
\vec{y_t} \in \mathbb{R}^{(d + m) \times 1 }
yt∈R(d+m)×1。我们将
y
t
⃗
\vec{y_t}
yt与输入门中的权重矩阵
W
i
W_i
Wi相乘再加上置偏值
b
i
b_i
bi,得到结果
M
i
M_i
Mi,然后对
M
i
M_i
Mi取Sigmoid,得到输入门的输出
i
t
i_t
it,表示输入的重要程度。具体的计算公式如下所示。
M
i
=
W
i
y
t
⃗
+
b
i
M_i = W_i\vec{y_t} + b_i
Mi=Wiyt+bi
i
t
=
σ
(
M
i
)
=
1
1
+
e
−
(
W
i
y
t
⃗
+
b
i
)
i_t = \sigma(M_i) = \frac{1}{1 + e^{-(W_i\vec{y_t} + b_i)}}
it=σ(Mi)=1+e−(Wiyt+bi)1
其中,
y
t
⃗
∈
R
(
d
+
m
)
×
n
\vec{y_t} \in \mathbb{R}^{(d + m) \times n}
yt∈R(d+m)×n,
W
i
∈
R
d
×
(
d
+
m
)
W_i \in \mathbb{R}^{d \times (d + m)}
Wi∈Rd×(d+m),
b
i
∈
R
d
×
1
b_i \in \mathbb{R}^{d \times 1}
bi∈Rd×1,
i
t
∈
R
d
×
1
i_t \in \mathbb{R}^{d \times 1}
it∈Rd×1。
输出门
输出门的输入为我们在模型输入中处理得到的 y t ⃗ \vec{y_t} yt,且 y t ⃗ ∈ R ( d + m ) × 1 \vec{y_t} \in \mathbb{R}^{(d + m) \times 1 } yt∈R(d+m)×1。我们将 y t ⃗ \vec{y_t} yt与输出门中的权重矩阵 W o W_o Wo相乘再加上置偏值 b o b_o bo,得到结果 M o M_o Mo,然后对 M o M_o Mo取Sigmoid,得到输出门的输出 o t o_t ot,具体的计算公式如下所示。
M
o
=
W
o
y
t
⃗
+
b
o
M_o = W_o\vec{y_t} + b_o
Mo=Woyt+bo
o
t
=
σ
(
M
o
)
=
1
1
+
e
−
(
W
o
y
t
⃗
+
b
o
)
o_t = \sigma(M_o) = \frac{1}{1 + e^{-(W_o\vec{y_t} + b_o)}}
ot=σ(Mo)=1+e−(Woyt+bo)1
其中,
y
t
⃗
∈
R
(
d
+
m
)
×
1
\vec{y_t} \in \mathbb{R}^{(d + m) \times 1}
yt∈R(d+m)×1,
W
o
∈
R
d
×
(
d
+
m
)
W_o \in \mathbb{R}^{d \times (d + m)}
Wo∈Rd×(d+m),
b
o
∈
R
d
×
1
b_o \in \mathbb{R}^{d \times 1}
bo∈Rd×1,
o
t
∈
R
d
×
1
o_t \in \mathbb{R}^{d \times 1}
ot∈Rd×1。
当前输入单元状态
在计算
c
t
c_t
ct之前,我们需要引入当前输入单元状态,并计算
c
t
~
\tilde{c_t}
ct~的值。
c
t
~
\tilde{c_t}
ct~是当前输入的单元状态,表示当前输入要保留多少内容到记忆中。我们将
y
t
⃗
\vec{y_t}
yt与当前时刻状态单元的权重矩阵
W
c
W_c
Wc相乘再加上置偏值
b
c
b_c
bc,得到结果
M
c
M_c
Mc,然后对
M
c
M_c
Mc取tanh,得到的输出
c
t
~
\tilde{c_t}
ct~。
c
t
~
\tilde{c_t}
ct~的计算如公式下所示。
M
c
=
W
c
y
t
⃗
+
b
c
M_c = W_c\vec{y_t} + b_c
Mc=Wcyt+bc
c
t
~
=
tanh
(
M
c
)
=
e
M
c
−
e
−
M
c
e
M
c
+
e
−
M
c
=
(
e
W
c
y
t
⃗
+
b
c
)
−
e
−
(
W
c
y
t
⃗
+
b
c
)
(
e
W
c
y
t
⃗
+
b
c
)
+
e
−
(
W
c
y
t
⃗
+
b
c
)
\tilde{c_t} = \text{tanh}(M_c) = \frac{e^{M_c}-e^{-M_c}}{e^{M_c}+e^{-M_c}} = \frac{(e^{W_c\vec{y_t} + b_c)}-e^{-(W_c\vec{y_t} + b_c)}}{(e^{W_c\vec{y_t} + b_c)}+e^{-(W_c\vec{y_t} + b_c)}}
ct~=tanh(Mc)=eMc+e−MceMc−e−Mc=(eWcyt+bc)+e−(Wcyt+bc)(eWcyt+bc)−e−(Wcyt+bc)
其中,
y
t
⃗
∈
R
(
d
+
m
)
×
1
\vec{y_t} \in \mathbb{R}^{(d + m) \times 1}
yt∈R(d+m)×1,
W
c
∈
R
d
×
(
d
+
m
)
W_c \in \mathbb{R}^{d \times (d + m)}
Wc∈Rd×(d+m),
b
c
∈
R
d
×
1
b_c \in \mathbb{R}^{d \times 1}
bc∈Rd×1,
c
t
~
∈
R
d
×
1
\tilde{c_t} \in \mathbb{R}^{d \times 1}
ct~∈Rd×1。
当前输入单元状态中,使用了tanh函数,tanh函数的取值范围为 ( − 1 , 1 ) (-1,1) (−1,1),当函数的值接近 − 1 -1 −1时代表着当前输入信息要被修正,当但函数值接近 1 1 1时,代码当前输入信息要被加强。
当前时刻单元状态
接下来我们进行当前时刻单元状态
c
t
c_t
ct的计算。我们使用遗忘门和输入门得到的结果
f
t
f_t
ft、
i
t
i_t
it和上一时刻单元状态
c
t
−
1
c_{t-1}
ct−1来计算当前时刻单元状态
c
t
c_t
ct。我们分别将
f
t
f_t
ft、
c
t
−
1
c_{t-1}
ct−1按元素相乘,
i
t
i_t
it和
c
t
~
\tilde{c_t}
ct~按元素相乘,然后再将两者相加得到我们的当前时刻单元状态
c
t
c_t
ct。具体计算如公式下所示。
c
t
=
f
t
∘
c
t
−
1
+
i
t
∘
c
t
~
c_t = f_t \circ c_{t-1} + i_t \circ \tilde{c_t}
ct=ft∘ct−1+it∘ct~
其中,
f
t
∈
R
d
×
1
f_t \in \mathbb{R}^{d \times 1}
ft∈Rd×1时遗忘门输出,
i
t
∈
R
d
×
1
i_t \in \mathbb{R}^{d \times 1}
it∈Rd×1是输入门输出,
c
t
~
∈
R
d
×
1
\tilde{c_{t}} \in \mathbb{R}^{d \times 1}
ct~∈Rd×1是当前输入状态单元,
c
t
−
1
∈
R
d
×
1
c_{t-1} \in \mathbb{R}^{d \times 1}
ct−1∈Rd×1 是上一时刻状态单元,
∘
\circ
∘表示 按元素乘。
模型输出
模型的输出是
h
t
h_t
ht和当前时刻的单元状态
c
t
c_t
ct,而
h
t
h_t
ht由当前时刻的单元状态
c
t
c_t
ct和输出门的输出
o
t
o_t
ot确定。我们将当前时刻的单元状态
c
t
c_t
ct取 tanh得到
d
t
d_t
dt,然后将
d
t
d_t
dt 与
o
t
o_t
ot按元素相乘得到最后的
h
t
h_t
ht,计算公式如下所示。通常,
h
t
h_t
ht会进一步传递给模型的上层或者作为最终的预测结果。
d
t
=
tanh
(
c
t
)
=
e
c
t
−
e
−
c
t
e
c
t
+
e
−
c
t
d_t = \text{tanh}(c_t) = \frac{e^{c_t}-e^{-c_t}}{e^{c_t}+e^{-c_t}}
dt=tanh(ct)=ect+e−ctect−e−ct
h
t
=
o
t
∘
d
t
h_t = o_t \circ d_t
ht=ot∘dt
其中
h
t
∈
R
d
×
1
h_t \in \mathbb{R}^{d \times 1}
ht∈Rd×1 为当前层隐藏状态,
o
t
∈
R
d
×
1
o_t \in \mathbb{R}^{d \times 1}
ot∈Rd×1为输出门的输出,
c
t
∈
R
d
×
1
c_t \in \mathbb{R}^{d \times 1}
ct∈Rd×1为当前时刻状态单元。
日期 | 开盘价 | 收盘价 | 最高价 | 最低价 |
---|---|---|---|---|
4月23日 | 3038.6118 | 3021.9775 | 3044.9438 | 3016.5168 |
4月24日 | 3029.4028 | 3044.8223 | 3045.6399 | 3019.1238 |
4月25日 | 3037.9272 | 3052.8999 | 3060.2634 | 3034.6499 |
4月26日 | 3054.9793 | 3088.6357 | 3092.4300 | 3054.9793 |
Table: SH000001
简单的LSTM例子
接下来我们根据上面的模型结构中的计算方法来简单计算一个LSTM的例子。
我们以取中国A股上证指数(SH000001)2024年4月23日-25日共3个交易日的数据为例,取开盘价、收盘价、最高价、最低价作为特征,具体数据如表格所示。使用LSTM模型计算预测2024年4月26日的开盘价、收盘价、最高价、最低价,损失函数使用MSE。我们取隐藏层状态 d d d的大小为 4 4 4,然后进行计算,预测下一天的数据。
我们把表格数据处理成 x t x_t xt的形式,也就是把每天的 4 4 4个特征,转换成 m × 1 m \times 1 m×1即 ( 4 × 1 ) (4 \times 1) (4×1)的向量,然后我们得到以 X X X的结果。
X = ( x 1 ⃗ , x 2 ⃗ , x 3 ⃗ ) = [ 3038.6118 3029.4028 3037.9272 3021.9775 3044.8223 3052.8999 3044.9438 3045.6399 3060.2634 3016.5168 3019.1238 3034.6499 ] X = (\vec{x_1}, \vec{x_2}, \vec{x_3}) = \begin{bmatrix} 3038.6118 & 3029.4028 & 3037.9272 \\ 3021.9775 & 3044.8223 & 3052.8999 \\ 3044.9438 & 3045.6399 & 3060.2634 \\ 3016.5168 & 3019.1238 & 3034.6499 \\ \end{bmatrix} X=(x1,x2,x3)= 3038.61183021.97753044.94383016.51683029.40283044.82233045.63993019.12383037.92723052.89993060.26343034.6499
由于隐藏层大小为 d = 4 d = 4 d=4,所以 h 0 h_0 h0、 c 0 c_0 c0的维度都是 4 × 1 4 \times 1 4×1,我们将 h 0 h_0 h0和 c 0 c_0 c0进行初始化为 0 ⃗ \vec{0} 0向量,即
h 0 = [ 0 , 0 , 0 , 0 ] T , c 0 = [ 0 , 0 , 0 , 0 ] T h_0 = [0, 0, 0, 0]^T, c_0 = [0, 0, 0, 0]^T h0=[0,0,0,0]T,c0=[0,0,0,0]T
随后我们初始化
W
f
W_f
Wf、
W
i
W_i
Wi、
W
c
W_c
Wc、
W
o
W_o
Wo(维度为
d
×
(
d
+
m
)
d \times (d + m)
d×(d+m),即
4
×
8
4 \times 8
4×8以及
b
f
b_f
bf、
b
i
b_i
bi、
b
c
b_c
bc、
b
o
b_o
bo,
W
W
W的元素值
∈
[
−
0.0001
,
0.0001
]
\in [-0.0001, 0.0001]
∈[−0.0001,0.0001],W是随机矩阵,如下所示。
W
f
=
[
−
0.0005
−
0.0010
−
0.0010
−
0.0004
−
0.0008
−
0.0006
−
0.0006
−
0.0007
0.0004
−
0.0009
−
0.0006
0.0009
0.0001
0.0004
0.0009
0.0003
−
0.0005
−
0.0006
0.0007
−
0.0003
−
0.0003
0.0001
0.0004
0.0006
−
0.0007
−
0.0008
0.0007
−
0.0006
0.0005
−
0.0003
−
0.0010
−
0.0002
]
W_f = \begin{bmatrix} -0.0005 & -0.0010 & -0.0010 & -0.0004 & -0.0008 & -0.0006 & -0.0006 & -0.0007 \\ 0.0004 & -0.0009 & -0.0006 & 0.0009 & 0.0001 & 0.0004 & 0.0009 & 0.0003 \\ -0.0005 & -0.0006 & 0.0007 & -0.0003 & -0.0003 & 0.0001 & 0.0004 & 0.0006 \\ -0.0007 & -0.0008 & 0.0007 & -0.0006 & 0.0005 & -0.0003 & -0.0010 & -0.0002 \\ \end{bmatrix}
Wf=
−0.00050.0004−0.0005−0.0007−0.0010−0.0009−0.0006−0.0008−0.0010−0.00060.00070.0007−0.00040.0009−0.0003−0.0006−0.00080.0001−0.00030.0005−0.00060.00040.0001−0.0003−0.00060.00090.0004−0.0010−0.00070.00030.0006−0.0002
W i = [ − 0.0006 − 0.0001 − 0.0003 0.0002 0.0008 0.0000 − 0.0003 − 0.0003 0.0007 − 0.0002 0.0006 0.0001 − 0.0009 − 0.0005 − 0.0007 − 0.0005 − 0.0008 0.0004 0.0007 − 0.0008 − 0.0008 0.0010 − 0.0006 − 0.0009 − 0.0005 0.0010 − 0.0006 − 0.0002 − 0.0002 0.0006 − 0.0007 0.0002 ] W_i = \begin{bmatrix} -0.0006 & -0.0001 & -0.0003 & 0.0002 & 0.0008 & 0.0000 & -0.0003 & -0.0003 \\ 0.0007 & -0.0002 & 0.0006 & 0.0001 & -0.0009 & -0.0005 & -0.0007 & -0.0005 \\ -0.0008 & 0.0004 & 0.0007 & -0.0008 & -0.0008 & 0.0010 & -0.0006 & -0.0009 \\ -0.0005 & 0.0010 & -0.0006 & -0.0002 & -0.0002 & 0.0006 & -0.0007 & 0.0002 \\ \end{bmatrix} Wi= −0.00060.0007−0.0008−0.0005−0.0001−0.00020.00040.0010−0.00030.00060.0007−0.00060.00020.0001−0.0008−0.00020.0008−0.0009−0.0008−0.00020.0000−0.00050.00100.0006−0.0003−0.0007−0.0006−0.0007−0.0003−0.0005−0.00090.0002
W c = [ 0.0001 0.0004 0.0000 − 0.0006 − 0.0006 − 0.0002 0.0003 0.0005 − 0.0002 − 0.0006 0.0005 − 0.0009 0.0002 − 0.0008 − 0.0003 − 0.0009 0.0002 0.0004 0.0000 0.0009 0.0003 0.0003 0.0006 − 0.0008 − 0.0007 − 0.0008 0.0009 − 0.0007 0.0002 − 0.0010 − 0.0006 − 0.0003 ] W_c = \begin{bmatrix} 0.0001 & 0.0004 & 0.0000 & -0.0006 & -0.0006 & -0.0002 & 0.0003 & 0.0005 \\ -0.0002 & -0.0006 & 0.0005 & -0.0009 & 0.0002 & -0.0008 & -0.0003 & -0.0009 \\ 0.0002 & 0.0004 & 0.0000 & 0.0009 & 0.0003 & 0.0003 & 0.0006 & -0.0008 \\ -0.0007 & -0.0008 & 0.0009 & -0.0007 & 0.0002 & -0.0010 & -0.0006 & -0.0003 \\ \end{bmatrix} Wc= 0.0001−0.00020.0002−0.00070.0004−0.00060.0004−0.00080.00000.00050.00000.0009−0.0006−0.00090.0009−0.0007−0.00060.00020.00030.0002−0.0002−0.00080.0003−0.00100.0003−0.00030.0006−0.00060.0005−0.0009−0.0008−0.0003
W o = [ − 0.0009 − 0.0005 0.0000 0.0001 − 0.0001 − 0.0004 − 0.0005 − 0.0007 0.0009 − 0.0005 0.0008 − 0.0009 0.0001 0.0004 − 0.0002 0.0004 − 0.0005 − 0.0004 0.0007 − 0.0008 − 0.0006 0.0008 0.0006 0.0010 − 0.0002 0.0008 0.0008 − 0.0002 0.0008 − 0.0004 0.0008 − 0.0002 ] W_o = \begin{bmatrix} -0.0009 & -0.0005 & 0.0000 & 0.0001 & -0.0001 & -0.0004 & -0.0005 & -0.0007 \\ 0.0009 & -0.0005 & 0.0008 & -0.0009 & 0.0001 & 0.0004 & -0.0002 & 0.0004 \\ -0.0005 & -0.0004 & 0.0007 & -0.0008 & -0.0006 & 0.0008 & 0.0006 & 0.0010 \\ -0.0002 & 0.0008 & 0.0008 & -0.0002 & 0.0008 & -0.0004 & 0.0008 & -0.0002 \\ \end{bmatrix} Wo= −0.00090.0009−0.0005−0.0002−0.0005−0.0005−0.00040.00080.00000.00080.00070.00080.0001−0.0009−0.0008−0.0002−0.00010.0001−0.00060.0008−0.00040.00040.0008−0.0004−0.0005−0.00020.00060.0008−0.00070.00040.0010−0.0002
b b b全部初始化为单位列向量即
b f = b i = b c = b o = [ 1 1 1 1 ] T b_f = b_i = b_c = b_o = \begin{bmatrix} 1 \\ 1 \\ 1 \\ 1 \end{bmatrix}^T bf=bi=bc=bo= 1111 T
然后我们将
h
0
h_0
h0与
x
1
x_1
x1拼在一起作为
y
1
⃗
\vec{y_1}
y1,即
y
1
⃗
=
[
h
0
;
x
1
⃗
]
=
[
0
0
0
0
3038.6118
3021.9775
3044.9438
3016.5168
]
T
\vec{y_1} = [h_0; \vec{x_1}] = \begin{bmatrix} 0 & 0 & 0 & 0 & 3038.6118 & 3021.9775 & 3044.9438 & 3016.5168 \end{bmatrix}^T
y1=[h0;x1]=[00003038.61183021.97753044.94383016.5168]T
我们依次计算遗忘门 f 1 f_1 f1,输入门 i 1 i_1 i1,输出门 o 1 o_1 o1,即
f 1 = σ ( W f y 1 ⃗ + b f ) = [ 0.0008 0.9985 0.9713 0.1164 ] , i 1 = σ ( W i y 1 ⃗ + b i ) = [ 0.8514 0.0010 0.0568 0.6491 ] , o 1 = σ ( W o y 1 ⃗ + b o ) = [ 0.0198 0.9577 0.9981 0.9842 ] f_1 = \sigma(W_f\vec{y_1} + b_f) = \begin{bmatrix} 0.0008 \\ 0.9985 \\ 0.9713 \\ 0.1164 \end{bmatrix}, i_1 = \sigma(W_i\vec{y_1} + b_i) = \begin{bmatrix} 0.8514 \\ 0.0010 \\ 0.0568 \\ 0.6491 \end{bmatrix}, o_1 = \sigma(W_o\vec{y_1} + b_o) = \begin{bmatrix} 0.0198 \\ 0.9577 \\ 0.9981 \\ 0.9842 \end{bmatrix} f1=σ(Wfy1+bf)= 0.00080.99850.97130.1164 ,i1=σ(Wiy1+bi)= 0.85140.00100.05680.6491 ,o1=σ(Woy1+bo)= 0.01980.95770.99810.9842
随后我们进行计算当前输入单元状态 c 1 ~ \tilde{c_1} c1~,即
c 1 ~ = tanh ( W c y 1 ⃗ + b c ) = [ 0.7923 − 0.9997 0.9805 − 0.9994 ] T \tilde{c_1} = \text{tanh}(W_c\vec{y_1} + b_c) = \begin{bmatrix} 0.7923 & -0.9997 & 0.9805 & -0.9994 \end{bmatrix}^T c1~=tanh(Wcy1+bc)=[0.7923−0.99970.9805−0.9994]T
接着我们计算当前时刻单元状态 c 1 c_1 c1,即
c 1 = f 1 ∘ c 0 + i 1 ∘ c 1 ~ = [ 0.0008 0.9985 0.9713 0.1164 ] ∘ [ 0 0 0 0 ] + [ 0.8514 0.0010 0.0568 0.6491 ] ∘ [ 0.7923 − 0.9997 0.9805 − 0.9994 ] = [ 0.6746 − 0.001 0.0557 − 0.6488 ] c_1 = f_1 \circ c_{0} + i_1 \circ \tilde{c_1} = \begin{bmatrix} 0.0008 \\ 0.9985 \\ 0.9713 \\ 0.1164 \end{bmatrix} \circ \begin{bmatrix} 0 \\ 0 \\ 0 \\ 0 \end{bmatrix} + \begin{bmatrix} 0.8514 \\ 0.0010 \\ 0.0568 \\ 0.6491 \end{bmatrix} \circ \begin{bmatrix} 0.7923 \\ -0.9997 \\ 0.9805 \\ -0.9994 \end{bmatrix} = \begin{bmatrix} 0.6746 \\ -0.001 \\ 0.0557 \\ -0.6488 \end{bmatrix} c1=f1∘c0+i1∘c1~= 0.00080.99850.97130.1164 ∘ 0000 + 0.85140.00100.05680.6491 ∘ 0.7923−0.99970.9805−0.9994 = 0.6746−0.0010.0557−0.6488
最后我们计算当前层隐藏层输出 h 1 h_1 h1,即
h 1 = o 1 ∘ d 1 = o 1 ∘ tanh ( c 1 ) = [ 0.0116 − 0.001 0.0556 − 0.5618 ] T h_1 = o_1 \circ d_1 = o_1 \circ \text{tanh}(c_1) = \begin{bmatrix} 0.0116 & -0.001 & 0.0556 & -0.5618 \end{bmatrix}^T h1=o1∘d1=o1∘tanh(c1)=[0.0116−0.0010.0556−0.5618]T
这样我们就完成了一次LSTM单元的正向传播计算,我们得到了 h 1 h_1 h1和 c 1 c_1 c1,我们将其传入下一层。
同理我们可以进行接下来 第
2
2
2个交易日 的计算。
我们将
h
1
h_1
h1与
x
2
⃗
\vec{x_2}
x2拼在一起作为
y
2
⃗
\vec{y_2}
y2,即
y 2 ⃗ = [ h 1 ; x 2 ⃗ ] = [ 0.0116 − 0.001 0.0556 − 0.5618 3029.4028 3044.8223 3045.6399 3019.1238 ] T \vec{y_2} = [h_1; \vec{x_2}] = \begin{bmatrix} 0.0116 & -0.001 & 0.0556 & -0.5618 & 3029.4028 & 3044.8223 & 3045.6399 & 3019.1238 \end{bmatrix}^T y2=[h1;x2]=[0.0116−0.0010.0556−0.56183029.40283044.82233045.63993019.1238]T
我们依次计算遗忘门 f 2 f_2 f2,输入门 i 2 i_2 i2,输出门 o 2 o_2 o2,即
f 2 = σ ( W f y 2 ⃗ + b f ) = [ 0.0008 0.9985 0.9715 0.1151 ] , i 2 = σ ( W i y 2 ⃗ + b i ) = [ 0.8503 0.0010 0.0583 0.6527 ] , o 2 = σ ( W o y 2 ⃗ + b o ) = [ 0.0196 0.9581 0.9981 . 9839 ] f_2 = \sigma(W_f\vec{y_2} + b_f) = \begin{bmatrix} 0.0008 \\ 0.9985 \\ 0.9715 \\ 0.1151 \end{bmatrix}, i_2 = \sigma(W_i\vec{y_2} + b_i) = \begin{bmatrix} 0.8503 \\ 0.0010 \\ 0.0583 \\ 0.6527 \end{bmatrix}, o_2 = \sigma(W_o\vec{y_2} + b_o) = \begin{bmatrix} 0.0196 \\ 0.9581 \\ 0.9981 \\.9839 \end{bmatrix} f2=σ(Wfy2+bf)= 0.00080.99850.97150.1151 ,i2=σ(Wiy2+bi)= 0.85030.00100.05830.6527 ,o2=σ(Woy2+bo)= 0.01960.95810.9981.9839
随后我们进行计算当前输入单元状态 c 2 ~ \tilde{c_2} c2~,即
c 2 ~ = tanh ( W c y 2 ⃗ + b c ) = [ 0.7935 − 0.9998 0.9806 − 0.9994 ] T \tilde{c_2} = \text{tanh}(W_c\vec{y_2} + b_c) = \begin{bmatrix} 0.7935 & -0.9998 & 0.9806 & -0.9994 \end{bmatrix}^T c2~=tanh(Wcy2+bc)=[0.7935−0.99980.9806−0.9994]T
接着我们计算当前时刻单元状态 c 2 c_2 c2,即
c 2 = f 2 ∘ c 1 + i 2 ∘ c 2 ~ = [ 0.6747 − 0.0010 0.0571 − 0.6524 ] T c_2 = f_2 \circ c_{1} + i_2 \circ \tilde{c_2} = \begin{bmatrix} 0.6747 & -0.0010 & 0.0571 & -0.6524 \end{bmatrix}^T c2=f2∘c1+i2∘c2~=[0.6747−0.00100.0571−0.6524]T
最后我们计算当前层隐藏层输出 h 2 h_2 h2,即
h 2 = o 2 ∘ d 2 = o 2 ∘ tanh ( c 2 ) = [ 0.0115 − 0.0010 0.0570 − 0.5640 ] T h_2 = o_2 \circ d_2 = o_2 \circ \text{tanh}(c_2) = \begin{bmatrix} 0.0115 & -0.0010 & 0.0570 & -0.5640 \end{bmatrix}^T h2=o2∘d2=o2∘tanh(c2)=[0.0115−0.00100.0570−0.5640]T
同理我们可以进行接下来 第
3
3
3个交易日 的计算。
我们将
h
2
h_2
h2与
x
3
⃗
\vec{x_3}
x3拼在一起作为
y
3
⃗
\vec{y_3}
y3,即
y 3 ⃗ = [ h 2 ; x 3 ⃗ ] = [ 0.0115 − 0.0010 0.0570 − 0.5640 3037.9272 3052.8999 3060.2634 3034.6499 ] T \vec{y_3} = [h_2; \vec{x_3}] = \begin{bmatrix} 0.0115 & -0.0010 & 0.0570 & -0.5640 & 3037.9272 & 3052.8999 & 3060.2634 & 3034.6499 \end{bmatrix}^T y3=[h2;x3]=[0.0115−0.00100.0570−0.56403037.92723052.89993060.26343034.6499]T
我们依次计算遗忘门 f 3 f_3 f3,输入门 i 3 i_3 i3,输出门 o 3 o_3 o3。
f 3 = σ ( W f y 3 ⃗ + b f ) = [ 0.0008 0.9985 0.9719 0.1135 ] , i 3 = σ ( W i y 3 ⃗ + b i ) = [ 0.8501 0.0010 0.0572 0.6518 ] , o 3 = σ ( W o y 3 ⃗ + b o ) = [ 0.0192 0.9584 0.9982 0.9841 ] f_3 = \sigma(W_f\vec{y_3} + b_f) = \begin{bmatrix} 0.0008 \\ 0.9985 \\ 0.9719 \\ 0.1135 \end{bmatrix}, i_3 = \sigma(W_i\vec{y_3} + b_i) = \begin{bmatrix} 0.8501 \\ 0.0010 \\ 0.0572 \\ 0.6518 \end{bmatrix}, o_3 = \sigma(W_o\vec{y_3} + b_o) = \begin{bmatrix} 0.0192 \\ 0.9584 \\ 0.9982 \\ 0.9841 \end{bmatrix} f3=σ(Wfy3+bf)= 0.00080.99850.97190.1135 ,i3=σ(Wiy3+bi)= 0.85010.00100.05720.6518 ,o3=σ(Woy3+bo)= 0.01920.95840.99820.9841
随后我们进行计算当前输入单元状态 c 3 ~ \tilde{c_3} c3~,即
c 3 ~ = tanh ( W c y 3 ⃗ + b c ) = [ 0.7956 − 0.9998 0.9807 − 0.9994 ] T \tilde{c_3} = \text{tanh}(W_c\vec{y_3} + b_c) = \begin{bmatrix} 0.7956 & -0.9998 & 0.9807 & -0.9994 \end{bmatrix}^T c3~=tanh(Wcy3+bc)=[0.7956−0.99980.9807−0.9994]T
接着我们计算当前时刻单元状态 c 3 c_3 c3,即
c 3 = f 3 ∘ c 2 + i 3 ∘ c 3 ~ = [ 0.6763 − 0.0010 0.0561 − 0.6515 ] T c_3 = f_3 \circ c_{2} + i_3 \circ \tilde{c_3} = \begin{bmatrix} 0.6763 & -0.0010 & 0.0561 & -0.6515 \end{bmatrix}^T c3=f3∘c2+i3∘c3~=[0.6763−0.00100.0561−0.6515]T
最后我们计算当前层隐藏层输出 h 3 h_3 h3,即
h 3 = o 3 ∘ d 3 = o 3 ∘ tanh ( c 3 ) = [ 0.0113 − 0.0010 0.0559 − 0.5636 ] T h_3 = o_3 \circ d_3 = o_3 \circ \text{tanh}(c_3) = \begin{bmatrix} 0.0113 & -0.0010 & 0.0559 & -0.5636 \end{bmatrix}^T h3=o3∘d3=o3∘tanh(c3)=[0.0113−0.00100.0559−0.5636]T
得到了
h
3
h_3
h3之后,我们可以简单将
h
3
h_3
h3的结果作为预测的结果,然后使用MSE进行计算损失,MSE的计算公式如下所示。
MSE
=
1
n
∑
i
=
1
n
(
y
i
^
−
y
i
)
2
\text{MSE} = \frac{1}{n} \sum_{i = 1}^{n} (\hat{y_i} - y_i )^2
MSE=n1i=1∑n(yi^−yi)2
MSE
=
1
4
[
(
3054.9793
−
0.0113
)
2
+
(
3088.6357
+
0.0010
)
2
+
(
3092.43
−
0.0559
)
2
+
(
3054.9793
+
0.5636
)
2
]
=
9437756.3022
\text{MSE} = \frac{1}{4} [(3054.9793 - 0.0113)^2 + (3088.6357 + 0.0010)^2 + ( 3092.43 - 0.0559)^2 + (3054.9793 + 0.5636)^2 ] \\ = 9437756.3022
MSE=41[(3054.9793−0.0113)2+(3088.6357+0.0010)2+(3092.43−0.0559)2+(3054.9793+0.5636)2]=9437756.3022
然后我们就得到我们的损失为
9437756.3022
9437756.3022
9437756.3022。
以上就完成了一次将LSTM用于预测的计算。可以看到误差很大,实际应用中会先将数据输入到LSTM前,会进行一次归一化,在LSTM的输出后,会将隐藏层的结果进行一层线性映射,然后使用逆归一化,这样得到结果会比较接近我们的指数。
小结
LSTM模型的具体训练步骤如下:
1.LSTM 单元的输入包含当前时刻的输入 v e c x t vec{x_t} vecxt、上一时刻的输出状态 h t − 1 h_{t-1} ht−1以及上一时刻的单元状态 c t − 1 c_{t-1} ct−1。在进行运算第一层LSTM单元时,我们会手动初始化 h 0 h_0 h0、 c 0 c_0 c0,而在后面的LSTM的单元中 h t − 1 h_{t-1} ht−1和 c t − 1 c_{t-1} ct−1,都可以由上一次的LSTM单元获得。其中, x t ⃗ ∈ R m × 1 \vec{x_t} \in \mathbb{R}^{m \times 1} xt∈Rm×1, m m m是输入特征的维度, h t − 1 h_{t-1} ht−1上一时刻的输出状态,形状为 h t − 1 ∈ R d × 1 h_{t-1} \in \mathbb{R}^{d \times 1} ht−1∈Rd×1, d d d是LSTM单元的隐藏状态大小, c t − 1 c_{t-1} ct−1是上一时刻的单元状态,形状为 c t − 1 ∈ R d × 1 c_{t-1} \in \mathbb{R}^{d \times 1} ct−1∈Rd×1。
我们通常会把
h
t
−
1
h_{t-1}
ht−1和
x
t
⃗
\vec{x_t}
xt拼在一起形成更长的向量
y
t
⃗
\vec{y_t}
yt,我们通常竖着拼,即
y
t
⃗
∈
R
(
d
+
m
)
×
1
\vec{y_t} \in \mathbb{R}^{(d + m) \times 1}
yt∈R(d+m)×1 ,然后
y
t
⃗
\vec{y_t}
yt会传入各个门。
y
t
⃗
=
[
h
t
−
1
;
x
t
⃗
]
=
[
h
t
−
1
x
t
⃗
]
\vec{y_t} = [h_{t-1};\vec{x_t}] = \left[{\begin{matrix}h_{t-1} \\ \vec{x_t} \end{matrix}}\right]
yt=[ht−1;xt]=[ht−1xt]
2.随后是计算各个门的输出,各个门的输入是 y t ⃗ \vec{y_t} yt。我们将 y t ⃗ \vec{y_t} yt与门中的权重矩阵 W W W相乘再加上置偏值 b b b,得到中间结果 M M M。然后对 M M M取Sigmoid,得到门的输出 g t g_t gt,其形状与单元状态 c t c_t ct相同,即 g t ∈ R d × 1 g_t \in \mathbb{R}^{d \times 1} gt∈Rd×1。
f
t
=
σ
(
W
f
y
t
⃗
′
+
b
f
)
=
1
1
+
e
−
(
W
f
y
t
⃗
+
b
f
)
f_t = \sigma(W_f\vec{y_t}' + b_f) = \frac{1}{1 + e^{-(W_f\vec{y_t} + b_f)}}
ft=σ(Wfyt′+bf)=1+e−(Wfyt+bf)1
i
t
=
σ
(
W
i
y
t
⃗
+
b
i
)
=
1
1
+
e
−
(
W
i
y
t
⃗
+
b
i
)
i_t = \sigma(W_i\vec{y_t} + b_i) = \frac{1}{1 + e^{-(W_i\vec{y_t} + b_i)}}
it=σ(Wiyt+bi)=1+e−(Wiyt+bi)1
o
t
=
σ
(
W
o
y
t
⃗
+
b
o
)
=
1
1
+
e
−
(
W
f
y
t
⃗
+
b
o
)
o_t = \sigma(W_o\vec{y_t} + b_o) = \frac{1}{1 + e^{-(W_f\vec{y_t} + b_o)}}
ot=σ(Woyt+bo)=1+e−(Wfyt+bo)1
其中,
y
t
⃗
∈
R
(
d
+
m
)
×
1
\vec{y_t} \in \mathbb{R}^{(d + m) \times 1}
yt∈R(d+m)×1,
W
f
、
W
i
、
W
o
∈
R
d
×
(
d
+
m
)
W_f、W_i、W_o \in \mathbb{R}^{d \times (d + m)}
Wf、Wi、Wo∈Rd×(d+m),
b
f
、
b
i
、
b
o
∈
R
d
×
1
b_f、b_i、b_o \in \mathbb{R}^{d \times 1}
bf、bi、bo∈Rd×1,
f
t
、
i
t
、
o
t
∈
R
d
×
1
f_t、i_t、o_t \in \mathbb{R}^{d \times 1}
ft、it、ot∈Rd×1。
3.计算当前输入单元状态
c
t
~
\tilde{c_t}
ct~的值,表示当前输入要保留多少内容到记忆中。我们将
y
t
⃗
\vec{y_t}
yt与当前时刻状态单元的权重矩阵
W
c
W_c
Wc相乘再加上置偏值
b
c
b_c
bc,得到中间结果
M
c
M_c
Mc,然后对
M
c
M_c
Mc取tanh,得到输出
c
t
~
\tilde{c_t}
ct~。
c
t
~
=
tanh
(
W
c
y
t
⃗
+
b
c
)
=
e
(
W
c
y
t
⃗
+
b
c
)
−
e
−
(
W
c
y
t
⃗
+
b
c
)
e
(
W
c
y
t
⃗
+
b
c
)
+
e
−
(
W
c
y
t
⃗
+
b
c
)
\tilde{c_t} = \text{tanh}(W_c\vec{y_t} + b_c) = \frac{e^{(W_c\vec{y_t} + b_c)}-e^{-(W_c\vec{y_t} + b_c)}}{e^{(W_c\vec{y_t} + b_c)}+e^{-(W_c\vec{y_t} + b_c)}}
ct~=tanh(Wcyt+bc)=e(Wcyt+bc)+e−(Wcyt+bc)e(Wcyt+bc)−e−(Wcyt+bc)
其中, y t ⃗ ∈ R ( d + m ) × 1 \vec{y_t} \in \mathbb{R}^{(d + m) \times 1} yt∈R(d+m)×1, W c ∈ R d × ( d + m ) W_c \in \mathbb{R}^{d \times (d + m)} Wc∈Rd×(d+m), b c ∈ R d × 1 b_c \in \mathbb{R}^{d \times 1} bc∈Rd×1, c t ~ ∈ R d × 1 \tilde{c_t} \in \mathbb{R}^{d \times 1} ct~∈Rd×1。
4.接下来我们进行当前时刻单元状态
c
t
c_t
ct的计算。我们使用遗忘门和输入门得到的结果
f
t
f_t
ft、
i
t
i_t
it和上一时刻单元状态
c
t
−
1
c_{t-1}
ct−1来计算当前时刻单元状态
c
t
c_t
ct。我们分别将
f
t
f_t
ft、
c
t
−
1
c_{t-1}
ct−1按元素相乘,
i
t
i_t
it和
c
t
~
\tilde{c_t}
ct~按元素相乘,然后再将两者相加得到我们的但钱时刻单元状态
c
t
c_t
ct。
c
t
=
f
t
∘
c
t
−
1
+
i
t
∘
c
t
~
c_t = f_t \circ c_{t-1} + i_t \circ \tilde{c_t}
ct=ft∘ct−1+it∘ct~
其中,
f
t
∈
R
d
×
1
f_t \in \mathbb{R}^{d \times 1}
ft∈Rd×1时遗忘门输出,
i
t
∈
R
d
×
1
i_t \in \mathbb{R}^{d \times 1}
it∈Rd×1是输入门输出,
c
t
~
∈
R
d
×
1
\tilde{c_{t}} \in \mathbb{R}^{d \times 1}
ct~∈Rd×1是当前输入状态单元,
c
t
−
1
∈
R
d
×
1
c_{t-1} \in \mathbb{R}^{d \times 1}
ct−1∈Rd×1 是上一时刻状态单元,
∘
\circ
∘表示 按元素乘。
5.最后模型的输出是
h
t
h_t
ht和当前时刻的单元状态
c
t
c_t
ct,而
h
t
h_t
ht由当前时刻的单元状态
c
t
c_t
ct和输出门的输出
o
t
o_t
ot确定。我们将当前时刻的单元状态
c
t
c_t
ct取 tanh得到
d
t
d_t
dt,然后将
d
t
d_t
dt 与
o
t
o_t
ot按元素相乘得到最后的
h
t
h_t
ht。
h
t
=
o
t
∘
d
t
=
o
t
∘
tanh
(
c
t
)
=
e
c
t
−
e
−
c
t
e
c
t
+
e
−
c
t
h_t = o_t \circ d_t = o_t \circ \text{tanh}(c_t) = \frac{e^{c_t}-e^{-c_t}}{e^{c_t}+e^{-c_t}}
ht=ot∘dt=ot∘tanh(ct)=ect+e−ctect−e−ct
其中
h
t
∈
R
d
×
1
h_t \in \mathbb{R}^{d \times 1}
ht∈Rd×1 为当前层隐藏状态,
o
t
∈
R
d
×
1
o_t \in \mathbb{R}^{d \times 1}
ot∈Rd×1为输出门的输出,
c
t
∈
R
d
×
1
c_t \in \mathbb{R}^{d \times 1}
ct∈Rd×1为当前时刻状态单元。
import torch
import torch.nn as nn
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
# 读取数据
df = pd.read_csv('sh_data.csv')
df = df.iloc[-30:, [2, 5, 3, 4]]
df1 = df[25:28].reset_index(drop=True)
df2 = df1.reset_index(drop=True)
data = df[['open', 'close', 'high', 'low']].values.astype(float)
# 标准化数据
scaler = MinMaxScaler(feature_range=(0, 1))
data = scaler.fit_transform(data)
# 创建时间序列数据
def create_sequences(data, time_step=1):
X, y = [], []
for i in range(len(data) - time_step):
X.append(data[i:(i + time_step)])
y.append(data[i + time_step])
return np.array(X), np.array(y)
time_step = 2 # 时间步长设置为2天
X, y = create_sequences(data, time_step)
# 转换为PyTorch张量
X = torch.FloatTensor(X)
y = torch.FloatTensor(y)
class LSTM(nn.Module):
def __init__(self, input_size, hidden_layer_size, output_size):
super(LSTM, self).__init__()
self.hidden_layer_size = hidden_layer_size
self.lstm = nn.LSTM(input_size, hidden_layer_size)
self.linear = nn.Linear(hidden_layer_size, output_size)
self.hidden_cell = (torch.zeros(1, 1, self.hidden_layer_size),
torch.zeros(1, 1, self.hidden_layer_size))
def forward(self, input_seq):
lstm_out, self.hidden_cell = self.lstm(input_seq.view(len(input_seq), 1, -1), self.hidden_cell)
predictions = self.linear(lstm_out.view(len(input_seq), -1))
return predictions[-1]
input_size = 4 # 输入特征数量
hidden_layer_size = 4
output_size = 4 # 输出特征数量
model = LSTM(input_size=input_size, hidden_layer_size=hidden_layer_size, output_size=output_size)
loss_function = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# epochs = 1
# for i in range(epochs):
# for seq, labels in zip(X, y):
# optimizer.zero_grad()
# model.hidden_cell = (torch.zeros(1, 1, model.hidden_layer_size),
# torch.zeros(1, 1, model.hidden_layer_size))
# y_pred = model(seq)
# single_loss = loss_function(y_pred, labels)
# single_loss.backward()
# optimizer.step()
# if i % 10 == 0:
# print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')
# 只进行一次训练
seq, labels = X[0], y[0]
optimizer.zero_grad()
model.hidden_cell = (torch.zeros(1, 1, model.hidden_layer_size),
torch.zeros(1, 1, model.hidden_layer_size))
y_pred = model(seq)
single_loss = loss_function(y_pred, labels)
single_loss.backward()
optimizer.step()
print(f'Single training loss: {single_loss.item():10.8f}')
model.eval()
# 预测下一天的四个特征
with torch.no_grad():
seq = torch.FloatTensor(data[-time_step:])
model.hidden_cell = (torch.zeros(1, 1, model.hidden_layer_size),
torch.zeros(1, 1, model.hidden_layer_size))
next_day = model(seq).numpy()
# 将预测结果逆归一化
next_day = scaler.inverse_transform(next_day.reshape(-1, output_size))
print(f'Predicted features for the next day: open={next_day[0][0]}, close={next_day[0][1]}, high={next_day[0][2]}, low={next_day[0][3]}')
# 获取训练集的预测值
train_predict = []
for seq in X:
with torch.no_grad():
model.hidden_cell = (torch.zeros(1, 1, model.hidden_layer_size),
torch.zeros(1, 1, model.hidden_layer_size))
train_predict.append(model(seq).numpy())
# 将预测结果逆归一化
train_predict = scaler.inverse_transform(np.array(train_predict).reshape(-1, output_size))
actual = scaler.inverse_transform(data)
# 绘制图形
plt.figure(figsize=(10, 6))
for i, col in enumerate(['open', 'close', 'high', 'low']):
plt.subplot(2, 2, i+1)
plt.plot(actual[:, i], label=f'Actual {col}')
plt.plot(range(time_step, time_step + len(train_predict)), train_predict[:, i], label=f'Train Predict {col}')
plt.legend()
plt.tight_layout()
plt.show()