LiteRT.js 使用入门

这是一份端到端 LiteRT.js 指南,其中介绍了如何转换 PyTorch 模型以在浏览器中运行并使用 WebGPU 加速。此示例使用 ResNet18 作为视觉模型,并使用 TensorFlow.js 进行预处理和后处理。

本指南将介绍如何执行以下步骤:

  1. 使用 AI Edge Torch 将 PyTorch 模型转换为 LiteRT。
    1. 将 LiteRT 软件包添加到您的 Web 应用中。
  2. 加载模型。
  3. 编写预处理和后处理逻辑。

转换为 LiteRT

使用 PyTorch 转换器笔记本将 PyTorch 模型转换为相应的 .tflite 格式。如需详细了解您可能会遇到的错误类型以及如何修正这些错误,请参阅 AI Edge Torch Converter README

您的模型必须与 torch.export.export 兼容,这意味着它必须可使用 TorchDynamo 导出。因此,它不得包含任何依赖于张量中运行时值的 Python 条件分支。如果您在 torch.export.export 期间看到以下错误torch.export.export,则表示您的模型无法通过 torch.export.export 导出。此外,模型张量不得具有任何动态输入或输出维度。这包括批次维度。

您还可以从与 TensorRT 兼容或可导出为 ONNX 的 PyTorch 模型开始:

  • 模型的 TensorRT 兼容版本可能是一个不错的起点,因为某些类型的 TensorRT 转换也要求模型是 TorchDynamo 可导出的。如果您在模型中使用任何 NVIDIA / CUDA 操作,则需要将它们替换为标准 PyTorch 操作。

  • 可导出为 ONNX 的 PyTorch 模型可以是一个不错的起点,不过有些 ONNX 模型使用 TorchScript 而不是 TorchDynamo 来导出,在这种情况下,模型可能无法导出为 TorchDynamo(不过它可能比原始模型代码更接近)。

如需了解详情,请参阅将 PyTorch 模型转换为 LiteRT

添加 LiteRT 软件包

从 npm 安装 @litertjs/core 软件包:

npm install @litertjs/core

导入软件包并加载其 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/`);

加载模型

导入并初始化 LiteRT.js 和 LiteRT-TFJS 转换实用程序。您还需要导入 TensorFlow.js,以便将张量传递给 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();

加载转换后的 LiteRT 模型:

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

编写模型流水线

编写将模型连接到应用的前处理和后处理逻辑。建议使用 TensorFlow.js 进行前处理和后处理,但如果不是使用 TensorFlow.js 编写的,您可以调用 await tensor.data 以获取 ArrayBuffer 形式的值,或调用 await tensor.array 以获取结构化 JS 数组。

以下是 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);
}

测试和问题排查

如需了解如何测试应用和处理错误,请参阅以下部分。

使用虚假输入进行测试

加载模型后,最好先使用虚假输入测试模型。这样一来,您就可以在花费时间为模型流水线编写预处理和后处理逻辑之前捕获任何运行时错误。如需检查这一点,您可以使用 LiteRT.js 模型测试工具或手动进行测试。

LiteRT.js 模型测试工具

LiteRT.js 模型测试器使用随机输入在 GPU 和 CPU 上运行模型,以验证模型是否能在 GPU 上正确运行。它会检查以下内容:

  • 输入和输出数据类型是否受支持。
  • 所有操作是否都可在 GPU 上运行。
  • GPU 输出与参考 CPU 输出的匹配程度。
  • GPU 推理的性能。

如需运行 LiteRT.js 模型测试工具,请运行 npm i @litertjs/model-tester,然后运行 npx model-tester。系统随即会打开一个浏览器标签页,供您运行模型。

手动模型测试

如果您不想使用 LiteRT.js 模型测试器 (@litertjs/model-tester),而是想手动测试模型,可以生成虚假输入并使用 runWithTfjsTensors 运行模型。

如需生成虚假输入,您需要知道输入张量的名称和形状。您可以通过调用 model.getInputDetailsmodel.getOutputDetails,使用 LiteRT.js 找到这些信息。查找这些变量的一种简单方法是在创建模型后设置断点。或者,您也可以使用模型分层图表

了解输入和输出的形状和名称后,您可以使用虚假输入来测试模型。这样一来,您就可以放心地在编写其余的机器学习流水线之前运行模型。这将测试是否支持所有模型操作。例如:

// 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);

错误类型

LiteRT.js 可能不支持某些 LiteRT 模型。错误通常属于以下类别:

  • 形状不匹配:仅影响 GPU 的已知 bug。
  • 不支持的操作:运行时不支持模型中的某项操作。WebGPU 后端的覆盖范围比 CPU 更有限,因此如果您在 GPU 上看到此错误,或许可以改为在 CPU 上运行模型。
  • 不支持的张量类型:LiteRT.js 仅支持 int32 和 float32 张量作为模型输入和输出。
  • 模型过大:LiteRT.js 可加载的模型大小有限。

不支持的操作

这表示所使用的后端不支持模型中的某项操作。您需要重写原始 PyTorch 模型以避免此操作,然后重新转换该模型,或者您或许可以在 CPU 上运行该模型。

对于 BROADCAST_TO,可以通过使模型中每个输入张量的批次维度相同来解决此问题。其他情况可能更复杂。

不支持的张量类型

LiteRT.js 仅支持 int32 和 float32 张量作为模型的输入和输出。

模型过大

这通常表现为对 Aborted() 的调用或模型加载时的内存分配失败。LiteRT.js 可加载的模型大小有限,因此如果您看到此消息,则表示您的模型可能过大。您可以尝试使用 ai-edge-quantizer 对权重进行量化,但将计算保持在 float32 或 float16,并将模型输入和输出保持为 float32 或 int32。