使用 Kubernetes 和 Intel® Gaudi® 加速器微调 LLM
介绍
用于文本生成的大型语言模型 (LLM) 越来越受欢迎,但众所周知,由于模型和数据集的规模庞大,训练这些模型需要大量的计算能力。Intel® Gaudi® 加速器为微调这些模型提供了一个强大、经济且可扩展的解决方案。而 Kubernetes(简称 K8s)是跨节点集群运行容器化工作负载的典范。在本博客中,我们将深入探讨如何使用 K8s 集群中的 Intel Gaudi 加速器节点,在 tatsu-lab/alpaca 数据集上微调最先进的模型,例如 meta-llama/Llama-3-8B-Instruct。
目录
组件
在本教程中,我们将使用多个 Intel Gaudi HPU 卡,通过 Hugging Face 数据集微调 Llama 3-8B-Instruct。要在集群上运行此作业,需要涉及几个不同的组件,这些将在本博客中进一步解释。
Helm Chart
我们要谈的第一个组件是 Helm chart。Helm 将我们作业中使用的所有不同组件整合在一起,并允许我们使用一个 helm install
命令部署所有内容。我们示例中使用的 K8s 资源包括:
- 用于运行 optimum-habana 示例的作业 (Job)
- 用作工作进程之间共享存储位置的持久卷声明 (PVC),用于数据集和模型文件
- 用于 🤗 门控模型的 Secret(可选)
- 数据访问 Pod(可选)
Helm chart 有一个 values.yaml 文件,其中包含用于 K8s 资源 spec 文件中的参数。我们的 values 文件包括 K8s 资源名称、工作 Pod 使用的容器镜像/标签、HPU 和内存资源、Python 脚本的参数等。当 Helm chart 部署时,values 将根据使用的 values 文件填充到 K8s spec 文件中。
容器
K8s 在容器化环境中运行作业,因此接下来我们需要一个 Docker 容器。这里,我们需要包含训练作业所需的所有依赖项。
Dockerfile 和 docker-compose.yaml 构建以下镜像:
- 一个 optimum-habana 基础镜像,它使用用于 Gaudi 的 PyTorch Docker 镜像作为基础,然后安装 optimum-habana 和 DeepSpeed 的 Habana 分支。
- 一个 optimum-habana-examples 镜像,它构建在 optimum-habana 基础之上。它包含来自示例目录中 requirements.txt 文件的安装以及此 GitHub 存储库 的克隆,以便运行示例脚本。
# Specify the base image name/tag
export BASE_IMAGE_NAME=vault.habana.ai/gaudi-docker/1.17.1/ubuntu22.04/habanalabs/pytorch-installer-2.3.1
export BASE_IMAGE_TAG=latest
# Specify your Gaudi Software version and Optimum Habana version
export GAUDI_SW_VER=1.17.1
export OPTIMUM_HABANA_VER=1.13.0
git clone https://github.com/huggingface/optimum-habana.git
# Note: Modify the requirements.txt file in the kubernetes directory for the specific example(s) that you want to run
cd optimum-habana/examples/kubernetes
# Set variables for your container registry and repository
export REGISTRY=<Your container registry>
export REPO=<Your container repository>
# Build the images
docker compose build
# Push the optimum-habana-examples image to a container registry
docker push <image name>:<tag>
包名 | 版本 | 目的 |
---|---|---|
PyTorch | 2.3.1 | 训练模型的基础框架 |
🤗 Transformers | 4.44.2 | 用于下载和微调 Hugging Face 模型的库 |
Optimum Habana | 1.13.0 | Transformers 和 Diffusers 库与 Intel Gaudi AI 加速器 (HPU) 之间的接口 |
DeepSpeed 的 Habana 分支 | 1.17.1 | 用于在 HPU 上使用 DeepSpeed 的 DeepSpeed 的 Habana 分支 |
微调脚本
我们使用的 Python 脚本 使用 LoRA 对因果语言模型进行微调以生成文本。此脚本是 Optimum Habana 示例脚本 中的一个。
在此示例中,Optimum-Habana 存储库被克隆到 optimum-habana-examples 容器中,因此无需进一步操作。如果您希望使用自己的脚本,可以使用基础 optimum-habana 容器来 COPY
脚本。
存储
本博客将使用 Hugging Face 数据集,但存储位置也可以用于自定义数据集,以及使用带有 NFS 后端 存储类 的普通 K8s 集群从 optimum-habana 示例脚本输出。如果您使用的是云服务提供商,则可以使用云存储桶代替。存储位置会挂载到容器中,这样我们就可以从该位置进行读写访问,而无需将其构建到镜像中。为了实现这一点,我们使用了 持久卷声明 (PVC)。
Secret
门控或私有模型要求您登录才能下载模型。如果正在训练的模型不是门控或私有模型,则不需要此操作。为了从 K8s 作业进行身份验证,我们使用 Hugging Face 用户只读访问令牌 定义一个 secret。来自 secret 的令牌将被挂载到容器中。
集群要求
本教程需要一个带有 Gaudi 加速器的 Kubernetes 集群。您还需要将 适用于 Kubernetes 的 Intel Gaudi 设备插件 部署到您的集群中。
如果能够使用 kubectl get nodes
列出节点,kubectl auth can-i get nodes
命令将返回“yes”,例如
NAME STATUS ROLES AGE VERSION
gaudi161432 Ready worker 48d v1.11.13
否则,请咨询您的集群管理员以获取您的用户组可用的节点列表。
知道节点名称后,使用 kubectl describe node <节点名称>
获取其 HPU 和内存容量。我们将在后续设置工作 Pod 规范时使用此信息。
教程:使用 Kubernetes 集群微调 Llama 3
客户端要求
kubectl
已安装 并配置为连接到您的集群- Helm
- 克隆
Optimum-Habana
存储库
可选
- 已获授权访问模型各自的 Hugging Face 模型卡页面上的
Meta-Llama-3-8B-Instruct
、Llama2-7b-hf
或同等模型。否则,可以使用huggyllama
模型而无需任何访问批准。请注意,本教程展示的是Meta-Llama-3-8B-Instruct
。
步骤 1:使用 Hugging Face 令牌设置 Secret
获取一个具有读取权限的 Hugging Face 令牌,并使用您的终端通过 echo <您的令牌> | base64
获取令牌的 base64 编码。
例如:
$ echo hf_ABCDEFG | base64
aGZfQUJDREVGRwo=
将编码后的令牌值复制并粘贴到 values.yaml
文件中 secret
部分的 encodedToken
字段。例如,要运行多卡 LoRA 微调作业,打开 examples/kubernetes/ci/multi-card-lora-clm-values.yaml
文件,并将编码后的令牌粘贴到第 41 行。
secret:
encodedToken: aGZfQUJDREVGRwo=
步骤 2:自定义 values.yaml 参数
examples/kubernetes/ci/multi-card-lora-clm-values.yaml
文件已设置为使用 tatsu-lab/alpaca 数据集微调 huggyllama/llama-7b。对于此博客,我们将模型名称更改为 meta-llama/Llama-3-8B-Instruct
。您还可以填写 dataset_name
以使用 Hugging Face 数据集,或者为自定义数据集提供 train_file
和 validation_file
路径。
同样,可以更改 values 文件以调整训练作业的数据集、epochs、最大步数、学习率、LoRA 配置、启用 bfloat16 等。
values 文件还包含设置 Pod 安全上下文 的参数,其中包含您的用户和组 ID,以允许以非 root 用户身份运行微调脚本。如果未设置用户和组 ID,则将以 root 身份运行。
# -- Specify a pod security context to run as a non-root user
podSecurityContext: {}
# runAsUser: 1000
# runAsGroup: 3000
# fsGroup: 2000
在下面的代码片段中指定要使用的 Gaudi 卡数量。Kubernetes 的 Intel Gaudi 设备插件能够将 Gaudi 加速器注册到容器集群中,用于计算工作负载。
resources:
limits:
# -- Specify the number of Gaudi card(s)
habana.ai/gaudi: &hpus 2
# -- Specify CPU resource limits for the job
cpu: 16
# -- Specify Memory limits requests for the job
memory: 256Gi
# -- Specify hugepages-2Mi requests for the job
hugepages-2Mi: 4400Mi
requests:
# -- Specify the number of Gaudi card(s)
habana.ai/gaudi: *hpus
# -- Specify CPU resource requests for the job
cpu: 16
# -- Specify Memory resource requests for the jobs
memory: 256Gi
# -- Specify hugepages-2Mi requests for the job
hugepages-2Mi: 4400Mi
values.yaml 文件中还有其他参数需要根据您的集群进行配置
storage:
# -- Name of the storage class to use for the persistent volume claim.
storageClassName: nfs-client
# -- Access modes for the persistent volume.
accessModes:
- "ReadWriteMany"
# -- Storage resources
resources:
requests:
storage: 30Gi
# -- Locaton where the PVC will be mounted in the pods
pvcMountPath: &pvcMountPath /tmp/pvc-mount
# -- A data access pod will be deployed when set to true
deployDataAccessPod: true
最后,最重要的 Python 命令
command:
- python
- /workspace/optimum-habana/examples/gaudi_spawn.py
- --world_size
- *hpus
- --use_mpi
- /workspace/optimum-habana/examples/language-modeling/run_lora_clm.py
- --model_name_or_path
- meta-llama/Llama-3-8B-Instruct
- --dataset_name
- tatsu-lab/alpaca
- --bf16=True
- --output_dir
- *pvcMountPath
- --num_train_epochs
- "3"
- --do_train
- --do_eval
- --use_habana
- --validation_split_percentage=4
- --adam_epsilon=1e-08
# Note, this has been shorted for cleanliness purposes.
步骤 3:将 Helm chart 部署到集群
将 Helm chart 部署到集群
# Navigate to the `examples/kubernetes` directory
cd examples/kubernetes
# Deploy the job using the Helm chart, specifying your values file name with the -f parameter
helm install -f ci/multi-card-lora-clm-values.yaml optimum-habana-examples-2card . -n <namespace>
步骤 4:监控作业
Helm chart 部署到集群后,K8s 资源(如 secret、PVC 和 worker pod)将被创建。可以通过使用 kubectl get pods
查看 pod 状态来监控作业。起初,pod 将显示为“Pending”(挂起),因为容器正在被拉取和创建,然后状态应变为“Running”(运行中)。
$ kubectl get pods -n <namespace>
NAME READY STATUS RESTARTS AGE
optimum-habana-examples-2card-dataaccess 1/1 Running 0 1m22s
optimum-habana-examples-2card-gaudijob 1/1 Running 0 1m22s
使用 kubectl logs <pod 名称> -n <命名空间>
查看训练日志。您还可以添加 -f
来流式传输日志。
$ kubectl logs optimum-habana-examples-2card-gaudijob
...
{'loss': 0.9585, 'grad_norm': 0.1767578125, 'learning_rate': 0.0001, 'epoch': 1.21, 'memory_allocated (GB)': 18.01, 'max_memory_allocated (GB)': 84.32, 'total_memory_available (GB)': 94.62}
步骤 5:下载训练好的模型
作业完成后,训练好的模型可以从 /tmp/pvc-mount/output/saved_model
(values 文件中为 --output_dir
参数定义的路径)复制到本地系统,使用以下命令:
kubectl cp --namespace <namespace> optimum-habana-examples-2card-dataaccess:/tmp/pvc-mount/output/saved_model .
步骤 6:清理
最后,可以使用 helm uninstall
命令及 Helm 作业名称从集群中删除资源。所有已部署的 Helm 版本列表可以通过 helm list
查看。
helm uninstall --namespace <namespace> optimum-habana-examples-2card
卸载 Helm chart 后,集群上的资源将显示“Terminating”(正在终止)状态,最终它们将消失。
结果
我们使用 Alpaca 数据集 在我们的 k8s 集群上,利用 Intel Gaudi HPU 上的 2 张卡对 Llama3-8B-Instruct 进行了 3 个 epoch 的微调。我们根据喜好在 multi-card-lora-clm-values.yaml
文件中调整了一些参数。衡量文本生成模型的准确性可能很棘手,因为没有明确的正确或错误答案。相反,我们使用 困惑度 指标来粗略衡量模型对其预测的置信度。我们预留了 4% 的数据用于评估。您的示例输出应包含以下内容:
***** train metrics *****
epoch
max_memory_allocated (GB)
memory_allocated (GB)
total_flos
total_memory_available (GB)
train_loss
train_runtime
train_samples_per_second
train_steps_per_second
...
***** eval metrics *****
epoch
eval_accuracy
eval_loss
eval_runtime
eval_samples
eval_samples_per_second
eval_steps_per_second
max_memory_allocated (GB)
memory_allocated (GB)
perplexity
total_memory_available (GB)
下一步
既然我们已经使用 Alpaca 数据集微调了 Llama 3-8B-Instruct,您可能想知道如何利用这些信息在 K8s 上运行您自己的工作负载。如果您使用的是 Llama 3 或类似的生成式文本模型,那么您很幸运,因为可以重复使用相同的 Docker 容器和脚本。您只需编辑适用的 values.yaml
文件的参数,以使用您的数据集并根据需要调整其他参数(学习率、epochs 等)。如果您想使用自己的微调脚本,您需要构建并推送一个包含运行训练作业所需库以及您的脚本的 Docker 容器。
请继续关注我们的下一篇博客,我们将介绍如何使用 K8s 和 Gaudi 进行多节点 LLM 微调。
本教程的所有脚本、Dockerfile 和 spec 文件都可以在 examples/kubernetes
中找到。
致谢
感谢为本文做出贡献并帮助审阅的同事:Dina Jones、Harsha Ramayanam、Abolfazl Shahbazi 和 Melanie Buehler
引用
@article{llama3modelcard,
title={Llama 3 Model Card},
author={AI@Meta},
year={2024},
url = {https://github.com/meta-llama/llama3/blob/main/MODEL_CARD.md}
}
@misc{alpaca,
author = {Rohan Taori and Ishaan Gulrajani and Tianyi Zhang and Yann Dubois and Xuechen Li and Carlos Guestrin and Percy Liang and Tatsunori B. Hashimoto },
title = {Stanford Alpaca: An Instruction-following LLaMA model},
year = {2023},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/tatsu-lab/stanford_alpaca}},
}