بهینهسازی Transformers.js برای برنامههای وب تولیدی

اجرای Transformers.js در یک نمونه اولیه به حداقل کد نیاز دارد: یک فراخوانی pipeline() ، چند خط پیکربندی، و شما هوش مصنوعی سمت کلاینت را در یک تب مرورگر دارید. ارسال همین تنظیمات به محیط عملیاتی جایی است که همه چیز خراب میشود. مدلهای بزرگ ONNX بارگذاری صفحات را متوقف میکنند، اجرای WASM با حلقه رندر React برای زمان نخ اصلی مبارزه میکند، و پیکربندیهای استاندارد bundler در برابر داراییهای باینری که هرگز برای مدیریت آنها طراحی نشدهاند، دچار مشکل میشوند. اکثر تیمها این مشکلات را تنها پس از استقرار کشف میکنند، زمانی که کاربران واقعی در شبکههای واقعی شروع به گزارش کندی Time to Interactive و افزایش ناگهانی حافظه بدون دلیل میکنند.
این مقاله یک راهنمای پیشنهادی ارائه میدهد: کپی-پیست کردن قالبهای پیکربندی Webpack و Vite، ذخیرهسازی مدل ONNX از طریق Cache API و IndexedDB، الگوهای Web Worker که خطاهای شروع سرد را از بین میبرند، و قلابهای حذف حافظه مخصوص React. هدف، یک ویژگی هوش مصنوعی سمت کلاینت است که مانند هر دارایی تولیدی بهینهشده دیگری رفتار کند.
تست شده با: @xenova/transformers@2.x (یا @huggingface/transformers@3.x )، onnxruntime-web@1.x ، webpack@5.x ، vite@5.x . گزینههای پیکربندی ممکن است در نسخههای اصلی متفاوت باشند -- وابستگیهای خود را بر این اساس پین کنید.
فهرست مطالب
آشنایی با گلوگاههای تولید
اندازه مدل ONNX و هزینه شبکه
فایلهای مدل ONNX داراییهای دودویی هستند، نه ماژولهای جاوا اسکریپت. یک مدل کوانتیزه شده مانند Xenova/all-MiniLM-L6-v2 تقریباً 23 مگابایت وزن دارد (اندازه فعلی را روی کارت مدل Hugging Face بررسی کنید، زیرا ممکن است بین نسخهها تغییر کند)؛ انواع کوانتیزه نشده معماریهای بزرگتر میتوانند از 100 مگابایت فراتر روند. این اندازهها مستقیماً زمان تعامل را افزایش میدهند، به خصوص در اتصالات تلفن همراه. بستهبندی ساده، که در آن Webpack یا Vite سعی میکنند فایلهای .onnx را از طریق لودرهای استاندارد پردازش کنند، با شکست مواجه میشود زیرا حبابهای دودویی جاوا اسکریپتهای قابل تغییر درخت نیستند. بستهبندی کننده یا خطا میدهد یا یک بستهبندی بسیار بزرگ تولید میکند.
تأخیر شروع سرد
تأخیر شروع سرد برای یک خط لوله Transformers.js به سه مرحله متوالی تقسیم میشود: دانلود شبکهای فایل ONNX، deserialize کردن نمودار مدل به backend WASM در ONNX Runtime، و یک مرحله استنتاج گرم کردن که JIT هستههای WASM را کامپایل میکند. برای all-MiniLM-L6-v2 در یک اتصال محدود (Chrome DevTools "Fast 3G" از پیش تعیین شده، 1.5 مگابیت در ثانیه دانلود)، مرحله دانلود به تنهایی میتواند 8 تا 12 ثانیه طول بکشد. deserialize بسته به CPU دستگاه 1 تا 3 ثانیه دیگر اضافه میکند. اگر چیزی ذخیره یا از قبل بارگذاری نشده باشد، این یک فاصله 10 تا 15 ثانیهای بین اقدام کاربر و اولین نتیجه است. مسافت پیموده شده شما متفاوت خواهد بود - بر اساس سختافزار و مشخصات شبکه هدف خود اندازهگیری کنید.
اگر چیزی ذخیره یا از قبل بارگذاری نشده باشد، این یک فاصله ۱۰ تا ۱۵ ثانیهای بین اقدام کاربر و اولین نتیجه است.
رقابت در نخ اصلی و فشار حافظه
به طور پیشفرض، اجرای WASM در ONNX Runtime روی نخ اصلی اجرا میشود و مستقیماً با چرخههای تطبیق و رنگآمیزی React رقابت میکند. همین امر به تنهایی میتواند باعث شود که رابط کاربری در حین اجرای مدل، منجمد به نظر برسد. در حین استنتاج، حداکثر تخصیص حافظه برای یک مدل جاسازی جمله میتواند به ۱۵۰ تا ۳۰۰ مگابایت از بافرهای آرایه تایپشده برسد، همانطور که از طریق performance.measureUserAgentSpecificMemory() در یک مکبوک ایر M2 مدل ۲۰۲۳ که کروم ۱۲۴ را اجرا میکند، اندازهگیری شده است - انتظار میرود که در دستگاهها و سیستمعاملهای مختلف، تفاوتهایی وجود داشته باشد. مکثهای جمعآوری زباله در این تخصیصها باعث افت فریم قابل مشاهده میشود و مشکل را در برنامههای React با رندرهای مکرر تشدید میکند.
پیکربندی Bundler برای Transformers.js
پیکربندی وبپک
Webpack برای برخورد با فایلهای .onnx به عنوان داراییهای ایستا به جای ماژولهای جاوا اسکریپت، به دستورالعملهای صریح نیاز دارد. قانون asset/resource آنها را در دایرکتوری خروجی کپی میکند و یک URL برمیگرداند، که دقیقاً همان چیزی است که ONNX Runtime انتظار دارد. شما همچنین به experiments.asyncWebAssembly نیاز دارید تا فایلهای .wasm به عنوان ماژولهای WebAssembly مناسب مدیریت شوند، fallbackها را برای سرکوب هشدارهای polyfill Node.js برطرف کنید، و یک IgnorePlugin برای سرکوب خطاهای ناشی از ارجاعات داخلی Node.js که Transformers.js حمل میکند اما هرگز در زمینه مرورگر اجرا نمیشود، داشته باشید.
مهم: وقتی asyncWebAssembly فعال است، برای فایلهای .wasm قانون جداگانهای برای asset/resource اضافه نکنید. این دو با هم ناسازگارند -- asyncWebAssembly مستلزم آن است که فایلهای .wasm به عنوان ماژولهای WebAssembly (از نوع webassembly/async ) پردازش شوند، در حالی که asset/resource این قانون را لغو کرده و آنها را به عنوان فایلهای استاتیک کپی میکند و در زمان اجرا، backend WASM را از کار میاندازد.
const webpack = require ( 'webpack' ) ; const path = require ( 'path' ) ;
module . exports = {
experiments : { asyncWebAssembly : true , } ,
module : { rules : [ { test : / \. onnx $ / , type : 'asset/resource' , generator : { filename : 'models/[name][ext]' , } , } , ] , } ,
resolve : { alias : {
'onnxruntime-web' : require . resolve ( 'onnxruntime-web' ) , } , fallback : { fs : false , path : false , crypto : false , } , } ,
plugins : [
new webpack . IgnorePlugin ( { resourceRegExp : / ^ ( fs | path | crypto | os | stream | buffer | util | events | assert | url ) $ / , contextRegExp : / node_modules / , } ) , ] , } ; این ورودیهای fallback به Webpack 5 میگویند که از polyfillهای Node.js که Transformers.js به آنها ارجاع میدهد اما هرگز در محیط مرورگر فراخوانی نمیکند، صرف نظر کند -- آنها فقط ارجاعات زمان ساخت هستند و باعث خطاهای زمان اجرا نمیشوند. IgnorePlugin مشخصکنندههای داخلی Node.js (مثلاً 'fs' ، 'path' ، 'crypto' ) را که Transformers.js وارد میکند اما در مرورگر اجرا نمیشود، دریافت میکند. contextRegExp تضمین میکند که فقط importهایی که از node_modules سرچشمه میگیرند، سرکوب شوند -- اگر کد برنامه شما به طور تصادفی 'fs' را وارد کند، Webpack همچنان آن را به عنوان خطا گزارش میدهد.
پیکربندی ویت
معماری مبتنی بر Rollup در Vite به شیوهای متفاوت عمل میکند. فایلهای ONNX را در assetsInclude تعریف کنید تا Vite با آنها به عنوان داراییهای استاتیک قابل وارد کردن رفتار کند. بسته Transformers.js را از طریق optimizeDeps.exclude از پیشبستهبندی وابستگیها مستثنی کنید، زیرا پیشبستهبندی سعی میکند فایلهای WASM وارد شده را تجزیه کند و با شکست مواجه میشود. تقسیم دستی تکهها، کتابخانه Transformers.js را از بستهبندی اصلی برنامه خارج نگه میدارد. قالب worker را روی ماژولهای ES تنظیم کنید.
نکتهای در مورد manualChunks : ورودی بخش transformers در زیر از '@xenova/transformers' (نسخه ۲) استفاده میکند. اگر @huggingface/transformers (نسخه ۳+) استفاده میکنید، نام بسته را به طور مناسب جایگزین کنید. فقط یکی باید فهرست شود -- استفاده از نام اشتباه، هشدار Rollup در مورد فقدان ورودی بخش ایجاد میکند.
import { defineConfig } from 'vite' ; import react from '@vitejs/plugin-react' ;
export default defineConfig ( { plugins : [ react ( ) ] ,
assetsInclude : [ '**/*.onnx' ] ,
optimizeDeps : {
exclude : [ '@xenova/transformers' , '@huggingface/transformers' ] , } ,
worker : { format : 'es' , } ,
build : { target : 'esnext' , rollupOptions : { output : { manualChunks : {
transformers : [ '@xenova/transformers' ] , } , } , } , } ,
server : { headers : { 'Cross-Origin-Opener-Policy' : 'same-origin' , 'Cross-Origin-Embedder-Policy' : 'require-corp' , } , } , } ) ; بدون هدرهای COOP/COEP در server.headers ، ONNX Runtime در طول توسعه به اجرای تکرشتهای برمیگردد. توجه داشته باشید که server.headers فقط برای vite dev اعمال میشود -- شما باید این هدرها را در سرور عملیاتی یا پلتفرم میزبانی خود نیز پیکربندی کنید.
ارائه مدلها از CDN در مقابل میزبانی مستقل
به طور پیشفرض، Transformers.js مدلها را از CDN مربوط به Hugging Face Hub دریافت میکند. این روش کار میکند اما به در دسترس بودن و محدودیتهای نرخ Hugging Face بستگی دارد. برای محیط عملیاتی، میزبانی مستقل روی S3/CloudFront، Vercel Edge یا یک CDN مشابه به شما امکان کنترل هدرهای کش و تضمین آپتایم را میدهد.
گزینه ۱: مدلهای CDN / Remote
import { env } from '@xenova/transformers' ; env . allowLocalModels = false ; env . remoteHost = 'https://cdn.yourapp.com/models/' ;گزینه ۲: مدلهای محلی خود-میزبانimport { env } from '@xenova/transformers' ; env . allowLocalModels = false ; env . remoteHost = 'https://cdn.yourapp.com/models/' ;
import { env } from '@xenova/transformers' ;
env . allowLocalModels = true ; env . allowRemoteModels = false ; env . localModelPath = '/models/' ; Cache-Control: public, max-age=31536000, immutable تنظیم کنید. دستورالعمل immutable به مرورگرها میگوید که اعتبارسنجی مجدد انجام ندهند، که این امر درخواستهای GET شرطی را در بازدیدهای بعدی حذف میکند. هنگام ارسال نسخه جدید مدل، به جای تکیه بر حذف حافظه پنهان از طریق رشتههای پرسوجو، بخش مسیر را تغییر دهید.
استراتژیهای ذخیرهسازی مدل
API کش برای بازدیدهای مکرر
Transformers.js از رابط برنامهنویسی کاربردی کش مرورگر (Cache API) به صورت داخلی برای فایلهای مدل دانلود شده استفاده میکند. شما میتوانید بازدیدهای کش را تأیید کرده و کش را از قبل پر کنید تا از دانلودهای اضافی در بازدیدهای مکرر جلوگیری شود.
async function ensureModelCached ( modelUrl , timeoutMs = 30000 ) { const CACHE_NAME = 'transformers-models-v1' ;
try { const cache = await caches . open ( CACHE_NAME ) ; const cached = await cache . match ( modelUrl ) ;
if ( cached ) { if ( process . env . NODE_ENV !== 'production' ) console . log ( 'Model cache hit' ) ; return ; }
if ( process . env . NODE_ENV !== 'production' ) console . log ( 'Pre-caching model...' ) ;
const controller = new AbortController ( ) ; const timer = setTimeout ( ( ) => controller . abort ( ) , timeoutMs ) ;
let response ; try { response = await fetch ( modelUrl , { mode : 'cors' , signal : controller . signal , } ) ; } finally { clearTimeout ( timer ) ; }
if ( response . ok ) {
await cache . put ( modelUrl , response . clone ( ) ) ; } else { console . warn ( ` Pre-cache fetch failed: HTTP ${ response . status } ` ) ; } } catch ( err ) { if ( err . name === 'QuotaExceededError' ) { console . warn ( 'Storage quota exceeded — falling back to network fetch' ) ; } else if ( err . name === 'AbortError' ) { console . warn ( 'Pre-cache fetch timed out — falling back to network fetch' ) ; } else if ( err . name === 'TypeError' || err . name === 'NetworkError' ) { console . warn ( 'Pre-cache network error — falling back to network fetch' , err . message ) ; } else { throw err ; } } }
await ensureModelCached ( 'https://cdn.yourapp.com/models/v1/all-MiniLM-L6-v2/onnx/model_quantized.onnx' ) ; واکشی با یک AbortController پیچیده شده است تا از وقفههای نامحدود در اتصالات متوقفشده جلوگیری کند. خطاهای شبکه و وقفههای زمانی به خوبی شناسایی میشوند -- این تابع با این موارد به عنوان خطاهای کش برخورد میکند و به برنامه اجازه میدهد تا به واکشی زمان اجرا بازگردد. نام کش را به یک شناسه نسخه مرتبط کنید. هنگام استقرار یک مدل جدید، نسخه را در نام کش افزایش دهید و کشهای قدیمی را در یک رویداد activate Service Worker حذف کنید.
IndexedDB برای سناریوهای آفلاین و PWA
رابط برنامهنویسی کاربردی کش (Cache API) به طور طبیعی با سرویس ورکرها جفت میشود، اما برای PWAهای آفلاین که نیاز به ذخیره بافرهای مدل قبل از فعال شدن سرویس ورکرها دارند، IndexedDB یک لایه ذخیرهسازی قابل اعتمادتر فراهم میکند. یک بستهبندی ناهمگام حدود 30 خطی در اطراف IDBObjectStore میتواند ArrayBuffer خام فایل ONNX را ذخیره کرده و آن را در بارگذاریهای بعدی بازیابی کند و آن را مستقیماً به ONNX Runtime بدون واکشی شبکه منتقل کند. برای جزئیات پیادهسازی در مورد ذخیره و بازیابی حبابهای دودویی بزرگ، به مستندات MDN IndexedDB مراجعه کنید.
اعتبارسنجی حافظه پنهان و سهمیهبندی ذخیرهسازی
سهمیههای ذخیرهسازی مرورگر به طور قابل توجهی متفاوت است. کروم بخشی از فضای دیسک موجود را به یک مبدأ اختصاص میدهد (برای محدودیتهای فعلی به مستندات API ذخیرهسازی مراجعه کنید، زیرا درصد آن بسته به نسخه کروم و دستگاه متفاوت است). سافاری سقف بسیار پایینتری را اعمال میکند - تقریباً ۱ گیگابایت برای هر مبدأ از Safari 17 (برای ارقام فعلی به مستندات webkit.org اپل مراجعه کنید). کاربران در دستگاههای با محدودیت ذخیرهسازی سریعتر به محدودیتها میرسند. همیشه نوشتنهای کش را در یک try/catch برای QuotaExceededError (همانطور که در قطعه کد API Cache در بالا نشان داده شده است) قرار دهید و به جای خرابی بیصدا، در هر بازدید به پخش مدل از CDN برگردید. کلیدهای کش نسخه با استفاده از هش ویرایش مدل از Hugging Face به طوری که دانلودهای جزئی یا خراب از نسخه قبلی هرگز نسخه فعلی را آلوده نکنند.
حذف تاخیر شروع سرد با Web Workerها
واگذاری استنتاج به یک کارگر متعهد
اجرای استنتاج روی نخ اصلی در یک نسخه آزمایشی قابل قبول است. در محیط عملیاتی، یک Web Worker باید مالک خط لوله باشد -- بدون آن، استنتاج رندر را برای کل مدت اجرای مدل مسدود میکند، که در سختافزارهای متوسط به معنای صدها میلیثانیه رابط کاربری بدون پاسخ در هر فراخوانی است. الگوی معماری ساده است. نخ اصلی پیامهای وظیفه را ارسال میکند. Worker خط لوله را یک بار برای هر ترکیب وظیفه/مدل منحصر به فرد بارگذاری میکند، آن را ذخیره میکند، استنتاج را اجرا میکند و نتایج را ارسال میکند.
این ورکر از یک حافظه پنهان مبتنی بر Map که توسط task:model کلیدگذاری شده است استفاده میکند، به طوری که پیکربندیهای مختلف خط لوله هر کدام تکلایه خود را دریافت میکنند. یک initPromise سریالی شده از پیامهای همزمان که باعث راهاندازی اولیههای تکراری pipeline() میشوند، جلوگیری میکند -- اولین فراخوانیکننده، promise را ایجاد میکند و همه فراخوانیکنندههای بعدی منتظر همان promise در حال اجرا میمانند تا زمانی که حل شود.
import { pipeline } from '@xenova/transformers' ;
const pipelineCache = new Map ( ) ; let initPromise = null ;
async function getPipeline ( task , model , progress_callback ) { const key = ` ${ task } : ${ model } ` ; if ( pipelineCache . has ( key ) ) return pipelineCache . get ( key ) ;
if ( ! initPromise ) { initPromise = pipeline ( task , model , { progress_callback } ) . then ( ( instance ) => { pipelineCache . set ( key , instance ) ; initPromise = null ; return instance ; } ) . catch ( ( err ) => { initPromise = null ; throw err ; } ) ; } return initPromise ; }
self . onmessage = async ( event ) => { const { id , type , task , model , input , options } = event . data ;
if ( type === 'warmup' ) { try { await getPipeline ( task , model , ( progress ) => self . postMessage ( { type : 'progress' , data : progress } ) ) ; self . postMessage ( { type : 'ready' } ) ; } catch ( error ) { self . postMessage ( { type : 'error' , error : error . message } ) ; } return ; }
if ( ! id ) {
console . warn ( '[worker] Received message without id, discarding:' , type ) ; return ; }
try { const pipe = await getPipeline ( task , model , null ) ; const result = await pipe ( input , options ) ; self . postMessage ( { id , type : 'result' , data : result } ) ; } catch ( error ) { self . postMessage ( { id , type : 'error' , error : error . message } ) ; } } ; ذخیره نمونه خط لوله از دانلود مجدد و deserialize کردن مجدد مدل در هر فراخوانی استنتاج جلوگیری میکند. progress_callback به نخ اصلی اجازه میدهد تا در طول دانلود اولیه، یک نشانگر بارگذاری نمایش دهد. پیامهای بدون شناسه ( id به طور صریح با یک هشدار حذف میشوند و از ارسال بیسروصدای نتایج غیرقابل مسیریابی به نخ اصلی جلوگیری میکنند.
پیش بارگذاری Worker در App Bootstrap
منتظر نمانید تا کاربر یک ویژگی هوش مصنوعی را فعال کند و سپس worker را نمونهسازی کنید. آن را در زمان نصب برنامه ایجاد کنید و بلافاصله یک پیام گرم کردن ارسال کنید. این کار دانلود مدل را با تعامل اولیه کاربر با رابط کاربری همپوشانی میکند و بیشتر تأخیر شروع سرد را پنهان میکند.
import { useEffect , useRef } from 'react' ;
function App ( ) { const workerRef = useRef ( null ) ;
useEffect ( ( ) => { const worker = new Worker (
new URL ( './inference.worker.js' , import . meta . url ) , { type : 'module' } ) ; workerRef . current = worker ;
worker . onerror = ( e ) => console . error ( 'Worker init error:' , e . message ) ;
worker . postMessage ( { type : 'warmup' , task : 'feature-extraction' , model : 'Xenova/all-MiniLM-L6-v2' , } ) ;
return ( ) => worker . terminate ( ) ; } , [ ] ) ;
return < > { } </ > ; }ملاحظات SharedArrayBuffer
بکاند WASM چند رشتهای در ONNX Runtime Web از SharedArrayBuffer برای توزیع محاسبات بین رشتهها استفاده میکند، اما مرورگرها برای در دسترس بودن SharedArrayBuffer به هدرهای Cross-Origin-Opener-Policy: same-origin و Cross-Origin-Embedder-Policy: require-corp دارند. شما باید این هدرها را نه فقط در مرحله توسعه، بلکه در سرور عملیاتی خود نیز تنظیم کنید. در Next.js، آنها را در next.config.js زیر headers() پیکربندی کنید. در Express، آنها را به عنوان میانافزار در همه پاسخها اضافه کنید. در Vercel، از پیکربندی هدرهای vercel.json استفاده کنید.
میتوانید با بررسی self.crossOriginIsolated === true در کنسول مرورگر، از کارکرد صحیح هدرها اطمینان حاصل کنید.
اگر این هدرها قابل تنظیم نباشند (مثلاً به دلیل الزامات iframe شخص ثالث)، ONNX Runtime به WASM تکرشتهای روی میآورد. هزینه عملکرد وابسته به مدل و سختافزار است؛ تیم ONNX Runtime استنتاج ۲ تا ۴ برابر کندتر را برای مدلهای ترانسفورماتور معمولی روی سختافزار چند هستهای گزارش میدهد (برای معیارهای خاص به مستندات وب ONNX Runtime مراجعه کنید).
مدیریت حافظه در برنامههای React
چرخه عمر خط لوله و دفع آن
خطوط لوله Transformers.js آرایههای نوعدار بزرگی را به صورت داخلی نگه میدارند. این آرایهها صرفاً به دلیل unmount شدن یک کامپوننت React، garbage collector نمیشوند؛ ارجاعات باید صریحاً پاک شوند. یک قلاب سفارشی که مقداردهی اولیه، پاکسازی و رفتار double-mount حالت React Strict را مدیریت میکند، این امر را قابل مدیریت نگه میدارد.
مهم: آرگومانهای task و model برای useTransformersPipeline باید مقادیر رشتهای پایدار باشند. اگر در هر رندر، یک ارجاع شیء جدید یا یک رشته ساختهشده پویا ارسال کنید، آرایه وابستگی اثر، یک چرخه خاتمه/بازسازی worker را در هر رندر آغاز میکند که باعث فرسودگی سریع حافظه و CPU میشود. در محل فراخوانی از لیترالهای رشتهای، ثابتها یا useMemo استفاده کنید.
import { useEffect , useRef , useCallback } from 'react' ;
const INFER_TIMEOUT_MS = 30000 ;
function useTransformersPipeline ( task , model ) { const workerRef = useRef ( null ) ;
useEffect ( ( ) => {
if ( workerRef . current ) return ;
const worker = new Worker ( new URL ( './inference.worker.js' , import . meta . url ) , { type : 'module' } ) ; workerRef . current = worker ;
worker . onerror = ( e ) => console . error ( 'Worker error:' , e . message ) ;
worker . postMessage ( { type : 'warmup' , task , model } ) ;
return ( ) => { workerRef . current ?. terminate ( ) ; workerRef . current = null ; } ; } , [ task , model ] ) ;
const infer = useCallback ( ( input , options = { } ) => { return new Promise ( ( resolve , reject ) => { if ( ! workerRef . current ) { return reject ( new Error ( 'Worker is not available' ) ) ; }
const id = typeof crypto !== 'undefined' && typeof crypto . randomUUID === 'function' ? crypto . randomUUID ( ) : ` ${ Date . now ( ) } - ${ Math . random ( ) . toString ( 36 ) . slice ( 2 ) } ` ;
let settled = false ;
const handler = ( e ) => { if ( e . data . id !== id ) return ; if ( settled ) return ; settled = true ; clearTimeout ( timeout ) ; workerRef . current ?. removeEventListener ( 'message' , handler ) ; workerRef . current ?. removeEventListener ( 'error' , errorHandler ) ; e . data . type === 'result' ? resolve ( e . data . data ) : reject ( new Error ( e . data . error ) ) ; } ;
const errorHandler = ( e ) => { if ( settled ) return ; settled = true ; clearTimeout ( timeout ) ; workerRef . current ?. removeEventListener ( 'message' , handler ) ; workerRef . current ?. removeEventListener ( 'error' , errorHandler ) ; reject ( new Error ( e . message ?? 'Worker error' ) ) ; } ;
const timeout = setTimeout ( ( ) => { if ( ! settled ) { settled = true ; workerRef . current ?. removeEventListener ( 'message' , handler ) ; workerRef . current ?. removeEventListener ( 'error' , errorHandler ) ; reject ( new Error ( 'Inference timed out or worker was terminated' ) ) ; } } , INFER_TIMEOUT_MS ) ;
workerRef . current . addEventListener ( 'message' , handler ) ; workerRef . current . addEventListener ( 'error' , errorHandler ) ; workerRef . current . postMessage ( { id , task , model , input , options } ) ; } ) ; } , [ task , model ] ) ;
return { infer } ; } اگر یک worker هنگام اجرای effect از قبل وجود داشته باشد (همانطور که در mount دوم React Strict Mode در حال توسعه اتفاق میافتد)، effect بدون ایجاد یک نسخه تکراری، زودتر برمیگردد. در هنگام پاکسازی، worker خاتمه مییابد و ref روی null تنظیم میشود و اجازه میدهد یک worker جدید در mount بعدی ایجاد شود.
هر فراخوانی infer دقیقاً یک شنونده message و یک شنونده error اضافه میکند که هر دو پس از اتمام، timeout یا خطا حذف میشوند. این کار از تجمع شنوندهها جلوگیری میکند. فراخوانی clearTimeout درون خود handlerها قرار دارد (نه در یک wrapper ناشناس جداگانه)، و تضمین میکند که timeout همیشه هنگام رسیدن پاسخ لغو میشود. یک errorHandler جداگانه خطاهای سطح worker (مانند خرابیهای بارگذاری اسکریپت) را که به رویداد error ارسال میشوند، به جای onmessage دریافت میکند.
نظارت بر حافظه در دوران رشد
تابع performance.measureUserAgentSpecificMemory() کروم (که فقط در یک زمینه cross-origin-isolated در دسترس است -- اطمینان حاصل کنید که هدرهای COOP/COEP تنظیم شدهاند) خوانش حافظه برنامهریزیشده را ارائه میدهد. میتوانید با بررسی self.crossOriginIsolated === true در کنسول، ایزولهسازی cross-origin را تأیید کنید. یک بودجه حافظه برای زیرسیستم هوش مصنوعی تنظیم کنید -- آن را از حداکثر تخصیص ۱۵۰ تا ۳۰۰ مگابایت که قبلاً اندازهگیری شده و برای دستگاههای هدف شما تنظیم شده است، استخراج کنید -- و در هنگام توسعه، هنگام عبور از آستانه، هشدارها را ثبت کنید:
if ( self . crossOriginIsolated ) { const memInfo = await performance . measureUserAgentSpecificMemory ( ) ; console . log ( 'Total bytes:' , memInfo . bytes ) ; }عکسهای فوری از پشته را در برگه حافظه Chrome DevTools قبل و بعد از استنتاج مقایسه کنید تا تأیید کنید که آرایههای نوعدار پس از آزادسازی، آزاد میشوند.
چک لیست تولید
بسته نرمافزاری خود را طوری پیکربندی کنید که فایلهای .onnx را به عنوان فایلهای استاتیک با استفاده از قالبهای Webpack یا Vite بالا، خارجیسازی کند. اجازه دهید asyncWebAssembly فایلهای .wasm را در Webpack مدیریت کند.
مدلها را از یک CDN با Cache-Control: public, max-age=31536000, immutable در مسیرهای نسخهبندیشده، ارائه دهید.
مدلهای پیشذخیرهسازیشده از طریق Cache API (با response.clone() ، AbortController timeout و QuotaExceededError handling) برای حذف دانلودهای اضافی.
تمام استنتاجها را در یک Web Worker با الگوی singleton pipeline اجرا کنید. Worker را در بوتاسترپ برنامه گرم کنید، نه در اولین تعامل با کاربر.
با استفاده از قلاب useTransformersPipeline خطوط لوله را هنگام unmount کردن، حذف کنید؛ بودجههای حافظه را در حین توسعه رصد کنید.
در صورت استفاده از اجرای WASM چند رشتهای، هدرهای COOP/COEP را هم روی سرورهای توسعه و هم در سرورهای عملیاتی تنظیم کنید.
با Lighthouse و WebPageTest تحت شرایط کنترلشده حسابرسی کنید تا رگرسیونها را در Time to Interactive تشخیص دهید.
حداقل یک مرحله را با تنظیمات پیشفرض "Fast 3G" در DevTools اجرا کنید تا عملکرد بارگذاری مدل تأیید شود.
چه چیزی در ادامه میآید؟
Transformers.js از نظر تولید انبوه قابل اجرا است، اما تنها زمانی که مدلهای ONNX به عنوان داراییهای سنگین با خط لوله تحویل، استراتژی ذخیرهسازی و چرخه عمر حافظه خاص خود در نظر گرفته شوند. قالبهای Webpack و Vite که در بالا ذکر شد، طوری طراحی شدهاند که با حداقل تغییر در پروژههای موجود قابل استفاده باشند. بکاند WebGPU برای ONNX Runtime Web نویدبخش انتقال کامل استنتاج از CPU است که منجر به تغییر شکل چندین مورد از این الگوها، به ویژه در مورد نخبندی و فشار حافظه، خواهد شد. نقشه راه Transformers.js پشتیبانی از WebGPU را به عنوان یک اولویت برای نسخههای آینده دنبال میکند.





ارسال نظر