大家好,我是锋哥。最近连载更新《TensorFlow2 Python深度学习》技术专题。
本课程主要讲解基于TensorFlow2的Python深度学习知识,包括深度学习概述,TensorFlow2框架入门知识,以及卷积神经网络(CNN),循环神经网络(RNN),生成对抗网络(GAN),模型保存与加载等。同时也配套视频教程 《2026版 TensorFlow2 Python深度学习 视频教程》
循环神经网络(RNN)简介
一,什么是RNN
RNN(Recurrent Neural Network)是一类用于处理序列数据的神经网络。首先我们要明确什么是序列数据,摘取百度百科词条:时间序列数据是指在不同时间点上收集到的数据,这类数据反映了某一事物、现象等随时间的变化状态或程度。这是时间序列数据的定义,当然这里也可以不是时间,比如文字序列,但总归序列数据有一个特点——后面的数据跟前面的数据有关系。
RNN是神经网络的一种,类似的还有深度神经网络DNN,卷积神经网络CNN,生成对抗网络GAN等等。RNN对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,利用了RNN的这种能力,使深度学习模型在解决语音识别、语言模型、机器翻译以及时序分析等NLP领域的问题时有所突破。
举几个具有序列特性的例子:
- 拿人类的某句话来说,也就是人类的自然语言,是不是符合某个逻辑或规则的字词拼凑排列起来的,这就是符合序列特性。
- 语音,我们发出的声音,每一帧每一帧的衔接起来,才凑成了我们听到的话,这也具有序列特性。
- 股票,随着时间的推移,会产生具有顺序的一系列数字,这些数字也是具有序列特性。
二、为什么要发明RNN
我们先来看一个NLP很常见的问题,命名实体识别,举个例子,现在有两句话:
第一句话:I like eating apple!(我喜欢吃苹果!)
第二句话:The Apple is a great company!(苹果真是一家很棒的公司!)
现在的任务是要给apple打Label,我们都知道第一个apple是一种水果,第二个apple是苹果公司,假设我们现在有大量的已经标记好的数据以供训练模型,当我们使用全连接的神经网络时,我们做法是把apple这个单词的特征向量输入到我们的模型中(如下图),在输出结果时,让我们的label里,正确的label概率最大,来训练模型,但我们的语料库中,有的apple的label是水果,有的label是公司,这将导致,模型在训练的过程中,预测的准确程度,取决于训练集中哪个label多一些,这样的模型对于我们来说完全没有作用。问题就出在了我们没有结合上下文去训练模型,而是单独的在训练apple这个单词的label,这也是全连接神经网络模型所不能做到的,于是就有了我们的循环神经网络。
三、RNN的基础知识
1、循环核介绍
循环核具有记忆力,通过不同时刻的参数共享,实现了对时间序列的信息提取
2、循环核按时间步展开
按时间步展开,就是把循环核按照时间轴方向展开。每个时刻记忆体状态信息ht被刷新,记忆体周围的参数矩阵wxh、whh和why是固定不变的。要训练优化的就是这些参数矩阵。训练完成后,使用效果最好的参数矩阵,执行前向传播,输出预测结果。循环神经网络,就是借助循环核提取时间特征后,送入全连接网络,实现连续数据的预测。
3、记忆体
循环核按照时间步展开后,可以发现,循环核是由多个记忆体构成,记忆体是循环神经网络储存历史状态信息的载体,每个记忆体都可以设定相应的个数,这个个数决定了记忆体可以存储历史状态信息的能力,记忆体个数越多,训练效果越好,但是由于记忆体的个数决定了参数矩阵的维度,因此记忆体个数越多,需要训练的参数量就越多,所需要消耗的资源就越大,训练时间就越长,因此需酌情评估。图中的例子中记忆体的个数为3,这个记忆体的个数,决定了ht的维度,进一步决定了Wxh、Whh以及Why的维度。
4、循环计算层
每个循环核构成一层循环计算层。循环计算层的层数是向输出方向增长的。
转载自:blog.csdn.net/qq_39439006…
通俗易懂理解循环神经网络(RNN)
🧠 用一个生活比喻来理解RNN
想象你在看电影时理解剧情的过程:
- 传统神经网络:像只看电影的一帧画面,不知道前后剧情
- 循环神经网络:像正常人看电影,能记住前面发生了什么,理解当前画面在整体剧情中的位置
📚 什么是RNN?简单说就是"有记忆的神经网络"
基本概念:
- 普通神经网络:每次分析都是"从零开始",没有记忆
- RNN:会"记住"之前看到的内容,用来帮助理解现在的内容
就像:
- 读句子时,你理解每个词的意思都依赖于前面的词
- "我今天吃了苹果" vs "我买了苹果手机"
- 同样的"苹果",意思完全不同,需要前面的语境来理解
🔄 RNN如何工作?"时间旅行"的神经网络
核心机制:
输入 → [当前分析 + 之前记忆] → 输出 + 新记忆
具体过程:
- 收到新的输入(比如一个新词)
- 结合"刚才的记忆"来理解这个输入
- 产生输出(比如理解这个词的意思)
- 更新记忆,为下一个输入做准备
举个读句子的例子:
句子:"我爱" + "吃" + "苹果"
第一步:看到"我爱"
- 记忆:空
- 输出:这是一个关于喜好的句子
- 新记忆:主题是"喜欢某物"
第二步:看到"吃"
- 记忆:主题是"喜欢某物"
- 输出:喜欢与吃相关的东西
- 新记忆:喜欢+吃 → 可能是食物
第三步:看到"苹果"
- 记忆:喜欢吃的食物
- 输出:喜欢吃的苹果(水果)
- 新记忆:完整的句子意思
🏗️ RNN的三种主要类型
- 基础RNN - 短期记忆型
- 像金鱼,只能记住最近的一点信息
- 简单但容易"忘记"太早的事情
- LSTM - 长期记忆高手 🎯
-
像聪明的人,能选择性记住重要信息
-
有三个"门"来控制记忆:
- 输入门:决定什么新信息要记住
- 忘记门:决定什么旧信息要忘记
- 输出门:决定输出什么信息
- GRU - 简化版LSTM
- 像LSTM的弟弟,简单但有效
- 只有两个门,计算更快
🌟 RNN的实际应用场景
📝 文本相关:
- 机器翻译:理解整个句子再翻译
- 聊天机器人:记住对话历史
- 文章生成:写小说时保持情节连贯
🎵 音频处理:
- 语音识别:根据前后语音判断当前词
- 音乐生成:创作连贯的旋律
⏰ 时间序列:
- 股票预测:基于历史价格预测未来
- 天气预报:基于过去天气预测未来
🆚 RNN vs 传统神经网络
| 传统神经网络 | RNN |
|---|---|
| 每次输入独立处理 | 考虑输入之间的关联 |
| 像看单张照片 | 像看连续视频 |
| 适合图片分类 | 适合语言、时间序列 |
💡 举个更生活的例子:聊天对话
你问AI:"今天天气怎么样?明天呢?"
普通神经网络回答:
- "今天天气怎么样?" → "今天晴天"
- "明天呢?" → "我不知道你在问什么"
RNN回答:
- "今天天气怎么样?" → "今天晴天"
- "明天呢?" → "明天也是晴天" (记得你在问天气)
⚠️ RNN的局限性
主要问题:记忆有限
- 就像人记不住太长的故事细节
- 处理很长序列时,会"忘记"开头的内容
解决方案:
- LSTM/GRU:改善长序列记忆
- 注意力机制:让模型能"回头看"重要部分
🎯 总结
RNN = 神经网络 + 记忆功能
- 核心价值:能处理有顺序、有关联的数据
- 关键思想:当前理解依赖于历史信息
- 适用场景:任何需要"上下文"的任务
就像人类理解语言需要上下文一样,RNN让AI也能具备这种"联系前后文"的能力! 🚀
IMDB数据集数据集简介
1. 数据集概述
IMDB 数据集是一个二分类情感分析的经典基准数据集。它包含了来自互联网电影数据库(IMDB)的 50,000 条影评文本,其中 25,000 条用于训练,另外 25,000 条用于测试。
数据集的标签是二元的:
- 0:代表负面评论
- 1:代表正面评论
一个关键的特点是,训练集和测试集是平衡的,这意味着它们各自包含 25,000 条正面和 25,000 条负面评论。
2 . 数据预处理形式
在 TensorFlow 2 中,IMDB 数据集已经过预处理。原始评论文本中的单词已经被转换为整数索引,这些索引对应于在一个词汇表中该单词的频率排名。
例如:
- 整数 1 通常代表数据集中最常出现的单词。
- 整数 2 代表第二常见的单词,以此类推。
- 整数 0 不代表任何特定单词,而是被用作 填充符。
- 整数 3 通常代表 “未知单词” ,即那些不在最常用词汇列表中的单词。
默认情况下,数据集被设置为仅保留词汇表中前 10,000 个最常用的单词(通过参数 num_words 控制)。这既有助于控制模型的复杂度,也减少了计算和内存的开销。
循环神经网络(SimpleRNN)示例
import tensorflow as tf
from keras import Input, layers
from keras.src.utils import pad_sequences
# 1. 加载 IMDB 数据集
max_features = 10000 # 使用词汇表中前 10000 个常见单词
maxlen = 100 # 每条评论的最大长度
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.imdb.load_data(num_words=max_features)
print(x_train.shape, x_test.shape)
print(x_train[0])
print(y_train)
# 2. 数据预处理:对每条评论进行填充,使其长度统一
x_train = pad_sequences(x_train, maxlen=maxlen)
x_test = pad_sequences(x_test, maxlen=maxlen)
# 3. 构建 RNN 模型
model = tf.keras.models.Sequential([
Input(shape=(maxlen,)),
layers.Embedding(input_dim=max_features, output_dim=128), # 嵌入层,将单词索引映射为向量 output_dim 嵌入向量的维度(即每个输入词的嵌入表示的长度)
layers.SimpleRNN(128), # SimpleRNN 层:包含 128 个神经元,激活函数默认使用 tanh
layers.Dense(1, activation='sigmoid') # 输出层:用于二分类(正面或负面),激活函数为 sigmoid
])
# 4. 模型编译
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 5. 模型训练
history = model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test), verbose=1)
# 6. 模型评估
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"Test accuracy: {test_acc}")
运行结果:
循环神经网络(LSTM)示例
LSTM(长短期记忆网络,Long Short-Term Memory)是一种特殊类型的循环神经网络(RNN),用于处理和预测序列数据。它能够有效地解决标准RNN在长期依赖问题中的缺点,如梯度消失和梯度爆炸问题。LSTM的关键在于其特殊的结构,其中包括了三个“门”机制:输入门、遗忘门和输出门,这些门控制信息流的进入、遗忘和输出,允许模型更好地捕捉和保持长期的依赖关系。
LSTM的基本结构
LSTM单元的结构包括以下几部分:
- 输入门(Input Gate) :决定哪些新信息被写入到单元状态。
- 遗忘门(Forget Gate) :决定哪些信息会从单元状态中丢弃或保留。
- 输出门(Output Gate) :决定哪些信息将用于输出。
tf.keras.layers.LSTM(
units,
activation='tanh',
recurrent_activation='sigmoid',
use_bias=True,
kernel_initializer='glorot_uniform',
recurrent_initializer='orthogonal',
bias_initializer='zeros',
unit_forget_bias=True,
dropout=0.0,
recurrent_dropout=0.0,
return_sequences=False,
return_state=False,
go_backwards=False,
stateful=False,
time_major=False,
unroll=False,
**kwargs
)
核心参数:
units- 最重要的参数
- 作用:定义LSTM层中记忆单元的数量
- 通俗理解:LSTM的"脑容量"或"记忆力大小"
- 影响:值越大,模型表达能力越强,但计算复杂度越高
- 建议范围:32-1024,根据任务复杂度选择
return_sequences- 输出控制
-
作用:控制是否返回所有时间步的输出
-
默认值:
False(只返回最后一个时间步的输出) -
使用场景:
False:用于分类、情感分析等只需要最终结果的场景True:用于序列标注、机器翻译等需要每个时间步输出的场景
dropout和recurrent_dropout- 正则化参数
dropout:输入单元的丢弃率,防止过拟合recurrent_dropout:循环连接的丢弃率,防止循环过拟合- 建议值:0.2-0.5,根据数据量和模型复杂度调整
activation和recurrent_activation- 激活函数
activation:主要计算的激活函数,默认'tanh'recurrent_activation:门控单元的激活函数,默认'sigmoid'
return_state- 状态返回
- 作用:是否返回LSTM的隐藏状态和细胞状态
- 使用场景:编码器-解码器结构、状态传递等高级应用
stateful- 状态保持
- 作用:批次间是否保持LSTM状态
- 使用场景:处理超长序列需要分批时保持状态连续性
unroll- 展开计算
- 作用:是否将RNN展开为前馈网络
- 优点:加速训练(适合短序列)
- 缺点:内存消耗大(不适合长序列)
示例:
import tensorflow as tf
from keras import Input, layers
from keras.src.utils import pad_sequences
# 1. 加载 IMDB 数据集
max_features = 10000 # 使用词汇表中前 10000 个常见单词
maxlen = 100 # 每条评论的最大长度
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.imdb.load_data(num_words=max_features)
print(x_train.shape, x_test.shape)
print(x_train[0])
print(y_train)
# 2. 数据预处理:对每条评论进行填充,使其长度统一
x_train = pad_sequences(x_train, maxlen=maxlen)
x_test = pad_sequences(x_test, maxlen=maxlen)
# 3. 构建 RNN 模型
model = tf.keras.models.Sequential([
Input(shape=(maxlen,)),
layers.Embedding(input_dim=max_features, output_dim=128), # 嵌入层,将单词索引映射为向量 output_dim 嵌入向量的维度(即每个输入词的嵌入表示的长度)
layers.LSTM(units=64, dropout=0.2, recurrent_dropout=0.2),
# LSTM 层:包含 64 个神经元,激活函数默认使用 tanh dropout表示在每个时间步上丢弃20% recurrent_dropout 递归状态(即隐藏状态)的dropout比率为20%
layers.Dense(1, activation='sigmoid') # 输出层:用于二分类(正面或负面),激活函数为 sigmoid
])
# 4. 模型编译
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 5. 模型训练
history = model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test), verbose=1)
# 6. 模型评估
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"Test accuracy: {test_acc}")
运行结果:
循环神经网络(GRU)示例
GRU(门控循环单元,Gated Recurrent Unit)是一种用于处理序列数据的递归神经网络(RNN)模型。它是为了克服传统RNN在长时间序列中训练时遇到的梯度消失问题(即记忆力衰减)而提出的。GRU相较于LSTM(长短时记忆网络),结构更简单,计算效率更高,但能够实现类似的性能。
GRU的工作原理
GRU通过引入两个重要的门控机制来控制信息的流动:
- 更新门(Update Gate) :决定了当前时刻的隐藏状态有多少部分应该被更新,多少部分保留旧的隐藏状态信息。这个门类似于LSTM中的遗忘门和输入门的结合。它通过一个Sigmoid激活函数控制当前输入和上一时刻的隐藏状态对当前时刻的影响。
- 重置门(Reset Gate) :控制遗忘多少旧的隐藏状态信息。它决定了当前时刻输入和过去隐藏状态的结合程度,从而决定了网络保留多少历史信息。通过一个Sigmoid激活函数来计算。
GRU的关键是利用这些门控机制在时间步之间传递信息,从而可以更好地捕获序列数据中的长期依赖性。
tf.keras.layers.GRU(
units,
activation='tanh',
recurrent_activation='sigmoid',
use_bias=True,
kernel_initializer='glorot_uniform',
recurrent_initializer='orthogonal',
bias_initializer='zeros',
dropout=0.0,
recurrent_dropout=0.0,
return_sequences=False,
return_state=False,
go_backwards=False,
stateful=False,
time_major=False,
unroll=False,
reset_after=True,
**kwargs
)
核心参数:
units- 核心参数
- 作用:定义GRU层中隐藏单元的数量
- 影响:决定模型的容量和复杂度,值越大表示记忆能力越强
- 选择:通常根据任务复杂度在32-512之间选择
activation- 激活函数
- 作用:定义输出计算的激活函数
- 默认值:
'tanh' - 功能:控制候选隐藏状态的计算
recurrent_activation- 循环激活函数
- 作用:定义门控机制的激活函数
- 默认值:
'sigmoid' - 功能:控制更新门和重置门的计算
return_sequences- 输出控制
-
作用:控制是否返回所有时间步的输出
-
默认值:
False(只返回最后一个时间步) -
应用:
False:用于序列分类任务True:用于序列标注或多层GRU堆叠
return_state- 状态返回
- 作用:是否返回最后的隐藏状态
- 使用场景:编码器-解码器架构或需要状态传递的场景
dropout- 输入丢弃率
- 作用:输入单元的随机丢弃比例,防止过拟合
- 范围:0.0-1.0,推荐0.2-0.5
recurrent_dropout- 循环丢弃率
- 作用:循环连接的随机丢弃比例
- 功能:专门防止循环连接上的过拟合
reset_after- 重置门位置
- 作用:控制重置门的应用顺序
- 默认值:
True(与CuDNN兼容的模式) - 影响:影响计算图和性能,通常保持默认
示例:
import tensorflow as tf
from keras import Input, layers
from keras.src.utils import pad_sequences
# 1. 加载 IMDB 数据集
max_features = 10000 # 使用词汇表中前 10000 个常见单词
maxlen = 100 # 每条评论的最大长度
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.imdb.load_data(num_words=max_features)
print(x_train.shape, x_test.shape)
print(x_train[0])
print(y_train)
# 2. 数据预处理:对每条评论进行填充,使其长度统一
x_train = pad_sequences(x_train, maxlen=maxlen)
x_test = pad_sequences(x_test, maxlen=maxlen)
# 3. 构建 RNN 模型
model = tf.keras.models.Sequential([
Input(shape=(maxlen,)),
layers.Embedding(input_dim=max_features, output_dim=128), # 嵌入层,将单词索引映射为向量 output_dim 嵌入向量的维度(即每个输入词的嵌入表示的长度)
layers.GRU(units=64, dropout=0.2, recurrent_dropout=0.2),
# GRU 层:包含 64 个神经元,激活函数默认使用 tanh dropout表示在每个时间步上丢弃20% recurrent_dropout 递归状态(即隐藏状态)的dropout比率为20%
layers.Dense(1, activation='sigmoid') # 输出层:用于二分类(正面或负面),激活函数为 sigmoid
])
# 4. 模型编译
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 5. 模型训练
history = model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test), verbose=1)
# 6. 模型评估
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"Test accuracy: {test_acc}")
运行结果: