15 从零学NLP系列教程:只生成语言模型之RNN与LSTM
系列进度
自然语言处理入门 · 第 15 / 30 篇
整理说明
这篇内容怎么整理
郭震 · 2026-06-04
阅读路线
先按这条路线读
先抓住主线,再回到代码、配置和图文细节,读起来会更稳。
RNN 用隐藏状态处理序列,LSTM 用门控机制缓解长依赖问题。它们是理解后续 Transformer 的重要对照。
我会画出每个时间步的信息流。只背公式容易混,看到状态如何传递才知道模型在记什么。
在前一篇中,我们讨论了语言模型中的N-gram模型,它通过基于词的n个前一个词的条件概率来捕捉语言的统计特性。然而,N-gram模型存在词汇稀疏和上下文信息不足的问题。在这篇文章中,我们将深入探讨基于循环神经网络(RNN)和长短期记忆(LSTM)网络的语言模型,这些模型能够更好地处理序列数据,并有效捕捉长程依赖关系。
循环神经网络(RNN)
RNN是一种用于处理序列数据的神经网络架构。在传统的神经网络中,输入的每一项都是独立的,而在RNN中,网络的输出不仅与当前输入有关,还与之前的状态(即之前的输入)相关。这种结构使得RNN能够处理可变长度的序列。
学习 RNN 与 LSTM 时,先看隐藏状态、输入序列、门控结构、梯度问题和语言模型预测目标。
RNN的工作原理
在每个时间步骤,RNN接收输入向量和前一个隐藏状态,并生成当前的隐藏状态. 这个过程可以用以下公式表示:
其中, 和 是权重矩阵,是偏置项,是激活函数(通常使用tanh或ReLU)。
最后,RNN会通过一个输出层生成当前时间步骤的输出:
示例:简单RNN语言模型
假设我们要生成一句话,“我爱自然语言处理”。在训练过程中,我们可以将这句话转换为一个字符序列。
import numpy as np
# 简单的字符到索引的映射
chars = '我爱自然语言处理'
char_to_idx = {ch: i for i, ch in enumerate(chars)}
idx_to_char = {i: ch for i, ch in enumerate(chars)}
# 超参数
hidden_size = 10
learning_rate = 0.01
# 初始化权重
W_h = np.random.randn(hidden_size, hidden_size) * 0.01
W_x = np.random.randn(hidden_size, len(chars)) * 0.01
W_y = np.random.randn(len(chars), hidden_size) * 0.01
# 训练过程等...
长短期记忆网络(LSTM)
虽然RNN在序列处理方面表现优越,但它在学习长程依赖时常常会遇到“梯度消失”或“梯度爆炸”的问题。为了解决这一问题,LSTM被提出。
读《从零学NLP系列教程:只生成语言模型之RNN与LSTM》时,可以把配图当成路线卡:先看整体顺序,再看每一步为什么这样做,最后再检查边界条件。
LSTM的结构
LSTM通过引入三个门(输入门、遗忘门和输出门)来控制信息的流动,以此来保持长期的记忆。LSTM的工作机制可以通过以下公式描述:
- 遗忘门(Forget Gate):
- 输入门(Input Gate):
- 当前单元状态:
- 输出门(Output Gate):
- 当前隐藏状态:
示例:LSTM语言模型
对于生成语言模型,LSTM模型同样可以应用于序列生成。以下是一个简单的LSTM实现示例:
import numpy as np
class LSTM:
def __init__(self, input_size, hidden_size):
self.W_f = np.random.randn(hidden_size, input_size + hidden_size) * 0.01 # Forget gate
self.W_i = np.random.randn(hidden_size, input_size + hidden_size) * 0.01 # Input gate
self.W_C = np.random.randn(hidden_size, input_size + hidden_size) * 0.01 # Cell gate
self.W_o = np.random.randn(hidden_size, input_size + hidden_size) * 0.01 # Output gate
self.hidden_size = hidden_size
self.C_prev = np.zeros((hidden_size, 1))
self.h_prev = np.zeros((hidden_size, 1))
def forward(self, x):
combined = np.vstack((self.h_prev, x))
f_t = self.sigmoid(np.dot(self.W_f, combined))
i_t = self.sigmoid(np.dot(self.W_i, combined))
C_hat_t = np.tanh(np.dot(self.W_C, combined))
self.C_prev = f_t * self.C_prev + i_t * C_hat_t
o_t = self.sigmoid(np.dot(self.W_o, combined))
self.h_prev = o_t * np.tanh(self.C_prev)
return self.h_prev
@staticmethod
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 使用LSTM生成字符序列...
如果《从零学NLP系列教程:只生成语言模型之RNN与LSTM》还没完全消化,可以从这张卡片的四个动作重新走一遍。
回看《从零学NLP系列教程:只生成语言模型之RNN与LSTM》时,不必一次做大项目,先用一条简单样例确认主线是否清楚。
总结
本文介绍了基于RNN和LSTM的语言模型。与N-gram模型相比,这些模型能够更有效地捕捉序列中的上下文信息和长程依赖性。在下篇文章中,我们将探讨更先进的语言模型 —— Transformers。这一模型引入了自注意力机制,并已成为现代自然语言处理的重要组成部分。通过不断改进,我们希望能实现更高性能的语言生成和理解任务。
继续阅读
从这篇继续找到相关教程
常见问题
读前先确认这三点
从零学NLP系列教程:只生成语言模型之RNN与LSTM适合谁读?
这是 自然语言处理入门 系列第 15 / 30 篇,适合正在学习自然语言处理入门,并且需要把概念落到操作步骤或判断标准里的读者。
读这篇自然语言处理入门教程要多久?
按中文技术文章阅读速度估算,通读大约 4 分钟;如果要跟着复现,建议把命令、配置和结果检查分开做。
这篇文章里的图文节点怎么用?
正文里有 6 个图文节点,可以先用它们抓住流程、配置和判断点,再回到对应段落细读。
分享文章
转发到常用平台
微信/朋友圈可先复制链接
相关教程
从相近问题继续读
继续阅读