Freigeben über


WebNN-API-Tutorial

Eine Einführung in WebNN, einschließlich Informationen zu den unterstützten Betriebssystemen, Modellen und mehr, finden Sie in der WebNN-Übersicht.

In diesem Tutorial wird gezeigt, wie Sie die WebNN-API zum Erstellen eines Systems zur Bildklassifizierung im Web nutzen, das mit der GPU auf dem Gerät hardwarebeschleunigt wird. Wir nutzen das MobileNetv2-Modell, bei dem es sich um ein Open-Source-Modell für Hugging Face zur Bildklassifizierung handelt.

Wenn Sie den fertigen Code dieses Tutorials anzeigen und ausführen möchten, finden Sie ihn auf unserem WebNN Developer Preview GitHub.

Hinweis

Die WebNN-API ist eine W3C Candidate Recommendation und befindet sich in den frühen Phasen einer Developer Preview. Bestimmte Funktionalität ist daher eingeschränkt. Wir haben eine Liste des aktuellen Support- und Implementierungsstatus.

Anforderungen und Einrichtung:

Einrichten von Windows

Stellen Sie sicher, dass Sie über die richtigen Versionen von Edge-, Windows- und Hardwaretreibern verfügen, wie im Abschnitt „Anforderungen für WebNN“ beschrieben.

Einrichten von Edge

  1. Laden Sie Microsoft Edge Dev herunter, und installieren Sie diese.

  2. Starten Sie Edge Beta, und navigieren Sie zu about:flags zur Adressleiste.

  3. Suchen Sie nach „WebNN-API“, klicken Sie auf das Dropdown-Feld, und legen Sie es auf „Aktiviert“ fest.

  4. Starten Sie Edge neu, wenn Sie dazu aufgefordert werden.

Eine Abbildung von WebNN, das in Edge Beta aktiviert ist

Einrichten der Entwicklungsumgebung

  1. Laden Sie Visual Studio Code (VSCode) herunter, und installieren Sie die Software.

  2. Starten Sie VSCode.

  3. Laden Sie die Live-Servererweiterung für VSCode in VSCode herunter, und installieren Sie sie.

  4. Wählen Sie File --> Open Folder aus, und erstellen Sie einen leeren Ordner an Ihrem gewünschten Speicherort.

Schritt 1: Initialisieren der Web-App

  1. Erstellen Sie zunächst eine neue index.html-Seite. Fügen Sie Ihrer Seite den folgenden Codebaustein hinzu:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Website</title>
  </head>
  <body>
    <main>
        <h1>Welcome to My Website</h1>
    </main>
  </body>
</html>
  1. Überprüfen Sie den Codebaustein und das Entwicklersetup, indem Sie unten rechts in VSCode die Schaltfläche „Go Live“ auswählen. Das sollte einen lokalen Server in Edge Beta starten, auf dem der Codebausteine ausgeführt wird.
  2. Erstellen Sie jetzt eine neue Datei namens main.js. Diese Datei enthält den gesamten JavaScript-Code für die App.
  3. Erstellen Sie als Nächstes einen Unterordner im Stammverzeichnis namens images. Laden Sie ein beliebiges Bild im Ordner herunter, und speichern Sie es. In dieser Demo verwenden wir den Standardnamen von image.jpg.
  4. Laden Sie das Modell mobilenet aus dem ONNX Model Zoo herunter. In diesem Lernprogramm werden Sie die Datei mobilenet2-10.onnx verwenden. Speichern Sie dieses Modell im Stammordner Ihrer Web-App.
  5. Laden Sie schließlich diese Bildklassendatei herunter, und speichern Sie sieimagenetClasses.js. Dies stellt 1000 allgemeine Klassifizierungen von Bildern bereit, die für Ihr Modell verwendet werden können.

Schritt 2: Hinzufügen von UI-Elementen und einer übergeordneten Funktion

  1. Ersetzen Sie im Text der <main>-html-Tags, die Sie im vorherigen Schritt hinzugefügt haben, den vorhandenen Code durch die folgenden Elemente. Dadurch wird eine Schaltfläche erstellt und ein Standardbild angezeigt.
<h1>Image Classification Demo!</h1> 
<div><img src="./images/image.jpg"></div> 
<button onclick="classifyImage('./images/image.jpg')"  type="button">Click Me to Classify Image!</button> 
<h1 id="outputText"> This image displayed is ... </h1>
  1. Nun fügen Sie ONNX Runtime Web zu Ihrer Seite hinzu, wobei es sich um eine JavaScript-Bibliothek handelt, mit der Sie auf die WebNN-API zugreifen können. Fügen Sie im Text der <head> HTML-Tags die folgenden JavaScript-Quellverknüpfungen hinzu:
<script src="./main.js"></script> 
<script src="imagenetClasses.js"></script>
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.18.0-dev.20240311-5479124834/dist/ort.webgpu.min.js"></script> 
  1. Öffnen Sie Ihre main.js-Datei, und fügen Sie den folgenden Codeschnipsel hinzu:
async function classifyImage(pathToImage){ 
  var imageTensor = await getImageTensorFromPath(pathToImage); // Convert image to a tensor
  var predictions = await runModel(imageTensor); // Run inference on the tensor
  console.log(predictions); // Print predictions to console
  document.getElementById("outputText").innerHTML += predictions[0].name; // Display prediction in HTML
} 

Schritt 3: Vorverarbeitung der Daten

  1. Die Funktion, die Sie gerade hinzugefügt haben, ruft getImageTensorFromPath auf, eine andere Funktion, die Sie implementieren müssen. Sie fügen sie unten sowie eine andere asynchrone Funktion hinzu, die zum Abrufen des Bildes aufgerufen wird.
  async function getImageTensorFromPath(path, width = 224, height = 224) {
    var image = await loadImagefromPath(path, width, height); // 1. load the image
    var imageTensor = imageDataToTensor(image); // 2. convert to tensor
    return imageTensor; // 3. return the tensor
  } 

  async function loadImagefromPath(path, resizedWidth, resizedHeight) {
    var imageData = await Jimp.read(path).then(imageBuffer => { // Use Jimp to load the image and resize it.
      return imageBuffer.resize(resizedWidth, resizedHeight);
    });

    return imageData.bitmap;
  }
  1. Sie müssen auch die oben referenzierte imageDataToTensor-Funktion hinzufügen, die das geladene Bild in einem Tensorformat rendert, das mit unserem ONNX-Modell funktioniert. Diese Funktion ist stärker involviert, obwohl sie vielleicht vertraut sein könnte, wenn Sie zuvor mit ähnlichen Bildklassifizierungs-Apps gearbeitet haben. Für eine erweiterte Erläuterung können Sie dieses ONNX-Tutorial aufrufen.
  function imageDataToTensor(image) {
    var imageBufferData = image.data;
    let pixelCount = image.width * image.height;
    const float32Data = new Float32Array(3 * pixelCount); // Allocate enough space for red/green/blue channels.

    // Loop through the image buffer, extracting the (R, G, B) channels, rearranging from
    // packed channels to planar channels, and converting to floating point.
    for (let i = 0; i < pixelCount; i++) {
      float32Data[pixelCount * 0 + i] = imageBufferData[i * 4 + 0] / 255.0; // Red
      float32Data[pixelCount * 1 + i] = imageBufferData[i * 4 + 1] / 255.0; // Green
      float32Data[pixelCount * 2 + i] = imageBufferData[i * 4 + 2] / 255.0; // Blue
      // Skip the unused alpha channel: imageBufferData[i * 4 + 3].
    }
    let dimensions = [1, 3, image.height, image.width];
    const inputTensor = new ort.Tensor("float32", float32Data, dimensions);
    return inputTensor;
  }

Schritt 4: Aufrufen von WebNN

  1. Sie haben nun alle erforderlichen Funktionen zum Abrufen Ihres Bilds und zum Rendern als Tensor hinzugefügt. Nachdem Sie nun die oben geladene Web-Bibliothek ONNX-Runtime verwendet haben, führen Sie Ihr Modell aus. Beachten Sie, dass Sie hier für die Verwendung von WebNN hier lediglich executionProvider = "webnn" angeben müssen – durch die Unterstützung der ONNX-Runtime gestaltet sich die Aktivierung von WebNN sehr einfach.
  async function runModel(preprocessedData) { 
    // Set up environment.
    ort.env.wasm.numThreads = 1; 
    ort.env.wasm.simd = true; 
    ort.env.wasm.proxy = true; 
    ort.env.logLevel = "verbose";  
    ort.env.debug = true; 

    // Configure WebNN.
    const executionProvider = "webnn"; // Other options: webgpu 
    const modelPath = "./mobilenetv2-7.onnx" 
    const options = {
	    executionProviders: [{ name: executionProvider, deviceType: "gpu", powerPreference: "default" }],
      freeDimensionOverrides: {"batch": 1, "channels": 3, "height": 224, "width": 224}
    };
    modelSession = await ort.InferenceSession.create(modelPath, options); 

    // Create feeds with the input name from model export and the preprocessed data. 
    const feeds = {}; 
    feeds[modelSession.inputNames[0]] = preprocessedData; 
    // Run the session inference.
    const outputData = await modelSession.run(feeds); 
    // Get output results with the output name from the model export. 
    const output = outputData[modelSession.outputNames[0]]; 
    // Get the softmax of the output data. The softmax transforms values to be between 0 and 1.
    var outputSoftmax = softmax(Array.prototype.slice.call(output.data)); 
    // Get the top 5 results.
    var results = imagenetClassesTopK(outputSoftmax, 5);

    return results; 
  } 

Schritt 5: Nachbearbeitung von Daten

  1. Schließlich fügen Sie eine softmax-Funktion hinzu, und dann die endgültige Funktion, um die wahrscheinlichste Bildklassifizierung zurückzugeben. Die softmax-Transformationen ihrer Werte werden zwischen 0 und 1 umgewandelt. Dabei handelt es sich um die für diese endgültige Klassifizierung erforderliche Wahrscheinlichkeitsform.

Fügen Sie zunächst die folgenden Quelldateien für Hilfsbibliotheken Jimp und Lodash im Head-Tag von main.js hinzu.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jimp/0.22.12/jimp.min.js" integrity="sha512-8xrUum7qKj8xbiUrOzDEJL5uLjpSIMxVevAM5pvBroaxJnxJGFsKaohQPmlzQP8rEoAxrAujWttTnx3AMgGIww==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

Fügen Sie nun diese folgenden Funktionen zu main.js hinzu.

// The softmax transforms values to be between 0 and 1.
function softmax(resultArray) {
  // Get the largest value in the array.
  const largestNumber = Math.max(...resultArray);
  // Apply the exponential function to each result item subtracted by the largest number, using reduction to get the
  // previous result number and the current number to sum all the exponentials results.
  const sumOfExp = resultArray 
    .map(resultItem => Math.exp(resultItem - largestNumber)) 
    .reduce((prevNumber, currentNumber) => prevNumber + currentNumber);

  // Normalize the resultArray by dividing by the sum of all exponentials.
  // This normalization ensures that the sum of the components of the output vector is 1.
  return resultArray.map((resultValue, index) => {
    return Math.exp(resultValue - largestNumber) / sumOfExp
  });
}

function imagenetClassesTopK(classProbabilities, k = 5) { 
  const probs = _.isTypedArray(classProbabilities)
    ? Array.prototype.slice.call(classProbabilities)
    : classProbabilities;

  const sorted = _.reverse(
    _.sortBy(
      probs.map((prob, index) => [prob, index]),
      probIndex => probIndex[0]
    )
  );

  const topK = _.take(sorted, k).map(probIndex => {
    const iClass = imagenetClasses[probIndex[1]]
    return {
      id: iClass[0],
      index: parseInt(probIndex[1].toString(), 10),
      name: iClass[1].replace(/_/g, " "),
      probability: probIndex[0]
    }
  });
  return topK;
}
  1. Sie haben nun alle erforderlichen Skripts hinzugefügt, um die Bildklassifizierung mit WebNN in Ihrer grundlegenden Web-App auszuführen. Mit der Live-Servererweiterung für VS Code können Sie jetzt Ihre grundlegende Webseite in der App starten, um die Ergebnisse der Klassifizierung selbst aufzurufen.