LSTM中的归一化与反归一化问题、预测未来值问题

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、LSTM为什么要进行归一化,以及如何反归一化?
  • 二、单特征时序预测中的反归一化
    • 1.单特征时序数据预测导入相关包
    • 2.读入数据
    • 3.对数据进行归一化
    • 利用3sigma原则查找异常点并删除
    • 自定义划分数据集,并构造批数据的函数
    • 构建LSTM模型,并编译运行
    • 验证模型并对预测值进行反归一化,并查看预测情况
    • 根据输入预测未来一段时间内的值
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

长短时记忆(LSTM)模型作为RNN的一种,经常被应用到时序数据的预测问题中,但是实际应用问题中会遇到不少问题,本文主要是结合第十届泰迪杯B题第一题介绍LSTM中单特征预测与多特征预测中归一化与反归一化问题,以及预测未来一段时间的代码


提示:以下是本篇文章正文内容,下面案例可供参考

一、LSTM为什么要进行归一化,以及如何反归一化?

众所周知,在使用机器学习时,经常会对原始数据进行处理,常用的方法有标准化和归一化。这样做的好处有:(1)可以避免各特征与目标值的量纲不同对预测性能造成影响;(2)同时加快梯度下降的速度;(3)数据更方便模型处理。归一化虽然可以有以上优点,但是萌新使用该方法时却经常遇到问题:如何将预测得到的值还原为原始数值的大小,便于进一步分析预测实际值的性能好坏?因此本文主要对LSTM的归一化和反归一化进行介绍,方便新手对反归一化有个基本的认知,不过由于知识有限,如果存在错误,希望大家指出!!!

二、单特征时序预测中的反归一化

1.单特征时序数据预测导入相关包

代码如下(示例):

#导入相关的包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import r2_score

import tensorflow as tf
from tensorflow.keras import Sequential,layers,utils

import warnings
warnings.filterwarnings('ignore')
#保证图中文输出正常
plt.rcParams['font.sans-serif']=['SimHei'] 
plt.rcParams['axes.unicode_minus']=False 
plt.rcParams.update({'font.size':12})

2.读入数据

代码如下(示例):

#读取数据集
dataset = pd.read_csv(r'D:/AM Learning/data/泰迪杯/第十届泰迪杯_电量预测/全部数据/附件1-区域15分钟负荷数据.csv')
#描述数据特征
dataset.describe()

根据数据存储路径获取相关数据,且数据格式可以为excel、csv。


3.对数据进行归一化

本次使用的归一化方法为Min-Max方法,其具体公式为Xi^ =(Xi-Min)/(Max-Min),其中Xi^为变换后得到的数值,Max为特征X的最大值,Min为最小值。主要用到的是fit_transform函数。

#数据进行归一化,均值为0,标准差为1;
scaler = MinMaxScaler()  #确定使用的数据处理方法
dataset['总有功功率(kw)'] = scaler.fit_transform(dataset['总有功功率(kw)'].values.reshape(-1,1))
#可视化数据分布情况
dataset['总有功功率(kw)'].plot(figsize=(16,8))
plt.tick_params(labelsize=18)
font2 = {
         'weight': 'normal',
         'size': 18,
         }
plt.xlabel('数据时间',font2)
plt.ylabel('总有功功率(kw)', font2)
plt.show()

利用3sigma原则查找异常点并删除

异常值往往会对LSTM预测的性能造成影响,因此需要查找出数据中的异常值,并对数据进行处理,本文使用3sigma原则查找异常值并显示,并删除异常值。

#利用3sigma原则发现异常点
def three_sigma(df_col):#df_col:DataFrame数据的某一列
    rule = (df_col.mean() - 3 * df_col.std() > df_col) | (df_col.mean() + 3 * df_col.std() < df_col) 
    index = np.arange(df_col.shape[0])[rule]
    outrange = df_col.iloc[index]
    return outrange

index_ = three_sigma(dataset['总有功功率(kw)']).index
print('\n异常数据如下:\n')
dataset.iloc[index_]
#index_
#删除异常值
dataset=dataset.drop(index_,axis =0)

自定义划分数据集,并构造批数据的函数

#功能函数:构造特征数据集和标签集
def create_new_dataset(dataset,seq_len):#dataset:原始数据集,seq_len:序列长度(时间跨度)
    X=[]
    y=[]#构建两个空列表
    
    start = 0  #初始位置
    end = dataset.shape[0]-seq_len  #截至位置
    
    for i in range(start,end):  #for循环构造特征数据集
        sample =dataset[i:i+seq_len] #基于时间宽度seq_len创建样本
        label = dataset[i+seq_len]   #创建sample对应的标签
        X.append(sample)     #将样本保存在X中
        y.append(label)     #将标签保存在y中
     return np.array(X), np.array(y)#以数组的形式返回特征数据集和标签集

#功能函数:基于新的特征的数据集和标签集,切分为训练集和测试集
def split_dataset(X, y,train_ratio):   #X:特征数据集,y:标签数据集,train_ratio:训练集所占X的比例
    X_len = len(X)     #特征数据集的样本数量
    train_data_len = int(X_len * train_ratio)  #训练集特征数量
    
    X_train = X[:train_data_len]
    y_train = y[:train_data_len]#训练集划分
    
    X_test = X[train_data_len:]
    y_test = y[train_data_len:]#测试集划分
    
    return X_train,X_test,y_train,y_test

#功能函数:基于划分的训练集创建批数据(batch dataset)
def create_batch_data(X, y, batch_size, data_type=1):    #batch_size一个数据块里的样本数量,data_type:数据集类型(1表示测试集,2表示训练集)
    if data_type ==1: #测试集
        dataset = tf.data.Dataset.from_tensor_slices((tf.constant(X),tf.constant(y)))  #封装x和y,成为tensor类型
        test_batch_data = dataset.batch(batch_size)  #构造批数据
        return test_batch_data
    else:#训练集
        dataset = tf.data.Dataset.from_tensor_slices((tf.constant(X),tf.constant(y)))  #封装x和y,成为tensor类型
        train_batch_data = dataset.cache().shuffle(1000).batch(batch_size)   #构造批数据
        return train_batch_data
接下来才是利用上面构造的函数对原始数据进行划分
#构建特征数据集和标签集,seq_len序列长度为30
SEQ_LEN = 30
X, y = create_new_dataset(dataset_original.values, SEQ_LEN)
#数据集切分
X_train,X_test,y_train,y_test =split_dataset(X, y ,train_ratio = 0.8)
X_train.shape
#创建批数据
test_batch_dataset = create_batch_data(X_test,y_test, batch_size=32,data_type=1)
train_batch_dataset = create_batch_data(X_train,y_train, batch_size=32,data_type=2)

构建LSTM模型,并编译运行

这里需要注意的是,LTSM的层数可以自行选择,但是应该注意的是除最后一层的LSTM的return_sequences应该为False外,其余LSTM层该参数都保持为True,否则会报错

#构建LSTM模型,SEQ_LEN就是构造数据的时间序列
model = Sequential([
  layers.LSTM(64,return_sequences=True,input_shape = (SEQ_LEN,1)),
  layers.LSTM(128,return_sequences=False),
  layers.Dense(1)
])

#定义checkpoint,保存权重文件
file_path = "td_15minmodel_02.hdf5"
checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(filepath=file_path,
                                                      monitor = 'loss',
                                                      mode='min',
                                                      save_best_only=True,
                                                      save_weights_only=True)

#模型编译
from keras import optimizers
model.compile(optimizer='adam',loss='mse')  

#模型训练
history = model.fit(train_batch_dataset,
                 epochs=10,validation_data=test_batch_dataset,
                 callbacks=[checkpoint_callback])

#显示train loss和val loss
plt.figure(figsize=(16,8))
plt.plot(history.history['loss'],label='训练集误差')
plt.plot(history.history['val_loss'],label='测试集误差')
plt.tick_params(labelsize=18)
font2 = {
       'weight': 'normal',
       'size': 18,
       }
plt.xlabel('训练论数',font2)
plt.ylabel('MAE误差', font2)
plt.title("误差")
plt.legend(loc='best')
plt.show()

验证模型并对预测值进行反归一化,并查看预测情况

其实对预测值反归一化需要注意一个问题,那就是预测值的形状shape要和归一化前的数据形状相同,在多特征中也是如此,后面将会进行介绍。

#模型验证,在测试集中进行验证
test_pred = model.predict(X_test, verbose =1)

#对得到的预测值和真实值进行反归一化
original_test_pred=scaler.inverse_transform(test_pred)
original_y_test=scaler.inverse_transform(y_test)

计算各种评价参数其中包括MSE,RMSE,MAE,MAPE,r2
from sklearn import metrics
 
# MAPE需要自己实现
def mape(y_true, y_pred):
    return np.mean(np.abs((original_y_test-original_test_pred) / original_y_test))
 
y_true = np.array(original_y_test)
y_pred = np.array(original_test_pred)
 
print('MSE:',metrics.mean_squared_error(original_y_test,original_test_pred))
 
print('RMSE:',np.sqrt(metrics.mean_squared_error(original_y_test,original_test_pred)))
 
print('MAE:',metrics.mean_absolute_error(original_y_test,original_test_pred))
 
print('MAPE:',mape(original_y_test,original_test_pred))

score = r2_score(original_y_test,original_test_pred)
print("r^2的值:",score)

根据输入预测未来一段时间内的值

可以发现在测试集上的结果不错,但是在实际应用过程中的预测性能怎么样呢,我们需要根据新的输入得出未来一段时间内的预测值。此时可以新建一项用于LSTM单特征预测的程序,方便后期用来实现单特征下不同LSTM模型预测未来值。

import pandas as pd
import numpy as np
import matplotlib.pylab as plt
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import Dropout
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
from tensorflow.keras.models import load_model
data = pd.read_csv(r'D:/AM Learning/data/泰迪杯/第十届泰迪杯_电量预测/全部数据/附件1-区域15分钟负荷数据.csv')
dataf = data['总有功功率(kw)'].values#加入数据
model = load_model('C:\\Users\\D\\python基础学习\\泰迪杯\\td1_LSTM_3.h5')#载入模型,
scaler = MinMaxScaler(feature_range=(0,1))
dataf = scaler.fit_transform(dataf.reshape(-1,1))
timesteps = 30#构造x,为30个数据,表示每次用前30个数据作为一段
length = 50##未来需要预测的数据量,这个可以根据日期及间隔来人为选择
predict_xlist = []#添加预测x列表
predict_y = []#添加预测y列表
predict_xlist.extend(dataf[dataf.shape[0]-timesteps:dataf.shape[0],0].tolist())#已经存在的最后timesteps个数据添加进列表,预测新值(未来新的预测值实在目前数据的最后timesteps个数据上预测得到)
while len(predict_y) < length:
    predictx = np.array(predict_xlist[-timesteps:])#从最新的predict_xlist取出timesteps个数据,预测新的predict_steps个数据(因为每次预测的y会添加到predict_xlist列表中,为了预测将来的值,所以每次构造的x要取这个列表中最后的timesteps个数据词啊性)
    predictx = np.reshape(predictx,(1,timesteps,1))#变换格式,适应LSTM模型
    #print("predictx"),print(predictx),print(predictx.shape)
    #预测新值
    lstm_predict = model.predict(predictx)
    #predict_list.append(train_predict) #新值y添加进列表,做x
    #滚动预测
    #print("lstm_predict"),print(lstm_predict[0])
    predict_xlist.extend(lstm_predict[0])#将新预测出来的predict_steps个数据,加入predict_xlist列表,用于下次预测
    # invert
    lstm_predict = scaler.inverse_transform(lstm_predict)
    predict_y.extend(lstm_predict[0])

y_predict = pd.DataFrame(predict_y,columns=["predict"])
#y_predict.to_csv("未来一段时间的预测.csv",index=False)

总结

本文主要对LSTM用于单特征时间序列预测的归一化和反归一化,以及预测未来的值进行了介绍,其中代码参考了唐国梁老师以及不少网友的代码,如有侵权,请联系本人。最后一点后续将用于补充多特征中LSTM的归一化与反归一化问题,其用到的函数与思想与单特征的一致,关键点在于在归一化时需要把特征与目标值分开归一化,反归一化时保证使用的scaler函数为目标值的scaler即可,因此只贴出部分代码。以上内容仅为个人观点,可能存在错误,欢迎大家相互讨论

#对特征与目标值进行归一化
data_f10=feature_data10.values
data_l10=label_data10.values

scaler10 = MinMaxScaler(feature_range=(0,1))
data_f10 = scaler10.fit_transform(data_f10)

scaler_10 = MinMaxScaler(feature_range=(0,1))
data_l10 = scaler10.fit_transform(data_l10)

predict_x10=data_f10[m-19:m+1,[0,1,2,3,4]]#导入特征
predict_x10 = np.array(predict_x10)#将特征从dataframe转换为array格式
predict_x10 = np.reshape(predict_x10,(1,20,5))#将数据的格式转换为LSTM的输入格式(样本量,时间序列长度,特征量)
lstm_predict10 = model10.predict(predict_x10)#预测

lstm_predict10 = scaler_10.inverse_transform(lstm_predict10)#对得到的预测值进行反归一化
lstm_predict10#显示预测值

你可能感兴趣的:(lstm,深度学习,机器学习)