项目文件夹

文件
wehub-resource-sync ba4be087d5
Create PR to main with cherry-pick from release / cherry-pick (push) Failing after 0s
CICD NeMo / pre-flight (push) Failing after 0s
CICD NeMo / configure (push) Has been skipped
Build, validate, and release Neural Modules / pre-flight (push) Failing after 1s
CICD NeMo / code-linting (push) Has been skipped
Build, validate, and release Neural Modules / release (push) Has been skipped
Build, validate, and release Neural Modules / release-summary (push) Has been cancelled
CICD NeMo / cicd-test-container-build (push) Has been cancelled
CICD NeMo / cicd-import-tests (push) Has been cancelled
CICD NeMo / L0_Setup_Test_Data_And_Models (push) Has been cancelled
CICD NeMo / cicd-main-unit-tests (push) Has been cancelled
CICD NeMo / cicd-main-speech (push) Has been cancelled
CICD NeMo / Nemo_CICD_Test (push) Has been cancelled
CICD NeMo / Coverage (e2e) (push) Has been cancelled
CICD NeMo / Coverage (unit-test) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
CICD NeMo / cicd-wait-in-queue (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:28:58 +08:00

409 行
16 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import unicodedata
import pytest
from nemo.collections.tts.g2p.models.i18n_ipa import IpaG2p
from nemo.collections.tts.g2p.utils import GRAPHEME_CASE_LOWER, GRAPHEME_CASE_MIXED, GRAPHEME_CASE_UPPER
class TestIpaG2p:
PHONEME_DICT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "phoneme_dict")
PHONEME_DICT_PATH_DE = os.path.join(PHONEME_DICT_DIR, "test_dict_de.txt")
PHONEME_DICT_PATH_EN = os.path.join(PHONEME_DICT_DIR, "test_dict_en.txt")
PHONEME_DICT_PATH_ES = os.path.join(PHONEME_DICT_DIR, "test_dict_es.txt")
GRAPHEME_PREFIX = "#"
@staticmethod
def _create_g2p(
phoneme_dict=PHONEME_DICT_PATH_EN,
locale=None,
apply_to_oov_word=lambda x: x,
use_chars=False,
phoneme_probability=None,
grapheme_case=GRAPHEME_CASE_UPPER,
grapheme_prefix="",
):
return IpaG2p(
phoneme_dict,
locale=locale,
apply_to_oov_word=apply_to_oov_word,
use_chars=use_chars,
phoneme_probability=phoneme_probability,
grapheme_case=grapheme_case,
grapheme_prefix=grapheme_prefix,
)
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_normalize_dict_with_phonemes(self):
# fmt: off
expected_symbols = {
'h', 'ə', 'ˈ', 'ɫ', 'o', 'ʊ',
'ˈ', 'w', 'ɝ', 'ɫ', 'd',
'ˈ', 'l', 'ɛ', 'd',
'ˈ', 'l', 'i', 'd',
'ɛ', 'n', 'ˈ', 'v', 'ɪ', 'd', 'i', 'ə',
'ˈ', 'd', 'ʒ', 'o', 'ʊ', 'n', 'z',
'ˈ', 'ɛ', 'ɹ', 'ˌ', 'p', 'ɔ', 'ɹ', 't',
}
# fmt: on
g2p = self._create_g2p()
assert expected_symbols == g2p.symbols
assert len(g2p.phoneme_dict["HELLO"]) == 1
assert len(g2p.phoneme_dict["WORLD"]) == 1
assert len(g2p.phoneme_dict["LEAD"]) == 2
assert len(g2p.phoneme_dict["NVIDIA"]) == 1
assert len(g2p.phoneme_dict["JONES"]) == 1
assert len(g2p.phoneme_dict["AIRPORT"]) == 1
assert g2p.phoneme_dict["HELLO"][0] == list("həˈɫoʊ")
assert g2p.phoneme_dict["WORLD"][0] == list("ˈwɝɫd")
assert g2p.phoneme_dict["LEAD"] == [list("ˈlɛd"), list("ˈlid")]
assert g2p.phoneme_dict["NVIDIA"][0] == list("ɛnˈvɪdiə")
assert g2p.phoneme_dict["JONES"][0] == list("ˈdʒoʊnz")
assert g2p.phoneme_dict["AIRPORT"][0] == list("ˈɛɹˌpɔɹt")
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_normalize_dict_with_graphemes_and_phonemes(self):
# fmt: off
expected_symbols = {
f"{self.GRAPHEME_PREFIX}{char}"
for char in {
'H', 'E', 'L', 'L', 'O',
'W', 'O', 'R', 'L', 'D',
'L', 'E', 'A', 'D',
'N', 'V', 'I', 'D', 'I', 'A',
'J', 'O', 'N', 'E', 'S',
'A', 'I', 'R', 'P', 'O', 'R', 'T',
}
}.union(
{
'h', 'ə', 'ˈ', 'ɫ', 'o', 'ʊ',
'ˈ', 'w', 'ɝ', 'ɫ', 'd',
'ˈ', 'l', 'ɛ', 'd',
'ˈ', 'l', 'i', 'd',
'ɛ', 'n', 'ˈ', 'v', 'ɪ', 'd', 'i', 'ə',
'ˈ', 'd', 'ʒ', 'o', 'ʊ', 'n', 'z',
'ˈ', 'ɛ', 'ɹ', 'ˌ', 'p', 'ɔ', 'ɹ', 't',
}
)
# fmt: on
g2p = self._create_g2p(use_chars=True, grapheme_prefix=self.GRAPHEME_PREFIX)
assert expected_symbols == g2p.symbols
assert len(g2p.phoneme_dict["HELLO"]) == 1
assert len(g2p.phoneme_dict["WORLD"]) == 1
assert len(g2p.phoneme_dict["LEAD"]) == 2
assert len(g2p.phoneme_dict["NVIDIA"]) == 1
assert len(g2p.phoneme_dict["JONES"]) == 1
assert len(g2p.phoneme_dict["AIRPORT"]) == 1
assert g2p.phoneme_dict["HELLO"][0] == list("həˈɫoʊ")
assert g2p.phoneme_dict["WORLD"][0] == list("ˈwɝɫd")
assert g2p.phoneme_dict["LEAD"] == [list("ˈlɛd"), list("ˈlid")]
assert g2p.phoneme_dict["NVIDIA"][0] == list("ɛnˈvɪdiə")
assert g2p.phoneme_dict["JONES"][0] == list("ˈdʒoʊnz")
assert g2p.phoneme_dict["AIRPORT"][0] == list("ˈɛɹˌpɔɹt")
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_replace_symbols(self):
g2p = self._create_g2p(use_chars=True, grapheme_prefix=self.GRAPHEME_PREFIX)
# fmt: off
# Get full vocab without 'i' (phoneme) and 'J' (grapheme)
fixed_symbols = {
f"{self.GRAPHEME_PREFIX}{char}"
for char in {
'H', 'E', 'L', 'L', 'O',
'W', 'O', 'R', 'L', 'D',
'L', 'E', 'A', 'D',
'N', 'V', 'I', 'D', 'I', 'A',
'O', 'N', 'E', 'S',
'A', 'I', 'R', 'P', 'O', 'R', 'T',
}
}.union(
{
'h', 'ə', 'ˈ', 'ɫ', 'o', 'ʊ',
'ˈ', 'w', 'ɝ', 'ɫ', 'd',
'ˈ', 'l', 'ɛ', 'd',
'ˈ', 'l', 'd',
'ɛ', 'n', 'ˈ', 'v', 'ɪ', 'd', 'ə',
'ˈ', 'd', 'ʒ', 'o', 'ʊ', 'n', 'z',
'ˈ', 'ɛ', 'ɹ', 'ˌ', 'p', 'ɔ', 'ɹ', 't',
}
)
# fmt: on
assert len(g2p.phoneme_dict["LEAD"]) == 2
assert len(g2p.phoneme_dict["JONES"]) == 1
assert len(g2p.phoneme_dict["NVIDIA"]) == 1
# Test with keep_alternate set to True (default)
g2p.replace_symbols(symbols=fixed_symbols, keep_alternate=True)
# Check that the alternate pron of "LEAD" was kept
assert len(g2p.phoneme_dict["LEAD"]) == 1
assert g2p.phoneme_dict["LEAD"][0] == list("ˈlɛd")
# Check that filtering was done for unique entries, both grapheme and phoneme
assert "JONES" not in g2p.phoneme_dict
assert "NVIDIA" not in g2p.phoneme_dict
# Check that other words weren't affected
assert g2p.phoneme_dict["HELLO"][0] == list("həˈɫoʊ")
assert g2p.phoneme_dict["WORLD"][0] == list("ˈwɝɫd")
assert g2p.phoneme_dict["AIRPORT"][0] == list("ˈɛɹˌpɔɹt")
# Test with keep_alternate set to False
g2p = self._create_g2p(use_chars=True, grapheme_prefix=self.GRAPHEME_PREFIX)
g2p.replace_symbols(symbols=fixed_symbols, keep_alternate=False)
# Check that both "LEAD" entries were removed
assert "LEAD" not in g2p.phoneme_dict
# Other checks remain the same
assert "JONES" not in g2p.phoneme_dict
assert "NVIDIA" not in g2p.phoneme_dict
assert g2p.phoneme_dict["HELLO"][0] == list("həˈɫoʊ")
assert g2p.phoneme_dict["WORLD"][0] == list("ˈwɝɫd")
assert g2p.phoneme_dict["AIRPORT"][0] == list("ˈɛɹˌpɔɹt")
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call(self):
input_text = "Hello world."
expected_output = [char for char in "həˈɫoʊ ˈwɝɫd."]
g2p = self._create_g2p()
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_file_or_object_dict_type(self):
input_text = "Hello world."
expected_output = [char for char in "həˈɫoʊ ˈwɝɫd."]
phoneme_dict = {"HELLO": ["həˈɫoʊ"], "WORLD": ["ˈwɝɫd"], "LEAD": ["ˈlɛd", "ˈlid"], "NVIDIA": ["ɛnˈvɪdiə"]}
g2p_file = self._create_g2p()
g2p_dict = self._create_g2p(phoneme_dict=phoneme_dict)
phonemes_file = g2p_file(input_text)
phonemes_dict = g2p_dict(input_text)
assert phonemes_dict == expected_output
assert phonemes_file == phonemes_dict
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_oov_word(self):
input_text = "Hello Kitty!"
expected_output = list("həˈɫoʊ") + [" "] + list("KITTY") + ["!"]
g2p = self._create_g2p()
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_oov_func(self):
input_text = "Hello Kitty!"
expected_output = list("həˈɫoʊ") + [" "] + list("test!")
g2p = self._create_g2p(apply_to_oov_word=lambda x: "test")
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_uppercase_grapheme_only(self):
input_text = "Hello world."
expected_output = [self.GRAPHEME_PREFIX + char if char not in " ." else char for char in input_text.upper()]
g2p = self._create_g2p(
use_chars=True,
phoneme_probability=0.0,
grapheme_case=GRAPHEME_CASE_UPPER,
grapheme_prefix=self.GRAPHEME_PREFIX,
)
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_lowercase_grapheme_only(self):
input_text = "Hello world."
expected_output = [self.GRAPHEME_PREFIX + char if char not in " ." else char for char in input_text.lower()]
g2p = self._create_g2p(
use_chars=True,
phoneme_probability=0.0,
grapheme_case=GRAPHEME_CASE_LOWER,
grapheme_prefix=self.GRAPHEME_PREFIX,
)
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_mixed_case_grapheme_only(self):
input_text = "Hello world."
expected_output = [self.GRAPHEME_PREFIX + char if char not in " ." else char for char in input_text]
g2p = self._create_g2p(
use_chars=True,
phoneme_probability=0.0,
grapheme_case=GRAPHEME_CASE_MIXED,
grapheme_prefix=self.GRAPHEME_PREFIX,
)
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_uppercase_grapheme_and_get_phoneme_only(self):
input_text = "Hello world."
expected_output = ["h", "ə", "ˈ", "ɫ", "o", "ʊ", " ", "ˈ", "w", "ɝ", "ɫ", "d", "."]
g2p = self._create_g2p(use_chars=True, phoneme_probability=1.0, grapheme_case=GRAPHEME_CASE_UPPER)
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_lowercase_grapheme_and_get_phoneme_only(self):
input_text = "Hello world."
expected_output = ["h", "ə", "ˈ", "ɫ", "o", "ʊ", " ", "ˈ", "w", "ɝ", "ɫ", "d", "."]
g2p = self._create_g2p(use_chars=True, phoneme_probability=1.0, grapheme_case=GRAPHEME_CASE_LOWER)
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_mixed_case_grapheme_and_get_phoneme_only(self):
input_text = "Hello world."
expected_output = ["h", "ə", "ˈ", "ɫ", "o", "ʊ", " ", "ˈ", "w", "ɝ", "ɫ", "d", "."]
g2p = self._create_g2p(use_chars=True, phoneme_probability=1.0, grapheme_case=GRAPHEME_CASE_MIXED)
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_with_escaped_characters(self):
input_text = "Hello |wo rld|."
expected_output = [
"h",
"ə",
"ˈ",
"ɫ",
"o",
"ʊ",
" ",
f"{self.GRAPHEME_PREFIX}wo",
f"{self.GRAPHEME_PREFIX}rld",
".",
]
g2p = self._create_g2p(grapheme_prefix=self.GRAPHEME_PREFIX)
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_instantiate_unsupported_locale(self):
with pytest.raises(ValueError, match="Unsupported locale"):
self._create_g2p(locale="en-USA")
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_de_de(self):
input_text = "Hallo „welt“" + " " + "Weg" + " " + unicodedata.normalize("NFD", "Abendröte!" + " " + "weg")
expected_output = (
list("hˈaloː „vˈɛlt“")
+ [" "]
+ list("vˈeːk")
+ [" "]
+ [f"{self.GRAPHEME_PREFIX}{char}" for char in unicodedata.normalize("NFC", "Abendröte")]
+ ["!"]
+ [" "]
+ list("vˈɛk")
)
g2p = self._create_g2p(
use_chars=True,
phoneme_dict=self.PHONEME_DICT_PATH_DE,
locale="de-DE",
grapheme_case=GRAPHEME_CASE_MIXED,
grapheme_prefix=self.GRAPHEME_PREFIX,
apply_to_oov_word=None,
)
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_en_us(self):
input_text = "Hello NVIDIA'S airport's Jones's airports worlds Kitty!"
g2p_upper = self._create_g2p(locale="en-US", grapheme_case=GRAPHEME_CASE_UPPER)
expected_output_upper = [char for char in "həˈɫoʊ ɛnˈvɪdiəz ˈɛɹˌpɔɹts ˈdʒoʊnzɪz ˈɛɹˌpɔɹts ˈwɝɫdz KITTY!"]
g2p_lower = self._create_g2p(
locale="en-US",
grapheme_case=GRAPHEME_CASE_LOWER,
grapheme_prefix=self.GRAPHEME_PREFIX,
apply_to_oov_word=None,
)
expected_output_lower = (
[char for char in "həˈɫoʊ ɛnˈvɪdiəz ˈɛɹˌpɔɹts ˈdʒoʊnzɪz ˈɛɹˌpɔɹts ˈwɝɫdz"]
+ [" "]
+ [f"{self.GRAPHEME_PREFIX}{char}" for char in "kitty"]
+ ["!"]
)
g2p_mixed = self._create_g2p(
locale="en-US",
grapheme_case=GRAPHEME_CASE_MIXED,
grapheme_prefix=self.GRAPHEME_PREFIX,
apply_to_oov_word=None,
)
expected_output_mixed = (
[char for char in "həˈɫoʊ ɛnˈvɪdiəz ˈɛɹˌpɔɹts ˈdʒoʊnzɪz ˈɛɹˌpɔɹts ˈwɝɫdz"]
+ [" "]
+ [f"{self.GRAPHEME_PREFIX}{char}" for char in "kitty"]
+ ["!"]
)
for g2p, expected_output in zip(
[g2p_upper, g2p_lower, g2p_mixed], [expected_output_upper, expected_output_lower, expected_output_mixed]
):
phonemes = g2p(input_text)
assert phonemes == expected_output
@pytest.mark.run_only_on('CPU')
@pytest.mark.unit
def test_forward_call_es_es(self):
input_text = "¿Hola mundo, amigo?"
expected_output = [char for char in "¿ˈola mˈundo, AMIGO?"]
g2p = self._create_g2p(phoneme_dict=self.PHONEME_DICT_PATH_ES, locale="es-ES")
phonemes = g2p(input_text)
assert phonemes == expected_output