Treinar um modelo com o Ray e o PyTorch no Google Kubernetes Engine (GKE)


Neste guia, demonstramos como treinar um modelo no Google Kubernetes Engine (GKE) usando Ray, PyTorch e o complemento Ray Operator.

Sobre o Ray

O Ray é um framework de computação escalonável e de código aberto para aplicativos de IA/ML. O Ray Train é um componente no Ray desenvolvido para treinamento e ajuste de modelos distribuídos. Você pode usar a API Ray Train para escalonar o treinamento em várias máquinas e fazer a integração com bibliotecas de machine learning, como o PyTorch.

É possível implantar jobs de treinamento do Ray usando os recursos RayCluster ou RayJob. Use um recurso do RayJob ao implantar jobs do Ray na produção pelos seguintes motivos:

  • O recurso RayJob cria um cluster do Ray temporário que pode ser excluído automaticamente quando um job é concluído.
  • O recurso RayJob oferece suporte a políticas de repetição para execução de jobs resilientes.
  • É possível gerenciar jobs do Ray usando os padrões conhecidos da API Kubernetes.

Objetivos

Este guia é destinado a clientes de IA generativa, usuários novos ou atuais do GKE, engenheiros de ML, engenheiros de MLOps (DevOps) ou administradores de plataformas interessados em usar os recursos de orquestração de contêineres do Kubernetes para disponibilizar modelos usando o Ray.

  • Crie um cluster do GKE.
  • Crie um cluster do Ray usando o recurso personalizado do RayCluster.
  • Treine um modelo usando um job do Ray.
  • Implantar um job do Ray usando o recurso personalizado RayJob.

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Saiba mais em Limpeza.

Antes de começar

O Cloud Shell vem pré-instalado com o software necessário para este tutorial, incluindo kubectl e a gcloud CLI. Se você não usa o Cloud Shell, é necessário instalar a gcloud CLI.

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.
  3. To initialize the gcloud CLI, run the following command:

    gcloud init
  4. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Enable the GKE API:

    gcloud services enable container.googleapis.com
  7. Install the Google Cloud CLI.
  8. To initialize the gcloud CLI, run the following command:

    gcloud init
  9. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  10. Make sure that billing is enabled for your Google Cloud project.

  11. Enable the GKE API:

    gcloud services enable container.googleapis.com
  12. Grant roles to your user account. Run the following command once for each of the following IAM roles: roles/container.clusterAdmin, roles/container.admin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE
    • Replace PROJECT_ID with your project ID.
    • Replace USER_IDENTIFIER with the identifier for your user account. For example, user:myemail@example.com.

    • Replace ROLE with each individual role.
  13. Instale o Ray.

Prepare o ambiente

Para preparar o ambiente, siga estas etapas:

  1. Inicie uma sessão do Cloud Shell no Console do Google Cloud clicando em Ícone de ativação do Cloud Shell Ativar o Cloud Shell no Console do Google Cloud. Isso inicia uma sessão no painel inferior do Cloud Console.

  2. Defina as variáveis de ambiente:

    export PROJECT_ID=PROJECT_ID
    export CLUSTER_NAME=ray-cluster
    export COMPUTE_REGION=us-central1
    export COMPUTE_ZONE=us-central1-c
    export CLUSTER_VERSION=CLUSTER_VERSION
    export TUTORIAL_HOME=`pwd`
    

    Substitua:

    • PROJECT_ID: seuID de projeto no Google Cloud.
    • CLUSTER_VERSION: a versão do GKE a ser usada. Precisa ser 1.30.1 ou mais recente.
  3. Clone o repositório do GitHub:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  4. Mude para o diretório de trabalho:

    cd kubernetes-engine-samples/ai-ml/gke-ray/raytrain/pytorch-mnist
    

Crie um cluster do GKE

Crie um cluster do GKE Autopilot ou Padrão:

Piloto automático

Criar um cluster do Autopilot:

gcloud container clusters create-auto ${CLUSTER_NAME}  \
    --enable-ray-operator \
    --cluster-version=${CLUSTER_VERSION} \
    --location=${COMPUTE_REGION}

Padrão

Criar um cluster padrão

gcloud container clusters create ${CLUSTER_NAME} \
    --addons=RayOperator \
    --cluster-version=${CLUSTER_VERSION}  \
    --machine-type=e2-standard-8 \
    --location=${COMPUTE_ZONE} \
    --num-nodes=4

Implantar um recurso do RayCluster

Implante um recurso do RayCluster no cluster:

  1. Analise o seguinte manifesto:

    apiVersion: ray.io/v1
    kind: RayCluster
    metadata:
      name: pytorch-mnist-cluster
    spec:
      rayVersion: '2.9.0'
      headGroupSpec:
        rayStartParams:
          dashboard-host: '0.0.0.0'
        template:
          metadata:
          spec:
            containers:
            - name: ray-head
              image: rayproject/ray:2.9.0
              ports:
              - containerPort: 6379
                name: gcs
              - containerPort: 8265
                name: dashboard
              - containerPort: 10001
                name: client
              resources:
                limits:
                  cpu: "2"
                  memory: "4Gi"
                requests:
                  cpu: "2"
                  memory: "4Gi"
      workerGroupSpecs:
      - replicas: 4
        minReplicas: 1
        maxReplicas: 5
        groupName: worker-group
        rayStartParams: {}
        template:
          spec:
            containers:
            - name: ray-worker
              image: rayproject/ray:2.9.0
              resources:
                limits:
                  cpu: 2
                  memory: "8Gi"
                requests:
                  cpu: 2
                  memory: "8Gi"

    Esse manifesto descreve um recurso personalizado do RayCluster.

  2. Aplique o manifesto ao seu cluster do GKE:

    kubectl apply -f ray-cluster.yaml
    
  3. Verifique se o recurso RayCluster está pronto:

    kubectl get raycluster
    

    O resultado será assim:

    NAME                    DESIRED WORKERS   AVAILABLE WORKERS   CPUS   MEMORY   GPUS   STATUS   AGE
    pytorch-mnist-cluster   2                 2                   6      20Gi     0      ready    63s
    

    Nesta saída, ready na coluna STATUS indica que o recurso do RayCluster está pronto.

Conectar-se ao recurso do RayCluster

Conecte-se ao recurso do RayCluster para enviar um job do Ray.

  1. Verifique se o GKE criou o serviço RayCluster:

    kubectl get svc pytorch-mnist-cluster-head-svc
    

    O resultado será assim:

    NAME                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                                AGE
    pytorch-mnist-cluster-head-svc   ClusterIP   34.118.238.247   <none>        10001/TCP,8265/TCP,6379/TCP,8080/TCP   109s
    
  2. Estabeleça uma sessão de encaminhamento de portas para o Ray head:

    kubectl port-forward svc/pytorch-mnist-cluster-head-svc 8265:8265 2>&1 >/dev/null &
    
  3. Verifique se o cliente Ray pode se conectar ao cluster usando o localhost:

    ray list nodes --address http://localhost:8265
    

    O resultado será assim:

    Stats:
    ------------------------------
    Total: 3
    
    Table:
    ------------------------------
        NODE_ID                                                   NODE_IP     IS_HEAD_NODE    STATE    NODE_NAME    RESOURCES_TOTAL                 LABELS
    0  1d07447d7d124db641052a3443ed882f913510dbe866719ac36667d2  10.28.1.21  False           ALIVE    10.28.1.21   CPU: 2.0                        ray.io/node_id: 1d07447d7d124db641052a3443ed882f913510dbe866719ac36667d2
    # Several lines of output omitted
    

Treinar um modelo

Treinar um modelo PyTorch usando o Conjunto de dados Fashion MNIST:

  1. Envie um job do Ray e aguarde a conclusão:

    ray job submit --submission-id pytorch-mnist-job --working-dir . --runtime-env-json='{"pip": ["torch", "torchvision"]}' --address http://localhost:8265 -- python train.py
    

    O resultado será assim:

    Job submission server address: http://localhost:8265
    
    --------------------------------------------
    Job 'pytorch-mnist-job' submitted successfully
    --------------------------------------------
    
    Next steps
      Query the logs of the job:
        ray job logs pytorch-mnist-job
      Query the status of the job:
        ray job status pytorch-mnist-job
      Request the job to be stopped:
        ray job stop pytorch-mnist-job
    
    Handling connection for 8265
    Tailing logs until the job exits (disable with --no-wait):
    ...
    ...
    
  2. Verifique o status do job:

    ray job status pytorch-mnist
    

    O resultado será assim:

    Job submission server address: http://localhost:8265
    Status for job 'pytorch-mnist-job': RUNNING
    Status message: Job is currently running.
    

    Aguarde até que Status for job seja COMPLETE. Isso pode levar 15 minutos ou mais.

  3. Veja os registros de jobs do Ray:

    ray job logs pytorch-mnist
    

    O resultado será assim:

    Training started with configuration:
    ╭─────────────────────────────────────────────────╮
    │ Training config                                  │
    ├──────────────────────────────────────────────────┤
    │ train_loop_config/batch_size_per_worker       8  │
    │ train_loop_config/epochs                     10  │
    │ train_loop_config/lr                      0.001  │
    ╰─────────────────────────────────────────────────╯
    
    # Several lines omitted
    
    Training finished iteration 10 at 2024-06-19 08:29:36. Total running time: 9min 18s
    ╭───────────────────────────────╮
    │ Training result                │
    ├────────────────────────────────┤
    │ checkpoint_dir_name            │
    │ time_this_iter_s      25.7394  │
    │ time_total_s          351.233  │
    │ training_iteration         10  │
    │ accuracy               0.8656  │
    │ loss                  0.37827  │
    ╰───────────────────────────────╯
    
    # Several lines omitted
    -------------------------------
    Job 'pytorch-mnist' succeeded
    -------------------------------
    

Implantar um RayJob

O recurso RayJob personalizado gerencia o ciclo de vida de um recurso do RayCluster durante a execução de um único job do Ray.

  1. Analise o seguinte manifesto:

    apiVersion: ray.io/v1
    kind: RayJob
    metadata:
      name: pytorch-mnist-job
    spec:
      shutdownAfterJobFinishes: true
      entrypoint: python ai-ml/gke-ray/raytrain/pytorch-mnist/train.py
      runtimeEnvYAML: |
        pip:
          - torch
          - torchvision
        working_dir: "https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/archive/main.zip"
        env_vars:
          NUM_WORKERS: "4"
          CPUS_PER_WORKER: "2"
      rayClusterSpec:
        rayVersion: '2.9.0'
        headGroupSpec:
          rayStartParams: {}
          template:
            spec:
              containers:
                - name: ray-head
                  image: rayproject/ray:2.9.0
                  ports:
                    - containerPort: 6379
                      name: gcs-server
                    - containerPort: 8265
                      name: dashboard
                    - containerPort: 10001
                      name: client
                  resources:
                    limits:
                      cpu: "2"
                      memory: "4Gi"
                    requests:
                      cpu: "2"
                      memory: "4Gi"
        workerGroupSpecs:
          - replicas: 4
            minReplicas: 1
            maxReplicas: 5
            groupName: small-group
            rayStartParams: {}
            template:
              spec:
                containers:
                  - name: ray-worker
                    image: rayproject/ray:2.9.0
                    resources:
                      limits:
                        cpu: "2"
                        memory: "8Gi"
                      requests:
                        cpu: "2"
                        memory: "8Gi"

    Esse manifesto descreve um recurso personalizado do RayJob.

  2. Aplique o manifesto ao seu cluster do GKE:

    kubectl apply -f ray-job.yaml
    
  3. Verifique se o recurso RayJob está em execução:

    kubectl get rayjob
    

    O resultado será assim:

    NAME                JOB STATUS   DEPLOYMENT STATUS   START TIME             END TIME   AGE
    pytorch-mnist-job   RUNNING      Running             2024-06-19T15:43:32Z              2m29s
    

    Nesta saída, a coluna DEPLOYMENT STATUS indica que o recurso do RayJob é Running.

  4. Veja o status do recurso RayJob:

    kubectl logs -f -l job-name=pytorch-mnist-job
    

    O resultado será assim:

    Training started with configuration:
    ╭─────────────────────────────────────────────────╮
    │ Training config                                  │
    ├──────────────────────────────────────────────────┤
    │ train_loop_config/batch_size_per_worker       8  │
    │ train_loop_config/epochs                     10  │
    │ train_loop_config/lr                      0.001  │
    ╰─────────────────────────────────────────────────╯
    
    # Several lines omitted
    
    Training finished iteration 10 at 2024-06-19 08:29:36. Total running time: 9min 18s
    ╭───────────────────────────────╮
    │ Training result                │
    ├────────────────────────────────┤
    │ checkpoint_dir_name            │
    │ time_this_iter_s      25.7394  │
    │ time_total_s          351.233  │
    │ training_iteration         10  │
    │ accuracy               0.8656  │
    │ loss                  0.37827  │
    ╰───────────────────────────────╯
    
    # Several lines omitted
    -------------------------------
    Job 'pytorch-mnist' succeeded
    -------------------------------
    
  5. Verifique se o job do Ray foi concluído:

    kubectl get rayjob
    

    O resultado será assim:

    NAME                JOB STATUS   DEPLOYMENT STATUS   START TIME             END TIME               AGE
    pytorch-mnist-job   SUCCEEDED    Complete            2024-06-19T15:43:32Z   2024-06-19T15:51:12Z   9m6s
    

    Nesta saída, a coluna DEPLOYMENT STATUS indica que o recurso do RayJob é Complete.

Limpar

Excluir o projeto

    Delete a Google Cloud project:

    gcloud projects delete PROJECT_ID

Excluir recursos individuais

Se você usou um projeto existente e não quer excluí-lo, exclua os recursos individuais. Para excluir o cluster, digite:

gcloud container clusters delete ${CLUSTER_NAME}

A seguir