Skip to main content
NetApp artificial intelligence solutions
Die deutsche Sprachversion wurde als Serviceleistung für Sie durch maschinelle Übersetzung erstellt. Bei eventuellen Unstimmigkeiten hat die englische Sprachversion Vorrang.

Testverfahren

In diesem Abschnitt werden die Aufgaben beschrieben, die zum Abschließen der Validierung erforderlich sind.

Voraussetzungen

Um die in diesem Abschnitt beschriebenen Aufgaben auszuführen, müssen Sie Zugriff auf einen Linux- oder macOS-Host haben, auf dem die folgenden Tools installiert und konfiguriert sind:

  • Kubectl (konfiguriert für den Zugriff auf einen vorhandenen Kubernetes-Cluster)

    • Installations- und Konfigurationsanweisungen finden Sie "hier," .

  • NetApp DataOps Toolkit für Kubernetes

    • Installationsanweisungen finden Sie "hier," .

Szenario 1 – On-Demand-Inferenz in JupyterLab

  1. Erstellen Sie einen Kubernetes-Namespace für AI/ML-Inferenz-Workloads.

    $ kubectl create namespace inference
    namespace/inference created
  2. Verwenden Sie das NetApp DataOps Toolkit, um ein persistentes Volume zum Speichern der Daten bereitzustellen, auf denen Sie die Inferenz durchführen.

    $ 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. Verwenden Sie das NetApp DataOps Toolkit, um einen neuen JupyterLab-Arbeitsbereich zu erstellen. Mounten Sie das im vorherigen Schritt erstellte persistente Volume mithilfe des --mount- pvc Option. Weisen Sie dem Arbeitsbereich nach Bedarf NVIDIA -GPUs zu, indem Sie die -- nvidia-gpu Option.

    Im folgenden Beispiel wird das persistente Volume inference-data wird im JupyterLab-Arbeitsbereichscontainer unter /home/jovyan/data . Bei Verwendung offizieller Project Jupyter-Container-Images /home/jovyan wird als oberstes Verzeichnis innerhalb der JupyterLab-Weboberfläche angezeigt.

    $ 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. Greifen Sie auf den JupyterLab-Arbeitsbereich zu, indem Sie die URL verwenden, die in der Ausgabe des create jupyterlab Befehl. Das Datenverzeichnis stellt das persistente Volume dar, das im Arbeitsbereich bereitgestellt wurde.

    Abbildung, die einen Eingabe-/Ausgabedialog zeigt oder schriftlichen Inhalt darstellt

  5. Öffnen Sie die data Verzeichnis und laden Sie die Dateien hoch, für die die Inferenz durchgeführt werden soll. Wenn Dateien in das Datenverzeichnis hochgeladen werden, werden sie automatisch auf dem persistenten Volume gespeichert, das im Arbeitsbereich bereitgestellt wurde. Um Dateien hochzuladen, klicken Sie auf das Symbol „Dateien hochladen“, wie im folgenden Bild gezeigt.

    Abbildung, die einen Eingabe-/Ausgabedialog zeigt oder schriftlichen Inhalt darstellt

  6. Kehren Sie zum obersten Verzeichnis zurück und erstellen Sie ein neues Notizbuch.

    Abbildung, die einen Eingabe-/Ausgabedialog zeigt oder schriftlichen Inhalt darstellt

  7. Fügen Sie dem Notebook Inferenzcode hinzu. Das folgende Beispiel zeigt Inferenzcode für einen Anwendungsfall zur Bilderkennung.

    Abbildung, die einen Eingabe-/Ausgabedialog zeigt oder schriftlichen Inhalt darstellt

    Abbildung, die einen Eingabe-/Ausgabedialog zeigt oder schriftlichen Inhalt darstellt

  8. Fügen Sie Ihrem Inferenzcode die Protopia-Verschleierung hinzu. Protopia arbeitet direkt mit Kunden zusammen, um anwendungsfallspezifische Dokumentation bereitzustellen, und liegt außerhalb des Geltungsbereichs dieses technischen Berichts. Das folgende Beispiel zeigt Inferenzcode für einen Anwendungsfall zur Bilderkennung mit hinzugefügter Protopia-Verschleierung.

    Abbildung, die einen Eingabe-/Ausgabedialog zeigt oder schriftlichen Inhalt darstellt

    Abbildung, die einen Eingabe-/Ausgabedialog zeigt oder schriftlichen Inhalt darstellt

Szenario 2 – Batch-Inferenz auf Kubernetes

  1. Erstellen Sie einen Kubernetes-Namespace für AI/ML-Inferenz-Workloads.

    $ kubectl create namespace inference
    namespace/inference created
  2. Verwenden Sie das NetApp DataOps Toolkit, um ein persistentes Volume zum Speichern der Daten bereitzustellen, auf denen Sie die Inferenz durchführen.

    $ 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. Füllen Sie das neue persistente Volume mit den Daten, für die Sie die Inferenz durchführen möchten.

    Es gibt mehrere Methoden zum Laden von Daten auf ein PVC. Wenn Ihre Daten derzeit in einer S3-kompatiblen Objektspeicherplattform wie NetApp StorageGRID oder Amazon S3 gespeichert sind, können Sie "NetApp DataOps Toolkit S3 Data Mover-Funktionen" . Eine weitere einfache Methode besteht darin, einen JupyterLab-Arbeitsbereich zu erstellen und dann Dateien über die JupyterLab-Weboberfläche hochzuladen, wie in den Schritten 3 bis 5 im Abschnitt „Szenario 1 – On-Demand-Inferenz in JupyterLab ."

  4. Erstellen Sie einen Kubernetes-Job für Ihre Batch-Inferenzaufgabe. Das folgende Beispiel zeigt einen Batch-Inferenzjob für einen Anwendungsfall zur Bilderkennung. Dieser Job führt für jedes Bild in einem Satz von Bildern eine Inferenz durch und schreibt Metriken zur Inferenzgenauigkeit in die Standardausgabe.

    $ 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. Bestätigen Sie, dass der Inferenzauftrag erfolgreich abgeschlossen wurde.

    $ 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. Fügen Sie Ihrem Inferenzjob die Protopia-Verschleierung hinzu. Anwendungsfallspezifische Anweisungen zum Hinzufügen der Protopia-Verschleierung finden Sie direkt bei Protopia, was jedoch nicht in den Rahmen dieses technischen Berichts fällt. Das folgende Beispiel zeigt einen Batch-Inferenzjob für einen Anwendungsfall zur Gesichtserkennung mit hinzugefügter Protopia-Verschleierung unter Verwendung eines ALPHA-Werts von 0,8. Dieser Job wendet die Protopia-Verschleierung an, bevor er für jedes Bild in einem Satz von Bildern eine Inferenz durchführt, und schreibt dann die Inferenzgenauigkeitsmetriken in die Standardausgabe.

    Wir haben diesen Schritt für die ALPHA-Werte 0,05, 0,1, 0,2, 0,4, 0,6, 0,8, 0,9 und 0,95 wiederholt. Die Ergebnisse können Sie in"Vergleich der Inferenzgenauigkeit."

    $ 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. Bestätigen Sie, dass der Inferenzauftrag erfolgreich abgeschlossen wurde.

    $ 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

Szenario 3 – NVIDIA Triton Inference Server

  1. Erstellen Sie einen Kubernetes-Namespace für AI/ML-Inferenz-Workloads.

    $ kubectl create namespace inference
    namespace/inference created
  2. Verwenden Sie das NetApp DataOps Toolkit, um ein persistentes Volume bereitzustellen, das als Modell-Repository für den NVIDIA Triton Inference Server verwendet werden kann.

    $ 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. Speichern Sie Ihr Modell auf dem neuen persistenten Datenträger in einem "Format" das vom NVIDIA Triton Inference Server erkannt wird.

    Es gibt mehrere Methoden zum Laden von Daten auf ein PVC. Eine einfache Methode besteht darin, einen JupyterLab-Arbeitsbereich zu erstellen und dann Dateien über die JupyterLab-Weboberfläche hochzuladen, wie in den Schritten 3 bis 5 in "Szenario 1 – On-Demand-Inferenz in JupyterLab . "

  4. Verwenden Sie das NetApp DataOps Toolkit, um eine neue NVIDIA Triton Inference Server-Instanz bereitzustellen.

    $ 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. Verwenden Sie ein Triton-Client-SDK, um eine Inferenzaufgabe auszuführen. Der folgende Python-Codeauszug verwendet das Triton Python-Client-SDK, um eine Inferenzaufgabe für einen Anwendungsfall zur Gesichtserkennung auszuführen. Dieses Beispiel ruft die Triton-API auf und übergibt ein Bild zur Inferenz. Der Triton Inference Server empfängt dann die Anfrage, ruft das Modell auf und gibt die Inferenzausgabe als Teil der API-Ergebnisse zurück.

    # 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. Fügen Sie Ihrem Inferenzcode die Protopia-Verschleierung hinzu. Anwendungsfallspezifische Anweisungen zum Hinzufügen der Protopia-Verschleierung finden Sie direkt von Protopia aus. Dieser Vorgang liegt jedoch außerhalb des Rahmens dieses technischen Berichts. Das folgende Beispiel zeigt denselben Python-Code wie im vorherigen Schritt 5, jedoch mit hinzugefügter Protopia-Verschleierung.

    Beachten Sie, dass die Protopia-Verschleierung auf das Bild angewendet wird, bevor es an die Triton-API übergeben wird. Somit verlässt das nicht verschleierte Bild nie die lokale Maschine. Nur das verschleierte Bild wird über das Netzwerk übertragen. Dieser Workflow ist auf Anwendungsfälle anwendbar, in denen Daten innerhalb einer vertrauenswürdigen Zone erfasst werden, dann aber zur Inferenz außerhalb dieser vertrauenswürdigen Zone weitergeleitet werden müssen. Ohne die Verschleierung durch Protopia ist es nicht möglich, diese Art von Workflow zu implementieren, ohne dass vertrauliche Daten jemals die vertrauenswürdige Zone verlassen.

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