――Kaldi ist ein Toolkit, mit dem Sie den Spracherkenner nach Ihren Wünschen anpassen können. In diesem Artikel werde ich erläutern, wie Sie mit dem JSUT-Korpus (Download) des japanischen Audiodatensatzes Kaldi lernen. .. Der JSUT-Korpus ist ein 10-Stunden-Audiokorpus, der zu Forschungszwecken erstellt wurde. Bitte beachten Sie, dass für die kommerzielle Nutzung der Kontakt zum Autor erforderlich ist.
Textdaten werden unter CC-BY-SA 4.0 usw. lizenziert. Weitere Informationen finden Sie in der LIZENZ-Datei. Audiodaten können nur in den folgenden Fällen verwendet werden. Forschung an akademischen Institutionen Nichtkommerzielle Forschung (einschließlich Forschung in kommerziellen Organisationen) Persönlicher Gebrauch (einschließlich Blogs) Wenn Sie es für kommerzielle Zwecke verwenden möchten, siehe unten. Die Weitergabe dieser Audiodaten ist nicht gestattet, es ist jedoch möglich, einen Teil des Korpus (z. B. etwa 100 Sätze) auf Ihrer Webseite oder Ihrem Blog zu veröffentlichen. Wenn möglich, wäre es hilfreich, wenn Sie mich kontaktieren könnten, wenn Sie Ihre Artikel, Blog-Beiträge oder andere Erfolge veröffentlichen. Die Untersuchung des Beitrags dieses Korpus ist für uns eine sehr nützliche Information.
―― Wenn Sie Kaldi, Julius usw. lernen und Stimmen wie tägliche Gespräche genau erkennen möchten, benötigen Sie im Allgemeinen Tausende von Stunden Sprachkorpus. Die folgenden Korpora sind als Sprachkorpora für Forschungszwecke bekannt. Auf Japanisch ist der CSJ-Korpus (wahrscheinlich) der größte Korpus und hat ein Datenvolumen von etwa 600 Stunden. Diese Korpora sind aufgeladen und können nicht einfach verwendet werden. Daher möchte ich dieses Mal das kostenlos verteilte JSUT-Korpus verwenden und die grundlegende Verwendung von Kaldi teilen.
――Zunächst müssen Sie den JSUT-Korpus vorbereiten, damit er in Kaldi verwendet werden kann. Wenn Sie dies tun können, wird der Rest automatisch mit der Kraft des Rezepts gelernt. Alles, was Sie tun müssen, ist, das JSUT auf die gleiche Weise zu pflegen, wie CSJ eingegeben wird. ――Die folgenden fünf Dateien müssen in großer Größe vorbereitet werden. - wav.scp - text - lexicon.txt - utt2spk - spk2utt
jsut``` Verzeichnis ist eine Kopie des
`s5``` Verzeichnisses. *** Bitte beachten Sie, dass die symbolische Linkdatei beim Kopieren vorhanden ist. *** ***--wav.scp: Textdatei mit dem Pfad zur Audiodatei
python
import os,sys
import glob
from sklearn.model_selection import train_test_split
import subprocess
import numpy as np
np.random.seed(seed=32)
def sort_file(fname):
subprocess.call(f'sort {fname} > {fname}.sorted',shell=True)
subprocess.call(f'rm {fname}',shell=True)
subprocess.call(f'mv {fname}.sorted {fname}',shell=True)
def convert_wav(wav_data_path,out_dir):
'''
* sampling frequency must be 16kHz
* wav file of JSUT is 48.1kHz, so convert to 16kHz using sox
e.g. FILE_ID sox [input_wavfilename] -r 16000 [output_wavfilename]
'''
for wav_data in wav_data_path:
fname = wav_data.split('/')[-1]
subprocess.call(f'sox {wav_data} -r 16000 {out_dir}/{fname}',shell=True)
subprocess.call(f'chmod 774 {out_dir}/{fname}',shell=True)
def make_wavscp(wav_data_path_list,out_dir,converted_jsut_data_dir):
'''
wav.scp: format -> FILE_ID cat PATH_TO_WAV |
'''
out_fname = f'{out_dir}/wav.scp'
with open(out_fname,'w') as out:
for wav_data_path in wav_data_path_list:
file_id = wav_data_path.split('/')[-1].split('.')[0]
out.write(f'{file_id} cat {converted_jsut_data_dir}/{file_id}.wav |\n')
sort_file(out_fname)
#Aktuelles Verzeichnis-> kaldi/egs/csj/jsut (jsut ist dasselbe wie s5, hat nur den Verzeichnisnamen geändert. Stellen Sie beim Kopieren von s5 sicher, dass Sie den symbolischen Link erben.(cp -Option a wie a hinzufügen))
data_dir = './data'
train_dir = f'{data_dir}/train'
eval_dir = f'{data_dir}/eval'
original_jsut_data_dir = '/path/to/JSUT/corpus'
converted_jsut_data_dir = '/path/to/converted/JSUT/corpus'
# make wav.scp of train and eval
wav_data_path = glob.glob(f'{original_jsut_data_dir}/*/wav/*.wav')
# convert JSUT wav data to 16kHz
convert_wav(wav_data_path,converted_jsut_data_dir)
# split data [train_size = 7196, test_size = 500]
train_wav_data_list, eval_wav_data_list = train_test_split(wav_data_path, test_size=500)
make_wavscp(train_wav_data_list,train_dir,converted_jsut_data_dir)
make_wavscp(eval_wav_data_list,eval_dir,converted_jsut_data_dir)
――Der Befehl sox wird beim Konvertieren von 44,1 kHz in 16 kHz verwendet. Sie können dies auch mit ffmpeg tun.
--``` sox [Name der zu konvertierenden Sprachdatei] -r 16000 [Name der Sprachdatei nach der Konvertierung]
`chmod``` hinzu. --Wav.scp Format --``` Datei-ID cat Pfad zur Audiodatei |
``np.random.seed (seed = 32)`
`
--Text und utt2spk werden auch für Schulungen und Tests erstellt
--wav.scp sollte so aussehenwav.scp
BASIC5000_0051 cat /home/kaldi/egs/csj/jsut/JSUT/BASIC5000_0051.wav |
BASIC5000_0053 cat /home/kaldi/egs/csj/jsut/JSUT/BASIC5000_0053.wav |
BASIC5000_0082 cat /home/kaldi/egs/csj/jsut/JSUT/BASIC5000_0082.wav |
BASIC5000_0094 cat /home/kaldi/egs/csj/jsut/JSUT/BASIC5000_0094.wav |
BASIC5000_0101 cat /home/kaldi/egs/csj/jsut/JSUT/BASIC5000_0101.wav |
...
...
--text: Einfach ausgedrückt handelt es sich um einen Transkriptionstext für die Stimme. Mit anderen Worten, es ist eine Textversion der gesprochenen Wörter. Der transkribierte Text wird von Anfang an im JSUT-Korpus bereitgestellt. Wenn es sich um ein Sprachkorpus handelt, sind mindestens Sprache und Transkription enthalten. --Textformat --``` [Sprach-ID] [Transkribierter Text mit Informationen zum Teiltext] `` `
z. B. UTT001 Morgen + Nomenklatur ist + Assistent / Assistent --Entfernen Sie die Satzzeichen.
Ersetzen Sie es genau genommen durch das
Zunächst wird der transkribierte Text morphologisch analysiert (gleichzeitig wird auch "Wortlesen" erfasst), um die Informationen zum Teiltext zu erhalten, und es wird eine "Transkript" -Datei erstellt. Erstellen Sie eine `Text
Datei aus dieser
`` `Transkript``` Datei. Die "Transkript" -Datei schreibt jedes Wort, das morphologisch analysiert wurde, in Form von "Wort" + Katakana-Lesen + Teilwort "".
MeCab (mecab-ipadic-neologd) *** wurde für den morphologischen Analysator verwendet.
--``` chasen_tagger = MeCab.Tagger ("-d / usr / local / lib / mecab / dic / mecab-ipadic-neologd") Bitte ändern Sie den Teil von
entsprechend Ihrer Umgebung.
――Bitte beachten Sie, dass das Ergebnis anders ausfällt, wenn Sie einen anderen morphologischen Analysator verwenden.
python
def make_transcript(transcript_data_path_list,train_dir,eval_dir,error_dir,eval_wav_data_list):
'''
text: format -> UTT_ID TRANSCRIPT
* UTT_ID == FILE_ID (one wav file <-> one utterance)
transcript_data_path_list:NUR Corpus-Transkriptionstextdatei(transcript_utf8.txt)Pfadliste zu(transcript_utf8.Es gibt mehrere txt)
train_dir:Für das Training
'''
# change hankaku to zenkaku
ZEN = "".join(chr(0xff01 + i) for i in range(94))
HAN = "".join(chr(0x21 + i) for i in range(94))
HAN2ZEN = str.maketrans(HAN,ZEN)
eval_utt_id_list = []
for eval_wav_data in eval_wav_data_list:
eval_utt_id_list.append(eval_wav_data.split('/')[-1].split('.')[0])
word_reading_fname = './word_reading.txt'
word_reading_dict = {} # {'word':'reading'}
with open(word_reading_fname,'r') as f:
lines = f.readlines()
for line in lines:
split_line = line.strip().split('+')
word_reading_dict[split_line[0]] = split_line[1]
out_train_fname = f'{train_dir}/transcript'
out_eval_fname = f'{eval_dir}/transcript'
out_no_reading_word_fname = f'{error_dir}/no_reading_word.txt'
no_reading_word_list = []
chasen_tagger = MeCab.Tagger ("-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd")
with open(out_train_fname,'w') as out_train, open(out_eval_fname,'w') as out_eval,\
open(out_no_reading_word_fname,'w') as no_reading:
for transcript_data_path in transcript_data_path_list:
with open(transcript_data_path,'r') as trans:
line = trans.readline()
while line:
split_line = line.strip().split(':')
utt_id = split_line[0]
transcript = split_line[1].translate(HAN2ZEN)
transcript = transcript.replace('・',' ').replace('-',' ').replace('』',' ').replace('『',' ').replace('」',' ').replace('「',' ')
node = chasen_tagger.parseToNode(transcript)
transcript_line = []
while node:
feature = node.feature
if feature != 'BOS/EOS,*,*,*,*,*,*,*,*':
surface = node.surface
split_feature = feature.split(',')
reading = split_feature[-1]
part_of_speech = '/'.join(split_feature[:2]).replace('/*','')
# extract no reading word to error/no_reading_word_list.txt
if reading == '*':
if surface not in no_reading_word_list:
no_reading_word_list.append(surface)
no_reading.write(f'{surface}\n')
if surface == '、' or surface == '。' or surface == ',' or surface == '.':
transcript_line.append('<sp>')
elif surface != '- -':
if reading == '*':
reading = word_reading_dict[surface]
transcript_line.append('{}+{}+{}'.format(surface,reading,part_of_speech))
else:
transcript_line.append('{}+{}+{}'.format(surface,reading,part_of_speech))
node = node.next
transcript_line = ' '.join(transcript_line)
if utt_id in eval_utt_id_list:
out_eval.write(f'{utt_id} {transcript_line}\n')
else:
out_train.write(f'{utt_id} {transcript_line}\n')
line = trans.readline()
sort_file(out_train_fname)
sort_file(out_eval_fname)
data_dir = './data'
train_dir = f'{data_dir}/train'
eval_dir = f'{data_dir}/eval'
original_jsut_data_dir = '/path/to/JSUT/corpus'
# split data [train_size = 7196, test_size = 500]
train_wav_data_list, eval_wav_data_list = train_test_split(wav_data_path, test_size=500)
# make text of train and eval
transcript_data_path = glob.glob(f'{original_jsut_data_dir}/*/transcript_utf8.txt')
make_transcript(transcript_data_path,train_dir,eval_dir,error_dir,eval_wav_data_list)
make_text(train_dir,eval_dir)
――Beide halbe und volle Breite sind in Ordnung, aber es ist bequemer, sie in voller Breite zu machen, sodass sie in volle Breite konvertiert wird.
word_reading.txt
Lesen von Informationen zu Wörtern, deren Katakana-Lesung bei der Durchführung einer morphologischen Analyse mit MeCab unbekannt war. ―― *** Im Fall von JSUT-Korpus gibt es in Katakana nicht so viele Wörter mit unbekannten Lesarten, so dass ich es selbst tun konnte, aber ich muss darüber nachdenken, wie ich einen Korpus mit zu vielen Wörtern mit unbekannten Lesungen verwenden kann. *** CSJ, JNAS usw. erfordern das Lesen von Katakana in einem anderen Text, sodass es kein Problem gibt, wenn Sie es verwenden.
Als nächstes schreiben Sie das Wort / Teil-Wortpaar aus der `Transkript```-Datei in die`
Text-Datei. --
Transcritp``` Es ist einfach, weil Sie einfach den Teil "Katakana Reading" des Wortes aus dem Inhalt der Datei entfernen.
python
def make_text(train_dir,eval_dir):
train_transcript_fname = f'{train_dir}/transcript'
eval_transcript_fname = f'{eval_dir}/transcript'
out_train_fname = f'{train_dir}/text'
out_eval_fname = f'{eval_dir}/text'
with open(train_transcript_fname,'r') as trian_trans, open(eval_transcript_fname,'r') as eval_trans, \
open(out_train_fname,'w') as out_train, open(out_eval_fname,'w') as out_eval:
train_trans_line = trian_trans.readline()
while train_trans_line:
split_train_trans_line = train_trans_line.strip().split(' ')
# if <sp> is in End of Sentence then remove it.
if split_train_trans_line[-1] == "<sp>":
split_train_trans_line.pop(-1)
out_train.write(split_train_trans_line[0]+' ') # write utt_id
for i,word in enumerate(split_train_trans_line[2:]):
if word == '<sp>':
out_train.write(' <sp>')
else:
split_word = word.split('+')
out_train.write(' {}+{}'.format(split_word[0],split_word[2]))
out_train.write('\n')
train_trans_line = trian_trans.readline()
eval_trans_line = eval_trans.readline()
while eval_trans_line:
split_eval_trans_line = eval_trans_line.strip().split(' ')
# if <sp> is in End of Sentence then remove it.
if split_eval_trans_line[-1] == "<sp>":
split_eval_trans_line.pop(-1)
out_eval.write(split_eval_trans_line[0]+' ') # write utt_id
for i,word in enumerate(split_eval_trans_line[2:]):
if word == '<sp>':
out_eval.write(' <sp>')
else:
split_word = word.split('+')
out_eval.write(' {}+{}'.format(split_word[0],split_word[2]))
out_eval.write('\n')
eval_trans_line = eval_trans.readline()
sort_file(out_train_fname)
sort_file(out_eval_fname)
data_dir = './data'
train_dir = f'{data_dir}/train'
eval_dir = f'{data_dir}/eval'
make_text(train_dir,eval_dir)
--lexicon.txt: Eine Textdatei wie ein Wortwörterbuch, in das für jede Zeile `Wort + Teilwort-Aussprachesymbol``` `geschrieben wird. Alphabetische Symbole werden mithilfe der Katakana-Leseinformationen hinzugefügt. --Die Erstellungsmethode besteht darin, jedes in der Transkriptdatei geschriebene Wort in die Datei "lexicon.txt" zu schreiben, um es nicht zu duplizieren, und "kana2phone" im CSJ-Rezept vorzubereiten. Wir werden Aussprachesymbole mit einer Datei namens
`hinzufügen.
python
def make_lexicon(train_dir,lexicon_dir):
'''
lexicon: format -> 'word'+'part of speech'
'''
transcript_fname = f'{train_dir}/transcript'
out_lexicon_fname = f'{lexicon_dir}/lexicon.txt'
out_lexicon_htk_fname = f'{lexicon_dir}/lexicon_htk.txt'
with open(transcript_fname,'r') as trans, open(out_lexicon_fname,'w') as out:
trans_line = trans.readline()
while trans_line:
split_trans_line = trans_line.strip().split(' ')[2:]
for word in split_trans_line:
if word != '<sp>':
out.write(word+'\n')
trans_line = trans.readline()
subprocess.call(f'sort -u {out_lexicon_fname} > {out_lexicon_htk_fname}',shell=True)
subprocess.call(f'./local/csj_make_trans/vocab2dic.pl -p local/csj_make_trans/kana2phone -e ./data/lexicon/ERROR_v2d -o {out_lexicon_fname} {out_lexicon_htk_fname}',shell=True)
subprocess.call(f"cut -d'+' -f1,3- {out_lexicon_fname} >{out_lexicon_htk_fname}",shell=True)
subprocess.call(f"cut -f1,3- {out_lexicon_htk_fname} | perl -ape 's:\t: :g' >{out_lexicon_fname}",shell=True)
data_dir = './data'
train_dir = f'{data_dir}/train'
lexicon_dir = f'{data_dir}/lexicon'
# make lexicon fomr data/train/transcript
make_lexicon(train_dir,lexicon_dir)
--lexicon.txt wird nur aus dem Trainingstext (jsut / data / train / transkript) erstellt. Mit dem Testtext können Sie keine korrekte Bewertung vornehmen.
--utt2spk: Eine Textdatei, in der ein Paar Sprach- und Sprecher-ID gespeichert ist. Die Sprach-ID wurde auch in der `text```-Datei und`
wav.scp``` verwendet. Im Fall des JSUT-Korpus sind die Sprach-ID und die Datei-ID identisch. Dies liegt an einer Audiodatei und einer Sprache. Im CSJ-Korpus usw. enthält eine Sprachdatei mehrere Äußerungen, sodass die Äußerungs-ID = Datei-ID nicht gilt.
python
def make_utt2spk(dir):
'''
In JSUT corpus, speaker number is one person.
It is not good for training Acoustic Model.
'''
text_fname = f'{dir}/text'
out_utt2spk_fname = f'{dir}/utt2spk'
speaker_id = "jsut_speaker"
with open(text_fname,'r') as text, open(out_utt2spk_fname,'w') as out:
text_line = text.readline()
while text_line:
utt_id = text_line.split(' ')[0]
out.write(f'{utt_id} {speaker_id}\n')
text_line = text.readline()
data_dir = './data'
train_dir = f'{data_dir}/train'
eval_dir = f'{data_dir}/eval'
# make utt2spk
make_utt2spk(train_dir)
make_utt2spk(eval_dir)
--spk2utt: Das Gegenteil von utt2spk
python
def make_spk2utt(dir):
utt2spk_fname = f'{dir}/utt2spk'
out_spk2utt_fname = f'{dir}/spk2utt'
with open(utt2spk_fname,'r') as utt2spk, open(out_spk2utt_fname,'w') as out:
speaker_utt_dict = {} # {'speaker_id':'utt_id'}
utt2spk_line = utt2spk.readline()
while utt2spk_line:
split_utt2spk_line = utt2spk_line.strip().split(' ')
utt_id = split_utt2spk_line[0]
spk_id = split_utt2spk_line[1]
if spk_id in speaker_utt_dict:
speaker_utt_dict[spk_id].append(utt_id)
else:
speaker_utt_dict[spk_id] = [utt_id]
utt2spk_line = utt2spk.readline()
for spk_id, utt_id_list in speaker_utt_dict.items():
out.write(f'{spk_id}')
for utt_id in utt_id_list:
out.write(f' {utt_id}')
out.write('\n')
data_dir = './data'
train_dir = f'{data_dir}/train'
eval_dir = f'{data_dir}/eval'
# make spk2utt
make_ spk2utt(train_dir)
make_ spk2utt(eval_dir)
--Klonen Sie Kaldi von Github, installieren Sie die erforderlichen Tools usw. und erstellen Sie zunächst ein Verzeichnis für jsut.
#Wechseln Sie in das Verzeichnis, in dem sich das CSJ-Rezept befindet
cd /home/kaldi/egs/csj
#Kopieren Sie das s5-Verzeichnis mit dem Namen jsut(Stellen Sie sicher, dass Sie mit Option a kopieren)
cp -a s5 jsut
Da es nicht möglich ist, mit dem JSUT-Korpus gemäß dem CSJ-Rezept zu trainieren, müssen einige Programme geändert werden.
Verwenden Sie `nnet3 TDNN + Chain``` zum Lernen. --- *** Dieses Mal werden wir den JSUT-Korpus verwenden, also müssen wir
run.sh``` usw. ändern. *** *** --Wenn Sie den CSJ-Korpus verwenden, können Sie das Shell-Skript ``
kaldi / egs / csj / run.sh``` ausführen, um die Daten vorzubereiten, das akustische Modell und das Sprachmodell zu lernen und auszuwerten.
Zusätzlich zu run.sh müssen Sie einige Dateien ändern. Notieren Sie sich die Dateinamen, die unten geändert werden müssen.
Fügen Sie Code hinzu, um wav.scp, Text usw. für jsut zu erstellen
Der Pfad wurde hinzugefügt, um das Lexikon nur zu verwenden
Änderung der Parameter des Parallelverarbeitungsteils
Änderung der Parameter des Parallelverarbeitungsteils
Änderung der Parameter des Parallelverarbeitungsteils
Da das JSUT-Korpus nur einen Sprecher hat, ist es grundsätzlich unmöglich, die Anzahl der parallelen Prozesse zu erhöhen. Wenn Sie eine große Anzahl von Lautsprechern haben, können Sie die Anzahl der Parallelen erhöhen.
Das geänderte Programm wird als Referenz auf Github gelegt. Programme zum Erstellen von `` wav.scp``` usw. sind ebenfalls enthalten. Bitte fügen Sie sie unter
`kaldi / egs / csj / jsut``` ein. Nachdem Sie die obigen 5 Dateien geändert haben, führen Sie einfach run.sh aus.
Programm --Dateien unter kaldi / egs / csj / jsut - prepare_data.py - word_reading.txt
Es enthält auch ein Verzeichnis mit dem Namen `src / prepare_data```. Nachdem prepare_data.py ausgeführt wurde, wird ein Verzeichnis mit dem Namen
jsut / data / lexicon``` erstellt und eine Datei mit dem Namen
ERROR_v2d``` erstellt. Diese Datei enthält Wörter, für die keine phonetischen Elemente hinzugefügt werden konnten. Diese Wörter müssen manuell korrigiert werden. Ich habe die modifizierte Version in
prepare_data``` für alle Fälle vorbereitet. Sie können es verwenden, indem Sie es durch ``
jsut / data / lexicon / lexicon.txt`` `ersetzen.
Obwohl es möglich ist, das Lernen durch parallele Verarbeitung zu beschleunigen, ist es aufgrund der unzureichenden Anzahl von CPU-Threads möglicherweise nicht möglich, gut zu lernen. Derzeit sind die Parameter so eingestellt, dass die Parallelverarbeitung, z. B. die Parallelverarbeitung, nicht so oft wie möglich ausgeführt wird. Dies wird einige Zeit in Anspruch nehmen, aber ich denke, dass Sie mit einem PC mit einer gewissen Leistung lernen können. --Parameter zum Ändern der Anzahl paralleler Prozesse (in run.sh festgelegt) - --nj N
Ändern Sie den N-Teil
Beim Training mit der GPU muss der "Exklusivmodus" eingestellt werden
-- sudo nvidia-smi -c 3
und führen Sie den Befehl aus.
――Da die Datenmenge nur etwa 10 Stunden beträgt, konnte ich überhaupt nicht lernen. - WER = 70.78
Recommended Posts