在多个 CPU 上高效训练
当在单个 CPU 上训练速度太慢时,我们可以使用多个 CPU。本指南重点介绍基于 PyTorch 的 DDP,它可以高效地在 裸机 和 Kubernetes 上进行分布式 CPU 训练。
英特尔® oneCCL 针对 PyTorch 的绑定
英特尔® oneCCL(集体通信库)是一个用于高效分布式深度学习训练的库,它实现了诸如 allreduce、allgather、alltoall 之类的集体操作。有关 oneCCL 的更多信息,请参阅 oneCCL 文档 和 oneCCL 规范。
模块 oneccl_bindings_for_pytorch
(在 1.12 版之前为 torch_ccl
)实现了 PyTorch C10D ProcessGroup API,可以动态加载为外部 ProcessGroup,目前仅适用于 Linux 平台。
查看有关 oneccl_bind_pt 的更多详细信息。
英特尔® oneCCL 针对 PyTorch 的绑定安装
以下 Python 版本提供了 Wheel 文件
扩展版本 | Python 3.6 | Python 3.7 | Python 3.8 | Python 3.9 | Python 3.10 |
---|---|---|---|---|---|
2.1.0 | √ | √ | √ | √ | |
2.0.0 | √ | √ | √ | √ | |
1.13.0 | √ | √ | √ | √ | |
1.12.100 | √ | √ | √ | √ | |
1.12.0 | √ | √ | √ | √ |
请运行 pip list | grep torch
以获取您的 pytorch_version
。
pip install oneccl_bind_pt=={pytorch_version} -f https://developer.intel.com/ipex-whl-stable-cpu
其中 {pytorch_version}
应为您的 PyTorch 版本,例如 2.1.0。查看有关 oneccl_bind_pt 安装 的更多方法。oneCCL 和 PyTorch 的版本必须匹配。
oneccl_bindings_for_pytorch 1.12.0 预构建的 wheel 不适用于 PyTorch 1.12.1(它适用于 PyTorch 1.12.0),PyTorch 1.12.1 应适用于 oneccl_bindings_for_pytorch 1.12.100。
英特尔® MPI 库
使用此基于标准的 MPI 实现,在英特尔® 架构上提供灵活、高效、可扩展的集群消息传递。此组件是英特尔® oneAPI HPC 工具包的一部分。
oneccl_bindings_for_pytorch 与 MPI 工具集一起安装。需要在使用它之前设定环境变量。
对于英特尔® oneCCL >= 1.12.0
oneccl_bindings_for_pytorch_path=$(python -c "from oneccl_bindings_for_pytorch import cwd; print(cwd)")
source $oneccl_bindings_for_pytorch_path/env/setvars.sh
对于英特尔® oneCCL 版本 < 1.12.0
torch_ccl_path=$(python -c "import torch; import torch_ccl; import os; print(os.path.abspath(os.path.dirname(torch_ccl.__file__)))")
source $torch_ccl_path/env/setvars.sh
英特尔® 针对 PyTorch 的扩展安装
英特尔针对 PyTorch 的扩展(IPEX)为使用 Float32 和 BFloat16 的 CPU 训练提供了性能优化(请参阅 单 CPU 部分 了解详情)。
以下“在训练器中使用”以英特尔® MPI 库中的 mpirun 为例。
在训练器中使用
要在训练器中使用 ccl 后端启用多 CPU 分布式训练,用户应在命令参数中添加 --ddp_backend ccl
。
让我们以 问答示例 为例
以下命令启用在一个 Xeon 节点上使用 2 个进程进行训练,每个套接字运行一个进程。变量 OMP_NUM_THREADS/CCL_WORKER_COUNT 可以调整以获得最佳性能。
export CCL_WORKER_COUNT=1 export MASTER_ADDR=127.0.0.1 mpirun -n 2 -genv OMP_NUM_THREADS=23 \ python3 run_qa.py \ --model_name_or_path google-bert/bert-large-uncased \ --dataset_name squad \ --do_train \ --do_eval \ --per_device_train_batch_size 12 \ --learning_rate 3e-5 \ --num_train_epochs 2 \ --max_seq_length 384 \ --doc_stride 128 \ --output_dir /tmp/debug_squad/ \ --no_cuda \ --ddp_backend ccl \ --use_ipex
以下命令启用在两个 Xeon(节点 0 和节点 1,以节点 0 为主进程)上总共使用四个进程进行训练,ppn(每个节点的进程数)设置为 2,每个套接字运行一个进程。变量 OMP_NUM_THREADS/CCL_WORKER_COUNT 可以调整以获得最佳性能。
在节点 0 中,您需要创建一个包含每个节点 IP 地址的配置文件(例如 hostfile),并将该配置文件路径作为参数传递。
cat hostfile xxx.xxx.xxx.xxx #node0 ip xxx.xxx.xxx.xxx #node1 ip
现在,在节点 0 中运行以下命令,4DDP 将在节点 0 和节点 1 中启用,并使用 BF16 自动混合精度
export CCL_WORKER_COUNT=1 export MASTER_ADDR=xxx.xxx.xxx.xxx #node0 ip mpirun -f hostfile -n 4 -ppn 2 \ -genv OMP_NUM_THREADS=23 \ python3 run_qa.py \ --model_name_or_path google-bert/bert-large-uncased \ --dataset_name squad \ --do_train \ --do_eval \ --per_device_train_batch_size 12 \ --learning_rate 3e-5 \ --num_train_epochs 2 \ --max_seq_length 384 \ --doc_stride 128 \ --output_dir /tmp/debug_squad/ \ --no_cuda \ --ddp_backend ccl \ --use_ipex \ --bf16
使用 Kubernetes
上一节中相同的分布式训练作业可以使用 Kubeflow PyTorchJob 训练操作符 部署到 Kubernetes 集群。
设置
此示例假定您拥有
- 访问已 安装 Kubeflow 的 Kubernetes 集群
kubectl
已安装并配置为访问 Kubernetes 集群- 可用于存储数据集和模型文件的 持久卷声明 (PVC)。设置 PVC 有多种选择,包括使用 NFS 存储类 或云存储桶。
- 包含您的模型训练脚本和运行脚本所需的所有依赖项的 Docker 容器。对于分布式 CPU 训练作业,这通常包括 PyTorch、Transformers、英特尔针对 PyTorch 的扩展、英特尔 oneCCL 针对 PyTorch 的绑定和 OpenSSH 以在容器之间进行通信。
以下代码段是 Dockerfile 的示例,它使用支持分布式 CPU 训练的基础镜像,然后将 Transformers 版本解压到 /workspace
目录,以便示例脚本包含在镜像中
FROM intel/intel-optimized-pytorch:2.3.0-pip-multinode
RUN apt-get update -y && \
apt-get install -y --no-install-recommends --fix-missing \
google-perftools \
libomp-dev
WORKDIR /workspace
# Download and extract the transformers code
ARG HF_TRANSFORMERS_VER="4.44.0"
RUN pip install --no-cache-dir \
transformers==${HF_TRANSFORMERS_VER} && \
mkdir transformers && \
curl -sSL --retry 5 https://github.com/huggingface/transformers/archive/refs/tags/v${HF_TRANSFORMERS_VER}.tar.gz | tar -C transformers --strip-components=1 -xzf -
在将 PyTorchJob 部署到集群之前,需要构建镜像并将其复制到集群节点或推送到容器注册表。
PyTorchJob 规范文件
Kubeflow PyTorchJob 用于在集群上运行分布式训练作业。PyTorchJob 的 yaml 文件定义了诸如以下参数:
- PyTorchJob 的名称
- 副本(工作器)的数量
- 用于运行训练作业的 python 脚本及其参数
- 每个工作器所需的资源类型(节点选择器、内存和 CPU)
- 要使用的 Docker 容器的镜像/标签
- 环境变量
- 用于 PVC 的卷挂载
卷挂载定义了 PVC 将在每个工作器 Pod 的容器中挂载的路径。此位置可用于数据集、检查点文件以及训练完成后保存的模型。
以下代码段是使用 4 个工作器运行 问答示例 的 PyTorchJob 的 yaml 文件示例。
apiVersion: "kubeflow.org/v1"
kind: PyTorchJob
metadata:
name: transformers-pytorchjob
spec:
elasticPolicy:
rdzvBackend: c10d
minReplicas: 1
maxReplicas: 4
maxRestarts: 10
pytorchReplicaSpecs:
Worker:
replicas: 4 # The number of worker pods
restartPolicy: OnFailure
template:
spec:
containers:
- name: pytorch
image: <image name>:<tag> # Specify the docker image to use for the worker pods
imagePullPolicy: IfNotPresent
command: ["/bin/bash", "-c"]
args:
- >-
cd /workspace/transformers;
pip install -r /workspace/transformers/examples/pytorch/question-answering/requirements.txt;
source /usr/local/lib/python3.10/dist-packages/oneccl_bindings_for_pytorch/env/setvars.sh;
torchrun /workspace/transformers/examples/pytorch/question-answering/run_qa.py \
--model_name_or_path distilbert/distilbert-base-uncased \
--dataset_name squad \
--do_train \
--do_eval \
--per_device_train_batch_size 12 \
--learning_rate 3e-5 \
--num_train_epochs 2 \
--max_seq_length 384 \
--doc_stride 128 \
--output_dir /tmp/pvc-mount/output_$(date +%Y%m%d_%H%M%S) \
--no_cuda \
--ddp_backend ccl \
--bf16 \
--use_ipex;
env:
- name: LD_PRELOAD
value: "/usr/lib/x86_64-linux-gnu/libtcmalloc.so.4.5.9:/usr/local/lib/libiomp5.so"
- name: TRANSFORMERS_CACHE
value: "/tmp/pvc-mount/transformers_cache"
- name: HF_DATASETS_CACHE
value: "/tmp/pvc-mount/hf_datasets_cache"
- name: LOGLEVEL
value: "INFO"
- name: CCL_WORKER_COUNT
value: "1"
- name: OMP_NUM_THREADS # Can be tuned for optimal performance
value: "240"
resources:
limits:
cpu: 240 # Update the CPU and memory limit values based on your nodes
memory: 128Gi
requests:
cpu: 240 # Update the CPU and memory request values based on your nodes
memory: 128Gi
volumeMounts:
- name: pvc-volume
mountPath: /tmp/pvc-mount
- mountPath: /dev/shm
name: dshm
restartPolicy: Never
nodeSelector: # Optionally use nodeSelector to match a certain node label for the worker pods
node-type: gnr
volumes:
- name: pvc-volume
persistentVolumeClaim:
claimName: transformers-pvc
- name: dshm
emptyDir:
medium: Memory
要运行此示例,请根据您的训练脚本和集群中的节点更新 yaml。
yaml 中的 CPU 资源限制/请求以 cpu 单位 定义,其中 1 个 CPU 单位相当于 1 个物理 CPU 内核或 1 个虚拟内核(取决于节点是物理主机还是虚拟机)。yaml 中定义的 CPU 和内存限制/请求量应小于单个机器上可用的 CPU/内存容量。通常建议不要使用整个机器的容量,以便为 kubelet 和操作系统保留一些资源。为了获得 “保证” 服务质量,请为工作器 Pod 设置相同的 CPU 和内存量,用于资源限制和请求。
部署
在使用适合您的集群和训练作业的值更新 PyTorchJob 规范后,可以使用以下方法将其部署到集群中
export NAMESPACE=<specify your namespace>
kubectl create -f pytorchjob.yaml -n ${NAMESPACE}
然后可以使用 kubectl get pods -n ${NAMESPACE}
命令列出您命名空间中的 Pod。您应该看到刚部署的 PyTorchJob 的工作器 Pod。最初,它们的状态可能是“Pending”,因为容器正在被拉取和创建,然后状态应该更改为“Running”。
NAME READY STATUS RESTARTS AGE
...
transformers-pytorchjob-worker-0 1/1 Running 0 7m37s
transformers-pytorchjob-worker-1 1/1 Running 0 7m37s
transformers-pytorchjob-worker-2 1/1 Running 0 7m37s
transformers-pytorchjob-worker-3 1/1 Running 0 7m37s
...
可以使用 kubectl logs <pod name> -n ${NAMESPACE}
查看工作器的日志。添加 -f
以流式传输日志,例如
kubectl logs transformers-pytorchjob-worker-0 -n ${NAMESPACE} -f
训练作业完成后,可以从 PVC 或存储位置复制训练后的模型。完成作业后,可以使用 kubectl delete -f pytorchjob.yaml -n ${NAMESPACE}
从集群中删除 PyTorchJob 资源。
总结
本指南介绍了如何使用裸机和 Kubernetes 集群上的多个 CPU 运行分布式 PyTorch 训练作业。这两种情况都利用了英特尔 PyTorch 扩展和英特尔 oneCCL PyTorch 绑定,以实现最佳训练性能,并且可以用作在多个节点上运行您自己的工作负载的模板。
< > 在 GitHub 上更新