时间序列预测:CNN-BiLSTM模型实践

技术

BiLSTM是一种深度学习模型,它结合了两个方向的长短期记忆网络(LSTM),即正向和反向。它的优势主要体现在两个方面:

  • 双向信息捕捉:BiLSTM能够同时从过去和未来的数据中学习,因为它有两个方向的LSTM单元,一个用于正向序列,另一个用于反向序列,这样,模型可以更全面地捕捉到时间序列中的关联信息,提高了对序列特征的理解和表征
  • 更丰富的上下文理解:由于BiLSTM可以在两个方向上捕捉信息,它能够更好地理解当前时刻的输入与其前后上下文之间的关系,这对于许多序列任务(如自然语言处理、时间序列预测等)都非常有用,因为理解上下文是解决这些任务的关键

总的来说,BiLSTM相比于单向的LSTM,能够更全面地捕捉序列中的信息,从而提高了模型对序列数据的理解和预测能力

1. 代码实现简单流程图

1.1 开始

  • 读取数据
  • 数据预处理
  • 将数据转换为时间序列格式
  • 检查数据完整性
  • 划分训练集、验证集和测试集
  • 归一化数据
  • 划分 时间窗口

1.2 模型构建

1.2.1 BiLSTM模型

  • 双向LSTM层
  • 密集层
  • 编译模型

1.2.2 CNN-BiLSTM模型

  • 双向LSTM层
  • 重塑层
  • 卷积层
  • 池化层
  • 展平层
  • 密集层
  • 编译模型

1.3 模型训练

  • 训练BiLSTM模型并保存训练历史
  • 训练CNN-BiLSTM模型并保存训练历史

1.4 模型评估

  • 使用测试集评估BiLSTM模型
  • 使用测试集评估CNN-BiLSTM模型

1.5 未来预测

  • 使用BiLSTM模型进行未来预测
  • 使用CNN-BiLSTM模型进行未来预测

1.6 绘制结果图表

  • 绘制训练集、验证集、测试集和预测结果的时序图(BiLSTM)
  • 绘制训练集、验证集、测试集和预测结果的时序图(CNN-BiLSTM)

1.7 结束

2. 代码实现

2.1 读取数据


          
import pandas as pd
          
import matplotlib.pyplot as plt
          
import numpy as np
          
plt.rcParams['font.sans-serif'] = 'SimHei' # 设置中文显示
          
plt.rcParams['axes.unicode_minus'] = False
          

          
df = pd.read_excel('data.xlsx')
      

2.2 数据预处理

2.2.1 数据转换及缺失检测


          
df['Date'] = pd.to_datetime(df['Year'].astype(str) + '-' + df['Day'].astype(str), format='%Y-%j')
          
df.set_index('Date', inplace=True)
          
df.drop(['Year', 'Day'], axis=1, inplace=True)
          

          
# 生成时间范围
          
start_date = pd.Timestamp('1990-01-01')
          
end_date = pd.Timestamp('2023-03-01')
          
date_range = pd.date_range(start=start_date, end=end_date, freq='D')
          

          
# 检查时间范围中是否包含DataFrame中的所有日期
          
missing_dates = date_range[~date_range.isin(df.index)]
          
print("Missing Dates:")
          
print(missing_dates)
      

picture.image

代码将DataFrame中的“Year”和“Day”列合并成日期,并设置为DataFrame的索引,然后生成一个时间范围,检查该范围中是否包含了DataFrame中的所有日期,避免时间范围不完整存在缺失

2.2.2 数据划分


          
# 定义划分比例
          
train_ratio = 0.7
          
val_ratio = 0.1
          
test_ratio = 0.2
          

          
# 计算划分的索引
          
train_split = int(train_ratio * len(df))
          
val_split = int((train_ratio + val_ratio) * len(df))
          

          
# 划分数据集
          
train_set = df.iloc[:train_split]
          
val_set = df.iloc[train_split:val_split]
          
test_set = df.iloc[val_split:]
          

          
plt.figure(figsize=(15, 10))
          
plt.subplot(3,1,1)
          
plt.plot(train_set, color='g',  alpha=0.3)
          
plt.title('train Temperature时序图')
          

          
plt.subplot(3,1,2)
          
plt.plot(val_set, color='b',  alpha=0.3)
          
plt.title('val Temperature时序图')
          

          
plt.subplot(3,1,3)
          
plt.plot(test_set, color='r',  alpha=0.3)
          
plt.title('test Temperature时序图')
          
plt.xticks(rotation=45)
          
plt.show()
      

picture.image

数据集按照指定的比例划分为训练集、验证集和测试集,并绘制它们的时序图,训练集用于训练模型,验证集用于调整模型超参数和评估性能,测试集用于评估模型在未知数据上的性能

2.2.3 归一化数据


          
from sklearn.preprocessing import MinMaxScaler
          

          
def normalize_dataframe(train_set, val_set, test_set):
          
    scaler = MinMaxScaler()
          
    scaler.fit(train_set)  # 在训练集上拟合归一化模型
          
    
          
    train = pd.DataFrame(scaler.transform(train_set), columns=train_set.columns, index = train_set.index)
          
    val = pd.DataFrame(scaler.transform(val_set), columns=val_set.columns, index = val_set.index)
          
    test = pd.DataFrame(scaler.transform(test_set), columns=test_set.columns, index = test_set.index)
          
    return train, val, test
          

          
train, val, test = normalize_dataframe(train_set, val_set, test_set)
          

          
plt.figure(figsize=(15, 10))
          
plt.subplot(3,1,1)
          
plt.plot(train, color='g',  alpha=0.3)
          
plt.title('train Temperature归一化时序图')
          

          
plt.subplot(3,1,2)
          
plt.plot(val, color='b',  alpha=0.3)
          
plt.title('val Temperature归一化时序图')
          

          
plt.subplot(3,1,3)
          
plt.plot(test, color='r',  alpha=0.3)
          
plt.title('test Temperature归一化时序图')
          
plt.xticks(rotation=45)
          
plt.show()
      

picture.image

将训练集、验证集和测试集进行归一化,并绘制归一化后的时 序图,归一化参考往期文章特征工程——数据转换 这里 归一化 采用 训练集 统计指标避免出现数据泄露

2.2.4 时间窗口划分


          
def prepare_data(data, win_size):
          
    X = []  
          
    y = []  
          

          
    for i in range(len(data) - win_size):
          
        temp_x = data[i:i + win_size] 
          
        temp_y = data[i + win_size]    
          
        X.append(temp_x)
          
        y.append(temp_y)
          

          
    X = np.asarray(X)
          
    y = np.asarray(y)
          
    X = np.expand_dims(X, axis=-1)
          
    return X, y
          

          
win_size = 30
          

          
# 训练集
          
X_train, y_train= prepare_data(train['Temperature'].values, win_size)
          

          
# 验证集
          
X_val, y_val= prepare_data(val['Temperature'].values, win_size)
          

          
# 测试集
          
X_test, y_test = prepare_data(test['Temperature'].values, win_size)
          

          
print("训练集形状:", X_train.shape, y_train.shape)
          
print("验证集形状:", X_val.shape, y_val.shape)
          
print("测试集形状:", X_test.shape, y_test.shape)
      

picture.image

这里的划分为单特征单步预测时间窗口为30,时间窗口划分方法参考往期文章时间窗口划分:时序预测模型的多种形式解析

2.3 BiLSTM模型构建

2.3.1

BiLSTM模型编译训练


          
from tensorflow.keras.models import Sequential
          
from tensorflow.keras.layers import LSTM, Bidirectional, Dense
          

          
model_bilstm = Sequential()
          
model_bilstm.add(Bidirectional(LSTM(128, activation='relu'), input_shape=(X_train.shape[1], X_train.shape[2])))
          
model_bilstm.add(Dense(64, activation='relu'))
          
model_bilstm.add(Dense(32, activation='relu'))
          
model_bilstm.add(Dense(16, activation='relu'))
          
model_bilstm.add(Dense(1))
          

          
model_bilstm.compile(optimizer='adam', loss='mse')
          
history = model_bilstm.fit(X_train, y_train, epochs=50, batch_size=32, validation_data=(X_val, y_val))
          

          
plt.figure()
          
plt.plot(history.history['loss'], c='b', label = 'loss')
          
plt.plot(history.history['val_loss'], c='g', label = 'val_loss')
          
plt.legend()
          
plt.show()
      

picture.image


        
            

          model\_bilstm.summary()
        
      

picture.image

2.3.2

BiLSTM模型评价


          
from sklearn import metrics
          
y_pred = model_bilstm.predict(X_test)
          
# 计算均方误差(MSE)
          
mse = metrics.mean_squared_error(y_test, np.array([i for arr in y_pred for i in arr]))
          
# 计算均方根误差(RMSE)
          
rmse = np.sqrt(mse)
          
# 计算平均绝对误差(MAE)
          
mae = metrics.mean_absolute_error(y_test, np.array([i for arr in y_pred for i in arr]))
          
from sklearn.metrics import r2_score # 拟合优度
          
r2 = r2_score(y_test, np.array([i for arr in y_pred for i in arr]))
          
print("均方误差 (MSE):", mse)
          
print("均方根误差 (RMSE):", rmse)
          
print("平均绝对误差 (MAE):", mae)
          
print("拟合优度:", r2)
      

picture.image

2.3.3

BiLSTM模型向后预测及可视化


          
# 取出预测的最后一个时间步的输出作为下一步的输入
          
last_output = model_bilstm.predict(X_test)[-1]
          
# 预测的时间步数
          
steps = 10  # 假设向后预测10个时间步
          
predicted = []
          
for i in range(steps):
          
    # 将最后一个输出加入X_test,继续向后预测
          
    input_data = np.append(X_test[-1][1:], last_output).reshape(1, X_test.shape[1], X_test.shape[2])
          
    # 使用模型进行预测
          
    next_output = model_bilstm.predict(input_data)
          
    # 将预测的值加入结果列表
          
    predicted.append(next_output[0][0])
          
    last_output = next_output[0]
          

          
# 反归一化    
          
df_max = np.max(train_set)
          
df_min = np.min(train_set)
          

          
series_1 = np.array(predicted)*(df_max-df_min)+df_min
          

          
plt.figure(figsize=(15,4), dpi =300)
          
plt.subplot(3,1,1)
          
plt.plot(train_set, color = 'c', label = '训练集')
          
plt.plot(val_set, color = 'r', label = '验证集')
          
plt.plot(test_set, color = 'b', label = '测试集')
          
plt.plot(pd.date_range(start='2016-08-12', end='2023-03-01', freq='D')
          
         ,y_pred*(df_max-df_min)+df_min, color = 'y', label = '测试集预测')
          

          
plt.plot(pd.date_range(start='2023-03-02', end='2023-03-11', freq='D')
          
         ,series_1, color = 'magenta',linestyle='-.', label = '未来预测')
          
plt.legend()
          
plt.subplot(3,1,2)
          
plt.plot(test_set, color = 'b', label = '测试集')
          
plt.plot(pd.date_range(start='2016-08-12', end='2023-03-01', freq='D')
          
         ,y_pred*(df_max-df_min)+df_min, color = 'y', label = '测试集预测')
          

          
plt.plot(pd.date_range(start='2023-03-02', end='2023-03-11', freq='D')
          
         ,series_1, color = 'magenta', linestyle='-.',label = '未来预测')
          
plt.legend()
          

          
plt.subplot(3,1,3)
          
plt.plot(test_set, color = 'b', label = '测试集')
          
plt.plot(pd.date_range(start='2016-08-12', end='2023-03-01', freq='D')
          
         ,y_pred*(df_max-df_min)+df_min, color = 'y', label = '测试集预测')
          

          
plt.plot(pd.date_range(start='2023-03-02', end='2023-03-11', freq='D')
          
         ,series_1, color = 'magenta',linestyle='-.', label = '未来预测')
          
# 设置x轴范围为2022年到未来预测的结束日期
          
plt.xlim(pd.Timestamp('2022-01-01'), pd.Timestamp('2023-03-11'))
          
plt.legend()
          
plt.show()
      

picture.image

2.4 CNN-BiLSTM模型构建

2.4.1

CNN- BiLSTM模型编译训练


          
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Reshape, Flatten
          
model_cnn_bilstm = Sequential()
          
model_cnn_bilstm.add(Bidirectional(LSTM(128, activation='relu'), input_shape=(X_train.shape[1], X_train.shape[2])))
          
# 添加Reshape层将LSTM的输出转换为3维
          
model_cnn_bilstm.add(Reshape((256, 1)))
          
model_cnn_bilstm.add(Conv1D(filters=64, kernel_size=7, activation='relu'))
          
model_cnn_bilstm.add(MaxPooling1D(pool_size=2))
          
model_cnn_bilstm.add(Flatten()) # 将池化后的输出展平成一维向量
          
model_cnn_bilstm.add(Dense(32, activation='relu'))
          
model_cnn_bilstm.add(Dense(16, activation='relu'))
          
model_cnn_bilstm.add(Dense(1))
          

          
model_cnn_bilstm.compile(optimizer='adam', loss='mse')
          
history = model_cnn_bilstm.fit(X_train, y_train, epochs=50, batch_size=32, validation_data=(X_val, y_val))
          

          
plt.figure()
          
plt.plot(history.history['loss'], c='b', label = 'loss')
          
plt.plot(history.history['val_loss'], c='g', label = 'val_loss')
          
plt.legend()
          
plt.show()
      

picture.image


        
            

          model\_cnn\_bilstm.summary()
        
      

picture.image

2.4.2 CNN-BiLSTM模型评价


          
y_pred = model_cnn_bilstm.predict(X_test)
          
mse = metrics.mean_squared_error(y_test, np.array([i for arr in y_pred for i in arr]))
          
rmse = np.sqrt(mse)
          
mae = metrics.mean_absolute_error(y_test, np.array([i for arr in y_pred for i in arr]))
          
from sklearn.metrics import r2_score 
          
r2 = r2_score(y_test, np.array([i for arr in y_pred for i in arr]))
          
print("均方误差 (MSE):", mse)
          
print("均方根误差 (RMSE):", rmse)
          
print("平均绝对误差 (MAE):", mae)
          
print("拟合优度:", r2)
      

picture.image

2.4.3

CNN- BiLSTM模型向后预测及可视化


          
last_output = model_cnn_bilstm.predict(X_test)[-1]
          
steps = 10  
          
predicted = []
          
for i in range(steps):
          
    input_data = np.append(X_test[-1][1:], last_output).reshape(1, X_test.shape[1], X_test.shape[2])
          
    next_output = model_cnn_bilstm.predict(input_data)
          
    predicted.append(next_output[0][0])
          
    last_output = next_output[0]
          
    
          
series_2 = np.array(predicted)*(df_max-df_min)+df_min
          

          
plt.figure(figsize=(15,4), dpi =300)
          
plt.subplot(3,1,1)
          
plt.plot(train_set, color = 'c', label = '训练集')
          
plt.plot(val_set, color = 'r', label = '验证集')
          
plt.plot(test_set, color = 'b', label = '测试集')
          
plt.plot(pd.date_range(start='2016-08-12', end='2023-03-01', freq='D')
          
         ,y_pred*(df_max-df_min)+df_min, color = 'y', label = '测试集预测')
          

          
plt.plot(pd.date_range(start='2023-03-02', end='2023-03-11', freq='D')
          
         ,series_2, color = 'magenta',linestyle='-.', label = '未来预测')
          
plt.legend()
          
plt.subplot(3,1,2)
          
plt.plot(test_set, color = 'b', label = '测试集')
          
plt.plot(pd.date_range(start='2016-08-12', end='2023-03-01', freq='D')
          
         ,y_pred*(df_max-df_min)+df_min, color = 'y', label = '测试集预测')
          

          
plt.plot(pd.date_range(start='2023-03-02', end='2023-03-11', freq='D')
          
         ,series_2, color = 'magenta',linestyle='-.', label = '未来预测')
          
plt.legend()
          

          
plt.subplot(3,1,3)
          
plt.plot(test_set, color = 'b', label = '测试集')
          
plt.plot(pd.date_range(start='2016-08-12', end='2023-03-01', freq='D')
          
         ,y_pred*(df_max-df_min)+df_min, color = 'y', label = '测试集预测')
          

          
plt.plot(pd.date_range(start='2023-03-02', end='2023-03-11', freq='D')
          
         ,series_2, color = 'magenta',linestyle='-.', label = '未来预测')
          
# 设置x轴范围为2022年到未来预测的结束日期
          
plt.xlim(pd.Timestamp('2022-01-01'), pd.Timestamp('2023-03-11'))
          
plt.legend()
          
plt.show()
      

picture.image

3. 往期推荐

基于LSTM模型的多输入多输出单步时间序列预测

使用LSTM模型预测多特征变量的时间序列

TCN时间序列卷积神经网络

基于VMD分解的VMD-CNN-LSTM时间序列预测模型实现

基于VMD分解的VMD-LSTM时间序列预测模型实现,大力提升预测精度!

ARIMA模型进阶——具有季节性的SARIMAX模型实现

时间序列——移动平均(SMA、EMA、WMA)

时间序列——平稳性检验

时间序列——季节性检验

长短期记忆网络LSTM在时序数据预测演示

如果你对类似于这样的文章感兴趣。

欢迎关注、点赞、转发~

0
0
0
0
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动客户端性能优化最佳实践
在用户日益增长、需求不断迭代的背景下,如何保证 APP 发布的稳定性和用户良好的使用体验?本次分享将结合字节跳动内部应用的实践案例,介绍应用性能优化的更多方向,以及 APM 团队对应用性能监控建设的探索和思考。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论