Refactor and improve: handling with yomi error, add preprocess options

This commit is contained in:
litagin02
2024-02-27 13:37:24 +09:00
parent dcad61be3a
commit ebea1be519
10 changed files with 101 additions and 56 deletions

View File

@@ -18,28 +18,14 @@ def cleaned_text_to_sequence(cleaned_text, tones, language):
return phones, tones, lang_ids
def get_bert(
text,
word2ph,
language,
device,
assist_text=None,
assist_text_weight=0.7,
ignore_unknown=False,
):
def get_bert(text, word2ph, language, device, assist_text=None, assist_text_weight=0.7):
if language == "ZH":
from .chinese_bert import get_bert_feature as zh_bert
return zh_bert(text, word2ph, device, assist_text, assist_text_weight)
from .chinese_bert import get_bert_feature
elif language == "EN":
from .english_bert_mock import get_bert_feature as en_bert
return en_bert(text, word2ph, device, assist_text, assist_text_weight)
from .english_bert_mock import get_bert_feature
elif language == "JP":
from .japanese_bert import get_bert_feature as jp_bert
return jp_bert(
text, word2ph, device, assist_text, assist_text_weight, ignore_unknown
)
from .japanese_bert import get_bert_feature
else:
raise ValueError(f"Language {language} not supported")
return get_bert_feature(text, word2ph, device, assist_text, assist_text_weight)

View File

@@ -1,4 +1,4 @@
def clean_text(text, language, use_jp_extra=True, ignore_unknown=False):
def clean_text(text, language, use_jp_extra=True, raise_yomi_error=False):
# Changed to import inside if condition to avoid unnecessary import
if language == "ZH":
from . import chinese as language_module
@@ -15,7 +15,7 @@ def clean_text(text, language, use_jp_extra=True, ignore_unknown=False):
norm_text = language_module.text_normalize(text)
phones, tones, word2ph = language_module.g2p(
norm_text, use_jp_extra, ignore_unknown=ignore_unknown
norm_text, use_jp_extra, raise_yomi_error=raise_yomi_error
)
else:
raise ValueError(f"Language {language} not supported")

View File

@@ -33,6 +33,16 @@ COSONANTS = set(
VOWELS = {"a", "i", "u", "e", "o", "N"}
class YomiError(Exception):
"""
OpenJTalkで、読みが正しく取得できない箇所があるときに発生する例外。
基本的に「学習の前処理のテキスト処理時」には発生させ、そうでない場合は、
ignore_yomi_error=Trueにしておいて、この例外を発生させないようにする。
"""
pass
# 正規化で記号を変換するための辞書
rep_map = {
"": ",",
@@ -166,7 +176,7 @@ def japanese_convert_numbers_to_words(text: str) -> str:
def g2p(
norm_text: str, use_jp_extra: bool = True, ignore_unknown: bool = False
norm_text: str, use_jp_extra: bool = True, raise_yomi_error: bool = False
) -> tuple[list[str], list[int], list[int]]:
"""
他で使われるメインの関数。`text_normalize()`で正規化された`norm_text`を受け取り、
@@ -175,7 +185,10 @@ def g2p(
- word2ph: 元のテキストの各文字に音素が何個割り当てられるかを表すリスト
のタプルを返す。
ただし`phones`と`tones`の最初と終わりに`_`が入り、応じて`word2ph`の最初と最後に1が追加される。
use_jp_extra: Falseの場合、「ん」の音素を「N」ではなく「n」とする。
raise_yomi_error: Trueの場合、読めない文字があるときに例外を発生させる。
Falseの場合は読めない文字が消えたような扱いとして処理される。
"""
# pyopenjtalkのフルコンテキストラベルを使ってアクセントを取り出すと、punctuationの位置が消えてしまい情報が失われてしまう
# 「こんにちは、世界。」と「こんにちは!世界。」と「こんにちは!!!???世界……。」は全て同じになる。
@@ -186,9 +199,9 @@ def g2p(
# punctuationがすべて消えた、音素とアクセントのタプルのリスト「ん」は「N」
phone_tone_list_wo_punct = g2phone_tone_wo_punct(norm_text)
# sep_text: 単語単位の単語のリスト
# sep_text: 単語単位の単語のリスト、読めない文字があったらraise_yomi_errorなら例外、そうでないなら読めない文字が消えて返ってくる
# sep_kata: 単語単位の単語のカタカナ読みのリスト
sep_text, sep_kata = text2sep_kata(norm_text, ignore_unknown=ignore_unknown)
sep_text, sep_kata = text2sep_kata(norm_text, raise_yomi_error=raise_yomi_error)
# sep_phonemes: 各単語ごとの音素のリストのリスト
sep_phonemes = handle_long([kata2phoneme_list(i) for i in sep_kata])
@@ -237,8 +250,12 @@ def g2p(
return phones, tones, word2ph
def g2kata_tone(norm_text: str, ignore_unknown: bool = False) -> list[tuple[str, int]]:
phones, tones, _ = g2p(norm_text, use_jp_extra=True, ignore_unknown=ignore_unknown)
def g2kata_tone(norm_text: str) -> list[tuple[str, int]]:
"""
テキストからカタカナとアクセントのペアのリストを返す。
推論時のみに使われるので、常に`raise_yomi_error=False`でg2pを呼ぶ。
"""
phones, tones, _ = g2p(norm_text, use_jp_extra=True, raise_yomi_error=False)
return phone_tone2kata_tone(list(zip(phones, tones)))
@@ -332,7 +349,7 @@ def g2phone_tone_wo_punct(text: str) -> list[tuple[str, int]]:
def text2sep_kata(
norm_text: str, ignore_unknown: bool = False
norm_text: str, raise_yomi_error: bool = False
) -> tuple[list[str], list[str]]:
"""
`text_normalize`で正規化済みの`norm_text`を受け取り、それを単語分割し、
@@ -341,6 +358,9 @@ def text2sep_kata(
例:
`私はそう思う!って感じ?` →
["", "", "そう", "思う", "!", "って", "感じ", "?"], ["ワタシ", "", "ソー", "オモウ", "!", "ッテ", "カンジ", "?"]
raise_yomi_error: Trueの場合、読めない文字があるときに例外を発生させる。
Falseの場合は読めない文字が消えたような扱いとして処理される。
"""
# parsed: OpenJTalkの解析結果
parsed = pyopenjtalk.run_frontend(norm_text)
@@ -369,10 +389,10 @@ def text2sep_kata(
# wordは正規化されているので、`.`, `,`, `!`, `'`, `-`, `--` のいずれか
if not set(word).issubset(set(punctuation)): # 記号繰り返しか判定
# ここはpyopenjtalkが読めない文字等のときに起こる
if ignore_unknown:
logger.error(f"Ignoring unknown: {word} in:\n{norm_text}")
continue
raise ValueError(f"Cannot read: {word} in:\n{norm_text}")
if raise_yomi_error:
raise YomiError(f"Cannot read: {word} in:\n{norm_text}")
logger.warning(f"Ignoring unknown: {word} in:\n{norm_text}")
continue
# yomiは元の記号のままに変更
yomi = word
elif yomi == "":

View File

@@ -19,12 +19,13 @@ def get_bert_feature(
device=config.bert_gen_config.device,
assist_text=None,
assist_text_weight=0.7,
ignore_unknown=False,
):
text = "".join(text2sep_kata(text, ignore_unknown=ignore_unknown)[0])
# text = text_normalize(text)
# 各単語が何文字かを作る`word2ph`を使う必要があるので、読めない文字は必ず無視する
# でないと`word2ph`の結果とテキストの文字数結果が整合性が取れない
text = "".join(text2sep_kata(text, raise_yomi_error=False)[0])
if assist_text:
assist_text = "".join(text2sep_kata(assist_text)[0])
assist_text = "".join(text2sep_kata(assist_text, raise_yomi_error=False)[0])
if (
sys.platform == "darwin"
and torch.backends.mps.is_available()