Come addestrare un’intelligenza artificiale specializzata nel rilevamento di oggetti con TensorFlow
In questo articolo proverò a fornire un tutorial su come addestrare una rete neurale specializzata nelle attività di riconoscimento di oggetti. Al fine di comprendere, pertanto, cosa verrà fatto vi anticipo che l’obiettivo generale sarà quelllo di imparare ad utilizzare ed addestrare un particolare tipo di rete neurale chiamata rete neurale convoluzionale o CNN. Allo scopo, dell’articolo, ritengo utile riprendere e ripetere in prima istanza i concetti di base legati al funzionamento di una rete CNN prima di andare avanti con la parte più tecnica.
Rete neurale convoluzionale
Una rete neurale convoluzionale (CNN o ConvNet dall’inglese convolutional neural network) è un tipo di rete neurale di tipo feed-forward ispirata all’organizzazione della corteccia visiva.
Come vedremo in seguito, una rete CNN è una rete costituita da più stadi e similarmente a quanto succede nella corteccia visiva ogni stadio è specializzato a fare cose diverse. Senza entrare nel dettaglio,infatti, possiamo affermare che il cervello umano opera delle semplificazioni per consentirci di riconoscere gli oggetti e così farà anche la nostra rete neurale. Giusto per dare qualche informazione in più, uno degli stadi intermedi, all’interno del cervello, è specializzato all’estrazione di forme o caratteristiche dell’immagine che si sta guardando. La stessa cosa la ritroveremo nelle reti CNN.
Una rete neurale convoluzionale funziona, in generale, come tutte le altre feed forward. Essa infatti è costituita da un blocco di input, uno o più blocchi nascosti (hidden layer), che effettuano calcoli tramite funzioni di attivazione (ad esempio RELU) e un blocco di output che effettua la classificazione vera e propria. La differenza, infatti, rispetto alle classiche reti feed forward è rappresentata dalla presenza dei livelli di convoluzione.
Che ruolo svolgono dunque questi ultimi all’interno della catena ?
I livelli di convoluzione svolgono un lavoro molto importante in quanto estraggono attraverso l’uso di filtri delle caratteristiche 0 features delle immagini di cui si vuole analizzare il contenuto.
A differenza pertanto di una feed forward tradizionale che lavora “sull’informazione generale dell’immagine” una CNN lavora e classifica l’immagine basandosi su particolari caratteristiche della stessa. In altre parole a secondo del tipo di filtro utilizzato è possibile identificare sull’immagine di riferimento cose diverse, ad esempio i contorni delle figure, le linee verticali, le linee orizzontali, le diagonali, etc.
Cerchiamo di semplificare il concetto.
Rispetto ad una semplice rete feed forward la CNN è in grado, pertanto, di trattare informazioni più specifiche ed essere di conseguenza più efficiente. Semplificando pertanto lo schema di funzionamento di una CNN possiamo dire che la rete è costituita da una catena di blocchi il cui ordine è rappresentato nel modo seguente:
Input->Conv->ReLU->Pool->Conv->ReLU->Pool->ReLU->Conv->ReLU->Pool->FullyConnected
Se si considera, inoltre, che la funzione ReLU è parte integrante del livello Conv possiamo ridurre la CNN allo schema seguente.
Cerchiamo di capire che ruolo svolgono i vari blocchi.
Tipicamente ogni layer convoluzionale viene fatto seguire da uno di Max-Pooling, riducendo via via la dimensione della matrice, ma aumentando il livello di “astrazione”. Si passa quindi da filtri elementari, come appunto linee verticali e orizzontali, a filtri via via più sofisticati, in grado ad esempio di riconoscere i fanali, il parabrezza… fino all’ultimo livello dove è in grado di distinguere un’automobile da un camion.
Il livello di input è costuituito da una sequenza di neuroni in grado di ricevere le informazioni dell’immagine da trattare. A questo livello, infatti, verrà passato il vettore di dati che rappresentano i pixel dell’immagine di ingresso. Nel caso ad esempio di una immagine a colori di 32 x 32 pixel il vettore di ingresso dovrà avere una lunghezza di 32 x 32 x 3; in pratica per ogni pixel dell’immagine di dimensione 32 X 32 avremo 3 valori che rapprensentano i tre colori dell’immagine in formato RGB (Red, Green e Blue) .
Il Livello convoluzionale (Conv) è il principale della rete. Il suo obiettivo è quello di individuare schemi, come ad esempio curve, angoli, circonferenze o quadrati raffigurati in un’immagine con elevata precisione. I filtri applicabili sono e possono essere più di uno; Maggiore è il loro numero e maggiore è la complessità delle features che si potranno individuare. Ma come funziona il livello di convoluzione? in pratica un filtro digitale (un piccola maschera ) è fatta scorrere sulle diverse posizioni dell’immagine in input; per ogni posizione viene generato un valore di output, eseguendo il prodotto scalare tra la maschera e la porzione dell’input coperta (entrambi trattati come vettori).
Nell’esempio in figura il filtro è rappresentato da una matrice 3×3 pertanto il pennello di scansione prenderà in esame solo una porzione di immagine di ingresso 3×3 tra cui si dovrà fare il prodotto con il filtro di convoluzione scelto.
di seguito il risulato con un esempio di filtro.
In definitiva l’immagine verrà scansionata pezzo per pezzo ottenendo in uscita una matrice di valori più piccola che rappresenta “l’immagine caratterizzata”
Il Livello ReLU (Rectified Linear Units) si pone l’obiettivo di annullare valori non utili ottenuti nei livelli precedenti ed è posto dopo i livelli convoluzionali.
Il Livello Pooling permette di identificare se la caratteristica di studio è presente nel livello precedente e rende più grezza l’immagine, mantenendo la caratteristica utilizzata dal livello convoluzionale. In altre parole il livello di pooling esegue un’aggregazione delle informazioni , generando feature map di dimensione inferiore.
Livello FC (o Fully connected, completamente connesso): E’ il livello che esegue di fatto la classificazione delle immagini.
Diario dell’installazione
Bene a questo punto fissiamo una agenda dei dei prossimi passi:
- Installazione di tensorflow
- configurazione della struttura delle directory e del software nencessario
- fase di labellizazione delle imagini
- creazione del labelmap
- training
- esportazione del modello di inferenza
- test del modello
Rilevamento di oggetti con TensorFlow
Per poter seguire questa guida, è necessario aver installato in precedenza TensorFlow-Gpu e aver scaricato l’Object Detection Api.
Per quanto riguarda l’installazione di TensorFlow-Gpu è possibile seguire la guida ufficiale su:
https://www.tensorflow.org/install/
L’Object Detection Api è possibile trovarlo all’interno di un repository su GitHub al seguente link:
https://github.com/tensorflow/models
Installiamo tutte le dipendenze; in particolare, Pandas e OpenCv non serviranno a TensorFlow, bensì agli script che provvederanno ad importare tali librerie.
pip install pillow pip install lxml pip install Cython pip install jupyter pip install matplotlib pip install pandas pip install opencv-python
Per completare la fase di setup, infine, basterà copiare e incollare i file presenti nel seguente path inclusi nell’archivio scaricato precedentemente da GitHub
path_fino_alla_cartella_models/models/research/object_detection
in una directory a vostra scelta:
Acquisizione immagini e creazione del dataset
Se tutto è stato installato correttamente, possiamo iniziare a costruire il nostro dataset. Prima di poter rilevare un oggetto attraverso delle immagini o attraverso un video, bisogna utilizzare degli strumenti che ci permettono di creare il set di immagini contenenti l’oggetto che vogliamo rilevare. Questa fase è molto importante perché la bontà del nostro risultato è direttamente proporzionale alla quantità e alla qualità delle immagini che forniamo in input alla rete neurale.
La prima cosa da fare è, quindi, quella di collezionare molte immagini raffiguranti l’oggetto da rilevare. Fatto ciò dividiamo le immagini che abbiamo trovato in questo modo:
l’80% delle immagini trovate, lo salviamo in una cartella che chiameremo “train” al seguente path:
path_fino_alla_cartella_models/models/research/object_detection/images/train
Il restante 20% lo salviamo in una cartella che chiameremo “test” al seguente path:
path_fino_alla_cartella_models/models/research/object_detection/images/test
NB: la cartella “images” è una cartella non presente nel repository e va dunque creata. Inoltre, è utile, al fine di ottenere un risultato più attendibile, inserire in entrambe le cartelle una serie di immagini diverse tra loro.
A questo punto, procediamo a scaricare da GitHub il tool LabelImg al seguente link:
https://github.com/tzutalin/labelImg
Questo tool ci permette di scontornare il nostro oggetto all’interno di ogni singola immagine del set. È importante fare ciò perché esplicitando l’oggetto da ricercare, faciliteremo il lavoro della rete neurale. L’installazione e l’utilizzo del tool sono molto semplici ed inoltre sono ben descritti nel link di GitHub.
Questo tool ci restituirà un file “.xml” per ogni immagine scontornata. Tale file racchiude una serie di parametri tra cui il nome dell’immagine, le sue dimensioni, la classe a cui tale immagine è stata associata e le coordinate del rettangolo da noi disegnato sull’immagine stessa. Al termine di tale operazione, la cartella images avrà una struttura di questo tipo:
image1.jpg
image1.xml
image2.jpg
image2.xml
..
Per poter racchiudere tutte le informazioni contenute nei file “.xml” in un unico file, utilizziamo lo script “xml_to_csv.py” contenuto nell’archivio. Per poter eseguire tale script basta eseguire il seguente comando dal terminale:
path_fino_alla_cartella_models/models/research/object_detection>python xml_to_csv.py
Tale script provvederà a creare due file “.csv” e li inserirà all’interno della cartella images. I due file saranno “train.csv” e “test.csv”.
A questo punto apriamo il file “generate_tfrecord.py” per modificarlo. Alla riga 31, il file si presenterà in questo modo:
# TO-DO replace this with label map def class_text_to_int(row_label): if row_label == 'label1': return 1 elif row_label == 'label2': return 2 elif row_label == 'label3': return 3 else: None
Questa funzione associa il parametro “row_label” a una delle etichette che abbiamo scelto per il nostro progetto. È importante quindi, replicare la struttura della funzione un numero di volte pari al numero delle classi che vogliamo rilevare, avendo cura di distinguere i valori di ritorno dei vari if a partire da 1 (non possiamo partire da 0 perché tale indice è riservato alla logica della rete neurale). Di seguito viene proposto un piccolo esempio. Supponiamo di voler rilevare due tipi di oggetti: bicchiere e bottiglia. Poiché il numero delle classi è pari a 2, la funzione sopracitata verrà modificata nel modo seguente:
# TO-DO replace this with label map def class_text_to_int(row_label): if row_label == 'bicchiere': return 1 elif row_label == 'bottiglia': return 2 else: None
Una volta modificato lo script “generate_tfrecord.py”, possiamo eseguirlo attraverso il comando, avendo cura di eseguirlo sia per la cartella “train” che per la cartella “test”:
python generate_tfrecord.py --csv_input=images train_labels.csv --image_dir=images train --output_path=train.record
python generate_tfrecord.py --csv_input=images test_labels.csv --image_dir=images test --output_path=test.record
Ovviamente entrambi i comandi vanno eseguiti sempre dalla cartella “object detection”. Il risultato sarà composto da due file che verranno inseriti nella cartella “object detection”: “train.record” e “test.record”.
Creazione della Label Map e configurazione del training
2.1 Creazione della Label Map
Prima di effettuare il training del nostro dataset, dobbiamo soltanto creare una label map. La label map può essere creata usando un editor di testo qualunque. Quello che bisogna ricordare è che tale file deve avere una struttura coerente nell’ordine e nei valori di ritorno a quella creata nel file “generate_tfrecord.py”. Quindi la label map relativa all’esempio precedente sarà:
item { id: name: 'bicchiere' } item { id: 2 name: 'bottiglia' }
Tale file va salvato con il nome “labelmap.pbtxt” nella cartella “training” (da creare) al path:
path_fino_alla_cartella_models/models/research/object_detection/training
NB: è importante cambiare l’estensione del file da “.txt” a “.pbtxt”.
2.2 Configurazione del training
A questo punto disponiamo di tutti i file necessari per eseguire il training. Resta soltanto da configurare quest’ultimo. La prima cosa da fare è quella di navigare verso la seguente directory:
path_fino_alla_cartella_models/models/research/object_detection/samples/configs
Tale cartella contiene al suo interno una serie di file di tipo “.config”. Tali file contengono la struttura dei modelli di training che possiamo scegliere per allenare la rete neurale a riconoscere gli oggetti desiderati. Il modello che scegliamo in questa guida è rappresentato dal file “faster_rcnn_inception_v2_pets.config”. Esso fa riferimento all’omonimo modello di training. Per poterlo adattare al nostro progetto, dobbiamo modificare tale file. Dunque, lo copiamo all’interno della cartella “training” (dove già risiede il file “labelmap.pbtxt”) e apportiamo le seguenti modifiche:
- Alla riga 9, impostiamo un numero di classi pari a quelle presenti nel nostro progetto.
- Alla riga 110, cambiamo il parametro fine_tune_checkpoint in:
fine_tune_chekpoint: "path_fino_alla_cartella_models/models/research/object_detection/\ faster_rcnn_inception_v2_coco_2018_01_28/model.ckpt"
Alla riga 126 e 128, cambiamo i seguenti parametri:
input_path :"path_fino_alla_cartella_models/models/research/object_detection/train.record"
label_map_path:"path_fino_alla_cartella_models/models/research/object_detection/training/labelmap.pbtxt"
Alla riga 132, modifichiamo cambiamo il parametro num_examples con il numero di immagini che sono contenute nella cartella /images/test.
Alla riga 140 e 142, cambiamo i seguenti parametri:
input_path:"path_fino_alla_cartella_models/models/research/object_detection/test.record"
label_map_path:"path_fino_alla_cartella_models/models/research/object_detection/training/labelmap.pbtxt"
Training
Per eseguire il training, basta eseguire a questo punto il seguente comando dalla cartella “object detection”:
python train.py --logtostderr --train_dir=training/ --pipeline_config_path=training/faster_rcnn_inception_v2_pets.config
Se tutto è stato fatto correttamente, inizierà la fase di training che a seconda della capacità computazionale della macchina su cui essa viene eseguita e del numero delle immagini da processare, potrà impiegarci più o meno tempo. Ad ogni modo sono previsti dal modello scelto, 20000 steps (è possibile modificare tale valore nel file “.config” del modello). Durante la fase di training verrà riportato anche il valore della perdita (“loss”) che diminuirà progressivamente con l’aumentare degli steps effettuati.
È possibile inoltre visualizzare l’andamento grafico di tali parametri, attraverso il tool tensorboard che è possibile lanciare da linea di comando in questo modo:
path_fino_alla_cartella_models/models/research/object_detection>tensorboard --logdir=training
A questo punto basta aprire il browser e collegarsi al seguente indirizzo:
http://localhost:6006
Ottenere il grafo relativo al progetto
Alla fine del training, avremo una serie di file all’interno della cartella “training”. Alcuni di questi file avranno una denominazione simile alla seguente:
model.ckpt-XXXX
dove “XXXX” corrisponde a un valore numerico. In particolare, notiamo la presenza di diversi file di questo tipo, distinguibili tra loro solo per mezzo di questo valore, tra l’altro sempre crescente. Quello che dobbiamo fare è trovare il file con il valore più alto tra quelli presenti in elenco. A questo punto non ci resta altro che eseguire tale comando dalla cartella “object detection”:
python export_inference_graph.py --input_type image_tensor \ --pipeline_config_path training/\faster_rcnn_inception_v2_pets.config \ --trained_checkpoint_prefix training/model.ckpt-XXXX --output_directory inference_graph
NB: il valore “XXXX” va sostituito con il relativo numero più grande presente all’interno della cartella “training”.
Questo comando genererà un file “frozen_inference_graph.pb” nella cartella “inference_graph” presente all’interno di “object detection”.
Testing
Per poter testare quanto fatto, è possibile utilizzare lo script “detect.py” (anch’esso presente nell’archivio). Prima di fare ciò, però, vanno modificati alcuni suoi parametri. Pertanto, apriamo lo script con un editor di testo e sostituiamo il contenuto del parametro PATH_TO_CKPT con:
'path_fino_alla_cartella_models/models/research/object_detection/inference_graph/frozen_inference_graph.pb'
e il contenuto del parametro PATH_TO_LABELS con:
os.path.join('training', 'label_map.pbtxt')
Infine, richiamiamo lo script appena modificato attraverso il comando:
python detect.py
Dopo alcune operazioni di inizializzazione, apparirà lo stream video della webcam e, se tutto è andato a buon fine, molto probabilmente riusciremo a rilevare gli oggetti desiderati.
Sono amante della tecnologia e delle tante sfumature del mondo IT, ho partecipato, sin dai primi anni di università ad importanti progetti in ambito Internet proseguendo, negli anni, allo startup, sviluppo e direzione di diverse aziende; Nei primi anni di carriera ho lavorato come consulente nel mondo dell’IT italiano, partecipando attivamente a progetti nazionali ed internazionali per realtà quali Ericsson, Telecom, Tin.it, Accenture, Tiscali, CNR. Dal 2010 mi occupo di startup mediante una delle mie società techintouch S.r.l che grazie alla collaborazione con la Digital Magics SpA, di cui sono Partner la Campania, mi occupo di supportare ed accelerare aziende del territorio .
Attualmente ricopro le cariche di :
– CTO MareGroup
– CTO Innoida
– Co-CEO in Techintouch s.r.l.
– Board member in StepFund GP SA
Manager ed imprenditore dal 2000 sono stato,
CEO e founder di Eclettica S.r.l. , Società specializzata in sviluppo software e System Integration
Partner per la Campania di Digital Magics S.p.A.
CTO e co-founder di Nexsoft S.p.A, società specializzata nella Consulenza di Servizi in ambito Informatico e sviluppo di soluzioni di System Integration, CTO della ITsys S.r.l. Società specializzata nella gestione di sistemi IT per la quale ho partecipato attivamente alla fase di startup.
Sognatore da sempre, curioso di novità ed alla ricerca di “nuovi mondi da esplorare“.
Comments