mirror of
https://github.com/skindhu/Build-A-Large-Language-Model-CN.git
synced 2026-07-29 15:20:24 +08:00
Revert "REAEME 文件中,增加「在线阅读」地址,并且,所有图片设置为居中。"
This commit is contained in:
@@ -24,9 +24,7 @@
|
||||
|
||||
在上一章中,我们学习并实现了多头注意力机制,这是大语言模型(LLM)的核心组件之一。本章将进一步实现 LLM 的其他组件,并将它们组装成一个与 GPT 类似结构的模型。我们将在下一章中训练该模型,以生成类人文本,具体过程如图 4.1 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.1.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.1.png" width="75%" />
|
||||
|
||||
大语言模型(LLM)架构(见图 4.1)由多个模块构成,我们将在本章中实现这些模块。接下来的内容,我们首先从整体视角介绍模型架构,然后详细讲解各个组件。
|
||||
|
||||
@@ -36,9 +34,7 @@
|
||||
|
||||
LLM(如GPT,即生成式预训练 Transformer,Generative Pretrained Transformer)是一种大型深度神经网络架构,设计用于逐词(或逐 token)生成新文本。然而,尽管模型规模庞大,其结构却并没有想象中那么复杂,因为模型的许多组件是重复的(后文将对此展开说明)。图 4.2 展示了一个类 GPT 的 LLM 的整体视图,并突出了其主要组成部分。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.2.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.2.png" width="75%" />
|
||||
|
||||
如图 4.2 所示,我们已经在之前的章节中讲解过几个模块,如输入的分词和嵌入,以及掩码多头注意力模块。本章的重点是实现 GPT 模型的核心结构(包括 Transformer 模块)。我们将在下一章对该模型进行训练,使其能够生成类人文本。
|
||||
|
||||
@@ -79,9 +75,7 @@ GPT_CONFIG_124M = {
|
||||
|
||||
使用上述配置,我们将从本章开始实现一个GPT占位架构(DummyGPTModel),如图4.3所示。这将为我们提供一个全局视图,了解所有组件如何组合在一起,以及在接下来的章节中需要编写哪些其他组件来组装完整的GPT模型架构。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.3.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.3.png" width="75%" />
|
||||
|
||||
图 4.3 中显示的编号框说明了我们编写最终 GPT 架构所需理解的各个概念的顺序。我们将从第 1 步开始,这是一个我们称之为 DummyGPTModel 的 GPT 占位架构:
|
||||
|
||||
@@ -145,9 +139,7 @@ class DummyLayerNorm(nn.Module): #E
|
||||
|
||||
接下来,我们将准备输入数据并初始化一个新的 GPT 模型,以展示它的用法。基于第二章实现的分词器,图 4.4 展示了数据在 GPT 模型中流入和流出的整体流程。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.4.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.4.png" width="75%" />
|
||||
|
||||
根据图 4.4 的步骤,我们使用第 2 章介绍的 tiktoken 分词器对包含两个文本的批量输入进行分词,以供 GPT 模型使用:
|
||||
|
||||
@@ -236,9 +228,7 @@ tensor([[[-1.2034, 0.3201, -0.7130, ..., -1.5548, -0.2390, -0.4667],
|
||||
|
||||
在我们用代码实现层归一化之前,先通过图 4.5 了解一下层归一化的工作原理。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.5.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.5.png" width="75%" />
|
||||
|
||||
我们可以通过以下代码重现图 4.5 中的示例,其中实现了一个具有 5 个输入和 6 个输出的神经网络层,并将其应用于两个输入样本:
|
||||
|
||||
@@ -289,9 +279,7 @@ Variance:
|
||||
|
||||
`dim` 参数用于指定张量中进行统计计算(如均值或方差)的维度,具体如图 4.6 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.6.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.6.png" width="75%" />
|
||||
|
||||
如图 4.6 所示,对于二维张量(如矩阵),在进行均值或方差计算等操作时,使用 `dim=-1` 等同于使用 `dim=1`,因为 `-1` 指的是张量的最后一个维度,即二维张量中的列。在后续对 GPT 模型加入层归一化时,模型会生成形状为 `[batch_size, num_tokens, embedding_size]` 的三维张量,我们依然可以使用 `dim=-1` 对最后一个维度进行归一化,而无需将 `dim=1` 改为 `dim=2`。
|
||||
|
||||
@@ -389,9 +377,7 @@ Variance:
|
||||
|
||||
在本节中,我们介绍了实现 GPT 架构所需的一个基础模块(`LayerNorm`),如图 4.7 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.7.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.7.png" width="75%" />
|
||||
|
||||
在下一节中,我们将探讨大语言模型中使用的 GELU 激活函数,它将替代我们在本节使用的传统 ReLU 函数。
|
||||
|
||||
@@ -454,9 +440,7 @@ plt.show()
|
||||
|
||||
如图 4.8 所示,ReLU 是一个分段线性函数,输入为正时输出输入值本身,否则输出零。而 GELU 是一种平滑的非线性函数,它近似于 ReLU,但在负值上也具有非零梯度。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.8.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.8.png" width="75%" />
|
||||
|
||||
如图 4.8 所示,GELU 的平滑性使其在训练过程中具有更好的优化特性,能够对模型参数进行更细微的调整。相比之下,ReLU 在零点处有一个拐角,这在网络深度较大或结构复杂时可能会增加优化难度。此外,ReLU 对所有负输入的输出为零,而 GELU 对负值允许一个小的非零输出。这意味着在训练过程中,接收负输入的神经元也能对学习过程产生一定的贡献,尽管贡献程度不及正输入。
|
||||
|
||||
@@ -481,9 +465,7 @@ def forward(self, x):
|
||||
|
||||
图 4.9 展示了当我们输入数据后,这个前馈网络内部如何调整嵌入维度。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.9.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.9.png" width="75%" />
|
||||
|
||||
按照图 4.9 中的示例,我们初始化一个新的 FeedForward 模块,设置 token 嵌入维度为 768,并输入一个包含 2 个样本且每个样本有 3 个 token 的数据集:
|
||||
|
||||
@@ -516,17 +498,13 @@ torch.Size([2, 3, 768])
|
||||
>
|
||||
> 将这种理解再应用到神经网络中,扩展后的高维空间可以让模型“看到”输入数据中更多的隐藏特征,提取出更丰富的信息。然后在收缩回低维度时,这些丰富的特征被整合到了输入的原始维度表示中,使模型最终的输出包含更多的上下文和信息。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.10.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.10.png" width="75%" />
|
||||
|
||||
此外,输入输出维度保持一致也有助于简化架构,方便堆叠多层(在后续的章节实现),无需调整各层维度,从而提升了模型的可扩展性。
|
||||
|
||||
如图4.11所示,我们目前已经实现了LLM 架构中的大部分模块。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.11.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.11.png" width="75%" />
|
||||
|
||||
下一节,我们将介绍“快捷连接”的概念,即在神经网络的不同层之间插入的连接结构,它对于提升深度神经网络架构的训练性能非常重要。
|
||||
|
||||
@@ -536,9 +514,7 @@ torch.Size([2, 3, 768])
|
||||
|
||||
接下来,我们来讨论快捷连接(也称跳跃连接或残差连接)的概念。快捷连接最初是在计算机视觉中的深度网络(尤其是残差网络)提出的,用于缓解梯度消失问题。梯度消失是指在训练中指导权重更新的梯度在反向传播过程中逐渐减小,导致早期层(靠近输入端的网络层)难以有效训练,如图 4.12 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.12.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.12.png" width="75%" />
|
||||
|
||||
如图 4.12 所示,快捷连接通过跳过一层或多层,为梯度提供一条更短的流动路径,这是通过将某层的输出加到后续层的输出上来实现的。因此,这种连接方式也称为跳跃连接。在反向传播中,快捷连接对保持梯度流动至关重要。
|
||||
|
||||
@@ -717,9 +693,7 @@ layers.4.0.weight has gradient mean of 1.3258541822433472
|
||||
|
||||
本节我们将实现 Transformer 模块,它是 GPT 和其他大语言模型架构的基本模块。这个在 124M 参数的 GPT-2 架构中重复了十几次的模块,结合了多头注意力、层归一化、dropout、前馈层和 GELU 激活等多个概念,详见图 4.13。在下一节中,我们将把这个 Transformer 模块连接到 GPT 架构的其余部分。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.13.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.13.png" width="75%" />
|
||||
|
||||
如图 4.13 所示,Transformer 模块结合了多个组件,包括第 3 章中的掩码多头注意力模块以及我们在 4.3 节中实现的前馈网络模块。
|
||||
|
||||
@@ -800,9 +774,7 @@ Transformer 模块结构中保持数据形状不变并非偶然,而是其设
|
||||
|
||||
在本节完成了 Transformer 模块的实现后,我们已经具备了实现 GPT 架构所需的全部基础模块(如图 4.14 所示)。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.14.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.14.png" width="75%" />
|
||||
|
||||
如图 4.14 所示,Transformer 模块由层归一化、带有 GELU 激活函数的前馈网络和快捷连接组成,这些内容在本章前面已经讨论过。正如我们将在接下来的章节中看到的,这个 Transformer 模块将构成我们要实现的 GPT 架构的核心部分。
|
||||
|
||||
@@ -816,9 +788,7 @@ Transformer 模块结构中保持数据形状不变并非偶然,而是其设
|
||||
|
||||
在我们通过代码构建 GPT-2 模型之前,先通过图 4.15 看一下模型的整体结构,该结构结合了本章目前为止介绍的所有概念。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.15.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.15.png" width="75%" />
|
||||
|
||||
如图 4.15 所示,我们在 4.5 节中编写的 Transformer 模块在 GPT 架构中会重复多次。在参数量为 1.24 亿的 GPT-2 模型中,该模块重复了 12 次,这一数量通过 `GPT_CONFIG_124M` 配置字典中的`n_layers`参数指定。在 GPT-2 最大的 15.42 亿参数模型中,Transformer 模块重复了 36 次。
|
||||
|
||||
@@ -986,9 +956,7 @@ Total size of the model: 621.83 MB
|
||||
|
||||
在本章的最后一节,我们将编写代码把 GPT 模型的张量输出转回文本。在开始之前,我们先简要回顾一下像 LLM 这样的生成模型是如何逐词生成文本的,如图 4.16 所示。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.16.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.16.png" width="75%" />
|
||||
|
||||
如图 4.16 所示,GPT 模型在给定输入上下文(例如 ‘Hello, I am’)后,逐步生成文本。每次迭代中,输入上下文会不断扩展,使模型能够生成连贯且符合上下文的内容。在第 6 次迭代时,模型已构建出完整句子 ‘Hello, I am a model ready to help.’。
|
||||
|
||||
@@ -996,9 +964,7 @@ Total size of the model: 621.83 MB
|
||||
|
||||
GPT 模型从输出张量到生成文本的过程涉及几个步骤(如图 4.17 所示)。这些步骤包括解码输出张量、根据概率分布选择 token,并将其转化为可读文本。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.17.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.17.png" width="75%" />
|
||||
|
||||
图 4.17 详细展示了 GPT 模型根据输入生成下一个 token 的单步过程。
|
||||
|
||||
@@ -1038,9 +1004,7 @@ def generate_text_simple(model, idx, max_new_tokens, context_size): #A
|
||||
|
||||
我们可以使用 `generate_text_simple` 函数逐步生成 token ID,每次生成一个 token ID 并将其附加到上下文中。其具体过程详见图 4.18(每次迭代生成 token ID 的步骤详见图 4.17)。
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter4/figure4.18.png" width="75%" />
|
||||
</div>
|
||||
<img src="../Image/chapter4/figure4.18.png" width="75%" />
|
||||
|
||||
如图 4.18 所示,我们以迭代的方式逐步生成 token ID。例如,在第 1 轮迭代中,模型接收到“Hello , I am”对应的 token 作为输入,预测下一个 token(ID 为 257,对应“a”),并将其添加到输入序列中。这个过程不断重复,直到模型在第六轮迭代后生成完整的句子“Hello, I am a model ready to help.”。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user