تشترك معظم النماذج اللغوية في أمر واحد، وهو أنّها
كبيرة الحجم بالنسبة إلى مصدر
يتم نقله عبر الإنترنت. يبلغ حجم أصغر نموذج لرصد العناصر في MediaPipe
(SSD MobileNetV2 float16
) 5.6 ميغابايت،
بينما يبلغ حجم أكبر نموذج حوالي 25 ميغابايت.
يبلغ حجم النموذج اللغوي الكبير (LLM) المفتوح المصدر
gemma-2b-it-gpu-int4.bin
1.35 غيغابايت، وهو حجم صغير جدًا بالنسبة إلى نموذج لغوي كبير.
يمكن أن تكون نماذج الذكاء الاصطناعي التوليدي ضخمة. لهذا السبب، يتم حاليًا استخدام الذكاء الاصطناعي بشكل كبير على السحابة الإلكترونية. تتزايد التطبيقات التي تشغّل نماذج محسّنة بشكل كبير مباشرةً على الجهاز. على الرغم من توفّر عروض توضيحية للنماذج اللغوية الكبيرة التي تعمل في المتصفح، إليك بعض الأمثلة على النماذج الأخرى التي تعمل في المتصفح:
- يستخدم Adobe Photoshop إصدارًا من نموذج
Conv2D
على الجهاز فقط لتوفير أداة ذكية لاختيار العناصر. - يستخدم Google Meet إصدارًا محسّنًا من
MobileNetV3-small
النموذج لتقسيم الأشخاص من أجل ميزة تمويه الخلفية. - تستخدم Tokopedia نموذج
MediaPipeFaceDetector-TFJS
لرصد الوجوه في الوقت الفعلي من أجل منع عمليات الاشتراك غير الصالحة في خدمتها. - يتيح Google Colab للمستخدمين استخدام نماذج من القرص الصلب في دفاتر ملاحظات Colab.
لإطلاق تطبيقاتك بشكل أسرع في المستقبل، عليك تخزين بيانات النموذج مؤقتًا على الجهاز بشكل صريح، بدلاً من الاعتماد على ذاكرة التخزين المؤقت الضمنية للمتصفح عبر HTTP.
على الرغم من أنّ هذا الدليل يستخدم gemma-2b-it-gpu-int4.bin model
لإنشاء برنامج دردشة آلي، يمكن تعميم هذا الأسلوب ليناسب نماذج أخرى وحالات استخدام أخرى على الجهاز. الطريقة الأكثر شيوعًا لربط تطبيق بنموذج هي عرض النموذج مع بقية موارد التطبيق. من الضروري تحسين عرض الإعلانات.
ضبط عناوين ذاكرة التخزين المؤقت الصحيحة
إذا كنت تعرض نماذج الذكاء الاصطناعي من خادمك، من المهم ضبط عنوان Cache-Control
الصحيح. يوضّح المثال التالي إعدادًا تلقائيًا جيدًا يمكنك الاستفادة منه لتلبية احتياجات تطبيقك.
Cache-Control: public, max-age=31536000, immutable
كل إصدار من نموذج الذكاء الاصطناعي هو مورد ثابت. يجب منح المحتوى الذي لا يتغيّر مطلقًا قيمة max-age
طويلة مع إيقاف التخزين المؤقت في عنوان URL الخاص بالطلب. إذا كنت بحاجة إلى تعديل النموذج، عليك
منحه عنوان URL جديدًا.
عندما يعيد المستخدم تحميل الصفحة، يرسل العميل طلب إعادة التحقّق، حتى إذا كان الخادم يعرف أنّ المحتوى ثابت. تشير توجيهة
immutable
بشكل صريح إلى أنّ إعادة التحقّق غير ضرورية، لأنّ المحتوى لن يتغيّر. لا تتوفّر إمكانية استخدام التوجيه immutable
على نطاق واسع في المتصفحات والخوادم الوسيطة لذاكرة التخزين المؤقت أو خوادم البروكسي، ولكن يمكنك ضمان تحقيق أقصى توافق من خلال دمجه مع التوجيه max-age
الذي يمكن فهمه على نطاق واسع. يشير توجيه الاستجابة public
إلى أنّه يمكن تخزين الاستجابة في ذاكرة تخزين مؤقت مشتركة.

Cache-Control
الإنتاج التي ترسلها Hugging Face عند طلب نموذج ذكاء اصطناعي.
(المصدر)
تخزين نماذج الذكاء الاصطناعي مؤقتًا من جهة العميل
عند عرض نموذج الذكاء الاصطناعي، من المهم تخزين النموذج مؤقتًا بشكل صريح في المتصفّح. يضمن ذلك توفُّر بيانات النموذج على الفور بعد أن يعيد المستخدم تحميل التطبيق.
وهناك عدد من التقنيات التي يمكنك استخدامها لتحقيق ذلك. بالنسبة إلى نماذج الرموز التالية، افترِض أنّ كل ملف نموذج مخزّن في كائن Blob
باسم blob
في الذاكرة.
لفهم الأداء، يتم شرح كل نموذج للرمز باستخدام الطريقتَين
performance.mark()
وperformance.measure()
. تعتمد هذه المقاييس على الجهاز ولا يمكن تعميمها.

يمكنك اختيار استخدام إحدى واجهات برمجة التطبيقات التالية لتخزين نماذج الذكاء الاصطناعي مؤقتًا في المتصفح: Cache API وOrigin Private File System API وIndexedDB API. ننصح بشكل عام باستخدام Cache API، ولكن يناقش هذا الدليل مزايا وعيوب جميع الخيارات.
Cache API
توفّر واجهة برمجة التطبيقات Cache API مساحة تخزين ثابتة لأزواج عناصر Request
وResponse
التي يتم تخزينها مؤقتًا في ذاكرة طويلة الأمد. على الرغم من أنّ هذه الواجهة محدّدة في مواصفات Service Workers، يمكنك استخدامها من سلسلة التعليمات الرئيسية أو عامل عادي. لاستخدامها خارج سياق عامل الخدمة، استدعِ طريقة Cache.put()
باستخدام كائن Response
اصطناعي، مقترنًا بعنوان URL اصطناعي بدلاً من كائن Request
.
يفترض هذا الدليل استخدام blob
في الذاكرة. استخدِم عنوان URL وهميًا كمفتاح ذاكرة التخزين المؤقت وResponse
اصطناعيًا استنادًا إلى blob
. إذا أردت تنزيل النموذج مباشرةً، عليك استخدام Response
الذي ستحصل عليه من خلال تقديم طلب fetch()
.
على سبيل المثال، إليك كيفية تخزين ملف نموذج واستعادته باستخدام Cache API.
const storeFileInSWCache = async (blob) => {
try {
performance.mark('start-sw-cache-cache');
const modelCache = await caches.open('models');
await modelCache.put('model.bin', new Response(blob));
performance.mark('end-sw-cache-cache');
const mark = performance.measure(
'sw-cache-cache',
'start-sw-cache-cache',
'end-sw-cache-cache'
);
console.log('Model file cached in sw-cache.', mark.name, mark.duration.toFixed(2));
} catch (err) {
console.error(err.name, err.message);
}
};
const restoreFileFromSWCache = async () => {
try {
performance.mark('start-sw-cache-restore');
const modelCache = await caches.open('models');
const response = await modelCache.match('model.bin');
if (!response) {
throw new Error(`File model.bin not found in sw-cache.`);
}
const file = await response.blob();
performance.mark('end-sw-cache-restore');
const mark = performance.measure(
'sw-cache-restore',
'start-sw-cache-restore',
'end-sw-cache-restore'
);
console.log(mark.name, mark.duration.toFixed(2));
console.log('Cached model file found in sw-cache.');
return file;
} catch (err) {
throw err;
}
};
Origin Private File System API
نظام الملفات الخاص بالمصدر (OPFS) هو معيار حديث نسبيًا لنقطة نهاية التخزين. وهي خاصة بمصدر الصفحة، وبالتالي لا تظهر للمستخدم، على عكس نظام الملفات العادي. ويتيح الوصول إلى ملف خاص تم تحسينه بشكل كبير من أجل الأداء، كما يتيح إمكانية الكتابة في محتواه.
على سبيل المثال، إليك كيفية تخزين ملف نموذج واستعادته في نظام OPFS.
const storeFileInOPFS = async (blob) => {
try {
performance.mark('start-opfs-cache');
const root = await navigator.storage.getDirectory();
const handle = await root.getFileHandle('model.bin', { create: true });
const writable = await handle.createWritable();
await blob.stream().pipeTo(writable);
performance.mark('end-opfs-cache');
const mark = performance.measure(
'opfs-cache',
'start-opfs-cache',
'end-opfs-cache'
);
console.log('Model file cached in OPFS.', mark.name, mark.duration.toFixed(2));
} catch (err) {
console.error(err.name, err.message);
}
};
const restoreFileFromOPFS = async () => {
try {
performance.mark('start-opfs-restore');
const root = await navigator.storage.getDirectory();
const handle = await root.getFileHandle('model.bin');
const file = await handle.getFile();
performance.mark('end-opfs-restore');
const mark = performance.measure(
'opfs-restore',
'start-opfs-restore',
'end-opfs-restore'
);
console.log('Cached model file found in OPFS.', mark.name, mark.duration.toFixed(2));
return file;
} catch (err) {
throw err;
}
};
IndexedDB API
IndexedDB هو معيار راسخ لتخزين بيانات عشوائية بطريقة ثابتة في المتصفّح. تشتهر IndexedDB بواجهة برمجة التطبيقات المعقّدة إلى حد ما، ولكن باستخدام مكتبة التفاف مثل idb-keyval، يمكنك التعامل مع IndexedDB كمتجر كلاسيكي لتخزين البيانات على شكل أزواج من المفاتيح والقيم.
على سبيل المثال:
import { get, set } from 'https://cdn.jsdelivr.net/npm/idb-keyval@latest/+esm';
const storeFileInIDB = async (blob) => {
try {
performance.mark('start-idb-cache');
await set('model.bin', blob);
performance.mark('end-idb-cache');
const mark = performance.measure(
'idb-cache',
'start-idb-cache',
'end-idb-cache'
);
console.log('Model file cached in IDB.', mark.name, mark.duration.toFixed(2));
} catch (err) {
console.error(err.name, err.message);
}
};
const restoreFileFromIDB = async () => {
try {
performance.mark('start-idb-restore');
const file = await get('model.bin');
if (!file) {
throw new Error('File model.bin not found in IDB.');
}
performance.mark('end-idb-restore');
const mark = performance.measure(
'idb-restore',
'start-idb-restore',
'end-idb-restore'
);
console.log('Cached model file found in IDB.', mark.name, mark.duration.toFixed(2));
return file;
} catch (err) {
throw err;
}
};
وضع علامة "مستمر" على مساحة التخزين
استخدِم الدالة navigator.storage.persist()
في نهاية أي من طرق التخزين المؤقت هذه لطلب الإذن باستخدام
مساحة التخزين الثابتة. تعرض هذه الطريقة وعدًا يتم تنفيذه بالقيمة true
في حال تم منح الإذن، والقيمة false
في حال عدم منحه. قد يلتزم المتصفّح بالطلب أو لا يلتزم به،
وذلك حسب القواعد الخاصة بالمتصفّح.
if ('storage' in navigator && 'persist' in navigator.storage) {
try {
const persistent = await navigator.storage.persist();
if (persistent) {
console.log("Storage will not be cleared except by explicit user action.");
return;
}
console.log("Storage may be cleared under storage pressure.");
} catch (err) {
console.error(err.name, err.message);
}
}
حالة خاصة: استخدام نموذج على قرص صلب
يمكنك الرجوع إلى نماذج الذكاء الاصطناعي مباشرةً من القرص الصلب للمستخدم كبديل لمساحة التخزين في المتصفّح. يمكن أن تساعد هذه التقنية التطبيقات التي تركّز على البحث في عرض مدى جدوى تشغيل نماذج معيّنة في المتصفح، أو تتيح للفنانين استخدام نماذج تم تدريبها ذاتيًا في تطبيقات إبداعية متخصصة.
File System Access API
باستخدام File System Access API، يمكنك فتح ملفات من القرص الصلب والحصول على FileSystemFileHandle يمكنك الاحتفاظ به في IndexedDB.
باستخدام هذا النمط، يحتاج المستخدم إلى منح إذن الوصول إلى ملف النموذج مرة واحدة فقط. بفضل الأذونات الثابتة،
يمكن للمستخدم اختيار منح إذن الوصول إلى الملف بشكل دائم. بعد إعادة تحميل التطبيق وتنفيذ إجراء مطلوب من المستخدم، مثل النقر بالماوس، يمكن استعادة FileSystemFileHandle
من IndexedDB مع إمكانية الوصول إلى الملف على القرص الصلب.
يتم طلب أذونات الوصول إلى الملفات والاستعلام عنها عند الحاجة، ما يسهّل عمليات إعادة التحميل المستقبلية. يوضّح المثال التالي كيفية الحصول على معرّف لملف من القرص الصلب، ثم تخزين المعرّف واستعادته.
import { fileOpen } from 'https://cdn.jsdelivr.net/npm/browser-fs-access@latest/dist/index.modern.js';
import { get, set } from 'https://cdn.jsdelivr.net/npm/idb-keyval@latest/+esm';
button.addEventListener('click', async () => {
try {
const file = await fileOpen({
extensions: ['.bin'],
mimeTypes: ['application/octet-stream'],
description: 'AI model files',
});
if (file.handle) {
// It's an asynchronous method, but no need to await it.
storeFileHandleInIDB(file.handle);
}
return file;
} catch (err) {
if (err.name !== 'AbortError') {
console.error(err.name, err.message);
}
}
});
const storeFileHandleInIDB = async (handle) => {
try {
performance.mark('start-file-handle-cache');
await set('model.bin.handle', handle);
performance.mark('end-file-handle-cache');
const mark = performance.measure(
'file-handle-cache',
'start-file-handle-cache',
'end-file-handle-cache'
);
console.log('Model file handle cached in IDB.', mark.name, mark.duration.toFixed(2));
} catch (err) {
console.error(err.name, err.message);
}
};
const restoreFileFromFileHandle = async () => {
try {
performance.mark('start-file-handle-restore');
const handle = await get('model.bin.handle');
if (!handle) {
throw new Error('File handle model.bin.handle not found in IDB.');
}
if ((await handle.queryPermission()) !== 'granted') {
const decision = await handle.requestPermission();
if (decision === 'denied' || decision === 'prompt') {
throw new Error('Access to file model.bin.handle not granted.');
}
}
const file = await handle.getFile();
performance.mark('end-file-handle-restore');
const mark = performance.measure(
'file-handle-restore',
'start-file-handle-restore',
'end-file-handle-restore'
);
console.log('Cached model file handle found in IDB.', mark.name, mark.duration.toFixed(2));
return file;
} catch (err) {
throw err;
}
};
ولا يستبعد بعضها البعض. قد تحدث حالة يتم فيها تخزين نموذج مؤقتًا بشكل صريح في المتصفّح واستخدام نموذج من القرص الصلب للمستخدم.
عرض توضيحي
يمكنك الاطّلاع على جميع طرق التخزين الثلاث العادية وطريقة القرص الصلب المستخدَمة في العرض التوضيحي لنموذج اللغة الكبير من MediaPipe.
مكافأة: تنزيل ملف كبير على شكل أجزاء
إذا كنت بحاجة إلى تنزيل نموذج كبير للذكاء الاصطناعي من الإنترنت، يمكنك تقسيم عملية التنزيل إلى أجزاء منفصلة، ثم تجميعها مرة أخرى على الجهاز.
توفر الحزمة fetch-in-chunks
دالة مساعدة يمكنك استخدامها في الرمز البرمجي. ما عليك سوى تمرير url
إليه. إنّ maxParallelRequests
(القيمة التلقائية: 6) وchunkSize
(القيمة التلقائية: حجم الملف المطلوب تنزيله مقسومًا على maxParallelRequests
) والدالة progressCallback
(التي تعرض downloadedBytes
وإجمالي fileSize
) وsignal
لإشارة AbortSignal
كلها عناصر اختيارية.
import fetchInChunks from 'fetch-in-chunks';
async function downloadFileWithProgress() {
try {
const blob = await fetchInChunks('https://example.com/largefile.zip', {
progressCallback: (downloaded, total) => {
console.log(`Downloaded ${((downloaded / total) * 100).toFixed(2)}%`);
},
});
return blob;
} catch (error) {
console.error('Error fetching file:', error);
}
}
downloadFileWithProgress();
اختيار الطريقة المناسبة لك
استعرض هذا الدليل طرقًا مختلفة لتخزين نماذج الذكاء الاصطناعي مؤقتًا في المتصفّح بشكل فعّال، وهي مهمة ضرورية لتحسين تجربة المستخدم وأداء تطبيقك. ينصح فريق التخزين في Chrome باستخدام Cache API لتحقيق أفضل أداء، وذلك لضمان الوصول السريع إلى نماذج الذكاء الاصطناعي وتقليل أوقات التحميل وتحسين الاستجابة.
يُعدّ نظام الملفات الخاص بالمصدر (OPFS) وIndexedDB خيارَين أقل قابلية للاستخدام. يجب أن تعمل واجهتا برمجة التطبيقات OPFS وIndexedDB على تسلسل البيانات قبل أن يتم تخزينها. تحتاج IndexedDB أيضًا إلى إلغاء تسلسل البيانات عند استرجاعها، ما يجعلها المكان الأسوأ لتخزين النماذج الكبيرة.
بالنسبة إلى التطبيقات المتخصصة، تتيح واجهة برمجة التطبيقات File System Access API الوصول المباشر إلى الملفات على جهاز المستخدم، ما يجعلها مثالية للمستخدمين الذين يديرون نماذج الذكاء الاصطناعي الخاصة بهم.
إذا كنت بحاجة إلى تأمين نموذج الذكاء الاصطناعي، احتفِظ به على الخادم. بعد تخزين البيانات على الجهاز، يصبح من السهل استخراجها من كلّ من ذاكرة التخزين المؤقت وIndexedDB باستخدام "أدوات المطوّرين" أو إضافة "أدوات المطوّرين" في OFPS. تتساوى واجهات برمجة التطبيقات الخاصة بالتخزين هذه في مستوى الأمان. قد تميل إلى تخزين نسخة مشفّرة من النموذج، ولكن عليك بعد ذلك الحصول على مفتاح فك التشفير وإرساله إلى العميل، ما قد يؤدي إلى اعتراض المفتاح. وهذا يعني أنّ محاولة الجهات السيئة سرقة نموذجك ستكون أصعب قليلاً، ولكنها ليست مستحيلة.
ننصحك باختيار استراتيجية تخزين مؤقت تتوافق مع متطلبات تطبيقك وسلوك الجمهور المستهدَف وخصائص نماذج الذكاء الاصطناعي المستخدَمة. يضمن ذلك أن تكون تطبيقاتك سريعة الاستجابة وقوية في ظل ظروف الشبكة وقيود النظام المختلفة.
الإقرارات
راجع هذا المستند كل من جوشوا بيل ورايلي غرانت وإيفان ستاد وناثان ميموت وأوستن سوليفان وإتيان نويل وأندريه باندارا وألكسندرا كليبر وفرانسوا بوفورت وبول كينلان وراشيل أندرو.