किसी वेबसाइट पर माइक्रोफ़ोन बटन जोड़ना आसान लगता है: उपयोगकर्ता की आवाज़ रिकॉर्ड करें, बोले गए शब्द पहचानें और उन्हें टेक्स्ट फ़ील्ड में डाल दें। लेकिन डेस्कटॉप और मोबाइल ब्राउज़र पर इसे भरोसेमंद ढंग से चलाना कहीं ज़्यादा मुश्किल है, खासकर जब आवाज़ की पहचान पूरी तरह स्थानीय रूप से करनी हो और माइक्रोफ़ोन का ऑडियो सर्वर पर अपलोड नहीं किया जा सकता हो।
चुनौती सिर्फ़ स्पीच रिकग्निशन मॉडल चुनने की नहीं है। सही ढंग से काम करने वाले सिस्टम को माइक्रोफ़ोन की अनुमति, ऑडियो कैप्चर, री-सैंपलिंग, ब्राउज़र वर्कर, WebAssembly, ONNX मॉडल की शुरुआत, टोकनाइज़ेशन, कैशिंग और ऐप्लिकेशन के टेक्स्ट इनपुट के साथ इंटीग्रेशन, सब कुछ ठीक से संभालना होता है।
इस लेख में OpenAI Whisper, Transformers.js, ONNX Runtime Web, AudioWorklet और अलग Web Worker की मदद से प्राइवेसी बनाए रखते हुए आवाज़ को टेक्स्ट में बदलने की व्यावहारिक सुविधा बनाने का तरीका बताया गया है। इसमें ऐसी वास्तविक इंटीग्रेशन समस्याएँ भी शामिल हैं जिनके कारण मॉडल फ़ाइलें सफलतापूर्वक डाउनलोड होने के बाद भी माइक्रोफ़ोन बटन लोडिंग की स्थिति में अटका रह सकता है।
मकसद सीधा है: विज़िटर माइक्रोफ़ोन बटन दबाए, सामान्य ढंग से बोले और पहचाना गया टेक्स्ट पहले से मौजूद इनपुट फ़ील्ड में दिखाई दे। उसे भेजने से पहले विज़िटर टेक्स्ट जाँच या बदल सके। माइक्रोफ़ोन की रिकॉर्डिंग को ब्राउज़र से बाहर भेजने की ज़रूरत नहीं होनी चाहिए।
पूरी तरह क्लाइंट-साइड स्पीच रिकग्निशन का मतलब
वेबसाइट पर वॉइस इनपुट जोड़ने के मूल रूप से दो अलग तरीके हैं।
पहले तरीके में स्पीच रिकग्निशन सेवा इस्तेमाल होती है। ब्राउज़र ऑडियो रिकॉर्ड करके उसे रिमोट सेवा को भेजता है और पहचाना गया टेक्स्ट वापस प्राप्त करता है। इससे इम्प्लीमेंटेशन आसान हो सकता है, लेकिन नेटवर्क पर निर्भरता, प्राइवेसी से जुड़े सवाल, प्रदाता की उपलब्धता और इस्तेमाल के हिसाब से संभावित लागत जुड़ जाती है।
दूसरे तरीके में इन्फ़रेंस सीधे ब्राउज़र में होता है। वेबसाइट स्पीच रिकग्निशन मॉडल और उसे चलाने वाला रनटाइम डाउनलोड करती है। ब्राउज़र माइक्रोफ़ोन का ऑडियो लेकर उसे संख्यात्मक सैंपल में बदलता है, मॉडल को स्थानीय रूप से चलाता है और किसी स्पीच रिकग्निशन सर्वर के बिना ट्रांसक्रिप्ट तैयार करता है।
यह लेख पूरी तरह दूसरे तरीके पर केंद्रित है।
ब्राउज़र की सामान्य स्पीच रिकग्निशन सुविधा और निश्चित रूप से स्थानीय पहचान को एक जैसा नहीं समझना चाहिए। Web Speech API सर्वर-आधारित रिकग्निशन इंजन का उपयोग कर सकता है। कुछ नए इम्प्लीमेंटेशन processLocally विकल्प देते हैं, लेकिन इसका समर्थन ब्राउज़र और ऑपरेटिंग सिस्टम के हिसाब से अलग होता है। यदि स्थानीय प्रोसेसिंग अनिवार्य है, तो सामान्य SpeechRecognition को अपने-आप चालू होने वाले बैकअप की तरह इस्तेमाल नहीं करना चाहिए।
इसके बजाय ऐप्लिकेशन पूरे इन्फ़रेंस पाइपलाइन को खुद नियंत्रित करता है।
Microphone
|
v
AudioWorklet
|
v
Float32 PCM audio
|
v
Dedicated Web Worker
|
v
16 kHz resampling
|
v
Whisper + ONNX Runtime Web
|
v
Tokenizer and text decoding
|
v
React input stateवेबसाइट तब भी JavaScript, WebAssembly, कॉन्फ़िगरेशन फ़ाइलें और मॉडल वेट उपलब्ध कराती है। ये सामान्य स्टैटिक डाउनलोड हैं। अंतर यह है कि पहचान के लिए माइक्रोफ़ोन का ऑडियो अपलोड करने के बजाय स्थानीय रूप से प्रोसेस होता है। उपयोगकर्ता बाद में किसी सामान्य फ़ॉर्म से तैयार टेक्स्ट भेजे, तो वह ऐप्लिकेशन की अलग कार्रवाई है।
व्यावहारिक मॉडल चुनना: Whisper Tiny या Base
Whisper कई भाषाओं की आवाज़ पहचानने वाले मॉडलों का परिवार है। एक ही मॉडल से अनेक भाषाएँ पहचानने की क्षमता इसे बहुभाषी वेबसाइटों के लिए खास तौर पर उपयोगी बनाती है।
छोटे वॉइस मैसेज के लिए दो मॉडल पहचान की गुणवत्ता और ब्राउज़र के संसाधनों की खपत के बीच अच्छा संतुलन देते हैं: Whisper Tiny और Whisper Base।
| मॉडल | चुने गए Q8 ONNX वेट का अनुमानित आकार | सुझाया गया उपयोग |
|---|---|---|
| Whisper Tiny | 41 MB | मोबाइल और कम संसाधनों वाले डिवाइस के लिए सावधानीपूर्ण विकल्प |
| Whisper Base | 77 MB | जाँचे हुए, पर्याप्त क्षमता वाले डिवाइस पर बेहतर गुणवत्ता वाला विकल्प |
ये आकार जाँचे गए एक्सपोर्ट में चुनी गई एन्कोडर और डिकोडर ONNX फ़ाइलों के हैं। इनमें हर सहायक फ़ाइल शामिल नहीं है और इन्हें ब्राउज़र की अधिकतम मेमोरी खपत नहीं समझना चाहिए।
Whisper Tiny इसलिए उपयोगी है कि यह जल्दी डाउनलोड होता है, आम तौर पर कम कंप्यूटिंग संसाधन लेता है और ऐसे मोबाइल माहौल के लिए बेहतर है जहाँ डिवाइस की क्षमता स्पष्ट न हो। Whisper Base सामान्यतः बेहतर पहचान देता है, खासकर कुछ भाषाओं में जहाँ छोटे मॉडल अधिक गलतियाँ करते हैं।
मॉडल का चुनाव केवल डिवाइस की भौतिक RAM देखकर नहीं करना चाहिए। ब्राउज़र को उपलब्ध मेमोरी, डिवाइस के बताए गए विनिर्देशों से काफ़ी कम हो सकती है। पेज की मौजूदा मेमोरी, WebAssembly के लिए आवंटन, अस्थायी टेंसर और ऑपरेटिंग सिस्टम पर मेमोरी का दबाव भी मायने रखते हैं।
सावधानीपूर्ण तरीका है कि अज्ञात क्षमता वाले मोबाइल डिवाइस पर Tiny से शुरुआत की जाए, Base केवल जाँचकर उपयुक्त पाए गए डिवाइस वर्गों के लिए रखा जाए और मॉडल शुरू न हो पाने पर Base से Tiny पर लौट आया जाए।
Tiny लोड करने से पहले असफल Base इंस्टेंस को पूरी तरह रिलीज़ करना ज़रूरी है। दोनों मॉडल मेमोरी में बनाए रखने से छोटे मॉडल पर लौटने का उद्देश्य ही ख़त्म हो जाता है।
प्रोडक्शन के लिए WebAssembly एक समझदारी भरा डिफ़ॉल्ट क्यों है
ONNX Runtime Web ब्राउज़र के अंदर न्यूरल नेटवर्क चलाने की सुविधा देता है। इस तरह के काम के लिए WebAssembly प्रमुख डेस्कटॉप और मोबाइल ब्राउज़र पर CPU से इन्फ़रेंस करने का व्यावहारिक रास्ता देता है।
WebGPU उपयुक्त हार्डवेयर पर समर्थित मॉडलों को तेज़ कर सकता है, लेकिन उसका उपलब्ध होना स्पीच रिकग्निशन पाइपलाइन के सही काम करने की गारंटी नहीं है। ब्राउज़र में navigator.gpu मौजूद होने से यह सुनिश्चित नहीं होता कि ONNX Runtime उस प्लेटफ़ॉर्म को सपोर्ट करता है, खास Whisper ग्राफ़ सही तरह शुरू होगा या बार-बार इन्फ़रेंस चलाने पर संसाधन संबंधी समस्याएँ नहीं आएँगी।
इसलिए सावधानीपूर्ण इम्प्लीमेंटेशन प्रोडक्शन बैकएंड के रूप में WASM का उपयोग कर सकता है और WebGPU को संगतता, सटीकता, मेमोरी तथा बार-बार चलने वाले सेशन के परीक्षणों के बाद अलग से परख सकता है।
शुरुआती इम्प्लीमेंटेशन में एक थ्रेड वाला WASM इस्तेमाल करने से पूरी वेबसाइट को क्रॉस-ओरिजिन आइसोलेशन और SharedArrayBuffer पर निर्भर भी नहीं बनाना पड़ता।
env.backends.onnx.wasm.numThreads = 1;
env.backends.onnx.wasm.proxy = false;अलग वर्कर में इन्फ़रेंस चलाने से भारी गणना मुख्य JavaScript थ्रेड से दूर रहती है। मॉडल ऑडियो प्रोसेस कर रहा हो तब भी इनपुट फ़ील्ड, वर्चुअल कीबोर्ड, स्क्रॉलिंग और ऐप्लिकेशन का इंटरफ़ेस प्रतिक्रिया देते रहते हैं।
पहली बार पेज लोड होते समय स्पीच रिकग्निशन बिल्कुल लोड न करें
माइक्रोफ़ोन की सुविधा उन विज़िटर पर नेटवर्क और मेमोरी का बड़ा बोझ नहीं डालनी चाहिए जो इसे कभी इस्तेमाल ही नहीं करते।
Transformers.js को शुरुआती ऐप्लिकेशन बंडल में पहले से इम्पोर्ट न करें। पेज खुलते ही ONNX Runtime शुरू न करें और खाली समय में मॉडल वेट अपने-आप डाउनलोड न करें।
इसके बजाय रिकग्निशन से जुड़े हिस्से तभी लोड करें जब उपयोगकर्ता स्पष्ट रूप से माइक्रोफ़ोन बटन दबाए।
पहली बार इस्तेमाल के लिए उपयुक्त क्रम यह है:
- माइक्रोफ़ोन बटन पर क्लिक प्राप्त करें।
getUserMedia()से माइक्रोफ़ोन की अनुमति माँगें।- अनुमति न मिले तो मॉडल डाउनलोड किए बिना त्रुटि दिखाएँ।
- अनुमति मिल जाए तो मॉडल की तैयारी के दौरान अस्थायी माइक्रोफ़ोन स्ट्रीम रिलीज़ कर दें।
- अलग वर्कर को डायनेमिक तरीके से शुरू करें और चुना हुआ स्थानीय मॉडल लोड करें।
- वर्कर से रिकग्निशन पाइपलाइन तैयार होने की पुष्टि मिलने तक रुकें।
- उपयोगकर्ता ने प्रक्रिया रद्द न की हो तो रिकॉर्डिंग अपने-आप शुरू करें।
- रिकॉर्डिंग चलने की स्थिति साफ़ शब्दों में दिखाएँ।
अनुमति माँगने की कार्रवाई उपयोगकर्ता के इंटरैक्शन से जुड़ी होनी चाहिए। ऐप्लिकेशन को रद्द की गई प्रक्रियाएँ और अलग-अलग ब्राउज़र में ऑडियो कैप्चर की पाबंदियाँ भी संभालनी चाहिए; यह मानकर नहीं चलना चाहिए कि रिकॉर्डिंग हर बार चुपचाप दोबारा शुरू हो जाएगी।
मॉडल तैयार करते समय वॉइस इनपुट तैयार किया जा रहा है जैसा स्पष्ट स्टेटस दिखना चाहिए। बिना किसी सार्थक बदलाव के घूमता लोडिंग संकेत, सफल मॉडल डाउनलोड को भी ख़राब माइक्रोफ़ोन जैसा दिखा सकता है।
वर्कर एक बार शुरू होने के बाद अगली डिक्टेशन के लिए कुछ समय उपलब्ध रखा जा सकता है। लगभग एक मिनट का निष्क्रियता टाइमआउट एक उचित शुरुआती विकल्प है। इसके बाद वर्कर बंद करके मेमोरी में मौजूद मॉडल रिलीज़ कर दें। ब्राउज़र कैश में रखी मॉडल फ़ाइलें अगली विज़िट पर फिर इस्तेमाल की जा सकती हैं।
AudioWorklet से रॉ ऑडियो कैप्चर करें
Whisper को संख्यात्मक ऑडियो सैंपल चाहिए। MediaRecorder ऑडियो फ़ाइल रिकॉर्ड करने के लिए बहुत अच्छा है, लेकिन स्थानीय स्पीच रिकग्निशन में उसे इस्तेमाल करने से एनकोड और डिकोड करने का अनावश्यक अतिरिक्त चरण जुड़ सकता है।
प्रोसेसिंग का सरल रास्ता यह है:
getUserMedia()
|
v
AudioContext
|
v
AudioWorklet
|
v
Float32 PCM buffers
|
v
Dedicated WorkerAudioWorklet ऑडियो प्रोसेसिंग का कोड ऐप्लिकेशन के मुख्य JavaScript थ्रेड से अलग चलाता है। यह PCM सैंपल कैप्चर कर सकता है, कई चैनलों को एक चैनल में मिला सकता है, ध्वनि के स्तर का सरल आकलन कर सकता है और जमा किए गए बफ़र वर्कर को भेज सकता है।
माइक्रोफ़ोन शुरू करने का सरल उदाहरण:
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
channelCount: { ideal: 1 },
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
},
});
const context = new AudioContext();
await context.audioWorklet.addModule(
'/assets/speech/pcm-worklet.js'
);
const source = context.createMediaStreamSource(stream);
const processor = new AudioWorkletNode(
context,
'pcm-capture'
);
source.connect(processor);
// Connect the worklet through an inaudible output path
// if required to keep the audio processing graph active.माइक्रोफ़ोन से जुड़ी शर्तें प्राथमिकताएँ हैं, गारंटी नहीं। ब्राउज़र इको कैंसलेशन, शोर कम करने और ऑटोमैटिक गेन कंट्रोल के लिए अलग-अलग एल्गोरिदम इस्तेमाल कर सकते हैं।
एक अहम बात यह है कि माइक्रोफ़ोन का सैंपल रेट ज़रूरी नहीं कि 16 kHz हो। कई ऑडियो कॉन्टेक्स्ट 44.1 kHz या 48 kHz पर चलते हैं। Whisper को 16 kHz इनपुट चाहिए, इसलिए कैप्चर किए गए सैंपल को सही तरीके से री-सैंपल करना होता है।
हर दूसरे या तीसरे सैंपल को छोड़ देना आम तौर पर सही समाधान नहीं है। विंडोड-सिंक जैसे उपयुक्त लो-पास री-सैंपलर से सैंपल रेट घटाते समय एलियासिंग कम होती है और बोलचाल की उपयोगी फ़्रीक्वेंसी बची रहती हैं।
री-सैंपलिंग का काम रियल-टाइम AudioWorklet कॉलबैक में करने के बजाय अलग वर्कर में रखें।
ब्राउज़र थ्रेड के बीच ऑडियो कुशलता से भेजें
AudioWorklet ऑडियो के बहुत सारे छोटे टुकड़े बना सकता है। हर ऑडियो रेंडरिंग क्वांटम को अलग संदेश में भेजने से अनावश्यक ओवरहेड हो सकता है।
व्यावहारिक तरीका है कि PCM के छोटे बैच इकट्ठा किए जाएँ और हर बैच को ArrayBuffer की तरह ट्रांसफ़र किया जाए।
worker.postMessage(
{
type: 'audio-chunk',
buffer,
},
[buffer]
);ट्रांसफ़र सूची अहम है: यह ArrayBuffer की पूरी अनावश्यक कॉपी बनाने के बजाय उसका स्वामित्व दूसरे हिस्से को सौंप देती है।
वर्कर इन टुकड़ों को ऑडियो एक्युमुलेटर में जोड़ सकता है और रिकॉर्डिंग रुकने पर इन्हें एक साथ मिला सकता है।
वास्तविक कैप्चर सैंपल रेट, जमा हुए सैंपल की संख्या और रिकॉर्डिंग की अवधि का रिकॉर्ड रखना भी उपयोगी है। यह मेटाडेटा संवेदनशील ऑडियो लॉग किए बिना खाली या मौन रिकॉर्डिंग की समस्या समझने में मदद करता है।
अलग न्यूरल नेटवर्क के बिना बोलने के बीच के विराम पहचानें
छोटे टेक्स्ट मैसेज की डिक्टेशन के लिए न्यूरल वॉइस एक्टिविटी डिटेक्टर अनिवार्य नहीं है।
एडैप्टिव RMS डिटेक्टर से कम संसाधनों में बोलने की गतिविधि का अनुमान लगाया जा सकता है। RMS सिग्नल का एम्प्लिट्यूड मापता है, जिससे ऐप्लिकेशन अनुमान लगा सकता है कि पहले बोल रहा व्यक्ति कब शांत हो गया है।
इंटरफ़ेस में माइक्रोफ़ोन बटन दोबारा दबाकर रिकॉर्डिंग मैन्युअल रूप से रोकने का विकल्प हमेशा होना चाहिए। चुप्पी अपने-आप पहचानना सुविधा है, सफल रिकग्निशन की अनिवार्य शर्त नहीं।
छोटी डिक्टेशन के लिए उचित शुरुआती मान ये हो सकते हैं:
| पैरामीटर | शुरुआती मान |
|---|---|
| विश्लेषण का अंतराल | 20 ms |
| शोर का कैलिब्रेशन | 300 ms |
| बोलने की पुष्टि | 140 ms |
| अपने-आप रुकने से पहले की चुप्पी | 1.4 सेकंड |
| बिना बोले टाइमआउट | 8 सेकंड |
| रिकॉर्डिंग की अधिकतम अवधि | 30 सेकंड |
ये कॉन्फ़िगरेशन शुरू करने के लिए मान हैं, हर स्थिति में आदर्श थ्रेशहोल्ड नहीं। धीमी आवाज़, बैकग्राउंड संगीत और माइक्रोफ़ोन की शोर प्रोसेसिंग RMS माप को प्रभावित कर सकते हैं।
वाक्य के आख़िरी शब्द अपने-आप कट जाने के बजाय उपयोगकर्ता को रिकॉर्डिंग मैन्युअल रूप से रोकने देना बेहतर है।
सामान्य छोटे टेक्स्ट इनपुट के लिए अंतरिम नतीजे पाने को Whisper बार-बार चलाने के बजाय एक बार ट्रांसक्रिप्शन करना आम तौर पर सरल है। बोले गए हिस्से को रिकॉर्ड करें, रोकें, मॉडल एक बार चलाएँ और अंतिम ट्रांसक्रिप्ट जोड़ दें।
पूरी Whisper पाइपलाइन के लिए केवल ONNX मॉडल काफ़ी नहीं
यह इम्प्लीमेंटेशन की सबसे महत्वपूर्ण बातों में से एक है।
Whisper इन्फ़रेंस केवल ONNX एन्कोडर के बाद ONNX डिकोडर चलाने का नाम नहीं है। पूरी ऑटोमैटिक स्पीच रिकग्निशन पाइपलाइन में ऑडियो फ़ीचर निकालना, टोकनाइज़ेशन, जनरेशन कॉन्फ़िगरेशन और अनुमानित टोकन को पढ़े जा सकने वाले टेक्स्ट में डिकोड करना भी शामिल है।
प्रोसेसिंग की पूरी श्रृंखला यह है:
Raw microphone PCM
|
v
Resampling to 16 kHz
|
v
Whisper feature extractor
|
v
ONNX encoder
|
v
ONNX decoder and token generation
|
v
Whisper tokenizer
|
v
Recognized textONNX सेशन सफलतापूर्वक शुरू होने से केवल इतना साबित होता है कि मॉडल ग्राफ़ लोड हो सकता है। इससे पूरी रिकग्निशन पाइपलाइन के काम करने की पुष्टि नहीं होती।
एक परीक्षण किए गए Transformers.js इंटीग्रेशन में मॉडल सेशन शुरू हो जाते थे, फिर भी ट्रांसक्रिप्शन विफल होता था, क्योंकि स्थानीय रूप से बनाई गई पाइपलाइन में आवश्यक प्रोसेसर और टोकनाइज़र सही तरह कॉन्फ़िगर नहीं थे।
संगत इम्प्लीमेंटेशन में स्थानीय मॉडल मेटाडेटा की मदद से इन हिस्सों को स्पष्ट रूप से शुरू किया गया।
नीचे उस खास संस्करण वाले तरीके का संक्षिप्त उदाहरण है, पूरा स्वतंत्र वर्कर नहीं:
import {
WhisperTokenizer,
WhisperFeatureExtractor,
WhisperProcessor,
} from '@huggingface/transformers';
// These values come from local model configuration files.
const tokenizer = new WhisperTokenizer(
tokenizerJson,
tokenizerConfig
);
const featureExtractor = new WhisperFeatureExtractor(
preprocessorConfig
);
const processor = new WhisperProcessor(
{},
{
tokenizer,
feature_extractor: featureExtractor,
},
''
);
// In the affected version-specific integration:
recognizer.tokenizer = tokenizer;
recognizer.processor = processor;कंस्ट्रक्टर के सही सिग्नेचर और इंटीग्रेशन के तरीके को इंस्टॉल किए गए Transformers.js संस्करण के हिसाब से जाँचना चाहिए। कुछ संस्करण और मॉडल एक्सपोर्ट ये हिस्से अपने-आप बना सकते हैं; उन्हें मैन्युअल रूप से बनाना हर स्थिति में ज़रूरी नहीं मानना चाहिए।
पूरी पाइपलाइन उपलब्ध होने पर भाषा और काम स्पष्ट रूप से बताकर छोटे ऑडियो का ट्रांसक्रिप्शन चलाया जा सकता है:
const result = await recognizer(audio16k, {
language: 'en',
task: 'transcribe',
return_timestamps: false,
});जब ऐप्लिकेशन को उपयोगकर्ता की भाषा पहले से पता हो, तो उसे स्पष्ट रूप से पास करने से अनावश्यक भाषा पहचान बचती है और छोटे ऑडियो के नतीजे अधिक अनुमानित हो सकते हैं।
Transformers.js और ONNX Runtime के संगत संस्करण निश्चित रखें
दूसरी महत्वपूर्ण समस्या Transformers.js और ONNX Runtime Web के आपसी संबंध से जुड़ी है।
Transformers.js ऊपरी स्तर की रिकग्निशन पाइपलाइन उपलब्ध कराता है, जबकि ONNX Runtime Web न्यूरल नेटवर्क ग्राफ़ चलाता है। इन पैकेजों का एक-दूसरे से और इस्तेमाल किए जा रहे खास ONNX मॉडल एक्सपोर्ट से संगत होना ज़रूरी है।
जाँचे गए इम्प्लीमेंटेशन में ONNX Runtime Web के एक डेवलपमेंट बिल्ड ने Whisper एन्कोडर शुरू कर दिया, लेकिन क्वांटाइज़्ड डिकोडर सेशन बनाते समय विफल हो गया। त्रुटि DequantizeLinear ऑपरेशन में ग़ायब क्वांटाइज़ेशन स्केल से संबंधित थी।
यह माइक्रोफ़ोन अनुमति, डाउनलोड या ऑडियो फ़ॉर्मैट की समस्या नहीं थी। ब्राउज़र मॉडल फ़ाइलें प्राप्त कर सकता था, लेकिन रनटाइम आवश्यक मॉडल ग्राफ़ में से एक को शुरू नहीं कर पा रहा था।
स्थिर और निश्चित ONNX Runtime Web रिलीज़ के साथ एन्कोडर और डिकोडर दोनों को सीधे जाँचने से उस विशेष असंगतता का समाधान हो गया।
संगतता की यह जाँच खास तौर पर उपयोगी थी क्योंकि इससे दो अलग दावों का अंतर साफ़ हुआ:
- मॉडल फ़ाइल सफलतापूर्वक डाउनलोड हुई।
- रनटाइम ने उस मॉडल फ़ाइल से इस्तेमाल करने योग्य इन्फ़रेंस सेशन सफलतापूर्वक बनाया।
ये दोनों एक जैसी बातें नहीं हैं।
जाँचे गए मॉडल एक्सपोर्ट में Transformers.js 4.2.0 के साथ ONNX Runtime Web 1.24.3 का संयोजन संगत था। नए डेवलपमेंट रनटाइम में क्वांटाइज़्ड डिकोडर ग्राफ़ पर त्रुटि आई। यह किसी खास संस्करण से जुड़ा अवलोकन है, इस बात का प्रमाण नहीं कि पुराने रनटाइम हमेशा बेहतर होते हैं।
किसी अप्रत्यक्ष रनटाइम डिपेंडेंसी को ओवरराइड करते समय सटीक संस्करण पैकेज लॉकफ़ाइल में निश्चित करें और इसका कारण दर्ज करें। रनटाइम अपग्रेड करने के लिए ब्राउज़र में पूरी इन्फ़रेंस पाइपलाइन का रिग्रेशन टेस्ट ज़रूरी होना चाहिए; केवल पैकेज का सफल इंस्टॉल होना काफ़ी नहीं है।
Whisper की डिबगिंग से पहले वर्कर बंडल का फ़ॉर्मैट जाँचें
JavaScript Web Worker को अलग-अलग मॉड्यूल फ़ॉर्मैट में बनाया जा सकता है।
एक इंटीग्रेशन समस्या इसलिए आई कि ऐप्लिकेशन वर्कर को ES मॉड्यूल की तरह शुरू कर रहा था, जबकि तैयार वर्कर बंडल क्लासिक वर्कर का importScripts() तरीका इस्तेमाल करता था।
इन दोनों को चलाने के तरीके एक-दूसरे की जगह इस्तेमाल नहीं किए जा सकते। मॉड्यूल वर्कर केवल क्लासिक वर्कर के लिए बने कोड को सीधे नहीं चला सकता।
व्यावहारिक सीख यह है कि किसी सामान्य ट्यूटोरियल से कंस्ट्रक्टर का विकल्प कॉपी करने के बजाय Worker कंस्ट्रक्टर को वास्तव में बने बंडल के अनुरूप रखें।
उदाहरण के लिए, नीचे दिए गए दोनों विकल्प अपने-आप एक जैसे नहीं हैं:
// Module worker: requires a compatible module bundle.
new Worker(workerUrl, { type: 'module' });
// Classic worker: requires a compatible classic bundle.
new Worker(workerUrl);सही चुनाव बंडलर और उससे तैयार JavaScript पर निर्भर करता है।
वर्कर शुरू होने में त्रुटि मिले तो ब्राउज़र का Console, तैयार वर्कर फ़ाइल और Network पैनल जाँचें। पता करें कि विफलता ONNX Runtime का आरंभ शुरू होने से भी पहले तो नहीं हो रही।
कुछ बंडल की गई ब्राउज़र लाइब्रेरी अपनी मॉड्यूल लोकेशन के आधार पर URL भी निकालती हैं। उन्हें वर्कर के भीतर पैक करते समय यह मानने के बजाय रनटाइम एसेट के वास्तविक पाथ जाँचें कि पुराने पैकेज-रिलेटिव URL अब भी सही काम करेंगे।
WASM और JavaScript मॉड्यूल सही MIME टाइप के साथ सर्व करें
JavaScript और ONNX मॉडल फ़ाइलें पूरी तरह संगत हों तब भी वेब सर्वर ग़लत रिस्पॉन्स हेडर लौटाए तो वे विफल हो सकती हैं।
एक वास्तविक समस्या में .mjs एक्सटेंशन वाला JavaScript मॉड्यूल application/octet-stream के रूप में सर्व किया जा रहा था। ब्राउज़र ने मॉड्यूल को अस्वीकार कर दिया क्योंकि रिस्पॉन्स का टाइप JavaScript मॉड्यूल चलाने के लिए उपयुक्त नहीं था।
आम तौर पर अपेक्षित रिस्पॉन्स टाइप ये हैं:
| फ़ाइल एक्सटेंशन | Content-Type |
|---|---|
| .mjs | application/javascript |
| .wasm | application/wasm |
सिर्फ़ सर्वर की कॉन्फ़िगरेशन फ़ाइल नहीं, प्रोडक्शन में वास्तव में लौट रहा रिस्पॉन्स जाँचें।
curl -I https://example.com/runtime/speech/v2/runtime.mjs
curl -I https://example.com/runtime/speech/v2/runtime.wasmऊपर दिए गए URL केवल उदाहरण हैं। चुने गए रनटाइम बिल्ड से सही संस्करण वाले एसेट फ़ाइलनाम इस्तेमाल करें।
सही HTTP स्टेटस मिलना अकेले पर्याप्त नहीं है। MIME टाइप, Content Security Policy, मॉड्यूल चलाने के तरीके या ग़लत एसेट URL के कारण ब्राउज़र फिर भी फ़ाइल अस्वीकार कर सकता है।
ध्यान रखें कि कोई रिक्वेस्ट सीधे ब्राउज़र टैब में खोलने पर सफल हो सकती है, लेकिन उसे चलने वाले मॉड्यूल या WebAssembly डिपेंडेंसी के रूप में लोड करते समय विफल हो सकती है।
अपरिवर्तनीय एसेट URL इस्तेमाल करें, लेकिन उपयोगकर्ता को ख़राब रनटाइम फ़ाइलों में न फँसाएँ
Whisper की मॉडल फ़ाइलें इतनी बड़ी होती हैं कि ब्राउज़र कैशिंग अहम हो जाती है।
संस्करण वाले अपरिवर्तनीय एसेट पाथ की मदद से ब्राउज़र हर बार उपयोगकर्ता के बोलने पर डाउनलोड करने के बजाय अपरिवर्तित मॉडल और रनटाइम फ़ाइलें दोबारा इस्तेमाल कर सकता है।
सामान्य कैश नीति इस तरह होती है:
Cache-Control: public, max-age=31536000, immutableलेकिन इससे डिप्लॉयमेंट की एक शर्त जुड़ती है: एक बार URL को अपरिवर्तनीय घोषित कर देने के बाद उसकी सामग्री बिना बताए नहीं बदलनी चाहिए।
रनटाइम की बाइनरी बदलनी पड़े तो उसे नए संस्करण वाले URL पर प्रकाशित करें।
/models/speech/v1/
/runtime/speech/v1/
/runtime/speech/v2/रनटाइम पाथ बदलने का उद्देश्य सिर्फ़ नाम बदलना नहीं है। सामग्री बदलकर भी ऐप्लिकेशन वही अपरिवर्तनीय URL रखे, तो ब्राउज़र पुराने कैश से असंगत WebAssembly बाइनरी इस्तेमाल करता रह सकता है।
JavaScript ग्लू कोड और WebAssembly फ़ाइलें एक ही रनटाइम वितरण से रखना भी उपयोगी है। अलग-अलग संस्करणों की रनटाइम फ़ाइलें मिलाने से ऐसी इनिशियलाइज़ेशन त्रुटियाँ हो सकती हैं जिनका कारण ढूँढ़ना मुश्किल होता है।
बिल्ड के समय तैयार मैनिफ़ेस्ट में अपेक्षित फ़ाइलें, उनके आकार और क्रिप्टोग्राफ़िक चेकसम रखने से डिप्लॉयमेंट से पहले ग़ायब या मेल न खाने वाली एसेट फ़ाइलें पकड़ी जा सकती हैं।
रिकग्निशन लाइब्रेरी को अपनी साइट पर होस्ट की गई एसेट फ़ाइलें इस्तेमाल करने के लिए कॉन्फ़िगर करें और दूर से मॉडल लोड करना बंद करें। तब स्थानीय मॉडल न मिले तो किसी बाहरी मॉडल होस्ट से अनचाहा डाउनलोड शुरू होने के बजाय स्पष्ट त्रुटि मिलेगी।
कुछ भी काम न करते हुए भी माइक्रोफ़ोन लोडिंग क्यों दिखाता है
डेवलपमेंट के दौरान आम लक्षण यह है कि माइक्रोफ़ोन बटन अनुमति माँगता है, लोडिंग ऐनिमेशन दिखाता है और आख़िर में कोई टेक्स्ट डाले बिना त्रुटि की स्थिति में लौट आता है।
ब्राउज़र यहाँ तक दिखा सकता है कि माइक्रोफ़ोन इस्तेमाल हो रहा है।
इससे साबित नहीं होता कि स्पीच रिकग्नाइज़र काम कर रहा है। यह केवल बताता है कि किसी समय माइक्रोफ़ोन कैप्चर की अनुमति मिली थी या वह सक्रिय था।
कई स्वतंत्र समस्याओं से लगभग एक जैसे लक्षण दिखाई दे सकते हैं:
- JavaScript मॉड्यूल का ग़लत MIME टाइप। ब्राउज़र रनटाइम मॉड्यूल डाउनलोड करता है, लेकिन उसे चलाने से इनकार कर देता है।
- वर्कर के फ़ॉर्मैट का मेल न खाना। तैयार वर्कर क्लासिक वर्कर API इस्तेमाल करता है लेकिन मॉड्यूल वर्कर के रूप में शुरू होता है, या इसका उलटा होता है।
- ONNX डिकोडर की असंगतता। रनटाइम एन्कोडर सेशन बना लेता है, लेकिन क्वांटाइज़्ड डिकोडर ग्राफ़ पर विफल हो जाता है।
- अधूरी रिकग्निशन पाइपलाइन। ONNX सेशन मौजूद हैं, लेकिन टोकनाइज़र या ऑडियो प्रोसेसर सही तरह शुरू नहीं हुआ।
इन समस्याओं की अलग-अलग जाँच ज़रूरी है। एक समस्या ठीक करने पर दूसरी सामने आ सकती है।
डिबगिंग का सबसे अहम सिद्धांत है कि HTTP 200 मिलने का मतलब मॉडल का सफलतापूर्वक शुरू होना नहीं है, और मॉडल के शुरू होने का मतलब सफल ट्रांसक्रिप्शन नहीं है।
यहाँ तक कि सफल ट्रांसक्रिप्शन से भी यह साबित नहीं होता कि नतीजा ऐप्लिकेशन के असली टेक्स्ट फ़ील्ड तक पहुँचा है।
हर चरण के बीच स्पष्ट डायग्नॉस्टिक संकेत होना चाहिए।
निजी बातचीत जुटाए बिना मॉनिटरिंग और डायग्नॉस्टिक्स जोड़ें
काम के डायग्नॉस्टिक लॉग रखने के लिए उपयोगकर्ता की कही बातें रिकॉर्ड करना ज़रूरी नहीं है।
ऐप्लिकेशन निम्न तकनीकी स्थितियों को ट्रैक कर सकता है:
permission-granted
worker-created
runtime-loading
runtime-ready
encoder-ready
decoder-ready
recording-started
pcm-received
recording-stopped
inference-started
inference-completed
result-insertedत्रुटि होने पर उसका चरण, स्थिर तकनीकी त्रुटि कोड और संवेदनशील जानकारी हटाया हुआ त्रुटि मेटाडेटा दर्ज करें।
मॉडल के शुरू न होने की समस्या को माइक्रोफ़ोन अनुमति न मिलने या ख़ाली रिकॉर्डिंग से अलग पहचान सकना चाहिए। डेवलपमेंट में वर्कर की त्रुटि दिखाई देनी चाहिए, किसी सामान्य एरर हैंडलर में दब नहीं जानी चाहिए।
माइक्रोफ़ोन सैंपल, पहचाने गए वाक्य, टोकन सीक्वेंस या बोलने की अन्य सामग्री लॉग न करें। परफ़ॉर्मेंस टेलीमेट्री में अवधि के समूह, चुनी गई मॉडल श्रेणी, शुरू होने का समय, इन्फ़रेंस समय और त्रुटि श्रेणियाँ रखी जा सकती हैं।
इससे प्राइवेसी की शर्तें कमज़ोर किए बिना ख़राब माइक्रोफ़ोन का कारण जानना कहीं आसान हो जाता है।
React स्टेट के ज़रिए ट्रांसक्रिप्ट इनपुट में डालें
स्पीच रिकग्निशन तभी उपयोगी है जब अंतिम ट्रांसक्रिप्ट मौजूदा फ़ॉर्म फ़ील्ड तक पहुँचे।
React ऐप्लिकेशन में इस फ़ील्ड की वैल्यू अक्सर कंपोनेंट स्टेट से नियंत्रित होती है। सीधे input.value बदलना फ़्रेमवर्क के रेंडरिंग व्यवहार से टकरा सकता है।
इसके बजाय पहचाना गया टेक्स्ट उसी स्टेट अपडेट के रास्ते से जाना चाहिए जो सामान्य टाइपिंग में इस्तेमाल होता है।
इंटीग्रेशन में असिंक्रोनस व्यवहार का भी ध्यान रखना ज़रूरी है। संभव है कि उपयोगकर्ता अतिरिक्त अक्षर टाइप कर चुका हो या कर्सर हटा चुका हो और उसके बाद रिकग्निशन पूरा हो।
रिकॉर्डिंग शुरू होने पर लिया गया पुराना इनपुट मान इस्तेमाल करने से उपयोगकर्ता के बाद के बदलाव ग़लती से मिट सकते हैं।
सरल इंटीग्रेशन पैटर्न इस तरह है:
const insertion = insertTranscript({
currentValue: latestValue,
transcript: recognizedText,
selection: currentSelection,
maxLength: inputLimit,
});
setMessage(insertion.value);
restoreCaret(insertion.caret);टेक्स्ट डालने वाला हेल्पर मौजूदा टेक्स्ट बचाकर रखे, ज़रूरत के हिसाब से चुना हुआ हिस्सा बदले, उचित स्पेस लगाए और फ़ील्ड की अधिकतम लंबाई का पालन करे।
Unicode पर ध्यान देना ज़रूरी है क्योंकि JavaScript में स्ट्रिंग की लंबाई UTF-16 कोड यूनिट में गिनी जाती है। मनमानी जगह स्ट्रिंग काटने से सरोगेट पेयर बीच से टूट सकता है।
सबसे ज़रूरी बात: स्पीच रिकग्निशन कभी अपने-आप फ़ॉर्म सबमिट नहीं करना चाहिए। भेजने से पहले उपयोगकर्ता को टेक्स्ट देखने और सुधारने का मौका मिलना चाहिए।
सामान्य टेक्स्ट इनपुट को प्रभावित किए बिना त्रुटियाँ संभालें
स्पीच वाला सबसिस्टम वैकल्पिक होना चाहिए।
यदि माइक्रोफ़ोन अनुमति न मिले, ब्राउज़र मॉडल के लिए मेमोरी आवंटित न कर पाए, वर्कर क्रैश हो जाए या रिकॉर्डिंग में आवाज़ न मिले, तब भी सामान्य टेक्स्ट फ़ील्ड काम करता रहना चाहिए।
माइक्रोफ़ोन के पूरे जीवनचक्र को स्पष्ट स्टेट से दिखाएँ, जैसे:
idle
preparing
listening
recognizing
errorऐसे लेबल इस्तेमाल करें जो एक्सेसिबल हों और स्थिति की जानकारी साफ़ दिखाई दे। लोडिंग के बाद माइक्रोफ़ोन का केवल लाल या गुलाबी हो जाना उपयोगकर्ता को लगभग कुछ नहीं बताता।
रद्द करने पर ऑडियो ट्रैक, ऑडियो कॉन्टेक्स्ट, बचे हुए बफ़र और सक्रिय रिकॉर्डिंग सेशन साफ़ होने चाहिए। इंटरफ़ेस को यह भी रोकना चाहिए कि पहले रद्द की गई रिकॉर्डिंग का देर से आया नतीजा नए इनपुट स्टेट को ओवरराइट कर दे।
बड़ा मॉडल मेमोरी या इनिशियलाइज़ेशन की बाधा से विफल हो तो छोटा मॉडल आज़माने से पहले उसे पूरी तरह रिलीज़ करें। दोबारा कोशिश की संख्या सीमित रखें ताकि ब्राउज़र न चल पाने वाले मॉडल बार-बार डाउनलोड या शुरू करने की कोशिश न करे।
स्थानीय पहचान विफल होने पर बिना बताए क्लाउड स्पीच सेवा पर न जाएँ। यदि केवल स्थानीय प्रोसेसिंग की सख़्त शर्त है, तो स्थानीय रिकग्नाइज़र की विफलता पर पूरी प्रक्रिया भी स्थानीय स्तर पर विफल होनी चाहिए।
ब्राउज़र की पूरी पाइपलाइन का परीक्षण करें
TypeScript चेक या प्रोडक्शन बिल्ड सफल होने से यह साबित नहीं होता कि वॉइस इनपुट काम करता है।
इसी तरह ब्राउज़र ONNX फ़ाइलें डाउनलोड कर सकता है, इससे दोनों मॉडल सेशन के सफलतापूर्वक शुरू होने की पुष्टि नहीं होती।
सबसे उपयोगी रिग्रेशन टेस्ट में वही वर्कर, रनटाइम फ़ाइलें, मॉडल, प्रोसेसर और टोकनाइज़र चलने चाहिए जो प्रोडक्शन में इस्तेमाल होते हैं।
किसी ज्ञात छोटी PCM रिकॉर्डिंग को असली ब्राउज़र इन्फ़रेंस पाथ में दें और सुनिश्चित करें कि नतीजे में ख़ाली न होने वाला ट्रांसक्रिप्ट मिलता है।
फिर वास्तविक ब्राउज़र में माइक्रोफ़ोन के पूरे इंटरैक्शन का परीक्षण करें।
- पहली बार पेज लोड: माइक्रोफ़ोन इस्तेमाल होने से पहले कोई स्पीच मॉडल या रनटाइम एसेट डाउनलोड न हो, यह जाँचें।
- कोल्ड स्टार्ट: माइक्रोफ़ोन अनुमति, मॉडल रिक्वेस्ट, JavaScript और WASM के MIME टाइप तथा वर्कर से सचमुच तैयार होने का जवाब जाँचें।
- ONNX की शुरुआत: एन्कोडर और डिकोडर दोनों सेशन बन सकें, यह सुनिश्चित करें।
- ऑडियो कैप्चर: AudioWorklet से ख़ाली न होने वाले PCM बफ़र बनें, यह जाँचें।
- री-सैंपलिंग: वर्कर को वास्तविक सैंपल रेट मिले और उससे सही 16 kHz ऑडियो बने, यह जाँचें।
- मैन्युअल स्टॉप: माइक्रोफ़ोन पर दूसरी बार क्लिक करने से रिकॉर्डिंग भरोसेमंद तरीके से रुके, यह सुनिश्चित करें।
- ऑटोमैटिक स्टॉप: बोलने के स्वाभाविक विराम पर धीमी आवाज़ के शब्द काटे बिना रिकॉर्डिंग समाप्त हो, यह जाँचें।
- ट्रांसक्रिप्शन: पूरा प्रोसेसर, मॉडल और टोकनाइज़र मिलकर पहचाने जा सकने वाला टेक्स्ट दें, यह जाँचें।
- इनपुट इंटीग्रेशन: पहचाना गया टेक्स्ट असली कंट्रोल्ड फ़ील्ड में दिखे और उपयोगकर्ता के बदलाव न मिटें, यह सुनिश्चित करें।
- समस्या के बाद रिकवरी: अनुमति अस्वीकार होना, फ़ाइल ग़ायब होना, वर्कर त्रुटियाँ, मॉडल शुरू न होना, रद्द करना और छोटे मॉडल पर लौटना जाँचें।
- प्राइवेसी: नेटवर्क रिक्वेस्ट देखें और सुनिश्चित करें कि माइक्रोफ़ोन ऑडियो किसी सर्वर या एनालिटिक्स सेवा को अपलोड नहीं होता।
- मोबाइल संगतता: कोल्ड स्टार्ट, वॉर्म स्टार्ट और लगातार कई डिक्टेशन सेशन समेत ये टेस्ट असली फ़ोन पर दोहराएँ।
पहले से रिकॉर्ड किए गए PCM वाले टेस्ट इन्फ़रेंस स्टैक की जाँच के लिए बहुत अच्छे हैं, लेकिन उनसे माइक्रोफ़ोन कैप्चर के काम करने की पुष्टि नहीं होती। अंतिम स्वीकार्यता परीक्षण में असली माइक्रोफ़ोन और वास्तविक यूज़र इंटरफ़ेस शामिल होना चाहिए।
ब्राउज़र में भरोसेमंद वॉइस टाइपिंग के लिए व्यावहारिक आर्किटेक्चर
जिस वेबसाइट को छोटी, बहुभाषी और प्राइवेसी बनाए रखने वाली वॉइस इनपुट सुविधा चाहिए, उसके लिए सबसे व्यावहारिक आर्किटेक्चर जानबूझकर सरल रखा जाता है:
React microphone button
|
v
Permission request
|
v
Lazy model initialization
|
v
AudioWorklet PCM capture
|
v
Adaptive RMS / manual stop
|
v
Dedicated Worker
|
v
16 kHz audio resampling
|
v
Whisper Tiny or Base Q8
|
v
Compatible ONNX Runtime WASM
|
v
Whisper processor + tokenizer
|
v
Final transcript
|
v
Controlled React inputऐप्लिकेशन को संस्करण वाली मॉडल और रनटाइम फ़ाइलें खुद होस्ट करनी चाहिए, इन्फ़रेंस मुख्य थ्रेड से बाहर चलाना चाहिए, अनावश्यक मॉडल डाउनलोड रोकने चाहिए, उपयोग के बाद संसाधन रिलीज़ करने चाहिए और पहचान के लिए माइक्रोफ़ोन का ऑडियो कभी अपलोड नहीं करना चाहिए।
WebGPU से तेज़ी लाना, न्यूरल वॉइस एक्टिविटी डिटेक्शन, लगातार आंशिक ट्रांसक्रिप्शन या अतिरिक्त मॉडल स्तर जैसे जटिल विकल्प बाद में तब जाँचे जा सकते हैं जब माप से उनकी अतिरिक्त जटिलता उचित साबित हो।
सबसे अहम सीख किसी खास लाइब्रेरी संस्करण या मॉडल के आकार से जुड़ी नहीं है। असली बात है ब्राउज़र की स्पीच रिकग्निशन पाइपलाइन के हर चरण के बीच की सीमा को जाँचना।
माइक्रोफ़ोन की अनुमति माँगने वाला डायलॉग सही चल रहा हो, तो इसका मतलब नहीं कि ऑडियो ट्रांसक्राइब हो रहा है। मॉडल डाउनलोड होने का मतलब नहीं कि डिकोडर शुरू हुआ। ONNX सेशन चलने का मतलब नहीं कि टोकनाइज़र उपलब्ध है। और सफल ट्रांसक्रिप्ट का मतलब भी नहीं कि ऐप्लिकेशन ने टेक्स्ट सही जगह डाला।
सुविधा तभी पूरी मानी जा सकती है जब विज़िटर माइक्रोफ़ोन दबाकर सामान्य ढंग से बोल सके, रिकॉर्डिंग रोक सके और उसे पहचाने गए शब्द संपादित किए जा सकने वाले फ़ील्ड में दिखें—वह भी रिकॉर्डिंग को किसी स्पीच रिकग्निशन सर्वर पर भेजे बिना।