[PYTHON] Wie man Kaldi mit JUST Corpus trainiert

Verwendung von JSUT Corpus mit Kaldi

――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.

Kaldi lernen

―― 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.

Installieren Sie Kaldi

Ausführungsumgebung

Wartung von NUR Korpus

――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

So erstellen Sie wav.scp

--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]

wav.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 |
...
...

So erstellen Sie Text

--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] `` `

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.

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)

So erstellen Sie lexicon.txt

--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.

So erstellen Sie utt2spk

--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)

So erstellen Sie spk2utt

--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)

Fang an zu lernen

--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

Genauigkeit beim Training nur mit dem JUST-Korpus

――Da die Datenmenge nur etwa 10 Stunden beträgt, konnte ich überhaupt nicht lernen. - WER = 70.78

Recommended Posts

Wie man Kaldi mit JUST Corpus trainiert
So testen Sie den Friends-of-Friends-Algorithmus mit pyfof
Ich habe versucht, die Sündenfunktion mit Chainer zu trainieren
So löschen Sie die angegebene Zeichenfolge mit dem Befehl sed! !! !!
Fügen Sie einfach den Treiber mit dem Mixer zum Formschlüssel hinzu
[Einführung in Python] Wie iteriere ich mit der Bereichsfunktion?
So erstellen Sie ein Untermenü mit dem Plug-In [Blender]
[Python] So legen Sie den Download-Speicherort mit youtube-dl fest
[Python] So schreiben Sie den Tabellenstil mit python-pptx um [python-pptx]
Wie man strukturiertes SVM von ChainCRF mit PyStruct lernt
[How to!] Lerne und spiele Super Mario mit Tensorflow !!
Wie aktualisiere ich mit SQLAlchemy?
Wie man mit Theano besetzt
Wie mit SQLAlchemy ändern?
So trennen Sie Zeichenfolgen mit ','
Wie man RDP auf Fedora31 macht
Wie benutzt man den Dekorateur?
Wie lösche ich mit SQLAlchemy?
So erhöhen Sie die Achse
So starten Sie die erste Projektion
Ich habe versucht zu simulieren, wie sich die Infektion mit Python ausbreitet
So manipulieren Sie das DOM im Iframe mit Selen
So gelangen Sie mit Vagrant in die Python-Entwicklungsumgebung
[Einführung in Python] So erhalten Sie Daten mit der Funktion listdir
So brechen Sie RT mit tweepy ab
Wie berechnet man den Autokorrelationskoeffizienten?
Python: So verwenden Sie Async mit
Verwendung der Zip-Funktion
Verwendung des optparse-Moduls
So verwenden Sie virtualenv mit PowerShell
So installieren Sie Python-Pip mit Ubuntu20.04LTS
Umgang mit unausgeglichenen Daten
Lesen des SNLI-Datensatzes
So erhalten Sie die Python-Version
Wie fange ich mit Scrapy an?
Erste Schritte mit Python
Wie fange ich mit Django an?
Aufblasen von Daten (Datenerweiterung) mit PyTorch
So überschreiben Sie die Ausgabe auf die Konsole
So installieren Sie den MySQL-Connector mit pip3
So verbinden Sie INNER mit SQL Alchemy
Verwendung des ConfigParser-Moduls
So installieren Sie Anaconda mit pyenv
[Hugo] Zusammenfassung zum Hinzufügen von Seiten zu der mit Learn erstellten Site
[Einführung in Python] So teilen Sie eine Zeichenfolge mit der Funktion split
So erhalten Sie die ID von Type2Tag NXP NTAG213 mit nfcpy
So erstellen Sie einen Befehl zum Lesen der Einstellungsdatei mit Pyramide
[Memo] Verwendung von Beautiful Soup4 (2) Zeigen Sie die Artikelüberschrift mit Anfragen an
[Python] Erklärt anhand eines Beispiels, wie die Formatierungsfunktion verwendet wird
So überwachen Sie den Ausführungsstatus von sqlldr mit dem Befehl pv
[Memo] Verwendung von BeautifulSoup4 (3) Zeigen Sie die Artikelüberschrift mit class_ an
So senden Sie eine Anfrage mit Python an die DMM (FANZA) -API
So führen Sie eine arithmetische Verarbeitung mit der Django-Vorlage durch