Поделиться через


Руководство по API WebNN

Дополнительные сведения о поддержке операционной системы, поддержке моделей и т. д. см. в обзоре WebNN.

В этом руководстве показано, как использовать API WebNN для создания системы классификации изображений в Интернете, которая является аппаратным ускорением с помощью GPU на устройстве. Мы будем использовать модель MobileNetv2, которая является моделью открытый код на hugging Face, используемой для классификации изображений.

Если вы хотите просмотреть и запустить окончательный код этого руководства, его можно найти на сайте WebNN Developer Preview GitHub.

Примечание.

API WebNN — это рекомендация кандидата W3C и находится на ранних этапах предварительной версии разработчика. Некоторые функции ограничены. У нас есть список текущих состояний поддержки и реализации.

Требования и настройка:

Настройка Windows

Убедитесь, что у вас есть правильные версии драйверов edge, Windows и оборудования, как описано в разделе "Требования к WebNN".

Настройка edge

  1. Скачайте и установите Microsoft Edge Dev.

  2. Запустите бета-версию Edge и перейдите about:flags в адресную строку.

  3. Найдите "API WebNN", щелкните раскрывающийся список и установите значение "Включено".

  4. Перезапустите Edge, как показано в запросе.

Изображение WebNN, включенное в бета-версии Edge

Настройка среды разработчика

  1. Скачайте и установите Visual Studio Code (VSCode).

  2. Запустите VSCode.

  3. Скачайте и установите расширение Live Server для VSCode в VSCode .

  4. Выберите и создайте File --> Open Folderпустую папку в нужном расположении.

Шаг 1. Инициализация веб-приложения

  1. Чтобы начать, создайте новую index.html страницу. Добавьте следующий стандартный код на новую страницу:
<!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. Проверьте стандартный код и настройку разработчика, нажав кнопку Go Live в нижней правой части VSCode. Это должно запустить локальный сервер в Пограничной бета-версии, на котором выполняется стандартный код.
  2. Теперь создайте новый файл с именем main.js. Это будет содержать код javascript для приложения.
  3. Затем создайте вложенную папку из корневого каталога с именем images. Скачайте и сохраните любое изображение в папке. Для этой демонстрации мы будем использовать имя image.jpgпо умолчанию.
  4. Скачайте модель мобильной сети из зоопарка моделей ONNX. В этом руководстве вы будете использовать файл mobilenet2-10.onnx . Сохраните эту модель в корневой папке веб-приложения.
  5. Наконец, скачайте и сохраните файл классов изображений. imagenetClasses.js Это обеспечивает 1000 распространенных классификаций изображений для используемой модели.

Шаг 2. Добавление элементов пользовательского интерфейса и родительской функции

  1. В тексте <main> html-тегов, добавленных на предыдущем шаге, замените существующий код следующими элементами. Они будут создавать кнопку и отображать изображение по умолчанию.
<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. Теперь вы добавите веб-сайт среды выполнения ONNX на страницу, которая является библиотекой JavaScript, которую вы будете использовать для доступа к API WebNN. В тексте <head> html-тегов добавьте следующие исходные ссылки javascript.
<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. main.js Откройте файл и добавьте следующий фрагмент кода.
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
} 

Шаг 3. Предварительная обработка данных

  1. Функция, которую вы только что добавили вызовы getImageTensorFromPath, другая функция, которую необходимо реализовать. Вы добавите его ниже, а также другую асинхронную функцию, которая вызывается для получения самого изображения.
  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. Кроме того, необходимо добавить imageDataToTensor функцию, на которую ссылается выше, которая будет отображать загруженный образ в тензорном формате, который будет работать с нашей моделью ONNX. Это более связанная функция, хотя это может показаться знакомым, если вы работали с аналогичными приложениями классификации изображений раньше. Дополнительные сведения см . в этом руководстве ONNX.
  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;
  }

Шаг 4. Вызов WebNN

  1. Теперь вы добавили все функции, необходимые для получения изображения и отрисовки его в виде тензора. Теперь, используя веб-библиотеку среды выполнения ONNX, загруженную выше, вы запустите модель. Обратите внимание, что для использования WebNN здесь просто укажите executionProvider = "webnn" . Поддержка onNX Runtime делает ее очень простой для включения WebNN.
  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; 
  } 

Шаг 5. Данные после обработки

  1. Наконец, вы добавите softmax функцию, а затем добавьте окончательную функцию для возврата наиболее вероятной классификации изображений. Преобразует softmax значения в диапазон от 0 до 1, что является формой вероятности, необходимой для этой окончательной классификации.

Сначала добавьте следующие исходные файлы для вспомогательных библиотек Джимп и Lodash в теге main.jsголовы.

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

Теперь добавьте следующие функции main.jsв .

// 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. Теперь вы добавили все скрипты, необходимые для запуска классификации образов с помощью WebNN в базовом веб-приложении. С помощью расширения Live Server для VS Code теперь можно запустить базовую веб-страницу в приложении, чтобы просмотреть результаты классификации самостоятельно.