diff --git a/README.md b/README.md index 0cfb3e9..d7ff4c1 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ | 邹雨衡 | 第3章贡献者 | || | 陈嘉诺 | 第4章贡献者 | 广州大学 |[@Tangent-90C](https://github.com/Tangent-90C)| | 高立业 | 第4章贡献者 | || +| 蒋文力 | 第4章贡献者 | |[@morcake](https://github.com/morcake)| | 丁悦 | 第5章贡献者 | 哈尔滨工业大学(威海)|[@dingyue772](https://github.com/dingyue772)| | 周景林 | 附录贡献者 | |[@Beyondzjl](https://github.com/Beyondzjl)| | 陈可为 | 附录贡献者 | |[@Ethan-Chen-plus](https://github.com/Ethan-Chen-plus)| diff --git a/Translated_Book/ch03/3.1.ipynb b/Translated_Book/ch03/3.1.ipynb new file mode 100644 index 0000000..1623f0e --- /dev/null +++ b/Translated_Book/ch03/3.1.ipynb @@ -0,0 +1,245 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3a1575c0", + "metadata": {}, + "source": [ + "# 第三章 编码注意力机制" + ] + }, + { + "cell_type": "markdown", + "id": "66113cf6", + "metadata": {}, + "source": [ + "**本章介绍**: " + ] + }, + { + "cell_type": "markdown", + "id": "51eda009", + "metadata": {}, + "source": [ + "- 探索在神经网络中使用注意力机制的原因 \n", + "- 引入基本的自我注意力框架并逐步发展到增强的自我注意力机制 \n", + "- 实现因果注意力模块,允许 LLM 一次生成一个令牌 \n", + "- 用丢弃来掩盖随机选择的注意力权重,以减少过度拟合 \n", + "- 将多个因果注意力模块堆叠到一个多头注意力模块中" + ] + }, + { + "cell_type": "markdown", + "id": "70639a43", + "metadata": {}, + "source": [ + "​\t\t在上一章中,你学习了如何准备用于训练 LLM 的输入文本。这涉及将文本拆分为单独的单词和子单词标记,这些标记可以编码为LLM的向量表示,即所谓的嵌入。" + ] + }, + { + "cell_type": "markdown", + "id": "e27240ef", + "metadata": {}, + "source": [ + "​\t\t在本章中,我们现在将研究 LLM 架构本身的一个组成部分,即注意力机制,如图 3.1 所示。" + ] + }, + { + "cell_type": "markdown", + "id": "96bed9d4", + "metadata": {}, + "source": [ + "图 3.1 对 LLM 进行编码的三个主要阶段的心智模型,在通用文本数据集上预训练 LLM,并在标记数据集上对其进行微调。本章重点介绍注意力机制,它是 LLM 架构的一个组成部分。" + ] + }, + { + "cell_type": "markdown", + "id": "6110037d", + "metadata": {}, + "source": [ + "![image-20240422132155860](../img/fig-3-1.png)" + ] + }, + { + "cell_type": "markdown", + "id": "c881b69f", + "metadata": {}, + "source": [ + "图 3.1 对 LLM 进行编码的三个主要阶段的心智模型,在通用文本数据集上预训练 LLM,并在标记数据集上对其进行微调。本章重点介绍注意力机制,它是 LLM 架构的一个组成部分。" + ] + }, + { + "cell_type": "markdown", + "id": "12929d08", + "metadata": {}, + "source": [ + "​\t\t注意力机制是一个综合性的话题,这就是为什么我们用一整章来讨论它。我们将在很大程度上孤立地看待这些注意力机制,并在机制层面上关注它们。在下一章中,我们将围绕自注意力机制对 LLM 的其余部分进行编码,以查看它的实际效果并创建一个模型来生成文本。" + ] + }, + { + "cell_type": "markdown", + "id": "1a46364a", + "metadata": {}, + "source": [ + "​\t\t在本章中,我们将实现四种不同的注意力机制变体,如图 3.2 所示。" + ] + }, + { + "cell_type": "markdown", + "id": "4d7832f4", + "metadata": {}, + "source": [ + "图 3.2 该图描述了我们将在本章中编写的不同注意力机制,从简化版本的自我注意力开始,然后添加可训练的权重。因果注意机制为自我注意力添加了一个掩码,允许 LLM 一次生成一个单词。最后,多头注意力将注意力机制组织成多个头,使模型能够并行捕获输入数据的各个方面。" + ] + }, + { + "cell_type": "markdown", + "id": "722bd9ac", + "metadata": {}, + "source": [ + "![image-20240422132325918](../img/fig-3-2.png)" + ] + }, + { + "cell_type": "markdown", + "id": "8f7c69a1", + "metadata": {}, + "source": [ + "图 3.2 该图描述了我们将在本章中编写的不同注意力机制,从简化版本的自我注意力开始,然后添加可训练的权重。因果注意机制为自我注意力添加了一个掩码,允许 LLM 一次生成一个单词。最后,多头注意力将注意力机制组织成多个头,使模型能够并行捕获输入数据的各个方面。" + ] + }, + { + "cell_type": "markdown", + "id": "e5d44ac0", + "metadata": {}, + "source": [ + "​\t\t图 3.2 中所示的这些不同的注意力变体是相互构建的,目标是在本章末尾实现一个紧凑而高效的多头注意力实现,然后我们可以将其插入到我们将在下一章中编写的 LLM 架构中。" + ] + }, + { + "cell_type": "markdown", + "id": "2ce0f1ad", + "metadata": {}, + "source": [ + "## 3.1 长序列建模的问题" + ] + }, + { + "cell_type": "markdown", + "id": "f0236642", + "metadata": {}, + "source": [ + "​\t\t在本章后面深入探讨 LLM 核心的自注意力机制之前,在 LLM 之前没有注意力机制的架构有什么问题?假设我们想要开发一个语言翻译模型,将文本从一种语言翻译成另一种语言。如图 3.3 所示,由于源语言和目标语言的语法结构,我们不能简单地逐字翻译文本。" + ] + }, + { + "cell_type": "markdown", + "id": "a7aa5953", + "metadata": {}, + "source": [ + "图 3.3 将文本从一种语言翻译成另一种语言时,例如德语翻译成英语时,不可能只是逐字翻译。相反,翻译过程需要上下文理解和语法对齐。" + ] + }, + { + "cell_type": "markdown", + "id": "273dc1ef", + "metadata": {}, + "source": [ + "![image-20240422132534189](../img/fig-3-3.png)" + ] + }, + { + "cell_type": "markdown", + "id": "84566bb5", + "metadata": {}, + "source": [ + "​\t\t为了解决我们无法逐字翻译文本的问题,通常使用具有两个子模块的深度神经网络,即所谓的编码器和解码器。编码器的工作是首先读取并处理整个文本,然后解码器生成翻译后的文本。" + ] + }, + { + "cell_type": "markdown", + "id": "f8211d92", + "metadata": {}, + "source": [ + "​\t\t在第 1 章(第 1.4 节,将 LLM 用于不同的任务)中介绍 transformer 架构时,我们已经简要讨论了编码器-解码器网络。在 Transformer 出现之前,递归神经网络 (RNN) 是语言翻译中最流行的编码器-解码器架构。" + ] + }, + { + "cell_type": "markdown", + "id": "79004eae", + "metadata": {}, + "source": [ + "​\t\tRNN 是一种神经网络,其中先前步骤的输出作为输入馈送到当前步骤,使其非常适合文本等顺序数据。如果您不熟悉 RNN,请不要担心,您无需了解 RNN 的详细工作原理即可进行此讨论;我们在这里的重点更多地放在编码器-解码器设置的一般概念上。" + ] + }, + { + "cell_type": "markdown", + "id": "e7bb634b", + "metadata": {}, + "source": [ + "​\t\t在编码器-解码器 RNN 中,输入文本被馈送到编码器中,编码器按顺序对其进行处理。编码器在每个步骤中更新其隐藏状态(隐藏层的内部值),试图在最终隐藏状态下捕获输入句子的全部含义,如图 3.4 所示。然后,解码器采用这种最终的隐藏状态开始生成翻译的句子,一次一个单词。它还会在每一步更新其隐藏状态,这应该携带下一个单词预测所需的上下文。" + ] + }, + { + "cell_type": "markdown", + "id": "03937031", + "metadata": {}, + "source": [ + "图 3.4 在变压器模型出现之前,编码器-解码器 RNN 是机器翻译的热门选择。编码器将源语言中的一系列标记作为输入,其中编码器的隐藏状态(中间神经网络层)对整个输入序列的压缩表示进行编码。然后,解码器使用其当前隐藏状态开始逐个标记的转换。" + ] + }, + { + "cell_type": "markdown", + "id": "fb703346", + "metadata": {}, + "source": [ + "![image-20240422132721999](../img/fig-3-4.png)" + ] + }, + { + "cell_type": "markdown", + "id": "311eba8a", + "metadata": {}, + "source": [ + "​\t\t虽然我们不需要知道这些编码器-解码器 RNN 的内部工作原理,但这里的关键思想是编码器部分将整个输入文本处理成隐藏状态(存储单元)。然后,解码器采用此隐藏状态以生成输出。你可以把这个隐藏状态看作是一个嵌入向量,这是我们在第 2 章中讨论的一个概念。" + ] + }, + { + "cell_type": "markdown", + "id": "afb6a419", + "metadata": {}, + "source": [ + "​\t\t编码器-解码器 RNN 的最大问题和局限性在于,在解码阶段,RNN 无法直接从编码器访问早期的隐藏状态。因此,它完全依赖于当前的隐藏状态,该状态封装了所有相关信息。这可能会导致上下文丢失,尤其是在依赖关系可能跨越很长距离的复杂句子中。" + ] + }, + { + "cell_type": "markdown", + "id": "d1da34f5", + "metadata": {}, + "source": [ + "​\t\t对于不熟悉 RNN 的读者来说,理解或研究这种架构并不是必需的,因为我们不会在本书中使用它。本节的要点是,编码器-解码器 RNN 有一个缺点,激发了注意力机制的设计。" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Translated_Book/ch03/3.2.ipynb b/Translated_Book/ch03/3.2.ipynb new file mode 100644 index 0000000..728bcd8 --- /dev/null +++ b/Translated_Book/ch03/3.2.ipynb @@ -0,0 +1,113 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2c3f5f82", + "metadata": {}, + "source": [ + "## 3.2 使用注意力机制捕获数据依赖关系" + ] + }, + { + "cell_type": "markdown", + "id": "fd722cbb", + "metadata": {}, + "source": [ + "​\t\t如前所述,在 transformer LLM 之前,通常将 RNN 用于语言建模任务,例如语言翻译。RNN 适用于翻译短句,但不适用于较长的文本,因为它们无法直接访问输入中的先前单词。" + ] + }, + { + "cell_type": "markdown", + "id": "253cd985", + "metadata": {}, + "source": [ + "​\t\t这种方法的一个主要缺点是,RNN 在将整个编码输入传递到解码器之前,必须记住处于单个隐藏状态的整个编码输入,如上一节中的图 3.4 所示。" + ] + }, + { + "cell_type": "markdown", + "id": "c6d08f54", + "metadata": {}, + "source": [ + "​\t\t因此,研究人员在 2014 年开发了所谓的 RNN 的 Bahdanau 注意力机制(以相应论文的第一作者命名),该机制修改了编码器-解码器 RNN,以便解码器可以在每个解码步骤中选择性地访问输入序列的不同部分,如图 3.5 所示。" + ] + }, + { + "cell_type": "markdown", + "id": "e8421d19", + "metadata": {}, + "source": [ + "图 3.5 使用注意力机制,网络的文本生成解码器部分可以有选择地访问所有输入令牌。这意味着,对于生成给定的输出令牌,某些输入令牌比其他输入令牌更重要。重要性由所谓的注意力权重决定,我们稍后将计算。请注意,此图显示了注意力背后的一般思想,并未描述Bahdanau机制的确切实现,这是本书范围之外的RNN方法。" + ] + }, + { + "cell_type": "markdown", + "id": "553837ba", + "metadata": {}, + "source": [ + "![image-20240422132957951](../img/fig-3-5.png)" + ] + }, + { + "cell_type": "markdown", + "id": "7f381cb8", + "metadata": {}, + "source": [ + "​\t\t有趣的是,仅仅三年后,研究人员发现RNN架构对于构建用于自然语言处理的深度神经网络并不需要,并提出了原始的Transformer架构(在第1章中讨论),其自注意力机制的灵感来自Bahdanau注意力机制。" + ] + }, + { + "cell_type": "markdown", + "id": "a986f2a9", + "metadata": {}, + "source": [ + "​\t\t自注意力是一种机制,它允许输入序列中的每个位置在计算序列的表示时关注同一序列中的所有位置。自我注意力是基于 transformer 架构的当代 LLM 的关键组成部分,例如 GPT 系列。" + ] + }, + { + "cell_type": "markdown", + "id": "187245d5", + "metadata": {}, + "source": [ + "​\t\t本章重点介绍如何编码和理解类 GPT 模型中使用的这种自注意力机制,如图 3.6 所示。在下一章中,我们将对 LLM 的其余部分进行编码。" + ] + }, + { + "cell_type": "markdown", + "id": "30fb7d25", + "metadata": {}, + "source": [ + "图 3.6 自注意力是变压器中的一种机制,用于计算更有效的输入表示,允许序列中的每个位置与同一序列中的所有其他位置交互并权衡其重要性。在本章中,我们将从头开始编写这种自注意力机制,然后在下一章中对类似 GPT 的 LLM 的其余部分进行编码。" + ] + }, + { + "cell_type": "markdown", + "id": "9c034d79", + "metadata": {}, + "source": [ + "![image-20240422133126835](../img/fig-3-6.png)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Translated_Book/ch04/4.1.ipynb b/Translated_Book/ch04/4.1.ipynb new file mode 100644 index 0000000..0ff9160 --- /dev/null +++ b/Translated_Book/ch04/4.1.ipynb @@ -0,0 +1,464 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bae559a1", + "metadata": {}, + "source": [ + "# 第四章 从头开始实现 GPT 模型以生成文本" + ] + }, + { + "cell_type": "markdown", + "id": "e3b02c54", + "metadata": {}, + "source": [ + "**本章介绍**:" + ] + }, + { + "cell_type": "markdown", + "id": "65964c57", + "metadata": {}, + "source": [ + "- 编写类似 GPT 的大型语言模型 (LLM) 编码,该模型可以训练生成类似人类的文本 \n", + "- 规范化层激活以稳定神经网络训练 \n", + "- 在深度神经网络中添加快捷方式连接以更有效地训练模型 \n", + "- 实现 transformer 模块以创建各种大小的 GPT 模型 \n", + "- 计算 GPT 模型的参数数量和存储需求" + ] + }, + { + "cell_type": "markdown", + "id": "73c209fb", + "metadata": {}, + "source": [ + "​\t\t在上一章中,你学习并编写了多头注意力机制,这是 LLM 的核心组件之一。在本章中,我们现在将对 LLM 的其他构建块进行编码,并将它们组装成一个类似 GPT 的模型,我们将在下一章中训练该模型以生成类似人类的文本,如图 4.1 所示。" + ] + }, + { + "cell_type": "markdown", + "id": "3c5efc3f", + "metadata": {}, + "source": [ + "图 4.1 对 LLM 进行编码的三个主要阶段的心智模型,在通用文本数据集上预训练 LLM,并在标记数据集上对其进行微调。本章重点介绍如何实现 LLM 架构,我们将在下一章中对其进行培训。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "5f4fdd61", + "metadata": {}, + "source": [ + "![image-20240422133749839](../img/fig-4-1.png)" + ] + }, + { + "cell_type": "markdown", + "id": "783519e0", + "metadata": {}, + "source": [ + "​\t\t图 4.1 中引用的 LLM 架构由几个构建块组成,我们将在本章中实现这些构建块。在下一节中,我们将从模型架构的自上而下的视图开始,然后再更详细地介绍各个组件。" + ] + }, + { + "cell_type": "markdown", + "id": "708fa8b4", + "metadata": {}, + "source": [ + "## 4.1 编写 LLM 架构" + ] + }, + { + "cell_type": "markdown", + "id": "1beee80f", + "metadata": {}, + "source": [ + "​\t\tLLM,例如 GPT(代表 Generative Pretrained Transformer),是大型深度神经网络架构,旨在一次生成一个单词(或标记)的新文本。然而,尽管它们的规模很大,但模型架构并没有你想象的那么复杂,因为它的许多组件都是重复的,我们将在后面看到。图 4.2 提供了类似 GPT 的 LLM 的自上而下的视图,其中突出显示了其主要组件。" + ] + }, + { + "cell_type": "markdown", + "id": "f58ad472", + "metadata": {}, + "source": [ + "图 4.2 GPT 模型的心智模型。在嵌入层旁边,它由一个或多个变压器模块组成,其中包含我们在上一章中实现的掩蔽多头注意力模块。" + ] + }, + { + "cell_type": "markdown", + "id": "ee7b74da", + "metadata": {}, + "source": [ + "![image-20240422133908887](../img/fig-4-2.png)" + ] + }, + { + "cell_type": "markdown", + "id": "f7237970", + "metadata": {}, + "source": [ + "​\t\t如图 4.2 所示,我们已经介绍了几个方面,例如输入标记化和嵌入,以及屏蔽的多头注意力模块。本章的重点将放在实现 GPT 模型的核心结构上,包括它的 transformer 模块,然后我们将在下一章中训练它以生成类似人类的文本。" + ] + }, + { + "cell_type": "markdown", + "id": "542d3ae9", + "metadata": {}, + "source": [ + "​\t\t在前几章中,为了简单起见,我们使用了较小的嵌入维度,确保概念和示例可以舒适地放在一个页面上。现在,在本章中,我们将扩展到一个小型 GPT-2 模型的大小,特别是具有 1.24 亿个参数的最小版本,正如 Radford 等人的论文“语言模型是无监督的多任务学习者”中所描述的那样。请注意,虽然原始报告提到了 1.17 亿个参数,但后来已更正。" + ] + }, + { + "cell_type": "markdown", + "id": "401bb3ab", + "metadata": {}, + "source": [ + "​\t\t第 6 章将重点介绍如何将预训练的权重加载到我们的实现中,并将其调整为具有 345、762 和 15.42 亿个参数的大型 GPT-2 模型。在深度学习和 GPT 等 LLM 的上下文中,术语“参数”是指模型的可训练权重。这些权重本质上是模型的内部变量,在训练过程中进行调整和优化,以最小化特定的损失函数。这种优化允许模型从训练数据中学习。" + ] + }, + { + "cell_type": "markdown", + "id": "b075ff94", + "metadata": {}, + "source": [ + "​\t\t例如,在由 2,048x2,048 维权重矩阵(或张量)表示的神经网络层中,该矩阵的每个元素都是一个参数。由于有 2,048 行和 2,048 列,因此该图层中的参数总数为 2,048 乘以 2,048,等于 4,194,304 个参数。" + ] + }, + { + "cell_type": "markdown", + "id": "7a9a4bae", + "metadata": {}, + "source": [ + "**GPT-2 与 GPT-3**" + ] + }, + { + "cell_type": "markdown", + "id": "3f0ffc50", + "metadata": {}, + "source": [ + "​\t\t请注意,我们之所以关注 GPT-2,是因为 OpenAI 已经公开了预训练模型的权重,我们将在第 6 章将其加载到我们的实现中。GPT-3 在模型架构方面基本相同,只是它从 GPT-2 的 15 亿个参数扩展到 GPT-3 的 1750 亿个参数,并且它使用更多的数据进行训练。在撰写本文时,GPT-3 的权重尚未公开。GPT-2 也是学习如何实现 LLM 的更好选择,因为它可以在一台笔记本电脑上运行,而 GPT-3 需要 GPU 集群进行训练和推理。根据 Lambda Labs 的数据,在单个 V100 数据中心 GPU 上训练 GPT-3 需要 355 年,在消费级 RTX 8000 GPU 上训练 GPT-3 需要 665 年。" + ] + }, + { + "cell_type": "markdown", + "id": "47fa26bc", + "metadata": {}, + "source": [ + "​\t\t我们通过以下 Python 字典指定小型 GPT-2 模型的配置,我们将在后面的代码示例中使用该字典:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb54f9ff", + "metadata": {}, + "outputs": [], + "source": [ + "GPT_CONFIG_124M = {\n", + "\t\"vocab_size\": 50257, # Vocabulary size\n", + " \"context_length\": 1024, # Context length\n", + " \"emb_dim\": 768, # Embedding dimension\n", + " \"n_heads\": 12, # Number of attention heads\n", + " \"n_layers\": 12, # Number of layers\n", + " \"drop_rate\": 0.1, # Dropout rate\n", + " \"qkv_bias\": False # Query-Key-Value bias\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "9c5bdac9", + "metadata": {}, + "source": [ + "​\t\t在GPT_CONFIG_124M词典中,为了清楚起见,我们使用简洁的变量名称,并防止长代码行:" + ] + }, + { + "cell_type": "markdown", + "id": "5578f3e3", + "metadata": {}, + "source": [ + "- “vocab_size”是指 50,257 个单词的词汇表,由第 2 章中的 BPE 分词器使用。\n", + "- “context_length”表示模型通过第 2 章中讨论的位置嵌入可以处理的最大输入标记数。\n", + "- “emb_dim”表示嵌入大小,将每个标记转换为 768 维向量。\n", + "- “n_heads”表示第3章中实现的多头注意力机制中的注意力头计数。\n", + "- “n_layers”指定模型中变压器块的数量,这将在后面的章节中详细阐述。\n", + "- “drop_rate”表示压差机制的强度(0.1 表示隐藏单位下降 10%),以防止过拟合,如第 3 章所述。\n", + "- “qkv_bias”确定是否在多头注意力的线性层中包含偏向量,以进行查询、键和值计算。按照现代 LLM 的规范,我们最初将禁用它,但当我们将 OpenAI 的预训练 GPT-2 权重加载到我们的模型中时,我们将在第 6 章中重新审视它。" + ] + }, + { + "cell_type": "markdown", + "id": "fc5fd3bf", + "metadata": {}, + "source": [ + "​\t\t使用上面的配置,我们将通过实现本节中的 GPT 占位符架构 (DummyGPTModel) 来开始本章,如图 4.3 所示。这将为我们提供一个全局视图,了解所有内容如何组合在一起,以及我们需要在即将到来的部分中编写哪些其他组件来组装完整的 GPT 模型架构。" + ] + }, + { + "cell_type": "markdown", + "id": "67566077", + "metadata": {}, + "source": [ + "图 4.3 一个心智模型,概述了我们对 GPT 架构进行编码的顺序。在本章中,我们将从 GPT 主干网(占位符架构)开始,然后再讨论各个核心部分,并最终将它们组装到最终 GPT 架构的 transformer 模块中。" + ] + }, + { + "cell_type": "markdown", + "id": "64a4b7c6", + "metadata": {}, + "source": [ + "![image-20240422134328260](../img/fig-4-3.png)" + ] + }, + { + "cell_type": "markdown", + "id": "dc685535", + "metadata": {}, + "source": [ + "​\t\t图 4.3 中所示的编号框说明了我们处理编码最终 GPT 架构所需的各个概念的顺序。我们将从第 1 步开始,一个占位符 GPT 主干,我们称之为 DummyGPTModel:" + ] + }, + { + "cell_type": "markdown", + "id": "f1f01dad", + "metadata": {}, + "source": [ + "**Listing 4.1 占位符 GPT 模型架构类**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1406a604", + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "class DummyGPTModel(nn.Module):\n", + " def __init__(self, cfg):\n", + " super().__init__()\n", + " self.tok_emb = nn.Embedding(cfg[\"vocab_size\"], cfg[\"emb_dim\"])\n", + " self.pos_emb = nn.Embedding(cfg[\"context_length\"], cfg[\"emb_dim\"])\n", + " self.drop_emb = nn.Dropout(cfg[\"drop_rate\"])\n", + " self.trf_blocks = nn.Sequential(\n", + " *[DummyTransformerBlock(cfg) for _ in range(cfg[\"n_layers\"])]) #A\n", + " self.final_norm = DummyLayerNorm(cfg[\"emb_dim\"]) #B\n", + " self.out_head = nn.Linear(\n", + " cfg[\"emb_dim\"], cfg[\"vocab_size\"], bias=False\n", + " )\n", + " def forward(self, in_idx):\n", + " batch_size, seq_len = in_idx.shape\n", + " tok_embeds = self.tok_emb(in_idx)\n", + " pos_embeds = self.pos_emb(torch.arange(seq_len, device=in_idx.device))\n", + " x = tok_embeds + pos_embeds\n", + " x = self.drop_emb(x)\n", + " x = self.trf_blocks(x)\n", + " x = self.final_norm(x)\n", + " logits = self.out_head(x)\n", + " return logits\n", + "class DummyTransformerBlock(nn.Module): #C\n", + " def __init__(self, cfg):\n", + " \tsuper().__init__()\n", + " def forward(self, x): #D\n", + " \treturn x\n", + "class DummyLayerNorm(nn.Module): #E\n", + " def __init__(self, normalized_shape, eps=1e-5): #F\n", + " \tsuper().__init__()\n", + " def forward(self, x):\n", + " \treturn x" + ] + }, + { + "cell_type": "markdown", + "id": "69969d27", + "metadata": {}, + "source": [ + "​\t\t此代码中的 DummyGPTModel 类使用 PyTorch 的神经网络模块 (nn.模块)。DummyGPTModel 类中的模型架构由标记和位置嵌入、dropout、一系列转换器块 (DummyTransformerBlock)、最终层归一化 (DummyLayerNorm) 和线性输出层 (out_head) 组成。配置是通过 Python 字典传入的,例如,我们之前创建的 GPT_CONFIG_124M 字典。" + ] + }, + { + "cell_type": "markdown", + "id": "fcc70ece", + "metadata": {}, + "source": [ + "​\t\tforward 方法描述了通过模型的数据流:它计算输入索引的标记和位置嵌入,应用 dropout,通过 transformer 模块处理数据,应用归一化,最后使用线性输出层生成 logits。" + ] + }, + { + "cell_type": "markdown", + "id": "1a13ef4b", + "metadata": {}, + "source": [ + "​\t\t上面的代码已经起作用了,我们将在本节后面准备输入数据后看到。但是,现在,请注意,在上面的代码中,我们已经使用了占位符(DummyLayerNorm 和 DummyTransformerBlock)来实现转换器块和层规范化,我们将在后面的章节中对其进行开发。" + ] + }, + { + "cell_type": "markdown", + "id": "98fee0c5", + "metadata": {}, + "source": [ + "​\t\t接下来,我们将准备输入数据并初始化一个新的 GPT 模型来说明它的用法。图 4.4 基于我们在第 2 章中看到的数字(我们对分词器进行编码)的基础上,提供了数据如何流入和流出 GPT 模型的高级概述。" + ] + }, + { + "cell_type": "markdown", + "id": "099417b4", + "metadata": {}, + "source": [ + "图 4.4 显示如何标记、嵌入和馈送到 GPT 模型的输入数据的大图概述。请注意,在我们之前编码的 DummyGPTClass 中,令牌嵌入是在 GPT 模型中处理的。在 LLM 中,嵌入的输入令牌维度通常与输出维度匹配。此处的输出嵌入表示我们在第 3 章中讨论的上下文向量。" + ] + }, + { + "cell_type": "markdown", + "id": "32ed7f95", + "metadata": {}, + "source": [ + "![image-20240422134652565](../img/fig-4-4.png)" + ] + }, + { + "cell_type": "markdown", + "id": "c79dcc6a", + "metadata": {}, + "source": [ + "​\t\t为了实现图 4.4 中所示的步骤,我们使用第 2 章中介绍的 tiktoken 分词器对 GPT 模型的两个文本输入组成的批次进行分词化:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ea35069", + "metadata": {}, + "outputs": [], + "source": [ + "import tiktoken\n", + "tokenizer = tiktoken.get_encoding(\"gpt2\")\n", + "batch = []\n", + "txt1 = \"Every effort moves you\"\n", + "txt2 = \"Every day holds a\"\n", + "\n", + "batch.append(torch.tensor(tokenizer.encode(txt1)))\n", + "batch.append(torch.tensor(tokenizer.encode(txt2)))\n", + "batch = torch.stack(batch, dim=0)\n", + "print(batch)" + ] + }, + { + "cell_type": "markdown", + "id": "7b93c468", + "metadata": {}, + "source": [ + "​\t\t两个文本的结果令牌 ID 如下所示:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2633edec", + "metadata": {}, + "outputs": [], + "source": [ + "tensor([[ 6109, 3626, 6100, 345], #A\n", + " [ 6109, 1110, 6622, 257]])" + ] + }, + { + "cell_type": "markdown", + "id": "272f3aaf", + "metadata": {}, + "source": [ + "​\t\t接下来,我们初始化一个新的 1.24 亿参数 DummyGPTModel 实例,并向其提供标记化的批处理:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a33ee9db", + "metadata": {}, + "outputs": [], + "source": [ + "torch.manual_seed(123)\n", + "model = DummyGPTModel(GPT_CONFIG_124M)\n", + "logits = model(batch)\n", + "print(\"Output shape:\", logits.shape)\n", + "print(logits)" + ] + }, + { + "cell_type": "markdown", + "id": "416827b6", + "metadata": {}, + "source": [ + "​\t\t模型输出(通常称为 logit)如下:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94a253a1", + "metadata": {}, + "outputs": [], + "source": [ + "Output shape: torch.Size([2, 4, 50257])\n", + "tensor([[[-1.2034, 0.3201, -0.7130, ..., -1.5548, -0.2390, -0.4667],\n", + " [-0.1192, 0.4539, -0.4432, ..., 0.2392, 1.3469, 1.2430],\n", + " [ 0.5307, 1.6720, -0.4695, ..., 1.1966, 0.0111, 0.5835],\n", + " [ 0.0139, 1.6755, -0.3388, ..., 1.1586, -0.0435, -1.0400]],\n", + " [[-1.0908, 0.1798, -0.9484, ..., -1.6047, 0.2439, -0.4530],\n", + " [-0.7860, 0.5581, -0.0610, ..., 0.4835, -0.0077, 1.6621],\n", + " [ 0.3567, 1.2698, -0.6398, ..., -0.0162, -0.1296, 0.3717],\n", + " [-0.2407, -0.7349, -0.5102, ..., 2.0057, -0.3694, 0.1814]]],\n", + " grad_fn=)" + ] + }, + { + "cell_type": "markdown", + "id": "0038eead", + "metadata": {}, + "source": [ + "​\t\t输出张量有两行对应于两个文本样本。每个文本样本由 4 个标记组成;每个标记都是一个 50,257 维的向量,与标记器词汇表的大小相匹配。" + ] + }, + { + "cell_type": "markdown", + "id": "c96a3546", + "metadata": {}, + "source": [ + "​\t\t嵌入有 50,257 个维度,因为每个维度都引用词汇表中的唯一标记。在本章的最后,当我们实现后处理代码时,我们将把这些 50,257 维的向量转换回标记 ID,然后我们可以将其解码为单词。" + ] + }, + { + "cell_type": "markdown", + "id": "1ff3c403", + "metadata": {}, + "source": [ + "​\t\t现在,我们已经自上而下地了解了 GPT 架构及其 inand 输出,我们将在接下来的部分中对各个占位符进行编码,从实际层规范化类开始,该类将替换上一段代码中的 DummyLayerNorm。" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Translated_Book/ch04/4.2.ipynb b/Translated_Book/ch04/4.2.ipynb new file mode 100644 index 0000000..22bcb8a --- /dev/null +++ b/Translated_Book/ch04/4.2.ipynb @@ -0,0 +1,461 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f1baec45", + "metadata": {}, + "source": [ + "## 4.2 使用层归一化对激活进行归一化" + ] + }, + { + "cell_type": "markdown", + "id": "0fa08967", + "metadata": {}, + "source": [ + "​\t\t由于梯度消失或爆炸等问题,训练具有多层的深度神经网络有时可能具有挑战性。这些问题导致训练动态不稳定,使网络难以有效调整其权重,这意味着学习过程很难为神经网络找到一组参数(权重),以最小化损失函数。换句话说,网络很难在一定程度上学习数据中的基本模式,从而无法做出准确的预测或决策。(如果您不熟悉神经网络训练和梯度概念,可以在附录 A 中的第 A.4 节 “轻松实现自动区分:PyTorch 简介”中找到这些概念的简要介绍。但是,不需要对梯度有深入的数学理解才能遵循本书的内容。" + ] + }, + { + "cell_type": "markdown", + "id": "f53ecf2b", + "metadata": {}, + "source": [ + "​\t\t在本节中,我们将实现层归一化,以提高神经网络训练的稳定性和效率。" + ] + }, + { + "cell_type": "markdown", + "id": "5fe24ad6", + "metadata": {}, + "source": [ + "​\t\t层归一化背后的主要思想是调整神经网络层的激活(输出),使其平均值为 0,方差为 1,也称为单位方差。这种调整加快了向有效重量的收敛速度,并确保了一致、可靠的训练。正如我们在上一节中看到的,基于 DummyLayerNorm 占位符,在 GPT-2 和现代 transformer 架构中,层归一化通常在多头注意力模块之前和之后以及最终输出层之前应用。" + ] + }, + { + "cell_type": "markdown", + "id": "9459b0b0", + "metadata": {}, + "source": [ + "​\t\t在代码中实现层归一化之前,图 4.5 直观地概述了层归一化的工作原理。" + ] + }, + { + "cell_type": "markdown", + "id": "1101131d", + "metadata": {}, + "source": [ + "图 4.5 层归一化的图示,其中 5 层输出(也称为激活)被归一化,使其均值为零,方差为 1。" + ] + }, + { + "cell_type": "markdown", + "id": "f4338794", + "metadata": {}, + "source": [ + "![image-20240422135247478](../img/fig-4-5.png)" + ] + }, + { + "cell_type": "markdown", + "id": "43a1ba1c", + "metadata": {}, + "source": [ + "​\t\t我们可以通过以下代码重新创建图 4.5 中所示的示例,其中我们实现了一个具有 5 个输入和 6 个输出的神经网络层,我们将其应用于两个输入示例:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b69a76c2", + "metadata": {}, + "outputs": [], + "source": [ + "torch.manual_seed(123)\n", + "batch_example = torch.randn(2, 5) #A\n", + "layer = nn.Sequential(nn.Linear(5, 6), nn.ReLU())\n", + "out = layer(batch_example)\n", + "print(out)" + ] + }, + { + "cell_type": "markdown", + "id": "d7901a3c", + "metadata": {}, + "source": [ + "​\t\t这将打印以下张量,其中第一行列出了第一个输入的层输出,第二行列出了第二行的层输出:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8170493d", + "metadata": {}, + "outputs": [], + "source": [ + "tensor([[0.2260, 0.3470, 0.0000, 0.2216, 0.0000, 0.0000],\n", + " [0.2133, 0.2394, 0.0000, 0.5198, 0.3297, 0.0000]],\n", + " grad_fn=)" + ] + }, + { + "cell_type": "markdown", + "id": "ae2b8b15", + "metadata": {}, + "source": [ + "​\t\t我们编码的神经网络层由一个线性层和一个非线性激活函数 ReLU(Rectified Linear Unit 的缩写)组成,它是神经网络中的标准激活函数。如果您不熟悉 ReLU,它只需将负输入阈值设置为 0,确保图层仅输出正值,这就解释了为什么生成的图层输出不包含任何负值。(请注意,我们将在 GPT 中使用另一个更复杂的激活函数,我们将在下一节中介绍)。" + ] + }, + { + "cell_type": "markdown", + "id": "7f4219cc", + "metadata": {}, + "source": [ + "​\t\t在对这些输出应用层归一化之前,让我们检查均值和方差:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e14c73d6", + "metadata": {}, + "outputs": [], + "source": [ + "mean = out.mean(dim=-1, keepdim=True)\n", + "var = out.var(dim=-1, keepdim=True)\n", + "print(\"Mean:\\n\", mean)\n", + "print(\"Variance:\\n\", var)" + ] + }, + { + "cell_type": "markdown", + "id": "fd30826f", + "metadata": {}, + "source": [ + "​\t\t输出如下:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a6c93ac", + "metadata": {}, + "outputs": [], + "source": [ + "Mean:\n", + " tensor([[0.1324],\n", + " [0.2170]], grad_fn=)\n", + "Variance:\n", + " tensor([[0.0231],\n", + " [0.0398]], grad_fn=)" + ] + }, + { + "cell_type": "markdown", + "id": "cb269d67", + "metadata": {}, + "source": [ + "​\t\t上面平均张量中的第一行包含第一输入行的平均值,第二输出行包含第二输入行的平均值。" + ] + }, + { + "cell_type": "markdown", + "id": "1a2aad91", + "metadata": {}, + "source": [ + "​\t\t在均值或方差计算等操作中使用 keepdim=True 可确保输出张量保持与输入张量相同的形状,即使该操作沿 dim 指定的维度减少张量也是如此。例如,如果没有 keepdim=True,则返回的平均张量将是二维向量 [0.1324, 0.2170],而不是二维矩阵 [[0.1324], [0.2170]]。" + ] + }, + { + "cell_type": "markdown", + "id": "da5c4a90", + "metadata": {}, + "source": [ + "​\t\tdim 参数指定在张量中计算统计数据(此处为均值或方差)的维度,如图 4.6 所示。" + ] + }, + { + "cell_type": "markdown", + "id": "fd68fe9b", + "metadata": {}, + "source": [ + "图 4.6 计算张量均值时的 dim 参数图示。例如,如果我们有一个维度为 [行、列] 的 2D 张量(矩阵),则使用 dim=0 将跨行(垂直,如底部所示)执行操作,从而产生聚合每列数据的输出。使用 dim=1 或 dim=-1 将跨列执行操作(水平,如顶部所示),从而生成聚合每行数据的输出。" + ] + }, + { + "cell_type": "markdown", + "id": "bb7a5e20", + "metadata": {}, + "source": [ + "![image-20240422135636907](../img/fig-4-6.png)" + ] + }, + { + "cell_type": "markdown", + "id": "eeb12e38", + "metadata": {}, + "source": [ + "​\t\t如图 4.6 所示,对于二维张量(如矩阵),使用 dim=-1 进行均值或方差计算等运算与使用 dim=1 相同。这是因为 -1 指的是张量的最后一个维度,它对应于 2D 张量中的列。之后,当向 GPT 模型添加层归一化时,该模型生成形状为 [batch_size、num_tokens、embedding_size] 的 3D 张量,我们仍然可以使用 dim=-1 进行最后一个维度的归一化,避免从 dim=1 更改为 dim=2。" + ] + }, + { + "cell_type": "markdown", + "id": "5325ca63", + "metadata": {}, + "source": [ + "​\t\t接下来,让我们将层归一化应用于我们之前获得的层输出。该操作包括减去均值并除以方差的平方根(也称为标准差):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66e13187", + "metadata": {}, + "outputs": [], + "source": [ + "out_norm = (out - mean) / torch.sqrt(var)\n", + "mean = out_norm.mean(dim=-1, keepdim=True)\n", + "var = out_norm.var(dim=-1, keepdim=True)\n", + "print(\"Normalized layer outputs:\\n\", out_norm)\n", + "print(\"Mean:\\n\", mean)\n", + "print(\"Variance:\\n\", var)" + ] + }, + { + "cell_type": "markdown", + "id": "637af25d", + "metadata": {}, + "source": [ + "​\t\t根据结果,我们可以看到,归一化层输出(现在也包含负值)的平均值为零,方差为 1:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ea812b6", + "metadata": {}, + "outputs": [], + "source": [ + "Normalized layer outputs:\n", + " tensor([[ 0.6159, 1.4126, -0.8719, 0.5872, -0.8719, -0.8719],\n", + " [-0.0189, 0.1121, -1.0876, 1.5173, 0.5647, -1.0876]],\n", + " grad_fn=)\n", + "Mean:\n", + " tensor([[2.9802e-08],\n", + " [3.9736e-08]], grad_fn=)\n", + "Variance:\n", + " tensor([[1.],\n", + " [1.]], grad_fn=)" + ] + }, + { + "cell_type": "markdown", + "id": "3dfd9a15", + "metadata": {}, + "source": [ + "​\t\t请注意,输出张量中的值 2.9802e-08 是 2.9802 × 10-8 的科学记数法,即十进制形式的 0.00000000298。该值非常接近 0,但由于计算机表示数字的精度有限,可能会累积较小的数值误差,因此它并不完全是 0。" + ] + }, + { + "cell_type": "markdown", + "id": "0c577985", + "metadata": {}, + "source": [ + "​\t\t为了提高可读性,我们还可以通过将 sci_mode 设置为 False 来关闭打印张量值时的科学记数法:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e739ce9", + "metadata": {}, + "outputs": [], + "source": [ + "torch.set_printoptions(sci_mode=False)\n", + "print(\"Mean:\\n\", mean)\n", + "print(\"Variance:\\n\", var)\n", + "Mean:\n", + " tensor([[ 0.0000],\n", + " [ 0.0000]], grad_fn=)\n", + "Variance:\n", + " tensor([[1.],\n", + " [1.]], grad_fn=)" + ] + }, + { + "cell_type": "markdown", + "id": "8810265c", + "metadata": {}, + "source": [ + "​\t\t到目前为止,在本节中,我们已经分步编码和应用了层归一化。现在让我们将这个过程封装在一个 PyTorch 模块中,稍后可以在 GPT 模型中使用:" + ] + }, + { + "cell_type": "markdown", + "id": "773faacb", + "metadata": {}, + "source": [ + "**Listing 4.2 A 层归一化类**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "98c3e02f", + "metadata": {}, + "outputs": [], + "source": [ + "class LayerNorm(nn.Module):\n", + "\tdef __init__(self, emb_dim):\n", + "\t\tsuper().__init__()\n", + "\t\tself.eps = 1e-5\n", + "\t\tself.scale = nn.Parameter(torch.ones(emb_dim))\n", + "\t\tself.shift = nn.Parameter(torch.zeros(emb_dim))\n", + "\tdef forward(self, x):\n", + "\t\tmean = x.mean(dim=-1, keepdim=True)\n", + "\t\tvar = x.var(dim=-1, keepdim=True, unbiased=False)\n", + "\t\tnorm_x = (x - mean) / torch.sqrt(var + self.eps)\n", + "\t\treturn self.scale * norm_x + self.shift" + ] + }, + { + "cell_type": "markdown", + "id": "20827f45", + "metadata": {}, + "source": [ + "​\t\t层归一化的这种特定实现在输入张量 x 的最后一个维度上运行,该维度表示嵌入维度 (emb_dim)。变量 eps 是添加到方差中的一个小常数 (epsilon),以防止在归一化过程中除以零。scale 和 shift 是两个可训练的参数(与输入的维度相同),如果确定这样做会提高模型在其训练任务中的性能,则 LLM 会在训练期间自动调整这些参数。这使模型能够学习最适合其正在处理的数据的适当缩放和移位。" + ] + }, + { + "cell_type": "markdown", + "id": "d8ea7351", + "metadata": {}, + "source": [ + "**偏差方差**" + ] + }, + { + "cell_type": "markdown", + "id": "c8de0ee3", + "metadata": {}, + "source": [ + "​\t\t在我们的方差计算方法中,我们通过设置 unbiased=False 来选择实现细节。对于那些对这意味着什么感到好奇的人,在方差计算中,我们除以方差公式中的输入数 n。这种方法不应用贝塞尔校正,贝塞尔校正通常使用分母中的 n-1 而不是 n 来调整样本方差估计中的偏差。这一决定导致了所谓的偏差估计。对于大规模语言模型 (LLM),其中嵌入维度 n 非常大,使用 n 和 n-1 之间的差异几乎可以忽略不计。我们选择这种方法是为了确保与 GPT-2 模型的归一化层兼容,并且因为它反映了 TensorFlow 的默认行为,该行为用于实现原始 GPT-2 模型。使用类似的设置可确保我们的方法与我们将在第 6 章中加载的预训练权重兼容。" + ] + }, + { + "cell_type": "markdown", + "id": "4336e78c", + "metadata": {}, + "source": [ + "​\t\t现在让我们在实践中尝试 LayerNorm 模块并将其应用于批处理输入:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "836af1ff", + "metadata": {}, + "outputs": [], + "source": [ + "ln = LayerNorm(emb_dim=5)\n", + "out_ln = ln(batch_example)\n", + "mean = out_ln.mean(dim=-1, keepdim=True)\n", + "var = out_ln.var(dim=-1, unbiased=False, keepdim=True)\n", + "print(\"Mean:\\n\", mean)\n", + "print(\"Variance:\\n\", var)" + ] + }, + { + "cell_type": "markdown", + "id": "82a8d756", + "metadata": {}, + "source": [ + "​\t\t根据结果,我们可以看到,层归一化代码按预期工作,并归一化两个输入中每个输入的值,使它们的均值为 0,方差为 1:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65693732", + "metadata": {}, + "outputs": [], + "source": [ + "Mean:\n", + " tensor([[ -0.0000],\n", + " [ 0.0000]], grad_fn=)\n", + "Variance:\n", + " tensor([[1.0000],\n", + " [1.0000]], grad_fn=)" + ] + }, + { + "cell_type": "markdown", + "id": "0c085356", + "metadata": {}, + "source": [ + "​\t\t在本节中,我们介绍了实现 GPT 架构所需的构建块之一,如图 4.7 中的心智模型所示。" + ] + }, + { + "cell_type": "markdown", + "id": "3d2a88bb", + "metadata": {}, + "source": [ + "图 4.7 一个心智模型,列出了我们在本章中实现的不同构建块,用于组装 GPT 架构。" + ] + }, + { + "cell_type": "markdown", + "id": "27dcaf81", + "metadata": {}, + "source": [ + "![image-20240422140325104](../img/fig-4-7.png)" + ] + }, + { + "cell_type": "markdown", + "id": "8ea0e0ee", + "metadata": {}, + "source": [ + "​\t\t在下一节中,我们将研究 GELU 激活函数,它是 LLM 中使用的激活函数之一,而不是我们在本节中使用的传统 ReLU 函数。" + ] + }, + { + "cell_type": "markdown", + "id": "ca7881c5", + "metadata": {}, + "source": [ + "**层归一化与批量归一化**" + ] + }, + { + "cell_type": "markdown", + "id": "77c57101", + "metadata": {}, + "source": [ + "​\t\t如果你熟悉批量归一化(一种常见且传统的神经网络归一化方法),您可能想知道它与层归一化相比如何。与跨批次维度归一化的批量归一化不同,图层归一化将跨要素维度归一化。LLM 通常需要大量的计算资源,可用的硬件或特定用例可以决定训练或推理期间的批处理大小。由于层归一化独立于批处理大小对每个输入进行归一化,因此在这些场景中提供了更大的灵活性和稳定性。这对于分布式训练或在资源受限的环境中部署模型时特别有用。" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Translated_Book/ch05/5.2.ipynb b/Translated_Book/ch05/5.2.ipynb index 183d9ca..84ad279 100644 --- a/Translated_Book/ch05/5.2.ipynb +++ b/Translated_Book/ch05/5.2.ipynb @@ -29,7 +29,7 @@ "id": "9791e0ca", "metadata": {}, "source": [ - "![image-20240422143154243](../img/image-5-11.png)" + "![image-20240422143154243](../img/fig-5-11.png)" ] }, { @@ -296,7 +296,7 @@ "id": "06408771", "metadata": {}, "source": [ - "![image-20240422144030197](..\\img\\image-5-12.png)" + "![image-20240422144030197](..\\img\\fig-5-12.png)" ] }, { @@ -344,7 +344,7 @@ "id": "6937bae6", "metadata": {}, "source": [ - "![image-20240422144152449](../img/image-5-13.png)" + "![image-20240422144152449](../img/fig-5-13.png)" ] }, { diff --git a/Translated_Book/img/fig-3-1.png b/Translated_Book/img/fig-3-1.png new file mode 100644 index 0000000..ce3b277 Binary files /dev/null and b/Translated_Book/img/fig-3-1.png differ diff --git a/Translated_Book/img/fig-3-2.png b/Translated_Book/img/fig-3-2.png new file mode 100644 index 0000000..3837406 Binary files /dev/null and b/Translated_Book/img/fig-3-2.png differ diff --git a/Translated_Book/img/fig-3-3.png b/Translated_Book/img/fig-3-3.png new file mode 100644 index 0000000..ae7bca2 Binary files /dev/null and b/Translated_Book/img/fig-3-3.png differ diff --git a/Translated_Book/img/fig-3-4.png b/Translated_Book/img/fig-3-4.png new file mode 100644 index 0000000..726b48a Binary files /dev/null and b/Translated_Book/img/fig-3-4.png differ diff --git a/Translated_Book/img/fig-3-5.png b/Translated_Book/img/fig-3-5.png new file mode 100644 index 0000000..dfd293d Binary files /dev/null and b/Translated_Book/img/fig-3-5.png differ diff --git a/Translated_Book/img/fig-3-6.png b/Translated_Book/img/fig-3-6.png new file mode 100644 index 0000000..7207841 Binary files /dev/null and b/Translated_Book/img/fig-3-6.png differ diff --git a/Translated_Book/img/fig-4-1.png b/Translated_Book/img/fig-4-1.png new file mode 100644 index 0000000..c8c3f83 Binary files /dev/null and b/Translated_Book/img/fig-4-1.png differ diff --git a/Translated_Book/img/fig-4-2.png b/Translated_Book/img/fig-4-2.png new file mode 100644 index 0000000..f785a23 Binary files /dev/null and b/Translated_Book/img/fig-4-2.png differ diff --git a/Translated_Book/img/fig-4-3.png b/Translated_Book/img/fig-4-3.png new file mode 100644 index 0000000..e6e8c18 Binary files /dev/null and b/Translated_Book/img/fig-4-3.png differ diff --git a/Translated_Book/img/fig-4-4.png b/Translated_Book/img/fig-4-4.png new file mode 100644 index 0000000..ebcac29 Binary files /dev/null and b/Translated_Book/img/fig-4-4.png differ diff --git a/Translated_Book/img/fig-4-5.png b/Translated_Book/img/fig-4-5.png new file mode 100644 index 0000000..434f1e9 Binary files /dev/null and b/Translated_Book/img/fig-4-5.png differ diff --git a/Translated_Book/img/fig-4-6.png b/Translated_Book/img/fig-4-6.png new file mode 100644 index 0000000..29b2b68 Binary files /dev/null and b/Translated_Book/img/fig-4-6.png differ diff --git a/Translated_Book/img/fig-4-7.png b/Translated_Book/img/fig-4-7.png new file mode 100644 index 0000000..194c36e Binary files /dev/null and b/Translated_Book/img/fig-4-7.png differ diff --git a/Translated_Book/img/image-5-11.png b/Translated_Book/img/fig-5-11.png similarity index 100% rename from Translated_Book/img/image-5-11.png rename to Translated_Book/img/fig-5-11.png diff --git a/Translated_Book/img/image-5-12.png b/Translated_Book/img/fig-5-12.png similarity index 100% rename from Translated_Book/img/image-5-12.png rename to Translated_Book/img/fig-5-12.png diff --git a/Translated_Book/img/image-5-13.png b/Translated_Book/img/fig-5-13.png similarity index 100% rename from Translated_Book/img/image-5-13.png rename to Translated_Book/img/fig-5-13.png diff --git a/jupyter notebook.bat b/jupyter notebook.bat new file mode 100644 index 0000000..4f7e123 --- /dev/null +++ b/jupyter notebook.bat @@ -0,0 +1 @@ +jupyter notebook \ No newline at end of file