بدء استخدام LiteRT.js

هذا دليل شامل حول LiteRT.js يغطي عملية تحويل نموذج PyTorch لتشغيله في المتصفح باستخدام تسريع WebGPU. يستخدم هذا المثال ResNet18 لنموذج الرؤية، وTensorFlow.js للمعالجة المسبقة واللاحقة.

سيتناول الدليل الخطوات التالية:

  1. حوِّل نموذج PyTorch إلى LiteRT باستخدام AI Edge Torch. 1- أضِف حزمة LiteRT إلى تطبيق الويب.
  2. حمِّل النموذج.
  3. كتابة منطق المعالجة المسبقة واللاحقة

التحويل إلى LiteRT

استخدِم دفتر ملاحظات PyTorch Converter لتحويل نموذج PyTorch إلى تنسيق .tflite المناسب. للحصول على دليل مفصّل حول أنواع الأخطاء التي قد تواجهها وكيفية إصلاحها، يُرجى الاطّلاع على ملف README الخاص بأداة AI Edge Torch Converter.

يجب أن يكون النموذج متوافقًا مع torch.export.export، ما يعني أنّه يجب أن يكون قابلاً للتصدير باستخدام TorchDynamo. لذلك، يجب ألا يحتوي على أي فروع شرطية في Python تعتمد على قيم وقت التشغيل ضمن الموترات. إذا ظهرت لك الأخطاء التالية أثناء torch.export.export، لا يمكن تصدير النموذج باستخدام torch.export.export. يجب أيضًا ألّا يتضمّن النموذج أي أبعاد إدخال أو إخراج ديناميكية في موتراته. ويشمل ذلك سمة الدُفعة.

يمكنك أيضًا البدء بنموذج PyTorch متوافق مع TensorRT أو يمكن تصديره إلى ONNX:

  • يمكن أن يكون إصدار النموذج المتوافق مع TensorRT نقطة بداية جيدة، لأنّ بعض أنواع عمليات التحويل في TensorRT تتطلّب أيضًا أن تكون النماذج قابلة للتصدير في TorchDynamo. إذا كنت تستخدم أي عمليات NVIDIA / CUDA في النموذج، عليك استبدالها بعمليات PyTorch العادية.

  • يمكن أن يكون نموذج PyTorch قابل للتصدير إلى ONNX نقطة بداية جيدة، على الرغم من أنّ بعض نماذج ONNX تستخدم TorchScript بدلاً من TorchDynamo للتصدير، وفي هذه الحالة، قد لا يكون النموذج قابلاً للتصدير إلى TorchDynamo (على الرغم من أنّه من المحتمل أن يكون أقرب إلى ذلك من رمز النموذج الأصلي).

لمزيد من المعلومات، اطّلِع على مقالة تحويل نماذج PyTorch إلى LiteRT.

إضافة حزمة LiteRT

ثبِّت حزمة @litertjs/core من npm:

npm install @litertjs/core

استورِد الحزمة وحمِّل ملفات Wasm الخاصة بها:

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

// Load the LiteRT.js Wasm files from a CDN.
await loadLiteRt('https://cdn.jsdelivr.net/npm/@litertjs/core/wasm/')
// Alternatively, host them from your server.
// They are located in node_modules/@litertjs/core/wasm/
await loadLiteRt(`your/path/to/wasm/`);

تحميل النموذج

استورِد LiteRT.js وأدوات تحويل LiteRT-TFJS وفعِّلها. عليك أيضًا استيراد TensorFlow.js لتمرير الموترات إلى LiteRT.js.

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

// TensorFlow.js imports
import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-webgpu';
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 TFJS use the same GPU device as LiteRT.js (for tensor conversion)
  const device = await getWebGpuDevice();
  tf.removeBackend('webgpu');
  tf.registerBackend('webgpu', () => new WebGPUBackend(device, device.adapterInfo));
  await tf.setBackend('webgpu');
  // ...
}

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 imageData = 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
  return 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]);
});

// Run the model
const outputs = await runWithTfjsTensors(model, [imageData]);
const probabilities = outputs[0];

// Get the top five classes.
const top5 = tf.topk(probabilities, 5);

const values = await top5.values.data();
const indices = await top5.indices.data();

// Clean up TFJS tensors
tf.dispose(outputs);
tf.dispose(top5);
tf.dispose(imageData);

// 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 Model Tester

يشغّل أداة اختبار النماذج LiteRT.js النموذج على وحدة معالجة الرسومات ووحدة المعالجة المركزية باستخدام مدخلات عشوائية للتحقّق من أنّ النموذج يعمل بشكل صحيح على وحدة معالجة الرسومات. يفحص المساعِد ما يلي:

  • تحديد ما إذا كانت أنواع بيانات الإدخال والإخراج متوافقة
  • ما إذا كانت جميع العمليات متاحة على وحدة معالجة الرسومات.
  • مدى تطابق مخرجات وحدة معالجة الرسومات مع مخرجات وحدة المعالجة المركزية المرجعية
  • أداء الاستدلال في وحدة معالجة الرسومات

لتشغيل أداة اختبار نماذج LiteRT.js، شغِّل npm i @litertjs/model-tester ثم npx model-tester. سيتم فتح علامة تبويب متصفّح لتشغيل النموذج.

اختبار النموذج اليدوي

إذا كنت تفضّل اختبار النموذج يدويًا بدلاً من استخدام أداة اختبار نموذج LiteRT.js (@litertjs/model-tester)، يمكنك إنشاء مدخلات وهمية وتشغيل النموذج باستخدام runWithTfjsTensors.

لإنشاء مدخلات وهمية، عليك معرفة أسماء وأشكال موترات الإدخال. يمكن العثور على هذه القيم باستخدام LiteRT.js من خلال استدعاء model.getInputDetails أو model.getOutputDetails. يمكنك بدلاً من ذلك استخدام مستكشف النماذج.

بعد معرفة أشكال وأسماء الإدخال والإخراج، يمكنك اختبار النموذج باستخدام إدخال مزيّف. يمنحك ذلك بعض الثقة في أنّ النموذج سيعمل قبل كتابة بقية مسار تعلُّم الآلة. سيؤدي ذلك إلى اختبار ما إذا كانت جميع عمليات النموذج متوافقة. على سبيل المثال:

// 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 = await runWithTfjsTensors(model, fakeInputs);
console.log(outputs);

أنواع الأخطاء

قد لا تكون بعض نماذج LiteRT متوافقة مع LiteRT.js. تندرج الأخطاء عادةً ضمن الفئات التالية:

  • عدم تطابق الشكل: خطأ معروف يؤثر في وحدة معالجة الرسومات فقط.
  • العملية غير متاحة: لا يتيح وقت التشغيل إجراء عملية في النموذج. تتوفّر WebGPU في نطاق أضيق من وحدة المعالجة المركزية، لذا إذا ظهرت لك هذه الرسالة على وحدة معالجة الرسومات، قد تتمكّن من تشغيل النموذج على وحدة المعالجة المركزية بدلاً من ذلك.
  • نوع Tensor غير متوافق: لا يتيح LiteRT.js استخدام سوى int32 وfloat32 tensors كمدخلات ومخرجات للنموذج.
  • النموذج كبير جدًا: هناك حدّ لحجم النماذج التي يمكن تحميلها باستخدام LiteRT.js.

العملية غير متاحة

يشير هذا إلى أنّ الخلفية المستخدَمة لا تتوافق مع إحدى العمليات في النموذج. عليك إعادة كتابة نموذج PyTorch الأصلي لتجنُّب هذه العملية وإعادة تحويله، أو قد تتمكّن من تشغيل النموذج على وحدة المعالجة المركزية.

في حالة BROADCAST_TO، يمكن حلّ هذه المشكلة من خلال جعل بُعد الدُفعة هو نفسه لكل موتر إدخال إلى النموذج. وقد تكون بعض الحالات أكثر تعقيدًا.

نوع Tensor غير متوافق

لا يتيح LiteRT.js سوى استخدام موترات int32 وfloat32 لمدخلات النموذج ومخرجاته.

النموذج كبير جدًا

يظهر ذلك عادةً على شكل طلب Aborted() أو تعذُّر تخصيص الذاكرة أثناء تحميل النموذج. يقتصر حجم النماذج التي يمكن تحميلها باستخدام LiteRT.js، لذا إذا ظهرت لك هذه الرسالة، قد يكون حجم النموذج كبيرًا جدًا. يمكنك محاولة تحديد عدد البتات للأوزان باستخدام ai-edge-quantizer، ولكن احتفِظ بالعمليات الحسابية على float32 أو float16، واحتفِظ بمدخلات النموذج ومخرجاته على float32 أو int32.