当前位置:   article > 正文

机器学习周记(第二十周:文献阅读-TCN and LSTM)2023.12.4~2023.12.10

机器学习周记(第二十周:文献阅读-TCN and LSTM)2023.12.4~2023.12.10

目录

摘要

ABSTRACT

1 论文标题

2 论文摘要

3 问题描述

4 过去方案

5 论文方案

6 相关代码


摘要

  本周阅读了一篇关于TCN和LSTM进行光伏功率预测的文章,光伏功率数据是非线性不平稳的数据,在一定程度上也能反应论文模型在水质预测方面的效果。论文模型可以通过单步或者多步对光伏功率进行预测,实验结果表明,其具有非常优异的表现。本周也针对论文模型进行了简单的复现,预测结果并不非常准确,仍需进一步优化和调参。

ABSTRACT

This week, We read a paper on photovoltaic power prediction using Temporal Convolutional Network (TCN) and Long Short-Term Memory (LSTM). Photovoltaic power data is characterized by non-linear and non-stationary patterns, which, to some extent, can reflect the performance of the proposed models in water quality prediction. The paper's models can forecast photovoltaic power through single-step or multi-step predictions, and experimental results demonstrate their outstanding performance. We also attempted a simple replication of the models this week, but the prediction results were not highly accurate, indicating the need for further optimization and parameter tuning.

1 论文标题

Accurate one step and multistep forecasting of very short-term PV power using LSTM-TCN model - ScienceDirect

2 论文摘要

  准确的光伏功率预测正在成为将光伏电站并入电网、调度和保障电网安全的强制性任务。本文提出了一种利用LSTM-TCN预测光伏功率的新模型。它由长短期记忆和时间卷积网络模型之间的组合组成。LSTM用于从输入数据中提取时态特征,然后与TCN结合,在特征和输出之间建立连接。所提出的模型已使用包含测量光伏功率的历史时间序列的数据集进行了测试。然后,在不同季节、时段预报、多云、晴朗和间断性天气下,与LSTMTCN模型的精度进行了比较。对于一步预测,结果表明,所提出的模型优于LSTMTCN模型。与LSTMTCN相比,平均绝对误差分别下降了、秋季8.47%,14.26%、冬季6.91%,15.18%、春季10.22%,14.26%和夏季14.26%,14.23%。 在多步预测方面,LSTM-TCN在2步到7步的光伏功率预测中均优于所有比较的模型。

3 问题描述

  随着人口的增长和经济的发展,能源需求逐年增加。利用光伏发电能源作为清洁可再生能源可以满足这种能源需求的增加。光伏发电技术以其高性能、高效率、低成本的特点在世界范围内得到了广泛的应用。将光伏系统并入电网可以带来经济效益和环境效益。然而,由于太阳能的间歇性,这类能源的高渗透率也给电网系统带来了很多挑战。光伏发电预测是控制太阳能发电间歇性的最经济、最可行的解决方案之一。然而,由于光伏发电数据具有非线性和不稳定的特点,以及光伏发电所依赖的气象条件,准确预测是一项复杂的任务。光伏发电功率预测分为四个层次:长期预测为一个月至一年,中期预测为一周至一个月,短期预测为一周或一周以内,极短期或超短期预测为一分钟至几分钟。长期预测为光伏发电、输配电提供长期规划和决策,保证电力系统的可靠运行。中期预测为电力系统调度提供中期决策支持。短期预测为电力系统运行提供支持,提高了电力系统的可靠性。极短期预测用于电力平滑、实时电力调度和存储控制。

4 过去方案

  光伏发电预测主要有三种方法:物理方法统计方法混合方法。物理模型可以定义为具有描述系统输入和输出的数学方程的确定性模型。统计模型基于持久性的概念,通常依赖于机器学习和深度学习模型,这些模型可以从光伏发电历史数据中学习,并返回预测的光伏发电。对于混合模型,它是将前面的方法(物理模型和统计模型)相结合,以优化光伏功率预测的准确性。

  在物理方法上,主要研究PV电池的等效电路。光伏发电功率预测是通过数值天气预报(numerical weather prediction,NWP)获取太阳辐照度、温度等输入参数得到的。光伏电池的物理模型需要大量的电路参数,这些参数对模型的准确性影响很大。在Comparison of different physical models for PV power output prediction - ScienceDirect中,比较了采用两种不同方法确定PV电池温度和实际天气数据的不同物理模型(基于3、4和5个参数)。结果表明,物理模型的准确性取决于用于校准该模型的数据和用于计算PV电池温度的方法。PV组件的电气数据表和估算方程可用于提取PV电池物理模型的参数。此外,还可以采用人工神经网络和进化算法等先进方法来计算这些参数。Applied Sciences | Free Full-Text | Application of Symbiotic Organisms Search Algorithm for Parameter Extraction of Solar Cell Models (mdpi.com)使用了一种名为共生生物搜索算法(Symbiotic Organisms Search algorithm)的算法,并与各种进化算法进行了比较,以识别PV模型参数。此外,还使用了一种名为Social Network Optimization的进化算法将PV模型参数与实测数据进行匹配。

  几种基于机器学习的方法已被用于直接或间接地预测PV功率(预测太阳辐照度)。使用多层感知器神经网络(MLP)预测24小时的光伏发电功率,平均绝对误差(MAE)低于5%。Forecasting Power Output of Photovoltaic Systems Based on Weather Classification and Support Vector Machines | IEEE Journals & Magazine | IEEE Xplore采用支持向量机(Support Vector Machine,SVM)方法结合天气分类,结合历史PV功率时间序列,提前一天预测PV功率。结果表明,不同天气条件下光伏发电的平均相对误差(MRE)平均值为8.64%。Short-term power forecasting system for photovoltaic plants - ScienceDirect采用短期人工神经网络(ANN),利用PV输出功率的历史数据和来自数值天气预报模型(NWP)的天气预报,提前16 ~ 39小时预测PV功率。该技术已与自回归综合移动平均(ARIMA)k -近邻(K-NN)进行了比较。结果表明,该方法优于其他方法。Day-ahead forecasting of solar photovoltaic output power using multilayer perceptron | Neural Computing and Applications (springer.com)中也使用了MLPANN的组合(MLP-ANN)来估计一天前的光伏功率。对于Levenberg-Marquardt等不同的学习算法,所得到的MAE在[1.92%-11.28%]范围内。Energies | Free Full-Text | Extreme Learning Machines for Solar Photovoltaic Power Predictions (mdpi.com)中已经开发了一种名为极限学习机(ELM)的算法,用于训练MLP网络提前一天进行预测。该方法在精度上优于反向传播算法。近年来,深度学习模型也被广泛应用于光伏发电功率预测。采用前馈深度神经网络(DNN)提前24小时预测光伏发电功率,MAE为2.9%。在准确性方面,最常用的深度学习模型是长短期记忆(LSTM)模型。在Deep learning neural networks for short-term photovoltaic power forecasting - ScienceDirect中,对LSTM、门控循环单元(GRU)卷积神经网络(CNN)CNN-LSTMCNN-GRU双向LSTM双向GRU等不同深度学习模型进行了比较研究。结果表明,LSTM模型在短期光伏发电预测方面优于其他深度学习模型。此外,LSTM模型可以与几种算法相结合来优化其精度,如灰色关联分析(GRA),如Hour-ahead photovoltaic power forecast using a hybrid GRA-LSTM model based on multivariate meteorological factors and historical power datasets - IOPscience所示。时序卷积神经网络(TCN)是一种为序列建模而设计的新型卷积结构,用于光伏发电功率预测,并与LSTMGRURNN多层前馈神经网络进行了比较。TCN在准确率上优于其他模型。

  如前所述,混合模型是结合两种方法(物理方法和统计方法)来提高预测精度的模型。一些文献中使用了几种混合模型。在Energies | Free Full-Text | A Physical Hybrid Artificial Neural Network for Short Term Forecasting of PV Plant Power Output (mdpi.com)中,一个名为物理混合人工神经网络(PHANN)的模型被用于预测光伏发电的提前日功率。该模型是基于双层人工神经网络(ANN)晴空太阳辐射物理模型相结合的,并与经典的人工神经网络进行了比较。结果表明,PHANN在准确率方面优于人工神经网络。此外,PHANN在日前功率预测中优于光伏组件物理五参数等效模型。此外,Energies | Free Full-Text | Computational Intelligence Techniques Applied to the Day Ahead PV Output Power Forecast: PHANN, SNO and Mixed (mdpi.com)报道了一种混合预测方法。该方法利用PHANN组合提高天气预报精度,利用社会网络优化(social network optimization, SNO)优化模型参数。结果表明,与PHANNSNO算法优化的光伏组件物理模型相比,混合方法提高了功率预测的准确性。

5 论文方案

  LSTM-TCN模型已被应用于不同的研究领域。Text Sentiment Classification Based on LSTM-TCN Hybrid Model and Attention Mechanism | Proceedings of the 4th International Conference on Computer Science and Application Engineering (acm.org)采用结合注意机制LSTM-TCN模型进行文本情感分类,并与有注意机制和不带注意机制的LSTM-CNNLSTMTCN模型进行比较。结果表明,LSTM-TCN优于LSTMTCN,在注意机制下,AT-LSTM-TCN优于AT-LSTM-CNN等模型。此外,在LSTM-TCN: dissolved oxygen prediction in aquaculture, based on combined model of long short-term memory network and temporal convolutional network | Environmental Science and Pollution Research (springer.com)中,LSTM-TCN也用于水产养殖中溶解氧的预测。将该模型与TCNLSTMCNN-LSTM进行了比较。与其他方法相比,LSTM-TCN的MAE=0.236%,MAPE=3.10%,RMSE=0.342,R2=0.94,表现出较好的性能。风电功率预测也是LSTM-TCN模型应用的领域。Ultra-short term wind power prediction applying a novel model named SATCN-LSTM - ScienceDirect采用LSTM-TCN结合自关注机制进行超短期风电预测,并与不同预测模型进行精度比较。同样,提出的模型SATCN-LSTM优于LSTMTCNCNN-LSTM,并且略优于TCN-LSTM(无自注意机制。与之前的工作一致,与其他深度学习模型相比,TCNLSTM模型的结合显示出了很好的结果。尽管LSTM-TCN模型具有较高的精度和性能,但据我们所知,它还没有被用于光伏发电功率预测。本文研究了LSTM-TCN模型的单步和多步光伏功率预测。引入LSTM从光伏发电的历史时间序列中提取时间特征,然后结合TCN构建提取的时间特征与输出之间的连接。然后将所提出的模型与LSTMTCN模型进行比较,以证明其有效性和准确性。

模型总体架构

  论文提出的模型结合LSTMTCN,利用其历史和气象数据(如全球水平辐射)来预测PV功率。LSTM-TCN的流程图如下图所示。该模型由三层LSTM模型组成,用于从输入数据中提取时间特征。第一层有256个单元,第二层有128个单元,第三层有64个单元。在LSTM层中加入3个堆叠的TCN模型残差块。每个残差块具有相同数量的Filter和内核大小。残差块的膨胀系数分别为1、2和4。将TCN模型与LSTM相结合,提高了预测精度,并建立了特征与输出之间的联系。最后,使用Dense层进行维度变换。用于模型训练和验证的损失函数是均方误差(MSE),模型优化器是自适应矩估计(Adam)。值得注意的是,LSTMTCN参数是在使用Keras调谐器函数搜索误差最小的最优参数后,测试了膨胀率内核大小Filter数量LSTM单元和层数等几个值后选择的。 

LSTM-TCN总体架构

  为了在时间序列预测中使用深度学习模型,必须将数据集转换为监督回归问题,其中输入特征和输出(标签)被呈现和定义。滑动窗口方法通过分割数据集来将数据集转换为监督学习问题,以获得一个序列(固定窗口大小)的观测值作为模型的输入,并获得固定数量的后续观测值作为输出,这些观测值可以是一步预测的一个后续观测值,也可以是多步预测的一系列后续观测值。通过每次滑动窗口一个时间步来对整个数据集重复分割,以获得下一个输入和输出序列。下图显示了多步时间序列预测(输入序列大小= 5,输出序列大小= 3)的滑动窗口过程。数据集分为训练数据集,用于训练所提出的深度学习模型和测试数据集,以评估模型的准确性和性能。

6 相关代码

运用TCN-LSTM进行预测:

  1. import argparse
  2. import time
  3. import numpy as np
  4. import pandas as pd
  5. import torch
  6. import torch.nn as nn
  7. from matplotlib import pyplot as plt
  8. from torch.nn.utils import weight_norm
  9. from torch.utils.data import DataLoader
  10. from torch.utils.data import Dataset
  11. from tqdm import tqdm
  12. # 随机数种子
  13. np.random.seed(0)
  14. class StandardScaler():
  15. def __init__(self):
  16. self.mean = 0.
  17. self.std = 1.
  18. def fit(self, data):
  19. self.mean = data.mean(0)
  20. self.std = data.std(0)
  21. def transform(self, data):
  22. mean = torch.from_numpy(self.mean).type_as(data).to(data.device) if torch.is_tensor(data) else self.mean
  23. std = torch.from_numpy(self.std).type_as(data).to(data.device) if torch.is_tensor(data) else self.std
  24. return (data - mean) / std
  25. def inverse_transform(self, data):
  26. mean = torch.from_numpy(self.mean).type_as(data).to(data.device) if torch.is_tensor(data) else self.mean
  27. std = torch.from_numpy(self.std).type_as(data).to(data.device) if torch.is_tensor(data) else self.std
  28. if data.shape[-1] != mean.shape[-1]:
  29. mean = mean[-1:]
  30. std = std[-1:]
  31. return (data * std) + mean
  32. def plot_loss_data(data):
  33. # 使用Matplotlib绘制线图
  34. plt.figure()
  35. plt.plot(data, marker='o')
  36. # 添加标题
  37. plt.title("loss results Plot")
  38. # 显示图例
  39. plt.legend(["Loss"])
  40. plt.show()
  41. class TimeSeriesDataset(Dataset):
  42. def __init__(self, sequences):
  43. self.sequences = sequences
  44. def __len__(self):
  45. return len(self.sequences)
  46. def __getitem__(self, index):
  47. sequence, label = self.sequences[index]
  48. return torch.Tensor(sequence), torch.Tensor(label)
  49. def create_inout_sequences(input_data, tw, pre_len, config):
  50. # 创建时间序列数据专用的数据分割器
  51. inout_seq = []
  52. L = len(input_data)
  53. for i in range(L - tw):
  54. train_seq = input_data[i:i + tw]
  55. if (i + tw + pre_len) > len(input_data):
  56. break
  57. if config.feature == 'MS':
  58. train_label = input_data[:, -1:][i + tw:i + tw + pre_len]
  59. else:
  60. train_label = input_data[i + tw:i + tw + pre_len]
  61. inout_seq.append((train_seq, train_label))
  62. return inout_seq
  63. def calculate_mae(y_true, y_pred):
  64. # 平均绝对误差
  65. mae = np.mean(np.abs(y_true - y_pred))
  66. return mae
  67. def create_dataloader(config, device):
  68. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>创建数据加载器<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
  69. df = pd.read_csv(config.data_path) # 填你自己的数据地址,自动选取你最后一列数据为特征列 # 添加你想要预测的特征列
  70. pre_len = config.pre_len # 预测未来数据的长度
  71. train_window = config.window_size # 观测窗口
  72. # 将特征列移到末尾
  73. target_data = df[[config.target]]
  74. df = df.drop(config.target, axis=1)
  75. df = pd.concat((df, target_data), axis=1)
  76. cols_data = df.columns[1:]
  77. df_data = df[cols_data]
  78. # 这里加一些数据的预处理, 最后需要的格式是pd.series
  79. true_data = df_data.values
  80. # 定义标准化优化器
  81. # 定义标准化优化器
  82. scaler = StandardScaler()
  83. scaler.fit(true_data)
  84. train_data = true_data[int(0.3 * len(true_data)):]
  85. valid_data = true_data[int(0.15 * len(true_data)):int(0.30 * len(true_data))]
  86. test_data = true_data[:int(0.15 * len(true_data))]
  87. print("训练集尺寸:", len(train_data), "测试集尺寸:", len(test_data), "验证集尺寸:", len(valid_data))
  88. # 进行标准化处理
  89. train_data_normalized = scaler.transform(train_data)
  90. test_data_normalized = scaler.transform(test_data)
  91. valid_data_normalized = scaler.transform(valid_data)
  92. # 转化为深度学习模型需要的类型Tensor
  93. train_data_normalized = torch.FloatTensor(train_data_normalized).to(device)
  94. test_data_normalized = torch.FloatTensor(test_data_normalized).to(device)
  95. valid_data_normalized = torch.FloatTensor(valid_data_normalized).to(device)
  96. # 定义训练器的的输入
  97. train_inout_seq = create_inout_sequences(train_data_normalized, train_window, pre_len, config)
  98. test_inout_seq = create_inout_sequences(test_data_normalized, train_window, pre_len, config)
  99. valid_inout_seq = create_inout_sequences(valid_data_normalized, train_window, pre_len, config)
  100. # 创建数据集
  101. train_dataset = TimeSeriesDataset(train_inout_seq)
  102. test_dataset = TimeSeriesDataset(test_inout_seq)
  103. valid_dataset = TimeSeriesDataset(valid_inout_seq)
  104. # 创建 DataLoader
  105. train_loader = DataLoader(train_dataset, batch_size=args.batch_size, shuffle=True, drop_last=True)
  106. test_loader = DataLoader(test_dataset, batch_size=args.batch_size, shuffle=False, drop_last=True)
  107. valid_loader = DataLoader(valid_dataset, batch_size=args.batch_size, shuffle=False, drop_last=True)
  108. print("通过滑动窗口共有训练集数据:", len(train_inout_seq), "转化为批次数据:", len(train_loader))
  109. print("通过滑动窗口共有测试集数据:", len(test_inout_seq), "转化为批次数据:", len(test_loader))
  110. print("通过滑动窗口共有验证集数据:", len(valid_inout_seq), "转化为批次数据:", len(valid_loader))
  111. print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>创建数据加载器完成<<<<<<<<<<<<<<<<<<<<<<<<<<<")
  112. return train_loader, test_loader, valid_loader, scaler
  113. class Chomp1d(nn.Module):
  114. def __init__(self, chomp_size):
  115. super(Chomp1d, self).__init__()
  116. self.chomp_size = chomp_size
  117. def forward(self, x):
  118. return x[:, :, :-self.chomp_size].contiguous()
  119. class TemporalBlock(nn.Module):
  120. def __init__(self, n_inputs, n_outputs, kernel_size, stride, dilation, padding, dropout=0.2):
  121. super(TemporalBlock, self).__init__()
  122. self.conv1 = weight_norm(nn.Conv1d(n_inputs, n_outputs, kernel_size,
  123. stride=stride, padding=padding, dilation=dilation))
  124. self.chomp1 = Chomp1d(padding)
  125. self.relu1 = nn.ReLU()
  126. self.dropout1 = nn.Dropout(dropout)
  127. self.conv2 = weight_norm(nn.Conv1d(n_outputs, n_outputs, kernel_size,
  128. stride=stride, padding=padding, dilation=dilation))
  129. self.chomp2 = Chomp1d(padding)
  130. self.relu2 = nn.ReLU()
  131. self.dropout2 = nn.Dropout(dropout)
  132. self.net = nn.Sequential(self.conv1, self.chomp1, self.relu1, self.dropout1,
  133. self.conv2, self.chomp2, self.relu2, self.dropout2)
  134. self.downsample = nn.Conv1d(n_inputs, n_outputs, 1) if n_inputs != n_outputs else None
  135. self.relu = nn.ReLU()
  136. self.init_weights()
  137. def init_weights(self):
  138. self.conv1.weight.data.normal_(0, 0.01)
  139. self.conv2.weight.data.normal_(0, 0.01)
  140. if self.downsample is not None:
  141. self.downsample.weight.data.normal_(0, 0.01)
  142. def forward(self, x):
  143. out = self.net(x)
  144. res = x if self.downsample is None else self.downsample(x)
  145. return self.relu(out + res)
  146. class TemporalConvNet(nn.Module):
  147. def __init__(self, num_inputs, outputs, pre_len, num_channels, n_layers, kernel_size=2, dropout=0.2):
  148. super(TemporalConvNet, self).__init__()
  149. layers = []
  150. self.pre_len = pre_len
  151. self.n_layers = n_layers
  152. self.hidden_size = num_channels[-2]
  153. self.hidden = nn.Linear(num_channels[-1], num_channels[-2])
  154. self.relu = nn.ReLU()
  155. self.lstm = nn.LSTM(self.hidden_size, self.hidden_size, n_layers, bias=True,
  156. batch_first=True) # output (batch_size, obs_len, hidden_size)
  157. num_levels = len(num_channels)
  158. for i in range(num_levels):
  159. dilation_size = 2 ** i
  160. in_channels = num_inputs if i == 0 else num_channels[i - 1]
  161. out_channels = num_channels[i]
  162. layers += [TemporalBlock(in_channels, out_channels, kernel_size, stride=1, dilation=dilation_size,
  163. padding=(kernel_size - 1) * dilation_size, dropout=dropout)]
  164. self.network = nn.Sequential(*layers)
  165. self.linear = nn.Linear(num_channels[-2], outputs)
  166. def forward(self, x):
  167. x = x.permute(0, 2, 1)
  168. x = self.network(x)
  169. x = x.permute(0, 2, 1)
  170. batch_size, obs_len, features_size = x.shape # (batch_size, obs_len, features_size)
  171. xconcat = self.hidden(x) # (batch_size, obs_len, hidden_size)
  172. H = torch.zeros(batch_size, obs_len - 1, self.hidden_size).to(device) # (batch_size, obs_len-1, hidden_size)
  173. ht = torch.zeros(self.n_layers, batch_size, self.hidden_size).to(
  174. device) # (num_layers, batch_size, hidden_size)
  175. ct = ht.clone()
  176. for t in range(obs_len):
  177. xt = xconcat[:, t, :].view(batch_size, 1, -1) # (batch_size, 1, hidden_size)
  178. out, (ht, ct) = self.lstm(xt, (ht, ct)) # ht size (num_layers, batch_size, hidden_size)
  179. htt = ht[-1, :, :] # (batch_size, hidden_size)
  180. if t != obs_len - 1:
  181. H[:, t, :] = htt
  182. H = self.relu(H) # (batch_size, obs_len-1, hidden_size)
  183. x = self.linear(H)
  184. return x[:, -self.pre_len:, :]
  185. def train(model, args, scaler, device):
  186. start_time = time.time() # 计算起始时间
  187. model = model
  188. loss_function = nn.MSELoss()
  189. optimizer = torch.optim.Adam(model.parameters(), lr=0.005)
  190. epochs = args.epochs
  191. model.train() # 训练模式
  192. results_loss = []
  193. for i in tqdm(range(epochs)):
  194. losss = []
  195. for seq, labels in train_loader:
  196. optimizer.zero_grad()
  197. y_pred = model(seq)
  198. single_loss = loss_function(y_pred, labels)
  199. single_loss.backward()
  200. optimizer.step()
  201. losss.append(single_loss.detach().cpu().numpy())
  202. tqdm.write(f"\t Epoch {i + 1} / {epochs}, Loss: {sum(losss) / len(losss)}")
  203. results_loss.append(sum(losss) / len(losss))
  204. torch.save(model.state_dict(), 'save_model.pth')
  205. time.sleep(0.1)
  206. # valid_loss = valid(model, args, scaler, valid_loader)
  207. # 尚未引入学习率计划后期补上
  208. # 保存模型
  209. print(f">>>>>>>>>>>>>>>>>>>>>>模型已保存,用时:{(time.time() - start_time) / 60:.4f} min<<<<<<<<<<<<<<<<<<")
  210. plot_loss_data(results_loss)
  211. def valid(model, args, scaler, valid_loader):
  212. lstm_model = model
  213. # 加载模型进行预测
  214. lstm_model.load_state_dict(torch.load('save_model.pth'))
  215. lstm_model.eval() # 评估模式
  216. losss = []
  217. for seq, labels in valid_loader:
  218. pred = lstm_model(seq)
  219. mae = calculate_mae(pred.detach().numpy().cpu(), np.array(labels.detach().cpu())) # MAE误差计算绝对值(预测值 - 真实值)
  220. losss.append(mae)
  221. print("验证集误差MAE:", losss)
  222. return sum(losss) / len(losss)
  223. def test(model, args, test_loader, scaler):
  224. # 加载模型进行预测
  225. losss = []
  226. model = model
  227. model.load_state_dict(torch.load('save_model.pth'))
  228. model.eval() # 评估模式
  229. results = []
  230. labels = []
  231. for seq, label in test_loader:
  232. pred = model(seq)
  233. mae = calculate_mae(pred.detach().cpu().numpy(),
  234. np.array(label.detach().cpu())) # MAE误差计算绝对值(预测值 - 真实值)
  235. losss.append(mae)
  236. pred = pred[:, 0, :]
  237. label = label[:, 0, :]
  238. pred = scaler.inverse_transform(pred.detach().cpu().numpy())
  239. label = scaler.inverse_transform(label.detach().cpu().numpy())
  240. for i in range(len(pred)):
  241. results.append(pred[i][-1])
  242. labels.append(label[i][-1])
  243. print("测试集误差MAE:", losss)
  244. # 绘制历史数据
  245. plt.plot(labels, label='TrueValue')
  246. # 绘制预测数据
  247. # 注意这里预测数据的起始x坐标是历史数据的最后一个点的x坐标
  248. plt.plot(results, label='Prediction')
  249. # 添加标题和图例
  250. plt.title("test state")
  251. plt.legend()
  252. plt.show()
  253. # 检验模型拟合情况
  254. def inspect_model_fit(model, args, train_loader, scaler):
  255. model = model
  256. model.load_state_dict(torch.load('save_model.pth'))
  257. model.eval() # 评估模式
  258. results = []
  259. labels = []
  260. for seq, label in train_loader:
  261. pred = model(seq)[:, 0, :]
  262. label = label[:, 0, :]
  263. pred = scaler.inverse_transform(pred.detach().cpu().numpy())
  264. label = scaler.inverse_transform(label.detach().cpu().numpy())
  265. for i in range(len(pred)):
  266. results.append(pred[i][-1])
  267. labels.append(label[i][-1])
  268. # 绘制历史数据
  269. plt.plot(labels, label='History')
  270. # 绘制预测数据
  271. # 注意这里预测数据的起始x坐标是历史数据的最后一个点的x坐标
  272. plt.plot(results, label='Prediction')
  273. # 添加标题和图例
  274. plt.title("inspect model fit state")
  275. plt.legend()
  276. plt.show()
  277. def predict(model, args, device, scaler):
  278. # 预测未知数据的功能
  279. df = pd.read_csv(args.data_path)
  280. df = df.iloc[:, 1:][-args.window_size:].values # 转换为nadarry
  281. pre_data = scaler.transform(df)
  282. tensor_pred = torch.FloatTensor(pre_data).to(device)
  283. tensor_pred = tensor_pred.unsqueeze(0) # 单次预测 , 滚动预测功能暂未开发后期补上
  284. model = model
  285. model.load_state_dict(torch.load('save_model.pth'))
  286. model.eval() # 评估模式
  287. pred = model(tensor_pred)[0]
  288. pred = scaler.inverse_transform(pred.detach().cpu().numpy())
  289. # 假设 df 和 pred 是你的历史和预测数据
  290. # 计算历史数据的长度
  291. history_length = len(df[:, -1])
  292. # 为历史数据生成x轴坐标
  293. history_x = range(history_length)
  294. # 为预测数据生成x轴坐标
  295. # 开始于历史数据的最后一个点的x坐标
  296. prediction_x = range(history_length - 1, history_length + len(pred[:, -1]) - 1)
  297. # 绘制历史数据
  298. plt.plot(history_x, df[:, -1], label='History')
  299. # 绘制预测数据
  300. # 注意这里预测数据的起始x坐标是历史数据的最后一个点的x坐标
  301. plt.plot(prediction_x, pred[:, -1], marker='o', label='Prediction')
  302. plt.axvline(history_length - 1, color='red') # 在图像的x位置处画一条红色竖线
  303. # 添加标题和图例
  304. plt.title("History and Prediction")
  305. plt.legend()
  306. if __name__ == '__main__':
  307. parser = argparse.ArgumentParser(description='Time Series forecast')
  308. parser.add_argument('-model', type=str, default='TCN-LSTM', help="模型持续更新")
  309. parser.add_argument('-window_size', type=int, default=126, help="时间窗口大小, window_size > pre_len")
  310. parser.add_argument('-pre_len', type=int, default=24, help="预测未来数据长度")
  311. # data
  312. parser.add_argument('-shuffle', action='store_true', default=True, help="是否打乱数据加载器中的数据顺序")
  313. parser.add_argument('-data_path', type=str, default='ETTh1-Test.csv', help="你的数据数据地址")
  314. parser.add_argument('-target', type=str, default='OT', help='你需要预测的特征列,这个值会最后保存在csv文件里')
  315. parser.add_argument('-input_size', type=int, default=7, help='你的特征个数不算时间那一列')
  316. parser.add_argument('-feature', type=str, default='MS', help='[M, S, MS],多元预测多元,单元预测单元,多元预测单元')
  317. parser.add_argument('-model_dim', type=list, default=[64, 128, 256], help='这个地方是这个TCN卷积的关键部分,它代表了TCN的层数我这里输'
  318. '入list中包含三个元素那么我的TCN就是三层,这个根据你的数据复杂度来设置'
  319. '层数越多对应数据越复杂但是不要超过5层')
  320. # learning
  321. parser.add_argument('-lr', type=float, default=0.001, help="学习率")
  322. parser.add_argument('-drop_out', type=float, default=0.05, help="随机丢弃概率,防止过拟合")
  323. parser.add_argument('-epochs', type=int, default=20, help="训练轮次")
  324. parser.add_argument('-batch_size', type=int, default=16, help="批次大小")
  325. parser.add_argument('-save_path', type=str, default='models')
  326. # model
  327. parser.add_argument('-hidden_size', type=int, default=64, help="隐藏层单元数")
  328. parser.add_argument('-kernel_sizes', type=int, default=3)
  329. parser.add_argument('-laryer_num', type=int, default=1)
  330. # device
  331. parser.add_argument('-use_gpu', type=bool, default=True)
  332. parser.add_argument('-device', type=int, default=0, help="只设置最多支持单个gpu训练")
  333. # option
  334. parser.add_argument('-train', type=bool, default=True)
  335. parser.add_argument('-test', type=bool, default=True)
  336. parser.add_argument('-predict', type=bool, default=True)
  337. parser.add_argument('-inspect_fit', type=bool, default=True)
  338. parser.add_argument('-lr-scheduler', type=bool, default=True)
  339. args = parser.parse_args()
  340. if isinstance(args.device, int) and args.use_gpu:
  341. device = torch.device("cuda:" + f'{args.device}')
  342. else:
  343. device = torch.device("cpu")
  344. print("使用设备:", device)
  345. train_loader, test_loader, valid_loader, scaler = create_dataloader(args, device)
  346. if args.feature == 'MS' or args.feature == 'S':
  347. args.output_size = 1
  348. else:
  349. args.output_size = args.input_size
  350. # 实例化模型
  351. try:
  352. print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始初始化{args.model}模型<<<<<<<<<<<<<<<<<<<<<<<<<<<")
  353. model = TemporalConvNet(args.input_size, args.output_size, args.pre_len, args.model_dim, args.laryer_num,
  354. args.kernel_sizes).to(device)
  355. print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始初始化{args.model}模型成功<<<<<<<<<<<<<<<<<<<<<<<<<<<")
  356. except:
  357. print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始初始化{args.model}模型失败<<<<<<<<<<<<<<<<<<<<<<<<<<<")
  358. # 训练模型
  359. if args.train:
  360. print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始{args.model}模型训练<<<<<<<<<<<<<<<<<<<<<<<<<<<")
  361. train(model, args, scaler, device)
  362. if args.test:
  363. print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始{args.model}模型测试<<<<<<<<<<<<<<<<<<<<<<<<<<<")
  364. test(model, args, test_loader, scaler)
  365. if args.inspect_fit:
  366. print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始检验{args.model}模型拟合情况<<<<<<<<<<<<<<<<<<<<<<<<<<<")
  367. inspect_model_fit(model, args, train_loader, scaler)
  368. if args.predict:
  369. print(f">>>>>>>>>>>>>>>>>>>>>>>>>预测未来{args.pre_len}条数据<<<<<<<<<<<<<<<<<<<<<<<<<<<")
  370. predict(model, args, device, scaler)
  371. plt.show()

运行结果:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/79924
推荐阅读
相关标签
  

闽ICP备14008679号