在人工智能的领域中,神经网络是推动技术发展的核心力量。今天,让我们深入探讨循环神经网络(RNN)
一、神经网络基础
(1)什么是神经网络
神经网络,又称人工神经网络,其设计灵感源于人类大脑的运作模式。它由众多被称为“节点”的处理单元构成,这些节点之间相互传递数据,恰似大脑中的神经元传递电脉冲。
在机器学习领域,神经网络扮演着关键角色。尤其是在深度学习中,它能够从无标签数据中提取有价值的信息,实现诸如识别照片中未知物品等复杂任务。
(2)神经网络的学习过程
神经网络的学习过程是一个迭代的过程,主要包括前向传播、损失函数计算和反向传播三个关键步骤。
在前向传播阶段,数据从输入层开始,依次经过隐藏层的处理,最终到达输出层。在这个过程中,每个神经元都会根据输入数据和自身的权重、偏差进行计算。权重决定了输入数据的相对重要性,而偏差则影响着神经元的激活程度。最初,我们会为权重和偏差赋予非零的随机值,这就是网络的参数初始化过程。
计算完成后,输出层的结果即为前向传播的最终输出,我们将其与真实值(ground-truth value)进行对比,通过损失函数(loss function)来衡量模型的性能。损失函数计算预测值与真实值之间的误差,常见的损失函数包括均方误差(Mean Squared Error,MSE)、平均绝对误差(Mean Absolute Error,MAE)、二元交叉熵(Binary Cross-entropy)、多类交叉熵(Multi-class Cross-entropy)等,它们分别适用于不同类型的任务,如回归问题或分类问题。
如果前向传播得到的预测值与真实值相差较大,说明我们需要调整网络的参数以降低损失函数的值。这就引出了反向传播过程,在这个阶段,我们会计算损失函数关于模型参数的梯度,并利用优化算法(如梯度下降 等)来更新参数。
二、循环神经网络
(1)RNN的定义与应用场景
循环神经网络(RNN)是一种专门设计用于处理顺序数据的神经网络架构。与传统的前馈神经网络不同。
RNN 能够通过内部状态来记忆之前输入的信息,从而更好地处理序列数据。在处理时间序列数据、语言建模或视频序列等任务时,RNN 表现出了独特的优势,因为这些任务中输入数据的顺序至关重要。
(2)顺序数据的概念
顺序数据是指具有特定顺序且顺序会影响数据含义的信息。
“The quick brown fox jumps over the lazy dog.”
每个单词都是数据的一部分,单词的顺序决定了句子的语义。如果将单词顺序打乱,句子就会变得毫无意义。
其他常见的顺序数据还包括时间序列数据(如股票价格、温度读数、网站流量等)和语音信号等。
(3)RNN与前馈神经网络的对比
- 前馈神经网络:数据在网络中仅沿一个方向流动,从输入层到输出层,不存在反馈回路。这种架构适用于模式识别等任务,但在处理顺序数据时存在局限性,因为它无法利用之前的输入信息。
- 循环神经网络:通过网络中的反馈回路,信号可以在前后时间步之间传递,使得网络能够记住之前的输入,从而更好地处理顺序数据。
(4)为什么使用RNN
传统的人工神经网络(ANN)在处理顺序数据(如文本)时面临挑战,因为它们要求输入数据具有固定的大小。在 ANN 中,每个输入被独立处理,无法捕捉元素之间的顺序和关系。
三、RNN的架构
3.1 RNN的时间展开
RNN的关键在于其内部状态或记忆,它能够跟踪已处理的数据,可以将 RNN 视为多个前馈神经网络在时间上的链式执行,每个时间步都有一个相同的网络结构在处理输入数据。
- 输入层:在每个时间步,输入层接收一个输入数据,并将其传递给隐藏层。与前馈网络不同,RNN 的输入是逐个时间步进行处理的,这使得网络能够适应动态变化的数据序列
- 隐藏状态:隐藏层在 RNN 中起着核心作用,它不仅处理当前输入,还会保留之前输入的信息。隐藏状态 h t h_t ht 在时间步 t t t是根据当前输入 X t X_t Xt和前一个隐藏状态 h t − 1 h_{t - 1} ht−1 计算得出的,计算公式如下:
h t = t a n h ( W ∗ [ h t − 1 , X t ] + b h ) h_t = tanh(W*[h_{t-1},X_t] + b_h) ht=tanh(W∗[ht−1,Xt]+bh)
- t a n h tanh tanh: 是非线性激活函数
- W W W:隐藏层的权重矩阵
- b h b_h bh: 隐藏层的偏差向量
- 输出序列:
RNN 的输出方式非常灵活,可以在每个时间步都产生输出(多对多),也可以在序列结束时产生一个单一输出(多对一),甚至可以从单个输入生成一个序列输出(一对多)。例如,对于多对多的 RNN,时间步 t t t的输出 O t O_t Ot 可以通过以下公式计算:
O t = V ∗ h t + b o O_t = V*h_t+b_o Ot=V∗ht+bo
- V V V: 输出层的权重矩阵
- b o b_o bo:输出层的偏差向
3.2 RNN的关键操作
3.2.1 前向传播
在 RNN 的前向传播过程中,对于每个时间步 t t t,网络会结合当前输入 X t X_t Xt 和前一个隐藏状态 h t − 1 h_{t - 1} ht−1 来计算新的隐藏状态 h t h_t ht和输出 O t O_t Ot。这个过程中会使用一些非线性激活函数(如 s i g m o i d sigmoid sigmoid 或 t a n h tanh tanh)来引入非线性变换.
def forward(self, inputs):h = np.zeros((1, self.hidden_size))self.last_inputs = inputsself.last_hs = {0: h}for i, x in enumerate(inputs):x = x.reshape(1, -1)h = np.tanh(np.dot(x, self.weights_ih) + np.dot(h, self.weights_hh) + self.bias_h)self.last_hs[i + 1] = hy = np.dot(h, self.weights_ho) + self.bias_oself.last_outputs = yreturn y
3.2.2 反向传播时间(BPTT)
与传统的反向传播不同,BPTT 会在时间上展开整个数据序列,并在每个时间步计算梯度,然后利用这些梯度来调整权重,以降低总体损失。
假设我们有一个长度为 T T T 的时间序列数据,在每个时间步 t t t 都有一个简单的损失函数 L t L_t Lt(如回归任务中的均方误差),那么总损失 L t o t a l L_{total} Ltotal 是每个时间步损失的总和:
L t o t a l = Σ t = 1 T L t L_{total}=\Sigma_{t=1}^{T}L_t Ltotal=Σt=1TLt
为了更新权重,我们需要计算 L t o t a l L_{total} Ltotal 关于权重的梯度。对于权重矩阵 U U U(输入到隐藏层)、 W W W(隐藏层到隐藏层)和 V V V(隐藏层到输出层),梯度的计算公式如下:
def backprop(self, d_y, learning_rate, clip_value=1):n = len(self.last_inputs)d_y_pred = (self.last_outputs - d_y) / d_y.sized_Whh = np.zeros_like(self.weights_hh)d_Wxh = np.zeros_like(self.weights_ih)d_Why = np.zeros_like(self.weights_ho)d_bh = np.zeros_like(self.bias_h)d_by = np.zeros_like(self.bias_o)d_h = np.dot(d_y_pred, self.weights_ho.T)for t in reversed(range(1, n + 1)):d_h_raw = (1 - self.last_hs[t] ** 2) * d_hd_bh += d_h_rawd_Whh += np.dot(self.last_hs[t - 1].T, d_h_raw)d_Wxh += np.dot(self.last_inputs[t - 1].reshape(1, -1).T, d_h_raw)d_h = np.dot(d_h_raw, self.weights_hh.T)for d in [d_Wxh, d_Whh, d_Why, d_bh, d_by]:np.clip(d, -clip_value, clip_value, out=d)self.weights_ih -= learning_rate * d_Wxhself.weights_hh -= learning_rate * d_Whhself.weights_ho -= learning_rate * d_Whyself.bias_h -= learning_rate * d_bhself.bias_o -= learning_rate * d_by
3.2.3 权重更新
在计算出梯度后,我们使用优化算法(如随机梯度下降)来更新权重。权重更新的公式如下: