Tra i servizi che possiamo utilizzare in Xojo tramite chiamate di API remote non possiamo trascurare quelli relativi all’apprendimento automatico, con particolare attenzione verso quelli che permettono di classificare le immagini.
Sulla rete possiamo trovare diverse API, fornite dai principali player del settore, che offrono questi servizi, con costi diversi e modalità di accesso piuttosto simili.
In questo post valutiamo alcuni esempi dei servizi offerti da IBM tramite Watson e come utilizzarli con Xojo
IBM Watson
Già da tempo la IBM ha aperto la sezione Watson dei propri servizi. Come tutti sono a pagamento ma è possibile testarli gratuitamente, come utente Lite, e quindi valutarne le possibilità. La documentazione e la varietà dei servizi è ampia e comprende, tra gli altri, i servizi di riconoscimento delle immagini.
Riconoscimento delle immagini in Watson
Questo servizio offre principalmente due possibilità: identificare i visi in una immagine (restituendone la posizione, il probabile genere del soggetto e il probabile intervallo d’età) e la classificazione (ovvero il riconoscimento di possibili tag associabili all’immagine).
Il concetto chiave di questo tipo di processo è: probabilità. A differenza di come pensano in molti, il risultato è un possibile risultato non una certezza, sta a noi decidere se accettare o meno questo tipo di risultato (ad esempio accettare in automatico i valori con probabilità alta e/o rimandare ad un check successivo “umano” quelli con probabilità medio bassa).
Per la classificazione Watson offre un vasto classificatore (con definizioni localizzate anche in italiano) ed è possibile creare i propri classificatori per progetti specifici. Per l’utente Lite è possibile crearne uno solo sostituibile ma non aggiornabile, ma non ci sono limiti nelle classi definibili a parte il limite di quante immagini (numero e dimensione totale) è possibile caricare sul sistema.
La documentazione del servizio è completa, semplice da utilizzare e dopo aver creato il servizio nel proprio account e possibile iniziare ad usarlo con il terminale o con l’interfaccia web offerta.
Uso con Xojo
Visto che i servizi sono API REST possiamo riprendere i concetti di quanto visto in Utilizzare API REST e in Generare file PDF tramite API Remote e quindi partire da un oggetto Xojo.Net.HTTPSocket per implementare un oggetto base.
Avendo diversi servizi a disposizione creiamo in questo caso una sottoclasse base per gestire le API e delle ulteriori sottoclassi di queste per sfruttare i vari servizi di Watson.
La classe base deve occuparsi di rendere unica la risposta (nel senso di tipologia, poi il contenuto cambia in base al servizio) e gestire in modo semplice l’invio dei dati. Visto che è possibile inviare file e che se questi sono multipli vanno inviati come .zip andrà creata una funzione che faccia la trasformazione dei file multipli in un singolo file di questo tipo.
Creiamo quindi la classe WatsonAPI come sottoclasse di Xojo.Net.HTTPSocket. Come abbiamo visto per la generazione del PDF creiamo la classe in modo da poter essere richiamata in modo autonomo (essendo fondamentalmente asincrona dobbiamo permettere di effettuare anche chiamate diverse) per cui creiamo, ad esempio, un dizionario condiviso, che manterrà in vita gli oggetti fintanto che lavorano e poi li elimina una volta terminato il loro compito.
Definiamo quindi una delegate che avrà come argomento la risposta (positiva, negativa o errore) che avrà come firma un argomento di tipo Xojo.Core.Dictionary
WatsonReplyFunction(d as Xojo.Core.Dictionary) |
Definiamo una proprietà private che rappresenta la funzione da richiamare, oltre a id per avere un identificativo dell’oggetto nel dizionario condiviso:
Private Property callback as WatsonReplyFunction |
Il costruttore (protetto perché in realtà saranno le sottoclassi a chiamarlo) sarà del tipo:
Protected Sub Constructor( cb as WatsonAPI.WatsonReplyFunction) //Registro la callback callback=cb Super.Constructor register Me End Sub |
Dove register è una funzione condivisa privata che assegna l’identificativo all’oggetto e salva la coppia identificativo, oggetto nel dizionario condiviso. A questa funzione corrisponde anche deRegister che serve ad eliminare l’oggetto con l’identificativo assegnato.
La scelta di rispondere con un dizionario ci da la libertà di gestire a questo livello la risposta ed eventualmente rimandare l’analisi specifica alle sottoclassi o agli oggetti che utilizzeranno il risultato. Facciamo in modo che il dizionario abbia almeno 3 valori: success (booleano ad indicare se tutto è andato a buon fine o meno), status (testo ad indicare il valore dell’HTTPStatus ricevuto) e result (testo o dizionario che contiene il risultato dell’operazione).
Implementiamo gli eventi:
Sub Error(err as RuntimeException) Handles Error //Tutte le risposte avranno la stessa struttura //In caso di errore devo rispondere in modo analogo ad una risposta corretta Dim d As New Xojo.Core.Dictionary d.Value("success")=False d.Value("status")="0" d.Value("result")=err.Reason //Cancello l'oggetto dal dictionary deRegister(Me) //restituisco la risposta callback.Invoke(d) End Sub Sub PageReceived(URL as Text, HTTPStatus as Integer, Content as xojo.Core.MemoryBlock) Handles PageReceived #Pragma Unused url //Interpreto la risposta Dim d As Xojo.Core.Dictionary Dim t As Text Try t=xojo.core.TextEncoding.UTF8.ConvertDataToText(Content) Catch t="" End Try If Not t.Empty Then Try d=xojo.Data.ParseJSON(t) Catch d=Nil End Try End If Dim reply As New Xojo.Core.Dictionary reply.Value("success")=HTTPStatus=200 And d<>Nil reply.Value("status")=HTTPStatus If d=Nil Then reply.Value("result")=t Else reply.Value("result")=d End If //Cancello l'oggetto dal dictionary deRegister(Me) //restituisco la risposta callback.Invoke(reply) End Sub |
Creiamo a questo punto la sottoclasse di WatsonAPI per la classificazione: WatsonVisualRecognition
Per la classificazione delle immagini possiamo utilizzare uno o più dei nostri classificatori e/o quello di default o quelli in beta (attualmente Food, per il cibo, o Explicit, per le immagini esplicite)
Quindi definiamo le costanti relative a questi classificatori
Public Const IBMDefault as Text = default Public Const IBMExplicit as Text = explicit Public Const IBMFood as Text = food |
Aggiungiamo le costante relative al servizio
//La versione corrente del servizio Private Const version as Text = 2016-05-20 //L'indirizzo in realtà dipende dalle impostazioni dell'utente Private Const kBaseUrl as Text = https://gateway.watsonplatform.net/visual-recognition/api/v3/ //La chiave di utilizzo del servizio Private Const keyVision as Text = ••••••• |
Definiamo ora un metodo pubblico per analizzare un file immagine present sul web:
Public Shared Sub classifyImage(cb as WastonAPI.WatsonReplyFunction, imageUrl as Text, threshold as single=0.5, paramArray classifiers as Text) //Il metodo richiede un metodo da richiamare per restituire i risultati, // l'indirizzo dell'immagine da analizzare // il valore minimo da considerare per il riconoscimento // una lista di classificatori da utilizzare //Creiamo l'istanza collegandola alla callback Dim w As New WatsonVisualRecognition(cb) //threshold è il valore minimo accettabile per la classificazione // deve essere compreso tra 0 e 1 If threshold<0.0 Then threshold=0.0 If threshold>1.0 Then threshold=1.0 //Per i classificatori posso usare sia quelli forniti che i miei //Devo indicare se e quali uso per cui analizziamo quelli //presenti nel parametro Dim useIBM As Boolean Dim usePersonal As Boolean Dim m() As Text For i As Integer=0 To classifiers.ubound Select Case classifiers(i) Case IBMDefault useIBM=True Case IBMExplicit, IBMFood useIBM=True //Questi classificatori sono solo in inglese w.RequestHeader("Accept-Language")="en" Else usePersonal=True End Select If m.IndexOf(classifiers(i))=-1 Then m.Append classifiers(i) Next Dim classifier_ids As Text=Text.Join(m, ",") //Lista della tipologia dei classificatori utilizzati Redim m(-1) If useIBM Then m.Append "IBM" If usePersonal Then m.Append "me" Dim owners As Text=Text.Join(m, ",") //Creo l'URL da richiamare Dim url As Text=kBaseUrl+"classify" //Creo la lista degli argomenti Dim args() As Text args.Append "api_key="+keyVision args.Append w.getReleaseArg args.Append "url="+imageUrl If Not owners.Empty Then args.Append "owners="+owners If Not classifier_ids.Empty Then args.Append "classifier_ids="+classifier_ids If threshold>0 Then args.Append "threshold="+threshold.ToText Dim parametri As Text=Text.Join(args, "&") w.send "GET", url+If(parametri.Empty, "", "?"+parametri) End Sub |
Ora possiamo richiedere la classificazione di una immagine:
Ad esempio possiamo mettere in una Window un pulsante con, nell’evento Action, il codice:
WatsonVisualRecognition.classifyImage(WeakAddressOf analyzeResponse, "https://watson-developer-cloud.github.io/doc-tutorial-downloads/visual-recognition/fruitbowl.jpg", .3) |
Dove analyzeResponse è un metodo della Window che traduce il Dictionary in qualcosa di utile (azioni su un db, elenco testuale o semplicemente mostra il risultato come testo).
Partendo da questo semplice metodo è possibile creare tutti gli altri (aggiungendo un po di funzioni di utilità alla classe base WatsonAPI).
Nei prossimi articoli vedremo altre funzionalità e come implementarle.
Ad esempio è possibile ottenere il riconoscimento delle informazioni di base dei visi e visualizzare le informazioni
O aggiornare in pochi minuti un database di immagini con le classificazioni per poter poi ritrovare le immagini di una certa tipologia
Creare i propri classificatori
Un aspetto non trascurabile di questa soluzione è che partendo da questo oggetto è semplice creare un’applicazione per generare, aggiornare, verificare un proprio classificatore che sia specifico agli obbiettivi della nostra soluzione.
Certamente è vero che è possibile farlo per mezzo dell’interfaccia Web ma poter avere dei feedback sulle immagini è decisamente più semplice e gestibile sul desktop che in una applicazione web. Inoltre possiamo sempre aggiungere un ulteriore livello di intelligenza che può aiutarci a creare il classificatore con le immagini più adatte a farlo funzionare correttamente.
Conclusioni
I servizi API di Watson permettono di dotare le proprie applicazioni di alcune caratteristiche di intelligenza artificiale, e la semplicità delle classi necessarie per farlo dimostra la notevole versatilità di Xojo.
Il prezzo, non nel senso economico (per quello c’è il listino IBM) è nel tempo di elaborazione, non proprio istantaneo. Ma questo è dovuto in enorme parte al traffico di rete; in genere per l’invio del dato e la ricezione della risposta, a parte nel caso dell’addestramento del classificatore dove si può rimanere anche per lungo tempo in attesa che termini il “training”.
Una caratteristica importante del servizio è, se si sviluppa per macOS, la possibilità di scaricare il proprio classificatore in formato CoreML e quindi di poterlo utilizzare anche in modalità offline tramite il plugin di MBS CoreML.