सभी लेख

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

ध्वनिविज्ञान स्टैक कैसे एक साथ फ़िट होता है

  • G2P
  • IPA
  • Phonetics
  • NLP
  • TTS
  • Linguistics
  • FOSS

अंग्रेज़ी शब्द “read” लीजिए। लिखा हुआ, यह आपको नहीं बताता कि इसे कैसे बोलना है। “I read the book yesterday” और “I read the book every day” वही पाँच अक्षर दो अलग-अलग ध्वनियों के लिए इस्तेमाल करते हैं — एक “red” के साथ तुकबंदी करता है, दूसरा “reed” के साथ। एक स्क्रीन रीडर, एक वॉयस असिस्टेंट, या एक सर्च बॉक्स जो केवल वर्तनी देखता है, इसे सही नहीं कर सकता। इसे उच्चारण के बारे में तर्क करना होगा, न कि केवल टेक्स्ट के बारे में।

वह तर्क समस्या — लिखे गए शब्दों को उनकी प्रतिनिधित्व की गई ध्वनियों में बदलना — यही है जिसे हमारा ध्वनिविज्ञान स्टैक हल करता है। यह पोस्ट इस बात का नक्शा है कि इसके हिस्से कैसे एक साथ फ़िट होते हैं, कच्चे नोटेशन से लेकर भाषा-विशिष्ट उच्चारण इंजनों और ध्वनि-आधारित खोज तक।

कुछ शब्द, सीधे-सीधे परिभाषित

कुछ शब्द जो पूरे लेख में आते हैं:

  • ग्रैफ़ीम: एक लिखित प्रतीक — एक अक्षर, या “ch” जैसा एक अक्षर-संयोजन।
  • फ़ोनीम: किसी भाषा में ध्वनि की एक विशिष्ट इकाई, जैसे “cat” में “k” ध्वनि।
  • IPA (International Phonetic Alphabet): ध्वनियों को सटीक रूप से लिखने के लिए एक मानक वर्णमाला, किसी भी भाषा की वर्तनी से स्वतंत्र। “Cat” IPA में kæt लिखा जाता है।
  • G2P (grapheme-to-phoneme): वर्तनी को ध्वनि में बदलने की सामान्य समस्या।
  • एलोफ़ोन: संदर्भ के अनुसार एक ही फ़ोनीम की एक भिन्न प्राप्ति — “top” में “t” और “stop” में “t” अंग्रेज़ी में एक ही फ़ोनीम हैं पर थोड़े अलग ढंग से उच्चारित होते हैं।
  • अक्षर-विभाजन (Syllabification): एक शब्द को अक्षरों में तोड़ना, जैसे “extraordinário” को ex-tra-or-di-ná-ri-o में।
  • समानाक्षर (Homograph): दो शब्द जो एक जैसे लिखे जाते हैं पर जिनके अर्थ अलग हैं; एक समध्वनि समानाक्षर (heterophonic homograph, या heterophone) एक ऐसा समानाक्षर है जो इस पर निर्भर करते हुए अलग-अलग उच्चारित होता है कि किस अर्थ का मतलब है, जैसे ऊपर वाला “read”/“read”।
  • आकृति-विज्ञान (Morphology): शब्दों की आंतरिक संरचना — उपसर्ग, मूल, प्रत्यय, विभक्तियाँ।
  • Part-of-speech (POS) टैगिंग: किसी वाक्य के हर शब्द को संज्ञा, क्रिया, विशेषण, आदि के रूप में लेबल करना।

मूल समस्या

वर्तनी ध्वनि की एक हानिकारी एनकोडिंग है। तीन अलग-अलग चीज़ें इसे उलटना कठिन बनाती हैं:

  1. अस्पष्टता। अर्थ, व्याकरण, या साधारण अनियमितता के अनुसार वही अक्षर अलग-अलग ध्वनियों में मैप हो सकते हैं (ऊपर वाला “read”; अंग्रेज़ी इनसे भरी है)।
  2. बोली। वही शब्द, वही भाषा में, इस पर निर्भर करते हुए अलग उच्चारित होता है कि वक्ता कहाँ से है। यूरोपीय और ब्राज़ीलियाई पुर्तगाली वर्तनी साझा करते हैं पर स्वर नहीं।
  3. कवरेज। दुनिया की अधिकांश भाषाओं के पास बिलकुल भी कोई पेशेवर रूप से क्यूरेट किया गया उच्चारण शब्दकोश नहीं है। एक G2P प्रणाली जो केवल लुकअप टेबल के ज़रिए काम करती है, वह प्रणाली है जो केवल मुट्ठी भर भाषाओं के लिए काम करती है।

टेक्स्ट-टू-स्पीच, स्पीच रिकग्निशन प्रशिक्षण डेटा, या ध्वन्यात्मक रूप से जागरूक खोज के किसी भी गंभीर प्रयास को इन तीनों से निपटना होगा।

स्टैक को परतों में क्यों बाँटा गया है

स्टैक समस्या को ऐसी परतों में बाँटता है जिन्हें एक-दूसरे के बारे में जानने की ज़रूरत नहीं:

  • नोटेशन — ध्वन्यात्मक वर्णमालाओं और स्क्रिप्ट के बीच रूपांतरण। इसका किसी विशेष भाषा के ध्वनिविज्ञान से कोई लेना-देना नहीं है; यह प्रतीक अनुवाद है।
  • ध्वनिविज्ञान — किसी दी गई भाषा के लिए वर्तनी को IPA में मैप करना, उस भाषा की ध्वनि प्रणाली के एक स्पेक का उपयोग करते हुए।
  • भाषा-विशिष्ट अपवाद हैंडलिंग — अनियमित शब्द, बोली की विचित्रताएँ, समानाक्षर, और आकृति-विज्ञान संरचना जिन्हें एक सामान्य इंजन केवल वर्तनी नियमों से अनुमानित नहीं कर सकता।

इन्हें अलग रखना एक डिज़ाइन निर्णय है, कोई दुर्घटना नहीं, और इसका एक सीधा फ़ायदा है: एक नई भाषा जोड़ने का मतलब है एक स्पेक (उसकी ध्वनि प्रणाली का वर्णन करने वाला डेटा) लिखना, कोई नया प्रोग्राम नहीं। जो इंजन स्पेक का उपभोग करता है, लैटिस खोज, टोकनाइज़र, दूरी मेट्रिक्स — इनमें से कुछ भी फिर से नहीं लिखा जाता। नीचे वाली नोटेशन परत हर भाषा द्वारा साझा की जाती है, यहाँ तक कि उनके द्वारा भी जिनके बारे में ध्वनिविज्ञान इंजन ने कभी नहीं सुना।

नोटेशन परत: scriptconv

scriptconv ध्वन्यात्मक नोटेशन और स्क्रिप्ट हैंडलिंग के लिए एक शून्य-निर्भरता वाला कोर है: ISO-15924 स्क्रिप्ट पहचान, ARPABET, X-SAMPA, Lexique, Kirshenbaum, Cotovía और RFE नोटेशन के आगे-पीछे IPA रूपांतरण, अरबी के लिए Buckwalter लिप्यंतरण, jamo में Hangul विघटन, और kana हैंडलिंग। इनमें से किसी को भी यह जानने की ज़रूरत नहीं कि कोई शब्द किस भाषा का है — IPA में एक फ़ोनीम स्ट्रिंग स्रोत भाषा से स्वतंत्र, वैसे ही ARPABET में बदलती है:

>>> import scriptconv as s
>>> s.ipa_to_arpa("kæt")
'K AE T'
>>> s.ipa_to_xsampa("kæt")
'k{t'

इसके ऊपर की हर परत यह मान सकती है कि नोटेशन रूपांतरण पहले ही हल हो चुका है।

इंजन: orthography2ipa

orthography2ipa क्रॉस-भाषा इंजन है। यह एक भाषा स्पेक — उस भाषा के ग्रैफ़ीम-टू-फ़ोनीम नियमों का एक घोषणात्मक विवरण — और टेक्स्ट का एक टुकड़ा लेता है, और IPA उत्पन्न करता है। इसे लिखे जाने तक यह 820 भाषाओं को कवर करने वाले स्पेक्स भेजता है (इंस्टॉल किए गए पैकेज पर available_codes() उस लंबाई की एक सूची लौटाता है; सटीक आँकड़े को एक बदलता हुआ लक्ष्य मानें, क्योंकि समय के साथ स्पेक्स जोड़े जाते हैं)।

>>> import orthography2ipa as o
>>> len(o.available_codes())
820

इंजन में स्वयं कोई भाषा-विशिष्ट कोड बेक नहीं किया गया है। एक नई भाषा एक नई स्पेक फ़ाइल है, हर दूसरे स्पेक की तरह उसी स्कीमा के विरुद्ध जाँची गई।

लैटिस: रैंक किए गए उम्मीदवार, एक अनुमान नहीं

ऊपर वाली अस्पष्टता समस्या को देखते हुए, प्रति शब्द एक अकेले आउटपुट के प्रति प्रतिबद्ध होना अक्सर ग़लत होता है। orthography2ipa इसके बजाय एक लैटिस — रैंक किए गए उम्मीदवार उच्चारणों का एक सेट — उत्पन्न करता है, और ऊपरी परतों को उस संदर्भ का उपयोग करते हुए इसे संकीर्ण करने देता है जो इंजन के पास ख़ुद नहीं है (अर्थ, part of speech, एक शब्दकोश प्रविष्टि)।

फिर से “read” लीजिए:

>>> from orthography2ipa import G2P
>>> g = G2P("en")
>>> g.transcribe("read")
'ɹiːd'
>>> g.candidates("read")
[IPAPath('ɹiːd', score=0.0), IPAPath('ɹɛd', score=1.0)]

बिना अधिक संदर्भ के इंजन अपना सबसे अच्छा अनुमान लौटाता है (वर्तमान काल, कम लागत) पर विकल्प (भूतकाल) को उसकी संलग्न लागत के साथ लैटिस पर रखता है। एक डाउनस्ट्रीम घटक जो जानता है कि वाक्य भूतकाल में है, पहले के बजाय दूसरा उम्मीदवार चुन सकता है। यही विचार, बड़े पैमाने पर, पुर्तगाली heterophones के लिए bifonia (नीचे) द्वारा इस्तेमाल किया जाता है: एक सामान्य-उद्देश्य लैटिस उम्मीदवार सप्लाई करता है, एक संकीर्ण, बेहतर-सूचित परत उनमें से चुनती है।

बोलियाँ प्रथम-श्रेणी हैं

एक ही भाषा के दो वक्ता एक ही वाक्य को अलग-अलग उच्चारित कर सकते हैं, और एक ध्वनिविज्ञान स्टैक जो “पुर्तगाली” को एक स्थिर ध्वनि प्रणाली मानता है, एक को छोड़कर हर बोली को ग़लत करेगा। orthography2ipa बोली हैंडलिंग को सीधे उजागर करता है — इंस्टॉल किए गए पैकेज पर available_profiles() lisbon, porto, estremenho, galician, और अन्य जैसी बोली और लेक्ट प्रोफ़ाइलों को सूचीबद्ध करता है — और tugaphone, इसके ऊपर बना पुर्तगाली फ़्रंटएंड, लुसोफ़ोन विभिन्नताओं में वही वाक्य फ़ोनीमाइज़ करता है। यहाँ एक वाक्य है जो सभी पाँच समर्थित बोलियों से चलाया गया है:

बोलीआउटपुट
pt-PT (पुर्तगाल)ˈbõ ˈdiɐ ˈkomu eˈʃta vɔˈse
pt-BR (ब्राज़ील)ˈbõ ˈdʒiɐ ˈkɔ̃mʊ eˈsta voˈse
pt-AO (अंगोला)ˈbõ ˈdiɐ ˈkomʊ eˈsta vɔˈse
pt-MZ (मोज़ाम्बिक)ˈbõ ˈdiɐ ˈkomu eˈsta vɔˈse
pt-TL (तिमोर-लेस्ते)ˈbõ ˈdiə ˈkoɔmʊ eˈsta vɔˈse

(“Bom dia, como está você?” — “सुप्रभात, आप कैसे हैं?”) व्यंजन ढाँचा सभी पाँचों में पहचानने योग्य रहता है, पर दो जाने-पहचाने चिह्नक उन्हें तुरंत अलग कर देते हैं। “dia” में, ब्राज़ीलियाई पुर्तगाली एक i से पहले के d को में बदल देती है, अंग्रेज़ी “jam” की शुरुआत वाली ध्वनि — बाकी एक सादा d रखती हैं। “está” में, यूरोपीय पुर्तगाली किसी अक्षर के अंत में s को ʃ के रूप में उच्चारित करती है, “shoe” का “sh”, जबकि हर दूसरी विभिन्नता s रखती है। एक बोली के नियमों से बना उच्चारण शब्दकोश हर दूसरी बोली के श्रोता के लिए ये दोनों ग़लत कर देगा।

euskaphone बास्क बोलियों के लिए वही करता है, एक अलग इंजन के बजाय सीधे orthography2ipa लैटिस पर बना:

>>> from euskaphone import EuskaPhonemizer
>>> EuskaPhonemizer().phonemize_sentence("Kaixo, zer moduz zaude?")
'kai̯ʃo s̻er modus̻ s̻au̯de'

भाषा-विशिष्ट फ़्रंटएंड

साझा इंजन के ऊपर ऐसे फ़्रंटएंड बैठते हैं जो वह जोड़ते हैं जो एक सामान्य स्पेक नहीं कर सकता: अनियमित शब्द, एक क्यूरेटेड शब्दकोश, sandhi (शब्द-सीमाओं पर ध्वनि परिवर्तन), और बोली-विशिष्ट ओवरराइड।

  • tugaphone — पुर्तगाली, pt-PT, pt-BR, pt-AO, pt-MZ और pt-TL में, एक क्यूरेटेड शब्दकोश को नियम-आधारित फ़ॉलबैक के साथ जोड़ते हुए (ऊपर दिखाया गया)।

  • euskaphone — बास्क, बोली-जागरूक, उसी लैटिस पर बना (ऊपर दिखाया गया)।

  • mwl_phonemizer — मिरांडीज़, पुर्तगाल के Terra de Miranda की Asturleonese भाषा, क्रॉस-वर्ड sandhi, allophony और बल के साथ:

    >>> from mwl_phonemizer import phonemize
    >>> phonemize("Falo la lhéngua mirandesa.")
    'ˈfalu lɐ ˈʎɛŋɡwa miɾɐˈndez̺ɐ.'
  • g2p_barranquenho — बारांकेन्यो के लिए पहला ओपन G2P, पुर्तगाल-स्पेन सीमा पर बारांकोस की इबेरो-रोमांस संपर्क भाषा। देखें बारांकेन्यो के लिए पहला फ़ोनमाइज़र प्रस्तुत है यह देखने के लिए कि इसके नियम नगरपालिका के अपने वर्तनी सम्मेलन से कैसे व्युत्पन्न किए गए थे।

  • arbtok — अरबी, orthography2ipa के लैटिस पर बना, बोली-जागरूक डायक्रिटाइज़ेशन जोड़ते हुए और आधुनिक मानक, शास्त्रीय, और कई क्षेत्रीय विभिन्नताओं को कवर करते हुए। अरबी लिपि सामान्यतः लघु-स्वर चिह्न छोड़ देती है जिनकी एक फ़ोनमाइज़र को ज़रूरत होती है, इसलिए arbtok का मुख्य काम साझा इंजन को परिणाम सौंपने से पहले उन्हें पुनर्स्थापित करना है। इसे किसी ऐसे व्यक्ति द्वारा बनाए रखा जाता है जो मूल रूप से अरबी नहीं बोलता, इसलिए इसे एक समाप्त, नेटिव-समीक्षित संदर्भ के बजाय सक्रिय विकास में मानें — उपयोगी, पर वह जगह जहाँ इसे किसी उपयोगकर्ता-सामने वाली चीज़ में भेजने से पहले किसी नेटिव वक्ता के विरुद्ध आउटपुट दोबारा जाँचना चाहिए।

इनमें से हर फ़्रंटएंड उसी साझा लैटिस इंजन और उसके नीचे उसी साझा नोटेशन परत के ऊपर भाषा-विशिष्ट लॉजिक की एक पतली परत है। इनमें से कोई भी IPA रूपांतरण या लैटिस खोज को फिर से लागू नहीं करता।

पुर्तगाली को समर्थन देने वाले औज़ार

पुर्तगाली के पास सबसे गहरा स्टैक है, क्योंकि पुर्तगाली उच्चारण वर्तनी नियमों से अधिक पर निर्भर करता है: यह अक्षर संरचना, शब्द वर्ग, और कभी-कभी साधारण अर्थ पर निर्भर करता है।

  • silabificador हस्तनिर्मित नियमों का उपयोग करते हुए शब्दों को अक्षरों में विभाजित करता है:

    >>> from silabificador import syllabify
    >>> syllabify("extraordinário")
    ['ex', 'tra', 'or', 'di', 'ná', 'ri', 'o']
  • tugalex tugaphone के पीछे का शब्दकोश है: असली शब्दों के लिए IPA ट्रांसक्रिप्शन, अक्षर डेटा, और वर्तनी नियम, ताकि सामान्य और अनियमित शब्दावली को हर बार वर्तनी से फिर से व्युत्पन्न न करना पड़े।

  • tugatagger कई POS टैगिंग बैकएंड (spaCy, Stanza, एक Brill-शैली टैगर, एक निर्भरता-रहित ह्यूरिस्टिक फ़ॉलबैक) को एक इंटरफ़ेस के पीछे लपेटता है, ताकि दूसरे औज़ार किसी एक विशेष बैकएंड के प्रति प्रतिबद्ध हुए बिना पूछ सकें “इस शब्द का part of speech क्या है”।

  • tugamorph एक नियम-आधारित आकृति-विज्ञान विश्लेषक है: यह केवल Python स्टैंडर्ड लाइब्रेरी का उपयोग करते हुए एक शब्द को उपसर्ग, मूल, प्रत्यय, विभक्ति और clitic में विभाजित करता है, वैकल्पिक रूप से silabificador और tugatagger द्वारा और तेज़ किया जाता है।

  • bifonia यूरोपीय पुर्तगाली समध्वनि समानाक्षरों को हल करता है — “sede” (प्यास, ˈsedɨ, बनाम मुख्यालय, ˈsɛdɨ) जैसे शब्द जहाँ सही उच्चारण व्याकरण पर नहीं, अर्थ पर निर्भर करता है। देखें सही ढंग से कहना: TTS के लिए पुर्तगाली हेटरोफ़ोन्स का असंदिग्धीकरण यह देखने के लिए कि इसे कैसे बनाया और आँका गया। यही ऊपर वाले लैटिस विचार के पीछे का ठोस मामला है: orthography2ipa “sede” के दोनों उम्मीदवार पठन सप्लाई कर सकता है, पर केवल bifonia जैसी एक अर्थ-जागरूक परत ही उनमें से चुन सकती है।

silabificador और tugaphone रोज़ाना कैसे साथ काम करते हैं, इसके बारे में अधिक जानने के लिए, देखें क्लासिकल NLP for पुर्तगाली: अक्षर-विभाजन और ग्रैफ़ीम-टू-फ़ोनीम, और इस सबके नीचे के व्यापक इंजन के लिए, 820 भाषाओं के लिए ग्रैफ़ीम-टू-IPA

ध्वनि-आधारित खोज: phonematcher

ऊपर की हर चीज़ टेक्स्ट को ध्वनि में बदलती है। phonematcher ध्वनि प्रतिनिधित्वों के साथ ही काम करता है: यह IPA प्रतीकों के बीच ध्वन्यात्मक दूरी की गणना करता है और यह इस आधार पर शब्द सूचियों पर फ़ज़ी खोज करता है कि शब्द कैसे सुनाई देते हैं, न कि वे कैसे लिखे जाते हैं।

>>> from phonematcher.distance import phonetic_distance
>>> phonetic_distance('b', 'p')   # voiced vs. voiceless bilabial stop — very similar
0.043478260869565216
>>> phonetic_distance('p', 'k')   # bilabial vs. velar stop — less similar
0.34782608695652173
>>> phonetic_distance('a', 'k')   # vowel vs. consonant — maximally different
1.0

वह दूरी मेट्रिक दो ठोस स्थितियों में उपयोगी है: शब्दों या नामों के एक कैटलॉग को उसकी सटीक वर्तनी के बजाय किसी चीज़ की ध्वनि के आधार पर खोजना (टाइपो-सहिष्णु वॉयस इंटरफ़ेस के लिए और लेखन प्रणालियों में उधार शब्दों को मिलाने के लिए उपयोगी), और यह तुलना करना कि दो संबंधित लेक्ट ध्वनिविज्ञान की दृष्टि से कितने क़रीब हैं — वही तरह की तुलना जो ऊपर वाली बोली तालिका आँख से करती है, पर आँकी हुई नहीं बल्कि गणना की हुई। phonematcher PyPI पर नहीं है; यह स्रोत से इंस्टॉल होता है (GitHub चेकआउट के विरुद्ध pip install -e ., साथ ही rapidfuzz)।

ईमानदार सीमाएँ

820 भाषा स्पेक्स में कवरेज निर्माण के अनुसार असमान है: जिन भाषाओं के पास एक स्थापित ध्वनिवैज्ञानिक साहित्य और एक शब्दकोश है वे ज़्यादातर सामान्य वर्तनी सम्मेलनों से अनुमानित एक पतले स्पेक वाली भाषाओं की तुलना में बेहतर आउटपुट देती हैं। गुणवत्ता लगातार वहाँ सबसे अच्छी है जहाँ एक क्यूरेटेड शब्दकोश मौजूद है — पुर्तगाली, tugalex द्वारा समर्थित, स्टैक का सबसे मज़बूत मामला है; जो भाषाएँ बिना किसी शब्दकोश के केवल स्पेक नियमों पर निर्भर करती हैं वे अनियमित और उधार शब्दावली को ग़लत ढंग से संभालेंगी।

कुछ घटक स्पष्ट रूप से समाप्त, नेटिव-समीक्षित संदर्भ नहीं हैं: arbtok को एक ग़ैर-नेटिव अरबी वक्ता द्वारा बनाए रखा जाता है और इसे किसी उपयोगकर्ता-सामने वाली चीज़ में उपयोग करने से पहले नेटिव निर्णय के विरुद्ध जाँचा जाना चाहिए। पतले स्पेक्स पर बने फ़्रंटएंड उसी पतलेपन को विरासत में पाते हैं — एक फ़्रंटएंड केवल उतना ही अच्छा है जितना उसके नीचे का स्पेक और शब्दकोश।

अगर आपकी भाषा के पास कोई स्पीच टूलिंग नहीं है तो यह क्यों मायने रखता है

दुनिया की अधिकांश भाषाओं के पास कोई व्यावसायिक TTS आवाज़ नहीं है, कोई व्यावसायिक STT मॉडल नहीं है, और कोई पेशेवर रूप से बनाए रखा गया उच्चारण शब्दकोश नहीं है। ऊपर वाला परतबद्ध डिज़ाइन इसका मतलब यह है कि उस खाई को शुरू से एक ध्वनिविज्ञान इंजन बनाने की ज़रूरत नहीं है: इसे लक्ष्य भाषा की ध्वनि प्रणाली के लिए एक स्पेक लिखने की ज़रूरत है और, जहाँ संभव हो, उसके अनियमित शब्दों का एक शब्दकोश। लैटिस इंजन, नोटेशन रूपांतरण, और खोज टूलिंग पहले से मौजूद हैं। अगर आपकी भाषा, बोली, या उत्पाद को ऐसे उच्चारण समर्थन की ज़रूरत है जो अभी मौजूद नहीं है, तो यह वही तरह का काम है जो हम लेते हैं — देखें हमारी सेवाएँ या संपर्क करें