序列模型
序列数据的定义是一组有先后次序的数据。显然文本也是一种序列模型,一段文字会因为单词的位置发生变化而使语句意思完全改变甚至变成一段没有任何意义的单词组合,例如:
狗咬人
人咬狗
咬人狗
人狗咬
可以看到前三句话都是一个有意义的句子,但是所表述的意思不相同,但最后一句已经没有意义了。
动力学系统
由有关联的变量组合在一起构成系统,且这些变量随时间变化而变化。这样的系统称为动力学系统。动力其实就是一种映射。
统计工具
处理序列数据需要用到统计工具,深度神经网络也可以进行处理。以股票价格预测为例。
其中,
x
t
x_t
xt表示在时间步
t
∈
Z
+
t\in{Z^+}
t∈Z+时候的价格,如果想要预测这个
x
t
x_t
xt,则需要根据过去的数据进行推断,则可以构建一个以下的条件概率:
x
t
∼
P
(
x
t
∣
x
t
−
1
,
x
t
−
2
,
.
.
.
,
x
1
)
x_t \sim P(x_t|x_{t-1}, x_{t-2}, ..., x_1)
xt∼P(xt∣xt−1,xt−2,...,x1)
自回归模型
为了实现这样一个预测,可以使用自回归模型,利用历史数据拟合出一条还不错的函数,使用这个函数来预测 t t t时间下的价格 x t x_t xt。但由于时间不断往前推进,数据量也是随着时间的变化而变化,该如何使用这些数据进行训练,一般有两种策略。
- 第一种策略
由于太长时间以前的数据可能对当前的预测起不到太大的作用,因此可以舍弃,因此可以使用一个时间窗口长度为
τ
\tau
τ的数据,即
x
t
−
1
,
x
t
−
2
,
.
.
.
,
x
t
−
τ
x_{t-1},x_{t-2},...,x_{t-\tau}
xt−1,xt−2,...,xt−τ的数据来构建模型,这种模型被称为自回归模型,因为它们是用自己的数据来预测自己的数据。我们在
τ
\tau
τ个数据上可以通过MLP训练一个网络模型
f
f
f,利用这个模型预测
x
t
x_t
xt。这样就变成了:
p
(
x
t
∣
x
t
−
1
,
x
t
−
2
,
.
.
.
,
x
1
)
=
p
(
x
t
∣
x
t
−
τ
,
.
.
.
,
x
t
−
1
)
=
p
(
x
t
∣
f
(
x
t
−
τ
,
.
.
.
,
x
t
−
1
)
)
p(x_t|x_{t-1},x_{t-2},...,x_1)=p(x_t|x_{t-\tau},...,x_{t-1})=p(x_t|f(x_{t-\tau},...,x_{t-1}))
p(xt∣xt−1,xt−2,...,x1)=p(xt∣xt−τ,...,xt−1)=p(xt∣f(xt−τ,...,xt−1))
- 第二种策略
保留对过去观测的总结 h t h_t ht,使用 h t h_t ht来对 x t x_t xt进行预测,即 x ^ t = P ( x t ∣ h t ) \hat{x}_t=P(x_t|h_t) x^t=P(xt∣ht),而通过 h t = g ( h t − 1 ∣ x t − 1 ) h_t=g(h_{t-1}|x_{t-1}) ht=g(ht−1∣xt−1)来更新 h t h_t ht。由于 h t h_t ht从未被观测到,因此这类模型被称为隐变量自回归模型。
训练
我们通过自己生成一些数据来验证以下上面的工具的有效性。我们生成1000个由正弦函数和白噪声生成的数据,时间步为 0 , 1 , . . . , 999 0,1,...,999 0,1,...,999。
import torch
from matplotlib import pyplot as plt
# 产生1000个点
T = 1000
time = torch.arange(0, T-1, dtype=torch.float32)
# 利用正弦函数与白噪声生成数据
x = torch.sin(0.01 * time) + torch.normal(0, 0.2, time.shape)
# 可视化
plt.plot(time, x)
plt.xlabel("time")
plt.ylabel("x")
plt.show()
我们将时间窗口 τ \tau τ设置为4,利用一个简单的两层MLP模型来进行预测。
#设置时间窗口长度为4
tau = 4
# 最后四个数据预测t=1000时的数据没有真实数据验证,因此我们取前T-tau个数据
features = torch.zeros((T - tau, tau))
for i in range(tau):
features[:, i] = x[i:T - tau + i]
# 标签就是第tau个数据之后的数据
labels = x[tau:].reshape(-1, 1)
# 使用pytorch自带的Dataset类与数据加载类DataLoader
from torch.utils.data import dataset as dataset
import torch.utils.data.dataloader as dataloader
class Mydataset(dataset):
def __init__(self, feature, label):
self.feature = feature
self.label = label
self.length = len(label)
def __getitem__(self, index):
return self.feature[index], self.label[index]
def __len__(self):
return self.length
my_dataset = Mydataset(features, labels, x)
my_dataloader = dataloader(dataset=my_dataset, batch_size=Batch_Size, shuffle=False)
划分特征与标签,以及制作好了数据加载器之后,接下来就可以进行训练了。我们利用relu作为激活函数,损失函数使用平方差损失。
# 定义网络层
import torch.nn as nn
class MLP(nn.Module):
def __init__(self):
self.hidden = nn.Linear(4, 10)
self.relu = nn.ReLU()
self.output = nn.Linear(10, 1)
def forward(self, label):
label = self.hidden(label)
label = self.relu(label)
return self.output(label)
net = MLP()
loss = nn.MSELoss()
pytorch自动对网络参数进行初始化,因此我们就不需要自己初始化了。接下来对数据进行训练并打印损失。
# 设置batch_size以及准备数据加载
Batch_Size = 16
my_dataset = Mydataset(features, labels)
my_dataloader = DataLoader(dataset=my_dataset, batch_size=Batch_Size, shuffle=False)
# 定义网络层
import torch.nn as nn
class MLP(nn.Module):
def __init__(self):
super(MLP, self).__init__()
self.hidden = nn.Linear(4, 10)
self.relu = nn.ReLU()
self.output = nn.Linear(10, 1)
def forward(self, label):
label = self.hidden(label)
label = self.relu(label)
return self.output(label)
# 实例化网络
net = MLP()
# 使用MSE作为损失函数
loss = nn.MSELoss()
# 设置学习率、优化器
lr = 0.01
optim = torch.optim.Adam(net.parameters(), lr)
# 创建两个变量来计算每个epoch的平均损失
loss_value_sum = 0
batch_count = 0
# 开始训练
for epoch in range(500):
for i, [x, y] in enumerate(my_dataloader):
batch_count += 1
net.zero_grad()
out = net(x)
loss_value = loss(out, y)
loss_value_sum += loss_value
loss_value.sum().backward()
optim.step()
# 每隔100轮打印结果
if((epoch+1)%100 == 0):
print(f"epoch: {epoch+1}\tloss: {loss_value_sum/batch_count}")
结果如下:
epoch: 50 loss: 0.06987334787845612
epoch: 100 loss: 0.06591326743364334
epoch: 150 loss: 0.0646330788731575
epoch: 200 loss: 0.06399275362491608
epoch: 250 loss: 0.06360907852649689
epoch: 300 loss: 0.06335277855396271
epoch: 350 loss: 0.06316973268985748
epoch: 400 loss: 0.06303232163190842
epoch: 450 loss: 0.06292548775672913
epoch: 500 loss: 0.062840037047863
预测
onestep_preds = net(features).detach().numpy()
# 可视化
plt.plot(time, x, 'b--', label='data')
plt.plot(time[tau-1:], onestep_preds, 'r--', label='onestep_preds')
plt.legend()
plt.show()
还不错
完整代码
import torch
from matplotlib import pyplot as plt
T = 1000
time = torch.arange(0, T - 1, dtype=torch.float32)
x = torch.sin(0.01 * time) + torch.normal(0, 0.2, time.shape)
plt.plot(time, x)
plt.xlabel("time")
plt.ylabel("x")
plt.show()
tau = 4
# 最后四个数据预测t=1000时的数据没有真实数据验证,因此我们取前T-tau个数据
features = torch.zeros((T - tau, tau))
for i in range(tau):
features[:, i] = x[i:T - tau + i]
# 标签就是第tau个数据之后的数据
labels = x[tau:].reshape(-1, 1)
# 使用pytorch自带的Dataset类与数据加载类DataLoader
from torch.utils.data import Dataset, DataLoader
class Mydataset(Dataset):
def __init__(self, feature, label):
super(Mydataset, self).__init__()
self.feature = feature
self.label = label
self.length = len(label)
def __getitem__(self, index):
return torch.Tensor(self.feature[index]), torch.Tensor(self.label[index])
def __len__(self):
return self.length
Batch_Size = 16
my_dataset = Mydataset(features, labels)
my_dataloader = DataLoader(dataset=my_dataset, batch_size=Batch_Size, shuffle=False)
# 定义网络层
import torch.nn as nn
class MLP(nn.Module):
def __init__(self):
super(MLP, self).__init__()
self.hidden = nn.Linear(4, 10)
self.relu = nn.ReLU()
self.output = nn.Linear(10, 1)
def forward(self, label):
label = self.hidden(label)
label = self.relu(label)
return self.output(label)
net = MLP()
loss = nn.MSELoss()
lr = 0.01
optim = torch.optim.Adam(net.parameters(), lr)
loss_value_sum = 0
batch_count = 0
for epoch in range(500):
for i, [x_data, y_data] in enumerate(my_dataloader):
batch_count += 1
net.zero_grad()
out = net(x_data)
loss_value = loss(out, y_data)
loss_value_sum += loss_value
loss_value.sum().backward()
optim.step()
if (epoch + 1) % 50 == 0:
print(f"epoch: {epoch + 1}\tloss: {loss_value_sum / batch_count}")
onestep_preds = net(features).detach().numpy()
# 可视化
plt.plot(time, x, 'b--', label='data')
plt.plot(time[tau-1:], onestep_preds, 'r--', label='onestep_preds')
plt.legend()
plt.show()