1.4 机器学习的基本概念

1.4.1 书中用到的术语介绍

现在每过一段时间都会出现几个新的网络用语,懂的人见了会心一笑,不懂的只能摸摸脑袋,不知道“梗”在哪里,少了许多乐趣。机器学习也有许多独特的用语,在本节,我们先把机器学习的“行话”都介绍清楚。

1.常用术语

模型:模型(Model)是机器学习的核心概念。如果认为编程有两大组成部分,即算法和数据结构,那么机器学习的两大组成部分就是模型和数据集。如果之前没有接触过相关概念,想必你现在很希望直观地理解什么是模型,但对模型给出一个简洁又严谨的定义并不容易,你可以认为它是某种机器学习算法在设定参数后的产物,它的作用和编程时用到的函数一样,可以根据某些输入得到某些输出。既然叫机器学习算法,不妨将它想象成一台机器,其上有很多旋钮,这些旋钮就是参数。机器本身是有输入和输出功能的,根据不同的旋钮组合,同一种输入可以产生不同的输出,而机器学习的过程就是找到合适的那组旋钮组合,通过输入得到你所希望的输出。

数据集:如果说机器学习的“机器”指的是模型,那么数据集就可以说是驱动着这台机器去“学习”的“燃料”。有些文献将数据集又分为训练集和测试集,其实它们的内容和形式并无差异,只是用在不同的地方:在训练模型阶段使用,就叫作训练集;在测试模型阶段使用,就叫作测试集。

数据:我们刚才提到了数据集,数据集就是数据的集合。在机器学习中,我们称一条数据为一个样本(Sample),形式类似一维数组。样本通常包含多个特征(Feature),如果是用于分类问题的数据集,还会包含类别(Class Label)信息,如果是回归问题的数据集,则会包含一个连续型的数值。

特征:这个术语又容易让你产生误解了。我们一般把可以作为人或事物特点的征象、标志等称作特征,譬如这个人鼻子很大,这就是特征,但在机器学习中,特征是某个对象的几个记录维度。我们都填写过个人信息表,特征就是这张表里的空格,如名字、性别、出生日期、籍贯等,一份个人信息表格可以看成一个样本,名字、籍贯这些信息就称作特征。前面说数据形式类似一维数组,那么特征就是数组的值。

向量:向量为线性代数术语,机器学习模型算法的运算均基于线性代数法则,不妨认为向量就是该类算法所对应的“数据结构”。一条样本数据就是以一个向量的形式输入模型的。一条监督学习数据的向量形式如下:

[特征X1值,特征X2值,…, Y1值]

矩阵:矩阵为线性代数术语,可以将矩阵看成由向量组成的数组,形式上也非常接近二维数组。前面所说的数据集,通常就是以矩阵的形式输入模型的,常见的矩阵形式如下:

[[特征X1值,特征X2值,…, Y1值],

[特征X1值,特征X2值,…, Y2值],

[特征X1值,特征X2值,…, Yn值]]

其实这个组织形式非常类似电子表格,不妨就以电子表格来对照理解。每一行就是一个样本,每一列就是一个特征维度,譬如某个数据集一共包括了7个样本,那就是有7行数据,每个样本又都有4个维度的特征,那就是每行数据有4列,用电子表格表示如图1-2所示,其中,A~D列为特征,E列为结果。

图1-2 用电子表格来表示机器学习的数据集矩阵

对比一下应该就能马上理解数据集是怎样用矩阵保存数据的。有一个需要注意的细节——我们在制作电子表格时习惯添加列头或行头,譬如第一行往往是时间、名字、班级这样的信息标签,但机器学习使用的数据集一般并不包含这些列头和行头,第一格的内容就是第一个样本的第一个特征的值,与我们日常习惯稍有区别。

2.常用函数

作为一门与数学关联很密切的科目,机器学习自然会用到大量函数,但不用恐慌,机器学习中函数所起的实际作用更接近于编程概念上的函数。在编程中,我们会给函数“喂”点什么,名曰“传参”,然后函数会反过来给我们“吐”点什么,名曰“返回值”。在机器学习中,函数扮演了类似的角色。下面我们先介绍两个机器学习中的“网红”函数:假设函数和损失函数。

假设函数(Hypothesis Function)将是我们机器学习大冒险中的主角。机器学习的模型训练依靠数据,但数据本身无法直接作为驱动模型训练的动力,而假设函数正好发挥了驱动引擎的作用,我们给假设函数灌入数据作为“燃料”,它就能产生动力输出并让学习过程运转起来。这个过程将在后面详述。

假设函数在本书中的写法是H(x),这里的x可以简单理解成矩阵形式的数据,我们把数据“喂”给假设函数,假设函数就会返回一个结果,而这个结果正是机器学习所得到的预测结果。为什么假设函数具有如此神奇的能力?很复杂,不过不要紧,后面我们将用整本书来解释它。

我们的机器学习之旅将是一部双主角的冒险历程,而另一大主角毫无疑问就是损失函数(Loss Function)。损失函数很重要,它为机器学习提供了学习动力。与其他算法不一样,机器学习不是一个一次就得到最终结果的计算过程,而是不断逼近学习目标的迭代过程。既然是要“逼近”,首先肯定需要通过衡量工具来度量当前距离目标是逼近了还是远离了,所以损失函数又叫目标函数。

损失函数用L(x)表示,L代表Loss,这里的x是假设函数的预测结果。函数返回值越大,表示结果偏差越大。机器学习中有一个与损失函数含义非常相近的术语,叫作成本函数(Cost Function),通常用J(x)表示,同样,这里的x也是假设函数的预测结果。成本函数与损失函数意义非常相近,同样是函数返回值越大,表示偏差越大。

损失函数和成本函数都表示预测结果与真实情况的偏差,概念非常相近,作用也类似,初学时很容易混淆。其实区别二者的关键在于对象,损失函数是针对单个样本,而成本函数则是针对整个数据集,也就是说,损失函数求得的总和就是成本函数。下次遇到要区别涉及偏差的函数时,你只需要看一看对象是谁:如果是针对单个样本的预测偏差,就是损失函数;如果要考察整个数据集的偏差之和,那么自然就是成本函数了。可以说,损失函数与成本函数既有联系又有区别,是微观与宏观的关系。

举个例子,假设有10个样本,我们会对这10个样本利用假设函数进行逐一预测,并逐一计算偏差,这时用到的是损失函数。当我们开始计算这10个样本的总体偏差时,我们用到的是成本函数。成本函数是由损失函数计算得到的,不过在实际计算时,可以选择令成本函数为损失函数值的总和,也可以令成本函数是损失函数值的平均,但无论是总和还是平均,其最终目标都是希望最小化成本,从而使假设函数的预测最可靠。

根据上述介绍,模型的每一轮预测结果偏差严格来说应该称作“成本值”,但这个词既不形象,念起来又拗口,后面我们将采用“损失值”来指代预测与实际的偏差。损失函数和成本函数都是衡量偏差的标尺工具,二者确实存在区别,但这种区别需要在引入样本、进入实际的模型训练阶段才能体现,仅从算法本身来看,二者并没有实质的不同。因此,许多教材在书写某个机器学习算法偏差值的计算公式时,有的会选择使用J(x)表示,有的会选择使用L(x)表示,看似杂乱无章,缺乏规范,实际上都是代表了相同的意思,即计算模型产生了多大的偏差。本书选择统一使用符号L(x),即用损失函数来表示损失值的计算方法。

前面我们提到模型需要利用成本函数进行多轮学习,这里又说对损失函数求和才能得到成本函数,也许这两个涉及成本函数的多次运算会让你感到困惑。这里其实涉及了三个步骤,第一步是将每个样本用损失函数进行计算之后,各自得到一个损失值,损失值的和为成本函数的损失值;第二步是将成本函数的损失值作为优化方法的输入,完成对假设函数的参数调整;第三步即重复第一步,计算出新的损失值,再重复第二步,继续调整假设函数的参数。二者的关系应该就更清楚了。

1.4.2 机器学习的基本模式

假设函数和损失函数是机器学习的重要概念,机器学习算法看似千差万别,但如果把算法结构都拆开来比较,肯定都是假设函数和损失函数这对固定组合,再搭配一些其他零部件。

同样,机器学习的运行模式也都大同小异,整个过程有点像骑共享单车。骑车的时候,我们用脚蹬踏板,踏板盘通过链条带动后轮转动,单车就可以前进了。在机器学习中,假设函数和损失函数就发挥着踏板盘和后轮的作用(如图1-3所示)。

图1-3 假设函数产生的偏差驱动着机器学习模型不断优化

要开始进行机器学习,至少要准备三样东西。首先当然是数据,如果机器学习是一架机器,数据就是燃料,若没有燃料,再强大的机器也得“趴窝”。然后是假设函数,再然后是损失函数。把数据“喂”给假设函数,假设函数会“吐”出一个结果,我们也说过了,这个是预测结果。刚开始,假设函数的预测与瞎猜基本是一个意思,很不可靠。有多不可靠呢?这就得问问损失函数了。损失函数好比一把尺子,我们把假设函数的预测结果“喂”给损失函数,损失函数也会“吐”出一个结果,且通常是数值形式,以便告诉我们它与真实情况到底差了多少。

这就是第一轮学习过程。但仅知道差了多少并没有什么用,我们要的是一个能进行有效预测的假设函数。那么,我们就会根据损失函数的返回结果,用一个名为优化方法的过程来调整假设函数。初始的假设函数就像没有经过训练的动物,需要“喂”给它数据,经过多轮学习后才终于打通任督二脉,成为绝世高手。

前面我们反复提到一个词——拟合,而假设函数和损失函数可以算是驱动拟合不断进行的两枚重要齿轮,通过二者的配合产生源源不断的动力,驱动着机器学习模型不断朝着损失值最小化的方向逼近,最终完成学习。

1.4.3 优化方法

上面已经反复提到了优化方法,优化方法可以算是假设函数和损失函数之间的传声筒。假设函数通过调整参数,能够对输入的数据产生期望的输出,即预测值。损失函数则可以通过比较预测值和实际值计算出损失值。可是损失函数得到损失值之后,好像有一点害羞,不好意思直接告诉假设函数出现了错误以及应该怎样修改。这时就轮到优化方法上场了,损失函数把损失值告诉优化方法,优化方法再告诉假设函数如何调整参数。

听起来好像很厉害,实际上优化方法可能非常简单,甚至简单做一下减法就可以是一种优化方法:

新参数值=旧参数值-损失值

不过这种优化方法过于简单粗暴,只能在极特殊的情况下才能奏效。幸运的是,有一类数学家专门研究优化方法(譬如牛顿法、拟牛顿法、共轭梯度法等)。

也许仅看到这些优化方法的名字就会让你倒吸一口凉气,幸好优化方法的目的只有一个,就是通过调整假设函数的参数,令损失函数的损失值达到最小。不妨先把优化方法当作min(L(x))函数,就好比排序算法虽然种类众多,但经典的仅有冒泡排序、选择排序、插入排序等十多种,思路和方法不同,执行效率也各不相同,但效果是完全一样的。

梯度下降(Gradient Descent)法是机器学习中常用的一种优化方法,梯度是微积分学的术语,某个函数在某点的梯度指向该函数取得最大值的方向,那么它的反方向自然就是取得最小值的方向。所以只要对损失函数采用梯度下降法,让假设函数朝着梯度的负方向更新权值,就能达到令损失值最小化的效果。

梯度下降法涉及微积分学的相关知识,需要了解其背后的性质才能更好地理解。不过梯度下降法的原理和日常生活中很常见的倒车入库非常类似。如果把车尾部和车库底杆之间的距离看成损失值,那么倒车也可以看作一个不断调整、让损失值逼近最小的过程。

我们怎么指挥司机倒车呢?首先观察倒车的方向是否正确,确保不会倒歪了,接着检查车尾部后面还有多少空间,如果还有空间,车子就会一点一点地往后蹭,当车差不多贴上底杆了,我们就喊一句“停”,倒车完成。不过总是一点点蹭,未免太浪费时间,所以当离底杆还有一段距离时,我们会让车子一次性多倒一点,如果已经很接近底杆了,我们就会提醒司机放慢倒车速度。总之,希望能够尽快缩小车尾部和底杆的距离。梯度下降法也是如此,首先由梯度确定方向,当损失值比较大时,梯度会比较大,假设函数的参数更新幅度也会大一些,随着损失值慢慢变小,梯度也随之慢慢变小,假设函数的参数更新也就随之变小了。这就是采用梯度下降作为优化方法时,利用梯度调整假设函数的参数,最终使损失值取得最小值的过程。

如果样本数量庞大,完成一次完整的梯度下降需要耗费很长时间,在实际工作中会根据情况调整每次参与损失值计算的样本数量。每次迭代都使用全部样本的,称为批量梯度下降(Batch Gradient Descent);每次迭代只使用一个样本的,称为随机梯度下降(Stochastic Gradient Descent)。因为需要计算的样本小,随机梯度下降的迭代速度更快,但更容易陷入局部最优,而不能达到全局最优点。