Skip to main content
NetApp artificial intelligence solutions
La versione in lingua italiana fornita proviene da una traduzione automatica. Per eventuali incoerenze, fare riferimento alla versione in lingua inglese.

Procedura di prova

In questa sezione vengono descritte le attività necessarie per completare la convalida.

Prerequisiti

Per eseguire le attività descritte in questa sezione, è necessario avere accesso a un host Linux o macOS con i seguenti strumenti installati e configurati:

  • Kubectl (configurato per l'accesso a un cluster Kubernetes esistente)

    • Le istruzioni per l'installazione e la configurazione possono essere trovate "Qui" .

  • Kit degli strumenti NetApp DataOps per Kubernetes

    • Le istruzioni di installazione possono essere trovate "Qui" .

Scenario 1 – Inferenza su richiesta in JupyterLab

  1. Creare uno spazio dei nomi Kubernetes per carichi di lavoro di inferenza AI/ML.

    $ kubectl create namespace inference
    namespace/inference created
  2. Utilizzare NetApp DataOps Toolkit per predisporre un volume persistente in cui archiviare i dati su cui eseguire l'inferenza.

    $ 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. Utilizzare NetApp DataOps Toolkit per creare un nuovo spazio di lavoro JupyterLab. Montare il volume persistente creato nel passaggio precedente utilizzando --mount- pvc opzione. Assegnare le GPU NVIDIA all'area di lavoro secondo necessità utilizzando -- nvidia-gpu opzione.

    Nell'esempio seguente, il volume persistente inference-data è montato sul contenitore dell'area di lavoro JupyterLab in /home/jovyan/data . Quando si utilizzano immagini ufficiali del contenitore Project Jupyter, /home/jovyan viene presentata come directory di primo livello all'interno dell'interfaccia web di 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. Accedi all'area di lavoro JupyterLab utilizzando l'URL specificato nell'output del create jupyterlab comando. La directory dati rappresenta il volume persistente montato nell'area di lavoro.

    Figura che mostra il dialogo di input/output o che rappresenta il contenuto scritto

  5. Apri il data directory e caricare i file su cui eseguire l'inferenza. Quando i file vengono caricati nella directory dati, vengono automaticamente archiviati sul volume persistente montato nell'area di lavoro. Per caricare i file, fare clic sull'icona Carica file, come mostrato nell'immagine seguente.

    Figura che mostra il dialogo di input/output o che rappresenta il contenuto scritto

  6. Torna alla directory di livello superiore e crea un nuovo notebook.

    Figura che mostra il dialogo di input/output o che rappresenta il contenuto scritto

  7. Aggiungere il codice di inferenza al notebook. L'esempio seguente mostra il codice di inferenza per un caso d'uso di rilevamento delle immagini.

    Figura che mostra il dialogo di input/output o che rappresenta il contenuto scritto

    Figura che mostra il dialogo di input/output o che rappresenta il contenuto scritto

  8. Aggiungi l'offuscamento Protopia al tuo codice di inferenza. Protopia collabora direttamente con i clienti per fornire documentazione specifica per i casi d'uso e non rientra nell'ambito di questo rapporto tecnico. L'esempio seguente mostra il codice di inferenza per un caso d'uso di rilevamento delle immagini con aggiunta dell'offuscamento Protopia.

    Figura che mostra il dialogo di input/output o che rappresenta il contenuto scritto

    Figura che mostra il dialogo di input/output o che rappresenta il contenuto scritto

Scenario 2 – Inferenza batch su Kubernetes

  1. Creare uno spazio dei nomi Kubernetes per carichi di lavoro di inferenza AI/ML.

    $ kubectl create namespace inference
    namespace/inference created
  2. Utilizzare NetApp DataOps Toolkit per predisporre un volume persistente in cui archiviare i dati su cui eseguire l'inferenza.

    $ 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. Popolare il nuovo volume persistente con i dati su cui si eseguirà l'inferenza.

    Esistono diversi metodi per caricare dati su un PVC. Se i tuoi dati sono attualmente archiviati in una piattaforma di archiviazione di oggetti compatibile con S3, come NetApp StorageGRID o Amazon S3, puoi utilizzare "Funzionalità di NetApp DataOps Toolkit S3 Data Mover" . Un altro metodo semplice è quello di creare uno spazio di lavoro JupyterLab e quindi caricare i file tramite l'interfaccia web di JupyterLab, come descritto nei passaggi da 3 a 5 nella sezione "Scenario 1 – Inferenza su richiesta in JupyterLab ."

  4. Crea un processo Kubernetes per la tua attività di inferenza batch. L'esempio seguente mostra un processo di inferenza batch per un caso d'uso di rilevamento delle immagini. Questo lavoro esegue l'inferenza su ciascuna immagine in un set di immagini e scrive le metriche di accuratezza dell'inferenza su 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. Conferma che il processo di inferenza è stato completato correttamente.

    $ 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. Aggiungi l'offuscamento di Protopia al tuo lavoro di inferenza. È possibile trovare istruzioni specifiche per i casi d'uso per aggiungere l'offuscamento di Protopia direttamente da Protopia, il che esula dall'ambito di questo rapporto tecnico. L'esempio seguente mostra un processo di inferenza batch per un caso d'uso di rilevamento facciale con offuscamento Protopia aggiunto utilizzando un valore ALPHA di 0,8. Questo lavoro applica l'offuscamento di Protopia prima di eseguire l'inferenza per ogni immagine in un set di immagini e quindi scrive le metriche di accuratezza dell'inferenza su stdout.

    Abbiamo ripetuto questo passaggio per i valori ALPHA 0,05, 0,1, 0,2, 0,4, 0,6, 0,8, 0,9 e 0,95. Puoi vedere i risultati in"Confronto dell'accuratezza dell'inferenza."

    $ 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. Conferma che il processo di inferenza è stato completato correttamente.

    $ 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

Scenario 3 – Server di inferenza NVIDIA Triton

  1. Creare uno spazio dei nomi Kubernetes per carichi di lavoro di inferenza AI/ML.

    $ kubectl create namespace inference
    namespace/inference created
  2. Utilizzare NetApp DataOps Toolkit per predisporre un volume persistente da utilizzare come repository di modelli per NVIDIA Triton Inference Server.

    $ 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. Memorizza il tuo modello sul nuovo volume persistente in un "formato" riconosciuto dal server di inferenza NVIDIA Triton.

    Esistono diversi metodi per caricare dati su un PVC. Un metodo semplice è quello di creare uno spazio di lavoro JupyterLab e quindi caricare i file tramite l'interfaccia web di JupyterLab, come descritto nei passaggi da 3 a 5 in "Scenario 1 – Inferenza su richiesta in JupyterLab . "

  4. Utilizzare NetApp DataOps Toolkit per distribuire una nuova istanza di NVIDIA Triton Inference Server.

    $ 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. Utilizzare un SDK client Triton per eseguire un'attività di inferenza. Il seguente estratto di codice Python utilizza l'SDK client Python Triton per eseguire un'attività di inferenza per un caso d'uso di rilevamento dei volti. Questo esempio richiama l'API Triton e passa un'immagine per l'inferenza. Il server di inferenza Triton riceve quindi la richiesta, richiama il modello e restituisce l'output di inferenza come parte dei risultati dell'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. Aggiungi l'offuscamento Protopia al tuo codice di inferenza. È possibile trovare istruzioni specifiche per i casi d'uso per aggiungere l'offuscamento di Protopia direttamente da Protopia; tuttavia, questo processo esula dall'ambito di questo rapporto tecnico. L'esempio seguente mostra lo stesso codice Python mostrato nel passaggio 5 precedente, ma con l'aggiunta dell'offuscamento di Protopia.

    Si noti che l'offuscamento Protopia viene applicato all'immagine prima che venga passata all'API Triton. In questo modo, l'immagine non offuscata non lascia mai la macchina locale. Solo l'immagine offuscata viene trasmessa attraverso la rete. Questo flusso di lavoro è applicabile ai casi d'uso in cui i dati vengono raccolti all'interno di una zona attendibile ma devono poi essere trasmessi al di fuori di tale zona attendibile per l'inferenza. Senza l'offuscamento di Protopia, non è possibile implementare questo tipo di flusso di lavoro senza che i dati sensibili escano dalla zona attendibile.

    # 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)