Vizuara-机器学习实践笔记-全-
Vizuara 机器学习实践笔记(全)
001:项目介绍与学习规划 🚀
在本节课中,我们将要学习如何开始你的机器学习之旅。我们将介绍“实践教学”项目的核心理念,并规划从零开始学习机器学习的路径。课程将帮助你明确学习目标,并指导你如何筛选初始学习资源。
大家好,欢迎来到“面向所有工程师的机器学习:实践教学”项目。
我是 Raj Danandeer 博士。在这一系列视频中,我将从零开始重新学习机器学习。我会将我的学习过程记录下来,并与你们分享所有资源,以便你们可以跟随我一起踏上这段旅程。
首先,让我介绍一下自己以及启动这个项目的原因。然后,我们将直接进入第一讲。
我的名字是 Raj Danandeer 博士。我于2017年从印度理工学院马德拉斯分校获得机械工程学士学位。随后,我在麻省理工学院完成了博士学位,并于2022年获得了机器学习领域的博士学位。实际上,我在2018年参加了麻省理工学院的第一次机器学习讲座,这完全改变了我的生活。在接下来的四年里,我掌握了机器学习概念,发表了ML研究,完成了机器学习实习和企业工作,并最终在麻省理工学院获得了机器学习领域的博士学位。
2022年,我回到印度,带着一个使命:教育数百万计划转型到机器学习领域的工程师,并指导他们的旅程。我希望能帮助所有想要转型到机器学习的学生。我一直在思考多种方式来帮助学生。什么才是最好的方式?如何确保学生能够开始他们的旅程,并且不会感到困惑、失去动力或停滞不前?
因此,我启动了这个名为“实践教学”的新项目。在这些视频中,我将扮演一个对机器学习一无所知的完全初学者。我会从零开始,就像你们如果从零开始学习一样。这样,你们就能与我在视频中展示的内容产生共鸣。我会以完全初学者的视角展示一切,就像你们想要转型到机器学习领域一样。
每天早晨,我都会发布前一天学习的内容,制作像这样的视频。我会制作讲义,分享参考资料,并完成作业,分享我的解答。你们可以看到,我已经带来了这本书,用来记录我学习的所有内容,它将作为学习资源。我还带来了不同颜色的笔,以便在学习材料时突出不同的信息。
在我重新学习这些材料时,我会分享哪些内容在工业界真正有用,哪些已经过时。我还会分享大量关于哪些领域包含开放研究课题的信息,以便感兴趣的学生也能开始他们的研究之旅。我会仔细选择学习材料,并向你们展示我是如何选择这些材料的。
对于那些感到困惑或停滞不前,无法成功转型到机器学习的学生来说,也许现有的课程和线下视频不够有启发性。如果看到别人从零开始学习机器学习,然后你可以跟随同样的旅程,这可能会激励你。这些视频完全免费,没有隐藏费用,纯粹是教与学。所以,你们可以加入我的旅程,一起转型到机器学习领域。
可能会有大量的视频,因为今天是我开始的第一天。每天我都会发布我的学习内容。在我们开始之前,你应该首先明确自己学习机器学习的目标。根据我的经验,最大的动力有三点:第一,如果你想在这个领域找到一份工作,但目前简历上没有相关经验,这可以成为开始学习机器学习的强大动力。第二,如果你想申请研究生院,但目前没有机器学习项目经验,因此你想学习机器学习。第三,你可以看到整个世界都在向机器学习和人工智能转型,你不想被落下。你对这个主题感兴趣,只是想了解机器学习,以便在这个快速转型的世界中感到自己属于其中。
我和所有希望转型的你们处境相似,因为我曾是机械工程专业,对机器学习一无所知。我在四、五年前了解了它,这完全改变了我的职业和个人生活。我希望我制作的这一系列视频能够激励、鼓舞并指导你们转型到机器学习的旅程。
我会每天在这个YouTube频道上发布内容,并在评论中附上资源和我在这本书中做的笔记,以便你们可以跟随学习。
非常感谢,我们第一讲见。
好的,让我们开始“实践教学”机器学习项目的第一讲视频。
正如你们在屏幕上看到的,我已经开始填写这本日记或笔记本,我将用它来维护我从零开始学习的所有讲义。今天将是一段漫长而富有成果的旅程的开始,所以让我们开始理解和学习机器学习的旅程吧。

首先,我要做的是进入访客模式,因为我不想让我过去的浏览历史影响我搜索不同内容时看到的结果。
让我首先说明,你们转型到机器学习的目标是以下三者之一:要么你想找一份工作,要么你想申请研究生院并转型到机器学习,第三是你只是想了解这个领域,因为整个世界都在朝这个方向发展,你对此感到好奇。
很好,那么作为一个初学者,我首先要搜索的是“ML for beginners”。好的。
我立刻可以看到已经出现了大量的结果。目前我无法比较这些内容,而且我也看到开始出现了一些赞助结果。
让我开始浏览所有的搜索结果,并尝试缩小我们开始学习的初始资源范围。
让我们排除一些我们肯定不需要的东西。例如,看看这个“Layers or data science online course”。但这看起来更像是针对就业安置的,而且这可能是一个为早期职业专业人士设计的付费项目。我们现在可能不需要这个,因为我们想要的是能在机器学习理论和实践知识方面为我们打下基础的东西。我们想要一种深入的课程,同时也能给我们实践经验。所以这看起来像是一个研究生项目,我们现在不需要这种程度的专业化,所以先排除这个。
这里还有另一个数据科学课程,显示“先就业后付款”等等。这也是付费的,所以我们现在先不考虑这些项目。而且这些项目可能有点“速成”,它们可能会教你快速完成项目,快速编码,但可能不会真正培养机器学习的坚实基础,而这是极其关键的。我的主要目标之一是指导你们作为初学者的旅程,但同时要打好基础,建立非常扎实的基本功,而不仅仅是做快速项目。
所以我现在也排除这个选项。好的,然后我听说过Coursera,也听说过Udemy。让我看看Coursera上的项目。让我搜索“machine learning for beginner Coursera”。
在这里你可以看到“面向初学者的顶级机器学习课程”。我可以看到这里又有大量的课程。这再次让人不知所措,很难选择到底该学什么,因为它们都有不错的评分。但我知道这门由吴恩达教授的课程相当不错,它有4.9的评分和2.1万条评论。让我点击看看这门课程的具体内容。
我可以看到可以免费注册,这很好。其次,我可以看到在这个项目中,我们将学习构建ML模型。

本节课中我们一起学习了“实践教学”项目的介绍和启动方法。我们明确了学习机器学习的三个主要动机:求职、升学或保持与时俱进的好奇心。我们开始了筛选初始学习资源的第一步,并认识到建立扎实的理论与实践基础的重要性,而不仅仅是追求速成项目。在下一节中,我们将深入探讨如何具体选择第一门课程并制定学习计划。
002:机器学习入门 🚀
在本节课中,我们将要学习机器学习的基本概念、历史背景以及它与人工智能和深度学习的区别。我们将结合MIT的理论课程与微软的实践课程内容,为你提供一个清晰、全面的入门指南。
概述
机器学习正深刻地影响着我们生活的方方面面。本节课将带你了解机器学习的定义、其无处不在的应用、当前的热潮背后的原因,并厘清机器学习、人工智能与深度学习之间的关系。
什么是机器学习?
我们首先从微软的课程开始。机器学习已经渗透到我们日常生活的每个角落。
以下是机器学习的一些关键应用领域:
- 游戏与竞技:例如AlphaGo和IBM的“深蓝”,它们学会了击败人类顶尖棋手。
- 内容推荐:Netflix、YouTube、亚马逊的“为您推荐”功能,会根据你的互动行为个性化推荐内容。
- 医疗健康:加速药物发现、辅助癌症诊断。
- 消费应用:手机助手(如Siri、Alexa)、面部识别解锁、指纹识别。
- 金融与识别:股市预测、医生或邮递员的字符识别。

由此可见,从你醒来接触手机的那一刻起,到教育、医疗等各个行业,机器学习都在发挥作用。
机器学习的热潮
上一节我们介绍了机器学习的广泛应用,本节中我们来看看公众对它的兴趣变化。当前围绕机器学习的炒作并非历来如此。

通过查看Google趋势数据可以发现,自2004年以来,全球对“机器学习”的搜索兴趣增长了上百倍,目前正处于历史峰值。印度在全球的兴趣排名中位列第三。这解释了为何当下学习机器学习如此重要——整个社会都高度关注这一领域。

这种热潮的根源与机器学习的发展历史密切相关。

人工智能、机器学习与深度学习
在了解历史之前,我们需要明确几个核心概念的区别。人们常常混淆人工智能、机器学习和深度学习。
我们可以从一个孩子的学习过程来理解智能的本质:我们不断从周围环境接收输入,并从中发现隐藏的模式,这些模式使我们能够做出决策。
基于这个类比,我们可以这样定义:
- 人工智能:一个广义概念,目标是让机器能够模拟人类的智能行为(如推理、学习、解决问题)。
- 机器学习:实现人工智能的一种主要方法。其核心是让计算机从数据中学习规律和模式,而无需为每个任务进行明确的编程。
- 关键思想:
学习 = 通过数据发现模式
- 关键思想:
- 深度学习:是机器学习的一个子领域,它使用被称为神经网络的复杂结构来学习数据中的模式,特别擅长处理图像、声音和文本等非结构化数据。
简单来说,深度学习是机器学习的一种方法,而机器学习是实现人工智能的一条重要途径。

机器学习简史
理解了核心概念后,我们来看看机器学习是如何发展到今天的。机器学习的发展并非一帆风顺,经历了多次“热潮”与“寒冬”。
以下是其发展的几个关键阶段:
- 1940-50年代:思想萌芽。沃伦·麦卡洛克和沃尔特·皮茨提出了第一个人工神经元数学模型。
- 1950-60年代:早期乐观。艾伦·图灵提出了“图灵测试”。1956年,“人工智能”一词在达特茅斯会议上被正式提出,人们非常乐观。
- 1970年代:第一次AI寒冬。由于计算能力有限、数据匮乏,早期承诺无法实现,资助大幅减少。
- 1980年代:专家系统兴起。基于规则的“专家系统”在商业上取得成功,但规模有限,最终再次遇冷。
- 1990年代-2000年代:机器学习崛起。统计学习方法(如支持向量机)成为主流。互联网兴起带来了更多数据。
- 2010年代至今:深度学习革命。得益于海量数据、强大的GPU计算和算法改进,深度学习取得突破性进展,引发了当前这一轮人工智能热潮。
所以,当前的火热并非偶然,是数据、算力和算法多年积累后共同作用的结果。
机器学习的基本流程
了解了历史背景,现在我们聚焦于机器学习本身是如何工作的。MIT的课程为我们提供了一个清晰的框架。
一个典型的机器学习项目遵循以下流程:
- 定义问题与收集数据:明确你要解决什么问题,并收集相关的数据。
- 数据预处理:清理数据,处理缺失值,将其转换为适合模型的格式。
- 选择与训练模型:选择一个算法(模型),用大部分数据(训练集)来训练它,让模型学习数据中的模式。
- 核心目标:找到一个函数
f,使得预测值 y ≈ f(输入数据 x)。
- 核心目标:找到一个函数
- 评估模型:使用未参与训练的数据(测试集)来评估模型的性能,看它是否能够很好地泛化到新数据。
- 调优与部署:根据评估结果调整模型参数,优化性能,最终将模型部署到实际应用中。
核心概念:监督学习
在众多机器学习类型中,监督学习是最常见和基础的一种。它就像一个有老师指导的学习过程。
监督学习的关键要素包括:
- 训练数据:每个样本都包含一个“输入”和一个对应的“正确答案”(标签)。例如:(电子邮件内容, 垃圾邮件/非垃圾邮件)。
- 假设函数:模型试图学习的从输入到输出的映射关系,可以表示为一个函数
h(x)。 - 损失函数:用于衡量模型的预测值
h(x)与真实标签y之间的差距。常见的损失函数包括均方误差:Loss = (1/n) * Σ (h(x_i) - y_i)^2。 - 学习算法:通过优化算法(如梯度下降)不断调整模型参数,以最小化损失函数,从而使
h(x)越来越接近真实的映射关系。
总结

本节课中我们一起学习了机器学习的入门知识。我们了解到机器学习已广泛应用于游戏、推荐、医疗等领域,并正处于发展热潮之中。我们厘清了人工智能、机器学习和深度学习三者之间的关系。回顾历史,我们看到当前的热潮得益于数据、算力和算法的共同突破。最后,我们介绍了机器学习的基本流程,并深入探讨了最常见的监督学习及其核心组件:训练数据、假设函数、损失函数和学习算法。这些基础概念为我们后续的深入学习奠定了坚实的基石。
003:机器学习模型类型 🧠
在本节课中,我们将要学习机器学习中不同类型的模型。我们将从最核心的监督学习开始,了解其基本概念,并通过一个直观的例子来加深理解。接着,我们会简要介绍其他类型的模型,如无监督学习和强化学习,为后续学习打下基础。

上一节我们回顾了人工智能、机器学习和深度学习之间的关系。本节中,我们来看看机器学习模型有哪些主要类型。
监督学习
监督学习是机器学习中非常重要且应用广泛的一种模型类型。在监督学习中,我们本质上被给予一个数据集,其中 x 代表输入,y 代表输出。假设我们有 M 个输入-输出对。

监督学习的主要思想是学习输入 x 与输出 y 之间的关系。我们被给予这些输入-输出对。

以下是监督学习的可能示例:
- 输入 x 可以是患者的血压水平、血糖水平等。
- 输出 y 可以是患者是否有心脏病发作的概率,或在不久的将来是否会心脏病发作。
另一个例子是:
- 输入 x 可以是一张图像。
- 输出 y 可以是“这是谁的脸”。
它之所以被称为“监督”学习,是因为我们被给予了“问题”和“答案”对。我们拥有一组数据,我们知道问题(即 x)和答案(即 y)。例如,如果问题是血压、血糖水平(输入),答案就是哪些患者可能心脏病发作。
真正的挑战在于,基于这些数据,我们必须找出如何回答未来的问题。假设数据集包含50名患者及其是否心脏病发作的信息(例如,25名患者答案为“是”,25名为“否”)。我们计划回答的真正问题是:假设一名新患者前来就诊,我测量了他们的血压、血糖水平等,我能否预测这名患者是否会心脏病发作?这就是监督学习设置下机器学习的主要任务。
为了更直观地理解监督学习,我们可以通过一个实践演示来观察。以下是一个使用“可教机器”网站进行的图像分类项目示例,它完美诠释了监督学习:
- 项目目标:让计算机学会区分猫和狗的图片。
- 准备数据:上传10张猫的图片作为一类输入,并告诉计算机这些图片的“答案”是“猫”。上传10张狗的图片作为另一类输入,并告诉计算机这些图片的“答案”是“狗”。
- 训练模型:基于这些输入-输出对(图片及其对应标签),让机器学习模型进行训练,学习区分猫和狗的特征模式。
- 测试模型:上传一张全新的、训练时未见过的猫或狗图片。
- 观察结果:训练好的模型能够以很高的准确率(例如100%)预测新图片是“猫”还是“狗”。
这个例子展示了监督学习的核心:模型通过学习已知的“问题-答案”对,掌握了内在规律,从而能够对新的、未知的“问题”给出预测“答案”。模型可能自动识别了诸如猫有尖耳朵、较长胡须等特征。
其他类型的机器学习模型
在深入探讨了监督学习之后,我们简要了解一下其他主要的机器学习模型类型。以下是机器学习的主要分类:
- 监督学习:如上所述,模型从带有标签的训练数据中学习。
- 无监督学习:模型在没有标签的数据中寻找隐藏的结构或模式。例如,对客户进行分组(聚类)。
- 强化学习:模型通过与环境互动并接收奖励或惩罚来学习如何采取行动以实现目标。例如,训练一个玩游戏的AI。

本节课中我们一起学习了机器学习模型的几种主要类型,并重点通过一个图像分类的例子深入理解了监督学习的工作原理。我们了解到,监督学习通过已知的输入-输出对来训练模型,使其能够对未来新的输入做出预测。理解这些基础模型类型是构建更复杂机器学习应用的第一步。
004:任何机器学习项目的六个步骤 🧠
在本节课中,我们将学习任何机器学习项目都通用的六个核心步骤。我们将通过一个简单的学生成绩预测例子,来具体理解每一步的含义和操作。
在上一讲中,我们介绍了三种主要的机器学习模型:监督学习、无监督学习和强化学习。本节中,我们来看看如何将一个实际的机器学习项目分解为可执行的步骤。



根据对数十个真实世界项目的观察,我发现任何机器学习项目本质上都遵循以下六个步骤。这个框架主要参考了MIT 6.036课程的内容。

以下是构成任何机器学习项目的六个主要步骤:
- 获取数据
- 构思可能的解决方案空间
- 定义“好”解决方案的标准
- 寻找算法
- 运行算法
- 验证结果
在这六个步骤中,除了第五步“运行算法”是由机器完成的,其余所有步骤都需要由人来完成。学习本系列课程的目标,就是让你最终能够独立完成所有这些步骤。

起初,这些步骤的概念可能有些抽象。为了更清晰地理解,我通常会边学习边做笔记。这也是我贯穿本系列的建议:观看视频时,请同步记笔记,这有助于理清概念。

让我们再次审视这六个步骤,并用更具体的术语来理解:
- 第一步:获取数据。
- 第二步:构思可能的解决方案空间。这也可以理解为生成假设。假设输入是
x,输出是y,我们需要找到x和y之间的关系。 - 第三步:定义“好”解决方案的标准。这里我们需要确定,是什么让一个假设比另一个假设更好。
- 第四步:寻找算法。我们需要选定学习算法,或者说最终确定我们的假设。
- 第五步:运行算法。
- 第六步:验证结果。
为了让大家更好地理解,我构建了一个简单的实践案例,并使用Excel表格来演示,以便于大家跟随这六个步骤。


让我们来看一个简单的问题。假设你被委派了这样一个任务:你访问了一所学校,学校希望你能够根据学生的学习小时数,来预测学生的考试分数百分比。
记住,上一讲我们提到机器学习处理的是预测和泛化。这正是此类问题:你获得了一些历史学生数据(学习时长和对应分数),你的任务是为一个新学生(给定其学习时长)预测其分数。你被要求使用机器学习来解决这个问题。
作为一个初学者,让我们遵循这六个步骤。第一步是收集数据。你向学校索要数据,因为对于机器学习来说,没有数据就无从谈起,正如我们需要食物才能生存一样。


学校答复说,他们有6名学生的数据,包括学习小时数和获得的分数百分比。当你将这些数据绘制在图表上时,X轴代表学习小时数,Y轴代表分数百分比。

很好,现在进入下一步。第二步是构思可能的解决方案空间。作为一名机器学习工程师,你现在需要找到输入(学习小时数)和输出(分数百分比)之间的关系。这时你就会思考可能的解决方案空间,也就是生成假设。



让我们生成两个假设:
- 第一个假设:输入和输出之间存在直线关系。
- 第二个假设:输入和输出之间存在曲线关系。
作为一名ML工程师,你想出了这两个假设。这就是第二步的含义:构思可能的解决方案空间,即思考输入 x 和输出 y 之间可能的关系(假设)。目前我们已经想到了两个假设。

现在,让我们进入至关重要的第三步:定义什么是“好”的解决方案。我们如何决定哪个假设更好?是第一个直线假设好,还是第二个曲线假设好?我们需要一个量化的指标来进行比较。


这时,损失函数就登场了。损失函数本质上是我们的预测值与实际数据值之间的差异。通俗地说,它衡量的是:当实际值是 A 时,我们预测了某个猜测值 G,我们对此有多“不满意”。就是这样。所以,损失函数就是你的猜测与实际值之间的差值。

本节课中,我们一起学习了构成任何机器学习项目的六个核心步骤:获取数据、构思假设、定义评估标准、选择算法、运行算法和验证结果。我们通过一个学生成绩预测的简单例子,初步理解了前三个步骤,特别是损失函数的概念。在接下来的课程中,我们将继续用这个例子,深入探讨如何应用这些步骤来构建一个完整的机器学习模型。
005:安装Python并运行你的第一个代码!🚀
在本节课中,我们将学习Python编程语言,并完成Python和VS Code编辑器的安装。你将亲手执行你的第一段Python代码。
上一节我们介绍了机器学习项目的六个步骤。本节中,我们来看看如何为这些步骤准备核心工具——Python。
什么是Python?🐍
Python是一种流行的编程语言,以其简单性和可读性而闻名。它被广泛应用于机器学习、人工智能等多个领域。
可以将Python视为所有机器学习工程师都应掌握的工具。如果没有Python,机器学习的研究进展将会显著放缓。Python使得工程师和学生能够轻松地在自己的电脑上运行机器学习算法。
我们之前看到的机器学习六个步骤,正是在Python环境中运行和执行的。因此,许多课程都非常重视Python。
为什么Python在机器学习中如此流行?✨
Python之所以在机器学习领域广受欢迎,主要有以下四个原因:
以下是四个关键原因:
- 简单易学:Python代码易于理解和编写。对于没有编程背景的工程师来说,用Python写代码比用C或C++简单得多。
- 丰富的库:大量研究人员和学生为机器学习和人工智能构建了现成的库。例如,训练一个语言模型时,无需从头编写所有代码,可以直接使用Python中预写的库。核心代码示例如下:
# 例如,使用scikit-learn库训练一个简单模型 from sklearn.linear_model import LinearRegression model = LinearRegression() model.fit(X_train, y_train) - 开源免费:Python可以完全免费下载、安装和使用,没有入门门槛。
- 强大的社区支持:有数百万用户每天都在使用Python。如果你在互联网上提出疑问,几乎总能得到解答。
如何安装Python?💻
现在,让我们开始动手安装Python。这个过程非常简单快捷,大约只需15到20分钟。
- 访问下载页面:打开浏览器,在搜索引擎中输入“download Python”,或直接访问Python官方网站。页面会根据你的操作系统(如Windows、Mac)显示相应的下载选项。
- 选择版本:请务必下载最新版本的Python。不要下载Python 2,因为它是非常旧的版本。点击对应的下载按钮。
- 运行安装程序:下载完成后,在你的“下载”文件夹中找到安装文件(如
.exe文件或.pkg文件)。双击运行它。 - 遵循安装向导:在安装过程中,请勾选“Add Python to PATH”选项(这对于在命令行中直接使用Python很重要),然后按照屏幕提示点击“Next”或“Install”完成安装。
安装代码编辑器:VS Code📝
仅仅安装Python还不够,我们还需要一个文本编辑器来编写代码。VS Code是一个功能强大且流行的选择。
上一部分我们安装了Python解释器,它负责执行代码。本节中,我们来看看代码编辑器的作用及其与解释器的区别。
编辑器是你编写和修改代码的工具,而解释器(Python)是读取并运行这些代码的程序。VS Code等编辑器提供了语法高亮、代码提示等功能,让编程更高效。

- 访问VS Code官网:在浏览器中搜索“Visual Studio Code”并进入官网。
- 下载安装包:选择对应你操作系统的版本进行下载。
- 安装VS Code:运行下载的安装程序,并遵循提示完成安装。
运行你的第一个Python代码🎉
我的编程哲学很简单:不要一开始就试图理解所有细节。先运行一个能工作的代码,看到输出结果,这会给你带来继续学习的巨大动力。之后,你再回头理解代码是如何编写的。
最好的学习方式就是先动手实践,直接运行你的第一段代码,然后再研究代码的构成。
现在,让我们来运行第一段Python代码。
- 打开VS Code:启动刚刚安装的VS Code。
- 新建Python文件:点击“File” -> “New File”,然后保存文件,命名为
hello.py(确保文件扩展名为.py)。 - 编写代码:在文件中输入以下代码:
这行代码使用了print("Hello, Machine Learning!")print()函数,它的作用是将括号内的内容输出到屏幕上。 - 运行代码:
- 在VS Code中,你可以点击右上角的“运行”三角按钮。
- 或者,打开终端(命令行),导航到你的文件所在目录,然后输入命令:
python hello.py

如果一切顺利,你将在终端或输出窗口中看到:Hello, Machine Learning!
总结📚

本节课中,我们一起学习了Python的基础知识及其在机器学习中的重要性。我们完成了Python解释器和VS Code编辑器的安装,并成功运行了第一个Python程序。
记住,学习编程的关键是勇于实践。如果在安装或运行代码过程中遇到任何问题,请随时在评论区留言,我们会尽快为你解答。
现在,你已经拥有了开始机器学习实践之旅的核心工具。在接下来的课程中,当我们学习回归、分类等算法时,你将能直接上手操作。准备好了吗?让我们继续前进!
006:线性分类器(第一部分) 🧠
在本节课中,我们将要学习并动手开发我们的第一个机器学习算法——线性分类器。我们将从一个具体的“猫狗分类”问题出发,逐步理解其背后的理论、数学公式以及构建思路。本节课将重点介绍机器学习项目六步法中的前两步:数据收集与假设空间生成。
在之前的五节课中,我们涵盖了多种内容,包括机器学习模型的类型、Python环境安装、运行第一个机器学习代码以及任何机器学习项目的六个步骤。如果你还没有观看之前的课程,请先学习它们,然后再回到本节课。
今天是一个非常有趣的主题。我们将开发我们的第一个机器学习算法。这是一节关于线性分类器的课程。我计划用两节课来讲解,今天是第一部分。我也会在大家面前写下许多内容,请准备好纸笔,这将极大地帮助你理解。让我们开始吧。

在开始之前,我想说明,我为今天的课程准备了四页手写笔记和演示文稿PDF,这些资料都会在视频的信息区提供给大家。
所以,让我们开始今天有趣的课程,我们将开发第一个机器学习算法——线性分类器。

问题定义:猫狗分类 🐱🐶
我们将要解决的问题是猫和狗的分类。记得在之前的一节课中,我们看过Teachable Machine界面,在那里我们进行了两类图像的分类:一类是猫,另一类是狗。我们上传了10张猫的图片和10张狗的图片,然后运行了AI模型。今天,我们将开发自己的AI模型,使其能够区分猫和狗。

我们将深入探讨一些理论和实践。另外我想分享的是,如果你去看微软的课程,他们讲解分类的方式是直接使用Python和项目,但没有提及分类的理论或背后的数学原理。而在今天的课程中,我们将遵循MIT课程的方法,深入探讨线性分类背后的理论和数学理解。
本系列课程的目标之一不仅是做实践项目,还要向你介绍机器学习的基础。我们当然也会做实际的分类项目,但最初大家确实需要理解理论。因此,这第一节课以及明天的课程将侧重于分类理论,之后我们会通过Python项目进行实践演示。
我们面临的问题是必须区分猫和狗。还有一点,我们基于所有这些图像测量了两个属性:我们测量了胡须长度,也测量了耳朵下垂指数。我们知道狗的耳朵是下垂的,所以我们构建了一个名为“耳朵下垂指数”的指标;我们也知道猫的胡须更长,所以我们测量了另一个指标“胡须长度”。
现在,你的任务是基于这两个测量值构建一个线性分类器。这样,如果我给你任何其他新动物,你再次测量这两个指标——耳朵下垂指数和胡须长度——你的模型应该能够预测它是一只猫还是一只狗。
我希望每个人都理解了这个问题。想象一下,你有10只猫和10只狗,并且你已经测量了所有这些动物的耳朵下垂指数和胡须长度,这就是你的训练数据。问题是构建一个线性分类器模型,该模型可以对任何新动物进行分类。假设现在我给了你另一只新动物,你测量了它的下垂指数和胡须长度,基于训练数据,你的工作是对新动物进行分类。
输入是这两个测量值,输出是分类结果:是猫还是狗。很好,我希望每个人都理解了这个问题。

回顾:机器学习六步法 📋
现在,每当有人给你任何像这样的机器学习项目时,你的第一反应应该是回到任何机器学习项目的六个步骤。
我想在本系列的第三天,我们学习了任何机器学习项目的六个步骤。我们看到这六个步骤是:1. 收集数据,2. 生成假设空间,3. 定义损失函数,4. 寻找算法,5. 运行算法,6. 验证结果。
在今天的课程中,我们将重点看第一步和第二步,并稍微涉及第三步。在明天的课程中,我们将看第四、第五和第六步。
好的,你被分配了这个项目,并且你有任何机器学习项目的六个步骤。让我们从第一步开始,收集数据。
第一步:收集数据 📊
现在,我将在这个白板上为大家书写。
我们的当前任务是生成线性分类器,或者说找到最佳的可能算法。
正如我提到的,我们看了第一步。第一步是收集数据。
现在让我们看看数据。我将在这里绘制数据。数据看起来像这样。这是y轴,这是x轴。X轴标签是胡须长度。Y轴标签是耳朵下垂指数。
好的,第一个数据点是狗的。狗通常有下垂的耳朵和较短的胡须长度,所以所有的数据点都分布在这里。这是我们收集的数据,这是狗的数据。
对于猫,所有的数据点都分布在这里。猫通常有较低的耳朵下垂指数,并且它们的胡须长度通常更长。所以这是猫的数据,分布在这里。

现在,我们的目标。让我切换回蓝色。我们的目标是找到最佳的线性分类器。当我们说找到最佳的线性分类器时,我们的目标是找到最佳的分隔线。
这条线将上述数据分隔开。这就是我们的目标。进一步的目标是,每当有人给我们一个新的数据点时,假设有人给了我们一个新的数据点,它位于这里的某个位置。基于那条直线,我们应该能够分类它是猫还是狗。这就是目标。
第二步:生成假设空间 🧩
如果我们进入第二步,我们必须生成一个假设空间。
由于我们正在构建一个线性分类器,假设空间将是所有可能的直线的集合。
让我再次画出上面的图。这是狗的数据。这是猫的数据。
好的,现在假设空间是所有可能存在的直线。让我们看看,假设空间在视觉上可能看起来像这样。
让我用橙色表示。所以假设可以是这条线,可以是那条线,可以是这条线,可以是那条线,可以是这个,可以是那个。

本节课中我们一起学习了线性分类器问题的定义,回顾了机器学习项目的六步法,并详细探讨了前两步:数据收集与假设空间生成。我们通过一个具体的猫狗分类例子,理解了如何将实际问题转化为数据点,并认识到线性分类器的核心任务是找到一条最佳直线来分隔不同类别的数据。在下一节课中,我们将继续探讨如何定义损失函数并寻找最优的算法。
007:线性分类器(第二部分)🚀




在本节课中,我们将继续学习线性分类器。我们将完成猫狗分类问题的剩余步骤,包括寻找算法、运行算法和验证结果。你将编写并运行你的第一个机器学习算法。



上一节我们介绍了猫狗分类问题的数据、假设空间和损失函数。本节中,我们来看看如何找到最优的分类器参数。





我们的目标是找到一条最优的直线(线性分类器),能够最好地区分猫和狗的数据点。这条直线由参数 θ₀、θ₁ 和 θ₂ 决定,其方程为:
θ₁x₁ + θ₂x₂ + θ₀ = 0


步骤四:寻找算法 🧠



现在,我们进入机器学习项目的第四个步骤:寻找算法。我们将介绍第一个算法——随机线性分类器。







随机线性分类器算法的工作原理如下:



输入:
- 数据集 D
- 一个超参数 K(例如,K=100)




输出:
- 最优的参数 θ₀、θ₁、θ₂,即区分猫狗的最佳直线。




以下是该算法的三步流程:






- 随机选择参数:随机生成 K 组 θ₀、θ₁、θ₂ 的值。这相当于随机画出 K 条不同的直线。
- 计算训练误差:对于这 K 组参数(即 K 条直线),分别计算它们在训练数据上的分类错误数量。训练误差就是分类错误的样本数。
- 选择最优假设:从所有尝试的直线中,选择训练误差最低的那一条直线所对应的参数。


直观地理解,这个算法就是尝试许多条随机生成的直线,然后挑出犯错最少的那一条。




步骤五:运行算法 ▶️




上一节我们定义了算法,本节中我们来看看如何实际运行它。




运行随机线性分类器算法意味着用代码实现上述三步流程。你需要:
- 编写代码来随机生成参数。
- 编写代码来计算给定参数下的分类错误数。
- 编写代码来比较所有尝试的结果,并保留最佳参数。




步骤六:验证结果 ✅




算法运行完毕后,我们会得到一组“最优”参数。最后一步是验证这些参数和对应的分类器是否真的有效。





验证通常包括:
- 检查训练集性能:确认算法在训练数据上确实达到了低错误率。
- 使用测试集:在一个全新的、算法从未见过的数据集(测试集)上评估分类器的性能。这是检验模型泛化能力的关键。
- 分析结果:如果测试集上性能也很好,说明模型成功。如果性能下降很多,可能意味着模型过拟合了训练数据。




本节课中我们一起学习了机器学习项目的完整流程,并以猫狗分类为例,完成了从定义问题到运行验证的所有步骤。我们重点介绍了随机线性分类器算法,这是一个简单但直观的入门算法,帮助你理解机器学习寻找最优解的基本思想。
008:Jupyter Notebooks、NumPy、Scikit-Learn与Matplotlib入门
在本节课中,我们将开始动手实践,学习如何在Python中实现线性分类器。我们将从理论转向代码,通过一个完整的项目来巩固前两节课学到的知识。本节课是实践部分的第一部分,我们将重点介绍开发环境和核心工具。
在上一节课中,我们探讨了线性分类器,并开发了第一个机器学习算法——随机线性分类算法。我们按照六个步骤执行了该算法:收集数据、生成线性假设类、定义损失函数、寻找算法、运行算法以及验证结果。
本节课中,我们将用Python代码完整地实现这六个步骤。通过从头开始编写代码,你将获得对之前所学理论的实际动手经验。本课程旨在理论与实践相结合,前两节课侧重于理论,而本节课将深入实践层面。
为了顺利进行编码,我们需要配置合适的开发环境并了解一些强大的Python库。因此,本节课的第一部分将专注于介绍Jupyter Notebook和三个关键的Python库:NumPy、Scikit-Learn和Matplotlib。我们将学习如何将它们集成到VS Code环境中。
Jupyter Notebook简介 📓
首先,我们来了解Jupyter Notebook。根据官方文档,Jupyter Notebook是进行交互式计算和沟通的社区标准工具。
我认为Jupyter Notebook在沟通方面表现卓越。如果仅在VS Code中编写代码,很难与他人有效沟通,因为将代码与标题、描述等内容整合起来相当困难。

Jupyter Notebook文档允许你混合文本、数学公式、图像、视频和代码。这使得解释和演示代码变得非常容易。总体而言,它是一个出色的沟通工具。
因此,我将通过Jupyter Notebook展示代码,这样能更清晰地展示每一行代码是如何工作的。这也是全球许多工程师、学生和教师(包括我自己)喜欢使用Jupyter Notebook进行机器学习教学和初始脚本编写的原因。
三大核心Python库 🧰
在介绍如何集成Jupyter Notebook之前,让我们先认识三个强大的Python库,它们构成了科学计算和机器学习的基础。
1. NumPy
第一个库是NumPy。访问 numpy.org 可以找到其官方网站。NumPy是Python科学计算的基础包。
NumPy允许我们定义和操作数组及矩阵。它提供了许多用于线性代数和其他机器学习框架的计算工具。这个库是完全开源的。
使用NumPy时,你首先需要告诉Python编辑器加载这个包。之后,你可以轻松地执行各种数组操作,例如创建二维数组、设置特定元素或按行查找最大值。NumPy使得定义数组和执行数学运算变得非常简单。
它在数据科学、机器学习和可视化领域有广泛的应用。实际上,它已成为科学计算中默认的数组操作包。
2. Scikit-Learn
第二个极其强大的包是Scikit-Learn,这是一个专门用于Python机器学习的库。
Scikit-Learn提供了大量开箱即用的函数,可以直接用于分类、回归、聚类等任务。这些库的目的是让他人预先编写好代码,这样当你想执行一个著名的机器学习算法时,无需从头编写所有代码,只需调用该库提供的函数即可。
Scikit-Learn本身构建在NumPy之上。无论是简单项目还是复杂项目,Scikit-Learn都是执行机器学习任务时最常使用的库。
3. Matplotlib
第三个要介绍的包是绘图库,名为Matplotlib。
例如,当你运行一个Python脚本并希望生成图表进行可视化时,Matplotlib就是你的工具。它是一个用于创建静态、动态和交互式可视化的综合库。

环境配置与安装 ⚙️

介绍完这些库和Jupyter Notebook后,我们将在VS Code环境中安装它们,以便我们的Python安装能够配置好这些库和Jupyter Notebook环境。
以下是配置步骤的简要概述:
- 确保已安装Python和VS Code。
- 在VS Code中安装Jupyter扩展。
- 使用Python的包管理工具
pip安装NumPy、Scikit-Learn和Matplotlib。
具体的安装和集成步骤将在下一部分详细演示。通过正确配置,你将拥有一个强大的开发环境,可以高效地编写、运行和可视化机器学习代码。
总结 📝
本节课中,我们一起学习了实践机器学习编码的准备工作。我们首先了解了Jupyter Notebook,这是一个强大的工具,能够混合代码、文本和可视化内容,极大地便利了代码的编写、演示和沟通。
接着,我们介绍了三个核心的Python库:用于高效数组运算和科学计算的NumPy,提供了丰富机器学习算法的Scikit-Learn,以及用于数据可视化的Matplotlib。这些库构成了Python机器学习生态系统的基石。

在下一部分,我们将动手配置这些工具,并开始编写我们的第一个线性分类器代码。你将看到如何将这些理论概念转化为实际运行的Python程序。
009:运行随机线性分类器算法 🚀
在本节课中,我们将学习如何在Python中实际运行随机线性分类器算法。我们将把之前学到的理论知识转化为代码,并观察其运行结果。如果你是一位Python初学者,本教程也将展示如何开始使用Python进行机器学习项目。


概述


在上一节中,我们介绍了Jupyter Notebook,并学习了如何安装它以及如何将其与VS Code集成。我们还安装了三个强大的Python库:NumPy、Scikit-learn和Matplotlib。这些工具将在今天的代码实践中用到。



本节中,我们将深入代码,实现随机线性分类器算法。如果你还没有学习之前的理论课程,强烈建议你先学习,以便更好地理解本节课的内容。
所需工具



以下是运行本教程代码所需准备的工具和环境:


- Python:需要安装Python。
- VS Code:需要安装VS Code编辑器。
- Jupyter Notebook:需要安装Jupyter Notebook。
- Scikit-learn:这是Python的机器学习库。
- NumPy:这是用于科学计算的库。
- Matplotlib:这是绘图库。

所有这些工具和库都在本系列课程的前一讲(第5讲)中进行了安装和配置。请确保你已经完成了这些步骤,以便顺利跟随本教程。



问题回顾

为了理解我们将要编写的代码,首先需要回顾一下随机线性分类器所要解决的问题。




本质上,这是一个线性分类问题。在x轴上,我们有“胡须长度”(特征x1)。在y轴上,我们有“耳垂松软度指数”(特征x2)。


我们有两类数据:
- 对于狗(第一类),数据点分布如图所示。
- 对于猫,数据点也分布如图所示。通常,猫的胡须长度更长,而耳垂松软度指数比狗更低,因此数据呈现这样的分布。


我们的目标是找到一条最佳的直线(即线性分类器),能够最好地将这两组数据分开。



从数学上讲,这条直线由以下公式定义:
θ₀ + θ₁x₁ + θ₂x₂ = 0


算法理解





要理解我们即将编写的代码,首先需要理解随机线性分类器算法本身。该算法的目标是找到最优的θ₀、θ₁和θ₂参数值,从而确定那条最佳的分类直线。



该算法包含三个步骤:


- 尝试K个随机选择:随机生成K组(例如K=100)不同的θ₀、θ₁和θ₂参数值。
- 计算训练误差:对于这K个假设(即K条由参数定义的直线),分别计算它们在训练数据上的分类误差。
- 选择最佳假设:从所有假设中,选择训练误差最低的那一个作为最终分类器。



直观上,这个算法非常简单:它让我们尝试许多条不同的直线(例如100条),然后选择在训练数据上犯错最少的那一条。


训练误差的计算方式如下:对于给定的假设(即一组θ参数)和一条数据点,我们根据公式 h(x) = sign(θ₀ + θ₁x₁ + θ₂x₂) 进行预测。如果预测的符号与数据点的真实标签一致,则分类正确;否则分类错误。训练误差就是所有数据点上分类错误的比例。



总结




本节课中,我们一起学习了运行随机线性分类器算法的准备工作。我们回顾了所需工具、要解决的分类问题以及算法的核心三步流程。在接下来的实践中,我们将把这些步骤转化为具体的Python代码。
010:感知机算法详解 🧠
在本节课中,我们将要学习最古老的机器学习模型之一——感知机算法。该算法于1957年提出,是理解现代机器学习发展的重要基石。我们将从零开始,通过白板演示的方式,完整地理解其原理和实现步骤。
上一节我们介绍了随机线性分类器,本节中我们来看看一个更“聪明”的算法——感知机算法。
感知机的历史背景

感知机算法不仅是模型,也曾被实现为物理机器。下图展示了名为Mark1的感知机机器,它是该算法的首次硬件实现。

这台机器的核心目标是模仿人脑处理视觉信息并从中学习的方式。在当时,神经网络和人工智能的概念尚未成型,感知机的提出为“机器学习算法可以模拟人脑”这一想法奠定了基础。




感知机是最早能够从错误中学习的模型之一,这本身就是机器学习的关键思想。它的主要应用是识别形状、图案、字母和数字。
问题定义
我们使用与之前构建随机线性分类器时相同的问题。假设我们收集了一批猫和狗的数据,测量两个特征:
- X1:胡须长度(x轴)
- X2:耳朵下垂指数(y轴)
狗的数据点通常具有较高的耳朵下垂指数和较低的胡须长度。猫的数据点则相反,具有较高的胡须长度和较低的耳朵下垂指数。


感知机算法的目标,就是找到一条能最佳区分这两类数据的直线。在二维空间中,这条直线的方程可以表示为:
公式: θ₁x₁ + θ₂x₂ + θ₀ = 0
我们的任务就是找到参数 θ₁、θ₂ 和 θ₀。
感知机算法
首先,让我们查看完整的感知机算法伪代码。算法的输入是训练数据 D 和一个超参数 T(迭代次数)。
以下是算法步骤:
- 初始化参数:将向量 θ 和标量 θ₀ 初始化为0。
- 代码表示:
θ = [0, 0],θ₀ = 0
- 代码表示:
- 循环迭代:对于
t = 1到T,执行以下步骤。 - 遍历数据:对于训练集 D 中的每一个数据点 (x, y),执行以下检查。
- 预测与判断:计算
y * (θ·x + θ₀)。- 如果结果
<= 0,说明当前分类错误或位于边界上。
- 如果结果
- 参数更新:如果分类错误,则按照以下规则更新参数:
- 公式:
θ = θ + y * x - 公式:
θ₀ = θ₀ + y
- 公式:
- 返回结果:循环结束后,返回找到的参数 θ 和 θ₀。
算法详解与演示
现在,我们一步步拆解这个算法。首先,我们使用向量表示法来简化。将 θ 视为包含 θ₁ 和 θ₂ 的向量,θ₀ 保持为标量。

核心思想是:算法遍历数据,每当遇到一个被当前决策边界(由 θ 和 θ₀ 定义)错误分类的点时,就更新参数。更新规则是将参数向正确分类该点的方向调整。
具体来说:
- 如果一个正类样本(
y = +1)被误分为负类,则θ增加x,θ₀增加1。 - 如果一个负类样本(
y = -1)被误分为正类,则θ减去x,θ₀减去1。
通过多次迭代(T 次),算法逐步修正错误,最终(在数据线性可分的情况下)收敛到一个能完美分类所有训练样本的决策边界。

本节课中我们一起学习了感知机算法,这是机器学习历史上的一座里程碑。我们了解了它的历史背景、核心目标,并逐步剖析了其算法步骤和更新原理。理解这个简单而强大的算法,有助于我们更好地领会现代复杂模型的思想根源。
011:编写感知机算法 🧠
在本节课中,我们将学习如何在Python中编写感知机算法的代码。感知机是最古老的机器学习模型之一,理解其实现方式对于掌握机器学习的基础至关重要。
上一节我们详细介绍了感知机算法的原理和历史背景。本节中,我们将动手实践,一步步用Python代码实现这个算法。
算法回顾

感知机算法的核心目标是找到一个线性分类器,即一条直线(或超平面),能够正确区分数据集中的正类(标记为+1)和负类(标记为-1)。其数学公式描述如下:
对于迭代次数 t = 1 到 T:
对于每个训练数据点 i = 1 到 n:
如果 y_i * (θ^T * x_i + θ_0) <= 0:
更新 θ = θ + y_i * x_i
更新 θ_0 = θ_0 + y_i
最终返回 θ 和 θ_0。
算法步骤详解
以下是感知机算法用自然语言描述的步骤,我们将据此编写代码:
- 首先,决定迭代次数
T。 - 初始化参数
θ和θ_0为0。这是我们的初始假设。 - 开始第一次迭代。
- 在每次迭代中,遍历数据集中的每一个点。
- 对于每个点,检查当前假设是否对其做出了错误分类。错误的条件是
y_i * (θ^T * x_i + θ_0) <= 0。 - 如果发生了错误,就“旋转”当前的假设直线,使其能正确分类该点。数学上,这个旋转操作对应着更新
θ和θ_0。 - 遍历完所有数据点后,完成一次迭代。
- 重复步骤3到7,直到完成预设的
T次迭代。 - 最终得到的
θ和θ_0就是学习到的分类器参数。
如果对上述步骤感到困惑,强烈建议回顾上一节关于感知机原理的详细讲解。
现在,让我们开始用Python实现这些步骤。
Python代码实现
我们将按照上述步骤,逐步构建感知机算法。
首先,导入必要的库并准备一个简单的二维数据集用于演示。
import numpy as np
import matplotlib.pyplot as plt
# 设置随机种子以确保结果可复现
np.random.seed(42)
# 生成模拟数据
# 生成20个负类样本(标签为-1),中心在(2, 2)
neg_centroid = [2, 2]
neg_data = np.random.randn(20, 2) + neg_centroid
neg_labels = -1 * np.ones(20)
# 生成20个正类样本(标签为+1),中心在(6, 6)
pos_centroid = [6, 6]
pos_data = np.random.randn(20, 2) + pos_centroid
pos_labels = np.ones(20)

# 合并数据和标签
X = np.vstack((neg_data, pos_data))
y = np.hstack((neg_labels, pos_labels))

# 可视化数据
plt.figure(figsize=(8, 6))
plt.scatter(neg_data[:, 0], neg_data[:, 1], color='red', label='Negative Class (y=-1)', alpha=0.7)
plt.scatter(pos_data[:, 0], pos_data[:, 1], color='green', marker='+', s=100, label='Positive Class (y=+1)', alpha=0.7)
plt.xlabel('Feature 1 (x1)')
plt.ylabel('Feature 2 (x2)')
plt.title('Perceptron Training Data')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
接下来,我们实现感知机训练函数。以下是核心代码:
def perceptron_train(X, y, max_iters=100):
"""
感知机训练函数。
参数:
X : numpy数组,形状为 (n_samples, n_features),输入特征。
y : numpy数组,形状为 (n_samples,),类别标签,应为+1或-1。
max_iters : 整数,最大迭代次数。
返回:
theta : numpy数组,学习到的权重向量。
theta_0 : 标量,学习到的偏置项。
mistakes_history : 列表,记录每次迭代中错误分类的数量。
"""
# 获取样本数量和特征数量
n_samples, n_features = X.shape
# 步骤1 & 2: 初始化参数 theta 和 theta_0 为0
theta = np.zeros(n_features)
theta_0 = 0.0
# 用于记录每次迭代的错误数,便于观察收敛情况
mistakes_history = []
# 步骤3: 开始迭代,t 从 1 到 T (max_iters)
for t in range(max_iters):
mistakes_in_this_epoch = 0
# 步骤4: 遍历数据集中的每一个样本点
for i in range(n_samples):
xi = X[i]
yi = y[i]
# 步骤5: 计算当前假设的预测值,并检查是否分类错误
# 公式: y_i * (θ^T * x_i + θ_0)
prediction = yi * (np.dot(theta, xi) + theta_0)
# 步骤6: 如果 prediction <= 0,说明分类错误
if prediction <= 0:
mistakes_in_this_epoch += 1
# 更新参数(旋转假设)
# 公式: θ = θ + y_i * x_i
theta = theta + yi * xi
# 公式: θ_0 = θ_0 + y_i
theta_0 = theta_0 + yi
# 记录本次迭代的错误数
mistakes_history.append(mistakes_in_this_epoch)
# 提前终止:如果本次迭代没有发生任何错误,说明已完美分类
if mistakes_in_this_epoch == 0:
print(f"Converged after {t+1} iterations.")
break
# 步骤9: 返回最终学习到的参数
return theta, theta_0, mistakes_history
现在,让我们使用这个函数来训练感知机模型。
# 训练感知机
theta, theta_0, mistakes_history = perceptron_train(X, y, max_iters=50)
print(f"Learned weights (theta): {theta}")
print(f"Learned bias (theta_0): {theta_0}")
为了理解训练过程,我们可以绘制错误数随迭代次数的变化图。
# 绘制训练过程中错误数的变化
plt.figure(figsize=(8, 5))
plt.plot(range(1, len(mistakes_history)+1), mistakes_history, marker='o', linestyle='-')
plt.xlabel('Iteration Number')
plt.ylabel('Number of Mistakes')
plt.title('Perceptron Training: Mistakes per Iteration')
plt.grid(True, alpha=0.3)
plt.show()
最后,我们将学习到的决策边界可视化,看看感知机是如何划分数据的。
# 定义一个函数来根据theta和theta_0计算决策边界
def plot_decision_boundary(theta, theta_0, X, y):
"""
绘制感知机决策边界和数据点。
"""
plt.figure(figsize=(8, 6))
# 绘制数据点
neg_idx = y == -1
pos_idx = y == 1
plt.scatter(X[neg_idx, 0], X[neg_idx, 1], color='red', label='Negative Class (y=-1)', alpha=0.7)
plt.scatter(X[pos_idx, 0], X[pos_idx, 1], color='green', marker='+', s=100, label='Positive Class (y=+1)', alpha=0.7)
# 计算决策边界线
# 决策边界是 θ^T * x + θ_0 = 0
# 对于二维情况,可以表示为: theta[0]*x1 + theta[1]*x2 + theta_0 = 0
# 重写为: x2 = -(theta[0]/theta[1]) * x1 - (theta_0/theta[1])
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x1_vals = np.linspace(x1_min, x1_max, 100)
# 避免除零错误
if abs(theta[1]) > 1e-10:
x2_vals = -(theta[0]/theta[1]) * x1_vals - (theta_0/theta[1])
plt.plot(x1_vals, x2_vals, 'b-', linewidth=2, label='Decision Boundary')
else:
# 如果theta[1]接近0,边界几乎是垂直的
vert_line_x = -theta_0 / theta[0] if abs(theta[0]) > 1e-10 else 0
plt.axvline(x=vert_line_x, color='b', linewidth=2, label='Decision Boundary')
plt.xlabel('Feature 1 (x1)')
plt.ylabel('Feature 2 (x2)')
plt.title('Perceptron Decision Boundary')
plt.legend()
plt.grid(True, alpha=0.3)
plt.axis('equal')
plt.show()

# 绘制最终决策边界
plot_decision_boundary(theta, theta_0, X, y)


此外,我们可以编写一个预测函数,用于对新数据进行分类。
def perceptron_predict(X, theta, theta_0):
"""
使用训练好的感知机模型进行预测。
参数:
X : numpy数组,形状为 (n_samples, n_features),输入特征。
theta : numpy数组,学习到的权重向量。
theta_0 : 标量,学习到的偏置项。
返回:
predictions : numpy数组,形状为 (n_samples,),预测的类别标签 (+1 或 -1)。
"""
# 计算线性输出: θ^T * x + θ_0
linear_output = np.dot(X, theta) + theta_0
# 应用符号函数作为激活函数
predictions = np.sign(linear_output)
# 确保输出为+1或-1(np.sign(0)会返回0,我们将其映射为+1,但通常不会发生)
predictions[predictions == 0] = 1
return predictions
# 在训练数据上计算准确率
train_predictions = perceptron_predict(X, theta, theta_0)
accuracy = np.mean(train_predictions == y) * 100
print(f"Training Accuracy: {accuracy:.2f}%")

总结
本节课中我们一起学习了如何用Python从头开始实现感知机算法。我们回顾了算法的数学原理和步骤,并将其转化为具体的代码,包括数据准备、模型训练、决策边界可视化和预测。


通过本教程,你应当能够:
- 理解感知机算法的核心更新规则。
- 独立编写感知机的训练和预测代码。
- 可视化训练过程和最终的分类结果。
- 认识到感知机作为线性分类器的基本特性和局限性(例如,它无法处理线性不可分的数据)。

感知机是神经网络和许多现代机器学习模型的基石,掌握其实现为学习更复杂的模型奠定了坚实的基础。
012:感知机收敛定理
在本节课中,我们将学习感知机算法相关的几个核心术语。我们将探讨感知机过原点、数据集的线性可分性、数据集的间隔,并最终介绍感知机收敛定理。这些概念对于理解更高级的机器学习模型(如支持向量机)至关重要。
上一讲我们实现了感知机算法。本节中,我们将深入其理论基础。
感知机过原点
首先,我们介绍“感知机过原点”的概念。在之前的感知机算法中,分类线由公式 θᵀx + θ₀ = 0 表示,其中 θ₀ 是一个偏移量。
感知机过原点是指,我们强制分类线必须穿过坐标原点。这相当于在算法中设置 θ₀ = 0。此时,分类线简化为 θᵀx = 0。
以下是感知机过原点的算法步骤:
- 初始化参数向量 θ 为零向量。
- 循环遍历数据集多次(例如10次)。
- 对于每个数据点 (xᵢ, yᵢ),检查是否分类错误。判断条件是计算 yᵢ * (θᵀxᵢ)。
- 如果该值小于或等于0,表示分类错误,则更新参数:θ = θ + yᵢ * xᵢ。
- 由于 θ₀ 恒为0,因此无需更新。

感知机过原点是标准感知机的一个简化版本,常用于理论分析,因为它减少了需要处理的参数。
数据集的线性可分性

接下来,我们定义“线性可分性”。一个数据集 D 被称为是线性可分的,当且仅当存在一个线性分类器(一条直线或超平面),能够完美地将所有数据点正确分类。
用数学语言描述:数据集 D = {(xᵢ, yᵢ)} 是线性可分的,如果存在一个参数向量 θ,使得对于数据集中的所有点,都满足 yᵢ * (θᵀxᵢ) > 0。
回顾之前的内容,yᵢ * (θᵀxᵢ) 大于0意味着该点被正确分类。因此,线性可分的定义就是:存在一个分类器,它在整个数据集上不犯任何错误。
例如,下图展示了一个线性可分的数据集,因为我们可以画出一条直线,将所有的“加号”点和“圆圈”点分开。



数据集的间隔

现在,我们进入本节课最重要的概念:间隔。首先,我们定义单个数据点相对于一条分类线的“间隔”。

假设我们有一条过原点的分类线 θᵀx = 0,其法向量方向由 θ 指定。同时,我们有一个带标签的数据点 (x, y),其中 y 为 +1 或 -1。

该点相对于这条分类线的“间隔” γ 定义为:
γ = y * (θᵀx) / ||θ||



这个公式的几何意义是:计算该点到分类线的带符号距离。距离的绝对值表示点到线的远近,符号由分类是否正确决定(正确为正,错误为负)。
理解了点间隔后,我们可以定义整个“数据集的间隔”。数据集的间隔 Γ 是指,在所有能完美分类该数据集的线性分类器中,其所能达到的最小点间隔的最大值。
换句话说:
- 首先,考虑所有能将数据集线性分开的分类器 θ。
- 对于每一个这样的分类器 θ,计算数据集中所有点相对于它的间隔,并取其中最小的那个值(即最接近分类线的那个点的间隔)。
- 最后,在所有候选分类器中,找到那个能让这个“最小间隔”最大的分类器。这个最大的“最小间隔”就是数据集的间隔 Γ。
数据集的间隔是一个衡量数据集分离难易程度的重要指标。间隔越大,说明两类数据点分离得越开,理论上也越容易找到一个鲁棒的分类器。

感知机收敛定理
最后,我们介绍“感知机收敛定理”。这是一个非常重要的理论结果。
感知机收敛定理指出:如果一个数据集是线性可分的,并且其间隔 Γ > 0,那么感知机算法(包括过原点版本)将在有限次迭代内收敛到一个正确的分类器。
更具体地说,定理给出了算法在收敛前所需更新次数(即犯错次数)的一个上界。该上界与 R² / Γ² 成正比,其中 R 是数据集中所有点的最大范数(即离原点最远点的距离)。
定理公式化表述:对于线性可分数据集,感知机算法的权重更新次数不会超过 (R / Γ)² 次。
这个定理的意义在于,它从理论上保证了感知机算法在线性可分条件下的有效性和有限步收敛性。间隔 Γ 越大(数据越容易分),算法收敛得越快。
总结
本节课中,我们一起学习了感知机算法的四个核心理论概念:
- 感知机过原点:将分类线约束为穿过原点,简化了模型和理论分析。
- 线性可分性:描述了存在一个线性分类器能完美分离数据集的性质,条件是存在 θ 使得对所有点满足 yᵢ(θᵀxᵢ) > 0。
- 间隔:包括点间隔 γ = y(θᵀx)/||θ|| 和数据集间隔 Γ。数据集间隔是衡量分类难易程度的关键度量,值越大表示分类越容易。
- 感知机收敛定理:该定理保证了在线性可分条件下,感知机算法会在有限步内收敛,且收敛速度与数据集间隔 Γ 密切相关。
理解这些概念为后续学习更复杂的分类模型(如支持向量机)奠定了坚实的基础。
013:特征表示的魔力 ✨


在本节课中,我们将学习机器学习中一个非常核心且强大的概念:特征表示。我们将探讨如何通过变换数据,使其在另一个空间中变得线性可分,从而解决原本看似无法用线性分类器处理的问题。



概述

在之前的课程中,我们主要处理了线性可分的数据集,即可以用一条直线(或一个点)完美地将正类和负类分开。然而,现实世界中的数据往往并非如此简单。本节课,我们将从一个一维的、非线性可分的数据集例子出发,直观地理解特征表示如何通过数据变换,将复杂问题转化为线性可分问题。




从挑战开始:非线性可分数据

上一节我们介绍了线性分类器。本节中,我们来看看一个更具挑战性的场景:如何分类一个非线性可分的数据集?




我们从一个简单的一维数据集开始。在一维空间中,所有点都分布在一条直线上。假设我们有四个点,其坐标和标签如下:



- 点A:坐标 x=1,标签为 负类
- 点B:坐标 x=-1,标签为 负类
- 点C:坐标 x=2,标签为 正类
- 点D:坐标 x=-2,标签为 正类


在一维空间中,分类器就是一个“分界点”。分界点左侧的点被归为一类,右侧的点被归为另一类。然而,对于上述数据,无论我们选择哪个点作为分界点,都无法做到让所有正类点在一侧,所有负类点在另一侧。


因此,这个一维数据集是非线性可分的。这意味着,我们无法找到一个点来正确地区分正类和负类。




引入魔法:特征表示

既然机器学习无法直接解决这个简单问题,那它的价值何在?关键在于,机器学习可以通过特征表示来施展“魔法”。
特征表示的核心思想非常简单:变换数据。具体来说,就是将原始数据映射(变换)到一个新的空间(特征空间)中,使得数据在这个新空间里变得线性可分。


用数学公式表示,如果我们用 φ(x) 表示特征变换函数,其中 x 是原始数据点,那么变换就是将 x 映射到新的特征向量。
实践变换:从一维到二维

让我们将上述思想应用到我们的例子中。我们将把一维数据 x 变换到二维空间。我们选择的变换函数是:
φ(x) = [x, x²]

也就是说,新空间的两个维度分别是原始值 x 和它的平方 x²。

现在,让我们计算每个点在新空间中的坐标:

以下是四个点变换后的结果:
- 点A (1, 负类) ->
φ(1) = [1, 1²] = [1, 1] - 点B (-1, 负类) ->
φ(-1) = [-1, (-1)²] = [-1, 1] - 点C (2, 正类) ->
φ(2) = [2, 2²] = [2, 4] - 点D (-2, 正类) ->
φ(-2) = [-2, (-2)²] = [-2, 4]





可视化魔力:新空间中的线性可分性
现在,我们在二维平面(横轴为 x,纵轴为 x²)上绘制这些变换后的点:
- 负类点:A‘ (1,1) 和 B’ (-1,1)
- 正类点:C‘ (2,4) 和 D’ (-2,4)



神奇的事情发生了!在二维空间中,这四个点可以被一条水平的直线完美分开。例如,直线 y = 2.5 就可以将位于其上方的正类点 (2,4) 和 (-2,4) 与位于其下方的负类点 (1,1) 和 (-1,1) 完全区分开。


通过一个简单的特征变换 φ(x) = [x, x²],我们将一个在一维空间中非线性可分的问题,转化为了在二维空间中线性可分的问题。这就是特征表示的威力。




总结

本节课中,我们一起学习了特征表示的核心概念:
- 问题:原始数据(如一维点集)可能不是线性可分的,导致线性分类器失效。
- 解决方案:应用特征表示,将数据从原始空间变换到一个新的特征空间。
- 关键:精心设计的变换(如
φ(x) = [x, x²])可以使数据在新空间中变得线性可分。 - 结果:我们可以在变换后的高维空间中,轻松地找到一个线性分类器(如一条直线)来解决原始空间的复杂分类问题。



特征表示是许多强大机器学习算法(如支持向量机、神经网络)的基础。理解这一概念,是掌握如何让模型处理复杂非线性模式的重要一步。
014:独热编码 🔥



在本节课中,我们将要学习特征表示策略,特别是独热编码。我们将探讨为什么需要编码,以及如何将现实世界中的离散数据(尤其是分类数据)转换为机器学习算法可以理解的格式。

上一节我们介绍了特征表示的概念,看到了如何通过将数据点从低维空间转换到高维空间,使原本线性不可分的数据变得线性可分。本节中,我们来看看一种专门用于处理分类数据的特征表示策略。



特征表示策略


机器学习算法需要数值输入。然而,现实世界的数据来源多种多样,并非总是数值形式。因此,我们需要一套策略,将原始数据“编码”成适合算法处理的格式。这就是特征表示策略。

以下是处理不同类型数据时常见的策略:




- 数值数据:可以直接使用,无需特殊编码。
- 分类数据:需要特殊处理,不能直接赋予数值标签。独热编码是处理此类数据最常用的策略之一。



为什么需要编码?


你可能会问,既然我们已经有了数据,为什么不直接把它传给学习算法呢?让我们通过例子来回答这个问题。




假设我们有两类离散数据:
- 汽车气缸数:例如 1, 2, 3 等。这些是数值。
- 手机制造商:例如 苹果, 三星, 一加, 诺基亚。这些是类别。



现在的问题是:如何将这些数据作为输入传递给机器学习算法(如感知机或分类器)?


方案一:将数据视为数值属性
这是最直接的想法。对于气缸数,我们可以直接使用原始数值,这没有问题。但对于手机制造商,我们可能会尝试为每个类别分配一个数字,例如:
- 苹果 = 1
- 三星 = 2
- 一加 = 3
- 诺基亚 = 4


然而,这是一个非常糟糕的主意。原因在于,手机制造商是分类数据,类别之间没有数值上的大小或顺序关系。如果我们编码为数字,算法可能会错误地认为“三星(2)大于苹果(1)”,或者“三星与苹果的差值(1)等于一加与诺基亚的差值(1)”,从而引入不存在的数值关系,混淆算法。对于性别(男/女)等其他分类数据也是如此。

因此,方案一不适用于分类数据。



解决方案:独热编码

现在让我们看看方案二:独热编码。这是处理分类数据的标准方法。


独热编码的原理是:如果一个离散特征(如手机制造商)有 M 个可能的取值,那么我们就用一个长度为 M 的二进制向量来表示它。在这个向量中,只有一个位置是“热”的(值为1),其余位置都是“冷”的(值为0)。




具体来说:
- 第一个类别(如苹果)表示为:
[1, 0, 0, 0] - 第二个类别(如三星)表示为:
[0, 1, 0, 0] - 第三个类别(如一加)表示为:
[0, 0, 1, 0] - 第四个类别(如诺基亚)表示为:
[0, 0, 0, 1]




这样,每个类别都被表示为一个独立的、正交的向量。当机器学习算法接收到这样的输入时,它知道只有对应类别的那个维度是“激活”的,而不会错误地推断出类别之间存在任何数值关系。




总结




本节课中我们一起学习了特征表示中的独热编码策略。我们了解到,对于像手机品牌、性别这样的分类数据,不能简单地用数字(1,2,3...)来编码,因为这会给算法引入错误的数值关系。正确的做法是使用独热编码,将每个类别表示为一个唯一的二进制向量(如 [1,0,0,0]),从而确保算法能正确理解数据的类别属性。这是机器学习数据预处理中至关重要且常用的一步。
015:逻辑回归第一部分 🧠

在本节课中,我们将学习如何将机器学习问题转化为优化问题,并初步认识逻辑回归及其核心组件——Sigmoid函数。我们将从优化框架开始,逐步理解逻辑回归的基本概念。
将机器学习视为优化问题 🔄


上一节我们介绍了感知机等算法,它们源于人类的直觉。但面对复杂的机器学习问题时,我们无法每次都依赖直觉来设计新算法。我们需要一套系统化的方法。


我们需要一种能够为任何问题推导出机器学习算法的机制或方法论。这就是为什么机器学习实践者会将机器学习问题转化为优化问题。
优化是一个发展迅速的数学领域,其中包含许多成熟的技术。如果我们将机器学习问题视为优化问题,就可以利用这些技术来解决它。

什么是优化问题?🎯

本质上,优化意味着我们希望优化某个函数。例如,预订网约车时,算法会根据我的位置、附近司机的距离、司机评分以及我的支付意愿,为我分配最佳司机。这就是一个优化问题,Uber试图优化的函数是“为我分配最佳司机”。

在日常生活中,当我们想从A地到B地时,我们可能会选择耗时最短的路线,这也是一个优化问题,我们在此优化的是时间。

在任何优化问题中,都有一个我们想要优化的函数,称为 J,它依赖于参数 θ。

在机器学习的语境下:
- J 是我们想要最小化的目标函数(通常是损失函数)。
- θ 是模型的参数。



例如,对于一个线性分类器 θ₁x₁ + θ₂x₂ + θ₀ = 0,其参数就是 θ₁、θ₂ 和 θ₀。

因此,优化问题的本质是:找到能够最小化 J 的参数 θ*。



机器学习中的目标函数 📉
你可能会想,这个目标函数具体是什么样子?对于机器学习问题,目标函数通常具有以下形式:
首先,目标函数 J(θ) 总是训练数据上的损失之和。我们希望最小化训练数据上的损失。
其次,还有一个额外的项,称为正则化器。我们稍后会详细了解它,但正则化器实际上有助于我们最小化或减少测试数据上的损失。
正如我们之前所见,仅仅确保训练数据上的损失最小化是不够的。我们需要确保当给出任何新数据点时,我们的机器学习算法在新数据点上的错误也很少。为此,我们使用了一种称为正则化的技术。



用数学公式表示,目标函数如下:
J(θ) = (1/n) * Σᵢ₌₁ⁿ L(h(x⁽ⁱ⁾), y⁽ⁱ⁾) + λ * R(θ)
以下是公式各部分的解释:



(1/n) * Σᵢ₌₁ⁿ L(h(x⁽ⁱ⁾), y⁽ⁱ⁾):这部分是经验风险或损失项。n是数据点的数量。L是损失函数,用于衡量预测值与真实值之间的差异。h(x⁽ⁱ⁾)是我们的假设(模型)对第i个样本的预测。y⁽ⁱ⁾是第i个样本的真实值。- 本质上,我们取假设的预测值,计算预测值与真实值之间的误差(即损失函数),然后对所有训练数据的损失函数取平均。


λ * R(θ):这部分是正则化项。λ是一个超参数,用于控制正则化的强度。R(θ)是正则化函数。

你可以将正则化器视为对我们的参数 θ 施加一种惩罚。这样做的目的是告诉算法:不要试图完美或过度精确地拟合数据,因为这可能导致过拟合问题。或者说,尝试保持最优解的简洁性,不要试图过拟合。

麻省理工学院的一个可视化示例很好地总结了这一点。观察一组数据,其中黑点是正类数据,空心点是负类数据。比较两个假设 h1 和 h2。h1 完美地穿过了所有点并分隔了数据,但它可能非常复杂,对噪声敏感,容易过拟合。而 h2 虽然可能没有完美分类所有训练点,但它更平滑、更简单,可能对未见数据具有更好的泛化能力。正则化的目标就是鼓励模型选择像 h2 这样更简单的解。

逻辑回归简介 ➕➖

现在,让我们将注意力转向本节课的核心主题之一:逻辑回归。



逻辑回归是一种广泛使用的机器学习算法,主要用于分类任务,尤其是二分类问题(例如,将邮件分类为垃圾邮件或非垃圾邮件,判断交易是否为欺诈等)。


尽管名字中有“回归”,但逻辑回归解决的是分类问题。它的核心思想是:不像线性回归那样直接输出一个连续值,逻辑回归希望输出一个介于0和1之间的概率值,表示某个样本属于正类的可能性。

Sigmoid函数:逻辑回归的核心 🧮
那么,逻辑回归如何将线性模型的输出(一个任意实数)转换为一个概率值呢?答案就是通过 Sigmoid函数(或称逻辑函数)。




Sigmoid函数是逻辑回归中至关重要的组成部分,事实上,它在许多现代机器学习算法中也经常被使用。本节课也将作为对Sigmoid函数含义的一个非常好的直观介绍。

Sigmoid函数的数学定义如下:


σ(z) = 1 / (1 + e⁻ᶻ)
其中,z 通常是线性组合,例如 z = θᵀx = θ₀ + θ₁x₁ + θ₂x₂ + ...。

这个函数有什么特别之处?
- 输出范围:无论输入
z是任何实数,Sigmoid函数的输出始终被压缩在 (0, 1) 区间内。这使得它非常适合解释为概率。 - 形状:它的图形是一条平滑的“S”形曲线。
- 当
z趋向于正无穷大时,σ(z)趋近于 1。 - 当
z趋向于负无穷大时,σ(z)趋近于 0。 - 当
z = 0时,σ(z) = 0.5。
- 当


在逻辑回归中,我们将线性组合 z = θᵀx 代入Sigmoid函数,得到:

h(x) = σ(θᵀx) = 1 / (1 + e⁻θᵀˣ)


这个 h(x) 就被解释为“给定输入特征 x,其标签 y=1(属于正类)的概率”,即 P(y=1|x; θ)。

总结 📚
本节课我们一起学习了三个关键内容:


- 机器学习作为优化:我们探讨了将机器学习问题转化为优化问题的必要性。优化提供了一个系统化的框架,通过最小化一个包含损失项和正则化项的目标函数
J(θ)来求解模型参数θ。 - 逻辑回归简介:我们认识了逻辑回归,它是一种用于二分类问题的强大算法,其目标是预测一个样本属于某个类别的概率。
- Sigmoid函数:我们深入了解了Sigmoid函数,它是逻辑回归的核心。该函数将线性模型的输出映射到(0,1)区间,从而可以解释为概率。其公式为
σ(z) = 1 / (1 + e⁻ᶻ)。

理解优化框架是掌握许多现代机器学习算法的基础,而逻辑回归及其Sigmoid函数是分类任务中的一个经典且重要的起点。在接下来的课程中,我们将继续探讨逻辑回归的损失函数以及如何对其进行优化。
016:交叉熵损失函数
在本节课中,我们将学习逻辑回归的第二个核心部分:损失函数。我们将重点介绍交叉熵损失函数,理解其数学形式、直观含义以及为何它适用于逻辑回归模型。
上一节我们介绍了逻辑回归的假设函数——Sigmoid函数。本节中,我们来看看如何衡量这个假设函数的预测效果,即定义其损失函数。
问题回顾与目标设定
首先,我们简要回顾一下逻辑回归要解决的问题。假设我们有一个一维数据集,包含四个数据点。
- 点1和点2属于正类,其真实标签
y = 1。 - 点3和点4属于负类,其真实标签
y = 0。

我们的目标是找到一个线性分类器,能够完美地将正类点与负类点分开。这个分类器在一维空间中就是一个点(决策边界)。
我们将使用上一节学习的Sigmoid函数作为我们的假设函数。

Sigmoid假设函数回顾
Sigmoid函数的数学公式如下:

公式:σ(z) = 1 / (1 + e^(-z))


其中,z = θ^T * x + θ₀。θ 和 θ₀ 是描述我们分类器的参数。

Sigmoid函数将任何实数输入 z 映射到 (0, 1) 区间。其函数图像呈S形。


我们的分类规则基于 z 的值:
- 如果
z = θ^T * x + θ₀ > 0,则预测值ŷ接近1(分类为正类)。 - 如果
z = θ^T * x + θ₀ < 0,则预测值ŷ接近0(分类为负类)。
Sigmoid函数的一个重要直觉是它与概率密切相关:
- 当
σ(z)接近1时,表示模型确信该样本属于正类的概率很高。 - 当
σ(z)接近0时,表示模型确信该样本属于负类的概率很高。


为何需要新的损失函数?


既然我们已经有了假设函数,为什么不能沿用线性回归中的均方误差(MSE)损失函数呢?
以下是原因:
- 非凸优化问题:在逻辑回归中,如果使用MSE损失函数,其代价函数会变成非凸函数。这意味着优化过程中可能会陷入局部最优解,而无法找到全局最优的参数。
- 与概率解释不匹配:Sigmoid输出的是概率。我们需要一个损失函数,能够有效地衡量预测概率分布与真实标签分布之间的差异。
因此,我们需要一个专为分类问题设计的损失函数。
交叉熵损失函数
交叉熵损失函数完美地解决了上述问题。它源于信息论,用于衡量两个概率分布之间的差异。

直观理解
想象一个猫狗分类模型。对于一个狗的图像:
- 真实分布:狗的概率为1,猫的概率为0。
- 模型预测分布:可能输出狗的概率为0.9,猫的概率为0.1。
交叉熵损失就是衡量这两个分布(真实 vs. 预测)之间的“距离”。预测越准确,这个“距离”就越小,损失值也越低。

数学定义

对于一个单独的训练样本 (x, y),交叉熵损失函数定义如下:

公式:Loss = - [ y * log(ŷ) + (1 - y) * log(1 - ŷ) ]
其中:
y是样本的真实标签(0或1)。ŷ是模型预测该样本为正类的概率,即ŷ = σ(θ^T * x + θ₀)。
让我们分析这个公式为何有效:

情况一:真实标签 y = 1
- 损失函数简化为:
Loss = - log(ŷ) - 如果模型预测正确(
ŷ接近1),则log(ŷ)接近0,损失很小。 - 如果模型预测错误(
ŷ接近0),则log(ŷ)会是一个很大的负数,加上负号后,损失会变得非常大。这严厉地惩罚了错误预测。
情况二:真实标签 y = 0
- 损失函数简化为:
Loss = - log(1 - ŷ) - 如果模型预测正确(
ŷ接近0),则1-ŷ接近1,log(1-ŷ)接近0,损失很小。 - 如果模型预测错误(
ŷ接近1),则1-ŷ接近0,log(1-ŷ)会是一个很大的负数,导致损失非常大。
应用于我们的例子
对于我们的四个数据点,总损失 J(θ) 是所有单个样本损失的平均值:
公式:J(θ) = -(1/m) * Σ [ y⁽ⁱ⁾ * log(ŷ⁽ⁱ⁾) + (1 - y⁽ⁱ⁾) * log(1 - ŷ⁽ⁱ⁾) ]

其中 m=4 是样本数量。


我们的目标就是找到一组参数 θ 和 θ₀,使得这个总损失 J(θ) 最小化。

总结


本节课中我们一起学习了逻辑回归的核心组成部分——交叉熵损失函数。
- 我们首先回顾了Sigmoid假设函数及其概率解释。
- 接着,我们理解了为何均方误差(MSE)不适用于逻辑回归,因为它会导致非凸的优化问题。
- 然后,我们深入介绍了交叉熵损失函数。我们从直观上理解了它如何衡量预测概率与真实分布之间的差异。
- 最后,我们给出了交叉熵损失函数的数学定义,并分析了它如何对正确预测给予小奖励,对错误预测给予大惩罚。


下一节,我们将探讨如何通过优化算法(如梯度下降)来最小化这个交叉熵损失函数,从而找到最佳的模型参数 θ 和 θ₀。
017:梯度下降算法 🧠






在本节课中,我们将学习机器学习中最重要的算法之一:梯度下降。我们将从直观理解开始,逐步深入到数学公式和代码实现,确保你彻底掌握其工作原理。




概述


在前面的课程中,我们定义了逻辑回归的假设函数(Sigmoid函数)和损失函数(交叉熵损失)。本节课的核心目标是探索如何找到那个能使损失函数最小化的参数 θ。本质上,我们将机器学习问题转化为了一个优化问题,而梯度下降正是解决此类问题的关键算法。




梯度下降的直观理解


与直接介绍数学公式不同,我们首先从视觉上理解这个算法为何有效,以及其背后的动机和直觉。





一个贯穿本节课的核心概念是:一个函数的梯度指明了函数值上升最陡的方向。这个直觉适用于任意维度,不仅是一维或二维。


为了展示梯度下降的直观性,我们将通过实际演示来说明。首先看一维情况,再扩展到二维。



一维示例:寻找最小值



假设我们有一个一维的损失函数:f(x) = x²。我们的目标是找到使 f(x) 最小的 x 值。



我们知道这个函数的图像是一个抛物线。但假设我们完全不知道这个图像长什么样,就像世界上还没有梯度下降算法时一样。我们只有这个函数,并且要找到它的最小值。你会怎么做?






以下是解决这个问题的步骤:



- 随机猜测一个起点:这是很自然的开始。例如,我们随机选择 x₁ = 9。此时,我们只知道 f(9) = 81。
- 决定移动方向:我们想找到一个能让 f(x) 值比81更小的新点。我们不能无休止地随机猜测。这时,我们回想起梯度的直觉:梯度方向是上升最快的方向。在一维中,梯度就是斜率。因此,梯度的反方向就是下降最快的方向。我们可以计算在当前点 x=9 处的导数(梯度)来指导我们。






想象你被蒙住眼睛站在一个一维的山坡上,你想走到最低点。你试探脚下的坡度,如果向左走是下坡,你就向左;如果向右走是下坡,你就向右。梯度下降算法正是将这种直觉数学化了。




通过这种方式,我们从一个随机点出发,沿着使函数值下降最快的方向(即负梯度方向)一步步移动,最终有望找到函数的最小值点。




在下一节中,我们将把这种直观理解形式化为数学算法,并看到它在更高维度上的威力。



总结


本节课我们一起学习了梯度下降算法的核心思想。我们了解到,梯度下降通过迭代地沿着损失函数梯度的反方向更新参数,来寻找函数的最小值点。我们从一维的直观例子出发,理解了“沿着最陡下降方向前进”这一基本原则,这为后续学习其数学形式和代码实现打下了坚实的基础。
018:从零实现逻辑回归 🧠

在本节课中,我们将把之前学到的所有知识模块组合起来,从头开始完整地实现逻辑回归算法。我们将学习其背后的数学原理,并了解如何用代码实现它。掌握这些内容对于应对机器学习面试中的常见问题至关重要。

概述 📋
在之前的课程中,我们学习了如何定义Sigmoid假设函数、交叉熵损失函数,以及梯度下降法的工作原理。现在,我们拥有了所有必要的工具,可以“从零开始”完整地实现逻辑回归。
当参加机器学习面试时,面试官常常会要求你从头实现逻辑回归。许多候选人因为对逻辑回归的基础原理不清晰而无法完成。他们可能只知道如何使用Python的scikit-learn包中的现成命令,但不了解其背后的实现和数学原理。本节课,我们将同时学习数学原理和代码实现。
本节课假设你具备基础的微积分和线性代数知识。在涉及相关部分时,我会进行提示。
问题定义 🎯


对于第一次加入学习的同学,我们先明确手头的问题。

假设我们在X轴上测量动物的胡须长度,在Y轴上测量其耳朵下垂指数。我们有两种动物:狗和猫。
- 对于狗,它们的耳朵下垂指数通常较高,而胡须长度较低。
- 对于猫,它们的数据点则分布在另一区域。


逻辑回归的目标是找到一个最佳的线性分类器,将这两类数据集分开。
这个线性分类器由三个变量定义:θ₁、θ₂ 和 θ₀。其形式为:θ₁x₁ + θ₂x₂ + θ₀。其中,x₁ 是胡须长度,x₂ 是耳朵下垂指数。


在线性代数中,这通常写作 θᵀx + θ₀,其中 θᵀ 是 [θ₁, θ₂],x 是 [x₁, x₂]。
因此,最终目标是找到最优的 θ₁、θ₂ 和 θ₀,以最好地区分这两类数据。



优化方法 🔄

之前我们尝试过感知机方法和随机线性分类器方法。但对于逻辑回归以及后续学习的大多数机器学习算法,我们使用一种称为“最大似然估计”的优化方法。
本质上,我们将采用一种优化方法:定义一个损失函数,然后尝试找到能最小化该损失函数的参数 θ₁、θ₂ 和 θ₀。


定义目标函数 📉


设目标函数为 J(θ),其定义为:
J(θ) = (1/n) * Σ L(h_θ(xⁱ), yⁱ)

其中:
n是数据点的数量。L是损失函数,衡量预测值(猜测)与实际值之间的差异。h_θ(x)是我们的假设函数做出的预测。

在高级机器学习算法中,我们通常还会加入正则化项。但为了简化,本教程暂不考虑正则化项,只关注损失函数部分。

因此,我们的目标是找到那个特定的 θ*,它能最小化 J(θ),也就是最小化这个损失函数。
我们的猜测由假设函数给出。我们使用Sigmoid假设函数,也称为逻辑函数。
核心函数回顾 📚


现在,让我们回顾一下之前课程中定义的两个核心函数。


1. 损失函数 L

对于逻辑回归,我们使用交叉熵损失函数。其定义如下:

L(h_θ(x), y) = - [y * log(h_θ(x)) + (1 - y) * log(1 - h_θ(x))]

2. 假设函数 h_θ

h_θ(x) 是Sigmoid函数,定义如下:


h_θ(x) = σ(θᵀx) = 1 / (1 + e^(-θᵀx))

为了简化接下来的导数计算,我暂时不显式包含 θ₀ 项(实际上它已包含在 θᵀx 的扩展形式中,即 θ₀ + θ₁x₁ + θ₂x₂)。在本讲座的动手演示部分(第二部分),我将展示如何处理 θ₀。



组合目标函数 🧩
现在,我们将损失函数 L 和假设函数 h_θ 代入 J(θ),看看这个函数具体是什么样子。


代入后,J(θ) 如下所示:
J(θ) = - (1/n) * Σ [ yⁱ * log(σ(θᵀxⁱ)) + (1 - yⁱ) * log(1 - σ(θᵀxⁱ)) ]


这就是我们需要最小化的目标函数(或损失函数)。我所做的只是将交叉熵损失和Sigmoid假设函数代入其中。


至此,我们完成了第一步:将逻辑回归问题转化为一个优化问题。我们希望找到最小化 J(θ) 的 θ*。

可以看到,我们现在不再将其视为纯粹的机器学习问题,而是一个数学优化问题。这就是为什么人们常说,要精通机器学习,必须精通优化和统计学。



实施梯度下降 ⬇️


现在让我们来看第二步,我们已经为此准备好了工具:实施梯度下降。

回顾一下梯度下降法:它通过迭代更新参数 θ 的值。算法从一个初始的 θ(我们通常随机初始化)开始,然后根据步长(学习率)和函数在该点的梯度来更新它。

更新规则为:θ_new = θ_old - α * ∇J(θ_old)
其中:
α是步长(学习率)。∇J(θ)是目标函数J在θ处的梯度。
因此,为了实施梯度下降,我们需要计算目标函数 J(θ) 的梯度。


总结 ✨
本节课中,我们一起学习了逻辑回归从理论到实现的关键步骤:


- 问题定义:明确了逻辑回归的目标是找到最佳线性分类器。
- 方法选择:确定使用基于最大似然估计的优化方法。
- 函数定义:回顾了交叉熵损失函数和Sigmoid假设函数。
- 目标构建:将逻辑回归问题形式化为最小化目标函数
J(θ)的优化问题。 - 解决方案:指出将通过梯度下降法来求解最优参数
θ*,并明确了下一步需要计算目标函数的梯度。

在接下来的部分,我们将深入推导梯度公式,并最终用代码实现整个逻辑回归算法。
019:正则化入门 🧠


在本节课中,我们将学习机器学习中一个最重要的概念——正则化。正则化是任何优秀机器学习课程都会涵盖的内容,其核心目标是提升模型在未见数据上的表现能力,即泛化能力。


上一节我们完整地从零实现了逻辑分类器,但并未涉及正则化。本节我们将重点探讨这个概念。




理解训练与测试数据

在深入正则化之前,首先需要理解任何机器学习算法都依赖数据。数据通常被分为两种类型:
- 训练数据:占数据的大部分,用于训练模型。
- 测试数据:占数据的少部分,用于评估模型在未知数据上的表现。

以下是一个简单的例子来说明这两个概念:
- 假设我们要构建一个区分猫和狗的分类器。
- 我们上传10张猫的图片和10张狗的图片作为训练数据。
- 基于这些训练数据,我们训练一个机器学习模型。
- 模型训练完成后,我们上传它从未见过的图片(例如一张狗的侧视图)作为测试数据。
- 如果模型能准确预测这些新图片,说明它具有良好的泛化能力。这正是所有机器学习模型的主要目标:不仅在训练数据上表现良好,更要在未见过的数据上表现良好。



正则化的主要目的就是为了实现这个目标。


正则化的数学形式


在逻辑回归中,我们将机器学习视为优化问题,目标是最小化一个目标函数。之前我们只关注了损失函数部分,但实际上完整的目标函数包含两项:




目标函数公式:
J(θ) = L(θ) + λ * R(θ)

其中:
L(θ)是损失函数,衡量模型预测与真实值之间的差异。对于逻辑回归,这通常是交叉熵损失。R(θ)是正则化项。λ是一个超参数,用于控制正则化的强度。



我们的目标是最小化整个目标函数 J(θ)。


正则化项的含义

以二维情况为例,假设我们的决策边界是直线:θ₁x₁ + θ₂x₂ + θ₀ = 0。一种常见的正则化项(L2正则化)形式为:


正则化项公式(二维):
R(θ) = (θ₁² + θ₂²) / n

其中 n 是数据点的数量。


由于我们需要最小化 J(θ),而 J(θ) 是损失 L(θ) 和正则项 λ * R(θ) 的和,这意味着我们同时也希望最小化正则化项 R(θ)。在二维例子中,即我们希望 θ₁ 和 θ₂ 的值尽可能小。


核心问题随之而来:为什么我们希望模型的参数(系数)变小?鼓励模型参数保持较小值背后的直觉是什么?

本节课我们一起学习了正则化的基本概念,理解了其目标是提升模型泛化能力,并初步认识了其在目标函数中的数学形式。下一节,我们将深入探讨“为什么需要小参数”这个核心问题,从而真正理解正则化的直觉。
020:在Python中为逻辑回归实现正则化 🧠




在本节课中,我们将学习如何将正则化概念应用于一个复杂的数据集,并使用Python进行实际实现。我们将通过一个具体的例子,来理解特征选择、特征表示以及正则化参数λ的选择。






概述


上一节我们介绍了正则化的基本概念和其防止过拟合的直观原理。本节中,我们将动手实践,在一个非线性可分的数据集上,为逻辑回归模型实现正则化。我们将学习如何使用多项式特征变换,并比较使用正则化与不使用正则化时模型的性能差异。



数据集介绍

我们使用的数据集在二维空间中呈现两个类别。第一类数据点分布在一个较小的内圈中,第二类数据点分布在一个半径更大的外圈中。


在Excel表格中,前58行数据标记为类别1,第58行到第118行数据标记为类别0。数据在视觉上呈现为同心圆分布。



问题定义与初步分析

假设你是一名机器学习工程师,任务是为上述数据集生成一个分类器。


首先,你应该注意到,这个数据集的决策边界不是一条直线。它看起来像一条曲线,可能近似于一个圆。这意味着我们之前使用的线性决策边界公式 θ₁x₁ + θ₂x₂ + θ₀ = 0 在这里不适用。


这个结论立刻引出了下一个关键点:我们需要使用特征表示技巧。


特征表示技巧


对于在二维空间中线性不可分的数据,我们可以将其投影到更高维的空间中,并尝试在那里找到一个线性分隔器。这就是特征表示技巧的核心思想。



如果我们不将数据投影到高维空间,只使用一阶特征(即 1, x₁, x₂),那么我们只能构造出直线型的分类器,这显然无法有效分隔当前数据。


因此,我们不应局限于使用一阶特征,而应考虑更高阶的特征。
- 二阶特征:这意味着将数据投影到二阶多项式空间。此时,我们的特征不仅包括
1,x₁,x₂,还会增加x₁x₂,x₁²和x₂²。- 由于加入了平方项,决策方程会变得更复杂,例如:
θ₁x₁ + θ₂x₂ + θ₃x₁² + θ₄x₁x₂ + ... = 0。这个方程可以描述抛物线或圆形等更复杂的形状。从直观上看,我们的数据呈圆形分布,方程x₁² + x₂² = r²正好描述了一个圆,因此我们肯定需要包含x₁²和x₂²项,即至少需要用到二阶特征。
- 由于加入了平方项,决策方程会变得更复杂,例如:
- 更高阶特征:随着阶数增加,特征项的数量会呈指数级增长。
- 例如,三阶特征还会包含
x₁²x₂,x₁x₂²,x₁³,x₂³等项。 - 在一些复杂的机器学习算法中,我们甚至可能使用到20阶的多项式。机器学习可以被看作是将数据从低维空间转换到高维空间的过程(神经网络也是如此),只是作为人类,我们难以想象三维以上的空间。
- 例如,三阶特征还会包含




所以,对于这个数据集的第二个重要结论是:我们必须使用特征表示技巧(这里特指多项式特征基)来转换我们的数据。


正则化的作用


正如上一节所学,在正则化中,我们的目标函数发生了变化。它增加了一个惩罚项,以防止模型参数变得过大,从而控制模型复杂度,避免过拟合。



接下来,让我们进入代码实践部分,看看如何具体实现这些步骤。

以下是实现的主要步骤:





- 数据加载与可视化:首先,我们将加载数据集并绘制散点图,直观地观察其分布。
- 特征工程:使用
PolynomialFeatures生成不同阶数(如1阶、2阶、5阶、10阶)的多项式特征。 - 模型训练(无正则化):在不同阶数的特征上训练逻辑回归模型(不设置正则化),并观察其在训练集和测试集上的表现。我们会发现,随着特征阶数升高,模型在训练集上表现完美,但在测试集上表现变差,这就是过拟合。
- 模型训练(带正则化):使用相同的特征,但在逻辑回归模型中引入L2正则化(通过设置参数
C,注意C是正则化强度的倒数,C越小,正则化越强)。我们将尝试不同的C值,观察正则化如何抑制过拟合,提升模型在测试集上的泛化能力。 - 决策边界可视化:对于表现最好的模型,我们将绘制其决策边界,直观地展示分类效果。

核心概念与代码


在本教程的代码实现中,以下几个核心概念将通过公式或代码来重点描述:



- 多项式特征生成:使用
sklearn.preprocessing.PolynomialFeatures。 - 逻辑回归模型:使用
sklearn.linear_model.LogisticRegression。 - 正则化强度:在
LogisticRegression中,参数C表示正则化强度的倒数,penalty=‘l2‘指定使用L2正则化。损失函数可以理解为:
J(θ) = 原始损失函数 + λ * Σ(θᵢ²)
其中λ与C的关系大致为λ ∝ 1/C。


总结

本节课中,我们一起学习了如何为一个非线性可分的同心圆数据集构建分类器。我们回顾了特征表示(特别是多项式特征)的重要性,并亲自动手实现了带正则化与不带正则化的逻辑回归模型。



通过实践,我们观察到:
- 低阶特征无法拟合复杂数据。
- 高阶特征虽然能完美拟合训练数据,但容易导致过拟合,在测试集上表现不佳。
- 引入L2正则化可以有效地控制模型复杂度,惩罚过大的参数,从而在保持模型对训练数据拟合能力的同时,显著提升其在新数据上的泛化性能。



这巩固了我们对正则化直观理解和实际应用的认识。
021:线性回归入门 🎯
在本节课中,我们将要学习机器学习中另一个非常流行的技术——回归分析。我们将首先从概念上理解线性回归、它的应用以及它为何如此受欢迎。然后,我们会将回归问题构建为一个机器学习问题框架。
回归的核心概念 📈


上一节我们介绍了本节课的概述,本节中我们来看看回归的核心思想。
回归的主要思想与分类类似,我们拥有类似的数据集。数据集中包含输入和输出对,即 x 和 y 对。其中,x_i 是输入,它可以存在于任何维度的空间(一维、二维或更高维),而 y_i 是输出。


回归与分类的关键区别在于,在回归中,输出值 y 取连续值。而在分类中,输出值总是取离散值,例如 +1、-1 或 0、1 等。

另一个非常重要的点是,与分类算法中的“分类”不同,在回归任务中我们将反复使用的核心词是“预测”。预测是回归方法的主要功能和关键所在,这也是它们如此受欢迎的原因。



回归的应用场景 🌍

上一节我们了解了回归的核心是预测,本节中我们来看看它的实际应用。

预测任务在我们周围无处不在。以下是回归的一些典型应用场景:


- 天气预报:需要预测未来几天的天气状况。
- 股票价格预测:预测股票是会上涨还是下跌。
- 需求预测:在像 Zomato、Swiggy 这样的配送应用中,需要预测客户需求,以便相应地分配资源。供应链行业中的需求预测是一个关键任务。
事实上,在各个领域,机器学习都使我们能够预测结果,这正是回归如此流行的原因。


一个直观的案例研究 🧑🏫


在将回归正式构建为机器学习框架之前,我想通过一个案例研究,从概念上解释回归思想的来源。
这个案例基于我们访问学校、向教师教授机器学习的真实经历。一所学校希望我们建立一个机器学习模型,能够根据他们提供的一些参数来预测学生的分数。


我们将这个问题简化为一个非常简单的案例呈现给教师们,并希望了解他们的反应。这也是理解回归动机的好方法。


问题如下:想象你有一批学生,正在收集他们的分数。

- 首先,假设你只有一名学生的数据:他学习了 4 小时,获得了 33 分(如图中点所示)。
- 问题:现在想象有另一名学生学习了 7 小时,你能预测他/她的分数吗?(未提供其他信息)
- 结果显示,学习了 7 小时的学生实际获得了 58 分(图中第二个点)。
- 新问题:考虑第三名学生,他学习了 8 小时,现在你有两个数据点,能预测他的分数吗?
- 第三名学习了 8 小时的学生实际获得了 67 分。
- 接着,向教师们展示了五个数据点(学习了 4、7、8、5、9 小时的学生及其分数)。
- 新问题:考虑第六名学生,他学习了 10 小时,你能预测他的分数吗?
- 第六名学习了 10 小时的学生实际获得了 83 分。
- 最后问题:现在假设你拥有六名学生的数据点,考虑第七名学习了 12 小时的学生,你能预测他的分数吗?
- 第七名学生实际获得了 100 分。


观察到的现象:随着提供给教师的训练数据点越来越多,教师预测的准确性也随之提高。当只有一两个点时,预测范围很广(如 50-90 分);当有五个点时,预测范围更集中(82-90 分);当有六个点时,预测值非常接近且准确(96-99 分,实际为 100 分)。


案例的启示:
- 这说明了训练数据对于任何机器学习模型的重要性。
- 它阐明了一个事实:当我们看到这些数据点时,我们的大脑会在潜意识中构建一个模型。我们认为这些点大致排列在一条直线上,因此存在一条穿过这些点的直线。这样,给定任何新的输入(学习时长),我们就能预测出分数。
这正是线性回归所做的事情。

构建回归问题框架 ⚙️


上一节我们通过案例直观感受了回归,本节中我们将正式把回归构建为一个机器学习问题。



线性回归的目标就是找到一条直线(在一维输入情况下)或一个超平面(在高维输入情况下),以最佳方式拟合给定的数据点,从而能够对新的输入进行预测。


这个过程可以概括为以下步骤:



- 假设模型:我们假设输入 x 和输出 y 之间存在线性关系。对于单变量(一个特征)线性回归,模型可以表示为:
y = w * x + b
其中 w 是权重(斜率),b 是偏置(截距)。


- 定义损失函数:我们需要一个标准来衡量预测直线的好坏。通常使用均方误差作为损失函数。它计算所有数据点上预测值
(ŷ_i)与实际值(y_i)之差的平方和。
Loss = (1/n) * Σ (y_i - ŷ_i)^2
其中ŷ_i = w * x_i + b,n是数据点数量。

- 优化目标:机器学习的目标是找到一组参数 (w, b),使得损失函数的值最小化。


- 进行预测:一旦通过优化找到了最佳的 w 和 b,对于任何新的输入
x_new,我们就可以使用公式y_pred = w * x_new + b来预测其输出值。

总结 📝



本节课中我们一起学习了线性回归的入门知识。

我们首先了解了回归与分类的区别,核心在于回归用于预测连续值输出。接着,我们探讨了回归在天气预报、股票分析和需求预测等领域的广泛应用。然后,通过一个预测学生分数的生动案例,我们直观地理解了回归的思想、训练数据的重要性以及大脑如何本能地拟合直线进行预测。最后,我们将回归问题形式化为一个机器学习框架,包括建立线性假设模型、定义均方误差损失函数以及通过优化参数最小化损失的目标。

下一节课,我们将深入探讨线性回归的假设函数具体形式,以及如何求解这个优化问题,即如何找到那条最佳的拟合直线。
022:普通最小二乘法逐步实现 🧮
在本节课中,我们将学习回归分析中一项非常重要的技术——普通最小二乘法。这项技术将帮助我们找到能够最小化平方误差的假设函数。
上一节我们介绍了线性回归,并学习了其假设函数(在二维或三维空间中是一条直线)和我们将要使用的损失函数——平方损失函数。本节中,我们将进入机器学习框架的第四步:将机器学习问题表述为一个优化问题,并通过优化过程找到最佳假设函数。这正是我们接下来要做的。
从实践演示开始 🚀
许多课程在讲解普通最小二乘法时,会直接从其矩阵形式开始,但并未展示如何通过动手实践一步步推导出解。与我们在本课程中的一贯做法类似,我们将从一个非常实用的动手演示开始,让你自己推导出解。这样,普通最小二乘法对你来说将变得非常直观。在你亲自为演示找到解之后,我们再来看矩阵形式,并观察矩阵形式与我们所得结果之间的相似之处。
如果你有一些线性代数的基础知识,对本节课会很有帮助。如果没有,也没关系。对于不理解的部分,你可以在课后去学习相关的线性代数知识。
以下是我们的实践问题:
我们有一个包含四个点的数据集,坐标如下:
- 点1: (1, 2)
- 点2: (2, 3)
- 点3: (3, 5)
- 点4: (4, 4)

我们的目标是找到最佳直线 y = θ₁x + θ₀,使得这条直线对所有数据点的平方损失最小。

构建优化问题 ⚙️

首先,我们需要构建需要最小化的目标函数。目标函数 J 依赖于两个参数 θ₁ 和 θ₀。一旦这两个参数确定,假设函数也就确定了。
目标函数是平均平方误差,对于我们的四个数据点,其公式如下:
J(θ₁, θ₀) = (1/4) * Σ (从 i=1 到 4) (预测值ᵢ - 实际值ᵢ)²
其中,预测值就是我们的假设函数:预测值ᵢ = θ₁ * xᵢ + θ₀。

因此,目标函数可以详细展开为:
J(θ₁, θ₀) = (1/4) * [ (θ₁*x₁ + θ₀ - y₁)² + (θ₁*x₂ + θ₀ - y₂)² + (θ₁*x₃ + θ₀ - y₃)² + (θ₁*x₄ + θ₀ - y₄)² ]
我们的任务是找到能够最小化 J 的 θ₀ 和 θ₁。
通过求导求解最优参数 📉
当我们有一个函数(例如 y = x²)并想找到使其最小的 x 时,我们会求导并令导数等于零。这正是我们现在要做的。
我们需要优化两个参数 θ₁ 和 θ₀。因此,我们需要确保目标函数 J 对这两个参数的偏导数都等于零,此时对应的 θ₀ 和 θ₁ 就是最优解(在凸优化问题中,这对应最小值点)。
所以,我们需要建立并求解以下两个方程:
∂J/∂θ₁ = 0∂J/∂θ₀ = 0
接下来,我们将具体计算这些偏导数。首先,将我们的数据点坐标代入详细的目标函数:
J(θ₁, θ₀) = (1/4) * [ (θ₁*1 + θ₀ - 2)² + (θ₁*2 + θ₀ - 3)² + (θ₁*3 + θ₀ - 5)² + (θ₁*4 + θ₀ - 4)² ]
现在,我们分别对 θ₁ 和 θ₀ 求偏导数。
对 θ₁ 求偏导 (∂J/∂θ₁):
应用链式法则,每一项的导数为:2*(θ₁*xᵢ + θ₀ - yᵢ) * xᵢ。
因此:
∂J/∂θ₁ = (1/4) * [ 2*(θ₁*1+θ₀-2)*1 + 2*(θ₁*2+θ₀-3)*2 + 2*(θ₁*3+θ₀-5)*3 + 2*(θ₁*4+θ₀-4)*4 ]
令其等于0,并简化(两边同时乘以4并除以2):
(θ₁*1+θ₀-2)*1 + (θ₁*2+θ₀-3)*2 + (θ₁*3+θ₀-5)*3 + (θ₁*4+θ₀-4)*4 = 0
对 θ₀ 求偏导 (∂J/∂θ₀):
每一项的导数为:2*(θ₁*xᵢ + θ₀ - yᵢ) * 1。
因此:
∂J/∂θ₀ = (1/4) * [ 2*(θ₁*1+θ₀-2) + 2*(θ₁*2+θ₀-3) + 2*(θ₁*3+θ₀-5) + 2*(θ₁*4+θ₀-4) ]
令其等于0,并简化(两边同时乘以4并除以2):
(θ₁*1+θ₀-2) + (θ₁*2+θ₀-3) + (θ₁*3+θ₀-5) + (θ₁*4+θ₀-4) = 0
整理并求解方程组 🔢
现在,我们得到了两个关于 θ₁ 和 θ₀ 的方程。让我们整理它们。
从 ∂J/∂θ₀ = 0 的方程开始(我们称其为方程 A):
(θ₁ + θ₀ - 2) + (2θ₁ + θ₀ - 3) + (3θ₁ + θ₀ - 5) + (4θ₁ + θ₀ - 4) = 0
合并同类项:
(θ₁+2θ₁+3θ₁+4θ₁) + (θ₀+θ₀+θ₀+θ₀) + (-2-3-5-4) = 0
10θ₁ + 4θ₀ - 14 = 0 (方程 A)
从 ∂J/∂θ₁ = 0 的方程开始(我们称其为方程 B):
1*(θ₁+θ₀-2) + 2*(2θ₁+θ₀-3) + 3*(3θ₁+θ₀-5) + 4*(4θ₁+θ₀-4) = 0
展开:
(θ₁+θ₀-2) + (4θ₁+2θ₀-6) + (9θ₁+3θ₀-15) + (16θ₁+4θ₀-16) = 0
合并同类项:
(θ₁+4θ₁+9θ₁+16θ₁) + (θ₀+2θ₀+3θ₀+4θ₀) + (-2-6-15-16) = 0
30θ₁ + 10θ₀ - 39 = 0 (方程 B)
现在我们有了一个二元一次方程组:
10θ₁ + 4θ₀ = 14(方程 A)30θ₁ + 10θ₀ = 39(方程 B)
我们可以用消元法求解。将方程 A 乘以 2.5:25θ₁ + 10θ₀ = 35。用方程 B 减去这个新方程:
(30θ₁ + 10θ₀) - (25θ₁ + 10θ₀) = 39 - 35
5θ₁ = 4
θ₁ = 0.8
将 θ₁ = 0.8 代入方程 A:
10*0.8 + 4θ₀ = 14
8 + 4θ₀ = 14
4θ₀ = 6
θ₀ = 1.5
得到最佳拟合直线 🎯
因此,通过普通最小二乘法,我们得到的最佳拟合直线参数为:
θ₁ = 0.8
θ₀ = 1.5
所以,最优假设函数(直线)是:
y = 0.8x + 1.5
这条直线最小化了给定四个数据点的总平方误差。
与矩阵形式关联 🔗
在我们亲手推导出解之后,现在可以简要关联到普通最小二乘法的常见矩阵形式。对于线性回归 y = Xθ,其中 X 是包含特征(和常数项)的设计矩阵,θ 是参数向量,y 是目标值向量,最小二乘解由以下正规方程给出:
θ = (XᵀX)⁻¹Xᵀy
我们刚才逐步求解的过程,本质上就是对这个特定小数据集手动推导并求解了这个正规方程。矩阵形式是将这个过程推广到任意数量特征和数据点的紧凑数学表示。
总结 📝

本节课中,我们一起学习了普通最小二乘法的核心思想与逐步实现。我们从具体的四个数据点出发,定义了平方损失目标函数,然后通过对其两个参数分别求偏导并令其为零,将问题转化为一个二元一次方程组。通过求解这个方程组,我们得到了最佳拟合直线的斜率和截距。最后,我们了解到这一逐步推导过程与OLS的经典矩阵形式在本质上是相通的。这种方法确保了我们对最小二乘原理有了坚实而直观的理解。
023:岭回归基础与直观理解 🏔️

在本节课中,我们将学习岭回归。上一节我们介绍了普通最小二乘法,但未加入正则化项。本节中,我们将探讨为最小二乘解添加正则化项的意义,以及它如何解决无正则化回归中的两个主要问题。课程最后,我们还将解释为何这种方法被称为“岭”回归。


回顾:普通最小二乘法


首先,我们简要回顾一下无正则化回归中得到的最优参数 θ(或 θ)。其公式如下:



θ = (WᵀW)⁻¹ Wᵀt*


如果你没有看过上一节关于普通最小二乘法的课程,强烈建议你先学习,这将使理解本节内容更加容易。


之前我们提到,无正则化回归会遇到两个主要问题。

以下是这两个问题:

- 逆矩阵不存在问题:如果矩阵 WᵀW 的逆不存在,我们该怎么办?上述公式完全依赖于求逆运算。如果逆矩阵根本不存在,我们该如何进行?这是第一个主要问题。
- 过拟合问题:第二个主要问题当然是过拟合。



如果使用正则化,特别是岭回归,这两个问题都能得到解决。本质上,我们是用一种方法同时解决了两个问题。现在,让我们开始了解岭回归。


岭回归的目标函数

岭回归实际上与我们在分类问题中看到的正则化回归非常相似。


岭回归的目标函数如下所示。我将它称为 J_Ridge,它同样依赖于参数 θ 和 θ₀(你可以将 θ 视为斜率,θ₀ 视为截距)。

J_Ridge(θ, θ₀) = (1/n) Σ (yᵢ - (θ·xᵢ + θ₀))² + λ ||θ||²



目标函数等于平方损失函数的平均值,现在我在这里添加了一个额外的项。事实上,这就是正则化项。它与我们在分类中添加的项完全相同。其中 λ 是一个超参数,为常数。在向量形式中,||θ||² 也可以写成 θᵀθ。


正则化的关键细节


这里我想指出一个小区别:当我们添加正则化项时,我们不包含截距 θ₀。这意味着,正如我们在分类中也看到的,添加这个正则化项意味着我们希望保持参数 θ 的幅度较小。

具体来说,如果斜率太大,我们就会惩罚它。记住,θ 是斜率,而 θ₀ 是截距。假设这是我的数据点,我想找到其中的回归线。



这是第一条线。如果我取一条斜率更高的线,岭回归就会惩罚它。所谓惩罚,意思是如果斜率更高,这个正则化项的值也会更高,从而导致损失函数或目标函数的值更高。我们不希望这样,我们希望目标函数 J 尽可能低,这意味着我们希望参数 θ(斜率)尽可能接近水平方向。


为何要减小参数幅度?


我们这样做是有原因的,在分类案例中也看到过。我们希望参数幅度较小的原因是,我们不希望最终答案捕捉到数据中的噪声。

如果数据中有噪声,我们不希望捕捉它。拥有较小的参数或较小的斜率可以确保噪声不被捕捉。一个需要记住的通用规则是:如果参数值太高,意味着我们的假设函数开始表现异常,并开始捕捉噪声。在这种情况下,我们应该小心。岭回归或正则化可以防止 θ 的幅度变得过高,并保持斜率较低。

关于截距的说明

我们说的第二点是:假设这是原始线。我告诉你,如果它旋转了,我们会惩罚它。但本质上我们说的是,如果我给这条线添加一个截距,即保持这条线的斜率相同,但改变 θ₀。


考虑蓝线 O,以及线 L1 和 L2。线 O、线 L1 和线 L2 具有相同的斜率,即相同的 θ,但它们有不同的 θ₀。我们不会惩罚这一点。截距是多少对我们来说完全没问题,我们不想惩罚截距。

在岭回归中,只有斜率受到惩罚。截距实际上不太重要,因为无论我们增加还是减少截距,并不意味着我们是否捕捉了噪声。更重要的是惩罚斜率。


这里我想指出的一个小区别(面试中可能会被问到,因为这是一个细微的点)是:在岭回归中,我们惩罚斜率,而不惩罚截距。如果我们惩罚截距,那么目标函数中应该还有一项,比如 λ θ₀² 之类的,但我们不添加它,因为截距可以存在,我们只是要惩罚斜率的增加。


岭回归的数学推导


在分类课程中,我们看到了这个正则化项的物理意义或直观理解。它确实有助于保持 θ 的幅度较小。今天,我们将看到一个实际操作的例子,看看它在实践中如何工作,以及它如何防止过拟合,防止我们捕捉训练数据中的噪声。

在此之前,我只想写出其数学来源。上次我们看到,最小化无岭回归的 J(θ) 给出了梯度公式:



∇J(θ) = (2/n) Wᵀ (Wθ - t)


现在,如果你对这个新目标函数求梯度,只需再添加一项,你可能已经猜到了。如果你求导,会得到 2λθ。


因此,岭回归目标函数 J_Ridge 的梯度是:
第一项实际上完全相同,因为平方损失没有变化:(2/n) Wᵀ (Wθ - t),但我们添加了另一项:+ 2λθ。请注意,这里所有项的维度都是 d×1。



为了简单起见,这里我没有考虑 θ₀,因为优化包含 θ₀ 的 J_Ridge 会变得复杂,我想在这里向你展示一个简单的公式。所以,目前为了简单起见,我只关注 θ。

我们还看到,在无岭回归的正常情况下,θ* 是:
θ = (WᵀW)⁻¹ Wᵀt*






本节课中我们一起学习了岭回归的基础概念。我们了解到,通过在普通最小二乘法的损失函数中添加一个关于参数 θ 幅度的正则化项(λ||θ||²),可以同时解决逆矩阵可能不存在以及模型过拟合的问题。正则化项惩罚大的斜率,促使模型参数保持较小,从而提升模型的泛化能力,避免过度拟合训练数据中的噪声。同时,我们还明确了正则化通常只应用于斜率参数 θ,而不应用于截距 θ₀。
024:回归面试要点回顾 📝
在本节课中,我们将对回归分析的核心内容进行总结和回顾。我们将重点探讨普通最小二乘法、正则化、梯度下降与随机梯度下降,以及训练误差与泛化误差的区别。本教程旨在为面试准备提供一个简洁的复习指南。
问题定义与假设

首先,我们来看线性回归问题的基本形式。我们拥有输入X和输出Y的配对数据。目标是找到一个假设函数W,该函数不仅能在训练数据上做出良好预测,也能在测试数据上表现优异。

在一维情况下,假设函数通常表示为一条直线。其通用形式为:
y = θᵀx + θ₀
回归分析的核心是预测。例如,根据历史天气数据预测未来天气,或根据历史股价数据预测未来股价。这与分类任务有本质区别。
损失函数
我们使用的损失函数是平方损失函数。其定义是预测值与真实值之差的平方和。
公式表示:
L(θ, θ₀) = Σ (θᵀxᵢ + θ₀ - yᵢ)²,其中求和从 i=1 到 n。

一个常见的面试问题是:为什么使用平方损失函数,而不是绝对值损失函数?

原因:绝对值函数 |x| 在 x=0 处不可微,且其数学性质不如平方函数 x² 优良。因此,在回归中我们通常选择平方损失函数。


目标函数与普通最小二乘法

接下来,我们讨论目标函数。第一种情况是无正则化。
此时,目标函数 J 就等于我们的损失函数:
J(θ, θ₀) = (1/n) * Σ (θᵀxᵢ + θ₀ - yᵢ)²

我们的目标是找到最优参数 θ* 和 θ₀*,以最小化目标函数 J。这个最小化平方误差的回归问题,也被称为普通最小二乘问题。

OLS 的一个显著优点是它存在解析解。为了得到这个解,我们首先定义矩阵。
矩阵定义:
- 定义增广矩阵
W,其第一列为全1,后面是特征数据。 - 定义目标值向量
T = [y₁, y₂, ..., yₙ]ᵀ。 - 定义参数向量
Θ = [θ₀, θ₁, ..., θ_d]ᵀ。
解析解公式:
Θ* = (WᵀW)⁻¹ WᵀT



这个公式非常简洁,我们很幸运能得到一个闭式解。然而,这个解也存在一些问题。

OLS 的问题与正则化的引入

尽管 OLS 有解析解,但它存在两个主要问题,这也是面试中常被问到的。

问题一:计算复杂度高
计算 (WᵀW)⁻¹ 的时间复杂度是 O(d³),其中 d 是特征维度。当特征维度很高时(例如,经过特征变换后维度达到15或更高),计算逆矩阵将变得非常耗时。

问题二:矩阵不可逆
如果输入特征之间存在多重共线性(即某些特征高度相关),那么矩阵 WᵀW 可能是奇异或接近奇异的,其逆矩阵可能不存在或不稳定。

为了解决这些问题,我们引入了正则化。正则化不仅能解决上述计算和数值问题,更重要的是,它能有效防止模型过拟合。

正则化

正则化通过在目标函数中增加一个惩罚项来实现,这个惩罚项与模型参数的复杂度有关。最常见的两种正则化是 L1(Lasso)和 L2(Ridge)。
L2 正则化(岭回归)目标函数:
J(θ, θ₀) = (1/n) * Σ (θᵀxᵢ + θ₀ - yᵢ)² + λ * Σ θⱼ²
其中,λ 是正则化强度参数。

L1 正则化(Lasso回归)目标函数:
J(θ, θ₀) = (1/n) * Σ (θᵀxᵢ + θ₀ - yᵢ)² + λ * Σ |θⱼ|


L1 正则化的一个关键特性是它能产生稀疏解,即自动进行特征选择,将一些不重要的特征的系数压缩为0。
优化算法:梯度下降
对于带有正则项的目标函数,通常没有像 OLS 那样的简单解析解。因此,我们需要使用迭代优化算法,最基础的就是梯度下降。
梯度下降的核心思想是沿着目标函数梯度的反方向(即下降最快的方向)逐步更新参数,以找到最小值点。
参数更新公式:
θ_new = θ_old - η * ∇J(θ_old)
其中,η 是学习率,控制每次更新的步长。


以下是梯度下降的基本步骤:
- 随机初始化参数 θ。
- 计算当前参数下的损失函数梯度 ∇J(θ)。
- 按照更新公式调整参数。
- 重复步骤2和3,直到损失函数收敛或达到预设的迭代次数。


随机梯度下降
标准梯度下降在每次更新时需要计算整个数据集的梯度,当数据集很大时,计算开销巨大。随机梯度下降 是解决这一问题的有效方法。


SGD 的核心思想是:每次迭代只随机抽取一个样本(或一小批样本,即 Mini-batch SGD)来计算梯度并更新参数。
SGD 更新公式(对于单个样本 i):
θ_new = θ_old - η * ∇J_i(θ_old)

与标准梯度下降相比,SGD 的优点和缺点如下:
- 优点:计算速度快,内存需求低,能够处理海量数据;并且由于其随机性,有时能帮助跳出局部极小值点。
- 缺点:更新路径噪声大,损失函数不会平稳下降,最终可能在最小值点附近震荡。
训练误差与泛化误差

最后,我们讨论机器学习中一对至关重要的概念:训练误差与泛化误差。

- 训练误差:模型在训练数据集上计算得到的误差。它衡量模型对已知数据的拟合程度。
- 泛化误差:模型在新的、未见过的数据(测试集)上期望的误差。它衡量模型的预测能力。
我们的终极目标是获得低的泛化误差,而不仅仅是低的训练误差。如果模型在训练集上误差很低,但在测试集上很高,则发生了过拟合。正则化正是解决过拟合、提升模型泛化能力的关键技术之一。
总结

本节课我们一起回顾了回归分析的核心要点:
- 我们定义了线性回归的问题(预测)和假设函数。
- 解释了使用平方损失函数的原因。
- 介绍了普通最小二乘法及其解析解,并分析了其存在的计算复杂度和矩阵不可逆问题。
- 引入了正则化(L1/L2)来解决上述问题并防止过拟合。
- 讲解了当没有解析解时,如何使用梯度下降及其变体随机梯度下降来优化目标函数。
- 区分了训练误差与泛化误差,明确了机器学习的最终目标是追求低的泛化误差。


掌握这些概念,将为你的机器学习面试打下坚实的基础。
025:30分钟理解神经网络架构 🧠

在本节课中,我们将学习神经网络架构的核心组成部分。我们将从最基本的构建单元开始,逐步将它们组合起来,最终理解一个完整的神经网络是如何工作的。内容设计简单直白,适合初学者。
概述
本节课我们将学习神经网络的两个核心构建模块:神经元和层。我们将解释神经元如何接收输入、进行计算并产生输出,以及如何通过堆叠神经元形成层来构建强大的神经网络模型。
神经元:神经网络的基本单元 🧱
上一节我们介绍了本课程的目标。本节中,我们来看看神经网络的第一个,也是最小的构建模块——神经元。
神经元是神经网络架构中最小的构建单元,其最简单的功能是接收输入并产生输出。它通常用一个圆圈表示。
神经元之所以被称为“神经元”,是因为这个概念最初是为了模拟生物大脑中的神经元而提出的。我们大脑中的神经元也以类似的方式工作:接收输入,然后产生输出。
因此,神经元的功能就是接收输入并产生输出。
你可能会想,这些输入和输出具体是什么?神经元内部到底发生了什么?
输入是用于进行预测的特征。例如,假设我们有三个主要属性:瞳孔直径、耳朵下垂指数和胡须长度。我们的目标是让神经网络接收这三个特征作为输入,然后输出判断目标是猫还是狗。
为了演示,让我们具体观察一个神经元及其输入、输出和内部过程。假设有三个输入:x1(瞳孔直径)、x2(耳朵下垂指数)和x3(胡须长度)。这个神经元有三个输入。
每个输入都有一个与之关联的权重,分别是 w1、w2 和 w3。权重的存在是为了表示该特征的重要性。例如,如果 w1 远大于 w2 和 w3,则表明这个神经元非常重视瞳孔直径这个特征。
现在,我们有一个输出,暂时称之为 y。
那么,在这个“魔法”发生的神经元内部,到底进行了哪些操作呢?每个神经元本质上执行三个重要功能:
以下是神经元内部执行的三个主要操作:
- 求和运算:对加权后的输入进行求和。
- 添加偏置:在求和结果上加上一个偏置项。
- 激活函数:对加上偏置后的结果应用一个非线性函数。
用数学公式表示,神经元的输出 y 为:
y = f(w1*x1 + w2*x2 + w3*x3 + b)
其中,f 是激活函数,b 是偏置项。
你可以看到求和、偏置和激活函数这三个元素是如何结合在一起的。
神经网络中的每个神经元都执行这三个功能:接收输入特征 x1, x2, x3,分配权重 w1, w2, w3,然后产生输出 y。
在神经元内部发生的这三个操作中,求和与添加偏置相对直接,它们在回归和分类问题中也出现过。神经元真正的力量主要来自于激活函数。
激活函数有很多种。例如:
- ReLU(修正线性单元):一个非常常见的激活函数。对于负的输入值,输出为0;对于正的输入值,输出等于输入值。其函数为
max(0, x)。 - Sigmoid(S型函数):另一个著名的激活函数,用于为事件分配概率。其数学形式为
1 / (1 + e^(-x))。
回到我们的猫狗分类例子,我们本质上想分配概率。Sigmoid 是一个很好的激活函数。我们可以使用 Sigmoid,然后如果输出大于 0.5,我们就说它是狗;如果小于 0.5,就说它是猫(选择 0.5 是因为 Sigmoid 函数在 y=0.5 处穿过中点)。
因此,如果我们使用 Sigmoid 激活函数,那么这个神经元的输出将变为:
y = σ(w1*x1 + w2*x2 + w3*x3 + b)
假设我们取一些随机值:w1=1, w2=1, w3=1, x1=1, x2=1, x3=1, b=0。对于这些权重值,神经元的输出将是 σ(3)。查看 Sigmoid 函数图,σ(3) 的值在正侧,大于 0.5。因此,根据我们的设定,我们会判断它为猫。
请记住,我们目前并不知道这些权重 w1, w2, w3 的具体值。寻找这些权重,使得模型的答案与我们的训练数据和测试数据良好匹配,正是训练或优化这个神经元的主要目的。但目前,我向你展示了神经元的主要功能。
这就是神经网络的第一个构建模块——神经元。
层:神经网络的表达能力之源 🏗️
你可能会想,这有什么特别的?因为看公式 σ(w^T x + b),这看起来就像逻辑回归,那里我们也有一个 Sigmoid 激活函数,也是根据是否大于 0.5 来分类。
那么神经网络的真正力量在哪里?神经网络的真正力量来自于第二个构建模块——层。
层就是将一堆神经元堆叠在一起。正是这里体现了神经网络真正的表达能力。
当我们想到层时,就是将一堆神经元堆叠在一起。让我们看看这种堆叠具体是什么样子。
假设这些仍然是我的输入:x1, x2, x3。现在,我们不是只有一个神经元,而是有多个神经元并排排列。例如,我们可以有四个神经元,它们都接收相同的三个输入 x1, x2, x3。这一组并排的神经元就形成了一个层,更具体地说,是一个密集层或全连接层,因为每个输入都连接到该层的每个神经元。
通过堆叠层,我们可以构建深度神经网络。一层的输出可以作为下一层的输入。这种分层结构使得网络能够学习输入数据中越来越复杂和抽象的特征。
例如,第一层可能学习识别图像中的边缘,第二层可能将这些边缘组合成形状(如圆形、方形),第三层可能将这些形状组合成更复杂的模式(如眼睛、鼻子),最终层则根据这些高级特征做出分类决策。
总结
本节课中,我们一起学习了神经网络架构的两个核心构建块:
- 神经元:接收输入、进行加权求和、添加偏置并应用激活函数以产生输出的基本计算单元。
- 层:通过堆叠多个神经元形成,是神经网络学习复杂模式和表达能力的来源。层的堆叠构成了深度神经网络的基础。

理解神经元和层是如何工作的,是理解任何现代神经网络模型的第一步。
026:反向传播的直观理解 🧠


在本节课中,我们将学习反向传播算法,但会以一种非常直观的方式进行。我们特意安排这一独立课程,目的是避免陷入复杂的数学推导,而是真正理解该算法背后的核心思想。你可能会忘记数学公式,例如如何计算一个矩阵对另一个矩阵的导数,但一旦你对反向传播有了直观的理解,你将永远不会忘记它,并且会更加欣赏其精妙之处。本讲座深受“3Blue1Brown”频道及其教程的启发。
问题背景
我们面临的问题与神经网络中常见的问题相同。我们收集了猫和狗的数据,测量了三个特征:胡须长度、瞳孔直径和耳朵松软指数。现在的问题是,如果给定一组新的这三个测量值,我们能否基于这些测量值预测它是猫还是狗?我们的任务是构建一个神经网络来进行这种预测。
神经网络结构
我们的神经网络结构如下:输入层有3个输入 x1、x2 和 x3,这是第一个隐藏层,这是第二个隐藏层,这是输出层。在输出层,最终的分类结果是猫或狗。
每一层都有与之相关的权重。例如,在这一层,每个神经元(这个、这个和这个)都有三个权重。因此,这里总共有九个权重。类似地,这里也有九个权重,这里也有九个权重,而这里有三个权重。
除了这些权重,这里的每个神经元(这个、这个、这个、这个、这个、这个和这个)都有一个偏置项与之关联。这些就是通常与神经元相关的权重。
神经元的基本操作
正如我们已经看到的,每个神经元执行三个基本操作:
- 求和运算:假设输入是
x1、x2、x3,权重是w1、w2、w3,则执行求和运算z = w1*x1 + w2*x2 + w3*x3。 - 添加偏置:然后加上一个偏置项
b,得到z + b。 - 应用激活函数:在本例中,我们使用Sigmoid激活函数
a = σ(z + b)。
对于输出神经元,其工作方式是计算上述值,如果大于0.5,则判定为猫;如果小于0.5,则判定为狗。
神经网络的设计目标
当我们说想要设计一个神经网络来进行预测时,本质上意味着我们需要找到最优的权重和偏置值。让我们写下权重矩阵:
- 第一层权重:
W1(一个3x3矩阵) - 第二层权重:
W2(一个3x3矩阵) - 输出层权重:
W3(一个3x1矩阵) - 偏置项:
b01、b02、b03
我们需要找到这些矩阵中每一个权重的最佳值。我们已经知道,实现这一目标的最佳方法是使用梯度下降法。
梯度下降与反向传播
在梯度下降中,主要目标是更新权重。对于一个特定的权重 w_i,其更新公式为:
w_i_new = w_i_old - α * (∂L/∂w_i)
其中 α 是学习率(步长),∂L/∂w_i 是损失函数 L 对权重 w_i 的梯度。
反向传播的核心作用就是帮助我们计算这个梯度:损失函数相对于权重的梯度。有了这个梯度,我们就可以更新权重。
今天,我们将从一个非常简单的角度来理解这个梯度。我们将观察损失函数的梯度,并了解不同神经元的权重如何影响这个梯度,从而理解反向传播算法背后的基本直觉。
梯度的直观意义
损失函数相对于权重的梯度 ∂L/∂W 本身是一个矩阵。这个矩阵包含了网络中每一个权重对应的梯度值。例如:
- 对于第一层(粉色线),有9个梯度值。
- 对于第二层(橙色线),有9个梯度值。
- 对于输出层(绿色线),有3个梯度值(为简化,暂不考虑偏置项)。
这个矩阵中的每一项都代表了一个含义:该权重发生微小变化时,损失函数会如何变化。
举例来说,假设第一条粉色线对应的梯度值是0.1,而某条绿色线对应的梯度值是3.2。这意味着:
- 损失函数对绿色权重的变化非常敏感:即使该权重发生微小变化,损失也会发生较大改变。
- 损失函数对粉色权重的变化不太敏感。
- 这条绿色线对损失的影响是那条粉色线的32倍。
这也意味着,在梯度下降更新过程中,这个绿色权重比那个粉色权重重要得多。改变这个绿色权重能更有效地改变或降低损失函数。
∂L/∂W 这个矩阵给了我们一个关键信息:损失函数对每一个权重的敏感度。它告诉我们哪些权重对损失贡献大(需要重点更新),哪些贡献小。
一个具体例子
为了更具体地说明,让我们看一个简单的训练示例。这里上传了一张猫的图片作为训练样本。

这个示例有三个输入:瞳孔直径、耳朵松软指数和胡须长度。这些输入将馈送到网络的第一层,经过两个隐藏层,最终到达输出神经元。输出神经元将给出答案:σ(w1*x1 + w2*x2 + w3*x3 + b)。如果该值大于0.5,则预测为猫;否则预测为狗。
本节总结

本节课中,我们一起学习了反向传播算法的直观理解。我们回顾了神经网络的基本结构和神经元操作,明确了神经网络训练的目标是找到最优的权重和偏置。我们了解到,梯度下降法通过计算损失函数对权重的梯度来更新权重,而反向传播正是高效计算这个梯度的算法。最重要的是,我们建立了对梯度 ∂L/∂W 的直观认识:它量化了损失函数对网络中每一个权重的敏感度,指导我们在更新时更关注那些对损失影响更大的权重。这种理解是掌握神经网络如何“学习”的关键一步。
027:神经网络激活函数

在本节课中,我们将全面学习神经网络中的激活函数。激活函数是神经网络变得如此强大和富有表现力的关键,它们将线性计算转换为非线性计算,使我们能够捕捉输入特征之间复杂的相互作用和关系。激活函数是神经网络的基本构建模块之一,有助于揭示输入与输出数据之间的复杂模式。本节课将致力于理解不同的激活函数:它们如何工作、各自的优缺点,以及针对特定机器学习问题应如何选择激活函数。在面试中,关于激活函数的问题也经常出现,因此清晰掌握相关概念非常重要。
在开始之前,我们先快速回顾一下神经网络的结构,以及激活函数具体位于何处。
神经网络结构回顾
这里展示了一个具有两个隐藏层的神经网络。它有一个包含三个不同输入的输入层和一个输出层。在第一层有权重矩阵 W1,第二层有 W2,第三层有 W3。你可以看到排列在不同层中的这些神经元。
这个神经网络也可以用下方的块状图表示:首先输入 x,经过第一组权重和偏置,得到线性求和结果 z1,即 z1 = W1^T x + b1。然后 z1 通过一个激活函数。基本上,这里的每个神经元都有一个激活函数,当激活函数应用于 z1 时,会产生该层的输出,称为 a1。例如,第一层中每个神经元都有一个激活函数和一个对应的输出,所有这些神经元的输出将作为下一层的输入。
因此,下一层的输入是 a1,然后它会经过权重 W2 和偏置 b2,再通过第二层神经元的激活函数,产生输出 a2。接着,a2 经过 W3、b3,通过最后一个神经元的激活函数,最终得到输出 a3。
这就是神经网络的构建块表示法。如果用函数形式表示输出,它将如下所示(这里使用了Sigmoid激活函数,W3、W2、W1 是权重,b1、b2、b3 是偏置):
y = σ( W3^T * σ( W2^T * σ( W1^T x + b1 ) + b2 ) + b3 )
本质上,这类似于 y = f(x),其中 f 是这个由多个Sigmoid函数嵌套而成的复杂函数。在理解不同激活函数的特性时,记住这种函数表示法会很有用。有时从构建块角度思考较难,但从函数角度思考则更容易。请记住,神经网络归根结底就是一个大函数,一个像这样比单个Sigmoid复杂得多的函数(它是Sigmoid的Sigmoid的Sigmoid)。所有神经网络本质上都可以用类似这样的巨大函数形式来表示。
现在我们已经有了这个概念,让我们从单个神经元层面来看。每个神经元执行三个操作:对权重进行求和运算、加上偏置项,然后应用一个激活函数。因此,激活函数 f 被应用于 w1x1 + w2x2 + w3*x3 + b。w 是根据输入馈入神经元的权重(输入是 x1, x2, x3,每个输入对应一个权重 w1, w2, w3),此外还有一个偏置 b。
这里展示的每一个神经元——第一隐藏层、第二隐藏层以及输出层的神经元——都执行这三个函数:求和、加偏置、然后对结果应用激活函数。因此,激活函数出现在所有有神经元的地方,它们不仅在正向传播中扮演基础角色,在我们为了优化权重而进行反向传播计算梯度时也至关重要。
接下来,让我们看看不同的激活函数,比较它们的异同,并了解各自的优缺点。
常用激活函数类型
我们将要看的第一个激活函数是Sigmoid函数。我们在之前的课程中已经多次涉及。
Sigmoid激活函数的形式是 y = 1 / (1 + e^{-x})。如果绘制这个函数,其图像大致如下所示。
这个激活函数总是返回一个正值。它的值被限制在1以内,永远不会高于1,始终介于0和1之间,且在 x=0 时 y=0.5。这就是Sigmoid激活函数,它通常用于二分类问题。
请记住,由于Sigmoid的输出值始终在0到1之间,因此Sigmoid函数的输出通常被视为概率。如果你想区分两个类别(例如是猫还是狗),你可以将Sigmoid的输出视为概率,然后设定如果输出大于0.5则判定为猫,小于0.5则判定为狗。因此,Sigmoid激活函数通常用于这类需要为两个类别分配概率的二分类问题。
现在,让我们看看这个特定的Sigmoid函数的优点和缺点。
以下是Sigmoid激活函数的主要优缺点:
-
优点1:输出范围固定且与概率相关
其输出始终介于0和1之间,并且与概率概念相关联。因此,它是用于二分类问题的非常好的函数。 -
优点2:平滑可微
这个函数是平滑的,意味着它在所有点上都是可微的,对所有输入 x 都可微。这对我们非常有用,因为请记住,在进行反向传播时,我们必须计算导数,所以我们需要确保激活函数也是可微的。Sigmoid的一个优点就是它相当平滑,从图中可以看出它是连续且处处可微的。
现在,让我们谈谈Sigmoid激活函数最大的缺点。为了真正理解这个缺点,我想使用之前提到的函数形式。请记住,如果在神经网络中使用Sigmoid激活函数,最终的 y 将类似于:
y = σ( W3^T * σ( W2^T * σ( W1^T x + b1 ) + b2 ) + b3 )
现在我们要做的是求导数。损失函数可能类似于 (y - y_actual)^2。现在,如果你想求损失关于权重 w 的导数,它将类似于 2 * (y - y_actual) * dy/dw。因此,我们最终需要计算整个表达式关于 w 的导数。让我们看看在应用链式法则时,这个导数会是什么样子。

总结

本节课我们一起学习了神经网络的核心组件——激活函数。我们首先回顾了神经网络的结构,明确了激活函数在神经元计算中的位置(求和、加偏置后应用)。然后,我们深入探讨了最经典的激活函数之一:Sigmoid函数。我们了解了它的数学公式、图形特征、适用于二分类问题的优点(输出为概率、平滑可微),并初步探讨了其可能存在的缺点(如梯度消失问题,这将在后续详细展开)。理解激活函数是掌握神经网络工作原理的关键一步。
028:梯度下降中的动量法


在本节课中,我们将学习自适应步长的概念。具体来说,我们将探讨三种不同的方法:动量法、Adadelta 和 Adam。Adam 是目前训练大多数机器学习算法时默认使用的优化器。但要理解 Adam,我们需要循序渐进。这三种方法(动量法、Adadelta 和 Adam)背后共同的核心概念是移动平均。
在深入理解移动平均之前,让我们先探讨为什么需要自适应步长。
为什么需要自适应步长?🤔
如果使用固定的步长,梯度下降的收敛过程可能会非常缓慢,这并非理想的做法。原因在于反向传播过程中,对于损失函数求导时,不同层的权重矩阵会以不同的方式累积梯度。适用于某一层的步长可能不适用于另一层。同样,在某一轮迭代中有效的步长,在另一轮迭代中可能效果不佳。因此,我们需要研究能随迭代过程变化的步长,即自适应步长。
在探讨如何为每次迭代调整步长之前,我们首先需要理解移动平均的概念。许多学生觉得 Adam 难以理解,正是因为没有掌握移动平均。实际上,这是一个非常简单的概念。
理解移动平均 📈
我们从一系列数字开始,例如 A1, A2, ..., An。这些数字的普通平均值是:
平均值 = (A1 + A2 + ... + An) / N
移动平均(或称运行平均)则是一个会随着我们观察的特定元素而变化的平均值。我们用 A1, A2, ..., An 表示序列中的元素,用 A^1, A^2, ..., A^n 表示每一步的移动平均值。
移动平均的公式如下:
A^0 = 0
A^t = γ * A^{t-1} + (1 - γ) * A_t
其中,A_t 是原始序列在时间步 t 的元素,A^t 是时间步 t 的移动平均值,γ 是一个介于 0 和 1 之间的参数(通常接近 1,如 0.9)。如果 γ 是固定的,我们称之为移动平均;如果 γ 随时间变化,则称为运行平均。在本节课中,我们假设 γ 固定。
让我们通过一个例子来理解这个公式的含义。
假设我们有一个序列:1, 2, 3, 4, 5, 6, 7, 8。令 γ = 0.9。
根据公式计算:
A^0 = 0A^1 = 0.9 * 0 + 0.1 * 1 = 0.1A^2 = 0.9 * 0.1 + 0.1 * 2 = 0.09 + 0.2 = 0.29A^3 = 0.9 * 0.29 + 0.1 * 3 = 0.261 + 0.3 = 0.561
观察这些移动平均值:
A^1只依赖于当前元素A1。A^2依赖于当前元素A2和过去的元素A1。A^3依赖于当前元素A3以及过去的元素A1和A2。
由此我们可以得到两个关键直觉:
- 移动平均捕捉了过去和现在的影响。当前的平均值包含了历史信息的“记忆”。
- 如果
γ的值非常接近 1(例如 0.9),那么“现在”的值比“过去”的值占主导地位。过去的值被赋予的权重较小。
现在,让我们看看如果 γ 是一个较小的值(例如 0.1)会发生什么。

如果 γ 值较小,那么公式中 (1 - γ) 部分较大,这意味着当前元素 A_t 的权重更大,而历史移动平均值 A^{t-1} 的权重更小。因此,移动平均对最近的变化会更加敏感,历史信息的“记忆”效应会更弱。相反,当 γ 接近 1 时,移动平均变化缓慢,对历史有更长的记忆,对近期波动的平滑效果更强。
理解了移动平均的概念后,在下一节中,我们将看到这个概念如何被应用到梯度下降优化中,形成动量法。
总结

本节课我们一起学习了自适应步长的必要性以及其核心基础概念——移动平均。我们了解到,固定步长在复杂的神经网络训练中可能效率低下,而自适应步长能根据梯度历史动态调整。移动平均通过一个简单的递归公式,巧妙地平衡了当前梯度与历史梯度信息,这为后续理解动量法、Adadelta 和 Adam 等高级优化器奠定了重要基础。
029:Python神经网络实践


在本节课中,我们将学习如何在Python中动手实现神经网络。我们将选取一个问题,构建神经网络架构,训练网络,获取结果,并进行测试。我们将使用一些在实践中工程师常用的标准Python库。
如果你想了解如何从零开始创建神经网络的更详细版本,我已经开始了另一个名为“从零开始神经网络”的系列,目前已有三到四个视频,我们将分解TensorFlow和Keras等库使用的命令。但在本讲座中,我将直接使用这些库的命令,因为本讲座的主要目的是展示当今实践中如何使用神经网络。如果你想获得更理论化的理解,请查看“从零开始神经网络”系列,我已将链接附在本视频中供你参考。
那么,让我们开始今天的课程。在进入问题和实际开始用Python编码之前,我想先描述一下通常在Python中编码神经网络时会听到或想到的三个框架。
以下是这三个框架。
- TensorFlow:如果你访问TensorFlow网站,你会看到TensorFlow基本上是一个用于运行机器学习模型或机器学习框架的平台,由Google开发。今天使用的许多Python代码都使用TensorFlow。
- Keras:Keras与TensorFlow有些不同,因为Keras就像一个API。你可以把它看作是你可以调用的函数。例如,如果你想将不同的层链接在一起以创建神经网络架构,可以使用Keras的Sequential API调用。同样,Keras使编写函数变得更容易。如果你想实现一个Dropout层,而不是从头开始编码,你只需一个命令。你还可以使用Keras接口(也称为API)非常简单地指定损失函数和梯度下降规则,而无需显式写出。
- PyTorch:PyTorch也是一个机器学习框架,你可以使用Python通过PyTorch定义和运行神经网络。它由Meta(前身为Facebook)开发。
现在,PyTorch和TensorFlow之间有一个很大的区别,我将尽可能简化这个区别。
首先,让我们看看在TensorFlow和PyTorch中编写用于创建神经网络的简单代码。
例如,这里我们使用TensorFlow。你导入tensorflow as tf,然后记住我说过Keras已集成到TensorFlow中。因此,如果你想使用Keras中的层API,你只需要from tensorflow.keras import layers。然后你可以定义神经网络的层。你甚至可以导入Sequential。这里我们定义了一个只有10个输入特征和1个输出的神经网络。

import tensorflow as tf
from tensorflow.keras import layers, Sequential
model = Sequential([
layers.Dense(1, input_shape=(10,))
])


另一方面,如果我们想用PyTorch做同样的事情,实现看起来像这样。
import torch
import torch.nn as nn


class SimpleNN(nn.Module):
def __init__(self):
super(SimpleNN, self).__init__()
self.linear = nn.Linear(10, 1)
def forward(self, x):
return self.linear(x)



所以PyTorch遵循一种更面向对象的方法,我们定义一个类。然后我们在类中定义两个方法,__init__是一个特殊方法,在创建此类的实例时自动调用。然后我们定义这个神经网络的权重:10个输入特征,1个输出。第二个方法是forward,它实现前向传播,然后返回神经网络的输出。
因此,编写相同的神经网络有两种不同的方式:TensorFlow使用更像基于API的方法或将函数链接在一起,而PyTorch使用面向对象的编程方法。
但这两种方法之间有一个更微妙且更大的区别:TensorFlow使用称为静态计算图的东西,而PyTorch使用称为动态计算图的东西。
让我试着解释一下这些是什么意思。假设我们想计算两个函数的和。记住TensorFlow使用静态计算图,所以我们定义计算函数,然后说c = a + b。现在,如果你想修改这个函数中的某些内容,你需要编写一个不同的函数,比如compute_extended,其中c = a + b,然后假设我想定义另一个变量d,它是c乘以2,那就是另一个变量。所以,你不能轻易地扩展这个函数,你必须编写另一个函数。原因是因为一旦定义了这个函数,TensorFlow就会创建一个静态计算图,并且很难灵活地处理这个函数。这是描述它的最简单方式。而如果你使用PyTorch,你可以动态地更改内容,初始加法可以动态执行,其中c = a + b,然后你还可以动态添加另一个操作d = c * 2,你不需要为它定义一个单独的函数。
本质上,我想说的是,PyTorch在代码上更灵活,因为它是在代码运行时动态地在变量之间创建计算图。而TensorFlow以静态方式创建图,所以如果你想改变某些东西,那么我们需要重新定义函数本身,因此它不太灵活。

总而言之,初学者都可以使用TensorFlow和PyTorch。但请记住,TensorFlow快速、稳健,但不是很灵活。因此,如果你想尝试各种东西,想将其用于研究,不太推荐TensorFlow,但它实际上非常适合生产级或工业级的机器学习框架。另一方面,PyTorch以动态方式生成这些图,因此非常灵活。所以,如果你想自己改变东西,想使用这些框架进行研究,那么PyTorch非常好。作为初学者,你可以同时使用PyTorch和TensorFlow,事实上我鼓励你两者都用,以了解两者之间的异同。
在本视频中,我们将使用TensorFlow。我希望你已经理解了这三个框架之间的区别。现在,你可以把TensorFlow/Keras放在一个篮子里,把PyTorch放在另一个篮子里。
那么,让我们开始今天的例子。今天,我们要做的是执行一个简单的分类任务。
首先,让我向你介绍输入数据。我们的输入数据或训练数据看起来像这样。每个输入有两个属性x1和x2。所以如果你绘制它们,它看起来会是这样,并且每个输入还有一个与之关联的颜色编码。本质上,有三种颜色编码:R、G或B。所以,你取的任何点(x1, x2),比如说我们取这个点,它被编码为红色;如果我们取这个点,它被编码为蓝色;如果我们取这个点,它被编码为绿色。

这也被称为螺旋数据。我们在本讲座中的目标是……
030:卷积神经网络(CNN)介绍 🧠

在本节课中,我们将学习卷积神经网络及其基本介绍。我们将从了解其应用开始,然后探讨为何需要专门研究这种网络,最后回顾其发展历史。
应用领域 📸
卷积神经网络(CNN)在图像处理领域有广泛应用。以下是几个核心应用示例。
猫狗图像分类
第一个应用是简单的猫狗图像分类。通过一个名为“可教机器”的界面,我们可以上传猫和狗的图片样本进行训练。训练完成后,模型需要能够正确分类新的测试图片。
以下是训练和测试的关键步骤:
- 上传10张猫和10张狗的图片作为训练数据。
- 训练模型识别图片中的模式和特征。
- 使用模型预测新图片是猫还是狗。
测试表明,即使面对与训练数据视角不同的图片,训练好的CNN模型也能以高准确率进行分类。
脑瘤检测
第二个应用在医疗健康领域,即脑瘤检测。模型需要根据核磁共振扫描图像,判断是否存在肿瘤(恶性)或不存在肿瘤(良性)。
以下是数据特点和模型任务:
- 恶性图像:通常包含明亮的白色斑点。
- 良性图像:看起来更均匀,没有明显的斑点。
- 模型任务:训练AI系统自动检测图像模式,并对新图像进行正确分类。
评估显示,训练好的神经网络能够准确预测肿瘤状态,其底层架构同样是卷积神经网络。
手写数字识别
第三个有趣的应用是手写数字分类。系统需要将用户手绘的像素图识别为对应的数字。
模型需要处理以下挑战:
- 将手写笔画转换为像素数据。
- 识别不同书写风格下的同一数字(例如,不同人书写“4”的方式可能不同)。
- 对任意手写数字进行准确分类。
除了上述例子,CNN在自动驾驶(识别物体、行人)、人脸识别等任何涉及图像处理的领域都发挥着核心作用。
上一节我们看到了CNN的强大应用,本节中我们来看看为什么传统的神经网络在处理图像时效率低下,从而引出对CNN的需求。

为何需要卷积神经网络?🤔

让我们通过一个简单的例子来理解。假设有一张32x32像素的RGB彩色猫的图片。
图像的总像素数计算如下:
总像素数 = 宽度 × 高度 × 通道数 = 32 × 32 × 3 = 3072

如果我们尝试使用普通的全连接前馈神经网络来处理这张图片,网络结构将面临巨大挑战。
以下是使用全连接网络处理此图像的问题:
- 输入层:需要3072个神经元,对应每个像素。
- 第一个隐藏层:假设有1000个神经元。
- 参数量:仅第一层就需要
3072 × 1000 ≈ 3, 000, 000个权重参数。 - 计算成本:如果网络有多层,参数量将轻松超过百万,导致计算极其昂贵且效率低下。
这只是全连接网络不适合图像任务的原因之一。更根本的原因在于,它忽略了图像中像素之间的空间局部关联性,而CNN正是为了解决这些问题而设计的。
在了解了CNN的必要性后,最后我们来简要回顾一下这项技术是如何发展起来的。
卷积神经网络发展简史 📜
尽管“卷积神经网络”这个术语在过去十年才变得非常流行,但人们在这个领域的研究已经持续了40到45年。了解并承认早期研究者的贡献非常重要。
从早期的感知机模型到现代复杂的深度CNN架构,其核心思想——利用卷积操作提取局部特征——经历了长期的演变和完善,才成就了今天在计算机视觉领域的统治地位。

本节课中我们一起学习了卷积神经网络的基本概念。我们首先探索了CNN在图像分类、医疗诊断和手写识别等多个领域的实际应用。接着,我们分析了传统全连接神经网络在处理图像数据时面临的计算效率低下和忽略空间信息等问题,从而理解了CNN被提出的必要性。最后,我们简要回顾了CNN长达数十年的发展历程。在接下来的课程中,我们将深入CNN的内部机制,学习其核心组件如卷积层、池化层等是如何工作的。
031:一维滤波器与卷积运算

在本节课中,我们将学习卷积神经网络中的一个核心概念:滤波器。我们将从一维图像入手,理解滤波器如何工作,并介绍卷积运算的基本原理。通过本课的学习,你将明白为什么滤波器是卷积神经网络的基础构建模块。
滤波器的重要性
上一节我们介绍了为什么传统的全连接神经网络不擅长处理图像数据。本节中,我们来看看卷积神经网络如何通过引入滤波器来解决这些问题。
传统神经网络在处理图像时,没有利用图像数据的两个关键特性:
- 空间局部性:图像中属于同一物体的像素在空间上是相邻的。例如,猫的眼睛像素会彼此靠近。
- 平移不变性:图像中的物体无论出现在画面的哪个位置,其类别不变。例如,猫在画面中向左或向右移动,它仍然是猫。
卷积神经网络的设计就是为了利用这两个特性,而滤波器正是实现这一目标的核心工具。
什么是滤波器?
滤波器可以理解为一种用于从数据中提取特定特征的工具。如果你使用过手机相机或Instagram的滤镜功能,那么你已经对滤波器有了直观的感受。在数学和机器学习中,滤波器是一个小型的数值矩阵(或向量),它通过一种称为卷积的运算在输入数据上滑动,以检测特定的模式。
一维图像与左边缘检测
为了理解滤波器的工作原理,我们从一个简单的一维“图像”开始。一维图像可以看作是一系列明暗交替的条纹。
以下是我们的一维图像示例,其中黑色区域用像素值0表示,白色区域用像素值1表示:
数据: [0, 0, 1, 1, 1, 0, 1, 0, 0, 0]
我们的目标是检测图像中的左边缘,即从黑色(0)到白色(1)的过渡位置。通过观察,我们可以发现左边缘出现在索引2(从0到1)和索引5(从0到1)的位置。现在,我们需要让计算机也能从像素数据中识别出这些边缘。
左边缘检测滤波器
为了检测从0到1的过渡,我们可以设计一个简单的滤波器。这个滤波器需要能对“低值到高值”的转变做出响应。
我们使用一个包含两个值的滤波器:[-1, 1]。这个滤波器被称为左边缘检测滤波器。
- 公式:
filter = [-1, 1] - 其原理是:当它滑过
[0, 1]这样的序列时,点积运算(-1)*0 + (1)*1 = 1会得到一个高正值,表示检测到了一个左边缘。
卷积运算
卷积运算就是让滤波器在输入数据上滑动,并在每个位置计算滤波器与对应数据窗口的点积。
点积的计算方法如下:对于两个向量 v1 = [a1, b1] 和 v2 = [a2, b2],它们的点积是 a1*a2 + b1*b2。
以下是卷积运算在一维图像上的逐步演示:
-
初始位置:将滤波器
[-1, 1]与数据的前两个元素[0, 0]对齐。- 计算点积:
(-1)*0 + (1)*0 = 0 - 结果:
0
- 计算点积:
-
滑动一步:将滤波器向右滑动一位,对齐
[0, 1]。- 计算点积:
(-1)*0 + (1)*1 = 1 - 结果:
1→ 检测到一个左边缘!
- 计算点积:
-
继续滑动:将滤波器对齐
[1, 1]。- 计算点积:
(-1)*1 + (1)*1 = 0 - 结果:
0
- 计算点积:
-
重复此过程,直到滤波器滑过整个数据序列。
将上述所有步骤的结果按顺序排列,就得到了卷积运算的输出特征图。
以下是完整的卷积过程图示:

总结
本节课中,我们一起学习了卷积神经网络的基础知识。我们首先回顾了图像数据的特性(空间局部性与平移不变性),并指出传统神经网络的不足。接着,我们引入了滤波器的概念,它是在数据上滑动以提取特征的小型模板。我们通过一个具体的例子——使用滤波器 [-1, 1] 在一维图像上检测左边缘——详细讲解了卷积运算的步骤。卷积运算的核心是点积,它量化了滤波器与局部数据的匹配程度,输出结果构成了新的特征图。

理解一维滤波器与卷积是学习更复杂的二维图像处理(如下一节课的内容)的重要基石。通过这种运算,神经网络能够有效地捕捉图像中的关键局部特征。
032:二维滤波器、通道与特征识别

在本节课中,我们将学习卷积神经网络中的二维滤波器。我们将了解如何将一维卷积的概念扩展到二维图像处理中,并学习如何使用滤波器来识别图像中的特征,例如边缘和孤立像素。
上一节我们介绍了一维滤波器的工作原理。本节中我们来看看如何将这些概念应用到更常见的二维图像上。
从一维到二维
我们通常处理的图像,如猫、狗的照片或手机中的图片,都是二维的。因此,将卷积操作扩展到二维空间,能让我们处理更贴近现实生活的数据。核心目标与一维情况相同:识别和量化图像中的特征。滤波器在二维图像中扮演着与一维情况相同的角色,只是滤波器的数量和维度处理需要更仔细。
问题定义:识别孤立像素
假设我们有一个二维图像,其中包含明亮的白色区域和黑暗的黑色区域。我们的任务是识别图像中的“孤立像素”,即一个被白色区域完全包围的黑色像素点。人类视觉可以轻松识别出下图中粉色箭头所指的这样一个像素。
然而,计算机“看到”的并非图像,而是一系列数字。对于计算机,白色区域被表示为数值 1,黑色区域被表示为数值 0。因此,上图在计算机中可能被表示为以下 4x4 的像素矩阵:
1 1 1 1
1 0 1 1
0 1 0 0
0 1 0 0
我们的目标是为计算机设计一套数学规则(即卷积运算和滤波器),使其能自动找出这个孤立像素。
二维卷积运算入门
为了便于理解,我们先从一个更简单的任务开始:检测图像中的左边缘。左边缘被定义为从黑色(0)到白色(1)的过渡区域。观察上述矩阵,我们可以发现三处这样的左边缘。
以下是执行二维卷积的步骤:
首先,我们需要选择一个合适的滤波器。为了检测从黑到白的过渡,我们使用一个简单的 1x2 滤波器:[-1, 1]。其直观意义是:-1 对应较暗区域,+1 对应较亮区域,两者的差值能突出变化。
接着,我们需要在每一行上滑动这个滤波器并进行点积运算。为了确保卷积后的输出矩阵尺寸与输入图像(4x4)保持一致,我们需要进行“填充”操作。具体做法是在每一行的末尾添加一个值为 0 的虚拟像素。
现在,让我们逐行进行卷积计算:
第一行计算:
输入行(已填充):[1, 1, 1, 1, 0]
- 滤波器位置1(覆盖像素1,1):
1*(-1) + 1*(1) = 0 - 滤波器位置2(覆盖像素1,1):
1*(-1) + 1*(1) = 0 - 滤波器位置3(覆盖像素1,1):
1*(-1) + 1*(1) = 0 - 滤波器位置4(覆盖像素1,0):
1*(-1) + 0*(1) = -1
第一行输出:[0, 0, 0, -1]
第二行计算:
输入行(已填充):[1, 0, 1, 1, 0]
- 位置1:
1*(-1) + 0*(1) = -1 - 位置2:
0*(-1) + 1*(1) = 1 - 位置3:
1*(-1) + 1*(1) = 0 - 位置4:
1*(-1) + 0*(1) = -1
第二行输出:[-1, 1, 0, -1]
第三行计算:
输入行(已填充):[0, 1, 0, 0, 0]
- 位置1:
0*(-1) + 1*(1) = 1 - 位置2:
1*(-1) + 0*(1) = -1 - 位置3:
0*(-1) + 0*(1) = 0 - 位置4:
0*(-1) + 0*(1) = 0
第三行输出:[1, -1, 0, 0]
第四行计算:
输入行(已填充):[0, 1, 0, 0, 0]
- 位置1:
0*(-1) + 1*(1) = 1 - 位置2:
1*(-1) + 0*(1) = -1 - 位置3:
0*(-1) + 0*(1) = 0 - 位置4:
0*(-1) + 0*(1) = 0
第四行输出:[1, -1, 0, 0]
最后,将所有行的输出结果组合起来,就得到了卷积后的特征图:
0 0 0 -1
-1 1 0 -1
1 -1 0 0
1 -1 0 0
在这个特征图中,数值为 1 的位置(第二行第二列)就对应了原图像中一个从黑到白的显著左边缘。通过这种方式,我们使用数学运算让计算机“识别”出了特征。
回到孤立像素检测
理解了基础的边缘检测后,我们可以设计更复杂的滤波器来检测“孤立像素”。这通常需要一个能感知中心像素与周围所有邻居差异的滤波器,例如一个 3x3 的滤波器。其核心思想是:如果中心像素是黑色(0),而周围8个像素都是白色(1),那么该点就是一个孤立像素。滤波器设计可能如下:
-1 -1 -1
-1 8 -1
-1 -1 -1
将这个滤波器与图像进行卷积运算,在孤立像素点处会得到一个很高的正响应值,从而将其标识出来。具体的计算过程与上述边缘检测示例类似,只是在二维平面上进行滑动和点积。
通道的概念
在实际的彩色图像中,每个像素点不止有一个数值。例如,在RGB色彩模型中,每个像素由红、绿、蓝三个通道的数值组成。因此,一个 224x224 的彩色图像,其数据维度是 224 x 224 x 3。
处理多通道图像时,我们的滤波器也需要相应的深度。对于一个三通道图像,每个滤波器本身也是一个三维体积,例如 3x3x3。卷积操作变为:滤波器的每个通道与输入图像的对应通道分别进行卷积,然后将所有通道的结果相加,再加上一个偏置项,最终生成一个二维的特征图。
# 简化示意:输入(高,宽,通道)与滤波器(高,宽,输入通道,输出通道)的卷积
output_feature_map[x, y, k] = bias[k] + sum( input[x+i, y+j, c] * filter[i, j, c, k] )
通过使用多个不同的滤波器,我们可以从输入图像中提取多种类型的特征,生成多个特征图,这些特征图堆叠起来就构成了新的“通道”。
总结
本节课中我们一起学习了二维卷积神经网络的基础知识。
- 我们首先将一维卷积的概念扩展到了二维图像,理解了计算机如何将图像视为像素矩阵。
- 通过一个检测左边缘的实例,我们详细演练了二维卷积的步骤,包括选择滤波器、进行填充以及逐行滑动计算点积。
- 我们探讨了如何利用这个原理去解决更复杂的问题,如识别孤立像素。
- 最后,我们引入了“通道”的概念,解释了如何对彩色图像(多通道输入)应用三维滤波器来提取特征,并生成新的特征图通道。

掌握这些二维卷积的基本操作,是理解现代卷积神经网络如何从图像中学习并识别复杂模式的关键第一步。
033:卷积神经网络中的滤波层

概述
在本节课中,我们将学习卷积神经网络中的一个核心组成部分——滤波层。我们将了解滤波层的构成、工作原理以及如何在实际网络中表示和计算它们。
回顾:滤波器基础
上一节我们介绍了二维滤波器。在此之前,我们还详细探讨了一维滤波器。本节中,我们来看看如何在卷积神经网络中表示滤波层。
在卷积神经网络中,存在不同类型的层,就像前馈神经网络中有隐藏层一样。其中一种层被称为滤波层。自然地,滤波层由滤波器组成。我们将学习这些滤波器在神经网络中的表示方法。
滤波层的工作原理
在神经网络中,每一层都有神经元,我们将它们堆叠起来。但在卷积神经网络中,其表示方式略有不同。
首先,让我们回顾一下上节课学到的滤波器概念。
想象有一张4x4的图像,其中包含一些白色像素和一些深色像素。
在滤波层中,我们首先要选择该层中所需的滤波器数量。例如,我选择在该特定层中使用四个滤波器。
接下来,这四个滤波器会被应用到图像上。假设每个滤波器都是2x2的尺寸。这个2x2的滤波器从图像的一个位置开始,然后在整个图像上滑动,执行卷积操作。
当一个滤波器操作时,输入是4x4的图像。但当四个这样的滤波器在这个基础图像上操作时,会产生四个4x4的图像。因此,我们实际上得到了一个维度为4x4x4的张量。这个深度被称为通道深度。
如果你想了解更多关于图像每个部分如何进行卷积操作的细节,请查看卷积神经网络部分的前两节课。
可视化卷积操作
我想向你展示一个关于卷积操作如何执行的可视化动画。
假设我们观察一个特定的滤波器。这个滤波器从一个特定位置开始,然后向右滑动,并在其经过的每个地方执行卷积操作,最终生成一个4x4的图像。因为有四个这样的滤波器,所以我们会得到一个4x4x4的张量。
让我们看看这个的可视化表示。这里你可以看到一个咖啡杯的图像。
首先,咖啡杯被分成三个通道:红色通道、绿色通道和蓝色通道。现在,请你关注红色通道,我将展示一个特定滤波器是如何应用的。
在红色通道上,应用了10个滤波器。让我们看看应用每个滤波器的结果。当我点击这里时,你会看到滤波器做了什么。你可以看到滤波器滑过图像的每个像素,然后输出一个结果。这个结果仍然保持与原始图像相同的大小。这就是滤波操作的执行方式。
如果你想看得更详细,这就是滤波操作的确切执行过程。我将光标移到左上角,然后慢慢向右移动,再向下移动,再向左移动。每当我在每个瞬间暂停时,你都可以看到正在执行的卷积操作。
滤波器像这样拖过整个图像,然后产生一个结果图像。这正是我在这里展示的:每个滤波器在图像上拖动并执行卷积操作时,都会产生一个相同大小的结果图像。因为有四个这样的滤波器,所以我们得到一个4x4x4的张量,对吗?
多层滤波操作
现在让我们看看在这个张量上操作的另一个滤波层。
这是我的第一个滤波层。现在让我转到第二个滤波层。观察第二个滤波层,其输入是一个4x4x4的张量。
同样,我必须决定两件事:我需要决定该层中滤波器的数量,以及每个滤波器的尺寸。
假设我决定下一层中滤波器的尺寸是2x2。但请记住,它不能仅仅是2x2,我还需要加上通道深度。因此,我在下一层构建的滤波器将是2x2x4。为什么需要添加通道深度?因为现在输入是一个张量。之前应用于图像的滤波器是二维的,但现在这是一个张量。所以,当我观察第二层的滤波器时,它肯定是一个2x2的滤波器,但它也有一个深度维度,因为它将穿过张量中堆叠的所有四个图像。
因此,下一层滤波器的维度将不仅仅是2x2,而是2x2x4。然后,这个2x2x4的滤波器将卷积整个张量。它首先从这里开始,并且也有这个深度,所以它会输出一个值。然后它移动到这里,再到这里,再到这里。这样一个2x2x4的滤波器将产生一个4x4的输出。
现在你可以看到,当一个滤波器在这个张量上操作时,由于滤波器具有与张量深度相同的深度,产生的输出是4x4的。
但在这一层,我们必须做的第二个选择是想要使用的滤波器数量。如果我们决定使用10个滤波器,那么每个滤波器都会创建一个4x4的表示。因此,该层的输出将是4x4x10。如果我们决定使用10个滤波器,就会有10个通道。
让我再重复一遍这部分。每当我们观察一个滤波层时,我们必须决定两件事:滤波器的数量和滤波器的尺寸。首先,滤波器的尺寸是2x2,但请记住,深度是固定的。滤波器的深度必须等于前一层输出的通道维度。因此,前一层输出的通道深度是4,所以这一层滤波器的深度必须是4。因此,滤波器的尺寸将是2x2x4。
这个2x2x4的滤波器将卷积整个张量,当它卷积时,将生成一个4x4的输出。我们必须做的第二个选择是滤波器的数量。如果我们决定使用10个滤波器,每个滤波器都会产生一个4x4的输出。所以,如果我们使用10个滤波器,那么输出将是4x4x10。这就是这个滤波层的输出维度。
然后我们将再次重复这个过程。请记住,如果我们要在下一个滤波层中做同样的过程,首先我们必须决定滤波器的尺寸。假设我使用3x3的尺寸。但同样,请记住,其深度是固定的,因为前一层输出有10个通道深度。所以滤波器的尺寸必须是3x3x10。假设我使用的滤波器数量是20个,那么这一层的输出将是4x4x20。
这就是滤波层在实际中的工作方式。

总结
本节课中,我们一起学习了卷积神经网络中滤波层的工作原理。我们了解到,滤波层需要决定两个关键参数:滤波器的尺寸(包括其深度,必须与前一层输出的通道数匹配)和滤波器的数量。每个滤波器卷积输入张量后会产生一个二维特征图,多个滤波器的输出堆叠起来就构成了新的、具有更多通道深度的输出张量。这是构建深层卷积网络的基础操作。
034:什么是卷积神经网络中的最大池化? 🧠

在本节课中,我们将要学习卷积神经网络中的一个重要组成部分——最大池化层。我们将了解它的定义、工作原理、参数设置以及它在网络中的作用。
在上一节中,我们介绍了卷积神经网络中的过滤层。本节中,我们来看看另一个在CNN实现中常见的层:最大池化层。我们将首先理解它是什么,然后探讨为什么需要使用它。
什么是最大池化层?
首先需要理解的是,过滤层包含需要优化的权重值。而最大池化层没有任何权重。描述这个层只需要两个参数:步长和尺寸。
假设我们有一张9x9的输入图像,每个像素都有随机分配的像素值。如果我们想将此图像通过一个最大池化层,首先需要确定池化窗口的尺寸。例如,如果尺寸是3x3,意味着我们将使用一个3行3列的窗口。
其次需要确定的是步长。步长意味着在移动到下一个池化窗口之前,我们需要跳过多少步。例如,如果步长为3,意味着池化窗口每次移动会留下3个像素的间隔。
最大池化如何工作?
一旦指定了步长和尺寸,最大池化层的工作就非常简单。它将输入图像划分为多个不重叠(或部分重叠,取决于步长)的块。对于每个块,它只取该块内所有像素值的最大值,并将这个最大值作为输出图像中对应位置的像素值。
以下是最大池化操作的核心步骤:
- 将池化窗口(例如3x3)放置在输入图像的左上角。
- 找出该窗口覆盖区域内所有像素的最大值。
- 将这个最大值写入输出图像的对应位置。
- 根据设定的步长,将池化窗口向右(或向下)移动。
- 重复步骤2-4,直到遍历完整个输入图像。
通过这个过程,输出图像的尺寸会显著缩小。如果输入尺寸是 nL * nL * c(其中c是通道数),使用尺寸为 f、步长为 s 的最大池化层后,输出尺寸将变为:
(nL / s) * (nL / s) * c
重要提示:最大池化层不改变通道数。它只改变图像的长度和宽度。
一个具体例子
为了更好地理解,让我们看一个具体例子。假设我们有一个4x4的输入图像,使用一个2x2的池化窗口,步长为2。
输入矩阵:
[ 5, 3, 2, 9 ]
[ 1, 8, 4, 6 ]
[ 7, 0, 3, 1 ]
[ 2, 5, 4, 2 ]
池化过程:
- 第一个2x2块
[5, 3; 1, 8]的最大值是 8。 - 第二个2x2块
[2, 9; 4, 6]的最大值是 9。 - 第三个2x2块
[7, 0; 2, 5]的最大值是 7。 - 第四个2x2块
[3, 1; 4, 2]的最大值是 4。
因此,输出矩阵是一个2x2的图像:
[ 8, 9 ]
[ 7, 4 ]
为什么使用最大池化?
了解了最大池化是什么之后,现在我们来探讨它在卷积神经网络中的关键作用。以下是使用最大池化层的主要优势:
- 特征提取与不变性:通过取局部区域的最大值,最大池化能够提取最显著的特征(如边缘、纹理),并使其对输入图像的小幅平移、旋转更具鲁棒性。
- 降维与减少计算量:最大池化显著减少了特征图的尺寸,从而大幅降低了后续层需要处理的参数数量和计算复杂度,提高了模型的效率。
- 防止过拟合:通过提供一种抽象化的表示,并在一定程度上忽略细节信息,最大池化有助于模型学习更通用的特征,从而降低过拟合的风险。
总结

本节课中我们一起学习了卷积神经网络中的最大池化层。我们了解到,最大池化是一个没有可训练权重的操作层,它通过滑动一个固定尺寸的窗口,并取窗口内像素的最大值来对特征图进行下采样。它的核心参数是窗口尺寸和步长。最大池化的主要作用是提取关键特征、增加模型的空间不变性、减少参数数量以控制计算成本,并帮助防止过拟合。它是构建高效、鲁棒的卷积神经网络架构的重要工具之一。
035:卷积神经网络架构详解 🧠

在本节课中,我们将学习卷积神经网络架构的所有内容。在过去的两次课程中,我们为理解此架构做好了准备。上一节我们介绍了最大池化层,再上一节我们介绍了滤波层。现在,我们已经准备好理解整个卷积神经网络架构及其包含的所有组件。
当你学习CNN或参加任何CNN课程时,通常会看到以这张图开始讲解。学生理解起来非常困难,因为图中包含太多内容,难以理解应该关注什么。
让我们看看这里有哪些不同的组件。这是一张斑马的图像。图中正在进行不同的操作。首先需要注意,这里写着“卷积”。这里的“卷积”指的就是滤波层。我们将复习它的含义。图中还有“池化”,这指的就是最大池化层。因此,首先应用滤波层,然后应用最大池化层,接着再次应用滤波层,再次应用最大池化层。在最后,有一个称为展平层的部分,最后是全连接层。
从组件角度来看,首先是滤波层,其次是最大池化层,第三是展平层,第四是全连接层。图中还有一些与激活函数相关的操作,但它们不能真正称为组件,因为它们是从神经网络中借鉴而来的。我会解释它们的含义。这里有ReLU激活函数,还有Softmax激活函数。这些只是非线性变换。例如,ReLU的作用是:对于x小于0,输出为0;对于x大于等于0,输出为x。因此,在进行卷积操作后,如果存在负的滤波值,它们会被置为0,而正的输出值保持不变。Softmax是必需的,因为这是一个分类问题。例如,如果有马、斑马和狗三个类别,我们想判断图像最接近这三种动物中的哪一种。如果输出显示马的概率是0.2,斑马是0.7,狗是0.1,这意味着有20%的概率是马,70%的概率是斑马,10%的概率是狗。在输出层和这些概率值之间,有一个称为Softmax的层。
本节课的主要目的是对这四大构建模块——滤波层、最大池化层、展平层和全连接层——进行一个宏观概述。让我们开始理解每一层的实际含义。
滤波层 🔍
本节内容基于上一讲的复习,因此复习所有这些概念将很有帮助。当你看到这样的滤波层时,请记住这里发生的是:我们有一张图像。假设这张图像通常有多个通道。让我这样开始:假设这是一张斑马图像,这是一张4x4的图像。首先做的是应用滤波器。滤波器的数量是确定的。假设滤波器大小是2x2。这个2x2的滤波器应用于这张图像,然后在整个图像上进行卷积,产生一个4x4的输出。这里有四个滤波器,因此它将产生一个4x4x4的张量。这就是滤波操作的本质。因此,每当你看一个滤波层时,问自己两个问题:第一,滤波器的大小是多少?在这个例子中是4x4。第二,我想使用多少个滤波器?在这个例子中,我们使用了四个滤波器。
让我们直观地看看这意味着什么。假设我这里有一个咖啡杯,我告诉过你滤波操作。在这一层,我问的第一个问题是:我想要多少个滤波器?我说我想要10个滤波器:1、2、3、4、5、6、7、8、9、10。很好。第二,滤波器的大小是多少?假设我想要一个2x2的滤波器。现在看看第一个滤波器做什么。当我移动到第一个滤波器时,你会看到它在图像上滑动并产生输出图像。如果我点击这里,我们实际上可以更详细地观察这个操作。让我点击这个图像。在这里你可以看到这个操作。假设我有10个滤波器,假设滤波器大小是3x3,然后你可以看到我现在用光标在这里滑动,你可以看到这里正在生成相应的输出。所以你可以看到,每个滤波器——这是一个滤波器——在整个图像上进行卷积,并生成一个与图像大小相同的输出。让我在这里播放这个动画。现在你可以看到动画正在播放。试着观察滤波器在图像上从左向右移动,以及输出在哪里移动。
这就是每个滤波器所做的:每个滤波器执行卷积操作,并产生与图像大小相同的输出。真正重要的是滤波器的数量,因为滤波器的数量将决定深度。例如,这里我使用了10个滤波器。假设输入是64x64,如果我使用10个滤波器,意味着输出是一个64x64x10的张量。这就是滤波层的输出。因此,首先总是决定滤波器的数量,其次总是决定滤波器的大小。这就是滤波层中发生的事情。
让我尝试用张量的形式来解释这一点。假设某一层的输出是4x4x4,因为有四个滤波器。现在你想再应用一个滤波层。首先,你决定滤波器的大小,这将是一个2x2x4的滤波器。因为请记住,输入大小是4x4x4,所以滤波器也必须是一个张量。滤波器将是……让我用橙色。我擦掉这个。所以在这一层,假设……是的,在这一层,假设我将使用一个2x2的滤波器,但我还需要与深度进行卷积,所以滤波器也必须具有深度4。因此,我在这里选择的滤波器是2x2x4。这个滤波器将在整个张量上进行卷积,并产生一个4x4的输出。我还必须决定滤波器的数量。所以如果在这一层我打算使用10个滤波器,每个滤波器的输出是4x4,而我使用了10个滤波器,那么最终输出将是4x4x10。
这就是当你考虑一个滤波层时发生的事情。当你考虑一个滤波层时,选择滤波器的数量,选择每个滤波器的大小,并确定输出。因此,每当你看到像这样的滤波层时,我希望你想象这个可视化层。具体来说,我希望你思考屏幕右侧的这个可视化,正在播放的动画:滤波器在悬停,然后执行卷积操作,输出正在生成。这正是正在发生的事情。这个滤波器在做什么?这里的每个滤波器都在检测一些特定的特征。这里有10个滤波器,对吧?也许这个滤波器在检测咖啡的边缘,也许这个滤波器在检测手柄的曲线形状,也许这个滤波器在检测咖啡中存在的液体纹理。这里的每个滤波器都有特定的目的,当它被训练后,它将执行识别特定特征的任务。这就是滤波层的优势:滤波层从输入图像中提取特征。
现在,除了理解滤波层,术语命名也非常重要。实际上有三件事需要确定:我们讨论过的滤波器数量,我们讨论过的滤波器大小,但还有一个需要确定的参数,那就是步长。
当滤波器在图像上操作时,如果步长等于2,输出图像的大小通常不会与输入相同。例如,当……

本节课中,我们一起学习了卷积神经网络架构的四大核心构建模块:滤波层、最大池化层、展平层和全连接层。我们重点回顾了滤波层的工作原理,包括如何确定滤波器数量和大小,以及其提取图像特征的核心作用。理解这些组件是掌握CNN如何工作的基础。
036:卷积神经网络中的反向传播 🔄

在本节课中,我们将学习卷积神经网络(CNN)中权重优化的核心思想,特别是反向传播算法如何应用于CNN。我们将了解CNN的架构,并解释如何通过梯度下降来更新其参数。
在之前的视频中,我们详细探讨了卷积神经网络的架构。我们研究了四个基本构建块:过滤层、最大池化层、展平层和全连接层。我们看到了这些构建块如何排列,形成一个看起来像这样的卷积神经网络示意图。
通常,输入图像会经过一系列层,例如一个过滤层,然后是一个池化层。过滤层也常被称为带有ReLU激活函数的卷积层。我们会多次重复“过滤-池化”的过程,直到到达展平层,最后是网络末端的全连接层。
这就是卷积神经网络的架构。我们讨论过,有两个构建块的参数需要优化。
首先,在每个过滤层或卷积层中,存在过滤器。过滤器的形状可能是一个张量,例如一个 3x3x4 的过滤器。这些过滤器携带的权重值(W1, W2等)是需要优化的参数。
池化层没有任何参数需要优化,因为它只是取输入区域的最大值。展平层同样没有参数。全连接层则具有权重和偏置,类似于传统的神经网络。
因此,需要优化参数的主要是过滤层(卷积层)和全连接层。在本讲座中,我将用变量 W 来表示所有这些组合参数。
我们需要优化所有参数 W,使得网络的输出能正确反映输入内容。例如,如果输入是斑马的图像,输出有三个类别:马、斑马和狗,那么参数 W 应使得斑马类别的概率最高,而马和狗的概率较低。
上一节我们介绍了CNN中需要优化的参数。本节中,我们来看看如何优化卷积神经网络的权重。
这不是一个深入的理论讲座,我不会展示数学计算。这将更像一个概述性讲座,旨在让你理解反向传播和梯度下降是如何实现的。和本系列一样,我会用一个实际例子来演示。
要理解卷积神经网络的权重如何优化,你需要明白,CNN本质上是一个数学函数,其形式如下:
输出 = CNN(输入图像, 权重W)
它接收图像作为输入,而权重就是过滤层和全连接层中的值。因此,我们需要优化这些权重。
我们遵循的策略与在传统神经网络中使用的非常相似:
- 首先计算损失。损失值取决于网络的预测值和真实值。
- 然后,计算损失相对于所有参数的偏导数(即梯度)。
- 最后,使用梯度下降(或其变体,如带动量的梯度下降、Adam、RMSProp等)来迭代更新每个参数。例如,对于一个参数
W1,在每次迭代中我们这样更新它:
W1 = W1 - 学习率 * (∂损失 / ∂W1)
通过这种方式,我们最终会优化这些参数以降低损失。
为了执行上述优化,一个非常关键的步骤是计算损失相对于所有参数的偏导数(梯度)。
我们实施的策略实际上与神经网络完全相同:
- 步骤一:找到损失相对于参数的偏导数。
- 步骤二:通过梯度下降方法更新权重。
现在,让我们重点看看如何找到这些偏导数。为此,我们需要进行反向传播。
假设我想找到损失相对于某个过滤层中权重的偏导数。为此,我需要:
- 找到损失相对于展平层输入的偏导数。
- 然后,找到池化层输出相对于其输入的偏导数。
- 接着,找到过滤层输出相对于其输入的偏导数。
- 依此类推,直到到达网络的最后层。
你会发现,对于池化层和过滤层,我们需要计算两种关键的偏导数:
- 对于池化层:如果输入是
x,输出是z,我们需要∂z / ∂x。 - 对于过滤层:同样,如果输入是
x,输出是z,我们需要∂z / ∂x。
以下是关于计算这些偏导数的关键点:
对于池化层(最大池化):我们所做的操作是取所有像素中的最大值。事实证明,求最大值算子的偏导数是相当容易的(虽然现在不深入细节,但请记住这是可行的)。
对于过滤层(卷积):如果你观察过滤器,它本质上执行的是点积操作。过滤器在输入上滑动并进行卷积运算,其核心就是点积。因此,求过滤层输出相对于输入的偏导数,最终归结为通过这个点积求导,这完全是可行的。
除此之外,我们在神经网络中已经遇到过并知道如何求导的其他部分,例如ReLU激活函数、Softmax函数和全连接层。
因此,结合对最大池化和卷积点积的求导能力,我们实际上可以处理整个卷积神经网络架构并计算梯度。这就是为什么反向传播在CNN中完全可行,从而使得优化步骤一(计算梯度)能够成功执行。

本节课中,我们一起学习了卷积神经网络中权重优化的核心思想。我们回顾了CNN的架构,明确了需要优化的参数位于过滤层和全连接层。我们了解到,优化过程遵循与标准神经网络相同的模式:计算损失、通过反向传播计算梯度、然后使用梯度下降更新权重。关键在于,CNN中新增的池化层和卷积层都可以进行有效的梯度计算(池化层通过最大值算子,卷积层通过点积运算),这使得整个网络能够通过反向传播进行训练。这就是像Google的Teachable Machine这样的工具背后,CNN能够被训练来区分不同类别图像的基本原理。
037:使用Python构建脑肿瘤分类CNN应用 🧠

在本节课中,我们将动手构建一个卷积神经网络模型,并将其应用于一个实际项目:开发一个能够根据MRI扫描图像判断是否存在脑肿瘤的人工智能模型。这是一个极佳的实践项目,能帮助你深入理解CNN的实际应用。
项目概述与目标
上一节我们介绍了课程目标,本节中我们来看看项目的具体细节和数据。
我们的目标是构建一个AI模型,它可以分析MRI扫描图像,并将其分类为“存在肿瘤”或“不存在肿瘤”。首先,让我们了解项目概览、目标以及我们将要使用的数据集。
以下是数据集的关键信息:
- 数据来源:数据集托管在Kaggle平台上,这是一个进行机器学习项目和竞赛的网站。
- 数据标签:数据集包含两个标签:“yes”(存在肿瘤)和“no”(不存在肿瘤)。
- 数据格式:数据集由大量黑白MRI图像的JPG文件组成。
- 数据可靠性:该数据集获得了社区的高度认可(例如,获得“金星”标识和大量支持),表明其可靠,适合用于本项目。
项目的最终目标是:当输入一张新的MRI图像时,我们的CNN模型能够准确预测其中是否存在脑肿瘤。此外,在本项目结束时,我们还将开发并部署一个交互式仪表盘,以便与朋友、同事分享,或将其添加到个人简历中。
问题背景与意义
在理解了数据集之后,我们有必要思考一下这个项目试图解决的实际问题。
脑肿瘤是指大脑内异常细胞的形成。主要分为两种类型:恶性和良性。医生通过MRI扫描来诊断。我们进行这个项目的原因是,面对海量的MRI扫描图像,医生手动逐一诊断既耗时又费力。人工智能可以在这方面提供帮助,因为它能够在极短时间内完成分类任务,这在患者数量激增(例如疫情期间分析胸部X光片)时尤其有用。
环境设置与包导入
现在我们已经理解了项目和目标,接下来可以进入第二步:设置环境。


在这一部分,我们将加载和导入一些必要的Python包。这里重点介绍两个主要的机器学习包及其重要性。

Keras 是一个高级神经网络API,它提供了丰富的库函数,使得构建和训练机器学习模型(如CNN)变得非常简单。Keras与TensorFlow深度集成,其核心优势在于通过简洁的代码(例如 model.compile(), model.fit())快速定义和训练模型,无需从零开始编写大量底层代码。

Scikit-learn 是一个功能强大的机器学习库。在本项目中,它将主要用于两个便捷功能:1)使用一行代码将数据集分割为训练集和测试集;2)轻松地对标签进行独热编码。

当然,我们还需要一些通用包,如用于科学计算的NumPy、用于数据处理的Pandas以及用于绘图的Matplotlib。
以下是需要导入的包和模块:
import keras
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
数据加载与预处理
导入必要的包之后,下一步是加载数据并进行预处理。
数据加载的具体步骤是:从Kaggle下载数据集,然后上传到Google云端硬盘。接着,在Google Colab中挂载云端硬盘,以便访问存储在那里的数据。
数据预处理的第一步是合并图像。数据集按标签(“yes”和“no”)分别存放在两个文件夹中。我们需要先将所有图像合并到一个列表中,然后再将其分割为训练集和测试集。
具体做法是:假设“no”文件夹有500张图像,“yes”文件夹有500张图像。我们将首先把所有1000张图像路径(或图像数据)追加在一起,并创建对应的标签列表,然后再进行训练/测试分割。

本节课中,我们一起学习了脑肿瘤分类CNN项目的目标、背景意义,并完成了环境设置、必要库的导入,以及数据加载与预处理的第一步规划。在接下来的课程中,我们将继续完成数据读取、模型构建、训练和评估等步骤。

浙公网安备 33010602011771号