Sviluppo di un sistema per l'interpretazione dei comandi basati sul riconoscimento vocale per l'interazione uomo-robot
←
→
Trascrizione del contenuto della pagina
Se il tuo browser non visualizza correttamente la pagina, ti preghiamo di leggere il contenuto della pagina quaggiù
Scuola Politecnica e delle Scienze di Base Corso di Laurea in Ingegneria Informatica Elaborato finale in Fondamenti di sistemi dinamici Sviluppo di un sistema per l'interpretazione dei comandi basati sul riconoscimento vocale per l'interazione uomo-robot Anno Accademico 2016/17 Candidato: Davide Russo matr. N46002152
Indice Indice..................................................................................................................................................III Introduzione..........................................................................................................................................4 Capitolo 1: Stato dell’arte.....................................................................................................................6 Capitolo 2: Implementazione.............................................................................................................15 2.1 Verifica della raggiungibilità del server....................................................................................16 2.1 Richiesta HTTP al server..........................................................................................................17 2.2 Interpretazione dei comandi.....................................................................................................18 2.3 Codifica dei comandi................................................................................................................21 2.4 Creazione del messaggio..........................................................................................................23 2.5 Visualizzazione dei comandi....................................................................................................24 Capitolo 3: Casi di test.......................................................................................................................25 3.1 Primo: comando singolo semplice............................................................................................25 3.2 Secondo: comando singolo articolato.......................................................................................26 3.3 Terzo: comando fuori dal dominio delle funzioni eseguite dal robot.......................................27 3.4 Quarto: due comandi semplici..................................................................................................28 3.5 Quinto: comandi multipli.........................................................................................................29 3.6 Sesto: interazione sociale.........................................................................................................30 Conclusioni.........................................................................................................................................31 Bibliografia.........................................................................................................................................32
Introduzione Il lavoro svolto in questa tesi ha lo scopo di implementare un sistema per l'interpretazione dei comandi inviati a un robot da un utente espressi in linguaggio naturale. Tale sistema estende un lavoro già presentato nella tesi di laurea [1], al quale abbiamo aggiunto diverse funzionalità in modo da superarne alcuni limiti, tra cui: • l’analisi della frase pronunciata dall’utente era eseguita in maniera statica nel senso che le parole della frase vengono analizzate senza eseguire alcuna analisi sintattica delle stesse, ma semplicemente confrontando le radici di queste basandoci su un dizionario interno all’applicativo e implementato dagli sviluppatori; • la possibilità di esprimere un solo comando per volta; • l’assenza di modulartità dell’algoritmo che comportava una particolare difficoltà nell’aggiunta di comandi. Quindi, gli obiettivi di questo lavoro di tesi sono: • utilizzare un motore di Natural Language Processing in cloud per interpretare un insieme di comandi richiesti da un operatore umano, in modo da permettere a questi di richiedere un’azione interagendo attraverso il linguaggio naturale; • implementare un sistema che permetta controllo e interazione sociale tra l'utente ed il robot; • implementare un sistema modulare, indipendente sia dalla piattaforma robotica utilizzata (nel nostro caso RoDyMan sviluppato dal PRISMA Lab dell’Università degli Studi di Napoli “Federico II”) che dal Natural Language Processor utilizzato; Tale lavoro di tesi si divide in tre parti: inizialmente verranno introdotte le tecnologie utilizzate, considerando sia il cuore dell’applicativo ovvero il Natural Language Processor, sia l’area di applicazione di quest’ultimo cioè il Natural Language Processing. Verranno descritte le soluzioni già esistenti in letteratura e quella utilizzata in questo lavoro di tesi, motivandone la scelta. In una seconda parte verranno descritte le tecniche implementative seguite durante lo sviluppo del sistema, e infine verranno discussi alcuni casi di test per dimostrare come funziona il sistema implementato e gli obiettivi raggiunti. 4
Per quanto riguarda l’implementazione di tale sistema, esso è basato su tecnologia Android, attraverso la programmazione in linguaggio Java e prevedendo un terminale mobile (cellulare, tablet, etc…) che permette l’interazione tra l’utente la piattaforma robotica. Per quanto riguarda la lingua scelta per l’interazione tra l’uomo e il sistema implementato, esso è l'inglese, a differenza di quello utilizzato nella precedente tesi, che era basata sull’italiano. Questa scelta nasce principalmente dal fatto che la lingua utilizzata dal Natural Language Processor utilizzato nel sistema implementato è appunto l’inglese. In ogni caso il sistema implementato produce comunque lo stesso risultato in quanto l’analisi del testo viene effettuata tramite l’analisi degli elementi sintattici del testo inserito dall’utente e non delle parole stesse del testo. 5
Capitolo 1: Stato dell’arte Natural Language Processing si riferisce al trattamento informatico (computer processing) del linguaggio naturale, per qualsiasi scopo, indipendente dal livello di approfondimento dell’analisi. Per linguaggio naturale si intende la lingua che usiamo nella vita di tutti i giorni, come l’inglese, il russo, il giapponese, il cinese, ed è sinonimo di linguaggio umano, principalmente per poterlo distinguere dal linguaggio formale, incluso il linguaggio dei computer. Così com’è, il linguaggio naturale è la forma di comunicazione più naturale e più comune, non solo nella sua versione parlata, ma anche in quella scritta. Rispetto al linguaggio formale, il linguaggio naturale è molto più complesso, contiene spesso sottintesi e ambiguità, il che lo rende molto difficile da elaborare. Le teorie dietro l’area chiamata Natural Language Processing è la linguistica computazionale che si focalizza su come diminuire il gap tra linguaggio naturale e linguaggio formale, attraverso dei formalismi descrittivi di un linguaggio naturale. Il Natural Language Processing si divide in due macro aree: il Natural Language Understanding e il Natural Language Generation. Queste due macro aree sono una l’opposta dell’altra: la prima ha come obiettivo lo studio delle comprensione da parte delle macchine del linguaggio naturale; la seconda ha come obiettivo quello di studiare come le macchine possono generare frasi in linguaggio naturale. In generale il Natural Language Processing prevede diverse fasi nell’analisi di un testo che ne fanno di esso una struttura su livelli. I livelli fondamentali su cui si basa l’elaborazione dell’analisi di una frase sono [2]: • lessico e morfologia, si occupa di effettuare l’analisi lessicale del testo posto in input. Il testo viene “spezzettato” attraverso un processo di tokenizzazione, formado una serie di token. In un Natural Language Processing ogni token è associato ad una parola del testo. Per ogni token, tramite i processi di Lemmatization e Morphological segmentation, vengono individuate tutte le parole con significato, detti lemmi, che compongono il testo in input e ne fa l’analisi morfologica, cioè individua il modo e il tempo dei verbi, se un nome è singolare o 6
plurale, etc.; • sintassi, si occupa di effettuare l’analisi sintattica del testo posto in input. Vengono cioè individuate tutte le parti del discorso, intesi come verbi, nomi, aggettivi, avverbi, preposizioni, pronomi. Il processo che si occupa di marcare ogni parola con la propria parte del discorso si chiama Part-of-speech tagging. Il processo, si divide in due sotto-processi: il primo, detto shallow parsing, produce un albero binario in cui vengono individuate le parti elementari cioè la parte nominale (NP) e la parte verbale (VP), mentre il secondo, detto full parsing, produce un albero sintattico in cui ogni parola viene marcata con il suo ruolo sintattico all’interno della frase. Di seguito viene fatto un esempio, supponendo che il testo in ingresso sia “Martina mangia la pizza”. Nell’esempio la stringa in ingresso è identificata con S. • semantica, si occupa di individuare il significato del testo. Non ci si spinge oltre nella spiegazione, ma vale la pena citare il processo Named Entity Recognition che è utilizzato per ricercare ed individuare gruppi di parole che possono formare un’entità, intesa come nomi di persona, paesi, eventi, etc.; 7
• pragmatica, si occupa di individuare il contesto in cui è posto il testo e di elaborarlo e utilizzarlo in funzione di esso. Il Natural Language Processing è implementato con un Natural Language Processor, che è un framework che racchiude tutti i processi utilizzati nei vari livelli. Allo stato attuale della teconologia, un Natural Language Processor è presente in rete come servizio cloud. Il cloud computing è un paradigma di erogazione di risorse informatiche caratterizzate dal fatto che queste risorse non sono collocate sul computer dell’utente ma su un server remoto e sono accessibili dall’utente attraverso la rete. Questo paradigma è offerto a partire da risorse che non vengono configurate e messe in opera dal fornitore apposta per l'utente, ma gli sono assegnate, grazie a procedure automatizzate, a partire da un insieme di risorse condivise con altri utenti lasciando all'utente la configurazione della risorsa. Quando l'utente rilascerà la risorsa, essa verrà riconfigurata nello stato iniziale e rimessa a disposizione nell’insieme condiviso delle risorse. I servizi fondamentali messi a disposizione dal cloud computing sono: • SaaS (Software as a Service), offre l’utilizzo di un software, installato su un server remoto, usufruibile tramite API (Application Programming Interface) accessibili via web; • DaaS (Data as a Service), offre le funzionalità di una memoria di massa, mettendo a disposizione dell’utente la gestione di dati disponibili in vari formati, come se fossero in locale; • HaaS (Hardware as a Service), offre l’utilizzo di risorse hardware atte all’elaborazione di dati collocate, di solito, in un centro elaborazione dati (CED) o data center cioè una struttura in cui sono presenti apparecchiature fisiche come i server, per la gestione dell’elaborazione dei dati, e tutti gli strumenti utili per il funzionamento di essi, come per esempio gruppi di continuità e sistemi di raffreddamento; Attualmente questi servizi sono raggruppati in due macro-servizi che sono: 8
• PaaS (Platform as a Service), offre, a differenza di Saas e Daas, una piattaforma costituita da una serie di programmi e librerie utilizzabili dall’utente che comprendono sia software che sistemi per la gestione di dati; • IaaS (Infrastructure as a Service), offre risorse hardware, non solo in termini di unità di elaborazione, ma anche in termini di dischi locali e infrastrutture di rete. L’applicazione del cloud computing alla robotica comporta una serie di vantaggi al punto da creare una nuova branca della robotica, detta cloud robotics. I vantaggi ottenuti sono in termini di potenza di calcolo e riduzione dei costi. Infatti è possibile creare dei robot a basso costo, dotati però di un “cervello” costituito dai servizi del cloud compunting di cui si cita il Machine Learning, Data Mining, Information Extraction, e altri. Questi servizi sono utili nel campo della robotica per: • la creazione di sistemi per la Human-Robot Interaction; • utilizzare di Big Data, cioè dati raccolti e/o diffusi su reti accessibili e di grandi dimensioni che possono consentire decisioni per problemi di classificazione o rivelare modelli; • Internet of Things per la robotica. Questi sistemi sono utilizzati per realizzare servizi del tipo: • Autonomous mobile robots, cioè la possibilità di creare auto che “si guidano da sole” in base alle immagini ricevute da GPS e comparate con quelle catturate dall’auto tramite fotocamere, sensori; • Cloud medical robots, un cloud che fa da assistenza ai medici attraverso l’accesso a vari servizi come un archivio di malattie, cartelle cliniche elettroniche, un sistema di gestione della salute del paziente, servizi di pratica, servizi di analisi, soluzioni cliniche, sistemi esperti; • Industrial robots, cioè robot utilizzati per la produzione di prodotti. Un’azienda che si occupa di offrire il cloud computing è detta cloud provider. I maggiori cloud provider esistenti al mondo sono, per esempio, Google, Amazon, IBM, Microsoft. Tra i software offerti dal cloud compunting di queste aziende, troviamo anche il Natural Language Processing. In particolare i prodotti che offrono questo tipo di servizio sono 9
Google Cloud Platform Natural Language API [3], Amazon Comprehend [4], IBM Watson Natural Language Understanding [5], Microsoft Azure Cognitive Services Language Understanding (LUIS) [6]. Di seguito si riporta una tabella contenente le peculiarità dei diversi sistemi offerti. Nome Servizi offerti Lingue supportate Peculiarità piattaforma • analisi sintattica; • analisi delle Inglese, sagnolo, Google Cloud relazioni; giapponese, cinese, • Analisi delle Platform • analisi delle francese, tedesco, opinioni relative Natural opinioni; italiano, coreano, alle entità. Language API • analisi delle portoghese, e altre. entità. • Rilevamento della lingua; • possibilità di • analisi sintattica; tradurre e poi • analisi delle Amazon analizzare; opinioni; Inglese, spagnalo. Comprehend • analisi di termini • analisi delle o argomenti entità. collezione di documenti (topic modeling) • analisi sintattica; Inglese, arabo, • analisi delle IBM Watson francese, tedesco, relazioni; • Analisi delle Natural italiano, • analisi delle emozioni di un Language giapponese, opinioni; testo. Understanding portoghese, • analisi delle coreano, spagnolo. entità. • Analisi delle Inglese americano, intent, cioè Microsoft francese canadese, azioni che Cognitive cinese, giapponese, • analisi sintattica; l’utente vuole Services coreano, francese, • analisi delle fare; Language tedesco, italiano, entità. • analisi di testi Understanding portoghese, malformati che (LUIS) spagnolo, spagnolo devono essere messicano. capiti. 10
Di seguito segue una lista di tabelle di costi per ogni cloud provider. Google Cloud Platform Natural Language API Servizio 0 → 5K 5K+ → 1M 1M+ → 5M 5M+ → 20M Analisi delle entità Gratis $1.00 $0.50 $0.25 Analisi delle opinioni Gratis $1.00 $0.50 $0.25 Analisi sintattica Gratis $0.50 $0.25 $0.125 Analisi delle opinioni Gratis $2.00 $1.00 $0.50 relative alle entità Il costo si riferisce a una spesa mensile per unità detta text record e rappresenta 1000 caratteri Unicode. Per oltre 20 milioni di text record, Google propone dei contratti commerciali ad-hoc per il cliente. Amazon Comprehend Tipo di servizio 0 → 10M 10M → 50M Over 50M NLP $0.0001 $0.00005 $0.000025 Amazon Comprehend Tipo di servizio First 100 MB For every MB over 100 Topic modeling $1.00 $0.004 Il costo fa riferimento a una singola unità che consiste in 100 caratteri Unicode. Il minimo di unità per poter usufruire dei servizi di Amazon è 3. IBM Watson Natural Language Understanding Tipo di contratto 0 → 30K Lite Gratis IBM Watson Natural Language Understanding Tipo di contratto 0 → 250K 250K+ → 5M Over 5M Standard $0.003 $0.001 $0.0005 11
Il costo fa riferimento a un canone mensile per unità della NLU che rappresenta 10000 caratteri. La Microsoft permette anche di stipulare contratti commerciali personalizzati. Microsoft Cognitive Services Language Understanding (LUIS) Every 1000 transaction Servizio 0 → 10K over 10K LUIS Gratis $1.50 Il costo fa riferimento ad un costo mensile. Per transaction s’intende una chiamata alle API del sistema con query che contengono fino a 500 caratteri. Il più idoneo per questo lavoro di tesi è Google Cloud Natural Language API in quanto le API offerte si adattano perfettamente alla stesura di un’applicazione Android, ma a causa dei costi, che si basano sul numero di query inviate e sul fatto che per accedere al servizio serve una partita IVA, la scelta è ricaduta su una soluzione open-source chiamata StanfordNLP. Questo Natural Language Processor è stato sviluppato da un team di ricercatori della Stanford University. Di seguito si riportano in tabella le caratteristiche del Natural Language Processor in esame. Nome Servizi offerti Lingue supportate Peculiarità piattaforma • analisi sintattica; • analisi delle Inglese, arabo, StafordNLP opinioni; cinese, francese, • Open-source • analisi delle tedesco, spagnolo. entità. Il servizio di Natural Language Processing è offerto in modo tale da poter installare un server privato al fine di creare un’architettura client-server, dove il client fa delle richieste al server, che vengono effettuate tramite protocollo HTTP e il server risponde con le informazioni legate al testo messo in ingresso in formato JavaScript Object Notation (JSON) [7]. JavaScript Object Notation è un formato dati adatto all’interscambio di essi nell’architettura client-server. JSON prende origine dalla sintassi degli oggetti letterali in 12
JavaScript. Un oggetto letterale può essere definito così: var JSON = { proprieta1: 'Valore', proprieta2: 'Valore', proprietaN: 'Valore' } Si tratta di coppie di proprietà/valori separate dalla virgola a eccezione dell’ultima. L’intero oggetto viene racchiuso tra parentesi graffe. A differenza di JavaScript, che può contenere anche funzioni e valori complessi, JSON ammette solo valori semplici e atomici, tra cui: • stringhe; • numeri; • array; • oggetti letterali; • true, false; • null. La caratteristica principale di StanfordNLP che più salta all’occhio è che la lingua utilizzata dal servizio è l’inglese, quindi tutti i comandi sono dati in lingua inglese. Particolare attenzione vale la pena darla al servizio Part-of-speech tagging di StanfordNLP utilizza il Penn Treebank tag set [8]. Questo set di tag è una legenda che esplicita la semantica di ogni tag utilizzato dal servizio Part-of-speech. Di cui di seguito viene mostrata la tabella. Tag Description CC Coordinating conjunction CD Cardinal number DT Determiner EX Existential there FW Foreign word IN Preposition or subordinating conjunction 13
JJ Adjective JJR Adjective, comparative JJS Adjective, superlative LS List item marker MD Modal NN Noun, singular or mass NNS Noun, plural NNP Proper noun, singular NNPS Proper noun, plural PDT Predeterminer POS Possessive ending PRP Personal pronoun PRP$ Possessive pronoun RB Adverb RBR Adverb, comparative RBS Adverb, superlative RP Particle SYM Symbol TO to UH Interjection VB Verb, base form VBD Verb, past tense VGD Verb, gerund or present participle VBG Verb, past participle VBN Verb, non-3rd person singular present VBZ Verb, 3rd person singular present WDT Wh-determiner WP Wh-pronoun WP$ Possessive wh-pronoun WRB Wh-adverb Di seguito verrà mostrato un esempio di applicazione di questa tabella supponendo di avere in ingresso il testo “Take the bottle and take the glass”. 14
Capitolo 2: Implementazione In questo capitolo verrà illustrato come l’applicazione Android realizza le funzionalità al fine di raggiungere gli obiettivi richiesti. Prima di analizzare l’algoritmo, verrà discussa l’architettura completa del sistema che permette l’interazione tra l’operatore e la piattaforma robotica. Tale architettura è proposta nella seguente figura. Di seguito si descrive il funzionamento del sistema. L’utente inserisce il comando tramite linguaggio naturale. L’applicativo trasforma in stringa il comando dell’utente e, attraverso un’interfaccia di comunicazione, invia la stringa al Natural Language Processor. Dopo l’elaborazione, la risposta del servizio ritorna all’applicativo, che, attraverso opportune elaborazioni, individua i comandi espressi dall’operatore e crea un messaggio per il robot. Successivamente quest’ultimo viene inviato al robot tramite su una rete che collega l’applicativo al robot. 15
L’algoritmo utilizzato precedentemente per decidere quali comandi dovesse svolgere il robot era statico, nel senso che l’analisi della frase dell’operatore non era fatta tramite un’analisi sintattica ma attraverso la comparazione delle parole della frase con un dizionario interno. Inoltre, come detto precedentemente prevedeva, l’interpretazione di un singolo comando alla volta. Con questo lavoro di tesi si è voluto rivoluzionare l’algoritmo di interpretazione dei comandi lasciando inalterati il modo in cui l’applicativo invia i comandi al robot (un apposito thread che apre una comunicazione con il robot tramite una socket di tipo UDP) e il modo in cui l’utente fornisce il comando all’applicativo (speech- to-text fornito da Google come API per Android). In particolare quest’ultima funzionalità dell’applicazione fornisce come output, sotto forma di stringa, il comando immesso dell’utente. Si vuole far notare che il servizio speech-to-text non è esente da errore di interpretazione. Da questo momento in poi si farà riferimento a questo risultato con il termine “stringa”. In particolare, si ricorda, che la stesura di questo nuovo algoritmo prevede che si possono dare più comandi al robot e che ci sia una forma di conoscenza con esso. L’algoritmo di interpretazione comandi si basa su cinque sotto-funzionalità: 1. verifica della raggiungibilità del server; 2. richiesta HTTP al server del Natural Language Processor; 3. interpretazione dei comandi; 4. codifica dei comandi; 5. creazione del messaggio; 6. visualizzazione dei comandi. Ogni sottofunzionalità è implementata come metodo della classe MainActivity, tranne la richiesta HTTP che è implementata con una classe apposita. Nelle sezioni seguenti verranno descritte in dettaglio ogni sotto-funzionalità. 2.1 Verifica della raggiungibilità del server Questa prima sotto-funzionalità si occupa di contattare il server tramite il comando da terminale ping. Infatti in Java sono presenti particolari tipi e metodi che permettono di 16
richiamare i comandi della Shell durante l’esecuzione di un programma. In particolare si utilizzano il tipo Process e il metodo Runtime.getRuntime().exec(String command) della libreria java.lang per adempiere all’obiettivo della sotto-funzionalità. Il comando è utilizzato in modo tale che se esso va a buon fine, allora l’utente può dare il comando al robot (in sostanza si apre la RecognizerIntent di Google), altrimenti viene visualizzato un Toast, che è un tipo di notifica di Android che appare sotto forma di riquadro in basso dello schermo, che dice all’utente che il server non è raggiungibile. Il codice per implementare la sotto-funzionalità è presentato di seguito: public boolean ping() throws IOException, InterruptedException { Process p1 = java.lang.Runtime.getRuntime().exec("ping -c 1 corenlp.run"); int returnVal = p1.waitFor(); return (returnVal==0); } 2.1 Richiesta HTTP al server L’utente inserisce il proprio comando se la verifica della raggiungibilità va a buon fine, e, tramite un processo di speech-to-text, quest’ultimo viene trasformato in stringa. La seconda sotto-funzionalità si occupa di contattare il server, inviando la stringa appena trasformata. Essa, come già detto, è implementata in una apposita classe Java, chiamata HTTPRequest, che estende la classe AsyncTask, che permette l’esecuzione di operazioni in background [9]. Il funzionamento è simile a un Thread, ma viene scelto AsyncTask perché, a differenza di Thread, viene utilizzato per l’esecuzione di brevi task asincroni che devono comunicare con un task principale. La classe AsyncTask possiede un metodo Abstract chiamato doInBackground(Params… params). Questo metodo viene sovrascritto con le istruzioni atte alla creazione di una comunicazione HTTP. Ciò avviene tramite i costrutti URL e HttpURLConnection. Il primo permette di dichiarare un puntatore ad una risorsa del World Wide Web intesa come Uniform Resource Locator a partire da un indirizzo passato come parametro di tipo String [10]. Il secondo permette di aprire una comunicazione con la URL dichiarata utilizzando il protocollo HTTP [11]. Entrambi i tipi sono contenuti nella libreria java.net. 17
La caratteristica principale di HttpURLConnection è quella di poter utilizzare diversi parametri per caratterizzare la connessione, come per esempio il comando da utilizzare nella connessione HTTP, che nel nostro caso è POST in quanto la stringa contenente il comando è inviata nel corpo del messaggio e non nell’header a differenza del comando GET. Una volta iniziata la comunicazione con il server vengono aperti due standard stream, uno di input e l’altro di output, in cui si scrive il messaggio e si legge la risposta. La connessione si chiude quando il server invia tutta la risposta. Il codice utilizzato è presentato di seguito: protected String doInBackground(Void... params) { String jsonStr = ""; try { URL url = new URL("http://corenlp.run:80/"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //istruzioni per caratterizzare la connessione DataOutputStream wr = new DataOutputStream(conn.getOutputStream()); wr.write(command.getBytes(Charset.forName("UTF-8"))); wr.close(); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); String temp; while ((temp = br.readLine()) != null) jsonStr += temp; br.close(); } catch (Exception e) { e.printStackTrace(); } return jsonStr; } 2.2 Interpretazione dei comandi Questa sotto-funzionalità è il perno principale dell’applicativo. L’ipotesi principale su cui si fonda è quella che ogni frase di senso compiuto, soprattutto per dare un comando, nella maggior parte delle volte, è formata da verbo e complemento oggetto (es. prendi la bottiglia). Il problema, quindi, che viene risolto è quello di dividere il verbo e il complemento oggetto a cui si riferisce. Questa sotto-funzionalità inoltre è quella che si occupa di costruire una forma di 18
“conoscenza sociale” da parte del robot. La sotto-funzionalità in esame è divisa ulteriormente in due parti: la prima ricerca un eventuale nome di persona all’interno della stringa attraverso il servizio Named Entity Recognition, mentre la seconda ricerca all’interno della stessa stringa ogni verbo e ogni nome grazie al servizio Lemmatization e Part-of-speech tagging. Si vuole ricordare come il server risponde con un messaggio in formato JSON, quindi sono previste delle linee di codice che trasformano la stringa di risposta del server, tramite appositi costrutti e tipi contenuti nella libreria org.json, in un JSONObject e di questo ne viene estratto un JSONArray. Da quest’ultimo vengono estrapolate le informazioni necessarie per l’elaborazione che vengono selezionate attraverso i tag utilizzati dal Part-of- speech tagging del Natural Language Processor utilizzato. In particolare, in questa sotto- funzionalità vengono utilizzati i tag VB e NN, che rispettivamente indicano un verbo alla forma base e un nome singolare. Il risultato di questa sotto-funzione sono due array di stringhe contenenti una tutti i verbi e l’altra tutti i nomi trovati nella stringa. Per ottenere una struttura omogenea, vengono inseriti in questi due array anche il nome di persona eventualmente trovato e un verbo, che è stato chiamato “social”, per identificare l’azione da far compiere al robot. Il vantaggio di avere due array distinti sta nel fatto che in ogni posizione del vettore contenente i verbi, in corrispondenza della stessa posizione (cioè nello stesso valore dell’indice), c’è il complemento oggetto a cui fa riferimento, realizzando di fatto uno “splitting intelligente” della stringa in cui ogni verbo è associato al relativo complemento oggetto, venendo incontro all’ipotesi fatta, cioè è stata realizzata una struttura del tipo verbo-complemento oggetto. Si vuole far notare che per evitare conflitti tra le due parti della sotto-funzionalità, nella seconda vengono saltate tutti i nomi di persona. È stato evidenziato, inoltre, come alcuni verbi e/o oggetti possono creare alcune incomprensioni nell’interpretazione del comando, per cui sono state scartate durante l’esecuzione della sotto-funzione. Esempi lampanti sono il verbo “be” e “please” e il nome “place”, saltato poiché il sistema riconosce quest’ultimo 19
come nome e non come verbo. Il codice fulcro della sotto-funzionalità è presentato di seguito: 20
public void commandInterpreter(String response) { JSONObject responseJsonObj; JSONArray responseJsonArray; if (response != null) try { responseJsonObj = new JSONObject(response); Log.d("MAIN", responseJsonObj.toString()); responseJsonArray = responseJsonObj.getJSONArray("sentences"); for (int i = 0; i < responseJsonArray.getJSONObject(0).getJSONArray("tokens").length(); i++) { if (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getStri ng("ner").contains("PERSON")) { vb.add("social"); nn.add(responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i) .getString("lemma")); } } for (int i = 0; i < responseJsonArray.getJSONObject(0).getJSONArray("tokens").length(); i++) { if (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getStri ng("lemma").equals("be") || (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getStri ng("lemma").equals("please")) || (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getStri ng("ner").equals("PERSON"))) continue; if (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getStri ng("pos").contains("VB")) { vb.add(responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSO NObject(i).getString("lemma")); if (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getStri ng("lemma").equals("stop")) nn.add("no obj"); } if (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getStri ng("pos").contains("NN")) nn.add(responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i) .getString("lemma")); } } catch (Exception e) { e.printStackTrace(); } else Log.d("MAIN", "STRINGA VUOTA"); } 2.3 Codifica dei comandi La sotto-funzionalità in esame si occupa di verificare se i verbi e/o gli oggetti riconosciuti sono rispettivamente funzioni che il robot svolge e/o oggetti che il robot vede e codifica il comando, l’oggetto e un eventuale errore, che si verifica quando la verifica di verbo e/o 21
oggetto fallisce. La verifica dei comandi e degli oggetti è un’operazione necessaria ai fini dell’obiettivo finale poiché il robot non svolge ovviamente tutti comandi che si vogliono, ma ha un dominio ristretto di funzioni preventivamente definito. In particolare i comandi eseguiti dal robot sono di prendere, lasciare oggetti, conoscenza, di andare in una direzione e fermarsi. Una volta effettuata la verifica, il comando e/o l’oggetto vengono codificati secondo le regole dettate da un protocollo di comunicazione, preventivamente accordato. Nello specifico, il protocollo prevede tre bit di cui il primo indica la presenza di errori, il secondo codifica il verbo e il terzo codifica l’oggetto. Si nota che se c’è un errore, gli altri due bit sono posti a zero. Per effettuare la verifica e la codifica, i verbi e gli oggetti sono stati inseriti in due strutture dati HashMap, una per i verbi e l’altra per gli oggetti, della libreria java.util di tipo dichiarate in una classe Java del progetto, chiamata Dictionary. La particolarità della struttura dati HashMap sta nel fatto che ogni elemento salvato è diviso in due: un valore e una chiave. Nel nostro caso il valore, di tipo Integer rappresenta la codifica del comando o dell’oggetto, mentre la chiave, di tipo String, è proprio il comando o l’oggetto. All’interno dell’applicazione viene utilizzato il metodo containsKey(String key), che prende una chiave come parametro e ne verifica la presenza nella HashMap, e il metodo get(String key), che data una chiave in ingresso restituisce il valore associato. Questi due metodi permettono, utilizzati in modo corretto, sia la verifica che la codifica, il tutto in poche righe di codice. Il risultato di questa sotto-funzionalità sono tre array di stringhe contenenti ognuno la codifica rispettivamente di errori, verbi e oggetti. Nel caso di nomi di persona, questi non vengono codificati, ma vengono lasciati inalterati e mandati al robot così come sono. Il codice per questa sotto-funzionalità è riportato di seguito. 22
public void commandEncorder() { if ((vb.size() == 0) || (vb.size() != nn.size())) { error.add("1"); command.add("0"); object.add("0"); } else { for (int i = 0; i < vb.size(); i++) { String verb = vb.get(i); String obj = nn.get(i); String commandEncoded = diz.getCommandCoding(verb); String objEncoded = diz.getObjectCoding(obj); if (verb.equals("social")) { error.add("0"); command.add(commandEncoded); object.add(obj); } else if ((commandEncoded.equals("not find")) || (objEncoded.equals("not find"))) { error.add("1"); command.add("0"); object.add("0"); } else { error.add("0"); command.add(commandEncoded); object.add(objEncoded); } } } } 2.4 Creazione del messaggio Questa sotto-funzionalità si occupa di creare il messaggio da mandare al robot. Ciò che fa è la concatenazione della tripla di elementi dei tre array creati dalla sotto-funzione che si occupa della codifica, per ogni posizione dei vettori. La concatenazione è possibile grazie al metodo append(String s) della classe StringBuilder. Il risultato finale sarà una stringa di cifre. Si vuole far notare che nel caso di un nome di persona, il messaggio mandato al robot non è formato da sole cifre ma anche di caratteri, che appunto rappresentano il nome di persona. Quando la creazione del messaggio è finita, viene abilitato l’invio della stringa da parte del thread che si occupa della comunicazione con il robot, ponendo al valore True una variabile Boolean addetta proprio alla verifica della presenza del messaggio. Il codice utilizzato è riportato di seguito: 23
public void commandCreator() { StringBuilder msg = new StringBuilder(); for (int i = 0; i < error.size(); i++) { msg.append(error.get(i)); } for (int i = 0; i < command.size(); i++) { msg.append(command.get(i)); } for (int i = 0; i < object.size(); i++) { msg.append(object.get(i)); } udpOutputData = msg.toString(); sendUdp = true; } 2.5 Visualizzazione dei comandi Questa sotto-funzionalità non è importante ai fini dell’obiettivo finale, serve per capire il risultato finale dell’applicazione, soprattutto da parte dell’utente. Si occupa di visualizzare in due TextView, tipo particolare di Android con cui è possibile visualizzare un testo, la stringa riconosciuta dalla RecognizerIntent e la sua elaborazione da parte dell’algoritmo, sia sotto forma di comandi e/o oggetti riconosciuti sia sotto forma di codifica. In particolare il protocollo di visualizzazione scelto prevede la seguente regola: ERROR: ‘bit dell’errore’ COMMAND: ‘bit del comando’ – ‘comando’ OBJECT: ‘bit dell’oggetto’ – ‘oggetto’ Nel capitolo successivo è possibile osservare nello specifico il risultato di tale sotto- funzione attraverso i casi di test. Il codice utilizzato è riportato di seguito: public void commandVisualizer() { StringBuilder res = new StringBuilder(); if (vb.size() == 0) for (int i = 0; i < error.size(); i++) vb.add("VB MISS"); if (nn.size() == 0) for (int i = 0; i < error.size(); i++) nn.add("OBJ MISS"); for (int i = 0; i < error.size(); i++) res.append("ERROR: ").append(error.get(i)).append(" COMMAND: ").append(command.get(i)).append(" - ").append(vb.get(i)).append(" OBJECT: ").append(object.get(i)).append(" - ").append(nn.get(i)).append("\n"); String result = res.toString(); mCommandOutputTv.setText(result); } 24
Capitolo 3: Casi di test In questo capitolo verranno mostrati vari casi di test per mostrare come l’applicativo Android si comporta di fronte a diversi input. Verranno esplicitati gli input e i gli output previsti, intesi come messaggio costruito da mandare al robot. 3.1 Primo: comando singolo semplice INPUT: “Take the bottle” OUTPUT: 011 COMPORTAMENTO: In questo test si è voluto testare un semplice comando al robot e, come previsto, il messaggio di uscita verso esso è 011. 25
3.2 Secondo: comando singolo articolato INPUT: “Would you please to take me the bottle?” OUTPUT: 011 COMPORTAMENTO: In questo secondo caso di test si vuole porre enfasi su quanto naturale è il linguaggio utilizzato per dare il comando con l’aggiunta non solo del verbo e del complemento oggetto ma anche di verbi ausiliari. L’uscita prevista coincide con quella reale, cioè 011. 26
3.3 Terzo: comando fuori dal dominio delle funzioni eseguite dal robot INPUT: “Do you like a pizza?” OUTPUT: 100 COMPORTAMENTO: In questo caso di test si è voluto testare il comportamento dell’applicativo in presenza di un input al di fuori del dominio delle funzioni del robot. L’uscita reale è 100 ed è pari a quella prevista. 27
3.4 Quarto: due comandi semplici INPUT: “Take the bottle and take the glass” OUTPUT: 011012 COMPORTAMENTO: Come ripetuto, uno degli obiettivi dell’algoritmo è quello di poter elaborare una stringa con all’interno più di un comando. In questo caso di test si verifica il corretto funzionamento della funzionalità. Il messaggio reale in uscita è pari a quello atteso. Si vuole far notare che l’algoritmo garantisce il corretto funzionamento anche in presenza di congiunzioni. 28
3.5 Quinto: comandi multipli INPUT: “Take the bottle, take the glass and stop” OUTPUT: 011012044 COMPORTAMENTO: In questo caso di test, si testa il comportamento dell’algoritmo in presenza di una stringa contenente più di due comandi. Il risultato finale è pari a quello atteso. Si vuole far notare che il comando stop è stato inserito poiché il robot in futuro potrebbe anche avere funzioni di movimento. 29
3.6 Sesto: interazione sociale INPUT: “I’m David” OUTPUT: 05David COMPORTAMENTO: Come ripetuto, il robot prevede una sorta di interazione sociale che consiste nel riconoscere il nome dell’utente nel caso in cui viene inserito nell’applicativo. Il comportamento reale è pari a quello atteso. Particolare attenzione si pone sul messaggio in uscita che risulta essere 05David, cioè come già detto il nome non è codificato quindi viene mandato così com’è al robot. 30
Conclusioni In questo lavoro di tesi abbiamo sviluppato un sistema per l’interpretazione dei comandi tramite riconoscimento vocale. Gli obbiettivi prefissati erano: • l’utilizzo di un Natural Language Processing per l’elaborazione del comando dell’utente; • rendere l’applicativo indipendente dal robot e dal Natural Language Processor e dal linguaggio utilizzato; • creare un’interazione sociale tra utente e robot. Come si è visto nel capitolo in cui sono stati presentati i test, tali obiettivi sono stati raggiunti, mostrando i risultati ottenuti seguendo l’implementazione proposta nel capitolo 2, utilizzando tecnologie mobile, basate su Android e Java e un motore di Natural Language processing in cloud. Tale Natural Language Processing utilizzato dal sistema implementato è fornito dalla Stanford University, chiamato Standord NLP. Di tale risorsa sono stati utilizzati i servizi di Named Entity Recognition, Lemmatization e Part-of-speech tagging in modo da permettere l’interpretazione di più comandi alla volta e implementando oltre il possibile controllo di una pattaforma robotica l’interazione sociale dell’utente con esso. Possibili sviluppi futuri possono essere: • la possibilità di poter “insegnare” al robot eventuali comandi attaverso tecniche di Machine Learning (es. versare = prendere bottiglia + prendere bicchiere + girare mano bottiglia); • Effettuare un’analisi semantica della frase in modo da non dover necessariamente specificare il dominio applicativo del nostro sistema. 31
Bibliografia [1] Thomas Villacci, “Sviluppo di un framework per l’interazione uomo-robot basato sulle tecnologie di sintesi e riconoscimento vocale”, Tesi triennale, 2017. [2] Daniel Jurafsky and James H. Martin, “Speech and Language Processing: An Introduction to Natural Language Processing, Speech Recognition, and Computational Linguistics, Prentice-Hall, 2nd edition, 2008. [3] Google Cloud Platform Natural Language API, https://cloud.google.com/natural- language/. [4] Amazon Comprehend, https://aws.amazon.com/comprehend/. [5] IBM Watson Natural Language Understanding, https://www.ibm.com/watson/services/natural-language-understanding/. [6] Microsoft Azure Cognitive Services Language Understanding (LUIS), https://azure.microsoft.com/it-it/services/cognitive-services/language-understanding- intelligent-service/. [7] Introduzione a JSON, https://www.json.org/json-it.html. [8] Stanford Log-linear Part-Of-Speech Tagger, https://nlp.stanford.edu/software/tagger.shtml. [9] AsyncTask, https://developer.android.com/reference/android/os/AsyncTask.html. [10] Classe URL, https://docs.oracle.com/javase/7/docs/api/java/net/URL.html. [11] Classe, HttpURLConnection, https://docs.oracle.com/javase/7/docs/api/java/net/HttpURLConnection.html. 32
Puoi anche leggere