循环神经网络,特别是LSTM和GRU,非常适合时间序列预测,因为它们能够捕捉数据内部的时间依赖关系。与静态回归模型不同,RNN逐步处理序列,并保持一个包含过去观测信息的内部状态(记忆)。构建一个有效的预测模型,需要根据具体的预测任务选择合适的输入-输出结构。在将数据输入RNN之前,时间序列数据通常需要预处理,包括归一化(将值缩放到特定范围,如[0, 1],或标准化为零均值和单位方差)和窗口化。窗口化将连续时间序列转换为适合监督学习的输入-输出对。一个输入窗口包含固定数量的过去时间步(回溯期),而对应的输出是我们希望预测的值。RNN的输入窗口化假设一个时间序列$x_1, x_2, x_3, \ldots, x_T$。为了训练RNN,我们创建重叠的窗口。如果选择回溯窗口大小为$W$,第一个输入序列可能是$(x_1, x_2, \ldots, x_W)$,第二个是$(x_2, x_3, \ldots, x_{W+1})$,依此类推。每个输入序列的目标取决于预测任务。digraph G { rankdir=LR; node [shape=box, style=filled, fillcolor="#a5d8ff", fontsize=10]; edge [arrowhead=vee, color="#adb5bd"]; subgraph cluster_ts { label="原始时间序列"; style=dashed; bgcolor="#e9ecef"; x1 [label="x1"]; x2 [label="x2"]; x3 [label="x3"]; x4 [label="x4"]; x5 [label="x5"]; x6 [label="x6"]; x7 [label="x7"]; x1 -> x2 -> x3 -> x4 -> x5 -> x6 -> x7; } subgraph cluster_w1 { label="窗口 1 (输入)"; style=dashed; bgcolor="#fff3bf"; w1_1 [label="x1"]; w1_2 [label="x2"]; w1_3 [label="x3"]; w1_1 -> w1_2 -> w1_3; } subgraph cluster_t1 { label="目标 1"; style=dashed; bgcolor="#ffc9c9"; t1 [label="x4"]; } subgraph cluster_w2 { label="窗口 2 (输入)"; style=dashed; bgcolor="#fff3bf"; w2_1 [label="x2"]; w2_2 [label="x3"]; w2_3 [label="x4"]; w2_1 -> w2_2 -> w2_3; } subgraph cluster_t2 { label="目标 2"; style=dashed; bgcolor="#ffc9c9"; t2 [label="x5"]; } subgraph cluster_w3 { label="窗口 3 (输入)"; style=dashed; bgcolor="#fff3bf"; w3_1 [label="x3"]; w3_2 [label="x4"]; w3_3 [label="x5"]; w3_1 -> w3_2 -> w3_3; } subgraph cluster_t3 { label="目标 3"; style=dashed; bgcolor="#ffc9c9"; t3 [label="x6"]; } edge [style=dashed, arrowhead=vee, color="#495057", fontsize=8]; w1_3 -> t1 [label="预测"]; w2_3 -> t2 [label="预测"]; w3_3 -> t3 [label="预测"]; } 从时间序列创建输入窗口(大小 W=3)和对应的单步目标。在大多数框架中,RNN层的输入形状是(batch_size, time_steps, features)。对于单变量时间序列(每个时间步一个值),features为1。time_steps维度对应于窗口大小$W$。多对一架构这是预测单个未来值最常见的设置。RNN(例如LSTM或GRU)处理长度为$W$的输入序列。我们通常只需要RNN 最后时间步的输出或隐藏状态,因为此状态概括了整个输入窗口的信息。随后在这个最终RNN输出之上添加一个全连接层来生成单个预测值。输入: 长度为$W$的序列,例如$(x_t, x_{t+1}, \ldots, x_{t+W-1})$。目标: 下一个时间步的单个值,例如$x_{t+W}$。RNN配置: RNN层(LSTM/GRU)应设置return_sequences=False(在Keras/TensorFlow中)或只使用最终隐藏状态(在PyTorch中)。这确保只有最后时间步的输出被传递下去。输出层: 一个具有一个单元的全连接层(通常用于回归问题的线性激活函数)。digraph G { rankdir=LR; node [shape=box, style=filled, fontname="sans-serif"]; subgraph cluster_input { label = "输入窗口 (W 步)"; bgcolor="#fff3bf"; rank=same; x1 [label="x(t)", fillcolor="#a5d8ff"]; x2 [label="x(t+1)", fillcolor="#a5d8ff"]; xdots [label="...", shape=plaintext]; xW [label="x(t+W-1)", fillcolor="#a5d8ff"]; x1 -> x2 -> xdots -> xW [style=invis]; // Keep them aligned } subgraph cluster_rnn { label = "RNN (LSTM/GRU)"; bgcolor="#d0bfff"; node [fillcolor="#e599f7"]; rnn1 [label="步骤 1"]; rnn2 [label="步骤 2"]; rnndots [label="...", shape=plaintext]; rnnW [label="步骤 W\n(使用最终状态)"]; } subgraph cluster_output { label = "输出"; bgcolor="#ffc9c9"; node [fillcolor="#ffa8a8"]; dense [label="全连接层(1)"]; pred [label="预测 x(t+W)"]; } // Connections edge [arrowhead=vee, color="#495057"]; x1 -> rnn1; x2 -> rnn2; xW -> rnnW; rnn1 -> rnn2 [label="h1", fontsize=9]; rnn2 -> rnndots [label="h2", fontsize=9]; rnndots -> rnnW [label="...", fontsize=9]; rnnW -> dense [label="hW (最终隐藏状态)", fontsize=9]; dense -> pred; }用于单步时间序列预测的多对一架构。RNN处理输入窗口,并且只有最终隐藏状态被全连接层用来预测下一个值。多对多架构有时,我们需要预测多个未来时间步。假设我们想预测接下来的$H$步(预测范围)。输入: 长度为$W$的序列,例如$(x_t, x_{t+1}, \ldots, x_{t+W-1})$。目标: 长度为$H$的序列,例如$(x_{t+W}, x_{t+W+1}, \ldots, x_{t+W+H-1})$。有几种方式来构建这类模型:向量输出 / 单次预测:类似于多对一,RNN处理整个长度为$W$的输入序列。只使用最后时间步的最终隐藏状态(或输出)。最终的全连接层具有$H$个输出单元,同时预测所有$H$个未来步骤。RNN配置: return_sequences=False(Keras/TF)或只使用最终隐藏状态(PyTorch)。输出层: Dense(H)。digraph G { rankdir=LR; node [shape=box, style=filled, fontname="sans-serif"]; subgraph cluster_input { label = "输入窗口 (W 步)"; bgcolor="#fff3bf"; rank=same; x1 [label="x(t)", fillcolor="#a5d8ff"]; x2 [label="x(t+1)", fillcolor="#a5d8ff"]; xdots [label="...", shape=plaintext]; xW [label="x(t+W-1)", fillcolor="#a5d8ff"]; x1 -> x2 -> xdots -> xW [style=invis]; } subgraph cluster_rnn { label = "RNN (LSTM/GRU)"; bgcolor="#d0bfff"; node [fillcolor="#e599f7"]; rnn1 [label="步骤 1"]; rnn2 [label="步骤 2"]; rnndots [label="...", shape=plaintext]; rnnW [label="步骤 W\n(使用最终状态)"]; } subgraph cluster_output { label = "输出 (H 步)"; bgcolor="#ffc9c9"; node [fillcolor="#ffa8a8"]; dense [label="全连接层(H)"]; p1 [label="预测 x(t+W)"]; p2 [label="预测 x(t+W+1)"]; pdots [label="...", shape=plaintext]; pH [label="预测 x(t+W+H-1)"]; p1 -> p2 -> pdots -> pH [style=invis]; // Alignment } edge [arrowhead=vee, color="#495057"]; x1 -> rnn1; x2 -> rnn2; xW -> rnnW; rnn1 -> rnn2 [label="h1", fontsize=9]; rnn2 -> rnndots [label="h2", fontsize=9]; rnndots -> rnnW [label="...", fontsize=9]; rnnW -> dense [label="hW (最终隐藏状态)", fontsize=9]; dense -> p1 [style=invis]; // Connect Dense to the group logically dense -> p2 [style=invis]; dense -> pH [style=invis]; // Indicate Dense outputs all predictions {rank=same; dense; p1; p2; pdots; pH;} edge [style=dashed, arrowhead=none]; dense -> p1; dense -> p2; dense -> pH; }多对多(向量输出 / 单次预测)架构。最终的RNN状态输入到一个全连接层,该层一次性输出整个预测范围$H$。序列输出 / 自回归预测:这种方法通常涉及一个多对一模型,该模型训练用于预测 仅仅 提前一步($H=1$)。为了预测多个步骤,它迭代地进行:使用$(x_t, \ldots, x_{t+W-1})$预测$x_{t+W}$。将预测的$\hat{x}{t+W}$用作下一步的输入。使用$(x{t+1}, \ldots, x_{t+W-1}, \hat{x}{t+W})$预测$x{t+W+1}$。将此过程重复$H$次,将先前的预测反馈回输入窗口。优点: 只需要一个单步预测模型。缺点: 误差可能在预测范围内累积,因为预测是基于先前的预测。序列到序列(编码器-解码器):使用两个RNN:一个 编码器 处理输入序列$(x_t, \ldots, x_{t+W-1})$并将其总结为一个上下文向量(通常是最终隐藏状态)。一个 解码器 RNN接收此上下文向量并逐步生成输出序列$(\hat{x}{t+W}, \ldots, \hat{x}{t+W+H-1})$。这是一种更高级的架构,通常用于输入和输出序列具有复杂关系或不同长度的情况。它将在序列到序列模型的背景下后续介绍。对于标准预测,前两种方法是更常用的起始点。选择合适的架构单步提前预测($H=1$): 使用多对一架构。它更简单,并直接为此任务优化。多步预测($H > 1$):**向量输出(单次预测)**方法通常更容易实现和初步训练。它直接优化以预测整个范围。自回归方法有时表现良好,特别是如果单步动态被很好地捕捉,但要留意误差累积。它可能需要仔细调整。对于更复杂的多步预测问题,可以考虑序列到序列模型,特别是如果需要长预测范围或在每个预测步骤中涉及辅助输入特征。无论何种架构,请记住输入数据需要适当地进行窗口化和归一化。回溯窗口$W$和预测范围$H$的选择是能影响性能的超参数,应根据问题选择,并可能使用验证数据进行调整。