1.2.2 机器如何学习
鉴于前面的场景,你作为程序员找出构成一条线的规则,然后用计算机实现它们,现在让我们看看机器学习方法会有什么不同。
让我们从了解机器学习代码的结构开始。虽然这在很大程度上是一个“Hello World”问题,但代码的整体结构与你在更复杂的代码中所看到的非常相似。
我喜欢绘制一个顶层架构,概述使用机器学习来解决这样的问题。请记住,在这种情况下,我们有x和y值,想算出W和B以便拥有一个直线方程。一旦有了这个方程,我们就可以得到给定x的新y值。
第1步:猜答案
是的,你没看错。首先,我们不知道答案可能是什么,因此猜测与任何其他答案一样。实际上,这意味着我们为W和B选择随机值。稍后我们会更智能地循环回这一步,所以后续的值不会是随机的,但我们纯粹是随机开始的。因此,让我们假设第一个“猜测”是W=10和B=5。
第2步:衡量猜测的准确性
现在有了W和B的值,我们可以把这些值用于已知的数据,看看这些猜测的好坏。因此,我们可以使用y=10x+5来计算每个x值的y,将y与“正确”值进行比较,并使用它来推导出我们的猜测有多好或多坏。显然,对于这种情况,我们的猜测非常糟糕,因为我们的数字会相差很远。我们很快就会详细讨论这个问题,但现在,我们意识到我们的猜测真的很糟糕,并且关于具体有多糟糕我们有一个衡量标准——这通常称为损失。
第3步:优化我们的猜测
现在我们有了一个猜测,并且对猜测的结果(或损失)有了了解,我们便有了可以帮助创建新的、更好的猜测的信息。这个过程称为优化。如果你过去查看过任何AI编码或训练,并且其中涉及大量数学,那么你很可能正在考虑优化。在这里,花哨的微积分在称为梯度下降的过程中可用于帮助做出更好的猜测。像这样的优化技术可以找出对参数进行小幅调整的方法,从而使误差最小。我不打算在这里详细介绍,虽然了解优化的工作原理是一项有用的技能,但事实是,像TensorFlow这样的框架会为你实现它们,因此你可以继续使用它们。随着时间的推移,值得深入研究它们以获得更复杂的模型,让你可以调整它们的学习行为。但就目前而言,你只需使用内置优化器。完成此操作后,你只需转到第1步。根据定义,重复此过程有助于我们随着时间的推移和多次循环,找出参数W和B。
这就是此过程被称为机器学习的原因。随着时间的推移,通过进行猜测,弄清楚猜测的好坏,根据该信息优化下一个猜测,然后重复它,计算机将“学习”W和B的参数(或者实际上其他任何东西),然后从那里找出构成我们一条线的规则。从视觉上看,这可能如图1-4所示。
图1-4:机器学习算法
在代码中实现机器学习
这里有很多描述,还有很多理论。现在让我们看看这在代码中会是什么样子,如此你就可以看到它自己运行了。很多代码一开始对你来说可能看起来很陌生,但随着时间的推移,你会掌握它的窍门。我喜欢称其为机器学习的“Hello World”,因为你要使用一个非常基础的神经网络(稍后我会解释)来“学习”一条线的参数W和B,仅给定几个点就行了。
这是代码(可以在本书的GitHub中找到代码示例):
这是使用TensorFlow Keras API编写的。Keras是一个开源框架,旨在通过高级API简化模型的定义和训练。随着TensorFlow 2.0的发布,它于2019年紧密集成到TensorFlow中。
让我们一行一行地探索。
首先是模型的概念。在创建学习数据细节的代码时,我们经常使用术语“模型”来定义结果对象。在这种情况下,模型大致类似于前面代码示例中的get_y()函数。这里的不同之处在于,模型不需要给定W和B。它会根据给定的数据自己计算出来,所以你可以直接问它y并给它一个x,它会给你答案。
所以我们的第一行代码看起来像这样——它定义了模型:
但剩下的代码是什么?好吧,让我们从单词Dense开始,你可以在第一组括号中看到它。你可能已经看过如图1-5所示的神经网络图片。
图1-5:基础神经网络
你可能会注意到,在图1-5中,左侧的每个圆圈(或神经元)都连接到右侧的每个神经元。每个神经元都以密集的方式连接到每个其他神经元。因此得名密集(Dense)。此外,左侧有三个堆叠的神经元,右侧有两个堆叠的神经元,它们依次形成非常明显的神经元“层”,其中第一个“层”有三个神经元,第二个有两个神经元。
让我们回到代码:
这段代码是说我们想要一个层序列(Sequential),在括号内,我们定义这些层序列。序列中的第一个将是Dense,表示如图1-5所示的神经网络。没有定义其他层,所以我们的Sequential只有一层。这一层只有一个单元,由units=1参数表示,该单元的输入形状只是一个值。
所以我们的神经网络将如图1-6所示。
图1-6:最简单的神经网络
这就是为什么我喜欢称其为神经网络的“Hello World”。它有一层,而该层有一个神经元。通过这行代码,我们现在已经定义了自己的模型架构。让我们继续下一行:
这里我们指定了内置函数来计算损失(记住第2步,我们想看看自己的猜测有多好或多坏)和优化器(第3步,我们生成一个新的猜测),这样就可以改进神经元内W和B的参数。
在这种情况下,'sgd'代表“随机梯度下降”,这超出了本书的范围。总之,它使用微积分和均方误差损失来计算如何最小化损失,一旦损失最小化,我们应该就能得到准确的参数。
接下来,让我们定义数据。两点可能不够,所以我在这个例子中将其扩展为6点:
np代表“NumPy”,这是一个常用于数据科学和机器学习的Python库,它使数据处理变得非常简单。你可以在https://numpy.org上了解有关NumPy的更多信息。
我们将创建一个由x值及其对应的y值组成的数组,给定x=-1,y为-3,当x为0时,y为-1,以此类推。快速查看可以表明,y=2x-1的关系对于这些值成立。
接下来让我们执行之前说过的循环——进行猜测,衡量损失的好坏,优化新的猜测,然后重复。在TensorFlow的说法中,这通常称为拟合,即我们有x's和y's,想要将x's拟合到y's,或者换句话说,找出规则使用我们拥有的示例为给定的x提供正确的y。epochs=500参数只是表明我们将循环500次:
当你运行这样的代码时(如果对此不熟悉,你将在本章后面看到如何执行此操作),你将看到如下输出:
注意损失(loss)值。单位并不重要,重要的是它变得越来越小。请记住,损失越低,你的模型就会表现得越好,其答案也就越接近你的预期。因此,第一次猜测的损失为32.4543,但到第五次猜测时,损失减少到13.3362。
如果后续查看500的最后5个epoch,并查看损失,我们会得到如下输出:
它小得多,大约为5.3×10-5。
这表明神经元计算出的W和B的值相差很小。它不是零,所以我们不应该期待精确的正确答案。例如,假设我们给定x=10,像这样:
答案不会是19,而是非常接近19的值,通常约为18.98。为什么?这里有两个主要原因。第一个是像这样的神经网络处理的是概率,而不是确定性,因此它计算出的W和B很可能是正确的,但可能不是100%准确。第二个原因是我们只给了神经网络6个点。虽然这6个点是线性的,但这并不能证明我们可能预测的所有其他点都必然在这条线上。数据可能偏离那条线……出现这种情况的可能性非常低,但它不为零。我们没有告诉计算机这是一条线,只是要求它找出x与y匹配的规则,它得出的结果看起来像一条线,但不能保证是一条线。
这是在处理神经网络和机器学习时需要注意的事情——你将处理像这样的概率!
我们模型的方法名称中也有提示——注意,我们没有要求它计算x=10.0的y,而是预测它。在这种情况下,预测(通常称为推理)反映了这样一个事实:模型将尝试根据它所知道的事实来确定“值”是什么,但可能并不总是正确的。