mirror of
https://github.com/skindhu/Build-A-Large-Language-Model-CN.git
synced 2026-07-29 15:20:24 +08:00
update with img path
This commit is contained in:
@@ -30,7 +30,9 @@
|
||||
|
||||
在之前的章节中,我们实现了 LLM 架构,完成了预训练,并将外部的预训练权重导入模型。接着,在上一章中,我们专注于对 LLM 进行特定分类任务的微调,即区分出正常短信和垃圾短信。在本章中,我们将介绍如何微调 LLM 以遵循人类指令(见图 7.1),这是开发用于聊天机器人、个人助理和其他对话任务的 LLM 的主要技术之一。
|
||||
|
||||
<img src="../Image/chapter7/figure7.1.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.1.png" width="75%" />
|
||||
</div>
|
||||
|
||||
图 7.1 展示了微调 LLM 的两种主要方式:用于分类任务的微调(步骤 8)和用于指令遵循的微调(步骤 9)。上一章中我们已实现了步骤 8,本章将重点讲解如何使用指令数据集微调 LLM,具体过程将在下一节进一步说明。
|
||||
|
||||
@@ -44,11 +46,15 @@
|
||||
|
||||
本章将专注于提升 LLM 遵循指令并生成理想回答的能力,如图 7.2 所示。
|
||||
|
||||
<img src="../Image/chapter7/figure7.2.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.2.png" width="75%" />
|
||||
</div>
|
||||
|
||||
在本章的剩余部分,我们将逐步实现指令微调过程,首先从数据集准备开始,如图 7.3 所示。
|
||||
|
||||
<img src="../Image/chapter7/figure7.3.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.3.png" width="75%" />
|
||||
</div>
|
||||
|
||||
数据集准备是指令微调中的关键环节,本章的大部分内容都将围绕这一过程展开。下一节将开始实现下载和格式化数据集的代码,这是数据集准备过程的第一步(如图 7.3 所示)。
|
||||
|
||||
@@ -124,7 +130,9 @@ antonym of 'complicated' is 'simple'."}
|
||||
|
||||
指令微调(instruction finetuning),也称为监督式指令微调(supervised instruction finetuning),是指在包含明确输入-输出对的数据集上对模型进行训练(例如从 JSON 文件中提取的输入-输出对)。在为大语言模型(LLM)格式化这些条目时,通常会使用多种不同的方法。图 7.4 展示了两种不同的示例格式(通常称为提示风格),这些格式常用于训练一些知名的 LLM,例如 Alpaca 和 Phi-3。Alpaca 是最早公开指令微调过程的 LLM 之一,而由微软开发的 Phi-3 则展示了提示风格的多样性。
|
||||
|
||||
<img src="../Image/chapter7/figure7.4.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.4.png" width="75%" />
|
||||
</div>
|
||||
|
||||
本章其余部分将使用 Alpaca 风格的提示方式,这是最受欢迎的提示风格之一,主要是因为它帮助定义了最初的微调方法。
|
||||
|
||||
@@ -224,7 +232,9 @@ Test set length: 110
|
||||
|
||||
随着我们进入指令微调过程的实施阶段,接下来的步骤(如图 7.5 所示)将重点介绍如何高效地构建训练批次。这一步需要定义一种方法,以确保模型在微调过程中能够接收到格式化的训练数据。
|
||||
|
||||
<img src="../Image/chapter7/figure7.5.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.5.png" width="75%" />
|
||||
</div>
|
||||
|
||||
在上一章中,训练批次是通过 PyTorch 的 `DataLoader` 类自动创建的,该类使用默认的`collate`函数将样本列表合并为批次。`collate ` 函数的作用是将单个数据样本列表合并成一个批次,以便模型在训练过程中能够高效处理。
|
||||
|
||||
@@ -232,11 +242,15 @@ Test set length: 110
|
||||
|
||||
本节将分几步介绍批处理过程(包括自定义`collate`函数的编写),具体内容如图 7.6 所示。
|
||||
|
||||
<img src="../Image/chapter7/figure7.6.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.6.png" width="75%" />
|
||||
</div>
|
||||
|
||||
首先,为实现图 7.6 中展示的步骤 2.1 和 2.2,我们编写了一个 `InstructionDataset` 类,它应用了上一节中的 `format_input` 函数,并对数据集中的所有输入进行了预分词,类似于第 6 章中的 `SpamDataset`。这两个步骤的详细说明见图 7.7。
|
||||
|
||||
<img src="../Image/chapter7/figure7.7.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.7.png" width="75%" />
|
||||
</div>
|
||||
|
||||
图 7.7 中展示的 两步操作通过 `InstructionDataset` 类的 `__init__` 构造函数实现。
|
||||
|
||||
@@ -279,7 +293,9 @@ The resulting token ID is 50256.
|
||||
|
||||
在第 6 章中,我们使用的填充方式是将数据集中的所有示例填充到相同长度。在本章中,我们将采用一种更为精细的方法,开发一个自定义的`collate`函数并传递给数据加载器。该自定义`collate`函数会将每个批次中的训练样本填充到相同长度,同时允许不同批次中的样本具有不同的长度,如图 7.8 所示。这种方法通过仅将序列扩展到每个批次中最长的序列长度,从而减少了不必要的填充,避免了对整个数据集进行冗余填充。
|
||||
|
||||
<img src="../Image/chapter7/figure7.8.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.8.png" width="75%" />
|
||||
</div>
|
||||
|
||||
我们可以通过以下自定义`collate`函数来实现图 7.8 所示的填充过程:
|
||||
|
||||
@@ -337,13 +353,17 @@ tensor([[ 0, 1, 2, 3, 4],
|
||||
|
||||
我们刚刚实现了自定义 `collate` 函数的第一个版本,用于从输入列表创建批次。然而,正如在第 5 章和第 6 章中所学的那样,我们还需要创建与输入 ID 批次相对应的目标 token ID 批次。图 7.9 显示了这些目标 ID,它们非常重要,因为它们代表我们希望模型生成的内容,并且在训练时用于计算损失,从而指导模型更新权重。这与之前章节的做法类似。
|
||||
|
||||
<img src="../Image/chapter7/figure7.9.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.9.png" width="75%" />
|
||||
</div>
|
||||
|
||||
如图 7.9 所示,我们需要修改自定义的`collate`函数,使其在返回输入 token ID 的基础上,同时返回目标 token ID。
|
||||
|
||||
与第 5 章中描述的 LLM 预训练过程类似,目标 token ID 与输入 token ID 一一对应,但会右移一个位置,这种设置(如图 7.10 所示)使得 LLM 能够学习如何预测序列中的下一个 token。
|
||||
|
||||
<img src="../Image/chapter7/figure7.10.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.10.png" width="75%" />
|
||||
</div>
|
||||
|
||||
以下为更新后的`collate`函数,它根据输入 token ID 生成目标 token ID(流程如图 7.10 所示):
|
||||
|
||||
@@ -396,13 +416,17 @@ tensor([[ 1, 2, 3, 4, 50256], #B
|
||||
|
||||
关于这个过程的更多细节将在实施此修改后讨论。(在第 6 章中,我们无需担心这个问题,因为当时只训练了最后一个输出 token。)
|
||||
|
||||
<img src="../Image/chapter7/figure7.11.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.11.png" width="75%" />
|
||||
</div>
|
||||
|
||||
如图 7.11 所示,在步骤 2.4 中,我们将文本结束 token(之前用作填充 token,token ID 为 50256)在目标 token 列表中替换为 -100(选择 -100 作为替代值的原因将在后续说明)。
|
||||
|
||||
然而,请注意,我们在目标列表中仍保留了一个文本结束 token(ID 为 50256),如图 7.12 所示。这使得 LLM 能够学习在接收到指令时何时生成结束 token,以指示生成的响应已完成。
|
||||
|
||||
<img src="../Image/chapter7/figure7.12.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.12.png" width="75%" />
|
||||
</div>
|
||||
|
||||
在以下代码中,我们修改了自定义的 `collate` 函数,将目标列表中 ID 为 50256 的 token 替换为 -100,图 7.12 展示了这一操作。此外,我们引入了一个 `allowed_max_length` 参数,用于选择性地限制样本的长度。当你使用的数据集超过 GPT-2 模型支持的 1024 个 token 的上下文长度时,这一调整将非常有用。更新后的 `collate` 函数代码如下:
|
||||
|
||||
@@ -527,7 +551,9 @@ loss_1 == loss_3: tensor(True)
|
||||
|
||||
在实践中,除了遮蔽填充 token 外,还常常将指令部分对应的目标 token ID 一并遮蔽,如图 7.13 所示。
|
||||
|
||||
<img src="../Image/chapter7/figure7.13.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.13.png" width="75%" />
|
||||
</div>
|
||||
|
||||
通过对指令部分对应的目标 token ID 进行掩码(如图 7.13 所示),交叉熵损失仅计算生成响应的目标 token ID,模型在训练时也会专注于生成准确的回答,而不是去记住指令内容,从而有助于减少过拟合。
|
||||
|
||||
@@ -545,7 +571,9 @@ loss_1 == loss_3: tensor(True)
|
||||
|
||||
在前一节中,我们完成了 `InstructionDataset` 类和 `custom_collate_fn` 函数的多个实现步骤。本节中,我们可以将 `InstructionDataset` 对象和 `custom_collate_fn` 函数直接传入 PyTorch 的数据加载器中(如图 7.14 所示)。加载器将自动对批次数据进行随机化和组织,为 LLM 的指令微调过程提供支持。
|
||||
|
||||
<img src="../Image/chapter7/figure7.14.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.14.png" width="75%" />
|
||||
</div>
|
||||
|
||||
在我们实现图 7.14 中所示的数据加载器创建步骤之前,我们需要先简要讨论在前一节中实现的 `custom_collate_fn` 中的`device`参数设置。
|
||||
|
||||
@@ -649,7 +677,9 @@ torch.Size([8, 69]) torch.Size([8, 69])
|
||||
|
||||
在正式开始指令微调之前,我们首先需要加载一个预训练的 GPT 模型,正如图 7.15 所示,该模型是我们希望进行微调的对象。
|
||||
|
||||
<img src="../Image/chapter7/figure7.15.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.15.png" width="75%" />
|
||||
</div>
|
||||
|
||||
如 7.15 概述了完整的指令微调流程,本节重点介绍第 4 步,即加载预训练的 LLM ,作为指令微调的起点,过程与前几章类似。然而,这次我们加载的是 3.55 亿参数的中等模型,而非之前使用的 1.24 亿参数的小模型。选择更大模型的原因是 1.24 亿参数的小模型容量有限,难以通过指令微调获得令人满意的效果。”
|
||||
|
||||
@@ -758,7 +788,9 @@ Convert the active sentence to passive: 'The chef cooks the
|
||||
|
||||
图 7.16 中的章节概述展示了本节的重点:对大语言模型(LLM)进行微调。我们将在上一节加载的预训练模型基础上,利用本章前面准备的指令数据集进一步训练该模型。
|
||||
|
||||
<img src="../Image/chapter7/figure7.16.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.16.png" width="75%" />
|
||||
</div>
|
||||
|
||||
如前所述,我们在本章开头实现指令数据集处理时,已经完成了所有关键工作。对于微调过程本身,我们可以复用第 5 章中实现的损失计算和训练函数:
|
||||
|
||||
@@ -798,7 +830,9 @@ Validation loss: 3.7619335651397705
|
||||
|
||||
表格 7.1 提供了在不同设备(包括 CPU 和 GPU)上训练每个模型的参考运行时间。在兼容的 GPU 上运行此代码无需修改代码,并且能够显著加快训练速度。对于本章展示的结果,我使用了 GPT-2 中型模型,并在 A100 GPU 上进行了训练。
|
||||
|
||||
<img src="../Image/chapter7/table_7.1.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/table_7.1.png" width="75%" />
|
||||
</div>
|
||||
|
||||
模型和数据加载器准备好后,我们可以开始训练模型。以下代码设置了训练过程的各项配置,包括初始化优化器、设置训练轮次、定义评估频率,并基于之前提到的第一个验证集样本(val_data[0])来评估训练过程中生成的 LLM 响应:
|
||||
|
||||
@@ -866,7 +900,9 @@ plot_losses(epochs_tensor, tokens_seen, train_losses, val_losses)
|
||||
|
||||
由此生成的损失曲线如图 7.17 所示。
|
||||
|
||||
<img src="../Image/chapter7/figure7.17.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.17.png" width="75%" />
|
||||
</div>
|
||||
|
||||
如图 7.17 的损失图所示,模型在训练集和验证集上的表现随着训练的进行显著提高。在初期阶段,损失的快速下降表明模型正在迅速学习数据中的有意义的模式和表示。随着训练进入第二个 epoch,损失继续减少,但速度放缓,表明模型正在微调其学习到的表示,并逐渐收敛到一个稳定的解。
|
||||
|
||||
@@ -884,7 +920,9 @@ plot_losses(epochs_tensor, tokens_seen, train_losses, val_losses)
|
||||
|
||||
在之前内容中,我们已经对 LLM 在指令数据集的训练部分进行微调,现在我们开始评估其在测试集上的表现。为此,我们首先对测试集中的每个输入生成模型的回答,并收集这些结果以便人工分析,详见图 7.18。
|
||||
|
||||
<img src="../Image/chapter7/figure7.18.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.18.png" width="75%" />
|
||||
</div>
|
||||
|
||||
我们从步骤 7 开始(详见图 7.18),通过`generate`函数输出模型回答,并将其与预期的前三个测试集答案并排展示,便于进行对比:
|
||||
|
||||
@@ -1039,7 +1077,9 @@ medium355M-sft.pth")).
|
||||
|
||||
之前章节中,我们通过查看模型在测试集中的 3 个示例上的响应来评估指令微调模型的性能。虽然这种方法可以提供模型表现的大致概况,但不适合用于大规模响应的评估。因此,我们在本节中实现了一种新方法(如图 7.19 的章节概览所示),利用另一个更大的大语言模型对微调模型的响应进行自动化评估。
|
||||
|
||||
<img src="../Image/chapter7/figure7.19.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.19.png" width="75%" />
|
||||
</div>
|
||||
|
||||
为了实现图 7.19 中第 9 步(以自动化方式评估测试集响应),我们使用了 Meta AI 开发的一个经过指令微调的 Llama 3 模型,该模型拥有 80 亿参数,可以通过开源应用程序 Ollama 在本地运行(官网:[https://ollama.com](https://ollama.com))。
|
||||
|
||||
@@ -1059,7 +1099,9 @@ Ollama 是一个高效的应用程序,适用于在笔记本电脑上运行大
|
||||
|
||||
在实现模型评估代码之前,我们需要先下载 Llama 3 模型,并通过命令行验证 Ollama 是否正常运行。
|
||||
|
||||
<img src="../Image/chapter7/figure7.20.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.20.png" width="75%" />
|
||||
</div>
|
||||
|
||||
如图 7.20 所示,在另一终端中运行 Ollama 应用程序或 Ollama 服务后,请在命令行(不是在 Python 会话中)执行以下命令来运行具有 80 亿参数的 Llama 3 模型:
|
||||
|
||||
@@ -1386,7 +1428,9 @@ Average score: 54.16
|
||||
|
||||
本章总结了大语言模型(LLM)开发流程的关键步骤,包括实现 LLM 架构、预训练模型以及针对特定任务的微调,具体内容可参考图 7.21。
|
||||
|
||||
<img src="../Image/chapter7/figure7.21.png" width="75%" />
|
||||
<div style="text-align: center;">
|
||||
<img src="Image/chapter7/figure7.21.png" width="75%" />
|
||||
</div>
|
||||
|
||||
接下来的小节将为你提供一些思路,帮助你在完成图 7.21 中展示的关键步骤后,进一步探索下去。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user