Sviluppo di un sistema per l'interpretazione dei comandi ... · dizionario interno...

32
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

Transcript of Sviluppo di un sistema per l'interpretazione dei comandi ... · dizionario interno...

Scuola Politecnica e delle Scienze di BaseCorso 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

[Dedica]

Indice

Indice..................................................................................................................................................IIIIntroduzione..........................................................................................................................................4Capitolo 1: Stato dell’arte.....................................................................................................................6Capitolo 2: Implementazione.............................................................................................................15

2.1 Verifica della raggiungibilità del server....................................................................................162.1 Richiesta HTTP al server..........................................................................................................172.2 Interpretazione dei comandi.....................................................................................................182.3 Codifica dei comandi................................................................................................................212.4 Creazione del messaggio..........................................................................................................232.5 Visualizzazione dei comandi....................................................................................................24

Capitolo 3: Casi di test.......................................................................................................................253.1 Primo: comando singolo semplice............................................................................................253.2 Secondo: comando singolo articolato.......................................................................................263.3 Terzo: comando fuori dal dominio delle funzioni eseguite dal robot.......................................273.4 Quarto: due comandi semplici..................................................................................................283.5 Quinto: comandi multipli.........................................................................................................293.6 Sesto: interazione sociale.........................................................................................................30

Conclusioni.........................................................................................................................................31Bibliografia.........................................................................................................................................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.

Nomepiattaforma

Servizi offerti Lingue supportate Peculiarità

Google CloudPlatformNatural

Language API

• analisi sintattica;• analisi delle

relazioni;• analisi delle

opinioni;• analisi delle

entità.

Inglese, sagnolo,giapponese, cinese,francese, tedesco,italiano, coreano,

portoghese, e altre.

• Analisi delle opinioni relative alle entità.

AmazonComprehend

• analisi sintattica;• analisi delle

opinioni;• analisi delle

entità.

Inglese, spagnalo.

• Rilevamento della lingua;

• possibilità di tradurre e poi analizzare;

• analisi di terminio argomenti collezione di documenti (topicmodeling)

IBM WatsonNatural

LanguageUnderstanding

• analisi sintattica;• analisi delle

relazioni;• analisi delle

opinioni;• analisi delle

entità.

Inglese, arabo,francese, tedesco,

italiano,giapponese,portoghese,

coreano, spagnolo.

• Analisi delle emozioni di un testo.

MicrosoftCognitiveServices

LanguageUnderstanding

(LUIS)

• analisi sintattica;• analisi delle

entità.

Inglese americano,francese canadese,cinese, giapponese,coreano, francese,tedesco, italiano,

portoghese,spagnolo, spagnolo

messicano.

• Analisi delle intent, cioè azioni che l’utente vuole fare;

• analisi di testi malformati che devono essere 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 opinionirelative alle entità

Gratis $2.00 $1.00 $0.50

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)

Servizio 0 → 10K Every 1000 transaction

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.

Nomepiattaforma

Servizi offerti Lingue supportate Peculiarità

StafordNLP

• analisi sintattica;• analisi delle

opinioni;• analisi delle

entità.

Inglese, arabo,cinese, francese,

tedesco, spagnolo.• Open-source

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

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

var JSON = { proprieta1: 'Valore', proprieta2: 'Valore', proprietaN: 'Valore'

}

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

2.1 Richiesta HTTP al serverL’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

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

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:

2.2 Interpretazione dei comandiQuesta 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

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

“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

2.3 Codifica dei comandiLa 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

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).getString("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).getString("lemma").equals("be") || (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getString("lemma").equals("please")) || (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getString("ner").equals("PERSON"))) continue; if (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getString("pos").contains("VB")) { vb.add(responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getString("lemma")); if (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getString("lemma").equals("stop")) nn.add("no obj"); } if (responseJsonArray.getJSONObject(0).getJSONArray("tokens").getJSONObject(i).getString("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");}

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

<String, Integer> 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

2.4 Creazione del messaggioQuesta 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 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.5 Visualizzazione dei comandiQuesta 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:

Nel capitolo successivo è possibile osservare nello specifico il risultato di tale sotto-

funzione attraverso i casi di test.

Il codice utilizzato è riportato di seguito:

24

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

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

ERROR: ‘bit dell’errore’ COMMAND: ‘bit del comando’ – ‘comando’ OBJECT: ‘bit dell’oggetto’ – ‘oggetto’

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 sempliceINPUT: “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 articolatoINPUT: “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 robotINPUT: “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 sempliciINPUT: “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 multipliINPUT: “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 socialeINPUT: “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