【机器学习】任务十二:循环神经网络
1.循环神经网络
1.1 什么是循环神经网络(RNN)?
循环神经网络(Recurrent Neural Network, RNN) 是一种用于处理序列数据的神经网络类型,它的主要特点是拥有循环连接,使得网络可以对序列中的每个时间步(timestep)进行处理,并将前一个时间步的信息传递到当前时间步,从而捕捉序列数据中的时序依赖关系。
1.2 RNN的基本结构和工作原理
RNN 的核心思想是:通过每个时间步的“隐藏状态”(hidden state)来记录过去的信息,并将这些信息传递给下一个时间步。这个隐藏状态不仅依赖于当前的输入,还依赖于上一个时间步的隐藏状态。
(1)输入:在每个时间步 ttt,RNN 接收一个输入 xtx_txt,这个输入可以是序列数据的一个元素,如单词、音频信号的一个采样点等。
(2)隐藏状态:每个时间步都会更新一个隐藏状态 hth_tht,它不仅受到当前输入的影响,还受到上一个时间步的隐藏状态的影响。隐藏状态捕捉了当前时刻和过去时刻的上下文信息。
隐藏状态的更新公式:
其中:
- ht:当前时间步的隐藏状态。
- ht−1:上一个时间步的隐藏状态。
- xt:当前时间步的输入。
- Wh:权重矩阵,分别控制隐藏状态和输入的影响。
- b:偏置项。
- f:激活函数,通常是非线性函数(如
tanh
或ReLU
)。
(3)输出:经过多个时间步后,RNN 会输出序列的预测结果。这个输出可以是每个时间步的预测(如时间序列预测),也可以是序列整体的预测(如文本分类)。
1.3 RNN的优缺点
优点:
- 序列数据处理能力:RNN 可以自然地处理序列数据(如文本、语音、时间序列等),并且能够捕捉到时间步之间的依赖关系。
- 共享权重:RNN 在每个时间步使用相同的权重,因此参数量相对较少,训练更加高效。
缺点:
- 梯度消失/爆炸问题:在长序列的训练过程中,RNN 的梯度可能变得非常小(梯度消失)或者非常大(梯度爆炸),导致网络难以学习长时间依赖关系。
- 长期依赖问题:标准 RNN 难以记住长期的依赖关系,因为每个时间步的隐藏状态只通过一个单一的矩阵乘法进行更新,导致远距离的信息丢失。
1.4 RNN的变种
为了解决 RNN 的缺点,出现了许多改进的变种:
- 长短期记忆网络(LSTM):LSTM 是 RNN 的一种改进,它通过引入三个门(输入门、遗忘门和输出门)来控制信息的流动,能有效缓解梯度消失和长期依赖问题。
- 门控循环单元(GRU):GRU 是另一种改进的 RNN 变种,与 LSTM 类似,但结构更简单,计算量较小,同样能够捕捉长期依赖。
1.5 RNN的应用
RNN 被广泛应用于需要处理时序数据的任务,以下是一些典型的应用场景:
自然语言处理(NLP):
- 机器翻译:RNN 能够处理句子的顺序信息,因此它在机器翻译任务中非常有效。
- 情感分析:分析文本中情感的倾向。
- 文本生成:生成连贯的文本内容。
语音识别:将语音信号转换为文本,RNN 能够捕捉音频信号的时序特性。
时间序列预测:用于股市预测、天气预报等领域,RNN 可以基于历史数据预测未来的趋势。
视频分析:在视频流中,RNN 可以用来捕捉帧与帧之间的时序关系,用于视频分类、动作识别等任务。
1.6 总结
RNN 是一种非常适合处理序列数据的神经网络架构,通过循环连接的方式,能够捕捉时序数据中的上下文信息。尽管它在处理短期依赖上表现良好,但由于梯度消失和长期依赖问题,RNN 在实际应用中往往被 LSTM 和 GRU 等变种所取代。这些变种改进了 RNN 的性能,能够更好地处理长序列数据中的长期依赖关系。
2.小实验
2.1 数字填充
2.1.1 代码与结果
如何使用 TensorFlow 的 pad_sequences
函数对一个不等长的序列进行填充
import tensorflow as tf # 导入所需库
s = [[1], [2, 3], [4, 5, 6]] # 初始化填充前序列 s
print("填充前序列为\n", s) # 显示填充前序列 s
# 填充序列并赋值给 a
a = tf.keras.preprocessing.sequence.pad_sequences(s, padding='post')
print("填充后序列为\n", a) # 显示填充后序列 a
填充前序列为
[[1], [2, 3], [4, 5, 6]]
填充后序列为
[[1 0 0]
[2 3 0]
[4 5 6]]
2.1.2 关键代码解释
# 填充序列并赋值给 a
a = tf.keras.preprocessing.sequence.pad_sequences(s, padding='post')
这里的 pad_sequences
是 TensorFlow 中的一个函数,属于 tf.keras.preprocessing.sequence
模块,用于对不同长度的序列进行填充,使它们的长度一致。具体来说,pad_sequences
会自动将每个子序列(列表)填充为相同的长度。
(1)参数详解
s
: 这是输入的原始序列,可以是一个嵌套的列表(或数组),每个子列表的长度可以不同。例如,在你的代码中,s = [[1], [2, 3], [4, 5, 6]]
是一个 3×3 的嵌套列表,不同的子序列有不同的长度:第一个子序列只有一个元素,第二个有两个,第三个有三个。
padding='post'
: 这个参数指定了填充的位置。
'post'
: 表示在序列的 末尾 添加填充值(默认是 0)。'pre'
: 表示在序列的 前面 添加填充值。
你在代码中选择了'post'
,所以会在每个子序列的末尾填充零。
(2)填充的方式
在这个例子中,原始序列的最大长度是 3,因为第三个子序列([4, 5, 6]
)有三个元素。所以 pad_sequences
会将其他长度较短的子序列填充到 3 的长度。
[1]
会被填充成[1, 0, 0]
,填充了两个零。[2, 3]
会被填充成[2, 3, 0]
,填充了一个零。[4, 5, 6]
不需要填充,因为它的长度已经是 3。
(3)填充后的序列
假设 s = [[1], [2, 3], [4, 5, 6]]
,填充后的结果 a
会是:
a = [[1, 0, 0], [2, 3, 0], [4, 5, 6]]
这样,所有的子序列就被填充成了相同的长度 3,方便后续的操作(例如,在神经网络中进行批处理时,通常需要所有输入数据的形状一致)。
(4)默认填充值
默认情况下,pad_sequences
会使用 0 进行填充,如果需要填充其他值,可以通过 value
参数来指定填充值。例如,如果你想用 -1 填充,可以这样写:
a = tf.keras.preprocessing.sequence.pad_sequences(s, padding='post', value=-1)
2.2 序列剪裁
2.2.1 代码与结果
import tensorflow as tf # 导入所需库
s = [[1, 2, 3, 9], [4, 5, 6], [7, 8]] # 初始化剪裁前序列 s
print("剪裁前序列为\n", s) # 显示剪裁前序列 s
# 剪裁序列并赋值给 a,
# maxlen=2: 通过设置 maxlen 参数,将序列的长度裁剪为2。如果序列的长度超过2,则保留最后的两个元素
a = tf.keras.preprocessing.sequence.pad_sequences(s, maxlen=2)
print("剪裁后序列为\n", a) # 显示剪裁后序列 a
剪裁前序列为
[[1, 2, 3, 9], [4, 5, 6], [7, 8]]
剪裁后序列为
[[3 9]
[5 6]
[7 8]]
2.2.2 关键代码解释
a = tf.keras.preprocessing.sequence.pad_sequences(s, maxlen=2)
(1)tf.keras.preprocessing.sequence.pad_sequences
作用
pad_sequences
是 TensorFlow 中用于处理变长序列的工具,它能够将一组不等长的序列填充或裁剪到相同的长度。这对于处理文本、时间序列或其他形式的序列数据尤其重要,因为在许多机器学习模型中(尤其是神经网络),输入的形状必须一致。
(2)maxlen
参数的作用
-
maxlen=2
: 这是控制输出序列最大长度的参数。-
maxlen
指定了每个序列的目标长度。如果一个子序列的长度超过了maxlen
,则会裁剪它(即从前端或后端去掉多余的元素)。如果一个子序列的长度小于maxlen
,则会根据padding
参数填充元素(默认为 0)。 -
在你的代码中,
maxlen=2
表示每个序列都会被裁剪或填充到 2 的长度。如果子序列的长度大于 2,它会裁剪;如果子序列的长度小于 2,它会用默认的填充值(0)填充。
-
(3)默认的裁剪方式
-
裁剪方式(
truncating
): 默认情况下,pad_sequences
会从 序列的前面(pre) 裁剪多余的元素。如果你不指定truncating
参数,它会假设为'pre'
,也就是裁剪掉超过maxlen
的前面的部分,保留后面的部分。 -
如果你希望从序列的末尾(
post
)裁剪,可以通过设置truncating='post'
来改变默认行为。
(4)填充方式
-
填充方式(
padding
): 默认情况下,pad_sequences
会在序列的末尾(post
)添加零填充,如果你希望填充的内容是其他值,可以通过value
参数指定填充值。 -
默认填充位置是
post
,即在序列的末尾进行填充。
(5)具体操作
考虑到 s = [[1, 2, 3, 9], [4, 5, 6], [7, 8]]
,每个子序列的长度不一致:
- 第一子序列
[1, 2, 3, 9]
长度为 4。 - 第二子序列
[4, 5, 6]
长度为 3。 - 第三子序列
[7, 8]
长度为 2。
当 maxlen=2
时,pad_sequences
会裁剪这些子序列的长度,使其都为 2。
- 第一子序列
[1, 2, 3, 9]
长度超过 2,所以会被裁剪,裁剪后的结果是[3, 9]
(默认从前面裁剪)。 - 第二子序列
[4, 5, 6]
长度为 3,所以它的前一个元素会被裁剪,裁剪后的结果是[5, 6]
。 - 第三子序列
[7, 8]
已经是 2 个元素,因此它不需要裁剪或填充,保持不变。
2.3 keras 搭建模型
2.3.1 代码和结果
这段代码展示了如何使用 TensorFlow 和 Keras 创建一个简单的神经网络模型,并使用嵌入层(Embedding
)来处理文本数据。
# 3.最常见的 100 个单词
import tensorflow as tf # 导入 TensorFlow 库
import numpy as np # 导入 NumPy 库
# 构建空的网络模型 model
model = tf.keras.Sequential()
# 创建嵌入层
embedding = tf.keras.layers.Embedding(output_dim=32, input_dim=100, input_length=400)
model.add(embedding) # 添加到网络模型 model 中
model.summary() # 显示网络模型的参数信息
# 显示 embedding 矩阵的值
print("embeddings矩阵=\n", embedding.get_weights())
# 定义输入文本
text = "Deep learning is an important concept raised by the current sciences."
# 定义分词器对象
token = tf.keras.preprocessing.text.Tokenizer(num_words=100)
token.fit_on_texts([text]) # 分词
input = token.texts_to_sequences([text]) # 输出向量序列
# 序列填充
test_seq = tf.keras.preprocessing.sequence.pad_sequences(
input, padding='post', maxlen=400, truncating='post'
)
# 使用向量序列应用网络模型
output_array = model.predict(test_seq)
# 输出矩阵的形状
print("输出矩阵的形状=", output_array.shape)
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (None, 400, 32) 3200
=================================================================
Total params: 3,200
Trainable params: 3,200
Non-trainable params: 0
_________________________________________________________________
embeddings矩阵=
[array([[-0.02878962, -0.03292129, 0.04554972, ..., -0.0058917 ,
0.01143857, 0.0233766 ],
[ 0.01397041, -0.01258515, 0.00929421, ..., -0.04004631,
0.0135694 , 0.0277424 ],
[ 0.04916861, -0.04198183, -0.04687737, ..., -0.03828448,
0.02139128, 0.02899126],
...,
[-0.0321876 , 0.0293329 , 0.02029986, ..., -0.00899869,
-0.00628114, -0.02092808],
[-0.0257414 , 0.03492158, -0.00921569, ..., -0.0181116 ,
-0.03592857, 0.02451548],
[ 0.04689746, 0.02985245, -0.04975928, ..., -0.02757919,
-0.01401014, -0.03509605]], dtype=float32)]
1/1 [==============================] - 0s 75ms/step
输出矩阵的形状= (1, 400, 32)
2.3.2 代码解释
(1)导入必要的库
import tensorflow as tf # 导入 TensorFlow 库
import numpy as np # 导入 NumPy 库
tensorflow
用于构建神经网络模型及其相关操作。numpy
用于数组操作,尽管在这个代码片段中未直接使用,但它通常用于处理数值数据和矩阵。
(2)构建空的神经网络模型
model = tf.keras.Sequential()
- 这里使用
tf.keras.Sequential
创建了一个空的顺序模型model
。这是一个简单的线性堆叠模型,用于顺序地添加层(例如Embedding
层)。
(3)创建嵌入层(Embedding Layer)
embedding = tf.keras.layers.Embedding(output_dim=32, input_dim=100, input_length=400)
model.add(embedding) # 添加到网络模型 model 中
Embedding
层用于将输入的整数序列映射为稠密的向量表示。这里的output_dim=32
表示嵌入空间的维度为 32,也就是说每个词会被映射为一个 32 维的向量。input_dim=100
表示输入的词汇表大小是 100,即模型将处理的词汇表最多包含 100 个不同的词。input_length=400
表示每个输入序列的长度为 400。也就是说,输入的每个文本序列将被填充或截断到 400 个词。
这个嵌入层将每个整数(词汇的索引)转换为一个 32 维的向量。
(4)打印模型结构
model.summary() # 显示网络模型的参数信息
model.summary()
会打印出当前模型的结构、各层的类型、每一层的参数量、输出形状等信息,帮助你理解模型的结构。
(5)获取并显示嵌入矩阵的值
print("embeddings矩阵=\n", embedding.get_weights())
embedding.get_weights()
会返回嵌入层的权重矩阵。这个权重矩阵是一个二维数组,其形状是(input_dim, output_dim)
,即(100, 32)
,表示每个词汇对应的 32 维向量表示。
(6)定义输入文本并进行分词处理
text = "Deep learning is an important concept raised by the current sciences."
- 这是输入的文本数据,用于展示如何将文本转换为模型可以处理的数值格式。
token = tf.keras.preprocessing.text.Tokenizer(num_words=100)
token.fit_on_texts([text]) # 分词
input = token.texts_to_sequences([text]) # 输出向量序列
Tokenizer
是 Keras 提供的文本处理工具,用于将文本转换为整数序列(每个单词对应一个唯一的整数)。num_words=100
表示词汇表的大小限制为前 100 个最常见的词。token.fit_on_texts([text])
对输入文本进行分词,构建词汇表。input = token.texts_to_sequences([text])
将文本转换为整数序列(将文本中的每个单词替换为它在词汇表中的索引)。
假设词汇表中的单词被编码为整数,转换后的 input
可能是类似于 [1, 2, 3, 4, 5, 6, 7, ...]
的形式。
(7)序列填充
test_seq = tf.keras.preprocessing.sequence.pad_sequences(
input, padding='post', maxlen=400, truncating='post'
)
pad_sequences
用于将不同长度的输入序列填充或截断到相同的长度。这里,maxlen=400
表示每个输入序列的最大长度为 400,超过这个长度的部分会被截断,长度不够的部分会被填充。padding='post'
表示填充将在序列的末尾进行(默认填充的值是 0)。truncating='post'
表示如果序列超过maxlen
,会从序列的末尾裁剪超出部分。
(8)使用模型进行预测
output_array = model.predict(test_seq)
- 通过调用
model.predict()
方法,将填充后的文本序列test_seq
输入到模型中,进行预测。此时,嵌入层会将每个单词(以索引表示)映射到对应的嵌入向量,输出一个矩阵。
(9) 输出矩阵的形状
print("输出矩阵的形状=", output_array.shape)
- 输出
output_array
的形状。由于模型只有一个Embedding
层,输出的形状应该是(batch_size, input_length, output_dim)
。具体来说,batch_size
是输入文本的批次大小(这里只有一个文本,批次大小为 1),input_length=400
是序列的长度,output_dim=32
是每个词的嵌入维度。因此,输出的形状应该是(1, 400, 32)
。
2.3.3 结果分析
(1)模型结构(model.summary()
的输出)
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (None, 400, 32) 3200
=================================================================
Total params: 3,200
Trainable params: 3,200
Non-trainable params: 0
_________________________________________________________________
- 模型类型:模型是一个
Sequential
类型的神经网络,它包含一系列按顺序排列的层。 - 嵌入层(Embedding):
- 输出形状:
(None, 400, 32)
,表示输入的每个序列都将转换为 400 个词的向量表示,每个词的嵌入向量是 32 维的。 - 参数数量:
3200
。这个值表示嵌入层的权重矩阵的参数数量。嵌入层的权重矩阵大小为input_dim * output_dim = 100 * 32 = 3200
,即 100 个词汇(词表大小)每个词用一个 32 维的向量表示。
- 输出形状:
(2)嵌入矩阵的权重(embedding.get_weights()
的输出)
embeddings矩阵=
[array([[-0.02878962, -0.03292129, 0.04554972, ..., -0.0058917 ,
0.01143857, 0.0233766 ],
[ 0.01397041, -0.01258515, 0.00929421, ..., -0.04004631,
0.0135694 , 0.0277424 ],
[ 0.04916861, -0.04198183, -0.04687737, ..., -0.03828448,
0.02139128, 0.02899126],
...,
[-0.0321876 , 0.0293329 , 0.02029986, ..., -0.00899869,
-0.00628114, -0.02092808],
[-0.0257414 , 0.03492158, -0.00921569, ..., -0.0181116 ,
-0.03592857, 0.02451548],
[ 0.04689746, 0.02985245, -0.04975928, ..., -0.02757919,
-0.01401014, -0.03509605]], dtype=float32)]
- 这是嵌入层的权重矩阵。这个矩阵的维度是
(input_dim, output_dim)
,即(100, 32)
,表示词汇表中每个词都被映射为一个 32 维的向量。 embedding.get_weights()
返回的是一个列表,其中包含嵌入层的权重矩阵。每个词的向量在该矩阵中都有一个对应的行。- 这些向量的数值是经过训练优化得到的。你可以看到,这些向量的值是浮动的,表示模型学到的词嵌入。
(3)模型的预测输出(model.predict()
的输出)
1/1 [==============================] - 0s 75ms/step
输出矩阵的形状= (1, 400, 32)
model.predict(test_seq)
返回的是模型对输入序列的预测输出。这里,模型的输出是每个输入单词的嵌入表示(通过Embedding
层)。- 输出矩阵的形状:
(1, 400, 32)
,说明:1
是批次大小,表示输入数据只有一个样本(即只有一个文本输入)。400
是输入序列的最大长度,每个序列被填充到 400 个词。32
是每个单词的嵌入维度,每个单词被映射为一个 32 维的向量。
这表明,对于每个输入序列中的每个词,模型输出一个 32 维的嵌入向量,总共有 400 个词,每个词由 32 维向量表示。
(4)总结
- 模型架构:你的模型只有一个嵌入层,负责将输入的词序列转换为对应的 32 维向量表示。
- 嵌入矩阵的权重:这个权重矩阵大小为
(100, 32)
,表示你定义了一个包含 100 个词汇的词表,每个词的向量表示有 32 个维度。 - 预测输出:模型根据输入文本,将每个词映射为一个 32 维的向量,输出的形状是
(1, 400, 32)
,即每个输入文本(长度为 400)的每个词(总共 400 个词)都有一个 32 维的嵌入表示。
这个过程展示了如何使用 Keras 创建一个基本的嵌入层,并通过 Tokenizer
将文本转换为适合嵌入层处理的格式。
2.4 RNN网络模型
2.4.1 代码和结果
# 构建 RNN 网络模型
import tensorflow as tf # 导入所需库
# 创建空的网络模型 model
model = tf.keras.Sequential()
# 创建嵌入层并添加到 model 中
model.add(tf.keras.layers.Embedding(10, 5, input_length=6))
# 创建简单循环层并添加到 model 中
model.add(tf.keras.layers.SimpleRNN(128))
# 创建全连接层作为输出层,并添加到 model 中
model.add(tf.keras.layers.Dense(5, activation='softmax'))
# 显示网络模型的参数信息
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_1 (Embedding) (None, 6, 5) 50
simple_rnn (SimpleRNN) (None, 128) 17152
dense (Dense) (None, 5) 645
=================================================================
Total params: 17,847
Trainable params: 17,847
Non-trainable params: 0
_________________________________________________________________
2.4.2 代码解释
(1)导入 TensorFlow 库
import tensorflow as tf # 导入所需库
- 导入 TensorFlow 库,这是使用 Keras 构建和训练模型的核心库。
(2)创建空的网络模型
model = tf.keras.Sequential()
- 使用
Sequential
创建一个空的顺序模型(model
)。在Sequential
模型中,层是按顺序堆叠的,每一层的输入是前一层的输出。
(3)添加嵌入层(Embedding
Layer)
model.add(tf.keras.layers.Embedding(10, 5, input_length=6))
Embedding(10, 5, input_length=6)
:嵌入层将输入的整数(每个词的索引)转换为一个 5 维的稠密向量。它的输出形状将是 (batch_size, input_length, output_dim)
,在这个例子中,输出形状是 (batch_size, 6, 5)
。
input_dim=10
:词汇表的大小为 10,即输入数据中的单词索引最多为 9。output_dim=5
:每个单词将被嵌入为一个 5 维的向量。input_length=6
:每个输入序列的长度为 6,即每个输入将包含 6 个单词(或词汇索引)。
(4)添加循环层(SimpleRNN
)
model.add(tf.keras.layers.SimpleRNN(128))
SimpleRNN(128)
:创建一个简单的循环神经网络(RNN)层,包含 128 个隐藏单元。
- 这个循环层将接收前一层(
Embedding
层)输出的向量序列,并使用 RNN 单元(128 个)处理这个序列数据,生成最终的一个输出向量。 - 输出的形状是
(batch_size, 128)
,即将输入的长度为 6 的序列压缩成一个 128 维的向量。
(5)添加全连接层(Dense
Layer)
model.add(tf.keras.layers.Dense(5, activation='softmax'))
Dense(5, activation='softmax')
:这是一个全连接层,用于最终输出分类结果。
该层的输出形状是 (batch_size, 5)
,表示每个样本将有 5 个类别的概率输出,且这些概率的和为 1。
5
:表示输出的类别数(即输出层有 5 个神经元)。activation='softmax'
:使用 softmax 激活函数,将输出值转换为概率分布,适用于多分类任务。
(6)显示网络模型的参数信息
model.summary() # 显示网络模型的参数信息
model.summary()
会打印出模型的各层结构,包括每一层的输出形状、参数数量等信息。这有助于了解网络的整体架构。
(7)模型结构和参数分析
假设批量大小(batch_size
)为 32,输入序列的长度为 6。通过 model.summary()
可以看到模型的详细参数信息。下面是每一层的详细解释:
-
嵌入层(Embedding):
- 输入:词汇表大小为 10,输出:每个单词是 5 维的向量。
- 参数数量:
input_dim * output_dim = 10 * 5 = 50
,即嵌入层有 50 个可训练参数。 - 输出形状:
(batch_size, 6, 5)
,即每个输入序列(长度 6)都会被转换为 6 个 5 维的向量。
-
简单循环层(SimpleRNN):
- 输入:每个单词被表示为 5 维向量(来自嵌入层),序列长度为 6。
- 输出:128 维的向量表示整个序列的特征(最终的 RNN 输出)。
- 参数数量:
(input_dim * units) + (units * units) + units = (5 * 128) + (128 * 128) + 128 = 640 + 16384 + 128 = 17052
,即该层有 17,052 个可训练参数。 - 输出形状:
(batch_size, 128)
,即每个输入序列通过 RNN 处理后输出一个 128 维的向量。
-
全连接层(Dense):
- 输入:128 维的向量(来自 RNN 层)。
- 输出:5 维的概率分布(来自 softmax 激活)。
- 参数数量:
(input_dim * output_dim) + output_dim = (128 * 5) + 5 = 640 + 5 = 645
,即该层有 645 个可训练参数。 - 输出形状:
(batch_size, 5)
,即每个输入样本会有 5 个类别的概率输出。
2.4.3 结果分析
embedding_1 (Embedding) (None, 6, 5) 50
simple_rnn (SimpleRNN) (None, 128) 17152
dense (Dense) (None, 5) 645
=================================================================
Total params: 17,847
Trainable params: 17,847
Non-trainable params: 0
第一层:embedding_1
(Embedding Layer)
- 输出形状:
(None, 6, 5)
,表示每个输入序列(长度为 6)会被映射为 6 个 5 维的向量。 - 参数数量:
50
。这个值由input_dim * output_dim
计算得出,即10 * 5 = 50
。input_dim=10
表示词汇表的大小,output_dim=5
表示每个词将被映射为一个 5 维的向量。
第二层:simple_rnn
(SimpleRNN Layer)
-
输出形状:
(None, 128)
,表示该层将输入的序列压缩成一个 128 维的向量。RNN 会处理输入序列,并将每个时间步的输出与前一时间步的状态结合,最终生成一个固定大小的输出向量。 -
参数数量:
参数数量=(input_dim×units)+(units×units)+units\text{参数数量} = (input\_dim \times units) + (units \times units) + units参数数量=(input_dim×units)+(units×units)+units17,152
。这个参数数量是由以下公式计算的:input_dim = 5
(来自嵌入层的每个词的向量维度)units = 128
(SimpleRNN 中的隐藏单元数量)
所以参数数量是:(5×128)+(128×128)+128=640+16,384+128=17,152
第三层:dense
(Dense Layer)
-
输出形状:
(None, 5)
,表示该层将产生一个 5 维的向量,用于分类任务的 5 个类别的概率。 -
参数数量:
参数数量=(input_dim×output_dim)+output_dim\text{参数数量} = (input\_dim \times output\_dim) + output\_dim参数数量=(input_dim×output_dim)+output_dim645
。这个值是由以下公式计算得出的:input_dim = 128
(来自 RNN 层的输出维度)output_dim = 5
(输出层的神经元数量)
所以参数数量是:(128×5)+5=640+5=645
总参数数量
-
总参数数量:
50(嵌入层)+17,152(RNN层)+645(全连接层)=17,8475017,847
,是所有层的参数总和,计算公式如下:
总结
- 嵌入层(Embedding):输入的整数序列(最大长度 6,每个单词映射到 5 维的向量),共有 50 个参数。
- RNN 层(SimpleRNN):接收嵌入层的输出,处理整个序列,输出一个 128 维的向量,包含 17,152 个可训练参数。
- 全连接层(Dense):将 RNN 输出的 128 维向量映射为 5 类的概率分布,包含 645 个参数。
这个网络模型是一个简单的序列分类模型,首先通过嵌入层将词汇序列转化为向量,再通过 RNN 层学习序列的时序特征,最后通过全连接层进行分类。
2.5 构建LSTM网络
创建了一个包含 LSTM(长短期记忆) 层的神经网络模型。与普通的 RNN 层不同,LSTM 层能够捕捉更长范围的时序依赖关系,因为它包含了门控机制(如遗忘门、输入门、输出门)
2.5.1 代码和结果
import tensorflow as tf # 导入所需库
# 创建空的网络模型 model
model = tf.keras.Sequential()
# 创建嵌入层并添加到 model 中
model.add(tf.keras.layers.Embedding(10, 5, input_length=6))
# 创建长短期记忆层(LSTM)并添加到 model 中
model.add(tf.keras.layers.LSTM(128))
# 创建全连接层作为输出层,并添加到 model 中
model.add(tf.keras.layers.Dense(5, activation='softmax'))
# 显示网络模型的参数信息
model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_2 (Embedding) (None, 6, 5) 50
lstm (LSTM) (None, 128) 68608
dense_1 (Dense) (None, 5) 645
=================================================================
Total params: 69,303
Trainable params: 69,303
Non-trainable params: 0
_________________________________________________________________
2.5.2 代码解释
(1)导入 TensorFlow 库
import tensorflow as tf # 导入所需库
- 这里导入了
tensorflow
库,它包含了构建和训练深度学习模型所需的工具。
(2)创建空的网络模型
model = tf.keras.Sequential()
- 使用
Sequential
模型来创建一个线性堆叠的网络模型,意味着每一层的输出都是下一层的输入。
(3)添加嵌入层(Embedding
Layer)
model.add(tf.keras.layers.Embedding(10, 5, input_length=6))
Embedding(10, 5, input_length=6)
:
该层的输出形状是 (batch_size, 6, 5)
,表示每个输入序列(长度为 6)将被转换为 6 个 5 维的词向量。
input_dim=10
:词汇表的大小为 10(输入的数据中的每个词汇的索引不超过 9)。output_dim=5
:每个词将被表示为一个 5 维的向量。input_length=6
:每个输入序列的长度为 6,即每个输入样本由 6 个词组成。
(4)添加 LSTM 层(LSTM
)
model.add(tf.keras.layers.LSTM(128))
LSTM(128)
:这是一个 LSTM 层,包含 128 个隐藏单元。
- LSTM 层能够学习输入数据的时序特征,捕捉长时间跨度的依赖关系。
- 该层的输出形状是
(batch_size, 128)
,即每个输入序列经过 LSTM 处理后将被压缩成一个 128 维的向量。LSTM 层会为输入序列生成一个固定长度的输出。
(5)添加全连接层(Dense
)
model.add(tf.keras.layers.Dense(5, activation='softmax'))
Dense(5, activation='softmax')
:这是一个全连接层,用于生成分类任务的输出。
该层的输出形状是 (batch_size, 5)
,即每个输入样本会生成一个 5 维的概率分布,表示其属于每个类别的概率。
5
:表示输出的类别数,即 5 个分类。activation='softmax'
:使用softmax
激活函数,将输出的 5 个值转换为一个概率分布。softmax
会将输出值转换为 0 到 1 之间的概率,且所有输出的概率和为 1。
(6)显示网络模型的参数信息
model.summary() # 显示网络模型的参数信息
model.summary()
会打印出模型的层结构、每一层的输出形状以及可训练的参数数量,帮助我们了解模型的详细结构。
2.5.3 结果分析
Layer (type) Output Shape Param #
=================================================================
embedding_2 (Embedding) (None, 6, 5) 50
lstm (LSTM) (None, 128) 68608
dense_1 (Dense) (None, 5) 645
=================================================================
Total params: 69,303
Trainable params: 69,303
Non-trainable params: 0
(1)嵌入层(embedding_2
)
- 输出形状:
(None, 6, 5)
,表示输入的每个序列(长度为 6)会被映射为 6 个 5 维的向量。 - 参数数量:
50
,这个是由input_dim * output_dim
计算得出的,即10 * 5 = 50
。input_dim=10
:词汇表的大小(输入数据的词汇数量,即最大单词索引为 9)。output_dim=5
:每个词的词向量维度。
(2)LSTM 层(lstm
)
-
输出形状:
(None, 128)
,表示 LSTM 层会将每个输入序列压缩成一个 128 维的向量。 -
参数数量:
参数数量=4×(input_dim×units+units×units+units)\text{参数数量} = 4 \times (input\_dim \times units + units \times units + units)参数数量=4×(input_dim×units+units×units+units)68,608
,这是由以下公式计算的:其中:
input_dim = 5
(来自嵌入层的词向量维度)。units = 128
(LSTM 层的隐藏单元数)。
所以,LSTM 层的参数数量为:
4×((5×128)+(128×128)+128)=4×(640+16,384+128)=68,6084
(3)全连接层(dense_1
):
-
输出形状:
(None, 5)
,表示该层输出 5 个类别的概率值。 -
参数数量:
参数数量=(input_dim×output_dim)+output_dim645
,这是由以下公式计算的:其中:
input_dim = 128
(来自 LSTM 层的输出维度)。output_dim = 5
(全连接层的输出维度,即 5 个类别)。
所以,全连接层的参数数量为:(128×5)+5=640+5=645
(4)总参数数量
-
总参数数量:
50(嵌入层)+68,608(LSTM 层)+645(全连接层)=69,30350 (\text{嵌入层}) + 68,608 (\text{LSTM 层}) + 645 (\text{全连接层}) = 69,30350(嵌入层)+68,608(LSTM 层)+645(全连接层)=69,30369,303
,是所有层的参数总和,即:所有这些参数都是 可训练的,因此
Trainable params
为69,303
,Non-trainable params
为0
。
(5)总结
这个模型是一个简单的文本分类模型,包含:
- 嵌入层:将词汇索引转换为 5 维的词向量。
- LSTM 层:捕捉输入序列的时序特征,并输出一个 128 维的向量表示。
- 全连接层:输出 5 个类别的概率分布,用于多分类任务。
3.基于 GRU 、LSTM的情感分析模型
3.1 构建 GRU 神经网络模型
实现了一个基于 GRU(门控循环单元)的情感分类任务,目标是利用 IMDb 数据集预测影评是正面(1)还是负面(0)。这段代码实现了一个基于 GRU 的情感分析模型,处理 IMDb 数据集中的电影评论情感分类任务。模型通过训练数据来学习并进行预测,并在每一轮训练时绘制损失和准确率图,以帮助评估模型的训练过程。最终,模型在测试集上的表现通过损失和准确率输出。
3.1.1 代码和结果
import tensorflow as tf
from tensorflow.keras.datasets import imdb
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, Dropout, GRU, Dense
from tensorflow.keras.optimizers import RMSprop
import matplotlib.pyplot as plt
# (1) 准备数据
# 导入 IMDb 数据集
vocab_size = 4000 # 词汇表大小
maxlen = 400 # 输入序列的最大长度
# 加载数据集(仅保留 vocab_size 个最常用的单词)
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=vocab_size)
# 对训练集和测试集的特征值进行序列填充
x_train = pad_sequences(x_train, maxlen=maxlen, padding='post', truncating='post')
x_test = pad_sequences(x_test, maxlen=maxlen, padding='post', truncating='post')
# (2) 构建 GRU 神经网络模型
model = Sequential()
# 嵌入层
model.add(Embedding(input_dim=vocab_size, output_dim=32, input_length=maxlen))
# Dropout 层
model.add(Dropout(0.3))
# GRU 层
model.add(GRU(64))
# Dropout 层
model.add(Dropout(0.3))
# 输出层
model.add(Dense(1, activation='sigmoid'))
# 显示模型的参数信息
model.summary()
# (3) 编译、训练和评估 GRU 神经网络模型
# 编译模型
model.compile(optimizer=RMSprop(), loss='binary_crossentropy', metrics=['accuracy'])
# 训练模型
history = model.fit(
x_train, y_train,
batch_size=64,
epochs=50,
validation_split=0.2,
verbose=2
)
# 评估模型
test_loss, test_accuracy = model.evaluate(x_test, y_test, batch_size=64, verbose=2)
print(f"Test Loss: {test_loss:.4f}, Test Accuracy: {test_accuracy:.4f}")
# (4) 绘制损失和准确率图
# 获取历史数据
train_loss = history.history['loss']
val_loss = history.history['val_loss']
train_accuracy = history.history['accuracy']
val_accuracy = history.history['val_accuracy']
# 创建子图
plt.figure(figsize=(12, 6))
# 子图 1:损失函数值折线图
plt.subplot(1, 2, 1)
plt.plot(train_loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Loss Function')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
# 子图 2:准确率折线图
plt.subplot(1, 2, 2)
plt.plot(train_accuracy, label='Training Accuracy')
plt.plot(val_accuracy, label='Validation Accuracy')
plt.title('Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
# 显示图形
plt.tight_layout()
plt.show()
3.1.2 代码解释
这段代码实现了一个基于 GRU(门控循环单元) 的情感分析模型,使用 IMDb 数据集来进行电影评论的情感分类任务。下面逐部分分析代码:
(1)准备数据
from tensorflow.keras.datasets import imdb
from tensorflow.keras.preprocessing.sequence import pad_sequences
vocab_size = 4000 # 词汇表大小
maxlen = 400 # 输入序列的最大长度
# 加载数据集(仅保留 vocab_size 个最常用的单词)
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=vocab_size)
# 对训练集和测试集的特征值进行序列填充
x_train = pad_sequences(x_train, maxlen=maxlen, padding='post', truncating='post')
x_test = pad_sequences(x_test, maxlen=maxlen, padding='post', truncating='post')
imdb.load_data()
:加载 IMDb 数据集,num_words=vocab_size
参数限制了使用数据集中前 4000 个最常见的单词。返回的数据(x_train, y_train)
和(x_test, y_test)
分别是训练集和测试集的数据和标签。x_train
和x_test
是电影评论的单词索引序列。y_train
和y_test
是对应的情感标签,0 代表负面评论,1 代表正面评论。
pad_sequences()
:对输入序列进行填充或截断。maxlen=400
限制了每个评论的最大长度为 400,padding='post'
表示在序列的末尾进行填充,truncating='post'
表示截断序列时,从末尾开始截取。
(2)构建 GRU 神经网络模型
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, Dropout, GRU, Dense
model = Sequential()
# 嵌入层
model.add(Embedding(input_dim=vocab_size, output_dim=32, input_length=maxlen))
# Dropout 层
model.add(Dropout(0.3))
# GRU 层
model.add(GRU(64))
# Dropout 层
model.add(Dropout(0.3))
# 输出层
model.add(Dense(1, activation='sigmoid'))
Sequential()
:创建一个顺序模型,即每一层的输出都作为下一层的输入。Embedding(input_dim=vocab_size, output_dim=32, input_length=maxlen)
:嵌入层将每个单词的索引转换为固定长度的稠密向量。input_dim=vocab_size
表示词汇表的大小为 4000,output_dim=32
表示嵌入向量的维度是 32,input_length=maxlen
表示每个输入序列的长度是 400。Dropout(0.3)
:在训练过程中随机丢弃 30% 的神经元,以防止过拟合。GRU(64)
:GRU 层包含 64 个单元。GRU 是一种循环神经网络(RNN)变体,能够捕捉序列数据的长期依赖关系。相比于 LSTM,GRU 结构更简洁,但通常在很多任务中表现相似。Dense(1, activation='sigmoid')
:输出层,使用sigmoid
激活函数来输出一个 0 到 1 之间的值,表示电影评论的情感分类(正面或负面)。
(3)编译、训练和评估 GRU 神经网络模型
from tensorflow.keras.optimizers import RMSprop
# 编译模型
model.compile(optimizer=RMSprop(), loss='binary_crossentropy', metrics=['accuracy'])
# 训练模型
history = model.fit(
x_train, y_train,
batch_size=64,
epochs=50,
validation_split=0.2,
verbose=2
)
# 评估模型
test_loss, test_accuracy = model.evaluate(x_test, y_test, batch_size=64, verbose=2)
print(f"Test Loss: {test_loss:.4f}, Test Accuracy: {test_accuracy:.4f}")
model.compile()
:编译模型,指定优化器RMSprop()
、损失函数binary_crossentropy
(用于二分类任务),并设置评估指标为准确率(accuracy)。model.fit()
:训练模型,使用训练数据x_train
和标签y_train
,batch_size=64
表示每次训练时使用 64 个样本,epochs=50
表示训练 50 轮,validation_split=0.2
表示将 20% 的训练数据用于验证,verbose=2
会打印出每轮的训练进度。model.evaluate()
:评估模型,计算测试集的损失和准确率,batch_size=64
表示每次评估时使用 64 个样本。
(4)绘制损失和准确率图
import matplotlib.pyplot as plt
# 获取历史数据
train_loss = history.history['loss']
val_loss = history.history['val_loss']
train_accuracy = history.history['accuracy']
val_accuracy = history.history['val_accuracy']
# 创建子图
plt.figure(figsize=(12, 6))
# 子图 1:损失函数值折线图
plt.subplot(1, 2, 1)
plt.plot(train_loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Loss Function')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
# 子图 2:准确率折线图
plt.subplot(1, 2, 2)
plt.plot(train_accuracy, label='Training Accuracy')
plt.plot(val_accuracy, label='Validation Accuracy')
plt.title('Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
# 显示图形
plt.tight_layout()
plt.show()
history.history
:包含训练过程中每一轮的损失值和准确率,以及在验证集上的损失和准确率。plt.subplot()
:创建一个 1 行 2 列的子图,第一个子图用于显示损失函数的变化,第二个子图用于显示准确率的变化。plt.plot()
:绘制折线图,分别显示训练集和验证集的损失值和准确率。plt.legend()
:添加图例,标注训练集和验证集。plt.tight_layout()
:调整子图布局,确保图形不会重叠。
3.2 构建LSTM神经网络模型
3.2.1 代码和结果
这段代码实现了一个简单的 LSTM 模型,处理 IMDb 数据集中的电影评论情感分类任务,包含数据预处理、模型构建、训练、评估和可视化步骤。通过训练 50 轮后,模型可以对测试集进行评估,并通过图形展示训练过程中的损失和准确率变化情况。
import tensorflow as tf
from tensorflow.keras.datasets import imdb
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, Dropout, LSTM, Dense
from tensorflow.keras.optimizers import RMSprop
import matplotlib.pyplot as plt
# (1) 准备数据
# 导入 IMDb 数据集
vocab_size = 4000 # 词汇表大小
maxlen = 400 # 输入序列的最大长度
# 加载数据集(仅保留 vocab_size 个最常用的单词)
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=vocab_size)
# 对训练集和测试集的特征值进行序列填充
x_train = pad_sequences(x_train, maxlen=maxlen, padding='post', truncating='post')
x_test = pad_sequences(x_test, maxlen=maxlen, padding='post', truncating='post')
# (2) 构建 LSTM 神经网络模型
model = Sequential()
# 嵌入层
model.add(Embedding(input_dim=vocab_size, output_dim=32, input_length=maxlen))
# Dropout 层
model.add(Dropout(0.3))
# LSTM 层
model.add(LSTM(64))
# Dropout 层
model.add(Dropout(0.3))
# 输出层
model.add(Dense(1, activation='sigmoid'))
# 显示模型的参数信息
model.summary()
# (3) 编译、训练和评估 LSTM 神经网络模型
# 编译模型
model.compile(optimizer=RMSprop(), loss='binary_crossentropy', metrics=['accuracy'])
# 训练模型
history = model.fit(
x_train, y_train,
batch_size=64,
epochs=50,
validation_split=0.2,
verbose=2
)
# 评估模型
test_loss, test_accuracy = model.evaluate(x_test, y_test, batch_size=64, verbose=2)
print(f"Test Loss: {test_loss:.4f}, Test Accuracy: {test_accuracy:.4f}")
# (4) 绘制损失和准确率图
# 获取历史数据
train_loss = history.history['loss']
val_loss = history.history['val_loss']
train_accuracy = history.history['accuracy']
val_accuracy = history.history['val_accuracy']
# 创建子图
plt.figure(figsize=(12, 6))
# 子图 1:损失函数值折线图
plt.subplot(1, 2, 1)
plt.plot(train_loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('LSTM - Loss Function')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
# 子图 2:准确率折线图
plt.subplot(1, 2, 2)
plt.plot(train_accuracy, label='Training Accuracy')
plt.plot(val_accuracy, label='Validation Accuracy')
plt.title('LSTM - Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
# 显示图形
plt.tight_layout()
plt.show()
3.2.2 代码解释
(1)准备数据
from tensorflow.keras.datasets import imdb
from tensorflow.keras.preprocessing.sequence import pad_sequences
vocab_size = 4000 # 词汇表大小
maxlen = 400 # 输入序列的最大长度
# 加载数据集(仅保留 vocab_size 个最常用的单词)
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=vocab_size)
# 对训练集和测试集的特征值进行序列填充
x_train = pad_sequences(x_train, maxlen=maxlen, padding='post', truncating='post')
x_test = pad_sequences(x_test, maxlen=maxlen, padding='post', truncating='post')
imdb.load_data()
:加载 IMDb 数据集,num_words=vocab_size
参数指定仅使用数据集中最常见的 4000 个单词进行处理。返回的(x_train, y_train)
和(x_test, y_test)
是训练和测试数据,x_train
和x_test
是评论的单词索引列表,y_train
和y_test
是对应的标签(0 或 1,表示负面或正面评论)。pad_sequences()
:对评论序列进行填充或截断,以确保所有输入序列的长度为 400。padding='post'
表示如果评论短于 400,则在后面填充 0;truncating='post'
表示如果评论超过 400,则截断评论后面的部分。
(2)构建 LSTM 神经网络模型
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, Dropout, LSTM, Dense
model = Sequential()
# 嵌入层
model.add(Embedding(input_dim=vocab_size, output_dim=32, input_length=maxlen))
# Dropout 层
model.add(Dropout(0.3))
# LSTM 层
model.add(LSTM(64))
# Dropout 层
model.add(Dropout(0.3))
# 输出层
model.add(Dense(1, activation='sigmoid'))
Sequential()
:创建一个顺序模型,即每一层的输出连接到下一层。Embedding()
:嵌入层将每个单词的索引转换为一个固定长度的稠密向量(这里是 32 维)。input_dim=vocab_size
表示词汇表大小为 4000,output_dim=32
表示嵌入向量的维度为 32,input_length=maxlen
表示每个输入序列的长度为 400。Dropout(0.3)
:在训练过程中随机丢弃 30% 的神经元,防止过拟合。LSTM(64)
:LSTM 层包含 64 个单元,主要用于处理序列数据,捕捉时间序列中的长期依赖关系。Dense(1, activation='sigmoid')
:输出层,使用sigmoid
激活函数输出一个 0 到 1 之间的值,表示正面(1)或负面(0)的概率。
(3)编译、训练和评估模型
from tensorflow.keras.optimizers import RMSprop
# 编译模型
model.compile(optimizer=RMSprop(), loss='binary_crossentropy', metrics=['accuracy'])
# 训练模型
history = model.fit(
x_train, y_train,
batch_size=64,
epochs=50,
validation_split=0.2,
verbose=2
)
# 评估模型
test_loss, test_accuracy = model.evaluate(x_test, y_test, batch_size=64, verbose=2)
print(f"Test Loss: {test_loss:.4f}, Test Accuracy: {test_accuracy:.4f}")
model.compile()
:编译模型,指定优化器RMSprop()
、损失函数binary_crossentropy
(二分类问题使用的常见损失函数),并设置评估指标为准确率(accuracy)。model.fit()
:训练模型,使用训练数据x_train
和标签y_train
,batch_size=64
表示每次训练时处理 64 个样本,epochs=50
表示训练 50 轮,validation_split=0.2
表示将 20% 的训练数据用于验证,verbose=2
会打印出每轮的训练进度。model.evaluate()
:在测试集上评估模型的性能,返回损失和准确率。
(4)绘制损失和准确率图
import matplotlib.pyplot as plt
# 获取历史数据
train_loss = history.history['loss']
val_loss = history.history['val_loss']
train_accuracy = history.history['accuracy']
val_accuracy = history.history['val_accuracy']
# 创建子图
plt.figure(figsize=(12, 6))
# 子图 1:损失函数值折线图
plt.subplot(1, 2, 1)
plt.plot(train_loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('LSTM - Loss Function')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
# 子图 2:准确率折线图
plt.subplot(1, 2, 2)
plt.plot(train_accuracy, label='Training Accuracy')
plt.plot(val_accuracy, label='Validation Accuracy')
plt.title('LSTM - Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
# 显示图形
plt.tight_layout()
plt.show()
history.history
:包含训练过程中每一轮的损失值(loss
)和准确率(accuracy
)以及验证集上的损失值和准确率。plt.subplot()
:创建一个 1 行 2 列的子图,分别用于显示损失函数值和准确率的变化。plt.plot()
:绘制折线图,分别显示训练集和验证集的损失和准确率。plt.legend()
:添加图例,区分训练和验证数据。plt.tight_layout()
:调整子图布局,确保不重叠。