DLAI-机器学习笔记-全-

DLAI 机器学习笔记(全)

1:欢迎来到机器学习 🚀

在本节课中,我们将要学习什么是机器学习,并了解它在日常生活和工业中的广泛应用。


什么是机器学习?🤖

机器学习是计算机科学的一个分支。它的核心是让计算机系统能够从数据中“学习”并改进,而无需为每个特定任务进行明确的编程。

上一节我们介绍了机器学习的定义,本节中我们来看看它在现实世界中的具体应用。

机器学习的日常应用 📱

以下是机器学习在我们日常生活中的一些常见例子:

  • 网络搜索:当你在谷歌、必应或百度上搜索“如何制作寿司卷”时,搜索引擎能快速找到相关网页。这是因为机器学习软件已经学会了如何对网页进行排序。
  • 图片识别:当你在 Instagram 或 Snapchat 上传照片并想标记朋友时,这些应用可以识别照片中的朋友并为他们添加标签。这也是机器学习。
  • 内容推荐:当你在视频流媒体平台看完一部《星球大战》电影后,平台可能会使用机器学习来推荐其他你可能喜欢的类似电影。
  • 语音与文本:每次你在手机上使用语音输入发送短信(如“嘿,Andrew,最近怎么样?”),或者告诉手机“嘿 Siri,播放蕾哈娜的歌”,又或者询问“好的 Google,显示我附近的印度餐厅”,背后都有机器学习在运作。
  • 垃圾邮件过滤:每当你收到一封标题为“恭喜你赢得一百万美元”的邮件时,你的邮件服务很可能会将其标记为垃圾邮件。这同样是机器学习的应用。

机器学习在工业与专业领域的应用 🏭

除了消费者应用,人工智能也正迅速进入大型公司和工业领域。

以下是几个关键领域的应用:

  • 应对气候变化:机器学习正在帮助优化风力涡轮机的发电效率。
  • 医疗健康:机器学习开始进入医院,帮助医生做出更准确的诊断。
  • 工业质检:在 Landing AI,我们做了大量工作,将计算机视觉技术应用于工厂,以检测生产线下来的产品是否存在缺陷。

课程目标与展望 🎯

机器学习是一门让计算机无需明确编程就能学习的科学。

在本课程中,你将学习机器学习的知识,并亲自动手编写代码来实现机器学习算法。

数百万学习者已经学习了本课程的早期版本,这门课程也促成了 Coursera 的创立。许多学员最终构建了令人兴奋的机器学习系统,甚至在人工智能领域开启了成功的职业生涯。

我很高兴你能与我一同踏上这段旅程。欢迎加入,让我们开始吧!


本节课中我们一起学习了机器学习的核心定义,并探索了它在从日常消费电子产品到工业与环保等关键领域的广泛应用。我们了解到,机器学习是一门让计算机通过数据自我改进的科学,而本课程将为你打开亲手构建这类系统的大门。

2:机器学习的应用 🚀

在本节课中,我们将学习机器学习的当前发展状况,并亲自动手实践实现机器学习算法。我们将了解最重要的机器学习算法,其中一些正是当今大型AI或科技公司正在使用的技术,并感受AI领域的最新进展。除了学习算法,我们还将掌握所有重要的实用技巧,以确保算法表现良好,并通过亲自实现来理解它们的工作原理。

什么是机器学习?🤖

上一节我们介绍了课程的整体目标,本节中我们来看看为什么机器学习在今天如此广泛应用。机器学习作为人工智能(AI)的一个子领域发展起来。我们曾希望构建智能机器,但事实证明,我们只能为机器编程完成一些基本任务,例如像GPS一样找到从A点到B点的最短路径。然而,对于大多数更有趣的任务,例如执行网络搜索、识别人声、诊断疾病、解读X光片或构建自动驾驶汽车,我们并不知道如何编写明确的程序来完成。

我们已知的实现这些任务的唯一方法,是让机器自己学习如何完成。

机器学习的实际应用案例 💡

以下是机器学习在现实世界中的一些关键应用领域,这些例子展示了其广泛的影响力:

  • 互联网与科技:例如,在谷歌大脑团队工作时,我致力于语音识别、谷歌地图街景图像的计算机视觉以及广告优化等问题。
  • 前沿科技与安全:在DeepMind工作期间,我的工作涉及从增强现实AI到打击支付欺诈,再到领导自动驾驶汽车团队等各个方面。
  • 传统行业转型:最近,在Landing AI、AI Fund和斯坦福大学,我致力于将AI应用于制造业、大规模农业、医疗保健、电子商务等领域。

如今,有成千上万甚至数百万的人正在从事机器学习应用的工作。当你掌握了这些技能后,我希望你也能发现涉足这些激动人心的不同应用乃至不同行业是极大的乐趣。

事实上,我很难想象在不久的将来,有哪个行业不会受到机器学习的显著影响。😊

人工智能的未来与机遇 🌟

展望更远的未来,包括我在内的许多人都对AI的梦想感到兴奋:有朝一日能建造出像你我一样智能的机器。这有时被称为人工通用智能(AGI)

我认为AGI被过度炒作了,我们距离那个目标仍然很遥远。我不知道实现它需要50年、500年还是更长时间,但大多数AI研究人员相信,最接近该目标的方法是使用学习算法,也许是那些从人脑工作原理中汲取了一些灵感的算法。在本课程后面,你也会听到更多关于追求AGI的内容。

根据麦肯锡的一项研究,预计到2030年,AI和机器学习每年将创造额外的13万亿美元价值。尽管机器学习已经在软件行业创造了巨大的价值,但我认为在软件行业之外的领域,如零售、旅游、交通、汽车、材料、制造业等,还有待创造更巨大的价值。

由于众多不同领域存在大量未开发的机会,目前市场对掌握机器学习技能的需求巨大且尚未得到满足。这就是为什么现在是学习机器学习的绝佳时机。

总结与预告 📚

本节课中我们一起学习了机器学习的广泛应用、其作为实现AI目标的核心方法的重要性,以及它在当前和未来创造巨大价值的潜力。如果你对机器学习的应用感到兴奋,我希望你能坚持学完这门课程。我几乎可以保证,你会发现掌握这些技能是值得的。

在下一个视频中,我们将探讨机器学习的更正式定义,并开始讨论机器学习问题和算法的主要类型。你将学习一些主要的机器学习术语,并开始了解不同算法的区别以及每种算法适用的场景。

让我们继续观看下一个视频。

3:什么是机器学习?🤖

在本节课中,我们将学习机器学习的定义,并了解其适用场景。课程将介绍机器学习的基本概念、主要类型以及学习本课程你将获得的知识与技能。


什么是机器学习?

亚瑟·塞缪尔给出了一个关于机器学习的定义。他将机器学习定义为:使计算机无需明确编程即可学习的研究领域

塞缪尔的成名之作是他在20世纪50年代编写的一个跳棋程序。这个程序的非凡之处在于,塞缪尔本人并非出色的跳棋玩家。

他编写了一个程序,让计算机与自己进行数万次对弈。通过观察哪些棋盘局势容易导致胜利,哪些容易导致失败,这个跳棋程序逐渐学会了识别棋盘局势的好坏。

通过努力走向有利局势并避开不利局势,他的程序在下跳棋方面变得越来越好。由于计算机有耐心与自己进行数万次对弈,它积累了大量的跳棋经验,最终其棋艺超过了塞缪尔本人。


一个关于学习机会的问题

在课程中,除了讲解,我偶尔会提出问题以确保你理解内容。这里有一个问题:如果计算机进行的对弈次数少得多,会发生什么?

以下是可能的结果:

  • 如果对弈次数少得多,程序的表现会变差。
  • 如果对弈次数少得多,程序的表现会更好。

感谢你查看这个测验。如果你选择了“表现会变差”这个答案,那么你是正确的。通常,你给学习算法提供的学习机会越多,它的表现就会越好。如果你第一次没有选对答案,那也完全没关系。这些测验问题的目的不是看你能否第一次就全部答对,而是帮助你练习正在学习的概念。

亚瑟·塞缪尔的定义是一个相当非正式的定义。在接下来的两个视频中,我们将一起深入探讨机器学习算法的主要类型。


机器学习的主要类型

在本课程中,你将学习许多不同的学习算法。机器学习的两种主要类型是监督学习无监督学习。我们将在接下来的几个视频中更详细地定义这些术语。

在这两种类型中,监督学习是现实世界应用中使用最多的机器学习类型,并且在本系列课程中见证了最快速的发展和创新。本系列课程共有三门,第一门和第二门课程将重点介绍监督学习,第三门课程将重点介绍无监督学习、推荐系统和强化学习。

迄今为止,最常用的学习算法类型是监督学习、无监督学习和推荐系统。


本课程的核心:工具与实践建议

本课程我们将花费大量时间的另一个方面是应用学习算法的实用建议。我对此有强烈的感受:学习算法就像给人一套工具。确保你拥有出色的工具固然重要,但同样重要甚至更重要的是确保你知道如何应用这些工具。

试想,如果有人给你一把最先进的锤子或手锯,然后说“祝你好运,现在你拥有了建造三层楼所需的所有工具”,这显然行不通。机器学习也是如此,确保你拥有工具非常重要,确保你知道如何有效地应用机器学习工具也同样重要。

这就是你在本课程中将获得的内容:工具本身以及有效应用这些工具的技能


避免常见陷阱

我经常拜访一些顶尖科技公司的朋友和团队。即使在今天,我仍然看到经验丰富的机器学习团队将机器学习算法应用于某些问题,有时他们努力了六个月却没有取得太大成功。

当我观察他们的做法时,我有时觉得我本可以在六个月前就告诉他们,当前的方法行不通,并且存在一种不同的使用这些工具的方法,能让他们有更大的成功机会。

因此,在本课程中,你将学到的一个相对独特的东西是,关于如何实际开发一个实用、有价值的机器学习系统的最佳实践。这样,你就不太可能成为那些花了六个月时间却走错方向的团队中的一员。

在本课程中,你将了解最熟练的机器学习工程师是如何构建系统的。我希望你完成本课程后,能成为当今世界上少数知道如何设计和构建严肃、强大的机器学习系统的人之一。


总结与预告

本节课我们一起学习了机器学习的定义,了解了其通过经验学习而非明确编程的核心思想,并初步认识了监督学习和无监督学习这两种主要类型。我们还明确了本课程不仅提供算法工具,更注重教授有效应用这些工具的实践技能。

在下一个视频中,让我们更深入地看看什么是监督学习,什么是无监督学习。此外,你还将学习何时可能想要使用它们。我们下个视频见。

4:监督学习第一部分 🧠

在本节课中,我们将学习监督学习的基本概念。监督学习是目前创造绝大多数经济价值的机器学习类型。我们将了解其定义、核心思想,并通过具体例子来理解它的工作原理。


什么是监督学习?🎯

监督学习,或称监督式机器学习,指的是学习从输入 X 到输出 Y 的映射关系的算法。其关键特征是,你需要为学习算法提供包含“正确答案”的示例。这里的“正确答案”指的是给定输入 X 所对应的正确标签 Y

通过观察大量正确的输入 X 和期望的输出标签 Y 配对,学习算法最终能够学会仅根据输入(无需输出标签)来做出相当准确的预测或猜测。


监督学习的应用实例 📧

以下是监督学习在不同领域的一些典型应用:

  • 垃圾邮件过滤:输入 X 是一封电子邮件,输出 Y 是判断这封邮件是“垃圾邮件”还是“非垃圾邮件”。
  • 语音识别:输入 X 是一段音频剪辑,算法的任务是输出对应的文本转录 Y
  • 机器翻译:输入 X 是英文句子,输出 Y 是对应的西班牙语、阿拉伯语、中文等其他语言的翻译。
  • 在线广告:输入 X 包含广告信息和你的一些信息,算法尝试预测你是否会点击该广告 Y。这是目前最具盈利能力的监督学习应用之一。
  • 自动驾驶:输入 X 是图像和其他传感器(如雷达)信息,输出 Y 是其他车辆的位置,以便自动驾驶汽车安全行驶。
  • 工业视觉检测:输入 X 是刚下生产线的产品(如手机)图片,输出 Y 是判断产品是否存在划痕、凹痕或其他缺陷。

监督学习的工作流程 🔄

在所有上述应用中,监督学习都遵循一个通用流程:

  1. 训练模型:首先,使用包含输入 X 和正确答案(即标签 Y)的示例数据集来训练模型。
  2. 进行预测:模型从这些输入-输出配对(XY)中学习后,便可以接收一个全新的、从未见过的输入 X,并尝试生成相应的输出 Y

深入案例:房价预测 🏠

为了更深入地理解,让我们看一个具体的例子:根据房屋面积预测房价。

假设你收集了一些数据,并将其绘制在图表上。横轴代表房屋面积(平方英尺),纵轴代表房屋价格(千美元)。

现在,如果你的朋友想知道他750平方英尺的房屋价格,学习算法如何帮助你呢?

一种方法是,算法可以尝试用一条直线来拟合数据。从这条直线上读取,你朋友的房子售价可能在15万美元左右。

然而,拟合直线并非唯一选择。对于这个应用,可能有其他方法效果更好。例如,你可能会决定用一条曲线(一个比直线更复杂的函数)来拟合数据。如果这样做并在此处进行预测,那么你朋友的房子售价可能接近20万美元。

在本课程后面,你将学习如何系统地决定是使用直线、曲线还是其他更复杂的函数来拟合数据。


回归问题 📈

这个房价预测的例子是监督学习的一个特定类型,称为回归

所谓回归,我们指的是尝试从无限多的可能数字中预测出一个数字。在我们的例子中,房价可以是15万、7万、18.3万或介于其间的任何其他数字。

核心公式:在回归中,我们试图学习一个函数 f,使得 Y ≈ f(X),其中 Y 是一个连续数值。


本节总结 ✨

本节课我们一起学习了监督学习的基础知识。我们了解到,监督学习是通过提供带标签的示例(输入 X 和正确答案 Y),让算法学会从输入到输出的映射关系。我们看到了它在垃圾邮件过滤、语音识别等多个领域的应用,并通过房价预测的案例,具体了解了回归这一种监督学习任务,其目标是预测一个连续的数值。

在下一节中,我们将探讨监督学习的另一种主要类型:分类问题。

5:监督学习(第二部分)🔍

在本节课中,我们将要学习监督学习的第二种主要类型:分类算法。我们将了解分类与回归的区别,并通过具体的例子(如乳腺癌检测)来理解分类问题是如何工作的。


从回归到分类 🧠

上一节我们介绍了回归算法,它用于预测连续的数值。本节中我们来看看监督学习的另一个重要分支:分类算法

监督学习算法学习从输入到输出(或从 X 到 Y)的映射关系。分类算法是监督学习的一种主要类型,它学习预测类别,而不是无限多的可能数字。

什么是分类问题?🎯

以乳腺癌检测为例。我们可以构建一个机器学习系统,作为医生的诊断工具来检测乳腺癌。早期检测可能挽救患者的生命。

系统利用患者的医疗记录,试图判断一个肿瘤(肿块)是恶性(即癌变的、危险的),还是良性(即非癌变的、不那么危险的)。

以下是该问题的一个简化数据集示例:

  • 肿瘤大小作为输入特征。
  • 标签为 0(代表良性)或 1(代表恶性)。

我们可以将数据绘制在图表上,横轴代表肿瘤大小,纵轴只取 0 或 1 两个值。

核心概念:分类与回归的关键区别在于,分类试图预测的只是少量可能的输出或类别(本例中为 0 和 1),而回归试图预测的是无限多个可能数字中的任何一个

多类别分类 📊

分类问题中也可以有两个以上的可能输出类别。

例如,一个学习算法除了判断良性或恶性,还可能输出恶性肿瘤的具体类型(如类型1、类型2)。在这种情况下,算法可以预测三个可能的输出类别。

在分类中,术语“输出类”和“输出类别”经常互换使用。

总结一下:分类算法预测类别。类别不一定是数字,也可以是非数值的(例如,预测一张图片是猫还是狗)。类别也可以是数字(如 0, 1, 2),但分类与回归(当我们将输出解释为数字时)的区别在于,分类预测的是一个小的、有限的、离散的可能输出类别集合,而不是像 0.5 或 1.7 这样的中间所有可能数字。

使用多个输入特征 🧩

在我们之前看的例子中,我们只有一个输入值(肿瘤大小)。但实际上,我们可以使用多个输入值来预测输出。

以下是一个使用两个输入特征的例子:

  • 输入1:肿瘤大小
  • 输入2:患者年龄

现在,每个数据点由两个值定义。我们仍然用圆圈表示良性肿瘤患者,用叉号表示恶性肿瘤患者。

当一位新患者前来就诊时,医生可以测量其肿瘤大小并记录其年龄。给定这个新的数据点,我们如何预测肿瘤是良性还是恶性?

面对这样的数据集,学习算法可能会做的是找到一条决策边界,将恶性肿瘤与良性肿瘤区分开来。

核心概念:学习算法需要决定如何为这些数据拟合一条边界线。算法找到的这条边界线将帮助医生进行诊断。例如,如果新数据点落在边界线的某一侧,则肿瘤更可能是良性的。

在实际的机器学习问题中,通常需要更多的输入特征。例如,在乳腺癌检测中,可能还会使用肿瘤块的厚度、细胞大小的均匀性、细胞形状的均匀性等特征。

本节总结 📝

本节课中我们一起学习了监督学习的核心内容:

  1. 监督学习:学习从输入 X 到输出 Y 的映射,算法从“正确答案”中学习。
  2. 两大类型
    • 回归:预测数字,来自无限多的可能输出数字(例如预测房价)。
    • 分类:预测类别,来自一个小的、有限的可能输出集合(例如判断肿瘤良性/恶性)。

你已经了解了什么是监督学习,包括回归和分类。接下来,机器学习还有第二种主要类型,称为无监督学习,我们将在下一个视频中探讨。

6:无监督学习第一部分 🧩

在本节课中,我们将要学习无监督学习。这是一种与监督学习不同的机器学习范式,其核心在于从没有标签的数据中自动发现结构或模式。

上一节我们介绍了监督学习,本节中我们来看看无监督学习。不要被“无监督”这个名字误导,无监督学习同样非常强大。

什么是无监督学习? 🤔

在监督学习中,每个数据样本都对应一个输出标签 Y,例如在肿瘤分类问题中,标签是“良性”或“恶性”。其数据形式可表示为:
(X, Y),其中 X 是特征,Y 是标签。

而在无监督学习中,我们只有输入数据 X,没有对应的标签 Y。我们的任务不是预测一个已知的答案,而是让算法自己从数据中找出有趣的结构、模式或分组。

例如,给定病人的年龄和肿瘤大小数据,但没有诊断标签。数据点可能如下图所示,我们的目标是发现数据中潜在的分组。

聚类算法:无监督学习的核心 🔍

聚类算法是无监督学习中最常见的类型之一。它的目标是将未标记的数据自动分组到不同的“簇”中。

以下是聚类算法的几个典型应用:

1. 谷歌新闻分组 📰

谷歌新闻每天会扫描互联网上的数十万篇文章,并自动将相关的故事聚集在一起。例如,所有关于“大熊猫产下双胞胎”的新闻会被归入同一个簇。

算法会自己发现哪些关键词(如“熊猫”、“双胞胎”、“动物园”)表明文章属于同一主题,而无需人工预先定义规则。这正体现了无监督学习的核心:算法在没有“监督”(即没有正确答案)的情况下自行学习

2. DNA微阵列数据分析 🧬

DNA微阵列数据可以测量不同个体的基因表达水平。通过聚类算法,研究人员可以将个体自动分组到不同的类别中(例如Type 1, Type 2, Type 3),从而发现潜在的基因型分类,而无需预先知道存在哪些类型的人。

3. 客户市场细分 🛒

许多公司拥有庞大的客户数据库。无监督学习可以用于市场细分,自动将客户分成不同的群组,以便提供更高效、个性化的服务。

例如,DeepLearning.AI团队曾对社区成员进行聚类分析,发现了几个主要的学习动机群体:

  • 寻求知识以提升技能的人。
  • 寻求职业发展(如晋升、换工作)的人。
  • 希望了解AI如何影响其工作领域的人。

总结与过渡 📝

本节课中我们一起学习了无监督学习,特别是聚类算法。我们了解到,聚类算法接收没有标签的数据,并尝试自动将它们分组到不同的簇中。

除了聚类,无监督学习还有其他类型。在下一节视频中,我们将继续探索其他类型的无监督学习算法。

7:无监督学习第二部分 🧠

在本节课中,我们将学习无监督学习的正式定义,并简要了解聚类之外的其它无监督学习类型。

上一节我们介绍了什么是无监督学习以及一种称为“聚类”的无监督学习类型。本节中,我们将给出无监督学习一个更正式的定义,并快速浏览除聚类之外的一些其它无监督学习类型。

无监督学习的定义 📖

在监督学习中,数据同时包含输入 X 和输出标签 Y。而在无监督学习中,数据仅包含输入 X,不包含输出标签 Y。算法的任务是从数据中发现一些结构、模式或有趣的信息。

无监督学习的类型 🔍

以下是三种主要的无监督学习类型:

  • 聚类算法:正如上一节所见,它将相似的数据点分组在一起。
  • 异常检测:用于检测异常事件。这在金融系统的欺诈检测中非常重要,因为异常事件或交易可能是欺诈的迹象。
  • 降维:它允许你将大数据集压缩成小得多的数据集,同时尽可能少地丢失信息。

如果你对异常检测和降维的概念还不太理解,请不要担心,我们将在后续课程中深入探讨。

理解检查 ✅

现在,我想请你回答一个问题,以检查你的理解。请不要有压力,第一次尝试没有答对也完全没关系。

请从以下选项中,选出你认为属于无监督学习的例子(其中两个是无监督学习例子,两个是监督学习例子):

  • 垃圾邮件过滤:如果你有标记为垃圾邮件或非垃圾邮件的数据,这可以作为一个监督学习问题来处理。
  • 新闻故事分组:这正是上一节视频中提到的谷歌新闻例子,你可以使用聚类算法将新闻文章分组,因此这属于无监督学习。
  • 市场细分:正如之前提到的,你可以将其作为一个无监督学习问题,因为你可以给算法一些数据,让它自动发现市场细分。
  • 糖尿病诊断:这很像监督学习视频中的乳腺癌例子,只是将“良性/恶性肿瘤”换成了“是否患有糖尿病”。因此,你可以像处理乳腺肿瘤分类问题一样,将其作为监督学习问题来处理。

总结与展望 🚀

本节课中,我们一起学习了无监督学习的正式定义,并认识了聚类、异常检测和降维这三种主要类型。尽管本节主要讨论了聚类,但在本系列的后续课程中,我们将更深入地探讨异常检测和降维。

在结束本节之前,我想分享一个我认为非常令人兴奋且有用的工具——Jupyter Notebook 在机器学习中的应用。让我们在下一个视频中一探究竟。

8:Jupyter笔记本入门指南 🚀

在本节课中,我们将学习机器学习领域最广泛使用的工具之一——Jupyter笔记本。我们将了解它的基本结构、两种核心单元格类型,并学习如何运行和修改代码。通过实际操作,你将能更直观地理解之前课程中介绍的监督学习和无监督学习概念。


概述

前面的视频介绍了监督学习和无监督学习,并展示了相关示例。

为了让你更深入地理解这些概念,本节课邀请你查看、运行代码,并在未来尝试自己编写代码来实现这些概念。

目前机器学习和数据科学从业者最广泛使用的工具是 Jupyter Notebook。这是一个默认的开发环境,许多人用它来编写实验代码和尝试新想法。在本节课中,你将直接在网页浏览器中使用Jupyter笔记本环境来亲自测试这些概念。

这不是一个简化或虚构的环境。这正是开发者在许多大型公司中使用的、完全相同的Jupyter笔记本工具。


可选实验与实践实验

在本课程中,你会看到一类称为 可选实验 的练习。

以下是可选实验的特点:

  • 它们设计得非常简单,我保证你能轻松完成每一个,因为它们不计分。
  • 你只需要打开它,运行我们提供的代码即可。
  • 通过阅读和运行可选实验中的代码,你将看到机器学习代码是如何运行的。
  • 你应该能够相对快速地完成它们,只需从上到下逐行运行代码。

可选实验完全是可选的,如果你不想做,完全可以不做。但我希望你能看一看,因为运行它们会让你对机器学习算法和代码有更深的体会和更多经验。

从下周开始,还会有一些 实践实验,它们将为你提供自己编写部分代码的机会。但我们下周再讨论这个,现在不用担心。我希望你先完成下一个可选实验,并学完本周的其余内容。


Jupyter笔记本界面初览

让我们看一个笔记本的例子。这是你打开第一个可选实验时会看到的界面。

你可以自由地上下滚动、浏览、将鼠标悬停在不同的菜单上,并查看这里的各种选项。

你可能会注意到笔记本中有两种类型的块,它们也被称为 单元格


两种核心单元格类型

笔记本中有两种类型的单元格。

第一种是 Markdown单元格,它基本上是一堆文本。在这里,如果你不喜欢我们写的文本,实际上可以编辑它。这些文本用于描述代码。

第二种类型的块或单元格看起来像这样,它是一个 代码单元格

这里我们已经提供了代码。如果你想运行这个代码单元格,按 Shift + Enter 将会运行此代码单元格中的代码。

顺便说一下,如果你点击一个Markdown单元格(它显示所有这些格式化标记),也可以在键盘上按 Shift + Enter,这也会将其转换回格式美观的文本。

这个可选实验展示了一些常见的Python代码,之后你可以在自己的Jupyter笔记本中运行它们。


如何操作与练习

当你自己进入这个笔记本时,我希望你做的是:选择单元格并按 Shift + Enter

阅读代码,看看它是否有意义,尝试预测你认为这段代码会做什么,然后按 Shift + Enter

接着看看代码实际做了什么。如果你愿意,可以随意进入并编辑代码,更改代码,然后运行它,看看会发生什么。

如果你以前没有在Jupyter笔记本环境中操作过,我希望你能更加熟悉Python和Jupyter笔记本。我花了大量时间在Jupyter笔记本中探索,因此我也希望你能从中获得乐趣。


总结

本节课中,我们一起学习了Jupyter笔记本的基本使用。我们了解了它是机器学习实践的核心工具,区分了可选实验实践实验,并重点掌握了笔记本中两种核心单元格:Markdown单元格(用于文本描述)和代码单元格(用于编写和执行代码)。关键的操作是使用 Shift + Enter 来运行单元格。

完成这些后,我期待在下一个视频中与你再见。在那里,我们将选取一个监督学习问题,并开始构建我们的第一个监督学习算法。我希望那也会很有趣,期待在那里见到你。😊

9:线性回归模型(第一部分)🏠📈

在本节课中,我们将学习监督学习的整体流程,并介绍本课程的第一个模型——线性回归模型。线性回归模型通过拟合一条直线来预测数据,是当今世界上应用最广泛的学习算法之一。熟悉线性回归后,这里涉及的许多概念也将适用于本专业后续课程中介绍的其他机器学习模型。

监督学习与线性回归简介

上一节我们概述了课程内容,本节中我们来看看如何应用线性回归解决一个具体问题。假设你想根据房屋面积来预测其价格,这是我们本周早些时候看到的一个例子。我们将使用美国波特兰市的房屋面积和价格数据。下图展示了这些数据,其中横轴代表房屋面积(平方英尺),纵轴代表房屋价格(千美元)。

我们在图中绘制了数据集中各个房屋的数据点。每个数据点(这些小叉号)代表一栋房屋,包含其面积和最近的售价。

现在,假设你是波特兰的一名房地产经纪人,正在帮助一位客户出售她的房屋。她问你:“你认为这栋房子能卖多少钱?”这个数据集可以帮助你估算她可能获得的价格。你首先测量了房屋的面积,结果是1250平方英尺。你认为这栋房子能卖多少钱?一种方法是根据数据集构建一个线性回归模型。你的模型将为数据拟合一条直线,可能如下图所示。

基于这条拟合数据的直线,你可以看到,如果一栋房屋的面积是1250平方英尺,它将在此处与拟合线相交。如果你将其追踪到左侧的纵轴,可以看到价格大约在22万美元左右。这就是一个监督学习模型的例子。

我们称之为监督学习,因为你首先通过提供包含正确答案的数据来训练模型。你为模型提供了房屋面积以及模型应为每栋房屋预测的价格的示例。


在这里,数据集中每栋房屋的价格(即正确答案)是已知的。这个线性回归模型是一种特定类型的监督学习模型,被称为回归模型,因为它预测数字作为输出,例如以美元计的价格。任何预测数字(如220000、1.5或-33.2)的监督学习模型都在解决所谓的回归问题。因此,线性回归是回归模型的一个例子,但还有其他模型用于解决回归问题,我们将在本专业的第二门课程中看到其中一些。


提醒一下,与回归模型相对,另一种最常见的监督学习模型称为分类模型。分类模型预测类别或离散类别,例如预测一张图片是猫(喵)还是狗(汪)。



或者,给定医疗记录,预测患者是否患有特定疾病。你将在本课程后面了解更多关于分类模型的内容。

因此,关于分类和回归的区别,需要记住的是:在分类中,只有少量可能的输出。如果你的模型识别猫与狗,那就是两种可能的输出;或者你可能试图识别患者10种可能的医疗状况中的任何一种。所以,如果存在一个离散的、有限的可能输出集合,我们称之为分类问题。而在回归中,模型可以输出的数字有无限多种可能。

数据的两种表示方式

除了将数据可视化为左侧的图表外,还有另一种查看数据的有用方式,那就是右侧的数据表。数据包含一组输入(这是房屋面积,即此列)和输出(你试图预测的价格,即此列)。


注意,横轴和纵轴对应于这两列:面积和价格。


因此,如果数据表中有47行,那么左侧图表上就有47个这样的叉号,每个叉号对应表中的一行。

例如,表的第一行是一栋面积为2104平方英尺的房屋。所以,它大约在这里。这栋房屋以40万美元售出,大约在这里。因此,表的第一行被绘制为此处的数据点。




机器学习标准符号

现在,让我们看看描述数据的一些符号。这些符号在你整个机器学习旅程中都会很有用。随着你对机器学习术语越来越熟悉,这些术语也将用于你与他人讨论机器学习概念,因为其中很多在人工智能领域是相当标准的。你将在本专业中多次看到这些符号,所以第一次看时记不住所有内容也没关系,随着时间的推移自然会更加熟悉。

你刚刚看到的、用于训练模型的数据称为训练集


注意,你客户的房屋不在此数据集中,因为它尚未售出,所以没有人知道它的价格。因此,要预测你客户房屋的价格,你首先训练模型从训练集中学习,然后该模型可以预测你客户房屋的价格。

在机器学习中,表示输入的标准符号是小写字母 x,我们称之为输入变量,也称为特征输入特征。例如,对于训练集中的第一栋房屋,x 是房屋的面积,所以 x = 2104

表示输出变量(即你试图预测的变量,有时也称为目标变量)的标准符号是小写字母 y。因此,这里 y 是房屋的价格。对于第一个训练示例,y = 400

数据中每栋房屋对应一行,在这个训练集中,有47行,每行代表一个不同的训练示例


我们将使用小写字母 m 来指代训练示例的总数,因此这里 m = 47

为了表示单个训练示例,我们将使用符号 (x, y)。因此,对于第一个训练示例,(x, y) 这对数字是 (2104, 400)

现在我们有很多不同的训练示例,实际上有47个。为了指代特定的训练示例(这对应于左侧表中的特定行),我将使用符号 x⁽ⁱ⁾, y⁽ⁱ⁾

上标告诉我们这是第 i 个训练示例,例如第一个、第二个或第三个,直到第47个训练示例。这里的 i 指的是表中的特定行。

例如,这是第一个示例。当训练集中 i = 1 时,x⁽¹⁾ = 2104y⁽¹⁾ = 400。我们在这里也添加上标¹。请注意,这个带括号的上标 i 不是指数运算。所以当我写这个时,这不是 x²,也不是 x 的 2 次方。它只是指第二个训练示例。因此,这个 i 只是训练集中的一个索引,指的是表中的第 i 行。

总结

本节课中我们一起学习了监督学习的基本流程,并深入探讨了线性回归模型。我们通过一个预测房屋价格的例子,了解了如何用训练集数据拟合一条直线来进行预测。我们还学习了机器学习中的标准符号,包括训练集、输入变量 x、输出变量 y、训练示例数量 m 以及表示单个训练示例的 (x⁽ⁱ⁾, y⁽ⁱ⁾)。这些概念和符号是理解更复杂机器学习模型的基础。在下一个视频中,我们将探讨如何将这个训练集输入学习算法,以便算法能从这些数据中学习。

10:线性回归模型第二部分 🏠➡️💰

在本节课中,我们将要学习监督学习算法是如何工作的。具体来说,我们将了解算法如何处理数据集,以及它最终会输出什么。通过这个过程,我们将建立起对线性回归模型的直观理解。

监督学习的工作流程

上一节我们介绍了监督学习的基本概念。本节中我们来看看一个监督学习算法的具体工作流程。

监督学习中的训练集同时包含输入特征(例如房屋面积)和输出目标(例如房屋价格)。输出目标是模型将要学习的正确答案。

为了训练模型,你需要将训练集(包括输入特征和输出目标)输入到你的学习算法中。然后,你的监督学习算法会产生一个函数。

我们将这个函数写作小写的 f,其中 f 代表函数。这个函数过去常被称为“假设”,但在本课程中,我将其称为函数 f。函数 f 的任务是接收一个新的输入 x,并输出一个估计值或预测值,我将其称为 ŷ(读作“y hat”)。

在机器学习中,惯例是 ŷ 代表对 y 的估计或预测。函数 f 被称为模型x 被称为输入输入特征,模型的输出是预测值 ŷ。模型的预测是 y 的估计值。当符号只是字母 y 时,它指的是目标,即训练集中的实际真实值。相比之下,ŷ 是一个估计值,它可能等于也可能不等于实际真实值。

例如,如果你在帮助客户卖房,那么房屋的真实价格在他们售出之前是未知的。因此,你的模型 f 根据面积输出一个价格,这个价格是对真实价格的估计或预测。

如何表示函数 f

当我们设计学习算法时,一个关键问题是:我们将如何表示函数 f?换句话说,我们将使用什么数学公式来计算 f

目前,我们假设 f 是一条直线。因此,你的函数可以写成:
f_{w,b}(x) = w * x + b

我稍后会定义 wb。现在你只需要知道 wb 是数字,为 wb 选择的值将根据输入特征 x 决定预测值 ŷ。所以,f_{w,b}(x) 表示 f 是一个以 x 为输入的函数,根据 wb 的值,f 将输出某个预测值 ŷ

有时,为了简化,我会直接写成 f(x),而不显式地在下标中包含 wb。这只是一种简化的表示法,其含义与 f_{w,b}(x) 完全相同。

可视化线性回归模型

让我们将训练集绘制在图表上,其中输入特征 x 在横轴上,输出目标 y 在纵轴上。算法从这些数据中学习,并生成一条最佳拟合线,可能就像这里显示的这条。

这条直线就是线性函数:f_{w,b}(x) = w * x + b,或者更简单地写作 f(x) = w * x + b。这个函数的作用是使用 x 的直线函数来预测 y 的值。

你可能会问,为什么我们选择线性函数(直线)而不是像曲线或抛物线那样的非线性函数呢?有时,你确实希望拟合更复杂的非线性函数,比如一条曲线。但由于线性函数相对简单且易于处理,我们将其作为基础,这最终将帮助你理解更复杂的非线性模型。

模型名称:线性回归

这个特定的模型有一个名称,叫做线性回归。更具体地说,这是单变量线性回归,其中“单变量”意味着只有一个输入变量或特征 x,即房屋的面积。单输入变量线性模型的另一个名称是一元线性回归,其中“uni”在拉丁语中意为“一”,“variate”意为“变量”,所以“univariate”只是“一个变量”的一种花哨说法。

在后面的视频中,你还会看到回归的另一种形式,即你希望基于不止房屋面积,而是基于你可能知道的关于房屋的其他一系列信息(如卧室数量和其他特征)来进行预测。

顺便提一下,看完这个视频后,还有一个可选的实验。你不需要编写任何代码,只需查看并运行代码,看看它做了什么。这个实验将向你展示如何在 Python 中定义一个直线函数,并让你尝试选择 wb 的值来拟合训练数据。如果你不想做这个实验,可以不做,但我希望你在看完这个视频后能尝试一下。

引入成本函数

以上就是线性回归的基本概念。为了让这个模型真正工作,你必须做的最重要的事情之一就是构建一个成本函数。成本函数的概念是机器学习中最普遍和最重要的思想之一,它既用于线性回归,也用于训练世界上许多最先进的人工智能模型。

本节课中我们一起学习了监督学习算法的工作流程,特别是线性回归模型的表示方法。我们了解到,模型 f 是一个以 wb 为参数的线性函数,用于根据输入 x 预测输出 ŷ。我们还介绍了线性回归(特别是单变量线性回归)的基本概念。为了评估和优化模型,我们需要一个关键工具——成本函数,这将是下一节的重点。

11:成本函数公式 📊

在本节课中,我们将学习线性回归中的一个核心概念:成本函数。成本函数用于衡量模型预测的准确性,帮助我们找到最优的模型参数。

概述

为了实施线性回归,首要关键步骤是定义成本函数。成本函数能够告诉我们模型的性能,从而指导我们改进模型。接下来,我们将详细探讨成本函数的含义及其计算方法。

模型与参数

回忆一下,我们有一个训练集,包含输入特征 X 和输出目标 Y

用于拟合该训练集的模型是线性函数:

f_wb(x) = w * x + b

其中,wb 被称为模型的参数。

在机器学习中,模型参数是可以在训练过程中调整以改进模型的变量。有时,参数 wb 也被称为系数或权重。

参数的作用

现在,让我们看看参数 wb 的作用。

根据为 wb 选择的不同值,会得到不同的函数 f(x),从而在图上生成不同的直线。记住,我们可以将 f(x) 写作 f_wb(x) 的简写。

我们将查看一些 f(x) 在图表上的示例。也许你已经熟悉在图表上绘制直线,但即使这对你来说是复习,也希望这能帮助你建立关于参数 wb 如何决定 f 的直觉。

  • w = 0b = 1.5 时,f 看起来像一条水平线。在这种情况下,函数 f(x) = 0 * x + 1.5,所以 f 始终是一个常数值,它总是预测 y 的估计值为 1.5。因此,ŷ 总是等于 b。在这里,b 也被称为 y 轴截距,因为这是它与垂直轴(即图上的 y 轴)相交的地方。
  • 第二个例子:如果 w = 0.5b = 0,那么 f(x) = 0.5 * x。当 x = 0 时,预测值也是 0;当 x = 2 时,预测值是 0.5 * 2 = 1。这样就得到一条看起来像这样的线。注意,斜率是 0.5。所以,w 的值给出了直线的斜率。
  • 最后,如果 w = 0.5b = 1,那么 f(x) = 0.5 * x + 1。当 x = 0 时,f(x) = b = 1,所以直线在 b(y 轴截距)处与垂直轴相交。同时,当 x = 2 时,f(x) = 2,所以直线看起来像这样。同样,斜率是 0.5,由 w 的值给出。

拟合数据的目标

回忆一下,你有一个如上图所示的训练集。在线性回归中,我们希望选择参数 wb 的值,使得函数 f 得到的直线能够很好地拟合数据,例如图中所示的这条线。

当我说直线在视觉上拟合数据时,你可以理解为:与其他可能的、离这些点不那么近的直线相比,由 f 定义的直线大致穿过或接近这些训练样本。

提醒一下符号表示:一个训练样本(例如这里的这个点)由 (xⁱ, yⁱ) 定义,其中 yⁱ 是给定输入 xⁱ 的目标值。

函数 f 也会为 y 生成一个预测值,它预测的 y 值是 ŷⁱ。对于我们选择的模型,f(xⁱ) = w * xⁱ + b。换句话说,预测值 ŷⁱ = f_wb(xⁱ),其中对于我们的模型,f(xⁱ) = w * xⁱ + b

那么现在的问题是:如何找到 wb 的值,使得对于许多(甚至所有)训练样本 (xⁱ, yⁱ),预测值 ŷⁱ 都接近真实目标值 yⁱ

定义成本函数

为了回答这个问题,我们首先看看如何衡量一条直线对训练数据的拟合程度。为此,我们将构建成本函数。

成本函数接收预测值 ŷ 并将其与目标值 y 进行比较,计算 ŷ - y。这个差值称为误差,我们用它来衡量预测值与目标值之间的差距。

接下来,我们计算这个误差的平方。同时,我们希望为训练集中的不同训练样本 i 计算这个平方误差项。例如,在测量样本 i 的误差时,我们会计算这个平方误差项。

最后,我们希望衡量整个训练集上的误差。具体来说,我们像这样对平方误差求和:

从 i = 1 到 M 求和 (ŷⁱ - yⁱ)²

记住,M 是训练样本的数量(对于这个数据集是 47)。注意,当训练样本更多(M 更大)时,你的成本函数会计算出一个更大的数字,因为它对更多样本求和。

因此,为了构建一个不会随着训练集规模增大而自动变大的成本函数,按照惯例,我们将计算平均平方误差,而不是总平方误差。我们通过除以 M 来实现这一点。

我们快完成了。还有最后一件事:按照惯例,机器学习人员使用的成本函数实际上会除以 2M。额外的除以 2 只是为了让我们后续的一些计算更简洁,但无论是否包含这个除以 2,成本函数仍然有效。

所以,这里的这个表达式就是成本函数,我们将用 J(w, b) 来指代它。这也被称为平方误差成本函数,之所以这样命名,是因为你对这些误差项取了平方。

在机器学习中,不同的人会为不同的应用使用不同的成本函数,但平方误差成本函数是线性回归(以及所有回归问题)中最常用的一个,因为它似乎能为许多应用提供良好的结果。

提醒一下,预测值 ŷ = f(x)。因此,我们可以将成本函数 J(w, b) 重写为:

J(w, b) = (1 / (2M)) * Σ (f(xⁱ) - yⁱ)²

其中求和从 i = 1 到 M。

最终,我们希望找到能使成本函数 J(w, b) 值较小的 wb

总结

本节课中,我们一起学习了线性回归成本函数的核心概念。我们了解到,成本函数 J(w, b) 通过计算预测值与真实值之间的平均平方误差,来量化模型对训练数据的拟合程度。其公式为:

J(w, b) = (1 / (2M)) * Σ (ŷⁱ - yⁱ)²

其中 ŷⁱ = w * xⁱ + b。成本函数的值越小,表示模型的预测越准确。在接下来的课程中,我们将探讨如何利用成本函数来寻找最优的参数 wb

12:成本函数直观理解 📊

在本节课中,我们将学习成本函数的直观理解。我们将通过一个简化示例,展示成本函数如何帮助找到模型的最佳参数。我们将看到,通过调整参数,成本函数如何变化,并最终找到使成本最小的参数值。


成本函数回顾

上一节我们介绍了成本函数的数学定义。本节中,我们来看看成本函数在实际中是如何工作的。

成本函数 J 用于衡量模型预测值与实际值之间的差异。线性回归的目标是找到参数 WB,使得成本函数 J(W, B) 最小化。数学上表示为:

[
\min_{W, B} J(W, B)
]


简化模型:仅使用参数 W

为了更好地可视化成本函数,我们使用一个简化模型:F_W(x) = W * x。这个模型去掉了参数 B(即设 B = 0),因此只包含一个参数 W。此时,成本函数 J 仅依赖于 W

[
J(W) = \frac{1}{2m} \sum_{i=1}^{m} (W \cdot x^{(i)} - y{(i)})2
]

我们的目标是找到使 J(W) 最小的 W 值。


可视化模型与成本函数

我们将并排绘制模型 F_W(x) 和成本函数 J(W) 的图形,以展示它们之间的关系。

训练数据

假设训练集包含三个点:(1,1), (2,2), (3,3)。

当 W = 1 时

模型 F_W(x) 是一条斜率为 1 的直线,完美穿过所有数据点。此时,每个数据点的预测误差为 0,因此成本函数 J(1) = 0

当 W = 0.5 时

模型 F_W(x) 是一条斜率为 0.5 的直线。此时,预测值与实际值之间存在误差。通过计算,我们得到 J(0.5) ≈ 0.58

当 W = 0 时

模型 F_W(x) 是一条水平线(斜率为 0)。此时,预测误差较大,计算得 J(0) ≈ 2.33

当 W = -0.5 时

模型 F_W(x) 是一条向下倾斜的直线。此时,预测误差更大,计算得 J(-0.5) ≈ 5.25


成本函数图形

通过计算不同 W 值对应的成本,我们可以绘制出成本函数 J(W) 的图形。以下是不同 W 值对应的成本点:

  • W = 1J = 0
  • W = 0.5J ≈ 0.58
  • W = 0J ≈ 2.33
  • W = -0.5J ≈ 5.25

将这些点连接起来,我们可以看到成本函数 J(W) 是一个开口向上的抛物线,最小值出现在 W = 1 处。


如何选择最佳参数

成本函数 J(W) 的最小值对应着使模型拟合数据最好的 W 值。在这个例子中,W = 1 使得成本最小,因此是最佳选择。

对于完整的线性回归模型(包含参数 WB),目标同样是找到使成本函数 J(W, B) 最小的参数值。


总结

本节课中,我们一起学习了成本函数的直观理解。通过简化模型,我们看到了不同参数值如何影响模型拟合和成本函数。成本函数的最小值对应着最佳参数,从而使模型能够最好地拟合数据。

在下一节课中,我们将可视化包含两个参数 WB 的完整线性回归模型的成本函数,并探索其三维图形。让我们继续前进!

13:可视化成本函数 🎯

在本节课中,我们将学习如何可视化线性回归中的成本函数。通过直观的图形,你将更好地理解成本函数如何随模型参数变化,以及如何找到使成本最小化的参数值。


成本函数回顾

上一节我们介绍了成本函数 J(W, B) 的基本概念。成本函数用于衡量模型预测值与实际值之间的差异,我们的目标是通过调整参数 WB 来最小化这个差异。

在上一节中,为了简化可视化,我们暂时将 B 设为 0。本节中,我们将回到包含两个参数的原始模型,并探索成本函数的更丰富可视化形式。


模型与成本函数

以下是我们的线性回归模型:

模型公式:

f(x) = W * x + B

成本函数公式(均方误差):

J(W, B) = (1/2m) * Σ (f(x_i) - y_i)^2

其中:

  • WB 是模型参数
  • m 是训练样本数量
  • x_i 是第 i 个输入特征
  • y_i 是第 i 个真实标签

我们的目标是找到使 J(W, B) 最小化的 WB 值。


三维曲面图

当模型有两个参数 WB 时,成本函数 J(W, B) 可以表示为一个三维曲面。这个曲面通常呈碗状(或吊床状),其最低点对应成本函数的最小值。

在这个三维图中:

  • 水平轴:参数 W
  • 垂直轴:参数 B
  • 高度轴:成本函数值 J(W, B)

曲面上每个点代表一对特定的 WB 值,以及对应的成本函数值。例如,当 W = -10B = -15 时,曲面上对应点的高度就是 J(-10, -15) 的值。


等高线图

为了更清晰地观察成本函数的细节,我们常用等高线图来替代三维曲面图。等高线图通过水平切片展示三维曲面,每个椭圆(或等高线)代表成本函数值相同的点集合。

在等高线图中:

  • 水平轴:参数 W
  • 垂直轴:参数 B
  • 每个椭圆:所有具有相同 J(W, B) 值的点

例如,图中三个标记点虽然对应不同的 WB 值,但它们的成本函数值相同。这三个点对应的线性函数(见左上图)在预测房价时表现都不佳。


理解等高线图

如果你不熟悉等高线图,可以想象自己正从高空俯视一个碗形曲面。碗的底部(成本函数最小值)位于最小椭圆的中心。随着椭圆向外扩展,成本函数值逐渐增加。

等高线图是一种在二维平面上可视化三维成本函数的便捷方式。通过观察椭圆分布,我们可以直观判断参数调整方向,从而更快地找到成本函数的最小值。


总结

本节课中,我们一起学习了成本函数的两种可视化方法:

  1. 三维曲面图:直观展示成本函数随两个参数变化的整体形状。
  2. 等高线图:通过水平切片展示相同成本值的点,便于定位最小值。

这些可视化工具帮助我们理解线性回归中参数调整如何影响成本函数,并为后续的优化算法(如梯度下降)奠定基础。下一节中,我们将观察不同 WB 值如何影响拟合直线,进一步巩固对成本函数的理解。

14:可视化理解成本函数与模型拟合 📊

在本节课中,我们将通过一系列可视化示例,直观地理解线性回归模型中参数 wb 的选择如何影响模型对数据的拟合程度,以及这些选择如何反映在成本函数 J(w, b) 的图形上。我们将看到,寻找最佳拟合直线的过程,本质上就是寻找使成本函数最小化的参数值。


示例一:较差的拟合

上一节我们介绍了成本函数的概念,本节中我们来看看具体的可视化示例。首先,我们观察一个拟合效果较差的例子。

在成本函数 J(w, b) 的等高线图中,有一个特定的点。对于这个点,w 约等于 -0.15,b 约等于 800。这对 (w, b) 值对应着一个特定的成本 J

事实上,这对 (w, b) 值对应着左侧的函数 f(x),即图中的这条直线。这条直线在纵轴上的截距是 800,因为 b = 800;直线的斜率是 -0.15,因为 w = -0.15

现在,观察训练集中的数据点,可以发现这条直线对数据的拟合效果不佳。对于这个 f(x) 函数,其预测的 y 值与训练数据中实际的 y 值相差甚远。

由于这条直线拟合效果差,在成本函数 J 的图形上,其对应的成本值位于此处,距离最小值相当远。这是一个相当高的成本,因为这对 (w, b) 的选择对训练集的拟合效果不好。


示例二:稍好但仍不理想的拟合

接下来,我们看另一个不同的 (w, b) 选择示例。

这是另一个函数,它仍然不是数据的理想拟合,但可能比上一个例子稍好一些。因此,图中这个点代表了产生那条直线的 (w, b) 参数对对应的成本。

此时,w 的值等于 0,b 的值约为 360。这对参数对应着这个函数,它是一条水平直线,因为 f(x) = 0 * x + 360


示例三:更差的拟合

让我们再看一个例子。这是 (w, b) 的另一个选择,使用这些值,最终得到这条直线 f(x)

同样,它对数据的拟合效果不佳,实际上,与上一个例子相比,它距离最小值更远。请记住,最小值位于最小椭圆的中心。


示例四:较好的拟合

最后一个例子,观察左侧的 f(x),这看起来是对训练集的一个相当好的拟合。你可以在右侧看到,代表成本的点非常接近小椭圆的中心,虽然不是精确的最小值,但已经非常接近。

对于这对 (w, b) 值,你得到了这条直线 f(x)。可以看到,如果测量数据点与直线上预测值之间的垂直距离,就得到了每个数据点的误差。所有这些数据点的误差平方和,在所有可能的直线拟合中,已经非常接近可能的最小误差平方和。


可视化总结

希望通过观察这些图形,你能更好地理解参数的不同选择如何影响直线 f(x),以及这如何对应于成本函数 J 的不同值。同时,希望你能看到,拟合效果更好的直线对应于成本函数 J 图形上更接近可能的最小成本的点。


可选实验:动手探索

在本视频之后的可选实验中,你将运行一些代码(所有代码都已提供,你只需按 Shift+Enter 运行并查看)。实验将向你展示成本函数在代码中是如何实现的。给定一个小型训练集和不同的参数选择,你将能够看到成本如何根据模型对数据的拟合程度而变化。

在可选实验中,你还可以操作一个交互式等高线图。你可以使用鼠标光标点击等高线图上的任意位置,然后会看到由你选择的参数 wb 的值定义的直线。

你还会看到一个点出现在显示成本的 3D 曲面图上。最后,可选实验还有一个 3D 曲面图,你可以使用鼠标光标手动旋转和环绕,以便更好地观察成本函数的样子。

希望你能享受操作可选实验的乐趣。


从可视化到算法

在线性回归中,与其手动尝试从等高线图中读取 wb 的最佳值(这并非一个好方法,并且一旦我们遇到更复杂的机器学习模型,这种方法将不再适用),我们真正需要的是一个高效的算法,可以编写成代码,自动找到使成本函数 J 最小化的参数 wb,从而得到最佳拟合直线。

存在一种用于实现此目的的算法,称为梯度下降。该算法是机器学习中最重要的算法之一。梯度下降及其变体不仅用于训练线性回归,还用于训练人工智能中一些最大、最复杂的模型。


本节课总结

本节课中,我们一起学习了如何通过可视化图形,将参数 (w, b)、预测函数 f(x) 和成本函数 J(w, b) 联系起来。我们看到了不同的参数选择会导致不同的拟合直线和成本值,而最佳拟合对应于成本函数的最小值附近。这为我们理解后续将学习的、用于自动寻找这些最优参数的梯度下降算法奠定了直观的基础。

15:梯度下降法 🧭

在本节课中,我们将要学习一种名为“梯度下降”的算法。这是一种用于寻找函数最小值的系统化方法,在机器学习中应用广泛,不仅是线性回归,也是训练高级神经网络模型(深度学习)的基础。

概述

上一节我们介绍了成本函数 J 的可视化,以及如何通过尝试不同的参数 W 和 B 来获得不同的成本值。我们希望能有一种更系统的方法,来找到使成本函数 J(W, B) 值最小的 W 和 B。

事实证明,有一种叫做“梯度下降”的算法可以用来实现这个目标。梯度下降在机器学习中无处不在,不仅用于线性回归,也用于训练最先进的神经网络模型(即深度学习模型)。深度学习模型是你在第二门课程中会学习的内容。因此,学习梯度下降将为你奠定机器学习中最重要的基石之一。

以下是梯度下降的概述:你有一个想要最小化的成本函数 J(W, B)。在我们目前看到的例子中,这是线性回归的成本函数。但事实证明,梯度下降是一种可以用来尝试最小化任何函数的算法,而不仅仅是线性回归的成本函数。

为了使关于梯度下降的讨论更具一般性,梯度下降适用于更一般的函数,包括那些具有两个以上参数的模型所使用的其他成本函数。

例如,如果你有一个成本函数 J,它是参数 W1, W2, ..., WN 和 B 的函数。你的目标是在参数 W1 到 WN 和 B 上最小化 J。换句话说,你想为 W1 到 WN 和 B 选择一组值,使 J 的值尽可能小。事实证明,梯度下降也是一种可以用来尝试最小化这个成本函数 J 的算法。

梯度下降的工作原理

你要做的是,从对 W 和 B 的某个初始猜测开始。在线性回归中,初始值是什么通常不太重要,所以一个常见的选择是将它们都设为 0。例如,你可以设 W=0 和 B=0 作为初始猜测。

使用梯度下降算法,你将不断一点点地改变参数 W 和 B,试图降低成本 J(W, B),直到 J 稳定在或接近一个最小值。

需要说明的一点是,对于某些函数 J,其形状可能不是“碗状”或“吊床状”,可能存在不止一个可能的最小值。

可视化梯度下降过程

让我们看一个更复杂的曲面图 J 的例子,来了解梯度下降在做什么。这个函数不是线性回归的平方误差成本函数。对于平方误差成本函数,你总是会得到一个碗状或吊床状的图形。但如果你在训练神经网络模型,你可能会得到这种类型的成本函数。

请注意坐标轴。底部坐标轴是 W 和 B。对于 W 和 B 的不同值,你在曲面 J(W, B) 上得到不同的点,而曲面在某点的高度就是成本函数的值。

现在,让我们想象这个曲面图实际上是一个略有起伏的户外公园或高尔夫球场的视图,高点是小山,低点是山谷。

请你想象一下,你正物理地站在这座山上的这个点。为了帮助你放松,想象这里有非常漂亮的绿草、蝴蝶和鲜花,是一座非常漂亮的山。你的目标是从这里出发,尽可能高效地到达其中一个山谷的底部。

梯度下降算法的作用是:你将原地旋转 360 度,环顾四周。如果我要朝某个方向迈出非常小的一步,并且我希望尽快下山,走向其中一个山谷。我应该选择哪个方向来迈出这一小步?

事实证明,如果你想尽可能高效地下山,如果你站在山上的这个点并环顾四周,你可能会注意到,下山的最佳方向大致是那个方向。在数学上,这是最陡下降的方向。这意味着,当你迈出一小步时,这一步带你下山的速度,比你朝任何其他方向迈出的一小步都要快。

所以,在迈出这第一步之后,你现在到了山上的这个点。

现在让我们重复这个过程。站在这个新点上,你将再次旋转 360 度,问自己:为了向下移动,我应该朝哪个方向迈出下一小步?如果你这样做并迈出另一步,你会朝那个方向移动一点,然后可以继续前进。

从这个新点,你可以再次环顾四周,决定哪个方向能让你最快地下山。再迈一步,又一步,如此继续,直到你发现自己位于这个山谷的底部,也就是这里的这个局部最小值。

你刚才所做的就是进行了多步梯度下降。

梯度下降的一个有趣特性

请记住,你可以通过选择参数 W 和 B 的起始值来选择曲面上的一个起始点。刚才你执行梯度下降时,是从这里的这个点开始的,对吧?

现在,想象一下,如果你再次尝试梯度下降,但这次你通过选择参数将起始点设置在右边几步远的地方。

如果你然后重复梯度下降过程,这意味着你环顾四周,朝最陡下降的方向迈出一小步。所以你最终到达这里,然后你再次环顾四周,再迈一步,依此类推。如果你第二次运行梯度下降,从我们第一次运行的位置向右几步远的地方开始,那么你最终会到达一个完全不同的山谷,即右边这个不同的最小值。

第一个和第二个山谷的底部都被称为局部最小值。因为如果你开始下第一个山谷,梯度下降不会引导你到第二个山谷;同样,如果你开始下第二个山谷,你会停留在那个最小值中,而不会找到进入第一个局部最小值的路径。

所以这是梯度下降算法的一个有趣特性,你将在后面看到更多相关内容。

在本视频中,你看到了梯度下降如何帮助你下山。在下一个视频中,让我们看看可以实现梯度下降工作的数学表达式。让我们继续下一个视频。

总结

本节课中我们一起学习了梯度下降算法的基本概念。它是一种通过迭代调整参数(如 W 和 B)来最小化成本函数 J 的系统方法。我们从初始猜测出发,反复计算最陡下降方向并迈出一小步,从而逐步逼近函数的最小值。我们还了解到,对于非凸函数,可能存在多个局部最小值,梯度下降的最终结果可能取决于起始点的选择。这为我们后续学习其数学实现奠定了基础。

16:实现梯度下降法 🧠

在本节课中,我们将学习如何实际实现梯度下降算法。我们将详细拆解算法的每一步,理解其核心概念,并确保以正确的方式更新参数。


梯度下降算法公式

梯度下降算法的核心公式如下:

在每一步中,参数 w 都会根据以下规则更新:新的 w 等于旧的 w 减去学习率 α 乘以成本函数 J(w, b)w 的偏导数。

这个表达式的含义是:通过取 w 的当前值,并减去右侧的表达式(α 乘以导数项)来调整参数 w

如果你觉得这个公式包含很多内容,不用担心,我们将一起逐步解析它。


理解等号的含义

首先,我们需要注意这里的等号。

在这个上下文中,等号是赋值运算符。具体来说,在编程中,a = c 意味着将 c 的值存储到变量 a 中。而 a = a + 1 意味着将 a 的值增加 1。

这与数学中的真值断言不同。在数学中,a = c 是断言 ac 的值相等。在编程语言(如Python)中,真值断言通常写作 a == c

在本课程中,我会尽量明确等号是用于赋值还是断言。


学习率 α 的作用

公式中的符号 α 是希腊字母,在这里被称为学习率

学习率通常是一个介于 0 和 1 之间的小正数,例如 0.01。

α 的作用是控制你下山(即向成本函数最小值移动)的步长大小。

  • 如果 α 很大,意味着你采取激进的梯度下降步骤,试图大步下山。
  • 如果 α 很小,意味着你采取微小的婴儿步下山。

我们将在后续课程中更深入地探讨如何选择合适的学习率 α


导数项的作用

最后,公式中的这一项是成本函数 J导数项

现在我们先不深入导数的细节,你可以暂时将这个用洋红色框标出的导数项理解为:它告诉你想朝哪个方向迈出婴儿步。结合学习率 α,它也决定了你下山步长的大小。

需要说明的是,导数源于微积分。但即使你不熟悉微积分,也完全不用担心。在本课程中,你无需任何微积分知识就能理解并实现梯度下降。


同时更新两个参数

记住,你的模型有两个参数:wb

因此,你还需要一个类似的赋值操作来更新参数 b

b := b - α * (∂/∂b) J(w, b)

在梯度下降算法中,你将重复这两个更新步骤,直到算法收敛。收敛意味着你到达了一个局部最低点,此时参数 wb 在每一步后不再发生显著变化。

关于如何正确实现梯度下降,还有一个重要的细节。

以下是正确实现梯度下降的方法,它执行同步更新

  1. 计算右侧表达式,将 w - α * (∂/∂w) J(w, b) 存入临时变量 temp_w
  2. 计算右侧表达式,将 b - α * (∂/∂b) J(w, b) 存入临时变量 temp_b
  3. temp_w 的值赋给 w
  4. temp_b 的值赋给 b

请注意,在计算导数项时,使用的 wb 是更新前的旧值。


错误的更新方式

相比之下,以下是一种不正确的梯度下降实现,它没有进行同步更新:

  1. 计算 temp_w = w - α * (∂/∂w) J(w, b)
  2. 立即w 更新为 temp_w 的值。
  3. 然后计算 temp_b = b - α * (∂/∂b) J(w, b)注意:此时计算导数项使用的 w 已经是新值。
  4. 最后将 b 更新为 temp_b 的值。

这种非同步更新的方式,其右侧的导数项与同步更新中的导数项不同,导致最终更新的参数值也不同。

在代码中实现梯度下降时,更自然的方式是采用正确的同步更新。当人们谈论梯度下降时,总是指的是参数同步更新的版本。

虽然非同步更新可能也能工作,但这并不是正确的实现方式,它实际上是具有不同属性的另一种算法。因此,建议你坚持使用正确的同步更新方法。


总结与预告

本节课中,我们一起学习了梯度下降算法的实现细节,包括其核心公式、学习率的作用、导数项的意义,以及同步更新参数这一关键步骤。

在下一个视频中,我们将深入探讨导数项。即使你不熟悉微积分,也完全不用担心。我们将一起建立对导数的直观理解,并获得实现和应用梯度下降所需的所有知识。掌握如何自己实现它将是一件令人兴奋的事情,让我们进入下一个视频继续学习。

17:梯度下降法直观理解 🧠

在本节课中,我们将深入学习梯度下降法,以更好地理解其工作原理和背后的逻辑。

概述

上一节我们介绍了梯度下降算法的基本形式。本节中,我们将通过更直观的示例,深入探讨梯度下降如何通过调整参数来最小化成本函数。

梯度下降算法回顾

以下是上一节视频中展示的梯度下降算法:

w = w - α * (d/dw) J(w)
b = b - α * (d/db) J(w, b)

其中,希腊字母 α 代表学习率,它控制着更新参数 wb 时的步长大小。而 d/dw 是导数项,在数学中通常用特殊字体表示。对于多变量微积分专家来说,这实际上是偏导数,但为了机器学习算法的实现,我们简称为导数。

单参数简化示例

为了更直观地理解学习率和导数的作用,我们使用一个更简单的例子:只优化一个参数 w

假设我们有一个成本函数 J(w),其中 w 是一个数值。这意味着梯度下降现在简化为:

w = w - α * (d/dw) J(w)

我们的目标是通过调整参数 w 来最小化成本 J(w)。这类似于之前将 b 暂时设为零的例子。通过单参数 w,我们可以用二维图形表示成本函数 J(w),而不是三维图形。

梯度下降的直观运作

让我们看看梯度下降在这个 J(w) 函数上是如何工作的。

以下是梯度下降在成本函数 J(w) 上的运作示意图:

在图中,横轴代表参数 w,纵轴代表成本 J(w)。我们首先初始化梯度下降,为 w 选择一个起始值。假设我们从函数 J 上的这个点开始。

梯度下降将按照以下公式更新 w

w = w - α * (d/dw) J(w)

现在,让我们看看这里的导数项意味着什么。

导数项的理解

在曲线上某一点的导数,可以理解为在该点绘制一条切线。这条切线是一条直线,恰好在该点接触曲线。在数学上,这条线的斜率就是函数 J 在该点的导数。

为了计算斜率,可以绘制一个小三角形。例如,如果三角形的高度为2,宽度为1,那么斜率就是2除以1,等于2。当切线向右上方倾斜时,斜率为正,这意味着导数是正数,大于0。

因此,更新后的 w 将是 w 减去学习率乘以某个正数。由于学习率总是正数,所以 w 减去一个正数会导致 w 的新值变小。在图上,这意味着向左移动,减小 w 的值。

您可能会注意到,如果目标是减小成本 J,那么这是正确的做法。因为当我们沿着曲线向左移动时,成本 J 减小,并且我们更接近 J 的最小值,即这里。到目前为止,梯度下降似乎在做正确的事情。

另一个示例

现在,让我们看另一个例子。假设我们使用相同的函数 J(w),但这次我们在不同的位置初始化梯度下降。

例如,选择一个在左侧的起始值 w,即函数 J 的这个点。

导数项仍然是 d/dw J(w)。当我们在这里绘制切线时,这条线向右下方倾斜,因此斜率为负。换句话说,J 在这一点的导数是负数。

例如,如果绘制一个三角形,高度为-2,宽度为1,那么斜率就是-2除以1,等于-2,这是一个负数。

当更新 w 时,我们得到 w 减去学习率乘以一个负数。这意味着我们从 w 中减去一个负数,但减去负数等同于加上一个正数,因此 w 最终会增加。

梯度下降的这一步导致 w 增加,这意味着在图上向右移动,并且成本 J 减小到这里。再次,梯度下降似乎在合理地工作,使您更接近最小值。

学习率 α 的重要性

希望以上两个示例展示了导数项的作用以及它如何帮助梯度下降改变 w 以使您更接近最小值。我希望这个视频让您对梯度下降中导数项的意义有了一些了解。

梯度下降算法中另一个关键量是学习率 α。如何选择 α?如果它太小会发生什么?如果太大又会发生什么?在下一节视频中,我们将更深入地探讨参数 α,以帮助建立对其作用的直观理解,以及如何为梯度下降的实现选择一个合适的 α 值。

以下是关于学习率选择的示意图:

总结

本节课中,我们一起学习了梯度下降法的直观理解。通过单参数示例,我们深入探讨了导数项如何指导参数更新方向,以及学习率在控制更新步长中的关键作用。下一节,我们将进一步研究学习率的选择及其对算法性能的影响。

18:学习率的选择与影响 📈

在本节课中,我们将深入探讨梯度下降算法中的关键参数——学习率。学习率的选择对梯度下降的效率有巨大影响,甚至决定了算法能否成功收敛。我们将分析学习率过小或过大时会发生什么,并解释梯度下降在接近最小值时的行为。

学习率的作用

上一节我们介绍了梯度下降的基本概念,本节中我们来看看学习率的具体作用。梯度下降的更新规则由以下公式表示:

W := W - α * (dJ/dW)

其中 α 就是学习率。它控制了我们在每次迭代中沿着梯度方向移动的步长。

学习率过小的情况

以下是学习率过小时可能出现的问题:

  • 更新步长极小:由于 α 值非常小(例如 0.0000001),每次参数更新只能移动微小的距离。
  • 收敛速度极慢:虽然成本函数 J 的值在下降,但下降过程非常缓慢。
  • 需要大量迭代:算法需要执行非常多的步骤才能接近最小值。

总结来说,如果学习率太小,梯度下降仍然有效,但会异常缓慢,需要很长的计算时间。

学习率过大的情况

现在,让我们看看如果学习率设置得太大,会发生什么情况:

  • 更新步长过大:参数更新会从一个点“跳跃”到很远的另一个点。
  • 可能越过最小值:巨大的步长可能导致算法跳过最小值,到达成本函数值更高的点。
  • 发散风险:在极端情况下,每次迭代都可能使成本 J 变得更大,导致算法无法收敛甚至发散,永远找不到最小值。

因此,过大的学习率会导致梯度下降失效

梯度下降在局部最小值处的行为

你可能会好奇,如果参数 W 已经位于一个局部最小值,梯度下降会怎么做?让我们通过一个例子来分析。

假设成本函数 J 有两个局部最小值。经过若干次梯度下降迭代后,参数 W 到达了其中一个最小值点(例如 W=5)。在这一点上,函数切线的斜率为 0,因此导数项 dJ/dW = 0

此时,梯度下降更新公式变为:
W := W - α * 0
这相当于 W := W

这意味着,当参数到达局部最小值时,梯度下降会保持参数不变,这正是我们期望的结果——算法稳定在解上。

固定学习率下的自动步长调整

这也解释了为什么即使使用固定的学习率 α,梯度下降也能收敛到局部最小值。其原理如下:

  1. 初始阶段步长大:在远离最小值的地方,斜率(导数)的绝对值较大,因此更新步长也较大。
  2. 接近时步长变小:随着参数接近最小值,斜率逐渐趋近于 0。
  3. 自动减速:即使 α 保持不变,由于导数项变小,更新步长也会自动减小,最终以微小的步伐稳定在最小值附近。

这个过程可以用以下伪代码概念来理解:

while not converged:
    slope = compute_gradient(w)  # 计算当前点的梯度(导数)
    w = w - alpha * slope        # 更新参数,步长 = alpha * |slope|

总结与预告

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

  1. 学习率(α) 是梯度下降中的关键超参数,控制更新步长。
  2. 学习率太小会导致收敛速度过慢。
  3. 学习率太大可能导致算法越过最小值甚至发散。
  4. 局部最小值处,导数为零,梯度下降会停止更新。
  5. 即使学习率固定,由于导数在接近最小值时会变小,更新步长也会自动减小,这有助于算法稳定收敛。

在接下来的视频中,我们将把梯度下降算法应用到线性回归的均方误差成本函数上。结合这两者,我们将得到第一个完整的学习算法——线性回归算法

19:线性回归的梯度下降法 🎯

在本节课中,我们将学习如何将线性回归模型、平方误差成本函数以及梯度下降算法结合起来,训练模型以拟合训练数据中的直线。


概述 📋

上一节我们介绍了线性回归模型、成本函数以及梯度下降算法。本节中,我们将把这些概念整合起来,使用平方误差成本函数和梯度下降算法来训练线性回归模型。


模型、成本函数与算法回顾 🔄

线性回归模型为:

f(x) = w * x + b

其平方误差成本函数为:

J(w, b) = (1/(2m)) * Σ (f(x_i) - y_i)²

梯度下降算法通过以下公式迭代更新参数 wb

w = w - α * (∂J/∂w)
b = b - α * (∂J/∂b)

计算梯度 📐

以下是计算成本函数 J 关于参数 wb 的偏导数的公式。

关于 w 的偏导数为:

∂J/∂w = (1/m) * Σ (f(x_i) - y_i) * x_i

关于 b 的偏导数为:

∂J/∂b = (1/m) * Σ (f(x_i) - y_i)

使用这些公式计算梯度并实现梯度下降,算法将有效运行。


公式推导(可选)🧮

你可能会好奇这些公式从何而来。它们是通过微积分推导得出的。如果你想了解完整的推导过程,本部分将简要说明。如果你不记得或不感兴趣,可以完全跳过,这不会影响你实现梯度下降和完成本课程。

首先,计算 J 关于 w 的偏导数。我们从成本函数 J 的定义开始,并代入 f(x_i) = w * x_i + b。根据微积分规则,求导后公式中的系数 2 会与成本函数定义中的 1/2 相抵消,从而得到上述简洁的公式。这正是我们在成本函数中预先定义 1/2 的原因。

对于 b 的偏导数,推导过程类似。代入模型定义后,根据微积分规则求导,同样会抵消系数 2,最终得到不含 x_i 项的导数表达式。


线性回归的梯度下降算法 ⚙️

现在,我们可以将导数公式代入梯度下降算法。

线性回归的梯度下降算法如下:反复执行以下更新,直到参数收敛。

w = w - α * [(1/m) * Σ (f(x_i) - y_i) * x_i]
b = b - α * [(1/m) * Σ (f(x_i) - y_i)]

记住,f(x) 是线性回归模型 w*x + b。每次迭代需要同时更新 wb


梯度下降与凸函数特性 🏔️

我们之前了解到,梯度下降可能会收敛到局部最小值而非全局最小值。全局最小值是成本函数 J 在所有可能点中值最小的点。

然而,当对线性回归使用平方误差成本函数时,情况有所不同。该成本函数呈碗状,只有一个全局最小值,没有其他局部最小值。这种函数的专业术语是“凸函数”。

凸函数是碗形函数,除了单一的全局最小值外,没有其他局部最小值。因此,只要学习率 α 选择得当,在凸函数上实现梯度下降总能收敛到全局最小值。


总结 ✨

本节课中,我们一起学习了如何实现线性回归的梯度下降。我们整合了模型、成本函数和算法,推导了关键的梯度公式,并了解了平方误差成本函数作为凸函数的优良特性,这保证了梯度下降能收敛到最优解。

恭喜你掌握了线性回归梯度下降的实现方法!本周还有一个视频,我们将看到这个算法的实际运行效果。

20:运行梯度下降法 🚀

在本节课中,我们将学习如何运行梯度下降算法来训练线性回归模型。我们将看到算法如何逐步调整参数,使模型更好地拟合数据,并理解“批量梯度下降”这一核心概念。


运行梯度下降法

上一节我们介绍了梯度下降的基本思想,本节中我们来看看当它应用于线性回归时会发生什么。让我们观察算法的实际运行过程。

左上角是模型与数据的拟合图,右上角是成本函数的等高线图。底部则是同一成本函数的三维曲面图。

通常,参数 wb 会被初始化为0。但为了演示,我们将 w 初始化为 -0.1,b 初始化为 900。这对应于初始模型:
f(x) = -0.1x + 900

现在,如果我们使用梯度下降法执行一步更新,成本函数的值会从初始点移动到另一个点,即向右下方移动。同时,拟合的直线也会发生轻微变化。

再执行一步更新,成本函数移动到第三个点,函数 f(x) 也再次调整。随着我们执行更多步骤,每次更新后成本都在下降,参数 wb 沿着一条轨迹移动。

观察左侧,对应的直线拟合效果越来越好,直到我们达到全局最小值。全局最小值对应着这条最终的拟合直线,它对数据有相对较好的拟合效果。这就是梯度下降法。我们将使用它来为房价数据拟合模型。

现在,你可以使用这个训练好的 f(x) 模型来预测客户或其他人的房屋价格。例如,如果你朋友的房子面积为1250平方英尺,你可以根据模型读出预测值,比如可能是25万美元。


批量梯度下降

更准确地说,这个梯度下降过程被称为批量梯度下降

术语“批量梯度下降”指的是,在梯度下降的每一步中,我们都会查看全部训练样本,而不是训练数据的一个子集。

在计算梯度下降的导数时,我们计算的是从 i=1m 的总和。批量梯度下降在每次更新时都会查看整个批次的训练样本。

我知道“批量梯度下降”可能不是最直观的名称,但这是机器学习社区的通用叫法。如果你听说过DeepLearning.AI发布的新闻通讯《The Batch》,它的命名也源于机器学习中的这个概念。

事实证明,还有其他版本的梯度下降法,它们不在每次更新时查看整个训练集,而是查看训练数据的较小子集。但对于线性回归,我们使用批量梯度下降。


课程总结与后续

以上就是线性回归的全部内容。恭喜你完成了第一个机器学习模型的学习。

在本视频随后的可选实验部分,你将回顾梯度下降算法,并学习如何在代码中实现它。

你将看到一个图表,展示成本如何随着训练迭代次数的增加而下降。你还将看到一个等高线图,观察随着梯度下降为参数 wb 找到越来越好的值,成本如何接近全局最小值。

请记住,完成可选实验只需阅读并运行提供的代码,无需自己编写代码。希望你花些时间去做,并熟悉梯度下降的代码,因为这将有助于你在未来自己实现这个以及其他算法。



感谢你坚持学完第一周的最后一个视频,恭喜你一路走到这里,你正在成为一名机器学习实践者的道路上。

除了可选实验,如果你还没有尝试,希望你也完成练习测验。这是检验你对概念理解程度的好方法。第一次没有全部答对也没关系,你可以多次参加测验,直到获得满意的分数。

现在你已经知道如何实现单变量线性回归,这标志着本周课程的结束。

下周,我们将学习如何让线性回归变得更强大。你将学习如何处理多个特征,而不仅仅是像房屋面积这样的单一特征。你还将学习如何拟合非线性曲线。这些改进将使算法更有用、更有价值。😊

最后,我们还将介绍一些实用技巧,这些技巧对于让线性回归在实际应用中真正发挥作用至关重要。

很高兴你能和我一起学习这门课程,期待下周与你再见。😊


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

  1. 如何运行梯度下降算法来优化线性回归模型。
  2. 批量梯度下降的定义,即每次更新使用全部训练数据计算梯度。
  3. 通过可视化观察成本下降和参数优化的过程。
  4. 训练好的模型可用于进行预测。
  5. 了解了后续课程将学习多特征和非线性拟合等更强大的技术。

21:多特征线性回归 🏠

在本节课中,我们将学习如何扩展线性回归模型,使其能够处理多个特征,而不仅仅是单一特征。这将使我们的模型更加强大,能够利用更多信息进行预测。


从单特征到多特征

上一节我们介绍了基于单一特征(如房屋面积)的线性回归模型。本节中,我们来看看当模型拥有多个特征时,会是什么样子。

在最初的线性回归版本中,我们有一个单一特征 X(房屋面积),用于预测目标值 y(房屋价格)。模型为:

f_wb(x) = w * x + b

但是,如果我们不仅知道房屋面积,还知道卧室数量、楼层数和房屋年龄,我们就能获得更多信息来预测价格。

为了引入新特征,我们使用变量 x₁, x₂, x₃, x₄ 来表示这四个特征。为简化起见,我们引入更多符号:

  • 我们用 xⱼ 或简写为 x_subj 来表示特征列表。在本例中,j 从 1 到 4。
  • 我们用小写字母 n 表示特征总数。本例中 n = 4
  • 和之前一样,我们用 x⁽ⁱ⁾ 表示第 i 个训练样本。现在,x⁽ⁱ⁾ 是一个包含四个数字的列表(或称为向量),它包含了第 i 个训练样本的所有特征。

例如,x⁽²⁾ 是第二个训练样本的特征向量,可能等于 [1416, 3, 2, 40]
要引用第 i 个训练样本中的第 j 个特征,我们写作 x⁽ⁱ⁾ⱼ。例如,x⁽²⁾₃ 表示第二个训练样本的第三个特征(楼层数),其值为 2

有时,为了强调 x 是一个向量而非单个数字,我们会在其上方画一个箭头(如 →x),但这在书写时是可选的。


多特征模型定义

现在我们已经有了多个特征,让我们看看模型会如何变化。

之前,模型定义为 f_wb(x) = w * x + b,其中 x 是单个数字。现在,对于多个特征,模型将定义为:

f_wb(x) = w₁ * x₁ + w₂ * x₂ + w₃ * x₃ + w₄ * x₄ + b

具体到房价预测,一个可能的模型可能是:

预测价格 = 0.1 * x₁ + 4 * x₂ + 10 * x₃ - 2 * x₄ + 80

我们可以这样理解这些参数:

  • b = 80:可以理解为房屋的基准价格是 8 万美元(假设房屋没有面积、卧室、楼层和年龄)。
  • w₁ = 0.1:每增加一平方英尺,价格增加 0.1(千美元),即 100 美元。
  • w₂ = 4:每增加一间卧室,价格增加 4(千美元),即 4000 美元。
  • w₃ = 10:每增加一层楼,价格增加 10(千美元),即 1 万美元。
  • w₄ = -2:房龄每增加一年,价格减少 2(千美元),即 2000 美元。

向量化表示法

一般来说,如果我们有 n 个特征,模型如下所示:

f_wb(x) = w₁ * x₁ + w₂ * x₂ + ... + w_n * x_n + b

接下来,我们将引入一种新的表示法,用更简洁但等价的方式重写这个表达式。

我们定义:

  • →w 为一个向量,包含所有参数 [w₁, w₂, ..., w_n]
  • →x 为一个向量,包含所有特征 [x₁, x₂, ..., x_n]
  • b 仍然是一个单独的数字(标量)。

使用这种表示法,模型可以更简洁地重写为:

f_wb(x) = →w · →x + b

这里的 · 代表线性代数中的点积运算。

那么,什么是点积呢?两个向量 →w→x 的点积计算方式如下:

  1. 将对应位置的元素相乘:w₁*x₁, w₂*x₂, ..., w_n*x_n
  2. 将所有乘积结果相加。

写出来就是:

→w · →x = w₁*x₁ + w₂*x₂ + ... + w_n*x_n

然后再加上 b,就得到了和上面完全相同的表达式。因此,点积表示法让我们能用更少的字符、更紧凑的形式来表达模型。


模型命名与总结

这种具有多个输入特征的线性回归模型,被称为多元线性回归。这与只有一个特征的单变量回归形成对比。

请注意,虽然你可能认为它叫“多元回归”,但该术语在统计学中通常指代其他概念(涉及多个因变量),因此我们这里称其为多元线性回归

本节课中,我们一起学习了如何将线性回归扩展到多个特征,即多元线性回归。我们引入了新的符号来表示特征和参数向量,并学习了如何使用向量点积来简洁地定义模型。

为了高效地实现这个模型,有一个非常巧妙的技巧叫做向量化,它能让实现此模型以及其他许多学习算法变得简单得多。让我们在下一节视频中看看什么是向量化。

22:向量化(第一部分)🚀

在本节课中,我们将学习一个在实现机器学习算法时非常有用的概念——向量化。使用向量化不仅能让你的代码更简洁,还能显著提升其运行效率。

概述 📋

向量化是一种编程技术,它允许我们利用现代数值线性代数库,甚至GPU(图形处理器)硬件来加速计算。GPU最初是为加速计算机图形而设计的,但事实证明,当你编写向量化代码时,它也能帮助你更快地执行代码。

接下来,我们通过一个具体例子来看看向量化的含义。

非向量化实现示例

假设我们有一个参数向量 W 和一个特征向量 x,其中 Wx 都包含三个数字(即 n=3)。

在数学表示中,索引通常从1开始,因此第一个值记为 W₁ 和 x₁。

在Python代码中,我们可以使用NumPy库(Python中最广泛使用的数值线性代数库)来定义这些变量。需要注意的是,Python中数组的索引是从0开始的。

import numpy as np
w = np.array([...]) # 假设w有三个值
x = np.array([...]) # 假设x有三个值
b = ... # 偏置项

第一种非向量化方法

一种不使用向量化的实现方式是手动将每个参数与其对应的特征相乘并相加。

f = w[0] * x[0] + w[1] * x[1] + w[2] * x[2] + b

这种方法在n=3时可行,但如果n是100或100,000,无论是编写代码还是计算机执行计算,效率都非常低下。

第二种非向量化方法(使用循环)

另一种方法,虽然仍不使用向量化,但通过for循环来改进。数学上,这对应于一个求和公式:

f = b + Σ (wⱼ * xⱼ), 其中 j 从 1 到 n。

在代码中,我们可以这样实现:

f = 0
for j in range(n): # 在Python中,range(n)使j从0遍历到n-1
    f = f + w[j] * x[j]
f = f + b

虽然这种实现比第一种稍好,但它仍然没有使用向量化,效率不高。

向量化实现 🚄

现在,让我们看看如何使用向量化来实现相同的计算。

上述函数的数学表达式本质上是向量 Wx点积,再加上偏置项 b

使用NumPy,我们可以用一行代码实现:

f = np.dot(w, x) + b

这行代码 np.dot(w, x) 实现了向量 Wx 之间的数学点积运算。当n很大时,这种向量化实现将比前面两种非向量化代码运行得快得多。

向量化的双重优势 ✨

向量化主要带来两个显著的好处:

  1. 代码更简洁:现在只需一行代码,编写和阅读都更容易。
  2. 运行速度更快:向量化实现比之前两种不使用向量化的实现要快得多。

向量化代码运行更快的原因是,在幕后,像 np.dot 这样的函数能够利用计算机中的并行硬件。无论你是在普通的计算机CPU上运行,还是在使用常用于加速机器学习任务的GPU上运行,这一点都成立。np.dot 函数利用并行硬件的能力,使其比我们之前看到的for循环或顺序计算要高效得多。

当n很大时,向量化版本变得非常实用。你不再需要像第一个非向量化版本那样,手动输入 w[0]*x[0] + w[1]*x[1] + ... 等大量项。

总结 🎯

本节课我们一起学习了向量化的核心概念。我们了解到:

  • 向量化是一种利用现代硬件并行计算能力来提升代码效率的技术。
  • 通过使用像NumPy这样的库,我们可以用简洁的代码(如 np.dot(w, x) + b)替代冗长的循环。
  • 向量化具有代码简洁执行高效的双重优势,是机器学习实践中不可或缺的技能。

在下一节中,我们将更深入地探讨向量化背后的原理,看看计算机是如何在幕后执行这些快速计算的。

23:向量化第二部分 🚀

在本节课中,我们将要学习向量化(Vectorization)的工作原理,以及它如何显著提升机器学习算法的运行效率。我们将深入探讨向量化在计算机硬件层面是如何实现的,并通过一个具体的多元线性回归例子,对比向量化和非向量化代码的区别。


概述

向量化是一种利用计算机并行处理能力,一次性对多个数据执行相同操作的技术。在机器学习中,处理大量数据和特征时,向量化能极大地减少计算时间。


向量化的“魔法”原理 ✨

上一节我们介绍了向量化的概念,本节中我们来看看这背后的“魔法”是如何实现的。

我记得当我第一次学习向量化时,我在电脑上花费了很多小时。我运行一个未向量化的算法版本,观察其运行时间,然后再运行向量化的代码版本,看看它能快多少。坦白说,相同的算法经过向量化后运行速度如此之快,这让我感到震惊,感觉几乎像变魔术一样。

让我们来弄清楚这个“魔术”的真正原理。让我们更深入地看看向量化实现在你的计算机幕后是如何工作的。

以下是一个未向量化的完整循环示例:

for j in range(0, 16):
    # 执行某些操作

像这样的完整循环在没有向量化的情况下运行。如果 j 的范围从 0 到 15,这段代码会一个接一个地执行操作。在第一个时间步(我写作 T0),它首先对索引 0 的值进行操作。在下一个时间步,它计算与索引 1 对应的值,依此类推,直到第 15 步。换句话说,它一步一步地、一个接一个地计算这些运算。

相比之下,计算机硬件通过向量化来实现类似 np.dot(w, x) 这样的函数。因此,计算机可以在单个步骤中获取向量 wx 的所有值,并同时并行地将每一对 wx 相乘。然后,计算机使用专门的硬件非常高效地将这 60 个数字全部相加,而不是需要一个接一个地进行不同的加法运算来累加这 60 个数字。

这意味着,使用向量化的代码可以在比非向量化代码少得多的时间内执行计算。当你在大型数据集上运行学习算法或尝试训练大型模型时(这在机器学习中很常见),这一点尤为重要。

因此,为学习算法实现正确的向量化版本,是使其高效运行并从而能够扩展到许多现代机器学习算法必须处理的大型数据集的关键一步。


多元线性回归中的向量化应用 📈

现在,让我们看一个具体的例子,了解向量化如何帮助实现多元线性回归(即具有多个输入特征的线性回归)。

假设你有一个问题,有 16 个特征和 16 个参数 w1w16,此外还有参数 b。你为这 16 个权重计算了 16 个导数项。在代码中,你可能将 w 和导数 d 的值存储在 NumPy 数组中。对于这个例子,我将暂时忽略参数 b

现在你想计算这 16 个参数中每一个的更新,即:
w_j := w_j - α * d_j,其中 j 从 1 到 16,α 是学习率(例如 0.1)。

在未向量化的代码中,你会这样做:

w1 = w1 - 0.1 * d1
w2 = w2 - 0.1 * d2
...
w16 = w16 - 0.1 * d16

或者使用一个完整的循环:

for j in range(0, 16):
    w[j] = w[j] - 0.1 * d[j]

相比之下,通过向量化,你可以想象计算机的并行处理硬件是这样的:它同时获取向量 w 中的所有 16 个值,并行地减去 0.1 乘以向量 d 中的所有 16 个值,并在一个步骤内将所有 16 个计算结果赋值回 w

在代码中,你可以这样实现:

w = w - 0.1 * d

在幕后,计算机获取这些 NumPy 数组 wd,并使用并行处理硬件来高效地执行所有 16 个计算。因此,使用向量化实现,你应该能得到一个高效得多的线性回归实现。

如果你的特征只有 16 个,速度差异可能不会很大。但如果你有数千个特征,并且可能有非常大的训练集,这种向量化实现将对你的学习算法的运行时间产生巨大影响。它可能意味着代码在一两分钟内完成,而不是需要许多小时才能完成同样的事情。


NumPy 与向量化实践 🛠️

在本视频之后的可选实验中,你将看到对机器学习中最常用的 Python 库之一的介绍,我们在本视频中已经提到过它,叫做 NumPy

你将看到如何在代码中表示向量,这些数字列表被称为 NumPy 数组。你还将看到如何使用名为 np.dot 的 NumPy 函数来计算两个向量的点积。

你还将看到向量化代码(例如使用 dot 函数)如何比完整循环运行得快得多。实际上,你可以自己计时,并希望看到它运行得更快。

这个可选实验介绍了相当多的新 NumPy 语法。因此,当你阅读可选实验时,请不要觉得你必须立即理解所有代码。你可以保存这个笔记本,并将其作为参考资料,以便在处理 NumPy 数组中的数据时查阅。


总结

恭喜你完成了这个关于向量化的视频!你已经学习了在实现机器学习算法中最重要和最有用的技术之一。

在下一个视频中,我们将把多元线性回归的数学与向量化结合起来,这样你就可以用向量化实现多元线性回归的梯度下降。让我们继续下一个视频。


24:多元线性回归的梯度下降法 🧮

在本节课中,我们将学习如何将梯度下降法、多元线性回归和向量化的知识结合起来,实现多元线性回归的向量化梯度下降。我们将从模型和代价函数的向量化表示开始,逐步推导出参数更新规则,并简要介绍另一种求解方法——正规方程。


模型与代价函数的向量化表示

上一节我们介绍了多元线性回归的基本概念。本节中,我们来看看如何使用向量化表示来简化模型。

之前,我们将多元线性回归模型定义为多个特征的加权和。现在,我们可以使用向量点积来更简洁地表示它。

  • 将参数 w1wn 收集到一个向量 w 中。
  • 模型可以写为:f(x) = w · x + b,其中 “·” 表示点积运算。

同样,代价函数 J 也从接受多个参数 w1...wnb,转变为接受一个参数向量 w 和一个标量 bJ(w, b)


梯度下降的参数更新规则

了解了模型的向量化表示后,我们来看看梯度下降的更新过程如何应用于多元线性回归。

梯度下降的核心是重复更新每个参数,使其减去学习率乘以代价函数对该参数的偏导数。

对于单变量线性回归,更新规则如下:

  • w = w - α * (∂J/∂w)
  • b = b - α * (∂J/∂b)

对于具有 n 个特征的多元线性回归,我们需要更新 n+1 个参数(w1wn 以及 b)。以下是更新规则:

关键点在于,对于每个 wj 的更新,其偏导数公式与单变量情况形式相似,但需要对应使用第 j 个特征值 x_j


正规方程法简介

在结束梯度下降的讨论前,我们简要了解一下求解线性回归参数的另一种方法——正规方程。

与迭代的梯度下降法不同,正规方程是一种基于线性代数的解析解法,可以直接一次性求解出最优的 wb,无需迭代。

以下是关于正规方程需要了解的几点:

  • 局限性:该方法仅适用于线性回归,无法推广到逻辑回归、神经网络等其他学习算法。
  • 计算效率:当特征数量 n 非常大时,该方法的计算速度可能较慢。
  • 实际应用:成熟的机器学习库在背后可能会使用此方法求解线性回归。对于大多数情况,尤其是自己实现算法时,梯度下降通常是更合适的选择。

代码实现与后续步骤

在紧随本视频的实践环节中,你将看到如何用代码实现多元线性回归模型。

以下是实现的关键步骤:

  1. 使用 NumPy 库定义模型并进行预测:f = np.dot(X, w) + b
  2. 计算代价函数。
  3. 实现多元线性回归的梯度下降算法。

你现在已经掌握了多元线性回归,这可能是当今世界上应用最广泛的学习算法之一。

为了使算法工作得更好,我们还需要掌握一些小技巧,例如恰当选择和缩放特征,以及选择合适的学习率 α。在接下来的视频中,我们将学习这些能显著提升多元线性回归性能的技巧。


本节课总结:我们一起学习了如何用向量化形式表示多元线性回归模型及其代价函数,推导了多元情况下的梯度下降参数更新规则,并简要了解了另一种直接求解法——正规方程。最后,我们指出了通过特征工程和学习率调优可以进一步优化模型性能。

25:特征缩放(第一部分)🚀

在本节课中,我们将学习一种称为特征缩放的技术。这项技术能显著提升梯度下降算法的运行效率,使其更快地找到模型的最优参数。

概述:特征大小与参数的关系

上一节我们讨论了梯度下降的基本原理。本节中,我们来看看特征取值范围的大小与其对应参数值大小之间的关系。

为了具体说明,我们用一个预测房价的例子。假设我们使用两个特征:

  • x₁:房屋面积(平方英尺)
  • x₂:卧室数量

在数据集中,房屋面积(x₁)的取值范围可能在300到2000平方英尺之间,而卧室数量(x₂)的取值范围可能在0到5间之间。显然,x₁的取值范围远大于x₂。

现在,考虑一个具体的房屋样本:面积2000平方英尺,5间卧室,售价50万美元。

以下是两种可能的参数组合:

组合一:

  • w₁ = 50
  • w₂ = 0.1
  • b = 50

预测价格(单位:千美元)为:50 * 2000 + 0.1 * 5 + 50 = 100,000 + 0.5 + 50 ≈ 100,050.5。这远高于实际的50万美元,不是一个好的参数组合。

组合二:

  • w₁ = 0.1
  • w₂ = 50
  • b = 50

预测价格为:0.1 * 2000 + 50 * 5 + 50 = 200 + 250 + 50 = 500。这个预测值(50万美元)与实际价格相符,是一个合理的参数组合。

通过这个例子,我们可以观察到一个规律:

  • 当某个特征(如房屋面积)的取值范围很大时,一个良好的模型往往会为其学习到一个相对较小的参数值(如0.1)。
  • 当某个特征(如卧室数量)的取值范围很小时,一个合理的参数值往往会相对较大(如50)。

特征范围对梯度下降的影响

理解了特征与参数的关系后,我们来看看这如何影响梯度下降。

如果将训练数据绘制在散点图上,以x₁(面积)为横轴,x₂(卧室数)为纵轴,你会发现横轴的范围远大于纵轴。

这种差异会反映在代价函数(J)的等高线图上。由于w₁的微小变化会乘以很大的x₁值,从而对预测结果和代价J产生巨大影响;而w₂需要更大的变化才能显著影响预测。因此,代价函数的等高线通常会呈现又高又瘦的椭圆形

在这种情况下运行梯度下降,算法可能会在峡谷状的等高线之间反复“弹跳”,需要很长时间才能曲折地找到全局最小值。

解决方案:特征缩放

为了解决上述问题,加速梯度下降,我们可以对特征进行缩放。这意味着对训练数据进行某种变换,使所有特征都处于可比的数值范围内。

例如,我们可以将x₁和x₂都缩放至0到1之间。经过缩放后,数据点的分布会变得更加“均匀”。

最关键的是,缩放后的特征x₁和x₂拥有了可比的价值范围。此时,基于缩放后数据定义的代价函数,其等高线图会变得更接近圆形,而非又高又瘦的椭圆形。

在这样的代价函数上运行梯度下降,算法能够找到一条更直接的路径快速收敛到全局最小值。

总结

本节课我们一起学习了特征缩放的第一部分核心思想。我们了解到,当不同特征具有差异巨大的取值范围时,会导致梯度下降运行缓慢。通过缩放特征,使它们处于可比的范围,可以显著加速梯度下降的收敛过程。

在下一节课中,我们将具体介绍几种实施特征缩放的常用方法。

26:特征缩放第二部分 🎯

在本节课中,我们将学习如何实现特征缩放。特征缩放是一种技术,用于将取值范围差异很大的特征,调整到彼此可比的范围,从而加速梯度下降算法的收敛。

上一节我们介绍了特征缩放的必要性,本节中我们来看看具体的实现方法。

1. 除以最大值法 📏

一种简单的特征缩放方法是除以每个特征的最大值。

假设特征 x1 的取值范围是 3 到 2000。我们可以通过将每个原始的 x1 值除以 2000(该范围的最大值)来获得缩放后的版本。缩放后的 x1 将落在 0.0015 到 1 之间。

公式:
x1_scaled = x1 / max(x1)

类似地,如果特征 x2 的取值范围是 0 到 5,我们可以将每个原始的 x2 值除以 5(最大值)。缩放后的 x2 将落在 0 到 1 之间。

公式:
x2_scaled = x2 / max(x2)

使用缩放后的 x1x2 绘制数据图,可能呈现如下分布:

2. 均值归一化法 ⚖️

除了除以最大值,另一种常见方法是均值归一化。这种方法将特征重新缩放,使其以零为中心。归一化后的特征值通常分布在 -1 到 +1 之间。

以下是实现步骤:

首先,计算训练集中特征 x1 的平均值(均值),记为 μ1。例如,μ1 可能是 600。

然后,对每个 x1 值,先减去均值 μ1,再除以取值范围(最大值减最小值)。

公式:
x1_normalized = (x1 - μ1) / (max(x1) - min(x1))

通过计算,归一化后的 x1 可能落在 -0.18 到 0.82 之间。

对于特征 x2,同样计算其均值 μ2(例如 2.3),然后进行归一化。

公式:
x2_normalized = (x2 - μ2) / (max(x2) - min(x2))

归一化后的 x2 可能落在 -0.46 到 0.54 之间。

使用均值归一化后的特征绘制数据图,可能呈现如下分布:

3. Z-score 标准化法 📊

最后一种常见的缩放方法是 Z-score 标准化。这种方法需要用到每个特征的标准差。

如果你不了解标准差,不必担心,本课程不要求你掌握其数学细节。简单来说,标准差衡量了数据分布的离散程度。

以下是实现步骤:

首先,计算每个特征的均值 μ 和标准差 σ。例如,特征 x1 的均值 μ1 为 600,标准差 σ1 为 450。

然后,对每个 x1 值,减去均值 μ1,再除以标准差 σ1

公式:
x1_zscore = (x1 - μ1) / σ1

经过计算,Z-score 标准化后的 x1 可能落在 -0.67 到 3.1 之间。

对于特征 x2,同样计算其均值 μ2 和标准差 σ2(例如 2.3 和 1.4),然后进行标准化。

公式:
x2_zscore = (x2 - μ2) / σ2

标准化后的 x2 可能落在 -1.6 到 1.9 之间。

使用 Z-score 标准化后的特征绘制数据图,可能呈现如下分布:

4. 特征缩放的经验法则 ✅

在进行特征缩放时,一个经验法则是尽量让每个特征 x 的取值范围大致在 -1 到 +1 之间。

但这个范围可以比较宽松。以下是几种常见情况:

  • 如果特征值在 -3 到 +3 或 -0.3 到 +0.3 之间,通常没有问题。
  • 如果特征值在 0 到 3 之间,你可以选择缩放它,但不缩放通常也能正常工作。
  • 如果特征值在 -2 到 +0.5 之间,同样,缩放与否均可。

然而,在以下情况下,进行特征缩放通常更有益:

  • 如果某个特征(如 x3)的取值范围非常大(例如 -100 到 +100),与 -1 到 +1 的范围差异巨大,那么最好将其缩放到更接近 -1 到 +1 的范围。
  • 如果某个特征(如 x4)的取值范围非常小(例如 -0.001 到 +0.001),这些值太小,也可能需要缩放。
  • 如果某个特征(如 x5,代表病人体温)的取值范围围绕一个较大值(例如 98.6 到 105 华氏度),与其他缩放后的特征相比数值较大,这会导致梯度下降运行变慢,此时进行特征缩放很可能会有帮助。

进行特征缩放几乎没有任何坏处。因此,如果你不确定,我鼓励你实施特征缩放。这个简单的技巧通常能让梯度下降运行得更快。

总结 📝

本节课中我们一起学习了三种特征缩放的具体方法:除以最大值法、均值归一化法和 Z-score 标准化法。我们还了解了特征缩放的经验法则,知道何时缩放特征以及缩放的目标范围。通过特征缩放,我们可以显著提高梯度下降算法的效率。

那么,在运行梯度下降时,如何判断它是否正常工作并找到了全局最小值或接近它的解呢?在下一个视频中,我们将学习如何判断梯度下降是否收敛,并随后讨论如何为梯度下降选择一个合适的学习率。

27:检查梯度下降法的收敛性 📉

在本节课中,我们将学习如何判断梯度下降法是否正在收敛,即它是否正在帮助我们找到接近成本函数全局最小值的参数。通过识别梯度下降法正常运行时的表现,我们也能为后续课程中选择合适的学习率 α 打下基础。


梯度下降法回顾

上一节我们介绍了梯度下降法的基本概念。本节中,我们来看看如何监控其运行过程。首先,让我们回顾一下梯度下降法的更新规则:

公式:
w = w - α * (∂J/∂w)
b = b - α * (∂J/∂b)

其中,α 是学习率,这是一个关键的选择。


绘制学习曲线

为了确保梯度下降法工作正常,我通常会绘制成本函数 J 的变化曲线。J 是在训练集上计算得出的。我会在梯度下降的每次迭代后,记录并绘制 J 的值。

这里需要注意,每次迭代指的是参数 wb 完成一次同步更新后。因此,在这张图中,横轴是已运行的梯度下降迭代次数,纵轴是成本 J 的值。这与之前以参数(如 w)为横轴的图不同。

这种曲线也被称为学习曲线。机器学习中有几种不同类型的学习曲线,在本课程后续部分你还会看到其他类型。

具体来说,曲线上的一点,例如横坐标为100的点,表示在运行了100次迭代(即参数更新了100次)后,你得到了当前的 wb 值。此时计算出的成本 J 值,就对应着纵轴上的这个点。

观察这张图有助于你了解成本 J 在每次梯度下降迭代后的变化情况。


判断收敛的迹象

以下是判断梯度下降是否正常工作的关键观察点:

成本应持续下降:如果梯度下降运行正常,成本 J 应该在每次迭代后都下降。如果 J 在某次迭代后反而增加了,这通常意味着学习率 α 选择不当(通常是过大),或者代码中存在错误。

曲线趋于平缓:观察学习曲线,例如在迭代到300次左右时,成本 J 可能开始趋于平缓,下降幅度变小。到了400次迭代,曲线可能已经完全平坦。这表明梯度下降已经大致收敛,因为成本不再显著下降。

通过观察学习曲线,你可以尝试判断梯度下降是否正在收敛。


迭代次数的差异

顺便提一下,梯度下降达到收敛所需的迭代次数在不同应用之间差异很大。对于一个应用,可能30次迭代就收敛了;而对于另一个应用,可能需要1000次甚至10万次迭代。事先很难预测梯度下降需要多少次迭代才能收敛,这就是为什么你需要绘制这样的学习曲线图,以确定何时可以停止训练你的特定模型。


自动收敛测试

另一种决定模型何时训练完成的方法是使用自动收敛测试

我们设 ε(希腊字母 Epsilon)为一个很小的数,例如 0.001 或 10⁻³。

公式:
ε = 0.001

判断逻辑:如果成本 J 在一次迭代中的减少量小于这个阈值 ε,那么你很可能处于曲线左侧所示的平坦部分,此时可以宣布收敛。

请记住,收敛(希望如此)表明你已经找到了接近 J 最小可能值的参数 wb

不过,我发现选择合适的阈值 ε 相当困难。因此,我实际上更倾向于查看左侧这样的图表,而不是依赖自动收敛测试。观察这个小图还能提前给你一些警告,告诉你梯度下降可能没有正确工作。


总结与过渡

本节课中,我们一起学习了梯度下降法正常运行时学习曲线应有的样子,以及如何通过观察曲线判断其是否收敛。你已经看到了当梯度下降运行良好时,学习曲线应该呈现的趋势。

掌握了这些洞察后,在下一节视频中,我们将利用这些知识,来看看如何选择一个合适的学习率 α

28:如何选择学习率 📈

在本节课中,我们将学习如何为梯度下降算法选择一个合适的学习率。学习率是影响模型训练效率和效果的关键超参数。我们将探讨学习率过大或过小带来的问题,并介绍一种系统化的方法来选择有效的学习率。

学习率的重要性

上一节我们介绍了梯度下降的基本原理,本节中我们来看看如何优化其核心参数——学习率。

一个合适的学习率能让你的学习算法运行得更好。如果学习率太小,算法收敛会非常缓慢。如果学习率太大,算法甚至可能无法收敛。

学习率过大的表现

以下是判断学习率是否过大的关键迹象。

如果你绘制了多次迭代的成本函数曲线,并发现成本有时上升有时下降,这应被视为梯度下降未正常工作的明确信号。这可能意味着代码中存在错误,或者有时仅意味着你的学习率太大了。

这是一个可能发生情况的示意图。

图中纵轴是成本函数 J,横轴代表一个参数,例如 w1

如果学习率太大,假设你从这里开始,你的更新步骤可能会越过最小值点,最终到达这里。在下一个更新步骤中,你再次越过,最终到达这里,依此类推。这就是为什么成本有时会上升而不是下降。

要解决这个问题,你可以使用一个更小的学习率。这样,你的更新可能从这里开始,下降一点,再下降一点,并有望持续下降直到达到全局最小值。

有时你可能会看到成本在每次迭代后持续增加,就像这条曲线一样。这也可能是由于学习率过大造成的,可以通过选择更小的学习率来解决。

代码错误的可能性

但像这样的学习率问题也可能是代码存在错误的信号。

例如,如果我的代码错误地将 w1 更新为 w1 + α * (dJ/dw1),这可能导致成本在每次迭代中持续增加。这是因为加上导数项会使你的成本 J 远离全局最小值,而不是接近它。

请记住,你应该使用减号。正确的代码更新应为:w1 := w1 - α * (dJ/dw1)

一个调试技巧

梯度下降正确实现的一个调试技巧是:只要学习率足够小,成本函数应该在每一次迭代中都下降。

如果梯度下降不工作,我经常做的一件事(希望这个技巧对你也同样有用)就是将 α 设置为一个非常非常小的数字,看看这是否会导致成本在每次迭代中下降。

即使将 α 设置为一个非常小的数字,如果 J 没有在每次迭代中下降,反而有时增加,那么这通常意味着代码中存在错误。

请注意,将 α 设置得非常小在这里是作为一个调试步骤。对于实际训练你的学习算法来说,一个非常小的 α 值并不是最高效的选择。

权衡与选择方法

一个重要的权衡是:如果你的学习率太小,梯度下降可能需要很多次迭代才能收敛。

当我运行梯度下降时,我通常会尝试一系列的学习率 α 值。

以下是选择学习率的具体步骤。

  1. 我会从尝试学习率 0.001 开始。
  2. 我也会尝试比它大10倍的值,比如 0.010.1,等等。
  3. 对于每个 α 的选择,你可能只需要运行梯度下降少数几次迭代,并绘制成本函数 J 随迭代次数变化的曲线。
  4. 在尝试了几个不同的值之后,你可能会选择那个看起来能使成本快速且持续下降的 α 值。

实际上,我通常这样做:尝试一个像这样的数值范围。在尝试了 0.001 之后,我会将学习率增加到 0.003(大约是3倍),之后我会尝试 0.01(这又大约是 0.003 的3倍)。这些尝试基本上是让每个 α 值大约是前一个值的3倍。

所以,我的做法是尝试一系列的值,直到我找到一个太小的值,同时也确保我找到了一个太大的值。然后我会尝试选择我能找到的最大可能的学习率,或者比找到的最大合理值稍小一点的值。当我这样做时,它通常能为我的模型提供一个良好的学习率。

我希望这个技巧也能帮助你为你的梯度下降实现选择一个好的学习率。

实践与后续内容

在接下来的可选实验中。

你也可以查看在代码中如何进行特征缩放,并观察学习率 α 的不同选择如何导致模型训练效果更好或更差。我希望你能愉快地尝试不同的 α 值,并观察不同选择的结果。请查看并运行可选实验中的代码,以获得关于特征缩放以及学习率 α 的更深入直觉。

选择学习率是训练许多学习算法的重要组成部分。我希望这个视频能让你对不同选择有直观的理解,并知道如何为 α 选择一个好的值。

现在,还有几个想法可以用来使多元线性回归更强大,那就是选择自定义特征,这也将允许你拟合曲线,而不仅仅是直线。让我们在下一个视频中看看这个内容。

总结

本节课中我们一起学习了如何为梯度下降算法选择学习率。我们了解到学习率过大会导致成本震荡或不收敛,而过小则会使收敛速度过慢。我们学习了一个实用的调试技巧——使用极小的学习率验证代码正确性,以及一个系统化的选择方法——尝试一系列按比例(如3倍)增长的值,并观察成本下降曲线,从而选取一个接近最大有效值的合适学习率。正确选择学习率是高效训练模型的关键一步。

29:特征工程 🛠️

在本节课中,我们将要学习特征工程。特征的选择对学习算法的性能有巨大影响。事实上,对于许多实际应用,选择或设计正确的特征是使算法良好工作的关键步骤。

接下来,让我们通过一个例子来看看如何为你的学习算法选择或设计最合适的特征。


特征工程示例:预测房价 🏠

我们通过回顾预测房价的例子来了解特征工程。

假设每个房子有两个特征:

  • x1 是地块的宽度(在房地产中也称为临街面)。
  • x2 是地块的深度(假设房屋建在矩形地块上)。

给定这两个特征 x1 和 x2,你可能会建立一个这样的模型:
f(x) = w1 * x1 + w2 * x2 + b
其中,x1 是临街面(宽度),x2 是深度。这个模型可能效果尚可。

但是,这里还有另一种在模型中使用这些特征的方式,可能会更有效。


创造新特征:面积的计算 📐

你可能会注意到,土地面积可以通过临街面(宽度)乘以深度来计算。你可能有一种直觉:土地面积比单独的临街面和深度更能预测房价。

因此,你可以定义一个新特征 x3
x3 = x1 * x2
这个新特征 x3 就等于地块的面积。

有了这个特征,你的模型可以变为:
f_wb(x) = w1*x1 + w2*x2 + w3*x3 + b
现在,模型可以根据数据选择参数 w1、w2 和 w3,以判断临街面、深度还是地块面积 x3 对预测房价最重要。


什么是特征工程?💡

我们刚才所做的——创造一个新特征——就是特征工程的一个例子。在特征工程中,你可以利用对问题的知识或直觉来设计新特征,通常是通过转换或组合问题的原始特征,以使学习算法更容易做出准确的预测。

因此,根据你对应用场景的洞察,有时通过定义新特征,而不是仅仅使用你最初拥有的特征,你可能会得到一个好得多的模型。这就是特征工程。


特征工程的扩展:拟合曲线 🔄

事实证明,有一种特征工程的方法不仅能让你拟合直线,还能拟合曲线(非线性函数)到你的数据上。

上一节我们介绍了通过组合特征(如面积)来改进模型的基本概念。在下一节中,我们来看看如何实现这一点,让模型能够学习更复杂的数据模式。


总结 📝

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

  1. 特征的重要性:特征的选择对算法性能有巨大影响。
  2. 特征工程的定义:通过转换或组合原始特征,利用领域知识设计新特征的过程。
  3. 一个具体例子:在房价预测中,将“宽度”和“深度”两个特征相乘,创造出更具预测力的“面积”特征。
  4. 特征工程的目标:使学习算法能更轻松、更准确地进行预测。
  5. 后续方向:特征工程可以进一步扩展,使模型能够拟合非线性关系。

30:多项式回归 📈

在本节课中,我们将学习如何将线性回归的思想与特征工程相结合,从而引入一种新的算法——多项式回归。这种算法允许我们为数据拟合曲线和非线性函数,以更好地捕捉数据中的复杂模式。


从直线到曲线 🔄

到目前为止,我们一直在用直线拟合数据。然而,并非所有数据都适合用直线来描述。让我们将多元线性回归和特征工程的思想结合起来,引入一种名为多项式回归的新算法。该算法允许你为数据拟合曲线和非线性函数。

假设你有一个住房数据集,特征 X 代表房屋面积(平方英尺)。从数据分布来看,直线似乎不能很好地拟合这个数据集。

因此,你可能希望拟合一条曲线,例如一个二次函数。这个函数不仅包含面积 x,还包含 x 的平方(即面积乘以自身)。

二次函数与三次函数 📊

二次函数模型可能为数据提供更好的拟合。但你可能随后会发现,二次模型在逻辑上并不完全合理,因为二次函数最终会下降。我们通常不会期望房屋价格随面积增大而下降,因为房屋面积越大,通常价格越高。

于是,你可能会选择三次函数模型。该模型不仅包含 x 的平方,还包含 x 的立方。这个模型可能产生一条曲线,能更好地拟合数据,因为随着面积增大,价格最终会回升。

这两种都是多项式回归的例子,因为你将原始特征 X 提升到了 2 次方或 3 次方(或其他次方)。对于三次函数,第一个特征是面积,第二个特征是面积的平方,第三个特征是面积的立方。

特征缩放的重要性 ⚖️

需要特别指出的是,如果你创建了像原始特征平方这样的幂次特征,那么特征缩放就变得尤为重要。

例如,如果房屋面积范围在 1 到 1000 平方英尺之间,那么第二个特征(面积平方)的范围将是 1 到 1,000,000,而第三个特征(面积立方)的范围将是 1 到 1,000,000,000。与原始特征 X 相比,特征 的取值范围差异巨大。如果使用梯度下降法,应用特征缩放以使特征值处于可比范围内是非常重要的。

其他特征选择 🔍

除了使用面积的平方和立方,你还有广泛的特征选择。另一个可行的替代方案是使用 x 的平方根。

你的模型可能看起来像这样:

预测值 = w₁ * x + w₂ * √x + b

平方根函数的曲线随着 x 增大而增长趋缓,但永远不会完全平坦,也绝不会下降。因此,这可能是适用于该数据集的另一个特征选择。

你可能会问,如何决定使用哪些特征?在后续的专项课程中,你将学习如何选择不同的特征和模型,并有一个评估不同模型性能的过程,以帮助你决定包含或排除哪些特征。

目前,只需意识到你在特征选择上拥有灵活性。通过特征工程和多项式函数,你有可能为数据获得更好的模型。

实践环节:代码实现与工具 🛠️

在本视频随后的可选实验中,你将看到一些实现多项式回归的代码,这些代码使用了诸如 x 等特征。请查看并运行代码,了解其工作原理。

之后还有另一个可选实验,展示了如何使用一个流行的开源工具包来实现线性回归。

Scikit-learn 是一个非常广泛使用的开源机器学习库,被全球许多顶级人工智能和互联网机器学习公司的从业者所使用。

因此,无论现在还是将来,如果你在工作中使用机器学习,很可能会使用像 Scikit-learn 这样的工具来训练模型。

完成这个可选实验不仅会让你更好地理解线性回归,还能让你看到如何使用像 Scikit-learn 这样的库,仅用几行代码就能完成这些任务。

为了让你对这些算法有扎实的理解并能够应用它们,我认为重要的是你要知道如何自己实现线性回归,而不仅仅是调用某个像黑盒子一样的 Scikit-learn 函数。但 Scikit-learn 在当今机器学习的实践方式中确实扮演着重要角色。

本周总结与展望 🎉

本周的内容即将结束,恭喜你完成了本周的所有视频。请务必查看练习测验和练习实验。我希望练习实验能让你尝试和实践我们本周讨论的思想。在练习实验中,你将实现线性回归。希望你享受让这个学习算法为自己工作的过程。

祝你好运!我也期待在下周的视频中与你再见。下周我们将超越回归(预测数字),讨论我们的第一个分类算法,该算法可以预测类别。

下周见!


本节课总结:在本节课中,我们一起学习了多项式回归的概念。我们探讨了如何通过特征工程,将原始特征转化为高次项(如平方、立方)来拟合非线性数据。我们强调了特征缩放对于此类模型的重要性,并简要介绍了使用 Scikit-learn 库进行实现的实践方向。最后,我们为本周的学习画上句号,并预告了下一周关于分类算法的内容。

31:分类问题与逻辑回归动机 🎯

在本节课中,我们将探讨为什么线性回归不适用于分类问题,并引出一种专门用于分类的算法——逻辑回归。我们将通过具体例子理解分类问题的特点,并分析线性回归在处理此类问题时的局限性。

概述 📋

欢迎来到本课程的第三周。本周结束时,您将完成本专项课程的第一个课程。上周我们学习了用于预测连续数值的线性回归。本周我们将学习分类问题,其输出变量 y 只能取少数几个可能值中的一个,而不是无限范围内的任意数字。

分类问题示例 📧

以下是几个分类问题的例子。

  • 垃圾邮件识别:判断一封电子邮件是否为垃圾邮件。输出答案是“否”或“是”。
  • 金融欺诈检测:判断一笔在线金融交易是否为欺诈。例如,信用卡是否被盗用。
  • 肿瘤分类:判断一个肿瘤是恶性还是良性。

在上述每个问题中,您想要预测的变量只能是两个可能值之一。这种只有两种可能输出的分类问题称为二元分类。“二元”指的是只有两个可能的类别。

类别表示约定 0️⃣1️⃣

按照惯例,我们可以用几种常见方式来表示这两个类别。

我们通常将类别指定为“否”或“是”,有时等价为“假”或“真”,或者非常普遍地使用数字 01。遵循计算机科学的惯例,0 表示假,1 表示真。我通常使用数字 01 来表示答案 y,因为这最符合我们想要实现的学习算法类型。

在术语上,通常将假或 0 类称为负类,将真或 1 类称为正类。例如,在垃圾邮件分类中,一封非垃圾邮件可称为负例,因为“是垃圾邮件吗?”这个问题的输出是“否”或 0。相反,一封垃圾邮件可称为正例,因为答案是“是”或 1

通常,“负”和“正”不一定意味着坏与好或邪恶与善良,它们只是用来传达“不存在”(0 或假)与“存在”(1 或真)的概念,例如电子邮件中垃圾属性的存在与否,或交易中欺诈活动的存在与否,或肿瘤中恶性特征的存在与否。

将哪一类称为假/0,哪一类称为真/1 有些随意。通常两种选择都可以,因此不同的工程师可能会互换,将正类定义为好邮件的存在,或真实交易的存在,或健康患者的存在。

线性回归用于分类的局限性 ⚠️

那么如何构建分类算法呢?我们来看一个用于分类肿瘤是否为恶性(1 类,正类,是)或良性(0 类,负类)的训练集示例。我在横轴上绘制了肿瘤大小,在纵轴上绘制了标签 y

现在,您可以在此训练集上尝试应用已知的算法——线性回归,并尝试用一条直线拟合数据。如果这样做,直线可能看起来像这样,这就是您的 f(x)

线性回归预测的不仅是 01 值,还包括 01 之间的所有数字,甚至小于 0 或大于 1 的数字。但这里我们想要预测类别。

您可以尝试选择一个阈值,例如 0.5。这样,如果模型输出值低于 0.5,则预测 y 等于 0(非恶性);如果模型输出值等于或大于 0.5,则预测 y 等于 1(恶性)。请注意,这个 0.5 的阈值在这一点与最佳拟合直线相交。因此,如果您在此处画一条垂直线,左侧的所有点最终预测为 y=0,右侧的所有点最终预测为 y=1

对于这个特定数据,线性回归似乎可以做出合理的判断。

新增数据点带来的问题 🔄

现在,让我们看看如果数据中多了一个训练样本(右边很远的一个)会发生什么。同时我们也扩展一下横轴。请注意,这个训练样本实际上不应该改变您对数据点的分类方式。我们刚才画的这条垂直分界线作为截止点仍然有意义:肿瘤小于此值应分类为 0,大于此值应分类为 1

但是,一旦在右侧添加了这个额外的训练样本,线性回归的最佳拟合线将像这样移动。如果您继续使用 0.5 的阈值,您现在会注意到,该点左侧的所有内容都被预测为 0(非恶性),而该点右侧的所有内容都被预测为 1(恶性)。

这不是我们想要的。因为在右侧添加那个样本不应该改变我们关于如何分类恶性与良性肿瘤的任何结论。但是,如果您尝试用线性回归来做这件事,添加这个感觉不应该改变任何东西的样本,最终会导致我们为这个分类问题学习到一个糟糕得多的函数。显然,当肿瘤很大时,我们希望算法将其分类为恶性。

我们刚才看到的是,当我们在右侧添加一个样本时,线性回归导致最佳拟合线移动,因此分界线(也称为决策边界)也向右移动。

引入逻辑回归 🧠

在下一个视频中,您将了解更多关于决策边界的内容,并学习一种称为逻辑回归的算法。该算法的输出值将始终介于 01 之间,并且会避免我们在幻灯片上看到的这些问题。

顺便说一下,逻辑回归这个名字令人困惑的一点是,尽管它包含“回归”一词,但实际上用于分类。不要被这个名字所迷惑,这个名字是历史原因造成的,它实际上用于解决输出标签 y01 的二元分类问题。

可选实验与总结 📝

在即将进行的可选实验中,您还可以看看尝试将线性回归用于分类时会发生什么。有时您很幸运,它可能有效。但通常效果不佳,这就是为什么我自己不使用线性回归进行分类的原因。在可选实验中,您将看到一个尝试对两个类别进行分类的交互式图表,您可能会注意到这通常效果不佳。这没关系,因为这激发了对不同模型来完成分类任务的需求。

所以请查看这个可选实验,之后我们将进入下一个视频,学习用于分类的逻辑回归。

总结 ✨

本节课我们一起学习了分类问题的基本概念,理解了为什么线性回归不适合处理分类任务,并通过一个肿瘤分类的例子具体分析了线性回归的局限性。我们认识到,由于分类输出是离散的类别,而线性回归输出连续值,直接应用会导致决策边界不稳定。这为我们引入专门用于分类、且输出值被限制在 01 之间的逻辑回归算法做好了铺垫。

32:逻辑回归算法详解 🧠

在本节课中,我们将要学习逻辑回归算法。逻辑回归是应用最广泛的分类算法之一,常用于预测二元结果(如是/否、恶性/良性等)。我们将从线性回归的局限性出发,逐步构建逻辑回归模型,并解释其输出含义。


从线性回归到逻辑回归

上一节我们介绍了线性回归在分类问题上的局限性。本节中我们来看看逻辑回归如何解决这个问题。

逻辑回归会为数据集拟合一条S形曲线。例如,在肿瘤分类问题中,横轴表示肿瘤大小,纵轴表示分类结果(0代表良性,1代表恶性)。逻辑回归模型会输出一个介于0和1之间的值,表示肿瘤为恶性的概率。


Sigmoid函数:逻辑回归的核心

逻辑回归算法的核心是一个重要的数学函数,称为Sigmoid函数(或Logistic函数)。

Sigmoid函数的形状呈S形,其输出值始终在0和1之间。如果用g(z)表示该函数,其公式为:

g(z) = 1 / (1 + e^(-z))

其中,e是自然常数,约等于2.7。

以下是Sigmoid函数的关键特性:

  • z非常大时,e^(-z)趋近于0,因此g(z)趋近于1。
  • z非常小(负值很大)时,e^(-z)变得非常大,因此g(z)趋近于0。
  • z = 0时,g(z)等于0.5。

构建逻辑回归模型

现在,我们使用Sigmoid函数来构建逻辑回归模型。这个过程分为两步。

第一步,我们计算线性函数:
z = w · x + b

第二步,将z的值传递给Sigmoid函数g
f(x) = g(z) = g(w · x + b)

将两个公式结合,就得到了逻辑回归模型:
f(x) = 1 / (1 + e^(- (w · x + b)))

该模型输入特征x,输出一个介于0和1之间的数字。


理解逻辑回归的输出

让我们回到肿瘤分类的例子,理解逻辑回归输出的含义。

逻辑回归的输出f(x)可以解释为:在给定输入特征x的条件下,标签y等于1的概率。在数学上,这可以表示为:
f(x) = P(y = 1 | x; w, b)

例如,如果一位患者的肿瘤大小为某个值x,模型输出0.7。这意味着模型预测该患者的肿瘤有70%的可能性是恶性的(即y=1)。相应地,肿瘤为良性(y=0)的概率就是30%,因为两者概率之和必须为1。


代码实现与可视化

在紧随本视频的选修实验课中,你将看到Sigmoid函数在代码中是如何实现的。

你将看到使用Sigmoid函数绘制的图形,它能在分类任务上取得比之前实验更好的效果。代码已提供,你只需运行即可。

建议你查看并熟悉这些代码,以加深对算法的理解。


总结与预告

本节课中我们一起学习了逻辑回归模型及其数学定义。逻辑回归是一个强大且应用广泛的算法,历史上甚至主导了相当长时间的互联网广告投放决策。

关于这个算法还有更多内容需要学习。在下一个视频中,我们将深入探讨逻辑回归的细节,观察一些可视化结果,并研究一个称为“决策边界”的概念。这将帮助我们理解如何将模型输出的数字(如0.3、0.7)映射到实际的预测类别(0或1)。

让我们进入下一个视频,继续学习逻辑回归。😊

33:决策边界 🧠

在本节课中,我们将学习逻辑回归模型中的决策边界概念。我们将探讨模型如何根据输入特征预测输出类别(0或1),并理解决策边界如何随模型参数和特征形式的变化而变化。


概述

上一节我们介绍了逻辑回归模型的基本原理。本节中,我们将深入探讨决策边界,以更好地理解逻辑回归模型如何进行预测计算。

逻辑回归模型的输出计算分为两步:

  1. 计算 z = w·x + b
  2. sigmoid函数 g(z) 应用于 z 值

sigmoid函数的公式为:
g(z) = 1 / (1 + e^{-z})

因此,模型的输出可以写为:
f(x) = g(z) = g(w·x + b)

我们将其解释为在给定输入 x 和参数 w、b 的条件下,y=1 的概率。


预测规则

为了将概率输出转换为具体的类别预测(0或1),我们需要设定一个阈值。常见的做法是选择 0.5 作为阈值。

以下是预测规则:

  • 如果 f(x) ≥ 0.5,则预测 ŷ = 1
  • 如果 f(x) < 0.5,则预测 ŷ = 0

接下来,我们分析模型何时会预测 y=1。


决策边界的推导

由于 f(x) = g(z),因此 f(x) ≥ 0.5 等价于 g(z) ≥ 0.5。

观察 sigmoid 函数图像可知,当 z ≥ 0 时,g(z) ≥ 0.5。
而 z = w·x + b,所以条件 z ≥ 0 等价于 w·x + b ≥ 0

由此,我们得到核心预测规则:

  • 模型预测 ŷ = 1,当且仅当 w·x + b ≥ 0
  • 模型预测 ŷ = 0,当且仅当 w·x + b < 0

决策边界 正是由方程 w·x + b = 0 定义的直线(或曲线),它将特征空间划分为预测为1和预测为0的两个区域。


线性决策边界示例

假设我们有一个包含两个特征(x1, x2)的分类问题训练集。红色叉号代表正例(y=1),蓝色圆圈代表负例(y=0)。

逻辑回归模型使用函数 f(x) = g(z) 进行预测,其中 z = w1·x1 + w2·x2 + b

假设参数值为:w1 = 1, w2 = 1, b = -3

那么,决策边界由下式决定:
w·x + b = 0 → x1 + x2 - 3 = 0 → x1 + x2 = 3

在二维平面上,这是一条直线。模型预测如下:

  • 在直线 x1 + x2 = 3右侧区域,模型预测 ŷ = 1
  • 在直线左侧区域,模型预测 ŷ = 0

这个例子展示了当使用线性特征时,逻辑回归的决策边界是一条直线。


非线性决策边界

通过引入多项式特征,逻辑回归可以学习更复杂的非线性决策边界。

考虑一个更复杂的例子,我们定义 z 为:
z = w1·x1² + w2·x2² + b

假设我们选择参数:w1 = 1, w2 = 1, b = -1

那么,决策边界(z=0)由下式决定:
x1² + x2² - 1 = 0 → x1² + x2² = 1

在二维平面上,这是一个圆形

  • 在圆(x1² + x2² ≥ 1),模型预测 ŷ = 1
  • 在圆(x1² + x2² < 1),模型预测 ŷ = 0

更复杂的决策边界

通过使用更高阶的多项式特征,我们可以得到更复杂的决策边界。

例如,定义 z 为:
z = w1·x1 + w2·x2 + w3·x1² + w4·x1·x2 + w5·x2² + b

通过调整参数,决策边界可以变成椭圆或其他更复杂的形状。模型可以预测 y=1 在形状内部,y=0 在形状外部。

关键点:如果只使用原始特征(如 x1, x2),决策边界将始终是线性的(一条直线)。只有引入多项式特征,才能获得非线性的决策边界。


总结

本节课中,我们一起学习了逻辑回归中的决策边界

  • 决策边界是模型用于区分预测类别(0或1)的分界线,由方程 w·x + b = 0 定义。
  • 预测规则很简单:在边界一侧预测1,另一侧预测0。
  • 决策边界的形状取决于模型参数 (w, b) 和所使用的特征形式
  • 使用原始线性特征时,决策边界是直线
  • 通过引入多项式特征,逻辑回归可以学习圆形、椭圆形等复杂的非线性决策边界,从而拟合更复杂的数据模式。

理解决策边界有助于我们直观地看到逻辑回归模型是如何根据输入做出决策的。在接下来的课程中,我们将学习如何通过定义成本函数和应用梯度下降来训练逻辑回归模型。

34:逻辑回归的成本函数 📊

在本节课中,我们将学习逻辑回归的成本函数。我们将了解为什么线性回归中使用的平方误差成本函数不适用于逻辑回归,并引入一个新的、更合适的成本函数。这个新的成本函数能确保梯度下降法可以有效地找到全局最优解。


为什么平方误差成本函数不适用?🤔

上一节我们介绍了逻辑回归模型。本节中我们来看看如何为它选择合适的成本函数。

成本函数用于衡量模型参数与训练数据的拟合程度,并指导我们选择更好的参数。在线性回归中,我们使用平方误差成本函数,其公式如下(为简化后续数学推导,将1/2置于求和符号内):

\[J(w,b) = \frac{1}{m} \sum_{i=1}^{m} \frac{1}{2}(f_{w,b}(x^{(i)}) - y^{(i)})^2 \]

对于线性模型 \(f_{w,b}(x) = w \cdot x + b\),这个成本函数是凸函数(呈碗状),梯度下降可以顺利找到全局最小值。

然而,如果将其直接用于逻辑回归模型 \(f_{w,b}(x) = \frac{1}{1 + e^{-(w \cdot x + b)}}\),绘制出的成本函数图像将不再是凸函数,而是非凸函数,表面崎岖不平,存在许多局部最小值。这会导致梯度下降算法容易陷入局部最优,而无法找到全局最佳参数。

因此,我们需要为逻辑回归设计一个不同的成本函数,使其保持凸性。


构建逻辑回归的损失函数 🧱

为了构建新的成本函数,我们首先定义单个训练样本的损失。损失函数 \(L\) 衡量模型预测值 \(f(x)\) 与真实标签 \(y\) 之间的差异。

对于逻辑回归,我们使用以下损失函数定义:

  • 当真实标签 \(y = 1\) 时,损失为:\(L(f(x), y) = -\log(f(x))\)
  • 当真实标签 \(y = 0\) 时,损失为:\(L(f(x), y) = -\log(1 - f(x))\)

以下是关于这个损失函数工作原理的详细解释。

情况一:当 \(y = 1\) 时 📈

我们首先分析当真实标签为1时,损失函数 \(-\log(f(x))\) 的行为。

由于逻辑回归的输出 \(f(x)\) 始终在0到1之间(表示概率),我们只需关注该区间内的函数图像。当 \(f(x)\) 接近1(即预测正确)时,损失值非常小,接近0。随着 \(f(x)\) 减小(预测准确性下降),损失值迅速增大。例如:

  • 若模型预测 \(f(x)=0.9\),损失很小。
  • 若模型预测 \(f(x)=0.1\),损失会非常大。

这激励模型在面对 \(y=1\) 的样本时,做出更接近1的预测。

情况二:当 \(y = 0\) 时 📉

接下来,我们分析当真实标签为0时,损失函数 \(-\log(1 - f(x))\) 的行为。

此时,逻辑与 \(y=1\) 时相反。当 \(f(x)\) 接近0(预测正确)时,损失值很小。当 \(f(x)\) 接近1(预测完全错误)时,损失值会变得极大,甚至趋近于无穷大。

这同样激励模型在面对 \(y=0\) 的样本时,做出更接近0的预测。

综上所述,这个损失函数有效地惩罚了错误的预测,并且惩罚力度随着错误程度的增加而增大。


从损失函数到成本函数 ⚙️

上一节我们定义了单个样本的损失。本节中,我们将基于此定义整个训练集的成本函数。

成本函数 \(J(w, b)\) 是所有训练样本损失的平均值:

\[J(w,b) = \frac{1}{m} \sum_{i=1}^{m} L(f_{w,b}(x^{(i)}), y^{(i)}) \]

其中,损失 \(L\) 根据 \(y\) 的值采用我们刚刚定义的两部分形式。

可以证明,选择这样的损失函数形式,能够使得最终的整体成本函数 \(J(w, b)\) 是一个凸函数。这意味着梯度下降法可以可靠地收敛到全局最小值(严格的凸性证明超出了本课程范围)。

在后续的编程练习中,你将直观地看到:

  • 使用平方误差成本函数会导致一个布满局部最小值的、不平滑的成本曲面。
  • 而使用新的逻辑回归损失函数,则会得到一个平滑的凸曲面,非常适合梯度下降优化。


总结 🎯

本节课中我们一起学习了逻辑回归的成本函数。

  1. 核心问题:线性回归的平方误差成本函数不适用于逻辑回归,因为它会导致非凸的成本曲面,阻碍梯度下降找到最优解。
  2. 解决方案:为逻辑回归引入了新的损失函数:
    • \(y=1\) 时:\(L = -\log(f(x))\)
    • \(y=0\) 时:\(L = -\log(1 - f(x))\)
  3. 最终成本函数:整个训练集的成本是每个样本损失的平均值:\(J(w,b) = \frac{1}{m} \sum_{i=1}^{m} L(f_{w,b}(x^{(i)}), y^{(i)})\)
  4. 优势:此成本函数是凸函数,确保了梯度下降法的有效性。

在下一节课中,我们将对这个成本函数进行简化书写,并推导出梯度下降的更新公式,从而实际应用于逻辑回归的参数训练。

35:逻辑回归的简化成本函数 📉

概述

在本节课中,我们将学习如何用一种更简洁的方式来表达逻辑回归的损失函数和成本函数。这种简化形式将使我们在后续使用梯度下降法拟合逻辑回归模型参数时,实现起来更加简便。


回顾:逻辑回归的损失函数

上一节我们介绍了逻辑回归的损失函数。作为回顾,其原始定义如下:

当 y=1 时:
loss = -log(f(x))

当 y=0 时:
loss = -log(1 - f(x))

其中,f(x) 是模型的预测值(即 g(z),sigmoid函数的输出),y 是真实标签(0 或 1)。


简化损失函数

由于我们处理的是二元分类问题,y 的值只能是 0 或 1。利用这个特性,我们可以将上述分段定义的损失函数合并成一个统一的表达式。

给定预测值 f(x) 和真实标签 y,简化的损失函数可以写为:

损失函数公式:
loss = -[ y * log(f(x)) + (1 - y) * log(1 - f(x)) ]

这个单行表达式与之前的分段定义是完全等价的。下面我们来验证一下。

以下是验证过程:

  • 情况一:当 y = 1 时
    公式变为:loss = -[ 1 * log(f(x)) + 0 * log(1 - f(x)) ] = -log(f(x))
    这与原始定义中 y=1 的情况一致。

  • 情况二:当 y = 0 时
    公式变为:loss = -[ 0 * log(f(x)) + 1 * log(1 - f(x)) ] = -log(1 - f(x))
    这与原始定义中 y=0 的情况一致。

由此可见,无论 y 是 1 还是 0,这个统一的简化公式都能给出正确的结果,从而避免了分段讨论的复杂性。


推导逻辑回归的成本函数

上一节我们介绍了损失函数是针对单个训练样本的,本节我们来看看如何基于简化的损失函数,推导出整个训练集的成本函数。

成本函数 J 是全部 m 个训练样本损失的平均值。将我们刚刚得到的简化损失函数代入,可以得到:

成本函数公式:
J(w, b) = -(1/m) * Σ [ y^(i) * log(f(x^(i))) + (1 - y^(i)) * log(1 - f(x^(i))) ]

其中,求和符号 Σ 表示对 i 从 1 到 m 的所有训练样本进行累加。

这个公式就是逻辑回归最常用的成本函数。你可能会问,为什么选择这个特定的函数?这里简要说明一下,这个成本函数源于统计学中的最大似然估计原理,它能有效地为模型寻找参数,并且具有凸函数的良好性质,这确保了梯度下降等优化算法能找到全局最优解。关于最大似然估计的细节,本课程不做深入探讨。


代码实现与可视化

在接下来的可选实验课中,你将看到如何在代码中实现这个逻辑回归成本函数。强烈建议你查看该实验,因为你将在本周的练习中亲自实现它。

该实验还会展示,选择不同的参数会导致不同的成本计算结果。例如,在图中你可以看到,拟合效果更好的蓝色决策边界所对应的成本,要低于品红色决策边界所对应的成本。这直观地验证了成本函数作为模型性能评估指标的有效性。


总结

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

  1. 如何将逻辑回归的分段损失函数,统一简化为一个简洁的表达式:loss = -[ y * log(f(x)) + (1 - y) * log(1 - f(x)) ]
  2. 如何基于简化损失函数,推导出逻辑回归的成本函数:J = -(1/m) * Σ [ y^(i) * log(f(x^(i))) + (1 - y^(i)) * log(1 - f(x^(i))) ]
  3. 了解到该成本函数源于最大似然估计,并具有凸性质。

掌握了这个简化的成本函数,我们现在已经准备好进入下一阶段:应用梯度下降法来训练逻辑回归模型。让我们在下一个视频中继续学习。

36:逻辑回归的梯度下降实现 🚀

在本节课中,我们将要学习如何使用梯度下降算法来训练逻辑回归模型。我们将找到能够最小化成本函数的参数 WB,并理解其与线性回归在实现上的关键区别。


概述 📋

为了拟合逻辑回归模型的参数,我们需要找到能够最小化成本函数 J(W, B) 的参数 WB 的值。我们将再次应用梯度下降算法来实现这一目标。

上一节我们介绍了逻辑回归的成本函数,本节中我们来看看如何通过梯度下降来优化参数。

梯度下降算法 🔄

可用于最小化成本函数的算法是梯度下降。

成本函数如下,如果你想将成本 J 作为 WB 的函数最小化,那么通常的梯度下降算法是:重复更新每个参数,新值等于旧值减去学习率 α 乘以对应的导数项。

以下是参数更新公式:

  • W 的更新:W_j := W_j - α * (∂J/∂W_j)
  • B 的更新:b := b - α * (∂J/∂b)

让我们具体看看这些导数项。

计算导数 🧮

首先,我们来看成本函数 JW_j 的导数。

经过微积分运算可以证明,成本函数 JW_j 的导数等于以下表达式:
∂J/∂W_j = (1/m) * Σ (f(x^(i)) - y^(i)) * x_j^(i)
其中,求和从 i=1mm 是训练样本数)。这里的 x_j^(i) 是第 i 个训练样本的第 j 个特征。

接下来,我们看成本函数 J 对参数 B 的导数。

结果如下,它与上面的表达式非常相似,只是末尾没有乘以 x_j^(i)
∂J/∂b = (1/m) * Σ (f(x^(i)) - y^(i))

需要提醒的是,与线性回归类似,执行这些更新的方式是使用同步更新。这意味着你需要首先为所有更新计算等式的右边部分,然后同时覆盖左边所有的值。

逻辑回归的梯度下降公式 📝

现在,让我将这些导数表达式代入到更新项中,就得到了逻辑回归的梯度下降算法。

以下是完整的更新规则:

  • W_j 更新W_j := W_j - α * [(1/m) * Σ (f(x^(i)) - y^(i)) * x_j^(i)]
  • B 更新b := b - α * [(1/m) * Σ (f(x^(i)) - y^(i))]

你可能会觉得奇怪:这两个方程看起来和之前线性回归的方程一模一样。那么,线性回归和逻辑回归实际上是一样的吗?

与线性回归的关键区别 ⚠️

尽管这些方程看起来相同,但这不是线性回归,因为函数 f(x) 的定义发生了变化。

  • 在线性回归中:f(x) = w·x + b
  • 在逻辑回归中:f(x) = sigmoid(w·x + b)

因此,虽然为线性回归和逻辑回归写出的算法看起来相同,但它们实际上是两个非常不同的算法,因为 f(x) 的定义不同。

监控收敛与加速技巧 ⚡

之前讨论线性回归的梯度下降时,你看到了如何监控梯度下降以确保其收敛。你可以将相同的方法应用于逻辑回归,以确保它也能收敛。

我之前写出的更新公式看起来像是每次更新一个参数 W_j。与讨论线性回归的向量化实现类似,你也可以使用向量化来使逻辑回归的梯度下降运行得更快。本视频不深入探讨向量化实现的细节,但你可以在可选实验中学到更多并查看代码。

你可能还记得在线性回归中使用过的特征缩放。特征缩放(即将所有特征缩放到相似的值范围,例如在 -1 和 +1 之间)可以帮助梯度下降更快地收敛。以相同方式应用特征缩放来使不同特征具有相似的值范围,也可以加速逻辑回归的梯度下降。

实验与实践 🔬

在即将进行的可选实验中,你将看到如何在代码中计算逻辑回归的梯度。这非常有用,因为你将在本周的练习实验中实现它。

在实验室中运行梯度下降后,将会有一组漂亮的动画图展示梯度下降的运行过程。你将看到 Sigmoid 函数、成本函数的等高线图、成本函数的三维曲面图以及学习曲线随着梯度下降运行而演变。

之后还会有另一个简短而实用的可选实验,我将向你展示如何使用流行的 Scikit-learn 库来训练用于分类的逻辑回归模型。

如今,许多机器学习从业者和公司都经常使用 Scikit-learn 作为工作的一部分。因此,我希望你也能查看 Scikit-learn 的相关功能,并了解它是如何使用的。


总结 🎯

本节课中我们一起学习了如何实现逻辑回归的梯度下降。这是一个非常强大且广泛使用的学习算法,你现在已经知道如何让它为你工作了。恭喜!

逻辑回归的实现核心在于:

  1. 定义正确的假设函数:f(x) = sigmoid(w·x + b)
  2. 应用梯度下降公式更新参数,其形式与线性回归相似,但内涵因 f(x) 不同而不同。
  3. 利用特征缩放和向量化等技术来优化算法性能。
  4. 掌握使用像 Scikit-learn 这样的高级工具库来简化模型训练过程。

37:过拟合问题 🎯

在本节课中,我们将要学习机器学习中两个常见且重要的问题:过拟合欠拟合。理解这两个概念对于构建有效的机器学习模型至关重要。我们将通过线性回归和逻辑回归的例子,直观地展示它们是什么,以及它们如何影响模型的性能。

概述:什么是过拟合与欠拟合?

你已经学习了线性回归和逻辑回归等算法,它们对许多任务效果良好。但有时,算法会遇到一个称为过拟合的问题,这会导致其性能变差。本节课将向你展示什么是过拟合,以及一个密切相关的、几乎相反的问题——欠拟合

在接下来的课程中,我将分享一些专门用于解决过拟合问题的技术,特别是称为正则化的方法。这是一种非常有用的技术,我一直在使用它。正则化将帮助你最小化过拟合问题,使你的学习算法工作得更好。现在,让我们先来看看什么是过拟合。

通过线性回归例子理解过拟合

为了帮助我们理解什么是过拟合,让我们看一个线性回归的例子。我将回到我们最初的例子:使用线性回归预测房价,其中你想根据房屋的大小来预测价格。

假设你的数据集看起来像这样,输入特征 x 是房屋的大小,目标值 y 是试图预测的房屋价格。

你可以做的一件事是将线性函数拟合到这个数据上。

如果你这样做,你会得到一条拟合数据的直线,可能看起来像这样。但这并不是一个很好的模型。

观察数据,似乎很明显,随着房屋面积的增加,房价趋于平缓。所以这个算法不能很好地拟合训练数据。这种情况的技术术语是模型欠拟合了训练数据。

另一个术语是算法具有高偏差。你可能在新闻中读到过一些学习算法不幸地表现出对某些种族或性别的偏见。术语“偏差”有多种含义。检查学习算法是否存在基于性别或种族等特征的偏见绝对至关重要。

但术语“偏差”也有第二个技术含义,这就是我在这里使用的含义,即如果算法欠拟合数据,意味着它甚至不能很好地拟合训练集,训练数据中存在一个清晰的模式,而算法却无法捕捉到。

思考这种偏差的另一种方式是,学习算法有一个非常强烈的先入之见,或者说一个非常强的偏差,认为房价完全是房屋大小的线性函数。

尽管有相反的数据,但这种认为数据是线性的先入之见导致它拟合了一条与数据匹配得很差的直线,从而导致它欠拟合数据。

现在,让我们看看模型的第二种变体。

如果你对数据拟合一个二次函数,使用两个特征 x,那么当你拟合参数 w1w2 时,你可以得到一条能更好地拟合数据的曲线,可能看起来像这样。

此外,如果你有一个不在五个训练样本中的新房子,这个模型可能在那所新房子上表现得相当好。所以,如果你是一个房地产经纪人,你希望你的学习算法即使在不在训练集中的例子上也能表现良好。这被称为泛化。从技术上讲,我们希望学习算法能够泛化得很好,这意味着即使是对它从未见过的新例子,也能做出好的预测。

所以这个二次模型似乎对训练集的拟合虽然不是完美,但相当好,并且我认为它能很好地泛化到新例子。

现在,让我们看看另一个极端,如果你对数据拟合一个四次多项式,那么你就有 xx⁴ 作为特征。

使用这个四次多项式,你实际上可以拟合一条恰好穿过所有五个训练样本的曲线。

你可能会得到一条看起来像这样的曲线。一方面,这似乎在拟合训练数据方面做得非常好,因为它完美地穿过了所有的训练数据。事实上,你可以选择使成本函数恰好等于零的参数,因为所有五个训练样本的误差都为零。

但这是一条非常曲折的曲线,它上下起伏。如果你在这里有一所房子,模型会预测这所房子比它小的房子更便宜。

所以我们不认为这是一个特别好的预测房价的模型。技术术语是,我们会说这个模型过拟合了数据,或者说这个模型有过拟合问题。

因为即使它非常地拟合训练集,但它拟合得“太好”了,因此它是过拟合的,而且看起来这个模型不会泛化到它从未见过的新例子。

这种情况的另一个术语是算法具有高方差

在机器学习中,许多人几乎可以互换地使用术语“过拟合”和“高方差”,也几乎可以互换地使用术语“欠拟合”和“高偏差”。过拟合或高方差背后的直觉是,算法非常、非常努力地试图拟合每一个训练样本。事实证明,如果你的训练集即使只有一点点不同,比如一所房子的价格只是高了一点点或低了一点点,那么算法拟合的函数最终可能会完全不同。

因此,如果两个不同的机器学习工程师将这个四次多项式模型拟合到只是略有不同的数据集上,他们最终可能会得到完全不同的预测,或者说高度可变的预测,这就是为什么我们说算法具有高方差。

将最右边的模型与中间的模型对比。对于同一所房子,似乎中间模型给出了更合理的价格预测。对于中间这种情况并没有一个特定的名称,但我将称之为“恰到好处”,因为它既没有欠拟合也没有过拟合。

所以你可以说,机器学习的目标是找到一个既没有欠拟合也没有过拟合的模型,换句话说,希望找到一个既没有高偏差也没有高方差的模型。

当我思考欠拟合和过拟合、高偏差和高方差时,我有时会想起儿童故事《金发姑娘和三只熊》。在这个儿童故事中,一个叫金发姑娘的女孩拜访了一个熊家庭。有一碗粥太冷了,不好吃。还有一碗粥太热了,也不能吃。但有一碗粥既不太冷也不太热,温度适中,正好可以吃。

所以总结一下,如果你有太多的特征,比如右边的四次多项式,那么模型可能很好地拟合训练集,但几乎是“太好”了,或者说过拟合且具有高方差。另一方面,如果你有太少的特征,那么就像左边的例子一样,它会欠拟合并具有高偏差。而在使用二次特征 x 的这个例子中,似乎是恰到好处的。

分类问题中的过拟合与欠拟合

到目前为止,我们看了线性回归模型中的欠拟合和过拟合。同样,过拟合也适用于分类问题。

这是一个有两个特征 x1x2 的分类例子。其中 x1 可能是肿瘤大小,x2 是患者年龄,我们试图将肿瘤分类为恶性(用叉号表示)或良性(用圆圈表示)。

你可以做的一件事是拟合一个逻辑回归模型,一个像这样的简单模型,其中 g 是 sigmoid 函数,里面的项是 z

如果你这样做,最终会得到一条直线作为决策边界,这是 z 等于零的线,它将正例和负例分开。

这条直线看起来并不糟糕,看起来还行,但看起来也不是对数据的很好拟合。所以这是一个欠拟合高偏差的例子。

让我们看另一个例子。如果你在特征中加入这些二次项,那么 z 就变成了中间这个新项,而决策边界(即 z 等于零的地方)看起来可能更像这样,更像一个椭圆或椭圆的一部分。

这是对数据的一个相当好的拟合,尽管它没有完美地分类训练集中的每一个训练样本。

注意一些叉号被错误地分类在圆圈之中。

但这个模型看起来相当不错,我称之为“恰到好处”,而且看起来它能很好地泛化到新患者。

最后,在另一个极端,如果你拟合一个非常高阶的多项式,带有许多许多像这样的特征,那么模型可能会非常努力地扭曲自己,以找到一个完美拟合你训练数据的决策边界。拥有所有这些高阶多项式特征允许算法选择这个真正过于复杂的决策边界。

如果特征是肿瘤大小和年龄,并且你试图将肿瘤分类为恶性或良性,那么这看起来并不是一个很好的预测模型。所以,这再次是过拟合高方差的一个实例,因为这个模型尽管在训练集上做得很好,但看起来不会很好地泛化到新例子。

总结

本节课中,我们一起学习了机器学习中的两个核心问题:

  1. 欠拟合:模型过于简单,无法捕捉数据中的基本模式,表现为高偏差。它甚至在训练数据上表现不佳。
  2. 过拟合:模型过于复杂,不仅学习了数据中的基本模式,还学习了噪声和随机波动,表现为高方差。它在训练数据上表现极好,但在新数据上泛化能力差。

我们的目标是找到一个“恰到好处”的模型,它既能很好地拟合训练数据,又能对新数据做出准确的预测。在下一节课中,我们将探讨如何解决过拟合问题,特别是通过正则化技术,并简要提及一些解决欠拟合的思路。

38:解决过拟合问题 🎯

在本节课中,我们将要学习机器学习中一个常见且重要的问题——过拟合,并探讨三种解决过拟合问题的核心方法。过拟合是指模型在训练数据上表现很好,但在新数据上表现不佳的现象。理解并解决过拟合是构建高效机器学习模型的关键一步。

概述 📋

在上一节中,我们介绍了过拟合和欠拟合的概念。本节中,我们来看看当发生过拟合时,可以采取哪些具体措施来应对。我们将重点讨论三种主要策略:获取更多训练数据、减少特征数量以及使用正则化技术。

解决过拟合的三种方法 🔧

以下是三种应对过拟合问题的核心策略。

1. 获取更多训练数据 📈

当模型出现高方差(即过拟合)时,最直接的方法是收集更多的训练数据。例如,在房价预测模型中,如果我们能获得更多关于房屋面积和价格的数据,学习算法就能学习到一个波动更小的函数。

核心思想:即使继续使用高阶多项式或包含大量特征的函数,只要有足够的训练样本,模型仍然可以表现良好。

总结:对抗过拟合的首要工具是获取更多训练数据。虽然这并非总是可行,但在数据可用时,这种方法通常效果显著。

2. 使用更少的特征 🎯

第二种解决过拟合的方法是减少使用的特征数量。在之前的例子中,模型特征包括尺寸 x 等多项式特征。减少这些特征的使用可以降低过拟合风险。

特征选择:在拥有大量特征(如房屋面积、卧室数量、房龄、社区平均收入、到最近咖啡店的距离等)但训练数据不足的情况下,模型容易过拟合。通过直觉或算法选择最相关的特征子集(如仅使用面积、卧室数量和房龄),可以减轻过拟合问题。

局限性:特征选择的缺点在于,它丢弃了部分可能对预测有用的信息。例如,也许所有100个特征都对房价预测有贡献。

后续学习:在课程2中,我们将看到一些自动选择最合适特征集的算法。

3. 应用正则化技术 ⚖️

第三种减少过拟合的方法是正则化。正则化不是直接消除特征(即将某些参数设为0),而是鼓励学习算法缩小参数值,从而更温和地减少某些特征的影响。

核心概念:即使拟合高阶多项式,只要能让算法使用较小的参数值(w₁, w₂, w₃, w₄...),最终得到的曲线就能更好地拟合训练数据。

公式表示:正则化通常通过修改代价函数来实现,例如在线性回归中加入正则化项:
J(w, b) = (1/2m) * Σ(ŷ⁽ⁱ⁾ - y⁽ⁱ⁾)² + (λ/2m) * Σ wⱼ²
其中,λ 是正则化参数,控制正则化的强度。

惯例:通常我们只减小 wⱼ 参数(w₁wₙ)的大小,是否对参数 b 进行正则化影响不大。在实践中,我经常使用正则化,这是一种非常有用的技术,也适用于后续将学到的神经网络。

总结与回顾 📝

本节课中我们一起学习了解决过拟合的三种主要方法:

  1. 收集更多数据:如果可能,获取更多训练数据是减少过拟合的有效方式。
  2. 特征选择:尝试选择并使用特征的一个子集。我们将在课程2中深入学习特征选择。
  3. 正则化:通过减小参数规模来减少过拟合。这将是下一视频的重点。

就个人而言,我经常使用正则化,这是一种训练学习算法(包括后续专业课程中将学到的神经网络)的非常有用的技术。

实践建议:可选实验 🧪

我建议大家查看关于过拟合的可选实验。在实验中,你将看到不同的过拟合示例,并通过点击图中的选项来调整这些示例。你还可以通过点击图表添加自己的数据点,观察拟合曲线的变化。实验涵盖了回归和分类的例子,你可以尝试改变多项式的阶数(如 x, , 等)。实验还提供了两种应对过拟合的选项:添加额外训练数据以减少过拟合,以及选择包含或排除哪些特征作为另一种减少过拟合的方法。希望这个实验能帮助你建立关于过拟合及其解决方法的直觉。

下节预告 🔮

本视频从较高层次介绍了正则化的思想。我意识到所有这些关于正则化的细节可能尚未完全明晰。在下一个视频中,我们将开始具体阐述如何应用正则化、正则化的确切含义,并探讨如何将正则化与我们的学习算法(线性回归、逻辑回归及未来的其他算法)结合,以避免过拟合。让我们在下一个视频中详细探讨。

39:正则化成本函数 📉

在本节课中,我们将学习如何通过修改成本函数来应用正则化,从而减少过拟合。正则化的核心思想是通过惩罚较大的参数值,促使模型学习更简单、更平滑的函数,提高泛化能力。


概述

上一节我们介绍了正则化的基本概念,即通过减小参数值来降低模型复杂度,从而减少过拟合。本节中,我们将基于这一直觉,为学习算法开发一个修改后的成本函数,以实际应用正则化。


从直觉到公式

回忆上一节的例子:如果我们用二次函数拟合数据,效果不错;但如果使用高阶多项式,则容易导致过拟合。

现在考虑以下情况:假设我们能够使参数 W3W4 变得非常小,接近于零。具体来说,我们在线性回归的成本函数中添加两项:1000 × W3²1000 × W4²。这里选择 1000 是因为它是一个较大的数,但任何其他大数也可以。

通过这个修改后的成本函数,如果 W3W4 较大,模型将受到惩罚。因为要最小化这个新函数,唯一的方法是使 W3W4 都变小,否则这两项会变得非常大。

因此,在最小化这个函数时,W3W4 将趋近于零,从而几乎抵消了特征 x⁴ 的影响。这样,我们最终得到的拟合结果更接近二次函数,可能只包含来自 x⁴ 的微小贡献。这比所有参数都较大时得到的复杂多项式拟合要好得多。

更一般地,正则化的思想是:如果参数值较小,模型就更简单(可能包含更少的特征),因此更不容易过拟合。


一般化的正则化方法

上一张幻灯片中,我们只惩罚了 W3W4。但更常见的实现方式是:如果你有很多特征(比如 100 个),你可能不知道哪些特征最重要、哪些应该被惩罚。

因此,正则化的典型实现方式是惩罚所有特征,更准确地说,是惩罚所有 Wⱼ 参数。可以证明,这通常会导致拟合出更平滑、更简单、更不易过拟合的函数。

例如,如果你有 100 个特征的数据,很难预先选择哪些特征应该包含或排除。因此,我们构建一个使用所有 100 个特征的模型,即参数 W₁W₁₀₀,以及第 101 个参数 b

由于我们不知道哪些参数重要,我们对所有参数进行轻微惩罚,通过添加以下新项来缩小它们:

λ × Σⱼ₌₁ⁿ Wⱼ²

其中 n 是特征数量(这里是 100)。这里的 λ(希腊字母 lambda)称为正则化参数。

类似于选择学习率 α,你现在也需要为 λ 选择一个值。

需要指出的是,按照惯例,我们通常将 λ 除以 2m,使得第一项和第二项都按 1/(2m) 缩放。这样做的好处是,更容易为 λ 选择一个合适的值。特别是,即使训练集大小增加(即 m 变大),之前选择的 λ 值也更可能继续有效。

另外,按照惯例,我们通常不惩罚参数 b。在实践中,是否惩罚 b 影响很小。一些机器学习工程师或算法实现可能会包含 λ/(2m) × b² 项,但在本课程中,我们采用更常见的惯例:只正则化参数 W,而不正则化参数 b


正则化成本函数总结

在这个修改后的成本函数中,我们希望最小化原始成本(均方误差成本)加上额外的第二项(称为正则化项)。这个新的成本函数平衡了两个目标:

  • 最小化第一项鼓励算法通过减小预测值与实际值之间的平方差来很好地拟合训练数据。
  • 最小化第二项鼓励算法保持参数 Wⱼ 较小,从而减少过拟合。

你选择的 λ 值指定了这两个目标之间的相对重要性或平衡方式。

以下是不同 λ 值对学习算法的影响:

  • 如果 λ = 0,则完全不使用正则化项,模型可能过拟合,拟合出过于复杂的曲线。
  • 如果 λ 非常大(例如 10¹⁰),则正则化项权重极大,最小化成本的唯一方式是使所有 W 值接近零,此时 f(x) ≈ b,模型拟合一条水平直线,导致欠拟合。

因此,你需要一个介于两者之间的 λ 值,以更适当地平衡最小化均方误差和保持参数较小这两个目标。当 λ 值既不太小也不太大时,你最终可能拟合出一个保持所有特征但形状更合理的四次多项式。


总结

本节课中,我们一起学习了如何通过修改成本函数来应用正则化。正则化通过在成本函数中添加惩罚项,促使模型参数变小,从而降低模型复杂度,减少过拟合。我们讨论了正则化的一般化方法,以及如何通过选择合适的 λ 值来平衡拟合训练数据和保持模型简单这两个目标。

在接下来的两节中,我们将具体探讨如何将正则化应用于线性回归和逻辑回归,以及如何使用梯度下降训练这些模型,从而在实际中避免过拟合。

40:正则化线性回归的梯度下降 🧮

在本节课中,我们将学习如何将梯度下降算法应用于正则化线性回归。我们将推导更新规则,并解释正则化如何影响参数更新过程。


概述

在上一节视频中,我们介绍了正则化线性回归的成本函数。本节中,我们将探讨如何使用梯度下降来最小化这个成本函数,并理解正则化项如何改变参数的更新方式。


正则化线性回归的成本函数

上一节我们得出的正则化线性回归成本函数如下:

公式:
[
J(\mathbf{w}, b) = \frac{1}{2m} \sum_{i=1}^{m} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y{(i)})2 + \frac{\lambda}{2m} \sum_{j=1}^{n} w_j^2
]

其中:

  • 第一部分是标准的平方误差成本函数。
  • 第二部分是额外的正则化项,(\lambda) 是正则化参数。
  • 目标是找到最小化正则化成本函数的参数 (\mathbf{w}) 和 (b)。

梯度下降回顾

在引入正则化之前,我们使用梯度下降来优化原始成本函数(仅第一部分)。其算法如下:

代码:

# 原始线性回归的梯度下降更新规则
repeat until convergence {
    w_j = w_j - α * (∂J/∂w_j)  # 对于 j = 1 到 n
    b = b - α * (∂J/∂b)
}

其中 (\alpha) 是一个很小的正数,称为学习率。


正则化后的梯度下降

对于正则化线性回归,梯度下降的更新规则看起来非常相似,只是成本函数 (J) 的定义有所不同。

公式:
[
\frac{\partial J}{\partial w_j} = \frac{1}{m} \sum_{i=1}^{m} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y^{(i)}) x_j^{(i)} + \frac{\lambda}{m} w_j
]
[
\frac{\partial J}{\partial b} = \frac{1}{m} \sum_{i=1}^{m} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y^{(i)})
]

请注意:

  • 对于 (w_j) 的导数增加了一项 (\frac{\lambda}{m} w_j)。
  • 对于 (b) 的导数保持不变,因为我们不对 (b) 进行正则化(不试图缩小 (b))。


正则化梯度下降算法

将上述导数代入梯度下降公式,我们得到正则化线性回归的完整算法。

以下是实现步骤:

代码:

# 正则化线性回归的梯度下降更新规则
repeat until convergence {
    w_j = w_j - α * [ (1/m) * Σ (f(xⁱ) - yⁱ) * x_jⁱ + (λ/m) * w_j ]  # 对于 j = 1 到 n
    b = b - α * (1/m) * Σ (f(xⁱ) - yⁱ)
}

请记住,所有参数的更新必须同步进行。


直观理解更新规则(可选)

为了更深入地理解公式的作用,我们可以重写 (w_j) 的更新规则。

公式:
[
w_j := w_j (1 - \alpha \frac{\lambda}{m}) - \alpha \frac{1}{m} \sum_{i=1}^{m} (f(\mathbf{x}^{(i)}) - y^{(i)}) x_j^{(i)}
]

第二项是未正则化线性回归的常规梯度下降更新。第一项中,((1 - \alpha \frac{\lambda}{m})) 是一个略小于 1 的数。

例如,设 (\alpha = 0.01),(\lambda = 1),(m = 50),则:
[
1 - \alpha \frac{\lambda}{m} = 1 - 0.01 \times \frac{1}{50} = 0.9998
]

因此,在每次迭代中,正则化的效果是先将 (w_j) 乘以一个略小于 1 的数(使其略微缩小),然后再进行常规更新。这解释了为什么正则化会持续地、轻微地缩小参数 (w_j) 的值。


导数计算推导(可选)

如果你对导数项的推导过程感兴趣,这里有一个简要说明。

对于线性回归,(f_{\mathbf{w},b}(\mathbf{x}) = \mathbf{w} \cdot \mathbf{x} + b)。

根据微积分规则,对 (w_j) 的导数计算如下:
[
\frac{\partial J}{\partial w_j} = \frac{1}{2m} \sum_{i=1}^{m} 2(\mathbf{w} \cdot \mathbf{x}^{(i)} + b - y^{(i)}) x_j^{(i)} + \frac{\lambda}{2m} \cdot 2 w_j
]

化简后(2 被约去),得到我们之前使用的表达式:
[
\frac{\partial J}{\partial w_j} = \frac{1}{m} \sum_{i=1}^{m} (f(\mathbf{x}^{(i)}) - y^{(i)}) x_j^{(i)} + \frac{\lambda}{m} w_j
]


总结

本节课中,我们一起学习了如何将梯度下降应用于正则化线性回归。

核心要点:

  1. 正则化成本函数在标准平方误差项基础上增加了 (\frac{\lambda}{2m} \sum w_j^2)。
  2. 梯度下降更新规则中,(w_j) 的导数增加了一项 (\frac{\lambda}{m} w_j),而 (b) 的更新不变。
  3. 从更新公式 (w_j := w_j (1 - \alpha \frac{\lambda}{m}) - ...) 可以直观看出,每次迭代正则化都会使 (w_j) 略微缩小。
  4. 掌握这些更新规则,你就能在特征多而训练样本相对较少时,使用正则化线性回归有效减少过拟合,从而在许多问题上获得更好的性能。

在下一个视频中,我们将把正则化的思想应用到逻辑回归中,以避免逻辑回归的过拟合问题。

41:正则化逻辑回归 🧠

在本节课中,我们将学习如何实现正则化逻辑回归。你将看到,其梯度下降更新规则与正则化线性回归非常相似。我们还将探讨正则化如何帮助逻辑回归模型避免过拟合,并介绍如何在代码中实现它。


过拟合问题与正则化的引入

上一节我们介绍了逻辑回归。本节中我们来看看当逻辑回归模型使用高阶多项式特征时,可能会遇到的问题。

如果使用非常高阶的多项式特征来拟合逻辑回归模型,模型可能会变得过拟合

上图中,Z 是一个高阶多项式,它被传入 Sigmoid 函数 来计算预测值 f。这可能导致决策边界过于复杂,完美地拟合了训练集,但泛化能力很差。

更普遍地说,当你用大量特征(无论是多项式特征还是其他特征)训练逻辑回归时,过拟合的风险会更高。


正则化逻辑回归的成本函数

为了解决过拟合问题,我们需要修改逻辑回归的成本函数,为其加入正则化项

逻辑回归原本的成本函数是:
J(w,b) = (1/m) * Σ L(f(x^(i)), y^(i))

要对其进行正则化,只需添加以下项:
+ (λ / (2m)) * Σ (w_j^2),其中求和从 j=1nn 是特征数量)。

因此,正则化后的成本函数为:

J_regularized(w,b) = (1/m) * Σ L(f(x^(i)), y^(i)) + (λ / (2m)) * Σ (w_j^2)

当我们最小化这个关于 wb 的成本函数时,它会惩罚参数 w1, w2, ..., wn,防止它们变得过大。

这样做之后,即使你使用带有很多参数的高阶多项式进行拟合,你仍然能得到一个更合理的决策边界,如上图所示。这个边界能有效区分正负样本,并且有望更好地泛化到训练集之外的新样本。


实现:梯度下降更新规则

现在我们知道需要最小化包含正则化项的成本函数 J(w,b)。那么如何实现呢?和之前一样,我们可以使用梯度下降法

以下是需要最小化的成本函数,梯度下降将执行以下同步更新:

w_jb 的更新规则如下。这些是梯度下降的常规更新规则。与正则化线性回归类似,当你计算这些导数项时,唯一的变化是w_j 的导数在末尾增加了一项 (λ/m) * w_j

以下是具体的更新公式:

  • w_j 的更新
    w_j := w_j - α * [ (1/m) * Σ ( (f(x^(i)) - y^(i)) * x_j^(i) ) + (λ/m) * w_j ]
  • b 的更新
    b := b - α * [ (1/m) * Σ (f(x^(i)) - y^(i)) ]

这个更新规则看起来与正则化线性回归非常相似。事实上,方程是完全相同的,唯一的区别是 f 的定义不再是线性函数,而是应用于 zSigmoid 函数

f = g(z) = 1 / (1 + e^(-z))

与线性回归类似,我们只正则化参数 w_j,而不正则化参数 b。这就是为什么 b 的更新规则没有变化。


代码实现与本周实验

在本周最后的可选实验中,你将重新审视过拟合问题。在实验的交互式图表中,你现在可以通过在梯度下降过程中启用正则化并选择 λ 值,来正则化你的回归和分类模型。

以下是实现过程中的一些关键代码片段图示:



请务必查看实现正则化逻辑回归的代码,因为在本周结束的实践实验中,你将需要自己实现它。




总结与展望 🎉

本节课中我们一起学习了如何实现正则化逻辑回归。你现在已经知道,通过向成本函数添加正则化项并相应调整梯度下降更新规则(特别是对 w_j 的更新),可以有效控制模型复杂度,防止过拟合。

掌握线性回归和逻辑回归,以及如何减少过拟合等技能,对于构建有价值的机器学习应用至关重要。恭喜你完成了第一门课程第三周也是最后一周的学习!

希望你能完成实践实验和测验。接下来还有更多令人兴奋的内容:在本专项的第二门课程中,你将学习神经网络(也称为深度学习算法)。神经网络是当今许多AI最新突破的核心。构建神经网络实际上运用了许多你已经学过的知识,如成本函数、梯度下降和Sigmoid函数。

再次祝贺你完成课程一的学习。希望你觉得实验很有帮助,我们将在下周关于神经网络的课程中再见。

42:以人为本的人工智能访谈 🧠💬

概述

在本节课中,我们将一起学习吴恩达教授与李飞飞教授关于“以人为本的人工智能”的对话。我们将了解李飞飞教授从物理学转向人工智能的历程、ImageNet项目的起源、人工智能在医疗等领域的应用,以及她对初学者进入AI领域的建议。本教程将对话内容整理成结构化的知识,帮助你理解人工智能发展的宏大愿景与个人成长路径。


从物理学到人工智能的转变 🔄

上一节我们概述了本次访谈的主题,本节中我们来看看李飞飞教授如何从物理学背景转向人工智能领域。

李飞飞教授最初在普林斯顿大学主修物理学。物理学培养了她提出宏大问题、追寻“北极星”目标的热情。在阅读爱因斯坦、罗杰·彭罗斯等伟大物理学家的著作时,她发现他们后期都在思考生命、智能等同样大胆的问题。这激发了她对智能这一主题的好奇心。

在本科期间,她在几个神经科学实验室实习,特别是与视觉相关的研究。她意识到,探究智能的本质与探究宇宙起源或物质构成一样,是一个极其大胆的问题。这促使她在研究生阶段从物理学转向了人工智能,尽管当时正值“AI寒冬”,该领域更多地被称为机器学习、计算机视觉或计算神经科学。

李飞飞教授认为,当前人工智能已成为一项普及且具有全球影响力的技术,任何人都有可能进入这个领域。她自身的经历表明,只要有热情,背景并非障碍。


ImageNet的起源故事 🖼️

上一节我们了解了李飞飞教授的学术转型,本节中我们来看看她主导的、对深度学习发展至关重要的ImageNet项目是如何诞生的。

ImageNet的起源并非简单地标注海量图片,而是源于对一个“北极星”级问题的追求。李飞飞教授在研究生阶段进入加州理工学院的计算机视觉与计算神经科学实验室。当时,机器学习作为新工具开始应用于计算机视觉领域,同时,数十年认知科学关于人类视觉处理的研究确立了一些关键规范问题,其中之一便是对自然物体的识别与理解。

她的博士研究很快遇到了一个持续至今的挑战:机器学习模型的泛化能力不足。模型容易过拟合,且缺乏足够的数据。当时整个领域依赖于手工设计特征。在博士阶段后期,她与导师意识到,如果相信物体识别这个“北极星”问题,就需要更多数据。

于是,他们先创建了Caltech-101数据集。当时互联网兴起,他们利用谷歌图片搜索下载图像,与母亲和几名本科生共同标注,建立了包含101个物体类别、约数千张图片的数据集。这个数据集为许多早期研究者(包括吴恩达教授和他的学生)提供了帮助。

然而,从数学角度,Caltech-101仍不足以支撑更强大的算法。成为助理教授后,李飞飞教授决定启动ImageNet项目——一个下载整个互联网图片并映射所有英语名词的宏大计划。这个想法最初遭到了许多质疑,例如有研究者公开质问:“如果你连一把椅子都识别不好,拥有22000个类别、1500万张图片的数据集有什么用?”但最终,这个庞大的数据集为全球无数研究者解锁了巨大价值,推动了深度学习的复兴。

李飞飞教授总结,ImageNet的成功是押注正确的“北极星”问题驱动它的数据相结合的结果。


人工智能在医疗与机器人领域的应用 🏥🤖

上一节我们探讨了基础研究的突破,本节中我们来看看李飞飞教授如何将计算机视觉与神经科学的基础应用于医疗保健和机器人等实际领域。

李飞飞教授的研究演化也遵循着动物视觉智能的演化路径。有两个方向令她特别兴奋:一是寻找能改善人类生活的真正有影响力的应用领域(如医疗保健),二是探究视觉的终极意义(这引导她尝试闭合感知与机器人学习之间的循环)。

在医疗保健方面,大约十年前,她与斯坦福医学院的Arnold Milstein博士合作时,得知一个令人震惊的数据:每年约有25万美国人死于医疗差错。其中,每年因医院获得性感染导致的死亡人数超过9.5万,是交通事故死亡人数的2.5倍以上,而这很大程度上与临床手部卫生实践不佳有关。另一个事实是,每年因跌倒导致的伤害和死亡花费超过700亿美元,主要发生在老年人家庭或医院。

2012年左右,正值硅谷对自动驾驶汽车技术兴奋不已之时,李飞飞教授观察了智能传感摄像头、激光雷达、机器学习算法以及对复杂高风险环境的整体理解技术。她意识到,在医疗保健服务中,许多人类行为过程处于“黑暗”中,如果能在病房或老年公寓部署智能传感器来帮助医护人员和患者更安全,将意义重大。于是,她与Arnold Milstein博士共同开启了“环境智能”研究议程。

然而,将AI应用于真实人类环境时,会面临许多人类层面的问题,例如隐私。她们最初使用不捕获RGB信息的深度摄像头来保护隐私。过去十年,技术发展为隐私保护计算提供了更强大的工具集,例如:

  • 设备端推理:随着芯片越来越强大,无需通过网络传输数据到中央服务器。
  • 联邦学习:虽然仍处于早期阶段,但这是另一个潜在的隐私保护工具。
  • 差分隐私
  • 加密技术

李飞飞教授指出,人类对隐私等问题的需求,实际上正在推动医疗保健领域环境智能的新一轮机器学习技术发展。


人工智能政策与生态系统建设 🏛️

上一节我们讨论了AI的技术应用与挑战,本节中我们来看看李飞飞教授在人工智能政策制定和生态系统建设方面所做的工作。

大约四年前,李飞飞教授与斯坦福大学的许多教职领导意识到,斯坦福大学在AI发展中有其历史角色和责任。他们认为,下一代AI的教育、研究和政策需要是以人为本的。

为此,他们成立了以人为本人工智能研究所。其中一项让她走出舒适区的工作便是更深入地参与政策思考与制定。她认为,AI对人类生活的影响(有时是负面影响)如此迅速,如果专家不参与政策讨论,对任何人都不利。这涉及到公平性、隐私、AI人才向产业流失、数据和计算资源集中在少数科技公司等问题。

斯坦福HAI参与的一项政策工作是牵头游说了一项名为《国家人工智能研究资源任务力量法案》的议案。该法案呼吁成立一个任务小组,为美国公共部门(尤其是高等教育和研究机构)制定路线图,以增加其获取AI计算和AI数据资源的机会,旨在重振美国AI创新与研究的生态系统。李飞飞教授是该法案下拜登政府任命的12人任务小组成员之一。她希望这项政策能起到激励作用,建设和 rejuvenate 生态系统。


给机器学习初学者的建议 🎓

上一节我们探讨了宏观的政策层面,本节中我们回到个人成长,看看李飞飞教授对刚刚开始机器学习之旅的人有何建议。

李飞飞教授认为,当今AI的入口比他们当年要宽广得多。对于有技术兴趣的人,互联网上有极其丰富的资源(如Coursera、YouTube、GitHub),鼓励年轻人利用这些资源学习,因为其中充满乐趣。

对于非技术背景但仍对AI充满热情的人,无论是下游应用、其激发的创造力、政策与社会角度,还是重要的社会问题(如数字经济、治理、历史、伦理、政治科学),她都邀请大家加入。因为有许多未知问题有待探索,例如:

  • 在数字时代如何定义和衡量我们的经济?
  • 当机器人和软件越来越多地参与工作流程时,这意味着什么?
  • 生成式AI的惊人进步对从音乐到艺术到写作的创作者意味着什么?

她认为,这是一个非常激动人心的时代,只要你对这个领域充满热情,无论来自何种背景,都能找到自己的角色。

此外,李飞飞教授还发起了名为“AI4ALL”(最初为“SAILORS”)的项目,旨在鼓励来自各行各业(尤其是传统上服务不足和代表性不足的社区)的高中生乃至更年轻的学生参与AI,通过夏令营、在线课程和大学途径项目,培养塑造未来AI的明日领袖。


总结

本节课中,我们一起学习了吴恩达教授与李飞飞教授的对话精华。我们回顾了李飞飞教授从物理学到AI的跨界历程,了解了ImageNet这一关键数据集背后的宏大愿景与迭代过程,探讨了AI在医疗保健等领域的实际应用与隐私挑战,认识了在政策层面构建健康AI生态系统的重要性,最后收获了给初学者的宝贵建议:AI领域依然年轻,充满无数待解的问题与机遇,无论你的背景如何,都可以找到参与并塑造未来AI的方式。保持好奇心,追寻自己的“北极星”问题。

43:欢迎 🎉

在本节课中,我们将要学习吴恩达《机器学习》专项课程第一门课的核心内容与学习路径。课程将重点介绍神经网络(深度学习)和决策树这两种强大且广泛应用的机器学习算法,并提供构建实用机器学习系统的宝贵建议。

课程概述 📋

欢迎来到机器学习专项课程的第一门课。在本课程中,你将学习神经网络(也称为深度学习算法)以及决策树。这些是当前最强大、应用最广泛的机器学习算法之一。你将有机会亲自实现它们,并让它们为你工作。

本课程的一个独特之处在于,它提供了关于如何构建机器学习系统的实用建议。在构建一个实用的机器学习系统时,你需要做出许多决策,例如:是应该花更多时间收集数据,还是应该购买更强大的GPU来构建更大的神经网络。

课程结构详解 🗓️

上一节我们介绍了课程的整体目标,本节中我们来看看为期四周的课程具体安排。

以下是本课程四周的详细学习内容:

  • 第一周:神经网络与推理
    我们将学习神经网络的工作原理以及如何进行推理或预测。例如,如果你从互联网下载了他人训练好并公开参数的神经网络,那么使用该网络进行预测的过程就称为推理。你将在第一周学习神经网络如何工作以及如何进行推理。

  • 第二周:训练神经网络
    接下来的一周,你将学习如何训练自己的神经网络。具体来说,如果你有一个带标签的训练样本集 (X, Y),你将学习如何为神经网络训练参数。

  • 第三周:构建机器学习系统的实用建议
    在第三周,我们将深入探讨构建机器学习系统的实用建议。我将与你分享一些技巧,我认为即使是当今高薪且成功构建机器学习系统的工程师,也并非总能始终如一地应用这些技巧。这些建议将帮助你更高效、更快速地构建自己的系统。

  • 第四周:决策树
    在课程的最后一周,你将学习决策树。虽然决策树在媒体上的热度不如神经网络,但它同样是应用广泛且非常强大的学习算法之一。如果你最终要构建一个应用,很可能会用到它。

学习起点:神经网络与人脑 🧠

以上我们了解了整个课程的框架,现在让我们正式进入学习。我们将从神经网络开始,并首先快速了解一下人类大脑(即生物大脑)是如何工作的。

让我们进入下一个视频。


本节课中我们一起学习了:机器学习专项课程第一门课的核心目标、独特价值(提供实用系统构建建议)以及为期四周的详细课程安排(神经网络推理、神经网络训练、系统构建建议、决策树)。课程即将从探索神经网络与人脑的关联正式开始。

44:神经元与大脑 🧠

在本节课中,我们将要学习神经网络最初的灵感来源——生物大脑的工作原理,并了解神经网络如何从这一生物灵感出发,发展成今天强大的机器学习工具。我们还将探讨为什么神经网络在近年来取得了如此迅猛的发展。

从大脑到神经网络

上一节我们介绍了神经网络的起源。本节中我们来看看其最初的生物动机。

几十年前,当神经网络首次被发明时,最初的动机是编写能够模仿人类大脑或生物大脑如何学习和运作的软件。

尽管今天的神经网络(有时也称为人工神经网络)与我们任何人所认为的大脑实际工作和学习的方式已经大不相同,但一些生物动机仍然保留在我们今天思考人工神经网络或计算机神经网络的方式中。

因此,让我们通过观察大脑如何工作以及这与神经网络的关系来开始。

大脑的卓越智能

人类大脑,或者更广义地说生物大脑,展现出了比我们迄今为止能够构建的任何东西都更高水平或更强大的智能。

因此,神经网络始于尝试构建模仿大脑的软件这一动机。

神经网络的发展历程

以下是神经网络发展过程中的几个关键阶段:

  • 20世纪50年代:神经网络研究开始。
  • 20世纪80年代和90年代初:重新流行起来,并在手写数字识别等应用中显示出巨大的潜力,当时甚至用于读取邮政编码以路由邮件,以及读取手写支票上的金额数字。
  • 20世纪90年代末:再次失宠。
  • 大约2005年:开始复兴,并随着“深度学习”这一术语的出现而有所重塑。

当时让我感到惊讶的事情之一是,深度学习和神经网络的含义非常相似,但我当时可能低估了“深度学习”这个术语听起来要好得多,因为它“深”且是“学习”,因此这成为了过去十年或十五年里兴起的品牌。自那时起,神经网络在一个又一个应用领域引发了革命。

我认为现代神经网络或深度学习产生巨大影响的第一个应用领域可能是语音识别,由于现代深度学习,我们开始看到更好的语音识别系统,像Leang和Jeff Hinton这样的作者对此起到了重要作用。

然后它开始进入计算机视觉领域。有时人们仍然会提到2012年的“ImageNet时刻”,那可能是最大的轰动,当时它抓住了人们的想象力,并对计算机视觉产生了巨大影响。

在接下来的几年里,它开始进入文本或自然语言处理等领域。现在,神经网络被用于从气候变化到医学成像,再到在线广告和更广泛的推荐系统等方方面面,现在机器学习的许多应用领域都使用神经网络,尽管今天的神经网络与大脑的学习方式几乎没有任何关系。

生物神经元的工作原理

上一节我们回顾了神经网络的历史。本节中我们来看看其灵感来源——生物神经元的具体结构。

这是一张说明大脑中神经元样子的示意图。

所有的人类思想都来自于你大脑和思维中像这样的神经元,它们发送电脉冲,有时与其他神经元形成新的连接。

给定一个像这样的神经元,它有许多输入,从其他神经元接收电脉冲,然后我圈出的这个神经元执行一些计算,并会发送其输出。

通过这些电脉冲传递给其他神经元,而这个上部神经元的输出又成为下面这个神经元的输入,它再次聚合来自多个其他神经元的输入,然后可能将其自己的输出发送给其他神经元,这就是构成人类思想的东西。

这是一张生物神经元的简化图。

一个神经元包含一个细胞体,如左图所示,如果你上过生物课,你可能会认出这是神经元的细胞核。正如我们在上一张幻灯片中看到的,神经元有不同的输入,在生物神经元中,输入线被称为树突,然后它偶尔会通过输出线(称为轴突)向其他神经元发送电脉冲。

不用担心这些生物学术语,如果你在生物课上见过它们,你可能记得它们,但为了构建人工神经网络,你实际上不需要记住任何这些术语。

但这个生物神经元可能会发送电脉冲,成为另一个神经元的输入。

人工神经元的数学模型

上一节我们了解了生物神经元。本节中我们来看看其简化的人工模型。

人工神经网络使用了一个非常简化的生物神经元数学模型,我在这里画一个小圆圈来表示单个神经元,神经元所做的是接收一些输入(一个或多个,只是数字),进行一些计算,并输出另一个数字,然后这个数字可以成为右边显示的第二个神经元的输入。

当你构建人工神经网络或深度学习算法时,通常希望同时模拟许多这样的神经元,而不是一次构建一个神经元,所以在这张图中,我画了三个神经元。

这些神经元集体所做的是输入几个数字,执行一些计算,并输出其他一些数字。

重要说明:生物与人工的差异

在这一点上,我想给出一个重要的告诫,尽管我在生物神经元和人工神经元之间做了一个松散的类比,但我认为今天我们对人类大脑如何工作几乎一无所知。事实上,每隔几年神经科学家就会在大脑如何工作方面取得一些根本性的突破,我认为在可预见的未来将继续如此,这对我来说是一个迹象,表明关于大脑实际如何工作还有许多突破有待发现,因此,盲目模仿我们今天所知的关于人类大脑的知识(坦率地说,这非常少)可能不会在构建真正智能方面走得太远,当然不会以我们目前的神经科学知识水平。

话虽如此,即使使用我们将要讨论的这些极其简化的神经元模型,我们也能够构建真正强大的深度学习算法。因此,当你深入研究神经网络和深度学习时,尽管起源是受生物学启发的,但不要过于认真地对待生物学动机。事实上,我们这些从事深度学习研究的人已经不再那么关注生物学动机,而是仅仅使用工程原理来找出如何构建更有效的算法,但我认为偶尔推测和思考生物神经元如何工作可能仍然很有趣。

神经网络为何现在兴起?

神经网络的思想已经存在了几十年,所以有几个人问我:“嘿,Andrew,为什么是现在?为什么只有在过去几年里神经网络才真正兴起?”

当我被问到这个问题时,我会为他们画这张图,如果别人问你这个问题,也许你也可以为他们画这张图。

让我在横轴上绘制你针对某个问题所拥有的数据量,在纵轴上绘制应用于该问题的学习算法的性能或准确性。

在过去的几十年里,随着互联网的兴起、手机的兴起、我们社会的数字化,我们在许多应用领域的数据量稳步向右移动。许多过去记录在纸上的东西,比如你订购某物,现在更可能是数字记录,而不是在纸上;如果你看医生,你的健康记录现在更可能是数字化的,而不是在纸上。

因此,在许多应用领域,数字数据量已经爆炸式增长。

我们看到的是,对于传统的机器学习算法,如逻辑回归和线性回归,即使你向这些算法输入更多数据,也很难让性能持续上升。就好像传统的学习算法,如线性回归和逻辑回归,它们根本无法随着我们现在可以输入的数据量而扩展,也无法有效利用我们为不同应用所拥有的所有这些数据。

而人工智能研究开始观察到的是,如果你在这个数据集上训练一个小型神经网络,那么性能可能看起来像这样。如果你训练一个中等规模的神经网络(意味着其中有更多的神经元),性能可能看起来像那样。如果你训练一个非常大的神经网络(意味着有很多这些人工神经元),那么对于某些应用,性能会持续上升。

因此,这意味着两件事:对于某一类你确实拥有大量数据的应用(有时你会听到“大数据”这个词被抛来抛去),如果你能够训练一个非常大的神经网络来利用你所拥有的海量数据,那么你可以在从语音识别到图像识别,再到自然语言处理应用等许多方面达到以前几代学习算法无法达到的性能,这导致了深度学习算法的兴起。这也是为什么更快的计算机处理器(包括GPU或图形处理器单元的兴起)——这种最初设计用于生成漂亮计算机图形的硬件,但结果证明对深度学习也非常强大——也是推动深度学习算法发展到今天这个样子的主要力量。

总结

本节课中我们一起学习了神经网络如何从模仿生物大脑的动机开始,了解了生物神经元的基本结构及其简化的人工模型。我们回顾了神经网络跌宕起伏的发展历程,并重点探讨了其近年来迅猛发展的关键原因:海量数据的可用性以及大型神经网络模型能够有效利用这些数据,从而在性能上超越传统算法。下一节,我们将更深入地探讨神经网络实际工作的细节。

45:03_01_03_需求预测

概述

在本节课中,我们将学习神经网络的基本工作原理。我们将从一个简单的需求预测例子开始,逐步构建一个神经网络模型,并解释其核心概念,如神经元、层、激活值和网络架构。通过本课,你将理解神经网络如何自动学习特征并进行预测。


从单个神经元到逻辑回归

为了说明神经网络的工作原理,我们从一个例子开始。我们将使用需求预测的例子,在这个例子中,你观察一个产品并尝试预测它是否会成为畅销品。

我们来看一个销售T恤的例子。你想知道某件特定的T恤是否会成为畅销品。你收集了不同T恤在不同价格下的销售数据,以及哪些成为了畅销品。零售商今天使用这种类型的应用程序来更好地规划库存水平和营销活动。如果你知道什么可能成为畅销品,你就可以提前计划,例如采购更多该商品的库存。

在这个例子中,输入特征X是T恤的价格,这是学习算法的输入。如果你应用逻辑回归来拟合一个Sigmoid函数到数据上,它可能看起来像那样。

那么你的预测输出可能看起来像这样:1 / (1 + e^(-(wx + b)))

之前,我们将此写作 f(x) 作为学习算法的输出。为了帮助我们构建神经网络,我将稍微改变一下术语,使用字母 a 来表示这个逻辑回归算法的输出。术语 a 代表激活,实际上是一个来自神经科学的术语,它指的是一个神经元向下游其他神经元发送高输出的程度。事实证明,这个逻辑回归单元或这个小逻辑回归算法可以被认为是大脑中单个神经元的非常简化的模型。

神经元所做的是将价格X作为输入,然后计算上面的公式,并输出数字 a,该数字通过此公式计算,它输出这件T恤成为畅销品的概率。

另一种思考神经元的方式是将其视为一台微型计算机,其唯一的工作是输入一个或几个数字(例如价格),然后输出一个或几个其他数字。在这种情况下,就是T恤成为畅销品的概率。正如我在上一个视频中提到的,逻辑回归算法比大脑中任何生物神经元所做的要简单得多,这就是为什么人工神经网络是人类大脑的一个极其简化的模型,尽管在实践中,正如你所知,深度学习算法确实工作得很好。


构建神经网络:层与激活

有了对单个神经元的描述,现在构建神经网络只需要将一堆这样的神经元连接或组合在一起。

现在让我们看一个更复杂的需求预测例子。在这个例子中,我们将有四个特征来预测一件T恤是否会成为畅销品。这些特征是:T恤的价格、运费、该特定T恤的营销量以及材料质量(是高质量厚棉布还是可能较低质量的材料)。

你可能会怀疑一件T恤是否会成为畅销品实际上取决于几个因素。首先是这件T恤的可负担性。其次是潜在买家对这件T恤的认知度。第三是感知质量,即买家是否认为这是一件高质量的T恤?

因此,我要做的是创建一个人工神经元来尝试估计这件T恤被认为高度可负担的概率。可负担性主要是价格和运费的函数,因为你必须支付的总金额是价格加上运费的总和。

所以我们将在这里使用一个小神经元,一个逻辑回归单元,输入价格和运费,并预测人们是否认为这是可负担的。

其次,我将在这里创建另一个人工神经元来估计这件T恤是否有很高的认知度。在这种情况下,认知度主要是T恤营销的函数。

最后,我将创建另一个神经元来估计人们是否认为这是高质量的。这可能主要是T恤价格和材料质量的函数。价格在这里是一个因素,因为幸运或不幸的是,如果一件T恤价格非常高,人们有时会认为它是高质量的,因为如果它非常昂贵,那么也许人们认为它必须是高质量的。

有了这些对可负担性、认知度和感知质量的估计,我们将这三个神经元的输出连接到右边这里的另一个神经元。然后,那里有另一个逻辑回归单元,最终输入这三个数字并输出这件T恤成为畅销品的概率。

在神经网络的术语中,我们将这三个神经元分组到一个所谓的层中。层是一组神经元的集合,它们接收相同或相似的特征作为输入,并一起输出几个数字。因此,左边的这三个神经元构成一个层,这就是我把它们画在彼此之上的原因。右边的单个神经元也是一个层。

左边的层有三个神经元,所以一个层可以有多个神经元,也可以像右边这个层一样只有一个神经元。右边的这个层也称为输出层,因为这个最终神经元的输出是神经网络预测的输出概率。

在神经网络的术语中,我们还将称可负担性、认知度和感知质量为激活值。术语“激活”来自生物神经元,指的是生物神经元向下游其他神经元发送高输出值或许多电脉冲的程度。因此,可负担性、认知度和感知质量上的这些数字就是这一层中这三个神经元的激活值。同样,这个输出概率是这里右边所示神经元的激活值。


神经网络的计算过程

因此,这个特定的神经网络进行如下计算:它输入四个数字,然后神经网络的这一层使用这四个数字来计算三个新的数字(也称为激活值),然后最终层(神经网络的输出层)使用这三个数字来计算一个数字。

在神经网络中,这四个数字的列表也称为输入层,它只是一个四个数字的列表。

现在我想对这个神经网络做一个简化。到目前为止我描述它的方式是,我们必须逐个检查神经元,并决定它从前一层接收哪些输入。例如,我们说可负担性只是价格和运费的函数,认知度只是营销的函数,等等。但是,如果你正在构建一个大型神经网络,手动决定哪些神经元应该接收哪些特征作为输入将是一项繁重的工作。

在实践中实现神经网络的方式是,某一层中的每个神经元,比如中间的这一层,将能够访问前一层(输入层)的每个特征、每个值。这就是为什么我现在从每个输入特征画箭头到中间这里显示的每个神经元。你可以想象,如果你试图预测可负担性,并且它知道价格、运费、营销和材料,也许它会学会忽略营销和材料,并通过适当设置参数来只关注与可负担性最相关的特征子集。

为了进一步简化这个神经网络的符号和描述,我将把这四个输入特征写成一个向量 X。我们将把神经网络视为拥有四个特征,它们构成了这个特征向量 x。这个特征向量被馈送到中间的这一层,然后该层计算三个激活值,也就是这三个数字。而这三个激活值反过来又成为另一个向量,被馈送到这个最终的输出层,最终输出这件T恤成为畅销品的概率。


神经网络的架构与术语

这就是神经网络的全部。它有几层,其中每一层输入一个向量并输出另一个数字向量。例如,中间的这一层输入四个数字 x,并输出三个对应于可负担性、认知度和感知质量的数字。

为了增加一点术语,你已经看到这一层被称为输出层,而这一层被称为输入层。为了也给中间的这一层起个名字,中间的这一层被称为隐藏层。

我知道这可能不是最好或最直观的名字,但这个术语来源于:当你有一个训练集时,在训练集中你可以观察到 Xy,你的数据集告诉你什么是 X 和什么是 y,所以你得到的数据告诉你正确的输入和正确的输出。但是你的数据集并没有告诉你可负担性、认知度和感知质量的正確值,因此这些的正确值是隐藏的,你在训练集中看不到它们,这就是为什么中间的这一层被称为隐藏层。


另一种视角:自动特征工程

我想与你分享另一种思考神经网络的方式,我发现这对建立我的直觉很有用。让我遮住这张图的左半部分,看看我们剩下什么。

你在这里看到的是一个逻辑回归算法或逻辑回归单元,它以一件T恤的可负担性、认知度和感知质量作为输入,并使用这三个特征来估计T恤成为畅销品的概率。所以这只是逻辑回归。但很酷的是,它没有使用原始特征(价格、运费、营销等),而是使用了新的、可能更好的特征——可负担性、认知度和感知质量——这些特征有望更好地预测这件T恤是否会成为畅销品。

所以,思考这个神经网络的一种方式是,它只是逻辑回归。但是,它是一个可以学习自己特征的逻辑回归版本,这使得它更容易做出准确的预测。

事实上,你可能还记得上一门课程中的这个住房例子,我们说如果你想预测房屋价格,你可能会取地块的前沿或宽度,乘以地块的深度,以构建一个更复杂的特征 x1 * x2,即地块的大小。所以我们在那里进行手动特征工程,我们必须查看特征 x1x2,并手动决定如何将它们组合在一起以提出更好的特征。

神经网络所做的是,它不需要你手动设计特征,正如你稍后将看到的,它可以学习自己的特征,使学习问题对自己来说更容易。这就是使神经网络成为当今世界上最强大的学习算法之一的原因。


总结与扩展

总结一下,神经网络是这样工作的:输入层有一个特征向量(在这个例子中是四个数字),它被输入到隐藏层,隐藏层输出三个数字(我将使用一个向量来表示这个隐藏层输出的激活向量),然后输出层接收这三个数字作为输入并输出一个数字,这将是神经网络的最终激活值或最终预测。

有一点需要注意,尽管我之前将这个神经网络描述为计算可负担性、认知度和感知质量,但神经网络的一个非常好的特性是,当你从数据中训练它时,你不需要明确地决定神经网络应该计算哪些其他特征(如可负担性等)。相反,它会自己完全弄清楚它想在隐藏层中使用哪些其他特征,这就是使它成为如此强大的学习算法的原因。

所以,你在这里看到了一个神经网络的例子,这个神经网络有一个隐藏层。让我们看一些其他神经网络的例子,特别是有多个隐藏层的例子。

这是一个例子:这个神经网络有一个输入特征向量 x,它被馈送到一个隐藏层(我称之为第一个隐藏层)。如果这个隐藏层有三个神经元,那么它将输出一个包含三个激活值的向量。这三个数字然后可以被输入到第二个隐藏层。如果第二个隐藏层有两个神经元(两个逻辑单元),那么这个第二个隐藏层将输出另一个现在包含两个激活值的向量,这个向量可能进入输出层,然后输出神经网络的最终预测。

或者这里是另一个例子:这是一个神经网络,其输入进入第一个隐藏层,第一个隐藏层的输出进入第二个隐藏层,然后进入第三个隐藏层,最后进入输出层。

当你构建神经网络时,你需要做出的决定之一是你想要多少个隐藏层,以及你希望每个隐藏层有多少个神经元。关于有多少个隐藏层以及每个隐藏层有多少个神经元的问题,是你神经网络架构的问题。在本课程后面,你将学习一些为神经网络选择合适架构的技巧,但选择正确数量的隐藏层和每层隐藏单元的数量也会影响学习算法的性能。所以在本课程后面,你也会学习如何为你的神经网络选择一个好的架构。

顺便说一下,在一些文献中,你看到这种类型的有多层的神经网络被称为多层感知机。所以你看到那只是指看起来像你在幻灯片上看到的这样的神经网络。


结束语

我知道我们在这个视频中讲了很多内容,所以感谢你坚持看完。你现在知道了神经网络是如何工作的。在下一个视频中,让我们看看这些想法如何应用到其他应用中,特别是我们将看看人脸识别的计算机视觉应用。让我们继续看下一个视频。

46:04_01_04_示例:图像识别 👁️

在本节课中,我们将学习神经网络如何应用于计算机视觉任务,特别是图像识别。我们将通过人脸识别的例子,了解神经网络如何从原始像素数据中自动学习并识别复杂的模式。


在上一节视频中,我们看到了神经网络在需求预测示例中的工作原理。本节中,我们来看看如何将类似的思想应用于计算机视觉应用。

如果你正在构建一个人脸识别应用程序,你可能希望训练一个神经网络。这个网络接收一张图片作为输入,并输出图片中人物的身份。

这张图片是1000x1000像素,因此它在计算机中的表示实际上是一个1000x1000的网格,也称为1000x1000的像素强度值矩阵。

在这个例子中,像素强度值或像素亮度值范围从0到255。因此,这里的197表示图像最左上角像素的亮度,185是旁边一个像素的亮度,依此类推。最右下角的像素亮度是214。

如果你将这些像素强度值展开成一个向量,最终会得到一个包含一百万个像素强度值的列表或向量。一百万是因为1000乘以1000等于一百万个数字。所以,人脸识别问题就是:能否训练一个神经网络,接收这个包含一百万个像素亮度值的特征向量作为输入,并输出图片中人物的身份?

以下是构建神经网络来执行此任务的一种方式。

输入图像X被送入第一层神经元,即第一个隐藏层。该层提取一些特征,其输出被送入第二个隐藏层。第二个隐藏层的输出再被送入第三个隐藏层,最后到达输出层。输出层会估计该图片是某个特定人物的概率。

一个有趣的现象是,观察一个在大量人脸图像上训练过的神经网络,并尝试可视化这些隐藏层在计算什么。事实证明,当你在大量人脸图片上训练这样一个系统,并观察隐藏层中不同神经元以了解它们可能计算什么时,你可能会发现以下情况。

以下是第一个隐藏层可能学习到的内容:

  • 一个神经元可能负责寻找类似这样的垂直线条或垂直边缘。
  • 第二个神经元可能寻找特定方向的线条或边缘。
  • 第三个神经元可能寻找另一个方向的线条。

因此,在神经网络的最初几层,你可能会发现神经元在图像中寻找非常短的线条或边缘。

如果我们观察下一个隐藏层,会发现这些神经元可能学会将许多短小的线条或边缘片段组合在一起,以寻找面部的组成部分。

以下是第二个隐藏层可能学习到的内容:

  • 第一个神经元看起来试图检测图像某个位置是否存在眼睛。
  • 第二个神经元看起来试图检测鼻子的轮廓。
  • 第三个神经元可能试图检测耳朵的底部。

接着,观察再下一层,在这个例子中,神经网络正在组合面部的不同部分,以尝试检测是否存在更大、更粗略的面部形状。最后,通过检测面部与不同面部形状的匹配程度,神经网络创建了一套丰富的特征,帮助输出层确定图片中人物的身份。

神经网络的一个显著特点是,在这个例子中,它可以完全自主地在不同的隐藏层学习这些特征检测器。没有人告诉它要在第一层寻找短小的边缘,在第二层寻找眼睛、鼻子和面部轮廓,在第三层寻找更完整的面部形状。神经网络能够仅从数据中自行找出这些模式。

需要注意的一点是,在这个可视化中,第一隐藏层的神经元被显示为观察相对较小的窗口以寻找边缘,第二隐藏层观察更大的窗口,第三隐藏层观察更大的窗口。因此,这些小小的神经元可视化实际上对应于图像中不同大小的区域。

为了更有趣,让我们看看如果在一个不同的数据集上训练这个神经网络会发生什么,比如大量汽车的侧面图片。

以下是训练汽车识别网络时各层可能学习的内容:

  • 第一层学习检测边缘,这与之前非常相似。
  • 第二隐藏层学习检测汽车的部件。
  • 第三隐藏层学习检测更完整的汽车形状。

因此,仅仅通过输入不同的数据,神经网络就会自动学习检测非常不同的特征,以便对其所训练的特定任务(无论是汽车检测、人物识别还是其他任务)进行预测。


这就是神经网络在计算机视觉应用中工作的方式。事实上,在本周晚些时候,你将看到如何自己构建一个神经网络并将其应用于手写数字识别应用。

到目前为止,我们一直在介绍神经网络的直观描述,让你感受它们是如何工作的。在下一个视频中,我们将更深入地探讨具体的数学原理和实现细节,了解如何实际构建神经网络的一层或多层,从而让你能够自己实现这样的网络。让我们继续观看下一个视频。

在本节课中,我们一起学习了神经网络如何应用于图像识别。我们了解到,神经网络能够从原始像素数据开始,在隐藏层中自动学习从简单边缘到复杂物体部件的层次化特征,最终完成识别任务。这种自动特征学习的能力是神经网络在计算机视觉领域强大的关键。

47:神经网络层 🧠

概述

在本节课中,我们将学习现代神经网络的基本构建模块——神经元层。我们将了解如何构建一个神经元层,并理解如何将这些构建模块组合起来,形成一个大型的神经网络。


神经元层的工作原理

上一节我们概述了神经网络的基本概念,本节中我们来看看一个具体的神经元层是如何工作的。

下图展示了一个需求预测的例子。该网络有四个输入特征,它们被输入到一个包含三个神经元的隐藏层。然后,隐藏层的输出被传递到一个仅包含一个神经元的输出层。

让我们放大隐藏层,观察其计算过程。这个隐藏层接收四个数字作为输入,这四个数字是层内三个神经元的共同输入。

每个神经元本质上都在实现一个小的逻辑回归单元或函数。

以下是第一个神经元的工作方式:

  • 它有两个参数 WB。为了表示这是第一个隐藏单元,我们将其下标标记为 W₁B₁
  • 它的输出是一个激活值 a,计算公式为 a = g(w₁ · x + b₁)。其中,w₁ · x + b₁ 是我们在之前逻辑回归课程中学到的熟悉的 z 值,而 g(z) 是熟悉的逻辑函数:g(z) = 1 / (1 + e⁻ᶻ)
  • 假设这个计算结果是数字 0.3,这就是第一个神经元的激活值 a。我们同样用下标将其标记为 a₁。因此,a₁ 可能是一个像 0.3 这样的数字,表示基于输入特征,该商品具有高可负担性的概率。

现在来看第二个神经元:

  • 第二个神经元有参数 W₂B₂,这是第二个逻辑单元的专属参数。
  • 它计算 a₂ = g(w₂ · x + b₂)。在这个例子中,假设结果为 0.7,表示我们认为潜在买家会注意到这件 T 恤的概率是 0.7。

同样地,第三个神经元:

  • 它有第三组参数 W₃B₃,并计算激活值 a₃ = g(w₃ · x + b₃),假设结果为 0.2。

在这个例子中,这三个神经元输出了 0.3、0.7 和 0.2。这个由三个数字组成的向量,就成为了激活值向量 a,它将被传递给神经网络的最终输出层。


层的编号与符号约定

当构建具有多层的神经网络时,为不同层编号会很有用。按照惯例:

  • 这个隐藏层被称为神经网络的 第 1 层
  • 输出层被称为 第 2 层
  • 输入层有时也被称为 第 0 层

如今,神经网络可以拥有数十甚至数百层。为了引入符号来帮助我们区分不同的层,我们将使用上标方括号 [l] 来索引不同的层。

具体来说:

  • a[1] 这个符号表示神经网络第一层(即这个隐藏层)的输出。
  • 同样,W₁[2]b₁[3] 是神经网络第一层中第一个单元的参数。
  • W₂[4]b₂[5] 是第一层中第二个隐藏单元的参数。
  • 这些激活值也可以加上标 [1],表示它们属于神经网络的第一层。

这个符号可能看起来有点复杂,但要记住的关键点是:每当看到上标 [1],它就指的是与神经网络第一层相关的量。如果看到上标 [2],则指与第二层相关的量,对于更多层的网络,第三层、第四层等也依此类推。


第二层(输出层)的计算

我们已经了解了这个神经网络第一层的计算,其输出是激活向量 a[6]。这个输出 a[7] 将成为第二层的输入。

现在,让我们放大神经网络的第二层(即输出层)的计算过程。

第二层的输入是第一层的输出,即我们刚刚在幻灯片前一部分计算出的向量 a[8] = [0.3, 0.7, 0.2]

因为输出层只有一个神经元,它所做的是计算其第一个(也是唯一一个)神经元的输出 a₁[2]**,计算公式为:**a₁[2] = g(w₁[9] · a[10] + b₁[11])。这里的 a[12] 是输入到该层的向量,g 和之前一样是应用于计算结果 z 的 Sigmoid 函数。

假设这算出的数字是 0.84,那么它就成为神经网络输出层的输出。在这个例子中,由于输出层只有一个神经元,这个输出是一个标量(单个数字),而不是一个数字向量。

沿用我们之前的符号约定,我们将使用上标 [2] 来表示与该神经网络第二层相关的量。因此,a[13] 是这一层的输出,也就是神经网络的最终输出。为了使符号一致,我们也可以给这些参数和激活值加上标 [2],表示它们属于神经网络的第二层。


生成最终预测

神经网络计算出 a[14] 后,还有一个可选的最终步骤:

  • 如果你想要一个二元预测(例如,是或否,1 或 0),你可以取计算出的数字 a₁[15](本例中是 0.84),并以 0.5 为阈值进行判断。
  • 如果它大于 0.5,你可以预测 ŷ = 1;如果小于 0.5,则预测 ŷ = 0。这个阈值处理过程在你学习专项课程第一门课的逻辑回归时也见过。

因此,如果你愿意,这个步骤可以给出最终预测 ŷ(1 或 0);如果你只想要一个成为畅销品的概率,则可以省略这一步。


总结

本节课中,我们一起学习了神经网络的核心构建块——神经元层的工作原理。我们了解到:

  1. 每一层接收一个数字向量作为输入。
  2. 应用多个逻辑回归单元(神经元)对其进行计算。
  3. 生成另一个数字向量,该向量在层与层之间传递。
  4. 直到最终输出层完成计算,得到神经网络的预测输出。
  5. 这个输出可以(可选地)通过 0.5 阈值处理来生成最终的二元预测。

这就是神经网络的工作方式。有了这个基础,我们就可以继续构建更复杂、更大型的神经网络模型。希望通过更多的例子,关于“层”的概念以及如何将它们组合起来构建神经网络的理解会更加清晰。

48:06_01_02 更复杂的神经网络 🧠

在本节课中,我们将学习如何将神经网络层组合起来,构建一个更复杂的多层神经网络。我们将详细介绍神经网络的层结构、各层之间的计算方式以及相关的数学符号,为后续理解神经网络的前向传播算法打下基础。

神经网络层结构回顾

在上一节视频中,我们学习了神经网络层的基本概念,它接收一个数字向量作为输入,并输出另一个数字向量。

本节中,我们来看看如何利用这种层结构来构建更复杂的神经网络。通过这个过程,我们希望使神经网络的符号表示变得更加清晰和具体。

一个四层神经网络的例子

让我们来看一个作为示例的更复杂的神经网络。这个网络包含四层(不计算输入层,即第0层)。其中,第1、2、3层是隐藏层,第4层是输出层。按照惯例,第0层是输入层。

约定:当我们说一个神经网络有“四层”时,这个数量包括了所有隐藏层和输出层,但不包括输入层。因此,按照网络层数的常规计数方式,这是一个四层神经网络。

聚焦第三层:计算过程详解

让我们放大观察第3层,也就是第三个(也是最后一个)隐藏层,来了解该层的计算过程。

第3层接收一个由前一层(第2层)计算出的向量 a[16] 作为输入,并输出另一个向量 a[17]

那么,第3层是如何执行从 a[18]a[19] 的计算呢?如果该层有3个神经元(或称为3个隐藏单元),那么它拥有参数 W1, B1, W2, B2, W3, B3。它的计算方式如下:

  • a1 = sigmoid( W1 · a[20] + B1 )
  • a2 = sigmoid( W2 · a[21] + B2 )
  • a3 = sigmoid( W3 · a[22] + B3 )

然后,该层的输出是一个由 a1, a2, a3 组成的向量。

按照惯例,为了更明确地表示这些与第3层相关的量,我们在所有符号的上标加上方括号 [3],以表明这些 WB 参数是与第3层的神经元相关联的,而这些激活值也是第3层的激活值。

请注意,上面公式中的项是 W1[23],表示与第3层相关的参数,它与 a[24](第2层的输出,即第3层的输入)进行点积。这就是为什么这里有 [3](因为是第3层的参数)和 [2](因为是第2层的输出)。

理解符号:一个小测验

现在,让我们快速检验一下对这些符号的理解。我将隐藏与第二个神经元相关的上标和下标。

在不回看视频的情况下(如果你想回看也可以,但建议先不要),你能思考并自行补全这个方程式中缺失的上标和下标吗?

请看看下面的方程式,并思考正确的上标和下标应该是什么。

以下是选项:

  1. a2[25] = g( W2[26] · a[27] + b2[28] )
  2. a2[29] = g( W2[30] · a[31] + b2[32] )
  3. a2[33] = g( W2[34] · a2[35] + b2[36] )

答案分析

如果你选择了第一个选项,那么你是正确的。

  • a2[37] 表示第3层第二个神经元的激活值。
  • 应用激活函数 g 时,我们使用同一个神经元的参数,因此 WB 的下标都是 2,上标都是 [3]
  • 输入特征是来自前一层(第2层)的输出向量,即 a[38]
  • 第二个选项使用了向量 a[39],但这并非前一层的输出向量。本层的输入是 a[40]
  • 第三个选项使用 a2[41] 作为输入,这只是一个单独的数字,而不是一个向量。请记住,正确的输入是向量 a[42](顶部有小箭头),而不仅仅是单个数字。

通用计算公式

总结一下,a2[43] 是与第3层第二个神经元相关的激活值,因此下标是 2。参数 W2[44]b2[45] 也是与第3层第二个神经元相关的。

以下是这个方程更通用的形式,适用于任意层 l 和任意单元 j

a_j[46] = g( W_j[47] · a[48] + b_j[49] )

其中:

  • a_j[50] 是第 l 层第 j 个单元(例如上面的 a2[51])的激活输出。
  • g 是激活函数(例如Sigmoid函数)。
  • W_j[52] 是第 l 层第 j 个单元的权重向量。
  • a[53] 是前一层的激活值向量(注意这里是 l-1,如上例中的 2)。
  • b_j[54] 是该层该单元的偏置参数。

这个公式给出了第 l 层第 j 个单元的激活值。上标 [l] 表示第 l 层,下标 j 表示第 j 个单元。在构建神经网络时,“单元 j” 指的是第 j 个神经元,这两个术语可以互换使用,因为层中的每个单元就是一个单独的神经元。

这里的 g 是Sigmoid函数。在神经网络上下文中,g 有另一个名称,也叫激活函数,因为 g 输出这个激活值。所以当我说“激活函数”时,指的就是这里的函数 g。目前你只见过Sigmoid这一种激活函数,但在后续课程中我们将看到其他函数也可以替代 g 的位置。因此,激活函数就是输出这些激活值的函数。

保持符号一致:输入层的表示

为了使所有符号保持一致,我还将给输入向量 x 另一个名称:a[55]

这样,同一个方程式也适用于第一层。当 l = 1 时,第一层的激活值 a[56] 就是 sigmoid( 权重 · a[57] + 偏置 ),而 a[58] 正是输入特征向量 x

本节总结

通过引入这套符号体系,你现在知道了如何根据参数以及前一层的激活值,计算神经网络中任意一层的激活值。

具体来说,你现在知道了在给定前一层激活值的情况下,如何计算任何一层的激活值。在接下来的视频中,我们将把这些知识整合到神经网络的推理算法(即前向传播算法)中,也就是让神经网络进行预测的方法。

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

  1. 多层神经网络的层结构定义和计数惯例。
  2. 神经网络中单层的详细计算过程。
  3. 神经网络中用于表示层、神经元、权重、偏置和激活值的标准数学符号。
  4. 计算任意层激活值的通用公式:a_j[59] = g( W_j[60] · a[61] + b_j[62] )
  5. 将输入层统一表示为 a[63],以保持公式的一致性。

这些概念是理解神经网络如何工作的基础,下一节我们将利用它们来实现完整的预测过程。

49:07_01_03_推理:进行预测与正向传播 🧠➡️

在本节课中,我们将学习如何将神经网络的知识整合成一个算法,使其能够进行推理或预测。这个算法被称为正向传播。我们将通过一个手写数字识别的例子来详细讲解其工作原理。


概述

我们将构建一个神经网络,用于区分手写数字0和1。这是一个二元分类问题。网络将接收一个图像作为输入,并输出该图像是数字1的概率。

为了简化,我们使用一个8x8像素的图像,即64个像素强度值作为输入特征。神经网络将包含两个隐藏层:第一层有25个神经元,第二层有15个神经元,最后是一个输出层。


正向传播算法详解

上一节我们介绍了神经网络的基本架构。本节中,我们来看看数据是如何从输入层,经过各隐藏层,最终到达输出层的。这个过程就是正向传播。

以下是正向传播的计算步骤:

  1. 从输入 x 计算第一层激活值 a¹
    第一层(第一个隐藏层)执行以下计算:
    a¹ = g(W¹·a⁰ + b¹)
    其中,a⁰ 是输入特征 x 是第一层的参数。由于该层有25个神经元,因此 是一个包含25个值的向量。

  2. 从 a¹ 计算第二层激活值 a²
    第二层(第二个隐藏层)执行类似的计算:
    a² = g(W²·a¹ + b²)
    其中, 是第二层的参数。该层有15个神经元,因此 是一个包含15个值的向量。

  3. 从 a² 计算输出层激活值 a³
    最后,输出层进行计算:
    a³ = g(W³·a² + b³)
    输出层只有一个神经元,因此 是一个标量值,代表图像是数字1的概率。我们可以将其阈值设为0.5来进行最终的二元分类判断。

这个计算序列从 x 开始,依次计算 ,最终得到 (即神经网络的输出 f(x))。由于计算方向是从左到右、从前到后传播神经元的激活值,因此该算法被称为正向传播

这与用于学习参数的反向传播算法形成对比,反向传播的内容将在下周学习。


网络架构特点

值得注意的是,本例中的神经网络架构(隐藏层神经元数量先多后少)是一种常见的选择。随着网络越接近输出层,隐藏单元的数量通常会减少。你在实践练习中会看到更多这样的例子。

掌握了正向传播算法,你就能下载他人训练好并发布在网上的神经网络参数,并利用他们的网络对你的数据进行推理预测。


总结

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

  1. 如何将神经网络的层间计算组织成正向传播算法。
  2. 该算法如何从输入 x 开始,通过逐层计算激活值,最终得到预测输出 f(x)
  3. 正向传播是进行神经网络推理(即利用已训练好的模型进行预测)的核心步骤。

在下一个视频中,我们将看看如何在 TensorFlow 中实际实现这个算法。

50:08_01_01_代码中的推理 🧠💻

在本节课中,我们将学习如何使用 TensorFlow 框架来实现神经网络的前向传播(推理)过程。我们将通过两个具体的例子——咖啡豆烘焙优化和手写数字识别——来演示如何在代码中构建网络层、计算激活值并得到预测结果。


神经网络推理的通用性

神经网络的一个显著特点是,相同的算法可以应用于许多不同的场景。为了清晰地展示神经网络的工作原理,本节将使用另一个例子来说明推理过程。

示例一:咖啡豆烘焙优化 ☕️

有时我喜欢在家烘焙咖啡豆,我最喜欢的是 Ash kungin 咖啡豆。那么,学习算法能否帮助优化烘焙过程以获得更高质量的咖啡豆呢?

在烘焙咖啡时,你可以控制两个参数:温度烘焙时长。在这个简化的例子中,我们创建了一个数据集,包含了不同的温度、时长组合以及对应的标签(咖啡是否好喝)。其中,正类(y=1)代表好咖啡,负类代表坏咖啡。

观察数据集可以发现一个合理的模式:如果温度太低或时间太短,咖啡豆会烘烤不足;如果温度太高或时间太长,咖啡豆则会烘烤过度甚至烧焦。只有在这个小三角形区域内的参数组合,才能产出好咖啡。

尽管这是一个简化示例,但实际上已有严肃的机器学习项目被用于优化咖啡烘焙。我们的任务是:给定一个包含温度和时长的特征向量 X(例如 200 摄氏度,17 分钟),如何通过神经网络推理来判断这个设置是否能产出好咖啡?

以下是使用 TensorFlow 实现推理的关键步骤:

  1. 定义输入:将输入特征 X 设置为一个包含两个数字的数组。

    X = np.array([200, 17])
    
  2. 构建第一层(隐藏层):使用 Dense 层定义第一个隐藏层,包含 3 个神经元,激活函数为 sigmoid

    layer_1 = Dense(units=3, activation='sigmoid')
    a1 = layer_1(X)
    # 假设 a1 的值为 [0.2, 0.7, 0.3]
    
  3. 构建第二层(输出层):定义第二个 Dense 层,包含 1 个神经元,同样使用 sigmoid 激活函数。

    layer_2 = Dense(units=1, activation='sigmoid')
    a2 = layer_2(a1)
    # 假设 a2 的值为 0.8
    
  4. 进行预测:通过阈值(例如 0.5)将连续的激活值转换为二元预测。

    y_hat = 1 if a2 >= 0.5 else 0
    

以上就是使用 TensorFlow 进行神经网络推理的核心步骤。其中涉及一些额外细节,例如如何加载 TensorFlow 库以及如何加载神经网络的参数 WB,这些将在实验课中详细讲解。


示例二:手写数字识别 ✍️➡️🔢

上一节我们通过咖啡烘焙的例子了解了推理的基本流程,现在让我们回到手写数字分类问题,看看如何处理图像数据。

在这个例子中,输入 X 是代表图像像素强度值的列表。

以下是构建和进行推理的步骤:

  1. 定义输入X 是一个包含像素强度值的数组。

    X = np.array([...]) # 像素值列表
    
  2. 构建并计算第一层:第一层是一个包含 25 个神经元、使用 sigmoid 激活函数的 Dense 层。

    layer_1 = Dense(units=25, activation='sigmoid')
    a1 = layer_1(X)
    
  3. 构建并计算第二层:类似地,设置第二层。

    layer_2 = Dense(units=15, activation='sigmoid')
    a2 = layer_2(a1)
    
  4. 构建并计算输出层:第三层是最终的 Dense 层。

    layer_3 = Dense(units=1, activation='sigmoid')
    a3 = layer_3(a2)
    

  1. 生成预测:最后,可以选择对 a3 设置阈值来得到最终的二元预测结果 y_hat


总结与下节预告 📝

本节课中,我们一起学习了在 TensorFlow 中实现神经网络前向传播(推理)的语法和步骤。我们通过咖啡烘焙和数字识别两个例子,演示了如何定义网络层、计算激活值并生成预测。

需要简要提及的是,TensorFlow 以特定的方式处理数据,理解其数据结构(如 NumPy 数组的格式)对于正确使用它至关重要。在下一个视频中,我们将具体看看 TensorFlow 是如何处理数据的。

51:TensorFlow与NumPy数据表示详解 🧮

在本节课中,我们将学习数据在NumPy和TensorFlow中是如何表示的。理解这些表示方式对于正确实现神经网络至关重要。我们将通过具体示例,清晰地展示矩阵、向量的概念及其在代码中的存储方式,并解释两种库之间数据格式转换的必要性。

TensorFlow中的数据表示

上一节我们介绍了课程目标,本节中我们来看看TensorFlow如何表示数据。假设你有一个来自咖啡预测示例的数据集,输入特征向量通常表示为:

x = np.array([[200, 17]])

你可能会问,为什么这里使用了两层方括号?这与数据的矩阵表示有关。

NumPy中的矩阵与向量

如果你觉得矩阵和向量是复杂的数学概念,请不要担心。我们将通过具体示例来讲解,你将掌握实现神经网络所需的所有矩阵和向量操作。

首先,让我们看一个矩阵的例子。

以下是一个具有两行三列的矩阵。注意,这里有两行和三个列。我们称之为一个2x3矩阵。

惯例是矩阵的维度写作“行数 x 列数”。在代码中存储这个2x3矩阵,你可以这样写:

x = np.array([[1, 2, 3],
              [4, 5, 6]])

请注意,方括号表明[1, 2, 3]是这个矩阵的第一行,[4, 5, 6]是第二行。最外层的方括号将第一行和第二行组合在一起。因此,这行代码将x定义为此数字数组。矩阵本质上就是一个二维数字数组。

让我们再看一个例子。

这里我写出了另一个矩阵。它有多少行和多少列?我们可以数一下,它有四行和两列。因此,这是一个4x2矩阵。

在代码中存储它,你会这样写:

x = np.array([[10, 20],
              [30, 40],
              [50, 60],
              [70, 80]])

这创建了一个包含这八个数字的二维数组。

矩阵可以有不同的维度。你看到了一个2x3矩阵和一个4x2矩阵的例子。矩阵也可以是其他维度,例如1x2或2x1。我们将在下一张幻灯片中看到这些例子。

因此,之前我们将输入特征向量x设置为np.array([[200, 17]])。这样做创建了一个1x2矩阵,即只有一行和两列。

让我们看一个不同的例子。如果你将x定义为:

x = np.array([[200],
              [17]])

这将创建一个2x1矩阵,它有两行和一列。因为第一行只是数字200,第二行只是数字17。所以,这包含了相同的数字,但放在一个2x1矩阵中,而不是1x2矩阵。

在NumPy中,顶部的例子也称为行向量,它是一个只有单行的向量。而这个例子也称为列向量,因为它是一个只有单列的向量。

使用双层方括号(如[[200, 17]])与单层方括号(如[200, 17])的区别在于:

顶部的两个例子是二维数组,其中一个维度恰好为1。而这个例子([200, 17])产生的是一个一维向量。所以这只是一个一维数组,没有行或列的概念,尽管按照惯例我们可能将X写成这样的列形式。

为了与此对比,我们在第一门课程中曾将x写成带单层方括号的形式([200, 17]),这在Python中产生的是一个一维向量,而不是二维矩阵。从技术上讲,这不是1x2或2x1,它只是一个没有行或列的线性数组,只是一个数字列表。

在第一门课程中,当我们处理线性回归和逻辑回归时,我们使用这些一维向量来表示输入特征X。而在TensorFlow中,惯例是使用矩阵来表示数据。

为什么会有这种惯例的转变?事实证明,TensorFlow被设计用于处理非常大的数据集。通过用矩阵而不是一维数组表示数据,TensorFlow可以在内部实现更高的计算效率。

回到我们最初的例子,对于数据集中第一个训练样本(特征为200摄氏度烘焙17分钟),我们是这样表示的:

x = np.array([[200, 17]])

这实际上是一个1x2矩阵,恰好有一行两列来存储数字200和17。

如果这些细节和复杂的惯例看起来很多,请不要担心。当你自己在可选实验和实践实验中看到代码的具体实现时,所有这些都会变得更加清晰。

神经网络中的前向传播

回到在神经网络中执行前向传播或推理的代码。

当你计算a1 = layer1(x)时,a1是什么?因为这一层有三个神经元,a1实际上将是一个1x3矩阵。

如果你打印出a1,你会得到类似这样的结果:tf.Tensor([[2.7, -1.2, 0.3]], shape=(1, 3), dtype=float32)

shape=(1, 3)指的是这是一个1x3矩阵。dtype=float32是TensorFlow的方式,表示这是一个浮点数,即一个可以有小数点的数字,在计算机中使用32位内存表示。

那么,什么是张量?这里的张量是TensorFlow团队创建的一种数据类型,用于高效存储矩阵并对其进行计算。所以,每当你看到“Tensor”,就把它看作是这几张幻灯片中讨论的矩阵。

从技术上讲,张量比矩阵更通用一些,但就本课程的目的而言,可以将张量视为表示矩阵的一种方式。

记得我在本视频开始时说过,TensorFlow有一种表示矩阵的方式,NumPy有另一种表示矩阵的方式。这是NumPy和TensorFlow创建历史留下的产物。不幸的是,有两种表示矩阵的方式被固化在了这两个系统中。

实际上,如果你想将a1(一个张量)转换回NumPy数组,你可以这样做:a1.numpy()。这将获取相同的数据,并以NumPy数组的形式返回,而不是TensorFlow数组或TensorFlow矩阵的形式。

现在,让我们看看第二层输出的激活值会是什么样子。

这是我们之前的代码:layer2是一个具有一个神经元和Sigmoid激活函数的密集层。a2通过将layer2应用于a1来计算:a2 = layer2(a1)

那么a2是什么?a2可能是一个像0.8这样的数字。从技术上讲,这是一个1x1矩阵,是一个有一行一列的二维数组。

因此,a2等于这个数字0.8。如果你打印出a2,你会看到它是一个只有一个元素(数字0.8)的TensorFlow张量,并且它是一个1x1矩阵。同样,它是一个float32类型的小数点数字,在计算机内存中占用32位。

再次强调,你可以使用a2.numpy()将TensorFlow张量转换为NumPy矩阵,这将把它转换回看起来像这样的NumPy数组。

希望这能让你了解数据在TensorFlow和NumPy中是如何表示的。

数据转换与协同工作

我习惯于在NumPy中加载和操作数据。当你将一个NumPy数组传递给TensorFlow时,TensorFlow会将其转换为自己内部的格式——张量,然后使用张量进行高效操作。当你读取数据时,可以将其保留为张量,或转换回NumPy数组。

我认为有点遗憾的是,这些库的演进历史使我们不得不做这些额外的转换工作,而实际上这两个库可以很好地协同工作。但是,在编写代码时,无论你使用的是NumPy数组还是张量,来回转换只是需要注意的事情。

总结

本节课中我们一起学习了数据在NumPy和TensorFlow中的表示方式。我们了解到:

  1. 矩阵是二维数组,维度表示为“行数 x 列数”。
  2. 向量可以表示为行向量(1xN矩阵)或列向量(Nx1矩阵)
  3. TensorFlow倾向于使用矩阵(即使是单样本)来表示数据以提高大规模计算效率,而早期课程中我们常用一维数组
  4. 张量是TensorFlow用于高效矩阵计算的核心数据结构。
  5. 可以使用.numpy()方法在TensorFlow张量和NumPy数组之间进行转换。

理解这些表示差异和转换方法,是使用TensorFlow构建有效神经网络模型的重要基础。在接下来的课程中,我们将应用这些知识来实际构建一个神经网络。

52:10_01_03_构建神经网络 🧠

概述

在本节课中,我们将学习如何在 TensorFlow 中构建一个完整的神经网络。我们将整合之前学到的关于层构建、前向传播和数据处理的零散知识,并介绍一种更简洁的构建方法。

整合知识:从层到网络

上一节我们介绍了如何在 TensorFlow 中构建单个层并进行前向传播。本节中,我们将把这些部分组合起来,构建一个完整的神经网络。

之前,我们手动地初始化数据 X,创建第一层,计算激活值 a1,然后创建第二层并计算 a2。这是一种显式的、逐层进行前向传播的方式。

# 之前的显式方法示例
layer1 = tf.keras.layers.Dense(units=3, activation='sigmoid')
a1 = layer1(X)
layer2 = tf.keras.layers.Dense(units=1, activation='sigmoid')
a2 = layer2(a1)

更简洁的方法:Sequential 模型

TensorFlow 提供了一种更简洁的实现前向传播和学习的方式。我们可以让 TensorFlow 自动将各层连接起来形成一个神经网络,这通过 Sequential 函数实现。

以下是构建神经网络的新方法:

  1. 创建第一层和第二层。
  2. 使用 Sequential 函数告诉 TensorFlow 将这些层按顺序连接起来。
model = tf.keras.Sequential([
    tf.keras.layers.Dense(units=3, activation='sigmoid'),
    tf.keras.layers.Dense(units=1, activation='sigmoid')
])

Sequential 框架可以为我们完成大量工作。

训练与推理

假设我们有一个如左图所示的训练集(以咖啡杯分类为例)。我们可以将训练数据输入 X 放入一个 NumPy 数组中(例如一个 4x2 的矩阵),目标标签 y 可以存储为一个长度为 4 的一维数组。

给定以矩阵 X 和数组 y 形式存储的数据,训练这个神经网络只需要调用两个函数:

  1. 调用 model.compile() 并设置一些参数(下周将详细讨论)。
  2. 调用 model.fit(X, y),告诉 TensorFlow 使用数据 Xy 来训练这个由层1和层2顺序连接而成的神经网络。
model.compile(...)  # 参数下周详述
model.fit(X, y)

最后,如何进行推理或前向传播?如果你有一个新样本 x_new(一个包含两个特征的 NumPy 数组),你只需调用 model.predict(x_new)。这将为你输出对应的 a2 值,而无需自己逐层计算。

predictions = model.predict(x_new)

代码惯例简化

按照 TensorFlow 的编码惯例,我们通常不会显式地将两个层赋值给变量 layer1layer2。更常见的写法是直接将层定义在 Sequential 函数内部,使代码更紧凑。

model = tf.keras.Sequential([
    tf.keras.layers.Dense(units=3, activation='sigmoid'),
    tf.keras.layers.Dense(units=1, activation='sigmoid')
])

应用于手写数字分类示例

让我们将这个新方法也应用于手写数字分类的例子。之前,我们有输入 X,然后逐层应用 layer1layer2layer3 来尝试分类数字。

使用新的 Sequential 编码惯例,你可以指定 layer1layer2layer3,并告诉 TensorFlow 为你将它们连接成一个神经网络。同样,你可以将数据存储在矩阵中,运行 compile 函数并按如下方式拟合模型。最后,使用 model.predict(x_new) 进行推理或预测。

按照惯例,我们同样会采用更紧凑的代码形式,直接将这三个层放入 Sequential 函数中。

model = tf.keras.Sequential([
    tf.keras.layers.Dense(units=25, activation='sigmoid'),
    tf.keras.layers.Dense(units=15, activation='sigmoid'),
    tf.keras.layers.Dense(units=1, activation='sigmoid')
])

理解与实现

有时,仅用几行代码就能构建一个复杂的、先进的神经网络,这可能会让人疑惑:这几行代码究竟做了什么?

机器学习专业课程的一个目标是让你能够使用像 TensorFlow 这样的前沿库高效地工作。但更重要的是,我希望你不仅会调用这几行代码,还能理解代码底层实际在做什么。

在实践中,大多数机器学习工程师并不经常用 Python 从头实现前向传播,我们只是使用 TensorFlow 和 PyTorch 等库。然而,理解这些算法的工作原理至关重要。这样,当出现问题时,你可以自己思考需要改变什么,什么可能有效,什么可能无效。

因此,在接下来的视频中,我们将回顾并分享如何用 Python 从头实现前向传播,以便你能自己理解整个过程。这样,即使你在调用库函数并让它高效运行、在你的应用中完成出色的工作时,你的脑海深处也能对你的代码实际在做什么有更深入的理解。

总结

本节课中,我们一起学习了在 TensorFlow 中构建神经网络的完整流程。我们首先回顾了逐层构建的显式方法,然后引入了更简洁的 Sequential 模型构建方式,它能够自动将各层连接起来。我们还介绍了如何使用 compilefitpredict 方法来训练网络和进行预测,并了解了遵循代码惯例的简化写法。最后,我们强调了理解底层实现原理的重要性,为接下来的深入学习做好了准备。

53:单层神经网络的正向传播实现 🧠

在本节课中,我们将学习如何仅使用 Python 和 NumPy 库,从零开始实现单层神经网络的正向传播过程。理解这一底层实现,有助于我们深入理解 TensorFlow 和 PyTorch 等高级框架背后的工作原理。


上一节我们介绍了神经网络的基本概念,本节中我们来看看如何用代码具体实现一个神经元和单层网络的计算。

我们将继续使用咖啡烘焙的模型作为示例。我们的目标是:给定一个输入特征向量 x,通过正向传播计算得到输出

在本次 Python 实现中,我将使用一维数组来表示所有的向量和参数,这就是为什么代码中只使用单层方括号,它代表一维数组,而非我们之前看到的二维矩阵。

计算第一个神经元激活值

首先,我们需要计算第一个激活值 a₁¹,即第一层的第一个神经元的输出。它由以下公式计算得出:

a₁¹ = g(z₁¹)

其中,z₁¹ 的计算公式为:

z₁¹ = w₁¹ · x + b₁¹

遵循本幻灯片中的命名约定,像 w₂₁ 这样的项,我将用变量 W2_1 来表示。下划线后的数字代表下标。因此,W2_1 即表示 w²₁

假设参数 w₁¹ = [1, 2],b₁¹ = -1。计算步骤如下:

  1. 计算 z₁¹ 为参数 w₁¹ 与输入 x 的点积,再加上 b₁¹
  2. 将 Sigmoid 激活函数 g 应用于 z₁¹,得到 a₁¹

计算同层其他神经元

接下来,我们以同样的方式计算 a₁²

类似地,w₁² = [2, -4],b₁² = 1。计算 z₁² 的中间项,然后应用 Sigmoid 函数,最终得到 a₁²

最后,用相同的方法计算 a₁³

组合第一层输出

现在我们已经计算出了三个标量值:a₁¹a₁²a₁³。我们需要将这三个数字组合成一个数组,作为第一层的输出

我们可以使用 NumPy 数组将它们组合起来,代码如下:

a1 = np.array([a11, a12, a13])

实现第二层计算

第一层的输出 将作为第二层的输入。现在让我们来实现第二层,以计算最终输出

的计算公式为:

a² = g(z²₁)

其中,z²₁ 的计算公式为:

z²₁ = w²₁ · a¹ + b²₁

我们将有对应的参数 w²₁b²₁。计算步骤是:

  1. 计算 z²₁ 为参数 w²₁ 的点积,再加上 b²₁
  2. 将 Sigmoid 激活函数应用于 z²₁,得到 a²₁(即最终的 )。

以上就是仅使用 Python 和 NumPy 实现正向传播的全部过程。


我们刚刚在这页代码中看到了许多表达式。在下一节视频中,我们将探讨如何简化这一过程,实现一个更通用的神经网络正向传播,而不是像刚才那样为每个神经元硬编码。

本节课中我们一起学习了如何从零开始实现单层神经网络的正向传播,包括单个神经元的计算、同层输出的组合以及向下一层的传递。理解这些基础步骤是掌握更复杂、更通用实现方式的基石。

54:12_01_02_正向传播的通用实现 🧠➡️

概述

在本节课中,我们将学习如何用Python实现神经网络的正向传播。我们将不再为每个神经元编写硬编码,而是构建一个通用的、可复用的函数来实现一个完整的神经网络层。这将帮助我们理解深度学习框架(如TensorFlow)底层的工作原理,并提升我们调试代码的能力。


从硬编码到通用实现

上一节我们介绍了如何通过为每个神经元硬编码来计算正向传播。本节中我们来看看如何编写一个通用的函数来实现一个完整的神经网络层(也称为“密集层”或“全连接层”)。

我们将定义一个名为 dense 的函数,它接收来自前一层的激活值 A_prev,以及当前层神经元的参数 WB

参数的组织方式

为了通用化,我们需要将参数组织成矩阵和向量的形式。

假设当前层(第1层)有3个神经元。每个神经元都有自己的权重向量 w 和偏置 b

  • 权重 w 被组织成一个矩阵 W。如果输入特征有2个(即 A_prev 是2维向量),那么 W 将是一个 2 x 3 的矩阵。第一列是第一个神经元的权重 w11,第二列是第二个神经元的权重 w12,以此类推。
  • 偏置 b 被组织成一个一维向量 B。例如,B = [b1, b2, b3]

dense 函数将接收前一层的激活值 A_prev、权重矩阵 W 和偏置向量 B,并输出当前层的激活值 A

dense 函数的代码实现

以下是实现 dense 函数的Python代码。我们将逐步解析它。

def dense(a_in, W, B):
    """
    实现一个神经网络层的正向传播。
    参数:
    a_in -- 来自前一层的激活值,形状为 (n_prev, )
    W -- 权重矩阵,形状为 (n_prev, n_units)
    B -- 偏置向量,形状为 (n_units, )
    返回:
    a_out -- 当前层的激活值,形状为 (n_units, )
    """
    units = W.shape[1]          # 获取当前层的神经元数量
    a_out = np.zeros(units)     # 初始化输出激活值向量

    for j in range(units):      # 遍历当前层的每个神经元
        w = W[:, j]             # 提取第j个神经元的权重列向量
        z = np.dot(w, a_in) + B[j] # 计算加权和z
        a_out[j] = g(z)         # 应用激活函数g(例如sigmoid)
    return a_out

以下是代码关键步骤的说明:

  1. 确定神经元数量units = W.shape[1] 通过权重矩阵 W 的列数获取当前层的神经元数量。
  2. 初始化输出a_out = np.zeros(units) 创建一个全零向量,用于存储即将计算的激活值。
  3. 循环计算每个神经元的激活值
    • for j in range(units): 循环遍历每一个神经元(索引 j)。
    • w = W[:, j] 从权重矩阵 W 中提取第 j 列,即第 j 个神经元的权重向量。
    • z = np.dot(w, a_in) + B[j] 计算该神经元的加权输入 z。公式为:z_j = w_j · a_in + b_j
    • a_out[j] = g(z)z 应用激活函数 g(例如sigmoid函数),得到该神经元的最终激活值 a_j
  4. 返回结果:函数返回计算得到的当前层激活值向量 a_out

构建多层神经网络

有了 dense 函数,我们就可以像搭积木一样,将多个层串联起来,实现整个神经网络的正向传播。

假设我们有一个4层神经网络(输入层不计入层数),其正向传播过程可以如下实现:

def sequential_forward_propagation(x, parameters):
    """
    实现一个多层神经网络的正向传播。
    参数:
    x -- 输入特征
    parameters -- 包含所有层W和B的字典
    返回:
    f_x -- 神经网络的最终输出
    """
    a1 = dense(x, parameters['W1'], parameters['b1'])  # 第一隐藏层
    a2 = dense(a1, parameters['W2'], parameters['b2']) # 第二隐藏层
    a3 = dense(a2, parameters['W3'], parameters['b3']) # 第三隐藏层
    f_x = dense(a3, parameters['W4'], parameters['b4']) # 输出层
    return f_x

在这个例子中:

  • x 是输入特征。
  • a1 是第一隐藏层的输出,它作为第二隐藏层的输入 a_in
  • 这个过程依次进行,直到计算出最终输出 f_x(即 a4)。

请注意,我们遵循线性代数的惯例,使用大写字母 W 表示矩阵,小写字母表示向量或标量。

理解底层原理的重要性

至此,你已经掌握了如何从零开始实现正向传播。即使在实践中你会使用强大的深度学习框架(如TensorFlow或PyTorch),理解其底层工作原理仍然至关重要。

当你的模型出现错误、运行缓慢或产生奇怪结果时,这种深入的理解能让你更有效地调试代码。机器学习代码很少能一次运行成功,因此调试能力是成为一名高效机器学习工程师的关键技能。

总结

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

  1. 通用层函数:我们构建了 dense 函数,它使用循环和矩阵操作,通用化地计算单个神经网络层的激活值。
  2. 网络构建:我们展示了如何通过顺序调用 dense 函数,将多个层连接起来,实现完整神经网络的正向传播。
  3. 底层价值:我们强调了理解算法底层实现的重要性,这不仅能加深对模型的理解,更是有效调试和解决实际问题的基石。

掌握这些基础概念,将为你在后续课程中使用更高级的框架打下坚实的基础。

55:通往通用人工智能的路径 🧠

在本节课中,我们将探讨人工智能领域一个宏大而充满未知的梦想:通用人工智能。我们将了解其与当前主流人工智能的区别,分析实现它的潜在路径与挑战,并审视一些支持其可能性的有趣科学发现。


什么是AGI?🤔

自从我青少年时期开始接触神经网络以来,我一直怀揣着一个梦想:也许有一天能构建出一个与我自身或典型人类一样智能的人工智能系统。这至今仍是我心中最鼓舞人心的人工智能梦想之一。然而,我认为通往这个目标的路径并不清晰,可能非常困难。我不知道这需要仅仅几十年并在我们有生之年看到突破,还是需要几个世纪甚至更长时间才能实现。

让我们看看这个AGI(通用人工智能)梦想是什么样子,并稍微推测一下未来可能的发展方向。这是一条不清晰且艰难的道路。


ANI与AGI:关键区别 🔍

我认为关于AGI存在很多不必要的炒作。其中一个原因可能是“人工智能”实际上包含两个截然不同的概念。

第一个是ANI,即狭义人工智能。这是一个执行单一、狭窄任务的AI系统,有时可以做得非常好,并具有巨大的价值,例如智能音箱、自动驾驶汽车、网络搜索,或应用于农业、工厂等特定领域的AI。过去几年,ANI取得了巨大进步,并在当今世界创造了巨大价值。由于ANI是AI的一个子集,ANI的快速进步在逻辑上意味着AI在过去十年也取得了巨大进步。

AI中的另一个不同概念是AGI,即通用人工智能。这是构建能够完成典型人类所能做的任何事情的AI系统的希望。尽管ANI(以及因此整个AI领域)取得了所有进步,但我不确定我们在AGI方面究竟取得了多少进展(如果有的话)。我认为ANI的所有进步让人们正确地得出结论:AI取得了巨大进步,但这导致一些人错误地认为,AI的许多进步必然意味着在AGI方面也取得了许多进步。

因此,当你被问及AI和AGI时,有时你可能会发现绘制下面这张图有助于解释AI领域的一些动态以及一些不必要的炒作来源。


模拟大脑的挑战 🧩

随着现代深度学习的兴起,我们开始模拟神经元。随着计算机(甚至GPU)速度越来越快,我们可以模拟更多的神经元。因此,多年前存在一种模糊的希望:如果我们能模拟大量神经元,也许就能模拟人脑或类似人脑的东西,从而得到真正智能的系统。遗憾的是,事实证明这并非那么简单。

我认为有两个原因。首先,我们构建的人工神经网络非常简单,一个逻辑回归单元所做的与任何生物神经元所做的完全不同,它比我们大脑中的任何神经元所做的都要简单得多。其次,即使到今天,我认为我们几乎不知道大脑是如何工作的。关于神经元究竟如何从输入映射到输出,存在许多我们至今仍不知道的基本问题。因此,试图在计算机中模拟这一点(更不用说用一个逻辑函数来模拟)距离准确模拟人脑的实际工作方式还非常遥远。

鉴于我们现在以及可预见的未来对人脑工作原理的理解非常有限,我认为仅仅试图模拟人脑作为通往AGI的路径将是一条极其困难的道路。


一线希望:大脑的可塑性实验 🐸

话虽如此,我们是否有希望在有生之年看到AGI的突破呢?让我分享一些证据,这些证据至少让我自己对这个希望保持信心。

在动物身上进行了一些引人入胜的实验,这些实验表明或强烈暗示,同一块生物脑组织可以执行范围惊人的广泛任务。这引出了“单一学习算法假说”:也许很多智能可以归因于一个或少数几个学习算法。如果我们能找出这一个或少数几个算法是什么,也许有一天我们就能在计算机中实现它。

让我分享一些这些实验的细节。

这是一个几十年前由罗·阿塔尔等人得出的结果。图中显示的是你大脑中的听觉皮层。你的大脑被连接成将来自耳朵的信号(以电脉冲的形式,取决于耳朵检测到的声音)传送到听觉皮层。事实证明,如果你重新连接动物的大脑,切断耳朵和听觉皮层之间的连接,转而将图像输入听觉皮层,那么听觉皮层就学会了“看”(听觉本指声音)。因此,这块在大多数人身上学会“听”的大脑组织,当被输入不同的数据时,它反而学会了“看”。

这是另一个例子。这部分大脑是你的体感皮层(体感指触觉处理)。如果你类似地重新连接大脑,切断触觉传感器与该大脑部分的连接,转而输入图像,那么体感皮层会学会“看”吗?已经有一系列这样的实验表明,大脑的许多不同部分,仅仅根据所给的数据,就可以学会看、感觉或听,就好像存在一个算法,根据所给的数据相应地学会处理输入。


人类感官替代的启示 👅

人们已经构建了一些系统,例如将一个摄像头(可能安装在某人额头上)映射到某人舌头上的电压网格模式。通过将灰度图像映射到舌头上的电压模式,这可以帮助视力受损的人用舌头“学会看”。

或者,有一些关于人类回声定位(或人类声纳)的迷人实验。像海豚和蝙蝠这样的动物使用声纳来“看”。研究人员发现,如果训练人类发出咔嗒声并聆听其如何从周围环境反射回来,人类有时可以学会一定程度的人类回声定位。

或者,这是一个触觉腰带。我在斯坦福的研究实验室以前也建造过类似的东西。如果你在腰间安装一圈振动器,并使用磁力罗盘进行编程,使得最北方向的振动器始终轻微振动,那么你不知何故就获得了一种方向感(一些动物有,但人类没有)。这感觉就像你走路时就知道北方在哪里,而不是感觉“哦,我腰的那个部位在振动”,而是感觉“哦,我知道北方在那里”。

还有给青蛙植入第三只眼的手术,大脑只是学会了处理这个输入。

一系列这样的实验表明,人脑具有惊人的适应性。神经科学家说它具有惊人的“可塑性”,这意味着它能适应处理令人眼花缭乱的各种感官输入。因此,问题是:如果同一块脑组织可以学会看、触摸、感觉甚至其他事情,它使用的是哪种算法?我们能否复制这个算法并在计算机中实现它?我为这些实验中使用的青蛙和其他动物感到难过,尽管我认为这些结论也相当引人入胜。


总结与展望 🚀

即使到今天,我认为研究AGI仍然是有史以来最迷人、最引人入胜的问题之一,也许有一天你会选择研究它。然而,我认为避免过度炒作很重要。我不知道大脑是否真的使用一个或少数几个算法,即使它是,我也不知道(我认为也没有人知道)这个算法是什么。但我仍然对这个希望保持信心,也许它确实存在,也许通过大量艰苦的工作,我们有一天能发现它的近似算法。

我仍然发现这是最迷人的话题之一,我仍然经常在空闲时间思考它,也许有一天你会成为为这个问题做出贡献的人。

在短期内,我认为即使不追求AGI,机器学习和神经网络也是一个非常强大的工具。即使不试图一路构建人类水平的智能,你也会发现神经网络对于你可能构建的应用程序来说是一套极其强大和有用的工具。

本周的必修视频到此结束,恭喜你学到这里!接下来的课程还会有一些可选视频,更深入地探讨神经网络的高效实现。特别是在接下来的可选视频中,我想与你分享一些关于如何实现神经网络向量化实现的细节,希望你也看看那些视频。

56:神经网络的高效向量化实现 🚀

在本节课中,我们将学习神经网络如何通过向量化和矩阵运算实现高效计算。理解这一概念对于掌握现代深度学习框架至关重要。

概述

深度学习研究者能够在过去十年中构建大规模神经网络,其中一个关键原因是神经网络可以被向量化。这意味着它们能够通过矩阵乘法高效实现。并行计算硬件(如GPU和某些CPU功能)非常擅长执行大型矩阵乘法运算。本节将探讨神经网络向量化实现的工作原理。

单层前向传播的原始实现

首先,回顾一下之前看到的单层前向传播(或正向传播)的实现代码。

以下是原始实现方式:

x = [1, 2]  # 输入
W = [[1, -3, 5],
     [2, 4, -6]]  # 第一、第二和第三个神经元的权重参数
B = [-1, 1, 2]  # 偏置参数

def dense(a_in, W, B):
    units = W.shape[1]
    a_out = np.zeros(units)
    for j in range(units):
        w = W[:, j]
        z = np.dot(w, a_in) + B[j]
        a_out[j] = g(z)  # g为激活函数,例如sigmoid
    return a_out

在这段代码中,x是输入,W是权重,B是偏置。此代码会输出三个数字(例如[1, 0, 1])。如果执行此计算,将得到类似[1, 0, 1]的结果。

向量化实现

接下来,我们可以开发此函数的向量化实现。

向量化实现如下:

x = [[1, 2]]  # 注意是双层括号,现在是一个二维数组(类似TensorFlow中的张量)
W = [[1, -3, 5],
     [2, 4, -6]]  # 与之前相同
B = [[-1, 1, 2]]  # 现在使用大写B,也是一个1x3的二维数组

def dense(A_in, W, B):
    Z = np.matmul(A_in, W) + B  # np.matmul执行矩阵乘法
    A_out = g(Z)  # g为激活函数(如sigmoid),按元素应用于矩阵Z
    return A_out

结果表明,上述for循环中的所有代码行都可以被仅仅几行代码替代,从而得到该函数的向量化实现。

具体计算步骤如下:

  1. 计算 Z = np.matmul(A_in, W) + B。这里A_inW都是矩阵,np.matmul是NumPy执行矩阵乘法的方式。
  2. 然后,A_out等于激活函数g(即sigmoid函数)按元素应用于矩阵Z
  3. 最后返回A_out

向量化实现的特点

在向量化实现中,所有量——输入X(即A_in的值)、WB,以及ZA_out——现在都是二维数组(即矩阵。

这被证明是神经网络中稠密层单步前向传播的一种非常高效的实现方式,因此被称为神经网络前向传播的向量化实现。

后续内容预告

那么,这段代码在做什么?它实际上是如何工作的?np.matmul究竟在做什么?

在接下来的两个视频中(同样为可选内容),我们将详细讲解矩阵乘法及其工作原理。如果你已经熟悉线性代数、向量、矩阵、转置和矩阵乘法,可以快速浏览这两个视频,然后直接进入本周最后一个视频。

在本周的最后一个视频(也为可选)中,我们将深入更多细节,解释np.matmul如何提供这种向量化实现。

接下来,让我们进入下一个视频,了解矩阵乘法的具体内容。

总结

本节课中,我们一起学习了神经网络前向传播的向量化实现。我们对比了使用循环的原始实现和使用矩阵运算的向量化实现,并理解了后者如何利用硬件并行计算能力大幅提升效率,这是现代深度学习能够成功并扩展到当今规模的关键思想之一。

57:矩阵乘法 🧮

在本节课中,我们将要学习矩阵乘法的基本概念和计算方法。我们将从向量的点积开始,逐步过渡到向量与矩阵的乘法,最后理解矩阵与矩阵的乘法。核心概念将通过公式代码进行描述,确保初学者能够轻松跟上。

概述 📋

矩阵乘法是线性代数中的基础运算,在机器学习中应用广泛。理解矩阵乘法有助于我们掌握后续更复杂的模型和算法。本节将从最简单的向量点积讲起,逐步构建对矩阵乘法的直观理解。

从向量点积开始

上一节我们介绍了矩阵的基本概念,本节中我们来看看如何计算两个向量的点积。点积是理解矩阵乘法的基石。

假设有两个向量:a = [1, 2]w = [3, 4]。它们的点积 z 计算如下:

公式
z = a · w = (1 * 3) + (2 * 4) = 3 + 8 = 11

更一般地,对于向量 AW,点积 Z 的计算公式为:
Z = A · W = Σ (A_i * W_i),即对应元素相乘后求和。

点积还有另一种等价的写法,即利用向量的转置。列向量 a 的转置 a^T 是一个行向量。

公式
Z = a^T * w
这里的 a^T1x2 的行向量(或矩阵),w2x1 的列向量,相乘结果 Z 是一个标量,与点积结果相同。

代码描述(Python)

import numpy as np
a = np.array([1, 2])
w = np.array([3, 4])
# 方法1: 点积
z_dot = np.dot(a, w)
# 方法2: 转置后相乘
z_transpose = np.dot(a.T, w) # 注意:对于一维数组,a.T 返回自身,此处仅为示意概念
print(z_dot, z_transpose) # 输出: 11 11

理解这两种等价的写法,对于后续理解矩阵乘法至关重要。

向量与矩阵的乘法

理解了向量的点积后,我们现在可以看看向量与矩阵的乘法。这是从一维运算到二维运算的自然延伸。

我们继续使用向量 a = [1, 2],其转置 a^T = [1, 2](视为 1x2 矩阵)。同时,我们定义一个 2x2 的矩阵 W

公式

    [3, 5]
W = [4, 6]

(更标准的写法是列优先,但为理解方便,此处用行表示列向量 [3, 4]^T[5, 6]^T

要计算 Z = a^T * W(一个 1x2 的矩阵),我们需要进行两次点积运算。

以下是计算步骤:

  1. 计算 Z 的第一个元素:取 a^TW 的第一列 [3, 4]^T 做点积。
    1*3 + 2*4 = 11
  2. 计算 Z 的第二个元素:取 a^TW 的第二列 [5, 6]^T 做点积。
    1*5 + 2*6 = 17

因此,结果 Z = [11, 17]

核心思想向量与矩阵的乘法,就是向量分别与矩阵的每一列做点积,结果构成输出向量的元素。

矩阵与矩阵的乘法

掌握了向量与矩阵的乘法后,让我们将其推广到更一般的矩阵与矩阵的乘法。这是构建复杂神经网络层计算的关键。

假设我们有一个矩阵 A,其两列分别为 [1, 2]^T[-1, -2]^T

公式

    [1,  -1]
A = [2,  -2]

以及之前用到的矩阵 W

首先,我们需要计算 A 的转置 A^T。转置操作是将矩阵的列变为行。

公式

A^T = [1,  2]
      [-1, -2]

现在,我们的目标是计算 Z = A^T * W。我们可以将 A^T 的每一行看作一个独立的行向量。

以下是计算过程:

  1. 计算 Z 的第一行:取 A^T 的第一行 [1, 2](即 a1^T)与矩阵 W 相乘。这正是上一节“向量与矩阵乘法”的例子,我们已计算出结果为 [11, 17]。这成为 Z 的第一行。
  2. 计算 Z 的第二行:取 A^T 的第二行 [-1, -2](即 a2^T)与矩阵 W 相乘。
    • 与第一列点积:(-1)*3 + (-2)*4 = -11
    • 与第二列点积:(-1)*5 + (-2)*6 = -17
      因此,第二行结果为 [-11, -17]

最终,我们得到:

公式

Z = A^T * W = [11,   17]
              [-11, -17]

核心思想矩阵与矩阵的乘法 C = A * B,可以理解为:结果矩阵 C 的第 i 行、第 j 列的元素,等于矩阵 A 的第 i 行与矩阵 B 的第 j 列的点积。

代码描述(Python)

import numpy as np
A = np.array([[1, -1],
              [2, -2]])
W = np.array([[3, 5],
              [4, 6]])
# 计算 A 的转置与 W 的乘积
Z = np.dot(A.T, W)
print(Z)
# 输出:
# [[ 11  17]
#  [-11 -17]]

总结 🎯

本节课中我们一起学习了矩阵乘法的核心知识。我们从向量的点积出发,认识到它可以表示为向量转置与另一向量的乘法。接着,我们学习了向量与矩阵的乘法,其本质是向量与矩阵每一列进行点积。最后,我们将此概念推广到矩阵与矩阵的乘法,其通用规则是:输出矩阵的每个元素,都是左矩阵的一行与右矩阵的一列的点积。

记住这个关键点:矩阵乘法是大量有序的点积运算,以逐个构建输出矩阵的元素。 虽然初学可能觉得信息量很大,但理解了这个基本模式,就能为后续学习更复杂的线性代数和机器学习算法打下坚实的基础。在接下来的视频中,我们将看到矩阵乘法的更一般化定义,这会使今天的所有内容变得更加清晰。

58:矩阵乘法规则 🧮

在本节课中,我们将学习矩阵乘法的通用规则。理解矩阵乘法是掌握神经网络向量化实现的关键一步。本节将详细拆解两个矩阵相乘的过程,并通过图示和计算示例帮助你建立清晰的概念。


矩阵乘法的通用形式

首先,我们来看一个矩阵 A,它是一个 2x3 的矩阵(两行三列)。我们可以将其三列视为三个向量:a₁a₂a₃

接下来,我们将计算 A 的转置(记为 Aᵀ)与另一个矩阵 W 的乘积。

Aᵀ 是通过将 A 的每一列“放倒”为行而得到的。因此,Aᵀ 的行分别是 a₁ᵀa₂ᵀa₃ᵀ

矩阵 W 是一个 2x4 的矩阵。我们同样可以将其四列视为四个向量:w₁w₂w₃w₄

为了清晰地展示计算过程,图中使用不同深浅的橙色标示 Aᵀ 的不同行(对应原 A 的不同列),使用不同深浅的蓝色标示 W 的不同列。


计算 Aᵀ 与 W 的乘积

我们的目标是计算矩阵 Z,其中 Z = Aᵀ WZ 将是一个 3x4 的矩阵。

W 的每一列会影响 Z 中对应列的计算结果。例如,w₁(最浅蓝色)影响 Z 的第一列,w₂ 影响第二列,依此类推。

Aᵀ 的每一行会影响 Z 中对应行的计算结果。例如,a₁ᵀ(最浅橙色)影响 Z 的第一行,a₂ᵀ 影响第二行,a₃ᵀ 影响第三行。

计算 Z 中每个元素的方法是:取 Aᵀ 的对应行与 W 的对应列,计算它们的点积(内积)。

以下是几个计算示例:

  1. 计算 Z 第一行第一列的元素(Z₁₁)

    • Aᵀ 的第一行(a₁ᵀ = [1, 2])和 W 的第一列(w₁ = [3, 4])。
    • 计算点积:1*3 + 2*4 = 3 + 8 = 11
    • 因此,Z₁₁ = 11
  2. 计算 Z 第三行第二列的元素(Z₃₂)

    • Aᵀ 的第三行(a₃ᵀ = [0.1, 0.2])和 W 的第二列(w₂ = [5, 6])。
    • 计算点积:0.1*5 + 0.2*6 = 0.5 + 1.2 = 1.7
    • 因此,Z₃₂ = 1.7
  3. 计算 Z 第二行第三列的元素(Z₂₃)

    • Aᵀ 的第二行(a₂ᵀ = [-1, -2])和 W 的第三列(w₃ = [7, 8])。
    • 计算点积:(-1)*7 + (-2)*8 = -7 - 16 = -23
    • 因此,Z₂₃ = -23

按照此规则计算所有元素后,我们得到完整的矩阵 Z


矩阵乘法的维度要求

矩阵乘法有一个重要的前提条件。在上例中,Aᵀ 是 3x2 矩阵,W 是 2x4 矩阵。

第一个矩阵的列数必须等于第二个矩阵的行数

这是因为点积运算要求两个向量的长度相同。在此例中,Aᵀ 的每一行是长度为2的向量,W 的每一列也是长度为2的向量,因此可以计算点积。

输出矩阵 Z 的维度由以下规则决定:

  • 行数等于第一个矩阵(Aᵀ)的行数。
  • 列数等于第二个矩阵(W)的列数。

因此,Z 是一个 3x4 的矩阵。这个规则可以概括为:
(m × n) 矩阵 * (n × p) 矩阵 = (m × p) 矩阵


总结与过渡

本节课我们一起学习了矩阵乘法的核心规则:通过计算第一个矩阵的行与第二个矩阵的列的点积来得到结果矩阵的每个元素,并且必须满足特定的维度匹配条件。

理解了矩阵乘法的机制后,我们就可以将其应用于神经网络的向量化实现中。在接下来的课程中,我们将看到如何利用矩阵运算一次性处理整个训练集的样本,从而极大地提升神经网络的计算效率。正如吴恩达老师所说,第一次理解并应用向量化实现时,其速度的提升令人印象深刻。

59:神经网络向量化实现 🧠💻

概述

在本节课中,我们将学习神经网络前向传播的向量化实现方法。我们将通过代码示例,详细解释如何利用矩阵乘法高效地计算神经网络的输出,并理解其背后的数学原理。


向量化实现入门

上一节我们介绍了神经网络的基本概念,本节中我们来看看如何用代码实现向量化的前向传播。

你之前已经看到,如何通过矩阵 AW 的转置相乘来计算矩阵 Z

在代码中,矩阵 A 是一个 numpy 数组,其元素与图中上方所示一致。

A 的转置(记为 A.T)将是一个行与列互换的矩阵。

numpy 中,除了直接设置,另一种计算转置的方法是使用 A.T 属性。这个转置函数将矩阵的列转换为行。

以下是初始化矩阵 W 的代码,它是一个二维的 numpy 数组。

为了计算 Z = A.T * W,你可以这样写:

Z = np.matmul(A.T, W)

这段代码将计算出上图中的矩阵 Z,并得到下方显示的结果。

有时你会在别人的代码中看到 Z = A @ W,这是调用矩阵乘法函数的另一种方式。不过,我认为使用 np.matmul 更清晰。因此,在本课程的代码中,我们使用 matmul 函数,而不是 @ 符号。


前向传播的向量化实现

现在,让我们看看前向传播的向量化实现是什么样子。

我将设 A.T 等于输入特征值 [200, 17]。这代表通常的输入特征:200度烘焙,17分钟。因此,A.T 是一个 1x2 的矩阵。

接着,我将参数 w1, w2, w3 按列堆叠,形成矩阵 W。同时,将偏置值 b1, b2, b3 放入一个 1x3 的矩阵 B 中。

事实证明,如果你计算 Z = A.T * W + B,将会得到三个数字。其计算过程是:取输入特征值,与第一列权重做点积,然后加上 b1 得到 165;与第二列权重做点积,加上 b2 得到 -531;与第三列权重做点积,加上 b3 得到 900

如果你愿意,可以暂停视频来仔细核对这些计算。这给出了 Z[1,1], Z[1,2], Z[1,3] 的值。

最后,如果函数 gsigmoid 激活函数逐个元素地应用到这三个数字上(即将 sigmoid 应用于 165, -531, 900),那么你将得到 A = g(Z)。结果基本上是 [1, 0, 1],因为 sigmoid(165) 非常接近 1,而 sigmoid(-531)sigmoid(900) 由于数值舍入也基本是 01


代码实现

让我们看看如何在代码中实现这一点。

A.T 等于 [200, 17] 这个 1x2 数组。矩阵 W2x3 矩阵,B1x3 矩阵。

因此,实现单层前向传播的方法是:

def dense(A_T, W, B):
    Z = np.matmul(A_T, W) + B
    A_out = g(Z)  # g 是激活函数,如 sigmoid
    return A_out

这段代码实现了上述计算。然后,A_out 即该层的输出,等于激活函数 g 逐元素应用于矩阵 Z 的结果,并返回 A_out,从而得到最终值。

如果你将此幻灯片与之前视频中的幻灯片进行比较,会发现一个细微的差别。按照惯例,在 TensorFlow 的实现中,我们称这个变量为 A_in 而不是 A_T,因此这也是代码的正确实现方式。

TensorFlow 中有一个惯例:单个样本实际上是以行的形式排列在矩阵 X 中,而不是在矩阵 X 的转置中。这就是为什么在 TensorFlow 中,代码实现实际上看起来是这样的。

但这解释了为什么仅用几行代码,你就可以实现神经网络的前向传播。更重要的是,由于现代计算机非常擅长高效地实现诸如 matmul 这样的矩阵乘法,因此你还能获得巨大的性能提升。


总结

本节课中我们一起学习了神经网络前向传播的向量化实现。我们通过具体的矩阵乘法示例和代码,理解了如何高效地计算神经网络的输出。你现在已经知道如何在神经网络中进行推理和前向传播,这非常酷。恭喜你!

完成测验和实验后,请在下一周回来,我们将学习如何实际训练一个神经网络。期待下周与你相见!

60:TensorFlow实现神经网络训练 🧠

在本节课中,我们将学习如何使用TensorFlow训练一个神经网络。上周我们介绍了神经网络如何进行推理(预测),本周我们将重点探讨如何利用数据训练神经网络的参数。


模型构建与编译

上一节我们介绍了神经网络推理的基本架构,本节中我们来看看如何在TensorFlow中构建并编译一个模型。

首先,我们需要指定神经网络的层结构。以下代码展示了如何构建一个用于手写数字识别(区分0和1)的神经网络:

model = Sequential([
    Dense(units=25, activation='sigmoid'),
    Dense(units=15, activation='sigmoid'),
    Dense(units=1, activation='sigmoid')
])

这段代码创建了一个顺序模型,包含:

  • 一个具有25个单元和Sigmoid激活函数的隐藏层。
  • 一个具有15个单元和Sigmoid激活函数的隐藏层。
  • 一个具有1个单元和Sigmoid激活函数的输出层。

接下来,我们需要编译模型,核心是指定所使用的损失函数。

model.compile(loss=BinaryCrossentropy())

这里我们使用了二元交叉熵损失函数。在后续视频中,我们将详细解释这个函数的具体含义。


模型训练

在定义了模型结构并指定了损失函数后,最后一步是使用数据对模型进行训练。

我们调用fit函数来启动训练过程:

model.fit(X, Y, epochs=100)

这个函数告诉TensorFlow,使用第二步中指定的损失函数,对第一步中定义的模型,在数据X和标签Y上进行训练。

参数epochs决定了梯度下降等学习算法运行的步数或轮数。


实现步骤总结

以下是使用TensorFlow训练神经网络的三个核心步骤:

  1. 指定模型:定义网络层结构,即如何从输入计算得到输出(推理过程)。
  2. 编译模型:指定训练所使用的损失函数。
  3. 训练模型:在数据上运行学习算法(如梯度下降),以最小化损失函数,从而优化模型参数。

理解代码背后的原理

虽然仅凭这几行代码就能训练模型,但理解其背后的运行机制至关重要。当学习算法初期未能按预期工作时,拥有清晰的概念框架将极大地帮助你进行调试。在接下来的视频中,我们将深入探讨TensorFlow实现中这些步骤的具体细节。


本节课中我们一起学习了使用TensorFlow训练神经网络的基本流程:构建模型、编译模型(指定损失函数)以及最终在数据上拟合模型。理解这三个步骤是掌握神经网络训练的关键。

61:训练细节详解 🧠

在本节课中,我们将深入探讨使用 TensorFlow 训练神经网络的具体步骤。我们将回顾逻辑回归的训练过程,并将其与神经网络的训练流程进行对比,帮助你理解 TensorFlow 在幕后是如何工作的。


第一步:定义模型架构 🏗️

上一节我们介绍了训练模型的三个通用步骤。本节中我们来看看如何将这些步骤应用于神经网络。第一步是定义模型如何根据输入 x 和参数 WB 计算输出。

对于逻辑回归,我们曾定义预测函数为:
f(x) = g(w·x + b)
其中 g(z) 是 sigmoid 函数:g(z) = 1 / (1 + e^{-z})

对于神经网络,我们使用 TensorFlow 代码来定义其架构。以下代码片段指定了一个具有两个隐藏层和一个输出层的神经网络:

model = Sequential([
    Dense(units=25, activation='relu'),
    Dense(units=15, activation='relu'),
    Dense(units=1, activation='sigmoid')
])

这段代码告诉 TensorFlow 网络的结构:第一层有 25 个神经元,第二层有 15 个,输出层有 1 个。它还定义了每层使用的激活函数(这里隐藏层使用 ReLU,输出层使用 sigmoid)。基于此,TensorFlow 能够推导出所有参数(W1, b1, W2, b2, W3, b3)并计算前向传播,得到输出 a3(即 f(x))。


第二步:指定损失与成本函数 📉

定义了模型如何计算输出后,下一步是指定用于评估模型性能的损失函数和成本函数。

对于逻辑回归,单个训练样本 (x, y) 的损失函数是:
L(f(x), y) = -[y * log(f(x)) + (1 - y) * log(1 - f(x))]
成本函数 J(W, b) 则是所有训练样本损失的平均值。

在 TensorFlow 中,对于二分类问题(如手写数字 0/1 识别),我们使用相同的损失函数,它被称为二元交叉熵损失。以下是编译模型并指定损失函数的代码:

model.compile(loss=tf.keras.losses.BinaryCrossentropy())

通过指定这个损失,TensorFlow 会自动将成本函数定义为所有训练样本损失的平均值,并致力于最小化这个成本 J(W, B)。这里的 WB 代表神经网络中所有层的参数集合。

如果需要解决回归问题(例如预测房价),可以使用不同的损失函数,例如均方误差损失

model.compile(loss=tf.keras.losses.MeanSquaredError())

此时,TensorFlow 将尝试最小化预测值 f(x) 与真实标签 y 之间平方误差的平均值。


第三步:使用优化算法最小化成本函数 ⚙️

指定了成本函数后,最后一步是使用优化算法来调整参数 WB,以最小化成本 J(W, B)

在逻辑回归中,我们使用梯度下降算法。对于神经网络中的每个参数 W_{lj},更新规则是:
W_{lj} = W_{lj} - α * (∂J/∂W_{lj})
其中 α 是学习率。计算这些偏导数 ∂J/∂W 的关键算法是反向传播

幸运的是,TensorFlow 为我们自动处理了反向传播的所有复杂计算。我们只需要调用 fit 函数:

model.fit(X, Y, epochs=100)

这行代码告诉 TensorFlow 使用训练数据 X 和标签 Y,运行 100 次迭代(epochs)来优化模型。TensorFlow 内部会使用比标准梯度下降更高效的优化算法(我们将在后续课程中介绍)。

随着技术的发展,成熟的库(如 TensorFlow、PyTorch)让开发者无需从零实现这些复杂算法,就像今天我们直接调用库函数来计算平方根或进行矩阵乘法一样。然而,理解其底层原理仍然至关重要,这样当出现意外情况时,你才能更好地调试和解决问题。


总结 ✨

本节课中我们一起学习了训练神经网络的三个核心步骤:

  1. 定义模型架构:使用 SequentialDense 层指定网络层数、神经元数量和激活函数。
  2. 编译模型并指定损失函数:根据问题类型(分类或回归)选择合适的损失函数(如 BinaryCrossentropyMeanSquaredError)。
  3. 训练模型:调用 model.fit 函数,TensorFlow 将自动执行反向传播和梯度下降(或其变种)来优化所有参数。

现在你已经掌握了训练一个基本神经网络(也称为多层感知机)的方法。在下一个视频中,我们将探讨如何通过使用不同的激活函数(替代 sigmoid 函数)来使神经网络变得更强大。让我们继续学习吧!

62:Sigmoid激活函数的替代方案 🧠

在本节课中,我们将学习除了Sigmoid函数之外,神经网络中其他常用的激活函数。我们将了解为什么需要替代方案,并介绍ReLU和线性激活函数,以及它们各自的适用场景。


概述

到目前为止,我们在神经网络的所有隐藏层和输出层中,一直使用Sigmoid激活函数。

我们最初这样构建神经网络,是因为我们将逻辑回归单元组合并连接在一起。然而,如果使用其他激活函数,你的神经网络可以变得更强大。让我们来看看如何实现这一点。

从Sigmoid到更灵活的激活函数

回顾上周的需求预测例子,给定价格、运输成本、营销材料,我们尝试预测产品是否高度可负担、认知度是否高、质量是否高,并基于此预测是否为畅销品。

但这个模型假设认知度是二元的,即人们要么知道,要么不知道。然而,潜在买家对你销售的T恤的认知程度可能并非二元。他们可能有点了解、比较了解、非常了解,或者产品可能已经彻底爆红。

因此,与其将认知度建模为一个二元数字(0或1),或一个0到1之间的概率值,不如让认知度可以是任何非负数,因为认知度可以从零到非常大的数值。

之前,我们使用以下公式来计算第二个隐藏单元的激活值(估计认知度):
a = g(z)
其中 g 是Sigmoid函数,因此输出值在0到1之间。

如果你希望 a 能够取更大的正值,我们可以换用不同的激活函数。

引入ReLU激活函数

事实证明,神经网络中一个非常常见的激活函数选择是这个函数,它的图像如下所示。当 z 小于0时,g(z) 为0;当 z 大于等于0时,g(z) 是一条45度的直线,即 g(z) = z

这个函数的数学公式是:
g(z) = max(0, z)

你可以自行验证,max(0, z) 的结果就是我在这里绘制的曲线。如果 ag(z),那么激活值 a 现在可以取0或任何非负值。

这个激活函数有一个名称,它被称为 ReLU(使用这种特殊的大小写)。ReLU代表“修正线性单元”。不必过于担心“修正”和“线性单元”的具体含义,这只是作者提出这个特定激活函数时赋予它的名称。在深度学习中,大多数人直接用ReLU来指代这个 g(z) 函数。

更广泛地说,你可以选择使用什么作为 g(z),有时我们会选择不同于Sigmoid的激活函数。

常用激活函数一览

以下是三种最常用的激活函数:

  1. Sigmoid激活函数
    公式为:g(z) = sigmoid(z)

  2. ReLU(修正线性单元)激活函数
    公式为:g(z) = max(0, z)

  3. 线性激活函数
    公式为:g(z) = z
    有时,当使用线性激活函数时,人们会说“我们没有使用任何激活函数”。因为如果 a = g(z)g(z) = z,那么 a 就等于 w·x + b,就好像其中根本没有 g 一样。在本课程中,我将称之为使用线性激活函数,但如果你听到别人使用“没有激活函数”这个术语,他们指的就是线性激活函数。

这三种可能是目前神经网络中最常用的激活函数。本周晚些时候,我们会提到第四种叫做Softmax的激活函数。但有了这些激活函数,你就能构建出丰富多样的强大神经网络。

如何选择激活函数?

在构建神经网络时,对于每个神经元,你是想使用Sigmoid激活函数、ReLU激活函数还是线性激活函数?你如何在不同的激活函数之间做出选择?

我们将在下一个视频中探讨这个问题。


总结

本节课中,我们一起学习了Sigmoid激活函数的局限性,并认识了两种重要的替代方案:ReLU函数和线性激活函数。我们了解了ReLU函数如何允许神经元输出更大的正值,以及线性激活函数的本质。掌握这些不同的激活函数是构建更强大、更灵活神经网络的基础。下一节,我们将深入探讨如何为神经网络的不同部分选择合适的激活函数。

63:激活函数选择指南 🧠

在本节课中,我们将学习如何为神经网络中的不同神经元选择合适的激活函数。我们将首先探讨输出层激活函数的选择,然后深入了解隐藏层激活函数的选择,最后总结一些实用的建议。

输出层激活函数的选择 📊

上一节我们介绍了激活函数的基本概念,本节中我们来看看如何为输出层选择激活函数。选择输出层激活函数时,通常取决于目标标签 y 的性质。以下是具体建议:

  • 二元分类问题:如果 y 的取值为 0 或 1,Sigmoid 激活函数几乎总是最自然的选择。因为神经网络可以学习预测 y=1 的概率,类似于逻辑回归。公式为:
    a = σ(z) = 1 / (1 + e^{-z})

  • 回归问题(y可正可负):如果你要预测一个既可为正也可为负的值(例如股价变化),推荐使用线性激活函数。这样神经网络的输出 f(x) 可以取任意实数值。公式为:
    a = g(z) = z

  • 回归问题(y仅非负):如果 y 只能取非负值(例如房屋价格),最自然的选择是 ReLU 激活函数,因为它只输出零或正值。公式为:
    a = g(z) = max(0, z)

总之,为输出层选择激活函数时,根据预测目标 y 的特性,通常有一个相当直接的选择。

隐藏层激活函数的选择 🏗️

了解了输出层的选择后,我们来看看隐藏层。在当今的神经网络实践中,ReLU(修正线性单元) 已成为隐藏层最常用的激活函数。

尽管神经网络最初常使用 Sigmoid 函数,但领域已经发展到更频繁地使用 ReLU,而很少使用 Sigmoid(除了输出层的二元分类问题)。

这主要有两个原因:

  1. 计算效率:ReLU 函数 max(0, z) 的计算比需要指数和倒数运算的 Sigmoid 函数更快。
  2. 梯度问题(更关键):Sigmoid 函数在图像的两端(z 值很大或很小时)会变得非常平坦,导致梯度很小。在梯度下降训练中,这会造成许多地方的梯度接近零,显著减慢学习速度。虽然梯度下降优化的是代价函数 J(W, b),但激活函数是计算的一部分,平坦的激活函数会导致代价函数中也出现更多平坦区域。

因此,对于大多数应用,在隐藏层默认使用 ReLU 激活函数是一个很好的选择。

总结与代码示例 📝

本节课我们一起学习了如何为神经网络选择激活函数。以下是核心建议总结:

  • 输出层:根据 y 选择。
    • 二元分类:Sigmoid
    • 回归(y可正可负):Linear
    • 回归(y仅非负):ReLU
  • 隐藏层:默认使用 ReLU

在 TensorFlow 中,你可以这样实现:

# 假设使用 Sequential 模型
model = tf.keras.Sequential([
    # 隐藏层使用 ReLU
    tf.keras.layers.Dense(units=25, activation='relu'),
    tf.keras.layers.Dense(units=15, activation='relu'),
    # 输出层:例如二元分类使用 sigmoid
    tf.keras.layers.Dense(units=1, activation='sigmoid')
    # 输出层:如需线性激活,则使用 activation='linear'
    # 输出层:如需 ReLU 激活,则使用 activation='relu'
])

通过使用这组更丰富的激活函数,你将能够构建比仅使用 Sigmoid 函数更强大的神经网络。

补充说明:在研究文献中,你有时会看到其他激活函数,如 Tanh、Leaky ReLU 或 Swish。每隔几年,研究人员可能会提出新的有趣函数,有时它们效果稍好。例如,Leaky ReLU 在某些情况下可能比标准 ReLU 表现更好。但对于绝大多数应用,本节课所学的知识已经足够。如果你有兴趣,可以自行查阅这些函数的资料。

掌握了这些关于激活函数的选择,希望你能在练习中应用这些想法。但这引出了另一个问题:我们为什么必须使用激活函数?为什么不能全部使用线性激活函数甚至不用激活函数?事实证明,这完全行不通。在下一个视频中,我们将探讨原因,并理解激活函数对于神经网络正常工作为何如此重要。

64:为什么神经网络需要激活函数? 🧠

在本节课中,我们将探讨为什么神经网络必须使用非线性激活函数。我们将通过一个简单的例子来说明,如果所有神经元都使用线性激活函数,那么无论神经网络有多少层,其效果都将等同于线性回归模型,从而失去了使用神经网络的意义。


线性激活函数的问题

上一节我们介绍了神经网络的基本结构。本节中我们来看看,如果在神经网络的所有神经元中都使用线性激活函数,会发生什么情况。

假设我们有一个用于需求预测的神经网络。如果为该网络中的所有节点都使用线性激活函数,那么这个庞大的神经网络将变得与线性回归模型没有区别。这将完全违背我们使用神经网络的初衷,因为它将无法拟合比我们在第一门课程中学到的线性回归模型更复杂的模式。

让我们通过一个更简单的例子来说明这一点。


一个简化的例子

考虑一个输入 x 仅为单个数字的神经网络。它有一个隐藏单元,参数为 W1B1,输出为 a1(也是一个数字)。第二层是输出层,同样只有一个输出单元,参数为 W2B2,输出为 A2(一个标量),也就是神经网络的最终输出 f(x)

现在,让我们看看如果在这个神经网络的每一处都使用线性激活函数 g(z) = z,会发生什么。

以下是计算过程:

  1. 计算 a1 作为 x 的函数:
    a1 = g(W1 * x + B1)
    由于 g(z) = z,所以 a1 = W1 * x + B1
  2. 计算 a2 作为 a1 的函数:
    a2 = g(W2 * a1 + B2) = W2 * a1 + B2
  3. 将第一步的 a1 表达式代入第二步:
    a2 = W2 * (W1 * x + B1) + B2
  4. 展开并简化表达式:
    a2 = (W2 * W1) * x + (W2 * B1 + B2)

如果我们令 W = W2 * W1B = W2 * B1 + B2,那么我们就证明了:
a2 = W * x + B

结论a2 仅仅是输入 x 的线性函数。因此,与其使用这个带有一个隐藏层和一个输出层的神经网络,我们不如直接使用一个线性回归模型。从线性代数的角度来看,这是因为线性函数的线性组合本身仍然是线性函数。这就是为什么在神经网络中,如果所有层都是线性的,那么多层结构并不能让网络计算或学习到任何比简单线性函数更复杂的特征。


一般情况下的结论

在更一般的情况下,如果你有一个多层神经网络,并且在所有隐藏层和输出层都使用线性激活函数,那么这个模型计算出的输出将完全等价于线性回归。

具体来说,输出 A4 可以表示为输入特征 X 的线性函数加上偏置项 B

另一种情况是,如果在所有隐藏层使用线性激活函数,但在输出层使用逻辑激活函数(Sigmoid),那么可以证明这个模型将等价于逻辑回归。此时,A4 可以表示为 1 / (1 + e^{-(Wx + B)}) 的形式(对于某些 WB 的值)。这样一来,这个庞大的神经网络并没有做到逻辑回归做不到的事情。

因此,一个常见的经验法则是:不要在神经网络的隐藏层中使用线性激活函数。事实上,通常推荐使用 ReLU 激活函数,它在大多数情况下都能很好地工作。


总结与过渡

本节课中我们一起学习了神经网络需要非线性激活函数的原因。核心在于,线性激活函数的堆叠不会增加模型的表达能力,最终整个网络会退化为一个简单的线性模型(如线性回归或逻辑回归),无法捕捉数据中的复杂非线性关系。

到目前为止,你已经学会了为二分类问题(y 为 0 或 1)和回归问题(y 取连续值)构建神经网络。在下一个视频中,我将与你分享分类问题的一个推广:当 y 不仅可以取两个值,还可以取三个、四个、十个甚至更多类别值时,如何构建用于此类多分类问题的神经网络。让我们一起来看一下。

65:多类别分类 🎯

在本节课中,我们将要学习多类别分类问题。这是一种分类问题,其输出标签不再局限于两个(例如0或1),而是可以有两个以上的可能类别。我们将了解其定义、应用场景,并预览解决此类问题的算法。


多类别分类的定义

多类别分类指的是输出标签可以有两个以上可能类别的分类问题。

例如,在我们之前看过的手写数字分类问题中,我们只尝试区分数字 01。但如果你需要识别信封上的邮政编码,实际上有 10 个可能的数字(0-9)需要识别。

或者,在本课程早期,你看到的例子是:如果你试图分类病人是否患有三种或五种不同疾病中的一种,那也是一个多类别分类问题。

另一个我经常接触的例子是工厂零件制造的视觉缺陷检测。你可能需要查看一张制药公司生产的药片图片,并判断它是否有划痕缺陷、变色缺陷或碎裂缺陷。这同样是多个类别,即你可以分类出药片可能具有的多种不同类型的缺陷。

因此,多类别分类问题仍然是一个分类问题,即 y 只能取少量离散的类别值(而不是任意数字),但现在 y 可以取两个以上的可能值。


与二元分类的对比

上一节我们介绍了多类别分类的基本概念。本节中,我们来看看它与我们熟悉的二元分类有何不同。

在之前的二元分类中,你可能有一个像下图这样的数据集,具有特征 X1X2。在这种情况下,逻辑回归会拟合一个模型来估计在给定特征 xy 等于 1 的概率,因为 y 只能是 01

对于多类别分类问题,数据集可能看起来像下面这样。我们有四个类别:圆圈代表一个类别,叉号代表另一个类别,三角形代表第三个类别,正方形代表第四个类别。

现在,我们不再仅仅估计 y 等于 1 的概率,而是需要估计:

  • y 等于 1 的概率是多少?
  • y 等于 2 的概率是多少?
  • y 等于 3 的概率是多少?
  • y 等于 4 的概率是多少?

事实证明,你在下一个视频中将学到的算法可以学习一个决策边界,可能如下图所示。这个边界将特征空间 X1X2 划分为四个区域,而不仅仅是两个。


内容总结与预告

本节课中我们一起学习了多类别分类问题的定义,并了解了它与二元分类在数据集和任务目标上的区别。

在下一个视频中,我们将学习 Softmax 回归算法。这是逻辑回归算法的一种推广,使用它你将能够处理多类别分类问题。之后,我们会将 Softmax 回归应用到新的神经网络中,这样你也能够训练神经网络来执行多类别分类任务。

让我们继续观看下一个视频。

66:Softmax 回归 🧮

在本节课中,我们将学习 Softmax 回归算法。这是一种用于多类别分类的算法,是逻辑回归的推广。我们将了解其工作原理、数学公式以及成本函数的定义。


逻辑回归回顾

上一节我们介绍了逻辑回归,它是一种用于二分类的算法。

逻辑回归适用于输出变量 y 可以取两个可能值(0 或 1)的情况。其计算过程如下:

首先计算:
z = w · x + b

然后计算:
a = g(z)

其中 g(z) 是应用于 z 的 Sigmoid 函数。我们将 a 解释为逻辑回归对给定输入特征 X 时 y 等于 1 的概率估计。

一个快速测验:如果 y 等于 1 的概率是 0.71,那么 y 等于 0 的概率是多少?答案是 0.29,因为两个概率之和必须为 1。

为了为推广到 Softmax 回归做准备,我们可以将逻辑回归视为计算两个数字:

  • a1:给定 x 时 y 等于 1 的概率。
  • a2:给定 x 时 y 等于 0 的概率,即 1 - a1

显然,a1a2 之和为 1。


Softmax 回归原理

现在,让我们将这个概念推广到 Softmax 回归。我们以一个具体的例子来说明,假设 y 可以取四个可能的输出值:1、2、3 或 4。

以下是 Softmax 回归的步骤:

首先,计算四个线性函数:

  • z1 = w1 · x + b1
  • z2 = w2 · x + b2
  • z3 = w3 · x + b3
  • z4 = w4 · x + b4

这里的 w1w2w3w4 以及 b1b2b3b4 是 Softmax 回归的参数。

接下来,应用 Softmax 函数计算每个类别的概率估计:

a1 = e^{z1} / (e^{z1} + e^{z2} + e^{z3} + e^{z4})

a2 = e^{z2} / (e^{z1} + e^{z2} + e^{z3} + e^{z4})

a3 = e^{z3} / (e^{z1} + e^{z2} + e^{z3} + e^{z4})

a4 = e^{z4} / (e^{z1} + e^{z2} + e^{z3} + e^{z4})

  • a1 被解释为模型对给定输入特征 x 时 y 等于 1 的概率估计。
  • a2 被解释为 y 等于 2 的概率估计。
  • 同理,a3a4 分别是 y 等于 3 和 4 的概率估计。

这些方程构成了 Softmax 回归模型的规范。如果能为所有参数学习到合适的选择,这个模型就能预测给定输入特征 X 时 y 为 1、2、3 或 4 的概率。

快速测验:假设你对一个新输入 X 运行 Softmax 回归,发现 a1 = 0.30a2 = 0.20a3 = 0.15。那么 a4 会是多少?因为所有概率之和必须为 1,所以 a4 = 1 - 0.30 - 0.20 - 0.15 = 0.35


通用 Softmax 回归公式

上面我们针对四个输出类别写出了公式,现在让我们写出 Softmax 回归在通用情况下的公式。

在一般情况下,y 可以取 n 个可能的值:1, 2, 3, ..., n。

Softmax 回归的计算如下:

对于每个类别 j(从 1 到 n),计算:
zj = wj · x + bj

Softmax 回归的参数是 w1, w2, ..., wnb1, b2, ..., bn

最后,计算类别 j 的概率输出 aj

aj = e^{zj} / (∑_{k=1}^{n} e^{zk})

这里使用变量 k 作为求和索引,而 j 指代一个特定的固定数字(如 j=1)。aj 被解释为模型对给定输入特征 X 时 y 等于 j 的概率估计。根据这个公式的构造,a1, a2, ..., an 这些数字相加总和始终为 1。

需要指出的是,如果将 Softmax 回归应用于 n=2(即只有两个输出类别)的情况,那么 Softmax 回归最终计算的内容基本上与逻辑回归相同(尽管参数可能略有不同)。这就是为什么说 Softmax 回归模型是逻辑回归的推广。


Softmax 回归的成本函数

定义了 Softmax 回归如何计算输出后,现在我们来看看如何为其指定成本函数。

回顾逻辑回归的成本函数。我们之前将逻辑回归的损失写为:
L = -y * log(a1) - (1 - y) * log(1 - a1)

由于 a2 = 1 - a1,我们可以将其简化为:
L = -y * log(a1) - (1 - y) * log(a2)

换句话说:

  • 如果 y = 1,损失是 -log(a1)
  • 如果 y = 0,损失是 -log(a2)

整个模型的成本函数是所有训练样本损失的平均值。

对于 Softmax 回归,通常使用的成本函数定义如下:

对于算法输出 a1an 且真实标签为 y 的情况,损失定义为:

  • 如果 y = 1,损失是 -log(a1)
  • 如果 y = 2,损失是 -log(a2)
  • ...
  • 如果 y = n,损失是 -log(an)

概括来说,如果 y = j,则损失为 -log(aj)

这个函数的作用是:-log(aj) 是一条曲线。如果 aj 非常接近 1,则损失非常小。如果 aj 只有 50% 的概率,损失会稍大一些。aj 越小,损失越大。这激励算法尽可能使 aj 变大(接近 1),因为无论 y 的实际值是什么,你都希望算法认为 y 等于该值的概率相当大。

需要注意的是,在这个损失函数中,每个训练样本的 y 只能取一个值。因此,你最终只计算一个 aj(即该特定训练样本中 y 的实际值 j 对应的那个)的 -log(aj)。例如,如果 y = 2,你最终计算的是 -log(a2),而不是 -log(a1) 或其他项。


总结

本节课中我们一起学习了 Softmax 回归。我们了解到:

  1. Softmax 回归是逻辑回归向多类别分类的推广。
  2. 其核心是通过线性函数计算得分 zj,然后使用 Softmax 函数将其转换为概率分布 aj
  3. Softmax 函数的公式确保所有输出概率之和为 1。
  4. 用于训练的成本函数是交叉熵损失,它鼓励模型为正确类别输出高概率。

这就是 Softmax 回归模型的规范及其成本函数。训练这个模型可以构建多类别分类算法。接下来,我们可以将这个 Softmax 回归模型融入神经网络,以构建性能更好的神经网络多类别分类器。

67:具有 Softmax 输出的神经网络 🧠

在本节课中,我们将学习如何构建一个能够进行多类别分类的神经网络。我们将把 Softmax 回归模型整合到神经网络的输出层中,并了解其前向传播过程以及在 TensorFlow 中的实现方式。


神经网络架构的演变

上一节我们介绍了用于二分类的神经网络。为了进行手写数字识别,我们使用了具有特定架构的网络。

现在,如果我们想对 0 到 9 这 10 个数字类别进行分类,就需要修改网络,使其拥有 10 个输出单元。

这个新的输出层将是一个 Softmax 输出层。我们有时会说这个神经网络有一个 Softmax 输出,或者说这个输出层是一个 Softmax 层。


前向传播过程

给定输入 X 后,第一层激活值 a1 的计算方式与之前完全相同。接着,第二隐藏层的激活值 a2 也以同样的方式计算。

现在,我们需要计算输出层的激活值,即 a3。以下是其工作原理。

对于 10 个类别,我们使用以下表达式计算 Z1Z10

Z1 = w1 · a2 + b1

以此类推,计算 Z1Z10

然后,a1 的计算公式如下:

a1 = e^(Z1) / (e^(Z1) + e^(Z2) + ... + e^(Z10))

这给出了 y 等于类别 1 的概率估计。

类似地,我们可以计算出 a2a10,它们分别对应 y 等于类别 2 到 10 的概率估计。

为了表述完整,如果我们想表明这些是第 3 层的量,技术上应该加上上标 3。虽然这会使符号略显复杂,但它明确了例如 Z1 是第 3 层的第一个单元的 Z 值。

这样,你的 Softmax 层就给出了对 10 个可能输出标签中任何一个的概率估计。


Softmax 激活函数的特性

我想提一下,Softmax 层有时也被称为 Softmax 激活函数。与我们目前见过的其他激活函数(如 Sigmoid、ReLU 和线性函数)相比,它在一个方面有点不同。

当我们看 Sigmoid、ReLU 或线性激活函数时,a1Z1 的函数,a2Z2 的函数,且仅依赖于 Z2。换句话说,为了获得激活值,我们可以将激活函数 g(无论是 Sigmoid、ReLU 还是其他函数)逐元素地应用于 Z1Z2 等,从而得到 a1a2a3a4

但对于 Softmax 激活函数,请注意 a1Z1Z2Z3 一直到 Z10 的函数。因此,每个激活值都依赖于 Z 的所有值。这是 Softmax 输出或 Softmax 激活函数的一个独特属性。

换句话说,如果你想计算 a1a10,它们是同时依赖于 Z1Z10 的函数。这与我们目前见过的其他激活函数不同。


在 TensorFlow 中的实现

如果你想实现本幻灯片中展示的神经网络,以下是相应的代码。

与之前类似,指定和训练模型有三个步骤。

第一步是告诉 TensorFlow 按顺序串联三个层:

  • 第一层:25 个单元,使用 ReLU 激活函数。
  • 第二层:15 个单元,使用 ReLU 激活函数。
  • 第三层:因为有 10 个输出单元,所以设置 10 个单元,并告诉 TensorFlow 使用 Softmax 激活函数。

第二步是指定损失函数。上一视频中介绍的损失函数,在 TensorFlow 中被称为 稀疏分类交叉熵 函数。

我知道这个名字有点拗口。对于逻辑回归,我们使用的是二元交叉熵函数。这里我们使用稀疏分类交叉熵函数。“稀疏分类”指的是你将 y 分类到不同的类别中(因此是“分类”),y 取值从 1 到 10。“稀疏”指的是 y 只能取这 10 个值中的一个。每张图片要么是 0,要么是 1,依此类推直到 9,你不会看到一张图片同时是数字 2 和数字 7。所以,“稀疏”指的是每个数字只属于其中一个类别。这就是为什么上一视频中的损失函数在 TensorFlow 中被称为稀疏分类交叉熵损失函数。

第三步,训练模型的代码与之前完全相同。

如果你使用这段代码,就可以在多类别分类问题上训练神经网络。


一个重要说明

如果你完全按照我在这里写的方式使用这段代码,它会工作。但请不要实际使用这段代码,因为在 TensorFlow 中有一个更好的代码版本,可以使 TensorFlow 工作得更好。所以,尽管本幻灯片中展示的代码可以工作,但不要使用我在这里写的方式。在本周后面的一个视频中,你会看到一个不同的版本,那实际上是实现此功能的推荐版本,效果会更好。我们将在后面的视频中详细查看。

所以,现在你知道了如何训练一个带有 Softmax 输出层的神经网络,但有一个注意事项:有一个不同的代码版本可以使 TensorFlow 更准确地计算这些概率。

让我们在下一个视频中看看那个版本,它也将展示我推荐你使用的、用于训练 Softmax 神经网络的实际代码。让我们继续下一个视频。


总结

本节课中,我们一起学习了如何构建用于多类别分类的 Softmax 输出神经网络。我们了解了其网络架构的变化、前向传播的数学原理,以及 Softmax 激活函数同时依赖所有输入的特性。最后,我们初步探讨了在 TensorFlow 中的实现步骤,并了解到有一个更优的实现版本将在后续课程中介绍。

68:Softmax 的改进实现 🚀

在本节课中,我们将学习如何更稳定地实现 Softmax 输出层。我们将探讨数值舍入误差的问题,并介绍一种能减少这些误差的改进实现方法。


数值舍入误差问题

上一节我们介绍了带有 Softmax 层的神经网络的基本实现。本节中,我们来看看该实现可能存在的问题以及如何改进。

计算机使用有限的内存存储数字(称为浮点数),这可能导致数值舍入误差。根据计算方式的不同,结果的精度会受到影响。

例如,计算同一个值 2/10000 有两种方式:

  • 方式一:直接计算 x = 2/10000
  • 方式二:计算 x = (1 + 1/10000) - (1 - 1/10000)

理论上两者结果相同,但在计算机中,第二种方式可能因中间步骤的舍入而产生微小误差。

# 方式一:直接计算
x = 2 / 10000
print(f"{x:.20f}")  # 输出:0.00020000000000000001

# 方式二:间接计算
x = (1 + 1/10000) - (1 - 1/10000)
print(f"{x:.20f}")  # 输出:0.00019999999999997797

虽然我们之前计算 Softmax 损失函数的方式在数学上是正确的,但存在一种不同的公式化方法,可以减少这类数值舍入误差,从而在 TensorFlow 中进行更精确的计算。


从逻辑回归理解改进思路

为了更好地理解这个改进思路,我们先回顾逻辑回归,然后再将其应用到 Softmax。

在逻辑回归中,对于一个样本,我们首先计算输出激活值 a
公式a = g(z) = 1 / (1 + e^{-z})

然后使用二元交叉熵公式计算损失:
公式loss = -[y * log(a) + (1-y) * log(1-a)]

以下是实现此计算的两步代码:

# 原始实现(两步计算)
a = tf.keras.activations.sigmoid(z)
loss = tf.keras.losses.BinaryCrossentropy()(y_true, a)

这种实现通常可行,数值误差并不严重。但如果我们允许 TensorFlow 不将 a 作为中间项显式计算,而是直接指定损失为 z 的函数,TensorFlow 就可以重新排列计算项,找到数值更稳定的计算方式。

改进后的实现将激活函数和损失计算合并:

# 改进实现(合并计算,数值更稳定)
loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)(y_true, z)

参数 from_logits=True 告诉 TensorFlow:输入的 z 是逻辑值(logits),尚未经过 Sigmoid 激活。TensorFlow 会将 a = 1/(1+e^{-z}) 的表达式代入损失公式,并优化计算过程以减少舍入误差。这种实现的缺点是代码可读性略有下降。


应用于 Softmax 回归

现在,让我们将这一思路应用到 Softmax 回归中。

回顾上一节的实现,我们首先计算 Softmax 激活值 a_j
公式a_j = e^{z_j} / (sum_{k=1}^{10} e^{z_k})

然后根据真实标签 y 计算损失:
公式loss = -log(a_y)

原始实现代码如下:

# 原始实现:显式计算 Softmax 激活值
outputs = tf.keras.layers.Dense(10, activation='softmax')(previous_layer)
loss = tf.keras.losses.SparseCategoricalCrossentropy()(y_true, outputs)

同样,我们可以通过合并计算来改进。我们不再让输出层计算 Softmax 概率,而是让它直接输出逻辑值 z1z10。然后,在损失函数中指定 from_logits=True

以下是改进后的实现:

# 改进实现:输出层为线性,损失函数合并计算
outputs = tf.keras.layers.Dense(10, activation='linear')(previous_layer) # 输出 logits (z)
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)(y_true, outputs)

这样做的原理是:当某些 z_j 的值非常大或非常小时,e^{z_j} 可能产生极大或极小的数值,容易导致计算不稳定(上溢或下溢)。通过将 Softmax 计算整合进损失函数,TensorFlow 可以在内部重新排列数学项,避免直接计算这些极值,从而得到更精确的损失值。


改进实现的注意事项

采用这种改进实现后,需要注意一个细节:神经网络的最后一层现在输出的是逻辑值 z1z10,而不是概率 a1a10

  • 在训练时:这没有问题,因为损失函数会处理 z
  • 在推理时(进行预测时):如果你需要得到具体的类别概率,则需要手动将输出 z 通过 Softmax 函数进行转换。
# 推理时,如果需要概率,需手动应用 Softmax
logits = model.predict(input_data)
probabilities = tf.nn.softmax(logits).numpy()
predicted_class = np.argmax(probabilities)

对于逻辑回归,如果采用了类似的合并实现,在需要得到最终概率时,同样需要将输出 z 通过 Sigmoid 函数进行转换。


总结

本节课中我们一起学习了 Softmax 层的改进实现方法。

  1. 问题根源:直接分步计算 Softmax 激活值和交叉熵损失可能因中间值的极端大小(e^{z})引入数值舍入误差。
  2. 解决方案:将输出层改为线性激活,直接输出逻辑值(logits),并在损失函数中使用 SparseCategoricalCrossentropy(from_logits=True)。这允许 TensorFlow 优化计算图,实现数值更稳定的损失计算。
  3. 核心代码:使用 Dense(10, activation='linear')SparseCategoricalCrossentropy(from_logits=True) 进行组合。
  4. 注意事项:改进后的模型在推理时,若需概率输出,需对 logits 额外应用 tf.nn.softmax

这种实现方式虽然牺牲了一点代码的直观性,但换来了更好的数值稳定性,是实践中更推荐的做法。

69:多输出分类(可选)🚗🚌🚶

在本节课中,我们将学习一种与多类别分类不同的分类问题——多标签分类。我们将了解其定义、应用场景,并学习如何构建神经网络来解决此类问题。


多标签分类的定义

你已经学习了多类别分类,其输出标签 Y 可以是两个或更多个可能类别中的任意一个。多标签分类是一种不同类型的分类问题,其特点是每个输入图像可以关联多个标签

让我用一个例子来说明。如果你正在构建一辆自动驾驶汽车或驾驶辅助系统,那么给定一张汽车前方的图片,你可能需要询问一系列问题:图片中是否有汽车?是否有公交车?是否有行人?

  • 在第一张图片中:有汽车,没有公交车,至少有一个行人。
  • 在第二张图片中:没有汽车,没有公交车,有行人。
  • 在第三张图片中:有汽车,有公交车,没有行人。

这些都是多标签分类问题的例子,因为单个输入图像 X 关联了三个不同的标签

这些标签分别对应图像中是否存在汽车、公交车或行人。因此,在这种情况下,目标值 Y 实际上是一个由三个数字组成的向量。

这与多类别分类不同。例如在手写数字识别中,Y 只是一个单一的数字,即使这个数字可以取10个不同的可能值。


如何构建多标签分类神经网络

上一节我们介绍了多标签分类的概念,本节中我们来看看如何构建神经网络来解决它。

一种方法是将其视为三个完全独立的机器学习问题。你可以构建一个神经网络来判断是否有汽车,第二个网络检测公交车,第三个网络检测行人。这种方法并非不合理。

但还有另一种方法,即训练一个单一的神经网络来同时检测汽车、公交车和行人

以下是这种方法的神经网络架构:

  1. 输入 X
  2. 第一个隐藏层输出 a1
  3. 第二个隐藏层输出 a2
  4. 最后的输出层将有三个输出神经元,输出 a3,这是一个包含三个数字的向量。

因为我们解决的是三个二元分类问题(是否有汽车?是否有公交车?是否有行人?),所以可以在输出层的这三个节点上使用 Sigmoid 激活函数。

因此,这里的 a3 将是 [a31, a32, a33],分别对应学习算法认为图像中是否存在汽车、公交车和行人。


多类别 vs. 多标签分类

多类别分类和多标签分类有时会被混淆。这就是为什么在本视频中,我想与你分享多标签分类问题的定义,以便你能根据具体应用场景,选择适合任务的正确方法。

我发现多类别分类和多标签分类有时会被混淆,这就是为什么我特意在本视频中向你明确什么是多标签分类,以便你能根据应用需求,选择正确的方法来完成工作。关于多类别和多标签分类的部分到此结束。


总结与预告

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

  1. 多标签分类的定义:一个输入可以对应多个输出标签。
  2. 其应用场景,如自动驾驶中的物体检测。
  3. 构建神经网络的两种思路:独立网络与单一多输出网络。
  4. 多输出网络的架构和 Sigmoid 激活函数的使用。
  5. 明确了多标签分类与多类别分类的核心区别。

在下一个视频中,我们将开始学习一些更高级的神经网络概念,包括一种比梯度下降更优的优化算法。让我们在下一个视频中看看这个算法,因为它将帮助你的学习算法学习得更快。让我们继续下一个视频。

70:高级优化算法 🚀

在本节课中,我们将学习一种比梯度下降更高效的优化算法——Adam算法。我们将了解它的工作原理、优势以及如何在代码中实现它。


梯度下降回顾

上一节我们介绍了梯度下降算法,它是机器学习中广泛使用的优化方法,是线性回归、逻辑回归以及早期神经网络实现的基础。

梯度下降的每一步更新参数 Wj 的公式如下:

Wj := Wj - α * ∂J/∂Wj

其中,α 是学习率,∂J/∂Wj 是成本函数 J 对参数 Wj 的偏导数。


梯度下降的局限性

本节中我们来看看梯度下降在实际应用中可能遇到的问题。

下图展示了一个成本函数的等高线图,其最小值位于椭圆中心。

如果学习率 α 设置得太小,梯度下降的每一步都会朝着大致相同的方向移动,但步长非常小。这会导致收敛速度缓慢。你可能会想,我们能否自动增大学习率,让算法迈出更大的步伐,从而更快地到达最小值?

相反,如果学习率 α 设置得太大,梯度下降的步骤可能会在最小值附近来回振荡,无法稳定收敛。这时,你又会希望算法能自动减小学习率。


Adam 算法介绍

为了解决上述问题,Adam 算法应运而生。Adam 代表自适应矩估计,它能根据每个参数的情况自动调整学习率。

Adam 算法的核心思想是:

  • 如果一个参数(如 WjB)持续朝着大致相同的方向更新,则增大该参数的学习率,使其在该方向上更快前进。
  • 如果一个参数持续来回振荡,则减小该参数的学习率,以稳定其更新路径。

与使用单一全局学习率的梯度下降不同,Adam 为模型中的每一个参数都维护一个独立的学习率。例如,如果你的模型有参数 W1W10 以及 B,那么 Adam 实际上会管理 11 个学习率参数:α1α10 对应 W1W10α11 对应 B

Adam 算法如何实现这一点的具体细节较为复杂,超出了本课程的范围。在后续更高级的深度学习课程中,你可能会学到更多。


Adam 算法的代码实现

以下是 Adam 算法在代码中的实现方式。模型定义与之前完全相同。

编译模型的方式也与之前非常相似,区别在于我们需要在 compile 函数中添加一个额外的参数,指定要使用的优化器为 Adam。

# 示例代码
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),
              loss='...',
              metrics=['accuracy'])

Adam 优化算法需要一个默认的初始学习率 α,在这个例子中我将其设置为 1e-3。在实践中使用 Adam 算法时,值得尝试几个不同的初始学习率值,包括较大和较小的值,以观察哪个能带来最快的训练性能。

与你在先前课程中学到的原始梯度下降算法相比,Adam 算法由于能自动适应学习率,因此对你所选学习率的具体值不那么敏感,鲁棒性更强。不过,稍微调整这个参数以尝试获得更快的训练速度仍然是值得的。


总结与对比

本节课中我们一起学习了 Adam 优化算法。

Adam 算法通常比梯度下降工作得更快,并且已经成为从业者训练神经网络时实际上的标准选择。如果你在决定使用哪种学习算法来训练神经网络,一个稳妥的选择就是使用 Adam 优化算法。如今,大多数从业者都会使用 Adam 而非原始的梯度下降算法。

掌握了这个方法,希望你的学习算法能够更快地进行学习。

在接下来的几个视频中,我将介绍一些更高级的神经网络概念。特别是在下一个视频中,让我们来看看一些其他类型的网络层。

71:其他层类型 🧠

概述

在本节课中,我们将学习神经网络中除密集层(Dense Layer)之外的其他层类型。我们将重点介绍卷积层(Convolutional Layer),了解其工作原理、优势以及适用场景。通过本课,你将初步理解神经网络架构的多样性。


密集层回顾

到目前为止,我们使用的所有神经网络层都是密集层类型。在这种层中,层中的每个神经元都能访问前一层所有神经元的激活值

事实证明,仅使用密集层类型,你实际上可以构建一些相当强大的学习算法。

为了帮助你进一步建立关于神经网络能力的直觉,实际上还存在一些具有其他属性的层类型。在本视频中,我将简要介绍这一点,并为你展示一个不同类型的神经网络层示例。


卷积层简介

在密集层中,例如第二隐藏层中某个神经元的激活值,是前一层所有激活值(a1)的函数。但对于某些应用,神经网络的设计者可能会选择使用不同类型的层。

你可能会在一些工作中看到的另一种层类型称为卷积层

让我用一个例子来说明。左边显示的是输入 X,它是一个手写数字 9。我将构建一个隐藏层,该层将根据这个输入图像 X 计算不同的激活值。

但我可以这样做:对于第一个隐藏单元(用蓝色绘制),我不让这个神经元查看图像中的所有像素,而是规定它只能查看这个小矩形区域内的像素。第二个神经元(用洋红色表示)同样不会查看整个输入图像,而是只查看图像中有限区域的像素。第三个、第四个神经元依此类推,直到最后一个神经元,它可能只查看图像的那个区域。

那么,为什么要这样做呢?为什么不让每个神经元都查看所有像素,而只让它们查看部分像素?

这样做的好处是:首先,它加快了计算速度。其次,使用这种称为卷积层的数字类型层的神经网络,可以需要更少的训练数据,或者也可以更不容易过拟合。我在之前的课程中简要提到过过拟合,下周当我们讨论使用学习算法的实用技巧时,也会更详细地探讨这个问题。

这种每个神经元只查看输入图像一个区域的优选层类型,就称为卷积层。研究人员 Yann LeCun 在如何使卷积层工作并推广其使用方面,贡献了许多细节。


卷积层详细示例

让我更详细地说明卷积层。如果一个神经网络中有多个卷积层,有时它被称为卷积神经网络

为了在本幻灯片上说明卷积层或卷积神经网络,我将不使用二维图像输入,而是使用一维输入。我将使用的激励示例是心电图信号(EKG 或 ECG)的分类。如果你在胸部放置两个电极,你会记录到类似这样的电压,对应你的心跳。这实际上是我的斯坦福研究小组曾研究过的内容,我们读取类似这样的心电图信号,试图诊断患者是否可能有心脏问题。

心电图信号(在某些地方称为 ECG,某些地方称为 EKG)只是一系列数字,对应这条曲线在不同时间点的高度。因此,你可能有大约 100 个数字,对应这条曲线在 100 个不同时间点的高度。

学习任务是:给定这个时间序列,给定这个心电图信号,进行分类,例如判断该患者是否患有心脏病或某种可诊断的心脏状况。

以下是卷积神经网络可能做的事情:

我将把心电图信号旋转 90 度放在侧面,这样我们就有了 100 个输入 x1, x2, ..., x100。

当我构建第一个隐藏层时,我不让第一个隐藏单元接收所有 100 个数字作为输入,而是让它只查看 x1 到 x20。这对应于只查看这个心电图信号的一个小窗口。

这里用不同颜色显示的第二个隐藏单元将查看 x11 到 x30,因此它查看这个心电图信号的另一个窗口。第三个隐藏单元查看另一个窗口 x21 到 x40,依此类推。在这个例子中,最后一个隐藏单元将查看 x81 到 x100,因此它查看这个心电图时间序列末尾的一个小窗口。

这就是一个卷积层,因为该层中的每个单元只查看输入的一个有限窗口。

现在,神经网络的这一层有 9 个单元。

下一层也可以是卷积层。在第二个隐藏层中,我设计我的第一个单元不查看前一层所有的激活值,而是只查看前五个激活值。然后,这个第二隐藏层中的第二个单元可能只查看另外五个数字,例如 a3 到 a7。该层中的第三个也是最后一个隐藏单元将只查看 a5 到 a9。

然后,也许最终,这些激活值 a2 被输入到一个 Sigmoid 单元,该单元确实会查看 a2 的这三个值,以便对是否存在心脏病进行二元分类。

这就是一个神经网络的例子,其第一个隐藏层是卷积层,第二个隐藏层也是卷积层,输出层是 Sigmoid 层。

事实证明,对于卷积层,你有很多架构选择,例如单个神经元应该查看多大的输入窗口,以及每层应该有多少个神经元。通过有效地选择这些架构参数,你可以为某些应用构建比密集层更有效的神经网络新版本。


总结与展望

回顾一下,这就是关于卷积层和卷积神经网络的内容。在本课程中,我不会更深入地探讨卷积网络,你也不需要了解它们就能完成作业并成功完成本课程。但我希望你会发现这个额外的直觉——神经网络也可以有其他类型的层——是有用的。

事实上,如果你有时听说最新的尖端架构,如 Transformer 模型、LSTM 或注意力模型,很多关于神经网络的研究,即使在今天,也涉及研究人员试图为神经网络发明新类型的层,并将这些不同类型的层作为构建块连接起来,以形成更复杂、更强大的神经网络。

这就是本周所需视频的全部内容。感谢你一直坚持学习到这里。我期待下周也能见到你,届时我们将开始讨论关于如何构建机器学习系统的实用建议。我希望你下周学到的技巧能帮助你更有效地构建有用的机器学习系统。我也期待下周见到你。😊

72:什么是导数(可选)📚

在本节课中,我们将学习导数的基本概念。导数在神经网络的反向传播算法中至关重要,它帮助我们理解当参数发生微小变化时,成本函数如何变化。我们将通过简单的例子和代码演示,让初学者也能轻松理解导数的含义和计算方法。


概述

你已经看到在 TensorFlow 中,可以指定神经网络架构来计算输出 y 作为输入 x 的函数,并指定成本函数。TensorFlow 会自动使用反向传播计算导数,并使用梯度下降或 Adam 等算法训练神经网络的参数。

反向传播算法计算成本函数关于参数的导数,是神经网络学习中的关键算法。在本节和接下来的几个可选视频中,我们将探讨反向传播如何计算导数。这些视频完全是可选的,会涉及一些微积分知识。如果你已经熟悉微积分,希望你能享受这些视频;如果不熟悉,我们将从微积分的基础开始,确保你拥有理解反向传播工作原理所需的所有直觉。


导数的直观理解

我将使用一个简化的成本函数:J(W) = W²。成本函数是参数 WB 的函数。在这个简化的例子中,我们假设 J(W) = W²,并暂时忽略 B。假设参数 W 的值等于 3。

那么 J(W) 将等于 9,因为 。如果我们把 W 增加一个微小的量,比如 ε = 0.001J(W) 的值会如何变化?

如果 W 增加 0.001,那么 W 变为 3.001,所以 J(W) 现在是 3.001² = 9.006001。我们看到,如果 W 增加 0.001,J(W) 大约增加了 6 倍那么多,即大约增加 6 * 0.001。这并不完全精确,实际上增加到 9.006001 而不是 9.006,但如果 ε 是无穷小(即非常非常小),这个比例会越来越精确。

在这个例子中,我们看到如果 W 增加 ε,那么 J 大约增加 6 * ε。在微积分中,我们会说 J(W) 关于 W 的导数等于 6。这意味着如果 W 增加一个微小的量,J(W) 会增加六倍那么多。

如果 ε 取不同的值,比如 ε = 0.002,那么 W 变为 3.002 变为 3.002² = 9.012004。在这种情况下,我们会得出结论:如果 W 增加 0.002,J(W) 大约增加 6 * 0.002,即大约增加到 9.012。这个 0.012 大约是 6 * 0.002。同样,由于 ε 不是无穷小,这里有一点误差(额外的 0.000004)。我们再次看到 W 的增加量与 J(W) 的增加量之间的比例是 6:1,这就是为什么 J(W) 关于 W 的导数等于 6。ε 越小,这个比例就越精确。

你可以暂停视频,用其他 ε 值自己尝试这个计算。关键是只要 ε 足够小,J(W) 的增加量与 W 的增加量之比应该是 6:1。

这引出了导数的非正式定义:如果每当 W 增加一个微小量 ε,导致 J(W) 增加 K * ε(在我们刚才的例子中,K 等于 6),那么我们说 J(W) 关于 W 的导数等于 K

你可能记得在实现梯度下降时,会反复使用以下规则更新参数 W

W := W - α * (dJ/dW)

其中 α 是学习率。梯度下降做了什么?注意,如果导数很小,那么这个更新步骤会对参数 W 做小的更新;如果这个导数项很大,则会导致参数 W 发生大的变化。这是合理的,因为如果导数小,意味着改变 WJ 的值影响不大,所以我们不必对 W 做大的改变。但如果导数大,意味着即使对 W 做微小的改变,也能显著改变或减少成本函数 J(W),所以在这种情况下,让我们对 W 做更大的改变,因为这样做实际上能显著减少成本函数 J


更多导数示例

我们刚才看到的例子是,如果 W = 3J(W) = W² = 9,那么如果 W 增加 ε = 0.001J(W) 变为 J(3.001) = 9.006001。换句话说,J 增加了大约 0.006,即 6 * 0.0016 * ε,这就是为什么 J(W) 关于 W 的导数等于 6。

让我们看看对于所有 W 值,导数会是什么。取 W = 2。在这种情况下,J(W) = W² = 4,如果 W 增加 0.001,那么 J(W) 变为 J(2.001) = 4.004001。所以 J(W) 从 4 增加到这个值,大约增加了 4 * ε,这就是为什么现在的导数是 4,因为 W 增加 ε 导致 J(W) 增加了四倍那么多。同样,这里额外的 0.000001 是因为不够精确,因为 ε 不是无穷小。

再看另一个例子,如果 W = -3J(W) = W² 仍然是 9,因为 (-3)² = 9。如果 W 再次增加 ε,那么现在 W = -2.999(-2.999)² = 8.994001。注意,这里 J(W) 下降了大约 0.006,即 6 * ε。所以在这个例子中,J 从 9 开始,但现在下降了(注意这里是向下箭头而不是向上箭头),下降了 6 * ε,或者等价地说,它增加了 -6 * ε,这就是为什么在这种情况下导数等于 -6,因为当 ε 很小时,W 增加 ε 导致 J(W) 增加 -6 * ε

可视化这一点的一种方法是绘制函数 J(W)。如果横轴是 W,纵轴是 J(W),那么当 W = 3 时,J(W) = 9;当 W = -3 时,也是 9;当 W = 2 时,J(W) = 4

如果你以前上过微积分课,可能会注意到导数对应于一条直线在 J(W) 函数某点(比如 W = 3)处的斜率。这条直线在该点的斜率(高度除以宽度)在 W = 3 时等于 6,在 W = 2 时等于 4,在 W = -3 时等于 -6。在微积分中,这些直线的斜率对应于函数的导数。但如果你以前没有上过微积分课,没有见过斜率的概念,不用担心。

在继续之前,做一个观察:在这三个例子中,J(W) 是同一个函数,J(W) = W²,但 J(W) 的导数取决于 W。当 W = 3 时,导数是 6;当 W = 2 时,导数是 4;当 W = -3 时,导数是 -6。如果你熟悉微积分(如果不熟悉也没关系),微积分可以让我们计算出 J(W) 关于 W 的导数为 2 * W。稍后,我将展示如何使用一个名为 SymPy 的 Python 包自己计算这些导数。

因为微积分告诉我们 的导数是 2W,所以当 W = 3 时,导数是 2 * 3 = 6;当 W = 2 时,是 2 * 2 = 4;当 W = -3 时,是 2 * (-3) = -6W 的值乘以 2 就得到导数。


使用 SymPy 计算导数

在结束之前,让我们再看几个例子。对于这些例子,我将设 W = 2。你在上一张幻灯片中看到,如果 J(W) = W²,那么导数我说是 2 * W,即 4。所以如果 W 增加 0.001(即 ε),J(W) 变为大约增加 4 * ε

让我们看看其他几个函数。如果 J(W) = W³,那么 2³ = 8。如果 J(W) = W,那么 W = 2。如果 J(W) = 1/W,那么 1/2 = 0.5。当成本函数 J(W)W1/W 时,J(W) 关于 W 的导数是什么?

让我展示如何使用名为 SymPy 的库和包来计算这些导数。

首先,导入 SymPy。我将告诉 SymPy 使用 JW 作为符号来计算导数。

对于第一个例子,成本函数 J = W²。注意 SymPy 如何以漂亮的字体渲染它。如果使用 SymPy 计算 J 关于 W 的导数,可以这样做。你会看到 SymPy 告诉你这个导数是 2W

让我选择一个变量 dJ_dW 并将其设置为等于这个表达式,然后在这里打印出来。所以是 2W。如果你想将 W 的值代入这个表达式求值,可以这样做:derivative.subs(W, 2),这意味着将 W 的值代入这个表达式并求值,得到 4,这就是为什么当 W 接近 2 时,我们看到 J 的导数等于 4。

让我们看其他一些例子。如果 J = W³ 呢?那么导数变为 3 * W²。所以根据微积分,这是 SymPy 为我们计算的。如果 J,那么 J 关于 W 的导数是 3W²,并且根据 W 是什么,导数的值也会改变。我们可以代入:如果 W = 2,在这种情况下得到 12。

或者如果 J = W 呢?在这种情况下,导数就等于 1。

我们最后一个例子是,如果 J = 1/W 呢?在这种情况下,导数结果是 -1 / W²,所以这是 -1/4

我将取出我们计算出的导数:对于 ,是 2W;对于 ,是 3W²;对于 W,就是 1;对于 1/W,是 -1 / W²。让我们把这些复制回另一张幻灯片。

所以 SymPy(实际上是微积分)展示的是:如果 J(W),导数是 3W²,当 W = 2 时等于 12;当 J(W) = W 时,导数就等于 1;当 J(W) = 1/W 时,导数是 -1 / W²,当 W = 2 时是 -1/4。这双重检查了我们从 SymPy 得到的这些表达式是正确的。

让我们尝试将 W 增加 ε。在这种情况下,J(W)(同样,你可以暂停视频,如果愿意,在自己的计算器上检查这个映射)是 2.001³ 变成这个值,所以 J 从 8 增加到大约 8.012,因此大约增加了 12 * ε,所以导数确实是 12。

或者如果 J(W) = W,那么如果 W 增加 ε,那么 J(W)(就是 W)现在是 2.001,所以它增加了 0.001,这正好是 ε 的值。所以 J(W) 增加了 1 * ε,因此导数确实等于 1。注意,这里实际上是精确的 ε,即使 ε 不是无穷小。

最后一个例子:如果 J(W) = 1/W,如果 W 增加 ε,那么 W1/2.001,结果 J(W) 大约是 0.49975(截断了一些额外数字)。但这结果是 0.5 - 0.00025。所以 J(W) 从 0.5 开始,下降了 0.00025。这个 0.00025 是 0.25 * ε,它下降了这么多,或者说增加了 -0.25 * ε,因为 -0.25 * ε 等于这里的这个项。

所以我们看到,如果 W 增加 εJ(W) 增加 -1/4-0.25 * ε,这就是为什么在这种情况下导数是 -1/4


导数符号说明

在结束这个视频之前,我想简要提一下在其他文本中可能看到的用于书写导数的符号。如果 J(W) 是单变量(比如 W)的函数,那么数学家有时会将导数写为 d/dW [J(W)]。注意,在 SymPy 中,这个符号使用小写字母 d

相比之下,如果 J 是多变量的函数,那么数学家有时会使用这个花体的 来表示关于其中一个参数 W_i 的导数。在我看来,区分这个常规的 d 和这个风格化的微积分导数符号 的符号,对我来说没有多大意义,而且在我看来,这种符号使微积分和导数符号变得复杂。

但由于历史原因,微积分文本会根据 J 是单变量函数还是多变量函数使用这两种不同的符号。但我认为,出于实际目的,这种符号约定往往只会使事情复杂化,我认为这实际上是不必要的。

所以对于本课程,我将到处使用 符号,即使只有一个变量。事实上,对于我们的大多数应用,函数 J 是多变量的,所以这个有时称为偏导数符号的符号,实际上是大多数情况下正确的符号,因为 J 通常有多个变量。但我希望在本课程中始终使用这种符号,以简化演示,使导数更容易理解。

事实上,这个符号是你在迄今为止的视频中一直看到的符号。为了简洁,有时你也会看到简写为 ∂J/∂W_i 或写成这样,这些只是这里这个表达式的简化缩写形式。


总结

本节课中,我们一起学习了导数的基本概念。导数描述了当参数 W 发生微小变化 ε 时,成本函数 J(W) 的变化量。如果 J(W) 的变化量是 K * ε,那么 K 就是 J(W) 关于 W 的导数。我们通过具体例子和 SymPy 代码演示了如何计算不同函数的导数,并简要介绍了导数的符号表示。理解导数是理解神经网络反向传播算法的基础。在下一节中,我们将学习如何通过计算图在神经网络中计算导数。

73:计算图(可选)📊

在本节课中,我们将要学习深度学习中的一个核心概念——计算图。计算图是理解神经网络如何进行计算,以及像TensorFlow这样的编程框架如何自动计算神经网络导数的关键工具。我们将通过一个简单的线性回归示例,一步步拆解计算图的构建过程,并学习如何通过它进行前向传播和反向传播。

概述:什么是计算图?🤔

计算图是深度学习中的一个关键思想。它也是像TensorFlow这样的编程框架自动计算神经网络导数的方式。让我们看看它是如何工作的。

我将通过一个小型神经网络示例来说明计算图的概念。

构建一个简单的计算图 🧱

这个神经网络只有一层,也就是输出层,并且输出层只有一个单元。

它接收输入 X,应用一个线性激活函数,并输出激活值 A。更具体地说,输出是 A = WX + B。所以这基本上是一个线性回归,但表示为一个具有一个输出单元的神经网络。

给定输出后,成本函数是 J = 1/2 * (A - Y)^2。对于这个小例子,我们只使用一个训练样本:输入 X = -2,真实输出值 Y = 2。这个网络的参数是 W = 2B = 8

接下来,我将展示如何使用计算图逐步计算成本函数 J。需要记住的是,在学习时,我们喜欢将成本函数 J 视为参数 WB 的函数。让我们将 J 的计算分解为单独的步骤。

以下是构建计算图的步骤:

  1. 计算 C = W * X:参数 W 是成本函数 J 的一个输入。我们首先需要计算 W 乘以 X。我们称这个结果为 CW 等于 2,X 等于 -2,所以 C 等于 -4。我们将在箭头上方写出这个值,以显示这条线上输出的值。

  1. 计算 A = C + B:下一步是计算 A,即 WX + B。这需要输入另一个参数 BA 等于 C 加上 B。将它们相加,结果是 4。

  1. 计算 D = A - Y:接下来计算 A 减去 Y,我们称之为 DY 等于 2,所以 4 减 2 等于 2。

  1. 计算 J = 1/2 * D^2:最后,J 是成本,即 1/2 * (A - Y)^21/2 * D^2。这正好等于 2。

我们刚刚构建了一个计算图。这里的“图”不是指带有 x 轴和 y 轴的图表,而是计算机科学意义上的图,即一组由边(或本例中的箭头)连接的节点。

这个计算图展示了我们如何计算神经网络输出 A前向传播步骤,并且还进一步计算了成本函数 J 的值。

现在的问题是,我们如何找到 J 相对于参数 WB 的导数?让我们接下来看看。

通过计算图进行反向传播 🔄

上一节我们介绍了如何构建计算图并进行前向传播。本节中,我们来看看如何利用这个图进行反向传播来计算导数。

这是上一张幻灯片的计算图,我们已经完成了前向传播,从左到右计算得出成本函数 J 等于 2。我们现在想做的是计算 J 相对于 W 的导数和 J 相对于 B 的导数。事实证明,前向传播是从左到右的计算,而计算导数将是一个从右到左的计算,这就是为什么它被称为反向传播——从右向左进行。

这个图的最终计算节点是这里的这个,它计算 J = 1/2 * D^2。反向传播的第一步是问:如果 D(这个节点的输入)的值改变一点点,J 的值会改变多少?

具体来说,我们会问如果 D 增加一点点,比如 0.001(在这种情况下是 ε 值),J 的值会如何变化?在这种情况下,如果 D 从 2 变为 2.001,那么 J 从 2 变为 2.002。所以如果 D 增加 ε,J 大约增加 2 倍的 ε。因此我们得出结论,J 相对于这个最终节点的输入值 D 的导数等于 2。

所以反向传播的第一步是得出这个值 2,写在这里,这个值是 J 相对于输入值 D 的导数。我们知道如果 D 改变一点点,J 会改变两倍,因为这个导数等于 2。

下一步是看前一个节点,问:J 相对于 A 的导数是多少?

要回答这个问题,我们必须问:如果 A 增加 0.001,J 会如何变化?我们知道如果 A 增加 0.001,D 就是 A - Y,所以如果 A 变成 4.001,D(即 A - Y)变成 4.001 减去 Y(等于 2),所以变成 2.001。因此,如果 A 增加 0.001,D 也增加 0.001。但我们之前已经得出结论,如果 D 增加 0.001,J 会增加两倍。所以现在我们知道了,如果 A 增加 0.001,D 增加 0.001,那么 J 大约增加 2 * 0.001

这告诉我们 J 相对于 A 的导数也等于 2。所以我要在这里填入那个值。这是 J 相对于 A 的导数,就像这是 J 相对于 D 的导数一样。

如果你以前上过微积分课,并且听说过链式法则,你可能会认出我刚才做的这个计算步骤实际上依赖于微积分的链式法则。如果你不熟悉链式法则,不用担心,你在接下来的视频中不需要知道它。但如果你见过链式法则,你可能会认出 J 相对于 A 的导数,等于 D 相对于 A 的变化率(即 D 相对于 A 的导数)乘以 J 相对于 D 的导数。上面的小计算表明 D 相对于 A 的偏导数是 1,我们之前表明 J 相对于 D 的导数等于 2,这就是为什么 J 相对于 A 的导数是 1 * 2,等于 2,也就是我们得到的值。但再次强调,如果你不熟悉链式法则,不用担心。

我们刚才在这里进行的逻辑推理解释了为什么我们知道 J 的增加量是 A 的两倍,这就是为什么这个导数项等于 2。

下一步是继续从右向左进行反向传播,我们会问:C 的微小变化会导致 J 改变多少?B 的微小变化会导致 J 改变多少?

我们弄清楚这一点的方法是问:如果 C 增加 ε(0.001),A 会改变多少?A 等于 C + B。所以如果 C 最终是 -3.999,那么 A(即 -3.999 + 8)变成 4.001。所以如果 C 增加 ε,A 也增加 ε。我们知道如果 A 增加 ε,那么因为 J 相对于 A 的导数是 2,我们知道这反过来会导致 J 增加 2 * ε。所以我们可以得出结论,如果 C 增加一点点,J 会增加两倍。我们知道这一点是因为我们知道 J 相对于 A 的导数是 2。这使我们能够得出结论,J 相对于 C 的导数也等于 2。

所以我要在这里填入那个值。再次说明,只有当你熟悉链式法则时,另一种写法是:J 相对于 C 的导数等于 A 相对于 C 的导数(结果是 1)乘以 J 相对于 A 的导数(我们之前算出来等于 2),所以这就是为什么它最终等于 2。

通过类似的计算,如果 B 增加 0.001,那么 A 也增加 0.001,J 增加 2 * 0.001,这就是为什么这个导数也等于 2。我在这里填入,J 相对于 B 的导数。这里是 J 相对于 C 的导数。

现在,最后一步是:J 相对于 W 的导数是多少?所以如果 W 增加 0.001,会发生什么?CW * X,如果 W 是 2.001,C(即 W * X)变成 -2 * 2.001,所以变成 -4.002。因此,如果 W 增加 ε,C 减少 2 * 0.001,或者等价地说,C 增加 -2 * 0.001。我们知道如果 C 增加 -2 * 0.001,因为 J 相对于 C 的导数是 2,这意味着 J 将增加 -4 * 0.001。因为如果 C 增加一定量,J 会改变两倍,所以 2 乘以 -2 倍的这个量就是 -4 倍的这个量。这使我们得出结论:如果 W 增加 0.001,J 增加 -4 * 0.001。所以 J 相对于 W 的导数是 -4。所以我要在这里写上 -4,因为那是 J 相对于 W 的导数。

再次说明,链式法则计算(如果你熟悉的话)是:这是 C 相对于 W 的导数乘以 J 相对于 C 的导数。这是 -2,这是 2,这就是为什么我们最终得到 -4。但再次强调,如果你不熟悉链式法则,不用担心。

总结与验证 ✅

上一节我们手动执行了计算图的反向传播。现在我们来总结并验证计算结果。

总结一下,我们刚刚所做的是在这个计算图中手动执行反向传播。前向传播是一个从左到右的计算,我们有 W = 2,这使我们能够计算 C,然后我们有 B,这使我们能够计算 A,然后是 D,最后是 J。反向传播从右向左进行,我们首先计算 J 相对于 D 的导数,然后回去计算 J 相对于 A 的导数,接着是 J 相对于 B 的导数、J 相对于 C 的导数,最后是 J 相对于 W 的导数。这就是为什么反向传播是从右到左的计算,而前向传播是从左到右的计算。

事实上,让我们仔细检查一下我们刚刚做的计算。对于给定的 wbXy 值,J 等于 1/2 * (wx + b - y)^2,即 1/2 * (2*(-2) + 8 - 2)^2,等于 2。现在,如果 w 增加 0.001,那么 J 变成 1/2 * (2.001*(-2) + 8 - 2)^2,如果你计算一下,结果大约是 1.996。所以 J 大约从 2 下降到 1.996,因此 J 下降了大约四倍的 ε。这表明如果 w 增加 ε,J 下降四倍的 ε,或者等价地说,J 增加 -4 * ε,这就是为什么 J 相对于 W 的导数是 -4,这也是我们在这里算出的结果。如果你想,可以暂停视频,自己仔细检查一下对于 B 会发生什么:另一个参数增加 ε,希望你发现 J 相对于 B 的导数确实是 2。当 B 增加 ε 时,J 增加两倍的 ε,正如这个导数计算所预测的那样。

为什么使用反向传播算法?⚡

我们为什么使用反向传播算法来计算导数?事实证明,反向传播是一种计算导数的有效方法。

我们将其安排为从右到左计算的原因是:如果你一开始就问 J 相对于 W 的导数是多少,那么要知道改变 W 如何影响改变 J,如果 W 增加 ε,J 会增加多少 ε?我们首先想知道的是 J 相对于 C 的导数是多少,因为改变 W 会改变这里的第一个量 C。所以要知道改变 W 如何影响 J,我们想知道改变 C 如何影响 J。但要知道改变 C 如何影响 J,最有用的计算方法是知道改变 C 会改变 A,所以你想知道改变 A 如何影响 J,依此类推。这就是为什么反向传播被安排为从右到左的计算,因为如果你从右到左进行计算,你可以找出改变 D 如何影响改变 J,然后你可以找出改变 A 如何影响 J,依此类推,直到你找到每个中间量(CAD)以及参数 WB 的导数。这样,通过一次从右到左的计算,你就可以找出改变任何这些中间量(CAD)以及输入参数 WB 会如何影响最终输出值 J

使反向传播高效的一点是,你注意到当我们进行从右到左的计算时,我们只需要计算一次这个项——J 相对于 A 的导数,然后这个量被用来计算 J 相对于 W 的导数和 J 相对于 B 的导数。

事实证明,如果一个计算图有 n 个节点(意味着 n 个这样的方框)和 p 个参数(本例中有两个参数),这个过程允许我们在大约 n + p 步内计算 J 相对于所有参数的导数,而不是 n * p 步。如果你有一个神经网络,比如说有 10,000 个节点和 maybe100,000 个参数,按现代标准来看,这甚至不算一个非常大的神经网络。能够在 10,000 + 100,000 = 110,000 步内计算导数,比需要 10,000 * 100,000 = 10 亿步要好得多。因此,使用计算图完成的反向传播算法为你提供了一种非常有效的方法来计算所有导数,这就是为什么它是当今深度学习算法实现方式中的一个关键思想。

课程总结 📝

本节课中,我们一起学习了计算图如何获取计算神经网络输出 A 以及成本函数 J 所需的所有步骤,并将逐步计算分解为不同的节点。然后使用从左到右的计算进行前向传播来计算成本函数 J,再使用从右到左的反向传播计算来计算所有导数。

在本视频中,你看到了这些思想被应用到一个小的神经网络示例。在下一个视频中,让我们将这些思想应用到一个更大的神经网络。让我们继续观看下一个视频。

74:更大神经网络示例与反向传播直觉 🧠

在本节课中,我们将通过一个更大的神经网络示例,深入理解计算图的工作原理以及反向传播的直觉。我们将一步步构建计算图,并解释如何高效地计算所有参数的梯度。


概述

在之前的课程中,我们学习了计算图和反向传播的基本概念。本节我们将把这些概念应用到一个包含单个隐藏层的更大神经网络示例中。我们将使用一个训练样本,并采用ReLU激活函数和平方误差成本函数,来演示前向传播和反向传播的计算过程。


网络结构与参数

我们使用的网络结构如下:一个输入层、一个包含单个神经元的隐藏层,以及一个输出层。为了使计算更易于处理,我们使用一个训练样本:输入 x = 1,真实标签 y = 5

网络参数设定为:

  • W1 = 2
  • B1 = 0
  • W2 = 3
  • B2 = 1

我们使用ReLU作为激活函数,其定义为:
g(z) = max(0, z)


前向传播计算

首先,我们进行前向传播,计算网络的预测值和成本。

以下是计算步骤:

  1. 计算隐藏层激活值 a1
    • z1 = W1 * x + B1 = 2 * 1 + 0 = 2
    • 由于 z1 > 0,ReLU激活函数的输出等于其输入。
    • 因此,a1 = g(z1) = 2

  1. 计算输出层激活值 a2

    • z2 = W2 * a1 + B2 = 3 * 2 + 1 = 7
    • 同样,z2 > 0,所以 a2 = g(z2) = 7
  2. 计算成本 J

    • 我们使用平方误差成本函数:J = 1/2 * (a2 - y)^2
    • 代入数值:J = 1/2 * (7 - 5)^2 = 1/2 * 4 = 2

至此,我们完成了网络的前向传播,得到了成本 J = 2


构建计算图

为了更清晰地展示计算过程,并为反向传播做准备,我们将上述步骤构建成一个计算图。

以下是计算图中的节点和计算顺序:

  • T1 = W1 * x:计算得到 2
  • z1 = T1 + B1:计算得到 2
  • a1 = g(z1):应用ReLU,得到 2
  • T2 = W2 * a1:计算得到 6
  • z2 = T2 + B2:计算得到 7
  • a2 = g(z2):应用ReLU,得到 7
  • J = 1/2 * (a2 - y)^2:计算得到 2

这个计算图清晰地展示了从输入 x 到最终成本 J 的数据流。


反向传播直觉

上一节我们构建了前向传播的计算图。本节中,我们来看看如何利用这个图进行反向传播,以高效计算所有参数(W1, B1, W2, B2)相对于成本 J 的梯度。

反向传播的核心是链式法则。我们从计算图的末端(成本 J)开始,逆向计算每个中间变量和参数的梯度。

以下是反向传播计算出的部分梯度值(具体推导过程遵循上一课的原理,此处省略详细步骤):

  • dJ/da2 = 2
  • dJ/dz2 = 2
  • dJ/dB2 = 2
  • dJ/dT2 = 2
  • dJ/dW1 = 6

我们以 dJ/dW1 = 6 为例进行验证。根据梯度的定义,它意味着如果 W1 增加一个微小值 ε,成本 J 将增加大约

验证

  • 假设 W12 变为 2.001(增加 ε=0.001)。
  • 重新进行前向传播:
    • a1 变为 2.001
    • a2 变为 3 * 2.001 + 1 = 7.003
    • J 变为 1/2 * (7.003 - 5)^2 ≈ 2.006005
  • 成本 J2 增加到了约 2.006,增量约为 0.006,这正好是 6 * 0.001。因此,dJ/dW1 = 6 的结论是正确的。


反向传播的效率优势

如果不使用反向传播,而采用“扰动法”逐个计算每个参数的梯度,效率会非常低下。

考虑一个有 n 个计算节点和 p 个参数的图:

  • 扰动法:需要分别扰动 p 个参数,每次扰动需要进行一次完整的前向传播(n 步计算)。总计算复杂度为 O(n * p)
  • 反向传播:只需进行一次前向传播(n 步)和一次反向传播(约 n 步),即可得到所有 p 个参数的梯度。总计算复杂度为 O(n)

对于现代大型神经网络(参数 p 可能达到数百万甚至数十亿),反向传播带来的效率提升是巨大的,它使得训练深度神经网络成为可能。


自动微分与现代框架

在TensorFlow、PyTorch等现代深度学习框架出现之前,研究人员必须手动推导并实现反向传播的数学公式。这个过程繁琐且容易出错。

如今,得益于自动微分技术(通常基于计算图实现),我们只需定义网络的前向传播,框架就能自动计算所有梯度。这大大降低了应用机器学习的门槛,让开发者能更专注于模型结构的设计。


总结

本节课中,我们一起学习了如何将计算图和反向传播的直觉应用到一个更大的神经网络示例中。

我们回顾了以下关键点:

  1. 如何为给定网络和样本执行前向传播并计算成本。
  2. 如何将计算过程组织成计算图
  3. 反向传播如何利用链式法则,高效地计算成本函数对所有参数的梯度。
  4. 通过数值验证了反向传播计算出的梯度是正确的。
  5. 理解了反向传播相比传统“扰动法”的巨大效率优势,这是训练深度神经网络的基础。
  6. 认识了自动微分在现代深度学习框架中的核心作用,它解放了研究者,使其无需手动计算复杂的导数。

掌握这些直觉,将帮助你更好地理解当你使用高级框架训练神经网络时,其底层究竟是如何高效工作的。

75:决定下一步尝试什么 🧭

在本节课中,我们将学习如何在一个机器学习项目中,系统性地决定下一步应该尝试什么来改进模型性能。我们将探讨多种可能的改进方向,并介绍如何通过诊断测试来高效地指导我们的决策,避免浪费数月时间在无效的尝试上。


现在,你已经学习了许多不同的机器学习算法,包括线性回归、逻辑回归,甚至深度学习和神经网络。下周你还会学到决策树。因此,你现在拥有了许多强大的机器学习工具。但如何有效地使用这些工具呢?我见过有些团队花费六个月来构建一个机器学习系统,而我认为一个更有经验的团队可能只需要几周。你能否快速构建一个高效的机器学习系统,很大程度上取决于你能否在项目过程中反复做出关于下一步该做什么的正确决策。

所以,在本周,我希望与你分享一些关于如何在机器学习项目中做出下一步决策的技巧,希望能为你节省大量时间。让我们来看看关于如何构建机器学习系统的一些建议。

让我们从一个例子开始。假设你已经实现了正则化线性回归来预测房价,因此你的学习算法有通常的成本函数:J(θ) = (1/2m) Σ (hθ(x⁽ⁱ⁾) - y⁽ⁱ⁾)² + (λ/2m) Σ θⱼ²。但如果你训练模型后发现,它的预测存在不可接受的大误差,那么当你构建机器学习算法时,下一步应该尝试什么呢?

通常,你可以尝试很多不同的事情。以下是几种常见的选项:

  • 获取更多训练数据:因为似乎拥有更多数据应该会有所帮助,对吗?
  • 尝试更少的特征:也许你认为特征太多了。
  • 获取额外的特征:例如,寻找房屋的额外属性添加到数据中,也许这会有帮助。
  • 添加多项式特征:你可以对现有特征x1、x2等,尝试添加如x1²、x2²、x1x2等多项式特征。
  • 调整正则化参数λ:你可能想知道λ值是否选择得当,可以尝试减小或增大它。

在任何给定的机器学习应用中,往往这些尝试中有些是富有成效的,有些则不是。能否高效地构建机器学习算法的关键在于,你是否能找到一种方法来明智地选择将时间投入到哪里。例如,我见过团队花费数月时间收集更多训练数据,认为更多数据会有帮助,但结果有时帮助很大,有时却没有。

因此,在本周,你将学习如何进行一系列诊断。所谓诊断,我指的是一种可以运行的测试,它能让你深入了解学习算法哪些部分有效、哪些无效,从而为改进其性能提供指导。其中一些诊断会告诉你,是否值得花费数周甚至数月去收集更多训练数据。如果值得,你就可以投入精力去获取更多数据,这有望带来性能提升;如果不值得,那么运行该诊断可能为你节省数月时间。

本周你还会看到,实现诊断可能需要时间,但运行它们可能是对你时间非常好的利用。


所以,本周我们将花大量时间讨论不同的诊断方法,这些方法可以指导你如何改进学习算法的性能。但首先,让我们看看如何评估学习算法的性能。我们将在下一个视频中进行。

76:模型评估 🧪

在本节课中,我们将学习如何评估机器学习模型的性能。掌握系统性的评估方法,不仅能判断模型的好坏,还能为后续的性能优化指明方向。

概述 📋

假设你已经训练了一个机器学习模型,接下来需要评估它的表现。系统性的评估方法有助于更清晰地规划如何提升模型性能。我们以预测房价为例,探讨模型评估的具体步骤。

模型评估的重要性 🔍

假设你训练了一个模型,根据房屋面积 x 来预测房价。该模型是一个四阶多项式,包含特征 xx⁴

公式f(x) = w₀ + w₁x + w₂x² + w₃x³ + w₄x⁴

由于我们使用五个数据点拟合了一个四阶多项式,模型在训练数据上表现得非常好。然而,我们并不满意这个模型,因为尽管它在训练数据上拟合得很好,但我们认为它无法泛化到训练集之外的新样本。

当仅使用房屋面积这一特征进行预测时,你可以绘制出模型曲线。从图中可以看出,曲线非常曲折,因此我们知道这不是一个好模型。

但是,如果你使用更多特征来拟合模型,例如 x₁(房屋面积)、x₂(卧室数量)、x₃(楼层数)和 x₄(房屋年龄),那么绘制 f 函数就变得困难得多,因为 f 现在是 x₁x₄ 的函数。如何绘制一个四维函数呢?

因此,为了判断模型是否表现良好,尤其是在特征超过一两个、难以绘制 f(x) 的应用场景中,我们需要一种更系统的方法来评估模型的表现。

数据集划分 📊

以下是一种你可以使用的技术。假设你有一个训练集,这里展示的是一个仅包含10个样本的小型训练集。

与其使用所有数据来训练模型的参数 WB,不如将训练集分成两个子集。我在这里画一条线,将70%的数据放入第一部分,我称之为训练集;将剩余30%的数据放入第二部分,我称之为测试集

我们将使用训练集(即前70%的数据)来训练模型参数,然后使用测试集来评估其性能。

在符号表示上,我将使用 (x₁, y₁)(xₘ, yₘ) 来表示训练样本,与之前相同。但在这个小例子中,我们会有七个训练样本。为了明确区分,我引入一个新的符号:m_train 表示训练样本的数量,在这个小数据集中是7。下标 train 强调我们正在查看数据的训练集部分。

对于测试集,我使用符号 (x₁_test, y₁_test) 表示第一个测试样本,依此类推直到 (x_m_test, y_m_test)m_test 是测试样本的数量,在这个例子中是3。

按照70/30或80/20的比例划分数据集是常见的做法,大部分数据进入训练集,较小部分进入测试集。

线性回归模型评估 📈

为了训练并评估一个模型,如果你使用的是带有平方误差成本的线性回归,过程如下。

首先,通过最小化成本函数 J(W, B) 来拟合参数。这是通常的平方误差成本函数加上正则化项。

公式J(W, B) = (1/(2m)) * Σ (f(xⁱ) - yⁱ)² + (λ/(2m)) * Σ wⱼ²

然后,为了判断模型的表现,你需要计算测试误差 J_test(W, B),它等于测试集上的平均误差。

公式J_test(W, B) = (1/(2 * m_test)) * Σ (f(x_testⁱ) - y_testⁱ)²

请注意,测试误差公式 J_test 不包含正则化项。这将帮助你了解学习算法的表现如何。

另一个通常有用的量是训练误差,它衡量了学习算法在训练集上的表现。

公式J_train(W, B) = (1/(2 * m_train)) * Σ (f(x_trainⁱ) - y_trainⁱ)²

同样,这个公式也不包含正则化项,与你用于拟合参数而最小化的成本函数不同。

在本视频前面看到的模型中,J_train(W, B) 会很低,因为训练样本的平均误差将为0或非常接近0,所以 J_train 将非常接近0。但是,如果你的测试集中有一些算法未训练过的额外样本,那么这些测试样本可能看起来像这样。算法预测的估计房价与这些房价的实际值之间存在很大差距,因此 J_test 会很高。看到 J_test 在这个模型上很高,让你意识到即使它在训练集上表现很好,实际上在泛化到训练集之外的新样本、新数据点时并不好。

逻辑回归模型评估 🧮

以上是使用平方误差成本的回归问题。现在让我们看看如何将此过程应用于分类问题,例如,如果你要对是0还是1的手写数字进行分类。

与之前一样,你通过最小化成本函数来拟合参数,以找到参数 WB。例如,如果你在训练逻辑回归,那么这就是成本函数 J(W, B)

公式J(W, B) = - (1/m) * Σ [yⁱ * log(f(xⁱ)) + (1 - yⁱ) * log(1 - f(xⁱ))] + (λ/(2m)) * Σ wⱼ²

然后计算测试误差 J_test,它是在测试集(即那30%不在训练集中的数据)上逻辑损失的平均值。

公式J_test = (1/m_test) * Σ L(f(x_testⁱ), y_testⁱ),其中 L 是逻辑损失函数。

训练误差也可以使用类似的公式计算,即算法用于最小化成本函数 J(W, B) 的训练数据上的平均逻辑损失。

分类错误率评估 ⚖️

我在这里描述的方法,通过观察测试误差的表现来判断学习算法是否做得好,是可行的。然而,在将机器学习应用于分类问题时,实际上还有另一种定义 J_testJ_train 的方式,可能更常用。那就是不使用逻辑损失来计算测试误差和训练误差,而是测量算法在测试集和训练集上错误分类的比例。

具体来说,在测试集上,你可以让算法对每个测试样本做出1或0的预测。回想一下,如果 f(x) ≥ 0.5,我们预测 ŷ 为1;如果 f(x) < 0.5,则预测为0。然后,你可以在测试集中统计 ŷ 不等于实际真实标签 y 的样本比例。

代码示例(概念性)

# 假设 predictions 是模型对测试集的预测结果(0或1),y_test 是真实标签
misclassified = sum(predictions != y_test)
J_test_error_rate = misclassified / len(y_test)

具体来说,如果你在对0和1的手写数字进行分类,那么 J_test 就是测试集中0被分类为1或1被分类为0的比例。类似地,J_train 是训练集中被错误分类的比例。

总结 🎯

本节课中,我们一起学习了如何系统性地评估机器学习模型的性能。

  • 核心方法:将数据集划分为训练集和测试集,例如70/30或80/20的比例。
  • 评估指标
    • 对于回归问题,通常使用均方误差(MSE) 作为训练误差 J_train 和测试误差 J_test 的衡量标准。
    • 对于分类问题,除了使用逻辑损失,更常用且直观的指标是错误分类率,即模型预测错误的样本比例。
  • 关键洞察:比较 J_trainJ_test 至关重要。一个很低的 J_train 配合一个很高的 J_test,通常表明模型出现了过拟合,即在训练集上表现完美,但无法泛化到新数据。

通过计算 J_testJ_train,你现在可以衡量模型在测试集和训练集上的表现。这个过程是自动为给定机器学习应用选择模型的第一步。例如,在预测房价时,你应该用直线、二阶多项式、三阶还是四阶多项式来拟合数据?事实证明,通过对本视频中看到的概念进行一点改进,你将能够拥有一个算法来帮助你自动做出这类决策。让我们在下一个视频中看看如何做到这一点。

77:模型选择与训练 🎯

概述

在本节课中,我们将学习如何使用交叉验证集和测试集来选择机器学习模型。我们将探讨为什么仅使用训练集和测试集进行模型选择可能存在问题,并介绍一种更可靠的三步数据划分方法。


模型选择的问题

上一节我们介绍了如何使用测试集评估模型性能。本节中,我们来看看如何改进这一方法,以便自动为机器学习算法选择合适的模型。

我们已经看到,当模型参数 WB 在训练集上拟合后,训练误差可能无法很好地反映算法在未见数据上的表现。具体来说,训练误差可能接近于零,这通常远低于实际的泛化误差,即模型在训练集之外的新样本上的平均误差。

上一节提到,测试集上的性能指标 J_test 能更好地预测模型在新数据上的表现。现在,我们来看看这如何影响我们使用测试集为特定机器学习应用选择模型的过程。


初始模型选择方法

假设我们正在拟合一个预测房价的回归模型。

以下是可能考虑的模型:

  • 一阶多项式(线性模型):我们使用 d = 1 表示拟合一个一阶多项式。在训练集上拟合后,得到参数 W1, B1,然后计算 J_test(W1, B1) 来估计其泛化能力。
  • 二阶多项式(二次模型):这是模型 y = w1x + w2x² + b。拟合后得到参数 W2, B2,并计算 J_test(W2, B2)
  • 更高阶多项式:可以继续尝试 d = 3 直到 d = 10 的多项式,分别得到参数和对应的 J_test 值。

一个可能(但并非最佳)的流程是:查看所有这些 J_test 值,选择数值最低的那个。例如,如果 J_test(W5, B5) 最低,那么你可能会选择五阶多项式(d = 5)作为最终模型。如果你想估计这个模型的性能,一个可能(但有缺陷)的做法是直接报告 J_test(W5, B5)


初始方法的缺陷

这个流程存在缺陷。J_test(W5, B5) 很可能是一个对泛化误差的乐观估计,即它可能低于实际的泛化误差。

原因在于,我们在上述流程中实际上拟合了一个额外的参数——多项式的阶数 d,并且我们使用了测试集来选择这个参数。正如之前所见,如果用训练数据拟合 W, B,那么训练误差会是泛化误差的过度乐观估计。同样,如果使用测试集来选择参数 d,那么测试集误差 J_test 现在也变成了对泛化误差的过度乐观(即偏低)估计。

因此,我们不推荐使用上述有缺陷的流程。相反,如果你想自动选择模型(例如决定使用几阶多项式),以下是修改后的训练和测试流程。


改进方法:引入交叉验证集

为了进行模型选择(即在可能用于机器学习应用的不同模型中进行选择),我们将修改流程,将数据划分为三个子集,而不是两个。

我们将数据分为:

  1. 训练集
  2. 交叉验证集
  3. 测试集

例如,对于10个训练样本,我们可以这样划分:

  • 训练集:占数据的60%。记作 {(x_train^(i), y_train^(i))}, i = 1...m_train,其中 m_train = 6
  • 交叉验证集:占数据的20%。记作 {(x_cv^(i), y_cv^(i))}, i = 1...m_cv,其中 m_cv = 2
  • 测试集:占数据的20%。记作 {(x_test^(i), y_test^(i))}, i = 1...m_test,其中 m_test = 2

交叉验证集这个名称指的是我们将用这部分额外数据来检查或验证不同模型的有效性或准确性。它有时也被简称为验证集开发集dev集


使用三个子集进行模型选择

有了训练集、交叉验证集和测试集这三个数据子集,我们可以使用以下三个公式计算误差:

  • 训练误差J_train(W, B) = (1/(2*m_train)) * Σ (y_train^(i) - f(x_train(i)))2
  • 交叉验证误差J_cv(W, B) = (1/(2*m_cv)) * Σ (y_cv^(i) - f(x_cv(i)))2
  • 测试误差J_test(W, B) = (1/(2*m_test)) * Σ (y_test^(i) - f(x_test(i)))2

注意,这些公式通常不包括训练目标函数中的正则化项。交叉验证误差也常被称为验证误差开发集误差

以下是进行模型选择的步骤:

对于之前提到的10个模型(d = 1d = 10):

  1. 使用训练集拟合每个模型的参数 W^d, B^d
  2. 使用交叉验证集计算每个模型的交叉验证误差 J_cv(W^d, B^d)
  3. 查看哪个模型的交叉验证误差最低。例如,如果 J_cv(W^4, B^4) 最低,那么你将选择四阶多项式作为最终模型。
  4. 最后,如果你想报告这个模型在新数据上泛化误差的估计,你应使用测试集,报告 J_test(W^4, B^4)

请注意,在整个过程中,你使用训练集拟合参数 W, B,使用交叉验证集选择参数 d(多项式阶数)。直到这一步,你都没有使用测试集来拟合任何参数(无论是 W, B 还是 d)。这就是为什么在这个例子中,J_test 将是模型 W^4, B^4 泛化误差的一个公平估计。


方法的应用与最佳实践

这个模型选择流程也适用于其他类型的模型选择,例如选择神经网络架构。

如果你正在为手写数字识别拟合模型,可能会考虑几种不同的神经网络结构(例如小型、中型、大型网络)。你可以:

  1. 训练所有这些模型,得到各自的参数。
  2. 使用交叉验证集评估每个模型的性能(对于分类问题,J_cv 通常是误分类样本的比例)。
  3. 选择交叉验证误差最低的模型。
  4. 使用测试集来估计最终所选模型的泛化误差。

机器学习中的最佳实践是:如果你需要对模型做出决策(例如拟合参数、选择模型架构如神经网络层数或多项式阶数),请仅使用训练集交叉验证集来做出所有这些决策。在仍在调整学习算法时,完全不要查看测试集。只有在确定了最终模型之后,才在测试集上进行评估。因为你没有使用测试集做出任何决策,这确保了你的测试集是对模型在新数据上泛化能力的公平且不过度乐观的估计。


总结

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

  1. 仅使用训练集和测试集进行模型选择的缺陷,即会导致对泛化误差的乐观估计。
  2. 如何通过将数据划分为训练集交叉验证集测试集三个部分来改进流程。
  3. 使用交叉验证集进行模型选择(如选择多项式阶数或神经网络架构)的具体步骤。
  4. 机器学习的最佳实践:在模型开发阶段仅使用训练集和交叉验证集,仅在最终评估时使用测试集,以确保评估的公正性。

掌握了评估学习算法和自动选择模型的方法后,我们将在接下来的视频中深入探讨一些强大的诊断工具,例如偏差与方差分析。

78:诊断偏差与方差 📊

在本节课中,我们将学习如何诊断机器学习模型中的偏差与方差问题。理解这两个核心概念是改进模型性能的关键。

概述

开发机器学习系统的典型流程是:提出想法、训练模型,但模型首次表现往往不尽如人意。构建系统的关键在于决定下一步如何改进性能。通过分析学习算法的偏差与方差,可以获得有效的改进指导。

偏差与方差的直观理解

上一节我们介绍了诊断偏差与方差的重要性,本节中我们来看看它们的直观含义。

你可能在线性回归课程中见过这个例子。给定这些数据:

  • 如果拟合一条直线,效果不佳。我们说该模型偏差高欠拟合数据集。
  • 如果拟合一个四次多项式,则方差高过拟合
  • 如果拟合一个二次多项式,效果看起来很好,恰到好处。

由于这是一个仅包含单个特征 X 的问题,我们可以绘制函数 F 的图像来观察。但如果特征更多,则无法轻易绘制并可视化其表现。

系统化的诊断方法

与其依赖可视化,更系统化的诊断方法是观察算法在训练集和交叉验证集上的性能。

以下是基于不同拟合情况的性能分析:

  • 左侧示例(欠拟合/高偏差)
    • 计算 J_train(训练集误差):算法在训练集上表现不佳,因此 J_train 值高。
    • 计算 J_CV(交叉验证误差):对于未见过的示例,模型表现也不好,因此 J_CV 值也高。
    • 高偏差算法的特征:即使在训练集上表现也不好。J_train 高是表明算法存在高偏差的强指标。

  • 右侧示例(过拟合/高方差)
    • 计算 J_train:模型在训练集上表现极佳,因此 J_train 值低。
    • 计算 J_CV:在训练集之外的房屋数据上评估时,交叉验证误差 J_CV 会相当高。
    • 高方差算法的特征J_CV 远大于 J_train。换句话说,它在已见数据上的表现远好于未见数据。这是算法存在高方差的强指标。

通过计算 J_trainJ_CV,并观察 J_train 是否高,或 J_CV 是否远大于 J_train,即使无法绘制函数 F,也能判断算法是否存在高偏差或高方差。

  • 中间示例(拟合良好)
    • J_train 较低,因为模型在训练集上表现很好。
    • 对于交叉验证集中的新示例,J_CV 也较低。
    • J_train 不高表明没有高偏差问题,J_CV 不比 J_train 差很多表明也没有高方差问题。

因此,二次模型对这个应用来说似乎是一个相当好的模型。

总结

  • d=1(线性多项式)时,J_train 高,J_CV 高。
  • d=4 时,J_train 低,但 J_CV 高。
  • d=2 时,两者都相当低。

偏差与方差的另一种视角

现在我们从另一个角度看待偏差与方差,特别是 J_trainJ_CV 如何随拟合多项式次数的变化而变化。

绘制一张图,横轴表示我们拟合数据的多项式次数 d。左侧对应较小的 d 值(如 d=1,拟合直线),右侧对应较大的 d 值(如 d=4 或更高,拟合高阶多项式)。

  • J_traind 的变化:随着拟合多项式的次数增加,训练误差 J_train 倾向于下降。因为当使用非常简单的线性函数时,它不能很好地拟合训练数据;而使用二次、三次或四次多项式时,它能越来越好地拟合训练数据。所以,随着多项式次数增加,J_train 通常会下降。
  • J_CVd 的变化J_CV 衡量模型在未用于拟合的数据上的表现。当 d=1(多项式次数很低)时,J_CV 相当高,因为它欠拟合,在交叉验证集上表现不佳。在右侧,当多项式次数非常大(如 d=4)时,它在交叉验证集上表现也不好,因此 J_CV 也高。但如果 d 适中(如二次多项式),则表现好得多。因此,随着多项式次数变化,J_CV 曲线会先下降后上升。
    • 如果多项式次数太低,会欠拟合,在交叉验证集上表现不佳。
    • 如果次数太高,会过拟合,在交叉验证集上表现也不佳。
    • 只有当次数适中时,才恰到好处。这就是为什么在我们的例子中,二次多项式最终具有较低的交叉验证误差,既没有高偏差也没有高方差问题。

如何诊断偏差与方差

以下是诊断学习算法偏差与方差的方法总结:

  • 诊断高偏差(欠拟合):关键指标是 J_train。这对应于曲线的最左侧部分,那里 J_train 高,通常 J_trainJ_CV 彼此接近。
  • 诊断高方差(过拟合):关键指标是 J_CV >> J_train>> 在数学中表示“远大于”)。这对应于曲线的最右侧部分,那里 J_CV 远大于 J_train,通常 J_train 会相当低。

同时存在高偏差与高方差的情况

尽管我们刚刚分别讨论了偏差与方差,但在某些情况下,可能同时存在高偏差和高方差。

  • 对于线性回归,这种情况不常发生。但对于神经网络,有些应用可能不幸地同时存在高偏差和高方差。
  • 识别这种情况的方法是:J_train(在训练集上表现不佳),但更糟糕的是,交叉验证误差 J_CV 比训练集误差 J_train 还要大得多。
  • 高偏差和高方差的概念对于应用于一维数据的线性模型并不常见。但为了直观理解,可以想象为:对于部分输入,模型非常复杂以至于过拟合;而对于其他部分输入,模型甚至不能很好地拟合训练数据,即欠拟合。在这个看起来有些人为的单特征输入例子中,我们在部分输入上过拟合(拟合训练集很好),在另一部分输入上欠拟合(甚至不能很好拟合训练数据)。这就是在某些应用中可能同时出现高偏差和高方差的方式。

这种情况的指标是:算法在训练集上表现差,并且在交叉验证集上的表现比在训练集上差得多。对于大多数学习应用,你可能主要面临高偏差或高方差问题,而不是两者同时存在,但有时两者确实可能同时发生。

总结

本节课中我们一起学习了诊断机器学习模型偏差与方差的核心方法。

  • 高偏差意味着模型甚至在训练集上表现也不好。
  • 高方差意味着模型在交叉验证集上的表现远差于在训练集上的表现。

当我训练机器学习算法时,几乎总是试图弄清楚算法在多大程度上存在高偏差(欠拟合)问题,还是高方差(过拟合)问题。这将为我们本周后续课程中如何改进算法性能提供良好的指导。

接下来,让我们看看正则化如何影响学习算法的偏差和方差,因为这有助于你更好地理解何时应该使用正则化。我们将在下一个视频中探讨。

79:正则化与偏差-方差分析 📊

在本节课中,我们将学习正则化参数 λ 如何影响学习算法的偏差、方差以及整体性能。通过理解 λ 的不同取值对模型拟合的影响,你将能够为算法选择合适的正则化参数。

概述

上一节我们介绍了多项式次数 D 对偏差和方差的影响。本节中,我们来看看正则化参数 λ 如何以类似的方式影响模型的性能。具体来说,我们将探讨 λ 的极端取值和中间取值如何导致欠拟合、过拟合或“恰到好处”的拟合,并学习如何使用交叉验证来选择最佳的 λ 值。

λ 对模型拟合的影响

我们使用一个四次多项式模型作为示例,该模型使用正则化进行拟合,其中 λ 是控制参数 W 大小与训练数据拟合程度之间权衡的正则化参数。

模型公式
J(w, b) = (1/(2m)) * Σ(y_hat - y)^2 + (λ/(2m)) * Σ w_j^2

λ 值极大的情况

假设我们将 λ 设置为一个非常大的值,例如 λ = 10000。

以下是这种情况下的结果:

  • 算法会极力保持参数 W 非常小。
  • 最终,W1, W2 等所有参数都会接近于零。
  • 模型近似为 f(x) ≈ b,即一个常数值函数。

因此,你会得到一个类似这样的模型,它显然具有高偏差,并且欠拟合训练数据,因为即使在训练集上表现也很差,J_train 很大。

λ 值极小的情况

假设我们将 λ 设置为一个非常小的值,极端情况下 λ = 0。

以下是这种情况下的结果:

  • 此时没有正则化。
  • 我们只是在不使用正则化的情况下拟合完整的四次多项式。
  • 最终会得到之前见过的、过拟合数据的曲线。

正如之前所见,这种模型的 J_train 很小,但 J_CV 远大于 J_train(或者说 J_CV 很大),这表明模型具有高方差,并且过拟合了数据。

λ 值适中的情况

如果 λ 取一个中间值,既不是极大的 10000,也不是极小的 0,那么你希望得到一个像这样“恰到好处”的模型,它能很好地拟合数据,同时具有较小的 J_trainJ_CV

使用交叉验证选择 λ

如果你正在尝试为正则化参数 λ 决定一个合适的值,交叉验证也提供了一种方法。

我们正在解决的问题是:如果你正在拟合一个四次多项式模型并使用正则化,如何选择一个好的 λ 值?这个过程类似于之前使用交叉验证选择多项式次数 D 的过程。

具体步骤如下:

  1. 尝试 λ = 0,最小化成本函数,得到参数 (w1, b1),然后计算交叉验证误差 J_CV(w1, b1)
  2. 尝试 λ = 0.01,再次最小化成本函数,得到第二组参数 (w2, b2),并评估其在交叉验证集上的表现。
  3. 继续尝试其他 λ 值,例如将其翻倍至 λ = 0.02,得到 J_CV(w3, b3)
  4. 持续翻倍尝试,经过多次后,λ 可能达到约 10,得到参数 (w12, b12)J_CV(w12, b12)

通过尝试 λ 的大范围可能值,使用这些不同的正则化参数拟合模型,然后在交叉验证集上评估性能,你就可以尝试挑选出正则化参数的最佳值。

具体来说,如果在本例中,你发现 J_CV(w5, b5) 在所有交叉验证误差中值最低,那么你可能会决定选择这个 λ 值,并使用 (w5, b5) 作为最终选定的参数。最后,如果你想报告泛化误差的估计值,可以报告测试集误差 J_test(w5, b5)

训练误差与交叉验证误差随 λ 的变化

为了进一步理解这个算法在做什么,我们来看看训练误差和交叉验证误差如何随参数 λ 的变化而变化。

在这个图中,X 轴标注的是正则化参数 λ 的值。

  • 左侧极端 (λ = 0):对应不使用任何正则化,我们最终会得到那条非常弯曲的曲线(当 λ 很小或为零时)。在这种情况下,我们有一个高方差模型,因此 J_train 会很小,而 J_CV 会很大,因为模型在训练数据上表现很好,但在交叉验证数据上表现差得多。
  • 右侧极端 (λ 非常大,如 10000):最终拟合出一个类似这样的模型,因此具有高偏差,它欠拟合数据。实际上,J_train 会很高,J_CV 也会很高。

观察 J_train 随 λ 的变化:
J_train 会像这样上升,因为在优化成本函数时,λ 越大,算法就越倾向于保持 W 的平方较小(即给正则项赋予更多权重),从而越少关注在训练集上实际表现良好。因此,随着 λ 增加,训练误差 J_train 往往会像这样增加。

观察交叉验证误差 J_CV 随 λ 的变化:
J_CV 会呈现这样的形状。因为我们已经看到,如果 λ 太小或太大,它在交叉验证集上表现都不会好——在左侧会过拟合,在右侧会欠拟合。会存在某个中间的 λ 值,使得算法表现最佳。

交叉验证所做的就是尝试许多不同的 λ 值(正如上一张幻灯片所示,尝试 λ=0, 0.01, 0.02 等),并在许多不同的点上评估交叉验证误差,然后希望选择一个具有低交叉验证误差的值,这有望对应你应用程序的一个好模型。

如果将这个图与上一节中横轴为多项式次数 D 的图进行比较,这两个图看起来有点像彼此的镜像。这是因为在拟合多项式次数时,曲线的左侧部分对应欠拟合和高偏差,右侧部分对应过拟合和高方差;而在这个图中,高方差在左侧,高偏差在右侧。这就是为什么这两个图像有点像彼此的镜像。但在两种情况下,交叉验证通过评估不同的值,都可以帮助你选择一个好的 D 值或一个好的 λ 值。

总结

本节课中,我们一起学习了正则化参数 λ 的选择如何影响算法的偏差、方差和整体性能。你也看到了如何使用交叉验证来为正则化参数 λ 做出一个好的选择。

到目前为止,我们讨论了高训练集误差(高 J_train)如何指示高偏差问题,以及高交叉验证误差 J_CV(特别是当它远高于 J_train 时)如何指示方差问题。但是,“高”或“远高于”这些词的实际含义是什么?在下一节视频中,我们将看看如何通过观察 J_trainJ_CV 的数值来判断它们是否过高或过低。事实证明,建立性能基线这一概念的进一步细化,将使你更容易查看 J_trainJ_CV 这些数字并判断它们的高低。让我们在下一节视频中看看这一切意味着什么。

80:建立性能基线水平 📊

在本节课中,我们将学习如何通过具体的训练误差和交叉验证误差数值,来判断一个学习算法是否存在高偏差或高方差问题。我们将以语音识别应用为例,并引入“性能基线水平”这一关键概念,它能帮助我们更准确地评估算法性能。


性能基线水平的概念

上一节我们讨论了通过误差判断偏差与方差。本节中我们来看看,当数据本身存在噪声、无法达到零误差时,如何建立一个合理的性能基准。

性能基线水平,指的是你的学习算法最终可以合理期望达到的误差水平。一个常见的建立基线的方法是测量人类在该任务上的表现。因为人类在处理非结构化数据(如音频、图像、文本)方面通常非常出色。

以下是几种建立基线水平的方法:

  • 测量人类在该任务上的表现。
  • 参考已有的竞争算法或先前实现的性能。
  • 基于先验经验进行估算。

语音识别案例分析

让我们通过一个语音识别的具体例子来理解这些概念。假设我们训练了一个语音识别系统。

  • 训练误差:算法在训练集上未能完全正确转录的音频片段百分比。假设为 10.8%
  • 交叉验证误差:算法在交叉验证集上的错误率。假设为 14.8%

如果只看这两个数字,10.8%的训练误差似乎很高,可能让人误以为存在高偏差问题。然而,关键在于建立性能基线。

我们测量了人类水平性能,发现人类转录这些音频的平均错误率为 10.6%。这个错误率之所以不低,是因为网络搜索中存在大量嘈杂的音频,即使人类也难以准确识别。

有了这个基线,我们的分析就发生了变化:

  • 训练误差(10.8%)仅比人类水平(10.6%)高出 0.2%。这表明算法在训练集上表现得相当好,并非高偏差。
  • 交叉验证误差(14.8%)比训练误差(10.8%)高出 4.0%。这个较大的差距表明算法存在高方差问题,即过拟合。

判断偏差与方差的通用方法

因此,要判断算法是否存在高偏差或高方差,我们需要关注两个关键差距:

  1. 训练误差与基线水平的差距:如果这个差距很大,则存在高偏差问题。公式表示为:J_train - Baseline
  2. 交叉验证误差与训练误差的差距:如果这个差距很大,则存在高方差问题。公式表示为:J_cv - J_train

让我们看另一个假设的例子:

  • 基线水平(人类错误率):1.0%
  • 训练误差:5.4%
  • 交叉验证误差:6.1%

分析如下:

  • 训练误差与基线的差距为 5.4% - 1.0% = 4.4%,差距很大,表明存在高偏差
  • 交叉验证与训练误差的差距为 6.1% - 5.4% = 0.7%,差距很小,表明方差问题不严重

一个算法也可能同时存在高偏差和高方差。例如:

  • 基线水平:1.0%
  • 训练误差:5.4%
  • 交叉验证误差:10.1%

此时,第一个差距(4.4%)和第二个差距(4.7%)都很大,说明算法同时受高偏差和高方差困扰。


总结

本节课中我们一起学习了如何利用性能基线水平来更准确地诊断学习算法的问题。

  • 在数据嘈杂、零误差不现实的任务中,建立性能基线(如人类水平表现)至关重要。
  • 通过比较训练误差与基线水平的差距,可以判断是否存在高偏差问题。
  • 通过比较交叉验证误差与训练误差的差距,可以判断是否存在高方差问题。
  • 这种方法比单纯看训练误差绝对值更为可靠,能让我们更清楚地了解算法性能与期望目标之间的距离。

为了进一步深化对算法性能的直觉理解,还有一个有用的工具——学习曲线。让我们在下一个视频中探讨它的含义。

81:📈 学习曲线

在本节课中,我们将要学习学习曲线。学习曲线是一种工具,它能帮助我们理解学习算法的性能如何随着经验量(例如训练样本的数量)的变化而变化。通过分析学习曲线的形状,我们可以诊断模型是存在高偏差(欠拟合)还是高方差(过拟合)问题,从而指导我们采取正确的改进措施。


什么是学习曲线?📊

上一节我们介绍了偏差与方差的概念,本节中我们来看看如何用学习曲线来可视化它们。

学习曲线通常绘制两种误差随训练集大小 M_train 变化的曲线:

  • 训练误差 J_train
  • 交叉验证误差 J_cv

横轴代表训练集大小,纵轴代表误差值。随着训练样本的增加,这两种误差会呈现出特定的变化趋势。


学习曲线的典型形状

以下是学习曲线通常如何变化:

  • 随着训练集规模 M_train 增大,模型能学到更多,因此交叉验证误差 J_cv 会下降
  • 有趣的是,训练误差 J_train 通常会随着训练集增大而上升

为什么训练误差会上升?

以下是原因分析:

  1. 训练样本极少时(例如1-3个),一个足够复杂的模型(如二次函数)可以完美拟合所有数据点,训练误差接近0。
  2. 训练样本增多时(例如4个或更多),模型要完美拟合所有样本变得困难,拟合曲线可能会出现微小偏差,因此训练误差会上升。
  3. 训练样本非常多时,模型几乎不可能完美拟合每一个样本,训练误差会持续上升并最终趋于平缓。

此外,交叉验证误差通常高于训练误差,因为模型参数是在训练集上优化的,因此在训练集上的表现通常会更好。


高偏差(欠拟合)的学习曲线 🔍

上一节我们了解了高偏差意味着模型过于简单。本节中我们来看看它在学习曲线上的表现。

当模型存在高偏差(例如用线性函数拟合非线性数据)时,学习曲线具有以下特征:

  • 训练误差 J_train:初始较高,并随着样本增加而上升,但很快会趋于平缓(达到平台期)。因为简单的模型(如直线)其拟合能力有限,增加更多数据也不会让它发生本质改变。
  • 交叉验证误差 J_cv:初始很高,随样本增加而下降,但同样会快速趋于平缓,且始终高于训练误差。
  • 与基准的差距J_trainJ_cv 最终都会稳定在一个较高的水平,与人类水平性能等基准存在明显差距。这个差距是存在高偏差的明确信号。

一个重要结论

如果学习算法存在高偏差,仅仅增加更多训练数据本身,并不会显著提升模型性能。因为模型本身太简单,无法从额外数据中获益。因此,在投入大量精力收集数据前,应先检查是否存在高偏差问题,并考虑使用更复杂的模型或增加特征。


高方差(过拟合)的学习曲线 🎯

现在,让我们看看存在高方差(例如用高阶多项式且正则化很弱)时,学习曲线是什么样子。

  • 训练误差 J_train:通常非常低(可能接近0,甚至低于人类水平),并随着样本增加而缓慢上升。
  • 交叉验证误差 J_cv:初始非常高,远高于训练误差,两者之间存在巨大差距。这个巨大差距是存在高方差的典型标志。
  • 趋势:随着训练样本增加,J_cv 有持续下降并逐渐接近 J_train 的趋势。

与高偏差的关键区别

对于高方差问题,增加更多的训练数据很可能会有帮助。因为更多的数据可以让复杂的模型学到更通用的模式,而不是仅仅记住训练集,从而使交叉验证误差有效降低,模型性能得以提升。


如何绘制与应用学习曲线 🛠️

在实际构建机器学习应用时,你可以通过以下步骤绘制学习曲线来辅助诊断:

以下是绘制学习曲线的步骤:

  1. 准备不同大小的训练子集(例如,用100、200、400个样本)。
  2. 分别用每个子集训练模型。
  3. 计算并记录每个模型在自身训练集上的误差 J_train 和在固定的交叉验证集上的误差 J_cv
  4. 以训练集大小为横轴,误差为纵轴,绘制 J_trainJ_cv 的曲线。

通过观察曲线的形状(是像高偏差还是高方差),你可以判断模型当前的主要问题。

注意:绘制完整的学习曲线需要训练多个不同规模的模型,计算成本较高,因此在实践中并不总是进行。但理解其背后的原理,能帮助你在脑海中形成直观判断。


回顾与总结 🎓

本节课中我们一起学习了学习曲线这一重要诊断工具。

  • 学习曲线描绘了训练误差 J_train 和交叉验证误差 J_cv 随训练集大小变化的趋势。
  • 高偏差(欠拟合)J_trainJ_cv 都很高且较早趋于平缓,两者差距小,但与理想性能差距大。增加数据帮助不大,需要更复杂的模型或更多特征。
  • 高方差(过拟合)J_train 很低,J_cv 很高,两者差距巨大。增加训练数据很可能有效,此外也可以尝试增强正则化或减少特征。

理解这些模式,能帮助你有针对性地改进机器学习模型,避免盲目尝试。在接下来的视频中,我们将回到房价预测的例子,看看如何运用偏差、方差和学习曲线的知识来做出具体决策。

82:重新审视决定下一步尝试什么 🔍

在本节课中,我们将学习如何通过分析训练误差和交叉验证误差,来判断学习算法是否存在高偏差或高方差问题。掌握这一诊断方法,将帮助我们更有效地选择改进策略,从而提升算法性能。


诊断偏差与方差

上一节我们介绍了通过观察训练误差和交叉验证误差来诊断算法问题。本节中,我们来看看如何根据诊断结果,决定下一步应该尝试哪些改进措施。

通过绘制学习曲线或直接比较 J_train(训练误差)和 J_CV(交叉验证误差),你可以判断学习算法是存在高偏差(欠拟合)还是高方差(过拟合)问题。这是我训练学习算法时常规的步骤。


改进措施分类

以下是六种常见的改进学习算法性能的思路。每种方法主要针对高偏差或高方差中的某一类问题。

  • 获取更多训练样本
  • 尝试更小的特征集
  • 获取额外的特征
  • 增加多项式特征
  • 减小正则化参数 λ
  • 增大正则化参数 λ

事实证明,这六项措施中,每一项要么有助于解决高方差问题,要么有助于解决高偏差问题。具体来说,如果你的学习算法存在高偏差,其中三项技术会很有用;如果存在高方差,则另外三项技术会很有用。


措施与问题的对应关系

让我们逐一分析,看看每项措施对应解决哪种问题。

1. 获取更多训练样本
我们在上一节看到,如果你的算法存在高偏差,仅仅获取更多训练数据本身可能帮助不大。相反,如果你的算法存在高方差(例如对很小的训练集过拟合),那么获取更多训练样本会有很大帮助。因此,获取更多训练样本有助于解决高方差问题。

2. 尝试更小的特征集
有时,如果你的学习算法特征过多,会赋予算法过大的灵活性去拟合非常复杂的模型。这有点像你拥有 x, x², x³, x⁴, x⁵ 等特征。如果你能消除其中一些,你的模型就不会那么复杂,也就不会出现高方差。因此,如果你怀疑算法中有许多不相关或冗余的特征,减少特征数量将有助于降低算法过拟合数据的灵活性。这是一种解决高方差问题的策略。

3. 获取额外的特征
这与采用更小的特征集正好相反。增加额外特征有助于解决高偏差问题。举一个具体例子:如果你仅根据房屋面积来预测房价,但房价实际上还很大程度上取决于卧室数量、楼层数和房龄,那么算法永远无法做得很好,除非你添加这些额外特征。这是一个高偏差问题,因为仅知道面积时,即使在训练集上也无法做好。只有当你告诉算法卧室数量、楼层数和房龄时,它才拥有足够的信息在训练集上表现得更好。

4. 增加多项式特征
这有点类似于增加额外特征。如果你的线性函数(直线)不能很好地拟合训练集,那么增加多项式特征可以帮助你在训练集上做得更好。帮助你在训练集上做得更好是解决高偏差问题的一种方法。

5. 减小正则化参数 λ
这意味着我们将减少对正则化项的重视,更多地关注拟合训练数据。这再次帮助你解决高偏差问题。

6. 增大正则化参数 λ
这与上一项相反。如果你过拟合了数据,增大 λ 是合理的,因为它迫使算法拟合一个更平滑、波动更小的函数。这用于解决高方差问题。


核心策略总结

我知道本节内容信息量很大,但希望你能掌握以下要点:

  • 如果你发现算法存在高方差,那么两个主要的解决方法是:

    1. 获取更多训练数据。
    2. 简化你的模型。
      • 简化模型意味着:获取更小的特征集,或增大正则化参数 λ,从而降低算法拟合非常复杂、波动剧烈曲线的灵活性。
  • 相反,如果你的算法存在高偏差,这意味着即使在训练集上表现也不佳。在这种情况下,主要的解决方法是使你的模型更强大,或赋予它更多灵活性以拟合更复杂或波动更大的函数。实现这一点的一些方法是:

    1. 提供额外特征。
    2. 添加多项式特征。
    3. 减小正则化参数 λ。

顺便提一下,如果你想知道是否可以通过减少训练集大小来解决高偏差问题,这实际上没有帮助。减少训练集大小可能会让你更好地拟合训练集,但这往往会恶化你的交叉验证误差和算法性能。因此,不要为了试图解决高偏差问题而随意丢弃训练样本。


持续实践与神经网络应用

我的一位斯坦福博士生在毕业多年后曾对我说,他在斯坦福学习时了解了偏差和方差,觉得自己懂了。但在多家公司工作多年后,他意识到偏差和方差是那种“短时间内学会,却需要一生去掌握”的概念之一。我认为偏差和方差是一个非常强大的思想。当我训练学习算法时,我几乎总是试图弄清楚它是存在偏差问题还是方差问题,而系统性地解决它的方法,我认为需要通过反复实践才能不断进步。

你会发现,理解这些概念将极大地帮助你在开发学习算法时,更有效地决定下一步该尝试什么。

我知道本节内容很多,如果你觉得信息量太大,没关系,不用担心。本周后续的实践练习和测验中,将提供更多机会来复习这些概念,让你能通过思考不同学习算法的偏差和方差来获得额外练习。所以,如果现在觉得内容很多,没关系,你将在本周晚些时候练习这些概念,并有望在那时加深对它们的理解。

在继续之前,偏差和方差在思考如何训练神经网络时也非常有用。因此,在下一节中,让我们看看这些概念如何应用于神经网络训练。让我们进入下一节。


本节课中,我们一起学习了如何通过诊断高偏差或高方差问题,来有针对性地选择改进学习算法性能的措施。我们明确了六种常见改进方法各自适用的场景,并总结了解决高方差和高偏差问题的核心策略。掌握这一诊断与决策框架,是高效进行机器学习模型迭代优化的关键。

83:偏差、方差与神经网络 🧠

在本节课中,我们将学习偏差和方差的概念,并探讨神经网络如何通过其独特的性质,为我们提供一种新的方法来处理这两个问题,从而提升算法性能。


偏差与方差的权衡

上一节我们介绍了模型性能评估的基本概念,本节中我们来看看偏差和方差如何影响学习算法的表现。

高偏差和高方差都会损害算法的性能。在神经网络兴起之前,机器学习工程师经常讨论偏差-方差权衡。这意味着你必须在模型复杂度(例如多项式次数或正则化参数 λ)之间取得平衡,以确保偏差和方差都不会过高。

以下是偏差与方差权衡的直观理解:

  • 高偏差(欠拟合):模型过于简单,无法捕捉数据中的潜在模式。例如,用线性模型拟合非线性数据。
  • 高方差(过拟合):模型过于复杂,对训练数据中的噪声也进行了学习,导致在新数据上泛化能力差。例如,用一个非常高阶的多项式拟合数据。

传统方法需要在模型复杂度上做出取舍,以找到一个使验证集误差最低的“最佳点”。


神经网络:一种新的范式

然而,神经网络,特别是与大数据结合时,为我们提供了一种新的思路,可以在一定程度上摆脱这种严格的权衡困境。

事实证明,大型神经网络在中小型数据集上训练时,通常是低偏差机器。这意味着,只要你的神经网络足够大,你几乎总是可以很好地拟合训练集(前提是训练集不是特别巨大)。

这为我们提供了一种新的“配方”,可以根据需要分别减少偏差或方差,而不必在两者之间进行艰难的权衡。


神经网络开发实用指南

以下是一个简单但强大的实用指南,用于使用神经网络开发高精度模型:

  1. 首先,在训练集上训练你的算法,并计算训练误差 J_train
  2. 评估是否存在高偏差问题:检查算法在训练集上的表现是否良好。如果 J_train 很高(例如,相对于人类水平或某个基准性能),则存在高偏差问题。
    • 解决方案:使用更大的神经网络(增加隐藏层数或每层的隐藏单元数)。不断增大网络规模,直到它在训练集上达到可接受的性能水平。
  3. 然后,评估是否存在高方差问题:在算法很好地拟合训练集后,检查它在交叉验证集上的表现。如果 J_CV 远高于 J_train,则存在高方差问题。
    • 解决方案:获取更多训练数据。然后返回步骤1重新训练模型,并再次检查偏差和方差。

你可以循环执行此过程,直到模型在交叉验证集上表现良好为止。需要注意的是,在实际开发中,你可能需要在“增大网络”和“收集数据”之间来回切换,因为随着算法的调整,你面临的主要问题(偏差或方差)可能会发生变化。


关于大型神经网络的常见问题

人们常问:“如果我的神经网络太大,会不会导致高方差(过拟合)问题?”

答案是:一个经过适当正则化的大型神经网络,其性能通常至少与小型神经网络一样好,甚至更好。

用代码表示,在 TensorFlow 中为一个全连接层添加 L2 正则化的方式如下:

# 无正则化的层
# tf.keras.layers.Dense(units=256, activation='relu')

# 添加 L2 正则化的层
tf.keras.layers.Dense(units=256, activation='relu',
                      kernel_regularizer=tf.keras.regularizers.l2(0.01))

因此,只要进行适当的正则化,使用更大的神经网络几乎总是有益的。主要的代价是计算成本会增加,可能会减慢训练和推理速度。

神经网络的正则化成本函数公式如下,与线性回归和逻辑回归类似:

J_reg = J + (λ / 2m) * Σ ||w||^2

其中,J 是原始成本函数(如均方误差或逻辑损失),求和项 Σ ||w||^2 是对网络中所有权重参数的平方和。


总结与核心要点

本节课中我们一起学习了偏差、方差的概念,以及神经网络如何改变我们处理这些问题的方式。

两个核心要点是:

  1. 只要进行适当的正则化,使用更大的神经网络几乎总是无害的(主要代价是计算速度)。
  2. 只要训练集不是特别巨大,神经网络(尤其是大型神经网络)通常是一个低偏差机器,它能很好地拟合复杂函数。这就是为什么在训练足够大的神经网络时,我们更常需要解决方差问题,而非偏差问题。

深度学习的发展确实改变了机器学习从业者对偏差和方差的思考方式。尽管如此,在训练神经网络时,测量偏差和方差并以此指导后续步骤,仍然是一个非常有效的方法。

84:机器学习开发的迭代循环 🔄

在本节课中,我们将学习开发机器学习系统的完整过程,特别是其核心的迭代循环。通过了解这个过程,你将能够在机器学习开发的各个阶段做出更明智的决策。

机器学习开发的迭代循环

上一节我们介绍了课程目标,本节中我们来看看机器学习开发的核心模式——迭代循环。

开发一个机器学习模型通常遵循一个循环往复的过程。首先,你需要决定系统的整体架构。这意味着选择你的机器学习模型,决定使用哪些数据,可能还需要选择超参数等。

给定这些决策后,你将实现并训练一个模型。正如之前提到的,当你第一次训练一个模型时,它几乎永远不会达到你期望的效果。

我建议的下一步是实施或查看一些诊断方法。例如,查看算法的偏差和方差,或者我们将在下一节视频中看到的“误差分析”。

基于诊断得出的见解,你可以做出决策,例如:是否应该让你的神经网络变得更大?是否应该改变Lambda正则化参数?或者是否应该添加更多数据、增加更多特征、或减少一些特征?

然后,你带着新的架构选择再次进入这个循环。通常需要多次迭代才能达到你想要的性能。

构建垃圾邮件分类器的例子

为了更好地理解这个循环,让我们看一个构建垃圾邮件分类器的具体例子。许多人都非常讨厌垃圾邮件,这也是我多年前研究过的一个问题。

左边的例子是一个典型的垃圾邮件,其中可能包含故意拼错的单词,如“watches”、“medicine”和“mortgages”,以试图绕过垃圾邮件识别器。相比之下,右边的邮件是我弟弟Alfred发来的关于圣诞节聚会的真实邮件。

那么,如何构建一个分类器来识别垃圾邮件和非垃圾邮件呢?

构建分类器的方法

以下是构建文本分类器的一种方法:

  • 模型选择:训练一个监督学习算法。输入特征X是邮件的特征,输出标签Y是1或0,分别代表是垃圾邮件或不是垃圾邮件。
  • 特征工程:一种构造邮件特征的方法是,选取英语(或其他词典)中的前10,000个单词,并用它们来定义特征x1, x2, ..., x10000
    • 例如,给定右边的邮件,如果我们的单词列表是[a, Andrew, buy, deal, discount, ...],那么我们会根据单词是否出现在邮件中,将这些特征设置为0或1。
    • 另一种方法是让这些数字不仅仅是0或1,而是计算该单词在邮件中出现的次数。
  • 算法训练:给定这些特征,你可以训练一个分类算法,例如逻辑回归模型或神经网络,来根据特征X预测Y

改进算法的思路

在你训练了初始模型后,如果它的表现不如预期,你很可能会产生多个改进学习算法性能的想法。

以下是一些可能的改进方向:

  • 收集更多数据:例如,通过“蜜罐”项目创建大量虚假电子邮件地址,故意让垃圾邮件发送者获取,从而收集大量已知的垃圾邮件数据。
  • 开发更复杂的特征:例如,基于电子邮件路由信息(即邮件在到达你之前经过的服务器序列)开发特征。邮件头中的路径信息有时能帮助判断其是否为垃圾邮件。
  • 优化文本特征:从邮件正文中提取更复杂的特征。例如,在之前提到的特征中,“discounting”和“discount”可能被视为不同的单词,但也许它们应该被视为同一个词。
  • 检测拼写错误:设计算法来检测故意拼错的单词(如“watches”),这可能有助于判断邮件是否为垃圾邮件。

如何选择改进方向

面对所有这些甚至更多的想法,你如何决定哪些更有前景呢?选择更有前景的方向,可以轻松地将你的项目速度提升10倍,相比选择一些不那么有希望的方向。

例如,我们已经知道,如果你的算法存在高偏差而非高方差,那么花数月时间在蜜罐项目上可能不是最有效的方向。但如果你的算法存在高方差,那么收集更多数据可能会大有帮助。

因此,在机器学习开发的迭代循环中,你可能会有很多关于如何修改模型或数据的想法。而提出不同的诊断方法,可以为你提供大量指导,告诉你哪些模型、数据或架构部分的选择最有希望尝试。

在之前的几节视频中,我们已经讨论了偏差和方差。在下一节视频中,我将开始向你介绍误差分析过程,这是另一套关键思想,用于深入了解哪些架构选择可能富有成效。

总结

本节课中我们一起学习了机器学习开发的迭代循环。我们了解到,开发过程是一个“决定架构 -> 实现训练 -> 诊断分析 -> 调整改进”的循环。通过垃圾邮件分类器的例子,我们看到了在模型表现不佳时,存在多种可能的改进路径(如增加数据、优化特征),而有效的诊断(如偏差/方差分析、误差分析)是选择正确改进方向的关键。下一节,我们将深入探讨误差分析的具体方法。

85:误差分析 🧐

在本节课中,我们将学习如何通过误差分析来诊断和改进机器学习算法。误差分析是继偏差-方差分析之后,第二重要的诊断工具。它能帮助你理解算法在哪些地方出错,并指导你优先处理哪些改进措施。

上一节我们介绍了偏差-方差分析,它帮助你判断模型是欠拟合还是过拟合。本节中我们来看看误差分析,它能告诉你模型具体在哪些类型的例子上犯错。

什么是误差分析?

误差分析指的是手动检查算法分类错误的样本,并尝试找出这些错误样本的共同模式或属性。

假设你的交叉验证集有500个样本(MCV = 500),其中算法错误分类了100个。

误差分析的过程就是仔细查看这100个被错误分类的样本。

如何进行误差分析?

以下是进行误差分析的具体步骤:

首先,你需要找出算法错误分类的样本,并尝试将它们按共同特征分组。

例如,在垃圾邮件分类任务中,你可能会发现许多被错误分类的垃圾邮件是推销药品的。

接下来,你可以手动统计这些类别的数量。例如,统计有多少封错误分类的邮件是药品推销邮件。假设你发现有21封。

同样,如果你怀疑故意拼写错误会影响分类器,你也可以统计有多少错误分类的样本包含故意拼写错误。假设你发现100个错误中有3个。

继续检查,你可能还会发现:

  • 7封邮件有异常的邮件路由信息。
  • 18封邮件是试图窃取密码的网络钓鱼邮件。

有时,垃圾邮件发送者会将垃圾信息写在图片里,而不是邮件正文中,这使得算法更难识别。这类邮件可以归类为“图片内嵌垃圾邮件”。

经过统计,你可能会得到类似下面的表格:

如何解读分析结果?

这个表格告诉你,药品推销垃圾邮件钓鱼邮件是主要问题,而故意拼写错误虽然存在,但影响较小。

具体来说,这个分析表明,即使你开发一个非常复杂的算法来检测故意拼写错误,也只能解决100个错误中的3个。因此,其净收益可能并不大。这并不意味着不值得做,但当你在确定工作优先级时,可能不会将其放在首位。

我之所以讲这个故事,是因为我曾经花了很多时间构建检测故意拼写错误的算法,后来才发现其整体影响其实很小。这是一个我希望在投入大量时间之前能进行更仔细误差分析的例子。

关于误差分析的注意事项

关于这个过程,有几点需要注意:

  1. 类别可以重叠:这些分类并不是互斥的。例如,一封药品推销垃圾邮件可能同时具有异常的路由信息;一封钓鱼邮件可能同时包含故意拼写错误。因此,一封邮件可以被计入多个类别。
  2. 处理大量错误样本:如果你的交叉验证集很大,例如有5000个样本,算法错误分类了1000个,你可能没有时间手动检查所有错误样本。在这种情况下,我通常会随机抽取一个子集(大约100到200个样本)进行检查。希望在合理时间内检查大约100个样本,能为你提供足够的统计数据,了解最常见的错误类型,从而找到最值得关注的方向。

误差分析如何指导后续行动?

完成分析后,如果你发现很多错误是药品推销垃圾邮件,这可能会给你一些改进的灵感。

例如:

  • 收集更多特定数据:你可能决定收集更多药品推销垃圾邮件的数据,而不是所有类型的数据,以帮助算法更好地识别这类邮件。
  • 设计新特征:你可能决定设计一些与特定药品名称相关的新特征,以帮助算法更好地识别这类垃圾邮件。

同样,对于钓鱼邮件,这个分析可能会启发你:

  • 查看邮件中的URL,并编写特殊代码来生成额外特征,以检测是否链接到可疑URL。
  • 专门收集更多钓鱼邮件的数据来帮助算法。

误差分析的意义在于,通过手动检查算法错误分类的样本,常常能激发灵感,知道尝试什么改进可能有用。同时,它也能告诉你某些类型的错误非常罕见,不值得花费大量时间去修复。

总结与关联

回顾我们的改进清单:

  • 偏差-方差分析告诉你收集更多数据是否有帮助。
  • 基于我们刚才的误差分析,看起来:
    • 更复杂的邮件特征可能有点帮助。
    • 专门检测药品推销或钓鱼邮件的更复杂特征可能会很有帮助。
    • 而检测拼写错误的帮助则不会那么大。

总的来说,我发现偏差-方差诊断这种形式的误差分析对于筛选或决定尝试哪些模型更改更有希望,都非常有帮助。

误差分析的局限性

误差分析的一个局限性是,它对于人类擅长的任务更容易进行。例如,你可以查看一封邮件并判断它为什么是垃圾邮件,以及算法为什么出错。

对于人类也不擅长的任务,误差分析会困难一些。例如,预测用户会在网站上点击什么,我无法预测,因此那里的误差分析往往更加困难。

但是,当你将误差分析应用于你能理解的问题时,它对于将你的注意力集中在更有希望的尝试方向上极为有用,这反过来可以轻松为你节省数月徒劳无功的工作时间。

本节课中我们一起学习了误差分析的方法、步骤和意义。它通过手动检查错误样本,帮助我们定位算法的主要问题所在,并指导我们优先处理最有效的改进措施,从而避免在低收益的工作上浪费精力。

在下一个视频中,我们将更深入地探讨为学习算法添加数据的问题。有时你判断存在高方差,并希望为其获取更多数据,而有一些技术可以使你添加数据的方式更加高效。让我们来看看这些方法,希望你能掌握一些为学习应用获取更多数据的好方法。

86:增加数据的方法 📈

在本节课中,我们将学习如何为机器学习应用增加、获取甚至创造更多数据。我们将探讨几种实用的技术,包括有针对性的数据收集、数据增强和数据合成。这些方法能帮助你更有效地提升模型性能,而不必总是盲目地收集所有类型的数据。


1. 有针对性的数据收集 🎯

上一节我们介绍了增加数据的重要性。本节中我们来看看如何更高效地增加数据。

通常,我们总是希望拥有更多数据。但试图获取所有类型的数据可能既缓慢又昂贵。一个替代方法是,根据误差分析的结果,有针对性地增加特定类型的数据。

例如,如果误差分析显示“药品垃圾邮件”是一个主要问题,那么你可以集中精力获取更多药品垃圾邮件的例子,而不是获取所有类型的邮件。这样能以更低的成本,帮助你的学习算法更聪明地识别这类垃圾邮件。

以下是具体操作步骤:

  1. 如果你有大量未标记的电子邮件数据。
  2. 可以要求标注人员快速浏览这些未标记数据,专门找出更多与药品相关的垃圾邮件例子。
  3. 与盲目增加所有类型邮件数据相比,这种方法能更有效地提升算法性能。

我希望你从这个例子中得到的通用模式是:虽然增加所有类型的数据没有错,但如果误差分析表明算法在某些特定数据子集上表现不佳,那么有针对性地增加这些类型的数据,可能是更高效的改进方式。


2. 数据增强技术 🔄

除了获取全新的训练样本 (X, Y),还有一种技术可以显著增加训练集大小,尤其是在图像和音频数据上,这种技术叫做数据增强

数据增强的做法是,对一个现有的训练样本进行修改,以创建一个新的训练样本。

图像数据增强示例

假设你正在构建一个OCR(光学字符识别)系统,用于识别A到Z的字母。给定一张字母“A”的图像,你可以通过以下方式创建新的训练样本:

  • 将图像旋转一定角度。
  • 放大或缩小图像。
  • 改变图像的对比度。

这些图像扭曲操作并不会改变“这是字母A”的事实。对于某些字母(并非所有),你还可以使用镜像。通过这种方式,你告诉算法:旋转、放大或缩小一点的字母A,仍然是字母A。创建这样的额外示例有助于学习算法更好地学习如何识别字母。

一个更高级的数据增强例子是,在字母A上放置一个网格,并引入网格的随机扭曲,从而创建出更丰富的字母A示例库。这个过程可以将一张图像变成一个训练示例,帮助算法更稳健地学习“什么是字母A”。

音频数据增强示例

这个想法同样适用于语音识别。假设你有一个原始音频片段:“What is today's weather?”。你可以通过以下方式进行数据增强:

  1. 取一段嘈杂的背景音频(如人群声音)。
  2. 将原始音频与背景噪音叠加,生成一个听起来像在嘈杂人群中说话的音频片段。
  3. 同样,可以叠加汽车噪音,生成听起来像在车里说话的音频片段。
  4. 还可以模拟糟糕的手机连接效果。

这样,你就可以将一个音频片段变成三个训练示例。在我从事语音识别系统工作时,这确实是人工增加训练数据规模、构建更准确识别器的关键技术。

数据增强的关键提示

数据增强的一个关键点是,你对数据所做的更改或扭曲,应该能代表测试集中可能出现的噪声或扭曲类型。

例如,扭曲字母A的图像,应该看起来像你在实际中可能希望识别的字母。为音频添加背景噪音或糟糕的手机连接效果,如果这能代表你预期在测试集中听到的情况,那么就是有益的增强方式。

相反,向数据添加纯粹随机、无意义的噪声通常帮助不大。例如,给字母A图像的每个像素添加随机噪声,生成的图像可能并不代表测试集中的常见情况,因此可能不那么有帮助。

你可以这样思考数据增强:如何以一种使生成的数据仍然与测试集数据非常相似的方式,来修改、扭曲或增加数据的噪声?因为学习算法最终需要在这些数据上表现良好。


3. 数据合成技术 🧪

数据增强是对现有训练样本进行修改以创建新样本,而另一种技术是数据合成,即从头开始创造全新的样本,而不是修改现有样本。

以照片OCR(照片光学字符识别)任务为例,该任务旨在让计算机自动读取图像中的文字。训练一个OCR算法来识别图像中的文字,一个关键步骤是能够识别小图像中心的字母。

为这项任务创建人工数据的一种方法是:利用计算机文本编辑器中的多种字体,打出随机文本,并使用不同的颜色、对比度和字体进行截图,从而生成如右图所示的合成数据。左边的图像是来自真实世界的真实数据,右边的图像是使用计算机字体合成的,看起来相当逼真。

通过这样的合成数据生成,你可以为照片OCR任务生成大量图像或示例。为特定应用编写代码以生成逼真的合成数据可能需要大量工作,但当你花时间做到这一点时,它有时能帮助你为应用生成大量数据,并大幅提升算法的性能。

数据合成技术主要用于计算机视觉任务,在其他应用(如音频)中使用较少。


4. 以数据为中心的开发方法 💡

本节课中介绍的所有技术都涉及如何设计系统所使用的数据。在机器学习过去几十年的发展中,大多数研究者的注意力都集中在传统的以模型为中心的方法上。

一个机器学习或AI系统既包含实现算法或模型的代码,也包含用于训练算法的数据。过去几十年,大多数机器学习研究者会下载数据集并固定数据,同时专注于改进算法或模型的代码。

得益于这种研究范式,我们今天拥有的算法(如线性回归、逻辑回归、神经网络以及下周将看到的决策树)已经非常优秀,能在许多应用中良好工作。

因此,有时将更多时间花在以数据为中心的方法上可能更有成效。这种方法专注于设计算法所使用的数据,可以包括:

  • 收集更多数据。
  • 根据误差分析结果,专门收集更多特定类型(如药品垃圾邮件)的数据。
  • 使用数据增强生成更多图像或音频。
  • 使用数据合成创建更多训练示例。

有时,这种对数据的关注可能是帮助学习算法提升性能的有效途径。希望本视频为你提供了一套工具,能高效地为学习算法增加更多数据,使其工作得更好。


总结 ✨

本节课中我们一起学习了为机器学习应用增加数据的几种核心方法:

  1. 有针对性的数据收集:基于误差分析,高效增加特定薄弱环节的数据。
  2. 数据增强:通过对现有样本(如图像旋转、音频加噪)进行合理变形来扩充数据集。
  3. 数据合成:从头创造全新的、逼真的训练样本,尤其在计算机视觉任务中有效。

我们还探讨了从以模型为中心转向以数据为中心的开发思路,这往往是提升算法性能的更高效途径。当然,有些应用确实难以获得更多数据,在下一节课中,我们将学习一种名为“迁移学习”的强大技术,它可以在数据有限的情况下,利用其他相关任务的数据来大幅提升你的算法性能。

87:迁移学习 🚀

在本节课中,我们将要学习一种名为“迁移学习”的强大技术。当你的应用缺乏足够数据时,迁移学习允许你利用来自其他任务的数据来提升模型性能。这是一种非常实用的方法。

概述

迁移学习的核心思想是:将一个在大规模数据集上训练好的模型(或其部分参数)应用到另一个相关但数据量较小的任务上。这能有效解决数据不足的问题,并加速模型训练。


迁移学习的工作原理

上一节我们介绍了迁移学习的基本概念,本节中我们来看看它的具体工作流程。

假设你想构建一个识别手写数字0到9的模型,但缺乏足够的手写数字标注数据。你可以按以下步骤操作:

  1. 寻找并利用大型数据集:首先,找到一个包含100万张图片的大型数据集,这些图片涵盖了猫、狗、汽车、人物等1000个不同类别。

  2. 预训练基础模型:在这个大型数据集上训练一个神经网络。该网络以图像X作为输入,学习识别这1000个类别。训练完成后,你会得到网络各层的参数:W1, b1, W2, b2, W3, b3, W4, b4, W5, b5

  3. 复制并修改网络结构:为了应用迁移学习,复制这个训练好的神经网络。保留前四层的参数(W1, b1W4, b4),但移除原来的顶层(输出层)

  4. 构建新的输出层:替换为一个新的、更小的输出层。对于手写数字识别任务,新输出层应有10个输出单元(对应数字0-9),而不是原来的1000个。因此,顶层的参数W5, b5需要重新初始化并训练,因为维度发生了变化。

  5. 参数初始化与微调:使用从预训练模型中获得的前四层参数作为新网络的初始值。然后,在你的手写数字小数据集上,运行优化算法(如梯度下降)来训练网络。


微调策略

在将预训练模型应用到新任务时,有两种主要的微调策略。以下是具体说明:

选项一:仅训练顶层参数

  • 保持从预训练模型中获得的前四层参数(W1, b1W4, b4)固定不变。
  • 只训练新添加的顶层参数(W5, b5)。

选项二:训练所有参数

  • 使用预训练模型的前四层参数作为初始值。
  • 然后,在你的数据集上训练所有层的参数,包括W1, b1W5, b5

选择建议

  • 如果你的训练集非常小,选项一可能效果更好,因为它能有效防止过拟合。
  • 如果你的训练集稍大一些,选项二可能带来更好的性能,因为它允许模型根据新任务调整所有特征。

为什么迁移学习有效? 🧠

你可能会疑惑,识别猫、狗、汽车学到的参数,怎么能帮助识别手写数字呢?这背后的直觉在于图像特征的层次性。

一个训练用于物体检测的神经网络,其各层学习到的特征具有通用性:

  • 第一层:通常学习检测边缘等低级特征。
  • 第二层:将边缘组合起来,学习检测角点和简单形状。
  • 更高层:组合简单形状,学习检测更复杂的通用形状(如曲线、基本部件)。

因此,通过在大规模图像数据集上预训练,模型学会了提取图像的通用低级和中级特征(如边缘、角点、形状)。这些特征对于许多计算机视觉任务(包括手写数字识别)都是有用的基础。


重要限制与前提

迁移学习并非万能,它有一个关键限制:

预训练和微调步骤的输入类型X必须相同

  • 如果你的最终任务是计算机视觉(处理图像),那么预训练模型也必须在图像数据上训练。
  • 如果你的任务是语音识别(处理音频),那么预训练模型应该在音频数据上训练。
  • 对于文本任务同理,预训练模型应基于文本数据

简而言之,特征空间需要对齐。


实践步骤与社区资源

在实践中,迁移学习通常包含两个步骤:

第一步:获取预训练模型

  • 从互联网下载一个在大型数据集上预训练好的神经网络及其参数。该网络的输入类型需与你的应用相同(如图像、音频、文本)。
  • 或者,你也可以自己训练一个大型模型,但这通常更耗时耗力。

第二步:在你的数据上微调

  • 将下载的预训练模型的顶层替换为适合你任务的新输出层。
  • 使用你自己的(通常较小的)数据集,采用上述选项一或选项二进行微调。

利用社区力量
机器学习社区的一个巨大优势是开源与共享。许多研究人员会将自己花费数周训练的大型预训练模型(如基于ImageNet的图像模型、GPT/BERT等文本模型)公开发布。你可以直接下载这些模型作为起点,这极大地降低了应用先进技术的门槛,让每个人都能在巨人的肩膀上快速构建有效的模型。


总结

本节课中我们一起学习了迁移学习技术。我们了解到:

  1. 迁移学习通过利用在大规模任务上学到的知识,来帮助解决数据稀缺的小规模任务。
  2. 其核心步骤是预训练微调
  3. 微调时可以选择冻结部分层更新所有权重
  4. 它有效的关键在于神经网络学习的底层特征具有通用性
  5. 其应用前提是输入数据类型必须一致
  6. 积极利用社区共享的预训练模型可以极大提升开发效率。

迁移学习是机器学习社区协作精神的完美体现,通过共享代码、模型和参数,我们共同推动了整个领域的发展。希望未来你也能为这个社区贡献自己的力量。

这就是关于迁移学习的全部内容。在下一个视频中,我们将探讨一个完整的机器学习项目周期所涉及的各个步骤。

88:机器学习项目完整周期 🚀

在本节课中,我们将学习一个机器学习项目从开始到部署及维护的完整生命周期。我们将以语音识别项目为例,详细讲解每个关键步骤,并了解在构建实际、有价值的机器学习系统时需要考虑的各个方面。


项目范围界定 🎯

首先,机器学习项目的第一步是界定项目范围。这意味着需要决定项目的具体内容和目标。例如,我曾决定开发一个用于语音搜索的项目,即让用户通过手机语音而非打字来进行网络搜索。这就是项目范围界定。

数据收集 📊

在决定了项目内容后,下一步是收集数据。你需要确定训练机器学习系统所需的数据类型,并着手获取音频数据及其对应的文本转录或标签。这就是数据收集阶段。

模型训练与迭代 🔄

在完成初步的数据收集后,便可以开始训练模型。例如,训练一个语音识别系统,并进行错误分析以持续改进模型。在训练模型和进行错误分析或偏差-方差分析后,常常会发现需要返回去收集更多数据。这可能意味着收集更多通用数据,或者根据错误分析的结果,专门收集某一特定类型的数据来提升算法性能。

例如,在开发语音识别系统时,我发现当背景中有汽车噪音时,模型表现很差。因此,我决定通过数据增强技术,获取更多听起来像在汽车环境中的语音数据,以提升算法性能。

你会多次经历这个循环:训练模型 -> 进行错误分析 -> 返回收集更多数据。这个过程会持续一段时间,直到你认为模型足够好,可以部署到生产环境中。

部署与监控 🚀

部署意味着让用户能够使用你的系统。部署系统后,必须持续监控其性能,并在性能下降时进行维护以恢复其表现。仅仅将机器学习模型放在服务器上是不够的。

有时在部署后,你会发现系统表现不如预期,这时可能需要返回重新训练模型,甚至收集更多数据。实际上,如果获得用户许可,从生产部署中收集的数据可以为你提供更多资源,用于持续提升系统性能。

部署细节:推理服务器与API

在训练出一个高性能的机器学习模型(如语音识别模型)后,常见的部署方式是将模型实现在一个服务器上,我们称之为推理服务器。它的职责是调用你训练好的模型来进行预测。

如果你的团队开发了一个移动应用(例如搜索应用),当用户对着应用说话时,应用可以通过API调用,将录制的音频片段传递给推理服务器。推理服务器的任务是将音频输入机器学习模型,并返回模型的预测结果,在这个例子中就是语音对应的文本转录。

这是一种常见的实现模式:应用通过API调用推理服务器,服务器根据输入 X 反复进行预测。用公式表示这个过程就是:Y_hat = model(X)

实现这一过程可能需要一些软件工程工作,来编写所有相关的代码。根据你的应用是需要服务少数用户还是数百万用户,所需的软件工程工作量会有很大不同。

系统监控与维护 🛠️

通常,你需要记录获取的数据,包括输入 X 和预测结果 Y_hat(前提是用户隐私和许可允许存储这些数据)。这些数据对于系统监控非常有用。

例如,我曾基于某个数据集构建了一个语音识别系统。但当有新的名人突然走红,或选举产生了新的政治家时,人们会搜索这些不在训练集中的新名字,导致我的系统表现不佳。正是通过监控系统,我们才能发现数据分布发生了变化,算法准确性在下降,从而促使我们重新训练模型并进行更新。

部署过程可能需要一定量的软件工程工作。对于某些只在个人电脑或一两个服务器上运行的应用,可能不需要太多运维工作。根据团队分工,可能由你构建机器学习模型,而由另一个团队负责部署。

MLOps:机器学习运维

机器学习领域正在兴起一个名为 MLOps 的领域,它代表机器学习运维。MLOps 指的是如何系统化地构建、部署和维护机器学习系统的一系列实践,以确保你的机器学习模型可靠、可扩展、有良好的日志记录、受到监控,并能在适当时机进行更新以保持良好的运行状态。

例如,如果你要将系统部署给数百万用户,你可能需要确保实现高度优化,以控制服务大量用户的计算成本不至于过高。

总结 📝

在本节课中,我们一起学习了机器学习项目的完整生命周期。我们从项目范围界定开始,经历了数据收集模型训练与迭代,最终到达部署与监控阶段。我们了解到,训练一个高性能模型是核心,但要将系统成功部署给用户,还需要考虑软件工程、系统监控、性能维护以及 MLOps 实践。最后,我们提到了构建机器学习系统时需要考虑的伦理问题,这将是许多应用中的关键议题。

在接下来的课程中,我们将继续探讨与机器学习系统伦理相关的重要理念。

89:公平性、偏见与伦理 ⚖️

在本节课中,我们将要学习机器学习中的公平性、偏见与伦理问题。机器学习算法如今影响着数十亿人,确保我们构建的系统公平、无偏见且符合伦理至关重要。我们将探讨相关案例,并学习一些实用的指导原则。

公平性与偏见问题

上一节我们介绍了课程主题,本节中我们来看看机器学习历史上一些因偏见而导致严重后果的案例。

不幸的是,在机器学习的历史中,确实出现过一些系统,其中一些被广泛报道,表现出完全不可接受的偏见水平。

例如,曾有一个招聘工具被证明歧视女性。虽然开发该系统的公司后来停止使用它,但人们更希望该系统从一开始就没有被部署。

还有一个有据可查的例子,人脸识别系统将深色皮肤个体与罪犯照片匹配的频率远高于浅色皮肤个体。这显然是不可接受的,我们整个社区应该做得更好,从一开始就不构建和部署存在此类问题的系统。

有些系统在批准银行贷款时存在偏见,歧视某些亚群体。我们也非常希望学习算法不会产生强化负面刻板印象的毒害效应。例如,如果我女儿在网上搜索某些职业,却看不到任何像她一样的人,我不希望这会阻碍她从事某些职业。

机器学习的负面用例

除了对个人的偏见和公平待遇问题,机器学习也存在一些不利的用例或负面用例。

例如,有一个被广泛引用和观看的视频,由BuzzFeed公司完全透明地发布,内容是前美国总统巴拉克·奥巴马的深度伪造视频。如果你愿意,可以在网上找到并观看整个视频。但创建此视频的公司是出于完全透明和充分披露的目的。显然,未经同意和披露就使用此技术生成虚假视频是不道德的。

不幸的是,我们也看到社交媒体有时会传播有毒或煽动性言论。因为优化用户参与度导致了算法这样做。

有些机器人被用来生成虚假内容,无论是出于商业目的(如在产品上发布虚假评论)还是政治目的。

机器学习也被用于构建有害产品、实施欺诈等。在机器学习的某些领域,就像电子邮件一样,垃圾邮件发送者和反垃圾邮件社区之间一直存在斗争。例如,我今天在金融行业看到,试图实施欺诈的人和打击欺诈的人之间也存在斗争。

不幸的是,机器学习被一些欺诈者和一些标准制定者所使用。因此,为了社会的利益,请不要构建对社会有负面影响的机器学习系统。如果你被要求从事你认为不道德的应用工作,我敦促你离开。就我而言,我曾多次审视一些项目,它们似乎财务上可行,能为公司赚钱,但我基于不道德的理由终止了这些项目。因为我认为,即使财务上可行,它也会让世界变得更糟,我永远不想参与这样的项目。

伦理的复杂性与指导原则

伦理是一个非常复杂和丰富的主题,人类已经研究了至少几千年。当人工智能变得更加普及时,我实际上去阅读了多本哲学和伦理学书籍,因为我曾天真地希望,如果能有一个包含五件事项的清单,我们做了这五件事就能符合伦理。但我失败了,我认为没有人能够提出一个简单的待办事项清单,来提供关于如何符合伦理的具体指导。

因此,我希望与大家分享的不是一个清单(因为我没能想出一个),而是一些通用的指导和建议,以确保工作偏见更少、更公平、更符合伦理。我希望这些相对通用的指导也能对你的工作有所帮助。

以下是使你的工作更公平、偏见更少、更符合伦理的一些建议。

部署前的风险评估

在部署一个可能造成伤害的系统之前,我通常会尝试组建一个多元化的团队,集思广益可能出错的事情,重点是可能对弱势群体造成的伤害。

在我的职业生涯中,我发现很多次,拥有一个更多元化的团队(我所说的多元化是指多个维度,从性别到种族,到文化,再到许多其他特质),实际上能使团队集体更善于提出可能出错的想法,并增加了我们在推出系统并给特定群体造成伤害之前识别问题并修复问题的几率。

行业标准与文献检索

除了进行多元化的头脑风暴,我还发现对你所在行业或特定应用领域的任何标准或指南进行文献搜索也很有用。

例如,在金融行业,已经开始建立关于系统(比如决定向谁批准贷款的系统)合理公平和无偏见意味着什么的标准。这些在不同领域仍在不断涌现的标准,可以根据你正在从事的工作为你的工作提供参考。

系统审计与性能测量

在识别出可能的问题后,我发现部署前根据这些已识别的可能伤害维度对系统进行审计很有用。你在上一个视频中看到了机器学习项目的完整周期,其中一个关键步骤通常是在你训练了一个模型之后、部署到生产环境之前,这是防止部署有问题东西的关键防线。如果团队已经集思广益,认为它可能对某些亚群体(如某些性别或种族)存在偏见,那么你可以命令系统测量性能,看看它是否真的对某些性别、种族或其他亚群体存在偏见,并确保在部署前识别并修复任何问题。

制定缓解与监控计划

最后,我发现制定一个缓解计划(如果适用的话)很有用。一个简单的缓解计划就是回退到我们知道相对公平的早期系统。即使在部署之后,也要持续监控伤害,以便在出现需要解决的问题时触发缓解计划并迅速采取行动。例如,所有优秀的自动驾驶汽车团队在将自动驾驶汽车上路之前,都制定了万一汽车发生事故时的缓解计划。这样,如果汽车发生事故,就已经有一个可以立即执行的缓解计划,而不是在事故发生后才手忙脚乱地想办法。

总结与展望

我已经参与了许多机器学习系统的工作,让我告诉你,伦理、公平和偏见问题需要认真对待,不能轻视或忽视。当然,有些项目的伦理影响比其他项目更严重。例如,如果我正在构建一个神经网络来决定如何烘焙我的咖啡豆,其伦理影响显然远小于你正在构建一个决定批准哪些银行贷款的系统,后者如果存在偏见会造成重大伤害。

但我希望我们所有从事机器学习工作的人能够不断进步,辩论这些问题,发现问题,在它们造成伤害之前修复它们,这样我们集体就可以避免机器学习世界以前犯过的一些错误。因为这很重要,我们构建的系统可以影响很多人。

以上就是关于开发机器学习系统过程的全部内容,祝贺你完成了本周的必修视频。本周我还有两个可选视频给你,关于处理倾斜数据集,即正负样本比例远非50:50的数据集。事实证明,处理这类机器学习应用需要一些特殊技术。所以,我希望在下个关于如何处理倾斜数据集的可选视频中见到你。

90:偏斜数据集的误差指标 📊

在本节课中,我们将要学习当处理类别分布极不均衡(即偏斜数据集)的分类问题时,为何传统的准确率指标会失效,以及如何采用更合适的评估指标——精确率与召回率。

偏斜数据集的问题 🤔

上一节我们介绍了分类问题的基本评估方法。本节中我们来看看当数据集中正负样本比例严重失衡时会出现什么问题。

假设你正在训练一个二元分类器,用于根据患者的化验数据检测一种罕见疾病。如果疾病存在,则 y = 1;否则 y = 0。假设你的模型在测试集上达到了 1% 的错误率,即 99% 的正确诊断率。这听起来是个很好的结果。

但如果这是一种罕见疾病,例如只有 0.5% 的患者真正患病,那么这个结果可能并不像听起来那么出色。具体来说,如果你写一个程序,总是输出 y = 0,这个极其简单的非学习算法将拥有 99.5% 的准确率(0.5% 的错误率)。

代码示例:

# 一个总是预测为负类的“笨”算法
def dumb_algorithm(features):
    return 0  # 总是预测 y = 0

因此,这个非常“笨”的算法(错误率 0.5%)竟然优于你的学习算法(错误率 1%)。但显然,一个总是说“没病”的诊断工具是毫无用处的。这意味着,仅凭 1% 的错误率,你无法判断结果的好坏。特别是当你有多个算法分别达到 99.5%、99.2%、99.6% 的准确率时,很难判断哪个算法最好,因为错误率最低的可能就是那个总是预测 y = 0 的无用算法。

精确率与召回率 📈

在偏斜数据集问题上,我们通常使用不同的误差指标,而不是仅仅依赖分类错误率,来评估学习算法的性能。具体来说,一对常用的指标是精确率和召回率。

为了评估算法在罕见类别(例如我们想检测的罕见疾病,y = 1)上的表现,构建一个混淆矩阵会很有用。混淆矩阵是一个 2x2 的表格。

以下是混淆矩阵的构成:

  • 横轴:实际类别(1 或 0)。
  • 纵轴:预测类别(1 或 0)。

通过统计验证集或测试集上的样本,我们可以填充这个矩阵。假设有 100 个验证样本,我们得到以下数据:

  • 真正例:实际为 1,预测为 1。例如:15 个。
  • 假正例:实际为 0,预测为 1。例如:5 个。
  • 假反例:实际为 1,预测为 0。例如:10 个。
  • 真反例:实际为 0,预测为 0。例如:70 个。

基于混淆矩阵,我们可以计算精确率和召回率。

精确率

精确率衡量的是:在所有我们预测为阳性的样本中,有多少是真正的阳性。

公式:

精确率 = 真正例 / (真正例 + 假正例)

在上述例子中,精确率 = 15 / (15 + 5) = 0.75。这意味着,在所有被算法诊断为患病的患者中,有 75% 的人确实患病。

召回率

召回率衡量的是:在所有实际为阳性的样本中,我们正确检测出了多少。

公式:

召回率 = 真正例 / (真正例 + 假反例)

在上述例子中,召回率 = 15 / (15 + 10) = 0.60。这意味着,在所有真正患病的患者中,算法成功找出了其中的 60%。

计算精确率和召回率有助于你判断算法是否有效。如果一个算法总是预测 y = 0,那么它的真正例数为 0,召回率将为 0。通常,精确率或召回率为 0 的算法不是有用的算法。

总结与展望 🎯

本节课中我们一起学习了偏斜数据集带来的评估挑战。我们了解到,传统的准确率指标在类别不平衡时可能产生误导。为此,我们引入了混淆矩阵、精确率和召回率这三个关键概念。精确率关注预测阳性的准确性,而召回率关注找出所有真实阳性的能力。通过确保这两个指标都保持在一个合理的较高水平,我们可以更有信心地认为学习算法是实用且有效的。

在下一节视频中,我们将探讨如何在精确率和召回率之间进行权衡,以优化学习算法的整体性能。

91:精确率与召回率的权衡 ⚖️

在本节课中,我们将学习机器学习中两个重要的评估指标——精确率与召回率,并探讨它们之间存在的权衡关系。我们将了解如何通过调整分类阈值来影响这两个指标,并学习一个结合它们的单一指标:F1分数。

精确率与召回率的定义 📊

上一节我们介绍了精确率与召回率的概念。本节中我们来看看它们的正式定义。

以下是精确率与召回率的计算公式:

  • 精确率:在所有被模型预测为正例的样本中,真正为正例的比例。
    • 公式精确率 = 真阳性 / (真阳性 + 假阳性)
  • 召回率:在所有实际为正例的样本中,被模型正确预测为正例的比例。
    • 公式召回率 = 真阳性 / (真阳性 + 假阴性)

分类阈值的影响 ⚙️

在理想情况下,我们希望模型同时拥有高精确率和高召回率。高精确率意味着当模型诊断一个病人患有罕见疾病时,该病人很可能确实患病,诊断是准确的。高召回率意味着如果一个病人确实患有该罕见疾病,模型很可能正确地识别出他们患病。

然而在实践中,精确率和召回率之间往往存在权衡。在本节中,我们将探讨这种权衡关系,以及如何选择一个合适的权衡点。

如果我们使用逻辑回归模型进行预测,模型会输出一个介于0和1之间的概率值。我们通常会将输出值与一个阈值(例如0.5)进行比较来做出预测。

以下是调整阈值对预测结果的影响:

  • 提高阈值(例如设为0.7或0.9):仅当模型非常有把握时,才预测为正例(y=1)。这会导致精确率提高(因为预测为正例时更可能是正确的),但召回率降低(因为会漏掉更多实际的正例)。
  • 降低阈值(例如设为0.3):只要模型认为有一定可能性,就预测为正例。这会导致召回率提高(因为能识别出更多实际的正例),但精确率降低(因为预测为正例时可能包含更多错误)。

通过选择不同的阈值,我们可以在精确率和召回率之间做出不同的权衡。对于大多数学习算法,都存在这样的权衡曲线。

如何选择阈值? 🎯

通过绘制不同阈值下的精确率-召回率曲线,我们可以尝试选择一个阈值,该阈值对应曲线上的一个点,能够平衡假阳性(误报)和假阴性(漏报)的成本,或者平衡高精确率和高召回率的好处。

需要注意的是,选择阈值通常不能通过交叉验证自动完成,因为这需要你根据具体应用场景来指定最佳的权衡点。在许多应用中,手动选择阈值来权衡精确率和召回率是最终的做法。

F1分数:结合精确率与召回率 🔢

如果我们想自动权衡精确率和召回率,而不是手动进行,可以使用一个称为 F1分数 的指标。它有时被用来自动结合精确率和召回率,以帮助你选择最佳值或两者之间的最佳权衡点。

使用精确率和召回率评估算法的一个挑战是,你现在使用两个不同的指标。如果你训练了三个不同的算法,它们的精确率和召回率数值可能难以直接比较。为了帮助你决定选择哪个算法,将精确率和召回率结合成一个单一的分数可能很有用。

以下是结合精确率与召回率的方法比较:

  • 简单平均(不推荐):直接计算精确率和召回率的算术平均值。这种方法效果不佳,因为它没有对较低的数值给予足够重视。
  • F1分数(推荐):这是结合精确率(P)和召回率(R)最常用的方法。F1分数是一种调和平均数,它对P和R中较低的值赋予更大的权重。其计算公式如下:
    • 公式F1 = 2 * (P * R) / (P + R)
    • 这等价于先计算1/P和1/R的平均值,再取倒数。

F1分数提供了一种在精确率和召回率之间进行权衡的方法。通过计算F1分数,我们可以更容易地比较不同算法的整体性能,并选择分数最高的那个。

总结 📝

本节课中我们一起学习了精确率与召回率之间的重要权衡关系。我们了解到,通过调整分类阈值,可以影响模型的精确率和召回率。此外,我们还介绍了F1分数,这是一个将精确率和召回率结合起来的单一指标,有助于我们更便捷地评估和比较不同的机器学习模型。掌握这些概念对于构建有效的机器学习系统至关重要。

92:决策树模型 🌳

在本节课中,我们将学习一种强大且广泛应用的机器学习算法——决策树。尽管决策树在学术界受到的关注相对较少,但它在实际应用和机器学习竞赛中表现卓越,是值得掌握的重要工具。我们将通过一个简单的猫分类示例,逐步理解决策树的工作原理和构建方法。

概述 📋

决策树是一种树形结构的模型,用于分类或回归任务。它通过一系列基于特征值的判断,最终在叶子节点给出预测结果。本节我们将以猫分类为例,详细介绍决策树的基本概念、结构以及学习过程。

决策树的基本概念 🧩

为了解释决策树的工作原理,我们使用一个猫分类的示例。假设你经营一个猫咪领养中心,需要根据动物的特征快速判断它是否为猫。

数据集示例

我们有一个包含10个训练样本的数据集,每个样本有三个特征:

  • 耳朵形状(Ear Shape):尖耳朵(Pointy)或垂耳朵(Floppy)
  • 脸型(Face Shape):圆形(Round)或非圆形(Not Round)
  • 胡须(Whiskers):有(Present)或无(Absent)

目标标签是是否为猫(Is Cat),用1(是)或0(否)表示。

以下是数据集的部分示例:

耳朵形状 脸型 胡须 是否为猫
Pointy Round Present 1
Floppy Not Round Present 1
... ... ... ...

该数据集中包含5只猫和5只狗,特征值为分类变量(即取有限离散值),任务为二分类。

决策树模型示例

决策树学习算法训练后可能输出如下模型:

根节点(Ear Shape)
├── 左分支(Pointy) → 决策节点(Face Shape)
│   ├── 左分支(Round) → 叶子节点(预测为猫)
│   └── 右分支(Not Round) → 叶子节点(预测为非猫)
└── 右分支(Floppy) → 决策节点(Whiskers)
    ├── 左分支(Present) → 叶子节点(预测为猫)
    └── 右分支(Absent) → 叶子节点(预测为非猫)

节点类型说明

  • 根节点(Root Node):树的顶部节点,第一个判断特征。
  • 决策节点(Decision Node):根据特征值决定分支方向。
  • 叶子节点(Leaf Node):树的末端,给出最终预测。

分类过程示例

对于新样本(耳朵形状=尖,脸型=圆,胡须=有):

  1. 从根节点开始,检查耳朵形状为“尖”,进入左分支。
  2. 在决策节点检查脸型为“圆”,进入左分支。
  3. 到达叶子节点,预测为猫。

决策树的多样性 🌿

决策树的结构可以多种多样。例如,另一棵决策树可能以胡须特征为根节点,或以不同顺序判断特征。不同的树在训练集和测试集上的表现可能不同。

以下是几种可能的决策树结构示例:

  1. 以耳朵形状为根节点,其次判断胡须。
  2. 以脸型为根节点,其次判断耳朵形状。
  3. 以胡须为根节点,其次判断脸型。
  4. 其他特征组合顺序。

决策树学习算法的目标是从所有可能的树中,选择在训练集上表现良好且能泛化到新数据(如交叉验证集和测试集)的模型。

总结 🎯

本节课我们一起学习了决策树的基本概念。我们通过猫分类示例,了解了决策树的结构(包括根节点、决策节点和叶子节点)以及分类过程。决策树通过一系列特征判断进行预测,具有直观易懂的优点。不同的决策树结构可能影响模型性能,因此学习算法需要选择最优的树。在接下来的课程中,我们将深入探讨如何构建和训练决策树。

93:决策树学习过程 🌳

在本节课中,我们将学习如何构建决策树。决策树是一种常用的机器学习算法,用于分类和回归任务。我们将详细介绍构建决策树的步骤,包括如何选择特征进行分裂以及何时停止分裂。


决策树构建过程概述

给定一个训练集,构建决策树的过程包括几个关键步骤。在上一节中,我们介绍了决策树的基本概念。本节中,我们来看看构建决策树的具体流程。


构建决策树的步骤

以下是构建决策树的主要步骤:

  1. 选择根节点的特征
    首先,我们需要决定在根节点使用哪个特征。例如,在一个包含10个猫和狗样本的训练集中,我们可能选择“耳朵形状”作为根节点的特征。

  2. 根据特征值分裂数据
    根据选定的特征值,将训练样本分成不同的子集。例如,将“尖耳朵”的样本移到左侧分支,将“垂耳朵”的样本移到右侧分支。

  3. 递归分裂子节点
    对每个分支重复上述过程,选择新的特征进行分裂,直到满足停止条件。

  4. 创建叶节点
    当某个节点的样本全部属于同一类别时,创建叶节点并做出预测。


关键决策点

在构建决策树的过程中,有两个关键决策需要做出:

1. 如何选择分裂特征

在每个节点,我们需要选择一个特征来分裂数据。决策树算法通常会选择能够最大化“纯度”的特征。纯度指的是子集中样本属于同一类别的程度。

例如,假设我们有以下特征:

  • 耳朵形状(尖耳朵、垂耳朵)
  • 脸型(圆形、非圆形)
  • 胡须(有、无)

算法需要计算每个特征分裂后的纯度,并选择纯度最高的特征。纯度的计算公式如下:

纯度 = 同一类别的样本数 / 总样本数

2. 何时停止分裂

停止分裂的条件包括:

  • 节点中的样本全部属于同一类别。
  • 树的深度达到预设的最大值。
  • 纯度提升低于某个阈值。
  • 节点中的样本数量过少。

例如,如果最大深度设置为2,则树不会分裂到深度3的节点。这有助于防止过拟合并保持树的简洁性。


决策树的深度

决策树的深度定义为从根节点到某个节点的跳数。根节点的深度为0,其子节点的深度为1,以此类推。限制树的深度可以防止树过于复杂,并减少过拟合的风险。


总结

本节课中,我们一起学习了决策树的构建过程。我们介绍了如何选择分裂特征以及何时停止分裂,并讨论了决策树深度的概念。决策树虽然包含许多细节,但这些步骤组合在一起形成了一个高效的机器学习算法。

在接下来的课程中,我们将深入探讨如何计算纯度以及如何选择最佳分裂特征。让我们继续学习!

94:📊 测量纯度

在本节课中,我们将学习如何量化一组样本的“纯度”。如果一组样本全部属于同一类别(例如全是猫或全不是猫),那么它就是非常“纯”的。但如果样本混合了不同类别,我们该如何衡量其纯度呢?让我们一起来了解“熵”的定义,它是衡量数据集“不纯度”的一个指标。

📈 熵的定义与直观理解

上一节我们引入了“纯度”的概念,本节中我们来看看如何用“熵”来具体测量它。熵函数通常用大写字母 H(P1) 表示,其中 P1 代表数据集中正例(例如“猫”)的比例。

下图展示了熵函数 H(P1) 的曲线。横轴是 P1(猫的比例),纵轴是熵值。

当样本中猫狗各半(P1 = 0.5)时,熵值达到最高点 1,此时数据集最“不纯”。相反,当样本全是猫(P1 = 1)或全不是猫(P1 = 0)时,熵值为 0,此时数据集最“纯”。

为了加深理解,我们来看几个具体例子。

以下是几个不同样本组成下的熵值计算示例:

  • 示例1:3只猫,3只狗
    • P1 = 3/6 = 0.5
    • H(0.5) = 1。这是最不纯的情况。
  • 示例2:5只猫,1只狗
    • P1 = 5/6 ≈ 0.83
    • H(0.83) ≈ 0.65。纯度比示例1高。
  • 示例3:6只猫,0只狗
    • P1 = 6/6 = 1
    • H(1) = 0。这是完全纯的情况(全是猫)。
  • 示例4:2只猫,4只狗
    • P1 = 2/6 ≈ 0.33
    • H(0.33) ≈ 0.92。由于接近0.5,不纯度很高。
  • 示例5:0只猫,6只狗
    • P1 = 0/6 = 0
    • H(0) = 0。这是完全纯的情况(全不是猫)。

从这些例子可以看出,随着样本从猫狗混合变为全是猫,不纯度(熵)从1降到了0,或者说纯度增加了。

🧮 熵的数学公式

了解了熵的直观意义后,现在我们来学习其具体的计算公式。设 P1 为正例(猫)的比例,那么负例(非猫)的比例 P0 = 1 - P1

熵的计算公式如下:

H(P1) = -P1 * log₂(P1) - P0 * log₂(P0)

或者等价地写为:

H(P1) = -P1 * log₂(P1) - (1 - P1) * log₂(1 - P1)

代码描述:

import numpy as np

def entropy(p1):
    if p1 == 0 or p1 == 1:
        return 0
    p0 = 1 - p1
    return -p1 * np.log2(p1) - p0 * np.log2(p0)

关于公式的几点说明:

  1. 对数底数:我们使用以2为底的对数(log₂),这是约定俗成的做法,它使得函数峰值恰好为1,便于解释。
  2. 处理边界情况:当 P1 = 0P1 = 1 时,会出现 0 * log₂(0) 的情况。按照计算熵的惯例,我们定义 0 * log₂(0) = 0,这样能正确计算出熵值为0。
  3. 与逻辑损失的相似性:你可能注意到这个公式与之前课程中学到的逻辑损失函数有些相似。这背后确有数学原理,但本课程不深入探讨。对于构建决策树来说,直接应用这个熵公式即可。

🔚 总结与预告

本节课中,我们一起学习了“熵”这一核心概念。熵是衡量数据集不纯度(或纯度)的函数,其值在0到1之间。当数据集中样本类别完全相同时,熵为0(最纯);当正负例各占一半时,熵为1(最不纯)。其计算公式为 H(P1) = -P1 * log₂(P1) - (1 - P1) * log₂(1 - P1)

除了熵,还有其他类似函数(如基尼系数)也可用于衡量不纯度,但在本系列课程中,我们将主要使用熵。

现在我们已经掌握了测量纯度的方法,在下一讲中,我们将看看如何利用熵在决策树的节点上做出选择,决定根据哪个特征进行数据分割。

95:决策树分裂准则——信息增益 📊

在本节课中,我们将学习决策树构建过程中的一个关键步骤:如何选择最佳特征进行节点分裂。我们将重点介绍信息增益的概念,它通过计算熵的减少量来帮助我们做出选择。


概述

决策树通过一系列特征分裂将数据划分为更纯净的子集。在上一节中,我们介绍了作为衡量数据不纯度的指标。本节中,我们来看看如何利用熵的减少量——即信息增益——来选择在每个节点上使用哪个特征进行分裂,从而构建更有效的决策树。


信息增益的定义

在决策树学习中,熵的减少量被称为信息增益。我们的目标是选择那个能最大程度降低子节点加权平均熵的特征进行分裂。

信息增益的通用计算公式如下:

信息增益 = 根节点熵 - (W_left * 左子节点熵 + W_right * 右子节点熵)

其中:

  • 根节点熵:分裂前,当前节点数据的不纯度。
  • W_left, W_right:分别代表进入左、右子节点的样本数占总样本数的比例(权重)。
  • 左/右子节点熵:分裂后,左、右子节点数据的不纯度。


计算示例:识别猫的决策树

假设我们正在构建一个识别猫的决策树,根节点有10个样本(5只猫,5只狗)。我们考虑三个候选特征:耳朵形状、脸型、是否有胡须。

以下是每个特征分裂后的数据分布与熵值计算:

1. 按“耳朵形状”分裂

  • 左子节点:5个样本,其中4只猫。P1_left = 4/5 = 0.8,熵 ≈ 0.72。
  • 右子节点:5个样本,其中1只猫。P1_right = 1/5 = 0.2,熵 ≈ 0.72。
  • 根节点熵(P1_root = 0.5)为1。
  • 信息增益 = 1 - (5/10 * 0.72 + 5/10 * 0.72) = 0.28

2. 按“脸型”分裂

  • 左子节点:7个样本,其中4只猫。P1_left = 4/7 ≈ 0.57,熵 ≈ 0.99。
  • 右子节点:3个样本,其中1只猫。P1_right = 1/3 ≈ 0.33,熵 ≈ 0.92。
  • 信息增益 = 1 - (7/10 * 0.99 + 3/10 * 0.92) ≈ 0.03

3. 按“胡须”分裂

  • 左子节点:4个样本,其中3只猫。P1_left = 3/4 = 0.75,熵 ≈ 0.81。
  • 右子节点:6个样本,其中2只猫。P1_right = 2/6 ≈ 0.33,熵 ≈ 0.92。
  • 信息增益 = 1 - (4/10 * 0.81 + 6/10 * 0.92) ≈ 0.12

如何选择分裂特征

比较三个特征的信息增益:

  • 耳朵形状:0.28
  • 脸型:0.03
  • 胡须:0.12

“耳朵形状”特征带来了最大的信息增益(0.28),意味着它能最有效地降低数据的不纯度。因此,我们选择在根节点按“耳朵形状”进行分裂。


为什么使用信息增益

使用信息增益(熵的减少量)而不仅仅是子节点的熵,主要有两个原因:

  1. 加权考虑:它通过W_leftW_right考虑了进入各子节点的样本数量。一个包含大量样本的高熵子节点,比一个只包含少量样本的高熵子节点问题更严重。
  2. 提供停止准则:如果所有可能分裂带来的信息增益都小于某个阈值,我们可以决定不再分裂该节点。这有助于防止树过度生长,降低过拟合风险。

总结

本节课中我们一起学习了决策树的核心分裂准则——信息增益。关键要点如下:

  • 信息增益衡量了通过某个特征分裂后,数据不纯度(熵)的减少程度。
  • 选择分裂特征时,我们计算每个特征对应的信息增益,并选择增益最大的那个。
  • 其计算公式为:信息增益 = 根节点熵 - 加权平均子节点熵
  • 使用信息增益不仅能找到最佳分裂点,还为决策树提供了重要的预剪枝停止准则。

现在你已经掌握了如何计算信息增益并选择分裂特征,下一节我们将把这些知识整合起来,看看构建决策树的完整算法。

96:决策树的构建与实现 🌳

在本节课中,我们将学习如何将信息增益准则应用于决策树的多个节点,从而构建一个包含多个节点的大型决策树。我们将详细介绍决策树的构建过程、停止分裂的条件,以及如何对新样本进行预测。


决策树的构建过程

上一节我们介绍了信息增益准则,本节中我们来看看如何利用它来构建整个决策树。

决策树的构建是一个递归过程,从根节点开始,逐步选择最佳特征进行分裂,直到满足停止条件。

以下是构建决策树的主要步骤:

  1. 从根节点开始:将所有训练样本置于决策树的根节点。
  2. 计算信息增益:针对所有可能的特征,计算其信息增益。
  3. 选择最佳特征:选择能带来最高信息增益的特征作为当前节点的分裂依据。
  4. 创建分支:根据所选特征的值,将数据划分为两个子集,并创建左、右分支。
  5. 递归分裂:对左分支和右分支重复步骤2-4,直到满足停止条件。

停止分裂的条件

在递归分裂过程中,我们需要设定停止条件,以防止树过度生长和过拟合。以下是常见的停止条件:

  • 节点纯度达到100%:当节点中所有样本都属于同一类别时(熵为零),停止分裂。
  • 达到最大深度:当树的深度达到预设的最大深度时,停止分裂。
  • 信息增益过小:如果进一步分裂带来的信息增益小于某个阈值,则停止分裂。
  • 节点样本数过少:如果节点中的样本数量低于某个阈值,则停止分裂。

构建过程会持续进行,直到满足你设定的一个或多个停止条件。


构建过程示例

让我们通过一个图示来具体了解这个过程。

我们首先将所有样本放在根节点。计算所有三个特征(耳形、胡须、脸形)的信息增益后,决定耳形是根节点的最佳分裂特征。根据此特征,我们创建左(尖耳)和右(垂耳)分支,并将数据子集分别发送到这两个分支。

现在,我们聚焦于左分支(包含5个样本)。我们的停止条件是“节点内样本属于同一类别”。由于该节点混合了猫和狗,不满足条件,因此需要继续分裂。

接下来,我们仅使用这5个样本,计算剩余特征(胡须、脸形)的信息增益。由于所有样本耳形相同,分裂耳形的信息增益为零。在胡须和脸形之间,脸形的信息增益最高。因此,我们选择脸形进行分裂。

分裂后,左子分支全是猫,满足停止条件,我们创建一个预测为“猫”的叶节点。右子分支全是狗,同样满足条件,创建一个预测为“非猫”的叶节点。

完成左子树后,我们以同样的方式构建右子树(包含另外5个样本)。计算信息增益后发现,胡须特征的信息增益最高。根据胡须是否存在进行分裂后,两个新分支都达到了纯度要求,因此分别创建预测“猫”和“非猫”的叶节点。


递归算法

请注意我们构建过程中的一个有趣特点:在决定了根节点的分裂特征后,我们构建左子树的方式是在一个5样本的子集上构建一个决策树,构建右子树的方式亦然。在计算机科学中,这被称为递归算法

递归意味着编写能调用自身的代码。构建决策树时,你通过构建更小的子树(左子树和右子树)来构建整个决策树,并将它们组合在一起。这就是为什么在决策树的软件实现中,你有时会看到对递归算法的引用。

如果你不完全理解递归算法的概念,也无需担心。你仍然可以完成本周的练习,并使用库来让决策树为你工作。但如果你要从零开始实现决策树算法,递归将是必须实现的步骤之一。


参数选择与过拟合

你可能会想知道如何选择最大深度这类参数。虽然没有唯一答案,但一些开源库会提供良好的默认值可供使用。

一个直观的理解是:最大深度越大,你允许构建的决策树就越大。这有点像拟合更高次数的多项式或训练更大的神经网络。它让决策树能够学习更复杂的模型,但如果对数据拟合了过于复杂的函数,也会增加过拟合的风险。

理论上,你可以使用交叉验证集来调整像最大深度这样的参数,尝试不同的值并选择在验证集上表现最好的那个。但在实践中,开源库有更好的方法为你选择这个参数。

另一个可以用来决定何时停止分裂的标准是:如果额外分裂带来的信息增益小于某个阈值(即任何特征分裂只能带来很小的熵减少或信息增益),那么你也可以决定不再分裂。

最后,你还可以在节点中的样本数量低于某个阈值时决定停止分裂。


使用决策树进行预测

现在你已经学会了如何构建决策树。如果你想进行预测,可以遵循本周第一个视频中介绍的过程:取一个新样本(例如测试样本),从根节点开始,沿着决策路径向下,直到到达一个叶节点,该叶节点会给出最终的预测结果。


总结

本节课中我们一起学习了决策树学习算法的核心构建过程。我们了解了如何从根节点开始,递归地使用信息增益选择最佳分裂特征,并设定了多种停止分裂的条件以防止过拟合。我们还简要探讨了递归算法在其中的作用以及如何选择树的最大深度等参数。最后,我们回顾了如何使用训练好的决策树对新样本进行预测。

在接下来的视频中,我们将进一步探讨这个算法的其他改进,例如如何处理具有两个以上取值的分类特征。

97:使用分类特征的独热编码 🏷️

在本节课中,我们将要学习如何处理可以取多个离散值的分类特征。我们将重点介绍一种称为“独热编码”的技术,它可以将具有多个类别的特征转换为多个二进制特征,从而使决策树等算法能够直接处理。

概述

在之前看到的例子中,每个特征只能取两个可能的值之一。例如,耳朵形状要么是尖的,要么是耷拉的;脸型要么是圆的,要么不是;胡须要么存在,要么不存在。但是,如果你有一个特征可以取两个以上的离散值,该怎么办呢?本节视频将介绍如何使用独热编码来处理这类特征。

引入多值分类特征

这里有一个我们宠物领养中心应用程序的新训练集。除了耳朵形状特征外,所有数据都与之前相同。现在,耳朵形状不仅可以是尖的或耷拉的,还可以是椭圆的。

因此,耳朵形状特征仍然是一个分类特征,但它可以取三个可能的值,而不仅仅是两个。这意味着,当你根据这个特征进行分割时,最终会创建三个数据子集,并为树构建三个子分支。

独热编码的解决方案

在本视频中,我想描述一种不同的方法来处理可以取多个值的特征,即使用独热编码。

具体来说,我们不使用一个可以取三个可能值的耳朵形状特征,而是创建三个新特征:

  • 第一个特征是:这个动物有尖耳朵吗?
  • 第二个特征是:它有耷拉耳朵吗?
  • 第三个特征是:它有椭圆耳朵吗?

对于第一个例子,我们之前说耳朵形状是尖的。现在,我们说这个动物的“尖耳朵”特征值为1,“耷拉耳朵”和“椭圆耳朵”特征值为0。

对于第二个例子,我们之前说它有椭圆耳朵。现在,我们说它的“尖耳朵”特征值为0(因为它没有尖耳朵),“耷拉耳朵”特征值也为0,但“椭圆耳朵”特征值为1。数据集中的其余示例依此类推。

这样,我们不是用一个可以取三个可能值的特征,而是构造了三个新特征,每个特征只能取两个可能值之一:0或1。

独热编码的通用方法

更详细地说,如果一个分类特征可以取k个可能的值(在例子中k=3),那么我们将通过创建k个只能取值0或1的二进制特征来替换它。

你会注意到,在这三个特征中,如果你查看任何一行,恰好有一个值等于1,这正是这种特征构建方法被称为“独热编码”的原因。因为这些特征中总有一个取值为1(即“热”特征),所以得名“独热编码”。

应用决策树算法

通过这种特征选择,我们现在回到了每个特征只取两个可能值之一的原始设置。因此,我们之前看到的决策树学习算法无需任何进一步修改即可应用于此数据。

扩展到神经网络

顺便说一下,尽管本周的材料主要关注训练决策树模型,但使用独热编码对分类特征进行编码的思想也适用于训练神经网络。

具体来说,如果你要获取脸型特征,并用1和0替换“圆”和“不圆”(其中“圆”对应1,“不圆”对应0),对于胡须特征,类似地用1替换“存在”,用0替换“不存在”。

那么请注意,我们已经获取了所有已有的分类特征(耳朵形状有3个可能值,脸型有2个,胡须有2个),并将其编码为这五个特征的列表:三个来自耳朵形状的独热编码,一个来自脸型,一个来自胡须。现在,这五个特征的列表也可以输入到神经网络或逻辑回归中,以尝试训练一个猫分类器。

因此,独热编码不仅适用于决策树学习,还可以让你使用1和0对分类特征进行编码,以便将其作为输入提供给神经网络(神经网络期望数字作为输入)。

总结

本节课中我们一起学习了独热编码。通过独热编码,你可以让决策树处理可以取两个以上离散值的特征。你也可以将此技术应用于神经网络、线性回归或逻辑回归的训练。

那么,对于可以取任何数值(不仅仅是少量离散值)的特征呢?在下一个视频中,让我们看看如何让决策树处理可以是任何数字的连续值特征。

98:连续值特征处理 🎯

在本节课中,我们将学习如何修改决策树算法,使其能够处理连续值特征,而不仅仅是离散值特征。连续值特征是指可以取任意数值的特征,例如动物的体重。

从离散到连续:决策树的扩展

上一节我们介绍了决策树如何处理离散特征(如耳朵形状)。本节中我们来看看当特征(如体重)是连续值时,决策树算法应如何调整。

决策树算法的核心流程与之前类似,但在选择分裂特征时,除了考虑“耳朵形状”、“脸型”和“胡须”等离散特征,现在也需要考虑“体重”这样的连续特征。如果根据“体重”特征进行分裂能带来比其他特征更高的信息增益,那么算法就会选择在“体重”特征上进行分裂。

如何对连续特征进行分裂?

关键在于如何确定分裂的阈值。我们通过一个例子来理解这个过程。

假设我们有一个修改后的宠物领养中心数据集,新增了“体重”(磅)这一连续特征。下图展示了根节点处数据的分布情况,横轴是动物体重,纵轴是标签(1代表猫,0代表非猫)。

对连续特征“体重”进行分裂,意味着我们需要选择一个阈值(例如8磅),并根据“体重是否小于等于该阈值”将数据划分为两个子集。学习算法的任务就是找到最佳的这个阈值。

以下是寻找最佳阈值的基本步骤:

  1. 考虑多个不同的候选阈值。
  2. 对每个候选阈值,计算其对应的信息增益。
  3. 选择能带来最高信息增益的阈值。

信息增益计算示例

让我们通过计算来具体理解。

情况一:以体重 ≤ 8磅 为阈值

  • 左子集:包含2只猫。
  • 右子集:包含3只猫和5只狗。
  • 信息增益计算为:信息增益 = H(0.5) - [ (2/10)*H(1) + (8/10)*H(3/8) ] ≈ 0.24

情况二:以体重 ≤ 9磅 为阈值

  • 左子集:包含4只猫。
  • 右子集:包含1只猫和5只狗。
  • 信息增益计算为:信息增益 = H(0.5) - [ (4/10)*H(1) + (6/10)*H(1/6) ] ≈ 0.61

情况三:以体重 ≤ 13磅 为阈值

  • 信息增益计算结果约为 0.40。

比较可知,以9磅为阈值的信息增益(0.61)最高。

选择候选阈值的通用方法

在实际操作中,我们不会只尝试三个值。一个通用的做法是:

  1. 将所有训练样本按该连续特征的值从小到大排序。
  2. 考虑排序后相邻样本特征值之间的中点作为候选阈值。
  3. 如果有10个训练样本,则会产生9个候选阈值。
  4. 计算每个候选阈值对应的信息增益,并选择增益最高的那个。

如果从该连续特征上分裂获得的信息增益,高于从任何其他特征(离散或连续)上分裂的增益,那么算法就会决定在当前节点按此特征和选定的阈值进行分裂。

在本例中,0.61的信息增益是最高的,因此算法会选择按“体重是否≤9磅”来分裂根节点,从而得到两个数据子集。

随后,可以递归地使用这两个子集来构建决策树的其余部分。

总结与过渡

本节课中我们一起学习了如何让决策树处理连续值特征。核心方法是:在每个节点考虑分裂时,为连续特征尝试不同的分裂阈值,进行常规的信息增益计算,如果该特征能提供所有可能特征中最高的信息增益,则按选定的阈值在此连续特征上进行分裂。

这就是决策树处理连续值特征的方法:尝试不同阈值,进行常规信息增益计算,并在该特征能提供最佳信息增益时,使用选定的阈值进行分裂。

以上是决策树核心算法的必备内容。接下来有一个可选视频,将把决策树学习算法推广到回归树。目前我们只讨论了用决策树进行分类预测(如判断是否为猫)。但如果你遇到的是回归问题,想要预测一个具体数值呢?下一个视频我将介绍决策树的一种泛化形式来处理这种情况。

99:回归树 🌳

在本节课中,我们将学习如何将决策树算法从分类问题推广到回归问题,用于预测连续数值。我们将通过一个预测动物体重的具体例子,详细讲解回归树的构建过程、核心的分裂选择标准,以及它与分类树的区别。


从分类到回归

上一节我们介绍了决策树在分类问题中的应用。本节中,我们来看看如何将其用于回归任务,即预测一个具体的数字。

我使用的例子是,利用之前用过的离散特征(如耳朵形状、脸型等)来预测动物的体重 Y。需要明确的是,这里的体重是我们要预测的目标输出值,而不是输入特征。这是一个回归问题,因为我们预测的是一个连续数值。

让我们看看回归树会是什么样子。


回归树的结构与预测

这里我已经为这个回归问题构建了一棵树。根节点根据耳朵形状分裂,然后左子树和右子树再根据脸型进行分裂。决策树在左右分支上选择相同的特征进行分裂是完全可行的,如果分裂算法认为这样做合适的话。

如果在训练中确定了这些分裂点,那么最下方的这个叶节点将包含四个动物,体重分别为 7.2、8.4、7.6 和 10.2。另一个节点则包含一个体重为 9.2 的动物,其余节点同理。

我们需要为这个决策树填充的最后一步是:如果一个测试样本到达了这个叶节点,我们应该为这个拥有尖耳朵和圆脸的动物预测什么体重?

决策树的预测方法是:取到达该叶节点的所有训练样本体重的平均值。通过计算这四个数字的平均值,我们得到 8.35。

另一方面,如果一个动物有尖耳朵但不是圆脸,那么模型将预测 9.2(或 9.20 磅),因为到达这个叶节点的唯一训练样本体重就是 9.2。同理,其他叶节点的预测值分别为 17.70 和 9.90。

因此,这个模型的工作流程是:给定一个新的测试样本,像往常一样沿着决策节点向下,直到到达一个叶节点,然后预测该叶节点的值。这个值是在训练时,通过计算到达同一叶节点的所有动物体重的平均值得到的。


如何选择分裂特征

如果你要从头开始构建一棵决策树来预测体重,关键决策(正如本周早些时候所看到的)是如何选择在哪个特征上进行分裂。让我通过一个例子来说明如何做出这个决定。

在根节点,你可以选择在耳朵形状上分裂。如果这样做,你会得到树的左右分支,每个分支有五个动物,其体重如下所示。

如果你选择在脸型上分裂,你会得到左右分支的动物及其对应的体重。

如果你选择在有/无胡须上分裂,则会得到这样的结果。

那么问题是:在根节点,给定这三个可能的分裂特征,你应该选择哪一个,才能在构建回归树时对动物体重做出最好的预测?

与分类问题中试图减少(一种不纯度的度量)不同,在回归树中,我们试图减少每个数据子集中目标值 y(这里是体重)的方差

如果你在其他上下文中见过方差的概念,那很好,这就是我们即将使用的统计学或数学上的方差概念。如果你以前不知道如何计算一组数字的方差,不用担心。在本节中,你只需要知道方差非正式地衡量了一组数字的变化范围有多大。

对于第一组数字(7.2, 9.2, ... , 1.2),其方差计算结果是 1.47,说明变化不大。而第二组数字(8.8, 15, 11, 18, 20)从 8.8 一直到 20,方差要大得多,计算结果是 21.87。

我们将这样评估这个分裂的质量:和之前一样,计算 W_leftW_right 作为进入左右分支的样本比例。

分裂后的加权平均方差将是:
W_left * Var_left + W_right * Var_right

在这个例子中,就是 (5/10) * 1.47 + (5/10) * 21.87。这个加权平均方差的作用,类似于我们在分类问题中决定使用哪个分裂时使用的加权平均熵。

我们可以为树中其他可能的分裂特征选择重复这个计算。对于中间的例子(按脸型分裂),左边数字的方差是 27.80,右边是 1.37。W_left = 7/10W_right = 3/10,因此可以计算出加权方差。

最后,对于按胡须特征分裂的例子,这是左右两边的方差以及对应的 W_leftW_right,加权方差如下所示。


选择最佳分裂:方差减少量

一个好的分裂选择方法是直接选择加权方差最低的那个。类似于计算信息增益时,我们不只是测量加权平均熵,而是测量熵的减少量(即信息增益)。对于回归树,我们同样测量方差的减少量

事实证明,如果你查看训练集中所有 10 个样本并计算它们的总方差,结果是 20.51。这个值对于根节点在所有情况下都是相同的,因为根节点就是这相同的 10 个样本。

因此,我们实际计算的是根节点的方差(20.51)减去分裂后的加权平均方差。对于按耳朵形状分裂,计算结果是 20.51 - 11.67 = 8.84。这意味着在根节点,方差是 20.51,按耳朵形状分裂后,这两个节点的平均加权方差降低了 8.84,所以方差减少量是 8.84。

类似地,计算中间例子(按脸型分裂)的方差减少量是 20.51 - 19.87 = 0.64,这是一个非常小的减少。对于胡须特征,方差减少量是 6.22。

在这三个例子中,8.84 给出了最大的方差减少量。因此,正如之前我们会选择给出最大信息增益的特征一样,对于回归树,我们会选择给出最大方差减少量的特征。这就是为什么选择耳朵形状作为分裂特征。

选择了耳朵形状作为分裂特征后,你现在在左右子分支中各有 5 个样本的子集。然后,你可以(递归地)对这 5 个样本构建一个新的决策树,再次评估不同的分裂特征选项,选择给出最大方差减少量的那个。在右侧分支也进行同样的操作。你会持续分裂,直到满足停止分裂的条件。


总结与展望

本节课中,我们一起学习了如何将决策树应用于回归问题。核心在于将预测目标从离散类别改为连续数值,并将分裂标准从最小化熵(信息增益)改为最小化加权平均方差(方差减少量)。我们通过计算不同分裂带来的方差减少量,来选择最佳分裂特征,从而构建出回归树。

到目前为止,我们讨论的是如何训练单个决策树。事实证明,如果你训练很多决策树(我们称之为决策树集成),你可以得到更好的结果。让我们在下一个视频中看看为什么以及如何做到这一点。

100:决策树集成 🎄

在本节课中,我们将要学习单个决策树的一个主要弱点,并探索一种通过构建多个决策树来提升模型鲁棒性的强大方法——决策树集成。

单个决策树的弱点

上一节我们介绍了决策树的基本构建过程。本节中我们来看看单个决策树存在的一个问题:它对数据中的微小变化可能非常敏感

具体来说,如果训练数据发生一点变动,生成的决策树结构可能会完全不同。这导致模型的预测不够稳定和可靠。

一个具体的例子

让我们通过一个例子来理解这种敏感性。我们一直使用的数据集中,在根节点进行分裂时,信息增益最高的特征是“耳朵形状”,这产生了两个数据子集,并在此基础上继续构建子树。

但是,如果你只改变10个训练样本中的1个——例如,将一只“尖耳朵、圆脸、有胡须”的猫,改为“软耳朵、圆脸、有胡须”的猫——情况就会发生变化。

仅仅改变这一个训练样本,最高信息增益的分裂特征就从“耳朵形状”变成了“胡须”。因此,左右子树得到的数据子集变得完全不同。随着递归运行决策树学习算法,左右两侧构建出的子树也完全不同。

改变一个训练样本就导致算法在根节点选择不同的分裂方式,从而生成一棵完全不同的树,这使得该算法不够鲁棒。

解决方案:决策树集成

正因为如此,使用决策树时,如果你训练的不是单个决策树,而是一整组不同的决策树,你通常会得到更好的结果,即更准确的预测。

这就是我们所说的树集成,它指的是一组(多棵)决策树的集合。在接下来的视频中,我们将看到如何构建这种树集成。

集成如何工作:投票机制

如果你拥有一个由三棵树组成的集成,其中每一棵都可能是对“猫”与“非猫”进行分类的一种合理方式。

当有一个新的测试样本需要分类时,你需要做的是:

  1. 让所有三棵树对这个新样本进行推理。
  2. 让它们对最终预测进行投票

例如,一个测试样本具有“尖耳朵、非圆脸、有胡须”的特征:

  • 第一棵树会沿着它的路径推断,并预测它是猫。
  • 第二棵树会沿着它的路径推断,并预测它不是猫。
  • 第三棵树会沿着它的路径推断,并预测它是猫。

这三棵树做出了不同的预测,我们将让它们投票。这三棵树中的多数票是“猫”,因此这个树集成的最终预测是:这是一只猫(这恰好是正确的预测)。

使用树集成的原因

我们使用树集成的原因是:通过拥有大量决策树并让它们投票,可以使你的整体算法对任何单棵树的行为不那么敏感(因为每棵树只占一票),从而使整体算法更加鲁棒

关键问题与下节预告

但是,如何获得所有这些合理但又略有不同的决策树,以便让它们投票呢?

在下一个视频中,我们将讨论一种来自统计学的技术,称为有放回抽样。这将成为我们在后续视频中构建树集成的关键技术。让我们进入下一个视频来探讨有放回抽样。


本节课总结:本节课我们一起学习了单个决策树对数据敏感的问题,并引入了决策树集成作为解决方案。我们了解了集成通过让多棵树投票来工作,从而使预测更加稳定和鲁棒。最后,我们提出了如何生成这些不同树的关键问题,为下一课的内容做好了铺垫。

101:有放回抽样 🎲

在本节课中,我们将学习一种称为“有放回抽样”的技术。这是构建树集成模型(如随机森林)的关键步骤。我们将通过简单的例子来理解其工作原理,并了解它如何应用于创建多个略有差异的训练集。


为了构建树集成模型,我们需要一种名为“有放回抽样”的技术。让我们来看看它的含义。

为了说明有放回抽样如何工作,我将用一个包含红、黄、绿、蓝四种颜色令牌的演示来展示。

我这里有四个颜色的令牌:红、黄、绿、蓝。我将用它们来演示有放回抽样的过程。

这是一个空的黑丝绒袋子。我将把这四个令牌例子放进袋子里。

我将进行四次有放回抽样。这意味着我将摇晃袋子,随机取出一个令牌,例如绿色。关键在于“有放回”部分:在取出下一个令牌之前,我需要把这个绿色令牌放回袋中,再次摇晃。然后取出另一个,比如黄色,再将其放回。接着取出蓝色,同样放回。最后再取出一个,结果又是蓝色。

我得到的令牌序列是:绿、黄、蓝、蓝。请注意,我得到了两次蓝色,而红色一次也没有被抽到。

如果你多次重复这个有放回抽样的过程,你可能会得到“红、黄、红、绿”或“绿、绿、蓝、红”等序列。你也可能得到“红、蓝、黄、绿”。

“有放回”这部分至关重要。因为如果每次抽样后不将令牌放回,那么从一个装有四个令牌的袋子里连续抽取四次,你将总是得到完全相同的四个令牌。这就是为什么每次抽取后将令牌放回很重要,它能确保我们不会每次都得到完全相同的四个令牌。


上一节我们通过令牌的例子理解了有放回抽样的基本概念。本节中,我们来看看这项技术如何应用于构建树集成模型。

有放回抽样应用于构建树集成的方式如下:我们将构建多个随机训练集,它们都与原始训练集略有不同。

具体来说,我们以10个猫狗训练样本为例。我们将这10个训练样本放入一个“理论上的袋子”中。

请不要真的把猫或狗放进袋子。你可以想象将训练样本放入一个理论上的袋子。使用这个理论上的袋子,我们将创建一个新的、包含10个样本的随机训练集,其大小与原始数据集完全相同。

创建方法如下:我们将伸手进入袋子,随机选取一个训练样本。假设我们得到了这个样本。然后,我们将其放回袋中。接着,再次随机选取一个训练样本,比如得到那个样本。如此反复,一次又一次。

请注意,这第五个训练样本与我们第二次取出的样本完全相同,但这没关系。继续这个过程,我们会得到另一个重复的样本,依此类推,直到最终获得10个训练样本,其中一些是重复的。

你还会注意到,这个新的训练集并没有包含原始10个训练样本中的所有样本,但这没关系,这是有放回抽样过程的一部分。


有放回抽样的过程让你能够构建一个新的训练集,它与你的原始训练集既有些相似,又相当不同。事实证明,这将是构建树集成模型的关键基石。

在下一节视频中,让我们看看如何利用这一点来构建树集成。


本节课总结

本节课中,我们一起学习了“有放回抽样”技术。我们通过颜色令牌的演示理解了其核心机制:每次抽取样本后都将其放回总体,从而允许同一样本被多次抽取,并可能产生包含重复样本且未包含所有原始样本的新数据集。我们了解到,这项技术是构建树集成模型(如随机森林)的基础,因为它能方便地生成多个略有差异的训练集,用于训练集成的各个决策树。

102:随机森林算法 🌲🤖

在本节课中,我们将学习如何构建一个比单一决策树更强大的树集成算法——随机森林。我们将从创建新训练集的方法开始,逐步讲解如何生成多棵决策树,并通过投票机制获得最终预测。最后,我们会探讨一个关键改进,使算法性能更优。


构建树集成的基础

上一节我们介绍了使用有放回抽样来创建新训练集的方法。本节中,我们来看看如何利用这种方法构建第一个树集成算法。

给定一个大小为 M 的训练集,我们可以通过以下步骤生成一个包含多棵决策树的集成模型:

以下是构建树集成的核心步骤:

  1. 设定一个循环次数 B(例如,B = 100)。
  2. 对于 b = 1B
    • 使用有放回抽样从原始训练集中创建一个新的、大小同样为 M 的训练集。
    • 在这个新生成的数据集上训练一棵决策树。

注意:有放回抽样意味着同一个训练样本可能在新数据集中出现多次,这是允许的。

通过这个过程,我们最终会得到 B 棵不同的决策树。当需要进行预测时,就让所有这些树进行“投票”,以决定最终的预测结果。

B 设置得更大通常不会损害性能,但超过某个点(例如,远大于10)后,性能提升会变得微乎其微,而计算成本却显著增加。因此,通常不推荐使用上千棵树。


从袋装决策树到随机森林 🌳➡️🌲🌲🌲

我们刚刚描述的算法通常被称为袋装决策树。这个名字来源于将训练样本放入一个“虚拟袋子”中进行抽样的想法。

然而,这个算法有一个可以改进的地方,这个改进能将它转变为性能更强大的随机森林算法

有时,即使使用了有放回抽样,生成的许多树在根节点或靠近根节点的位置仍然可能选择相同的特征进行分裂。为了进一步使每棵树更加不同,我们可以在每个节点选择分裂特征时引入随机性。

以下是随机森林算法的关键改进:

  • 在每个节点,当需要从 n 个可用特征中选择一个进行分裂时:
    • 我们不从全部 n 个特征中挑选。
    • 而是随机选择一个包含 k 个特征的子集(其中 k < n)。
    • 算法只能从这个 k 个特征的子集中,选择信息增益最高的特征来进行分裂。

当特征数量 n 很大时(例如几十、几百个),k 的一个典型选择是 k = sqrt(n)。这个技巧在特征数量较多的问题上效果更明显。


为什么随机森林更强大?

随机森林比单一决策树更稳健的原因在于:

  1. 有放回抽样:已经让算法探索了训练数据的许多微小变化,并训练了不同的决策树。
  2. 特征子集选择:进一步增加了树与树之间的差异性。
  3. 集成平均:算法对所有由数据微小变化产生的树进行了平均。

这意味着,训练集的任何进一步微小变化,都不太可能对随机森林的整体输出产生巨大影响,因为它已经探索并平均了许多数据变化。


总结与预告

本节课中,我们一起学习了随机森林算法。我们从基础的袋装决策树开始,理解了如何通过有放回抽样构建多棵决策树。接着,我们引入了在每个节点随机选择特征子集的关键改进,从而得到了更强大、更稳健的随机森林算法。

随机森林是一种非常有效的算法,希望你能在工作中很好地应用它。

在随机森林之外,还有一种性能更优的算法——提升决策树。在下一节课中,我们将讨论一个名为 XGBoost 的提升决策树算法。

103:XGBoost 算法详解 🚀

在本节课中,我们将要学习一种强大且广泛应用的机器学习算法——XGBoost。我们将了解它如何通过改进传统的决策树集成方法,来更高效、更准确地构建模型。

多年来,机器学习研究者提出了许多构建决策树和决策树集成的方法。如今,最常用、最流行的决策树集成实现是一种名为 XGBoost 的算法。它运行速度快,开源实现易于使用,并且已成功应用于赢得众多机器学习竞赛以及许多商业场景中。让我们来看看 XGBoost 是如何工作的。

从装袋法到提升法 🔄

上一节我们介绍了装袋法决策树算法。本节中我们来看看如何对其进行修改,使其性能大幅提升。

以下是之前写下的装袋法算法:

for b in range(1, B+1):
    # 使用有放回抽样创建大小为 M 的新训练集
    new_training_set = sample_with_replacement(original_training_set, size=M)
    # 在新数据集上训练决策树
    tree_b = train_decision_tree(new_training_set)

给定一个大小为 M 的训练集,重复 B 次:使用有放回抽样创建一个大小为 M 的新训练集,然后在新数据集上训练决策树。第一次循环时,我们可能创建一个这样的训练集并训练出这样的决策树。

但这里我们将要改变算法,即在除了第一次之外的每一次循环中(第二次、第三次等),当我们进行抽样时,不再以 1/M 的均等概率从所有 M 个样本中选取,而是增加选取那些先前训练的树在训练中表现不佳(即分类错误)的样本的概率

核心思想:刻意练习 🎹

这背后有一个称为“刻意练习”的理念。例如,如果你正在学习弹钢琴并试图掌握一首曲子,与其一遍又一遍地练习整首(比如五分钟的)曲子(这非常耗时),不如先弹奏整首曲子,然后将注意力集中在你尚未弹好的部分,反复练习这些较小的片段。事实证明,这是你学好钢琴的更有效方法。

提升法的思想与此类似。我们将查看目前已训练好的决策树,找出我们仍然做得不好的地方,然后在构建下一个决策树时,更多地关注那些我们尚未处理好的样本。这样,我们不是查看所有训练样本,而是将更多注意力集中在尚未处理好的那部分样本上,让新构建的决策树(集成中的下一个树)努力在这些样本上表现良好。这就是提升法背后的思想,事实证明它有助于学习算法更快地学习并做得更好。

提升法的工作流程 📊

具体来说,我们会查看刚刚构建的这棵树,然后回到原始训练集(注意,这是原始训练集,不是通过有放回抽样生成的),遍历所有 10 个样本,查看这个已学习的决策树对所有 10 个样本的预测结果。

以下是预测结果列,我在每个样本旁边根据树的分类是否正确打上了勾或叉。

那么,在第二次循环中,我们将使用有放回抽样生成另一个包含 10 个样本的训练集,但每次我们从这 10 个样本中选取一个时,会给予这三个我们仍然分类错误的样本更高的被选中概率。这样,第二个决策树就能通过类似“刻意练习”的过程,将注意力集中在算法尚未处理好的样本上。

提升过程将总共进行 B 次迭代。在每次迭代中,你会查看集成中从第 1 棵树到第 B-1 棵树尚未处理好的样本。当你构建第 B 棵树时,你将更高概率地选取那些先前已构建的树集成仍然表现不佳的样本

关于具体如何增加选取这个样本与那个样本的概率的数学细节相当复杂,但为了使用现成的树实现,你无需担心这些细节。

主流实现:XGBoost ⚡

在不同的提升法实现方式中,当今应用最广泛的是 XGBoost,它代表“极限梯度提升”。这是一个开源的提升树实现,非常快速高效。XGBoost 还很好地选择了默认的分裂标准和停止分裂的条件。XGBoost 的一项创新是它内置了正则化以防止过拟合。

在机器学习竞赛中(例如广泛使用的竞赛网站 Kaggle),XGBoost 通常是一种极具竞争力的算法。事实上,XGBoost 和深度学习算法似乎是这类竞赛中最常胜出的两种算法。

有一个技术细节需要注意:XGBoost 实际上不是通过有放回抽样来操作,而是为不同的训练样本分配不同的权重。因此,它实际上不需要生成大量随机选择的训练集副本,这使得它比使用有放回抽样过程效率更高一些。但你在上一张幻灯片上看到的关于 XGBoost 如何选择关注样本的直觉仍然是正确的。

如何使用 XGBoost 🛠️

XGBoost 的实现细节相当复杂,这就是为什么许多从业者会使用实现它的开源库。以下是你使用 XGBoost 需要做的全部。

以下是使用 XGBoost 的步骤:

  1. 导入 XGBoost 库。
  2. 将模型初始化为 XGBoost 分类器。
  3. 使用 .fit() 方法在训练数据上训练模型。
  4. 使用训练好的模型进行预测。
# 1. 导入库
from xgboost import XGBClassifier

# 2. 初始化模型
model = XGBClassifier()

![](https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/dlai-ml/img/b7c17112408685d851fc68b3b24fff94_5.png)

# 3. 训练模型
model.fit(X_train, y_train)

![](https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/dlai-ml/img/b7c17112408685d851fc68b3b24fff94_7.png)

# 4. 进行预测
predictions = model.predict(X_test)

如果你想将 XGBoost 用于回归而不是分类,那么只需将上面这行代码中的 XGBClassifier 改为 XGBRegressor,代码的工作方式类似。

总结 📝

本节课中我们一起学习了 XGBoost 算法。我们了解到,XGBoost 是对传统装袋法决策树的一种改进,它通过“刻意练习”的思想,在构建后续决策树时,更多地关注先前模型表现不佳的样本,从而提升集成的整体性能。XGBoost 因其高效、快速、内置正则化以及出色的竞赛表现,成为当今最流行的决策树集成算法之一。通过简单的几行代码,我们就可以轻松调用 XGBoost 库来构建强大的分类或回归模型。

希望你会发现这个算法对你未来构建的许多应用都非常有用。

104:何时使用决策树 🌳

在本节课中,我们将探讨决策树(包括树集成方法)与神经网络这两种强大的学习算法各自的优缺点,并分析在何种场景下应选择其中一种。我们将从数据类型、训练速度、可解释性等多个维度进行比较。


结构化数据与非结构化数据 📊

上一节我们介绍了两种主要的算法类型,本节中我们来看看它们各自适合处理的数据类型。

决策树和树集成方法通常在表格数据(也称为结构化数据)上表现良好。这意味着如果你的数据集看起来像一个巨大的电子表格,那么决策树值得考虑。

例如,在房价预测应用中,数据集包含房屋面积、卧室数量、楼层数和房龄等特征。这类数据以分类或连续值特征的形式存储在电子表格中,无论是用于分类(预测离散类别)还是回归(预测数值)问题,决策树都能处理得很好。

相比之下,不建议在非结构化数据上使用决策树和树集成方法。非结构化数据包括图像、视频、音频或文本,这些数据通常不以电子表格格式存储。我们稍后会看到,神经网络在处理非结构化数据任务时往往表现更佳。


决策树的优势与劣势 ⚖️

了解了适用数据类型后,我们来详细看看决策树类算法的具体特点。

决策树和树集成方法的一大优势是训练速度非常快。你可能还记得前几周讨论的机器学习开发迭代循环图:如果模型需要数小时来训练,那么这会限制你迭代优化算法性能的速度。由于决策树(包括树集成)训练速度相当快,这允许你更快速、更高效地完成迭代循环。

此外,小的决策树可能具有人类可解释性。如果你只训练一个单独的决策树,并且该树只有几十个节点,那么你可以打印出这棵树来准确理解它是如何做出决策的。

我认为决策树的可解释性有时被略微高估了。因为当你构建一个包含100棵树的集成模型,且每棵树都有数百个节点时,通过观察整个集成模型来理解其行为就变得困难,可能需要借助单独的可视化技术。但如果你有一个小的决策树,你确实可以查看它并理解其分类逻辑,例如,它是通过以某种方式查看某些特征来判断某物是否为猫。

如果你决定使用决策树或树集成方法,对于大多数应用,我可能会推荐使用 XGBoost。树集成方法的一个小缺点是,它比单个决策树计算成本稍高。因此,如果你的计算预算非常紧张,可能会使用单个决策树。但除此之外,我几乎总是会使用树集成方法,特别是 XGBoost。


神经网络的特点 🔄

看完了决策树,我们再来看看神经网络在相同维度上的表现。

与决策树和树集成方法相比,神经网络在所有类型的数据上都能良好工作,包括表格/结构化数据、非结构化数据,以及同时包含结构化和非结构化组件的混合数据。

在表格化结构化数据上,神经网络和决策树通常具有竞争力。但在图像、视频、音频和文本等非结构化数据上,神经网络将是首选算法,而非决策树或树集成。

然而,神经网络的缺点是可能比决策树慢。一个大型神经网络可能需要很长时间来训练。

神经网络的其他好处包括支持迁移学习。这一点非常重要,因为对于许多只有少量数据的应用,能够使用迁移学习并在更大的数据集上进行预训练,对于获得有竞争力的性能至关重要。

最后,如果你正在构建一个由多个机器学习模型协同工作的系统,将多个神经网络串联起来进行训练可能比串联多个决策树更容易。这背后的原因相当技术性,在本课程中你无需担心。简而言之,它涉及到即使串联多个神经网络,你也可以使用梯度下降法一起训练它们;而对于决策树,你一次只能训练一棵树。


课程总结与展望 🎉

在本节课中,我们一起学习了如何根据数据类型、训练速度、可解释性以及是否支持迁移学习等因素,在决策树(及树集成)与神经网络之间做出选择。

你已经完成了高级学习算法课程的所有视频内容,感谢你坚持学习到这里,祝贺你!你现在已经学会了如何构建和使用神经网络与决策树,并听取了许多关于如何使这些算法为你良好工作的实用建议和技巧。

然而,即使你已经了解了监督学习的全部内容,那也只是学习算法能力的一部分。监督学习需要带有标签Y的训练数据集。还有另一套非常强大的算法,称为无监督学习算法,你甚至不需要标签Y,算法就能找出数据中非常有趣的模式并对其进行处理。我期待在专项课程的第三门也是最后一门关于无监督学习的课程中再次见到你。

在结束本课程之前,我希望你也能在实践测验和实验练习中享受实践决策树思想的乐趣。祝你实验顺利!或者,对于那些可能是《星球大战》粉丝的同学,让我说:愿森林与你同在

105:吴恩达与克里斯·曼宁谈自然语言处理发展史 🧠💬

概述

在本节课中,我们将跟随吴恩达(Andrew Ng)与斯坦福大学教授克里斯·曼宁(Chris Manning)的对话,回顾自然语言处理(NLP)领域从基于规则的系统到现代大规模语言模型的发展历程。我们将了解关键的技术转折点、核心思想以及未来展望。


从语言学博士到NLP先驱 🔄

吴恩达首先介绍了他的老朋友与合作者克里斯·曼宁教授。曼宁教授是斯坦福大学计算机科学教授、斯坦福人工智能实验室主任,同时也是自然语言处理领域被引用次数最多的研究者之一。

尽管曼宁教授今天在机器学习和NLP领域成就斐然,但他的学术起点却截然不同。他的博士学位实际上是在语言学领域,专注于研究语言的句法。那么,他是如何从研究句法转变为一名NLP研究者的呢?

曼宁教授指出,他至今仍兼任语言学教授,偶尔也会教授纯粹的语言学课程。他最初对人类语言及其运作方式、人们如何理解和使用语言深感兴趣。这种兴趣很早就引导他开始思考如今我们视为机器学习或计算思想的概念。

人类语言的两个核心问题吸引了他:儿童如何习得语言,以及成人如何进行流畅的交流。这些问题促使他很早就开始接触机器学习。他意识到,人类在成长过程中可以学会完全不同的语言,这非常神奇,而机器是否也能学习语言呢?


学术背景与早期研究 📚

曼宁教授在本科阶段实际上学习了三个专业:数学、计算机科学和语言学。在申请研究生时,他同时申请了卡内基梅隆大学(因其在计算语言学方面的实力)和斯坦福大学。最终,他选择在斯坦福大学攻读语言学博士学位,因为当时计算机科学系还没有自然语言处理方向。

90年代初,NLP领域正处于变革前夕。当时的主流是基于规则的、逻辑的声明式系统。但与此同时,数字化的文本和语音材料(如法律文件、报纸文章、议会记录)开始大量出现。曼宁教授敏锐地意识到,从海量的人类语言材料中进行实证研究,必将带来激动人心的成果,这将他引向了新型的自然语言处理研究,并塑造了他后续的职业生涯。

可以说,他的职业生涯最初更偏向语言学,但随着数据、机器学习和实证方法的兴起,逐渐转向了NLP和机器学习。


什么是自然语言处理(NLP)? ❓

NLP代表自然语言处理。另一个有时使用的术语是计算语言学,两者基本是同一概念。

自然语言处理这个术语本身有点奇怪,因为它意味着我们处理的是人类语言。作为计算机科学家,当我们说“语言”时,通常指的是编程语言,因此需要加上“自然”一词来特指人类使用的语言。

总体而言,自然语言处理是指对人类语言进行任何智能化的处理。这可以分解为理解人类语言、生成人类语言和习得人类语言。

人们通常从不同应用的角度来思考NLP,例如机器翻译、问答系统、生成广告文案、文本摘要等。由于人类世界的绝大部分信息都是通过语言材料处理和传递的,因此NLP的应用非常广泛。


从规则系统到统计方法的转变 📈

当曼宁教授开始攻读研究生时,大多数自然语言处理系统都是手工构建的。这些系统使用各种规则和推理程序来尝试构建对文本的理解路径。

例如,一条规则可能是描述英语句子的结构:通常由主语名词短语、动词和宾语名词短语组成。这有助于理解句子的含义。规则也可能涉及词义消歧,比如“star”这个词在电影语境下可能指演员,而非天体。

如今看来,这种方法似乎不太可能成功,但在当时是标准做法。只有当大量数字文本和语音材料可用时,人们才开始意识到可以走另一条路:计算人类语言材料的统计数据并构建机器学习模型。

曼宁教授在90年代中后期开始深入研究,早期的工作通常被称为“统计自然语言处理”,后来逐渐融入更广泛的概率人工智能和机器学习方法中。这一趋势一直持续到大约2010年。


深度学习在NLP中的兴起 🚀

大约在2010年,使用大型人工神经网络的深度学习新浪潮开始兴起。曼宁教授表示,他要感谢当时还在斯坦福全职工作的吴恩达,因为吴恩达对深度学习领域的新进展非常兴奋,并鼓励他关注神经网络。

曼宁教授在研究生时期上过戴夫·鲁梅尔哈特的神经网络课程,但并未将其用于自己的研究。在吴恩达的推动下,他和他的学生开始在NLP会议上发表最早的深度学习论文。最初,新想法很难被接受,一些论文被会议拒绝,转而发表在机器学习会议或深度学习研讨会上。但很快,情况开始改变,人们对神经网络产生了极大兴趣。

曼宁教授认为,神经网络时期(大约从2010年开始)本身可以分为两个阶段。第一阶段直到2018年左右,人们成功地将神经网络用于各种任务,如句法分析、情感分析、问答等。但这本质上是用更好的机器学习模型(神经网络)去做以前用逻辑回归或支持向量机做的同类任务。


2018年的转折点:大规模自监督模型 ⚡

曼宁教授认为,更大的变化发生在2018年左右。那时,人们开始利用海量人类语言材料构建大型自监督模型,如BERT和GPT及其后续模型。

这些模型仅仅通过在海量文本上进行词语预测,就获得了关于人类语言的惊人知识。回顾过去,这很可能被视为一个更大的分水岭,真正改变了NLP的工作方式。

在大型语言模型趋势兴起之前,曼宁教授等人的研究工作(如GloVe词向量)已经令人印象深刻。词向量通过神经网络学习一组数字(向量)来表示一个单词。GloVe等工作简化了数学原理,使计算机能够学习到词语含义的细微差别。

这些词向量已经展示了自监督学习的思想:仅使用大量文本,就能构建出对词语含义有深刻理解的模型。这为后来发展到能够理解整段文本和上下文含义的BERT、GPT等模型奠定了基础。


预测下一个词是“AI完备”的任务吗? 🤔

预测下一个词这个简单的任务,其效果令人惊讶。曼宁教授解释说,虽然任务本身是给定上文预测下一个词,但要想做得尽可能好,模型实际上需要理解句子的其余部分,知道“谁对谁做了什么”,甚至需要理解世界知识。

例如,如果文本是“斐济使用的货币是__”,那么模型就需要一些世界知识才能给出正确答案。因此,优秀的模型既能学习句子结构和含义,也能学习关于世界的事实。这使得预测下一个词有时被称为“AI完备”任务——即你需要无所不知才能完美完成它。

吴恩达询问曼宁教授是否认为预测下一个词是AI完备的。曼宁教授表示不完全同意,他认为人类还有其他非语言性的能力,如数学洞察力或解决三维现实世界难题的能力。但另一方面,语言确实比一些人想象的更接近普遍性,因为我们用语言描述和思考世界的方方面面,学习语言使用也间接学习了世界的许多侧面。


NLP的未来展望与挑战 🔮

大型语言模型在过去几年取得了令人兴奋的成功。曼宁教授描述了典型的两阶段过程:首先通过预测下一个词在海量文本上预训练一个大模型;然后针对特定任务(如问答、摘要、检测有害内容)进行微调。预训练模型的语言知识使其能够快速泛化,用少量标注数据就能达到以前需要大量数据的效果。

更近期的激动人心的工作甚至超越了微调。通过“提示”或“指令”方法,用户可以直接用自然语言(可能附带例子或明确指令)告诉模型要做什么,模型就能执行。即使对于有30年NLP研究经验的曼宁教授来说,这效果也令人惊叹。

关于提示工程是否是未来的方向,曼宁教授认为它既是未来的方式,但目前人们也在进行大量“黑客式”的措辞调整以让模型更好工作。他希望随着发展,这种对措辞的依赖会减少,就像人与人交流一样,无需纠结具体用词。基本方向是,人类语言将成为指示计算机操作的指令语言,取代菜单、按钮或编写代码。


数据驱动与结构化知识的平衡 ⚖️

吴恩达提出了一个关于未来技术路线的问题:在依赖数据驱动的机器学习与融入手工编码的约束或显式结构之间,平衡点将在哪里?

曼宁教授认为,毫无疑问,从数据中学习是未来的方向。但他也认为,拥有更多结构、更多归纳偏置、能够利用语言本质的模型仍有空间。当前的Transformer模型本质上是一个巨大的关联机器,它从海量数据(数百亿词)中吸收一切关联。这种规模化策略非常有效,但也显示出人类学习从有限数据中提取信息的效率要高得多。

曼宁教授不认为未来的改进会来自人们显式地将传统语言规则加入系统。相反,像Transformer这样的模型正在自己发现语言的结构。它们从未被明确告知主语、宾语等概念,但通过训练,它们学到了语言学家花数十年发现的语言结构事实。


给进入AI/NLP领域者的建议 💡

对于想要进入机器学习、AI或NLP领域的人,曼宁教授认为现在是一个绝佳的时机。软件和计算机科学正在基于机器学习被重塑,各行各业都有自动化、利用人类语言解读等机会。

以下是他的具体建议:

打好基础
学习机器学习的核心方法,理解如何从数据构建模型、设置损失函数、进行训练和诊断错误。这些核心技能对NLP尤其相关。

掌握关键模型
了解常用的特定模型,特别是我们今天讨论很多的Transformer。它们正越来越多地应用于机器学习的其他领域,如视觉、生物信息学甚至机器人学。

理解问题领域
学习一些关于人类语言和问题本质的知识。即使不直接将语言规则编码进系统,了解语言中可能发生的情况、需要注意什么以及可能想要建模什么,仍然是一项有用的技能。


跨学科背景如何进入AI领域 🌉

曼宁教授本人就是从语言学背景进入AI的。现在,来自各行各业的人都想开始从事AI工作。他认为可以从许多不同的起点以不同的方式切入。

利用现有工具
一个令人惊叹的转变是,现在有非常优秀的软件包用于神经网络建模。这些软件易于使用,你不需要理解很多高深的技术细节,只需要对机器学习的概念、如何训练模型以及如何通过输出的数字判断模型是否正常工作有一个高层面的理解。甚至很多高中生也能入门。

掌握必要数学
但如果你想达到更深层次,真正理解底层原理,一定的数学基础是必不可少的。深度学习基于微积分,需要优化函数。如果没有这方面的背景,最终可能会遇到瓶颈。

何时学习都不晚
即使你主修历史或非数学的心理学,如果决定要学习这些新模型,去修一门微积分课程也为时不晚。曼宁教授自己的故事就是例证:尽管他现在在斯坦福工程学院任职,但他的博士学位是语言学,他凭借一些数学、语言学知识和编程能力,成功转向了构建AI模型。


抽象层与底层理解 🛠️

吴恩达提出了一个关于现代编码框架(如TensorFlow或PyTorch)及其自动微分等功能,是否降低了对微积分理解需求的问题。

曼宁教授表示肯定。在早期(2010-2015年),他们需要手工计算每个模型的导数,然后用代码实现。而现在,构建深度学习模型完全不需要知道这些。在他自己的深度学习课程中,他们仍然会花两周时间讲解矩阵微积分和雅可比矩阵,确保学生理解反向传播的原理,但之后整个课程都使用PyTorch,学生再也不需要手动计算导数了。

这里存在一个关于技术基础需要多深的问题。就像今天的计算机科学家是否需要理解电子学、晶体管或CPU内部原理一样,这很复杂。一方面,了解更底层的知识有时有助于解决问题或把握新机遇(如吴恩达早期将机器学习移植到GPU上)。另一方面,大多数人必须信任某些抽象层,并且如今大多数神经网络建模工作确实可以在完全不懂微积分的情况下完成。

吴恩达补充道,抽象层的可靠性决定了你需要深入底层修复问题的频率。就像我们不需要懂量子物理也能使用计算机一样,如果排序函数API足够可靠,我们也不需要理解其内部原理。我们正站在巨人的肩膀上,事物每个月都在变得更加复杂和令人兴奋。


总结 🎯

本节课中,我们一起回顾了自然语言处理从基于规则的系统到统计方法,再到深度学习和大规模语言模型波澜壮阔的发展历程。我们了解了克里斯·曼宁教授从语言学转向NLP研究的个人旅程,探讨了NLP的核心定义、关键的技术转折点(如2018年自监督模型的兴起),以及预测下一个词是否AI完备等深刻问题。

我们还展望了NLP的未来,包括提示工程、数据驱动与结构化知识的平衡,并获得了给进入该领域的学习者和跨背景研究者的宝贵建议。最后,我们讨论了现代软件抽象层如何改变了对底层数学知识的需求。

NLP领域仍有大量工作等待完成,欢迎更多人加入这个激动人心的领域,共同推动世界向前发展。

106:欢迎来到无监督学习、推荐系统与强化学习 🎉

在本节课中,我们将要学习吴恩达《机器学习》系列第三门也是最后一门课程的核心内容。这门课程将超越监督学习,介绍无监督学习、推荐系统与强化学习三大关键技术。这些技术是当今许多重要商业应用的核心,掌握它们将帮助你成为机器学习领域的专家。


课程概述 📚

在前两门课程中,我们重点学习了监督学习。在第三门也是最后一门课程中,我们将探讨一系列超越监督学习的新技术。这些技术将为你提供一套强大的额外工具,丰富你的技能库。完成本课程和整个系列后,你将有望成为机器学习领域的专家。

让我们开始具体了解。


第一周:无监督学习 🧩

上一节我们介绍了本课程的整体框架,本节中我们来看看第一周的内容。

第一周我们将从无监督学习开始。你将学习聚类算法和异常检测。

以下是第一周的核心学习目标:

  • 聚类算法:这是一种将数据分组到不同簇中的方法。
  • 异常检测:这是一种识别数据中异常点或模式的技术。

这两种技术如今被许多公司用于重要的商业应用中。到本周末,你将了解这些算法的工作原理,并能够自己动手实现它们。


第二周:推荐系统 🎬

了解了无监督学习的基础后,本节中我们来看看第二周的主题。

第二周,你将学习推荐系统。当你访问在线购物网站或视频流媒体网站时,它是如何向你推荐产品或电影的?

以下是关于推荐系统的关键信息:

  • 推荐系统是商业上最重要的机器学习技术之一。
  • 它驱动着价值数千亿美元的商品或服务的流转。
  • 尽管非常重要,但这项技术在学术界获得的关注却出人意料地少。

在第二周,我希望你能学会这些系统的工作原理,并能够为自己实现一个推荐系统。

如果你对在线广告系统的工作原理感到好奇,对推荐系统的描述也会让你了解那些大型在线广告技术公司是如何决定向你展示哪些广告的。


第三周:强化学习 🤖

在掌握了推荐系统之后,本节中我们进入最后一周的学习。

在第三周也是本课程的最后一周,你将学习强化学习。你可能在新闻中读到过,强化学习在玩各种电子游戏方面表现出色,甚至超越了人类。我自己也曾多次使用强化学习来控制各种不同的机器人。

以下是强化学习的特点:

  • 强化学习是一项新兴技术。
  • 其商业应用的数量远不及本周或前两门课程中涵盖的其他技术。
  • 它是一项令人兴奋的技术,正在为你能够用学习算法实现的功能开辟新疆界。

在最后一周,你将亲自实现强化学习,并用它来操控一个模拟的月球着陆器。当你在课程后期看到自己的代码成功运行时,我想你会对强化学习所能实现的功能印象深刻。


总结与展望 🚀

本节课中我们一起学习了本门课程(机器学习系列第三门课)的路线图。我们概述了接下来三周的核心内容:无监督学习推荐系统强化学习。这些技术将极大地扩展你解决现实世界问题的能力。

我非常高兴能与你一同探讨无监督学习、推荐系统和强化学习。让我们进入下一个视频,开始学习一种重要的无监督学习算法——聚类算法。

107:02_01_01_什么是聚类 🧩

在本节课中,我们将要学习聚类这一核心概念。聚类是一种无监督学习算法,它能够自动发现数据中相似或相关的点,并将它们分组。我们将通过对比有监督学习来理解聚类的特点,并了解其常见的应用场景。


什么是有监督学习?📊

上一节我们提到了聚类是无监督学习。为了更好地理解它,我们先回顾一下你已经熟悉的有监督学习。

在有监督学习中,我们拥有一个包含输入特征 X 和对应标签 Y 的训练集。例如,对于一个二元分类问题,我们的数据集可能包含特征 X1X2,以及标签 y(通常用 01 表示)。我们可以将数据绘制在图上,并使用逻辑回归或神经网络等算法来学习一个决策边界。

公式表示:训练集为 {(x^(1), y^(1)), (x^(2), y^(2)), ..., (x^(m), y^(m))},其中 x 是特征,y 是标签。

什么是无监督学习?🔍

与有监督学习不同,在无监督学习中,我们只拥有输入特征 X,而没有对应的标签 Y

因此,当我们绘制数据时,图上只有一堆点,而没有像“X”和“O”这样的符号来区分不同的类别。由于没有目标标签 y,我们无法告诉算法什么是“正确的”答案。相反,我们要求算法自己去发现数据中有趣的结构或模式。

什么是聚类算法?🎯

聚类算法是无监督学习中最先接触到的算法之一。它专门用于寻找数据中的一种特定结构:将数据点分组到不同的“簇”中

具体来说,聚类算法会观察类似上图的数据,并尝试判断这些数据是否可以分成几个内部相似的点群。例如,它可能发现一个数据集实际上由两个不同的簇组成。

核心任务:给定一个无标签的数据集 {x^(1), x^(2), ..., x^(m)},聚类算法将其划分为 K 个簇。

聚类的应用有哪些?🌐

以下是聚类算法在现实世界中的几个应用实例:

  • 新闻文章分组:将内容相似的新闻文章(例如,关于熊猫的不同报道)自动归类在一起。
  • 市场/用户细分:例如,分析在线学习平台的用户,根据他们的学习目标(如提升技能、职业发展、了解AI影响)将其分成不同的群体,以便提供更有针对性的帮助。
  • 基因数据分析:通过分析不同个体的基因表达数据,将具有相似特征的人分组,用于疾病研究或种群分析。
  • 天文数据分析:天文学家利用聚类将太空中的天体分组,以分析哪些天体属于同一个星系,或识别太空中的特定结构。

如今,聚类技术被广泛应用于上述领域以及更多其他场景。


总结与预告 📝

本节课中我们一起学习了聚类的基本概念。我们了解到聚类是一种无监督学习算法,其目标是在没有标签的数据中自动发现相似的数据点并将它们分组。我们通过对比有监督学习(有X和y)和无监督学习(只有X)来明确了聚类的特点,并列举了它在新闻、市场、基因和天文等多个领域的实际应用。

在下一节视频中,我们将深入探讨最常用的聚类算法——K均值算法,一起来看看它是如何工作的。

108:K均值算法直观理解 🧠

在本节课中,我们将学习K均值聚类算法的直观工作原理。我们将通过一个具体的例子,一步步地展示算法如何将未标记的数据点自动分组为不同的簇。


算法概述

K均值算法是一种迭代算法,用于将数据集划分为K个不同的簇。其核心思想是:首先随机初始化K个簇中心点(称为“质心”),然后重复执行两个步骤,直到质心的位置不再发生变化。

上一节我们介绍了聚类的基本概念,本节中我们来看看K均值算法的具体执行过程。

算法步骤详解

让我们通过一个包含30个未标记数据点的例子来理解K均值算法。

第一步:随机初始化质心

算法首先会随机猜测两个簇中心的位置。在这个例子中,我们要求算法寻找两个簇(K=2)。初始的质心位置用红色叉号和蓝色叉号表示。这只是一个随机的初始猜测,可能并不准确,但它是一个起点。

第二步:重复执行两个核心操作

K均值算法会反复执行两个不同的操作:

  1. 将每个数据点分配给最近的质心
  2. 根据分配结果,移动每个质心到其所属点的平均位置

以下是这两个步骤的详细说明。

操作一:分配数据点到质心

算法会遍历数据集中的每一个数据点(例如,x1 到 x30),并检查该点距离红色质心(红叉)更近,还是距离蓝色质心(蓝叉)更近。然后,将该点分配给距离更近的那个质心。

为了直观展示,我会根据每个点距离哪个质心更近,将其涂成红色或蓝色。

例如,上方的点距离红色质心更近,因此被涂成红色;而下方的点距离蓝色质心更近,因此被涂成蓝色。

操作二:移动质心到平均位置

完成分配后,算法会查看所有被标记为红色的点,计算它们的平均位置,然后将红色质心(红叉)移动到这个平均位置。对蓝色点执行相同的操作:计算所有蓝色点的平均位置,并将蓝色质心(蓝叉)移动到那里。

这样,我们就得到了两个更新后、位置可能有所改善的质心。

迭代过程

现在,我们有了新的质心位置,算法将重复上述两个步骤:

  1. 重新分配:再次遍历所有30个训练样本,根据新的质心位置,重新判断每个点距离红色还是蓝色质心更近,并更新其颜色。由于质心移动了,一些点的颜色可能会改变。
  2. 重新计算质心:再次根据当前所有红色点的位置计算新的红色质心,根据所有蓝色点的位置计算新的蓝色质心,并移动它们。

如果持续重复这两个步骤,最终数据点的颜色和质心的位置将不再发生变化。此时,我们说K均值算法已经收敛

在这个例子中,算法成功地将上方的点识别为一个簇,将下方的点识别为另一个簇。


核心概念总结

本节课中我们一起学习了K均值算法的直观流程。其核心是两个迭代步骤,可以用伪代码概括如下:

重复执行直到收敛 {
    # 步骤一:分配点
    for i = 1 to m (m个数据点) {
        c^(i) := 距离点 x^(i) 最近的质心的索引 (从1到K)
    }
    # 步骤二:移动质心
    for k = 1 to K {
        μ_k := 所有被分配到簇k的点的坐标平均值
    }
}

其中:

  • c^(i) 表示第 i 个数据点被分配到的簇的编号。
  • μ_k 表示第 k 个簇的质心坐标。

课程总结

在本节课中,我们通过一个生动的例子,直观地理解了K均值聚类算法的工作原理。算法的两个关键步骤是:将每个点分配给最近的质心,以及将每个质心移动到其所属点的平均位置。通过反复迭代这两个步骤,算法能够自动将数据分组,直到结果稳定。

在下一节视频中,我们将学习如何形式化地定义这两个步骤,并写出完整的K均值算法。让我们继续前进到下一个视频。

109:K均值算法 🧠

在本节课中,我们将详细学习K均值算法的具体步骤。我们将了解算法如何初始化、如何将数据点分配给簇中心,以及如何更新簇中心的位置。通过本教程,你将能够自己实现这个算法。


算法概述

K均值算法是一种迭代聚类方法,旨在将数据点划分为K个簇。其核心思想是:首先随机初始化K个簇中心,然后重复执行两个步骤——将每个点分配给最近的簇中心,并根据分配结果重新计算每个簇的中心位置。

算法步骤详解

上一节我们了解了K均值算法的直观运行过程,本节中我们来看看其具体的数学和代码实现步骤。

1. 随机初始化簇中心

第一步是随机初始化K个簇中心,记作 μ₁, μ₂, ..., μₖ

  • 在之前的例子中,这对应于我们为红色十字(簇中心1)和蓝色十字(簇中心2)随机选择位置。
  • 簇中心 μ 是与训练样本具有相同维度的向量。例如,如果每个训练样本有 n=2 个特征,那么 μ₁μ₂ 也是二维向量。

2. 迭代执行分配与移动

初始化后,K均值算法将重复执行以下两个步骤。

步骤一:分配点到簇中心

这一步是为每个数据点“上色”,即将其分配给最近的簇中心。

以下是其数学描述:对于 i = 1m 的每一个训练样本 x⁽ⁱ⁾,我们计算:

c⁽ⁱ⁾ = argminₖ ||x⁽ⁱ⁾ - μₖ||²

其中:

  • c⁽ⁱ⁾ 是样本 x⁽ⁱ⁾ 被分配到的簇的索引(1 到 K)。
  • ||x⁽ⁱ⁾ - μₖ|| 表示 x⁽ⁱ⁾ 与簇中心 μₖ 之间的欧几里得距离(L2范数)。
  • 我们寻找使该距离最小的 k 值,这等价于寻找最近的簇中心。实际实现中,通常最小化平方距离,因为结果相同且计算更方便。

具体示例:如果一个点更接近红色簇中心(μ₁),则其 c⁽ⁱ⁾ 被设为 1;如果更接近蓝色簇中心(μ₂),则 c⁽ⁱ⁾ 被设为 2。

步骤二:移动簇中心

第二步是根据分配结果,更新每个簇中心的位置。

以下是其数学描述:对于 k = 1K 的每一个簇,我们计算:

μₖ = (1 / |Sₖ|) * Σ_{i ∈ Sₖ} x⁽ⁱ⁾

其中:

  • Sₖ 是所有被分配到簇 k 的训练样本的集合。
  • |Sₖ| 是该集合中样本的数量。
  • 新的 μₖ 就是所有属于该簇的点的平均值(均值向量)。

具体示例:对于所有被标记为红色的点(c=1),计算它们在横轴(特征1)和纵轴(特征2)上坐标的平均值,这个平均值点就是红色簇中心的新位置。蓝色簇中心的更新同理。

3. 一个特殊情况

在移动簇中心时,可能会遇到一个特殊情况:某个簇没有被分配到任何数据点(即 |Sₖ| = 0)。

  • 此时,计算平均值的公式将除以零,没有定义。
  • 最常见的处理方法是直接移除这个空簇,最终得到 K-1 个簇。
  • 如果必须保留 K 个簇,也可以选择随机重新初始化这个簇中心的位置,希望在下一次迭代中它能分配到一些点。

算法的应用场景

尽管我们主要用分离良好的簇来演示K均值,但它也经常应用于数据点并非明显分离的场景。

示例:假设你是一名T恤设计师,需要决定小号、中号、大号T恤的尺寸。你收集了潜在客户的身高和体重数据,这些数据在二维空间中是连续分布的,没有清晰的边界。

  • 尽管如此,运行K均值算法(例如设置K=3)仍可能将这些点大致分为三个组。
  • 这三个簇的中心可以为你提供三种尺寸最需要适配的“代表性”身高和体重,从而帮助你科学地制定尺寸标准。

这个例子说明,即使数据没有形成严格分离的群组,K均值算法也能提供有用的结果。


总结

本节课中我们一起学习了K均值聚类算法的完整流程:

  1. 随机初始化 K 个簇中心。
  2. 重复迭代两个核心步骤:
    • 分配:将每个数据点分配给最近的簇中心。
    • 移动:根据分配结果,将每个簇中心移动到其所属所有点的平均值位置。
  3. 讨论了空簇的特殊情况及处理方法。
  4. 了解了算法在数据非明显聚类场景下的实际应用

这个算法通过不断优化簇内点的紧密程度来工作。那么,这个算法最终会收敛吗?它究竟在优化什么目标?为了更深入地理解K均值算法,并探究其收敛的原因,我们将在下一节中看到,K均值实际上是在优化一个特定的代价函数。让我们在下一个视频中一探究竟。

110:K-Means 优化目标 🎯

在本节课中,我们将学习K-Means聚类算法的优化目标。我们将了解K-Means算法实际上是在最小化一个特定的成本函数,并探讨这个成本函数如何指导算法的两个核心步骤。


在之前的课程中,您已经看到许多监督学习算法通过定义一个成本函数,并使用梯度下降或其他算法来优化它。K-Means算法也不例外,它也在优化一个特定的成本函数,尽管它使用的优化算法不是梯度下降,而是您在上一课中看到的迭代过程。

让我们来看看K-Means的成本函数是什么。

首先,我们回顾一下使用的符号。c^(i) 表示训练样本 x^(i) 当前被分配到的簇的索引(从1到K)。μ_k 表示第k个簇中心的位置。我们引入一个新符号 μ_c^(i),它表示样本 x^(i) 被分配到的那个簇中心的位置。

例如,如果我们看第10个训练样本 x^(10)c^(10) 会告诉我们它被分配到了哪个簇(比如红色或蓝色),那么 μ_c^(10) 就是那个簇中心的位置。

基于这个符号,K-Means算法最小化的成本函数 J 可以定义为:

J(c^(1), ..., c^(m), μ_1, ..., μ_K) = (1/m) * Σ_{i=1}^{m} ||x^(i) - μ_c^(i)||²

换句话说,K-Means的成本函数是每个训练样本 x^(i) 与其被分配到的簇中心 μ_c^(i) 之间距离平方的平均值。

对于上面的例子,我们需要计算 x^(10)μ_c^(10) 之间的距离平方,并将其作为求平均的项之一。K-Means算法所做的就是尝试找到样本的分配 c^(i) 和簇中心的位置 μ_k,以最小化这个平方距离。


从视觉上看,在之前视频的K-Means运行过程中,成本函数的计算方式是:查看每一个蓝色点,测量它们到蓝色簇中心的距离并计算平方;同样地,查看每一个红色点,测量它们到红色簇中心的距离并计算平方。然后,对所有红点和蓝点的这些距离平方值求平均,就得到了在当前参数配置下成本函数 J 的值。

算法的每一步都会尝试更新簇分配 c^(1)c^(m)(在这个例子中是30个),或者更新簇中心 μ_1μ_2 的位置,以持续降低这个成本函数 J。顺便提一下,这个成本函数 J 在文献中也被称为畸变函数


现在,让我们更深入地看看算法,以及为什么算法要最小化这个成本函数 J(或畸变)。上面我复制了前一页的成本函数公式。

事实证明,K-Means算法的第一部分(将点分配给最近的簇中心)是在尝试更新 c^(1)c^(m),以在固定 μ_1μ_K 的情况下,尽可能最小化成本函数 J

而算法的第二步(移动簇中心)则是在尝试固定 c^(1)c^(m),但更新 μ_1μ_K,以尽可能最小化成本函数或畸变。

让我们看看为什么是这样。

第一步:分配点

在第一步中,如果你想选择 c^(i) 的值来最小化 ||x^(i) - μ_c^(i)||²,你应该怎么做?这个表达式是训练样本 x^(i) 与其被分配到的簇中心之间的距离平方。

为了最小化这个距离平方,你应该将 x^(i) 分配给最近的簇中心。举一个简化的例子,如果你有两个簇中心(μ_1μ_2)和一个训练样本 x^(i)。如果你把它分配给簇中心1,这个距离平方会很大;如果你把它分配给更近的簇中心2,这个距离平方会小得多。因此,为了最小化这一项,你会将 x^(i) 分配给更近的簇中心,这正是算法所做的。

所以,将点分配给簇中心的步骤,就是在选择 c^(i) 的值以尝试最小化 J,同时暂时不改变 μ_k

第二步:移动簇中心

第二步是移动簇中心。事实证明,将 μ_k 设置为分配给该簇的所有点的平均值,是能够最小化成本函数 J 的选择。

再举一个简化的例子。假设一个簇只分配了两个点,如下图所示。如果簇中心在当前位置,两个距离平方的平均值是 (1² + 9²)/2 = 41。但如果你将簇中心移动到这两个点的中间位置(即平均值 (1+11)/2 = 6),那么两个距离平方的平均值就变成了 (5² + 5²)/2 = 25,这比41小得多。实际上,你可以尝试改变簇中心的位置,会发现取分配给该簇所有点的平均值位置,确实是使距离平方和最小的值。


K-Means算法在优化成本函数 J 这一事实意味着它保证会收敛。在每一次迭代中,畸变成本函数应该下降或保持不变。如果它没有下降或保持不变(在最坏的情况下甚至上升),那就意味着代码中存在错误,因为K-Means的每一步都在设置 c^(i)μ_k 的值以试图减少成本函数。

此外,如果成本函数停止下降,这也为你提供了一种判断K-Means是否已经收敛的方法。一旦有一次迭代中成本函数保持不变,通常就意味着K-Means已经收敛,你应该停止运行算法。在某些罕见情况下,你可能需要运行K-Means很长时间,而畸变下降得非常缓慢。这有点像梯度下降,运行更长时间可能有点帮助,但如果成本函数下降的速度变得非常非常慢,你也可以认为它已经足够接近收敛,不必花费更多的计算周期来运行更长时间。

因此,计算成本函数有助于你判断算法是否已经收敛。

事实证明,利用成本函数还有另一个非常有用的方法,那就是使用多个不同的随机初始化的簇中心。这样做通常可以帮助你找到更好的聚类结果。我们将在下一个视频中看看如何做到这一点。


本节课总结

本节课我们一起学习了K-Means聚类算法的优化目标。我们了解到K-Means通过最小化一个称为畸变的成本函数 J 来工作,该函数衡量了每个数据点与其所属簇中心之间距离平方的平均值。我们详细分析了算法的两个步骤——分配点和移动簇中心——是如何分别针对固定另一组参数来最小化这个成本函数的。理解这个优化目标不仅解释了算法的工作原理,还为我们提供了判断算法收敛性和改进聚类结果(例如通过多次随机初始化)的重要工具。

111:K均值初始化 🎯

在本节课中,我们将要学习K均值聚类算法的第一步:如何初始化聚类中心。我们将探讨随机初始化的具体方法,以及如何通过多次运行算法来获得更好的聚类结果。


K均值聚类算法的第一步是随机选择位置作为聚类中心μ₁到μ_K的初始猜测。但具体如何执行这个随机猜测呢?本节视频将展示这一过程,并介绍如何通过多次尝试不同的初始中心μ₁到μ_K,从而找到更优的聚类结果。

初始化方法详解

上一节我们介绍了K均值算法的整体流程,本节中我们来看看如何具体实现其第一步。

运行K均值时,聚类中心的数量K应始终小于训练样本数M。如果K大于M,则没有足够的训练样本来为每个聚类中心分配至少一个样本,这没有意义。

在之前的例子中,我们设K=2,M=30。

选择聚类中心最常见的方法是随机选择K个训练样本。以下是一个训练集示例:

如果随机选择两个训练样本,可能会选中这一个和这一个。然后,我们将μ₁到μ_K设置为这K个训练样本的位置。例如,我可能在这里初始化红色聚类中心,并在那里初始化蓝色聚类中心。

当K=2时,如果这是你的随机初始化,运行K均值后,算法很可能会判定这些是数据集中的两个聚类。

需要注意的是,这种初始化聚类中心的方法与之前视频演示中使用的略有不同。在之前的演示中,我将聚类中心μ₁和μ₂初始化为随机点,而不是位于特定训练样本之上。那样做是为了让演示更清晰,但本幻灯片展示的才是实际更常用的初始化方法。

初始化的影响与局部最优

使用这种方法,有可能出现红色十字在这里、蓝色十字在那里的初始化情况。根据随机初始聚类中心的选择,K均值最终会为数据集找到不同的聚类集合。

让我们看一个稍复杂的例子:

在这个数据集中,我们尝试找到三个聚类,即K=3。

如果使用一种随机初始化运行K均值,可能会得到上方的结果。这看起来是一个相当好的选择,将数据很好地分成了三个不同的聚类。

但如果使用另一种初始化,比如恰好将两个聚类中心初始化在这组点内,一个初始化在另一组点内,运行K均值后,可能会得到这种聚类结果。这看起来就不那么理想了。

这实际上是一个局部最优解。K均值试图最小化失真代价函数,即上一视频中看到的J(c₁, ..., c_M, μ₁, ..., μ_K)。但由于这种不太幸运的随机初始化选择,算法恰好陷入了局部最小值。

这是另一个局部最小值的例子,不同的随机初始化导致K均值找到了这种将数据分成三个聚类的方式。同样,它看起来不如上方那个结果好。

多次随机初始化策略

如果你想给K均值多次机会来寻找最佳局部最优解,如果你想尝试多种随机初始化,从而有更好的机会找到上方那种良好的聚类,你可以对K均值算法做一件事:多次运行它,然后尝试找到最佳的局部最优解。

事实证明,如果你运行K均值三次,并得到三种不同的聚类结果,那么在这三种解决方案中进行选择的一种方法是:计算所有三种解决方案(或K均值找到的这三种聚类选择)的代价函数J。

然后根据哪个能给出最小的代价函数J值,从这三种中选择一个。

实际上,如果你观察上方这种聚类分组,绿十字与绿点的距离平方和相对较小,红十字与红点的距离也较小,蓝十字同理。因此,对于上方的例子,代价函数J会相对较小。

但在下方的例子中,蓝十字到所有蓝点的距离较大,红十字到所有红点的距离也较大。这就是为什么下方这些例子的代价函数J会更大,也是为什么如果你从这三个选项中选择失真最小、代价函数J最小的那个,最终会选择上方的聚类中心选择方案。

让我更正式地将此写成一个算法,其中你将使用不同的随机初始化多次运行K均值。

以下是算法步骤:

如果你想使用100次随机初始化运行K均值,那么你需要:

  1. 运行100次循环。
  2. 使用本视频前面看到的方法随机初始化K均值:选择K个训练样本,并让聚类中心初始位置为这K个训练样本的位置。
  3. 使用该随机初始化,运行K均值算法直至收敛。这将给你一组聚类分配和聚类中心。
  4. 最后,按如下方式计算失真度,即计算代价函数。

完成此过程(例如100次)后,最终选择给出最低成本的聚类集合。

事实证明,如果这样做,通常能给你一组好得多的聚类,其失真函数值远低于只运行一次K均值得到的结果。

实践建议

当我使用这种方法时,我在这里填入了数字100。

通常,运行此过程大约50到1000次是相当常见的。如果运行次数远超过1000次,计算成本往往会变得很高,并且多次运行后的收益会递减。而尝试至少50或100次随机初始化,通常比只尝试一次随机初始化能得到好得多的结果。

使用这种技术,你更有可能最终得到上方那种良好的聚类选择,而不是下方那些较差的局部最小值。因此,当我亲自使用K均值时,我几乎总是使用多次随机初始化,因为这能让K均值在最小化失真代价函数和寻找更好的聚类中心方面做得更好。


在本节课中,我们一起学习了K均值聚类算法的初始化步骤。我们了解到,随机选择K个训练样本作为初始中心是常见做法,但单次运行可能陷入局部最优。通过多次随机初始化并选择代价函数J最小的结果,可以显著提高找到高质量聚类方案的概率。通常建议运行50到1000次以获得稳定良好的结果。

在结束K均值的讨论之前,还有一个视频将探讨如何选择聚类中心的数量K。让我们进入下一个视频来了解这一点。

112:如何选择聚类数量?🤔

在本节课中,我们将要学习一个关键问题:在使用K均值算法时,如何确定最佳的聚类数量K。K均值算法需要你输入一个参数K,即你希望找到的类别数量。但如何决定是使用2个、3个、5个还是10个类别呢?让我们一起来探讨。


聚类数量选择的模糊性

对于许多聚类问题,K的正确值通常是模糊的。如果向不同的人展示同一个数据集,并询问他们看到了多少个聚类,答案可能会不同。有些人可能会说有两个明显的聚类,而另一些人可能会看到四个。他们都是对的。因为聚类是一种无监督学习算法,没有给定的“正确答案”标签可供复制。在许多应用中,数据本身并不能清晰地指示其中包含多少个聚类。

例如,对于某些数据,很难明确判断它包含两个、三个还是四个聚类。


肘部法则

在K均值的学术文献中,有一些技术可以尝试自动为特定应用选择聚类数量。这里简要介绍一种你可能看到别人提到的方法——肘部法则,尽管我个人并不常用它。

肘部法则的操作步骤如下:

  1. 使用不同的K值多次运行K均值算法。
  2. 绘制成本函数J(或称为畸变函数)随聚类数量K变化的曲线。

公式:成本函数J
J(c, μ) = (1/m) * Σ ||x^(i) - μ_c(i)||²
其中,m是样本数,x^(i)是第i个样本,μ_c(i)是该样本所属簇的中心点。

通常你会发现,当聚类数量很少时(例如K=1),成本函数J的值会很高。随着K值的增加,成本函数J会下降,曲线可能如下图所示。

如果曲线呈现这样的形态:成本函数在K=3之前快速下降,之后下降速度明显变缓,那么我们就可以选择K=3。这种方法之所以被称为“肘部法则”,是因为曲线的拐点形状类似于人的手肘。

通过绘制成本函数J随K变化的曲线,可以帮助你获得一些洞察。然而,我个人很少使用肘部法则来选择聚类数量,因为对于许多应用,正确的类别数量本身就是模糊的,而且很多成本函数曲线是平滑下降的,并没有一个清晰的“肘部”拐点可供选择K值。

一个重要提醒不能通过选择使成本函数J最小化的K值来决定K。因为这样做几乎总是会导致你选择可能的最大K值,因为更多的类别几乎总是能降低J值。因此,最小化J值并不是一个好的选择K值的技术。


实践中的选择方法

那么在实践中如何选择K值呢?通常,你运行K均值是为了获得聚类,以便用于后续的某些目的。也就是说,你将利用这些聚类来做一些事情。

我通常推荐的做法是:根据K均值算法对后续下游任务的执行效果来评估和选择K值

让我用一个T恤尺码的例子来说明。

  • 方案一:你可以在此数据集上运行K均值,找到3个聚类。这样你可能会得到类似下图的聚类结果,并据此确定小、中、大号T恤的尺码。
  • 方案二:你也可以用K=5运行K均值,可能会得到类似下图的聚类结果,从而将T恤尺码分为特小号、小号、中号、大号和特大号。

这两种方案都是对数据进行聚类的完全有效且合理的方式。但是,选择使用3个还是5个聚类,现在可以基于对你的T恤业务是否有意义来决定。

这里存在一个权衡:

  • 合身程度:尺码越多(5个),T恤的合身性可能越好。
  • 成本:但生产和运输5种不同类型的T恤,会比3种类型带来额外的成本。

因此,在这种情况下,我会分别用K=3和K=5运行K均值,然后审视这两种解决方案。基于“更多尺码带来更好合身性”与“制造更多T恤带来额外成本”之间的权衡,来决定哪种方案对T恤业务更有意义。


另一个应用:图像压缩

在编程练习中,你还会看到K均值在图像压缩中的应用。这实际上是K均值最有趣的可视化示例之一。

在那里,你将看到另一个权衡:

  • 压缩图像质量:图像看起来有多好。
  • 压缩程度:为了节省空间,图像能被压缩到多小。

在那个编程练习中,你可以利用这种权衡,根据你希望图像看起来多好与你希望压缩后的图像文件有多小,来手动决定最佳的K值。


课程总结

本节课中我们一起学习了如何为K均值聚类算法选择合适的聚类数量K。我们了解到:

  1. 聚类数量的选择通常是模糊的,没有绝对正确的答案。
  2. 介绍了肘部法则,但也指出了其局限性。
  3. 学习了实践中更有效的方法:根据聚类结果对后续实际业务目标(如T恤尺码划分、图像压缩质量与大小的权衡)的贡献来评估和选择K值。

恭喜你学会了第一个无监督学习算法!现在你不仅了解如何进行监督学习,也掌握了无监督学习。希望你在实践练习中也能感受到乐趣,这确实是我所知最有趣的练习之一。

接下来,我们将准备学习第二个无监督学习算法——异常检测,即如何从数据中发现不寻常或异常的事物。这被证明是无监督学习在商业上最重要的应用之一,我本人在许多不同应用中多次使用过它。让我们在下一个视频中继续探讨异常检测。

113:异常检测算法 🕵️

在本节课中,我们将要学习第二种无监督学习算法——异常检测。我们将了解它的工作原理、核心概念以及它在现实世界中的广泛应用。

算法概述

上一节我们介绍了无监督学习的基本概念,本节中我们来看看异常检测算法。

异常检测算法观察一组未标记的正常事件数据集,从而学会检测或标记出是否存在不寻常或异常的事件。

一个具体例子

为了更好地理解,让我们看一个例子。我的一些朋友曾使用异常检测来检测飞机制造公司生产的飞机发动机可能存在的问题。

当一家公司制造飞机发动机时,你确实希望该发动机可靠且运行良好,因为飞机发动机故障会带来非常严重的后果。

因此,我的一些朋友使用异常检测来检查制造后的飞机发动机是否看起来异常,或者是否存在任何问题。

以下是具体思路。当一架飞机发动机从装配线下线后,你可以计算该发动机的多个不同特征。

例如,特征 X1 测量发动机产生的热量,特征 X2 测量振动强度,依此类推,还可以有更多特征。

但为了简化说明,我将只使用两个特征 X1X2,分别对应发动机的热量和振动。

事实证明,飞机制造商并不会制造那么多有问题的发动机。因此,更容易收集的数据类型是:如果你制造了 M 台飞机发动机,收集这 M 台发动机运行时的特征 X1X2,很可能其中大多数都是正常的发动机,而不是有缺陷的。

异常检测问题定义

异常检测问题是:在学习算法已经了解了这 M 个关于飞机发动机通常如何运行(即产生多少热量和振动)的例子之后,如果一台全新的飞机发动机从装配线下线,并且它有一个由 X_test 给出的新特征向量,我们想知道:这台发动机看起来与之前制造的发动机相似吗?所以它可能没问题吗?或者这台发动机是否有什么非常奇怪的地方,可能导致其性能值得怀疑?这意味着也许我们应该在让它被安装在飞机上之前更仔细地检查它,希望不会出任何问题。

算法工作原理

以下是异常检测算法的工作原理。让我在这里绘制示例 x1xM,用这些叉号表示,图中的每个数据点对应一台具有特定热量和振动量的特定发动机。

如果这台新的飞机发动机 x_test 从装配线下线,并且你要绘制它的 x1x2 值,如果它在这里,你会说,好吧,这可能没问题,看起来与其他飞机发动机非常相似。也许我不需要担心这个。

但是,如果这台新飞机发动机的热量和振动特征,比如说,远在下方这里,那么这个位于下方的数据点看起来与我们在顶部看到的那些非常不同,因此我们可能会说,哎呀,这看起来像是一个异常,这看起来不像我以前见过的例子,我们最好在让这台发动机安装在飞机上之前更仔细地检查它。

如何用算法解决这个问题?

执行异常检测最常见的方法是通过一种称为密度估计的技术。

这意味着,当你获得包含这 M 个示例的训练集时,你要做的第一件事是建立一个模型,用于表示 X 的概率 P(X)。换句话说,学习算法将尝试找出哪些特征值 X1X2 具有高概率,以及哪些值在数据中出现的可能性较低或概率较低。

在我们这里的这个例子中,我认为在那个中间的小椭圆内看到例子的可能性相当大,所以中间那个区域会有高概率。也许这个椭圆内的事物概率稍低一些。这个椭圆或这个椭圆形内的事物概率更低,而外部的事物概率则更低。如何根据训练集决定哪些区域概率更高、哪些更低的具体细节,我们将在接下来的几个视频中看到。

在学习了 P(X) 的模型之后,当你获得新的测试示例 X_test 时,你要做的是计算 P(X_test) 的概率。如果它很小,或者更准确地说,如果它小于某个我将称为 ε 的小数(这是一个希腊字母 epsilon,你应该把它看作一个小数),这意味着 P(X) 非常小,或者换句话说,你看到的某个用户的特定 X 值相对于你见过的其他用户来说非常不可能。

但如果 P(X_test) 小于某个小阈值或某个小数 ε,我们会发出一个标志,表示这可能是一个异常。例如,如果 X_test 远在这里,一个例子落在这里的概率实际上相当低,因此希望对于这个 X_test 值,P(X_test) 将小于 ε,这样我们就会将其标记为异常。

相反,如果 P(X_test) 不小于 ε,如果 P(X_test) 大于等于 ε,那么我们会说它看起来没问题,这看起来不像异常,这对应于如果你有一个例子在这里,比如说 A,我们的模型 P(X) 会说靠近中间这里的例子实际上概率相当高,新飞机发动机具有接近这些内部椭圆特征的可能性非常大,因此对于这些例子,P(X) 会很大,我们会说它没问题,不是异常。

异常检测的应用

异常检测如今在许多应用中使用,它经常用于欺诈检测。例如,如果你运行一个具有许多不同功能的网站,如果你计算 X_i 作为用户 i 活动的特征,特征的示例可能包括:该用户登录的频率、他们访问了多少网页、他们进行了多少交易、或者他们在讨论论坛上发了多少帖子、他们的打字速度是多少?他们似乎每秒能打多少个字符?有了这样的数据,你就可以从数据中建模 P(X),以模拟给定用户的典型行为。

在欺诈检测的常见工作流程中,你不会仅仅因为一个账户看起来异常就自动关闭它,而是可能会要求安全团队进行更仔细的检查,或设置一些额外的安全检查,例如要求用户通过手机号码验证身份,或要求他们通过验证码来证明他们是人类,等等。但像这样的算法如今被常规使用,以试图发现不寻常或可能有点可疑的活动,这样你就可以更仔细地筛查那些账户,以确保没有欺诈行为,这种类型的欺诈检测既用于发现虚假账户,也经常用于尝试识别金融欺诈。例如,如果存在一种非常不寻常的购买模式,那么这可能值得安全团队进行更仔细的审视。

异常检测也经常用于制造业。你在上一张幻灯片中看到了飞机制造的例子。但是,在许多大洲的许多工厂中,许多制造商都常规使用异常检测来查看他们刚刚制造的从飞机发动机到印刷电路板、智能手机、电机等许多东西,看看你是否刚刚制造了一个行为有些奇怪的单元。因为这可能表明你的飞机发动机或印刷电路板等存在问题,可能会让你想在将该物品运送给客户之前进行更仔细的检查。

它还用于监控集群和数据中心的计算机,其中如果 X_i 是某台机器 i 的特征,例如,如果特征捕获了内存使用情况、每秒磁盘访问次数、CPU负载,特征也可以是比率,例如 CPU 负载与网络流量的比率。那么,如果某台特定的计算机表现得与其他计算机非常不同,可能值得查看一下那台计算机,看看是否有什么问题,例如它是否发生了硬盘故障或网络故障,或者有什么问题,或者也许它被黑客入侵了。

异常检测是那些应用非常广泛的算法之一,尽管你似乎不常听到人们谈论它。我记得我第一次从事异常检测的商业应用是当我帮助一家电信公司实施异常检测,以查看任何一个蜂窝基站是否以异常方式运行,因为这可能意味着该蜂窝基站有问题,所以他们希望派技术人员去查看,希望更多的人能获得良好的手机信号覆盖。我也曾使用异常检测来发现欺诈性金融交易,如今我经常用它来帮助制造公司发现他们可能制造但应该更频繁检查的异常零件。因此,它是你工具箱中一个非常有用的工具,在接下来的几个视频中,我们将讨论如何构建这些算法并让它们为你工作。

核心算法组件

为了使异常检测算法工作,我们需要使用高斯分布来对数据 P(X) 进行建模。所以让我们进入下一个视频来讨论高斯分布。


本节课中我们一起学习了异常检测算法的基本概念、工作原理及其在欺诈检测、制造业监控等领域的广泛应用。我们了解到,该算法的核心是通过密度估计(通常使用高斯分布)来建模正常数据的概率分布 P(X),并通过比较新数据点的概率与阈值 ε 来判断其是否为异常。

114:高斯正态分布 🧮

在本节课中,我们将学习高斯分布,也称为正态分布。这是构建异常检测算法的基础。我们将了解其数学定义、参数含义,以及如何从数据中估计这些参数。


高斯分布的定义与公式

上一节我们提到了异常检测,本节中我们来看看其核心工具——高斯分布。

高斯分布描述了一个随机变量 x 取值的概率分布。它由两个参数决定:均值 μ 和方差 σ²。其概率密度函数 P(x) 的图像呈钟形曲线,中心在 μ 处,宽度由 σ 决定。

该分布的概率密度函数公式如下:

P(x) = (1 / (√(2π) σ)) * e^(-( (x - μ)² / (2σ²) ))

其中,π 是圆周率,e 是自然常数。这个公式精确地描述了钟形曲线的形状。


参数 μσ 的影响

理解了公式后,我们来看看改变参数 μσ 会如何影响分布的形状。

以下是不同参数组合下高斯分布的变化:

  • μ = 0, σ = 1:这是标准正态分布,曲线以0为中心,宽度适中。
  • μ = 0, σ = 0.5:曲线仍以0为中心,但变得更窄、更高。因为概率曲线下总面积恒为1,所以变窄就必须变高。
  • μ = 0, σ = 2:曲线以0为中心,但变得更宽、更矮。
  • μ = 5, σ = 0.5:曲线形状与 σ=0.5 时相同,但中心向右平移到了 μ=5 的位置。

从数据中估计参数

在实际应用中,我们通常有一个包含 m 个样本的数据集,需要从中估计出最合适的 μσ²

给定数据集后,我们可以通过以下公式计算参数的估计值:

  • 均值 μ 的估计公式为:μ = (1/m) * Σ (xⁱ),即所有样本值的平均值。
  • 方差 σ² 的估计公式为:σ² = (1/m) * Σ ( (xⁱ - μ)² ),即每个样本与均值之差的平方的平均值。

在统计学中,这两个公式被称为参数 μσ² 的“最大似然估计”。有些教材会建议方差公式的分母使用 m-1 而非 m,但在实际机器学习应用中,两者差异很小,使用 m 即可。


与异常检测的联系

现在,我们可以将高斯分布与异常检测联系起来。

当我们从训练数据中估计出 μσ²,并得到对应的高斯分布 P(x) 后,对于任何一个新样本 x_test,我们可以计算其概率 P(x_test)

  • 如果 x_test 落在概率较高的区域(靠近 μ),则 P(x_test) 值较高,我们认为它是一个正常样本。
  • 如果 x_test 落在概率很低的区域(远离 μ 的尾部),则 P(x_test) 值很低,我们就有理由认为它是一个异常点。

从单特征到多特征

以上讨论基于 x 是单个数字(即只有一个特征)的情况。

然而,对于实际的异常检测问题,我们通常拥有多个特征。在下一节视频中,我们将把单变量高斯分布的概念扩展,构建一个能够处理多个特征的、更复杂的异常检测算法。


本节课中我们一起学习了高斯(正态)分布。我们掌握了其数学公式,理解了参数 μσ 如何影响分布形状,学会了如何从数据中估计这些参数,并初步了解了如何利用计算出的概率 P(x) 来识别异常点。这是构建异常检测系统的关键第一步。

115:异常检测算法 🚨

在本节课中,我们将学习如何构建一个异常检测算法。我们将从高斯分布的基础出发,逐步推导出完整的算法流程,并通过一个具体例子来理解其工作原理。


概述

异常检测算法的核心思想是:通过对正常数据特征的分布进行建模,计算新数据点属于该分布的概率。如果概率低于某个阈值,则将该点标记为异常。

上一节我们介绍了高斯(正态)分布如何描述单个特征。本节中,我们将利用高斯分布来构建一个完整的异常检测系统。


算法步骤

以下是构建异常检测系统的具体步骤。

1. 选择特征

首先,需要选择你认为可能指示异常的特征 ( x_i )。例如,在飞机引擎的例子中,我们选择了热量和振动两个特征。

2. 拟合参数

假设我们有 ( m ) 个训练样本 ( x^{(1)}, x^{(2)}, ..., x^{(m)} ),每个样本有 ( n ) 个特征。对于每个特征 ( j ),我们需要估计其高斯分布的参数:均值 ( \mu_j ) 和方差 ( \sigma_j^2 )。

计算公式如下:

  • 均值 ( \mu_j ):特征 ( j ) 在所有训练样本上的平均值。
    [
    \mu_j = \frac{1}{m} \sum_{i=1}^{m} x_j^{(i)}
    ]
  • 方差 ( \sigma_j^2 ):特征 ( j ) 与其均值之差的平方的平均值。
    [
    \sigma_j^2 = \frac{1}{m} \sum_{i=1}^{m} (x_j^{(i)} - \mu_j)^2
    ]

如果有向量化实现,可以一次性计算所有特征的均值向量 ( \mu ):
[
\mu = \frac{1}{m} \sum_{i=1}^{m} x^{(i)}
]

3. 计算新样本的概率

对于一个新样本 ( x ),我们计算其概率 ( p(x) )。我们假设各个特征之间是统计独立的,因此联合概率是每个特征概率的乘积:
[
p(x) = \prod_{j=1}^{n} p(x_j; \mu_j, \sigma_j^2)
]
其中,每个特征的概率由高斯分布公式给出:
[
p(x_j; \mu_j, \sigma_j^2) = \frac{1}{\sqrt{2\pi}\sigma_j} \exp\left(-\frac{(x_j - \mu_j)2}{2\sigma_j2}\right)
]

4. 判断异常

最后,将计算出的概率 ( p(x) ) 与一个预先设定的阈值 ( \epsilon ) 进行比较:

  • 如果 ( p(x) < \epsilon ),则将该样本标记为异常。
  • 如果 ( p(x) \ge \epsilon ),则认为该样本正常。

算法的直观理解是:如果新样本的任何一个特征值相对于训练集中看到的分布来说过大或过小,都会导致该特征的概率 ( p(x_j) ) 非常小,从而使整体乘积 ( p(x) ) 变小,最终被判定为异常。


算法示例

让我们通过一个具体例子来理解算法的工作过程。

假设我们有一个数据集,包含两个特征 ( x_1 ) 和 ( x_2 )。

  • 特征 ( x_1 ) 的均值 ( \mu_1 = 5 ),方差 ( \sigma_1^2 \approx 4 )(标准差 ( \sigma_1 \approx 2 ))。
  • 特征 ( x_2 ) 的均值 ( \mu_2 = 3 ),方差 ( \sigma_2^2 = 1 )(标准差 ( \sigma_2 = 1 ))。

这对应于 ( x_1 ) 的一个较宽的高斯分布和 ( x_2 ) 的一个较窄的高斯分布。将两个特征的概率相乘,我们得到联合概率分布 ( p(x) ) 的曲面图。曲面中心区域(特征值接近均值)的概率较高,边缘区域(特征值远离均值)的概率较低。

现在考虑两个测试样本:

  1. 测试样本1 ( x_{test}^{(1)} ):位于数据密集区域。
  2. 测试样本2 ( x_{test}^{(2)} ):位于数据稀疏的偏远区域(例如 ( x_1=8, x_2=0.5 ))。

设定阈值 ( \epsilon = 0.02 )。

  • 计算得 ( p(x_{test}^{(1)}) \approx 0.04 > \epsilon ),算法判定其为正常。
  • 计算得 ( p(x_{test}^{(2)}) \approx 0.0021 < \epsilon ),算法判定其为异常。

结果符合预期:算法认为位于训练数据分布中心附近的点正常,而远离训练数据分布的点可能是异常。


总结

本节课中我们一起学习了异常检测算法的构建过程。我们从选择特征开始,通过高斯分布对每个特征的正常范围进行建模,并计算新样本属于该联合分布的概率。通过设定阈值 ( \epsilon ),我们可以系统性地识别出具有异常特征组合的数据点。

下一节,我们将深入探讨如何选择阈值 ( \epsilon ),以及如何评估异常检测系统的性能。

116:开发与评估异常检测系统 🚀

在本节课中,我们将学习如何开发并评估一个异常检测系统。我们将探讨如何利用少量带标签的异常数据来指导算法开发,并介绍评估系统性能的实用方法。

上一节我们介绍了异常检测算法的基本原理。本节中,我们来看看如何系统地开发和评估一个异常检测系统。

开发异常检测系统的实用技巧

开发学习算法时,例如选择不同的特征或调整参数(如 ε),如果有一种方法可以评估算法性能,那么做出决策就会容易得多。这种方法有时被称为实数评估。如果你能通过改变特征或参数,并计算出一个数值来判断算法是变好还是变坏,那么就能更容易地决定是否保留这次改动。

引入带标签的数据

尽管我们主要讨论的是无标签数据,但为了评估,我们需要稍微改变一下假设。我们假设拥有一些带标签的数据,其中包含少量已知的异常样本。

  • 对于已知的异常样本,我们为其关联标签 y = 1
  • 对于我们认为正常的样本,我们为其关联标签 y = 0

异常检测算法学习所用的训练集仍然是这个无标签的训练集 {x^(1), ..., x^(m)}。我们假设所有这些样本都是正常的(即 y = 0)。在实践中,即使有少数异常样本混入训练集,算法通常也能正常工作。

划分数据集

为了评估算法,拥有少量异常样本非常有用,这样我们就可以创建交叉验证集和测试集。

以下是数据集划分的一个示例:

假设你制造飞机引擎多年,收集了10000个正常引擎的数据和20个有缺陷(异常)引擎的数据。我们将数据划分如下:

  • 训练集:6000个正常引擎。
  • 交叉验证集:2000个正常引擎 + 10个已知异常引擎。
  • 测试集:2000个正常引擎 + 10个已知异常引擎。

系统开发流程

以下是开发和评估异常检测系统的步骤:

  1. 训练模型:在训练集上训练算法,拟合高斯分布。
  2. 在交叉验证集上评估:查看算法能正确标记出多少个异常引擎。你可以使用交叉验证集来调整参数 ε,也可以增删或调整特征 x_j
  3. 在测试集上最终评估:在调整好参数和特征后,在独立的测试集上评估算法,看它能发现多少个异常引擎,以及错误地将多少正常引擎标记为异常。

另一种数据划分方案

当异常样本数量极少时(例如只有2个),另一种常见的做法是只使用训练集和交叉验证集,而不单独划分测试集。

  • 优点:在数据量极少时,这是更可行的方案。
  • 缺点:由于没有独立的测试集,你无法公平地评估算法在未来数据上的泛化性能,存在过拟合交叉验证集的风险。

如何评估算法性能

在交叉验证集或测试集上评估算法的具体步骤如下:

  1. 在训练集上拟合模型 p(x)
  2. 对于交叉验证集或测试集中的任何一个样本 x,计算 p(x)
  3. 进行预测:
    • 如果 p(x) < ε,则预测 ŷ = 1(异常)。
    • 如果 p(x) ≥ ε,则预测 ŷ = 0(正常)。
  4. 将预测结果 ŷ 与真实标签 y 进行比较,评估准确性。

由于异常检测的数据分布通常是高度偏斜的(异常样本远少于正常样本),除了准确率,还可以考虑使用以下评估指标:

  • 真阳性、假阳性、假阴性、真阴性
  • 精确率、召回率、F1分数

这些指标能更好地衡量算法在大量正常样本中发现少数异常样本的能力。

总结

本节课中我们一起学习了如何开发和评估异常检测系统。核心要点是,即使主要使用无监督学习,拥有少量带标签的异常样本对于指导参数调整和特征选择至关重要。我们介绍了数据集的标准划分方法,以及在数据极少时的替代方案,并讨论了在偏斜数据分布下如何选择合适的评估指标。这使得构建一个实用的异常检测系统变得更加高效和可靠。

这引出了一个问题:既然有了一些带标签的样本,为什么还要使用无监督学习?为什么不直接使用监督学习算法呢?在下一节视频中,我们将比较异常检测与监督学习,并探讨在何种情况下应优先选择其中一种方法。

117:异常检测与监督学习对比 🎯

在本节课中,我们将学习如何在实际应用中,根据数据集的特点,在异常检测算法和监督学习算法之间做出合适的选择。我们将通过对比两种方法的核心思想、适用场景以及具体案例,帮助你建立一个清晰的决策框架。

概述

当你的数据集中只有少量正例(y=1)和大量负例(y=0)时,选择使用异常检测还是监督学习是一个需要仔细权衡的问题。本节将分享一些思考和具体建议,帮助你在这两类算法之间做出选择。

异常检测的适用场景

上一节我们介绍了异常检测的基本概念,本节中我们来看看它最适合在什么情况下使用。

异常检测算法通常在以下情况下是更合适的选择:你拥有非常少的正例(例如0到20个正例的情况并不少见),以及相对大量的负例。这些负例用于构建概率模型 p(x)。需要记住的是,p(x) 的参数仅从负例中学习。正例数量极少,因此它们仅用于交叉验证集和测试集,以进行参数调整和模型评估。

以下是异常检测更适用的核心情况:

  • 正例(异常)类型繁多且未来可能出现全新类型:例如,飞机发动机可能有多种不同的故障方式,并且未来可能出现一种全新的、从未见过的故障模式。你手头少量的正例(如20个)可能无法涵盖所有可能的故障类型。这使得任何算法都难以从这少量正例中学习到“异常”或“正例”的完整特征。未来的异常可能与我们迄今为止见过的任何异常示例都完全不同。
  • 算法逻辑:异常检测算法观察正常的例子(即y=0的负例),并尝试建模它们的特征。任何与正常情况偏差很大的事物都会被标记为异常,即使它是数据集中从未出现过的一种全新故障模式

监督学习的适用场景

与异常检测不同,监督学习以另一种方式看待问题。

相比之下,如果你拥有数量较多的正例和负例,那么监督学习可能更适用。当你应用监督学习时,理想情况下,你希望有足够的正例让算法了解正例的特征。监督学习倾向于假设未来的正例很可能与训练集中的正例相似。

以下是监督学习更适用的核心情况:

  • 未来正例与历史正例相似:例如,在垃圾邮件检测中,虽然垃圾邮件有多种类型,但多年来,垃圾邮件通常试图推销类似的产品或将你引向类似的网站。因此,你在未来几天收到的垃圾邮件,很可能与你过去在训练集中见过的垃圾邮件相似。这就是为什么监督学习对垃圾邮件检测效果很好,因为它旨在检测更多你过去可能在训练集中见过的垃圾邮件类型。

具体案例对比

以下是几个具体领域的案例,展示了两种方法的不同适用性:

  • 金融欺诈检测:欺诈手段层出不穷,每年甚至每月都可能出现全新的欺诈形式。因此,异常检测常被用于寻找任何与过去交易记录不同的行为。
  • 制造业缺陷检测
    • 如果你想检测已知的、先前见过的缺陷(例如,智能手机外壳上的划痕是一种常见缺陷),并且你能获得足够多有划痕手机(y=1)的训练样本,那么监督学习效果很好,可以训练系统判断新生产的手机是否有划痕。
    • 如果你怀疑未来会出现全新的故障方式,那么异常检测效果更好。
  • 数据中心机器监控:如果机器被黑客入侵,其行为可能以一种全新的、不同于以往任何方式的形式表现出来,这更像一个异常检测应用。事实上,许多安全相关应用(因为黑客经常发现入侵系统的新方法)都会使用异常检测。
  • 天气预报:天气类型通常只有有限的几种(晴天、雨天、多云、下雪等),因为你反复看到相同的标签,所以天气预报往往是一个监督学习任务。
  • 疾病诊断:如果你想根据患者的症状判断其是否患有一种你以前见过的特定疾病,那么这也往往是一个监督学习应用。

总结

本节课中,我们一起学习了如何根据数据集特征选择异常检测或监督学习。

希望这为你提供了一个决策框架:当你拥有少量正例和大量负例时,是选择异常检测还是监督学习。异常检测试图发现可能是你前所未见的、全新的正例;而监督学习则观察你的正例,并试图判断未来的例子是否与你已经见过的正例相似。

需要指出的是,在构建异常检测算法时,特征的选择非常重要。在构建异常检测系统时,我通常会花一些时间来调整用于系统的特征。在下一个视频中,我将分享一些关于如何调整输入给异常检测算法的特征的实用技巧。

118:特征选择技巧 🎯

在本节课中,我们将学习如何为异常检测算法选择和优化特征。特征选择对于异常检测至关重要,因为算法仅从无标签数据中学习,难以自动识别哪些特征应被忽略。我们将探讨如何使特征更接近高斯分布,以及如何通过错误分析创建新特征来提升算法性能。


使特征更接近高斯分布 📊

上一节我们介绍了特征选择的重要性,本节中我们来看看如何通过变换使特征更接近高斯分布。高斯分布(即正态分布)的曲线对称且呈钟形,这有助于异常检测模型更好地拟合数据。

如果特征的分布不是高斯分布,我们可以尝试对其进行变换。以下是常见的变换方法:

  • 对数变换log(x)
  • 对数加常数变换log(x + C)
  • 平方根变换sqrt(x)x**0.5
  • 立方根变换x**(1/3)

在实际操作中,我们可以通过绘制直方图来观察特征分布,并尝试不同的变换参数,直到分布看起来更接近高斯分布。

以下是使用Python进行特征变换的示例代码:

import matplotlib.pyplot as plt
import numpy as np

# 假设x是原始特征数据
x = np.random.exponential(scale=2, size=1000)

# 绘制原始特征的直方图
plt.hist(x, bins=50, color='blue', alpha=0.7)
plt.title('Original Feature Distribution')
plt.show()

# 尝试对数变换
x_log = np.log(x + 0.001)  # 加一个小常数避免对0取对数
plt.hist(x_log, bins=50, color='green', alpha=0.7)
plt.title('Log Transformed Feature Distribution')
plt.show()

# 尝试幂变换
x_power = x**0.4
plt.hist(x_power, bins=50, color='red', alpha=0.7)
plt.title('Power Transformed Feature Distribution (0.4)')
plt.show()

通过不断调整参数,我们可以找到使特征分布最接近高斯分布的变换方式。请注意,对训练集应用的任何变换,都必须同样应用于交叉验证集和测试集。


通过错误分析创建新特征 🔍

在训练异常检测模型后,如果其在交叉验证集上表现不佳,我们可以通过错误分析来改进特征。错误分析的核心是检查那些被算法错误分类的样本,并思考如何通过新特征来区分它们。

例如,假设我们有一个特征x1(用户交易次数),但某个异常用户在此特征上与其他正常用户相似。如果我们发现该用户的打字速度异常快,就可以创建一个新特征x2(打字速度)。这样,异常用户在新特征x2上会表现出异常值,从而使算法更容易检测到。

以下是创建新特征的常见方法:

  • 组合现有特征:例如,x5 = CPU负载 / 网络流量
  • 创建多项式特征:例如,x6 = (CPU负载)**2 / 网络流量
  • 基于领域知识设计特征:例如,在数据中心监控中,可以创建“高CPU负载但低网络流量”的特征

通过添加这些新特征,我们可以使正常样本的P(x)值保持较大,而使异常样本的P(x)值变小,从而提高检测准确率。


总结与回顾 🏁

本节课中我们一起学习了异常检测中的特征选择与优化技巧。我们首先探讨了如何通过变换使特征更接近高斯分布,以提升模型拟合效果。随后,我们介绍了如何通过错误分析创建新特征,以区分那些难以被现有特征捕捉的异常样本。

特征工程是异常检测中的关键步骤,精心设计的特征可以显著提升算法性能。在实践中,建议结合领域知识,不断迭代和优化特征,以达到最佳检测效果。


下周我们将探讨推荐系统,了解其工作原理及如何构建。推荐系统是机器学习中商业价值极高的算法之一,掌握它将帮助你理解日常生活中的个性化推荐,并能够自行构建类似系统。请继续完成实验练习,我们下周再见!

119:推荐系统构建 🎬

在本节课中,我们将学习推荐系统的基本概念和构建方法。推荐系统在商业应用中具有巨大价值,能够根据用户的历史行为预测其可能喜欢的物品,从而提供个性化推荐。


推荐系统简介

推荐系统广泛应用于电商、流媒体和外卖平台,通过分析用户行为预测其偏好,从而提升用户体验和商业效益。本节将以电影评分预测为例,介绍推荐系统的基本框架。

数据表示与符号定义

在构建推荐系统时,我们需要明确用户、物品及评分数据的表示方式。以下是常用的符号定义:

  • 用户数量:用 NU 表示,例如 NU = 4 表示有 4 位用户。
  • 物品数量:用 NM 表示,例如 NM = 5 表示有 5 部电影。
  • 评分矩阵:用 Y 表示,Y[i][j] 表示用户 j 对电影 i 的评分。
  • 评分指示矩阵:用 R 表示,R[i][j] = 1 表示用户 j 对电影 i 进行了评分,否则为 0。

例如,用户 Alice(用户 1)对电影 1 的评分为 5 星,对电影 3 未评分,因此 Y[1][1] = 5R[3][1] = 0

推荐系统的目标

推荐系统的核心目标是预测用户对未评分物品的评分,从而推荐高分物品。例如,若预测用户对某部电影的评分为 5 星,系统可将该电影推荐给用户。

算法构建思路

在下一节中,我们将基于电影特征(如浪漫或动作类型)开发推荐算法。假设我们已知这些特征,可以构建模型预测用户评分。后续课程将探讨在没有特征的情况下如何实现推荐。


本节课中,我们一起学习了推荐系统的基本概念、数据表示方法以及系统目标。下一节将开始构建基于特征的推荐算法。

120:使用物品特征构建推荐系统 🎬

在本节课中,我们将学习如何利用物品(例如电影)的特征来构建一个推荐系统。我们将看到,当每个物品都有描述其特征的数值时,如何为不同用户训练模型来预测他们的评分。


数据集与特征介绍

上一节我们讨论了推荐系统的基本概念。本节中,我们来看看一个具体的例子。假设我们有一个包含四位用户和五部电影的数据集,用户对部分电影进行了评分。

现在,我们额外获得了每部电影的两个特征:

  • x₁:表示电影的“浪漫”程度。
  • x₂:表示电影的“动作”程度。

例如:

  • 电影《Love at last》非常浪漫,所以特征为 [0.9, 0]
  • 电影《Nonstop car chases》有一点浪漫,但动作场面非常多,所以特征为 [0.1, 1.0]

我们用以下符号表示:

  • n_u = 用户数量(此处为4)
  • n = 电影数量(此处为5)
  • n = 特征数量(此处为2)

因此,电影 i 的特征向量可以表示为 x⁽ⁱ⁾。例如,电影3(《Cute puppies of love》)的特征是 x⁽³⁾ = [0.99, 0]


为单个用户建立预测模型

让我们以第一位用户 Alice 为例,看看如何预测她对电影的评分。

我们可以为 Alice 建立一个类似于线性回归的模型。预测她对电影 i 的评分公式为:

预测评分 = w·x⁽ⁱ⁾ + b

其中:

  • w 是权重参数向量(与特征维度相同)。
  • b 是偏置参数。
  • x⁽ⁱ⁾ 是电影 i 的特征向量。

例如,如果我们为 Alice 学习到的参数是 w⁽¹⁾ = [5, 0]b⁽¹⁾ = 0,那么她对电影3的预测评分为:
w⁽¹⁾·x⁽³⁾ + b⁽¹⁾ = 5 * 0.99 + 0 * 0 = 4.95

这个预测是合理的,因为 Alice 之前给浪漫电影评分很高,而给动作电影评分很低。《Cute puppies of love》是一部浪漫电影,预测高分符合她的偏好。

由于我们有多个用户,每个用户都有自己独特的偏好,因此我们需要为每个用户 j 学习一套独立的参数 w⁽ʲ⁾b⁽ʲ⁾

通用的预测模型对于用户 j 和电影 i 可以写为:

预测评分 = w⁽ʲ⁾·x⁽ⁱ⁾ + b⁽ʲ⁾

这就像为数据集中的每个用户分别训练一个线性回归模型。


定义成本函数

为了学习参数 w⁽ʲ⁾b⁽ʲ⁾,我们需要定义一个成本函数。首先,回顾一下符号:

  • r(i, j) = 1 如果用户 j 对电影 i 评过分,否则为 0。
  • y⁽ⁱʲ⁾ = 用户 j 给电影 i 的实际评分。
  • m⁽ʲ⁾ = 用户 j 评过分的电影数量。

我们专注于单个用户 j。其成本函数基于均方误差,并只对用户实际评过分的电影求和:

J(w⁽ʲ⁾, b⁽ʲ⁾) = (1 / (2m⁽ʲ⁾)) * Σ [ (w⁽ʲ⁾·x⁽ⁱ⁾ + b⁽ʲ⁾ - y⁽ⁱʲ⁾)² ] + (λ / (2m⁽ʲ⁾)) * Σ [ (w_k⁽ʲ⁾)² ]

以下是该公式的组成部分说明:

  • 误差项(w⁽ʲ⁾·x⁽ⁱ⁾ + b⁽ʲ⁾ - y⁽ⁱʲ⁾)² 计算预测评分与实际评分的平方差。
  • 求和范围Σ 只对满足 r(i, j)=1 的电影 i 进行,即用户 j 评过分的电影。
  • 归一化1/(2m⁽ʲ⁾) 是均值归一化项,m⁽ʲ⁾ 是用户评分的电影数。
  • 正则化项(λ/(2m⁽ʲ⁾)) * Σ (w_k⁽ʲ⁾)² 用于防止过拟合,其中 λ 是正则化参数。

通过最小化这个成本函数 J(w⁽ʲ⁾, b⁽ʲ⁾),我们可以得到用户 j 的一组良好参数 w⁽ʲ⁾b⁽ʲ⁾

注:在推荐系统中,成本函数中的 m⁽ʲ⁾ 是一个常数,即使去掉它,优化得到的参数 wb 也是相同的。因此,为了简便,有时会省略它。


为所有用户学习参数

上一节我们定义了单个用户的成本函数。现在,我们将其扩展到所有用户。

为了学习所有用户的参数(w⁽¹⁾, b⁽¹⁾w⁽ⁿᵘ⁾, b⁽ⁿᵘ⁾),我们需要最小化所有用户成本函数的总和:

J = Σ [ J(w⁽ʲ⁾, b⁽ʲ⁾) ] (对 j 从 1 到 n_u 求和)

这个总成本函数 J 就是我们的最终目标函数。使用梯度下降或其他优化算法来最小化 J,我们就可以同时为所有用户学到一组良好的参数,用于预测他们对未评分电影的评分。

这个方法本质上是为每个用户训练一个独立的线性回归模型,所有模型的训练通过一个总成本函数联合进行。


总结与展望

本节课中我们一起学习了如何利用物品的特征来构建推荐系统。核心步骤包括:

  1. 定义模型:为每个用户 j 使用线性模型 w⁽ʲ⁾·x⁽ⁱ⁾ + b⁽ʲ⁾ 来预测其对物品 i 的评分。
  2. 构建成本函数:基于用户已有的评分,定义包含正则化项的均方误差成本函数。
  3. 联合优化:通过最小化所有用户成本函数的总和,来学习所有用户的参数。

这个方法的前提是我们需要事先知道每个物品的特征(x⁽ⁱ⁾)。然而,在实际应用中,我们可能无法获得足够详细或有效的特征。

在下一节课中,我们将探讨这个算法的改进版本。即使没有预先定义好的物品特征,我们也能通过学习来自动发掘这些特征,从而构建出强大的推荐系统。让我们继续学习。

121:协同过滤算法 🎬

在本节课中,我们将要学习协同过滤算法。这是一种在缺乏电影特征数据时,如何从用户评分中学习出这些特征,并同时优化用户参数和电影特征的方法。

概述

上一节我们介绍了如何利用已知的电影特征(如浪漫程度和动作程度)来预测用户评分。本节中我们来看看,如果这些特征未知,我们如何从用户评分数据中学习出这些特征,并构建一个完整的推荐模型。

从参数推导特征

假设我们有以下用户评分数据,但电影的特征 x1x2 是未知的(用问号表示)。

为了说明,我们假设已经通过某种方式学习了四个用户的参数:

  • 用户1:w1 = [5, 0], b1 = 0
  • 用户2:w2 = [5, 0], b2 = 0
  • 用户3:w3 = [0, 5], b3 = 0
  • 用户4:w4 = [0, 5], b4 = 0

预测用户 j 对电影 i 的评分公式为:w_j · x_i + b_j。为简化,本例中所有 b 均为0。

现在,我们尝试为电影1猜测一个合理的特征向量 x1。根据四位用户的评分:

  • 用户1(Alice)评分为5:w1 · x1 ≈ 5
  • 用户2(Bob)评分为5:w2 · x1 ≈ 5
  • 用户3评分为0:w3 · x1 ≈ 0
  • 用户4评分为0:w4 · x1 ≈ 0

一个可能的选择是令 x1 = [1, 0]。这样计算可得:

  • w1 · x1 = 5*1 + 0*0 = 5
  • w2 · x2 = 5*1 + 0*0 = 5
  • w3 · x1 = 0*1 + 5*0 = 0
  • w4 · x1 = 0*1 + 5*0 = 0

这个结果与观测到的评分一致。同理,我们可以利用已知的用户参数,为其他电影推导出特征向量 x2, x3 等,使得模型的预测尽可能接近用户的真实评分。

协同过滤之所以可行,是因为我们有多个用户对同一部电影进行了评分。这种“协作”使得我们能够推断出电影的特征。在典型的线性回归中,如果只有一个用户的数据,是无法凭空学习出特征的。

学习电影特征的成本函数

给定所有 nu 个用户的参数 w1...wnu, b1...bnu,如果我们想学习某部特定电影 i 的特征 xi,可以使用以下成本函数:

我们希望通过最小化这个成本函数来选择特征 xi,使得对所有给电影 i 评过分的用户 j,模型预测的评分 w_j · x_i + b_j 与实际评分 y(i,j) 的平方差最小。r(i,j)=1 表示用户 j 对电影 i 有评分。

最后,我们可以加入正则化项 (λ/2) * Σ (x_ik)² 以防止过拟合。

要学习数据集中所有 nm 部电影的特征 x1...xnm,只需将上述针对单部电影的成本函数对所有电影求和:

J = Σ (针对电影 i 的成本函数)

通过梯度下降或其他优化算法最小化这个总成本函数,我们就能为所有电影学习到一组良好的特征。这在机器学习中是很了不起的,因为特征通常需要外部提供,而此算法可以自动从数据中学习。

完整的协同过滤算法

到目前为止,我们假设用户参数 wb 是已知的。现在,我们将上一节学习 wb 的算法,与本节学习 x 的算法结合起来。

学习特征的成本函数(上图)与学习参数的成本函数包含相同的核心误差项。它们都是对所有存在评分的用户-电影对 (i, j) 求和。

因此,我们可以将两者合并,形成一个用于同时学习 w, bx 的总体成本函数:

J(w, b, x) = (1/2) * Σ (w_j · x_i + b_j - y(i,j))² + (λ/2) * Σ (w_jk)² + (λ/2) * Σ (x_ik)²
(求和范围:所有 r(i,j)=1(i, j) 对)

为了最小化这个关于 w, bx 的成本函数,我们可以使用梯度下降法。

以下是梯度下降的更新步骤(为简洁,下标表示略有不正式):

  1. 更新用户参数 wb
    w_j := w_j - α * (∂J/∂w_j)
    b_j := b_j - α * (∂J/∂b_j)
  2. 更新电影特征 x
    x_i := x_i - α * (∂J/∂x_i)

通过同时更新所有这些参数,算法能够找到较好的 w, bx 的值。在这个模型中,w, bx 都是需要学习的参数。

这个算法被称为协同过滤。其名称源于“协作”的理念:多个用户对同一部电影的评分行为,共同提供了关于这部电影特性的信息,使得算法能够推断出合适的电影特征。进而,这些特征可以用来预测其他尚未对该电影评分的用户未来的可能评分。

从星级评分到二元标签


到目前为止,我们的问题设定都使用了1到5星(或0到5星)的电影评分。推荐系统另一个非常常见的应用场景是处理二元标签,例如用户是否“喜欢”、“收藏”或与某个项目发生了交互。

在下一节视频中,我们将看看如何将目前看到的模型推广到二元标签的情况。

总结

本节课中我们一起学习了协同过滤算法的核心思想。我们从如何利用已知用户参数推导电影特征入手,定义了学习特征的成本函数。接着,我们将学习用户参数和学习电影特征的过程统一到一个整体的成本函数中,并通过梯度下降法同时优化所有参数(w, b, x)。这种方法的关键在于利用多个用户的评分数据“协同”地推断出项目和用户的潜在特征,从而实现对未知评分的预测。最后,我们了解到该算法可以进一步推广到处理二元交互数据。

122:17_02_04 二元标签(收藏、喜欢与点击)🎯

在本节课中,我们将学习推荐系统或协同过滤算法的一个重要应用场景:处理二元标签数据。我们将探讨如何将之前学习的算法推广到用户仅表达“喜欢”或“不喜欢”的场景,而非给出1到5星的评分。


从线性回归到二元标签预测 🔄

上一节我们介绍了基于评分的协同过滤算法。本节中,我们来看看当数据标签变为二元(例如“喜欢”/“不喜欢”)时,算法应如何调整。这个过程与我们之前在课程中从线性回归推广到逻辑回归、从预测数值到预测二元标签的思路非常相似。

以下是一个带有二元标签的协同过滤数据集示例。标签“1”表示用户喜欢或与某部电影产生了互动。

例如,标签1可能意味着爱丽丝完整观看了《Love at Last》和《Romance Forever》,但在播放了几分钟《Nonstop Car Chases》后决定停止观看并退出。或者,这可能意味着她在应用上明确点击了“喜欢”或“收藏”来表示她喜欢这些电影,但在查看了《Nonstop Car Chases》和《Swords vs. Karate》后没有点击喜欢。问号通常表示用户尚未观看该项目,因此他们无法决定是否喜欢它。

核心问题是:我们如何将上一视频中看到的协同过滤算法应用于此数据集?通过预测爱丽丝、鲍勃、卡罗尔和戴夫有多大可能喜欢他们尚未评分的项目,我们可以决定应该向他们推荐这些项目的程度。


二元标签的定义与应用场景 📊

在二元标签的协同过滤中,定义标签1、0和问号的方式有很多种。以下是几个常见的例子。

在购物网站上,标签可以表示用户J在接触到(被展示)某商品后是否选择购买它。

  • 1 表示用户购买了该商品。
  • 0 表示用户没有购买该商品。
  • 问号 表示用户甚至没有被展示过该商品。

在社交媒体环境中,标签1或0可以表示用户在看到内容后是否收藏或点赞了它,问号则表示他们尚未看到该内容。

许多网站不会要求用户进行显式评分,而是利用用户行为来推断用户是否喜欢某内容。例如:

  • 如果用户在某内容上花费了至少30秒,则分配标签 1,因为用户觉得该内容有吸引力。
  • 如果用户被展示了某内容但花费时间不足30秒,则分配标签 0
  • 如果用户尚未被展示该内容,则分配 问号

另一种根据用户行为隐式生成评分的方法是观察用户是否点击了某项目。这在在线广告中很常见:

  • 如果用户看到了广告并点击了它,则分配标签 1
  • 如果用户看到了但没有点击,则分配标签 0
  • 问号 则表示用户根本还没有看到过该广告。

通常,这些二元标签具有以下大致含义:

  • 标签1:用户在看到项目后产生了互动。互动可以指点击、花费30秒以上、明确收藏/点赞或购买。
  • 标签0:用户在看到项目后没有产生互动。
  • 问号:项目尚未展示给用户。

算法推广:从线性到逻辑模型 🧠

给定这些二元标签,我们来看看如何将之前几节中类似线性回归的算法推广到预测这些二元输出。

之前,我们使用公式 y(i,j) = w(j)·x(i) + b(j) 来预测评分 y(i,j),这很像线性回归模型。

对于二元标签,我们将预测 y(i,j) 等于1的概率,公式不再是 w(j)·x(i) + b(j),而是 g(w(j)·x(i) + b(j))。其中,g(z) 是逻辑函数:g(z) = 1 / (1 + e^(-z))。这就像我们在逻辑回归中看到的那样。

我们实际上是将一个类似线性回归的模型,转变为一个类似逻辑回归的模型,用于预测用户喜欢或与项目互动的概率。

为了构建这个算法,我们还需要将成本函数从平方误差成本函数修改为更适合二元标签、类似逻辑回归模型的成本函数。


修改成本函数 ⚖️

之前,我们使用的成本函数中,f(x) 项扮演着算法预测值的角色。

现在,对于二元标签 y(i,j)(取值为1、0或问号),预测值 f(x)w(j)·x(i) + b(j) 变成了 g(w(j)·x(i) + b(j)),其中 g 是逻辑函数。

类似于我们推导逻辑回归时,我们为单个样本写出了以下损失函数:如果算法预测为 f(x),真实标签为 y,则损失为:
L(f(x), y) = -y * log(f(x)) - (1 - y) * log(1 - f(x))
这有时也被称为二元交叉熵成本函数,也是我们训练神经网络处理二元分类问题时使用的标准成本函数。

为了使其适应协同过滤场景,我将写出新的成本函数,它是所有参数 WB 以及所有用户/项目特征参数 X 的函数。

现在,我们需要对所有满足 r(i,j)=1(i, j) 对求和(这与顶部的求和类似)。并且,我们不再使用平方误差成本函数,而是使用上面定义的损失函数 L(f(x), y(i,j))。其中,f(x) 是我对 g(w(j)·x(i) + b(j)) 的简写。

将这个代入,就得到了可用于二元标签协同过滤的成本函数。


总结与展望 📝

本节课中,我们一起学习了如何将类似线性回归的协同过滤算法推广到处理二元标签。这极大地扩展了该算法所能解决的应用范围。

现在,尽管你已经了解了算法的核心结构和成本函数,但还有一些实现技巧可以使你的算法效果更好。在下一节中,我们将看看如何实现此算法的一些细节,以及一些能使算法运行得更快的小修改。让我们进入下一个视频。

123:均值归一化 📊

在本节课中,我们将学习均值归一化(Mean Normalization)技术。这是一种在构建推荐系统时,用于预处理用户评分数据的方法。通过均值归一化,我们可以让协同过滤算法运行得更高效,并且在面对新用户(尚未对任何项目评分)时,能做出更合理的预测。

概述

在之前的线性回归课程中,我们已经了解到特征归一化可以帮助算法更快地运行。对于使用数字评分(如1到5星)的推荐系统,均值归一化同样重要。它通过调整评分数据,使其具有一致的均值,从而提升算法的效率和性能。

均值归一化的动机

假设我们有一个电影评分数据集,包含四位用户对五部电影的评分。现在,我们引入第五位用户 Eve,她尚未对任何电影评分。

如果我们直接在这个数据集上训练协同过滤算法,由于正则化项会促使参数变小,并且 Eve 的评分数据不参与成本函数计算,算法很可能会将 Eve 的参数 W5B5 都学习为 0。

这意味着,算法会预测 Eve 对所有电影的评分都是 0 星。这显然不是一个有意义的预测,因为我们更希望新用户的初始预测能基于其他用户的平均评分,而不是一个固定的零值。

均值归一化的步骤

为了解释均值归一化,我们首先将所有评分(包括未知的)放入一个矩阵中。

以下是均值归一化的具体操作步骤:

  1. 计算每部电影的平均评分:对于每一部电影,我们仅根据已评分的用户计算其平均分。
  2. 构建均值向量:将所有电影的平均分收集到一个向量中,我们称之为 mu
  3. 执行归一化:从每一个原始评分中,减去对应电影的平均分 mu[i]

经过上述步骤,我们得到一组新的 Y_ij 值。这些值将作为算法训练时使用的目标值。

预测时的调整

当我们使用学习到的参数 w_jb_j 为用户 j 预测电影 i 的评分时,公式为:
预测评分 = w_j · x_i + b_j

但是,由于我们在训练前从原始评分中减去了均值 mu[i],为了得到最终在原始评分尺度(如0-5星)上的预测值,我们必须将这个均值加回去:

最终预测评分 = (w_j · x_i + b_j) + mu[i]

对新用户的影响

现在,让我们看看这对新用户 Eve 意味着什么。由于 Eve 没有评分记录,算法很可能仍会学习到 W5 = 0B5 = 0

那么,对于电影1的预测将是:
最终预测评分 = (0 · x_1 + 0) + mu[1] = mu[1] = 2.5

这比预测所有电影为0星要合理得多。均值归一化的效果是,让新用户的初始预测值等于其他用户对该电影评分的平均值。

行归一化与列归一化

在上面的例子中,我们对矩阵的(即每部电影)进行了归一化,使其均值为零。这主要解决了新用户的预测问题。

另一种方法是归一化矩阵的(即每个用户),使其均值为零。这在处理全新项目(尚无任何评分)时可能有用。然而,在实践中,处理新用户的问题通常比处理新项目更为紧迫,因为一个新项目在获得足够评分前,可能根本不会被推荐给用户。因此,在本周的实践练习中,仅对行进行归一化通常就足够了。

总结

本节课我们一起学习了均值归一化技术。我们了解到,通过对评分数据进行均值归一化处理,不仅能使推荐算法运行得更快,更重要的是,它能显著提升算法在面对评分数据极少甚至没有评分的新用户时的预测合理性。这个实现细节将使你的推荐系统表现得更好。

在下一节视频中,我们将探讨如何亲自实现这一技术。

124:协同过滤的TensorFlow实现 🎬

在本节课中,我们将学习如何使用TensorFlow来实现协同过滤算法。我们将看到,TensorFlow不仅能用于构建神经网络,还能高效地实现其他类型的机器学习算法,例如协同过滤。其核心优势在于能够自动计算成本函数的导数,从而简化梯度下降等优化过程。


从线性回归到自动微分

上一节我们介绍了协同过滤的基本概念。本节中,我们来看看如何利用TensorFlow的自动微分功能来优化算法。

你或许习惯于将TensorFlow视为构建神经网络的工具,这确实是它的主要用途之一。然而,TensorFlow对于构建其他类型的学习算法,如协同过滤算法,也大有裨益。

我喜欢将TensorFlow用于此类任务的原因之一是:在许多应用中,为了实现梯度下降,你需要找到成本函数的导数。而TensorFlow可以自动为你计算出成本函数的导数。你只需实现成本函数本身,无需了解任何微积分知识或手动求导,仅用几行代码就能让TensorFlow计算出导数项,进而用于优化成本函数。

让我们看看这是如何运作的。你可能还记得课程1中的这张图,这正是我们讨论第一个线性回归示例时,优化参数 W 时所使用的图。当时我们设 B=0,因此模型预测为 f(x) = w * x。我们的目标是找到使成本函数 J 最小化的 W 值。

我们通过梯度下降更新来实现这一点,更新规则如下:

w := w - α * (∂J/∂w)

如果你也需要更新 B,你会使用类似的表达式。但若设 b=0,你只需忽略第二个更新项,并持续执行此梯度下降更新直到收敛。有时计算这个导数或偏导数项可能很困难,而TensorFlow正好可以在这方面提供帮助。


一个简单的TensorFlow示例

让我们通过一个简单的例子来理解。我将使用一个非常简单的成本函数:J = (w*x - 1)²。这里,w*x 是简化的 f_w(x),而 y 等于1。这就是我们的成本函数。

如果我们有 f(x) = w*xy=1(针对我们拥有的一个训练样本),并且我们不针对 B 进行优化,那么梯度下降算法将重复执行以下更新直到收敛:

w := w - α * (∂J/∂w)

事实证明,如果你实现了这里的成本函数 J,TensorFlow可以自动为你计算这个导数项,从而使梯度下降得以运行。

以下代码提供了一个高层次概述:

w = tf.Variable(3.0)  # 初始化参数 w 为 3.0
x = 1.0
y = 1.0
alpha = 0.01
iterations = 30

for i in range(iterations):
    with tf.GradientTape() as tape:
        f_w = w * x
        cost_J = (f_w - y) ** 2
    # 自动计算导数
    [dJ_dw] = tape.gradient(cost_J, [w])
    # 更新参数 w
    w.assign_sub(alpha * dJ_dw)

在这段代码中:

  • tf.Variable 将参数 w 初始化为 3.0,并告知 TensorFlow w 是我们想要优化的参数。
  • 我们设置 x=1, y=1,学习率 α=0.01,并运行30次梯度下降迭代。
  • tf.GradientTape() 是关键:它记录计算成本 J 所需的一系列操作,这是实现自动微分所必需的。
  • tape.gradient(cost_J, [w]) 会自动计算成本 J 关于 w 的导数,我们称之为 dJ_dw
  • 最后,我们通过 w.assign_sub(alpha * dJ_dw) 执行更新。对TensorFlow变量需要使用特殊的赋值方法。

请注意,借助TensorFlow的梯度功能,你需要做的主要工作是告诉它如何计算成本函数 J,其余的语法会使TensorFlow自动为你计算出导数。

通过这个过程,TensorFlow将从 w=3 开始(如图中虚线所示斜率),执行梯度步骤更新 w,然后重复计算导数和更新 w,最终达到 w=1 的最优值。

这个流程让你无需自己计算导数项就能实现梯度下降。这是TensorFlow一个非常强大的功能,称为自动微分。其他一些机器学习包(如PyTorch)也支持自动微分。有时人们也称之为“autograd”(自动求导),技术上正确的术语是自动微分,而“autograd”实际上是一个特定软件包的名称。但有时人们提到“autograd”时,只是指代自动计算导数这一相同概念。


实现协同过滤算法

现在,让我们看看如何利用自动微分来实现协同过滤算法。

事实上,一旦你能自动计算导数,你就不局限于使用梯度下降,还可以使用更强大的优化算法,如Adam优化器

为了在TensorFlow中实现协同过滤,你可以使用以下语法:

# 指定优化器为Adam,并设置学习率
optimizer = keras.optimizers.Adam(learning_rate=1e-1)

![](https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/dlai-ml/img/1373277e7c2bbe60f758f4dfd2abf40c_5.png)

for iter in range(200):
    # 使用梯度带记录计算成本的操作
    with tf.GradientTape() as tape:
        # 实现计算协同过滤成本函数 J 的代码
        # 成本函数 J 的输入参数包括 X, W, B, 归一化后的评分矩阵 Ynorm,
        # 评分指示矩阵 R,用户数 Nu,电影数 Nm,以及正则化参数 lambda
        cost_value = cofi_cost_func_v(X, W, b, Ynorm, R, Nu, Nm, lambda)
    # 自动计算成本函数关于 X, W, B 的梯度
    grads = tape.gradient(cost_value, [X, W, b])
    # 使用优化器应用计算出的梯度来更新参数
    optimizer.apply_gradients(zip(grads, [X, W, b]))

以下是关键步骤说明:

  1. 首先,指定优化器为带有所设学习率的Adam优化器。
  2. 然后,进行一定次数(例如200次)的迭代。
  3. 和之前一样,使用 tf.GradientTape() as tape,并提供计算成本函数 J 值的代码。
  4. 回想一下,在协同过滤中,成本函数 J 的输入参数包括 X, W, B,以及归一化的评分矩阵 Ynorm 和评分指示矩阵 R,还有用户数 Nu、电影数 Nm 以及正则化参数 λ
  5. 这段语法将使TensorFlow记录用于计算成本的运算序列。
  6. 通过 tape.gradient(...) 请求,你将获得成本函数关于 X, W, B 的导数(梯度)。
  7. 最后,使用我们之前指定的优化器(Adam优化器),配合刚刚计算出的梯度,通过 apply_gradients 函数来更新参数。Python中的 zip 函数只是将数字重新排列成适合 apply_gradients 函数的顺序。

如果你对协同过滤使用梯度下降,回想一下,成本函数 JW, BX 的函数。应用梯度下降时,你需要计算关于 W 的偏导数,然后如下更新 W,同样计算关于 B 的偏导数并更新 B,类似地更新特征 X,并重复此过程直到收敛。

但如前所述,有了TensorFlow和自动微分,你不仅可以使用标准的梯度下降,还可以使用像Adam优化器这样更强大的优化算法。

你在实践实验室中使用的数据集是一个真实的数据集,包含真实用户对真实电影的评分。这是MovieLens数据集,由Harper和Konstan提供。希望你享受在一个真实的电影和评分数据集上运行协同过滤算法,并亲眼看看它能得到的结果。


为何不使用标准神经网络流程?

如果你想知道为什么我们必须以这种方式实现,为什么不能使用密集层(Dense layer)然后 model.compilemodel.fit?原因在于,协同过滤算法及其成本函数无法整齐地适配到TensorFlow的密集层或其他标准神经网络层类型中。这就是为什么我们必须以另一种方式实现:我们自己实现成本函数,但利用TensorFlow的自动微分工具(也称为AutoDiff),并使用TensorFlow实现的Adam优化算法,让它为我们完成优化成本函数的大部分工作。

如果你的模型是由一系列密集神经网络层或TensorFlow支持的其他类型层组成的,那么旧的 model.compilemodel.fit 实现方法仍然有效。但即使不是这种情况,TensorFlow中的这些工具也能为你提供一种非常有效的方法来实现其他学习算法。


总结

本节课中,我们一起学习了如何使用TensorFlow实现协同过滤算法。核心要点包括:

  1. TensorFlow的自动微分功能可以自动计算成本函数的导数,极大简化了优化过程。
  2. 通过 tf.GradientTape() 记录运算,并使用 tape.gradient() 获取梯度。
  3. 我们可以利用更高级的优化器(如Adam)而不仅仅是基础梯度下降。
  4. 协同过滤的成本函数结构特殊,因此需要直接实现成本函数并利用自动微分,而非套用标准的神经网络层构建流程。

希望你能享受在本周的实践实验室中进一步探索协同过滤练习。如果代码和语法看起来很多,请不要担心,确保你拥有成功完成该练习所需的一切。在下一个视频中,我们将继续讨论协同过滤的更多细节,特别是如何根据一部电影找到相关项目(即,与这部电影相似的其他电影有哪些)。让我们进入下一个视频。

125:协同过滤与查找相关物品 🎯

在本节课中,我们将学习协同过滤算法的一个重要应用:如何为用户正在查看的特定物品(例如一本书或一部电影)查找并推荐其他相似的物品。我们还将探讨协同过滤算法的一些局限性,并简要介绍可以解决这些问题的“基于内容的过滤”算法。


查找相关物品 🔍

上一节我们介绍了协同过滤算法如何学习每个物品的特征向量。本节中我们来看看如何利用这些特征向量来查找相似的物品。

在协同过滤算法中,我们为每个物品 i(例如每部电影)学习到一个特征向量 x_i。在实践中,这些自动学习的特征(如 x1, x2, x3)可能难以单独解释,但它们作为一个整体,能够有效地表征该物品的特性。

给定物品 i 的特征向量 x_i,如果我们想找到与它相似的其他物品 k,我们可以计算其特征向量 x_kx_i 之间的相似度。具体方法是计算两个向量之间的平方欧氏距离

以下是衡量相似度的公式:

distance = Σ (x_k[l] - x_i[l])^2,其中 l 从 1 到 nn 是特征数量)。

这个公式计算的是向量 x_kx_i 之间的平方距离。在数学上,这个距离有时也写作 ||x_k - x_i||^2

实际操作中,我们不会只找一个距离最小的物品,而是:
以下是查找相关物品的步骤:

  1. 对于目标物品 i,获取其特征向量 x_i
  2. 计算它与系统中所有其他物品特征向量 x_k 的距离。
  3. 选择距离最小的 5 个或 10 个物品。
  4. 这些物品就是与物品 i 最相关的推荐项。

因为特征向量 x_i 蕴含了物品 i 的本质信息,所以拥有相似特征向量的其他物品 x_k,自然与物品 `i** 高度相似。这个查找相关物品的思路,也将是我们后续构建更强大推荐系统的一个基础模块。


协同过滤的局限性 ⚠️

在介绍了如何利用协同过滤查找相关物品后,我们也需要了解该算法存在的一些不足。认识这些局限性有助于我们理解为何需要其他更先进的算法。

协同过滤算法依赖于用户对物品的评分数据。它的一个主要弱点是处理冷启动问题的能力不强。
以下是冷启动问题的两个典型场景:

  • 新物品问题:当目录中新增了一个物品(例如一部刚上映的电影),几乎没有用户对它进行过评分,算法就很难对该物品进行有效的排名和推荐。
  • 新用户问题:对于一个新用户,如果他只对极少数物品进行了评分,系统也很难准确地为他推荐可能感兴趣的内容。

我们在之前的视频中看到,均值归一化可以在一定程度上缓解这个问题,但或许还有更好的方法。

协同过滤的第二个局限性是,它无法自然地利用关于物品或用户的附加信息(Side Information)
以下是几个附加信息的例子:

  • 关于物品的信息:对于电影,你可能知道它的类型、主演、制片公司、预算等。
  • 关于用户的信息:你可能了解用户的人口统计学特征(如年龄、性别、地理位置)、他们明确表达的偏好,甚至可以从他们的IP地址、使用的设备(移动端/桌面端)或网络浏览器中推断出一些线索。例如,使用不同浏览器(Chrome, Firefox, Safari, Edge)的用户行为模式可能存在差异,这些信息都能为预测用户喜好提供线索。

因此,尽管协同过滤是一套非常强大的算法,但它也存在上述局限。


总结与展望 🚀

本节课中,我们一起学习了如何利用协同过滤算法学习到的物品特征向量来查找和推荐相关物品,其核心是通过计算特征向量间的平方欧氏距离来度量相似性。同时,我们也探讨了协同过滤在应对冷启动问题和利用附加信息方面的局限性。

在下一节视频中,我们将开始发展基于内容的过滤算法。这种算法能够有效地利用我们刚才提到的各种附加信息,从而解决协同过滤的许多不足。基于内容的过滤是当今许多商业应用中使用的先进技术,让我们一起去看看它是如何工作的。

126:协同过滤与基于内容的过滤对比 🎬

在本节课中,我们将开始学习第二种推荐系统算法——基于内容的过滤算法。首先,我们将对比之前讨论的协同过滤方法与这种新的基于内容过滤方法。

协同过滤与基于内容过滤的对比

上一节我们介绍了协同过滤的基本思想。本节中,我们来看看基于内容的过滤采取了何种不同的方法。

协同过滤的一般方法是:根据与你评分相似的其他用户的评分,向你推荐物品。算法利用用户对物品的评分数据,来为你推荐新物品。

相比之下,基于内容的过滤采用了一种不同的方法来决定向你推荐什么。基于内容的过滤算法会根据用户特征和物品特征来寻找良好匹配,从而向你推荐物品。

换句话说,它需要拥有每个用户的一些特征,以及每个物品的一些特征,并利用这些特征来尝试决定哪些物品和用户可能彼此匹配良好。

基于内容过滤的数据表示

在基于内容的过滤算法中,我们仍然拥有用户对某些物品评分的数据。因此,我们将继续使用 R(i, j) 来表示用户 j 是否对物品 i 进行了评分,并继续使用 y(i, j) 来表示用户 j 对物品 i 的评分(如果已定义)。

但基于内容过滤的关键在于,我们将能够充分利用用户和物品的特征,从而可能找到比纯协同过滤方法更好的匹配。

电影推荐中的特征示例

以下是电影推荐中可能使用的一些特征示例。

用户特征示例:

  • 你可能知道用户的年龄。
  • 你可能知道用户的性别。这可以是一个独热编码特征,类似于我们在讨论决策树时看到的情况,根据用户自我认同的性别是男性、女性或未知等,可以有多个值。
  • 你可能知道用户的国家。如果世界上大约有200个国家,那么这将是一个具有大约200个可能值的独热编码特征。
  • 你还可以查看用户的过去行为来构建特征向量。例如,如果你查看目录中的前1000部电影,你可能会构建1000个特征,这些特征告诉你用户过去观看过这1000部最受欢迎电影中的哪些。
  • 实际上,你也可以利用用户可能已经给出的评分来构建新特征。事实证明,如果你有一组电影,并且你知道每部电影属于哪种类型,那么用户对每种类型电影的平均评分(例如,用户评分的所有浪漫电影的平均评分,用户评分的所有动作电影的平均评分,等等)也可以成为描述用户的强大特征。这个特征的一个有趣之处在于,它实际上依赖于用户给出的评分,但这完全没有问题。构建依赖于用户评分的特征向量是完全可行的描述该用户的方法。

基于上述特征,你可以为每个用户 j 构建一个特征向量 x_u^(j)u 代表用户,上标 j 表示第 j 个用户)。

电影特征示例:

  • 同样,你也可以为每部电影或每个物品构建一组特征,例如电影的年份、电影的类型(一种或多种)。
  • 如果已知电影的影评,你可以构建一个或多个特征来捕捉影评人对电影的评价。
  • 或者,再次地,你实际上可以利用用户对电影的评分来构建一个特征,例如这部电影的平均评分。这个特征再次依赖于用户给出的评分,但这同样没有问题。你可以为给定电影构建一个依赖于该电影所获评分的特征,例如电影的平均评分。
  • 如果你愿意,还可以有按国家或按用户人口统计特征划分的平均评分等,以构建其他类型的电影特征。

基于此,对于每部电影 i,你可以构建一个特征向量,我将其表示为 x_m^(i)m 代表电影,上标 i 表示第 i 部电影)。

基于内容过滤的算法目标

给定这样的特征,任务是尝试判断给定的电影 i 是否会是用户 j 的良好匹配。请注意,用户特征和电影特征在大小上可能非常不同。例如,用户特征可能是1500个数字,而电影特征可能只有50个数字,这也没关系。

在基于内容的过滤中,我们将开发一种学习匹配用户和电影的算法。之前,我们通过 w(j)·x(i) + b(j) 来预测用户 j 对电影 i 的评分。

为了开发基于内容的过滤,我将去掉 b(j)。事实证明,这完全不会损害基于内容过滤的性能。此外,我不再为用户 j 使用 w(j),为电影 i 使用 x(i),而是用 v_u^(j) 替换这个表示法(这里的 v 代表向量,它将是为用户 j 计算的一组数字,下标 u 代表用户),并用 v_m^(i) 替换 x(i)(下标 m 代表电影,上标 i 表示第 i 部电影)。

因此,v_u^(j) 是一个根据用户 j 的特征计算出的向量(一组数字),v_m^(i) 是根据上一张幻灯片中看到的电影 i 的特征计算出的一组数字。如果我们能够为这些向量 v_u^(j)v_m^(i) 找到合适的选择,那么希望这两个向量之间的点积将成为用户 j 给电影 i 评分的一个良好预测。

向量表示的直观理解

为了说明学习算法可能得出什么,假设 v_u(即用户向量)最终捕获了用户的偏好,例如 [4.9, 0.1, ...] 这样的数字列表,其中第一个数字表示他们有多喜欢浪漫电影,第二个数字表示他们有多喜欢动作电影,依此类推。而 v_m(电影向量)是 [4.5, 0.2, ...] 等,这些数字捕获了这部电影在多大程度上是浪漫电影,在多大程度上是动作电影等。

那么,点积(将这些列表中的数字逐元素相乘然后求和)有望给出这个特定用户有多喜欢这部特定电影的感觉。

核心挑战与总结

因此,挑战在于:给定用户 j 的特征 x_u^(j),我们如何计算这个简洁或紧凑地表示用户偏好的向量 v_u^(j)?类似地,给定电影 i 的特征,我们如何计算 v_m^(i)?请注意,虽然 x_ux_m 的大小可能不同(一个可能是很长的数字列表,另一个可能短得多),但这里的 v 必须具有相同的大小,因为如果你想计算 v_uv_m 之间的点积,那么两者必须具有相同的维度,例如可能都是32个数字。

总结一下:

  • 在协同过滤中,我们拥有许多用户对不同物品的评分。
  • 相比之下,在基于内容的过滤中,我们拥有用户特征和物品特征,以找到一种方法来发现用户和物品之间的良好匹配。
  • 我们将通过为用户计算向量 v_u,为电影(物品)计算向量 v_m,然后取它们之间的点积来尝试找到良好匹配。

那么,我们如何计算 v_uv_m 呢?让我们在下一个视频中探讨这个问题。

127:基于内容过滤的深度学习 🧠

在本节课中,我们将学习如何利用深度学习技术来构建一个基于内容的过滤算法。我们将了解如何设计用户网络和电影网络,如何训练这些网络,以及如何利用它们进行预测和寻找相似项目。


概述

基于内容的过滤算法通过分析用户和项目的特征来进行推荐。本节课程将介绍一种使用深度学习实现该算法的方法,这是目前许多重要商业推荐系统构建的基础。


用户与电影的特征向量

回忆一下我们的方法:给定一个描述用户的特征向量(如年龄、性别、国家等),我们需要计算一个用户向量 VU。同样,给定一个描述电影的特征向量(如发行年份、电影风格等),我们需要计算一个电影向量 VM


构建神经网络

为了计算上述向量,我们将使用神经网络。第一个神经网络被称为用户网络

以下是用户网络的一个示例:它以用户特征列表 XU(如年龄、性别、国家等)作为输入。然后,通过几层(例如密集神经网络层),它将输出描述用户的向量 VU

请注意,在这个神经网络中,输出层有32个单元,因此 VU 实际上是一个包含32个数字的列表。与之前使用的大多数神经网络不同,其最后一层不是只有一个单元,而是有32个单元。

类似地,为了计算电影的 VM,我们可以构建一个电影网络。该网络以电影特征作为输入,通过神经网络的几层处理,最终输出描述电影的向量 VM


进行预测

最终,我们将预测该用户对这部电影的评分,公式为:

预测评分 = VU · VM

用户网络和电影网络在理论上可以拥有不同数量的隐藏层和每层不同数量的单元。只有输出层需要具有相同的大小或维度。

在之前的描述中,我们预测的是1到5星或0到5星的电影评分。如果我们处理的是二元标签(例如,用户是否喜欢或收藏了某个项目),你也可以修改此算法,将输出从 VU · VM 改为应用 sigmoid 函数,并用它来预测 Y(IJ) = 1 的概率。

为了明确表示,我们也可以在这里添加上标 IJ,以强调这是用户 J 对电影 I 的预测。


统一的网络架构

虽然我将用户网络和电影网络画成了两个独立的神经网络,但实际上我们可以将它们合并到一个单一的图表中,就像一个单一的神经网络。

图表的上半部分是用户网络,它输入 XU 并最终计算出 VU。图表的下半部分是电影网络,它输入 XM 并最终计算出 VM。然后这两个向量进行点积运算(图中的点代表点积),从而得到我们的预测。


训练模型

这个模型有很多参数,神经网络的每一层都有一套常规的参数。那么,如何训练用户网络和电影网络的所有参数呢?

我们将构建一个成本函数 J,它与你在协同过滤中看到的成本函数非常相似。假设你拥有一些用户对某些电影的评分数据。

成本函数 J 的公式如下:

J = Σ (VU(J) · VM(I) - Y(IJ))²

我们将对所有拥有标签(即 Y(IJ) 有值)的用户-电影对 (I, J) 求和。

我们训练这个模型的方式是:根据神经网络的参数,你会得到不同的用户向量和电影向量。因此,我们希望训练神经网络的参数,使得得到的用户和电影向量能在此处的预测中产生较小的平方误差。

需要明确的是,用户网络和电影网络没有单独的训练过程。下面的这个表达式,就是用于训练用户网络和电影网络所有参数的成本函数。我们将根据 VUVM 预测 Y(IJ) 的效果来评判这两个网络。

使用这个成本函数,我们将应用梯度下降或其他优化算法来调整神经网络的参数,以使成本函数 J 尽可能小。如果你想对这个模型进行正则化,我们也可以添加通常的神经网络正则化项,以鼓励神经网络保持其参数值较小。


寻找相似项目

训练完成后,你也可以利用这个模型来寻找相似的项目。这与我们之前在协同过滤中看到的、特征帮助你寻找相似项目的方法是类似的。

让我们来看一下。

VU(J) 是一个长度为32的向量,它描述了具有特征 XU(J) 的用户 J。类似地,VM(I) 是一个长度为32的向量,它描述了具有这些特征的电影 I

那么,给定一部特定的电影,如果你想找到其他与之相似的电影,该怎么办呢?向量 VM(I) 描述了电影 I。因此,如果你想找到其他与之相似的电影,你可以寻找其他电影 K,使得描述电影 K 的向量与描述电影 I 的向量之间的距离(或平方距离)很小。

这个表达式的作用类似于我们之前在协同过滤中讨论的、寻找与特征 X(I) 相似的特征 X(K) 的电影。因此,通过这种方法,你也可以找到与给定项目相似的项目。

最后一点说明:这可以提前进行预计算。我的意思是,你可以让计算服务器在夜间运行,遍历所有电影列表,并为每部电影找到与之相似的电影。这样,明天如果有用户访问网站并浏览某部特定电影时,你已经可以预先计算出10部或20部更相似的电影,在那个时候展示给用户。能够提前预计算与给定电影相似的电影,这一点在我们后面讨论如何将这种方法扩展到非常大的电影目录时将变得非常重要。


深度学习的力量

这就是你如何使用深度学习来构建基于内容的过滤算法。

你可能还记得,当我们讨论决策树以及决策树与神经网络的优缺点时,我提到过神经网络的优点之一是,更容易将多个神经网络组合在一起,让它们协同工作以构建更大的系统。你刚才看到的实际上就是这样一个例子,我们可以将用户网络和电影网络组合在一起,然后对输出进行内积运算。这种将两个神经网络组合在一起的能力,使我们能够设计出更复杂且功能强大的架构。


实践注意事项

有一点需要注意:如果你在实践中实现这些算法,我发现开发人员通常最终会花费大量时间精心设计需要输入到这些基于内容的过滤算法中的特征。因此,如果你最终要商业构建其中一个系统,可能值得花一些时间为这个应用设计好的特征。

就这些应用而言,我们所描述的算法的一个局限性是:如果你有一个包含大量不同电影的大型目录需要推荐,运行起来在计算上可能会非常昂贵。


总结

在本节课中,我们一起学习了如何利用深度学习构建基于内容的推荐系统。我们介绍了用户网络和电影网络的设计,如何通过统一的成本函数训练它们,以及如何利用学习到的向量进行评分预测和寻找相似项目。我们还讨论了这种方法的优势以及在实践中需要注意的特征工程和计算效率问题。

在下一个视频中,我们将探讨一些实际问题,以及如何修改此算法以使其能够扩展到处理甚至非常大的项目目录。让我们在下一个视频中继续学习。

128:从大型目录中推荐 🎯

在本节课中,我们将学习推荐系统如何从包含成千上万甚至数百万物品的大型目录中,高效地挑选出少量物品进行推荐。我们将重点介绍一个两阶段方法:检索排名


推荐系统面临的挑战

上一节我们介绍了使用神经网络预测用户对物品的评分。然而,当用户访问网站时,我们拥有用户特征 X_U

但如果需要将成千上万的物品输入神经网络,通过计算内积来确定推荐哪些产品,那么每次用户访问网站时都需要运行数百万次神经网络推理,这在计算上是不可行的。


两阶段解决方案:检索与排名

为了解决上述挑战,许多大规模推荐系统采用两个步骤实现:检索排名

检索步骤的目标是生成一个包含大量可能物品候选的列表,力求覆盖你可能推荐给用户的各种可能性。在检索阶段,即使包含许多用户可能不喜欢的物品也没关系。

排名步骤则会对这些候选物品进行精细调整,并挑选出最佳的物品推荐给用户。

以下是检索步骤的一个例子。

检索步骤的具体操作

以下是检索步骤可能采取的几个行动:

  • 基于最近观看记录:针对用户最近观看的10部电影,分别找出10部最相似的电影。例如,如果用户观看了向量为 V_IM 的电影,你可以找到向量为 V_KM 的相似电影。正如上一视频所见,寻找与给定电影相似的电影可以预先计算,因此你可以直接通过查找表获取结果。
  • 基于用户偏好类型:根据用户观看最多的3种电影类型(例如爱情片、喜剧片、历史剧),将每种类型中排名前10的电影加入候选列表。
  • 基于用户所在地区:将用户所在国家/地区排名前20的电影也加入此列表。

这个检索步骤可以非常快速地完成,最终你可能会得到一个包含一百或数百部可能推荐电影的列表。这个列表有望包含一些好的选项,但即使包含一些用户完全不会喜欢的选项也可以接受。检索步骤的目标是确保广泛的覆盖范围,拥有足够多的电影,以便其中至少包含许多好的选择。

最后,我们会将检索步骤中获得的所有物品合并成一个列表,去除重复项以及用户已经观看或购买过、你可能不想再次推荐的物品。

排名步骤

检索与排名步骤是紧密衔接的。检索步骤提供了一个粗筛后的候选池,排名步骤则在此基础上进行精准排序。

排名步骤是第二个阶段。在此阶段,你将获取检索步骤得到的列表(可能只有数百部电影),并使用学习到的模型对它们进行排名。

这意味着你需要将用户特征向量和电影特征向量输入神经网络,并为每一对用户-电影组合计算预测评分。基于此,你现在就拥有了所有(例如)100多部电影中,用户最可能给出高评分的那些电影。然后,你可以根据你认为用户会给出最高评分的顺序,向用户展示这个排名后的物品列表。

一个额外的优化是:如果你已经预先计算了所有电影的 V_M,那么你只需要对神经网络的这一部分进行一次推理,计算出 V_U。然后,取这个刚刚为当前网站用户计算出的 V_U,并与检索步骤中获得的电影的 V_M 计算内积。

如果检索步骤只提出几百部电影,这个计算可以相对快速地完成。


关键决策:检索多少物品?

你需要为此算法做出的一个决策是:在检索步骤中,你希望检索多少物品来输入更精确的排名步骤?

在检索步骤中,检索更多物品往往会带来更好的性能,但算法最终会变得更慢。为了优化检索物品数量(例如是检索100、500还是1000件物品)之间的权衡,我建议进行离线实验,看看检索更多物品能在多大程度上带来更相关的推荐。

具体来说,如果你的神经网络模型预测的、检索物品的 Y_IJ 等于1的估计概率,或者 Y 的估计评分很高,那么如果你检索500件物品而不是仅100件,这个值最终会高得多。这将支持检索更多物品,即使它会稍微减慢算法速度。

然而,通过独立的检索步骤和排名步骤,当今许多推荐系统能够同时提供快速和准确的结果。因为检索步骤试图剔除大量不值得进行更详细推理和内积计算的物品,然后排名步骤再对用户实际可能喜欢的物品进行更仔细的预测。


总结与伦理考量

本节课中,我们一起学习了如何使推荐系统即使在电影、产品等非常庞大的目录上也能高效工作。

事实证明,尽管推荐系统具有重要的商业价值,但它们也伴随着一些重大的伦理问题。不幸的是,已经有一些推荐系统造成了伤害。因此,在你构建自己的推荐系统时,我希望你采取合乎伦理的方法,用它来服务你的用户和整个社会,以及你自己和你可能工作的公司。让我们在下一个视频中看看与推荐系统相关的伦理问题。

129:推荐系统的伦理使用 ⚖️

在本节课中,我们将探讨推荐系统的伦理问题。尽管推荐系统为许多企业带来了巨大利润,但其某些应用场景却可能对用户乃至整个社会产生负面影响。我们将分析这些潜在问题,并思考如何设计系统以最大化其益处、减少其危害。

上一节我们介绍了推荐系统的基本配置和目标设定。本节中,我们来看看推荐系统在实际应用中可能引发的一些伦理困境。

推荐系统的目标设定

设计推荐系统时,需要设定其核心目标,这决定了系统将向用户推荐什么内容。以下是几种常见的目标设定:

  • 最大化用户喜好:例如,推荐用户最可能给出五星评分的电影。公式可表示为:argmax(P(评分=5星 | 用户, 电影))
  • 最大化购买概率:例如,推荐用户最可能购买的商品。公式可表示为:argmax(P(购买 | 用户, 商品))
  • 最大化点击率:在广告场景中,许多公司倾向于展示用户最可能点击的广告,尤其是当广告主出价较高时。因为公司的收入通常取决于广告是否被点击以及每次点击的出价。
  • 最大化公司利润:一些网站会优先推荐能为公司带来最高利润的商品,而非最相关或用户最可能购买的商品。
  • 最大化用户参与度/停留时间:视频或社交媒体网站可能通过推荐系统来最大化用户的观看时间或停留时间,从而展示更多广告。

前两种目标看似无害,而后三种目标则可能引发伦理问题。接下来,我们将深入探讨这些潜在的问题用例。

潜在的问题用例分析

广告行业的放大效应

广告行业有时会放大某些有害的商业行为,当然也能放大优秀的企业。

良性循环示例(旅游业)
一家优秀的旅游公司提供出色的旅行体验,从而获得更高利润。高利润使其有能力在广告竞价中出价更高,从而获得更多流量和用户。这形成了一个良性循环:服务越好 -> 利润越高 -> 广告投入越大 -> 用户越多。

恶性循环示例(发薪日贷款行业)
发薪日贷款公司通常向低收入人群收取极高利息。那些最擅长榨取客户每一分钱的公司利润更高,因此能在广告中出价更高,从而获得更多流量,进而剥削更多客户。这形成了一个恶性循环,使得最具剥削性的公司反而能获得最多曝光。

一个可能的缓解措施是拒绝为剥削性企业投放广告。然而,如何准确定义“剥削性企业”本身就是一个难题。

最大化参与度的副作用

最大化用户参与度(如视频观看时间、社交媒体停留时间)已被广泛报道可能导致不良后果。

为了吸引用户长时间停留,推荐系统可能会放大阴谋论或充满仇恨、毒性的内容,因为这些内容通常具有很高的参与度。即使这些内容对个人和社会有害,系统仍可能优先推荐它们。

一个不完美但可行的缓解方案是尝试过滤掉有问题的内容,例如仇恨言论、欺诈、诈骗或某些暴力内容。但同样,如何精确定义应过滤的内容极具挑战性。

透明度问题

当用户使用许多应用或网站时,他们通常认为推荐是基于个人喜好。然而,许多应用和网站实际上是在试图最大化自身利润,而非用户的满意度。

我鼓励开发者和公司尽可能向用户透明公开推荐内容所依据的标准。虽然这并不容易,但提高透明度有助于增加用户信任,并促使系统为社会创造更多价值。

总结与展望

本节课中,我们一起学习了推荐系统可能面临的伦理挑战。推荐系统是一项强大且有利可图的技术,但也存在一些有问题的应用场景。

在构建推荐系统或任何机器学习技术时,希望大家不仅考虑其可能带来的益处,也深思其潜在的危害。邀请多元视角进行讨论和辩论,并致力于构建那些你真正相信能让社会变得更好的事物。

希望我们所有从事人工智能工作的人,都能共同努力,只做让世界变得更美好的工作。

感谢聆听。关于推荐系统,我们还有最后一个视频,将探讨在 TensorFlow 中实现基于内容的过滤推荐系统的实用技巧。让我们进入推荐系统的最后一个视频。

130:基于内容过滤的TensorFlow实现 🎬

在本节课中,我们将学习如何在TensorFlow中实现基于内容过滤的推荐算法。我们将通过分析关键代码片段,理解如何构建用户网络和物品(电影)网络,并计算它们之间的相似度以进行预测。


概述

基于内容过滤的核心思想是为用户和物品分别学习一个向量表示,并通过计算这两个向量的点积来预测用户对物品的偏好程度。在TensorFlow中,我们可以通过构建两个神经网络(用户网络和物品网络)来实现这一过程。

上一节我们介绍了基于内容过滤的基本概念,本节中我们来看看如何在TensorFlow中具体实现它。


构建用户网络

首先,我们需要构建一个用户神经网络。这个网络的结构与我们之前实现的全连接神经网络类似。

以下是构建用户网络的关键步骤:

  • 我们使用Sequential模型。
  • 在这个例子中,我们有两个全连接层,隐藏单元的数量在此指定。
  • 最后一层输出32个数字。
# 示例代码:用户网络结构
user_model = tf.keras.Sequential([
    tf.keras.layers.Dense(256, activation='relu'),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(32)  # 输出用户向量 Vu
])

构建物品(电影)网络

接下来,我们构建物品网络。这里我们将电影视为物品。

以下是构建物品网络的关键步骤:

  • 我们再次使用几个全连接隐藏层。
  • 该层输出32个数字。
  • 对于隐藏层,我们使用默认的激活函数,即ReLU激活函数。
# 示例代码:物品网络结构
item_model = tf.keras.Sequential([
    tf.keras.layers.Dense(256, activation='relu'),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(32)  # 输出物品向量 Vm
])


处理输入与向量归一化

我们需要告诉TensorFlow如何将用户特征或物品特征(即电影特征)输入到两个神经网络中。

以下是处理输入和进行向量归一化的步骤:

  • 此语法提取用户的输入特征并将其输入到我们上面定义的用户神经网络中,以计算用户向量Vu
  • 为了使算法效果更好,我们增加了归一化步骤,将向量Vu的长度归一化为1。这行代码对向量的长度进行归一化,也称为L2范数归一化,但基本上是将向量V的长度设置为1。
  • 我们对物品网络(电影网络)执行相同的操作。这提取物品特征并将其输入到我们上面定义的物品神经网络中,并计算电影向量Vm
  • 最后,此步骤也将该向量的长度归一化为1。
# 示例代码:输入处理与归一化
# 假设 user_features 和 item_features 是输入数据
vu = user_model(user_features)
vu = tf.nn.l2_normalize(vu, axis=1)  # 归一化用户向量

vm = item_model(item_features)
vm = tf.nn.l2_normalize(vm, axis=1)  # 归一化物品向量

计算点积与模型输出

在计算出VuVm之后,我们必须计算这两个向量之间的点积。

以下是计算点积和定义模型输出的步骤:

  • Keras有一个特殊的层类型。注意,我们之前在这里使用了tf.keras.layers.Dense,而这里是tf.keras.layers.Dot。实际上,有一个特殊的Keras层可以直接计算两个数字之间的点积。
  • 因此,我们将使用它来计算向量VuVm的点积,这给出了神经网络的输出,即最终的预测值。
  • 最后,为了告诉Keras模型的输入和输出是什么,这行代码指明整体模型是一个以用户特征和电影(或物品)特征为输入,以上面定义的输出为输出的模型。
# 示例代码:计算点积与定义模型
dot_product = tf.keras.layers.Dot(axes=1)([vu, vm])

model = tf.keras.Model(inputs=[user_features_input, item_features_input], outputs=dot_product)

损失函数与训练

我们用于训练此模型的成本函数将是均方误差成本函数。

# 示例代码:编译模型
model.compile(optimizer='adam', loss='mse')

这些是实现基于内容过滤神经网络的关键代码片段。你可以在实践实验室中查看其余代码,但希望你能尝试运行并了解所有这些代码片段如何组合成一个可工作的基于内容过滤算法的TensorFlow实现。

实际上,我之前没有讨论的另一个步骤是归一化向量Vu的长度,这会使算法效果更好。因此,TensorFlow有这个L2 normalize函数来归一化向量,它也被称为归一化向量的L2范数,因此得名。


总结

本节课中我们一起学习了基于内容过滤推荐系统的TensorFlow实现。我们了解了如何构建用户和物品的双塔神经网络模型,如何对学习到的向量进行归一化处理,以及如何通过计算点积来生成预测。这些是构建现代推荐系统的重要基础。

感谢你坚持学习推荐系统的所有材料,这是一个令人兴奋的领域。希望你在实践实验室中享受这些想法和代码的乐趣。这标志着我们关于推荐系统的视频讲座以及本专业倒数第二周的结束。

我期待下周也能见到你,我们将讨论强化学习这项激动人心的技术。希望你喜欢测验和实践实验室,我期待下周与你再见。

131:主成分分析(PCA)入门 🧠

在本节课中,我们将学习一种名为主成分分析的无监督学习算法。该算法常用于数据可视化,特别是当你拥有大量特征(例如10个、50个甚至上千个)时。由于我们无法直接绘制高维数据,PCA可以帮助我们将数据的特征数量减少到两个或三个,从而能够进行可视化,帮助数据科学家更好地理解和探索数据。

数据降维的需求 📉

在深入PCA的工作原理之前,我们先来看看为什么需要降维。假设你有一个关于乘用车的数据集,其中包含许多特征,例如:

  • 汽车长度
  • 汽车宽度
  • 车轮直径
  • 汽车高度

如果你希望可视化这些数据,但无法绘制100维的图表,PCA就能派上用场。它可以将具有大量特征的数据集压缩到更少的维度。

PCA工作原理示例 🚗

为了更好地描述PCA,我们将以汽车数据为例。

示例一:长度与宽度

假设数据集只有两个特征:x1(汽车长度)和x2(汽车宽度)。由于道路宽度的限制,汽车的宽度变化通常不大。因此,数据点可能如下图所示,其中x1变化很大,而x2变化很小。

如果你想减少特征数量,一个简单的做法是只保留x1,因为x2提供的信息很少。PCA算法应用于此数据时,基本上会自动做出这个决定。

示例二:长度与车轮直径

现在,假设x1仍是长度,x2是车轮直径。车轮直径确实有一些变化,数据分布可能如下图所示。同样,如果你想简化为一个特征,可能还是会选择x1。PCA处理这个数据集时,结果也类似。

示例三:长度与高度

这是一个更复杂的情况。x1是长度,x2是高度,两者都有显著变化。数据点分布可能显示,较大的汽车通常更长、更高,而较小的汽车则相反。

此时,你不希望只保留长度或只保留高度,因为两者都包含重要信息。PCA的解决方案是引入一个新的轴,我们称之为z轴。这个z轴不是凭空出现的第三个维度,而是位于原始x1-x2平面内的一条新直线。

z轴可能对应于“汽车大小”这个概念。这样,原本需要用两个数字(x1, x2)描述的汽车,现在可以用一个数字(它在z轴上的坐标)来大致捕获其核心信息。PCA的核心思想正是如此:定义一个新的坐标系(由“主成分”轴构成),使得数据在新坐标系下的坐标能最大程度地保留原始数据的有用信息,同时使用更少的维度。

PCA的实际应用 🌍

在实践中,PCA通常用于将大量特征(如10、20、50甚至上千个)减少到2或3个,以便进行二维或三维可视化。

例如,假设你有一个关于各国发展状况的数据集,包含50个特征,如GDP、人均GDP、人类发展指数、预期寿命等。你无法在二维屏幕上绘制50维数据。

PCA可以将这50个特征(x1, x2, ..., x50)压缩成两个新特征z1z2。然后,你可以将各个国家根据它们的(z1, z2)坐标绘制在图上。

你可能会发现:

  • z1大致对应国家规模与总GDP(大国通常有更高的GDP)。
  • z2大致对应人均经济活动水平(人均GDP)。

于是,美国(大国,高人均)可能位于图的右上方;新加坡(小国,高人均)可能位于图的中上方;而其他组合则分布在不同的位置。

这种可视化让你能够洞察高维数据的结构和模式,有时还能发现数据中异常或有趣的现象。

总结 📝

本节课我们一起学习了主成分分析的基本概念。PCA是一种强大的无监督学习算法,它能将高维数据(特征多)降维到低维空间(通常是2维或3维),从而让我们能够可视化数据,并更好地理解数据的内在结构和关键信息。在接下来的课程中,我们将具体探讨PCA算法是如何实现的。

132:PCA算法详解 🧠

在本节课中,我们将学习主成分分析(PCA)算法。PCA是一种无监督学习技术,用于降低数据维度,同时尽可能保留原始数据中的信息。我们将从直观理解开始,逐步深入到其数学原理,并解释PCA与线性回归的区别。


数据预处理与PCA的目标

上一节我们介绍了PCA的基本概念,本节中我们来看看PCA具体如何工作。假设你有一个包含两个特征X1和X2的数据集。数据最初使用X1和X2轴绘制或表示,但你想用仅一个特征(我们称之为Z轴)来替换这两个特征。如何选择一个新轴,使其能很好地捕捉或表示数据?让我们看看PCA是如何做的。

以下是包含五个训练示例的数据集。请记住,这是一种无监督学习算法,因此我们只有x1和x2,没有标签Y。例如,这里的一个点可能坐标为x1=10,x2=8。

如果我们不想使用X1和X2轴,如何选择不同的轴来捕捉或表示数据?

在应用PCA的后续步骤之前,需要注意预处理:特征应首先归一化以具有零均值。此处已假设完成此操作。如果特征x1和x2的取值范围差异很大(例如,x1是房屋面积,x2是卧室数量),那么在应用PCA的后续步骤之前,你还需要先进行特征缩放。

假设特征已归一化至零均值(即从每个特征中减去均值),并且可能也进行了特征缩放,使范围相差不大。接下来PCA会做什么?


选择新轴与投影

为了检查PCA的作用,让我们移除X1和X2轴,只留下五个训练示例。这里的点代表原点。现在,PCA需要选择一个轴(而不是之前的两个轴)来捕捉这五个示例的重要信息。

如果我们选择这个轴作为Z轴(在这个例子中,它实际上与X1轴相同),那么对于这个示例,我们将只捕获其在Z轴上的坐标值。对于第二个示例,我们将捕获这个值,依此类推。

换句话说,我们将取每个示例并将其投影到Z轴上的一个点。“投影”指的是你取这个示例,并使用与Z轴成90度角的线段将其带到Z轴。这里的小方框用于表示该线段与Z轴成90度角。

选择这个方向作为Z轴不是一个坏选择,但有更好的选择。这个选择不算太差,因为当你将示例投影到Z轴上时,你仍然捕捉到了数据的很大一部分分布。这五个点在这里相当分散,因此仍然捕捉到了原始数据集中的大量方差或变化。这意味着投影到Z轴上的数据的方差或变化相当大,因此我们仍然捕捉到了原始五个示例中的大量信息。


不同轴选择的影响

以下是轴Z的一些可能选择。这是另一个选择,但实际上不是一个好选择。如果我选择这个作为我的Z轴,那么如果我取同样的五个示例并将它们投影到Z轴上,我最终会得到这五个点。你会注意到,与之前的选择相比,这五个点被挤压在一起,它们之间的差异量或方差或变化要小得多。这意味着,选择这个Z轴,你捕捉到的原始数据集的信息要少得多,因为你部分地将所有五个示例挤压在一起。

让我们看最后一个选择:如果我选择这个作为Z轴,这实际上比我们之前看到的两个选择更好。因为如果我们将数据投影到Z轴上,我们会发现这些点实际上相距甚远。因此,即使我们现在只使用一个坐标或一个数字来表示或捕捉每个训练示例,而不是使用两个数字或两个坐标X1和X2,我们仍然捕捉到了原始数据中的大量变化和信息。

在PCA算法中,这个轴被称为主成分。它是当你将数据投影到其上时,能获得最大可能方差的轴。因此,如果你要将数据减少到一个轴或一个特征,这个主成分实际上是一个很好的选择,这就是PCA会做的。


可视化不同轴选择的影响

这里我们有10个训练示例。当我们滑动这里的滑块时(你可以在一个可选实验室中自己尝试),Z轴的角度会改变。你在左侧看到的是每个示例通过那个与Z轴成90度的短线段投影的结果,右侧是数据的投影,即这10个示例的Z坐标值。

你会注意到,当我把轴设置在这里时,点被挤压在一起,因此这保留了较少原始数据的信息。而如果我把Z轴设置成这样,这些点分散得多,因此这捕捉到了原始数据集中更多的信息。这就是为什么主成分对应于将Z轴设置在这里,如果你要求PCA将数据减少到一个数字或一个维度,这就是PCA会做出的选择。

像Scikit-learn这样的机器学习库(你将在下一个视频中了解更多)可以帮助你自动找到主成分,但让我们更深入地了解一下它是如何工作的。


PCA的数学原理

这是我的X1和X2轴,这是一个训练示例,坐标为X1轴上的2和X2轴上的3。假设PCA已经找到了这个作为Z轴。我在这里画的这个小箭头是一个长度为1的向量,指向PCA将选择或我们已经选择的Z轴方向。

这个长度为1的向量实际上是向量 [0.71, 0.71](四舍五入后,实际上是0.707等更多数字)。那么,给定这个在x1、x2轴上坐标为 [2, 3] 的示例,我们如何将其投影到Z轴上?公式是取向量 [2, 3] 和这个向量 [0.71, 0.71] 的点积。

如果你计算 [2, 3][0.71, 0.71] 的点积,结果是 2*0.71 + 3*0.71 = 3.55。这意味着从原点到这个点的距离是3.55,也就是说,如果我们想用一个数字来尝试捕捉这个示例,那个数字就是3.55。

到目前为止,我们已经讨论了如何使用PCA将数据降低到一个维度或一个数字,我们通过找到主成分(有时也称为第一主成分)来实现。在这个例子中,我们找到了这个作为第一个轴。事实证明,如果你要选择第二个轴,第二个轴将始终与第一个轴成90度角。如果你要选择第三个轴,那么第三个轴将与第一个和第二个轴成90度角。

顺便说一下,在数学中,90度有时被称为垂直。“垂直”一词仅意味着成90度角。因此,数学家有时会说第二个轴Z2与第一个轴Z1成90度角或垂直。如果你选择额外的轴,它们也与Z1和Z2以及PCA将选择的任何其他轴成90度角或垂直。因此,如果你有50个特征并想找到三个主成分,那么如果那是第一个轴,第二个轴将与之成90度角,第三个轴也将与第一个和第二个轴成90度角。


PCA与线性回归的区别

现在,我经常被问到的一个问题是:PCA与线性回归有何不同?事实证明,PCA不是线性回归,它是一个完全不同的算法,让我解释为什么。

线性回归是一种监督学习算法,你有数据X和Y。这里的数据集,水平轴是特征X,垂直轴是标签Y。在线性回归中,你试图拟合一条直线,使预测值尽可能接近真实标签Y。换句话说,你试图最小化这些小线段的长度,这些小线段在垂直方向上,它们与Y轴对齐。

相比之下,在PCA中,没有真实标签Y,所以你只有未标记的数据x1和x2。此外,你不是试图拟合一条线来使用x1预测x2。相反,算法平等对待x1和x2,我们试图找到这个轴Z,结果是我们最终使这些小线段变小。当你将数据投影到Z上时。

在线性回归中,有一个数字Y被给予特殊对待,我们总是试图测量拟合线与Y之间的距离,这就是为什么这些距离仅在Y轴方向上测量。而在PCA中,你可以有很多特征,x1、x2,可能一直到x50(如果你有50个特征),所有50个特征都被平等对待,我们只是试图找到一个轴Z,使得当数据使用这些线段投影到Z轴上时,你仍然尽可能保留原始数据的方差。

我知道当我在二维空间中绘制这些东西时(只有两个特征,这是我能在平面计算机显示器上绘制的全部),这些箭头看起来可能有点相似。但当你拥有两个以上的特征时(大多数情况如此),线性回归和PCA之间的差异以及箭头的作用是非常大的。这些箭头用于完全不同的目的,并给出非常不同的答案:线性回归用于预测目标输出Y,而PCA试图处理许多特征,平等对待它们,并减少表示数据所需的轴的数量。

事实证明,最大化这些投影的分布将对应于最小化这些线段的距离,即点必须移动以投影到Z上的距离。

为了以另一种方式说明线性回归和PCA之间的区别:如果你有一个看起来像这样的数据集,线性回归所能做的就是拟合一条看起来像那样的线。而如果你的数据看起来像这样,PCA将选择这个作为主成分。因此,如果你试图预测Y的值,应该使用线性回归;如果你试图减少数据集中的特征数量(例如为了可视化),应该使用PCA。


PCA的重建步骤

最后,在结束这个视频之前,你还可以用PCA做一件事。回想一下这个坐标为 [2, 3] 的示例。我们发现,如果你将其投影到Z轴上,最终得到3.55。你可以做的一件事是,如果你有一个示例,其中z=3.55,仅给定这个数字z=3.55,我们能尝试找出原始示例是什么吗?

事实证明,PCA中有一个称为重建的步骤,即尝试从这个数字z=3.55回到原始的2个数字x1和x2。事实证明,你没有足够的信息来完全恢复x1和x2,但你可以尝试近似它。具体来说,公式是取这个数字3.55(即z)乘以我们刚才拥有的那个长度为1的向量 [0.71, 0.71]

这最终得到 [2.52, 2.52],也就是这里的这个点。因此,我们可以用这个新点(坐标为 [2.52, 2.52])来近似原始训练示例(坐标为 [2, 3])。原始点与投影点之间的差异是这里的小线段。在这种情况下,这是一个不错的近似:[2.52, 2.52][2, 3] 并不远。因此,仅用一个数字,我们就可以对原始训练示例的坐标进行合理的近似。这被称为PCA的重建步骤。


总结

本节课中我们一起学习了PCA算法。PCA算法查看你的原始数据,并选择一个或多个新轴(Z或可能是Z1和Z2)来表示你的数据。通过将原始数据投影到你的新轴上,这为你提供了一组更小的数字,你可以用它们来可视化你的数据。

你已经看到了数学原理,现在让我们看看如何在代码中实现它。在下一个视频中,我们将看看如何使用Scikit-learn库自己应用PCA。让我们继续下一个视频。

133:使用Scikit-learn实现PCA 🛠️

在本节课中,我们将学习如何使用Scikit-learn库来实现主成分分析(PCA)。我们将从数据预处理开始,逐步完成PCA的拟合、方差解释和最终的数据转换,并了解PCA的常见应用场景。

概述 📋

PCA是一种用于降维和数据可视化的强大技术。通过将高维数据投影到少数几个主成分上,我们可以捕捉数据中的主要变化模式。本节将详细介绍在Scikit-learn中执行PCA的步骤。

实现步骤 🚶‍♂️

上一节我们介绍了PCA的数学原理,本节中我们来看看如何在代码中实现它。

1. 特征缩放

如果数据特征的值域差异很大,进行预处理以缩放特征至可比较的范围是重要的。例如,在分析不同国家的特征时,GDP可能以万亿美元计,而其他特征可能小于100。在这种情况下,特征缩放有助于PCA为你找到一个好的坐标轴选择。

2. 拟合PCA模型

下一步是运行PCA算法来“拟合”数据,以获得两个或三个新轴Z1、Z2,可能还有Z3。这里假设你想要两个或三个轴以便在2D或3D中可视化数据。如果你需要更多轴,PCA实现也可以提供,只是更难可视化。

在Scikit-learn中,你将使用fit函数或fit方法来完成此操作。PCA中的fit函数会自动执行均值归一化,它会减去每个特征的均值,因此你无需单独执行均值归一化。

运行fit函数后,你将获得新轴Z1、Z2(可能还有Z3)。在PCA中,我们也称这些为主成分,其中Z1是第一主成分,Z2是第二主成分,Z3是第三主成分。

3. 检查解释方差比

之后,建议查看这些新轴或新主成分各自解释了数据中多少方差。这让你了解将这些数据投影到这些轴上是否有助于保留原始数据集中的大部分变异性或信息。这是通过explained_variance_ratio_函数完成的。

4. 转换数据

最后,你可以转换数据,即将其投影到新的主成分轴上。这是通过transform方法完成的。然后,对于每个训练示例,你只需得到两个或三个数字,并可以绘制这些数字以详细可视化你的数据。

以下是PCA代码的具体示例:

代码示例 💻

这是一个数据集X,包含六个示例。X是一个NumPy数组,包含这里的六个示例。

为了运行PCA将此数据从两个数字(x1, x2)减少到仅一个数字Z,你需要运行PCA并要求它拟合一个主成分。因此,这里的n_components等于1。

from sklearn.decomposition import PCA
import numpy as np

# 示例数据
X = np.array([...]) # 你的数据

# 创建PCA模型,指定主成分数量为1
pca1 = PCA(n_components=1)
# 拟合模型
pca1.fit(X)

事实证明,如果你打印pca1.explained_variance_ratio_,结果是0.992。这告诉你,在这个例子中,当你选择一个轴时,它捕获了原始数据集中99.2%的变异性或信息。

最后,如果你想获取每个训练示例并将其投影到一个数字上,你可以调用transform方法。

# 转换数据
Z = pca1.transform(X)

这将输出一个包含六个数字的数组,对应你的六个训练示例。例如,第一个训练示例[1, 1]投影到Z轴上得到数字1.383,依此类推。因此,如果要用一个维度可视化这些数据,这个数字将用于表示第一个示例。第二个示例被投影为这个数字,等等。

在可选实验中,你可以看到这六个示例已被投影到这条轴上,这就是为什么所有六个示例现在都位于这条看起来像这样的线上。第一个训练示例[1, 1]被映射到这个点,该点距离原点的距离为1.38。所以这就是为什么这里是1.38。

另一个示例 🔄

这个数据是二维数据,我们将其降为一维。如果你要计算两个主成分呢?从二维开始,最终也得到二维。这对于可视化来说不是那么有用,但可能有助于我们更好地理解PCA及其代码的工作原理。

这是相同的代码,只是我将n_components改为了2。我要求算法找到两个主成分。

# 创建PCA模型,指定主成分数量为2
pca2 = PCA(n_components=2)
pca2.fit(X)

如果你这样做,pca2.explained_variance_ratio_变为[0.992, 0.0008]。这意味着Z1(第一主成分)仍然解释了99.2%的方差,而Z2(第二主成分或第二轴)解释了0.8%的方差。这两个数字加起来等于1,因为数据是二维的,所以Z1和Z2两个轴共同解释了数据中100%的方差。

如果你要转换或将数据投影到Z1和Z2轴上,这就是你得到的结果。现在,第一个训练示例被映射到这两个数字,对应于在Z1和Z2上的投影。第二个示例被投影到Z1和Z2上,变成了这两个数字。

如果你要重建原始数据,大致上这是Z1,这是Z2。那么第一个训练示例[1, 1]在Z1轴上的距离为1.38(因此是这个数字),在Z2轴上的距离为0.29(因此是这个距离)。重建实际上看起来与原始数据完全相同,因为如果你将二维数据“降维”到二维数据,就没有近似,你可以通过投影到Z1和Z2上得到原始数据集。

PCA的应用场景 🌐

在结束之前,我想分享一些应用PCA的建议。

PCA经常用于可视化,你将数据减少到两个或三个数字,以便可以绘图,就像你在早期关于不同国家数据的视频中看到的那样,因此可以可视化不同的国家。

你可能偶尔会听到PCA的其他一些应用,这些应用在过去(可能是10、15、20年前)更流行,但现在少得多。

PCA的另一个可能用途是数据压缩。例如,如果你有一个包含许多不同汽车的数据库,每辆车有50个特征,但它占用了数据库太多空间,或者通过互联网传输50个数字需要太长时间,那么你可以做的一件事就是将这些50个特征减少到更少的特征。可以是10个特征,对应10个轴或10个主成分。你无法轻易可视化10维数据,但这是所需存储空间的五分之一,或者是网络传输成本的五分之一。许多年前,我更多地看到PCA用于此应用,但今天,随着现代存储能够存储相当大的数据集,以及现代网络能够比以前更快地传输更多数据,我很少看到这种PCA应用。

PCA的另一个应用,同样在过去(可能是10年、20年前)更常见,但现在少得多,是用于加速监督学习模型的训练。其思想是,如果你有1000个特征,拥有1000个特征使得监督学习算法运行缓慢,也许你可以使用PCA将其减少到100个特征。然后你的数据集基本上更小,你的监督学习算法可能运行得更快。这在一些老一代学习算法(例如支持向量机)的运行时间上曾起到作用。但事实证明,对于现代机器学习算法(如深度学习),这实际上没有太大帮助。更常见的做法是直接将高维数据集输入到你的神经网络中,而不是运行PCA,因为PCA也有一些计算成本。所以你可能会在其他一些研究论文中听到这个,但我真的不再经常看到这样做了。

但我今天使用PCA最常见的事情是可视化,我发现将高维数据降维以进行可视化非常有用。

总结 🎯

本节课中我们一起学习了如何使用Scikit-learn库实现PCA。我们涵盖了从数据预处理、模型拟合、解释方差分析到数据转换的完整流程。我们还探讨了PCA在数据可视化、压缩以及加速模型训练等方面的应用,并了解了在现代机器学习实践中其最常见的用途是数据可视化。掌握PCA将帮助你在面对新的高维数据集时,能够有效地将其降至二维或三维进行可视化,从而获得对数据的新见解。

134:什么是强化学习?🤖

在本节课中,我们将要学习强化学习的基本概念。强化学习是机器学习的核心支柱之一,它通过奖励和惩罚机制,让机器自主学会如何采取行动以达到最佳目标。尽管目前在商业应用中不如监督学习广泛,但强化学习在机器人控制、游戏和优化等领域展现出巨大潜力。


强化学习示例:自动驾驶直升机 🚁

上一节我们介绍了强化学习的整体概念,本节中我们来看看一个具体例子:让自动驾驶直升机学会飞行。

这是一架斯坦福自动驾驶直升机,重32磅,配备了机载计算机、GPS、加速度计、陀螺仪和磁罗盘,能精确感知自身状态。控制这类直升机通常需要使用操纵杆,程序需要每秒十次根据直升机的位置、方向、速度等信息(我们称之为状态 S),来决定如何操纵两个控制杆以保持直升机平衡并飞行。

公式表示任务:

动作 A = 函数(状态 S)

通过强化学习算法,我们甚至能让这架直升机学会倒飞等特技动作。以下是实现这一目标的关键思路。


为什么不用监督学习?🤔

你可能会想,是否可以使用监督学习来解决这个问题?即收集大量状态数据,并让人类专家标注出每个状态下应采取的最佳动作,然后训练一个神经网络来学习从状态到动作的映射。

然而,这种方法存在挑战:

  • 直升机在空中飞行时,对于任一状态,“唯一正确”的动作通常非常模糊(例如,操纵杆应该向左推一点还是推很多?)。
  • 很难获取一个包含理想动作的大规模高质量数据集 (X, Y)

因此,对于控制直升机这类机器人任务,监督学习效果不佳,我们转而使用强化学习。


核心:奖励函数 🏆

强化学习的一个关键输入是奖励函数。它告诉智能体(如直升机)什么时候做得好,什么时候做得不好。

理解奖励函数的一个好方法是类比训练小狗

  • 当小狗行为良好时,你说“好狗狗”并给予奖励。
  • 当小狗行为不当时,你说“坏狗狗”并给予惩罚。
  • 小狗通过这种方式自行学会多做能获得奖励的事,少做会导致惩罚的事。

训练强化学习算法也是如此:

  • 当直升机飞行平稳时,给予正奖励(例如,每秒 +1)。
  • 当直升机飞行不佳或坠毁时,给予负奖励(例如 -1000)。

代码示例奖励逻辑:

if helicopter_is_flying_well:
    reward = +1
elif helicopter_crashes:
    reward = -1000
else:
    reward = -0.1

奖励函数的强大之处在于,你只需要告诉算法“做什么”(即目标),而不是“怎么做”(即具体每一步的最佳动作),这为系统设计提供了极大的灵活性。


更多应用实例 🎮

强化学习不仅用于控制直升机,还成功应用于许多其他领域。以下是部分应用场景:

  • 机器人控制:例如,训练机器狗跨越障碍。通过奖励其向屏幕左侧移动,机器狗能自动学会如何协调四肢来越过各种障碍。
  • 工厂优化:重新安排工厂布局和流程,以最大化生产效率和吞吐量。
  • 金融交易:优化大宗股票的交易执行策略,以在较长时间内平滑卖出,从而获得更优价格。
  • 游戏:从国际象棋、围棋到各类电子游戏,强化学习算法已能达到甚至超越人类顶尖水平。

总结 📚

本节课中我们一起学习了强化学习的基础。强化学习通过定义奖励函数,让智能体在环境中通过试错自主学习最佳策略,而不是依赖于大量人工标注的数据。其核心思想可以概括为:

核心任务: 让算法学会选择一个函数,将状态 S 映射到动作 A,以最大化随时间累积的总奖励。

虽然目前其应用广度不及监督学习,但强化学习在机器人学、游戏AI和复杂系统优化等领域正发挥着不可替代的作用。在接下来的课程中,我们将进一步形式化强化学习问题并开始开发自动选择优良动作的算法。

135:强化学习火星探测器示例 🚀

在本节课中,我们将通过一个简化的火星探测器示例,来具体了解强化学习的基本形式。我们将学习强化学习中的核心概念,如状态、动作、奖励和回报,并理解它们如何共同作用,以指导智能体(如火星车)做出决策。


为了具体阐述强化学习的形式化框架,我们不会使用像直升机或机器狗那样复杂的例子,而是采用一个受火星探测器启发的简化示例。这个例子改编自斯坦福大学教授艾玛·布鲁斯基尔和我的一位合作者杰克·里蒂·阿格拉韦尔的构思,后者编写的代码目前正在实际控制火星探测器,并帮助我梳理和构建了这个示例。

让我们开始吧。

我们将基于一个受火星探测器启发的简化示例来展开强化学习。在这个应用中,探测器可以处于六个位置中的任何一个,如上图所示的六个方框。探测器可能从某个位置开始,比如这里显示的四个点中的位置四。在强化学习中,火星探测器的位置被称为状态

我将这六个状态分别称为状态1、状态2、状态3、状态4、状态5和状态6。因此,探测器从状态4开始。

探测器被送往火星是为了执行不同的科学任务。它可以前往不同地点,使用传感器(如钻头、雷达或光谱仪)分析星球上不同位置的岩石,或者前往不同地点拍摄有趣的照片供地球上的科学家研究。在这个例子中,左侧的状态1有一个非常有趣的表面,科学家非常希望机器人能去采样;状态6也有一个相当有趣的表面,科学家也比较希望探测器去采样,但不如状态1那么有趣。因此,我们更希望在状态1执行科学任务,而不是在状态6,但状态1距离更远。

我们将通过奖励函数来反映状态1可能具有的更高价值。

因此,状态1的奖励是 R(1) = 100。状态6的奖励是 R(6) = 40。而中间所有其他状态(状态2、3、4、5)的奖励,我将其写为 R(s) = 0,因为在这些状态下没有那么多有趣的科学工作可做。

在每个时间步,探测器可以选择两个动作之一:它可以向左走,也可以向右走。

那么问题是,探测器应该做什么?在强化学习中,我们非常关注奖励,因为这是我们判断机器人做得好坏的方式。

让我们看一些可能发生的情况示例。

如果机器人选择向左走,从状态4开始。那么,最初从状态4开始,它会获得奖励0;向左移动后到达状态3,再次获得奖励0;然后到达状态2,获得奖励0;最后到达状态1,获得奖励100。对于这个应用,我假设当它到达状态1或状态6时,这一天就结束了。在强化学习中,我们有时称此为终止状态。这意味着在到达这些终止状态之一后,它会在该状态获得奖励,但之后不会再发生任何事情。也许机器人电量耗尽或当天时间用完,这就是为什么它只能享受100或40的奖励,然后这一天就结束了,之后无法获得额外的奖励。

现在,机器人也可以选择向右走。在这种情况下,从状态4开始,它首先会获得奖励0;然后向右移动到达状态5,获得另一个奖励0;接着到达右侧的另一个终止状态——状态6,并获得奖励40。

但是,向左走和向右走并不是唯一的选择。机器人可以做的一件事是:从状态4开始,决定向右移动。它从状态4到5,在状态4获得奖励0,在状态5获得奖励0;然后它可能改变主意,决定开始向左走,如下所示。在这种情况下,它会在状态4获得奖励0,然后到状态3、状态2,最后到达状态1时获得奖励100。

在这一系列动作和状态中,机器人浪费了一些时间,所以这可能不是一个很好的行动选择,但它是算法可以选择的一种方式,不过希望它不会选择这个。

总结一下,在每个时间步,机器人处于某个状态,我称之为 S。它可以选择一个动作 A,并且享受从该状态获得的奖励 R(S)。作为其动作的结果,它会到达某个新状态 S‘

作为一个具体例子,当机器人在状态4时,它采取了“向左”的动作,它“享受”了(也许并不享受)与该状态4相关的奖励0,并最终进入了新状态3。当你学习具体的强化学习算法时,你会发现这四个要素——状态 S、动作 A、奖励 R(S) 和下一个状态 S‘——基本上是你每次采取动作时都会发生的事情,这将成为强化学习算法在决定如何采取行动时所关注的核心要素。为了清晰起见,这里的奖励 R(S) 是与状态 S 相关联的,所以这个奖励0是与状态4相关联的,而不是与状态3相关联的。

以上就是强化学习应用如何运作的形式化描述。在下一个视频中,让我们看看如何具体指定我们希望强化学习算法做什么。特别是,我们将讨论强化学习中的一个重要概念——回报。让我们进入下一个视频,看看那是什么意思。

136:强化学习中的回报 📈

在本节课中,我们将学习强化学习中的一个核心概念——回报。回报用于量化评估智能体在一系列行动中获得的总收益,并考虑了时间因素对奖励价值的影响。

上一节我们介绍了强化学习中的状态、行动和即时奖励。本节中我们来看看如何将这些即时奖励整合成一个衡量长期表现的单一指标。

回报的定义

回报是智能体在一条轨迹(即一系列状态和行动)中获得的所有奖励的加权和。其核心思想是:越早获得的奖励,其价值越高。这通过一个称为折扣因子的参数来实现。

回报的通用计算公式如下:

G = R1 + γ * R2 + γ² * R3 + γ³ * R4 + ... + γ^(T-1) * R_T

其中:

  • G 代表回报。
  • R1, R2, R3, ... R_T 代表在每个时间步获得的即时奖励。
  • γ (Gamma) 是折扣因子,是一个介于0和1之间的数(通常为0.9、0.99等)。
  • T 是到达终止状态的总步数。

折扣因子的作用

折扣因子 γ 使强化学习算法变得“缺乏耐心”。它赋予即时奖励全额价值,而对未来奖励进行折现。γ 值越小,算法越看重短期收益;γ 值越接近1,算法越有远见,更考虑长期收益。

在金融领域,折扣因子有直观的解释:它类似于利率或货币的时间价值。今天的一美元比未来的一美元更值钱,因为今天的钱可以投资生息。

计算示例

让我们通过一个简单的网格世界例子来具体计算回报。假设智能体从状态4出发,始终向左移动,获得的奖励序列为:[0, 0, 0, 100](到达状态1终止)。设定折扣因子 γ = 0.5

其回报计算过程为:

G = 0 + 0.5 * 0 + 0.5² * 0 + 0.5³ * 100
  = 0 + 0 + 0 + 12.5
  = 12.5

以下是不同策略下,从各状态出发的回报示例:

  • 策略A:始终向左

    • 从状态1开始:G = 100
    • 从状态2开始:G = 50
    • 从状态3开始:G = 25
    • 从状态4开始:G = 12.5
  • 策略B:始终向右

    • 从状态4开始:G = 10 (计算:0 + 0.5*0 + 0.25*40 = 10
    • 从状态5开始:G = 20
  • 策略C:混合策略(在状态5时向右,其他向左)

    • 从状态5开始:G = 20 (计算:0 + 0.5*40 = 20

通过比较可以看出,从状态4出发,策略A(回报12.5)优于策略B(回报10)。而策略C在状态5时做出了更优的局部决策。

回报与负奖励

当环境中存在负奖励(惩罚)时,折扣因子会产生一个有趣的效果:它会激励系统尽可能地将惩罚推迟到遥远的未来

例如,如果需要支付10美元(即奖励为-10),那么如果能将支付推迟几年,由于金钱的时间价值,未来的10美元比现在的10美元“代价”更小。因此,回报机制自然地引导系统规避近期的惩罚。

本节课中我们一起学习了强化学习的核心概念——回报。我们明确了回报是未来奖励的折扣总和,理解了折扣因子如何影响智能体对短期与长期收益的权衡,并通过实例演示了如何计算不同策略下的回报。

下一节,我们将以此为基础,正式定义强化学习算法的目标。

137:强化学习中的决策策略 🧠

在本节课中,我们将学习强化学习算法如何选择行动,并正式介绍“策略”这一核心概念。

概述

上一节我们讨论了强化学习中的回报概念。本节中,我们将探讨算法如何决定在特定状态下采取何种行动,这被称为“策略”。

什么是策略?

在强化学习问题中,有多种选择行动的方式。以下是几个例子:

  • 选择较近的奖励:如果左侧奖励更近,则向左移动;如果右侧奖励更近,则向右移动。
  • 选择较大的奖励:总是朝着当前可获得的较大奖励方向移动。
  • 选择较小的奖励:虽然这通常不是好主意,但这也是一种可能的选项。
  • 特定规则:例如,通常向左移动,除非距离右侧奖励仅一步之遥,此时则向右移动。

策略的正式定义

强化学习的目标是找到一个称为策略的函数,记作 π。策略 π 的职责是接收任意状态 S 作为输入,并映射到它希望我们采取的行动 A

用公式表示,策略函数为:
A = π(S)

例如,对于某个具体策略,其映射关系可能如下:

  • 在状态 S2 时,策略输出行动“向左”。
  • 在状态 S3 时,策略输出行动“向左”。
  • 在状态 S4 时,策略输出行动“向左”。
  • 在状态 S5 时,策略输出行动“向右”。

因此,π(S) 告诉我们,在状态 S 下应该采取什么行动。

策略的目标

强化学习的最终目标是找到一个策略 ππ(S),它能告诉你在每个状态下应采取什么行动,以最大化总回报

关于术语“策略”的说明:它可能不是描述 π 功能最直观的词汇,但已成为强化学习领域的标准术语。或许称 π 为“控制器”会更自然,但“策略”是目前通用的叫法。

总结

本节课我们一起学习了强化学习中的核心概念——策略。我们了解到,策略 π 是一个函数,它根据当前状态 S 来决定行动 A,其终极目标是通过一系列行动最大化累积回报。

在上一节中,我们介绍了从状态、行动、奖励到回报等一系列概念。下一节,我们将快速回顾这些概念,然后开始探索如何寻找优秀策略的算法。

138:强化学习核心概念回顾 🧠

在本节课中,我们将回顾强化学习的关键概念,并了解这些概念如何应用于不同场景。我们将以火星车例子为基础,系统地梳理状态、动作、奖励、折扣因子、回报和策略等核心要素。


状态、动作与奖励

上一节我们介绍了火星车的例子,本节中我们来看看强化学习问题的基本构成要素。

强化学习问题通常由以下几个核心部分定义:

  • 状态:描述系统当前情况的信息。在火星车例子中,状态是六个可能的位置,编号为1到6。
  • 动作:智能体在每个状态下可以执行的操作。火星车的动作是向左或向右移动。
  • 奖励:智能体在执行某个动作并到达新状态后获得的即时反馈。火星车在状态1(最左)获得奖励100,在状态6(最右)获得奖励40,在其他状态获得奖励0。

折扣因子与回报

理解了即时反馈后,我们需要考虑长期收益。折扣因子和回报就是为此设计的。

折扣因子(通常用希腊字母 γ 表示)是一个略小于1的数(例如0.5或0.99),它决定了未来奖励在当前的价值。回报则是从当前时刻开始,所有未来奖励经过折扣后的总和。

回报的计算公式如下:
G = R1 + γ * R2 + γ^2 * R3 + ...
其中,R1, R2, R3... 代表未来每一步获得的奖励。

在火星车例子中,我们使用的折扣因子是 0.5

策略

有了衡量长期收益的“回报”,智能体需要一个行动指南来最大化它,这就是策略。

策略(通常用 π 表示)是一个函数,它根据当前状态告诉智能体应该采取哪个动作。强化学习算法的目标就是找到一个最优策略,使得智能体在任何状态下获得的期望回报最大。

概念的应用

这些核心概念构成了一套通用形式化框架,可以应用于众多领域。

以下是几个不同领域的应用示例:

  • 自动驾驶直升机

    • 状态:直升机的位置、姿态、速度等。
    • 动作:操纵控制杆的各种方式。
    • 奖励:飞行平稳时+1,坠毁时-1000。
    • 目标:学习一个策略,根据状态输入,输出控制动作以安全飞行。
  • 国际象棋游戏

    • 状态:棋盘上所有棋子的位置。
    • 动作:所有合法的走法。
    • 奖励:赢棋+1,输棋-1,和棋0。
    • 折扣因子:通常非常接近1,如0.99。
    • 目标:学习一个策略,根据棋盘局面,选择能最大化获胜概率的走法。

马尔可夫决策过程

这套形式化框架有一个专门的名称:马尔可夫决策过程

MDP 中的“马尔可夫”性质是指:未来只取决于当前状态,而与如何到达当前状态的历史无关。换句话说,在 MDP 中,你只需要知道“现在在哪里”,而不需要知道“过去是怎么来的”。

另一种理解 MDP 的视角是智能体与环境的交互循环:

  1. 智能体根据策略 π 选择动作 A。
  2. 动作 A 作用于环境,环境发生变化。
  3. 环境反馈给智能体新的状态 S‘ 和即时奖励 R。
  4. 智能体根据新状态再次决策,循环往复。

这个循环清晰地刻画了强化学习智能体通过试错进行学习的过程。


本节课中我们一起学习了强化学习的核心概念框架:状态、动作、奖励、折扣因子、回报和策略。我们了解到这套框架被称为马尔可夫决策过程,并且看到了它在控制问题和游戏等不同领域的广泛应用。在接下来的视频中,我们将开始探索如何计算一个关键量——状态-动作价值函数,这是开发强化学习算法的第一步。

139:状态-动作价值函数定义 🧠

在本节课中,我们将要学习强化学习中的一个核心概念——状态-动作价值函数,它通常被称为 Q函数。理解这个函数是后续学习强化学习算法的基础。


概述

状态-动作价值函数是一个关键的量,强化学习算法会尝试计算它。这个函数告诉我们,在特定状态下采取某个特定动作,然后从该点开始最优地行动,所能获得的总回报是多少。

上一节我们介绍了回报和最优策略的概念,本节中我们来看看如何量化评估在某个状态下采取某个具体动作的“好坏”。


Q函数的定义

状态-动作价值函数通常用大写字母 Q 表示。它是一个关于状态 S 和动作 A 的函数。

Q(S, A) 的值等于:如果你从状态 S 开始,只采取一次动作 A,然后在此之后都遵循最优策略行动,最终所能获得的总回报。

用公式可以表示为:
Q(S, A) = 回报(从状态S开始,执行动作A,之后遵循最优策略π*)

这里有一个看似矛盾的地方:为了计算 Q(S, A),我们需要知道“之后的最优行为”。但如果我们已经知道了最优策略,为什么还需要计算 Q 函数呢?这个定义确实有些循环论证的意味。不过请放心,在后续的具体算法中,我们会找到一种方法,在尚未得出最优策略之前就能计算出 Q 函数。


通过例子理解 Q 函数

让我们通过之前火星车探索的例子来具体理解 Q 函数。假设折扣因子 γ = 0.5,并且我们已经知道下图所示的是一个最优策略(在状态2、3、4向左,在状态5向右)。

以下是几个计算 Q(S, A) 值的具体例子:

例子1:计算 Q(状态2, 向右)

  1. 在状态2,执行动作“向右”,到达状态3,获得即时奖励 0
  2. 从状态3开始,遵循最优策略(向左),回到状态2,奖励 0
  3. 在状态2,遵循最优策略(向左),到达状态1(终点),获得奖励 100
    因此,总回报 = 0 + 0.5 × 0 + 0.5² × 0 + 0.5³ × 100 = 12.5
    所以,Q(状态2, 向右) = 12.5。这个值仅仅计算了“如果向右然后最优行动”的回报,并不代表向右是个好主意。

例子2:计算 Q(状态2, 向左)

  1. 在状态2,执行动作“向左”,直接到达终点状态1,获得奖励 100
    因此,总回报 = 0 + 0.5 × 100 = 50
    所以,Q(状态2, 向左) = 50

例子3:计算 Q(状态4, 向左)

  1. 在状态4向左,到状态3 (奖励0)。
  2. 从状态3最优行动(向左),到状态2 (奖励0)。
  3. 从状态2最优行动(向左),到状态1 (奖励100)。
    总回报 = 0 + 0.5×0 + 0.5²×0 + 0.5³×100 = 12.5
    所以,Q(状态4, 向左) = 12.5

按照这个方法为所有状态和动作进行计算,我们可以得到完整的 Q 函数表,如下图所示:

在终点状态(1和6),无论采取什么动作,都只会得到对应的终点奖励(100或40)。


为什么 Q 函数重要?

状态-动作价值函数(Q函数)之所以关键,是因为一旦我们能够计算出它,它就为我们提供了一种选择动作的方法。

观察上图中的 Q 值,你会发现一个规律:

  • 在状态2,Q(左)=50 大于 Q(右)=12.5
  • 在状态3,Q(左)=25 大于 Q(右)=6.25
  • 在状态4,Q(左)=12.5 大于 Q(右)=10
  • 在状态5,Q(右)=20 大于 Q(左)=10

从任何状态 S 出发,你能获得的最佳可能回报,就是 Q(S, A) 对所有可能动作 A 取最大值:
最佳回报(S) = max over A of Q(S, A)

更重要的是,为了获得这个最佳回报,你应该选择的动作,正是那个能使 Q(S, A) 值最大的动作 A

因此,如果我们有办法计算出每个状态和每个动作的 Q(S, A),那么制定策略就变得非常简单:当处于状态 S 时,只需查看所有可能的动作 A,然后选择使 Q(S, A) 最大的那个动作。

用策略函数表示就是:
π*(S) = argmax over A of Q(S, A)

这直观上是合理的,因为 Q(S, A) 衡量了“在状态S下采取动作A,然后一路最优能获得多少回报”。为了获得最大总回报,你自然应该选择能带来最大 Q 值的那个动作。


术语说明

  • Q函数状态-动作价值函数 这两个术语可以互换使用。
  • 在部分强化学习文献中,这个函数也可能被称为 最优Q函数 或记为 Q*,它们指代的是我们这里定义的同一个概念。在本课程中,你无需为此担心。

总结

本节课中我们一起学习了状态-动作价值函数(Q函数)

我们了解到:

  1. Q(S, A) 定义了在状态 S 下执行动作 A,之后遵循最优策略所能获得的总回报。
  2. 通过具体的火星车例子,我们计算了几个状态-动作对的 Q 值。
  3. Q 函数的核心作用在于:一旦已知 Q 函数,最优策略就是简单地选择能使 Q 值最大化的动作,即 π*(S) = argmax_A Q(S, A)

尽管 Q 函数的定义看起来有些循环(需要最优策略来计算,而最优策略又需要 Q 函数来得出),但在接下来的视频中,我们将探讨如何实际计算这些 Q 值,并最终构建出能够学习最优策略的强化学习算法。

140:状态-动作价值函数示例 🚀

在本节课中,我们将通过一个火星车示例,深入探讨状态-动作价值函数 Q(s, a) 的数值变化。我们将直观地理解强化学习问题中,奖励和折扣因子如何影响 Q 函数的值、最优回报以及最优策略。课程最后提供了一个可选实验,供你动手调整参数并观察结果。


上一节我们介绍了状态-动作价值函数 Q(s, a) 的基本概念。本节中,我们来看看一个具体的火星车示例,通过调整参数来观察 Q 函数如何变化。

我们使用一个 Jupyter Notebook 来演示。代码中定义了两个动作和多个状态,并设置了终端奖励。左侧终端的奖励为 100,右侧终端的奖励初始为 40,中间状态的奖励为 0。折扣因子 γ 初始设为 0.5。我们暂时忽略“失误概率”,后续视频会讨论它。

运行以下代码,可以计算并可视化最优策略以及 Q 函数 Q(s, a):

# 初始化参数
num_states = 6
num_actions = 2
terminal_left_reward = 100
terminal_right_reward = 40
gamma = 0.5

目前你无需关心计算 Q(s, a) 的具体算法,只需关注其输出值。这些值与课程中展示的是一致的。

现在,有趣的部分开始了。我们可以修改一些参数,观察 Q(s, a) 如何随之改变。

首先,我将右侧终端的奖励修改为一个更小的值,例如 10:

terminal_right_reward = 10

重新运行代码后,观察 Q(s, a) 的变化。例如,在状态 5:

  • 选择向左并在之后采取最优行为,得到的回报是 6.25。
  • 选择向右并在之后采取最优行为,得到的回报仅为 5。

当右侧奖励变得很小(只有 10)时,即使你离它很近,最优策略也变成了从所有状态都向左移动。

接下来,我们进行其他调整。将右侧终端奖励恢复为 40,但将折扣因子 γ 改为 0.9:

terminal_right_reward = 40
gamma = 0.9

折扣因子越接近 1,表示智能体越“有耐心”,更愿意为了未来的高回报等待更久,因为未来的奖励不会被大幅折现(乘以 0.5 的高次幂,而是乘以 0.9 的高次幂)。

重新运行代码后,观察 Q(s, a) 的变化。现在在状态 5,向左移动的回报是 65.61,高于向右的回报 36。值得注意的是,36 正好是 0.9 乘以终端奖励 40,这些数值是合理的。当智能体更有耐心时,即使在状态 5,它也会选择向左。

现在,让我们将 γ 改为一个更小的数,比如 0.3:

gamma = 0.3

这表示对未来奖励进行大幅折现,智能体会变得“极其没有耐心”。重新运行代码,行为再次改变。注意,现在在状态 4,智能体没有耐心去争取更大的 100 奖励,因为折扣因子 γ 太小(0.3)。它宁愿选择更近的、只有 40 的奖励。

以下是你可以尝试调整的核心参数及其影响:

  • 终端奖励:直接影响目标的价值。
  • 折扣因子 γ:控制智能体对未来奖励的重视程度。γ 越大,越有耐心;γ 越小,越短视。

我希望你能通过自己动手调整这些数值并运行代码,获得以下直观感受:

  1. Q(s, a) 的值如何随参数变化。
  2. 最优回报(即 Q(s, a) 两个值中较大的那个)如何变化。
  3. 最优策略如何随之改变。

因此,我鼓励你去完成这个可选实验,尝试修改奖励函数和折扣因子 γ,观察 Q(s, a) 的值、不同状态下的最优回报以及最优策略如何随这些不同值而变化。

通过动手实践,你将能更深刻地理解在强化学习应用中,这些不同的量是如何受到奖励设置等因素影响的。

在你完成实验后,我们将准备好回来讨论可能是强化学习中最重要的一个方程——贝尔曼方程。希望你在可选实验中玩得开心,之后让我们一起来学习贝尔曼方程。


本节课中我们一起学习了如何通过火星车示例来探索状态-动作价值函数 Q(s, a)。我们通过调整终端奖励和折扣因子,直观地观察了 Q 值、最优回报和最优策略的动态变化,这为理解强化学习智能体的决策过程奠定了坚实的基础。

141:36_03_03_贝尔曼方程 🧠

概述

在本节课中,我们将要学习强化学习中的一个核心概念——贝尔曼方程。这个方程为我们提供了一种计算状态-动作价值函数 Q(s, a) 的方法,从而帮助我们找到从每个状态出发的最佳动作。

状态-动作价值函数回顾

上一节我们介绍了状态-动作价值函数 Q(s, a) 的定义。它表示从状态 s 出发,采取动作 a 一次,然后在此之后始终采取最优行为所能获得的总回报。

如果我们可以计算出 Q(s, a),那么从任何状态选择最佳动作就变得很简单:只需选择能使 Q(s, a) 值最大的动作 a。

核心问题:如何计算这些 Q(s, a) 值?在强化学习中,有一个关键方程可以帮助我们解决这个问题,那就是贝尔曼方程。

贝尔曼方程介绍

为了描述贝尔曼方程,我们首先需要明确一些符号定义:

  • s:表示当前状态。
  • r(s):表示当前状态 s 的即时奖励。例如,在我们的 MDP 示例中,r(状态1) = 100,r(状态2) = 0,r(状态6) = 40。
  • a:表示在当前状态 s 下采取的动作。
  • s‘:表示在执行动作 a 后到达的新状态。例如,从状态4执行动作“左”,会到达状态3。
  • a’:表示在到达新状态 s‘ 后可能采取的动作。

符号约定是:s 和 a 对应当前状态和动作,而加上撇号(‘)的 s’ 和 a‘ 则对应下一个状态和动作。

以下是贝尔曼方程的核心公式:

Q(s, a) = r(s) + γ * max[a‘] Q(s‘, a‘)

这个公式包含了很多信息,让我们先通过几个例子来理解它,然后再探讨其背后的原理。

贝尔曼方程应用示例

让我们通过具体例子来应用贝尔曼方程。

示例一:计算 Q(状态2, 动作右)
假设当前状态是2,动作是“右”。

  • 执行“右”动作后到达的新状态 s‘ 是状态3。
  • 根据贝尔曼方程:
    Q(2, 右) = r(2) + γ * max[a‘] Q(3, a‘)
  • 已知:r(2) = 0,折扣因子 γ = 0.5。
  • 状态3有两个可能的Q值:Q(3, 左) = 25,Q(3, 右) = 6.25。因此 max[a‘] Q(3, a‘) = 25。
  • 计算:Q(2, 右) = 0 + 0.5 * 25 = 12.5。

这个结果与我们之前计算的 Q(2, 右) 值一致。

示例二:计算 Q(状态4, 动作左)
假设当前状态是4,动作是“左”。

  • 执行“左”动作后到达的新状态 s‘ 同样是状态3。
  • 根据贝尔曼方程:
    Q(4, 左) = r(4) + γ * max[a‘] Q(3, a‘)
  • 已知:r(4) = 0,γ = 0.5。
  • max[a‘] Q(3, a‘) 仍然是 25。
  • 计算:Q(4, 左) = 0 + 0.5 * 25 = 12.5。

因此,Q(4, 左) 也等于 12.5。

关于终止状态的说明:如果当前状态是终止状态(如状态1或6),那么没有下一个状态 s‘,贝尔曼方程简化为 Q(s, a) = r(s)。这就是为什么终止状态的 Q 值直接等于其奖励值(100 或 40)。

你可以暂停视频,尝试将贝尔曼方程应用到其他状态-动作对上,验证计算结果。

贝尔曼方程的直观理解

现在,让我们深入理解贝尔曼方程背后的逻辑。

首先,回顾一下 Q(s, a) 的定义:从状态 s 出发,采取动作 a,然后最优行动所获得的总回报。这个总回报是由一系列随时间衰减的奖励之和计算得出的:

总回报 = R1 + γ * R2 + γ² * R3 + ...

贝尔曼方程将这个总回报序列分解为两个部分:

  1. 即时奖励 r(s):这是在状态 s 执行动作 a 后立刻获得的奖励(即 R1)。
  2. 未来回报的折现值:到达新状态 s‘ 后,从该状态开始未来所有奖励总和的折现值。

更具体地说,方程中的 max[a‘] Q(s‘, a‘) 正是从新状态 s‘ 出发,采取最优策略所能获得的最佳总回报。而乘以折扣因子 γ,则表示这个未来回报在当前时刻的价值。

核心直觉:在强化学习中,从当前状态获得的总回报,等于“立即得到的奖励”加上“从下一个状态开始所能获得的最佳未来回报的折现值”。这就是贝尔曼方程的精髓。

为了更清晰地建立联系,让我们再看一个例子:Q(4, 左) = 12.5。

  • 这个总回报 12.5 来自于奖励序列:在状态4得0,在状态3得0,在状态2得0,最后在状态1得100,并经过折扣计算(0.5² * 100 = 25,再乘以一个γ得到12.5)。
  • 贝尔曼方程将其分解为:
    • 第一部分:r(4) = 0(即时奖励)。
    • 第二部分:γ * (从状态3开始的最佳回报)。从状态3开始的最佳回报正是 max[Q(3, 左), Q(3, 右)] = 25。
    • 因此,0 + 0.5 * 25 = 12.5。

我知道贝尔曼方程是一个有些复杂的方程。如果你觉得理解起来有困难,不必过于担心。只要掌握如何应用这个方程,你就能让后续的强化学习算法正确工作。我希望你至少能从高层次上理解“将总回报分解为即时奖励与未来回报”这一核心思想是合理的。

总结

本节课我们一起学习了强化学习中的关键工具——贝尔曼方程。

  • 我们回顾了状态-动作价值函数 Q(s, a) 的作用。
  • 我们引入了贝尔曼方程的公式:Q(s, a) = r(s) + γ * max[a‘] Q(s‘, a‘)
  • 我们通过具体示例演示了如何应用该方程进行计算。
  • 最后,我们探讨了方程背后的直观理解:总回报是即时奖励与折现后的未来最佳回报之和。

在接下来的课程中,我们将基于贝尔曼方程来开发实际的强化学习算法。在进入算法部分之前,有一个关于随机马尔可夫决策过程的可选视频,介绍了当动作执行效果具有随机性时的应用,你可以选择观看。之后,我们将正式开始构建强化学习算法。

142:37_03_04 随机环境(可选)🎲

在本节课中,我们将学习强化学习在随机环境中的应用。我们将探讨当机器人的行动结果不完全确定时,如何建模和优化其行为策略。


概述

在之前的课程中,我们假设机器人在执行一个动作后,会确定性地转移到下一个状态。然而,在实际应用中,由于各种不确定因素(如地面打滑、风力影响),行动的结果往往是随机的。本节将介绍如何将强化学习框架扩展到这种随机环境中。


随机环境示例

上一节我们介绍了确定性环境下的马尔可夫决策过程,本节中我们来看看当环境变得随机时会发生什么。

在某些应用中,当你采取一个行动时,结果并不总是完全可靠的。例如,如果你命令你的火星车向左移动,可能因为地面有小石子或非常滑,它打滑并朝错误的方向移动。在实践中,许多机器人并不总是能完全按照你的指令行动,因为有风吹离航线或地面打滑等原因。因此,我们之前讨论的强化学习框架有一个推广,用于建模随机或随机环境

继续我们简化的火星车示例,假设你采取行动并命令它向左移动。大多数时候它会成功,但如果有10%或0.1的概率,它实际上意外打滑并朝相反方向移动。

因此,如果你命令它向左移动,它有90%(即0.9)的概率正确向左移动,但有0.1的概率实际上向右移动。在这个例子中,它有90%的概率最终到达状态3,10%的概率到达状态5。

相反,如果你命令它向右移动并采取向右的行动,它有0.9的概率最终到达状态5,0.1的概率到达状态3。这就是一个随机环境的例子。


随机环境下的策略执行

让我们看看在这个强化学习问题中会发生什么。

假设你使用这里显示的策略,在状态2、3和4时向左移动,在状态5时尝试向右移动。如果你从状态4开始并遵循这个策略,那么你访问的实际状态序列可能是随机的。

例如,在状态4,你将向左移动,也许你有点幸运,它确实到达了状态3。然后你告诉它再次向左移动,也许它确实到达了那里。你告诉它再次向左移动,它到达了那个状态。如果发生这种情况,你最终会得到奖励序列 0, 0, 0, 100

但是,如果你第二次尝试完全相同的策略,也许你第二次的运气稍差一些。你从这里开始,直到向左移动,假设它成功了,所以从状态4得到0,从状态3得到0。在这里,你试图向左移动,但这次你不走运,机器人打滑并最终回到了状态4。然后你告诉它向左移动,然后向左,然后向左,最终得到100的奖励。在这种情况下,这将是你观察到的奖励序列,因为你从4到3,回到4,再到3,2,然后1。

甚至有可能,如果你从状态4开始,遵循策略向左移动,你可能在第一步就不走运,最终到达了状态5,因为它打滑了。然后在状态5,你命令它向右移动,它成功了,你最终到达这里。在这种情况下,你看到的奖励序列将是 0, 0, 40,因为你从4到5,然后到状态6。


期望回报

我们之前将回报写为这些折后奖励的总和。但是,当强化学习问题是随机的时,你并不能确定地看到一个奖励序列,相反,你会看到不同的奖励序列。

因此,在一个随机强化学习问题中,我们感兴趣的不是最大化回报,因为那是一个随机数。我们感兴趣的是最大化折后奖励总和的平均值。这里的“平均值”指的是,如果你将你的策略尝试一千次、一万次或一百万次,你会得到许多像那样的不同奖励序列。如果你对所有不同序列的这个折后奖励总和取平均值,那么这就是我们所说的期望回报

在统计学中,术语“期望”只是“平均”的另一种说法。但这意味着我们想要最大化我们预期平均获得的折后奖励总和。其数学符号表示如下:

公式: E[R1 + γR2 + ...]

其中 E 表示期望值。

因此,强化学习算法的工作是选择一个策略 π,以最大化折后奖励的平均值或期望总和。


总结与贝尔曼方程

综上所述,当你有一个随机强化学习问题或一个随机马尔可夫决策过程时,目标是选择一个策略,以决定在状态 S 下采取什么行动 A,从而最大化期望回报。

这与我们之前讨论的内容的最后一个不同之处在于,它稍微修改了贝尔曼方程。

这是我们之前写下的贝尔曼方程,但现在不同之处在于,当你采取行动 A 和状态 S 时,你到达的下一个状态 S' 是随机的。当你在状态 S 并尝试向左移动时,下一个状态 S' 可能是状态2,也可能是状态4。因此,S' 现在是随机的,这也是为什么我们在这里也放了一个平均算子或期望算子。

所以我们说,从状态 S 采取一次行动 A 并以最优方式行事的总回报,等于你立即获得的奖励(也称为即时奖励),加上折现因子 γ,再加上你预期平均获得的未来回报。


实验与影响

如果你想加深对这些随机强化学习问题的直觉,可以回到我刚才展示的可选实验。其中参数 misstep_prob(失误概率)是你的火星车朝你命令的相反方向移动的概率。

如果我们设置 misstep_prob 为 0.1 并执行笔记本,那么这里的数字就是最优回报。如果你采取最佳可能的行动,采取这个最优策略,但机器人有10%的时间步向错误方向。这些就是这个随机MDP的Q值。

请注意,这些值现在略低一些,因为你无法像以前那样好地控制机器人。Q值以及最优回报都下降了一点。事实上,如果你增加失误概率,比如有40%的时间机器人甚至不朝你命令的方向移动,只有60%的时间它按照你的指示移动,那么这些值最终会更低,因为你对机器人的控制程度降低了。

因此,鼓励你使用可选实验,改变失误概率的值,看看它如何影响最优回报或最优期望回报,以及Q值 Q(S, A)


展望

到目前为止,我们所做的一切都在使用这个马尔可夫决策过程,即这个只有六个状态的火星车。对于许多实际应用,状态数量会大得多。下一个视频将把我们目前讨论的强化学习或马尔可夫决策过程框架,推广到更丰富、可能更有趣的问题集,这些问题的状态空间更大,特别是具有连续状态空间。让我们在下一个视频中看看这个。


本节课总结

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

  1. 随机环境的概念:行动结果不确定,由概率分布决定。
  2. 随机环境下的策略执行会产生不同的状态和奖励序列。
  3. 强化学习在随机环境中的目标是最大化期望回报,即所有可能奖励序列的平均折后奖励总和。
  4. 随机环境修改了贝尔曼方程,需要引入期望算子 E 来计算未来回报的平均值。
  5. 环境随机性(如失误概率)会降低对系统的控制度,从而导致最优回报和Q值下降。

通过理解随机环境,我们为处理更复杂、更贴近现实的强化学习问题奠定了基础。

143:连续状态空间应用示例 🚀

在本节课中,我们将学习什么是连续状态空间,以及如何将之前讨论的概念推广到这种连续状态空间中。我们将通过对比简化的火星车例子,理解离散状态与连续状态的区别,并探讨在机器人控制(如汽车、直升机)和模拟登月器等实际应用中,状态是如何被定义的。


在上一节中,我们讨论了具有离散状态空间的马尔可夫决策过程。本节中,我们来看看当状态空间是连续时的情况。

许多机器人控制应用,包括你在实践实验室中操作的登月器应用,都拥有连续的状态空间。

这意味着,与之前简化的火星车例子(它只能处于六个可能位置之一)不同,大多数机器人可以处于无数个连续值位置中的任何一个。

例如,假设火星车可以在一条线上任意移动,其位置由一个介于0到6公里之间的数字表示,那么任何中间数字都是有效的。这就是一个连续状态空间的例子,因为位置可以由诸如2.7公里或4.8公里这样的数字表示。


汽车控制示例 🚗

让我们看另一个例子。我将使用控制汽车或卡车的应用作为示例。

如果你正在构建一辆自动驾驶汽车或卡车,并希望控制其平稳行驶,那么这辆卡车的状态可能包括几个数字。

假设卡车保持在平面上行驶,你或许不需要担心它的高度。因此,状态可能包括其x位置、y位置、方向角θ,以及它在x方向的速度、在y方向的速度和转弯的角速度。

以下是卡车状态向量的构成:

状态 s = [x, y, θ, ẋ, ẏ, θ̇]

其中:

  • x, y 是位置坐标。
  • θ 是方向角(例如,0到360度之间)。
  • ẋ, ẏ 分别是x和y方向的速度。
  • θ̇ 是角速度(即方向角变化的快慢)。

对于之前六状态的火星车例子,状态只是六个可能数字中的一个。而对于汽车,状态由这个六维向量构成,其中每个数字都可以在其有效范围内取任意值。


直升机控制示例 🚁

接下来,我们看一个更复杂的例子。如果你正在构建一个强化学习算法来控制一架自主直升机,你该如何描述直升机的位置呢?

直升机的位置包括其x位置(例如,南北方向)、y位置(东西方向)以及z位置(离地高度)。

但除了位置,直升机还有朝向。传统上,可以用三个额外的数字来捕捉这个朝向:横滚角(向左或向右倾斜)、俯仰角(向前或向后倾斜)和偏航角(指南针方向,如面向北或东)。

此外,要控制直升机,我们还需要知道它在x、y、z方向上的速度,以及它的角速度(即横滚、俯仰、偏航变化的速率)。

因此,用于控制自主直升机的状态实际上是以下12个数字的列表:

状态 s = [x, y, z, φ, θ, ψ, ẋ, ẏ, ż, φ̇, θ̇, ψ̇]

其中:

  • x, y, z 是位置坐标。
  • φ, θ, ψ 分别是横滚角、俯仰角、偏航角(常用希腊字母表示)。
  • ẋ, ẏ, ż 是线速度。
  • φ̇, θ̇, ψ̇ 是角速度。

策略的工作就是观察这12个数字,并决定对直升机采取什么适当的行动。


连续状态MDP的核心概念 📊

在连续状态强化学习问题,或称连续状态马尔可夫决策过程中,问题的状态不再只是少数几个可能的离散值(如1到6的数字),而是一个数字向量,其中每个分量都可以取大量可能值中的任何一个。

在本周的实践实验室中,你将亲自实现一个强化学习算法,并将其应用于模拟的登月器应用(在模拟中让某个物体在月球着陆)。


本节课中,我们一起学习了连续状态空间的概念。我们了解到,与离散状态不同,在机器人等实际应用中,状态通常由多个连续变量构成的向量来描述,例如汽车的位置、速度和朝向,或直升机更复杂的空间姿态与运动状态。这为策略函数提供了更丰富但也更复杂的输入信息。在接下来的视频中,我们将具体看看登月器这个连续状态应用包含了哪些内容。

144:月球着陆器环境介绍 🚀

在本节课中,我们将学习一个经典的强化学习应用环境——月球着陆器。我们将了解这个模拟任务的目标、状态空间、动作空间以及奖励函数的设计。

月球着陆器让你操控一个模拟飞行器在月球上着陆。它类似于一个基础视频游戏,并被许多强化学习研究者广泛使用。接下来,我们具体看看这个应用。

任务目标 🎯

在这个应用中,你操控一艘正在快速接近月球表面的着陆器。你的任务是在适当的时机点燃推进器,使其安全降落在指定着陆台上。

为了让你直观感受,下图展示了月球着陆器成功着陆的过程。它通过向下、向左或向右喷射推进器来调整姿态,最终降落在两个黄色旗帜之间。




如果强化学习算法策略表现不佳,着陆器可能会坠毁在月球表面,如下图所示。

动作空间 🕹️

上一节我们介绍了任务目标,本节中我们来看看智能体可以执行哪些动作。

在每个时间步,你有四种可能的动作选择:

  • 无操作:不执行任何动作,惯性和重力会将你拉向月球表面。
  • 点燃左侧推进器:你会看到左侧出现一个小红点,这个动作倾向于将着陆器推向右侧。
  • 点燃主引擎:点燃底部的向下主引擎。
  • 点燃右侧推进器:这个动作会点燃右侧推进器,将你推向左侧。

为了简化描述,我们有时会将动作称为:

  • nothing:无操作
  • left:点燃左侧推进器
  • main:点燃主引擎
  • right:点燃右侧推进器

你的任务是持续选择动作,以使着陆器安全降落在着陆台的两个旗帜之间。

状态空间 📊

了解了动作之后,我们来看看环境如何描述着陆器的状态。

状态 S 是一个包含8个变量的向量:

  1. 位置 x:水平方向(左/右)的位置。
  2. 位置 y:垂直方向(高度)的位置。
  3. 速度 x_dot:水平方向的速度。
  4. 速度 y_dot:垂直方向的速度。
  5. 角度 theta:着陆器向左或向右倾斜的角度。
  6. 角速度 theta_dot:角度变化的速度。
  7. 左腿触地 L:一个二进制值(0或1),表示左支撑腿是否接触地面。
  8. 右腿触地 R:一个二进制值(0或1),表示右支撑腿是否接触地面。

其中,x, y, x_dot, y_dot, theta, theta_dot 是连续数值,而 LR 是二元变量。引入 LR 是因为着陆器细微的位置差异对其是否成功着陆影响很大。

奖励函数 ⚖️

定义了状态和动作,我们需要一个奖励函数来引导智能体学习正确的行为。

月球着陆器的奖励函数设计如下:

  • 成功着陆奖励:如果成功降落在着陆台上,根据其飞行和抵达着陆台中心的精确程度,获得 +100 到 +140 的奖励。
  • 移动奖励:根据其靠近或远离着陆台的程度给予额外奖励。靠近获得正奖励,远离获得负奖励
  • 坠毁惩罚:如果坠毁,获得 -100 的大额惩罚。
  • 软着陆奖励:如果实现软着陆(非坠毁),获得 +100 奖励。
  • 腿部触地奖励:每有一条支撑腿(左腿或右腿)触地,获得 +10 奖励。
  • 燃料消耗惩罚:为了鼓励节约燃料,避免不必要的推进器点火:
    • 每次点燃主引擎,获得 -0.3 奖励。
    • 每次点燃左侧或右侧推进器,获得 -0.03 奖励。

这是一个中等复杂度的奖励函数。月球着陆器应用的设计者经过深思熟虑,将期望的行为(如安全着陆、节约燃料)和不期望的行为(如坠毁)编码在奖励函数中。

当你构建自己的强化学习应用时,通常也需要仔细思考并明确指定你期望和不期望的行为,并将其编码到奖励函数中。不过,设计奖励函数通常比为每一个可能的状态指定精确的正确动作要容易得多,后者在许多强化学习应用中要困难得多。

问题定义与目标 🎯

综合以上信息,月球着陆器问题的正式定义如下:

我们的目标是学习一个策略 π,当给定一个状态 S 时,该策略能选择一个动作 a,即:
a = π(S)

其目标是最大化回报,即折扣奖励的总和:
G = R_1 + γR_2 + γ^2R_3 + ...

通常对于月球着陆器,我们使用一个相当大的折扣因子 γ,其值设为 0.985,非常接近 1。

如果你能学习到一个实现此目标的策略 π,那么你就成功地让这艘月球着陆器安全着陆了。

这是一个令人兴奋的应用!现在,我们终于准备好开发学习算法了。该算法将利用神经网络和深度学习来得出一个能让月球着陆器着陆的策略。让我们进入下一个视频,开始学习深度强化学习。

总结 📝

本节课中,我们一起学习了月球着陆器强化学习环境。我们明确了任务目标是安全降落在指定区域,定义了四个基本动作,了解了由位置、速度、角度和腿部触地状态构成的八维状态空间,并分析了精心设计的奖励函数如何引导智能体学习安全、高效着陆的行为。最后,我们将问题形式化为学习一个最大化折扣回报的策略 π,为后续引入深度强化学习算法解决此问题奠定了基础。

145:学习状态价值函数 🧠

在本节课中,我们将学习如何使用强化学习来控制“月球着陆器”或其他强化学习问题。核心思想是训练一个神经网络来计算或近似状态-动作价值函数 Q(S, A),从而帮助我们选择最佳动作。

神经网络近似 Q 函数

上一节我们介绍了强化学习的目标。本节中我们来看看如何具体实现。关键在于训练一个神经网络,其输入是当前状态 S 和当前动作 A,输出是 Q(S, A) 的近似值。

具体到月球着陆器任务,状态 S 是之前提到的8个数字的列表(如位置、速度、角度等)。动作 A 有四种可能:无操作、左推进器、主引擎、右推进器。我们可以使用独热编码(one-hot)来表示这四种动作。

因此,神经网络的输入 X 是一个包含12个数字的向量(8个状态值 + 4个动作编码)。这个向量将被送入一个神经网络,例如,第一隐藏层有64个单元,第二隐藏层有64个单元,输出层有一个单元。

神经网络的输出就是 Q(S, A) 的近似值。我们也可以将这个目标值称为 Y。请注意,强化学习与监督学习不同:这里我们输入的是状态-动作对 (S, A),目标是输出一个标量值 Q(S, A),而不是直接输出一个动作。

一旦我们训练好这个神经网络,当着陆器处于某个状态 S 时,我们就可以用网络分别计算四个动作对应的 Q(S, A) 值,然后选择值最高的那个动作来执行。

如何训练神经网络:贝尔曼方程与训练集构建

那么,如何训练神经网络来输出准确的 Q(S, A) 呢?方法是利用贝尔曼方程来创建包含大量样本 (X, Y) 的训练集,然后像在监督学习中训练神经网络一样,学习从 XY 的映射。

以下是构建训练集的具体步骤:

首先,我们回顾贝尔曼方程:
Q(S, A) = R(S) + γ * maxA' Q(S', A')

方程的右边就是我们希望 Q(S, A) 等于的值,我们将其设为目标值 Y。神经网络的输入 X 就是状态-动作对 (S, A)

为了获得训练数据,我们让智能体(如月球着陆器)在环境中尝试各种动作(可以是随机的)。每次尝试都会产生一个四元组经验:(S, A, R(S), S'),即“在状态 S 下采取动作 A,获得了即时奖励 R(S),并转移到了新状态 S'”。

收集到大量这样的经验元组后,我们可以用每一个来构建一个训练样本 (X, Y)

  • X = (S, A) (将状态和动作编码合并)
  • Y = R(S) + γ * maxA' Q(S', A')

你可能会问,计算 Y 时需要的 Q(S', A') 从哪里来?最初,我们并不知道真正的 Q 函数。我们可以从一个随机初始化的神经网络(即我们对 Q 函数的随机猜测)开始计算这个值。随着算法迭代,这个猜测会越来越准。

完整的 DQN 算法流程 🚀

上一节我们介绍了如何构建单个训练样本。本节中我们来看看如何将这些步骤整合成一个完整的算法——深度Q网络(Deep Q-Network, DQN)算法。

以下是算法的完整步骤:

  1. 初始化:随机初始化神经网络 Q 的所有参数。这代表我们对 Q 函数的初始随机猜测。
  2. 收集经验:让智能体在环境中(如月球着陆器模拟器)执行动作,可以是随机动作。将每次交互得到的经验元组 (S, A, R, S') 存储起来。通常我们只保留最近一定数量(例如10,000个)的经验,这个存储区称为“经验回放缓冲区”。
  3. 创建训练集:从经验回放缓冲区中取出一批经验(例如10,000个)。对于每个经验 (S, A, R, S'),使用当前的 Q 网络来计算目标值 Y
    Y = R + γ * maxA' Q(S', A')
    这样就得到了一个训练集,其中 X = (S, A)Y 是计算出的目标值。
  4. 训练神经网络:使用这个训练集,以监督学习的方式(如最小化均方误差)训练一个新的神经网络 Q_new,使其学会从 X 预测 Y
  5. 更新网络:将旧的 Q 网络参数更新为刚训练好的 Q_new 网络的参数,即令 Q = Q_new
  6. 重复:不断重复步骤2到5。

这个算法的核心思想是:通过贝尔曼方程,利用当前(可能不完美)的 Q 估计来生成更准确的目标值 Y,然后训练网络去拟合这些目标值。每次迭代,Q 网络的估计都会变得更准确一点,从而在下一次迭代中生成更好的目标值 Y。如此循环,Q 函数最终会收敛到一个较好的近似。

本节课中我们一起学习了 DQN 算法的基本原理。我们了解到,通过结合神经网络和贝尔曼方程,并利用经验回放机制,可以逐步学习出复杂环境中的状态-动作价值函数,从而为智能体找到优秀的决策策略。在接下来的课程中,我们将看到一些对此基础算法的改进,使其性能更加出色。

146:算法改进:优化的神经网络架构 🚀

在本节课中,我们将学习如何改进深度Q网络(DQN)的神经网络架构,使其计算效率更高。我们将探讨一种能够同时输出所有可能动作Q值的网络结构,并简要介绍即将学习的Epsilon贪婪策略。

从上一节的架构说起

上一节我们介绍了一种神经网络架构,它输入状态和动作,试图输出对应的Q函数值Q(s, a)。这种架构需要为每个状态单独进行多次推理来计算不同动作的Q值,效率较低。

更高效的架构设计

本节中我们来看看一种更高效的神经网络架构。事实证明,对网络结构进行一项改动可以大幅提升算法效率。因此,大多数DQN的实现实际上都采用了我们将在本视频中看到的这种更高效架构。

以下是这种架构的核心思路:与其训练一个网络来输出单个Q(s, a)值,不如训练一个神经网络来同时输出给定状态s下所有可能动作的Q值。

这是我们之前看到的神经网络架构。它输入12个数字(状态和动作的编码),输出Q(s, a)。每当我们处于某个状态s时,我们必须分别进行四次神经网络推理来计算这四个值,以便选择能给我们最大Q值的动作a。这效率很低,因为我们必须从每个单一状态进行四次推理。

相反,训练一个单一的神经网络来同时输出所有这四个值被证明是更高效的。

修改后的神经网络架构如下所示。

  • 输入是8个数字,对应于登月器的状态。
  • 然后数据经过神经网络,第一隐藏层有64个单元。
  • 第二隐藏层有64个单元。
  • 现在输出层有四个输出单元。神经网络的任务是让这四个输出单元分别输出:Q(s, “无操作”)、Q(s, “左”)、Q(s, “主引擎”)、Q(s, “右”)。

因此,神经网络的任务是同时计算当我们处于状态s时所有四个可能动作的Q值。

这被证明是更高效的,因为给定一个状态s,我们只需运行一次推理就能获得所有这四个值,然后可以非常快速地选择使Q(s, a)最大化的动作a。

您还会注意到,在贝尔曼方程中,有一个步骤我们必须计算 max_{a'} Q(s', a')。这个值乘以折扣因子γ,然后加上奖励R(s)。这个神经网络也使得计算这个最大值更加高效,因为我们同时获得了状态s'下所有动作a'的Q(s', a')值。这样,您就可以直接选取最大值来计算贝尔曼方程右侧的这个值。

对神经网络架构的这一改动使强化学习算法的效率大大提高,因此我们将在实践实验中使用这种架构。

过渡到策略改进

接下来,还有一个想法将对算法有很大帮助,它被称为Epsilon贪婪策略。这会影响您在学习过程中如何选择动作。让我们在下一个视频中看看这意味着什么。

本节总结

本节课中我们一起学习了如何优化DQN的神经网络架构。关键改进是将网络输出从单一动作的Q值改为一个状态s下所有可能动作的Q值向量。这种设计通过单次前向传播即可获得所有Q值,极大地提升了计算效率,特别是在执行动作选择和计算贝尔曼方程中的最大值时。在接下来的课程中,我们将学习Epsilon贪婪策略,以进一步改进智能体在探索与利用之间的平衡。

147:算法改进:贪婪策略 🚀

在本节课中,我们将学习强化学习算法中的一个重要改进策略——ε-贪婪策略。我们将探讨如何在算法学习过程中选择动作,以平衡“利用”已知知识和“探索”未知可能性,从而更有效地学习。


算法中的动作选择问题

上一节我们介绍了强化学习算法的基本框架。在本节中,我们来看看算法运行中的一个关键步骤:如何在环境中采取行动

在算法运行过程中,即使我们仍在学习如何近似状态-动作价值函数 Q(S, A),我们也需要在“月球着陆器”环境中采取行动。那么,在学习阶段,我们应如何选择这些动作呢?

最常见的方法是使用一种称为 ε-贪婪策略 的方法。让我们看看它是如何工作的。

动作选择方案对比

以下是两种不同的动作选择方案:

方案一:总是选择最优动作

一种自然的想法是,在状态 S 下,总是选择能最大化当前估计值 Q(S, A) 的动作 A。即使 Q(S, A) 的估计并不完美,我们也尽力而为,使用当前的最佳猜测。

# 伪代码示例:总是选择最优动作
action = argmax_a(Q_network.predict(state))

这种方法可能有效,但并非最佳选择。

方案二:ε-贪婪策略

更常用的方法是 ε-贪婪策略。其核心思想是:

  • 大多数时候(例如,95%的概率),我们选择能最大化 Q(S, A) 的动作(贪婪动作)。
  • 一小部分时间(例如,5%的概率),我们随机选择一个动作。
# 伪代码示例:ε-贪婪策略
import random

epsilon = 0.05  # 探索概率
if random.random() < epsilon:
    action = random.choice(all_possible_actions)  # 探索:随机选择
else:
    action = argmax_a(Q_network.predict(state))   # 利用:选择最优动作

为什么需要随机探索?

随机探索至关重要。假设由于随机初始化,神经网络错误地认为“启动主引擎”从来不是一个好主意(即 Q(S, main) 的初始值很低)。如果算法总是选择当前最优动作(方案一),它就永远不会尝试启动主引擎,因此也永远无法学习到在某些情况下启动主引擎实际上是个好主意。

通过方案二,我们在每一步都有一个小概率尝试不同的动作。这使得神经网络能够克服其自身可能存在的、关于“什么动作不好”的错误先入之见,并发现新的可能性。

探索与利用的权衡

在强化学习文献中,这被称为 探索与利用的权衡

  • 探索:尝试可能不是当前最佳的动作,以获取更多关于环境的信息。
  • 利用:利用当前学到的知识,选择估计能带来最大回报的动作(即贪婪动作)。

ε-贪婪策略就是这个权衡的一种具体实现方式。历史上,选择贪婪动作有时也被称为 利用步骤

关于名称与参数调整

需要指出的是,“ε-贪婪策略”这个名称可能有些令人困惑,因为算法在 1-ε(例如95%)的时间里是贪婪的,只在 ε(例如5%)的时间里进行探索。一个更准确的名称或许是“1-ε贪婪策略”,但历史原因使得“ε-贪婪策略”这个叫法被沿用下来。

此外,强化学习中一个常用的技巧是让 ε 的值随时间衰减

  • 开始时:设置较高的 ε(例如 1.0),进行大量随机探索,快速收集数据。
  • 逐渐地:随着学习进行,逐步降低 ε(例如降至 0.01),更多地依赖不断改进的 Q 函数估计来选择好的动作。

强化学习的调参特点

与监督学习相比,强化学习算法通常对超参数的选择更为敏感。例如,在监督学习中,学习率设置得稍小,可能只是让训练时间变长几倍。而在强化学习中,如果 ε 或其他参数设置不当,学习时间可能会延长十倍甚至百倍。这使得调参过程有时更具挑战性。

不过,在配套的实践练习中,我们会提供一组经过验证的良好参数,帮助你成功实现月球着陆器的着陆。


总结

本节课我们一起学习了强化学习中的 ε-贪婪策略。我们了解到,为了有效学习,算法需要在利用当前最佳知识和探索未知可能性之间取得平衡。ε-贪婪策略通过以大概率选择最优动作、小概率随机选择动作的方式,巧妙地实现了这一平衡。我们还讨论了让探索概率 ε 随时间衰减的技巧,以及强化学习算法调参的一些特点。

在下一个可选视频中,我们将介绍另外两个算法改进:小批量采样软更新。即使没有这些改进,当前的算法也能工作,但这些改进可以显著提升算法的运行速度和稳定性。

148:算法改进:小批量与软更新 🚀

在本节课中,我们将学习强化学习算法的两项重要改进:小批量学习软更新。这两项技术能分别提升算法的训练速度和收敛稳定性。


小批量学习 📊

上一节我们介绍了基础的强化学习算法。本节中我们来看看如何通过小批量学习来加速训练过程。这个思想同样适用于监督学习,能显著提升如神经网络、线性回归等模型的训练效率。

在监督学习中,例如预测房价的线性回归模型,我们定义了成本函数 J(W, B)

J(W, B) = (1 / 2M) * Σ (ŷ - y)²

梯度下降算法通过以下公式更新参数:

W := W - α * (∂J(W, B) / ∂W)
B := B - α * (∂J(W, B) / ∂B)

当训练集规模 M 非常庞大时(例如1亿个样本),每次梯度下降迭代都需要计算整个数据集的平均梯度,这将非常缓慢。

小批量梯度下降的核心思想是:不每次使用全部数据,而是每次迭代只使用一个较小的子集(小批量)

以下是其工作原理:

  • 选择批量大小:设定一个较小的数量 M'(例如1000),代替总样本数 M
  • 修改成本函数:每次迭代的成本函数变为基于 M' 个样本计算:J(W, B) ≈ (1 / 2M') * Σ (ŷ - y)²
  • 迭代过程:每次迭代随机选取不同的 M' 个样本进行梯度计算和参数更新。

与标准的批量梯度下降平稳地走向成本函数最小值不同,小批量梯度下降的路径是有噪声但方向大致正确的。虽然每一步可能不够精确,但每一步的计算成本大大降低,使得整体训练速度更快。

回到我们的强化学习算法,我们之前会将回放缓冲区中存储的大量 (S, A, R, S') 元组全部用于训练。应用小批量思想后,我们可以每次只从缓冲区中随机抽取一个子集(例如1000个)来训练神经网络。这会使每次训练更新略有噪声,但能显著加快整体强化学习算法的运行速度


软更新 🔄

我们刚刚了解了如何用小批量加速训练。接下来,我们看看如何用“软更新”技术使算法收敛更稳定。

在之前的算法中,我们训练出一个新的Q网络 Q_new 后,会直接执行 Q = Q_new。这种硬替换可能带来问题:如果某次训练得到的 Q_new 质量不佳,甚至比旧的Q网络更差,那么这次更新就会直接损害当前对Q函数的估计。

软更新方法旨在更平缓地更新Q网络的参数,避免因单次不良更新导致性能骤降。

假设Q网络的参数为 WB。训练后得到新参数 W_newB_new

  • 原始算法(硬更新)
    W = W_new
    B = B_new

  • 软更新算法
    W = τ * W_new + (1 - τ) * W
    B = τ * B_new + (1 - τ) * B

其中,τ 是一个很小的超参数(例如0.01)。这意味着每次更新,新参数值只以很小的比例(1%)融入现有参数,而现有参数保留了绝大部分(99%)的权重。

以下是软更新的关键点:

  • 控制更新幅度τ 控制了你向新值移动的激进程度。τ=1 时退化为硬更新。
  • 提升稳定性:这种渐进式的更新方式使得强化学习算法收敛更可靠,减少了振荡或发散的可能性。

总结 🎯

本节课中我们一起学习了强化学习算法的两项改进:

  1. 小批量学习:通过每次迭代只使用数据的一个子集进行训练,大幅提升了算法速度。这项技术对监督学习和强化学习都同样有效。
  2. 软更新:通过渐进式地混合新旧网络参数(W = τ * W_new + (1 - τ) * W),取代直接的硬替换,使得算法的收敛过程更加稳定可靠

结合这两项改进,你的强化学习算法将能更高效、更稳定地在“月球着陆器”等复杂挑战中学习,并成功完成任务。

149:强化学习现状 🚀

在本节课中,我们将探讨强化学习这一激动人心的技术领域,并对其当前的实际应用现状、面临的挑战以及未来潜力进行客观分析。


强化学习概述与个人背景

强化学习是一系列令人兴奋的技术。

事实上,在我攻读博士学位期间,强化学习曾是我论文的研究主题。因此,我至今仍对这些想法感到兴奋。

尽管强化学习背后有强大的研究动力和热情,但我认为围绕它存在一些,有时甚至是大量的炒作。

因此,我希望做的是与大家分享一个实用的视角,了解强化学习目前在应用层面的效用究竟如何。


炒作的原因与模拟环境的局限性

围绕强化学习产生炒作的原因之一是,许多研究出版物都基于模拟环境。基于我在模拟环境和真实机器人两方面的工作经验,我可以告诉你,让强化学习算法在模拟环境或视频游戏中运行,远比在真实机器人上运行要容易得多。

许多开发者评论说,即使他们在模拟环境中取得了成功,要在现实世界或真实机器人上实现功能,其挑战性也令人惊讶。

因此,如果你将这些算法应用于实际应用,我希望你注意这一局限性,以确保你的成果能在实际应用中有效工作。


实际应用现状与对比

其次,尽管媒体对强化学习进行了大量报道,但如今强化学习的实际应用数量远少于监督学习和无监督学习。

如果你正在构建一个实际应用,你会发现监督学习和无监督学习有用或适合这项工作的概率,远高于你最终使用强化学习的概率。

我自己曾多次使用强化学习,特别是在机器人控制应用中。但在我日常的应用工作中,我最终使用监督学习和无监督学习的频率要高得多。


研究潜力与未来展望

目前,强化学习领域有许多激动人心的研究。我认为强化学习在未来应用中的潜力非常巨大。

强化学习仍然是机器学习的主要支柱之一。因此,将其作为一个框架,在你开发自己的机器学习算法时,我希望它能帮助你更有效地构建可运行的机器学习系统。


课程总结与寄语

我希望你喜欢本周关于强化学习的材料。特别是,我希望你能享受亲自让“月球着陆器”成功着陆的过程。

当你实现一个算法,然后看到因为你编写的代码,月球着陆器安全降落在月球上时,我希望这将是一次令人满意的体验。

这标志着我们这门专项课程的结束。让我们进入最后一个视频进行总结。


本节课总结: 我们一起学习了强化学习的现状。我们认识到,尽管强化学习在模拟环境中取得了显著成功,并拥有巨大的研究潜力,但其在真实世界中的应用仍面临挑战,且目前实际应用规模小于监督学习和无监督学习。理解这些现实情况,有助于我们更务实、更有效地在合适的场景中运用这项技术。

150:机器学习专项课程总结与致谢 🎓

在本节课中,我们将一起回顾吴恩达《机器学习》专项课程的核心内容,并对整个学习旅程进行总结。


课程内容回顾 📚

上一节我们完成了所有技术内容的学习,本节中我们来看看整个专项课程的脉络与核心收获。

我们共同度过了许多视频课程,这是最后一节。让我们一起总结所学过的主要课题。

第一门课程:监督学习

第一门课程涵盖了监督机器学习,包括回归和分类。你学习了线性回归、逻辑回归、成本函数以及梯度下降算法。

以下是监督学习的核心算法:

  • 线性回归:用于预测连续值。其假设函数通常表示为 hθ(x) = θ₀ + θ₁x₁ + ... + θₙxₙ
  • 逻辑回归:用于分类问题。其假设函数使用Sigmoid函数:hθ(x) = g(θᵀx),其中 g(z) = 1 / (1 + e⁻ᶻ)
  • 梯度下降:用于优化模型参数的通用算法。其更新规则为:θⱼ := θⱼ - α * ∂J(θ)/∂θⱼ

第二门课程:高级算法与实践建议

第二门课程我们探讨了更高级的学习算法,包括神经网络、决策树和树集成方法。同时,我们也学习了机器学习的实践建议,例如偏差与方差、如何划分训练集、验证集和测试集,以及如何高效地改进学习算法。

以下是本部分的核心概念:

  • 神经网络:通过多层神经元结构模拟复杂函数。
  • 决策树与集成方法:如随机森林,通过构建多棵树并汇总结果来提高预测性能。
  • 偏差与方差诊断:这是分析和改进模型性能的关键框架。

第三门课程:无监督学习与强化学习

第三门课程是关于无监督学习、推荐系统和强化学习。我们讨论了聚类算法、异常检测算法、协同过滤和基于内容的过滤。在最后一周,我们学习了强化学习。

掌握了这一系列工具后,你现在已经具备了构建广泛机器学习应用的能力。


祝贺与展望 🚀

恭喜你坚持到了最后一个视频。如果你完整地学完了这个专项课程,那么你现在已经拥有了非常扎实的机器学习基础。我认为你已经为成为一名机器学习专家奠定了良好的开端。

如你所知,机器学习正在对社会产生巨大影响。它是一个强大的工具,每天有数十亿人通过网页搜索、产品推荐、语音识别等众多应用在使用它。它甚至通过助力科学发现来增进人类知识,创造着数十亿美元的价值,并催生了几年前还无法想象的新应用。

但我认为,机器学习的最佳应用尚未被发明出来。而这把我们带到了你面前。你现在完全有能力运用机器学习的工具自己构建应用,并成就伟大的事业。我希望你能运用这些技能让他人的生活变得更美好。


最后的致谢 🙏

在结束这门课程之前,我想对你说最后一件事。教授这门课程对我而言是一件乐事,但就在不久之前,我自己也是一名学生。因此,我深知学习这些东西是多么耗时。我知道你生活忙碌,有许多其他事务需要处理。所以,感谢你抽出时间观看视频、完成测验和实验。我知道你在这门课程中投入了大量的时间和精力。

因此,我只想对你表示衷心的感谢。感谢你成为这门课程的学生。我非常感激你,并感谢你花时间与我、与这个专项课程共度的所有时光。谢谢你。


总结

本节课中,我们一起回顾了整个机器学习专项课程的知识体系,从监督学习到无监督学习与强化学习。你已建立起坚实的理论基础,并掌握了构建实际应用的强大工具集。学习之旅告一段落,但运用知识创造价值的征程才刚刚开始。再次祝贺你完成课程!

151:人工智能与机器人技术 🚀🤖

在本节课中,我们将跟随吴恩达教授与斯坦福大学教授切尔西·芬恩的对话,探讨机器学习,特别是强化学习在机器人技术领域的应用、挑战与未来展望。我们将了解机器人技术的现状、如何让机器人更通用、以及研究人员在实验室中的日常工作。


概述

切尔西·芬恩教授的研究专注于将机器学习,尤其是强化学习,应用于机器人技术。本次对话探讨了机器人能力的现状、实现通用性的挑战、数据收集的重要性、模拟与现实世界的差距,以及为进入该领域的学习者提供的建议。


机器人技术的现状:能力与局限 🤔

上一节我们介绍了课程主题,本节中我们来看看机器人当前真正能做什么和不能做什么。

在流行媒体中,我们常看到机器人完成各种复杂动作的视频。这些演示虽然令人印象深刻,但机器人通常是为特定环境精心设置的。如果环境或任务发生改变,机器人可能无法正常工作。

当前机器人技术的核心挑战在于泛化能力,即让机器人能够处理多种不同的场景、物体和环境。目前,机器人能在工厂等受控环境中可靠工作,但要获得人类般的灵活性和通用技能仍然非常困难。

当看到一个酷炫的机器人演示时,我们应该思考以下问题:

  • 如果环境中的某些东西改变了会怎样?
  • 如果机器人的起始位置稍有不同会怎样?
  • 系统对这些变化的鲁棒性或韧性如何?

那些即使被“戳一下”或环境稍有变化仍能工作的演示,才真正令人印象深刻。


实现机器人泛化的路径:数据是关键 📊

上一节我们讨论了机器人泛化能力的挑战,本节中我们来看看如何通过数据来解决这个问题。

在其他机器学习领域,使用大型多样化数据集(如整个维基百科)训练模型取得了巨大成功。切尔西教授的研究方向之一,就是尝试在机器人领域复制这种成功,即收集多样化的数据并让机器人从中学习。

这面临几个挑战:

  1. 缺乏现成数据集:不存在像“维基百科”那样的机器人运动控制数据集。
  2. 需要自主收集数据:机器人原则上可以自己收集数据,这既是机遇也是挑战。

为了解决这个问题,研究探索了多种方法:

  • 让机器人在多种不同环境中自主收集数据。
  • 向机器人演示如何完成任务。
  • 利用互联网数据(如人类视频)来教导机器人。
  • 整合所有这些不同数据源。

目标是让机器人在见过如此多样化的数据后,能够泛化到实验室环境之外的真实世界。


模拟与现实世界的挑战 🎮➡️🌍

上一节我们探讨了利用真实数据的重要性,本节中我们来看看模拟器作为数据源的作用与局限。

模拟和电子游戏一样,可以生成大量数据用于训练AI系统。然而,模拟器中的物理规律与现实世界并不完全相同。例如,桌面摩擦或瓶盖螺纹的摩擦力很难精确建模。

模拟器面临的挑战包括:

  • 物理建模不精确:模拟引擎的物理参数存在误差,导致在模拟中学到的策略无法直接迁移到现实世界。
  • 创建内容成本高:真实世界极其多样,在模拟中创建日常生活中遇到的所有物体和环境需要大量手动工作,难以规模化。

因此,模拟是一个有前景的数据补充来源,但由于这些不准确性和迁移挑战,不应是唯一依赖的方法。我们应努力利用大量真实数据。


进军非受控环境:家庭与厨房 🏠

上一节我们提到了受控环境,本节中我们来看看将机器人应用到家庭等非受控环境的探索。

工厂等受控环境是机器人已取得成功的地方。从长远来看,切尔西教授希望看到机器人能进入家庭或办公室等任何环境并发挥作用。

为了实现这一目标,需要开始在真实环境中收集数据。通常,机器人研究只在实验室环境中收集一次数据。如果只收集实验室数据,机器人永远无法进入真实世界。因此,研究正在尝试启动数据收集工作,将机器人真正放入现实世界,让它们看到不同家庭中的多样性,而不仅仅是实验室中的狭窄数据。

关于机器人需要见过多少个不同的厨房才能在一个新厨房里成功完成“制作一碗麦片”这样的复杂任务,切尔西教授估计可能需要成千上万个厨房的数据。即使有了大量数据,问题可能仍未完全解决,就像自动驾驶领域一样。因此,除了依赖数据,还需要让机器人具备在新环境中即时适应的能力。


元学习:让机器人学会学习 🧠

上一节我们讨论了机器人需要适应新环境,本节中我们来看看“元学习”如何帮助机器人做到这一点。

元学习,即“学会学习”,其动机在于:当机器人进入一个新厨房时,我们不希望它只是运行之前学到的知识,它可能需要通过少量试错经验来即时适应和学习。例如,打开一个略有不同的 pantry 门。

如果仅用少量数据从头开始进行机器学习,效果不会好。元学习试图利用先前在多个厨房或任务中获得的经验,来优化学习新事物的能力。其核心思想是,基于学习过许多旧事物的经验,来提升学习新事物的效率。

元学习的一种方法是双层优化,将一个学习问题嵌套在另一个学习问题中,优化内部学习问题的所有参数,以实现更快或更高效的学习。


机器人实验室的日常与调试 🛠️

上一节我们介绍了一些算法思想,本节中我们来看看在机器人实验室中进行研究的实际体验。

在实验室工作与运行机器学习实验有相似之处,都需要编写代码、训练模型和评估模型。但除此之外,还需要与真实的机器人交互。

工作流程可能包括:

  • 从模拟开始,使用物理引擎迭代算法。
  • 设置控制栈,以便向机器人发送移动指令。
  • 设置摄像头。
  • 通过VR控制器等方式引导机器人收集数据。
  • 在真实机器人上运行模型进行评估。

看到机器人真正做出动作,而不仅仅是看到模型准确度数字,是非常有成就感的。但这过程也需要耐心,因为机器人可能会出故障,可能出现比纯软件系统更多的错误。

调试机器人系统涉及排除软件和硬件两方面的错误源。一些方法包括:

  • 检查机器人收集的数据和看到的图像。
  • 可视化模型学习到的特征。
  • 在多个机器人上重放动作以确保可复现性。

与纯软件不同,硬件错误可能导致机器人损坏,需要时间修复或更换部件,这是机器人学独特的挑战。


强化学习在机器人中的应用与实践技巧 ⚙️

上一节我们了解了实验室的日常工作,本节中我们深入探讨强化学习这一核心工具在机器人中的应用。

强化学习对机器人学非常有吸引力,因为它原则上允许机器人通过试错、从自身收集的数据中自主学习。然而,将其应用于现实世界也面临挑战:

  1. 奖励函数定义:真实世界不会给机器人“分数”。机器人可能需要同时学习任务的含义(奖励函数)和学习如何在物理世界中完成任务。解决方法包括提供成功示例、完整演示,或使用逆强化学习等技术来推断行为背后的奖励函数。
  2. 自主重置:在试错过程中,尝试一次任务后需要回到起点才能再次尝试。学习这种重置行为本身也具有挑战性,涉及学习如何执行任务与学习如何从失败中恢复之间的复杂交互。

切尔西教授是较早探索完全端到端机器人强化学习的研究者之一,即训练单个神经网络直接从机器人摄像头拍摄的图像映射到机器人关节的扭矩。早期这项工作面临质疑,但如今使用神经网络表示策略(根据图像选择动作)已成为机器人学中更主流的范式。

她预测,随着机器学习不断进步,这种范式在机器人领域将继续增长,特别是在处理世界物体和环境的巨大多样性方面。


未来展望:标准化与可共享数据集 🤝

上一节我们回顾了端到端学习的发展,本节中我们展望机器人技术的未来方向。

切尔西教授对未来感到兴奋的一个方向是转向使用更广泛、可重复使用的数据集。目前机器人学习领域仍多为每个项目在实验室收集小规模数据。如果计算机视觉研究人员必须为每个项目收集 ImageNet 数据,进展将非常缓慢。

她的预测是,未来几年将转向一种新范式:大量重复使用数据集,使用更大型的数据集,并理想化地在不同机构和机器人平台之间共享数据集,从而扩大系统训练数据的规模,使其能够更广泛地泛化。

实现这一愿景的挑战在于硬件标准化。一个可行的起点是:

  1. 社区达成共识,在特定机器人平台和大致设置上标准化。
  2. 在该标准化平台上收集大量多样化数据。
  3. 取得初步成功后,逐步放宽标准化程度,纳入更多不同的机器人、夹具和环境。

给初学者的建议与学习资源 📚

在最后一节,我们为希望进入强化学习或机器人领域的学习者提供一些实用建议。

切尔西教授的主要建议是动手实践,尝试构建一些东西。通过设定目标并尝试实现来学习,是最有效的方式之一。

如何开始:

  • 从模拟机器人入手:学习曲线较低,无需购买硬件。可以探索 MuJoCo 等免费物理引擎。
  • 接触真实硬件:虽然更具挑战性,但成功时成就感更大。现在有许多价格相对便宜的机器人套件,甚至可以用乐高搭建机器人入门。

推荐学习资源:

  • 强化学习:在线课程、视频、Sutton & Barto 的教科书。
  • 机器人操作系统:学习 ROS 有助于与机器人交互。
  • 代码库与教程:利用网上的各种教程和代码库。

这是一个令人兴奋的领域,进入的途径每年都在增长,也变得更容易。


总结

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

  • 当前机器人技术在受控环境中表现良好,但泛化到新环境仍是核心挑战。
  • 解决泛化问题需要多样化的大规模数据,数据收集是关键。
  • 模拟器有用但存在局限,需与真实数据结合。
  • 元学习可以让机器人更快地适应新场景。
  • 在机器人实验室工作涉及软件、硬件和调试的综合技能。
  • 强化学习是机器人学的重要框架,但需解决奖励定义和自主重置等实际问题。
  • 未来的一个重点是建立标准化、可共享的大型数据集以推动进步。
  • 对于初学者,最好的方式是动手实践,并从模拟和现有资源开始学习。

感谢切尔西·芬恩教授分享她在机器人技术与强化学习交叉领域的深刻见解。


  1. 1 ↩︎

  2. 1 ↩︎

  3. 1 ↩︎

  4. 1 ↩︎

  5. 1 ↩︎

  6. 1 ↩︎

  7. 1 ↩︎

  8. 1 ↩︎

  9. 2 ↩︎

  10. 1 ↩︎

  11. 2 ↩︎

  12. 1 ↩︎

  13. 2 ↩︎

  14. 2 ↩︎

  15. 2 ↩︎

  16. 2 ↩︎

  17. 3 ↩︎

  18. 2 ↩︎

  19. 3 ↩︎

  20. 2 ↩︎

  21. 2 ↩︎

  22. 2 ↩︎

  23. 3 ↩︎

  24. 2 ↩︎

  25. 3 ↩︎

  26. 3 ↩︎

  27. 2 ↩︎

  28. 3 ↩︎

  29. 3 ↩︎

  30. 3 ↩︎

  31. 3 ↩︎

  32. 3 ↩︎

  33. 3 ↩︎

  34. 3 ↩︎

  35. 2 ↩︎

  36. 3 ↩︎

  37. 3 ↩︎

  38. 2 ↩︎

  39. 3 ↩︎

  40. 2 ↩︎

  41. 2 ↩︎

  42. 2 ↩︎

  43. 3 ↩︎

  44. 3 ↩︎

  45. 3 ↩︎

  46. l ↩︎

  47. l ↩︎

  48. l-1 ↩︎

  49. l ↩︎

  50. l ↩︎

  51. 3 ↩︎

  52. l ↩︎

  53. l-1 ↩︎

  54. l ↩︎

  55. 0 ↩︎

  56. 1 ↩︎

  57. 0 ↩︎

  58. 0 ↩︎

  59. l ↩︎

  60. l ↩︎

  61. l-1 ↩︎

  62. l ↩︎

  63. 0 ↩︎

posted @ 2026-03-26 08:16  绝不原创的飞龙  阅读(3)  评论(0)    收藏  举报