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

Testverfahren

Beitragende

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 auf einen Linux- oder macOS-Host zugreifen können, auf dem die folgenden Tools installiert und konfiguriert sind:

  • Kubectl (für Zugriff auf ein vorhandenes Kubernetes-Cluster konfiguriert)

    • Anweisungen zur Installation und Konfiguration finden Sie "Hier".

  • NetApp DataOps Toolkit für Kubernetes

    • Installationsanweisungen finden Sie "Hier".

Szenario 1 – On-Demand-Inferenz in JupyterLab

  1. Kubernetes-Namespace für KI/ML-Inferenz-Workloads erstellen

    $ kubectl create namespace inference
    namespace/inference created
  2. Verwenden Sie das NetApp DataOps Toolkit, um ein persistentes Volume für die Daten bereitzustellen, auf denen Sie die Inferenz ausfü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 Workspace zu erstellen. Mounten Sie das persistente Volume, das im vorherigen Schritt mit dem erstellt wurde --mount- pvc Option. Weisen Sie mit dem NVIDIA-GPUs dem Workspace nach Bedarf zu -- nvidia-gpu Option.

    Im folgenden Beispiel wird das persistente Volume beschrieben inference-data Wird im JupyterLab-Workspace-Container unter installiert /home/jovyan/data. Bei der Verwendung von offiziellen Projekt-Jupyter-Container-Images, /home/jovyan Wird als oberes Verzeichnis innerhalb der JupyterLab-Weboberfläche dargestellt.

    $ 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 über die URL zu, die in der Ausgabe des angegeben ist create jupyterlab Befehl. Das Datenverzeichnis stellt das persistente Volume dar, das in den Arbeitsbereich gemountet wurde.

    Die Abbildung zeigt den Input/Output-Dialog oder die Darstellung des schriftlichen Inhalts

  5. Öffnen Sie das data Verzeichnis erstellen und die Dateien hochladen, auf denen die Inferenz durchgeführt werden soll. Wenn Dateien in das Datenverzeichnis hochgeladen werden, werden sie automatisch auf dem persistenten Volume gespeichert, das in den Arbeitsbereich eingebunden wurde. Um Dateien hochzuladen, klicken Sie wie im folgenden Bild gezeigt auf das Symbol Dateien hochladen.

    Die Abbildung zeigt den Input/Output-Dialog oder die Darstellung des schriftlichen Inhalts

  6. Kehren Sie zum Verzeichnis der obersten Ebene zurück und erstellen Sie ein neues Notebook.

    Die Abbildung zeigt den Input/Output-Dialog oder die Darstellung des schriftlichen Inhalts

  7. Fügen Sie dem Notebook den Inferenzcode hinzu. Im folgenden Beispiel wird der Inferenzcode für einen Anwendungsfall der Bilderkennung gezeigt.

    Die Abbildung zeigt den Input/Output-Dialog oder die Darstellung des schriftlichen Inhalts

    Die Abbildung zeigt den Input/Output-Dialog oder die Darstellung des schriftlichen Inhalts

  8. Fügen Sie Protopia Obfuscation in Ihren Inferenzcode ein. Protopia arbeitet direkt mit Kunden zusammen, um eine anwendungsspezifische Dokumentation bereitzustellen und befindet sich außerhalb des Umfangs dieses technischen Berichts. Das folgende Beispiel zeigt den Inferenzcode für eine Bilderkennung, wobei Protopia obfuscation hinzugefügt wurde.

    Die Abbildung zeigt den Input/Output-Dialog oder die Darstellung des schriftlichen Inhalts

    Die Abbildung zeigt den Input/Output-Dialog oder die Darstellung des schriftlichen Inhalts

Szenario 2 – Batch-Inferenz auf Kubernetes

  1. Kubernetes-Namespace für KI/ML-Inferenz-Workloads erstellen

    $ kubectl create namespace inference
    namespace/inference created
  2. Verwenden Sie das NetApp DataOps Toolkit, um ein persistentes Volume für die Daten bereitzustellen, auf denen Sie die Inferenz ausfü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, auf denen Sie die Inferenz durchführen möchten.

    Es gibt verschiedene Methoden zum Laden von Daten auf ein PVC. Wenn Ihre Daten aktuell in einer S3-kompatiblen Objekt-Storage-Plattform wie NetApp StorageGRID oder Amazon S3 gespeichert sind, können Sie verwenden "Funktionen des NetApp DataOps Toolkit S3 Data Mover". Eine weitere einfache Methode ist es, einen JupyterLab-Arbeitsbereich zu erstellen und dann Dateien über die JupyterLab-Webschnittstelle hochzuladen, wie in den Schritten 3 bis 5 im Abschnitt “ beschriebenSzenario 1 – On-Demand-Inferenz in JupyterLab.“

  4. Erstellen eines Kubernetes-Jobs für die Batch-Inferenz. Das folgende Beispiel zeigt einen Batch-Inferenzauftrag für einen Anwendungsfall zur Bilderkennung. Dieser Job führt die Inferenz auf jedem Bild in einem Satz von Bildern durch und schreibt Metriken zur Inferenzgenauigkeit.

    $ 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 Protopia Obfuscation zu Ihren Inferenz Job. Die anwendungsspezifische Anleitung zum Hinzufügen von Protopia-Obfuskation kann direkt aus Protopia gefunden werden, die nicht im Rahmen dieses technischen Berichts liegt. Das folgende Beispiel zeigt einen Batch-Inferenzauftrag für eine Gesichtserkennung Anwendungsfall mit Protopia-Obfuscation, die durch die Verwendung eines ALPHAWERTS von 0.8 hinzugefügt wurde. Bei diesem Job wird die Protopia-Obfuskation vor der Durchführung der Inferenz für jedes Bild in einer Reihe von Bildern angewendet und dann Kenngrößen für die Inferenzgenauigkeit geschrieben.

    Wir haben diesen Schritt für ALPHA-Werte 0.05, 0.1, 0.2, 0.4, 0.6, wiederholt. 0.8, 0.9 und 0.95. Die Ergebnisse sehen Sie in "„Vergleich der Genauigkeit bei der Inferenzierung“."

    $ 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 Inferenz Server

  1. Kubernetes-Namespace für KI/ML-Inferenz-Workloads erstellen

    $ 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. Sie können Ihr Modell auf dem neuen persistenten Volume in einem speichern "Formatieren" Das wird vom NVIDIA Triton Inferenz Server erkannt.

    Es gibt verschiedene Methoden zum Laden von Daten auf ein PVC. Eine einfache Methode ist es, einen JupyterLab-Arbeitsbereich zu erstellen und dann Dateien über die JupyterLab-Webschnittstelle hochzuladen, wie in den Schritten 3 bis 5 in “ beschriebenSzenario 1 – On-Demand-Inferenz in JupyterLab. „

  4. Verwenden Sie das NetApp DataOps Toolkit, um eine neue NVIDIA Triton Inferenz Server-Instanz zu implementieren.

    $ 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 zur Durchführung einer Inferenz. Im folgenden Python-Code-Auszug wird das Triton Python-Client-SDK verwendet, um eine Inferenzaufgabe für einen Anwendungsfall zur Gesichtserkennung durchzuführen. Dieses Beispiel nennt die Triton API und führt ein Bild zur Inferenz durch. Der Triton Inference Server erhält 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 Protopia Obfuscation in Ihren Inferenzcode ein. Die anwendungsspezifische Anleitung zum Hinzufügen von Protopia-Obfuskation kann direkt aus Protopia gefunden werden; dieser Vorgang liegt jedoch außerhalb des Geltungsbereichs dieses technischen Berichts. Das folgende Beispiel zeigt denselben Python-Code, der im vorhergehenden Schritt 5, jedoch mit Protopia Obfuscation hinzugefügt wird.

    Beachten Sie, dass die Protopia-Obfuskation auf das Bild angewendet wird, bevor es an die Triton-API übergeben wird. So verlässt das nicht-verschleierte Bild nie die lokale Maschine. Nur das obfuscated Image wird über das Netzwerk übertragen. Dieser Workflow eignet sich für Anwendungsfälle, in denen Daten in einer vertrauenswürdigen Zone erfasst und dann zur Inferenz außerhalb der vertrauenswürdigen Zone weitergeleitet werden müssen. Ohne Protopia Obfuscation ist es nicht möglich, diesen Workflow ohne sensible Daten zu implementieren, die 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)