深度学习-利用LSTM预测多输出

#存储真实值和预测值的相关信息
y_pred_df = pd.DataFrame(index=y_pred)
y_pred_df.to_csv(r'./{}/LSTM_pred.csv'.format(DIR), encoding='gbk', sep=',')
y_real_df = pd.DataFrame(index=y_real)
y_real_df.to_csv(r'./{}/LSTM_real.csv'.format(DIR), encoding='gbk', sep=',')

# 画出真实值和预测值的图像并计算相关指标
model_plot(y_real, y_pred, savename)
RMSE = np.sqrt(mean_squared_error(y_pred, y_real))
R2_SCORE = r2_score(y_pred, y_real)
print('RMSE: {}\nR2_SCORE: {}\n'.format(RMSE, R2_SCORE))

return RMSE, R2_SCORE, RMSE_normal, R2_SCORE_normal

## 7.保存模型信息和绘图
这部分没什么难点,比较简单,就是存储一下运行的结果。
```python
'''保存模型信息'''
def model_save(RMSE, R2_SCORE, RMSE_normal, R2_SCORE_normal, savename):
    with open(r'./{}/LSTM.txt'.format(DIR), 'a') as fh:
        fh.write('参数设置:\nTIME_STEP: {}\tDELAY: {}\n'.format(TIME_STEP, DELAY))
        fh.write('RMSE: {}\nR2_SCORE: {}\n'.format(RMSE, R2_SCORE))
        fh.write('RMSE_normal: {}\nR2_SCORE: {}\n\n'.format(RMSE_normal, R2_SCORE_normal))
        print('%s模型信息保存成功!\n\n\n' % savename)

'''绘图相关'''
def model_plot(y_real, y_pred, savename):
    plt.cla()
    fig1 = plt.figure(figsize=(10, 14), dpi=80)
    plt.subplots_adjust(hspace=0.3)  #hspace=0.3为子图之间的空间保留的高度,平均轴高度的一部分.加了这个语句,子图会稍变小,因为空间也占用坐标轴的一部分

    ax1 = fig1.add_subplot(1, 1, 1)  # 1行x1列的网格里第一个
    ax1.plot(y_real, '-', c='blue', label='Real', linewidth=2)
    ax1.plot(y_pred, '-', c='red', label='Predict ', linewidth=2)
    ax1.legend(loc='upper right')
    ax1.set_xlabel('min')
    ax1.set_ylabel('KWH')
    ax1.grid()

    fig1.savefig('./{}/{}.png'.format(DIR, savename))
    plt.close()

8.结果部分

里插入图片描述
测试集损失函数比训练集好很多的原因可能是因为加入了Dropout,训练时开启,丢掉一部分数据集,测试时Dropout关闭。

使用了10万行数据训练,1万测试,RMSE=0.03。

9.全部代码

import tensorflow as tf
from tensorflow_core import keras
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error, r2_score
from numpy import concatenate

pd.set_option('display.max_columns', None) #显示所有列

'''修改数据'''
SaveFile_Path = r''  # 此处加入读取和保存的文件路径
readname = ''  #读取文件的名字
savename = r''  #存储文件的名字
os.chdir(SaveFile_Path)  # 修改当前工作目录

DIR = 'LSTM'
if not os.path.exists(DIR):  #判断括号里的文件是否存在的意思,括号内的可以是文件路径。
    os.makedirs(DIR)  #用于递归创建目录

TIME_STEP = 360 #时间步长360min 6h
DELAY = 10 #预测十分钟后
BATCHSZ = 5  #一个批处理(batch)大小
EACH_EPOCH = 3  #循环次数
LR = 0.0001  #keras.optimizers.Adam中的学习率

'''
绘制初始数据曲线
'''
def plot_init():
    df = pd.read_csv(SaveFile_Path + '\\' + readname, header=0, index_col=0, encoding='gbk')
    values = df.values
    col = df.shape[1]
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    plt.figure()
    for i in range(0, col):
        plt.subplot(col, 1, i+1)
        plt.plot(values[:, i])
        plt.title(df.columns[i], y=0.5, loc='right')
    plt.show()

'''
LSTM数据准备
'''

def train_test(timestep, nextstep):   #timestep时间步长, nextstep预测多长时间
    # load dataset
    dataset = pd.read_csv(SaveFile_Path + '\\' + readname, header=0, index_col=0, encoding='gbk')
    values = dataset.values
    # ensure all data is float
    values = values.astype('float32')
    # normalize features归一化
    scaler = MinMaxScaler(feature_range=(0, 1))  # sklearn 归一化函数
    scaled = scaler.fit_transform(values)  # fit_transform(X_train) 意思是找出X_train的均值和​​​​​​​标准差,并应用在X_train上。
    # split into train and test sets  将数据集进行划分,然后将训练集和测试集划分为输入和输出变量,最终将输入(X)改造为LSTM的输入格式,即[samples,timesteps,features]
    n_train_hours = 2000 #20万行作训练集,11万作测试集
    train = scaled[:n_train_hours, :]
    test = scaled[n_train_hours:3000, :]

    train_X = []
    train_y = []
    test_X = []
    test_y = []

    # 测试集:
    # 利用for循环,遍历整个测试集,提取测试集中连续360min的特征量作为输入特征test_X,第361-370min的发电量作为标签
    for i in range(len(train)-timestep-nextstep+1):
        train_X.append(train[i:(i+timestep), :])
        btemp = train[i+timestep:i+timestep+nextstep, 0]
        b = []
        for j in range(len(btemp)):
            b.append(btemp[j])
        train_y.append(b)

    # 对训练集进行打乱
    np.random.seed(7)
    np.random.shuffle(train_X)
    np.random.seed(7)
    np.random.shuffle(train_y)
    tf.random.set_seed(7)
    # 将训练集由list格式变为array格式
    train_X = np.array(train_X, dtype=np.float32)
    train_y = np.array(train_y, dtype=np.float32)

    # 使x_train符合RNN输入要求:[送入样本数, 循环核时间展开步数, 每个时间步输入特征个数]。
    # 此处整个数据集送入,送入样本数为train_X.shape[0];输入360min数据,预测出第361min的发电量,循环核时间展开步数为360; 每个时间步送入的特征是某一min的运行数据,16个数据,故每个时间步输入特征个数为16
    train_X = np.reshape(train_X, (train_X.shape[0], 360, 16))

    # 测试集:
    # 利用for循环,遍历整个测试集,提取测试集中连续360min的特征量作为输入特征test_X,第361-370min的发电量作为标签
    for i in range(len(test)-timestep-nextstep+1):
        test_X.append(test[i:(i + timestep), :])
        btemp = test[i + timestep:i + timestep + nextstep, 0]
        b = []
        for j in range(len(btemp)):
            b.append(btemp[j])
        test_y.append(b)

    test_X, test_y = np.array(test_X, dtype=np.float32), np.array(test_y, dtype=np.float32)
    test_X = np.reshape(test_X, (test_X.shape[0], 360, 16))

    #print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)
    return train_X, train_y, test_X, test_y, scaler

'''
构造模型
'''
def model_build(train_datas): #train_datas = train_X
    # LSTM层
    model = keras.models.Sequential()
    model.add(keras.layers.LSTM(40, input_shape=(train_datas.shape[1:]), return_sequences=True, )) #, return_sequences=True 400记忆体个数
    model.add(keras.layers.Dropout(0.1))
    model.add(keras.layers.LSTM(30, return_sequences=True)) # model.add(keras.layers.Dropout(0.5))
    model.add(keras.layers.Dropout(0.1))
    model.add(keras.layers.LSTM(40, return_sequences=True))
    model.add(keras.layers.Dropout(0.1))
    model.add(keras.layers.LSTM(40))
    model.add(keras.layers.BatchNormalization())  #批标准化:对一小批数据(batch)做标准化处理(使数据符合均值为0,标准差为1分布)
    model.add(keras.layers.Dense(train_y.shape[1]))   #全连接层
#配置训练方法
    model.compile(optimizer=keras.optimizers.Adam(lr=LR, amsgrad=True), loss='mse', metrics=[rmse])  # mae: mean_absolute_error

    return model

'''模型拟合'''
def model_fit(model, train_datas, train_labels,x_test, y_test):    #train_X, train_y, test_X, test_y


    checkpoint_save_path = "./checkpoint/LSTM_stock.ckpt" #模型保存位置

    if os.path.exists(checkpoint_save_path + '.index'):
        print('-------------load the model-----------------')
        model.load_weights(checkpoint_save_path)

    lr_reduce = keras.callbacks.ReduceLROnPlateau('val_loss',     #学习停止,模型会将学习率降低2-10倍,该hui
                                                  patience=4,
                                                  factor=0.7,
                                                  min_lr=0.00001)
    best_model = keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,#保存模型
                                                 monitor='val_loss',
                                                 verbose=0,
                                                 save_best_only=True,
                                                 save_weights_only=True,
                                                 mode='min',
                                                 )

    early_stop = keras.callbacks.EarlyStopping(monitor='val_rmse', patience=15)

    history = model.fit(
        train_datas, train_labels,
        validation_data=(x_test, y_test),
        batch_size=BATCHSZ,
        epochs=EACH_EPOCH,
        verbose=2,
        callbacks=[
        best_model,
        early_stop,
        lr_reduce,
                    ]
    )

    loss = history.history['loss']
    val_loss = history.history['val_loss']
    #画出训练集和测试集的损失函数
    plt.plot(loss, label='Training Loss')
    plt.plot(val_loss, label='Validation Loss')
    plt.title('Training and Validation Loss')
    plt.legend()
    plt.savefig('./{}/{}.png'.format(DIR, '损失函数'))
    plt.close()

    return model, history

'''评价部分'''
def rmse(y_true, y_pred):  #sqrt求元素平方根  mean求张量平均值
    return keras.backend.sqrt(keras.backend.mean(keras.backend.square(y_pred - y_true), axis=-1))

def model_evaluation(model, test_X, test_y, savename):
    yhat = model.predict(test_X)
    yhat = yhat[:, -1]    #只取最后一个时刻的进行分析,1-9min的结果不是很重要
    y_normal = yhat.copy()
    y_normal[y_normal < 0] = 0  #将预测值归一化中小于0的都置0
    test_X = test_X[:, 0, :]
    test_y = test_y[:, -1]   #对应取test_y最后一个时刻

    #画出归一化后真实值和预测值的图像计算相关指标
    model_plot(test_y, y_normal, '归一化')
    RMSE_normal = np.sqrt(mean_squared_error(y_normal, test_y))
    R2_SCORE_normal = r2_score(y_normal, test_y)
    print('RMSE_normal: {}\nR2_SCORE_normal: {}\n'.format(RMSE_normal, R2_SCORE_normal))

    #返回计算归一化前
    yhat = yhat.reshape(len(yhat), 1)
    inv_yhat = concatenate((yhat, test_X[:, 1:]), axis=1)
    inv_y = scaler.inverse_transform(inv_yhat)  # 预测值转化
    y_pred = inv_y[:, 0]   #预测值由归一化值转为真实值
    y_pred[y_pred < 0] = 0  #将预测值真实值中小于0的都置0

    test_y = test_y.reshape((len(test_y), 1))
    inv_yact_hat = concatenate((test_y, test_X[:, 1:]), axis=1)
    inv_y = scaler.inverse_transform(inv_yact_hat)
    y_real = inv_y[:, 0]  # 标签由归一化值转为真实值
    '''
    在这里为什么进行比例反转,是因为我们将原始数据进行了预处理(连同输出值y),
    此时的误差损失计算是在处理之后的数据上进行的,为了计算在原始比例上的误差需要将数据进行转化。
    同时笔者有个小Tips:就是反转时的矩阵大小一定要和原来的大小(shape)完全相同,否则就会报错。
    '''

    #存储真实值和预测值的相关信息
    y_pred_df = pd.DataFrame(index=y_pred)
    y_pred_df.to_csv(r'./{}/LSTM_pred.csv'.format(DIR), encoding='gbk', sep=',')
    y_real_df = pd.DataFrame(index=y_real)
    y_real_df.to_csv(r'./{}/LSTM_real.csv'.format(DIR), encoding='gbk', sep=',')

    # 画出真实值和预测值的图像并计算相关指标
    model_plot(y_real, y_pred, savename)
    RMSE = np.sqrt(mean_squared_error(y_pred, y_real))
    R2_SCORE = r2_score(y_pred, y_real)
    print('RMSE: {}\nR2_SCORE: {}\n'.format(RMSE, R2_SCORE))

    return RMSE, R2_SCORE, RMSE_normal, R2_SCORE_normal

'''保存模型信息'''
def model_save(RMSE, R2_SCORE, RMSE_normal, R2_SCORE_normal, savename):
    with open(r'./{}/LSTM.txt'.format(DIR), 'a') as fh:
        fh.write('参数设置:\nTIME_STEP: {}\tDELAY: {}\n'.format(TIME_STEP, DELAY))
        fh.write('RMSE: {}\nR2_SCORE: {}\n'.format(RMSE, R2_SCORE))
        fh.write('RMSE_normal: {}\nR2_SCORE: {}\n\n'.format(RMSE_normal, R2_SCORE_normal))
        print('%s模型信息保存成功!\n\n\n' % savename)

'''绘图相关'''
def model_plot(y_real, y_pred, savename):
    plt.cla()
    fig1 = plt.figure(figsize=(10, 14), dpi=80)
    plt.subplots_adjust(hspace=0.3)  #hspace=0.3为子图之间的空间保留的高度,平均轴高度的一部分.加了这个语句,子图会稍变小,因为空间也占用坐标轴的一部分

    ax1 = fig1.add_subplot(1, 1, 1)  # 1行x1列的网格里第一个
    ax1.plot(y_real, '-', c='blue', label='Real', linewidth=2)
    ax1.plot(y_pred, '-', c='red', label='Predict ', linewidth=2)
    ax1.legend(loc='upper right')
    ax1.set_xlabel('min')
    ax1.set_ylabel('KWH')
    ax1.grid()

    fig1.savefig('./{}/{}.png'.format(DIR, savename))
    plt.close()

if __name__ == '__main__':
    #plot_init()  画初始数据图像
    train_X, train_y, test_X, test_y, scaler = train_test(timestep=360, nextstep=10)
    '''模型拟合'''
    model = model_build(train_X)
    model, history = model_fit(model, train_X, train_y, test_X, test_y)
    '''训练集评估'''
    RMSE_list, R2_SCORE_list, RMSE_normal_list, R2_SCORE_normal_list = model_evaluation(model, train_X, train_y, '训练')

    '''测试集评估'''
    RMSE_list, R2_SCORE_list, RMSE_normal_list, R2_SCORE_normal_list = model_evaluation(model, test_X, test_y, '验证')
    model_save(RMSE_list, R2_SCORE_list, RMSE_normal_list, R2_SCORE_normal_list, '验证')

10.总结

以上,来自刚入门的小菜鸡,如果有描述不当的地方,欢迎指正,问就是我错了。

其实写完之后再回看整个过程,发现代码本身其实并不难,但是对刚入门的新人来说,不会的东西太多了,调参都不知道该咋调,所以花了好多时间才写出来,希望这篇文章可以帮到同新入门的小白~

还有部分疑问:无法使用keras模型中的model.save(‘my_model.hdf5’)一直报错a bytes-like object is required, not ‘str’。

  • 12
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
CNN-LSTM 是一种结合了卷积神经网络(CNN)和长短期记忆网络(LSTM)的模型,广泛应用于单变量的预测问题。它能够对时间序列数据进行有效的特征提取和建模。 在使用 CNN-LSTM 进行单变量预测时,首先我们需要准备输入数据。通常,我们会将单变量的时间序列数据按照固定的时间窗口大小切分成多个子序列,每个子序列包含了一段连续的时间步。这样,我们可以将每个子序列作为模型的一个输入样本。 接下来,我们构建 CNN-LSTM 模型。模型的第一部分是卷积神经网络(CNN),它能够通过卷积层和池化层对输入的时间窗口进行特征提取。CNN 通过滑动窗口的方式对时间序列数据进行卷积运算,并通过池化层提取最重要的特征。这些特征能够捕捉到不同时间尺度下的模式。 第二部分是长短期记忆网络(LSTM),它能够在时间序列数据中捕捉到长期依赖性和时序关系。LSTM 使用了一种门控机制,能够有效地处理训练数据中的长期依赖关系,避免梯度消失或梯度爆炸问题。通过堆叠多个 LSTM 层,我们可以进一步提取更高层次的时序特征。 最后,我们将 LSTM输出连接到一个全连接层,通过该层将特征映射到预测目标的维度上。常见的预测目标可以是下一个时间步的数值,或者是二分类或多分类问题。 在 Python 中,我们可以使用 TensorFlow 或者 Keras 等深度学习框架来实现 CNN-LSTM 模型。相关的库和函数可以帮助我们构建模型、处理数据和进行预测。同时,我们还可以利用一些常用的优化算法和评估指标来优化模型的性能和评估预测的准确性。 总而言之,CNN-LSTM 是一种适用于单变量预测问题的深度学习模型,能够有效地从时间序列数据中学习并提取有用的时序特征,从而进行准确的预测

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值