Python 与 TensorFlow2 生成式 AI(一)(2)https://developer.aliyun.com/article/1512062
总结
在本章中,我们概述了 TensorFlow 是什么,以及它如何作为深度学习研究的改进,我们还探讨了设置 IDE、VSCode 和可重现应用程序的基础,Docker 容器。为了编排和部署 Docker 容器,我们讨论了 Kubernetes 框架,以及如何使用其 API 扩展容器组。最后,我描述了 Kubeflow,一个建立在 Kubernetes 上的机器学习框架,它允许我们运行端到端的流水线、分布式训练和参数搜索,并为训练后的模型提供服务。然后,我们使用 Terraform,一种 IaaS 技术,设置了 Kubeflow 部署。
在深入具体项目之前,我们将介绍神经网络理论的基础知识以及你需要编写基本训练作业的 TensorFlow 和 Keras 命令,在 Kubeflow 上。
参考资料
- Abadi, Martín 等(2016 年)TensorFlow:异构分布式系统上的大规模机器学习。arXiv:1603.04467。
arxiv.org/abs/1603.04467
。 - 谷歌。TensorFlow。检索日期为 2021 年 4 月 26 日,网址:
www.tensorflow.org/
- MATLAB,马萨诸塞州南提克:The MathWorks Inc。
www.mathworks.com/products/matlab.html
- Krizhevsky A., Sutskever I., & Hinton G E. 使用深度卷积神经网络的 ImageNet 分类。
papers.nips.cc/paper/4824-imagenet-classification-with-deepconvolutional-neural-networks.pdf
- Dean J., Ng A. (2012 年 6 月 26 日)。利用大规模脑模拟进行机器学习和 AI。Google | The Keyword。
blog.google/technology/ai/using-large-scale-brain-simulations-for/
- Mnih, V., Kavukcuoglu, K., Silver, D., Graves, A., Antonoglou, I., Wierstra, D., Riedmiller, M. (2013)。使用深度强化学习玩 Atari 游戏。arXiv:1312.5602。
arxiv.org/abs/1312.5602
- 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/
- Devlin, J., Chang, M. W., Lee, K., & Toutanova, K. (2018)。Bert:用于语言理解的深度双向 transformers 的预训练。arXiv:1810.04805。
arxiv.org/abs/1810.04805
- Al-Rfou, R.,等人 (2016)。Theano:快速计算数学表达的 Python 框架。arXiv。
arxiv.org/pdf/1605.02688.pdf
- Collobert R., Kavukcuoglu K., & Farabet C. (2011)。Torch7:一个类似 Matlab 的机器学习环境。
ronan.collobert.com/pub/matos/2011_torch7_nipsw.pdf
- Abadi M.,等人 (2015)。TensorFlow:异构分布式系统上的大规模机器学习。download.tensorflow.org/paper/whitepaper2015.pdf
- Abadi, Martín,等人 (2016)。TensorFlow:异构分布式系统上的大规模机器学习。arXiv:1603.04467。
arxiv.org/abs/1603.04467
- Jouppi, N P,等人 (2017)。数据中心张量处理单元的性能分析。arXiv:1704.04760。
arxiv.org/abs/1704.04760
- 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
stackoverflow.com/questions/57273888/keras-vs-TensorFlow-code-comparison-sources
- Harris M. (2016). Docker vs. 虚拟机. Nvidia developer blog.
developer.nvidia.com/blog/nvidia-docker-gpu-server-application-deployment-made-easy/vm_vs_docker/
- 一个视觉双关语 - 该项目的原始代码名称为 Seven of Nine,来自电视剧星际迷航:航海家号中的博格角色。
- Kubernetes 组件。 (2021 年 3 月 18 日) Kubernetes.
kubernetes.io/docs/concepts/overview/components/
- 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
- 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.1和3.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。 如果阈值值T
为1
,那么输入(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 到调谐感知器
除了这些有关表示XOR
和XNOR
操作的限制外,还有一些附加的简化会限制 TLU 模型的表达能力;权重是固定的,输出只能是二进制(0 或 1)。显然,对于像神经元这样的系统来说,“学习”需要对环境做出响应,并根据先前的经验反馈确定不同输入的相关性。这个观点在加拿大心理学家唐纳德·赫布(Donald Hebb)1949 年的著作《行为的组织》中有所体现,他提出,附近的神经细胞的活动随着时间会趋同,有时被简化为赫布定律:“放电在一起联结在一起”^(7 8)。基于赫布的权重随时间变化的提议,康奈尔航空实验室的研究员弗兰克·罗森布拉特(Frank Rosenblatt)在 1950 年代提出了感知器(perceptron)模型。⁹ 他用自适应权重替代了 TLU 模型中的固定权重,并增加了偏置项,得到了一个新的函数:
我们注意到,输入I已被标记为X以突显它们可以是任何值,而不仅仅是二进制0
或1
。将赫布的观察与 TLU 模型相结合,感知器的权重将根据简单的学习规则进行更新:
- 从一组 J 个样本x(1) …. x(j)出发。这些样本都有标签 y,可以是 0 或 1,提供有标记的数据(y, x)(1) …. (y, x)(j)。这些样本可以是单个值,此时感知器有单个输入,也可以是长度为N且具有* i*的多值输入的向量。
- 初始化所有权重w为小的随机值或 0。
- 使用感知器函数计算所有示例x的估计值yhat。
- 使用学习速率r更新权重,以更接近于每一步t中训练的期望输出值:
,对于所有的J个样本和N个特征。概念上,需要注意如果y为 0 且目标值为 1,我们希望通过一定的增量r增加权重的值;同样,如果目标值为 0 且估计值为 1,我们希望减小权重,使得输入值不超过阈值。 - 重复步骤 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 的偏导数:和,这将使我们能够计算出b和w的更新。为了实现这个目标,让我们从计算最终神经元输入的更新规则开始;我们希望使用链式规则来计算误差E对于每个这些输入的偏导数(在本例中有五个,对应于五个隐藏层神经元):
我们可以通过对损失函数求导来得到值:
对于单个示例,这只是输入和输出值之间的差异。对于,我们需要对 Sigmoid 函数进行偏导数:
综上所述,我们有:
如果我们想要计算特定参数x(如权重w或偏置项b)的梯度,我们需要多做一步:
我们已经知道第一项,且x仅通过来自下层y的输入依赖于w,因为这是一个线性函数,所以我们得到:
如果我们想为隐藏层中的一个神经元计算此导数,我们以同样的方式对这个输入y[i]进行偏导数计算,这很简单:
因此,我们总共可以对所有输入到这个隐藏层的单元求和:
我们可以递归地重复这个过程以获得所需的更新规则,因为我们现在知道如何在任何层计算y或w的梯度。这使得更新权重的过程变得高效,因为一旦我们通过反向传播计算了梯度,我们就可以结合连续的梯度通过层来得到网络任何深度所需的梯度。
现在,我们已经得到了每个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
Python 与 TensorFlow2 生成式 AI(一)(4)https://developer.aliyun.com/article/1512064