《机器学习系统设计:Python语言实现》一1.2 设计原理

.本节书摘来自华章出版社《机器学习系统设计:Python语言实现》一书中的第1章,第1.2节,作者 [美] 戴维·朱利安(David Julian),更多章节内容可以访问云栖社区“华章计算机”公众号查看

1.2 设计原理

我们经常拿系统设计和其他事物的设计进行类比,例如建筑设计。在一定程度上,这种类比是正确的,它们都是依据规格说明,在结构体中放置设计好的组件。但当我们考虑到它们各自的运行环境时,这种类比就会瓦解。在建筑设计上,通常会假设,当景观正确形成后就不会再改变。
软件环境则有些不同。系统是交互和动态的。我们设计的任何系统,诸如电子、物理,或人类,都会嵌入在其他系统中。同样,在计算机网络中有不同的层(应用层、传输层、物理层,等等),具有不同的含义和功能集,所以在项目中,需要在不同的层完成所需执行的活动。
作为这些系统的设计师,我们必须对其背景(即我们所工作的领域)具有强烈意识。领域知识能够赋予我们工作的背景,为我们在数据中发现模式提供线索。
机器学习项目可以分解为如下6项不同的活动:
定义目标和规格说明
准备和探索数据
建立模型
实现
测试
部署
设计师主要关注前三项活动。但是,他们通常需要在其他活动中扮演主要角色,并且在许多项目中必须如此。同时,这些活动在项目的时间表中不一定是线性序列。但重点是,这些都是明确不同的活动。这些活动可以并行进行,或者彼此相互作用,但通常会涉及不同类型的任务,在人力和其他资源、项目阶段和外在性上相互分离。而且,我们需要考虑到不同的活动所涉及的操作模式也彼此不同。想想看,我们在勾勒想法时、进行特定分析任务时,以及编写一段代码时,大脑工作方式的差异。
通常,最困难的是从何下手。我们可以先专研某一问题的不同要素,构思其中的特征集,或者考虑用什么模型。这样就能得出目标和规格说明的定义。或者我们可能不得不进行一些初步研究,例如检查潜在的数据集和数据源、评估适用的技术,或与其他工程师、技术专家和系统用户进行探讨。我们还需要探索操作环境和各种约束;确定这是一个Web应用还是科学家们的实验室研究工具。
在设计的早期阶段,我们的工作流程会在不同要素的工作上切来切去。例如,我们首先着手于必须要解决的一般性问题,这时可能只是形成一些关于任务的思路,然后就将其分解为我们认为是关键的特征,尝试使用模拟的数据集对其建立一些模型,再回过头来修订特征集,调整模型,细化任务,改进模型。当感觉系统足够健壮时,可以使用一些真实数据进行测试。当然,有可能需要回过头来改变特征集。
对于机器学习设计师而言,选择和优化特征通常是一项主要活动(其本身就是一个任务)。在没有充分地描述任务之前,我们无法真正确定所需的特征。当然,任务和特征都受我们所建立的可行模型的类型的约束。

1.2.1 问题的类型

作为设计师,我们的责任是解决问题。我们要在所提供的数据上得出预期的成果。第一步是以机器能够理解的方式来表示问题,同时这种方式也能够承载人类的意图。以下六点概括了问题的类型,可以帮助我们精确定义所要解决的机器学习问题:
探索(Exploratory):分析数据,寻找模式,例如趋势或不同变量之间的关系。探索通常会得出一些假设,例如,饮食和疾病的关联、犯罪率和城镇住宅的关联等。
描述(Descriptive):总结数据的具体特征。例如,平均寿命、平均温度,或人口中左撇子的数量等。
推理(Inferential):推理性问题是用来支持假设的,例如,使用不同数据集来证明或证伪寿命和收入之间存在一般性关联。
预测(Predictive):预测未来的行为。例如,通过分析收入来预测寿命。
原因(Casual):试图发现事物的原因。例如,短寿命是否是由低收入导致的?
机制(Mechanistic):试图解答诸如“收入和寿命关联的机制是什么?”的问题。
大多数机器学习系统在开发过程中会涉及多种问题类型。例如,我们首先需要探索数据来发现模式或趋势,然后需要描述数据的具体关键特征。这样,我们可能会给出一个假设,并发现其原因或特定问题背后的机制。

1.2.2 问题是否正确

问题在其主题领域内必须是合理且有意义的。领域知识能够帮助我们理解数据中重要的事物,发现有意义的特定模式和相关性。
问题应该尽可能具体,同时还能给出有意义的答案。通常在开始时,对问题的陈述不那么具体,例如“财富是否意味着健康”。因此,我们需要进一步研究。我们会发现,可以从税务局得到地域财富统计,可以通过健康的对立面,即疾病,来度量健康,而疾病数据可以从医院接诊处获得。这样,我们就能够将疾病和地域财富进行关联,验证最初的命题:“财富意味着健康”。我们可以发现,更为具体的问题会依赖于多个可能存疑的假设。
我们还应该考虑到,穷人可能没有医疗保险,因此生病了也不大可能去医院,而这一因素可能会混淆我们的结果。我们想要发现的事物和试图去度量的事物之间具有相互作用。这种相互作用可能会隐藏真实的疾病率。但是还好,因为知道这些领域知识,我们或许能够在模型中解释这些因素。
通过学习尽可能多的领域知识,能够让事情变得简单得多。
检查一下我们的问题是否已经有答案,或者部分问题已有答案,又或者已经存在数据集对此有所启示,这都有可能节省大量的时间。通常,我们需要同时从不同角度来处理问题。我们应该尽可能做更多的准备性研究。其他设计师完成的工作很可能会对我们所有启发。

1.2.3 任务

任务是一段时间内进行的特定活动。我们必须区分人工任务(计划、设计和实现)和机器任务(分类、聚类、回归等)。同时也要考虑有时人工任务会和机器任务重合,例如,为模型选择特征。在机器学习中,我们真正的目标正是要尽可能地将人工任务变换为机器任务。
现实世界中的问题到具体任务的适配并不总是很容易。很多现实世界的问题看起来可能有概念上的关联,但是却需要非常不同的解决方案。与之相反,看起来完全不同的问题,却可能需要相同的方法。不幸的是,在问题和特定任务之间的适配不存在什么简单的查找表,而更多依赖于背景和领域。同样的问题,换个领域,可能就因为缺少数据而无法解决。然而,在适用于解决众多最具共性的问题类型的大量方法中,存在少数通用的任务。换言之,在所有可能的规划任务的空间内,存在一个任务子集,适用于特定问题。在这个子集内,存在更小的任务子集,是简单有效的。
机器学习任务大致有如下三种环境:
有监督学习(Supervised learning):其目标是,从有标签训练数据中学习建立模型,允许对不可见的未来数据进行预测。
无监督学习(Unsupervised learning):处理无标签数据,其目标是在数据中发现隐含模式,以抽取有意义的信息。
强化学习(Reinforcement learning):其目标是,开发一个系统,基于该系统与其环境的相互作用,提高系统的性能。其中通常会引入奖赏信号。与有监督学习类似,但是没有标签训练集,强化学习使用奖赏函数来持续改进其性能。
现在,让我们看看一些主要的机器学习任务。下图可以作为我们的起点,尝试决定不同的机器学习问题适用什么类型的任务。
《机器学习系统设计:Python语言实现》一1.2 设计原理

1.分类
分类大概是最常见的任务类型了,主要是因为它相对容易,很好理解,能够解决很多常见问题。分类基于特征对一组实例(样本)赋予类别。分类是有监督学习方法,它依赖标签训练集来建立模型参数。建立好的模型可以应用于无标签数据,用来预测每个实例所属的类别。大致上,有两种分类任务:二分类(binary classification)和多分类(multiclass classification)。垃圾邮件检测就是一种典型的二分类任务,它只有两种类别,即垃圾或非垃圾,它根据邮件内容来确定其所属类别。手写识别则是多分类的例子,它需要预测输入的是什么字符。在这种情况下,每个字母和数字字符都是一个类别。多分类有时可以通过链接多个二分类任务来实现,但是这种方法会丢失信息,我们无法定义单一决策边界。因此,多分类和二分类通常需要分别对待。
2.回归
在某些情形下,我们所关心的事物并非是离散的类别,而是连续变量,例如概率。这种类型的问题称为回归问题。回归分析的目的是,理解自变量的变化如何影响因变量的变化。最简单的回归问题是线性的,为了进行预测,需要将一组数据近似为一条直线。这种方法通常需要最小化训练集中每个实例的误差平方和。典型的回归问题有,通过给定的症状范围和严重程度,评估相应疾病的可能性,或者根据过往表现来预测测试得分。
3.聚类
聚类是最为著名的无监督方法。聚类关注的是,对无标签数据集内实例的相似性进行度量。我们通常会基于实例的特征值,采用几何模型,度量实例之间的距离,来确定其相似性。我们可以使用任意封闭的度量值,来确定每个实例所属的聚类簇。在数据挖掘和探索性数据分析中会经常使用聚类。有大量不同的方法和算法用来执行聚类任务,其中有些会利用这种基于距离的方法,并且还会发现每个聚类簇的中心点,还有一些方法会利用基于分布的统计技术。
关联和聚类有关,也是一种无监督任务,用以在数据中发现特定类型的模式。很多产品推荐系统都使用了这一方法,例如Amazon和其他网店。
4.降维
很多数据集中,每个实例都包含了大量特征或度量值。这会给计算能力和内存分配带来挑战。同时,很多特征会包含冗余信息,或与其他特征相关的信息。在这种情况下,学习模型的性能可能会显著退化。降维最常用于特征预处理,它将数据压缩到较低维度的子空间,但同时保留了有用的信息。降维也常用于数据可视化,通常将高维数据投影为一维、二维、或三维数据。
源自这些基础的机器学习任务还有大量派生任务。在许多应用中,学习模型可能只是用来进行预测以建立因果关系。我们必须知道,解释和预测并不相同。模型可以用来进行预测,但除非明确地知道它是如何进行预测的,否则我们无法形成可理解的解释。解释需要人类的领域知识。
我们还可以使用预测模型发现与一般模式不同的例外。而这时,我们感兴趣的正是这些与预测偏离的个例。这通常称为异常检测(anomaly detection),而且有着广泛应用,例如,银行欺诈检测、噪声过滤,甚至是寻找外星生命。
还有一种重要且可能有用的任务是子群发现。子群发现的目标与聚类不同,不是对整个领域进行划分,而是发现具有基本上不同分布的子群。本质上,子群发现是试图在目标因变量和大量解释性自变量之间找到关系。我们并非试图找到领域内的完整关系,而是其中不同的具有重要意义的一组实例。例如,对目标变量heart disease=true,建立一个子群,其解释变量为smoker=true且family history=true。
最后,我们还要考虑控制类型的任务。这些任务是根据不同条件,对控制设置进行优化,最大化收益。控制任务可以有多种方式。我们可以克隆专家行为:机器直接学习人类,对不同条件下的行动进行预测。这项任务是学习专家行动的预测模型。这类似于强化学习,其任务是学习条件和最优行动之间的关系。
5.错误
对于机器学习系统,软件缺陷会给现实世界带来非常严重的后果;如果我们的算法用于装配线机器人,当它把人分类为产品组件会发生什么?显然,对于关键系统,我们需要对失败进行计划。在我们的设计过程和系统中,应该具备健壮的故障和错误检测程序。
有时,有必要只是为了调试和检查逻辑缺陷而设计十分复杂的系统。可能还有必要创建具有特定统计结构的数据集,或者创建人造人去模拟人机界面。例如,为了验证设计在数据、模型和任务等不同层次上都是合理的,我们需要开发一系列方法。错误可能是难以跟踪的,但是作为科学家,我们必须假设系统中存在错误,否则就必须努力进行论证。
对于软件设计师而言,识别和优雅地捕获错误的思想很重要,但作为机器学习系统设计师,我们必须更进一步。在我们的模型中,我们需要获取从错误中学习的能力。
我们必须考虑如何选择测试集,特别是,测试集如何代表其余数据集。例如,如果与训练集相比,测试集充满更多噪声数据,这将会导致恶劣的结果,说明我们的模型过度拟合,然而事实上却并非如此。为了避免这种情况,可以使用交叉验证。交叉验证将数据随机分为大小相等的数据块,例如分为十个数据块。我们使用其中九个数据块对模型进行训练,使用剩下的一个数据块进行测试。然后,重复十次该过程,每次使用不同的一个数据块进行测试。最后,我们采用十次测试的平均结果。除了分类,交叉验证还可以用于其他有监督学习问题,但是正如我们所知道的,无监督学习问题需要不同的评估方法。
在无监督任务中,我们没有有标签训练集。因此,评估会有些棘手,因为我们不知道正确的结果是什么样的。例如,在聚类问题中,为了比较不同模型的质量,我们可以度量簇直径和簇间距之间的比率。然而,对于复杂问题,我们可能永远不知道是否存在更好的模型,也许该模型还没有建立。
6.优化
优化问题在不同领域都普遍存在,例如,金融、商业、管理、科学、数学和工程等。优化问题包括以下几个方面:
目标函数,我们想要最大化或最小化目标函数。
决策变量,即一组可控输入。这些输入在特定约束内可变,以满足目标函数。
参数,即不可控或固定的输入。
约束条件,即决策变量和参数之间的关系。约束条件定义了决策变量的取值空间。
大多数优化问题只有单一的目标函数。当有多目标函数时,我们通常会发现它们彼此有冲突,例如,降低成本和增加产量。在实践中,我们试图将多目标转化为单一函数,例如通过创建目标函数的加权组合。在成本和产量例子中,类似单位成本这样的变量可能会解决问题。
为实现优化目标,我们需要控制决策变量。决策变量可能包括诸如资源或人工等事物。每个运行模型的模块,其参数是不变的。我们可以使用几种测试案例,选择不同的参数,测试在多种条件下的变化。
对于众多不同类型的优化问题,存在成千上万的解决算法。大多数算法首先会找到一个可行解,然后通过调整决策变量,进行迭代改进,以此来发现最优解。使用线性规划技术可以相当好地解决许多优化问题。其中假设目标函数和所有约束与决策变量呈线性关系。如果这些关系并非是线性的,我们通常会使用适当的二次函数。如果系统是非线性的,则目标函数可能不是凸函数。也就是说,可能会存在多个局部极小值,同时不能保证局部极小值是全局极小值。
7.线性规划
线性模型为何如此普遍?首先是因为其相对容易理解和实现。线性规划有着完善的数学理论基础,其于18世纪中期就已经发展形成,此后,线性规划在数字计算机的发展中扮演着极为关键的角色。因为计算机的概念化大量依赖于线性规划理论基础,所以计算机极为适合于实现线性规划。线性函数总是凸函数,即只有一个极小值。线性规划(Linear Programming,LP)问题通常使用单纯形法求解。假设要求解优化问题,我们将采用如下语法来表示该问题:
《机器学习系统设计:Python语言实现》一1.2 设计原理

假设其中的x1和x2都大于等于0。我们首先需要做的是将其转换为标准型。完成这一转换需要确保该问题是极大化问题,即需要将min z转换为max –z。我们还需要通过引入非负松弛变量将不等式转换为等式。这里的例子已经是最大化问题了,所以可以保留我们的目标函数不变。我们需要做的是将约束条件中的不等式变为等式:
《机器学习系统设计:Python语言实现》一1.2 设计原理

如果以z来表示目标函数的值,则可以将目标函数变换为如下表达式:
《机器学习系统设计:Python语言实现》一1.2 设计原理

如此,我们可以得到如下线性方程组:


《机器学习系统设计:Python语言实现》一1.2 设计原理

我们的目标是求解极大化z,并且注意其中所有变量都是非负值。我们可以发现x1和x2出现在所有方程中,我们称之为非基变量。x3和x4只出现在一个方程中,我们称之为基变量。通过将所有非基变量赋值为0,我们可以得到一个基解。这样可以得到如下方程组:
《机器学习系统设计:Python语言实现》一1.2 设计原理

这是最优解吗?还记得我们的目标是求极大化z吗?在线性方程组的第一个方程中有z减x1和x2,因此我们还是能够增大这些变量的。但如果该方程的所有系数都是非负数,则不可能增大z。我们得知,当目标方程的所有系数为正时,可以得到最优解。
这里不是这种情况。所以,我们要把目标方程中系数为负的非基变量通过主元消元法(pivoting),变为基变量(例如x1,称之为进基变量(entering variable))。同时,我们会将基变量变为非基变量,称之为离基变量(leaving variable)。我们可以看到,x1同时出现在两个约束条件方程中,那我们选择哪一个进行主元消元呢?记住我们的目标是要确保系数为正。我们发现,当主元方程的右值与其各自的进基系数的比值最小时,可以得到另一个基解。对于此例中的x1,第一个约束条件方程右值和进基系数比是4/2,第二个约束条件方程是3/1。因此,我们选择对约束条件方程1中的x1进行主元消元。
用约束条件方程1除以2,得到如下方程式:
《机器学习系统设计:Python语言实现》一1.2 设计原理

这样,我们可以得到x1,然后将其代入到其他方程中,对x1进行消元。当我们进行一系列代数运算后,最终可以得到如下线性方程组:
《机器学习系统设计:Python语言实现》一1.2 设计原理

如上,我们得到另一个基解。但是,这是最优解吗?因为在目标方程中,还存在一个负系数,所以答案是否定的。我们同样可以对x2运用主元消元法,使用最小右值系数比规则,我们发现,可以选择第三个方程中的3/2x2作为主元。这样可以得到如下方程组:
《机器学习系统设计:Python语言实现》一1.2 设计原理

这样,我们可以得到解为和。因为在第一个方程中不存在负系数,所以这是最优解。
我们能够以如下图形进行可视化。在阴影区域,我们可以发现可行解。
《机器学习系统设计:Python语言实现》一1.2 设计原理

8.模型
线性规划为我们提供了将现实世界问题编码为计算机语言的策略。然而,必须记得我们的目标并非只是解决一个问题实例,而是要建立模型来解决新产生数据中的独特问题。这是机器学习的本质。学习模型必须具有评估其输出的机制,据此来改变其行为,以达到最佳求解状态。
这种模型本质上是一种假设,也就是说,是对现象的一种推荐解释。其目标是对问题进行归纳。对于有监督学习问题,从训练集获取的知识被应用于对无标签数据的测试上。对于无监督学习问题,例如聚类,系统不是从训练集学习,而是从数据集本身的特征中学习,例如相似度。无论是哪种情况,其过程都是迭代的,都要重复定义良好的任务集,使模型更接近正确的假设。
模型是机器学习系统的核心,是对学习的实现。有大量模型及其变体,其作用各不相同。机器学习系统可以解决出现在众多不同背景下的问题(回归、分类、关联等)。它们已经成功地被应用于科学、工程学、数学、商业,甚至社会科学等几乎所有的分支,它们与其所应用的领域一样具有多样性。
这种多样性给机器学习系统带来了巨大的问题解决能力,但同时也让设计师有些望而生畏。对于特定问题,哪一个或哪一些模型是最佳的,做出这样的抉择并不容易。对于复杂事物,通常有多个模型能够解决问题,或者解决这个问题需要多个模型。在开始着手这样的项目时,我们无法轻易知道由初始问题到解决该问题的最为准确和有效的路径。
就我们的目标而言,可以将模型分为重叠、互斥和排他的三类:几何、概率和逻辑。在这三个模型之间,必须要关注的区别是如何对实例空间(样本空间)进行划分。实例空间可以被认为是所有可能的数据实例,但并不是每个实例都要在数据中出现。实际数据是实例空间的子集。
划分实例空间有两种方式:分组和分级。两者之间的关键差异在于,分组模型将实例空间划分为固定的离散单元,称为段(segment)。分组具有有限解析度,并且无法区分超越了这一解析度的类型。另一方面,分级会对整个实例空间形成全局模型,而不是将空间分为段。理论上,分级的解析度是无限的,无论实例多么相似,分级都能够加以区分。分组和分级的区别也不是绝对的,很多模型都兼而有之。例如,基于连续函数的线性分类器一般被认为是分级模型,但也存在其无法区分的实例,比如平行于决策边界的线或面。
(1)几何模型
几何模型使用实例空间的概念。几何模型最显著的例子是,所有特征都是数值,并且可以在笛卡儿坐标系中坐标化。如果我们只有两到三个特征,则很容易可视化。然而,很多机器学习问题都有成百上千个特征和维度,因此不可能对这些空间进行可视化。但是,很多几何概念,例如线性变换,还是能在这种超空间中应用的。为了便于理解,例如,我们认为很多学习算法具有平移不变性,也就是说,与坐标系原点位置无关;同时,我们可以采用几何概念中的欧几里得距离来度量实例间的相似性;这样我们就有了一种对相似实例进行聚类并形成决策边界的方法。
假设,我们使用线性分类器对文章段落是快乐的还是悲伤的进行分类。我们可以设计一个测试集,给每个测试都分配一个权重w,以确定每个测试对总体结果的贡献。
我们可以用每个测试的分值乘以其权重,并进行求和,以作为总体分值,并以此建立决策边界,例如,快乐分值是否大于阈值t。
《机器学习系统设计:Python语言实现》一1.2 设计原理

每个特征值对总体结果的贡献都是独立的,因此这一规则是线性的。这一贡献依赖于特征的相对权重,权重可以为正,也可以为负。计算总体分值时,个体特征值不受阈值限制。
我们可以使用向量来重新表示以上求和算式,w表示权重向量(w1, w2, ..., wn),x表示测试分值向量(x1, x2, ..., xn)。同时,如果向量维度相等,我们可以定义决策边界为:
《机器学习系统设计:Python语言实现》一1.2 设计原理

我们可以认为w是负值(悲伤)“质心”N,指向正值(快乐)“质心”P的向量。可以通过分别计算正负值的平均值得到正负值的质心:
《机器学习系统设计:Python语言实现》一1.2 设计原理

我们的目标是以正负值质心的中间值作为决策边界。可以看到,w与P-N成正比或相等,(P+N)/2即为决策边界。因此,可以得出如下算式:
《机器学习系统设计:Python语言实现》一1.2 设计原理

《机器学习系统设计:Python语言实现》一1.2 设计原理

在实践中,真实的数据是具有噪声的,并且不一定容易分离。即便有时数据是容易分离的,但特定的决策边界也可能并没有多大意义。考虑到数据是稀疏的,例如在文本分类器中,单词总数对于每个单词实例的数量而言是巨大的。在这样大而空的实例空间内找到决策边界可能很容易,但是否为最佳选择呢?有种选择方法是使用边缘,即决策边界与实例之间的最小距离。我们将在本书中探索这些技巧。
(2)概率模型
贝叶斯分类器是概率模型的典型例子,即给定一些训练数据(D)和基于初始训练集的概率(特定假设,h),给出后验概率P(h/D)。
《机器学习系统设计:Python语言实现》一1.2 设计原理

举个例子,假设我们有一袋大理石,其中40%是红色的,60%是蓝色的,同时,一半红色大理石和所有蓝色大理石有白斑。当我们伸进袋子选择一块大理石时,可以通过感受其纹理得知是否有白斑。那么得到红色大理石的概率有多少?
设P(R|F)等于随机抽取有白斑的大理石是红色的概率:
P(F|R)=红色大理石有白斑的概率,为0.5。
P(R)=大理石为红色的概率,为0.4。
P(F)=大理石有白斑的概率,为0.5×0.5+1×0.6=0.8。
《机器学习系统设计:Python语言实现》一1.2 设计原理

概率模型允许我们明确地计算概率,而不仅是在真或假中二选一。正如我们所知,其中的关键是需要建立映射特征变量到目标变量的模型。当采用概率方法时,我们假设其背后存在着随机过程,能建立具有良好定义但未知的概率分布。
例如,垃圾邮件检测器。特征变量X可以由代表邮件可能是垃圾的词语集合组成。目标变量Y是实例类型,取值为垃圾或火腿。我们关心的是给定X的情况下,Y的条件概率。对于每个邮件实例,存在一个特征向量X,由表示垃圾词语是否出现的布尔值组成。我们试图发现目标变量Y的布尔值,代表是否为垃圾邮件。
此时,假设我们有两个词语,x1和x2,构成了特征向量X。由训练集,我们可以构造下表:
《机器学习系统设计:Python语言实现》一1.2 设计原理

我们可以看到,如果在特征向量中加入更多词语的话,该表格会快速增长失控。对于n维特征向量,我们会有2n种情况需要区分。幸运的是,有其他方法可以解决这一问题,稍后我们会看到。
表1.1中的概率叫作后验概率,用于我们具备先验分布知识的情况下。例如,有1/10的邮件是垃圾邮件。然而,如果我们知道X中包含x2=1,但是不确定x1的值,在这种情况下,该实例可以是行2,垃圾邮件的概率是0.7,也可能是行4,垃圾邮件的概率是0.8。其解决方案是,基于x1=1的概率,对行2和行4进行平均。也就是说,需要考虑x1出现在邮件中的概率,无论是否为垃圾邮件:
《机器学习系统设计:Python语言实现》一1.2 设计原理

上式称为似然函数。如果由训练集得知x1=1的概率是0.1,x1=0的概率是0.9,因为概率和必须为1,那么,我们就可以计算邮件包含垃圾词语的概率是0.7×0.9+ 0.8×0.1=0.71。
这是一个似然函数的例子:P(X|Y)。X是我们已知的,Y是我们未知的,那么,为什么我们想要知道以Y为条件X的概率呢?对于理解这一点,我们可以考虑任意邮件包含特定随机段落的概率,假设是《战争与和平》的第127段。显然,无论邮件是否为垃圾邮件,这种可能性都很小。我们真正关心的并不是这两种可能性的量级,而是它们的比率。包含有特定词语组合的邮件更像是垃圾邮件,还是非垃圾邮件?这些有时被称为生成模型,因为我们可以对所有相关变量进行采样。
我们可以利用贝叶斯定理在先验分布和似然函数之间进行变换:
《机器学习系统设计:Python语言实现》一1.2 设计原理

P(Y)是先验概率,即在观察到X之前,每种类型的可能性。同样,P(X)是不考虑Y的概率。如果只有两种类型,我们可以采用比率的方式。例如,当想要知道数据更支持哪一类型时,可以采用如下比率形式(spam,垃圾邮件;ham,非垃圾邮件):
《机器学习系统设计:Python语言实现》一1.2 设计原理

如果比率小于1,我们假设位于分母的类型的可能性较大。如果比率大于1,则位于分子的类型的可能性较大。当代入表1.1中的数据时,可以计算出如下后验概率:
《机器学习系统设计:Python语言实现》一1.2 设计原理

似然函数对于机器学习非常重要,因为它创建了生成模型。如果我们知道每个词语在词语表中的概率分布,以及在垃圾邮件和非垃圾邮件中出现的可能性,我们就能够根据条件概率P(X|Y=垃圾)生成随机垃圾邮件了。
(3)逻辑模型
逻辑模型以算法为基础。它们可以被翻译为人类能够理解的一组正式规则。例如,如果x1和x2都为1,则邮件被分类为垃圾邮件。
这些逻辑规则可以被组织为树形结构。在下图中,我们看到在每一分支上迭代地划分了实例空间。叶子由矩形区域组成(在高维度的情况下,或者是超矩形),代表了实例空间的段。基于所要解决的任务,叶子上标记了类型、概率、实数等。
《机器学习系统设计:Python语言实现》一1.2 设计原理

特征树对于表示机器学习问题十分有用,即便是那些第一眼看上去不是树结构的问题。例如,在之前章节中的贝叶斯分类器,我们可以根据特征值的组合,将实例空间划分为多个区域。决策树模型通常会引入剪枝技术,删除给出错误结果的分支。在第3章中,我们将看到在Python中表示决策树的多种方式。
请注意,决策规则可能会重叠,并且给出矛盾的预测。
这些规则被认为是逻辑不一致的。当没有考虑特征空间的所有坐标时,规则也可能是不完整的。有很多方法可以解决这些问题,我们将在本书中详细介绍。
树学习算法通常是以自顶向下的方式工作的,因此,首要任务是选择合适的特征,用于在树顶进行划分,以使划分结果在后续节点具有更高纯度。纯度指的是,训练样本都属于同一类型的程度。当逐层向下时,我们会发现在每一节点上,训练样本的纯度都在增加,也就是说,这些样本逐层被划分为自己的类型,直到抵达叶子,每一叶子的样本都属于同一类型。
从另一角度来看,我们关注降低决策树后续节点的熵。熵是对混乱的度量,在树顶(根节点)最高,逐层降低,直到数据被划分为各自的类型。
在更为复杂的问题中,有更大的特征集和决策规则,有时不可能找到最优的划分,至少在可接受的时间范围内是不可行的。我们真正关心的是创建最浅树,实现到达叶子的最短路径。就其用于分析的时间而言,每一额外的特征都会带来每个节点的指数级增长,因此,找到最优决策树比实际上使用子最优树来完成任务,所需要的时间更长。
逻辑模型的一个重要特性是,它们可以为其预测提供一定程度的解释。例如,对于决策树做出的预测,通过追溯由叶子到根的路径,可以确定得出最终结果的条件。逻辑模型的优点之一是:能够被人类探查,揭示更多问题。
9.特征
在现实生活中,我们得到的信息越多越有价值,那么所做出的决策也就越好,同理,对于机器学习任务,模型的好坏与其特征息息相关。在数学上,特征是实例空间到特定域内集合的映射函数。对于机器学习而言,我们所做的大多数度量都是数值型的,因此,大多数常见的特征域是实数集合。其他常见域还有布尔、真或假、整数(通常用于计算某一特征发生的次数),或诸如颜色或形状等有限集合。
模型是依据其特征进行定义的。单一特征也能够成为模型,我们称之为单变量模型。我们可以区别特征的两种用法,这与分组和分级的区别相关。
首先,我们关注实例空间的具体区域对特征进行分组。设X为一封邮件,f为对邮件中某一词语x1进行计数的特征。我们可以建立如下条件:
当f(X)=0,表示邮件不包含x1;当f(X)>0,则表示邮件中包含一次或多次x1。这种条件叫作二划分(binary splits),因为它们将实例空间划分为两个分组:满足条件的和不满足条件的。我们还可以将实例空间划分为多于两个的段,即非二划分。例如,当f(X)=0;05,等等。
其次,我们可以对特征进行分级,计算每个特征对总体结果的独立贡献。在我们之前所述的简单线性分类器中,其决策规则如下:
《机器学习系统设计:Python语言实现》一1.2 设计原理

该规则是线性的,因此每一特征对某一实例的分值具有独立贡献,其贡献依赖于wi。如果wi为正,则值为正的xi会增加总分值。如果wi为负,则值为正的xi会减少总分值。如果wi很小或者为零,则其对总体结果的贡献可以忽略不计。可以看到,这些特征对最终预测做出了可度量的贡献。
特征的这两种用法,即划分(分组)和预测(分级)可以在同一模型中组合。有个典型的例子是,当我们求某一非线性函数的近似解时,例如y sin x,其中-1《机器学习系统设计:Python语言实现》一1.2 设计原理

在特征构建和变换方面,有很多工作可以用来改进模型性能。在大多数机器学习问题中,特征并不一定是直接可用的,而是需要从原始数据集中构建,并变换为模型可用的形式。这在诸如文本分类等问题中尤为重要。在垃圾邮件例子中,我们使用了词语包的表示形式,因为这样可以忽略词语的顺序,然而也会丢失有关文本含义的重要信息。
离散化是特征构建的一个重要部分。有时,我们可以把特征划分为相应的块,以抽取与任务更为相关或更多的信息。例如,假设数据包含一组精确的收入信息,而我们要试图确定人们的经济收入与其所居住的城郊之间是否存在关系。显然,收入范围比精确收入更适合作为特征集,尽管严格地说,这样会丢失一些信息。如果选择恰当的收入范围区间,我们不仅不会丢失与问题有关的信息,而且会让模型执行得更好,得出的结果也更易于解释。
这突出了特征选择的主要任务:从噪声中分离信号。
现实世界的数据总是包含大量我们不需要的信息,以及简单的随机噪声,而从中分离出我们所需要的数据,可能只是其中的一小部分数据,对模型的成功是十分重要的。当然,我们不能丢掉对我们可能重要的信息。
通常,特征可能是非线性的,而且线性回归的结果可能并不理想。有种技巧是对实例空间进行变换。假设我们的数据如下图所示。显然,线性回归只是得出一种合理的拟合,如下图左侧所示。然而,如果对实例空间的数据取平方,即x=x2,y=y2,我们能得出更好的拟合结果,如下图右侧所示。
《机器学习系统设计:Python语言实现》一1.2 设计原理

进一步,我们还可以采用核方法(kernel trick)。其思想是创建更高维度的隐式特征空间,即通过特定函数,有时称为相似性函数(similarity function),将原始数据集的数据点映射到高维空间。
例如,设和。
如下所示,建立二维到三维的映射:
《机器学习系统设计:Python语言实现》一1.2 设计原理

对应于二维空间中的点x1和x2,其在三维空间中如下所示:
《机器学习系统设计:Python语言实现》一1.2 设计原理

这两个向量的点积则为:
《机器学习系统设计:Python语言实现》一1.2 设计原理

我们发现,通过对原二维空间的向量点积取平方,即可得到三维空间的向量点积,而无须求得三维空间的向量,再进行点积计算。这里,我们可以定义核函数为k(x1, x2) = (x1, x2)2。在高维空间计算点积通常代价更高,而使用核方法则更具优势,我们可以看到,这一技术在机器学习的支持向量机(Support Vector Machines,SVM)、主成分分析(Principle Component Analysis,PCA)和相关性分析中都有着广泛应用。
在之前提及的基本线性分类器中,将决策边界定义为w•x = t。向量w等于正样本平均值和负样本平均值的差,即p-n。设点n = (0, 0)和p = (0, 1)。假设我们通过两个训练样本来计算正平均值,p1 = (-1, 1)和p2 = (1, 1),则其平均值如下所示:
《机器学习系统设计:Python语言实现》一1.2 设计原理

这样,我们可以得到决策边界为:
《机器学习系统设计:Python语言实现》一1.2 设计原理

如果采用核方法,我们可以用下式来计算决策边界:
《机器学习系统设计:Python语言实现》一1.2 设计原理

基于我们之前定义的核函数,可以得到下式:
《机器学习系统设计:Python语言实现》一1.2 设计原理

这样,我们可以导出如下决策边界:
《机器学习系统设计:Python语言实现》一1.2 设计原理

这正是以t为半径围绕原点的圆。
另一方面,使用核方法,每一个新实例可以根据每一个训练样本进行评估。对于这种更为复杂的计算,其回报是能够获得更为灵活的非线性决策边界。
特征之间的相互作用是非常有趣和重要的,相关性是其中的一种相互作用形式。例如,在博客帖子中的词语,我们可能期望词语冬天和寒冷之间是正相关的,冬天和炎热之间是负相关的。那么这对我们任务中的模型意味着什么呢?假如我们是在进行情绪分析,如果这些词语一起出现,我们可能需要考虑降低其中每个词语的权重,因为附加了另一相关词语后,其对总体结果的贡献,比起该词语本身,在一定程度上会有所减弱。
同样是在情绪分析中,我们通常需要变换某些特征,以捕获其含义。例如,词组不快乐包含了两个词语不和快乐,如果只是使用1-grams分词法,可能会得到正面的情绪分值,而其本意明显是负面的。一种解决方案是(如果使用2-grams分词法,则可能会不必要地使模型复杂化),当这两个词语以这一顺序出现时,则建立一个新的特征,即不快乐,并对其分配情绪分值。
选择和优化特征是值得花费时间的。这在学习系统设计中是十分重要的部分。这也是一种迭代设计,需要在两个阶段之间不断反复。首先,需要理解我们所研究现象的特性;其次,需要通过实验来测试我们的想法。实验能够让我们对现象进行更为深入的观察,让我们能够获得更为深入的理解,并对特征进行优化。我们需要重复这一过程,直到模型足以准确反映真实的情况。
1.2.4 统一建模语言
机器学习系统可以是复杂的。对于人脑而言,理解整个系统的所有交互通常很困难。我们需要某种方式将系统抽象为一组分离的功能组件。通过图形和场景能够可视化系统的结构和行为。
UML是一种形式化语言,可以让我们以一种精确的方式对设计思想进行可视化和沟通。我们使用代码来实现系统,使用数学来表示其背后的原理,但这里还有第三个方面,在某种意义上,与前两者正交,即可视化表示系统。对设计进行绘制的过程可以帮助我们从不同的角度对其进行概念化。或许我们可以将其想象为对解决方案的三角定位。
概念模型是描述问题元素的理论方法。概念模型能够帮助我们澄清假设,证明某一特性,并帮助我们对系统的结构和交互建立基本理解。
UML能够简化复杂性,让我们能够清晰无误地与团队成员、客户和其他涉众沟通我们的设计,UML的出现正是为了满足这些需要。模型是真实系统的简化表示。这里,我们使用了模型一词的一般性含义,而不是在机器学习中更为精确的定义。UML几乎可以用来对系统所有可想象的方面进行建模。UML的核心思想是清晰地表示核心属性和功能,去除无关和潜在歧义的元素。
1.类图
类图是对系统的静态结构进行建模。类表示具有共同特征的抽象实体。类的用途在于,其表达并强制化面向对象编程方法。我们可以看到,通过在代码中分离不同的对象,每个对象都是自包含单元,则我们针对每个对象的工作会更为清晰。我们可以将对象定义为特定的一组特征和与其他对象的交互。这样就可以将复杂程序分解为相互独立的功能组件。我们还可以通过继承来定义对象的子类。继承特别有用,能够在模型中反映真实世界中的层次(例如,程序员是人类的子类,Python程序员是程序员的子类)。面向对象编程可以加快整体开发速度,因为它允许复用组件,并且拥有丰富的已开发组件的类库。同时,所开发的代码更易于维护,因为我们可以替换或改变类,并且(通常)能够理解这些变化是如何对整体系统产生影响的。
事实上,面向对象编码会导致更为庞大的代码库,这意味着程序运行会变慢。但这最终并非是一种“非此即彼”的情形。对于大量的简单任务,如果不会再次使用,我们可能并不会花时间去创建类。一般地,如果我们发现在输入一些重复的代码,或者在创建同一类型的数据结构,这时,创建一个类或许是个好主意。面向对象编程的最大优点是能够在一个对象中封装数据及操作这些数据的函数。这些软件对象能够以一种相当直接的方式与真实世界中的对象相对应。
最初,设计面向对象系统可能需要花些时间。然而,一旦建立起可行的类结构和类定义,则实现这些类所需的编码任务会变得更为清晰。创建类结构是着手对系统进行建模的一种非常有用的方法。当我们定义类时,我们关心的是一组特定属性,是所有可能的属性或无关的属性的子集。它应该是真实系统的准确表示,但我们需要判断哪些是有关的,哪些是无关的。因为真实世界的现象很复杂,而我们所拥有的关于系统的信息往往不是完整的,所以这种判断是困难的。我们只能依赖于自身认知,因此领域知识(对我们所要建模的系统的理解)至关重要,无论是对于软件、自然,还是人类。
2.对象图
对象图是系统运行时的逻辑视图。对象图是特定时刻的快照,可以理解为类图的实例。在程序运行时,许多参数和变量的值在变化,而对象图的功能正是在于描绘这些变化。对象图所表示的一个关键方面正是运行时绑定。通过使用对象间的连线,我们可以对特定运行时配置进行建模。对应于对象类之间的关联关系,对象之间具有连接。因此,对象间的连接强制绑定了与类相同的约束。
《机器学习系统设计:Python语言实现》一1.2 设计原理

类图和对象图都是由相同的基本元素构成,其中,类图表示类的抽象蓝图,对象图表示在特定时刻对象的真实状态。单一对象图不能表示类的所有实例,因此在绘制对象图时,我们必须限定于重要实例或那些覆盖系统基本功能的实例。在对象图中,应当明确对象之间的关联,并标示重要变量的值。
3.活动图
活动图将在一个过程中的独立活动链接在一起,用于对系统的工作流程进行建模。活动图特别适用于对协同任务集的建模。活动图是UML规范中最常用的工具之一,因为其格式基于传统流程图,所以直观并易于理解。活动图主要包括活动、边(有时称为路径)和决策。活动由圆角矩形表示,边由箭头线表示,决策由菱形表示。活动图通常具有开始节点和结束节点。
《机器学习系统设计:Python语言实现》一1.2 设计原理

4.状态图
系统改变行为依赖于其所在状态,状态图用于对此进行建模。状态图使用状态和转换来表示。状态表示为圆角矩形,转换表示为箭头线。转换具有触发事件,触发事件写在箭头线上。
大多数状态图会包含一个初始伪状态和最终状态。伪状态是控制转换流的状态。伪状态的另一个例子是选择伪状态,其标示了决定转换的布尔条件。
状态转换系统由四种元素组成,分别是:
S = {s1, s2, …}:状态集合
A = {a1, a2, …}:活动集合
E = {e1, e2, …}:事件集合
y: S(A U E)→2s:状态转换函数
第一种元素S是主体所有可能状态的集合。活动是代理能够改变主体所做的事情。事件可以发生于主体,并且不为代理所控制。状态转换函数y有两个输入:主体的状态,活动与事件的并集。状态转换函数根据所输入的特定活动或事件,给出所有可能的输出状态。
假设我们有个仓库,存储三种货物,每种货物只能存储一件。我们可以使用如下矩阵来表示仓库存储的所有可能状态:
《机器学习系统设计:Python语言实现》一1.2 设计原理

我们还可以为事件和活动定义类似的二进制矩阵,E表示卖出事件,A表示订购活动。
在这个简单的例子中,转换函数可以作用于实例s(S中的一列),即s' = s + a - e,其中s'是系统的最终状态,s是初始状态,a和e分别是活动和事件。
我们可以用如下转换图来表示。
《机器学习系统设计:Python语言实现》一1.2 设计原理

上一篇:【C 语言】结构体 ( 结构体偏移量计算 | 代码示例 )


下一篇:代谢组学数据分析的统计学方法综述