如果你曾经有过成功完成一个生产就绪的机器学习 ( ML )应用的乐趣,或者你目前正在开发你的第一个 ML 项目,我相信当我说“这不是一个简单的任务”时,你会同意我的观点。”
我为什么这么说?好吧,如果我们忽略收集正确的训练数据、分析和理解这些数据、然后构建和训练可能的最佳模型所涉及的复杂性,我相信你会同意 ML 过程本身是一个复杂的任务过程,非常耗时,并且完全是手动的,使得它非常难以自动化。正是这些因素,加上许多其他因素,导致了 ML 任务难以自动化。
本章的主要目标是通过回顾一个实际例子来强调这些挑战,这个例子为为什么 ML 过程的自动化是困难的做好了准备。本章将强调在执行这种自动化时应该考虑哪些控制因素,以及如何利用各种Amazon Web Services(AWS)能力来使推动 ML 项目进入生产的任务不那么令人生畏并且完全自动化。在本章结束时,我们将建立一个通过自动化克服这些挑战的共同基础。
因此,在本章中,我们将涵盖以下主题:
- ML 流程概述
- 洗钱过程的复杂性
- 端到端 ML 流程的一个示例
- AWS 如何使 ML 开发和部署过程更容易自动化
技术要求
您需要访问 Jupyter 笔记本环境,以便按照本章中的示例进行操作。虽然已经为 ML 过程的各个步骤提供了示例代码,但是在本书的 GitHub 资源库(https://GitHub . com/packt publishing/Automated-Machine-Learning-on-AWS/blob/main/chapter 01/ML % 20 process % 20 example . ipynb)中提供了一个 Jupyter 笔记本示例,供您按照自己的进度完成整个示例。
有关如何设置 Jupyter 笔记本环境的更多说明,您可以参考安装指南(https://jupyterlab . readthe docs . io/en/stable/getting _ started/installation . html)来设置 Jupyter lab 或 classic Jupyter 笔记本。或者,对于使用开发 IDE 的本地笔记本开发,比如 Visual Studio 代码,可以参考 VS 代码文档(https://Code . Visual Studio . com/docs/data science/jupyter-notebooks)。
洗钱流程概述
不幸的是,在执行 ML 时没有既定的操作指南。这是因为每个 ML 用例都是独一无二的,并且特定于应用,因此利用了最终的 ML 模型。相反,有一个通用的过程模式,大多数数据科学家、 ML 工程师和 ML 实践者都遵循这个模式。这个过程模型被称为数据挖掘的跨行业标准过程 ( CRISP-DM )和虽然不是每个人都一字不差地遵循该过程的具体步骤,但大多数生产 ML 模型都可能以某种形式通过使用 CRISP-DM 方法提供的防护栏来构建。
因此,当我们提到 ML 流程时,我们总是指使用 CRSIP-DM 的护栏构建生产就绪 ML 模型的整体方法。
下图显示了 CRISP-DM 关于创建 ML 从业者可能遵循的典型流程的指南概述: ![Figure 1.1 – Overview of a typical ML process ]
图 1.1–典型 ML 流程概述
简而言之,过程从 ML 从业者开始,他们的任务是提供一个解决特定业务用例的 ML 模型。然后,ML 从业者发现、吸收并分析适当的数据集,该数据集可以被有效地利用来实现 ML 项目的目标。
一旦数据被分析,ML 从业者确定最适用的建模技术,从数据中提取最相关的信息来处理用例。这些技术包括以下内容:
- 确定最适用的最大似然算法
- 创建数据的新方面(设计新特性),可以进一步提高所选模型的整体有效性
- 将数据分成训练集和测试集,用于模型训练和评估
然后,ML 从业者编纂算法的架构和训练/测试/评估例程。然后执行这些例程来确定可能的最佳模型参数——优化模型以适应数据和业务用例的参数。
最后,将最佳模型部署到生产中,以服务于与业务用例的初始目标相匹配的预测。
如您所见,整个过程似乎相对简单易懂。所以,你可能想知道所有的大惊小怪是什么。比如你可能在问自己,这个过程中的复杂在哪里?或你为什么说这很难自动化?
虽然这个过程看起来很简单,但执行时的现实却大不相同。下图提供了一个更真实的表示,展示了 ML 从业者在开发 ML 用例时可能观察到的情况: ![Figure 1.2 – Overview of a realistic ML process ]
图 1.2–现实 ML 流程概述
正如你所见,整个过程远比图 1.1 所示的典型表示复杂。在这个过程中可能有多种不同的途径。每个行动过程都是基于流程中上一步所获得的结果。此外,采取特定的行动过程可能并不总是产生预期的结果,因此迫使 ML 从业者不得不重新设置或返回并选择一组不同的标准,这将有望产生更好的结果。
因此,现在我们已经提供了典型的 ML 过程应该包括什么的高级概述,让我们检查一些使 ML 过程变得困难的复杂性和挑战。
洗钱过程的复杂性
过程中的每一次迭代都是一次实验,以观察在过程的前一部分所做的改变是否会产生更好的结果或更优化的 ML 模型。正是这个迭代的过程使得 ML 工作流难以自动化。每次迭代或实验的目标都是提高模型的整体预测能力。在每次迭代中,我们微调参数,发现新的变量,并验证这些变化提高了模型预测的整体准确性。每个实验还提供了对我们在整个过程中所处位置以及下一步可能采取的措施的进一步了解。本质上,必须潜在地返回并调整先前的步骤,或者甚至返回到该过程的最开始,并从不同的一组数据、参数或者甚至不同的 ML 模型开始,这是一个手动过程。但即使是不成功的实验也有价值,因为它们让我们从错误中学习,并有希望引导我们走向成功。
注意
容忍失败,不让失败破坏整个 ML 过程,是任何成功 ML 战略的关键因素。
因此,如果整个过程是复杂的,并且执行该方法产生失败,这将有希望导致更成功的结果,这将影响整个 ML 策略。显而易见,为什么自动化整个过程是具有挑战性的,但却是必要的,因为它现在已经成为任何 ML 项目整体成功标准的关键部分。
现在我们已经很好地了解了是什么让 ML 过程变得困难,让我们通过一个实际的例子来进一步探讨这些挑战。
端到端 ML 流程示例
为了更好地说明整个 ML 过程是困难的并且自动化是具有挑战性的,但也是至关重要的,我们将用一个实际操作的用例来搭建舞台。
介绍 ACME 渔业物流
ACME Fishing Logistics 是一个虚构的组织,关注海螺或鲍鱼种群的过度捕捞。他们的主要目标是教育渔民如何判断一只鲍鱼是否达到繁殖年龄。确定年龄的过程之所以具有挑战性,是因为为了验证鲍鱼的年龄,需要剥壳,以便对壳的内部进行染色,然后通过显微镜计数年轮的数量。这包括销毁鲍鱼,以确定它是否足够老,可以保留或放回海洋。因此,ACME 的章程和他们网站背后的目标是帮助渔民评估鲍鱼的各种物理特征,以便他们可以在不杀死它的情况下确定它的年龄。
ML 的情况
正如你可能想象的那样,ACME 在通过简单的教育过程来防止鲍鱼过度捕捞方面并没有取得令人难以置信的成功。首席技术官决定必须实现更加积极主动的战略。由于这一点,他们已经让网站管理员使用 ML 来更准确地预测鲍鱼的年龄,当渔民将他们捕获的身体特征输入网站的新年龄计算器模块时。这就是你进来的地方,作为 ACME 的常驻 ML 从业者-你的工作是创建 ML 模型,为新的年龄计算器提供鲍鱼年龄预测。
我们可以从使用 CRISP-DM 指南开始,并构建业务用例。业务用例是一个包罗万象的步骤,它建立了总体框架并整合了 CRISP-DM 流程的各个步骤。
这一阶段的目的是建立业务目标,并创建实现这些目标的项目计划。这个阶段还包括确定相关的标准,这些标准从商业的角度定义了项目是否被认为是成功的;例如:
- 业务目标:这个项目的目标是创建一个年龄计算器 web 应用,使渔民能够确定他们捕获的鲍鱼的年龄,以确定它是否低于繁殖年龄阈值。为了确定如何实现这个业务目标,出现了几个问题。比如年龄预测需要多精确?将使用哪些评估指标来确定预测的准确性?什么是可接受的精度阈值?用例是否有有效的数据?这个项目需要多长时间?像这样的问题有助于为计划设定现实的目标。
- 项目计划:可以通过调查这些问题的答案可能是什么来制定项目计划。例如,通过调查使用什么数据以及在哪里找到数据,我们可以开始制定获取数据的困难,这影响了项目可能需要多长时间。此外,了解模型的复杂性,这也会影响项目时间表,因为更复杂的模型需要更多的时间来构建、评估和调整。
- 成功标准:随着项目计划开始形成,我们开始了解成功的样子以及如何衡量它。例如,如果我们知道创建一个复杂的模型会对交付时间表产生负面影响,我们可以放宽模型的可接受预测准确性标准,并减少开发生产级模型所需的时间。此外,如果商业目标仅仅是帮助渔民确定鲍鱼年龄,但我们无法跟踪他们是否遵守建议,那么我们的成功标准可以被衡量——不是根据模型的准确性,而是根据年龄计算器被访问和使用的频率。例如,如果我们一天有 10 个应用点击,那么这个项目就被认为是成功的。
虽然这些只是流程这一阶段的示例,但它说明了在任何 ML 流程开始之前,必须进行仔细的预先考虑和规划,以及一组非常具体的目标。它还说明了过程的这个阶段不能自动化,尽管有一个带有预定义目标的设定计划创建了一个基础,在这个基础上有可能合并自动化框架。
从数据中获得洞察力
现在整个商业案例已经就绪,我们可以从数据阶段开始,深入实际的 ML 流程。如下图所示,数据阶段是业务案例框架中的第一个单独步骤: ![Figure 1.3 – The data stage ]
图 1.3–数据阶段
正是在这一点上,我们决定了哪些数据可用,如何接收数据,数据看起来像什么,数据的哪些特征与预测年龄最相关,以及哪些功能需要重新设计以创建最佳的生产就绪模型。
重要说明
众所周知,流程中的数据获取和探索性分析部分可能会占整个工作的 70%-80%。
一个值得被认为是生产就绪的模型只有在它被训练的数据的基础上才是好的。需要充分分析和完全理解这些数据,以提取最相关的特征用于模型构建和训练。我们可以使用一种通常被称为的技术来实现这一点,即探索性数据分析 ( EDA ),在这种技术中,我们评估数据的统计成分,潜在地可视化并创建图表来完全掌握特征相关性。一旦我们掌握了特征的重要性,我们可能会选择获取更重要的数据,删除不重要的数据,并可能设计数据的新方面,所有这些都是为了让训练好的模型从这些最佳特征中学习。
让我们看一个例子,看看这个过程的这个阶段对于年龄计算器用例可能是什么样子。
获取、吸收和理解数据
对于我们的例子,我们将使用鲍鱼数据集。
注意
鲍鱼数据集来自加州大学欧文分校的 ML 知识库:Dua,d .和 Graff,C. (2019)。http://archive.ics.uci.edu/ml 的 UCI 机器学习知识库。加州欧文:加州大学信息与计算机科学学院。
该数据集包含鲍鱼的各种物理特征,可用于确定其年龄。以下步骤将带您了解如何访问和浏览数据集:
- 我们可以使用以下示例 Python 代码加载数据集,该代码使用
pandas
库(https://pandas.pydata.org)通过read_csv()
方法以逗号分隔值(csv
)格式接收数据。由于源数据没有任何列名,我们可以查看column_names
:import pandas as pd column_names = ["sex", "length", "diameter", "height", "whole_weight", "shucked_weight", "viscera_weight", "shell_weight", "rings"] abalone_data = pd.read_csv("http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.data", names=column_names)
- 既然已经下载了数据,我们可以开始将它作为数据帧进行分析。首先,我们将抽取前五行数据的样本,以确保我们已经成功下载了它,并验证它是否与网站上突出显示的属性信息相匹配。以下示例 Python 代码调用了
abalone_data
数据帧上的head()
方法:abalone_data.head()
下面的屏幕截图显示了执行该调用的输出: ![Figure 1.4 – The first five rows of the Abalone Dataset ]
图 1.4–鲍鱼数据集的前五行
尽管我们只查看数据的前五行,但它与存储库网站提供的属性信息相匹配。例如,我们可以看到性别列有标称值,显示鲍鱼是雄性( M )、雌性( F )还是幼体( I )。我们还有年轮栏,用来确定鲍鱼的年龄。诸如重量、直径和高度的附加栏详细描述了鲍鱼的附加特征。这些特征都有助于确定其年龄(以年为单位)。使用年轮数加上 1.5 来计算年龄。
- 接下来,我们可以使用下面的示例代码来调用
abalone_data
数据帧上的describe()
方法:abalone_data.describe()
下面的截图显示了数据集的汇总统计,以及各种统计细节,如百分位数、均值、标准差: ![Figure 1.5 – The summary statistics of the Abalone Dataset ]
图 1.5–鲍鱼数据集的汇总统计数据
注意
此时,我们可以通过可视化和绘制关键要素之间的任何相关性来了解数据,从而进一步了解数据的分布情况,并确定数据集中最重要的要素。我们还应该确定我们是否有缺失的数据,以及我们是否有足够的数据。
仅使用汇总统计数据来理解数据往往会产生误导。虽然我们不会在这个例子中执行这些可视化任务,但是您可以通过查看 Kaggle 上的 Anscombe's Quartet 例子(https://www . ka ggle . com/carlmcbrideellis/Anscombe-s-Quartet-and-the-importance-of-EDA)来回顾为什么使用图形技术对理解数据如此重要。
前面的任务强调了我们从数据集的汇总统计数据中得出的一些重要观察结果。例如,在查看了数据集的描述性统计数据(图 1.5 )后,我们得出了以下重要观察结果:
- 每列的计数值为 4177 。我们可以推断出,对于每个特征,我们有相同数量的观察值,因此没有缺失值。这意味着我们不必以某种方式推断这些丢失的值可能是什么,或者从数据中删除包含它们的行。如果数据丢失,大多数 ML 算法都会失败。
- 如果你查看环列的 75% 值,在 11 环和最大环量之间有一个显著的差异,即 29 。这意味着数据可能包含异常值,这些异常值可能会添加不必要的噪声,并影响训练模型的整体模型有效性。
- 虽然在图 1.4 中可以看到性别列,但是在图 1.5 中显示的汇总统计不包括该列。这是因为该列中数据的类型。如果你参考数据集网站的属性信息部分(https://archive.ics.uci.edu/ml/datasets/abalone,你会看到这个性别栏由名义数据组成。这种类型的数据用于为没有量化值的数据提供标签或类别。由于没有量化值,因此无法显示该列的汇总统计信息。根据为解决业务目标而选择的最大似然算法的类型,我们可能需要将该数据转换为定量格式,因为并非所有的最大似然算法都适用于名义数据。
接下来的一组步骤将帮助我们应用从数据集学到的知识,使其与流程的模型训练部分更加兼容:
- 在这一步中,我们关注于转换
abalone_data
DataFrame 上的get_dummies()
方法,这将把男性( M )、女性( F )和婴儿( I )的类别转换成单独的特征列。在这里,这些新列中的数据将或者反映其中一个类别,如果为真则用 1(1)表示,如果为假则用 0(0)表示:abalone_data = pd.get_dummies(abalone_data)
- 再次运行
head()
方法,现在显示新转换数据的前五行:Abalone_data.head()
下面的截图显示了转换后的数据集的前五行。在这里,您可以看到性别列已被删除,取而代之的是三个新列(每个新类别一列),数据现在表示为 1 或 0 的离散值: ![Figure 1.6 – The first five rows of the converted Abalone Dataset ]
图 1.6–转换后的鲍鱼数据集的前五行
- 为模型构建和训练准备数据的下一步是将环列从数据中分离出来,将其建立为目标,或我们试图预测的变量。下面的示例代码说明了这一点:
y = abalone_data.rings.values del abalone_data["rings"]
- 既然目标变量已经被隔离,我们可以标准化特征。然而,并不是所有的数据集都需要标准化。通过查看图 1.5 ,我们可以看到,汇总统计数据显示,特性具有不同的范围。这些不同的范围,尤其是当值很大时,会影响模型在训练期间的整体有效性。因此,通过归一化特征,模型可以更快地收敛到全局最小值。以下代码示例显示了如何通过首先将现有特性从的 scikit-learn 或 sklearn Python 库(https://scikit-learn.org/stable/):
转换为import numpy as np from sklearn import preprocessing X = abalone_data.values.astype(np.float) X = preprocessing.normalize(X)
normalize()
方法来规范化现有特性
基于来自数据集的初始观察,我们已经应用了必要的变换来为模型训练准备特征。例如,我们将性别列从名义数据类型转换为定量数据类型,因为该数据将在确定鲍鱼的年龄时发挥重要作用。
从这个例子中,您可以看到数据步骤的目标是关注探索和理解数据集。我们还使用这一步来应用我们所学的知识,并改变数据或预处理成适合下游模型建立和训练过程的表示。
建立正确的模型
既然已经获取、分析和处理了数据,我们就准备进入 ML 过程的下一个阶段,在这里我们将着眼于构建合适的 ML 模型,以适应业务用例,并将其与我们新获得的数据理解相匹配: ![Figure 1.7 – The model building stage ]
图 1.7–模型构建阶段
不幸的是,没有一种适用于所有用例的算法。然而,通过利用我们从业务目标和数据集收集的知识,我们可以定义一个潜在的算法列表来使用。
例如,从我们的业务案例中我们知道,我们希望通过使用年轮的数量来预测鲍鱼的年龄。通过分析和理解数据集,我们还知道在环列中有一个目标或标记变量。这个目标变量是一个介于 1 和 29 之间的离散数值,因此我们可以将我们的可能算法列表细化为一个监督学习算法,该算法在一组离散的可能值中预测一个数值。
以下是可应用于示例业务案例的几种可能的算法:
- 线性回归
- 支持向量机
- 决策树
- 朴素贝叶斯
- 神经网络
同样,这个列表中没有一个算法与用例及数据完全匹配。因此,ML 过程是一个实验,通过多种可能的排列进行工作,从每种排列中获得洞察力,并应用所学到的知识来进一步完善最优模型。
影响从哪种算法开始的一些附加因素基于 ML 从业者的经验,加上所选择的算法如何解决所需的业务目标和成功度量。例如,如果一个必需的成功标准是在两周内完成模型,那么这可能会消除使用更复杂算法的选项。
建立神经网络模型
继续与年龄计算器实验,我们将实现一个神经网络算法,也称为的人工神经网络 ( 安)、深度神经网络 ( DNN ),或者多层感知器 ( MLP )。
在高层次上,神经网络是模仿大脑的人工构造,通过通常被称为神经元或感知器的对数据进行小型非线性计算。通过将这些神经元分组为单独的层,然后将这些层组合在一起,我们可以组装一种机制的构建模块,该机制将数据作为输入,并找到输出(或目标)的依赖关系(或相关性)。通过优化过程,这些依赖关系被进一步细化,以使预测输出尽可能接近实际目标值。
注意
在这个例子中使用神经网络模型的主要原因是为了引入深度学习框架。深度学习框架,比如如 py torch(https://pytorch.org/)、tensor flow(https://www.tensorflow.org/)、MXNet(https://mxnet.apache.org/)可以用来创建更复杂的神经网络。然而,从 ML 流程自动化的角度来看,它们也会引入一些复杂性。因此,通过利用深度学习框架,我们可以在本书的后面部分为解决这些复杂性奠定基础。
下面的是我们将为我们的示例构建的神经网络架构的图示: ![Figure 1.8 – Neural network architecture ]
图 1.8–神经网络架构
组成该架构的各个组件将在以下步骤中解释:
- 为了开始构建模型架构,我们需要从 TensorFlow 深度学习框架中加载必要的库。除了
tensorflow
库,我们还将导入 Keras API。Keras(https://keras.io/)库允许我们创建更高级别的神经网络架构的抽象,更容易理解和使用。例如,在 Keras 中,我们也加载了Sequential
和Dense
类。这些类允许我们定义一个使用顺序神经网络层的模型架构,并定义每一层中神经元的类型和数量:import tensorflow as tf from tensorflow import keras from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense
- 接下来,我们可以使用
Dense
类来定义组成神经网络的层列表:network_layers = [ Dense(256, activation='relu', kernel_initializer="normal", input_dim=10), Dense(128, activation='relu'), Dense(64, activation='relu'), Dense(32, activation='relu'), Dense(1, activation='linear') ]
- 接下来,我们必须将模型定义为一个
Sequential()
模型,或者仅仅是一个层列表:model = Sequential(network_layers)
- 一旦定义了模型结构,我们必须使用
compile()
方法:
编译它以进行训练model.compile(optimizer="adam", loss="mse", metrics=["mae", "accuracy"])
- 一旦模型被编译,就可以调用
summary()
方法来查看它的架构:model.summary()
下面的屏幕截图显示了调用该方法的结果。尽管它显示的是文本输出,但网络架构与图 1.8 中所示的网络架构相匹配: ![Figure 1.9 – Summary of the compiled neural network architecture ]
图 1.9-编译后的神经网络架构总结
正如所见,模型的第一层 matches Dense()
类用于表示这一层有 256 个神经元或单元,连接到下一层的每个神经元。第 1 层还初始化参数(模型权重和偏差),以便每个神经元表现不同,并捕捉我们希望通过训练优化的不同模式。层 1 也被配置为期望具有 10 个维度的输入数据。这些维度对应于鲍鱼数据集的以下特征:
- 长度
- 直径
- 高度
- 整体重量
- 震惊重量
- 内脏重量
- 外壳重量
- 性 _F
- 性 _I
- 性 _M 层 1 也被配置为使用非线性整流线性单元 ( ReLU )激活函数,这允许神经网络从数据集学习复杂的关系。然后我们重复这个过程,添加层 2 到层 4 ,指定这些层中的每一层分别有 128、64、32 和 1 个神经元或单元。最后一层只有一个输出——预测的年轮数。由于模型的目标是确定该输出如何与数据集中的实际环数相关,因此使用了线性激活函数。
一旦我们构建了模型架构,我们就使用下面的重要参数通过compile()
方法来编译模型:
- 损失:该参数指定将使用的目标函数(也称为成本函数)的类型。在高层次上,目标函数计算预测结果与实际值有多远或多近。它根据输入数据计算模型预测的环数与实际环数之间的误差量。在这个例子中,均方误差 ( MSE )被用作目标函数,其中测量所有数据点的平均误差量。
- 优化器:训练期间的目标是最小化预测环数和实际环数之间的误差量。亚当优化器用于迭代更新有助于减少损失(或误差)的神经网络权重。
- Metrics: The evaluation metrics, Mean Absolute Error (MAE), and prediction accuracy are captured during model training and used to provide insight into how effectively the model is learning from the input data.
注意
compile-method](https://keras.io/api/models/model_training_apis/#compile-method))。
现在我们已经为神经网络算法建立了架构,我们需要看看它如何适合预处理数据集的顶部。这个任务就是通常所说的训练模型。
训练模型
如下图所示,ML 过程的下一步是根据预处理的鲍鱼数据训练数据集: ![Figure 1.10 – The model training stage ]
图 1.10–模型训练阶段
训练编译后的模型相对简单。以下步骤概述了如何将从流程的模型训练部分中剔除:
- This first step is not necessary to train the model, but sometimes, the output from the training process can be unwieldy and difficult to interpret. Therefore, a custom class called
cleanPrint()
can be created to ensure that the training output is neat. This class uses the KerasCallback()
method to print a dash ("-"
) as the training output:
注意 在每个时期显示模型的性能是一个很好的实践,因为这提供了对每个时期之后的改进的洞察。然而,因为我们是为class cleanPrint(keras.callbacks.Callback): def on_epoch_end(self, epoch, logs): if epoch+1 % 100 == 0: print("!") else: print("-", end="")
2000
纪元训练的,所以我们使用cleanPrint()
类来使输出更整洁。我们稍后将删除此回调。 - 接下来,我们必须将预处理后的鲍鱼数据分成两个主要组——一组用于来自 sklearn 库的
model_selection()
类的train_test_split()
方法:from sklearn.model_selection import train_test_split training_features, testing_features, training_labels, testing_labels = train_test_split(X, y, test_size=0.2, random_state=42)
- 训练流程的最后一部分是启动模型训练流程。这是通过在编译的模型上调用
fit()
方法并提供training_features
和training_labels
数据集来完成的,如下面的示例代码所示:training_results = model.fit(training_features, training_labels, validation_data=(testing_features, testing_labels), batch_size=32, epochs=2000, shuffle=True, verbose=0, callbacks=[cleanPrint()])
既然模型训练过程已经开始,我们可以回顾一下代码的几个关键方面。首先,将数据分成训练和测试数据集通常是数据预处理步骤的一部分。然而,我们在模型训练步骤中执行此任务,以便为损失和优化函数提供额外的上下文。例如,创建这两个独立的数据集是评估模型训练效果的重要部分。使用训练数据集训练该模型,然后对照测试数据集评估其有效性。该评估程序指导模型(使用损失函数和优化函数)减少预测环数和实际环数之间的误差量。本质上,这使得模型更好或者优化了模型。为了创建训练和测试数据的良好分割,我们必须提供四个附加变量,如下所示:
training_features
:对应于鲍鱼属性的鲍鱼数据集的 10 列,包括这些观察值的 80%。testing_features
:鲍鱼数据集的相同 10 列,包括另外 20%的观察值。training_labels
:在training_features
数据集中每个观察的环数(目标标签)。testing_labels
: The number of rings (target label) for each observation in thetesting_features
dataset. 小费fit-method](https://keras.io/api/models/model_training_apis/#fit-method))中找到。
其次,一旦数据被成功分割,我们可以使用fit()
方法并添加以下参数来进一步管理训练过程:
validation_data
:testing_features
和testing_labels
数据集,模型使用这些数据集来评估经过训练的神经网络权重在多大程度上减少了测试数据中预测的环数和实际环数之间的误差量。batch_size
:该参数定义了通过神经网络传播的训练数据的样本数量。该参数可用于影响训练过程的整体速度。batch_size
越高,从训练数据中使用的样本数量就越多,这意味着在更新神经网络的权重之前,组合起来估计损失的样本数量就越多。epochs
:该参数定义了训练过程将迭代训练数据的次数。epochs
越高,必须通过训练数据进行越多的迭代来优化神经网络的权重。shuffle
:该参数指定在开始训练迭代之前是否混洗数据。每次模型遍历数据时都要对数据进行洗牌,这将迫使模型更好地进行归纳,并防止它学习训练数据中的有序模式。verbose
和callbacks
:这些参数是与显示每个epoch
的训练进度和输出相关的。将输出设置为零并使用cleanPrint()
类将简单地显示一个破折号(epoch
)。
训练过程需要 12 分钟完成,为我们提供一个经过训练的model
对象。在下一节中,我们将使用训练好的模型来评估它对新数据进行预测的效果。
评估训练好的模型
一旦模型被训练,我们就可以进入 ML 过程的下一个阶段:模型评估阶段。在这个阶段,根据业务用例中已经建立的目标和成功标准来评估已训练的模型,目标是确定已训练的模型是否准备好用于生产: ![Figure 1.11 – The model evaluation step ]
图 1.11-模型评估步骤
当评估一个训练好的模型时,大多数 ML 从业者简单地使用一个适合模型类型的评估标准对模型预测的质量进行评分。其他的 ML 实践者更进一步去想象和理解预测。以下步骤将引导您使用这两种方法中的后一种:
- 使用下面的示例代码,我们可以加载必要的 Python 库。第一个库是
matplotlib
。pyplot()
类是不同函数的集合,允许交互式和编程式的绘图生成。第二个库,mean_squarred_error()
,来自sklearn
包,为 ML 从业者提供了一个简单的方法,使用均方根误差 ( RMSE )度量来评估模型的质量。由于神经网络模型是一个基于监督学习的回归模型,RMSE 是一种常用的方法,用于测量模型预测的错误率:import matplotlib.pyplot as plt from sklearn.metrics import mean_squared_error
- 然后,导入的库用于可视化预测,以便更好地了解模型的质量。下面的代码生成一个图,其中包含了量化预测质量所需的信息:
fig, ax = plt.subplots(figsize=(15, 10)) ax.plot(testing_labels, model.predict(testing_features), "ob") ax.plot([0, 25], [0, 25], "-r") ax.text(8, 1, f"RMSE = {mean_squared_error(testing_labels, model.predict(testing_features), squared=False)}", color="r", fontweight=1000) plt.grid() plt.title("Abalone Model Evaluation", fontweight="bold", fontsize=12) plt.xlabel("Actual 'Rings'", fontweight="bold", fontsize=12) plt.ylabel("Predicted 'Rings'", fontweight="bold", fontsize=12) plt.legend(["Predictions", "Regression Line"], loc="upper left", prop={"weight": "bold"}) plt.show()
执行这段代码将创建两个子图。第一个子图是散点图,显示来自测试数据集的模型预测,以及地面实况标签。第二子图在这些预测上叠加一条回归线,以突出预测的环数与实际环数之间的线性关系。代码的其余部分标记了绘图的各种属性,并显示了预测的 RMSE 分数。以下是该图的一个示例: ![Figure 1.12 – An example Abalone Model Evaluation scatterplot ]
图 1.12–鲍鱼模型评估散点图示例
这里有三件事应该立即引起注意:
- RMSE 评估度量给训练模型的分数是 2.54。
- 描述实际环数和预测环数之间相关性的回归线没有通过大多数预测。
- 有相当多的预测在正负尺度上都远离回归线。这显示了数据点的预测环数与实际环数之间的高误差率。
这些观察结果和其他观察结果应该与业务用例中概述的目标和成功标准进行比较。然后,ML 从业者和企业所有者都可以判断训练好的模型是否准备好用于生产。
例如,如果年龄计算器应用的主要目标是使用模型预测作为渔民获得鲍鱼年龄的简单概念的粗略指南,那么模型可以做到这一点,因此可以认为已经准备好生产。另一方面,如果年龄计算器应用的主要目标是提供准确的年龄预测,那么示例模型可能不能被认为是生产就绪的。
因此,如果我们确定模型还没有准备好投入生产,ML 流程的后续步骤是什么?下一节将回顾一些选项。
探索可能的后续步骤
由于模型已经被认为不适合生产,在模型评估阶段之后可以采取几种方法。下图突出显示了三个可能的选项,可以考虑作为可能的后续步骤: ![Figure 1.13 – Next step options ]
图 1.13–下一步选项
让我们更深入地探索这三个可能的后续步骤,以确定哪个选项最适合年龄计算器用例的目标。
选项 1–获取模式数据
第一种选择要求 ML 从业者回到流程的开始并获取更多数据。由于 UCI 鲍鱼库是唯一公开可用的数据集,该任务可能涉及通过人工捕捞鲍鱼或对渔民进行捕捞调查来实际收集更多观察数据。不管怎样,这都需要时间!
然而,简单地向数据集添加更多的观测值并不一定会转化为更高质量的模型。因此,获得更多数据也意味着获得更高质量的特征。这意味着 ML 从业者将需要重新评估现有的数据,进一步深入分析以更好地理解哪些特性是最重要的,然后重新设计这些特性或从中创建新的特性。这也很费时间!
选项 2–选择另一个模型
要考虑的第二个选项包括使用完全不同的算法构建一个全新的模型,该模型仍然与用例匹配。例如,ML 从业者可能使用另一种监督学习、基于回归的算法进行研究。
不同的算法可能还需要重新构造数据,以便更适合算法所需的输入类型。例如,选择一个梯度推进回归算法,比如 XGBoost ,要求目标标签是数据集中的第一列。选择另一种算法并重新设计数据需要额外的时间!
选项 3–调整现有模型
回想一下在构建现有神经网络模型时,有一些可调参数是在编译过程中配置的。例如,使用特定的优化器和损失函数来编译模型。
此外,当训练现有的神经网络模型时,提供其他可调参数,例如个时期的数量和个批量。
注意
选择正确的选项没有最佳实践。请记住,流程中的每一次迭代都是一次实验,目的是从实验中收集更多信息,以确定下一步行动或下一个选项。
虽然选项 3 可能看起来很简单,但是在下一节中,您将会看到这个选项也涉及到多个潜在的迭代,因此也是耗时的。
调整我们的模型
正如我们已经强调的,可以调整多个参数或超参数来更好地调整或优化现有模型。因此,该过程的这一阶段也被称为超参数优化。下图显示了超参数优化过程需要什么: ![Figure 1.14 – The hyperparameter optimization process ]
图 1.14–超参数优化过程
在评估模型以确定哪些超参数可以调整后,使用这些参数训练模型。经过训练的模型再次与业务目标和成功标准进行比较,以确定它是否准备好投入生产。然后重复这个过程,不断地调整、训练和评估,直到生产就绪模型被生产出来。
确定要优化的最佳超参数
同样,没有精确的方法来获得最佳超参数。整个过程中的每一次迭代都有助于缩小超参数组合对更优化模型的贡献。
然而,开始这个过程的一个好地方是更深入地研究模型训练期间发生的事情,并进一步了解模型如何从数据中学习。
您还记得,当执行fit()
方法来训练模型并将结果绑定到training_results
参数时,我们能够获得模型调整所需的额外指标。以下步骤将向您展示如何提取和可视化这些指标的示例:
- 通过对
training_results
参数使用history()
方法,我们可以使用下面的示例代码来绘制训练和测试过程的预测误差。plt.rcParams["figure.figsize"] = (15, 10) plt.plot(training_results.history["loss"]) plt.plot(training_results.history["val_loss"]) plt.title("Training vs. Testing Loss", fontweight="bold", fontsize=14) plt.ylabel("Loss", fontweight="bold", fontsize=14) plt.xlabel("Epochs", fontweight="bold", fontsize=14) plt.legend(["Training Loss", "Testing Loss"], loc="upper right", prop={"weight": "bold"}) plt.grid() plt.show()
以下是执行上述代码后的绘图示例: ![Figure 1.15 – Training vs. Testing Loss ]
图 1.15–训练与测试损失
- 同样,通过将样本代码中的
loss
和val_loss
参数分别替换为mae
和val_mae
,我们可以看到一个一致的趋势:plt.rcParams["figure.figsize"] = (15, 10) plt.plot(training_results.history["mae"]) plt.plot(training_results.history["val_mae"]) plt.title("Training vs. Testing Mean Absolute Error", fontweight="bold", fontsize=14) plt.ylabel("mae", fontweight="bold", fontsize=14) plt.xlabel("Epochs", fontweight="bold", fontsize=14) plt.legend(["Training MAE", "Testing MAE"], loc="upper right", prop={"weight": "bold"}) plt.grid() plt.show()
在执行了前面的代码之后,我们将得到以下输出: ![Figure 1.16 – Training vs. Testing Mean Absolute Error ]
图 1.16–训练与测试平均绝对误差 图 1.16 和图 1.15 清楚地显示了几个特别重要的趋势:
- 模型从训练数据中学习的内容与其对测试数据的预测之间存在明显的差异。这表明模型在训练时没有学到任何新东西,本质上是过度拟合数据。该模型与训练数据相关联,而不能与测试数据集中新的、看不见的数据相关联。
- 这种差异似乎发生在大约 250 个时期/训练迭代。由于训练过程被设置为 2,000 个时期,这表明模型被过度训练,这可能是它过度拟合训练数据的原因。
- 测试 MAE 和测试损耗都具有不稳定的梯度。这意味着当模型参数通过训练过程被更新时,更新的幅度太大,导致不稳定的神经网络,并因此导致对测试数据的不稳定预测。因此,该图描绘的波动本质上突出了一个爆炸梯度问题,表明该模型过度拟合了数据。
基于这些观察,可以调整几个超参数。例如,要改变的一个明显的参数是历元或训练迭代的数量,以防止过度拟合。类似地,我们可以将优化函数从亚当改为随机梯度下降 ( SGD )。SGD 允许将特定的学习率设置为其参数之一,这与 Adam 优化器使用的自适应学习率相反。通过指定一个小的学习率参数,我们实质上是重新调整模型更新,以确保它们是小的和可控的。
另一种解决方案可能是使用正则化技术,如 L1 或 L2 正则化,来惩罚模型上的一些神经元,从而创建一个更简单的神经网络。同样,通过减少层的数量和每层中的神经元来简化神经网络架构将具有与正则化相同的效果。
最后,减少样本数量或批量大小可以控制训练过程中梯度的稳定性。
既然我们已经对需要调整的超参数有了一个清晰的概念,下一节将向您展示如何进一步优化模型。
调整、训练和重新评估现有模型
我们可以通过以下步骤开始模型调整:
- 我们必须做出的第一个改变是神经网络架构本身。下面的示例代码描述了新的结构,其中只使用了两个网络层,而不是四个。每层只有 64 个神经元:
network_layers = [ Dense(64, activation='relu', kernel_initializer="normal", input_dim=10), Dense(64, activation='relu'), Dense(1, activation='linear') ]
- 再次使用与上一个例子相同的参数重新编译模型:
model = Sequential(network_layers) model.compile(optimizer="adam", loss="mse", metrics=["mae", "accuracy"]) model.summary()
下面的屏幕截图显示了调整后的神经网络体系结构的文本摘要: ![Figure 1.17 – Summary of the tuned neural network architecture ]
图 1.17–调整后的神经网络架构总结
下图显示了转向神经网络体系结构的可视化表示: ![Figure 1.18 – Tuned neural network architecture ]
图 1.18–调整后的神经网络架构
- Lastly, the
fit()
method is called on the new model. However, this time, the number ofepochs
has been reduced to200
andbatch_size
has also been reduced to8
:
注意 在前面的代码示例中,training_results = model.fit(training_features, training_labels, validation_data=(testing_features, testing_labels), batch_size=8, epochs=200, shuffle=True, verbose=1)
cleanPrint()
回调已被移除,以显示 200 个时期的训练和验证数据的评估指标。 - 一旦新模型训练完成,先前使用的评估代码可以重新执行,以显示评估散点图。以下是该散点图的一个示例: ![Figure 1.19 –Abalone Evaluation scatterplot ]
图 1.19–鲍鱼评估散点图
新模型没有捕捉到所有的预测,因为在正负尺度上仍然有几个异常值。然而,大多数数据点的整体拟合度有了显著的提高。RMSE 得分从 2.54 下降到 2.08 进一步量化了这一点。
同样,这些观察结果应该与业务用例中概述的目标和成功标准进行比较,以衡量模型是否准备好投入生产。
如下面的图所示,如果找不到生产就绪模型,那么进一步调整模型、获取和设计更多数据或构建完全不同的模型的选项仍然可用: ![Figure 1.20 – Additional process options ]
图 1.20–附加流程选项
如果模型被视为生产就绪,ML 从业者可以进入 ML 过程的最后阶段,如下图所示。这是模型部署阶段: ![Figure 1.21 – The model deployment stage ]
图 1.21–模型部署阶段
在下一节中,我们将回顾将模型部署到生产中所涉及的过程。
将优化的模型部署到生产中
模型部署在某种程度上是一个灰色区域,因为一些 ML 从业者没有将这个阶段应用到他们的 ML 过程中。例如,一些 ML 实践者可能认为他们的任务范围仅仅是提供一个解决业务用例的生产就绪 ML 模型。一旦这个模型被训练,他们简单地把它交给应用开发团队或应用所有者,让他们测试并把模型集成到应用中。
或者,一些 ML 从业者将与应用团队合作,将模型部署到测试或质量保证 ( QA )环境中,以确保经过训练的模型成功地与应用集成。
无论 ML 从业者角色的范围是什么,模型部署都是 CRISP-DM 方法的一部分,并且应该总是被考虑到整个 ML 过程中,特别是如果 ML 过程是自动化的。
虽然 CRISP-DM 方法以模型部署阶段结束,如前图所示,但该过程实际上是一个连续的过程。一旦将模型部署到生产应用中,就需要不断地对其进行监控,以确保它不会偏离其预期目的,从而始终如一地提供对未知数据或新数据的准确预测。如果出现这种情况,ML 从业者将被要求再次启动 ML 过程,以重新优化模型,并使其推广到这一新数据。下图显示了 ML 流程的实际情况: ![Figure 1.22 – Closing the loop ]
图 1.22–结束循环
那么,再一次,为什么 ML 过程很难?
使用这个简单的用例示例,您有望看到,不仅探索数据以及构建、训练、评估、调优、部署和监控模型的过程存在固有的复杂性,整个过程也是复杂的、手动的、迭代的和连续的。 我们如何简化流程,以确保结果始终是与业务用例相匹配的优化模型?这就是汽车发挥作用的地方。
利用 AutoML 简化 ML 流程
AutoML 是一个宽泛的术语,根据你问谁,它有不同的意思。当提到 AutoML 时,一些 ML 实践者可能会指向一个专用的软件应用、一组工具/库,甚至是一个专用的云服务。简而言之,AutoML 是一种方法论,它允许你创建一个可重复的、可靠的、简化的,当然还有自动化的 ML 过程。
过程是可重复的,因为它每次执行时都遵循相同的模式。这个过程是可靠的,因为它保证总是产生一个匹配用例的优化模型。这个过程被简化了,任何不必要的步骤都被删除了,尽可能地提高了效率。最后,也是最重要的,该过程可以自动启动和执行,并由一个事件触发,例如在检测到模型概念漂移后重新训练模型。 AWS 提供了多种功能,可用于构建简化的 AutoML 流程。在下一节中,我将重点介绍一些专用的云服务以及其他服务,它们可以用来使 ML 过程更加简单和自动化。
AWS 如何使 ML 开发和部署过程更容易自动化
本书剩余章节的重点将是通过实际操作的例子来展示如何在 AWS 上实现 ML 过程的自动化。通过扩展年龄计算器的例子,您将看到各种 AWS 功能和服务如何被用来做这件事。例如,本书的下两章将重点介绍如何使用 AWS AI/ML 堆栈的一些本机功能,如下所示:
- 使用 SageMaker Autopilot 自动创建、管理和部署优化的鲍鱼预测模型,同时使用无代码和有代码的方法。
- 使用自动引导库来确定用于鲍鱼模型的最佳深度学习算法,以及用于更复杂的 ML 用例的示例,如计算机视觉。
本书的第二、第三和第四部分将重点关注利用不一定是 AI/ML 堆栈一部分的其他 AWS 服务,例如:
- AWS CodeCommit 和 CodePipeline,它们将使用一个持续集成和持续交付 ( CI/CD )管道交付鲍鱼用例。
- AWS Step 函数和数据科学 Python SDK,创建一个编码管道来生成鲍鱼模型。
- 亚马逊管理阿帕奇气流 ( MWAA )的工作流程,自动化和管理 ML 流程。
最后,本书的第五部分将扩展第二部分和第三部分中涵盖的一些中心主题,为您提供一个跨职能、敏捷团队如何实现端到端鲍鱼计算器示例的实践示例,作为机器学习软件开发生命周期 ( MLSDLC )的一部分。
总结
正如我在开始时所说的,本章的主要目标是强调当构建一个商业用例的 ML 解决方案时,ML 从业者可能面临的许多挑战。在这一章中,我向你介绍了一个 ML 用例的例子——鲍鱼计算器——我用它向你展示 ML 过程在现实中有多困难。
通过遍历该过程的每一步,我解释了其中涉及的复杂性,以及您可能会遇到的挑战。我还强调了为什么 ML 过程是复杂的、手动的、迭代的和连续的,这为使用 AutoML 的可重复的、简化的和可靠的自动化过程奠定了基础。
在下一章中,我们将通过向您介绍一个名为 SageMaker Autopilot 的本地 AWS 服务来探索如何开始实现一个 AutoML 方法。
文章列表
- AWS自动化机器学习-一、AWS 上的自动化机器学习入门
- AWS自动化机器学习-七、使用 AWS 步骤函数构建 ML 工作流
- AWS自动化机器学习-三、使用 AutoGluon 技术自动化复杂的模型开发
- AWS自动化机器学习-九、使用 Amazon Managed Workflows 为 Apache AirFlow 构建 ML 工作流
- AWS自动化机器学习-二、使用 SageMaker 自动驾驶器自动化机器学习模型开发
- AWS自动化机器学习-五、自动化 ML 模型的持续部署
- AWS自动化机器学习-八、使用 Apache Airflow 实现机器学习过程的自动化
- AWS自动化机器学习-六、使用 AWS 步骤函数自动化机器学习过程
- AWS自动化机器学习-十、机器学习软件开发生命周期(MLSDLC)简介
- AWS自动化机器学习-十一、MLSDLC 的持续集成、部署和训练
- AWS自动化机器学习-四、机器学习的持续集成和持续交(CI/CD)
- AWS自动化机器学习-第一部分:自动机器学习过程和 AWS 上的 AutoML 的基础知识
- AWS自动化机器学习-第三部分:优化以源代码为中心的自动化机器学习方法
- AWS自动化机器学习-第二部分:通过持续集成和持续交付(CI/CD)实现机器学习过程的自动化
- AWS自动化机器学习-第五部分:自动化 AWS 上的端到端生产应用
- AWS自动化机器学习-第四部分:优化以数据为中心的自动化机器学习方法
- AWS自动化机器学习-零、前言