Deepspeech et le Training

Objectif

Un court article pour montrer comment trainer Deepspeech un peu plus rapidement.

Nous allons utiliser un script qui va lire un fichier qui contient toutes nos phrases (sentences2.txt)

Le script va automatiquement créer les fichiers vocabulary.txt & voices.csv

Côté serveur, on va utiliser un autre script qui va se connecter à notre client (Raspberry pi) et il va aller chercher tous les fichiers dont il a besoin pour lancer un training.

NOTE

Après avoir copié les scripts, veuillez transformer les tabs en espaces.

Matériel Nécessaire

  • J'utilise le serveur précédemment créé Tensorflow & Deepspeech
  • Un raspberry pi 3 avec Stretch
  • Un micro array Matrix Voice

Partie 1 : Configurer le Serveur

Le script

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Auteur : Tommy Gingras
# Date : 2018-02-21
import paramiko
import scp
import os
from paramiko import SSHClient
from scp import SCPClient

## VARIABLES DEFINITONS ##
server='1.1.1.1'
port='22'
user='pi'
password='PAssWORD'

## FUNCTIONS DEFINITIONS
def clear():
    os.system('clear')

def progress(filename, size, sent):
    clear()
    print filename + " " + str(size)

def createSSHClient(server, port, user, password):
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(server, port, user, password)
    return client

def clean_vocabulary(filename):
    os.system("sed -i -e 's/\xc2\xa0/ /' "+filename)
    os.system("sed -i -e '/^$/d' "+filename)

## 'MAIN' ##
ssh = createSSHClient(server, port, user, password)
# Sanitize to authorize widlcard
scp = SCPClient(ssh.get_transport(), sanitize=lambda x: x, progress=progress)

scp.get('/home/pi/john/vocabulary.txt', '/home/tgingras/vocal/data/new/')
scp.get('/home/pi/john/voice.csv', '/home/tgingras/vocal/data/new/')
scp.get("/home/pi/john/voices/valid/*", '/home/tgingras/vocal/data/audio/', recursive=True)


# Already existant vocabulary file open read file
vocabulary_file=open('/home/tgingras/vocal/data/vocabulary.txt','r+')
_vocabulary = vocabulary_file.readlines()

# File to append vocabulary
new_vocabulary = open('/home/tgingras/vocal/data/new/vocabulary.txt', 'r+')
_new_vocabulary = new_vocabulary.readlines()
append_array = []

# create the append list (vocabulary)
for line in _new_vocabulary:
        if line in _vocabulary:
                print('line already present')
        else:
                append_array.append(line.decode('string_escape'))

# check voice file
new_voice_file = open('/home/tgingras/vocal/data/new/voice.csv', 'r+')
voice = open('/home/tgingras/vocal/data/voice.csv','a')
new_voice = new_voice_file.readlines()

voice_file=open('/home/tgingras/vocal/data/voice.csv','r+')
_voice=voice_file.readlines()
append_voice=[]

# create the append list (voice)
for line in new_voice:
        if line in _voice:
                print('voice already present')
        else:
                append_voice.append(line.decode('string_escape'))

_vocabulary = vocabulary_file.readlines()
# open existant vocabulary file in append mode
vocabulary = open('/home/tgingras/vocal/data/vocabulary.txt','a')
# for each element in the array, append it
for item in append_array:
    vocabulary.write("%s" % item)
vocabulary.close()


for item in append_voice:
    voice.write("%s" % item)
voice.close()

clean_vocabulary('/home/tgingras/vocal/data/vocabulary.txt')


os.system("sed -i -e 's/\xc2\xa0/ /' /home/tgingras/vocal/data/voice.csv")
os.system("sed -i -e '/^$/d' /home/tgingras/vocal/data/voice.csv")


os.system("/home/tgingras/vocal/kenlm/build/bin/./lmplz --text /home/tgingras/vocal/data/vocabulary.txt --arpa /home/tgingras/vocal/data/words.arpa --o 5")
os.system("/home/tgingras/vocal/kenlm/build/bin/./build_binary -T -s /home/tgingras/vocal/data/words.arpa /home/tgingras/vocal/data/lm.binary")
os.system("/home/tgingras/vocal/tfv1/tensorflow/bazel-bin/native_client/generate_trie /home/tgingras/vocal/data/alphabet.txt /home/tgingras/vocal/data/lm.binary /home/tgingras/vocal/data/vocabulary.txt /home/tgingras/vocal/data/trie")

print("From there we can launch the training script")

Explication du script

Pour le moment tout est 'Hard codé'. N'oubliez pas de modifier chaque chemin pour refléter votre setup.

Sinon le script se connecte au Raspberry Pi et va chercher les fichiers audios puis les fichiers voices.csv et vocabulary.txt. ensuite il recrée les fichiers : lm.binary, trie et words.arpa

Pour lancer le script, il faut seulement l'enregistrer : auto_prepare.py et lancer python auto_prepare.py

Partie 2 : Configurer le Client

Le Script

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import pyaudio
import wave
import subprocess
import time

last_entry = 0
start_point = 0
location = ''
validLocation = ''
trainingPath = ''

def clear():
    os.system('clear')

def get_prefix():
    filename = raw_input("\n  >> Please enter the file prefix( e.g. 'tom-18-02-17-near-' ) ?? : ")
    return filename

def set_sentences_file():
    user_link = raw_input("\n  >> Please enter the text sentences path (Default : /home/pi/john/sentences2.txt) [ENTER] to select default : ")
    if user_link == '':
        return '/home/pi/john/sentences2.txt'
    else:
        return user_link

def load_sentences():
    text_link = set_sentences_file()
    text = open(text_link)
    return text.readlines()

def print_header(sentences_file):
    clear()
    print('Sentences to read : '+str(len(sentences_file))+' lines')
    raw_input('\n\nPress [ENTER] to continue')

def preparation_question():
    global last_entry
    global start_point

    user_last_entry = raw_input("\n\n  >> Which is the last entry recorded ? (If no record = 0, Otherwise check the Excel): [0]")
    if user_last_entry == '':
        last_entry=0
    else:
        last_entry = int(user_last_entry)

    user_start_point = raw_input("\n\n  >> Which line from the sentences file you want to read ? (0 = First one): [0]")
    if user_start_point == '':
        start_point=0
    else:
        start_point = int(user_start_point)

def folders_structure(prefix):
    global location
    global validLocation
    global trainingPath

    location = raw_input("\n  >> Where to save the temporary record ?? : [/home/pi/john/voices/]")
    if location == '':
        location = '/home/pi/john/voices/'+prefix

    validLocation = raw_input("\n  >> Where to save the validated record ?? : [/home/pi/john/voices/valid/]")
    if validLocation == '':
        validLocation = '/home/pi/john/voices/valid/'

    trainingPath = raw_input("\n  >> Where is the training path (Deepspeech server) ?? : ['/home/tgingras/vocal/data/audio/']")
    if trainingPath == '':
        trainingPath = '/home/tgingras/vocal/data/audio/'

def read(sentences_file, data):
    index = data
    print('▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒')
    print(' Read this sentence :  '+str(sentences_file[index]))+' Index: '+str(index+1)
    print('▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒')

    return

def record(data):
    index = data
    print('▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒')
    print('                                 WAIT BEFORE READING')
    print('▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒')
    os.system('killall -9 arecord')
    time.sleep(1)
    print('▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒')
    print('                                 LISTENING ...')
    print('▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒')
    os.system('arecord --fatal-error -r 16000 -c 1 -f S16_LE --device=mic_channel8 -t wav  '+index+'.wav')
    print('\n                  saved to : '+str(index)+'.wav')
    return

def recording_process(sentences_file, last_entry, start_point, prefix, location, validLocation, trainingPath):
    a=(len(sentences_file))
    for value in range(last_entry, (a)):
        read(sentences_file, (value-last_entry)+start_point)
        record(location+str(value+1))
        while True:
            keyboard = raw_input('Keep          : [ENTER]\nRetry         : [r]    \nKeep And Exit : [k]    \nPlay          : [p]    \n ')
            if keyboard == '' or keyboard == 'k': # KEEP
                clear()
                print('Processing ...')
                # Process to save the sample.
                vocalCSV = open('voice.csv', 'a')
                doneSentences = open(prefix+'_sentences.csv', 'a')
                doneSentences.write(str(sentences_file[(value-last_entry)+start_point]))
                doneSentences.close()
                name=prefix+str(value+1)+'.wav'
                newFile=location+str(value+1)+'.wav'
                newFile_p=validLocation+prefix+str(value+1)+'.wav'
                # SILENCE is optional, maube it will cause bugs
                os.system('sox -c 1 -r 16k -t wav ' + newFile + ' '+ newFile_p + ' silence -l 1 0.1 1% -1 2.0 1%')
                print('Processing ....')
                os.system('rm ' + newFile)
                filesize=subprocess.check_output("ls -l "+newFile_p+" | awk '{print $5}'", shell=True).strip()
                vocalCSV.write(trainingPath+name+','+str(filesize)+','+str(sentences_file[(value-last_entry)+start_point]))
                vocalCSV.close()
                vocabularyCSV=open('vocabulary.txt','a')
                vocabularyCSV.write(str(sentences_file[(value-last_entry)+start_point]))
                vocabularyCSV.close()
                print('OK.')
                if keyboard == 'k':
                    #print("Last entry : " + str(last_entry) + " & Line from the sentence file : " + str(start_point))
                    print('Bye Bye')
                    return 'Bye Bye !'
                break
            if keyboard == 'r':
                print(' Try Again\n')
                read(sentences_file, (value-last_entry)+start_point)
                record(location+str(value+1))
                clear()
            if keyboard == 'p':
                print('Plug a speaker !')
                os.system('aplay ' + location+str(value+1)+'.wav')
            else:
                print('\nplease be Careful.')
                clear()



if __name__ == "__main__":
    clear()
    # Get the prefic for final files.
    prefix = get_prefix()
    # Load the sentences that needs to be read.
    sentences_file = load_sentences()
    print_header(sentences_file)
    preparation_question()
    folders_structure(prefix)
    print(location)
    print(validLocation)
    print(trainingPath)
    start = recording_process(sentences_file, last_entry, start_point, prefix, location, validLocation, trainingPath)

Explication du script

Vous devez fournir un fichier qui contient toutes les phrases à lire. (sentences2.txt)
Répondre aux question selon ce que vous voulez faire.

Précision sur la question du 'training path' : lorsque le serveur va effectuer le training, il se sert du fichier voice.csv pour faire l'association des fichiers et de leur contenu. Alors il est important de mettre le chemin complet où le serveur va stocker les données.

Pour l'exécuter : nano record.py & python record.py

Partie 3 : Utilisation du script Client

remplir toutes les questions. puis suivre les instructions à l'écran.

Pour arrêter l'enregistrement, (Après avoir lu la phrase.) Appuyer sur CTRL+C

[p] Vous pouvez écouter votre échantillon avant de la sauvegarder.

[enter]  permet de sauvegarder et de passer à la prochaine phrase.

[k] permet de sauvegarder et de quitter.

[r] permet de recommencer l'enregistrement.

NOTE

Après avoir enregistrer plus d'un heure, j'ai remarquer que certain enregistrement était trop bruyant ou qui manquait des mots. Alors il est très important, pour obtenir un résultat optimal d'écouter et de valider manuellement chaque enregistrement et de mettre à jour les fichiers voice.csv et vocabulary.txt en conséquence.

Partie 4 : Utilisation du script Serveur

Lorsque la session d'enregistrement est terminé sur le client, vous pouvez lancer le script. Il ira automatiquement chercher tous les fichiers nécessaires. ensuite vous pouvez manuellement partir le training.

Laisser un commentaire