TuBrief
Subscribed Channels
Videos
Community

ثلاثة حقول ألغام ستواجهها عند الانتقال من TensorFlow.js إلى LiteRT.js

TuBrief Editorial
July 18, 2026
0
Computing/Software

Written with AI assistance from the source video. The video is the authority.

العربية한국어EnglishEspañol中文हिन्दीDeutschFrançaisPortuguêsРусскийBahasa Indonesia日本語

Related Video

جوجل جعلت TensorFlow.js شيئاً من الماضي (LiteRT.js)8:10

جوجل جعلت TensorFlow.js شيئاً من الماضي (LiteRT.js)

Better Stack

More from the community

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

September 13, 2026

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

September 13, 2026

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

September 13, 2026

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

September 13, 2026

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

September 12, 2026

Apple Won the AI Race

September 12, 2026

Comments (0)

Log in to leave a comment

No posts yet

© 2026 . All rights reserved.

TuBrief
Subscribed Channels
Videos
Community
Log in

ثلاث ثغرات ستواجهها عند الانتقال من TensorFlow.js إلى LiteRT.js

إن تشغيل نماذج الذكاء الاصطناعي في متصفح الويب أمر مثير، لكنك سرعان ما تصطدم بالحدود. فعند معالجة صور عالية الدقة أو بدء عمليات حسابية ثقيلة، تبدأ الشاشة بالتقطع. يستخدم TensorFlow.js (TF.js) واجهة WebGL البرمجية وارتباطات نواة JavaScript، وهو ما يتسبب في حمل زائد (overhead) كبير في مصفوفات الحسابات واسعة النطاق.

على الجانب الآخر، قامت LiteRT.js بترحيل وقت تشغيل C++ الأصلي إلى المتصفح عبر تجميعه باستخدام WebAssembly (Wasm). هذا الهيكل يعالج الاختناقات من جذورها. علاوة على ذلك، أصبحت عملية جلب نماذج PyTorch إلى الويب أبسط. ففي السابق، كان عليك التحويل من PyTorch إلى ONNX، ثم المرور عبر TensorFlow للوصول إلى تنسيق TF.js، مما يؤدي إلى كسر توافقية المعاملات وفقدان الدقة. الآن، يمكنك الاكتفاء بمكتبة واحدة (ai-edge-torch) لاستخراج ملف .tflite قياسي وانتهى الأمر.

إذا كنت لا ترغب في التخلي عن أصولك البرمجية الحالية، يمكنك استخدام حزمة @litertjs/tfjs-interop التي توفرها Google. تعتمد هذه الطريقة على الإبقاء على مسار معالجة البيانات (Pre-processing/Post-processing) الخاص بـ TF.js كما هو، واستبدال جزء تنفيذ تنبؤ النموذج الأساسي بـ LiteRT.js فقط.

حل تعارض NCHWNCHWNCHW و NHWCNHWCNHWC

عادة ما تتطلب نماذج TFLite المحولة من PyTorch بنية تعتمد على القنوات أولاً (Channel-first) وهي NCHWNCHWNCHW (القنوات، الارتفاع، العرض). ومع ذلك، فإن مصفوفة ImageData الخاصة بـ Canvas في المتصفح تكون بتنسيق NHWCNHWCNHWC (الارتفاع، العرض، القنوات) حيث يتم ترتيب البكسلات متتالياً. أنت بحاجة إلى كود معالجة أولية يقوم بهذا التحويل بشكل متزامن دون إثقال كاهل الخيط الرئيسي (Main Thread).

أولاً، نقوم بإنشاء Float32Array من خلال ضرب إجمالي عدد البكسلات في ImageData في 3. بعد ذلك، نحدد إزاحة مواقع القنوات الحمراء والخضراء والزرقاء المسطحة عند 0، وإجمالي عدد البكسلات، وضعف إجمالي عدد البكسلات على التوالي. أخيراً، نقوم بتطبيع قيم البكسل التي تتراوح بين 0 و 255 بالقسمة على 255.0 وتخصيصها لكل إزاحة قناة. هذه هي عملية إعادة ترتيب بيانات الإدخال إلى مخزن تينسور (Tensor) مسطح بتنسيق NCHWNCHWNCHW.

`javascript
/**

  • أداة معالجة أولية لتحويل مخزن ImageData بتنسيق NHWC إلى NCHW Float32Array عالي الأداء
  • @param {ImageData} imageData - بيانات البكسل الأصلية التي تم الحصول عليها من HTML5 Canvas
  • @param {number} width - دقة العرض الأفقية للصورة المطلوبة من قبل النموذج
  • @param {number} height - دقة العرض الرأسية للصورة المطلوبة من قبل النموذج
  • @returns {Float32Array} مخزن تينسور مسطح مُعاد ترتيبه بتنسيق NCHW
    */
    export function preprocessNHWCToNCHW(imageData, width, height) {
    const { data } = imageData;
    const totalPixels = width * height;
    const nchwBuffer = new Float32Array(totalPixels * 3);

const rChannelOffset = 0;
const gChannelOffset = totalPixels;
const bChannelOffset = totalPixels * 2;

for (let i = 0; i < totalPixels; i++) {
const srcIndex = i * 4;
nchwBuffer[rChannelOffset + i] = data[srcIndex] / 255.0;
nchwBuffer[gChannelOffset + i] = data[srcIndex + 1] / 255.0;
nchwBuffer[bChannelOffset + i] = data[srcIndex + 2] / 255.0;
}
return nchwBuffer;
}

`

منع تقطع الشاشة باستخدام Web Workers و Zero-Copy

عند تشغيل التعلم العميق في الواجهة الأمامية، فإن أسوأ سيناريو هو تجمد واجهة المستخدم. لكي يعرض المتصفح رسومًا متحركة سلسة بمعدل 60 إطاراً في الثانية، يجب أن يعالج حلقة الأحداث (Event Loop) المهام المتزامنة في أقل من 16.6 مللي ثانية. لكن عمليات التينسور تعيق بسهولة حلقة العرض الرئيسية أحادية الخيط.

تتميز LiteRT.js بسرعة تنفيذ أساسية أسرع بحوالي 3 مرات من الأدوات المستندة إلى JavaScript التقليدية. وعند إضافة أجهزة تسريع مثل WebGPU أو WebNN، تصل السرعة إلى ما بين 5 إلى 60 ضعفاً مقارنة بوضع CPU. تتطلب واجهة WebNN، التي تستخدم NPU مخصصاً، تفعيل ميزة تكامل وعود JavaScript (JSPI) لربط مجدول نواة WebAssembly المتزامن بحلقة التحكم في الأجهزة غير المتزامنة للمتصفح. للاستفادة من موارد التسريع هذه مع الحفاظ على استجابة الخيط الرئيسي، يجب عزل تهيئة المكتبة ومسار الاستدلال بالكامل داخل Web Worker.

في هذه الحالة، إذا قمت بتمرير المخازن المؤقتة (Buffers) كما هي أثناء تبادل البيانات بين الخيوط، فسيحدث نسخ داخلي للذاكرة مما يؤدي إلى تراكم الحمل الزائد في الذاكرة (CPU و Heap). يجب استخدام الكائنات القابلة للنقل (Transferable Objects) لنقل ملكية عنوان الذاكرة الفيزيائي نفسه للقضاء على زمن الوصول. يتم إلغاء صلاحية المخزن المؤقت الذي نُقلت ملكيته فوراً في سياق المرسل، مما يضمن أمان العمليات بين الخيوط.

`javascript
// litert-worker.js - وحدة Web Worker مخصصة لعمليات استدلال الخلفية
import { loadLiteRt, loadAndCompile, Tensor } from '@litertjs/core';

let compiledModel = null;
let isLoaded = false;

self.onmessage = async (event) => {
const { type, payload } = event.data;

switch (type) {
case 'LOAD_MODEL':
try {
await loadLiteRt(payload.wasmDirectory, { jspi: payload.enableJspi || false });
compiledModel = await loadAndCompile(payload.modelUrl, {
accelerator: payload.accelerator || 'webgpu'
});
isLoaded = true;
self.postMessage({ type: 'MODEL_READY' });
} catch (err) {
self.postMessage({ type: 'ERROR', error: Initialization failed: ${err.message} });
}
break;

case 'RUN_INFERENCE':
  if (!isLoaded || !compiledModel) {
    self.postMessage({ type: 'ERROR', error: 'Model has not been loaded' });
    return;
  }
  try {
    const rawInputData = payload.bufferData;
    const inputShape = payload.shape;
    const inputTensor = new Tensor(rawInputData, inputShape);
    const results = await compiledModel.run(inputTensor);
    const cpuOutputTensor = await results[0].moveTo('wasm');
    const outputBuffer = cpuOutputTensor.toTypedArray();

    inputTensor.delete();
    cpuOutputTensor.delete();
    results[0].delete();

    self.postMessage(
      {
        type: 'INFERENCE_COMPLETE',
        payload: {
          data: outputBuffer,
          shape: results[0].shape
        }
      },
      [outputBuffer.buffer]
    );
  } catch (err) {
    self.postMessage({ type: 'ERROR', error: `Inference failed: ${err.message}` });
  }
  break;

default:
  self.postMessage({ type: 'UNKNOWN_OP' });

}
};

`

`javascript
// litert-bridge.js - فئة منسق الذكاء الاصطناعي للخيط الرئيسي
export class LiteRtBridge {
constructor(workerPath) {
this.worker = new Worker(workerPath);
this.promiseMap = new Map();
this.tokenCounter = 0;

this.worker.onmessage = (event) => {
  const { type, payload, error } = event.data;

  if (type === 'MODEL_READY') {
    if (this.initResolve) this.initResolve();
  } else if (type === 'INFERENCE_COMPLETE') {
    const currentToken = this.tokenCounter;
    const promiseHandler = this.promiseMap.get(currentToken);
    if (promiseHandler) {
      promiseHandler.resolve(payload);
      this.promiseMap.delete(currentToken);
    }
  } else if (type === 'ERROR') {
    const currentToken = this.tokenCounter;
    const promiseHandler = this.promiseMap.get(currentToken);
    if (promiseHandler) {
      promiseHandler.reject(new Error(error));
      this.promiseMap.delete(currentToken);
    } else if (this.initReject) {
      this.initReject(new Error(error));
    }
  }
};

}

bootstrap(wasmDirectory, modelUrl, accelerator = 'webgpu') {
return new Promise((resolve, reject) => {
this.initResolve = resolve;
this.initReject = reject;
this.worker.postMessage({
type: 'LOAD_MODEL',
payload: { wasmDirectory, modelUrl, accelerator, enableJspi: true }
});
});
}

execute(inputFloat32Array, inputShape) {
return new Promise((resolve, reject) => {
this.tokenCounter++;
this.promiseMap.set(this.tokenCounter, { resolve, reject });
this.worker.postMessage(
{
type: 'RUN_INFERENCE',
payload: {
bufferData: inputFloat32Array,
shape: inputShape
}
},
[inputFloat32Array.buffer]
);
});
}
}

`

لا تثق بجمع القمامة التلقائي

عند استخدام TF.js، كان نمط tf.tidy() هو المعيار لتنظيف التينسورات ضمن نطاق الاستدعاء المتزامن. ولكن مع تداخل الكود غير المتزامن أو الوعود (Promises)، كانت تنشأ أخطاء تؤدي إلى تحرير التينسورات خارج النطاق قبل انتهاء المهمة أو نسيان تجميعها.

تعد LiteRT.js أكثر صرامة؛ فهي ليست خاضعة لعملية جمع القمامة (GC) الخاصة بمحرك المتصفح. لا يمكن لمحركات JavaScript مثل V8 تتبع حالة الذاكرة (Heap) لمساحة ذاكرة WebAssembly الخطية الافتراضية ومخازن WebGPU المؤقتة. إذا لم تقم باستدعاء .delete() بشكل صريح على مثيل التينسور الذي انتهى عمله، فإن ذاكرة المتصفح ستتراكم إلى ما لا نهاية. في الخدمات التي تبث إطارات فيديو عالية الدقة عشرات المرات في الثانية، ستموت علامة التبويب في غضون دقائق.

راحة بالك تكمن في إنشاء فئة تعقب النطاق (Scope Tracker) تقوم بتسجيل عمر التينسورات التي تم إنشاؤها عبر مسار الاستدلال غير المتزامن وتضمن تدميرها بشكل جماعي.

`javascript
/**

  • مدير نطاق ذاكرة غير متزامن يقوم بتتبع وتدمير تينسورات WebAssembly يدوياً وبشكل مؤكد
    */
    export class LiteRtScopeTracker {
    constructor() {
    this.trackList = new Set();
    }

/**

  • ضم التينسور الذي تم إنشاؤه أو نقله إلى قائمة إدارة دورة الحياة
  • @param {Tensor} tensor - تينسور LiteRT.js المراد تعقبه وتدميره
  • @returns {Tensor} إعادة كائن التينسور لدعم كتابة الكود المضمن
    */
    register(tensor) {
    if (tensor && typeof tensor.delete === 'function') {
    this.trackList.add(tensor);
    }
    return tensor;
    }

/**

  • فرض هيكل إدارة مسار تينسور آمن داخل كتلة التنفيذ غير المتزامنة
  • @param {Function} asyncCallable - دالة منطق عمل الاستدلال غير المتزامن
  • @returns {Promise<*>} نتيجة البيانات الخام النهائية التي تعيدها كتلة التنفيذ
    */
    async enforceScope(asyncCallable) {
    try {
    const outputResult = await asyncCallable(this);
    if (Array.isArray(outputResult)) {
    outputResult.forEach((item) => this.trackList.delete(item));
    } else {
    this.trackList.delete(outputResult);
    }
    return outputResult;
    } finally {
    this.disposeAll();
    }
    }

/**

  • إلغاء عزل جميع تينسورات TFLite الأصلية الموجودة في مساحة Wasm والتي لا تزال مرتبطة بالكائنات الخاضعة للإدارة
    */
    disposeAll() {
    for (const tensor of this.trackList) {
    try {
    tensor.delete();
    } catch (err) {
    console.error('حدث خطأ أثناء تنظيف ذاكرة تينسور Wasm الأصلية:', err);
    }
    }
    this.trackList.clear();
    }
    }

`

`javascript
// مثال تنفيذ استدلال ذكاء اصطناعي متعدد غير متزامن آمن وقوي باستخدام متعقب نطاق الذاكرة
export async function runRobustVisionInference(rawPixelArray, compiledModel) {
const scopeTracker = new LiteRtScopeTracker();
try {
return await scopeTracker.enforceScope(async (scope) => {
const inputTensor = scope.register(new Tensor(rawPixelArray, [1, 3, 224, 224]));
const predictionResults = await compiledModel.run(inputTensor);
predictionResults.forEach((tensor) => scope.register(tensor));

  const firstOutputTensor = predictionResults[0];
  const wasmTransferTensor = scope.register(await firstOutputTensor.moveTo('wasm'));
  const targetJsArray = wasmTransferTensor.toTypedArray();

  return targetJsArray;
});

} catch (err) {
console.error('حدث عطل فادح أثناء تنفيذ مسار النموذج:', err);
throw err;
}
}

`

التحميل المشروط لملفات Wasm الثقيلة

لا يمكن تجاهل مشكلة زيادة حجم تحميل الموارد. لضمان عمل "هز الشجرة" (Tree Shaking) في حزم البناء (Bundlers)، يجب إزالة المراجع الثابتة بنمط CommonJS وكتابة الكود المصدري بناءً على بناء جملة وحدات ES6 (import/export). يجب أيضاً التأكد من إعداد sideEffects: false في أداة البناء لجعل الحزمة أخف وزناً.

تختار مكتبة @litertjs/core، وهي وقت تشغيل LiteRT.js الأساسي، التحميل الانتقائي لثلاث مجموعات من أنوية WebAssembly بناءً على أداء الجهاز. في المتصفحات الحديثة مثل Chrome أو Edge، يتم اختيار الوحدة التي تدعم تعدد الخيوط وSIMD (litert_wasm_simd.wasm)، بينما في البيئات القديمة مثل Safari يتم تحميل وحدة الاحتياط الأساسية (litert_wasm.wasm). إذا فشل تجميع GPU، يتم تفعيل وقت تشغيل XNNPACK كجهاز مساعد لنقل معاملات الأجهزة بالكامل إلى بيئة Wasm الآمنة (Sandbox) الخاصة بـ CPU.

لمنع تأخير التحميل الأولي، يجب التحقق من المُسرع المناسب للمواصفات وتحميل الوحدة ديناميكياً.

`javascript
// litert-loader.js - محرك استكشاف الجهاز في وقت التشغيل والربط الديناميكي للمسرعات
export async function bootstrapHighPerformanceInferenceEngine() {
const supportsWebGpu = 'gpu' in navigator;
let chosenAccelerator = 'wasm';

if (supportsWebGpu) {
try {
const gpuAdapter = await navigator.gpu.requestAdapter();
if (gpuAdapter) {
const info = await gpuAdapter.requestDevice();
if (info) {
chosenAccelerator = 'webgpu';
}
}
} catch (e) {
console.warn("فشل فحص ملف تعريف GPU، يتم حل سلسلة التنفيذ إلى WASM كخيار احتياطي.");
}
}

const { loadLiteRt, loadAndCompile } = await import('@litertjs/core');
const cdnWasmHostPath = 'https://cdn.jsdelivr.net/npm/@litertjs/core/wasm/';

await loadLiteRt(cdnWasmHostPath, {
jspi: chosenAccelerator === 'webnn'
});

return {
loadAndCompile,
chosenAccelerator
};
}

`

اجعل نقل ملكية الذاكرة عبر Web Worker وتدمير الكائنات في نطاق Wasm بشكل صريح ضمن ركائز تصميمك. عندما تبدأ في التحكم في تدفق البيانات بشكل مباشر، يمكنك طرح خدمات الذكاء الاصطناعي على الجهاز (On-device AI) في بيئة الإنتاج دون القلق بشأن انهيار المتصفح.