ज़्यादातर एआई मॉडल में एक बात कॉमन होती है: इंटरनेट पर ट्रांसफ़र किए जाने वाले संसाधन के लिए, वे काफ़ी बड़े होते हैं. MediaPipe के ऑब्जेक्ट डिटेक्शन मॉडल (SSD MobileNetV2 float16
) का सबसे छोटा साइज़ 5.6 एमबी है और सबसे बड़ा साइज़ करीब 25 एमबी है.
ओपन-सोर्स एलएलएम का साइज़ 1.35 जीबी है, जो एलएलएम के लिए बहुत कम माना जाता है.gemma-2b-it-gpu-int4.bin
जनरेटिव एआई मॉडल बहुत बड़े हो सकते हैं. यही वजह है कि एआई का ज़्यादातर इस्तेमाल, क्लाउड में किया जाता है. ज़्यादा से ज़्यादा ऐप्लिकेशन, सीधे डिवाइस पर बेहतर तरीके से ऑप्टिमाइज़ किए गए मॉडल चला रहे हैं. ब्राउज़र में चल रहे एलएलएम के डेमो मौजूद हैं. हालांकि, यहां ब्राउज़र में चल रहे अन्य मॉडल के कुछ प्रोडक्शन-ग्रेड उदाहरण दिए गए हैं:
- ऑब्जेक्ट चुनने वाले अपने बेहतर टूल के लिए, Adobe Photoshop डिवाइस पर
Conv2D
मॉडल का एक वैरिएंट चलाता है. - Google Meet, बैकग्राउंड धुंधला करने की सुविधा के लिए, व्यक्ति के सेगमेंटेशन के लिए
MobileNetV3-small
मॉडल का ऑप्टिमाइज़ किया गया वर्शन चलाता है. - Tokopedia, अपनी सेवा के लिए अमान्य साइनअप को रोकने के लिए, रीयल-टाइम में चेहरे की पहचान करने के लिए
MediaPipeFaceDetector-TFJS
मॉडल का इस्तेमाल करता है. - Google Colab की मदद से, उपयोगकर्ता अपनी हार्ड डिस्क में मौजूद मॉडल का इस्तेमाल, Colab नोटबुक में कर सकते हैं.
आने वाले समय में अपने ऐप्लिकेशन को तेज़ी से लॉन्च करने के लिए, आपको डिवाइस पर मॉडल डेटा को साफ़ तौर पर कैश मेमोरी में सेव करना चाहिए. इसके लिए, एचटीटीपी ब्राउज़र के कैश मेमोरी पर निर्भर न रहें.
इस गाइड में चैटबॉट बनाने के लिए gemma-2b-it-gpu-int4.bin model
का इस्तेमाल किया गया है. हालांकि, इस तरीके को डिवाइस पर मौजूद अन्य मॉडल और इस्तेमाल के अन्य उदाहरणों के हिसाब से बनाया जा सकता है. किसी ऐप्लिकेशन को मॉडल से कनेक्ट करने का सबसे आम तरीका यह है कि ऐप्लिकेशन के बाकी संसाधनों के साथ-साथ मॉडल को भी दिखाया जाए. डिलीवरी को ऑप्टिमाइज़ करना ज़रूरी है.
सही कैश मेमोरी हेडर कॉन्फ़िगर करना
अगर आपके सर्वर से एआई मॉडल दिखाए जाते हैं, तो सही Cache-Control
हेडर कॉन्फ़िगर करना ज़रूरी है. यहां दिए गए उदाहरण में एक अच्छी डिफ़ॉल्ट सेटिंग दिखाई गई है. इसे अपने ऐप्लिकेशन की ज़रूरतों के हिसाब से बनाया जा सकता है.
Cache-Control: public, max-age=31536000, immutable
एआई मॉडल का रिलीज़ किया गया हर वर्शन, स्टैटिक रिसॉर्स होता है. ऐसे कॉन्टेंट के लिए, अनुरोध यूआरएल में max-age
का लंबा वर्शन और कैश मेमोरी को हटाने की सुविधा का इस्तेमाल करें जो कभी नहीं बदलता. अगर आपको मॉडल अपडेट करना है, तो आपको उसे एक नया यूआरएल देना होगा.
जब उपयोगकर्ता पेज को फिर से लोड करता है, तो क्लाइंट फिर से पुष्टि करने का अनुरोध भेजता है. भले ही, सर्वर को पता हो कि कॉन्टेंट स्थिर है. immutable
निर्देश से साफ़ तौर पर पता चलता है कि फिर से पुष्टि करने की ज़रूरत नहीं है, क्योंकि कॉन्टेंट में कोई बदलाव नहीं होगा. ब्राउज़र और इंटरमीडियरी कैश या प्रॉक्सी सर्वर, immutable
डायरेक्टिव के साथ ज़्यादातर काम नहीं करते. हालांकि, इसे max-age
डायरेक्टिव के साथ जोड़कर, ज़्यादा से ज़्यादा काम करने की सुविधा मिल सकती है. max-age
डायरेक्टिव को सभी ब्राउज़र और सर्वर समझते हैं. public
रिस्पॉन्स डायरेक्टिव से पता चलता है कि रिस्पॉन्स को शेयर किए गए कैश मेमोरी में सेव किया जा सकता है.
क्लाइंट-साइड पर एआई मॉडल कैश मेमोरी में सेव करना
एआई मॉडल दिखाते समय, यह ज़रूरी है कि मॉडल को ब्राउज़र में साफ़ तौर पर कैश मेमोरी में सेव किया जाए. इससे यह पक्का होता है कि उपयोगकर्ता के ऐप्लिकेशन को फिर से लोड करने के बाद, मॉडल का डेटा तुरंत उपलब्ध हो.
ऐसा करने के लिए, कई तकनीकों का इस्तेमाल किया जा सकता है. यहां दिए गए कोड सैंपल के लिए, मान लें कि हर मॉडल फ़ाइल, मेमोरी में 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 स्पेसिफ़िकेशन में बताया गया है, लेकिन इस एपीआई का इस्तेमाल मुख्य थ्रेड या किसी सामान्य वर्कर्स से किया जा सकता है. इसे सेवा वर्कर कॉन्टेक्स्ट के बाहर इस्तेमाल करने के लिए, Request
ऑब्जेक्ट के बजाय सिंथेटिक यूआरएल के साथ जोड़े गए सिंथेटिक Response
ऑब्जेक्ट के साथ, Cache.put()
तरीके को कॉल करें.
इस गाइड में, मेमोरी में सेव blob
का इस्तेमाल किया गया है. कैश मेमोरी कुंजी के तौर पर किसी फ़र्ज़ी यूआरएल और blob
के आधार पर बनाए गए Response
का इस्तेमाल करें. अगर आपको मॉडल को सीधे डाउनलोड करना है, तो आपको fetch()
अनुरोध करने पर मिलने वाले Response
का इस्तेमाल करना होगा.
उदाहरण के लिए, 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
Origin Private File System (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, ब्राउज़र में किसी भी तरह के डेटा को लगातार स्टोर करने के लिए एक जाना-पहचाना स्टैंडर्ड है. यह अपने कुछ हद तक जटिल एपीआई के लिए बदनाम है. हालांकि, 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 की मदद से, हार्ड डिस्क से फ़ाइलें खोली जा सकती हैं और 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 LLM डेमो में, केस स्टोरेज के तीनों सामान्य तरीके और हार्ड डिस्क का तरीका देखा जा सकता है.
बोनस: बड़ी फ़ाइल को अलग-अलग हिस्सों में डाउनलोड करना
अगर आपको इंटरनेट से कोई बड़ा एआई मॉडल डाउनलोड करना है, तो डाउनलोड को अलग-अलग हिस्सों में बांटकर, एक साथ डाउनलोड करें. इसके बाद, क्लाइंट पर फिर से एक साथ जोड़ें.
यहां एक हेल्पर फ़ंक्शन दिया गया है, जिसका इस्तेमाल अपने कोड में किया जा सकता है. आपको सिर्फ़ url
को पास करना होगा. chunkSize
(डिफ़ॉल्ट: 5 एमबी), maxParallelRequests
(डिफ़ॉल्ट: 6), progressCallback
फ़ंक्शन (जो downloadedBytes
और कुल fileSize
की रिपोर्ट करता है), और AbortSignal
सिग्नल के लिए signal
, ये सभी वैकल्पिक हैं.
अपने प्रोजेक्ट में नीचे दिए गए फ़ंक्शन को कॉपी किया जा सकता है या npm से fetch-in-chunks
पैकेज इंस्टॉल किया जा सकता है.
async function fetchInChunks(
url,
chunkSize = 5 * 1024 * 1024,
maxParallelRequests = 6,
progressCallback = null,
signal = null
) {
// Helper function to get the size of the remote file using a HEAD request
async function getFileSize(url, signal) {
const response = await fetch(url, { method: 'HEAD', signal });
if (!response.ok) {
throw new Error('Failed to fetch the file size');
}
const contentLength = response.headers.get('content-length');
if (!contentLength) {
throw new Error('Content-Length header is missing');
}
return parseInt(contentLength, 10);
}
// Helper function to fetch a chunk of the file
async function fetchChunk(url, start, end, signal) {
const response = await fetch(url, {
headers: { Range: `bytes=${start}-${end}` },
signal,
});
if (!response.ok && response.status !== 206) {
throw new Error('Failed to fetch chunk');
}
return await response.arrayBuffer();
}
// Helper function to download chunks with parallelism
async function downloadChunks(
url,
fileSize,
chunkSize,
maxParallelRequests,
progressCallback,
signal
) {
let chunks = [];
let queue = [];
let start = 0;
let downloadedBytes = 0;
// Function to process the queue
async function processQueue() {
while (start < fileSize) {
if (queue.length < maxParallelRequests) {
let end = Math.min(start + chunkSize - 1, fileSize - 1);
let promise = fetchChunk(url, start, end, signal)
.then((chunk) => {
chunks.push({ start, chunk });
downloadedBytes += chunk.byteLength;
// Update progress if callback is provided
if (progressCallback) {
progressCallback(downloadedBytes, fileSize);
}
// Remove this promise from the queue when it resolves
queue = queue.filter((p) => p !== promise);
})
.catch((err) => {
throw err;
});
queue.push(promise);
start += chunkSize;
}
// Wait for at least one promise to resolve before continuing
if (queue.length >= maxParallelRequests) {
await Promise.race(queue);
}
}
// Wait for all remaining promises to resolve
await Promise.all(queue);
}
await processQueue();
return chunks.sort((a, b) => a.start - b.start).map((chunk) => chunk.chunk);
}
// Get the file size
const fileSize = await getFileSize(url, signal);
// Download the file in chunks
const chunks = await downloadChunks(
url,
fileSize,
chunkSize,
maxParallelRequests,
progressCallback,
signal
);
// Stitch the chunks together
const blob = new Blob(chunks);
return blob;
}
export default fetchInChunks;
अपने लिए सही तरीका चुनना
इस गाइड में, ब्राउज़र में एआई मॉडल को असरदार तरीके से कैश मेमोरी में सेव करने के अलग-अलग तरीकों के बारे में बताया गया है. यह काम, आपके ऐप्लिकेशन के उपयोगकर्ता अनुभव और परफ़ॉर्मेंस को बेहतर बनाने के लिए ज़रूरी है. Chrome की स्टोरेज टीम, बेहतर परफ़ॉर्मेंस के लिए Cache API का सुझाव देती है. इससे, एआई मॉडल को तुरंत ऐक्सेस किया जा सकता है, लोड होने में लगने वाला समय कम हो जाता है, और ऐप्लिकेशन तुरंत काम करने लगता है.
OPFS और IndexedDB, कम इस्तेमाल किए जाने वाले विकल्प हैं. डेटा को सेव करने से पहले, OPFS और IndexedDB API को उसे सीरियलाइज़ करना होगा. IndexedDB को डेटा वापस पाने के लिए, उसे डिसीरियलाइज़ भी करना पड़ता है. इसलिए, बड़े मॉडल को सेव करने के लिए, यह सबसे खराब जगह है.
खास ऐप्लिकेशन के लिए, फ़ाइल सिस्टम ऐक्सेस एपीआई, उपयोगकर्ता के डिवाइस पर मौजूद फ़ाइलों का सीधा ऐक्सेस देता है. यह उन उपयोगकर्ताओं के लिए बेहतर है जो अपने एआई मॉडल मैनेज करते हैं.
अगर आपको अपने एआई मॉडल को सुरक्षित रखना है, तो उसे सर्वर पर सेव करें. क्लाइंट पर सेव होने के बाद, DevTools या OFPS DevTools एक्सटेंशन की मदद से, कैश मेमोरी और IndexedDB, दोनों से डेटा निकालना आसान है. ये स्टोरेज एपीआई, सुरक्षा के मामले में एक जैसे होते हैं. हो सकता है कि आप मॉडल का एन्क्रिप्ट (सुरक्षित) किया गया वर्शन सेव करना चाहें. हालांकि, इसके लिए आपको क्लाइंट को डिक्रिप्ट करने की कुंजी देनी होगी, जिसे इंटरसेप्ट किया जा सकता है. इसका मतलब है कि आपके मॉडल को चुराने के लिए, किसी बुरे व्यक्ति को थोड़ी ज़्यादा मेहनत करनी पड़ेगी. हालांकि, यह असंभव नहीं है.
हमारा सुझाव है कि आप कैश मेमोरी सेव करने की ऐसी रणनीति चुनें जो आपके ऐप्लिकेशन की ज़रूरतों, टारगेट ऑडियंस के व्यवहार, और इस्तेमाल किए जा रहे एआई मॉडल की विशेषताओं के हिसाब से हो. इससे यह पक्का होता है कि आपके ऐप्लिकेशन, नेटवर्क की अलग-अलग स्थितियों और सिस्टम की पाबंदियों के तहत भी रिस्पॉन्सिव और बेहतर तरीके से काम करते हैं.
आभार
इसकी समीक्षा, जोशुआ बेल, रेली ग्रांट, एवन स्टैड, नेथन मेमॉट, ऑस्टिन सुलिवन, एटिने नोएल, ऐंड्रे बंदरा, ऐलेक्ज़ेंड्रा क्लेपर, फ़्रांस्वा बफ़ोर, पॉल किनलन, और रेचल एंड्रयू ने की है.