सभी लेख

· Casimiro Ferreira· 11 मिनट पढ़ें

ख़राब ऑडियो साफ़ करना: audiosronnx में डिनॉइज़िंग और बैंडविड्थ विस्तार

  • ONNX
  • denoising
  • bandwidth extension
  • speech

एक रिकॉर्डिंग दो अलग-अलग तरीक़ों से ख़राब हो सकती है, और उनके समाधान ओवरलैप नहीं करते।

पहला तरीक़ा: पृष्ठभूमि शोर वाणी के ऊपर बैठा है — ट्रैफ़िक, एक पंखा, कमरे की गुनगुनाहट। जो सिग्नल मायने रखता है वह मौजूद है; उसमें कुछ और मिला दिया गया है। उसे हटाना डिनॉइज़िंग है।

दूसरा तरीक़ा: रिकॉर्डिंग ने शुरू में ही पूरा सिग्नल कभी कैप्चर नहीं किया। टेलीफ़ोन ऑडियो 8,000 सैंपल प्रति सेकंड (8 kHz) पर सैंपल किया जाता है; एक पूर्ण-गुणवत्ता वाली रिकॉर्डिंग आमतौर पर 48 kHz होती है। सैंपल दर वह उच्चतम फ़्रीक्वेंसी तय करती है जिसे एक डिजिटल सिग्नल प्रतिनिधित्व कर सकता है, इसलिए एक 8 kHz कॉल में 4 kHz से ऊपर बिलकुल कोई सामग्री नहीं है — शांत नहीं, फ़िल्टर नहीं की गई, बस कभी रिकॉर्ड ही नहीं हुई। उस ऑडियो को फिर से भरा-पूरा सुनाना का मतलब है ऐसी प्रशंसनीय उच्च फ़्रीक्वेंसियाँ गढ़ना जो कभी कैप्चर नहीं हुईं। यह बैंडविड्थ विस्तार है।

audiosronnx इन्हें दो अलग-अलग एंट्री पॉइंटों वाली दो अलग-अलग समस्याओं की तरह मानता है, क्योंकि ग़लत का उपयोग करना ग़लत काम करता है। एक शोर भरे सिग्नल पर एक बैंडविड्थ एक्सटेंडर चलाइए और यह शोर के एक उच्च-फ़्रीक्वेंसी संस्करण को वफ़ादारी से पुनर्निर्मित करेगा। डिनॉइज़िंग को पहले होना होगा।

from audiosronnx import load_denoise, load_sr

clean, rate = load_denoise("dpdfnet").denoise("noisy_call.wav")   # remove noise
wide, _     = load_sr("lavasr").upscale(clean, rate)              # extend to 48 kHz

load_denoise() और load_sr() एक-दूसरे के इंजनों को अस्वीकार करते हैं — load_denoise से एक बैंडविड्थ एक्सटेंडर माँगना चुपचाप ग़लत काम करने के बजाय एक त्रुटि उठाता है।

यह पहचान से पहले क्यों आता है

स्पीच रिकग्निशन और स्पीकर पहचान आमतौर पर तुलनात्मक रूप से साफ़ ऑडियो पर प्रशिक्षित होती हैं। एक रिकग्नाइज़र को 8 kHz टेलीफ़ोन वाणी, या नीचे चलते पंखे वाली वाणी दीजिए, और word error rate बढ़ जाती है — इसलिए नहीं कि मॉडल ख़राब है, बल्कि इसलिए कि इनपुट अब उस चीज़ जैसा नहीं दिखता जिस पर इसे प्रशिक्षित किया गया था। पहचान या डायराइज़ेशन के लिए इस्तेमाल होने वाली स्पीकर एम्बेडिंग पर भी वही लागू होता है: शोर और लापता बैंडविड्थ उसी सटीक ध्वनिक विवरण को विकृत करते हैं जिस पर वे एम्बेडिंग निर्भर करती हैं।

इससे सफ़ाई एक पाइपलाइन चरण बन जाता है जो पहचान से पहले बैठता है, इसका विकल्प नहीं। एक शोर भरी 8 kHz फ़ोन कॉल के लिए एक असली पाइपलाइन ऐसी दिखती है: डिनॉइज़ करें, फिर 48 kHz तक विस्तार करें, फिर परिणाम पर पहचान या स्पीकर ID चलाएँ। पहले इनपुट को ठीक किए बिना एक बेहतर रिकग्नाइज़र में बदलना ग़लत जगह पर मेहनत ख़र्च करना है — मॉडल उसी क्षतिग्रस्त सिग्नल पर बिगड़ता है चाहे वह कितना भी अच्छा हो।

इंजन रजिस्ट्री

audiosronnx दस डिनॉइज़र और सात बैंडविड्थ एक्सटेंडर भेजता है, सभी load_denoise() / load_sr() के ज़रिए नाम से लोड करने योग्य, सभी शुद्ध ONNX रनटाइम पर बिना torch के।

डिनॉइज़र:

इंजनदरआकारलाइसेंस
dpdfnet (डिफ़ॉल्ट)8/16/48 kHz8.7–14.9 MBApache-2.0
mossformer248 kHz229 MBApache-2.0
frcrn16 kHz57.5 MBApache-2.0
mpsenet16 kHz9.7 MBMIT
gtcrn16 kHz0.54 MBMIT
cmgan16 kHz7.8 MBMIT
metadenoiser16 kHz19–34 MBCC-BY-NC-4.0
mossformergan16 kHz17.7 MBApache-2.0
voicefixer44.1 kHz415 MBMIT
deepfilternet48 kHz~2 MBMIT

बैंडविड्थ एक्सटेंडर:

इंजनइनपुटआकारलाइसेंस
lavasr (डिफ़ॉल्ट)8–48 kHz~52 MBApache-2.0
novasr16 kHz~0.2 MBApache-2.0
flowhighकोई भी~200 MBMIT
hifiganbweकोई भी~4 MBMIT
apbweकोई भी (12 kHz बैंड)~120 MBMIT
sidon16 kHz~410 MBMIT
callenhancer8–16 kHz~3 GB / ~1.3 GB int8CC-BY-NC-4.0

लाइब्रेरी में सबसे छोटा मॉडल, gtcrn, 0.54 MB है। सबसे बड़ा, voicefixer, 415 MB है — लगभग 800 गुना बड़ा, और यह एक अलग काम करता है: यह एक रीस्टोरेशन मॉडल है जो शोर, reverb, clipping और लापता बैंडविड्थ को एक बार में एक समस्या के बजाय साथ में संभालता है।

अधिकांश वेट्स MIT या Apache-2.0 हैं। दो नहीं हैं: metadenoiser और callenhancer CC-BY-NC-4.0 के अंतर्गत भेजे जाते हैं, ग़ैर-व्यावसायिक। वह लाइसेंस वेट्स को कवर करता है, उनके साथ प्रोसेस किए गए ऑडियो को नहीं, और लाइब्रेरी इसे उपयोग के हर बिंदु पर बताती है — audiosronnx list इसे प्रति इंजन रिपोर्ट करता है। कुछ भी किसी कॉलर को अपने time-domain आर्किटेक्चर के लिए metadenoiser चुनने से नहीं रोकता, पर यह चुनाव जानबूझकर करना होता है।

रजिस्ट्री इसलिए मौजूद है क्योंकि कोई एक मॉडल हर रिकॉर्डिंग पर नहीं जीतता। dpdfnet डिफ़ॉल्ट है क्योंकि इसे किसी अतिरिक्त निर्भरता की ज़रूरत नहीं और यह एक मॉडल से 8, 16 और 48 kHz कवर करता है। mossformer2 फ़ुलबैंड इनपुट पर सबसे अच्छा मापा गया चुनाव है। mossformergan भेजे गए डिनॉइज़रों में सबसे उच्च प्रकाशित PESQ स्कोर (3.47) पोस्ट करता है। gtcrn वह चुनाव है जब बाधाकारी बंधन फ़ुटप्रिंट हो, 0.54 MB पर। ब्रॉडबैंड गॉसियन शोर वाले एक टेस्ट क्लिप पर, डिनॉइज़रों ने 19 dB इनपुट SNR पर 3.5 से 5.9 dB SNR वापस पाया, जो 5 dB के कठिन इनपुट पर 7.5–13.7 dB तक बढ़ जाता है। यह एक कृत्रिम, आक्रामक शोर मामला है: यह इंजनों को लगातार रैंक करता है पर बड़बड़ शोर या कोडेक विरूपणों के बारे में बहुत कम बताता है, यही ठीक कारण है कि रजिस्ट्री केवल विजेता भेजने के बजाय दस मॉडल रखती है।

cmgan जानबूझकर हारने के बावजूद रखे गए मॉडल का सबसे स्पष्ट मामला है: यह gtcrn द्वारा PESQ और SNR दोनों पर हावी होता है, जो चौदह गुना छोटा है, फिर भी बना रहता है — ताकि cmgan के विरुद्ध बनाए गए प्रकाशित परिणाम पुनरुत्पादनीय बने रहें और तुलना के लिए एक अलग आर्किटेक्चर उपलब्ध रहे।

बैंडविड्थ-विस्तार की तरफ़, sidon और callenhancer lavasr या novasr से एक अलग काम करते हैं: मौजूदा सिग्नल के ऊपर एक प्रशंसनीय उच्च बैंड जोड़ने के बजाय, वे एक न्यूरल वोकोडर के ज़रिए शुरू से वाणी को पुनःसंश्लेषित करते हैं, जो कोडेक क्षति को ठीक कर सकता है जिसे एक बैंड एक्सटेंडर छू नहीं सकता — बहुत अधिक कंप्यूटेशनल क़ीमत पर। callenhancer विशेष रूप से टेलीफ़ोनी ऑडियो पर प्रशिक्षित है, यही कारण है कि इसके वेट्स ग़ैर-व्यावसायिक लाइसेंस रखते हैं।

जो शामिल नहीं हो पाया

audiosronnx केवल तभी एक इंजन भेजता है जब यह एक अकेले स्थिर ONNX ग्राफ़ में निर्यात होता है, onnxruntime के ज़रिए CPU पर चलता है, एक स्पष्ट लाइसेंस रखता है, और मूल कार्यान्वयन के विरुद्ध शुरू से अंत तक सत्यापित किया गया है — केवल कच्चे मॉडल के विरुद्ध नहीं, क्योंकि एक ग्राफ़ जो नेटवर्क से मेल खाता है पर उसके आसपास के सामान्यीकरण से नहीं, ऐसा ऑडियो पैदा करता है जो सुनने में ठीक लगता है और चुपचाप ग़लत है।

प्रोजेक्ट का docs/not-shipped.md हर उस उम्मीदवार को दस्तावेज़ करता है जिसका इसने मूल्यांकन किया और अस्वीकार किया, विशेष कारण के साथ, जो इसे रिपॉज़िटरी के अधिक उपयोगी दस्तावेज़ों में से एक बनाता है क्योंकि यह दिखाता है कि “शुद्ध ONNX, केवल-CPU” आज असल में क्या कर सकता है, इसका दावा करने के बजाय।

इटरेटिव सैंपलरों के पास निर्यात करने के लिए कोई स्थिर ग्राफ़ नहीं है। डिफ़्यूज़न और फ़्लो-मैचिंग मॉडल प्रति उच्चारण एक नेटवर्क को कई बार चलाते हैं, एक ऐसे लूप के साथ जिसकी लंबाई निर्यात के समय तय नहीं होती। AudioSR (एक अलग VAE, LDM और वोकोडर के साथ लगभग 6 GB का एक latent-diffusion पाइपलाइन) और SGMSE दोनों यहीं आते हैं — SGMSE की अपनी 2025 स्ट्रीमिंग फ़ॉलो-अप केवल एक उपभोक्ता GPU पर रीयल टाइम तक पहुँचती है, CPU की तो बात ही छोड़ दें।

Location-variable convolutions अयोग्य दिखते हैं और ज़्यादातर नहीं हैं। resemble-enhance को लंबे समय तक इस दस्तावेज़ में LVCNet, वोकोडर के location-variable convolution, पर इस सिद्धांत के तहत अस्वीकार किया गया था कि unfold और einsum के ज़रिए प्रति स्थिति पूर्वानुमानित कर्नेल एक स्थिर ग्राफ़ में नहीं समा सकते। सीधे परीक्षण करने पर, यह ग़लत निकला — दोनों ऑप्स के ONNX समकक्ष हैं। असली विफलता एक अलग, अच्छी तरह समझी गई ट्रेसिंग त्रुटि है (“ONNX export of convolution for kernel of unknown shape”) जिसे कोडबेस में कहीं और BigVGAN के resamplers के लिए पहले ही हल किया जा चुका है। जो अब भी resemble-enhance को बाहर रखता है वह है पैमाना: एक CFM ODE सैंपलर और एक ऑटोएन्कोडर सहित चार नेटवर्क, 44.1 kHz पर — एक दायरे का निर्णय, कोई असंभवता नहीं।

कुछ मॉडलों के पास निर्यात करने के लिए कुछ भी प्रशिक्षित नहीं है। RNNoise हाथ से लिखे C के रूप में भेजा जाता है, किसी प्रशिक्षण योग्य फ़्रेमवर्क में कोई ग्राफ़ नहीं — इसे पोर्ट करने का मतलब होगा शुरू से एक समकक्ष नेटवर्क को फिर से प्रशिक्षित करना। Fast-ULCNet केवल आर्किटेक्चर कोड प्रकाशित करता है, कोई चेकपॉइंट बिलकुल नहीं।

एक प्रतिबंधात्मक लाइसेंस एक लेबलिंग निर्णय है, स्वचालित अयोग्यता नहीं — यही ठीक कारण है कि callenhancer और metadenoiser भेजे जाते हैं। जो अयोग्य है वह है बिना किसी लाइसेंस के प्रकाशित वेट्स: mdctGAN को ठीक इसी कारण अस्वीकार किया गया था, एक torch.fft-आधारित फ़्रंट एंड के ऊपर जो भरोसेमंद ढंग से निर्यात नहीं होता।

मॉडल के अंदर बुलाया गया torch.stft एक असली संरचनात्मक अवरोधक है। NU-Wave2 का सैंपलर लूप समस्या नहीं है — वह ग्राफ़ के बाहर numpy में चल सकता था, जैसे हर दूसरे इंजन का STFT करता है। जो इसे रोकता है वह यह है कि इसकी forward विधि आंतरिक रूप से torch.stft और torch.istft बुलाती है, जिसे लाइब्रेरी जानबूझकर अपने भेजे गए हर ग्राफ़ से बाहर रखती है, और जो सामान्यतः सबसे कम भरोसेमंद ढंग से निर्यात होने वाला ऑपरेटर भी है। इसे ठीक करने का मतलब होगा मॉडल को ट्रांसफ़ॉर्म सीमा पर विभाजित करना, एक ऑपरेटर-अदला-बदली के बजाय असली पुनर्संरचना।

किसी मॉडल के आर्किटेक्चर को पुनरुत्पादित करना उसके आउटपुट को पुनरुत्पादित करने जैसा नहीं है। LiSenNet 56 K पैरामीटर का है, 300 KB से कम — यह लाइब्रेरी में सबसे छोटा इंजन होता। इसका सार्वजनिक रूप से उपलब्ध ONNX पोर्ट चलता है और प्रशंसनीय दिखने वाला क्षीण ऑडियो उत्पन्न करता है, पर शुरू से अंत तक मापने पर यह सिग्नल को नष्ट कर देता है: 11 dB इनपुट पर −10.8 dB SNR। पोर्ट के अपने ही संदर्भ कार्यान्वयन को बिलकुल पुनरुत्पादित करने पर वही नकारात्मक परिणाम मिलता है, जिसका मतलब है कि संदर्भ कार्यान्वयन स्वयं उस फ़्रंट एंड से मेल नहीं खाता जिसका इसकी अपनी दस्तावेज़ी वर्णन करती है — अभी सत्यापित करने के लिए कोई सही लक्ष्य नहीं है।

इन सबमें एक पैटर्न है कि दिलचस्प विफलताएँ शायद ही कभी “मॉडल बहुत बड़ा है” या “डिफ़्यूज़न धीमा है” होती हैं। वे विशिष्ट होती हैं: एक सटीक प्रतिस्थापन वाला एक असमर्थित ऑपरेटर (torch.complex का कोई ONNX ऑप नहीं है, पर atan2(im, re) वही फ़ेज़ कोण गणना करता है), एक इनपुट के रनटाइम आकार से बना एक टेंसर जिसे एक ट्रेसर पिन नहीं कर सकता, या एक ट्रांसफ़ॉर्म जो ग्राफ़ सीमा के ग़लत तरफ़ रखा गया है।

पुष्टि करना कि आउटपुट असल में बेहतर हुआ

एक ONNX फ़ाइल जो चलती है यह प्रमाण नहीं है कि एक रिकॉर्डिंग बेहतर हुई। दो अलग-अलग विफलता मोड बाहर से एक जैसे दिखते हैं: एक डिनॉइज़र जो शोर के साथ वाणी को भी म्यूट कर देता है, और एक बैंडविड्थ एक्सटेंडर जो ग़लत harmonic सामग्री वाला एक उच्च बैंड जोड़ता है, दोनों ऐसा ऑडियो उत्पन्न करते हैं जो बिना त्रुटि के चलता है और आकस्मिक सुनने में साफ़ भी लग सकता है।

बहन लाइब्रेरी speechonnxmetrics इस निर्णय को प्रभाव के बजाय मापने योग्य बनाने के लिए मौजूद है। यह ऑडियो को MOS (Mean Opinion Score, समझी गई गुणवत्ता की 1–5 रेटिंग) पर दो तरीक़ों से स्कोर करती है: DNSMOS और UTMOS जैसे संदर्भ-रहित न्यूरल अनुमानक, जो तुलना करने के लिए किसी साफ़ मूल के बिना एक रिकॉर्डिंग को स्कोर करते हैं, और STOI और SI-SDR जैसी intrusive मेट्रिक्स, जिन्हें साफ़ संदर्भ चाहिए और जो मापती हैं कि आउटपुट असल में उससे कितना क़रीब है।

import speechonnxmetrics as s

s.score("clean.wav", ["utmos"])
# -> {'utmos': 4.41}

s.score("denoised.wav", ["stoi", "si_sdr"], ref="clean.wav")
# -> {'stoi': 0.66, 'si_sdr': -26.9}

किसी डिनॉइज़र या एक्सटेंडर से पहले और बाद में चलाइए और एक असली तुलना का आकार सामने आ जाता है: कच्चे और प्रोसेस्ड ऑडियो पर DNSMOS या UTMOS यह देखने के लिए कि समझी गई गुणवत्ता बिलकुल हिली या नहीं, और — जब एक साफ़ संदर्भ मौजूद हो, जो कृत्रिम शोर परीक्षणों के लिए तो होता है पर एक असली फ़ोन कॉल के लिए शायद ही कभी — SI-SDR या STOI यह देखने के लिए कि क्या प्रोसेस्ड सिग्नल असल में उसकी ओर अभिसरित हुआ न कि बस अलग सुनाई दिया। यही वह अनुशासन है जो ऊपर वाली डिनॉइज़र तालिका में SNR के आँकड़ों के पीछे है: एक विशेष शोर स्थिति से जुड़ी एक संख्या, कोई विशेषण नहीं। शुद्ध-ONNX लाइब्रेरियों का व्यापक परिवार जिसमें यह फ़िट होता है, ख़ुद speechonnxmetrics सहित, शुद्ध-ONNX स्पीच लाइब्रेरियों का एक परिवार में कवर किया गया है।

किसी रिकग्नाइज़र, किसी स्पीकर ID प्रणाली, या किसी मानव श्रोता तक पहुँचने से पहले ऑडियो को साफ़ करना अपने आप में एक इंजीनियरिंग टुकड़ा है, अपनी ही समझौतों की रजिस्ट्री और उन तरीक़ों की अपनी ही सूची के साथ जिन्हें आज़माया गया और जो किसी असली सिग्नल के संपर्क में आने पर टिके नहीं रहे।

किसी विशेष पाइपलाइन में इसे लागू करने के बारे में सवाल: संपर्क करें