RNN,即循环神经网络(Recurrent Neural Network),是一种用于处理序列数据的神经网络。与传统的前馈神经网络不同,RNN具有内部的记忆机制,可以记住之前输入的信息,并将其用于当前输入的处理中。这使得RNN非常适合于处理和预测时间序列数据、自然语言处理、语音识别等任务。
整体来看,RNN有被取代的趋势,但RNN是理解大模型的核心,大模型里的很多理念都来源于RNN。学习RNN不是为了怎么使用,而是分析为何消亡,这样才能更深刻的理解 transformer为何活着, transformer是点对点解决RNN的问题。
RNN的基本结构
在RNN中,信息不仅沿着输入向量传播,还从一个步骤传递到下一个步骤。这意味着每个输出都依赖于之前的计算,并且所有计算都涉及相同的参数集。这种结构允许网络学习到序列中的模式,即使这些模式出现在不同的时间点上。
RNN的问题
-
梯度消失/爆炸问题:在训练深度RNN时,可能会遇到梯度消失或梯度爆炸的问题。这是因为通过时间的反向传播(BPTT)会将错误信号传回很多时间步长,导致权重更新变得非常小(消失)或非常大(爆炸)。这使得网络难以学习长期依赖关系。
-
无法有效捕捉长距离依赖:由于上述梯度问题,标准RNN在处理非常长的序列时可能表现不佳,尤其是在需要记住很久之前的信息时。
解决方案
为了克服这些问题,研究人员开发了多种改进的RNN模型:
-
长短期记忆网络(LSTM, Long Short-Term Memory):LSTM是一种特殊的RNN类型,它能够学习长期依赖关系。LSTM通过引入“门”的概念来控制信息流,包括遗忘门、输入门和输出门,从而有效地解决了梯度消失问题。
-
门控循环单元(GRU, Gated Recurrent Unit):GRU是另一种改进型RNN,它简化了LSTM的设计,将遗忘门和输入门合并为更新门,并且没有单独的输出门。尽管结构更简单,但在许多任务中,GRU的表现与LSTM相当,有时甚至更好。
-
双向RNN(BiRNN, Bidirectional RNN):BiRNN由两个RNN组成,一个处理正向的时间序列,另一个处理反向的时间序列。这允许网络同时考虑过去和未来的信息,对于某些任务来说是非常有用的。
-
深度RNN:通过堆叠多个RNN层,可以创建深度RNN,以提高模型的能力。然而,这也增加了模型的复杂性和计算需求。
RNN的使用
1. RNN 要解决什么问题?
- 时序信号的抽取特征
- 顺序
2. RNN 的解决方法
- 循环
- 逐个处理
- 前后依赖
- 后一项直接依赖前一项,间接依赖前面所有的项
- 前后进行【手递手】传递
- 中间隐藏状态
3. RNN API 如何调用?
- nn.RNN
- 自动挡(自动循环)
- 用于编码器
- nn.RNNCell
- 手动挡(手动循环)
- 用于解码器
时序共享,每一步用的是同一个A;进来多少步就出去多少步,即(X)进来70步,出去的(h)也是70步。hn是最后一步,最后一步很重要,我们经常用最后一步去解决很多问题。因为理论上所有步的信息都聚合到了最后一步上。h0也需要写上,因为解码自回归的时候需要。
所有的网络都是在处理特征,这是人工智能的核心问题,不论是全链接、卷积、RNN循环和transformer,它们都是在处理特征,所以我们只关心特征的维度。
import torch
from torch import nn
"""
1. Simple RNN
"""
"""
时序类数据结构:[seq_len, batch_size, embedding_dim]
seq_len:序列长度
batch_size:批量数据有多少个
embedding_dim:嵌入维度,也就是每个词是多大的向量
"""
# 比如一个短信70个字,3个短信,每个字是256个维度
X = torch.randn(70, 3, 256)
h0 = torch.zeros(1, 3, 512, dtype=torch.float32)
X.shape, h0.shape
# 构建一个循环神经网络,输入层维度256,隐藏层维度512
rnn = nn.RNN(input_size=256, hidden_size=512)
# 调用RNN
out, hn = rnn(X, h0)
# [seq_len, batch_size, hidden_size]
out.shape
# [1, batch_size, hidden_size]
hn.shape
LSTM 博客
http://colah.github.io/posts/2015-08-Understanding-LSTMs/