orthography2ipa — это Python-пакет на чистых данных — декларативный JSON, тонкая подключаемая логика, без обученных весов, — который отображает написание в IPA и моделирует то, как эти фонемы проявляются в контексте. Он поставляется с 909 языковыми спецификациями, охватывающими 820 языков (плюс 89 узлов классификационных клад), по более чем 20 языковым семьям. Установите его, читайте данные, форкайте данные. Ничего не спрятано в чекпойнте.
Он является фонологическим слоем под всем, что находится ниже по цепочке: производимая им решётка кандидатов потребляется арабским TTS-фронтендом arbtok, португальскими стеками TugaPhone и silabificador (см. классический NLP для португальских слогов и фонем), фонемизатором барранкеньу, фонемизатором мирандского языка и фонемным основанием для TTS, который работает на картошке.
Две карты, а не одна
Критическое различие: карта графем сообщает вам, какие фонемы написание может представлять. Карта аллофонов сообщает вам, как фонема проявляется в контексте. Смешение этих двух — самый распространённый вид сбоя в системах G2P.
import orthography2ipa
en = orthography2ipa.get("en-GB")
en.graphemes["th"] # ['θ', 'ð'] — one spelling, two possible phonemes
en.allophones["t"] # ['t', 'tʰ', 'ʔ', 'ɾ'] — one phoneme, four realisations
Английское ⟨th⟩ подлинно неоднозначно между /θ/ и /ð/ — это факт написания-в-фонему. Английское /t/ проявляется как простой смычный, придыхательный смычный, гортанный смычный или одноударный в зависимости от того, где оно оказывается, — это факт фонемы-в-реализацию. Раздельное хранение этих двух означает, что можно идти текст → кандидаты в фонемы для транскрипции и фонема → поверхностная реализация для моделирования произношения без того, чтобы одно портило другое. Для TTS это разница между правдоподобным акцентом и роботизированным; для ASR это разница между лексиконом, соответствующим тому, что люди действительно говорят, и тем, который соответствует словарю.
Что несёт каждый язык
Каждый язык — это замороженный dataclass LanguageSpec, и он несёт гораздо больше, чем список фонем: графемы (включая диграфы и триграфы), карту аллофонов, позиционные графемы для контекстно-зависимых переопределений (начало слова, интервокальное положение, перед /i/), взвешенную родословную с несколькими предками, правила сандхи между словами, необязательный тональный инвентарь и происхождение — QualityTier, проходящий stub → skeleton → research → production, ScriptType (алфавит, абджад, абугида, …) и библиографические источники с привязкой к страницам.
Правило включения строгое, и его стоит сформулировать прямо: включаются только отображения, обоснованные официальной орфографией и документированной грамматикой. Произвольные правила подстрок исключаются. Португальское ⟨lh⟩, немецкое ⟨sch⟩ и английское ⟨th⟩ включены, потому что они являются стандартными орфографическими единицами. Удобные, но выдуманные эвристики — нет. Когда спецификация объявляет графемы, но не имеет явной карты аллофонов, выводится базовая карта тождества — каждая фонема как минимум является собственной поверхностной реализацией, — чтобы ничто не исчезало молча.
Региональные разновидности получают собственные спецификации, а не флаг на родителе. Бразильский и европейский португальский систематически расходятся, поэтому они являются отдельными объектами LanguageSpec, связанными родословной:
pt_br = orthography2ipa.get("pt-BR")
pt_br.graphemes["t"] # ['t', 't͡ʃ'] — palatalisation before /i/
Деревья диалектов остаются поддерживаемыми, потому что JSON-файлы поддерживают наследование graphemes_base / allophones_base: вариант объявляет только то, что отличается от его родителя. Родословная взвешена и имеет несколько предков — родитель, субстрат, суперстрат, адстрат, — что является честным способом моделировать языки, которые являются продуктами контакта, а не чистыми потомками.
Глубоко на местах, а не только широко
Цифра 820 — это широта; глубина — вот где работа. Спецификации идут лект за лектом там, где это делает диалектологическая литература, и каждая из них цитируется по этой литературе с привязкой к страницам, а не сопоставлена по шаблону с фонемной таблицей.
Иберийское покрытие — самый ясный пример: более 100 спецификаций для языков полуострова. Каждый романский язык Испании — кастильский, каталанский/валенсийский, галисийский (и норма RAG, и реинтеграционистская норма), астурийский, арагонский и его долинные разновидности (ансотано, чистабин, бенаскский…), эстремадурский — наряду с баскским, ибероромантскими креолами и историческими слоями, которые большинство ресурсов полностью пропускают: андалусийский арабский и мосарабский. Арабская сторона несёт 34 диалектных лекта (от неджди и хиджазского через левантийский, магрибский и полуостровные разновидности), а лузофонская сторона — 46 лектов португальского языка и языков Португалии, вплоть до рионорского, гуадрамильского и мирандских субдиалектов.
Насколько нам известно, некоторые из них являются первой машиночитаемой фонологией, когда-либо опубликованной для данной разновидности, — то есть структурированной, валидированной по схеме спецификацией графем/аллофонов, которую может запрашивать программа, в отличие от фонемного инвентаря, описанного только в прозе диалектологической литературы, — рионорский и гуадрамильский среди них. Работа ниже по цепочке поставляет первые словари IPA для барранкеньу и мирандского языка.
Решётка кандидатов, а не единственная догадка
Написание не является чистой задачей сегментации, поэтому флагманская архитектура — это решётка кандидатов. PhonetokTokenizer выполняет токенизацию графем maximal-munch — жадно предпочитая самую длинную совпадающую орфографическую единицу — и, поверх таблицы графем спецификации, производит по-позиционную решётку ранжированных кандидатов IPA вместо одного хрупкого результата:
from orthography2ipa.phonetok import PhonetokTokenizer
tok = PhonetokTokenizer(orthography2ipa.get("en-GB"))
tok.ipa_best("through") # 'θɹɔː'
for path in tok.ipa_beam("through", beam_width=8):
print(path.ipa, path.score) # θɹɔː 0.0, ðɹɔː 1.0, θɹoʊ 1.0, …
Решётка — это контракт, на котором строится всё семейство ниже по цепочке. Специфичный для языка движок потребляет общую решётку и добавляет только ту фонологию, которую статическая таблица выразить не может, удерживая каждого потребителя на одном и том же обоснованном ядре:
- arbtok строит фонологию арабского TTS на решётке, добавляя ассимиляцию солнечных букв, элизию hamzat al-waṣl, геминацию и обработку лигатур — а также новую rawi-lattice fusion, которая восстанавливает недостающие краткие гласные недиакритизированного диалектного текста, оценивая по-символьное распределение ансамбля под лицензированием запрошенного лекта, а не доверяя свободному генератору.
- TugaPhone, mwl_phonemizer (мирандский) и g2p_barranquenho — все потребляют одно и то же lattice-core для своих лузофонских разновидностей.
Измерение расстояния между языками
Поскольку данные структурированы, а не запечены в веса, можно сравнивать языки напрямую. Метрики расстояния охватывают измерения инвентаря, графемы, аллофона и родословной, плюс отдельное семейство расстояний по системе письма:
from orthography2ipa.distance import phonological_distance
d = phonological_distance(orthography2ipa.get("pt-BR"), orthography2ipa.get("pt-PT"))
d.combined # 0.0515 — near-identical
d.inventory.feature_mean # phoneme-inventory distance
d.grapheme.mean_ipa_distance # grapheme-mapping divergence
d.allophone_sim # allophone-overlap similarity
Векторы признаков также доступны, поэтому почти идентичная пара, как два португальских стандарта, оказывается на 0,0515, тогда как подлинно далёкие пары чётко расходятся. Это полезно как для решений о трансферном обучении, так и для бутстраппинга малоресурсных языков и диалектометрии.
Как мы знаем, что данные хоть чего-то стоят
Надёжный «эталон» (gold) для G2P почти не существует — большинство публичных наборов данных являются собственным выводом фонемизатора, повторно используемым в качестве референса, поэтому низкий уровень ошибок относительно них означает «согласуется с этим инструментом», а не «правильно». Мы прямо говорим об этом и построили вокруг этого методологию верификации, вместо того чтобы сообщать одно лестное число.
Для разновидностей, которые нас волнуют больше всего, эталон авторский, а не собранный из скрапинга: набор предложений, привязанный к движку, для каждого лекта, оцениваемый слепыми парами, арбитрируемый по литературе с привязкой к страницам и возвращаемый через классы коррекции в петлю обратной связи движка — расхождение между выводом движка и исправленной формой является наводкой на реальную ошибку в спецификации. По всему TTS-эталону, привязанному к движку, и подтверждениям из первичных источников насчитывается несколько тысяч верифицированных строк. Рамка намеренно честна в отношении происхождения: синтетическая и арбитрируемая по литературе там, где это всё, что есть, и подлинно человеческий эталон там, где он есть, — набор мирандского языка mirandese_g2p от носителей, подтверждения из первичных источников с привязкой к страницам и вклады носителей. Утверждения о точности делаются только относительно человеческого эталона; идеальный результат относительно собственного черновика движка ничего бы не значил.
Цифры, читаемые как направленные и всегда цитируемые по своему источнику (docs/scoreboard.md, docs/benchmarks.md и документы по бенчмаркам репозиториев ниже по цепочке):
- Арабские диалекты, чистый недиакритизированный ввод — тяжёлый, реалистичный для развёртывания случай. На чистом TTS-эталоне arbtok (33 лекта) rawi-lattice fusion под диалектным лицензированием достигает среднего PER 0.189, обгоняя тот же ансамбль, запущенный как свободный генератор (0.193), с разрывом, сконцентрированным на лектах, которые сильнее всего расходятся с MSA. На большинстве лектов arbtok обгоняет espeak-ng на чистом вводе; на самом MSA espeak — который настроен на MSA — всё ещё выигрывает (espeak 0.176 против arbtok 0.245).
- Арабские диалекты, диакритизированный ввод — с присутствующими знаками PER arbtok находится на уровне 0.01–0.08 на лект, значительно ниже единственного голоса MSA у espeak (например, неджди 0.009 против espeak 0.221; египетский 0.027 против espeak 0.287). У espeak нет диалектных голосов, поэтому это честно сравнение яблок с апельсинами — но в разрыве и суть.
- Португальский, относительно экспертно-человеческого эталона — лиссабонский европейский португальский оказывается на PER 0.029 (88% точных совпадений) на первичных источниках с привязкой к страницам, а мирандский эталон от носителей — на 0.146.
Каждая из этих цифр — это свойство текущего состояния данных, перекрёстно сверенное с бутстрап-доверительным интервалом, а не трофей из таблицы лидеров. Там, где интервал широк или выборка мала, scoreboard так и говорит.
CLI
Всё вышеперечисленное доступно без написания Python. Консольный скрипт orthography2ipa поставляется с list, info, transcribe и distance, и каждая подкоманда принимает --json для передачи в конвейер.
orthography2ipa list --family Romance
orthography2ipa info pt-BR --graphemes
orthography2ipa transcribe en-GB "through" --beam 8
orthography2ipa distance es-ES it-IT --json
Почему важны чистые данные
Весь набор спецификаций валидируется по схеме — замороженные dataclass-ы в стиле pydantic, проверяемые набором тестов целостности, с SCHEMA.md, документирующим форму. Там, где статическая таблица подлинно не может выразить правила, вокруг данных подключается специфичная для языка логика: слогоделители регистрируются через группу entry-point, а более тяжёлые движки строятся на общей решётке ниже по цепочке.
Нет непрозрачной модели, решающей, как звучат языки ваших пользователей. Отображения поддаются проверке, источники цитируются с привязкой к странице, а добавление языка — это написание одного валидированного JSON-файла — начните с docs/adding_a_language.md и руководства по началу работы. Для всех, кто строит TTS, ASR или фонетический NLP и отказывается отдавать свою фонологию на аутсорс чёрному ящику — и хочет, чтобы она работала на собственном оборудовании, — в этом и суть. Это Apache 2.0, и это ваше, чтобы изучать, расширять и размещать самостоятельно.