前馈网络,包括多层感知机(MLP),独立处理输入。如果你两次输入相同的MLP,它会产生相同的输出,不知晓任何之前的交互。这适用于许多任务,但对于顺序很重要的数据呢?考虑预测句子中的下一个词、分析股市趋势或语音转录。含义或预测通常很大程度上依赖于之前的内容。前馈网络缺乏记住序列中过去信息的固有机制。循环神经网络(RNN)专门设计来处理这类序列信息。其核心原理是循环:逐个处理序列元素,同时保持内部记忆,通常称为隐藏状态。设想阅读一个句子。你不会孤立地处理每个词。你对当前词的理解受已读词语的影响。RNN模拟了这一过程。在每一步(例如,句子中的每个词或时间序列中的每个点),RNN基于两项内容进行计算:当前输入元素(在时间步$t$的$x_t$)。来自上一步的隐藏状态($h_{t-1}$)。此计算生成一个新的隐藏状态($h_t$),它捕获了当前输入的信息以及过去的有关上下文。这个隐藏状态$h_t$随后传递到下一个时间步($t+1$),作为网络目前所见内容的记忆。这种“循环”机制,即一步的输出(通过隐藏状态)反馈到下一步的输入中,是使网络循环的原因。重要的一点是,在每个时间步都使用相同的权重和偏置进行计算。这种参数共享使得RNN高效,并使它们能够泛化不同长度序列中不同位置的模式。我们可以用数学方式表示时间步$t$的隐藏状态更新。一种常用形式是使用激活函数(如tanh或ReLU),应用于当前输入和前一个隐藏状态的组合:$$ h_t = f(W_{hh} h_{t-1} + W_{xh} x_t + b_h) $$其中:$h_t$ 是当前时间步 $t$ 的隐藏状态。$h_{t-1}$ 是前一时间步 $t-1$ 的隐藏状态。$x_t$ 是当前时间步 $t$ 的输入。$W_{hh}$ 是应用于前一隐藏状态的权重矩阵。$W_{xh}$ 是应用于当前输入的权重矩阵。$b_h$ 是隐藏状态计算的偏置项。$f$ 是一个非线性激活函数(例如,tanh)。网络在每个时间步也可能产生输出$y_t$,通常基于当前的隐藏状态计算:$$ y_t = g(W_{hy} h_t + b_y) $$其中:$y_t$ 是时间步 $t$ 的输出。$W_{hy}$ 是连接隐藏状态到输出的权重矩阵。$b_y$ 是输出偏置项。$g$ 是另一个激活函数(例如,用于分类的softmax)。从视觉上看,我们可以将循环随时间“展开”。下图展示了一个展开为三个时间步的简单RNN。注意隐藏状态$h$如何从一步传递到下一步,沿着序列携带信息。digraph G { rankdir=LR; node [shape=rect, style="filled", fillcolor="#e9ecef", fontname="sans-serif"]; edge [fontname="sans-serif"]; subgraph cluster_t_minus_1 { label="时间 t-1"; bgcolor="#f8f9fa"; style=dashed; xt_1 [label="x[t-1]", shape=circle, fillcolor="#a5d8ff"]; ht_1 [label="h[t-1]", fillcolor="#ffe066"]; yt_1 [label="y[t-1]", shape=circle, fillcolor="#b2f2bb"]; xt_1 -> ht_1 [label=" W_xh"]; ht_1 -> yt_1 [label=" W_hy"]; } subgraph cluster_t { label="时间 t"; bgcolor="#f8f9fa"; style=dashed; xt [label="x[t]", shape=circle, fillcolor="#a5d8ff"]; ht [label="h[t]", fillcolor="#ffe066"]; yt [label="y[t]", shape=circle, fillcolor="#b2f2bb"]; xt -> ht [label=" W_xh"]; ht -> yt [label=" W_hy"]; } subgraph cluster_t_plus_1 { label="时间 t+1"; bgcolor="#f8f9fa"; style=dashed; xt_p1 [label="x[t+1]", shape=circle, fillcolor="#a5d8ff"]; ht_p1 [label="h[t+1]", fillcolor="#ffe066"]; yt_p1 [label="y[t+1]", shape=circle, fillcolor="#b2f2bb"]; xt_p1 -> ht_p1 [label=" W_xh"]; ht_p1 -> yt_p1 [label=" W_hy"]; } ht_prev [label="h[t-2]", shape=plaintext, fontcolor="#ced4da"]; ht_prev -> ht_1 [label=" W_hh", style=dashed, fontcolor="#adb5bd", color="#adb5bd"]; ht_1 -> ht [label=" W_hh", style=dashed]; ht -> ht_p1 [label=" W_hh", style=dashed]; ht_p1 -> ht_next [label=" W_hh", style=dashed, fontcolor="#adb5bd", color="#adb5bd"]; ht_next [label="h[t+2]", shape=plaintext, fontcolor="#ced4da"]; }一个RNN单元随时间展开。隐藏状态h作为记忆,将信息从一个时间步传递到下一个时间步。相同的权重(W_hh、W_xh、W_hy)应用于每个时间步。这种循环结构,以演进的隐藏状态为核心,使RNN能够捕获序列中元素之间的依赖关系,使它们适用于涉及自然语言、时间序列数据及其他上下文重要的有序输入任务。