【python深度学习】Day 55 序列预测任务介绍

知识点

1.序列预测介绍

(1)单步预测

(2)多步预测的2种方式

2.序列数据的处理:滑动窗口

3.多输入多输出任务的思路

4.经典机器学习在序列任务上的劣势;以随机森林为例

作业:手动构造类似的数据集(如cosx数据),观察不同的机器学习模型的差异

一、序列预测介绍

1.序列预测是什么?

结构化数据集中,每个样本之间独立无关,样本之间调换顺序不影响模型的训练。但是,当数据存在先后关系时,且用有顺序关系的数据预测下一步的值,我们称之为序列预测。

举个例子,比如有过去30天的股票价格,我们希望预测第31天的价格。比如之前的单车预测,有前60天的单车需求数据,希望预测后面20天的销量。或者文本,人的语言是有顺序的,预测下一个单词是这也是序列预测任务。

2.序列预测的x-y对

如何把之前结构化的数据,转化为序列?

答案是通过滑动窗口这个方式来实现。序列任务也需要有自己的数据对,这样才符合监督学习的训练。把原始数据序列,转化为x-y这样的标签对,用前seq_length个时间步,预测下一个时间步的监督学习格式。

假设原始数据 data = [10, 20, 30, 40, 50, 60, 70, 80, 90],

序列长度 seq_length = 3

这个x-y序列对如下

X = [
  [10, 20, 30],  # 用前3步预测第4步
  [20, 30, 40],  # 用2-4步预测第5步
  [30, 40, 50],  # 用3-5步预测第6步
  [40, 50, 60],  # 用4-6步预测第7步
  [50, 60, 70],  # 用5-7步预测第8步
  [60, 70, 80]   # 用6-8步预测第9步
]

y = [40, 50, 60, 70, 80, 90]  # 每个X对应的下一个值

其中第一个x-y对是[10, 20, 30]–>[40],
第二个x-y对是[20, 30, 40]–>[50],
第三个x-y对是[30, 40, 50]–>[60],
第四个x-y对是[40, 50, 60]–>[70],
第五个x-y对是[50, 60, 70]–>[80],
第六个x-y对是[60, 70, 80]–>[90]。
这个样本对,和我们之前结构化数据见到的样本-标签对 是一个逻辑。

注意,最后三个值 [70, 80, 90] 不能作为输入(因为没有后续值作为目标),所以生成的样本数为 len(data) - seq_length = 9 - 3 = 6

可以把上述过程理解为一个尺寸为3的窗口在滑动的过程(类似于卷积核滑动),

滑动窗口过程:
[10, 20, 30]40
   [20, 30, 40]50
      [30, 40, 50]60
         [40, 50, 60]70
            [50, 60, 70]80
               [60, 70, 80]90

3.序列预测的标准输入

一个标准的序列数据张量通常具有三个维度:[ 批量大小, 序列长度, 特征维度 ]

  • 批量大小 (batch_size):一次输入模型的序列样本数量
  • 序列长度 (Sequence Length / time_steps):每个样本的序列长度,即该样本的序列长度是多少
  • 特征维度(features):每个时间步输入的特征向量维度,即每个序列元素的属性表示

以文本分类任务为例,假设:
输入:100 个句子(batch_size=100),每个句子最长为 20 个单词(time_steps=20),每个单词用 100 维词向量表示(features=100),则输入张量形状为(100, 20, 100)。

二、单步预测与多步预测

预测的任务是有顺序关系,此时训练集和测试集是有明确的时间顺序。任务是用历史数据来预测未来数据。

  1. 单步预测(Single-Step Prediction):一次只预测下一时刻

训练集构建:若窗口大小为 5,训练样本形如 [x1,x2,x3,x4,x5]→x6,[x2,x3,x4,x5,x6]→x7,以此类推

此时只会预测到81个结束了,单步预测指的是只预测一个时刻,比如只预测未来一天的。

  1. 多步预测(Multi-Step Prediction):一次预测多个时刻
  • (1)递归式多步预测(滚动预测):先用预测第81天的,再用81天的预测数据和历史数据预测第82天的,以此类推,这种方式会造成误差的累计
  • (2)直接式多步预测:构建模型直接输出未来多个时刻的值(如输入特征 x60-x80,标签 x81-x90),这样输出的是一次性的预测结果,不会累计误差

这种直接式的多步预测,也是我们第一次接触多输入多输出这种情况。我们之前做的回归任务,都是多输入单输出。

这种多输入多输出的任务叫做MIMO(Multiple-Input Multiple-Output)

三、代码实战

1.准备工作

导入相关的库

# 准备工作

import numpy as np
import random
import os
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
# 显示中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显示负号正常
plt.rcParams['axes.unicode_minus'] = False
import warnings
warnings.filterwarnings("ignore")

# 设置随机种子确保结果可复现,全局随机函数
def set_seed(seed=42, deterministic=True):
    """
    设置全局随机种子,确保实验可重复性
    
    参数:
        seed: 随机种子值,默认为42
        deterministic: 是否启用确定性模式,默认为True
    """
    # 设置Python的随机种子
    random.seed(seed) 
    os.environ['PYTHONHASHSEED'] = str(seed) # 确保Python哈希函数的随机性一致,比如字典、集合等无序
    
    # 设置NumPy的随机种子
    np.random.seed(seed)
    
    # 设置PyTorch的随机种子
    torch.manual_seed(seed) # 设置CPU上的随机种子
    torch.cuda.manual_seed(seed) # 设置GPU上的随机种子
    torch.cuda.manual_seed_all(seed)  # 如果使用多GPU
    
    # 配置cuDNN以确保结果可重复
    if deterministic:
        torch.backends.cudnn.deterministic = True
        torch.backends.cudnn.benchmark = False


# 设置随机种子
set_seed(42)

2.2 数据生成

没有导入外部数据集,自己编写数据

# ===== 步骤1:生成合成时间序列 =====
x = np.linspace(0, 100, 1000) # 在 0 到 100 之间生成 1000 个均匀分布的点作为x
y = np.sin(x) + 0.1 * x + np.random.normal(0, 0.5, 1000)  # 正弦波+线性趋势+噪声

# 可视化原始数据
plt.figure(figsize=(12, 6))
plt.plot(y)
plt.title('合成时间序列数据(正弦波+趋势+噪声)')
plt.xlabel('时间步')
plt.ylabel('值')
plt.grid(True)
plt.show()

生成数据结果

3.数据划分与预处理

import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler

# # ===== 步骤1:生成合成时间序列(已经生成)  ===== 
# x = np.linspace(0, 100, 1000)
# y = np.sin(x) + 0.1 * x + np.random.normal(0, 0.5, 1000)

# # =============================================================
# # =================== 正确流程的代码实现 ======================
# # =============================================================

# ===== 步骤2:划分原始数据,并进行正确的标准化 =====

# 1. 定义划分点
train_size = int(len(y) * 0.8)
seq_length = 30

# 2. 划分原始数据(仅用于fit缩放器)
train_data_raw = y[:train_size]
# 注意:测试集暂时不需要单独划分出来

# 3. 数据标准化 (关键步骤!)
#    - 创建缩放器
#    - 仅在训练数据上进行拟合(fit),学习其分布
#    - 对整个数据集进行转换(transform)
scaler = MinMaxScaler(feature_range=(0, 1))
scaler.fit(train_data_raw.reshape(-1, 1))
scaled_y = scaler.transform(y.reshape(-1, 1)).flatten()

# ===== 步骤3:对完整的、缩放后的数据应用滑动窗口 =====

def create_sequences(data, seq_length):
    """
    将数据转换为适合RNN输入的序列格式 
    """
    X, y = [], []
    for i in range(len(data) - seq_length):
        X.append(data[i:i+seq_length])
        y.append(data[i+seq_length])
    return np.array(X), np.array(y)

# 在整个数据集上创建序列
all_X, all_y = create_sequences(scaled_y, seq_length)

# ===== 步骤4:划分序列数据集(X和y) =====

# 计算分割点。最后一个训练样本的标签是原始数据中的 train_data[train_size-1]。
# 这个样本的起始索引是 (train_size - 1) - seq_length。
# 因此,我们总共可以生成 (train_size - seq_length) 个训练样本。
split_idx = train_size - seq_length

X_train = all_X[:split_idx]
y_train = all_y[:split_idx]

X_test = all_X[split_idx:]
y_test = all_y[split_idx:]

# ===== 步骤5:验证结果 =====
print("原始数据总长度:", len(y))
print("训练数据原始长度:", train_size)
print("测试数据原始长度:", len(y) - train_size)
print("-" * 30)
print("序列长度 (seq_length):", seq_length)
print("滑动窗口后样本总数:", len(all_X))
print("-" * 30)
print("训练集划分点 (split_idx):", split_idx)
print("训练集特征(X_train)形状:", X_train.shape) # (770, 30) -> (800-30, 30)
print("训练集标签(y_train)形状:", y_train.shape)   # (770,)
print("测试集特征(X_test)形状:", X_test.shape)   # (200, 30) -> (1000-30 - 770, 30)
print("测试集标签(y_test)形状:", y_test.shape)     # (200,)
print("-" * 30)

4. 模型构建

用机器学习,线性回归、随机森林模型,效果不好,见Day55

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值