Merge branch 'dev' into master

This commit is contained in:
Stardust·减
2023-09-30 09:12:30 +08:00
committed by GitHub
11 changed files with 75 additions and 253 deletions

View File

@@ -11,7 +11,12 @@ def cleaned_text_to_sequence(cleaned_text, tones, language):
Returns:
List of integers corresponding to the symbols in the text
"""
phones = [_symbol_to_id[symbol] for symbol in cleaned_text]
phones = [] # _symbol_to_id[symbol] for symbol in cleaned_text
for symbol in cleaned_text:
try:
phones.append(_symbol_to_id[symbol])
except KeyError:
phones.append(0) # symbol not found in ID map, use 0('_') by default
tone_start = language_tone_start_map[language]
tones = [i + tone_start for i in tones]
lang_id = language_id_map[language]

View File

@@ -2,17 +2,13 @@
# compatible with Julius https://github.com/julius-speech/segmentation-kit
import re
import unicodedata
import sys
from transformers import AutoTokenizer
from text import punctuation, symbols
try:
import MeCab
except ImportError as e:
raise ImportError("Japanese requires mecab-python3 and unidic-lite.") from e
from num2words import num2words
import pyopenjtalk
BERT = "./bert/bert-large-japanese-v2"
_CONVRULES = [
# Conversion of 2 letters
"アァ/ a a",
@@ -353,99 +349,6 @@ def hira2kata(text: str) -> str:
return text.replace("う゛", "")
_SYMBOL_TOKENS = set(list("・、。?!"))
_NO_YOMI_TOKENS = set(list("「」『』―()[][]"))
_TAGGER = MeCab.Tagger()
def text2kata(text: str) -> str:
parsed = _TAGGER.parse(text)
res = []
for line in parsed.split("\n"):
if line == "EOS":
break
parts = line.split("\t")
word, yomi = parts[0], parts[1]
if yomi:
res.append(yomi)
else:
if word in _SYMBOL_TOKENS:
res.append(word)
elif word in ("", ""):
res.append("")
elif word in _NO_YOMI_TOKENS:
pass
else:
res.append(word)
return hira2kata("".join(res))
_ALPHASYMBOL_YOMI = {
"#": "シャープ",
"%": "パーセント",
"&": "アンド",
"+": "プラス",
"-": "マイナス",
":": "コロン",
";": "セミコロン",
"<": "小なり",
"=": "イコール",
">": "大なり",
"@": "アット",
"a": "エー",
"b": "ビー",
"c": "シー",
"d": "ディー",
"e": "イー",
"f": "エフ",
"g": "ジー",
"h": "エイチ",
"i": "アイ",
"j": "ジェー",
"k": "ケー",
"l": "エル",
"m": "エム",
"n": "エヌ",
"o": "オー",
"p": "ピー",
"q": "キュー",
"r": "アール",
"s": "エス",
"t": "ティー",
"u": "ユー",
"v": "ブイ",
"w": "ダブリュー",
"x": "エックス",
"y": "ワイ",
"z": "ゼット",
"α": "アルファ",
"β": "ベータ",
"γ": "ガンマ",
"δ": "デルタ",
"ε": "イプシロン",
"ζ": "ゼータ",
"η": "イータ",
"θ": "シータ",
"ι": "イオタ",
"κ": "カッパ",
"λ": "ラムダ",
"μ": "ミュー",
"ν": "ニュー",
"ξ": "クサイ",
"ο": "オミクロン",
"π": "パイ",
"ρ": "ロー",
"σ": "シグマ",
"τ": "タウ",
"υ": "ウプシロン",
"φ": "ファイ",
"χ": "カイ",
"ψ": "プサイ",
"ω": "オメガ",
}
_NUMBER_WITH_SEPARATOR_RX = re.compile("[0-9]{1,3}(,[0-9]{3})+")
_CURRENCY_MAP = {"$": "ドル", "¥": "", "£": "ポンド", "": "ユーロ"}
_CURRENCY_RX = re.compile(r"([$¥£€])([0-9.]*[0-9])")
@@ -453,48 +356,12 @@ _NUMBER_RX = re.compile(r"[0-9]+(\.[0-9]+)?")
def japanese_convert_numbers_to_words(text: str) -> str:
res = _NUMBER_WITH_SEPARATOR_RX.sub(lambda m: m[0].replace(",", ""), text)
res = _CURRENCY_RX.sub(lambda m: m[2] + _CURRENCY_MAP.get(m[1], m[1]), res)
res = _NUMBER_RX.sub(lambda m: num2words(m[0], lang="ja"), res)
res = text
for x in _CURRENCY_MAP.keys():
res = res.replace(x, _CURRENCY_MAP[x])
return res
def japanese_convert_alpha_symbols_to_words(text: str) -> str:
return "".join([_ALPHASYMBOL_YOMI.get(ch, ch) for ch in text.lower()])
def japanese_text_to_phonemes(text: str) -> str:
"""Convert Japanese text to phonemes."""
res = unicodedata.normalize("NFKC", text)
res = japanese_convert_numbers_to_words(res)
# res = japanese_convert_alpha_symbols_to_words(res)
res = text2kata(res)
res = kata2phoneme(res)
return res
def is_japanese_character(char):
# 定义日语文字系统的 Unicode 范围
japanese_ranges = [
(0x3040, 0x309F), # 平假名
(0x30A0, 0x30FF), # 片假名
(0x4E00, 0x9FFF), # 汉字 (CJK Unified Ideographs)
(0x3400, 0x4DBF), # 汉字扩展 A
(0x20000, 0x2A6DF), # 汉字扩展 B
# 可以根据需要添加其他汉字扩展范围
]
# 将字符的 Unicode 编码转换为整数
char_code = ord(char)
# 检查字符是否在任何一个日语范围内
for start, end in japanese_ranges:
if start <= char_code <= end:
return True
return False
rep_map = {
"": ",",
"": ",",
@@ -510,18 +377,9 @@ rep_map = {
def replace_punctuation(text):
pattern = re.compile("|".join(re.escape(p) for p in rep_map.keys()))
replaced_text = pattern.sub(lambda x: rep_map[x.group()], text)
replaced_text = re.sub(
r"[^\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF"
+ "".join(punctuation)
+ r"]+",
"",
replaced_text,
)
replaced_text = text
for x in rep_map.keys():
replaced_text = replaced_text.replace(x, rep_map[x])
return replaced_text
@@ -533,54 +391,42 @@ def text_normalize(text):
return res
def distribute_phone(n_phone, n_word):
phones_per_word = [0] * n_word
for task in range(n_phone):
min_tasks = min(phones_per_word)
min_index = phones_per_word.index(min_tasks)
phones_per_word[min_index] += 1
return phones_per_word
tokenizer = AutoTokenizer.from_pretrained("./bert/bert-base-japanese-v3")
tokenizer = AutoTokenizer.from_pretrained(BERT)
def g2p(norm_text):
tokenized = tokenizer.tokenize(norm_text)
phs = []
ph_groups = []
for t in tokenized:
if not t.startswith("#"):
ph_groups.append([t])
else:
ph_groups[-1].append(t.replace("#", ""))
st = [x.replace("#", "") for x in tokenized]
word2ph = []
for group in ph_groups:
phonemes = kata2phoneme(text2kata("".join(group)))
# phonemes = [i for i in phonemes if i in symbols]
for i in phonemes:
assert i in symbols, (group, norm_text, tokenized)
phone_len = len(phonemes)
word_len = len(group)
aaa = distribute_phone(phone_len, word_len)
word2ph += aaa
phs += phonemes
phones = ["_"] + phs + ["_"]
tones = [0 for i in phones]
phs = pyopenjtalk.g2p(norm_text).split(
" "
) # Directly use the entire norm_text sequence.
for sub in st: # the following code is only for calculating word2ph
wph = 0
for x in sub:
sys.stdout.flush()
if x not in ["?", ".", "!", "", ","]: # This will throw warnings.
phonemes = pyopenjtalk.g2p(x)
else:
phonemes = "pau"
# for x in range(repeat):
wph += len(phonemes.split(" "))
# print(f'{x}-->:{phones}')
word2ph.append(wph)
phonemes = ["_"] + phs + ["_"]
tones = [0 for i in phonemes]
word2ph = [1] + word2ph + [1]
return phones, tones, word2ph
return phonemes, tones, word2ph
if __name__ == "__main__":
tokenizer = AutoTokenizer.from_pretrained("./bert/bert-base-japanese-v3")
tokenizer = AutoTokenizer.from_pretrained(BERT)
text = "hello,こんにちは、世界!……"
from text.japanese_bert import get_bert_feature
text = text_normalize(text)
print(text)
phones, tones, word2ph = g2p(text)
phones, tones, word2ph = g2p_ojt(text)
bert = get_bert_feature(text, word2ph)
print(phones, tones, word2ph, bert.shape)

View File

@@ -2,7 +2,12 @@ import torch
from transformers import AutoTokenizer, AutoModelForMaskedLM
import sys
tokenizer = AutoTokenizer.from_pretrained("./bert/bert-base-japanese-v3")
BERT = "./bert/bert-large-japanese-v2"
tokenizer = AutoTokenizer.from_pretrained(BERT)
# bert-large model has 25 hidden layers.You can decide which layer to use by setting this variable to a specific value
# default value is 3(untested)
BERT_LAYER = 3
models = dict()
@@ -24,13 +29,13 @@ def get_bert_feature(text, word2ph, device=None):
inputs = tokenizer(text, return_tensors="pt")
for i in inputs:
inputs[i] = inputs[i].to(device)
res = models[device](**inputs, output_hidden_states=True)
res = torch.cat(res["hidden_states"][-3:-2], -1)[0].cpu()
res = model(**inputs, output_hidden_states=True)
res = res["hidden_states"][BERT_LAYER]
assert inputs["input_ids"].shape[-1] == len(word2ph)
word2phone = word2ph
phone_level_feature = []
for i in range(len(word2phone)):
repeat_feature = res[i].repeat(word2phone[i], 1)
repeat_feature = res[0][i].repeat(word2phone[i], 1)
phone_level_feature.append(repeat_feature)
phone_level_feature = torch.cat(phone_level_feature, dim=0)

View File

@@ -74,6 +74,7 @@ num_zh_tones = 6
# japanese
ja_symbols = [
"pau",
"N",
"a",
"a:",
@@ -117,6 +118,8 @@ ja_symbols = [
"z",
"zy",
]
for x in range(ord("a"), ord("z") + 1):
ja_symbols.append(chr(x).upper())
num_ja_tones = 1
# English