在单张消费级显卡上微调大型语言模型

原文:towardsdatascience.com/fine-tuning-llms-on-a-single-consumer-graphic-card-6de1587daddb

生成式 AI

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/56d3ec173cf4d5da4ec03feaffcb830d.png

作者(Midjourney)的图片

背景

当我们想到大型语言模型或其他任何生成模型时,首先想到的硬件是 GPU。没有 GPU,许多生成 AI、机器学习、深度学习和数据科学的进步将是不可能的。如果 15 年前,游戏玩家热衷于最新的 GPU 技术,那么今天数据科学家和机器学习工程师也加入了他们,并追求这个领域的新闻。尽管通常游戏玩家和 ML 用户在关注两种不同的 GPU 和显卡。

游戏用户通常使用消费级显卡(如 NVIDIA GeForce RTX 系列 GPU),而机器学习和人工智能开发者通常关注数据中心和云计算 GPU(如 V100、A100 或 H100)的新闻。与数据中心 GPU(通常在 40GB 到 80GB 范围内)相比,游戏显卡通常具有更少的 GPU 内存(截至 2024 年 1 月最多 24GB)。此外,它们的价格也是另一个显著差异。虽然大多数消费级显卡的价格可能高达 3000 美元,但大多数数据中心显卡的价格从那个价格开始,并且可以轻松达到数万美元。

由于许多人,包括我自己,可能为了游戏或日常使用而拥有一张消费级显卡,他们可能想知道是否可以使用相同的显卡来训练、微调或推理 LLM 模型。在 2020 年,我写了一篇关于我们是否可以使用消费级显卡进行数据科学项目的全面文章(文章链接)。当时,模型大多是小型机器学习或深度学习模型,甚至一张 6GB 内存的显卡也能处理许多训练项目。但,在这篇文章中,我将使用这样的显卡来训练具有数十亿参数的大型语言模型。

对于这篇文章,我使用了我的 GeForce 3090 RTX 显卡,它拥有 24GB 的 GPU 内存。供您参考,数据中心显卡如 A100 和 H100 分别拥有 40GB 和 80GB 的内存。此外,典型的 AWS EC2 p4d.24xlarge 实例拥有 8 个 GPU(V100),总共有 320GB 的 GPU 内存。如您所见,简单消费级 GPU 与典型云 ML 实例之间的差异是显著的。但问题是,我们是否可以在单张消费级显卡上训练大型模型?如果是的话,有哪些技巧和经验教训?阅读本文的其余部分,以了解详情。

硬件和软件设置

在加载任何 LLM 模型或训练数据集之前,我们需要找出我们为此过程所需的硬件和软件。

如前所述,我使用了 NVIDIA GeForce RTX 3090 GPU,因为它在消费级 GPU 中内存(24GB)容量最高(顺便说一句:4090 型号的内存容量也相同)。它是基于 Ampere 架构,与著名的 A100 GPU 所采用的架构相同。您可以在这里了解更多关于 GeForce RTX 3090 GPU 的规格信息。

经过所有测试后,我相信 24GB 是我们与具有数十亿参数的 LLMs 工作所需的最小 GPU 内存量。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/06e946ea85aa105af132c9194bc7594a.png

图片由作者提供

除了显卡之外,我们还需要确保我们的 PC 有一个良好的通风系统。在微调过程中,GPU 的温度很容易升高,其风扇不足以保持其冷却。更高的 GPU 温度会降低 GPU 性能,这个过程将花费更长的时间。

除了硬件之外,还有一些软件方面的考虑因素我必须在此提及。首先,如果您是 Windows 用户,我有一个坏消息要告诉您。一些库和工具只能在 Linux 上运行。具体来说,bitsandbytes,它经常被用于模型量化,并不适合 Windows。有些人为 Windows 制作了包装器(例如这里),但它们各有优缺点。因此,我的建议是要么在 WSL 上安装 Linux,要么像我一样有一个双启动系统,并在您处理 LLMs 时完全切换到 Linux。

此外,您还需要安装 PyTorch 和兼容的 CUDA 版本。我的建议是安装 CUDA 12.3(链接)。然后您需要访问这个页面,根据您的系统、CUDA 版本和包管理器系统,下载并安装正确的 PyTorch(pytorch.org/)。

注意:如果您使用的是 CUDA 12.3,您可能需要在您的.bashrc 文件中添加或配置 BNB_CUDA_VERSION 和 LD_LIBRARY_PATH 环境变量。以下是一个参考示例。

export BNB_CUDA_VERSION=123
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/<YOUR-USER-DIR>/local/cuda-12.3

最后,您需要在您的系统上安装以下软件包。我建议创建一个新的虚拟环境(venv),以避免与系统上已安装的其他软件包冲突。此外,以下是我成功使用的软件包版本供您参考:

torch==2.1.2
transformers==4.36.2
datasets==2.16.1
bitsandbytes==0.42.0
peft==0.7.1

技术背景

现在您已经为在您的系统上处理 LLMs 准备好了所有硬件和软件,最好简要回顾一下您将在下一节中遇到的技术概念。

大型语言模型包含数百万或数十亿个参数。通常,我们在经过长时间训练过程并在数十亿美元的成本之后,使用在数十亿甚至数千亿个标记上训练过的预训练模型。每个模型参数都需要 32 位(4 字节)的内存来加载。一般来说,每 10 亿个参数大约需要 4GB 的内存来仅加载模型。使用更少内存加载(以及稍后推理或训练模型)的一种技术是“量化”。在这种技术中,我们将模型权重的精度从 32 位全精度(fb32)降低到 16 位(fp16 或 bfloat16)、8 位(int8)甚至更低(了解更多)。正如你可以想象的那样,通过降低模型权重的精度,我们可以将更大的模型加载到有限的内存中,但这是以降低模型性能为代价的。然而,一些研究表明,fp32 和 bfloat16 之间的模型性能差异微不足道,许多著名模型(包括 Llama2)都是在 bfloat16 上预训练的。

量化是我们必须在单个具有 24GB 内存的 GPU 上微调或推理大型语言模型时必须使用的技术。稍后你将看到bitsandbytes库可以帮助我们实现模型量化。

即使使用最严格的量化技术,我们仍然无法预先训练一个拥有数百万参数的小型 LLM 模型。Chris Fregley 等人在他们新出版的在 AWS 上的生成式 AI一书中,给出了训练模型所需的内存的良好经验法则。正如他们解释的那样,对于一个模型的每 10 亿个参数,我们需要 6GB 的内存(以 16 位半精度计算)来加载和训练模型。记住,内存大小只是训练故事的一部分。完成预训练所需的时间是另一个重要部分。例如,最小的 Llama2 模型(即 Llama2 7B)有 70 亿个参数,它花费了 184320 个 GPU 小时来完成其训练(了解更多)。

因此,大多数人(即使拥有大量的硬件资源和预算)也更愿意使用预训练模型,并且只为他们的特定用例进行微调。然而,在资源有限(如单个 GPU)的情况下,完整的微调过程可能会令人望而却步。因此,参数高效微调(PEFT),它仅更新模型参数的有限子集,在更少的计算资源下看起来更加现实。

在不同的 PEFT 技术中,LoRA(低秩适配)因其计算效率而非常受欢迎。在这种技术中,我们冻结所有原始模型权重,取而代之的是训练低秩矩阵,这些矩阵可以添加到 Transformer 架构的特定层中(了解更多)。在许多情况下,使用 LoRA 对 LLM 进行微调时,我们更新了 0.5%的模型权重。

QLoRA 是 LoRA 的一种变体,它结合了 LoRA 和我们所解释的量化概念。具体来说,在我们的 QLoRA 实现中,我们将使用 nf4 或正常浮点 4 进行模型的微调。QLoRA 在我们的案例研究中对使用单个消费级 GPU 微调大型模型非常有帮助。

编码时间

最后,到了编码时间!

你可以在我的 GitHub 仓库这里找到工作状态的 Jupyter Notebook。对于这段代码的许多部分,我受到了 Mathieu Busquet 在这篇整洁的文章的启发并遵循了他的指示。

我不会逐行讨论代码,但我会强调对在单个 GPU 上微调大型模型重要的代码部分。

Transformer 模型

首先,我选择了Mistral 7B 模型 (mistralai/Mistral-7B-v0.1) 进行这次测试。Mistral AI 开发的 Mistral 7B 模型是一个于 2023 年 9 月发布的开源 LLM(论文链接)。从许多方面来看,这个模型在性能上超过了 Llama2 等著名模型(见下文图表)。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/eea25778c0b43ae3dd37d6059ee96cda.png

来自 Mistral 发布博客文章的图片(mistral.ai/news/announcing-mistral-7b/)

数据集

此外,我使用了 Databricks 的 databricks-dolly-15k 数据集(在 CC BY-SA 3.0 许可下)进行微调(了解更多)。我使用了这个数据集的一个小子集(1000 行)来减少微调时间和证明概念。

配置

在模型加载时,我使用了以下量化配置来克服我面临的 GPU 内存限制。

quantization_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
)

这种量化配置对于在单个 GPU 上微调模型至关重要,因为它有一个低精度存储数据类型,nf4(4 位正常浮点),这是一个 4 位数据类型,以及一个计算数据类型,即bfloat16。在实践中,这意味着每当使用 QLORA 权重张量时,我们将张量反量化为bfloat16,然后以 16 位执行矩阵乘法(在原始论文中了解更多)。

此外,如前所述,我们正在使用 LoRA 与量化结合,称为 QLoRA,以克服内存限制。以下是 LoRA 的配置:

lora_config = LoraConfig(
    r=16,
    lora_alpha=64,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj"], 
    bias="none",
    lora_dropout=0.05,
    task_type="CAUSAL_LM",
)

对于我的 LoRA 配置,我将 rank 设置为 16。建议将 rank 设置在 4 到 16 之间,以在减少可训练参数数量和模型性能之间取得良好的平衡。最后,我们将 LoRA 应用于 Mistral 7B Transformer 模型中的一部分线性层。

训练和监控

使用我的单张显卡,我可以完成 4 个训练周期(1000 步)。对我来说,进行此类测试的一个目的,即在单个本地 GPU 上训练一个 LLM,是为了在没有任何限制的情况下监控硬件资源。在训练期间监控 GPU 的最简单工具之一是 Nvidia 系统管理接口(SMI)。只需打开一个终端,并在您的命令行中输入:

nvidia-smi

或者,为了持续监控和更新,使用(每秒刷新一次):

nvidia-smi -l 1

因此,您将看到 GPU 上每个进程的内存使用情况。在下面的 SMI 视图中,我只是加载了模型,它大约消耗了 5GB 的内存(归功于量化)。此外,如您所见,模型是由 Anaconda3 Python(一个 Jupyter 笔记本)进程加载的。

+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 545.23.08              Driver Version: 545.23.08    CUDA Version: 12.3     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA GeForce RTX 3090        On  | 00000000:29:00.0  On |                  N/A |
| 30%   37C    P8              33W / 350W |   5346MiB / 24576MiB |      5%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|    0   N/A  N/A      1610      G   /usr/lib/xorg/Xorg                          179MiB |
|    0   N/A  N/A      1820      G   /usr/bin/gnome-shell                         41MiB |
|    0   N/A  N/A    108004      G   ...2023.3.3/host-linux-x64/nsys-ui.bin        8MiB |
|    0   N/A  N/A    168032      G   ...seed-version=20240110-180219.406000      117MiB |
|    0   N/A  N/A    327503      C   /home/***/anaconda3/bin/python             4880MiB |
+---------------------------------------------------------------------------------------+

这里(以下快照)是训练过程大约 30 步后的内存状态。如您所见,现在使用的 GPU 内存约为 15GB。

+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 545.23.08              Driver Version: 545.23.08    CUDA Version: 12.3     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA GeForce RTX 3090        On  | 00000000:29:00.0  On |                  N/A |
| 30%   57C    P2             341W / 350W |  15054MiB / 24576MiB |    100%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|    0   N/A  N/A      1610      G   /usr/lib/xorg/Xorg                          179MiB |
|    0   N/A  N/A      1820      G   /usr/bin/gnome-shell                         40MiB |
|    0   N/A  N/A    108004      G   ...2023.3.3/host-linux-x64/nsys-ui.bin        8MiB |
|    0   N/A  N/A    168032      G   ...seed-version=20240110-180219.406000      182MiB |
|    0   N/A  N/A    327503      C   /home/***/anaconda3/bin/python            14524MiB |
+---------------------------------------------------------------------------------------+

虽然 SMI 是一个简单的监控 GPU 内存使用的工具,但仍有一些高级监控工具可以提供更详细的信息。这些高级工具之一是 PyTorch 内存快照,您可以在这篇有趣的文章中了解更多信息。

摘要

在这篇文章中,我向您展示了在单个 24GB GPU(如 NVIDIA GeForce RTX 3090 GPU)上微调大型语言模型(如 Mistral 7B)的可能性。然而,正如详细讨论的那样,需要特殊的 PEFT 技术,如 QLoRA。此外,模型的批大小也很重要,我们可能需要更长的训练时间,仅仅是因为我们的资源有限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值