分布式訓練training-operator和pytorch-distributed?RANK變量不統(tǒng)一解決
正文
我們在使用 training-operator 框架來實現(xiàn) pytorch 分布式任務時,發(fā)現(xiàn)一個變量不統(tǒng)一的問題:在使用 pytorch 的分布式 launch 時,需要指定一個變量是 node_rank 。同時,在 OpenMMLab 框架的 dist_train.sh 里,讀取的系統(tǒng)環(huán)境變量是 NODE_RANK(如果系統(tǒng)里 NODE_RANK 沒有被指定,則用默認值0)。
dist_train.sh
#!/usr/bin/env bash
CONFIG=$1
GPUS=$2
NNODES=${NNODES:-1}
NODE_RANK=${NODE_RANK:-0} # 如果NODE_RANK沒有被設置為系統(tǒng)變量,則使用默認值0
PORT=${PORT:-29500}
MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"}
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
python -m torch.distributed.launch \
--nnodes=$NNODES \
--node_rank=$NODE_RANK \ # 作為torch.distributed.launch參數的一部分
--master_addr=$MASTER_ADDR \
--nproc_per_node=$GPUS \
--master_port=$PORT \
$(dirname "$0")/train.py \
$CONFIG \
--seed 0 \
--launcher pytorch ${@:3}
而在 training-operator 里,NODE_RANK 這個環(huán)境變量是以 RANK 的形式出現(xiàn)的。

這就會導致:通過 training-operator 啟動的訓練 pod 里只有 RANK 變量,沒有 NODE_RANK 變量,那么, dist_train.sh 里的 $NODE_RANK 變量是一個默認值 0,每一個被啟動的訓練 pod 里的 NODE_RANK 也是 0。這會讓每個pod都認為自己是第 0 個,每個 pod 都無法感知到別的 pod 的存在,那就會各自為政,在自己的 NODE 節(jié)點上重復性的做單機多卡的分布式訓練。
那么,為了實現(xiàn)多機多卡的訓練,就勢必需要解決 training-operator 提供的環(huán)境變量 RANK 與 torch.distributed.launch 需要的環(huán)境變量 NODE_RANK 的不統(tǒng)一的問題。
解決的思路有兩個方向
- 保持 training-operator 的 RANK 變量不變,在訓練的 pod 容器里,將 RANK 變量賦值給 NODE_RANK
- 修改 training-operator,添加 NODE_RANK 變量,并將 NODE_RANK 變量的值設為 RANK 的值
這里選第二個,因為第一個方案沒走通。。。
- 首先,將 training-operator 克隆到本地:GitHub - kubeflow/training-operator: Training operators on Kubernetes.
- 接著,全局搜索 RANK,發(fā)現(xiàn)該變量只出現(xiàn)在
./pkg/controller.v1/pytorch/envvar.g里:

- 然后,添加一個
name=NODE_RANK,value= strconv.Itoa(rank)的環(huán)境變量
func setPodEnv(obj interface{}, podTemplateSpec *corev1.PodTemplateSpec, rtype, index string) error {
pytorchjob, ok := obj.(*kubeflowv1.PyTorchJob)
if !ok {
return fmt.Errorf("%+v is not a type of PyTorchJob", obj)
}
for i := range podTemplateSpec.Spec.Containers {
// Initialize the environment variables.
if len(podTemplateSpec.Spec.Containers[i].Env) == 0 {
podTemplateSpec.Spec.Containers[i].Env = make([]corev1.EnvVar, 0)
}
// Set PYTHONUNBUFFERED to true, to disable output buffering.
// Ref https://stackoverflow.com/questions/59812009/what-is-the-use-of-pythonunbuffered-in-docker-file.
podTemplateSpec.Spec.Containers[i].Env = append(
podTemplateSpec.Spec.Containers[i].Env, corev1.EnvVar{
Name: "PYTHONUNBUFFERED",
Value: "0",
})
// If the master is not null, then we need to set the MASTER_ADDR and RANK.
if pytorchjob.Spec.PyTorchReplicaSpecs[kubeflowv1.PyTorchJobReplicaTypeMaster] != nil {
envVars, err := GetMasterEnvVarGenerator().Generate(pytorchjob)
if err != nil {
return err
}
// Set master related environment variables.
podTemplateSpec.Spec.Containers[i].Env = append(
podTemplateSpec.Spec.Containers[i].Env, envVars...)
// Set world size and rank.
rank, err := strconv.Atoi(index)
if err != nil {
return err
}
if rtype == strings.ToLower(string(kubeflowv1.PyTorchJobReplicaTypeWorker)) {
rank = rank + 1
}
totalReplicas := getTotalReplicas(pytorchjob)
podTemplateSpec.Spec.Containers[i].Env = append(podTemplateSpec.Spec.Containers[i].Env, corev1.EnvVar{
Name: "WORLD_SIZE",
Value: strconv.Itoa(int(totalReplicas)),
})
podTemplateSpec.Spec.Containers[i].Env = append(podTemplateSpec.Spec.Containers[i].Env, corev1.EnvVar{
Name: "RANK",
Value: strconv.Itoa(rank),
})
// 新增一個名為NODE_RANK的環(huán)境變量
podTemplateSpec.Spec.Containers[i].Env = append(podTemplateSpec.Spec.Containers[i].Env, corev1.EnvVar{
Name: "NODE_RANK",
Value: strconv.Itoa(rank),
})
}
// Set the elastic environment variables if the elasticPolicy is not null.
if pytorchjob.Spec.ElasticPolicy != nil {
envVars, err := GetElasticEnvVarGenerator().Generate(pytorchjob)
if err != nil {
return err
}
// Set elastic related environment variables.
podTemplateSpec.Spec.Containers[i].Env = append(
podTemplateSpec.Spec.Containers[i].Env, envVars...)
}
}
return nil
}
- 重新編譯:
go build & docker build
# Build manager binary.
go build -o bin/manager cmd/training-operator.v1/main.go
# Build docker image with the manager.
docker build -t ${IMG} -f build/images/training-operator/Dockerfile .
# Push docker image with the manager.
docker push ${IMG}
- 替換掉默認的鏡像,在./manifests/base/deployment.yaml里修改鏡像地址為上一步驟docker push的地址

- 重新部署, 在./manifests/overlays/standalone目錄下
kubectl apply -k .
獲得 NODE_RANK變量
如下:

以上就是分布式訓練training-operator和pytorch-distributed RANK變量不統(tǒng)一解決的詳細內容,更多關于pytorch RANK變量不統(tǒng)一的資料請關注腳本之家其它相關文章!

