dl-arch-hb-merge-0

50 阅读1小时+

深度学习架构手册(一)

原文:annas-archive.org/md5/3540d3ca64cea2cc08daad9a2559bb49

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

作为一名深度学习的从业者和爱好者,我花了多年时间从 Kaggle、GitHub、同事以及现实生活中的应用案例中学习和积累经验。我意识到,在深度学习领域,缺乏一套完整的、端到端的资源。虽然传统的大规模开放在线课程MOOC)有帮助,但它们通常缺乏那些只能通过实际操作经验获得的实用知识和现实洞察。

为了弥补这一差距,我创建了《深度学习架构师手册》,这是一本全面且实用的指南,结合了我独特的经验和见解。本书将帮助你了解深度学习的复杂领域,为你提供那些通常需要多年实践经验才能获得的知识和见解,并将其浓缩为一个可以在几天或几周内消化的资源。

本书深入探讨了深度学习生命周期的各个阶段,从规划和数据准备到模型部署和治理。在这段旅程中,你将接触到基础和先进的深度学习架构,如多层感知机MLPs)、卷积神经网络CNNs)、递归神经网络RNNs)、自编码器、变换器以及最前沿的方法,如神经架构搜索NAS)。本书分为三部分,涵盖了基础方法、模型洞察以及 DLOps,探讨了高级话题,如 NAS、对抗性性能和大语言模型LLM)解决方案。通过本书的学习,你将为设计、开发和部署有效的深度学习解决方案做好充分准备,释放其全部潜力,并推动各类应用的创新。

我希望这本书能作为我回馈社区的一种方式,通过激发讨论、挑战假设并启发深度学习领域的新思想和新方法。我邀请你与我一起踏上这段旅程,期待在我们共同探索深度学习这一迷人世界的过程中,听到你的想法和反馈。欢迎通过 LinkedIn 与我联系:www.linkedin.com/in/chineeki…,也可以通过 Kaggle 联系我:www.kaggle.com/dicksonchin93,或者通过我 LinkedIn 个人资料上的其他渠道联系。你的独特经历和观点无疑将有助于本书的持续发展以及深度学习社区的整体进步。

本书的读者对象

本书最适合深度学习从业者、数据科学家和机器学习开发人员,他们希望探索深度学习架构来解决复杂的商业问题。本书的读者群体是那些将把知识应用到商业用例中的深度学习和 AI 领域的专业人士。为了充分利用本书内容,需要具备 Python 编程的工作知识和基本的深度学习技术理解。

本书涵盖内容

第一章深度学习生命周期,介绍了深度学习项目的关键阶段,重点是规划和数据准备,为整本书对深度学习生命周期的全面探索奠定了基础。

第二章设计深度学习架构,深入探讨了深度学习架构的基础,包括 MLP,并讨论了它们在先进神经网络中的作用,以及反向传播和正则化的重要性。

第三章理解卷积神经网络,深入探讨了 CNN 及其在图像处理中的应用,以及 CNN 领域中的各种模型家族。

第四章理解循环神经网络,探讨了 RNN 的结构和变体及其有效处理序列数据的能力。

第五章理解自编码器,探讨了自编码器作为表示学习方法的基本原理及其在不同数据模态中的应用。

第六章理解神经网络变换器,深入探讨了变换器的多功能性质,能够处理多种数据模态而没有明确的数据特定偏见,并讨论了它们在各种任务和领域中的潜在应用。

第七章深度神经网络架构搜索,介绍了 NAS 的概念,作为一种自动化设计先进神经网络的方法,并讨论了其在不同场景中的应用和局限性。

第八章探索有监督深度学习,涵盖了各种有监督学习问题类型、实现和训练深度学习模型的技术,以及使用流行的深度学习框架进行的实际实现。

第九章探索无监督深度学习,讨论了深度学习对无监督学习的贡献,特别强调了无监督预训练方法。利用互联网上大量免费的数据,这种方法提高了下游有监督任务的模型性能,并为走向通用人工 智能AI)铺平了道路。

第十章探索模型评估方法,概述了模型评估技术、度量工程和优化评估度量的策略。

第十一章解释神经网络预测,深入探讨了预测解释的领域,重点介绍了集成梯度技术及其在理解神经网络预测中的实际应用。

第十二章解读神经网络,深入探讨了模型理解的细微差别,并展示了揭示神经元检测模式的技术。通过探索真实图像并通过优化生成图像来激活特定神经元,你将获得神经网络决策过程的宝贵见解。

第十三章探索偏差与公平性,解决了机器学习模型中偏差与公平性的关键问题,讨论了各种类型、度量标准和程序化方法来检测和缓解偏差。

第十四章分析对抗性性能,探讨了对抗性性能分析在识别机器学习模型漏洞和弱点中的重要性,并提供了实践示例和分析技巧。

第十五章深度学习模型的生产部署,重点介绍了在生产环境中部署深度学习模型的关键组件、需求和策略,包括架构选择、硬件基础设施和模型包装。

第十六章治理深度学习模型,探讨了模型治理的基本支柱,包括模型使用、模型监控和模型维护,同时提供了监控深度学习模型的实际步骤。

第十七章在动态环境中有效管理漂移效应,讨论了漂移的概念及其对模型性能的影响,并提供了检测、量化和减轻深度学习模型漂移的策略。

第十八章探索 DataRobot AI 平台,展示了 AI 平台,特别是 DataRobot,在简化和加速深度学习生命周期中的好处,并强调了该平台的各种功能和能力。

第十九章构建 LLM 解决方案,深入探讨了 LLM 及其潜在应用、挑战和创建有效、具备上下文意识解决方案的策略。

为了充分利用本书

本章提供的代码已在配置为 Python 3.10、Ubuntu 20.04 LTS 64 位操作系统、32 GB 内存和 RTX 2080TI GPU 的计算机上进行测试,用于运行深度学习模型。虽然代码已在此特定配置下测试过,但也可能适用于其他配置;不过,兼容性和性能无法保证。Python 依赖项已包含在每章相应 GitHub 文件夹中的requirements.txt文件中,便于安装。此外,可能还需要一些非 Python 软件;其安装说明将在每个相关教程的开头提到。对于这些软件安装,您需要参考外部手册或指南进行安装。在进行本书中的实际代码部分时,请记住系统配置的潜在差异。

如果您使用的是本书的数字版本,建议您亲自输入代码,或从本书的 GitHub 仓库获取代码(链接将在下一部分提供)。这样可以帮助您避免与复制粘贴代码相关的潜在错误。

下载示例代码文件

您可以从 GitHub 上下载本书的示例代码文件,网址是github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook。如果代码有更新,它会在 GitHub 仓库中更新。

我们还提供了来自我们丰富书籍和视频目录的其他代码包,您可以在github.com/PacktPublishing/找到。赶紧查看吧!

使用的约定

本书中使用了多种文本约定。

Code in text:表示文本中的代码字、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 用户名。以下是一个示例:“我们将使用pandas进行数据处理和结构化,使用matplotlibseaborn绘制图表,使用tqdm可视化迭代进度,并使用lingua进行文本语言检测。”

代码块设置如下:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from tqdm import tqdm
from lingua import Language, LanguageDetectorBuilder
tqdm.pandas()

任何命令行输入或输出如下所示:

sudo systemctl start node_exporter sudo systemctl start prometheus

粗体:表示新术语、重要词汇或屏幕上显示的词汇。例如,菜单或对话框中的文字以粗体显示。以下是一个示例:“现在我们可以通过点击左上角选项卡中的三条线按钮,然后在管理下拉菜单中点击数据源选项卡来设置 Prometheus 链接。”

小贴士或重要说明

如下所示。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果您对本书的任何部分有疑问,请通过电子邮件联系我们,邮箱地址是customercare@packtpub.com,并在邮件主题中提到书名。

勘误:虽然我们已经尽力确保内容的准确性,但错误难免发生。如果你发现书中的错误,我们将非常感激你能向我们报告。请访问www.packtpub.com/support/err…并填写表格。

盗版:如果你在互联网上遇到我们的作品的任何非法复制形式,我们将非常感激你能提供该位置或网站名称。请通过版权@packt.com与我们联系,并提供相关链接。

如果你有兴趣成为作者:如果你在某个领域具有专业知识,且有兴趣撰写或参与书籍的编写,请访问authors.packtpub.com

分享你的想法

你的评论对我们和技术社区非常重要,将帮助我们确保提供优质的内容。

分享你的想法

一旦你阅读完*《深度学习架构师手册》*,我们非常希望听到你的想法!点击这里直接前往亚马逊评论页面,分享你的反馈。

你的评论对我们和技术社区非常重要,将帮助我们确保提供优质的内容。

下载这本书的免费 PDF 副本

感谢购买本书!

你喜欢在路上阅读,但又无法随身携带印刷书籍吗?

你的电子书购买是否与你选择的设备不兼容?

别担心,现在购买每本 Packt 书籍,你都会免费获得该书的无 DRM PDF 版本。

随时随地,在任何设备上阅读。直接从你喜爱的技术书籍中搜索、复制并粘贴代码到你的应用程序中。

优惠不仅仅如此,你还可以每天在邮箱中收到独家折扣、新闻通讯和精彩的免费内容。

按照以下简单步骤获取福利:

  1. 扫描二维码或访问以下链接

packt.link/free-ebook/9781803243795

  1. 提交你的购买凭证

  2. 就是这样!我们将直接把你的免费 PDF 和其他福利发送到你的邮箱。

第一部分 – 基础方法

本书的这一部分将帮助你全面理解深度学习架构中的基础方法和技术。从深度学习生命周期开始,你将从高层次探讨各个阶段,从规划和数据准备到模型开发、洞察、部署和治理。接着,你将深入学习设计深度学习架构的复杂性,例如 MLP、CNN、RNN、自动编码器和变压器。你还将了解神经架构搜索这一新兴方法及其对深度学习领域的影响。

在本部分中,您还将深入探讨监督式和无监督深度学习的实际应用,涵盖如二分类、多分类、回归和多任务学习等主题,以及无监督预训练和表示学习。通过聚焦于实际应用,本部分提供了关于使用流行框架和编程语言实现深度学习模型的宝贵见解。

到本部分结束时,您将对深度学习架构、方法和生命周期有坚实的基础,这将帮助您继续面对在构建深度学习解决方案过程中遇到的其他挑战。

本部分包含以下章节:

  • 第一章, 深度学习生命周期

  • 第二章, 设计深度学习架构

  • 第三章, 理解卷积神经网络

  • 第四章, 理解递归神经网络

  • 第五章, 理解自编码器

  • 第六章, 理解神经网络变换器

  • 第七章, 深度神经架构搜索

  • 第八章, 探索监督式深度学习

  • 第九章, 探索无监督深度学习

第一章:深度学习生命周期

在本章中,我们将探讨深度学习生命周期的复杂性。深度学习生命周期与机器学习生命周期具有相似的特点,它既是一个框架,也是一个方法论,能够让一个深度学习项目要么取得巨大的成功,要么在适当的时候完全废弃。我们将掌握为什么这个过程是循环的,并深入理解生命周期的一些初步过程。此外,我们还将简单预览生命周期后续过程的高层次内容,这些内容将在未来的章节中深入探讨。

综合来看,本章将帮助你完成以下内容:

  • 理解深度学习生命周期与机器学习生命周期之间的相似性和差异性

  • 理解领域知识在深度学习项目中的作用

  • 理解规划深度学习项目的几个关键步骤,确保它能够切实创造现实世界的价值

  • 在高层次上掌握一些深度学习模型开发的细节

  • 在高层次上掌握模型解释的重要性以及深度学习解释技术的多样性

  • 探索模型部署的高层次概念及其治理

  • 学会选择必要的工具来执行深度学习生命周期中的过程

我们将在以下章节中讲解这些内容:

  • 机器学习生命周期

  • 深度学习生命周期的构建策略

  • 数据准备阶段

  • 深度学习模型开发

  • 提供模型洞察

  • 风险管理

技术要求

本章包括一些Python编程语言的实用实现。要完成本章内容,你需要一台安装了以下库的计算机:

  • pandas

  • matplotlib

  • seaborn

  • tqdm

  • lingua

代码文件可在 GitHub 上找到:github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook/tree/main/CHAPTER_1

理解机器学习生命周期

深度学习是更广泛的机器学习类别的一个子集。它与其他机器学习算法的主要区别在于其基础构建块——神经网络。随着深度学习自 2000 年代初以来的巨大进步,它通过其机器学习对等物实现了许多以前无法完成的壮举。特别是,深度学习在识别复杂模式方面取得了突破,这些模式存在于复杂且非结构化的数据中,例如文本、图像、视频和音频。如今,深度学习的一些成功应用包括图像中的人脸识别、音频数据中的语音识别和文本数据中的语言翻译。

另一方面,机器学习是更广泛的人工智能类别的一个子集。其算法,如基于树的模型和线性模型,虽然不被视为深度学习模型,但仍然适用于涉及表格数据的广泛应用场景,而表格数据是大多数小型和大型组织所存储的数据。这些表格数据可能存在于多个结构化数据库中,数据跨度可能达到 1 到 10 年的历史数据,且这些数据具有用于构建预测性机器学习模型的潜力。一些机器学习算法的显著预测应用包括金融行业中的欺诈检测、电子商务中的产品推荐以及制造业中的预测性维护。图 1*.1* 显示了深度学习、机器学习和人工智能之间的关系,以便更清晰地区分它们:

图 1.1 – 人工智能关系

图 1.1 – 人工智能关系

现在我们大致了解了深度学习和机器学习的概念,我们准备好一窥机器学习生命周期,如图 1**.2所示:

图 1.2 – 深度学习/机器学习生命周期

图 1.2 – 深度学习/机器学习生命周期

深度学习算法相比其他机器学习算法尽管更加先进和复杂,但确保这两个领域成功所需的指导方法无疑是相同的。机器学习生命周期涉及六个阶段,这些阶段之间以不同的方式相互作用:

  1. 规划

  2. 数据准备

  3. 模型开发

  4. 交付 模型洞察

  5. 模型部署

  6. 模型治理

图 1*.2* 展示了这六个阶段以及通过箭头表示的可能阶段转换。通常,一个机器学习项目会根据业务需求在各个阶段之间反复迭代。在深度学习项目中,大多数创新性的预测性应用场景需要手动数据收集和数据标注,这一过程属于数据准备阶段。由于这个过程通常非常耗时,尤其是当数据本身不容易获得时,一种解决方案是从一个可以接受的初始数据量开始,然后过渡到模型开发阶段,随后进入交付模型洞察阶段,以确保从想法中得到的结果是合理的。

在初步验证过程之后,依照业务需求,实践者将决定是否重新进入数据准备阶段,并根据不同数据量的里程碑,周期性地迭代这些阶段,直到模型开发和业务指标的结果都令人满意。获得必要的利益相关者批准后,项目将进入模型部署阶段,届时构建的机器学习模型将被提供,以便其预测结果得以使用。最后阶段是模型治理,在这一阶段,实践者执行管理已部署机器学习模型的风险、性能和可靠性的任务。模型部署和模型治理都值得更深入的讨论,并将在本书后期的章节中单独介绍。每当任何关键指标未能维持在某一确定的置信度水平时,项目将返回到数据准备阶段,并重新开始相同的流程。

理想的机器学习项目应根据业务需求,按周期性阶段流动。然而,机器学习项目通常容易遭遇高概率的失败。根据 Dimensional Research 和 Alegion 进行的一项调查,涵盖了来自 20 个不同业务行业的约 300 名机器学习从业者,78%的机器学习项目在部署前的某个阶段被拖延或受阻。此外,Gartner 预测,85%的机器学习项目将失败(venturebeat.com/2021/06/28/why-most-ai-implementations-fail-and-what-enterprises-can-do-to-beat-the-odds/)。通过预见意外并在它们发生之前预判失败,实践者可以在规划阶段及早避免潜在的失败因素。这也引出了图 1**.2中与垃圾图标捆绑在一起的概念。一个良好的项目,通常只有在交付模型洞察阶段被舍弃,那时已明确提出的模型和项目无法交付令人满意的结果。

现在我们已经概述了机器学习生命周期,让我们逐个阶段深入探讨,并将其分解为多个部分,帮助你发现完成每个阶段所需的关键技巧和方法。这些阶段将以抽象的方式进行讨论,并不是对你在项目中最终应采取的实际步骤的具体描述,因为每个项目都是独特的,策略应始终根据具体情况进行评估。

策划构建深度学习系统

深度学习模型只有成为执行某种操作的系统的一部分,才能实现实际的价值。将深度学习模型从研究论文带到实际的现实世界应用并非易事。因此,在进行任何项目之前进行适当的规划,是实现目标的更可靠和结构化的方式。本节将讨论一些在规划深度学习项目以走向成功时,可能会有帮助的考虑因素和策略。

启程

如今,深度学习从业者往往过于专注于模型构建过程中的算法部分。要避免被最先进SOTA)的研究技术吸引,需要相当大的心理耐力。像pixtopix这样的疯狂技术,它能够从草图或图像掩模中生成高分辨率的真实彩色图像,和像自然语言处理NLP)技术中的GPT-3(一个拥有 1750 亿参数的文本生成模型,由 OpenAI 开发)和 GPT-4(GPT-3 及其子模型的继任者,一个多模态文本生成模型,能够生成从文本摘要到代码生成的任何文本格式内容)一样,为什么不让它们成为主流呢?!

开个玩笑,要成为一名真正的深度学习架构师,我们需要达成共识:任何成功的机器学习或深度学习项目都是从业务问题出发的,而不是从你刚在网上读到的、附带公共 GitHub 仓库的闪亮新研究论文开始的。规划阶段通常会涉及许多对机器学习算法细节不太了解的业务高管,而且通常,同一批人根本不关心这些内容。对于专注于业务的利益相关者来说,这些算法令人望而却步,再加上人工智能技术本身采纳所面临的巨大心理障碍,这并不会让项目更容易被采纳。

评估深度学习的价值

深度学习在处理非结构化数据方面最为突出。这包括图像数据、文本数据、音频数据和视频数据。这在很大程度上得益于模型自动学习并从原始数据中提取复杂、高层次特征的能力。在图像和视频的案例中,深度学习模型能够捕捉空间和时间模式,识别物体、场景和活动。对于音频数据,深度学习能够理解语音、噪音和各种声音元素的细微差别,使得构建语音识别、语音助手和音频分类系统等应用成为可能。对于文本数据,深度学习模型能够捕捉上下文、语义和语法,从而支持如情感分析、机器翻译和文本摘要等自然语言处理(NLP)任务。

这意味着,如果您的公司在其业务流程中存在并利用这些数据,可能有机会借助深度学习来解决一个问题。然而,切勿仅仅为了使用深度学习而过度复杂化问题。用一个更容易理解的比喻来说,你不会用一把巨大的大锤去把钉子钉进木头。虽然可能有效,而且你或许能成功,但你可能会把钉子弄弯,甚至可能在使用过程中伤到自己。

一旦识别出一个问题,就要评估解决它的商业价值。并非所有问题都是一样的,它们可以根据业务影响、价值、复杂度、风险、成本和深度学习的适用性进行排序。通常,你应该寻找高影响、高价值、低复杂度、低风险、低成本并且适合深度学习的问题。虽然这些指标之间存在权衡,但简而言之,要确保你发现的问题值得用深度学习来解决。一个普遍的经验法则是,面对问题时,总是优先选择更简单的解决方案,即使这意味着放弃使用深度学习技术。简单的方法往往更可靠、成本更低、风险更小,且更快见效。

考虑一个问题,在视频流中需要移除背景场景,只保留人类或必要的物体不受影响,以便可以将更合适的背景场景叠加为背景。这是当今所有电影类型中,专业电影制作行业常见的问题。

语义分割是指在图像的宽度和高度维度上为每个像素分配标签的任务,这是一种解决此类问题所需的方法。在这种情况下,任务需要分配标签,以帮助识别哪些像素需要被移除。随着许多公开可用的语义分割数据集的出现,深度学习在语义分割领域取得了显著进展,使其能够实现对世界的非常满意的细粒度理解,足以广泛应用于自动驾驶和机器人导航等行业。然而,深度学习并不以 100%无误著称,几乎总是存在一些误差,即使在受控的评估数据集上也是如此。例如,在人体分割的情况下,模型可能会在细小的发丝区域产生最多的错误。大多数电影制作人追求电影的完美呈现,并要求每一个像素都被恰当且无误地移除,因为电影演员的时间非常昂贵。此外,若场景没有使用绿幕拍摄,那么手动去除物体将浪费大量时间和金钱,而这些物体本可以通过绿幕简单去除。这是一个不应过度复杂化问题的例子。要解决上述问题,你只需要一个绿幕:具体来说,罕见的色度键绿色。当绿幕在需要数字化叠加图像的区域妥善准备时,图像处理技术本身可以去除被认为属于色度键绿色范围内的小光强度范围的像素,并通过基于规则的解决方案有效地实现语义分割。绿幕是一种更简单、经济、万无一失且快速设置的解决方案。

这真是一口气说完!现在,我们来看看一个更简单的问题。假设我们需要自动化地识别下雨的情况。在这个应用场景中,理解识别下雨的实际需求和目标是非常重要的:仅仅在下雨发生的瞬间检测到雨是否足够吗?还是我们需要识别雨是否会在不久的未来发生?我们将如何使用降雨事件的信息?这些问题将帮助我们判断是否需要使用深度学习。我们作为人类,知道通过视觉输入可以预测雨,比如通过观察降水的存在或者云层的状况。然而,如果应用场景仅仅需要在下雨时进行检测,并且识别下雨的目标是决定何时给植物浇水,那么一个更简单的方法就是使用电子传感器来检测水的存在或湿度。只有当你想预测未来是否会下雨,比如说在 15 分钟内,深度学习才更有意义,因为有很多气象因素之间的相互作用可能会影响降雨情况。只有通过对每个应用场景进行头脑风暴,分析所有潜在的解决方案,甚至是深度学习之外的方案,才能确保深度学习相较于其他解决方案带来切实的商业价值。不要因为想用深度学习就随便应用它。

有时,当你直接考虑某个应用场景时,价值不清晰,或者即使价值很明确,但你却不知道如何执行时,可以考虑寻找同行业公司的一些参考项目。同一行业的公司通常会有很高的可能性优化相同的流程或解决相同的痛点。类似的参考项目可以作为设计深度学习系统的指南,并且能够证明正在考虑的应用场景值得使用深度学习技术。当然,并不是每个人都能获得这种详细信息,但你会惊讶于谷歌现在能告诉你什么。即使没有类似的项目可以作为直接参考,你仍然有可能从其他已经在同一行业内带来价值的机器学习项目中获得灵感。

不得不承认,有时拒绝深度学习确实是一个难以接受的决定,因为大多数从业者都是靠实现深度学习解决方案来获得报酬的。然而,及早放弃深度学习将使你能将时间集中在更有价值的问题上,这些问题更适合通过深度学习来解决,并且能够避免在某些情况下,简单的解决方案胜过深度学习时,削弱深度学习潜力的风险。深度学习是否值得采用的标准应当根据具体情况进行评估,作为一名从业者,最好的建议是遵循常识。花足够的时间进行问题探索和价值评估。你最不想发生的事情,就是花费大量时间准备数据、构建深度学习模型,并提供非常有说服力的模型见解,结果却发现你试图预测的标签对于业务没有足够的价值,无法进一步投资。

定义成功

曾经听过类似这样的句子吗:“我的深度学习模型在验证数据集上达到了 99%的准确率!”?数据科学家经常犯一个错误,那就是仅仅通过验证指标来判断一个机器学习项目的成功,这些验证指标通常是在模型开发过程中用于评估机器学习模型的表现。像准确率、精确度或召回率这样的模型构建指标在机器学习项目中确实很重要,但除非这些指标能够为业务带来价值,并以某种方式与业务目标相连接,否则它们往往毫无意义。一个项目可能取得了不错的准确率,但仍然未能实现预期的业务目标。这种情况通常出现在没有在早期定义出合适的成功指标,从而导致在数据准备和模型开发阶段使用了错误的标签。此外,即使模型指标对业务流程产生了积极影响,也有可能无法有效地传达给业务相关方,最糟糕的情况是,当报告按原样呈现时,模型的成功并未得到认可。

成功指标在早期定义时,充当着机器学习项目的保护线,确保项目目标与业务目标一致。保护线之一是,成功指标可以帮助指导选择一个适当的标签,该标签在推理时能切实改善业务流程,或以其他方式为业务创造价值。首先,让我们确保我们对标签的含义有统一的理解,标签是你希望机器学习模型预测的一个值。机器学习模型的目的是根据某些形式的输入数据自动分配这些标签,因此在数据准备和模型开发阶段,需要选择一个标签来服务于这一目的。选择错误的标签可能会对深度学习项目造成灾难性后果,因为有时候,当数据无法直接获取时,意味着项目必须从数据准备阶段重新开始。标签应始终间接或直接与成功指标相关联。

成功指标,顾名思义,可以是复数,范围从基于时间的成功定义或里程碑到整体项目成功,从无形到有形。最佳实践是通常进行头脑风暴并记录所有可能的成功标准,从低层次到高层次。另一个最佳实践是确保始终与无形指标一起定义有形成功指标。无形指标能产生意识,但有形指标确保事物是可衡量的,从而使其更加可达。以下是一些无形且难以衡量的指标示例:

  • 提高客户满意度

  • 提高员工表现

  • 改善股东前景

指标是衡量事物的方式,并与目标紧密相关,以确保交易的达成。目标本身可以是无形的,类似于前面列出的几个示例,但只要它与有形指标挂钩,项目就有了良好的开端。当你有了明确的目标时,问问自己,如何证明目标已经实现、展示或衡量。以下是一些可能与业务目标对齐的机器学习项目的有形成功指标示例:

  • 增加客户的停留时间,这可以作为客户满意度的一个代理指标

  • 增加公司收入,这可以作为员工表现的一个代理指标

  • 增加点击率CTR),这可以作为定向营销活动有效性的一个代理指标

  • 增加客户生命周期价值CLTV),这可以作为长期客户满意度和忠诚度的一个代理指标

  • 提高转化率,这可以作为促销活动和网站用户体验成功的一个代理指标

这个概念并不新颖,也不仅限于机器学习项目——几乎每个为公司进行的项目都需要与业务目标对齐。许多基础的项目管理技术同样适用于机器学习项目,花时间学习一些项目管理技能对机器学习项目来说是有益的,并且这些技能可以迁移到机器学习项目中。此外,由于机器学习被认为是一项基于软件的技术,软件项目管理方法论同样适用。

最后需要记住的一点是,机器学习系统不仅仅关乎你的机器学习模型有多先进,而是关于人类与机器智能如何协作,以实现更大的目标并创造价值。

规划资源

深度学习通常涉及拥有大量参数(即权重)的神经网络架构。这些架构的大小可以从仅包含几个参数到包含数百亿个参数。例如,OpenAI 的 GPT-3 文本生成模型包含 1750 亿个神经网络参数,存储大小约为 350 GB。这意味着,要运行 GPT-3,你需要一台至少有 350 GB 随机存取内存RAM)的机器!

深度学习模型框架,如 PyTorch 和 TensorFlow,已经被构建为可以与被称为图形处理单元GPU)的设备一起使用,这些设备能提供巨大的神经网络模型训练和推理加速。市面上的 GPU 设备通常配备 12 GB 的 GPU 内存,远远不足以在 GPU 模式下加载 GPT-3 模型。然而,仍然有方法将大型模型划分到多个 GPU 上,并在 GPU 上运行该模型。此外,某些方法还可以支持分布式 GPU 模型训练和推理,以支持在任何一个使用点更大的数据批量大小。GPU 并不是便宜的设备,最广泛使用的 GPU 品牌 Nvidia 的价格从几百美元到数十万美元不等。随着加密货币技术的兴起,GPU 的可用性显著下降,因为人们一有库存就会立即购买它们。所有这些都突显了在深度学习模型训练和推理之前规划计算资源的重要性。

在项目初期,将你的模型开发和部署需求与计算资源分配对齐是非常重要的。首先,通过浏览研究论文或提供技术总结的优质网站,评估适合当前任务的深度学习架构的规模范围,并为模型开发过程预留计算资源。

提示

paperswithcode.com 提供了按各种任务分类的广泛技术总结!

当计算资源不可随时使用时,确保你总是提前制定购买计划,尤其是涉及到 GPU 时。但如果不想使用物理机器怎么办?使用计算资源的另一种选择是使用付费云计算资源提供商,您可以轻松地在世界任何地方在线访问。在模型开发阶段,分配更多 GPU 和更大 RAM 的一个好处是,可以通过在训练过程中使用更大的数据批次,或者允许同时训练多个模型,从而加速模型训练。通常也可以使用仅 CPU 进行深度学习模型训练,但模型训练时间会不可避免地更长。

在训练过程中所需的基于 GPU 和 CPU 的计算资源,通常在推理时使用时被认为是过剩的。不同的应用程序有不同的部署计算需求,决定分配哪些资源规格可以通过以下三个问题来评估:

  • 推理请求的频率是多少?

    • 短时间内的多个推理请求可能意味着需要在多个计算设备上并行运行多个推理服务
  • 每次请求预测时,平均请求的样本数量是多少?

    • 设备的 RAM 需求应与批处理大小预期相匹配
  • 你需要多快的回复?

    • 如果响应时间要求为秒级或更快,则需要 GPU

    • 如果你不在乎响应时间,CPU 也能完成任务

资源规划不仅仅局限于计算资源规划,还扩展到人力资源规划。假设团队中有多少名深度学习工程师和数据科学家共同工作,最终将影响模型开发过程中所使用的软件库和工具的选择。选择这些工具的类比将在未来的章节中介绍。

下一步是准备数据。

数据准备

数据对于机器学习模型,就像燃料对于汽车、电力对于电子设备、食物对于身体一样。机器学习模型通过尝试捕捉提供的输入数据与输出数据之间的关系来工作。类似于人脑的工作方式,机器学习模型将尝试遍历收集到的数据示例,并慢慢建立记忆,以便将提供的输入数据映射到提供的目标输出数据。数据准备阶段包括为构建机器学习模型准备好可以直接使用的数据的方法和过程,其中包括以下内容:

  • 原始输入和目标输出数据的获取

  • 获取数据的探索性数据分析

  • 数据预处理

我们将在以下小节中讨论这些话题。

深度学习问题类型

深度学习可以大致分为两类问题类型,即有监督学习无监督学习。这两种问题类型都涉及构建一个能够根据明确的数据输入做出有根据的预测输出的深度学习模型。

有监督学习是一种涉及标签的问题类型,标签作为学习的真实来源。标签可以有多种形式,可以细分为两种问题类型,即分类回归。分类是指在给定输入数据时,预测特定离散类别的问题。许多更复杂的问题来自于基础的分类问题类型,如实例分割多标签分类目标检测。而回归则是指在给定输入数据时,预测连续数值的问题。同样,复杂问题类型也可以从基础的回归问题类型中派生出来,如多重回归图像边界框回归

另一方面,无监督学习是一种不涉及标签的问题类型,其目标可以有很大的差异。异常检测、聚类和特征表示学习是最常见的属于无监督学习类别的问题类型。

我们将在第八章《探索有监督深度学习》和第九章《探索无监督深度学习》中分别讲解这两种问题类型。

接下来,让我们了解在获取数据时应该考虑的事项。

获取数据

在深度学习的背景下,获取数据通常涉及非结构化数据,包括图像数据、视频数据、文本数据和音频数据。有时候,数据可以通过某些业务流程在数据库中预先存储并随时使用,但更多时候,它必须从头开始从环境中手动收集。此外,通常这些数据的标签并不容易获得,需要手动标注。随着深度学习算法能够处理和消化高度复杂数据的能力,它也需要比传统机器学习方法更多的数据。执行大量数据收集和数据标注的需求是深度学习今天被认为具有高门槛的主要原因。

在机器学习项目中,不要急于快速选择一个算法。花一些时间正式定义可以用来预测目标变量的特征。在这个过程中,寻求领域专家的帮助,并集思广益,寻找与目标变量相关的潜在预测特征。在实际项目中,通常会花费大量时间规划和获取数据,同时确保获得的数据适合机器学习模型使用,之后才会将剩余时间用于模型构建、部署和治理。虽然关于如何处理模型开发阶段的低质量数据已经进行了大量研究,但大多数技术并不全面,且在一定程度上只能掩盖数据固有的质量问题。如果在数据采集阶段对质量保证表现出无知,只在数据科学部分表现出热情,那么项目从一开始就注定会失败。

制定数据采集策略是一项艰巨的任务,特别是当你不知道什么才算是高质量数据时。让我们来看一下,在实际商业应用和机器学习的背景下,你应该考虑的一些数据质量的关键要素:

  • 代表性:数据在多大程度上能代表真实世界数据集?

  • 一致性:注释方法有多一致?相同的模式是否匹配相同的标签,还是存在一些不一致之处?

  • 全面性:收集的数据集中是否涵盖了特定标签的所有变体?

  • 独特性:数据中是否包含大量重复或相似的数据?

  • 公平性:收集的数据是否对某些特定标签或数据群体存在偏见?

  • 有效性:数据中是否包含无效字段?数据输入是否与其标签匹配?是否存在缺失数据?

让我们详细看看这些要素。

代表性

数据应尽可能模仿在模型部署期间将接收到的数据方式进行收集。在基于研究的深度学习项目中,研究人员通常会在一个封闭的环境中收集数据,并控制环境变量。研究人员偏好从控制环境中收集数据的原因之一是他们可以建立更稳定的机器学习模型,并通常试图证明某个观点。最终,当研究论文发布时,你会看到使用精心挑选的数据得出的惊人结果,旨在给人留下深刻印象。这些基于控制数据建立的模型,在应用到随机的、无法控制的现实世界示例时,往往会惨遭失败。别误会——拥有这些控制数据集在某些时候有助于建立更稳定的机器学习模型,但将无法控制的现实世界示例作为训练和评估数据集的主要部分,是实现通用模型的关键。

有时,获取的训练数据有保质期,并不会永远保持代表性。这个场景被称为数据漂移,将在本章末尾的管理风险部分中详细讨论。数据质量的代表性指标还应根据模型在部署期间将接收的数据的未来预期进行评估。

一致性

标注不一致的数据标签使得机器学习模型更难从中学习。当多个标注者之间的领域理念和标注策略存在差异且没有正确定义时,就会发生这种情况。例如,“Regular”和“Normal”意思相同,但对机器而言,它们是两个完全不同的类别;同样,“Normal”和“normal”仅有大小写差异!

在执行实际标注过程之前,练习在规划阶段制定合适的标签标注策略。对于一些简单的一致性错误,在数据标注后可以进行清理,但有些一致性错误可能难以检测且复杂难以修正。

全面性

机器学习致力于建立一个对任何特定标签的多种变体和视角都能保持稳健的决策机制。具备这一能力和成功实现它是两回事。决策稳健性的先决条件之一是用于训练和评估的数据本身必须足够全面,以涵盖每个标签的所有可能变体。如何判断全面性呢?这取决于标签的复杂性以及它们在模型部署时自然呈现的变体有多大。更复杂的标签自然需要更多的样本,而较简单的标签则需要更少的样本。

在深度学习的背景下,一个好的起点是每个标签至少拥有 100 个样本,并进行模型构建和推导模型见解的实验,以查看是否有足够的样本使模型能够对标签的未见变体进行泛化。当模型未能产生令人信服的结果时,您需要回到数据准备阶段,收集更多特定标签的数据变体。机器学习生命周期本质上是一个循环过程,您将在不同阶段之间实验、探索并验证,以获得解决问题所需的答案,因此不要害怕循环执行这些不同的阶段。

独特性

虽然拥有完整和全面的数据有助于构建一个能应对数据变化的稳健机器学习模型,但在获取的数据集中重复相同数据变体的版本则有可能导致模型产生偏见。一个有偏见的模型会做出偏颇的决策,这些决策可能不道德、违法,甚至有时完全没有意义。此外,任何特定标签的获取数据量在所有数据都重复或非常相似时变得毫无意义。

机器学习模型通常是在获取的部分数据上进行训练,然后在其他数据子集上进行评估,以验证模型在未见数据上的表现。当获取的数据集中的非独特部分偶然被放入评估分区时,模型可能会报告对重复数据输入存在偏见的评分。

公平性

获取的数据集是否适当地代表了少数群体?该数据集是否对人口中的多数群体存在偏向?机器学习模型出现偏见的原因可能有很多,但其中一个主要原因是数据表示偏见。确保数据公平、公正地表示是所有机器学习从业者的道德责任。偏见的类型有很多,因此这个话题将有自己的一章,并将在第十三章《探索偏见与公平》中介绍,偏见 和公平的缓解方法。

有效性

数据集中是否存在异常值?数据集中是否缺失数据?您是否不小心将一个空白的音频或图像文件添加到了正确收集和标注的数据集中?数据输入的标注标签是否被认为是有效标签?这些是考虑数据集有效性时应该问的一些问题。

无效数据对机器学习模型来说是无用的,其中一些会使所需的预处理变得复杂。无效性的原因可以从简单的人工错误到复杂的领域知识错误不等。减少无效数据的一种方法是将已验证的数据和未验证的数据分开。在数据样本进入已验证数据集类别之前,包含某种形式的自动化或人工数据验证过程。这些验证逻辑可以来源于业务流程或常识。例如,如果我们把年龄作为输入数据,存在可接受的年龄范围,也有一些完全不可能的年龄范围,如 1000 岁。设立简单的防护措施并在收集这些数据时验证这些值,可以立即纠正它们,从而获得准确且有效的数据。否则,在模型构建阶段,这些数据很可能会被丢弃。维护一个结构化的框架来验证数据可以确保大多数数据保持相关且可供机器学习模型使用,并避免简单的错误。

对于更复杂的无效数据,例如领域意识形态中的错误,领域专家在确保数据保持合理和逻辑性方面发挥着重要作用。在讨论如何收集和标注数据以进行模型开发时,始终确保包括领域专家来定义数据的输入和输出。

通过探索性数据分析(EDA)理解数据

数据获取后,分析数据以检查其特征、存在的模式以及数据的一般质量是至关重要的。了解你处理的数据类型可以帮助你为后续的模型构建阶段规划策略。绘制分布图、计算统计数据,并进行单变量和多变量分析,以理解数据之间的内在关系,这可以进一步确保数据的有效性。不同变量类型的分析方法不同,并且可能需要事先具备某种领域知识。在以下子部分中,我们将通过探索性数据分析EDA)实际处理基于文本的数据,以了解进行 EDA 任务的好处。

实际文本 EDA

在本节中,我们将使用 Python 代码手动探索和分析一个特定于文本的数据集,目的是在本书的后续部分使用相同的数据集构建深度学习模型。我们使用的数据集将基于印度电商网站上商品的文本描述预测商品的类别。这个用例将有助于自动将广告商品进行分组,以供用户推荐使用,并可以帮助提高电商网站的购买量:

  1. 我们先定义在笔记本中将使用的库。我们将使用pandas进行数据处理和结构化,matplotlibseaborn用于绘制图表,tqdm用于可视化迭代进度,lingua用于文本语言检测:

    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    from tqdm import tqdm
    from lingua import Language, LanguageDetectorBuilder
    tqdm.pandas()
    
  2. 接下来,让我们使用pandas加载文本数据集:

    dataset = pd.read_csv('ecommerceDataset.csv')
    
  3. pandas有一些方便的函数来可视化和描述加载的数据集;我们来使用它们。我们从可视化原始数据的三行开始:

    dataset.head(3)
    

    这将在你的笔记本中显示以下图形:

图 1.3 – 可视化文本数据集样本

图 1.3 – 可视化文本数据集样本

  1. 接下来,让我们通过可视化数据列的统计信息来描述数据集:

    dataset.describe()
    

    这将在你的笔记本中显示以下图形:

图 1.4 – 显示数据集的统计信息

图 1.4 – 显示数据集的统计信息

  1. 通过这些可视化,可以明显看出,数据集的描述与数据集中的内容一致,其中category列包含四个独特的类别,与名为description的文本描述数据列配对,且有证据表明它们是字符串。从描述函数中获得的一个重要见解是,文本描述中存在重复项。我们可以通过取所有重复项中的第一个示例行来删除重复项,但我们还必须确保重复项属于相同类别,因此我们来做一下:

    for i in tqdm(range(len(unique_description_information))):
         assert(
        len(
          dataset[
            dataset['description'] ==
            unique_description_information.keys()[i]
          ]['category'].unique()
        ) == 1
      )
    dataset.drop_duplicates(subset=['description'], inplace=True)
    
  2. 让我们检查一下各列的数据类型:

    dataset.dtypes
    

    这将在你的笔记本中显示以下图形:

图 1.5 – 显示数据集各列的数据类型

图 1.5 – 显示数据集各列的数据类型

  1. 当某些样本本身不是字符串数据类型时,比如空数据或数字数据,pandas 会自动使用Object数据类型,这会将整个列分类为 pandas 无法识别的数据类型。让我们检查空值:

    dataset.isnull().sum()
    

    这将给我们以下输出:

图 1.6 – 检查空值

图 1.6 – 检查空值

  1. 看起来描述列有一个空值,正如预期的那样。这可能是获取数据时的错误,或者它可能确实为空。无论如何,让我们删除这一行,因为我们无法恢复它,并将列转换为字符串:

    dataset.dropna(inplace=True)
    for column in ['category', 'description']:
        dataset[column] = dataset[column].astype("string")
    
  2. 之前,我们发现了四个独特的类别。让我们通过可视化其分布来确保每个类别都有足够的样本:

    sns.countplot(x="category", data=dataset)
    

    这将显示以下图形:

图 1.7 – 显示类别分布的图表

图 1.7 – 显示类别分布的图表

每个类别都有相当数量的数据样本,并且看起来没有异常类别。

  1. 这里的目标是通过印度电子商务网站上的商品描述预测商品类别。从这个背景来看,我们知道印度公民讲印地语,因此数据集可能不仅包含英语数据。让我们尝试使用一个名为 Lingua 的开源语言检测工具来估计和验证数据集中可用的语言。Lingua 结合了基于规则的方法和基于机器学习模型的方法,能够检测 70 多种文本语言,非常适合短语、单词和句子的检测。由于这个原因,Lingua 在运行时和内存性能上表现更佳。让我们从lingua库初始化语言检测实例开始:

    detector = LanguageDetectorBuilder.from_all_languages(
          ).with_preloaded_language_models().build()
    
  2. 现在,我们将随机抽取数据集的一小部分进行语言检测,因为检测算法需要时间完成。使用数据的 10%作为样本应能充分理解数据:

    sampled_dataset = dataset.sample(frac=0.1, random_state=1234)
    sampled_dataset['language'] = sampled_dataset[
        'description'
    ].progress_apply(lambda x: detector.detect_language_of(x))
    
  3. 现在,让我们可视化语言的分布:

    sampled_dataset['language'].value_counts().plot(kind='bar')
    

    这将显示以下图形:

图 1.8 – 文本语言分布

图 1.8 – 文本语言分布

  1. 有趣的是,Lingua 检测到了一些异常样本,它们不是英语。异常语言看起来可能是 Lingua 检测错误的结果。印地语也被检测到了,这比其他语言更有说服力,因为数据来自印度的电子商务网站。让我们来看一下这些样本:

    sampled_dataset[
        sampled_dataset['language'] == Language.HINDI
    ].description.iloc[0]
    

    这将显示以下文本:

图 1.9 – 可视化印地语文本

图 1.9 – 可视化印地语文本

  1. 看起来这里混合了印地语和英语。那其他语言呢,比如法语?

    sampled_dataset[
        sampled_dataset['language'] == Language.FRENCH
    ].description.iloc[0]
    

    这将显示以下文本:

图 1.10 – 可视化法语文本

图 1.10 – 可视化法语文本

  1. 看起来potpourri是这里的重点词,因为这是一个借用的法语词汇,但文本总体上还是英语。

  2. 由于语言列表中不包括不使用空格作为逻辑词单元分隔符的语言,我们尝试通过基于空格的词分隔来衡量词语的分布。词计数和字符计数会影响深度学习神经网络的参数,因此在 EDA 过程中理解这些值会非常有用:

    dataset['word_count'] = dataset['description'].apply(
        lambda x: len(x.split())
    )
    plt.figure(figsize=(15,4))
    sns.histplot(data=dataset, x="word_count", bins=10)
    

    这将显示以下条形图:

图 1.11 – 词计数分布

图 1.11 – 词计数分布

通过对文本数据的探索和分析,我们可以推测出几个原因,这些原因有助于在模型开发阶段确定我们应该使用的模型类型和结构:

  • 标签样本采样得当,每个标签有 5,000-11,000 个样本,这使得它们适用于深度学习算法。

  • 原始数据不干净,存在缺失数据和重复项,但可以通过手动处理修复。直接使用原始数据进行模型开发可能会导致模型产生偏差。

  • 数据集包含多种语言,但主要是英文文本;这将帮助我们在模型开发阶段做出合适的模型选择。

  • 大多数样本少于 1,000 个单词,一些样本有 1,000-8,000 个单词。在一些非关键性应用场景中,我们可以安全地将单词数限制在大约 1,000 个单词,以便我们可以构建一个具有更好内存和运行时性能的模型。

前面的实际示例应该提供了一个简单的 EDA 体验,足以让你理解在进入模型开发阶段之前执行深入 EDA 的好处和重要性。与实际文本 EDA 类似,我们在 Packt GitHub 仓库中为其他数据集准备了一个实际的 EDA 样本工作流,包括音频、图像和视频数据集,你可以探索这些内容,亲自动手实践。

本节中需要掌握的一个主要概念是 EDA 的重要性,以及你应该展现出多大的好奇心来揭示数据的真相。一些方法可以推广到其他类似的数据集,但将任何特定的 EDA 工作流视为灵丹妙药,会使你忽视人们在这一领域不断贡献的研究。每当你怀疑数据中存在某些问题时,就要对数据提出疑问,并尽可能通过手动或自动检查来揭示答案。在获得这些答案时要富有创造力,并保持对学习新方法的渴望,探索如何在数据中发现关键信息。

在本节中,我们已通过方法论和实践步骤讲解了不同类型数据的 EDA 过程。接下来,我们将探讨为实际模型使用准备数据需要做什么。

数据预处理

数据预处理包括数据清理、数据结构化和数据转换,以便深度学习模型能够使用处理后的数据进行模型训练、评估和部署推理。处理后的数据不仅仅是为了让机器学习模型接受,还应该以一种优化学习潜力并提高机器学习模型指标性能的方式进行处理。

数据清理是一个旨在提高数据质量的过程。EDA 过程是识别数据集问题的前提,只有在进行数据清理之前,才能发现数据中的任何问题。数据清理和 EDA 通常是迭代执行的,直到达到令人满意的数据质量水平。清理可能很简单,比如删除重复值、删除空值或删除不合逻辑的值,无论是从常识角度还是从业务逻辑角度来看。这些都是我们之前解释过的概念,同样的风险和问题也适用。

数据结构化是一个协调数据摄取和加载过程的过程,涉及从存储的数据中加载已经清理并验证过质量的数据。这个过程决定了数据应如何从一个或多个源加载,并输入到深度学习模型中。听起来很简单,对吧?如果这是一个小型的单一 CSV 数据集,可能不会出现性能或内存问题,这会非常简单。然而,实际上,在数据可能因存储限制而被分割并存储在不同源的情况下,这个过程可能非常复杂。以下是一些你需要在此过程中考虑的具体因素:

  • 你的计算机有足够的内存来处理你所需的批次大小,以供模型使用吗?确保同时考虑模型大小,以免出现内存过载和内存溢出OOM)错误!

  • 你的数据来自不同的来源吗?确保你拥有访问这些数据源的权限。

  • 访问这些源时的速度延迟可接受吗?考虑将数据转移到一个更好的硬件资源上,这样你就能以更高的速度访问,例如使用固态硬盘SSD)代替硬盘驱动器HDD),并从远程网络可访问的源转移到直接的本地硬件源。

  • 你甚至有足够的本地存储空间来存储这些数据吗?确保你有足够的存储空间来保存这些数据,不要超载存储空间,以免影响性能,甚至导致计算机崩溃。

  • 优化数据加载和处理过程,使其更加高效。存储和缓存固定的数据处理输出,这样你可以节省时间,避免重新计算这些输出。

  • 确保数据结构化过程是确定性的,即使有些过程需要随机性。随机确定性指的是当随机性在循环重复时可以被复现。确定性有助于确保已获得的结果可以被复现,并确保模型构建方法可以公正且可靠地进行比较。

  • 记录数据,以便在需要时调试过程。

  • 数据划分方法。确保选择适合你数据集的正确交叉验证策略。如果包含时间特征,考虑是否应该构建基于时间的划分方法,其中训练数据来自早期时间的示例,而评估数据则来自未来。如果没有,分层划分方法将是最佳选择。

不同的深度学习框架,例如基于 TensorFlow 的keras,基于 PyTorch 的Catalyst,基于 PyTorch 的fast ai,基于 PyTorch 的pytorch lightning,以及基于 PyTorch 的ignite

最后,数据转换是一个应用独特数据变量特定预处理的过程,用以将原始清理后的数据转化为更具表现力、可用和可学习的格式。在执行数据结构化和转换过程时,一个重要的因素是你打算使用的深度学习模型类型。任何形式的数据转换通常依赖于深度学习架构,并且依赖于它能接受的输入类型。最广为人知和常见的深度学习模型架构是为了解决特定的数据类型问题而发明的,例如卷积神经网络用于图像数据,变换器模型用于基于序列的数据,基础的多层感知器用于表格数据。然而,深度学习模型被认为是灵活的算法,可以适应不同形式和大小的数据,即使在多模态数据条件下也能处理。通过过去几年与领域专家的合作,深度学习专家已经能够构建出能够处理多种数据模态甚至多个非结构化数据模态的创意深度学习架构,这些架构在学习跨模态模式方面取得了成功。以下是两个例子:

  • 强健的自监督音视频语音识别,由 Meta AI(前 Facebook)发布(arxiv.org/pdf/2201.01763v2.pdf

    • 这解决了在多重发言的情况下进行语音识别的问题,构建了一个基于深度学习变换器的模型,可以同时接受音频和视觉数据,称为 AV-HuBERT。

    • 视觉数据作为补充数据,帮助深度学习模型分辨应该关注哪个说话者。

    • 它在 LRS3-TED 视觉和音频唇语识别数据集上达到了最新的最先进的结果

  • 通过简单的序列到序列学习框架统一架构、任务和模态,由达摩院和阿里巴巴集团发布(arxiv.org/pdf/2202.03052v1.pdf

    • 他们构建了一个模型,该模型可以处理文本和图像数据,并发布了一个预训练模型

    • 在 COCO 字幕数据集上完成了图像标注任务,并达到了最先进的结果

也就是说,数据转换主要分为两部分:特征工程数据缩放。深度学习以其特征工程能力而闻名,特征工程替代了从原始数据中手动构建定制特征的需求。然而,这并不意味着始终没有进行特征工程的意义。许多成功的深度学习模型利用了经过工程处理的特征作为输入。

既然我们知道了数据预处理的内容,让我们理论上和实践上讨论和探索不同的非结构化数据预处理技术。

文本数据预处理

文本数据可以是不同语言的,并存在于不同领域,从描述性数据到信息文档和自然人类文本评论。以下是一些最常用的文本数据预处理方法,它们被广泛应用于深度学习:

  • 词干提取:一种去除单词后缀的过程,试图将单词还原为其基础形式。这有助于促进不同形式的同一单词的特征跨使用。

  • 词形还原:一种将单词简化为其基础形式的过程,从而生成真实的英语单词。词形还原具有与词干提取相似的许多好处,但由于其生成的语言学有效的单词简化输出,被认为是更好的选择。

  • 文本标记化,通过字节对编码BPE):标记化是将文本分割成不同部分的过程,这些部分将被编码并供深度学习模型使用。BPE 是一种基于子词的文本分割算法,它允许常见词汇作为单个标记输出,而稀有词汇则被分割成多个标记。这些分割后的标记可以复用来自匹配子词的表示。这样做是为了减少同时存在的词汇量,减少超出词汇表范围的标记,并使标记表示的学习更加高效。

一种不常见的预处理方法,但对于构建更具泛化能力的文本深度学习模型非常有用的是文本数据增强。文本数据增强可以通过几种方式进行:

  • 用同义词替换动词:这可以通过使用来自 NLTK 库的 WordNet 英语词汇数据库中的同义词词典集来完成。获得的增强文本将通过动词同义词替换保持相同的含义。

  • 回译:这涉及将文本翻译成另一种语言,再翻译回原始语言,使用像 Google 这样的翻译服务或开源的翻译模型。获得的回译文本将会以略微不同的形式呈现。

音频数据预处理

音频数据本质上是基于序列的数据,在某些情况下,可能存在多个序列。音频最常用的预处理方法之一是将原始音频数据转换为不同形式的频谱图,使用短时傅里叶变换STFT),这是一种将音频从时域转换为频域的过程。频谱图音频转换使得音频数据能够在多个频率范围内被分解和表示,而不是像单一波形那样表示所有音频频率的信号组合。这些频谱图是二维数据,因此可以作为图像处理,并输入卷积神经网络。数据缩放方法,如对数缩放和对数梅尔缩放,通常也会应用于这些频谱图,以进一步强调频率特征。

图像数据预处理

图像数据增强是一种基于图像的特征工程技术,能够增加原始数据的全面性潜力。应用此技术的最佳实践是构建数据流水线,在训练过程中按批次随机应用图像增强,而不是为深度学习模型提供固定的增强数据集。选择图像增强类型需要理解使用案例的业务需求。以下是一些不适合应用某些增强方法的示例:

  • 当图像的方向影响目标标签的有效性时,方向修改类的数据增强方法,如旋转和图像翻转,不适用

  • 当图像的颜色影响目标标签的有效性时,颜色修改类的数据增强方法,如灰度、通道混洗、色调饱和度变化和 RGB 偏移并不适用

在排除显然不适用的数据增强方法后,找出最佳增强方法列表的一个常见但有效的方法是通过迭代实验和模型比较。

开发深度学习模型

让我们从简短回顾一下什么是深度学习。深度学习的核心基础构建块是神经网络。神经网络是一种旨在模拟人类大脑的算法。它的构建块叫做神经元,模拟了人类大脑中数十亿个神经元。在神经网络的上下文中,神经元是存储简单信息(称为权重和偏置)的对象。把它们看作是算法的记忆。

深度学习架构本质上是具有三层或更多神经网络层的神经网络架构。神经网络层可以分为三大类——输入层、隐藏层和输出层。输入层是最简单的层组,其功能是将输入数据传递给后续层。该层组不包含偏置,可以看作是被动神经元,但它的连接中仍包含到后续层神经元的权重。隐藏层由包含偏置的神经元组成,并且与后续层的神经元之间有权重连接。最后,输出层由与类别数量和问题类型相关的神经元组成,并包含偏置。计算神经网络层时的最佳实践是排除输入层。因此,具有一个输入层、一个隐藏层和一个输出层的神经网络被视为一个两层神经网络。下图展示了一个基本的神经网络,也叫做多层感知器MLP),它有一个输入层、一个隐藏层和一个输出层:

图 1.12 – 一个简单的深度学习架构,也叫做 MLP

图 1.12 – 一个简单的深度学习架构,也叫做 MLP

作为更广泛机器学习类别的一个子集,深度学习模型能够通过损失函数和优化算法从数据中学习模式,优化算法用于优化损失函数。损失函数定义了模型的误差,以便更新其记忆(权重和偏差),使其在下一次迭代中表现得更好。优化算法是一种决定根据损失值更新权重的策略的算法。

通过这个简短的回顾,让我们深入总结一下常见的深度学习模型家族。

深度学习模型家族

这些层可以有多种形式,因为研究人员已经能够发明新的层定义来应对新的问题类型,并几乎总是配备有非线性激活函数,使得模型能够捕捉数据之间的非线性关系。随着层的变化,出现了许多不同的深度学习架构家族,专门针对不同的问题类型。一些最常见和广泛使用的深度学习模型如下:

  • 多层感知机MLP)用于表格数据类型。将在第二章中详细探讨,设计深度学习架构

  • 卷积神经网络用于图像数据类型。将在第三章中详细探讨,理解卷积神经网络

  • 自编码器用于异常检测、数据压缩、数据去噪和特征表示学习。将在第五章中详细探讨,理解自编码器

  • 门控循环单元GRU)、长短期记忆网络LSTM)和变压器用于序列数据类型。这些将在第四章中详细探讨,理解循环神经网络,以及第六章中详细探讨,理解神经网络变压器

这些架构将是第 2 到第六章的重点,我们将讨论它们的方法论,并进行一些实际评估。接下来,让我们探索深度学习中可以解决的问题类型。

模型开发策略

今天,由于深度学习框架如 PyTorch 和 TensorFlow 的出现,以及它们的高级库封装,深度学习模型的发明和创建变得容易。此时,你应该选择哪个框架,取决于它们的接口偏好,因为这两个框架都经过多年改进,已经成熟。只有在迫切需要一个非常自定义的功能来应对独特问题时,才需要选择能够执行你所需要的框架。一旦选择了深度学习框架,深度模型的创建、训练和评估过程基本上就涵盖了所有内容。

然而,这些框架并不直接提供模型管理功能。模型管理是一个技术领域,它使团队、企业和深度学习从业者能够可靠、快速和高效地构建模型、评估模型、交付模型洞察、将模型部署到生产环境并管理模型。模型管理有时也被称为机器学习运维MLOps)。你可能仍然在想,为什么需要这样的功能,尤其是如果你曾在 Kaggle 这样的平台上构建过深度学习模型,Kaggle 提供数据和机器学习问题作为竞赛。那么,以下是一些推动你需要利用这些功能的因素:

  • 手动比较模型很繁琐:

    • 手动在 Excel 表格中输入性能数据以追踪模型性能既慢又不可靠
  • 模型产物很难追踪:

    • 一个模型有许多产物,比如它的训练权重、性能图表、特征重要性和预测解释

    • 比较模型产物也很繁琐

  • 需要进行模型版本管理,以确保模型构建实验不会被重复:

    • 用最可靠的模型洞察覆盖表现最好的模型,是你最不希望经历的事情

    • 版本控制应该依赖于数据分区方法、模型设置和软件库版本

  • 部署和管理模型并不简单

根据参与项目的团队规模以及组件需要重复使用的频率,适合使用不同的软件和库。这些软件和库分为付费和免费的(通常是开源的)类别。Metaflow,一个开源软件,适用于数据科学团队规模较大的情况,其中组件需要在其他项目中频繁复用,而MLFlow(开源软件)更适合小型团队或单人团队。其他值得注意的模型管理工具包括Comet(付费)、Weights & Biases(付费)、Neptune(付费)和Algorithmia(付费)。

这样,我们已经简要概述了深度学习模型开发的方法和策略;接下来我们将在章节中深入探讨模型开发的相关话题。但在此之前,让我们继续概览模型洞察的交付主题。

交付模型洞察

模型性能指标,在仅用于模型比较和选择的情况下,通常不是可靠地获取最佳模型的最有效方法。当人们关注机器学习模型可能做出的决策时,他们通常需要更多的信息和洞察力,以最终将信任寄托于模型的决策能力。最终,当模型不被信任时,它们就不会被部署。然而,信任不仅仅依赖于模型的洞察力。建立对模型的信任涉及确保准确、可靠和无偏的预测,这些预测与领域专业知识和商业目标一致,同时向利益相关者提供有关模型性能指标、决策逻辑及其预测背后理由的洞察。解决潜在偏见并展示公平性是赢得对模型可靠性信心的关键。这个持续的信任建立过程超越了初始部署,因为模型必须始终展现合理的决策过程、证明预测的合理性,并保持无偏的表现。通过建立信任,模型成为一个有价值且可靠的工具,能在现实应用中发挥作用,推动各个领域和行业的广泛采用和利用。

提供对业务有价值的模型洞察。除了为确保模型信任并消除信任问题而提供模型洞察外,实际的性能指标同样重要。尽可能将模型指标转化为通俗易懂的商业指标,以有效地传达模型可能对业务带来的积极影响。成功指标应在规划阶段定义,并在此阶段报告实际值。

在模型部署后,建立对模型的信任并不会停止。类似于人类在生活中需要解释自己的决策,机器学习模型(如果预期替代人类来自动化决策过程)也需要如此。这一过程被称为预测解释。在某些情况下,模型的决策被期望作为参考,其中会有一个人类专家参与验证决策,确保决策正确后再执行。预测解释在这种情况下几乎总是必需的,因为模型的使用者希望了解模型为何做出预测,而不是直接使用预测结果。

模型洞察力也可以帮助你提升模型的表现。记住,机器学习生命周期本质上是一个迭代过程。以下是一些可能出现这种情况的具体示例:

  • 你意识到模型对某一特定群体存在偏见,并决定回到数据获取阶段,增加来自该群体的数据,或者更换为对偏见具有鲁棒性的建模技术。

  • 你意识到模型在某一类上的表现不好,然后回到模型开发阶段,使用一个不同的深度学习模型损失函数,专注于表现不佳的那一类。

深度学习模型被认为是一个黑箱模型。然而,实际上,今天已有许多关于深度学习解释方法的研究论文,这些方法使得深度学习突破了黑箱的局限性。我们将在本书的第二部分深入探讨我们可以如何解释并为深度学习模型提供洞见。

现在我们对深度学习生命周期中的相关过程有了更多的了解,在接下来的部分,我们将讨论你需要关注的、可能贯穿整个项目生命周期的风险。

风险管理

深度学习系统从一开始就暴露于多种风险中,从构思到系统采纳。通常,分配到深度学习项目中的大多数人仅负责机器学习生命周期的某个特定阶段,如模型开发或数据准备。当生命周期的一个阶段的工作在后期产生问题时,尤其是当相关团队成员缺乏对整个大局的理解时,这种做法可能会带来不利影响。深度学习系统中涉及的风险通常与机器学习生命周期各个阶段之间的交互有关,并遵循垃圾进,垃圾出的概念。确保所有参与构建系统的人都对系统最终输出的结果负责,而不仅仅是各个独立阶段的结果,这是管理机器学习系统风险的基础要素之一。

那么,风险是什么呢?让我们从一个可以在任何实际成果产生之前就能处理的问题开始——也就是在评估用例的价值时发生的事情。

伦理和监管风险

深度学习几乎可以应用于任何行业,但一些最难以在其中推广深度学习的行业是高度监管的行业,如医疗和金融行业。这些行业的法规最终决定了深度学习系统在行业中能做什么或不能做什么。这些法规大多由政府引入,通常涉及伦理和法律的考量。在这些高度监管的行业中,常常会出现每月、每周甚至每天进行的审计,以确保公司符合所施加的法规。某些行业之所以受到更加严格的监管,主要是因为这些行业的任何行为所带来的后果会对人民或国家的福祉造成巨大的成本。深度学习系统需要以符合这些法规的方式进行构建,以避免面临停用的风险,无法被采纳的风险,甚至最严重的,面临监管官员巨额罚款的风险。

有时,深度学习模型的表现可以远超其人类同行,但另一方面,没有任何深度学习模型是完美的。人类会犯错,大家都知道这一点,但我们需要意识到的另一个现实是,机器无疑也会犯错。最大的风险是,当人类过度信任机器,甚至完全依赖机器做决策时!那么,我们该如何考虑这些错误,并且谁将为这些错误负责呢?

让我们看看在人类没有机器的情况下,我们会怎么做。当风险较高时,重要的决策总是需要经过一系列的审批层级,才能最终做出决定。这些审批层级表明了重要决策需要有责任和可靠性。在决策过程中获得的审批越多,我们就越能说自己在做出这一重要决策时充满信心。一些常见的需要审批层级的重要决策包括:在公司中决定雇佣一名员工、决定向客户收取的保险费用,或者决定是否将资金投入到某个企业。考虑到这一点,深度学习系统在面对高风险应用时,需要具备类似的审批工作流程,以便在深度学习模型做出某个预测决策时获得批准。这些工作流程可以包括任何形式的洞察力,解释预测结果可以帮助领域专家更容易地判断决策的有效性。增加人类的参与使得深度学习系统变得更加符合伦理,并且足够值得信赖,能够成为高风险决策流程的一部分。

让我们以一个医疗行业的使用案例为例——例如,通过 X 光扫描预测肺病的使用案例。从伦理角度来看,深度学习模型完全有能力通过预测极其危害健康的疾病,如晚期肺癌,剥夺一个人的生存希望,这显然是错误的。如果预测到的极端疾病被误诊,患者可能会不必要地感到悲痛,或花费不必要的金钱进行昂贵的检查来验证结果。在深度学习系统中设立审批工作流程,允许医生将这些结果作为辅助方法使用,将解决使用自动决策系统时可能产生的伦理问题。

业务背景不匹配

重申在定义成功部分提到的观点,将所需的深度学习输入数据和目标标签选择与业务背景对齐,并了解这些目标预测如何被使用,是深度学习系统能否被采纳的关键所在。这里的风险在于,当业务价值未被恰当地定义,或者深度学习系统未能正确匹配时,可能会导致系统的失败。即使目标适合业务背景,如何消费这些预测才是获取价值的关键。如果在任何方面未能与业务背景匹配,系统可能会被拒绝。

理解业务背景涉及了解目标用户群体是谁,以及他们不包括谁。这个过程中的一个关键步骤是记录和构建用户群体画像。用户画像包含了关于他们的工作流程、历史、统计数据和关注点的信息,这些信息应当提供构建一个符合潜在用户具体需求的系统所需的背景。不要害怕进行市场调研,并在构建用户画像的过程中对目标用户需求进行适当验证。毕竟,构建一个深度学习系统需要付出大量努力,如果浪费时间构建一个没人愿意使用的系统,那就得不偿失。

数据收集和标注的风险

你的机器学习模型的效果将取决于数据的质量。与机器学习相比,深度学习需要大量更多的数据。此外,通常对于新的深度学习使用案例,数据不是现成的,需要手动收集并精心标注。确保数据收集和标注的方式能够保证质量,是一项非常艰巨的任务。

用于收集和标注数据以供深度学习使用的策略和方法因使用案例不同而有所差异。有时,数据收集和标注过程可以同时进行。这种情况可以发生在自然标签存在时,或者在数据收集之前已经预先指定标签时:

  • 自然标签 是在一段时间后通常会自然产生的标签。例如,这可以是通过超声波图像确定婴儿的性别;另一个用例是房屋价格作为标签,其输入数据为该房产的一系列图像。

  • 预先指定的标签 是在收集输入数据时预先确定的标签。例如,这可以是在受控环境中收集的一些语音数据的性别标签,仅用于构建机器学习模型,或者在拍摄面部照片以构建机器学习模型之前的某项调查中的个人年龄。

这两种类型的标签相对来说风险较低,因为标签很可能是准确的。

一种最终的数据收集和数据注释方法,具有相当大的风险,是在收集数据后进行标注。后注释 需要对所需标签的特征具有某种形式的领域知识,并且由于人为评估错误,不总是导致 100%的真实值。无意识的标注错误发生在标注者仅仅因为偶然而导致错误。另一方面,有意识的标注错误发生在标注者有意决定以错误的标签决策。这种意图可能源于数据中某种模式所根深蒂固的或宽泛的信念,或者仅仅是为某种原因故意制造的明显错误。这里所呈现的风险是标签可能被错误地注释。幸运的是,有几种策略可以执行以消除这些风险;这些将在以下段落中介绍。

由于人类易于出现一定程度的错误,无意识的标注错误很难避免。然而,不同的人通常有不同程度的注意力和选择性标签者的选择可能对某些人来说是一种可行的选择。如果标签者仅因金融补偿而被聘请来标注您的数据标签,另一个可行的选择是在提供需要标记数据的数据之间周期性地提供已经标记的秘密数据以供标签者标记。这种策略允许您评估单个标签者的表现,并根据其注释工作的准确性提供补偿(是的,我们也可以使用用于评估机器学习模型的指标)。作为积极的副作用,标签者还将被激励在他们的注释工作中表现更好。

有意识的标注错误是这里最危险的风险,因为它们可能影响整个数据集的质量。当对与任何目标标签相关的数据模式达成错误共识,并应用于整个标注过程中时,标签的合法性直到机器学习生命周期的后期才会被发现。机器学习模型仅具备学习所需模式的技术,以便将输入数据映射到提供的目标标签,即使标签是错误的,它们也很可能表现得很好。只要标注者在标注过程中强加了某种模式,不论对错,机器学习模型都会尽力学习所需的模式,以执行准确的输入到输出映射。那么,我们如何减轻这种风险呢?确保在领域专家的帮助下正确定义意识形态,在减轻过程中起着重要作用,但这一策略本身的有效性因涉及的领域专家数量、专家的专业水平、标注者数量以及收集数据的异常程度或质量而有所不同。

即使是领域专家有时也会犯错。以医生为例——你有多少次听说过医生开错药方或做出错误的医学评估?你有多少次误听别人说话后不得不猜测对方说了什么?在最后一个例子中,领域专家就是你,而领域是语言听力理解。此外,当标注者超过一个人并形成标注团队时,一个最突出的风险就是不同偏好的领域意识形态之间的标注数据不匹配。有时,这种情况发生是因为某些标签在数字格式中可能存在不同的变体,或者存在让标注者分析能力受阻的混乱模式。有时,这也可能是由于标注者或领域专家对某个标签或输入数据的固有偏见。随后,数据中的偏见会导致模型中的偏见,并引发伦理问题,从而削弱对机器学习模型决策的信任。当对机器学习模型缺乏信任时,项目将无法被采纳,并失去其商业价值。关于偏见、公平和信任的主题将在第十三章中更广泛地讨论,该章将详细阐述偏见的来源及其缓解方式。

标签错误但仍有正确标签的数据称为噪声标签。幸运的是,深度学习中有一些方法可以帮助你绕过噪声标签,例如弱监督学习。然而,请记住,最好的做法是从源头上解决问题,即在数据收集和标注时解决,而不是事后再修正。让我们深入探讨另一种可以使标注过程更安全的策略。深度学习项目的数据标注过程通常涉及使用软件工具来标注特定所需的标签。软件工具使得标注工作更快、更轻松,使用标注协作软件工具可以让标注工作更加防错。一个好的协作标注工具会让标注人员以某种方式对齐他们的数据发现,从而促进标注时的一致性。例如,当识别到容易误解的模式时,自动通知整个标注团队,可以帮助防止更多数据被错误标注,并确保之前相关的数据得到重新审查。

最后需要提到的是,未标记数据并不是一个风险,而是机器学习中巨大的潜力。尽管它缺乏具体的标签来实现特定目标,但数据之间存在固有的关系是可以学习的。在第九章《探索无监督深度学习》中,我们将探讨如何利用无监督学习将这些数据作为后续监督学习的基础,这就是更广为人知的半监督学习工作流程。

接下来,我们将讨论与机器学习模型使用数据时相关的另一个安全风险。

数据安全风险

在机器学习的背景下,安全性与防止数据被未经授权使用、保护数据隐私以及防止与数据使用相关的非预期事件或攻击有关。这里的风险是当数据安全受到破坏时,可能会导致未能遵守监管标准、模型性能的下降,或商业流程的破坏与损毁。在本节中,我们将介绍四种数据安全风险,分别是敏感数据处理、数据许可、软件许可和对抗性攻击。

敏感数据处理

一些数据比其他数据更敏感,并且可能与监管风险相关。敏感数据引发了数据隐私法规,这些法规管理在不同法域中个人数据的使用。法规的具体内容有所不同,但通常围绕合法保护个人数据使用权利,并要求对这些数据采取的任何行动都必须获得同意,同时在使用此类数据时需要遵循相关条款。这些法规的例子包括涵盖欧盟的通用数据保护条例GDPR)、涵盖新加坡的个人数据保护法PDPA)、仅涵盖美国加利福尼亚州的加利福尼亚消费者隐私法CCPA)以及涵盖美国弗吉尼亚州的消费者数据保护法CDPA)。这意味着,你不能仅仅收集被归类为个人的数据显示、标注数据、建立模型并部署,而不遵守这些法规,因为在某些法域中,这样做会被视为犯罪。除了请求同意外,常用的一种方法是通过匿名化数据来减轻这种风险,使得数据无法被任何单个个体识别。然而,匿名化必须可靠地进行,以确保关键的通用信息得以保留,同时可靠地消除任何重建个人身份的可能性。确保敏感和个人数据得到妥善处理,在建立机器学习模型的决策信任方面有着重要作用。在处理敏感和个人数据时,务必保持极高的谨慎,以确保你的机器学习项目的长期可持续性。

数据与软件许可

尤其对于深度学习项目来说,构建一个高质量的模型需要大量数据。公开可用的数据集的存在有助于通过部分消除收集和标注数据所需的成本和时间,从而缩短项目的时间和复杂性。然而,大多数数据集与软件一样,都有相关的许可协议。这些许可协议规定了与其关联的数据可以如何使用。关于商业问题的机器学习模型,数据许可的最重要标准是数据是否允许商业使用。由于大多数商业用例因其产生的利润而被视为商业用例,因此具有禁止商业使用条款的数据集不能使用。防止商业使用的许可条款的一个例子是所有衍生自创作共用署名-非商业性使用CC-BY-NC)许可的条款。同样,开源软件也会对深度学习项目构成风险。在使用任何公开可用的数据或软件之前,一定要确保三次检查其许可协议。将具有防止商业使用条款的数据或代码用于商业项目,会使项目面临因许可侵权而被罚款或起诉的风险。

对抗攻击

当机器学习应用广泛时,它会暴露出自身容易受到针对性攻击的风险,这些攻击旨在恶意地破坏和操控模型的决策。这引出了一个可能影响已部署深度学习模型的攻击类型,称为对抗性攻击。对抗性攻击是一种攻击方式,通过特定的方式操控数据输入,从而影响机器学习模型的预测。最常见的对抗性攻击是通过对实际输入数据进行修改,生成对抗性数据示例。这些修改后的数据看起来仍然符合输入数据的合法性,但却能扭曲模型的预测。此类攻击的风险程度在不同的使用场景中有所不同,具体取决于用户与系统的交互程度,最终将输入数据传递给机器学习模型。对于深度学习模型来说,最广为人知的对抗性示例之一是经过优化的图像,它看起来像是随机的色彩噪声。当这种图像用于扰动另一张图像的像素值,通过覆盖其像素值时,就会生成一个对抗性示例。扰动后的原始图像从视觉上看起来与人眼几乎无差,但却能导致错误的误分类。下图展示了这种扰动的示例,摘自第十四章分析对抗性表现。该图展示了一个神经网络 ResNet50 的结果,ResNet50 经过训练可以分类人脸身份,当给定人脸图像时,它能够正确预测身份。然而,当该图像与另一个噪声图像数组结合在一起时,这个噪声图像数组是通过访问预测类别概率自动生成的,模型错误地预测了人脸图像的身份,即使合成后的图像与原始图像在视觉上完全相同:

图 1.13 – 潜在的基于图像的对抗性攻击示例

图 1.13 – 潜在的基于图像的对抗性攻击示例

理论上,对抗性攻击可以使用任何类型的数据进行,并不限于图像。随着机器学习应用场景的风险增大,攻击者更有可能投入资金组建研究团队,以产生新的对抗性示例。

一些对抗样本及其生成方法依赖于对深度学习模型本身的访问,以便制作定向对抗样本。这意味着,潜在的攻击者除非能够访问该模型,否则几乎不可能成功地混淆别人创建的模型。然而,许多企业直接使用公开可用的预训练模型,并应用迁移学习方法,以减少满足业务用例所需的工作量。任何公开可用的预训练模型也同样对攻击者开放,允许他们为个别模型构建定向对抗样本。这类预训练模型的例子包括所有公开可用的 ImageNet 预训练卷积神经网络模型及其权重。

那么,我们如何尝试减轻这种风险呢?

我们可以用来减轻对抗性攻击风险的方法之一,是通过使用公共研究中的已知方法集的示例来训练深度学习模型。通过使用已知的对抗样本进行训练,模型将学会在学习过程中忽略这些对抗信息,并在验证和推理阶段不受此类样本的影响。评估不同变体的对抗样本还帮助我们设定期望,了解模型何时会失败。

在这一节中,我们深入探讨了处理和使用数据进行机器学习时的安全问题。在第十四章分析对抗性表现中,我们将深入实践评估针对不同数据模态的对抗性攻击技术,并探讨如何在深度学习的背景下减轻这些攻击。接下来,我们将深入探讨模型开发过程中的另一个核心风险类别。

过拟合和欠拟合

在模型开发过程中,即训练、验证和测试机器学习模型的过程中,最基础的风险之一就是过拟合和欠拟合。

过拟合是指机器学习模型对提供的训练数据集样本和已学习的模式产生过度偏向,以至于它只能区分训练数据集中的样本,而无法区分验证集和测试集中的任何样本。

欠拟合则是指机器学习模型未能捕捉到提供的训练、验证和测试数据集中的任何模式。

学习可泛化的模式输出映射能力是构建有价值且可用的机器学习模型的关键。然而,达成一个良好拟合的模型并没有银弹,通常需要在数据准备、模型开发和模型交付洞察阶段之间反复迭代。

以下是一些防止过拟合并确保深度学习中泛化的建议:

  • 预测最终部署条件时,增加数据增强

  • 收集足够的数据,覆盖目标标签的每一种可能变异

  • 与领域专家合作,了解关键指标和模式

  • 使用交叉验证方法,确保模型在未见数据上进行公平评估

  • 尽可能使用更简单的神经网络架构

以下是一些防止欠拟合的建议:

  • 评估各种不同的模型

  • 与领域专家合作,了解关键指标和模式

  • 确保数据清洁,尽可能减少错误

  • 确保数据足够充足

  • 在构建模型时,从少量数据输入开始,逐步增加更复杂的数据,以确保模型能在数据上恰当地拟合

在本节中,我们讨论了构建模型时的问题。接下来,我们将讨论一种在模型训练完成后会影响模型的风险。

模型一致性风险

机器学习过程的一个主要特征是它是循环的。模型会不断重新训练,旨在寻找更好的设置。这些设置可能是不同的数据划分方法、不同的数据转换方法、不同的模型,或是相同的模型但具有不同的模型设置。通常,构建的模型需要相互公平且公正地进行比较。模型一致性是确保不同模型版本和性能指标之间能够进行公平比较的唯一特征。在获得模型之前的每个过程都要求保持一致性,以便当任何人尝试用相同的设置执行相同的过程时,应该能够获得和再现相同的模型。我们需要重申,即使在构建模型时某些过程需要随机性,它也需要是可随机确定的。这是必要的,以确保设置中的唯一差异是目标设置,而不是其他任何因素。

模型一致性不仅仅体现在模型的可重现性上——它还扩展到模型预测的一致性。当使用不同的批量大小设置进行预测时,预测结果应该是相同的,而且相同的数据输入应始终产生相同的预测结果。模型预测中的不一致性是一个重大的警示信号,表明模型所产生的任何结果都无法代表在部署时的实际表现,任何由模型得出的见解都可能是误导信息。

为了应对模型一致性风险,始终确保通过尽可能地种子化随机数生成器,使代码产生一致的结果。在模型开发阶段,构建模型后,始终手动或自动验证模型的一致性。

模型退化风险

当你构建了一个模型、验证了它,并展示了它对业务的影响后,你接着将其进入模型部署阶段并准备好投入使用。一个常见的错误是认为这就意味着深度学习项目的结束,你可以把手放开,任由模型自我运行。遗憾的是,大多数机器学习模型会退化,这取决于你在模型开发阶段实现的泛化程度,以及在实际环境中可用数据的情况。一个常见的场景是,当模型被部署时,最初模型的表现和部署过程中接收到的数据特征保持一致,并随着时间推移发生变化。时间有可能改变环境条件以及世界上的任何事物。机器老化,季节变化,人也会改变,预见到这些条件和变量的变化可以帮助你确保模型在业务中保持相关性和影响力。

模型退化的方式可以分为三类,分别是数据漂移概念漂移模型漂移。从概念上讲,漂移可以与一艘船缓慢漂离其理想位置相联系。在机器学习项目中,不是船漂离,而是数据或模型从其原始的感知行为或模式中漂离。我们简要地介绍一下这几种漂移类型。

数据漂移是一种退化现象,涉及到模型所需提供预测值的输入数据。当一个部署后的模型经历数据漂移时,意味着在部署过程中接收到的数据不属于机器学习模型在训练和验证过程中所用数据的固有分布。如果有方法验证在部署过程中提供的新数据上的模型,数据漂移可能会导致模型验证期间原始预期指标性能的变化。一个深度学习模型中数据漂移的例子可以是在户外预测人类行为的用例。在这个用例中,数据漂移表现为原始数据是在夏季收集的,数据中包括穿夏季服装的人,而由于冬季的到来,人们则穿上了冬季服装。

概念漂移是一种与输入数据如何与目标输出数据交互变化相关的退化形式。在规划阶段,领域专家和机器学习从业者合作定义可能影响目标输出数据的输入变量。这个定义好的输入和输出设置随后将用于构建机器学习模型。然而,有时并不是所有可能影响目标输出数据的上下文都会作为输入变量包含在内,这可能是由于数据的可获得性或缺乏领域知识的原因。这就引入了依赖于缺失上下文条件的情况,这些缺失的上下文与为机器学习收集的数据相关。当缺失的上下文条件与训练和验证数据中存在的基本值发生漂移时,就会发生概念漂移,导致原始概念变得无关或发生偏移。简而言之,这意味着相同的输入不再映射到训练数据中的相同输出。在深度学习的背景下,我们可以以一个基于文本数据的情感分类用例为例。假设一个评论或讲话根据司法管辖区和文本本身可以是负面、中立或正面情感。这意味着在某些司法管辖区,人们对什么被视为负面、中立或正面的阈值不同,并且对事物的评分方式也不同。仅使用文本数据训练模型将无法准确地跨司法管辖区进行泛化,因此每当模型在另一个司法管辖区部署时,它都会面临概念漂移。

最后,模型漂移是一种与操作指标和易于测量的指标相关的退化形式。可以归类为模型漂移的退化因素包括模型延迟、模型吞吐量和模型错误率。与其他两种漂移类型相比,模型漂移的指标通常更容易衡量和跟踪。

缓解这些风险的最佳工作流程之一是跟踪这三种类型漂移下的指标,并为新机器学习模型的构建提供清晰路径,这个过程我称之为漂移重置。现在我们已经简要概述了模型退化,在第十六章《深度学习模型的管理》和第十七章《在动态环境中有效管理漂移效应》中,我们将更深入地讨论这些风险,并实际探讨如何在深度学习的背景下缓解这一风险。

总结

在本章中,你学到了如何以一种可重复、一致的方式成功完成深度学习项目,虽然这是从中高层次的视角来看。我们在本章讨论的主题被结构化为深度学习生命周期早期阶段的更全面内容,这包括规划和数据准备阶段。

在接下来的章节中,我们将更全面地探讨生命周期的中后期阶段。这涉及数据准备阶段之后的所有内容,包括模型开发、模型洞察、模型部署以及最后的模型治理。

在下一章,我们将更深入地探索常见和广泛使用的深度学习架构。

进一步阅读

  • 石博文、徐维宁、阿卜杜勒拉赫曼·穆罕默德。强大的自监督视听语音识别,2022 年。芝加哥丰田技术学院,Meta AI:arxiv.org/pdf/2201.01763v2.pdf

  • 王鹏、杨安、门锐、林俊阳、白帅、李志康、马建新、周畅、周靖仁、杨红霞。通过简单的序列到序列学习框架统一架构、任务和模态。达摩院,阿里巴巴集团:arxiv.org/pdf/2202.03052v1.pdf

第二章:设计深度学习架构

在上一章中,我们回顾了整个深度学习生命周期,并理解了从头到尾使深度学习项目成功的含义。有了这些知识后,我们现在准备进一步深入探讨深度学习模型的技术细节。本章将深入探讨业界常用的深度学习架构,并理解每种架构设计背后的原因。对于中级和高级读者,这将是一个简要回顾,以确保术语的定义一致性。对于初学者,本章将以易于理解的方式呈现架构,帮助你快速掌握深度学习领域中有用的神经网络架构。

理解多种架构背后的方法论可以让你创新适用于特定使用案例的自定义架构,最重要的是,能够根据数据输入或问题类型选择合适的基础架构。

本章将重点介绍多层感知机MLP)网络架构。全面讲解 MLP 及一些与神经网络实现相关的关键概念,如梯度、激活函数和正则化方法,为后续章节中更复杂架构类型的探索奠定基础。具体来说,本章将涵盖以下内容:

  • 使用 MLP 探索神经网络的基础

  • 理解神经网络的梯度

  • 理解梯度下降

  • 从零开始实现 MLP

  • 使用深度学习框架实现 MLP

  • 设计 MLP

技术要求

本章包括一些使用Python编程语言的实践实现。为完成这些内容,你需要在计算机上安装以下库:

  • pandas

  • Matplotlib

  • Seaborn

  • Scikit-learn

  • NumPy

  • Keras

  • PyTorch

代码文件可以在 GitHub 上找到:github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook/tree/main/CHAPTER_2

使用 MLP 探索神经网络的基础

深度学习架构是通过至少使用三个感知机层(不包括输入层)来创建的。感知机是由神经元单元组成的单层网络。神经元单元包含一个偏置变量,并作为与其他神经元连接的节点。这些神经元将与不同层中的其他神经元进行交互,连接/节点之间应用权重。感知机也称为全连接层密集层,而 MLP 也被称为前馈神经网络全连接****神经网络

让我们回顾上一章的 MLP 图示,以便更好地理解。

图 2.1 – 简单的深度学习架构,也叫做 MLP

图 2.1 – 简单的深度学习架构,也叫做 MLP

该图显示了如何将三个数据列输入传递到输入层,然后逐步传播到隐藏层,最后通过输出层。尽管图中没有展示,但在隐藏层和输出层的输出上应用了额外的激活函数。隐藏层的激活函数为模型添加了非线性,使神经网络能够捕捉输入数据和输出数据之间的非线性关系。输出层使用的激活函数取决于问题类型,具体内容将在第八章《探索监督式深度学习》中更详细地讨论。

在深入讨论相关的隐藏激活方法之前,我们首先需要了解梯度消失问题。梯度消失问题是指在反向传播过程中,损失函数相对于模型参数的梯度变得非常小。这可能导致学习速度缓慢和收敛效果差,因为权重更新极少甚至没有更新。梯度消失问题在使用将输入值压缩到狭窄范围的激活函数时尤为突出。为了解决这个问题,Rectified Linear Unit (ReLU) 激活函数得到了广泛应用,因为它能够在一定程度上缓解梯度消失问题。ReLU 将负值映射为零,保持正值,如图 2.2 所示。

图 2.2 – ReLU、Leaky ReLU 和 PReLU 输入/输出图

图 2.2 – ReLU、Leaky ReLU 和 PReLU 输入/输出图

除了 ReLU,还有其他一些有用的隐藏层激活函数,它们有助于缓解梯度消失问题,同时提供各种优点。以下是其中一些:

  • Leaky ReLU:Leaky ReLU 是 ReLU 函数的一种变体,它允许负输入值有一个小的、非零的梯度。这有助于缓解“死亡 ReLU”问题,当神经元的输入值持续为负时,神经元会变得不活跃并停止学习。Leaky ReLU 为负输入值引入了一个小的斜率,确保梯度不会完全消失。

  • Parametric ReLU (PReLU):PReLU 是 ReLU 函数的另一种变体,其中负斜率在训练过程中进行学习,使得模型能够自适应其行为。这种灵活性可能会提高性能,但代价是增加了复杂性并存在过拟合的风险。

此外,随着我们在本书中深入探讨不同的主流架构,我们将探索更多的隐藏激活函数。这些激活函数各有优缺点,激活函数的选择取决于要解决的具体问题和所使用的架构。理解、实验和评估这些激活函数对于在神经网络的隐藏层中选择最适合的激活函数至关重要。此外,评估与模型构建相关的任何实验的推荐方法将在第八章中进行探讨,探索监督式深度学习

接下来,从一层传播到另一层的过程称为前向传递或前向传播,其公式一般可以定义如下:

a = g( ∑ n=0 神经元 wx + b)

在这里,a 表示神经网络层的输出(称为激活),g 表示非线性激活函数,w 表示神经元连接之间的权重,x 表示输入数据或激活值,b 表示神经元的偏置。不同类型的神经网络层以不同方式消耗和输出数据,但通常仍以此公式为基础。

理解神经网络梯度

MLP(多层感知机)中的机器学习目标是找到能够有效地将输入映射到期望输出的权重和偏置。权重和偏置通常是随机初始化的。在训练过程中,使用提供的数据集,它们会以批次的方式进行迭代更新,以最小化损失函数,该函数使用一种叫做反向传播(backpropagation)的方法计算梯度。批次是用于训练或评估的数据集的一个子集,允许神经网络以较小的组处理数据,而不是一次性处理整个数据集。损失函数也被称为误差函数或代价函数。

反向传播是一种技术,用来找出每个神经元的权重和偏置变化对总体损失的敏感度,方法是利用损失函数相对于权重和偏置的偏导数。来自微积分的偏导数是衡量函数相对于某一变量变化速率的工具,使用一种叫做微分的技术,在神经网络中得到有效应用。一种方便的方法叫做链式法则,它允许通过分别计算每个函数的偏导数,即神经网络中的前向传播,来得到神经网络的导数。明确来说,导数可以称为变化的敏感度、梯度或变化率。其基本思想是,当我们知道哪个模型参数对误差的影响最大时,可以根据其幅度和方向相应地更新其权重。让我们以一个简单的两层多层感知机(MLP)为例,每一层有一个神经元,如图 2.3所示,来理解这一过程。

图 2.3 – 两层多层感知机的示意图

图 2.3 – 两层多层感知机的示意图

为了更清晰,w表示神经元连接的权重,b表示神经元的偏置,L表示层级。不同问题类型需要不同的损失函数,但为了说明问题,我们假设这是一个回归问题的 MLP,在这里我们使用均方误差作为损失函数,从最终层激活值和数值目标值中计算损失组件。损失函数可以定义为以下形式:

L =  1 _ n  ∑ i=1 n (a2 − y) 2

这里,n 表示神经元的总数。为了获得损失函数相对于输出层权重w2的变化率δL _ δw2,让我们基于链式法则定义公式。考虑以下内容:

z2 = w2 ∙ a1 + b2

所以,如果 a2 = g(z2),其中 g 是 ReLU 函数,那么输出层权重 w2 的梯度将定义如下:

δL _ δw2  =  δL _ δa2  ∙  δa2 _ δz2  ∙  δz2 _ dw2

损失函数相对于 w2 的变化率可以通过将三个独立的变化组件相乘来计算:即损失函数相对于第二层输出的变化,激活输出相对于没有激活的包装 z2 的变化,以及包装 z2 相对于 w2 的变化。接下来我们定义这些组件。现在考虑以下内容:

e = a2 − y

基于链式法则,第一个变化组件将被定义为以下内容:

δL _ δa2  =  δL _ δe  ⋅  δe _ δa2

δL _ δe  = 2e

δe _ δa2  = 1

将这个简化的组件表示放入公式中,得到以下方程:

δL _ δa2  =  2 _ n (a2 − y)

对于第二个变化组件,它可以通过以下公式定义:

δa2 _ δz2  = g ′ (z2) = 1

在此情况下,输出层没有应用激活函数。

对于第三个也是最后一个变更组件,它可以用以下公式来定义:

δz2 _ δw2  = a1

最后,将三个组件的简化表示代入公式中,以获得输出层权重 w2 的梯度,结果为以下方程:

δL _ δw2 =  2 _ n(a2 − y) ∙ a1

现在你需要做的就是将实际值代入公式,以获得第二层的权重梯度。相同的公式结构可以类似地应用于隐藏层的权重 w1,如下所示:

a1 = g(z1)

z1 = w1 ∙ a0 + b1

δL _ δw1  =  δL _ δa1  ∙  δa1 _ δz1  ∙  δz1 _ δw1

在使用链式法则展开 δL _ δa1 后,公式可以变为以下形式:

δL _ δw1  =  δL _ δa2  ∙  δa2 _ δz2  ∙  δz2 _ δa1  ∙  δa1 _ δz1  ∙  δz1 _ δw1

额外的各个组件可以定义如下:

δz2 _ δa1  = w2

δa1 _ δz1  = g ′ (z1) = 0 如果 a2 < 0,1 如果 a2 > 0

δz1 _ δw1  = a0

这里的 a0 是输入数据。现在,我们定义隐藏层权重 w1 的梯度,并用表示法来代入实际值进行计算,如下所示:

δL _ δw1  =  2 _ n (a2 − y) ∙ w2 ∙ g ′ (z1) ∙ a0

相同的过程可以重复应用于偏置项,以获得其梯度。只需将 z 关于权重的偏导数替换为 z 关于偏置的偏导数,如下所示:

δz2 _ δb2  =  δz1 _ δb1  = 1

δL _ δb2  =  δL _ δa2  ∙  δa2 _ δz2  ∙  δz2 _ δb2

δL _ δb2  = 2(a2 − y)

δL _ δb1  =  δL _ δa2  ∙  δa2 _ δz2  ∙  δz2 _ δa1  ∙  δa1 _ δz1  ∙  δz1 _ δb1

现在,我们定义第一个偏置项的梯度,并用表示法来代入实际值进行计算,如下所示:

δL _ δb1  =  2 _ n  ∙ w2 ∙ (a2 − y) ∙ g ′ (z1)

之前定义的公式是特定于示例神经网络的,其中每层只有一个神经元。在实际应用中,这些层通常包含多个神经元。要计算包含多个神经元和多个数据样本的层的损失和导数,你只需对所有值进行平均。

一旦获得了梯度或导数,就可以使用不同的策略来更新权重。用于优化神经网络权重和偏置的算法称为优化器。如今有许多优化器选项,每种都有自己的优缺点。由于梯度用于优化权重和偏置,因此这个优化过程被称为梯度下降

理解梯度下降

思考深度学习模型的损失,一种好的方式是将其视为一个三维损失景观,景观中有许多不同的山丘和谷底,谷底代表着更优的结果,如图 2.4所示。

图 2.4 – 一个示例损失景观

图 2.4 – 一个示例损失景观

然而,实际上我们只能近似这些损失景观,因为神经网络的参数值可能存在无限多种组合。实践者通常用来监控每个训练和验证周期中损失表现的方式是简单地绘制一个二维折线图,其中 x 轴表示执行的周期数,y 轴表示损失表现。一个周期是神经网络训练过程中对整个数据集的单次迭代。图 2.4 中的损失景观是神经网络三维损失景观的近似。为了可视化 图 2.4 中的三维损失景观,我们可以使用两个随机初始化的参数和一个完全训练好的参数,这些参数来自神经网络中的相同神经元位置。损失可以通过对这三个参数进行加权求和来计算。完全训练好的参数的权重保持不变,而两个随机初始化的参数的权重则会被调整。这个过程使我们能够近似 图 2.4 中所示的三维损失景观。在这个图中,x 轴和 y 轴分别是来自同一个神经网络的两个随机初始化参数的权重,而 z 轴则是损失值。梯度下降的目标是尝试找到 全局 最深的谷底,而不是被困在 局部 谷底或 局部 最小值中。计算出来的梯度提供了建议的方向,用于逐步调整和更新权重与偏置。需要注意的是,梯度提供了最快增大损失函数的方向,因此在梯度下降时,参数会从梯度中被减去。让我们通过一个简单的梯度下降形式来了解如何更新权重和偏置:

w = w − α ∙  δL _ δw

b = b − α ∙  δL _ δb

在这里,α指的是学习率,它控制着你希望深度学习模型的训练进度。学习率是一个超参数,决定了神经网络在优化过程中学习和更新权重与偏置的速度。学习率越高,深度学习模型在损失函数中的步伐就越大。通过反复应用这个参数更新步骤,神经网络会慢慢地朝着下降的方向移动,从而使学习到的参数集能够有效地将输入映射到期望的目标值。所有数据样本的梯度会被计算并求平均,从而获得一个更新方向,用于更新权重和偏置。

数据集有时可能过大,导致基础梯度下降算法的学习过程变慢,因为在更新神经网络参数之前,需要从每个样本计算梯度。随机梯度下降法SGD)就是为了解决这个问题而创建的。其核心思想是通过批次学习数据集,并通过不同的数据批次划分来迭代学习整个数据集,而不是等待从整个数据集获取梯度再更新网络参数。这样,即使数据集非常大,学习过程也能保持高效,并且能够快速看到初步结果。

梯度下降算法有很多变种,每种算法都有不同的优点,适用于特定的情况。在这里,我们将列出一些在各种数据集上表现良好且相关的梯度下降算法:

  • 动量法Momentum):动量法是 SGD 的一种变体,它引入了一个“动量”项,帮助优化器更有效地在损失函数的空间中导航。这个动量项是梯度的移动平均值,帮助优化器克服局部最小值并加速收敛。动量项还为优化器增加了一些惯性,使其在具有一致梯度的方向上采取更大的步伐,从而加速收敛。

  • 均方根传播法RMSProp):RMSProp 是一种自适应学习率优化算法,它为每个参数单独调整学习率。通过将学习率除以平方梯度的指数衰减平均值,RMSProp 有助于防止在 SGD 收敛过程中观察到的震荡现象。这使得收敛过程更加稳定且更快地趋向最优解。

  • 自适应矩估计Adam):Adam 是另一种流行的优化算法,它结合了 Momentum 和 RMSProp 的优点。它为每个参数维持独立的自适应学习率,并且还包含一个动量项。这种组合使得 Adam 能够快速收敛并找到更精确的解,因此它成为许多深度学习任务中的常见选择。

尽管有许多梯度下降算法可供选择,但选择合适的算法取决于具体问题和数据集。一般来说,由于 Adam 算法的自适应特性及其结合了 Momentum 和 RMSProp 的优点,它通常被推荐作为一个好的起点。为了确定最适合您特定深度学习任务的算法,必须尝试不同的算法及其超参数,并验证它们的性能。接下来,我们将使用 Python 编写一个多层感知机(MLP)模型。

从零开始实现多层感知机(MLP)

如今,创建神经网络及其层并进行反向传播的过程已经被封装在深度学习框架中。微分过程已经被自动化,不再需要手动定义导数公式。移除深度学习库提供的抽象层将有助于巩固你对神经网络内部结构的理解。因此,让我们手动并显式地创建这个神经网络,并实现前向传播和反向传播的逻辑,而不是使用深度学习库:

  1. 我们将首先导入 numpy 以及来自 scikit-learn 库的方法,以加载示例数据集并执行数据分区:

    import numpy as np
    from sklearn import datasets
    from sklearn.model_selection import train_test_split
    
  2. 接下来,我们定义 ReLU,这是使 MLP 非线性的方法:

    def ReLU(x):
      return np.maximum(x, 0)
    
  3. 现在,让我们部分定义初始化 MLP 模型所需的类,该模型有一个隐藏层和一个输出层,能够进行前向传播。层由权重表示,其中 w1 是隐藏层的权重,w2 是输出层的权重。此外,b1 是输入层和隐藏层之间连接的偏置,b2 是隐藏层和输出层之间连接的偏置:

    class MLP(object):
      def __init__(
        self, input_layer_size, hidden_layer_size, output_layer_size, seed=1234
    ):
        rng = np.random.RandomState(seed)
        self.w1 = rng.normal(
          size=(input_layer_size, hidden_layer_size)
        )
        self.b1 = np.zeros(hidden_layer_size)
        self.w2 = rng.normal(
          size=(hidden_layer_size, output_layer_size)
        )
        self.b2 = np.zeros(output_layer_size)
        self.output_layer_size = output_layer_size
        self.hidden_layer_size = hidden_layer_size
    def forward_pass(self, x):
        z1 = np.dot(x, self.w1) + self.b1
        a1 = ReLU(z1)
        z2 = np.dot(a1, self.w2)  + self.b2
        a2 = z2
        return z1, a1, z2, a2
    
  4. 为了让 MLP 学习,我们将现在实现反向传播方法,生成偏置和权重的平均梯度:

    def ReLU_gradient(x):
          return np.where(x > 0, 1, 0)
    def backward_pass(self, a0, z1, a1, z2, a2, y):
        number_of_samples = len(a2)
        average_gradient_w2 = (
          np.dot(a1.T, (a2 - y)) *
          (2 / (number_of_samples * self.output_layer_size))
        )
        average_gradient_b2 = (
          np.mean((a2 - y), axis=0) * (2 / self.output_layer_size)
        )
        average_gradient_w1 = np.dot(
          a0.T, np.dot((a2 - y), self.w2.T) * ReLU_gradient(z1)
        ) * 2 / (number_of_samples * self.output_layer_size)
        average_gradient_b1 = np.mean(
          np.dot((a2 - y), self.w2.T) * ReLU_gradient(z1), axis=0
        ) *  2 / self.output_layer_size
        return (
          average_gradient_w2, average_gradient_b2, average_gradient_w1, average_gradient_b1
        )
    

    请注意,ReLU 函数的导数是 f′(x) = 1 如果 x > 0,f′(x) = 0 如果 x <= 0。

  5. 对于最后一个类方法,我们将实现梯度下降步骤,利用反向传播得到的平均梯度,这是更新偏置和权重的过程:

    def gradient_descent_step(
        self, learning_rate, average_gradient_w2, average_gradient_b2, average_gradient_w1, average_gradient_b1
      ):
        self.w2 = self.w2 - learning_rate * average_gradient_w2
        self.b2 = self.b2 - learning_rate * average_gradient_b2
        self.w1 = self.w1 - learning_rate * average_gradient_w1
        self.b1 = self.b1 - learning_rate * average_gradient_b1
    
  6. 现在我们已经手动创建了一个正确的 MLP 类,让我们设置数据集并尝试从中学习。MLP 的结构只允许使用表格结构的数据集,这些数据集既是单维的又是数值型的,因此我们将使用一个名为 diabetes 的数据集,它包含 10 个数值特征,即年龄、性别、体重指数、平均血压和 6 项血清测量数据,作为输入数据,同时有一个衡量糖尿病疾病进展的定量指标作为目标数据。该数据集已经方便地保存在 scikit-learn 库中,因此让我们首先加载输入的 DataFrame:

    diabetes_data = datasets.load_diabetes(as_frame=True)
    diabetes_df = diabetes_data['data']
    
  7. 现在,我们将把 DataFrame 转换为 NumPy 数组值,以便准备好供神经网络使用:

    X = diabetes_df.values
    
  8. 加载数据的最后一步是从糖尿病数据中加载目标数据,并确保它有一个额外的外部维度,因为 PyTorch 模型以这种方式输出其预测结果:

    target = np.expand_dims(diabetes_data['target'], 1)
    
  9. 接下来,让我们将数据集分为 80% 用于训练,20% 用于验证:

    X_train, X_val, y_train, y_val = train_test_split(
      X, target, test_size=0.20, random_state=42
    )
    
  10. 现在,数据已经准备好,包括训练和评估分区,让我们从定义的类初始化一个 MLP 模型,包含一个 20 神经元的隐藏层和一个 1 神经元的输出层:

    mlp_model = MLP(
      input_layer_size=len(diabetes_df.columns),
      hidden_layer_size=20,
      output_layer_size=target.shape[1]
    )
    
  11. 数据和模型准备好后,到了训练我们从头开始构建的模型的时候。由于数据集足够小,有 442 个样本,所以使用梯度下降法时没有运行时问题,因此我们将在这里使用完整的梯度下降进行 100 个周期。一个周期意味着对整个训练数据集进行一轮完整的训练:

    iterations = 100
    training_error_per_epoch = []
    validation_error_per_epoch = []
    for i in range(iterations):
      z1, a1, z2, a2 = mlp_model.forward_pass(X_train)
      (
        average_gradient_w2,
        average_gradient_b2,
        average_gradient_w1,
        average_gradient_b1
      ) = mlp_model.backward_pass(X_train, z1, a1, z2, a2, y_train)
      mlp_model.gradient_descent_step(
        learning_rate=0.1,
        average_gradient_w2=average_gradient_w2,
        average_gradient_b2=average_gradient_b2,
        average_gradient_w1=average_gradient_w1,
        average_gradient_b1=average_gradient_b1,
      )
      _, _, _, a2_val = mlp_model.forward_pass(X_val)
      training_error_per_epoch.append(mean_squared_error(y_train, a2)
      validation_error_per_epoch.append(
        mean_squared_error(y_val, a2_val)
      )
    
  12. 让我们使用matplotlib绘制训练集和验证集的均方误差:

    plt.figure(figsize=(10, 6))
    plt.plot(training_error_per_epoch)
    plt.plot(validation_error_per_epoch,  linestyle = 'dotted')
    plt.show()
    

    这将生成以下图表:

图 2.5 – 训练和验证集的均方误差与周期数的图

图 2.5 – 训练和验证集的均方误差与周期数的图

到此为止,你已经实现了一个 MLP,并且从头开始训练了它,而无需依赖深度学习框架!但是,我们的实现是否正确和合理?让我们在下一节验证这一点。

使用深度学习框架实现 MLP

深度学习框架旨在简化并加速深度学习模型的开发。它们提供了大量常用的神经网络层、优化器和通常用于构建神经网络模型的工具,并且提供了非常容易扩展的接口以实现新方法。反向传播本身被框架的用户抽象化,因为梯度会在后台自动计算,并在需要时使用。最重要的是,它们允许使用 GPU 来高效地进行模型训练和预测。

在本节中,我们将使用一个名为 PyTorch 的深度学习框架构建与上一节相同的 MLP 模型,并验证这两种实现是否产生相同的结果:

  1. 我们将从导入必要的库开始:

    Import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
  2. 接下来,让我们定义包含两个全连接层的 MLP 类,并定义一个前向传播方法,该方法带有可以设置输入层大小、隐藏层大小和输出层大小的参数:

    Class MLPPytorch(nn.Module):
      def __init__(
        self, input_layer_size, hidden_layer_size, output_layer_size
      ):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(input_layer_size, hidden_layer_size)
        self.fc2 = nn.Linear(hidden_layer_size, output_layer_size)
    class MLPPytorch(nn.Module):
      def __init__(
        self, input_layer_size, hidden_layer_size, output_layer_size
      ):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(input_layer_size, hidden_layer_size)
        self.fc2 = nn.Linear(hidden_layer_size, output_layer_size)
      def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x
    
  3. 你会注意到没有实现反向传播函数,这减少了定义神经网络模型所需的工作量。当你从Pytorch模块类继承时,反向传播功能将自动提供,与您定义的Pytorch层一起使用。最后,让我们使用隐藏层大小为 10 以及根据糖尿病数据集的输入和输出大小来初始化 MLP:

    Net = MLPPytorch(
      input_layer_size=len(diabetes_df.columns),
      hidden_layer_size=10,
      output_layer_size=y_train.shape[1],
    )
    
  4. 现在,让我们检查一下使用numpy变体的前向和反向传播功能。首先,让我们初始化Pytorch MLP,并从基于numpy的 MLP 模型复制权重:

    with torch.no_grad():
           net.fc1.weight.copy_(
        torch.from_numpy(mlp_model.w1.T)
      )
      net.fc1.bias.copy_(
        torch.from_numpy(mlp_model.b1)
      )
      net.fc2.weight.copy_(
        torch.from_numpy(mlp_model.w2.T)
      )
      net.fc2.bias.copy_(
        torch.from_numpy(mlp_model.b2)
      )
    
  5. 现在,让我们将数据集准备为适合 PyTorch 模型使用的 Tensor 对象:

    torch_input = torch.from_numpy(X_train)
    torch_target = torch.from_numpy(y_train)
    
  6. 为了获得相同的梯度,我们必须使用相同的 MSE 损失并应用反向传播:

    criterion = nn.MSELoss()
    output = net(torch_input.float())
    loss = criterion(output, torch_target.float())
    loss.backward()
    
  7. 现在,让我们验证这两种实现的梯度:

    np.testing.assert_almost_equal(output.detach().numpy(), a2, decimal=3)
    np.testing.assert_almost_equal(net.fc2.weight.grad.numpy(), average_gradient_w2.T, decimal=3)
    np.testing.assert_almost_equal(net.fc2.bias.grad.numpy(), average_gradient_b2, decimal=3)
    np.testing.assert_almost_equal(net.fc1.weight.grad.numpy(), average_gradient_w1.T, decimal=3)
    np.testing.assert_almost_equal(net.fc1.bias.grad.numpy(), average_gradient_b1, decimal=3)
    

这巩固了你对神经网络基础知识的理解,以及对多层感知器(MLP)架构的掌握,为你深入学习深度学习领域中的更高级概念做好准备。在我们进入下一节讨论更高级的神经网络之前,我们将先探讨正则化这一主题,最后再探讨如何设计一个具有实际应用案例的 MLP。

正则化

深度学习中的正则化已经发展到现在的阶段,指的是任何用于增加构建模型对外部数据的泛化能力的神经网络、数据或训练过程中的附加或修改。如今,所有高效能的神经网络都在架构中嵌入了某种形式的正则化方法。这些正则化方法中的一些会带来额外的有益副作用,比如加速训练过程或提升训练数据集上的表现。但最终,正则化的主要目标是提高泛化能力,换句话说,就是提升外部数据上的表现指标并减少错误。简要回顾一下,以下是一些常见的正则化方法:

  • Dropout 层:在训练过程中,按指定的概率随机删除所有神经节点的信息,通过将神经节点的输出替换为零,实质上使得信息无效。这减少了对任何单一节点/信息的过度依赖,并增加了泛化的概率。

  • L1/L2 正则化:这些方法向损失函数添加一个惩罚项,旨在减少模型为特征分配高权重的情况。L1 正则化,也称为 Lasso,使用权重的绝对值,而 L2 正则化,也称为 Ridge,使用权重的平方值。通过控制权重的大小,这些方法有助于防止过拟合并提高泛化能力。通常,这些方法应用于输入特征。

  • 批量归一化层:这种方法在训练和推理阶段对外部数据进行标准化,通过将数据缩放到均值为零、标准差为一来实现。这是通过去除计算得到的均值并将其除以计算得到的标准差来完成的。均值和标准差通过小批量(基于模型确定的训练批量大小)在训练过程中进行计算并迭代更新。在推理阶段,会应用在训练过程中计算得到的最终学习运行均值和标准差。这有助于提高训练时间、训练稳定性和泛化能力。请注意,每个元素都有自己的均值和标准差。研究表明,批量归一化可以平滑损失函数的曲面,使得更容易达到最优值。

  • 组归一化层:与每个元素在批次中的均值和标准差不同,组归一化按每个样本的组对数据进行标准化,每组有一个均值和一个标准差。可以配置组的数量。由于硬件限制,批量归一化在批次中样本数量较小时会降低性能。当每批的数据量非常小时,这一层优于批量归一化,因为均值和标准化的更新不依赖于批次。然而,在大批量数据下,批量归一化仍然优胜。

  • 权重标准化:这将相同的标准化过程应用于神经网络的权重。神经网络的权重在训练后可能会增长到非常大的数字,这将导致输出值过大。这个方法的想法是,如果我们使用批量归一化层,输出值反正会被标准化,那为什么不退一步,将同样的过程应用于权重本身,确保在变成输出值之前,权重的值已经以某种形式被标准化呢?一些简单的基准测试表明,当与组归一化层结合使用时,在低批量大小下,它能取得比批量归一化在高批量大小设置下更好的性能。

  • 随机深度:与其在训练阶段通过 dropout 概念上让神经网络使用更窄的层,随机深度则是在训练过程中减少网络的深度。这个正则化方法利用了 ResNet 中跳跃连接的概念,稍后会介绍,跳跃连接是指将前面层的输出额外传递到后面的层。在训练过程中,跳跃连接之间的层会被完全绕过,从而随机模拟一个更浅的网络。这个正则化器可以加速训练时间,并提高模型的泛化性能。

  • [0, 0, 0, 1][0.0001, 0.0001, 0.0001, 0.9999],分别表示。这个方法的想法是,我们不应该训练模型让它对结果过于自信,这会表明它对训练数据过拟合,并且无法推广到外部数据。该方法鼓励同一类别样本的最后一层输出更接近,而鼓励不同类别样本之间的输出保持相等的距离。此外,这也有助于缓解在标签不准确的样本中产生的过度自信。

  • 数据增强:当原始数据不足以充分代表任何标签的所有变异时,数据增强有助于在计算上增加数据的变异性,从而用于训练。这通过模型能够学习更完整的数据变异性,来有效地提高泛化能力。这可以应用于任何数据类型,并将在 第八章 中进行更详细的介绍,探索 监督学习

正则化是任何神经网络架构中的重要组成部分,在本章介绍的所有架构中都会看到它。当为特定问题选择正则化技术时,首先应考虑数据集的特性和你试图解决的问题。例如,如果你有一个小批量大小,群体归一化或权重标准化可能比批量归一化更适合。如果数据集的变化有限,数据增强可以用来提高泛化能力。选择这些技术时,可以从简单的正则化方法(如 dropout 或 L1/L2 正则化)开始,并评估其性能。然后,你可以单独或组合尝试其他技术,并比较它们对模型性能的影响。监控训练和验证指标非常重要,以确保所选的正则化方法不会导致过拟合或欠拟合。最终,正则化技术的选择取决于实验与验证的结合、领域知识以及对特定问题和数据集的理解。

接下来,让我们深入探讨 MLP(多层感知器)的设计。

设计 MLP

表格数据并不是神经网络最擅长的领域,而且往往在性能指标上,提升决策树的表现优于 MLP。然而,有时在某些数据集上,神经网络可能会超越提升树模型。在处理表格数据时,务必将 MLP 与其他非神经网络模型进行基准测试。

MLP 是最简单的神经网络形式,可以在两个维度上进行高层次的修改,这两个维度与所有神经网络类似:网络的宽度和网络的深度。从头开始构建标准 MLP 架构时,一个常见策略是从浅层和狭窄的宽度开始,获得一个小的基准后,再逐渐增加这两个维度。通常,对于表格数据上的 MLP,增加网络深度的性能收益在大约第四层时会趋于停滞。ReLU 是一种标准的激活层,已证明可以允许稳定的梯度和最佳的任务学习。然而,如果你有时间追求实际价值,考虑将激活层替换为更高级的激活层。目前,激活层研究领域非常细致,结果大多没有标准化,在不同的数据集上反馈混合,因此使用任何高级激活层并不能保证获得更好的性能。

MLP 的一种改进方法是使用一种叫做去噪自编码器的神经网络,生成可以作为 MLP 输入的去噪特征。这个进展将在稍后的第五章,《理解自编码器》中详细描述。训练方法与架构密切相关,旨在实现良好的性能。这些方法大多是通用的,不依赖于任何特定架构,因此将在第八章,《探索监督式深度学习》和第九章,《探索无监督深度学习》中分别介绍。

接下来,让我们总结一下本章的内容。

总结

MLP 是深度学习中基础性的架构组成部分,不仅限于处理表格数据,它也不再是一个被取代的旧架构。MLP 在当今许多先进的神经网络架构中非常常见,作为子组件用于提供更自动化的特征工程、减少大特征的维度,或者将特征形状调整为目标预测所需的形状。请留意接下来几章中将介绍的 MLP,或者更重要的是,全连接层!

深度学习框架提供的自动梯度计算简化了反向传播的实现,使我们能够专注于设计新的神经网络。确保这些网络中使用的数学函数是可微分的至关重要,尽管在采用成功的研究成果时通常会自动处理这一点。这就是开源研究与强大深度学习框架结合的魅力所在!

正则化是神经网络设计中的一个关键方面,虽然我们在本章中已详细讨论了它,但后续章节将展示它在不同架构中的应用,而无需进一步解释。

在下一章,我们将深入探讨另一种类型的神经网络,称为卷积神经网络,它特别适用于与图像相关的任务,并且有着广泛的应用。

第三章:理解卷积神经网络

MLP(多层感知器)结构设计用于处理一维数据,无法直接处理二维或更高维度的数据,除非进行预处理。一维数据也叫做表格数据,通常包括分类数据、数值数据,甚至可能是文本数据。二维数据或更高维度的数据通常是图像数据。当图像为灰度格式时,它是二维的;当图像有 RGB 层并且能较好地表现人类视觉时,它是三维的;而多于三维的图像则是高光谱图像。通常,为了让 MLP 能够处理图像,你需要将数据展平,并有效地以一维格式表示相同的数据。展平数据在某些情况下可能是可行的,但舍弃了定义图像的空间特征,丧失了捕捉该关系到目标的潜力。此外,展平数据在处理大图像时并不合适。图像数据的一个重要特征是,目标可以出现在图像的任何空间位置。简单的 MLP 对数据输入的位置高度依赖,无法适应图像中目标的不断变化的位置和方向。

这正是 卷积神经网络CNN)层的强项,通常是处理图像数据时机器学习专家的首选方法。迄今为止,顶尖的卷积架构始终在图像数据集上超过 MLP 架构的性能。在本章中,我们将涵盖以下内容,并重点讨论 CNN:

  • 理解卷积神经网络层

  • 理解池化层

  • 构建 CNN 架构

  • 设计一个用于实际应用的 CNN 架构

  • 探索 CNN 架构家族

技术要求

本章包括一些在 Python 编程语言中的实际实现。要完成这些任务,你需要一台安装了以下库的计算机:

  • pandas

  • matplotlib

  • seaborn

  • scikit-learn

  • numpy

  • keras

  • pytorch

本章的代码文件可在 GitHub 上找到:github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook/tree/main/CHAPTER_2

理解卷积神经网络层

现在,让我们专注于卷积层的基础知识,从 图 3.1 开始,它展示了卷积滤波器的操作过程。滤波器是一个小的权重矩阵,用于从输入数组中提取特征或模式。卷积滤波器是一种滑动图像的滤波器,执行卷积操作,通过计算点积来提取特征:

图 3.1 – 卷积滤波器在 Fashion MNIST 数据集中 T 恤图像上的操作

图 3.1 – 卷积滤波器在 Fashion MNIST 数据集中 T 恤图像上的操作

卷积层由多个大小相同的卷积滤波器构成。卷积滤波器是 CNN 中主要的模式检测器,每个滤波器都会学习识别图像中存在的多维模式。这些模式可以从低级模式,如线条和边缘,到中级模式,如圆形或方形,再到高级模式,如特定款式的 T 恤或鞋子,具体取决于卷积层在 CNN 中的深度。在 第十二章《解读神经网络》中,我们将探讨所学习的模式,并对这些模式进行定性和客观评估。

卷积滤波器可以是多维的,但对于普通图像来说,二维卷积滤波器更常用。核在 CNN 层中类似于滤波器。这些二维滤波器的权重数量与其大小相同,并在与输入图像数据相同大小的部分上进行点积操作,以得到一个单一的值;随后,这些值会加上一个偏置项。通过以滑动窗口的方式,按照定义的步幅,从上到下、从左到右系统地对同一操作进行处理,卷积滤波器将完成一次前向传播,得到一个维度较小的二维输出。在这里,步幅指的是滑动滤波器时每次移动的像素步数,最小步幅至少为 1 像素。此操作基于目标可能出现在图像中的任意空间位置,并在整个图像中使用相同的模式识别方法。

图 3.1 显示了一个二维卷积滤波器,大小为 5x5 像素,其中包含 25 个可学习的权重组件和一个偏置组件,以及一个 28x28 像素大小的 T 恤输入图像。滤波器的大小是可以配置为其他值的,通常范围在 1 到 7 之间,通常是正方形,但也可以设置为不规则的矩形形状。典型的滤波器大小值可能看起来太小,无法识别图像中预测 T 恤的高阶特征,但当多个滤波器按非周期性顺序一个接一个地应用时,操作结束时的滤波器会有更大部分图像的感受野。感受野是指卷积滤波器可以“看到”或响应的输入空间的区域。它决定了影响特定输出单元的输入的空间范围。为了捕捉低级特征,滤波器必须具有较小的感受野,而为了捕捉高级特征,滤波器必须具有较大的感受野。图 3.2 描述了三个滤波器按顺序应用时的这一概念:

图 3.2 – 第二个卷积滤波器的感受野大小为 3x3,尽管其卷积滤波器大小仅为 2x2

图 3.2 – 第二个卷积滤波器的感受野大小为 3x3,尽管其卷积滤波器大小仅为 2x2

现在,从左到右、从上到下的滑动窗口只是一个很好地可视化过程的方法。然而,实际上,这个过程可以一次性并行处理,以利用 GPU 的并行处理能力。图 3.3 显示了一个 4x4 像素维度的卷积滤波器,步长为 4,在同一 28x28 T 恤图像上应用的所有窗口位置。这将导致 7x7 的数据输出大小:

图 3.3 – 在 28x28 图像上,使用 4x4 卷积滤波器,步长为 4 像素,进行所有窗口位置的操作

图 3.3 – 在 28x28 图像上,使用 4x4 卷积滤波器,步长为 4 像素,进行所有窗口位置的操作

考虑一个 CNN 层,包含 16 个 5x5 像素的滤波器,步长为 1 像素。使用这种层配置对 T 恤图像进行前向传递时,输出的总像素数为 16x26x26(深度 x 宽度 x 高度)。在图像为 RGB 图像且有红、绿、蓝三个通道的情况下,由于卷积滤波器是二维的,相同的滤波器将以类似方式应用于三个通道,每个通道使用标准卷积层处理。对三个通道分别应用的滤波器产生的三维输出将被加总起来。卷积滤波器的数量将是输出数据通道的大小,并且将作为后续卷积层的输入通道大小。图 3.4展示了卷积滤波器的 3x3 输出在通道方向上的加和过程。请注意,偏置仅在每个滤波器跨越通道时添加一次,而不是按通道添加:

图 3.4 – 多通道 3x3 卷积滤波器输出的聚合

图 3.4 – 多通道 3x3 卷积滤波器输出的聚合

在前面的图中,Conv卷积层 的缩写,并将在本章的其余部分作为简化图示的约定使用。由于了解神经网络的大小对于确保拥有足够的计算资源以容纳和处理网络是有益的,接下来我们也将计算该卷积层将包含的参数数量。参数的数量可以通过以下方式计算:

输入通道数 x 过滤器数量 x ((过滤器宽度 x 过滤器高度) + 1)

将相应的数字代入公式,你将得到 416 个参数。如果这些权重以浮动点 32fp32)格式存储,则以字节为单位,意味着 416x32 位/8=1,664 字节。由于输出数据的大小为 16x26x26,在空间维度上的数据大小以非常缓慢的速度减少,且由于最终目标是将这些值减少到目标数据的大小,因此我们需要一些方法来减小数据的尺寸。这里引入了另一层,叫做池化层,来实现这一目标。

理解池化层

仅通过从图像的 CNN 层进行前向传播,二维输出数据的大小可能会减少,但仍然是一个相当大的尺寸。为了进一步减少数据的大小,使用了一种叫做池化层的层类型来战略性地聚合和整合值,同时保持有用的信息。可以将这个操作看作是一种图像调整大小的方法,同时尽可能保持更多的信息。该层没有学习的参数,主要是为了简化且有意义地减少输出数据。池化层通过应用类似滑动窗口滤波器的过程来工作,配置与卷积层相似,但它不是应用点积和加偏置,而是进行一种聚合。聚合函数可以是最大聚合、最小聚合或平均聚合。应用这些聚合的层分别称为最大池化、最小池化和平均池化。

考虑在第一个 CNN 层后应用一个滤波器大小为 4、步幅为 2 的平均池化层。经过一次前向传播,16x26x26 的输出将变成 16x12x12 的数据大小。这大大减少了数据的尺寸!

另一种类型的池化层应用全局聚合函数。这意味着数据的整个二维宽度和高度组件将被聚合成一个单一的值。这种池化层的变种通常被称为全局池化层。该层应用于将数据完全转化为一维结构,以便它可以与一维目标兼容。该层在keras库中直接可用,但在pytorch中则需要通过将池化滤波器的大小设置为与输入特征图的大小相同间接实现。

构建 CNN 架构

CNN 架构通常是通过一个个堆叠的概念逻辑块构建的。这些逻辑块的结构方式相同,具有相同类型的层和层连接,但在参数配置上可能有所不同,比如滤波器的大小、步幅、所使用的填充类型以及填充的数量。最简单的逻辑卷积块是卷积层、池化层和激活函数,按此顺序排列。填充是指在卷积操作后,为了保持输入图像的空间维度而在图像周围添加的额外像素。逻辑块是用来简洁而高效地描述和引用架构的一种方式。它们还使你能够以深度可扩展的方式构建 CNN 架构,而无需逐一创建和设置每个层的参数。深度与深度度相同,指的是神经网络层的数量。

可以根据目标是逐渐缩小特征图还是扩大特征图来设计参数。对于一维目标的情况,目标可能是将特征逐渐缩小为一维特征,以便将特征传递给全连接层。然后,这些全连接层可以进一步将(仍然很大的)维度映射到适合目标的维度。你可以在图 3.5中看到这样的架构的简单设计:

图 3.5 – 从零开始构建的简单 CNN 架构,同时遵循逻辑块类比

图 3.5 – 从零开始构建的简单 CNN 架构,同时遵循逻辑块类比。

pytorch 的代码中,这个例子看起来是这样的:

class ConvArch(nn.Module):
  def __init__(self):
       super(ConvArch, self).__init__()
       self.conv1 = nn.Conv2d(
            in_channels=1, out_channels=12, kernel_size=4,
            stride=2, padding=0
       )
       self.conv2 = nn.Conv2d(
            in_channels=12, out_channels=5, kernel_size=3,
            stride=1, padding=0
       )
       self.fc2 = nn.Linear(5, 3)
  def forward(self, x):
       x = F.relu(
            F.avg_pool2d(
                 self.conv1(x), kernel_size=3, stride=2
            )
       )
       x = F.relu(
            F.avg_pool2d(
                 self.conv2(x), kernel_size=3, stride=1
            )
       )
       x = F.avg_pool2d(x, kernel_size=x.size()[2:])
       x = self.fc2(x.reshape((x.size()[:2])))
       return x

同样,CNN 架构的反向传播将由深度学习库自动处理。

我们构建的架构是基于基本的分类问题类型,这也证明了在网络末尾需要一个全连接网络的必要性;这通常被称为“头”。使用的卷积层逻辑块集被称为网络的“主干”。网络的头部可以根据问题类型替换为其他结构,但主干可以完全适配到大多数架构中,适用于任何问题类型,如目标检测、图像生成、图像描述或通过表征学习的图像识别。关于这些问题类型的讨论会在第八章探索有监督深度学习中进一步展开。

现在我们已经成功手动创建了一个简单的 CNN,我们的理论已经与卷积网络的核心算法相契合并同步,接下来我们将能够轻松设计更先进的 CNN 主干架构。但在此之前,这就引出了一个问题:我们如何为我们的使用场景设计 CNN?

为实际使用设计 CNN 架构

对于实际的使用场景,CNN 不应像 MLP 那样设计。实际使用意味着目标不是为未探索的问题类型研究一个新的创新架构。今天,基于 CNN 已经取得了许多进展。这些进展通常有两种形式:

  • 它设定了一个全新的基准,彻底重新设计了 CNN 架构的构建方式。

  • 它是建立在现有基准 CNN 架构的基础上,同时补充和提升了基准架构的性能。

与多层感知机(MLP)的理想设计方法相比,卷积神经网络(CNN)的关键区别在于,应该使用已发布的 CNN 架构结构,而不是从零开始设计架构。CNN 架构的结构定义了层的类型以及不同类型的层如何连接;它们通常通过逻辑块实现。此外,特定结构的独特性定义了 CNN 架构的家族。新的 CNN 研究进展通常会以不同的尺寸配置出现,以便根据计算资源限制、运行时要求或数据集和问题复杂性选择合适的架构尺寸。与 MLP 设计类似,如果资源和运行时不是限制条件,在选择了 CNN 架构结构后,应该根据问题的复杂性和数据集的大小,从一个合理小的 CNN 开始。你可以逐渐测试更大的 CNN,看看当增加大小时,性能是否有所提升,反之如果性能下降。

不同的 CNN 架构结构或架构家族通常用于捕捉网络固有的不同架构问题。有些架构家族的设计方式是利用了更好的硬件资源,否则就无法执行这些架构。为了获得良好的性能,一个好的做法是在初始阶段多样化你使用的架构结构类型。选择具有相似浮点运算每秒(FLOP)的架构结构尺寸变体,并运行实验以获得性能得分,理想情况下,选择较小的尺寸以最大化探索效率。与模型的参数数量相比,考虑每秒浮点运算作为模型复杂度的指标更为相关;因为参数数量没有考虑模型的实际运行时,而模型的运行时可能会受益于并行化。一旦获得了这些数字,就选择顶尖的模型家族,并尝试更大的尺寸变体进行基准测试,以找到最适合你使用案例的模型变体。

大多数 CNN 的研究改进都基于一个简单的基线架构。这意味着对同一个基线架构所做的所有其他改进并没有一起进行基准测试。将这些改进一起测试往往是互补的,但有时也可能对模型的度量性能产生不利影响。迭代地基准测试不同的配置,可能是获得令人满意的性能提升的最系统、最扎实的方法。

研究人员如何评估他们的改进呢?为了设计出适用于实际场景的 CNN 架构,了解如何评估架构将帮助你逐步朝着可接受的度量性能目标前进。在第十章,《探索模型评估方法》中,我们将更详细地讨论评估策略。用于评估 CNN 改进的主要方法之一是通过在名为ImageNet的公开图像数据集上的 top-1 预测准确率,该数据集包含数百万张图像,并有多个类别。ImageNet被认为是一个高度复杂的案例,每个类别都有无限种可能的变体,从室内到室外,从真实数据到合成数据都有涵盖。

那么,我们如何判断改进是否具有价值呢?改进的目标通常是提高基于ImageNet数据集的 top-1 准确率,提升模型前向传递的效率,或是专门优化网络的训练时间。

然而,仅仅按照ImageNet上的 top-1 准确率性能改进对架构进行排名,这种评估方式是有偏的,因为在大多数情况下,模型的绝对排名在应用相同架构到不同的图像数据集时会有所不同。将其作为选择现成架构的起点是明智的,但一定要确保评估一些ImageNet上表现最佳的架构,以找出最适合的方案。此外,虽然ImageNet是通过手动努力构建的,包括查询搜索引擎并将候选图像通过 Amazon Mechanical Turk 进行验证,但它仍然包含一些标签噪声,这可能会混淆度量性能背后的含义。

至于提高模型前向传递效率的改进方向,通常是通过减少架构的参数数量、将计算密集型的逻辑组件分解成多个组件来减少操作量,或者用具有更高并行性潜力的层来替换原有层。在这一方向上进行的改进,通常会保持或提高在ImageNet验证数据集上的度量性能得分。这个方向的改进是针对一系列设计用于低资源边缘设备上运行的卷积神经网络(CNN)架构的主要关注点,我们稍后会深入探讨。

探索不同 CNN 架构家族以获得更好的指标性能的一个高效方法是,选择那些有公开实现的模型家族,且这些实现包括了在ImageNet上训练的预训练权重。通过使用预训练权重初始化你的架构,可以带来一系列好处,包括更快的训练、更少的过拟合,以及即使使用的数据集与预训练权重的训练数据集属于不同的问题子集,依然能提高泛化能力。这个过程叫做迁移学习,我们将在第八章《探索监督式深度学习》和第九章《探索无监督深度学习》中详细学习。

探索 CNN 架构家族

现在,我们不再回顾 CNN 多年来的发展历史,而是来看一下经过精心挑选的不同模型架构家族。这些架构家族的选择是基于它们之间有足够的差异和多样性。需要注意的一点是,神经网络正在以惊人的速度进步。考虑到这一点,本文将介绍的架构家族是确保今天依然相关的。此外,本文将只呈现架构家族中最重要的信息,将研究论文中的大量内容简化成简洁而充分的细节。

在深入探讨这个话题之前,另一个需要注意的事情是,数据集上的指标性能通常是不同架构之间的主要比较方法,因此请注意,模型的指标性能是通过训练方法和架构的集体贡献来实现的。训练方法包括与模型架构无直接关系的细节,如所使用的损失函数、数据增强策略和数据分辨率。这些主题将在第八章《探索监督式深度学习》中讲解。我们将在这里介绍的架构家族有ResNetDenseNetMobileNetEfficientNetShuffleNetMicroNet

理解 ResNet 模型家族

ResNet 架构,始于 2015 年,是基于深度网络训练困难的前提提出的,并旨在解决这一问题。梯度消失是神经网络面临的一个广泛已知的问题,即网络越深,来自数据的信息越少。然而,普通的深度 CNN 架构并未出现梯度消失的问题,这通过验证梯度信息得到了证明。梯度消失的主要原因是我们使用了过多的激活函数,将数据压缩到非常小的值范围内。例如,sigmoid 函数就是一个典型,它将数据映射从 0 到 1。所以,如果不确定,建议在架构中使用 ReLU!

ResNet 的 Res 部分是以残差(residuals)一词命名的。其理念是,从残差中学习比从未经修改的特征图中学习要容易得多。残差是通过添加来自早期层到后期层的跳跃连接(skip connections)来实现的。通俗来说,这意味着将早期部分的特征图(而非拼接)添加到网络后期部分的特征图中。这种加法的结果创建了将由后续卷积层学习的残差,后续卷积层再次应用更多跳跃连接并创建更多残差。残差可以轻松地应用于任何具有不同配置的 CNN 架构,并且可以被视为对较旧基线架构的改进。然而,作者还提出了多种利用不同数量卷积层的跳跃连接的架构变体,包括 ResNet-18、ResNet-34、ResNet-50、ResNet-101 和 ResNet-152。ResNet 架构家族为残差网络的简单使用提供了一个模板,并最终使其成为研究新进展的最流行基线。实际的架构设计在这里没有正式展示,因为记住这些设计对于掌握 CNN 知识没有任何影响。相反,图 3.6 显示了单个逻辑块的残差计算:

图 3.6 – ResNet 模型家族中实际残差连接方法的示例

图 3.6 – ResNet 模型家族中实际残差连接方法的示例

为总结整个基线 ResNet 尺寸变体,以下表格显示了所有不同尺寸变体的配置摘要:

图 3.7 – 基础 ResNet 不同尺寸变体

图 3.7 – 基础 ResNet 不同尺寸变体

表格中标注的括号表示定义为逻辑块的串行卷积层的集合。ResNet 还额外做了一个改进,将多个基础逻辑块归为一个更高层次的类别,称为“层名称”(layer name)。不同尺寸变体中,层名称的分组数目是相同的。逻辑块和更高层次的分组方法仅仅是为了让你能够简单有效地描述和引用复杂的架构。逻辑块中的前两个数字,通过乘法连接,定义了卷积滤波器的大小;紧随其后的数字和逗号定义了滤波器的数量。

研究发现,跳跃连接可以使任何神经网络的损失景观更加平滑,从而使学习过程变得更加容易和稳定。这使得神经网络更容易收敛到最优解。图 3*.8* 显示了没有跳跃连接的神经网络的损失景观,左侧地形起伏不平,充满了许多山丘和山谷;而右侧则是通过使用 ResNet 的变种添加跳跃连接后,地形变得平滑,且明显形成一个单一的山谷:

图 3.8 – 左侧为没有跳跃连接的损失景观,右侧为带有跳跃连接的损失景观

图 3.8 – 左侧为没有跳跃连接的损失景观,右侧为带有跳跃连接的损失景观

从表格中可以得出的一个显著信息是,从 ResNet-50 开始,该架构采用了 1x1 卷积滤波器。由于该滤波器仅在通道维度上操作一个单维滤波器权重,因此该操作相当于在通道维度上以窗口方式应用全连接层。由于全连接层本身就是一个网络,再加上卷积网络,这种操作通常被称为 网络中的网络。接下来,我们将探讨以 ResNet 架构为基础的不同改进。

改进 ResNets

如前所述,ResNet 被认为是一个模型家族,包含了许多不同的变种,不仅在规模上有所不同,还具有不同的架构改进。任何基于 ResNet 的 CNN 架构都属于这个模型家族。这里,我们提到了一些值得注意的架构进展,并根据 ResNet 提供了它们的主要改进描述;它们按年份排序:

  • ImageNet 与 ResNet 各变种相比,即使在参数数量相同的情况下,仍有明显差异,通常也有优势。

  • 挤压与激励网络 (SE-ResNet)(2017 年):由于二维卷积操作没有考虑通道之间的关系,仅仅考虑特征图的局部和空间(宽度和高度)信息,因此提出了一种利用通道关系的方法,该方法包括挤压模块和激励模块。这是一种可以重复应用于现有 CNN 架构多个部分的方法。图 3*.9* 显示了该方法的结构,其中在宽度和高度维度上应用了全局平均池化,随后使用两个全连接网络进行特征缩放和恢复至相同的尺寸,以便将通道信息进行组合:

图 3.9 – 挤压与激励结构

图 3.9 – 挤压与激励结构

该结构的缩放部分是值的相乘。当与 ResNet 结合使用时,架构被称为 SE-ResNet。

  • ResNet-D(2019 年):这一进展对架构参数进行了简单的调整,以提高度量性能,同时保持参数数量,尽管在 FLOP 规范略有增加。由于 ResNet 的某些路径使用步长为 2 的 1x1 卷积,舍弃了 3/4 的信息,而调整步幅大小以确保不会明确删除任何信息是其中的一种调整。他们还通过将 7x7 卷积变为三个串行的 3x3 卷积来减少计算负载。

  • ResNet-RS(2021 年):这一进展结合了 ResNet-D 和压缩激励网络,并使用依赖于网络大小的图像尺寸。然而,它的增长速度比 EfficientNets(稍后会介绍)慢。

理解 DenseNet 架构系列

DenseNet 是一种架构系列,最早于 2018 年初提出。这种架构基于跳跃连接的思想,类似于 ResNet 架构系列,但更加强大,意味着它使用大量的跳跃连接。这些架构系列中的跳跃连接不同之处在于它们使用串联而不是通过求和的残差连接。求和允许较早的信息直接编码到未来层的输出中,无需修改神经元的数量,尽管稍微降低了信息的优势,需要未来层学习解码这些信息。串联增加了架构的大小,因为你需要创建额外的神经元来容纳额外的信息,从而使模型能够处理原始数据。两者都提供了使用跳跃连接的类似优势。逻辑块称为稠密块,其中块中每个后续层通过特征图串联可以访问块中它之前所有层的所有输出。在这些块中,使用零填充以确保每层输出的空间尺寸保持不变,以便可以串联特征图。这种设置促进了同一块中层之间的大量特征重用,并允许模型参数数量保持不变,同时增加了模型的学习能力。对于所有块中的每个后续层,每个块中的过滤器数量均固定为称为增长率的常数值,因为需要一种结构化的方式来添加层,以避免指数增加通道数。以 32 个过滤器为常量,块中第二层的输入将是具有 32 个通道的特征图,块中第三层的输入将是 64 个并串联,依此类推。

为了创建一个完整的网络架构,多个密集块依次堆叠,中间插入单独的卷积层和池化层,以逐渐减少特征图的空间维度。图 3*.10* 显示了 DenseNet 模型家族下四种不同 DenseNet 模型架构的网络结构:

图 3.10 – DenseNet 模型家族,其中“conv”对应于批量归一化、ReLU 和卷积层的顺序层

图 3.10 – DenseNet 模型家族,其中“conv”对应于批量归一化、ReLU 和卷积层的顺序层

这使得 DenseNet 能够在 top-1 ImageNet 精度方面超越其前身网络架构。

理解 EfficientNet 架构家族

EfficientNet 于 2020 年推出,通过使用自动化 神经架构搜索 (NAS) 方法,创建了一个高效的小基础架构,并利用易于使用的复合缩放方法来缩放架构的深度、宽度和图像分辨率。神经架构搜索是以平衡 FLOPS 和精度的方式进行的。使用的 NAS 方法来源于另一项研究 MnasNet,将在第七章中详细介绍,深度神经架构搜索

复合缩放方法简单且可以扩展到任何其他网络,尽管 ResNet-RS 表明,缩放分辨率较慢提供了更多的价值。深度、宽度和分辨率的缩放方法定义在以下方程式中,其中结果将与原始基础架构的参数相乘,以放大架构:

depth = α φ , width = β φ, resolution = γ φ

这里,φ 是可以根据需求调整为不同值的系数,其他变量是常数,应设置为优化某些内容,如我们改变系数时 FLOPS 增加率的常数。对于 EfficientNet,这些常数被限制为满足以下条件:

α ∙ β 2 ∙ γ 2 ≈ 2

这将限制系数的增加,使得 FLOPS 约增加 2 φ。EfficientNet 设置为 α = 1.2,β = 1.1,γ = 1.15。这一复合缩放策略使得可以创建七个 EfficientNet 模型,命名为 B0 到 B7。图 3*.11* 显示了 EfficientNet-B0 的结构:

图 3.11 – EfficientNet-B0 架构结构

图 3.11 – EfficientNet-B0 架构结构

请注意,MBConv 也被称为反向残差块,将在稍后的 理解 MobileNetV2 部分中对其进行详细介绍。

EfficientNetV2,于 2021 年发布,发现大图像分辨率会减慢训练时间,其中深度卷积在早期层较慢,且同时扩展深度、宽度和分辨率并非最优。EfficientNetV2 还使用了 NAS(神经架构搜索)来找到基础架构,但对 MBConv 块进行了修改,增加了更多的参数和操作,原因是它在某些情况下可以更快,具体取决于输入和输出数据的形状、它们在整个架构中的位置以及数据如何传输到计算处理器。稍后我们正式介绍 MBConv 时会详细解释这一点。EfficientNetV2 还使用了原始的复合缩放方法,但通过将最大图像分辨率设置为 480 并在架构的最后几个阶段添加额外层来提高网络容量。还增加了一种新的训练方法来减少训练时间;我们将在下一章详细讨论这些内容。这些改进使得四种不同的 EfficientNetV2 模型诞生,分别为EfficientNetV2-SEfficientNetV2-MEfficientNetV2-LEfficientNetV2-XL。与原始的 EfficientNet 在相似 FLOP 值下,这些模型的 top-1 准确率超越了原有的 EfficientNet:

图 3.12 – EfficientNetV2-S 架构结构

图 3.12 – EfficientNetV2-S 架构结构

EfficientNetV2-S 作为基础架构,类似于 EfficientNetB0 作为基础架构,架构结构如图 3.12所示。

了解适用于小型边缘设备的小型和快速 CNN 架构系列

一类明确的架构组在 CNN 架构的世界中占有一席之地,这些架构并非为了可扩展性或打破ImageNet基准而设计,而是为了小型设备而建造。小型设备通常被称为边缘设备,因为它们足够小巧和紧凑,可以移动,或可以物理部署并具备数据处理能力,数据就是在其产生的地方进行处理的。我们的智能手机就是能够生成图像的移动设备的例子。不是移动设备的边缘设备包括闭路电视视频摄像头和门铃摄像头。将模型部署到边缘的关键好处如下所示:

  • 减少通信延迟:与简单的数值或类别数据相比,图像和实时视频流的数据量较大。通过在边缘进行计算,减少了需要传输到集中式服务器的数据量,从而减少了数据传输所需的时间。有时,当计算在边缘进行时,可以完全消除传输需求,从而显著简化系统。

  • 减少带宽需求:当图像在产生的地方进行处理时,只需返回简单的数据格式,如数值型或类别型数据,从而避免了对高带宽设备的需求。

  • 增加冗余:集中式服务器意味着单点故障。将处理分配到各个边缘设备可以确保任何一个设备的故障不会影响整个系统。

针对边缘设备的小型 CNN 架构通常在没有单一显著基准的情况下设计整个模型结构,因此没有一个合适的模型家族来对这些架构进行分类。为了方便引用这些专为此目的设计的架构,建议将以下架构视为面向边缘的架构。还有其他可以应用于整个架构的技术,用以进一步优化模型的效率,我们将在第十五章《在生产中部署深度学习模型》中进行探讨,但目前我们先来看看其中的两个架构,即SqueezeNetMobileNet

理解 SqueezeNet

SqueezeNet 于 2016 年开发,旨在构建小型且快速的 CNN,并具备前一节所描述的优势,同时强调部署到内存有限的硬件上。可以采用的三种策略如下:

  • 对于一个具有 3x3 滤波器的卷积层,部分使用 1x1 滤波器来替代。这意味着 1x1 滤波器和 3x3 滤波器在理论上可以共存于一个单独的层,并应用于相同的输入。然而,具体实现会有所不同,因为 1x1 滤波器和 3x3 滤波器会在同一输入上应用并行分支路径。这被称为扩展层(expand layer)。

  • 通过使用少量 1x1 滤波器来减少传递到并行 1x1 和 3x3 滤波器的数据输入通道数量。这被称为 squeeze 层。

  • 在架构的后期阶段对特征图进行下采样,以便大多数卷积层可以访问基于发现的较大特征图,从而提高指标性能。通过在早期阶段较少使用步幅为 2 像素的池化层,而在后期阶段及较大间隔时更多使用,从而实现这一点。在每个卷积层后并不总是使用池化层。

创建了一个逻辑块叫做 Fire,它提供了便于创建多个模块以构建架构的功能。该模块使得在 squeeze 层中配置 1x1 大小滤波器的数量、在扩展层中的 1x1 滤波器和 3x3 滤波器成为可能。这在图 3.13中有所展示。请注意,针对 3x3 卷积层的输出应用了填充操作,以确保其可以与扩展层中 1x1 卷积层的输出进行连接:

图 3.13 – 火焰模块/逻辑块

图 3.13 – 火焰模块/逻辑块

使用了八个Fire模块构建了一个叫做 SqueezeNet 的架构,它保持了从历史架构AlexNet(本书中未介绍,因为如今已不再实际使用)中传承下来的ImageNet性能,同时体积缩小了 50 倍。

理解 MobileNet

第一个 MobileNet 版本,称为 MobileNetV1,于 2017 年推出,专为移动和嵌入式设备设计,重点优化延迟问题,并通过这一过程使得网络尺寸较小,而不是反过来。

在整个架构中,除了第一层之外,深度可分离卷积层被广泛使用,这一版本的 MobileNet 包含 28 层。该层通过将标准卷积层分解为两层——深度卷积层和点卷积层——来构建。这种两层的设计理念是,使用标准卷积滤波器计算代价较高。深度卷积层为每个输入通道使用一个独特的滤波器,每个输入通道只有一个滤波器。这种每个通道一个滤波器的设计,使得ImageNet中只有 1%的独特案例。该分解也使得参数数量减少了约 5 倍。这一过程本质上是一种因式分解。深度卷积逻辑模块如下图所示:

图 3.14 – 深度可分离卷积层作为逻辑模块

图 3.14 – 深度可分离卷积层作为逻辑模块

MobileNet 还提供了两个可配置的参数,用以减少模型的大小和计算需求,同时在一定程度上牺牲性能。第一个是宽度倍增器,用于配置整个架构中的输入和输出通道。第二个是介于 0 和 1 之间的分辨率倍增器,适用于原始的 224x224 图像尺寸,当图像输入网络时可减小输入和输出特征图的尺寸。如果需要更快的运行时,这些参数还可以适配其他架构。图 3.15显示了 MobileNetV1 架构的结构和层配置:

图 3.15 – MobileNetV1 架构

图 3.15 – MobileNetV1 架构

MobileNet 被认为是一个独特的模型家族,基于第一个版本进行了两项改进。它们分别是 MobileNetv2 和 MobileNetv3-small,二者都在 2019 年推出。

理解 MobileNetV2

在我们深入了解 MobileNetV2 之前,先定义一下瓶颈层是什么,这是该进展中使用的核心思想。瓶颈层通常是一个输出特征图较少的层,且相对于前后层有较少的输出。MobileNetV2 建立在瓶颈层是信息所在的思想上;非线性激活会在瓶颈层破坏过多的信息,因此应用了线性层,最终通过捷径连接对瓶颈层进行残差运算。MobileNetV2 基于深度可分离卷积构建,添加了没有 ReLU 的线性瓶颈层,并在瓶颈层上应用残差。这一构建模块如下图所示。它被称为瓶颈反向残差块

图 3.16 – MobileNetV2 的瓶颈反向残差块,也称为 MBConv

图 3.16 – MobileNetV2 的瓶颈反向残差块,也称为 MBConv

至于整个网络架构,第一版 MobileNet 中的所有深度可分离层都被新模块替换,唯一没有替换的是第一个具有 3x3 卷积核和 32 个滤波器的卷积层,如图 3.16所示。一个小的额外细节是,它们使用了ReLU6激活函数,这在低精度计算中表现良好。MobileNetV2 架构使用所示的逻辑块来创建多个具有不同设置的重复层块。这一架构使得在相同 FLOPs 下,ImageNet 上的性能曲线比 MobileNetV2 提高了约 5%到 10%。请记住,EfficientNetV2 使用了这个块,并且还有一个版本的块将线性瓶颈层和过滤层重新融合在一起。使用两个层而不是一个层的目的是减少所需的操作次数,但对于边缘设备来说,由于内存访问的瓶颈,实际的延迟可能会有所不同。有时,使用融合版本可能会在具有更多参数的情况下提高运行时速度,从而能从中学习更多信息。

了解 MobileNetV3-small

对于MobileNetV3-small,对 MobileNetV2 进行了几处修改:

  • 它使用了一种更先进的非线性函数,称为ImageNet

  • 在初始层和最后几层,计算量被进一步减少。

  • 对于第一层,如图 3.15所示,32 个滤波器被减少到 16 个,并使用了硬切换非线性函数,实现了 2 毫秒的运行时间和 1000 万 FLOPS 的节省。

  • 对于最后几层,最后一个 1x1 的瓶颈卷积层被移至最终平均池化层之后,同时前面的瓶颈(1x1)层和过滤(3x3)层也被移除。

  • 它使用了一种平台感知的网络架构搜索的修改版本,称为 Mnasnet,以及一种称为 NetAdapt 的后搜索层减少方法来减少延迟,自动找到基于 MobileNetV1、MobileNetV2 以及 squeeze 和 excitation 网络的优化架构,同时考虑延迟和精度表现。NetAdapt 和 MnasNet 会在 第七章深度神经 架构搜索 中介绍。

MobileNetV3-small 最终在相同的参数和 FLOPS 下,取得了比 MobileNetV2 更高的 top-1 ImageNet 精度。

了解 ShuffleNet 架构系列

ShuffleNet 有两个版本,ShuffleNetV1 和 ShuffleNetV2,我们将分别讨论这两个版本。

ShuffleNetV1,发布于 2017 年,重用了已知的卷积变种——组卷积,其中每个卷积滤波器仅负责输入数据通道的一个子集。MobileNet 通过为每个通道使用一个滤波器,采用了这一特殊的变种。组卷积通过仅在输入通道特征的小子集上操作,节省了计算成本。然而,当多个组卷积层依次堆叠时,通道间的信息不会相互作用——最终导致精度下降。ShuffleNetV1 使用通道洗牌操作,在多个组卷积层堆叠之间手动传递信息,而不牺牲 FLOPs。这使得网络既高效又小巧。

ShuffleNetV2,发布于 2018 年,基于 ShuffleNetV1,并专注于在实际应用中架构的运行时效率,同时考虑诸如内存访问成本、数据输入输出I/O)以及网络并行度等因素。以下四种设计策略被用于打造这一新架构:

  • 从输入到输出保持相等的通道宽度,以最小化内存访问成本。

  • 降低组卷积以最小化内存访问成本。

  • 减少网络碎片化以增加并行性。例如,单个构建模块中的卷积和池化操作的数量。

  • 减少诸如 ReLU 等逐元素操作,因为它们有较高的内存访问成本:

图 3.17 – 左侧为 ShuffleNetV1 的两个构建模块,右侧为 ShuffleNetV2 的两个构建模块

图 3.17 – 左侧为 ShuffleNetV1 的两个构建模块,右侧为 ShuffleNetV2 的两个构建模块

图 3.17 中,左侧的前两个结构显示了 ShuffleNetV1 的两个构建模块,而右侧的最后两个结构则显示了 ShuffleNetV2 的两个构建模块。

了解 MicroNet,这是当前边缘计算领域的最先进架构

创建于 2021 年的 MicroNet,是在延迟和可实现的 top-1 ImageNet 精度表现方面的当前最先进技术,适用于 400 万到 2100 万 FLOP 范围内。MicroNet 的创新性体现在两个方面:

  • 它引入了来自 MobileNet 的瓶颈/逐点卷积层和深度卷积层的因式分解版本,称为微因式分解卷积,其方式是减少输入数据到输出数据的连接/路径数量。这是通过使用多个分组卷积和一些膨胀卷积来实现的。膨胀卷积仅仅是内核中具有固定间距的卷积。将这些技术视为一种稀疏计算,只计算最需要的部分,以确保最小的输入到输出路径冗余。

  • 它引入了一种新的激活函数,称为动态 shift-max,该函数利用分组卷积的输出,通过应用更高阶的非线性(两次),同时增强组间的连接。这是通过使用挤压和激励块的分组输出(每个通道生成一个单一值)作为加权机制,来获得基于组加权和的最大值。将此视为对 ShuffleNet 中通道洗牌的改进。图 3*.18* 显示了动态 shift-max 操作结构的示意图,展示了一个来自四个组、使用分组卷积操作的 12 通道的单一示例特征图:

图 3.18 – 动态 shift-max 一般操作流程

图 3.18 – 动态 shift-max 一般操作流程

MicroNet 利用了 ShuffleNet(通道洗牌机制)、ResNet(跳跃连接)、SENet(挤压和激励网络)和 MobileNet(从已因式分解的卷积中创建因式分解版本)等概念,并在其创新之上,创造了通过聚焦稀疏性概念和高效信息流改进的高效网络。该网络的具体细节可能令人不知所措,坦白说,也很难理解,因此此处提供的信息并不包含所有细节:

图 3.19 – 三个逻辑块的示意图,这些块被称为微块,用于构建不同大小的 MicroNet 变体

图 3.19 – 三个逻辑块的示意图,这些块被称为微块,用于构建不同大小的 MicroNet 变体

然而,图 3*.19* 显示了逻辑块如何在今天最先进的网络中,基于相同的思想构建不同规模的网络。

总结用于边缘计算的 CNN 架构

为了总结边缘计算的架构,现在你已经拥有了专家在该领域中用来构建高度有效的 CNN 架构的直观知识,这些架构相比今天的大型模型,如 GPT-3,能够以极小的占用空间运行。下图展示了不同架构家族在边缘计算中的总体 top-1 ImageNet 精度表现与 FLOPS 图:

图 3.20 – 低于 4 亿 FLOPS 的边缘架构系列的 top-1 准确率性能

图 3.20 – 低于 4 亿 FLOPS 的边缘架构系列的 top-1 准确率性能

对这些结果应保持谨慎态度;模型之间的训练策略可能不同,会显著影响可实现的ImageNet top-1 准确率,同时在不同模型运行的随机初始化之间可能会得到不同的结果。此外,延迟并不完全由参数数量或 FLOPS 直接表示,还受到各个操作的内存访问成本、I/O 访问成本以及不同操作的并行程度的影响。

总结一下,图 3.21 显示了本章介绍的所有 CNN 模型系列在 FLOPS 基础上的整体性能图:

图 3.21 – 基于 FLOPS 的 ImageNet top-1 准确率的整体 CNN 模型系列性能

图 3.21 – 基于 FLOPS 的 ImageNet top-1 准确率的整体 CNN 模型系列性能

再次强调,我们应对这里呈现的结果保持谨慎,因为在ImageNet数据集上执行的训练技术并未在不同的基准测试中完全标准化。训练技术的差异可能导致结果差异巨大,且将在第八章,《探索监督深度学习》和第九章,《探索无监督深度学习》中更为详细地讨论。另一个重要的注意点是,尽管ImageNet被认为是一个足够大的图像数据集,可以作为基准使用,但由于数据本身在某些情况下已被证明存在噪声标签和系统性错误,保持一定的怀疑态度是必要的。ImageNet的修正版ImageNet Real已经发布,但并非所有模型都以此为基准进行测试或预训练。为了 100%确定哪种架构在特定数据集上预训练后表现更好,请在你的数据集上进行训练!此外,FLOPS 指标并不能完全代表模型的实际延迟,实际延迟可能会因代码结构、模型如何分布在多个设备上、可用的 GPU 或 CPU 数量,以及模型架构的并行程度而产生较大差异。

概述

CNN 是捕捉图像数据模式的首选模型。本章介绍的精选架构是核心骨干网络,可以作为基础,进一步用于解决更多定制化的下游任务,如图像目标检测和图像生成。

本章涵盖的卷积神经网络(CNN)将在后续章节中作为基础,帮助你学习其他基于深度学习的知识。请慢慢消化,并查看本书 GitHub 仓库中离线实现的深度学习库中不同架构的实现;我们这里不会呈现实际的实现代码。既然我们已经详细讲解了中低级的卷积神经网络,接下来我们将切换话题,探讨递归神经网络。

第四章:理解递归神经网络

递归神经网络 (RNN) 是一种设计用来处理顺序数据的神经网络,能够意识到数据的顺序。顺序数据可以包括基于时间的序列数据,也可以是具有顺序但没有时间成分的数据,例如文本数据。这类神经网络的应用基于数据本身的性质。对于时间序列数据,这可以用于现在预测(利用过去和当前数据进行的预测)或目标预测。对于文本数据,可以用于语音识别和机器翻译等应用。

随着能够捕获序列数据并完全移除递归连接的神经网络(如 Transformer)的出现,过去几年递归神经网络的研究有所放缓,并且取得了更好的性能。然而,RNN 在今天的实际应用中仍然广泛使用,作为一个良好的基准或仅仅是一个替代模型,因为它们的计算量较少,内存需求低,并且具有合理的度量性能,适用于更快速的计算。

最突出的两种 RNN 层是 长短期记忆 (LSTM) 和 门控递归单元 (GRU) 。本书不会介绍原始的递归神经网络,而是通过 LSTM 和 GRU 作为复习。LSTM 和 GRU 的主要操作提供了一种机制,仅保留相关记忆并忽略无用的数据,这是为时间序列或顺序数据精心设计的关键归纳偏差。

在本章中,我们将更深入地探讨这两种 RNN 网络。具体来说,我们将涵盖以下主题:

  • 理解 LSTM

  • 理解 GRU

  • 理解标准 GRU 和 LSTM 层的进展

技术要求

本章内容简洁明了,但仍涵盖了 Pytorch 库中一些实际的实现。

你可以在 GitHub 上找到本章的代码文件,链接地址为 github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook/tree/main/CHAPTER_4

理解 LSTM

LSTM 于 1997 年发明,但仍然是广泛采用的神经网络。LSTM 使用 tanh 激活函数,因为它提供了非线性特性,同时提供了可以在更长序列中保留的二阶导数。tanh 函数有助于防止梯度爆炸和梯度消失。LSTM 层使用一系列顺序连接的 LSTM 单元。让我们深入了解 图 4.1 中 LSTM 单元的样子。

图 4.1 – 在一系列 LSTM 单元中的 LSTM 单元的视觉深度解析,形成了一个 LSTM 层

图 4.1 – 在一系列 LSTM 单元中的 LSTM 单元的视觉深度解析,形成了一个 LSTM 层

左侧的第一个 LSTM 单元展示了 LSTM 单元的高级结构,左侧的第二个 LSTM 单元展示了 LSTM 单元的中级操作、连接和结构,而右侧的第三个单元则是另一个 LSTM 单元,强调了 LSTM 层是由多个 LSTM 单元按顺序连接而成的。可以将 LSTM 单元视为包含四个门控机制,它们提供了忘记、学习、记住和使用序列数据的方式。你可能会好奇的一个显著问题是,为什么 sigmoid 在三条路径中被显示为一个独立的过程,这些路径从输入经过隐藏状态,到达遗忘门、记忆门和使用门。图 4*.1* 中展示的结构是 LSTM 单元的经典图示,但没有包含关于连接权重的信息。这是因为输入通过加权求和的过程,将前一个单元的隐藏状态和当前序列数据结合起来,每一条连接都有不同的权重集。图 4*.2* 显示了单个 LSTM 单元的最终低级结构,考虑了权重因素:

图 4.2 – LSTM 单元的低级结构

图 4.2 – LSTM 单元的低级结构

图 4**.2中,W 和 B 分别代表权重和偏置。两个小写字母分别代表数据类型和门控机制。数据类型分为两类——由 h 表示的隐藏状态和由 i 表示的输入数据。涉及的门控机制包括由 F 表示的遗忘机制、由 L 表示的学习机制(学习机制涉及两个权重和偏置),以及由 U 表示的使用机制。为了正确理解一个 LSTM 单元有多少个参数,我们还需要解码隐藏状态和输入状态权重向量的维度。为了方便起见,我们假设输入向量的大小为 n,隐藏状态的大小为 m。

隐藏状态权重的维度为:

nm

而输入状态权重的维度为:

另一方面,偏置是输入向量的大小。对于 Tensorflow 和使用 Tensorflow 的 Keras,偏置每个机制只添加一次。对于 PyTorch,偏置对于每个隐藏状态和输入状态权重都会添加。对于 PyTorch,偏置的参数数量可以定义为:

2n

由于有四个机制,如图 4**.2所示,这意味着在 PyTorch 实现中,LSTM 的参数数量可以根据以下公式计算:

参数数量 = 4(nm + n² + 2n)

现在我们了解了实际参数在单元中的位置,接下来我们深入探讨这些门控机制。

解码 LSTM 的遗忘机制

遗忘机制通过使用 sigmoid 激活函数与前一个单元状态相乘来实现。该门机制的名称暗示它基于当前输入序列和前一个单元输出的组合来确定需要删除的信息。可以这样理解:在01的范围内,过去的信息有多相关?sigmoid 机制强制将该范围限定在01之间。接近0的值会更多地遗忘前一个单元状态(长期记忆),而接近1的值则会更少遗忘前一个单元状态的记忆。

解码 LSTM 的学习机制

学习机制通过结合前一个单元输出的 sigmoid 激活函数和tanh激活函数,将遗忘门的输出与使用门的输出相乘,应用于当前输入序列的输出。这种机制也被称为输入门。该机制允许从当前输入序列中学习信息。学习到的信息随后会传递给记忆机制。此外,学习到的信息还会传递到下一个 LSTM 单元,用于信息使用的机制中。这两个机制将会依次介绍。

解码 LSTM 的记忆机制

记忆机制通过将遗忘过程剩余的信息与已学习的信息相加来实现,这就是学习门的输出。该门的输出将被视为 LSTM 单元的当前单元状态。单元状态包含所谓的 LSTM 序列的长期记忆。可以简单地将该机制视为一个操作,允许网络选择性地决定保留和记住输入的哪些部分。

解码 LSTM 的“信息使用”机制

信息使用机制通过在当前单元状态上应用 tanh 激活函数进行非线性处理,利用当前输入序列和前一个单元输出作为加权机制,决定应使用多少来自过去和现在的相关信息。应用使用门的输出将得到隐藏状态,这个状态也将作为下一个 LSTM 单元的前一个单元输出。

构建完整的 LSTM 网络

通常,要创建一个完整的 LSTM 网络,会将多个 LSTM 层连接在一起,使用多个 LSTM 单元的隐藏状态序列作为后续序列数据,应用到下一个 LSTM 层。在经过几个 LSTM 层后,前一层的隐藏状态序列通常会传递到一个全连接层,形成基于监督学习的简单 LSTM 架构的基础。图 4.3 显示了如何实现这一点的可视化结构:

图 4.3 – 一个简单的 LSTM 网络,包含两个 LSTM 层,输出连接到一个全连接层

图 4.3 – 一个简单的 LSTM 网络,包含两个 LSTM 层,并将其输入到一个全连接层

基于图 4**.3中描绘的网络,PyTorch 中的实现将如下所示:

  1. 首先,我们需要导入 PyTorch 库中的nn模块:

    import torch.nn as nn
    
  2. 现在,我们将基于图 4**.3定义网络架构,这次使用顺序 API 而不是类方法:

    RNN = nn.Sequential(
      nn.LSTM(
         input_size=10, hidden_size=20,
         num_layers=2, dropout=0,
      ),
      nn.Linear(in_features=10, out_features=10),
      nn.Softmax(),
    )
    
  3. LSTM 的输入大小、隐藏层大小和层数,以及线性层的输出特征大小,可以根据输入数据集和需求进行配置。注意,每个时间步或序列步的输入数据的大小可以大于一。这使得我们能够轻松地将原始特征映射到更具代表性的特征嵌入,并利用它们的描述能力。此外,通过将dropout参数设置为 0 到 1 之间的值,可以轻松添加 dropout 正则化器,这将在每层(除了最后一层)以指定的概率引入 dropout 层。在步骤 2中定义的pytorch中的 RNN 现在可以像任何在类中定义的 PyTorch 模型一样进行训练。

和往常一样,PyTorch 让构建 RNN 变得更加简单和快捷。接下来,我们将介绍另一种类型的 RNN,称为门控 递归单元

理解 GRU

门控递归 单元GRU)是在 2014 年发明的,基于 LSTM 中实现的思想。GRU 旨在简化 LSTM,并提供一种更快速、更高效的方式,实现与 LSTM 相同的目标——根据过去和现在的数据自适应地记住和遗忘。在学习能力和可达的性能指标方面,二者没有明显的“银弹”赢家,通常在行业中,二者会相互对比,找出哪个方法提供更好的性能水平。图 4*.4*展示了 GRU 的结构。

图 4.4 – GRU 的低级表示

图 4.4 – GRU 的低级表示

图 4*.4采用与图 4**.2*中 LSTM 相同的权重和偏置符号表示。这里有三种不同的小写字母符号表示。R 代表重置门,z 代表更新门,h 代表用于获得下一个隐藏状态的权重。这意味着 GRU 单元的参数比 LSTM 单元少,具有三组权重和偏置,而不是四组。这样,GRU 网络比 LSTM 网络稍微更快。

虽然通常描述为一个单元,但需要多个 LSTM 单元依次连接的相同理论也适用于 GRU:GRU 网络层将有多个 GRU 单元依次连接在一起。GRU 只包含两个机制,称为重置门更新门,并且只有来自前一个 GRU 单元的一个输入,以及一个输出到下一个 GRU 单元。这个单一的输入-输出明显比 LSTM 更高效,因为我们需要进行的操作更少。现在,让我们深入了解这两个机制。

解码 GRU 的重置门

GRU 的重置门作为一种机制,用于忘记先前单元的长期信息,也称为隐藏状态。这个机制的目标类似于 LSTM 单元中的忘记门。同样,它将在 0 到 1 的范围内,根据当前输入序列和先前单元状态,决定我们应该减少和删除多少先前获得的长期信息。

然而,GRU 的重置门在功能上与 LSTM 的忘记门不同。LSTM 的忘记门决定从长期记忆中忘记哪些信息,而 GRU 的重置门决定忘记多少先前的隐藏状态。

解码 GRU 的更新门

GRU 的更新门控制从长期记忆中传递到当前保持的记忆的信息量。这类似于 LSTM 中的记忆门,帮助网络记住长期信息。与前一个单元的隐藏单元相关的每个权重将学习捕捉短期依赖和长期依赖。短期依赖通常会有接近0的重置门输出值,更频繁地遗忘先前的信息,而长期依赖则通过学习长期依赖的权重和隐藏状态位置来进行反向操作。

在与 LSTM 的记忆门的差异方面,LSTM 的记忆门决定了从当前输入和先前的隐藏状态中记住哪些信息,而 GRU 的更新门决定了记住多少先前的隐藏状态。

GRU 是一种简单的 RNN,相比 LSTM 具有更高效的操作。现在我们已经解码了 LSTM 和 GRU,接下来,我们不再重复类似 LSTM 的仅含 GRU 的完整网络,而是探索如何在这两种方法的基础上进行改进。

理解相较于标准 GRU 和 LSTM 层的进展

GRU 和 LSTM 是当前最广泛使用的 RNN 方法,但有人可能会想知道如何突破标准 GRU 或标准 LSTM 能够达到的极限。构建这种直觉的一个好方法是理解这两种层类型都能够接受序列数据,而要构建一个网络,你需要多个 RNN 层。这意味着完全可以在同一个网络中结合 GRU 和 LSTM 层。然而,这并不足以被视为一种突破,因为完全的 LSTM 网络或完全的 GRU 网络随时都可以超过 GRU 和 LSTM 组合网络的性能。让我们深入了解一下可以在这些标准 RNN 层上做的另一个简单改进,叫做双向 RNN

解码双向 RNN

GRU 和 LSTM 都依赖于数据的顺序特性。这个顺序可以是时间步递增的前向序列,也可以是时间步递减的后向序列。选择使用哪个方向通常取决于试验和错误,往往自然的选择是使用前向时间顺序。

在 1997 年,出现了一种改进叫做双向 RNN(Bidirectional RNNs),它结合了前向 RNN 和后向 RNN,旨在最大化 RNN 模型能够处理的输入数据。原始的想法是利用未来信息和历史信息来估算当前时间步的值,因为未来和历史信息是可以获取的,两个 RNN 分别处理不同的数据集。这样自然就能够在这种数据设置下实现更好的预测性能。如今,这一想法已经扩展为一个通用的层,应用于相同的序列数据估计,并且也证明能够提供预测性能的提升。图 4.5 展示了使用 GRU 的双向 RNN 的一个例子,其中来自前向和后向 GRU 的隐藏状态被连接在一起:

图 4.5 – 双向 GRU

图 4.5 – 双向 GRU

连接后的隐藏状态可以传递给全连接层,进行标准的监督学习目标。以下是一个在 PyTorch 中实现双向 GRU 的示例:

RNN = nn.Sequential(
  nn.GRU(
     input_size=10, hidden_size=20,
     num_layers=2, bidirectional=True
  ),
  nn.Linear(in_features=10, out_features=10),
  nn.Softmax(),
)

接下来,让我们探索一下基于 LSTM 做出的一个改进。

向 LSTM 添加窥视孔(Peepholes)

2000 年引入的窥视孔使得细胞状态(来自前一个和当前的细胞),即 LSTM 中的长期记忆,能够影响 LSTM 单元中的 sigmoid 门控机制。直觉是,长期记忆包含了过去时间步的信息,而这些信息在前一个单元的隐藏状态中不可用的短期记忆中无法获取。这使得与普通 LSTM 相比,预测性能得到了改善。图 4.6 展示了来自细胞状态的额外窥视孔连接:

图 4.6 – LSTM 窥视孔连接

图 4.6 – LSTM 窥视孔连接

然而,这种方法的一个陷阱是,由于状态的长期记忆特性,单元状态可能会随着时间的推移增长到非常大的值,因为其没有边界。这可能会导致门控机制饱和,始终处于打开状态,从而使门控机制有时失效。这就引出了我们将在下一小节讨论的最后一种改进。

为了超越 LSTM 的窥视孔连接限制,加入工作记忆

2021 年,LSTM 的窥视孔连接上进行了改进,通过使用 tanh 激活函数来对单元状态施加边界,从而对单元状态进行限制。这一简单的改进在实验基准测试中表现出了比未加边界的 LSTM 更好的性能,被称为 LSTM 的工作记忆连接图 4.7 显示了 LSTM 结构中的工作记忆连接:

图 4.7 – LSTM 结构中的工作记忆连接

图 4.7 – LSTM 结构中的工作记忆连接

RNN 与标准的 MLP 类似,意思是没有一个通用的数据集可以作为不同研究项目中的参考数据集。即使使用相同的数据集,结果也可能不是最终的真理来源,因为数据集并不广泛,无法泛化到其他数据集。换句话说,在序列数据中没有类似 ImageNet 的通用数据集。文本数据、视频数据和其他时间序列数据之间差异很大,但本质上都被视为序列数据,并且可以输入到 RNN 中。无论是 RNN 还是 MLP,从任何地方获得的基准测试结果都需要谨慎看待,因为结果可能因数据集不同而大相径庭。然而,图 4.8 显示了在 COCO 数据集上使用 GRU、LSTM 和一个考虑生成文本自然性度量的基准版本——生成的文本自然性越高越好。

图 4.8 – 在 COCO 数据集上进行图像标题任务的 RNN 基准测试

图 4.8 – 在 COCO 数据集上进行图像标题任务的 RNN 基准测试

图中显示,LSTM-WM 在不同评估分数上的单次实验中优于其他方法。再次提醒,尽管如此,COCO 数据集并不代表序列数据或时间序列数据。

在你自己的数据集上试试看,确保了解!到此为止,我们已经讲解了 RNN 的重要概念,从基础到高级。接下来,让我们总结一下本章内容。

总结

循环神经网络是一种神经网络,明确在其结构中包括了序列数据的归纳偏置。

存在几种不同类型的 RNN,但它们的整体结构保持相同的高层次概念。主要是它们提供了不同的方式来决定从哪些数据中学习、记住哪些数据以及在记忆阶段从记忆中忘记哪些数据。

然而,请注意,最近出现了一种叫做 transformers 的架构,它将在第六章《理解神经网络 Transformer》一章中介绍,该架构证明了在处理序列数据时,递归并不是实现良好性能的必要条件

到此为止,我们已经完成了对 RNN 的介绍,并将在下一章简要探讨自编码器的世界。

第五章:理解自编码器

自编码器是一种主要用于实现表示学习的模型类型。表示学习是一种深度学习任务,专注于生成紧凑且具代表性的特征来表示任何单个数据样本,无论是图像、文本、音频、视频还是多模态数据。经过某种形式的表示学习后,模型将能够将输入映射到更可表示的特征,这些特征可用于区分自身与其他样本输入。所获得的表示将存在于潜在空间中,不同的输入样本将共同存在。这些表示也称为嵌入。自编码器的应用将与表示学习应用紧密联系,一些应用包括为其他后续监督学习目标生成预测特征,比较和对比野外样本,以及执行有效的样本识别。

注意,自编码器并不是执行表示学习的唯一方式。表示学习的主题将在第八章探索监督深度学习,以及第九章探索无监督深度学习中进一步讨论。

现在,我们知道自编码器学习生成独特的表示或嵌入。但架构是什么样的?让我们先了解标准架构,然后再发现一些更有用的进展。

在本章中,将涵盖以下主题:

  • 解码标准自编码器

  • 探索自编码器变体

  • 构建 CNN 自编码器

技术要求

本章包括一些在Python编程语言中的实际实现。要完成它,您需要安装以下库的计算机:

  • pandas

  • Matplotlib

  • Seaborn

  • scikit-learn

  • NumPy

  • Keras

  • PyTorch

代码文件可以在 GitHub 上找到:github.com/PacktPublishing/The-Deep-Learning-Architect-Handbook/tree/main/CHAPTER_5.

解码标准自编码器

自编码器更像是一个概念,而不是一个实际的神经网络架构。这是因为它们可以基于不同的基础神经网络层。处理图像时,您构建 CNN 自编码器;处理文本时,您可能希望构建 RNN 自编码器;处理包含图像、文本、音频、数字和分类数据的多模态数据集时,则使用不同层次的组合作为基础。自编码器主要基于三个组件,称为编码器瓶颈层解码器。这在图 5**.1中有所说明。

图 5.1 – 自编码器概念

图 5.1 – 自编码器概念

标准自编码器的编码器通常接收高维数据,并将其压缩为比原始数据维度更小的任意尺度,这样就会生成所谓的瓶颈表示,瓶颈表示紧密联系着瓶颈部分,意味着它具有一个紧凑的表示,并不包含任何无用的信息。瓶颈部分随后会传递给解码器,解码器使用与编码器所用尺度完全相反的方式来扩展维度,最终生成与输入数据相同维度的输出数据。

注意

编码器和解码器结构并非仅限于自编码器,也被广泛应用于其他架构中,例如变换器(transformers)。

区别在于瓶颈部分,它承载着代表性特征。瓶颈通常被压缩为较小的维度,但有时也可以增大瓶颈的尺寸,以容纳更多具有预测能力的代表性特征。

自编码器通常是用来重建输入数据的。自编码器模型的训练过程涉及比较生成的输出数据与输入数据之间的距离。在优化过程中,模型能够生成接近输入数据的输出,当模型能够完全重建原始输入数据时,可以认为瓶颈部分比原始输入数据本身具有更加紧凑和概括性的表示。该紧凑的表示随后可以用来实现其他任务,例如样本识别,或者甚至可以通过存储较小的瓶颈特征来节省空间,而不是存储原始的庞大输入数据。编码器和解码器并不局限于单层结构,可以定义为多层结构。然而,标准自编码器只有一个单一的瓶颈特征,这也被称为代码,或潜在特征

现在,让我们来探索自编码器的不同变种。

探索自编码器的变种

对于表格数据,网络结构可以非常简单。它仅使用一个包含多个全连接层的多层感知器(MLP),这些层逐渐减少编码器的特征数量,并使用多个全连接层逐渐增加解码器的输出数据,使其与输入数据的维度和大小相同。

对于时间序列或顺序数据,可以使用基于 RNN 的自编码器。关于基于 RNN 的自编码器,最常被引用的研究项目之一是使用基于 LSTM 的编码器和解码器的版本。该研究论文名为 Sequence to Sequence Learning with Neural Networks,作者为 Ilya Sutskever、Oriol Vinyals 和 Quoc V. Le(arxiv.org/abs/1409.3215)。该研究方法不是堆叠编码器 LSTM 和解码器 LSTM,而是将每个 LSTM 单元的隐藏状态输出序列垂直使用,解码器层按顺序继续编码器 LSTM 的顺序流,并以反向顺序输出重建的输入。还使用了额外的解码器 LSTM 层来并行优化,预测未来的序列。这个结构如 图 5.2 所示。需要注意的是,这种结构也可以通过使用原始的展平图像像素作为输入,适配到视频图像模态中。该架构也被称为 seq2seq

图 5.2 – 基于 LSTM 的自编码器结构

图 5.2 – 基于 LSTM 的自编码器结构

编码器 LSTM 的隐藏状态输出可以被认为是 LSTM 自编码器的潜在特征。Transformers 是一种新型架构,也能处理顺序数据,后面将在本章介绍。它有一些变体也可以被视为一种自编码器——一些 transformers 可以是自编码器,但并非所有 transformers 都是自编码器。

对于图像数据,通过使用卷积层,我们可以通过多层卷积和池化层逐渐缩小特征,直到应用全局池化层并将数据转化为 1 维特征。这代表了自编码器的编码器生成瓶颈特征。这一工作流与我们在前一节中讨论的 CNN 相同。然而,对于解码器来说,为了将 1 维池化特征重新放大为 2 维的类似图像数据,需要一种特殊形式的卷积,称为 反卷积

本节提到的变体都是关于使用标准的自编码器结构,但通过不同的神经网络类型来实现它们。还有两种额外的自编码器变体,其中一种变体基于数据输入的操作,另一种变体则是通过实际修改自编码器结构来实现数据生成目标。

对于用于数据处理的变体,其思想是在训练过程中向输入数据添加噪声,并保持原始输入数据不受噪声影响,作为目标用于预测性地重建数据。该自编码器的这种变体被称为去噪自编码器,因为其主要目标是去噪数据。由于目标已经从压缩数据转变为去噪数据,因此瓶颈特征不再限制于小尺寸。后续利用的特征不再仅仅局限于单一瓶颈特征,而是可以是网络中多个中间层的特征,或者简单地是去噪后的重建输出。该方法利用了神经网络执行自动特征工程的固有能力。去噪自编码器最著名的应用是在一个由葡萄牙保险公司 Porto Seguro 主办的基于表格数据的 Kaggle 竞赛中获得第一名的解决方案,在这个竞赛中,来自多个中间层的特征被输入到一个单独的多层感知机(MLP)中,以预测驾驶员未来是否会提出保险索赔(www.kaggle.com/competitions/porto-seguro-safe-driver-prediction/discussion/44629)。

对于使用自编码器结构修改的变体,其思想是生成两个瓶颈特征向量,分别表示标准差值和均值列表,以便根据均值和标准差值对不同的瓶颈特征值进行采样。这些采样的瓶颈特征值可以传递到解码器中,生成新的随机数据输出。该自编码器的变体被称为变分自编码器

现在我们已经对自编码器的变体有了一个较好的概述,接下来让我们深入研究 CNN 自编码器,并使用深度学习库构建一个 CNN 自编码器。

构建 CNN 自编码器

让我们先来了解一下什么是转置卷积图 5.3 展示了一个在 2x2 大小输入上使用 2x2 大小卷积滤波器,步幅为 1 的转置卷积操作示例。

图 5.3 – 转置卷积滤波操作

图 5.3 – 转置卷积滤波操作

图 5.3中,注意每个 2x2 的输入数据都标有从14的数字。这些数字用于映射输出结果,输出结果呈现为 3x3 的形式。卷积核会以滑动窗口的方式将每个权重分别应用到输入数据中的每一个值,四次卷积操作的输出结果会在图的下方呈现。操作完成后,每个输出结果将逐元素相加,形成最终的输出并加上偏置。这个示例过程展示了如何将一个 2x2 的输入扩展到 3x3 的输出,而无需完全依赖填充。

让我们在下面实现一个卷积自编码器模型,并在Fashion MNIST图像数据集上进行训练。这个数据集包含了鞋子、包包、衣服等时尚商品的图像:

  1. 让我们开始导入必要的库:

    import torch.nn as nn
    import torchvision
    from PIL import Image
    
  2. 接下来,我们将定义整个卷积自编码器的结构:

    class ConvAutoencoder(nn.Module):
      def __init__(self):
        super(ConvAutoencoder, self).__init__()
        self.encoder = None
        self.decoder = None
      def forward(self, x):
        bottleneck_feature = self.encoder(x)
        reconstructed_x = self.decoder(
          bottleneck_feature
        )
        return reconstructed_x
    

    这里展示的代码是一个基于PyTorch的卷积自编码器结构,包含编码器和解码器的变量占位符。编码器负责接收一张图片并逐步减少其维度,直到只剩下一个具有小表示足迹的单维度——瓶颈特征。解码器接着会接收该瓶颈特征,并生成与原始输入图像大小相同的特征图。编码器和解码器将在接下来的两步中定义。

  3. 编码器将被设计为接收一个 28x28 的灰度图像(单通道)。这个图像尺寸是 Fashion MNIST 数据集的默认大小。以下逻辑展示了定义编码器的代码,替换掉步骤 2中定义的占位符:

    self.encoder = nn.Sequential(
          nn.Conv2d(1, 16, 4),
        nn.ReLU(),
        nn.MaxPool2d(2, 2),
        nn.Conv2d(16, 4, 4),
        nn.ReLU(),
        nn.AvgPool2d(9),
    )
    

    定义的编码器有两个卷积层,每个卷积层后面跟着非线性激活函数ReLU和一个池化层。卷积层的滤波器大小分别为164。第二个池化层是一个全局平均池化层,用于将 4x9x9 的特征图减少到 4x1x1,每个通道只有一个值来表示自己。这意味着编码器将压缩原始 28x28 图像的维度,原图的 784 个像素仅保留四个特征,压缩率高达 99.4%!

  4. 解码器将接收每张图像的这四个特征,并再次生成 28x28 的输出特征图,重建原始的图像大小。整个模型没有应用填充。解码器将被如下定义,替换掉在步骤 2中定义的占位符解码器:

    self.decoder = nn.Sequential(
      nn.ConvTranspose2d(4, 16, 5, stride=2),
      nn.ReLU(),
      nn.ConvTranspose2d(16, 4, 5, stride=2),
      nn.ReLU(),
      nn.ConvTranspose2d(4, 1, 4, stride=2),
      nn.Sigmoid(),
    )
    

    这里使用了三层卷积转置层。每个卷积层后面都有一个非线性激活层,前两层使用了ReLU(作为标准的非线性激活函数),最后一层使用了sigmoid。使用sigmoid是因为fashion MNIST数据集已经被归一化,使得其值介于01之间。这里定义的卷积转置层采用了与编码器类似的滤波器配置,从16个滤波器到4个,最终使用1个滤波器生成单通道灰度图像。

  5. 现在我们已经定义了卷积自编码器,接下来让我们从torchvision库加载时尚 MNIST 数据集。本教程将使用Catalyst库来简化训练过程,因此我们将从torchvision中获取fashion MNIST数据集加载器和喂入器类,并对其进行修改,以便在Catalyst库中使用:

    class FashionMNISTImageTarget(
      torchvision.datasets.FashionMNIST
    ):
      def __getitem__(self, index):
       img = self.data[index]
       img = Image.fromarray(
         img.numpy(), mode="L"
       )
       if self.transform is not None:
         img = self.transform(img)
       return img, img
    

    来自torchvision的类已经具备下载和加载fashion MNIST数据集的必要逻辑。然而,数据喂入方法getitem的格式不符合图像生成的预期格式,因此需要进行修改,以使本实验能够正常运行。

  6. 请注意,Pillow库用于在第 5 步中加载图像。这是为了方便我们使用torchvision中的工具执行不同的转换步骤,例如图像增强。然而,在本实验中,我们将直接将pillow图像转换为Pytorch张量,使用如下的转换逻辑:

    def transform_image(image):
        return torchvision.transforms.ToTensor()(image)
    
  7. 现在,让我们加载fashion MNIST的训练和验证数据集:

    train_fashion_mnist_data = FashionMNISTImageTarget(
      'fashion_mnist/', download=True, train=True,
      transform=transform_image,
    )
    valid_fashion_mnist_data = FashionMNISTImageTarget(
      'fashion_mnist/', download=True, train=False,
      transform=transform_image,
    )
    loaders = {
      "train": DataLoader(
        train_fashion_mnist_data, batch_size=32,
        shuffle=True
      ),
      "valid": DataLoader(
        valid_fashion_mnist_data, batch_size=32
      ),
    }
    

    上面的代码会将数据集下载到fashion_mnist文件夹中(如果该文件夹不存在)。此外,loaders变量也在这里定义,以便可以被Catalyst库使用。

  8. 由于优化目标是减少重建像素值与目标像素值之间的差异,因此我们将在这里使用均方误差作为重建损失:

    criterion = nn.MSELoss()
    

    需要注意的是,尽管重建损失是无监督表示学习中的常见目标,但根据具体的算法或方法,可能会使用其他的指标或目标。例如,在Catalyst中,我们可以用来训练我们的模型:

    runner = dl.SupervisedRunner(
         input_key="features", output_key="scores", target_key="targets", loss_key="loss"
    )
    
  9. 接下来,我们将定义一个通用的函数,使得通过代码执行多个训练和验证实验变得更加简单:

    def train_and_evaluate_mlp(
      trial_number, net, epochs,
      load_on_stage_start=False, best_or_last='last',
      verbose=False
    ):
      model = net
      optimizer = optim.Adam(
        model.parameters(), lr=0.02
      )
      checkpoint_logdir = "logs/trial_{}_autoencoder".format(
    trial_number)
      runner.train(
        model=model,
        criterion=criterion,
        optimizer=optimizer,
        loaders=loaders,
        num_epochs=epochs,
        callbacks=[
                dl.CheckpointCallback(
                    logdir=checkpoint_logdir,
                    loader_key="valid",
                    metric_key="loss",
                    load_on_stage_end='best',
                )
        ],
        logdir="./logs",
        valid_loader="valid",
        valid_metric="loss",
        minimize_valid_metric=True,
        verbose=verbose,
      )
      with open(
        os.path.join(checkpoint_logdir, '_metrics.json'),
        'r'
      ) as f:
        metrics = json.load(f)
        if best_or_last == 'last':
          valid_loss = metrics['last']['_score_']
        else:
          valid_loss = metrics['best']['valid']['loss']
      return valid_loss
    

    这些只是基本的训练和评估模板代码,没有应用任何技巧。我们将在第八章中深入探讨训练监督模型的技巧,探索监督学习 深度学习

  10. 现在,我们准备通过以下逻辑来训练和评估 CNN 自编码器模型:

    cnn_autoencoder = ConvAutoencoder()
    best_valid_loss = train_and_evaluate_mlp(
        0, cnn_autoencoder, 20, load_on_stage_start=False, best_or_last='last', verbose=True
    )
    

    基于验证损失,表现最好的cnn_autoencoder权重将在训练完成 20 个 epoch 后自动加载。

  11. 使用提供的训练代码训练前述模型后,在 Fashion MNIST 数据集(图像尺寸为 1x28x28)上,您应该会得到类似于 图 5.4 中所示的示例输入/输出对,通过以下代码:

    input_image = valid_fashion_mnist_data[0][0].numpy()
    predicted_image = cnn_autoencoder(
      torch.unsqueeze(valid_fashion_mnist_data[0][0], 0)
    )
    predicted_image = predicted_image.detach().numpy(
    ).squeeze(0).squeeze(0)
    f, axarr = plt.subplots(2,1,  figsize=(5, 5))
    axarr[0].imshow(predicted_image, cmap='gray')
    axarr[1].imshow(input_image.squeeze(0), cmap='gray')
    

图 5.4 – Autoencoder 在 Fashion MNIST 数据集上的示例结果;底部表示原始图像,顶部表示重建图像

图 5.4 – Autoencoder 在 Fashion MNIST 数据集上的示例结果;底部表示原始图像,顶部表示重建图像

从结果来看,可以清楚地看出瓶颈特征能够相对完整地重建整个图像,它们可以作为更具代表性和紧凑的特征,代替原始输入数据使用。将瓶颈特征的表示值从 4 增加到大约 10 个特征,也应该能提高重建图像的质量。欢迎尝试并实验不同的参数!

总结

Autoencoder 被认为是实现跨数据模态表示学习的基本方法。可以将其架构视为一个框架,您可以在其中嵌入多种神经网络组件,从而允许您处理不同模态的数据或利用更先进的神经网络组件。

但是,请注意,它们并不是唯一的学习表示特征的方法。Autoencoder 还有许多应用,主要围绕不同的训练目标,使用相同的架构。在本章中简要介绍的两个变体是去噪自编码器和变分自编码器,它们将在 第九章《探索无监督深度学习》中得到详细介绍。现在,让我们再次转变思路,探索 Transformer 模型系列!

第六章:理解神经网络 Transformer

不要与同名的电气设备混淆,神经网络中的 Transformer 是多才多艺的 NN 变体。Transformer 能够处理并捕捉来自任何模态的数据模式,包括顺序数据如文本数据和时间序列数据、图像数据、音频数据和视频数据。

Transformer 架构于 2017 年提出,目的是取代基于 RNN 的序列到序列架构,主要聚焦于机器翻译任务,即将文本数据从一种语言转换为另一种语言。其表现优于基准的 RNN 模型,证明了我们不需要像 RNN 那样依赖数据的顺序性质(内在归纳偏置)。此后,Transformer 成为了神经网络架构家族的根基,并衍生出能够捕捉其他数据模态模式的模型变体,同时持续提升原始文本序列数据任务的性能。这表明,我们实际上不需要在模型中内置与数据模态相关的归纳偏置,而是可以让模型学习这些内在的结构和模式。你有顺序数据,比如视频、文本或音频吗?让神经网络学习它的顺序特性。你有图像数据吗?让神经网络学习像素之间的空间和深度关系。你大概明白了。

在我们深入探讨 Transformer 之前,先休息一下,看看本章将涵盖的主题:

  • 探索神经网络中的 Transformer

  • 全面解码原始 Transformer 架构

  • 仅使用编码器来发现 Transformer 的改进

  • 仅使用解码器来发现 Transformer 的改进

探索神经网络中的 Transformer

图 6.1 提供了 Transformer 所带来的影响概述,感谢各种 Transformer 模型变体的涌现。

图 6.1 – Transformer 的不同模态和模型分支

图 6.1 – Transformer 的不同模态和模型分支

变压器本身的架构并没有内在的归纳偏差。归纳偏差是指学习算法对数据做出的预假设。此偏差可以在模型架构或学习过程中构建,并有助于引导模型学习数据中的特定模式或结构。传统模型,如 RNN,通过其设计引入归纳偏差,例如假设数据具有顺序结构,且元素的顺序很重要。另一个例子是 CNN 模型,这些模型专门为处理网格状数据(如图像)设计,通过使用卷积层和池化层来引入归纳偏差,表现为局部连接性和位移不变性。在这种情况下,模型架构本身对能够学习的模式施加了某些约束。

变压器的设计理念是,我们应该允许模型根据提供的所有输入数据决定如何以及在哪里聚焦。这一过程通过多个机制的汇聚来技术性地执行,每个机制以不同的方式决定关注的焦点。因此,变压器完全依赖输入数据提供的信息来确定数据是否具有任何形式的归纳偏差(如果有的话)。这些聚焦组件正式被称为注意力层(attention layers)。在变压器之上进行的改进,形成了你在图 6.1中看到的分支,它们在结构上并没有偏离变压器的基础结构。通常,添加不同模态的改进是通过数据设置变体来完成的,以使输入数据结构适应变压器的结构,并根据目标任务应用的变化调整不同的预测输出细节。

目前,变压器(transformers)是具有最大学习和识别能力的架构家族,能够识别现实世界中存在的高度复杂的模式。除此之外,从 2022 年中到 2023 年近一年的时间里,变压器展示了其信息处理能力仅受硬件资源能力的限制。目前,最大的变压器模型之一是 Google 提供的PaLM,它比基础变压器扩展了许多倍,具有天文数字般的5400 亿参数。此外,坊间传闻,OpenAI 的 GPT-4 多模态文本生成模型(目前作为服务提供而非开源模型)包含多个模型或单个模型,其参数总和超过一万亿。这些庞大的模型通常需要数月的训练才能实现最佳性能,并且需要高性能的、最先进的 GPU。

你可能会好奇,为什么我们要训练这样一个庞大的模型,并且会怀疑它所提供的价值是否值得。让我们评估一下其中一个 Transformer 模型——GPT-3,这是由 OpenAI 开发的。GPT-3 是一种语言模型,它接收输入文本并基于它认为最合适、最有用的响应生成文本预测。这个模型涉及许多 NLP 领域的任务,这些任务传统上需要为每个任务使用不同的模型来完成,这使得它成为一个任务无关模型。它能够完成的任务包括机器翻译、阅读理解、推理、算术处理等,并且在展示广泛的语言理解能力方面表现出色,根据查询输入的文本格式提供不同的结果。例如,终端用户的应用包括生成能实现所描述目标的任何指定语言的代码,撰写具有指定主题的小说,获取关于任何公众人物的任何请求信息,总结客户反馈,通常或聚焦于特定话题,如“客户的烦恼是什么”,以及为虚拟世界中的角色对话增加现实感。迄今为止,GPT-3 已被应用于 300 多个领域和行业,虽然这个数字看似不大,但它为更多的创新和神经网络的广泛应用铺平了道路。

Transformer 经过多年的渐进式改进和严谨研究,虽然不断演化,但并未偏离太多原始架构。这意味着,理解原始架构对于理解所有最新和最强大的改进版 Transformer(如 GPT-3)至关重要。基于此,我们来深入了解 2017 年的原始 Transformer 架构,并讨论过去五年中研究带来的哪些组件发生了变化或适应,催生了新的模型架构。

全面解读原始的 Transformer 架构

在我们深入探讨模型结构之前,先来谈谈 Transformer 的基本目的。

正如我们在上一章中提到的,Transformer 也是一种架构族,利用了编码器和解码器的概念。编码器将数据编码成所谓的代码,解码器则将代码解码成类似于原始未处理数据的格式。最初的 Transformer 同时使用了编码器和解码器的概念来构建整个架构,并展示了其在文本生成中的应用。随后的适应和改进则要么仅使用编码器,要么仅使用解码器来完成不同的任务。然而,在 Transformer 中,编码器的目标并不是压缩数据以实现数据的更小、更紧凑的表示,而是主要作为特征提取器。此外,Transformer 中解码器的目标并不是重新生成相同的输入。

注意

可以使用 Transformer 组件构建一个实际的自动编码器结构,而不是使用 CNN 或 RNN 组件,但本书中将不涉及这一部分内容。

原始的 Transformer 将编码器和解码器中的数据/权重维度固定为统一的单一大小,从而能够进行残差连接,使用的维度为 512。

Transformer 还利用逻辑块结构来定义其创新之处,这使得你可以轻松扩展其规模。Transformer 的核心操作是识别输入数据样本中每个数据单元应关注整个输入数据的哪一部分。这种聚焦机制是通过一种叫做注意力层的神经网络层来实现的。关于注意力机制的许多变种在本书中将不会涉及。

Transformer 使用了最简单的注意力变种,该变种使用 softmax 激活函数来实现,softmax 强制所有值的和为 1.0,通常每层会有一个最强的焦点。可以将其视为类似于 RNN 中的门控机制,具体细节可以参考 第四章理解循环神经网络,以获得关于 RNN 的完整背景和它的工作原理。Transformer 使用多个注意力层,在这些层中,每个输入将以多种方式进行聚焦,而为了简化引用和扩展,这些层被统称为 多头 注意力 层。

变压器的输入和输出是以标记(tokens)的形式存在的。标记是指代可以传递进出变压器的输入数据的单个单位。尽管变压器的结构并没有明确设计以适应这些假设,这些标记可以是顺序的或非顺序的,分组的或非分组的。通过自变压器提出以来的研究和进展,文本的标记可以是词语或子词,图像的标记可以是图像块,音频的标记可以是音频数据的顺序时间窗口,例如,一秒钟的时间窗口,视频的标记可以是单个图像帧或一组图像帧。由于模型本身没有对任何数据模态类型的内在归纳偏见,因此数据的位置信息或顺序信息是通过标记显式地编码到数据中的。这些可以从增量的离散整数位置(1, 2, 3 …)到介于离散整数值之间的连续浮动位置(1.2, 1.4556, 2.42325 …),从绝对位置到相对位置,最后,每个标记或嵌入的单一值位置可以是可学习的。原始的变压器使用了一种映射函数,将绝对整数位置映射到考虑标记数据维度和预定统一数据维度大小的相对浮动位置,但现今最先进的模型通常采用可学习的嵌入。嵌入是一个查找表,用于将任何离散的类别数据编码为原始类别的更高维度和更复杂的表示,可以准确地区分不同的类别。请注意,实际的输入数据标记本身通常也会根据输入数据(图像帧或块通常不使用嵌入,因为它们已经具有较高的维度,但文本标记是类别性的,因此会使用嵌入)应用嵌入。

图 6.2展示了架构的高级概览:

图 6.2 – 通过输入和输出的可视化查看变压器,其中包括用于将英语句子翻译成普通话对应句子的编码器和解码器任务

图 6.2 – 通过输入和输出的可视化查看变压器,其中包括用于将英语句子翻译成普通话对应句子的编码器和解码器任务

编码器和解码器结构有些相似,唯一的区别是解码器多了一个中间的softmax激活函数。

解码器还会接收除编码器输出之外的其他数据。

为了在输出数量没有约束的情况下实现数据生成,转换器被设计成一个自回归模型。自回归模型使用第一次预测的结果作为第二次预测的输入,并随后用作接下来的预测输入。下图展示了这个预测过程的一个示例,使用基于文本的数据,旨在实现从英语到普通话的机器翻译。

图 6.3 – 转换器的自回归工作流

图 6.3 – 转换器的自回归工作流

转换器的每次前向传递都会预测一个单一的输出。在转换器输出时,预测的句末标记会被加入,表示预测完成。然而,在训练过程中,不需要执行这个自回归循环;相反,会在随机位置生成掩码,屏蔽所有目标标记之后的未来标记,以防止模型直接复制单个目标标记并获取未来标记的信息。简单来说,掩码机制在预测或推理阶段不会被使用。

除了实际的注意力机制外,多头注意力层还有一些其他关键操作。图 6.4 显示了解开后的多头注意力层,以及之前提到的在缩放点积注意力层中的注意力机制。

图 6.4 – 使用缩放点积注意力结构的多头注意力层

图 6.4 – 使用缩放点积注意力结构的多头注意力层

将多个注意力层视为多个不同的聚焦模式在人类对相同数据的贡献。softmax来自聚焦在不重要的区域。注意力机制旨在找出每个标记与整个输入标记集合的相关程度,换句话说,就是找出哪些标记与哪些标记进行了交互,并弄清楚它们之间的依赖关系。回想一下,模型的维度必须在整个架构中保持固定大小,以确保值可以在不需要额外处理的情况下相加。由于多个注意力头的输出将被连接而不是相加,因此线性层的维度必须在各个头之间均匀分配。例如,使用 8 个头和 512 的数据/权重维度,每个头的查询、键和值的线性层维度将是 512/8=64 个神经元。图 6.5 显示了输出及其形状的一个简单示例,工作流中每个标记的固定数据/权重维度为 3,使用与图 6.4相同的输入文本数据。

图 6.5 – 多头注意力层在连接前的部分操作,使用单词“I”在“I love you”上下文中的查询,线性层输出维度为 3

图 6.5 – 在上下文“I love you”中,使用词语“I”的查询与输出维度为 3 的线性层连接之前的多头注意力部分操作

对于其他两个查询词“love”和“you”,也会执行相同的操作。单个头部的操作以一种方式关注输入数据,而其他头部则以不同的方式关注输入数据。在编码器-解码器层中,查询来自解码器层,而键和值来自编码器层。这使得解码器能够选择在给定的输入文本中集中注意力的位置,以生成下一个输出。图 6.6展示了一个很好的方式来思考多头注意力的输出,其中四个头的多头注意力关注文本的不同部分。

图 6.6 – 带有四个头的多头注意力示例输出

图 6.6 – 带有四个头的多头注意力示例输出

Transformer 中的正则化机制与其他先前的架构(如 CNN、RNN 和 MLP)中描述的目的一致,这部分内容在这里不再赘述。这总结了 Transformer 的所有组件。这个基础架构使我们能够比旧的架构(如序列到序列或自编码器)获得更好的数据生成结果。尽管原始架构主要关注文本数据的应用,但该概念已被改编并适用于处理其他类型的数据模态,如图像、视频和音频数据。回想一下,原始模型之后的改进和适应仅使用了编码器或仅使用了解码器。在接下来的两个主题中,我们将分别深入探讨这两个不同的概念。

揭示仅使用编码器的 Transformer 改进

我们将讨论的第一类基于变压器的架构进展是仅使用原始变压器的编码器部分并使用相同多头注意力层的变压器。采用仅编码器的变压器线通常是因为没有使用掩蔽的多头注意力层,因为不使用下一标记预测训练设置。在这类改进中,训练目标和设置会根据不同的数据模态有所变化,并且在相同数据模态下的序列改进也会略有不同。然而,有一个概念在不同数据模态中保持相对恒定,那就是使用半监督学习方法。在变压器的情况下,这意味着首先执行一种无监督学习方法,然后再执行直接的监督学习方法。无监督学习为变压器提供了一种基于对数据特性更广泛理解来初始化其状态的方式。这一过程也被称为预训练模型,这只是一种无监督学习的形式。无监督学习将在第八章中进行更为详细的讨论,探索无监督 深度学习

一些改进是针对数据模态的特性进行的,主要包括文本特定的改进,并涉及优化任务类型的变化。其他改进则是通用的变压器架构改进,可以推广到不同的数据模态。我们将从专注于通用架构改进开始,然后再深入讨论专门为文本量身定制的改进,这些改进可能适用于其他数据模态,也可能不适用。首先,我们将介绍一种仅包含编码器结构的基础架构,称为 BERT。

softmax层。这个特定任务已被证明是作为预训练方法非常有效,它允许下游后续的监督任务获得更好的表现。由于标记在原始序列中的不同位置被掩蔽,因此该架构被称为双向的,因为除了过去的标记外,未来的标记也可以被注意并预测过去的标记。例如,15%的标记被掩蔽,其中 10%被随机替换为随机标记,充当噪声,这使得模型类似于去噪自编码器,并使其对噪声具有鲁棒性。标准的自回归结构通常是单向的,无法利用未来的标记。BERT 还引入了一种方法,通过单个输入表示编码多个句子。这是通过添加一个特殊的分隔符标记来实现的,该标记具有自己的学习嵌入,用于表示文本之间的分隔,以及一个额外的段落嵌入,标识该标记属于哪个句子。这还允许进行另一项任务——softmax。这三种嵌入(位置嵌入、段落嵌入和标记嵌入)在传递给 Transformer 之前被加在一起。图 6.7展示了这一架构。

图 6.7 – BERT 架构

图 6.7 – BERT 架构

该架构专注于表示额外输入的方式,并通过多任务学习增强模型对数据模态性质的理解,超越了之前在多个下游文本任务上的表现。

现在我们已经介绍了基础的仅编码器模型,准备探索针对仅编码器 Transformer 的不同类别的进展。此次改进将按进展类别进行探索,而非按模型分类,因为不同的模型结合了许多改进,比较实际的进展会变得困难。三种进展类型如下:

  • 改进基于数据模态的预训练任务

  • 在紧凑性、速度和效率方面的架构改进

  • 核心/功能性架构改进

我们将跳过基于数据的改进细节,例如使用多语种数据构建多语种 BERT,或使用基于子词的标记来减少词汇量并增加词汇复用性的问题。

改进仅编码器的预训练任务和目标

回想一下,MLM 和 NSP 是基础 BERT 模型中使用的任务,其中 MLM 被提到是一个有助于理解文本语言的强大任务,但它可以很容易地适应来自其他数据模态的 token。相比之下,NSP 已被证明不稳定,并且并非在所有情况下都能切实帮助模型提升性能。一个改进方向是使用句子顺序预测SOP)。与仅仅使用同一文档中正向连续句子的倒序作为负句相比,SOP 在提升下游监督任务性能方面展现了更高的一致性。从概念上讲,它学习句子之间的衔接性,而不是同时预测两句话是否来自同一主题。这一方法是在 2020 年的ALBERT模型中引入的,该模型主要关注于 Transformer 的效率。

另一个值得注意的改进是替代 token 检测RTD)。RTD 来自ELECTRA模型,它预测一个 token 是否被随机 token 替代,并且要求学习所有 token 位置的信息,这与 MLM 只从掩码 token 位置进行学习的过程不同。这是对 MLM 目标的改进。

接下来,我们将深入探讨在架构紧凑性、速度和效率方面的改进。

提升仅编码器 Transformer 的架构紧凑性和效率

Transformer 已经证明它们具备极大的结构能力,能够接收随模型增长的信息。一些关于 Transformer 的研究集中在将 Transformer 模型扩展到数百亿参数的规模,无论是在仅解码器还是仅编码器模型中。使用这些庞大模型所取得的成果令人惊叹,但由于需要使用最先进的 GPU 和机器,这些机器并不是大多数人都能轻松获得或负担得起的,因此这些模型不适合实际使用。这时,架构紧凑性和效率的改进可以帮助平衡这一差距。由于我们大多数人受限于可用的硬件资源,如果模型能在保持相同性能的前提下,在架构上有效缩小尺寸,这将使得在将模型扩展到硬件资源极限时,能够提供更高的性能。

该领域的另一项显著进展再次来自 ALBERT 模型,这是一种轻量级 BERT 模型。其主要改进在于以卷积层被分解的方式分解了嵌入层,从而实现深度卷积。使用较小的嵌入维度,并通过一个全连接层将较小维度的嵌入映射到所需的相同维度。由于嵌入是必需的权重,并且对参数的数量有贡献,分解嵌入层使得变换器模型的参数数量更少,从而使得 BERT 在较少参数的情况下具有更好的性能。需要注意的是,一些改进增加了模型并行性并使用了巧妙的内存管理,但这些内容在此不作详细讨论。不过,由于有一些方法能在推理和训练阶段加速模型,因此这些内容将在第十五章《在生产中部署深度学习模型》中讨论,该章专门讲解部署。

改进仅编码器变换器的核心功能架构

该领域最显著的进展来自于 2021 年推出的解码增强 BERT 与解耦注意力DeBERTa)模型,它被认为是当前架构的 SOTA。其主要思路是使用两个独立的位置编码嵌入,分别是相对位置和绝对位置。然而,这些位置嵌入并不是与输入令牌嵌入一起相加的,而是作为独立的查询和键被输入到变换器的层中,并拥有自己的全连接层。这使得可以获得三种显式的注意力图,具体包括内容到位置的注意力、内容到内容的注意力和位置到内容的注意力。然后,这些不同的注意力图会被加总在一起。相对位置特别用于每个中间多头注意力层的内部缩放点积注意力层,作为键或查询,取决于它是被内容关注还是关注内容。

这允许在每一层注意力机制中显式地考虑原始相对位置的关系,而不是在经过多层后可能被遗忘。我们本质上是在为相对位置数据应用一种跳跃连接的形式。相对编码使得模型能够通过概念上允许在每个相对位置上重用模式标识符,从而学习到更具泛化性的表示。这种解决方案类似于如何在单幅图像的不同分区使用相同的卷积滤波器,而不是在整个图像上使用一个全连接层,这样做无法跨整个输入空间重用模式标识符。至于绝对位置的使用,它仅仅是由于文本数据的特性需要绝对位置,以便区分不同事物的绝对重要性,例如警察比大多数人拥有更多的权力,卡车比摩托车大。然而,绝对位置仅在最后几层中添加一次,使用一种叫做增强掩码解码器EMD)的机制。EMD 将绝对位置嵌入作为缩放点积注意力的查询组件。没有明确的理由说明为什么只在最后几层应用,实际上它可能可以扩展到每一层。

分桶

使用分桶机制将多个位置组合成一个数字,以减少嵌入的数量,从而减少参数数量。可以将其视为分组位置信息,再次增加了模型的紧凑性。

揭示仅编码器变换器如何适应其他数据模态

变换器能够处理其他数据模态。首先,输入数据只需要以序列格式结构化。之后,由于预训练是变换器的核心,需要为仅编码器变换器制定合适的任务目标,以实现一个能够捕捉特定数据模态理解的预训练状态。尽管使用文本引入变换器时,重点放在无监督预训练方法上,但它实际上可以是监督、无监督或半监督的,关键在于将模型的权重设置为一个状态,以便后续的监督下游任务能够加以利用。

对于图像,Vision TransformerViT)于 2021 年推出,形成了基于图像的变压器的基础,并证明了与卷积基础模型相比,变压器在图像任务中能实现竞争性的性能。变压器处理图像时最棘手的问题之一是图像的体积庞大,处理整个大输入空间会迅速膨胀变压器模型的大小。ViT 通过将图像拆分为系统的补丁来解决这个问题,每个补丁随后会输入到一个单一的全连接层中,从而大幅度减少每个图像补丁的维度。维度已减少的补丁数据将作为变压器的令牌嵌入。补丁转换是将图像输入以变压器可以处理的格式表示的关键过程。此外,这种基于补丁的机制引入了一些形式的图像领域归纳偏差,但架构的其余部分几乎不受图像数据归纳偏差的影响。由于变压器几乎总是与预训练方法结合使用以提高性能,ViT 采用了类似于 CNN 模型系列的预训练方式,使用监督分类预训练。

对于音频,wav2vec 2.0于 2020 年创建,采用了与其前身wav2vec相似的概念,但不同的是它在架构中采用了仅解码器的变压器。简要概述是,wav2vec 2.0 首先使用一维卷积将特定时间窗口长度的音频编码为一个较低维度的空间,作为特征向量。每个窗口的编码特征向量随后会被输入到解码器变压器中并作为令牌。这里应用了无监督预训练方法,类似于 BERT 中的 MLM,其中输入的编码特征向量的某些部分被屏蔽,任务是预测被屏蔽的编码特征向量。预训练后,模型将在下游任务中进行微调,通常在变压器输出之后接一个线性层,用于语音识别等任务。wav2vec 2.0 在下游语音识别数据集上实现了新的 SOTA,并证明了音频与变压器的兼容性极好。

VideoBERT,由 Google 在 2019 年创建,不仅展示了如何为 transformer 模型建模视频数据的方法,还展示了 transformer 模型以多模态方式同时学习视频和文本的联合表示的能力。需要注意的是,视频本身就是一种多模态数据,因为视频数据包含多个图像帧以及音频数据组件。在 VideoBERT 中,使用带有明确起始和结束时间戳注释的烹饪视频句对数据集来对模型进行预训练。该模型通过将一预设数量的帧通过预训练的 3D 卷积模型编码成特征向量并将其视为 token 来处理图像帧序列。这些编码后的视频 token 随后与文本数据配对,并使用类似于 MLM 的目标来预测视频和文本 token 的掩码 token。该模型可以作为视频和文本相关数据的特征提取器,或者通过在 transformer 模型输出上增加一个线性层进行微调。

不同数据模态的前述总结旨在提醒我们,transformer 不仅可以应用于文本数据,还能应用于更多的数据模态,而不是作为这些模态当前技术状态的介绍。这里的一个关键点是,不同的数据模态需要被排列成具有降维的 token 格式序列,并且需要为仅编码器的 transformers 制定合适的任务目标,以便其达到能够捕捉数据模态主要特征的预训练状态。此外,BERT 的预训练方法可以轻松适配到其他数据模态。因此,与其称之为“masked language modeling”,不如称之为“masked token modeling”。接下来,让我们揭示仅使用解码器的 transformer 改进。

使用仅解码器的 transformer 改进

回想一下,transformer 的解码器模块专注于自回归结构。对于仅解码器的 transformer 模型,预测 token 的自回归任务保持不变。随着编码器的移除,架构必须调整其输入,以接受多个句子,类似于 BERT 的做法。开始、结束和分隔符 token 被用来顺序编码输入数据。在预测过程中,依然会进行掩码操作,以防止模型依赖当前 token 来预测未来的输入数据 token,这与原始 transformer 及位置嵌入相似。

深入了解 GPT 模型家族

所有这些架构概念都是由 GPT 模型在 2018 年提出的,GPT 是生成预训练(Generative Pre-training)的缩写。顾名思义,GPT 也采用了无监督预训练作为初始阶段,随后进入监督微调阶段以应对下游任务。GPT 主要关注文本数据,但相同的概念也可以应用于其他数据模态。GPT 使用基础的语言建模任务进行预训练,通过给定前面的标记作为上下文,在一个固定的窗口中预测下一个标记,从而限制输入的上下文标记数量。在预训练权重后,模型会根据任何监督任务类型的目标进行微调。GPT-1 在语言建模任务目标的辅助作用下,与主要的监督任务一起使用,试图提升下游任务的表现,并且证明它只在较大的数据集上能提高表现。此外,模型首次展示了将语言建模作为预训练任务应用于其他任务的泛化能力,即使不进行微调,仍能取得良好的成绩。这种行为被称为零-shot,将在第九章,《探索无监督深度学习》中详细讨论。该模型在 7,000 本未公开的书籍上进行了预训练,具有 1.17 亿个参数和 12 个重复的解码器块。

需要明确的是,GPT 模型能够在不进行微调的情况下执行任务(少样本或零样本学习)并不意味着微调在特定领域任务中的好处可以被忽略。尽管 GPT 系列模型在各种任务中展示了令人印象深刻的能力,但对于特定应用,微调仍然可能具有优势,从而提升性能并更好地适应给定任务的独特需求。

进入下一个进展,GPT-2 在架构上没有进行实质性的变化,其主要思想是强调零-shot 学习的能力,并通过任务条件化的概念,将 GPT-1 模型在层数、嵌入维度、上下文窗口大小和词汇量上扩大了 10 倍。任务条件化的概念是指你告诉模型应该生成什么样的输出,而不是仅仅依赖于训练任务类型所决定的固定、单一的已知预测输出。为了实现类似人类的真正灵活性,它们并没有添加新的架构来适应任务条件化的思想,而是决定直接将变换器的文本输入用作任务条件化的方法。例如,像“我爱你 = ,我爱深度学习=”这样的自由形式文本输入自然引导变换器执行从英语到普通话的翻译。也可以通过另一种方式来指定任务和上下文,例如“(翻译成普通话),我爱深度学习 =”。GPT-2 通过在各种文本领域(如新闻文章、小说、维基百科和博客)上进行语言建模目标的预训练,实现了这一点,基本上使用了比 GPT-1 训练数据集更大的数据集。这项工作展示了真正的 AI 泛化潜力,尽管目前只使用了文本和图像数据。对于图像数据,这是通过一个名为DALL-E的模型来实现的,该模型能够根据提供的输入文本生成图像。

最后,GPT-3 模型在 2020 年推出。它类似于之前的进展,主要变化是增加了模型各个组件的规模,并且通过不同的方式来规定任务条件,以适应不同类型的文本示例。我们这里不会深入讨论这一点。尽管 GPT-2 在零-shot 设置中表现良好,且不需要对下游任务进行微调,但只有通过微调,该模型才能在多个文本语言数据集上超越以前的基准。GPT-3 的庞大规模彻底消除了微调的需求,在没有任何微调的情况下超越了之前的基准。然而,该模型需要专门的机器进行训练,个人或小型组织难以直接使用。目前,它是 OpenAI API 服务的一部分。GPT-3 展示了在许多不同任务中的广泛适用性,无需任何微调,其中一些最引人注目的应用包括为任何编程语言生成代码、讲故事和制作非常逼真的聊天机器人。这三项改进推动了许多基于语言任务的数据集的性能限制,使其达到更高水平,并且作为机器学习普适性组件的一个例子,展示了 transformers 的巨大学习潜力,只受到硬件资源的限制。

接下来,我们将讨论一种略有不同的解码器模型,值得一提的是 XLNet 模型。

深入了解 XLNet 模型

如果你和你的朋友被给出提示“_ _ 是一座城市”,你们两个会分别预测填入空白的是“纽约”吗?这是编码器-仅 transformers 的 MLM 目标的一个缺陷,因为它们通过一次预测多个标记来学习。然而,标准的自回归模型由于其自回归的特性,只预测未来并且一次只预测一个标记,因此不会受到这个问题的影响。编码器-仅 transformers 具有双向序列支持,这有助于提高性能。XLNet 主要基于这样一个思想:使自回归模型以双向方式关注数据,就像编码器-仅模型一样,同时保持自回归模型预测单一输出的优势。

XLNet 通过在 MLM 预训练期间调节解码器单独的变换器来实现双赢。其思路是通过使用掩蔽机制,预训练模型在所有标记序列顺序的排列上进行训练。标准自回归模型在预训练时按“1-2-3-4”的顺序使用来预测第五个标记,但在 XLNet 中,可以将顺序改为“3-4-1-5”来预测第二个标记。XLNet 还引入了另一个额外的查询线性层隐藏状态,并添加了一个额外的单一固定可学习嵌入,将与每个标记嵌入一起穿过注意力层,作为与原始基于标记的内容流一起的额外流路径。这个额外路径被称为查询流。整个过程称为掩蔽的双流注意力层,如图 6.8所示。

图 6.8 – XLNet 工作流

图 6.8 – XLNet 工作流

内容基于流的前向传播机制对于序列排序中的任意位置具有能够关注其自身位置和之前位置的内容的能力,而基于查询的流对于序列排序中的任意位置仅具有关注其之前位置内容的能力。图 6.8 (a) 显示了在模型中实际位置 1 的内容流的注意力操作如何能够关注其他位置的所有标记,因为“3-2-4-1”排序方式将位置 1 排列为最后一个标记。图 6.8 (b) 显示了如何通过位置 1 的标记内容来更新额外的查询流路径数据(表示为g),并且g组件作为查询来获取下一个g作为注意力层的输出。如果模型的第四个位置需要更新,内容流中将仅关注第二和第三个位置的内容,因为根据排列后的序列顺序,第一个位置是未来的标记。基于此概念动态应用掩蔽机制,以防止每个位置的hg在定义之前关注排列序列中的内容位置。注意,图 6.8 (c) 中的w组件表示共享于所有位置的单一权重嵌入,适用于查询流。在预训练过程中,查询流作为最终分类输出路径,适用于 MLM 目标,其中最终的h组件会被忽略。然而,在传递到微调阶段时,查询流组件和基于查询的线性层隐藏状态会被丢弃或完全忽略。

通过在预训练期间利用单词预测的语言建模目标,同时使数据的双向上下文得以实现的创新方法,提升了下游监督学习任务的性能。

讨论解码器单独的变换器模型的其他进展

自 GPT 问世以来,基础的解码器架构没有发生任何具有重大影响的变化。大多数工作集中在以下三个方面:

  • 将 transformer 应用于不同的任务类型

  • 将模型规模扩大到数千亿个参数

  • 专注于工程解决方案,以使得一个巨大的模型可以在现有的硬件资源上进行训练

其中一个最著名的工作是利用解码器 transformer 处理其他任务类型的 DALL-E 模型,由 OpenAI 团队开发。在 DALL-E 的第一版和第二版中,transformers 被用来自回归地预测一个紧凑的图像嵌入,给定文本输入和一些其他特定于实现的输入。这些图像嵌入随后通过其他机制转换成实际的图像嵌入。

对于模型扩展,GPT-3 展示了随着模型容量的增加,性能也可以得到提升。首先提到的是Megatron,该模型通过工程策略使 transformer 架构更加高效,实现了并行化,并且能够使用 512 个 GPU 进行训练,使其比 GPT-2 大了 5.6 倍。GPT-3 超越了 Megatron,但另一个名为BLOOM的模型使用 Megatron 作为基础,将其扩展到 176B,匹配了 GPT-3 的 175B 模型规模。最值得注意的并不是它比 GPT-3 更大,而是 BLOOM 开创了一个新范式——开源,这意味着研究人员和机构的集体工作和贡献也可以在大规模上训练出高效且具有优异表现的模型。过去,这些庞大的模型一直是大型企业独占的,因为它们需要极其庞大的硬件资源。除此之外,另一个在 2022 年发布的模型PaLM将 GPT 的规模扩展到了 5400 亿个参数,超过了其他所有较小的 GPT 变体模型的性能。PaLM 2通过在多个任务上提供增强的性能,超越了其前身,包括英语和多语言理解、推理和代码生成。它通过优化计算扩展法则、利用多样的多语言数据集以及实施架构改进实现了这一目标。PaLM 2 在多语言能力、分类、问答和翻译任务中表现突出,展示了其广泛的适用性。该模型还结合了控制令牌来减少毒性,并为负责任的发展和部署提供了指导原则。

在为特定任务选择变换器模型时,采取平衡的方法至关重要,除了性能指标外,还需要考虑多个因素。模型的大小和资源需求在确定所选模型的可行性和适用性方面起着重要作用。尽管像 GPT-3 这样的大型模型展现了令人印象深刻的能力,但它们的资源需求可能并不适合所有情况,特别是对于计算资源有限的个人或小型组织。

在为特定任务选择变换器模型时,权衡模型的大小、性能和资源需求是至关重要的。在某些情况下,小型模型可能提供足够的性能,同时更高效且更环保。此外,尽管模型的规模不如一些最知名的模型(如 GPT-3),但通过微调通常可以提高模型在特定领域任务中的表现。最终,最佳选择取决于特定任务的独特需求和限制,以及可用于模型训练和部署的资源。

总结

变换器是多功能的神经网络(NN),能够捕捉任何数据模态之间的关系,而无需在架构中引入显式的数据特定偏见。与其说是一个能够直接处理不同数据模态的神经网络架构,不如说是需要仔细考虑数据输入结构,并设计合适的训练任务目标,才能成功构建一个高效的变换器。即使对于当前的最先进(SOTA)架构,预训练的好处仍然适用。预训练这一过程是“迁移学习”概念的一部分,迁移学习将在监督学习和无监督学习章节中有更详细的讨论。目前,变换器可以执行数据生成和监督学习任务,并且越来越多的研究在探索变换器应用于未开发的细分任务和数据模态。期待未来几年中,变换器作为技术进步的前沿,带来更多深度学习的创新。

到目前为止,你已经获得了根据你的数据和需求适当地选择和设计神经网络架构所需的知识。在第二章第六章中介绍的大多数架构和概念从零实现起来是很有挑战的,但借助像github.com/rwightman/pytorch-image-models这样的开源工作,以及许多深度学习框架的帮助,使用一个模型只是导入库并添加几行代码的问题。实际上,理解每个架构的底层实现并不是使用这些模型所必需的,因为采用公开可用的工作(包括在大数据集上预训练的权重)非常方便。今天,大多数情况下,理解像 CNN 这样的架构概念的大致原理,并且知道一些训练模型的基本内容,实际上就足以从这些几乎可以直接使用的架构/模型中获得实际好处。然而,理解这些架构的实现细节是至关重要的,在以下情况下将证明其价值:

  • 当事情失败或未按预期工作时(模型未收敛到最优解或发生发散,模型错误与准备输入数据的形状不符,等等)。

  • 当需要根据你的数据集、运行时要求或性能要求选择一个更合适的架构时。

  • 当你发明一个崭新的神经网络层时。

  • 当你想解码神经网络实际上学习了什么,或者当你想理解神经网络为什么做出某个预测时。这将在第十一章《解释神经网络预测》和第十二章《解释神经网络》中进行更深入的探讨。

  • 将一个架构领域的概念应用到另一个领域。例如,DenseNet 和 ResNet 的跳跃连接可以轻松地转移到 MLPs 中。

既然你已经读完了这一章,这一章完成了神经网络架构的具体内容,给自己点个赞吧。你现在已经掌握了处理图像(CNNs)、时间序列或序列数据(RNNs)、处理表格数据(MLPs)以及万金油(transformers)模型的知识。然而,架构的性能与数据类型、数据结构、数据预处理方法、权重学习与优化方法、损失函数以及优化任务密切相关。关于这些组件的更多细节将在第八章《探索监督式深度学习》和第九章《探索无监督深度学习》中解读。

在下一章,我们将探讨一种新兴的方法,通过神经架构搜索自动设计神经网络模型。