Pierwsze kroki z LiteRT.js

Kompletny przewodnik po LiteRT.js obejmujący proces konwersji modelu PyTorch do uruchamiania w przeglądarce z akceleracją WebGPU. W tym przykładzie jako model wizyjny używany jest model ResNet18, a do przetwarzania wstępnego i końcowego używana jest biblioteka TensorFlow.js.

W przewodniku znajdziesz te informacje:

  1. Skonwertuj model PyTorch na LiteRT za pomocą AI Edge Torch.
    1. Dodaj pakiet LiteRT do swojej aplikacji internetowej.
  2. Wczytaj model.
  3. Napisz logikę przetwarzania wstępnego i końcowego.

Konwertowanie na LiteRT

Użyj notebooka PyTorch Converter, aby przekonwertować model PyTorch do odpowiedniego formatu .tflite. Szczegółowy przewodnik po typach błędów, które mogą wystąpić, i sposobach ich naprawy znajdziesz w pliku README konwertera AI Edge Torch.

Model musi być zgodny z torch.export.export, co oznacza, że musi być eksportowalny za pomocą TorchDynamo. Dlatego nie może zawierać żadnych warunkowych gałęzi Pythona, które zależą od wartości w tensorach w czasie działania. Jeśli podczas eksportowania torch.export.exportwystąpią te błędy,torch.export.export model nie będzie eksportowany za pomocą torch.export.export. Model nie może też mieć dynamicznych wymiarów wejściowych ani wyjściowych w tensorach. Obejmuje to wymiar partii.

Możesz też zacząć od modelu PyTorch zgodnego z TensorRT lub eksportowanego do ONNX:

  • Dobrym punktem wyjścia może być wersja modelu zgodna z TensorRT, ponieważ niektóre typy konwersji TensorRT wymagają też, aby modele były eksportowane w TorchDynamo. Jeśli w modelu używasz operacji NVIDIA / CUDA, musisz je zastąpić standardowymi operacjami PyTorch.

  • Model PyTorch, który można wyeksportować do ONNX, może być dobrym punktem wyjścia, chociaż niektóre modele ONNX używają do eksportowania TorchScript zamiast TorchDynamo. W takim przypadku model może nie być eksportowalny do TorchDynamo (chociaż prawdopodobnie jest bliższy niż oryginalny kod modelu).

Więcej informacji znajdziesz w artykule Konwertowanie modeli PyTorch na LiteRT.

Dodawanie pakietu LiteRT

Zainstaluj pakiet @litertjs/core z npm:

npm install @litertjs/core

Zaimportuj pakiet i wczytaj jego pliki Wasm:

import {loadLiteRt} from '@litertjs/core';

// They are located in node_modules/@litertjs/core/wasm/
// Serve them statically on your server.
await loadLiteRt(`your/path/to/wasm/`);

Wczytywanie modelu

Zaimportuj i zainicjuj LiteRT.js oraz narzędzia do konwersji LiteRT-TFJS. Musisz też zaimportować TensorFlow.js, aby przekazywać tensory do LiteRT.js.

import {CompileOptions, loadAndCompile, loadLiteRt, setWebGpuDevice} from '@litertjs/core';
import {runWithTfjsTensors} from '@litertjs/tfjs-interop';

// TensorFlow.js imports
import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-webgpu'; // Only WebGPU is supported
import {WebGPUBackend} from '@tensorflow/tfjs-backend-webgpu';

async function main() {
  // Initialize TensorFlow.js WebGPU backend
  await tf.setBackend('webgpu');

  // Initialize LiteRT.js's Wasm files
  await loadLiteRt('your/path/to/wasm/');

  // Make LiteRt use the same GPU device as TFJS (for tensor conversion)
  const backend = tf.backend() as WebGPUBackend;
  setWebGpuDevice(backend.device);
  // ...
}

main();

Wczytaj przekonwertowany model LiteRT:

const model = await loadAndCompile('path_to_model.tflite', {
  accelerator: 'webgpu', // or 'wasm'
});

Tworzenie potoku modelu

Napisz logikę przetwarzania wstępnego i końcowego, która połączy model z aplikacją. Zalecamy używanie TensorFlow.js do przetwarzania wstępnego i końcowego, ale jeśli nie jest ono napisane w TensorFlow.js, możesz wywołać await tensor.data, aby uzyskać wartość jako ArrayBuffer, lub await tensor.array, aby uzyskać strukturalną tablicę JS.

Oto przykład kompleksowego potoku dla modelu ResNet18:

// Wrap in a tf.tidy call to automatically clean up intermediate TensorFlow.js tensors.
// (Note: tidy only supports synchronous functions).
const top5 = tf.tidy(() => {
  // Get RGB data values from an image element and convert it to range [0, 1).
  const image = tf.browser.fromPixels(dogs, 3).div(255);

  // These preprocessing steps come from https://github.com/pytorch/vision/blob/main/torchvision/models/resnet.py#L315
  // The mean and standard deviation for the image normalization come from https://github.com/pytorch/vision/blob/main/torchvision/transforms/_presets.py#L38
  const imageData = image.resizeBilinear([224, 224])
    .sub([0.485, 0.456, 0.406])
    .div([0.229, 0.224, 0.225])
    .reshape([1, 224, 224, 3])
    .transpose([0, 3, 1, 2]);

  // You can pass inputs as a single tensor, an array, or a JS Object
  // where keys are the tensor names in the TFLite model.
  // When passing an Object, the output is also an Object.
  // Here, we're passing a single tensor, so the output is an array.
  const probabilities = runWithTfjsTensors(model, imageData)[0];

  // Get the top five classes.
  return tf.topk(probabilities, 5);
});

const values = await top5.values.data();
const indices = await top5.indices.data();
top5.values.dispose(); // Clean up the tfjs tensors.
top5.indices.dispose();

// Print the top five classes.
const classes = ... // Class names are loaded from a JSON file in the demo.
for (let i = 0; i < 5; ++i) {
  const text = `${classes[indices[i]]}: ${values[i]}`;
  console.log(text);
}

Testowanie i rozwiązywanie problemów

W sekcjach poniżej znajdziesz informacje o sposobach testowania aplikacji i obsługi błędów.

Testowanie za pomocą fałszywych danych wejściowych

Po wczytaniu modelu warto najpierw przetestować go za pomocą fałszywych danych wejściowych. Pozwoli to wykryć błędy wykonania, zanim poświęcisz czas na napisanie logiki przetwarzania wstępnego i końcowego dla potoku modelu. Aby to sprawdzić, możesz użyć narzędzia LiteRT.js Model Tester lub przetestować model ręcznie.

Tester modelu LiteRT.js

Tester modelu LiteRT.js uruchamia model na procesorze graficznym i procesorze, używając losowych danych wejściowych, aby sprawdzić, czy model działa prawidłowo na procesorze graficznym. Sprawdza ona te kwestie:

  • czy obsługiwane są typy danych wejściowych i wyjściowych;
  • Określa, czy wszystkie operacje są dostępne na GPU.
  • Jak bardzo dane wyjściowe GPU pasują do referencyjnych danych wyjściowych CPU.
  • Wydajność wnioskowania na GPU.

Aby uruchomić tester modelu LiteRT.js, wpisz npm i @litertjs/model-tester, a następnie npx model-tester. Otworzy się karta przeglądarki, na której możesz uruchomić model.

Ręczne testowanie modelu

Jeśli wolisz ręcznie przetestować model zamiast używać testera modelu LiteRT.js (@litertjs/model-tester), możesz wygenerować fałszywe dane wejściowe i uruchomić model za pomocą runWithTfjsTensors.

Aby wygenerować fałszywe dane wejściowe, musisz znać nazwy i kształty tensorów wejściowych. Możesz je znaleźć za pomocą LiteRT.js, wywołując model.getInputDetails lub model.getOutputDetails. Prostym sposobem na ich znalezienie jest ustawienie punktu przerwania po utworzeniu modelu. Możesz też użyć Eksploratora modeli.

Gdy poznasz kształty i nazwy danych wejściowych i wyjściowych, możesz przetestować model za pomocą fałszywych danych wejściowych. Daje to pewność, że model będzie działać, zanim napiszesz resztę potoku uczenia maszynowego. W ten sposób sprawdzisz, czy wszystkie operacje modelu są obsługiwane. Na przykład:

// Imports, initialization, and model loading...
// Create fake inputs for the model
const fakeInputs = model.getInputDetails().map(
    ({shape, dtype}) => tf.ones(shape, dtype));

// Run the model
const outputs = runWithTfjsTensors(model, fakeInputs);
console.log(outputs);

Typy błędów

Niektóre modele LiteRT mogą nie być obsługiwane przez LiteRT.js. Błędy zwykle należą do tych kategorii:

  • Niezgodność kształtu: znany błąd, który występuje tylko w przypadku GPU.
  • Operacja nie jest obsługiwana: środowisko wykonawcze nie obsługuje operacji w modelu. Backend WebGPU ma bardziej ograniczony zakres niż CPU, więc jeśli widzisz ten błąd na GPU, możesz zamiast tego uruchomić model na CPU.
  • Nieobsługiwany typ tensora: LiteRT.js obsługuje tylko tensory int32 i float32 jako dane wejściowe i wyjściowe modelu.
  • Model Too Large (Model jest za duży): LiteRT.js ma ograniczenia dotyczące rozmiaru modeli, które może wczytywać.

Operacja nie jest obsługiwana

Oznacza to, że używany backend nie obsługuje jednej z operacji w modelu. Musisz ponownie napisać oryginalny model PyTorch, aby uniknąć tej operacji, i ponownie go przekonwertować. Możesz też uruchomić model na procesorze.

W przypadku BROADCAST_TO można to rozwiązać, ustawiając ten sam wymiar partii dla każdego tensora wejściowego modelu. Inne przypadki mogą być bardziej skomplikowane.

Nieobsługiwany typ tensora

LiteRT.js obsługuje tylko tensory int32 i float32 jako dane wejściowe i wyjściowe modelu.

Model jest za duży

Zwykle pojawia się jako wywołanie funkcji Aborted() lub błąd przydzielania pamięci podczas wczytywania modelu. LiteRT.js ma ograniczenia dotyczące rozmiaru modeli, które może wczytywać, więc jeśli widzisz ten komunikat, Twój model może być zbyt duży. Możesz spróbować kwantyzować wagi za pomocą ai-edge-quantizer, ale obliczenia powinny być wykonywane w formacie float32 lub float16, a dane wejściowe i wyjściowe modelu w formacie float32 lub int32.