Creare un classificatore di immagini personalizzato usando Transfer Learning
Sommario
- Summary
- Configurazione
- Eseguire l'esempio : eseguire il training di un classificatore di fiori
- Creare un classificatore di immagini personalizzato
- Uso di un modello di base diverso
Riepilogo
Le immagini precedenti sono immagini di test usate nella seconda parte di questa esercitazione. L'attività consiste nel eseguire il training di un classificatore in grado di distinguere diverse categorie di immagini (in pecore e lupo di esempio) modificando un modello di classificatore esistente, il modello di base. In questo caso viene usato un modello di ResNet_18 sottoposto a training nel corpus ImageNet. Il training viene eseguito solo su 15 immagini per classe in pochi secondi e si stimano correttamente tutte le 10 immagini di test (si notino i pochi granelli di sale).
Di seguito sono riportate le risorse principali per l'esercitazione sull'apprendimento per il trasferimento:
Recipe | TransferLearning.py e TransferLearning_Extended.py (vedere Esempi/Immagine/TransferLearning). |
modelli con training preliminare | Come modello di base per l'apprendimento per il trasferimento viene usato un modello di ResNet_18 con training preliminare. |
Data | Set di dati Flowers con 102 categorie e immagini di esempio di pecore e lupi (vedere Configurazione). |
Come eseguire | Seguire la descrizione seguente. |
Eseguire la configurazione
Per eseguire il codice in questo esempio, è necessario un ambiente Python CNTK (vedere qui per informazioni sulla configurazione).
Per scaricare i dati necessari e il modello con training preliminare eseguire il comando seguente nella cartella Examples/Image/TransferLearning :
python install_data_and_model.py
Eseguire l'esempio
In questa sezione verrà creato un classificatore per il set di dati Flowers. Il set di dati è stato creato dal Visual Geometry Group presso l'Università di Oxford per le attività di classificazione delle immagini. È costituito da 102 diverse categorie di fiori comuni al Regno Unito e contiene circa 8000 immagini che sono suddivise in tre set di una volta 6000 e due volte 1000 immagini. Per altri dettagli, vedere la home page di VGG.
Per eseguire il training e la valutazione di un modello di apprendimento di trasferimento nell'esecuzione del set di dati Flowers
python TransferLearning.py
Il modello ottiene l'accuratezza del 93% sul set di dati Flowers dopo il training per 20 periodi.
Il concetto di base dell'apprendimento per il trasferimento
Quando si usa un modello di base per trasferire l'apprendimento si basano essenzialmente sulle funzionalità e sul concetto appresi durante il training del modello di base. Per una rete neurale neurale convoluzionale, ResNet_18 in questo caso, ciò significa, ad esempio, tagliare il livello denso finale responsabile della stima delle etichette di classe del modello di base originale e sostituirlo con un nuovo livello denso che stima le etichette di classe della nuova attività a portata di mano. L'input per il livello precedente e il nuovo livello di stima è lo stesso, è sufficiente riutilizzare le funzionalità di cui è stato eseguito il training. Eseguire quindi il training di questa rete modificata, solo i nuovi pesi del nuovo livello di stima o tutti i pesi dell'intera rete.
Il codice seguente è la parte di TransferLearning.py
che crea il nuovo modello dal modello di base:
# Load the pretrained classification net and find nodes
base_model = load_model(base_model_file)
feature_node = find_by_name(base_model, feature_node_name)
last_node = find_by_name(base_model, last_hidden_node_name)
# Clone the desired layers with fixed weights
cloned_layers = combine([last_node.owner]).clone(
CloneMethod.freeze if freeze else CloneMethod.clone,
{feature_node: Placeholder(name='features')})
# Add new dense layer for class prediction
feat_norm = input_features - Constant(114)
cloned_out = cloned_layers(feat_norm)
z = Dense(num_classes, activation=None, name=new_output_node_name) (cloned_out)
Creare un classificatore di immagini personalizzato
Nella sezione precedente è stato eseguito il training di un classificatore che distingue 102 diverse categorie di fiori usando circa 6000 immagini per il training. In questa sezione verranno usate solo 15 immagini per categoria per creare un classificatore in grado di indicare un lupo da una pecora. Viene usato lo stesso ResNet_18
modello di base per l'apprendimento di trasferimento. Per eseguire il training e la valutazione dell'esecuzione del modello
python TransferLearning_Extended.py
Il modello viene testato su cinque immagini di pecore e lupo ognuna e prevede correttamente tutte le etichette. Il file di output contiene per riga una rappresentazione JSON dei risultati della stima:
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":0.997, "Wolf":0.003}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.994, "Wolf":0.006}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.614, "Wolf":0.386}, "image": "..."}]
[{"class": "unknown", "predictions": {"Wolf":0.980, "Sheep":0.020}, "image": "..."}]
Si noti che le ultime tre immagini non hanno una classe di verità di base assegnata, che è naturalmente uno scenario valido, ad esempio per l'assegnazione di punteggi alle immagini non visualizzate in un servizio Web. Le immagini effettive sono le tre immagini di uccelli mostrate di seguito. La classe di verità di base per questi nell'output JSON è impostata su unknown
. Si noti che le stime per i concetti su cui è stato eseguito il training del classificatore sono piuttosto buone nonostante le poche immagini di training. Si tratta di parti di grandi dimensioni a causa del modello di base con training preliminare. Le stime per i concetti non visualizzati, ad esempio immagini di uccelli, non sono ovviamente molto significative, poiché il classificatore conosce solo pecore e lupo. Più avanti.
Struttura di cartelle per set di immagini personalizzati
È possibile usare lo TransferLearning_Extended.py
script con le proprie immagini. Di seguito sono riportate le informazioni necessarie:
class_mapping
- Matrice contenente i nomi delle categorie, ad esempio['wolf', 'sheep']
train_map_file
- Un file di testo che contiene per riga prima un URL dell'immagine e una scheda separati dall'indice di categoria corrispondente, ad esempio0
per lupo o1
per pecore:test_map_file
- Un file di testo mappa le immagini di test alla categoria corrispondente. Per le categorie sconosciute nelle immagini di test usare-1
come indice di categoria.
Lo script può generare tutti e tre gli elementi precedenti se si strutturano le immagini nel modo seguente:
<image root folder>
Train
Sheep
Wolf
Test
Sheep
Wolf
<optional: image files with unknown label directly here>
Vedere <cntk root>/Examples/Image/DataSets/Animals/
come esempio. Ogni sottocartella nella Train
cartella verrà considerata come una categoria (solo a livello singolo, nessuna ricorsione). Per il training di singole immagini nella Train
cartella radice vengono ignorate perché non hanno una categoria assegnata. Le sottocartelle aggiuntive nella Test
cartella che non si verificano nella Train
cartella vengono ignorate. Per testare singole immagini non classificate vengono usate anche per l'assegnazione dei punteggi, ad esempio le immagini archiviate direttamente nella Test
cartella come le tre immagini di uccelli nell'esempio.
Per usare le cartelle di immagini personalizzate è necessario impostare train_image_folder
e test_image_folder
nella parte superiore dello TransferLearning_Extended.py
script:
# define data location and characteristics
train_image_folder = "<your_image_root_folder>/Train"
test_image_folder = "<your_image_root_folder>/Test"
Eseguire quindi semplicemente python TransferLearning_Extended.py
. Di seguito viene descritto come usare un modello di base diverso.
Per l'assegnazione dei punteggi non è necessario usare un test_map_file
oggetto , ad esempio se si desidera assegnare un punteggio a singole immagini una alla volta. È sufficiente caricare il modello di apprendimento con training una sola volta e quindi chiamare eval_single_image
ogni volta che si vogliono ottenere stime per una nuova immagine:
# once:
# load the trained transfer learning model
trained_model = load_model(new_model_file)
# for every new image:
# get predictions for a single image
probs = eval_single_image(trained_model, img_file, image_width, image_height)
Qualche granello di sale
Le immagini di training devono coprire sufficientemente gli scenari da assegnare un punteggio in un secondo momento. Se il classificatore vede concetti o contesti completamente nuovi, è probabile che si verifichi un errore. Solo alcuni esempi:
- Si esegue il training solo su immagini da un ambiente vincolo (ad esempio, all'interno) e si tenta di assegnare punteggi alle immagini da un ambiente diverso (all'aperto).
- Si esegue il training solo su immagini di un certo make e cercare di segnare altri.
- Le immagini di test hanno caratteristiche ampiamente diverse, ad esempio rispetto all'illuminazione, allo sfondo, al colore, alle dimensioni, alla posizione e così via.
- Le immagini di test contengono concetti completamente nuovi.
L'aggiunta di una categoria catch-all può essere una buona idea, ma solo se i dati di training per tale categoria contengono immagini sufficientemente simili alle immagini previste in fase di assegnazione dei punteggi. Come nell'esempio precedente, se si esegue il training di un classificatore con immagini di pecore e lupo e lo si usa per assegnare un'immagine di un uccello, il classificatore può comunque assegnare solo un'etichetta di pecore o lupo, poiché non conosce altre categorie. Se si aggiungesse una categoria catch-all e si aggiungessero immagini di training di uccelli, il classificatore potrebbe prevedere correttamente la classe per l'immagine dell'uccello. Tuttavia, se la presenteremo, ad esempio un'immagine di un'auto, si presenta allo stesso problema di prima, perché conosce solo pecore, lupo e uccello (che ci è appena capitato di chiamare catch-all). Di conseguenza, i dati di training, anche per catch-all, devono coprire sufficientemente tali concetti e immagini che ci si aspetta più avanti in fase di assegnazione dei punteggi.
Un altro aspetto da tenere presente è che un particolare modello di base potrebbe funzionare molto bene per alcune attività di apprendimento di trasferimento e non come bene per altri. Ad esempio, il modello precedente ResNet_18
è stato sottoposto a training preliminare nel corpus ImageNet, che contiene molte immagini di animali, persone, automobili e molti altri oggetti ogni giorno. L'uso di questo modello di base nell'apprendimento di trasferimento per creare un classificatore per oggetti simili ogni giorno può funzionare bene. L'uso dello stesso modello di un modello di base per creare un classificatore per immagini di microrganismi o disegni a matita può produrre solo risultati mediocri.
Uso di un modello di base diverso
Per usare un modello diverso come modello di base, è necessario adattare i parametri seguenti in TransferLearning.py
(stesso per TransferLearning_Extended.py
):
# define base model location and characteristics
_base_model_file = os.path.join(base_folder, "..", "..", "..", "PretrainedModels", "ResNet_18.model")
_feature_node_name = "features"
_last_hidden_node_name = "z.x"
_image_height = 224
_image_width = 224
_num_channels = 3
Per esaminare i nomi dei nodi presenti nel modello e quale scegliere last_hidden_node
come è possibile stampare tutti i nomi di nodo e le forme nodo usando le righe seguenti (vedere __main__
il metodo in TransferLearning.py
):
# You can use the following to inspect the base model and determine the desired node names
node_outputs = get_node_outputs(load_model(_base_model_file))
for out in node_outputs: print("{0} {1}".format(out.name, out.shape))