名称:使用高寒山区输电线路覆冰厚度数据的单一时间序列的LSTM网络
具体步骤一览:
1、 时间序列原始数据预处理
2、 时间序列数据集制作
3、 lstm网络训练
4、 lstm网络测试
5、 模型处理结果可视化处理
1、时间序列原始数据预处理
具体步骤:
1、 pandas数据清洗
2、 原始的一维度时间序列数据的平滑处理
pandas数据清洗
因为覆冰厚度文件保存成.csv文件,主要使用pandas读取。
而pandas是一种很好的开源数据处理工具。我对pandas的使用还非常短浅。我认为需要特殊学习一下pandas。
在本次实验中没有用到太多pandas数据清晰的内容,就此略去。
时间序列数据的平滑处理
平滑:包括移动平均平滑、三次指数平滑等方法。其目的就是让变动剧烈的时间数据减少噪声,减少毛刺。
移动平均的代码:
def plotMovingAverage(series, window, plot_intervals=False, scale=1.96, plot_anomalies=False):
rolling_mean = series.rolling(window = window).mean()
plt.figure(figsize=(13,5))
plt.title("Moving average\n window size = {}".format(window))
plt.plot(rolling_mean, "g", label="Rolling mean trend")
if plot_intervals:
mae = mean_absolute_error(series[window:], rolling_mean[window:])
deviation = np.std(series[window:] - rolling_mean[window:])
lower_bond = rolling_mean - (mae + scale * deviation)
upper_bond = rolling_mean + (mae + scale * deviation)
plt.plot(upper_bond, "r--", label="Upper Bond / Lower Bond")
plt.plot(lower_bond, "r--")
# Having the intervals, find abnormal values
if plot_anomalies:
anomalies = pd.DataFrame(index=series.index)
anomalies['series<lower_bond'] = series[series<lower_bond]
anomalies['series>upper_bond'] = series[series>upper_bond]
plt.plot(anomalies, "ro", markersize=10)
plt.plot(series[window:], label="Actual values")
plt.legend(loc="upper left")
plt.grid(True)
return rolling_mean
的确,移动平均的代码我还没有搞懂,这一部分只是我之前找到的博客。
2、 时间序列数据集制作
时间序列数据集在pytorch的框架下,最好是使用Dataset、DataLoader制作。然后就可以循环使用。
时间序列数据集,说白就是(seq,label)的形式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-csiKSIgl-1643079110707)(en-resource://database/585:0)]
以上就是seq,整整30个数据,然后label就是一个数据。
lstm的目的就是用这30个数据来预测最后的一个数据。
在这里,30就是时间窗window。
代码:
# 加载数据的同时实现数据归一化
def load_data(file_name):
global MAX, MIN
df = pd.read_csv(file_name)
columns = df.columns
data = df[columns[-1]]
MAX = np.max(data)
MIN = np.min(data)
data = (data - MIN) / (MAX - MIN)
return data
# 数据类
class MyDataset(Dataset):
def __init__(self, data):
self.data = data
def __getitem__(self, item):
return self.data[item]
def __len__(self):
return len(self.data)
# 真正制作时间序列数据集
def making_seq(file_name, batch_size):
print("创造时序数据")
data = load_data(file_name)
load = data.tolist()
load = torch.FloatTensor(load).view(-1)
data = data.values.tolist()
seq = []
# 30是时间窗,这边就直接认为是这个了。
for i in range(len(data) - 30):
train_seq = []
train_label = []
for j in range(i, i + 30):
train_seq.append(load[j])
train_label.append(load[i + 30])
train_seq = torch.FloatTensor(train_seq).view(-1)
train_label = torch.FloatTensor(train_label).view(-1)
seq.append((train_seq, train_label))
Dtr = seq[0:int(len(seq) * 0.7)]
Dte = seq[int(len(seq) * 0.7):len(seq)]
train_len = int(len(Dtr) / batch_size) * batch_size
test_len = int(len(Dte) / batch_size) * batch_size
Dtr, Dte = Dtr[:train_len], Dte[:test_len]
train = MyDataset(Dtr)
test = MyDataset(Dte)
# shuffle=Fasle,这样子数据集就是连续的,便于可视化研究
Dtr = DataLoader(dataset=train, batch_size=batch_size, shuffle=False, num_workers=0)
Dte = DataLoader(dataset=test, batch_size=batch_size, shuffle=False, num_workers=0)
return Dtr, Dte
3、lstm网络训练
一、网络定义
很多人的博客上,lstm网络是自己一步步搭建的。本人因为时间有限,就直接引用了pytorch自带的lstm网络结构。
import torch.nn as nn
import torch
class LSTM(nn.Module):
def __init__(self, input_size, hidden_size, num_layers, output_size, batch_size):
super().__init__()
# input_size:专指输入数据的维度数,这里是1
self.input_size = input_size
# hidden_size:专指lstm网络内部的节点数,可以自定义,这里是64
self.hidden_size = hidden_size
# num_layers:专指网络层数,这里是1层,也可以是多层,可训练时间更长
self.num_layers = num_layers
# output_size:专指输入数据的维度数,这里是1
self.output_size = output_size
self.batch_size = batch_size
# num_directions:专指网络是否是双向的,本次是单向的,为1.
self.num_directions = 1
self.lstm = nn.LSTM(self.input_size, self.hidden_size, self.num_layers, batch_first=True)
self.linear = nn.Linear(self.hidden_size, self.output_size)
def forward(self, input_seq):
h_0 = torch.randn(self.num_directions * self.num_layers, self.batch_size, self.hidden_size)
c_0 = torch.randn(self.num_directions * self.num_layers, self.batch_size, self.hidden_size)
seq_len = input_seq.shape[1]
# input(batch_size, seq_len, input_size)
# input(1, 30, 1)
input_seq = input_seq.view(self.batch_size, seq_len, 1)
output, _ = self.lstm(input_seq, (h_0, c_0))
# 以下这一部分我不太理解
ouput = output.contiguous().view(self.batch_size * seq_len, self.hidden_size)
pred = self.linear(output)
pred = pred.view(self.batch_size, seq_len, -1)
pred = pred[:, -1, :]
return pred
二、模型训练:
# name;csv文件路径
# batch_size:自定义的批处理的大小,这里是1,方便后续画图
def LSTM_train(name, batch_size):
Dtr, Dte = making_seq(file_name=name, batch_size=batch_size)
input_size, hidden_size, num_layers, output_size = 1, 64, 1, 1
model = LSTM(input_size, hidden_size, num_layers, output_size, batch_size=batch_size)
loss_fn = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 迭代循环15次
epoches = 15
for i in range(epoches):
# cnt:计数器
cnt = 0
print("当前", i)
for (seq, label) in tqdm(Dtr):
cnt += 1
y_pred = model(seq)
loss = loss_fn(y_pred, label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if cnt % 100 == 0:
print('epoch', i, ':', cnt - 100, '~', cnt, loss.item())
return model
4、 模型测试:
一、一种mape函数:
说实话,我没有仔细研究过这个函数,具体原理我也不是很清楚。
def mape(actual, pred):
actual, pred = np.array(actual), np.array(pred)
return np.mean(np.abs((actual - pred) / actual)) * 100
二、测试函数:
import torch.nn as nn
import torch
from tqdm import tqdm
from scipy.interpolate import make_interp_spline
import numpy as np
from itertools import chain
import matplotlib.pyplot as plt
def test(name, b, lstm):
global MAX, MIN
Dtr, Dte = making_seq(file_name=name, batch_size=b)
pred = []
y = []
print("loading model....")
model = torch.load(lstm)
model.eval()
print("predicting...")
for (seq, target) in tqdm(Dte):
target = list(chain.from_iterable(target.data.tolist()))
y.extend(target)
seq_len = seq.shape[1]
seq = seq.view(model.batch_size, seq_len, 1)
with torch.no_grad():
y_pred = model(seq)
y_pred = list(chain.from_iterable(y_pred.data.tolist()))
pred.extend(y_pred)
y, pred = np.array([y]), np.array([pred])
y = (MAX - MIN) * y + MIN
pred = (MAX - MIN) * pred + MIN
print('accuracy:', mape(y, pred))
# plot
x = [i for i in range(1, 151)]
x_smooth = np.linspace(np.min(x), np.max(x), 600)
y_smooth = make_interp_spline(x, y.T[0:150])(x_smooth)
plt.plot(x_smooth, y_smooth, c='green', marker='*', ms=1, alpha=0.75, label='true')
y_smooth = make_interp_spline(x, pred.T[0:150])(x_smooth)
plt.plot(x_smooth, y_smooth, c='red', marker='o', ms=1, alpha=0.75, label='pred')
plt.grid(axis='y')
plt.legend()
plt.show()
5、最终图像可视化
这是在整体时间序列数据上的可视化图像,可见前面的训练集数据拟合的很好,可是最后面的大下坡之后的数据就预测的不太准确。而且出现了明显的时间滞后性,这个我就不太明白该如何处理。
本次学习成果记录完成,我想要再次写下自己目前拓展的领域,记录下自己的不足之处。
学习是一个不断进步的过程,这次我学会了真正训练lstm网络,粗略涉及了时间序列数据挖掘领域。但是这有一次深深刺痛我身上的弱点。
1、 查找博客和相关资料时候目标不明确,信息来源不可靠——以后要尽可能使用一些指标,或者说囤积可靠的网址。
2、 python语言的使用不熟练,针对python面向对象的书写还几乎为0
3、 pytorch运用不熟练,我还是不熟悉pytorch的运行机制、pytorch其中神奇的小工具,每一次新的尝试都会耗费大量精力。坦诚的说,每一次课程设计,技术储备时间比真正的课设设计时间要多出10倍不止,这极大限制我学习的进度。
4、 数据挖掘不熟悉,针对一个固定的数据集,训练模型前无法有效分析数据以选择相关的模型,模型训练出来也无法判断结果的好坏。
5、 计算机基础知识匮乏,电脑出了故障还需要自己去找修电脑的,真的是笑死人了。