第二十周机器学习笔记:初步认识PINN

第二十周周报

摘要

本文主要探讨了物理信息神经网络(PINN)的基本概念、工作机制、训练过程,并与传统机器学习模型进行了比较。PINN通过将物理定律嵌入损失函数中,实现了对物理现象的精确模拟和预测,减少了对数据量的依赖,并提高了模型在数据稀缺情况下的预测能力。文章还详细介绍了构建PINN模型的步骤,包括确定问题域和物理定律、选择网络架构、准备数据集、定义损失函数、训练模型、验证和测试模型、调参与优化以及解释和应用。此外,文章通过代码实战比较了RNN、LSTM和Transformer在股市预测中的表现,发现LSTM在这种时序预测任务中表现最佳,而Transformer表现最差。最后,文章对结果进行了分析与总结,指出了LSTM的优势和Transformer在时序预测任务中的局限性。

Abstract

This paper primarily investigates the fundamental concepts, operating mechanisms, and training processes of Physics-Informed Neural Networks (PINNs), and compares them with traditional machine learning models. PINNs achieve precise simulation and prediction of physical phenomena by embedding physical laws into the loss function, thereby reducing the dependence on the volume of data and enhancing the model's predictive capability in scenarios where data is scarce. The paper also provides a detailed exposition of the steps involved in constructing PINN models, which include identifying the problem domain and physical laws, selecting network architectures, preparing datasets, defining loss functions, training models, validating and testing models, adjusting parameters and optimizing, as well as interpreting and applying the models. Furthermore, the paper compares the performance of Recurrent Neural Networks (RNNs), Long Short-Term Memory (LSTM) networks, and Transformers in stock market forecasting through code implementation, finding that LSTM outperforms the others in such time-series prediction tasks, while Transformer performs the worst. Finally, the paper analyzes and summarizes the results, highlighting the advantages of LSTM and the limitations of Transformer in time-series forecasting tasks.

一、初步认识物理信息神经网络(PINN)

1.PINN的基本概念

1. PINN的基本原理

物理信息神经网络(PINN)是一种深度学习模型,它通过将物理定律嵌入到神经网络的损失函数中,实现了对物理现象的精确模拟和预测。这种模型不仅依赖于数据驱动的学习,还利用物理法则来指导模型的训练过程。

2.PINN模型的工作机制

PINN模型的核心在于其损失函数的设计,该函数不仅包括数据误差项,还特别加入了物理信息误差项。这种设计确保了模型在拟合数据的同时,还能够遵循物理定律。例如,在流体动力学问题中,PINN模型会将Navier-Stokes方程作为物理信息项纳入损失函数,以确保预测结果的物理一致性。

3. PINN模型的训练过程
PINN模型的训练过程涉及以下几个关键步骤:

  • 定义物理问题和相应的物理定律 :明确模型的目标和对应的物理定律,如流体力学中的Navier-Stokes方程:
    ρ ( ∂ u ∂ t + u ⋅ ∇ u ) = − ∇ p + μ ∇ 2 u + f \rho \left( \frac{\partial \mathbf{u}}{\partial t} + \mathbf{u} \cdot \nabla \mathbf{u} \right) = -\nabla p + \mu \nabla^2 \mathbf{u} + \mathbf{f} ρ(∂t∂u+u⋅∇u)=−∇p+μ∇2u+f
    其中,ρ 是流体密度,u 是速度向量,p 是压力,μ 是动力粘度,f 是外力。

  • 构建神经网络:设计适合问题复杂性的神经网络结构,输入参数通常包括位置、时间等,输出为物理量的预测值。

  • 定义损失函数 :损失函数由数据误差项和物理信息误差项组成,后者确保模型预测结果满足物理定律。例如,对于一维热传导方程:
    ∂ u ∂ t − α ∂ 2 u ∂ x 2 = 0 \frac{\partial u}{\partial t} - \alpha \frac{\partial^2 u}{\partial x^2} = 0 ∂t∂u−α∂x2∂2u=0

    其中,u(x,t) 是温度分布,α 是热扩散系数 。物理信息误差项可以表示为:
    L P D E = [ ∂ u ^ ∂ t − α ∂ 2 u ^ ∂ x 2 ] 2 \mathcal{L}_{PDE} = \left[ \frac{\partial \hat{u}}{\partial t} - \alpha \frac{\partial^2 \hat{u}}{\partial x^2} \right]^2 LPDE=[∂t∂u^−α∂x2∂2u^]2

    数据误差项(如果有实际观测数据u~obs~)可以表示为:
    L d a t a = ∣ ∣ u ^ − u o b s ∣ ∣ 2 \mathcal{L}{data} = || \hat{u} - u{obs} ||^2 Ldata=∣∣u^−uobs∣∣2

  • 训练网络 :使用梯度下降等优化算法调整网络权重,最小化整体损失函数:
    L = λ P D E L P D E + λ d a t a L d a t a \mathcal{L} = \lambda_{PDE} \mathcal{L}{PDE} + \lambda{data} \mathcal{L}_{data} L=λPDELPDE+λdataLdata
    其中,λ~PDE~ 和 λ~data~ 是权衡两个误差项重要性的超参数。

  • 模型验证与测试:在训练集以外的数据上验证模型的准确性和物理定律的遵循情况。

2. PINN与传统机器学习的区别

PINN与传统机器学习的主要区别在于其对物理知识的集成。传统机器学习方法主要依赖于大量数据,而,PINN通过引入物理定律作为先验知识,减少了对数据量的依赖,提高了模型在数据稀缺情况下的预测能力。
比较如下:
1. 物理约束的融合

  • PINN:将物理定律直接融入模型训练,通过损失函数中的额外项确保模型遵循物理规律。
  • 传统机器学习:通常不考虑物理约束,侧重于从数据中学习模式和关系。

2. 数据依赖性

  • PINN:对数据质量和数量的依赖较小,物理约束提供了额外的指导信息。
  • 传统机器学习:需要大量的标记数据,数据稀缺或标注成本高昂时性能可能受影响。

3. 泛化能力

  • PINN:整合物理法则,即使在数据稀缺环境中也能保持合理的预测。
  • 传统机器学习:可能在数据密集区域内泛化良好,但对于新数据或极端情况可能难以提供准确预测。

4. 问题适用性

  • PINN:适用于可以被明确物理定律描述的科学计算和工程问题。
  • 传统机器学习:适用于各类问题,特别是在物理定律未知或难以描述的情况下。

3.构建PINN的步骤

构建一个PINN模型通常需要以下步骤:

  1. 确定问题域和物理定律
    明确研究问题和相应的物理定律,如偏微分方程。
  2. 选择网络架构
    根据问题的复杂性选择合适的神经网络架构,如全连接网络或卷积神经网络。
  3. 准备数据集
    收集相关的观测数据,用于校准模型预测。
  4. 定义损失函数
    构建包含数据驱动损失和物理驱动损失的损失函数。
  5. 训练模型
    使用优化算法调整网络参数,最小化总损失。
  6. 对模型进行验证和测试
    使用独立数据集测试模型的泛化能力。
  7. 调参与优化
    调整网络架构、超参数或损失函数中的权重,以改善模型表现。
  8. 解释和应用
    解释模型预测与物理过程的关系,并将其应用于实际问题。

通过这些步骤,可以构建一个适用于特定物理学问题的PINN模型。

二、代码实战------比较RNN、LSTM、Transformer在股市预测的表现

1. 环境如下:

  • 系统:windows11
  • anaconda3:10.8
  • pytorch:11.8
  • pycharm
  • python:3.8

2.数据集的收集与预处理:
数据集是上海证券交易所(代码:sh)在2000年1月4日至2007年10月22日期间的部分交易日的股票市场数据。数据包括每个交易日的日期、开盘价(open)、收盘价(close)、最高价(high)、最低价(low)、成交量(volume)等信息。

我们通过一个dataset类来对数据集进行预处理:

python 复制代码
# 这个类的主要作用是将股票数据集加载并预处理,然后提供给PyTorch模型进行训练或测试。
# 它首先读取CSV文件中的股票价格数据,然后根据train_flag参数决定是创建训练集还是测试集。
# 数据被标准化处理后,通过__getitem__方法,
# 可以获取到用于模型训练或测试的样本序列。每个样本包含T个时间步长的数据点和一个目标值(即下一个时间点的值)。
import pandas as pd  # 导入pandas库,用于数据处理
import numpy as np  # 导入numpy库,用于数值计算
import torch  # 导入PyTorch库,用于深度学习模型
import math  # 导入math库,用于数学运算
from torch.utils.data import DataLoader, Dataset  # 从PyTorch中导入DataLoader和Dataset类

# 定义一个名为StockDataset的类,它继承自PyTorch的Dataset类
class StockDataset(Dataset):
    def __init__(self, file_path, time_step=10, train_flag=True):
        # 读取数据
        with open(file_path, "r", encoding="GB2312") as fp:  # 以只读模式打开文件,使用GB2312编码
            data_pd = pd.read_csv(fp)  # 使用pandas读取CSV文件
        self.train_flag = train_flag  # 标记数据集是用于训练还是测试
        self.data_train_ratio = 0.9  # 训练数据占总数据的比例
        self.T = time_step  # 时间步长,即用于预测的过去数据点的数量
        if train_flag:  # 如果是训练数据集
            self.data_len = int(self.data_train_ratio * len(data_pd))  # 计算训练数据的长度
            data_all = np.array(data_pd['close'])  # 将'close'列转换为numpy数组
            data_all = (data_all - np.mean(data_all)) / np.std(data_all)  # 对数据进行标准化处理
            self.data = data_all[:self.data_len]  # 截取训练数据
        else:  # 如果是测试数据集
            self.data_len = int((1 - self.data_train_ratio) * len(data_pd))  # 计算测试数据的长度
            data_all = np.array(data_pd['close'])  # 将'close'列转换为numpy数组
            data_all = (data_all - np.mean(data_all)) / np.std(data_all)  # 对数据进行标准化处理
            self.data = data_all[-self.data_len:]  # 截取测试数据
        print("data len:{}".format(self.data_len))  # 打印数据长度

    def __len__(self):  # 定义数据集的长度
        return self.data_len - self.T  # 返回数据长度减去时间步长

    def __getitem__(self, idx):  # 定义如何获取单个样本
        # Transformer、RNN用这个
        return self.data[idx:idx + self.T], self.data[idx + self.T]  # 返回从idx开始的T个数据点和下一个时间点的值
        # LSTM用这个
        # seq = self.data[idx:idx + self.T]
        # target = self.data[idx + self.T]
        # return torch.tensor(seq, dtype=torch.float).view(-1, 1), torch.tensor(target, dtype=torch.float)

1.RNN在股市预测中的表现

RNN的模型如下:

python 复制代码
# 定义一个循环神经网络(RNN)模型
class RNN(nn.Module):
    def __init__(self, rnn_layer=2, input_size=1, hidden_size=4):
        super(RNN, self).__init__()
        self.rnn_layer = rnn_layer  # RNN层数
        self.input_size = input_size  # 输入层维度
        self.hidden_size = hidden_size  # 隐藏层维度
        self.rnn = nn.RNN(  # 定义RNN层
            input_size=self.input_size,  # 输入层维度
            hidden_size=self.hidden_size,  # 隐藏层维度
            num_layers=self.rnn_layer,  # RNN层数
            batch_first=True  # 批处理优先
        )
        self.fc = nn.Linear(self.hidden_size, self.input_size)  # 全连接层

    # 初始化隐藏状态
    def init_hidden(self, x):
        batch_size = x.shape[0]  # 批处理大小
        init_h = torch.zeros(self.rnn_layer, batch_size, self.hidden_size, device=x.device).requires_grad_()
        return init_h

    # 前向传播函数
    def forward(self, x, h=None):
        x = x.unsqueeze(2)  # 增加一个维度
        h = h if h else self.init_hidden(x)  # 如果没有提供隐藏状态,则初始化
        out, h = self.rnn(x, h)  # RNN层
        out = self.fc(out[:, -1, :]).squeeze(1)  # 全连接层并去除维度为1的维度
        return out

然后我们对RNN模型进行训练,代码如下:

python 复制代码
import tqdm  # 导入tqdm库,用于显示进度条
from models import *  # 从models模块导入所有模型
from dataset.dataset_rnn_transformer import StockDataset  # 从dataset模块导入StockDataset类
from torch.utils.data import DataLoader  # 从PyTorch中导入DataLoader类
from tensorboardX import SummaryWriter  # 导入tensorboardX的SummaryWriter,用于记录训练过程

checkpointdir = '../checkpoints/RNN'  # 设置模型检查点目录
stock_file = '../stocks/shangzheng.csv'  # 设置股票数据文件路径
logger = SummaryWriter(checkpointdir)  # 创建SummaryWriter实例,用于记录训练日志

# 引入设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")


def l2_loss(pred, label):  # 定义L2损失函数
    loss = torch.nn.functional.mse_loss(pred, label, size_average=True)  # 使用均方误差作为损失函数
    loss.to(device)
    return loss


def train(model, dataloader, optimizer):  # 定义训练函数
    model.train()  # 将模型设置为训练模式
    loader = tqdm.tqdm(dataloader)  # 创建进度条
    loss_epoch = 0  # 初始化损失值
    for idx, (data, label) in enumerate(loader):  # 遍历数据集
        data, label = data.float(), label.float()  # 将数据和标签转换为浮点数
        data, label = data.to(device), label.to(device)  # 使用GPU
        output = model(data)  # 模型前向传播
        optimizer.zero_grad()  # 清零梯度
        loss = l2_loss(output, label)  # 计算损失
        loss.backward()  # 反向传播
        optimizer.step()  # 更新模型参数
        loss_epoch += loss.detach().item()  # 累加损失
    loss_epoch /= len(loader)  # 计算平均损失
    return loss_epoch  # 返回平均损失


def eval(model, dataloader):  # 定义评估函数
    model.eval()  # 将模型设置为评估模式
    loader = tqdm.tqdm(dataloader)  # 创建进度条
    loss_epoch = 0  # 初始化损失值
    for idx, (data, label) in enumerate(loader):  # 遍历数据集
        data, label = data.float(), label.float()  # 将数据和标签转换为浮点数
        data, label = data.to(device), label.to(device)  # 引入GPU
        output = model(data)  # 模型前向传播
        loss = l2_loss(output, label)  # 计算损失
        loss_epoch += loss.detach().item()  # 累加损失
    loss_epoch /= len(loader)  # 计算平均损失
    return loss_epoch  # 返回平均损失


def main():  # 定义主函数
    dataset_train = StockDataset(file_path=stock_file, time_step=10)  # 创建训练数据集
    dataset_test = StockDataset(file_path=stock_file, time_step=10, train_flag=False)  # 创建测试数据集
    train_loader = DataLoader(dataset_train, batch_size=64, shuffle=True)  # 创建训练数据加载器
    test_loader = DataLoader(dataset_test, batch_size=64, shuffle=False)  # 创建测试数据加载器
    model = RNN(rnn_layer=2, input_size=1, hidden_size=4)  # 实例化RNN模型
    model = model.to(device)  # 引入GPU
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)  # 创建Adam优化器
    total_epoch = 3000  # 设置训练轮数
    for epoch_idx in range(total_epoch):  # 训练循环
        if epoch_idx % 50 == 0:  # 每50轮打印一次训练结果
            train_loss = train(model, train_loader, optimizer)  # 训练模型
            print("stage: train, epoch:{:5d}, loss:{}".format(epoch_idx, train_loss))  # 打印训练损失
            logger.add_scalar('Train/Loss', train_loss, epoch_idx)  # 记录训练损失
        if epoch_idx % 100 == 0:  # 每100轮评估一次模型
            eval_loss = eval(model, test_loader)  # 评估模型
            print("stage: test, epoch:{:5d}, loss:{}".format(epoch_idx, eval_loss))  # 打印测试损失
            torch.save(model.state_dict(), "{}/checkpoint_{:0>3}.ckpt".format(checkpointdir, epoch_idx))  # 保存模型检查点
            logger.add_scalar('Test/Loss', eval_loss, epoch_idx)  # 记录测试损失


if __name__ == '__main__':
    main()  # 运行主函数

训练3000轮,每100轮记录一次训练参数:

训练结果如下:

然后我们对训练结果进行预测
选择2900这个最后训练的最优参数

代码如下:

python 复制代码
# 导入所需的库
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import torch
import tqdm
from dataset.dataset_rnn_transformer import StockDataset
from torch.utils.data import DataLoader
from models import RNN

# 定义股票数据文件的路径和模型检查点的路径
stock_file = '../stocks/shangzheng.csv'
loadckpt = '../checkpoints/RNN/checkpoint_2900.ckpt'


def plot():
    # 加载测试数据集
    dataset_test = StockDataset(file_path=stock_file, time_step=10, train_flag=False)
    # 创建数据加载器,设置批量大小为64,不打乱数据顺序
    test_loader = DataLoader(dataset_test, batch_size=64, shuffle=False)
    # 使用tqdm库包装测试数据加载器,以显示进度条
    loader = tqdm.tqdm(test_loader)
    model = RNN()
    # 确定是否有可用的GPU,如果有,则使用GPU,否则使用CPU
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    # 使用weights_only=True来加载权重
    weights = torch.load(loadckpt, map_location=device, weights_only=True)
    # 加载模型权重到模型中
    model.load_state_dict(weights, strict=False)
    # 初始化预测结果和标签的列表
    preds = []
    labels = []
    # 遍历测试数据加载器中的所有数据
    for idx, (data, label) in enumerate(loader):
        # 将数据和标签转换为浮点数类型
        data, label = data.float(), label.float()
        # 使用模型进行预测
        output = model(data)
        # 将预测结果添加到列表中
        preds += (output.detach().tolist())
        # 将标签添加到列表中
        labels += (label.detach().tolist())
    # 创建绘图
    fig, ax = plt.subplots()
    # 生成x轴的数据,即预测结果的索引
    data_x = list(range(len(preds)))
    # 在图表上绘制预测结果,使用红色线条
    ax.plot(data_x[-60:], preds[-60:], label='RNN模型预测的股票收盘价', color='red')
    # 在图表上绘制实际标签,使用蓝色线条
    ax.plot(data_x[-60:], labels[-60:], label='实际的股票收盘价', color='blue')
    # 设置X轴标签
    ax.set_xlabel('索引号', fontsize=15)
    # 设置Y轴标签
    ax.set_ylabel('股票价格', fontsize=15)
    # 显示图例
    plt.legend()
    # 加入标题
    plt.title("RNN", fontsize=30, loc='center', color='red')
    # 显示图表
    plt.show()
    # 指定保存图表的路径
    save_path = 'plot_figure'
    base_filename = 'RNN_stock_prediction_plot'
    filename = os.path.join(save_path, f"{base_filename}.png")
    if os.path.exists(filename):
        counter = 1
        while os.path.exists(os.path.join(save_path, f"{base_filename}_{counter}.png")):
            counter += 1
        filename = os.path.join(save_path, f"{base_filename}_{counter}.png")
    plt.savefig(filename)
    plt.close()
    print(f"Plot saved as {filename}")

# 如果这是主程序,则运行绘图函数
if __name__ == '__main__':
    plot()

于是就得到了预测结果:

2.LSTM在股市预测中的表现

模型:

python 复制代码
class LSTM(nn.Module):
    def __init__(self, lstm_layer=2, input_dim=1, hidden_size=8):
        super(LSTM, self).__init__()
        self.hidden_size = hidden_size
        self.lstm_layer = lstm_layer
        self.lstm = nn.LSTM(
            input_size=input_dim,
            hidden_size=hidden_size,
            num_layers=self.lstm_layer,
            batch_first=True
        )
        self.out_layer = nn.Linear(hidden_size, input_dim)

    def init_hidden(self, x):
        batch_size = x.shape[0]
        init_h = (torch.zeros(self.lstm_layer, batch_size, self.hidden_size, device=x.device),
                  torch.zeros(self.lstm_layer, batch_size, self.hidden_size, device=x.device))
        return init_h

    def forward(self, x, h=None):
        h = h if h else self.init_hidden(x)
        output, hidden = self.lstm(x, h)
        out = self.out_layer(output[:, -1, :]).squeeze(1)
        return out

训练的代码类似于上面,就不赘述了

训练的参数如下:

同样制作图表,结果如下:

下面记录一下在处理LSTM模型时候,遇到的问题:

  1. 错误: RuntimeError: input.size(-1) must be equal to input_size. Expected 1, got 8 表明您的 LSTM 层期望的输入特征维度是 1,但是实际上接收到的输入特征维度是 8。
    这个问题通常是由于以下几个原因造成的:
  2. 数据预处理:StockDataset 类返回的数据形状可能不正确。您的 getitem 方法应该返回一个形状为 (time_step, input_dim) 的张量,其中 input_dim 是 1,因为您的 LSTM 模型的 input_dim 参数设置为 1。
  3. 模型定义:在 LSTM 类中,您定义了 self.emb_layer 来将输入特征维度从 input_dim(1)映射到 hidden_size(8)。然后,您将这个输出传递给 LSTM 层。但是,LSTM 层期望的输入特征维度仍然是 1。

为了解决这个问题,我们需要确保数据的形状与模型的期望输入相匹配。以下是解决方案:

  1. 步骤 1: 检查 StockDataset 的 getitem 方法
python 复制代码
def __getitem__(self, idx):
    # 返回从idx开始的T个数据点和下一个时间点的值
    # 确保返回的数据形状是 (time_step, 1) 
    seq = self.data[idx:idx + self.T]
    target = self.data[idx + self.T]
    return torch.tensor(seq, dtype=torch.float).view(-1, 1), torch.tensor(target, dtype=torch.float)
  1. 步骤 2: 调整 LSTM 类的定义
    由于您的 LSTM 层期望的输入特征维度是 1,您不需要 self.emb_layer 来改变输入特征维度。您可以直接将输入传递给 LSTM 层。
python 复制代码
class LSTM(nn.Module):
    def __init__(self, lstm_layer=2, input_dim=1, hidden_size=8):
        super(LSTM, self).__init__()
        self.hidden_size = hidden_size
        self.lstm_layer = lstm_layer
        self.lstm = nn.LSTM(
            input_size=input_dim,
            hidden_size=hidden_size,
            num_layers=self.lstm_layer,
            batch_first=True
        )
        self.out_layer = nn.Linear(hidden_size, input_dim)

    def init_hidden(self, x):
        batch_size = x.shape[0]
        init_h = (torch.zeros(self.lstm_layer, batch_size, self.hidden_size, device=x.device),
                  torch.zeros(self.lstm_layer, batch_size, self.hidden_size, device=x.device))
        return init_h

    def forward(self, x, h=None):
        h = h if h else self.init_hidden(x)
        output, hidden = self.lstm(x, h)
        out = self.out_layer(output[:, -1, :]).squeeze(1)
        return out

3.Transformer在股市预测中的表现

模型:

python 复制代码
class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):  # 增加 max_len 的默认值,以适应更长的序列
        super(PositionalEncoding, self).__init__()
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0).transpose(0, 1)
        self.register_buffer('pe', pe)

    def forward(self, x):
        # 动态调整位置编码的长度以匹配输入张量 x 的长度
        pe = self.pe[:x.size(0), :]
        return x + pe


# 定义一个函数,用于生成Transformer模型中的遮罩,以防止未来信息的泄露
def _generate_square_subsequent_mask(sz):
    # 创建一个上三角矩阵,用于遮罩未来信息
    mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
    # 将0值替换为负无穷,1值替换为0,以便于后续的计算
    mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
    return mask


# 定义Transformer模型
class Transformer(nn.Module):
    def __init__(self, feature_size=1, num_layers=4, dropout=0.1):
        super(Transformer, self).__init__()
        self.pos_encoder = PositionalEncoding(feature_size)
        self.encoder_layer = nn.TransformerEncoderLayer(
            d_model=feature_size,
            nhead=8,
            dropout=dropout,
            batch_first=True
        )
        self.transformer_encoder = nn.TransformerEncoder(self.encoder_layer, num_layers=num_layers)
        self.decoder = nn.Linear(feature_size, 1)  # 确保输出层的输出尺寸为 1
        self.init_weights()

    def init_weights(self):
        initrange = 0.1
        self.decoder.bias.data.zero_()
        self.decoder.weight.data.uniform_(-initrange, initrange)

    def forward(self, src):
        src = src.unsqueeze(2)
        src = self.pos_encoder(src)
        output = self.transformer_encoder(src)
        # 确保输出的尺寸与输入的批量大小一致
        output = self.decoder(output[:, -1, :])  # 只取序列的最后一个输出
        return output.squeeze(1)  # 压缩输出,移除多余的维度,以匹配最终的标签形状

对模型进行训练,同上

最后是结果:

4.结果分析与总结

以下是对结果的分析与探讨:

Total loss如下:

模型 total Loss(损失函数:均方误差)
RNN 0.0036613855459918716
LSTM 0.0032446914311013604
Transformer 0.017887676032642796

结果是显而易见的,RNN与LSTM在这种时序预测中的表现是比较好的,LSTM更优一些。Transformer是最差的。

1.为什么LSTM表现最好?

  1. 长期依赖问题:LSTM(长短期记忆网络)是为了解决RNN(循环神经网络)在处理长序列数据时遇到的梯度消失或梯度爆炸问题而设计的。LSTM通过引入门控机制(输入门、遗忘门、输出门)来控制信息的流动,有效地捕捉长期依赖关系。
  2. 参数共享:LSTM在时间序列的不同位置共享相同的参数,这使得模型能够学习到时间序列中不同位置的相同模式,提高了模型的泛化能力。
  3. 更好的梯度流:LSTM的设计允许梯度更有效地流动,减少了梯度消失或爆炸的问题,使得模型在训练过程中更加稳定。

2.Transformer表现差的原因包括:

  1. 对时序数据的适应性 :Transformer最初是为处理自然语言处理任务设计的,它依赖于自注意力机制来捕捉序列中任意两个位置之间的关系。然而,这种机制可能不如RNN和LSTM那样自然地适应时序数据,特别是在股票价格预测这类具有明显时间序列特性的任务中。
  2. 缺乏时间序列特性的建模 :Transformer没有像RNN和LSTM那样的循环结构,它不直接建模时间序列数据的顺序性,这可能导致它在捕捉时间序列数据的动态变化和趋势方面不如RNN和LSTM有效。
  3. 超参数调整和模型结构:Transformer模型的表现可能受到超参数设置和模型结构的影响。例如,如果Transformer的层数、头数或隐藏层大小没有针对特定的时序预测任务进行优化,可能会导致性能不佳。

3.RNN表现比LSTM差的原因:

  1. 梯度问题 :传统的RNN由于其简单的循环结构,在处理长序列时容易出现梯度消失或爆炸的问题,导致模型难以学习到长期依赖关系。
  2. 参数更新问题:RNN在每个时间步更新参数时,是独立更新的,这可能导致模型难以捕捉到时间序列中的长期模式。

LSTM之所以比RNN表现好,主要是因为它通过门控机制有效地解决了长期依赖问题,并且能够更稳定地处理长序列数据,防止梯度爆炸和梯度消失。

而Transformer在股票价格预测这类时序预测任务中表现不佳,可能是因为它最初是为处理自然语言数据设计的,缺乏对时序数据的自然适应性,以及可能需要针对时序数据进行特定的结构和超参数调整。
因此,在实际应用中,选择合适的模型需要考虑任务的特性和数据的特点,以及模型的适应性和泛化能力。

总结

本周因为要准备考试和课程论文,进度缓慢,之后需要加快进度。

本周的工作主要集中在对物理信息神经网络(PINN)的理解和应用上,以及比较不同深度学习模型在股市预测任务中的表现。通过深入研究PINN,我们了解到其在物理现象模拟中的优势,尤其是在数据稀缺的情况下。在代码实战部分,我们通过比较RNN、LSTM和Transformer在股市预测中的表现,发现LSTM因其长短期记忆能力在时序预测任务中表现最佳。相反,Transformer由于其原始设计并非针对时序数据,因此在这类任务中表现不佳。此外,我们还讨论了RNN和LSTM在处理长序列数据时的梯度问题,以及LSTM如何通过门控机制解决这些问题。最后,文章指出了在实际应用中选择合适模型的重要性,需要考虑任务特性、数据特点以及模型的适应性和泛化能力。

下一周计划继续跑模型和深入的理解PINN。

相关推荐
ZHOU_WUYI14 分钟前
4.metagpt中的软件公司智能体 (ProjectManager 角色)
人工智能·metagpt
靴子学长1 小时前
基于字节大模型的论文翻译(含免费源码)
人工智能·深度学习·nlp
AI_NEW_COME2 小时前
知识库管理系统可扩展性深度测评
人工智能
海棠AI实验室2 小时前
AI的进阶之路:从机器学习到深度学习的演变(一)
人工智能·深度学习·机器学习
hunteritself2 小时前
AI Weekly『12月16-22日』:OpenAI公布o3,谷歌发布首个推理模型,GitHub Copilot免费版上线!
人工智能·gpt·chatgpt·github·openai·copilot
IT古董3 小时前
【机器学习】机器学习的基本分类-强化学习-策略梯度(Policy Gradient,PG)
人工智能·机器学习·分类
centurysee3 小时前
【最佳实践】Anthropic:Agentic系统实践案例
人工智能
mahuifa3 小时前
混合开发环境---使用编程AI辅助开发Qt
人工智能·vscode·qt·qtcreator·编程ai
四口鲸鱼爱吃盐3 小时前
Pytorch | 从零构建GoogleNet对CIFAR10进行分类
人工智能·pytorch·分类
冷眼看人间恩怨3 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget