时间序列预测(二)——前馈神经网络(Feedforward Neural Network, FNN)

时间:2024-10-19 08:01:44

目录

一、基本结构和工作原理

1、基本结构

2、工作原理

(1)前向传播(Forward Propagation)

(2)反向传播(Backward Propagation)

3、输入和输出形状要求

(1)输入数据

(2)输出数据

二、补充的几个概念

1、激活函数(Activation Function)

2、损失函数(Loss Function)

3、优化器(Optimizer)

三、前馈神经网络(FNN)与多层感知器(MLP)的关系

四、代码实现

具体代码如下:

输出结果:​编辑

试验:


上一篇文章有提到线性回归方程。

时间序列预测(二)——前馈神经网络(Feedforward Neural Network, FNN)-****博客

与线性回归相比:

线性回归只有一个线性层,输入直接映射到输出,不包含隐藏层和激活函数。而前馈神经网络通常包含多个隐藏层和非线性激活函数,可以学习更复杂的模式和非线性关系。当前馈神经网络只有一个输入层和一个输出层,且没有激活函数时,它实际上等价于线性回归。

接下来,讲前馈神经网络。

前馈神经网络(Feedforward Neural Networks)是最基础的神经网络类型,信号单向流动,从输入层到隐藏层再到输出层,没有反馈循环,它适用于非时间序列的预测问题。在时间序列预测中,前馈神经网络(FNN) 虽然没有时间依赖性,但可以通过适当的数据预处理来实现时间序列预测的功能。它的核心思路是将时间序列的历史数据转换为独立特征,然后通过 FNN 进行预测。这种方法称为 滞后输入法(Lagged Input Method)窗口法

一、基本结构和工作原理

1、基本结构

前馈神经网络(Feed-Forward Neural Network,简称FNN)是一种基本且广泛应用的人工神经网络结构。其结构由多个层次组成,主要包括:

  • 输入层:接收外部输入的数据,并将其传递给下一层。
  • 隐藏层:位于输入层和输出层之间,可以有一层或多层,负责对输入数据进行非线性变换和特征提取。
  • 输出层:接收隐藏层的输出,并将最终的结果输出

d6aae14cd98c49c2bbae44d572deaae6.png

2、工作原理

前馈神经网络的工作原理主要包括前向传播和反向传播两个过程。

(1)前向传播(Forward Propagation)

首先,输入数据X=[x1​,x2​,…,xn​]进入输入层,然后依次经过每个隐藏层的神经元,每个神经元通过权重和偏置对输入进行线性组合,并经过激活函数处理。每层的输出成为下一层的输入。最后,隐藏层的最终输出通过输出层,生成预测结果。

如果是回归问题,输出可以是连续值;如果是分类问题,输出可以是概率分布或类别标签。

其中每个隐藏层(及输出层)的具体计算过程如下:

aef32e66b774409ea2609b8acc652fd1.png

涉及到激活函数后面介绍。

在前向传播中,网络的参数(权重和偏置)保持不变,网络对输入数据生成初始预测结果。

(2)反向传播(Backward Propagation)

反向传播是神经网络在训练过程中使用的,其实思路是和上一篇文章讲的线性回归一样的,也是如下两步:

a、先计算计算损失函数;

b、更新权重和偏置。从输出层开始,逐层计算误差的梯度,并沿着网络向输入层传播。每层的梯度用于更新更新各层的权重和偏置,使得模型的损失逐步减小。(即梯度下降,这只是优化器的一种,后面介绍)。

这里的损失函数要丰富一些,常见的损失函数包括均方误差(MSE)、交叉熵损失等,后面介绍。

总得来说,前向传播用来计算预测结果。反向传播用来计算误差并更新参数。这两个过程相互交替,逐步优化模型,使其对输入数据的预测越来越准确。

值得注意的是,这里的“反馈”仅用于权重的调整,并不构成循环反馈,即网络整体仍然是前馈的。网络接收新信息后,改变连接权值,调节最终输出,直至网络输出误差满足精度要求,结束训练。

3、输入和输出形状要求

(1)输入数据

  • 形状:通常是一个二维张量,形状为 (batch_size,input_size)
    • batch_size:每次训练时输入的样本数量。
    • input_size:每个样本的特征数量。

(2)输出数据

  • 形状:输出的形状取决于任务类型:
    • 对于回归任务,输出通常为 (batch_size,output_size),其中 output_size 通常是 1。
    • 对于分类任务,输出可能为 (batch_size, num_classes),其中 num_classes 是类别数量。

因此,需要注意要对数据进行转化

二、补充的几个概念

1、激活函数(Activation Function)

激活函数是神经网络中每个神经元的输出函数,用于引入非线性,从而使神经网络能够逼近复杂的非线性关系。没有激活函数的网络只能表示线性变换(如上一篇的线性回归不需要激活函数),因此不能解决实际中的非线性问题。激活函数的输入是该神经元的加权和,输出则是处理后的值,通过激活函数的输出可以决定是否激活一个神经元,或者说一个神经元的输出程度。

具体请看下面这篇文章:

时间序列预测(三)——激活函数(Activation Function)-****博客


2、损失函数(Loss Function)

损失函数是衡量模型预测值与真实值之间差异的函数,指导模型在训练过程中不断优化。根据任务类型和数据分布不同,常用的损失函数分为回归损失函数(如线性回归模型用到的)和分类损失函数两大类。

具体请看下面这篇文章:

时间序列预测(四)——损失函数(Lossfunction)-****博客

3、优化器(Optimizer)

优化器用于更新神经网络的权重,以减少或最小化损失函数(loss function)的值以提高模型准确性,同时,优化器还能根据损失函数的梯度调整学习速率,帮助模型更好地学习,避免在训练过程中陷入局部最小值。常见的有BGD、SGD、Adam和RMSProp。

具体请看下面这篇文章:

​时间序列预测(五)——优化器(Optimizer)、学习率-****博客

三、前馈神经网络(FNN)与多层感知器(MLP)的关系

看其他文章又看到了多层感知器,说是和前馈神经网络很像,查了一下:

多层感知器是一种经典的前馈人工神经网络,是前馈人工神经网络的一个子集,通常专指由多个隐藏层组成的网络。所有的多层感知器都是前馈神经网络,但并非所有的前馈神经网络都是多层感知器。前馈神经网络可以包含更复杂的结构,如卷积层、池化层等,而多层感知器则主要由全连接层组成。

四、代码实现

同前一篇文章一样,根据一个包含道路曲率(Curvature)、车速(Velocity)、侧向加速度(Ay)和方向盘转角(Steering_Angle)真实的数据集,去预测未来的方向盘转角。经过上一篇文章分析,最终特征选择道路曲率、车速和历史方向盘转角(这里取了五个时刻的历史方向盘转角)这三个(7个)作为特征。

具体代码如下:
# 这是一个 多层神经网络(具体来说是一个前馈神经网络)。与线性回归的主要区别在于:

# 非线性激活函数:在隐藏层中使用了 ReLU 激活函数,允许模型学习非线性关系。线性回归无法直接处理复杂的非线性模式,而神经网络可以通过激活函数来捕捉这些关系。

# 多层结构:该神经网络包含了隐藏层和多个神经元,这使得模型能够在特征之间建立更复杂的关系。线性回归只有一个层(输入直接到输出)和一组线性权重。

# 如果只用一个没有激活函数的单层神经网络,它会退化为线性回归。因此,这里添加的隐藏层和非线性激活使得模型可以处理复杂的非线性数据。



import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error as mae, r2_score
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler

# 1. 数据预处理
# 读取数据  
data = pd.read_excel('input_data_20241010160240.xlsx')  # 替换为你的数据文件路径  

# 提取特征和标签  
labels = data['Steering_Angle'].values
features = data[['Curvature',  'Velocity']].values  # 使用 NumPy 数组

# 添加历史方向盘转角作为特征 (假设历史窗口长度为5)
window_size = 5
history_features = []
for i in range(window_size, len(data)):
    past_angles = labels[i - window_size:i]
    history_features.append(list(past_angles))
features = features[window_size:]
labels = labels[window_size:]

# 合并特征
features = np.hstack((features, history_features))

# 归一化,道路曲率、速度、侧向加速度和方向盘转角的量级可能不同,对特征和标签进行归一化处理有助于训练。
scaler_x = StandardScaler()
scaler_y = StandardScaler()
features = scaler_x.fit_transform(features)
labels = scaler_y.fit_transform(labels.reshape(-1, 1))


# 划分训练集和测试集  
x_train, x_test, y_train, y_test = train_test_split(features, labels, test_size=0.2)

# 转换为 PyTorch 张量
x_train_tensor = torch.tensor(x_train, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.float32).view(-1, 1)
x_test_tensor = torch.tensor(x_test, dtype=torch.float32)
y_test_tensor = torch.tensor(y_test, dtype=torch.float32).view(-1, 1)

# 2. 创建神经网络模型
class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(window_size + 2, 64)  # 输入层到隐藏层,输入包含历史转角的特征
        self.fc2 = nn.Linear(64, 32)  # 隐藏层到隐藏层
        self.fc3 = nn.Linear(32, 1)   # 隐藏层到输出层
    
    def forward(self, x):
        x = torch.relu(self.fc1(x))  # 激活函数
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 实例化模型
model = SimpleNN()

# 3. 设置损失函数和优化器
criterion = nn.MSELoss()  # 均方误差损失
optimizer = optim.Adam(model.parameters(), lr=0.001)  # Adam 优化器

# 4. 训练模型
num_epochs = 1000
for epoch in range(num_epochs):
    model.train()
    
    # 前向传播
    outputs = model(x_train_tensor)
    loss = criterion(outputs, y_train_tensor)

    # 后向传播和优化
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    if (epoch + 1) % 100 == 0:
        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')

# 5. 预测
model.eval()
with torch.no_grad():
    y_pred_tensor = model(x_test_tensor)

y_pred = scaler_y.inverse_transform(y_pred_tensor.numpy())  # 将预测值逆归一化
y_test = scaler_y.inverse_transform(y_test)  # 逆归一化真实值

# 评估指标
# 使用 R^2 和 MAE 进行评估
r2 = r2_score(y_test, y_pred)
mae_score = mae(y_test, y_pred)
print(f"R^2 score: {r2:.4f}")
print(f"MAE: {mae_score:.4f}")

# 支持中文
plt.rcParams['font.sans-serif'] = ['SimSun']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 绘制实际值和预测值的对比图
plt.figure(figsize=(10, 6))
plt.plot(range(len(y_test)), y_test, label='实际值', color='blue')
plt.plot(range(len(y_pred)), y_pred, label='预测值', color='red')
plt.xlabel('样本索引')
plt.ylabel('Steering Angle')
plt.title('实际值与预测值对比图')
plt.legend()
plt.grid(True)
plt.show()
输出结果:

试验:

1、如果只考虑道路曲率、车速,不考虑历史方向盘转角,输出结果:(比较差)

2、如果考虑道路曲率、车速,同时考虑八个时刻的历史方向盘转角,输出结果:(效果也没有最开始的好,说明特征多了也不一定会有好的训练效果)

 参考文章:

前馈神经网络(Feed-Forward Neural Network) - JackYang - 博客园 (cnblogs.com)

深度学习神经网络基础知识(三)前向传播,反向传播和计算图_前向传播公式-****博客

别忘了给这篇文章点个赞哦,非常感谢。我也正处于学习的过程,可能会有错,如果有问题,欢迎在评论区留言讨论,一起学习!

a89ae1f052204243ad7682a9c9d8732f.png