• Non ci sono risultati.

Il framework Caffe

4.1.2 Utilizzo dei protocol buffer

Per utilizzare Caffe è necessario capire che cos’è un protocol buffer. I pro-tocol buffer costituiscono un meccanismo flessibile, efficiente ed automatico per la serializzazione di strutture di dati. Si può vedere un protocol buffer come una sorta di file XML, ma in realtà più piccolo, veloce e semplice. E’ possibile definire in che maniera strutturare i propri dati e, una volta fat-to quesfat-to, si può utilizzare un apposifat-to codice, precedentemente ideafat-to, per scrivere e leggere nella struttura creata; per il codice è possibile utilizzare una certa varietà di linguaggi di programmazione. In particolare, si definisce la propria struttura di dati definendo dei protocol buffer message all’interno di un file .proto. Un protocol buffer message è una piccola unità logica che con-tiene una serie di coppie (nome-valore). Per dare un’idea, la figura 4.1 mostra il contenuto di semplice file “.proto” che definisce un message contenenti le informazioni di una persona.

Dalla figura 4.1 si nota che il formato di un message è piuttosto sempli-ce: ciascun message possiede uno o più campi numerati unicamente ed ogni campo ha un suo nome ed un sua tipologia, ovvero può essere un intero, un float, un booleano ecc., oppure, come nell’esempio appena mostrato, anche un altro message come PhoneNumber. Si intuisce dunque che i message si possono anche nidificare, permettendo in questo modo di creare strutture di dati gerarchiche. Si possono inoltre specificare quali parametri sono obbliga-tori (required ), quali opzionali (optional ) e quali possono essere ripetuti più volte (repeated ). Nell’esempio una persona potrebbe avere più di un numero di telefono). Una volta definiti i propri message si avvia il compilatore dei protocol buffer per lo specifico linguaggio di programmazione voluto, passan-dogli il file .proto in modo che si generino le classi per l’accesso ai dati con lo specifico linguaggio desiderato, ad esempio Java, C++ oppure Python. Tali classi forniscono sia un semplice accesso ad ogni campo specificato, come ad esempio name() e set_name() per quanto riguarda la lettura e la scrittura di un nome di una persona, sia metodi per serializzare i dati in output oppure che permettono operazioni di parsing. L’esecuzione di questo processo sull’e-sempio di figura 4.1 creerà dunque una classe Person con i campi specificati. Nell’esempio di figura 4.2 e 4.3 vengono mostrati degli esempi di codice, in cui si è scelto C++ come linguaggio di programmazione, che si può scrivere dopo aver creato la classe Person (il primo è una scrittura, il secondo è una lettura di dati).

Figura 4.1: Esempio file .proto

Figura 4.3: Esempio codice C++ per lettura dati

Si possono aggiungere nuovi campi ai formati dei propri message senza andare ad intaccare la loro compatibilità con altri script che utilizzano an-cora una loro vecchia versione. I vecchi file binari semplicemente ignorano i nuovi campi durante il parsing dei parametri. Questo risulta molto utile, in quanto con Caffe capita spesso di avere qualche parametro aggiuntivo in mol-te situazioni. I protocol buffer costituiscono dunque per Caffe una semplice interfaccia, con cui gli utenti definiscono in maniera relativamente semplice i dati delle proprie reti e allo stesso modo posso modificare, aggiungere, ri-muovere dati senza preoccuparsi di dover ricompilare Caffe ogni volta. Nella sezione successiva si vedranno alcuni esempi di definizione di semplici reti tramite l’utilizzo dei protocol buffer.

4.2 Anatomia di una rete di Caffe

Caffe definisce la struttura di una rete neurale convoluzionale utilizzando i protocol buffer, esplicitando ogni singolo layer con i suoi parametri, partendo dall’input layer ed andando fino all’output layer. Nello specifico, nell’intero processo di utilizzo della rete in cui i dati “viaggiano” all’interno di essa, sia con la forward propagation che con la backward propagation, Caffe salva, trasmette e manipola le informazioni tramite blob. Le blob sono gli array standard che costituiscono l’interfaccia di memoria utilizzata dal framework per organizzare i dati. Come si può intuire, un layer invece costituisce l’unità fondamentale dell’intero modello di una rete, anche per quanto riguarda la sua computazione. Una rete si ottiene dunque da una collezione di layer fra loro connessi. Una blob descrive sostanzialmente come le informazioni vengono salvate e comunicate fra un layer ed un altro, come mostrato in figura 4.4, in cui la prima blob fornisce l’input al layer mentre la seconda ne contiene l’output.

I parametri relativi a ciascun layer vengono dichiarati all’interno della de-finizione dei layer stessi, mentre i parametri relativi all’allenamento generale

Figura 4.4: Esempio con due blob

della rete vengono definiti separatamente, in un altro protocol buffer. Ciò serve per rendere indipendente la definizione di una rete rispetto alla sua configurazione, o meglio il solving, e rendere inoltre più modulare il tutto. Si vedrà tutto questo nel dettaglio più avanti.

4.2.1 Blob

La struttura generale di una blob è quella di un array di dimensioni N x K x H x W, dove:

• N viene detto parametro di batch size. Esso rappresenta in pratica, il numero di elementi di input, cioè di immagini in questo caso, che vengo-no processate nella rete di volta in volta (importante per l’allenamento di una rete). Questo parametro va settato in base alla memoria che si ha a disposizione sul proprio computer (memoria della scheda video se si lavora in modalità GPU, oppure la RAM che si lavora in modalità CPU). La variazione di questo parametro non intacca le prestazioni della rete in termini di accuratezza della classificazione. Certamente un valore più basso di batch size provocherà una fase di training più lenta, perchè si processano appunto meno immagini alla volta, ma ciò è in linea col fatto che si possiede dunque un hardware più limitato. • K rappresenta il numero di canali delle immagini o la profondità di un

volume all’interno della rete.

• W è la larghezza delle immagini o di un volume all’interno della rete. Questo è il formato standard delle blob, anche se poi in realtà in certi casi assumono forme anche diverse. Ad esempio nei FC layer invece di essere in 4D sono semplicemente bidimensionali, in quanto ciò è sufficiente per rappre-sentare le informazioni per essi. Le dimensioni delle blob variano a seconda del tipo e della configurazione di un determinato layer. Ad esempio per un layer convoluzionale con 96 kernel, con un receptive field 11 x 11 che agiscono su un input di profondità 3, la blob risultante ha dimensioni 96 x 3 x 11 x 11. Per un layer FC avente 1000 neuroni, ciascuno dei quali è collegato ad un volume di 1024 neuroni, la blob ha dimensioni 1000 x 1024.