基于TCN-Transformer-KAN混合模型实现电力负荷多变量时序预测——柯尔莫哥洛夫-阿诺德网络模型融合详解(PyTorch版)

在这里插入图片描述
首先,与初代KAN相比,KAN2.0不仅优化了网络架构,还提供了更多工具,如pykan等,这些工具展现了KAN在发现物理定律方面的惊人能力,包括守恒量、拉格朗日量、隐藏对称性和本构方程等‌。此外,KAN2.0还引入了MultKAN模型,通过增加额外的乘法层,更清晰地揭示数据中的乘法结构,从而增强了模型的可解释性和表达能力‌。其次,KAN2.0在研究范式上实现了转变。它不仅仅是一个神经网络架构的升级,更是一种AI+Science的研究范式。这种范式使得AI与科学的研究更具有交互性和可解释性,旨在弥合AI世界的连接主义和科学世界的符号主义之间的不相容性‌。KAN2.0还强调了“好奇心驱动的科学”,研究者不仅关注数据处理的结果,更探索这些结果背后的本质和原理‌。最后,在应用领域上,KAN2.0也进行了深化。它不仅可以轻松应对经典物理学研究,还可以根据研究者的需求进行专属定制,将专业知识作为辅助变量添加到输入中去‌。

前言

系列专栏:【深度学习:算法项目实战】✨︎
涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对抗网络、门控循环单元、长短期记忆、自然语言处理、深度强化学习、大型语言模型和迁移学习。

近年来,深度学习技术在时间序列预测领域取得了显著进展,尤其是TCN(Temporal Convolutional Networks,时间卷积网络)、Transformer以及KAN(Kolmogorov-Arnold Networks,柯尔莫哥洛夫-阿诺德网络)等模型的提出,为负荷预测提供了新的思路和方法。TCN作为一种专门为时间序列数据设计的卷积神经网络,通过膨胀卷积有效捕捉长程依赖关系,并具备良好的并行化能力;Transformer则凭借其自注意力机制,在自然语言处理领域取得巨大成功后,也被广泛应用于时间序列数据的全局特征提取;KAN则基于Kolmogorov-Arnold表示定理,通过可学习的激活函数逼近复杂函数关系,展现出较高的准确性和可解释性。

鉴于上述模型各自的优势,本文提出了一种基于TCN-Transformer-KAN混合模型的电力负荷时序预测方法,旨在通过结合三者的长处,进一步提高负荷预测的精度和效率。具体而言,TCN负责提取时间序列数据的局部特征和短期依赖关系,Transformer捕捉全局特征和长程依赖关系,而KAN则利用其强大的函数逼近能力处理复杂的非线性关系。三者相辅相成,共同提升模型的预测性能。

本文详细阐述了TCN-Transformer-KAN混合模型的构建过程,包括数据预处理、模型结构设计、训练与优化策略等。通过实际电力负荷数据的实验验证,展示了该混合模型在提高预测精度和效率方面的显著效果。有望为智能电网的规划与运营提供有力支持。我们相信,随着深度学习技术的不断发展,该模型将在更多领域展现出其独特的优势和广泛的应用前景。

1. 数据集介绍

本文用到的数据集是ETTh1.csv,ETTh1数据集是电力变压器数据集(ETDataset)的一部分,旨在用于长序列时间序列预测问题的研究。该数据集收集了中国两个不同县两年的数据,以预测特定地区的电力需求情况。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns

from kan import KAN

import torch
import torch.nn as nn
from torch.nn.utils.parametrizations import weight_norm
from torch.utils.data import DataLoader, TensorDataset, Subset
from torchinfo import summary
from torchmetrics.functional.regression import (
    mean_absolute_error,
    mean_absolute_percentage_error,
    mean_squared_error,
    mean_squared_log_error,
    r2_score
)
np.random.seed(0) # 随机种子
torch.manual_seed(0)
df = pd.read_csv('../ETTh1.csv', index_col=0, parse_dates=True)
print(df.head(5))
                      HUFL   HULL   MUFL   MULL   LUFL   LULL         OT
date                                                                    
2016-07-01 00:00:00  5.827  2.009  1.599  0.462  4.203  1.340  30.531000
2016-07-01 01:00:00  5.693  2.076  1.492  0.426  4.142  1.371  27.787001
2016-07-01 02:00:00  5.157  1.741  1.279  0.355  3.777  1.218  27.787001
2016-07-01 03:00:00  5.090  1.942  1.279  0.391  3.807  1.279  25.044001
2016-07-01 04:00:00  5.358  1.942  1.492  0.462  3.868  1.279  21.948000

2. 数据可视化

时序数据往往包含大量的时间点记录,直接查看原始数据可能既耗时又难以捕捉关键信息。通过数据可视化,可以能够迅速把握数据的整体趋势和波动情况。接下来我们观察一下OT列的特征趋势。

plt.style.use('seaborn-v0_8-whitegrid')
fig, ax = plt.subplots(figsize=(20, 6))

# 绘制数据
ax.plot(df['OT'], color='darkorange' ,label='Trend')

# 设置x轴为时间轴,并显示具体日期
locator = mdates.AutoDateLocator(minticks=8, maxticks=12)  # 自动定位刻度
formatter = mdates.DateFormatter('%Y-%m-%d')  # 自定义刻度标签格式
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)

# 设置标题
plt.title('OT  Trend', fontdict={'family': 'Times New Roman', 'fontsize': 15, 'color':'black'})
plt.xticks(rotation=45) # 旋转刻度标签以提高可读性
plt.ylabel('Temp', fontdict={'family': 'Times New Roman', 'fontsize': 14})
plt.legend(loc="upper right", prop={'family': 'Times New Roman'})
plt.show()

趋势

3. 特征工程

3.1 特征缩放(归一化)

在单变量预测中,‌过往目标数据是唯一的输入特征‌,模型通过分析目标变量自身的历史规律进行预测。然而,在多变量预测中,‌过往目标数据与其他外部特征结合构成模型的输入特征,但目标变量的历史信息乃是核心。
接下来我们按照公式 x s t d = x − x m i n x m a x − x m i n x_{std}=\frac{x-x_{min}} {x_{max}-x_{min}} xstd=xmaxxminxxmin 进行数据归一化,详细解释参考文章 机器学习:基于XGBoost极端梯度提升实现股票价格预测——TimeSeriesSplit交叉验证与GridSearchCV超参数调优详解

X = df # 选取特征,目标数据与其他外部特征结合
X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0)) # 特征缩放
print(X_std)
                         HUFL      HULL      MUFL      MULL      LUFL  \
date                                                                    
2016-07-01 00:00:00  0.615599  0.454943  0.628980  0.467510  0.556576   
2016-07-01 01:00:00  0.612708  0.459449  0.626458  0.464878  0.550279   
2016-07-01 02:00:00  0.601143  0.436920  0.621438  0.459689  0.512595   
2016-07-01 03:00:00  0.599698  0.450437  0.621438  0.462320  0.515693   
2016-07-01 04:00:00  0.605480  0.450437  0.626458  0.467510  0.521990   
...                       ...       ...       ...       ...       ...   
2018-06-26 15:00:00  0.453765  0.558574  0.458955  0.589577  0.481107   
2018-06-26 16:00:00  0.371392  0.608137  0.376064  0.599956  0.487404   
2018-06-26 17:00:00  0.550572  0.576597  0.572038  0.587018  0.506298   
2018-06-26 18:00:00  0.689299  0.576597  0.720262  0.587018  0.500000   
2018-06-26 19:00:00  0.708091  0.558574  0.737019  0.548059  0.506298   

                         LULL        OT  
date                                     
2016-07-01 00:00:00  0.613765  0.691018  
2016-07-01 01:00:00  0.620783  0.636233  
2016-07-01 02:00:00  0.586144  0.636233  
2016-07-01 03:00:00  0.599955  0.581468  
2016-07-01 04:00:00  0.599955  0.519656  
...                       ...       ...  
2018-06-26 15:00:00  0.655196  0.299159  
2018-06-26 16:00:00  0.689608  0.301955  
2018-06-26 17:00:00  0.655196  0.286521  
2018-06-26 18:00:00  0.634594  0.276679  
2018-06-26 19:00:00  0.641386  0.272466  

[17420 rows x 7 columns]

3.2 构建监督学习数据

在时间序列预测中,我们将时间序列数据转换为监督学习格式,以方便各种机器学习模型进行预测。

lag = 30  # 时间步
x_array = np.array([X_std[i:i+lag] for i in range(len(X_std) - lag)])
y_array = np.array(X_std.iloc[lag:, -1]).reshape(-1, 1)
x_tensor = torch.tensor(x_array, dtype=torch.float)
y_tensor = torch.tensor(y_array, dtype=torch.float)
print(x_tensor.shape, y_tensor.shape)  # 检查一下数据维度
torch.Size([17390, 30, 7]) torch.Size([17390, 1])

3.3 数据集划分(Subset)

使用 torch.utils.data 模块中的 Subset 划分数据集,本质是按索引进行切片,可以去查看Subset源代码

# 使用 TensorDataset 类将两个张量 x_tensor 和 y_tensor 组合成一个数据集
dataset = TensorDataset(x_tensor, y_tensor) 
train_idx = list(range(len(dataset)*3//5)) # 划分训练集索引
val_idx = list(range(len(dataset)*3//5, len(dataset)*4//5)) # 划分验证集索引
test_idx = list(range(len(dataset)*4//5, len(dataset))) # 划分测试集索引
train_set = Subset(dataset, indices=train_idx) # 创建 Subset 对象,用于训练
val_set = Subset(dataset, indices=val_idx) # 创建 Subset 对象,用于验证
test_set = Subset(dataset, indices=test_idx) # 创建 Subset 对象,用于测试

3.4 数据加载器

接下来,我们将使用 DataLoader创建数据加载器

train_loader = DataLoader(dataset=train_set, batch_size=128, shuffle=True)
valid_loader = DataLoader(dataset=val_set,batch_size=128, shuffle=False)
test_loader = DataLoader(dataset=test_set,batch_size=128, shuffle=False)

4. 构建时序预测模型(TSF)

4.1 构建TCN神经网络

构建Chomp1d模块,裁剪因果卷积产生的右侧多余填充‌,确保模型的时间维度对齐且严格遵循因果约束‌1

class Chomp1d(nn.Module):
    def __init__(self, chomp_size):
        super(Chomp1d, self).__init__()
        self.chomp_size = chomp_size

    def forward(self, x):
        return x[:, :, :-self.chomp_size].contiguous()

在TCN残差块TemporalBlock中,Chomp1d通常与因果卷积、ReLU激活和Dropout组合使用:2

class TemporalBlock(nn.Module):
    def __init__(self, n_inputs, n_outputs, kernel_size, stride, dilation, padding, dropout=0.2):
        super(TemporalBlock, self).__init__()
        self.conv1 = weight_norm(nn.Conv1d(n_inputs, n_outputs, kernel_size,
                                           stride=stride, padding=padding, dilation=dilation))
        self.chomp1 = Chomp1d(padding) # 裁剪右侧多余填充
        self.relu1 = nn.ReLU()
        self.dropout1 = nn.Dropout(dropout)

        self.conv2 = weight_norm(nn.Conv1d(n_outputs, n_outputs, kernel_size,
                                           stride=stride, padding=padding, dilation=dilation))
        self.chomp2 = Chomp1d(padding)
        self.relu2 = nn.ReLU()
        self.dropout2 = nn.Dropout(dropout)

        self.net = nn.Sequential(self.conv1, self.chomp1, self.relu1, self.dropout1,
                                 self.conv2, self.chomp2, self.relu2, self.dropout2)
        self.downsample = nn.Conv1d(n_inputs, n_outputs, 1) if n_inputs != n_outputs else None
        self.relu = nn.ReLU()
        self.init_weights()

    def init_weights(self):
        self.conv1.weight.data.normal_(0, 0.01)
        self.conv2.weight.data.normal_(0, 0.01)
        if self.downsample is not None:
            self.downsample.weight.data.normal_(0, 0.01)

    def forward(self, x):
        out = self.net(x)
        res = x if self.downsample is None else self.downsample(x)
        return self.relu(out + res)

TemporalConvNet 模块,通过‌堆叠多层膨胀因果卷积块‌,逐级扩大时间感受野,从而捕捉时间序列中的长期依赖关系

class TemporalConvNet(nn.Module):
    def __init__(self, num_inputs, num_channels, kernel_size=2, dropout=0.2):
        super(TemporalConvNet, self).__init__()
        layers = []
        num_levels = len(num_channels)
        for i in range(num_levels):
            dilation_size = 2 ** i
            in_channels = num_inputs if i == 0 else num_channels[i-1]
            out_channels = num_channels[i]
            layers += [TemporalBlock(in_channels, out_channels, kernel_size, stride=1, dilation=dilation_size,
                                     padding=(kernel_size-1) * dilation_size, dropout=dropout)]

        self.network = nn.Sequential(*layers)

    def forward(self, x):
        return self.network(x)

4.2 构建TCN-Transformer-KAN网络结构

‌TCN-Transformer-KAN ‌\text{TCN-Transformer-KAN} TCN-Transformer-KAN 网络结构‌是一个融合了时间卷积网络 ‌Temporal Convolutional Network, TCN ‌\text{Temporal Convolutional Network, TCN} Temporal Convolutional Network, TCN ‌Transformer ‌\text{Transformer} Transformer ‌Kolmogorov-Arnold Network, KAN ‌\text{Kolmogorov-Arnold Network, KAN} Kolmogorov-Arnold Network, KAN 3 的复合神经网络结构。这种结构旨在结合各自组件的优势,以提高神经网络的性能、可解释性和适应性。以下是对该网络结构的详细定义:

class TCN_Transformer_KAN(nn.Module):
    def __init__(self, input_size,
                 output_size,
                 num_channels,
                 hidden_dim,
                 kernel_size,
                 transformer_heads,
                 transformer_layers,
                 dropout
                 ):
        super(TCN_Transformer_KAN, self).__init__()
        self.tcn = TemporalConvNet(
            num_inputs=input_size,
            num_channels=num_channels,
            kernel_size = kernel_size,
            dropout = dropout)
        transformer_encoder_layer = nn.TransformerEncoderLayer(
            d_model=hidden_dim,
            nhead=transformer_heads,
            dim_feedforward=hidden_dim * 2,
            dropout=dropout,
            batch_first=True
        )
        self.transformer_encoder = nn.TransformerEncoder(
            encoder_layer=transformer_encoder_layer,
            num_layers=transformer_layers)
        self.kan = KAN([hidden_dim, output_size])

    def forward(self, x):
        x = x.permute(0, 2, 1) # 调整输入形状 TemporalConvNet 的期望输入 [N, C_in, L_in]
        x = self.tcn(x)  # [N, C_out, L_out=L_in]
        x = x.permute(0, 2, 1) # 调整输出形状 [N, L_out, C_out]
        x = self.transformer_encoder(x)
        x = x[:, -1, :]  # 最后一个时间步的输出 [N, C_out]
        return self.kan(x)

上述代码self.kan = KAN([hidden_dim, output_size]) :创建了一个 KAN (从 from kan import KAN 导入)实例,它接收一个包含两个元素的列表作为参数,用于将经过前面网络处理后的特征维度从 hidden_dim 转换为最终期望的输出维度 output_size 4

4.3 实例化模型、定义损失函数和优化器

params = {
    'input_size': 7, # 'input_size',C_in
    'output_size': 1, # 单步 预测
    'num_channels': [128] * 3,
    'hidden_dim': 128,
    'kernel_size': 4,
    'transformer_heads': 8,
    'transformer_layers': 3,
    'dropout': .2,
}
ts_model = TCN_Transformer_KAN(**params) # 实例化模型
criterion = nn.MSELoss() # 损失函数
optimizer = torch.optim.Adam(params=ts_model.parameters(), lr=0.0001) # 定义优化器

时序预测中,除了常用的损失函数criterion = nn.MSELoss(),我们还可以使用nn.L1Loss()nn.SmoothL1Loss()nn.CrossEntropyLoss()等损失函数

4.4 模型概要

# batch_size, seq_len, input_size(in_channels)
summary(model=ts_model, input_size=(128, 30, 7))
==============================================================================================================
Layer (type:depth-idx)                                       Output Shape              Param #
==============================================================================================================
TCN_Transformer_KAN                                          [128, 1]                  --
├─TemporalConvNet: 1-1                                       [128, 128, 30]            --
│    └─Sequential: 2-1                                       [128, 128, 30]            --
│    │    └─TemporalBlock: 3-1                               [128, 128, 30]            70,656
│    │    └─TemporalBlock: 3-2                               [128, 128, 30]            131,584
│    │    └─TemporalBlock: 3-3                               [128, 128, 30]            131,584
├─TransformerEncoder: 1-2                                    [128, 30, 128]            --
│    └─ModuleList: 2-2                                       --                        --
│    │    └─TransformerEncoderLayer: 3-4                     [128, 30, 128]            132,480
│    │    └─TransformerEncoderLayer: 3-5                     [128, 30, 128]            132,480
│    │    └─TransformerEncoderLayer: 3-6                     [128, 30, 128]            132,480
├─MultKAN: 1-3                                               [128, 1]                  4
│    └─ModuleList: 2-3                                       --                        --
│    │    └─KANLayer: 3-7                                    [128, 1]                  2,432
│    └─SiLU: 2-4                                             [128, 128]                --
│    └─ModuleList: 2-5                                       --                        --
│    │    └─Symbolic_KANLayer: 3-8                           [128, 1]                  640
==============================================================================================================
Total params: 734,340
Trainable params: 732,800
Non-trainable params: 1,540
Total mult-adds (Units.MEGABYTES): 29.44
==============================================================================================================
Input size (MB): 0.11
Forward/backward pass size (MB): 62.92
Params size (MB): 0.80
Estimated Total Size (MB): 63.83
==============================================================================================================

5. 模型训练

5.1 定义训练函数

在模型训练之前,我们需先定义 train 函数来执行模型训练过程

def train(model, iterator):
    model.train()
    epoch_loss = 0

    for batch_idx, (data, target) in enumerate(iterable=iterator):
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        epoch_loss += loss.item()
    avg_loss = epoch_loss / len(iterator)

    return avg_loss

上述代码定义了一个名为 train 的函数,用于训练给定的模型。它接收模型、数据迭代器作为参数,并返回训练过程中的平均损失。

5.2 定义评估函数

def evaluate(model, iterator): # Being used to validate and test
    model.eval()
    epoch_loss = 0

    with torch.no_grad():
        for batch_idx, (data, target) in enumerate(iterable=iterator):
            output = model(data)
            loss = criterion(output, target)
            epoch_loss += loss.item()

        avg_loss = epoch_loss / len(iterator)

        return avg_loss

上述代码定义了一个名为 evaluate 的函数,用于评估给定模型在给定数据迭代器上的性能。它接收模型、数据迭代器作为参数,并返回评估过程中的平均损失。这个函数通常在模型训练的过程中定期被调用,以监控模型在验证集或测试集上的性能。通过评估模型的性能,可以了解模型的泛化能力和训练的进展情况。

5.3 定义早停法并保存模型

定义早停法以便在模型训练过程中调用

class EarlyStopping:
    def __init__(self, patience=5, delta=0.0):
        self.patience = patience  # 允许的连续未改进次数
        self.delta = delta        # 损失波动容忍阈值
        self.counter = 0          # 未改进计数器
        self.best_loss = float('inf')  # 最佳验证损失值
        self.early_stop = False   # 终止训练标志

    def __call__(self, val_loss, model):
        if val_loss < (self.best_loss - self.delta):
            self.best_loss = val_loss
            self.counter = 0
            # 保存最佳模型参数‌:ml-citation{ref="1,5" data="citationList"}
            torch.save(model.state_dict(), 'best_model.pth')
        else:
            self.counter +=1
            if self.counter >= self.patience:
                self.early_stop = True
EarlyStopper = EarlyStopping(patience=50, delta=0.00001)  # 设置参数

若不想使用早停法EarlyStopper,参数patience设置一个超大的值,delta设置为0,即可。

5.4 定义模型训练主程序

通过定义模型训练主程序来执行模型训练

def main():
    train_losses = []
    val_losses = []

    for epoch in range(300):
        train_loss = train(model=ts_model, iterator=train_loader)
        val_loss = evaluate(model=ts_model, iterator=valid_loader)

        train_losses.append(train_loss)
        val_losses.append(val_loss)

        print(f'Epoch: {epoch + 1:02}, Train MSELoss: {train_loss:.5f}, Val. MSELoss: {val_loss:.5f}')

        # 触发早停判断
        EarlyStopper(val_loss, model=ts_model)
        if EarlyStopper.early_stop:
            print(f"Early stopping at epoch {epoch}")
            break

    plt.figure(figsize=(10, 5))
    plt.plot(train_losses, label='Training Loss')
    plt.plot(val_losses, label='Validation Loss')
    plt.xlabel('Epoch')
    plt.ylabel('MSELoss')
    plt.title('Training and Validation Loss over Epochs')
    plt.legend()
    plt.grid(True)
    plt.show()

5.5 执行模型训练过程

main()
Epoch: 140, Train MSELoss: 0.00045, Val. MSELoss: 0.00019
Epoch: 141, Train MSELoss: 0.00045, Val. MSELoss: 0.00022
Epoch: 142, Train MSELoss: 0.00044, Val. MSELoss: 0.00021
Epoch: 143, Train MSELoss: 0.00046, Val. MSELoss: 0.00029
Epoch: 144, Train MSELoss: 0.00044, Val. MSELoss: 0.00021
Epoch: 145, Train MSELoss: 0.00043, Val. MSELoss: 0.00027
Epoch: 146, Train MSELoss: 0.00044, Val. MSELoss: 0.00023
Epoch: 147, Train MSELoss: 0.00044, Val. MSELoss: 0.00021
Epoch: 148, Train MSELoss: 0.00043, Val. MSELoss: 0.00023
Epoch: 149, Train MSELoss: 0.00043, Val. MSELoss: 0.00024
Epoch: 150, Train MSELoss: 0.00043, Val. MSELoss: 0.00024
Epoch: 151, Train MSELoss: 0.00044, Val. MSELoss: 0.00032
Epoch: 152, Train MSELoss: 0.00042, Val. MSELoss: 0.00021
Epoch: 153, Train MSELoss: 0.00046, Val. MSELoss: 0.00022
Epoch: 154, Train MSELoss: 0.00044, Val. MSELoss: 0.00046
Epoch: 155, Train MSELoss: 0.00044, Val. MSELoss: 0.00020
Early stopping at epoch 154

损失记录

6. 模型预测

6.1 构建预测函数

接下来,我们通过构建 predict 函数来对模型中的数据进行预测。

def predict(model, iterator):
    model.eval()
    targets = []
    predictions = []

    with torch.no_grad():
        for batch_idx, (data, target) in enumerate(iterable=iterator):
            output = model(data)
            targets.append(target)
            predictions.append(output)

    targets = torch.cat(targets)
    predictions = torch.cat(predictions, dim=0)
    return predictions, targets

这个函数的主要作用是使用给定的模型对输入的数据迭代器 iterator 中的数据进行预测,并收集相应的预测结果和真实目标值,最后返回整理好的预测值和目标值。它常用于在训练好模型之后,对测试集或者验证集等数据进行预测的场景。具体来说:

model.eval() 这行代码将模型设置为评估模式。在很多深度学习模型中,尤其是包含像 dropout、batch normalization 等在训练阶段和评估阶段行为不同的层时,调用 eval() 方法可以确保模型在预测时使用正确的计算方式。例如,在训练阶段,dropout 会按照一定概率随机丢弃神经元,而在评估阶段则不会进行这种随机丢弃操作,以保证预测结果的稳定性和确定性。

with torch.no_grad(): 不需要计算梯度。因为在预测阶段,我们并不需要进行反向传播来更新模型的参数,关闭梯度计算可以节省内存并提高计算效率。

targets = torch.cat(targets)predictions = torch.cat(predictions, dim=0):在遍历完所有批次后,使用 torch.cat 函数将收集到的 targets 列表中的张量按照维度进行拼接,使其成为一个连续的张量,方便后续对整体的预测结果和目标值进行统一的分析和处理。对于 predictions 也是同样的操作,指定了拼接维度为 0,通常在按照批次维度拼接预测结果张量时会这样做,确保拼接后的张量结构符合预期,能正确反映所有批次的预测情况。

7. 模型验证

使用 torch.load() 加载 .pth 文件,并通过 load_state_dict() 将权重映射到模型‌

ts_model.load_state_dict(state_dict=torch.load('best_model.pth'))  # 加载权重

7.1 验证集预测

val_pred, val_true = predict(ts_model, valid_loader)

7.2 验证集评估

7.2.1 回归拟合图

通过seaborn库中regplot函数绘制回归拟合图,我们可以清晰地看到模型预测值与实际数据点之间的吻合程度,即拟合度。

plt.figure(figsize=(5, 5), dpi=100)
sns.regplot(x=val_true.numpy(), y=val_pred.numpy(), scatter=True, marker="*", color='orange',line_kws={'color': 'red'})
plt.show()

回归拟合图

7.2.2 评估指标

torchmetrics库提供了各种损失函数,我们可以直接用来计算张量的损失,而无需转换成numpy数组形式。

mae = mean_absolute_error(preds=val_pred, target=val_true)
print(f"Mean Absolute Error: {mae:.5f}")

mape = mean_absolute_percentage_error(preds=val_pred, target=val_true)
print(f"Mean Absolute Percentage Error: {mape * 100:.4f}%")

mse = mean_squared_error(preds=val_pred, target=val_true)
print(f"Mean Squared Error: {mse:.4f}")

msle = mean_squared_log_error(preds=val_pred, target=val_true)
print(f"Mean Squared Log Error: {msle:.4f}")

r2 = r2_score(preds=val_pred, target=val_true)
print(f"R²: {r2:.4f}")
Mean Absolute Error: 0.01120
Mean Absolute Percentage Error: 7.2963%
Mean Squared Error: 0.0002
Mean Squared Log Error: 0.0001: 0.9711

8. 模型测试

8.1 测试集预测

test_pred, test_true = predict(ts_model, test_loader)

8.2 测试集评估

8.2.1 回归拟合图

通过seaborn库中regplot函数绘制回归拟合图,我们可以清晰地看到模型预测值与实际数据点之间的吻合程度,即拟合度。

plt.figure(figsize=(5, 5), dpi=100)
sns.regplot(x=test_true.numpy(), y=test_pred.numpy(), scatter=True, marker="*", color='orange',line_kws={'color': 'red'})
plt.show()

回归拟合图

8.2.2 评估指标

torchmetrics库提供了各种损失函数,我们可以直接用来计算张量的损失,而无需转换成numpy数组形式。

mae = mean_absolute_error(preds=test_pred, target=test_true)
print(f"Mean Absolute Error: {mae:.5f}")

mape = mean_absolute_percentage_error(preds=test_pred, target=test_true)
print(f"Mean Absolute Percentage Error: {mape * 100:.4f}%")

mse = mean_squared_error(preds=test_pred, target=test_true)
print(f"Mean Squared Error: {mse:.4f}")

msle = mean_squared_log_error(preds=test_pred, target=test_true)
print(f"Mean Squared Log Error: {msle:.4f}")

r2 = r2_score(preds=test_pred, target=test_true)
print(f"R²: {r2:.4f}")
Mean Absolute Error: 0.00959
Mean Absolute Percentage Error: 4.7268%
Mean Squared Error: 0.0002
Mean Squared Log Error: 0.0001: 0.9623

预测可视化

matplotlib 库中,我们可以直接使用张量数据来绘制图表

plt.figure(figsize=(20, 5))
plt.plot(torch.cat(tensors=(val_true, test_true), dim=0), label='Actual Value')
plt.plot(torch.cat(tensors=(val_pred, test_pred), dim=0), label='Prediction Value')
plt.axvline(len(val_pred), color='red', linestyle='-.')
plt.text(0, 0.4, 'Validation', fontsize=18)
plt.text(len(val_pred), 0.4, 'Test', fontsize=18)
plt.ylabel('Temperature', fontsize=18)
plt.title('Prediction Results', fontsize=24)
plt.legend()
plt.grid(True)
plt.show()

预测可视化

n_idx = len(y_tensor)
val_size = len(val_idx)
test_size = len(test_idx)

x_plot = list(range(len(dataset)))
y_plot = y_tensor

x_val_line = x_plot[n_idx - val_size - test_size]
x_test_line = x_plot[n_idx - test_size]
plt.style.use('_mpl-gallery')
plt.figure(figsize=(16, 8))
plt.rcParams['font.family'] = 'serif'
plt.title(f'{ts_model.__class__.__name__} Prediction Results', fontsize=40, pad=20)
plt.plot(x_plot, y_plot, label='Actual Value')
plt.plot(val_idx, val_pred, label='Validation Prediction', color='green')
plt.plot(test_idx, test_pred, label='Testing Prediction', color='orange')
plt.axvline(x_val_line, color='red', linestyle='-.')
plt.axvline(x_test_line, color='red', linestyle='-.')
plt.text(x_val_line, 0.8, 'Validation', fontsize=20)
plt.text(x_test_line, 0.8, 'Test', fontsize=20)
plt.text(0, 0.0,
         f'MAE: {mae:.5f}\n'+
         f'MAPE: {mape * 100:.4f}%\n'+
         f'MSE: {mse:.4f}\n'+
         f'MSLE: {msle:.4f}\n'+
         f'R²: {r2:.4f}', fontsize=18)
plt.ylabel('Temperature', fontsize=30)
plt.legend()
plt.show()

预测可视化

完整源码

参考链接


  1. An Empirical Evaluation of Generic Convolutional and Recurrent Networks for Sequence Modeling ↩︎

  2. TCN Source Code ↩︎

  3. KAN 2.0: Kolmogorov-Arnold Networks Meet Science ↩︎

  4. KAN Source Code ↩︎

### 关于面包板电源模块 MB102 的 USB 供电规格及兼容性 #### 1. **MB102 基本功能** 面包板电源模块 MB102 是一种常见的实验工具,主要用于为基于面包板的小型电子项目提供稳定的电压输出。它通常具有两路独立的稳压输出:一路为 5V 和另一路可调电压(一般范围为 3V 至 12V)。这种设计使得它可以满足多种芯片和传感器的不同工作电压需求。 #### 2. **USB 供电方式** MB102 支持通过 USB 接口供电,输入电压通常是标准的 5V DC[^1]。由于其内部集成了 LM7805 稳压器以及可调节电位器控制的直流-直流变换电路,因此即使输入来自电脑或其他低功率 USB 设备,也能稳定地向负载供应电力。不过需要注意的是,如果项目的功耗较高,则可能超出某些 USB 端口的最大电流能力(一般是 500mA),从而引起不稳定现象或者保护机制启动断开连接的情况发生。 #### 3. **兼容性分析** 该型号广泛适用于各种微控制器单元 (MCU),特别是那些像 Wemos D1 R32 这样可以通过杜邦线轻松接入并共享相同逻辑级别的系统[^2]。另外,在提到 Arduino Uno 板时也表明了良好的互操作性,因为两者均采用相似的标准接口定义与电气特性参数设置[^4]: - 对于需要 3.3V 工作环境下的组件来说,只需调整好对应跳线帽位置即可实现精准匹配; - 当涉及到更多外围扩展应用场合下,例如带有多重模拟信号采集任务的情形里,利用 MB102 提供干净无干扰的基础能源供给就显得尤为重要了[^3]。 综上所述,对于打算构建以单片机为核心的原型验证平台而言,选用具备良好声誉记录且易于获取配件支持服务链路上下游资源丰富的品牌产品——如这里讨论过的这款特定类型的配电装置不失为明智之举之一。 ```python # 示例 Python 代码展示如何检测硬件状态 import machine pin = machine.Pin(2, machine.Pin.IN) if pin.value() == 1: print("Power supply is stable.") else: print("Check your connections and power source.") ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

矩阵猫咪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值