AI-For-Beginners 课程实战:用循环神经网络(RNN)与 LSTM 建模文本序列顺序
教程人工智能机器学习深度学习【免费下载链接】AI-For-Beginners12 Weeks, 24 Lessons, AI for All!项目地址https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners点击查看免费下载循环神经网络RNN解决了词向量嵌入聚合后丢失词序信息的问题是文本生成、机器翻译、问答等复杂 NLP 任务的基础架构。本文基于AI-For-Beginners课程 第 16 课循环神经网络系统讲解 RNN 的数学原理、LSTM 门控机制、双向与多层网络结构并结合仓库中的 PyTorch 与 TensorFlow 两个实战 Notebook给出可直接运行的文本分类实现。为什么需要 RNN词嵌入丢失了顺序在课程前几节13-TextRep 与 14-Embeddings中我们使用嵌入表示文本并在嵌入之上叠加一个简单的线性分类器。这种架构能捕获句子中单词的聚合语义但不考虑词的顺序——因为嵌入上的聚合操作如取平均已经把词序信息从原始文本中抹掉了。无法建模词序的模型也就无法解决更复杂、更含歧义的任务例如文本生成text generation或问答question answering。例如not like与like not的聚合结果可能相同但语义完全不同。要捕获文本序列的含义就需要另一种神经网络架构循环神经网络Recurrent Neural NetworkRNN。使用 RNN 时我们把句子一个符号一个符号地送入网络网络产出某种状态state再将这个状态与下一个符号一起再次送入网络如此往复。上图左侧是 RNN 的展开形式右侧是更紧凑的循环反馈形式。注意所有 RNN 块共享同一套权重shareable weights。RNN 的核心工作机制给定输入 token 序列 X₀, X₁, ..., XₙRNN 创建一串神经网络块并通过反向传播backpropagation端到端地训练这一串块每个网络块接收一对输入 (Xᵢ, Sᵢ)输出 Sᵢ₊₁最终状态 Sₙ或输出 Yₙ送入线性分类器产生分类结果所有网络块的权重完全相同整个序列只用一次反向传播即可完成训练。因为状态向量 S₀, ..., Sₙ 在网络中被逐级传递RNN 能够学习词之间的顺序依赖。例如当序列中某处出现单词not时网络可以学会对状态向量中的某些元素取反从而表达否定语义。解剖一个 RNN 单元Cell一个简单的 RNN 单元接收前一状态 Sᵢ₋₁ 和当前符号 Xᵢ 作为输入需要产出输出状态 Sᵢ在生成式网络场景中有时还关心另一个输出 Yᵢ。单元内部有两套权重矩阵权重矩阵W变换输入符号权重矩阵H变换输入状态。网络输出按下式计算Sᵢ σ(W × Xᵢ H × Sᵢ₋₁ b)其中 σ 是激活函数如 tanhb 是额外偏置。TensorFlow NotebookRNNTF.ipynb给出了等价的数学形式Sᵢ₊₁ f(W_H × Sᵢ W_I × Xᵢ b)对于生成/翻译这类需要在每一步都产出值的任务还会额外引入输出矩阵 W_OYᵢ f(W_O × Sᵢ b_O)。嵌入层如何影响矩阵维度在很多实际场景中输入 token 在进入 RNN 前会先经过**嵌入层embedding layer**以降低维度。此时若输入向量的维度为emb_size状态向量的维度为hid_size则矩阵 W 的尺寸为emb_size × hid_size矩阵 H 的尺寸为hid_size × hid_size。长短期记忆网络LSTM解决梯度消失经典 RNN 的主要问题之一是所谓的梯度消失vanishing gradients问题。由于 RNN 通过一次反向传播端到端训练误差很难传播到网络最前面的层因此网络无法学习相距很远 token 之间的关系。解决这一问题的方法之一是引入显式状态管理即所谓的门gates。最著名的两类门控架构是长短期记忆网络Long Short Term MemoryLSTM和门控循环单元Gated Recurrent UnitGRU。LSTM 的组织方式与 RNN 类似但有两个状态在层与层之间传递实际状态Ccell state隐藏向量Hhidden vector。在每个单元中隐藏向量 Hᵢ 与输入 Xᵢ 拼接二者通过门控制状态 C 的更新。每个门都是带sigmoid 激活输出范围 [0,1]的神经网络与状态向量相乘时可以视作按位掩码bitwise mask。LSTM 包含以下三个门对应上图中从左到右的顺序门作用遗忘门forget gate接收隐藏向量决定向量 C 中的哪些分量需要遗忘、哪些需要保留传递输入门input gate从输入向量与隐藏向量中取信息插入到状态中输出门output gate先经带 tanh 激活的线性层变换状态再用隐藏向量 Hᵢ 选择部分分量产出新状态 Cᵢ₊₁把状态 C 想象成一组开关状态 C 的分量可以被看作可开关的标志位flags。例如当序列中出现名字Alice时我们推测它指代女性角色于是在状态中把句子中有阴性名词的标志置位当之后出现短语and Tom时再把有复数名词的标志置位。通过这种状态操纵模型可以跟踪句子各部分的语法属性——这正是 LSTM 能够在长序列中保留信息的原因。双向与多层 RNN前面讨论的循环网络都从序列开头向末尾单向运行这与我们阅读、听语音的方式一致看起来自然。但许多实际场景中我们可以随机访问输入序列因此把循环计算两个方向各跑一遍往往更合理——这类网络称为双向 RNNbidirectional RNN。使用双向网络时需要两个隐藏状态向量每个方向各一个。无论是单向还是双向循环网络都会捕获序列中的某些模式并将其存入状态向量或传给输出。与卷积网络类似我们可以在第一层循环网络之上再叠加一层循环网络从第一层提取的低层模式中构建更高层的模式——这就是多层 RNNmulti-layer RNN由两个或更多循环网络组成前一层的输出作为下一层的输入。实战一PyTorch 实现 RNN / LSTM 文本分类仓库提供了 RNNPyTorch.ipynb 与配套工具模块 torchnlp.py使用经典的AG_NEWS新闻四分类数据集类别World、Sports、Business、Sci/Tech进行实验。数据加载与预处理工具模块 torchnlp.py 中load_dataset()负责加载数据集并构建词表train_dataset, test_dataset torchtext.datasets.AG_NEWS(root./data) classes [World, Sports, Business, Sci/Tech] counter collections.Counter() for (label, line) in train_dataset: counter.update(torchtext.data.utils.ngrams_iterator(tokenizer(line), ngramsngrams)) vocab torchtext.vocab.vocab(counter, min_freqmin_freq)encode()将文本 token 映射为词表索引padify()把一个 minibatch 中的所有序列填充到相同长度不足部分补 0使其可以组成批量张量。简单 RNN 分类器在 PyTorch 中单个循环单元对应torch.nn.RNNCell由多个单元组成的层对应torch.nn.RNN。RNNPyTorch.ipynb中定义了如下的RNNClassifierclass RNNClassifier(torch.nn.Module): def __init__(self, vocab_size, embed_dim, hidden_dim, num_class): super().__init__() self.hidden_dim hidden_dim self.embedding torch.nn.Embedding(vocab_size, embed_dim) self.rnn torch.nn.RNN(embed_dim, hidden_dim, batch_firstTrue) self.fc torch.nn.Linear(hidden_dim, num_class) def forward(self, x): batch_size x.size(0) x self.embedding(x) x, h self.rnn(x) return self.fc(x.mean(dim1))关键点这里出于简洁使用了未预训练的嵌入层如需更好效果可以换成上一单元介绍的 Word2Vec / GloVe 预训练嵌入torch.nn.RNN层返回两个输出x是每一步单元输出的序列h是序列最后一个元素的最终隐藏状态我们取所有步输出的均值x.mean(dim1)再送入全连接分类层。训练配置batch_size16、embed_dim64、hidden_dim32、学习率lr0.001。注意RNN 按序列长度展开后参与反向传播的层数很多训练困难需要较小的学习率与较大的数据集强烈建议使用 GPU。LSTM 分类器只改一层LSTM 的内部结构虽然复杂但 PyTorch 把实现封装在torch.nn.LSTMCell与torch.nn.LSTM中因此分类器结构与简单 RNN 几乎一致class LSTMClassifier(torch.nn.Module): def __init__(self, vocab_size, embed_dim, hidden_dim, num_class): super().__init__() self.hidden_dim hidden_dim self.embedding torch.nn.Embedding(vocab_size, embed_dim) self.embedding.weight.data torch.randn_like(self.embedding.weight.data) - 0.5 self.rnn torch.nn.LSTM(embed_dim, hidden_dim, batch_firstTrue) self.fc torch.nn.Linear(hidden_dim, num_class) def forward(self, x): x self.embedding(x) x, (h, c) self.rnn(x) return self.fc(h[-1])注意forward中取的是h[-1]——即最后一层、序列末尾的隐藏状态LSTM 返回的(h, c)中h是所有层各方向的最后隐藏状态。进阶Packed Sequence 高效训练用padify填充的序列会带来问题为填充项额外创建的 RNN 单元也参与训练却不携带有效信息造成浪费。PyTorch 提供了packed sequence打包序列机制来只训练到序列的真实长度。假设一个 minibatch 为[[1,2,3,4,5], [6,7,8,0,0], [9,0,0,0,0]]其中 0 是填充值真实长度向量为[5,3,1]。打包后表示为一个向量[1,6,9,2,7,3,8,4,5]加上长度向量[5,3,1]训练时按步收缩 minibatch避免无效计算。RNNPyTorch.ipynb中的LSTMPackClassifier展示了完整用法class LSTMPackClassifier(torch.nn.Module): def __init__(self, vocab_size, embed_dim, hidden_dim, num_class): super().__init__() self.hidden_dim hidden_dim self.embedding torch.nn.Embedding(vocab_size, embed_dim) self.embedding.weight.data torch.randn_like(self.embedding.weight.data) - 0.5 self.rnn torch.nn.LSTM(embed_dim, hidden_dim, batch_firstTrue) self.fc torch.nn.Linear(hidden_dim, num_class) def forward(self, x, lengths): x self.embedding(x) pad_x torch.nn.utils.rnn.pack_padded_sequence( x, lengths, batch_firstTrue, enforce_sortedFalse) pad_x, (h, c) self.rnn(pad_x) x, _ torch.nn.utils.rnn.pad_packed_sequence(pad_x, batch_firstTrue) return self.fc(h[-1])注意pack_padded_sequence要求长度向量位于 CPU 上因此训练函数中需要避免把长度数据搬到 GPU——这正是torchnlp.py中train_epoch_emb(..., use_pack_sequenceTrue)参数的作用见 torchnlp.py。RNN、LSTM、GRU 三类循环层都原生支持打包输入。Notebook 中的实测效果复现结果参考Notebook 中记录的训练日志显示AG_NEWS 训练集一个 epoch 内简单 RNN 分类器处理约 10.8 万样本后准确率约 0.806LSTM 分类器约 11.8 万样本后准确率约 0.773早期上升缓慢是正常现象Packed LSTM约 11.8 万样本后准确率约 0.814同时省去了填充 token 带来的无效计算。这些数字来自 Notebook 运行输出的复现结果仅供对照参考实际效果会因设备、随机种子与训练时长而不同。实战二TensorFlow/Keras 实现 RNN 与双向 LSTM仓库还提供了 RNNTF.ipynbKeras 版本使用tfds.load(ag_news_subset)加载数据集。首次运行前需要安装tensorflow_datasets并预取数据import tensorflow as tf from tensorflow import keras import tensorflow_datasets as tfds physical_devices tf.config.list_physical_devices(GPU) if len(physical_devices) 0: tf.config.experimental.set_memory_growth(physical_devices[0], True)简单 RNN 分类器Keras 版在 Keras 中简单循环层对应keras.layers.SimpleRNN。模型结构为TextVectorization词表大小 20000→Embedding(20000, 64)→SimpleRNN(16)→Dense(4, activationsoftmax)仅用新闻标题title训练model keras.models.Sequential([ vectorizer, keras.layers.Embedding(vocab_size, embed_size), keras.layers.SimpleRNN(16), keras.layers.Dense(4, activationsoftmax) ]) model.compile(losssparse_categorical_crossentropy, metrics[acc], optimizeradam)Notebook 记录该标题模型验证准确率约 0.80——只训练标题会限制精度上限。处理变长序列MaskingTextVectorization会自动用填充 token 对齐序列但这些 token 同样参与训练可能干扰收敛。Notebook 给出两种缓解手段用tf.data.experimental.bucket_by_sequence_length按序列长度分桶、重排数据集使用掩码masking在Embedding层上设置mask_zeroTrue或在模型中显式加入Masking层。在标题描述全文本上启用掩码后模型验证准确率提升到约 0.88Notebook 记录值。Keras 版 LSTM 与双向多层 LSTMLSTM 在 Keras 中同样只是一行替换——把SimpleRNN换成keras.layers.LSTM。双向网络则需要用keras.layers.Bidirectional包装循环层该包装层内部复制两层并将其中一份的go_backwards设为True。多层 RNN 在 Keras 中就是简单地把多个循环层堆叠除最后一层外其余循环层都必须设置return_sequencesTrue以输出每一步的中间状态。Notebook 最终构建了两层双向 LSTMmodel keras.models.Sequential([ vectorizer, keras.layers.Embedding(vocab_size, 128, mask_zeroTrue), keras.layers.Bidirectional(keras.layers.LSTM(64, return_sequencesTrue)), keras.layers.Bidirectional(keras.layers.LSTM(64)), keras.layers.Dense(4, activationsoftmax) ])这是 Notebook 中精度最高的模型训练中即达约 0.87代价是训练时间明显更长。小结与进阶方向RNN 不仅能用于序列分类还能胜任更多任务文本生成、机器翻译等——这些将在课程的下一单元17-GenerativeNetworks、18-Transformers中继续探讨。本课程还布置了一个配套作业assignment.md使用本课的 PyTorch 或 TensorFlow Notebook换用自己的数据集例如天气相关的推文数据集使用时注明出处重跑并改写 Notebook记录你的发现。这是把会跑示例变成会用模型的关键一步。延伸阅读建议深入理解 LSTM 内部机制推荐研读 Christopher Olah 的经典文章Understanding LSTM Networks阅读 GRU门控循环单元相关资料对比其与 LSTM 的差异与适用场景关注 RNN 的注意力扩展Show, Attend and Tell图像描述中的视觉注意力与Grid LSTM等变体理解循环架构如何向生成任务与长程依赖演进。运行环境提示本课依赖 PyTorch/TensorFlow 生态。本地安装可参考 NLP 分节的依赖清单 requirements-pytorch.txt 与 requirements-tf.txt训练较大模型时建议使用 GPU并注意通过调整 minibatch 大小控制显存占用。赞分享教程人工智能机器学习深度学习【免费下载链接】AI-For-Beginners12 Weeks, 24 Lessons, AI for All!项目地址https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners点击查看免费下载相关推荐AI-For-Beginners 课程指南用循环神经网络RNN与 LSTM 建模文本序列顺序AI For Beginners 课程指南用循环神经网络RNN与 LSTM 建模文本序列顺序 本篇文章以 AI For Beginners 课程第 5 单教程人工智能机器学习深度学习AI-For-Beginners 课程解读循环神经网络RNN与 LSTM 序列建模实战AI For Beginners 课程解读循环神经网络RNN与 LSTM 序列建模实战 导读 本篇文章基于 AI For Beginners 开源课程第教程人工智能机器学习深度学习循环神经网络RNN与 LSTM 实战指南基于 AI-For-Beginners 的 NLP 序列建模教程循环神经网络RNN与 LSTM 实战指南基于 AI For Beginners 的 NLP 序列建模教程 导读 本教程来自开源课程 AI For Begi教程人工智能机器学习深度学习创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考