Deep Learning学习: Pytorch实现LSTM-火灾温度预测

  • 一,前言(引用)

>- **🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/Z9yL_wt7L8aPOr9Lqb1K3w) 中的学习记录博客**
>- **🍖 原作者:[K同学啊](https://mtyjkh.blog.csdn.net/)**

二,训练准备

1.训练环境
  • Framework: Pytorch, matplotlib,numpy
  • Compiler:Jupyter Lab
  • Cpu: AMD Ryzen 5600H
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

device(type='cpu')

2.数据集

5948 行 × 4 列 数据可视化如下:

使用 .iloc 方法按位置选择数据。: 表示选择所有行,1: 表示从第二列开始选择所有列(Python中的索引从0开始)。

这段代码的作用是去掉数据框 data 的第一列,创建一个新的数据框 dataFrame。

dataFrame = data.iloc[:,1:]
3.LSTM
介绍

长短期记忆神经网络(LSTM)是一种特殊的循环神经网络(RNN)。原始的RNN在训练中,随着训练时间的加长以及网络层数的增多,很容易出现梯度爆炸或者梯度消失的问题,导致无法处理较长序列数据,从而无法获取长距离数据的信息。

LSTM应用的领域包括:文本生成、机器翻译、语音识别、生成图像描述和视频标记等。

LSTM是一种特殊的RNN,主要是为了解决长序列训练过程中的梯度消失和梯度爆炸问题。

将时序数据按照可视化实现:

nn.lstm()

torch.nn.LSTM(input_sizehidden_sizenum_layers=1bias=Truebatch_first=Falsedropout=0.0bidirectional=Falseproj_size=0device=Nonedtype=None)

在时间步t时,  
h_t是隐藏状态,  
c_t是细胞状态,  
x_t是输入,  
h_(t-1)是时间t-1时的隐藏状态,或者在时间0时的初始隐藏状态,  
i_t, f_t, g_t, o_t分别是输入门、遗忘门、细胞门和输出门。  
σ是sigmoid函数,⊙是Hadamard乘积。

在多层LSTM中,第l层(l≥2)的输入x_t(l)是上一层l-1的隐藏状态h_t(l-1)乘以dropout δ_t(l-1),其中每个δ_t(l-1)是一个Bernoulli随机变量,其值为0的概率等于dropout。

如果指定proj_size > 0,将使用带有投影的LSTM。这将以如下方式改变LSTM单元:首先,h_t的维度将从hidden_size改变为proj_size(W_hi的维度将相应改变)。其次,每层的输出隐藏状态将乘以一个可学习的投影矩阵:h_t = W_hr h_t。需要注意的是,作为这一变化的结果,LSTM网络的输出形状也会有所不同。有关所有变量的确切维度,请参见下面的输入/输出部分。更多细节可以在https://arxiv.org/abs/1402.1128中找到。

参数:

- input_size – 输入x中期望的特征数量
- hidden_size – 隐藏状态h中的特征数量
- num_layers – 循环层的数量。例如,设置num_layers=2意味着将两个LSTM堆叠在一起,形成一个堆叠LSTM,第二个LSTM将接收第一个LSTM的输出并计算最终结果。默认值:1
- bias – 如果为False,则该层不使用偏置权重b_ih和b_hh。默认值:True
- batch_first – 如果为True,则输入和输出张量将以(batch, seq, feature)的形式提供,而不是(seq, batch, feature)。注意,这不适用于隐藏状态或细胞状态。详细信息请参见下面的输入/输出部分。默认值:False
- dropout – 如果不为零,则在每个LSTM层的输出上(除最后一层外)引入一个dropout层,dropout的概率等于dropout。默认值:0
- bidirectional – 如果为True,则成为双向LSTM。默认值:False
- proj_size – 如果> 0,则使用对应大小的LSTM投影。默认值:0
双向LSTM:

将LSTM设置为双向的优点和缺点:

优点:

* 更全面的上下文信息: 双向LSTM可以同时考虑序列中前面的和后面的信息,从而获得更全面的上下文信息。这对于自然语言处理任务(如情感分析、命名实体识别)尤其有用,因为这些任务往往需要考虑整个句子或段落的语义。

* 提高模型性能: 在许多任务中,双向LSTM可以显著提高模型的性能,例如提高分类准确率、降低错误率等。

* 捕捉更复杂的模式: 双向LSTM可以捕捉序列中更复杂的模式和依赖关系,例如长距离的依赖关系。

缺点:

* 计算量更大: 相比单向LSTM,双向LSTM的计算量更大,需要更多的计算资源。

* 模型复杂度更高: 双向LSTM的模型结构更加复杂,需要更多的参数进行训练,这可能导致过拟合的问题。

* 实时性较差: 由于需要同时考虑序列中的前后信息,双向LSTM在处理实时数据时可能存在一定的延迟。

总结:

将LSTM设置为双向可以显著提高模型的性能,但同时也增加了模型的复杂度和计算量。因此,是否采用双向LSTM需要根据具体的任务和数据集来进行权衡。如果任务对上下文信息的依赖性较强,并且计算资源充足,那么采用双向LSTM是一个不错的选择。

何时考虑使用双向LSTM:

* 自然语言处理任务: 如情感分析、命名实体识别、机器翻译等。

* 时间序列预测: 当需要考虑历史数据和未来数据对当前预测的影响时。

* 语音识别: 当需要考虑语音信号中的上下文信息时。

何时不考虑使用双向LSTM:

* 实时性要求较高: 如果对模型的响应速度有较高的要求,则不适合使用双向LSTM。

* 数据量较小: 如果训练数据量较小,使用双向LSTM可能会导致过拟合。

* 计算资源有限: 如果计算资源有限,无法支撑双向LSTM的训练,则需要考虑其他模型。

三,训练过程

初始化:
import torch.nn.functional as F
import numpy as np
import pandas as pd
import torch
from torch import nn
import matplotlib.pyplot as plt
import seaborn as sns
导入数据:
data = pd.read_csv("woodpine2.csv")

dataFrame = data.iloc[:,1:]

预处理:

对特定列的数据进行归一化处理,对`dataFrame`中指定的列('CO 1'、'Soot 1'、'Tem1')进行归一化处理,将这些列的值缩放到0到1之间,以便在后续处理或建模时,这些特征的数值范围保持一致。

具体步骤如下:

1. 导入库:

from sklearn.preprocessing import MinMaxScaler

 导入`MinMaxScaler`,这是一个用于将数据按最小最大值进行缩放的类。`MinMaxScaler`将数据缩放到给定的范围内(默认为0到1)。

2.复制数据:

   dataFrame = data.iloc[:,1:].copy()

 `data.iloc[:, 1:]`表示从`data`数据框中提取除第一列之外的所有列(假设第一列可能是索引或无关列)。然后,用`.copy()`方法创建一个新的数据框`dataFrame`,以免修改原始数据。

3. 初始化MinMaxScaler:
  

sc = MinMaxScaler(feature_range=(0,1))

 初始化`MinMaxScaler`,指定特征缩放的范围为0到1。这个缩放器会将数据的最小值映射为0,最大值映射为1,中间的数值按比例缩放。

4. **逐列缩放数据**:
 

 for i in ['CO 1','Soot 1','Tem1']:
       dataFrame[i] = sc.fit_transform(dataFrame[i].values.reshape(-1,1))


   这段代码对`dataFrame`中的三列数据——'CO 1'、'Soot 1'、'Tem1'——进行归一化处理。具体过程:
   - `dataFrame[i].values`提取出第`i`列的数据,以NumPy数组的形式表示。
   - `reshape(-1,1)`将数组从一维变为二维(这是因为`MinMaxScaler`需要二维数组作为输入,数据通常按行进行操作)。
   - `sc.fit_transform()`会根据每一列的数据计算最小值和最大值,并将这些数据缩放到0到1之间。
   - 缩放后的数据会替换原数据框`dataFrame`中的对应列。

5. 输出数据框形状:
  
 

 print("dataFrame",dataFrame.shape)

dataFrame (5948, 3)

然后将时间序列数据转换为模型训练所需的输入(X)和输出(y),以便能够在机器学习模型(例如神经网络)中进行训练。

width_X = 8
width_y = 1

X = []
y =[]

in_start = 0
for _,_ in data.iterrows():
    in_end = in_start + width_X
    out_end = in_end + width_y

    if out_end<len(dataFrame):
        X_ = np.array(dataFrame.iloc[in_start:in_end,])
        y_ = np.array(dataFrame.iloc[in_end:out_end,0])
        X.append(X_)
        y.append(y_)
    
    in_start = in_start+1

X = np.array(X)
y = np.array(y).reshape(-1,1,1)

print("X", X.shape, "y", y.shape)

得到 X (5939, 8, 3) y (5939, 1, 1)。前8个时间段的数据给X,后面一个给y。

检查是否有空值, 得到False:

print(np.any(np.isnan(X)))
print(np.any(np.isnan(y)))
划分数据集:
X_train = torch.tensor(np.array(X[:5000]),dtype=torch.float32)
y_train = torch.tensor(np.array(y[:5000]),dtype= torch.float32)
X_test = torch.tensor(np.array(X[:5000]),dtype=torch.float32)
y_test = torch.tensor(np.array(y[:5000]),dtype= torch.float32)
print("X_train", X_train.shape, "y_train", y_train.shape)

X_train torch.Size([5000, 8, 3]) y_train torch.Size([5000, 1, 1])

加载:
from torch.utils.data import TensorDataset,DataLoader
train_dl = DataLoader(TensorDataset(X_train,y_train),batch_size=64,shuffle=False)
test_dl = DataLoader(TensorDataset(X_test,y_test),batch_size=64,shuffle=False)
LSTM:
class model_lstm(nn.Module):
    def __init__(self):
        super(model_lstm,self).__init__()
        self.lstm0 = nn.LSTM(input_size=3,hidden_size=320,num_layers=1,batch_first=True)
        self.lstm1 = nn.LSTM(input_size=320,hidden_size=320,num_layers=1,batch_first=True)
        self.fc0 = nn.Linear(320,1)

    def forward(self,x):
        out,hidden1 = self.lstm0(x)
        out,_ = self.lstm1(out,hidden1)
        out = self.fc0(out)
        return out[:,-1:,:]  # Keeping only the last time step output
    
model = model_lstm()
  1. LSTM层

    • self.lstm0 = nn.LSTM(input_size=3, hidden_size=320, num_layers=1, batch_first=True)
      • 这是模型的第一个LSTM层,接受的输入特征维度为3,隐藏层的特征维度为320,LSTM层的层数为1。
      • batch_first=True 指定输入数据的维度顺序为 (batch_size, seq_len, input_size),也就是以batch为第一维度。
    • self.lstm1 = nn.LSTM(input_size=320, hidden_size=320, num_layers=1, batch_first=True)
      • 第二个LSTM层,接受前一个LSTM层的输出(大小为320),继续处理。
  2. 全连接层

    • self.fc0 = nn.Linear(320, 1)
      • 全连接层,将第二个LSTM层的输出从320维的特征映射到1维,用于输出最终预测结果。

前向传播 (forward method):

  1. 第一个LSTM层

    • out, hidden1 = self.lstm0(x):将输入x传入第一个LSTM层。out是LSTM层的输出,hidden1是LSTM的隐藏状态(包括h_nc_n)。
  2. 第二个LSTM层

    • out, _ = self.lstm1(out, hidden1):将第一个LSTM层的输出和其隐藏状态传递给第二个LSTM层,继续处理。
  3. 全连接层

    • out = self.fc0(out):将第二个LSTM层的输出传递给全连接层,将输出维度从320变成1,得到每个时间步的预测值。
  4. 只保留最后一个时间步的输出

    • return out[:, -1:, :]:LSTM模型通常会输出整个序列的预测,但在这段代码中,只取最后一个时间步的预测作为最终输出,这种方法常用于预测下一个时间步的值。

结构:

model_lstm(
  (lstm0): LSTM(3, 320, batch_first=True)
  (lstm1): LSTM(320, 320, batch_first=True)
  (fc0): Linear(in_features=320, out_features=1, bias=True)
)

也可用torch.summary实现:

summary(model, input_size=(8, 3))

测试模型:

output = model(torch.rand(30, 8, 3))
print(output.shape)

torch.Size([30, 1, 1])

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            LSTM-1          [-1, 8, 320]             414,720
            LSTM-2          [-1, 8, 320]             820,480
            Linear-3         [-1, 1, 1]                321
================================================================
Total params: 1,235,521
Trainable params: 1,235,521
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.06
Params size (MB): 4.71
Estimated Total Size (MB): 4.77
----------------------------------------------------------------
训练循环:
import copy
def train(train_dl, model, loss_fn, opt, lr_scheduler=None):
    size = len(train_dl.dataset)
    num_batches = len(train_dl)
    train_loss = 0
    for x, y in train_dl:
        x, y = x.to(device), y.to(device)

        pred = model(x)
        loss = loss_fn(pred, y)

        opt.zero_grad()
        loss.backward()
        opt.step()

        train_loss += loss.item()

    if lr_scheduler is not None:
        lr_scheduler.step()
        print("learning rate = {:.5f}".format(opt.param_groups[0]['lr']), end=" ")

    train_loss /= num_batches
    return train_loss

学习率调度器(可选):

如果 lr_scheduler 不为 None,则调用调度器的 step() 方法更新学习率。

测试循环:
def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss = 0

    with torch.no_grad():
        for x, y in dataloader:  
            x, y = x.to(device), y.to(device)

            y_pred = model(x)
            loss = loss_fn(y_pred, y)

            test_loss += loss.item()

    test_loss /= num_batches
    return test_loss
训练过程:
model = model_lstm()
model = model.to(device)
loss_fn = nn.MSELoss()
learn_rate = 1e-1
opt = torch.optim.SGD(model.parameters(),lr=learn_rate,weight_decay=1e-4)
epochs = 50
train_loss = []
test_loss = []
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(opt,epochs,last_epoch=-1)

for epoch in range(epochs):
                   model.train()
                   epoch_train_loss = train(train_dl,model,loss_fn,opt,lr_scheduler)

                   model.eval()
                   epoch_test_loss = test(test_dl,model,loss_fn)

                   train_loss.append(epoch_train_loss)
                   test_loss.append(epoch_test_loss)

                   template = ('Epoch:{:2d},Train_loss:{:.5f},Test_loss:{:.5f}')
                   print(template.format(epoch+1,epoch_train_loss,epoch_test_loss))

print("="*20,'Done',"="*20)

learning rate = 0.09990 Epoch: 1,Train_loss:0.00127,Test_loss:0.18627
learning rate = 0.09961 Epoch: 2,Train_loss:0.01406,Test_loss:0.17888
learning rate = 0.09911 Epoch: 3,Train_loss:0.01368,Test_loss:0.17160
learning rate = 0.09843 Epoch: 4,Train_loss:0.01326,Test_loss:0.16411
learning rate = 0.09755 Epoch: 5,Train_loss:0.01277,Test_loss:0.15613
learning rate = 0.09649 Epoch: 6,Train_loss:0.01221,Test_loss:0.14741
learning rate = 0.09524 Epoch: 7,Train_loss:0.01155,Test_loss:0.13776
learning rate = 0.09382 Epoch: 8,Train_loss:0.01078,Test_loss:0.12705
learning rate = 0.09222 Epoch: 9,Train_loss:0.00991,Test_loss:0.11526
learning rate = 0.09045 Epoch:10,Train_loss:0.00894,Test_loss:0.10248
learning rate = 0.08853 Epoch:11,Train_loss:0.00789,Test_loss:0.08898
learning rate = 0.08645 Epoch:12,Train_loss:0.00680,Test_loss:0.07519
learning rate = 0.08423 Epoch:13,Train_loss:0.00570,Test_loss:0.06165
learning rate = 0.08187 Epoch:14,Train_loss:0.00465,Test_loss:0.04895
learning rate = 0.07939 Epoch:15,Train_loss:0.00369,Test_loss:0.03759
learning rate = 0.07679 Epoch:16,Train_loss:0.00285,Test_loss:0.02794
learning rate = 0.07409 Epoch:17,Train_loss:0.00215,Test_loss:0.02013
learning rate = 0.07129 Epoch:18,Train_loss:0.00159,Test_loss:0.01411
learning rate = 0.06841 Epoch:19,Train_loss:0.00117,Test_loss:0.00966
learning rate = 0.06545 Epoch:20,Train_loss:0.00085,Test_loss:0.00650
learning rate = 0.06243 Epoch:21,Train_loss:0.00063,Test_loss:0.00432
learning rate = 0.05937 Epoch:22,Train_loss:0.00047,Test_loss:0.00286
learning rate = 0.05627 Epoch:23,Train_loss:0.00036,Test_loss:0.00190
learning rate = 0.05314 Epoch:24,Train_loss:0.00028,Test_loss:0.00128
learning rate = 0.05000 Epoch:25,Train_loss:0.00023,Test_loss:0.00088
...
learning rate = 0.00039 Epoch:48,Train_loss:0.00015,Test_loss:0.00015
learning rate = 0.00010 Epoch:49,Train_loss:0.00015,Test_loss:0.00015
learning rate = 0.00000 Epoch:50,Train_loss:0.00015,Test_loss:0.00015
==================== Done ====================

可视化:

import matplotlib.pyplot as plt

plt.figure(figsize=(5,3),dpi=120)

plt.plot(train_loss,label='LSTM Training Loss')
plt.plot(test_loss,label= 'LSTM Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()

验证示例:
predicted_y_lstm= sc.inverse_transform(model(X_test).detach().numpy().reshape(-1,1))
y_test_1 = sc.inverse_transform(y_test.reshape(-1,1))
y_test_one = [i[0] for i in y_test_1]
predicted_y_lstm_one= [i[0] for i in predicted_y_lstm]

plt.figure(figsize=(5,3), dpi=120)
plt.plot(y_test_one[:2000],color='red',label='real_temp')
plt.plot(predicted_y_lstm_one[:2000],color='blue',label='prediction')

plt.title('Title')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()

from sklearn import metrics

RMSE_lstm = metrics.mean_squared_error(predicted_y_lstm_one,y_test_1)**0.5
R2_lstm = metrics.r2_score(predicted_y_lstm_one,y_test_1)

print('均方根误差:%.5f' % RMSE_lstm)
print('R2:%.5f' % R2_lstm)

均方根误差:3.49072 R2:0.99709

四,总结

在本次实验中,通过使用 PyTorch 进行数据处理、模型构建、训练与测试,深入了解了时间序列预测任务中的 LSTM 网络模型训练过程,重点包括以下几个方面:

1. 数据预处理:
   我们首先对数据进行了归一化处理,确保模型训练时不同特征的数值范围一致。然后使用滑动窗口机制,将时间序列数据转换为模型的输入特征 `X` 和输出目标 `y`。输入数据形状为 `(batch_size, seq_len, feature_dim)`,通过多步滑动生成样本。

2. 模型构建:
   使用了两层 LSTM 叠加网络,结合全连接层实现时间序列的回归预测。通过 PyTorch 的 `nn.LSTM` 和 `nn.Linear` 实现网络结构,重点介绍了如何通过设置 `input_size`、`hidden_size` 和 `batch_first` 构建合适的 LSTM 网络。

3. 前向传播与反向传播:
   前向传播中,LSTM 网络逐步处理输入序列的每个时间步数据,最后通过全连接层得到每个序列的预测值。通过反向传播计算梯度并更新参数,使得模型逐步学习训练数据中的模式。

4. 学习率调度器:
   通过使用 `torch.optim.lr_scheduler.CosineAnnealingLR` 对学习率进行动态调整,有效改善了模型的收敛性能。学习率从初始值逐渐衰减,避免了在训练中期因学习率过大导致的训练不稳定。

5. 损失函数与优化器:
   使用均方误差损失函数 (`nn.MSELoss`) 计算预测值与真实标签之间的误差,并通过随机梯度下降 (`SGD`) 优化器进行参数更新。通过可视化训练和测试损失的变化,验证了模型的收敛性和泛化能力。

6. 模型评估与验证:
   训练结束后,我们通过可视化训练损失和验证损失,观察模型在不同训练阶段的表现。同时,模型对测试集进行了预测,并将预测结果与真实值进行对比,通过绘制对比图清晰展示模型的预测效果。

7. 总结:
   本实验通过调整 LSTM 网络的层数、隐藏单元数、学习率等超参数,提升了模型对时间序列的预测能力。在动态学习率的帮助下,训练过程更加稳定,模型性能也得到了进一步提升。实验中还演示了如何通过数据的预处理、滑动窗口机制构建时间序列样本,提升模型对时序特征的学习能力。

通过这次实验,掌握了在训练深度学习模型中的完整流程,包括从数据预处理、模型设计、训练优化到最终评估的全过程操作,深入理解了 PyTorch 在时间序列预测中的应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值