Caso prático do TensorFlow: previsão de dados de temperatura LSTM com base na entrada de vários recursos (com código Python completo e conjunto de dados)

Olá a todos, na seção anterior, apresentei a previsão de um único recurso do LSTM. Se você estiver interessado, consulte: Caso prático do TensorFlow: Usando LSTM para previsão de energia

Hoje, vou compartilhar com vocês como usar a rede neural recorrente LSTM para completar a previsão de temperatura com vários recursos. A versão completa do código e os dados podem ser obtidos no final do artigo.

1. Importe o kit de ferramentas

Eu uso GPU para acelerar a computação, amigos sem GPU podem remover o segmento de código que chama GPU.

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 调用GPU加速
gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
for gpu in gpus:
    tf.config.experimental.set_memory_growth(gpu, True)

2. Leia o conjunto de dados

O conjunto de dados é registrado a cada 10 minutos, com 42w linhas de dados e 14 colunas de feições.As primeiras 10 colunas de feições, exceto a coluna de tempo, são selecionadas para este modelo. Use o método de plotagem dos pandas para plotar recursos versus tempo.

#(1)读取数据集
filepath = 'D:/deeplearning/test/神经网络/循环神经网络/climate.csv'
data = pd.read_csv(filepath)
print(data.head())  # 数据是10min记录一次的

#(2)特征选择
# 选择从第1列开始往后的所有行的数据
feat = data.iloc[:, 1:11]  # 最后4个特征列不要
date = data.iloc[:, 0]   # 获取时间信息

#(3)利用pandas绘图展示每个特征点分布情况
feat.plot(subplots=True, figsize=(80,10),  # 为每一列单独开辟子图,设置画板大小
          layout=(5,2), title='climate features')  # 14张图的排序方式,设置标题

As informações do conjunto de dados são as seguintes

Plote os dados característicos das últimas 10 colunas, exceto a coluna DateTime característica de tempo em função do tempo

3. Pré-processamento de dados

Devido à grande quantidade de dados, todos eles usados ​​para treinamento podem causar um erro de uso insuficiente de memória.Aqui, os primeiros 2w de dados são levados para treinamento. Encontre a média e o desvio padrão de cada coluna de recurso no conjunto de treinamento e use a média e o desvio padrão do conjunto de treinamento para pré-processamento de padronização para todo o conjunto de dados . Use os dados de temperatura do ar normalizados como rótulo .

#(4)特征数据预处理
train_num = 20000  # 取前2w组数据用于训练
val_num = 23000  # 取2w-2.3w的数据用于验证
# 2.3w-2.5w的数据用于验证用于测试

# 求训练集的每个特征列的均值和标准差
feat_mean = feat[:train_num].mean(axis=0)
feat_std = feat[:train_num].std(axis=0)

# 对整个数据集计算标准差
feat = (feat - feat_mean) / feat_std

# 保存所有的气温数据,即标签数据
targets = feat.iloc[:,1]   # 取标准化之后的气温数据作为标签值

4. Funções de série temporal para extrair recursos e rótulos

Mova o conjunto de dados por meio de uma janela deslizante, por exemplo, preveja a temperatura em um ponto/segmento no futuro usando 20 linhas de 10 recursos atuais. A tarefa requer o uso de 5 dias consecutivos de dados para prever o valor da temperatura no próximo ponto de tempo, e os dados são registrados uma vez a cada 10 minutos.

Previsão para um determinado ponto de tempo: há 5*24*6=720 dados em cinco dias, a janela desliza um passo de cada vez, o primeiro intervalo da janela deslizante é range(0, 720, 1) e a 720ª temperatura é previsto. A segunda faixa de janela deslizante é range(1,721,1), e a 721ª temperatura é prevista. range() pega o valor independentemente da cabeça e cauda

Previsão para um determinado período de tempo: como o conjunto de dados é registrado uma vez a cada 10 minutos, a diferença entre as duas linhas de dados é muito pequena. Você pode definir um tamanho de etapa para obter os dados do recurso a cada 60 minutos e o intervalo da primeira janela deslizante range(0 , 720, 6) , prevê os dados de temperatura horária para o próximo dia inteiro , ou seja, range(720, 720+24*6, 6) . A segunda faixa de janela deslizante é range(1,721,6), e a faixa horária de temperatura(721, 721+24*6, 6) é prevista para o dia seguinte

Aqui está a previsão dos dados em um determinado momento. Os parâmetros são os seguintes, que podem ser modificados por você.

'''
dataset 代表特征数据
start_index 代表从数据的第几个索引值开始取
history_size 滑动窗口大小
end_index 代表数据取到哪个索引就结束
target_size 代表预测未来某一时间点还是时间段的气温。例如target_size=0代表用前20个特征预测第21个的气温
step 代表在滑动窗口中每隔多少步取一组特征
point_time 布尔类型,用来表示预测未来某一时间点的气温,还是时间段的气温
true 原始气温数据的所有标签值
'''

def TimeSeries(dataset, start_index, history_size, end_index, step,
               target_size, point_time, true):
    
    data = []  # 保存特征数据
    labels = []  # 保存特征数据对应的标签值
    
    start_index = start_index + history_size  # 第一次的取值范围[0:start_index]
    
    # 如果没有指定滑动窗口取到哪个结束,那就取到最后
    if end_index is None:
        # 数据集最后一块是用来作为标签值的,特征不能取到底
        end_index = len(dataset) - target_size
        
    # 滑动窗口的起始位置到终止位置每次移动一步
    for i in range(start_index, end_index):
        
        # 滑窗中的值不全部取出来用,每隔60min取一次
        index = range(i-history_size, i, step)  # 第一次相当于range(0, start_index, 6)
        
        # 根据索引取出所有的特征数据的指定行
        data.append(dataset.iloc[index])
        
        # 用这些特征来预测某一个时间点的值还是未来某一时间段的值
        if point_time is True:  # 预测某一个时间点
            # 预测未来哪个时间点的数据,例如[0:20]的特征数据(20取不到),来预测第20个的标签值
            labels.append(true[i+target_size])
        
        else:  # 预测未来某一时间区间
            # 例如[0:20]的特征数据(20取不到),来预测[20,20+target_size]数据区间的标签值
            labels.append(true[i:i+target_size])
    
    # 返回划分好了的时间序列特征及其对应的标签值
    return np.array(data), np.array(labels)

5. Divida o conjunto de dados

Use a função de série temporal acima para obter os valores de recurso e rótulo necessários para o treinamento. Aqui está um exemplo de previsão do valor da temperatura no próximo ponto de tempo. history_size especifica o tamanho da janela da série temporal , ou seja, quantas linhas de dados são usadas para prever o valor da temperatura em um ponto de tempo; target_size representa o valor em qual ponto de tempo no futuro , que é 0, como intervalo O recurso de (0, 720, 1) é usado para prever o valor da temperatura no ponto de tempo 720+0. Quando point_time=False, significa prever um determinado período de tempo .

#(6)划分数据集
history_size = 5*24*6  # 每个滑窗取5天的数据量=720
target_size =  0  # 预测未来下一个时间点的气温值
step = 1  # 步长为1取所有的行

# 构造训练集
x_train, y_train = TimeSeries(dataset=feat, start_index=0, history_size=history_size, end_index=train_num,
                              step=step, target_size=target_size, point_time=True, true=targets)

# 构造验证集
x_val, y_val = TimeSeries(dataset=feat, start_index=train_num, history_size=history_size, end_index=val_num,
                          step=step, target_size=target_size, point_time=True, true=targets)

# 构造测试集
x_test, y_test =  TimeSeries(dataset=feat, start_index=val_num, history_size=history_size, end_index=25000,
                              step=step, target_size=target_size, point_time=True, true=targets)

# 查看数据集信息
print('x_train_shape:', x_train.shape)  # (19280, 720, 10)
print('y_train_shape:', y_train.shape)  # (19280,)

6. Construa o conjunto de dados

Converta os valores do recurso dividido e os valores do rótulo para o tipo de tensor, embaralhe() aleatoriamente as linhas do recurso do conjunto de treinamento e treine batchsize=128 conjuntos de dados por etapa em cada iteração. Defina o iterador iter() , pegue um lote de dados do conjunto de dados next() . O valor do rótulo y_train representa um valor de temperatura do rótulo para cada 720 linhas de dados de feição na janela deslizante .

#(7)构造数据集
train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train))  # 训练集
train_ds = train_ds.batch(128).shuffle(10000)  # 随机打乱、每个step处理128组数据

val_ds = tf.data.Dataset.from_tensor_slices((x_val, y_val))  # 验证集
val_ds = val_ds.batch(128)  

test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test))  # 测试集
test_ds = test_ds.batch(128)  

# 查看数据集信息
sample = next(iter(train_ds))  # 取出一个batch的数据
print('x_train.shape:', sample[0].shape)  # [128, 720, 10]
print('y_train.shape:', sample[1].shape)  # [128, ]

7. Construção do modelo

O próximo passo é personalizar a rede LSTM. Não importa como você deseja construí-la. Deve-se notar que existe um parâmetro return_sequences na camada layers.LSTM(), que representa o último valor na saída retornada seqüência, ou todos os valores . Padrão Falso . Geralmente, return_sequences=True é usado quando a próxima camada ou LSTM é usada.

#(8)模型构建
inputs_shape = sample[0].shape[1:]  # [120,10]  不需要写batch的维度大小
inputs = keras.Input(shape=inputs_shape)  # 输入层

# LSTM层,设置l2正则化
x = layers.LSTM(units=8, dropout=0.5, return_sequences=True, kernel_regularizer=tf.keras.regularizers.l2(0.01))(inputs)
x = layers.LeakyReLU()(x)
x = layers.LSTM(units=16, dropout=0.5, return_sequences=True, kernel_regularizer=tf.keras.regularizers.l2(0.01))(inputs)
x = layers.LeakyReLU()(x)
x = layers.LSTM(units=32, dropout=0.5, kernel_regularizer=tf.keras.regularizers.l2(0.01))(x)
x = layers.LeakyReLU()(x)
# 全连接层,随即正态分布的权重初始化,l2正则化
x = layers.Dense(64,kernel_initializer='random_normal',kernel_regularizer=tf.keras.regularizers.l2(0.01))(x)
x = layers.Dropout(0.5)(x)
# 输出层返回回归计算后的未来某一时间点的气温值
outputs = layers.Dense(1)(x)  # 标签shape要和网络shape一样

# 构建模型
model = keras.Model(inputs, outputs)

# 查看网络结构
model.summary()

A estrutura da rede é a seguinte

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_3 (InputLayer)         [(None, 720, 10)]         0         
_________________________________________________________________
lstm_7 (LSTM)                (None, 720, 16)           1728      
_________________________________________________________________
leaky_re_lu_7 (LeakyReLU)    (None, 720, 16)           0         
_________________________________________________________________
lstm_8 (LSTM)                (None, 32)                6272      
_________________________________________________________________
leaky_re_lu_8 (LeakyReLU)    (None, 32)                0         
_________________________________________________________________
dense_4 (Dense)              (None, 64)                2112      
_________________________________________________________________
dropout_2 (Dropout)          (None, 64)                0         
_________________________________________________________________
dense_5 (Dense)              (None, 1)                 65        
=================================================================
Total params: 10,177
Trainable params: 10,177
Non-trainable params: 0
_________________________________________________________________

8. Treinamento de rede

Use o erro absoluto médio como a função de perda de regressão, avalie **.evaluate()** em todo o conjunto de teste após o treinamento e calcule a perda de todo o conjunto de teste.

# 网络编译
model.compile(optimizer = keras.optimizers.Adam(0.001),  # adam优化器学习率0.001
              loss = tf.keras.losses.MeanAbsoluteError())  # 计算标签和预测之间绝对差异的平均值
                          
epochs = 15  # 网络迭代次数

# 网络训练
history = model.fit(train_ds, epochs=epochs, validation_data=val_ds)

# 测试集评价
model.evaluate(test_ds)  # loss: 0.1212

O processo de treinamento é o seguinte:

Epoch 1/15
151/151 [==============================] - 11s 60ms/step - loss: 0.8529 - val_loss: 0.4423
Epoch 2/15
151/151 [==============================] - 9s 56ms/step - loss: 0.3999 - val_loss: 0.2660
------------------------------------------
------------------------------------------
Epoch 14/15
151/151 [==============================] - 9s 56ms/step - loss: 0.1879 - val_loss: 0.1442
Epoch 15/15
151/151 [==============================] - 9s 56ms/step - loss: 0.1831 - val_loss: 0.1254

9. Visualização do processo de treinamento

Todos os indicadores do processo de treinamento da rede são salvos no histórico, apenas a perda de erro média absoluta é usada aqui e a curva de mudança do indicador de perda a cada iteração é desenhada.

#(10)查看训练信息
history_dict = history.history  # 获取训练的数据字典
train_loss = history_dict['loss']  # 训练集损失
val_loss = history_dict['val_loss']  # 验证集损失

#(11)绘制训练损失和验证损失
plt.figure()
plt.plot(range(epochs), train_loss, label='train_loss')  # 训练集损失
plt.plot(range(epochs), val_loss, label='val_loss')  # 验证集损失
plt.legend()  # 显示标签
plt.xlabel('epochs')
plt.ylabel('loss')
plt.show()

10. Fase de previsão

Para tornar o desenho claro, apenas os primeiros 200 grupos de recursos no conjunto de teste** (cada grupo tem 720 linhas e 10 colunas, 720 representa um tamanho de janela deslizante, 10 representa o número de colunas de recursos) são previstos, usando a função .predict()** Obtenha o valor da temperatura prevista de cada grupo correspondente ao momento seguinte.

#(12)预测阶段
# x_test[0].shape = (720,10)
x_predict = x_test[:200]  # 用测试集的前200组特征数据来预测 
y_true = y_test[:200]  # 每组特征对应的标签值

y_predict = model.predict(x_predict)  # 对测试集的特征预测

# 绘制标准化后的气温曲线图
fig = plt.figure(figsize=(10,5))  # 画板大小
axes = fig.add_subplot(111)  # 画板上添加一张图
# 真实值, date_test是对应的时间
axes.plot(y_true, 'bo', label='actual')
# 预测值,红色散点
axes.plot(y_predict, 'ro', label='predict')
 
plt.legend()  # 注释
plt.grid()  # 网格
plt.show()

A comparação entre o valor previsto e o valor real é a seguinte

Código e dados completos

O código completo e os dados foram colocados em segundo plano, basta responder por palavra-chave

Se você deseja participar da troca técnica, a melhor maneira de comentar ao adicionar é: fonte + direção de interesse, o que é conveniente para encontrar amigos com ideias semelhantes

Método ①, Adicionar ID do WeChat: dkl88191, Observações: do CSDN +
Método de temperatura ②, número público de pesquisa do WeChat: aprendizado de Python e mineração de dados, resposta em segundo plano: temperatura

Acho que você gosta

Origin blog.csdn.net/m0_59596937/article/details/127193545
Recomendado
Clasificación