机器学习实战:模型构建与应用
上QQ阅读APP看书,第一时间看更新

1.6 初学机器学习

如前所述,机器学习的范例是你有数据,这些数据是被标注的,并且你想搞清楚匹配数据与标签的规则。用代码来展示一个最简单的情况,如下所示,考虑这两组数字:

X和Y的值之间存在一种关系(例如,当X是-1时Y是-3;当X是3时Y是5,等等)。你能看出来吗?

几秒种之后,你可能就会发现它们的关系是Y=2X-1。你是如何计算出来的呢?不同的人通过不同的方法解决它,但是我经常听到的观察是X在序列中每次增加1,然后Y每次增加2。因此,Y=2X+/-某个值。他们看到当X=0时,Y=-1,于是发现答案可能是Y=2X-1。接下来他们验证其他的值,然后发现这个假设成立,于是答案就是Y=2X-1。

这和机器学习的过程非常相似。让我们来看一下你可以编写TensorFlow代码用一个神经网络来替你解决这个问题。

下面是完整的代码,采用TensorFlow Keras API。如果你还看不懂,不用担心,我们会逐行解读:

让我们从第一行开始。你也许已经听说过神经网络,并且已经看过使用多层互联神经元来解释它们的示意图,有点像图1-18。

图1-18:一个典型的神经网络

当你看到这样的神经网络时,可以把每个“圆圈”想象成一个神经元,每一列圆圈是一层。于是,在图1-18中有三层;第一层有5个神经元,第二层有4个,第三层有2个。

如果回看第一行代码,我们会发现正在定义一个最简单的神经网络。它只有一层,并且只包含一个神经元:

当使用TensorFlow时,你使用Sequential来定义你的层。在Sequential中,你可以指定每一层看起来是什么样的。在我们的Sequential中只有一行,因此只有一层。

接下来你可以使用keras.layers API来定义每一层看起来是什么样的。这里有许多不同类型的层,但是这里我们使用的是Dense层。“Dense”指的是完全(密集地)连接的神经元组合,就像你在图1-18中看到的那样,每个神经元都与下一层的所有神经元连接。这是最常用的层类型。我们的Dense层指定了units=1,于是整个神经网络中只有一个密集层(包含一个神经元)。最终,当你声明神经网络的第一层时(这种情况下,它是唯一的层),必须告诉它输入数据的类型。在这种情况下,输入数据是X,它只是一个值,于是我们指定那是它的类型。

下一行开始变得真正有趣起来:

如果你曾经做过任何与机器学习相关的事情,也许就能看出它涉及许多数学运算。如果你好多年没有做过演算,它也许看起来是一个门槛。这一部分涉及数学—这是机器学习的核心。

在这样的情况下,计算机不知道X和Y之间的关系是什么。于是它会猜测。例如,它猜Y=10X+10。接下来它就要评估这个猜测有多好或有多坏。这就是损失函数的作用。

它已经知道了当X是-1,0,1,2,3和4时的答案,于是损失函数可以比较这些和猜测关系的结果。如果它猜Y=10X+10,那么当X=1时,Y就是0。正确的答案是-3,所以猜的不太对。但是当X是4时,猜测的结果是50,然而正确答案是7。这就真的离谱了。

有了这些知识,计算机可以再猜一次。这就是优化器的工作。这里涉及许多复杂的数值计算,但是有了TensorFlow,这些计算都被隐藏了。你只需要为不同的场景选择适当的优化器即可。在这个例子中,我们选择了一个叫作sgd的优化器,sgd代表随机梯度下降(stochastic gradient descent),这是一个复杂的数学方程,当给定数值、前一次的猜测以及那次猜测计算到的误差(或损失)时,可以生成下一个猜测。随着时间的推移,它的工作是最小化损失,并且通过这样做来让猜测方程不断接近正确答案。

接下来,我们简单地将数据定义为每个层期待的数据格式。在Python中,TensorFlow可以使用一个叫作Numpy的库,这里我们将数据放入一个Numpy数组来让它能更好地处理它们:

模型学习的过程会通过model.fit命令来启动,如下所示:

你可以把这个读为“将这些Xs拟合到这些Ys,并尝试500次”。因此,在第一次尝试中,计算机会猜测这个关系(例如,类似Y=10X+10的函数),然后衡量这个猜测有多好或有多坏。然后它会把这些结果传送给优化器,优化器会生成下一次的猜测。这个过程会重复,其中的逻辑是随着时间的推移误差(或损失)会逐渐降低,于是“猜测”就会越来越好。

图1-19展示了这在Colab notebook中运行的截图。看一看这些随时间变化的损失值。

图1-19:训练神经网络

我们可以看到经过前10个回合,损失从3.2868变为0.9682。这就是说,仅仅10次尝试之后,神经网络就比它第一次猜测的表现好多了。接下来看一看到了第500次尝试时发生了什么(见图1-20)。

图1-20:训练神经网络—最后5个回合

我们现在可以看到损失是2.61×10-5。这个损失已经变得如此之小,模型已经大概搞清楚这些数字之间的关系是Y=2X-1。机器已经学习到了它们之间的关系。

最后一行代码使用训练后的模型来得到一个预测,就像这样:

equa当处理ML模型时,预测这个词经常被用到。但是,不要把它当作看到了未来!这个词被使用的原因是我们需要处理一定程度的不确定性。回想一下我们之前提到的运动检测场景。当一个人以一定速度移动时,她大概率是在走路。类似地,当一个模型学习两个事物之间的关系时,它会告诉我们大概的结果是什么。换句话说,它是在预测结果(之后你同样会学到推理,即模型从众多结果中挑选一个,并推测它选中的是正确的结果。)

当X是10时,如果我们让模型去预测Y是多少,你觉得答案会是什么呢?你可能立刻想到19,但是那是不正确的。它会选择一个非常接近19的值。这有几个原因。首先,我们的损失不是0。它仍是一个非常小的值,于是我们应该期待任何预测的值都会有一个很小的偏差。第二,神经网络只使用了很少量的数据进行训练—在这个情况下是6组(X,Y)的值。

模型中只有一个神经元,并且那个神经元学习了一个权重和一个偏差,所以Y=WX+B。这看起来和我们想要的关系Y=2X-1一模一样,我们希望它能学到X=2和B=-1。鉴于模型只在6组数据上训练,答案可能永远不会是准确的值,只会是很接近它们的数字。

你可以自己运行一下代码来看看结果。当我运行时得到的是18.977888,但是你的结果也许会不太一样,因为当神经网络初始化时,有一个随机元素:你的初始猜测可能和我的有一点不同,也和第三个人的不同。

查看网络所学内容

当我们用线性关系将Xs匹配到Ys时,这显然是一个很简单的问题。在本章之前提到过,神经元包含它们可以学习的权重和偏差参数,这使得一个单一的神经元可以学习像这样的关系:当Y=2X-1时,权重是2,偏差是-1。通过TensorFlow,我们可以看到学习到的权重和偏差,只需简单地更改我们的代码,如下所示:

不同之处在于我创建了一个叫作l0的变量来保存Dense层。因此,当神经网络结束学习之后,我可以打印出这一层学习到的值(或者权重)。

在我的例子中,输出如下所示:

因此,学习到的X和Y之间的关系是Y=1.9967953X-0.9900647。

这和我们期待的(Y=2X-1)非常接近,而且我们可以说这和现实更接近,因为我们假设这个关系对其他数值依然成立。