版本问题
导包
import tensorflow as tf
加载数据
加载并准备 MNIST 数据集。将样本数据从整数转换为浮点数:
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
搭建模型
通过堆叠层来构建 tf.keras.Sequential 模型。
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10)
])
-
Flatten 层:
- 作用: 将输入的二维数据(如图像)展平为一维。这对于将图像数据传递给全连接层(Dense layer)是必要的,因为全连接层通常接受一维输入。
- 参数:
input_shape
(tuple) - 输入数据的形状。这里(28, 28)
表示输入数据是 28x28 的二维数组(如 MNIST 图像)。
-
Dense 层:
- 作用: 全连接层,每个神经元与前一层的每一个神经元都连接。它对输入数据进行线性变换,并通过激活函数引入非线性。
- 参数:
- units (int) - 神经元的数量。这决定了该层的输出维度。例如,这里是 128。
- activation (str or function) - 激活函数,用于引入非线性。这里使用的是
'relu'
(Rectified Linear Unit),它将负值置为零,正值保持不变。
-
Dropout 层:
- 作用: 用于正则化,防止模型过拟合。它在训练过程中随机“丢弃”或忽略一定比例的神经元,减少过拟合的风险。
- 参数:
- rate (float) - 表示丢弃的比例。在这里,
0.2
表示在训练过程中,有 20% 的神经元会被随机忽略。
- rate (float) - 表示丢弃的比例。在这里,
-
Dense 层(输出层):
- 作用: 这是模型的输出层,用于生成最终的预测结果。这里的输出层没有激活函数,意味着它将计算线性输出值。
- 参数:
- units (int) - 神经元的数量。这里是 10,通常对应于分类问题中的类别数(例如 MNIST 数据集中有 10 个数字类别)。
对于每个样本,模型都会返回一个包含 logits 或 log-odds 分数的向量,每个类一个。
predictions = model(x_train[:1]).numpy()
predictions
[[-0.06230168 -0.4859897 -0.4349398 0.502046 0.8077229 0.466933
-0.27846724 -0.13608396 -0.27969462 0.17068624]]
在深度学习模型中,特别是在分类任务中,model(x_train[:1]).numpy()
返回的是模型对于输入样本的预测结果。这些结果通常是 logits 或 log-odds 分数,具体的理解如下:
-
Logits: Logits 是模型的原始输出值,通常是经过最后一层线性变换得到的。它们表示每个类别的相对得分,并且通常不会直接用于概率预测。Logits 可以看作是每个类的未归一化的得分。
model(x_train[:1])
调用模型对输入 x_train[:1]
(一个样本)的预测,返回的是 logits。这些 logits 是一个包含每个类分数的向量。
例如,假设模型是一个用于 MNIST 手写数字分类的神经网络,并且模型的最后一层是一个全连接层,其输出维度为 10(因为 MNIST 有 10 个类别)。对于一个输入样本,model(x_train[:1])
可能返回一个形如 [[-0.5, 2.3, 0.1, ..., 1.2]]
的数组,这里的每个值都代表了对应类别的 logits。
为了从 logits 转换为概率,通常会使用 Softmax 函数。Softmax 函数将 logits 转换为概率分布,使得所有类别的概率和为 1
tf.nn.softmax 函数将这些 logits 转换为每个类的概率
print(tf.nn.softmax(predictions).numpy())
[[0.07015824 0.18569683 0.05213543 0.04906429 0.13317907 0.02939027
0.10510781 0.115359 0.10000554 0.15990353]]
注:可以将 tf.nn.softmax 烘焙到网络最后一层的激活函数中。虽然这可以使模型输出更易解释,但不建议使用这种方式,因为在使用 softmax 输出时不可能为所有模型提供精确且数值稳定的损失计算。
使用 losses.SparseCategoricalCrossentropy 为训练定义损失函数,它会接受 logits 向量和 True
索引,并为每个样本返回一个标量损失。
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
此损失等于 true 类的负对数概率:如果模型确定类正确,则损失为零。
tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
是 TensorFlow 中用于多分类任务的损失函数。在训练过程中,它用于衡量模型预测的类别概率分布与真实类别标签之间的差距。让我们详细解释一下这段代码和其含义:
这里 SparseCategoricalCrossentropy
是一个损失函数,专门用于处理类别标签为整数索引(而不是 one-hot 编码)的分类任务。
from_logits=True
: 这个参数表示模型的输出是 logits(未经 softmax 激活的原始预测分数)。如果from_logits=False
,则表示模型的输出已经经过 softmax 激活,变成了概率分布。在这种情况下,损失函数会内部应用 softmax 来计算类别概率。
SparseCategoricalCrossentropy
计算的是交叉熵损失,它衡量的是模型预测的概率分布与实际标签分布之间的差异。具体来说,它计算了 true 类别的负对数概率。这个损失函数的公式如下:
其中:
- z \) 是模型输出的 logits 向量。
- 真实标签 i 是 logits 向量中实际的类别索引。
-
Logits 输入: 模型输出的是一个 logits 向量,每个元素表示一个类别的未归一化的预测分数。假设有三个类别,模型的 logits 输出可能是[2.0,1.0,0.1]。
-
Softmax 应用: 损失函数内部会对 logits 应用 softmax 函数,将其转换为概率分布。对于上述 logits,softmax 计算的结果可能是[0.7,0.2,0.1]。
-
计算负对数概率: 根据实际标签,找到正确类别的概率并取其负对数。例如,如果真实标签是类别 0,对应的概率是 0.7,损失就是 −log(0.7)。
在深度学习中,使用 from_logits=True
的 SparseCategoricalCrossentropy
损失函数意味着模型输出的是 logits(即未经 softmax 归一化的原始预测分数)。
-
选择合适的输出层:
-
没有激活函数:模型的最后一层(输出层)应该是一个全连接层(Dense 层),但不需要应用 softmax 激活函数。这样,网络输出的是 logits。
-
示例代码:
import tensorflow as tf # 假设分类任务有 10 个类别 num_classes = 10 model = tf.keras.Sequential([ tf.keras.layers.Dense(128, activation='relu', input_shape=(784,)), tf.keras.layers.Dense(num_classes) # 这里没有激活函数 ])
在上述示例中,
Dense(num_classes)
输出了一个形状为(batch_size, num_classes)
的 logits 张量。 -
-
定义损失函数:
-
使用
from_logits=True
的SparseCategoricalCrossentropy
损失函数来计算损失。 -
示例代码:
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
-
- 编译
-
在开始训练之前,使用 Keras Model.compile 配置和编译模型。将 optimizer 类设置为
adam
,将loss
设置为您之前定义的loss_fn
函数,并通过将metrics
参数设置为accuracy
来指定要为模型评估的指标。model.compile(optimizer='adam', loss=loss_fn, metrics=['accuracy'])
训练模型
-
训练模型时,确保目标标签(ground truth labels)是整数索引(而不是 one-hot 编码)。
-
示例代码:
model.fit(train_data, train_labels, epochs=5, batch_size=32)
使用 from_logits=True
的好处
-
数值稳定性:
- 避免数值不稳定性:直接对 logits 应用 softmax 可能导致数值不稳定性,尤其是在极端值的情况下(例如 logits 的绝对值非常大)。使用
from_logits=True
的损失函数内部会更稳定地处理这个过程,减轻数值不稳定问题。
- 避免数值不稳定性:直接对 logits 应用 softmax 可能导致数值不稳定性,尤其是在极端值的情况下(例如 logits 的绝对值非常大)。使用
-
性能优化:
- 更高效的计算:损失函数会内部处理 softmax 和交叉熵的计算,这可以避免在模型中额外计算 softmax。尤其在训练大型模型时,这可以减少计算开销和内存使用。
-
简化模型设计:
- 模型更简洁:在模型中不需要额外添加 softmax 层,使得模型结构更简洁。这样可以减少模型复杂度和潜在的错误来源。
让我们更详细地讨论 model.compile
方法的参数,并通过一些实际场景的例子来说明它们的用途。
1. optimizer
描述: 优化器是训练过程中用于更新模型权重的算法。不同的优化器具有不同的更新规则。
常见选择:
'adam'
: 自适应动量优化器,通常效果很好,适合大多数任务。'sgd'
: 随机梯度下降,常与动量(momentum)一起使用。'rmsprop'
: 根均方根传播,适用于处理稀疏梯度问题。
示例场景:
- 场景: 在训练一个用于图像分类的卷积神经网络(CNN)。希望模型在训练时快速收敛且能处理各种学习率的情况。
- 代码:
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
2. loss
描述: 损失函数用来衡量模型预测结果与实际标签之间的差距。在训练过程中,优化器会最小化这个损失函数。
常见选择:
'categorical_crossentropy'
: 适用于多类别分类任务,目标是 one-hot 编码。'sparse_categorical_crossentropy'
: 适用于多类别分类任务,目标是整数标签。'mean_squared_error'
: 适用于回归任务,计算预测值与实际值的均方误差。
示例场景:
- 场景: 进行房价预测任务,目标是通过特征预测房屋价格。目标是连续的实数。
- 代码:
model.compile(optimizer='adam', loss='mean_squared_error', metrics=['mae'])
3. loss_weights
描述: 如果模型有多个输出,可以为每个输出指定不同的损失权重。这对训练复杂模型很有用。
示例场景:
- 场景: 你在进行多任务学习,一个模型同时进行图像分类和物体检测。你希望分类任务的损失对总损失的贡献比检测任务大。
- 代码:
model.compile(optimizer='adam', loss=['categorical_crossentropy', 'mean_squared_error'], loss_weights=[0.7, 0.3], metrics=['accuracy'])
4. metrics
描述: 评估指标用于监控模型在训练和验证过程中的性能。它们不影响模型训练,但提供了有关模型性能的有用信息。
常见选择:
'accuracy'
: 常用于分类任务,表示预测准确率。'mae'
: 平均绝对误差,常用于回归任务。'mse'
: 均方误差,回归任务的另一个常用指标。
示例场景:
- 场景: 在训练一个二分类模型,如垃圾邮件分类器。希望监控模型的准确率。
- 代码:
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
5. loss_reduction
描述: 指示如何将损失值从单个样本聚合成批次损失值。主要用于处理自定义损失函数时的行为控制。
示例场景:
- 场景: 使用自定义损失函数,并希望控制损失聚合的方式(例如,取平均或求和)。
- 代码:
model.compile(optimizer='adam', loss=tf.keras.losses.MeanSquaredError(reduction=tf.keras.losses.Reduction.SUM))
6. weighted_metrics
描述: 计算加权指标,可以根据样本权重调整指标值。
示例场景:
- 场景: 在处理类别不平衡的数据集时,你希望按样本权重计算指标。
- 代码:
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'], weighted_metrics=['accuracy'])
7. run_eagerly
描述: 如果设置为 True
,模型将以 Eager Execution 模式运行,这有助于调试,但可能导致训练速度变慢。
示例场景:
- 场景: 你在调试模型,并需要逐步检查每一步的执行情况。
- 代码:
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', run_eagerly=True)
8. steps_per_execution
描述: 在每次 tf.function
执行时,处理的步骤数。可以提高训练效率,减少上下文切换的开销。
示例场景:
- 场景: 你希望在大型数据集上进行训练,通过每次执行多个步骤来优化训练速度。
- 代码:
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', steps_per_execution=10)
训练、保存、调用模型
我们详细讨论 model.fit
方法的所有参数,并结合实际场景进行解释。接着,我们讲解如何保存训练后的模型、如何调用和预测新数据。下面是 model.fit
方法及其参数的详细说明。
model.fit
方法的参数
-
x
- 类型: 训练数据(通常是 NumPy 数组、TensorFlow 张量或其他形式的输入数据)。
- 作用: 作为模型训练的输入数据。
- 示例场景: 你正在训练一个图像分类模型,
x_train
是包含图像数据的数组。
-
y
- 类型: 训练数据的标签(通常是 NumPy 数组、TensorFlow 张量或其他形式的标签)。
- 作用: 作为模型训练的目标数据。
- 示例场景: 对应于
x_train
,y_train
是图像数据对应的分类标签。
-
batch_size
- 类型: 整数或 None。
- 作用: 定义每次梯度更新的样本数。默认值为 32。
- 示例场景: 你有一个大的数据集,选择较小的
batch_size
可以使内存占用更少,但可能会导致训练速度变慢。 - 代码:
model.fit(x_train, y_train, epochs=5, batch_size=64)
-
epochs
- 类型: 整数。
- 作用: 定义训练过程中模型的迭代次数。
- 示例场景: 你希望模型经过 5 次完整的训练周期,以期获得更好的训练效果。
- 代码:
model.fit(x_train, y_train, epochs=5)
-
verbose
- 类型: 整数(0, 1, 2)。
- 作用: 设置训练过程的日志显示模式。0 表示无输出,1 表示进度条,2 表示每个 epoch 后一行日志。
- 示例场景: 你希望在训练过程中查看详细的进度信息。
- 代码:
model.fit(x_train, y_train, epochs=5, verbose=1)
-
validation_split
- 类型: 浮点数,介于 0 和 1 之间。
- 作用: 从训练数据中分割出一部分数据作为验证集。比如设置为 0.2 表示将 20% 的训练数据用于验证。
- 示例场景: 你希望在每个 epoch 后评估模型性能,但没有单独的验证集数据。
- 代码:
model.fit(x_train, y_train, epochs=5, validation_split=0.2)
-
validation_data
- 类型: 元组 (x_val, y_val),x_val 和 y_val 是验证数据和对应的标签。
- 作用: 提供单独的验证集数据,用于在每个 epoch 后评估模型。
- 示例场景: 你有一个专门的验证集
x_val
和y_val
,希望在训练过程中使用它来监控模型性能。 - 代码:
model.fit(x_train, y_train, epochs=5, validation_data=(x_val, y_val))
-
shuffle
- 类型: 布尔值或字符串 (‘batch’)。
- 作用: 是否在每个 epoch 前打乱训练数据。默认值是
True
。 - 示例场景: 如果训练数据有序且存在偏差,你可能希望打乱数据以提高模型泛化能力。
- 代码:
model.fit(x_train, y_train, epochs=5, shuffle=True)
-
class_weight
- 类型: 字典或 None。
- 作用: 为不同类别指定不同的权重,用于处理类别不平衡问题。
- 示例场景: 你的数据集中某些类别的样本远少于其他类别,想要调整训练过程中对这些类别的重视程度。
- 代码:
class_weight = {0: 1., 1: 5.} model.fit(x_train, y_train, epochs=5, class_weight=class_weight)
-
sample_weight
- 类型: 数组或 None。
- 作用: 为每个训练样本指定不同的权重。
- 示例场景: 你有一个训练数据集,其中某些样本比其他样本更重要。
- 代码:
sample_weight = np.array([1, 2, 1, 1, ...]) model.fit(x_train, y_train, epochs=5, sample_weight=sample_weight)
-
initial_epoch
- 类型: 整数。
- 作用: 从指定的 epoch 开始训练,可以用来继续训练。
- 示例场景: 你之前训练了模型到第 10 个 epoch,现在希望继续从第 11 个 epoch 开始训练。
- 代码:
model.fit(x_train, y_train, epochs=15, initial_epoch=10)
-
steps_per_epoch
- 类型: 整数。
- 作用: 每个 epoch 包含的步骤数,通常用于
tf.data
数据集。 - 示例场景: 你在使用自定义数据集时,想指定每个 epoch 训练的步骤数。
- 代码:
model.fit(x_train, y_train, epochs=5, steps_per_epoch=100)
-
validation_steps
- 类型: 整数。
- 作用: 每个验证 epoch 包含的步骤数,通常用于
tf.data
数据集。 - 示例场景: 你在使用自定义验证集时,指定每个验证 epoch 的步骤数。
- 代码:
model.fit(x_train, y_train, epochs=5, validation_data=(x_val, y_val), validation_steps=50)
-
max_queue_size
- 类型: 整数。
- 作用: 数据预取队列的大小。用于控制数据加载的并发性。
- 示例场景: 你希望加快数据加载速度,通过调整队列大小来优化数据预取。
- 代码:
model.fit(x_train, y_train, epochs=5, max_queue_size=10)
-
workers
- 类型: 整数。
- 作用: 用于数据加载的线程数。
- 示例场景: 你希望使用多个线程来提高数据加载速度。
- 代码:
model.fit(x_train, y_train, epochs=5, workers=4)
-
use_multiprocessing
- 类型: 布尔值。
- 作用: 是否使用多进程数据加载。
- 示例场景: 在数据预处理或加载过程中,你希望通过多进程加速数据读取。
- 代码:
model.fit(x_train, y_train, epochs=5, use_multiprocessing=True)
训练完的模型如何保存和调用
保存模型:
- 场景: 训练完成后,你希望将模型保存到磁盘以便下次使用。
- 代码:
model.save('my_model.h5') # 保存为 HDF5 格式
加载模型:
- 场景: 你希望加载之前保存的模型进行预测或进一步训练。
- 代码:
from tensorflow.keras.models import load_model model = load_model('my_model.h5')
使用模型进行预测
预测新数据:
- 场景: 你有一些新的图像数据,希望使用训练好的模型进行分类。
- 代码:
predictions = model.predict(x_new) # x_new 是新的图像数据
示例完整流程:
-
训练模型:
model.fit(x_train, y_train, epochs=5, batch_size=32, validation_split=0.2)
-
保存模型:
model.save('my_model.h5')
-
加载模型:
from tensorflow.keras.models import load_model model = load_model('my_model.h5')
-
预测新数据:
predictions = model.predict(x_new)