docs:第六章 大模型训练流程实践 图片格式 参考格式修改
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 77 KiB |
BIN
docs/chapter6/images/3-1.png
Normal file
BIN
docs/chapter6/images/3-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 193 KiB |
@@ -8,33 +8,45 @@
|
|||||||
- 从零实现的 LLM 训练无法较好地实现多卡分布式训练,训练效率较低;
|
- 从零实现的 LLM 训练无法较好地实现多卡分布式训练,训练效率较低;
|
||||||
- 和现有预训练 LLM 不兼容,无法使用预训练好的模型参数
|
- 和现有预训练 LLM 不兼容,无法使用预训练好的模型参数
|
||||||
|
|
||||||
因此,在本章中,我们将介绍目前 LLM 领域的主流训练框架 transformers,并结合分布式框架 deepspeed、高效微调框架 peft 等主流框架,实践使用 transformers 进行模型 Pretrain、SFT 全流程,更好地对接业界的主流 LLM 技术方案。
|
因此,在本章中,我们将介绍目前 LLM 领域的主流训练框架 Transformers,并结合分布式框架 deepspeed、高效微调框架 peft 等主流框架,实践使用 transformers 进行模型 Pretrain、SFT 全流程,更好地对接业界的主流 LLM 技术方案。
|
||||||
|
|
||||||
### 6.1.1 框架介绍
|
### 6.1.1 框架介绍
|
||||||
|
|
||||||
transformers 是由 Hugging Face 开发的 NLP 框架,通过模块化设计实现了对 BERT、GPT、LLaMA、T5、ViT 等上百种主流模型架构的统一支持。通过使用 transformers,开发者无需重复实现基础网络结构,通过 AutoModel 类即可一键加载任意预训练。
|
Transformers 是由 Hugging Face 开发的 NLP 框架,通过模块化设计实现了对 BERT、GPT、LLaMA、T5、ViT 等上百种主流模型架构的统一支持。通过使用 Transformers,开发者无需重复实现基础网络结构,通过 AutoModel 类即可一键加载任意预训练,图6.1 为 Hugging Face Transformers 课程首页:
|
||||||
|
|
||||||

|
<div align='center'>
|
||||||
|
<img src="./images/1-1.png" alt="alt text" width="90%">
|
||||||
|
<p>图6.1 Hugging Face Transformers</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
同时,框架内置的 Trainer 类封装了分布式训练的核心逻辑,支持 PyTorch 原生 DDP、DeepSpeed、Megatron-LM 等多种分布式训练策略。通过简单配置训练参数,即可实现数据并行、模型并行、流水线并行的混合并行训练,在 8 卡 A100 集群上可轻松支持百亿参数模型的高效训练。配合SavingPolicy和LoggingCallback等组件,实现了训练过程的自动化管理。其还支持与 Deepspeed、peft、wandb 等框架进行集成,直接通过参数设置即可无缝对接,从而快速、高效实现 LLM 训练。
|
同时,框架内置的 Trainer 类封装了分布式训练的核心逻辑,支持 PyTorch 原生 DDP、DeepSpeed、Megatron-LM 等多种分布式训练策略。通过简单配置训练参数,即可实现数据并行、模型并行、流水线并行的混合并行训练,在 8 卡 A100 集群上可轻松支持百亿参数模型的高效训练。配合 SavingPolicy 和 LoggingCallback 等组件,实现了训练过程的自动化管理。其还支持与 Deepspeed、peft、wandb、Swanlab 等框架进行集成,直接通过参数设置即可无缝对接,从而快速、高效实现 LLM 训练。
|
||||||
|
|
||||||
对 LLM 时代的 NLP 研究者更为重要的是,HuggingFace 基于 transformers 框架搭建了其庞大的 AI 社区,开放了数亿个预训练模型参数、25万+不同类型数据集,通过 transformers、dataset、evaluate 等多个框架实现对预训练模型、数据集及评估函数的集成,从而帮助开发者可以便捷地使用任一预训练模型,在开源模型及数据集的基础上便捷地实现个人模型的开发与应用。
|
对 LLM 时代的 NLP 研究者更为重要的是,HuggingFace 基于 Transformers 框架搭建了其庞大的 AI 社区,开放了数亿个预训练模型参数、25万+不同类型数据集,通过 Transformers、Dataset、Evaluate 等多个框架实现对预训练模型、数据集及评估函数的集成,从而帮助开发者可以便捷地使用任一预训练模型,在开源模型及数据集的基础上便捷地实现个人模型的开发与应用。
|
||||||
|
|
||||||

|
<div align='center'>
|
||||||
|
<img src="./images/1-2.png" alt="alt text" width="90%">
|
||||||
|
<p>图6.2 Hugging Face Transformers 模型社区</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
在 LLM 时代,模型结构的调整和重新预训练越来越少,开发者更多的业务应用在于使用预训练好的 LLM 进行 Post Train 和 SFT,来支持自己的下游业务应用。且由于预训练模型体量大,便捷集成 deepspeed 等分布式训练框架逐渐成为 LLM 时代 NLP 模型训练的必备技能。因此,transformers 已逐步成为学界、业界 NLP 技术的主流框架,不管是企业业务开发还是科研研究,都逐渐首选 transformers 进行模型实现。同时,新发布的开源 LLM 如 DeepSeek、Qwen 也都会第一时间在 transformers 社区开放其预训练权重与模型调用 Demo。通过使用 transformers 框架,可以高效、便捷地完成 LLM 训练及开发,实现工业级的产出交付。接下来,我们就会以 transformers 框架为基础,介绍如何通过 transformers 框架实现 LLM 的 Pretrain 及 SFT。
|
在 LLM 时代,模型结构的调整和重新预训练越来越少,开发者更多的业务应用在于使用预训练好的 LLM 进行 Post Train 和 SFT,来支持自己的下游业务应用。且由于预训练模型体量大,便捷集成 deepspeed 等分布式训练框架逐渐成为 LLM 时代 NLP 模型训练的必备技能。因此,Transformers 已逐步成为学界、业界 NLP 技术的主流框架,不管是企业业务开发还是科研研究,都逐渐首选 Transformers 进行模型实现。同时,新发布的开源 LLM 如 DeepSeek、Qwen 也都会第一时间在 Transformers 社区开放其预训练权重与模型调用 Demo。通过使用 Transformers 框架,可以高效、便捷地完成 LLM 训练及开发,实现工业级的产出交付。接下来,我们就会以 Transformers 框架为基础,介绍如何通过 Transformers 框架实现 LLM 的 Pretrain 及 SFT。
|
||||||
|
|
||||||
### 6.1.2 初始化 LLM
|
### 6.1.2 初始化 LLM
|
||||||
|
|
||||||
我们可以使用 transformers 的 AutoModel 类来直接初始化已经实现好的模型。对于任意预训练模型,其参数中都包含有模型的配置信息。如果是想要从头训练一个 LLM,可以使用一个已有的模型架构来直接初始化。这里,我们以 Qwen-2.5-1.5B (https://huggingface.co/Qwen/Qwen2.5-1.5B/tree/main)的模型架构为例:
|
我们可以使用 transformers 的 AutoModel 类来直接初始化已经实现好的模型。对于任意预训练模型,其参数中都包含有模型的配置信息。如果是想要从头训练一个 LLM,可以使用一个已有的模型架构来直接初始化。这里,我们以 [Qwen-2.5-1.5B](https://huggingface.co/Qwen/Qwen2.5-1.5B/tree/main)的模型架构为例:
|
||||||
|
|
||||||

|
<div align='center'>
|
||||||
|
<img src="./images/1-3.png" alt="alt text" width="90%">
|
||||||
|
<p>图6.3 Qwen-2.5-1.5B</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
该界面即为 HuggingFace 社区中的 Qwen-2.5-1.5B 模型参数,其中的 config.json 文件即是模型的配置信息,包括了模型的架构、隐藏层大小、模型层数等:
|
该界面即为 HuggingFace 社区中的 Qwen-2.5-1.5B 模型参数,其中的 `config.json` 文件即是模型的配置信息,包括了模型的架构、隐藏层大小、模型层数等,如图6.4所示:
|
||||||
|
|
||||||

|
<div align='center'>
|
||||||
|
<img src="./images/1-4.png" alt="alt text" width="90%">
|
||||||
|
<p>图6.4 Qwen-2.5-1.5B config.json 文件</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
我们可以沿用该模型的配置信息,初始化一个 Qwen-2.5-1.5B 模型来进行训练,也可以在该配置信息的基础上进行更改,如修改隐藏层大小、注意力头数等,来定制一个模型结构。HuggingFace 提供了 python 工具来便捷下载想使用的模型参数:
|
我们可以沿用该模型的配置信息,初始化一个 Qwen-2.5-1.5B 模型来进行训练,也可以在该配置信息的基础上进行更改,如修改隐藏层大小、注意力头数等,来定制一个模型结构。HuggingFace 提供了 Python 工具来便捷下载想使用的模型参数:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import os
|
import os
|
||||||
@@ -44,9 +56,12 @@ os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
|
|||||||
os.system('huggingface-cli download --resume-download Qwen/Qwen2.5-1.5B --local-dir your_local_dir')
|
os.system('huggingface-cli download --resume-download Qwen/Qwen2.5-1.5B --local-dir your_local_dir')
|
||||||
```
|
```
|
||||||
|
|
||||||
此处的 “Qwen/Qwen2.5-1.5B”即为要下载模型的标识符,对于其他模型,可以直接复制 HuggingFace 上的模型名即可:
|
如图6.5,此处的 “Qwen/Qwen2.5-1.5B”即为要下载模型的标识符,对于其他模型,可以直接复制 HuggingFace 上的模型名即可:
|
||||||
|
|
||||||

|
<div align='center'>
|
||||||
|
<img src="./images/1-5.png" alt="alt text" width="90%">
|
||||||
|
<p>图6.5 模型下载标识</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
下载完成后,可以使用 AutoConfig 类直接加载下载好的配置文件:
|
下载完成后,可以使用 AutoConfig 类直接加载下载好的配置文件:
|
||||||
|
|
||||||
@@ -69,11 +84,14 @@ from transformers import AutoModelForCausalLM
|
|||||||
model = AutoModelForCausalLM.from_config(config,trust_remote_code=True)
|
model = AutoModelForCausalLM.from_config(config,trust_remote_code=True)
|
||||||
```
|
```
|
||||||
|
|
||||||
由于 LLM 一般都是 CausalLM 架构,此处使用了 AutoModelForCausalLM 类进行加载。如果是用于分类任务训练,可使用 AutoModelForSequenceClassification 类来加载。查看该 model,可以看到其架构和定义的配置文件相同:
|
由于 LLM 一般都是 CausalLM 架构,此处使用了 AutoModelForCausalLM 类进行加载。如果是用于分类任务训练,可使用 AutoModelForSequenceClassification 类来加载。查看该 model,图6.6可以看到其架构和定义的配置文件相同:
|
||||||
|
|
||||||

|
<div align='center'>
|
||||||
|
<img src="./images/1-6.png" alt="alt text" width="70%">
|
||||||
|
<p>图6.6 模型结构输出结果</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
该 model 就是一个从零初始化的 Qwen-2.5-1.5B 模型了。一般情况下,我们很少从零初始化 LLM 进行预训练,较多的做法是继承一个预训练好的 LLM ,在自己的语料上进行后训练。这里,我们也介绍如何从下载好的模型参数中初始化一个预训练好的模型。
|
该 model 就是一个从零初始化的 Qwen-2.5-1.5B 模型了。一般情况下,我们很少从零初始化 LLM 进行预训练,较多的做法是加载一个预训练好的 LLM 权重,在自己的语料上进行后训练。这里,我们也介绍如何从下载好的模型参数中初始化一个预训练好的模型。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from transformers import AutoModelForCausalLM
|
from transformers import AutoModelForCausalLM
|
||||||
@@ -105,13 +123,16 @@ from datasets import load_dataset
|
|||||||
ds = load_dataset('json', data_files='/mobvoi_seq_monkey_general_open_corpus.jsonl')
|
ds = load_dataset('json', data_files='/mobvoi_seq_monkey_general_open_corpus.jsonl')
|
||||||
```
|
```
|
||||||
|
|
||||||
注意,由于数据集较大,加载可能会出现时间较长或内存不够的情况,建议前期测试时将预训练数据集拆分一部分出来进行测试。加载出来的 ds 是一个 DatasetDict 对象,加载的数据会默认保存在 train 键对应的值中,可以通过以下代码查看:
|
注意,由于数据集较大,加载可能会出现时间较长或内存不够的情况,建议前期测试时将预训练数据集拆分一部分出来进行测试。加载出来的 ds 是一个 DatasetDict 对象,加载的数据会默认保存在 `train` 键对应的值中,可以通过以下代码查看:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
ds["train"][0]
|
ds["train"][0]
|
||||||
```
|
```
|
||||||
|
|
||||||

|
<div align='center'>
|
||||||
|
<img src="./images/1-7.png" alt="alt text" width="100%">
|
||||||
|
<p>图6.7 数据集展示</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
可以通过 feature 属性查看数据集的特征(也就是列),这里需要保存一下数据集的列名,因为后续数据处理时,再将文本 tokenize 之后,需要移除原先的文本:
|
可以通过 feature 属性查看数据集的特征(也就是列),这里需要保存一下数据集的列名,因为后续数据处理时,再将文本 tokenize 之后,需要移除原先的文本:
|
||||||
|
|
||||||
@@ -229,13 +250,13 @@ trainer = Trainer(
|
|||||||
trainer.train()
|
trainer.train()
|
||||||
```
|
```
|
||||||
|
|
||||||
注:上述代码存放于 ./code/pretrian.ipynb 文件中。
|
> 注:上述代码存放于 `./code/pretrian.ipynb` 文件中。
|
||||||
|
|
||||||
### 6.1.5 使用 DeepSpeed 实现分布式训练
|
### 6.1.5 使用 DeepSpeed 实现分布式训练
|
||||||
|
|
||||||
由于预训练规模大、时间长,一般不推荐使用 Jupyter Notebook 来运行,容易发生中断。且由于预训练规模大,一般需要使用多卡进行分布式训练,否则训练时间太长。在这里,我们介绍如何基于上述代码,使用 DeepSpeed 框架实现分布式训练,从而完成业界可用的 LLM Pretrain。
|
由于预训练规模大、时间长,一般不推荐使用 Jupyter Notebook 来运行,容易发生中断。且由于预训练规模大,一般需要使用多卡进行分布式训练,否则训练时间太长。在这里,我们介绍如何基于上述代码,使用 DeepSpeed 框架实现分布式训练,从而完成业界可用的 LLM Pretrain。
|
||||||
|
|
||||||
长时间训练一般使用 sh 脚本设定超参,再启动写好的 python 脚本实现训练。我们使用一个 python 脚本(./code/pretrain.py)来实现训练全流程。
|
长时间训练一般使用 bash 脚本设定超参,再启动写好的 python 脚本实现训练。我们使用一个 Python 脚本(`./code/pretrain.py`)来实现训练全流程。
|
||||||
|
|
||||||
先导入所需第三方库:
|
先导入所需第三方库:
|
||||||
|
|
||||||
@@ -444,7 +465,7 @@ wandb.init(project="pretrain", name="from_scrach")
|
|||||||
|
|
||||||
在启动训练后,终端会输出 wandb 监测的 url,点击即可观察训练进度。此处不再赘述 wandb 的使用细节,欢迎读者查阅相关的资料说明。
|
在启动训练后,终端会输出 wandb 监测的 url,点击即可观察训练进度。此处不再赘述 wandb 的使用细节,欢迎读者查阅相关的资料说明。
|
||||||
|
|
||||||
完成上述代码后,我们使用一个 sh 脚本(./code/pretrain.sh)定义超参数的值,并通过 deepspeed 启动训练,从而实现高效的多卡分布式训练:
|
完成上述代码后,我们使用一个 sh 脚本(`./code/pretrain.sh`)定义超参数的值,并通过 Deepspeed 启动训练,从而实现高效的多卡分布式训练:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 设置可见显卡
|
# 设置可见显卡
|
||||||
@@ -477,7 +498,7 @@ deepspeed pretrain.py \
|
|||||||
--report_to wandb
|
--report_to wandb
|
||||||
# --resume_from_checkpoint ${output_model}/checkpoint-20400 \
|
# --resume_from_checkpoint ${output_model}/checkpoint-20400 \
|
||||||
```
|
```
|
||||||
在安装了 deepspeed 第三方库后,可以直接通过 deepspeed 命令来启动多卡训练。上述脚本命令主要是定义了各种超参数的值,可参考使用。在第四章中,我们介绍了 DeepSpeed 分布式训练的原理和 ZeRO 阶段设置,在这里,我们使用 ZeRO-2 进行训练。此处加载了 ds_config_zero.json 作为 DeepSpeed 的配置参数:
|
在安装了 Deepspeed 第三方库后,可以直接通过 Deepspeed 命令来启动多卡训练。上述脚本命令主要是定义了各种超参数的值,可参考使用。在第四章中,我们介绍了 DeepSpeed 分布式训练的原理和 ZeRO 阶段设置,在这里,我们使用 ZeRO-2 进行训练。此处加载了 `ds_config_zero.json` 作为 DeepSpeed 的配置参数:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -534,17 +555,17 @@ deepspeed pretrain.py \
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
最后,在终端 sh 运行该 pretrain.sh 脚本即可开始训练。
|
最后,在终端 bash 运行该 `pretrain.sh` 脚本即可开始训练。
|
||||||
|
|
||||||
## 6.2 模型有监督微调
|
## 6.2 模型有监督微调
|
||||||
|
|
||||||
在上一节,我们介绍了如何使用 transformers 框架快速、高效地进行模型预训练。在本部分,我们将基于上部分内容,介绍如何使用 transformers 框架对预训练好的模型进行有监督微调。
|
在上一节,我们介绍了如何使用 Transformers 框架快速、高效地进行模型预训练。在本部分,我们将基于上部分内容,介绍如何使用 Transformers 框架对预训练好的模型进行有监督微调。
|
||||||
|
|
||||||
### 6.2.1 Pretrain VS SFT
|
### 6.2.1 Pretrain VS SFT
|
||||||
|
|
||||||
首先需要回顾一下,对 LLM 进行预训练和进行有监督微调的核心差异在于什么。在第四章中提到过,目前成型的 LLM 一般通过 Pretrain-SFT-RLHF 三个阶段来训练,在 Pretrain 阶段,会对海量无监督文本进行自监督建模,来学习文本语义规则和文本中的世界知识;在 SFT 阶段,一般通过对 Pretrain 好的模型进行指令微调,即训练模型根据用户指令完成对应任务,从而使模型能够遵循用户指令,根据用户指令进行规划、行动和输出。因此,Pretrain 和 SFT 均使用 CLM 建模,其核心差异在于,Pretrain 使用海量无监督文本进行训练,模型直接对文本执行“预测下一个 token”的任务;而 SFT 使用构建成对的指令对数据,模型根据输入的指令,建模后续的输出。反映到具体的训练实现上,Pretrain 会对全部 text 进行 loss 计算,要求模型对整个文本实现建模预测;而 SFT 仅对输出进行 loss 计算,不计算指令部分的 loss。
|
首先需要回顾一下,对 LLM 进行预训练和进行有监督微调的核心差异在于什么。在第四章中提到过,目前成型的 LLM 一般通过 Pretrain-SFT-RLHF 三个阶段来训练,在 Pretrain 阶段,会对海量无监督文本进行自监督建模,来学习文本语义规则和文本中的世界知识;在 SFT 阶段,一般通过对 Pretrain 好的模型进行指令微调,即训练模型根据用户指令完成对应任务,从而使模型能够遵循用户指令,根据用户指令进行规划、行动和输出。因此,Pretrain 和 SFT 均使用 CLM 建模,其核心差异在于,Pretrain 使用海量无监督文本进行训练,模型直接对文本执行“预测下一个 token”的任务;而 SFT 使用构建成对的指令对数据,模型根据输入的指令,建模后续的输出。反映到具体的训练实现上,Pretrain 会对全部 text 进行 loss 计算,要求模型对整个文本实现建模预测;而 SFT 仅对输出进行 loss 计算,不计算指令部分的 loss。
|
||||||
|
|
||||||
因此,相较于上一节完成的 Pretrain 代码,SFT 部分仅需要修改数据处理环节,实现对指令对数据转化为训练样本的构建,其余部分和 Pretrain 是完全一致的实现逻辑。本部分代码脚本为 ./code/finetune.py。
|
因此,相较于上一节完成的 Pretrain 代码,SFT 部分仅需要修改数据处理环节,实现对指令对数据转化为训练样本的构建,其余部分和 Pretrain 是完全一致的实现逻辑。本部分代码脚本为`./code/finetune.py`。
|
||||||
|
|
||||||
### 6.2.2 微调数据处理
|
### 6.2.2 微调数据处理
|
||||||
|
|
||||||
@@ -622,7 +643,7 @@ for i in tqdm(range(len(sources))):
|
|||||||
```
|
```
|
||||||
上述代码沿承了 Qwen 的 Chat Template 逻辑,读者也可以根据自己的偏好进行修改,其核心点在于 User 的文本不需要拟合,因此 targets 中 User 对应的文本内容是使用的 IGNORE_TOKEN_ID 进行遮蔽,而 Assistant 对应的文本内容则是文本原文,是需要计算 loss 的。目前主流 LLM IGNORE_TOKEN_ID 一般设置为 -100。
|
上述代码沿承了 Qwen 的 Chat Template 逻辑,读者也可以根据自己的偏好进行修改,其核心点在于 User 的文本不需要拟合,因此 targets 中 User 对应的文本内容是使用的 IGNORE_TOKEN_ID 进行遮蔽,而 Assistant 对应的文本内容则是文本原文,是需要计算 loss 的。目前主流 LLM IGNORE_TOKEN_ID 一般设置为 -100。
|
||||||
|
|
||||||
完成拼接后,将 tokenize 后的数值序列转化为 tensor,再拼接成 Dataset 所需的字典返回即可:
|
完成拼接后,将 tokenize 后的数值序列转化为 `Torch.tensor`,再拼接成 Dataset 所需的字典返回即可:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
input_ids = torch.tensor(input_ids)
|
input_ids = torch.tensor(input_ids)
|
||||||
@@ -662,7 +683,7 @@ class SupervisedDataset(Dataset):
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
该类继承自 torch 的 Dataset 类,可以直接在 Trainer 中使用。完成数据处理后,基于上一节脚本,修改数据处理逻辑即可,后续模型训练等几乎完全一致,此处附上主函数逻辑:
|
该类继承自 Torch 的 Dataset 类,可以直接在 Trainer 中使用。完成数据处理后,基于上一节脚本,修改数据处理逻辑即可,后续模型训练等几乎完全一致,此处附上主函数逻辑:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 加载脚本参数
|
# 加载脚本参数
|
||||||
@@ -756,7 +777,7 @@ trainer.save_model()
|
|||||||
|
|
||||||
## 6.3 高效微调
|
## 6.3 高效微调
|
||||||
|
|
||||||
在前面几节,我们详细介绍了基于 transformers 框架对模型进行 Pretrain、SFT 以及 RLHF 的原理和实践细节。但是,由于 LLM 参数量大,训练数据多,通过上述方式对模型进行训练(主要指 SFT 及 RLHF)需要调整模型全部参数,资源压力非常大。对资源有限的企业或课题组来说,如何高效、快速对模型进行领域或任务的微调,以低成本地使用 LLM 完成目标任务,是非常重要的。
|
在前面几节,我们详细介绍了基于 Transformers 框架对模型进行 Pretrain、SFT 以及 RLHF 的原理和实践细节。但是,由于 LLM 参数量大,训练数据多,通过上述方式对模型进行训练(主要指 SFT 及 RLHF)需要调整模型全部参数,资源压力非常大。对资源有限的企业或课题组来说,如何高效、快速对模型进行领域或任务的微调,以低成本地使用 LLM 完成目标任务,是非常重要的。
|
||||||
|
|
||||||
### 6.3.1 高效微调方案
|
### 6.3.1 高效微调方案
|
||||||
|
|
||||||
@@ -764,9 +785,12 @@ trainer.save_model()
|
|||||||
|
|
||||||
**Adapt Tuning**。即在模型中添加 Adapter 层,在微调时冻结原参数,仅更新 Adapter 层。
|
**Adapt Tuning**。即在模型中添加 Adapter 层,在微调时冻结原参数,仅更新 Adapter 层。
|
||||||
|
|
||||||
具体而言,其在预训练模型每层中插入用于下游任务的参数,即 Adapter 模块,在微调时冻结模型主体,仅训练特定于任务的参数。
|
具体而言,其在预训练模型每层中插入用于下游任务的参数,即 Adapter 模块,在微调时冻结模型主体,仅训练特定于任务的参数,如图6.8所示。
|
||||||
|
|
||||||

|
<div align='center'>
|
||||||
|
<img src="./images/3-1.png" alt="alt text" width="90%">
|
||||||
|
<p>图6.8 Adapt Tuning</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
每个 Adapter 模块由两个前馈子层组成,第一个前馈子层将 Transformer 块的输出作为输入,将原始输入维度 $d$ 投影到 $m$,通过控制 $m$ 的大小来限制 Adapter 模块的参数量,通常情况下 $m << d$。在输出阶段,通过第二个前馈子层还原输入维度,将 $m$ 重新投影到 $d$,作为 Adapter 模块的输出(如上图右侧结构)。
|
每个 Adapter 模块由两个前馈子层组成,第一个前馈子层将 Transformer 块的输出作为输入,将原始输入维度 $d$ 投影到 $m$,通过控制 $m$ 的大小来限制 Adapter 模块的参数量,通常情况下 $m << d$。在输出阶段,通过第二个前馈子层还原输入维度,将 $m$ 重新投影到 $d$,作为 Adapter 模块的输出(如上图右侧结构)。
|
||||||
|
|
||||||
@@ -813,9 +837,12 @@ $$h = W_0 x + \Delta W x = W_0 x + B A x$$
|
|||||||
|
|
||||||
在开始训练时,对 $A$ 使用随机高斯初始化,对 $B$ 使用零初始化,然后使用 Adam 进行优化。
|
在开始训练时,对 $A$ 使用随机高斯初始化,对 $B$ 使用零初始化,然后使用 Adam 进行优化。
|
||||||
|
|
||||||
训练思路如图:
|
训练思路如图6.9所示:
|
||||||
|
|
||||||

|
<div align='center'>
|
||||||
|
<img src="./images/3-2.jpg" alt="alt text" width="90%">
|
||||||
|
<p>图6.9 LoRA</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
#### (2)应用于 Transformer
|
#### (2)应用于 Transformer
|
||||||
|
|
||||||
@@ -1016,11 +1043,14 @@ trainer = Trainer(
|
|||||||
trainer.train()
|
trainer.train()
|
||||||
```
|
```
|
||||||
|
|
||||||
如果是应用在 DPO、KTO 上,则也相同的加入 LoRA 参数并通过 get_peft_model 获取一个 LoRA 模型即可,其他的不需要进行任何修改。但要注意的是,LoRA 微调能够大幅度降低显卡占用,且在下游任务适配上能够取得较好的效果,但如果是需要学习对应知识的任务,LoRA 由于只调整低秩矩阵,难以实现知识的注入,一般效果不佳,因此不推荐使用 LoRA 进行模型预训练或后训练。
|
如果是应用在 DPO、KTO 上,则也相同的加入 LoRA 参数并通过 `get_peft_model` 获取一个 LoRA 模型即可,其他的不需要进行任何修改。但要注意的是,LoRA 微调能够大幅度降低显卡占用,且在下游任务适配上能够取得较好的效果,但如果是需要学习对应知识的任务,LoRA 由于只调整低秩矩阵,难以实现知识的注入,一般效果不佳,因此不推荐使用 LoRA 进行模型预训练或后训练。
|
||||||
|
|
||||||
**参考资料**
|
**参考资料**
|
||||||
|
|
||||||
1. [LoRA: Low-rank Adaptation of Large Language Models](https://arxiv.org/pdf/2106.09685.pdf)
|
[1] Neil Houlsby, Andrei Giurgiu, Stanislaw Jastrzebski, Bruna Morrone, Quentin de Laroussilhe, Andrea Gesmundo, Mona Attariyan, and Sylvain Gelly. (2019). *Parameter-Efficient Transfer Learning for NLP.* arXiv preprint arXiv:1902.00751.
|
||||||
2. [Intrinsic Dimensionality Explains the Effectiveness of Language Model Fine-Tuning](https://arxiv.org/abs/2012.13255)
|
|
||||||
3. [Parameter-Efficient Transfer Learning for NLP](http://proceedings.mlr.press/v97/houlsby19a/houlsby19a.pdf)
|
[2] Edward J. Hu, Yelong Shen, Phillip Wallis, Zeyuan Allen-Zhu, Yuanzhi Li, Shean Wang, Lu Wang, and Weizhu Chen. (2021). *LoRA: Low-Rank Adaptation of Large Language Models.* arXiv preprint arXiv:2106.09685.
|
||||||
4. [Prefix-Tuning: Optimizing Continuous Prompts for Generation](https://arxiv.org/abs/2101.00190)
|
|
||||||
|
[3] Armen Aghajanyan, Luke Zettlemoyer, and Sonal Gupta. (2020). *Intrinsic Dimensionality Explains the Effectiveness of Language Model Fine-Tuning.* arXiv preprint arXiv:2012.13255.
|
||||||
|
|
||||||
|
[4] Xiang Lisa Li 和 Percy Liang. (2021). *Prefix-Tuning: Optimizing Continuous Prompts for Generation.* arXiv preprint arXiv:2101.00190.
|
||||||
Reference in New Issue
Block a user