Skip to main content
NetApp Solutions
O português é fornecido por meio de tradução automática para sua conveniência. O inglês precede o português em caso de inconsistências.

Procedimento de teste

Colaboradores

Esta seção descreve as tarefas necessárias para concluir a validação.

Pré-requisitos

Para executar as tarefas descritas nesta seção, você deve ter acesso a um host Linux ou macOS com as seguintes ferramentas instaladas e configuradas:

  • Kubectl (configurado para acesso a um cluster Kubernetes existente)

    • As instruções de instalação e configuração podem ser "aqui"encontradas .

  • Toolkit DataOps do NetApp para Kubernetes

    • As instruções de instalação podem ser "aqui"encontradas .

Cenário 1 – inferência sob demanda no JupyterLab

  1. Crie um namespace do Kubernetes para workloads de inferência de AI/ML.

    $ kubectl create namespace inference
    namespace/inference created
  2. Use o Toolkit DataOps do NetApp para provisionar um volume persistente para armazenar os dados nos quais você fará a inferência.

    $ netapp_dataops_k8s_cli.py create volume --namespace=inference --pvc-name=inference-data --size=50Gi
    Creating PersistentVolumeClaim (PVC) 'inference-data' in namespace 'inference'.
    PersistentVolumeClaim (PVC) 'inference-data' created. Waiting for Kubernetes to bind volume to PVC.
    Volume successfully created and bound to PersistentVolumeClaim (PVC) 'inference-data' in namespace 'inference'.
  3. Use o Toolkit DataOps do NetApp para criar um novo workspace do JupyterLab. Monte o volume persistente que foi criado na etapa anterior usando a --mount- pvc opção. Aloque GPUs NVIDIA para o espaço de trabalho conforme necessário usando a -- nvidia-gpu opção.

    No exemplo a seguir, o volume persistente inference-data é montado no contentor do workspace JupyterLab em /home/jovyan/data. Ao usar imagens de contentor oficiais do Project Jupyter, /home/jovyan é apresentado como o diretório de nível superior dentro da interface web do JupyterLab.

    $ netapp_dataops_k8s_cli.py create jupyterlab --namespace=inference --workspace-name=live-inference --size=50Gi --nvidia-gpu=2 --mount-pvc=inference-data:/home/jovyan/data
    Set workspace password (this password will be required in order to access the workspace):
    Re-enter password:
    Creating persistent volume for workspace...
    Creating PersistentVolumeClaim (PVC) 'ntap-dsutil-jupyterlab-live-inference' in namespace 'inference'.
    PersistentVolumeClaim (PVC) 'ntap-dsutil-jupyterlab-live-inference' created. Waiting for Kubernetes to bind volume to PVC.
    Volume successfully created and bound to PersistentVolumeClaim (PVC) 'ntap-dsutil-jupyterlab-live-inference' in namespace 'inference'.
    Creating Service 'ntap-dsutil-jupyterlab-live-inference' in namespace 'inference'.
    Service successfully created.
    Attaching Additional PVC: 'inference-data' at mount_path: '/home/jovyan/data'.
    Creating Deployment 'ntap-dsutil-jupyterlab-live-inference' in namespace 'inference'.
    Deployment 'ntap-dsutil-jupyterlab-live-inference' created.
    Waiting for Deployment 'ntap-dsutil-jupyterlab-live-inference' to reach Ready state.
    Deployment successfully created.
    Workspace successfully created.
    To access workspace, navigate to http://192.168.0.152:32721
  4. Acesse a área de trabalho do JupyterLab usando o URL especificado na saída create jupyterlab do comando. O diretório de dados representa o volume persistente que foi montado na área de trabalho.

    Figura que mostra a caixa de diálogo de entrada/saída ou que representa o conteúdo escrito

  5. Abra o data diretório e carregue os arquivos nos quais a inferência deve ser executada. Quando os arquivos são carregados para o diretório de dados, eles são armazenados automaticamente no volume persistente que foi montado na área de trabalho. Para fazer upload de arquivos, clique no ícone carregar arquivos, como mostrado na imagem a seguir.

    Figura que mostra a caixa de diálogo de entrada/saída ou que representa o conteúdo escrito

  6. Retorne ao diretório de nível superior e crie um novo bloco de anotações.

    Figura que mostra a caixa de diálogo de entrada/saída ou que representa o conteúdo escrito

  7. Adicione o código de inferência ao bloco de anotações. O exemplo a seguir mostra o código de inferência para um caso de uso de deteção de imagem.

    Figura que mostra a caixa de diálogo de entrada/saída ou que representa o conteúdo escrito

    Figura que mostra a caixa de diálogo de entrada/saída ou que representa o conteúdo escrito

  8. Adicione ofuscação Protopia ao seu código de inferência. O Protopia trabalha diretamente com os clientes para fornecer documentação específica para casos de uso e está fora do escopo deste relatório técnico. O exemplo a seguir mostra o código de inferência para um caso de uso de deteção de imagem com ofuscação Protopia adicionada.

    Figura que mostra a caixa de diálogo de entrada/saída ou que representa o conteúdo escrito

    Figura que mostra a caixa de diálogo de entrada/saída ou que representa o conteúdo escrito

Cenário 2 – inferência em lote no Kubernetes

  1. Crie um namespace do Kubernetes para workloads de inferência de AI/ML.

    $ kubectl create namespace inference
    namespace/inference created
  2. Use o Toolkit DataOps do NetApp para provisionar um volume persistente para armazenar os dados nos quais você fará a inferência.

    $ netapp_dataops_k8s_cli.py create volume --namespace=inference --pvc-name=inference-data --size=50Gi
    Creating PersistentVolumeClaim (PVC) 'inference-data' in namespace 'inference'.
    PersistentVolumeClaim (PVC) 'inference-data' created. Waiting for Kubernetes to bind volume to PVC.
    Volume successfully created and bound to PersistentVolumeClaim (PVC) 'inference-data' in namespace 'inference'.
  3. Preencha o novo volume persistente com os dados nos quais você realizará a inferência.

    Existem vários métodos para carregar dados em um PVC. Se seus dados estiverem armazenados atualmente em uma plataforma de storage de objetos compatível com S3, como o NetApp StorageGRID ou o Amazon S3, você poderá usar "Funcionalidades de transferência de dados do Toolkit S3 do NetApp DataOps"o . Outro método simples é criar uma área de trabalho do JupyterLab e, em seguida, fazer upload de arquivos através da interface web do JupyterLab, conforme descrito nas etapas 3 a 5 na seção "Cenário 1 – inferência sob demanda no JupyterLab."

  4. Crie um trabalho do Kubernetes para sua tarefa de inferência em lote. O exemplo a seguir mostra um trabalho de inferência em lote para um caso de uso de deteção de imagem. Esta tarefa executa a inferência em cada imagem em um conjunto de imagens e grava as métricas de precisão da inferência em stdout.

    $ vi inference-job-raw.yaml
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: netapp-inference-raw
      namespace: inference
    spec:
      backoffLimit: 5
      template:
        spec:
          volumes:
          - name: data
            persistentVolumeClaim:
              claimName: inference-data
          - name: dshm
            emptyDir:
              medium: Memory
          containers:
          - name: inference
            image: netapp-protopia-inference:latest
            imagePullPolicy: IfNotPresent
            command: ["python3", "run-accuracy-measurement.py", "--dataset", "/data/netapp-face-detection/FDDB"]
            resources:
              limits:
                nvidia.com/gpu: 2
            volumeMounts:
            - mountPath: /data
              name: data
            - mountPath: /dev/shm
              name: dshm
          restartPolicy: Never
    $ kubectl create -f inference-job-raw.yaml
    job.batch/netapp-inference-raw created
  5. Confirme se a tarefa de inferência foi concluída com êxito.

    $ kubectl -n inference logs netapp-inference-raw-255sp
    100%|██████████| 89/89 [00:52<00:00,  1.68it/s]
    Reading Predictions : 100%|██████████| 10/10 [00:01<00:00,  6.23it/s]
    Predicting ... : 100%|██████████| 10/10 [00:16<00:00,  1.64s/it]
    ==================== Results ====================
    FDDB-fold-1 Val AP: 0.9491256561145955
    FDDB-fold-2 Val AP: 0.9205024466101926
    FDDB-fold-3 Val AP: 0.9253013871078468
    FDDB-fold-4 Val AP: 0.9399781485863011
    FDDB-fold-5 Val AP: 0.9504280149478732
    FDDB-fold-6 Val AP: 0.9416473519339292
    FDDB-fold-7 Val AP: 0.9241631566241117
    FDDB-fold-8 Val AP: 0.9072663297546659
    FDDB-fold-9 Val AP: 0.9339648715035469
    FDDB-fold-10 Val AP: 0.9447707905560152
    FDDB Dataset Average AP: 0.9337148153739079
    =================================================
    mAP: 0.9337148153739079
  6. Adicione ofuscação Protopia ao seu trabalho de inferência. Você pode encontrar instruções de uso específicas de caso para adicionar ofuscação Protopia diretamente do Protopia, que está fora do escopo deste relatório técnico. O exemplo a seguir mostra uma tarefa de inferência em lote para um caso de uso de deteção de rosto com ofuscação Protopia adicionada usando um valor ALFA de 0,8. Esta tarefa aplica a ofuscação do Protopia antes de executar a inferência para cada imagem em um conjunto de imagens e, em seguida, grava métricas de precisão da inferência em stdout.

    Repetimos esta etapa para os valores ALFA 0,05, 0,1, 0,2, 0,4, 0,6, 0,8, 0,9 e 0,95. Você pode ver os resultados em ""Comparação da precisão da inferência.""

    $ vi inference-job-protopia-0.8.yaml
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: netapp-inference-protopia-0.8
      namespace: inference
    spec:
      backoffLimit: 5
      template:
        spec:
          volumes:
          - name: data
            persistentVolumeClaim:
              claimName: inference-data
          - name: dshm
            emptyDir:
              medium: Memory
          containers:
          - name: inference
            image: netapp-protopia-inference:latest
            imagePullPolicy: IfNotPresent
            env:
            - name: ALPHA
              value: "0.8"
            command: ["python3", "run-accuracy-measurement.py", "--dataset", "/data/netapp-face-detection/FDDB", "--alpha", "$(ALPHA)", "--noisy"]
            resources:
              limits:
                nvidia.com/gpu: 2
            volumeMounts:
            - mountPath: /data
              name: data
            - mountPath: /dev/shm
              name: dshm
          restartPolicy: Never
    $ kubectl create -f inference-job-protopia-0.8.yaml
    job.batch/netapp-inference-protopia-0.8 created
  7. Confirme se a tarefa de inferência foi concluída com êxito.

    $ kubectl -n inference logs netapp-inference-protopia-0.8-b4dkz
    100%|██████████| 89/89 [01:05<00:00,  1.37it/s]
    Reading Predictions : 100%|██████████| 10/10 [00:02<00:00,  3.67it/s]
    Predicting ... : 100%|██████████| 10/10 [00:22<00:00,  2.24s/it]
    ==================== Results ====================
    FDDB-fold-1 Val AP: 0.8953066115834589
    FDDB-fold-2 Val AP: 0.8819580264029936
    FDDB-fold-3 Val AP: 0.8781107458462862
    FDDB-fold-4 Val AP: 0.9085731346308461
    FDDB-fold-5 Val AP: 0.9166445508275378
    FDDB-fold-6 Val AP: 0.9101178994188819
    FDDB-fold-7 Val AP: 0.8383443678423771
    FDDB-fold-8 Val AP: 0.8476311547659464
    FDDB-fold-9 Val AP: 0.8739624502111121
    FDDB-fold-10 Val AP: 0.8905468076424851
    FDDB Dataset Average AP: 0.8841195749171925
    =================================================
    mAP: 0.8841195749171925

Cenário 3 – servidor de inferência NVIDIA Triton

  1. Crie um namespace do Kubernetes para workloads de inferência de AI/ML.

    $ kubectl create namespace inference
    namespace/inference created
  2. Use o kit de ferramentas de DataOps do NetApp para provisionar um volume persistente a ser usado como um repositório de modelo para o servidor de inferência NVIDIA Triton.

    $ netapp_dataops_k8s_cli.py create volume --namespace=inference --pvc-name=triton-model-repo --size=100Gi
    Creating PersistentVolumeClaim (PVC) 'triton-model-repo' in namespace 'inference'.
    PersistentVolumeClaim (PVC) 'triton-model-repo' created. Waiting for Kubernetes to bind volume to PVC.
    Volume successfully created and bound to PersistentVolumeClaim (PVC) 'triton-model-repo' in namespace 'inference'.
  3. Armazene seu modelo no novo volume persistente em um "formato" que seja reconhecido pelo servidor de inferência NVIDIA Triton.

    Existem vários métodos para carregar dados em um PVC. Um método simples é criar uma área de trabalho do JupyterLab e, em seguida, fazer upload de arquivos através da interface web do JupyterLab, conforme descrito nas etapas 3 a 5 em "Cenário 1 – inferência sob demanda no JupyterLab. "

  4. Use o Toolkit DataOps do NetApp para implantar uma nova instância do servidor de inferência do NVIDIA Triton.

    $ netapp_dataops_k8s_cli.py create triton-server --namespace=inference --server-name=netapp-inference --model-repo-pvc-name=triton-model-repo
    Creating Service 'ntap-dsutil-triton-netapp-inference' in namespace 'inference'.
    Service successfully created.
    Creating Deployment 'ntap-dsutil-triton-netapp-inference' in namespace 'inference'.
    Deployment 'ntap-dsutil-triton-netapp-inference' created.
    Waiting for Deployment 'ntap-dsutil-triton-netapp-inference' to reach Ready state.
    Deployment successfully created.
    Server successfully created.
    Server endpoints:
    http: 192.168.0.152: 31208
    grpc: 192.168.0.152: 32736
    metrics: 192.168.0.152: 30009/metrics
  5. Use um SDK do cliente Triton para executar uma tarefa de inferência. O trecho de código Python a seguir usa o SDK do cliente Triton Python para executar uma tarefa de inferência para um caso de uso de deteção de rosto. Este exemplo chama a API Triton e passa em uma imagem para inferência. O Triton Inference Server então recebe a solicitação, invoca o modelo e retorna a saída de inferência como parte dos resultados da API.

    # get current frame
    frame = input_image
    # preprocess input
    preprocessed_input = preprocess_input(frame)
    preprocessed_input = torch.Tensor(preprocessed_input).to(device)
    # run forward pass
    clean_activation = clean_model_head(preprocessed_input)  # runs the first few layers
    ######################################################################################
    #          pass clean image to Triton Inference Server API for inferencing           #
    ######################################################################################
    triton_client = httpclient.InferenceServerClient(url="192.168.0.152:31208", verbose=False)
    model_name = "face_detection_base"
    inputs = []
    outputs = []
    inputs.append(httpclient.InferInput("INPUT__0", [1, 128, 32, 32], "FP32"))
    inputs[0].set_data_from_numpy(clean_activation.detach().cpu().numpy(), binary_data=False)
    outputs.append(httpclient.InferRequestedOutput("OUTPUT__0", binary_data=False))
    outputs.append(httpclient.InferRequestedOutput("OUTPUT__1", binary_data=False))
    results = triton_client.infer(
        model_name,
        inputs,
        outputs=outputs,
        #query_params=query_params,
        headers=None,
        request_compression_algorithm=None,
        response_compression_algorithm=None)
    #print(results.get_response())
    statistics = triton_client.get_inference_statistics(model_name=model_name, headers=None)
    print(statistics)
    if len(statistics["model_stats"]) != 1:
        print("FAILED: Inference Statistics")
        sys.exit(1)
    
    loc_numpy = results.as_numpy("OUTPUT__0")
    pred_numpy = results.as_numpy("OUTPUT__1")
    ######################################################################################
    # postprocess output
    clean_pred = (loc_numpy, pred_numpy)
    clean_outputs = postprocess_outputs(
        clean_pred, [[input_image_width, input_image_height]], priors, THRESHOLD
    )
    # draw rectangles
    clean_frame = copy.deepcopy(frame)  # needs to be deep copy
    for (x1, y1, x2, y2, s) in clean_outputs[0]:
        x1, y1 = int(x1), int(y1)
        x2, y2 = int(x2), int(y2)
        cv2.rectangle(clean_frame, (x1, y1), (x2, y2), (0, 0, 255), 4)
  6. Adicione ofuscação Protopia ao seu código de inferência. Você pode encontrar instruções específicas para uso para adicionar ofuscação Protopia diretamente do Protopia; no entanto, esse processo está fora do escopo deste relatório técnico. O exemplo a seguir mostra o mesmo código Python que é mostrado na etapa anterior 5, mas com ofuscação Protopia adicionada.

    Note que a ofuscação Protopia é aplicada à imagem antes de ser passada para a API Triton. Assim, a imagem não ofuscada nunca sai da máquina local. Apenas a imagem ofuscada é transmitida pela rede. Esse fluxo de trabalho é aplicável aos casos de uso em que os dados são coletados em uma zona confiável, mas precisam ser passados fora dessa zona confiável para inferência. Sem ofuscação do Protopia, não é possível implementar esse tipo de fluxo de trabalho sem que dados confidenciais saiam da zona confiável.

    # get current frame
    frame = input_image
    # preprocess input
    preprocessed_input = preprocess_input(frame)
    preprocessed_input = torch.Tensor(preprocessed_input).to(device)
    # run forward pass
    not_noisy_activation = noisy_model_head(preprocessed_input)  # runs the first few layers
    ##################################################################
    #          obfuscate image locally prior to inferencing          #
    #          SINGLE ADITIONAL LINE FOR PRIVATE INFERENCE           #
    ##################################################################
    noisy_activation = noisy_model_noise(not_noisy_activation)
    ##################################################################
    ###########################################################################################
    #          pass obfuscated image to Triton Inference Server API for inferencing           #
    ###########################################################################################
    triton_client = httpclient.InferenceServerClient(url="192.168.0.152:31208", verbose=False)
    model_name = "face_detection_noisy"
    inputs = []
    outputs = []
    inputs.append(httpclient.InferInput("INPUT__0", [1, 128, 32, 32], "FP32"))
    inputs[0].set_data_from_numpy(noisy_activation.detach().cpu().numpy(), binary_data=False)
    outputs.append(httpclient.InferRequestedOutput("OUTPUT__0", binary_data=False))
    outputs.append(httpclient.InferRequestedOutput("OUTPUT__1", binary_data=False))
    results = triton_client.infer(
        model_name,
        inputs,
        outputs=outputs,
        #query_params=query_params,
        headers=None,
        request_compression_algorithm=None,
        response_compression_algorithm=None)
    #print(results.get_response())
    statistics = triton_client.get_inference_statistics(model_name=model_name, headers=None)
    print(statistics)
    if len(statistics["model_stats"]) != 1:
        print("FAILED: Inference Statistics")
        sys.exit(1)
    
    loc_numpy = results.as_numpy("OUTPUT__0")
    pred_numpy = results.as_numpy("OUTPUT__1")
    ###########################################################################################
    
    # postprocess output
    noisy_pred = (loc_numpy, pred_numpy)
    noisy_outputs = postprocess_outputs(
        noisy_pred, [[input_image_width, input_image_height]], priors, THRESHOLD * 0.5
    )
    # get reconstruction of the noisy activation
    noisy_reconstruction = decoder_function(noisy_activation)
    noisy_reconstruction = noisy_reconstruction.detach().cpu().numpy()[0]
    noisy_reconstruction = unpreprocess_output(
        noisy_reconstruction, (input_image_width, input_image_height), True
    ).astype(np.uint8)
    # draw rectangles
    for (x1, y1, x2, y2, s) in noisy_outputs[0]:
        x1, y1 = int(x1), int(y1)
        x2, y2 = int(x2), int(y2)
        cv2.rectangle(noisy_reconstruction, (x1, y1), (x2, y2), (0, 0, 255), 4)