VMD-CNN-BiLSTM-Attention预测模型——电力负荷预测模型实例&代码解析

  想解决电力负荷预测中的非线性、非平稳难题?这篇博客介绍了一种创新混合模型,结合变分模态分解(VMD)CNN-BiLSTM-Attention深度学习架构,实现超高精度预测!

✨ 核心亮点
✅ VMD分解:有效分离复杂负荷信号,提取关键频率特征
✅ 混合深度学习:CNN捕捉局部模式 + BiLSTM学习长时序依赖 + 注意力机制聚焦关键时段
✅ 端到端优化:从数据预处理(滑动窗口、归一化)到模型训练(MSE损失+Adam优化),完整流程公开!
✅ 工业级评估:提供MSE、MAE、MAPE、R²多指标验证,预测误差显著低于传统方法

📊 适用场景:电网调度、能源管理、工业用电预测等,尤其适合非平稳时序数据

结尾附完整代码!!


    本博客示例代码实现了一个基于VMD(Variational Mode Decomposition,变分模态分解)、CNN(卷积神经网络)、BiLSTM(双向长短期记忆网络)和Attention(注意力机制)的电力负荷预测模型。

目录

导入库 

数据准备和预处理

预测指标计算 

结果图绘制 

完整代码 


导入库 ——

import os 
import math 
import pandas as pd 
import openpyxl

from math import sqrt 
from numpy import concatenate 
import matplotlib.pyplot as plt 
import numpy as np

from sklearn.preprocessing import MinMaxScaler 
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import LabelEncoder 
from sklearn.metrics import mean_squared_error 
from tensorflow.keras.layers import * 
from tensorflow.keras.models import *  
from sklearn.metrics import mean_squared_error, mean_absolute_error,r2_score 
from pandas import DataFrame  
from pandas import concat 
import keras.backend as K
from scipy.io import savemat, loadmat 
from sklearn.neural_network import MLPRegressor  
from keras.callbacks import LearningRateScheduler  
from tensorflow.keras import Input, Model, Sequential  
import mplcyberpunk
from qbstyles import mpl_style
import warnings
from prettytable import PrettyTable #可以优美的打印表格结果
from keras.layers import Dense, Activation, Dropout, LSTM, Bidirectional, LayerNormalization, Input
from tensorflow.keras.models import Model

warnings.filterwarnings("ignore") 

1. 基础工具库

库名作用
os操作系统接口(如文件路径管理、环境变量读取)
math提供数学函数(如sqrt),用于计算平方根等。
warnings控制警告信息的显示(ignore表示忽略所有警告)。

2. 数据处理与分析

库名作用
pandas (pd)数据处理核心库,用于读取CSV/Excel(read_csvread_excel)、操作DataFrame。
numpy (np)数值计算库,提供数组操作(如reshapeconcatenate)和数学函数。
scipy.io读写MATLAB文件(savematloadmat

3. 机器学习与深度学习

库名/模块作用
sklearn.preprocessing数据预处理:
MinMaxScaler:归一化到[0,1]
StandardScaler:标准化
LabelEncoder:标签编码
sklearn.metrics评估指标:
mean_squared_error(MSE)
mean_absolute_error(MAE)
r2_score(R²)。
sklearn.neural_networkMLPRegressor(多层感知机回归模型)
tensorflow.keras深度学习模型构建:
InputModelSequential:模型结构定义
LSTMConv1DDense等:神经网络层
LearningRateScheduler:动态学习率调整
keras.backend (K)提供底层张量操作(如自定义损失函数)

4. 可视化

库名作用
matplotlib.pyplot (plt)绘图库,用于绘制预测结果对比图。
mplcyberpunk为Matplotlib添加赛博朋克风格(代码中未实际生效,因样式被后续rcParams覆盖)。
qbstyles (mpl_style)快速设置Matplotlib样式

5. 其他工具

库名作用
prettytable生成美观的表格(用于打印评估指标结果)。
openpyxl读写Excel文件(被pandas间接调用,用于读取.xlsx文件)。

关键库的协同作用

  1. 数据流

    • pandas读取数据 → numpy数组转换 → sklearn预处理 → tensorflow.keras建模 → matplotlib可视化。

  2. 模型结构

    • 结合Conv1D(提取局部特征)、BiLSTM(捕捉时序依赖)、Attention(动态权重分配)构建混合模型。

  3. 评估与展示

    • sklearn.metrics计算误差 → prettytable格式化输出 → matplotlib绘制预测曲线。


数据准备和预处理 ——

  • 数据读取:从CSV文件读取电力负荷数据,从Excel文件读取VMD分解后的数据。

  • 数据整合:将原始数据与VMD分解后的各模态分量(IMF)进行组合。

  • 数据划分:将数据划分为训练集(85%)和测试集(15%)。

  • 归一化处理:使用MinMaxScaler对训练集和测试集进行归一化处理。

数据集格式

dataset=pd.read_csv("电力负荷预测数据2.csv",encoding='gb2312')
print(dataset)#显示dataset数据

dataset_vmd = pd.read_excel("VMD.xlsx")
values_vmd = dataset_vmd.values
values_vmd = values_vmd.astype('float32')

values = dataset.values[:,1:] #只取第2列数据,要写成1:2;只取第3列数据,要写成2:3,取第2列之后(包含第二列)的所有数据,写成 1:

values = values.astype('float32')

def data_collation(data, n_in, n_out, or_dim, scroll_window, num_samples):
    res = np.zeros((num_samples,n_in*or_dim+n_out))
    for i in range(0, num_samples):
        h1 = data[scroll_window*i: n_in+scroll_window*i,0:or_dim]
        h2 = h1.reshape( 1, n_in*or_dim)
        h3 = data[n_in+scroll_window*(i) : n_in+scroll_window*(i)+n_out,-1].T
        h4 = h3[np.newaxis, :]
        h5 = np.hstack((h2,h4))
        res[i,:] = h5
    return res


def attention_layer(inputs, time_steps):
    # 自定义的注意力机制层
    a = Permute((2, 1))(inputs)  # 将特征维度和时间步维度互换
    a = Dense(time_steps, activation='softmax')(a)  # 使用Dense层计算时间步的权重
    a_probs = Permute((2, 1), name='attention_vec')(a)  # 再次互换维度,使其与原始输入对齐
    output_attention_mul = Multiply()([inputs, a_probs])  # 将注意力权重应用到输入上
    return output_attention_mul

def cnn_bilstm_attention_model():
    # 定义一个包含CNN, LSTM和注意力机制的模型
    inputs = Input(shape=(vp_train.shape[1], vp_train.shape[2]))
    conv1d = Conv1D(filters=64, kernel_size=2, activation='relu')(inputs)
    maxpooling = MaxPooling1D(pool_size=2)(conv1d)
    reshaped = Reshape((-1, 64 * maxpooling.shape[1]))(maxpooling)  # 重塑形状以适应LSTM层
    lstm_out = Bidirectional(LSTM(128, return_sequences=True))(reshaped)  # LSTM层
    attention_out = attention_layer(lstm_out, time_steps=reshaped.shape[1])  # 注意力机制层
    attention_flatten = Flatten()(attention_out)  # 展平输出以适应全连接层
    outputs = Dense(vt_train.shape[1])(attention_flatten)  # 全连接层

    model = Model(inputs=inputs, outputs=outputs)
    model.compile(loss='mse', optimizer='Adam')
    model.summary()

    return model


#定义输入和输出
n_in = 5  # 输入前5行的数据
n_out = 1  # 预测未来1步的数据
or_dim = values.shape[1]        # 记录特征数据维度
num_samples = 2000  # 可以设定从数据中取出多少个点用于本次网络的训练与测试。
scroll_window = 1  #如果等于1,下一个数据从第二行开始取。如果等于2,下一个数据从第三行开始取
n_train_number = int(num_samples * 0.85)  #取出85%作为训练集,剩余的为测试集
n_test_number = num_samples - n_train_number   #测试集数量
predicted_data =[]
actual_data = []
# In[7]:
for vmd_num in  range (0,len(values_vmd[0])):
    imf = values_vmd[:,vmd_num]
    imf = imf.reshape(-1,1)
    combined_data = np.hstack((values[:,0:-1],imf)) # 把VMD分解的每列数据与原始数据特征列组合到一起

    res = data_collation(combined_data, n_in, n_out, or_dim, scroll_window, num_samples)
    # 把数据集分为训练集和测试集
    combined_data = np.array(res)
    # 将前面处理好的DataFrame(combined_data)转换成numpy数组,方便后续的数据操作。

    n_train_number = int(num_samples * 0.85)
   
    Xtrain = combined_data[:n_train_number, :n_in*or_dim]
    Ytrain = combined_data[:n_train_number, n_in*or_dim:]


    Xtest = combined_data[n_train_number:, :n_in*or_dim]
    Ytest = combined_data[n_train_number:,  n_in*or_dim:]

    # 归一化
    m_in = MinMaxScaler()
    vp_train = m_in.fit_transform(Xtrain)  # 注意fit_transform() 和 transform()的区别
    vp_test = m_in.transform(Xtest)  # 注意fit_transform() 和 transform()的区别

    m_out = MinMaxScaler()
    vt_train = m_out.fit_transform(Ytrain)  # 注意fit_transform() 和 transform()的区别
    vt_test = m_out.transform(Ytest)  # 注意fit_transform() 和 transform()的区别

    vp_train = vp_train.reshape((vp_train.shape[0], n_in, or_dim))

    vp_test = vp_test.reshape((vp_test.shape[0], n_in, or_dim))


    model = cnn_bilstm_attention_model()

    model.fit(vp_train, vt_train, batch_size=32, epochs=50, validation_split=0.25, verbose=2)

    yhat = model.predict(vp_test)

    yhat = yhat.reshape(num_samples-n_train_number, n_out)

    yy = m_out.inverse_transform(yhat) # 反归一化

    predicted_data.append(yy)
    actual_data.append(Ytest)

1. data_collation(data, n_in, n_out, or_dim, scroll_window, num_samples)

作用

将原始时间序列数据转换为适合监督学习的滑动窗口格式,支持多输入多输出(MIMO)的时序预测任务。

功能流程
  1. 输入参数

    • data: 原始数据(NumPy数组)。

    • n_in: 输入时间步数(历史窗口大小)。

    • n_out: 输出时间步数(预测步长)。

    • or_dim: 原始数据的特征维度(列数)。

    • scroll_window: 滑动窗口步长(控制样本重叠程度)。

    • num_samples: 生成的样本总数。

  2. 核心操作

    • 初始化结果数组 res,形状为 (num_samples, n_in*or_dim + n_out)

    • 循环生成每个样本:

      • 历史数据:取 scroll_window*i 到 n_in+scroll_window*i 行的前 or_dim 列特征,展平为一行。

      • 未来数据:取未来 n_out 步的最后一列(目标值),转置后与历史数据拼接。

    • 返回整理后的数据集。

关键库函数
  • numpy.zeros: 初始化结果数组。

  • numpy.reshape: 调整数据形状。

  • numpy.hstack: 水平拼接特征和目标值。

整体作用

将时间序列数据转换为监督学习格式,例如:
输入:[t-4, t-3, t-2, t-1, t] 的特征 → 输出:[t+1] 的目标值。


2. attention_layer(inputs, time_steps)

作用

实现注意力机制层,动态分配不同时间步的权重,增强模型对关键时间步的关注。

功能流程
  1. 输入参数

    • inputs: 输入张量(通常是LSTM层的输出)。

    • time_steps: 时间步数。

  2. 核心操作

    • 维度置换:通过 Permute 交换特征和时间步维度。

    • 权重计算:用 Dense 层和 softmax 生成时间步权重。

    • 权重应用:将权重与原输入相乘,得到加权后的输出。

关键库函数
  • tensorflow.keras.layers.Permute: 调整张量维度顺序。

  • tensorflow.keras.layers.Dense: 全连接层计算注意力权重。

  • tensorflow.keras.layers.Multiply: 对输入应用注意力权重。

整体作用

提升模型对重要时间步的敏感性,尤其在长序列中避免信息稀释。


3. cnn_bilstm_attention_model()

作用

构建结合CNN、双向LSTM和注意力机制的混合神经网络模型。

功能流程
  1. 输入层:接收形状为 (n_in, or_dim) 的输入。

  2. CNN层

    • Conv1D: 提取局部时序特征(64个滤波器,核大小2)。

    • MaxPooling1D: 降维(池化大小2)。

  3. BiLSTM层

    • Bidirectional(LSTM): 双向LSTM捕捉前后时序依赖(128个单元)。

  4. 注意力层:调用 attention_layer 动态加权时间步。

  5. 输出层Flatten + Dense 输出预测值。

关键库函数
  • tensorflow.keras.layers.Conv1D/MaxPooling1D: CNN特征提取。

  • tensorflow.keras.layers.Bidirectional: 双向LSTM。

  • tensorflow.keras.Model: 构建模型。

整体作用

通过混合架构同时捕捉局部特征(CNN)、长期依赖(BiLSTM)和关键时间点(Attention),提升预测精度。


4. 主流程中的关键操作

数据预处理
  • VMD数据整合

    combined_data = np.hstack((values[:,0:-1], imf))  # 合并原始特征与VMD分量
    • 使用 numpy.hstack 横向拼接数据。

  • 归一化

    m_in.fit_transform(Xtrain)  # 训练集归一化
    m_out.transform(Xtest)     # 测试集归一化
    • sklearn.preprocessing.MinMaxScaler: 将数据缩放到 [0, 1]。

模型训练与预测
  • 训练

    model.fit(vp_train, vt_train, batch_size=32, epochs=50)
    • tensorflow.keras.Model.fit: 使用Adam优化器最小化MSE损失。

  • 预测与反归一化

    yy = m_out.inverse_transform(yhat)  # 将预测值还原为原始量纲
    • sklearn.preprocessing.MinMaxScaler.inverse_transform: 反向缩放数据。


总结

函数/操作作用关键库依赖
data_collation时间序列转监督学习格式numpy(数组操作)
attention_layer动态加权关键时间步tensorflow.keras.layers
cnn_bilstm_attention_model构建混合预测模型(CNN+BiLSTM+Attention)tensorflow.keras
主流程数据整合、归一化、训练、预测sklearnnumpytensorflow

通过组合这些函数,代码实现了:

  1. 数据准备(滑动窗口 + VMD分解)

  2. 混合建模(CNN局部特征 + BiLSTM时序依赖 + Attention关键点聚焦)

  3. 端到端预测(归一化→训练→反归一化→评估)。


预测指标计算 ——

pre_test = []

for i in range(0, len(predicted_data[0])):
    sum = 0
    for j in range(0, len(predicted_data)):
        sum = sum + predicted_data[j][i]
    pre_test.append(sum)

pre_test = np.array(pre_test)

res = data_collation(values, n_in, n_out, or_dim, scroll_window, num_samples)
values = np.array(res)
actual_test = values[n_train_number:,  n_in*or_dim:]
actual_test = actual_test.reshape(num_samples-n_train_number, n_out)

def mape(y_true, y_pred):
    # 定义一个计算平均绝对百分比误差(MAPE)的函数。
    record = []
    for index in range(len(y_true)):
        # 遍历
        temp_mape = np.abs((y_pred[index] - y_true[index]) / y_true[index])
        # 计算MAPE
        record.append(temp_mape)
        # 将MAPE添加到记录列表中。
    return np.mean(record) * 100
    # 返回所有记录的平均值,乘以100得到百分比。

def evaluate_forecasts(Ytest, predicted_data, n_out):
    mse_dic = []
    rmse_dic = []
    mae_dic = []
    mape_dic = []
    r2_dic = []
    table = PrettyTable(['测试集指标','MSE', 'RMSE', 'MAE', 'MAPE','R2'])
    for i in range(n_out):
        actual = [float(row[i]) for row in Ytest]  #一列列提取
        
        predicted = [float(row[i]) for row in predicted_data]
        mse = mean_squared_error(actual, predicted)

        mse_dic.append(mse)
        rmse = sqrt(mean_squared_error(actual, predicted))

        rmse_dic.append(rmse)
        mae = mean_absolute_error(actual, predicted)

        mae_dic.append(mae)
        MApe = mape(actual, predicted)

        mape_dic.append(MApe)
        r2 = r2_score(actual, predicted)

        r2_dic.append(r2)
        if n_out == 1:
            strr = '预测结果指标:'
        else:
            strr = '第'+ str(i + 1)+'步预测结果指标:'
        table.add_row([strr, mse, rmse, mae, str(MApe)+'%', str(r2*100)+'%'])

    return mse_dic,rmse_dic, mae_dic, mape_dic, r2_dic, table



mse_dic,rmse_dic, mae_dic, mape_dic, r2_dic, table = evaluate_forecasts(actual_test, pre_test, n_out)

print(table)#显示预测指标数值

1. pre_test 数据整合

作用

将多个VMD分量的预测结果(predicted_data)按样本点求和,得到最终预测值。
方法

  • 遍历每个样本点(i),累加所有VMD分量在该点的预测值(predicted_data[j][i])。

  • 结果存储在 pre_test 中,形状为 (n_test_samples, n_out)

关键库

  • numpy:用于数组操作(np.array转换)。


2. actual_test 真实值提取

作用

从原始数据中提取测试集的真实目标值。
方法

  1. 调用 data_collation 重新整理原始数据(values)为监督学习格式。

  2. 取测试集部分(n_train_number:)的最后 n_out 列作为真实值。

  3. 调整形状为 (n_test_samples, n_out)

关键库

  • numpy:数组切片和形状调整(reshape)。


3. mape(y_true, y_pred)

作用

计算平均绝对百分比误差(MAPE),衡量预测值与真实值的相对偏差。
公式

MAPE=100%n∑i=1n∣ytrue(i)−ypred(i)ytrue(i)∣MAPE=n100%​i=1∑n​​ytrue(i)​ytrue(i)​−ypred(i)​​​

方法

  • 遍历每个样本点,计算绝对百分比误差并求均值。

  • 结果乘以100转换为百分比。

关键库

  • numpynp.abs(绝对值)、np.mean(求均值)。


4. evaluate_forecasts(Ytest, predicted_data, n_out)

作用

综合评估预测结果的多个指标,并以表格形式输出。
计算指标

  1. MSE(均方误差)

    MSE=1n∑i=1n(ytrue(i)−ypred(i))2MSE=n1​i=1∑n​(ytrue(i)​−ypred(i)​)2
    • 使用 sklearn.metrics.mean_squared_error

  2. RMSE(均方根误差)

    RMSE=MSERMSE=MSE​
    • 通过 math.sqrt 计算。

  3. MAE(平均绝对误差)

    • 使用 sklearn.metrics.mean_absolute_error

  4. MAPE:调用自定义的 mape 函数。

  5. R²(决定系数)

    • 使用 sklearn.metrics.r2_score

输出格式

  • 使用 prettytable.PrettyTable 生成美观的表格,包含各步预测的指标。

关键库

  • sklearn.metrics:计算MSE、MAE、R²。

  • math:计算平方根(RMSE)。

  • prettytable:格式化输出表格。


5. 模型与方法总结

使用的模型
  • 核心模型cnn_bilstm_attention_model()

    • 结构

      1. CNN层Conv1D + MaxPooling1D):提取局部时序特征。

      2. BiLSTM层Bidirectional(LSTM)):捕捉双向长期依赖。

      3. 注意力层attention_layer):动态加权关键时间步。

      4. 全连接层Dense):输出最终预测。

    • 损失函数:MSE(均方误差)。

    • 优化器:Adam。

评估方法
  • 多指标验证:通过MSE、RMSE、MAE、MAPE、R²全面评估预测性能。

  • 反归一化:将预测值从归一化范围还原到原始量纲(MinMaxScaler.inverse_transform)。


代码整体流程

  1. 数据整合:合并VMD分量与原始特征,生成监督学习格式。

  2. 训练与预测:用CNN-BiLSTM-Attention模型预测各VMD分量结果并求和。

  3. 评估:计算测试集的多个误差指标,输出表格。

关键库与函数依赖

功能主要库/函数
数据操作numpy(数组操作)、pandas(数据读取)
机器学习sklearn.preprocessing.MinMaxScaler(归一化)、sklearn.metrics(评估指标)
深度学习模型tensorflow.keras.layers(网络层)、tensorflow.keras.Model(模型构建)
结果可视化prettytable(表格打印)

通过结合信号分解(VMD)、混合深度学习模型和严谨的评估方法,代码实现了高精度的电力负荷预测。


结果图绘制 ——

## 结果图绘制
from matplotlib import rcParams

config = {
            "font.family": 'serif',
            "font.size": 10,# 相当于小四大小
            "mathtext.fontset": 'stix',#matplotlib渲染数学字体时使用的字体,和Times New Roman差别不大
            "font.serif": ['Times New Roman'],#Times New Roman
            'axes.unicode_minus': False # 处理负号,即-号
         }
rcParams.update(config)

plt.ion()
for ii in range(n_out):

    plt.rcParams['axes.unicode_minus'] = False

    plt.style.use('cyberpunk')

    plt.figure(figsize=(10, 2), dpi=300)
    x = range(1, len(actual_test) + 1)
    
    plt.xticks(x[::int((len(actual_test)+1))])
    
    plt.tick_params(labelsize=5)  # 改变刻度字体大小
    
    plt.plot(x, pre_test[:,ii], linestyle="--",linewidth=0.5, label='predict')
    
    plt.plot(x, actual_test[:,ii], linestyle="-", linewidth=0.5,label='Real')
    
    plt.rcParams.update({'font.size': 5})  # 改变图例里面的字体大小
   
    plt.legend(loc='upper right', frameon=False)
    
    plt.xlabel("Sample points", fontsize=5)
    
    plt.ylabel("value", fontsize=5)
    
    if n_out == 1:     #如果是单步预测
        plt.title(f"The prediction result of VMD-CNN-BiLSTM-Attention :\nMAPE: {mape(actual_test[:, ii], pre_test[:, ii])} %")
    else:
        plt.title(f"{ii+1} step of VMD-CNN-BiLSTM-Attention prediction\nMAPE: {mape(actual_test[:,ii], pre_test[:,ii])} %")


plt.ioff()  
plt.show()

1. 全局样式设置

作用

配置Matplotlib的全局绘图样式,确保图表风格统一且支持中文/符号显示。

python

复制

rcParams.update({
    "font.family": 'serif',          # 使用衬线字体(如Times New Roman)
    "font.size": 10,                 # 基础字体大小
    "mathtext.fontset": 'stix',      # 数学字体风格
    "font.serif": ['Times New Roman'], # 指定具体字体
    'axes.unicode_minus': False      # 解决负号显示问题
})

关键方法

  • matplotlib.rcParams.update():覆盖默认绘图参数。


2. 单图绘制逻辑(循环内)

作用

为每个预测步长(n_out)绘制一幅真实值与预测值的对比图。

分步实现
  1. 初始化画布

    plt.figure(figsize=(10, 2), dpi=300)  # 10英寸宽、2英寸高,300DPI分辨率
    • 适合横向展示长时间序列。

  2. 生成x轴坐标

    x = range(1, len(actual_test) + 1)  # 样本点从1开始编号
  3. 调整刻度

    plt.xticks(x[::int((len(actual_test)+1)])  # 稀疏显示刻度标签
    plt.tick_params(labelsize=5)               # 刻度字体大小
    • 避免x轴标签重叠。

  4. 绘制曲线

    # 预测值(虚线)
    plt.plot(x, pre_test[:,ii], linestyle="--", linewidth=0.5, label='predict')
    # 真实值(实线)
    plt.plot(x, actual_test[:,ii], linestyle="-", linewidth=0.5, label='Real')
    • 样式区别:虚线(预测)vs 实线(真实),细线宽(0.5)。

  5. 添加图例和标签

    plt.legend(loc='upper right', frameon=False)  # 无边框图例
    plt.xlabel("Sample points", fontsize=5)       # x轴标签
    plt.ylabel("value", fontsize=5)              # y轴标签
    • 小字体(5号)避免拥挤。

  6. 动态标题

    title = f"Step {ii+1} Prediction (MAPE: {mape(actual_test[:,ii], pre_test[:,ii]):.2f}%)"
    plt.title(title)
    • 调用自定义的mape()函数显示误差。


3. 交互模式控制

作用

管理绘图窗口的交互行为。

plt.ion()  # 开启交互模式(允许动态更新)
plt.ioff() # 关闭交互模式(显示后阻塞)
plt.show() # 显示所有图像

适用场景

  • 在Jupyter等环境中实时显示图表时使用ion(),脚本中可省略。


4. 关键绘图方法总结

功能方法/参数作用
样式配置rcParams.update()全局设置字体、大小、数学符号等。
创建画布plt.figure(figsize, dpi)控制图像尺寸和分辨率。
绘制曲线plt.plot(x, y, linestyle, ...)绘制折线图,支持样式定制(线型、宽度、标签)。
刻度控制plt.xticks()tick_params()调整刻度位置和标签字体。
图例与标题plt.legend()plt.title()添加图例和动态标题(含MAPE值)。
交互控制plt.ion()plt.ioff()控制是否允许绘图后继续执行代码。

5. 可视化效果设计

  1. Cyberpunk风格

    • 虽然代码中尝试设置plt.style.use('cyberpunk'),但实际被rcParams覆盖,未生效。若需启用,需删除rcParams中的字体配置。

  2. 学术图表规范

    • 字体统一:强制使用Times New Roman,适合论文发表。

    • 紧凑布局:小字号和细线宽避免图表元素重叠。

    • 误差直观化:直接在标题中显示MAPE,便于快速评估。


完整代码 ——

import os 
import math 
import pandas as pd 
import openpyxl

from math import sqrt 
from numpy import concatenate 
import matplotlib.pyplot as plt 
import numpy as np

from sklearn.preprocessing import MinMaxScaler 
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import LabelEncoder 
from sklearn.metrics import mean_squared_error 
from tensorflow.keras.layers import * 
from tensorflow.keras.models import *  
from sklearn.metrics import mean_squared_error, mean_absolute_error,r2_score 
from pandas import DataFrame  
from pandas import concat 
import keras.backend as K
from scipy.io import savemat, loadmat 
from sklearn.neural_network import MLPRegressor  
from keras.callbacks import LearningRateScheduler  
from tensorflow.keras import Input, Model, Sequential  
import mplcyberpunk
from qbstyles import mpl_style
import warnings
from prettytable import PrettyTable #可以优美的打印表格结果
from keras.layers import Dense, Activation, Dropout, LSTM, Bidirectional, LayerNormalization, Input
from tensorflow.keras.models import Model

warnings.filterwarnings("ignore") 


dataset=pd.read_csv("电力负荷预测数据2.csv",encoding='gb2312')
print(dataset)#显示dataset数据

dataset_vmd = pd.read_excel("VMD.xlsx")
values_vmd = dataset_vmd.values
values_vmd = values_vmd.astype('float32')

values = dataset.values[:,1:] #只取第2列数据,要写成1:2;只取第3列数据,要写成2:3,取第2列之后(包含第二列)的所有数据,写成 1:

values = values.astype('float32')

def data_collation(data, n_in, n_out, or_dim, scroll_window, num_samples):
    res = np.zeros((num_samples,n_in*or_dim+n_out))
    for i in range(0, num_samples):
        h1 = data[scroll_window*i: n_in+scroll_window*i,0:or_dim]
        h2 = h1.reshape( 1, n_in*or_dim)
        h3 = data[n_in+scroll_window*(i) : n_in+scroll_window*(i)+n_out,-1].T
        h4 = h3[np.newaxis, :]
        h5 = np.hstack((h2,h4))
        res[i,:] = h5
    return res


def attention_layer(inputs, time_steps):
    # 自定义的注意力机制层
    a = Permute((2, 1))(inputs)  # 将特征维度和时间步维度互换
    a = Dense(time_steps, activation='softmax')(a)  # 使用Dense层计算时间步的权重
    a_probs = Permute((2, 1), name='attention_vec')(a)  # 再次互换维度,使其与原始输入对齐
    output_attention_mul = Multiply()([inputs, a_probs])  # 将注意力权重应用到输入上
    return output_attention_mul

def cnn_bilstm_attention_model():
    # 定义一个包含CNN, LSTM和注意力机制的模型
    inputs = Input(shape=(vp_train.shape[1], vp_train.shape[2]))
    conv1d = Conv1D(filters=64, kernel_size=2, activation='relu')(inputs)
    maxpooling = MaxPooling1D(pool_size=2)(conv1d)
    reshaped = Reshape((-1, 64 * maxpooling.shape[1]))(maxpooling)  # 重塑形状以适应LSTM层
    lstm_out = Bidirectional(LSTM(128, return_sequences=True))(reshaped)  # LSTM层
    attention_out = attention_layer(lstm_out, time_steps=reshaped.shape[1])  # 注意力机制层
    attention_flatten = Flatten()(attention_out)  # 展平输出以适应全连接层
    outputs = Dense(vt_train.shape[1])(attention_flatten)  # 全连接层

    model = Model(inputs=inputs, outputs=outputs)
    model.compile(loss='mse', optimizer='Adam')
    model.summary()

    return model


#定义输入和输出
n_in = 5  # 输入前5行的数据
n_out = 1  # 预测未来1步的数据
or_dim = values.shape[1]        # 记录特征数据维度
num_samples = 2000  # 可以设定从数据中取出多少个点用于本次网络的训练与测试。
scroll_window = 1  #如果等于1,下一个数据从第二行开始取。如果等于2,下一个数据从第三行开始取
n_train_number = int(num_samples * 0.85)  #取出85%作为训练集,剩余的为测试集
n_test_number = num_samples - n_train_number   #测试集数量
predicted_data =[]
actual_data = []
# In[7]:
for vmd_num in  range (0,len(values_vmd[0])):
    imf = values_vmd[:,vmd_num]
    imf = imf.reshape(-1,1)
    combined_data = np.hstack((values[:,0:-1],imf)) # 把VMD分解的每列数据与原始数据特征列组合到一起

    res = data_collation(combined_data, n_in, n_out, or_dim, scroll_window, num_samples)
    # 把数据集分为训练集和测试集
    combined_data = np.array(res)
    # 将前面处理好的DataFrame(combined_data)转换成numpy数组,方便后续的数据操作。

    n_train_number = int(num_samples * 0.85)
   
    Xtrain = combined_data[:n_train_number, :n_in*or_dim]
    Ytrain = combined_data[:n_train_number, n_in*or_dim:]


    Xtest = combined_data[n_train_number:, :n_in*or_dim]
    Ytest = combined_data[n_train_number:,  n_in*or_dim:]

    # 归一化
    m_in = MinMaxScaler()
    vp_train = m_in.fit_transform(Xtrain)  # 注意fit_transform() 和 transform()的区别
    vp_test = m_in.transform(Xtest)  # 注意fit_transform() 和 transform()的区别

    m_out = MinMaxScaler()
    vt_train = m_out.fit_transform(Ytrain)  # 注意fit_transform() 和 transform()的区别
    vt_test = m_out.transform(Ytest)  # 注意fit_transform() 和 transform()的区别

    vp_train = vp_train.reshape((vp_train.shape[0], n_in, or_dim))

    vp_test = vp_test.reshape((vp_test.shape[0], n_in, or_dim))


    model = cnn_bilstm_attention_model()

    model.fit(vp_train, vt_train, batch_size=32, epochs=50, validation_split=0.25, verbose=2)

    yhat = model.predict(vp_test)

    yhat = yhat.reshape(num_samples-n_train_number, n_out)

    yy = m_out.inverse_transform(yhat) # 反归一化

    predicted_data.append(yy)
    actual_data.append(Ytest)

pre_test = []

for i in range(0, len(predicted_data[0])):
    sum = 0
    for j in range(0, len(predicted_data)):
        sum = sum + predicted_data[j][i]
    pre_test.append(sum)

pre_test = np.array(pre_test)

res = data_collation(values, n_in, n_out, or_dim, scroll_window, num_samples)
values = np.array(res)
actual_test = values[n_train_number:,  n_in*or_dim:]
actual_test = actual_test.reshape(num_samples-n_train_number, n_out)

def mape(y_true, y_pred):
    # 定义一个计算平均绝对百分比误差(MAPE)的函数。
    record = []
    for index in range(len(y_true)):
        # 遍历
        temp_mape = np.abs((y_pred[index] - y_true[index]) / y_true[index])
        # 计算MAPE
        record.append(temp_mape)
        # 将MAPE添加到记录列表中。
    return np.mean(record) * 100
    # 返回所有记录的平均值,乘以100得到百分比。

def evaluate_forecasts(Ytest, predicted_data, n_out):
    mse_dic = []
    rmse_dic = []
    mae_dic = []
    mape_dic = []
    r2_dic = []
    table = PrettyTable(['测试集指标','MSE', 'RMSE', 'MAE', 'MAPE','R2'])
    for i in range(n_out):
        actual = [float(row[i]) for row in Ytest]  #一列列提取
        
        predicted = [float(row[i]) for row in predicted_data]
        mse = mean_squared_error(actual, predicted)

        mse_dic.append(mse)
        rmse = sqrt(mean_squared_error(actual, predicted))

        rmse_dic.append(rmse)
        mae = mean_absolute_error(actual, predicted)

        mae_dic.append(mae)
        MApe = mape(actual, predicted)

        mape_dic.append(MApe)
        r2 = r2_score(actual, predicted)

        r2_dic.append(r2)
        if n_out == 1:
            strr = '预测结果指标:'
        else:
            strr = '第'+ str(i + 1)+'步预测结果指标:'
        table.add_row([strr, mse, rmse, mae, str(MApe)+'%', str(r2*100)+'%'])

    return mse_dic,rmse_dic, mae_dic, mape_dic, r2_dic, table



mse_dic,rmse_dic, mae_dic, mape_dic, r2_dic, table = evaluate_forecasts(actual_test, pre_test, n_out)

print(table)#显示预测指标数值

## 结果图绘制
from matplotlib import rcParams

config = {
            "font.family": 'serif',
            "font.size": 10,# 相当于小四大小
            "mathtext.fontset": 'stix',#matplotlib渲染数学字体时使用的字体,和Times New Roman差别不大
            "font.serif": ['Times New Roman'],#Times New Roman
            'axes.unicode_minus': False # 处理负号,即-号
         }
rcParams.update(config)

plt.ion()
for ii in range(n_out):

    plt.rcParams['axes.unicode_minus'] = False

    plt.style.use('cyberpunk')

    plt.figure(figsize=(10, 2), dpi=300)
    x = range(1, len(actual_test) + 1)
    
    plt.xticks(x[::int((len(actual_test)+1))])
    
    plt.tick_params(labelsize=5)  # 改变刻度字体大小
    
    plt.plot(x, pre_test[:,ii], linestyle="--",linewidth=0.5, label='predict')
    
    plt.plot(x, actual_test[:,ii], linestyle="-", linewidth=0.5,label='Real')
    
    plt.rcParams.update({'font.size': 5})  # 改变图例里面的字体大小
   
    plt.legend(loc='upper right', frameon=False)
    
    plt.xlabel("Sample points", fontsize=5)
    
    plt.ylabel("value", fontsize=5)
    
    if n_out == 1:     #如果是单步预测
        plt.title(f"The prediction result of VMD-CNN-BiLSTM-Attention :\nMAPE: {mape(actual_test[:, ii], pre_test[:, ii])} %")
    else:
        plt.title(f"{ii+1} step of VMD-CNN-BiLSTM-Attention prediction\nMAPE: {mape(actual_test[:,ii], pre_test[:,ii])} %")


plt.ioff()  
plt.show()

### BiLSTM-Attention 模型介绍 BiLSTM (Bidirectional Long Short-Term Memory) 是 RNN 的一种改进版本,其双向结构允许模型同时利用过去的和未来的上下文信息来提高性能[^3]。具体来说,在处理序列数据时,标准 LSTM 只能从前向后读取输入序列;而 BiLSTM 则分为两个方向:一个向前读取序列,另一个则反向读取同一序列。 #### Attention 机制的作用 为了进一步提升模型的表现能力,引入了 Attention 机制。该机制使得模型能够在不同位置上分配不同的权重给输入序列的不同部分,从而更好地聚焦于重要的特征[^2]。这不仅增强了上下文语义信息的理解,还帮助捕获更深层次的数据特性。 ### 应用实例 在语音情感分类领域,有研究表明采用基于 AttentionBiLSTM 构建的多情感分类模型取得了良好的效果。此方法先通过 Attention 层加强了对话语中关键成分的关注度,再经由 Softmax 函数实现最终的情感类别预测。 对于时间序列预测任务而言,也有研究者提出了 VMD-CNN-BiLSTM-Attention 组合模型。其中 BiLSTM 负责提取时间维度上的依赖模式,而 Attention 部分有助于突出显示那些对未来趋势影响较大的历史时刻。 ```python import tensorflow as tf from tensorflow.keras.layers import Input, Dense, Bidirectional, LSTM, Concatenate, Dropout, Attention from tensorflow.keras.models import Model def build_bilstm_attention_model(input_shape): inputs = Input(shape=input_shape) bilstm_output = Bidirectional(LSTM(64, return_sequences=True))(inputs) attention_out = Attention()([bilstm_output, bilstm_output]) dropout_layer = Dropout(0.5)(attention_out) dense_output = Dense(128, activation='relu')(dropout_layer) final_output = Dense(num_classes, activation='softmax')(dense_output) model = Model(inputs=inputs, outputs=final_output) return model ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值