Practical Deep Learning for Coders(第一章)

! [ -e /content ] && pip install -Uqq fastbook
import fastbook
fastbook.setup_book()
from fastbook import *

您的深度学习之旅

您好,感谢您让我们加入您的深度学习之旅,无论您现在处于哪个阶段!在本章中,我们将告诉您在本书中可以期待什么,介绍深度学习的关键概念,并在不同的任务上训练我们的第一个模型。无论您是否有技术或数学背景(当然,如果有也很好!);我们撰写这本书是为了让尽可能多的人能够接触到深度学习。

深度学习适合所有人

很多人认为,要在深度学习中取得出色的成果,您需要各种难以获得的东西,但正如您将在本书中看到的,那些人是错的。以下是一些您绝对不需要的东西,仍然可以进行世界级的深度学习。

你不需要什么来进行深度学习

误区(不需要)真相
大量的数学知识只需高中数学即可
大量的数据我们曾在少于50个数据项上取得突破性的结果
大量昂贵的计算机您可以免费获得进行最先进工作的所需资源

深度学习是一种计算机技术,用于提取和转换数据——应用范围从人类语音识别到动物图像分类——通过使用多层神经网络。每一层都从前一层获取输入,并逐步优化它们。这些层通过算法进行训练,最小化错误并提高准确性。这样,网络就学会执行指定的任务。我们将在下一节详细讨论训练算法。

深度学习具有强大的能力、灵活性和简单性。这就是为什么我们相信它应该应用于许多学科,包括社会和物理科学、艺术、医学、金融、科学研究等等。举个个人的例子,尽管没有医学背景,Jeremy 创立了 Enlitic,一家使用深度学习算法来诊断疾病的公司。在成立公司的几个月内,其算法被宣布比放射科医生更准确地识别恶性肿瘤。

以下是数千项任务中的一些示例,在这些领域,深度学习或大量使用深度学习的方法现在是世界上最好的:

  • 自然语言处理(NLP):回答问题;语音识别;摘要文档;分类文档;在文档中查找名称、日期等;搜索提及某个概念的文章
  • 计算机视觉:卫星和无人机图像解释(例如,用于灾难恢复);人脸识别;图像字幕;读取交通标志;在自动驾驶汽车中定位行人和车辆
  • 医学:在放射影像中发现异常,包括CT、MRI和X光图像;统计病理切片中的特征;测量超声波中的特征;诊断糖尿病视网膜病变
  • 生物学:蛋白质折叠;蛋白质分类;许多基因组学任务,如肿瘤-正常测序和分类具有临床可操作性的基因突变;细胞分类;分析蛋白质/蛋白质相互作用
  • 图像生成:图像上色;提高图像分辨率;去除图像噪声;将图像转换为著名艺术家的风格
  • 推荐系统:网络搜索;产品推荐;主页布局
  • 游戏:国际象棋、围棋、大多数Atari视频游戏和许多实时策略游戏
  • 机器人学:处理难以定位的物体(例如,透明、有光泽、缺乏纹理)或难以拾取的物体
  • 其他应用:金融和物流预测、文本转语音等等...

值得注意的是,深度学习有如此多样化的应用,但几乎所有的深度学习都基于一种类型的模型,即神经网络。

但神经网络实际上并不是全新的。为了对该领域有更广泛的视角,值得从一点历史开始。

神经网络:简要历史

1943年,神经生理学家 Warren McCulloch 和逻辑学家 Walter Pitts 联手开发了人工神经元的数学模型。在他们的论文《神经活动中固有思想的逻辑演算》中,他们宣称:

由于神经活动的“全或无”特性,神经事件及其之间的关系可以通过命题逻辑来处理。发现每个网络的行为都可以用这些术语来描述。

McCulloch 和 Pitts 意识到,真实神经元的简化模型可以用简单的加法和阈值表示,如图所示。Pitts 是自学成才的,12岁时就收到了与伟大的 Bertrand Russell 一起在剑桥大学学习的邀请。他没有接受这个邀请,实际上他一生中从未接受任何高级学位或权威职位。他的大部分著名工作都是在他无家可归的时候完成的。尽管他缺乏正式认可的职位,且日益与社会隔绝,他与 McCulloch 的合作具有影响力,并被一位名叫 Frank Rosenblatt 的心理学家所接受。

屏幕截图 2024-10-02 195734.png

自然和人工神经元

Rosenblatt 进一步发展了人工神经元,使其具备学习的能力。更重要的是,他致力于构建第一台实际使用这些原理的设备——Mark I 感知器。在《智能自动机的设计》中,Rosenblatt 写到了这项工作:“我们现在即将见证这样一台机器的诞生——一台能够感知、识别和识别其周围环境的机器,无需任何人工训练或控制。”感知器被构建出来,能够成功地识别简单的形状。

一位名叫 Marvin Minsky 的 MIT 教授(他与 Rosenblatt 在同一所高中只差一届!)与 Seymour Papert 一起撰写了一本名为《感知器》(MIT 出版社)的书,关于 Rosenblatt 的发明。他们展示了单层这些设备无法学习一些简单但关键的数学函数(如 XOR)。在同一本书中,他们还展示了使用多层设备可以解决这些限制。不幸的是,只有他们的第一个观点被广泛认可。结果,全球学术界在接下来的二十年里几乎完全放弃了神经网络。

过去50年中神经网络领域最关键的工作可能是 David Rumelhart、James McClellan 和 PDP 研究小组于1986年由 MIT 出版社发布的多卷本《并行分布式处理》(PDP)。第一章阐述了与 Rosenblatt 类似的希望:

人们比当今的计算机更聪明,因为大脑采用的基本计算架构更适合处理人们擅长的自然信息处理任务的核心方面。...我们将介绍一种用于建模认知过程的计算框架,与其他框架相比,这种框架似乎更接近于大脑可能采用的计算方式。

PDP 在此提出的前提是,传统计算机程序的工作方式与大脑非常不同,这可能是计算机程序在执行大脑容易完成的任务(如识别图像中的物体)时表现不佳的原因。作者声称,PDP 方法“比其他框架更接近”大脑的工作方式,因此可能更善于处理这些类型的任务。

实际上,PDP 中概述的方法与当今神经网络的方法非常相似。该书将并行分布式处理定义为需要:

  1. 一组处理单元
  2. 一个激活状态
  3. 每个单元的输出函数
  4. 单元之间的连接模式
  5. 通过连接网络传播激活模式的传播规则
  6. 一个激活规则,用于将作用于一个单元的输入与该单元的当前状态组合,以产生该单元的输出
  7. 一个学习规则,通过经验来修改连接模式
  8. 系统必须在其中操作的环境

我们将在本书中看到,现代神经网络处理了上述每个要求。

在1980年代,大多数模型都使用了第二层神经元,从而避免了 Minsky 和 Papert 识别的问题(这是他们在上述框架中的“单元之间的连接模式”)。确实,神经网络在80年代和90年代被广泛用于实际项目。然而,再次由于对理论问题的误解,该领域受到了阻碍。理论上,添加一层额外的神经元就足以使这些神经网络近似任何数学函数,但实际上,这样的网络通常太大、太慢,无法发挥作用。

尽管研究人员在30年前就表明,为了获得实际的良好性能,需要使用更多层的神经元,但直到最近十年,这一原则才得到更广泛的认可和应用。神经网络现在终于实现了它们的潜力,得益于使用更多的层,以及计算机硬件的改进、数据可用性的增加和算法调整,使得神经网络可以更快、更容易地训练。我们现在拥有了 Rosenblatt 所承诺的:“一台能够感知、识别和识别其周围环境的机器,无需任何人工训练或控制。”

这就是您将在本书中学习如何构建的。但首先,由于我们将花费大量时间在一起,让我们先相互了解一下……

我们是谁

我们是 Sylvain 和 Jeremy,是您旅程中的向导。我们希望您会发现我们非常适合这个角色。

Jeremy 从事机器学习的使用和教学已有大约30年。他在25年前开始使用神经网络。在此期间,他领导了许多以机器学习为核心的公司和项目,包括创立了第一家专注于深度学习和医学的公司 Enlitic,并担任世界上最大的机器学习社区 Kaggle 的总裁和首席科学家。他与 Dr. Rachel Thomas 一起共同创立了 fast.ai,这是本书所基于的课程的组织。

不时地,您会直接听到我们的声音,在像这样的边栏中来自 Jeremy:

J:大家好,我是 Jeremy!您可能感兴趣的是,我没有任何正式的技术教育。我完成了文学学士学位,主修哲学,成绩并不理想。我更感兴趣的是做实际项目,而不是理论学习,所以我在大学期间全职在一家名为 McKinsey and Company 的管理咨询公司工作。如果您是那种愿意亲自动手构建东西,而不是花费数年学习抽象概念的人,那么您会理解我的想法!请留意来自我的边栏,了解最适合那些数学或正式技术背景较少的人(也就是像我这样的人)的信息……

另一方面,Sylvain 对正式的技术教育非常了解。事实上,他撰写了10本数学教科书,涵盖了整个高级法语数学课程!

S:与 Jeremy 不同,我没有花费多年时间编写代码和应用机器学习算法。相反,我最近通过观看 Jeremy 的 fast.ai 课程视频进入了机器学习世界。因此,如果您是那种没有打开终端并在命令行编写命令的人,那么您会理解我的感受!请留意来自我的边栏,了解最适合那些在数学或正式技术背景更强但缺乏实际编码经验的人(也就是像我这样的人)的信息……

fast.ai 课程已经被成千上万的学生学习,来自各行各业,来自世界各地。Sylvain 脱颖而出,成为 Jeremy 见过的最令人印象深刻的学生,这使他加入了 fast.ai,并与 Jeremy 一起成为 fastai 软件库的共同作者。

所有这些意味着在我们之间,您拥有了两全其美的优势:他们比任何人都更了解该软件的人,因为他们编写了它;一位数学专家和一位编码和机器学习专家;同时也了解在数学上是相对局外人,以及在编码和机器学习方面是相对局外人的感觉。

任何看过体育比赛的人都知道,如果您有一个两人解说团队,那么您还需要第三个人来做“特别评论”。我们的特别评论员是 Alexis Gallagher。Alexis 拥有非常多样化的背景:他曾是数学生物学研究员、剧本作家、即兴表演者、McKinsey 咨询师(就像 Jeremy 一样!)、Swift 程序员和 CTO。

A:我决定是时候学习这些 AI 东西了!毕竟,我几乎尝试了其他所有东西……但我并没有构建机器学习模型的背景。不过……能有多难呢?我将在整本书中学习,就像您一样。请留意我的边栏,了解我在旅途中发现的学习技巧,希望您也会发现它们有用。

如何学习深度学习

哈佛大学教授 David Perkins,著有《整体学习》(Making Learning Whole,Jossey-Bass),对教学有很多见解。基本思想是教授整个游戏。这意味着,如果您正在教授棒球,您首先带人们去看棒球比赛或让他们参与其中。您不会教他们如何从头开始缠绕棉线制作棒球、抛物线的物理学或球在球棒上的摩擦系数。

哥伦比亚大学数学博士、前布朗大学教授和K-12数学教师 Paul Lockhart 在有影响力的文章《一位数学家的哀叹》中想象了一个噩梦般的世界,在这个世界中,音乐和艺术的教学方式就像数学的教学方式一样。孩子们在学习乐谱和理论十多年之前,不能听音乐或演奏音乐,在课堂上将乐谱移调到不同的调上。在艺术课上,学生们学习颜色和工具,但在大学之前不允许真正绘画。听起来荒谬吗?这就是数学的教学方式——我们要求学生花费数年进行机械记忆和学习枯燥、脱节的基础知识,我们声称这些基础知识将在很久以后才会有回报,当时他们中的大多数人已经放弃了这个学科。

不幸的是,这是许多关于深度学习的教学资源的起点——要求学习者按照 Hessian 的定义和损失函数的泰勒近似定理进行学习,而不提供实际工作的代码示例。我们并不是在抨击微积分。我们热爱微积分,Sylvain 甚至在大学教授过它,但我们不认为这是学习深度学习的最佳起点!

在深度学习中,如果您有动力改进您的模型以使其变得更好,那将会非常有帮助。那时,您才开始学习相关的理论。但您需要首先拥有模型。我们几乎通过真实的例子教授一切。当我们构建这些示例时,我们会越来越深入,并将向您展示如何使您的项目越来越好。这样,您将逐步学习到所有相关的理论基础,以上下文的方式,在这种方式下,您将看到它为什么重要以及它是如何工作的。

因此,这里是我们对您的承诺。贯穿整本书,我们将遵循以下原则:

  • 教授整个游戏。我们将首先展示如何使用一个完整、可用、最先进的深度学习网络来解决现实世界的问题,使用简单、富有表现力的工具。然后我们将逐渐深入了解这些工具是如何构建的,构建这些工具的工具是如何构建的,等等……
  • 始终通过示例教学。我们将确保有一个您可以直观理解的上下文和目的,而不是从代数符号操作开始。
  • 尽可能简化。我们花了数年时间构建工具和教学方法,使以前复杂的主题变得非常简单。
  • 消除障碍。深度学习一直以来都是一个非常排他的游戏。我们正在将其开放,确保每个人都可以参与。

深度学习最难的部分是手工艺性的:如何知道您是否有足够的数据,数据是否格式正确,您的模型是否正确训练,如果不是,您应该怎么做?这就是为什么我们相信通过实践来学习。与基本的数据科学技能一样,深度学习只有通过实践经验才能提高。试图花费太多时间在理论上可能适得其反。关键是要编写代码并尝试解决问题:理论可以稍后再来,当您有了上下文和动机时。

旅途中会有困难的时候。有时候您会感到困惑。不要放弃!回到书中找到您确定没有困惑的最后一部分,然后从那里开始慢慢阅读,找到第一个不清楚的地方。然后尝试自己进行一些代码实验,并在网上搜索您遇到的问题的更多教程——通常您会发现一些不同的角度可能会帮助它点击。同样,第一次阅读时不理解一切(尤其是代码)是正常的。试图在继续之前按顺序理解材料有时会很困难。有时候,事情会在您从后面的部分获得更多上下文、获得更大的整体图景后突然清晰起来。因此,如果您在某一部分卡住了,尝试继续阅读,并记下稍后再回来。

请记住,您不需要任何特定的学术背景就可以在深度学习中取得成功。许多重要的突破是由没有博士学位的人在研究和工业界取得的,例如 Alec Radford 所撰写的《使用深度卷积生成对抗网络的无监督表示学习》——过去十年中最有影响力的论文之一,被引用了5000多次,当时他还是一名本科生。即使在特斯拉,他们正努力解决制造自动驾驶汽车的极其艰巨的挑战,首席执行官 Elon Musk 说:

确实不需要博士学位。重要的是对 AI 的深刻理解以及以实际有用的方式实现神经网络的能力(后一点才是真正困难的)。不在乎您是否甚至没有高中毕业。

然而,要取得成功,您需要将本书中所学应用到个人项目中,并始终坚持不懈。

您的项目和心态

无论您是想训练您的模型来区分叶子上的植物是否有病害、自动生成编织图案、从 X 光片中诊断肺结核,还是确定浣熊何时使用您的猫门,我们将让您尽快使用深度学习来解决自己的问题(通过他人的预训练模型),然后逐步深入细节。您将在下一章的前30分钟内学习如何使用深度学习以最先进的准确性解决您自己的问题!(如果您现在迫不及待想要开始编码,可以直接跳到那里。)有人认为,要使用像谷歌那样的数据和计算资源才能进行深度学习,但事实并非如此。

那么,哪些任务适合做为测试案例?您可以训练您的模型来区分毕加索和莫奈的画作,或者挑选您女儿的照片而不是您儿子的照片。专注于您的爱好和激情是有帮助的——在刚开始时设定四五个小项目往往比努力解决一个大问题效果更好。由于很容易陷入困境,过早尝试过于雄心勃勃的目标往往会适得其反。然后,一旦您掌握了基础知识,目标是完成一些让您真正自豪的事情!

J:深度学习几乎可以用于任何问题。例如,我的第一个创业公司是一家名为 FastMail 的公司,于1999年推出,提供增强的电子邮件服务(至今仍在提供)。2002年,我设置它使用了一种原始形式的深度学习,即单层神经网络,帮助分类电子邮件并阻止客户收到垃圾邮件。

在擅长深度学习的人中,共同的性格特征包括游戏性和好奇心。已故物理学家 Richard Feynman 就是一个可能在深度学习方面表现出色的例子:他对亚原子粒子的运动的理解来自于他对盘子在空中旋转时如何摇摆的兴趣。

现在让我们专注于您将学习的内容,从软件开始。

软件:PyTorch、fastai 和 Jupyter(以及为什么这并不重要)

我们使用数百个机器学习项目,使用数十个不同的包和许多不同的编程语言。在 fast.ai,我们使用当今大多数主要的深度学习和机器学习包编写了课程。PyTorch 于2017年推出后,我们花了1000多个小时进行测试,然后决定将来在课程、软件开发和研究中使用它。从那时起,PyTorch 已成为世界上增长最快的深度学习库,已经用于顶级会议上的大多数研究论文。这通常是工业界使用的领先指标,因为这些论文最终会被用于商业产品和服务。我们发现 PyTorch 是最灵活、最具表现力的深度学习库。它不会在速度和简单性之间进行权衡,而是同时提供两者。

PyTorch 最适合作为低级基础库,提供高级功能的基本操作。fastai 库是在 PyTorch 之上添加这些高级功能的最流行的库。它特别适合本书的目的,因为它在软件架构中提供了深度分层(甚至有一篇关于这种分层 API 的同行评审学术论文)。在本书中,随着我们深入深度学习的基础知识,我们也将深入 fastai 的各个层次。本文涵盖了 fastai 库的第2版,这是一个从头开始的重写,提供了许多独特的功能。

然而,您学习什么软件并不重要,因为切换到另一个库只需要几天的时间。真正重要的是正确学习深度学习的基础和技术。我们的重点将是使用清晰表达您需要学习的概念的代码。当我们教授高级概念时,我们将使用高级的 fastai 代码。当我们教授低级概念时,我们将使用低级的 PyTorch,甚至是纯 Python 代码。

如果您觉得现在似乎有新的深度学习库正在迅速出现,那么您需要为未来几个月和几年中更快的变化速度做好准备。随着越来越多的人进入这个领域,他们将带来更多的技能和想法,并尝试更多的东西。您应该假设今天您学习的任何特定库和软件在一两年内都将过时。想想在网络编程领域发生的库和技术栈的变化数量——这比深度学习更成熟、增长更缓慢的领域。我们坚信,学习的重点应该是理解底层技术以及如何在实践中应用它们,以及如何在新的工具和技术发布时快速建立专业知识。

到本书结束时,您将理解 fastai 内部的几乎所有代码(以及大部分的 PyTorch),因为在每一章中,我们都将深入到训练模型的每个阶段。这意味着您将学习现代深度学习中最重要的最佳实践——不仅了解如何使用它们,还了解它们的真正工作原理和实现方式。如果您想在另一个框架中使用这些方法,您将拥有必要的知识来做到这一点。

由于学习深度学习最重要的是编写代码和进行实验,因此拥有一个优秀的代码实验平台非常重要。最流行的编程实验平台称为 Jupyter。这将是我们在整个书中使用的。我们将向您展示如何使用 Jupyter 来训练和实验模型,并检查数据预处理和模型开发管道的每个阶段。Jupyter Notebook 是用于 Python 数据科学的最流行工具,理由充分。它功能强大、灵活且易于使用。我们认为您会喜欢它!

让我们看看它在实践中是如何工作的,并训练我们的第一个模型。

您的第一个模型

正如我们之前所说,我们将教您如何做事,然后再解释它们为何有效。遵循这种自上而下的方法,我们将从实际训练一个图像分类器开始,以近乎100%的准确率识别狗和猫。要训练这个模型并运行我们的实验,您需要进行一些初始设置。别担心,它看起来并不像看起来那么难。

S:即使设置部分看起来一开始令人望而生畏,也不要跳过它,特别是如果您几乎没有或没有使用终端或命令行的经验。大部分实际上并不需要,您会发现最简单的服务器可以用您常用的网络浏览器进行设置。为了学习,关键是您在阅读本书的同时运行自己的实验。

获取 GPU 深度学习服务器

要完成本书中的几乎所有内容,您都需要访问一台带有 NVIDIA GPU 的计算机(不幸的是,其他品牌的 GPU 尚未被主要的深度学习库完全支持)。然而,我们不建议您购买一个;实际上,即使您已经有了,我们也不建议您立即使用它!设置计算机需要时间和精力,而您现在需要将所有精力集中在深度学习上。因此,我们建议您租用一台已经预装了您所需一切的计算机,并准备好使用。使用时的费用可能低至每小时0.25美元,甚至有些选项是免费的。

术语:图形处理单元(GPU):也称为显卡。计算机中一种特殊的处理器,可以同时处理成千上万个单一任务,特别设计用于在计算机上显示3D环境以玩游戏。这些基本任务与神经网络所做的非常相似,因此 GPU 可以比普通 CPU 更快地运行神经网络。所有现代计算机都包含 GPU,但很少包含深度学习所需的那种 GPU。

最佳的 GPU 服务器选择将随着时间的推移而变化,因为公司会来来去去,价格也会发生变化。我们在本书的网站上维护了我们推荐的选项列表,所以现在就去那里,按照说明连接到 GPU 深度学习服务器。别担心,在大多数平台上设置只需大约两分钟,许多平台甚至不需要任何付款,甚至不需要信用卡即可开始。

A:我的两分钱建议:听从这个建议!如果您喜欢计算机,您可能会想要设置自己的机器。小心!这可以实现,但涉及的事情出乎意料地多,而且会分散注意力。这本书之所以不是名为《您想知道的有关 Ubuntu 系统管理、NVIDIA 驱动程序安装、apt-get、conda、pip 和 Jupyter Notebook 配置的一切》,是有充分理由的。那将是一本独立的书。作为在工作中设计和部署我们的生产机器学习基础设施的人,我可以证明它有其满足感,但它与建模的关系就像维护飞机与驾驶飞机的关系一样。

每个网站上列出的选项都包含一个教程;完成教程后,您将看到如图所示的屏幕。

Jupyter Notebook 的初始视图

现在,您已准备好运行您的第一个 Jupyter Notebook!

术语:Jupyter Notebook:一款软件,允许您在单个交互式文档中包含格式化文本、代码、图像、视频等等。Jupyter 因其广泛的使用和对许多学术领域和工业界的巨大影响,获得了软件的最高荣誉——ACM 软件系统奖。Jupyter Notebook 是数据科学家用于开发和与深度学习模型交互的最广泛使用的软件。

运行您的第一个 Notebook

笔记本按章节编号,然后按笔记本编号进行标记,因此它们与本书中呈现的顺序相同。因此,您将看到列出的第一个笔记本就是您现在需要使用的笔记本。您将使用这个笔记本来训练一个可以识别狗和猫照片的模型。为此,您将从 fast.ai 数据集集合中下载一个狗和猫照片的数据集,并使用它来训练模型。数据集只是一些数据——可以是图像、电子邮件、财务指标、声音或其他任何东西。有许多可供训练模型的免费数据集。其中许多数据集是由学者创建的,以帮助推动研究,有些是为竞赛而提供的(有竞赛,数据科学家可以竞争谁的模型最准确!),有些是其他过程的副产品(如财务申报)。

注意:完整和精简的笔记本:有两个文件夹包含不同版本的笔记本。full 文件夹包含用于创建您现在正在阅读的书的确切笔记本,包含所有的文章和输出。stripped 版本具有相同的标题和代码单元格,但删除了所有输出和文章。在阅读章节后,我们建议您通过精简的笔记本进行操作,关闭书本,看看在执行之前能否猜出每个单元格会显示什么。还可以尝试回忆代码在演示什么。

要打开一个笔记本,只需点击它。笔记本将打开,看起来像这样(请注意,不同平台可能在细节上略有不同;您可以忽略这些差异)。

笔记本示例

一个笔记本由单元格组成。主要有两种类型的单元格:

  1. 包含格式化文本、图像等的单元格。这些使用一种称为 Markdown 的格式,您将很快学习它。
  2. 包含可以执行的代码的单元格,输出将立即显示在下面(可以是纯文本、表格、图像、动画、声音,甚至是交互式应用程序)。

Jupyter 笔记本可以处于两种模式之一:编辑模式或命令模式。在编辑模式下,键盘输入将像通常一样输入单元格中的字母。然而,在命令模式下,您不会看到任何闪烁的光标,键盘上的按键将具有特殊功能。

在继续之前,按键盘上的 Escape 键切换到命令模式(如果您已经在命令模式下,这无所谓,所以现在按下它以防万一)。要查看所有可用功能的完整列表,请按 H;按 Escape 退出此帮助屏幕。请注意,在命令模式下,与大多数程序不同,命令不需要您按住 Control、Alt 或类似的键——您只需按下所需的字母键。

您可以按 C 复制一个单元格(需要先选择单元格,单元格周围会有轮廓;如果尚未选择,单击它一次)。然后按 V 粘贴其副本。

单击以 # CLICK ME 开头的单元格以选择它。该行的第一个字符表示后面的是 Python 中的注释,因此在执行单元格时会被忽略。单元格的其余部分,无论您是否相信,都是一个完整的系统,用于创建和训练用于识别猫和狗的最先进的模型。那么,让我们现在训练它吧!为此,只需在键盘上按 Shift-Enter,或按工具栏上的播放按钮。然后等待几分钟,同时发生以下事情:

  1. 一个名为 Oxford-IIIT Pet Dataset 的数据集,包含来自37个不同品种的7349张猫和狗的图像,将从 fast.ai 数据集集合下载到您正在使用的 GPU 服务器,然后解压缩。
  2. 一个已经在130万张图像上训练过的预训练模型,使用了一个竞赛获胜的模型,将从互联网上下载。
  3. 预训练模型将使用迁移学习的最新进展进行微调,创建一个专门用于识别狗和猫的模型。

前两个步骤只需在您的 GPU 服务器上运行一次。如果您再次运行该单元格,它将使用已经下载的数据集和模型,而不是再次下载。让我们看看单元格的内容和结果:

# CLICK ME
from fastai.vision.all import *
path = untar_data(URLs.PETS)/'images'

def is_cat(x): return x[0].isupper()
dls = ImageDataLoaders.from_name_func(
    path, get_image_files(path), valid_pct=0.2, seed=42,
    label_func=is_cat, item_tfms=Resize(224))

learn = vision_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(1)
epoch	train_loss	valid_loss	error_rate	time
0	0.180385	0.023942	0.006766	00:16
epoch	train_loss	valid_loss	error_rate	time
0	0.056023	0.007580	0.004060	00:20

您可能不会看到与书中完全相同的结果。在训练模型时,有很多小的随机变化来源。然而,在这个例子中,我们通常看到错误率远低于0.02。

重要提示:训练时间:根据您的网络速度,下载预训练模型和数据集可能需要几分钟。运行 fine_tune 可能需要一分钟左右。本书中的模型通常需要几分钟的训练时间,您的模型也是如此,因此最好在这段时间内制定充分利用时间的好方法。例如,在您的模型训练时继续阅读下一节,或者打开另一个笔记本并将其用于一些编码实验。

边栏:本书是在 Jupyter Notebook 中编写的

我们使用 Jupyter Notebook 编写了这本书,因此对于本书中的几乎每个图表、表格和计算,我们都会向您展示完全相同的代码,以便您自己复制。这就是为什么在本书中,您经常会看到一些代码,紧跟着一个表格、一张图片或一些文本。如果您访问本书的网站,您将找到所有的代码,您可以尝试运行并修改每个示例。

您刚刚看到了一个输出表格的单元格在书中是如何显示的。以下是一个输出文本的单元格示例:

1+1
2

Jupyter 将始终打印或显示最后一行的结果(如果有的话)。例如,下面是一个输出图像的单元格示例:

img = PILImage.create(image_cat())
img.to_thumb(192)

结束边栏

那么,我们如何知道这个模型是否好?在表格的最后一列,您可以看到错误率,即被错误分类的图像比例。错误率作为我们的指标——我们选择的衡量模型质量的指标,直观且易于理解。正如您所看到的,该模型几乎完美无缺,即使训练时间只有几秒钟(不包括一次性下载数据集和预训练模型)。事实上,您已经达到的准确性远远超过10年前任何人所取得的成绩!

最后,让我们检查一下这个模型是否真的有效。去拿一张狗或猫的照片;如果您手头没有,只需搜索 Google Images 并下载您在那里找到的图像。现在执行定义了 uploader 的单元格。它将输出一个您可以点击的按钮,这样您就可以选择您想要分类的图像:

#hide_output
uploader = widgets.FileUpload()
uploader
FileUpload(value={}, description='Upload')

一个上传按钮

现在,您可以将上传的文件传递给模型。确保这是单个狗或猫的清晰照片,而不是线条图、卡通或类似的东西。笔记本将告诉您它认为它是狗还是猫,以及它的信心有多大。希望您会发现您的模型做得很好:

#hide
# For the book, we can't actually click an upload button, so we fake it
uploader = SimpleNamespace(data = ['images/chapter1_cat_example.jpg'])

img = PILImage.create(uploader.data[0])
is_cat,_,probs = learn.predict(img)
print(f"这是猫吗?: {is_cat}.")
print(f"它是猫的概率: {probs[1].item():.6f}")
这是猫吗?: True.
它是猫的概率: 1.000000

祝贺您完成了您的第一个分类器!

但这意味着什么?您实际上做了什么?为了解释这一点,让我们再次放大,看看大图景。

什么是机器学习?

您的分类器是一个深度学习模型。如前所述,深度学习模型使用神经网络,它最早可追溯到20世纪50年代,并且由于最近的进展而变得强大。

另一个关键的背景部分是,深度学习只是机器学习中一个现代的领域。为了理解您在上一节中训练自己的分类模型的本质,您不需要了解深度学习。只需看看您的模型和训练过程如何是这些概念的例子,这些概念适用于一般的机器学习。

因此,在本节中,我们将描述机器学习是什么。我们将看看关键概念,并展示它们如何追溯到最初介绍它们的文章。

机器学习与常规编程一样,是让计算机完成特定任务的一种方式。但是,如果我们使用常规编程来完成我们刚才所做的事情:在照片中识别狗和猫,我们该怎么做呢?我们将不得不为计算机写下完成任务所需的确切步骤。

通常,当我们编写程序时,为计算机写下完成任务的步骤并不难。我们只需考虑如果我们必须手动完成任务,我们会采取哪些步骤,然后将它们翻译成代码。例如,我们可以编写一个排序列表的函数。通常,我们会编写一个看起来像这样的函数(其中输入可能是未排序的列表,结果是排序的列表)。

# 传统程序的示意图
# 输入 -> 程序 -> 结果

但对于在照片中识别物体,这有点棘手;当我们在图片中识别物体时,我们采取的步骤是什么?我们真的不知道,因为这一切都在我们的大脑中发生,我们没有意识到!

早在计算机时代的黎明,1949年,IBM 的研究员 Arthur Samuel 开始研究一种不同的方法来让计算机完成任务,他称之为机器学习。在他1962年的经典文章《人工智能:自动化的前沿》中,他写道:

为此类计算对计算机进行编程充其量是一项困难的任务,这主要不是因为计算机本身存在任何内在的复杂性,而是因为需要以最令人沮丧的细节逐步列出整个过程。正如任何程序员都会告诉您的那样,计算机是巨大的白痴,而不是巨大的大脑。

他的基本思想是:不要告诉计算机解决问题的确切步骤,而是向它展示需要解决的问题的示例,让它自己弄清楚如何解决。这证明是非常有效的:到1961年,他的跳棋程序已经学到了很多东西,甚至击败了康涅狄格州的冠军!以下是他对自己想法的描述(来自同一篇文章):

假设我们安排某种自动方法,根据实际性能来测试任何当前的权重分配的有效性,并提供一种机制来改变权重分配,以最大化性能。我们不必深入研究此类过程的细节,就可以看到它可以完全自动化,并且可以看到这样编程的机器将从其经验中“学习”。

在这段简短的陈述中,嵌入了许多强大的概念:

  1. “权重分配”的概念
  2. 每个权重分配都有一些“实际性能”
  3. 需要有一种“自动方法”来测试该性能
  4. 需要一种“机制”(即另一个自动过程)通过改变权重分配来提高性能

让我们逐一理解这些概念,以了解它们在实践中是如何结合在一起的。首先,我们需要理解 Samuel 所说的权重分配是什么意思。

权重只是变量,权重分配是这些变量的特定值选择。程序的输入是它处理以产生结果的值——例如,接受图像像素作为输入,并返回分类“狗”作为结果。程序的权重分配是定义程序如何操作的其他值。

由于它们将影响程序,它们在某种意义上是另一种输入,因此我们将更新我们在上面的基本图,并将其替换为包含权重分配的图。

# 使用权重分配的程序的示意图
# 输入 -> 模型(包含权重)-> 结果

我们将盒子的名称从程序更改为模型。这是为了遵循现代术语,并反映模型是一种特殊的程序:它可以根据权重做许多不同的事情。它可以以多种不同的方式实现。例如,在 Samuel 的跳棋程序中,不同的权重值将导致不同的跳棋策略。

(顺便说一下,Samuel 所称的“权重”在当今通常被称为模型参数,以防您遇到过该术语。术语权重保留用于特定类型的模型参数。)

接下来,Samuel 说我们需要一种自动方法,根据实际性能来测试任何当前的权重分配的有效性。在他的跳棋程序中,模型的“实际性能”将是它的下棋水平。您可以通过设置它们彼此对弈,看看哪个通常获胜,来自动测试两个模型的性能。

最后,他说我们需要一种机制来改变权重分配,以最大化性能。例如,我们可以查看获胜模型和失败模型之间的权重差异,并将权重稍微进一步调整到获胜的方向。

现在我们可以看到他为什么说这样的过程可以完全自动化,并且可以看到这样编程的机器将从其经验中“学习”。当权重的调整也是自动化时,学习将变得完全自动化——也就是说,而不是我们手动调整权重来改进模型,而是依靠基于性能产生调整的自动机制。

我们现在可以看到,Samuel 所设想的训练机器学习模型的全貌如图所示。

# 训练机器学习模型的示意图
# 输入 -> 模型(包含权重)-> 结果
# 结果 -> 性能
# 性能 -> 权重(更新)

请注意模型的结果(例如,在跳棋游戏中的移动)和其性能(例如,它是否赢得了比赛,或者它赢得的速度有多快)之间的区别。

还要注意,一旦模型经过训练——也就是说,一旦我们选择了最终的、最好的、最喜欢的权重分配——那么我们可以认为权重是模型的一部分,因为我们不再改变它们。

因此,实际使用训练后的模型看起来像这样。

# 使用训练好的模型作为程序
# 输入 -> 模型 -> 结果

这看起来与我们最初的图表相同,只是将程序替换为模型。这是一个重要的见解:训练好的模型可以像常规计算机程序一样对待。

术语:机器学习:通过允许计算机从其经验中学习,而不是通过手动编码每个步骤来训练程序。

什么是神经网络?

想象一下跳棋程序的模型可能是什么样子并不难。可能会有一系列编码的跳棋策略,以及某种搜索机制,然后权重可以改变策略的选择方式、在搜索期间关注棋盘的哪些部分等等。但对于图像识别程序,或者理解文本,或者我们可能想象的许多其他有趣的问题,模型可能是什么样子并不明显。

我们希望有某种函数,它足够灵活,可以用于解决任何给定的问题,只需改变其权重。令人惊讶的是,这个函数实际上存在!它就是神经网络,我们已经讨论过。也就是说,如果您将神经网络视为数学函数,事实证明它是一个非常灵活的函数,取决于其权重。一个称为通用近似定理的数学证明表明,该函数可以在理论上以任何精度解决任何问题。神经网络如此灵活意味着,在实践中,它们通常是合适的模型类型,您可以将精力集中在训练它们的过程中——也就是说,找到好的权重分配。

但训练过程呢?可以想象,您可能需要为每个问题找到一种新的“机制”来自动更新权重。这将是繁琐的。我们希望这里也有一种完全通用的方法来更新神经网络的权重,以改进其在任何给定任务上的表现。方便的是,这也存在!

这就是随机梯度下降(SGD)。我们将在后面的章节中详细了解神经网络和 SGD 是如何工作的,以及解释通用近似定理。目前,我们将使用 Samuel 自己的话:我们不必深入研究此类过程的细节,就可以看到它可以完全自动化,并且可以看到这样编程的机器将从其经验中“学习”。

J:别担心,SGD 和神经网络都不是数学上复杂的。它们几乎完全依赖于加法和乘法来完成工作(但它们确实做了大量的加法和乘法!)。当学生们看到细节时,我们听到的主要反应是:“就这些吗?”

换句话说,回顾一下,神经网络是一种特定的机器学习模型,完全符合 Samuel 的原始概念。神经网络之所以特殊,是因为它们高度灵活,这意味着只需找到合适的权重,就可以解决异常广泛的问题。这很强大,因为随机梯度下降为我们提供了一种自动找到这些权重值的方法。

现在我们已经放大了,让我们现在再缩小回来,使用 Samuel 的框架重新审视我们的图像分类问题。

我们的输入是图像。我们的权重是神经网络中的权重。我们的模型是一个神经网络。我们的结果是神经网络计算的值,如“狗”或“猫”。

那么下一步,自动测试任何当前权重分配的有效性的自动方法是什么?确定“实际性能”很容易:我们可以简单地将模型的性能定义为其预测正确答案的准确性。

将这一切放在一起,并假设 SGD 是我们更新权重分配的机制,我们可以看到我们的图像分类器是一个机器学习模型,就像 Samuel 所设想的那样。

一些深度学习术语

Samuel 工作于20世纪60年代,从那时起术语发生了变化。以下是现代深度学习中对于我们已经讨论的所有部分的术语:

  • 模型的函数形式称为其架构(但要小心——有时人们使用模型作为架构的同义词,因此这可能会令人困惑)。
  • 权重被称为参数
  • 自变量是用于计算预测的数据,不包括标签。
  • 模型的结果称为预测
  • 性能的度量称为损失
  • 损失不仅取决于预测,还取决于正确的标签(也称为目标或因变量);例如,“狗”或“猫”。
  • 我们的图表现在看起来像这样:
# 详细的训练循环
# 输入、自变量 -> 模型(架构,参数)-> 预测
# 预测、标签 -> 损失
# 损失 -> 参数(更新)

机器学习的固有局限性

从这个图表中,我们现在可以看到训练深度学习模型的一些基本内容:

  • 没有数据就无法创建模型。
  • 模型只能学习训练时输入数据中看到的模式。
  • 这种学习方法只会创建预测,而不是推荐行动。
  • 仅有输入数据的示例是不够的;我们还需要这些数据的标签(例如,仅有狗和猫的图片不足以训练模型;我们需要为每张图片提供标签,说明哪些是狗,哪些是猫)。

通常来说,我们已经看到大多数声称没有足够数据的组织,实际上是指没有足够的标记数据。如果任何组织有兴趣在实践中使用模型,那么他们可能有一些输入,他们计划用模型来处理。并且可能他们已经以其他方式(例如,手动或使用某些启发式程序)完成了这项工作,所以他们有这些过程的数据!例如,放射科诊所几乎肯定会有一个医疗扫描的存档(因为他们需要能够检查患者随时间的进展),但这些扫描可能没有包含诊断或干预的结构化标签(因为放射科医生通常会创建自由文本的自然语言报告,而不是结构化数据)。我们将在本书中讨论许多标记方法,因为它在实践中是一个非常重要的问题。

由于这些类型的机器学习模型只能进行预测(即,试图复制标签),这可能会导致组织目标和模型能力之间存在显著差距。例如,在本书中,您将学习如何创建一个推荐系统,可以预测用户可能购买的产品。这通常用于电子商务,例如通过显示排名最高的商品来自定义主页上显示的产品。但此类模型通常是通过查看用户及其购买历史(输入)以及他们继续购买或查看的商品(标签)来创建的,这意味着模型可能会告诉您用户已经拥有或已经知道的产品,而不是他们最有可能感兴趣的新产品。这与您当地书店的专家可能做的事情非常不同,他们会通过提问来了解您的品味,然后告诉您您从未听说过的作者或系列。

另一个关键见解来自于考虑模型如何与其环境交互。这可能会创建反馈回路,如下所述:

  • 基于过去逮捕地点创建了一个预测性警务模型。实际上,这并不是在预测犯罪,而是在预测逮捕,因此部分只是反映了现有警务流程中的偏见。
  • 执法人员可能会使用该模型来决定将警力活动集中在哪里,导致这些地区的逮捕增加。
  • 关于这些额外逮捕的数据将被反馈回去,用于重新训练未来版本的模型。
  • 这是一个正反馈回路,模型使用得越多,数据就越偏颇,使模型更加偏颇,依此类推。

反馈回路也可能在商业环境中造成问题。例如,视频推荐系统可能偏向于推荐那些视频观看者最多的内容(例如,阴谋论者和极端主义者往往比平均水平观看更多在线视频内容),导致这些用户增加他们的视频消费,导致推荐更多此类视频。我们将在后面的章节中更详细地讨论这个主题。

现在您已经看到了理论基础,让我们回到我们的代码示例,详细了解代码如何对应于我们刚才描述的过程。

我们的图像识别器如何工作

让我们看看我们的图像识别器代码如何映射到这些想法。我们将每一行放入一个单独的单元格,并查看每一行在做什么(我们不会解释每个参数的每个细节,但会给出重要部分的描述;完整的细节将在本书后面介绍)。

第一行导入了所有 fastai.vision 库。

from fastai.vision.all import *

这为我们提供了创建各种计算机视觉模型所需的所有函数和类。

J:许多 Python 程序员建议避免像这样导入整个库(使用 import * 语法),因为在大型软件项目中,它可能会导致问题。然而,对于像 Jupyter Notebook 这样的交互式工作,它非常有效。fastai 库专门设计为支持这种交互式使用,它只会将必要的部分导入到您的环境中。

第二行从 fast.ai 数据集集合中下载标准数据集(如果之前未下载),将其解压缩(如果之前未解压缩),并返回一个包含解压缩位置的 Path 对象:

path = untar_data(URLs.PETS)/'images'

第三行定义了一个函数 is_cat,根据数据集创建者提供的文件名规则标记猫:

def is_cat(x): return x[0].isupper()

第四行中,我们在告诉 fastai 我们拥有什么类型的数据集以及它是如何结构化的:

dls = ImageDataLoaders.from_name_func(
    path, get_image_files(path), valid_pct=0.2, seed=42,
    label_func=is_cat, item_tfms=Resize(224))

对于不同类型的深度学习数据集和问题,有各种不同的类——在这里我们使用 ImageDataLoaders。类名的第一部分通常是您拥有的数据类型,例如 imagetext

我们必须告诉 fastai 的另一个重要信息是如何从数据集中获取标签。计算机视觉数据集通常被结构化为图像的标签是文件名或路径的一部分——最常见的是父文件夹名称。fastai 附带了许多标准化的标记方法,以及编写您自己的方法。在这里,我们告诉 fastai 使用我们刚才定义的 is_cat 函数。

最后,我们定义了需要的转换(Transforms)。转换包含在训练期间自动应用的代码;fastai 包含了许多预定义的转换,添加新的转换就像创建一个 Python 函数一样。有两种类型:item_tfms 应用于每个项目(在本例中,每个项目都被调整为224像素的方形),而 batch_tfms 一次应用于一批项目,使用 GPU,因此它们特别快(我们将在整本书中看到许多这样的例子)。

为什么是224像素?这是由于历史原因的标准大小(旧的预训练模型需要这个确切的大小),但您几乎可以传递任何值。如果您增加大小,通常会得到更准确的模型(因为它将能够关注更多细节),但以速度和内存消耗为代价;如果您减小大小,则相反。

注意:分类和回归:分类和回归在机器学习中有非常具体的含义。这是我们将在本书中研究的两种主要模型类型。分类模型是试图预测一个类别或类别的模型。也就是说,它预测的是离散的可能性,如“狗”或“猫”。回归模型是试图预测一个或多个数值的模型,例如温度或位置。有时人们使用回归一词来指代一种特定的模型,称为线性回归模型;这是一种不好的做法,我们不会在本书中使用这种术语!

Pet 数据集包含7390张猫和狗的图片,包括37个不同的品种。每张图像都使用其文件名进行标记:例如,文件 great_pyrenees_173.jpg 是数据集中 Great Pyrenees 品种狗的第173个示例。文件名以大写字母开头,如果图像是猫,否则以小写字母开头。我们必须告诉 fastai 如何从文件名中获取标签,我们通过调用 from_name_func(这意味着标签可以通过对文件名应用函数来提取),并传递 is_cat,它返回 x[0].isupper(),如果第一个字母是大写(即是猫),则评估为 True

这里要提到的最重要的参数是 valid_pct=0.2。这告诉 fastai 保留20%的数据,不在训练模型时使用它。这20%的数据称为验证集;其余的80%称为训练集。验证集用于测量模型的准确性。默认情况下,保留的20%是随机选择的。参数 seed=42 将随机种子设置为每次运行代码时的相同值,这意味着每次运行时我们都会得到相同的验证集——这样,如果我们更改模型并重新训练它,我们就知道任何差异是由于模型的更改,而不是由于有不同的随机验证集。

fastai 将始终使用仅验证集来显示您的模型的准确性,永远不会使用训练集。这一点非常关键,因为如果您在足够长的时间内训练一个足够大的模型,它最终会记住数据集中每个项目的标签!结果将不是一个有用的模型,因为我们关心的是模型在以前未见过的图像上的表现。当我们创建模型时,这始终是我们的目标:使其对在训练后才看到的数据有用。

即使您的模型尚未完全记住所有数据,早期的训练中它也可能记住其中的某些部分。因此,您训练的时间越长,模型在训练集上的准确性就会越好;验证集的准确性也会在一段时间内提高,但最终会开始变差,因为模型开始记住训练集,而不是在数据中找到可推广的底层模式。当这种情况发生时,我们称模型过拟合

图中显示了过拟合时的情况,使用了一个简化的示例,其中我们只有一个参数,以及基于函数 x**2 的随机生成数据。正如您所看到的,虽然过拟合模型的预测在接近观测数据点的数据上是准确的,但在该范围之外就会大大偏离。

过拟合的示例

屏幕截图 2024-10-02 204644.png

过拟合是所有机器学习从业者和所有算法在训练时面临的最重要和具有挑战性的问题。正如您将看到的,很容易创建一个在其已训练数据上做出预测的模型,但要在模型以前未见过的数据上做出准确的预测要困难得多。当然,这是在实践中真正重要的数据。例如,如果您创建一个手写数字分类器(我们很快就会这样做!),并将其用于识别支票上的数字,那么您永远不会看到模型训练过的任何数字——支票将不得不处理略有不同的书写变体。您将在本书中学习许多避免过拟合的方法。然而,只有在您确认过拟合实际发生(即,您已经观察到验证准确性在训练过程中变差)之后,才应使用这些方法。我们经常看到从业者即使在他们有足够的数据时也使用了过拟合避免技术,最终得到的模型可能比他们本可以达到的准确性要低。

重要提示:验证集:当您训练模型时,您必须始终有训练集和验证集,并且必须仅在验证集上测量模型的准确性。如果您训练时间过长,数据不足,您将看到模型的准确性开始变差;这称为过拟合。fastai 将 valid_pct 默认设置为0.2,因此即使您忘记了,fastai 也会为您创建一个验证集!

代码的第五行告诉 fastai 创建一个卷积神经网络(CNN),并指定要使用的架构(即要创建的模型类型)、我们想要训练的数据以及要使用的指标:

learn = vision_learner(dls, resnet34, metrics=error_rate)

为什么使用 CNN?这是创建计算机视觉模型的当前最先进方法。我们将在本书中详细了解 CNN 是如何工作的。它们的结构受到人类视觉系统的启发。

fastai 中有许多不同的架构,我们将在本书中介绍(以及讨论如何创建您自己的)。然而,大多数时候,选择架构并不是深度学习过程中非常重要的一部分。这是学术界喜欢谈论的事情,但在实践中,您不太可能需要花费太多时间在这上面。有一些标准的架构大多数时候都有效,在本例中,我们使用一种称为 ResNet 的架构,我们将在本书中详细讨论;对于许多数据集和问题,它既快速又准确。resnet34 中的 34 指的是该架构变体中的层数(其他选项有 18、50、101 和 152)。使用更多层的架构的模型需要更长的训练时间,并且更容易过拟合(即,在验证集的准确性开始变差之前,您不能训练它们太多轮)。另一方面,当使用更多数据时,它们可能会更准确。

什么是指标?指标是使用验证集衡量模型预测质量的函数,并将在每个 epoch 结束时打印出来。在本例中,我们使用的是 error_rate,这是 fastai 提供的一个函数,它正如其名称所示:告诉您验证集中被错误分类的图像的百分比。另一个常见的分类指标是准确率(即 1.0 - error_rate)。fastai 提供了更多指标,我们将在本书中讨论。

指标的概念可能让您想起损失函数,但它们之间有重要区别。损失函数的全部目的是定义一个用于训练系统自动更新权重的“性能度量”。换句话说,好的损失函数选择是 SGD 易于使用的选择。但指标是为人类消费而定义的,因此好的指标是易于理解的,尽可能接近您希望模型执行的任务。有时,您可能会认为损失函数是一个合适的指标,但这并不总是如此。

vision_learner 还有一个参数 pretrained,默认为 True(因此在本例中使用,即使我们没有指定),这将您的模型的权重设置为已经由专家训练的值,这些权重已经在130万张照片上训练过,可以识别一千种不同的类别(使用著名的 ImageNet 数据集)。已经训练过权重的模型称为预训练模型。您几乎应该始终使用预训练模型,因为这意味着您的模型在您甚至没有向其展示任何数据之前,就已经非常有能力了。正如您将看到的,在深度学习模型中,这些能力中的许多都是您需要的,几乎无论您的项目细节如何。例如,预训练模型的部分将处理边缘、梯度和颜色检测,这对于许多任务都是必需的。

当使用预训练模型时,vision_learner 将移除最后一层,因为它始终是专门为原始训练任务(即 ImageNet 数据集分类)定制的,并用一个或多个新的随机权重层替换它们,大小适合您正在处理的数据集。模型的最后一部分称为头部

使用预训练模型是我们能够更快地训练更准确模型的最重要方法,使用更少的数据、更少的时间和金钱。您可能认为这意味着使用预训练模型将是学术深度学习中最受研究的领域……但您非常非常错误!预训练模型的重要性在大多数课程、书籍或软件库功能中通常没有得到认可或讨论,在学术论文中也很少考虑。截至 2020 年初,我们正在撰写本书,情况才刚刚开始改变,但可能需要一段时间。因此,请小心:您交谈的大多数人可能会大大低估您在深度学习中可以用少量资源做到的事情,因为他们可能不会深入了解如何使用预训练模型。

将预训练模型用于与最初训练任务不同的任务被称为迁移学习。不幸的是,由于迁移学习研究不足,很少有领域有预训练模型可用。例如,目前在医学领域很少有可用的预训练模型,使得在该领域使用迁移学习具有挑战性。此外,尚不清楚如何将迁移学习用于诸如时间序列分析等任务。

术语:迁移学习:将预训练模型用于与最初训练任务不同的任务。

代码的第六行告诉 fastai 如何拟合模型:

learn.fine_tune(1)

正如我们所讨论的,架构只描述了数学函数的模板;除非我们为其包含的数百万个参数提供值,否则它实际上什么也不做。

这是深度学习的关键——确定如何拟合模型的参数,使其解决您的问题。为了拟合模型,我们必须至少提供一条信息:查看每张图像的次数(称为 epoch 数)。您选择的 epoch 数将在很大程度上取决于您可用的时间,以及您在实践中发现拟合模型需要多长时间。如果您选择的数字太小,您可以稍后再训练更多的 epoch。

但是为什么该方法称为 fine_tune,而不是 fit?fastai 实际上确实有一个名为 fit 的方法,它确实拟合一个模型(即多次查看训练集中的图像,每次更新参数,使预测与目标标签越来越接近)。但在本例中,我们从预训练模型开始,我们不想丢弃它已经拥有的所有能力。正如您将在本书中学习的那样,有一些重要的技巧可以将预训练模型适应新的数据集——这个过程称为微调。

术语:微调:一种迁移学习技术,通过使用与预训练不同的任务进行额外的 epoch 训练,更新预训练模型的参数。

当您使用 fine_tune 方法时,fastai 将为您使用这些技巧。您可以设置一些参数(我们稍后会讨论),但在这里显示的默认形式下,它执行两个步骤:

  1. 使用一个 epoch 仅拟合模型中那些必要的部分,以使新的随机头部能够与您的数据集正确配合。
  2. 在调用方法时使用指定的 epoch 数来拟合整个模型,更新后面的层(尤其是头部)的权重比前面的层更快(正如我们将看到的,通常不需要对预训练的权重进行太多更改)。

模型的头部是新添加的,特定于新数据集的部分。一个 epoch 是完整遍历数据集一次。在调用 fit 之后,会打印每个 epoch 的结果,显示 epoch 编号、训练和验证集的损失(用于训练模型的“性能度量”)以及您请求的任何指标(在本例中是错误率)。

因此,使用所有这些代码,我们的模型仅从标记的示例中学习识别猫和狗。但它是如何做到的呢?

我们的图像识别器学到了什么

现在我们有一个运行良好的图像识别器,但我们不知道它实际上在做什么!尽管许多人抱怨深度学习会导致无法理解的“黑盒”模型(即,给出预测但无人能理解),但事实远非如此。有大量的研究显示如何深入检查深度学习模型,并从中获得丰富的见解。话虽如此,各种机器学习模型(包括深度学习和传统统计模型)都可能难以完全理解,特别是当考虑到它们在遇到与训练数据非常不同的数据时会如何表现时。我们将在本书中讨论这个问题。

2013 年,博士生 Matt Zeiler 和他的导师 Rob Fergus 发表了论文《可视化和理解卷积网络》,展示了如何可视化模型中每一层学到的神经网络权重。他们仔细分析了赢得 2012 年 ImageNet 竞赛的模型,并使用这种分析大大改进了模型,使得他们能够在 2013 年的竞赛中获胜!以下是他们发布的第一层权重的图片。

卷积神经网络第一层的激活

这张图片需要一些解释。对于每一层,浅灰色背景的图像部分显示了权重重建的图片,而底部较大的部分显示了与每组权重最强匹配的训练图像部分。对于第一层,我们可以看到,模型已经发现了表示对角线、水平和垂直边缘以及各种不同梯度的权重。这些是模型为计算机视觉学习的基本构建块。它们已被神经科学家和计算机视觉研究人员广泛分析,事实证明,这些学习的构建块与人眼的基本视觉机制以及在深度学习时代之前开发的手工计算机视觉特征非常相似。下一层如图所示。

卷积神经网络第二层的激活

对于第二层,有九个权重重建的示例,分别对应模型发现的特征。我们可以看到,模型已经学会创建特征检测器,寻找角、重复的线条、圆形和其他简单的模式。这些是由第一层开发的基本构建块构建的。对于每一个,这张图片的右侧显示了与这些特征最紧密匹配的实际图像的小块。例如,第二行第一列的特定模式匹配了与日落相关的梯度和纹理。

下图显示了论文中展示的第三层特征重建的结果。

卷积神经网络第三层的激活

正如您在图片右侧所见,特征现在能够识别并匹配更高级别的语义组件,如车轮、文本和花瓣。利用这些组件,第四层和第五层可以识别更高级别的概念,如图所示。

卷积神经网络第四层和第五层的激活

这篇文章研究的是一个名为 AlexNet 的较老的模型,它只包含五层。自那时以来开发的网络可以有数百层——所以您可以想象这些模型所开发的特征是多么丰富!

当我们之前微调我们的预训练模型时,我们调整了那些最后几层的关注点(花、人类、动物),以专注于猫与狗的问题。更广泛地说,我们可以将这样的预训练模型专门化用于许多不同的任务。让我们看一些例子。

图像识别器可以处理非图像任务

顾名思义,图像识别器只能识别图像。但许多东西都可以表示为图像,这意味着图像识别器可以学习完成许多任务。

例如,声音可以转换为频谱图,它是一张显示音频文件中每个时间点上各个频率的图表。fast.ai 的学生 Ethan Sutin 使用这种方法,轻松超越了一个环境声音检测模型的已发布准确率,使用了包含 8,732 个城市声音的数据集。fastai 的 show_batch 清楚地显示了每个不同声音都有一个非常独特的频谱图,正如下图所示。

声音的频谱图

屏幕截图 2024-10-02 205319.png

时间序列可以通过简单地在图表上绘制时间序列来轻松转换为图像。然而,通常最好尝试以一种尽可能方便地提取最重要组件的方式来表示您的数据。在时间序列中,季节性和异常值等内容最有可能引起兴趣。有各种可用于时间序列数据的转换方法。例如,fast.ai 的学生 Ignacio Oguiza 使用一种称为 Gramian 角差场(GADF)的方法,将时间序列数据集转换为图像,用于橄榄油分类;您可以在下图中看到结果。然后他将这些图像输入到一个与本章中看到的类似的图像分类模型中。尽管他的训练集图像只有 30 张,但他的结果准确率超过了 90%,接近最先进的水平。

将时间序列转换为图像

屏幕截图 2024-10-02 205327.png

另一个有趣的 fast.ai 学生项目示例来自 Gleb Esman。他在 Splunk 从事欺诈检测工作,使用了一个包含用户鼠标移动和鼠标点击的数据集。他通过绘制一张图像,将鼠标指针的位置、速度和加速度用彩色线条显示,点击用小的彩色圆圈显示,如下图所示。然后他将其输入到一个与本章中我们使用的类似的图像识别模型中,效果非常好,最终为这种欺诈分析方法获得了专利!

将计算机鼠标行为转换为图像

屏幕截图 2024-10-02 205333.png

另一个示例来自 Mahmoud Kalash 等人的论文《使用深度卷积神经网络进行恶意软件分类》,该论文解释说“恶意软件二进制文件被划分为 8 位序列,然后转换为等效的十进制值。这个十进制向量被重塑,生成一个灰度图像,代表恶意软件样本”,如下图所示。

恶意软件分类过程

屏幕截图 2024-10-02 205437.png

然后,作者展示了通过这个过程生成的不同类别恶意软件的“图片”,如下图所示。

恶意软件示例

屏幕截图 2024-10-02 205442.png

如您所见,不同类型的恶意软件对人眼来说看起来非常独特。研究人员基于这种图像表示训练的模型在恶意软件分类方面比学术文献中任何以前的方法都更准确。这表明将数据集转换为图像表示的一个好经验法则是:如果人眼可以从图像中识别类别,那么深度学习模型也应该能够做到。

总的来说,您会发现,如果您对如何表示数据有一点创造力,深度学习中的少数通用方法可以走很长的路!您不应将上述方法视为“投机取巧”,因为实际上它们通常(如上所述)超过了以前的最先进结果。这些确实是考虑这些问题领域的正确方法。

术语回顾

我们刚刚覆盖了大量信息,所以让我们简要回顾一下,下面提供了一个方便的词汇表。

深度学习词汇表

术语含义
标签(Label)我们试图预测的数据,例如“狗”或“猫”
架构(Architecture)我们试图拟合的模型的模板;我们将输入数据和参数传递给的实际数学函数
模型(Model)架构与特定参数集的组合
参数(Parameters)改变模型可以执行什么任务的值,通过模型训练更新
拟合(Fit)更新模型的参数,使模型使用输入数据的预测与目标标签匹配
训练(Train)“拟合”的同义词
预训练模型(Pretrained model)已经在其他数据集上训练的模型,通常使用大型数据集,将被微调
微调(Fine-tune)使用与预训练不同的任务,更新预训练模型的参数
轮次(Epoch)数据集的一次完整遍历
损失(Loss)衡量模型好坏的指标,选择用于通过 SGD 进行训练
度量(Metric)使用验证集测量模型好坏的指标,为人类使用而选择
验证集(Validation set)从训练中保留的一组数据,仅用于衡量模型好坏
训练集(Training set)用于拟合模型的数据;不包含验证集中的任何数据
过拟合(Overfitting)训练模型,使其“记住”输入数据的特定特征,而不是很好地泛化到训练期间未见过的数据
CNN卷积神经网络;一种在计算机视觉任务中表现特别好的神经网络

有了这些词汇,我们现在可以总结到目前为止介绍的所有关键概念。花点时间回顾这些定义并阅读以下摘要。如果您能理解这个解释,那么您就有能力理解接下来的讨论。

机器学习是一门学科,我们不是通过完全自己编写程序来定义程序,而是通过从数据中学习来定义。深度学习是机器学习中的一个专门领域,使用多层神经网络。图像分类是一个典型的示例(也称为图像识别)。我们从标记的数据开始,即一组我们已经为每个图像分配了标签的图像,指示它代表什么。我们的目标是生成一个程序,称为模型,给定一个新图像,将对该新图像代表的内容做出准确的预测。

每个模型都从架构的选择开始,这是关于该类模型如何在内部工作的通用模板。训练(或拟合)模型的过程是找到一组参数值(或权重)的过程,这些值将该通用架构专门化为适合我们特定数据类型的模型。为了定义模型在单个预测上表现得有多好,我们需要定义一个损失函数,它确定我们如何将预测评分为好或坏。

为了加快训练过程,我们可能从一个预训练模型开始——一个已经在他人数据上训练过的模型。然后,我们可以通过在我们的数据上进一步训练它来将其适应我们的数据,这个过程称为微调。

当我们训练模型时,一个关键问题是确保我们的模型能够泛化——也就是说,它从我们的数据中学习的经验教训也适用于它将遇到的新项目,这样它就可以对这些项目做出良好的预测。风险在于,如果我们训练模型不当,它可能不会学习一般的经验教训,而是有效地记住它已经看到的内容,然后对新图像做出不佳的预测。这种失败称为过拟合。为避免这种情况,我们始终将数据分为两部分,训练集和验证集。我们通过仅向模型展示训练集来训练模型,然后通过查看模型在验证集项目上的表现来评估模型的表现。通过这种方式,我们检查模型从训练集中学到的经验教训是否是可以泛化到验证集的经验教训。为了让人们评估模型在验证集上的整体表现,我们定义了一个度量。在训练过程中,当模型已经看到了训练集中的每个项目时,我们称之为一个轮次。

所有这些概念都适用于机器学习一般领域。也就是说,它们适用于所有种类的通过数据训练来定义模型的方案。深度学习的独特之处在于一类特定的架构:基于神经网络的架构。特别是,像图像分类这样的任务在很大程度上依赖于卷积神经网络,我们将在后面讨论。

深度学习不仅仅适用于图像分类

近年来,深度学习在图像分类方面的有效性已被广泛讨论,甚至在识别 CT 扫描中的恶性肿瘤等复杂任务上表现出超越人类的结果。但它能做的远不止这些,正如我们将在这里展示的那样。

例如,让我们谈谈对自动驾驶汽车至关重要的事情:定位图片中的物体。如果自动驾驶汽车不知道行人在哪里,那么它就不知道如何避开他们!

创建一个能够识别图像中每个像素内容的模型称为分割

以下是我们如何使用 fastai 训练一个分割模型,使用 Gabruel J. Brostow、Julien Fauqueur 和 Roberto Cipolla 在论文《视频中的语义对象类别:高清晰度真实数据库》中提供的 Camvid 数据集的一个子集

path = untar_data(URLs.CAMVID_TINY)
dls = SegmentationDataLoaders.from_label_func(
    path, bs=8, fnames = get_image_files(path/"images"),
    label_func = lambda o: path/'labels'/f'{o.stem}_P{o.suffix}',
    codes = np.loadtxt(path/'codes.txt', dtype=str)
)

learn = unet_learner(dls, resnet34)
learn.fine_tune(8)
epoch	train_loss	valid_loss	time
0	2.641862	2.140568	00:02
epoch	train_loss	valid_loss	time
0	1.624964	1.464210	00:02
1	1.454148	1.284032	00:02
2	1.342955	1.048562	00:02
3	1.199765	0.852787	00:02
4	1.078090	0.838206	00:02
5	0.975496	0.746806	00:02
6	0.892793	0.725384	00:02
7	0.827645	0.726778	00:02

我们甚至不会逐行解释这段代码,因为它与我们之前的示例几乎相同!(尽管我们将在第 18 章深入研究分割模型,以及我们在本章中简要介绍的所有其他模型,以及更多内容。)

屏幕截图 2024-10-02 205543.png

我们可以通过要求模型对图像的每个像素进行颜色编码来可视化它完成任务的效果。正如您所见,它几乎完美地对每个物体的每个像素进行分类。例如,注意所有的汽车都被覆盖了相同的颜色,所有的树木也被覆盖了相同的颜色(在每对图像中,左侧图像是实际标签,右侧是模型的预测):

learn.show_results(max_n=6, figsize=(7,8))

另一个深度学习在过去几年中显著改进的领域是自然语言处理(NLP)。计算机现在可以生成文本、自动将一种语言翻译成另一种语言、分析评论、标记句子中的词语,等等。以下是训练一个可以比五年前世界上任何东西都更好地分类电影评论情感的模型所需的全部代码:

from fastai.text.all import *

dls = TextDataLoaders.from_folder(untar_data(URLs.IMDB), valid='test')
learn = text_classifier_learner(dls, AWD_LSTM, drop_mult=0.5, metrics=accuracy)
learn.fine_tune(4, 1e-2)
epoch	train_loss	valid_loss	accuracy	time
0	0.878776	0.748753	0.500400	01:27
epoch	train_loss	valid_loss	accuracy	time
0	0.679118	0.674778	0.584040	02:45
1	0.653671	0.670396	0.618040	02:55
2	0.598665	0.551815	0.718920	05:28
3	0.556812	0.507450	0.752480	03:1

如果在运行此单元格后遇到“CUDA 内存不足错误”,点击菜单中的“Kernel”,然后选择“Restart”。不要执行上面的单元格,而是在其中复制并粘贴以下代码:

from fastai.text.all import *

dls = TextDataLoaders.from_folder(untar_data(URLs.IMDB), valid='test', bs=32)
learn = text_classifier_learner(dls, AWD_LSTM, drop_mult=0.5, metrics=accuracy)
learn.fine_tune(4, 1e-2)

这将批量大小减少到 32(我们将稍后解释)。如果您仍然遇到相同的错误,将 32 改为 16。

这个模型使用了 Andrew Maas 等人的论文《用于情感分析的词向量学习》中提供的 IMDb 大型电影评论数据集。它可以很好地处理数千字的电影评论,但让我们测试一下它如何处理一个非常简短的评论:

learn.predict("I really liked that movie!")
('pos', tensor(1), tensor([0.0040, 0.9960]))

在这里,我们可以看到模型认为这条评论是积极的。结果的第二部分是“pos”在我们数据词汇表中的索引,最后一部分是分配给每个类别的概率(99.6% 的概率为“pos”,0.4% 的概率为“neg”)。

现在轮到您了!写下您自己的简短电影评论,或者从互联网上复制一条,看看这个模型对此的看法。

旁注:顺序很重要

在 Jupyter Notebook 中,您执行每个单元格的顺序非常重要。它不像 Excel,当您在任何地方键入内容时,一切都会更新——它有一个内部状态,每次您执行一个单元格时都会更新。例如,当您运行笔记本的第一个单元格(带有“CLICK ME”注释)时,您创建了一个名为 learn 的对象,其中包含一个模型和图像分类问题的数据。如果我们直接运行刚刚在文本中显示的单元格(预测评论情感的那个),我们会得到一个错误,因为这个 learn 对象不包含文本分类模型。此单元格需要在包含以下内容的单元格之后运行:

from fastai.text.all import *

dls = TextDataLoaders.from_folder(untar_data(URLs.IMDB), valid='test')
learn = text_classifier_learner(dls, AWD_LSTM, drop_mult=0.5,
                                metrics=accuracy)
learn.fine_tune(4, 1e-2)

输出本身可能具有误导性,因为它们包括上次执行单元格的结果;如果您更改单元格中的代码而不执行它,旧的(误导性)结果将保留。

除非我们明确提到,否则在书籍网站上提供的笔记本是按照顺序运行的,从上到下。一般来说,在实验时,您会发现自己以任何顺序执行单元格以加快速度(这是 Jupyter Notebook 的一个超级不错的功能),但一旦您探索并得到了代码的最终版本,请确保您可以按顺序运行笔记本的单元格(否则您的未来自己可能不记得您采取的复杂路径!)。

在命令模式下,按两次 0 将重启内核(这是驱动您的笔记本的引擎)。这将清除您的状态,使其看起来像您刚刚在笔记本中启动一样。从“Cell”菜单中选择“Run All Above”可以运行到您当前位置为止的所有单元格。在开发 fastai 库时,我们发现这非常有用。

结束旁注

如果您对 fastai 的某个方法有任何疑问,您应该使用 doc 函数,传入方法名称:

doc(learn.predict)

这将弹出一个小窗口,内容如下:

屏幕截图 2024-10-02 210207.png

doc 提供了一个简短的单行解释。“Show in docs”链接会带您进入完整的文档,您可以在其中找到所有细节和大量示例。此外,fastai 的大多数方法都只有几行代码,因此您可以点击“source”链接,查看幕后究竟发生了什么。

让我们继续讨论一些可能不那么吸引人但在商业上更有用的东西:从普通的表格数据构建模型。

术语:表格数据(Tabular data)——以表格形式的数据,例如来自电子表格、数据库或 CSV 文件。表格模型是尝试根据表格中其他列的信息来预测某一列的模型。

事实证明,这看起来也非常相似。以下是训练一个模型所需的全部代码,该模型将根据个人的社会经济背景预测他们是否是高收入者:

from fastai.tabular.all import *
path = untar_data(URLs.ADULT_SAMPLE)

dls = TabularDataLoaders.from_csv(path/'adult.csv', path=path, y_names="salary",
    cat_names = ['workclass', 'education', 'marital-status', 'occupation',
                 'relationship', 'race'],
    cont_names = ['age', 'fnlwgt', 'education-num'],
    procs = [Categorify, FillMissing, Normalize])

learn = tabular_learner(dls, metrics=accuracy)

正如您所见,我们必须告诉 fastai 哪些列是分类的(即,包含来自一组离散选择的值,例如职业),哪些是连续的(即,包含表示数量的数字,例如年龄)。

对于这个任务,没有可用的预训练模型(通常,对于任何表格建模任务,都没有广泛可用的预训练模型,尽管一些组织已经为内部使用创建了它们),因此我们在这种情况下不使用 fine_tune。相反,我们使用 fit_one_cycle,这是最常用于从头开始训练 fastai 模型的方法(即不使用迁移学习):

learn.fit_one_cycle(3)
epoch	train_loss	valid_loss	accuracy	time
0	0.372397	0.357177	0.832463	00:08
1	0.351544	0.341505	0.841523	00:08
2	0.338763	0.339184	0.845670	00:08

这个模型使用了来自 Rob Kohavi 的论文《扩大朴素贝叶斯分类器的准确性:决策树混合》的 Adult 数据集,该数据集包含一些关于个人的人口统计数据(如教育、婚姻状况、种族、性别以及他们的年收入是否超过 5 万美元)。该模型的准确率超过 80%,训练时间约为 30 秒。

让我们再看一个例子。推荐系统在电子商务中特别重要。像亚马逊和 Netflix 这样的公司努力推荐用户可能喜欢的产品或电影。以下是如何使用 MovieLens 数据集训练一个模型,该模型将根据用户的先前观看习惯预测他们可能喜欢的电影:

from fastai.collab import *
path = untar_data(URLs.ML_SAMPLE)
dls = CollabDataLoaders.from_csv(path/'ratings.csv')
learn = collab_learner(dls, y_range=(0.5,5.5))
learn.fine_tune(10)
epoch	train_loss	valid_loss	time
0	1.510897	1.410028	00:00
epoch	train_loss	valid_loss	time
0	1.375435	1.350930	00:00
1	1.270062	1.173962	00:00
2	1.023159	0.879298	00:00
3	0.797398	0.739787	00:00
4	0.685500	0.700903	00:00
5	0.646508	0.686387	00:00
6	0.623985	0.681087	00:00
7	0.606319	0.676885	00:00
8	0.606975	0.675833	00:00
9	0.602670	0.675682	00:00

这个模型以 0.5 到 5.0 的评分范围预测电影评分,平均误差约为 0.6。由于我们在预测一个连续的数字,而不是一个类别,我们必须使用 y_range 参数告诉 fastai 我们的目标范围。

尽管我们实际上没有使用预训练模型(与表格模型的原因相同,我们没有使用预训练模型),但这个例子表明 fastai 允许我们在这种情况下使用 fine_tune(您将在第 8 章了解这是如何以及为什么可行的)。有时最好尝试 fine_tunefit_one_cycle,看看哪个更适合您的数据集。

我们可以使用之前看到的相同 show_results 调用,查看一些用户和电影 ID、实际评分和预测:

learn.show_results()

userId	movieId	rating	rating_pred
0	66.0	79.0	4.0	3.978900
1	97.0	15.0	4.0	3.851795
2	55.0	79.0	3.5	3.945623
3	98.0	91.0	4.0	4.458704
4	53.0	7.0	5.0	4.670005
5	26.0	69.0	5.0	4.319870
6	81.0	16.0	4.5	4.426761
7	80.0	7.0	4.0	4.046183
8	51.0	94.0	5.0	3.499996

旁注:数据集——模型的食粮

您已经在本节中看到了相当多的模型,每个模型都使用不同的数据集来执行不同的任务。在机器学习和深度学习中,没有数据我们什么都做不了。因此,创建数据集供我们训练模型的人是(通常被低估的)英雄。一些最有用和最重要的数据集是那些成为重要学术基准的数据集;也就是说,被研究人员广泛研究并用于比较算法变化的数据集。其中一些成为家喻户晓的名字(至少在训练模型的家庭中!),如 MNIST、CIFAR-10 和 ImageNet。

本书中使用的数据集之所以被选中,是因为它们提供了您可能遇到的数据类型的极佳示例,并且学术文献中有许多使用这些数据集的模型结果,您可以与您的工作进行比较。

本书中使用的大多数数据集的创建都花费了创建者大量的工作。例如,稍后在本书中,我们将向您展示如何创建一个可以在法语和英语之间进行翻译的模型。实现这一目标的关键输入是由宾夕法尼亚大学的 Chris Callison-Burch 教授在 2009 年准备的法语/英语平行文本语料库。该数据集包含超过 2000 万对法语和英语的句子。他以一种非常聪明的方式构建了这个数据集:通过抓取数百万加拿大网页(这些网页通常是多语言的),然后使用一组简单的启发式方法将法语内容的 URL 转换为指向相同内容的英语 URL。

当您查看本书中的数据集时,思考一下它们可能来自哪里,以及它们是如何整理的。然后思考一下您可以为自己的项目创建哪些有趣的数据集。(我们甚至会在稍后带您一步步完成创建自己的图像数据集的过程。)

fast.ai 花费了大量时间创建了流行数据集的精简版本,这些版本专为支持快速原型设计和实验而设计,更易于学习。在本书中,我们经常从使用精简版本开始,然后扩展到完整版本(就像我们在本章中所做的那样!)。事实上,这就是世界顶级实践者在实践中进行建模的方式;他们在大部分实验和原型设计中使用他们数据的子集,只有在他们对需要做什么有了很好的理解之后才使用完整的数据集。

结束旁注

我们训练的每个模型都显示了训练和验证损失。良好的验证集是训练过程最重要的部分之一。让我们看看为什么以及如何创建一个验证集。

验证集和测试集

正如我们已经讨论过的,模型的目标是对数据进行预测。但模型训练过程本质上是愚蠢的。如果我们用所有数据训练一个模型,然后使用相同的数据评估模型,我们将无法知道我们的模型在未见过的数据上表现如何。没有这个对训练我们的模型非常有价值的指导信息,我们的模型很有可能会善于对该数据做出预测,但在新数据上表现不佳。

为避免这种情况,我们的第一步是将数据集分为两部分:训练集(模型在训练中看到的部分)和验证集(仅用于评估)。这使我们能够测试模型是否从训练数据中学习了可以泛化到新数据(验证数据)的经验教训。

理解这种情况的一种方式是,从某种意义上说,我们不希望我们的模型通过“作弊”获得好的结果。如果它对一个数据项做出了准确的预测,那应该是因为它已经学习了该类型项的特征,而不是因为模型已经通过实际看到该特定项而被塑造。

分离我们的验证数据意味着我们的模型在训练中从未看到过它,因此完全没有被它污染,也没有以任何方式作弊。对吗?

事实上,不一定。情况更微妙。这是因为在现实情况下,我们很少只通过训练一次权重参数来构建模型。相反,我们可能会通过各种建模选择来探索模型的多个版本,这些选择涉及网络架构、学习率、数据增强策略以及我们将在后续章节中讨论的其他因素。其中许多选择可以描述为超参数的选择。这个词反映了它们是关于参数的参数,因为它们是控制权重参数含义的更高级别的选择。

问题是,尽管普通的训练过程在学习权重参数的值时只关注训练数据上的预测,但我们自己并非如此。我们,作为建模者,在决定在训练之间探索新的超参数值时,正在通过查看验证数据上的预测来评估模型!因此,模型的后续版本间接地被我们看到的验证数据所塑造。就像自动训练过程有过拟合训练数据的危险一样,我们通过人类的试错和探索有过拟合验证数据的危险。

这种困境的解决方案是引入另一个级别的更高级别保留数据,即测试集。就像我们将验证数据从训练过程中保留一样,我们必须将测试集数据从我们自己手中保留。它不能用于改进模型;它只能用于在我们所有努力的最后评估模型。实际上,我们根据我们希望将数据与训练和建模过程隔离的程度定义了数据的层次划分:训练数据是完全公开的,验证数据是较少公开的,测试数据是完全隐藏的。这个层次结构与不同类型的建模和评估过程本身相对应——自动训练过程(带有反向传播)、在训练之间尝试不同超参数的更手动的过程,以及我们最终结果的评估。

测试集和验证集应该有足够的数据,以确保您对准确性有一个良好的估计。例如,如果您正在创建一个猫检测器,您通常希望在验证集中至少有 30 只猫。这意味着,如果您有一个包含数千个项目的数据集,使用默认的 20% 验证集大小可能比您需要的更多。另一方面,如果您有大量数据,使用其中的一部分进行验证可能没有任何缺点。

拥有两个级别的“保留数据”——验证集和测试集,其中一个级别代表您几乎对自己隐藏的数据——可能看起来有点极端。但之所以经常需要这样做,是因为模型倾向于以最简单的方式做出良好预测(记忆),而我们作为容易犯错的人,倾向于欺骗自己关于模型表现有多好。测试集的纪律帮助我们保持知识上的诚实。这并不意味着我们总是需要一个单独的测试集——如果您有很少的数据,您可能只需要一个验证集——但通常最好尽可能使用一个。

同样的纪律可以在您打算雇用第三方为您执行建模工作时至关重要。第三方可能无法准确理解您的需求,或者他们的激励机制甚至可能鼓励他们误解您的需求。一个好的测试集可以极大地减轻这些风险,并让您评估他们的工作是否解决了您的实际问题。

直截了当地说,如果您是组织中的高级决策者(或者您正在为高级决策者提供建议),最重要的收获是:如果您确保自己真正理解测试集和验证集是什么以及它们为什么重要,那么您就可以避免我们在组织决定使用 AI 时看到的最大失败来源。例如,如果您正在考虑引入外部供应商或服务,确保您保留一些供应商从未见过的测试数据。然后,您使用您根据实际重要的指标来检查他们的模型在您的测试数据上的表现,并决定什么样的性能是足够的。(最好您自己尝试一些简单的基线,这样您就知道一个非常简单的模型可以达到什么效果。通常会发现您的简单模型表现得和外部“专家”生成的模型一样好!)

在定义测试集时运用判断力

为了很好地定义一个验证集(可能还有一个测试集),有时您需要做的不仅仅是从原始数据集中随机抓取一部分。请记住:验证集和测试集的一个关键属性是它们必须代表您将在未来看到的新数据。这听起来像是不可能的任务!按照定义,您还没有看到这些数据。但通常您仍然知道一些事情。

查看一些示例案例很有启发性。其中许多示例来自 Kaggle 平台上的预测建模竞赛,这是您可能在实践中看到的问题和方法的良好代表。

一种情况是,如果您正在查看时间序列数据。对于时间序列,选择数据的随机子集将既过于容易(您可以查看您尝试预测的日期之前和之后的数据),也不代表大多数业务用例(您正在使用历史数据来构建用于未来的模型)。如果您的数据包含日期,并且您正在构建一个用于未来的模型,您将希望选择具有最新日期的连续部分作为您的验证集(例如,可用数据的最后两周或最后一个月)。

假设您想将下图中的时间序列数据拆分为训练集和验证集。

屏幕截图 2024-10-02 210542.png

随机子集是一个不好的选择(填补空白太容易,并且不代表您在生产中需要的内容),如下图所示。

屏幕截图 2024-10-02 210537.png

相反,使用较早的数据作为训练集(并将较晚的数据用于验证集),如下图所示。

屏幕截图 2024-10-02 210533.png

例如,Kaggle 有一个竞赛,预测厄瓜多尔连锁超市的销售额。Kaggle 的训练数据从 2013 年 1 月 1 日到 2017 年 8 月 15 日,测试数据跨度为 2017 年 8 月 16 日到 2017 年 8 月 31 日。通过这种方式,竞赛组织者确保参赛者正在为一个从他们模型的角度来看在未来的时间段进行预测。这类似于量化对冲基金交易员使用回测来检查他们的模型是否基于过去数据对未来时期具有预测能力的方式。

第二种常见情况是,当您可以轻松预见到您将在生产中进行预测的数据可能与您用于训练模型的数据在质量上有何不同。

在 Kaggle 的分心驾驶员竞赛中,自变量是驾驶员在车内的图片,因变量是诸如发短信、吃东西或安全地向前看等类别。很多图片是同一驾驶员在不同姿势下的,如下图所示。如果您是一家保险公司,根据这些数据构建模型,请注意您最感兴趣的是模型在以前未见过的驾驶员上的表现(因为您可能只有少数人的训练数据)。考虑到这一点,竞赛的测试数据包含不在训练集中的人的图像。

屏幕截图 2024-10-02 210525.png

如果您将上图中的一张图像放入训练集,另一张放入验证集,您的模型将很容易对验证集中的那张做出预测,因此它似乎比它在新的人身上表现得更好。另一种观点是,如果您在训练模型时使用了所有的人,您的模型可能会过拟合这些特定人的特性,而不仅仅是学习状态(发短信、吃东西等)。

在 Kaggle 的渔业竞赛中也存在类似的动态,目的是识别捕捞船只捕获的鱼的种类,以减少对濒危种群的非法捕捞。测试集由训练数据中未出现的船只组成。这意味着您希望您的验证集中包含不在训练集中的船只。

有时,您可能不清楚您的验证数据将如何不同。例如,对于使用卫星图像的问题,您需要收集更多信息,了解训练集是否仅包含某些地理位置,或者是否来自地理分散的数据。

现在,您已经对如何构建模型有所了解,您可以决定接下来想要深入了解什么。

一个“选择你自己的冒险”时刻

如果您想了解更多关于如何在实践中使用深度学习模型,包括如何识别和修复错误、创建真正可用的 Web 应用程序,以及避免您的模型对您的组织或更广泛的社会造成意外伤害,那么请继续阅读接下来的两章。如果您想开始学习深度学习的基础知识,了解其背后的工作原理,请跳到第 4 章。(您小时候是否读过“选择你自己的冒险”书籍?嗯,这有点像那样……只是包含了更多的深度学习,而不是那本系列书籍所包含的。)

您需要阅读所有这些章节才能在本书中进一步推进,但您可以完全自行决定阅读它们的顺序。它们彼此独立。如果您跳到第 4 章,我们会在结尾提醒您在继续之前回来阅读您跳过的章节。

练习题

在数页的长篇大论中,很难知道您真正需要关注和记住的关键内容。因此,我们在每章的末尾准备了一系列问题和建议的步骤。所有答案都在本章的正文中,因此如果您对这里的任何内容不确定,请重新阅读该部分内容,确保您理解它。所有这些问题的答案也可以在本书的网站上找到。如果您遇到困难,可以访问论坛,获得其他学习这些材料的人的帮助。

对于更多的问题,包括详细的答案和视频时间线链接,请查看 Radek Osmulski 的 aiquizzes。

  1. 你需要这些来进行深度学习吗?
    • 大量的数学知识 F
    • 大量的数据 F
    • 大量昂贵的计算机 F
    • 博士学位 F
  2. 列举深度学习现在在世界上最好的五个领域。
  3. 基于同名的书,第一个基于人工神经元原理的设备叫什么?
  4. 基于同名的书,并行分布式处理(PDP)的要求是什么?
  5. 阻碍神经网络领域发展的两个理论误解是什么?
  6. 什么是 GPU?
  7. 打开一个笔记本并执行一个包含 1+1 的单元格。会发生什么?
  8. 遍历本章精简版笔记本的每个单元格。在执行每个单元格之前,猜测会发生什么。
  9. 完成 Jupyter Notebook 在线附录。
  10. 为什么使用传统的计算机程序来识别照片中的图像很困难?
  11. Samuel 所说的“权重分配”是什么意思?
  12. 在深度学习中,我们通常用什么术语来表示 Samuel 所说的“权重”?
  13. 画一幅图,总结 Samuel 对机器学习模型的看法。
  14. 为什么很难理解深度学习模型为什么会做出特定的预测?
  15. 能够证明神经网络可以以任意精度解决任何数学问题的定理叫什么?
  16. 你需要什么来训练一个模型?
  17. 反馈回路如何影响预测性警务模型的推出?
  18. 我们在猫识别模型中是否总是必须使用 224×224 像素的图像?
  19. 分类和回归有什么区别?
  20. 什么是验证集?什么是测试集?为什么我们需要它们?
  21. 如果您不提供验证集,fastai 会做什么?
  22. 我们是否总是可以使用随机样本作为验证集?为什么或为什么不可以?
  23. 什么是过拟合?提供一个示例。
  24. 什么是度量?它与“损失”有何不同?
  25. 预训练模型如何提供帮助?
  26. 什么是模型的“头部”?
  27. CNN 的早期层可以找到什么样的特征?后期层呢?
  28. 图像模型是否只对照片有用?
  29. 什么是“架构”?
  30. 什么是分割?
  31. y_range 用于什么?什么时候需要它?
  32. 什么是“超参数”?
  33. 在组织中使用 AI 时,避免失败的最佳方法是什么?

进一步研究

每章还有一个“进一步研究”部分,提出了文本中未完全回答的问题,或者提供更高级的任务。这些问题的答案不在本书的网站上;您需要自己进行研究!

  1. 为什么 GPU 对深度学习有用?CPU 有何不同,为什么它对深度学习的效果较差?
  2. 试着想出三个反馈回路可能影响机器学习使用的领域。看看你能否找到实际发生的已记录示例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值