康奈尔-CS5787-应用机器学习笔记-全-

康奈尔 CS5787 应用机器学习笔记(全)

1.1:机器学习导论 🚀

概述

在本节课中,我们将学习机器学习的基本概念。我们将探讨机器学习的定义、它在现实世界中的应用,以及它与传统编程方法的区别。通过具体的例子,我们将理解为什么机器学习是当今最具影响力的技术之一。


什么是机器学习?🤔

机器学习是当今最令人兴奋的新兴技术之一。它对技术、现实世界产品和政策制定产生了巨大影响。

在本课程中,我们将了解什么是机器学习。我们将探讨机器学习领域的主要工具、技术思想和算法。最后,我们将学习如何利用这些技术和算法解决现实世界中有趣的实际问题。

我想从几年前媒体的一些片段开始讨论。

左边是几年前《麻省理工科技评论》的一个片段,当时它将深度学习(机器学习的一种形式)命名为年度新兴技术之一。自那时起,它在科学技术的各个领域带来了重要突破。

一个非常著名的重大突破领域是围棋。中间是《自然》杂志的封面,展示了谷歌DeepMind的一个项目。该项目训练了一个基于机器学习的系统,一个基于人工智能的系统,击败了当时的围棋世界冠军。这一成就曾被认为需要数十年才能实现,但借助机器学习的最新进展,它在几年内就完成了。

最后,右边是巴拉克·奥巴马关于机器学习、人工智能和自动化话题的对话片段。这表明这项新兴技术的影响不仅限于纯粹的技术应用,还塑造了政策,并成为最高决策者讨论的一部分。

这些都是关于机器学习的有趣片段,但机器学习到底是什么?


机器学习的日常应用 📱

你可能今天已经多次使用过机器学习,甚至没有意识到。

例如,如果你使用像谷歌这样的搜索引擎来查找这些课程材料,那么你几乎肯定使用了机器学习。当你向谷歌搜索输入查询时,它使用机器学习算法来理解你的查询,并将其转换为某种形式,以便与谷歌的后端执行。后端也会使用机器学习算法来检索与你的查询相关的信息。

如果你使用智能手机,你也几乎肯定依赖于机器学习。你的智能手机在许多组件中使用机器学习。例如,如果它具有个人助理功能,如苹果Siri或谷歌助手。这些系统严重依赖机器学习。例如,当你对着手机说话时,它使用由机器学习驱动的语音识别算法,将你的声音转换为一系列字符或单词。然后,它使用另一组机器学习算法将这些单词转换为计算机可以理解的含义。接着,它还会使用机器学习来理解这些单词的含义,然后针对某种后端执行此查询以检索信息。在这个例子中,它会使用查询来检索某些图像。

最后,如果你今天使用了电子邮件,你也几乎肯定依赖于机器学习。机器学习被广泛应用于像Gmail这样的应用程序中进行垃圾邮件检测。这里是我自己几天前垃圾邮件文件夹的截图。我不必阅读所有这些无用的垃圾邮件,因为有一个机器学习系统为我进行了分类,并将它们放在一个单独的位置。

更广泛地说,如果你与银行、信用卡公司或金融机构打交道,这些公司都在使用机器学习来自动检测恶意和欺诈行为,标记并在其发生前阻止它。因此,如果你的信用卡账户最近没有发生大量欺诈,这部分归功于用于此主题的机器学习算法。

我想强调的第四个机器学习应用,也许是一个新兴应用,是自动驾驶汽车或无人驾驶汽车。我认为,自动驾驶汽车是机器学习最有趣和强大的应用之一,它们具有改变社会的巨大潜力。自动驾驶汽车可以显著减少交通拥堵,最重要的是,它们可以大大减少道路上的事故数量,并为原本无法获得出行机会的人提供出行便利。这只是机器学习在未来可能产生比今天更重大影响的一个例子。

这些都是机器学习是什么以及机器学习能做什么的例子,但我们如何定义机器学习呢?


机器学习的定义 📖

我在这里提出一个由机器学习早期先驱亚瑟·塞缪尔提出的定义。亚瑟·塞缪尔因构建了最早的算法之一、最早的使用机器学习玩棋盘游戏(在他那个时代是跳棋)的系统而闻名。他的系统工作方式是,它自己与自己进行了许多游戏,并从自我对弈中学会了如何玩得好。

因此,亚瑟·塞缪尔给出了以下机器学习定义:

机器学习是一个研究领域,它赋予计算机学习的能力,而无需进行显式编程。

这是一个非常有趣的定义,但起初可能听起来有些模糊。这里的“学习”是什么意思?“显式编程”又是什么意思?

为了更好理解这一点,让我们再看一个例子,重新审视我们之前提到的自动驾驶汽车的例子。


自动驾驶汽车示例 🚗

自动驾驶汽车实际上是一个非常复杂的系统,由许多不同的组件组成,其中很大一部分由机器学习驱动。其中一个系统是物体检测。为了理解该做什么,汽车需要知道周围道路上发生了什么。

这里你看到特斯拉自动驾驶团队演示文稿中的一个截图,展示了物体检测系统可能产生的输出示例。物体检测系统在道路上不同的物体上放置了边界框,并将我们前面的汽车用红色高亮显示,左边还有一个用黄色高亮显示的骑自行车的人。这种输出对于拥有有效的自动驾驶汽车系统至关重要。

那么,如何构建一个机器学习系统来实现这一点呢?为了理解机器学习与非机器学习的区别,让我们首先尝试一种不依赖机器学习的更简单方法,即仅涉及经典编程或经典软件工程的方法。

解决这个问题的一种方法是使用某种基于规则的系统。在图像上,你看到汽车摄像头输入的可能样子,这是我们之前的图像。因此,如果我们想检测系统中有什么,我们可以做类似这里看到的事情。这当然不是真实的代码,但原则上,它正在做的是检索某种物体,然后尝试查看这个物体是否有轮子。如果它有轮子,那么它可能是一辆汽车或一辆自行车。如果它有四个轮子,那么这很可能是一辆汽车。如果它有两个轮子,我们会想象这可能是一辆自行车。但是,实际上,如果你看这张图像,我们前面的第一辆红色汽车,从这个角度看实际上也只有两个轮子。这意味着如果从后面看,汽车也可能有两个轮子。因此,我们可以尝试修改这段代码,并指定如果从后面看到物体并且它有两个轮子,那么它可能是一辆汽车;如果从侧面看到,那么它可能是一辆自行车。

你可以看到,即使在这个简单的例子中,编写能够理解图像内容的代码的规则也迅速变得非常复杂。你可以很容易地想象,当我们开始看到真实汽车将接收到的所有类型的图像和所有类型的输入时,这将比任何基于规则的系统所能处理的要复杂得多。作为实际编程系统所有可能行为的人类程序员,我们将无法涵盖系统将看到的所有可能情况。

因此,机器学习的方法是,不手动指定规则,而是向机器展示大量期望行为的示例。然后通过观察这些行为来教导机器执行所需的任务。

在这个例子中,这又是特斯拉同一演示文稿中的一张幻灯片。这里是一个汽车可能看到的视图示例,其中道路上的不同物体已被高亮显示并赋予了标签。这里的大框是车辆的示例,右边是已被高亮显示的行人轮廓。

机器学习的想法是,如果我们给计算机一个足够大的带有注释或其他额外信息的图像数据集,通过观察这个庞大的图像集,计算机将能够学习什么使行人成为行人,什么使汽车成为汽车,并且它将能够自动从数据中推断出这些规则。因此,作为人类程序员,我们不再需要手动编写所有这些规则。我们只需要指定一个大的示例数据集,然后我们还需要编写一个更短的元算法,计算机遵循该算法将能够学习期望的行为,并自行自动推断出低级规则。

对于我们关心的许多现实世界应用来说,这是一种更有效的编程方法。


重新审视定义与重要性 🌟

现在我们可以重新审视机器学习的定义,希望“学习”和“显式编程”的定义现在更清晰一些。

当然,这是自动驾驶汽车的一个例子,但同类算法和同类方法可以应用于无数其他领域。它可以应用于医学诊断,例如,我们可以展示健康个体和不健康个体的放射学图像,通过提供足够数量的示例,机器将学会自动区分它们。

这些相同的方法也适用于其他领域,如工业自动化、机器翻译,我们会给计算机许多不同语言的相同句子的示例。这些都是我们可以使用这种编写软件的方法(即机器学习)来开发系统的应用领域。

那么,为什么这种方法有趣呢?有几个原因。

首先,这种方法允许为广泛的现实世界领域构建系统,而这些领域我们无法以任何其他方式编写系统。目前,我们构建物体检测系统、语音识别系统和许多其他类型的实用软件的最佳和最有效方法,关键依赖于机器学习。除了使用机器学习,我们不知道任何更好的编写软件的方法。这是一个非常实际的原因。

一个更普遍的原因是,工程和科学,特别是人工智能领域的目标之一是构建能够表现出智能行为,并能解决我们通常认为需要智能才能完成的广泛任务的通用系统。机器学习被广泛认为是实现这一目标的重要工具之一。因此,通过研究机器学习,我们学习技能和技术,这些对于构建越来越智能的系统至关重要。

最后,第三个更哲学的原因是,通过研究机器学习的工程和科学方面,我们也能够推断并获得关于人类学习以及学习本质的更好知识。在许多情况下,机器学习的研究为人类学习提供了新的见解。

总的来说,这些都是我认为机器学习是科学和工程领域真正令人兴奋、最激动人心的新兴领域之一的原因。在本课程中,我希望能够告诉你更多关于机器学习的知识,并给你更多关于机器学习是什么、它能做什么的例子,再次让你相信为什么这是一个如此令人兴奋的新兴领域。


总结

在本节课中,我们一起学习了机器学习的基本概念。我们探讨了机器学习的定义,了解了它在搜索引擎、智能手机、电子邮件过滤和自动驾驶汽车等日常生活中的广泛应用。通过对比基于规则的传统编程和基于示例的机器学习方法,我们理解了机器学习“从数据中学习,而非显式编程”的核心思想。最后,我们讨论了机器学习在解决复杂问题、推动人工智能发展以及增进对人类学习理解方面的重要性。机器学习是一个强大且不断发展的领域,为构建智能系统和解决现实世界问题提供了关键工具。

1.2:机器学习的三种方法 🧠

在本节课中,我们将继续介绍机器学习,描述三种通用的机器学习算法家族。这三种方法代表了构建机器学习系统和解决机器学习问题的三种高层次途径。

监督学习 📊

上一节我们介绍了机器学习的基本概念,本节中我们来看看第一种主要方法:监督学习。监督学习的核心思想是,我们希望机器学习算法能对一组特定的输入输出有用的预测。实现这一目标的方法是收集一个带标签的训练数据集,其中包含输入和输出的配对。然后,我们训练算法,使其在我们的数据上输出正确的预测。当模型看到与训练集相似的新数据时,它也能输出准确的预测。

在自动驾驶汽车检测行人的例子中,我们有行人和汽车的标签。如果我们给这个计算机视觉系统足够多带标签的区域,它将学会在从未见过的新图像上准确预测什么是行人,什么是汽车。

以下是监督学习的一个更具体的例子:波士顿房价数据集。数据集中的每个数据点代表一所房子。我们知道关于这所房子的不同属性,例如价格、社区、面积、邮政编码、房间数量等。我们可以使用Python中流行的机器学习库Scikit-learn加载这个数据集,并使用Matplotlib库将其可视化。

在这个可视化中,X轴代表社区中未获得高中文凭的成年人百分比,Y轴代表房价。可以看到,在未获得高中文凭成年人比例较高的社区,房价往往较低。一个简单的监督学习算法会将某些输入(如教育水平)映射到特定输出(如房价)。我们有一个由x(教育水平)和y(房价)组成的训练数据集,可以将其用作监督学习算法的训练数据,以预测新房屋的价格。

这里,我们从Scikit-learn库加载一个名为Kernel Ridge的特定算法。我们将在课程后面详细了解这个算法。现在,我们只需加载这个算法,在数据上训练它。算法会学习一条穿过数据的曲线,该曲线揭示了社区教育水平与房价之间的关系。未来,如果获得一个新的社区教育水平数据,我们就可以使用这条曲线进行新的预测。这是一个非常简单的监督学习例子。

监督学习方法可以应用于许多领域。当今一些最成功的机器学习应用都涉及监督学习,例如分类医学图像、语言翻译,以及之前提到的在自动驾驶汽车系统中检测物体。

无监督学习 🔍

监督学习并非我们唯一能做的机器学习类型。本课程将涉及的另一个重要类型是无监督学习。在无监督学习中,我们有一个没有任何标签或人工注释的数据集。我们的目标是学习数据中结构的一些有趣信息。

例如,我们可能会发现这个数据集包含几个聚类,即数据中存在彼此更相似的不同点群,找出它们会很有用。也许我们有一些具有某些相似性的个体群体,例如相似的生理测量值,这可能意味着他们有相似的疾病或健康状况。我们可以通过检查数据的结构来发现这一点,这对于后续可视化结构也很有用。或者,可能存在一些不寻常、有趣的数据点或错误,我们希望识别这些数据点。无监督学习的另一个应用是,如果我们有一个被噪声掩盖的信号,但知道该信号具有某种有趣的结构,我们可以尝试从噪声中恢复它。例如,尝试在嘈杂的房间或电话中恢复人类语音。

这是一个无监督学习能做什么的小例子。我将使用Scikit-learn库数据集模块中的函数加载另一个名为Iris(鸢尾花)的数据集。在这个可视化中,每个数据点对应一朵花,X轴和Y轴分别代表以厘米为单位的萼片宽度和萼片长度。仅观察这个数据集,你已经可以看到它具有某种结构:这些点似乎彼此更相似,而这些点则不同。

无监督学习算法可用于提取数据中的这种结构。具体来说,我们将尝试学习该数据的概率密度,并使用一个在本课程中也会看到的算法,称为高斯混合模型。我们假设数据由三个我们试图学习的成分组成,但我们只告诉算法有三个成分,然后看看学习数据会发生什么。实际上,它首先发现这里有两个组:这一组和这一组,这是我们通过检查数据已经看到的。然后它进一步指出,这一组可以分解为两个更小的子组,由这里的两个红点表示。

有趣的是,这个数据集实际上是有标签的。我之前没有使用它们,但现在也可以加载它们,并用不同的颜色显示每朵花的标签。我们看到,数据中确实存在三个类别:第一个类别对应这个聚类,所有这些蓝色的花实际上来自同一个鸢尾花亚种,称为山鸢尾。而这组点实际上也包含两种类型的花,这里用黄色和棕色标记,分别称为变色鸢尾和维吉尼亚鸢尾。这两种花彼此更相似,但通过无监督学习,我们能够发现这两个点对应这两个类别的中心。确实,黄色的花往往更多地出现在这个组的这一侧,而棕色的花则更多地出现在另一侧。在没有查看标签的情况下,仅通过观察数据的结构,我们就能够识别出这三组花,尽管我们不知道它们具体是什么。

无监督学习也有很多实际应用。例如,被称为推荐系统的系统,如Netflix上的电影推荐系统,其某些组件或某些版本可以是无监督的,或者至少更复杂的生产系统包含一些无监督组件。我们还可以将无监督学习用于异常检测,例如识别错误、噪声或与正常状态截然不同的数据点(如可能发生故障的工厂组件)。我之前提到的信号去噪是无监督学习的另一个有用应用。

强化学习 🎮

最后一种机器学习类型,也是一种非常有趣的方法,称为强化学习。在强化学习中,我们有一个略有不同的设置。这里有一个智能体在某个环境中行动。这个智能体基于其内部状态决定采取特定行动。它将行动发送给环境,环境以某种方式发生变化。然后,根据发生的情况,智能体的状态发生变化。随着状态变化,智能体还会观察到在特定状态下采取特定行动所获得的奖励。这个智能体的目标是随时间最大化奖励。

让我给你一个更具体的例子:想象一个试图执行某项任务的机器人,比如清洁厨房桌面的一部分。如果机器人执行涉及清洁的任务,它会获得奖励(我们说“好机器人”);如果机器人做了不相关的事情,比如打碎了一个玻璃杯,我们会给它一个负奖励(我们说“坏机器人,停下”)。从这类奖励中学习的智能体和算法被称为强化学习智能体,这是我描述的第三种机器学习方法。

这种方法也有很多实际应用。例如,玩国际象棋或围棋等棋盘游戏的智能体,最先进的智能体是使用强化学习构建的。强化学习在工业中也有很多实际应用,例如数据中心的冷却系统。我们可以使用强化学习来设计何时开启或关闭冷却的策略,以保持数据中心处于最佳性能,同时不产生过多成本。

强化学习与常规监督学习略有不同,不仅因为这种奖励结构,还因为这些状态。在强化学习中,智能体常常会遇到一些能带来即时奖励的局部最优决策,但这些决策可能导致其进入奖励不佳的状态。智能体可能会陷入某种不良状态。因此,强化学习比其他类型的机器学习更具挑战性,因为我们必须进行这种长期规划和探索,以确保不会陷入任何不良状态。此外,导致我们进入良好状态的决策可能不会立即显现其好结果,好的结果可能在未来更远的时间才能观察到,这使得学习智能体的良好策略变得更加困难。

人工智能与深度学习 🤖

最后,我想在本视频中区分的两个相关领域是人工智能和深度学习。我们经常在机器学习的相同语境中听到它们,但它们并不完全相同。

首先,人工智能是一个普遍的学术和应用领域,关注于构建模仿人类智能行为的系统。人工智能的最终目标(也称为通用人工智能或强人工智能)是构建表现得像人类一样智能的实体。但实现这一目标还有许多子目标,例如逻辑推理或规划。

机器学习是人工智能的一个子集,专注于构建能够从经验中学习的机器。这是智能系统的一个特征,但不是唯一特征。例如,逻辑可能是智能系统的一个属性,不一定与学习相关;逻辑推理也可能与学习无关。尽管如此,机器学习被认为是构建人工智能系统的一个非常重要的工具,也被认为是实现那个长期目标最有希望的方法。

深度学习是机器学习的一个子集,专注于一种称为神经网络的特定模型,其灵感大致来源于大脑的工作原理。这类模型在今天非常成功,可以解决许多重要的实际问题,如图像识别、语音识别、机器翻译。在这些任务上,深度学习模型取得了非常令人印象深刻的结果,这就是为什么深度学习是当今一个特别有趣和令人兴奋的应用领域。

总结 📝

本节课中,我们一起学习了机器学习的三种主要方法:监督学习、无监督学习和强化学习。监督学习使用带标签的数据训练模型进行预测;无监督学习旨在发现未标记数据中的内在结构;强化学习则让智能体通过与环境交互并获得奖励来学习最优策略。我们还简要区分了人工智能、机器学习和深度学习之间的关系。理解这些基本方法为我们后续深入学习具体的算法和应用奠定了坚实的基础。

1.3:关于本课程 📚

在本节课中,我们将要学习本课程的具体安排、教学方式以及你将学到什么。我们将了解课程为何采用“应用”与“机器学习”相结合的方法,并预览课程中将使用的工具和独特的可执行教学材料。

欢迎回来。在之前的视频中,我们了解了什么是机器学习,并探讨了处理机器学习问题的不同方法,例如监督学习、强化学习和无监督学习。在本部分,我想更多地介绍课程本身,我们将学习什么内容,如何学习,以及为何采用这种特定的方式。

课程方法:理论与实践并重

首先,我想说明我们将如何呈现将要学习的各种材料。

本课程名为“应用机器学习”,它包含两个部分:“应用”和“机器学习”。它是一门机器学习课程,意味着我们将学习来自机器学习不同子领域的广泛工具,并了解每个机器学习领域的核心算法。因此,你将学习到一个非常广泛、通用且适用于极广范围问题的通用工具包。有些课程倾向于只专注于一个子领域,例如仅专注于深度学习。在本课程中,我们也会学习深度学习,但同时也会学习许多其他被广泛使用、非常重要且对机器学习至关重要的算法。

本课程的另一部分是“应用”部分。我们将非常强调应用。我们不仅会定义算法是什么,还会通过实现它们或在练习、示例中应用它们来展示它们如何工作。稍后我将给出几个例子来说明这是如何运作的。

为何采用此方法?

那么,为何采用这种特定的方法?一个非常实际的原因是,应用机器学习非常有用,这是一项非常重要且备受重视的技能。它之所以受重视,是因为机器学习可以在不同的实际领域产生巨大影响。这是一个非常实际的原因。

另一个原因是,定义算法和应用算法之间常常存在差距。你可能知道如何在纸上写出算法,但使其在实践中工作是另一项技能。理解两者都非常重要,弥合这一差距是我们在这里要尝试做的事情。

最后,一个更偏向教学法的原因是,为了理解特定算法的工作原理,最好的方法通常是实现该算法,或者至少研究其实现。因此,通过专注于应用,我们也能更好地理解这些算法是如何工作的。

课程内容概览

现在,我们将要学习哪些内容?

我们将要学习的第一件事是广泛的算法,同样来自机器学习的不同子领域。这些通常是我们需要了解的最重要的算法。我们将使用数学语言和标准符号来定义它们。

然后,我们还将学习如何从头开始实现这些算法,或者使用机器学习库来实现,以及如何将它们应用于计算机视觉、语言、医学分析等各个领域的问题。

本课程另一个特有的方面,是学习如何应对和解决实践中的机器学习问题,这在标准的机器学习课程中通常不涉及。例如,你可能将某种算法(如深度神经网络)应用于一个问题,但一旦有了第一个实现,接下来该做什么往往并不明确。你是尝试改变算法的内部结构,尝试不同的算法,尝试获取更多数据,还是尝试其他方法?在实践中部署这些算法需要一种实际的问题解决方法,这也是我们将在课程中探讨的内容。

课程使用的工具

在整个课程中,我们将使用许多标准的机器学习软件包。这些软件包都基于 Python 编程语言。其中最重要的一些包括:

  • Scikit-learn:一个实现了许多非常流行和标准的核心机器学习算法的库。
  • 深度学习库:如 TensorFlow、Keras 和 PyTorch。
  • 核心线性代数库:所有这些库都建立在核心线性代数模块之上,例如 NumPy。
  • Pandas:这是一个更偏向数据处理的库。我们的许多工作也将涉及这个库。

我认为,能够看到如何使用这些库来实现这些算法将非常有价值。

独特的可执行教学材料

本课程一个非常有趣的部分,也是我个人非常兴奋的一点,是拥有可执行的课程材料。你在前面的幻灯片中已经看到了几个例子。

其理念是,我将通过这些幻灯片展示的所有材料,实际上都是可执行的笔记本。因此,我将向你展示可以直接在幻灯片中运行的代码,并且它实际上是在我电脑的浏览器中运行的。

我们将使用这些代码来演示机器学习中的不同概念。因此,我们将使用数学符号展示概念,然后你将看到如何从头实现它,或者如何通过现有库使用它。你在这些幻灯片或我将与你分享的材料中看到的所有代码,都可以下载到你的电脑上,你可以在本地运行所有内容,并复现我将在幻灯片或其他材料中展示的所有结果和图表。

为了更好地理解这意味着什么,让我们看一个例子。这是一个单元格的例子,这个单元格包含 Python 代码。这里我只是导入了一些标准库:NumPy、Matplotlib、Scikit-learn。我可以将此单元格作为幻灯片的一部分执行,现在这段代码就在我录制此视频时在我的浏览器中运行了。

为了让你了解这可能如何使用,让我们看另一个例子,或者作为这个例子的后续,我可以使用刚刚加载的这些库来加载数据。这里我使用 Scikit-learn 的 datasets 模块来加载一个手写数字数据集。

这里我使用了一点 Python 代码在屏幕上显示四个数字。这是一个包含 8x8 像素手写数字图像的玩具数据集,这里我刚刚向你展示了四个例子,来自这个数据集的四个数字。

这是一种我们可以使用可执行幻灯片来演示机器学习算法如何工作的方式。在这些幻灯片中,我现在将实时训练一个机器学习算法。这里我导入一个神经网络分类器,并准备训练它。

通过这行代码来识别这些数字并根据它们的标签进行分类。我现在将执行这个幻灯片。当然,这是一个小型玩具数据集。所以,在刚刚过去的一秒钟里,这段代码执行了,我刚刚在我的笔记本电脑上训练了这个机器学习模型。

现在,如果我向下滚动,我可以显示这些结果。我现在正在执行这段代码。对于数据中的这四个随机样本,模型已经在我浏览器中实时生成了预测。这里它说这是 9,这是 4。这些有点难以区分,但这个它说是 6。这个我认为是 8,不太清楚,也许是 9。这些都是分辨率非常低、非常玩具化的图像。

但你在这里看到的是一种实时演示算法、演示机器学习算法如何被导入和使用的方式。我刚刚实时使用它来对这个数据集进行分类。这是本课程真正令人兴奋的事情之一,我希望你会发现它很有用。

总结

本节课中,我们一起学习了《应用机器学习》课程的整体框架。我们了解了课程将采用理论与实践并重的方法,涵盖从核心算法到实际应用的广泛内容。我们预览了课程中将使用的 Python 生态系统工具,如 Scikit-learn、TensorFlow 和 NumPy。最重要的是,我们介绍了本课程独特的可执行教学材料,它将允许我们实时运行代码、演示算法并加深理解。在接下来的课程中,我们将开始深入探索这些激动人心的机器学习概念和工具。

1.4:课程安排与其他信息 📚

在本节课中,我们将学习康奈尔大学《应用机器学习》课程的具体安排、课程结构、先修要求以及其他重要信息。通过了解这些内容,你可以更好地准备并参与本课程的学习。


课程形式:翻转课堂 🔄

上一节我们介绍了课程的整体目标,本节中我们来看看课程的具体形式。本课程将采用“翻转课堂”的形式进行。

翻转课堂的核心思想是:课程的主要内容和材料以预录形式单独提供。具体到本课程,这些材料就是你正在观看的讲座视频和幻灯片。

课堂面授时间将主要用于问答、讨论问题、解答疑问以及进行实践教程。这是面授环节的重点。因此,你需要提前观看视频并复习材料,以便熟悉内容,从而能够积极参与课堂讨论并提出问题。


课程内容结构 📊

本课程内容大致分为25个讲座,并划分为四个主要部分。

以下是课程内容的四个主要模块:

  1. 机器学习算法:课程的一个重要部分是学习来自机器学习不同领域的算法。这包括监督学习算法无监督学习算法,我们也会简要提及强化学习。大部分讲座将专注于理解和解释这些算法。
  2. 机器学习基础:除了定义算法,我们还需要理解这些算法在实践中为何有效。机器学习背后有深刻的理论,它使用统计学语言和各种数学技术来证明,当给定特定类型和数量的数据时,机器学习算法确实有效。我们不会深入探讨理论和数学证明,但会从高层次解释机器学习算法有效的原因,同样重要的是,它们何时会失效以及有何局限性。
  3. 机器学习实践应用:理解了机器学习的基础,我们就能实现列表中的第三点:理解如何在实践中有效应用机器学习。我们将用大约四到五个讲座来探讨机器学习的实际应用方面。在学习了核心算法之后,我们将学习如何通过理解算法有效和失效的原因,来诊断机器学习模型的性能。我们还将探讨如何利用这些知识来理解为何某个算法在特定问题上可能表现不佳,以及解决问题的流程是什么,从而使其工作得更好,甚至榨取算法的最大性能。这将涉及诸如误差分析、理解过拟合与欠拟合、跟踪性能指标、测量算法性能等主题。
  4. 高级专题与嘉宾讲座:课程的最后部分将涵盖一些高级主题。我们还将邀请客座讲师,分享他们在其专业领域内特定机器学习主题的见解和讲座。

先修要求 📝

本课程的先修要求比较通用。

以下是主要的先修条件:

  • 编程经验:具备基本的编程经验。理想情况下,这应该是在任何编程语言中至少有一年的编程经验,但最好是Python。大量材料将以Python代码形式呈现,因此熟悉Python对你将非常有帮助。
  • 数学基础:机器学习也使用数学语言定义。两个最重要的数学领域是线性代数概率论。为了充分理解本课程材料,具备大学水平的线性代数和概率论背景将非常有用。这里所说的大学水平,是指学习过并熟悉本科阶段线性代数和概率论入门课程所涵盖的内容。

如果你不熟悉所有这些主题,我们也会分享材料并进行教程,帮助你快速掌握其中一些主题。或者,如果你忘记了某些内容,希望这些材料能作为复习资料,对你有所帮助。


其他课程细节 ℹ️

最后,关于课程的一些其他细节。

  • 课程网站:我们将使用课程网站来分享材料。康奈尔学生将有一个托管在特定平台上的课程网站(链接将另行分享)。同时,也会有一个面向公众的课程版本,用于存放这些课程材料。具体网址未在幻灯片中列出,因为它们可能随时间变化,但会通过适当渠道分享。我们将使用这些网站进行讨论、发布作业等。
  • 评分方式:本课程的评分将是作业课程项目的结合。请查阅网站了解课程项目的具体信息。基本上,会有四次作业。课程项目将涉及实现一个机器学习算法来解决你感兴趣的问题,我们会分享项目想法。我们也会分享关于该项目要求的具体细节,但本质上,它是一篇5到8页长的论文,在课程结束时提交。
  • 教材:本课程没有指定必修教材。但如果你想从其他来源阅读相关材料,我个人推荐Hastie、Tibshirani和Friedman合著的教材《The Elements of Statistical Learning》。这是一本优秀的教材,涵盖了本课程将涉及的所有内容甚至更多,我强烈推荐。

总结与展望 ✨

本节课中,我们一起学习了《应用机器学习》课程的具体安排。我们了解了课程将采用翻转课堂的形式,明确了课程内容分为算法、基础、实践和高级专题四个主要模块。我们还讨论了学习本课程所需的编程和数学先修知识,并介绍了课程网站、评分方式和推荐教材等后勤细节。

最后,我想再次分享我对教授这门课程的兴奋之情。我认为机器学习是一个极其迷人的研究领域,我个人非常高兴能创作这些讲座。我希望你能像我一样享受这些学习材料。

感谢你的参与。

2.1:第一部分:监督机器学习问题

概述

在本节课中,我们将深入学习监督机器学习。我们将通过一个具体的医学预测示例,详细拆解监督学习算法的核心组成部分,包括数据集、算法和预测模型。我们将使用简单的线性回归作为例子,让初学者能够清晰地理解整个过程。


回顾与引入

上一讲我们介绍了什么是机器学习,探讨了其不同应用领域,并理解了机器学习为何是解决实际问题的有趣方法。

我们还了解到构建机器学习算法主要有三种方法:监督学习无监督学习强化学习,并分别看到了这三种方法的示例。

在本讲中,我们将从这三种方法中的第一种——监督学习——开始,深入探讨机器学习的核心算法和技术思想。我们的目标是更详细地解释什么是机器学习,并提供基本术语和符号,以便我们能够开始定义新的研究,并在下一讲中学习具体的机器学习算法。


监督学习定义回顾

首先,让我们回顾上一讲中给出的监督学习的简要定义。

监督学习是一种设置,我们试图通过给计算机一个带有标签的训练示例数据集,来教会它执行特定任务。例如,在物体检测系统中,这些标签可以是给定图像中内容的标注。在特斯拉的一个演示中,我们看到了行人和车辆的标注。

除了这个数据集,我们还指定一个学习算法,模型将通过该算法推断出什么构成了行人,什么构成了车辆,并能在新图像上准确预测出行人和车辆。


深入示例:预测糖尿病风险

在本视频中,我想深入探讨另一个监督学习的例子,并利用这个例子展示构成一个监督学习算法的不同组件。

我将使用一个贯穿本视频及后续几个视频的连续示例:一个预测糖尿病风险的医学示例。

假设我们有一个糖尿病患者的数据库。这个数据集来自一家医院,我们拥有患者的医疗记录,其中包含各种数据,包括生理测量值、患者的年龄和性别。

除此之外,我们还拥有一个糖尿病风险评分。我们的目标是从这些数据中预测糖尿病风险。更广泛地说,我们可能还想了解医疗记录中的不同变量和数据如何影响这些患者的糖尿病风险。


监督机器学习问题的三个重要组成部分

首先,我想强调监督机器学习问题的三个重要组成部分。

正如前面提到的,监督学习方法包括提供一个数据集和一个相对简短的学习算法。如果机器遵循这个算法,它将能够从数据集中提取所需的知识,以执行有用的任务,如预测或其他数据分析任务。这与更传统的软件工程方法形成对比,在传统方法中,我们会尝试通过一组规则或详细指令来手动指定计算机应执行的行为。

为了使内容更具体,一个监督学习算法接收一个数据集,并输出一个预测模型。我们将在本视频的其余部分看到监督学习数据集的样子、算法的样子以及预测模型的样子。


1. 监督学习数据集

在我们的预测糖尿病风险示例中,我们将使用机器学习中一个非常常见的数据集,称为UCI糖尿病数据集。这是一个“玩具”数据集,因为它相对较小,广泛用于机器学习教学。

具体来说,在这个数据集中,正如我提到的,它有不同的生理测量值。我们特别感兴趣的测量值是身体质量指数

对于每个患者,我们将拥有他们的身体质量指数,并且我们还将获得一个糖尿病风险评分,这是一个从0到300的数字,表示一个人患糖尿病的风险程度。

我们使用这个数据集的目标是理解这两个变量之间的关系。

以下是加载和可视化数据集的代码示例:

import sklearn
import numpy as np
import pandas as pd

# 加载数据
from sklearn.datasets import load_diabetes
data = load_diabetes()
X = data.data[:, np.newaxis, 2]  # 取BMI特征
y = data.target

# 可视化前25个数据点
import matplotlib.pyplot as plt
plt.scatter(X[:25], y[:25])
plt.xlabel('BMI')
plt.ylabel('Diabetes Risk')
plt.show()

通过可视化,我们可以立即看到数据中的一些结构:身体质量指数较高的患者(图中右侧)往往具有较高的糖尿病风险评分。这表明数据中存在我们可以利用的模式。


2. 学习算法与模型假设

一旦我们有了数据集,我们就想为预测糖尿病风险的任务定义一个算法。定义监督学习算法的第一步是假设身体质量指数和糖尿病风险之间的关系。

我们首先假设风险是身体质量指数的一个线性函数。换句话说,对于一组未知的参数,风险 y 和身体质量指数 x 之间存在线性关系。

这个关系可以用以下公式表示:

y = θ₁ * x + θ₀

这只是一个简单的线性方程,一条具有斜率和截距的直线。我们首先假设存在某条直线可以解释身体质量指数和糖尿病风险之间的关系。

这里有一些技术定义:斜率 θ₁ 和截距 θ₀ 我们将称为这个方程的参数x输入或自变量,y目标或因变量。


3. 拟合模型与获得预测

假设 xy 之间存在这种关系,监督学习算法的目标是以与我们所拥有数据一致的方式学习参数 θ

我们将看到许多学习这些参数的方法。为了给出一个例子,我将使用 scikit-learn 库中的一个算法作为“黑盒”来获取模型的一组参数。

以下是使用线性回归算法拟合模型的代码:

from sklearn.linear_model import LinearRegression

# 创建并拟合模型
model = LinearRegression()
model.fit(X[:25], y[:25])  # 使用前25个点训练

# 获取学习到的参数(斜率和截距)
theta_1_star = model.coef_[0]
theta_0_star = model.intercept_
print(f"斜率 θ₁*: {theta_1_star}")
print(f"截距 θ₀*: {theta_0_star}")

运行后,算法可能会告诉我们,对于这个数据集,理想的斜率约为37,截距约为-800。通过固定这些参数值 θ₁*θ₀*,我们定义了我之前提到的预测模型 f*

现在,预测模型 f* 只是 x 的函数,它输出目标 y 的估计值。

让我们可视化由 scikit-learn 算法给出的、具有我们刚看到的斜率和截距的预测模型:

# 绘制数据点和拟合的直线
plt.scatter(X[:25], y[:25], label='Training Data')
plt.plot(X[:25], model.predict(X[:25]), color='black', label='Learned Model')
plt.xlabel('BMI')
plt.ylabel('Diabetes Risk')
plt.legend()
plt.show()

正如你所见,这条黑线确实拟合了我们在数据中观察到的线性趋势。它不能完美地拟合每个示例,但它捕捉到了总体趋势。直观地说,如果我们要根据数据猜测这个区域的糖尿病风险,我们可能会猜测在200左右,而不是100左右。因此,这个模型确实为我们提供了对糖尿病风险一些直观有效的估计。


4. 使用模型进行预测

既然我们有了一个预测模型,我们就可以开始使用它来对数据进行预测。

例如,如果我们有一个新的数据点,一个新患者 x‘,我们知道他的身体质量指数,那么我们可以将这个 x‘ 代入我们参数已知的预测模型中,这将给我们一个输出 y‘,即我们对这位新患者糖尿病风险的估计。

让我们看看这在我们已有的数据集上是什么样子。假设我们收集了三个新患者的数据点:

# 假设的新数据点 (BMI值)
new_patients_bmi = np.array([[-0.05], [0.05], [0.15]])
# 使用模型进行预测
predictions = model.predict(new_patients_bmi)

# 可视化
plt.scatter(X[:25], y[:25], label='Training Data')
plt.plot(X[:25], model.predict(X[:25]), color='black', label='Learned Model')
plt.scatter(new_patients_bmi, predictions, color='red', marker='x', s=100, label='Predictions for New Patients')
plt.scatter(new_patients_bmi, [y[26], y[27], y[28]], color='red', label='Actual New Patients') # 假设这些是新数据的真实值
plt.xlabel('BMI')
plt.ylabel('Diabetes Risk')
plt.legend()
plt.show()

在图中,黑线是我们学习到的模型。红点是新患者的身体质量指数值。红叉显示了我们的模型为这些患者提供的预测。

你可以看到,对于左边的患者,我们的预测几乎完全正确。对于另外两个例子,我们有些高估了他们的风险,这是因为这条线只是对身体质量指数和糖尿病风险之间真实关系的一个近似。

此外,我们可能无法完美地学习这种关系,因为这里本质上存在一些随机性。即使我们拥有大量数据,知道患者的身体质量指数,也可能无法直接预测他们的糖尿病风险,因为本质上还有其他因素在起作用,这些因素在数据中表现为噪声。

红叉是这些患者的预测值,请注意,这只是我们看到的红点在线上的对应值。虽然存在一些误差,但总体上我们在正确的范围内。


总结

在本节课中,我们一起学习了监督机器学习的基本流程。我们能够使用一个简单的监督学习算法,结合糖尿病患者的数据库,来学习一个模型——一个简单的线性模型,该模型仍然可以用于预测新患者的糖尿病风险。

更广泛地说,监督学习方法非常有用,因为它可以为我们提供一套强大的工具,用于对新的、未见过的数据进行预测。它在这个简单的例子中有效,但这种方法可以非常复杂,并且可以在更复杂的场景中产生非常强大的模型,例如自动驾驶汽车或更复杂的医疗场景。

此外,我们还可以使用这个模型来回答关于数据的更一般性问题,例如不同变量之间关系的结构。在我们的糖尿病玩具示例中,我们的模型本质上证实了我们的直觉:糖尿病风险随着身体质量指数的增加而增加。这种对数据中变量关系的分析是我们可以使用监督学习进行的另一种分析类型。

正如我所提到的,监督学习是我们在糖尿病和自动驾驶汽车背景下看到的一个例子,但这是一种广泛使用且非常强大的方法,在包括机器翻译在内的许多其他领域都有应用。例如,在机器翻译中,我们会给计算机提供许多不同语言中相同句子的示例对。监督学习在医疗领域、自动驾驶汽车、工业应用、语音识别等许多其他领域都有广泛应用。

希望这个分析对你有用,你能够对监督学习算法是什么样子获得一些直觉。在接下来的系列视频中,我们将更深入地探讨监督学习问题的每个组成部分——数据、算法、预测模型——并使用数学符号更精确地定义它们。在后续的讲座中,我们将看到许多监督机器学习算法的例子以及它们的工作原理。

2.2:监督机器学习剖析:数据集 📊

在本节课中,我们将深入学习监督机器学习问题的核心组成部分,特别是数据集。我们将使用数学符号和定义来精确描述数据集的构成,这对于后续理解各种机器学习算法至关重要。

在上一节中,我们介绍了什么是监督学习,并看到了一个简单的监督学习问题及其算法的运行示例。本节中,我们将更深入地探讨监督学习问题的不同组成部分,并重点聚焦于数据集。

监督学习的三要素

回顾之前的视频,一个监督学习问题通常包含以下三个核心部分:

  1. 数据集
  2. 算法
  3. 预测模型

例如,如果我们为自动驾驶汽车构建一个物体检测系统,我们需要指定一个数据集(其中包含算法应遵循的行为信号)和一个学习算法。当计算机在数据上执行该算法时,它会学习数据的结构并输出一个预测模型,该模型可用于预测未来遇到的数据实例。

深入理解数据集

我们将继续使用UCI糖尿病数据集作为贯穿本节的示例。该数据集包含多名患者的记录,每条记录包含多项测量值,例如生理指标。之前我们重点关注了BMI,但实际上该数据集中还有其他有用的测量值。

以下是加载和查看该数据集描述的代码:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import datasets

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/f2e09eba317b8eb9a1c7ebabb2294634_13.png)

# 加载糖尿病数据集
diabetes = datasets.load_diabetes()
print(diabetes.DESCR)

数据集描述显示,总共有442名患者,每名患者有10项测量值,包括年龄、性别、BMI、血压等。

数据集的数学定义

为了用数学语言精确定义后续的算法,我们需要更形式化地定义数据集。

我们将一个数据集 D 定义为一个包含 n 个训练样本的数学集合:
D = {(x⁽¹⁾, y⁽¹⁾), (x⁽²⁾, y⁽²⁾), ..., (x⁽ⁿ⁾, y⁽ⁿ⁾)}

其中:

  • 上标 i 是样本索引,并非指数运算。
  • 每个元组 (x⁽ⁱ⁾, y⁽ⁱ⁾) 称为一个训练样本
  • y⁽ⁱ⁾ 称为目标值。在我们的例子中,这就是糖尿病风险。
  • x⁽ⁱ⁾ 称为输入输入对象。这通常是我们在进行预测前拥有的患者数据(各项测量值)。

机器学习算法通过学习大量“输入-正确目标”的配对样本,来学会从 x 预测 y

我们可以查看糖尿病数据集的具体数值,它已被分为输入数据(diabetes.data)和目标值(diabetes.target)两部分。输入数据是一个数据框,每一行代表一名患者,每一列代表一项测量值。某些数值(如年龄出现负值)是数据预处理(如归一化)的结果,这是为了让算法更容易处理数据,属于常见的技术操作。

输入对象:属性与特征

现在,让我们更详细地了解输入 x,并引入相关符号。

我们假设每个输入对象 x⁽ⁱ⁾ 是一个 d 维向量:
x⁽ⁱ⁾ = [x₁, x₂, ..., x_d]ᵀ ∈ 𝒳

其中:

  • 向量中的每个标量值对应一项测量(如年龄、BMI)。
  • 𝒳 称为特征空间,通常是 d 维实数空间 ℝᵈ 或其子集。

向量中的每一项通常被称为属性,例如患者的年龄、性别、BMI。这些是我们拥有的关于患者的不同信息片段,将用于进行预测。

另一个相关但略有区别的重要概念是特征。有时,原始属性可能不足以做出有效预测,或者对算法来说难以处理。例如,我们可能认为“老年男性”患糖尿病的风险特别高。这就需要我们结合“性别”和“年龄”两个属性,创建一个新的特征“是否为老年男性”。

以下是创建“老年男性”特征的代码示例:

# 假设 df 是包含‘sex’和‘age’列的数据框
df[‘old_man‘] = (df[‘sex‘] > 0) & (df[‘age‘] > threshold_value)

通过创建丰富而有意义的特征,我们可以极大地提升机器学习算法的效能。虽然某些算法理论上能从数据中自动学习这种组合关系,但通常由于算法结构限制或数据不足,自定义特征对于算法成功至关重要。

特征的形式化定义

我们使用一个特征函数 φ 来形式化地定义特征提取过程:
φ: 𝒳 → ℝᵖ
φ(x) = [φ₁(x), φ₂(x), ..., φ_p(x)]ᵀ

该函数将原始输入对象 x 映射到一个新的 p 维向量。p 可能与原始维度 d 不同。这个新向量的每个分量就是一个特征(例如,“是否为老年男性”、“是否肥胖”)。函数 φ 的输出结果称为特征化后的输入

需要说明的是,在实践中,“特征”和“属性”这两个术语经常互换使用。当上下文清晰时,我们通常直接称 x 为特征向量。

特征的类型:连续与离散

特征可以分为两种重要类型:连续特征和离散特征。某些算法可能对其中一种类型处理得更好。

  • 连续特征:可以在实数区间内取任意值。例如,BMI特征就是一个连续特征,其直方图呈钟形曲线分布。
  • 离散(分类)特征:只能取有限个可能值中的一个。例如,性别特征(在数据集中编码为两个数值)就是一个离散特征,其直方图仅显示两个柱状。

目标变量:回归与分类

现在,让我们更深入地讨论目标变量 y。每个 y 称为目标,其取值属于目标空间 𝒴。在我们的例子中,𝒴 就是糖尿病风险值的集合。

目标变量 y 同样可以是连续或离散的,这定义了两种根本不同的监督学习问题:

  1. 回归问题:目标变量 y 是连续的。我们的目标是拟合一条曲线(或超平面)来近似 xy 之间的关系。预测糖尿病风险就是一个回归问题。
  2. 分类问题:目标变量 y 是离散的。每个取值代表一个类别。我们的目标是在特征空间中找到一个决策边界,将不同类别的样本分开。对于一个新输入,根据其落在边界的哪一侧来预测其类别。

我们可以通过设定阈值,将回归问题转化为分类问题。例如,将糖尿病风险高于150的定义为“高风险”,否则为“低风险”。以下是使用逻辑回归算法(尽管名字叫“回归”,但它是一种分类算法)在这个二分类数据集上进行训练和预测的示例:

from sklearn.linear_model import LogisticRegression

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/f2e09eba317b8eb9a1c7ebabb2294634_47.png)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/f2e09eba317b8eb9a1c7ebabb2294634_48.png)

# 假设 X 是特征, y_binary 是二值化(高风险/低风险)后的标签
model = LogisticRegression()
model.fit(X, y_binary)
predictions = model.predict(X)

算法在大多数样本上都能正确分类,尽管存在少数错误。这展示了如何训练一个分类模型。

总结

本节课中,我们一起深入探讨了监督机器学习中的数据集组件。我们学习了:

  • 数据集的数学定义:D = {(x⁽ⁱ⁾, y⁽ⁱ⁾)},包含输入 x 和目标 y
  • 输入对象 x 作为向量,其分量可以是属性或人工构造的特征,并引入了特征函数 φ 的形式化描述。
  • 特征的两大类型:连续特征离散特征
  • 目标变量的两大类型所定义的核心问题:回归问题(连续目标)与分类问题(离散目标)。

在下一节中,我们将把目光转向监督学习问题的另一个核心组成部分:学习算法。

2.3:监督学习的解剖:学习算法 🧠

在本节课中,我们将学习监督机器学习算法的核心组成部分。我们将定义模型类、目标函数和优化器这三个关键元素,并使用数学符号和代码示例来具体说明它们是如何协同工作的。

在之前的课程中,我们定义了监督机器学习问题的结构。我们收集一个数据集来指定算法的期望行为,然后通过一个学习算法处理数据,最终输出一个预测模型。这个模型旨在未来新的数据集上实现良好的预测性能。

上一节我们介绍了监督学习数据集的结构和表示方法。本节中,我们将转向学习算法本身,并为其建立一套数学符号体系,以便在后续课程中定义各种监督学习算法。

监督学习算法的三大组件

一个监督机器学习算法在最高层次上,完全由以下三个重要组件定义:

  • 模型类:这是算法可能输出的所有预测模型的集合。模型类定义了我们可以考虑的所有可能的函数形式。
  • 目标函数:这个函数用于量化一个特定模型对数据集的拟合程度。我们通常希望找到能使目标函数值最优(例如最小化)的模型。
  • 优化器:这是一个程序或算法,用于在模型类中搜索并找到那个能优化(通常是极小化)目标函数的模型。

因此,优化器在给定模型类和目标函数的情况下运行,最终输出一个预测模型,这就是我们算法的结果。

组件详解

为了更具体地定义这些组件,我们将以糖尿病数据集作为贯穿始终的例子。该数据集包含患者的各项生理指标(如年龄、性别、BMI、血压等)以及其糖尿病风险。

1. 模型与模型类

预测模型是算法最终输出的结果。我们可以将其视为一个将输入 x 映射到目标 y 的函数。

在糖尿病例子中,这个函数接收患者的特征,并输出其糖尿病风险值。模型通常带有参数,我们用符号 θ 表示。参数存在于某个集合 Θ 中。一个具有特定参数 θ 的模型,我们记作 f_θ

模型类是我们考虑的所有可能函数 f: X → Y 的一个子集。如果模型带有参数,我们可以通过有效参数集合来定义模型类。模型类 F 可以表示为所有具有参数 θ ∈ Θ 的模型 f_θ 的集合。

示例:线性模型
机器学习中一个非常常见的方法是假设 yx 之间存在线性关系。即,目标 y 通过以下线性方程与输入 x 和参数 θ 相关:

y = θ_0 + θ_1 * x_1 + θ_2 * x_2 + ... + θ_d * x_d

这里,为了得到 y,我们取每个属性 x_j,乘以对应的参数 θ_j,然后将所有结果相加。所有 θ_j 构成一个 d 维的参数向量 θ,与 d 维的输入 x 结合以进行预测。

2. 目标函数

给定一个模型类,我们需要一种方法来衡量哪个模型对数据拟合得好,哪个拟合得差。为此,我们定义了目标函数,它通常也被称为损失函数,因为当模型拟合不佳时,它的值往往很大;拟合良好时,值则很小。

目标函数是一个在模型集合上定义的函数。给定一个模型 f,它给出一个评分。我们通常希望最小化这个目标函数。如果 f 由参数 θ 参数化,我们可以将目标 J 直接定义为参数 θ 的函数:J(θ)

以下是两个常用的目标函数示例:

  • 均方误差:这是监督学习中最广泛使用的目标之一。其定义是预测值与真实目标值之差的平方的平均值。公式为:
    MSE = (1/n) * Σ (y_i - f_θ(x_i))^2
    当这个值很小时,意味着模型的预测与真实目标非常接近。
  • L1误差(平均绝对误差):我们直接计算模型预测值与真实目标标签之间的绝对差值的平均值。公式为:
    MAE = (1/n) * Σ |y_i - f_θ(x_i)|
    同样,我们希望最小化这个差值。

代码示例

from sklearn.metrics import mean_squared_error, mean_absolute_error

y_true = [3, -0.5, 2, 7]
y_pred = [2.5, 0.0, 2, 8]

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/ba1d10f894103b5243dd5f5d9047ea27_20.png)

mse = mean_squared_error(y_true, y_pred)  # 输出:0.375
mae = mean_absolute_error(y_true, y_pred) # 输出:0.5

3. 优化器

定义算法所缺失的最后一个组件是优化器。优化器是一个解决以下最小化问题的程序:在模型类 F 中,找到能使目标函数 J 的值最小的模型 f

直观地说,优化器寻找在训练数据上具有最佳拟合度的模型。如果 F 由参数 θ 参数化,那么这个优化问题就转化为在所有可能的 θ 值上最小化函数 J(θ)

代码示例
在Scikit-learn库中,LinearRegression 类内部使用了一个优化算法(基于最小二乘法)来解决均方误差最小化问题。

from sklearn.linear_model import LinearRegression

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/ba1d10f894103b5243dd5f5d9047ea27_28.png)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/ba1d10f894103b5243dd5f5d9047ea27_30.png)

# 假设 X_train, y_train 是我们的训练数据
model = LinearRegression()  # 定义模型类(线性模型)和优化器
model.fit(X_train, y_train) # 执行优化,找到最优参数θ

当调用 fit 方法时,它解决了我们之前幻灯片上的优化问题,结果是一个训练好的模型,可用于进行预测。

完整算法示例

现在,我们可以将这三个组件组合起来,在糖尿病数据集上运行一个完整的学习算法。

# 1. 准备数据(模型类、目标函数和优化器的输入)
X_train = ... # 训练集特征
y_train = ... # 训练集目标值
X_test = ...  # 测试集特征(新患者)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/ba1d10f894103b5243dd5f5d9047ea27_34.png)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/ba1d10f894103b5243dd5f5d9047ea27_35.png)

# 2. 定义并运行学习算法(包含三大组件)
from sklearn.linear_model import LinearRegression
algorithm = LinearRegression() # 实例化算法(内含模型类、MSE目标、优化器)
algorithm.fit(X_train, y_train) # 优化器运行,找到最优模型

# 3. 使用输出的预测模型进行预测
predictions_train = algorithm.predict(X_train)
predictions_test = algorithm.predict(X_test)

通过可视化,我们可以看到模型在训练数据上的拟合情况,以及对新患者数据(测试集)的预测。与仅使用单一特征(如BMI)的简单模型相比,使用所有特征的多元线性模型通常能输出更接近真实值的预测,从而获得更高的准确性。

总结

本节课中,我们一起学习了监督机器学习算法的核心解剖结构。我们了解到,一个监督学习问题由数据集和算法组成。而算法本身又包含三个基本组件:

  1. 模型类:定义了可能的预测函数集合。
  2. 目标函数:用于评估模型对数据拟合好坏的量化标准。
  3. 优化器:在模型类中搜索并找到能优化目标函数的模型的程序。

这三个组件共同构成了一个完整的学习算法。在接下来的课程中,我们将看到第一个非玩具性质的完整算法实例,并从第一性原理出发推导出所有这些组件。

3.1:优化与微积分 📈

在本节课中,我们将学习优化和微积分的基础知识,这些知识对于定义和理解机器学习算法至关重要。我们将从回顾监督学习算法的结构开始,然后深入探讨导数、梯度以及梯度下降算法。这些概念将帮助我们构建第一个监督学习算法——线性回归。

监督学习算法结构回顾

在上一讲中,我们详细探讨了监督学习问题的结构及其算法组成部分。本节中,我们将简要回顾这些组成部分,为后续内容奠定基础。

监督学习算法的核心结构包括三个部分:模型类、目标函数和优化器。

  • 模型类:这是我们考虑的所有可能模型的集合。例如,所有线性模型的集合。
  • 目标函数:这是一个函数,用于评估模型类中某个模型对数据的拟合程度。
  • 优化器:这是一个算法,用于根据目标函数的指示,从模型类中选择一个能很好拟合数据的模型。

更正式地说,优化器的任务可以表述为:在模型类 F 中,寻找一个模型 f,使得目标函数 J(f) 的值最小(即损失最低)。一个常见的目标函数例子是均方误差(MSE),它衡量模型预测值 f(x) 与实际目标值 y 之间的差异。

微积分基础:导数

为了定义后续的优化算法,我们需要回顾微积分中的一个关键概念——导数。

导数可以直观地理解为函数在某一点的切线斜率,或者函数在该点的瞬时变化率。对于函数 f(θ),其在点 θ₀ 处的导数记为 df/dθ|_(θ=θ₀)

一个重要的性质是:函数在其最小值点处的导数为零。这直观上是因为在最低点,函数的瞬时变化率为零,切线是水平的。

从导数到梯度

我们即将处理的函数通常比简单的二次函数复杂得多,它们往往是多变量函数。对于多变量函数,我们需要将导数的概念推广到偏导数梯度

偏导数 ∂f/∂θⱼ 是固定其他所有变量后,函数相对于第 j 个变量的导数。

梯度 ∇f(θ) 是一个向量,其每个分量是函数相对于对应输入变量的偏导数。梯度指向函数在该点处增长最快的方向。相反,负梯度(-∇f(θ))则指向函数下降最快的方向

此外,梯度向量总是垂直于函数在该点的等高线(即函数值相同的点构成的曲线)。

核心优化算法:梯度下降 🚀

基于梯度的直观理解,我们可以定义机器学习中一个极其重要的优化算法——梯度下降

梯度下降算法的直觉非常简单:为了找到函数的最小值,我们从某个初始点开始,然后反复沿着负梯度方向(即函数下降最快的方向)移动一小步,直到接近最小值点。

梯度下降的更新规则可以用以下公式表示:
θ^(i+1) = θ^(i) - α * ∇J(θ^(i))
其中:

  • θ^(i) 是第 i 次迭代的参数值。
  • α学习率(或步长),决定了每次更新移动的幅度。
  • ∇J(θ^(i)) 是目标函数 J 在点 θ^(i) 处的梯度。

以下是梯度下降算法的伪代码实现:

初始化参数 θ
设置学习率 α
设置收敛阈值 ε

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/9949a8982856d3528863331614605a3f_45.png)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/9949a8982856d3528863331614605a3f_47.png)

while 未收敛:
    计算当前点的梯度 g = ∇J(θ)
    更新参数:θ = θ - α * g
    检查是否收敛(例如,检查参数变化或梯度范数是否小于 ε)

学习率 α 的选择至关重要:

  • α 太小:收敛速度非常慢,可能需要很多步才能到达最小值。
  • α 太大:可能导致更新步伐过大,在最小值附近震荡甚至发散,无法收敛。
  • α 合适:能够以合理的步数稳定地收敛到最小值。

总结

本节课中,我们一起学习了优化与微积分的基础知识,这些是理解机器学习算法的基石。我们回顾了监督学习算法的结构,重温了导数的概念,并将其推广到多变量函数的梯度。最后,我们深入探讨了梯度下降算法的原理、公式实现以及学习率对优化过程的关键影响。在接下来的课程中,我们将应用梯度下降来构建我们的第一个监督学习算法——线性回归。

3.2:梯度下降法 🧠

在本节课中,我们将学习如何使用梯度下降法来定义我们的第一个监督学习算法——线性模型。我们将从回顾梯度下降的基本原理开始,然后逐步构建一个完整的线性回归模型,并使用梯度下降法对其进行优化。最后,我们将在一个真实数据集上应用这个算法。


梯度下降法回顾 📉

上一节我们介绍了监督学习的基本框架。本节中,我们来看看如何利用梯度下降法来优化模型。

梯度下降法是一种迭代优化算法,用于最小化目标函数 J。其核心更新规则如下:

公式: θ_i = θ_{i-1} - α * ∇J(θ_{i-1})

为了应用此规则,我们首先对函数 J 的最小值进行一个初始猜测 θ₀。然后,我们根据上述规则反复更新对 θ 的猜测。规则中的关键元素是梯度 ∇J。梯度乘以 -1 后,指向函数 J 在当前点 θ_{i-1} 处最陡峭的下降方向。通过沿此方向移动一个步长 α,我们更新对最小值的估计。如果步长 α 选择得当,新估计值 θ_i 处的函数值将小于前一次迭代的值。

以下是梯度下降法的一个简单伪代码实现:

while J(θ_i) < J(θ_{i-1}):
    θ_i = θ_{i-1} - α * ∇J(θ_{i-1})

只要函数值在连续两次猜测之间持续减小,算法就会根据梯度下降更新规则继续优化。


定义学习算法 🧩

现在,我们利用梯度下降法来构建一个学习算法。定义一个学习算法需要三个部分:模型族、目标函数和优化器(此处即梯度下降法)。

模型族:线性模型

我们首先从模型族开始。对于此示例,我们将使用之前见过的简单线性模型族。

线性模型具有以下形式。我们假设目标 y 与输入 x 之间的关系是线性的,具体形式如下。我们取 d 维特征向量 x 的每个特征,并假设 y 是这些特征的加权和,权重由模型参数 θ 给出。

公式: y ≈ θ₀ + θ₁*x₁ + θ₂*x₂ + ... + θ_d*x_d

这是我们在监督学习开始时看到的第一个示例的扩展。当时我们看的是具有特定斜率和截距的直线模型,而此形式将其推广到了多维输入。我们仍然有一个截距 θ₀,但现在每个特征都有自己由 θ 参数给出的斜率。

为了简化这个形式,我们可以给每个输入添加一个虚拟特征,其索引为 0。这个特征的特殊之处在于其值处处为 1。

公式: x₀ = 1,则模型可写为:y ≈ Σ_{j=0}^{d} θ_j * x_j

使用向量表示法,我们可以更紧凑地将其写为 θx 的点积(内积)。

公式: f_θ(x) = θᵀ x

这个函数不仅在数学上易于定义,在 Python 中也易于实现。

def f_theta(x, theta):
    return np.dot(x, theta)

目标函数:均方误差

定义监督学习算法所需的剩余组件是目标函数。对于目标函数,我们将使用之前见过的均方误差。

回忆一下,均方误差是模型在数据集中每个训练样本上所做预测的误差平方的平均值(或总和)。我们查看线性模型预测的目标估计值 f_θ(x) 与实际目标值 y 之间的差异。如果这两个值彼此接近,则此目标函数或损失函数的值就小;如果模型预测值与我们认为它应该预测的值之间存在较大差异,则此值就大。

公式: J(θ) = (1/2n) * Σ_{i=1}^{n} (f_θ(x⁽ⁱ⁾) - y⁽ⁱ⁾)²

我们寻找能在整个数据集上实现最佳平均误差总和的 θ

我们同样可以轻松地实现它。

def mean_squared_error(theta, X, y):
    predictions = np.dot(X, theta)
    return np.mean((predictions - y) ** 2) / 2

计算梯度

为了使用梯度下降法优化均方误差,我们需要获得其梯度的表达式。

函数的梯度是其偏导数的向量。因此,为了获得梯度,我们需要计算此目标函数相对于某个参数 θ_j 的偏导数形式。

幸运的是,这个表达式非常简单。由于这是一个二次函数,根据微积分法则,其偏导数具有以下特定形式。

公式: ∂J/∂θ_j = (1/n) * Σ_{i=1}^{n} (f_θ(x⁽ⁱ⁾) - y⁽ⁱ⁾) * x_j⁽ⁱ⁾

我们可以组合这些偏导数以获得梯度的表达式。

公式: ∇J(θ) = (1/n) * Xᵀ (Xθ - y)

这是一个简单的表达式,在 NumPy 中,我们可以用一行代码定义它。

def gradient(theta, X, y):
    n = len(y)
    predictions = np.dot(X, theta)
    return (1/n) * np.dot(X.T, predictions - y)


在真实数据集上应用 🏥

现在,让我们在线性模型上对一个真实数据集应用梯度下降法。我们将再次使用之前见过的糖尿病数据集。

该数据集包含许多患者的数据,对于每位患者,我们有各种测量值。我们将使用的指标是身体质量指数。除了 BMI,我们还有他们的糖尿病风险评分,这是一个大致在 0 到 300 之间(或略高)的数字。我们有兴趣训练一个线性模型,根据一个人的 BMI 来预测其糖尿病风险。

以下是该数据集的散点图,包含了为此示例选择的 20 个数据点。可以看到数据点之间存在线性趋势,这意味着尝试拟合线性模型是合理的。

实现算法

梯度下降法反复应用前述更新规则。这里,我们用之前看到的梯度定义实例化了梯度下降法。我们只需将之前的梯度下降定义与梯度表达式结合即可。

现在,我们有了一个完整的监督学习算法:我们指定了一个模型,指定了一个目标函数,并在此指定了如何使用优化算法(即梯度下降法)来获得此模型的最佳参数。该算法通常被称为最小二乘法或 Widrow-Hoff 学习规则,这是机器学习中一个非常简单但非常重要且具有实际意义的算法。

让我们现在实现这个算法。

def gradient_descent(X, y, theta_init, alpha, max_iters):
    theta = theta_init
    for i in range(max_iters):
        grad = gradient(theta, X, y)
        theta = theta - alpha * grad
        # 可选:每100次迭代记录或打印损失值
        if i % 100 == 0:
            loss = mean_squared_error(theta, X, y)
            print(f"Iteration {i}: Loss = {loss}")
    return theta

当我们运行此优化过程时,随着迭代次数增加,目标函数的值不断下降,直到大致稳定在某个值。

现在,我们可以可视化这个函数,它看起来像下面这条线。我们取优化序列中的最后一个点(即最终参数集),并用它来计算估计值——本质上是绘制由优化过程最后一组参数指定的直线。图中显示的蓝色直线确实是对我们数据集的拟合,它大致匹配了我们之前看到的线性趋势。

至此,我们已成功使用梯度下降法将监督学习应用于此问题,优化了一个线性模型,并获得了我们的第一个监督学习算法。


总结 ✨

本节课中,我们一起学习了如何将梯度下降法应用于线性回归模型,从而构建一个完整的监督学习算法。我们回顾了梯度下降的原理,定义了线性模型和均方误差目标函数,推导并计算了梯度,最后在一个真实的糖尿病数据集上实现了算法并进行了可视化验证。在接下来的课程中,我们将探讨这种方法的扩展,并了解使用优化算法拟合线性模型的其他几种方式。

3.3:普通最小二乘法 📊

在本节课中,我们将学习一种新的线性模型拟合方法——普通最小二乘法。这是一种比梯度下降更实用的方法,通过求解正规方程来直接找到最优参数。

概述

上一节我们介绍了使用梯度下降法学习线性模型。本节中,我们将探讨一种更高效的方法——普通最小二乘法。这种方法通过求解一组线性方程(正规方程)来直接找到最小化均方误差的参数,无需迭代调整学习率。

设计矩阵与向量化表示

为了定义普通最小二乘法算法,我们首先需要引入一些新的数学符号。

设计矩阵

设计矩阵是一种方便表示训练数据集的方式。每个训练样本的输入是一个D维向量,设计矩阵就是将所有样本的输入向量按行堆叠起来。

设计矩阵的定义
设我们有n个训练样本,每个样本的输入特征为D维向量。设计矩阵X是一个n×D的矩阵,其中第i行对应第i个样本的特征向量。

代码表示

# X 是一个 n x D 的矩阵
# 每一行是一个样本的特征向量
X = np.array([
    [x_1^1, x_1^2, ..., x_1^D],
    [x_2^1, x_2^2, ..., x_2^D],
    ...,
    [x_n^1, x_n^2, ..., x_n^D]
])

目标向量

类似地,我们将所有训练样本的目标值(标签)堆叠成一个列向量。

目标向量的定义
y是一个n维列向量,其中第i个元素是第i个样本的目标值。

代码表示

# y 是一个 n 维列向量
y = np.array([y_1, y_2, ..., y_n])

均方误差的向量化表示

现在,我们可以使用矩阵表示法来重写均方误差(MSE)目标函数。

均方误差的原始定义
均方误差是预测值与真实值之间平方差的平均值。

向量化表示
使用设计矩阵和目标向量,我们可以将均方误差写成更紧凑的形式:

公式

J(θ) = (1/n) * ||Xθ - y||²

其中:

  • X是设计矩阵(n×D)
  • θ是参数向量(D×1)
  • y是目标向量(n×1)
  • ||·||表示欧几里得范数(向量的长度)

这种表示等价于原始的均方误差定义,但使用了更简洁的矩阵运算。

推导梯度并求解最优参数

有了均方误差的向量化表示,我们可以推导其梯度表达式。

梯度推导

通过矩阵微积分运算,我们可以得到均方误差关于参数θ的梯度:

公式

∇J(θ) = (2/n) * Xᵀ(Xθ - y)

这个梯度表达式告诉我们目标函数在任意参数点θ处的变化方向。

利用梯度为零的条件

在函数的最小值点,梯度必须为零。由于均方误差是二次函数,它只有一个最小值点。

设置梯度为零

0 = (2/n) * Xᵀ(Xθ - y)

简化后得到正规方程

XᵀXθ = Xᵀy

求解正规方程

正规方程是一组线性方程,我们可以直接求解参数θ:

最优参数的闭式解

θ* = (XᵀX)⁻¹Xᵀy

这个公式给出了最小化均方误差的最优参数。需要注意的是,矩阵XᵀX必须是可逆的,这在大多数实际应用中都能满足。

在糖尿病数据集上的应用

让我们将普通最小二乘法应用于UCI糖尿病数据集,该数据集包含患者的身体测量指标(如BMI)和糖尿病风险评分。

数据准备

以下是加载和准备数据的步骤:

代码示例

import numpy as np

# 加载糖尿病数据集(示例)
# X_train: 训练特征矩阵 (n_samples x n_features)
# y_train: 训练目标向量 (n_samples,)

# 添加偏置项(截距)
X_train_bias = np.c_[np.ones((X_train.shape[0], 1)), X_train]

应用普通最小二乘法

使用推导出的公式计算最优参数:

代码实现

# 计算最优参数
theta_best = np.linalg.inv(X_train_bias.T @ X_train_bias) @ X_train_bias.T @ y_train

进行预测

使用学习到的参数对新样本进行预测:

预测代码

# 对新数据点进行预测
X_new_bias = np.c_[np.ones((X_new.shape[0], 1)), X_new]
y_pred = X_new_bias @ theta_best

算法总结

以下是普通最小二乘法算法的关键点总结:

算法特性

  • 类型:监督学习算法
  • 适用问题:回归问题(目标变量连续)
  • 模型类型:线性模型
  • 优化目标:最小化均方误差
  • 优化方法:求解正规方程

优点

  1. 无需调整学习率等超参数
  2. 计算效率高(直接求解线性方程组)
  3. 有闭式解,保证找到全局最优

局限性

  1. 需要计算矩阵逆,当特征维度很高时计算成本高
  2. 要求XᵀX矩阵可逆
  3. 对异常值敏感

与梯度下降法的比较

以下是两种方法的对比:

计算复杂度

  • 普通最小二乘法:O(D³),其中D是特征数量
  • 梯度下降法:O(k·D),其中k是迭代次数

适用场景

  • 当特征数量较少(D < 10000)时,普通最小二乘法通常更快
  • 当特征数量非常大时,梯度下降法可能更合适
  • 当数据无法完全装入内存时,需要使用随机梯度下降等变体

实际应用注意事项

在实际应用普通最小二乘法时,需要考虑以下问题:

处理不可逆矩阵

如果XᵀX不可逆,可以采取以下措施:

解决方案

  1. 检查特征之间是否存在多重共线性
  2. 使用正则化(如岭回归)
  3. 使用伪逆代替逆矩阵

代码示例

# 使用伪逆处理不可逆情况
theta_best = np.linalg.pinv(X_train_bias.T @ X_train_bias) @ X_train_bias.T @ y_train

数值稳定性

为了提高数值稳定性,建议:

最佳实践

  1. 对特征进行标准化
  2. 使用QR分解等数值稳定的方法求解
  3. 添加小的正则化项确保矩阵可逆

总结

本节课中我们一起学习了普通最小二乘法——我们的第一个非玩具监督学习算法。我们:

  1. 引入了设计矩阵和目标向量的表示法,使数学表达更简洁
  2. 推导了均方误差的向量化形式,便于矩阵运算
  3. 计算了梯度并设置其为零,得到了正规方程
  4. 求解正规方程得到了参数的最优闭式解
  5. 在糖尿病数据集上实现了算法并进行了预测
  6. 分析了算法的优缺点及其与梯度下降法的比较

普通最小二乘法是线性回归的基础,也是许多更复杂算法的起点。在后续课程中,我们将以此为基础,探讨正则化、非线性扩展等更高级的主题。

关键公式回顾

  • 设计矩阵:X ∈ ℝⁿˣᵈ
  • 目标向量:y ∈ ℝⁿ
  • 均方误差:J(θ) = (1/n)||Xθ - y||²
  • 最优参数:θ* = (XᵀX)⁻¹Xᵀy

这个算法在实际中被广泛使用,例如scikit-learn中的线性回归模型在背后就实现了这些计算。掌握普通最小二乘法为我们理解更复杂的机器学习算法奠定了坚实基础。

3.4:非线性最小二乘法 🧮

在本节课中,我们将学习如何将之前介绍的线性最小二乘法扩展到非线性模型。我们将重点介绍一种称为“非线性最小二乘法”的技术,它允许我们使用非线性特征来拟合数据,同时仍然利用线性模型的优化框架。


多项式函数简介 📈

上一节我们介绍了梯度下降及其在监督学习算法中的应用。本节中,我们来看看如何将线性模型扩展到非线性关系。

多项式函数是形式如下的函数:

[
f(x) = \theta_0 + \theta_1 x + \theta_2 x^2 + \dots + \theta_p x^p
]

以下是多项式函数的几个例子:

  • 二次函数:( f(x) = x^2 )
  • 三次函数:( f(x) = x^3 )
  • 更复杂的多项式:( f(x) = x^3 + 2x^2 + x + 1 )

多项式函数具有很强的表达能力,能够拟合数据中复杂的非线性关系。


通过特征映射拟合多项式 🛠️

现在,我们将展示如何利用普通最小二乘法的工具来拟合任意复杂度的多项式。

考虑一个一维回归场景,输入属性 ( x ) 是标量。我们定义一个特征映射函数 ( \phi(x) ):

[
\phi(x) = [1, x, x^2, \dots, xp]T
]

这个函数将一维属性映射到一个 ( p+1 ) 维的空间。

现在,考虑基于这组特征的线性模型类。一个由参数 ( \theta ) 参数化的模型实例具有以下形式:

[
f(x; \theta) = \theta^T \phi(x) = \theta_0 + \theta_1 x + \theta_2 x^2 + \dots + \theta_p x^p
]

这正是我们之前定义的多项式。因此,这个模型类本质上包含了所有 ( p ) 次多项式。

关于这个模型类,有几个重要的观察结果:

  1. 对输入 ( x ) 是非线性的:函数 ( \phi(x) ) 本身是非线性的,这使我们能够建模数据中复杂的非线性关系。
  2. 对参数 ( \theta ) 是线性的:模型 ( f(x; \theta) ) 是参数 ( \theta ) 的线性函数。因此,它本质上仍然是一个线性模型。

这意味着我们可以继续使用之前学到的关于最小二乘法和优化线性模型的所有知识,来高效地优化这个模型类。


实例:用多项式拟合糖尿病数据 🩺

为了让概念更具体,让我们用UCI糖尿病数据集的一个例子来说明。我们将再次使用BMI来预测糖尿病风险评分。

以下是使用多项式特征进行线性回归的步骤:

  1. 计算非线性特征:我们从一个只包含BMI特征的设计矩阵 ( X ) 开始。然后,我们创建包含最高3次多项式特征的新设计矩阵。

    # 假设 X 是原始BMI数据(一维)
    X_poly = np.column_stack([np.ones_like(X), X, X**2, X**3])
    

    结果是一个包含四列的设计矩阵:常数项、BMI、BMI² 和 BMI³。

  2. 应用正规方程:我们可以直接使用之前推导的正规方程来求解最优参数 ( \theta )。

    theta = np.linalg.inv(X_poly.T @ X_poly) @ X_poly.T @ y
    

  1. 进行预测和可视化:对于新的 ( x ) 值,我们先计算其多项式特征,然后用学到的 ( \theta ) 进行预测。

    x_new_poly = np.column_stack([np.ones_like(x_new), x_new, x_new**2, x_new**3])
    y_pred = x_new_poly @ theta
    

与简单的线性模型相比,这个三次多项式模型能够更好地捕捉数据中BMI与糖尿病风险之间的非线性关系,尤其是在低BMI区域趋势平缓,而在高BMI区域增长更快的模式。


扩展到多元多项式 🌐

到目前为止,我们只定义了一维变量的多项式特征。但同样的方法可以扩展到任意维度的多项式。

例如,一个二元二次多项式的一般形式为:

[
f(x_1, x_2) = \theta_{00} + \theta_{10}x_1 + \theta_{01}x_2 + \theta_{20}x_1^2 + \theta_{02}x_2^2 + \theta_{11}x_1 x_2
]

为了捕捉所有二次项,我们可以定义如下的特征映射函数:

[
\phi(x_1, x_2) = [1, x_1, x_2, x_1^2, x_2^2, x_1 x_2]^T
]

这个函数将二维输入 ( (x_1, x_2) ) 映射到六维特征空间。同样的原理可以推广到具有任意数量变量和任意次数的多项式。


更一般的非线性特征 🌀

我们采用的方法实际上更为通用。原则上,只要我们的模型在参数 ( \theta ) 上是线性的,我们就可以将任何形式的非线性特征与模型结合使用,并仍然能够应用普通最小二乘法的工具来优化和寻找最佳拟合。

以下是其他非线性函数的例子,它们也可以用作特征:

  • 正弦函数:( \sin(x) )
  • 余弦函数:( \cos(x) )
  • 组合函数:( \cos(x) + \sin(2x) + \cos(4x) )

这些函数可以产生复杂的形状,用于拟合数据中可能遇到的各种关系。我们可以设计的函数种类几乎是无限的。


总结 📝

本节课中,我们一起学习了如何将普通最小二乘法扩展为非线性最小二乘法。这种算法是回归和监督学习的一个实例。

其核心思想是:

  • 模型族在参数上仍然是线性的,这使我们能利用高效的线性优化工具(如正规方程)。
  • 但我们使用了输入属性的非线性函数作为特征(如多项式、三角函数等),这使得模型能够表达和拟合数据中复杂的非线性关系。

这些模型通常通过最小化均方误差的正规方程进行优化。至此,我们已经看到了几个真实世界的机器学习算法。在接下来的课程中,我们将探讨更多算法,并开始更深入地理解这些算法为何有效。

4.1:数据分布 📊

在本节课中,我们将暂时转换视角,探讨一个与之前略有不同的主题。我们将利用这节课简要讨论监督学习基础的一些方面。具体来说,我们将探究监督学习算法为何有效、何时有效,以及为何有时会失效。然后,我们将利用这些直觉来开发一套新的学习算法。

在前几讲中,我们定义了监督学习,并看到了监督学习算法的初步示例。现在,在本讲中,我们希望获得更多关于它们为何有效的直觉。这将是本讲前半部分的内容。在本讲后半部分,我们将利用这些直觉来改进我们已经见过的算法,以解决我们将在本讲开头识别出的问题。

为何监督学习有效?🤔

在第一个视频中,我们将再次建立一些数学基础,以便在下一个视频中回答这个问题。具体来说,我将定义一个称为“数据分布”的数学概念。这个概念将有助于:第一,解释算法为何有时有效、有时无效;第二,在本讲及后续课程中定义一套新的学习算法。

接下来,我将描述一个关于数据来源的特定假设。如果我们做出这个假设,就能更好地解释,甚至证明在某些情况下学习算法有效,而在其他情况下无效。

数据分布的概念 📈

回顾我们之前的课程,一个监督机器学习问题包含以下组成部分:为了应用监督机器学习,我们必须创建一个训练数据集,它是一组带标签的输入 X 和目标标签 Y。然后,我们指定一个学习算法。当该算法在训练数据集上执行时,其输出是一个预测模型,我们可以用它来对未来进行预测。

我之前将数据集视为给定的。现在,我想探讨这个数据集可能来自何处。我将做出一个特定的假设。这个假设在实践中可能成立,也可能不成立。算法可能有效,也可能无效。即使这个假设不完全成立,算法可能仍然有效。事实上,这个假设无法完全验证。但如果我们做出这个假设,我们就可以证明它们有效,并且也能获得关于它们为何无效的直觉。

你很快就会明白我的意思。现在,让我介绍这个我想引入的数学概念。

数据分布的定义 📊

这个数学原理被称为“数据分布”。通常在机器学习中,我们假设我们拥有的数据来自某种概率分布。我们称这个概率分布为“数据分布”,并用大写字母 P 表示这个概率。我们使用特定的符号 (X, Y) ~ P 来表示 XY 是根据这个概率分布的。

这可以是 XY 上的任意分布。例如,在医疗环境中,我们观察来到医院的病人,你可以考虑所有可能病人的集合,其中一些病人出现的可能性更大,一些更小。我们假设看到某个特定病人的数据存在一定的概率,而我们观察到的病人是从这个未知的概率分布中抽取的。

具体来说,我们的训练集具有以下形式:我们说它由来自数据分布 P独立同分布样本组成,简称 IID

再次强调,这是机器学习中通常做出的一个假设。算法有效并非必须满足此假设。但我们可以用它来证明,当此假设满足时,算法在特定情况下会有效。我们也可以用它来获得关于算法为何在实践中无效的直觉,并利用这个分布来开发更好、改进的学习算法。

理解“独立同分布” 🔄

这个定义中我尚未彻底定义的关键部分是“独立同分布”。本质上,这意味着每个训练样本都来自同一个分布,即“同分布”部分。因此,每个样本都是数据分布 P 的一个样本。同时,当我们收集一个特定的训练样本时,它不依赖于之前收集的样本。也就是说,如果我们收集第 N 个训练样本,并且之前已经收集了一个数据集,那么这个新数据独立于我们之前碰巧收集到的数据。

让我们看一个例子。一个简单的例子是抛硬币。每次抛硬币正面或反面朝上的概率相同,并且硬币正面或反面朝上的结果不依赖于之前抛掷的结果。

一个反例是像年度人口普查数据这样的东西。想象我们测量一个城市随时间变化的人口。人口的变化本质上是一个表现为随机的未知量。如果城市以某个数量增长,我们很难预测将有多少新人口迁入或有多少人口出生。这本质上是一个我们可以视为随机的未知量。因此,一个城市随时间变化的人口可以被视为来自一个随机概率分布的样本,但这些样本是相关的,因为特定年份的城市人口将密切依赖于并关联于前一年的城市人口。由于这些样本密切相关,它们不是独立的。因此,这个例子违反了独立性假设。

创建并理解数据分布 🛠️

为了更好地理解数据分布,我们也可以创建自己的数据分布。现在我将定义一个特定的数据分布,并从中抽取样本。我将在 NumPy 中实现这一点。第一步是定义一个函数,我将围绕这个函数收集样本。

我将使用 NumPy 定义以下函数:

import numpy as np

def true_function(x):
    return np.sin(2 * np.pi * x) + 0.3 * np.cos(9 * 2 * np.pi * x)

我们可以将其可视化,它看起来如下所示。考虑以下函数,这显然是一个非线性函数。

现在,考虑以下生成数据的过程。我可以从数据分布中抽取样本,步骤如下:

首先,我在这个函数的定义域内生成一个随机的 x。我将在 0 到 1 之间均匀随机地选择一个 x,这是该函数的定义域。

然后,我将通过查看该函数的真实值并添加一些噪声来生成目标 y。这将是围绕该函数的高斯或正态分布噪声(白噪声)。它的均值为零,因此我只是随机扰动该函数在此输入 X 处的值。

我可以在 NumPy 中实现如下。这里我生成 30 个样本,生成 30 个随机的 x,然后为了生成 Y,我计算真实函数的值,然后加上随机噪声。我是否按某个小值进行了缩放?然后这就定义了 Y 的集合。

我可以将这些 xy 可视化如下,这些是蓝点。

现在,蓝色数据集(蓝点集)是我们从数据分布生成的机器学习数据集。该数据分布由我们刚刚描述的过程组成:我们取一个 X,然后生成一个该函数的带噪声实现值。

现在这是一个有效的机器学习数据集,我们可以应用之前见过的学习算法来学习 XY 之间的关系。我们可以尝试从这些噪声数据中学习这个函数的真实形状。但再次强调,在这个例子中,它受我们刚刚定义的概率分布支配。

为何假设数据来自分布? 🤷

现在,我想再次总结一下,为什么我们希望假设我们正在处理的数据集是从一个分布中采样的?

首先,在许多情况下,事实上在机器学习的大多数实际应用中,我们试图建模的过程本质上表现得好像其中存在随机性。通常,数据收集的方式存在不确定性和随机性。例如,数据本身可能是使用有噪声的仪器收集的。例如,我们可能使用一个只有一定精度的温度计,而我们收集的数据是围绕某个真实值的随机样本,我们无法收集到该真实值,因为我们的数据收集过程中存在一些误差。我们的数据收集过程中存在一些固有的噪声,因此我们拥有的数据不是确定性的,它具有一定的随机性,我们希望做出这个建模假设。

通常,我们试图建模的过程本身也存在不确定性。例如,如果我们试图预测某只股票的价格,那么这个价格是由大量我们无法纳入系统的因素决定的。它可能取决于特定交易者在特定日子的情绪,这些是我们无法测量的因素。我们只有有限数量的特征可以使用。因此,出于设计系统的目的,这个股票价格本质上表现为一个随机量。它表现为一个具有某些信号的随机量,其中一部分我们可以预测,但还有很大一部分是随机的,我们无法预测。

因此,通过概率分布来建模通常是准确、现实且反映实际情况的。

最后,第三个也是最相关的原因是,如果我们做出我之前所做的假设,那么我们就可以使用概率和统计学的工具来分析监督学习算法,并证明它们有效。如果它们在实践中无效,我们可以利用从数据分布中获得的直觉来解释它们为何无效。是的,我们也可以使用分布的语言来定义模型准确意味着什么。这是我想在下一个视频中探讨的内容。


本节课中我们一起学习了数据分布的概念,它是理解监督学习算法为何有效的基础。我们定义了数据分布 (X, Y) ~ P 和独立同分布样本,并通过一个函数加噪声的例子直观地理解了数据生成过程。最后,我们探讨了假设数据来自概率分布的现实意义和理论价值,为后续分析算法的有效性奠定了基础。

4.2:为什么监督学习有效? 🤔

在本节课中,我们将探讨监督学习算法在实践中能够有效工作的原因。我们将基于之前介绍的数据分布概念,定义什么是“好的”机器学习模型,并解释为什么在特定假设下,监督学习能够产生泛化能力强的模型。


数据分布回顾 📊

上一节我们介绍了数据分布的概念。它是一个数学假设,即我们处理的数据(特别是训练数据集中的X和Y)是从某个概率分布P中独立同分布(IID)采样得到的。

公式表示:训练数据 ( D = {(x_i, y_i)}_{i=1}^n ) 中的每个样本对 ((x_i, y_i)) 都独立地来自同一个分布 ( P )。


什么是好的模型? ✅

假设我们已经运行了一个监督学习算法,并得到了一个预测模型。如何判断它是否是一个好模型?这个模型必须做出准确的预测,但关键在于,这些预测必须对我们在训练时未曾见过的新数据也保持准确。最终,我们的目标是将模型部署到现实世界中,让它处理新的输入数据并做出准确预测。理解训练数据与模型未来将遇到的数据之间的区别,对于理解机器学习至关重要。


留出数据集的概念 📈

为了使上述直觉更精确,我们引入“留出数据集”的概念。除了训练数据集,我们定义另一个具有相同结构的数据集,它也由来自同一分布P的IID样本对 ((x, y)) 组成,但与训练集互斥。我们用带点的符号(例如 (\dot{x}, \dot{y}))来区分留出数据集。

核心思想:一个机器学习模型要表现良好,不仅需要很好地拟合训练数据(蓝色点),还需要能够很好地拟合留出数据(红色点)。后者是衡量模型准确性的真正标准。


模型准确性的形式化定义 🎯

我们假设有一个阈值来定义什么是“准确”的预测。对于回归问题,可能是预测值 (\hat{y}) 与真实值 (y) 足够接近;对于分类问题,可能是预测标签正确。具体定义方式取决于问题本身。

我们可以形式化地定义模型的准确性:一个预测模型 (f) 是准确的,当它在随机留出样本上犯错的概率很小。

公式表示
[
P_{(x, y) \sim P}[\text{accurate}(f(x), y) = \text{False}] \leq \epsilon
]
其中 (\epsilon) 是一个很小的数(例如0.01或1%)。这意味着,如果我们从分布P中采样大量留出数据点,模型在其中做出错误预测的比例不超过 (\epsilon)。

反之,如果模型在留出数据上犯错的概率至少为 (\epsilon),则认为它是不准确的。


泛化能力 🌉

模型在不同于训练集的新数据上取得良好性能的特性,被称为泛化。我们希望监督学习方法返回的模型具有良好的泛化能力。


为什么监督学习能返回泛化好的模型? 🛠️

监督学习的高级流程如下:

  1. 收集带标签的训练数据集 (D)。
  2. 运行学习算法,找到一个在训练数据集上拟合良好的模型。

我们声称,如果训练数据集 (D) 足够大,这个流程将返回一个泛化能力好的模型。下面简要说明原因(基于两个简化假设):

  1. 有限模型类:我们工作的模型类 (M) 中最多包含 (H) 个不同的模型。
  2. 完美拟合假设:模型类 (M) 中至少有一个模型能完美拟合训练数据(即对每个训练样本都预测准确),并且学习算法会选择这个模型。

论证思路

  • 假设监督学习返回的模型 (f) 是不准确的(即泛化差)。
  • 由于 (f) 被选中,它必须完美拟合了所有 (n) 个训练样本。
  • 因为 (f) 不准确,它在任何一个随机样本上预测准确的概率最多为 (1 - \epsilon)。
  • 因此,(f) 完美拟合所有 (n) 个独立训练样本的概率最多是 ((1 - \epsilon)^n)。
  • 考虑到模型类中最多有 (H) 个不准确的模型,那么任何一个不准确模型完美拟合训练数据的概率最多是 (H \cdot (1 - \epsilon)^n)。

随着训练样本数量 (n) 的增加,概率 (H \cdot (1 - \epsilon)^n) 会以指数速度趋近于0。这意味着,只要我们有足够多的数据,监督学习算法返回一个不准确(即泛化差)的模型的概率将变得极低。

结论:在数据量足够大的情况下,监督学习算法返回的模型几乎肯定能很好地泛化到新数据。


总结 📝

本节课我们一起学习了:

  1. 数据分布是理解监督学习的基础假设。
  2. 好的模型需要在未见过的留出数据上表现准确,这引出了泛化的核心概念。
  3. 我们形式化定义了模型的准确性,即模型在留出数据上犯错的概率应低于一个阈值 (\epsilon)。
  4. 通过一个简化的理论论证,我们说明了为什么在拥有足够多训练数据且模型类有限的情况下,监督学习能够返回泛化能力强的模型。

在下一节中,我们将利用本节定义的准确性和泛化概念,来分析当数据量不足或其他条件不满足时,监督学习可能遇到的问题和失败模式。

4.3:过拟合与欠拟合 📊

在本节课中,我们将探讨监督学习可能失效的几个原因。上一节我们定义了模型准确性的含义,并概述了监督学习为何能高概率输出准确模型。本节我们将从相反的角度,审视监督学习的几种失败模式,并探讨如何缓解这些问题。

泛化:模型准确性的关键 🔑

在机器学习中,泛化是一个核心概念。模型要准确,必须能够泛化。具体来说,我们假设数据由一个概率分布 P 描述,我们的训练数据是从该分布中抽取的一组样本。我们还有另一组独立的样本,称为留出集。如果模型能够泛化,意味着它在留出集上也是准确的,这保证了模型在部署到现实世界、面对与训练数据不同的新数据时,也能提供准确的预测。

多项式回归示例 📈

为了理解泛化,让我们回顾本课程早期介绍的一个模型:多项式回归。在一维情况下,我们拟合一条从 xy 的曲线。多项式回归模型的形式如下:

y = w_0 + w_1 * x + w_2 * x^2 + ... + w_d * x^d

其中,d 是多项式的最高次数。通过使用最小二乘法,我们可以拟合这种形式的非线性模型。

使用多项式模型的原因在于,相比线性模型,它们能更好地拟合数据,从而提高模型准确性。

模型表达能力与拟合效果 🎯

让我们通过一个具体例子来观察。假设我们有一条真实曲线(蓝色)和一组从该曲线采样的训练数据(蓝色点)。

以下是拟合不同次数多项式模型的结果:

  • 一次多项式(直线):模型过于简单,无法捕捉数据的真实趋势,拟合效果差。
  • 二次多项式:模型开始能够捕捉数据的主要弯曲趋势,拟合效果显著改善。
  • 三次多项式:模型能够非常紧密地拟合数据点,曲线形状与真实趋势接近。

以下是使用 scikit-learn 库生成这些多项式的示例代码:

from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression

# 创建多项式特征
poly_features = PolynomialFeatures(degree=d)
X_poly = poly_features.fit_transform(X_train)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/9ff65c49ee9114c8e6f5a111dbb89781_23.png)

# 在多项式特征上拟合线性回归模型
model = LinearRegression()
model.fit(X_poly, y_train)

核心结论是:增加模型的表达能力(复杂度)有助于更好地拟合训练数据

过拟合:当模型“学得太好”时 ⚠️

那么,如果我们继续增加模型的复杂度会怎样?让我们尝试拟合一个30次的多项式。

结果发现,这个高阶多项式模型几乎完美地穿过了所有训练数据点。然而,它产生了剧烈的波动,其曲线形状(橙色)与真实的蓝色曲线相去甚远。除了训练点,它在其他位置的预测都极不准确。

这个例子展示了为什么增加模型容量不是我们唯一需要关心的事情。虽然我们完美地拟合了训练数据,但模型的预测却非常糟糕。这种现象被称为过拟合

过拟合是机器学习中一个极其重要的概念,也是算法最主要的失败模式之一。其核心思想是:当模型过拟合时,它能完美拟合训练数据集,但这导致模型在训练数据区域之外的表现极不准确,因此模型无法泛化。

欠拟合:当模型“学得不够”时 📉

与过拟合紧密相关的另一个概念是欠拟合。欠拟合就是我们最初尝试用直线拟合非线性数据时遇到的问题。模型过于简单,无法捕捉数据中的潜在模式。由于模型连训练数据都拟合不好,对于来自同一分布的留出数据,其预测自然也不准确。

因此,在过拟合和欠拟合之间找到平衡点,是应用机器学习的主要挑战之一。找到两者之间的恰当平衡,是构建一个在实践中表现良好的机器学习系统需要解决的核心问题。

一个既不过拟合也不欠拟合的准确模型,应该能够平滑地跟随真实曲线,而没有之前看到的剧烈波动,例如我们最初看到的那个三次多项式。

如何诊断过拟合与欠拟合? 🩺

我们将在课程后续更深入地讨论如何在实际中部署、分析、调试和改进机器学习模型。从高层次看:

  • 过拟合:模型在训练集上表现(性能)非常高,但在留出集上的表现非常低
  • 欠拟合:模型在训练集上的表现很低,因此在留出集上的表现也很低

让我们通过一个包含留出集(红色点)的例子来直观感受:

  • 线性模型(欠拟合):模型不够灵活,无法捕捉数据真实形状,在红点上的均方误差(MSE)较高(例如0.2)。
  • 20次多项式(过拟合):曲线疯狂波动,与真实数据不符,留出误差巨大。
  • 5次多项式(良好拟合):留出误差最低(例如0.01),橙色曲线既紧密接近红色点,又匹配了已知的真实曲线形状。

应对策略:如何解决过拟合与欠拟合? 🛠️

在本课程中,我们将看到许多策略。在此,我们先给出一些高层面的思路:

应对欠拟合:解决方案是增加模型类的复杂度

  • 考虑更复杂的模型,例如非线性模型。
  • 使用更具表达能力的特征。例如,之前的多项式特征,或者根据领域知识手动组合特征(例如,为“老年男性”创建一个组合特征)。

应对过拟合:解决方案是降低模型的复杂度

  1. 直接限制模型类:例如,从使用高度非线性的算法切换到仅使用线性模型的算法。
  2. 惩罚复杂模型(更常用):保持相同的模型类,但修改目标函数,使其倾向于选择更简单的模型。这种方法在实践中应用非常广泛,我们将在后续视频中详细讨论。

总结 📝

本节课我们一起学习了监督学习中两个核心的失败模式:过拟合欠拟合。过拟合指模型对训练数据学得“太好”,以至于捕捉了噪声而非规律,导致泛化能力差;欠拟合则指模型过于简单,无法捕捉数据的基本趋势。我们通过多项式回归的例子直观展示了这两种现象,并介绍了通过调整模型复杂度(增加以解决欠拟合,减少以解决过拟合)来寻找最佳平衡点的基本思路。理解并处理好这对矛盾,是构建有效机器学习模型的关键。

4.4:第4部分:正则化 🛡️

在本节课中,我们将要学习一种称为正则化的技术。正则化是解决机器学习模型过拟合问题的核心方法之一。我们将详细探讨其原理、一种具体实现(L2正则化),并了解如何将其应用于多项式回归等场景。

概述

在之前的视频中,我们了解到过拟合是监督学习算法面临的一个主要问题。我们简要地看到了一些减少过拟合的思路和方法,而正则化就是其中之一。本节我们将深入讨论正则化,并在此过程中定义一些重要的新机器学习算法。

过拟合问题回顾

首先,让我们再次回顾本讲一直使用的设定。我们处于一个数据从数据分布P中采样的场景中,我们同时拥有训练数据集和保留数据集,它们都是从分布P中独立同分布采样得到的。我们说,如果一个模型在训练分布上训练后,在真实世界的新样本(特别是保留数据集)上也能表现良好,那么它就具有泛化能力。

监督学习可能失败的原因之一,是我们为现有数据量选择了一个过于复杂的模型。如果数据集很小,我们可能会完美地拟合数据,并且找到的模型在该数据上表现极佳,但在训练集之外的表现却非常糟糕,因此无法泛化。

我们来看一个简单的例子。这里有一个由蓝点组成的训练数据集,这些蓝点都围绕蓝色曲线(数据的真实形状)分布。图中拟合了一个30次的多项式,该多项式在我们的训练集上几乎完美地拟合了蓝色数据点。然而,一旦超出这个区域,其性能就变得非常差。这就是一个严重过拟合的例子。

正则化的核心思想

正则化的思想是通过改变我们的目标函数来减少过拟合,新目标函数包含一个惩罚复杂模型的项。

例如,在上述多项式回归的场景中,一种正则化方法是优化那些既能很好地拟合数据,又会对函数具有很高次数(即高次多项式)的情况施加惩罚。

让我给出一个更通用的正则化定义,然后我们将看到如何将其应用于我们刚才看到的场景。

正则化的高层思想如下:
我们定义一个增强的目标函数J,它为模型类M中的元素分配一个分数。这个目标函数J由一个定义在训练数据集上的损失函数,加上一个额外的项R组成,R被称为正则化器,用于惩罚复杂的模型。

让我们再次剖析这个目标函数。如前所述,我们有一个目标函数L(例如均方误差)。如果我们只有L,那么我们将得到一个标准的线性回归问题,可以计算正规方程,从而得到最小二乘算法。现在,我们添加了这个额外的项R,它根据某种“复杂”的定义来惩罚某些模型。最后,这里的λ是一个正则化参数,它控制我们惩罚复杂模型的强度。

在实践中,我们的模型通常由参数θ参数化,因此我们可以使用以下等效符号来书写这个表达式,其中所有项都是θ的函数。

L2正则化:一个具体示例

在实践中,我们可以使用什么作为正则化器呢?一个正则化器可能是什么样子的好例子?

我将在这里定义第一个正则化示例,称为L2正则化。这是一种在实践中被广泛使用的重要方法,它实际上可以解决我们之前看到的过拟合问题。

假设我们有一个以下形式的线性模型(与之前讲座中看到的模型相同):
y = θ^T x

L2正则化的思想是定义一个目标函数,其中正则化器由权重θ的L2范数组成。因此,正则化器就是参数向量θ各分量平方的和。

让我们再次看看这些组成部分。如前所述,正则化器就是参数平方和,这被称为θ的L2范数(因此得名L2正则化)。这个正则化器会惩罚大的参数值。我将给出几种理解为什么这有助于防止过拟合的方式,其中一种方式是,它防止我们过度依赖任何单一特征。同时,它恰好也能惩罚那些具有我们之前看到的 erratic(不稳定)和不规则行为的模型。

最后,我想补充一点,尽管L2正则化在这里定义于线性模型的背景下,我们也可以将其应用于本课程后面将看到的许多机器学习模型,例如神经网络或支持向量机。只要我们有参数向量θ,我们就能以不同的方式应用L2正则化。

将L2正则化应用于多项式回归

现在,让我们看看如何将L2正则化应用于多项式回归。

在多项式回归的背景下,回想一下我们有这些多项式特征φ,我们在这些特征上拟合一个线性模型,并使用均方误差损失。因此,这里的这一项就是线性回归的标准目标函数。而在这一侧,我添加了新的L2正则化器。

我将从一个演示开始,展示这个算法能做什么。这里我将为不同的数据集拟合该模型的几个实例。我们仍将使用从相同函数中采样的数据。我将为这些数据拟合几个不同的模型。这里我有与之前相同的蓝点集,并且我刚刚采样了另外两个训练集。

现在,我拟合了两个次数为15的多项式回归模型。蓝色模型是未经正则化的,你可以看到它具有我们之前讨论过的不稳定行为,它有这些剧烈的摆动,显然这看起来不像数据,在这个区域有一个巨大的摆动。在橙色中,我展示了相同的模型,但为其添加了正则化参数为0.1的正则化项。在接下来的几张幻灯片中,我将定义其内部实现方式,但现在你只需将其视为我们为来自同一分布的这三个样本中的每一个拟合的两种模型类型。你可以看到,蓝色模型每次输出一个非常不同的预测,而经过正则化的橙色模型输出的结果看起来更合理,也更像数据的真实形状。这只是一个演示,展示了L2正则化如何真正帮助我们解决这个过拟合问题,而且这种方法恰好非常简单且易于实现。

为了获得更多直觉,我们可以查看正则化模型和非正则化模型找到的权重。这些是非正则化模型的权重,你可以看到这些权重非常大,数量级达到3000、10000、100万、1000万,这些只是我抽样的前4个权重(模型总共学习了15个权重)。这意味着,在实践中,为了使多项式函数完美拟合我们的数据点,它必须在数值上具有非常高的权重。这是另一种直觉:减小这些权重的大小会迫使我们得到一个平滑的函数,因为要得到一个不平滑的函数,我们需要这些非常大的权重来指定它。

现在,如果我们查看同一组权重但在模型经过正则化之后的情况,这些权重要小得多,它们是-2.7、-1.2、0.9等等,它们都很小且表现良好。因此,即使它仍然是一个15次多项式,曲线也表现得非常好。我想再次强调,我们在这里拟合的橙色曲线仍然是一个15次多项式,但这是一个具有非常小且表现良好的系数的15次多项式,因此它看起来像我们期望的表现良好的曲线。

如何选择正则化参数λ

一个我想简要讨论的实际问题是,我们如何选择参数λ。λ被称为模型的超参数,我们将在后面更详细地重新讨论这一点,但我只想给你一种可以设置λ值的方法,使其工作良好。我们稍后会更详细地探讨为什么这是一个好主意,但本质上,这里一个简单的策略是:将λ设置为在保留验证集上表现良好的某个值。想象一下,我们拥有训练集,然后我们还有另一个与训练集不同的、小的保留集。我们可以选择那个在保留集上给我们最佳性能的λ值。这是一种估计我们泛化误差的方法。再次强调,我们将在后面更详细地重新讨论这一点,但我只想简要提及,有几种选择λ的标准方法。

拟合正则化线性模型

在剩下的几张幻灯片中,我想探讨一下如何拟合我们之前看到的那种形式的正则化线性模型。

回想一下,一个监督学习算法由模型类、目标函数和优化器组成。我们已经看到了我们的模型类和目标函数是什么样子:我们有线性模型,我们的目标是L2正则化的损失函数。但我们如何拟合这个损失函数呢?

事实证明,有一种简单的方法可以做到这一点,它是对我们拟合非正则化最小二乘模型方法的一个简单修改。

我们可以将正则化目标写成这种形式(与我们之前看到的相同),在这里我用其向量化形式替换了均方误差。你可以重新回顾我们关于最小二乘的讲座,看看我们是如何得出这个特定定义的,但这与我们在线性回归中使用的符号相同。

现在我们有了目标函数的向量化形式,我们可以应用一个非常相似的方法来推导这个表达式的梯度。这里的梯度简单地是我们损失的梯度加上正则化器的梯度。我们已经在之前的讲座中计算了这个损失的梯度,它具有这种形式。现在我们只需要计算这个函数的梯度,但这恰好就是λθ。我们可以使用以下恒等式轻松看出这个目标的梯度就是λθ。如果这个符号不清楚,这里的L2范数等价于向量与其自身的转置,所以这个项也等于θ^T θ,或者等于θ与自身的点积。

现在我们有了这种形式,我们可以将其重写为一个稍微更方便的形式,这几乎与我们之前未正则化的正规方程相同,只是我们在这里多了一个额外的λ项。

以同样的方式,使用我们之前遵循的相同策略,我们可以将导数设为零,以获得一组具有这种形式的正规方程。现在,我们可以再次通过求解这个方程中的θ来获得最优θ的封闭形式。

因此,我们再次推导出了一组可以求解的正规方程,并为我们之前看到的L2正则化模型提供了一个封闭形式的表达式。

最后,我想在这里指出的另一个重要点是,这个矩阵现在将总是可逆的。这是因为这里的矩阵I是满秩的,所以将这个满秩矩阵加到这个满秩矩阵上,结果仍然是满秩矩阵,因此它是可逆的。我们已经看到了如何拟合一个正则化模型,并且也看到了如何解决我们之前提到的潜在数值问题。当我们处理未正则化的回归时,我们说过这个矩阵在理论上可能不可逆,但现在通过添加一个小的正则化器,我们可以使其完全可逆。因此,在实践中,如果存在任何关于矩阵求逆的问题,我们可以通过添加少量正则化来正则化我们的模型。

岭回归算法

这样做的结果产生了一种称为岭回归的算法。岭回归只是L2正则化回归的另一个名称。

这是一个最小二乘法的扩展算法,事实上它几乎是相同的算法。它是一种监督学习,特别是回归的形式,它使用线性模型的形式,并使用正规方程进行优化。唯一的区别是,我们的目标函数现在是L2正则化的均方误差。

再次强调,岭回归是机器学习中一个非常重要的算法,在实践中被广泛使用。这不是一个玩具算法,而是在各种机器学习系统中广泛用于生产环境的东西。它在减少过拟合和提高数值性能方面表现要好得多。

在下一讲(或下一个视频)中,我们将继续讨论正则化,并看到另一种也将非常重要的正则化形式。

总结

在本节课中,我们一起学习了:

  1. 正则化的核心概念:通过修改目标函数,添加一个惩罚项来限制模型复杂度,从而防止过拟合。
  2. L2正则化:一种具体且广泛使用的正则化方法,它惩罚模型参数(权重)的平方和(L2范数)。
  3. 岭回归:应用了L2正则化的线性回归算法,它通过修改正规方程来求解,并且总能保证解的存在(矩阵可逆)。
  4. 正则化参数λ的作用与选择:λ控制正则化的强度,可以通过在独立的验证集上测试不同λ值的效果来选择。
  5. 正则化的效果:通过限制权重的大小,L2正则化倾向于产生更平滑、更稳定的模型,显著提升了模型的泛化能力,尤其是在数据量有限或特征较多时。

正则化是构建健壮、泛化能力强的机器学习模型的关键工具之一。

4.5:L1正则化与稀疏性 🎯

在本节课中,我们将继续探讨正则化,并学习一种与之前不同的新正则化方法。通过了解这种新技术,我们还将学习机器学习中一个非常重要的概念:稀疏性。

概述 📋

在上一节中,我们介绍了正则化作为一种惩罚复杂机器学习模型的方法。它通过在标准机器学习目标函数上添加一个称为正则化项的额外项来实现,该正则化项为复杂模型分配较高的值。

正则化回顾 🔄

正则化的通用形式可以表示为:

目标函数 = 损失函数 + λ * 正则化项

其中:

  • 损失函数 可以是均方误差等标准机器学习损失
  • 正则化项 衡量模型的复杂度
  • λ 是控制正则化强度的参数

在上一节中,我们学习了L2正则化,它使用L2范数作为正则化项。本节中,我们将探讨使用L1范数代替L2范数来惩罚复杂模型的不同方法。

L1正则化介绍 🔍

L1正则化的目标函数为:

J(θ) = MSE(θ) + λ * ||θ||₁

其中L1范数定义为:

||θ||₁ = Σ|θᵢ|

与L2正则化类似,添加L1范数会惩罚较大的权重,降低模型复杂度,并鼓励选择更简单、更平滑的模型。然而,这两种范数在选择解的方式上存在显著差异。

LASSO算法 🎯

LASSO(最小绝对收缩和选择算子)本质上是L1正则化的线性回归,类似于岭回归是L2正则化的线性回归。

以下是LASSO的关键特点:

算法类型:监督学习算法,具体为回归算法
模型类型:线性模型
优化目标:均方误差 + L1正则化项

优化方法

有多种方法可以优化LASSO目标函数:

  1. 梯度下降法:我们已经学习了实现梯度下降所需的所有要素
  2. 坐标下降法:专门用于L1正则化的优化技术
  3. 最小角回归(LARS):专门为此任务设计的算法

约束形式的正则化 ⛓️

在深入比较L1和L2正则化之前,我们需要了解正则化的另一种形式:约束优化。

两种正则化形式

  1. 惩罚项形式min J(θ) = MSE(θ) + λ * R(θ)
  2. 约束形式min MSE(θ),约束条件为R(θ) ≤ λ'

这两种方法在本质上是等价的,对于给定的λ,存在某个λ'使得两个问题具有相同的解。

L1与L2正则化的几何解释 📐

为了理解L1和L2正则化的差异,让我们通过几何视角来观察。

约束区域形状

  • L2约束||θ||₂ ≤ λ' 形成一个圆形区域
  • L1约束||θ||₁ ≤ λ' 形成一个菱形区域

优化过程

均方误差的等高线是椭圆形的。最优解是椭圆与约束区域相切的点:

  • L2正则化:切点通常位于圆形边界上,所有参数都较小但非零
  • L1正则化:切点通常位于菱形的角上,导致某些参数恰好为零

这种几何差异解释了为什么L1正则化会产生稀疏解。

稀疏性概念 🌟

稀疏性是指向量中大部分元素为零的性质。在机器学习中,稀疏性具有以下重要意义:

稀疏性的优势

  1. 可解释性:非零参数标识了最重要的特征
  2. 计算效率:只需要存储和计算非零参数
  3. 特征选择:自动选择相关特征,减少数据收集成本

例如,在糖尿病预测中,如果有100个生理测量指标,L1正则化可能只保留10个非零权重,这10个特征就是模型认为最重要的预测因子。

实际示例对比 📊

让我们通过糖尿病预测任务来比较岭回归和LASSO的实际表现。

岭回归结果分析

随着正则化参数λ的增加:

  • 所有权重的幅度逐渐减小
  • 所有参数保持非零状态
  • 权重均匀收缩

LASSO结果分析

随着正则化参数λ的增加:

  • 权重幅度减小
  • 某些参数在特定λ值处变为零
  • 最终只保留少数非零参数

这种差异清晰地展示了L1正则化产生稀疏解的特性。

总结 📝

在本节课中,我们一起学习了:

  1. L1正则化:使用L1范数作为正则化项的正则化方法
  2. LASSO算法:L1正则化线性回归的具体实现
  3. 稀疏性:向量中大部分元素为零的重要性质
  4. 几何解释:L1和L2正则化在约束区域形状上的差异
  5. 实际应用:稀疏性在特征选择和模型可解释性中的价值

L1正则化不仅是机器学习中的一个重要工具,更是理解稀疏性和特征选择的关键概念。通过产生稀疏解,LASSO不仅提供了正则化的好处,还增强了模型的可解释性和计算效率。

5.1:概率建模(第一部分)🎯

在本节课中,我们将学习概率建模的基本概念。我们将探讨如何用概率的语言来定义机器学习算法,并理解为什么监督学习能够工作。此外,我们还将看到如何通过概率模型来捕捉预测的不确定性,并基于此构建更原则化的机器学习模型。

概率建模概述 📊

上一节我们介绍了监督学习模型作为从输入 x 映射到目标 y 的函数。本节中,我们将从一个不同的角度——概率建模——来审视监督学习。

具体来说,我们将扩展机器学习模型的概念,使其包含更一般的概率模型。一个概率模型,就像数据分布一样,是 xy 上的一个概率分布。与常规监督学习模型可以有参数类似,概率模型也可以由参数 θ 参数化,我们将其记为 P_θ(x, y)

即使这个定义假设了 xy 的联合模型,我们仍然可以像常规监督学习模型那样,通过观察给定 xy 的条件概率 P_θ(y | x) 来进行预测。换句话说,如果我们知道 xy 的联合分布,就可以利用概率规则从中推导出给定 xy 的分布。

一个具体的概率模型示例 🧮

为了更好地理解概率模型,让我们看一个具体的例子。我们将使用之前预测糖尿病风险的简化场景,并对输入(BMI)和目标(风险)进行离散化处理。

以下是离散化设定:

  • 目标 y(风险):取两个值,0(低风险)或 1(高风险)。
  • 输入 x(BMI):离散化为三个等级,0(低)、1(中)、2(高)。

基于此,我们可以用一个简单的表格来定义一个概率模型,该表格列出了 xy 每种组合的联合概率。

以下是该概率模型的表格表示:

BMI (x) 风险 (y) 概率 P(x, y)
0.35
0.20
0.10
0.15
0.05
0.15

这是一个有效的 xy 联合概率分布。我们可以利用这个分布进行预测。例如,要计算给定 xy 的条件分布,我们使用条件概率公式:

P(y | x) = P(x, y) / P(x)

其中 P(x) 是边缘概率,通过对所有 y 求和得到:P(x) = Σ_y P(x, y)

对上述表格应用此计算,我们可以得到给定不同 BMI 等级时的风险预测概率。例如:

  • 对于一个 低 BMI 的患者,其 高风险 的概率约为 20%,低风险 的概率约为 80%。
  • 对于一个 高 BMI 的患者,其 高风险 的概率约为 64%,低风险 的概率约为 36%。

这个简单的模型可以通过统计训练数据集中落入每个类别的患者比例来估计。

为什么使用概率模型?🤔

概率模型之所以有用,主要有以下几个原因:

以下是概率模型的几个关键优势:

  1. 捕捉预测不确定性:数据可能本身存在噪声(如测量误差),或者预测任务本身具有内在随机性(如股票预测)。概率模型通过输出分布而非单一值,可以自然地表达这种不确定性。
  2. 构建原则化模型:如果我们对数据生成过程有先验知识(例如,糖尿病风险受血糖影响,而血糖又受饮食影响),我们可以将这些因果关系编码到概率模型的结构中,从而构建更贴合问题本质的模型。
  3. 提供新的理论视角:概率框架为理解监督学习为何有效提供了新的解释。更重要的是,它为我们理解现有算法和推导新算法提供了一个统一的原则。我们将在本讲后续部分深入探讨这一点。

总结 📝

本节课我们一起学习了概率建模的基础。我们首先将监督学习模型的概念扩展为 xy 的联合概率分布 P_θ(x, y),并了解到可以通过条件概率 P_θ(y | x) 进行预测。接着,我们通过一个离散化的糖尿病风险预测示例,具体展示了如何构建和利用一个简单的概率模型。最后,我们探讨了概率模型的三大优势:量化不确定性、编码领域知识以构建结构化模型,以及为机器学习提供新的理论基石。在接下来的部分,我们将深入探讨如何从数据中学习这些概率模型的参数,即最大似然估计

5.2:蒙特卡洛估计 🎲

在本节课中,我们将暂时离开之前关于概率模型的讨论,转而介绍一个在本讲及后续课程中都非常有用的工具——蒙特卡洛估计。这是一种数学方法,用于近似计算期望值,在机器学习的许多领域都有应用。

建立符号与概念

在定义蒙特卡洛估计之前,我们先建立一些符号。

假设我们有一个随机变量 x,它存在于某个空间 X 中,并由概率分布 P 描述。这里的 x 不一定是一个特征向量,它可以是任何随机变量。例如,它可以是从数据分布中抽取的样本,也可以是抛硬币的结果,或者是掷骰子的结果。总之,x 是某个随机变量。

如果 x 是随机变量,我们就可以计算该随机变量各种函数的期望值。

例如,假设 x 是掷两个骰子的结果,G 是一个计算其点数和函数。我们想要估计两次掷骰子点数和的期望值。

以下公式给出了期望值的定义:

E[G(x)] = Σ_x G(x) * P(x) (当 x 为离散变量时)

这个基本概率概念表明,函数 G 的期望值(即两次掷骰子的期望点数和)等于函数 G 值的加权平均,权重就是概率 P(x)。换句话说,我们列举 x 所有可能的结果(例如点数和为2、3、4等),将每个结果对应的 G(x) 值乘以其发生的概率,然后求和。

期望值在统计学和机器学习中都非常重要。然而,计算这些值通常很困难。例如,如果 x 可能取值的数量非常庞大,那么求和运算在实践上可能无法实现。或者,当 x 是连续变量时,期望值定义会变成一个积分,我们可能无法得到这个积分的闭式解。

因此,在实践中,大多数应用场景并不精确计算期望值,而是使用近似技术。蒙特卡洛估计就是这样一种用于近似计算期望值的技术。

蒙特卡洛估计的定义

我们仍然对近似计算期望值感兴趣。蒙特卡洛估计的核心思想是:从数据分布 P 中抽取一定数量的样本(记作 T),然后简单地计算函数 G 在这些样本上的平均值。

我们的估计量 Ĝ,作为这 T 个随机样本的函数,就是我们对期望值的蒙特卡洛估计:

Ĝ = (1/T) * Σ_{t=1}^{T} G(x^{(t)}),其中 x^{(t)} ~ P

为了直观理解为什么这个方法合理,假设 x 可以取三个值,且概率不同。当我们从分布 P 中抽样时,每个可能值出现的次数大致与其概率 P(x) 成比例。因此,当我们计算平均值时,实际上是在以近似于真实概率分布的比例对函数值进行加权平均。

一个具体示例

让我们通过一个具体例子使其更明确。假设我们掷五个骰子,想知道掷出“2”的期望次数。

具体地,令 x 为一个向量,其每个分量是1到6之间的数字,代表第 j 个骰子的结果。那么,函数 G(x) 就是这次投掷中“2”出现的次数。

我们可以如下计算该期望值的蒙特卡洛估计(使用Python和NumPy):

import numpy as np

# 生成10000次随机投掷,每次投5个骰子
# 结果是一个 5行 x 10000列 的矩阵,每个元素是0-5(代表骰子点数-1)
rolls = np.random.randint(0, 6, size=(5, 10000))

# 计算每次投掷中“2”(即数值1)出现的次数
num_twos = np.sum(rolls == 1, axis=0)

# 计算这10000次投掷中“2”出现次数的平均值,作为期望值的估计
estimate = np.mean(num_twos)
print(estimate)

运行此代码,估计值大约在0.83(即83%)左右。如果重新运行,会得到略有不同的值,但都围绕0.83波动。如果增加样本数量(例如到一百万),估计值会稳定在更接近0.83的数值上。

实际上,通过解析计算可以得出,掷五个骰子得到“2”的期望次数确实是 5/6 ≈ 0.833...。蒙特卡洛估计的结果与此一致。

蒙特卡洛估计的性质

现在,我们强调一下蒙特卡洛估计的几个重要性质:

  • 无偏性:蒙特卡洛估计是真实期望值的无偏估计。这意味着,随着抽取的样本增多,估计值的平均值会等于真实的期望值。在上面的例子中,估计值有时是0.84,有时是0.82,但它们的均值在0.83左右。
  • 方差递减:随着收集的样本数量增加,估计值的方差会减小。这在上面的例子中也很明显:使用一万个样本时,估计值在0.82到0.84之间变化;而使用一百万个样本时,估计值只在0.833的小数点后几位波动。因此,样本越多,估计越准确。

总结

本节课我们一起学习了一种称为蒙特卡洛估计的工具。它可以应用于机器学习中许多需要计算期望值的领域。这是一种非常简单且通用的近似方法,适用于任何能够从中抽取样本的概率分布。

在下一个视频中,我们将展示如何将这一技术应用于一个重要的机器学习问题。

5.3:第3部分:最大似然学习 🎯

在本节课中,我们将学习最大似然估计。这是一个通用原则,可用于推导新的机器学习算法,并解释许多我们之前见过的算法为何有效,以及它们如何被理解为概率建模的实例。

概述

我们假设数据(输入 x 和目标 y)是从一个数据分布中独立同分布采样得到的。这个数据分布是 xy 上的一个联合概率分布。我们的训练集就是来自该数据分布的一组独立同分布样本。

既然我们假设数据来自概率分布,那么将我们的机器学习模型也定义为概率分布就是合理的。我们可以尝试拟合这类模型,使其与数据分布相似。这里,我们定义了一个带参数 θ 的特定分布。这同样属于监督学习的范畴,因为给定输入 x,我们可以应用概率论的基本规则来获得给定 xy 的条件分布,并用于预测。本质上,概率模型是一种使用机器学习来拟合数据分布的方法。

拟合概率模型的目标

既然我们希望通过拟合概率模型来近似数据分布,我们该如何做?应该使用什么样的目标函数呢?

定义“好”的概率模型有许多不同方式,取决于我们希望从模型中获得什么。之前许多课程的重点是预测准确性,即寻找一个能准确从 x 预测 y 的模型。我们也可以尝试构建能近似 xy 之间关系的模型。如果我们的模型与真实数据分布具有相同的结构,但缺少一些参数,我们可以通过数据来学习这些参数,将我们对分布结构的先验知识编码进去,然后观察数据告诉我们这些参数的值。例如,我们可以推断患者的哪些特征会影响其糖尿病风险。

最后,一个更通用的目标是密度估计。我们试图在所有可能的 xy 值上近似整个数据分布。如果我们能做到这一点,那么我们将能够回答任何查询,进行预测,并探究该分布结构的概率特性。密度估计是一个重要的目标,也是本讲我们将要追求的目标。

比较分布:KL散度

为了进行密度估计,我们需要某种度量两个分布之间相似性或距离的方法。为此,我们将使用信息论中的一个工具:Kullback-Leibler散度

以下是KL散度对于离散变量 x 的数学定义:

D_KL(P || Q) = Σ_x P(x) * log( P(x) / Q(x) )

虽然其精确形式对于理解本视频内容并非绝对关键,但关键要点是:这是在机器学习、物理学和许多科学领域中广泛使用的比较概率分布的方法。

KL散度的性质

KL散度有几个重要性质,使其成为比较概率分布的良好方式:

  1. 非负性D_KL(P || Q) ≥ 0
  2. 零值条件D_KL(P || Q) = 0 当且仅当 PQ 处处相等。
  3. 非对称性D_KL(P || Q) ≠ D_KL(Q || P),因此它被称为“散度”而非“距离”。
  4. 信息论根源:它源于信息论,衡量用分布 Q 来近似真实分布 P 时造成的信息损失。

从KL散度到最大似然估计

假设我们想使用KL散度作为训练目标来比较我们的模型分布 P_θ(x, y) 和数据分布 P_data(x, y)。这将给出如下目标:

min_θ D_KL(P_data || P_θ) = E_{(x,y)~P_data} [ log( P_data(x, y) / P_θ(x, y) ) ]

我们希望最小化这个值,使其接近零,这样两个分布就会相同。

现在,我们可以简化这个表达式。根据对数运算的性质,我们可以将其分解为两项:

D_KL(P_data || P_θ) = E_{(x,y)~P_data} [ log P_data(x, y) ] - E_{(x,y)~P_data} [ log P_θ(x, y) ]

关键观察是:左边第一项仅依赖于数据分布 P_data,而与我们的模型参数 θ 无关。因此,如果我们只关心优化模型,可以忽略这一项。最小化KL散度就等价于最大化第二项 E_{(x,y)~P_data} [ log P_θ(x, y) ]

这引出了本讲的核心原则:最大似然估计。项 log P_θ(x, y) 被称为似然(的对数形式)。最大似然原则在于优化以下目标:

max_θ E_{(x,y)~P_data} [ log P_θ(x, y) ]

这对应于最小化模型分布与数据分布之间的KL散度。

让我们更详细地检查这个目标的作用。本质上,我们从数据中采样点,并要求模型为这些来自数据的点分配高概率。由于概率总和为1,模型自然会为那些不来自数据的点分配低概率。这是一种学习概率模型的通用方法,统计学中有广泛的理论支持其合理性。

实践中的最大似然:经验对数似然

然而,如果我们想在实践中优化这个目标,会遇到一个问题:这是一个关于数据分布的期望值。精确计算这个期望值通常是不可行的,一方面因为 xy 的可能空间可能非常大,另一方面我们甚至不知道真实的数据分布 P_data 是什么。

不过,我们可以使用本讲前面部分介绍的蒙特卡洛估计原理来近似这个期望值。如果我们有来自数据分布的样本,那么我们可以简单地通过对样本集上的对数概率求平均来估计这个期望。回想一下,我们的训练数据集正是我们所需要的——它就是来自数据分布的一组样本。

因此,为了应用最大似然估计,我们可以简单地计算以下目标,称为经验对数似然

max_θ (1/N) * Σ_{i=1}^N log P_θ(x_i, y_i)

其中 {(x_i, y_i)} 是我们的训练数据。换句话说,我们要求模型为我们收集到的、来自数据分布的样本集分配高概率。通过优化这个目标,我们近似地优化了真实的对数似然,从而用我们的模型 P_θ 近似了真实的数据分布。

一个简单示例:抛硬币

让我们考虑一个最大似然估计的简单应用,以便更好地直观理解它的作用。

假设我们抛一枚随机硬币,我们有一个抛硬币结果的数据集,两种可能结果是正面(H)或反面(T)。例如,我们的数据集可能是:{H, H, T, H, T}(3次正面,2次反面)。

我们假设数据来自一个数据分布。由于只有两种结果,数据分布可以表示为一个伯努利分布,为两种结果分配概率。我们的模型也将采用相同的形式,即伯努利分布族,只有一个参数 θ(正面的概率)。模型定义如下:

P_θ(H) = θ
P_θ(T) = 1 - θ

现在,让我们看看如何将最大似然估计应用于此设置。

给定数据集 {H, H, T, H, T},模型下的数据似然为:

L(θ) = P_θ(H) * P_θ(H) * P_θ(T) * P_θ(H) * P_θ(T) = θ * θ * (1-θ) * θ * (1-θ) = θ^3 * (1-θ)^2

我们希望找到使这个似然最大化的 θ。我们可以绘制这个函数,或者解析求解。对于更一般的情况,如果有 N_H 次正面和 N_T 次反面,则对数似然为:

log L(θ) = N_H * log(θ) + N_T * log(1-θ)

通过对 θ 求导并令其为零,我们可以找到最优解:

d/dθ [log L(θ)] = N_H/θ - N_T/(1-θ) = 0

解得:

θ_ML = N_H / (N_H + N_T)

在我们的例子中,N_H = 3, N_T = 2,所以 θ_ML = 3/5 = 0.6。这是一个非常直观的结果:最大似然估计认为正面的概率就是数据中正面出现的频率。

条件最大似然估计

最后,有时我们感兴趣的不是拟合 xy 的联合模型,而是给定 xy 的条件模型。当然,我们可以从联合模型中获得条件模型,但有时我们希望直接使用条件模型。

在这种情况下,我们可以应用条件最大似然估计原则。此时,我们优化的是数据条件分布与模型条件分布之间的期望KL散度。通过类似的推导,这对应于优化以下目标:

max_θ E_{(x,y)~P_data} [ log P_θ(y | x) ]

同样,我们可以使用经验近似:

max_θ (1/N) * Σ_{i=1}^N log P_θ(y_i | x_i)

这被称为条件最大似然估计原则。在接下来的几讲中,我们将看到这一原则在许多重要机器学习模型中的应用。

总结

在本节课中,我们一起学习了:

  1. 概率建模的动机:将机器学习模型视为概率分布,以近似未知的数据分布。
  2. KL散度:作为比较两个概率分布相似性的重要工具,其最小化等价于最大似然估计。
  3. 最大似然估计原理:通过最大化模型在训练数据上的(对数)似然来学习模型参数,其本质是要求模型为观测到的数据分配高概率。
  4. 经验近似:由于真实期望不可计算,我们使用训练数据的经验平均来近似最大似然目标。
  5. 简单示例:通过抛硬币的例子,我们直观地看到最大似然估计给出了符合直觉的参数估计(正面出现的频率)。
  6. 扩展:最大似然原则可以自然地扩展到条件分布的学习,即条件最大似然估计。

最大似然估计为许多机器学习算法提供了一个统一的理论基础,并且作为一个优化目标,它可以与梯度下降等优化算法结合,应用于非常复杂的模型。

5.4:最大似然的扩展 🧠

在本节课中,我们将继续探讨最大似然估计,并了解这一原理的一些重要扩展。我们将重点介绍统计学和机器学习中的一个核心思想——贝叶斯方法,并探讨其与频率主义方法的区别。最后,我们将学习一种实用的近似方法:最大后验估计。


频率主义方法回顾 🔄

上一节我们介绍了最大似然估计。在最大似然学习中,我们优化以下目标:

我们有一个带参数 θ 的概率模型 P。最大似然为我们提供了一个目标函数,该函数可以通过最小化KL散度从第一性原理推导出来。目标函数如下:

公式:
L(θ) = E_{(x,y) ~ D}[log P(x, y; θ)]

在实践中,我们无法计算这个期望值,但可以使用蒙特卡洛估计来近似,即用从数据分布中抽取的样本的平均值来代替。因此,模型会为来自数据分布的样本分配高概率。

这种训练概率模型的方法是频率主义方法的一个实例。频率主义思想的核心是:我们试图估计的参数 θ 是一个固定但未知的量。我们的目标就是去发现这个真实但未知的参数。

例如,考虑之前视频中抛硬币的场景。硬币有一个未知的偏差(偏向正面或反面的程度)。我们观察到大量抛掷结果,这些结果来自一个真实的数据分布。最大似然估计就是一种推断这个未知但固定参数的统计程序。


贝叶斯方法介绍 🎲

与频率主义方法形成对比的是贝叶斯方法。在贝叶斯方法中,我们将要推断的参数 θ 本身视为一个值未知的随机变量

这是一个非常有趣的想法。为了进行贝叶斯统计,我们将定义 XYθ 的联合分布。这需要定义两个组成部分:

  1. 似然:这是在给定参数 θ 的某个赋值下,XY 的概率。即 P(x, y | θ)。这与我们之前使用的概率分布相同。
  2. 先验:这是为 θ 的不同值分配概率的分布,即 P(θ)。直观上,先验指定了我们在看到任何数据之前对 θ 的信念。

通过这两个部分,我们可以定义 XYθ 的联合分布:P(x, y, θ) = P(x, y | θ) * P(θ)。这里需要强调的是,θ 现在和 XY 一样是一个随机量。

理解这一点可能需要一些哲学思考。一种理解方式是,将 θ 的概率分布视为我们对其真实值的信念。不同的 θ 值根据我们的先验信念和观察到的数据,被赋予了不同的可信度。


贝叶斯推断与预测 🔮

在频率统计中,我们推断一个能很好解释数据的 θ 值。但在贝叶斯方法中,由于 θ 是随机变量,我们应考虑在给定数据集 Dθ 的概率分布,即后验分布

公式:
P(θ | D) = P(D | θ) * P(θ) / P(D)

其中,P(D) = ∫ P(D | θ) * P(θ) dθ(对所有可能的 θ 积分)。这个表达式原则上是可以计算的,因为 P(D | θ) 是我们的似然函数,P(θ) 是先验分布。

那么,在这个框架下,我们如何对新的 X 预测 Y 呢?与频率主义不同,我们没有一个单一的 θ 估计值,而是有一个完整的分布。

公式:
P(y | x, D) = ∫ P(y | x, θ) * P(θ | D) dθ

这个表达式利用了全概率定律。左边的 P(y | x, D) 被称为后验预测分布。其思想是,我们考虑所有可能的参数集(例如,所有可能拟合数据的直线或曲线),根据它们在给定数据下的概率对每条曲线进行加权,最终的预测输出是所有模型的加权平均“投票”。


贝叶斯方法的优势与挑战 ⚖️

贝叶斯方法非常强大,原因如下:

  • 不确定性估计:我们不仅能预测 Y 的单一值,还能预测一个分布。这个分布不仅考虑了预测本身的不确定性,还考虑了模型本身的不确定性,因此通常被认为是更高质量的不确定性估计。
  • 融入先验知识:通过先验分布 P(θ),我们可以将关于问题的已有知识以原则性的方式纳入统计过程。
  • 统一的建模框架:它为创建、构建和推理概率模型提供了一个通用框架。

然而,贝叶斯方法的主要缺点是计算复杂性。计算对所有可能模型的平均积分通常非常困难,在几乎所有有趣的应用中都无法精确计算。

因此,贝叶斯统计和贝叶斯机器学习的一个重要部分就是为这些贝叶斯量寻找良好、准确且可处理的近似方法。


最大后验估计:一种实用的近似 🛠️

在本视频的剩余部分,我们将重点介绍一种贝叶斯学习的近似方法,称为最大后验估计

由于计算完整的参数后验分布 P(θ | D) 非常困难,MAP学习的想法是,用后验分布中最可能的那个单一参数值来近似整个分布。

换句话说,我们计算一个估计值 θ_MAP

公式:
θ_MAP = argmax_θ P(θ | D)

然后,在进行预测时,我们不再对所有可能的参数取平均,而是仅使用这个单一的MAP估计值来近似。

如何计算这个参数呢?我们可以再次应用贝叶斯规则:

公式:
P(θ | D) ∝ P(D | θ) * P(θ)

其中,P(D) 是与 θ 无关的常数,可以忽略。因此,MAP估计是以下目标函数的最大化者:

公式:
θ_MAP = argmax_θ [ log P(D | θ) + log P(θ) ]

观察这个公式,MAP目标函数对应于我们常规的对数似然 log P(D | θ),加上一个来自先验 log P(θ) 的额外项。因此,MAP估计试图在根据似然很好拟合数据尊重我们先验信念之间取得平衡。

在实践中,MAP估计是一种非常常见、强大且实用的近似贝叶斯推断的方法。许多常见的机器学习算法都是MAP估计的特例。


示例:抛硬币的MAP估计 🪙

让我们看一个在抛硬币情境下MAP估计的简单例子。我们假设与之前相同的模型和数据集。

数据:三次正面,两次反面。
模型P(Heads) = θ, P(Tails) = 1 - θ

此外,我们为 θ 的先验假设一个特定的分布——Beta分布P(θ) ∝ θ^(α-1) * (1-θ)^(β-1)αβ 是Beta分布的参数(超参数),我们可以根据先验知识来选择它们。

现在,我们可以计算MAP目标。似然部分为 P(D | θ) = θ^3 * (1-θ)^2。先验部分为 P(θ) ∝ θ^(α-1) * (1-θ)^(β-1)。因此,联合分布正比于:
P(D | θ) * P(θ) ∝ θ^(3+α-1) * (1-θ)^(2+β-1) = θ^(α+2) * (1-θ)^(β+1)

取对数后,对数后验为:
log P(θ | D) ∝ (α+2) * log θ + (β+1) * log (1-θ)

通过求导并设为零(与最大似然类似),我们得到MAP估计:

公式:
θ_MAP = (α + 3) / (α + β + 5)

让我们仔细看看这个结果。现在,我们对正面概率的估计不再是简单的“正面次数/总投掷次数”(即3/5=0.6)。我们额外加上了参数 αβ

你可以将 αβ 理解为虚拟的正面和反面计数。例如,如果我们设 α = 10β = 20,那就相当于我们在看到实际数据之前,已经看到了10个虚拟正面和20个虚拟反面。然后在这个基础上计算似然。

这是一个通过设置特定先验来修改估计过程,并将先验知识(这里解释为虚拟计数)注入模型的例子。

具体演示

  • 如果我们设 α = 1, β = 1(一个常见的无信息先验),则 θ_MAP = (1+3)/(1+1+5) = 4/7 ≈ 0.57。尽管数据中正面占60%,但MAP估计更接近0.5,因为先验倾向于认为硬币是公平的,并且我们数据量很小(仅5次投掷)。
  • 如果我们设 α = 10, β = 10(强烈的先验信念认为硬币公平),则 θ_MAP = (10+3)/(10+10+5) = 13/25 = 0.52,更加接近0.5。
  • 如果我们设 α = 0, β = 0(退化为最大似然),则 θ_MAP = 3/5 = 0.6

这展示了如何通过贝叶斯或MAP方法,利用先验知识来影响估计结果。


总结 📝

本节课中,我们一起学习了最大似然原理的重要扩展:

  1. 我们回顾了频率主义方法,其将模型参数视为固定但未知的量,目标是通过数据估计其值。
  2. 我们引入了贝叶斯方法,其核心创新是将模型参数本身视为随机变量,并通过先验分布和后验分布来描述我们对参数的不确定性。
  3. 我们探讨了贝叶斯方法的优势(如不确定性估计、融入先验知识)和主要挑战(计算复杂)。
  4. 为了应对计算挑战,我们学习了一种实用的近似方法——最大后验估计。MAP通过寻找后验分布中概率最大的参数值,在数据似然和先验信念之间进行权衡,是连接频率主义和贝叶斯思想的桥梁。
  5. 最后,我们通过一个抛硬币的实例,具体演示了如何计算和应用MAP估计,并解释了先验参数(如Beta分布中的 α, β)的直观含义。

在下一讲中,我们将看到MAP原理在一些我们已经熟悉的算法中的应用。

5b.1:概率线性回归 📊

在本节课中,我们将从概率视角重新审视线性回归算法。我们将看到,之前学习的普通最小二乘法实际上是概率模型框架下的一个特例。通过引入概率假设,我们不仅能理解最小二乘法的深层原理,还能推导出更通用、更强大的回归算法。


回顾:概率模型与线性回归

上一节我们介绍了如何将机器学习算法构建为拟合数据分布的概率模型。本节中,我们将结合概率模型与线性回归,为已学习的算法建立一个新的概率视角。

概率模型可以看作是数据 (X, Y) 上的一个联合分布,我们的目标是使用模型去近似真实的数据分布。在监督学习中,我们通常通过概率运算将联合分布转化为条件分布。

模型通常由一组参数 θ 参数化,我们将其概率记为 P_θ(Y|X)

我们可以使用条件最大似然原则来训练概率模型。其核心目标是最大化模型在数据分布样本上的概率。


最小二乘法的概率视角

在之前的课程中,我们学习了线性回归算法,特别是最小二乘法。其模型形式为线性函数:

ŷ = θ^T x

优化目标是均方误差(MSE),即最小化预测值与真实目标值之间的平方差总和:

L(θ) = Σ (y_i - θ^T x_i)^2

你可能会问,为什么我们最初选择均方误差作为优化目标?毕竟我们可以使用L1损失或其他任何鼓励预测值与目标值相似的函数。

这个问题的答案,可以通过从概率视角审视线性回归来找到。


推导概率线性回归算法

现在,让我们尝试使用概率方法来推导一个监督学习算法。一个完整的算法需要定义三个要素:模型类别目标函数优化方法

1. 定义模型类别

我们提出以下由参数 θ 参数化的概率分布族作为我们的模型类别。为简化起见,假设 Y 是标量(一维回归问题)。

我们假设给定输入 x 时,目标 y 的条件分布是一个高斯分布(正态分布)

P_θ(y | x) = N(y; μ(x; θ), σ^2)

其中,均值 μx 和参数 θ 的函数。具体地,我们令均值 μx 的线性函数:

μ(x; θ) = θ^T x

因此,完整的模型是:

P_θ(y | x) = 1 / (√(2π)σ) * exp( - (y - θ^T x)^2 / (2σ^2) )

这个公式的含义是:给定一个输入 xy 的分布是一个以 θ^T x 为中心的高斯钟形曲线。方差 σ^2 在此处被视为一个固定的超参数(后续可扩展)。

2. 定义目标函数

遵循概率机器学习方法,一个自然的目标函数是条件最大对数似然。对于单个数据点 (x, y),其对数似然为:

log P_θ(y | x) = - (y - θ^T x)^2 / (2σ^2) - log(√(2π)σ)

对于整个数据集,我们希望最大化总的对数似然,这等价于最小化负对数似然。观察上式,第二项 - log(√(2π)σ) 是与参数 θ 无关的常数。因此,最大化对数似然等价于最小化第一项,即:

Σ (y_i - θ^T x_i)^2

这正是我们熟悉的最小二乘损失函数

3. 优化方法

由于目标函数与最小二乘法完全相同,我们可以使用之前学过的任何优化方法,例如梯度下降法正规方程


结论与意义

通过上述推导,我们从一个简单的概率假设(即目标 y 在给定 x 时服从以线性函数为均值的高斯分布)出发,自然地得到了最小二乘线性回归算法。

这回答了最初的问题:我们使用均方误差损失,是因为它源于一个非常基本且广泛适用的概率假设——正态分布。最小二乘法是概率框架下的一个特例。


概率框架的扩展能力

概率方法的强大之处在于其可扩展性。我们不仅可以复现经典算法,还能轻松推导出新的、更强大的算法。

以下是几个扩展方向:

  • 异方差回归:我们可以不仅用参数 θ 建模均值 μ,还用另一组参数建模方差 σ^2,使其也成为 x 的函数。这样,模型不仅能预测目标值,还能给出预测的置信区间,这在许多实际应用中非常有用。
  • 其他分布假设:高斯分布并非适用于所有数据类型。例如:
    • 对于计数数据,泊松分布可能更合适。
    • 对于只能取两个值(是/否)的分类数据,伯努利分布是自然的选择(我们将在后续分类课程中详细讨论)。
    • 对于正值数据,指数分布伽马分布可能更好。

通过为不同的数据选择合适的分布并进行参数化,我们可以得到专门针对该数据类型优化的、性能更佳的机器学习算法。


本节课中,我们一起学习了如何从概率视角理解线性回归,并推导出最小二乘法。我们看到,概率框架不仅为现有算法提供了理论依据,更是设计和理解新算法的强大工具。在接下来的课程中,我们将继续探索概率模型在分类等其他任务中的应用。

5b.2:贝叶斯算法(第二部分)🎯

在本节课中,我们将继续探讨机器学习的概率方法。本节将聚焦于贝叶斯学习方法,并通过一个具体示例,展示如何从贝叶斯视角推导出我们之前见过的机器学习算法。

贝叶斯方法回顾

上一节我们介绍了频率主义方法,本节中我们来看看贝叶斯方法。贝叶斯方法将参数集θ视为一个随机变量,其值恰好未知。这与频率主义方法形成对比,后者假设存在一个真实的θ需要估计。

例如,在抛硬币场景中:

  • 频率主义:硬币有真实的正面/反面概率,我们需要估计这个概率
  • 贝叶斯方法:我们定义并处理硬币偏向某个值的概率

在贝叶斯方法中,我们需要定义两个组件:

  1. 似然模型:形式为P(x,y|θ),这与我们之前使用的模型相同
  2. 先验分布:指定我们在看到任何数据之前对分布的初始信念

通过相乘这两个组件,我们形成所有变量的联合分布:

P(θ,x,y) = P(θ) × P(x,y|θ)

这样我们就得到了θ的概率分布。

贝叶斯学习的挑战

然而,贝叶斯方法在实践中通常难以处理。为了应用贝叶斯方法,我们需要将联合概率转换为后验概率P(θ|数据)。这些操作在计算上非常昂贵,是一个计算上难以处理的问题。

为了解决这个问题,有多种近似算法:

以下是主要的贝叶斯近似方法:

  • 变分推断:用更简单的分布近似后验
  • 马尔可夫链蒙特卡洛:通过采样近似后验
  • 最大后验估计:寻找最可能的参数值

最大后验估计

在上一讲中,我们看到了最大后验估计方法。这是对完全贝叶斯方法的一种近似。在最大后验学习中,我们最大化以下目标:

argmax_θ P(θ,x,y)

我们不关注θ给定数据的完整分布,而是尝试在联合模型下找到最可能的θ值。

现在,我将重点介绍MAP方法,并展示如何将本课程早期的一些算法推导为MAP学习方法的特例。

岭回归的贝叶斯推导

我将要推导为MAP方法特例的算法是岭回归。在岭回归中,我们拟合以下形式的线性模型:

ŷ = θᵀx

岭模型拟合L2正则化的均方误差,形式如下:

min_θ Σ(yᵢ - θᵀxᵢ)² + λ||θ||²

左侧是我们的均方误差,右侧是我们的正则化器,即参数向量θ的L2范数。通过惩罚其L2范数,我们强制参数θ变小。

现在让我们看看如何将岭回归解释为MAP估计的特例。与之前的视频相同,我们将从概率视角定义概率模型。我们将定义模型类别,并应用最大似然原理获得目标函数。然后我们将看到,这同样是岭回归的一个特例。

在模型本身方面,我们将使用相同的条件高斯分布。我们将使用之前看到的相同高斯方法,拟合给定x和θ的y的条件分布:

P(y|x,θ) = N(y; θᵀx, σ²)

这是一个高斯分布,其均值由x的线性函数参数化。

为了应用MAP方法,我们还需要定义θ的先验分布。我们将假设这是一个高斯先验,均值为0,方差为τ²:

P(θ) = N(θ; 0, τ²I)

这只是θ上的分布。应用高斯分布的定义,我们得到先验的以下表达式。现在我们可以应用最大后验方法来最大化以下目标函数,即我们联合分布的对数概率:

log P(θ,x,y) = log P(θ) + log P(x,y|θ)

代入我们之前使用的定义,我们得到以下形式。现在,我通过取出这些常数或乘法常数来简化这些项,因为我取了对数。我将它们放入这个加法常数项中,现在指数与对数相消,我得到以下表达式:

-Σ(yᵢ - θᵀxᵢ)²/(2σ²) - ||θ||²/(2τ²) + 常数

这与之前的形式相同,只是现在我有了这个额外的项θ²,它将被添加到我们的均方误差目标中。

现在你可以看到,这恰好是我们之前的均方误差方法,其中我们添加了一个高斯先验。这个高斯先验恰好对应于参数θ的平方值。如果θ是多维的,那么这将只是平方参数的和。

结论与总结

我们在这里看到的是,岭回归也是使用高斯先验执行MAP估计的特例,就像最小二乘法是最大似然的特例一样。

让我更精确地再次陈述这一点。我再次定义岭算法,这是我们早期视频中看到的相同定义。现在我只是添加了这个概率解释:通过拟合岭最小二乘模型,等价于使用MAP估计拟合条件高斯概率分布。

以同样的方式,采用概率方法并使用最大似然原理为我们提供了广泛的机器学习算法,这也是解释为什么我们对标准机器学习算法做出某些选择的方式。对于最大似然学习是这样,对于MAP学习或机器学习的贝叶斯方法也是如此。

本质上,我们既可以解释经典的机器学习算法使用贝叶斯或MAP方法,也可以推导新的算法。更一般地说,任何时候我们对模型参数应用某种先验,都很容易将其解释为正则化的一种形式。

这里的直觉是,我们有先验知识,我们事先理解模型的权重应该很小,好模型的权重很小。因此,我们将其编码为先验,并假设这是一个以零为中心的高斯先验。通过做出我们有先验的假设,我们从概率视角自然地推导出正则化。

例如,如果我们使用L1正则化,我们可以将其解释为在数据上使用拉普拉斯先验,这是另一个概率分布。因此,我们不是假设权重上的高斯先验,而是假设拉普拉斯先验,我们得到L1正则化。许多其他算法也会有类似的解释。

在本节课中,我想再次强调,概率方法是非常强大的思考机器学习模型的方式,既能解释机器学习模型的作用,也能推导新的模型和算法。

6.1:分类(第一部分)🎯

在本节课中,我们将要学习监督学习的另一个重要类型:分类问题。我们将回顾分类问题的定义,建立相关符号,并通过一个经典数据集来理解分类与回归问题的区别。


监督学习回顾

上一节我们介绍了监督学习的基本框架。为了应用监督学习,我们需要收集一个训练数据集,其中每个训练实例都是一个由目标值和输入特征组成的对。接着,我们指定一个学习算法来分析这个数据集,并生成一个预测模型。

回归与分类的区别

到目前为止,我们见过的所有算法都属于回归问题,其目标变量是连续的,取值于实数集。在本节中,我们来看看当目标变量是离散值时的情况,这被称为分类问题。

我们将目标变量 y 可能取值的集合记为 𝒴,其中 y₁, y₂, ..., yₖK 个可能的类别。

一个重要的特例是二元分类,即当 K = 2 时,只有两个类别。

示例数据集:鸢尾花

让我们通过一个经典数据集来具体理解分类问题。这个数据集由著名统计学家费舍尔于1936年创建,如今被广泛用于演示各种机器学习算法。

以下是该数据集的关键信息:

  • 数据集包含三种不同鸢尾花亚种的测量数据。
  • 特征包括花萼长度、花萼宽度、花瓣长度和花瓣宽度。
  • 目标变量是一个取值为0、1或2的数值,对应不同的花种。

我们可以将数据可视化。例如,选取前三个特征在三维空间中绘图,可以看到不同花种的数据点形成了各自的簇,例如 Iris-setosa(山鸢尾)明显与其他两种分开。

分类问题的直观理解

理解回归与分类的差异至关重要。

  • 在回归中,我们试图找到一条曲线或直线来拟合连续的目标值。例如,在糖尿病预测任务中,我们拟合了一条穿过BMI与糖尿病风险评分数据点的直线。
  • 在分类中,我们的目标是找到特征空间的一个划分,将不同类别的数据点分隔到不同的区域。我们寻找的是这些区域之间的决策边界

从概率角度看,分类模型输出的概率 P(Y) 可以简单地解释为给定数据点属于某个类别的可能性。

可视化决策边界

为了更清晰地展示,我们将鸢尾花数据投影到二维空间(使用前两个特征)。在这个二维图中,Iris-setosa 仍然清晰地自成一群,而另外两种花则较为相似地分布在另一区域。

现在,我们可以使用一个分类算法(例如逻辑回归)来拟合数据,尝试将 Iris-setosa 与其他花种分开。算法会找到一条线(决策边界),将特征空间划分为两个区域,分别对应两个类别。


本节课中我们一起学习了分类问题的基本概念,明确了其与回归问题的核心区别,并通过鸢尾花数据集直观地理解了特征空间划分和决策边界。在下一节中,我们将开始探讨具体的分类算法。

6.2:最近邻算法 👨‍🏫

在本节课中,我们将学习分类问题,并介绍第一个分类算法——最近邻算法。我们将从分类问题的基本设定开始,逐步深入理解最近邻算法的原理、实现细节及其优缺点。

分类问题回顾 📚

上一节我们介绍了分类问题的基本概念。本节中,我们来看看分类问题的具体设定。

假设我们有一个监督学习数据集,形式如下:

D = {(x₁, y₁), (x₂, y₂), ..., (xₙ, yₙ)}

其中目标变量 y 取离散值,来自 K 个可能的类别集合:

y ∈ {1, 2, ..., K}

这与我们之前看到的回归问题不同,在回归问题中 y 是连续变量。

最近邻算法介绍 🎯

现在让我们看看第一个适用于分类的算法——最近邻算法。这个算法看似简单,但实际上是机器学习中的重要算法。我们将用它来理解监督学习的一些理论特性,并在某些实际应用中发挥作用。

最近邻算法的定义如下:

假设我们有一个形式为上述的训练数据集 D。当我们收到一个新的查询点 x'(来自数据分布但不同于训练点)时,我们的目标是预测其标签 y'。

最近邻算法采用以下简单方法:

  1. 在数据集 D 中找到与查询点 x' 最接近的训练样本
  2. 返回该最近邻样本的标签

用数学公式表示:

y' = yᵢ,其中 i = argminᵢ distance(xᵢ, x')

距离度量 📏

我们尚未定义如何确定哪个点最接近查询点 x'。换句话说,我们需要定义数据点之间的相似性或距离概念。

以下是几种常用的距离度量:

欧几里得距离

这是最自然且广泛使用的距离度量,定义为 L2 范数:

distance(x, x') = ||x - x'||₂ = √(Σⱼ(xⱼ - x'ⱼ)²)

闵可夫斯基距离

这是欧几里得距离的推广,使用 p 范数:

distance(x, x') = ||x - x'||ₚ = (Σⱼ|xⱼ - x'ⱼ|ᵖ)^(1/p)

其中 p 通常取值在 1 到无穷大之间:

  • 当 p=2 时,即为欧几里得距离
  • 当 p=1 时,成为 L1 范数(曼哈顿距离)

汉明距离

当 x 和 x' 具有离散值时,我们可以使用汉明距离:

distance(x, x') = Σⱼ 1{xⱼ ≠ x'ⱼ}

即 x 和 x' 在不同分量上的数量。

K最近邻算法 🔢

在观察最近邻算法的实际应用时,我们可能会注意到决策边界可能不够平滑。为了解决这个问题,我们引入了 K最近邻算法。

K最近邻算法是最近邻算法的重要扩展,在实际应用中更为常用。它只对原始算法做了轻微修改:

  1. 找到查询点 x' 的 K 个最近邻,形成邻域集合 Nₖ(x')
  2. 输出这些数据点标签的某种共识

共识可以通过多种方式计算,包括:

  • 多数投票(分类问题)
  • 平均值(回归问题)
  • 加权平均值

用数学公式表示:

对于分类:

y' = mode({yᵢ : i ∈ Nₖ(x')})

对于回归:

y' = (1/K) Σᵢ∈Nₖ(x') yᵢ

算法特性分析 🔍

为了更好地理解 K最近邻算法,让我们回顾之前讲座中提到的数据分布概念。

数据分布是 x 和 y 上的概率分布 P(x, y)。我们的训练数据集是从这个分布中独立同分布采样得到的。

K最近邻算法实际上是在估计真实的数据分布。给定查询点 x',我们计算其邻域并考虑这些数据点的平均值,这实际上是条件概率 P(y|x') 的估计。

从高层次论证来看,这个近似等式成立是因为 y' 是期望值的蒙特卡洛估计。

算法总结 📊

以下是 K最近邻算法的主要特性总结:

算法类型:监督学习算法

适用问题:分类和回归均可使用

模型族:基于不同训练实例的共识

优化问题:在推理时通过最近邻搜索解决

概率解释:近似数据分布密度

优缺点分析 ⚖️

现在让我们简要讨论 K最近邻算法的优缺点:

优点 ✅

  1. 无限制模型形式:算法不假设任何限制性的模型形式
  2. 充分利用数据:使用整个数据集,如果有大量数据,可以充分利用而不压缩或限制
  3. 理论精度高:理论上可以任意好地拟合真实数据分布

缺点 ❌

  1. 计算挑战
    • 需要存储整个数据集
    • 需要有效的查询机制
  2. 维度灾难:在高维空间中,数据需求实际上很高

总结 🎓

本节课中我们一起学习了最近邻算法及其扩展 K最近邻算法。我们从分类问题的基本设定开始,详细介绍了算法的定义、距离度量的选择、K最近邻的改进以及算法的理论特性。

最近邻算法虽然简单,但为我们理解更复杂的机器学习算法奠定了基础。在接下来的课程中,我们将进一步探讨这些优缺点,并学习 K最近邻算法的一个重要特性和区别。

6.3:非参数模型 📊

在本节课中,我们将要学习非参数模型。上一节我们介绍了K近邻算法,本节中我们来看看什么是非参数模型,以及它与参数模型的区别。

参数模型回顾

在监督学习的课程中,我们定义了监督学习模型是一个函数 F,它将输入 x 映射到目标 y。模型通常具有参数 θ,我们用以下符号表示 F 由这些参数参数化:

F(x; θ) → y

这意味着每个参数实例化都定义了一个模型,定义了从输入 x 到目标 y 的特定映射。到目前为止,我们看到的所有监督学习算法示例都是这种参数模型的实例。

非参数模型定义

K近邻算法是我们遇到的第一个无法用一组参数以这种形式表达的模型。K近邻实际上是一种更通用的模型类型,它不与一组参数相关联,这使其成为非参数模型。我将更精确地定义什么是非参数模型。

以下是参数模型与非参数模型的核心区别:

  • 参数模型:模型类的复杂度(或自由度)是固定的,不随数据集大小增加而增加。例如,线性模型的参数维度 D 是固定的。
  • 非参数模型:模型类的复杂度(或自由度)会随着数据集大小的增加而持续增加。K近邻算法就是一个典型例子。

K近邻算法回顾

K近邻算法使用一个包含 N 个训练样本的训练数据集 D。在预测时,我们收到一个查询点 x',然后算法找到 K 个与 x' 最接近的训练样本,我们称这个样本集合为邻域 N。接着,K近邻算法返回这些训练样本目标值的平均值,或者该邻域中最频繁的目标值,更一般地说,是该邻域中的某种共识标签或共识目标。

算法核心步骤

  1. 给定查询点 x'
  2. 计算 x' 与训练集中所有点的距离。
  3. 选择距离最近的 K 个点作为邻域 N
  4. 对邻域 N 中的目标值进行聚合(如平均或投票),得到预测结果。

非参数模型的优势与劣势

非参数模型(如K近邻)通常具有以下特点:

  • 优势:由于在训练时从不将训练数据集压缩成更小且固定的参数集,因此不会丢失任何信息。随着收集更多数据,模型类的复杂度持续增加,可以提供更准确的预测。
  • 劣势:计算复杂度高。需要存储整个数据集,并在预测时直接在其上进行计算(如搜索),因此存储和计算需求都更大。此外,更容易过拟合训练集,需要仔细选择超参数(如邻域大小 K)以避免过拟合。

总结

本节课中我们一起学习了非参数模型的概念。我们回顾了K近邻算法,并明确了参数模型与非参数模型的关键区别:参数模型的复杂度固定,而非参数模型的复杂度随数据量增长。K近邻作为典型的非参数模型,其优势在于信息保留完整,劣势在于计算开销大且需注意过拟合风险。理解这一区别有助于我们在不同场景下选择合适的机器学习算法。

6.4:逻辑回归 📊

在本节课中,我们将学习一种新的监督学习分类算法——逻辑回归。逻辑回归将解决最近邻算法的许多局限性,并且是分类问题中参数化方法的一个例子。逻辑回归是监督学习中最重要的算法之一,是每个人工具箱中必备的工具。

分类问题回顾

在监督学习中,我们有一个形式为 (X, Y) 的训练数据集。如果目标变量 Y 是离散的,即取有限个可能类别中的一个,我们称之为分类问题。具体来说,如果类别数量等于两个,我们称之为二分类问题。本节课我们将专注于二分类问题,逻辑回归正是针对二分类设计的算法。在下一讲中,我们将看到如何将逻辑回归扩展到多类别。

我们将以鸢尾花数据集作为运行示例。该数据集中的每个数据点代表一朵花,我们收集了每朵花的多个测量值,如花瓣长度和花瓣宽度。在本例中,我们将两个非山鸢尾花类型合并,目标是区分山鸢尾花与非山鸢尾花。图中蓝色点代表山鸢尾花,棕色点代表非山鸢尾花。

最小二乘法的局限性

一种潜在的参数化分类方法是使用最小二乘法。最小二乘模型假设监督学习模型的形式为 f(x) = θ^T x,我们通过最小化均方误差来拟合模型。在理论上,我们可以将其视为纯粹的线性回归问题。

然而,这种方法存在几个问题。首先,模型的输出 f(x) 没有限制在0和1之间,而我们知道二分类的标签只能是0或1。其次,这种方法在实践中无法提供最佳性能。例如,在鸢尾花数据集中,线性回归得到的决策边界虽然能分离两个类别,但存在误分类点,且边界附近的点分类不明确。因此,这不是最佳解决方案。

逻辑回归模型

在本节中,我们将介绍一种新的模型 f,它将输入和目标变量联系起来。该模型的形式为 f(x) = σ(θ^T x),其中 θ^T x 是我们的线性模型,σ 是Sigmoid函数或逻辑函数。Sigmoid函数将实数映射到0和1之间的区间,其形式为:

σ(z) = 1 / (1 + e^{-z})

Sigmoid函数具有以下性质:

  • 当输入 z 趋近于正无穷时,输出趋近于1。
  • 当输入 z 趋近于负无穷时,输出趋近于0。
  • 当输入 z 为0时,输出为0.5。
  • 其导数具有方便的形式:σ'(z) = σ(z) * (1 - σ(z))

将输出限制在0和1之间对于二分类问题非常合理。我们可以将输出解释为属于类别1的概率,而属于类别0的概率则为 1 - σ(θ^T x)

概率解释与最大似然估计

为了定义逻辑回归的目标函数,我们采用分类问题的概率方法。我们可以将模型的输出 σ(θ^T x) 解释为给定输入 x 时,目标 y 为1的概率。这定义了一个参数化的伯努利分布:

P(y | x; θ) = (σ(θ^T x))^y * (1 - σ(θ^T x))^(1-y)

接下来,我们应用条件最大似然原理来推导目标函数。最大似然估计的目标是选择参数 θ,使得从数据分布中采样的 (x, y) 具有高概率。最大化该目标等价于最小化模型预测分布与真实数据分布之间的KL散度。

对于给定的数据集 D,似然函数为所有数据点概率的乘积。我们通常使用其对数形式,即对数似然,也称为对数损失或交叉熵目标:

L(θ) = Σ_{i=1}^{n} [y_i * log(σ(θ^T x_i)) + (1 - y_i) * log(1 - σ(θ^T x_i))]

模型、目标函数(以及即将介绍的优化器)共同定义了逻辑回归算法。需要注意的是,尽管名为“回归”,逻辑回归实际上是一个分类算法,这是一个历史遗留的误称。

梯度下降优化

为了优化对数似然目标函数,我们将使用梯度下降法。梯度下降是一种通用的优化算法,通过反复沿目标函数 J(θ) 的负梯度方向更新参数:

θ := θ - α * ∇J(θ)

其中 α 是学习率。

我们需要计算对数似然函数的梯度。通过对数似然函数求导,我们可以得到梯度的表达式。经过推导,梯度向量的形式为:

∇L(θ) = Σ_{i=1}^{n} (σ(θ^T x_i) - y_i) * x_i

有趣的是,这个表达式与均方误差的梯度形式非常相似,都包含了预测值与真实值之差与输入 x 的乘积项。不过,由于这里的函数 f 是Sigmoid函数,其具体值是不同的。

算法实现与结果

我们可以轻松地在Python中实现梯度下降算法来优化逻辑回归。在鸢尾花数据集上运行逻辑回归后,算法找到了一个线性的决策边界来完美分离两个类别。与之前线性回归的结果相比,逻辑回归的分类效果更好,所有点都被正确分类。

关于逻辑回归,有几点需要注意:

  1. 逻辑回归找到的决策边界是线性的,这是由其模型形式决定的。
  2. 与最小二乘法不同,逻辑回归没有闭式解,因此必须使用梯度下降等数值优化方法。
  3. 逻辑回归可以概率视角理解为拟合一个参数化的伯努利分布。

总结

本节课我们一起学习了逻辑回归,这是一种适用于二分类问题的监督学习算法(尽管名称带有“回归”,它实为分类算法)。我们介绍了由线性决策边界构成的模型族,并使用最大似然估计(即交叉熵损失)进行训练。我们通过梯度下降法优化目标函数,并从概率视角将其理解为拟合参数化的伯努利分布。

在下一讲中,我们将探讨如何将逻辑回归扩展到多类别分类问题。

6.5:Softmax回归 🧮

在本节课中,我们将学习如何将二分类的逻辑回归算法,扩展为能够处理任意数量类别(K类)的多分类算法。我们将重点介绍一种名为Softmax回归的算法,它直接输出多类概率,并保留了我们之前讨论过的概率解释。


从二分类到多分类

上一节我们介绍了用于二分类的逻辑回归算法。本节中,我们来看看如何将其推广到多分类问题。

一种简单且在实践中常用的方法是“一对多”方法。这种方法为每个类别训练一个分类器,用于区分该类与所有其他类别。虽然这种方法有效,但从概念上讲,它并不完全令人满意,特别是它失去了逻辑回归原有的有效概率解释。

然而,对于逻辑回归,我们可以拟合一个不同的模型,它是逻辑回归的推广,能直接输出多类概率。


Softmax函数:逻辑函数的推广

为了将逻辑回归推广到多类,我们需要一个能将K维输入映射为K维概率向量的函数。这个函数称为Softmax函数。

以下是Softmax函数的定义。它是一个向量值函数,输出一个包含K个概率的向量。其第k个分量的计算公式如下:

\[\sigma(\mathbf{z})_k = \frac{e^{z_k}}{\sum_{j=1}^{K} e^{z_j}} \]

这个函数分两步工作:

  1. 首先,对输入向量 \(\mathbf{z}\) 的每个分量应用指数函数 \(e^{z_k}\),确保所有分量非负。
  2. 然后,将所有指数化的分量进行归一化(即除以它们的总和),使得输出向量的所有分量之和为1,从而形成一个有效的概率分布。

与逻辑函数的关系

让我们看看当类别数K=2时,Softmax函数的特殊情况。

此时,第一个类别的概率形式可以简化为:

\[P(y=1) = \frac{1}{1 + e^{-z_2}} \]

(这里我们通过设定 \(z_1 = 0\) 来消除参数冗余,因为概率分布对缩放不变。)

你可以看到,这个形式与我们之前使用的逻辑函数(Sigmoid函数)非常相似。这表明Softmax函数本质上是Sigmoid函数在多分类场景下的自然推广。


定义Softmax回归模型

在二分类的逻辑回归中,我们的模型形式是 \(f(\mathbf{x}) = \sigma(\theta^T \mathbf{x})\),它将线性组合 \(\theta^T \mathbf{x}\) 通过Sigmoid函数映射为一个概率。

在K分类的Softmax回归中,我们的模型将输出K个概率。模型为每个类别k分配一个参数向量 \(\theta_k\)。给定输入 \(\mathbf{x}\),模型计算每个类别的“分数” \(\theta_k^T \mathbf{x}\),然后通过Softmax函数将这些分数转换为概率:

\[P(y=k | \mathbf{x}; \theta) = \frac{e^{\theta_k^T \mathbf{x}}}{\sum_{j=1}^{K} e^{\theta_j^T \mathbf{x}}} \]

模型的参数现在是K组 \(\theta\) 向量,每组对应一个类别。


训练目标与优化

与逻辑回归类似,我们可以从概率角度为Softmax回归定义训练目标。

我们将Softmax模型的输出解释为一个分类分布的参数。分类分布是伯努利分布(二项分布)在多类别上的推广。然后,我们应用最大似然估计原则来寻找最优参数。

训练目标是最大化训练数据在该模型下的似然概率。通过优化这组参数 \(\theta\),我们就得到了Softmax回归算法。这个优化通常使用梯度下降法来完成。


应用示例:鸢尾花数据集

让我们将Softmax回归应用于完整的鸢尾花数据集(包含三个类别)。

以下是使用Python的scikit-learn库实现Softmax回归的示例代码:

from sklearn.linear_model import LogisticRegression

# 创建Softmax回归模型(在scikit-learn中,LogisticRegression默认支持多分类)
model = LogisticRegression(multi_class='multinomial', solver='lbfgs')
# 拟合模型
model.fit(X_train, y_train)
# 进行预测
predictions = model.predict(X_test)

拟合模型后,我们可以可视化其决策边界。Softmax回归找到的是线性决策边界,在特征空间中用直线(或超平面)分隔不同的类别。


总结 📝

本节课中我们一起学习了Softmax回归算法。我们来总结一下关键点:

  • 算法定位:Softmax回归是一种用于多分类问题的监督学习算法。
  • 核心思想:它是逻辑回归在多分类上的直接推广,通过Softmax函数将多个线性分数转换为概率分布。
  • 模型特点:模型寻找数据之间的线性决策边界。
  • 训练目标:其目标函数(可称为Softmax损失)是最大似然估计的特例,通常使用梯度下降法优化。
  • 概率解释:可以从概率角度将其解释为参数化了一个分类分布。

Softmax回归因其概率解释的优雅性和有效性,成为多分类问题中的一个基础且重要的模型。

7.1:生成模型(第一部分)🎯

在本节课中,我们将学习一种新的机器学习方法——生成模型。我们将探讨生成模型与之前学过的判别模型之间的核心区别,并通过一个具体的例子来理解生成模型的工作原理。

概述 📋

在之前的课程中,我们学习了多种监督学习算法,特别是分类算法。本节课,我们将引入一种定义机器学习算法的通用方法,称为生成方法。这种方法的一个特例,将为我们之前见过的分类问题提供一套新的监督学习算法。

生成模型的概念 🤔

首先,我们来定义什么是生成模型。

回想一下我们之前对监督学习的定义:应用监督学习涉及指定一个包含标记示例的训练数据集,以及一个学习算法。当学习算法与训练集结合时,我们得到一个预测性的监督学习模型。这个学习算法可以看作由三个不同的部分组成:

  • 模型类:所有可能模型的集合。
  • 目标函数:根据模型对数据的拟合程度,对模型类中的所有模型进行排序。
  • 优化器:根据我们的目标函数选择最佳模型的算法。

为了定义一套新的、被称为生成算法的学习算法,我们将从定义一个包含生成模型的特定模型类开始,稍后再讨论目标函数和优化器。

概率模型的回顾 📊

在之前的课程中,我们讨论了如何从概率的角度定义机器学习模型。我们从一个最通用的概率机器学习模型定义开始:

一个参数化的概率模型是一个函数,它将每一组输入 X 和输出 y 映射到一个概率值 P(X, y; θ),其中 θ 是模型的参数。

这些模型的思想是,如果我们的数据来自某个数据分布,那么我们的模型也应该是一个分布,然后我们可以尝试去近似这两个分布。

我们看到,最大似然估计 是拟合这类概率模型的一种方法。尽管我们在这里拟合的是关于 Xy 的联合模型,我们仍然可以通过查看给定 Xy 的条件分布来计算预测。我们可以通过以下公式从完整模型中获取这个条件分布:

P(y | X; θ) = P(X, y; θ) / P(X; θ)

这只是概率论中的一个简单恒等式。

判别模型 vs. 生成模型 ⚖️

概率模型与条件概率模型之间的区别非常重要,它构成了两类重要算法的基础:生成式判别式

  • 生成模型:拟合输入 X 和目标 y 的联合分布 P(X, y) 的模型。
  • 判别模型:拟合条件分布 P(y | X) 的模型。逻辑回归就是这类模型的一个例子。

这两种模型有什么区别?为什么区分它们很重要?

  1. 模型形式不同:它们是参数化两种完全不同概率的函数。生成模型需要所有 (X, y) 对的概率之和为1,而判别模型只需要对于每个 X,所有 y 的条件概率之和为1。
  2. 训练目标不同:应用最大似然估计时,通常会得到不同的目标函数,从而导致不同的预测结果。
  3. 用途不同:如果我们只关心预测,可能会使用一类模型;如果我们关心生成数据或建模 X 的其他属性,则会使用生成模型。

一个具体例子:鸢尾花数据集 🌸

为了让这个区别更具体,让我们再次看看之前用于演示分类算法的鸢尾花数据集。

该数据集包含数百个数据点,每个点对应一朵花,具有萼片长度、宽度等测量特征,以及一个表示花类型的标签(0, 1, 2)。

判别式方法

在该数据集上,判别模型(如逻辑回归或Softmax回归)会尝试直接将特征空间划分为与每个类别相关联的区域。给定特征 X,判别模型会直接映射到预测的类别 y。这就像一个函数,在特征空间的每个点上直接输出类别。

这相当于用决策边界将特征空间分割成不同的区域。

生成式方法

生成式方法会做一些略有不同的事情。在鸢尾花的例子中,生成模型会首先为每种类型的花建立一个模型,描述该类花看起来是什么样子。

换句话说,对于每个类别 y,我们会尝试估计概率 P(X | y)。这可以看作是在给定类别下,花朵特征 X 是如何“生成”的模型。

现在,给定一朵新花 X',我们不是直接将其映射到一个类别,而是将其与我们已有的三个花卉模型进行匹配。然后,我们确定 X' 与哪个模型最相似。我们将 X' 的标签指定为与其最匹配的花卉模型的标签。

数学上,这意味着我们进行以下操作:我们关心在模型下最可能的类别 y。我们使用贝叶斯规则来转换这个表达式:

argmax_y P(y | X) = argmax_y P(X | y) * P(y) / P(X)

由于 P(X) 对于所有 y 是相同的,因此最可能的类别由下式给出:

argmax_y P(X | y) * P(y)

在实践中,我们通常假设先验概率 P(y) 是均匀的,或者从数据中估计。因此,我们基本上会输出使得 P(X | y) 最高的类别 y。换句话说,我们计算数据点在每个花卉类别下的概率,并输出给出该点最高出现概率的类别。

总结与比较 📝

那么,生成式和判别式这两种方法,哪个更好?我们该用哪个?

  • 如果只关心预测:我们并不真的需要一个关于 X 的模型。判别模型只建模 P(y | X),而生成模型还建模 P(X | y)。如果我们不建模不需要的部分,就能用更少的建模假设来更精确地解决我们关心的问题。在实践中,这通常意味着预测更可能准确。
  • 如果关心其他任务:例如生成新数据样本、处理缺失值,或者我们知道真实的数据生成过程并且可以构建更准确的生成模型,那么在这些情况下,我们会希望使用生成式方法。

本节课,我们一起探讨了生成模型的基本概念及其与判别模型的核心区别。我们了解到,生成模型通过建模数据的联合分布 P(X, y) 来工作,并利用贝叶斯规则进行分类预测。选择哪种方法取决于我们的具体任务:纯预测任务可能更适合判别模型,而涉及数据生成或理解数据本身的任务则可能受益于生成模型。

在下一个视频中,我们将看到第一个非玩具示例的生成算法。

7.2:第二部分:高斯判别分析 🧠

在本节课中,我们将学习第一个非玩具示例的生成模型,即高斯判别分析。我们将首先定义该模型,然后探讨如何将其拟合到数据中,以及使用何种目标函数和优化算法。


分类问题回顾 📊

上一节我们介绍了分类问题的基本设定。在监督机器学习中,我们有一个包含输入 x 和目标 y 的训练数据集。如果目标 y 是连续的,我们面临回归问题;如果 y 是离散的,即取 K 个可能值之一,我们则面临分类问题。我们的目标是预测新输入 x 的真实类别。


生成模型与判别模型 🔄

在上一节中,我们看到了两种解决分类问题的方法,主要区别在于我们拟合的模型类型。监督学习算法涉及选择模型,特别是概率模型。我们可以使用两种概率模型:

  1. 生成模型:对 xy 的联合分布进行建模,形式为 p(x, y)
  2. 判别模型:仅对给定 xy 的条件分布进行建模,形式为 p(y | x)

这两种方法代表了机器学习算法中一个非常重要的区别。在本节中,我们将定义一个生成模型的具体示例。


高斯混合模型介绍 🎲

在定义生成模型之前,我们先介绍高斯混合模型的概念。

高斯混合模型是一种概率分布,其形式如下:

p(x) = Σ_{k=1}^{K} φ_k * N(x | μ_k, Σ_k)

其中:

  • N(x | μ_k, Σ_k) 是均值为 μ_k、协方差矩阵为 Σ_k 的高斯分布的概率密度函数。
  • φ_k 是分配给每个高斯分布的权重,介于0和1之间,且所有 φ_k 之和为1。

这本质上是 K 个高斯分布的加权平均,φ_k 定义了每个高斯分布的混合比例。


可视化高斯混合模型 📈

在一维情况下,我们可以轻松可视化高斯混合模型。例如,考虑两个高斯分量的混合:

  • 第一个高斯分量:均值 μ_1 = 1,标准差 σ_1 = 0.5,权重 φ_1 = 0.6
  • 第二个高斯分量:均值 μ_2 = -1,标准差 σ_2 = 0.5,权重 φ_2 = 0.4

混合后的概率密度函数将有两个峰值,分别对应两个高斯分量的中心。这个混合分布本身也是一个有效的概率分布,其积分总和为1。


高斯判别分析模型 🧩

我们将使用高斯混合模型的概念来定义一个生成模型,作为高斯判别分析算法的基础。

为了定义联合分布 p_θ(x, y),我们需要定义两个部分:

  1. 类别 y 的分布:我们假设 y 服从分类分布。这意味着 y 取第 k 个类别的概率是 φ_k

    p(y = k) = φ_k
    
  2. 给定类别 yx 的条件分布:对于每个类别 k,我们假设 x 服从一个多元高斯分布,具有特定的均值 μ_k 和协方差矩阵 Σ_k

    p(x | y = k) = N(x | μ_k, Σ_k)
    

将这两部分结合起来,我们就得到了 xy 的联合分布,它正是一个高斯混合模型

p(x, y) = Σ_{k=1}^{K} φ_k * N(x | μ_k, Σ_k)

这就是高斯判别分析算法的主要建模假设。


数据生成的故事 📖

理解这个模型的一种方式是将其视为一个数据生成过程的故事:

  1. 首先,从分类分布中抽样一个类别 y。我们有 K 个可能的类别,其比例由 φ_k 给出。
  2. 然后,根据抽到的类别 k,从对应的高斯分布 N(x | μ_k, Σ_k) 中抽样一个数据点 x

如果我们回到之前的高斯混合模型示例,可以想象我们反复抛一枚硬币来决定是从橙色还是蓝色的高斯分布中抽样,然后绘制一个点。重复多次后,所有点的分布将呈现出混合模型的形状。

这种用故事定义生成模型的方法非常有趣,许多生成模型都可以通过这种数据生成的故事来理解和构建。


在鸢尾花数据集上的示例 🌸

现在,让我们看一个在更实际数据上应用高斯混合模型的例子——鸢尾花数据集。

该数据集包含三种鸢尾花的四个测量值。如果我们只取前两个维度进行可视化,数据点呈现出三个类别的聚类。


定义并采样模型数据 🎯

我们尝试为这个数据集拟合一个生成模型。首先,我们凭直觉猜测模型的参数,然后从模型中抽样数据,并与真实数据进行比较,以验证模型是否合理。

我们假设的参数如下:

  • 类别比例 φ_k:由于有三个类别,我们假设均匀分布,即每个 φ_k = 1/3
  • 均值 μ_k:通过目视检查二维图,我们为三个类别分别设定均值。
  • 协方差矩阵 Σ_k:我们为每个分布假设一个对角协方差矩阵,并缩放一个较小的因子(如0.05)。

定义了这些参数后,我们可以轻松地从模型中生成数据:

  1. 首先抽样一组随机的类别标签 y
  2. 然后根据每个类别对应的高斯分布抽样数据点 x

可视化与比较 👁️

将生成的样本数据与真实数据一起可视化,我们可以看到:

  • 从“蓝色”类别生成的样本点大致落在真实蓝色数据点的区域。
  • 从“浅棕色”和“深棕色”类别生成的样本点也分别落在对应真实数据的近似区域。

这表明,即使只是通过目视检查并凭直觉选择参数,我们的模型也能生成与真实数据结构相似的数据。这证明了该模型能够捕捉数据中某些相关的模式。


总结 📝

在本节课中,我们一起学习了:

  1. 高斯判别分析是一种生成式监督学习算法。
  2. 其核心是假设数据由高斯混合模型生成,即每个类别的数据服从一个特定的高斯分布,而类别本身服从一个分类分布。
  3. 该模型可以理解为一个数据生成过程:先抽样类别,再根据该类别的分布抽样特征。
  4. 通过一个简单的示例,我们展示了即使使用直观猜测的参数,该模型也能生成与真实鸢尾花数据集结构相似的样本数据。

在下一节中,我们将探讨如何从数据中学习这些模型参数,从而形成一个完整的学习算法。

7.3:高斯判别分析(学习)🎯

在本节课中,我们将学习高斯判别分析算法的第二部分,即如何通过最大似然估计来学习模型的参数。上一节我们介绍了高斯判别分析所使用的概率模型,本节中我们来看看如何从数据中拟合这个模型。

概述

高斯判别分析是一种用于分类的生成式模型。它假设数据由K个高斯分布混合生成,每个类别对应一个高斯分布。我们的目标是学习这些高斯分布的参数(均值、协方差矩阵)以及每个类别的先验概率。

模型回顾

我们有一个带标签的训练数据集,输入为 X,目标为 yy 可以取K个可能的离散值(类别)。模型的联合概率分布定义为:

P(x, y) = P(y) * P(x|y)

其中:

  • P(y) 是一个分类分布(Categorical Distribution),参数为 φ_k,表示类别k的先验概率。
  • P(x|y=k) 是一个多元高斯分布,参数为均值 μ_k 和协方差矩阵 Σ_k

因此,完整的模型参数集 θ 包含所有的 μ_kΣ_kφ_k

最大似然估计

由于我们定义了一个概率模型,学习参数的自然方法是最大似然估计。我们希望找到参数 θ,使得模型对观测到的训练数据赋予最高的概率。

我们的目标是最大化对数似然函数:

L(θ) = Σ_{i=1}^{n} log P(x_i, y_i; θ)

利用模型的分解形式,我们可以将对数似然重写为:

L(θ) = Σ_{k=1}^{K} [ Σ_{i: y_i=k} log P(x_i|y_i=k; μ_k, Σ_k) + Σ_{i: y_i=k} log P(y_i=k; φ_k) ]

这个分解非常关键。它表明,对数似然函数可以分解为多个项的和,而每个项只依赖于一部分参数(例如,φ_k 只出现在第二项中,μ_kΣ_k 只出现在对应类别的第一项中)。

这意味着我们可以分别优化每组参数,而无需考虑其他参数。这极大地简化了优化过程。

学习先验概率 φ_k

为了找到最优的 φ_k,我们只需要优化对数似然中与 φ 相关的部分:

L(φ) = Σ_{k=1}^{K} [ Σ_{i: y_i=k} log φ_k ]

通过求导并令导数为零,我们可以得到一个闭式解。最优的 φ_k 是训练数据中属于类别k的样本比例:

φ_k = (属于类别k的样本数) / (总样本数 n)

这个结果非常直观:类别的先验概率就是它在数据中出现的频率。

学习高斯参数 μ_k 和 Σ_k

类似地,为了找到最优的 μ_kΣ_k,我们只需要优化对数似然中与第k类数据相关的部分:

L(μ_k, Σ_k) = Σ_{i: y_i=k} log N(x_i; μ_k, Σ_k)

这等价于仅使用第k类的数据来拟合一个高斯分布。其最大似然解同样有闭式形式:

  • 均值 μ_k 是第k类所有样本的均值。
  • 协方差矩阵 Σ_k 是第k类所有样本的协方差矩阵。

进行预测

学习到模型参数后,对于一个新的输入 x,我们使用贝叶斯规则来预测其类别 y

P(y=k | x) ∝ P(x | y=k) * P(y=k)

我们计算 x 在每个类别的高斯分布下的概率,乘以该类别的先验概率,然后选择概率最高的类别作为预测结果。

实践示例:鸢尾花数据集

让我们将高斯判别分析应用于经典的鸢尾花数据集。该数据集包含三种鸢尾花的测量值。

以下是拟合模型的核心代码逻辑(概念展示):

# 对于每个类别 k
for k in range(K):
    # 获取属于类别 k 的所有数据点 X_k
    X_k = X[y == k]
    # 计算均值 μ_k
    mu[k] = np.mean(X_k, axis=0)
    # 计算协方差 Σ_k
    sigma[k] = np.cov(X_k.T)
    # 计算先验概率 φ_k
    phi[k] = len(X_k) / len(X)

预测函数的核心逻辑如下:

def predict(x):
    # 计算每个类别的后验概率(未归一化)
    probs = []
    for k in range(K):
        # P(x|y=k) 使用多元高斯概率密度函数
        likelihood = multivariate_normal.pdf(x, mean=mu[k], cov=sigma[k])
        # P(y=k) * P(x|y=k)
        probs.append(phi[k] * likelihood)
    # 返回概率最高的类别
    return np.argmax(probs)

将学习到的高斯判别分析模型应用于整个特征空间并可视化,我们可以得到决策边界。在这个例子中,由于每个类别使用不同的协方差矩阵,决策边界是二次的(Quadratic),因此这种方法也被称为二次判别分析

高斯判别分析的特殊形式

高斯判别分析有几个著名的特例:

  • 线性判别分析:强制所有类别共享同一个协方差矩阵(Σ_1 = Σ_2 = ... = Σ_K)。这会导致线性的决策边界。
  • 高斯朴素贝叶斯:假设每个类别的高斯分布的协方差矩阵是对角矩阵,即特征之间相互独立。
  • 二次判别分析:即我们刚刚讨论的标准形式,每个类别有自己的协方差矩阵,产生二次决策边界。

生成式模型 vs. 判别式模型

最后,我们来简要讨论何时选择生成式模型(如GDA)或判别式模型(如逻辑回归)。

判别式模型的优势

  • 通常对数据做出更少的假设,只建模 P(y|x),不建模 P(x)。因此模型错误指定的风险更小。
  • 在拥有大量数据时,通常能获得更好的预测性能。现代深度学习应用大多属于此类。

生成式模型的优势

  • 能够生成新的数据样本 x(例如生成图像、文本、分子)。
  • 可以处理缺失特征。如果某些特征值缺失,可以利用 P(x|y) 来推断它们。
  • 如果对数据生成过程 P(x|y) 有准确的先验知识,并将其融入模型,可能会获得更优的性能。
  • 对于许多有趣的生成模型(如GDA),最大似然估计有闭式解,训练更简单、更快速。

总结

本节课中我们一起学习了高斯判别分析算法的完整流程:

  1. 我们定义了一个生成式概率模型,该模型假设每个类别的数据服从一个高斯分布。
  2. 我们使用最大似然估计来学习模型参数。
  3. 得益于模型的对数似然函数可以分解,我们得到了参数 φ_kμ_kΣ_k闭式解,无需复杂的迭代优化。
  4. 我们了解了如何使用贝叶斯规则进行预测,并在鸢尾花数据集上进行了实践。
  5. 我们讨论了GDA的特例(LDA,朴素贝叶斯)以及生成式与判别式模型的优缺点。

在下一讲中,我们将继续探索生成式算法,并介绍一个非常重要且广泛应用的新模型:朴素贝叶斯。

8.1:文本分类与朴素贝叶斯 🧠

在本节课中,我们将学习一种新的分类算法——朴素贝叶斯。我们将从文本分类这一实际应用问题入手,介绍如何将文本数据转换为机器学习模型可以处理的特征,并最终应用朴素贝叶斯算法进行分类。


回顾生成式建模

上一讲中,我们开始讨论一种新的监督学习方法——生成式建模。我们介绍了第一个生成式算法的例子。本节中,我们将继续这个话题,学习另一个非常重要的算法——朴素贝叶斯


文本分类问题 📄

在介绍算法之前,我们先从一个实际应用问题开始——文本分类。文本分类是一个非常重要的实际问题,也是引入朴素贝叶斯算法的绝佳场景。

分类问题回顾

在过去的几讲中,我们一直在研究分类问题。在分类中,我们有一个包含输入和目标变量的训练数据集。目标是离散的,可以取K个可能的值,这些值被称为K个类别。我们当前所处的场景就是分类。

文本分类的特殊性

本节中,我们将关注一个特定的分类问题——文本分类。这是一个值得研究的问题,因为许多实际应用问题都属于文本分类的范畴。例如:

  • 垃圾邮件过滤是一个文本分类问题。
  • 基于欺诈性金融声明的欺诈检测,即将金融声明分类为有效或欺诈。
  • 分析医疗记录并将其分类,例如判断患者是否健康。

这些都是文本分类的有趣例子,也是机器学习实际应用中经常遇到的问题。

这个问题与我们之前看到的问题有所不同。不同之处在于:

  1. 我们现在分类的是单词序列。这些序列的长度可以不同,我们可以分类不同的句子(每个句子都不同),甚至可以分类非常长的文档。
  2. 问题的维度可能非常大。回想一下,x是一个具有特定维度的向量。现在,如果x是一个序列,其中序列的每个元素(单词)都可以取大量可能的值,那么从某种意义上说,这个问题的维度非常大。这意味着我们必须思考如何将现有算法应用于这个数据集,这需要一些额外的工作。


20新闻组数据集示例 📰

为了展示文本分类问题的样子,我将使用机器学习中一个著名的数据集——20新闻组数据集

这是一个文本分类问题的实例,其中包含大约20,000个文档,这些文档大致均匀地收集自20个不同的在线新闻组。每个新闻组涵盖一个不同的主题。你可以把它们想象成在线论坛的帖子,每个论坛都有不同的主题。这些主题包括医学、计算机图形学、宗教等,涵盖的主题非常广泛。

在这个例子中,分类问题是确定一条消息来自哪个新闻组,这本质上等同于对消息中讨论的主题进行分类。这个数据集在机器学习中被广泛用于对各种算法进行基准测试,也用于演示目的。

加载数据

我们同样可以从Scikit-learn库中加载这个数据集。在Scikit-learn的datasets模块中,有一个函数可以让我们轻松加载不同的数据集。我将使用这个函数来加载20新闻组数据集。

为了简化,我将只关注以下四个主题:无神论、基督教(特定宗教)、计算机图形学和医学。我将加载这四个主题的数据。

加载后,我们的目标变量集合现在由这四个类别组成:无神论、计算机图形学、医学、基督教。

数据示例

这是一个来自数据集的邮件示例(索引为3的输入)。你可以看到这是一条发布到基督教数据集的消息。发帖人正在撰写一篇关于天主教会在波兰作用的论文,并正在寻求有关其研究主题的信息。

这里我们看到了分类问题输入的一个例子。这个单词序列(甚至是字符序列)对应着我们模型的输入X。这就是X的样子。因为我们只加载了四个主题,所以当前数据集中大约有2,000个数据点。


文本特征化 🔧

现在让我们看看如果尝试将分类算法应用于这个数据集会发生什么。如前所述,我们遇到的第一个问题是,这个数据中的每个输入现在都是一个任意长度的字符序列。

我们迄今为止看到的所有监督学习算法都假设我们有一个D维的特征向量φ(x)。因此,为了应用我们现有的监督学习算法,我们需要为这段文本提出某种特征化方法。那么,我们可以使用什么作为特征呢?

以下是几种可能的思路:

  1. 手工设计特征:我们可以基于文本定义手工设计的特征。例如,如果我们知道要分类的消息与宗教、图形学或医学有关,那么我们可以利用这些先验知识来手工设计自己的特征。例如,一个有用的特征可能是“消息是否包含‘church’这个词”。或者,邮件是否来自美国境内或境外,邮件中的组织是否是大学,发件人是否签名,签名是男性名字还是女性名字。这些可能都是有用的特征。通过查看数据并思考,我们可以提出自己手工设计的一组特征。这可能会给我们带来非常好的特征,但创建这些特征需要大量工作。那么,有没有更好、更简单、更自动化的方法呢?

  1. 基于词汇表的通用特征:一种通用的定义特征的方法是查看每个可能的单词,并计算它出现的次数,或者用一个指示器表示它是否出现。就像我们之前对“church”这个词所做的那样,但我们可以对字典中的每个可能的单词都这样做。我们会有一个特征:“消息是否包含‘aardvark’”,另一个特征:“消息是否包含‘apple’”,依此类推,直到“消息是否包含‘zebra’”。所有可能的单词。这实际上是一个非常强大的想法,我稍后会详细讨论。

  2. 专用算法:最后,我想强调的是,也存在一些专门的算法,可以直接处理字符序列。在要点1和要点2中,我提出了定义固定数量D个特征的方法,然后我们可以将这些特征输入到我们的监督学习算法中。但我也想指出,存在一些更现代的、基于神经网络的算法,属于深度学习领域,它们甚至可以直接接收原始的、未经特征化的字符序列。然而,在本讲中,我们暂时不会研究这第三类算法。我们将专注于如何创建固定数量的特征D。


词袋模型 👜

我将采用的方法将受到第二个要点(基于词汇表的通用特征)的启发。我将提出一种定义特征的方法,这种方法在机器学习中非常重要,并且是文本分类最标准的方法之一。这种方法的名字叫做词袋模型

让我们看看词袋模型是什么意思。

一步:定义词汇表

创建词袋特征或词袋表示的第一步是定义一个所谓的词汇表。词汇表是所有对我们来说可能感兴趣的单词的集合。这可以是字典中的全部单词集合,或者如果我们处于某个更专业的医学领域,那么它可能是一个医学词汇表。但本质上,这是我们宇宙中存在的、为我们正在解决的机器学习问题而存在的所有可能单词的集合。

二步:构建特征向量

词袋表示的思想是定义以下一组特征,这也是我之前提到的。在词袋表示中,我们定义一组特征φ,其数量等于我们词汇表的大小。因此,这个向量φ的维度等于我们词汇表V的维度。

所以φ(x)是一个很长的列向量(因为词汇表通常很大)。在每个位置上,我都有一个0或1。如果消息X包含我们词汇表中的第一个单词(例如“church”),那么第一个条目就是1,否则为0。第二个条目包含1,如果我们有单词“doctor”,否则为0。对于“fervently”也是如此。在我们的词汇表中,我们假设单词有某种顺序。对于每个单词,我们将在该向量中找到一个索引,并在该索引处放置一个二进制指示器,表示单词是否出现在消息X中。

这是一个特征化的例子。对于大多数单词,比如“church”、“fervently”、“purple”,这些词没有出现,但我们有单词“doctor”出现了。现在,这可能告诉我们一些信息,它可能是一个迹象,表明这个文档X与医学有关。

在20新闻组数据集上的示例

我们可以使用Scikit-learn轻松地为我们计算这些词袋特征。我们将使用一个名为CountVectorizer的对象,它本质上会为我们计算这组特征。

在这里,我将数据转换为特征化矩阵X_train。现在X_train具有以下形状:2000 x 35000。2000是我们训练集中的样本数量,35000是维度。所以CountVectorizer识别出了一个大小为35000的词汇表,我们数据集的每个元素在这种情况下都被表示为一个35000维的向量。

为了更具体,我可以使用这个CountVectorizer对象来检索与每个单词关联的向量中的索引。例如,单词“church”的索引是8609,单词“computer”的索引是9338。我可以回到之前加载的那条消息(索引3),查看这条消息是否包含某些单词。X_train是一个大约2000 x 35000的矩阵。我可以打印出这个矩阵在索引3(我们消息的索引)和单词“church”的索引处的值。同样,我可以查看单词“computer”、“doctor”、“important”的索引处的值。对于单词“church”,这条消息的向量化表示中有一个1(确实为1,因为“church”这个词确实存在)。“computer”为0(所以单词“computer”在这里没有找到),“doctor”没有找到,“important”找到了。如果你查看这条消息,你会发现“important”确实在这里。所以你可以看到,这种向量表示确实有效,并给出了我们想要的结果。


词袋模型的实践考虑 ⚙️

词袋模型是机器学习中一个非常重要的思想,每当我们进行文本分类时,在大多数情况下,词袋模型是表示文本文档的一种非常标准和常见的方法。在实践中,这项技术有一些细微的修改。我给了你高层次的概念,但在应用这项技术时,你应该注意一些实际的考虑因素。

  1. 词频 vs. 词存在:通常,特征化表示φ(x)的第j个分量不仅包含单词是否存在的二进制指示器,还包含该单词在文档中出现的次数。这是我之前展示的词袋模型版本的一个微小变化。这两种方法在实践中都有使用,你需要尝试哪一种对你的数据效果更好。

  2. 文本预处理:被特征化的原始文本通常不仅仅是文本本身,而是经过处理的版本。例如,一种常见的处理技术称为词干提取,这意味着我们只考虑单词的词干,而不是单词本身。例如,如果我们的数据中有单词“slowly”和“slowness”,两者都会被提取词干,我们只保留“slow”。我们只会在词汇表中使用“slow”,而“slowly”和“slowness”都会映射到“slow”。这只是为了确保我们的数据集中没有太多冗余的单词。

  3. 过滤停用词和罕见词:过滤停用词(如“the”、“a”、“and”)也非常常见。罕见词也经常被排除,或者我们会应用某种后处理来处理它们。这些都是重要的实际考虑因素,还有其他因素,但我只是想让你了解一下这是什么。


应用分类算法 🚀

现在我们有了这些词袋特征,我们可以应用任何现成的分类算法来进行文本分类。例如,我们可以使用之前看到的逻辑回归方法。

在这里,我从Scikit-learn导入逻辑回归,在这一行中实例化它,然后将其拟合到特征化的数据集(即具有词袋表示的矩阵)。这样,逻辑回归就被训练好了。

现在我们可以使用这个模型来预测新的输出。这里我创建了一组关于不同主题的新文档。我对这个数据集应用了特征化。然后在下一行,我生成预测。现在我们看到这里我们有正确的类别。


总结 📝

本节课中,我们一起学习了文本分类的基本概念,并介绍了词袋模型这一强大的文本特征化方法。我们了解到,为了将文本数据输入到标准的机器学习算法中,需要将其转换为固定维度的数值特征向量。词袋模型通过构建词汇表并为每个单词创建二进制或计数特征来实现这一点。最后,我们看到了如何将处理后的特征应用于逻辑回归等分类算法。这为下一节正式引入朴素贝叶斯算法——一个特别适合高维稀疏数据(如文本)的分类器——奠定了基础。

8.2:朴素贝叶斯(第二部分) 🧠

在本节课中,我们将要学习朴素贝叶斯算法。上一节我们介绍了文本分类问题及其特征表示方法,本节中我们来看看如何为这类问题构建一个专门的生成式模型。

概述

朴素贝叶斯算法是一种用于分类的生成式模型,特别适用于文本分类等具有高维、离散特征的问题。它通过一个关键的简化假设,使得模型在计算上变得可行。

文本分类问题回顾

我们的核心问题是文本分类。这是一个分类问题,其中输入 x 是一个单词或字符序列,长度可变且维度很高。我们需要将 x 分类到特定类别中。实际应用包括垃圾邮件过滤、欺诈检测和医疗记录分类等。

应用机器学习算法的主要挑战在于如何将输入 x 转化为特征。我们提出的方法是词袋特征表示法

以下是词袋特征表示法的定义:

  • 给定输入 x 和一个词汇表 V(我们考虑的所有单词的集合)。
  • x 的特征表示是一个长向量,每个单词对应一个分量。
  • 该分量的值为 0 或 1,取决于该单词是否出现在 x 中。

例如,如果词汇表是 [“church”, “doctor”, “patient”],而输入句子是 “doctor patient”,那么其特征向量就是 [0, 1, 1]

生成式模型与判别式模型

我们已见过两类监督学习算法:判别式模型(如逻辑回归)和生成式模型(如高斯判别分析)。它们的区别在于是否对 xy 的联合分布进行建模。

生成式模型的思想是:我们学习每个类别下数据点 x 的分布模型。当需要对新数据点 x' 进行预测时,我们计算它在每个类别模型下的概率,并报告概率最高的类别。

数学上,这可以定义为:
y_hat = argmax_y P(y | x) = argmax_y P(x | y) * P(y)

高斯判别分析是生成式模型的一个例子。它假设给定 yx 的条件概率是一个多元高斯分布(正态分布),而类别分布是一个分类分布。这定义了一个高斯混合模型。

将 GDA 应用于文本分类的问题

如果我们尝试将高斯判别分析算法应用于文本分类,会立即遇到一个问题:我们的数据(词袋表示)是离散的,而 GDA 模型假设数据服从连续的正态分布。因此,这个模型是“错误设定”的。

我们可以尝试通过假设条件分布是分类分布来解决这个问题。然而,这带来了另一个问题:如果我们的特征向量维度 D 非常大(例如,词汇表有 10000 个词),那么 x 可能取值的数量是 2^D(2 的 10000 次方),这是一个天文数字。用分类分布来建模 x,意味着我们需要指定 2^D - 1 个参数,这在计算和存储上都是不可能的。

朴素贝叶斯假设

朴素贝叶斯算法通过一个关键的简化假设解决了这个高维问题,这个假设被称为朴素贝叶斯假设

为了处理高维的 x,我们假设给定 yx 的分布可以按以下形式分解:
P(x | y) = ∏_{j=1}^{D} P(x_j | y)

这意味着,给定类别 y,特征向量 x 中每个分量(例如,每个单词是否出现)的概率是相互独立的。x 的整体概率只是各个分量概率的乘积。

具体到我们的二进制词袋表示例子中,每个分量 x_j 给定 y 的概率可以用一个伯努利分布来建模。伯努利分布只有一个参数(单词出现的概率)。因此,整个 P(x | y) 的分布就是 D 个伯努利分布的乘积。

现在,我们只需要 D 个参数(而不是 2^D - 1 个)来指定这个分布。这是一个巨大的简化,使得算法可以扩展到非常大的词汇量。

当然,这个假设做了很大的简化(例如,“医生”和“病人”这些词在现实中通常是相关的),但实践表明,这种算法的经验性能通常很好。

伯努利朴素贝叶斯模型

基于朴素贝叶斯假设,我们现在可以定义朴素贝叶斯算法。首先定义其模型。

与高斯判别分析策略相同,我们将定义一个关于 y 的概率分布,以及一个给定 yx 的条件概率分布。

  1. 类别先验分布:与 GDA 相同,是一个分类分布。

    • P(y = k) = φ_k
  2. 条件似然分布:与 GDA 使用高斯分布不同,这里我们使用基于朴素假设的伯努利分布乘积。

    • P(x | y = k) = ∏_{j=1}^{D} P(x_j | y = k)
    • 其中,每个 P(x_j | y = k) 都是一个伯努利分布。

因此,伯努利朴素贝叶斯模型的完整数学形式是:
P(x, y) = P(y) * P(x | y) = (Categorical(y; φ)) * (∏_{j=1}^{D} Bernoulli(x_j; ψ_{jk}))

这里,模型参数包括分类分布的参数 φ 和所有伯努利分布的参数 ψ_{jk}(对于第 k 个类别,第 j 个单词出现的概率)。参数总数大约是 K * D,这是可管理的。

总结

本节课中我们一起学习了朴素贝叶斯算法的核心思想。我们回顾了文本分类问题,指出了直接将高斯判别分析等模型应用于离散高维数据时遇到的问题。为了解决参数爆炸的难题,朴素贝叶斯引入了条件独立性假设,将高维联合分布分解为多个一维分布的乘积,从而极大地简化了模型。最后,我们定义了伯努利朴素贝叶斯模型的具体形式。在下一节中,我们将探讨如何从数据中学习这个模型的参数。

8.3:朴素贝叶斯(学习)📚

在本节课中,我们将要学习如何从数据中学习朴素贝叶斯模型。我们将回顾模型定义,推导其最大似然估计的闭式解,并通过一个文本分类的实例来演示其应用。


模型回顾与学习目标 🎯

上一节我们介绍了朴素贝叶斯算法及其使用的模型。本节中,我们来看看如何从数据中学习这个模型的参数。

朴素贝叶斯算法的动机问题是文本分类。这是一个分类问题,其输入是单词或字符序列,我们需要对这些序列进行分类。该问题有许多有趣的应用,例如垃圾邮件过滤或欺诈检测。

将机器学习算法应用于文本分类的主要挑战在于找到良好的特征表示。因为每个输入 x 只是一个单词或字符序列,我们不清楚如何像之前所有算法那样将其转换为固定长度的特征向量。一种实现方式是使用词袋表示法。这意味着我们的特征向量是一个很长的列向量,其长度对应于词汇表中的单词数量。每个可能的单词都有一个对应的索引分量,该分量的值为 01,取决于该单词是否出现在文本 x 中。

我们为这类数据提出了一个特定模型,其中 x 是二元特征向量,y 是类别。该模型包含两个部分:

  1. 类别 y 的概率分布,这是一个分类分布,与高斯判别分析中的相同。

  2. 给定类别 k 时数据 x 的条件概率,其形式如下:

    P(x | y=k) = ∏_{j=1}^{D} Bernoulli(x_j; ψ_{jk})
    

    这是 D 个伯努利分布的乘积,每个伯努利分布都有一个参数 ψ_{jk},我们总共有 D × K 个这样的参数。由于这个分布在各个分量上可分解(即看到每个单词的概率与其他单词是否出现独立),我们称其为朴素贝叶斯假设,这也是算法名称的由来。


最大似然估计推导 🔍

现在,我们如何学习这个模型?因为我们有一个概率模型,所以有一个原则性的方法来推导这些模型的目标函数,即最大似然原理。这是我们的目标,我们寻找能使来自数据分布的观测数据具有高概率的模型参数 θ

为了使内容更具体,我们的参数集 θ 将包含之前看到的所有 φ 和所有 ψ。所有这些参数共同构成了模型参数 θ

那么,如果我们对这个问题应用最大似然原理会发生什么?这里的情况与我们为高斯判别分析算法所做的工作非常相似。

以下是其对数似然的展开。由于我们的概率由 P(y)P(x|y) 两部分组成,我可以将它们分开。在下一行中,我使用了一种不同的方式来枚举所有数据点:首先枚举所有类别 k,然后枚举所有属于特定类别 k 的点。由于朴素贝叶斯假设,每个 P(x|y) 在不同的分量上可分解,因此 P(x|y) 的对数实际上是每个分量 j 的对数概率之和。这就是第二个求和项的来源。

现在,我要做一个与我们在研究高斯判别分析时相同的观察:涉及特定参数的所有项都只出现在求和式的一个小子集中。

例如,所有涉及参数 φ_k 的项都在这里。这是涉及 φ_k 的所有项的集合,而另一边的所有项都不涉及 φ_k。因此,如果我想优化这个模型,我只需要优化这一项。类似地,对于每个参数 ψ_{jk},涉及该参数的项也只在求和式的一个特定子集中。如果我想找到最大似然估计的 ψ_{jk},我只需要关注求和中的这一小部分项。

这是一个非常有力的观察,它将再次为我们提供模型参数的闭式解。


参数闭式解 📝

重申一下,如果我们想找到某个参数 ψ_{jk} 的最大值,我可以忽略除这一小部分项之外的所有项,只需要最大化这个特定的项集。对于参数 φ 也是如此,我只需要查看对数似然的这一部分,即这个子集。

那么,当我们优化这个模型时会发生什么?由于我们有这些简化的假设,我们可以再次为参数推导出一个非常简单的闭式解。

首先,对于参数 φ_k,我们有一个与之前相同的形式,推导过程也完全相同。这是因为我们正在优化的这部分模型与高斯判别模型中的形式完全相同,因此结果也相同。

这个公式表示,φ_k 简单地估计为训练数据中属于类别 k 的样本比例。

类似地,我们可以遵循相同的方法推导出参数 ψ_{jk} 的值。如果你自己想推导,只需取我之前幻灯片上定义最大似然 ψ_{jk} 的表达式。这是一个伯努利分布的对数概率,你可以代入伯努利分布的定义,求导,令导数为零,然后解出 ψ_{jk},就会得到以下表达式:

ψ_{jk} = (∑_{i: y_i=k} x_{ij}) / N_k

其中,N_k 是类别为 k 的数据点数量,分子是类别为 k 且单词 j 出现的数据点数量。这又是一个非常直接的定义。本质上,ψ_{jk} 是在类别 k 中看到单词 j 的概率,我们将其估计为训练集中类别 k 的文档中出现单词 j 的比例。

这为我们提供了 ψφ 的闭式表达式。现在,我们既指定了训练目标(对数似然),也指定了优化过程,使我们能够获得该模型的最大似然参数。


进行预测与实例演示 🚀

我想再次提醒,如果我们想查询模型以进行特定预测,可以使用以下应用了贝叶斯规则的方程:

P(y=k | x) ∝ P(y=k) * P(x | y=k)

等式右边两项 P(y)P(x|y) 我们都有表达式,它们都涉及伯努利分布。这里的直觉是,给定一个新文档 x,我查看它在我为每个类别建立的模型下的概率,然后返回使 x 具有最高概率的类别。也就是说,我选择最能解释这个新数据点的类别。

现在,为了说明这一点,我将使用在第一讲中介绍的20个新闻组数据集。

以下是使用该数据集的应用步骤。首先,我使用之前展示的特征提取器来计算特征,输出是一个维度约为2000×1000的矩阵 X_train,其中2000是训练样本数,1000是特征数。

现在,让我们计算该数据集上参数的最大似然估计。与高斯判别分析一样,这段代码非常简短,这也是生成式算法的优势之一。计算这些闭式表达式的代码量很小。

我初始化了 φ 矩阵和 ψ 矩阵。对于每个类别 k,我查看属于该类别的数据子集,该类别的向量 ψ_k 就是该子集中每个单词出现的次数。因为这个矩阵是二元的,我可以简单地沿数据点轴取平均值,来计算训练数据中每个单词出现的平均次数。类似地,对于 φ_k,它只是类别 k 的样本数除以数据中的总样本数。

为了计算预测,我们实现了贝叶斯规则的一个形式。这是一个非常简单的Python函数。我在这里做了一些重塑操作以向量化这些运算。我计算了类别 y 的对数概率和给定 yx 的对数概率。现在,我可以通过应用贝叶斯规则(结合这两项)来计算给定 xy 的对数概率。我在对数空间中操作,因为这些概率通常非常小,为了避免处理计算机可能无法有效表示的极小数字,我将所有内容转换为对数尺度,这使得算法在数值上更稳定。

现在,我可以在我们的训练数据集上调用这个函数来获得一组预测,这些就是为该数据集预测的类别。

让我们看看如何应用这个算法。我有一段输入文本“OpenGL on the GPU is fast”,我使用scikit-learn的特征提取器来计算其特征表示,然后运行我的朴素贝叶斯预测函数来计算其目标。确实,它预测这段文本对应于“图形”主题的数据集。


总结 📋

本节课中我们一起学习了朴素贝叶斯算法的学习过程。

我介绍了一种称为伯努利朴素贝叶斯的算法。它是监督学习算法的一个实例,解决了多类分类问题。该模型将数据建模为伯努利分布的混合(或者更准确地说,是在朴素贝叶斯假设下各维度完全分解的分类分布的混合)。该模型使用对数似然进行优化,并且与高斯判别分析一样,存在闭式解。

尽管这个伯努利朴素贝叶斯模型非常简单,但在许多数据集上,它的性能仍然接近最先进水平。直到最近几年,基于神经网络的方法才开始超越朴素贝叶斯,但在很长一段时间里,它都是一个难以超越的强大基准。因此,即使这是一个简单的算法,它的效果也非常好。

此外,它还能非常容易地扩展到非常大的数据集。你刚才看到训练这个算法非常简单,我们只需要进行一些矩阵运算。因此,如果我们有一个词汇量非常大的大型数据集,我们可以利用解非常简单的特点,在非常大的数据集上运行它。即使是逻辑回归这样的算法,由于涉及数值优化过程,其扩展性也可能不如朴素贝叶斯好。这是另一个原因,说明在处理文本分类问题时,朴素贝叶斯是一个值得尝试的好算法。

9.1:分类间隔 📏

在本节课中,我们将学习机器学习中一个非常重要的算法家族——支持向量机。支持向量机是一种可以应用于多种问题的算法,包括回归、分类,甚至某些类型的无监督学习。本节课我们将首先介绍支持向量机背后的核心思想:分类间隔。

背景与问题设定

上一节我们介绍了支持向量机的基本概念。本节中,我们来看看支持向量机所针对的具体问题背景。

我们将专注于二元分类问题。在二元分类中,我们有一个监督学习数据集,包含输入 X 和目标 Y。目标 Y 取两个可能值之一。在本讲中,我们将引入新的符号表示:用 +1-1 来表示这两个类别,这会使后续的数学推导更简洁。

我们将考虑的模型类别是线性模型。模型形式如下:

公式: f(x) = θ^T x + θ_0

这里,θ 是权重向量,θ_0 是偏置项。我们特意将偏置项 θ_0 与权重向量 θ 分开表示,以简化推导。

示例数据集:鸢尾花

为了有一个贯穿本讲的运行示例,我们将使用之前见过的鸢尾花数据集

该数据集包含每朵花的四个特征(植物各部分的测量值)。我们将鸢尾花 Setosa 类别标记为 +1,其他两个类别合并标记为 -1。下图展示了一个数据子集的可视化,蓝色点代表 Setosa 花(+1),棕色点代表其他花(-1)。

选择最佳决策边界 🤔

考虑鸢尾花数据集。我们之前已经见过多种可用于此数据集分类的机器学习方法。

每种算法都会计算出一个分离两个类别的决策边界,而不同的算法会输出不同的边界。例如,逻辑回归会输出一条线,高斯判别分析会输出一个不同的超平面。

一个关键问题是:我们如何确定哪个边界是最好的?特别是在这个例子中,两个类别是线性可分的,这意味着存在无限多个能完美分离数据的超平面。那么,我们如何从这无限多个可能的超平面中进行选择呢?

为了说明这个问题,请看以下示例。我们使用三种不同的算法(逻辑回归、感知机、Ridge分类器)拟合同一数据集,它们给出了显著不同的边界。

你可以看到这些线的斜率不同,边界的位置也不同。有的边界非常靠近棕色点,有的则靠近蓝色点。那么,哪个超平面最好呢?

间隔的直观理解 🛡️

支持向量机算法的核心思想是选择具有高间隔的边界。支持向量机的基本前提是:我们应该选择具有最大可能间隔的决策边界,这意味着边界应尽可能远离数据点。

间隔可以理解为分离超平面与最近数据点之间的距离。我们希望最大化这个间隔,将分离边界放置在尽可能远离数据点的位置。

让我通过一个例子来具体说明这意味着什么。

考虑以下分离超平面。这个超平面存在问题,因为它非常靠近棕色数据集。事实上,这个棕色点几乎就在决策边界上。

现在,想象如果我们稍微扰动这个数据集,或者测量中存在微小误差,这个棕色点很容易越过边界并被错误分类。同样,在中间的例子中,超平面也非常靠近蓝色点,轻微的扰动也会导致误分类。

相比之下,左侧的超平面距离数据点较远,具有高间隔。如果我们扰动数据点或超平面,由于存在误差裕度,分类结果仍然可能是正确的。因此,这个超平面看起来更稳定,对误差和扰动更具鲁棒性。

这就是为什么从无限多个能完美分离数据的超平面中,我们可能希望选择像这样具有高间隔的超平面的直观原因。这个直观论证为寻找高间隔超平面的算法(如支持向量机)提供了依据。

在下图中,高间隔超平面(黑色实线)与最近的数据点(黑色圆圈标出)之间有健康的间隔区域(虚线之间)。这个区域的宽度就是间隔的大小,我们希望使其尽可能大。

形式化定义间隔 📐

我们已经对如何找到好的分离超平面有了直观理解。现在,我们来看看如何将这个直觉形式化。第一步是给出间隔更正式的定义。

由于我们的目标 y+1-1,我们可以使用以下定义:一个数据点的间隔是其预测值的大小。预测值 f(x) 是一个数字。如果这个数字很大,我们预测为正类;如果非常负,则预测为负类。

为了确保间隔值为正,我们将其乘以 y。因此,任何被正确分类的点都会有一个正的间隔。我们希望这个值很高。

公式: γ̃_i = y_i * f(x_i) = y_i * (θ^T x_i + θ_0)

我声称这是一个好的间隔定义。如何验证呢?

考虑两种情况:

  1. 假设一个点 x_i 的真实标签是正的(y_i = +1)。如果模型的分数 f(x_i) 非常大且为正,那么这个间隔也会很大。这意味着我们非常确信这是一个正类点,或者说该点远离超平面(分数为0时正好在边界上)。所以,高分意味着高置信度和高间隔。
  2. 反之,如果真实标签是负的(y_i = -1),当分数趋向负无穷时,间隔最大化。这意味着我们以高置信度将其分类为负类。

因此,更高的间隔意味着更高的分类置信度,也意味着该点距离决策边界更远。

几何间隔:解决缩放问题 ⚖️

然而,上述定义存在一个问题。如果我们取参数 θθ_0 并乘以一个正标量 α,得到新参数。由于 α 为正,它不会改变分配给每个点的分数的符号,因此不会改变预测的标签。但是,我们定义的间隔却被缩放了 α 倍。

这意味着我们可以仅通过重新缩放参数来任意增大间隔,而没有真正改变决策边界的位置或分配的标签。这是一个不好的特性。

为了解决这个问题,我将稍微修改之前的定义,引入几何间隔的概念。

公式: γ_i = y_i * (θ^T x_i + θ_0) / ||θ||

这里,我通过 θ 的范数 ||θ|| 对分数进行了归一化。现在,如果我们重新缩放 θ,由于进行了归一化,我们不能再任意增大间隔了。这个缩放技巧不再有效。

这个几何间隔的定义既保留了之前的直观含义,又解决了缩放问题。这将是我们在本讲中使用的间隔定义。

几何间隔的几何解释 📏

为了让你确信这是一个好的定义,我将从另一个角度——几何角度——推导这个公式。这也解释了为什么我称之为“几何”间隔。

我给出的几何间隔公式 γ_i 实际上精确地对应于点 x_i 到分离超平面的距离

如何证明这一点呢?论证并不复杂。

首先,为了简化推导,假设真实标签 y_i = +1(对于 y_i = -1,论证类似)。然后基于两个观察:

  1. 决策边界上的点满足方程:θ^T x + θ_0 = 0。这是一个多维向量空间中描述超平面的公式。
  2. 向量 θ / ||θ|| 垂直于这个分离超平面,并且其范数为1(根据我们的构造,因为除以了范数)。

从几何上看,从点 x_i 到超平面的最短路径(垂线)方向正是单位法向量 θ / ||θ|| 的方向。设这个最短距离为 γ_i,那么边界上离 x_i 最近的点 x_0 可以表示为:
x_0 = x_i - γ_i * (θ / ||θ||)

由于 x_0 在超平面上,它满足 θ^T x_0 + θ_0 = 0。将 x_0 的表达式代入并求解 γ_i,我们得到:
γ_i = (θ^T x_i + θ_0) / ||θ||
这正是我们之前定义的几何间隔公式(当 y_i = +1 时)。

因此,几何间隔 γ_i 就是点 x_i 到决策超平面的欧几里得距离。这进一步证实了我们定义的合理性。

我们可以用代码绘制出这些表示间隔的线段,其长度正是最近点到超平面的距离。

总结

本节课我们一起学习了支持向量机算法的核心动机——最大化分类间隔

首先,我们明确了在二元分类问题中,当数据线性可分时,存在无数个分离超平面。为了从中选出最鲁棒、最稳定的一个,我们引入了间隔的概念。间隔越大,决策边界离数据点越远,模型对数据扰动和测量误差的容忍度就越高。

接着,我们形式化地定义了间隔。最初的简单定义 γ̃_i = y_i * f(x_i) 虽然直观,但存在参数缩放导致间隔任意增大的问题。为此,我们引入了几何间隔 γ_i = y_i * (θ^T x_i + θ_0) / ||θ||。这个定义不仅保留了高间隔代表高分类置信度的直观意义,还通过归一化权重向量的范数解决了缩放问题。更重要的是,我们证明了几何间隔在数值上等于数据点到决策超平面的几何(欧几里得)距离,这为其命名和重要性提供了坚实的几何解释。

现在,我们已经建立了寻找高间隔分类器的目标。在接下来的课程中,我们将基于这个目标,推导出能够实际计算出最大间隔超平面的算法——即支持向量机

9.2:最大间隔分类器 🎯

在本节课中,我们将继续学习最大间隔分类器。上一节我们介绍了间隔的直观概念和数学定义,本节中我们将探讨如何通过优化算法找到具有最大间隔的超平面。

概述

在二分类问题中,我们使用线性模型进行数据划分。模型形式如下:

公式y = sign(θ^T x + θ₀)

其中目标变量 y 取值为 -1+1θ 是参数向量,θ₀ 是偏置项。

上一节我们定义了数据点 x 到超平面的间隔公式:

公式margin(x) = y * (θ^T x + θ₀) / ||θ||

我们的目标是找到使所有数据点最小间隔最大化的超平面参数。

优化问题推导

初始优化问题

以下是寻找最大间隔超平面的第一种优化问题表述:

公式

maximize γ
subject to y_i(θ^T x_i + θ₀) / ||θ|| ≥ γ, for all i

其中 γ 是所有数据点间隔的下界(最小值)。我们同时优化 θθ₀γ,目标是最大化最小间隔。

问题简化

初始优化问题存在两个主要困难:

  1. 约束条件高度非线性(包含除法运算)
  2. 存在冗余的自由度

为了解决第二个问题,我们注意到如果将所有参数乘以正常数 c,间隔值保持不变。这意味着我们可以通过添加约束来消除这种冗余。

我们添加约束 ||θ|| = 1/γ,这总是可以通过适当的缩放实现。将这个关系代入原问题,得到简化形式:

公式

maximize 1/||θ||
subject to y_i(θ^T x_i + θ₀) ≥ 1, for all i

最终优化形式

最大化 1/||θ|| 等价于最小化 ||θ||。为了计算方便,我们使用平方范数并添加系数 1/2,得到最终优化问题:

公式

minimize (1/2)||θ||²
subject to y_i(θ^T x_i + θ₀) ≥ 1, for all i

这是一个二次优化问题,具有线性约束条件,比原始问题更容易求解。

支持向量机算法

通过上述推导,我们得到了支持向量机(SVM)分类算法的基本形式:

以下是该算法的关键特征:

  • 模型类型:线性决策边界
  • 学习类型:监督学习,二分类
  • 优化目标:最大化间隔
  • 问题形式:二次规划问题

这个公式为我们提供了第一个可实际求解的最大间隔分类器。后续课程中,我们将看到这个算法的其他变体。

总结

本节课中我们一起学习了最大间隔分类器的优化问题推导。我们从直观的间隔最大化目标出发,通过一系列数学变换,将复杂的非线性优化问题转化为易于求解的二次规划问题。最终得到的支持向量机算法框架为线性分类问题提供了坚实的理论基础和实用的解决方案。

9.3:软间隔与合页损失 🛡️

在本节课中,我们将继续学习支持向量机(SVM)。我们将探讨之前方法的一些重要局限性,并介绍如何通过引入软间隔和合页损失来解决这些问题。最终,我们将得到一个更强大、更实用的SVM优化问题形式。

回顾硬间隔SVM

上一节我们介绍了如何通过优化问题找到最大化间隔的决策边界。该优化问题如下:

公式:

minimize (1/2) * ||θ||^2
subject to y_i * (θ^T x_i + θ_0) >= 1, for all i

这个公式要求所有数据点都被正确分类,并且其函数间隔至少为1。同时,我们最小化权重θ的范数,这解决了函数间隔的缩放问题。

然而,这个公式有一个重要的局限性:它假设数据是线性可分的。在现实中,大多数分类问题并不存在一个能完美分隔两个类别的超平面。

引入软间隔

为了处理非线性可分的数据,我们需要允许一些数据点违反“函数间隔至少为1”的约束。为此,我们引入一个松弛变量 ξ_i。

以下是引入松弛变量后的优化问题:

公式:

minimize (1/2) * ||θ||^2 + C * Σ ξ_i
subject to y_i * (θ^T x_i + θ_0) >= 1 - ξ_i, for all i
           ξ_i >= 0, for all i

这里,C是一个超参数,用于控制对违反约束的惩罚强度。如果C很大,模型会尽量避免违反约束;如果C很小,模型则允许更多的违反。

推导合页损失形式

通过分析松弛变量ξ_i的最优值,我们可以将上述带约束的优化问题转化为一个无约束的形式。

观察发现,对于给定的θ,最优的松弛变量ξ_i可以表示为:

公式:

ξ_i = max(0, 1 - y_i * (θ^T x_i + θ_0))

为了简化表示,我们引入符号 (z)_+ = max(0, z)。因此,ξ_i = (1 - y_i * (θ^T x_i + θ_0))_+

将这个表达式代回目标函数,并经过一些代数变换(例如,将常数项C吸收到正则化系数λ中),我们可以得到SVM的最终无约束优化形式:

公式:

minimize Σ (1 - y_i * (θ^T x_i + θ_0))_+ + λ * ||θ||^2

这个公式非常强大。它由两部分组成:

  1. 损失函数(1 - y_i * f(x_i))_+,这被称为合页损失
  2. 正则化项λ * ||θ||^2,这是我们熟悉的L2正则化器。

因此,最大化间隔的SVM问题等价于最小化合页损失加上L2正则化

深入理解合页损失

现在,让我们更仔细地看看合页损失。对于一个预测值 f 和真实标签 y(取值为+1或-1),合页损失定义为:

公式:

L_hinge(y, f) = max(0, 1 - y * f)

为了直观理解,假设真实标签 y = 1。那么损失变为 max(0, 1 - f)。其特性如下:

  • 当预测 f >= 1 时,损失为0。这意味着预测不仅正确,而且“足够自信”。
  • 当预测 f < 1 时,损失为正。预测越偏离正确方向(即越接近或小于0),损失越大。

下图对比了合页损失与其他常见损失函数(假设y=1):

代码/图示描述:

L2损失: (1 - f)^2
L1损失: |1 - f|
合页损失: max(0, 1 - f)
  • L2损失(橙色):在f=1处最小,呈抛物线形状,对远离1的预测给予较大惩罚。
  • L1损失(蓝色):在f=1处最小,呈V形,惩罚与误差的绝对值成正比。
  • 合页损失(绿色):当 f >= 1 时,损失为0;当 f < 1 时,损失线性增长。其形状像一个“合页”,因此得名。

合页损失的关键洞察是:它只惩罚那些使预测朝向错误类别的误差。如果一个预测在正确类别上“过于自信”(f > 1),它不会受到惩罚。这使得它对异常值不那么敏感。

合页损失的性质与总结

以下是合页损失的主要性质:

  • 对异常值鲁棒:由于它不惩罚“过于正确”的预测,因此受异常点的影响较小。
  • 诱导大间隔:最小化合页损失(加上正则化)天然地促使模型寻找具有较大间隔的决策边界。
  • 不可微点:合页损失在 y*f = 1 处有一个“折点”,在此处不可微。这可能在理论上带来一些优化挑战,但在实践中,有成熟的方法(如次梯度下降)可以很好地处理。
  • 缺乏概率解释:与均方误差(对应高斯分布均值)或绝对值误差(对应拉普拉斯分布中位数)不同,合页损失没有直接的概率模型解释。但这通常不影响其在实际中的出色表现。

本节课总结:
在本节课中,我们一起学习了支持向量机从硬间隔到软间隔的演进。通过引入松弛变量,我们使SVM能够处理非线性可分的数据。更重要的是,我们通过数学推导,将带约束的SVM优化问题转化为了一个最小化合页损失加L2正则化的无约束优化问题。合页损失因其只惩罚“有害”误差、诱导大间隔和对异常值鲁棒的特性,成为一个非常强大且广泛使用的分类损失函数。在下一讲中,我们将探讨如何优化这个目标函数。

9.4:支持向量机优化(第四部分)🎯

在本节课中,我们将学习支持向量机(SVM)的优化方法,特别是基于合页损失(Hinge Loss)的无约束优化问题。我们将从上一节介绍的合页损失公式出发,探讨如何通过梯度下降法来优化这个目标函数,并最终实现一个完整的SVM算法。

模型与损失函数回顾

上一节我们介绍了SVM的一种新形式,它涉及一个无约束优化问题,其核心是最小化合页损失。让我们先回顾一下这个模型。

我们处理的是线性模型,其向量化形式为:
[
f(x) = \theta^T x
]
其中,目标变量 ( y ) 是二元的,取值为 -1 或 1。

合页损失函数定义为:
[
\ell_{\text{hinge}}(y, f(x)) = \max(0, 1 - y \cdot f(x))
]
这个损失函数具有有趣的特性:它类似于L1损失,但只惩罚那些导致错误分类的预测。具体来说,如果预测值 ( y \cdot f(x) < 1 ),则会产生线性惩罚;如果 ( y \cdot f(x) \geq 1 ),则惩罚为零。

SVM的目标可以看作是优化一个正则化的合页损失:
[
J(\theta) = \sum_{i=1}^{n} \max(0, 1 - y^{(i)} \cdot f(x^{(i)})) + \frac{\lambda}{2} |\theta|^2
]
其中,( \lambda ) 是正则化参数。

梯度下降法简介

在之前的课程中,我们使用梯度下降法来优化其他算法的参数。梯度下降是一种迭代算法,我们从参数的某个初始猜测开始,然后反复应用以下更新步骤:
[
\theta_{\text{new}} = \theta_{\text{old}} - \eta \nabla J(\theta_{\text{old}})
]
其中,( \eta ) 是学习率,( \nabla J(\theta) ) 是目标函数 ( J ) 在 ( \theta ) 处的梯度。通过沿着负梯度方向移动一小步,我们可以确保目标函数值减小。

合页损失的梯度

现在,我们来看看如何将梯度下降法应用于合页损失。合页损失函数有两个不同的区域,这取决于预测值 ( y \cdot f(x) ) 是否小于1。

线性区域

当 ( y \cdot f(x) < 1 ) 时,我们处于线性区域。此时,损失函数为:
[
\ell_{\text{hinge}} = 1 - y \cdot f(x)
]
其梯度为:
[
\nabla \ell_{\text{hinge}} = -y \cdot \nabla f(x)
]
由于我们的模型是线性的,即 ( f(x) = \theta^T x ),因此梯度简化为:
[
\nabla \ell_{\text{hinge}} = -y \cdot x
]

平坦区域

当 ( y \cdot f(x) > 1 ) 时,我们处于平坦区域。此时,损失函数为零,因此梯度也为零:
[
\nabla \ell_{\text{hinge}} = 0
]

拐点处的梯度

当 ( y \cdot f(x) = 1 ) 时,函数有一个拐点。从技术上讲,梯度在此处未定义。然而,在实际优化中,我们可以选择线性区域的梯度或平坦区域的梯度(或它们的任意组合),这仍然有效。这种可能的梯度集合被称为次梯度(subgradient)。

次梯度下降算法

基于以上观察,我们可以定义一个“类梯度”函数,用于优化合页损失。以下是该函数的定义:
[
\tilde{\nabla} \ell_{\text{hinge}} =
\begin{cases}
-y \cdot x & \text{if } y \cdot f(x) < 1 \
0 & \text{if } y \cdot f(x) > 1
\end{cases}
]
在拐点处,我们可以选择任意值(例如0或 (-y \cdot x)),这不会影响优化结果。

现在,我们可以使用这个“类梯度”函数来实现次梯度下降算法。算法的伪代码如下:

  1. 初始化参数 ( \theta )。
  2. 重复直到收敛:
    a. 计算“类梯度” ( \tilde{\nabla} J(\theta) )。
    b. 更新参数:( \theta \leftarrow \theta - \eta \tilde{\nabla} J(\theta) )。

Python实现

以下是使用Python实现SVM优化算法的步骤。

定义模型和损失

首先,我们定义线性模型和合页损失函数。

import numpy as np

def linear_model(x, theta):
    return np.dot(x, theta)

def hinge_loss(y, f):
    return np.maximum(0, 1 - y * f)

def objective(theta, X, y, lambda_reg):
    f = linear_model(X, theta)
    loss = np.sum(hinge_loss(y, f))
    reg = (lambda_reg / 2) * np.dot(theta, theta)
    return loss + reg

实现次梯度

接下来,我们实现“类梯度”函数。

def approximate_gradient(theta, X, y):
    f = linear_model(X, theta)
    mask = (y * f) < 1
    grad = np.zeros_like(theta)
    if np.any(mask):
        grad = -np.dot(X[mask].T, y[mask])
    return grad

次梯度下降算法

最后,我们实现次梯度下降算法来优化参数。

def subgradient_descent(X, y, lambda_reg, learning_rate=0.01, iterations=1000):
    n_features = X.shape[1]
    theta = np.zeros(n_features)
    
    for i in range(iterations):
        grad = approximate_gradient(theta, X, y)
        theta = theta - learning_rate * (grad + lambda_reg * theta)
        
        # 可选:打印每次迭代的目标函数值
        if i % 100 == 0:
            obj = objective(theta, X, y, lambda_reg)
            print(f"Iteration {i}: Objective = {obj}")
    
    return theta

可视化结果

优化完成后,我们可以可视化决策边界,以验证模型是否能够正确分类数据并保持较大的间隔。

import matplotlib.pyplot as plt

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/f25e5d2e2ea4fdb53605eb9882eae806_34.png)

# 假设 X 和 y 是训练数据
theta_optimal = subgradient_descent(X, y, lambda_reg=0.1)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/f25e5d2e2ea4fdb53605eb9882eae806_36.png)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/f25e5d2e2ea4fdb53605eb9882eae806_37.png)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/f25e5d2e2ea4fdb53605eb9882eae806_39.png)

# 绘制数据点和决策边界
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='bwr')
x_boundary = np.linspace(X[:, 0].min(), X[:, 0].max(), 100)
y_boundary = -(theta_optimal[0] * x_boundary) / theta_optimal[1]
plt.plot(x_boundary, y_boundary, 'k-')
plt.show()

算法总结

本节课我们一起学习了支持向量机的优化方法,特别是基于合页损失的无约束优化问题。我们回顾了合页损失函数的定义,探讨了其梯度在不同区域的行为,并引入了次梯度的概念。通过实现次梯度下降算法,我们成功优化了SVM的目标函数,并可视化了一个具有较大间隔的决策边界。

总结一下,我们的SVM算法具有以下特点:

  • 任务:监督式二分类。
  • 模型族:线性模型。
  • 目标函数:L2正则化的合页损失。
  • 优化器:次梯度下降法。

这种方法在实际应用中非常常见,因为它避免了处理复杂的约束条件,同时仍然能够产生具有良好泛化能力的模型。

10.1:拉格朗日对偶(第一部分)🚀

在本节课中,我们将继续学习支持向量机,并引入一种新的、重要的方法来表述最大间隔问题。这种方法被称为拉格朗日对偶,它不仅为支持向量机提供了另一种求解视角,也是优化和机器学习中广泛使用的核心概念。我们将首先介绍拉格朗日对偶的一般数学概念,然后在后续课程中将其应用于支持向量机。

回顾:支持向量机作为约束优化问题

在上一讲中,我们介绍了支持向量机,并将其定义为一系列优化问题的解。其中一个关键形式是以下约束优化问题

我们寻找一个分离超平面 θ,使其具有较小的 L2 范数,并且所有样本都被正确分类。这等价于寻找具有最大间隔的超平面。其数学形式可以概括为:

最小化目标函数 J(θ),同时满足一系列约束条件 C_k(θ) ≤ 0

这个公式将 SVM 表述为一个带约束的优化问题。而拉格朗日对偶正是研究这类约束优化问题性质的一套强大工具。

拉格朗日方法:从约束到惩罚

上一节我们回顾了SVM的约束优化形式。本节中,我们来看看拉格朗日方法如何提供一种不同的视角。

考虑一个通用的约束优化问题:
最小化 J(θ),约束条件为 C_k(θ) ≤ 0(对于所有 k)。

拉格朗日方法的核心思想是将约束条件转化为目标函数中的惩罚项。我们引入非负的拉格朗日乘子 λ_k,并构造拉格朗日函数 L(θ, λ)

L(θ, λ) = J(θ) + Σ_k λ_k * C_k(θ)

这里,λ_k 衡量了对应约束 C_k(θ) ≤ 0 的重要性。通过最小化这个拉格朗日函数,我们实际上是在同时最小化原始目标 J(θ) 和惩罚违反约束的行为(因为 C_k(θ) > 0 会导致惩罚项为正)。当 λ_k 足够大时,任何违反约束的 θ 都会使 L(θ, λ) 变得很大,从而在优化过程中被排除。

原问题与对偶问题

上一节我们介绍了拉格朗日函数。本节中,我们将基于它定义两个密切相关的优化问题:原问题对偶问题

首先,我们定义原拉格朗日形式 P(θ)
P(θ) = max_{λ ≥ 0} L(θ, λ)

这个函数对每个 θ,取拉格朗日函数在所有非负 λ 上的最大值。一个关键结论是:最小化 P(θ) 等价于求解原始的约束优化问题

直观理解如下:

  • 如果某个 θ 违反了约束(即存在 C_k(θ) > 0),那么通过令对应的 λ_k → +∞,可以使 L(θ, λ) 趋于 +∞,从而 P(θ) = +∞。因此,最小化 P(θ) 时绝不会选择违反约束的 θ。
  • 如果 θ 满足所有约束(即所有 C_k(θ) ≤ 0),那么为了使 L(θ, λ) 最大,最优的 λ_k 应为 0(因为此时 C_k(θ) ≤ 0,乘以正的 λ_k 只会减少 L 的值)。此时,P(θ) = J(θ)。

因此,min_θ P(θ) 的解就是原始问题 min_θ J(θ) s.t. C_k(θ) ≤ 0 的解。

现在,我们通过交换 min 和 max 的顺序,得到对偶函数 D(λ)
D(λ) = min_θ L(θ, λ)

然后,定义对偶问题为:最大化 D(λ),约束条件为 λ ≥ 0

原问题与对偶问题的关系

上一节我们分别定义了原问题和对偶问题。本节中,我们来看看它们之间深刻的关系。

一个基本的不等式总是成立:对偶问题的最优值 ≤ 原问题的最优值。即:
max_{λ ≥ 0} D(λ) ≤ min_θ P(θ)

这个性质被称为弱对偶性

然而,在许多重要且常见的问题中(包括线性规划和我们即将讨论的SVM),一个更强的性质成立:强对偶性。这意味着:
max_{λ ≥ 0} D(λ) = min_θ P(θ)

也就是说,原问题和对偶问题具有相同的最优目标值。因此,我们可以通过求解对偶问题来获得原问题的最优解。这通常能带来计算或理论上的优势,例如将对 θ 的复杂约束转化为对 λ 的更简单的约束(如非负性),或者揭示问题的新结构。

与正则化的联系

在继续将拉格朗日对偶应用于SVM之前,我们简要回顾一下它与之前所学知识的联系。

还记得我们之前讨论的正则化吗?我们经常处理如下形式的优化问题:
min_θ J(θ) + β * R(θ)
其中 R(θ) 是正则化项(如 L2 范数),β 是控制正则化强度的超参数。

利用本节介绍的拉格朗日对偶理论,可以严格证明:这种基于惩罚项(β * R(θ)) 的正则化形式,等价于一个带有约束(R(θ) ≤ t) 的优化问题。参数 β 和约束边界 t 之间存在一一对应的关系。这再次印证了“惩罚”与“约束”在优化中的紧密联系。

总结

本节课我们一起学习了拉格朗日对偶的核心概念。我们了解到:

  1. 拉格朗日方法通过引入乘子 λ,将约束优化问题转化为无约束的拉格朗日函数。
  2. 通过定义原问题 min_θ max_{λ≥0} L(θ, λ),我们恢复了原始的约束优化。
  3. 通过交换极值顺序,我们得到对偶问题 max_{λ≥0} min_θ L(θ, λ)
  4. 在满足强对偶性的条件下,求解对偶问题等价于求解原问题,这常常能简化计算或提供新的见解。
  5. 这一理论统一了“惩罚”与“约束”的视角,并为我们之前学习的正则化提供了理论基础。

在下一讲中,我们将应用这套强大的拉格朗日对偶工具,重新推导支持向量机,并得到其著名的对偶形式,这将揭示SVM的更多重要性质。

10.2:支持向量机的对偶形式 🧠

在本节课中,我们将学习如何将拉格朗日理论应用于支持向量机,并推导出其对偶形式。我们将从回顾SVM的基本概念开始,逐步构建其拉格朗日函数,并最终得到一个仅依赖于数据点内积的优化问题。

概述

上一节我们介绍了拉格朗日乘子法。本节中,我们将利用该理论,为支持向量机问题构建一个对偶优化形式。这种形式具有一些独特的优势,例如优化变量数量与数据点数量相关,并且其目标函数仅依赖于数据点之间的内积。

支持向量机回顾

首先,让我们快速回顾一下支持向量机的基本设定。我们处于二分类场景中,有一个包含输入和目标值的训练数据集,目标值取两个可能值之一:+1 或 -1。

我们目前使用线性模型族,其形式如下:
y = sign(θ^T x + θ_0)
其中 y 是目标值(-1 或 1)。我们可以简洁地使用参数向量 θ 和输入 x 的点积来表示这个线性形式。这里我们将截距 θ_0 与主参数向量 θ 区分开来,稍后会看到原因。

我们的目标是找到一个好的线性模型,即一个能区分两个类别的决策边界。

在SVM课程的开始,我们定义了间隔的概念,即任意点到决策边界(或称分离超平面)的距离。我们可以推导出该距离的简洁公式:
γ_i = y_i (θ^T x_i + θ_0) / ||θ||
这正好对应了点 x_i 到分离超平面的距离,我们称之为间隔 γ。

我们希望遵循的原则是找到具有大间隔的超平面。这意味着它不仅能分离两个类别,而且其位置尽可能远离任何邻近的数据点。

支持向量机是一种优化最优间隔的算法。我们定义了以下优化问题,它是更直观问题表述的重新形式化。我们从一个优化间隔 γ 的问题开始,经过一系列数学变换,推导出以下二次规划问题,其解与原始问题等价。

SVM的约束优化问题

以下是支持向量机问题的典型约束版本:
min_{θ, θ_0} 1/2 ||θ||^2
subject to y_i (θ^T x_i + θ_0) ≥ 1 for all i

这个约束确保每个点都被正确分类,其预测得分至少为 1 或 -1(对应两个类别),或者可以更高/更低。在这组约束下,我们寻找具有最小范数的向量 θ。

这是一个约束优化问题。在上一节视频中我们看到,对于任何约束优化问题,我们都可以将其表述为带有惩罚项的无约束版本。

构建拉格朗日函数

我们可以定义以下拉格朗日原始形式,其中我们将约束作为惩罚项加入:
L(θ, θ_0, λ) = 1/2 ||θ||^2 - Σ_{i=1}^n λ_i [y_i (θ^T x_i + θ_0) - 1]
其中 λ_i ≥ 0 是拉格朗日乘子。

我们最小化一个关于 θ 的函数,但这个函数本身又是一个关于 λ 的优化问题。我们已经看到,这个拉格朗日原始形式的最小值与上述约束目标函数的最小值相同。

需要说明的是,在实践中,我们不会直接使用这个表达式来求解优化问题。它更像是一个数学形式,用于推导其他实际可解的优化公式。

拉格朗日对偶

这两个形式具有相同的最优解。现在,我想应用拉格朗日理论到SVM问题。第一步是定义拉格朗日函数。

考虑以下目标函数,这是我们的拉格朗日目标,其中我们添加了带有拉格朗日乘子 λ 的约束。

在上一节视频中,我定义了拉格朗日对偶的概念。给定一个拉格朗日函数,我们可以优化其原始形式,或者我们可以翻转 min 和 max 的顺序,得到首先关于 λ 求最大,然后关于 θ 求最小的问题。这个新的优化函数称为对偶函数 D(λ)

推导SVM的对偶形式

现在让我们应用这个理论,为之前SVM问题的拉格朗日函数构建对偶形式。

回想一下,拉格朗日函数具有上述形式。我们想要优化这个拉格朗日函数,即关于 λ 最大化关于 θ 的最小值。D(λ) 就是我们将要关于 λ 最大化的函数,它是拉格朗日函数关于原始变量(θ, θ_0)的最小值。

因此,它是这个目标函数的最小值。我们如何将其用于优化呢?第一步是,对于每个 λ 值,我们可以以闭式解的形式计算出这个最小值。这是一个关于 θ 的二次目标函数,存在唯一最小值。

我们可以对这个二次函数求导,令导数为零,然后求解 θ。通过数学运算(本讲座不展开推导),我们可以得到 θ 的闭式表达式。

当我们设导数为零时,梯度(一个向量)将等于零,这给出一组方程。从这些方程中,我们可以推导出以下关系。这些方程必须成立,其中一个方程恰好给出了 θ 的表达式。

给定这个 θ 的表达式(即我们的最小化 θ),我们可以将其代入上面看到的拉格朗日目标函数中。

将其代回拉格朗日函数后,一切都被简化了。现在,我们的目标函数表达式具有以下形式。这是将 θ 的表达式代入拉格朗日函数后的结果。

SVM对偶优化问题

现在这个表达式是优化问题的解。对于任何 λ,这都是具有最优参数 θ 的拉格朗日函数形式。因此,我得到了 D(λ) 的闭式表达式。

现在唯一的问题是,除了这个形式,我还必须确保 λ 始终为非负。此外,以下约束必须成立:Σ_{i=1}^n λ_i y_i = 0。我们通过设定最优 θ 推导出这个约束,因此也必须将其作为对偶优化问题的一部分来强制执行。

现在,关于 λ 最大化这个函数,就得到了对偶优化问题。我定义了SVM问题的对偶版本,其形式如下:
max_λ Σ_{i=1}^n λ_i - 1/2 Σ_{i=1}^n Σ_{j=1}^n λ_i λ_j y_i y_j (x_i^T x_j)
subject to λ_i ≥ 0 for all i, and Σ_{i=1}^n λ_i y_i = 0

在SVM的拉格朗日对偶中,我们知道对偶的最大值小于或等于原始的最小值。但事实证明,在SVM的背景下,我们可以证明这两个问题实际上是等价的,它们达到相同的最优目标值。这意味着现在SVM的对偶形式可以用来表述我们的问题。

对偶形式的性质

现在让我们看看对偶形式的一些性质。我们可以做出以下几点观察。

首先,这又是一个约束二次优化问题。这是一个相当好的形式,我们知道如何求解二次优化问题。

其次,现在我们的优化变量是 λ。让我们看看有多少个 λ:我们恰好有 n 个,与训练数据集的大小相同。在之前的问题中我们有 n 个约束,现在我们有 n 个变量。

最后,一个重要的观察是:这个目标函数依赖于我们的数据 X,但它只依赖于不同 x 之间的点积。这非常有趣,我们将在下一讲中重新讨论这一点。这是一个非常重要的性质,它将开启一类全新的、非常强大的SVM类型算法。但现在,我们暂时把它放在这里,先看看对偶的其他方面。

何时使用原始形式或对偶形式?

我们定义了原始形式和对偶形式。我们如何知道何时求解对偶或原始形式?原因有很多,我先从两点开始。

首先,注意原始形式的优化变量维度与特征数量呈线性关系。优化变量的数量等于特征的数量 D。因此,如果我们有一个具有许多数据点的大型数据集,我们应更关注原始形式。

然而,如果我们有很多特征,但数据点较少,我们则希望使用对偶形式。例如,如果我们想做某种多项式扩展,那么可以使用对偶版本。如果我们有非常多的多项式特征(例如非常高次的多项式),我们的优化变量数量并不依赖于所使用的多项式特征的次数。因此,我们可以在对偶中使用大量特征,但优化问题的复杂度不会随着添加更多特征而增加。这是一个重要的性质,我们将在后面重新讨论。

总结

本节课中,我们一起学习了支持向量机的对偶形式推导。我们从回顾SVM和间隔的概念开始,然后构建了其拉格朗日函数。通过应用拉格朗日对偶理论,我们得到了一个仅依赖于数据点内积的优化问题。这个对偶形式将优化变量从特征数量(D)转换为数据点数量(n),并且在处理高维特征空间时具有潜在优势。在下一节中,我们将探讨如何实际求解这个对偶问题,并了解这种对偶方法的更多实际应用方面。

10.3:支持向量机对偶问题详解 🧠

在本节课中,我们将深入学习支持向量机(SVM)的对偶问题。我们将从回顾SVM的基本概念开始,然后探讨如何处理不可分数据集,接着介绍如何求解对偶问题,最后通过一个实际例子来展示SVM的应用。

回顾:支持向量机与最大间隔

上一节我们介绍了支持向量机的基本思想,即寻找一个能够最大化分类间隔的决策超平面。本节中,我们来看看如何通过优化问题来形式化这一目标。

我们处理的是二分类问题。我们有一个训练数据集,包含样本 ( x ) 和标签 ( y ),其中 ( y ) 取值为 -1 或 +1,表示样本所属的类别。

寻找最大间隔超平面可以表述为以下约束二次优化问题(原问题):

\[\begin{aligned} \min_{\theta, \theta_0} \quad & \frac{1}{2} \|\theta\|^2 \\ \text{s.t.} \quad & y^{(i)}(\theta^T x^{(i)} + \theta_0) \geq 1, \quad \forall i \end{aligned} \]

引入拉格朗日对偶

利用拉格朗日理论,我们可以推导出上述原问题的对偶问题。求解这个对偶问题可以得到与原问题相同的最优目标值。

对偶问题的形式如下:

\[\begin{aligned} \max_{\lambda} \quad & \sum_{i=1}^n \lambda_i - \frac{1}{2} \sum_{i=1}^n \sum_{j=1}^n \lambda_i \lambda_j y^{(i)} y^{(j)} (x^{(i)})^T x^{(j)} \\ \text{s.t.} \quad & \lambda_i \geq 0, \quad \forall i \\ & \sum_{i=1}^n \lambda_i y^{(i)} = 0 \end{aligned} \]

处理不可分数据:松弛变量

到目前为止的分析仅适用于线性可分问题。在实践中,数据往往不是线性可分的,这意味着不存在一个能完美分类所有训练样本的线性决策边界。

为了解决这个问题,我们需要修改优化问题。我们在之前的讲座中通过引入松弛变量 ( \xi_i ) 来实现。

松弛变量允许约束被轻微违反,其违反程度由 ( \xi_i ) 的大小表示。我们希望这种违反尽可能小,因此在目标函数中引入惩罚项 ( C \sum_{i=1}^n \xi_i )。

引入松弛变量后的原问题(软间隔SVM)为:

\[\begin{aligned} \min_{\theta, \theta_0, \xi} \quad & \frac{1}{2} \|\theta\|^2 + C \sum_{i=1}^n \xi_i \\ \text{s.t.} \quad & y^{(i)}(\theta^T x^{(i)} + \theta_0) \geq 1 - \xi_i, \quad \forall i \\ & \xi_i \geq 0, \quad \forall i \end{aligned} \]

软间隔SVM的对偶问题

现在,我们来看软间隔SVM的对偶问题。为了找到对偶,我们首先定义拉格朗日函数,它现在包含两组对偶变量:( \lambda ) 和 ( \mu )。

拉格朗日函数 ( \mathcal{L} ) 定义为:

\[\mathcal{L}(\theta, \theta_0, \xi, \lambda, \mu) = \frac{1}{2}\|\theta\|^2 + C\sum_{i=1}^n \xi_i - \sum_{i=1}^n \lambda_i [y^{(i)}(\theta^T x^{(i)} + \theta_0) - 1 + \xi_i] - \sum_{i=1}^n \mu_i \xi_i \]

通过对原变量 ( \theta, \theta_0, \xi ) 最小化拉格朗日函数,并代入最优条件,我们可以推导出对偶问题。

简化后的对偶问题形式如下:

\[\begin{aligned} \max_{\lambda} \quad & \sum_{i=1}^n \lambda_i - \frac{1}{2} \sum_{i=1}^n \sum_{j=1}^n \lambda_i \lambda_j y^{(i)} y^{(j)} (x^{(i)})^T x^{(j)} \\ \text{s.t.} \quad & 0 \leq \lambda_i \leq C, \quad \forall i \\ & \sum_{i=1}^n \lambda_i y^{(i)} = 0 \end{aligned} \]

这与之前的对偶形式非常相似,只是增加了对 ( \lambda_i ) 的上界约束 ( C )。

如何求解对偶问题?🔧

现在我们有了对偶优化问题。一个有趣的问题是:如何实际求解它?

虽然可以使用通用的二次规划求解器,但这并非最高效的方法。存在许多专门为此类问题设计的优化算法。

其中一种方法是基于坐标下降的思想。坐标下降是一种优化多变量函数的通用方法,其思想很简单:在每次迭代中,选择一个维度(变量),固定其他所有变量,仅在该维度上优化目标函数,然后循环往复。

以下是坐标下降的步骤概述:

  1. 选择目标函数 ( F ) 的一个维度 ( j )。
  2. 固定所有其他变量 ( x_k (k \neq j) )。
  3. 找到使 ( F ) 最小的 ( x_j ) 值。
  4. 选择另一个维度,重复上述过程,直到函数收敛。

对于SVM对偶问题,由于其存在约束 ( \sum_{i=1}^n \lambda_i y^{(i)} = 0 ),我们不能一次只优化一个变量。因此,在实践中采用一种称为序列最小优化 的算法。

SMO算法步骤如下:

  1. 重复以下步骤直至收敛。
  2. 选择一对拉格朗日乘子 ( \lambda_i ) 和 ( \lambda_j )(实践中存在启发式方法选择这对变量)。
  3. 固定所有其他 ( \lambda_k (k \neq i, j) )。
  4. 在满足约束的条件下,联合优化 ( \lambda_i ) 和 ( \lambda_j )。
  5. 选择另一对变量,重复此过程。

这个过程在实践中非常高效,是求解SVM对偶问题的标准方法。

从对偶解恢复原始解

在求解对偶问题后,我们最终仍然关心分离超平面的参数 ( \theta ) 和 ( \theta_0 )。那么,如何从对偶解中恢复原始解呢?

在推导对偶问题时,我们通过最小化拉格朗日函数对 ( \theta ) 求导,得到了 ( \theta ) 关于 ( \lambda ) 的表达式。因此,给定最优的 ( \lambda ),最优的 ( \theta ) 具有以下形式:

\[\theta = \sum_{i=1}^n \lambda_i y^{(i)} x^{(i)} \]

对于截距项 ( \theta_0 ),也可以通过支持向量计算得到。例如,对于任意支持向量 ( x^{(j)} )(即 ( \lambda_j > 0 ) 且 ( \xi_j = 0 )),有:

\[\theta_0 = y^{(j)} - \theta^T x^{(j)} \]

支持向量与模型稀疏性 🎯

支持向量机一个有趣的性质是,观察最优参数 ( \theta ) 的表达式:

\[\theta = \sum_{i=1}^n \lambda_i y^{(i)} x^{(i)} \]

这个求和是对所有数据点 ( i ) 进行的,每个数据点 ( x^{(i)} ) 由一个标量 ( \lambda_i y^{(i)} ) 加权。

在实践中,对偶问题的最优解中,大多数 ( \lambda_i ) 的值将为0。因此,尽管求和形式上是对 ( n ) 个项,但实际上只有少数 ( \lambda_i ) 非零的项对求和有贡献。

那些 ( \lambda_i > 0 ) 所对应的数据点,正是距离分离超平面最近的点,也就是位于间隔边界上的点。这些点被称为支持向量

直观上,最大间隔超平面的位置仅由最靠近它的那些点(即支持向量)决定。远离边界的点如何移动,并不会改变决策边界。这就是为什么SVM的解具有稀疏性,并且算法得名“支持向量机”。

实例演示:鸢尾花数据集 🌸

现在,我们通过一个例子来演示SVM的应用。我们将使用经典的鸢尾花数据集,并只使用前两个特征进行二分类(判断是否为山鸢尾)。

以下是使用 scikit-learn 库中SVM算法的步骤概要:

  1. 加载并预处理数据,将标签转换为 -1 和 +1。
  2. 使用 SVC 类(默认求解对偶问题)或 LinearSVC 类(求解原问题或合页损失形式)来训练模型。
  3. 拟合模型后,可以可视化决策超平面和间隔边界。
  4. 从训练好的模型中提取支持向量。

在可视化结果中,实线代表分离超平面,虚线代表间隔边界。位于虚线(间隔边界)上的点被突出显示(例如用黑色圆圈圈出),这些点就是支持向量。可以看到,决策边界的形状完全由这些支持向量决定。如果改变那些远离边界的点,决策边界不会发生变化。

总结

本节课中,我们一起深入学习了支持向量机的对偶问题。

我们首先回顾了通过最大化间隔来寻找最优超平面的原问题。接着,我们利用拉格朗日理论推导了其对应的对偶问题,并了解到求解对偶问题可以得到相同的最优解。

为了处理现实中常见的线性不可分数据,我们引入了松弛变量,得到了软间隔SVM的原问题及其对偶形式。

我们探讨了如何高效求解对偶问题,介绍了坐标下降的思想以及专门为SVM设计的序列最小优化 算法。

最后,我们学习了如何从对偶解中恢复原始超平面参数,并理解了支持向量的核心概念——正是这些位于间隔边界上的少数点决定了最终模型,赋予了SVM稀疏性和鲁棒性。

通过鸢尾花数据集的实例,我们直观地观察了支持向量如何影响决策边界。对偶形式不仅是求解SVM的有效工具,也为后续引入核方法以处理非线性问题奠定了重要基础,这将是我们在后续课程中探讨的内容。

11.1:核技巧(直观理解)🚀

在本节课中,我们将开始学习一个名为“核”的新主题。核是一个有趣且重要的概念,其背后的动机是如何使我们的机器学习算法更加灵活和非线性。到目前为止,我们研究的大多数模型,无论是回归还是分类,都是线性模型,即输入X的线性函数。在本讲中,我们将探讨一种新方法,可以在不显著增加复杂度或使其更难处理的情况下,使这些模型非线性化。这将是一种通用方法,适用于我们目前见过的大多数机器学习算法,并且实际上可以扩展到无监督学习领域。其核心思想就是核以及所谓的核技巧。

动机:从线性回归出发 🔍

在正式定义核和核技巧之前,我们先从一个简单的算法——线性回归——开始,来理解使用核方法的高层动机。

线性回归回顾

回想一下,在线性回归中,我们假设模型的形式为:模型的输出 f(x) 是输入属性 x 的某种线性组合。我们可以用向量化形式表示为 θ^T x。其中,θ 是模型参数,x 是我们的输入向量。

我们使用均方误差损失来训练这些模型,即目标值与模型预测值 θ^T x 之间的平方差的平均值。后来,我们还看到可以对这个目标函数进行正则化,即对权重 θ 添加惩罚项。这鼓励我们找到一个既误差小、参数数值也小的解,这样的解在数值上表现更好,过拟合更少,对新数据的泛化能力更强。使用L2正则化目标函数学习参数向量的算法被称为岭回归。

引入非线性特征

前面展示的模型本质上是线性的。但在早期课程中,我们看到,原则上,我们可以通过定义非线性特征 φ(x) 来使任何线性模型非线性化。这里的 φ 可以是任何类型的特征,例如多项式特征,或者由 x 导出的正弦和余弦集合。因此,φ(x) 可以高度非线性,但它在 θ 上仍然是线性的。这意味着我们可以使用本课程中见过的所有标准技术来学习 θ,最终得到一个关于 x 的非线性函数。

为了更精确地探讨这种方法,我们引入一些新的符号。

特征化设计矩阵

我们引入一个新矩阵 Φ,称为特征化设计矩阵。回想一下,设计矩阵是一个矩阵,其中我们将所有输入 X 作为该矩阵的行堆叠在一起。特征化设计矩阵与此类似,只是我们堆叠的是特征向量,而不是原始属性向量。每个 x 是带有一些原始属性的输入,现在我们用 φ 将其特征化,得到一个维度为 P 的列向量。然后,我们将每个向量转置为行向量,并将所有这些行堆叠在一起,形成一个名为特征化设计矩阵的矩阵。我们用大写字母 Φ 表示这个矩阵,以区别于原始设计矩阵。

岭回归的闭式解

给定设计矩阵,我们可以使用正规方程来计算线性回归模型参数的闭式解,以最小化均方误差目标函数。对于特征化矩阵 Φ,我们可以做同样的事情,只需将正规方程中的 X 替换为 Φ。这为我们提供了当输入被特征 φ 化时,参数 θ 的解。

接下来,我们将对这个算法进行一点变换。

对偶形式与重要观察

我们将使用一个称为“推入矩阵恒等式”的线性代数恒等式,将正规方程变换为另一种形式,称为对偶形式。这种变换为我们提供了最优权重 θ 的不同公式。

一个重要的观察是,在对偶形式中,最优参数 θ 可以表示为特征矩阵 Φ 与一个新向量 α 的乘积。进一步推导表明,最优参数 θ 实际上是所有特征化训练点的线性组合。具体来说,θ 是训练点特征 φ(x_i) 的加权和,权重为 α_i

这个观察非常关键,它构成了许多涉及核的算法的核心。

核技巧的初步体现

为什么这个观察很有趣?假设我们现在想为一个新输入 x' 做出预测。根据线性模型的定义,模型在 x' 处的输出是 φ(x')θ 的点积。将 θ 的表达式代入后,我们发现模型预测值可以表示为所有训练点特征与 x' 特征的点积的加权平均,权重为 α_i

这里的关键观察是:如果我们知道 α_i,并且能够高效地计算这些点积,那么我们可以在不需要实际知道特征向量 φ 本身的情况下获得预测。换句话说,方程中直接使用的不是特征本身,而是它们的点积。如果我们能以某种方式在不实际需要特征的情况下计算点积,并且计算效率比计算实际特征更高,那么我们就可以显著加速这类模型的推理过程。这个想法就是我们即将介绍的核技巧的雏形。

这个技巧甚至比刚才描述的更强大、更通用。它更通用,因为我们不仅在做预测时不需要特征本身而只需要它们的点积,在学习 θ 时也是如此。

为了证明这一点,让我们考虑特征矩阵 Φ 与其转置的乘积 K = ΦΦ^T。这个 K 矩阵是一个 n×n 矩阵,其第 (i, j) 项是第 i 个点和第 j 个点的特征的点积。根据我们之前推导出的 α 的定义,我们可以将 K 代入公式来计算 α。同样,这个计算过程只依赖于点积矩阵 K,而不需要直接知道特征是什么。

因此,给定一个新输入 x',我们可以:

  1. 使用只涉及点积矩阵 K 的公式高效地计算 α
  2. 然后使用涉及 φ(x') 与各 φ(x_i) 点积的公式进行预测。

如果我们能够快速计算这些特征的点积(稍后我们将展示如何做到这一点),那么它将显著加速我们使用非线性特征进行机器学习的速度。

总结 📝

本节课中,我们一起学习了核方法的基本动机。我们从线性回归出发,回顾了如何通过引入非线性特征 φ(x) 来使模型非线性化。通过推导岭回归的对偶形式,我们发现最优模型参数 θ 可以表示为训练样本特征的线性组合。更重要的是,模型的预测和参数学习过程最终都只依赖于特征之间的点积 φ(x_i)^T φ(x_j),而不需要直接使用高维甚至无限维的特征向量 φ(x) 本身。这个关键的观察是核技巧的基础,它允许我们在不显式计算复杂特征映射的情况下,高效地在高维特征空间中工作。在接下来的视频中,我们将更精确地探讨如何利用这个核技巧。

11.2:核技巧(示例)🚀

在本节课中,我们将学习核技巧(Kernel Trick)的核心概念。我们将通过一个具体的例子,展示如何高效地计算特征之间的点积,从而避免直接处理高维特征向量,提升机器学习算法的效率。


概述

在上一节中,我们介绍了线性回归模型,并展示了如何通过特征映射(Feature Mapping)将其转化为非线性模型。我们注意到,某些算法的优化过程仅依赖于特征之间的点积,而非特征本身。本节中,我们将通过多项式特征的例子,详细说明如何利用核技巧高效计算这些点积。


线性回归与特征映射

线性回归模型的预测形式为:

[
f(x) = \theta^T \phi(x)
]

其中,(\phi(x)) 是将原始输入 (x) 映射到高维特征空间的非线性函数。通过这种方式,我们可以在保持模型对参数 (\theta) 线性的同时,学习输入 (x) 的非线性关系。


特征化设计矩阵

为了表示所有训练样本的特征,我们定义特征化设计矩阵 (\Phi):

[
\Phi = \begin{bmatrix}
\phi(x_1)^T \
\phi(x_2)^T \
\vdots \
\phi(x_n)^T
\end{bmatrix}
]

这个矩阵的每一行对应一个样本的特征向量转置。


对偶形式与点积

线性回归的正规方程可以表示为对偶形式。在对偶形式中,最优参数 (\theta^*) 可以表示为训练样本特征的线性组合:

[
\theta^* = \sum_{i=1}^{n} \alpha_i \phi(x_i)
]

其中,(\alpha_i) 是权重系数。模型的预测可以重写为:

[
f(x') = \phi(x')^T \theta^* = \sum_{i=1}^{n} \alpha_i \phi(x')^T \phi(x_i)
]

这个表达式仅涉及特征之间的点积 (\phi(x')^T \phi(x_i)),而不需要显式计算特征向量本身。


核技巧示例:多项式特征

以下是一个具体的例子,说明如何高效计算多项式特征的点积。

多项式特征定义

假设输入 (x) 是一个 (d) 维向量。我们定义二阶多项式特征映射 (\phi),其维度为 (d^2)。特征向量的每个分量由两个输入属性的乘积构成:

[
\phi(x)_{ij} = x_i \cdot x_j
]

例如,当 (d=3) 时,特征向量包含9个分量:(x_1^2, x_1x_2, x_1x_3, x_2x_1, x_2^2, x_2x_3, x_3x_1, x_3x_2, x_3^2)。

直接计算点积

直接计算两个样本 (x) 和 (z) 的特征点积需要 (O(d^2)) 时间:

[
\phi(x)^T \phi(z) = \sum_{i=1}^{d} \sum_{j=1}^{d} (x_i x_j) (z_i z_j)
]

高效计算点积

然而,我们可以利用以下恒等式,在 (O(d)) 时间内计算相同的点积:

[
\phi(x)^T \phi(z) = (x^T z)^2
]

证明如下:

[
(x^T z)^2 = \left( \sum_{i=1}^{d} x_i z_i \right)^2 = \sum_{i=1}^{d} \sum_{j=1}^{d} (x_i z_i)(x_j z_j) = \sum_{i=1}^{d} \sum_{j=1}^{d} (x_i x_j)(z_i z_j) = \phi(x)^T \phi(z)
]

通过这种方式,我们避免了显式构造高维特征向量,大幅提升了计算效率。


推广到高阶多项式

上述技巧可以推广到任意阶 (p) 的多项式特征。此时,特征映射的维度为 (d^p),直接计算点积需要 (O(d^p)) 时间。但利用核技巧,我们可以高效计算:

[
\phi_p(x)^T \phi_p(z) = (x^T z)^p
]

这个计算仅需 (O(d)) 时间,与特征维度无关。这意味着我们可以使用任意高阶的多项式特征,而不会增加额外的计算成本。


核化岭回归

基于上述观察,我们可以定义核化岭回归(Kernelized Ridge Regression)算法。该算法利用对偶形式和核技巧,仅通过点积进行训练和预测,从而高效处理高维甚至无限维特征空间。


核技巧的广泛应用

核技巧不仅适用于岭回归,还可以应用于许多其他机器学习算法:

以下是核技巧的一些应用场景:

  • 分类算法:如逻辑回归和支持向量机(SVM)。
  • 无监督学习:如核主成分分析(Kernel PCA)和密度估计。
  • 其他线性模型:任何仅依赖于特征点积的算法都可以进行核化。

核技巧的核心思想是:当算法仅依赖于特征点积时,我们可以通过定义高效的核函数 (K(x, z) = \phi(x)^T \phi(z)) 来隐式地在高维特征空间中操作,而无需显式计算 (\phi(x)) 和 (\phi(z))。


总结

本节课中,我们一起学习了核技巧的基本原理。我们通过多项式特征的例子,展示了如何高效计算高维特征空间中的点积,从而避免直接处理高维向量带来的计算负担。核技巧允许我们在许多机器学习算法中使用复杂、高维的特征映射,而不会显著增加计算成本,这是机器学习中一项强大而实用的技术。

11.3:第3部分:支持向量机中的核技巧 🔧

在本节课中,我们将学习核技巧在支持向量机中的应用。我们将看到,通过仅使用特征之间的点积,而不是特征本身,可以高效地处理高维特征,从而显著提升算法性能。


概述

上一节我们介绍了核技巧的基本思想,即通过高效计算特征点积来加速机器学习算法。本节中,我们将具体探讨如何将核技巧应用于支持向量机,使其能够处理高维特征,同时保持计算效率。


支持向量机回顾

支持向量机在其最基本的形式中执行二元分类任务。我们有一个训练数据集,目标变量Y取两个可能的值,通常表示为-1或+1。

我们在此数据上考虑的机器学习模型是一个线性模型,其形式与之前讨论线性回归时相同。不同之处在于,我们现在使用不同的损失函数和目标函数,将该模型用于分类任务。

支持向量机中使用的特定目标函数如下,这是一个约束优化问题:

\[\min_{\theta, \theta_0, \psi} \frac{1}{2} \|\theta\|^2 + C \sum_{i=1}^{n} \psi_i \]

约束条件为:

\[y_i (\theta^T \phi(x_i) + \theta_0) \geq 1 - \psi_i, \quad \psi_i \geq 0 \]

在之前的讲座中我们看到,这是一个关于参数θ和θ0的优化问题,约束条件是每个训练实例在一定的误差裕度ψ内被正确分类。我们的目标函数是最小化误差ψ的幅度,同时也最小化决策边界超平面θ的范数。

最小化这个范数等价于优化两个类别之间的最大间隔。这意味着,通过最小化这个范数,我们选择的超平面尽可能远离属于两个类别中任一类的训练点。


对偶问题与核技巧

在最近关于支持向量机的讲座中,我们了解到,利用拉格朗日对偶原理,可以将上述问题重新表述为另一种形式。这个优化问题被称为原始问题的对偶问题

优化这个对偶问题能得到与原始问题相同的最小值。虽然优化方式不同——现在变量是λ,并且这是一个带有约束的关于λ的二次优化问题——但两者是等价的。

以下是支持向量机对偶问题的优化目标:

\[\max_{\lambda} \sum_{i=1}^{n} \lambda_i - \frac{1}{2} \sum_{i=1}^{n} \sum_{j=1}^{n} \lambda_i \lambda_j y_i y_j \phi(x_i)^T \phi(x_j) \]

约束条件为:

\[\sum_{i=1}^{n} \lambda_i y_i = 0, \quad 0 \leq \lambda_i \leq C \]

原始问题的主要变量是超平面的参数,数量约为特征维度D。而对偶问题有n个变量λ,其中n是数据集的大小。

通过求解对偶问题,我们可以得到原始问题参数的公式。最优的θ可以表示为所有输入特征X的加权线性组合:

\[\theta^* = \sum_{i=1}^{n} \lambda_i y_i \phi(x_i) \]

这里的权重由标签y和对偶变量λ的最优赋值给出。这个形式与我们之前在岭回归例子中看到的非常相似。

给定最优θ具有这种形式,我们也可以在新数据点上进行预测,其公式同样只涉及特征φ的点积:

\[f(x') = \theta^{*T} \phi(x') = \sum_{i=1}^{n} \lambda_i y_i \phi(x_i)^T \phi(x') \]


应用核技巧

现在,让我们再次审视这些公式。顶部的公式是支持向量机对偶问题的优化目标,下面的公式是进行预测的公式。两者都只涉及特征之间的点积,而不涉及特征本身

因此,就像我们在岭回归中所做的那样,我们也可以在这里应用核技巧。如果我们能高效地计算这些点积,那么我们就能在高维特征空间中高效地使用这个对偶版本的算法。

我们可以更一般地定义核技巧。假设我们有一些特征φ,并且我们找到了比标准定义更有效的方法来计算这个点积。我们称这个计算方法由一个函数K给出,这个函数K很快就会被我们称为核函数

假设我们想计算这两个特征之间的点积,我们可以简单地调用这个函数K,它会快速计算出这个点积。我们在上一个视频中看到的例子是,如果我们的特征是输入X属性的所有可能乘积的多项式特征,那么我们可以使用以下简单技巧在线性时间内高效计算点积,这就定义了我们的核函数。

现在,假设我们有一个核函数,我们可以用以下特定形式定义我们的优化问题。我简单地将点积替换为核函数。我们将以一种使计算真正快速的方式构造这个核函数。

我们的预测公式也将具有以下特定形式,其中我再次用核函数替换了点积:

\[f(x') = \sum_{i=1}^{n} \lambda_i y_i K(x_i, x') \]

因此,一切都将能够高效计算。我们能够在支持向量机中使用非常高维的特征,而不会真正产生使用它们所带来的大量计算成本。


核化支持向量机

这就定义了另一个算法,我将其称为核化支持向量机分类,它是我们之前看到的常规支持向量机分类算法的核化版本。

与之前的视频一样,我们正在求解这个对偶问题。这定义了一个新的二元分类算法。由于我们使用了这些潜在的高度非线性特征,我们能够学习非线性决策边界

我们能够通过使用诸如之前看到的序列最小优化等算法来解决支持向量机优化问题的对偶问题,从而计算这些边界。


总结

在本节课中,我们一起学习了如何将核技巧应用于支持向量机。我们回顾了支持向量机的对偶形式,并展示了其优化目标和预测公式都只依赖于特征间的点积。通过定义一个高效的核函数K来计算这些点积,我们可以使支持向量机能够高效地处理高维甚至无限维的特征空间,从而学习复杂的非线性决策边界,而无需显式计算和存储高维特征本身。核技巧是支持向量机的一大优势,使其在实践中非常强大和灵活。在接下来的视频中,我们将看到更多可以在支持向量机及其他算法中使用的核函数示例。

11.4:第4部分:核函数类型 🔍

在本节课中,我们将学习核函数(Kernel)的概念及其在机器学习中的应用。我们将探讨几种常见的核函数类型,并通过实例展示它们如何影响分类边界。此外,我们还将了解核函数背后的数学理论及其在实际应用中的优缺点。


核函数简介

在前几节视频中,我们介绍了核技巧(Kernel Trick),并看到了至少两个可以应用核技巧的机器学习算法示例:线性回归(Linear Regression)和支持向量机(Support Vector Machines)。实际上,核技巧可以应用于许多机器学习算法中,包括监督学习和无监督学习算法。

在本节中,我们将继续探讨更多可以应用核技巧的核函数示例,并展示如何通过核技巧更高效地计算特征。


核技巧的应用场景

我们再次回到线性模型族的场景。这里的模型在参数 θ 上是线性的,但在特征 φ 上是非线性的。这与我们之前看到过的支持向量机类似。现在,我们将探讨如何高效地使用高维特征 φ

一个可以应用核技巧的算法是核岭回归(Kernel Ridge Regression)。假设我们使用以下形式的正规方程(Normal Equations),可以证明最优参数 θ 具有以下形式,由一些分配给输入的权重参数 α 定义。

对于新的预测,我们可以通过以下公式计算,该公式仅涉及点积(Dot Products),而不涉及特征本身。同样,学习 α 也仅涉及点积,而不涉及特征。

类似地,在支持向量机中,第一行是支持向量机的目标函数公式,第二行是使用支持向量机进行预测的公式。这两个公式都仅涉及特征的点积,而不涉及特征本身。

那么,还有哪些类型的特征允许我们高效地计算这些特征呢?


核函数的定义

为了使这一点更加精确,我将定义什么是核函数。核函数是一个简单的函数,它接受两个输入点 xz,并计算它们之间的得分。我们可以定义一个与特征 φ 相关联的核函数,其形式如下:

公式: K(x, z) = φ(x)^T φ(z)

这个核函数将计算点积,但以一种比标准点积定义更高效的方式进行。我们也可以将核函数视为不一定与特定特征集相关联的通用函数,并称之为核函数。稍后我们将看到选择特征集的影响。


核函数的性质

我们可以认为核函数具有几个性质。首先,核函数可以简单地定义为任意两个点之间的点积。其次,我们可以将其视为定义 xz 之间相似性的一种度量。

在这个早期的例子中,如果 xz 相同,那么这个点积将最大;如果 xz 非常不同,那么这个得分将很小。因此,我们可以将这个函数视为输出一个数字,告诉我们 xz 有多相似。即使没有明确定义 φ,我们也可以保持这种解释。

任何测量两个点 xz 之间相似性的函数,当 xz 相等时取较大值,当 xz 非常不同时取较小值,我们都可以将其视为相似性度量。核函数是定义相似性度量的一种特定方式。


核函数示例

在本节中,我将通过一个玩具数据集(Toy Dataset)来展示几种核函数的示例。这个数据集包含两个类别:蓝色和棕色。我们将观察不同核函数如何影响使用分类算法分离这两个类别的效果。

需要注意的是,这个数据集不是线性可分的(Linearly Separable)。在这个二维空间中,没有一条直线可以完全分离棕色和蓝色类别。因此,我们将探讨使用核函数学习非线性决策边界的不同方法。

以下是数据集中的点:

代码:

# 示例数据集点
X = [[0, 0], [1, 1], [1, 0], [0, 1], [2, 2], [2, 1], [1, 2], [3, 3]]
y = [0, 0, 0, 0, 1, 1, 1, 1]  # 类别标签

线性核函数

最简单的核函数示例是线性核函数(Linear Kernel)。如果我们简单地取 xz 的点积,这是一个有效的操作。如果我们将这个核函数插入到之前看到的任何分类算法中,我们只会计算一个线性决策边界,这里在速度上没有任何节省。

我们可以通过以下代码在支持向量机中应用线性核函数:

代码:

from sklearn.svm import SVC

# 使用线性核函数的SVM
svm_linear = SVC(kernel='linear')
svm_linear.fit(X, y)

可视化结果后,我们得到预期的线性决策边界。由于数据集不是线性可分的,这个边界无法正确分类所有点,例如有一个点被错误分类。


多项式核函数

现在,让我们考虑一个稍微更有趣的核函数:多项式核函数(Polynomial Kernel)。我们将使用以下形式的多项式核函数:

公式: K(x, z) = (x^T z + c)^d

这个表达式对应于一个高维特征空间中的点积,该空间的维度大致为 (d + n choose n),其中 n 是原始特征维度。随着 d 的增加,维度呈指数级增长。但通过使用这个公式,我们可以在线性时间内计算这个特征空间中的点积。

以下是一个多项式核函数的示例代码:

代码:

# 使用多项式核函数的SVM
svm_poly = SVC(kernel='poly', degree=3)
svm_poly.fit(X, y)

结果是一个非线性决策边界,它正确地分类了之前被错误分类的蓝色点。支持向量(Support Vectors)是那些在最优解中系数 λ 非零的点,它们决定了边界的位置。


高斯核函数(RBF核函数)

另一种有趣的核函数类型是高斯核函数(Gaussian Kernel),也称为径向基函数核(RBF Kernel)。其形式如下:

公式: K(x, z) = exp(-γ ||x - z||^2)

这个核函数可以视为相似性度量。当 x 等于 z 时,它取最大值1;当 x 远离 z 时,它衰减到0。这个核函数与一个无限维的特征集相关联,但我们可以通过简单地将这个核函数定义插入到核化支持向量机中来使用它。

以下是一个高斯核函数的示例代码:

代码:

# 使用高斯核函数的SVM
svm_rbf = SVC(kernel='rbf', gamma=0.5)
svm_rbf.fit(X, y)

通过调整 gamma 参数,我们可以得到不同的决策边界,从围绕点的局部区域到更类似于多项式的边界。


核函数的数学理论

我们已经看到,对于许多特征 φ,我们可以定义核函数来高效地计算特征的点积。现在,考虑一个任意形式的核函数 K,是否存在一个隐含的特征映射与之相关联?换句话说,如果我们在核化支持向量机的定义中插入任意定义的 K,我们是否隐式地在某个高维特征空间中进行回归或分类?

答案是,有一个数学理论来确定是否如此。首先,如果 K 对应于某个特征集 φ,那么它必须满足以下性质:对于任意点集,核矩阵(Kernel Matrix)必须是对称且半正定的(Positive Semidefinite)。

有一个重要的定理称为默瑟定理(Mercer's Theorem),它指出这个必要条件也是充分条件。换句话说,只要我们定义的核函数满足默瑟定理的条件,我们就隐式地在某个高维特征空间中进行分类或回归,而无需实际计算这些特征。


核函数的优缺点

核函数显然是一个非常强大的工具,但它们是否是一个免费的午餐,可以随处使用呢?答案是否定的。核函数的一个主要优点是,我们可以使用非常高维的特征,而无需实际形成这些特征,所有计算仍然在属性数量上是线性的。然而,这些方法的计算成本至少是数据集大小的平方,即 O(n^2),其中 n 是数据集大小。

原因是,为了形成数据的核矩阵,我们需要计算所有点对之间的距离,这至少需要 n^2 次操作。因此,所有使用核技巧的核化算法都是非参数(Nonparametric)的,其复杂度随着数据集大小的增加而增加。

对于非常大的数据集(如数百万张图像),核方法可能难以扩展。在实践中,对于小型到中型数据集(例如数万到数十万个数据点),核方法非常有效,易于训练,并且通常能提供准确的解决方案。


总结

在本节课中,我们一起学习了核函数的概念及其在机器学习中的应用。核函数可以视为定义向量对之间相似性的一种方式,并且通常与高维特征相关联。许多机器学习算法可以用核函数来定义,这样我们就可以在不增加额外成本的情况下使用非常高维的特征。因此,核函数是机器学习中一个非常强大的思想。

12.1:决策树(第一部分)🎯

在本节课中,我们将学习一种新的监督学习算法——决策树。这与我们之前见过的监督学习算法有很大不同。之前的算法多从数学建模的角度出发,而决策树则试图模仿人类在解决特定机器学习任务时的决策过程。我们将首先定义什么是决策树,然后探讨如何学习它们。

概述

决策树属于基于树的算法家族。它是一种直观的模型,通过一系列简单的规则来做出预测。本节我们将正式定义决策树及其核心组成部分。

决策树模型定义

回顾监督学习的定义,我们需要定义数据、模型类别、目标函数和优化器。决策树是一种与我们之前所见截然不同的模型类别。

为了定义这个模型类别,我们以之前见过的糖尿病数据集为例。

数据集示例:糖尿病风险预测

该数据集包含约442名患者的各项生理测量指标,例如年龄、性别、BMI、血压等,共10个属性。我们的目标是根据这些属性预测患者的糖尿病风险。为简化问题,我们将风险二值化:风险值大于150为高风险,否则为低风险。

人类决策过程的模拟

想象一下,如果是一位医生来诊断,他可能会遵循一个基于规则的过程。例如,先看患者的年龄,如果是老年人,再考虑性别,接着可能查看特定的生理指标。这种分步骤、基于规则的决策方式,正是决策树试图模仿的。

决策树算法示例

以下代码展示了如何使用Scikit-learn中的决策树分类器,并在糖尿病数据集上进行训练和可视化:

from sklearn.tree import DecisionTreeClassifier, plot_tree
# 假设 X_train 包含特征数据,y_train 包含标签(高风险/低风险)
clf = DecisionTreeClassifier(max_depth=2)
clf.fit(X_train[:, :3], y_train) # 仅使用前三个特征
plot_tree(clf)

运行后,我们会得到一个树状图。树的每个节点对应一个基于数据的决策规则。例如,第一个规则可能是“BMI是否小于0.009”。根据这个判断,数据会流向左侧或右侧分支。在第二层,可能根据血压值再次进行划分。最终,每个叶节点代表一个决策区域,并给出该区域内样本的风险分布(例如,86个样本中,76个高风险,10个低风险)。这个树结构以一种完全数据驱动的方式,识别出了具有高BMI和高血压的高风险患者群体。

决策树的形式化定义

现在,让我们更精确、更形式化地定义决策树。

核心概念1:决策规则

决策规则是决策树最基本的构建模块。本质上,一个决策规则将特征空间划分为两个互不相交的区域。

  • 形式:一个规则通常只应用于一个特征或属性。它输出一个二元结果(真/假)。
  • 数学表示:对于连续变量,规则通常基于一个阈值 c 进行划分。规则可以表示为:
    • 如果 x_j < c,则流向左侧分支。
    • 如果 x_j >= c,则流向右侧分支。
  • 图形解释:在决策树中,规则与内部节点关联。根据规则的结果(真/假),数据实例会沿着相应的边(左或右)流向子节点。

核心概念2:决策区域

决策区域由一系列决策规则定义。

  • 定义:考虑一个由 M 个规则组成的序列。对于这些规则结果的每一种可能赋值组合(例如,规则1为真,规则2为假……),我们都会得到一个特定的决策区域。
  • 数学表示:区域 R 是满足该特定规则赋值组合的所有输入 x 的集合。
  • 与树的关系:在决策树中,这些区域对应着树的叶节点。从根节点到叶节点的路径,定义了一个唯一的规则序列,从而确定了一个唯一的区域。

决策树的数学表达

有了区域的概念,我们可以如下定义决策树模型:

决策树将特征空间划分为一系列决策区域 R ∈ ℛ。对于每个区域 R,模型学习一个预测值 y_R。那么,对于任意输入 x,决策树的预测 f(x) 为:

f(x) = Σ_{R∈ℛ} y_R * I(x ∈ R)

其中,I(·) 是指示函数(当条件为真时值为1,否则为0)。由于区域互斥且完备,对于任何 x,有且仅有一个区域的指示函数值为1,因此 f(x) 就等于 x 所属区域对应的预测值 y_R

需要强调的是,这些区域是通过递归分割得到的,这使得它们可以被组织成树形结构。

决策树结构图示

以下是一个决策树及其对应特征空间划分的经典示例(参考Hastie等人教材):

  • 树结构:根节点根据 x1 是否小于 t1 进行分割。右侧分支继续根据 x1 是否小于 t3 分割,然后再根据 x2 进行分割,最终形成5个叶节点(区域R1-R5)。
  • 区域划分:在二维特征空间 (x1, x2) 中,这些规则将空间划分为多个矩形区域。每个矩形对应树中的一个叶节点。
  • 预测可视化:如果为每个区域分配一个预测值(例如一个实数),我们可以在三维空间中将其可视化为一个阶梯状的平面。

重要说明:并非所有区域划分都能由决策树产生。决策树产生的区域具有轴平行(axis-aligned)的特性。例如,一个斜对角划分的区域就无法通过仅基于单个变量阈值的递归分割得到。

决策树的优缺点

决策树是一种直观且重要的机器学习算法。

优点

  1. 高度可解释性:树结构可以轻松可视化,规则易于人类理解和表述。
  2. 数据预处理要求低:无需担心特征缩放、连续/离散变量编码等问题。同时适用于回归和分类任务,是一种便捷的“开箱即用”算法。

缺点

  1. 模型能力与复杂度权衡:如果树很小(简单),则模型可能不够强大(欠拟合)。如果树很大(复杂),则极易过拟合训练数据。
  2. 正则化困难:对于过拟合的决策树,没有特别直观的正则化方法(尽管存在剪枝等技术)。

尽管如此,决策树仍然是机器学习中一个非常重要且被广泛使用的算法。

总结

本节课我们一起学习了决策树的基本概念。我们了解到决策树是一种模仿人类规则推理的监督学习模型。它通过递归地应用基于单个特征的简单决策规则,将特征空间划分为不同的区域,并在每个区域给出预测。我们形式化地定义了决策规则、决策区域以及整个决策树模型,并通过示例说明了其工作原理和直观解释。最后,我们讨论了决策树易于解释、预处理要求低等优点,以及可能存在的过拟合和正则化挑战等缺点。在接下来的课程中,我们将深入探讨如何从数据中学习(构建)这些决策树。

12.2:决策树学习(第二部分)🌳

在本节课中,我们将学习如何从数据中构建决策树。上一节我们介绍了决策树的基本概念,本节中我们来看看具体的构建算法。

决策树回顾

决策树是一个函数,其形式如下:

\[f(x) = \sum_{R \in \mathcal{R}} y_R \cdot \mathbb{I}(x \in R) \]

决策树将特征空间 X 隐式地划分为一组互不相交的区域 R,每个区域属于集合 \mathcal{R}。给定输入 x,它属于其中一个区域,指示函数 \mathbb{I}(x \in R) 在该区域非零,在其他区域为零,从而输出预测 y

我们可以将决策树可视化为树结构。区域对应底部的叶子节点,每个内部节点对应一个二元规则,决定我们是向左走还是向右走。

决策树构建算法

现在,我们来看看如何从数据中学习这些决策树。从高层次看,构建树的伪代码过程相当简单。

以下是构建过程的高层伪代码:

while tree_not_ready():
    region = get_region_from_tree()
    data_in_region = get_data_in_region(region)
    new_rule = split_region(data_in_region)
    add_rule_to_tree(new_rule)

本质上,我们重复此过程直到树构建完成。通常,当树达到特定节点数时,我们认为它已就绪。在树仍在构建时,我们从树中检索一个区域(对应一个叶子节点),并获取映射到该区域的数据(即所有属于该区域的训练实例)。然后,我们根据该区域内的数据,通过将区域分成两部分来创建新规则。

这个过程的核心是 split_region 函数,它决定了基于该区域的数据添加什么新规则。

如何选择分裂规则?

split_region 函数如何选择规则?想象我们有一个数据集 D。需要强调的是,这个过程是贪婪的,因此一旦选择了一个区域,我们只关注该区域的数据。为了确定添加哪个规则,我们只需查看所有可能的规则集,并选择一个在该数据集上获得最低可能损失的规则。

用数学语言描述,我们有一个定义在数据集上的损失函数 L。规则将点集分为预测为“真”和“假”的两部分。我们在这两个区域中定义损失的概念。我们的优化问题是选择一个规则,使得这两部分损失之和尽可能小。

我们考虑一组可能的规则。首先,让我们看看可能的规则集 S 是什么。

可能的规则集

如前所述,这些规则通常非常简单。首先,我们总是希望只在一个属性上进行分裂。如果我们想在两个属性上分裂,我们可以通过两个连续的规则来实现。对于连续变量,我们选择一个阈值 t,规则为:如果 x < t 则为真,否则为假。对于分类特征,由于没有顺序,我们通常选择一个特定类别,如果属性取该值则为真,否则为假。

对于每种规则,我们希望在数据集上定义某种损失概念。请注意,这里我没有定义模型的任何预测,因为预测将由此规则进入的数据集自动确定。

回归问题的损失函数

如果我们的目标变量 y 是连续的,一个常见的损失是 L2 误差。给定数据集 D 中的一组数据点,我们首先计算该点集中预测的平均值。根据我们的最小二乘误差损失,在该分裂内的最佳预测就是平均值,该平均预测的损失就是所有点的 L2 损失之和。如果数据不平衡,我们可能还需要取平均。

如果这个区域最终成为叶子节点,那么我们在该区域的预测值就是该区域内 y 的平均值。

使用均方误差损失重写我们的分类规则,公式如下:

\[L(D_{\text{true}}) = \sum_{i \in D_{\text{true}}} (y_i - \bar{y}_{\text{true}})^2 \]

其中,\bar{y}_{\text{true}}D_{\text{true}}y 的平均值。

这种损失会激励我们将所有具有相同预测值的样本组合到树的同一个节点中。换句话说,如果我们能将在该区域内目标值 y 非常相似的所有 y 聚集在一起,那么这种损失就会很小。

分类问题的损失函数

如果我们处理的是分类问题,可以遵循类似的方法。最简单的方法是使用误分类率,即计算有多少点被错误分类,并取平均值。当然,给定一个分裂,对这些点的类别的最佳猜测就是这些点中最常见的类别。

如果这是最终的叶子节点,那么我们只需预测这个最常见的类别作为决策树的预测。

还可以使用其他损失,例如基尼指数。这些损失都是为了优化两个类别不混合的分裂。同样,最佳分裂是能够将所有同类点聚集在一起的分裂。

算法总结与注意事项

以上就是构建这类决策树的高层直觉。当然,我省略了许多实践细节。在实践中,实现一个好的决策树学习算法需要大量工作,有许多文献和开源包经过多年迭代,包含许多重要的实践细节和启发式方法。

一个实践方面是,在实践中,树被构建到一定深度,然后会被剪枝。实际上,先构建一个较大的树,然后移除那些对优化损失没有太大帮助的分支更容易。这有助于防止树对数据过度拟合,也使树更紧凑、节点更少,从而更具可解释性。

其他算法变体

我定义了一种非常流行的构建树的方法,但这个算法存在变体。有些方法可能不是递归的。实现替代方法的一些算法名称包括 ID3C4.5C5.0。这些是较旧的算法,但在许多机器学习库中仍用于构建决策树。

完整算法定义:CART

现在总结一下我们所看到的内容。结合第一个视频中决策树的定义(我们的模型类定义),以及本节定义的优化目标、优化过程,我们现在有了一个完整的机器学习算法的定义。它有时被称为“决策树”,但如前所述,构建决策树有略微不同的方式。为了更精确,我将这个算法定义为 CART,这是一个更精确的术语。

CART 代表分类与回归树。这是一种监督学习算法,顾名思义,可以解决回归和分类问题。模型族是决策树,目标函数是我之前提到的所有规则,优化器是我之前给出的贪婪方法,并附加了一些启发式方法(如剪枝)。

本节课中,我们一起学习了决策树的核心构建算法 CART,理解了其贪婪分裂的本质、不同问题下的损失函数选择,以及一些重要的实践考量,如剪枝。

12.3:第3部分:Bagging 🎒

在本节课中,我们将学习一种名为 Bagging 的技术,它可以帮助我们减少机器学习模型(特别是决策树)的过拟合问题。上一节我们介绍了决策树算法及其局限性,本节中我们来看看如何通过Bagging来提升模型的稳定性和泛化能力。

概述 📋

Bagging,全称 Bootstrap Aggregation(自助聚合),是一种通过组合多个模型来降低预测方差、减少过拟合的通用方法。它的核心思想是:通过从原始数据集中抽取多个随机子集(称为自助样本),在每个子集上独立训练一个模型,然后将这些模型的预测结果进行平均,从而得到一个更稳定、更准确的最终预测。

过拟合与高方差问题 🔍

在深入Bagging之前,我们需要理解它要解决的核心问题:过拟合 及其导致的 高方差

过拟合是机器学习中一种常见的失败模式。当我们训练一个表达能力过强的模型(例如一个非常高阶的多项式)时,它能够完美地拟合训练数据。但由于训练数据并不能覆盖真实世界中所有可能的情况,模型在训练集上表现优异,却无法泛化到新数据上。它完美地拟合了数据,但在训练点之外的预测可能非常糟糕。

为了直观说明,我们来看一个例子。假设我们有一个由正弦和余弦混合而成的真实函数(蓝色曲线),并从该函数附近随机采样了40个带噪声的数据点(蓝色散点)。我们的目标是从这些带噪声的样本中学习真实函数的形状。

如果我们用一个高阶多项式来拟合这些数据,会发生什么?以下是具体步骤:

  1. 我们从40个点中随机抽取20个点(有放回抽样),形成一个子数据集。
  2. 在这个子数据集上,我们训练一个高阶多项式回归模型。
  3. 我们重复此过程多次,每次使用不同的随机子集。

以下是观察到的结果:

  • 每个子数据集上训练出的多项式模型(橙色曲线)形状差异巨大。
  • 尽管我们使用完全相同的机器学习算法(相同超参数的多项式回归),但不同数据子集产生的预测模型却截然不同。
  • 在训练数据稀少的区域,这些模型的预测值会出现剧烈且不合理的波动。

这种现象就是 高方差 问题。如果一个算法容易过拟合,那么对训练数据的微小扰动(例如,更换一个数据点或抽取不同的子集)就会导致最终学到的模型发生巨大变化。这种预测模型随数据扰动而剧烈变化的特性,我们称之为高方差。

Bagging 解决方案 🛠️

那么,如何减少过拟合和高方差行为呢?Bagging 的核心思想就是:通过平均多个在数据随机子集上训练的模型,来降低模型空间的巨大变异性。

“Bootstrap”指的是我们从训练集中有放回地随机抽取子集的过程。每次抽取得到的随机子集称为一个 自助样本。它显然与原始数据具有相同的分布,但样本点会有所不同。

我们可以在每个自助样本上训练一个模型。然后,如果我们对这些模型的预测进行平均,那么即使每个模型都会犯自己的错误(例如,一个模型可能在某些区域过度预测,另一个则可能预测不足),只要我们有足够多的模型,这些随机误差就会相互抵消。最终,我们将得到一个对数据微小变化更加稳定的预测模型,从而显著减少过拟合。

以下是 Bootstrap Aggregation 的伪代码流程:

# 输入:训练集 (X, y),基础模型算法,自助样本数量 M
# 输出:集成模型 Ensemble

1. 初始化一个空的模型集合 Ensemble = []
2. 对于 i 从 1 到 M:
    a. 从训练集 (X, y) 中有放回地随机抽取 n 个样本,构成自助样本 (X_i, y_i)
    b. 使用基础模型算法在 (X_i, y_i) 上训练一个新模型 model_i
    c. 将 model_i 添加到 Ensemble 中
3. 对于新的测试点 x_test,其最终预测为:
   prediction(x_test) = average( model.predict(x_test) for model in Ensemble )

在机器学习中,这种组合在一起的模型集合被称为 集成

Bagging 实战演示 📈

现在,让我们将自助聚合应用到之前的多项式回归问题上。我们将从40个数据点中抽取大量(例如10,000个)包含30个点的自助样本,并在每个样本上训练一个多项式回归模型,最后将所有模型的预测进行平均。

以下是演示结果的关键观察:

  • 单个模型(橙色曲线):每个模型仅在其训练的子集(蓝色散点)上表现,在数据稀少区域表现出剧烈且不一致的波动,模型间差异很大。
  • Bagging集成模型(绿色曲线):这是10,000个类似橙色模型的预测平均值。尽管它没有在训练集上达到完美拟合(尤其是在数据本身稀少的区域),但其整体行为更加合理和稳定。在单个模型犯巨大错误的区域,绿色曲线的预测要好得多。

这个例子清晰地展示了 Bootstrap Aggregation 如何能显著减少过拟合

Bagging 的变体与扩展 🔄

Bagging是一种在机器学习中广泛应用的方法。除了标准形式,还有一些值得了解的扩展:

  • Pasting:如果我们进行的是 无放回 抽样,这种方法有时被称为 Pasting。
  • 随机特征:我们也可以在特征空间而非样本空间进行子采样,即随机抽取一部分特征来训练模型,这也有助于防止过拟合,称为随机特征。
  • 随机修补:如果同时结合样本子采样和特征子采样,我们就得到了随机修补。

总结 🎯

本节课中我们一起学习了 Bagging 技术。它是一种强大的、与模型无关的集成方法,能够有效降低机器学习模型的过拟合和高方差。其工作原理是:

  1. 从原始数据集中生成多个自助样本。
  2. 在每个自助样本上独立训练一个基础模型。
  3. 将所有基础模型的预测结果进行平均,作为最终输出。

Bagging 的优势在于它是一个“黑盒”技术,不需要了解被聚合的基础模型的内部细节,因此可以方便地应用于各种机器学习算法,在需要提升模型稳定性和泛化能力的实际应用中非常有用。

12.4:第4部分:随机森林 🌲

在本节课中,我们将学习一种新的基于树的算法——随机森林。我们将看到决策树存在过拟合问题,而随机森林通过一种称为“装袋法”的技术来解决这个问题。随机森林使用决策树作为基础构建块,通过组合多个树的预测来获得更稳定、更准确的结果。


决策树回顾与过拟合问题

上一节我们介绍了决策树算法。决策树通过一系列规则将特征空间划分为多个区域(记为 R)。对于新输入 x,算法首先判断其落入哪个区域,然后输出该区域的预测值。在实践中,这些规则可以组织成树状结构。

例如,一个基于两个变量 x1x2 的决策树,通过判断 x1 > T1 等条件,将空间最终划分为五个区域。

然而,决策树有一个显著问题:过拟合。这意味着模型过于复杂,过度学习了训练数据中的噪声和细节,导致在新数据上表现不佳。


可视化决策树的过拟合

为了具体理解过拟合,我们使用经典的鸢尾花数据集进行实验。该数据集包含150朵花的测量数据(以厘米为单位)和对应的三种鸢尾花类别。

以下是使用数据集前两个特征训练决策树并可视化其决策边界的结果:

从图中可以观察到两个主要问题:

  1. 决策边界不平滑且呈块状:模型只能表示由轴平行线划分的矩形区域,这通常不符合真实数据中更可能存在的平滑或对角线边界。
  2. 明显过拟合:模型为了正确分类少数异常点(例如图中孤立的棕色点),在两类别的中间区域划分出一个巨大的棕色块。这种过于精确和复杂的边界形状对未见过的数据泛化能力差。


高方差问题

过拟合与高方差问题密切相关。如果一个算法是高方差的,意味着训练数据的微小扰动会导致生成的预测模型发生巨大变化。

我们可以通过一个实验来可视化决策树的高方差性:从数据中多次随机抽取40个样本子集,并在每个子集上训练一个独立的决策树。

结果显示,每个决策树产生的决策边界都完全不同。这戏剧性地证明了决策树的高方差问题——数据的小幅变化会导致预测结果的巨大波动。


随机森林:用装袋法解决过拟合

随机森林正是为了解决决策树的过拟合和高方差问题而设计的。其核心思想是应用我们之前学过的装袋法技术。

随机森林本质上就是装袋法应用于决策树模型,即一个由多个装袋决策树组成的集成模型。

装袋法的伪代码如下,其中基础学习器 base_model 被替换为决策树:

def bagging(X, y, base_model=DecisionTree, n_models=300):
    models = []
    for i in range(n_models):
        # 1. 自助采样
        X_sample, y_sample = bootstrap_sample(X, y)
        # 2. 在采样数据上训练一个基础模型(决策树)
        model = base_model.fit(X_sample, y_sample)
        models.append(model)
    return models

def predict_bagging(models, X_new):
    # 3. 对所有模型的预测进行平均(分类问题可改为投票)
    predictions = [model.predict(X_new) for model in models]
    return np.mean(predictions, axis=0)


随机森林的效果

现在,让我们在鸢尾花数据集上应用随机森林。我们训练一个包含300棵树的随机森林,每棵树仅使用10个随机数据点进行训练。

与单棵决策树相比,随机森林的决策边界发生了显著改善:

  • 边界更平滑、更自然:出现了我们期望的平滑曲线,而不是锯齿状的块状边界。
  • 过拟合大大减少:模型不再为孤立点创建不合理的独立区域,决策边界更加直观。
  • 泛化能力更强:整体预测行为更好,对新数据的泛化能力预计会更强。

这是因为,尽管每棵单独的树可能都在过拟合数据,但它们在训练集外会以随机的方式犯错。通过对足够多的树进行平均,这些随机的过拟合误差会相互抵消,从而得到更稳定的预测。


随机森林的优缺点总结

本节课我们一起学习了随机森林算法。它基于决策树,使用装袋法进行集成,有效缓解了过拟合问题。

以下是随机森林相对于其他算法的优缺点:

优点:

  • 继承决策树的优点:无需对输入特征进行缩放;能同等处理连续和离散特征;无需大改即可同时适用于分类和回归任务。
  • 实践中的高准确性:随机森林是一类非常有竞争力的算法,在许多经典机器学习基准测试和Kaggle竞赛中表现优异。

缺点:

  • 丧失可解释性:虽然单棵决策树易于解释,但由数百棵树平均得到的随机森林模型不再具有可解释性。
  • 对原始非结构化数据效果有限:尽管准确,但随机森林通常在经过预处理的特征上效果更好。对于图像、音频等原始非结构化数据模态,其表现不如深度学习等专门模型。

总结:随机森林通过集成多个高方差、低偏差的决策树,有效降低了整体模型的方差,从而获得了更优的泛化性能。它是一种强大且实用的机器学习工具。

13.1:第1部分:提升与集成学习 🚀

在本节课中,我们将学习一类新的机器学习算法,其核心思想称为“提升”。我们将从上一讲关于决策树的内容出发,探索如何将决策树等简单算法与其他算法结合,构建更复杂、更强大的机器学习模型。本讲介绍的提升算法在实践中非常重要,它们代表了在许多实际关键问题上机器学习精度的最先进水平。

从装袋法到提升法 📦

上一讲中,我们讨论了过拟合问题。这是机器学习中常见的一种失效模式,尤其是在使用大型决策树时。问题在于模型表达能力过强,对于我们所拥有的少量数据来说过于强大,因此它能完美拟合训练集,但无法泛化,无法在训练集之外产生准确的预测。

我们介绍了一种通过平均多个模型来减少机器学习模型过拟合的通用方法,这些模型各自在数据的随机子集上训练。这被称为装袋法。在装袋法中,我们反复从训练集中抽取随机子样本,在每个随机样本上拟合一个新模型,并将其添加到我们的模型集合中。在预测时,我们输出所有这些模型的平均值。由于每个模型都是在略有不同的训练数据版本上训练的,它们在训练区域之外产生的误差往往不同。具体来说,这些误差会相互抵消。一个模型可能预测过高,另一个可能预测过低。但平均而言,这种抵消会产生泛化能力更好的预测。

与过拟合相反的问题是欠拟合。当我们的模型对于所拥有的数据来说过于简单时,就会发生欠拟合。例如,如果数据更适合用高阶多项式描述,但我们尝试使用线性回归,那么我们将无法很好地拟合训练数据,因此对于来自与训练数据相同分布的新数据,我们也不会准确。

提升法的核心思想 ⬆️

本讲将介绍的提升法,可以看作是通过将不同的机器学习模型组合成一个更大、更强大的模型来减少欠拟合的一种方法。从这个意义上说,它与装袋法非常相似。我们将许多更简单的模型组合成一个更大的模型,我们称之为集成模型,用 F 表示。

但与装袋法不同,装袋法中的基模型往往表达能力过强,我们希望通过平均来减少其过拟合;而在提升法中,这些基模型 G 太小、太简单,往往对数据欠拟合。我们使用提升法使模型集成比每个单独模型更强大、更具表现力。

我们实现这一目标的一般方法是:让每个新添加到集成中的模型,查看先前模型的误差,并尝试拟合其他模型未能很好拟合的那部分数据。这样,每个新模型都会纠正集成中先前模型所犯的错误。

弱学习器 🤖

定义或使用提升算法的一个关键要素是模型 G,它被称为弱学习器。之所以称之为弱学习器,是因为对这些模型的唯一要求是它们比随机猜测略好一些。在实践中,弱学习器可以是线性模型、特征数量较少的简单线性模型、小型决策树(如深度为1、2或3的决策树),或者任何确实对数据欠拟合但仍能提取少量信号、使其准确率高于随机猜测的模型。

提升算法的一般步骤 🔄

给定一个能产生比平均略好模型的弱学习算法,我们可以通过以下方式将这些模型组合成一个更强大的模型,这是提升法采用的一般方法。

  1. 我们从一个初始弱学习器开始,将其拟合到我们的数据集上,并最初将我们的集成模型设置为该模型。
  2. 然后,我们根据当前集成模型在这些数据点上的准确性来计算数据点的权重。如果我们的集成模型 F(最初仅由基学习器组成)是准确的,那么该点的权重将很小;如果它非常不准确,我们将为不准确的点分配更多权重。
  3. 接着,我们将在同一数据集上拟合下一个弱学习器,但现在我们将添加这些权重 W。我们将通过这个权重来衡量每个点的损失。因此,现有模型准确性较低的点将拥有更大的权重,我们将倾向于更多地最小化这些点的误差,而不是在模型更准确的其他点上。
  4. 现在,我们有了第二个弱学习器,它经过训练以拟合先前模型的误差,我们将其以某个权重 α 添加到我们的集成中,该权重决定了这个新基学习器的重要性。
  5. 然后我们回到步骤2,重新计算权重,训练另一个弱学习器,并重复步骤2、3、4,直到我们得到一个足够强大的模型 F

可以看到,在每一步,模型 F 都会变得越来越有表现力,因为它将是大量弱学习器的总和,每个弱学习器在数据的不同子集上是准确的,因此它们的集成将能够在我们整个训练数据集上实现良好的准确性。

如果用Python实现,可以使用以下通用结构:最初从一组均匀的权重开始,然后如果要创建包含多个模型的集成,则使用初始权重集拟合模型,查看其预测,创建新的权重集,然后将其添加到集成中。输出将是某种共识,例如在此过程中训练的所有弱学习器预测的加权平均值。

AdaBoost 算法简介 📜

提升法最初源于机器学习理论文献。有一个理论问题是:是否可能将每个仅比随机猜测略好(准确率略高于50%)的弱学习器组合成一个具有高准确率的强学习器,并以一种可证明的方式,用定理保证该算法的成功。这个问题在80年代末和90年代初被研究,最初得出了一些理论结果,最终这条研究路线催生了第一个实用的提升算法,称为 AdaBoost。AdaBoost 在历史上非常重要,从理论角度看,它解决了人们提出的这个理论问题;在实践中,它长期以来一直是一个非常强大的机器学习算法,应用广泛,并长期被认为是该领域的最先进技术。

AdaBoost 最初是为分类问题开发的算法,尽管也有适用于回归的版本。它学习一个弱学习器的集成,这些弱学习器通常是决策树,使用一种称为指数损失的目标函数,以及一种特定形式的优化器。我们将在下一个视频中讨论这些细节。

以下是该算法的高层结构,作为一个具体的提升算法示例:

  1. 从一组均匀的权重和一个空集成开始。
  2. 进行多次迭代:
    • 拟合一个弱学习器(例如决策树)。
    • 计算加权误分类误差。如果所有权重为1,则这只是误分类点的百分比;如果权重非零,则这是误分类点的加权平均值。
    • 定义模型权重 α,它是误分类误差的简单函数。
    • 以确定的权重 α 将此弱学习器添加到我们的集成中。
    • 使用特定的更新规则计算数据点的新权重 W,该规则通过 α 的指数来增加误分类数据点的权重。这保证了所有权重为正,并且我们出错的点拥有更大的权重。

这个过程遵循我们之前看到的高层提升结构:我们为准确性较低的点分配更多权重。重复此过程,我们就得到了 AdaBoost 算法。

AdaBoost 实践演示 🎯

现在让我们看看这个算法在一个小型玩具数据集上的实际效果。这里我们使用一个简单的二维数据集,其中有两个类别(蓝色和橙色)。这个数据集明显是线性不可分的:蓝色类别被橙色类别包围在一个簇中,而在另一个簇中,橙色类别被蓝色点包围。这是一个复杂的数据集。

让我们在这个数据集上训练 AdaBoost。在实践中使用 AdaBoost 很简单,例如使用 scikit-learn 库。我们可以导入 AdaBoostClassifier。由于它是一个提升算法,它需要一个基分类器(弱学习器),这里我们定义为一个深度为1的决策树分类器。我们将学习一个包含200个这样的分类器的集成。

定义此算法非常简单,我们可以用一行代码将其拟合到数据上。结果给出了以下决策边界。可以看到,即使使用一个非常简单的模型(200个深度为1的决策树),我们也能够学习到这个数据的结构。算法识别出橙色类别几乎正确地包围了蓝色区域,而在另一个区域则相反。使用一个非常简单的现成模型,无需任何重要的超参数调整,就能够学习到这个高度非线性可分的结构。这初步展示了提升算法的强大能力。

集成学习概览 🤝

需要指出的是,提升法和装袋法是称为集成学习的通用思想的特例。集成学习意味着将多个机器学习模型组合成一个更大、更强大的模型。装袋法和提升法是两种最流行的集成技术,分别用于减少过拟合和欠拟合。但还有其他类型的集成方法也值得了解。

其中之一是堆叠法。堆叠法意味着我们训练 M 个输入模型,每个模型都在我们的训练数据下训练。然后我们冻结它们,再训练另一个模型,该模型的输入特征是这些模型的输出。因此,输出模型 F 的输入属性将是模型 G 的输出。这是一种简单的尝试方法,当基模型 G 差异很大时效果最好,例如结合决策树、支持向量机、线性模型和朴素贝叶斯,然后以这四个输出作为特征训练另一个模型,这通常是一种可以有效带来额外性能提升的方法。

另外需要强调的是,我们在早期讲座中看到的贝叶斯方法,在技术上也是一种集成形式,因为我们平均了许多具有不同参数 θ 的模型,并有效地使用了这些权重集合。贝叶斯方法实际上是定义集成算法的一种非常有原则的方法。在实践中,这个公式可能无法精确实现,但许多集成算法在某种意义上都是这个公式的特例,因为它们提供了对其的近似。

集成学习的理念对于应用机器学习非常有用。在实践中,使用许多最先进的算法时,如果我们尝试集成,通常总能再挤出一点性能。例如,对于神经网络,如果我们从不同的初始点训练几种不同类型的神经网络,我们可以进一步提高性能。此外,集成很重要,因为机器学习中的许多算法,如提升算法,实际上都是集成的形式。当然,在实践中我们可能并不总是能够使用集成,因为训练多种不同类型的模型成本很高。

本节课我们一起学习了提升算法的核心思想、一般步骤,并通过 AdaBoost 了解了其具体实现。我们还探讨了提升法与装袋法的区别,以及集成学习的 broader 概念。下一节我们将从另一个视角来看待提升法,并利用它来定义新的算法。

13.2:加法模型 📈

在本节课中,我们将学习加法模型。这是一种理解提升算法的新视角,它能帮助我们解释已讨论过的提升算法的行为,并用于推导新型的提升算法。

加法模型概述

上一节我们介绍了提升算法。本节中,我们来看看加法模型。回忆一下,提升是一类用于组合模型以减少欠拟合问题的算法。当我们的模型过于简单,无法捕捉数据中的复杂模式时,就会发生欠拟合。例如,如果数据由高次多项式描述,而我们试图用线性模型去拟合,那么我们将无法很好地拟合训练数据,也无法对新数据做出良好预测。

提升的核心思想是通过组合许多不准确的模型来减少欠拟合。这些模型以集成的方式组合。与我们之前看到的装袋法不同,这些模型往往不准确,但通过在每个新模型上重点训练先前模型犯最大错误的那些数据点,它们可以组合成一个更准确的模型。

在机器学习中,提升更一般地被称为加法模型。加法模型是机器学习模型的一个重要类别,我们见过线性模型、多项式模型,而加法模型是另一个重要的类别。

加法模型的结构

加法模型之所以得名,是因为它是多个较简单模型的加权和。

其结构如下:

F(x) = Σ α_t * G_t(x; φ_t)

其中:

  • F(x) 是最终的集成模型。
  • G_t(x; φ_t) 是第 t 个基学习器(或子模型),它有自己的参数集 φ_t
  • α_t 是权重,表示该基学习器在集成中的重要性。
  • 整个集成模型 F 的参数是所有 α_tφ_t 的集合。

这个模型比我们目前见过的模型更通用、更强大。例如,它比线性模型更强大,因为这里的每个 G_t 可以复杂得多。事实上,这个模型在其参数上不一定是线性的,因为 G_t 在其自身参数上可能不是线性的。例如,如果 G_t 是决策树,那么它将是一个非线性模型。这使得集成更强大,但也使其更难训练。

训练加法模型:前向分步加法建模

我们见过的训练这类加法模型的一种方法当然是提升。这里简要总结一下提升方法:我们从一个初始的弱学习器开始,在数据上训练它;然后计算权重,这些权重在 F 犯错的数据点上较高,在 F 准确的数据点上较低;我们使用这些权重在数据上训练一个新的弱学习器,因此弱学习器会优先处理先前模型犯错的数据点;然后将其添加到我们的集成中,并重复此过程。

这是训练加法模型的一种方法。但它恰好是一个更通用框架的一个实例,这个框架被称为前向分步加法建模

这种方法是一种通用方法,适用于拟合任何类型的加法模型,可以使用任何类型的基学习器和各种目标函数。提升算法可以作为这个框架的一个特例被推导出来。

假设我们有一个损失函数 L 来衡量我们拟合的好坏。我们最初从一个模型开始,该模型仅由一个基学习器 G 构成,我们通过最小化损失来将其拟合到训练数据集上。

然后,在每次后续迭代 t 中,我们考虑当前集成模型 F_{t-1},并希望添加一个新的基学习器 α_t * G_t(φ_t)。我们通过解决以下优化问题来确定新模型的参数 φ_t 和权重 α_t

(α_t, φ_t) = argmin_{α, φ} Σ L(y_i, F_{t-1}(x_i) + α * G(x_i; φ))

关键点在于,我们保持 F_{t-1} 中所有先前基学习器的参数固定不变,只优化新添加的 αφ。如果基模型足够简单,那么可以高效地执行此最小化。这将改进先前的误差,因为我们总是可以将 α 设为 0,新模型永远不会比之前的模型差。

当然,这种方法也有问题,即我们可能过于贪婪,或者可能错过某些更好的解。例如,如果我们能联合优化集成内部所有子模型的参数,可能会找到更好的解,但我们不这样做,只优化新添加的部分。这阻止我们找到集成的全局最优参数集,但它保持了优化过程的易处理性。

实践要点与损失函数

在深入探讨加法模型之前,我们先介绍一些实践要点,更详细地描述这些组件可能是什么样子。

以下是可用于此方法的常见基模型 G

  • 决策树:在实践中常用于提升。
  • 三次样条:可以以这种方式拟合的模型示例。
  • 核化模型:也可以在这里使用。

我们可以重复此训练过程一定次数的迭代(次数可以是固定的),或者我们可以在一个验证集上监控性能。当我们看到验证集性能不再提高,而我们只是在减少训练误差但验证误差没有改善甚至增加时,我们就知道这是开始过拟合数据的时候,可以停止添加新模型。这有时也称为早停

使用或拟合这类模型时,一个重要的设计选择是损失函数 L。以下是几种用于此类加法模型的损失函数示例。

指数损失

我们想在加法模型的背景下介绍一种以前从未见过的新型损失:指数损失

指数损失的公式如下:

L(y, F) = exp(-y * F)

这里,y 是我们的真实标签,F 是对 y 的估计值。y 是 -1 或 1,所以这是用于二分类的损失。我们可以验证这是一个合理的损失,因为如果真实类别 y=1,那么当 F 趋向正无穷时,损失会很小;类似地,如果 y=-1,那么当 F 趋向负无穷时,损失会很小。

损失函数可视化

为了更直观地理解,我们可以将指数损失与其他常见损失函数进行比较,例如 L2 损失(平方误差)、L1 损失(绝对误差)和合页损失(用于 SVM)。

y=1 时:

  • L2 损失:形状为抛物线,最小值在 F=1
  • L1 损失:形状为绝对值函数,最小值在 F=1
  • 合页损失:在 F>=1 时为 0,在 F<1 时线性增长。
  • 指数损失:随着 F 远离错误类别(-1)而减小,其最小值实际上在 F 趋向正无穷时。

指数损失的有趣之处在于,我们可以证明 AdaBoost 就是使用指数损失的前向分步加法建模。

从加法模型推导提升算法

这是本视频中唯一稍具数学性的部分。如果你没有完全跟上所有推导,这没关系,但请记住高层次的观点:从前面的表达式,我们可以推导出 AdaBoost 的更新规则,这意味着 AdaBoost 是前向分步加法建模的一个特例。

通过分析指数损失下的前向分步优化问题,我们可以推导出与 AdaBoost 完全相同的权重更新规则和 α 的计算公式。这证明了我们的主张。

这个观察很有趣,它表明 AdaBoost 是拟合加法模型方法的一个实例。但这也意味着,如果我们采用前向分步方法,并将指数损失换成另一种损失,这可以被视为推导新提升算法的一种方式。

L2Boost(用于回归)

特别地,AdaBoost 之前是为分类定义的。我们可能也想进行回归任务的提升。我们可以通过采用前向分步方法并将损失函数换为平方损失,来推导出一个有原则的回归提升算法。

结果将是另一种称为 L2Boost 的算法。在这个算法中,我们可以证明,在每个时间步 t,我们解决以下最小化问题:新添加的模型 G_t 最小化 L2 误差,其试图拟合的目标变量是残差 R,即 y 与先前模型预测值之间的差。因此,这本质上是在拟合先前模型预测错误的部分,这也是一种提升形式。

LogitBoost(更稳健的分类)

我们可以考虑的另一种损失是逻辑损失(或对数损失)。其公式类似于指数损失的对数形式:

L(y, F) = log(1 + exp(-2 * y * F))

这个损失很有用,因为它解决了 AdaBoost 存在的一些问题。让我们比较一下指数损失和合页损失或 L2 损失。指数损失在远离良好预测的方向上增长得非常快,会给被严重误分类的点分配非常大的惩罚。优化指数损失的算法会非常努力地最小化最坏情况的预测。

虽然最小化最坏情况预测在某些情况下是好的,但在许多实践中,我们存在异常值。这些点可能由噪声引入,或者是数据收集过程的产物。通常最好的解决方案是忽略它们。指数损失会极力去拟合这些本应被忽略的异常点,这可能导致性能不佳。

实际上,在应用机器学习时,通常更倾向于使用像合页损失那样线性增长、对异常值惩罚不那么严重的损失函数,这在实践中往往能带来更好的性能。

逻辑损失是解决此问题的一种方法。因为这里我们取了对数,当预测趋向错误时,这个函数的增长速度不如指数函数快,因此不会过度强调拟合异常值。在实践中,这可以产生更好的提升算法。

如果我们推导逻辑损失下的前向分步加法建模所优化的损失,它将看起来不同。结果将是一个不同于 AdaBoost 的算法,称为 LogitBoost。我们也可以推导出一种定义权重的方法。这也可以被视为在每一步对数据进行加权优化,但权重公式会不同,α 的公式也会不同,因此这将是一种称为 LogitBoost 的不同类型的提升算法。

总结与局限

本节课中,我们一起学习了加法模型及其训练方法——前向分步加法建模。我们已经看到了几种提升算法。我们目前看到的这些不同类型的提升算法,它们改进了我们最初称为 AdaBoost 的基本算法。它们很有用,因为它们可以优化各种目标函数,并且在实践中,这些目标函数可以为我们带来额外的好处,例如对异常值更鲁棒或扩展到回归任务(而 AdaBoost 仅用于分类)。

当然,这些算法也有一些局限性:

  • 计算时间可能是个问题,因为我们在每一步都需要重复解决优化问题。
  • 每种损失函数可能需要专门的推导才能得到像 AdaBoost 或 L2Boost 那样的更新方程。
  • 这种优化可能具有过于贪婪的特性,我们将在后续深入探讨这个问题及其解决方案。

总而言之,我们已经看到了具有加法形式的模型,它们可以使用前向分步方法进行拟合,这种方法可以重现 AdaBoost,也可以引发出新的算法。

13.3:梯度提升 🚀

在本节课中,我们将要学习一种非常强大的集成学习方法——梯度提升。梯度提升是目前性能最先进的提升算法之一,在Kaggle等机器学习竞赛中取得了大量优异成绩。我们将从基本概念出发,逐步理解其工作原理和实现方式。

提升算法回顾

上一节我们介绍了提升算法的基本思想,本节中我们来看看梯度提升的具体实现。

提升是一种通过组合多个较弱的“基学习器”来构建强大模型的技术。其核心思想是顺序地训练模型,每个新模型都专注于纠正前序模型所犯的错误。

我们可以将提升过程视为拟合一个加性模型。该模型的形式如下:

\[F_T(x) = \sum_{t=1}^{T} \alpha_t g_t(x; \phi_t) \]

其中:

  • F_T(x) 是最终的集成模型。
  • g_t 是第 t 个基学习器(例如决策树)。
  • \alpha_t 是第 t 个模型的权重。
  • \phi_t 是第 t 个模型的参数。

整个模型的参数是所有 \alpha_t\phi_t 的集合。这个模型是“加性”的,但由于每个 g_t 对其参数 \phi_t 可能是高度非线性的,因此整个模型并不是线性的。

拟合这类加性模型的通用方法是前向分步加法建模。其过程如下:

  1. 初始化模型,例如设 F_0(x) = 0
  2. 对于第 t 步,固定前 t-1 步已学到的所有模型参数。
  3. 仅针对当前步骤的残差或损失,优化求解第 t 个基学习器 g_t 及其权重 \alpha_t

这种方法将复杂的整体优化问题分解为一系列可处理的子问题。

我们之前看到,通过选择不同的损失函数,可以推导出不同的提升算法:

  • 使用指数损失,得到 AdaBoost。
  • 使用 L2 损失(均方误差),得到用于回归的 L2Boost。

然而,前向分步方法也存在一些局限性:

  • 对于每种新的损失函数,都需要手动推导权重 \alpha_t 的更新公式,这个过程繁琐且并非总是可行。
  • 如果在每一步都精确地求解最优的基学习器(即“完全拟合”),算法可能会过于贪婪,导致在某个步骤上过拟合,特别是当基学习器 g_t 本身非常灵活时。

因此,我们希望找到一种方法,能够通用地处理各种损失函数,易于训练,并能更有效地控制过拟合。这就是梯度提升算法。

从函数空间优化到梯度提升 🧠

为了理解梯度提升,我们首先需要了解函数空间优化的概念。这是一种思想实验,帮助我们建立直觉。

在传统的参数优化中,我们优化的是模型(如线性回归的权重)的参数。而在函数空间优化中,我们设想直接在一个由所有可能函数 F: X -> Y 组成的空间中进行优化。

一个关键的观察是:我们只有 n 个训练数据点。因此,尽管函数空间是无限的,但对于拟合训练集这个目的而言,我们只关心函数在这 n 个点上的值。所有在这些点上输出相同值的函数,在训练集上是等价的。

因此,优化整个函数空间等价于优化一个 n 维向量 \mathbf{F} = [F(x_1), F(x_2), ..., F(x_n)]。我们的优化目标变为:

\[\min_{\mathbf{F} \in \mathbb{R}^n} J(\mathbf{F}) = \sum_{i=1}^{n} L(y_i, F_i) \]

其中 F_i = F(x_i)L 是损失函数。

如果我们要用梯度下降法优化这个目标,就需要计算损失 J 相对于向量 \mathbf{F} 的梯度。这个梯度的第 i 个分量是:

\[[\nabla J(\mathbf{F})]_i = \frac{\partial L(y_i, F_i)}{\partial F_i} \]

这定义了一个有效的梯度。那么,梯度下降更新步骤为:

\[\mathbf{F} \leftarrow \mathbf{F} - \alpha \nabla J(\mathbf{F}) \]

其中 \alpha 是学习率。

但请注意,这只是一个思想实验。直接这样优化有两个主要问题:

  1. 过于简单:这是一个无约束优化问题。对于许多损失函数(如L2损失),我们可以直接令 F_i = y_i 来达到理论上的最小损失,但这毫无意义。
  2. 无法泛化:我们学习到的只是一组在训练集上完美的数值 F_i,但并没有得到一个可以在新数据点 x 上进行预测的函数 F(x)。这样的“模型”在测试集上会完全失败。

我们真正想要的是:找到一个函数 F(x),它不仅在训练集上损失小,更重要的是,对于从数据分布中采样的任何新数据点,它都能做出准确预测。

那么,如何实现呢?答案是:通过一个模型来学习这个梯度

梯度提升算法详解 ⚙️

梯度提升的核心思想是:在函数空间中进行梯度下降,但每一步的“梯度”不再是一个简单的数值向量,而是通过一个监督学习模型来近似。

1. 近似函数梯度

假设在当前步骤,我们有一个函数估计 F(x)。我们想要计算其函数梯度。对于任意输入 x,函数梯度定义为:

\[g(x) = \left[ \frac{\partial L(y, s)}{\partial s} \right]_{s=F(x)} \]

这是一个从 x 到实数的函数。在训练集上,我们可以精确计算出每个点 x_i 对应的梯度值 g(x_i)

现在,我们面临一个标准的监督学习问题:

  • 输入:特征 x_i
  • 输出/目标:函数梯度值 g(x_i)
  • 目标:学习一个模型 G(x),使得 G(x_i) ≈ g(x_i)

我们通常使用 L2 损失 来拟合这个模型,并且选择决策树作为基学习器 G 的模型族。决策树不需要复杂的特征预处理,能处理各种类型的数据,且通过控制树深可以方便地控制模型复杂度,防止过拟合。

这样,我们得到的 G(x) 就是对真实函数梯度 g(x) 的一个具有泛化能力的近似。即使对于未见过的 xG(x) 也能给出合理的梯度估计。

2. 梯度提升步骤

将上述近似梯度代入函数空间的梯度下降更新公式,就得到了梯度提升算法。

以下是梯度提升算法的步骤:

  1. 初始化:以一个简单的模型开始,例如常数值:F_0(x) = \arg\min_{\gamma} \sum_{i=1}^n L(y_i, \gamma)

  2. 对于 t = 1T(迭代次数)

    • a. 计算伪残差(负梯度):对于 i = 1, ..., n,计算

      \[r_{ti} = -\left[ \frac{\partial L(y_i, F(x_i))}{\partial F(x_i)} \right]_{F(x)=F_{t-1}(x)} \]

    • b. 拟合基学习器:使用决策树等基学习器,在数据集 {(x_i, r_{ti})}_{i=1}^n 上拟合一个模型 g_t(x),目标是让 g_t(x_i) 接近 r_{ti}
    • c. 计算最优权重:对于树 g_t(x) 的每个叶子节点 j,计算该节点内的最优权重 \gamma_{tj},以最小化损失:

      \[\gamma_{tj} = \arg\min_{\gamma} \sum_{x_i \in R_{tj}} L(y_i, F_{t-1}(x_i) + \gamma) \]

      其中 R_{tj} 是第 t 棵树第 j 个叶子节点对应的样本区域。
    • d. 更新模型

      \[F_t(x) = F_{t-1}(x) + \nu \cdot g_t(x) \]

      其中 \nu 是学习率(收缩因子),通常 0 < \nu < 1,用于控制每棵树的影响,防止过拟合。

  1. 输出最终模型F_T(x)

最终模型是一个加性模型:F(x) = F_0(x) + \nu \sum_{t=1}^T g_t(x)

3. 算法优势与特性

梯度提升算法具有以下显著优势:

  • 通用性:适用于任何可微分的损失函数,无需为每种损失手动推导特定更新规则。
    • 回归:L2损失(均方误差)、L1损失(绝对误差)、Huber损失(鲁棒回归)、分位数损失(预测区间)。
    • 分类:对数损失(逻辑回归)、指数损失(AdaBoost)。
    • 其他:泊松损失(计数数据)、负二项式损失等。
  • 强大的基学习器:通常使用决策树,因其能处理混合类型特征、无需特征缩放、并能自动进行特征选择。
  • 有效的正则化
    • 树复杂度:通过限制树的最大深度、叶子节点数等来控制。
    • 学习率 (\nu):较小的学习率需要更多的树,但通常能获得更好的泛化性能。
    • 子采样:每次迭代时随机使用一部分训练数据(类似随机梯度下降),增加随机性,防止过拟合。
    • 早停:在验证集性能不再提升时停止迭代。
  • 高性能:在结构化数据(表格数据)问题上,梯度提升树(如XGBoost, LightGBM, CatBoost)通常是性能最好的算法之一,在许多Kaggle竞赛中占据主导地位。

梯度提升示例与应用 🎯

让我们通过一个简单的回归示例来直观感受梯度提升的能力。

假设真实函数为 y = \sin(2\pi x) + \epsilon,其中 \epsilon 是噪声。我们从这个函数中采样一些点作为训练数据。

使用梯度提升回归(例如Scikit-learn中的 GradientBoostingRegressor),设置基学习器为最大深度为3的决策树,共训练250棵树。拟合结果如下图所示:

(想象一条平滑的正弦曲线,上面散布着一些噪声点。梯度提升模型拟合出的是一条紧密跟随数据点趋势的红色曲线。这条曲线由许多小决策树的输出叠加而成,因此在局部可能呈现不规则的阶梯状,但整体上能很好地捕捉数据的非线性模式。)

这个例子展示了梯度提升如何无需对数据分布做任何强假设(如多项式形式),仅通过组合简单的决策树,就能灵活地拟合复杂的非线性关系。

总结与展望 📚

本节课中我们一起学习了梯度提升算法。

我们首先回顾了提升算法的核心思想,并指出了前向分步加法建模的局限性。接着,我们通过“函数空间优化”的思想实验,引出了梯度提升的概念:在函数空间中进行梯度下降,并用一个监督学习模型来近似每一步的梯度

我们详细剖析了梯度提升算法的步骤:计算负梯度(伪残差)、用基学习器(通常是决策树)拟合这些残差、更新加性模型。该算法因其通用性(适用于任何可微损失)、强大的正则化手段(树参数、学习率、子采样、早停)和卓越的性能而闻名,是处理结构化数据问题的利器。

梯度提升树是当前最强大的提升算法形式,也是机器学习中性能最先进的算法之一。它在许多实际问题中表现优异,常常超越其他流行方法。其成功得益于对数据预处理要求低、可扩展性强以及能够优化多样化的目标函数。

当然,梯度提升也有其局限性。它主要适用于结构化数据(具有明确特征列的数据),对于图像、音频、文本等非结构化数据,决策树作为基学习器难以直接有效地处理,此时深度学习等方法通常更合适。

总而言之,对于特征定义清晰的表格数据问题,梯度提升树应该是你优先尝试的算法。它集成了简单与强大,是机器学习工具箱中不可或缺的组件。

14.1:人工神经元 🧠

在本节课中,我们将开始一个关于神经网络的两部分系列讲座。神经网络是一种非常重要的机器学习模型,其灵感来源于大脑的工作原理。近年来,神经网络在许多重要问题上非常有效,例如图像分类、机器翻译和语音识别。在这些以及许多其他问题上,基于神经网络的方法已显著推动了技术前沿。在本讲及后续课程中,我们将从基础开始,逐步构建,最终定义一些更近期的深度学习模型,这些模型取得了巨大成功,并在应用领域实现了突破。

从基础开始:人工神经元

上一节我们介绍了本系列讲座的概览。本节中,我们将从最基础的部分开始,描述一个神经元的数学模型,我们称之为人工神经元。这个简单的模型将成为本讲及后续课程中所有后续模型的基础构建块。

问题设定:二元分类

本节我们将聚焦于二元分类问题。在这个设定中,目标变量 y 是离散的,我们假设两个类别分别编码为 01。我们有一个将输入映射到目标的模型,记作 F。我们将尝试以一种受大脑工作原理启发的方式来构建这个模型。

回顾:逻辑回归

在我们早期的课程中,我们看到了一个非常流行且有效的解决二元分类问题的算法:逻辑回归。在逻辑回归中,我们假设模型 F 采用以下形式:它由一个线性部分与一个Sigmoid函数组合而成。Sigmoid函数的形式如下,它将线性部分的输出压缩到 01 之间的值,我们可以将其解释为类别为 1 的概率。

公式:
F(x) = σ(w^T x + b)
其中 σ(z) = 1 / (1 + e^{-z})

迈向新模型:神经科学的视角

现在,让我们开始构建一个受不同思想启发的二元分类模型。我们将从神经科学和大脑生物学的角度出发,并最终会看到它与逻辑回归的许多联系。

下图展示了构成大脑基本单元的神经元的主要部分。在高层次上,你可以将神经元视为具有几个关键元素:

  • 在神经元中央,有细胞核。
  • 这个核心部分通过称为树突的结构接收输入。
  • 树突是细胞的延伸部分,它们向外分支并连接到其他细胞,从邻近神经元接收输入信号。
  • 这些输入信号可以看作具有一定强度的电信号。
  • 细胞有多个树突,每个都接收一个输入信号。
  • 所有信号在细胞核处汇总。
  • 当来自这些信号的总输入足够强时,细胞核会做出反应,细胞内部会激发,这意味着它会沿着称为轴突的输出分支发送另一个电脉冲。
  • 当神经元通过其树突接收到足够强的信号时,它就会激发,并沿着轴突发送电信号,该信号随后会连接到其他神经元。

人工神经元:数学模型

人工神经网络的基本构建块将是这个生物神经元的数学化、理想化模型。

这个人工模型如下图所示。我们将用变量 x 表示进入神经元的输入,x 是一个输入向量。你可以通过与生物神经元类比来理解:将 x 的每个输入维度视为通过树突馈送到我们人工神经元模型的输入。

在这个理想化模型中,我们将通过权重 w 来调节输入。因此,在神经元内部,它为每个树突分配了不同的权重。然后,一旦我们从每个树突接收到信号,它们就在细胞体内求和。如果输入的总和足够大,它也会激发一个信号。激发信号的最小阈值由我们称为激活函数的函数指定,在这个例子中记作 f。你可以将其视为一个函数:当这些调制信号的总和很大时,该函数输出一个较大的值;当输入较低时,它输出一个较低的值。输出相当于生物神经元中轴突发送的信号。

总结一下,你可以将人工神经元视为三个操作的序列:

  1. 获取输入,并为它们分配权重。
  2. 对这些输入求和,得到进入神经元主体的总信号。
  3. 使用激活函数将这个总信号转换为通过轴突发送的输出。

人工神经元的输出形式由以下方程给出,其中 f 再次表示激活函数。

公式:
输出 = f( w^T x + b )

形式化定义

让我们用一些数学符号更正式地定义一下。我们将说,一个人工神经元是一个模型,它从输入集合(在这个特定例子中,我们可以取实数,范围在0和1之间,但实际上可以是不同的范围)映射到某个明确定义的范围。它执行以下操作:我们将权重与输入相乘,然后将结果传递给一个激活函数。在接下来的幻灯片中,我将使用符号 σ 来表示激活函数,这通常是一个非线性函数。由于是非线性的,人工神经元(更准确地说,是人工神经元的组合,特别是它们的组合)将能够建模比简单线性模型更复杂的非线性函数。

激活函数示例:感知机

在我之前给出的定义中,除了激活函数外,其他部分都已明确定义。这里我将给你一个激活函数的例子:它就是简单的阶跃函数。它非常简单:如果信号为正,则输出 1;如果为负,则输出 0

具有以下激活函数的人工神经元被称为感知机。感知机是一个非常古老的算法,由康奈尔大学的 Frank Rosenblatt 在 20 世纪 50 年代开发,被认为是第一个人工神经元模型。在 50 年代和 60 年代初期,这类模型曾引起极大兴趣。它们至今仍因历史原因而重要,尽管这种特定的激活函数如今在实践中已不常用。

我们可以将这个激活函数可视化,它看起来像下面的阶跃函数。本质上,如果进入细胞体的输入很小(从负无穷到阈值 0 的任何值),它不会激发任何信号;一旦我们超过阈值,神经元就会激发,其细胞发送一个输出信号,我们将其编码为 1

公式:
f(z) = 1 如果 z > 0,否则为 0

另一种人工神经元:逻辑回归模型

另一种我们之前见过的人工神经元是以下逻辑模型。这里我使用 Sigmoid 函数作为我的激活函数。如果我这样做,那么我实际上就得到了逻辑回归模型。有趣的是,我们可以将逻辑回归解释为另一种简单的人工神经元模型。

为了更好地理解这一点,让我们看看 Sigmoid 函数的形状。当输入较小(接近负无穷)时,这个 Sigmoid 函数输出一个接近 0 的低值;当我们接近阈值 0 时,它开始增加并输出一个较大的值;一旦输入超过大约 4,从 4 到正无穷,我们输出的值看起来非常接近 1。你可以将其视为我们之前看到的阶跃函数的平滑版本。

在很长一段时间里,这被用作神经网络的主要激活函数。它的平滑性是好的,因为它更容易使用梯度下降进行优化。在很长一段时间里,这个 Sigmoid 神经元(与逻辑回归是同一个模型)是最流行的人工神经元模型。

公式:
σ(z) = 1 / (1 + e^{-z})

现代激活函数

现在,这些并不是唯一存在的激活函数类型。在过去的 10 到 15 年里,研究人员意识到实际上其他激活函数更好,它们改进了阶跃函数和 Sigmoid 函数。事实上,使用这些激活函数的神经元仍然表现出我们期望的神经元模型行为(即输入低时不激发,输入高时激发),而且它们具有更好的数值特性,更容易使用梯度下降进行优化。

在现代神经网络中,我们经常使用以下修正线性单元激活函数。它使优化更容易,并且仍然以如下方式对神经元进行建模。

公式:
ReLU(z) = max(0, z)

这是双曲正切函数。它仍然具有我们想要的 S 形行为,只是现在输入被压缩在 -11 之间。

公式:
tanh(z) = (e^z - e^{-z}) / (e^z + e^{-z})

这个激活函数的形式是:当我们在阈值以下时,输出为 0;然后,在我们超过阈值后,信号的大小是线性的。因此,这也表现得像一个人工神经元,但这个函数实际上具有更好的数值特性,并且更容易优化。

实践演示:在鸢尾花数据集上

让我们快速演示一下这在实践数据上是如何工作的。我们将再次使用我们的鸢尾花数据集。我将导入并加载它,将 class 2 重命名为标签 1,这意味着我们将对这两个类别进行分类,所以这是一个二元分类问题。

我们可以非常容易地实现一个具有 Sigmoid 激活函数的人工神经元。定义神经元只需两行代码,我们也可以在一行中轻松定义其梯度。如果你想看这个梯度的推导,可以回顾我们关于逻辑回归的讲座。

现在,梯度下降又只需要几行代码。我们运行一定次数的迭代。

最终结果是一个分离数据的线性边界,这正是你从一个与逻辑回归具有相同函数形式的模型中所期望的。

所以,这将是我们构建更复杂算法的基础模块。尽管这个决策边界是线性的,我们将在接下来的几个视频中看到如何组合多个人工神经元来构建一个更复杂的函数,该函数将变成非线性的,并且能够从数据中学习许多有趣的函数。

总结

在本节课中,我们一起学习了人工神经元。这是一种可用于回归和分类的算法,我们可以将其与任何损失函数一起使用(尽管如果使用某些损失函数,我们可能需要更改激活函数)。原则上,我们可以使其适用于回归和分类。

该模型族是“线性变换后接非线性激活”。它通常仍然学习一个线性决策边界,但这是模型的类型。我们可以使用不同的目标函数(如 L2 损失或其他类型的损失)来优化它。对于人工神经元的最优权重,没有封闭形式的解,我们通常使用梯度下降。

人工神经元有许多著名的特例,其中两个是感知机逻辑回归

14.2:人工神经网络(第二部分) 🧠

在本节课中,我们将学习如何将单个神经元组合成更复杂的模型——人工神经网络。上一节我们介绍了单个神经元的基本模型,本节中我们来看看如何将这些神经元连接起来,形成多层网络结构。

从神经元到网络 🌐

在上一节中,我们定义了单个神经元的模型。该模型接收输入 x,通过权重 w 对输入的每个维度进行调制,然后求和,最后通过一个激活函数 σ 输出结果。激活函数通常在输入较大时输出较大值,输入较小时输出较小值,以此模拟生物神经元的行为。著名的例子包括感知机和逻辑回归。

现在,我们将探讨如何将神经元组合成神经网络。通常,这是通过构建神经网络层来实现的。

神经网络层 🧱

神经网络层是一个函数,它接收一个 d 维输入,并输出一个 p 维结果。它的工作原理是将 p 个神经元并行应用于输入 x。每个神经元都有自己的权重,并且它们通常共享相同的激活函数。该层的全部参数是所有神经元参数的集合。我们称 p 为层的大小,即神经元的数量。

我们可以将其可视化如下:输入是 x,每个神经元都有一个权重向量 w_i。输入通过箭头(对应权重)进入神经元,进行加权求和,然后通过激活函数 σ 产生输出。

为了便于表示和计算,我们通常将这些权重组合成一个矩阵 W。矩阵 W 的每一行都是一个权重向量 w_i^T 的转置。这样,整个层的操作可以用线性代数简洁地表示为:

h = σ(Wx)

其中,σ 被逐元素地应用于向量 Wx。这种表示方式不仅更简洁,而且利用线性代数库可以实现高效计算。

构建神经网络 🏗️

一个神经网络就是多个层的组合。假设我们有 L 层。我们称内部的层为隐藏层,最终的层为输出层。隐藏层之所以“隐藏”,是因为它们的输出不直接可见,而是作为下一层的输入。

如果我们预测的目标 y 是回归或分类任务,那么输出层通常是一维的。它可能不使用激活函数,或者根据预测目标的需要使用特定的激活函数。

用函数复合的符号表示,一个 L 层的神经网络可以定义为:

f(x) = f_L ∘ f_{L-1} ∘ ... ∘ f_1(x)

其中, 表示函数复合,即 f ∘ g(x) = f(g(x))

视觉上,我们可以将神经网络表示为:输入进入一个隐藏层,然后可能经过一系列这样的隐藏层,最终到达输出层。

一个简单的Python实现 💻

以下是一个简单的Python实现示例,展示了一个具有二维输入、一个包含3个神经元的隐藏层以及一个输出层的神经网络:

import numpy as np

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/b2ba8df9507e13fa8c4c4cdafe6eb19c_23.png)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/b2ba8df9507e13fa8c4c4cdafe6eb19c_24.png)

# 定义激活函数(Sigmoid)
def sigmoid(z):
    return 1 / (1 + np.exp(-z))

# 初始化权重矩阵
# 隐藏层:3个神经元,每个接收2维输入
W = np.random.randn(3, 2)
# 输出层:1个神经元,接收3维输入(来自隐藏层)
V = np.random.randn(1, 3)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/b2ba8df9507e13fa8c4c4cdafe6eb19c_26.png)

# 前向传播函数
def forward(x):
    # 隐藏层计算
    h = sigmoid(np.dot(W, x))
    # 输出层计算(此处假设为回归,不使用激活函数)
    y_hat = np.dot(V, h)
    return y_hat

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/b2ba8df9507e13fa8c4c4cdafe6eb19c_28.png)

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/b2ba8df9507e13fa8c4c4cdafe6eb19c_30.png)

# 示例输入
x = np.array([0.5, -1.2])
output = forward(x)
print(f"网络输出: {output}")

这段代码使用NumPy高效地执行线性代数运算,简单地实现了神经网络的前向传播过程。

神经网络的类型与层 🔄

我们之前定义的层,其中前一层的每个神经元都连接到后一层的每个神经元,被称为全连接层密集层

但在实践中,许多有用的层并不具有这种密集连接,或者会在连接中重用参数。以下是一些重要的层类型:

  • 卷积层:在下一讲中,我们将看到卷积层,它应用一种称为卷积的特殊操作。这种层在图像处理中尤其重要。
  • 循环神经网络层:这类层的输出可以反馈到更早的层,从而打破了有向无环图的结构,适用于处理序列数据。

总结与展望 📝

本节课我们一起学习了人工神经网络。我们定义了将单个神经元组合成层,再将多层组合成网络的方法。给出的例子使用了全连接层。神经网络是用于监督学习(回归和分类)的强大算法,通常使用梯度下降法进行优化。

即使本节描述的基本形式通常不是解决图像分类等复杂问题的最先进模型,神经网络仍然非常有效且灵活。理论上,足够大的神经网络可以近似任何函数。它们在处理音频、图像等非结构化输入数据时表现尤为出色,因为我们很难手动为这些数据定义特征。

然而,神经网络也有其局限性:需要大量数据、训练可能较慢且困难。训练时需要仔细选择学习率、参数初始值,并对输入数据进行规范化处理。当网络表现不佳时,调试也颇具挑战性。尽管如此,神经网络仍然是机器学习中非常重要且强大的模型。

在下一讲中,我们将探讨如何使用梯度下降来训练这个模型。

14.3:反向传播算法 🧠

在本节课中,我们将学习如何训练人工神经网络。核心方法是使用梯度下降法,而为了高效计算神经网络中所有参数的梯度,我们将使用一个名为反向传播的特殊算法。

上一节我们介绍了人工神经元和神经网络的基本结构,本节中我们来看看如何通过梯度下降来优化这些网络。

梯度下降与神经网络优化

通常,我们使用梯度下降来优化机器学习模型。即使对于单个神经元(等同于逻辑回归),我们也需要使用梯度下降。对于使用感知机或其他任何激活函数的模型,同样没有闭式解,因此也必须使用梯度下降进行优化。

梯度下降的结构如下:我们从参数的某个初始猜测开始,然后反复应用以下更新规则,沿着目标函数梯度的反方向移动参数,以快速最小化目标函数。

公式
θ_new = θ_old - η * ∇J(θ_old)

为了应用梯度下降,我们需要计算神经网络模型中每个参数相对于目标函数的梯度。反向传播算法正是用来高效计算这些梯度的。

反向传播的核心:链式法则 🔗

反向传播的核心思想源于微积分中的一个简单公式——链式法则

链式法则指出,对于两个可微函数 FG 的复合函数 F(G(x)),其梯度为:
∇[F(G(x))] = ∇F(G(x)) * ∇G(x)

或者用另一种记法:若 y = f(u)u = g(x),则:
dy/dx = (dy/du) * (du/dx)

神经网络正是由一系列函数复合而成的结构。因此,要计算整个网络的梯度,我们需要将链式法则应用于这一系列层。

反向传播算法详解

假设我们有一个包含 L 层的神经网络。我们想计算目标函数 J 相对于第 L 层某个参数 θ_L 的梯度。

根据链式法则,这个梯度可以分解为:
∂J/∂θ_L = (∂J/∂y_L) * (∂y_L/∂y_{L-1}) * ... * (∂y_{L+1}/∂y_L) * (∂y_L/∂θ_L)

这里,y_i 表示第 i 层的输出。

反向传播算法的关键洞察在于计算复用。当我们计算第 L 层参数的梯度时,会计算出许多中间梯度(例如 ∂y_L/∂y_{L-1})。如果我们接下来要计算更早一层(如第 L-1 层)参数 θ_{L-1} 的梯度,其公式会包含许多相同的中间项。

因此,反向传播算法在计算过程中会存储这些中间梯度,并在计算下游层的梯度时重复使用它们。这本质上是一种动态规划算法,它通过复用前序计算来高效实现链式法则。

简而言之:

  • 链式法则 给出了梯度计算的理论公式。
  • 反向传播 是实现该公式的算法,它通过动态规划避免重复计算。

一个简单的例子:单层梯度计算

让我们通过一个简单的例子来具体理解。假设有一个层,接收输入 x,产生输出 y,并带有参数 θ。该层是最终输出层,我们有一个损失函数 J,其目标值为 y_hat

计算梯度的步骤如下:

  1. 首先计算损失 J 对输出 y 的梯度:∂J/∂y
  2. 然后,利用链式法则计算损失 J 对参数 θ 的梯度:∂J/∂θ = (∂J/∂y) * (∂y/∂θ)
  3. 同时,我们可能还需要计算损失 J 对输入 x 的梯度(如果 x 是前一层输出):∂J/∂x = (∂J/∂y) * (∂y/∂x)

注意,∂J/∂y 这个值在步骤2和步骤3中都被用到了。反向传播算法会存储这个值并复用。

手动计算:一个两层神经网络示例 🧮

为了使概念更具体,我们手动为一个简单的两层神经网络实现反向传播。该网络有2个输入、3个隐藏单元和1个输出单元,所有层都使用Sigmoid激活函数。

网络结构

  • 输入:x = [5, 3]
  • 隐藏层权重 W 和输出层权重 V 为给定值。
  • 目标输出:y_hat = 1

前向传播
我们首先进行前向传播,计算每个神经元的输出和最终的损失值(这里使用交叉熵损失)。计算过程如下(具体数值计算已在前文图示中完成):

  1. 计算隐藏层三个神经元的输出 h1, h2, h3
  2. 计算最终输出 y
  3. 计算损失 J

反向传播(计算梯度)
现在我们开始反向传播,计算所有权重的梯度。

以下是计算步骤的分解:

首先,计算损失 J 对最终输出 y 的梯度 ∂J/∂y

接着,计算输出层权重 V 的梯度。对于每个权重 v_i,其梯度为:
∂J/∂v_i = (∂J/∂y) * (∂y/∂v_i)
其中 ∂y/∂v_i 可以根据Sigmoid函数和其输入计算得出。

然后,我们需要计算损失 J 对隐藏层输出 h 的梯度,例如 ∂J/∂h1。这需要用到链式法则:
∂J/∂h1 = (∂J/∂y) * (∂y/∂h1)
注意,∂J/∂y 是之前已经计算并存储的值。

最后,计算隐藏层权重 W 的梯度。对于权重 w_11,其梯度为:
∂J/∂w_11 = (∂J/∂h1) * (∂h1/∂w_11)
这里,∂J/∂h1 是上一步计算并存储的值,∂h1/∂w_11 可以根据神经元公式计算。

通过这种方式,我们可以从后向前,层层递推地计算出网络中所有权重的梯度。计算出的具体梯度值已在前文图示中标明(红色和紫色数字)。

代码实现概览 💻

在Python中,我们可以通过定义“层”类来实现反向传播。每个层类包含权重、激活函数,以及前向传播和反向传播的方法。

核心思想

  • forward 函数:接收输入,计算并返回该层的输出,同时保存中间结果(如加权和、激活值)以供反向传播使用。
  • backward 函数:接收从后面层传来的梯度 dout(即损失对该层输出的梯度),利用链式法则和保存的中间结果,计算并返回:
    1. 损失对该层权重的梯度。
    2. 损失对该层输入的梯度(将传递给前一层)。

通过将多个这样的层堆叠起来,就构成了一个神经网络。训练时,先进行前向传播计算损失,然后从最后一层开始反向调用各层的 backward 方法,逐层传播梯度并更新权重。

课程提供的示例代码使用给定的权重和输入,运行反向传播后计算出的梯度值与手动计算的结果一致。随后运行梯度下降,可以看到损失逐渐下降至零,网络预测值逼近目标值1。

总结

本节课中我们一起学习了反向传播算法。我们了解到:

  1. 训练神经网络需要使用梯度下降
  2. 反向传播是利用链式法则高效计算神经网络梯度的算法。
  3. 其核心是通过动态规划策略存储和复用中间梯度,避免重复计算。
  4. 我们通过一个两层网络示例手动演算了反向传播的过程,并概述了其代码实现的基本思路。

掌握反向传播使我们能够利用梯度下降来训练强大的人工神经网络模型。在接下来的课程中,我们将探讨更现代的深度学习网络架构。

15.1:什么是深度学习?🧠

在本节课中,我们将继续学习神经网络系列讲座。我们将探讨比之前课程中介绍的更现代的神经网络版本。这些更现代的神经网络属于一个非常激动人心的机器学习领域,即深度学习。深度学习将是本讲的重点。在第一部分视频中,我们将介绍什么是深度学习、它为何有趣,并给出高层次的学习动机,同时解释它与早期神经网络工作的对比。


回顾经典神经网络

上一讲我们回顾了,神经网络是由多层神经元组成的模型。每一层都是一组人工神经元。它接收输入 X,并应用一个变换。该变换包含 P 个神经元,每个神经元都有权重。这些权重用于调制输入,然后输入进入激活函数,激活函数产生信号,即该层的输出。在向量化形式中,我们可以这样表示:

公式: 输出 = 激活函数(权重 * 输入 + 偏置)

当我们把多个这样的层串联起来应用时,就得到了所谓的神经网络。因此,神经网络是如下函数的组合:

公式: f(x) = f_L(... f_2(f_1(x)))

我们可以将其可视化为一个图。最后一层是单个神经元,即输出层。中间这些蓝色层被称为神经网络的隐藏层


什么是深度学习?🚀

这些是经典的神经网络。近年来,它们在某种意义上被重新定义为一个新的领域、一套新的算法,称为深度学习

深度学习本质上仍然是神经网络的研究。但现代神经网络与早期神经网络的不同之处在于,它们往往更大,尤其是更深,即拥有更多层。在神经网络发展的早期,大多数研究关注的是层数较少的网络,可能通常只有一个隐藏层,有时两、三、四、五层。而作为深度学习一部分的现代神经网络,可以拥有数百甚至数千层,这使它们的能力显著增强。

深度学习的另一个重点是,将这些神经网络应用于非常大的数据集。早期的神经网络模型较小,训练也更困难,因此通常应用于较小的数据集。这在很大程度上也是因为当时只有较小的数据集可用。然而,在过去的15到20年里,更大的数据集变得可用,并且人们发现神经网络在这些大型数据集上表现非常好,这也是推动神经网络重新受到关注的部分原因。

当我们谈论深度学习时,通常指的是将神经网络应用于拥有数百万甚至更多输入样本的非常大的数据集。这些神经网络在大型数据集上,对所谓的非结构化数据效果最好。非结构化数据指的是原始信号,如图像、音频,有时是字符级别的文本。这些输入是非结构化的,因为很难为它们定义特征。例如,为一张图片是否包含猫或狗定义特征,不像为医疗记录定义特征(如患者是否为老年人或是否患有某些已知重要的疾病组合)那么容易。我们无法轻易地为图像、文本或音频等非结构化数据手工设计特征,至少非常困难。而神经网络和深度学习正是在这类非结构化输入的大型数据集上大放异彩。

最后,现代深度学习的另一个重要方面是其实现和运行方式。我们之所以能在大型数据集上训练神经网络,部分原因在于专用硬件的可用性,如图形处理单元以及更现代的芯片(你可能听说过张量处理单元)。此外,还有许多专门用于训练这类深度神经网络的特殊方法。


为什么深度模型如此有趣?💡

深度模型或拥有许多层的模型之所以有趣,原因之一是,一个深的神经网络能够以比浅层网络更紧凑的方式表示更复杂的模型。

换句话说,许多理论工作已经表明,即使只有一个隐藏层的神经网络也可以逼近任何函数。因此,这些神经网络是非常灵活的模型。但是,这个隐藏层的大小可能非常大,它会随着函数的复杂性而快速增长。相反,如果我们有一个更深的网络,我们可以使用相对较少、较小的隐藏层来表示相同复杂度的函数。因此,通过使神经网络变深,我们可以学习真正复杂的映射。

为了让你了解神经网络可以学习何种映射,这里有一个称为“图像描述”的应用示例。我们有一张图像,希望输出描述图像内容的英文文本。这是一个非常困难的问题,我们目前学到的任何经典算法都无法直接应用于此问题。我们无法将逻辑回归或朴素贝叶斯应用于此任务。但是,当我们应用带有深度神经网络的深度学习时,我们可以将图像输入一系列神经元,然后是另一系列称为循环神经网络的神经元。这些神经元的输出将类似于以下内容,这是对图像内容的惊人准确的描述。这展示了深度神经网络可以学习的映射的复杂性和丰富性。


深度神经网络如何工作?🔍

深度神经网络之所以有效,并且能够更紧凑地学习复杂映射,是因为它们能够构建数据的有用表示

当我们通过神经网络的多个层输入数据时,我们发现每一层倾向于学习越来越抽象的数据特征或表示,并且这些表示建立在之前层的表示之上。

让我给你一个具体的例子。这是一个具有两个隐藏层的神经网络的可视化(这实际上被认为是相对较浅的网络)。如果我们使这些神经网络足够深,我们会发现它将学习特别有用的特征。例如,如果我们检查较低层(可能是第一或第二层)神经元的权重,通常会看到类似这样的东西:它们是一些小的边缘。因此,每个神经元对其输入应用一个小边缘检测器,当它在图像中看到该边缘时就会激活。

然后,如果我们观察中层特征,我们会发现这些神经元倾向于在看到这些边缘的特定组合时激活,这些组合形成了有趣的特征。在这里,你看到一只眼睛、一只耳朵、一个鼻子。这意味着,当该神经元看到来自较低层的输入组合,而这些组合对应于构成耳朵、鼻子或某种熟悉形状的边缘结构时,它就会激活。

在神经元的最高层,我们可能会看到一般类型的人脸。这意味着,当输入具有与某些高层人脸类型相似特征的人脸时,这些神经元就会激活。

换句话说,当我们从数据中训练深度神经网络时,它学习的权重编码了数据的有用表示。神经网络纯粹从数据中学习这些表示,它事先并不知道什么是脸或什么是鼻子。这些神经元以越来越抽象的层次组织起来,这是深度学习算法的一个特征。深度学习模型的许多成功都归功于其学习这种分层表示的能力。


深度学习的优势与驱动力

我之前提到,深度神经网络可以实现非常好的性能。部分原因在于它们能够非常高效地利用数据。

考虑算法性能作为其接收数据量的函数。如果我们只有少量数据,性能会提高。但对于传统学习算法,一旦达到某个数据点数量(可能是1万或10万),模型性能就会饱和,更多的数据不再有用。然而,对于神经网络,性能往往会随着数据量的增加而持续提高。如果我们有一个非常大的神经网络,其性能会随着数据量的增加而持续提升。因此,在数据量非常大的情况下,大型神经网络往往显著优于经典的机器学习模型。

一种理解方式是,神经网络是一个非常复杂的模型,拥有大量参数,表达能力非常强。通过给它大量数据,它可以利用其强大的表示能力,非常准确地从数据中提取大量信号,从而实现高精度。

我们之所以能够首先使用这类数据集,是因为神经网络可以从特定的专用硬件中受益。换句话说,有许多专门的工具,包括软件框架和硬件(如图形处理单元),使得神经网络能够扩展到真正的大型数据集。并非所有算法都能从这些专用计算单元中受益,但由于神经网络基于涉及线性代数的一系列操作,它们往往能从中大大受益。这也是我们能够将它们扩展到非常大的数据集的部分原因,也是其有效性的一个重要原因。

最后,关于深度学习的另一个有趣之处是,它仍然与神经科学有很多联系。简单的神经网络最初就是受大脑工作原理启发而定义的。如果我们进一步研究大脑的功能,会发现大脑内部的许多连接由许多层神经元组成。因此,我们大脑内部的神经网络是深的,这有时被认为是构建深度人工神经网络的动机之一。

事实上,如果我们分析大脑的某些部分,例如视觉皮层,会发现许多行为与我们在深度神经网络模型中看到的非常相似。在人类大脑的视觉系统中,通常有一系列区域,如V1、V2,以及下游的一些其他区域。如果我们研究具有这类视觉系统的动物大脑,通常会发现这些神经元序列(或区域)对应于以顺序模式相互连接的神经元层,每个区域对应于越来越抽象的数据表示。例如,在处理视觉输入的大脑最前端部分,有类似于边缘检测器的结构,这类似于我之前展示的人工神经网络图像中的边缘。随着我们沿着视觉系统向前移动,这些神经元倾向于被越来越复杂的模式激活。从这个意义上说,生物神经网络往往也是深的,并且也倾向于编码这种分层表示。因此,深度神经网络的行为与生物神经网络惊人地相似。


深度学习的成功应用与总结

由于我提到的所有这些特性——能够使用大量数据、能够高效训练、能够学习表示——深度学习在过去十年左右取得了巨大成功。事实上,这是一次真正的突破,它推动了许多技术的最先进水平,如语音识别或机器翻译。今天的机器翻译系统(例如在谷歌翻译中)的准确性显著提高。如果你进行对比,你会对机器翻译准确性的提升感到震惊,这主要归功于过去五年左右的创新。

此外,它还实现了许多以前不可能的新成果。这里展示一个有趣的例子:这些图像实际上是由神经网络生成的。我们可以将神经网络用于监督学习,也可以用它来生成图像。如果你以前没见过,我鼓励你访问相关网站,它会使用神经网络实时生成并不真实存在的人脸图片。这都是由一个在大型人脸数据集上训练的神经网络生成的。从这个意义上说,神经网络解锁了新的功能,每年仍然让研究人员感到惊讶。

因此,正如我所说,神经网络是非常强大的模型,它们达到了新的技术水平,并且可以与其他算法结合。例如,我们可以推导出这些算法的变体,其中某些组件是神经网络,其他组件是概率性的。我们可以将所有这些混合在一起,推导出作为经典算法更强大泛化的新算法。

我认为深度神经网络的主要缺点与我之前谈论常规神经网络时提到的相同:由于数值不稳定性,它们通常训练缓慢且困难,并且需要大量训练数据。


本节课总结:
在本节课中,我们一起学习了深度学习的核心概念。我们回顾了经典神经网络的基本结构,并将其与更现代、更深的神经网络进行了对比。我们探讨了深度学习为何强大,包括其通过深度结构紧凑表示复杂函数的能力、从数据中自动学习分层特征表示的能力,以及利用大规模数据和专用硬件实现卓越性能的潜力。我们还看到了深度学习在图像描述等复杂任务上的成功应用,并了解了其与生物神经系统的联系。最后,我们总结了深度学习的优势与当前面临的挑战。

15.2:卷积与池化 🧠

在本节课中,我们将学习卷积神经网络的两个核心构建模块:卷积池化操作。我们将从基本定义出发,通过直观的例子理解它们的工作原理,并探讨它们在处理图像等数据时的强大能力。


卷积操作 🔍

上一节我们介绍了深度学习的背景。本节中,我们来看看卷积操作的具体定义。

卷积是在两个向量 F(滤波器)和 G(信号)之间进行的一种运算。假设 F 的维度为 N,G 的维度为 M(通常 M > N)。在深度学习中,卷积运算(通常指互相关)定义如下:

公式(F * G)[p] = Σ_{t=0}^{N-1} F[t] * G[p + t]

其中,我们约定当索引超出 G 的范围时,其值为 0。这个运算可以理解为滤波器 F 在信号 G 上“滑动”并进行点积。结果值的大小反映了滤波器所代表的模式在信号该位置的匹配程度。

为了更直观地理解,请看以下图示示例:

图中绿色方块代表一个三维滤波器 [1, 0, -1],灰色方块代表信号。黄色方块展示了在每个位置进行卷积计算的结果。例如,在位置 p=0 时,计算为 1*0 + 0*0 + (-1)*2 = -2

技术说明:在数学上,严格定义的卷积运算会先将滤波器翻转。但在深度学习的语境和大多数库的实现中,“卷积”通常指代上述的互相关运算。两者在实践效果上是等价的。

以下是一个简单的Python实现示例:

def convolve_1d(signal, filter):
    result_length = len(signal) - len(filter) + 1
    result = []
    for i in range(result_length):
        result.append(np.dot(filter, signal[i:i+len(filter)]))
    return np.array(result)

卷积的直观应用 🎯

理解了基本定义后,我们通过两个具体例子来看看卷积能做什么。

边缘检测

我们可以设计一个特定的滤波器来检测信号中的突变(边缘)。

代码

filter = np.array([-1, 1]) # 边缘检测滤波器
signal = np.ones(30)
signal[10:15] = 0 # 在信号中插入两段零值
signal[20:25] = 0
result = convolve_1d(signal, filter)

可视化结果如下:

上图中,蓝色是滤波器,橙色是原始信号,绿色是卷积结果。可以看到,卷积结果仅在信号值发生从1到0或从0到1跳变的位置(即“边缘”)出现非零值(1或-1)。这证明了该滤波器成功定位了信号中的边缘。

信号平滑

卷积也可以用于平滑信号,例如通过一个类似钟形曲线的滤波器计算加权移动平均。

代码

filter = np.array([0.05, 0.1, 0.2, 0.3, 0.2, 0.1, 0.05]) # 平滑滤波器(示例值)
smoothed_signal = convolve_1d(signal, filter)

结果如下图所示:

经过卷积后,原始信号(橙色)中的尖锐过渡被平滑(绿色曲线),但整体形状得以保留。

小结:通过设计不同的滤波器,卷积操作可以提取信号特征(如边缘)或改变信号特性(如平滑)。在机器学习中,这些滤波器的参数通常不是手动设计,而是通过梯度下降从数据中学习得到,使其能自动检测对任务有用的模式。


扩展到二维与卷积层 🖼️

卷积的概念可以自然地扩展到二维,这对于图像处理至关重要。

在二维卷积中,信号 G 是一个矩阵(如图像),滤波器 F 是一个小矩阵(如3x3)。运算过程是滤波器在图像的长和宽两个方向上滑动,在每个位置计算对应区域的点积。

下图展示了二维卷积的过程:

蓝色网格是输入图像,灰色方块是滤波器,绿色网格是输出的激活图。激活图上每个点的值,代表了滤波器所寻找的模式在输入图像对应位置的匹配强度。

基于此,我们可以定义神经网络中的卷积层。与全连接层使用矩阵乘法不同,卷积层使用卷积运算。一个卷积层包含多个不同的滤波器,每个滤波器会产生一张独立的激活图。

考虑一个32x32像素的彩色图像,它可以表示为一个32(高)x 32(宽)x 3(颜色通道)的三维数组(张量)。如果我们使用一个5x5x3的滤波器对其进行卷积,将得到一张28x28的二维激活图(因为5x5的滤波器在32x32的图像上,每边有4个像素无法完全覆盖,这被称为“边缘效应”)。

一个卷积层通常有多个滤波器。例如,一个有5个滤波器的层,会对同一输入图像进行5次不同的卷积操作,产生5张激活图。这些激活图可以堆叠起来,形成一个28(高)x 28(宽)x 5(通道)的新三维张量,作为下一层的输入。


二维卷积实例:图像边缘检测 📸

让我们将之前一维的边缘检测思想应用到二维图像上。

我们定义两个3x3的滤波器,分别用于检测垂直边缘和水平边缘。

代码

import numpy as np
from scipy import signal

# 定义边缘检测滤波器
vertical_filter = np.array([[1, 0, -1],
                            [1, 0, -1],
                            [1, 0, -1]])

horizontal_filter = np.array([[1, 1, 1],
                              [0, 0, 0],
                              [-1, -1, -1]])

# 假设 `image` 是一个灰度图像矩阵
vertical_edges = signal.correlate2d(image, vertical_filter, mode='valid')
horizontal_edges = signal.correlate2d(image, horizontal_filter, mode='valid')

应用这些滤波器到一张建筑图像上的效果如下:

左图是垂直边缘滤波器的输出,它高亮了图像中的垂直线条(如楼梯)。右图是水平边缘滤波器的输出,它高亮了水平线条。图像中平坦的区域(如天空)在激活图中显示为暗色(值接近0)。

卷积的适用场景:当我们的任务具有空间不变性时,卷积特别有用。例如,在图像分类中,我们关心图像中是否包含“猫耳朵”这种纹理,而不在乎它具体出现在图像的哪个位置。卷积滤波器通过在整张图像上滑动来检测特征,正好满足了这一需求。


池化操作 ⬇️

在卷积层之后,我们常常会使用池化操作。池化层的作用是降低数据的空间维度(即缩小图像尺寸),它本身没有可学习的参数。

最常见的池化方式是最大池化。它将输入图像划分成不重叠的区域(例如2x2的方块),然后输出每个区域内的最大值。

下图展示了最大池化的过程:

一个4x4的输入被划分为四个2x2的区域。最大池化后,每个区域取其最大值,得到一个2x2的输出。平均池化是另一种选择,它取每个区域的平均值。

池化的意义:池化提供了对平移的小幅度不变性。它告诉网络“特征是否在这个大致区域出现”,而不关心其精确位置。这既减少了后续层需要处理的数据量,降低了计算复杂度,也有助于防止过拟合,并能使模型关注更宏观的特征。


总结 📝

本节课中我们一起学习了卷积神经网络的两个基础操作:

  1. 卷积:一种滑动点积运算,使用滤波器从输入数据(如一维信号或二维图像)中提取局部特征。滤波器参数可以通过学习得到,使其能自动识别对任务有用的模式。
  2. 池化:一种下采样操作(如最大池化),用于减少数据的空间尺寸,增强模型对特征位置微小变化的鲁棒性,并降低计算负担。

卷积和池化是构建卷积神经网络的核心模块。在接下来的课程中,我们将看到如何将这些模块组合起来,形成强大的深度学习模型,以处理图像、语音等具有空间或时间结构的数据。

15.3:卷积神经网络(第三部分) 🧠

在本节课中,我们将学习一种重要的深度学习模型——卷积神经网络。我们将结合上一讲中介绍的两个核心操作:卷积和池化,来构建和理解这种网络。

卷积与池化回顾

上一节我们介绍了卷积和池化操作。本节中,我们来看看如何将它们组合起来构建卷积神经网络。

卷积是一种对信号(如图像)进行的操作。我们使用一个滤波器在信号上滑动,并在每个位置计算点积,从而生成一个激活图。这个激活图能反映出滤波器所编码的特定模式在信号中的位置和强度。

池化操作则通常作用于卷积的输出上。它将输入(例如一个2x2的网格)划分为多个区域,并仅保留每个区域中的最大值。这样做的好处是,只要某个有用特征出现在该区域(如上象限),无论它具体在哪个像素上,信息都被保留了下来。池化降低了数据的维度,保留了关键信息,并使后续的数据分析更加容易。

卷积神经网络的结构 🏗️

卷积神经网络本质上也是一系列层的组合,但这些层现在可以按模块进行组织。每个模块通常包含以下结构:

  1. 卷积层:应用多个滤波器对输入进行卷积运算,生成一组堆叠的激活图。
  2. 非线性激活层:例如Sigmoid或ReLU(修正线性单元),为网络引入非线性。
  3. 池化层:对激活图进行下采样,减少其空间尺寸。

通常在网络的最后,还会连接一个或多个全连接层(或称密集层),用于最终的分类或回归任务。

因此,卷积神经网络的定义很简单:它是由我们之前学过的操作(卷积、激活、池化)组合而成的。网络的参数主要存在于卷积层的滤波器中,网络会通过训练学习哪些滤波器是重要的。

这是一个基础的网络结构,即使对这个结构进行微小的修改,也能在实践中取得非常好的效果。

经典网络架构实例 📚

以下是两个历史上著名的卷积神经网络实例,它们展示了这种结构的强大能力。

LeNet-5 (1998)

这是一个用于手写数字识别的早期成功模型。其工作流程如下:

  • 输入:一张灰度数字图像。
  • 处理过程:图像经过多个卷积层和池化层(图中称为“子采样”)的交替处理。特征图的尺寸逐渐减小,而深度(滤波器数量)可能变化。
  • 输出:最终的特征被展平并输入到几个全连接层中,最后一个输出层为10个可能的数字类别生成概率。

这个模型在当时达到了最先进的性能,并被成功应用于邮件分拣等实际任务中。

AlexNet (2012)

这是一个在ImageNet大规模视觉识别挑战赛中取得突破性成果的网络。

  • 输入:一张224x224像素的三通道彩色图像。
  • 处理过程:网络使用了更大尺寸的卷积核(如11x11),并包含了更多的卷积层和池化层。特征图的尺寸在过程中不断减小,而深度(激活图数量)则显著增加。
  • 输出:最终的特征被送入全连接层,输出一个1000维的向量,对应1000个图像类别。

AlexNet在2012年将图像分类的错误率大幅降低,证明了卷积神经网络的巨大潜力,并推动了其在计算机视觉各个领域的广泛应用。

卷积神经网络的特征学习 🔍

在卷积神经网络出现之前,计算机视觉算法依赖于手工设计的特征(如SIFT、HOG)。卷积神经网络的优势在于,它能够以纯数据驱动的方式,自动从数据中学习到层次化的特征。

为了理解这一点,我们可以可视化网络不同层学到的内容:

  • 底层:神经元学习检测简单的几何形状,如各种方向的边缘斑点或颜色过渡。
  • 中层:神经元开始对更复杂的图案产生响应,例如曲线圆形初级纹理,这些看起来像是物体的局部组件(如车轮)。
  • 高层:神经元变得高度专业化,能够响应非常具体和复杂的模式,例如狗的鼻口部眼睛,甚至是人的轮廓

这意味着卷积神经网络能够自动构建一个从简单到复杂的特征层次结构,这种强大的表示能力是其在许多真实数据集上取得高精度的关键。

卷积神经网络的特性与扩展 💡

卷积神经网络不仅本身是强大的模型,还具有一些有用的特性:

  • 迁移学习:我们可以在一个大型数据集(如ImageNet)上预训练一个卷积神经网络,然后将其中的部分层(尤其是高层特征提取器)迁移到另一个较小的、不同的数据集任务上。即使新数据集很小,也能获得高性能。
  • 模块化组件:卷积神经网络可以作为更复杂算法的构建模块。例如,在第一讲中提到的图像描述生成算法,就使用卷积神经网络作为初始的图像解析器。

总结 📝

本节课中,我们一起学习了卷积神经网络。我们回顾了其核心组件——卷积和池化操作,并了解了如何将它们组织成有效的网络结构。通过分析LeNet-5和AlexNet这两个经典实例,我们看到了卷积神经网络在实际应用中的演变和强大性能。更重要的是,我们探讨了网络如何自动学习从简单边缘到复杂物体的层次化特征,这是其成功的关键。最后,我们还了解了卷积神经网络的迁移学习能力和其作为基础模块的扩展性。正是这些特点,使得卷积神经网络成为计算机视觉领域不可或缺的强大工具。

16.1:无监督学习简介 🧠

在本节课中,我们将开始学习一个全新的主题——无监督学习。我们将了解它与之前学过的监督学习有何不同,并通过具体例子和算法来探索其核心概念与应用。

概述

无监督学习是一种与监督学习截然不同的机器学习类型。在监督学习中,我们使用带有标签的数据进行训练,而无监督学习则只使用未标记的数据。算法的目标是自行发现数据中隐藏的有趣结构,例如聚类、异常点或有用的信号。

无监督学习简介

上一节我们概述了无监督学习的基本概念。本节中,我们将通过一个具体的数据集和算法来深入了解。

数据集示例:鸢尾花数据集

我们使用熟悉的鸢尾花数据集作为示例。该数据集包含150朵花的四个测量值(单位:厘米),并对应三个不同的物种。为了进行无监督学习,我们将暂时忽略这些标签。

在Python中,我们可以使用scikit-learn加载并可视化数据。例如,我们选择花萼长度和花萼宽度这两个特征,在二维平面上绘制所有数据点。由于没有标签,所有点都被视为同等,这正是一个典型的无监督学习数据集。

算法示例:K均值聚类

接下来,我们介绍一个非常重要且流行的无监督学习算法——K均值聚类。

该算法被称为K均值,因为它试图在数据中找到K个聚类,其中K是我们需要预先设定的值。算法通过搜索并返回每个聚类的质心(即该聚类中所有点的中心或均值点)来识别这些聚类。

以下是使用scikit-learn运行K均值算法的基本代码:

from sklearn.cluster import KMeans
# 实例化模型,设定寻找3个聚类
kmeans = KMeans(n_clusters=3)
# 仅使用特征数据(无标签)进行拟合
kmeans.fit(X)
# 获取找到的聚类中心
centroids = kmeans.cluster_centers_

当我们对鸢尾花数据的前两个特征运行K均值(设K=3)后,算法找到了三个质心。将这些质心(在图中用红色菱形表示)绘制在数据散点图上,可以发现它们大致落在了三个不同的区域。

有趣的是,如果我们事后将真实的物种标签重新标注到数据点上,会发现K均值在完全不知道标签的情况下,成功地将三个物种大致区分开来。这展示了无监督学习自动发现数据内在结构的能力。

无监督学习的应用

上一节我们通过K均值看到了无监督学习如何发现聚类。实际上,无监督学习的应用远不止于此。

以下是几个关键的应用领域:

  • 可视化与降维:当数据维度很高时(例如784维的手写数字图像),我们可以使用无监督学习方法将其投影到二维或三维空间,以便于观察和理解数据的内在结构。
  • 异常检测:在训练了聚类模型后,如果一个新数据点不属于任何已知聚类,它可能是一个异常点。这在工业预测性维护或欺诈检测中非常有用。
  • 信号去噪:例如,可以从混合了白噪声的录音中,提取出清晰的人声信号。
  • 发现隐藏模式:在基因序列分析中,无监督学习能够仅根据基因数据,重建出个体的地理 ancestry 分布图,其结构甚至与欧洲地图惊人相似。
  • 生成模型:现代深度学习中的生成模型(如GANs、VAEs)是无监督学习的重要分支。它们可以学习数据分布,并生成新的数据样本,例如进行“微笑属性”的人脸图像算术运算。

总结

本节课中,我们一起学习了无监督学习的基础知识。我们了解到它与监督学习的核心区别在于不使用标签数据。通过鸢尾花数据集和K均值算法的实例,我们看到了无监督学习如何自动发现数据中的聚类结构。最后,我们探讨了无监督学习在可视化、异常检测、信号处理和生成模型等多个领域的强大应用。在接下来的课程中,我们将更深入地学习降维、聚类、密度估计等具体的无监督学习方法。

16.2:无监督学习的语言(第二部分) 🧠

在本节课中,我们将更深入地探讨无监督学习的语言,并正式定义在后续几讲中会频繁使用的术语。我们将从无监督学习问题的基本构成开始,并将其与监督学习进行对比,最后通过一个具体示例(K均值算法)来巩固这些概念。


数据集 📊

在无监督学习中,数据集是输入的集合,其中每个输入是一个 d 维的属性或特征向量。这与监督学习类似,但没有标签 Y,只有输入 X。这符合我们之前的直觉:无监督学习可以被视为移除了数据标签的监督学习。

与监督学习类似,我们假设存在一个数据分布 P 来支配这个数据集。所有输入都从这个概率分布中独立同分布地采样。这意味着每个样本 x 都是独立的,并且来自同一个分布 P

独立同分布 意味着:

  • 独立:每个样本的抽取不依赖于其他样本。
  • 同分布:所有样本都来自同一个概率分布 P

例如,抛硬币的结果序列是独立同分布的。相反,年度人口普查数据则不是独立的,因为某一年的人口数量会受到前一年人口数量的影响。


无监督学习算法的构成 ⚙️

在高层结构上,无监督学习算法与监督学习算法相似,都包含模型类目标函数优化器。然而,模型类和目标函数的具体选择(以及相应的优化器)将无监督学习与监督学习区分开来。

模型与模型类

在非概率定义下,一个模型是一个映射 f,它接收一个输入 x,并返回关于该输入结构的某种有趣信息 s,其中 s 属于某个结构集合 S

例如,结构可以是一个聚类标签、一个低维表示,或一些潜在特征。模型通常由参数 θ 参数化。

模型类 是所有可能模型的集合。如果模型是参数化的,模型类可以表示为一组参数 θ 的集合 Θ

目标函数

给定一个模型类,我们需要衡量特定模型对数据的拟合程度。为此,我们定义一个目标函数 J。在参数化模型中,J 是参数空间上的函数,我们通常希望最小化它(因此它常被视为损失函数)。

优化器

优化器 是一种算法,用于在模型类中找到能最优拟合数据(即最小化目标函数)的模型或参数。


示例:K均值算法 🔍

上一节我们介绍了无监督学习的一般框架,本节中我们来看看一个具体示例:K均值聚类算法,并分析它如何融入上述框架。

K均值模型

K均值算法旨在发现数据中的 K 个隐藏聚类。每个聚类由其质心(即聚类中点的均值)表示。

在K均值中,模型 f 将每个数据点 x 映射到其所属的聚类。该模型完全由 K 个质心的位置参数化。因此,参数 θ 就是这组质心 {c₁, c₂, ..., cₖ}

K均值的目标函数

如何判断一组质心定义的聚类是“好”的?一个直观的想法是:属于一个聚类的点应该离该聚类的质心很近。因此,一个好的聚类应使所有点到其最近质心的距离之和最小。

这引出了K均值的目标函数:

J(θ) = Σᵢ ||xᵢ - c_{f(xᵢ)}||²

其中:

  • xᵢ 是第 i 个数据点。
  • c_{f(xᵢ)} 是点 xᵢ 被分配到的聚类的质心。
  • ||...||² 表示欧几里得距离的平方。

最小化这个目标函数,就能找到一组“好”的质心。

K均值的优化器

K均值使用一种迭代的交替优化算法来最小化上述目标函数。算法步骤如下:

  1. 初始化:随机选择 K 个点作为初始质心,或随机将点分配到聚类。
  2. 重复以下两步直到收敛
    • 分配步骤:固定当前质心,将每个数据点分配给离它最近的质心所在的聚类。即更新 f(x)
    • 更新步骤:固定当前聚类分配,将每个聚类的质心重新计算为该聚类中所有点的均值。即更新 cₖ

这个过程会不断减少目标函数的值,直到质心的移动非常小(收敛)。

下图展示了K均值在二维数据上的迭代过程。初始时,质心(用X和O表示)位置随机,点被随机着色。经过几次迭代后,质心移动到更能代表其所属点群中心的位置,点的颜色(聚类分配)也随之稳定下来。


总结 📝

本节课中我们一起学习了无监督学习的核心概念和术语。我们首先定义了无监督学习的数据集是无标签的输入集合,并假设数据是独立同分布的。接着,我们剖析了无监督学习算法的三个核心组件:模型类目标函数优化器

最后,我们通过K均值聚类算法这一具体示例,演示了这些组件如何协同工作:其模型是由质心定义的聚类映射,目标函数是点到最近质心的距离平方和,优化器则采用交替最小化的迭代过程。

理解这个通用框架,将帮助我们更好地学习和分析后续将遇到的各种无监督学习算法。

16.3:无监督学习的实践应用

在本节课中,我们将探讨在实际应用无监督学习算法时需要考虑的一些重要方面。我们已经定义了第一个无监督学习算法,现在来看看在实践中使用它时需要注意的关键事项。

泛化概念在无监督学习中的延伸

上一节我们介绍了无监督学习的基本算法,本节中我们来看看如何将“泛化”这一概念从监督学习延伸到无监督学习。

在监督学习中,泛化意味着模型在训练数据上学习后,对于来自同一分布的新数据(测试数据)也能表现良好。然而,在无监督学习中,由于没有标签,我们无法直接沿用基于训练集和测试集准确率的泛化定义。

尽管如此,无监督学习仍然存在一种有用的泛化概念。我们可以将数据分布视为由两部分组成:信号噪声。信号可能代表隐藏的聚类结构、人类语音或数据所在的低维空间;而噪声则是干扰信号的随机性,例如聚类内部的随机散布、语音中的白噪声或数据点偏离低维空间的微小扰动。

一种适用于监督和无监督学习的泛化思考方式是:一个好的模型应该学习信号 F,而丢弃噪声 E。这种直觉可以帮助我们更好地理解无监督学习何时有效,何时无效。

一个运行示例:带隐藏结构的数据

为了说明这些概念,我们将使用一个二维数据集作为本视频的运行示例。该数据由四个真实的聚类组成,但当我们移除标签后,数据看起来像是均匀分布的,而实际上其内部隐藏着结构。

以下是生成该数据的代码片段:

# 使用 make_blobs 生成包含四个聚类的二维数据
from sklearn.datasets import make_blobs
X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)

模型过于简单:欠拟合现象

在无监督学习中,如果我们尝试使用过于简单的模型来拟合数据,可能会观察到类似“欠拟合”的现象。

例如,让我们在这个数据集上运行 K-Means 算法,并要求它找出两个聚类(k=2)。算法会返回两个质心(用红色菱形表示)。虽然它确实找到了两组有用的点(例如,视觉上该区域有更多空白,表明这两组点似乎彼此不同),但这个结果仍然不能令人满意,因为它没有识别出数据中全部的结构信息。此时,目标函数(如惯性)的值约为 462。

增加模型复杂度:从拟合信号到拟合噪声

当然,为了缓解这个问题,我们可以增加聚类的数量。

当我们将聚类数增加到四个(k=4)时,结果恢复了我们已知的数据结构。同时,目标函数值从 462 改善到了 164。

我们可以继续增加聚类数。例如,设置 k=10k=20,目标函数值会变得更小。然而,尽管目标函数值很小,但超过某个点后,聚类结果就不再有用。算法开始将来自同一高斯分布的样本中,由于采样随机性形成的空白区域或稀疏点群识别为独立的聚类。此时,模型不再拟合真实的信号,而是在拟合数据中的噪声。

从这种意义上说,我们本质上是在过拟合数据。我们开始拟合噪声而非信号。虽然“过拟合”这个概念主要定义并用于监督学习,但在此我想指出,这是无监督学习的另一种失败模式:模型识别的“信号”现在对应的是噪声,而不是数据分布中真实的信号。

将例子推向极端,使用 k=50 时,算法会将少数几个点识别为一个独立的聚类,这显然是仅仅在拟合噪声,即使 K-Means 的目标函数值非常小,这也毫无用处。

评估聚类质量:启发式方法

与监督学习不同,在无监督学习中我们通常没有真实的标签来客观评估聚类的质量。不过,存在一些启发式方法。

一种流行的启发式方法是肘部法则。这是一种用于调整无监督学习超参数的通用方法。

该方法的思路是,绘制我们之前看到的目标函数值(例如惯性)随超参数 k(聚类数)变化的曲线。然后观察这条曲线,我们将曲线下降速率发生转折的点称为“肘部”。损失值会随着 k 增加而下降,但在某个点之后,下降速率会变慢,曲线趋于平缓。这个“肘部”点通常是对模型超参数的一个良好猜测。

以下是应用肘部法则的示例步骤:

  1. 对不同 k 值(例如从 1 到 10)运行 K-Means。
  2. 记录每个 k 对应的惯性值。
  3. 绘制惯性随 k 变化的曲线。
  4. 寻找曲线拐点(斜率明显变化处),该点对应的 k 值即为建议的聚类数。

在我们的示例中,当 k 大约为 4 时,曲线变得平缓,下降速率饱和。k=4 正是数据中真实的聚类数量,这个红点就被称为“肘部”。

诊断失败模式与解决方案

更一般地说,检测这些失败模式(我将其类比为过拟合和欠拟合)在实践中可能更通用的正确说法是:我们是在拟合噪声还是未拟合信号。我倾向于将其与监督学习中的过拟合/欠拟合进行类比。

这个问题在无监督学习中诊断起来更加困难,通常需要依赖我们的直觉或人工评估,以判断发现的结构是否有用。在某些应用中,我们可能可以访问到少量标签,或者可以手动标注一些数据点进行检查(例如,在处理数字图像时,可以手动标记一些数字以查看发现的结构是否有用)。也存在一些基于真实标签来衡量拟合准确性的技术。

此外,有一类本质上是概率性的模型(我们很快会讨论无监督学习中的概率方法),在那里我们可以通过查看数据的似然来获得类似于监督学习中过拟合/欠拟合的定义。我们将为无监督学习定义与监督学习相同的优化对数似然的理念,并且这个似然目标函数实际上可以用来评估模型在训练集和留存数据上的拟合质量。这是理解无监督学习这两种失败模式的第三种方法,我们将在后续课程中探讨。

目前,对于像 K-Means 这样的算法,评估通常需要:

  • 人工直觉和检查。
  • 创建小型标注数据集或获取标签,然后使用基于标签的质量评估技术。

如果我们认为正在过拟合数据中的噪声,当然有不同的解决方案:

  • 降低模型复杂度:例如,在 K-Means 中减少 k 的值。
  • 惩罚高复杂度:在单一模型中,可以对选择较大的 k 值施加惩罚(成本)。
  • 使用概率模型与正则化:采用概率模型并对其应用正则化技术。

总结

本节课中我们一起学习了在实际中应用无监督学习算法并非总是直截了当的。我们很容易出现两种问题:要么未能提取出全部信号(欠拟合),要么开始拟合噪声(过拟合)。监督学习中也存在类似问题,但在无监督学习中,由于缺乏标注数据,挑战更大,需要专门的技术。

在实践中,应用无监督学习时需要考虑的主要问题是:你是在拟合噪声还是在拟合信号?存在一些技术来评估这个问题,其中一些已在本次课程中提到,另一些我们将在后续讲座中看到。

17.1:无监督概率模型(第一部分)

在本节课中,我们将开始学习无监督学习的第一个主题:密度估计。首先,我会介绍解决此问题所需的几个重要概念,然后定义什么是密度估计,并介绍几种进行密度估计的初步方法。

无监督学习中的概率模型

在之前的课程中,我们探讨了监督学习背景下的概率模型。现在,我想在无监督学习的背景下也引入它们。概率模型将成为我们执行密度估计的工具,并且在我们后续所有关于无监督学习的课程中都会用到。

首先,让我们再次设定背景。我们讨论的是无监督学习,在这种模式下,我们有一个没有标签的数据集。我们的目标是让算法观察这些数据,并从中学习到一些有趣的结构,而无需依赖人工监督。这里的“有趣结构”可能包括聚类、异常值、噪声中的有用信号(例如白噪声中的人声)等。

在之前的视频中,我们定义了无监督学习的一般设置:我们的数据集现在由特征或属性向量组成,但没有标签。我们假设这些特征来自某个数据分布,并且是该分布的独立同分布样本。然后,我们定义了无监督模型的概念,即一个将输入映射到有趣结构组件(如聚类或低维表示)的函数。与监督学习类似,我们的模型通常带有参数,记为 θ。

在监督学习中,我们假设数据来自一个数据分布,一种有趣的建模方法是参数化一个概率分布,并使其拟合以近似真实的数据分布。在无监督学习中,我们也将采用同样的方法。我们将假设概率模型是属性空间上的一个概率分布,并尝试用这个模型来近似我们的数据分布。我们使用符号 P_θ 来表示一个参数为 θ 的概率模型。

为什么概率模型有用?

我们见过不同类型的模型。在监督学习中,我们见过非概率模型和概率模型,并且非概率模型通常有等价的概率模型。在无监督学习的背景下,我们也有这两种方法。

假设我们试图用概率模型 P 来拟合数据。我们可以用它来解决哪些有趣的任务呢?

  1. 生成样本:通过一些额外的技术和算法,我们可以从这个分布中抽取样本。例如,我们可以用学习到的人脸分布来生成人脸图像。
  2. 发现数据结构:如果数据中存在聚类,我们可以检查我们的概率模型,并尝试从中恢复这些聚类。
  3. 密度估计:这是最通用的任务,即尽可能准确地学习数据分布本身。我们的数据分布在 x 空间的不同部分赋予不同的权重,我们的目标是学习一个模型,使其在 x 空间上赋予相同或相似的权重/概率。如果我们找到了一个与数据分布结构相同的概率分布,我们就说我们准确地估计了这个密度。

密度估计是近似数据分布的问题。它是一个非常通用的任务,因为如果我们能近似数据分布,我们就能解决所有其他问题,如生成样本、表示学习、聚类或异常检测等。从这个意义上说,密度估计是我们能解决的最通用的方法。在本讲中,我们将探讨几种实现密度估计的方法,并且在后续课程中,我们也会看到密度估计的各种形式被应用于聚类或降维等实际问题。

分布间的距离度量

如果我们的目标是用模型近似数据分布,那么我们需要首先定义分布之间距离的概念。

在之前关于监督学习的讨论中,我们见过KL散度。这是在机器学习、数学和信息论中广泛使用的度量,用于精确比较两个分布。这也将是我们在本例中使用的方法。

在开始使用之前,我想提醒您KL散度的几个性质:

  • 非负性:KL散度总是非负的。
  • 零值条件:当且仅当两个分布 PQ 完全相同时,KL散度为零。这使得它成为我们想要用 Q 近似 P 时一个有用的优化目标。
  • 不对称性:KL散度是不对称的,这就是为什么我们称它为“散度”而不是“距离”。

如果我们使用KL散度来用模型近似数据分布,我们将优化如下形式的目标:

D_KL(P_data || P_θ) = E_{x~P_data} [log (P_data(x) / P_θ(x))]

展开对数项:

= E_{x~P_data} [log P_data(x)] - E_{x~P_data} [log P_θ(x)]

您可能会注意到,第一项 E_{x~P_data} [log P_data(x)] 仅依赖于数据分布,与我们的模型无关。因此,我们只关心第二项 - E_{x~P_data} [log P_θ(x)],即我们之前见过的负对数似然的期望。

因此,为了最小化KL散度,我们可以等价地最大化模型的期望对数似然,即 E_{x~P_data} [log P_θ(x)]

最大似然估计

然而,上面这个目标通常难以直接处理,因为我们不知道真实的数据分布 P_data,无法计算这个期望值。但是,我们拥有从数据分布中抽取的样本。因此,我们可以使用蒙特卡洛原理,用经验似然来近似这个期望。假设我们有一个包含 D 个点的随机数据集,这个近似就是:

E_{x~P_data} [log P_θ(x)] ≈ (1/D) * Σ_{i=1}^{D} log P_θ(x_i)

这简化为我们熟悉的对数似然目标,我们通过最大化它来学习模型参数 θ。

这样,我们就再次推导出了之前在监督学习课程中见过的原则:最大似然估计。您可以看到,同样的原理只需对符号稍作修改(将 P(x, y) 替换为 P(x)),就可以推导出用于学习无监督概率模型的最大似然目标。

一个简单示例

让我用一个简单的例子来说明这个原理如何应用。

想象一个简单的场景:我们有一个关于抛硬币结果的概率分布。假设有一枚硬币,它有一个未知的偏差,即正面朝上的概率为 θ,反面朝上的概率为 1-θ。我们抛了这枚硬币五次,观察到三次正面和两次反面。

我们可以假设一个特定的概率模型,它只有一个参数 θ(正面朝上的概率)。我们的数据集是三次正面和两次反面。根据模型定义,数据的似然函数为:

L(θ) = P(数据|θ) = θ^3 * (1-θ)^2

现在,我们可以将似然函数绘制为参数 θ 的函数。正如您所料,最大似然原则会选择使似然函数最大化的 θ 值。在这个例子中,当 θ = 0.6(即60%)时,似然函数达到最大。这是合理的,因为我们的数据集中有60%的正面和40%的反面。这个例子只是为了提醒您最大似然估计是如何工作的,并将其应用到一个简单问题上。

总结

在本节课中,我们定义了什么是概率模型,并推导出了可用于学习任何概率模型的目标函数:最大似然估计。对于无监督模型,最大似然估计只是对监督学习版本的一个小修改。

在接下来的视频中,我们将把这些原理应用到有趣的模型类别中,以执行密度估计。

17.2:第2部分:核密度估计 📊

在本节课中,我们将学习一种名为核密度估计的技术,用于执行密度估计。我们将从最简单的直方图方法开始,逐步过渡到更平滑、更强大的核密度估计方法,并探讨其原理、应用和局限性。


概述

密度估计是无监督学习中的一个核心问题,其目标是从数据中学习一个概率分布模型。这个模型可以用于许多下游任务,如异常检测、数据生成和数据可视化。在本节中,我们将介绍两种基础的密度估计方法:直方图密度估计和核密度估计。


直方图密度估计 📈

上一节我们定义了概率模型和学习目标,本节中我们首先来看一种最简单的密度估计方法:直方图密度估计。

其基本思想是将输入空间划分为若干个等宽的区间(称为“箱”或“格”),然后统计每个区间内数据点的数量。通过归一化这些计数,我们可以得到每个区间的概率估计。

以下是直方图密度估计的步骤:

  1. 将数据范围划分为 B 个等宽的区间。
  2. 统计每个区间内的数据点数量。
  3. 将每个区间的计数除以数据点总数 N,得到该区间的概率密度。

公式:对于第 i 个区间,其概率密度估计为 P_i = count_i / N

让我们通过一个一维混合高斯分布的例子来直观理解。我们生成的数据来自两个高斯分布的混合:一个均值为0,权重30%;另一个均值为5,权重70%。

import numpy as np
import matplotlib.pyplot as plt

![](https://github.com/OpenDocCN/dsai-notes-zh/raw/master/docs/crnl-cs5787-appml/img/c77e7ed64deb6f266884422e4a951a6b_28.png)

# 生成混合高斯数据
np.random.seed(0)
n_samples = 20
# 30% 来自 N(0,1),70% 来自 N(5,1)
samples_1 = np.random.normal(0, 1, int(n_samples * 0.3))
samples_2 = np.random.normal(5, 1, int(n_samples * 0.7))
X = np.concatenate([samples_1, samples_2])

# 定义直方图区间
bins = np.arange(-5, 10, 1.5) # 从-5到10,宽度为1.5

绘制数据点和直方图后,我们可以看到两个明显的“峰”,分别对应两个高斯分布的中心。然而,这种方法存在几个明显的缺点。


直方图方法的局限性 ⚠️

虽然直方图方法简单直观,但它有几个关键缺陷:

  1. 维度灾难:区间的数量随维度 D 呈指数级增长。在一维需要 B 个区间,在 D 维就需要 B^D 个区间,这在高维数据中是不可行的。
  2. 结果不光滑:估计出的概率密度是分段常数,呈现阶梯状,不符合我们对许多真实分布光滑的预期。
  3. 依赖于区间划分:直方图的形状严重依赖于区间边界的位置。稍微移动边界,得到的分布形状可能完全不同,这缺乏稳定性。

为了克服后两个缺点,我们引入核密度估计。


核密度估计简介 🧠

核密度估计是直方图方法的一个扩展,旨在产生光滑且不依赖于固定网格的密度估计。

其核心思想是:不再为固定的区间计数,而是为空间中的每一个查询点 x 计算密度。计算方式是,查看数据集中有多少点“靠近” x,并对这些点进行加权求和。“靠近”的程度和权重由一个称为核函数的函数来定义。

一个最基础的核是“礼帽”核,它简单地统计落在以 x 为中心、宽度为 Δ 的区间内的点数。

公式(礼帽核密度估计)
P(x) ∝ (1/N) * Σ_{i=1}^{N} I(||x - x_i|| ≤ Δ/2)
其中 I(·) 是指示函数,当条件成立时为1,否则为0。

这种方法解决了边界依赖问题,但估计结果仍然不光滑。为了获得光滑的估计,我们需要使用光滑的核函数。


使用光滑核函数 ✨

核函数 K(x, z) 衡量两个点 xz 之间的相似度。相似度越高,函数值越大。在核密度估计中,我们使用核函数对数据点进行加权平均。

通用核密度估计公式
P(x) ∝ (1/N) * Σ_{i=1}^{N} K(x, x_i)

这里,K 是一个光滑的函数。我们可以将最终的密度估计 P(x) 理解为在每个数据点 x_i 上放置一个微小的“概率山丘”(即核函数),然后将所有这些山丘叠加起来的结果。

以下是几种常用的核函数:

  • 高斯核K(x, z) = exp(-||x - z||^2 / (2Δ^2))。产生非常光滑的估计。
  • Epanechnikov 核K(x, z) = max(0, 1 - ||x - z||^2 / Δ^2)。在计算效率上更优。
  • 线性核(三角核)K(x, z) = max(0, 1 - ||x - z|| / Δ)
  • 礼帽核K(x, z) = I(||x - z|| ≤ Δ)。即我们最初介绍的非光滑版本。

让我们用高斯核重新估计之前的例子:

from sklearn.neighbors import KernelDensity

# 使用高斯核进行密度估计,带宽 Δ=0.75
kde = KernelDensity(kernel='gaussian', bandwidth=0.75)
kde.fit(X.reshape(-1, 1)) # 需要二维数组输入

# 在密集点上评估对数密度
x_plot = np.linspace(-5, 10, 1000).reshape(-1, 1)
log_dens = kde.score_samples(x_plot)
dens = np.exp(log_dens)

现在得到的密度曲线是一条光滑的、双峰的曲线,很好地捕捉了底层两个高斯分布的结构。每个数据点上的红色虚线小高斯核叠加起来,形成了蓝色的光滑估计曲线。


带宽选择与过拟合/欠拟合 🎯

核密度估计中有一个关键的超参数:带宽 Δ。它控制了核函数的宽度,从而决定了密度估计的光滑程度

  • 带宽过大(Δ=3:估计曲线过于平滑,会丢失数据中的细节结构(例如双峰可能合并成单峰),导致欠拟合
  • 带宽过小(Δ=0.2:估计曲线会紧贴每个数据点,产生许多不必要的峰值和波动,实际上是拟合了数据中的噪声,导致过拟合

选择合适的带宽至关重要。在实践中,我们可以通过以下方式选择:

  1. 启发式方法/可视化:尝试不同的带宽,选择能产生“合理”光滑曲线的值。
  2. 网格搜索与似然评估:由于我们有一个明确的概率模型,可以在一个验证集上计算对数似然,选择使验证集似然最大的带宽。这类似于监督学习中的模型选择。


总结与展望 🚀

本节课我们一起学习了核密度估计。

  • 核密度估计是一种非参数化的密度估计方法,它通过将核函数放置于每个数据点并求和来构建分布。
  • 它解决了直方图方法的不光滑和边界依赖问题,并能更灵活地拟合数据形状。
  • 其主要优点是理论上给定足够数据可以逼近任意分布。
  • 其主要缺点是计算成本随数据量增长(需要存储和计算所有数据点),并且同样受到“维度灾难”的影响——在高维空间中需要指数级的数据点才能获得可靠估计。

核密度估计是处理低维数据密度估计的一个强大工具。在接下来的课程中,我们将探讨一些参数化的密度估计模型(如高斯混合模型),它们能更高效地处理高维数据,并解决核密度估计的一些计算瓶颈。

17.3:第3部分:隐变量模型 🧩

在本节课中,我们将要学习隐变量模型。这是一种特殊的概率模型,对于理解后续视频中的算法至关重要。我们将探讨其基本概念、动机、示例、优势以及学习时面临的挑战。

概述

上一节我们介绍了概率模型的基础。本节中,我们来看看如何通过引入隐变量来构建更强大、更具表达能力的模型。隐变量模型能够捕捉数据中复杂的、未标注的结构,例如人脸数据中的性别、姿态等特征。

什么是隐变量模型?

一个标准的概率模型是定义在观测变量 x 空间上的概率分布,记作 P(x; θ)。我们通过最大化数据的对数似然来拟合模型。

然而,我们经常需要建模的数据集(例如人脸图像)具有复杂的结构。这些结构(如性别、姿态)是数据中真实存在的变异因素,但在训练数据中并未被标注或观测到。隐变量模型 的思想就是将这些未观测到的变异因素作为额外的变量 z 引入模型。

更正式地说,一个隐变量模型是定义在两类变量上的联合概率分布:P(x, z; θ)。其中 x 是我们观测到的多维数据点,而 z 是那些未被观测到但具有意义的结构性变量。

通过边缘化隐变量 z,我们可以得到仅关于观测数据 x 的分布:P(x; θ) = Σ_z P(x, z; θ)。这个模型同样可以用来近似真实的数据分布。

为什么使用隐变量模型?

以下是引入隐变量 z 的两个主要原因:

  1. 简化条件分布学习:如果我们知道了 z(例如,知道了性别、姿态),那么在给定这些条件下,图像 x 的分布 P(x|z) 会比无条件分布 P(x) 简单得多,也更容易学习。
  2. 学习数据的有意义表示:训练好模型后,我们可以通过计算后验概率 P(z|x) 来推断给定数据 x 最可能的隐表示 z。这可以用于聚类或发现数据中的有趣结构。

一个简单示例:高斯混合模型

最简单的隐变量模型之一是高斯混合模型

  • 隐变量 z:一个分类变量,表示数据点属于哪个混合成分(例如,K个类别之一)。其分布为分类分布:P(z=k) = φ_k
  • 观测变量 x:在给定隐变量类别 z=k 的条件下,x 的分布是一个多元高斯分布:P(x|z=k) = N(x; μ_k, Σ_k)

因此,联合分布定义为:P(x, z=k; θ) = φ_k * N(x; μ_k, Σ_k)

高斯混合模型比单一高斯分布更能拟合复杂数据。例如,对于具有两个簇的数据,单一高斯只能给出一个宽泛的拟合,而混合高斯可以用两个分量精确地捕捉每个簇。

隐变量模型的应用

一旦模型被训练,隐变量 z 可以成为数据的有用表示:

  • 在表示上进行算术运算:例如,在图像数据中,我们可以计算“微笑女性”的隐向量与“中性女性”的隐向量之差,然后将这个差异加到“中性男性”的隐向量上,从而生成“微笑男性”的隐表示,并最终生成对应的图像。
  • 可视化与聚类:我们可以将高维的隐表示 z 投影到二维空间进行可视化。通常,具有相似语义的数据点(如相同的手写数字)会在隐空间中聚集在一起。

学习隐变量模型的挑战

尽管强大,隐变量模型的学习存在主要挑战:计算复杂性

应用最大似然原则意味着我们需要最大化边缘对数似然:log P(x; θ) = log Σ_z P(x, z; θ)

这个目标函数中的求和(或积分)项 Σ_z 通常是计算上难以处理的。即使 z 是维度不高的离散变量,可能的组合数也会呈指数增长(例如,30维二值变量有约10亿种组合)。对于连续隐变量,则需要处理难以计算的积分。

因此,直接优化这个目标函数在大多数实际应用中是不可行的。

总结与展望

本节课中我们一起学习了隐变量模型。

  • 核心思想:通过引入未观测的隐变量 z 来增强概率模型的表达能力,使其能够捕捉数据中复杂的内部结构。
  • 主要优势:模型更强大,能够学习数据的有意义表示(z),这些表示可用于聚类、数据操作等任务。
  • 主要挑战:模型的学习(最大化边缘似然)和推断(计算后验 P(z|x))通常计算困难,需要借助近似算法。

在接下来的课程中,我们将看到几个具体的隐变量模型实例,并学习如何通过近似方法(如期望最大化算法、变分推断等)来有效地学习和使用这些模型,以解决无监督学习中的有趣问题。

18.1:第一部分:高斯混合模型 🎯

在本节课中,我们将继续讨论无监督学习,并深入探讨一种称为“聚类”的新问题类型。我们将重新定义聚类,回顾K均值算法,并引入一种基于高斯混合模型的新聚类方法。


概述 📋

在无监督学习场景中,我们拥有一个仅包含属性向量、不包含标签的数据集。这些数据是从某个数据分布中独立同分布采样得到的。聚类的目标是识别数据及其分布中的有趣结构,特别是识别数据中的不同“成分”或“簇”。

一个簇可以被视为数据点的一个子集,其特点是:簇内的数据点彼此之间的相似度高于它们与簇外点的相似度。这精确地定义了我们之前的直觉:一个簇是在特征空间中聚集在一起、并与邻近点明显区分开的一组点。


回顾K均值算法 🔄

上一节我们介绍了聚类的概念,本节中我们来看看第一个具体的算法:K均值。

K均值是一个简单的算法,旨在从数据中发现K个隐藏的簇,每个簇由其质心(均值)来表征。它通过迭代过程来最小化以下目标函数:

目标函数公式:
J = Σ_i ||x_i - μ_{c_i}||^2

其中,x_i 是数据点,μ_{c_i} 是点 x_i 被分配到的簇的质心。这个函数是所有数据点到其所属簇质心的距离平方和。它捕捉了我们的直觉:簇内的点应该彼此更相似,或者说,更接近其簇的中心。

K均值通过一个交替最小化过程来优化这个目标函数:

  1. 分配步骤:将每个数据点分配给距离最近的质心。
  2. 更新步骤:将每个簇的质心重新计算为该簇所有点的平均值。

这个过程反复迭代,直到质心不再发生显著变化。


K均值的局限性 ⚠️

尽管K均值重要且实用,但它也存在一些局限性。

以下是K均值算法面临的主要挑战:

  • 易陷入局部最优:K均值优化的目标函数是非凸的,存在许多局部最优解。算法从不同的初始点开始,可能收敛到不同的解,其中一些解的质量远低于全局最优解。因此,实践中通常需要多次随机重启算法以选择最佳结果。
  • 难以可靠评估聚类效果:增加簇的数量(K值)总是可以降低目标函数值(即“拟合得更好”),但这可能导致对噪声的过拟合,而非捕捉数据的真实结构。
  • 硬分配问题:K均值对点的簇分配是“硬”的,即一个点要么完全属于一个簇,要么完全不属于。这种分配方式无法提供关于点属于某个簇的“置信度”或“概率”。

引入高斯混合模型 🧮

为了应对上述局限性,我们将定义一种新的聚类算法,它基于一种称为高斯混合模型的概率模型。

高斯混合模型是一种隐变量模型,它定义了可见变量 x 和隐变量 z 的联合分布。在聚类上下文中:

  • x 是我们在数据集中能观察到的属性向量。
  • z 是一个分类变量,表示点 x 所属的簇的索引(取值从1到K),但它是隐藏的、不可观测的。

该模型的联合分布分解为两部分:
模型公式:
p(x, z; θ) = p(z; φ) * p(x | z; μ, Σ)
其中:

  • p(z=k; φ) = φ_k 是簇 k先验概率
  • p(x | z=k; μ, Σ) = N(x; μ_k, Σ_k) 是给定簇 k 后,x条件概率分布,这里假设为一个均值为 μ_k、协方差矩阵为 Σ_k高斯(正态)分布

模型参数 θ 是所有 φ_kμ_kΣ_k 的集合。该模型假设数据由K个高斯分布混合生成,每个分布对应一个簇。


高斯混合模型用于聚类 🎲

现在,我们来看看如何利用这个模型进行聚类。一旦我们根据数据拟合好了高斯混合模型,就可以通过计算后验概率来推断任何数据点 x 最可能属于哪个簇。

后验概率公式:
p(z=k | x; θ) = [p(z=k; φ) * p(x | z=k; μ, Σ)] / [Σ_{j=1}^K p(z=j; φ) * p(x | z=j; μ, Σ)]

这个公式给出了点 x 来自簇 k 的概率。与K均值的“硬分配”不同,这提供了一种软分配,每个点对每个簇都有一个隶属概率。这直接解决了K均值无法提供置信度信息的问题。


模型学习与挑战 🧠

为了从数据中学习高斯混合模型的参数 θ,我们采用最大边际似然原则。

学习目标函数(边际对数似然):
L(θ) = Σ_i log p(x_i; θ) = Σ_i log [Σ_{k=1}^K p(z_i=k; φ) * p(x_i | z_i=k; μ, Σ)]

我们需要最大化这个函数。与我们在监督学习中学到的高斯判别分析(GDA)相比,GDA中类别标签 y(相当于这里的 z)是已知的,因此优化更简单。而在无监督的GMM中,由于 z 未知,目标函数中需要对所有可能的 z 值求和,这使得优化问题变得复杂,没有简单的闭式解。

更重要的是,这个边际对数似然函数通常是非凸的,存在多个局部最优解。想象将多个单峰的高斯分布混合,得到的混合分布可能具有多个峰值(局部极大值)。使用梯度下降等算法时,从不同起点开始,可能收敛到不同的局部最优解。


其他聚类算法简介 🌐

除了K均值和高斯混合模型,还有许多其他类型的聚类算法值得了解。虽然本课程无法涵盖所有,但以下是一些有趣的类型:

以下是几种其他重要的聚类范式:

  • 层次聚类:簇具有层次结构,可以自底向上(聚合式)或自顶向下(分裂式)构建树状图。
  • 软聚类/重叠聚类:允许数据点同时属于多个簇,这在如主题建模等领域非常有用(例如,一篇文档可能涉及多个主题)。
  • 图聚类:在图形数据中,寻找内部连接紧密、与外部连接相对稀疏的子图(社区发现)。

如果你对这些算法感兴趣,可以查阅如 scikit-learn 等库的文档,其中实现了许多此类算法,可能对你解决实际应用问题有所帮助。


总结 📝

本节课中,我们一起学习了:

  1. 重新审视了聚类的核心目标:在无标签数据中发现内在的组别结构。
  2. 回顾了K均值算法的原理及其交替优化过程。
  3. 分析了K均值的主要局限性:局部最优、评估困难、硬分配。
  4. 引入了基于概率框架的高斯混合模型,它通过假设数据由多个高斯分布混合生成来建模簇。
  5. 解释了如何用GMM进行软分配,即计算数据点属于各簇的后验概率。
  6. 讨论了GMM参数学习的挑战,即最大化非凸的边际对数似然函数。
  7. 简要介绍了层次聚类、重叠聚类和图聚类等其他聚类范式。

在接下来的部分,我们将深入探讨用于优化高斯混合模型参数的具体实用算法——期望最大化算法。

18.2:期望最大化算法(第二部分)🎯

在本节课中,我们将学习如何优化高斯混合模型,这是在上一节中定义的一种新的聚类模型。为此,我们将引入一种名为“期望最大化”的新优化算法。

概述

上一节我们介绍了高斯混合模型。本节中,我们来看看如何通过期望最大化算法来拟合这个模型。期望最大化是一种用于优化包含潜在变量模型的通用方法,尤其适用于高斯混合模型。

高斯混合模型回顾

我们处于无监督学习场景中,正在优化一个仅包含属性、没有标签的数据集上的模型。我们为这个特定模型定义的一种方法是基于高斯混合模型。

高斯混合模型是如下形式的概率分布:

P(X, Z) = P(Z) * P(X|Z)

它是一个潜在变量分布,其中我们有一组变量 Z,这些变量不在数据中,但我们将以无监督的方式推断它们。Z 为生成数据点的簇提供了一个索引,而 X 则为来自每个簇(簇1、簇2……簇K)的点的分布提供了一个模型。

这定义了一个联合概率模型。我们可以将这个模型拟合到数据中,以找到最可能的簇配置,即生成我们数据的最可能的簇配置。

学习高斯混合模型的挑战

学习高斯混合模型是困难的。如果我们遵循最大似然原则,我们希望优化数据的对数概率。但为了计算对数概率,我们必须计算对所有 Z 的以下求和:

log P(X) = log Σ_Z P(X, Z)

这个目标不再像我们在监督学习中使用相同模型时那样有封闭形式的解。在这个例子中,我们必须对所有可能的 Z 值求和,而不是只有一个指定的值。这使得问题显著更具挑战性。

期望最大化算法简介

期望最大化算法是一种通用方法,可用于拟合多种潜在变量模型,尤其适用于高斯混合模型。它是一种可用于最大化边际对数似然的算法,其形式如下:

max_θ Σ_i log Σ_Z P(X_i, Z_i; θ)

这里,我们最大化一个数据集的边际对数似然,其中 X 是观测到的,但对于每个点,我们必须计算上述求和,并且我们希望优化这个目标函数。

考虑我们试图优化的这个目标。观察发现,如果我们知道真实的 Z,即如果我们知道这个 X 的最佳簇(我们称之为 Z_i),那么我们就不需要进行整个求和,我们可以直接代入,从而得到一个形式如下的目标:

max_θ Σ_i log P(X_i, Z_i; θ)

这是一个非常好的目标,因为现在每个变量都已定义,这简化为我们在早期课程中见过的监督学习设置。现在,这变成了与我们在高斯判别分析算法中解决的问题相同的问题,并且我们已经知道这个问题的封闭形式解。

同样,这是在高斯混合模型的背景下,但即使我们的模型 P 不是高斯混合模型,通常也有简单的算法来学习每个变量都被观测到的概率模型的参数。

因此,给定我们对 Z 的猜测,我们就有了这个联合概率,所有变量都被观测到,我们可以计算参数 θ。下一个观察是,如果我们知道 θ,我们也可以通过查看这个概率来计算簇分配:

P(Z|X)

它为我们提供了簇分配可能是什么的猜测。

算法核心思想

期望最大化的思想是重复交替这两个步骤。给定参数的初始猜测(例如每个簇均值的初始猜测),我们计算簇分配(即这里的猜测 Z)。然后,我们优化模型中的最佳参数集。接着,给定一组参数,我们计算下一个簇分配,并持续交替进行。

这看起来与我们用于拟合K均值算法的过程非常相似。这里的第一个步骤对应于计算簇质心,第二个步骤对应于将每个点分配到其簇质心。虽然不完全相同,但其精神非常相似。其机制不同,并且它优化的是不同类型的模型。实际上,它可以优化更一般类型的模型,但其精神是相似的。

算法步骤详解

期望最大化算法交替执行两个步骤:E步和M步。

在E步中,给定权重的初始估计,我们计算每个数据点属于每个簇的概率,即 P(Z|X)。我们用它来“幻觉”出由 Z 定义的期望簇分配。

在M步中,给定我们“幻觉”出的这些簇分配,我们通过优化给定已获得的 Z 的边际似然来找到新的参数 θ

然后我们重复执行这个过程。该算法保证最终会收敛,并且保证在每一步都会增加数据的边际似然。

算法形式化定义

以下是该算法的完整形式化定义。期望最大化算法(简称EM)在无监督数据集上学习潜在变量模型的参数 θ,并执行以下过程直到收敛。

首先,在E步中,它为每个数据点计算该点属于每个簇的概率,即 P(Z|X)

然后,在下一步的M步中,给定这个分配,它通过求解以下优化问题来计算新的权重:

θ_{t+1} = argmax_θ Σ_i E_{Z~P(Z|X_i; θ_t)} [log P(X_i, Z_i; θ)]

这个问题与监督学习中的问题非常相似,因为这里的概率是完全观测到的。这里唯一有趣的部分是期望。如果我们对 Z 只有一个最佳猜测,那么我们可以直接优化这些对数概率的和。但是,因为我们对 Z 的估计不是单一的,我们有一个关于 Z 簇的概率分布,所以我们计算所有可能 Z 下这个对数概率的期望值。

M步产生一个参数估计,这是对前一组参数的改进。这里,前一组参数 θ_t 是从上一步固定下来的。E步产生一个关于簇分配的概率分布。然后,我们计算数据和簇分配的联合对数概率,并最大化这个联合对数概率的期望值,其中期望是关于我们从E步得到的、对簇分配的最佳猜测概率分布的。

目标函数解析

为了更清楚地说明这个目标是什么,我在这里重写了它。我只是将期望重写为对这些K个簇的求和。对于每个数据点,我们再次最大化数据在给定簇为k的情况下的期望概率。换句话说,我有一个可见点 X,我查看 X 来自每个可能簇的概率,并用我当前猜测的、它确实属于该簇的概率来加权这些概率。

当然,如果 X 来自其簇的概率与我们从E步得到的猜测一致,那么这个值就会很高。在这一点上,我们就已经收敛了。

期望最大化的优缺点总结

期望最大化是机器学习中一个非常重要且广为人知的算法,用于优化潜在变量模型。它相对容易实现,并且保证收敛,这非常好。它也在许多机器学习模型中有效。

然而,它的局限性在于它并非在所有模型中都能工作。特别是,我们需要能够有效地计算后验概率,但并非在所有模型中都能计算,尽管在许多模型中是可以的。此外,它可能会陷入局部最优。这是K均值算法和本算法共有的一个缺点。

实际上,存在不遭受局部最优问题困扰的期望最大化算法,但这些算法要复杂得多。在实践中,EM算法易于实现且效果相当好,因此通常是实际使用的算法。

总结

本节课中,我们一起学习了期望最大化算法的核心思想、形式化定义及其在高斯混合模型优化中的应用。我们了解到,EM算法通过交替执行E步(计算后验概率)和M步(最大化期望对数似然)来迭代优化模型参数,是一种强大且广泛使用的无监督学习工具。在下一节中,我们将具体探讨如何将EM算法应用于高斯混合模型。

18.3:高斯混合模型中的期望最大化算法 🧮

在本节课程中,我们将学习如何将期望最大化算法应用于高斯混合模型。我们将推导该算法的具体步骤,并详细讲解其在高斯混合模型中的使用方法。

概述

上一节我们介绍了聚类的基本概念。本节中,我们将深入探讨期望最大化算法在高斯混合模型中的具体应用。我们将推导算法的每一步,并解释其背后的数学原理。

高斯混合模型回顾

回忆一下,混合模型是一种具有以下形式的概率分布:

我们有一个包含可见变量 X 和潜在(或未观测)变量 Z 的分布。Z 是聚类分配。给定一个数据点 x,其对应的 z 表示 x 来自哪个聚类的索引。x 是从 k 个可能的聚类中生成的点,给定聚类 k 时观察到 x 的概率由一个均值为 μ、协方差为 Σ 的高斯分布给出。这些参数由 k 索引,因为每个聚类都不同。

每个聚类的先验概率由 φ 给出。

在该模型下,我们假设数据生成过程如下:首先根据 φ 给出的概率选择一个聚类索引,然后从该高斯分布中采样一个点。

所有参数由 μΣφ 组成。

期望最大化算法回顾

期望最大化算法是一种可用于拟合任何潜在变量模型的算法。它重复以下两个步骤直到收敛:

  1. E步:给定参数的当前估计值,我们尝试“猜测”聚类分配。我们根据模型当前告诉我们的信息,计算给定当前参数 θ 时,聚类分配的最佳猜测。
  2. M步:给定由后验分布提供的这些概率性聚类分配,我们通过最大化数据的期望对数似然来计算新的参数权重,其中期望是关于聚类分配 Z 的,概率是关于参数的当前最佳猜测的。

高斯混合模型的E步推导

现在,让我们为高斯混合模型推导这些步骤。

E步相对简单。我们在之前的视频中看到,可以使用贝叶斯规则计算 x 属于聚类 K 的概率:

P(Z=k | X=x; θ) = [φ_k * N(x; μ_k, Σ_k)] / [Σ_{j=1}^{K} φ_j * N(x; μ_j, Σ_j)]

这里,我们简单地应用了条件概率的定义,然后使用模型的定义将分子分解为两个部分,并在分母处使用了全概率公式。这里的所有项都易于计算,因此我们可以非常容易地计算这个概率。

所以,这是给定当前参数 θ 的最佳猜测下,x 源自每个分量的概率向量。因此,E步相当直接。

高斯混合模型的M步推导

M步也相对可行。回忆一下,在M步中,我们希望最大化数据的期望对数概率,其中 Z 是从前一步E步得到的最佳猜测中采样的。

我们想要优化这个函数。对于高斯混合模型,我们可以使用全概率定律将其分解为以下形式:

E_{Z~Q}[log P(X, Z; θ)] = Σ_{i=1}^{N} Σ_{k=1}^{K} Q(Z_i=k) [log N(X_i; μ_k, Σ_k) + log φ_k]

这里,第一组分量是给定 ZX 的条件分布,第二组是 Z 的先验分布。我们通过将联合概率分解为 P(X|Z) 乘以 P(Z) 来将其分解为这两个部分。这种分解在我们之前在监督学习背景下处理这类模型时也出现过。

现在,让我们分别最大化这两个部分,因为这里的条件概率部分只涉及 μΣ 参数,而先验部分只涉及 φ 参数。由于我们有一个求和式,我们可以分别优化它们。

让我们从前者开始,我们希望最大化这个特定值。我们这里本质上得到的是一个高斯分布的对数似然,均值为 μ,协方差为 Σ,只是现在我们为每一项分配了一个权重。每个 X 都有一个与之相关的权重。

这意味着,我们不是在拟合所有数据的似然,而是在拟合加权数据集的似然,每个数据点的权重由其属于类别 K 的概率给出。这里,我只从前面的求和式中选择了涉及特定 k 值的 μ_kΣ_k 的项。

因此,最终的优化问题简化为在加权数据集上拟合并最大化一个高斯分布的似然。

M步的闭式解

因此,我们可以很容易地以闭式计算出该问题的解。

对于均值 μ_k 和协方差 Σ_k,最优估计为:

μ_k = [Σ_{i=1}^{N} Q(Z_i=k) * X_i] / [Σ_{i=1}^{N} Q(Z_i=k)]

Σ_k = [Σ_{i=1}^{N} Q(Z_i=k) * (X_i - μ_k)(X_i - μ_k)^T] / [Σ_{i=1}^{N} Q(Z_i=k)]

你可以通过求前面表达式的导数,设为零并求解这些参数来验证这一点。同时,你也应该能理解这是合理的:首先,如果我们对未加权的数据拟合高斯分布,那么均值和方差的最佳估计就是数据的均值和方差。现在,给定数据有权重,那么每个分量的均值的最佳猜测是数据的加权平均,每个点 X 对均值的贡献由其概率加权。同样,这里我们也取协方差的加权平均。这里的归一化参数 N_k(属于类别 K 的点数)也是属于类别 K 的概率之和。这相当直观。

对于涉及参数 φ_k 的剩余项,我们可以做出同样的论证。每个类别比例的最佳估计是属于该类别的点的总和。这里我们有 N_k,即与该类别关联的点数。但由于每个点不是以“硬”方式关联,而是以“软”方式关联,其关联强度由概率给出,我们只需对所有点属于类别 K 的概率求和。这就是分子。分母是所有类别的总和。为了说服自己这是合理的,想象一个“硬”分配,其中每个 x 只分配给一个聚类,那么这就简化为简单地计算分配给聚类 K 的点数。但鉴于我们这里是“软”分配,我们对概率求和,这是正确的做法。

φ_k = [Σ_{i=1}^{N} Q(Z_i=k)] / N

算法总结

总结一下,高斯混合模型中的期望最大化算法在两个步骤之间交替进行:

  • E步:使用我们的公式计算这个分配 Q(Z_i=k)。这是每个 X_i 的概率向量。
  • M步:使用我前面几张幻灯片中的公式计算 μΣφ 的最佳猜测。

本节课总结

在本节课中,我们一起学习了期望最大化算法在高斯混合模型中的具体应用。我们首先回顾了高斯混合模型和期望最大化算法的基本框架。然后,我们详细推导了算法的E步和M步。在E步中,我们计算了数据点属于各个聚类的后验概率。在M步中,我们通过最大化加权数据的期望对数似然,得到了模型参数(均值、协方差和混合权重)的更新公式。这些步骤迭代进行,直到模型收敛。

18.4:第4部分 - 概率模型的泛化能力 🎯

在本节课中,我们将探讨概率模型(特别是高斯混合模型)在实际应用中的一个重要问题:泛化能力。我们将分析模型如何避免过拟合与欠拟合,并将其与K均值聚类进行对比。通过这一对比,我们将介绍一种适用于概率模型的改进过拟合检测方法,并解释为何概率模型在无监督学习中具有独特价值。


数据分布与高斯混合模型回顾

上一节我们介绍了聚类的基本概念,本节中我们来看看概率模型在无监督学习中的应用。在无监督学习中,我们假设数据来自某个未知的概率分布,数据集仅包含属性特征,没有标签。

我们使用一种称为高斯混合模型的概率模型。该模型假设数据由多个高斯分布混合生成,每个数据点属于其中一个成分。模型包含以下要素:

  • 隐变量 Z:为每个数据点分配一个成分索引。
  • 混合权重 φ:表示每个成分被选中的先验概率。
  • 高斯分布参数:每个成分 k 有其均值 μ_k 和协方差 Σ_k

生成过程可描述为:首先根据 φ 选择一个成分索引 z,然后从对应的高斯分布 N(μ_z, Σ_z) 中采样得到观测数据 x。我们的目标是根据观测到的数据 x,推断出最佳的模型参数 (φ, μ, Σ)

泛化能力与合成数据集

机器学习中的一个核心概念是泛化能力。我们基于训练数据拟合模型,但更关心模型是否学到了超越训练集的有用特性,能否对新数据做出良好解释。

为了探讨高斯混合模型的泛化问题,我们创建一个合成数据集。该数据集使用以下代码生成,它实际上来自一个具有四个成分的高斯混合模型。

# 示例代码:使用scikit-learn生成高斯混合数据
from sklearn.datasets import make_blobs
X, y_true = make_blobs(n_samples=150, centers=4, cluster_std=0.60, random_state=0)

下图可视化了生成的数据点。虽然我们看不到标签,但为了说明,我们用颜色标出了其真实的四个成分来源。在实际训练中,我们不会使用这些标签

此外,我们还预留了50个数据点作为独立验证集,它们将不参与任何训练过程。

不同组件数量下的模型表现

现在,我们尝试用不同组件数量的高斯混合模型来拟合训练数据,并观察其表现。

组件过少:欠拟合

如果我们使用组件数量过少的模型(例如2个组件),将无法捕捉数据中真实的四个成分结构。

以下是使用scikit-learn拟合两个组件模型的方法:

from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=2)
gmm.fit(X_train) # 仅使用特征X,没有标签y

可视化结果显示,模型找到了两个聚类中心(红点),但显然未能恢复数据的真实结构。此时,模型在训练集和验证集上的对数似然值相近,但都较差,这是典型的欠拟合

组件适中:良好拟合

当我们将组件数量增加到4个时,模型成功恢复了数据的真实结构。训练对数似然值得到改善。

组件过多:过拟合

如果我们继续增加组件数量(例如10个或20个),模型开始拟合数据中的噪声。例如,它会将少数几个离群点单独划分为一个簇。

虽然模型在训练集上的对数似然值持续提升(从-3.89到-2.92,值越大越好),表明对训练数据的拟合越来越好,但这可能是以捕捉噪声为代价的。

关键观察点在于:此时,模型在验证集上的对数似然值开始下降。这表明模型学到的结构不能很好地泛化到新数据,出现了过拟合

极端情况:严重过拟合

在极端情况下,我们使用与数据点数量相近的组件(如150个点使用50个组件)进行拟合。模型在训练集上的对数似然达到最高(-1.58),但对验证集的拟合极差(对数似然降至-28)。

原因在于,模型为训练集中的微小噪声(如两个紧邻的点)也创建了组件。这些组件在验证集对应的区域可能没有数据点,导致验证集中的点来自任何组件的概率都非常低,因此对数似然值很差。

利用对数似然检测过拟合

以上实验揭示了一个对于概率模型非常重要且有趣的观察:我们可以使用验证集上的对数似然作为检测过拟合的指标

在概率模型中,我们有一个明确的评估指标——对数似然。通过比较模型在训练集和验证集上的对数似然,我们可以判断其泛化能力:

  • 如果模型在训练集上似然高,但在验证集上似然低,则很可能发生了过拟合。
  • 如果两者都低,则可能是欠拟合。

为了更清晰地展示这一规律,我们绘制了以下图表:横轴为高斯混合模型的组件数量(从1到25),纵轴为负对数似然(值越低越好)。

从图中可以看到:

  • 训练集曲线(蓝色):随着组件数量增加,负对数似然持续下降,拟合能力不断增强。
  • 验证集曲线(橙色):在组件数量达到3或4个时,性能达到最佳并进入平台期。此后继续增加组件,验证集性能不再提升甚至恶化,明确指示了过拟合。

这种曲线形态与监督学习中的训练/验证误差曲线非常相似。这表明,在无监督的概率模型中,我们同样可以获得用于模型选择和复杂度控制的实用工具,这是概率模型的一大优势。

方法局限与注意事项

尽管使用验证集对数似然检测过拟合非常有用,但我们需要指出,这并非一个万无一失的解决方案。

它提供了检测过拟合的重要线索,但不如监督学习中测量分类准确率那样直接可靠。例如,在上图的特定情况下,即使组件数量达到8或9个,验证集负对数似然的上升幅度也并不非常剧烈,可能使最佳K值的选择不够明显。

此外,如果数据量显著增大,验证集性能的平台期可能会持续更久,使得过拟合的拐点更难被察觉。

因此,对数似然比较是一个应当掌握和使用的强大技术,但在实践中仍需谨慎,并结合其他检查手段。你可以通过修改本例附带的代码,调整参数,来更深入地理解其优势和局限。


总结

本节课中,我们一起学习了概率模型泛化能力的重要性。我们了解到,泛化能力对于监督学习和无监督学习同样关键。

在高斯混合模型这类概率模型中,我们可以通过比较模型在训练集和独立验证集上的对数似然来有效检测过拟合。如果发现过拟合,可以采用与之前类似的技术来缓解,例如减少高斯混合模型中的组件数量

这种基于概率的评估框架,为无监督学习中的模型选择与验证提供了坚实的理论基础和实用工具。

19.1:什么是降维? 📉

在本节课中,我们将继续学习无监督学习的主题,并开始探讨一个名为“降维”的新问题。这是继密度估计和聚类之后,我们将讨论的第三个无监督学习问题,也是一个非常重要的主题。

概述

降维旨在发现数据中的冗余特征或高度相关的特征,并用更少、更低维度的特征集自动替换它们。这样做可以使数据更易于思考、计算更高效、可视化更直观,并作为其他算法的输入,提升整体分析效果。

降维的定义与动机

上一节我们介绍了无监督学习的通用设定。本节中,我们来看看降维的具体定义及其应用场景。

降维可以被形式化地定义为一种无监督学习问题。其目标是学习一个模型 F,该模型具有参数 θ,并将输入 X 映射到另一个集合 Z 中的点。Z 是数据的低维表示所在的空间。用公式表示即:

z = F(x; θ)

一个特别重要且流行的降维类型是线性降维。在线性降维中,我们假设输入存在于 ℝ^D 空间,并选择潜在的低维集合为 ℝ^P 空间,其中 P < D。此时,函数 F 是一个线性映射,其参数 θ 可以关联到一个矩阵 W。低维表示 z 的计算公式为:

z = Wx

以下是降维有用的一些具体原因:

  • 简化数据:更少的特征使数据更易于理解和处理。
  • 提升计算效率:低维数据能加速后续算法的运行。
  • 改善可视化:将数据降至2维或3维,便于我们直观观察其结构和模式。
  • 作为预处理步骤:降维后的数据可以作为其他无监督或有监督算法(如分类)的输入,有时能获得更好的性能。

降维实例

为了更具体地理解降维,让我们看几个实际例子。

第一个例子涉及鸢尾花数据集。考虑花瓣长度和花瓣宽度这两个特征,它们都以厘米为单位,并且高度相关。这暗示存在一个更根本的属性(例如“植物大小”)可以解释这两个测量值。通过降维,我们可以用这个单一的“植物大小”属性来替代原来的两个特征,从而简化数据分析。

另一个例子是手写数字数据集。原始图像是28x28像素,即784维的向量。通过一种基于神经网络的非线性降维算法,我们可以将这些高维数据转换为2维表示。在这个2维空间中,即使没有使用标签信息,不同数字也形成了清晰分离的簇,这极大地便利了后续的分类和可视化。

第三个例子是关于人类DNA序列的分析。每个数据点是一个表示基因组中特定位置核苷酸存在与否的高维向量。通过降维,我们发现学到的两个最重要的低维轴竟然与地理坐标(南北和东西)高度相关。将数据投影到这个2维空间后,我们几乎重现了欧洲地图。这揭示了人类基因组变异的主要来源与地理邻近性相关,这是一个有趣的科学发现。

降维方法的类型

降维领域拥有丰富多样的算法。虽然本课程的重点将放在线性降维方法上,但了解其他类型也很有帮助。

以下是几种主要的降维方法类别:

  • 线性方法:例如主成分分析(PCA),它通过线性变换寻找数据中方差最大的方向。
  • 非线性方法:包括基于核函数的方法,可以将线性算法(如PCA)扩展为非线性版本。
  • 基于深度学习的方法:利用神经网络学习复杂的非线性映射,功能非常强大。
  • 基于独立性的方法:例如独立成分分析(ICA),通过最大化信号间的独立性,常用于语音去噪或盲源分离。
  • 概率方法:为降维算法建立概率模型(如概率PCA),从而提供不确定性估计,并能使用对数似然来衡量过拟合。

总结

本节课我们一起学习了降维的核心概念。我们了解到,降维是一种旨在发现并压缩数据中冗余或相关特征的无监督学习技术。我们通过鸢尾花数据、手写数字和基因组数据等实例,看到了降维在简化数据、提升可解释性、改善可视化以及揭示潜在结构方面的强大作用。最后,我们简要回顾了线性、非线性、深度学习和概率性等多种降维方法。在接下来的课程中,我们将深入探讨几种特别重要和流行的具体降维算法。

19.2:主成分分析(PCA)📊

在本节课中,我们将要学习一种非常重要的降维算法——主成分分析。我们将从基础概念开始,逐步理解其模型、优化目标以及如何求解。


无监督学习问题回顾

上一节我们介绍了无监督学习问题的基本组成部分。本节中,我们来看看主成分分析的具体定义。

一个无监督学习问题包含一个数据集和一个学习算法。与监督学习不同,数据集没有标签。学习算法从一个模型类和一个目标函数开始,目标函数衡量模型对数据的拟合程度,优化器则选择能优化该目标的模型。算法与数据集结合后,输出一个无监督学习模型。

在之前的视频中,我们定义了降维模型:它是一个为数据输入输出低维表示的函数。现在,我们将定义第一个算法——主成分分析。我们将通过定义模型类、定义要优化的目标函数,并说明如何优化这个目标来介绍它。


线性降维与PCA模型假设

我们试图解决的问题是线性降维。我们的数据存在于一个D维空间X中,我们希望找到一个维度为P(P < D)的低维空间Z,使得Z能很好地表示我们的数据。

因为我们进行的是线性降维,我们假设映射X到Z的模型F具有以下形式:它是一个线性函数,因此可以表示为矩阵乘法。所以,Z将是输入X的线性变换。

主成分分析模型基于以下关于数据行为的假设:

  • 它假设即使我们的数据是D维的,大多数数据点也靠近一个P维的低维子空间。
  • 由于这是一个线性子空间,我们可以为该子空间选择一组标准正交基向量。
  • 由于大多数数据位于或靠近这个子空间,我们可以用位于这个低维空间Z中的一个点X̃来近似任何数据点X。因此,它可以写成张成该空间的基向量的线性组合。

通过一个例子更容易理解这些假设。


直观理解:数据子空间与坐标

当我说数据存在于一个低维子空间时,可以想象三维空间中的点。即使空间是三维的,大多数点也靠近一个二维平面。在这种情况下,数据本质上存在于这个低维子空间(蓝色平面)中,这就是我们要学习的空间Z,它存在于更大的三维空间X中,但维度更低。

给定这个平面,我们可以找到它的一组基。如果我们有这样一个平面,我们可以找到两个向量作为这个空间的基,这些向量我们用W表示,它们是该平面的标准正交基。那么,潜在表示Z将是在这个特定空间中的坐标。

换句话说,如果这两个向量对应我的基,就好像它们沿着这个二维平面放置了坐标轴,它们的值在这个平面内定义了一个新的坐标系。对于每个点,我们可以找到它在这个平面上的位置,即在新坐标系(Z1和Z2)中的坐标。因此,一个存在于三维空间X中的点,我可以通过将其投影到这个平面上,然后找到它在基下的坐标,从而找到它在Z系统中的坐标。

这是对PCA目标的一个直观解释:我们希望找到这个平面(或任何其他低维空间),并希望使用基于一组张成该空间的向量W的坐标系来表示这些点在该平面中的表示。


PCA模型的形式化定义

在PCA中,我们学习一个模型函数F_θ,它将X映射到Z。该模型的参数θ是一个矩阵W,它是一个D x P的矩阵,包含P个标准正交列向量。因此,在PCA中,我们选择一个可以写成标准正交矩阵的线性变换。这个操作的结果将是X在这个由这些标准正交列向量定义的新坐标系中的表示。

使用这种模型,我们可以执行两个任务:

  1. 编码:基于一个X,我们找到输入的低维表示:Z = W^T X
  2. 解码:因为W是一个具有标准正交列的矩阵,我们可以再次将W应用于Z:X̃ = W Z。结果X̃是位于低维子空间中的一个点,但现在这个点是在X坐标系中表示的。

因此,我们可以执行解码操作,基于其低维表示来恢复该点的高维近似表示。


PCA的学习目标:重构误差

现在,给定这个模型,我们如何学习它?PCA可以被视为优化两个不同的目标,稍后我将展示它们是等价的。

定义目标的一个非常自然的方式称为重构误差。这个目标试图解决的问题是:我们如何找到一个好的低维子空间,作为数据的良好低维表示?

PCA的重构观点认为,一个好的目标是最小化每个点与我们定义的低维空间内其最近近似值之间的差异。换句话说,如果我们从一个点X开始,我们将其编码以获得向量Z,然后我们通过应用解码变换来解码该点。现在我们得到同一个X的解码版本X̃。通常它不会等于原始的X,它将是我们最好的近似。因为获得Z是一个有损操作,所以X是D维的,现在我们得到一个P维的点,然后我们将其映射回一个D维空间。但当我们将其降到P维时,我们丢失了一些信息,因此这个X̃永远不会等于原始的X,但会很接近。一个好的子空间是当我们的重构与原始点的值紧密匹配时。

我们可以通过图像来看这一点。这里我有一个由基定义的二维子空间,但大多数点不会完全落在这个平面上,它们会稍微在外面一点,在平面上方或下方一点。我们能选择的最佳平面是尽可能接近所有点的平面。因此,上述重构目标试图最小化的正是:选择一个尽可能接近所有点的平面,使得每个X在平面上的投影X̃尽可能接近其原始点。


PCA的另一个学习目标:方差最大化

我们可以用于PCA的另一个目标称为方差最大化。为了解释这意味着什么,我们将看一个例子。

再次考虑我们的鸢尾花数据集。这里我画了两条线:之前看到的蓝线和另一条垂直于蓝线的橙线。我们可以尝试在这两条线之间选择,哪一条能代表这个数据更好的低维子空间?这里我们从二维投影到一维,我给出了两个选项:蓝色和橙色。

直观上,蓝线显然更好,因为它更好地捕捉了数据的形状。另一方面,橙线捕捉数据形状的效果最差。因此直观上,蓝线更好。我们希望自动识别蓝线更好这一事实。

我们如何做到这一点?一种方法是观察到,如果我们将点投影到蓝线上,它们会比投影到橙线上更加分散。这就是我们所说的最大化数据方差。直观上,蓝线更好,因为点投影到其上时更加分散,这是我稍后会再次审视的直觉。

为了让这一点更精确,让我们看下面这个更简单的数据集,我们只有五个点。一种降维方法是使用下面这条线。这里我有数据在这个一维平面上的投影。这些数据紧密地聚集在均值周围。我们称这种投影具有低方差,所以黑色的点(即在这个低维平面上的投影)彼此都很相似,因此方差低。

另一种方法是沿着下面这条线投影。现在我们的黑点具有高方差,均值在零附近某处,但数据沿着这个方向分散得多。我们的目标是从数据中自动识别出这个高方差方向。


方差最大化的数学定义

让我们从数学上定义高方差的含义,然后为此推导一个目标。

引入以下符号: 表示任何值的经验期望。我指的是简单地对输入数据点取平均。我们假设每个点Xi的概率为1/n,因此任何函数关于X的这个分布的期望值将只是平均值。同时,我们假设数据已经中心化,意味着我们选择的X使得均值为0。如果不是这样,我们将应用一个称为中心化的变换,通过从所有数据点中减去均值来确保这一点。事实上,这是应用PCA时通常需要做的操作。给定我们执行了这个操作,我们的数据是中心化的,那么数据的平均值等于0,并且投影数据的期望值也将等于0。

现在我们可以计算投影数据期望方差的公式。投影坐标中的数据等于Z,它是X的变换。我们想最小化这个,或者至少想有一个这个方差的公式,即投影坐标中的数据围绕其均值变化的程度。当然,Z的定义只是点X的变换,所以我们可以用其定义替换这里的Z。由于我们执行了这个变换,这个值将等于0,所以我们只剩下以下值,这是数据方差的定义。

因此,我们可以定义第二个目标,即方差目标,记作J2,这是我之前的定义。同样,如果我写出这个期望在这里的含义,那只是投影数据点的平均范数,并且因为所有这些投影数据点的平均值为零,它们的幅度就是方差的定义。


两个目标的等价性

一个有趣的事实是,这两个目标是等价的。因此,如果我们最大化方差,或者最小化重构目标,那么这两者是等价的。

这里有一个例子展示了这背后的直觉。想象我们有一个一维平面,并将这些二维点投影到其上,或者说我们寻找最佳的一维平面。一种解决方案是重构视图,即我们希望选择尽可能接近这些黑点的平面,因此我们希望最小化这些红线的长度。另一种观点是方差最大化,即这些在线上的红点(即投影)应该尽可能分散。事实证明,最大化分散度的直线和最小化到点距离的直线是同一条直线。

我们也可以在数学上证明这是正确的。这需要两个步骤:首先,我取一个输入X,通过一些代数操作,我加减这个量,可以将其分解为两个向量的和。第一个对应于投影数据点在原始空间中的坐标,第二个简单地是X与其投影X̃之间的差。你可以看到它们是正交的,因为我们执行的是正交投影。到目前为止,这只是一个简单的恒等式。

现在,如果我假设它们是正交的,我可以计算上一张幻灯片左右两边的范数。然后我利用它们正交的事实,说明它们和的范数是它们范数的和。然后在这里我只是进行一个变换,我用这个向量替换这个向量,因为W是一个标准正交矩阵,用一个标准正交矩阵乘以一个向量不会改变其范数。所以第三行也成立。现在我们推导出了这个恒等式。

这里的观察是,这非常接近我们的目标。事实上,这里这只是我们的方差目标,而这是我们的重构目标,它们的和等于一个常数。

因此,这意味着当我们最大化一个时,我们就在最小化另一个。或者更数学地说,这是我们的目标定义,现在我将之前的恒等式代入这一项,我得到一些看起来像我的第二个目标加上一个常数再乘以n的东西。从这个意义上说,这两个目标是等价的。


优化方差最大化目标

所以我现在已经为主成分分析定义了两个目标。问题是现在我们如何优化这两个目标中的任何一个?我们可以选择任何一个,我们将选择执行方差最大化的那个,即J2。回想一下,这是J2的形式,这里我只是明确地写出这意味着什么。W再次回想一下,这是一个矩阵,其中每一列都是一个标准正交向量,那是我们的标准正交基。我可以直接展开L2范数的定义,这个目标将具有这种特定形式。

为了理解如何优化这个目标,让我们以稍微不同的方式重写它。这是我们之前的目标定义,这是从上一张幻灯片复制的。现在,这只是一个平方项,所以是这个项乘以它自己。在第二行,我只是把平方写成了同一个数相乘两次。现在在第三行,我所做的只是重新排列一些项并改变求和的顺序。最后,在最后一行,我替换了这个表达式。首先,这是两个向量的外积,它的维度是D x D,现在我加了n个这样的外积,所以这里是一个D x D矩阵,我将其表示为Σ̂。这个矩阵恰好是数据的经验协方差矩阵。

总结一下,这是一系列代数操作,从我们的方差目标开始,并将其转化为P项的和,其中每一项我们都有这个矩阵Σ,并从两边乘以W。这只是重写相同目标的不同方式。


协方差矩阵的特征分解与最优解

现在,这个矩阵Σ是半正定的。你可以很容易地通过应用半正定矩阵的定义来检查这一点。正因为如此,它有一个特征分解,即这种形式的分解:我们可以将Σ̂写成三个矩阵的乘积:Q、Λ和Q^T。其中Q是一个矩阵,其列是此矩阵的标准正交特征向量,Λ是一个对角矩阵,除了对角线外其他地方都是0,对角线上的条目从上到下依次是λ1, λ2, ..., λD,这些就是特征值。我们也可以将这个表达式重写为以下形式,即特征向量的外积加权λj的和。

现在我们可以利用这个事实来推导我们方差最大化目标的最优解公式。

首先,让我们看一个更简单的情况,当P=1时。当P=1时,这是我们必须优化的目标。我们只优化一个向量W,我们想选择最佳的W来最大化以下目标。

我们如何选择这个W?利用特征分解,我们可以将目标写成如下形式。这里我只是应用了特征分解的定义。所以我们这里有d项的和,其中我们有λj,并且我们乘以W与每个特征向量的点积。

我声称,这个问题的最优解在W等于第一个特征向量时达到。并且这个目标达到的最优值是λ1,即最大特征向量的值。为什么这是真的?首先,让我们定义这些项。这是我们的目标形式,我可以将每个点积称为αj。

你应该观察到这些αj平方的和,基本上就是这个和,它简单地是这个向量的值。所以我用Q乘以W,取它的L2范数平方。因为W是标准正交的,并且所有的Q都是标准正交的,我们只是在做标准正交向量之间的点积。所以这个值将是1。因此αj的和将是1。

现在我们的目标在这里有这个形式,我只是插入了我的αj定义。因为αj平方的和是1,这本质上是λj的加权平均值。所以我们这里有1个单位的权重,它们加起来等于1,这将是λj之间的加权平均值,权重在0到1之间,和为1,它们是αj的平方。因为这个加权平均值,当所有权重都归于α1时,这个加权平均值被最大化,并达到其可能的最大值λ1。所以当α1等于1且所有其他α等于0时。那么,这个值达到其可能的最大值λ1。如果情况不是这样,如果α1小于1,并且其他一些α非零,那么我们将得到λ1和另一个小于λ1的特征值之间的平均值,这将为目标给出一个更小的值。所以α1必须等于1。α1何时等于1?当W与最大特征向量Q1的点积等于1时,两个向量的点积等于1当向量相同时。因此,这个目标函数的最优解在W等于Q1(即主导特征向量)时达到。

最后,我为一维设置给出了这个例子,但对于P>1的情况同样成立。当P大于1时,我们的目标是一个这样的和。这个和像前面的例子一样被最大化,当每个W,基本上P个W是P个最大的特征向量时,那么达到的最大值是P个最大特征值的和。

同样,如果情况不是这样,那么我们将取出一个λ并用一个更小的λ替换它,这只会使目标值变小。这里,这些λ的和可以被称为低维投影所解释的方差。如果P等于D,那么我们将恢复所有的特征值,这个数字将等于1。我们的想法是找到一个P,使得P仍然很低,但我们仍然可以解释数据中由这个分数解释的大部分变化。


PCA算法总结与实现步骤

最后,我刚才提到的所有内容定义了主成分分析算法的全部内容。正如我之前所说,它是一种用于无监督学习,特别是降维的算法。它优化一个模型族,这是一组这种形式的线性投影。我们必须选择这个矩阵W,我们通过优化两个目标之一来选择它:要么是重构误差,要么是方差最大化。实现这一点的方法是执行一个称为矩阵特征分解的操作。

当应用这个算法时,有几个实际考虑因素值得注意:

  1. 数据标准化:应用PCA时,将数据标准化为零均值和单位方差非常重要。这意味着对于每个数据点和每个维度,我们计算该维度上数据的经验平均值,并将其减去,以确保沿每个维度的数据点平均值等于0。同样,我们还计算该维度上数据的标准差,并除以标准差以确保数据的标准差等于1。我们这样做的原因是为了解决由于单位选择可能出现的各种缩放问题。
  2. 选择维度P:应用PCA时需要做出的一个重要决定是选择数据的维度P。与K-means一样,对此没有简单的解决方案,在实践中我们求助于启发式方法,如肘部法则。换句话说,我们尝试不同的P值,并查看我们某个目标函数(如方差或重构误差)的值,当我们在曲线中看到一个肘部时,就像我们对K-means所做的那样,这意味着我们可能为数据找到了一个好的P值。


PCA应用实例:鸢尾花数据集

作为应用PCA的一个例子,让我在之前见过的鸢尾花数据集上实现一个简单版本。回想一下,我们的鸢尾花数据集实际上是四维的,在大多数例子中我只使用了前两个维度,但事实上它是更高维的,它有四个维度。

我们如何应用PCA将此数据转换为更低的两维空间?在NumPy中实现PCA非常简单。X这里是我们的设计矩阵,是一个N x D的矩阵。我们可以使用一点NumPy来计算矩阵Σ的值。我们可以形成协方差矩阵。现在我们可以执行Σ的特征分解以获得特征向量Q,我们选择W为前P个特征向量,然后我们沿着这些特征向量投影数据,这就是PCA所做的一切。

哦,顺便说一下,我想再次强调,当我加载鸢尾花数据集时,我对数据进行了白化处理,或者说我中心化并标准化了数据以使其标准差为1。我想再次强调,这在应用PCA时很重要。

好的,所以这里我们定义了PCA。现在我们可以将其应用于我们的数据集。应用非常简单。我们应用这个投影Z,现在我可以可视化数据,我有这两个不同的簇,分别是山鸢尾和非山鸢尾花。我可以通过用标签(我已丢弃)可视化它们来确认结构,但现在我可以在这里使用它们来验证我的簇的质量。你可以看到不同的簇被合理地很好分离,即使这两个簇在这里有些混合,但总体上仍然有很好的分离水平。这种分离水平通常比我们之前只使用前两个维度时要好。

验证PCA为我们提供了比仅仅取前两个维度更有用、更好的数据投影的一种方法是,我们可以在这个数据上训练分类器。这里我在没有PCA的数据上训练一个softmax分类器,只使用前两个维度,这是我们在本讲座所有例子中一直使用的那种投影。然后我也可以做同样的事情,但现在我使用PCA坐标中的数据。softmax分类器的准确率(我回想一下,这是多项逻辑回归,与softmax分类器相同)在PCA维度上显著高于仅使用数据前两个维度的准确率。


PCA的优缺点总结

这结束了我们对PCA的讨论和示例,我想通过总结这种方法的几个优点和缺点来结束。

  • 优点:它非常简单直观,实现快速,并且在实践中效果很好。它提供了许多良好的可视化效果。
  • 缺点:它仍然是线性的,你最终会发现线性降维方法不够灵活的应用场景。特别是,如果你处理像图像这样的数据集,那么有更有效的无监督降维方法,这些方法基于深度学习技术,在处理图像、音频或传感器数据等模态时效果更好,而PCA效果较差,尽管它仍然可以在图像中找到有趣的信号。此外,作为一种无监督方法,选择像P这样的超参数可能有点艺术性。

然而,尽管有这些限制,PCA是一个非常重

20.1:机器学习开发工作流程 🚀

在本节课中,我们将开始课程的一个新主题,重点介绍构建机器学习模型、理解其性能并以系统化、迭代的方式改进性能的技术。

概述 📋

作为我们即将探讨内容的动机,请考虑以下例子。假设你在一个数据集上训练了一个图像分类器,现在达到了80%的准确率。接下来该做什么?你可以尝试通过向算法添加更多数据来进一步提高准确率,或者可以检查算法是否得到了适当的优化,也可以尝试训练更长时间。你可以尝试使用更大的模型、更小的模型,可以进行正则化,可以添加特征。这些都是为了尝试提高准确率而可能做出的各种设计决策和改进。本课程这一部分的重点,将是关于如何优先处理这些决策并以系统化的方式改进机器学习模型性能的原则性方法。

机器学习开发工作流程 🔄

本讲座的第一部分将重点介绍一个特定的机器学习开发工作流程,我将对其进行介绍。这本质上是一种改进和迭代机器学习模型的系统化方法。我将首先对这个工作流程进行高层次概述,然后在本次讲座的剩余部分以及接下来的讲座中,我们将深入探讨具体细节,并审视这个工作流程中各个步骤的细节。

监督学习背景回顾 📊

在开始之前,让我们回顾一下在进行机器学习时所处的通用环境。在本讲座中,我将以监督学习作为贯穿始终的例子,但这些思想也可以扩展到无监督学习。回想一下,在监督学习中,我们假设处理的数据来自一个称为数据分布的概率分布,我们的数据集 D 是一组来自分布 P 的独立同分布样本。

泛化与评估 🎯

当我们构建机器学习模型时,特别是在监督学习中,我们通常在训练集上优化其性能。但正如我们在课程开始时所见,这并不是机器学习的真正目标。我们的机器学习算法只有在能够很好地泛化到新数据时才有效。为了评估我们的算法是否泛化良好,我们引入了“保留集”这个概念,它是一个来自相同分布但与训练集不同的数据集。如果我们正确地学习了数据中的信号,那么我们应该能够泛化到这个新数据集,并且在这个独立的保留集上也应该有很高的准确率。这就是我们如何知道是否成功训练了一个机器学习模型。因此,在本视频中,我将介绍一个工作流程算法,用于构建具有良好泛化能力并在机器学习算法的实际部署中表现良好的模型。

数据集划分:训练集、开发集与测试集 📁

在机器学习工作中,通常将数据划分为几个不同的子集。数据的主要且通常是最大的子集称为训练集,这是我们整个课程中一直使用的核心数据,我们在此之上训练算法、最小化目标函数。此外,通常还会使用一个称为开发集的数据集,也称为验证集或保留验证集。这是一个独立的数据集,用于调整算法,例如,我们可以用它来执行超参数选择,比如选择正则化器的最佳强度。因此,顾名思义,这是一个我们用于开发模型的数据集。最后,我们通常还有一个单独的测试集,用于在模型开发完成后评估其最终性能。

核心工作流程步骤 🛠️

这三个数据集定义了以下用于构建和迭代机器学习模型的工作流程:

  1. 训练模型:通常,当我们开始构建机器学习模型时,从一个初始想法开始,然后在训练集上拟合它。
  2. 评估开发集性能:然后,我们观察它在开发集上的性能,并使用指标来评估其在开发集上的表现。由于开发集与训练集不同,这让我们初步了解该模型在现实世界中的表现如何,是否过拟合或其性能是否足够。
  3. 模型选择与改进:基于观察到的性能,我们思考对模型的改进,执行所谓的“模型选择”。我们可能会对模型进行更改,例如,更改某些超参数(如正则化强度),或者尝试新的神经网络架构,或者向模型添加新特征。
  4. 迭代循环:根据第二步的性能,我们决定要进行哪些更改,然后实施这些更改,回到步骤1,重新训练模型,再次观察其在开发集上的性能。如果我们的更改成功,通常会保留它们,然后可以尝试其他想法,用这些新想法再次重新训练模型,只要模型在开发集上的性能在提高,我们就不断重复步骤1和2。
  5. 最终测试与部署:最后,一旦我们对模型的性能感到满意,就在测试集上评估它。如果对性能满意,就部署模型。模型在测试集上的性能是我们对模型部署到现实世界后预期表现的最准确估计。

这是一个非常高层次的工作流程。在本视频以及接下来的讲座中,我将介绍这个工作流程的细节,例如使用哪些指标,如何使用这些指标的结果来确定对模型进行哪些更改等。

关于工作流程的高级观察 👁️

我还想对这个流程提出几点高层次观察。

第一个观察是,这个过程本质上涉及试错。机器学习是一个领域,至少在应用方面,经常有很多试错。我们相信某些特征或架构更改是有用的,我们尝试它们,训练模型,根据结果观察其表现,我们可能对哪些更改成功、哪些需要不同更改有一些直觉,然后只要性能在提高,我们就执行这个试错过程。

我描述的这个工作流程有助于快速迭代模型。当你开始构建机器学习模型时,第一步是建立数据集(训练集、开发集、测试集),建立性能指标,然后运行这个工作流程,快速迭代模型的性能,开始朝着一个能在某种程度上解决问题的第一个原型努力,然后快速进行更改和改进。

如何选择开发集与测试集? 🤔

现在,在这个工作流程中,一个重要决策是如何选择开发集和测试集?

首先,让我们看看它们的目标。如前所述,开发集的目标从根本上说是检测模型性能的变化。我们使用开发集来测试我们对模型所做的各种更改是否提高了性能,以及我们是否在朝着改进系统的方向前进。

而测试集的目标是,在我们完成实验和模型迭代后,自信地估计现实世界的性能。因为我们反复选择在开发集上表现最好的模型,最终我们有过拟合开发集性能的风险。如果我们执行了数百次迭代,并且总是只选择在开发集上表现最好的模型,那么很可能我们会过于乐观,选择的模型在开发集上的表现会优于在整个数据分布上的表现。这就是为什么拥有一个独立的测试集很重要,因为一旦我们完成了所有更改,并且真正优化了模型以在开发集上表现良好,我们很可能已经略微过度优化了它,我们希望有一个对未来性能的独立、无偏估计。这就是我们拥有测试集的原因。我们在测试集上运行模型,现在最终知道一旦模型部署到现实世界,我们的性能水平是多少。实际上,这可能比我们在开发集上的性能略低。

为了能够对未来性能进行这种无偏且自信的估计,重要的是在迭代模型时将测试集放在一边,不去动它。如果我们也开始在测试集上迭代,或者过于频繁地查看测试集,那么我们也有风险对模型进行针对测试集优化的更改,因此测试集将不再是对未来性能的准确估计。这是一个重要的实践细节,是你在实践中构建机器学习系统时需要遵循的重要规则。

选择数据集的两个重要考虑因素 ⚖️

那么,我们如何选择这两个数据集呢?我想强调两个重要的考虑因素。

考虑因素一:代表性

第一个考虑因素是,我们从这些数据集中获得的未来性能估计,必须代表我们将在现实世界中看到的实际性能。因此,这些数据集中的数据应来自我们预期在生产中看到的相同数据分布。当我说“生产”时,我指的是在现实世界中,或者一旦我们部署了模型并且它在现实世界中做有用的事情时。我们将看到一些数据,这些数据的分布应尽可能接近我们开发和测试集中的数据。这样我们才能知道模型部署后将如何工作。

例如,考虑一个检测特定图像是否包含狗的图像分类器。如果我们想预测算法检测狗的效果如何,我们希望在我们的开发和测试集中有一组狗的图像。训练数据通常也来自相同的分布,但这并非严格必要。例如,我们可以在训练期间尝试用其他动物的额外图像来增强数据,这是一种数据增强形式,是训练机器学习模型的一种方式,我们可以用额外的输入来增强这些数据,以尝试提高性能。但我们最终感兴趣的是狗的分类准确率,这就是为什么我们的数据应该由狗的图片组成。

另一个例子是,如果我们有高分辨率或低分辨率图像,并且我们知道在现实世界中只会看到低分辨率图像,那么在测试集和开发集中包含这些图像就很重要。或者,如果我们看到来自不同地理区域的图像或输入,例如来自欧洲和北美,并且我们知道我们的系统将只在欧洲部署,那么测试集中应该只有欧洲的输入。但是,如果发现仅仅拥有更多数据有助于模型学习额外的信号,我们可以在训练集中添加一些北美输入,这在原则上可能有助于训练,尽管不能保证。

考虑因素二:数据集大小

另一个问题是这些数据集应该有多大。在经典机器学习中,曾经有一个规则,大约20%到30%的数据应保留给开发和测试集。在现代机器学习中,我们的数据集通常很大,例如,我们可能有数十万甚至数百万张图像的数据集。由于我们的最终目标是估计未来性能,我们不需要30%的数据,我们不需要30万张图像在测试集中。很可能我们可以用3000或5000个输入的测试集来估计未来性能。因此,我们实际需要的数据点数量主要取决于任务的复杂性,但最好将其视为一个绝对数字:什么数字能给我们足够的变异性?或者什么数字能足够大地减少这种估计的方差和不确定性?估计这一点的一种方法是,对大小为3000的各种输入进行子采样,观察它们之间的方差。如果大小为3000的各种子集给出相似的性能,那么大小为3000的随机子集很可能就是未来性能的一个稳定估计。

模型选择中的实践考虑 💡

以上是关于如何选择我们使用的数据集的讨论。我还想强调在执行模型选择时的两个实践考虑,即在模型选择步骤决定对模型进行哪些更改时。

考虑因素一:定义优化指标

作为这个工作流程的一部分,需要做出的关键决策是我们要优化哪些指标。为了以系统化的方式改进机器学习模型,我们需要一个概念,需要一个准确且量化的概念,来定义什么是好的机器学习模型,什么是坏的机器学习模型。这可能意味着像准确率这样简单的东西,也可能意味着更复杂的东西。例如,如果我们在一个敏感的医疗应用中,并且对假阳性或假阴性的数量有严格的阈值,那么这也可以作为我们指标的一部分。例如,如果我们正在做一个信息检索系统,那么精确率和召回率可能是有用的指标(我们稍后会看到什么是精确率和召回率)。为了以系统化的方式迭代模型,我们需要以某种方式将所有考虑因素、所有我们拥有的指标组合成一个单一的数字,一个单一的主指标。这只是为了让我们能够确定某个特定更改是否有用,这是一个二元决策,因此必须根据某个单一的量化度量来进行。如果我们有多个感兴趣的指标,例如,如果我们对精确率和召回率感兴趣,我们可以通过取它们的平均值或某种组合将它们合并成一个单一指标。在这个例子中,结合精确率和召回率会得到所谓的 F 分数。或者,如果我们有其他指标,比如计算性能可能是一个,假阳性数量可能是另一个,我们也可以将这些作为约束条件。如果一个更改违反了我们对最差计算复杂度的容忍度,那么我们就不对模型进行这个更改。但再次强调,为了能够执行这种系统化的迭代,拥有一个衡量分类器好坏的单一度量标准至关重要,这样我们才能在每一步、每次在开发集上评估一个新想法时,能够接受或拒绝这个想法。这需要一个性能的量化度量,我们可以说它相对于之前是否更好。

考虑因素二:如何更新模型

最后一个问题是,我们如何更新模型?我们将更详细地探讨这一点,但本质上,一旦我们观察了性能,我们就想决定是保留这个模型还是进行更改。这可以基于我们的直觉。例如,如果我们注意到模型过拟合了,那么我们知道需要更多地正则化它,这将指导我们在下一次迭代中尝试的新想法类型。或者,我们可以遵循一种系统化的方法,执行某种网格搜索,甚至还有来自名为“贝叶斯优化”等领域的更专门的算法可以用于此过程。我们稍后将对此提供更多直觉。

工作流程示例:训练神经网络 🧠

最后,我想用一个例子来总结。想象一下,我们正在训练一个神经网络,并且希望将这种工作流程应用于训练神经网络。

假设我们最初想进行图像分类。我们从卷积神经网络开始,在开发集上获得了90%的准确率。然后,我们可以观察其性能,发现它有点过拟合数据。于是,我们可以对Dropout层的强度进行网格搜索,看看是否能使其性能更高一些。然后我们发现,使用不同的值,我们可以获得95%的准确率,这就是我们下一个最佳模型。然后,我们甚至可以尝试一个全新的想法,例如在网络内部添加残差连接等新的架构元素。现在我们正在改变架构。假设我们这样做了,现在在开发集上获得了更大的性能提升。我们在训练集上重新训练新架构,在开发集上评估,现在获得了99%的性能,这更好了。现在我们非常满意,并在测试集上估计这个性能,现在性能略低,为97%,但仍然相当不错。但这表明我们略微过拟合了开发集的性能,因为我们只选择了在开发集上表现良好的更改。97%仍然很好,现在我们可以将模型部署到生产中,并且可以断言,一旦开始部署到真实图像上,这个模型的性能很可能在97%左右。

工作流程的局限性与总结 📝

最后,整个工作流程并非没有一些局限性。如前所述,过拟合开发集是一个我们可能遇到的问题。此外,在现实世界中,一旦模型运行并接收真实输入,用户提供给模型的输入类型往往会发生变化。例如,如果我们将这个模型部署到欧洲和北美地区,随着时间的推移,我们可能会有更多来自欧洲的用户,他们看起来与北美用户不同,因此我们在生产中看到的分布可能会发生变化。同时,我们可能在某些时候意识到,我们一直在优化的指标并不是用户真正关心的,我们需要进一步改变它。因此,在这些情况下,我们需要回过头来,对我们的流程进行更改。如果我们意识到这不是我们关心的,我们可能想要更改指标;如果我们过拟合了它们,或者它们不再代表整体分布,我们可能需要定期更新我们的开发和测试集。这些都是你应该意识到的局限性。

接下来,我们将更具体地探讨如何在这个工作流程中评估机器学习系统的性能。

在本节课中,我们一起学习了机器学习开发的核心工作流程,包括数据集划分(训练集、开发集、测试集)、迭代改进模型的步骤、选择开发集与测试集的原则,以及模型选择中的关键考虑因素。这个系统化的方法旨在帮助我们高效地构建和优化能够良好泛化的机器学习模型。

20.2:第二部分:评估分类模型 📊

在本节课中,我们将学习如何评估机器学习模型。在上一节视频中,我们介绍了一个以原则性方式构建、迭代和改进机器学习模型的工作流程。该流程的一个关键步骤是理解和量化我们当前模型的性能,以及模型下一次迭代的表现。本节视频将定义一组适用于许多机器学习算法的重要指标,这些指标在整个机器学习领域中被广泛使用,并将作为量化我们性能的准确方法。我们将从分类问题开始。

分类问题回顾 📈

分类是监督学习的一种形式,其中我们的目标变量 y 是离散的,它取 K 个可能值中的一个,其中 K 是类别数量。如果 K=2,我们有一个特殊情况,称为二分类,此时我们只有两个类别。

一个分类任务的例子是我们在本课程中一直使用的鸢尾花数据集。在本视频中,我将创建一个此数据集的略微修改版本,其中一个类别的样本数量较少。通常,这个数据集有三个类别,每个类别有50个样本,但现在我只取120个样本:前两个类别各取50个,第三个类别取20个,从而使这个数据人为地变得不平衡。此外,我将使用一个函数将数据集分成两个子集:第一个子集记为 X,作为我们的标准训练集;另一个包含50个样本的数据集将作为我们的保留集。这个分割是随机进行的。

我们可以像之前一样在二维空间中可视化这个数据集,其中每种颜色对应三个不同类别中的一个。你可以看到棕色类别的点比其他类别的点少,这同样是因为我对这个数据集进行了子采样。此外,对于本视频将要进行的分析,我将只关注图中显示的以下两个特征,因此所有点都将是二维的。

模型训练与预测 🚀

由于这是监督学习,我们处理的是具有特定形式的机器学习模型:我们有一个输入 x,我们的模型 fx 转换为目标 y

我们可以在刚刚创建的数据集上训练一个这种形式的模型。这里我将拟合一个 softmax 模型,它在 scikit-learn 中通过 LogisticRegression 类实现。我在这里实例化这个模型,然后在训练集上拟合它,接着在我们的保留集上生成预测。这组预测将存储在向量 y_pred 中。本视频中将要介绍的指标将量化这个向量 y_pred 的准确性、性能和质量。

准确率:最简单的性能度量 ✅

我们可以使用的最简单的性能度量是分类器的准确率,它简单地表示我们预测正确标签的点的百分比。当准确率达到100%时,我们完全准确;为0%时,我们完全不准确。在这个特定数据上,我们可以轻松测量准确率,只需一行 NumPy 代码即可完成。我们在这个数据上的准确率是 84%

请注意,我们是在保留集上评估准确率,因为这是实际用于估计未来性能的预测集。此外,如果我们在进行模型迭代或模型选择,尝试不同的模型思路,我们希望使用这个保留集(或称为开发集)的准确率来指导我们的模型设计。

混淆矩阵:更详细的性能视图 📊

准确率并不是查看分类器性能的唯一方式。另一个有用的工具是混淆矩阵。混淆矩阵是一个简单的表格:如果我们有 K 个类别,那么我们有 K 行和 K 列。我将用变量 y_j 索引行(y轴),用变量 ŷ_k 索引列(x轴)。位于第 y_j 行、第 ŷ_k 列的单元格,包含了真实类别为 y_j 但被分类为 ŷ_k 的样本数量。

让我给你一个具体的例子。这里我使用 scikit-learn 中的 plot_confusion_matrix 函数为鸢尾花数据集的保留集生成混淆矩阵。如前所述,因为我们有 K=3 个类别,所以我们有三行三列。每列由类别索引 0、1 或 2 索引,行也是如此。如果我查看位于 (0, 0) 的单元格,这是真实类别为 0 且被正确分配为类别 0 的点数,我有 19 个这样的点。

在单元格 (1, 1) 中,我有真实标签为 1 且预测标签也为 1 的点数,即它们被正确分类,我有 22 个这样的点。但是,我还有另外两个类别 1 的点:一个被错误地分配了标签 0,另一个被错误地分配了标签 2。所以,在总共 24 个点中,22 个被正确分类,2 个被错误分类。对于类别 0,我们总共有 19 个点,它们都被正确分类。所以,我们对这两个类别的分类效果非常好。

但是,如果我们看第三个类别(标签为 2 的类别),那么总共有 7 个点,但其中 6 个被错误分类,它们被错误地分配了标签 1。因此,实际上,尽管我们在这个数据集上的总体准确率是相当可观的 84%,但我们几乎完全错误分类了所有属于类别 2 的标签。这是通过查看混淆矩阵可以获得的一个重要附加信息。

二分类中的关键概念与指标 🔑

混淆矩阵还可以用来推导几个重要的概念、定义和用于衡量分类器性能的指标。首先在二分类的背景下定义它们会最容易。

在二分类中,我们的混淆矩阵只是一个 2x2 的表格。这里,我们的两行是两个可能的类别,列也对应两个类别。由于是二分类,我随机地将两个类别中的一个标记为正类(y=1),另一个标记为负类(y=0)。然后,在正类中,在真正为正类的点中,有一部分被正确分类为正类,这些是真正例,它们的数量在这个单元格中。但有一部分被错误地误分类为负类,因此它们的数量在假反例单元格中,它们被称为假反例。类似地,对于另一个类别,我们有假正例真反例

这些术语——真正例、假正例、假反例——在整个机器学习和统计学中都非常重要,了解它们是什么很重要,通过查看这个简单的 2x2 混淆矩阵很容易理解它们。

我们也可以使用这些术语来定义我们之前看到的准确率。准确率就是正确预测的数量除以总预测的数量。正确预测的数量是真正例和真反例的数量,除以所有预测的数量(即所有单元格中的数字之和)。这就是我们之前定义的准确率。

敏感度与特异度:更精确的度量 🎯

现在,我们可以定义更精确的准确率概念,称为敏感度特异度。在许多应用中,例如在医学应用中,我们也希望衡量并在每个类别上达到最低的性能水平。我们希望正例和负例都准确。这就是敏感度和特异度所捕捉的。

敏感度,也称为召回率真正例率,你可以将其视为正类上的“准确率”。因此,分子是真正例的数量,分母是正类点的数量,即真正例的数量加上假反例的数量。

类似地,特异度是负类上的“准确率”,即真反例的数量除以负类点的数量,负类点的数量是真反例的数量加上假正例的数量。

敏感度和特异度在机器学习和统计学中经常用于衡量二分类器的性能,理解和掌握这些概念非常重要。

平衡准确率:综合敏感度与特异度 ⚖️

敏感度和特异度之所以有用,其中一个原因是,尤其是在类别不平衡的情况下,通常可能具有很高的特异度,但敏感度却很差。我们甚至可能具有很高的准确度和很高的特异度,但敏感度可能很低,反之亦然。因此,同时查看两者很重要。我们也可以查看一个将它们结合起来的单一度量,称为平衡准确率,它简单地取两者的平均值。这个度量对于检测类别不平衡以及其中一个度量表现不佳的情况很有用。

精确率与召回率:关注正类预测 🔍

另一组与敏感度和特异度类似但不完全相同的指标是精确率召回率

精确率,也称为阳性预测值,是真正例的数量占我们预测为正类的所有点的比例。我们预测为正类的点的数量是真正例加上假正例,这些是我们分配了正类标签的所有点,它们位于这一列中。

召回率与敏感度是同一个概念,即被正确识别的正类点的数量。

这两个度量在机器学习的应用中很有用,例如在信息检索中。例如,我们正在构建一个搜索引擎,我们想知道在所有相关结果(正类)中,我们分类为相关的结果有多少是真正相关的,这就是精确率;在所有存在的相关结果中,我们捕捉到了多少,这就是召回率。这就是一个可以思考精确率和召回率的应用场景。同样,如果我们构建一个搜索引擎,正例是相关的网页,那么精确率关注的是搜索引擎返回的所有网页中有多少是真正相关的,而召回率衡量的是我们找到了多少相关网页。

一个重要说明是,这并没有真正报告任何关于负类的性能。它只报告了关于正类预测的性能,我们没有量化我们在负例上的准确性。我们不这样做的原因是,在诸如搜索引擎的应用中,我们假设存在一个非常大的负类点集合,但我们并不真正关心我们是否将这些点识别为负类。我们只关心返回的点,这是用户唯一看到的东西,我们希望在这方面看起来准确。负类点,搜索引擎的用户永远不会看到它们,因此,将我们的分析重点放在这些点上就不那么重要了。

因此,精确率和召回率在此类应用中很有用。这也为我们在何时可能希望使用精确率和召回率,而不是敏感度和特异度提供了直觉。我想指出,如果我们有完美的精确率和召回率,我们也将有完美的敏感度和特异度,反之亦然。所以,这些度量并非完全不同,它们都衡量分类器的性能,但它们往往比其他度量更能揭示某些特性。例如,如果我们处于一个不关心真反例的环境中,比如在搜索引擎中,我们只关心识别正确的正例而不遗漏任何正例,那么精确率和召回率更有用。但是,例如,在医学环境中,我们正在检测一个人是否患有某种疾病,那么我们希望在正例(我们预测有疾病或疾病真正存在的例子)上报告我们的性能,同时也在没有疾病的患者上报告性能,我们也希望正确地告诉他们他们没有患病。因此,如果这两个值都很重要,那么通过查看特异度和敏感度,我们可以更清晰、更直观地了解我们系统的性能。

F1 分数:结合精确率与召回率 📊

最后,可以将这些度量结合成一个单一的度量,称为 F 分数。F 分数定义为精确率和召回率的几何平均值。几何平均值使用这个公式定义:我们不取算术平均值,而是查看精确率和召回率的倒数,然后应用这个公式。它的特性是结合了它们,并且有用,因为首先,如果我们有完美的精确率和完美的召回率,它等于 1,这正是我们想要的;其次,如果精确率或召回率为 0,它也等于 0。这比算术平均值更有用,因为即使召回率为 0,算术平均值仍然可能非零。因此,在极端情况下,它能更好地捕捉我们期望的行为。

总结 📝

在本节课中,我们一起学习了评估分类模型性能的关键指标。我们从最简单的准确率开始,然后介绍了混淆矩阵,它提供了更详细的性能视图。基于混淆矩阵,我们定义了二分类中的核心概念:真正例假正例真反例假反例

接着,我们探讨了更精确的度量:敏感度(召回率)和特异度,它们分别衡量模型在正类和负类上的表现。我们还介绍了平衡准确率,用于综合评估类别不平衡时的性能。

然后,我们学习了精确率召回率,这两个指标特别关注正类预测的质量,常用于信息检索等场景。最后,我们了解了如何通过 F1 分数(精确率和召回率的调和平均数)将精确率和召回率结合成一个单一的综合指标。

这些指标是评估分类算法性能的基础工具,理解它们对于构建和迭代有效的机器学习模型至关重要。在下一节视频中,我们将探讨一些更高级的分类评估指标。

20.3:高级分类指标 📊

在本节课中,我们将继续讨论分类指标,并学习一些更高级的指标。我们将重点介绍如何通过调整阈值来平衡敏感性和特异性,以及如何使用ROC曲线和AUC来评估分类器的性能。此外,我们还将探讨在多类分类问题中如何计算和平均这些指标。


分类回顾

上一节我们介绍了分类的基本概念,特别是二分类问题中的敏感性和特异性。本节中,我们将看看如何通过调整分类器的置信度阈值来平衡这两个指标。

分类是一种监督学习形式,其中目标变量Y取离散的类别值,类别数量为K。当K=2时,我们称之为二分类。

在之前的视频中,我们定义了二分类中的两个基本指标:敏感性和特异性。敏感性可以理解为在真实类别为正的样本上的准确率,而特异性则是在真实类别为负的样本上的准确率。

敏感性公式
[
\text{Sensitivity} = \frac{\text{True Positives}}{\text{True Positives} + \text{False Negatives}}
]

特异性公式
[
\text{Specificity} = \frac{\text{True Negatives}}{\text{True Negatives} + \text{False Positives}}
]

在许多应用中,我们可能需要在敏感性和特异性之间进行权衡。例如,在诊断某种重要疾病时,我们可能希望尽可能捕捉到所有阳性病例,即使这意味着会产生一些假阳性。


通过阈值调整进行权衡

一种实现敏感性和特异性权衡的方法是,仅在我们模型非常确信的情况下才将样本预测为正类。通过提高预测为正类的置信度阈值,我们可以提高在正类样本上的准确性(即敏感性),但可能会降低在负类样本上的性能(即特异性)。

大多数机器学习分类器都允许我们这样做,因为它们会输出置信度分数或与置信度相关的分数。例如,在我们训练过的IIS数据集的softmax模型中,模型会输出每个类别的概率,这些概率可以作为置信度度量。

以下是使用Python代码获取预测概率的示例:

# 假设 model 是已训练好的分类器,X_holdout 是测试集
prediction_probabilities = model.predict_proba(X_holdout)
# 获取第一个样本属于类别0的概率
prob_class_0 = prediction_probabilities[0, 0]

我们可以通过设置不同的阈值T来将这些概率转换为实际预测。例如,如果我们希望检测所有可能的类别0实例,可以将阈值设得较低(如25%)。相反,如果我们希望预测非常准确,可以将阈值设得较高(如90%)。


ROC曲线与AUC

为了更精确地量化敏感性和特异性之间的权衡,我们使用接收者操作特征曲线(ROC曲线)。ROC曲线通过可视化不同阈值下的真阳性率(TPR,即敏感性)和假阳性率(FPR,即1-特异性)来展示这种权衡。

真阳性率(TPR)公式
[
\text{TPR} = \frac{\text{True Positives}}{\text{True Positives} + \text{False Negatives}}
]

假阳性率(FPR)公式
[
\text{FPR} = \frac{\text{False Positives}}{\text{False Positives} + \text{True Negatives}}
]

以下是绘制ROC曲线的步骤:

  1. 计算每个样本属于特定类别(如类别2)的概率。
  2. 选择一系列阈值T。
  3. 对于每个阈值,将概率大于T的样本预测为该类别。
  4. 计算每个阈值下的TPR和FPR。
  5. 以FPR为横轴,TPR为纵轴绘制曲线。

在ROC曲线图中:

  • 左下角 (0,0):对应最严格的阈值,不预测任何样本为正类。
  • 右上角 (1,1):对应最宽松的阈值,将所有样本预测为正类。
  • 左上角 (0,1):是理想点,表示所有正类样本都被正确分类,且没有负类样本被错误分类。
  • 对角线:表示随机猜测分类器的性能。

曲线越接近左上角,分类器的性能越好。

我们还可以将ROC曲线的性能汇总为单一指标,即曲线下面积(AUC)。AUC值越接近1,表示分类器性能越好;随机分类器的AUC为0.5。

在Scikit-learn中,可以使用以下代码计算AUC:

from sklearn.metrics import roc_curve, auc

# 计算属于类别2的概率
probs_class_2 = prediction_probabilities[:, 2]
# 计算ROC曲线
fpr, tpr, thresholds = roc_curve(y_true, probs_class_2)
# 计算AUC
roc_auc = auc(fpr, tpr)

多类分类中的指标

以上讨论主要针对单个类别(二分类或一对多)。在多类分类中,我们也可以为每个类别计算敏感性和特异性。具体方法是:将当前类别视为“正类”,将所有其他类别合并视为“负类”,从而为每个类别构建一个二分类混淆矩阵,并计算相应的指标。

计算完每个类别的指标(如精确率、召回率)后,我们需要将它们合并为一个整体评估指标。主要有两种平均方法:

以下是两种主要的平均方法:

  • 宏平均(Macro-averaging):平等对待每个类别,直接计算所有类别指标的平均值。
    • 宏精确率公式
      [
      \text{Macro Precision} = \frac{1}{K} \sum_{k=1}^{K} \text{Precision}_k
      ]
  • 微平均(Micro-averaging):平等对待每个样本,通过汇总所有类别的真阳性、假阳性等计数来计算全局指标。
    • 微精确率公式
      [
      \text{Micro Precision} = \frac{\sum_{k=1}^{K} \text{TP}k}{\sum^{K} (\text{TP}_k + \text{FP}_k)}
      ]

此外,还有加权平均,它是宏平均的一种变体,根据每个类别的样本数量进行加权。

在Scikit-learn中,可以方便地使用classification_report函数查看每个类别的精确率、召回率以及宏平均和加权平均结果。

from sklearn.metrics import classification_report
print(classification_report(y_true, y_pred))

该报告能帮助我们识别模型在哪些特定类别上表现不佳。例如,如果某个类别的召回率很低,说明模型很难正确识别该类的样本。


总结

本节课中,我们一起学习了高级分类指标。我们首先回顾了如何通过调整置信度阈值来权衡敏感性和特异性。接着,我们深入探讨了ROC曲线和AUC,这两个工具能帮助我们可视化并量化分类器在不同阈值下的性能。最后,我们介绍了如何将二分类指标扩展到多类分类问题,并讲解了宏平均和微平均这两种汇总方法。

掌握这些指标对于全面评估和比较不同分类模型的性能至关重要。在下一讲中,我们将把注意力转向回归问题的评估指标。

20.4:第4部分:回归模型评估指标 📊

在本节课中,我们将学习如何评估回归模型的性能。我们将介绍几种常用的回归评估指标,并讨论它们的优缺点,帮助你理解如何选择和使用这些指标来衡量模型的预测效果。


回归问题回顾

上一节我们介绍了回归模型的基本概念。本节中,我们来看看如何量化回归模型的性能。

回归是监督学习的一种形式,其目标变量 Y 是连续的,而非离散的。我们关注的是量化回归模型的性能,这些模型是将输入 X 映射到目标 y 的函数。


常用回归损失函数

以下是两种最常见的回归损失函数,它们既可作为训练目标,也可作为评估指标。

  1. 均方误差:计算预测值与真实值之间差值的平方的平均值。
    公式MSE = (1/n) * Σ(y_i - ŷ_i)^2

  2. 平均绝对误差:计算预测值与真实值之间差值的绝对值的平均值。
    公式MAE = (1/n) * Σ|y_i - ŷ_i|

这些指标可以直接计算,但存在一些局限性。


标准损失函数的局限性

使用上述指标存在两个主要问题。

  1. 未考虑目标变量的尺度:例如,误差为5,对于真实值为10的目标来说很大(误差率50%),但对于真实值为100的目标来说很小(误差率5%)。同一数据集内,不同尺度的目标会使相同误差值代表不同的含义。

  1. 可解释性差:很难直观判断一个误差值(如MAE=10)是好是坏。此外,不同指标(如MSE和MAE)给出的数值不同,难以直接比较优劣。

因此,这些简单的回归指标不如分类中的准确率那样直观易懂。接下来,我们将探讨解决这些问题的几种方法。


处理尺度问题的方法

当数据尺度差异较大时,可以使用以下方法调整评估指标。

1. 比例损失

比例损失通过除以目标值来标准化误差,使其成为相对误差。

  • 平均绝对百分比误差:计算绝对误差相对于真实值的百分比。
    公式MAPE = (1/n) * Σ|(y_i - ŷ_i) / y_i| * 100%
    注意:当真实值 y 接近或等于0时,此公式可能不稳定(分母过小)。

  • 对称平均绝对百分比误差:为了缓解分母过小的问题,使用预测值和真实值的平均值作为分母。
    公式sMAPE = (1/n) * Σ|y_i - ŷ_i| / ((|y_i| + |ŷ_i|)/2) * 100%

2. 对数损失

另一种方法是先对目标值取对数,将数据转换到相近的尺度,再计算误差。

  • 均方根对数误差:一种常见形式,计算对数变换后的均方误差。
    公式RMSLE = sqrt( (1/n) * Σ(log(1 + y_i) - log(1 + ŷ_i))^2 )
    注意:公式中的 +1 是为了防止当 y 接近0时,对数函数趋向负无穷。

决定系数

最后,我们介绍一个非常有用且可解释性强的指标:决定系数。

决定系数衡量的是模型预测相对于简单基准模型(即始终预测目标平均值)的改进程度。

公式R² = 1 - (Σ(y_i - ŷ_i)^2) / (Σ(y_i - ȳ)^2)
其中,ȳ 是目标变量 y 的平均值。

  • R² = 1:表示模型完美预测了所有数据点(误差为0)。
  • R² = 0:表示模型的预测效果与仅预测平均值相当。
  • R² < 0:表示模型的预测效果比仅预测平均值还要差(通常意味着模型存在严重问题)。

R²的值通常在0到1之间,值越接近1,说明模型相对于简单基准的改进越大,性能越好。这为我们提供了一个清晰的、基于基准比较的性能评估框架。


总结

本节课中,我们一起学习了评估回归模型性能的多种指标。

  • 我们回顾了均方误差平均绝对误差这两个基础但存在尺度与解释性问题的指标。
  • 为了解决尺度问题,我们介绍了比例损失对数损失
  • 最后,我们重点讲解了决定系数,它是一个将模型性能与简单常数预测基准进行比较的、高度可解释的指标。

理解这些指标的特点和适用场景,对于正确评估和比较不同回归模型至关重要。如需了解更多回归评估指标,建议查阅相关机器学习库(如Scikit-learn)的官方文档。

21.1:第1部分:误差分析 📊

在本节课中,我们将继续上一讲的主题,探讨如何在现实世界中应用机器学习,以及有哪些核心技术和策略可以提升机器学习模型的性能。本节课的重点是模型的迭代与改进,即如何高效地对机器学习模型进行修改以提升其表现。

概述

想象一下,你训练了一个机器学习分类器,它在某个数据集上达到了80%的准确率。接下来你该做什么?你可以尝试添加更多数据、扩大模型规模、增加特征,或者添加正则化。有太多不同的改进思路。本节课的目标就是介绍一些原则性的方法和技术,帮助你做出决策,并优先处理那些能最有效提升模型性能的想法。

贯穿本节课的一个核心主题是快速、数据驱动的模型迭代。这里的数据驱动是关键。在许多机器学习应用中,我们并非领域专家,因此不能仅凭直觉来构建一个好的系统。我们必须依赖一种原则性的方法:详细检查模型在数据上的表现,分析其产生的错误和评估指标,然后基于这些观察结果,快速更新模型,并实施有针对性的改进。通过这种方式,我们可以快速迭代出多个模型版本,系统地提升性能。

机器学习模型开发流程回顾 🔄

在上一讲中,我们定义了开发机器学习模型的高级工作流程,它包含三个步骤:

  1. 训练阶段:从当前最佳模型猜想开始,在训练集上拟合模型。
  2. 评估与迭代阶段:在一个独立的开发集上评估模型性能,使用一组评估指标。根据观察到的性能和指标,提出一系列改进想法,并生成模型的新版本。然后重复训练和评估步骤,直到对模型性能满意为止。
  3. 最终测试阶段:在一个与训练集和开发集都不同的测试集上评估模型性能,这将是模型部署到实际用户后性能的最佳估计。

本节课的重点是模型选择步骤,即我们如何观察和衡量模型在开发集上的性能,以及基于这些观察结果,我们应该对模型做出哪些改变。

为什么需要数据驱动的决策? 🤔

为了说明这一点的重要性,我们来看一个例子。假设你训练了一个动物图片分类器。你创建了第一个版本,然后从用户那里听到一些反馈,说有些狗被错误地分类为猫。你有一个选择:可以集成一个商业的狗分类器。你可能想创建一个模型集成,在网上寻找最好的狗分类器,然后将其与你自己的模型集成以提高准确率。你估计完成这项工作大约需要一个月的时间。

那么,你应该推进这个集成工作吗?你应该花这一个月时间来提升模型在狗图片上的准确率吗?做出这个决策的关键在于数据驱动,即以系统、客观的方式检查模型的实际性能。

误差分析:系统识别常见错误 🎯

具体来说,最好的决策方式是:从开发集中随机抽取一组样本(例如100个样本),然后统计模型在这些样本上犯错的次数。接着,查看这些错误样本,看看有多少属于你试图修复的问题(例如,将狗误分类为猫)。如果只有5%的错误样本存在这个问题,那么修复它可能不那么紧迫;但如果50%的错误样本都是这个问题,那么这似乎是一个应该优先处理并修复的错误。

这个例子展示了一个通用过程,称为误差分析。其核心思想是系统地识别模型最常见的错误类型,以便我们能够优先修复最常见和最重要的错误

本质上,误差分析的工作流程如下:

  1. 训练一个初始模型。
  2. 在开发集上运行该模型,并收集模型出错的随机样本子集(例如分类错误的样本)。
  3. 手动检查这些错误,尝试识别其中的模式和常见的错误类别。
  4. 一旦对常见错误类型有了概念,就统计每个错误类别下有多少个开发集样本。
  5. 根据这些统计结果,优先处理最重要的错误。在决策时,还应结合其他信息,例如修复每种错误所需的时间。

误差分析实例:图像分类器 📸

假设你在训练一个图像分类器。你训练了模型,在开发集上运行,然后随机抽取了一组错误样本。在查看了前20或30个错误后,你发现这些错误要么是因为图像模糊,要么是因为图像被翻转了,或者有时是数据标签本身标错了(实际上你的系统预测正确,但开发集的标签是错的)。

于是,这些发现就成为了你的错误类别。在检查了初始样本后,你看到了不同的错误类型。

接下来,你可以遍历整个随机错误样本子集,并使用一个类似下表的工具将它们归类:

图像编号 模糊 翻转 标签错误
1 X X
2 X
3 X
... ... ... ...

在这个例子中,第三张图像被“误分类”是因为开发集的标签本身有误。第二张图像因为被翻转而导致模型无法准确分类。第一张图像同时受到两种错误类型的影响:既模糊又被翻转。

为所有图像完成分类后,我可以计算模糊、翻转和标签错误各自所占的百分比。在这个例子中,我发现一半的图像错误是因为我们向模型输入了翻转的图像,而模型没有经过适当的调整来处理这类图像。这表明,修复翻转错误是我们当前最应该优先投入时间的地方

例如,我们可以通过将训练集中的每张图像都创建一份翻转副本,并用相同的标签训练模型,以此来修复翻转错误。同时,注意到有相当大比例(30%)的样本存在标签错误,这也提示我们应该花时间清理数据,修复已发现的标签问题。

误差分析的注意事项 ⚠️

误差分析也对开发集的大小提出了一个特定的下限要求。为了使误差分析有用,我们需要看到足够多的模型错误类型。理想情况下,我们希望至少有50到100个错误样本(如果能更多则更好)。为了观察到这么多错误,开发集必须足够大。具体来说,如果模型的错误率为 ε(即模型出错的概率为 ε),那么我们希望开发集的大小约为 100 / ε。例如,如果模型有5%的错误率,为了看到大约100个错误,我们需要一个大约2000个样本的开发集。

最后,关于开发集的一个重要注意事项是:通过修复开发集上的错误,你很容易对开发集产生过拟合。因此,定期更新开发集以最小化这种形式的过拟合非常重要。

在训练集还是开发集上进行误差分析? 📊

另一个需要考虑的问题是,误差分析应该在开发集上进行,还是在训练集上进行?根据之前的定义,我们应该使用开发集。这是正确的,因为我们最终希望了解系统部署到生产环境后可能出现的错误。如果我们正确构建了开发集,它将与生产环境中的数据来自同一分布,并且是对部署系统将看到的错误的最佳估计。

然而,如果模型在训练集上犯错,这也是一个问题。因为如果它在训练集上犯错,那么它在开发集或生产环境中也很可能犯错。因此,为了修复我们的模型,我们也需要修复训练集上的错误。在大多数情况下,如果不修复训练集上的错误,我们同样无法修复生产环境中的错误。

因此,正确的方法是两种类型的误差分析都进行,具体取决于我们更关注修复开发集还是训练集上的问题。两者都很重要,各有其用途。

实战示例:手写数字识别 🔢

现在,让我们看一个在真实数据集上进行误差分析的最后示例。我们将使用Scikit-learn中的digits数据集(MNIST数字数据集的缩小版)。

首先,加载数据并将其可视化。我们可以看到每个数字的一个样本。

接着,将数据随机分成两个大小相等的子集:训练集和开发集。

给定训练集,我们面临一个多分类问题。我们将训练一个softmax分类器(在Scikit-learn中通过LogisticRegression类实现)。实例化该类,在训练集上拟合模型,并在开发集上生成预测。

检查准确率后,我们发现模型在开发集上的准确率约为93%。这个结果不错,但并不完美。那么,为什么我们没有达到完美的准确率?我们该如何改进?

首先,我假设某些数字比其他数字更容易被误分类。这是一种利用先验知识来定义错误类别的方法。

我们可以定量地检查数据。这里,我们查看模型出错的点及其标签,并绘制这些标签的直方图。我发现直方图非常不均匀。正如预期的那样,有些标签被正确分类的频率更高,而有些则更容易被误分类。特别是,错误最多的数字是3和7。

这已经是很有用的信息,并且开始看起来像一组错误类别。事实上,你甚至可以将这些视为一种自动生成的错误类别,而无需手动检查数据。

让我们看看这里最常见的错误类型:误分类数字3的情况。

我们可以通过显示被误分类的“3”的子集,并将其与正确分类的“3”进行比较,来进一步调查这个问题。

执行此操作的代码会生成两行图像:顶部一行是被误分类的“3”,底部一行是被正确分类的“3”。

你可以立即在这些结果中看到一个模式:底部的“3”更平滑,看起来像是书写工整的“3”;而顶部的“3”看起来更像是涂鸦。直观上,模型在分类那些看起来“更差”的“3”时遇到了更多困难。例如,其中一个看起来非常接近“7”,实际上模型在这个案例中预测它就是“7”。

通过检查这些错误,我们识别出了一种特定的错误类型:存在某种书写“3”的风格会导致模型出错。如果我们想进一步调查,可以更深入地探究为什么模型会对这些特定的“3”犯错误,并提出修复方案,或者为模型提供更多此类“3”的示例。虽然有很多不同的解决方案,但我们已经确定这个错误对于当前模型和数据来说尤为重要。

误差分析的局限性与总结 📝

以上就是误差分析的最后一个示例。在结束之前,我也想指出误差分析的一些局限性。

正如前面提到的,在进行误差分析时,很容易对开发集产生过拟合。因为每一步,每当你尝试一个新模型时,你都在详细检查,并花费大量精力,根据开发集上的具体错误来指导所有更改。结果,你很快就会得到一个在开发集上表现非常好的模型,但你只会意识到该数据集上的错误,因此容易对其过拟合。

此外,误差分析是一个需要投入工作的过程,它很繁琐,但尽管如此,它仍然非常重要。

最后,对误差分析的一个更高层次的批评是,它关注非常具体、细微的错误(例如那些被误分类的“3”),但有时可能会忽略全局。例如,我们可能正在以一种非常糟糕的方式对数据过拟合,而仅仅查看非常微小的细节可能并不明显。

因此,某些更大的趋势可能会被遗漏。这也将促使我们讨论本讲其余部分将介绍的其他类型的分析。

总结

在本节课中,我们一起学习了误差分析这一核心工具。我们了解到,通过系统地收集、检查和分类模型在开发集或训练集上产生的错误,可以数据驱动地识别出最常见和最重要的问题。这帮助我们优先分配开发资源,高效地迭代和改进模型。我们通过图像分类和手写数字识别的实例,演示了误差分析的实际应用步骤。同时,我们也认识到误差分析存在过拟合开发集、过程繁琐以及可能忽略宏观趋势等局限性,这为后续学习其他分析方法奠定了基础。记住,从快速构建一个端到端的初始原型开始,然后通过快速观察和修复实际错误,是高效开发优秀机器学习系统的关键。

21.2:第2部分:偏差与方差分析 📊

在本节课中,我们将学习一种用于理解和诊断机器学习模型性能的重要分析方法——偏差与方差分析。我们将探讨这两种误差的来源、如何量化它们,以及如何根据分析结果来改进模型。

概述

偏差与方差分析的核心思想是识别机器学习系统可能存在的两类问题:偏差方差。通过理解这两种误差的重要性,我们可以指导对模型进行有针对性的改进。

偏差与方差:两种核心问题

在之前的课程中,我们识别了影响机器学习模型(包括分类、回归甚至无监督学习)的两个重要问题。

第一个问题是过拟合。当我们提到“方差”时,我们指的是过拟合问题。方差问题是过拟合问题的同义词。这是一个非常常见的故障模式,通常发生在我们使用一个表达能力非常强的模型,但数据集太小(或至少对于该模型来说太小)的情况下。因此,表达能力强的模型会“记住”数据,在训练集上非常准确,但这种记忆化的解决方案无法泛化。

为了举例说明什么是过拟合或高方差,我将使用之前课程中使用过的一个玩具数据集。这里有一条真实的蓝色曲线,我在这条曲线周围采样了40个点,这些点将作为回归问题的训练集,我将尝试从这些蓝色点(即样本)中恢复出蓝色曲线。

接下来,我将进行一个实验:从这40个点中随机抽取30个点。在每个由30个点组成的随机子集上,我将拟合一个高阶多项式,即一个10次多项式函数。

结果如下所示。这里有三个从数据中随机抽取的30个点子集。橙色曲线是拟合10次多项式的结果。

第一个观察结果是,所有这些解决方案都能很好地拟合训练数据。你可以看到橙色曲线穿过了这组点,穿过了这个点,这里有两个蓝点,它都接触到了。因此,橙色曲线非常接近所有的训练集点。但在训练集之外的区域,它出现了非常大的跳跃,显然这些预测是错误的。所以,我们的模型在每一个例子中都过拟合了。

另一个观察结果是,橙色曲线的形状在这三个样本中差异很大。这就是为什么我们将这种过拟合问题称为“方差”。当我们稍微扰动数据时,当我们从总共40个点中选择不同的30个点时,我们会得到非常不同的橙色曲线,即非常不同的解决方案。因此,我们学习的模型会随着数据集的扰动而发生显著变化。

这里我有几个点,它们来自同一个分布但不同的30个点子集,这个不同的子集完全改变了模型的输出(曲线的形状)。因此,我们说这里存在一个高方差问题,因为当我们稍微扰动数据时,解决方案变化很大。

再次强调,倾向于过拟合的算法被称为高方差

我们看到的另一个问题被称为欠拟合,也称为偏差。这里的问题是相反的:我们的函数或模型表达能力不足,无法学习数据的真实结构。在这种情况下,我们称其为高偏差

以下是在刚才展示的多项式数据集上同时展示过拟合和欠拟合(即方差和偏差)的例子。

在中间,我有一个过拟合的例子,我们再次拟合了一个高阶多项式(一个表达能力很强的函数),它很好地拟合了数据,但在数据之外,它的预测非常奇怪且不正确。

相反的问题——欠拟合或偏差——是当我们试图用一条直线来近似数据集时。显然,直线过于简单,遗漏了数据的结构。而一个5次多项式则拟合得很好,在函数的大部分区域,它都能很好地拟合真实的蓝色曲线。

因此,偏差和方差(或过拟合和欠拟合)是机器学习中非常重要的问题。

事实上,我要说一些更强有力的观点:机器学习中的每一个错误,要么是偏差,要么是方差。过拟合和欠拟合是我们在机器学习中可能遇到的仅有的两类问题。

当一个机器学习系统表现不佳时,要么是因为它有偏差问题(无法从数据中提取出应该提取的真实信号),要么是因为它提取了某些东西,但这些东西无法泛化到新数据(即方差问题)。如果我们没有任何偏差和任何方差,那么我们就成功地提取了真实信号并且它能泛化,因此我们得到了一个完美的模型。

所以,仅有的两种错误类型,或者换句话说,我们在机器学习中可能遇到的每一种错误,要么是偏差类型,要么是方差类型。因此,理解这两种误差来源至关重要。

量化偏差与方差

理解偏差和方差的第一步是量化它们。

我们可以通过以下简单公式轻松量化它们:

开发集误差 = (开发集误差 - 训练误差) + 训练误差

通过分组这些项,我们可以将前两项(开发集误差 - 训练误差)作为方差的估计,而训练误差项作为偏差的估计。

因此,偏差的定量估计就是我们的训练误差:我们的模型是否足够强大以拟合训练数据?方差的定量估计是第二项,即开发集误差与训练误差之间的差异:模型是否能泛化到新数据(开发集)?如果在开发集上误差高得多,那么它在训练集上学到的东西就无法泛化。因此,这是一个合理的方差估计。

使用这两个估计,我们可以诊断模型当前是否存在高方差或高偏差:

  • 如果训练误差高,那么我们就有高偏差
  • 如果开发集误差远高于训练误差,那么我们就有高方差

让我们看几个例子。

考虑以下情况:我们的训练误差是15%,开发集误差是16%,但如果让人类来解决这个问题,他们可以轻松达到2%的误差。这表明我们的系统与人类性能相比仍有很大差距。如果这是一个如图像分类的任务,那么这将强烈表明我们的系统仍然远离最优,并且我们的训练误差很高,因此我们存在高偏差,即我们欠拟合了数据。

另一个例子是:如果人类误差是2%,我们的训练误差是4%,但我们的开发集误差是12%,远高于4%。在训练集上,我们能够匹配或接近人类性能。但在开发集上,我们的性能要差得多,这表明我们在训练集上获得的高性能仅仅是通过过拟合(以某种方式记忆)实现的。所以这是一个高方差或过拟合的例子。

最后,假设你看到训练误差为2.5%,开发集误差为3%。那么训练集和开发集的误差彼此接近,并且都接近最优的人类误差。这在我看来是一个表现良好的模型。所以这是一个良好性能的例子。

偏差方差分析的思想就是观察我们在训练集上的表现,观察在开发集上的表现,并量化我们是否处于高方差或高偏差的状态。

解决方差问题(减少过拟合)

基于我们所犯的错误类型,我们可以有针对性地解决问题:要么改进方差,要么尝试改进偏差。有原则性的方法可以做到这一点。

让我们首先从解决方差问题开始。我们如何减少方差?如何减少模型中的过拟合?

最好的方法是给模型提供更多数据。这是最安全的方法,如果你能做到,这无疑是最佳方法。我们之所以首先出现过拟合,是因为我们没有足够的数据来拟合一个表达能力强的模型。我们的模型相对于我们拥有的数据量来说太大了。如果我们增加数据集的大小,模型的表达能力和数据量将达到合适的水平,这将解决我们的过拟合问题。或者换句话说,如果我们添加更多数据,那么之前记忆化的解决方案将不再适用于我们添加的新数据,算法将不得不学习一个更好的解决方案。

这是解决方差问题的理想方式,但并不总是可行的,因为获取这些额外数据可能需要大量时间或精力,即使我们拥有这些数据,在其上训练我们的模型也可能过于昂贵。

因此,还有其他几种更易于应用的解决方案。

以下是三个例子:

1. 正则化模型
这是我们之前在几节课中看到的方法,几乎适用于所有模型。例如包括L2正则化、用于神经网络的Dropout、提前停止(尤其适用于梯度提升树等算法)。正则化的缺点是它会降低模型的表达能力。因此,它迫使我们使用更简单的模型,这可能会增加偏差。即使总体误差会得到改善,我们也是在以增加偏差为代价来减少方差。理想情况下,我们希望保持偏差不变,只减少方差,这样会严格更好。

2. 删除或更改特征
想象一个极端情况:我们的训练集中有20个数据点,但我们有10,000个特征。很可能其中一些特征会纯粹由于偶然性与这20个例子的标签相关,而这些特征实际上是无关的。模型将通过错误地假设这些特征确实与标签相关来过度拟合数据。这是一个过拟合的例子。通过拥有更少的特征,尤其是更少的无关特征,过拟合数据将变得更加困难。这也是减少模型表达能力的一种不同方式(取决于你是否将特征化视为模型的一部分或数据集准备的一部分)。但这种方法的缺点是你必须手动理解哪些特征是重要的,哪些不重要,可能需要大量的人工努力。

3. 直接减小模型规模
例如,你可以减少正在使用的神经网络层的大小,或者使用更简单的核函数(例如,使用低次多项式核甚至线性核,而不是使用高阶多项式核)。这种方法有效,但在实践中,正则化通常更受青睐,因为它通常提供了一种更平滑的控制正则化的方式。在实践中,它提供了在偏差与方差之间、在表达能力强的模型与表达能力弱的模型之间更好、更准确、更平滑的权衡。

解决偏差问题(减少欠拟合)

同样,减少偏差也有一个理想的方法,那就是增加模型的表达能力。如果我们的模型太简单,那么它将无法很好地拟合数据,我们需要使这个模型更复杂、更大。这可能涉及为神经网络添加更多层、减小RBF核的带宽或增加我们正在使用的多项式的次数。一般来说,更大、表达能力更强的模型会更好地拟合数据。这种改变将减少偏差,如果我们处于偏差问题中,那么它对方差的影响不会那么大。然而,这通常也不可行,因为训练一个非常大的模型可能很昂贵。

因此,我们可以做以下其他事情:

1. 调查并添加更有用的特征
类似于我们之前对方差的讨论,我们可以尝试调查特征,并尝试向数据中添加更多有用的特征,使分类数据更容易。例如,如果我们正在进行某种医学分类,并且我们认为某个特定群体(例如青少年男孩)是我们试图预测的疾病的高风险群体,那么拥有一个名为“这是青少年男孩吗”的特征对于执行这个特定的医学预测任务将非常有用。这是一种方法,但同样,它是费力的,需要手动思考和人工工作,需要批判性的人类思维来创建这些额外的特征,这些特征将使问题对于更简单的分类器更容易处理。原则上,如果你有优秀的特征,即使是一个简单的线性模型也能工作得很好,但设计这些特征确实很困难。

2. 减少正则化
这与我们之前讨论的正则化相反。

3. 调整模型结构
例如,调整神经网络的架构。这方面的一个例子是,如果你使用密集神经网络进行图像分类,那么切换到卷积神经网络几乎肯定会以更少的参数获得更好的性能,因为卷积架构更适合分析图像,并且它更有效地利用其参数来解决这个问题。因此,如果模型架构更好、更适合给定任务,那么一个参数数量更少的较小模型可能比一个参数更多的大模型工作得更好。用我们在课堂上学到的概念来理解这意味着什么:通过调整模型架构,我们正在调整模型类别或我们正在其中搜索最优模型的假设类。如果我们能选择我们的模型类别,使其更小但更接近输入和目标之间的真实映射,那么我们将能够在一个更小、更好的模型类别中更容易地找到这个真实函数,并且需要更少的数据。这就是调整模型架构思想的含义。

最后,我想指出,对于减少偏差和方差,我们都可以使用我们之前的误差分析来指导我们需要做的更改。例如,我们可以用它来确定要添加或删除哪些特征,或者需要收集哪些额外的数据。因此,这两种方法是互补的。

实战示例:在Scikit-learn数字数据集上应用

现在,让我们看一个使用Scikit-learn数字数据集进行偏差方差分析的具体示例,我们在本讲和上一个视频中开始使用这个数据集。

回想一下,我们开始处理这个数据集,它是一个数字数据集,这些是小的8x8图像。以下是每个数字的示例。

我们可以在这里训练一个来自Scikit-learn的小型全连接网络。这是一个非常简单的神经网络模型。我在训练集上拟合它,并在我的开发集和训练集上计算预测。

现在,如果我计算这个模型的准确率,我得到以下结果:在开发集上的准确率为93.7%,在训练集上的准确率为100%。

这立即表明我们正在过拟合数据,或者至少相对于欠拟合而言,我们偏向于过拟合。

解决这个问题的一种方法是向模型添加正则化,正如我之前所说。在Scikit-learn中,有一种向此模型添加正则化的方法。Scikit-learn支持添加L2正则化,这就是我在这里要做的。alpha是L2正则化强度参数。我将把它打开并设置为1,然后拟合第二个模型。

现在,在开发集和训练集上计算其预测并可视化其性能,我看到通过添加正则化,我们在开发集上的性能提高了1%。

这是一个非常简单的例子,我们还没有完全解决这个问题。例如,我们在训练集上仍然有100%的准确率,所以我们仍然存在一些过拟合。但仅仅通过添加一点正则化,我们就轻松地将性能提高了1%,从大约6.5%的错误率降到5.5%的错误率,这是一个非常显著的错误率下降,只需在我之前的代码中添加几个字符。

这就是一个偏差方差分析如何提高模型性能的例子。

偏差方差分析与误差分析的对比

我还想将偏差方差分析与我在上一个视频中描述的误差分析进行对比。

首先,我想提一下,这两种分析着眼于不同类型的问题。在误差分析中,我们倾向于查看特定的系统性错误。例如,我们的模型可能对所有狗的图像都分类错误,如果我们调查,可能会发现这是因为我们的训练集中实际上缺少狗的图像。因此,通过误差分析,我们可以解决错误分类狗这个特定问题,这是重要的,需要观察和修复。

而偏差方差分析揭示了不同类型的错误,它揭示了高层次的一般性错误。例如,它揭示的主要问题是:我们是过拟合还是欠拟合?它提出了解决这个问题的通用方案,这些方案不涉及修复数据的特定子集,而是整体提高模型的性能,而不专注于特定的错误类别,它可能同时影响所有错误类别。

因此,如果你想知道何时应该使用这两种方法中的哪一种,答案是你应该同时使用它们,它们在许多方面是互补的。

例如,通过执行偏差方差分析,我们可以识别主要的错误类别,以及哪些数据集对误差分析有用。例如,如果我们有高偏差,那么意味着在训练集上执行误差分析将是有用的。如果我们在训练集上表现很好,那么这可能没有用。我们可以利用偏差方差来确定我们是否需要在训练集或开发集上执行误差分析。它还可以提供在误差分析中有用的类别示例(例如,缺少训练标签的狗的图像),为我们在执行误差分析时可能期望的类别类型提供额外的直觉。

反过来,误差分析对于修复偏差和方差也很有用。例如,如果我们想确定要添加哪些特征或需要收集哪些额外数据(我们是收集狗的图像还是猫的图像,还是两者都收集),我们可以使用误差分析来确定。因此,这两种方法是高度互补的。

我们之前讨论过的另一种模型调优是超参数搜索。超参数搜索也与偏差方差分析相关,因为偏差方差可以帮助指导我们的超参数搜索。通常,超参数通过网格搜索或随机搜索来选择。通过偏差方差分析,我们可以确定超参数空间的哪些子集值得探索。例如,如果我们过拟合,那么我们需要增加正则化,因此搜索比当前使用值更高的、更好的Dropout参数范围是有意义的。因此,这种分析为如何执行超参数搜索提供了直觉。

总结

最后,我想通过再次强调来结束关于偏差方差的讨论:在机器学习中,我们犯的大多数错误——事实上,我们犯的每一个错误——要么是过拟合的实例,要么是欠拟合的实例。

在任何时候,当我们迭代模型时,它要么偏向于过拟合,要么偏向于欠拟合。因此,改进一个机器学习模型意味着持续经历一个循环,该循环涉及在这两个步骤之间交替:要么我们致力于减少欠拟合,要么我们致力于减少过拟合。

本质上,改进机器学习模型是一个长循环,我们在这些步骤之间交替,直到我们消除了过拟合和欠拟合,并得到一个符合我们理想性能的模型。

因此,偏差和方差分析很重要,因为在任何给定时间,我们的模型要么过拟合要么欠拟合。为了改进模型,我们的算法是确定我们当前是处于过拟合状态还是欠拟合状态。如果我们处于欠拟合状态,我们需要使模型更具表现力;如果我们处于过拟合状态,我们需要给它更多数据,或者我们可以进行我们提出的其他一些更改。在任何时候,我们应该要么进行一种更改,要么进行另一种更改。我们之前看到的误差分析可以帮助我们指导这个过程,并帮助我们确定应该进行这两种更改中的哪一种。

在本节课中,我们一起学习了偏差与方差分析。我们了解了偏差(欠拟合)和方差(过拟合)是机器学习模型性能不佳的两种核心根源。我们学习了如何通过比较训练误差和开发集误差来量化它们,并探讨了针对高偏差和高方差问题的不同解决策略。最后,我们看到了如何将偏差方差分析与误差分析和超参数搜索结合使用,以系统性地改进模型。记住,模型改进的过程通常是在解决偏差和解决方差之间不断迭代的循环。

21.3:第3部分:基线模型 🧪

在本节课中,我们将学习一个用于诊断机器学习模型性能的重要工具:基线模型。基线模型帮助我们理解模型性能的好坏,为评估提供上下文和参考标准。


概述

在上一节中,我们讨论了模型评估的指标和方法。本节中,我们将介绍基线模型的概念及其在机器学习分析中的重要性。基线模型是用于比较的简单模型,帮助我们判断当前模型的性能是否令人满意。


什么是基线模型?

基线模型的核心思想是将机器学习系统的结果和性能置于上下文中。通过比较基线模型,我们可以更好地理解当前模型的优劣。

例如,假设你训练了一个回归模型,其平均L1误差为20。这个误差是好是坏?为了判断,我们可以计算训练集中所有目标值的平均值,并创建一个始终输出该平均值的模型。如果这个简单模型的误差也是20,那么我们的机器学习模型就没有学到任何有用的信息,因为它并不比简单地预测平均值更好。

在许多其他情况下,误差20可能是一个非常好的结果。因此,我们需要通过与其他模型比较来理解这个结果的好坏。


基线模型的类型

以下是几种常见的基线模型类型,它们可以帮助我们从不同角度评估模型性能。

1. 简单模型

简单模型包括随机猜测或输出常数/平均预测。这些模型虽然简单,但能揭示我们训练的机器学习模型的弱点。

公式示例:

  • 平均预测:y_pred = mean(y_train)

2. 可解释模型

可解释模型如线性回归或决策树模型,可以作为基线模型与复杂的非线性模型(如神经网络)进行比较。有时,经过良好调优的线性模型性能可能与复杂的先进模型相当。

代码示例:

from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X_train, y_train)

3. 现有模型或上一代模型

在实际部署机器学习系统时,我们可能需要与现有的或上一代模型进行比较。这有助于评估新模型的改进程度。

4. 非机器学习方法

有些问题可能不需要机器学习,通过手动编写的规则或经典编程方法就能解决。这些方法通常更简单、易于维护且稳定。因此,与非机器学习基线模型比较也非常有用。


性能上下界

基线模型可以帮助我们确定性能的下界,即我们希望超越的最低性能标准。同时,我们还需要估计性能的上界,即最优性能。

最优性能通常非零,即使在图像识别或语音识别等任务中,人类也无法达到完美性能。因此,估计最优性能有助于指导我们的努力方向。

公式示例:

  • 总误差 = 可避免偏差 + 不可避免偏差 + 方差
  • 可避免偏差 = 训练误差 - 最优误差
  • 不可避免偏差 = 最优误差

如何估计最优性能?

估计最优性能有多种方法,以下是几种常见的方式:

1. 人工标注

通过人工标注数据,我们可以估计人类在随机子集或开发集上的性能。如果人工标注成本过高,可以使用众包服务(如Amazon Mechanical Turk)。

2. 下游应用需求

下游应用可能对性能有特定要求,这些要求可以作为我们的目标或上界。

3. 偏差-方差分析

通过引入最优误差的概念,我们可以更精确地量化偏差和方差,并将其分为可避免和不可避免的部分。


偏差-方差分析示例

假设我们有以下性能数据:

  • 最优误差:7%
  • 训练误差:8%
  • 开发误差:9%

在这种情况下,训练误差和开发误差都接近最优误差,说明模型性能已经接近理想水平。如果训练误差远高于最优误差,则存在可避免的偏差;如果开发误差远高于训练误差,则存在方差问题。

公式示例:

  • 偏差 = 训练误差 - 最优误差
  • 方差 = 开发误差 - 训练误差

总结

在本节课中,我们一起学习了基线模型的概念及其在机器学习分析中的重要性。基线模型帮助我们理解模型性能的上下文,通过比较简单模型、可解释模型或非机器学习方法,我们可以更好地评估当前模型的优劣。此外,通过估计性能的上下界,我们可以更精确地进行偏差-方差分析,指导模型改进的方向。

基线模型是机器学习实践中不可或缺的工具,它让我们的评估更加全面和可靠。在下一节中,我们将进一步探讨如何利用这些工具优化模型性能。

22.1:第1部分:学习曲线 📈

在本节课中,我们将继续探讨如何在实践中应用机器学习,以及如何运用系统性的方法和技术,使机器学习系统在现实应用中提供良好且可靠的性能。本讲的重点是介绍各种有助于诊断模型性能的工具和技术,这些工具能提供比我们目前所见更深入的模型性能视图。

概述

上一讲我们讨论了机器学习实践中的一般性问题。本节中,我们将介绍一种名为“学习曲线”的新工具。学习曲线能帮助我们理解如何解释当前的性能水平,以及如何对机器学习模型进行何种调整。

学习曲线的动机

整体而言,本系列讲座的动机可以用以下例子概括:假设我们训练了一个机器学习分类器,其准确率达到了可接受的水平(例如80%)。作为系统开发者,我们想知道下一步该做什么:是添加更多数据、尝试更复杂的模型,还是增加更多特征?我们希望找到系统性的方法来优先处理这些决策,以最小的努力快速达到良好的准确率水平。

偏差与方差问题回顾

在之前的课程中,我们了解到机器学习模型通常受两类问题影响:过拟合和欠拟合。

  • 过拟合:当我们尝试将一个表达能力非常强的模型(例如高阶多项式)应用于数据量不足以支撑该模型容量的数据集时,就会发生过拟合。结果,模型会“记住”训练集,其学到的解仅在训练集上表现良好,而在新数据上表现不佳。过拟合的模型也被称为高方差模型。之所以得此名,是因为如果我们稍微扰动训练集,模型在训练集之外的预测结果往往会大不相同。由于过拟合算法的输出对训练集的微小变化很敏感,我们称之为高方差。
  • 欠拟合:与高方差问题相反的是欠拟合,也称为偏差。这意味着我们尝试使用的模型对于所拥有的数据集来说过于简单。例如,我们可能试图用一个线性函数去拟合一个非常复杂的高阶多项式。我们最多只能捕捉到总体趋势,但无法准确学习信号的全部内容。当一个模型无法拟合数据时,我们说它具有高偏差。

这两类问题几乎是定义上机器学习中仅有的两类问题:要么我们没有正确拟合训练数据,要么我们拟合了训练数据但解无法泛化。如果我们很好地拟合了训练数据并且解能够泛化,那么我们就解决了问题。因此,几乎可以定义,任何阻碍机器学习算法达到最佳性能的问题,要么是偏差问题,要么是方差问题。这就是理解这两类问题的重要性所在。

什么是学习曲线?

学习曲线是通过以下图表来更深入理解过拟合和欠拟合的一种方法。这里有一个学习曲线的例子。

这张学习曲线图展示了模型性能随训练集大小的变化关系。

  • X轴:表示用于训练的数据点数量。我们从较小的数量开始,随着在X轴上向右移动,数据量逐渐增加。
  • Y轴:表示模型产生的误差。原则上,这可以是任何性能度量指标。

图中红色的曲线是学习曲线,它展示了机器学习模型在开发集上的性能如何随训练集大小变化。可以看到,随着我们给模型提供更多数据,它的性能变得更好,因此红色的开发集误差下降。

需要注意的是,这条曲线是针对使用特定一组超参数的模型定义的,并且这组超参数是固定的。因此,对于每个训练集大小 M,为了获得对应的性能点,我们都会重新训练模型,但关键在于,在整个曲线生成过程中,我们重用同一组超参数。例如,我们使用具有相同层数和层大小的神经网络,或者如果使用岭回归,则保持相同的正则化参数。这样,曲线上不同点之间才具有可比性。

理想性能与数据需求

在此图中还可以显示其他元素,其中之一是我们的性能上限或理想/期望性能,我在图中用绿色表示。通常,在构建机器学习系统时,设定一个我们希望达到的性能目标上限非常有用。这使我们能够估计还有多少工作要做,更系统地优先考虑对模型的更改,并为我们提供关于导致效率低下的原因的直觉。它还可以帮助我们诊断模型所犯错误的类型。

在同一张图上显示期望性能(绿色)后,学习曲线立即为我们提供了另一组有用的信息:即我们的算法当前需要多少额外数据才能达到某个期望的性能水平。

在这个特定例子中,可以看到开发集误差呈下降趋势,即使在我们提供了现有最大数据量时,它似乎仍在继续线性下降。因此,通过观察此图,我们可以推断开发集误差的未来走向。似乎如果我们再提供大约两倍(甚至可能更少)的数据,按照这个速度,它很快就能达到我们期望的性能水平。这告诉我们,收集更多数据将非常有益,并且有一个简单的解决方案可以实现我们所需的性能水平。

因此,在查看学习曲线时,可视化最优、理想或期望性能通常非常有用。

识别性能瓶颈

相对于期望性能水平查看学习曲线,也可以告诉我们存在其他类型的问题,并且我们可以识别出比刚才展示的例子更棘手的问题。

在这个例子中,可以看到开发集误差已经趋于平稳,即使我们提供更多数据,它仍然表现出相同的性能水平。因此,这是一个通过观察曲线我们发现,收集更多数据并将其提供给模型无法解决我们问题的例子。这同样是构建这些模型时非常有用的信息,对于指导我们的工作非常重要。

训练误差学习曲线

学习曲线不仅可以在开发集上测量,也可以在训练集上测量。图中蓝色的曲线表示训练误差。

这里有几个重要的观察点:

  1. 我们的训练误差几乎总是(实际上应该总是)低于开发误差。这仅仅是因为训练误差是我们可以直接优化的目标,我们可以拟合一个直接最小化训练集的模型。而开发误差除了训练集本身的误差外,还需要泛化到开发集。因此,从训练到开发,我们不仅需要准确拟合训练集,还需要泛化到开发集,这是一个更难的问题,所以开发误差总是高于训练误差。
  2. 与开发误差不同,训练误差随着我们给模型提供更多数据而增加。其直觉如下:如果我们有一个或两个训练点,模型很容易“记住”这两个点(我们在此曲线的每个点都使用相同的超参数集,因此模型表达能力相同)。但随着我们给模型越来越多的数据,它不再能够完美拟合这些数据,因此其训练误差增加。一个具有固定容量的模型,面对一个非常大的、包含许多需要建模的边缘情况的数据集,在固定的表达能力下,将无法完美拟合一个庞大的训练集。因此,对于更大的训练集,我们的训练误差会更高;而对于可以记忆的微小数据集,训练误差几乎完美。

这些是关于训练误差曲线形状的两个观察点。当你构建自己的学习曲线时,你应该也能看到我描述的这些现象。

诊断高偏差问题

利用开发集和训练集上的学习曲线,我们可以诊断之前在课程中看到的许多错误,特别是可以更精确地诊断偏差和方差问题。

下图所示的曲线代表了一个高偏差数据集或一个对于我们所拥有数据来说偏差过高的模型。

随着我们给模型更多数据,它不再能够很好地拟合,并且稳定在一个仍然远低于期望性能水平的高错误率上。这表明模型无法很好地拟合数据,因此我们面临一种偏差问题。

在实践中,通过同时查看训练误差来诊断这个问题要容易得多。一个原因是,仅凭视觉可能不容易检测开发误差何时达到平台期。从更实际的角度来看,拥有额外的信息源——即训练误差学习曲线——总是好的。

在这里我们看到,训练误差也在增加并趋于平稳,稳定在一个相当高的水平。这为我们提供了强有力的证据,表明给模型更多数据将不再有帮助,模型根本无法很好地拟合我们已有的数据。这提供了额外的证据,因为我们知道训练误差必须始终低于开发误差。现在训练误差稳定在一个过高的水平,开发误差将无法进一步下降,因为它无法跨越蓝色曲线。因此,我们的红色误差确实处于平台期,并且永远不会达到绿色的期望性能水平。

这种类型的学习曲线是高偏差问题的特征,它使我们能够更可靠地诊断偏差问题,并提供比常规偏差-方差分析更多的信息。常规分析可能只关注特定数据量 M 下的开发误差、训练误差和期望性能值。而通过查看所有值、查看整个曲线,我们可以更可靠地确定我们是否真的存在过拟合问题,或者是否真的存在偏差问题。通过查看整个学习曲线,而不是仅仅一个点,我们可以更有信心地进行这些诊断。

诊断高方差问题

同样,学习曲线也可以帮助我们诊断高方差问题。

在这个例子中,我们的训练误差很好,接近最优,但我们的开发误差明显更高。这表明我们处于一个由于这个差距而无法很好泛化到开发集的阶段。因此,我们可以给这个模型更多数据。我们还可以看到,随着提供更多数据,这些曲线仍在快速变化。所以,如果我们推断这些曲线的形状,以及红色曲线和蓝色曲线之间仍然存在很大可填补的差距这一事实,我们可以预期开发误差会降低。这与我们的直觉相符:模型既存在过拟合,而提供更多数据将有助于缓解过拟合。提供更多数据是防止过拟合的最佳方法,并且我们根据这条曲线预期该解决方案会奏效。

同时存在高偏差和高方差

最后,在这个例子中,我们同时存在高方差和高偏差。

蓝色曲线达到的平台期显著高于绿色性能水平,而红色曲线甚至比蓝色曲线更高。这表明我们同时存在偏差和方差问题。

实例分析:手写数字识别

为了进一步说明学习曲线,让我们看一个在本课程早期见过的算法实例。

对于这个例子,我们将使用 scikit-learn 的数字数据集,这是我在早期讲座中介绍过的 MNIST 数据集的降维版本。它包含手写数字,已降维到 8x8 的网格。这里展示了每个数字的样本,以便了解它们的外观。

以下代码定义了一些用于可视化学习曲线的样板代码。你不需要理解这段代码来理解这个例子,核心示例在下面。

现在,我将使用上述函数为数字数据集上的两种算法生成学习曲线。

第一种算法是高斯朴素贝叶斯模型,这是朴素贝叶斯算法对连续输入 X 的扩展。回想一下,最常见的标准朴素贝叶斯形式假设 X 是离散值,但我们也可以定义一个适用于连续输入的版本,它是一个略有不同的变体。

我们还将把它与使用RBF(径向基函数)核的支持向量机进行比较。RBF核有时也可能被称为高斯核,因为它使用高斯分布(钟形曲线)来衡量两个输入之间的距离。

现在,在这段代码中,我们为两种算法生成学习曲线:左侧是高斯朴素贝叶斯模型,右侧是SVM。Y轴表示开发集上的准确率。

你可以看到,其形状符合我们之前的直觉:随着给模型更多数据,训练准确率下降,但开发集准确率上升,然后这两条曲线稳定在大约85%的准确率水平。我们看到,一旦达到大约1400个数据点,高斯朴素贝叶斯算法就不再真正受益于拥有更多数据,我们基本上已经达到了该算法当前能处理的最佳数据量。这是一个相当低的值,这与朴素贝叶斯是一个相当简单的线性算法的事实一致,因此对于像我们试图分类的数字这样更复杂的数据集,它往往达到较低的性能水平。

这里我们展示了使用RBF核的SVM的性能。SVM,尤其是带有RBF核的SVM,是一种表达能力更强且非线性的算法。因此,其性能明显更高。开始时,红色训练曲线和开发集之间存在巨大差距。随着我们提供更多数据,开发集性能提高。这里有趣的是,训练准确率几乎不下降,这再次表明这是一个表达能力非常强的模型,即使对于大型数据集也能很好地拟合。但我们看到,如果我们给算法大约600或800个样本,我们将处于曲线急剧下降的区域,那么我们的学习曲线分析会告诉我们,需要给它更多数据。即使在这里,如果我们有更多数据,仍然可以给模型提供,它可能还会继续改进。

此外,这里绿色和红色曲线之间可能仍存在微小差距,这表明可能存在一点过拟合。因此,我们可能希望调整超参数 gamma 来解决这个问题。

总之,这些图是在真实算法上生成的,并且与我们之前概述的理论一致。

学习曲线的局限性

最后,关于学习曲线可能存在的局限性:

  1. 计算时间:计算这些曲线需要一定的时间,因为我们必须为数据的每个子集重新训练算法。
  2. 噪声:如果数据量很小,这些曲线的形状有时可能会有噪声。例如,如果我们只有20个样本,根据我们选择哪20个样本,我们可能会有不同的性能水平。这就是我所说的学习曲线可能有噪声的意思。为了纠正这一点,我们需要对多个20点子集进行平均,这又会增加计算强度。
  3. 需要经验解读:学习曲线需要人类直觉来解读,并且需要一些经验才能更熟悉这些曲线。

总结

本节课中,我们一起学习了学习曲线这一重要工具。我们了解到,学习曲线通过绘制模型性能(如误差或准确率)随训练集大小变化的图表,帮助我们深入诊断模型是存在高偏差(欠拟合) 还是高方差(过拟合) 问题。我们探讨了如何结合训练误差曲线开发集误差曲线进行更可靠的诊断,并通过手写数字识别的实例看到了学习曲线在实际算法中的应用。最后,我们也认识了学习曲线在计算成本、噪声和解读经验方面的局限性。掌握学习曲线,能为我们系统性地改进机器学习模型提供宝贵的指导。

22.2:第二部分:损失曲线 📉

在本节课中,我们将学习一种用于诊断和解释机器学习算法性能的重要工具——损失曲线。我们将探讨损失曲线的定义、如何绘制以及如何利用它来识别模型训练中的常见问题,如过拟合、欠拟合和优化问题。


损失曲线的定义与绘制

上一节我们介绍了学习曲线,本节中我们来看看损失曲线。损失曲线绘制的是训练目标函数(即损失函数)的值随训练步数(或迭代次数)的变化情况。它可以在训练集或开发集(验证集)上计算。

大多数机器学习模型通过迭代优化过程(如梯度下降)来最小化一个目标函数,该函数也称为损失函数。损失函数在优化过程中的取值对于模型诊断非常有用。

损失曲线的公式可以表示为:
[
L(\theta_t) \quad \text{vs.} \quad t
]
其中,( L ) 是损失函数,( \theta_t ) 是第 ( t ) 步训练后的模型参数。


解读损失曲线:一个示例

以下是损失曲线可能呈现的形状示例,它也说明了损失曲线如何像学习曲线一样帮助我们诊断模型行为。

上图展示了三条不同的损失曲线:

  • 第一条曲线(红色)显示了训练准确率随训练周期(Epoch)增加而提升,这是正常现象。
  • 第二条曲线(绿色)是开发集(验证集)损失曲线的一个表现良好的例子。它与训练曲线紧密贴合,表明过拟合程度很低。
  • 第三条曲线(蓝色)则是一个出现问题的例子。开发集误差显著高于训练误差,并且在后期甚至开始恶化,这是模型过拟合的典型表现。


损失曲线诊断的常见问题

通过观察损失曲线的形态,我们可以识别出模型训练中的几种关键问题。

过度训练(过拟合)

过度训练发生在模型训练时间过长时。此时,训练误差持续改善,但验证误差却开始变差甚至上升。

解决方案通常是:

  1. 减少训练步数(早停)。
  2. 更理想的方法是正则化模型。例如,对于神经网络可以尝试添加Dropout,对于梯度提升树可以尝试使用更小的树作为基学习器。

损失曲线通过展示完整的学习轨迹(而非仅最终结果),帮助我们识别此类问题。如果只看最终性能点,我们可能无法发现存在一组能提供更好性能的中间参数。

训练不足(欠拟合)

与过度训练相反,如果模型没有训练足够长的时间,其误差可能尚未收敛。

在之前的示例中,如果蓝色曲线(训练损失)仍在持续下降,则表明通过延长训练时间,可以进一步降低训练误差(当然,需同时关注验证误差以防过拟合)。损失曲线可以为我们提供这种延长训练的指示。

优化问题:学习率设置不当

损失曲线还能帮助诊断优化算法本身的问题,例如学习率设置不当。

上图展示了不同学习率下的损失曲线:

  • 蓝色曲线(学习率过小):损失下降非常缓慢,需要很长时间才能达到较低误差。
  • 绿色与黄色曲线(学习率过大):损失初期快速下降,但随后稳定在一个较高的平台(绿色),甚至因步长过大而发散到损失很高的区域(黄色),无法接近最优解。
  • 红色曲线(学习率合适):损失平滑、稳定地下降至较低水平,收敛速度适中。

损失曲线的优势与局限性

损失曲线与上一节学习曲线相比,具有一些明显优势:

  • 无需额外计算:在模型训练过程中即可记录,几乎是“免费”获得的诊断工具。
  • 能揭示特定问题:如过度训练、训练不足以及某些优化问题,这些可能是其他曲线不易直接发现的。

然而,损失曲线也有其局限性:

  • 它可能无法最有效地估计模型是处于高偏差还是高方差状态。
  • 它不能直接预测如果提供更多数据,模型性能将如何变化。

因此,当诊断存在歧义时,我们仍需要结合使用学习曲线损失曲线。两者是互补的工具,共同使用可以提供更可靠、全面的模型诊断。


本节课中我们一起学习了损失曲线这一重要工具。我们了解了如何绘制和解读损失曲线,并利用它来诊断过度训练、训练不足以及学习率设置不当等常见问题。记住,损失曲线与学习曲线是相辅相成的,在实际工作中结合使用它们,能更有效地指导和优化机器学习模型的开发流程。

22.3:第3部分:验证曲线 📊

在本节课中,我们将学习一个用于诊断和解释机器学习模型性能的重要工具:验证曲线。我们将了解它的作用、如何生成以及如何解读。

上一节我们讨论了模型评估的基本流程,本节中我们来看看一个专门用于分析超参数影响的工具。

验证曲线是模型开发工作流中的一部分。在这个工作流中,我们迭代地训练新模型,在开发集上观察其性能,调整模型参数并重新训练。当我们对模型满意后,才在独立的测试集上进行最终评估。

验证曲线对所有具有重要超参数的机器学习模型都很有用,这基本上涵盖了大多数模型。超参数是指那些影响模型性能,但我们不直接在训练过程中优化的参数。

验证曲线通过展示模型性能随特定超参数值变化的函数关系,来解释超参数的影响。它会同时显示模型在训练集和开发集上的性能。

以下是机器学习中常见超参数的例子:

  • L2正则化的强度lambdaalpha
  • 神经网络层数num_layers
  • 神经网络大小layer_size
  • 学习率learning_rate
  • RBF核函数的带宽gamma
  • K均值聚类中的簇数量n_clusters

为了具体说明,我们来看一个验证曲线的例子。我们将使用本讲前面介绍过的数字数据集,训练一个带有RBF核的支持向量机(SVM)模型,并测试该核函数带宽超参数的不同取值。

回忆一下,RBF核的带宽参数 gamma 控制着模型在预测新数据点时,需要考虑的邻近数据点的范围。较小的 gamma 值意味着考虑更广范围的点,而较大的 gamma 值则使模型更关注非常邻近的点。

在Scikit-learn中,有一个方便的函数 validation_curve 可以用来计算验证曲线。通过少量代码,我们可以生成并可视化这条曲线。

以下是通过代码生成的验证曲线示例:

这就是一个验证曲线的例子。

在X轴上,是超参数 gamma 的不同取值。在Y轴上,是模型的性能指标,这里用的是准确率。

对于较小的 gamma 值(图左侧),代表训练集准确率的橙色曲线和代表验证集准确率的蓝色曲线都很低且相似。随着 gamma 值增加,两条曲线的性能都得到提升,并在某个区域(大约 10^-310^-1 之间)趋于稳定,达到较高的性能水平。

然而,当 gamma 值继续增大(超过 10^-1),橙色曲线(训练准确率)保持在高位,但蓝色曲线(验证准确率)开始下降。这意味着,对于大约在 10^-310^-1 范围之后的 gamma 值,模型开始对数据过拟合。较高的 gamma 值增加了模型的表达能力,使其在训练集上表现优异,但在验证集上表现变差,这正是过拟合的迹象。

通过绘制整条曲线,我们可以清晰地看到发生了什么。这个工具就是验证曲线,它能帮助我们直观地理解使用不同超参数值所带来的影响。

本节课中我们一起学习了验证曲线。我们了解到,验证曲线是一个强大的可视化工具,它能同时展示模型在训练集和验证集上性能随某个超参数变化的趋势。通过观察两条曲线的分离情况,我们可以诊断模型是欠拟合、拟合良好还是过拟合,从而为超参数调优提供直观的指导。

22.4:第4部分 - 分布不匹配 🧩

在本节课中,我们将要学习机器学习中一个特定的问题:分布不匹配。这个问题会影响模型的性能,尤其是在训练数据与开发/测试数据来自不同分布时。我们将探讨如何诊断、量化并解决这一问题。


机器学习中的数据划分

上一节我们介绍了机器学习模型开发的基本流程。在开发模型时,我们通常使用三个不同的数据集:

  • 训练集:用于训练或拟合我们的模型。
  • 开发集:用于检查我们所做的特定建模选择是否有效,并用于开发和改进模型。
  • 测试集:用于评估模型的最终性能。

为了使机器学习工作良好,这些数据集必须能代表我们期望从该模型获得的最终性能。

具体来说,开发集和测试集应来自我们预期在生产环境中看到的数据分布。一旦部署了系统,我们将看到特定的输入模式,这些模式应与我们在开发和测试集中看到的相似。

与开发集和测试集不同,训练集可能来自相同的分布,也可能不是。例如,如果我们正在构建一个用于检测图像中是否有猫的分类器,那么拥有一个包含多种动物图像的数据集可能是有用的。或者,如果我们正在检测猫与狗,拥有其他动物的图像也可能有助于模型学习更通用的特征,从而提高猫类别的性能。


分布不匹配问题

当这些分布条件不满足时,就会出现问题。例如,当开发集或测试集不再代表真实的数据分布,或者当我们选择的训练集与开发集差异太大时,就可能出现分布不匹配。如果我们添加了太多与最终任务(如分类猫狗)无关的图像(例如汽车或PDF文档),这可能会损害我们的性能。这是一种由于分布不匹配而产生的不同类型的错误。

在实践中,我们在现实世界中看到的数据最终会与我们正在处理的数据变得不同。理解和检测这类问题非常重要,因为它们可能导致非常显著的性能问题。我们目前所做的所有分析都假设这些不同的分布以我之前描述的方式相关,但如果这个假设不成立,很多分析就会失效。


诊断分布不匹配:训练-开发集

为了诊断训练集和开发集之间的不匹配问题,我们可以创建一个特殊的新数据集,并通过观察模型在这个新数据集上的性能来判断问题是否存在。我们称这个新数据集为训练-开发集,它是训练集和开发集之间的混合。

以下是创建训练-开发集的方法:
我们从训练集中随机抽取一个子集,将其用作第二个验证集或开发集。例如,如果我们的训练集有200,000张图像,我们可以随机抽取5,000或10,000张,将它们保留为一个单独的验证集。现在,在模型训练完成后,我们将同时在标准开发集和训练-开发集上测量性能。

这个数据集使我们能够诊断偏差、方差,以及关键的分布不匹配问题。


通过误差分析进行诊断

以下是使用误差模式进行诊断的方法。我们已经确定开发误差很高,这可能源于方差问题、偏差问题,也可能是分布不匹配问题(意味着我们在训练集中添加了太多不相关的示例,从而分散了模型的注意力)。

以下是几种典型的误差模式及其含义:

  • 情况一:高方差

    • 训练误差低,但训练-开发误差和标准开发误差都高。
    • 这表明模型过拟合了训练集,无法泛化到训练-开发集和标准开发集。
  • 情况二:高可避免偏差

    • 训练误差、训练-开发误差和开发误差都高(例如,远高于人类误差)。
    • 这表明模型存在高偏差,即使在训练集上也表现不佳。
  • 情况三:分布不匹配

    • 训练误差和训练-开发误差都低,但标准开发误差显著更高。
    • 这表明模型能很好地泛化到训练-开发集(与训练集同分布),但无法泛化到标准开发集。原因只能是标准开发集与训练集的分布差异很大。


具体示例:猫图像分类器

让我们通过一个具体例子来理解。假设我们正在构建一个猫图像分类器。

  • 训练集:包含猫和其他动物的图像。
  • 训练-开发集:从训练集中抽样,因此也包含猫和其他动物。
  • 开发集和测试集:只包含猫的图像,因为我们最终只评估对猫的分类性能。

现在,观察以下误差模式:

  1. 高方差示例

    • 训练误差 = 2%
    • 训练-开发误差 = 6%
    • 标准开发误差 = 6%
    • 模型能完美拟合训练集,但即使在同分布的训练-开发集上误差也更大,这表明过拟合。
  2. 高偏差示例

    • 训练误差 = 10%
    • 训练-开发误差 = 11%
    • 标准开发误差 = 12%
    • 人类误差 = 1%
    • 即使训练误差也远未接近人类误差,这是高可避免偏差的典型例子。

  1. 分布不匹配示例
    • 训练误差 = 4%
    • 训练-开发误差 = 5%
    • 标准开发误差 = 13%
    • 模型能泛化到训练-开发集,但不能泛化到标准开发集,这明确指向了分布不匹配。

量化分布不匹配误差

我们可以在之前学习的误差分解基础上,增加一个分布不匹配分量,从而更精确地量化开发误差。

开发误差可以分解为:
开发误差 = (可避免偏差) + (方差) + (分布不匹配) + (不可避免偏差)

更形式化地,我们可以这样表示:
Err_dev = (Err_train - Err_bayes) + (Err_dev_train - Err_train) + (Err_dev - Err_dev_train)

其中:

  • Err_train - Err_bayes 近似为可避免偏差
  • Err_dev_train - Err_train 代表了分布不匹配(因为训练-开发集与训练集同分布,此差值反映了模型对同分布新数据的泛化能力,若差值大则说明训练集本身可能有问题,但这里我们主要关注下一项)。
  • Err_dev - Err_dev_train 是关键的分布不匹配量化指标,它直接衡量了开发集分布与训练集分布之间的差异所导致的额外误差。
  • 剩余部分为不可避免偏差

这是一种更定量化的方式来表述我们刚刚描述的概念。


检测开发集与测试集的“陈旧化”

尽管这个想法是在训练集和开发集的背景下描述的,但我们也可以在其他上下文中使用它。例如,我们可以使用相同的思路来检测开发集和测试集是否“陈旧”,即它们是否不再代表生产数据分布。

为了确定它们是否陈旧,我们会收集额外的真实世界数据,并将其作为另一个开发集或测试集(可称为“真实世界数据集”)。如果我们的模型在旧的开发集上泛化得很好,但在新数据集上表现不佳,那就意味着我们的旧数据已经变得陈旧,是时候更新我们的数据集了。

在实践中,当机器学习系统部署到生产环境后,数据最终都会变得陈旧,因此监控这种行为非常重要。


如何解决数据不匹配问题 🛠️

最后,如何解决数据不匹配问题?本质上,这需要理解导致不匹配的数据属性。

我们需要弄清楚不匹配是因为我们在不同环境中收集数据(例如,用户来自不同国家,不同数据集的国家分布不同),还是因为数据采集条件不同(例如,一些声音在汽车内录制,另一些在安静房间内录制),或者是其他原因(例如,语音录音中有不同口音)。

理解导致差异的具体属性非常重要。一旦理解了这些属性,我们就希望最小化它们的影响。我们可以选择:

  1. 移除导致不匹配的数据部分。
  2. 添加更多匹配良好的数据来弥补任何缺陷。

理解数据的哪些属性导致了不匹配,以及应该移除或添加哪些数据,最好的方法是通过检查数据。这可以通过我们之前学过的误差分析来完成。误差分析是一个有效且通用的过程,用于理解如何修复机器学习模型、模型主要犯哪些错误。同样的过程可以应用在这里,以解释机器学习系统的错误,并解释数据或分布不匹配在此过程中的作用。


总结

在本节课中,我们一起学习了机器学习中的分布不匹配问题。我们了解到,当训练数据与评估数据(开发集/测试集)来自不同分布时,模型的性能会受到影响。我们介绍了使用训练-开发集来诊断这一问题的方法,并通过分析训练误差、训练-开发误差和开发误差的模式,来区分高方差、高偏差和分布不匹配。我们还探讨了如何量化分布不匹配误差,以及如何检测数据集的“陈旧化”。最后,我们讨论了解决数据不匹配的策略,核心在于理解数据差异的属性,并通过误差分析指导数据集的修正。掌握这些诊断和解决方法,对于构建鲁棒且能在真实世界中良好工作的机器学习系统至关重要。

posted @ 2026-03-26 12:32  布客飞龙IV  阅读(6)  评论(0)    收藏  举报