使用 Kubernetes 和 Intel® Gaudi® 加速器微调 LLM

社区文章 发布于 2024 年 9 月 9 日

介绍

用于文本生成的大型语言模型 (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

客户端要求

可选

  • 已获授权访问模型各自的 Hugging Face 模型卡页面上的 Meta-Llama-3-8B-InstructLlama2-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_filevalidation_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 JonesHarsha RamayanamAbolfazl ShahbaziMelanie 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}},
}

社区

注册登录 发表评论