在多个CPU上进行高效训练
当在单个CPU上训练速度太慢时,我们可以使用多个CPU。本指南重点介绍基于PyTorch的DDP,以在裸金属和Kubernetes上高效地进行分布式CPU训练。
Intel® oneCCL 绑定 PyTorch
Intel® oneCCL(集体通信库)是一个用于高效分布式深度学习训练的库,实现了诸如allreduce、allgather、alltoall等集体操作。有关oneCCL的更多信息,请参阅oneCCL文档和oneCCL规范。
模块 oneccl_bindings_for_pytorch
(1.12版本之前称为 torch_ccl
)实现了 PyTorch C10D ProcessGroup API,可以动态加载为外部 ProcessGroup,目前仅适用于 Linux 平台
查看oneccl_bind_pt的更多详细信息。
Intel® oneCCL 绑定 PyTorch 安装
以下Python版本的Wheel文件可用:
扩展版本 | Python 3.7 | Python 3.8 | Python 3.9 | Python 3.10 | Python 3.11 |
---|---|---|---|---|---|
2.5.0 | √ | √ | √ | √ | |
2.4.0 | √ | √ | √ | √ | |
2.3.0 | √ | √ | √ | √ | |
2.2.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.4.0。
查看更多关于 oneccl_bind_pt 安装 的方法。
oneCCL 和 PyTorch 的版本必须匹配。
Intel® MPI 库
使用这个基于标准的MPI实现,在Intel®架构上提供灵活、高效、可扩展的集群消息传递。此组件是Intel® oneAPI HPC工具包的一部分。
oneccl_bindings_for_pytorch 已与 MPI 工具集一起安装。使用前需要先配置环境。
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
Intel® PyTorch 扩展安装
Intel Extension for PyTorch (IPEX) 为CPU训练提供了Float32和BFloat16的性能优化(请参阅单CPU部分以了解更多信息)。
以下“在Trainer中的使用”以Intel® MPI库中的mpirun为例。
在Trainer中的使用
要在Trainer中启用多CPU分布式训练并使用ccl后端,用户应在命令参数中添加--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 examples/pytorch/question-answering/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(node0和node1,以node0为主进程)上总共四个进程的训练,ppn(每个节点的进程数)设置为2,每个插槽运行一个进程。变量OMP_NUM_THREADS/CCL_WORKER_COUNT可以调整以获得最佳性能。
在node0中,您需要创建一个包含每个节点IP地址的配置文件(例如hostfile),并将该配置文件的路径作为参数传递。
cat hostfile xxx.xxx.xxx.xxx #node0 ip xxx.xxx.xxx.xxx #node1 ip
现在,在node0中运行以下命令,4DDP将在node0和node1中启用,并带有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 examples/pytorch/question-answering/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的Intel扩展、PyTorch的Intel oneCCL绑定以及用于容器间通信的OpenSSH。
下面的代码片段是一个Dockerfile的示例,它使用了一个支持分布式CPU训练的基础镜像,然后将Transformers版本提取到/workspace
目录中,以便示例脚本包含在镜像中:
FROM intel/intel-optimized-pytorch:2.4.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.46.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的容器中挂载的路径。此位置可用于数据集、检查点文件以及训练完成后保存的模型。
下面的代码片段是一个用于PyTorchJob的yaml文件示例,该作业有4个工作者运行问答示例。
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 units 中定义,其中1个CPU单位相当于1个物理CPU核心或1个虚拟核心(取决于节点是物理主机还是虚拟机)。yaml中定义的CPU和内存限制/请求应小于单台机器上可用的CPU/内存容量。通常不建议使用机器的全部容量,以便为kubelet和操作系统留出一些资源。为了获得工作pod的“guaranteed” 服务质量,应为资源限制和请求设置相同的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
查看工作者的日志。添加 -f
以流式传输日志,例如:
kubectl logs transformers-pytorchjob-worker-0 -n ${NAMESPACE} -f
训练任务完成后,训练好的模型可以从PVC或存储位置复制。当你完成任务后,可以使用kubectl delete -f pytorchjob.yaml -n ${NAMESPACE}
从集群中删除PyTorchJob资源。
摘要
本指南介绍了在裸金属和Kubernetes集群上使用多个CPU运行分布式PyTorch训练任务。这两种情况都利用了Intel Extension for PyTorch和Intel oneCCL Bindings for PyTorch以获得最佳训练性能,并且可以作为模板在多节点上运行您自己的工作负载。
< > Update on GitHub