- 什么是 LSTM? - 简单理解 LSTM 的工作原理和为什么它重要。
- 应用场景 - LSTM 在哪些领域大放异彩。
- 核心代码实现 - 构建一个完整的 LSTM 模型,并进行训练和预测。
- 进阶技巧 - 如何调优模型、处理更复杂的数据。
- 总结与资源 - 提供进一步学习的资料。
什么是 LSTM?
LSTM 的全称是 Long Short-Term Memory,中文为“长短期记忆网络”,它是一种特殊的循环神经网络,专门为了解决传统 RNN 在处理长序列数据时遇到的 梯度消失 和 梯度爆炸 问题。

传统 RNN 的瓶颈
想象一下你正在读一篇很长的故事,当你读到第 10 段时,你可能已经忘了第 1 段讲的是什么了,传统的 RNN 就是这样,它像一个“健忘”的读者,信息在传递过程中会不断衰减,导致它很难记住早期的、对理解整体至关重要的信息。
LSTM 的“记忆”机制
LSTM 通过精巧的“门控”机制来解决这个问题,你可以把它想象成一个带有“输入门”、“遗忘门”和“输出门”的智能系统。
- 单元状态:这是 LSTM 的核心,可以看作是它的“记忆线”,信息可以相对轻松地在单元状态中流动,只受到微小的线性影响。
- 遗忘门:决定从细胞状态中丢弃什么信息,在处理一个新句子时,这个门可能会决定忘记主语是“他”还是“她”。
- 输入门:决定让多少新的信息存入细胞状态,它会先筛选出需要更新的信息,然后更新到细胞状态中。
- 输出门:决定基于细胞状态输出什么信息,它会对细胞状态进行一次过滤,只输出当前时间步需要的信息。
LSTM 能够学会“重要的长期信息,忘记”不相关的短期信息,从而在处理长序列时表现出色。
应用场景
LSTM 特别适合处理和预测 序列数据,即数据点之间存在时间或顺序依赖关系,常见应用包括:

- 自然语言处理:
- 文本生成:根据前面的文字,预测下一个词,从而生成文章、诗歌、代码。
- 情感分析:判断一段评论是正面的还是负面的。
- 机器翻译:将一种语言序列翻译成另一种语言序列。
- 时间序列预测:
- 股票价格预测:根据历史股价数据预测未来的价格。
- 天气预测:根据过去的气温、湿度等数据预测未来的天气。
- 能源消耗预测:预测未来一段时间内的用电量。
- 其他领域:
- 语音识别:将声学信号序列转换为文字序列。
- 视频分析:理解视频帧与帧之间的关系。
核心代码实现:用 TensorFlow/Keras 构建LSTM
我们将通过一个经典的例子——“根据莎士比亚的文本风格生成新的文本”——来学习如何使用 LSTM。
步骤概览
- 准备环境
- 加载数据
- 数据预处理 (最关键的一步)
- 构建 LSTM 模型
- 编译和训练模型
- 使用模型进行文本生成
步骤 1: 准备环境
确保你已经安装了 TensorFlow。
pip install tensorflow numpy
步骤 2: 加载数据
我们将使用 TensorFlow 内置的莎士比亚数据集。
import tensorflow as tf
import numpy as np
import os
import time
# 下载数据集
path_to_file = tf.keras.utils.get_file('shakespeare.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')
# 读取数据,并解码为 utf-8
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')
# 查看数据的前 250 个字符
print(text[:250])
步骤 3: 数据预处理
我们需要将文本转换成数字,因为神经网络只能处理数字。

- 创建词汇表:找出文本中所有唯一的字符。
- 字符到数字的映射:为每个字符分配一个唯一的整数 ID。
- 创建训练样本:将长文本分割成一系列的输入-目标对,如果我们想预测下一个字符,那么输入是 "Hello",目标就是 "ello"。
# 1. 创建词汇表
vocab = sorted(set(text))
print(f'{len(vocab)} 个唯一字符')
# 2. 创建字符到数字的映射
char2idx = {u:i for i, u in enumerate(vocab)}
idx2char = np.array(vocab)
# 将整个文本转换为整数列表
text_as_int = np.array([char2idx[c] for c in text])
# 3. 创建训练样本
# 定义输入序列的长度
seq_length = 100
examples_per_epoch = len(text)//(seq_length+1)
# 创建数据集
char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)
# 使用 batch 方法将字符序列转换为所需的长度
sequences = char_dataset.batch(seq_length+1, drop_remainder=True)
# 将每个序列拆分为输入和目标
def split_input_target(chunk):
input_text = chunk[:-1]
target_text = chunk[1:]
return input_text, target_text
dataset = sequences.map(split_input_target)
# 打印一个例子
for input_example, target_example in dataset.take(1):
print('输入数据: ', repr(''.join(idx2char[input_example.numpy()])))
print('目标数据: ', repr(''.join(idx2char[target_example.numpy()])))
# 批处理和打乱数据
BATCH_SIZE = 64
BUFFER_SIZE = 10000
dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
print(dataset)
步骤 4: 构建 LSTM 模型
我们将使用 Keras 的 Sequential 模型来构建一个简单的 LSTM 网络。
Embedding层:将整数编码的字符转换为密集的向量(词嵌入),这个向量能让模型更好地理解字符之间的关系。LSTM层:核心的 LSTM 层,用于学习序列中的时间依赖关系。Dense层:一个全连接层,用于在 LSTM 输出的基础上进行预测。
# 嵌入维度
vocab_size = len(vocab)
# 模型的嵌入维度
embedding_dim = 256
# RNN 的单元数量
rnn_units = 1024
def build_model(vocab_size, embedding_dim, rnn_units, batch_size):
model = tf.keras.Sequential([
tf.keras.layers.Embedding(vocab_size, embedding_dim,
batch_input_shape=[batch_size, None]),
tf.keras.layers.LSTM(rnn_units,
return_sequences=True,
stateful=True,
recurrent_initializer='glorot_uniform'),
tf.keras.layers.Dense(vocab_size)
])
return model
model = build_model(
vocab_size=vocab_size,
embedding_dim=embedding_dim,
rnn_units=rnn_units,
batch_size=BATCH_SIZE)
# 检查模型结构
model.summary()
步骤 5: 编译和训练模型
在训练之前,我们需要定义损失函数和优化器。
- 损失函数:由于我们是在预测下一个字符(一个多分类问题),所以使用
SparseCategoricalCrossentropy是最合适的,我们需要从logits(模型的原始输出)中计算概率。 - 优化器:
Adam是一个常用的、效果很好的优化器。
# 定义损失函数
def loss(labels, logits):
return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)
model.compile(optimizer='adam', loss=loss)
# 训练模型
# 为了快速看到效果,我们只训练 10 个周期
EPOCHS = 10
# 在 Colab 或有 GPU 的机器上训练会快很多
history = model.fit(dataset, epochs=EPOCHS)
步骤 6: 使用模型进行文本生成
训练完成后,我们就可以用它来生成新的文本了,生成文本的过程是一个循环:
- 给模型一个起始字符串(称为 "种子")。
- 将种子字符串转换为数字。
- 用模型预测下一个字符的概率分布。
- 根据这个概率分布随机选择一个字符(或者直接选概率最高的)。
- 将选中的字符附加到种子字符串的末尾。
- 将新的、更长的字符串作为新的输入,重复步骤 3-5,直到生成足够长的文本。
# 由于我们训练时使用了 batch_size,为了生成文本,我们需要一个 batch_size=1 的模型
model = build_model(vocab_size, embedding_dim, rnn_units, batch_size=1)
model.load_weights(tf.train.latest_checkpoint('./training_checkpoints')) # 假设你保存了检查点
model.build(tf.TensorShape([1, None]))
# 生成文本的函数
def generate_text(model, start_string):
# 生成文本的字符数量
num_generate = 1000
# 将起始字符串转换为数字(向量化)
input_eval = [char2idx[s] for s in start_string]
input_eval = tf.expand_dims(input_eval, 0)
# 存储生成的文本
text_generated = []
# 控制生成文本的随机性,值越大,文本越随机;值越小,文本越可预测。
temperature = 1.0
model.reset_states()
for i in range(num_generate):
predictions = model(input_eval)
# 移除批处理维度
predictions = tf.squeeze(predictions, 0)
# 用温度参数调整预测分布
predictions = predictions / temperature
predicted_id = tf.random.categorical(predictions, num_samples=1)[-1,0].numpy()
# 将预测的 ID 附加到输入中,并传递回模型
input_eval = tf.expand_dims([predicted_id], 0)
text_generated.append(idx2char[predicted_id])
return (start_string + ''.join(text_generated))
# 生成文本,以 "ROMEO:" 开头
print(generate_text(model, start_string=u"ROMEO: "))
进阶技巧
-
调优超参数:
rnn_units:增加这个值可以让模型有更强的记忆能力,但也会增加计算量和过拟合的风险。embedding_dim:增加这个值可以让模型学习更丰富的字符关系。batch_size和seq_length:影响训练速度和模型对长序列的依赖性。EPOCHS:训练的轮数,可以观察训练损失和验证损失来决定何时停止。
-
使用
GRU:GRU(Gated Recurrent Unit) 是 LSTM 的一个简化版本,参数更少,计算速度更快,在许多任务上表现与 LSTM 相当,你可以将tf.keras.layers.LSTM轻松替换为tf.keras.layers.GRU进行尝试。# 将 LSTM 替换为 GRU tf.keras.layers.GRU(rnn_units, return_sequences=True, stateful=True, recurrent_initializer='glorot_uniform')
-
堆叠 LSTM 层: 你可以堆叠多个 LSTM 层,让模型学习更高层次的时间模式,只需在第一个 LSTM 层后添加更多
LSTM层即可。model = tf.keras.Sequential([ tf.keras.layers.Embedding(vocab_size, embedding_dim, batch_input_shape=[batch_size, None]), tf.keras.layers.LSTM(rnn_units, return_sequences=True, stateful=True, recurrent_initializer='glorot_uniform'), tf.keras.layers.LSTM(rnn_units, return_sequences=True, stateful=True, recurrent_initializer='glorot_uniform'), # 第二层 LSTM tf.keras.layers.Dense(vocab_size) ]) -
Dropout: 为了防止过拟合,你可以在 LSTM 层之间添加
Dropout层。model = tf.keras.Sequential([ tf.keras.layers.Embedding(vocab_size, embedding_dim, batch_input_shape=[batch_size, None]), tf.keras.layers.LSTM(rnn_units, return_sequences=True, stateful=True, recurrent_initializer='glorot_uniform'), tf.keras.layers.Dropout(0.2), # 添加 Dropout tf.keras.layers.Dense(vocab_size) ])
总结与资源
恭喜!你已经学会了如何使用 TensorFlow 和 Keras 来构建、训练和部署一个 LSTM 模型用于文本生成。
关键要点回顾:
- LSTM 是处理序列数据的强大工具,通过门控机制解决了长程依赖问题。
- 数据预处理 是成功的关键,将文本转换为适合神经网络输入的格式(如整数序列)至关重要。
Embedding层 能将离散的字符 ID 转换为有意义的连续向量。- 生成文本 是一个迭代的过程,通常涉及随机采样来增加文本的多样性。
进一步学习的资源:
- 官方 TensorFlow 教程:Text generation using a RNN with eager execution (本教程的灵感来源,非常详细)
- Keras 文档:关于循环层的官方文档
- Andrej Karpathy 的博客:The Unreasonable Effectiveness of Recurrent Neural Networks (必读!经典中的经典,用 Python 从零开始实现 RNN)
- 吴恩达的 Deep Learning Specialization:Coursera 上的课程,其中第二门课专门讲解序列模型,对 LSTM 和 GRU 有非常透彻的讲解。
