Reconocimiento de voz con herramientas de aprendizaje automático

En mi trabajo, me enfrenté a la necesidad de verificar los registros de llamadas para verificar el cumplimiento de los empleados con el guión de conversación con los clientes. Por lo general, se asigna un empleado para esto, que pasa una gran cantidad de tiempo escuchando grabaciones de conversaciones. Nos propusimos la tarea de reducir el tiempo dedicado a la verificación utilizando herramientas de reconocimiento automático de voz (ASR). Examinaremos más de cerca una de estas herramientas.





Nvidia NeMo es un  conjunto de herramientas de aprendizaje automático para crear y entrenar modelos con tecnología GPU.





Los modelos de NeMo utilizan un enfoque moderno para el reconocimiento de voz: Clasificación de tiempo conexionista (CTC).





Antes de CTC, se utilizaba un enfoque en el que el archivo de audio de entrada se dividía en segmentos de voz separados y se predecían tokens a partir de ellos. Luego, los tokens se combinaron, los repetidos se colapsaron en uno y el resultado se introdujo en la salida del modelo.





Al mismo tiempo, la precisión del reconocimiento se resintió, ya que una palabra con letras repetidas no se consideró 100% reconocida correctamente. Por ejemplo, "coOperation" se redujo a "coOperation".





Con CTC, sigue prediciendo un token por segmento de tiempo del discurso y, además, usa un token vacío para averiguar dónde doblar los tokens duplicados. La aparición de una ficha vacía ayuda a separar las letras duplicadas que no deben doblarse.





Para mi tarea, tomé uno de los modelos (Jasper 10 × 5) y lo entrené desde cero. Para la capacitación, se eligió un conjunto de datos públicos de conversaciones telefónicas, que contiene grabaciones de audio cortadas y sus transcripciones.





Para entrenar el modelo, debe preparar un archivo de manifiesto que contenga información sobre el archivo de audio y la transcripción de este archivo. El archivo de manifiesto tiene su propio formato:





{{"audio_filepath": "path/to/audio.wav", "duration": 3.45, "text": "sometext"}…{"audio_filepath": "path/to/audio.wav", "duration": 3.45, "text": "sometext"}}
      
      



El modelo acepta archivos de audio solo en formato * .wav. Es necesario recorrer la lista completa de archivos de audio y usar la utilidad de la consola para recodificar archivos de audio con una resolución diferente a la requerida:





def convertToWav(self, ext):
        if not os.path.exists(self.datadir + '/dataset'):
            tar = tarfile.open(self.an4Path);
            tar.extractall(path=self.datadir);
        sphList = glob.glob(self.datadir + '/dataset/**/*' + ext, recursive=True);
        for sph in sphList:
            wav = sph[:-4] + '.wav';
            cmd = ["sox", sph, wav];
            subprocess.run(cmd);
            print('renamed ' + ext + ' to ' + wav);
      
      



Para construir un manifiesto de prueba y entrenamiento, utilicé la siguiente función, en la que obtuvimos la duración de un archivo de audio usando la función obtener duración (nombre de archivo = ruta de audio ) de la biblioteca Librosa, conocemos la ruta a los archivos de transcripción y audio archivos:





def buildManifest(self, transcript_path, manifest_path, wav_path):
        with open(transcript_paths, 'r') as fin:
            with open(manifest_path, 'w') as fout:
                for line in fin:
                    transcript = line[: line.find('(')-1].lower();
                    transcript = transcript.replace('<s>', '').replace('</s>', '');
                    transcript = transcript.strip();
                    file_id = line[line.find('(')+1 : -2];
                    audio_path = os.path.join(self.datadir, wav_paths, file_id[file_id.find('-')+1 : file_id.rfind('-')], file_id +'.wav');
                    duration = librosa.core.get_duration(filename=audio_path);
                    metadata = {
                        "audio_filepath": audio_path,
                        "duration": duration,
                        "text": transcript
                    }
                    print(metadata);
                    json.dump(metadata, fout);
                    fout.write('\n');
      
      



, :





config.yaml:
name: &name "Jasper10x5"
model:
  sample_rate: &sample_rate 16000
  labels: &labels [" ", "a", "", "", "", "", "", "", "", "", "", "", "", "",
                   "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "'"]
 preprocessor:
    _target_: nemo.collections.asr.modules.AudioToMelSpectrogramPreprocessor
    normalize: "per_feature"
    sample_rate: *sample_rate
    features: &n_mels 64
    n_fft: 512
    frame_splicing: 1
    dither: 0.00001
    stft_conv: false
      
      



. pytorch_lighting :





import nemo;
class NemoASR:
    def __init__(self, dataDir):
        self.datadir = dataDir;
        self.CONF_PATH = './config.yaml';
        yaml = YAML(typ="safe");
        with open(self.CONF_PATH) as f:
            self.CONFIG = yaml.load(f);

    def train(self, transcriptionPATH, manifestPATH, wavPATH, testTranscriptionPATH, testManifestPATH, testWavPATH):
        print("begin train");
        train_transcripts = self.datadir + transcriptionPATH;
        train_manifest = self.datadir + manifestPATH;
        if not os.path.isfile(train_manifest):
            self.buildManifest(train_transcripts, train_manifest, wavPATH);
        test_transcripts = self.datadir + testTranscriptionPATH;
        test_manifest = self.datadir + testManifestPATH;
        if not os.path.isfile(test_manifest):
            self.buildManifest(test_transcripts, test_manifest, testWavPATH);
        # params from ./config.yaml
        self.CONFIG['model']['train_ds']['manifest_filepath'] = train_manifest;
        self.CONFIG['model']['validation_ds']['manifest_filepath'] = test_manifest;
        trainer = pl.Trainer(max_epochs=500, gpus=1);
        self.model = nemo_asr.models.EncDecCTCModel(cfg=DictConfig(self.CONFIG['model']), trainer=trainer);
        trainer.fit(self.model);
        print("end train");
#-------------------------------------------------------------
nemoASR = NemoASR('.');
if (nemoASR.checkExistsDataSet()):
    print('dataset loaded');
    nemoASR.train('./dataset/etc/train.transcription',  './dataset/train_manifest.json','./dataset/wav/an4_clstk', './dataset/etc/test.transcription', './dataset/test_manifest.json', './dataset/wav/an4test_clstk');
    nemoASR.model.save_to('./model.sbc');
      
      



:





files = ['./an4/wav/an4_clstk/mgah/cen2-mgah-b.wav'];
    for fname, transcription in zip(files, nemoASR.model.transcribe(paths2audio_files=files)):
        print(f"Audio in {fname} was recognized as: {transcription}");
      
      



, .





NeMo   :





  • GPU;





  • , ;





  • .





Entre las deficiencias, podemos señalar la  necesidad de incluir una gran cantidad de bibliotecas de gran peso, así como el hecho de que la herramienta es relativamente nueva y algunas funciones del modelo están en prueba beta.





Al resolver un problema de reconocimiento de voz, tuve una experiencia interesante con los modelos ASR. Pude entrenar el modelo en un conjunto de datos aleatorios y obtuve suficiente precisión para reconocer con confianza las conversaciones telefónicas.





Sugerimos utilizar esta herramienta no solo para el reconocimiento de voz, sino también para generar archivos de audio basados ​​en texto (TTS) y reconocimiento de locutor.








All Articles