โมเดล AI ส่วนใหญ่มีสิ่งที่เหมือนกันอย่างน้อย 1 อย่างนั่นคือ
ค่อนข้างใหญ่สำหรับทรัพยากรที่
ผ่านอินเทอร์เน็ต โมเดลการตรวจจับออบเจ็กต์ MediaPipe ที่มีขนาดเล็กที่สุด
(SSD MobileNetV2 float16
) หนัก 5.6 MB
และขนาดที่ใหญ่ที่สุดอยู่ที่ประมาณ 25 MB
LLM โอเพนซอร์ส
gemma-2b-it-gpu-int4.bin
นาฬิกาที่ขนาด 1.35 GB ซึ่งถือว่าน้อยมากสำหรับ LLM
โมเดล Generative AI สามารถมีขนาดมหาศาล นี่จึงเป็นสาเหตุที่ปัจจุบันมีการใช้ AI อย่างล้นหลาม
ในระบบคลาวด์ แอปใช้โมเดลที่มีการเพิ่มประสิทธิภาพสูงโดยตรงมากขึ้นเรื่อยๆ
ในอุปกรณ์ ขณะการสาธิตการทำงานของ LLM ในเบราว์เซอร์
ต่อไปนี้เป็นตัวอย่างระดับเวอร์ชันที่ใช้งานจริงของโมเดลอื่นๆ ที่ใช้งานใน
เบราว์เซอร์:
- Adobe Photoshop เรียกใช้ตัวแปรของโมเดล
Conv2D
บนอุปกรณ์สำหรับเครื่องมือการเลือกวัตถุอัจฉริยะ - Google Meet ใช้โมเดล
MobileNetV3-small
เวอร์ชันที่มีการเพิ่มประสิทธิภาพ สำหรับการแบ่งกลุ่มบุคคล สำหรับฟีเจอร์การเบลอพื้นหลัง - Tokopedia เรียกใช้โมเดล
MediaPipeFaceDetector-TFJS
สำหรับการตรวจจับใบหน้าแบบเรียลไทม์เพื่อป้องกันการลงชื่อสมัครใช้บริการที่ไม่ถูกต้อง - Google Colab อนุญาตให้ผู้ใช้ใช้โมเดลจากฮาร์ดดิสก์ได้ ในสมุดบันทึก Colab
หากต้องการให้แอปพลิเคชันของคุณเปิดขึ้นในอนาคตเร็วขึ้น คุณควรทำแคช ข้อมูลโมเดลในอุปกรณ์ แทนที่จะใช้เบราว์เซอร์ HTTP โดยนัย แคช
ขณะที่คู่มือนี้ใช้ gemma-2b-it-gpu-int4.bin model
ในการสร้างแชทบ็อต
วิธีการนี้สามารถนำไปปรับให้เหมาะสมกับโมเดลอื่นๆ และกรณีการใช้งานอื่นๆ ได้
ในอุปกรณ์ วิธีที่ใช้กันมากที่สุดในการเชื่อมต่อแอปกับโมเดลคือการให้บริการ
ควบคู่ไปกับทรัพยากรที่เหลือของแอป การเพิ่มประสิทธิภาพ
กำหนดค่าส่วนหัวของแคชที่ถูกต้อง
หากคุณแสดงโมเดล AI จากเซิร์ฟเวอร์ คุณต้องกำหนดค่า
Cache-Control
ส่วนหัว ตัวอย่างต่อไปนี้แสดงการตั้งค่าเริ่มต้นที่มีประสิทธิภาพ ซึ่งคุณสามารถสร้าง
ตามความต้องการของแอป
Cache-Control: public, max-age=31536000, immutable
เวอร์ชันที่เปิดตัวของโมเดล AI แต่ละเวอร์ชันเป็นทรัพยากรแบบคงที่ เนื้อหาที่ไม่
การเปลี่ยนแปลงควรได้รับ
max-age
รวมกับการป้องกันแคช
ใน URL ของคำขอ หากต้องการอัปเดตโมเดล คุณต้อง
ให้ URL ใหม่
เมื่อผู้ใช้โหลดหน้านี้ซ้ำ ไคลเอ็นต์จะส่งคำขอตรวจสอบอีกครั้ง
แต่เซิร์ฟเวอร์จะรู้ว่าเนื้อหาเสถียร
immutable
ระบุไว้อย่างชัดเจนว่าการตรวจสอบความถูกต้องอีกครั้งนั้นไม่จำเป็นเนื่องจาก
เนื้อหาจะไม่เปลี่ยนแปลง คำสั่ง immutable
คือ
ไม่ได้รับการสนับสนุนในวงกว้าง
เบราว์เซอร์และแคชที่เป็นตัวกลางหรือพร็อกซีเซิร์ฟเวอร์ แต่โดย
การรวมผลิตภัณฑ์กับ
เป็นที่เข้าใจกันโดยทั่วไปเกี่ยวกับคำสั่ง max-age
คุณจะมั่นใจได้ว่า
ความสามารถในการใช้งานร่วมกัน public
คำสั่งการตอบกลับระบุว่าสามารถเก็บการตอบกลับไว้ในแคชที่แชร์
แคชโมเดล AI ฝั่งไคลเอ็นต์
เมื่อแสดงโมเดล AI คุณต้องแคชโมเดลอย่างชัดเจนใน เบราว์เซอร์ การดำเนินการนี้ช่วยให้มั่นใจได้ว่าข้อมูลโมเดลจะพร้อมใช้งานหลังจากที่ผู้ใช้โหลดซ้ำ แอปนั้น
มีเทคนิคมากมายที่คุณสามารถใช้เพื่อให้บรรลุเป้าหมายนี้ สำหรับ
ให้สมมติว่าไฟล์โมเดลแต่ละไฟล์ถูกเก็บไว้ใน
ออบเจ็กต์ Blob
ชื่อ blob
ในหน่วยความจำ
เพื่อให้เข้าใจประสิทธิภาพ ตัวอย่างโค้ดแต่ละรายการจะมีคำอธิบายประกอบ
performance.mark()
และ performance.measure()
มาตรการเหล่านี้ขึ้นอยู่กับอุปกรณ์และไม่สามารถทำให้เป็นแบบทั่วไปได้
คุณเลือกใช้ API ต่อไปนี้เพื่อแคชโมเดล AI ในเบราว์เซอร์ได้ Cache API Origin Private File System API และ IndexedDB API คำแนะนำทั่วไปคือให้ใช้ API แคช แต่คู่มือนี้จะพูดถึงข้อดีและข้อเสียของ ตัวเลือกทั้งหมด
API แคช
Cache API มี
พื้นที่เก็บข้อมูลถาวรสำหรับ Request
และออบเจ็กต์ Response
และแคชไว้ในหน่วยความจำที่ใช้ได้นาน แม้ว่า
ในข้อมูลจำเพาะของ Service Workers
คุณจะใช้ API นี้จากเทรดหลักหรือผู้ปฏิบัติงานทั่วไปก็ได้ เพื่อใช้ภายนอก
บริบทของโปรแกรมทำงานของบริการ เรียก
เมธอด 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;
}
};
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;
}
};
API ของ IndexedDB
IndexedDB เป็นมาตรฐานที่มั่นคงซึ่งใช้จัดเก็บข้อมูลที่กำหนดเองในลักษณะแบบถาวร ในเบราว์เซอร์ ซึ่งมีชื่อเสียงจาก API ที่ค่อนข้างซับซ้อน แต่โดยการใช้ ไลบรารี Wrapper เช่น 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);
}
}
กรณีพิเศษ: ใช้รุ่นบนฮาร์ดดิสก์
คุณสามารถใช้โมเดล AI อ้างอิงได้โดยตรงจากฮาร์ดดิสก์ของผู้ใช้เป็นทางเลือก ไปยังพื้นที่เก็บข้อมูลของเบราว์เซอร์ เทคนิคนี้จะช่วยให้แอปที่มุ่งเน้นการค้นคว้าแสดงข้อมูล ความเป็นไปได้ในการเรียกใช้โมเดลที่ระบุในเบราว์เซอร์ หรืออนุญาตให้ศิลปินใช้ โมเดลที่ฝึกฝนด้วยตนเองในแอปความคิดสร้างสรรค์ของผู้เชี่ยวชาญ
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;
}
};
วิธีการเหล่านี้ใช้ร่วมกันไม่ได้ อาจมีกรณีที่ทั้งสองฝ่าย แคชรุ่นในเบราว์เซอร์ให้ชัดเจน และใช้โมเดลจากฮาร์ดดิสก์ของผู้ใช้
สาธิต
คุณสามารถดูวิธีจัดเก็บเคสปกติทั้ง 3 วิธีและวิธีที่ใช้ฮาร์ดดิสก์ ที่ใช้ในการสาธิต MediaPipe LLM
โบนัส: ดาวน์โหลดไฟล์ขนาดใหญ่เป็นกลุ่ม
หากต้องการดาวน์โหลดโมเดล AI ขนาดใหญ่จากอินเทอร์เน็ต ให้โหลด ดาวน์โหลดเป็นส่วนย่อยๆ แล้วต่อเข้ากับไคลเอ็นต์อีกครั้ง
นี่คือฟังก์ชันตัวช่วยที่คุณใช้ในโค้ดได้ คุณต้องสอบผ่าน
ซึ่งก็คือ url
chunkSize
(ค่าเริ่มต้น: 5 MB), maxParallelRequests
(ค่าเริ่มต้น: 6) ฟังก์ชัน progressCallback
(ซึ่งรายงานฟังก์ชัน
downloadedBytes
และ fileSize
ทั้งหมด) และ signal
สำหรับ
สัญญาณ AbortSignal
เป็นตัวเลือกที่ไม่บังคับทั้งหมด
คุณสามารถคัดลอกฟังก์ชันต่อไปนี้ในโปรเจ็กต์หรือ
ติดตั้งแพ็กเกจ fetch-in-chunks
จาก npm
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;
เลือกวิธีที่เหมาะกับคุณ
คู่มือนี้ได้สำรวจวิธีการต่างๆ ในการแคชโมเดล AI อย่างมีประสิทธิภาพใน ซึ่งเป็นงานที่สำคัญในการปรับปรุงประสบการณ์ของผู้ใช้ และ ประสิทธิภาพของแอป ทีมพื้นที่เก็บข้อมูลของ Chrome ขอแนะนำ Cache API สำหรับ ประสิทธิภาพที่ดีที่สุด เพื่อให้เข้าถึงโมเดล AI ได้อย่างรวดเร็ว ลดเวลาในการโหลด และปรับปรุงการตอบสนอง
OPFS และ IndexedDB เป็นตัวเลือกที่ใช้งานได้น้อยกว่า OPFS และ API ของ IndexedDB ต้องเรียงลำดับข้อมูลก่อนจึงจะจัดเก็บได้ IndexedDB ยังต้อง ทำการดีซีเรียลไลซ์ข้อมูลเมื่อดึงมา ทำให้เป็นตำแหน่งที่แย่ที่สุดในการเก็บข้อมูล โมเดลขนาดใหญ่
สำหรับแอปพลิเคชันเฉพาะกลุ่ม File System Access API จะช่วยให้คุณเข้าถึงไฟล์ได้โดยตรง ในอุปกรณ์ของผู้ใช้ ซึ่งเหมาะสำหรับผู้ใช้ที่จัดการโมเดล AI ของตนเอง
หากต้องการรักษาโมเดล AI ของคุณให้ปลอดภัย ให้เก็บโมเดลไว้บนเซิร์ฟเวอร์ เมื่อจัดเก็บไว้ใน การดึงข้อมูลจากทั้ง Cache และ IndexedDB ด้วย DevTools หรือส่วนขยาย OFPS DevTools API พื้นที่เก็บข้อมูลเหล่านี้มีการรักษาความปลอดภัยอย่างเท่าเทียมกันโดยธรรมชาติ คุณอาจอยากลองใช้ จัดเก็บโมเดลเวอร์ชันที่เข้ารหัสไว้ แต่จากนั้นคุณจะต้องได้รับการถอดรหัส ไปยังไคลเอ็นต์ซึ่งอาจถูกดักจับได้ นี่หมายถึงความพยายามของผู้ไม่ประสงค์ดี ในการขโมยโมเดลของคุณนั้นยากขึ้นเล็กน้อย แต่ก็เป็นไปไม่ได้
เราขอแนะนำให้คุณเลือกกลยุทธ์การแคชที่สอดคล้องกับ ข้อกำหนด พฤติกรรมของกลุ่มเป้าหมาย และลักษณะของโมเดล AI ซึ่งช่วยให้มั่นใจได้ว่าแอปพลิเคชันของคุณจะมีการตอบสนองและมีประสิทธิภาพภายใต้ เงื่อนไขของเครือข่ายและข้อจำกัดของระบบ
กิตติกรรมประกาศ
รีวิวนี้ได้รับการตรวจสอบโดย Joshua Bell, Reilly Grant, Evan Stade, Nathan Memmott Austin Sullivan, Etienne Noël, André Bandarra, Alexandra Klepper François Beaufort, Paul Kinlan และ Rachel Andrew