Come addestrare un’intelligenza artificiale specializzata nel rilevamento di oggetti con TensorFlow

AI, INTELLIGENZA ARTIFICIALE

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”

 

Processo di windowing

 

 

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:

  1. Installazione di tensorflow
  2. configurazione della struttura delle directory e del software nencessario
  3. fase di labellizazione delle imagini
  4. creazione del labelmap
  5. training
  6. esportazione del modello di inferenza
  7. 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.

Se vuoi farmi qualche richiesta o contattarmi per un aiuto riempi il seguente form

    Comments