Python-与-TensorFlow2-生成式-AI(一)

Python 与 TensorFlow2 生成式 AI(一)

原文:zh.annas-archive.org/md5/d06d282ea0d9c23c57f0ce31225acf76

译者:飞龙

协议:CC BY-NC-SA 4.0

序言

“想象力比知识更重要。”

– 阿尔伯特·爱因斯坦,《爱因斯坦关于宇宙宗教和其他见解与格言》(2009)

在本书中,我们将探索生成式人工智能,这是一种使用先进的机器学习算法生成合成(但惊人逼真)数据的尖端技术。生成模型一直以来都引起了跨领域研究人员的兴趣。随着机器学习和更具体地说是深度学习领域的最新进展,生成式建模在研究作品数量和它们在不同领域的应用中迅速增长。从艺术作品和音乐作曲到合成医学数据集,生成建模正在推动想象力和智能的界限。理解、实现和利用这些方法所需的思考和努力量简直令人惊叹。一些较新的方法(如 GANs)非常强大,但难以控制,使得整个学习过程既令人兴奋又令人沮丧。

使用 Python 和 TensorFlow 2 进行生成式人工智能 是我们作者和 Packt Publishing 的才华横溢团队数小时辛勤工作的结果,帮助你理解这个生成式建模领域的 深度广度狂野 空间。本书的目标是成为生成式建模空间的万花筒,并涵盖广泛的主题。本书带你走向旅程,在这个过程中,你不仅仅是读懂理论和了解基础知识,还通过示例发现了这些模型的潜力和影响。我们将使用各种开源技术来实现这些模型——Python 编程语言、用于深度神经网络开发的 TensorFlow 2 库,以及云计算资源,如 Google Colab 和 Kubeflow 项目。

对本书中各种主题、模型、架构和示例的理解将帮助你轻松探索更复杂的主题和尖端研究。

本书适合对象

使用 Python 和 TensorFlow 2 进行生成式人工智能 面向数据科学家、机器学习工程师、研究人员和对生成式建模以及将最先进的架构应用于真实数据集感兴趣的开发者。这本书也适合那些具有中级深度学习相关技能的 TensorFlow 初学者,他们希望扩展自己的知识基础。

开始阅读本书只需要基本的 Python 和深度学习技能。

本书内容包括

第一章生成式人工智能简介:从模型中“提取”数据,介绍了生成式人工智能领域,从概率论基础到最近的这些方法的应用产品。

第二章建立一个 TensorFlow 实验室,描述了如何使用开源工具——Python、Docker、Kubernetes 和 Kubeflow——为使用 TensorFlow 开发生成式人工智能模型设置计算环境,以便在云中运行可扩展的代码实验室。

第三章深度神经网络的构建模块,介绍了深度神经网络的基础概念,这些概念将在本卷的其余部分中被利用——它们是如何受生物研究启发的,研究人员在开发越来越大规模和复杂模型时克服了哪些挑战,以及网络架构、优化器和正则化器的各种构建模块,这些构建模块在本书其余部分的生成式人工智能示例中被利用。

第四章教网络生成数字,演示了如何实现深度置信网络,这是一种突破性的神经网络架构,通过生成式人工智能方法在分类手写数字图像方面取得了最先进的结果,这种方法教会网络在学习对其进行分类之前生成图像。

第五章使用 VAE 用神经网络绘制图片,描述了变分自动编码器(VAEs),这是从深度置信网络发展而来的一种先进技术,通过巧妙地使用贝叶斯统计学中的客观函数来创建复杂对象的更清晰图像。读者将实现一个基本和高级的 VAE,后者利用了逆自回归流(IAF),这是一种递归变换,可以将随机数映射到复杂的数据分布以创建引人注目的合成图像。

第六章使用 GAN 进行图像生成,介绍了生成对抗网络,或 GANs,作为强大的生成建模深度学习架构。从 GANs 的基本构建模块和其他基本概念开始,本章介绍了许多 GAN 架构以及它们如何用于从随机噪声生成高分辨率图像。

第七章使用 GAN 进行风格转移,专注于生成建模的创造性应用,特别是称为风格转移的 GAN。应用例如将黑白图像转换为彩色图像,航拍地图转换为类似谷歌地图的输出,以及去除背景,都可以通过风格转移实现。我们涵盖了许多成对和非成对的架构,如 pix2pix 和 CycleGAN。

第八章使用 GAN 进行深度伪造,介绍了 GAN 的一个有趣且有争议的应用,称为深度伪造。该章节讨论了深度伪造的基本构建模块,例如特征和不同的操作模式,以及一些关键架构。它还包括了一些实际示例,以基于所涵盖的关键概念生成虚假照片和视频,这样读者就可以创建自己的深度伪造流水线。

第九章文本生成方法的兴起,介绍了与文本生成任务相关的概念和技术。我们从深度学习模型中不同的文本向量表示方式入手,讲述了语言生成的基本知识。然后我们介绍了不同的架构选择和解码机制,以实现高质量的输出。本章为后续更复杂的文本生成方法奠定了基础。

第十章NLP 2.0:使用 Transformer 生成文本,介绍了 NLP 领域最新最先进的技术,重点介绍了一些基于 Transformer 架构(如 GPT-x)的最先进的文本生成能力,以及它们如何彻底改变了语言生成和 NLP 领域。

第十一章使用生成模型创作音乐,介绍了使用生成模型创作音乐。这是一种有趣但具有挑战性的生成模型应用,涉及理解与音乐相关的许多细微差别和概念。本章涵盖了许多不同的生成音乐的方法,从基本的 LSTMs 到简单的 GANs,最终到用于多声部音乐生成的 MuseGAN。

第十二章使用生成式 AI 玩游戏:GAIL,描述了生成式 AI 和强化学习之间的联系,强化学习是一种机器学习的分支,教授“代理”在执行指定任务时在真实或虚拟“环境”中导航。通过 GAN 和强化学习之间的联系,读者将通过模仿跳跃运动的专家示例,教会一个跳跃形象在 3D 环境中导航。

第十三章生成式 AI 的新兴应用,描述了最近在生成式 AI 领域的研究,涵盖了生物技术、流体力学、视频和文本合成等各个方面。

要最大限度地发挥本书的效益

要跟上本书的代码,请推荐以下要求:

  • 硬件(用于本地计算):

    • 128GB 硬盘

    • 8GB 内存

    • Intel Core i5 处理器或更高版本

    • NVIDIA 8GB 及以上显卡(GTX1070 及以上)

  • 软件:

    • Python 3.6 及以上版本

    • TensorFlow 2.x

    • Chrome/Safari/Firefox 浏览器(如果在云中训练,则通过 Google Colab 或 Kubeflow 直接执行代码)

下载示例代码文件

本书的代码包托管在 GitHub 上,网址为 github.com/PacktPublishing/Hands-On-Generative-AI-with-Python-and-TensorFlow-2。我们还有其他书籍和视频的代码包,请访问github.com/PacktPublishing/。一并查看吧!

下载彩色图片

我们还提供了该书中所使用的屏幕截图/图表的彩色图像的 PDF 文件。您可以在此处下载:static.packt-cdn.com/downloads/9781800200883_ColorImages.pdf

使用的约定

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

CodeInText:表示文本中的代码词,数据库表名,文件夹名,文件名,文件扩展名,路径名,虚拟 URL,用户输入和 Twitter 用户名。例如:"我们可以使用show_examples()函数直观地绘制一些示例。"

一段代码块设置如下:

def cd_update(self, x):
    with tf.GradientTape(watch_accessed_variables=False) as g:
        h_sample = self.sample_h(x)
        for step in range(self.cd_steps):
            v_sample = tf.constant(self.sample_v(h_sample))
            h_sample = self.sample_h(v_sample) 

当我们希望引起您对代码块特定部分的注意时,相关行或项目将用粗体显示:

def cd_update(self, x):
    with tf.GradientTape(watch_accessed_variables=False) as g:
        h_sample = self.sample_h(x)
        **for** **step** **in****range****(self.cd_steps):**
            v_sample = tf.constant(self.sample_v(h_sample))
            h_sample = self.sample_h(v_sample) 

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

pip install tensorflow-datasets 

粗体:表示一个新术语,一个重要词汇,或者您在屏幕上看到的单词,在菜单或对话框中也会以这种方式呈现在文本中。例如:"从管理面板中选择系统信息"。

警告或重要说明会出现在此处。

提示和技巧会出现在此处。

联系我们

我们的读者的反馈总是受欢迎的。

一般反馈:如果您对本书的任何方面有疑问,请在消息主题中提及书名,发送电子邮件至customercare@packtpub.com

勘误:尽管我们已尽一切努力确保内容的准确性,但错误是无法避免的。如果您在本书中发现了错误,我们将不胜感激。请访问www.packtpub.com/support/errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。

盗版:如果您在互联网上发现我们作品的任何非法复制形式,我们将不胜感激,如果您提供给我们位置地址或网站名称。请联系我们,发送邮件至copyright@packtpub.com并附上链接到该材料的链接。

如果您有兴趣成为作者:如果您在某个专题上有专业知识,并且有兴趣撰写或为一本书作出贡献,请访问authors.packtpub.com

评论

请留下评论。一旦您阅读并使用了本书,为什么不在您购买的网站上留下评论呢?潜在读者可以看到并使用您的客观意见来做出购买决定,我们在 Packt 可以了解到您对我们产品的看法,我们的作者也能看到您对他们书籍的反馈。谢谢!

有关 Packt 的更多信息,请访问packtpub.com

第一章:生成人工智能简介:“从模型中“勾画”数据

在本章中,我们将深入探讨生成模型的各种应用。在此之前,我们将退后一步,详细研究生成模型与其他类型的机器学习的区别。区别在于任何机器学习算法的基本单位:概率以及我们用数学量化我们遇到的数据的形状和分布的各种方法。

在本章的其余部分,我们将 covers:

  • AI 的应用

  • 判别和生成模型

  • 实施生成模型

  • 概率的规则

  • 为什么使用生成模型?

  • 生成模型的独特挑战

AI 的应用

在 2018 年 10 月的纽约,国际拍卖行佳士得在印刷品与多重品展销会上以 43.25 万美元的价格出售了埃德蒙·贝拉米的肖像(图 1.1)。这次销售之所以引人注目,既因为销售价格比这件作品的初步估计高出 45 倍,也因为这幅肖像的非同寻常的起源。与佳士得自 18 世纪以来出售的大多数其他艺术品不同,埃德蒙·贝拉米的肖像既不是用油画或水彩画完成的,其创作者甚至不是人类;相反,它是由一个复杂的机器学习算法完全产生的数字图像。创作者——一个名为 Obvious 的巴黎集体利用了从 14 世纪到 20 世纪创作的 1.5 万幅肖像的集合来调整一个能够生成美学上类似但合成的图像的人工神经网络模型。

图 1.1:埃德蒙·贝拉米的肖像¹

画像远非唯一一个展示机器学习惊人成果的领域。事实上,如果你在过去几年关注新闻,你可能已经看到许多关于现代 AI 系统应用于各种问题的开创性成果的故事,从硬科学到数字艺术。像 Obvious 创建的深度神经网络模型现在可以在训练有素的医生水平上对人体解剖的 X 光图像进行分类,²在传统棋类游戏如围棋(一种类似国际象棋的亚洲游戏)³和多人游戏⁴中战胜人类大师,并且对法语翻译成英语时对语法细微差别表现出惊人的敏感性。⁵

判别模型和生成模型

这些人工智能的其他示例与生成埃德蒙·贝拉米的肖像的模型在一个重要的方面有所不同。在所有这些其他应用中,模型被呈现一组输入数据,例如英文文本、X 射线图像或游戏棋盘上的位置,这些数据与目标输出配对,例如翻译句子中的下一个词、X 射线图的诊断分类或游戏中的下一步。事实上,这可能是您在以往预测建模经验中最熟悉的 AI 模型类型;它们被广泛称为判别模型,其目的是在一组输入变量和目标输出之间创建映射。目标输出可以是一组离散类别(例如在翻译中下一个出现的英文单词),也可以是连续结果(例如预期一个客户在接下来的 12 个月内在在线商店中的消费金额)。

应该注意的是,这种模型,其中数据被标记评分,仅代表了现代机器学习能力的一半。另一类算法,比如生成了在佳士得拍卖会上出售的人造肖像的算法,不会从输入变量中计算得分或标签,而是生成新数据。与判别模型不同,输入变量通常是与现实世界值无关的数字向量,甚至经常是随机生成的。这种模型——被称为生成模型——可以从随机噪声中产生复杂的输出,例如文本、音乐或图像,并且是本书的主题。

即使您当时不知道,您可能也曾在新闻中看到其他生成模型的实例,与前面提到的判别示例并列。一个突出的例子是深度伪造,这是一种视频,其中一个人的脸被系统地替换为另一个人的脸,通过使用神经网络重新映射像素。⁶

图 1.2:一个深度伪造图像⁷

也许您还看到过关于 AI 模型生成虚假新闻的报道,最初由 OpenAI 公司的科学家因担心其可能被用于在线制造宣传和误导而感到恐慌。⁸

图 1.3:使用 GPT-2 创建的聊天机器人对话⁹

在这些以及其他应用中,例如谷歌的语音助手 Duplex,它可以通过与人类实时动态创建对话来进行餐厅预订¹⁰,或者可以生成原创音乐作品的软件¹¹,我们被环绕着生成式人工智能算法的输出。

图 1.4:使用生成对抗网络(GANs)进行风格迁移的示例¹²

这些模型能够处理各种领域的复杂信息:创建逼真的图像或照片上的滤镜 (图 1.4),合成声音,对话文本,甚至是最佳玩视频游戏的规则。你可能会问,这些模型从哪里来?我如何能自己实现它们?我们将在下一节中更多地讨论这个问题。

实现生成模型

尽管生成模型理论上可以使用各种机器学习算法来实现,但在实践中,它们通常是通过深度神经网络构建的,这些网络非常适合捕捉图像或语言等数据的复杂变化。

在本书中,我们将专注于使用TensorFlow 2.0来实现这些深度生成模型的许多不同应用。TensorFlow 是一个 C++框架,有 Python 编程语言的 API,用于开发和应用深度学习模型。它是谷歌在 2013 年开源的,并且已经成为研发和部署神经网络模型的最受欢迎的库之一。

随着 2.0 版的发布,以前版本中开发的那些样板代码得到了清理,使用了高级抽象层,使我们能够专注于模型而不是计算过程的其它部分。最新版本还引入了即时执行的概念,允许网络计算按需运行,这将是我们实现某些模型的重要好处。

在接下来的章节中,你将学习不仅是这些模型背后的理论,还有在流行的编程框架中实现它们所需的实际技能。在第二章建立一个 TensorFlow 实验室中,你将学习如何设置一个云环境,以便你可以使用Kubeflow框架来分布式运行 TensorFlow,并记录你的实验。

实际上,正如我将在第三章深度神经网络的构建模块中更详细地描述的那样,自 2006 年以来,大规模神经网络模型的深度学习研究已经产生了各种各样的生成模型应用。其中第一个是受限玻尔兹曼机,它被堆叠在多个层中以创建深度信念网络。我将在第四章教网络生成数字中描述这两种模型。后来的创新包括变分自动编码器VAEs),它们可以有效地从随机数生成复杂的数据样本,我将在第五章使用 VAEs 用神经网络绘制图片中描述这些技术。

我们还将在本书的第六章使用 GAN 生成图像中更详细地讨论用于创建埃德蒙·贝拉米的肖像的算法 GAN。从概念上讲,GAN 模型在两个神经网络之间创建竞争。一个(称为生成器)从一组随机数开始生成逼真的(或者在 Obvious 的实验中,艺术性的)图像,并应用数学变换。在某种意义上,生成器就像一名艺术学生,从笔触和创意灵感中创作新的绘画。

第二个网络,被称为判别器,试图分类一幅图片是否来自一组真实世界的图片,还是由生成器创建。因此,判别器就像一个老师,评分学生是否产生了与他们试图模仿的绘画相媲美的作品。随着生成器变得越来越擅长欺骗判别器,它的输出越来越接近于它被设计来复制的历史示例。

有许多类 GAN 模型,附加变体在第七章使用 GAN 进行风格迁移,和第十一章使用生成模型创作音乐,在我们讨论高级模型时涵盖。生成模型中的另一个关键创新是在自然语言数据领域。通过以一种计算可扩展的方式代表句子中单词之间的复杂相互关系,Transformer 网络及其基于其之上构建的双向编码器 TransformerBERT)模型呈现了在应用中生成文本数据的强大构建模块,如聊天机器人,在第九章文本生成方法的崛起,和第十章NLP 2.0:使用 Transformer 生成文本中我们将更详细地讨论。

第十二章使用生成式 AI 玩视频游戏:GAIL 中,您还将看到诸如 GAN 和 VAE 等模型如何被用于生成不仅仅是图像或文本,而是一组允许使用强化学习算法开发的游戏网络更高效地处理和导航其环境的规则集——本质上,学会学习。生成模型是一个不断增长的巨大研究领域,所以遗憾的是,我们无法在本书中涵盖每一个主题。对于感兴趣的读者,进一步主题的参考资料将在第十三章生成式 AI 的新兴应用中提供。

要开始一些背景信息,让我们讨论一下概率规则。

概率规则

在最简单的层面上,模型,无论是用于机器学习还是更经典的方法,如线性回归,都是关于各种数据如何相互关联的数学描述。

在建模任务中,我们通常考虑将数据集的变量分成两大类:

  1. 独立数据,主要是指模型的 输入,用 X 表示。这些可以是分类特征(例如某学生所在的六所学校中的“0”或“1”),连续的(例如相同学生的身高或考试成绩),或序数的(班级中学生的排名)。

  2. 依赖数据 相反,则是我们模型的输出,用 Y 表示。(请注意,在某些情况下,Y 是一个 标签,可用于条件产生输出,例如在条件 GAN 中。)与自变量一样,这些可以是连续的、分类的或序数的,它们可以是数据集每个元素的个体元素或多维矩阵(张量)。

那么如何使用统计描述我们模型中的数据呢?换句话说,我们如何定量描述我们可能看到的值,以及频率如何,哪些值更有可能一起出现?一种方式是询问数据中观察特定值的可能性,或者该值的概率。例如,如果我们想知道掷六面骰子出现 4 的概率是多少,答案是平均来说,我们会在六次掷骰子中看到一次 4。我们写为:

P(X=4) = = 16.67%

其中 P 表示 概率为

什么定义了特定数据集的允许概率值?如果我们想象数据集所有可能值的集合,比如掷骰子的所有值,那么概率将每个值映射到 0 和 1 之间的数。最小值是 0,因为我们不可能有看到结果的负概率;最不可能的结果是我们永远不会看到特定值的机会,或者是 0%的概率,比如在六面骰子上掷出 7。同样,我们也不可能有大于 100%的观察结果概率,其值为 1;概率为 1 的结果是绝对确定的。与数据集相关联的这组概率值属于离散类(例如骰子的面)或无限潜在值的集合(例如身高或体重的变化)。不过,在任一情况下,这些值必须遵循某些规则,这些规则是由数学家安德烈·科尔莫戈洛夫在 1933 年描述的 概率公理

  1. 观察的概率(掷骰子点数、特定身高等)是 0 到 1 之间的非负有限数。

  2. 在所有可能观察空间中至少出现一项观察结果的概率是 1。

  3. 不同、互斥事件的联合概率是各个事件概率的总和。

尽管这些规则可能看起来抽象,但你将会在第三章深度神经网络的基础组件中看到,它们与开发神经网络模型直接相关。例如,规则 1 的一个应用是在预测目标类别的softmax函数中生成介于 1 和 0 之间的概率。规则 3 用于将这些结果归一化到 0-1 范围内,保证它们是深度神经网络的相互独立的预测(换句话说,实际世界中的图像逻辑上不能同时被分类为狗和猫,而只能是狗或猫,这两个结果的概率是可加的)。最后,第二条规则提供了我们可以使用这些模型生成数据的理论保证。

然而,在机器学习和建模的背景下,我们通常不只是对观察到一条输入数据的概率X感兴趣;我们更想知道,基于这些数据,Y条件概率是多少。换句话说,我们想知道基于数据对一组数据的标签有多大可能性。我们将此表示为给定 X 的 Y 的概率,或者给定 X 条件下的 Y 的概率

P(Y|X)

我们还可以询问关于YX的另一个问题,即它们一起发生或它们的联合概率有多大,这可以使用前面的条件概率表达式表示如下:

P(X, Y) = P(Y|X)P(X) = P(X|Y)(Y)

此公式表示X 和 Y 的概率。在XY完全独立的情况下,这就是它们的乘积:

P(X|Y)P(Y) = P(Y|X)P(X) = P(X)P(Y)

你会发现,这些表达式在我们讨论第四章教授网络生成数字中的补充先验受限玻尔兹曼机模拟独立数据样本的能力中变得重要。它们也是贝叶斯定理的构建模块,我们将在下一节中讨论。

判别建模和生成建模以及贝叶斯定理

现在让我们考虑这些条件和联合概率规则如何与我们为各种机器学习应用构建的预测模型相关联。在大多数情况下——比如预测电子邮件是否欺诈性或客户未来生命周期价值的美元金额——我们对条件概率P(Y|X=x)感兴趣,其中Y是我们试图建模的结果集,X表示输入特征,x是输入特征的特定值。如前所述,这种方法被称为判别建模。¹⁴判别建模试图学习数据X与结果Y之间的直接映射。

另一种理解判别建模的方法是在贝叶斯定理的背景下,¹⁵它关联了数据集的条件和联合概率:

P(Y|X) = P(X|Y)P(Y)/P(X) = P(X, Y)/P(X)

在贝叶斯公式中,表达式P(X|Y)/P(X)被称为似然或观察到的X给出Y观察概率的支持证据。P(Y)先验或结果的合理性,P(Y|X)后验或给出到目前为止与结果相关的所有独立数据的观察概率。概念上,贝叶斯定理表明结果的概率是其基线概率与此结果的输入数据条件概率的乘积。

这个定理是作者去世两年后发表的,在前言中理查德·普赖斯描述它为上帝存在的一个数学论证,这或许是适当的,因为托马斯·贝叶斯在生前是一位牧师。

在判别学习的背景下,我们可以看到判别模型直接计算后验概率;我们可以有似然或先验模型,但在这种方法中并不需要。即使你可能没有意识到,你可能在机器学习工具包中使用的大多数模型都是判别模型,例如以下模型:

  • 线性回归

  • 逻辑回归

  • 随机森林

  • 梯度提升决策树(GBDT)

  • 支持向量机(SVM)

前两种(线性回归和逻辑回归)是在数据X的条件下模型结果Y,使用正态或高斯函数(线性回归)或 S 型函数(逻辑回归)。相比之下,最后三种没有正式的概率模型—它们计算将X映射到Y的函数(一组树用于随机森林或 GDBT,或者 SVM 的内积分布),使用损失或错误函数来调整这些估计值。鉴于这种非参数性质,一些作者认为这构成了一类非模型判别算法。(16)

相比之下,生成模型试图学习标签和输入数据的联合分布P(Y, X)。回想一下通过联合概率的定义:

P(X, Y) = P(X|Y)P(Y)

我们可以将贝叶斯定理重写如下:

P(Y|X) = P(X, Y)/P(X)

与判别情况不同,我们的目标不是使用P(Y|X)直接映射XY,而是模拟XY的联合概率P(X, Y)。虽然我们可以使用XY的联合分布来计算后验概率P(Y|X)并学习一个目标模型,但我们也可以使用此分布来通过联合采样新的元组(x, y)或使用目标标签Y采样新的数据输入,使用以下表达式:

P(X|Y=y) = P(X, Y)/P(Y)

生成模型的例子包括以下内容:

  • 朴素贝叶斯分类器

  • 高斯混合模型

  • 潜在狄利克雷分配(LDA)

  • 隐马尔可夫模型

  • 深度波尔兹曼机

  • 变分自动编码器(VAEs)

  • 生成对抗网络(GANs)

朴素贝叶斯分类器,虽然被称为判别模型,但利用贝叶斯定理来学习XY的联合分布,假设X变量是独立的。同样,高斯混合模型描述了数据点属于一组正态分布之一的可能性,使用标签和这些分布的联合概率。

LDA 将文档表示为单词和一组潜在关键字列表(主题)的联合概率,这些关键字列表在文档中使用。隐马尔可夫模型表示数据的状态和下一个状态的联合概率,例如一周中连续几天的天气。正如你将在第四章中看到的,教网络生成数字,深度波尔兹曼机学习标签和与之相关的数据向量的联合概率。我们将在第 5、6、7 和 11 章中涵盖的 VAE 和 GAN 模型也利用联合分布来映射复杂的数据类型。这种映射允许我们从随机向量生成数据,或者将一种数据转换为另一种。

如前所述,生成模型的另一个观点是,如果我们知道一个结果Y,它们允许我们生成X的样本。在前述列表中的前四个模型中,这种条件概率只是模型公式的一个组成部分,而后验估计仍然是最终目标。然而,在最后三个示例中,它们都是深度神经网络模型,学习关于一个隐藏或潜在变量ZX的条件是实际上的主要目标,以便生成新的数据样本。利用多层神经网络所允许的丰富结构,这些模型可以近似表示复杂数据类型的分布,如图像、自然语言和声音。此外,Z不再是目标值,而在这些应用中通常是一个随机数,仅用作从中生成大量假设数据点的输入。在我们有标签的程度上(比如一个生成的图像应该是狗还是海豚,或者一个生成的歌曲的流派),模型就是P(X|Y=y, Z=z),其中标签Y控制着除了Z的随机性外其他数据的生成。

为什么使用生成模型?

现在我们已经回顾了生成模型的内容,并在概率语言中更正式地定义了它们,为什么我们首先需要这样的模型呢?它们在实际应用中提供了什么价值?为了回答这个问题,让我们简要地浏览一下我们将在本书的其余部分更详细地讨论的主题。

深度学习的承诺

正如已经指出的,我们将在本书中调查的许多模型都是深度的、多级的神经网络。过去 15 年来,深度学习模型在图像分类、自然语言处理和理解以及强化学习方面取得了复兴。这些进展是由于在调整和优化非常复杂的模型方面的传统挑战的突破,再加上对更大的数据集、分布式计算能力的访问以及诸如 TensorFlow 这样的框架,使得原型设计和复制研究变得更加容易。

构建更好的数字分类器

用于在机器学习和计算机视觉中基准算法的一个经典问题是对来自 MNIST 数据集的像素化图像中表示的 0-9 之间的哪个手写数字进行分类的任务。¹⁷ 在这个问题上取得的一个重大突破发生在 2006 年,当时多伦多大学和新加坡国立大学的研究人员发现了一种训练深度神经网络执行此任务的方法。¹⁸

他们的一个关键观察是,与其训练一个网络直接预测给定图像(X)的最可能的数字(Y),不如首先训练一个可以生成图像的网络,然后作为第二步对它们进行分类。在第四章教网络生成数字中,我将描述这个模型是如何改进过去的尝试的,并且如何创建自己的受限玻尔兹曼机深度玻尔兹曼机模型,这些模型可以生成新的 MNIST 数字图像。

生成图像

使用 MNIST 数据集的方法生成像Edmond Belamy 的肖像这样的图像的一个挑战是,图像通常没有标签(如数字);相反,我们想要使用一个潜在向量Z将随机数空间映射到一组人工图像,就像我在本章中早些时候描述的那样。

另一个限制是我们希望促进这些图像的多样性。如果我们输入在某个范围内的数字,我们希望知道它们生成不同的输出,并且能够调整生成的图像特征。为此,VAE 被开发出来生成多样化和逼真的图像(图 1.5)。

图 1.5:来自 VAE 的样本图像¹⁹

在图像分类任务的背景下,能够生成新图像可以帮助我们增加现有数据集中的示例数量,或者如果我们现有数据集严重偏向于特定类型的照片,则减少偏差。应用可能包括为时尚电子商务网站的产品照片生成替代姿势(角度、色调或透视镜头)(图 1.6):

图 1.6:使用深度生成模型模拟替代姿势²⁰

风格转移和图像变换

除了将人工图像映射到随机数空间之外,我们还可以使用生成模型学习将一种图像映射到另一种图像。这种模型可以用于将一幅马的图像转换为斑马的图像(图 1.7),创建深度伪造视频,其中一个演员的脸被另一个演员的脸替换,或将照片转换为绘画(图 1.21.4):²¹

图 1.7:CycleGAN 将条纹应用于马身上以生成斑马²²

应用生成建模的另一个引人注目的例子是一项研究,该研究发现了艺术家巴勃罗·毕加索的失落杰作被另一幅图像所覆盖的情况。在对《老吉他手》《蹲着的乞丐》进行 X 射线成像后,显示了一个女人和一个风景的早期图像位于其下面(图 1.8),研究人员使用了毕加索蓝色时期的其他绘画或其他彩色照片(图 1.8)来训练神经风格转移模型,该模型将黑白图像(覆盖绘画的 X 射线放射图)转换为原始艺术作品的着色。然后,将这种转换模型应用于隐藏图像使他们能够重建着色版本的丢失绘画:

图 1.8:深度学习被用于给绘画场景的 X 射线图着色(中),通过从示例中学习颜色模式(列 d)生成失落艺术品的着色版本(极右)²³

所有这些模型都使用了之前提到的 GAN,这是一种在 2014 年提出的深度学习模型²⁴。除了改变图像的内容(如前面斑马的例子中),这些模型还可以用于将一幅图像映射到另一幅图像,比如成对的图像(例如具有相似面部特征的狗和人,如图 1.9),或者从图像生成文本描述(图 1.10):

图 1.9:Sim-GAN 用于将人类映射到动物或动漫脸部²⁵

图 1.10:Caption-GAN 用于从图像生成描述²⁶

我们还可以根据一些辅助信息如标签来调节生成图像的属性,这是 GAN-Gogh 算法中采用的方法,它通过将期望的艺术家作为输入提供给生成模型来合成不同艺术家风格的图像(图 1.4)。²⁷我将在第六章使用 GAN 生成图像第七章使用 GAN 进行风格转移中描述这些应用。

虚假新闻和聊天机器人

人类一直想要与机器交谈;第一个聊天机器人,ELIZA,²⁸是在 1960 年代在麻省理工学院编写的,它使用一个简单的程序来转换用户的输入并生成一个回应,以心理治疗师的方式频繁地以问题形式回应。

更复杂的模型可以生成全新的文本,例如谷歌的 BERT 和 GPT-2,^(29 30)它们使用一种称为Transformers的单元。神经网络中的Transformers模块允许网络在文本中的前导词汇上提出新词汇,在生成合理的语言片段时强调更相关的词汇。然后,BERT 模型将Transformers单元结合成自然语言模式和上下文重要性的强大多维编码。此方法可用于自然语言处理NLP)任务的文档创建,或用于聊天机器人对话系统(图 1.3)。

声音合成

声音,如图像或文本,是一种复杂的、高维度的数据。音乐尤其复杂:它可能涉及一个或多个音乐家,具有时间结构,并且可以分为主题相关的段落。所有这些组成部分都被纳入到先前提到的模型中,比如 MuseGAN,该模型使用 GAN 生成这些各种组件,并将它们合成为逼真但合成的音乐曲目。我将在第十一章“用生成模型创作音乐”中描述 MuseGAN 及其变种的实施。

游戏规则

前述应用涉及我们可以看到、听到或阅读的数据类型。然而,生成模型还可以用于生成规则。这在深度学习的一种流行应用中很有用:使用算法玩棋盘游戏或 Atari 视频游戏。³¹

虽然这些应用传统上使用强化学习RL)技术来训练网络以在这些游戏中采用最佳策略,但新的研究表明使用 GAN 来提出新规则作为训练过程的一部分,³²或生成合成数据来激发整体学习过程。³³我们将在第十二章“用生成 AI 玩视频游戏:GAIL”中研究这两种应用。

生成模型的独特挑战

鉴于生成模型具有的强大应用,实施它们的主要挑战是什么?正如所述,这些模型大多利用复杂数据,需要我们拟合大型模型来捕捉其特征和分布的所有细微差别。这对于我们必须收集的示例数量以充分代表我们试图生成的数据类型,以及构建模型所需的计算资源都有影响。我们将在第二章“设置 TensorFlow 实验室”中讨论使用云计算框架和图形处理单元GPU)并行训练这些模型的技术。

由于数据复杂性及我们试图生成数据而不是数字标签或值的事实,我们对模型准确度的概念变得更加复杂:我们不能简单地计算到单个标签或分数的距离。

我们将在第五章使用 VAE 进行神经网络绘图,以及第六章使用 GAN 进行图像生成中讨论深度生成模型(如 VAE 和 GAN 算法)采用不同方法来确定生成图像是否与真实世界图像可比。最后,正如提到的,我们的模型需要允许我们生成大量和多样化的样本,而我们将讨论的各种方法采用不同的方法来控制数据的多样性。

摘要

在本章中,我们讨论了生成建模是什么,以及它如何适应更熟悉的机器学习方法的格局。我使用概率论和贝叶斯定理来描述这些模型如何以与判别式学习相反的方式进行预测。

我们审查了生成式学习的用例,既针对特定类型的数据,也针对一般性的预测任务。最后,我们还研究了构建这些模型所面临的一些专门挑战。

在下一章中,我们将通过探索如何使用 Docker 和 Kubeflow 为 TensorFlow 2.0 设置开发环境,开始实际实现这些模型。

参考文献

  1. www.christies.com/features/A-collaboration-between-two-artists-one-human-one-a-machine-9332-1.aspx

  2. Baltruschat, I.M., Nickisch, H., Grass, M.等(2019)。多标签胸部 X 射线分类的深度学习方法比较。Sci Rep 9, 6381。doi.org/10.1038/s41598-019-42294-8

  3. AlphaGo(无日期)。DeepMind。2021 年 4 月 20 日检索自deepmind.com/research/case-studies/alphago-the-story-so-far

  4. AlphaStar 团队(2019 年 10 月)。AlphaStar:使用多智能体强化学习在《星际争霸 II》中达到大师级水平。DeepMind。deepmind.com/blog/article/AlphaStar-Grandmaster-level-in-StarCraft-II-using-multi-agent-reinforcement-learning

  5. Devlin, J., Chang, M., Lee, K., Toutanova, K.(2019)。BERT:用于语言理解的深度双向转换器的预训练。arXiv。arxiv.org/abs/1810.04805v2

  6. Brandon, J.(2018 年 2 月 16 日)。可怕的高科技色情:令人毛骨悚然的“深度假视频”正在兴起。福克斯新闻。www.foxnews.com/tech/terrifying-high-tech-porn-creepy-deepfake-videos-are-on-the-rise

  7. seanbmcgregor.com/DeepfakeDetectionGame.html

  8. 更好的语言模型及其影响。(2019 年 2 月 14 日)。OpenAI。openai.com/blog/better-language-models/

  9. devopstar.com/static/2293f764e1538f357dd1c63035ab25b0/d024a/fake-facebook-conversation-example-1.png

  10. Leviathan Y., Matias Y. (2018 年 5 月 8 日)。Google Duplex:用于电话实现真实世界任务的 AI 系统。Google AI 博客。 ai.googleblog.com/2018/05/duplex-ai-system-for-natural-conversation.html

  11. Hao-Wen Dong, Wen-Yi Hsiao, Li-Chia Yang 和 Yi-Hsuan Yang。MuseGANsalu133445.github.io/musegan/

  12. neurohive.io/wp-content/uploads/2018/06/neural-style-transfer-example-e1530287419338.jpg

  13. Kolmogorov A. N., (1956). 概率论基础.(第 2 版)。纽约:切尔西出版公司。 www.york.ac.uk/depts/maths/histstat/kolmogorov_foundations

  14. Jebara, Tony., (2004). 机器学习:判别式与生成式。Kluwer Academic (Springer)。www.springer.com/gp/book/9781402076473

  15. Bayes Thomas, (1763) LII. 解决机会命理学问题的尝试。由已故牧师 Bayes 先生 F.R.S.与约翰坎顿先生的信交流。R. Soc.53370–418. royalsocietypublishing.org/doi/10.1098/rstl.1763.0053

  16. Jebara, Tony., (2004). 机器学习:判别式与生成式。Kluwer Academic (Springer)。www.springer.com/gp/book/9781402076473

  17. yann.lecun.com/exdb/mnist/

  18. G. Hinton, S. Osindero, & Y.-W. Teh. (2005). 用于深度信念网络的快速学习算法www.cs.toronto.edu/~fritz/absps/ncfast.pdf

  19. jaan.io/images/variational-autoencoder-faces.jpgmiro.medium.com/max/2880/1*jcCjbdnN4uEowuHfBoqITQ.jpeg

  20. Esser, P., Haux, J., Ommer, B., (2019). 用于图像合成的无监督稳健潜在特征分解. arXiv。arxiv.org/abs/1910.10223

  21. CycleGAN。TensorFlow Core。2021 年 4 月 26 日检索自 www.tensorflow.org/tutorials/generative/cyclegan

  22. www.tensorflow.org/tutorials/generative/images/horse2zebra_2.png

  23. Bourached, A., Cann, G. (2019). 失落的艺术品. arXiv:1909.05677. arxiv.org/pdf/1909.05677.pdf

  24. Goodfellow, I. J., Pouget-Abadie, J., Mirza, M., Xu, B., Warde-Farley, D., Ozair, S., Courville, A., Bengio, Y. (2014). 生成对抗网络. arXiv. arxiv.org/abs/1406.2661

  25. Goodfellow, I. J., Pouget-Abadie, J., Mirza, M., Xu, B., Warde-Farley, D., Ozair, S., Courville, A., Bengio, Y. (2014). 生成对抗网络. arXiv. arxiv.org/abs/1406.2661

  26. Gorti, S. K., Ma, Jeremy (2018). 使用循环一致性对抗网络的文本-图像-文本翻译. arXiv. arxiv.org/abs/1808.04538

  27. rkjones4, adam-hanna, erincr & rodrigobdz (2020). GANGogh. GitHub 代码库. github.com/rkjones4/GANGogh

  28. Weizenbaum Joseph. (1976) 计算机与人类的思维. W. H. Freeman and company. blogs.evergreen.edu/cpat/files/2013/05/Computer-Power-and-Human-Reason.pdf

  29. Schwartz B., (2019, October 25). 欢迎 BERT:谷歌最新的搜索算法,以更好地理解自然语言. Search Engine Land. searchengineland.com/welcome-bert-google-artificial-intelligence-for-understanding-search-queries-323976

  30. 更好的语言模型及其影响. (2019, February 14). OpenAI. openai.com/blog/better-language-models/

  31. Mnih V., Kavukcuoglu K., Silver D., Graves A., Antonoglou I., Wierstra D., Riedmiller M. (2013, January 01). 使用深度强化学习玩 Atari 游戏. DeepMind. deepmind.com/research/publications/playing-atari-deep-reinforcement-learning

  32. Liu, Yang; Zeng, Yifeng; Chen, Yingke; Tang, Jing; Pan, Yinghui (2019). 自我改进的生成对抗强化学习. AAMS 2019. www.ifaamas.org/Proceedings/aamas2019/pdfs/p52.pdf

  33. Kasgari, A T Z, Saad, W., Mozaffari, M., Poor, H V (2020). 经验丰富的深度生成对抗强化学习用于无模型超可靠低延迟通信. arXiv. arxiv.org/abs/1911.03264

第二章:设置 TensorFlow 实验室

现在你已经在 第一章 中看到了生成模型的所有令人惊叹的应用,你可能想知道如何开始实施这些使用这些算法的项目。在本章中,我们将介绍一些工具,这些工具将在本书的其余部分中用于实现各种生成式 AI 模型中使用的深度神经网络。我们的主要工具是由 Google^(1 2) 开发的 TensorFlow 2.0 框架;然而,我们还将使用其他一些资源来简化实现过程(在 2.1 表格 中总结)。

我们可以广泛分类这些工具:

  • 用于可复制的依赖管理的资源(Docker,Anaconda)

  • 数据整理和算法开发的探索性工具(Jupyter)

  • 部署这些资源到云端并管理其生命周期的工具(Kubernetes,Kubeflow,Terraform)

工具 项目网址 用途
Docker www.docker.com/ 应用程序运行时依赖封装
Anaconda www.anaconda.com/ Python 语言包管理
Jupyter jupyter.org/ 交互式 Python 运行时和绘图/数据探索工具
Kubernetes kubernetes.io/ Docker 容器编排和资源管理
Kubeflow www.kubeflow.org/ 基于 Kubernetes 开发的机器学习工作流引擎
Terraform www.terraform.io/ 用于可配置和一致部署 Kubeflow 和 Kubernetes 的基础设施脚本语言
VSCode code.visualstudio.com/ 集成开发环境(IDE)

表格 2.1:用于生成对抗模型开发的技术栈

在本章中,我们将在我们将我们的代码从笔记本电脑移到云端的旅程中,首先描述一些有关 TensorFlow 在本地运行时的背景知识。然后,我们将描述一系列软件工具,这些工具将使在本地或云端运行全面的 TensorFlow 实验更加容易,如笔记本、容器和集群管理器等。最后,我们将通过一个简单的实例来介绍如何建立一个可重现的研究环境,运行本地和分布式训练,并记录我们的结果。我们还将探讨如何在一台机器内的多个 CPU/GPU 单元(纵向扩展)和云端的多台机器(横向扩展)上并行化 TensorFlow 以加速训练。通过本章的学习,我们将准备好扩展这个实验室框架,用各种生成式 AI 模型来实施项目。

首先,让我们深入了解 TensorFlow 的细节,这是本书剩余部分将用于开发模型的库。 TensorFlow 解决了神经网络模型开发中的哪些问题?它采用了哪些方法?它在多年来如何发展?为了回答这些问题,让我们回顾一些促成 TensorFlow 发展的深度神经网络库的历史。

深度神经网络的发展和 TensorFlow

正如我们将在 第三章 深度神经网络的构建模块 中看到的那样,深度神经网络本质上由矩阵运算(加法、减法、乘法)、非线性变换和使用这些组件的导数进行的基于梯度的更新组成。

在学术界,研究人员通常使用诸如 MATLAB³ 这样的高效原型工具来运行模型和准备分析。虽然这种方法允许进行快速实验,但它缺乏工业软件开发的元素,例如 面向对象OO)开发,它可以实现可重现性和干净的软件抽象,从而使工具可以被大型组织所采用。这些工具对于大型数据集的扩展也存在困难,并且对于此类工业用途可能会带来繁重的许可费用。然而,在 2006 年之前,这种计算工具在大多数情况下仍然足够。然而,随着应用深度神经网络算法处理的数据集的增长,取得了突破性的成果,如:

  • ImageNet 数据集上的图像分类⁴

  • 在 YouTube 视频中大规模无监督地发现图像模式⁵

  • 创造了能够像人类一样能够玩雅达利视频游戏和围棋的人工智能代理^(6 7)

  • 通过 Google 开发的 BERT 模型实现的最先进的语言翻译⁸

在这些研究中开发的模型随着它们应用到的数据集的规模而变得复杂起来(请参阅表 2.2 以了解其中一些模型的巨大规模)。由于工业用例需要稳健且可扩展的框架来开发和部署新的神经网络,一些学术团体和大型技术公司投资于通用工具包的开发,用于实现深度学习模型。这些软件库将常见模式编码为可重用的抽象,使即使复杂的模型通常也可以体现在相对简单的实验脚本中。

模型名称 年份 # 参数
AlexNet 2012 61M
YouTube CNN 2012 1B
Inception 2014 5M
VGG-16 2014 138M
BERT 2018 340M
GPT-3 2020 175B

表 2.2:按年份分类的模型参数数量

这些框架的早期示例包括 Theano,⁹ 蒙特利尔大学开发的 Python 包,以及 Torch,¹⁰ 由 Facebook 的研究人员在 Lua 语言中编写,后来被转移到 Python,以及由 Google¹¹开发的具有 Python 绑定的 C++运行时的 TensorFlow。

在本书中,我们将主要使用 TensorFlow 2.0,因为它被广泛采用,并且它具有方便的高级界面Keras,可以抽象出大部分关于定义常规层和模型架构的重复工作。

TensorFlow 是 Google 内部称为DistBelief的工具的开源版本。¹²DistBelief 框架由分布式工作者(在一组机器上运行的独立计算过程)组成,这些工作者会对网络进行正向和反向梯度下降处理(我们将在第三章深度神经网络的构建基块中讨论训练神经网络的常见方式),并将结果发送给参数服务器进行更新。DistBelief 框架中的神经网络被表示为一个有向无环图DAG),以损失函数结束,产生与观察目标(如图像类别或代表翻译模型中句子中最可能的下一个词的词汇概率分布)进行比较的标量(数值)。

DAG 是一种软件数据结构,由节点(操作)和数据()组成,信息仅沿着边的单向流动(因此有向),而且没有循环(因此无环)。

尽管 DistBelief 允许 Google 生产出多个大型模型,但它有一些限制:

  • 首先,Python 脚本接口是使用一组对应 C++中底层实现的预定义层开发的;添加新颖的层类型需要在 C++中编码,这对生产力构成了障碍。

  • 其次,虽然该系统非常适合使用基本的随机梯度下降SGD)(我们将在第三章深度神经网络的构建基块中更详细地描述的算法)在大规模数据上训练前馈网络,但它缺乏灵活性,无法容纳循环、强化学习或对抗学习范式 —— 后者对于我们在本书中实现的许多算法贯穿全文非常重要。

  • 最后,这个系统难以缩小规模,比如在具有 GPU 的台式机和具有多个核心的分布式环境中运行相同的作业,部署还需要不同的技术栈。

这些考虑共同促使 TensorFlow 作为一种通用的深度学习计算框架的发展:一种可以让科学家灵活地尝试新的层架构或前沿的训练范式,同时还允许这种实验在笔记本电脑(用于早期工作)和计算集群(用于扩展更成熟的模型)上使用相同的工具运行,同时还通过为两者提供一个公共运行时来简化研究和开发代码之间的过渡。

尽管这两个库都共享计算图的概念(将网络表示为操作(节点)和数据(边缘)的图形)和数据流编程模型(其中矩阵操作通过图的有向边缘传递并对其应用操作),但 TensorFlow 与 DistBelief 不同,设计为图的边缘是张量(n 维矩阵),图的节点是原子操作(加法、减法、非线性卷积或队列和其他高级操作),而不是固定的层操作——这允许在定义新计算时具有更大的灵活性,甚至允许进行突变和有状态更新(这些仅是图中的附加节点)。

数据流图本质上充当一个“占位符”,其中数据被插入到定义的变量中,并可以在单台或多台机器上执行。TensorFlow 在 C++运行时优化构建的数据流图,允许优化,例如向 GPU 发送命令。图的不同计算也可以在多台机器和硬件上执行,包括 CPU、GPU 和 TPU(Google 开发的自定义张量处理芯片,在 Google Cloud 计算环境中可用)¹¹,因为在 TensorFlow 中以高层次描述的相同计算被实现为在多个后端系统上执行。

因为数据流图允许可变状态,本质上,不再像 DistBelief 那样有一个集中的参数服务器(尽管 TensorFlow 也可以以参数服务器配置的方式分布式运行),因为持有状态的不同节点可以执行与任何其他工作节点相同的操作。此外,控制流操作(例如循环)允许对可变长度输入进行训练,例如在循环网络中(参见第三章深度神经网络的构建模块)。在训练神经网络的情况下,每一层的梯度简单地表示为图中的附加操作,允许使用相同框架包含像速度这样的优化(如 RMSProp 或 ADAM 优化器,在第三章深度神经网络的构建模块中描述)而不是修改参数服务器逻辑。在分布式训练的情况下,TensorFlow 还具有几个检查点和冗余机制(“备份”工作节点以防单个任务失败),使其适用于在分布式环境中进行稳健的训练。

TensorFlow 2.0

尽管在数据流图中表示操作原语可以灵活定义 Python 客户端 API 内的新层,但也可能导致大量“样板”代码和重复的语法。出于这个原因,高级 API Keras¹⁴被开发出来提供高级抽象;层使用 Python 类表示,而特定运行环境(例如 TensorFlow 或 Theano)是执行该层的“后端”,就像原子 TensorFlow 运算符可以在 CPU、GPU 或 TPU 上具有不同的底层实现一样。尽管作为与框架无关的库开发,Keras 已包含在 TensorFlow 2.0 版本的主要发布中。为了可读性的目的,我们将在本书中大部分模型使用 Keras 来实现,而在需要实现特定操作或突出基础逻辑时,将恢复到底层的 TensorFlow 2.0 代码。请参阅表 2.3以比较这些库在低(TensorFlow)或高(Keras)级别上如何实现各种神经网络算法概念。

对象 TensorFlow 实现 Keras 实现
神经网络层 张量计算 Python 层类
梯度计算 图运行操作符 Python 优化器类
损失函数 张量计算 Python 损失函数
神经网络模型 图运行会话 Python 模型类实例

表 2.3:TensorFlow 和 Keras 比较

为了向您展示 Keras 和 TensorFlow 1.0 在实现基本神经网络模型时所做的抽象的区别,让我们看一下使用这两个框架编写卷积层的示例(请参阅第三章深度神经网络的构建块)。在第一种情况下,在 TensorFlow 1.0 中,您可以看到很多代码涉及显式指定变量、函数和矩阵操作,以及梯度函数和运行时会话来计算网络的更新。

这是 TensorFlow 1.0 中的多层感知器¹⁵:

X = tf.placeholder(dtype=tf.float64)
Y = tf.placeholder(dtype=tf.float64)
num_hidden=128
# Build a hidden layer
W_hidden = tf.Variable(np.random.randn(784, num_hidden))
b_hidden = tf.Variable(np.random.randn(num_hidden))
p_hidden = tf.nn.sigmoid( tf.add(tf.matmul(X, W_hidden), b_hidden) )
# Build another hidden layer
W_hidden2 = tf.Variable(np.random.randn(num_hidden, num_hidden))
b_hidden2 = tf.Variable(np.random.randn(num_hidden))
p_hidden2 = tf.nn.sigmoid( tf.add(tf.matmul(p_hidden, W_hidden2), b_hidden2) )
# Build the output layer
W_output = tf.Variable(np.random.randn(num_hidden, 10))
b_output = tf.Variable(np.random.randn(10))
p_output = tf.nn.softmax( tf.add(tf.matmul(p_hidden2, W_output), 
           b_output) )
loss = tf.reduce_mean(tf.losses.mean_squared_error(
        labels=Y,predictions=p_output))
accuracy=1-tf.sqrt(loss)
minimization_op = tf.train.AdamOptimizer(learning_rate=0.01).minimize(loss)
feed_dict = {
    X: x_train.reshape(-1,784),
    Y: pd.get_dummies(y_train)
}
with tf.Session() as session:
    session.run(tf.global_variables_initializer())
    for step in range(10000):
        J_value = session.run(loss, feed_dict)
        acc = session.run(accuracy, feed_dict)
        if step % 100 == 0:
            print("Step:", step, " Loss:", J_value," Accuracy:", acc)
            session.run(minimization_op, feed_dict)
    pred00 = session.run([p_output], feed_dict={X: x_test.reshape(-1,784)}) 

相比之下,Keras 中相同卷积层的实现通过使用在 Python 类中体现的抽象概念(如层、模型和优化器)大大简化了。这些类封装了计算的底层细节,使代码逻辑更易读。

还要注意,在 TensorFlow 2.0 中,运行会话的概念(惰性执行,即仅在显式编译和调用时才计算网络)已经被弃用,而采用了渴望执行的方式,在调用网络函数(如 callcompile)时动态调用会话和图,网络行为就像任何其他 Python 类一样,而无需显式创建 session 作用域。使用 tf.Variable() 声明变量的全局命名空间概念也已被替换为默认垃圾收集机制

这是 Keras 中的多层感知器层¹⁵:

import TensorFlow as tf
from TensorFlow.keras.layers import Input, Dense
from keras.models import Model
l = tf.keras.layers
model = tf.keras.Sequential([
    l.Flatten(input_shape=(784,)),
    l.Dense(128, activation='relu'),
    l.Dense(128, activation='relu'),
    l.Dense(10, activation='softmax')
])
model.compile(loss='categorical_crossentropy', 
              optimizer='adam',
              metrics = ['accuracy'])
model.summary()
model.fit(x_train.reshape(-1,784),pd.get_dummies(y_train),nb_epoch=15,batch_size=128,verbose=1) 

现在我们已经了解了 TensorFlow 库的一些细节以及它为深度神经网络模型的开发(包括我们将在本书中实现的生成模型)而非常合适的原因,让我们开始建立我们的研究环境。虽然我们可以简单地使用像 pip 这样的 Python 包管理器在我们的笔记本电脑上安装 TensorFlow,但我们希望确保我们的过程尽可能健壮和可复制 - 这将使我们更容易将我们的代码打包到不同的机器上运行,或者通过指定我们在实验中使用的每个 Python 库的确切版本来保持我们的计算一致。我们将首先安装一个集成开发环境IDE),这将使我们的研究更容易 - VSCode。

VSCode

Visual Studio CodeVSCode)是由微软公司开发的开源代码编辑器,可用于许多编程语言,包括 Python。它允许调试,并与诸如 Git 等版本控制工具集成; 我们甚至可以在 VSCode 中运行 Jupyter 笔记本(我们将在本章后面描述)。安装说明因您使用的是 Linux、macOS 还是 Windows 操作系统而异:请查看您系统的单独说明,网址为 code.visualstudio.com。安装完成后,我们需要使用 Git 克隆本书项目的源代码副本,命令如下:

git clone git@github.com:PacktPublishing/Hands-On-Generative-AI-with-Python-and-TensorFlow-2.git 

此命令将把本书项目的源代码复制到我们的笔记本电脑上,允许我们本地运行和修改代码。一旦您复制了代码,请使用 VSCode 打开此书的 GitHub 存储库(图 2.1)。我们现在准备开始安装我们将需要的一些工具;打开install.sh文件。

图 2.1:VSCode IDE

对于我们来说特别有用的一个功能是,VSCode 具有集成(图 2.2)终端,我们可以在其中运行命令:您可以通过选择View,然后从下拉列表中选择Terminal来访问此功能,这将打开一个命令行提示:

图 2.2:VSCode 终端

选择TERMINAL选项卡,并选择解释器为bash;现在您应该能够输入正常的命令。将目录更改为Chapter_2,我们将在其中运行我们的安装脚本,您可以在 VSCode 中打开该脚本。

我们将运行的安装脚本将下载并安装我们在最后几章中将使用到的各种组件;我们将使用的全面框架是Kubeflow库,它处理我们在本卷的后几章中将使用到的各种数据和培训流水线。在本章的其余部分,我们将介绍 Kubeflow 是如何构建在 Docker 和 Kubernetes 之上的,以及如何在几个流行的云提供商上设置 Kubeflow。

Kubernetes(Kubeflow 基于此技术)本质上是一种管理使用Docker创建的容器化应用程序的方式,它允许创建和持久化可重现、轻量级的执行环境以适用于各种应用。虽然我们将使用 Docker 创建可重复的实验运行时,以了解其在虚拟化解决方案整体景观中的位置以及为什么它对现代应用程序开发如此重要,让我们稍微偏离一下,详细描述 Docker 的背景。

Docker:一个轻量级的虚拟化解决方案

开发强大的软件应用程序的一个持续挑战是使它们在与开发它们的机器不同的机器上运行相同。这些环境上的差异可能涵盖多个变量:操作系统、编程语言库版本和硬件,如 CPU 型号。

在处理这种异构性时,传统上一种方法是使用虚拟机VM)。虽然虚拟机能够在多样化的硬件和操作系统上运行应用程序,但它们也受到资源密集型的限制(图 2.3):每个运行在主机上的虚拟机都需要资源开销来运行完全独立的操作系统,以及所有来宾系统中的应用程序或依赖项。

图 2.3:虚拟机与容器¹⁶

然而,在某些情况下,这是一种不必要的开销级别;我们不一定需要运行一个完全独立的操作系统,而只需要一个一致的环境,包括一个操作系统内的库和依赖项。对于指定运行时环境的轻量级框架的需求促使了 2013 年Docker 项目的创建,用于容器化。本质上,容器是运行应用程序的环境,包括所有依赖项和库,允许可重现部署 Web 应用程序和其他程序,例如数据库或机器学习流水线中的计算。对于我们的用例,我们将使用它提供一个可重现的 Python 执行环境(Python 语言版本和库)来运行我们生成式机器学习流水线中的步骤。

本章余下部分的许多示例和本书中的项目需要安装 Docker。有关如何为您的特定操作系统安装 Docker 的说明,请参阅此处的指南。要验证您已成功安装该应用程序,请在终端上运行以下命令,该命令将打印出可用的选项:

docker run hello-world 

重要的 Docker 命令和语法

要了解 Docker 的工作原理,了解用于所有 Docker 容器的模板Dockerfile是有用的。作为示例,我们将使用 Kubeflow 项目中的 TensorFlow 容器笔记本示例(链接)。

此文件是 Docker 应如何采用基本操作环境、添加依赖项并在打包后执行软件的一组说明:

FROM public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/jupyter-tensorflow:master-abf9ec48
# install - requirements.txt
COPY --chown=jovyan:users requirements.txt /tmp/requirements.txt
RUN python3 -m pip install -r /tmp/requirements.txt --quiet --no-cache-dir \
 && rm -f /tmp/requirements.txt 

虽然容器之间的确切命令会有所不同,但这将让您了解我们可以如何使用容器来管理应用程序——在这种情况下,使用一致的库集运行 Jupyter 笔记本进行交互式机器学习实验。一旦我们为我们的特定操作系统安装了 Docker 运行时,我们将通过运行以下命令来执行这样一个文件:

Docker build -f <Dockerfilename> -t <image name:tag> 

当我们这样做时,会发生一些事情。首先,我们从远程存储库中检索base文件系统或 image,这有点类似于我们在使用 Java 构建工具(如 Gradle 或 Maven)或 Python 的 pip 安装程序时,从 Artifactory 收集 JAR 文件的方式。有了这个文件系统或 image,然后我们为 Docker build 命令设置所需的变量,比如用户名和 TensorFlow 版本,以及容器的运行时环境变量。我们确定将用于运行命令的 shell 程序,然后安装我们需要运行 TensorFlow 和笔记本应用程序的依赖项,并指定在启动 Docker 容器时要运行的命令。然后,我们使用由基本 image 名称和一个或多个 tags(比如版本号,或者在许多情况下,简单地用时间戳来唯一标识这个 image)组成的标识符保存这个快照。最后,要实际启动运行这个容器的笔记本服务器,我们将发出以下命令:

Docker run <image name:tag> 

默认情况下,Docker 会运行在 Dockerfile 文件中的可执行命令;在我们目前的示例中,这是启动笔记本服务器的命令。然而,并非一定如此;我们也可以有一个 Dockerfile,它只是为应用程序构建一个执行环境,并发出在该环境内运行的命令。在这种情况下,命令看起来会像这样:

Docker run <image name:tag> <command> 

Docker run 命令允许我们测试我们的应用程序是否可以成功在 Dockerfile 指定的环境中运行;然而,通常我们希望在云中运行此应用程序,以便利用分布式计算资源或能够托管向全球公开的 Web 应用程序,而不是在本地。要做到这一点,我们需要将构建的镜像移到一个远程存储库,使用 push 命令,这个远程存储库可能与我们最初拉取初始镜像的存储库相同,也可能不同。

Docker push <image name:tag> 

注意,image 名称可以包含对特定注册表的引用,比如本地注册表或在主要云提供商(如 AWS 的 弹性容器服务(ECS),Azure 的 Azure Kubernetes 服务(AKS) 或 Google 的容器注册表)上托管的注册表。将镜像发布到远程注册表允许开发人员共享镜像,并使我们可以在云中部署容器。

使用 Docker-compose 连接 Docker 容器

到目前为止,我们只讨论了一些基本的 Docker 命令,这些命令可以让我们在单个容器中运行单个服务。然而,你也许能够理解,在“现实世界”中,我们通常需要同时运行一个或多个应用程序 – 例如,一个网站将同时有一个获取和处理用户活动数据的网络应用程序和一个用于记录信息的数据库实例。在复杂的应用程序中,网站甚至可能由多个专门用于特定用例的小型网络应用程序或微服务组成,比如前端、用户数据或订单管理系统。对于这些类型的应用程序,我们需要多个容器彼此通信。Docker-compose 工具(docs.docker.com/compose/)就是为此类应用程序而设计的:它允许我们使用YAML格式在应用文件中指定多个 Docker 容器。例如,一个具有 Redis 数据库实例的网站配置可能如下:

version: '3'
services:
  web:
    build: .
    ports:
    - "5000:5000"
    volumes:
    - .:/code
    - logvolume01:/var/log
    links:
    - redis
  redis:
    image: redis
volumes:
  logvolume01: {} 

代码 2.1:Docker Compose 的 yaml 输入文件

这里的两个应用程序容器分别是webredis数据库。文件还指定了与这两个应用程序相关联的卷(磁盘)。使用这个配置,我们可以运行以下命令:

docker-compose up 

这将启动 YAML 文件中指定的所有容器,并允许它们彼此通信。然而,即使 Docker 容器和 docker-compose 允许我们使用一致的执行环境构建复杂的应用程序,当我们将这些服务部署到云端时,我们可能会遇到鲁棒性问题。例如,在一个网站应用程序中,我们无法保证应用程序运行的虚拟机会持续长时间,因此我们需要管理自愈和冗余的进程。这也与分布式机器学习流水线有关,其中我们不希望因为集群中的一个节点出现问题就不得不终止整个作业,因此我们需要备份逻辑来重新启动工作的一部分。此外,虽然 Docker 具有 docker-compose 功能来链接应用程序中的几个容器,但它没有健壮的规则来控制这些容器之间的通信,或者如何将它们作为一个单元进行管理。出于这些目的,我们转向 Kubernetes 库。

Kubernetes:强大的多容器应用程序管理

Kubernetes 项目-有时缩写为 k8s-诞生于谷歌内部称为Borg的容器管理项目。Kubernetes 来自希腊词 navigator,如项目标识的七条辐射轮所示。¹⁸ Kubernetes 使用 Go 编程语言编写,提供了一个强大的框架,在由云提供商管理的底层资源上部署和管理 Docker 容器应用程序(例如亚马逊网络服务AWS)、Microsoft Azure 和Google 云平台GCP))。

Kubernetes 基本上是用来控制由一个或多个部署在云中的 Docker 容器组成的应用程序的工具;这个容器的集合称为Pod。每个 Pod 可以有一个或多个副本(以实现冗余),这称为资源副本集。Kubernetes 部署的两个主要组件是控制平面节点。控制平面承载了部署和管理 Pod 的集中逻辑,由(图 2.4)组成:

图 2.4:Kubernetes 组件¹⁸

  • Kube-api-server:这是主要的应用程序,它侦听用户的命令以部署或更新 Pod,或通过ingress管理对 Pod 的外部访问。

  • Kube-controller-manager:管理每个 Pod 副本数量等功能的应用程序。

  • Cloud-controller-manager:管理特定于云提供商的功能。

  • Etcd:维护不同 Pod 的环境和状态变量的键值存储。

  • Kube-scheduler:负责找到运行 Pod 的工作进程的应用程序。

虽然我们可以设置自己的控制平面,但在实践中,通常我们会将此功能由我们的云提供商管理,例如谷歌的Google Kubernetes 引擎GKE)或亚马逊的弹性 Kubernetes 服务EKS)。Kubernetes 节点-集群中的各个单独的机器-每个都运行一个名为kubelet的应用程序,该应用程序监视运行在该节点上的 Pod。

现在,我们已经对 Kubernetes 系统有了一个高层次的了解,接下来让我们来看一下你将需要与 Kubernetes 集群进行交互、更新其组件以及启动和停止应用程序的重要命令。

重要的 Kubernetes 命令

为了与在云中运行的 Kubernetes 集群进行交互,我们通常使用Kubernetes 命令行工具kubectl)。有关在您的操作系统上安装 kubectl 的说明可以在kubernetes.io/docs/tasks/tools/install-kubectl/找到。要验证您是否成功安装了 kubectl,可以在终端中再次运行help命令:

kubectl --help 

与 Docker 一样,kubectl 有许多命令;我们将使用的一个重要命令是apply命令,它与docker-compose类似,它将一个 YAML 文件作为输入并与 Kubernetes 控制平面通信以启动、更新或停止 Pod:

kubectl apply -f <file.yaml> 

作为 apply 命令运行方式的示例,让我们看一下用于部署 web 服务器 (nginx) 应用程序的 YAML 文件:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-svc
  labels:
    app: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80 

此文件中指定的资源将按照文件中列出的顺序在 Kubernetes 集群节点上创建。首先,我们创建负载均衡器,它在nginx web 服务器的副本之间路由外部流量。metadata 用于为这些应用程序打标签,以便稍后使用 kubectl 进行查询。其次,我们使用一致的容器(镜像为 1.7.9)创建一组 3nginx pod 的副本,它们分别使用其容器上的端口 80

Kubernetes 集群的同一组物理资源可以在多个虚拟集群中共享,使用命名空间 – 这使我们可以将资源分隔到多个用户或组之间。例如,这可以让每个团队运行自己的一组应用程序,并在逻辑上表现得好像他们是唯一的用户。稍后,在我们对 Kubeflow 的讨论中,我们将看到如何使用此功能在同一 Kubeflow 实例上逻辑分区项目。

用于配置管理的 Kustomize

像大多数代码一样,我们最终可能希望将用于向 Kubernetes 发出命令的 YAML 文件存储在版本控制系统中,例如 Git。这导致一些情况下这种格式可能不理想:例如,在机器学习管道中,我们可能执行超参数搜索,其中相同的应用程序以稍微不同的参数运行,导致大量重复的命令文件。

或者,我们可能有一些参数,例如 AWS 账户密钥,出于安全原因,我们不希望将其存储在文本文件中。我们还可能希望通过将我们的命令拆分为 base 和附加部分来增加重用性;例如,在 代码 2.1 中显示的 YAML 文件中,如果我们想要在不同的数据库中运行 ngnix,或者指定 Amazon、Google 和 Microsoft Azure 提供的不同云对象存储中的文件存储。

对于这些用例,我们将使用 Kustomize 工具(kustomize.io),也可通过 kubectl 使用:

kubectl apply -k <kustomization.yaml> 

或者,我们可以使用 Kustomize 命令行工具。kustomization.yaml 是一个 Kubernetes 应用程序的模板;例如,考虑以下模板,用于 Kubeflow 示例存储库中的训练作业(github.com/kubeflow/pipelines/blob/master/manifests/kustomize/sample/kustomization.yaml):

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  # Or
# github.com/kubeflow/pipelines/manifests/kustomize/env/gcp?ref=1.0.0
  - ../env/gcp
  # Kubeflow Pipelines servers are capable of 
  # collecting Prometheus metrics.
  # If you want to monitor your Kubeflow Pipelines servers 
  # with those metrics, you'll need a Prometheus server 
  # in your Kubeflow Pipelines cluster.
  # If you don't already have a Prometheus server up, you 
  # can uncomment the following configuration files for Prometheus.
  # If you have your own Prometheus server up already 
  # or you don't want a Prometheus server for monitoring, 
  # you can comment the following line out.
  # - ../third_party/prometheus
  # - ../third_party/grafana
# Identifier for application manager to apply ownerReference.
# The ownerReference ensures the resources get garbage collected
# when application is deleted.
commonLabels:
  application-crd-id: kubeflow-pipelines
# Used by Kustomize
configMapGenerator:
  - name: pipeline-install-config
    env: params.env
    behavior: merge
secretGenerator:
  - name: mysql-secret
    env: params-db-secret.env
    behavior: merge
# !!! If you want to customize the namespace,
# please also update 
# sample/cluster-scoped-resources/kustomization.yaml's 
# namespace field to the same value
namespace: kubeflow
#### Customization ###
# 1\. Change values in params.env file
# 2\. Change values in params-db-secret.env 
# file for CloudSQL username and password
# 3\. kubectl apply -k ./
#### 

我们可以看到此文件引用了位于相对路径 ../base 的单独的 kustomization.yaml 文件中的 base 配置集。要编辑此文件中的变量,例如,要更改应用程序的命名空间,我们将执行:

kustomize edit set namespace mykube 

我们还可以添加配置映射以传递给训练作业,使用键-值格式,例如:

kustomize edit add configmap configMapGenerator --from-literal=myVar=myVal 

最后,当我们准备在 Kubernetes 上执行这些命令时,我们可以动态地build并应用所需的kubectl命令,假设kustomization.yaml在当前目录中。

kustomize build . |kubectl apply -f - 

希望这些示例演示了 Kustomize 如何提供一种灵活的方式来使用模板生成我们在本书后面的工作流程中经常需要的 kubectl YAML;我们将经常利用它来参数化我们的工作流程。

现在我们已经了解了 Kubernetes 如何在云中管理 Docker 应用程序,以及 Kustomize 如何允许我们灵活地重用kubectl yaml命令,让我们看看这些组件如何在 Kubeflow 中联系在一起,以运行我们稍后将进行的创建 TensorFlow 生成式 AI 模型的实验。

Kubeflow:一个端到端的机器学习实验室

正如本章开始时所描述的,端到端机器学习研究和开发的lab有许多组件(表 2.1),例如:

  • 管理和版本化库依赖,例如 TensorFlow,并将其打包为可复现的计算环境

  • 可视化数据并尝试不同设置的交互式研究环境

  • 指定管道步骤的系统化方式 – 数据处理、模型调优、评估和部署

  • 分布式运行建模过程所需资源的供应

  • 具有快照历史版本的研究过程的强大机制

正如我们在本章前面所描述的,TensorFlow 被设计用于利用分布式资源进行训练。为了利用这一能力,我们将使用 Kubeflow 项目。Kubeflow 建立在 Kubernetes 之上,具有几个在管理端到端机器学习应用程序过程中有用的组件。要安装 Kubeflow,我们需要拥有现有的 Kubernetes 控制平面实例,并使用 kubectl 启动 Kubeflow 的各个组件。设置步骤会略有不同,取决于我们是使用本地实例还是主要云服务提供商之一。

通过 MiniKF 在本地运行 Kubeflow

如果我们想快速开始或在本地原型化我们的应用程序,我们可以避免设置云账户,而是使用虚拟机模拟我们在云中配置资源的方式。要在本地设置 Kubeflow,我们首先需要安装 VirtualBox (www.virtualbox.org/wiki/Downloads) 以运行虚拟机,以及 Vagrant 以在 VirtualBox 虚拟机上运行配置,用于设置 Kubernetes 控制平面和 Kubeflow(www.vagrantup.com/downloads.html)。

安装了这两个依赖项后,创建一个新目录,切换到该目录并运行:

vagrant init arrikto/minikf
vagrant up 

这将初始化 VirtualBox 配置并启动应用程序。现在,您可以导航到http://10.10.10.10/并按照说明启动 Kubeflow 和 Rok(Arrikto 创建,用于 Kubeflow 实验中使用的数据的存储卷)。一旦这些被提供,你应该看到一个像这样的屏幕(图 2.5):

图 2.5:在虚拟盒子中的 MiniKF 安装界面¹⁹

登录到 Kubeflow 查看各个组件的仪表板(图 2.6):

图 2.6:MiniKF 中的 Kubeflow 仪表板

我们将在后面回到这些组件,并了解 Kubeflow 提供的各种功能,但首先,让我们一起看看如何在云中安装 Kubeflow。

在 AWS 中安装 Kubeflow

在 AWS 上运行 Kubeflow,我们需要在云中提供一个 Kubernetes 控制平面。幸运的是,亚马逊提供了一个名为 EKS 的托管服务,它可以方便地提供一个控制平面来部署 Kubeflow。按照以下步骤在 AWS 上部署 Kubeflow:

  1. 注册 AWS 账户并安装 AWS 命令行界面

    这是与各种 AWS 服务进行交互所需的,根据您平台上的说明位于docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html。安装完成后,输入:

    aws configure 
    

    为了设置您的账户和密钥信息来提供资源。

  2. 安装 eksctl

    这个命令行实用程序允许我们从命令行在亚马逊中提供一个 Kubernetes 控制平面。按照docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html上的说明进行安装。

  3. 安装 iam-authenticator

    为了允许 kubectl 与 EKS 进行交互,我们需要使用 IAM 验证器提供正确的权限来修改我们的 kubeconfig。请参考docs.aws.amazon.com/eks/latest/userguide/install-aws-iam-authenticator.html上的安装说明。

  4. 下载 Kubeflow 命令行工具

    链接位于 Kubeflow 发布页面(github.com/kubeflow/kubeflow/releases/tag/v0.7.1)。下载其中一个目录,并使用以下命令解压 tarball:

    tar -xvf kfctl_v0.7.1_<platform>.tar.gz 
    
  5. 构建配置文件

    输入 Kubeflow 应用程序目录(${KF_DIR})、部署名称(${KF_NAME})和部署的基本配置文件的路径(${CONFIG_URI})的环境变量,位于raw.githubusercontent.com/kubeflow/manifests/v0.7-branch/kfdef/kfctl_aws.0.7.1.yaml用于 AWS 部署,运行以下命令生成配置文件:

    mkdir -p ${KF_DIR}
    cd ${KF_DIR}
    kfctl build -V -f ${CONFIG_URI} 
    

    这将在本地生成一个名为kfctl_aws.0.7.1.yaml的本地配置文件。如果这看起来像 Kustomize,那是因为kfctl在内部使用 Kustomize 来构建配置。我们还需要为本地配置文件的位置添加一个环境变量${CONFIG_FILE},在这种情况下是:

    export CONFIG_FILE=${KF_DIR}/kfctl_aws.0.7.1.yaml 
    
  6. 在 EKS 上启动 Kubeflow

    使用以下命令启动 Kubeflow:

    cd ${KF_DIR}
    rm -rf kustomize/ 
    kfctl apply -V -f ${CONFIG_FILE} 
    

    所有 Kubeflow 组件变为可用将需要一些时间;您可以通过使用以下命令来检查进度:

    kubectl -n kubeflow get all 
    

    一旦它们都可用,我们可以使用以下命令获取 Kubeflow 仪表板的 URL 地址:

    kubectl get ingress -n istio-system 
    

这将带我们到上面的 MiniKF 示例中显示的仪表盘视图。请注意,在默认配置中,此地址对公众开放;对于安全应用程序,我们需要按照www.kubeflow.org/docs/aws/authentication/中的说明添加身份验证。

在 GCP 中安装 Kubeflow

像 AWS 一样,Google 云平台GCP)提供了一个托管的 Kubernetes 控制平面 GKE。我们可以使用以下步骤在 GCP 中安装 Kubeflow:

  1. 注册 GCP 账户并在控制台上创建一个项目

    该项目将是与 Kubeflow 相关的各种资源所在的位置。

  2. 启用所需服务

    在 GCP 上运行 Kubeflow 所需的服务包括:

    • 计算引擎 API

    • Kubernetes 引擎 API

    • 身份和访问管理(IAM)API

    • 部署管理器 API

    • 云资源管理器 API

    • 云文件存储 API

    • AI 平台培训和预测 API

  3. 设置 OAuth(可选)

    如果您希望进行安全的部署,那么,与 AWS 一样,您必须按照说明添加身份验证到您的安装中,位于(www.kubeflow.org/docs/gke/deploy/oauth-setup/)。或者,您可以只使用 GCP 账户的用户名和密码。

  4. 设置 GCloud CLI

    这类似于前一节中涵盖的 AWS CLI。安装指南可在cloud.google.com/sdk/找到。您可以通过运行以下命令来验证您的安装:

    gcloud --help 
    
  5. 下载 Kubeflow 命令行工具

    链接位于 Kubeflow 发行版页面(github.com/kubeflow/kubeflow/releases/tag/v0.7.1)。下载其中一个目录并使用以下命令解压 tar 文件:

    tar -xvf kfctl_v0.7.1_<platform>.tar.gz 
    
  6. 登录 Google 云并创建用户凭据

    接下来,我们需要创建一个登录账户和凭据令牌,用于与我们的账户中的资源进行交互。

    gcloud auth login
    gcloud auth application-default login 
    
  7. 设置环境变量并部署 Kubeflow

    与 AWS 一样,我们需要为一些关键环境变量输入值:包含 Kubeflow 配置文件的应用程序(${KF_DIR}),Kubeflow 部署的名称(${KF_NAME}),基本配置 URI 的路径(${CONFIG_URI}- 对于 GCP,这是raw.githubusercontent.com/kubeflow/manifests/v0.7-branch/kfdef/kfctl_gcp_iap.0.7.1.yaml),Google 项目的名称(${PROJECT})以及它所在的区域(${ZONE})。

  8. 启动 Kubeflow

    与 AWS 一样,我们使用 Kustomize 构建模板文件并启动 Kubeflow:

    mkdir -p ${KF_DIR}
    cd ${KF_DIR}
    kfctl apply -V -f ${CONFIG_URI} 
    

    一旦启动了 Kubeflow,您可以使用以下命令获取仪表板的 URL:

    kubectl -n istio-system get ingress 
    

在 Azure 上安装 Kubeflow

Azure 是微软公司的云服务,和 AWS 和 GCP 一样,我们可以利用它来安装 Kubeflow,利用在 Azure 云中驻留的 Kubernetes 控制平面和计算资源。

  1. 在 Azure 上注册账户

    azure.microsoft.com注册账号-可用于实验的免费层。

  2. 安装 Azure 命令行实用程序

    请参阅docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest上平台的安装说明。您可以通过在本地计算机的命令行上运行以下命令来验证安装:

    az 
    

    这应该会打印出您可以在控制台上使用的命令列表。首先,通过以下命令登录您的帐户:

    az login 
    

    并输入您在步骤 1中注册的帐户凭据。您将被重定向到浏览器以验证您的帐户,之后您应该会看到类似以下的响应:

    "You have logged in. Now let us find all the subscriptions to which you have access": …
    [
    { 
        "cloudName": …
        "id" ….
    …
        "user": {
    …
    }
    }
    ] 
    
  3. 为新集群创建资源组

    我们首先需要创建新应用所在的资源组,使用以下命令:

    az group create -n ${RESOURCE_GROUP_NAME} -l ${LOCATION} 
    
  4. 在 AKS 上创建 Kubernetes 资源

    现在,在您的资源组上部署 Kubernetes 控制平面:

    az aks create -g ${RESOURCE_GROUP_NAME} -n ${NAME} -s ${AGENT_SIZE} -c ${AGENT_COUNT} -l ${LOCATION} --generate-ssh-keys 
    
  5. 安装 Kubeflow

    首先,我们需要获取凭据以在我们的 AKS 资源上安装 Kubeflow:

    az aks get-credentials -n ${NAME}  -g ${RESOURCE_GROUP_NAME} 
    
  6. 安装 kfctl

    安装并解压缩 tarball 目录:

    tar -xvf kfctl_v0.7.1_<platform>.tar.gz 
    
  7. 设置环境变量

    与 AWS 一样,我们需要为一些关键环境变量输入值:包含 Kubeflow 配置文件的应用程序(${KF_DIR}),Kubeflow 部署的名称(${KF_NAME}),和基本配置 URI 的路径(${CONFIG_URI}- 对于 Azure,这是raw.githubusercontent.com/kubeflow/manifests/v0.7-branch/kfdef/kfctl_k8s_istio.0.7.1.yaml)。

  8. 启动 Kubeflow

    与 AWS 一样,我们使用 Kustomize 构建模板文件并启动 Kubeflow:

    mkdir -p ${KF_DIR}
    cd ${KF_DIR}
    kfctl apply -V -f ${CONFIG_URI} 
    

    一旦启动 Kubeflow,您可以使用端口转发将本地端口8080的流量重定向到集群中的端口80,以使用以下命令在localhost:8080上访问 Kubeflow 仪表板:

    kubectl port-forward svc/istio-ingressgateway -n istio-system 8080:80 
    

使用 Terraform 安装 Kubeflow

对于这些云提供商,你可能会注意到我们有一套共同的命令;创建一个 Kubernetes 集群,安装 Kubeflow,并启动应用程序。虽然我们可以使用脚本来自动化这个过程,但想要像我们的代码一样,有一种方法来版本控制和持久化不同的基础设施配置,允许创建运行 Kubeflow 所需资源集合的可重现的配方,这将是可取的。这也有助于我们在不完全重写安装逻辑的情况下,潜在地在不同的云提供商之间移动。

模板语言Terraform (www.terraform.io/)是由 HashiCorp 创建的一种用于基础设施即服务IaaS)的工具。就像 Kubernetes 有一个 API 来更新集群上的资源一样,Terraform允许我们使用 API 和模板语言来抽象不同的底层云提供商的交互,使用命令行工具和用 GoLang 编写的核心组件。Terraform 可以使用用户编写的插件进行扩展。

图 2.7:Terraform 架构²⁰

让我们以安装 Kubeflow 在 AWS 上使用 Terraform 指南的一个例子,位于github.com/aws-samples/amazon-eks-machine-learning-with-terraform-and-kubeflow上。一旦你在 EC2 容器上建立所需的 AWS 资源并安装了 terraform,aws-eks-cluster-and-nodegroup.tf Terraform 文件用于使用命令创建 Kubeflow 集群:

terraform apply 

这个文件中有一些关键组件。一个是指定部署方面的变量:

variable "efs_throughput_mode" {
   description = "EFS performance mode"
   default = "bursting"
   type = string
} 

另一个是指定我们正在使用的云提供商的规范:

provider "aws" {
  region                  = var.region
  shared_credentials_file = var.credentials
resource "aws_eks_cluster" "eks_cluster" {
  name            = var.cluster_name
  role_arn        = aws_iam_role.cluster_role.arn
  version         = var.k8s_version

  vpc_config {
    security_group_ids = [aws_security_group.cluster_sg.id]
    subnet_ids         = flatten([aws_subnet.subnet.*.id])
  }

  depends_on = [
    aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy,
    aws_iam_role_policy_attachment.cluster_AmazonEKSServicePolicy,
  ]

  provisioner "local-exec" {
    command = "aws --region ${var.region} eks update-kubeconfig --name ${aws_eks_cluster.eks_cluster.name}"
  }

  provisioner "local-exec" {
    when    = destroy
    command = "kubectl config unset current-context"
  }

}
  profile   = var.profile
} 

还有另一个是诸如 EKS 集群这样的资源:

resource "aws_eks_cluster" "eks_cluster" {
  name     = var.cluster_name
  role_arn = aws_iam_role.cluster_role.arn
  version  = var.k8s_version

  vpc_config {
    security_group_ids = [aws_security_group.cluster_sg.id]
    subnet_ids         = flatten([aws_subnet.subnet.*.id])
  }

  depends_on = [
    aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy,
    aws_iam_role_policy_attachment.cluster_AmazonEKSServicePolicy,
  ]

  provisioner "local-exec" {
    command = "aws --region ${var.region} eks update-kubeconfig --name ${aws_eks_cluster.eks_cluster.name}"
  }

  provisioner "local-exec" {
    when    = destroy
    command = "kubectl config unset current-context"
  }

} 

每次运行 Terraform apply 命令时,它都会遍历这个文件,确定要创建哪些资源,调用哪些底层 AWS 服务来创建它们,以及他们应该使用哪组配置进行配置。这为编排诸如 Kubeflow 之类的复杂安装提供了一种清晰的方式,这是一种版本化的、可扩展的模板语言。

现在我们已经成功地在本地或在云端的托管 Kubernetes 控制面板上安装了 Kubeflow,让我们看看平台上有哪些可用的工具。

Kubeflow 组件简介

现在我们已经在本地或云端安装了 Kubeflow,让我们再次看看 Kubeflow 仪表板(图 2.8):

图 2.8:Kubeflow 仪表板

让我们来看看这个工具包提供了什么。首先,注意到在上面的面板中,我们有一个下拉菜单,其中指定了名称为anonymous – 这是前面提到的 Kubernetes 的namespace。虽然我们的默认值是anonymous,但我们可以在我们的 Kubeflow 实例上创建多个命名空间,以容纳不同的用户或项目。这可以在登录时完成,我们在那里设置一个个人资料(图 2.9):

图 2.9:Kubeflow 登录页面

或者,与 Kubernetes 中的其他操作一样,我们可以使用 YAML 文件应用一个命名空间:

apiVersion: kubeflow.org/v1beta1
kind: Profile
metadata:
  name: profileName  
spec:
  owner:
    kind: User
    name: userid@email.com 

使用kubectl命令:

kubectl create -f profile.yaml 

一旦我们有了命名空间,我们可以做些什么呢?让我们看看可用的工具。

Kubeflow 笔记本服务器

我们可以使用 Kubeflow 在一个命名空间中启动一个 Jupyter 笔记本服务器,在这里我们可以运行实验性的代码;我们可以通过用户界面中的Notebook Servers选项卡并选择NEW SERVER来启动笔记本(图 2.10):

图 2.10:Kubeflow 笔记本创建

然后我们可以指定参数,比如要运行哪个容器(可能包括我们在之前关于 Docker 讨论中检查过的 TensorFlow 容器),以及分配多少资源(图 2.11)。

图 2.11:Kubeflow Docker 资源面板

您还可以指定一个持久卷PV)来存储数据,即使笔记本服务器被关闭,数据仍然保留,以及特殊资源,比如 GPU。

一旦启动,如果您已经指定了一个包含 TensorFlow 资源的容器,您可以在笔记本服务器中开始运行模型。

Kubeflow 流水线

对于笔记本服务器,我们举了一个单一容器(笔记本实例)应用的例子。Kubeflow 还通过pipelines功能为我们提供了运行多容器应用工作流(如输入数据、训练和部署)的能力。Pipelines 是遵循领域特定语言DSL)的 Python 函数,用于指定将编译为容器的组件。

如果我们在用户界面上点击 pipelines,我们会被带到一个仪表盘(图 2.12):

图 2.12:Kubeflow 流水线仪表盘

选择其中一个流水线,我们可以看到组件容器的视觉概览(图 2.13)。

图 2.13:Kubeflow 流水线可视化

创建新的运行之后,我们可以为该流水线的特定实例指定参数(图 2.14)。

图 2.14:Kubeflow 流水线参数

一旦流水线创建完毕,我们可以使用用户界面来可视化结果(图 2.15):

图 2.15:Kubeflow 流水线结果可视化

在幕后,用于生成此流水线的 Python 代码是使用流水线 SDK 编译的。我们可以指定组件来自具有 Python 代码的容器:

@kfp.dsl.component
def my_component(my_param):
  ...
  return kfp.dsl.ContainerOp(
    name='My component name',
    image='gcr.io/path/to/container/image'
  )
or a function written in Python itself:
@kfp.dsl.python_component(
  name='My awesome component',
  description='Come and play',
)
def my_python_func(a: str, b: str) -> str: 

对于纯 Python 函数,我们可以使用编译器将其转换为一个操作:

my_op = compiler.build_python_component(
  component_func=my_python_func,
  staging_gcs_path=OUTPUT_DIR,
  target_image=TARGET_IMAGE) 

然后我们使用 dsl.pipeline 装饰器将此操作添加到流水线中:

@kfp.dsl.pipeline(
  name='My pipeline',
  description='My machine learning pipeline'
)
def my_pipeline(param_1: PipelineParam, param_2: PipelineParam):
  my_step = my_op(a='a', b='b') 

我们使用以下代码进行编译:

kfp.compiler.Compiler().compile(my_pipeline, 'my-pipeline.zip') 

运行此代码:

client = kfp.Client()
my_experiment = client.create_experiment(name='demo')
my_run = client.run_pipeline(my_experiment.id, 'my-pipeline', 
  'my-pipeline.zip') 

我们还可以将此 ZIP 文件上传到流水线 UI,在那里 Kubeflow 可以使用编译生成的 YAML 实例化作业。

现在你已经看到了生成单个流水线结果的过程,我们下一个问题是如何生成这样一个流水线的最佳参数。正如你将在第三章深度神经网络的构建模块中看到的那样,神经网络模型通常具有多个配置,称为超参数,它们管理着它们的体系结构(例如层数、层大小和连接性)和训练范式(例如学习率和优化器算法)。Kubeflow 具有用于优化此类参数网格的内置实用程序,称为Katib

使用 Kubeflow Katib 优化模型超参数

Katib 是一个框架,用于使用不同的输入运行同一作业的多个实例,例如神经架构搜索(用于确定神经网络中正确的层数和大小)和超参数搜索(例如为算法找到正确的学习率)。与我们见过的其他 Kustomize 模板一样,TensorFlow 作业指定了一个通用的 TensorFlow 作业,并为参数留有占位符:

apiVersion: "kubeflow.org/v1alpha3"
kind: Experiment
metadata:
  namespace: kubeflow
  name: tfjob-example
spec:
  parallelTrialCount: 3
  maxTrialCount: 12
  maxFailedTrialCount: 3
  objective:
    type: maximize
    goal: 0.99
    objectiveMetricName: accuracy_1
  algorithm:
    algorithmName: random
  metricsCollectorSpec:
    source:
      fileSystemPath:
        path: /train
        kind: Directory
    collector:
      kind: TensorFlowEvent
  parameters:
    - name: --learning_rate
      parameterType: double
      feasibleSpace:
        min: "0.01"
        max: "0.05"
    - name: --batch_size
      parameterType: int
      feasibleSpace:
        min: "100"
        max: "200"
  trialTemplate:
    goTemplate:
        rawTemplate: |-
          apiVersion: "kubeflow.org/v1"
          kind: TFJob
          metadata:
            name: {{.Trial}}
            namespace: {{.NameSpace}}
          spec:
           tfReplicaSpecs:
            Worker:
              replicas: 1 
              restartPolicy: OnFailure
              template:
                spec:
                  containers:
                    - name: tensorflow 
                      image: gcr.io/kubeflow-ci/tf-mnist-with-
                             summaries:1.0
                      imagePullPolicy: Always
                      command:
                        - "python"
                        - "/var/tf_mnist/mnist_with_summaries.py"
                        - "--log_dir=/train/metrics"
                        {{- with .HyperParameters}}
                        {{- range .}}
                        - "{{.Name}}={{.Value}}"
                        {{- end}}
                        {{- end}} 

我们可以使用熟悉的 kubectl 语法来运行它:

kubectl apply -f https://raw.githubusercontent.com/kubeflow/katib/master/examples/v1alpha3/tfjob-example.yaml 

或通过 UI(图 2.16):

图 2.16:Kubeflow 上的 Katib UI

在这里你可以看到这些多参数实验的结果可视化,或者一个表格(图 2.172.18)。

图 2.17:Kubeflow 多维参数优化的可视化

图 2.18:Kubeflow 多结果实验的 UI

总结

在本章中,我们概述了 TensorFlow 是什么,以及它如何作为深度学习研究的改进,我们还探讨了设置 IDE、VSCode 和可重现应用程序的基础,Docker 容器。为了编排和部署 Docker 容器,我们讨论了 Kubernetes 框架,以及如何使用其 API 扩展容器组。最后,我描述了 Kubeflow,一个建立在 Kubernetes 上的机器学习框架,它允许我们运行端到端的流水线、分布式训练和参数搜索,并为训练后的模型提供服务。然后,我们使用 Terraform,一种 IaaS 技术,设置了 Kubeflow 部署。

在深入具体项目之前,我们将介绍神经网络理论的基础知识以及你需要编写基本训练作业的 TensorFlow 和 Keras 命令,在 Kubeflow 上。

参考资料

  1. Abadi, Martín 等(2016 年)TensorFlow:异构分布式系统上的大规模机器学习。arXiv:1603.04467。arxiv.org/abs/1603.04467

  2. 谷歌。TensorFlow。检索日期为 2021 年 4 月 26 日,网址:www.tensorflow.org/

  3. MATLAB,马萨诸塞州南提克:The MathWorks Inc。www.mathworks.com/products/matlab.html

  4. Krizhevsky A., Sutskever I., & Hinton G E. 使用深度卷积神经网络的 ImageNet 分类papers.nips.cc/paper/4824-imagenet-classification-with-deepconvolutional-neural-networks.pdf

  5. Dean J., Ng A. (2012 年 6 月 26 日)。利用大规模脑模拟进行机器学习和 AI。Google | The Keyword。blog.google/technology/ai/using-large-scale-brain-simulations-for/

  6. Mnih, V., Kavukcuoglu, K., Silver, D., Graves, A., Antonoglou, I., Wierstra, D., Riedmiller, M. (2013)。使用深度强化学习玩 Atari 游戏。arXiv:1312.5602。arxiv.org/abs/1312.5602

  7. Silver D, Schrittwieser J, Simonyan K, Antonoglou I, Huang A, Guez A, Hubert T, Baker L, Lai M, Bolton A, Chen Y, Lillicrap T, Hui F, Sifre L, van den Driessche G, Graepel T, Hassabis D. (2017)。在没有人类知识的情况下掌握围棋自然。550(7676):354-359。pubmed.ncbi.nlm.nih.gov/29052630/

  8. Devlin, J., Chang, M. W., Lee, K., & Toutanova, K. (2018)。Bert:用于语言理解的深度双向 transformers 的预训练。arXiv:1810.04805。arxiv.org/abs/1810.04805

  9. Al-Rfou, R.,等人 (2016)。Theano:快速计算数学表达的 Python 框架。arXiv。arxiv.org/pdf/1605.02688.pdf

  10. Collobert R., Kavukcuoglu K., & Farabet C. (2011)。Torch7:一个类似 Matlab 的机器学习环境ronan.collobert.com/pub/matos/2011_torch7_nipsw.pdf

  11. Abadi M.,等人 (2015)。TensorFlow:异构分布式系统上的大规模机器学习download.tensorflow.org/paper/whitepaper2015.pdf

  12. Abadi, Martín,等人 (2016)。TensorFlow:异构分布式系统上的大规模机器学习。arXiv:1603.04467。arxiv.org/abs/1603.04467

  13. Jouppi, N P,等人 (2017)。数据中心张量处理单元的性能分析。arXiv:1704.04760。arxiv.org/abs/1704.04760

  14. van Merriënboer, B., Bahdanau, D., Dumoulin, V., Serdyuk, D., Warde-Farley, D., Chorowski, J., Bengio, Y. (2015)。Blocks 和 Fuel:深度学习框架。arXiv:1506.00619。arxiv.org/pdf/1506.00619.pdf

  15. stackoverflow.com/questions/57273888/keras-vs-TensorFlow-code-comparison-sources

  16. Harris M. (2016). Docker vs. 虚拟机. Nvidia developer blog. developer.nvidia.com/blog/nvidia-docker-gpu-server-application-deployment-made-easy/vm_vs_docker/

  17. 一个视觉双关语 - 该项目的原始代码名称为 Seven of Nine,来自电视剧星际迷航:航海家号中的博格角色。

  18. Kubernetes 组件。 (2021 年 3 月 18 日) Kubernetes. kubernetes.io/docs/concepts/overview/components/

  19. Pavlou C. (2019). 在本地端到端的 ML 管道:Notebooks 和 Kubeflow Pipelines 在新 MiniKF 上. Medium | Kubeflow. medium.com/kubeflow/an-end-to-end-ml-pipeline-on-prem-notebooks-kubeflow-pipelines-on-the-new-minikf-33b7d8e9a836

  20. Vargo S. (2017). 使用 Terraform 管理 Google 日历. HashiCorp. www.hashicorp.com/blog/managing-google-calendar-with-terraform

第三章:深度神经网络的构建模块

在本书中,我们将实现的广泛范围的生成式人工智能模型都是建立在过去十年来在深度学习和神经网络方面的进步基础上的。虽然在实践中我们可以在不参考历史发展的情况下实现这些项目,但追溯它们的基本组成部分将使您对这些模型如何为什么工作有更深入的理解。在本章中,我们将深入探讨这一背景,向您展示生成式人工智能模型是如何从基础构建的,如何将较小的单元组装成复杂的架构,这些模型中的损失函数是如何优化的,以及一些当前的理论解释为什么这些模型如此有效。掌握了这些背景知识,您应该能够更深入地理解从本书的第四章开始的更高级模型和主题背后的推理,《教网络生成数字》。一般来说,我们可以将神经网络模型的构建模块分为一些关于模型如何构建和训练的选择,我们将在本章中进行介绍:

使用哪种神经网络架构:

  • 感知器

  • 多层感知器MLP)/ 前馈

  • 卷积神经网络CNNs

  • 循环神经网络RNNs

  • 长短期记忆网络LSTMs

  • 门控循环单元GRUs

在网络中使用哪些激活函数:

  • 线性

  • Sigmoid

  • Tanh

  • ReLU

  • PReLU

使用什么优化算法来调整网络参数:

  • 随机梯度下降SGD

  • RMSProp

  • AdaGrad

  • ADAM

  • AdaDelta

  • 无 Hessian 优化

如何初始化网络的参数:

  • 随机

  • Xavier 初始化

  • He 初始化

正如你所理解的,这些决策的产物可能导致大量潜在的神经网络变种,开发这些模型的一个挑战之一是确定每个选择中的正确搜索空间。在描述神经网络历史的过程中,我们将更详细地讨论这些模型参数的影响。我们对这个领域的概述始于这一学科的起源:谦逊的感知器模型。

感知器——一个功能中的大脑

最简单的神经网络架构——感知器——受生物研究的启发,旨在理解心理加工的基础,试图用数学公式表示大脑的功能。在本节中,我们将涵盖一些早期研究,以及它是如何激发了现在的深度学习和生成式人工智能领域的。

从组织到 TLUs

AI 算法的近期受欢迎可能会给人一种错误的印象,认为这个领域是新的。许多近期的模型基于几十年前的发现,这些发现因云端的大规模计算资源以及用于并行矩阵计算的定制硬件(如图形处理单元GPUs)、张量处理单元TPUs)和可编程门阵列FPGAs))而得到了重振。如果我们认为神经网络的研究包括其生物启发和计算理论,那么这个领域已经有上百年的历史了。事实上,19 世纪科学家 Santiago Ramón y Cajal 详细解剖插图中描述的其中一个最早的神经网络,这些插图基于对相互连接的神经细胞层的实验观察,启发了神经元学说—即大脑是由单独的、物理上不同且专门的细胞组成,而不是一个连续的网络。¹ Cajal 观察到的视网膜的不同层也启发了特定的神经网络架构,比如我们将在本章后面讨论的 CNN。

图 3.1:由 Santiago Ramón y Cajal 绘制的神经元相互连接的网络³

这种简单神经细胞相互连接的观察使得计算研究人员推测精神活动可能如何由简单的逻辑运算表示,进而产生复杂的精神现象。最初的“自动机理论”通常被追溯到麻省理工学院的 Warren McCulloch 和 Walter Pitts 于 1943 年发表的一篇文章。³ 他们描述了一个简单的模型,即阈值逻辑单元TLU),其中二进制输入根据阈值转换为二进制输出:

其中,I 代表输入值,W 代表权重范围为 (0, 1) 或 (-1, 1),而 f 是一个阈值函数,根据输入是否超过阈值 T 将这些输入转换成二进制输出:⁴

在视觉上和概念上,McCulloch 和 Pitts 的模型与启发它的生物神经元(图 3.2)之间存在一定的相似性。他们的模型将输入整合成输出信号,就像神经元的自然树突(神经元的短输入“臂”,从其他细胞接收信号)将输入通过轴突(细胞的长“尾巴”,将从树突接收到的信号传递给其他神经元)合成一个单一的输出。我们可以想象,就像神经细胞被组成网络以产生复杂的生物学电路一样,这些简单的单元可能被连接起来以模拟复杂的决策过程。

图 3.2:TLU 模型和生物神经元^(5 6)

实际上,使用这个简单的模型,我们已经可以开始表示几个逻辑操作。 如果我们考虑一个带有一个输入的简单神经元的情况,我们可以看到 TLU 可以解决恒等或否定函数(表 3.13.2)。

对于一个简单地返回输入作为输出的恒等操作,权重矩阵在对角线上会有 1(或者对于单个数字输入,权重矩阵会简单地是标量 1,如表 1所示):

恒等
输入
1
0

Table 3.1:恒等操作的 TLU 逻辑

同样地,对于否定操作,权重矩阵可以是负对角线矩阵,在阈值为 0 时翻转输出的符号:

否定
输入
1
0

Table 3.2:否定操作的 TLU 逻辑

给定两个输入,TLU 也可以表示诸如 AND 和 OR 等操作。 在这里,可以设置一个阈值,使得组合输入值必须超过2(对应 AND 操作的输出为1)或者1(对应 OR 操作的输出为1,如果两个输入中任意一个为1)。

AND
输入 1
0
1
0
1

Table 3.3:AND 操作的 TLU 逻辑

OR
输入 1
0
1
0
1

Table 3.4:OR 操作的 TLU 逻辑

然而,TLU 无法捕获诸如“异或”(XOR)的模式,它只有在OR条件为真时才会输出1

异或
输入 1
0
1
0
1

Table 3.5:XOR 操作的 TLU 逻辑

要看到这为什么是真的,考虑一个有两个输入和正权重值为1的 TLU。 如果阈值值T1,那么输入(0, 0),(1, 0)和(0, 1)将产生正确的值。 然而,(1, 1)会发生什么? 因为阈值函数对于任何求和大于1的输入都返回1,它无法表示 XOR(表 3.5),因为 XOR 要求一旦超过不同的、更高的值,就要计算不同的输出。 改变一个或两个权重为负值也没有帮助; 问题在于决策阈值只能单向操作,不能对更大的输入进行反转。

同样地,TLU 不能表示“异或”的否定,即XNOR表 3.6)。

XNOR
输入 1
0
1
0
1

Table 3.6:XNOR 操作的 TLU 逻辑

XOR操作类似(表 3.5),通过考虑一个含有两个 1 的权重矩阵,可以说明无法通过 TLU 函数来表示XNOR操作(表 3.6);对于两个输入(1, 0)或(0, 1),如果我们设置输出 1 的阈值为 2,则获得了正确的值。与XOR操作类似,当输入为(0, 0)时会遇到问题,因为我们无法设置第二个阈值来使和为 0 时输出 1。

从 TLUs 到调谐感知器

除了这些有关表示XORXNOR操作的限制外,还有一些附加的简化会限制 TLU 模型的表达能力;权重是固定的,输出只能是二进制(0 或 1)。显然,对于像神经元这样的系统来说,“学习”需要对环境做出响应,并根据先前的经验反馈确定不同输入的相关性。这个观点在加拿大心理学家唐纳德·赫布(Donald Hebb)1949 年的著作《行为的组织》中有所体现,他提出,附近的神经细胞的活动随着时间会趋同,有时被简化为赫布定律:“放电在一起联结在一起”^(7 8)。基于赫布的权重随时间变化的提议,康奈尔航空实验室的研究员弗兰克·罗森布拉特(Frank Rosenblatt)在 1950 年代提出了感知器(perceptron)模型。⁹ 他用自适应权重替代了 TLU 模型中的固定权重,并增加了偏置项,得到了一个新的函数:

我们注意到,输入I已被标记为X以突显它们可以是任何值,而不仅仅是二进制01。将赫布的观察与 TLU 模型相结合,感知器的权重将根据简单的学习规则进行更新:

  1. 从一组 J 个样本x(1) …. x(j)出发。这些样本都有标签 y,可以是 0 或 1,提供有标记的数据(y, x)(1) …. (y, x)(j)。这些样本可以是单个值,此时感知器有单个输入,也可以是长度为N且具有* i*的多值输入的向量。

  2. 初始化所有权重w为小的随机值或 0。

  3. 使用感知器函数计算所有示例x的估计值yhat

  4. 使用学习速率r更新权重,以更接近于每一步t中训练的期望输出值:

    ,对于所有的J个样本和N个特征。概念上,需要注意如果y为 0 且目标值为 1,我们希望通过一定的增量r增加权重的值;同样,如果目标值为 0 且估计值为 1,我们希望减小权重,使得输入值不超过阈值。

  5. 重复步骤 3-4,直到预测输出y和实际输出yhat之间的差值低于某个期望的阈值。在有非零偏置项b的情况下,也可以使用类似的公式来计算更新。

尽管简单,你可以理解这样的分类器可以学习到许多模式,但仍然不能学习到XOR函数。然而,通过将几个感知机组合成多个层,这些单元可以表示任何简单的布尔函数,¹⁰而且麦卡洛克和皮茨此前已经推测过将这些简单单元组合成一个通用计算引擎或图灵机,可以表示标准编程语言中的任何操作。然而,前述学习算法对每个单元独立操作,这意味着它可以扩展到由许多层感知机组成的网络(图 3.3)。

图 3.3:一个多层感知机¹¹

然而,麻省理工学院的计算机科学家马文·明斯基和西摩·帕珀特在 1969 年的书籍《感知机》中表明,一个三层前馈网络需要至少有一个这些单元(在第一层)与所有输入之间的完全(非零权重)连接才能计算所有可能的逻辑输出¹²。这意味着,与生物神经元只连接到少数邻居相比,这些计算模型需要非常密集的连接。

虽然后来的架构中已经融入了连接的稀疏性,比如 CNNs,但这种密集的连接仍然是许多现代模型的特征,特别是在通常形成模型倒数第二层隐藏层的全连接层中。除了这些模型在当时的硬件上计算上不便利外,对于稀疏模型无法计算所有逻辑运算的观察被研究界更广泛地解释为感知机无法计算 XOR。虽然是错误的,¹³但这个观点导致了 AI 在随后的几年里资金的枯竭,有时这段时期被称为AI 冬季¹⁴。

神经网络研究的下一次革命将需要一种更有效的方法来计算复杂模型中更新所需的参数,这种技术将被称为反向传播

多层感知机和反向传播

尽管自从《感知机》出版后,直到 1980 年代,神经网络的大规模研究资金都在下降,但研究人员仍然认识到这些模型有价值,特别是当它们被组装成由多个感知机单元组成的多层网络时。事实上,当输出函数的数学形式(即模型的输出)被放宽为多种形式(如线性函数或 Sigmoid 函数)时,这些网络可以解决回归和分类问题,理论结果表明,3 层网络可以有效逼近任何输出。¹⁵然而,这项工作没有解决这些模型的计算解的实际限制,而之前描述的感知机学习算法等规则对它们的应用造成了很大的限制。

对神经网络的重新关注始于反向传播算法的普及,该算法尽管在 20 世纪 60 年代已经被发现,但直到 20 世纪 80 年代才被广泛应用于神经网络,此前的多项研究强调了它在学习这些模型中的权重方面的有用性。¹⁶ 正如你在感知机模型中所看到的,更新权重的学习规则在没有“隐藏”层的情况下是相对容易推导出来的。输入只被感知机一次性地转换为输出值,意味着可以直接调整权重以产生期望的输出。当输入和输出之间有隐藏层时,问题就变得更加复杂:我们何时改变内部权重以计算输入权重遍历到最终输出的激活值?我们如何根据输入权重来修改它们?

反向传播技术的见解在于,我们可以利用微积分中的链式法则来高效地计算网络中每个参数相对于损失函数的导数,并且结合学习规则,这为训练多层网络提供了一种可扩展的方法。

让我们用一个例子来说明反向传播:考虑一个像图 3.3中所示的网络。假设最终层中的输出是使用 S 形函数计算的,这将产生一个值在 0 到 1 之间:

此外,值y,即最终神经元的输入之和,是隐藏单元的 S 形输入的加权和:

我们还需要一个概念,来判断网络在完成任务时是表现良好还是不良好。在这里可以使用的一个直观的误差函数是平方损失:

其中yhat是估计值(来自模型输出),y是所有输入示例J和网络K的输出的实际值的总和(其中K=1,因为只有一个输出值)。 反向传播开始于“前向传递”,在这一步中我们计算内层和外层所有输出的值,从而得到yhat的估计值。然后我们进行后向传递来计算梯度来更新权重。

我们的总体目标是计算每个神经元的权重w和偏置项 b 的偏导数:,这将使我们能够计算出bw的更新。为了实现这个目标,让我们从计算最终神经元输入的更新规则开始;我们希望使用链式规则来计算误差E对于每个这些输入的偏导数(在本例中有五个,对应于五个隐藏层神经元):

我们可以通过对损失函数求导来得到值

对于单个示例,这只是输入和输出值之间的差异。对于,我们需要对 Sigmoid 函数进行偏导数:

综上所述,我们有:

如果我们想要计算特定参数x(如权重w或偏置项b)的梯度,我们需要多做一步:

我们已经知道第一项,且x仅通过来自下层y的输入依赖于w,因为这是一个线性函数,所以我们得到:

如果我们想为隐藏层中的一个神经元计算此导数,我们以同样的方式对这个输入y[i]进行偏导数计算,这很简单:

因此,我们总共可以对所有输入到这个隐藏层的单元求和:

我们可以递归地重复这个过程以获得所需的更新规则,因为我们现在知道如何在任何层计算yw的梯度。这使得更新权重的过程变得高效,因为一旦我们通过反向传播计算了梯度,我们就可以结合连续的梯度通过层来得到网络任何深度所需的梯度。

现在,我们已经得到了每个w(或其他需要计算的神经元参数)的梯度,我们如何制定"学习规则"来更新权重?在他们的论文中,Hinton 等人指出,我们可以在每个样本批处理上计算梯度后应用更新,但建议在所有样本上计算平均值后应用更新。梯度表示误差函数相对于参数发生最大变化的方向;因此,为了更新,我们希望将权重推向相反的方向,e是一个小值(步长):

然后在训练过程中的每个时间点t,我们使用计算出的梯度更新权重:

扩展这个方法,Hinton 等人提出了一个当前梯度的指数加权更新加上先前更新的方法:

其中 alpha 是一个衰减参数,用于加权先前更新的贡献,取值范围从 0 到 1。根据这个过程,我们将使用一些小的随机值初始化网络中的权重,选择步长e,并通过前向和后向传播以及参数更新进行迭代,直到损失函数达到某个期望值。

现在我们已经描述了反向传播背后的形式数学,让我们看看它在实践中如何在 TensorFlow 2 等软件包中实现。

实践中的反向传播

虽然通过这种推导来理解深度神经网络的更新规则是有用的,但对于大型网络和复杂架构来说,这显然会很快变得难以管理。因此,幸运的是,TensorFlow 2 可以自动处理这些梯度的计算。在模型初始化期间,每个梯度都被计算为图中张量和操作之间的中间节点:例如,参见图 3.4

图 3.4:将梯度操作插入到 TensorFlow 图中¹⁸

在上述图的左侧显示了一个成本函数 C,它是从修正线性单元ReLU)的输出中计算得到的(一种我们将在本章后面介绍的神经元函数),而这个输出又是通过将一个权重向量乘以输入 x 并添加一个偏置项 b 计算得到的。在右侧,你可以看到 TensorFlow 已经扩展了这个图,以计算作为整个控制流一部分所需的所有中间梯度。

在存储了这些中间值之后,通过递归操作将它们组合成完整的梯度的任务交给了 GradientTape API。在幕后,TensorFlow 使用一种称为反向模式自动微分的方法来计算梯度;它将依赖变量(输出y)固定,并且从网络的末端递归地向前计算所需的梯度。

例如,让我们考虑以下形式的神经网络:

图 3.5:反向模式自动微分¹⁹

如果我们想要计算输出 y 关于输入 x 的导数,我们需要重复地代入最外层的表达式²⁰:

因此,为了计算所需的梯度,我们只需从上到下遍历图,当我们计算时存储每个中间梯度。这些值被存储在一个记录上,被称为磁带,这是一个对早期计算机的参考,其中信息存储在磁带上,²¹然后用于重放值以进行计算。另一种方法是使用前向模式自动微分,从下到上计算。这需要两次而不是一次传递(对于每个馈入到最终值的分支),但在概念上更容易实现,不需要反向模式的存储内存。然而,更重要的是,反向模式模仿了我之前描述的反向传播的推导。

这个磁带(也称为Wengert Tape,以其开发者之一命名)实际上是一个数据结构,你可以在 TensorFlow Core API 中访问到。例如,导入核心库:

from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf 

然后,可以使用 tf.GradientTape() 方法来获取这个磁带,在其中你可以评估与图中间值相关的梯度²²:

x = tf.ones((2, 2))
with tf.GradientTape() as t:
  t.watch(x)
  y = tf.reduce_sum(x)
  z = tf.multiply(y, y)
# Use the tape to compute the derivative of z with respect to the
# intermediate value y.
dz_dy = t.gradient(z, y)
# note that the resulting derivative, 2*y, = sum(x)*2 = 8
assert dz_dy.numpy() == 8.0 

默认情况下,GradientTape() 使用的内存资源在调用 gradient() 后被释放;但是,你也可以使用 persistent 参数来存储这些结果²³:

x = tf.constant(3.0)
with tf.GradientTape(persistent=True) as t:
  t.watch(x)
  y = x * x
  z = y * y
dz_dx = t.gradient(z, x)  # 108.0 (4*x³ at x = 3)
dy_dx = t.gradient(y, x)  # 6.0 

现在你已经看到 TensorFlow 如何实际计算梯度以评估反向传播,让我们回顾一下反向传播技术是如何随着时间的推移而发展,以应对实际实现中的挑战的细节。

反向传播的缺陷

虽然反向传播过程提供了一种以合理方式更新网络内部权重的方法,但它存在几个缺点,使得深度网络在实践中难以使用。其中一个是 梯度消失 的问题。在我们推导反向传播公式时,你看到网络中更深层次的权重的梯度是来自更高层的连续偏导数的乘积。在我们的例子中,我们使用了 Sigmoid 函数;如果我们绘制出 Sigmoid 的值及其一阶导数,我们可以看到一个潜在的问题:

图 3.6:Sigmoid 函数及其梯度²⁴

随着 Sigmoid 函数的值向极端值(0 或 1,代表“关闭”或“打开”)增加或减少,梯度的值趋近于零。这意味着从隐藏激活函数 y 的这些梯度得到的更新值 wb 会趋向于零,使得权重在迭代之间变化很小,使得反向传播过程中隐藏层神经元的参数变化非常缓慢。很显然,这里的一个问题是 Sigmoid 函数饱和;因此,选择另一个非线性函数可能会规避这个问题(这确实是作为 ReLU 提出的解决方案之一,我们稍后会讨论)。

另一个问题更微妙,与网络如何利用其可用的自由参数有关。正如你在 第一章生成型 AI 简介:“从模型中“绘制”数据 中看到的,变量的后验概率可以计算为似然和先验分布的乘积。我们可以将深度神经网络看作是这种概率的图形表示:神经元的输出,取决于其参数,是所有输入值和这些输入上的分布(先验)的乘积。当这些值变得紧密耦合时就会出现问题。举个例子,考虑一下头痛的竞争性假设:

图 3.7:解释逆效应

如果一个病人患有癌症,那么关于他们是否感冒的证据是如此压倒性,以至于没有提供额外价值;实际上,两个先前的假设的价值由于其中一个的影响而变得耦合。这使得计算不同参数的相对贡献变得棘手,特别是在深层网络中;我们将在我们关于《第四章,教网络生成数字》中讨论受限玻尔兹曼机和深度信念网络的问题。正如我们在该章节中将更详细地描述的那样,一项 2006 年的研究²⁵展示了如何抵消这种效应,这是对深度神经网络中可行推断的最早的一次突破,这一突破依赖于产生手绘数字图像的生成模型。

除了这些问题之外,在 20 世纪 90 年代和 21 世纪初,神经网络更广泛应用的其他挑战还包括像支持矢量机²⁶、梯度和随机梯度提升模型²⁷、随机森林²⁸甚至是惩罚回归方法如 LASSO²⁹和 Elastic Net³⁰这样的方法,用于分类和回归任务。

虽然理论上,深度神经网络的表征能力可能比这些模型更强,因为它们通过连续层构建输入数据的分层表示,与通过单一转换给出的“浅”表示如回归权重或决策树相反,但在实践中,训练深层网络的挑战使得这些“浅”方法对实际应用更有吸引力。这也与较大网络需要调整成千上万甚至是百万参数的事实相搭上了较大计算资源的事实,使这些实验在云供应商提供的廉价计算资源的爆炸之前是不可行的,包括 GPU 和 TPU 特别适用于快速矩阵计算。

现在我们已经介绍了训练简单网络架构的基础知识,让我们转向更复杂的模型,这些模型将构成书中许多生成模型的基础:CNNs 和序列模型(RNNs,LSTMs 等)。

网络的种类:卷积和递归

到目前为止,我们主要通过引用前馈网络来讨论神经网络的基础知识,其中每个输入都连接到每个层的每个输出。虽然这些前馈网络有助于说明深层网络的训练方式,但它们只是现代应用中使用的一类更广泛架构的一部分,包括生成模型。因此,在讨论使训练大型网络变得实用的一些技术之前,让我们回顾一下这些替代的深度模型。

视觉网络:卷积架构

正如本章开头所指出的,深度神经网络模型的灵感之一是生物神经系统。当研究人员试图设计可以模仿视觉系统功能的计算机视觉系统时,他们转向了视网膜的结构,这是在 20 世纪 60 年代神经生物学家 David Huber 和 Torsten Weisel 的生理学研究中揭示的。³¹ 正如以前所描述的,生理学家 Santiago Ramon Y Cajal 提供了神经结构如视网膜被安排在垂直网络中的视觉证据。

图 3.8:视网膜的“深层神经网络”^(32 33)

Huber 和 Weisel 研究了猫的视网膜系统,展示了它们对形状的知觉是由排列在一列中的单个细胞的活动所组成的。每一列细胞都被设计用来检测输入图像中边缘的特定方向;复杂形状的图像是由这些简单图像拼接在一起的。

早期的 CNNs

这种列的概念启发了对 CNN 结构的早期研究³⁴。与前馈网络中学习单元之间的个体权重不同,这种结构(图 3.9)使用了专门用于检测图像中特定边缘的一组神经元中的共享权重。网络的初始层(标记为 H1)由每个 64 个神经元的 12 组组成。这些组中的每个都是通过在 16 x 16 像素的输入图像上传递一个 5 x 5 的网格来得到的;这个组中的每一个 64 个 5 x 5 的网格共享相同的权重,但与输入的不同空间区域相关联。你可以看到,如果它们的接受域重叠了两个像素,那么每个组中必须有 64 个神经元来覆盖输入图像。

当这 12 组神经元在 H1 层中结合在一起时,它们形成了 12 个表示图像中特定边缘的存在或不存在的 8 x 8 网格(图 3.9)。这种权重共享在直观上是有意义的,因为由权重表示的卷积核被指定用来检测图像中的不同颜色和/或形状,不管它出现在图像的哪个位置。这种降采样的效果是一定程度上的位置不变性;我们只知道边缘发生在图像某个区域内,但由于降采样导致的分辨率降低,我们无法知道确切位置。因为它们是通过将一个 5 x 5 的矩阵(卷积核)与图像的一部分相乘得到的,这种操作被用在图像模糊和其他转换中,这 5 x 5 的输入特征被称为 卷积核,也给网络起了名字。

图 3.9:卷积神经网络³⁵

当我们有了这 12 个缩小了的 8 x 8 图像时,下一层(H2)还有 12 组神经元;在这里,卷积核是 5 x 5 x 8——它们横跨从H1上的一个 8 x 8 地图,遍及 12 个中的 8 个组。由于一个 5 x 5 的网格可以在 8 x 8 的网格上上下移动四次以覆盖 8 x 8 网格中的所有像素,我们需要 16 个这样的 5 x 5 x 8 组的神经元。

就像视觉皮层中更深层的细胞一样,网络中的更深层对来自不同边缘检测器的多个列进行整合,将信息组合在一起。

最后,该网络的第三个隐藏层(H3)包含 30 个隐藏单元和H2中的 12 x 16 个单元之间的全全连接,就像在传统的前馈网络中一样;最终的 10 个输出单元将输入图像分类为 10 个手写数字之一。

通过权重共享,在该网络中的自由参数总数得到了减少,虽然在绝对术语中仍然很大。虽然反向传播成功地用于此任务,但需要为一组成员受限的图像设计精心的网络,这些图像具有局限性的结果——对于如检测来自数百或数千个可能类别的对象等实际应用,需要采用其他方法。

AlexNet 和其他 CNN 创新技术

2012 年的一篇文章产生了最先进的结果,使用一个被称为 AlexNet 的模型将 ImageNet 中的 130 万张图像分类为 1000 种分类。这些模型要实现训练,需要采用一些后来的创新技术。(36)如我之前提到的一样,一个是使用 ReLU(37)替代 sigmoid 或双曲正切函数。ReLU 是以下形式的函数:

与 sigmoid 函数或 tanh 相比,在函数饱和时,其导数会缩小至 0,而 ReLU 函数具有恒定的梯度和 0 处的不连续性(图 3.10)。这意味着梯度不会饱和,导致网络的深层训练更慢,导致优化困难。

图 3.10:替代激活函数的梯度(38)

虽然 ReLU 函数具有非消失梯度和低计算要求的优势(因为它们只是阈值线性变换),但缺点是如果输入低于 0,则它们可能会“关闭”,导致再次出现 0 梯度。这个问题在之后的工作中得到解决,在 0 以下引入了一个“泄漏”。(39)

进一步的改进是使此阈值自适应,具有斜率为a参数化泄漏 ReLUPReLU)。(40)

AlexNet 使用的另一个技巧是辍学。⁴¹ 辍学的想法受到合奏方法的启发,在合奏方法中,我们对许多模型的预测进行平均,以获得更稳健的结果。显然,对于深度神经网络来说,这是不可行的;因此,一个妥协方案是以 0.5 的概率随机将某些神经元的值设为 0。这些值在每次反向传播的前向传递中被重置,允许网络有效地对不同的架构进行采样,因为“辍学”的神经元在该传递中不参与输出。

图 3.11:辍学

AlexNet 中使用的另一个增强是局部响应归一化。尽管 ReLU 不像其他单元那样饱和,模型的作者仍然发现限制输出范围有价值。例如,在一个单个卷积核中,他们使用相邻卷积核的值对输入进行归一化,这意味着总体响应被重新缩放⁴²:

其中a是图像上给定xy位置处的非标准化输出,j的总和是在相邻卷积核上,Bk和 alpha 是超参数。这种重新缩放让人想起后来被广泛应用于卷积和其他神经网络架构中的一种创新,批量归一化⁴³。批量归一化还对网络内部的“原始”激活应用转换:

其中x是非标准化输出,By是尺度和偏移参数。这种转换被广泛应用于许多神经网络架构,以加速训练,尽管它的有效原因仍然是争论的话题。⁴⁴

现在你对使大型 CNN 训练成为可能的一些方法论进步有了一些了解,让我们来研究 AlexNet 的结构,看看我们将在后面章节中实现的生成模型中使用的一些额外的架构组件。

AlexNet 架构

尽管图 3.12中的 AlexNet 架构看起来令人生畏,但一旦我们将这个大型模型分解为单独的处理步骤,就不那么难理解了。让我们从输入图像开始,跟踪通过每个后续神经网络层的一系列转换为每个图像计算输出分类的方法。

图 3.12:AlexNet

输入到 AlexNet 的图像大小为 224 x 224 x 3(对于 RGB 通道)。第一层由 96 个单元和 11 x 11 x 3 卷积核组成;输出经过响应归一化(如前所述)和最大化池化。最大化池化是一种采取n x n网格上的最大值来记录输入中是否“任何位置”出现模式的操作;这又是一种位置不变性的形式。

第二层也是一组规模为 5 x 5 x 8 的卷积,以 256 个为一组。第三层到第五层都有额外的卷积,没有规范化,接着是两个全连接层和一个输出大小为 1,000 表示 ImageNet 中可能的图像类。AlexNet 的作者使用了几个 GPU 来训练模型,这种加速对输出非常重要。

图 3.13:来自 AlexNet 的图像核

在初始的 11 x 11 x 3 卷积中,即训练过程中学到的特征中(图 3.13),我们可以看到可识别的边缘和颜色。虽然 AlexNet 的作者没有展示出网络中更高层次的神经元合成这些基本特征的例子,但另一项研究提供了一个示例,在该研究中,研究人员训练了一个大型的 CNN 来对 YouTube 视频中的图像进行分类,得到了网络最上层的一个神经元,它似乎是一个猫探测器(图 3.14)。

图 3.14:从 YouTube 视频中学习到的猫探测器⁴⁵

这个概述应该让你明白 CNN 架构看起来的样子,以及什么样的发展使得它们随着时间的推移而成为图像分类器或基于图像的生成模型的基础更加可行。现在我们将转向另一类更专业的架构——RNN,这种架构用于开发时间或基于序列的模型。

序列数据的网络

除了图像数据,自然语言文本也一直是神经网络研究中的一个热门话题。然而,与我们迄今为止检查的数据集不同,语言有一个重要的顺序与其含义相关。因此,为了准确地捕捉语言或时间相关数据中的模式,有必要使用专门设计用于此目的的网络。

RNN 和 LSTM

让我们想象一下,我们试图预测句子中的下一个词,给定到目前为止的词。试图预测下一个词的神经网络不仅需要考虑当前词,还需要考虑可变数量的先前输入。如果我们只使用一个简单的前馈 MLP,该网络实际上会将整个句子或每个词都处理为一个向量。这引入了这样一个问题:要么必须将可变长度的输入填充到一个共同的长度,并且不保留任何相关性的概念(也就是说,在生成下一个预测时,句子中哪些单词比其他单词更相关),或者在每一步中只使用上一个词作为输入,这样会丢失句子其余部分的上下文和提供的所有信息。这种问题激发了“原生”RNN⁴⁶,它在计算一个神经元的输出时,不仅考虑当前输入,还考虑前一步的隐藏状态:

可以将这个过程想象为每一层递归地馈送到下一个时间步骤的序列中。实际上,如果我们“展开”序列的每个部分,我们最终得到一个非常深的神经网络,其中每一层共享相同的权重。⁴⁷

图片

图 3.15:展开的 RNN⁴⁸

训练深度前馈网络所具有的困难也同样适用于循环神经网络;使用传统激活函数时,梯度往往在长距离上衰减(或者如果梯度大于 1,则爆炸)。

然而,与前馈网络不同,RNNs 不是用传统的反向传播进行训练,而是用一种称为时间反向传播BPTT)的变体:网络被展开,如前所述,使用反向传播,对每个时间点的误差进行平均处理(因为每一步都有一个“输出”,即隐藏状态)。⁴⁹此外,在 RNNs 的情况下,我们遇到的问题是网络的记忆非常短暂;它只包含最近单元的信息,而当前单元之前的信息则难以保持长期上下文。对于翻译等应用来说,这显然是一个问题,因为句子末尾的单词的解释可能依赖于句子开头的术语,而不仅仅是直接前面的术语。

LSTM 网络的开发是为了使 RNNs 能够在长序列上保持上下文或状态。⁵⁰

图片

图 3.16:LSTM 网络

在传统的 RNN 中,我们只保留来自前一步隐藏单元激活的短期记忆h。除了这个短期记忆外,LSTM 架构引入了一个额外的层c,即“长期”记忆,它可以持续多个时间步长。从某种意义上说,这种设计让人想起了电容器,它可以使用c层来储存或保持“电荷”,一旦达到某个阈值就释放它。为了计算这些更新,一个 LSTM 单元由许多相关的神经元或门组成,这些门在每个时间步骤上一起作用来转换输入。

给定输入向量x和前一时刻t-1的隐藏状态h,在每个时间步长,LSTM 首先计算了一个值,从 0 到 1 表示c的每个元素中“遗忘”了多少信息:

图片

我们进行第二次类似的计算来确定要保留输入值的哪些部分:

图片

现在我们知道了c的哪些元素被更新了;我们可以计算这个更新如下:

图片

其中图片是一个 Hadamard 积(逐元素乘法)。本质上,这个方程告诉我们如何使用 tanh 变换计算更新,使用输入门过滤它们,并使用忘记门将它们与前一个时间步的长期记忆结合起来,以潜在地过滤掉旧值。

要计算每个时间步的输出,我们计算另一个输出门:

图片

并且在每一步计算最终输出时(隐藏层作为下一步的短期记忆提供给下一步),我们有:

提出了许多这种基本设计的变体;例如,“窥视孔”LSTM 用c(t-1)替代了h(t-1)(因此每个操作都可以“窥视”长期记忆单元),⁵¹而 GRU⁵²通过删除输出门简化了整体设计。这些设计的共同之处在于,它们避免了训练 RNN 时出现的梯度消失(或爆炸)困难,因为长期记忆充当缓冲区,以维持梯度并在许多时间步骤上传播神经元激活。

构建更好的优化器

到目前为止,在本章中,我们已经讨论了几个例子,其中更好的神经网络架构实现了突破;然而,与此同样(甚至更加)重要的是用于在这些问题中最小化误差函数的优化过程,通过选择产生最低误差的参数来“学习”网络的参数。回顾我们对反向传播的讨论,这个问题有两个组成部分:

  • 如何初始化权重:在许多历史应用中,我们看到作者使用了一定范围内的随机权重,并希望通过反向传播的使用从这个随机起始点至少得到一个局部最小化的损失函数。

  • 如何找到局部最小损失:在基本的反向传播中,我们使用梯度下降和固定学习率以及一阶导数更新来遍历权重矩阵的潜在解空间;然而,有充分的理由相信可能存在更有效的方法来找到局部最小值。

事实上,这两者都被证明是深度学习研究进展的关键考虑因素。

梯度下降到 ADAM

正如我们在反向传播的讨论中看到的那样,1986 年提出的用于训练神经网络的原始版本在获取梯度并更新权重之前对整个数据集进行了损失平均。显然,这相当慢,并且使模型的分发变得困难,因为我们无法分割输入数据和模型副本;如果我们使用它们,每个副本都需要访问整个数据集。

相比之下,SGD 在n个样本后计算梯度更新,其中n可以是从 1 到N(数据集的大小)的范围。在实践中,我们通常执行小批量梯度下降,其中n相对较小,而且我们在每个 epoch(数据的一次遍历)后随机分配数据给n批次。

但是,SGD 可能会很慢,导致研究人员提出加速搜索最小值的替代方案。正如在原始反向传播算法中所见,一个想法是使用一种记住先前步骤并在前进方向继续的指数加权动量形式。已经有提出了各种变体,如Nesterov Momentum,它增加了一个项来增加这种加速^(53)。

与原始反向传播算法中使用的动量项相比,将当前动量项加到梯度中有助于保持动量部分与梯度变化保持一致。

另一种优化方法,称为自适应梯度Adagrad)^(54),通过该参数梯度的平方和(G)来缩放每次更新的学习率;因此,经常更新的元素被降采样,而不经常更新的元素被推动以更大的幅度进行更新:

这种方法的缺点是,随着我们继续训练神经网络,总和G将无限增加,最终将学习率缩小到一个非常小的值。为了解决这个缺点,提出了两种变体方法,RMSProp^(55)(经常应用于 RNN)和 AdaDelta^(56),在计算G时加入固定宽度窗口的 n 步。

自适应动量估计ADAM)^(57)可以看作是一种尝试将动量和 AdaDelta 结合起来;动量计算用于保留过去梯度更新的历史,而在 AdaDelta 中使用的固定更新窗口内的衰减平方梯度总和用于调整结果梯度的大小。

这里提到的方法都具有一阶的特性:它们只涉及损失对输入的一阶导数。虽然计算简单,但这可能导致在神经网络参数的复杂解空间中导航时出现实际挑战。如图 3.17所示,如果我们将权重参数的景观视为一条沟壑,那么一阶方法要么在曲率快速变化的区域移动得太快(顶部图像),超调极小值,要么在曲率较低的极小值“沟壑”中移动得太慢。理想的算法将考虑曲率和曲率变化的变化速率,允许优化器顺序方法在曲率变化特别缓慢时采用更大的步长,反之亦然(底部图像)。

图 3.17:复杂的景观和二阶方法^(58)

因为它们利用了导数的改变速率(二阶导数),这些方法被称为二阶,并且在优化神经网络模型中已经取得了一定的成功^(59)。

然而,每次更新所需的计算量比一阶方法大,因为大多数二阶方法涉及大型矩阵求逆(因此内存利用率高),需要近似来使这些方法可扩展。然而,最终,实际优化网络的突破之一不仅来自于优化算法,还包括我们如何初始化模型中的权重。

Xavier 初始化

正如之前所述,在早期研究中,常常用一定范围的随机值初始化神经网络的权重。2006 年在深度置信网络的训练中取得的突破,正如您将在第四章教授网络生成数字中看到的那样,使用了预训练(通过生成建模方法)来在执行标准反向传播之前初始化权重。

如果您曾经在 TensorFlow Keras 模块中使用过一个层,您会注意到层权重的默认初始化是从截断的正态分布或均匀分布中抽取的。这个选择是从哪里来的?正如我之前描述的,使用 S 型或双曲线激活函数的深度网络的一个挑战是,它们倾向于变得饱和,因为这些函数的值受到非常大或负的输入的限制。我们可以解释初始化网络的挑战是保持权重在这样一个范围内,以至于它们不会使神经元的输出饱和。另一种理解方法是假设神经元的输入和输出值具有类似的方差;信号在通过神经元时不会被大幅放大或减小。

在实践中,对于一个线性神经元,y = wx + b,我们可以计算输入和输出的方差为:

b是常数,因此我们剩下:

由于权重矩阵中有N个元素,并且我们希望var(y)等于var(x),这给出了:

因此,对于权重矩阵w,我们可以使用方差为 1/N(输入和输出单元的平均数量,因此权重的数量)的截断正态分布或均匀分布。⁶⁰变体也已经应用于 ReLU 单元:⁶¹这些方法被称为它们原始作者的名字,如 Xavier 或 He 初始化。

总的来说,我们回顾了 TensorFlow 2 中底层使用的几种常见优化器,并讨论了它们如何改进基本的 SGD 形式。我们还讨论了聪明的权重初始化方案如何与这些优化器共同作用,使我们能够训练越来越复杂的模型。

摘要

在本章中,我们涵盖了深度学习的基本词汇 - 如起始对感知器和多层感知器的研究导致了简单的学习规则被放弃,而采用反向传播。我们还研究了专门的神经网络架构,如基于视觉皮层的卷积神经网络(CNNs),以及专门用于序列建模的循环网络。最后,我们检查了最初为反向传播提出的梯度下降算法的变体,这些变体的优点包括动量,并描述了将网络参数放在更容易导航到局部最小值范围的权重初始化方案。

在这种背景下,我们将着手进行生成模型的项目,首先是使用深度信念网络生成 MNIST 数字的项目,见第四章教授网络生成数字

参考文献

  1. López-Muñoz F., Boya J., Alamo C. (2006). 神经元理论,神经科学的基石,颁给圣地亚哥·拉蒙·伊·卡哈尔的诺贝尔奖 100 周年。《大脑研究公报》. 70 (4–6):391–405. pubmed.ncbi.nlm.nih.gov/17027775/

  2. Ramón y Cajal, Santiago (1888). 鸟类中枢神经中枢结构

  3. McCulloch, W.S., Pitts, W. (1943). 神经活动中所固有的思想的逻辑演算。数理生物物理学通报5, 115–133. doi.org/10.1007/BF02478259

  4. 请注意:Rashwan M., Ez R., reheem G. (2017). 阿拉伯语言语音识别的计算智能算法.《开罗大学工程领域杂志》. 12. 886-893. 10.21608/auej.2017.19198. wwwold.ece.utep.edu/research/webfuzzy/docs/kk-thesis/kk-thesis-html/node12.html

  5. Rashwan M., Ez R., reheem G. (2017). 阿拉伯语言语音识别的计算智能算法.《开罗大学工程领域杂志》. 12. 886-893. 10.21608/auej.2017.19198. wwwold.ece.utep.edu/research/webfuzzy/docs/kk-thesis/kk-thesis-html/node12.html

  6. 人工神经元. 维基百科. 检索日期:2021 年 4 月 26 日,网址:en.wikipedia.org/wiki/Artificial_neuron

  7. Shackleton-Jones Nick. (2019 年 5 月 3 日).人们如何学习:设计教育和培训,以提高绩效。Kogan Page。英国伦敦

  8. Hebb, D. O. (1949). 行为组织:神经心理学理论。纽约:Wiley 和 Sons 出版社

  9. Rosenblatt, Frank (1957). 感知器-一个认知和识别自动装置。报告 85-460-1. 康奈尔航空实验室。

  10. Marvin Minsky 和 Seymour Papert,1972 年(第二版,第一版 1969 年)《感知器:计算几何的介绍》,MIT 出版社,剑桥,马萨诸塞州

  11. Hassan, Hassan & Negm, Abdelazim & Zahran, Mohamed & Saavedra, Oliver. (2015). 利用高分辨率卫星图像评估人工神经网络进行浅水湖泊水深估计:以 El Burullus Lake 为例. 国际水技术期刊. 5.

  12. Marvin Minsky 和 Seymour Papert, 1972 (第二版带有更正,第一版 1969) 感知机:计算几何简介, The MIT Press, 剑桥 MA

  13. Pollack, J. B. (1989). "无意伤害:感知机扩展版评论". 数学心理学杂志. 33 (3): 358–365.

  14. Crevier, Daniel (1993), AI:人工智能的动荡探索, 纽约,纽约: BasicBooks.

  15. Cybenko, G. 通过 S 型函数的叠加进行逼近. 数学. 控制信号系统 2, 303–314 (1989). doi.org/10.1007/BF02551274

  16. Goodfellow, Ian; Bengio, Yoshua; Courville, Aaron (2016). 6.5 反向传播和其他差分算法. 深度学习. MIT 出版社. pp. 200–220

  17. Rumelhart, D., Hinton, G. & Williams, R. (1986) 通过反向传播误差学习表示. 自然 323, 533–536. doi.org/10.1038/323533a0

  18. Guess A R., (2015 年 11 月 10 日). Google 开源机器学习库 TensorFlow. DATAVERSITY. www.dataversity.net/google-open-sources-machine-learning-library-tensorflow/

  19. Berland (2007). ReverseaccumulationAD.png. 维基百科. 可从: commons.wikimedia.org/wiki/File:ReverseaccumulationAD.png

  20. 自动微分. 维基百科. en.wikipedia.org/wiki/Automatic_differentiation

  21. R.E. Wengert (1964). 一个简单的自动导数评估程序. Comm. ACM. 7 (8): 463–464.;Bartholomew-Biggs, Michael; Brown, Steven; Christianson, Bruce; Dixon, Laurence (2000). 算法的自动微分. 计算与应用数学杂志. 124 (1–2): 171–190.

  22. TensorFlow 作者 (2018). automatic_differentiation.ipynb. 可从: colab.research.google.com/github/tensorflow/tensorflow/blob/r1.9/tensorflow/contrib/eager/python/examples/notebooks/automatic_differentiation.ipynb#scrollTo=t09eeeR5prIJ

  23. TensorFlow 作者. 梯度和自动微分简介. TensorFlow. 可从: www.tensorflow.org/guide/autodiff

  24. Thomas (2018). 梯度消失问题和 ReLU – TensorFlow 调查. 机器学习冒险。查阅:adventuresinmachinelearning.com/vanishing-gradient-problem-tensorflow/

  25. Hinton, Osindero, Yee-Whye (2005). 深度信念网络的快速学习算法. 多伦多大学,计算机科学。查阅:www.cs.toronto.edu/~fritz/absps/ncfast.pdf

  26. Cortes, C., Vapnik, V. 支持向量网络. 机器学习 20, 273–297 (1995). doi.org/10.1007/BF00994018

  27. Friedman, J. H. (February 1999). 贪婪函数逼近:梯度增强机 (PDF)

  28. Breiman, L. 随机森林. 机器学习 45, 5–32 (2001). doi.org/10.1023/A:1010933404324

  29. Tibshirani R. (1996). 通过套索实现回归收缩和选择. 英国皇家统计学会杂志。Wiley. 58 (1): 267–88.

  30. Zou H., Hastie T. (2005). 通过弹性网络实现正规化和变量选择. 英国皇家统计学会杂志 B 系列:301–320

  31. Hubel D. H., Wiesel T. N. (1962) 感觉野,视交互及猫脑视觉皮层功能体系结构. 生理学杂志,1962, 160: 106-154。doi.org/10.1113/jphysiol.1962.sp006837

  32. charlesfrye.github.io/FoundationalNeuroscience/img/corticalLayers.gif

  33. Wolfe, Kluender, Levy (2009). 感知和知觉. 坎伯兰:Sinauer Associates Inc.。

  34. LeCun, Yann, et al. 反向传播应用于手写邮政编码识别. 神经计算,1.4 (1989): 541-551.

  35. LeCun, Yann, et al. 反向传播应用于手写邮政编码识别. 神经计算,1.4 (1989): 541-551.

  36. 使用深度卷积神经网络进行 ImageNet 分类www.nvidia.cn/content/tesla/pdf/machine-learning/imagenet-classification-with-deep-convolutional-nn.pdf

  37. Nair V., Hinton G E. (2010). 修正线性单元改进限制玻尔兹曼机. 机器学习国际会议论文集,2010 年,以色列海法。

  38. Agarap A F. (2019, September 5). 通过梯度噪音添加来避免伴随梯度消失的问题. 朝着数据科学。towardsdatascience.com/avoiding-the-vanishing-gradients-problem-96183fd03343

  39. Maas A L., Hannun A Y., Ng A Y. (2013). 修正线性非线性改进神经网络声学模型. 机器学习国际会议论文集,2013 年,美国佐治亚州亚特兰大市。

  40. He,K.,Zhang,X.,Ren,S.,Sun,J.(2015)。 深入挖掘整流器:在 ImageNet 分类上超越人类水平性能。 arXiv:1502.01852。arxiv.org/abs/1502.01852

  41. Hinton,G E.,Srivastava,N.,Krizhevsky,A.,Sutskever,I.,Salakhutdinov,R R.(2012)。 通过防止特征检测器的协同适应来改进神经网络。 arXiv:1207.0580。arxiv.org/abs/1207.0580

  42. Krizhevsky A.,Sutskever I.,Hinton G E.(2012)。 使用深度卷积神经网络的 ImageNet 分类。神经信息处理系统 25(NIPS 2012)的一部分。papers.nips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf

  43. Ioffe,S.,Szegedy,C.(2015)。 批量归一化:通过减少内部协变量转移加速深层网络训练。 arXiv:1502.03167。 arxiv.org/abs/1502.03167

  44. Santurkar,S。,Tsipras,D。,Ilyas,A。,Madry,A.(2019)。 批量归一化如何帮助优化? arXiv:1805.11604。 arxiv.org/abs/1805.11604

  45. Dean J.,Ng,A Y.(2012)。 使用大规模脑模拟进行机器学习和人工智能。The Keyword | Google。blog.google/technology/ai/using-large-scale-brain-simulations-for/

  46. Rumelhart,D.,Hinton,G.和 Williams,R.(1986 年)通过反向传播错误学习表示自然 323,533–536。 doi.org/10.1038/323533a0

  47. LeCun,Y.,Bengio,Y.和 Hinton G.(2015)。 深度学习自然 521,436–444。 www.nature.com/articles/nature14539.epdf

  48. Olah(2015 年)。 理解 LSTM 网络. colah 的博客。可从colah.github.io/posts/2015-08-Understanding-LSTMs/获取。

  49. Mozer,M. C.(1995)。 用于时间模式识别的聚焦反向传播算法。在 Chauvin,Y .; Rumelhart,D.(eds。)。 反向传播:理论,体系结构和应用。 ResearchGate。 Hillsdale,NJ:劳伦斯 Erlbaum 凯斯。第 137-169 页。

  50. Greff K.,Srivastava,R K。,Koutník,J.,Steunebrink,B R。,Schmidhuber,J.(2017)。 LSTM:搜索空间奥德赛。 arXiv:1503.04069v2。 arxiv.org/abs/1503.04069v2

  51. Gers FA, Schmidhuber E. LSTM 循环网络学习简单的无上下文和有上下文的语言. IEEE 交易神经网络。 2001 年;12(6):1333-40. doi:10.1109/72.963769。 PMID:18249962。

  52. Cho, K., van Merrienboer, B., Gulcehre, C., Bahdanau, D., Bougares, F., Schwenk, H., Bengio, Y. (2014). 使用 RNN 编码器-解码器学习短语表示用于统计机器翻译。arXiv:1406.1078。arxiv.org/abs/1406.1078

  53. Sutskever, I., Martens, J., Dahl, G. & Hinton, G. (2013). 初始化和动量在深度学习中的重要性。第 30 届国际机器学习大会论文集, PMLR 28(3):1139-1147.

  54. Duchi J., Hazan E., Singer Y. (2011). 用于在线学习和随机优化的自适应次梯度方法。机器学习研究杂志 12 (2011) 2121-2159.

  55. Hinton, Srivastava, Swersky. 神经网络用于机器学习,第 6a 讲。可从:www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf

  56. Zeiler, M D. (2012). ADADELTA:一种自适应学习率方法。arXiv:1212.5701。arxiv.org/abs/1212.5701

  57. Kingma, D P., Ba, J. (2017). Adam:一种随机优化方法。arXiv:1412.6980。arxiv.org/abs/1412.6980

  58. Martens J. (2010). 通过无 Hessian 优化的深度学习。ICML. Vol. 27. 2010.

  59. Martens J. (2010). 通过无 Hessian 优化的深度学习。ICML. Vol. 27. 2010.

  60. Glorot X., Bengio Y., (2010). 理解训练深度前馈神经网络的困难。第十三届人工智能与统计国际会议论文集。

  61. He, K., Zhang, X., Ren, S., Sun, J. (2015). 深入研究整流器:在 ImageNet 分类上超越人类水平性能。arXiv:1502.01852。arxiv.org/abs/1502.01852

posted @ 2024-04-30 11:43  绝不原创的飞龙  阅读(5)  评论(0编辑  收藏  举报