Hầu hết các mô hình AI đều có ít nhất một điểm chung, đó là
tương đối lớn cho một tài nguyên
được chuyển qua Internet. Mô hình phát hiện đối tượng MediaPipe nhỏ nhất
(SSD MobileNetV2 float16
) nặng 5,6 MB
và tệp lớn nhất là khoảng 25 MB.
Mô hình ngôn ngữ lớn (LLM) nguồn mở
gemma-2b-it-gpu-int4.bin
có tốc độ 1,35 GB và đây được coi là rất nhỏ đối với một LLM.
Các mô hình AI tạo sinh có thể là vô cùng lớn. Đó là lý do ngày nay AI được rất nhiều người sử dụng
trên đám mây. Ngày càng có nhiều ứng dụng chạy trực tiếp các mô hình được tối ưu hoá cao
trên thiết bị. Trong khi bản minh hoạ của các LLM chạy trong trình duyệt
dưới đây là một số ví dụ ở cấp sản xuất về các mô hình khác chạy trong
trình duyệt:
- Adobe Photoshop chạy một biến thể của mô hình
Conv2D
trên thiết bị để cung cấp công cụ chọn đối tượng thông minh. - Google Meet chạy phiên bản tối ưu hoá của mô hình
MobileNetV3-small
để phân đoạn người dùng cho tính năng làm mờ nền. - Tokopedia chạy mô hình
MediaPipeFaceDetector-TFJS
để phát hiện khuôn mặt theo thời gian thực nhằm ngăn các lượt đăng ký không hợp lệ đối với dịch vụ của YouTube. - Google Colab cho phép người dùng sử dụng các mô hình từ ổ đĩa cứng của họ trong sổ tay Colab.
Để các ứng dụng chạy trong tương lai được chạy nhanh hơn, bạn nên lưu vào bộ nhớ đệm một cách rõ ràng dữ liệu mô hình trên thiết bị, thay vì dựa vào trình duyệt HTTP ngầm ẩn bộ nhớ đệm.
Mặc dù hướng dẫn này sử dụng gemma-2b-it-gpu-int4.bin model
để tạo một chatbot,
cách tiếp cận này có thể được khái quát hoá để phù hợp với các mô hình khác và các trường hợp sử dụng khác
trên thiết bị. Cách phổ biến nhất để kết nối một ứng dụng với một mô hình là phân phát
cùng với phần còn lại của các tài nguyên ứng dụng. Điều quan trọng là phải tối ưu hoá
của bạn.
Định cấu hình tiêu đề bộ nhớ đệm phù hợp
Nếu phân phát các mô hình AI từ máy chủ của mình, bạn cần phải định cấu hình đúng
Cache-Control
. Ví dụ sau đây trình bày một chế độ cài đặt mặc định ổn định mà bạn có thể tạo
cho các nhu cầu của ứng dụng.
Cache-Control: public, max-age=31536000, immutable
Mỗi phiên bản được phát hành của một mô hình AI là một tài nguyên tĩnh. Nội dung không bao giờ
nên áp dụng các thay đổi
max-age
kết hợp với chặn truy xuất bộ nhớ đệm
trong URL yêu cầu. Nếu cần cập nhật mô hình, bạn phải
cung cấp cho nó một URL mới.
Khi người dùng tải lại trang, máy khách sẽ gửi yêu cầu xác thực lại, thậm chí
mặc dù máy chủ biết rằng nội dung ổn định. Chiến lược phát hành đĩa đơn
immutable
chỉ rõ rằng việc xác thực lại là không cần thiết vì
sẽ không thay đổi. Lệnh immutable
là
không được hỗ trợ rộng rãi
bởi trình duyệt và bộ nhớ đệm trung gian hoặc máy chủ proxy, nhưng thông qua
kết hợp công cụ này với
đều hiểu được lệnh max-age
trên toàn cầu, bạn có thể đảm bảo tối đa
khả năng tương thích. public
lệnh phản hồi cho biết rằng phản hồi có thể được lưu trữ trong bộ nhớ đệm dùng chung.
Lưu các mô hình AI vào bộ nhớ đệm ở phía máy khách
Khi phân phát một mô hình AI, bạn cần lưu mô hình đó vào bộ nhớ đệm một cách rõ ràng trong trình duyệt. Điều này đảm bảo dữ liệu mô hình luôn có sẵn sau khi người dùng tải lại ứng dụng.
Có một số kỹ thuật mà bạn có thể sử dụng để đạt được điều này. Dành cho:
mã mẫu, giả sử mỗi tệp mô hình được lưu trữ trong một
Đối tượng Blob
có tên blob
trong bộ nhớ.
Để hiểu rõ hiệu suất, mỗi mã mẫu được chú thích bằng
performance.mark()
và performance.measure()
. Các phương pháp đo lường này phụ thuộc vào thiết bị và không thể tổng quát hoá.
Bạn có thể chọn sử dụng một trong các API sau để lưu các mô hình AI vào bộ nhớ đệm trong trình duyệt: Cache API, API Hệ thống tệp riêng tư gốc và APIIndexedDB. Theo đề xuất chung, bạn nên sử dụng Cache API, nhưng hướng dẫn này thảo luận về ưu và nhược điểm của tất cả tuỳ chọn.
API Bộ nhớ đệm
API Bộ nhớ đệm cung cấp
bộ nhớ liên tục trong Request
và Response
đối tượng
các cặp được lưu vào bộ nhớ đệm trong bộ nhớ dài hạn. Mặc dù
được xác định trong thông số kỹ thuật của Trình chạy dịch vụ,
bạn có thể dùng API này từ luồng chính hoặc một worker thông thường. Để sử dụng ngoài trời
ngữ cảnh của trình chạy dịch vụ, hãy gọi phương thức
Phương thức Cache.put()
có đối tượng Response
tổng hợp, được ghép nối với một URL tổng hợp thay vì
Đối tượng Request
.
Hướng dẫn này giả định một blob
trong bộ nhớ. Sử dụng một URL giả làm khoá bộ nhớ đệm
Response
tổng hợp dựa trên blob
. Nếu bạn trực tiếp tải xuống
bạn sẽ sử dụng Response
sẽ nhận được từ việc tạo một fetch()
của bạn.
Ví dụ: dưới đây là cách lưu trữ và khôi phục tệp mô hình bằng 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 hệ thống tệp riêng tư gốc
Nguồn gốc hệ thống tệp riêng tư (OPFS) là một tiêu chuẩn còn khá trẻ cho một điểm cuối của bộ nhớ. Thông tin này là riêng tư đối với nguồn gốc của trang và do đó không hiển thị cho người dùng, không giống như hệ thống tệp thông thường. Nền tảng này mang đến cho bạn quyền truy cập vào được tối ưu hoá cao cho hiệu suất và cung cấp quyền ghi vào nội dung.
Ví dụ: dưới đây là cách lưu trữ và khôi phục tệp mô hình trong 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 là tiêu chuẩn lâu dài để lưu trữ dữ liệu tuỳ ý một cách liên tục trong trình duyệt. Nó nổi tiếng với API hơi phức tạp, nhưng bằng cách sử dụng một thư viện trình bao bọc, chẳng hạn như idb-keyval bạn có thể coi IndexedDB như kho lưu trữ khoá-giá trị cổ điển.
Ví dụ:
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;
}
};
Đánh dấu bộ nhớ là đã lưu trữ
Gọi navigator.storage.persist()
ở cuối bất kỳ phương thức lưu vào bộ nhớ đệm nào trong số này để yêu cầu quyền sử dụng
bộ nhớ liên tục. Phương thức này trả về một lời hứa phân giải thành true
nếu
quyền đã được cấp và nếu không thì false
. Trình duyệt
có thể đáp ứng hoặc không đáp ứng yêu cầu,
tuỳ thuộc vào các quy tắc dành riêng cho trình duyệt.
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);
}
}
Trường hợp đặc biệt: Sử dụng mẫu trên ổ đĩa cứng
Để thay thế, bạn có thể tham chiếu các mô hình AI trực tiếp từ ổ đĩa cứng của người dùng vào bộ nhớ của trình duyệt. Kỹ thuật này có thể giúp các ứng dụng tập trung vào nghiên cứu thể hiện tính khả thi của việc chạy các mô hình nhất định trong trình duyệt hoặc cho phép nghệ sĩ sử dụng người mẫu tự đào tạo trong các ứng dụng sáng tạo chuyên nghiệp.
API Truy cập hệ thống tệp
Với API Truy cập hệ thống tệp, bạn có thể mở các tệp từ đĩa cứng và lấy FileSystemFileHandle mà bạn có thể giữ lại IndexedDB.
Với mẫu này, người dùng chỉ cần cấp quyền truy cập vào tệp mô hình
một lần. Nhờ có các quyền lâu dài,
người dùng có thể chọn cấp vĩnh viễn quyền truy cập vào tệp. Sau khi tải lại
và một cử chỉ bắt buộc của người dùng, chẳng hạn như nhấp chuột,
Có thể khôi phục FileSystemFileHandle
từ IndexedDB bằng quyền truy cập vào tệp này
trên ổ đĩa cứng.
Quyền truy cập vào tệp được truy vấn và yêu cầu nếu cần. Điều này khiến liền mạch để tải lại trong tương lai. Ví dụ sau đây trình bày cách lấy một xử lý một tệp từ ổ đĩa cứng, sau đó lưu trữ và khôi phục tên người dùng.
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;
}
};
Các phương pháp này không loại trừ lẫn nhau. Có thể có trường hợp cả hai bạn lưu một mô hình vào bộ nhớ đệm rõ ràng trong trình duyệt và sử dụng một mô hình từ đĩa cứng của người dùng.
Bản minh hoạ
Bạn có thể thấy cả ba phương thức lưu trữ theo trường hợp thông thường và phương thức ổ đĩa cứng được triển khai trong bản minh hoạ LLM MediaPipe.
Bật mí thêm cho bạn: Tải tệp lớn xuống theo từng phần
Nếu bạn cần tải một mô hình AI lớn xuống từ Internet, hãy tải song song tải xuống thành các phần riêng biệt, sau đó khâu lại với nhau trên máy khách.
Sau đây là một hàm trợ giúp mà bạn có thể sử dụng trong mã của mình. Bạn chỉ cần vượt qua
đó là url
. chunkSize
(mặc định: 5MB), maxParallelRequests
(mặc định: 6), hàm progressCallback
(báo cáo về
downloadedBytes
và tổng fileSize
), cũng như signal
cho một
Tất cả tín hiệu AbortSignal
đều không bắt buộc.
Bạn có thể sao chép hàm sau trong dự án hoặc
cài đặt gói fetch-in-chunks
qua gói 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;
Chọn phương thức phù hợp với bạn
Tài liệu hướng dẫn này trình bày nhiều phương pháp để lưu mô hình AI vào bộ nhớ đệm một cách hiệu quả trong trình duyệt, một tác vụ quan trọng đối với việc nâng cao trải nghiệm của người dùng và hiệu suất của ứng dụng. Nhóm bộ nhớ Chrome đề xuất dùng Cache API cho hiệu suất tối ưu, để đảm bảo truy cập nhanh vào các mô hình AI, giảm thời gian tải và cải thiện khả năng phản hồi.
OPFS và IndexedDB là các lựa chọn ít hữu dụng hơn. OPFS và API IndexedDB chuyển đổi tuần tự dữ liệu trước khi lưu trữ. IndexedDB cũng cần giải tuần tự dữ liệu khi được truy xuất, khiến dữ liệu trở thành nơi kém chất lượng nhất để lưu trữ dữ liệu mô hình lớn.
Đối với các ứng dụng thích hợp, API Truy cập hệ thống tệp cung cấp quyền truy cập trực tiếp vào tệp trên thiết bị của người dùng, phù hợp với những người dùng tự quản lý mô hình AI.
Nếu bạn cần bảo mật mô hình AI, hãy giữ mô hình đó trên máy chủ. Sau khi được lưu trữ trên khách hàng, việc trích xuất dữ liệu từ cả Bộ nhớ đệm và IndexedDB bằng Công cụ cho nhà phát triển hoặc tiện ích OFPS Công cụ cho nhà phát triển. Các API lưu trữ này vốn có tính bảo mật ngang nhau. Có thể bạn sẽ muốn lưu trữ phiên bản đã mã hoá của mô hình, nhưng sau đó bạn cần lấy mã giải mã cho máy khách mà có thể bị chặn. Điều này có nghĩa là hành vi của đối tượng xấu để đánh cắp mô hình của bạn hơi khó hơn, nhưng không phải là không thể.
Bạn nên chọn chiến lược lưu vào bộ nhớ đệm phù hợp với các yêu cầu, hành vi của đối tượng mục tiêu và đặc điểm của các mô hình AI này đã sử dụng. Điều này đảm bảo ứng dụng của bạn có tính thích ứng và mạnh mẽ trên nhiều điều kiện mạng và hạn chế về hệ thống.
Xác nhận
Người đánh giá: Joshua Bell, Reilly Grant, Evan Stade, Nathan Memmott Austin Sullivan, Etienne Noël, André Bandarra, Alexandra Klepper, François Beaufort, Paul Kinlan và Rachel Andrew.