七、使用 Keras:深入探讨
本章涵盖
- 使用
Sequential
类、功能 API 和模型子类创建 Keras 模型 - 使用内置的 Keras 训练和评估循环
- 使用 Keras 回调函数自定义训练
- 使用 TensorBoard 监控训练和评估指标
- 从头开始编写训练和评估循环
您现在对 Keras 有了一些经验——您熟悉 Sequential 模型、Dense 层以及用于训练、评估和推断的内置 API——compile()
、fit()
、evaluate()
和 predict()
。您甚至在第三章中学习了如何从 Layer 类继承以创建自定义层,以及如何使用 TensorFlow 的 GradientTape 实现逐步训练循环。
在接下来的章节中,我们将深入研究计算机视觉、时间序列预测、自然语言处理和生成式深度学习。这些复杂的应用将需要比 Sequential
架构和默认的 fit()
循环更多的内容。所以让我们首先把你变成一个 Keras 专家!在本章中,您将全面了解如何使用 Keras API:这是您将需要处理下一个遇到的高级深度学习用例的关键方法。
7.1 一系列工作流程
Keras API 的设计遵循“逐步揭示复杂性”的原则:使入门变得容易,同时使处理高复杂性用例成为可能,只需要在每一步进行增量学习。简单用例应该易于接近,任意高级工作流程应该是可能的:无论您想做多么小众和复杂的事情,都应该有一条明确的路径。这条路径建立在您从更简单工作流程中学到的各种东西之上。这意味着您可以从初学者成长为专家,仍然可以以不同的方式使用相同的工具。
因此,并没有一种“真正”的使用 Keras 的方式。相反,Keras 提供了一系列工作流程,从非常简单到非常灵活。有不同的构建 Keras 模型的方式,以及不同的训练方式,满足不同的需求。因为所有这些工作流程都基于共享的 API,如 Layer
和 Model
,所以任何工作流程的组件都可以在任何其他工作流程中使用——它们可以相互通信。
7.2 构建 Keras 模型的不同方式
Keras 有三种构建模型的 API(见图 7.1):
- Sequential 模型,最易接近的 API——基本上就是一个 Python 列表。因此,它仅限于简单的层堆叠。
- 功能 API 专注于类似图形的模型架构。它在可用性和灵活性之间找到了一个很好的中间点,因此它是最常用的模型构建 API。
- 模型子类化,一种低级选项,您可以从头开始编写所有内容。如果您想要对每一点都有完全控制,这是理想的选择。但是,您将无法访问许多内置的 Keras 功能,并且更容易出错。
图 7.1 逐步揭示模型构建的复杂性
7.2.1 Sequential 模型
构建 Keras 模型的最简单方法是使用已知的 Sequential 模型。
列表 7.1 Sequential
类
from tensorflow import keras from tensorflow.keras import layers model = keras.Sequential([ layers.Dense(64, activation="relu"), layers.Dense(10, activation="softmax") ])
请注意,可以通过 add()
方法逐步构建相同的模型,这类似于 Python 列表的 append()
方法。
列表 7.2 逐步构建一个顺序模型
model = keras.Sequential() model.add(layers.Dense(64, activation="relu")) model.add(layers.Dense(10, activation="softmax"))
在第四章中,您看到层只有在第一次调用它们时才会构建(也就是说,创建它们的权重)。这是因为层的权重形状取决于它们的输入形状:在输入形状未知之前,它们无法被创建。
因此,前面的 Sequential 模型没有任何权重(列表 7.3),直到您实际在一些数据上调用它,或者使用输入形状调用其 build()
方法(列表 7.4)。
列表 7.3 尚未构建的模型没有权重
>>> model.weights # ❶ ValueError: Weights for model sequential_1 have not yet been created.
❶ 在那时,模型尚未构建。
列表 7.4 第一次调用模型以构建它
>>> model.build(input_shape=(None, 3)) # ❶ >>> model.weights # ❷ [<tf.Variable "dense_2/kernel:0" shape=(3, 64) dtype=float32, ... >, <tf.Variable "dense_2/bias:0" shape=(64,) dtype=float32, ... > <tf.Variable "dense_3/kernel:0" shape=(64, 10) dtype=float32, ... >, <tf.Variable "dense_3/bias:0" shape=(10,) dtype=float32, ... >]
❶ 构建模型 - 现在模型将期望形状为(3,)的样本。输入形状中的 None 表示批次大小可以是任意值。
❷ 现在你可以检索模型的权重。
模型构建完成后,你可以通过summary()
方法显示其内容,这对调试很有帮助。
列表 7.5 summary()
方法
>>> model.summary() Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_2 (Dense) (None, 64) 256 _________________________________________________________________ dense_3 (Dense) (None, 10) 650 ================================================================= Total params: 906 Trainable params: 906 Non-trainable params: 0 _________________________________________________________________
如你所见,这个模型恰好被命名为“sequential_1”。你可以为 Keras 中的所有内容命名 - 每个模型,每个层。
列表 7.6 使用name
参数为模型和层命名
>>> model = keras.Sequential(name="my_example_model") >>> model.add(layers.Dense(64, activation="relu", name="my_first_layer")) >>> model.add(layers.Dense(10, activation="softmax", name="my_last_layer")) >>> model.build((None, 3)) >>> model.summary() Model: "my_example_model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= my_first_layer (Dense) (None, 64) 256 _________________________________________________________________ my_last_layer (Dense) (None, 10) 650 ================================================================= Total params: 906 Trainable params: 906 Non-trainable params: 0 _________________________________________________________________
逐步构建 Sequential 模型时,能够在添加每个层后打印当前模型的摘要非常有用。但在构建模型之前无法打印摘要!实际上,有一种方法可以让你的Sequential
动态构建:只需提前声明模型输入的形状即可。你可以通过Input
类实现这一点。
列表 7.7 预先指定模型的输入形状
model = keras.Sequential() model.add(keras.Input(shape=(3,))) # ❶ model.add(layers.Dense(64, activation="relu"))
❶ 使用 Input 声明输入的形状。请注意,shape 参数必须是每个样本的形状,而不是一个批次的形状。
现在你可以使用summary()
来跟踪模型输出形状随着添加更多层而变化的情况:
>>> model.summary() Model: "sequential_2" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_4 (Dense) (None, 64) 256 ================================================================= Total params: 256 Trainable params: 256 Non-trainable params: 0 _________________________________________________________________ >>> model.add(layers.Dense(10, activation="softmax")) >>> model.summary() Model: "sequential_2" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_4 (Dense) (None, 64) 256 _________________________________________________________________ dense_5 (Dense) (None, 10) 650 ================================================================= Total params: 906 Trainable params: 906 Non-trainable params: 0 _________________________________________________________________
处理转换输入的层(如第八章中将学习的卷积层)时,这是一个相当常见的调试工作流程。
7.2.2 功能 API
Sequential 模型易于使用,但其适用性极为有限:它只能表达具有单个输入和单个输出的模型,按顺序一个接一个地应用各个层。实际上,很常见遇到具有多个输入(例如图像及其元数据)、多个输出(关于数据的不同预测)或非线性拓扑的模型。
在这种情况下,你将使用功能 API 构建模型。这是你在实际应用中遇到的大多数 Keras 模型所使用的方法。它既有趣又强大,感觉就像玩乐高积木一样。
一个简单的例子
让我们从一些简单的东西开始:我们在上一节中使用的两个层的堆叠。其功能 API 版本如下列表所示。
列表 7.8 具有两个Dense
层的简单功能模型
inputs = keras.Input(shape=(3,), name="my_input") features = layers.Dense(64, activation="relu")(inputs) outputs = layers.Dense(10, activation="softmax")(features) model = keras.Model(inputs=inputs, outputs=outputs)
让我们一步一步地过一遍这个过程。
我们首先声明了一个Input
(请注意,你也可以为这些输入对象命名,就像其他所有内容一样):
inputs = keras.Input(shape=(3,), name="my_input")
这个inputs
对象保存了关于模型将处理的数据形状和 dtype 的信息:
>>> inputs.shape (None, 3) # ❶ >>> inputs.dtype # ❷ float32
❶ 模型将处理每个样本形状为(3,)的批次。每批次的样本数量是可变的(由 None 批次大小表示)。
❷ 这些批次将具有 dtype float32。
我们称这样的对象为符号张量。它不包含任何实际数据,但它编码了模型在使用时将看到的实际数据张量的规格。它代表未来的数据张量。
接下来,我们创建了一个层并在输入上调用它:
features = layers.Dense(64, activation="relu")(inputs)
所有 Keras 层都可以在实际数据张量和这些符号张量上调用。在后一种情况下,它们将返回一个新的符号张量,带有更新的形状和 dtype 信息:
>>> features.shape (None, 64)
在获得最终输出后,我们通过在Model
构造函数中指定其输入和输出来实例化模型:
outputs = layers.Dense(10, activation="softmax")(features) model = keras.Model(inputs=inputs, outputs=outputs)
这是我们模型的摘要:
>>> model.summary() Model: "functional_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= my_input (InputLayer) [(None, 3)] 0 _________________________________________________________________ dense_6 (Dense) (None, 64) 256 _________________________________________________________________ dense_7 (Dense) (None, 10) 650 ================================================================= Total params: 906 Trainable params: 906 Non-trainable params: 0 _________________________________________________________________
多输入,多输出模型
与这个玩具模型不同,大多数深度学习模型看起来不像列表,而更像图形。例如,它们可能具有多个输入或多个输出。正是对于这种类型的模型,功能 API 真正发挥作用。
假设你正在构建一个系统,根据优先级对客户支持票据进行排名并将其路由到适当的部门。你的模型有三个输入:
- 票据的标题(文本输入)
- 票据的文本主体(文本输入)
- 用户添加的任何标签(假定为独热编码的分类输入)
我们可以将文本输入编码为大小为vocabulary_size
的一维数组(有关文本编码技术的详细信息,请参阅第十一章)。
您的模型还有两个输出:
- 票证的优先级分数,介于 0 和 1 之间的标量(sigmoid 输出)
- 应处理票证的部门(对部门集合进行 softmax)
您可以使用几行代码使用函数式 API 构建此模型。
列表 7.9 多输入、多输出函数式模型
vocabulary_size = 10000 num_tags = 100 num_departments = 4 title = keras.Input(shape=(vocabulary_size,), name="title") # ❶ text_body = keras.Input(shape=(vocabulary_size,), name="text_body") # ❶ tags = keras.Input(shape=(num_tags,), name="tags") # ❶ features = layers.Concatenate()([title, text_body, tags]) # ❷ features = layers.Dense(64, activation="relu")(features) # ❸ priority = layers.Dense(1, activation="sigmoid", name="priority")(features)# ❹ department = layers.Dense( num_departments, activation="softmax", name="department")(features) # ❹ model = keras.Model(inputs=[title, text_body, tags], # ❺ outputs=[priority, department]) # ❺
❶ 定义模型输入。
❷ 通过将它们连接起来,将输入特征组合成一个张量 features。
❸ 应用中间层以将输入特征重新组合为更丰富的表示。
❹ 定义模型输出。
❺ 通过指定其输入和输出来创建模型。
函数式 API 是一种简单、类似于乐高的、但非常灵活的方式,用于定义这样的层图。
训练多输入、多输出模型
您可以像训练序贯模型一样训练模型,通过使用输入和输出数据的列表调用fit()
。这些数据列表应与传递给Model
构造函数的输入顺序相同。
列表 7.10 通过提供输入和目标数组列表来训练模型
import numpy as np num_samples = 1280 title_data = np.random.randint(0, 2, size=(num_samples, vocabulary_size)) # ❶ text_body_data = np.random.randint(0, 2, size=(num_samples, vocabulary_size)) # ❶ tags_data = np.random.randint(0, 2, size=(num_samples, num_tags)) # ❶ priority_data = np.random.random(size=(num_samples, 1)) # ❷ department_data = np.random.randint(0, 2, size=(num_samples, num_departments))# ❷ model.compile(optimizer="rmsprop", loss=["mean_squared_error", "categorical_crossentropy"], metrics=[["mean_absolute_error"], ["accuracy"]]) model.fit([title_data, text_body_data, tags_data], [priority_data, department_data], epochs=1) model.evaluate([title_data, text_body_data, tags_data], [priority_data, department_data]) priority_preds, department_preds = model.predict( [title_data, text_body_data, tags_data])
❶ 虚拟输入数据
❷ 虚拟目标数据
如果您不想依赖输入顺序(例如,因为您有许多输入或输出),您还可以利用给Input
对象和输出层命名的名称,并通过字典传递数据。
列表 7.11 通过提供输入和目标数组的字典来训练模型
model.compile(optimizer="rmsprop", loss={"priority": "mean_squared_error", "department": "categorical_crossentropy"}, metrics={"priority": ["mean_absolute_error"], "department": ["accuracy"]}) model.fit({"title": title_data, "text_body": text_body_data, "tags": tags_data}, {"priority": priority_data, "department": department_data}, epochs=1) model.evaluate({"title": title_data, "text_body": text_body_data, "tags": tags_data}, {"priority": priority_data, "department": department_data}) priority_preds, department_preds = model.predict( {"title": title_data, "text_body": text_body_data, "tags": tags_data})
函数式 API 的强大之处:访问层连接性
函数式模型是一种显式的图数据结构。这使得可以检查层如何连接并重用先前的图节点(即层输出)作为新模型的一部分。它还很好地适应了大多数研究人员在思考深度神经网络时使用的“心智模型”:层的图。这使得两个重要用例成为可能:模型可视化和特征提取。
让我们可视化我们刚刚定义的模型的连接性(模型的拓扑结构)。您可以使用plot_model()
实用程序将函数式模型绘制为图形(参见图 7.2)。
keras.utils.plot_model(model, "ticket_classifier.png")
图 7.2 由plot_model()
在我们的票证分类器模型上生成的图
您可以在此图中添加模型中每个层的输入和输出形状,这在调试过程中可能会有所帮助(参见图 7.3)。
keras.utils.plot_model( model, "ticket_classifier_with_shape_info.png", show_shapes=True)
图 7.3 添加形状信息的模型图
张量形状中的“None”表示批处理大小:此模型允许任意大小的批处理。
访问层连接性还意味着您可以检查和重用图中的单个节点(层调用)。model.layers
模型属性提供组成模型的层列表,对于每个层,您可以查询layer.input
和layer.output
。
列表 7.12 检索函数式模型中层的输入或输出
>>> model.layers [<tensorflow.python.keras.engine.input_layer.InputLayer at 0x7fa963f9d358>, <tensorflow.python.keras.engine.input_layer.InputLayer at 0x7fa963f9d2e8>, <tensorflow.python.keras.engine.input_layer.InputLayer at 0x7fa963f9d470>, <tensorflow.python.keras.layers.merge.Concatenate at 0x7fa963f9d860>, <tensorflow.python.keras.layers.core.Dense at 0x7fa964074390>, <tensorflow.python.keras.layers.core.Dense at 0x7fa963f9d898>, <tensorflow.python.keras.layers.core.Dense at 0x7fa963f95470>] >>> model.layers[3].input [<tf.Tensor "title:0" shape=(None, 10000) dtype=float32>, <tf.Tensor "text_body:0" shape=(None, 10000) dtype=float32>, <tf.Tensor "tags:0" shape=(None, 100) dtype=float32>] >>> model.layers[3].output <tf.Tensor "concatenate/concat:0" shape=(None, 20100) dtype=float32>
这使您能够进行特征提取,创建重用另一个模型中间特征的模型。
假设您想要向先前的模型添加另一个输出—您想要估计给定问题票证解决所需时间,一种难度评级。您可以通过三个类别的分类层来实现这一点:“快速”、“中等”和“困难”。您无需从头开始重新创建和重新训练模型。您可以从先前模型的中间特征开始,因为您可以访问它们,就像这样。
列表 7.13 通过重用中间层输出创建新模型
features = model.layers[4].output # ❶ difficulty = layers.Dense(3, activation="softmax", name="difficulty")(features) new_model = keras.Model( inputs=[title, text_body, tags], outputs=[priority, department, difficulty])
❶ 层[4] 是我们的中间密集层
让我们绘制我们的新模型(参见图 7.4):
keras.utils.plot_model( new_model, "updated_ticket_classifier.png", show_shapes=True)
图 7.4 我们新模型的绘图
7.2.3 继承 Model 类
你应该了解的最后一个模型构建模式是最高级的一个:Model
子类化。你在第三章学习了如何子类化Layer
类来创建自定义层。子类化Model
与此类似:
- 在
__init__()
方法中,定义模型将使用的层。 - 在
call()
方法中,定义模型的前向传递,重用先前创建的层。 - 实例化你的子类,并在数据上调用它以创建其权重。
将我们之前的例子重写为一个子类模型
让我们看一个简单的例子:我们将使用Model
子类重新实现客户支持票务管理模型。
图 7.14 一个简单的子类模型
class CustomerTicketModel(keras.Model): def __init__(self, num_departments): super().__init__() # ❶ self.concat_layer = layers.Concatenate() # ❷ self.mixing_layer = layers.Dense(64, activation="relu") # ❷ self.priority_scorer = layers.Dense(1, activation="sigmoid") # ❷ self.department_classifier = layers.Dense( # ❷ num_departments, activation="softmax") def call(self, inputs): # ❸ title = inputs["title"] text_body = inputs["text_body"] tags = inputs["tags"] features = self.concat_layer([title, text_body, tags]) features = self.mixing_layer(features) priority = self.priority_scorer(features) department = self.department_classifier(features) return priority, department
❶ 不要忘记调用 super()构造函数!
❷ 在构造函数中定义子层。
❸ 在 call()方法中定义前向传递。
一旦你定义了模型,你可以实例化它。请注意,它只会在第一次在一些数据上调用它时创建它的权重,就像Layer
子类一样:
model = CustomerTicketModel(num_departments=4) priority, department = model( {"title": title_data, "text_body": text_body_data, "tags": tags_data})
到目前为止,一切看起来与Layer
子类化非常相似,这是你在第三章遇到的工作流程。那么,Layer
子类和Model
子类之间的区别是什么呢?很简单:一个“层”是你用来创建模型的构建块,而一个“模型”是你实际上将要训练、导出用于推断等的顶层对象。简而言之,一个Model
有fit()
、evaluate()
和predict()
方法。层没有。除此之外,这两个类几乎是相同的。(另一个区别是你可以保存模型到磁盘上的文件中,我们将在几节中介绍。)
你可以像编译和训练 Sequential 或 Functional 模型一样编译和训练Model
子类:
model.compile(optimizer="rmsprop", loss=["mean_squared_error", "categorical_crossentropy"], # ❶ metrics=[["mean_absolute_error"], ["accuracy"]]) # ❶ model.fit({"title": title_data, # ❷ "text_body": text_body_data, # ❷ "tags": tags_data}, # ❷ [priority_data, department_data], # ❸ epochs=1) model.evaluate({"title": title_data, "text_body": text_body_data, "tags": tags_data}, [priority_data, department_data]) priority_preds, department_preds = model.predict({"title": title_data, "text_body": text_body_data, "tags": tags_data})
❶ 作为损失和指标参数传递的结构必须与 call()返回的完全匹配——这里是两个元素的列表。
❷ 输入数据的结构必须与 call()方法所期望的完全匹配——这里是一个具有标题、正文和标签键的字典。
❸ 目标数据的结构必须与 call()方法返回的完全匹配——这里是两个元素的列表。
Model
子类化工作流是构建模型的最灵活方式。它使你能够构建无法表示为层的有向无环图的模型——想象一下,一个模型在call()
方法中使用层在一个for
循环内,甚至递归调用它们。任何事情都是可能的——你有控制权。
警告:子类模型不支持的内容
这种自由是有代价的:对于子类模型,你需要负责更多的模型逻辑,这意味着你的潜在错误面更大。因此,你将需要更多的调试工作。你正在开发一个新的 Python 对象,而不仅仅是将 LEGO 积木拼在一起。
函数式模型和子类模型在本质上也有很大的不同。函数式模型是一个显式的数据结构——层的图,你可以查看、检查和修改。子类模型是一段字节码——一个带有包含原始代码的call()
方法的 Python 类。这是子类化工作流程灵活性的源泉——你可以编写任何你喜欢的功能,但它也引入了新的限制。
例如,因为层之间的连接方式隐藏在call()
方法的内部,你无法访问该信息。调用summary()
不会显示层连接,并且你无法通过plot_model()
绘制模型拓扑。同样,如果你有一个子类模型,你无法访问层图的节点进行特征提取,因为根本没有图。一旦模型被实例化,其前向传递就变成了一个完全的黑匣子。
7.2.4 混合和匹配不同的组件
重要的是,选择这些模式之一——Sequential 模型、Functional API 或 Model
子类化——不会将您排除在其他模式之外。Keras API 中的所有模型都可以平滑地相互操作,无论它们是 Sequential 模型、Functional 模型还是从头开始编写的子类化模型。它们都是同一系列工作流的一部分。
例如,您可以在 Functional 模型中使用子类化层或模型。
列表 7.15 创建包含子类化模型的 Functional 模型
class Classifier(keras.Model): def __init__(self, num_classes=2): super().__init__() if num_classes == 2: num_units = 1 activation = "sigmoid" else: num_units = num_classes activation = "softmax" self.dense = layers.Dense(num_units, activation=activation) def call(self, inputs): return self.dense(inputs) inputs = keras.Input(shape=(3,)) features = layers.Dense(64, activation="relu")(inputs) outputs = Classifier(num_classes=10)(features) model = keras.Model(inputs=inputs, outputs=outputs)
相反地,您可以将 Functional 模型用作子类化层或模型的一部分。
列表 7.16 创建包含 Functional 模型的子类化模型
inputs = keras.Input(shape=(64,)) outputs = layers.Dense(1, activation="sigmoid")(inputs) binary_classifier = keras.Model(inputs=inputs, outputs=outputs) class MyModel(keras.Model): def __init__(self, num_classes=2): super().__init__() self.dense = layers.Dense(64, activation="relu") self.classifier = binary_classifier def call(self, inputs): features = self.dense(inputs) return self.classifier(features) model = MyModel()
7.2.5 记住:使用合适的工具来完成工作
您已经了解了构建 Keras 模型的工作流程的范围,从最简单的工作流程 Sequential 模型到最先进的工作流程模型子类化。何时应该使用其中一个而不是另一个?每种方法都有其优缺点——选择最适合手头工作的方法。
一般来说,Functional API 为您提供了易用性和灵活性之间的很好的权衡。它还为您提供了直接访问层连接性的功能,这对于模型绘图或特征提取等用例非常强大。如果您可以使用 Functional API——也就是说,如果您的模型可以表示为层的有向无环图——我建议您使用它而不是模型子类化。
今后,本书中的所有示例都将使用 Functional API,仅因为我们将使用的所有模型都可以表示为层的图。但是,我们将经常使用子类化层。一般来说,使用包含子类化层的 Functional 模型既具有高开发灵活性,又保留了 Functional API 的优势。
7.3 使用内置的训练和评估循环
逐步披露复杂性的原则——从非常简单到任意灵活的工作流程的访问,一步一步——也适用于模型训练。Keras 为您提供了不同的训练模型的工作流程。它们可以简单到在数据上调用 fit()
,也可以高级到从头开始编写新的训练算法。
您已经熟悉了 compile()
、fit()
、evaluate()
、predict()
的工作流程。作为提醒,请查看以下列表。
列表 7.17 标准工作流程:compile()
、fit()
、evaluate()
、predict()
from tensorflow.keras.datasets import mnist def get_mnist_model(): # ❶ inputs = keras.Input(shape=(28 * 28,)) features = layers.Dense(512, activation="relu")(inputs) features = layers.Dropout(0.5)(features) outputs = layers.Dense(10, activation="softmax")(features) model = keras.Model(inputs, outputs) return model (images, labels), (test_images, test_labels) = mnist.load_data() # ❷ images = images.reshape((60000, 28 * 28)).astype("float32") / 255 test_images = test_images.reshape((10000, 28 * 28)).astype("float32") / 255 train_images, val_images = images[10000:], images[:10000] train_labels, val_labels = labels[10000:], labels[:10000] model = get_mnist_model() model.compile(optimizer="rmsprop", # ❸ loss="sparse_categorical_crossentropy", # ❸ metrics=["accuracy"]) # ❸ model.fit(train_images, train_labels, # ❹ epochs=3, # ❹ validation_data=(val_images, val_labels)) # ❹ test_metrics = model.evaluate(test_images, test_labels) # ❺ predictions = model.predict(test_images) # ❻
❶ 创建一个模型(我们将其分解为一个单独的函数,以便以后重用)。
❷ 加载数据,保留一些用于验证。
❸ 通过指定其优化器、要最小化的损失函数和要监视的指标来编译模型。
❹ 使用 fit() 训练模型,可选择提供验证数据以监视在未见数据上的性能。
❺ 使用 evaluate() 在新数据上计算损失和指标。
❻ 使用 predict() 在新数据上计算分类概率。
有几种方法可以自定义这个简单的工作流程:
- 提供您自己的自定义指标。
- 将 callbacks 传递给
fit()
方法以安排在训练过程中的特定时间点执行的操作。
让我们来看看这些。
Python 深度学习第二版(GPT 重译)(三)(2)https://developer.aliyun.com/article/1485270