没错!我在单个消费级显卡上微调大模型

当我们思考大型语言模型或其他生成模型时,首先想到的是 GPU。没有 GPU,许多生成式人工智能、机器学习、深度学习和数据科学方面的进展将是不可能的。如果在15年前,游戏玩家对最新的 GPU 技术充满热情。

今天数据科学家和机器学习工程师也会加入他们,并关注该领域的最新消息。尽管通常游戏玩家和 ML 用户关注的是两种不同类型的 GPU 和显卡。

游戏用户通常使用消费级显卡(如 NVIDIA GeForce RTX 系列 GPU),而 ML 和 AI 开发人员通常关注数据中心和云计算 GPU 的新闻(如 V100、A100 或 H100)。与数据中心 GPU(通常在40GB到80GB的范围内)相比,游戏显卡的 GPU 内存通常要少得多(截至2024年1月最多为24GB)。此外,它们的价格是另一个重要的区别。虽然大多数消费级显卡的价格可能高达3000美元,但大多数数据中心显卡的价格从那个价格开始,很容易就能达到几万美元。

由于许多人,包括我自己,可能拥有一款消费级显卡用于游戏或日常使用,他们可能会想知道他们是否可以将同一款显卡用于 LLM 模型的训练、微调或推理。

在2020年,我写了一篇关于我们是否可以使用消费级显卡进行数据科学项目的全面文章。当时,模型主要是小型 ML 或深度学习模型,即使是具有6GB内存的显卡也能处理许多训练项目。但是,在本文中,我将使用这样一款具有数十亿参数的大型语言模型的显卡。

在我使用了我的 eoforce 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),(FYI:4090型号的内存大小也相同)。它基于安培架构,这与著名的 A100 GPU 使用的架构相同。您可以在此处了解有关GeForce RTX 3090 GPU规格的更多信息。

经过所有测试,我相信24GB是我们需要用于处理数十亿参数的LLM的最小GPU内存量。

除了显卡外,我们需要确保我们的个人电脑有一个良好的通风系统。在微调过程中,GPU 的温度很容易升高,它的风扇不足以保持冷却。较高的 GPU 温度会降低 GPU 的性能,并且处理过程将需要更长的时间。

除了硬件外,这里有一些软件方面的考虑必须提到。首先,如果您是 Windows 用户,我有一个坏消息要告诉您。一些库和工具只能在 Linux 上运行。具体来说,bitsandbytes 在模型量化中经常使用,但在 Windows 上并不友好。有些人为 Windows 制作了一个封装器(例如在此处),但它们有利有弊。因此,我的建议是要么在 WSL 上安装 Linux,要么像我一样拥有双引导系统,并在使用 LLM 时完全切换到 Linux。

此外,您需要安装 PyTorch 和兼容的 CUDA 版本。我的建议是安装 CUDA 12.3(链接)。然后,您需要转到此页面,并根据您的系统、CUDA 版本和包管理器系统下载并安装正确的 PyTorch(https://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//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

技术背景

现在,您的系统已准备好用于处理 LLM,最好对您将在接下来的部分中遇到的技术概念进行简要回顾。

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

量化是我们在使用单个GPU(24GB内存)对大型语言模型进行微调或推理时必须使用的技术。后面您将看到,bitsandbytes 库可以帮助我们实现模型量化。

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

因此,大多数人(即使拥有大量的硬件资源和预算)更倾向于使用预训练模型,并仅对其进行特定用途的微调。尽管如此,完全微调的过程在资源有限的情况下(如单个GPU)可能会很困难。因此,只更新模型参数的有限子集的 Paremer-Efficient Fine-Tuning (PEFT) 看起来更加现实,可以更节省计算资源。

在不同的 PEFT 技术中,LoRA(Low Ranking Adaption)非常流行,因为它具有计算效率。在这种技术中,我们冻结所有原始模型权重,而是训练可以添加到Transformer架构特定层的低秩矩阵(阅读更多)。在许多情况下,使用 LoRA 对LLM 进行微调时,我们更新模型权重的0.5%。

QLoRA 是 LoRA 的变体,结合了我们解释的量化概念。具体来说,在我们的 QLoRA 实现中,我们将使用 nf4 或 Normal Float 4 来微调模型。QLoRA 在我们的案例研究中非常有用,用于在单个消费级GPU 上微调大型模型。

编码时间

最后,是编码时间!

您可以在我的 GitHub 仓库中找到可用的 Jupyter Notebook。在这段代码的许多部分中,我受到了 Mathieu Busquet 在这篇整洁的文章中的启发和指导。地址:github.com/tamiminaser/llm-single-gpu/blob/main/qlora_mistral_7b.ipynb

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

Transformer 模型

首先,我选择了 Mistral 7B 模型(mistralai/Mistral-7B-v0.1)进行测试。Mistral AI 开发的 Mistral 7B 模型是一个开源的LLM,于2023年9月发布(论文链接)。从许多方面来看,该模型的性能优于诸如 Llama2 等知名模型(请参阅以下图表)。

数据集

此外,我使用了 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 配置,我将排名设置为16。建议将排名设置在4到16之间,以在减少可训练参数数量和模型性能之间取得良好的权衡。最后,我们将 LoRA 应用于我们 Mistral 7B transformer 模型中的一部分线性层。

训练和监控

使用我的单个显卡,我完成了4个时期(1000步)的训练。对我来说,进行这样的测试,即在单个本地GPU上训练LLM,其目的之一是监控硬件资源,没有任何限制。在训练过程中,用于监视 GPU 的最简单的工具之一是 Nvidia 系统管理接口(SMI)。只需打开一个终端,并在命令行中键入:

nvidia-smi

或者用于连续监视和更新的命令(每1秒刷新一次):

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 Memory Snapshot,您可以在这篇有趣的文章中阅读更多。

总结

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

技术交流

技术要学会分享、交流,不建议闭门造车。一个人走的很快、一堆人可以走的更远。

建立了NLP&大模型技术交流群, 大模型学习资料、数据代码、技术交流提升, 均可加知识星球交流群获取,群友已超过2000人,添加时切记的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

方式①、微信搜索公众号:机器学习社区,后台回复:技术交流
方式②、添加微信号:mlc2060,备注:技术交流

用通俗易懂的方式讲解系列

参考链接

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

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值