一、内容摘要
神经网络在序列预测任务中具有广泛的应用,它们能够对各种类型的序列数据进行建模和预测,例如时间序列、趋势分析、自然语言和DNA序列等。
在这篇博客中,我们将介绍如何使用神经网络进行简单的序列预测任务,包括数据准备、模型构建、训练和预测等方面。
说明:本文涉及方法均为说明性demo,实际数据应用请使用符合数据特性的模型和方法。
二、版本及环境
Anaconda做环境控制(与项目本身关系不大)
Python
Pytorch
三、结构及方法
1.样例数据准备
样例数据使用了一种非常简单的构造方法,即叠加两种不同频率的波形构成目标数据(参考傅里叶变换),因此得到的数据其实是一种较为理想纯净的、具有明显频率特征的数据,代码如下:
def seq2sets(seq, ws):
out = []
L = len(seq)
for i in range(L - ws):
window = seq[i:i + ws]
label = seq[i + ws:i + ws + 1]
out.append((window, label))
return out
# Sequence and Datasets
t = np.arange(0, 1, 0.001) # 时刻序列
train_seq = torch.FloatTensor(np.sin(2 * np.pi * 1 * t) + np.sin(2 * np.pi * 300 * t))
train_data = seq2sets(train_seq, WINDOW_SIZE)
plt.plot(train_seq)
plt.show()
该数据序列绘图如下,可以看出由高频和低频两部分组成:
数据集裁剪方式为根据设定的窗口大小进行切片,长序列裁剪成许多短序列(参考代码中的seq2sets函数)。代码仅供说明用途,非最佳处理方式。
序列预测的目标:预测1000~1200位置上的数据。
- 模型输入:一个WINDOW_SIZE大小的输入
- 模型输出:一个单点输出
多步预测可以采用将预测值继续丢进网络进行下一步预测的方法实现。
2.BP神经网络
可以认为是最基本的神经网络结构,能学习和存贮大量的输入-输出模式映射关系,而无需事前揭示描述这种映射关系的数学方程。
BP网络通过反向传播来不断调整网络的权值和阈值,使网络的误差最小。
class BP(nn.Module):
def __init__(self, input_dim):
super().__init__()
self.Linear1 = nn.Linear(input_dim, 50)
self.Linear2 = nn.Linear(50, 1)
self.relu = nn.ReLU()
def forward(self, x):
x = self.Linear1(x)
x = self.relu(x)
x = self.Linear2(x)
return x
3.CNN(卷积神经网络)
图像识别中经典的网络结构,以局部视野和全局共享为最大特性。
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1d = nn.Conv1d(1, 64, kernel_size=5, stride=3)
self.relu = nn.ReLU(inplace=True)
self.Linear1 = nn.Linear(64 * 66, 50)
self.Linear2 = nn.Linear(50, 1)
def forward(self, x):
x = self.conv1d(x)
x = self.relu(x)
x = x.view(-1)
x = self.Linear1(x)
x = self.relu(x)
x = self.Linear2(x)
return x
4.LSTM(长短期记忆网络)
LSTM是一种特殊的递归神经网络 。被设计用于解决一般递归神经网络中普遍存在的长期依赖问题,使用LSTM可以有效的传递和表达长时间序列中的信息并且不会导致长时间前的有用信息被忽略(遗忘)。
class LSTM(nn.Module):
def __init__(self, inp_dim, out_dim, mid_dim, mid_layers):
super(LSTM, self).__init__()
self.rnn = nn.LSTM(inp_dim, mid_dim, mid_layers) # rnn
self.reg = nn.Sequential(
nn.Linear(mid_dim, mid_dim),
nn.ReLU(),
nn.Linear(mid_dim, out_dim),
) # regression
def forward(self, x):
y = self.rnn(x)[0] # y, (h, c) = self.rnn(x)
seq_len, batch_size, hid_dim = y.shape
y = y.view(-1, hid_dim)
y = self.reg(y)
y = y.view(seq_len, batch_size, -1)
return y
def output_y_hc(self, x, hc):
y, hc = self.rnn(x, hc) # y, (h, c) = self.rnn(x)
seq_len, batch_size, hid_dim = y.size()
y = y.view(-1, hid_dim)
y = self.reg(y)
y = y.view(seq_len, batch_size, -1)
return y, hc
四、完整代码
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import time
class BP(nn.Module):
def __init__(self, input_dim):
super().__init__()
self.Linear1 = nn.Linear(input_dim, 50)
self.Linear2 = nn.Linear(50, 1)
self.relu = nn.ReLU()
def forward(self, x):
x = self.Linear1(x)
x = self.relu(x)
x = self.Linear2(x)
return x
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1d = nn.Conv1d(1, 64, kernel_size=5, stride=3)
self.relu = nn.ReLU(inplace=True)
self.Linear1 = nn.Linear(64 * 66, 50)
self.Linear2 = nn.Linear(50, 1)
def forward(self, x):
x = self.conv1d(x)
x = self.relu(x)
x = x.view(-1)
x = self.Linear1(x)
x = self.relu(x)
x = self.Linear2(x)
return x
class LSTM(nn.Module):
def __init__(self, inp_dim, out_dim, mid_dim, mid_layers):
super(LSTM, self).__init__()
self.rnn = nn.LSTM(inp_dim, mid_dim, mid_layers) # rnn
self.reg = nn.Sequential(
nn.Linear(mid_dim, mid_dim),
nn.ReLU(),
nn.Linear(mid_dim, out_dim),
) # regression
def forward(self, x):
y = self.rnn(x)[0] # y, (h, c) = self.rnn(x)
seq_len, batch_size, hid_dim = y.shape
y = y.view(-1, hid_dim)
y = self.reg(y)
y = y.view(seq_len, batch_size, -1)
return y
def output_y_hc(self, x, hc):
y, hc = self.rnn(x, hc) # y, (h, c) = self.rnn(x)
seq_len, batch_size, hid_dim = y.size()
y = y.view(-1, hid_dim)
y = self.reg(y)
y = y.view(seq_len, batch_size, -1)
return y, hc
def seq2sets(seq, ws):
out = []
L = len(seq)
for i in range(L - ws):
window = seq[i:i + ws]
label = seq[i + ws:i + ws + 1]
out.append((window, label))
return out
def train(model, optimizer, criterion, epoch):
model.train()
for seq_i, y_train in train_data:
optimizer.zero_grad()
y_pred = model(seq_i.reshape(1, 1, -1))
loss = criterion(y_pred, y_train)
loss.backward()
optimizer.step()
print(f'Epoch: {epoch:2} Loss: {loss.item():10.8f}')
def predict(model, future_step):
model.eval()
# 选取序列最后WINDOW_SIZE个值开始预测
preds = train_seq[-WINDOW_SIZE:].tolist()
# 循环的每一步表示向时间序列向后滑动一格
for i in range(future_step):
seq_i = torch.FloatTensor(preds[-WINDOW_SIZE:])
with torch.no_grad():
preds.append(model(seq_i.reshape(1, 1, -1)).item())
return preds[-future_step:]
if __name__ == '__main__':
# Configurations
WINDOW_SIZE = 200
FUTURE_STEP = 200
EPOCH_MAX = 20
LEARNING_RATE = 0.0001
MODEL_DICT = {'method_index': [0, 1, 2, 3],
'method_name': ['BP', 'CNN', 'LSTM'],
'model': [BP(input_dim=WINDOW_SIZE),
CNN(),
LSTM(inp_dim=WINDOW_SIZE, out_dim=1, mid_dim=20, mid_layers=3)],
}
# Sequence and Datasets
t = np.arange(0, 1, 0.001) # 时刻序列
train_seq = torch.FloatTensor(np.sin(2 * np.pi * 1 * t) + np.sin(2 * np.pi * 300 * t))
# train_seq = torch.FloatTensor(np.load('frequence.npz')['arr_0'])
train_data = seq2sets(train_seq, WINDOW_SIZE)
plt.plot(train_seq)
plt.show()
# Model setting
for model_index, model in enumerate(MODEL_DICT['method_name']):
model = MODEL_DICT['model'][model_index]
criterion = nn.MSELoss() # loss function: mse
optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE) # optimizer and lr
start_time = time.time()
for epoch in range(EPOCH_MAX):
train(model, optimizer, criterion, epoch)
print(f'\nDuration: {time.time() - start_time:.0f} seconds')
preds = predict(model, future_step=WINDOW_SIZE)
# 对比真实值和预测值
plt.grid()
plt.plot(train_seq)
plt.plot(np.arange(len(train_seq), len(train_seq) + FUTURE_STEP, 1), preds)
# x = np.arange('2018-02-01', '2019-02-01', dtype='datetime64[M]').astype('datetime64[D]')
plt.show()
五、部分运行结果
三种方法的结果展示:
说明:该结果仅基于极短时间的训练。并且由于数据非常简单,因此BP体现出较好的效果。
但本结果并不能表明三种模型在实际应用中的性能,一般来说,LSTM和Transformer类型的网络能对序列数据处理更好。