Alysia - Assistente vocale multifunzionale.

Kode

Utente Emerald
10 Dicembre 2013
1,226
81
371
623
1. Introduzione
architecture.png

Il progetto è open-source e ha lo scopo di testare le funzionalità di Natural Language Understanding di Rasa e dei moduli NVIDIA NeMo (integrati nelle ultime versioni di NVIDIA Riva per la gestione di assistenti virtuali). Lo scopo del progetto è stato quello di testare alcune funzionalità delle librerie messe in open-source da NVIDIA e verificarne l'integrabilità di servizi esterni come Rasa NLU. Scrivo questa discussione per mostrarvi un opportunità di creare in maniera semplice e veloce un proprio assistente virtuale (un pò come Alexa o Siri ma fatte da voi utilizzando modelli di Automatic Speech Recognition, Text-To-Speech e Natural Language Processing). I moduli sono stati tutti testati singolarmente e lo scopo ultimo del progetto, da come si può vedere dalla repository che posterò, è quella di evitare il parallelismo GPU (questo ha fatto si che si possa riadattare il bot in sistemi free-GPU e integrare i moduli in Arduino o altri hardware isolated systems a basso costo (perchè siamo poveri)).

Questo progetto è universitario, data dalla magistrale di Informatica dell'Alma Mater Studiorum per il progetto di Natural Language Processing. Devo dire che mi sono divertito e non poco a creare e testare i moduli per poi integrarli in una architettura ad-hoc di tipo client-server.

Importante: Capire come siano fatti i modelli utilizzati NON è necessario al fine di aggiornare o implementare nuovi funzionalità di Alysia (si, è il nome dell'assistente).

2. Indice

La presentazione del progetto è divisa in varie sezioni:
  • ASR: Automatic Speech Recognition, per la sintetizzazione vocale in uno spettrogramma mel e la conversione in un testo scritto.
  • NLP: Nello specifico andremo ad utilizzare Rasa per il Natural Language Understanding, basato su un orientamento ad intent come in dialogflow e sistema autonomo di actions che, dopo un rapido test di applicabilità, abbiamo deciso di non utilizzare data l'inefficienza delle performance in runtime (sono stati sostituiti con funzioni scratch implementate come API all'interno di un semplice server Flask). Sono stati integrati moduli NLP di NVIDIA NeMo per la gestione di contenuti su Wikipedia.
  • TTS: Text-To-Speech, in cui si passa dal testo alla codifica di uno spettrogramma mel per poi alla generazione sonora all'interno di un file audio da riprodurre (rappresenta la voce del nostro assistente).

3. ASR

Da come si può vedere nella prima immagine, i moduli di Automatic Speech Recognition sono stati presi da un modello Quartznet 15x5. Esso rappresenta un modello allo stato dell'arte attuale (superando anche Megatron e altri modelli derivati). Ha un WER di circa il 4% su dataset noti come LibriSpeech e ha una velocità di trascrizione elevata (riuscendo a supportare anche testi molto lunghi con margini di errore minimi).
Esso è formato da una rete neurale in cui si hanno:
  1. Convolutional Layer seguita da 15 blocchi ripetuti 5 volte
  2. Ogni blocco è formato da:
    1. 1D depth-wise convolution layer (in cui ogni canale ha un valore del kernel differente)
    2. Pointwise convolution layer con dimensione 1x1 del kernel su vari canali paralleli
    3. Layer di normalizzazione per evitare ripetizioni (Rimuove ad esempio ripetizioni come AAAAAANNNIMAAL in ANIMAL)
    4. ReLu layer che applica la funzione di ReLu sull'output
  3. Modulo CTC loss per la gestione dell'associazione testuale
Specifico che in seguito a questo modello è stato introdotto un Beam classifier capace di convergere l'output del CTC loss nel vocabolario inglese (questo perchè l'ASR è basato sulla classificazione di caratteri e non di parole, come risultato parziale si hanno termini con caratteri insensati, ad esempio potrebbe uscire "anmel" al posto di "animal"), ciò fa si che si abbia una associazione basata sulla massima confidency tra il seriale di caratteri del CTC loss e i termini del vocabolario inglese.
Sono consapevole che è difficile capire come sia fatta una rete frutto di una ricerca moderna in un campo complesso come il Natural Language Processing, per chi vuole sapere di più su questo modello, me lo faccia sapere nei commenti, cosi al massimo possono anche darvi un mio notebook con un implementazione basata su tensorflow.

4. NLP/NLU

Questa parte non fa altro che prendere un valore testuale e processarlo logicamente per poter generare una risposta ad una domanda. Vi sono due parti:
  1. Rasa NLU: Rappresenta un modello orientato ad intent in cui non si fa altro che processare la domanda, verificare quale sia l'intent (associazione tra la struttura della domanda con un suo significato semantico) e restituirlo al server per compiere la procedura correlata. In alternativa è possibile integrare un Rasa Server in grado di computare le procedure direttamente nell'intent-system e restituire la risposta sottoforma di messaggio JSON.
  2. NeMo NLP: Abbiamo creato un semplice scraper che processa dei testi all'interno di wikipedia in associazione ai termini utilizzati ad una domanda, successivamente abbiamo creato un input in formato JSON che sia conforme alla struttura di SQuAD 2.0 su cui è allenato un modello QAModel fornito da NeMo NVIDIA stesso nelle sue collezioni NLP. L'output è una risposta inerente alla domanda con un margine di precisione testato e visibile all'interno delle valutazioni nella documentazione ufficiale del progetto (vedere la repositry GitHub per maggiori info). Un esempio può essere "Search how old is John Cena?" e la risposta è 44. Abbiamo utilizzato Wikipedia come banca dati poichè open-source, siete liberi di modificarlo a vostro piacimento per integrarlo con piattaforme più mirate (modificando ovviamente anche lo scraper).
Non mi soffermerò molto sui modelli NLP, evidenzio solo la caratteristica che raffigura la struttura di base rappresentata come un DIET classifier per Rasa NLU e un modello BERT per il servizio di domande su Wikipedia.

5. TTS

Questa parte ci ha creato non pochi problemi, soprattutto per l'assenza di un sistema con GPU. I moduli di TTS utilizzano per default CUDA X. Per chi non lo sapesse, è una libreria basata sulla parallelizzazione di sottoprocessi in grado di definire un ottimizzazione delle performance, per usarla si ha l'obbligo di una GPU o una TPU. In assenza di ciò, i moduli server-side crashano, per questo motivo all'interno del progetto troverete la classe con i moduli TTS di NeMo che non verranno utilizzati, si ha comunque possibilità di integrarli se avete GPU NVIDIA in vostro possesso. In alternativa, si può utilizzare un modulo di WebSpeech API di Javascript compatibili con i modelli integrati (per capirci, quelli che si trovano nei vecchi Mac intel-based).

Fatto sta che abbiamo testato:
  1. FastSpeech: Generatore di spettrogrammi a partire da un input testuale. Rappresentato da sotto-moduli in grado di predirne la durata e il picco dello spettrogramma per ogni lettera scandita all'interno del testo, secondo un approccio parallelo.
  2. HiFiGAN: Esso è un modello GAN (Generative Adversarial Network) in grado di trasformare uno spettrogramma (quello del FastSpeech definito prima) in voce. Nello specifico esso è un modello composto da un generatore e due discriminatori (multi-scale e multi-period).

Non mi soffermo molto su questi modelli, vi rilascio alle loro ricerche nelle referenze sottostanti per capirne meglio la struttura.

6. Architettura

In conclusione possiamo sintetizzare l'architettura nei seguenti punti:

  1. Flask Server: Ha la funzionalità di gestire le actions in seguito alle specifiche degli intent, richiamare i moduli di ASR e TTS ed inoltrare le request al server Rasa.
  2. Rasa Server: Ha la funzione di definire il servizio di intent per associare una semantica alle domande
  3. ASR: Modello Quartznet15x5 di NVIDIA NeMo per la sintetizzazione vocale
  4. TTS: Modello di FastSpeech + HiFiGAN per la conversione vocale di un testo
  5. QAModel: Modello NLP di NeMo in grado di dare una risposta logica basata sullo scraping di Wikipedia
  6. Database MongoDB: Immagazzina le informazioni necessarie per la gestione di entità logiche.
Generalmente possiamo sintetizzare il pattern architetturale come se fosse un semplice client-server.



7. Riferimenti

Repository:
https://github.com/kode-git/NeMo-virtual-assistant
Ogni riferimento è interno alla documentazione nel folder ./doc
 
Piccola curiosità...perché utilizzi un database noSQL? Per ottimizzare la ricerca vocale?
In generale si usano tantissimo nei progetti di machine learning perché ti permettono di fare rapid prototyping (non devi progettare l'infrastruttura) e perché il bottleneck è tutto nella fase di training e prediction. Se ci pensi è più o meno la stessa ragione per cui si usa Python invece di C++ nonostante è risaputo che Python sia un linguaggio molto lento: la parte di codice che percepisci come lenta (minuti/ore per essere eseguita) è in realtà scritta in C++ e gira in parallelo sulla GPU, mentre la parte di codice che Python esegue lentamente in realtà termina in pochi secondi. Il più delle volte impegnarsi a ottimizzare queste "piccole cose" è fatica sprecata: premature optimization is the root of all evil.
 
  • Mi piace
Reazioni: Kode e 0xbro
Sisi, sei stato chiaro. Comunque complimenti per il progetto, dagli seguito. ;)

Sisi, l' idea è di collegarla con procedure in grado di richiamare attuatori IoT per comunicare wireless con sistemi domotici (lampadine, TV e altro e implementare comandi come "Alysia, accendi la TV"... in pratica come fa Alexa, attualmente sto lavorando per accumulare dati sulla temperatura e umidità per applicare poi un tipico decision tree e determinare se accendere o meno i riscaldamenti (avendoli il termostato intelligente possono applicarlo stesso a casa mia).

In generale si usano tantissimo nei progetti di machine learning perché ti permettono di fare rapid prototyping (non devi progettare l'infrastruttura) e perché il bottleneck è tutto nella fase di training e prediction. Se ci pensi è più o meno la stessa ragione per cui si usa Python invece di C++ nonostante è risaputo che Python sia un linguaggio molto lento: la parte di codice che percepisci come lenta (minuti/ore per essere eseguita) è in realtà scritta in C++ e gira in parallelo sulla GPU, mentre la parte di codice che Python esegue lentamente in realtà termina in pochi secondi. Il più delle volte impegnarsi a ottimizzare queste "piccole cose" è fatica sprecata: premature optimization is the root of all evil.
Vabbè se ci pensi alla fine dipende dal contesto, mi è capitato ad esempio di dover ottimizzare delle routing per il reinforcement learning per migliorare la velocità di calcolo della policy function nel PPO (Ti sto parlando di progetti di autopilotaggio in cui ogni secondo può decidere tra l'investire un pedone o fermarsi). Il ML anche un minimo miglioramento è fondamentale per poter aumentare quell'1% che risulta essere comunque significativo.
 
  • Love
Reazioni: --- Ra ---
Non me ne intendo, ma sembra un gran bel progetto. Piccola curiosità...perché utilizzi un database noSQL? Per ottimizzare la ricerca vocale?
 
Ultima modifica:
Non me ne intendo, ma sembra un gran bel progetto. Piccola curiosità...perché utilizzi un database noSQL? Per ottimizzare la ricerca vocale?
Generalmente quando si tratta di un progetto di gestione di dialoghi, molto spesso le entità catturate non hanno una struttura fissa, avendo la mancanza di uno schema si parla nella maggior parte di casi di dati non strutturati, quindi applicare database SQL risulta essere molto difficile (anche se comunque fattibile dato che le entità si possono normalizzare per farle convergere sotto le proprietà di uno schema SQL)

Ti faccio un esempio:
Alysia, can you add buy milk at 20:40 to my list?
Entità: buy milk at 20:40 (can you add <ENTITY-TOKEN> to my list come intent)
Ora la struttura è indivisibile perché non tutte le task possono avere un orario, oppure la struttura della frase come <azione><oggetto><tempo>. Per questo motivo o lo inserisci all'interno di un database noSQL oppure dovresti potresti semplicemente creare una row SQL con la coppia "ID-TASK" e "TESTO-TASK" ma questo peggiorerebbe, come hai detto te, la ricerca e le operazioni di inserimento e di cancellazioni per integrazioni con sistemi più complessi.

Non so se sia stato chiaro.
 
Generalmente quando si tratta di un progetto di gestione di dialoghi, molto spesso le entità catturate non hanno una struttura fissa, avendo la mancanza di uno schema si parla nella maggior parte di casi di dati non strutturati, quindi applicare database SQL risulta essere molto difficile (anche se comunque fattibile dato che le entità si possono normalizzare per farle convergere sotto le proprietà di uno schema SQL)

Ti faccio un esempio:
Alysia, can you add buy milk at 20:40 to my list?
Entità: buy milk at 20:40 (can you add <ENTITY-TOKEN> to my list come intent)
Ora la struttura è indivisibile perché non tutte le task possono avere un orario, oppure la struttura della frase come <azione><oggetto><tempo>. Per questo motivo o lo inserisci all'interno di un database noSQL oppure dovresti potresti semplicemente creare una row SQL con la coppia "ID-TASK" e "TESTO-TASK" ma questo peggiorerebbe, come hai detto te, la ricerca e le operazioni di inserimento e di cancellazioni per integrazioni con sistemi più complessi.

Non so se sia stato chiaro.
Sisi, sei stato chiaro. Comunque complimenti per il progetto, dagli seguito. ;)
 
In generale si usano tantissimo nei progetti di machine learning perché ti permettono di fare rapid prototyping (non devi progettare l'infrastruttura) e perché il bottleneck è tutto nella fase di training e prediction. Se ci pensi è più o meno la stessa ragione per cui si usa Python invece di C++ nonostante è risaputo che Python sia un linguaggio molto lento: la parte di codice che percepisci come lenta (minuti/ore per essere eseguita) è in realtà scritta in C++ e gira in parallelo sulla GPU, mentre la parte di codice che Python esegue lentamente in realtà termina in pochi secondi. Il più delle volte impegnarsi a ottimizzare queste "piccole cose" è fatica sprecata: premature optimization is the root of all evil.
Grazie delle delucidazioni @St3ve , sei sempre molto preparato.