Condividi tramite



Luglio 2018

Volume 33 Numero 7

Il presente articolo è stato tradotto automaticamente.

Esecuzione dei test - Introduzione alla classificazione delle immagini DNN con CNTK

Dal James McCaffrey

James McCaffreyClassificazione delle immagini è quindi necessario stabilire in quale categoria di un'immagine di input a cui appartiene, ad esempio che identifica una fotografia come uno che contengono "mele" o "oranges" o "bananas". I due approcci più comuni per la classificazione delle immagini utilizza un standard per reti neurali profonde (DNN) o una rete neurale convoluzionale (CNN). In questo articolo spiegherò l'approccio di rete neurale profonda, usando la libreria CNTK.

Dare un'occhiata figura 1 per vedere quale ha inizio in questo articolo. Il programma demo crea un modello di classificazione di immagini per un subset del set di dati modificati National Institute of Standards e Technology (MNIST). Il set di dati di training demo è costituito da 1.000 immagini di cifre scritte a mano. Ogni immagine è 28 elevata da 28 pixel di larghezza (784 pixel) e rappresenta una cifra, da 0 a 9.

Classificazione delle immagini con una rete neurale profonda con CNTK
Figura 1 classificazione delle immagini usando una rete neurale profonda con CNTK

Il programma demo crea una rete neurale standard con nodi di input 784 (uno per ogni pixel), due livelli di elaborazione nascosti (ognuno con 400 nodi) e 10 nodi di output (uno per ogni cifra possibili). Il training del modello con 10.000 iterazioni. Diminuisce lentamente la perdita (noto anche come errore di training) e l'accuratezza della stima aumenta più lentamente, che indica l'utilizzo di training.

Al termine del training, la demo si applica il modello con training per un set di dati di test di 100 elementi. L'accuratezza del modello è % 84.00, in modo 84 delle immagini di 100 test classificate correttamente.

Questo articolo presuppone che si dispone di intermedi o migliori la capacità di utilizzare un linguaggio C-famiglia di programmazione, ma non presuppone che una conoscenza molto di CNTK o reti neurali. La demo è codificata con Python, ma anche se non si conosce Python, dovrebbe essere possibile seguire la procedura senza troppa difficoltà. Nella sua interezza in questo articolo è illustrato il codice per il programma demo. I dati di due file usati sono disponibili nel download che accompagna questo articolo.

Informazioni sui dati

Set di dati MNIST completo è costituito da 60.000 immagini per le immagini di training e 10.000 per i test. Il set di training è un po' insolitamente, contenuto in due file, uno che contiene tutti i valori di pixel e uno che contiene i valori di etichetta associata (da 0 a 9). Le immagini di test sono contenute anche in due file.

Inoltre, i file di quattro origine vengono archiviati in un formato binario proprietario. Quando si usano reti neurali profonde, recupero dei dati in un formato utilizzabile è quasi sempre tempo ed è difficile. Figura 2 Mostra il contenuto della prima immagine di training. Il punto chiave è che ogni immagine è 784 pixel e ogni pixel è un valore compreso tra 00h (0 decimale) e FFh (255 decimale).

Un'immagine MNIST
Figura 2, un'immagine MNIST

Prima di scrivere il programma di dimostrazione, ho scritto un programma di utilità per leggere i file binari di origine e di scrivere un subset del loro contenuto nei file di testo che possono essere facilmente utilizzati da un oggetto lettore CNTK. File mnist_train_1000_cntk.txt sarà simile a:

|digit 0 0 0 0 0 1 0 0 0 0 |pixels 0 .. 170 52 .. 0
|digit 0 1 0 0 0 0 0 0 0 0 |pixels 0 .. 254 66 .. 0
etc.

Ottenere i dati binari non elaborati di MNIST in formato CNTK non è semplice. Il codice sorgente per il programma di utilità è reperibile in: bit.ly/2ErcCbw.

Sono disponibili 1.000 righe di dati e ognuna rappresenta un'immagine. Il tag "| digit" e "| pixel" indicare l'inizio del valore da prevedere e i valori di predittore. L'etichetta di cifra è frequente a quello con codifica in cui la posizione del bit 1 indica la cifra. Pertanto, nel codice precedente, le prime due immagini rappresentano un "5" e "1". Ogni riga di dati con valori di pixel 784, ognuno dei quali è compreso tra 0 e 255. File mnist_test_100_cntk.txt ha 100 immagini e Usa lo stesso formato di facile integrazione con CNTK.

Nei problemi di rete neurali più si desidera normalizzare i valori di predittore. Invece di normalizzazione direttamente i valori di pixel nei file di dati, il programma demo Normalizza i dati in tempo reale, come si vedrà a breve.

Il programma Demo

Il programma di dimostrazione completo, con alcune modifiche di lieve entità per risparmiare spazio, viene presentato nel figura 3. Controllo degli errori è stato rimosso. Impostare un rientro con due caratteri di spazio anziché le quattro consueto per risparmiare spazio. Si noti che il carattere "\" viene usato Python per la continuazione di riga.

Figura 3 completo dimostrativo listato del programma

# mnist_dnn.py
# MNIST using a 2-hidden layer DNN (not a CNN)
# Anaconda 4.1.1 (Python 3.5.2), CNTK 2.4
import numpy as np
import cntk as C
def create_reader(path, input_dim, output_dim, rnd_order, m_swps):
  x_strm = C.io.StreamDef(field='pixels', shape=input_dim,
    is_sparse=False)
  y_strm = C.io.StreamDef(field='digit', shape=output_dim,
    is_sparse=False)
  streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
  deserial = C.io.CTFDeserializer(path, streams)
  mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order,
    max_sweeps=m_swps)
  return mb_src
# ===================================================================
def main():
  print("\nBegin MNIST classification using a DNN \n")
  train_file = ".\\Data\\mnist_train_1000_cntk.txt"
  test_file  = ".\\Data\\mnist_test_100_cntk.txt"
  C.cntk_py.set_fixed_random_seed(1)
  input_dim = 784  # 28 x 28 pixels
  hidden_dim = 400
  output_dim = 10  # 0 to 9
  X = C.ops.input_variable(input_dim, dtype=np.float32)
  Y = C.ops.input_variable(output_dim)  # float32 is default
  print("Creating a 784-(400-400)-10 ReLU classifier")
  with C.layers.default_options(init=\
    C.initializer.uniform(scale=0.01)):
    h_layer1 = C.layers.Dense(hidden_dim, activation=C.ops.relu,
      name='hidLayer1')(X/255) 
    h_layer2 = C.layers.Dense(hidden_dim, activation=C.ops.relu,
      name='hidLayer2')(h_layer1)
    o_layer = C.layers.Dense(output_dim, activation=None,
      name='outLayer')(h_layer2)
  dnn = o_layer               # train this
  model = C.ops.softmax(dnn)  # use for prediction
  tr_loss = C.cross_entropy_with_softmax(dnn, Y)
  tr_eror = C.classification_error(dnn, Y)
  max_iter = 10000   # num batches, not epochs
  batch_size = 50   
  learn_rate = 0.01
  learner = C.sgd(dnn.parameters, learn_rate)
  trainer = C.Trainer(dnn, (tr_loss, tr_eror), [learner]) 
  # 3. create reader for train data
  rdr = create_reader(train_file, input_dim, output_dim,
    rnd_order=True, m_swps=C.io.INFINITELY_REPEAT)
  mnist_input_map = {
    X : rdr.streams.x_src,
    Y : rdr.streams.y_src
  } 
  # 4. train
  print("\nStarting training \n")
  for i in range(0, max_iter):
    curr_batch = rdr.next_minibatch(batch_size, \
      input_map=mnist_input_map)
    trainer.train_minibatch(curr_batch)
    if i % int(max_iter/10) == 0:
      mcee = trainer.previous_minibatch_loss_average
      macc = (1.0 - trainer.previous_minibatch_evaluation_average) \
        * 100
      print("batch %4d: mean loss = %0.4f, accuracy = %0.2f%% " \
        % (i, mcee, macc))
  print("\nTraining complete \n")
  # 5. evaluate model on test data
  rdr = create_reader(test_file, input_dim, output_dim,
    rnd_order=False, m_swps=1)
  mnist_input_map = {
    X : rdr.streams.x_src,
    Y : rdr.streams.y_src
  }
  num_test = 100
  test_mb = rdr.next_minibatch(num_test, input_map=mnist_input_map)
  test_acc = (1.0 - trainer.test_minibatch(test_mb)) * 100
  print("Model accuracy on the %d test items = %0.2f%%" \
    % (num_test,test_acc)) 
  print("\nEnd MNIST classification using a DNN \n")
if __name__ == "__main__":
  main()

La demo mnist_dnn.py dispone di una funzione di supporto, create_reader. Tutta la logica di controllo è in sola funzione principale. Essendo giovani CNTK e in fase di sviluppo continuo, è consigliabile aggiungere un commento che riporta in dettaglio la versione usata (2.4 in questo caso).

Installazione di CNTK può essere difficile se si ha familiarità con il mondo di Python. Prima di tutto si installa una distribuzione Anaconda di Python, che contiene l'interprete di Python necessario, i pacchetti necessari, ad esempio NumPy e SciPy e utilità, ad esempio pip. Ho utilizzato Anaconda3 4.1.1 a 64 bit, che include Python 3.5. Dopo aver installato Anaconda, installare CNTK come pacchetto Python, non è un sistema autonomo, tramite l'utilità di pip. Da una shell normale, il comando utilizzato è:

>pip install https://cntk.ai/PythonWheel/CPU-Only/cntk-2.4-cp35-cp35m-win_amd64.whl

Si noti il "cp35" nel file wheel che indica il file deve essere utilizzato con Python 3.5. Prestare attenzione a; quasi tutti gli errori di installazione CNTK che ho visto sono state a causa di incompatibilità tra le versioni di CNTK di Anaconda.

La firma della funzione di lettura è create_reader (percorso, input_dim, output_dim, rnd_order, m_swps). Il parametro percorso punta a un file di training o test in formato CNTK. Il parametro rnd_order è un flag booleano che verrà impostato su True per i dati di training perché si desidera elaborare i dati di training in ordine casuale per evitare che oscilla avanti senza stato di avanzamento di training. Il parametro sarà essere impostato su False durante la lettura dei dati di test per valutare l'accuratezza del modello perché order non è importante quindi. Il parametro m_swps ("sweep massimo") verrà impostato per la costante INFINITELY_REPEAT per i dati di training (in modo che possa essere elaborato più volte) e impostato su 1 per la valutazione dei dati di test.

Creazione del modello

La demo prepara una rete neurale profonda con:

train_file = ".\\Data\\mnist_train_1000_cntk.txt"
test_file  = ".\\Data\\mnist_test_100_cntk.txt"
C.cntk_py.set_fixed_random_seed(1)
input_dim = 784
hidden_dim = 400
output_dim = 10
X = C.ops.input_variable(input_dim, dtype=np.float32)
Y = C.ops.input_variable(output_dim)  # 32 is default

È in genere una buona idea impostare in modo esplicito CNTK globale numero seed casuale in modo che i risultati sia riproducibili. Il numero di nodi di input e outpui è determinato dai dati, ma il numero di nodi nascosti l'elaborazione è un parametro gratuito e deve essere determinato da tentativi ed errori. Uso delle variabili a 32 bit è il valore predefinito per CNTK e si verifica tipica per le reti neurali perché la precisione ottenuta tramite 64 bit non è vale la pena la riduzione delle prestazioni.

La creazione della rete come segue:

with C.layers.default_options(init=
  C.initializer.uniform(scale=0.01)):
  h_layer1 = C.layers.Dense(hidden_dim,
    activation=C.ops.relu, name='hidLayer1')(X/255) 
  h_layer2 = C.layers.Dense(hidden_dim,
  activation=C.ops.relu, name='hidLayer2')(h_layer1)
  o_layer = C.layers.Dense(output_dim, activation=None,
    name='outLayer')(h_layer2)
dnn = o_layer               # train this
model = C.ops.softmax(dnn)  # use for prediction

Python con istruzione è una scorciatoia sintattica per applicare un set di argomenti comuni a più funzioni. E qui viene usato per inizializzare tutti i pesi di rete in valori casuali compresi tra -0,01 e +0.01. L'oggetto X contiene i valori di input 784 per un'immagine. Si noti che ogni valore viene normalizzato proporzionalmente dividendoli per 255 in modo che i valori di input effettivi saranno nell'intervallo [0.0, 1.0].

 L'atto di valori di input normalizzata come input per il primo livello nascosto. Gli output del primo livello nascosto fungono da input per il secondo livello nascosto. Quindi, gli output del secondo livello nascosto vengono inviati a livello di output. I due livelli nascosti utilizzano l'attivazione ReLU (unità lineare rettificata), che, per la classificazione delle immagini, spesso funziona meglio di attivazione tanh standard.

Si noti che non vi sia Nessuna attivazione applicata ai nodi di output. Si tratta di una particolarità di CNTK, perché la funzione di training di CNTK prevede valori non elaborati e non attivati. L'oggetto di rete neurale profonda è semplicemente un alias di praticità. L'oggetto modello presenta softmax attivazione in modo che può essere utilizzato dopo il training per eseguire stime. Perché Python assegna per riferimento, l'oggetto di rete neurale profonda di training inoltre esegue il training di oggetto modello.

Training della rete neurale

La rete neurale viene preparata per il training con:

tr_loss = C.cross_entropy_with_softmax(dnn, Y)
tr_eror = C.classification_error(dnn, Y)
max_iter = 10000 
batch_size = 50   
learn_rate = 0.01
learner = C.sgd(dnn.parameters, learn_rate)
trainer = C.Trainer(dnn, (tr_loss, tr_eror), [learner])

L'oggetto di perdita (tr_loss) training indica CNTK come misurare l'errore durante il training. L'errore di entropia incrociata è in genere la scelta migliore per i problemi di classificazione. L'oggetto di errore (tr_eror) di classificazione di training è utilizzabile per calcolare automaticamente la percentuale di stime inesatte durante il training o dopo il training. È necessario specificare una funzione di perdita, ma è facoltativo specificare una funzione di errore di classificazione.

I valori per il numero massimo di iterazioni di training, il numero di elementi in un batch per eseguire il training contemporaneamente e la velocità di apprendimento, è tutti gratuiti parametri che devono essere determinati da tentativi ed errori. È possibile considerare l'oggetto strumento di apprendimento come un algoritmo, mentre l'oggetto trainer l'oggetto che utilizza lo strumento di apprendimento per trovare i valori ottimali per i pesi della rete neurale e i valori di pregiudizi. Lo strumento di apprendimento del gradiente stocastica (sgd) è l'algoritmo più primitivo ma funziona bene per i problemi semplici. Le alternative includono stima istante adattivo (adam) e propagazione radice quadrata media (rmsprop).

Viene creato un oggetto di lettura per i dati di training con queste istruzioni:

rdr = create_reader(train_file, input_dim, output_dim,
  rnd_order=True, m_swps=C.io.INFINITELY_REPEAT)
mnist_input_map = {
  X : rdr.streams.x_src,
  Y : rdr.streams.y_src
}

Se si esamina il codice create_reader figura 3, si noterà che specifichi i nomi di tag ("pixel" e "digit") usati nel file di dati. È possibile prendere in considerazione create_reader e il codice per creare un oggetto lettore come codice boilerplate per i problemi di classificazione di immagini di rete neurale profonda. Tutto ciò che devi modificare sia i nomi di tag e il nome del dizionario dei mapping (mnist_input_map).

Dopo che tutto ciò che è stato preparato, corsi di formazione viene eseguita, come illustrato nella figura 4.

Figura 4 corsi di formazione

print("\nStarting training \n")
for i in range(0, max_iter):
  curr_batch = rdr.next_minibatch(batch_size, \
    input_map=mnist_input_map)
  trainer.train_minibatch(curr_batch)
  if i % int(max_iter/10) == 0:
    mcee = trainer.previous_minibatch_loss_average
    macc = (1.0 - \
      trainer.previous_minibatch_evaluation_average) \
        * 100
    print("batch %4d: mean loss = %0.4f, accuracy = \
      %0.2f%% " % (i, mcee, macc))

Il programma demo è progettato in modo che ogni iterazione elabora un batch di elementi di training. Molte librerie di rete neurale usano il termine "epoch" per fare riferimento a un passaggio in tutti gli elementi di training. In questo esempio, poiché sono presenti 1.000 elementi di training e le dimensioni del batch sono impostata su 50, uno come valore epoch sarà 20 iterazioni.

Un'alternativa al training di un numero fisso di iterazioni è per arrestare l'addestramento perdita/errore scende sotto una soglia. È importante visualizzare perdita/errore durante il training perché un errore di training è la regola anziché l'eccezione. Errore di entropia incrociata è difficile da interpretare direttamente, ma si desidera visualizzare i valori che tendono a ottenere più piccoli. Invece di visualizzare l'errore di classificazione Media ("errato del 25%"), la demo calcola e visualizza l'accuratezza della classificazione Media ("pari al 75% corretta"), che è una metrica più naturale nella mio parere.

La valutazione e usando il modello

Dopo che un classificatore di immagini è stato eseguito il training, in genere è opportuno valutare il modello sottoposto a training sui dati di test che viene contenuti. La demo calcola una precisione della classificazione, come illustrato nella figura 5.

Figura 5 precisione della classificazione Computing

rdr = create_reader(test_file, input_dim, output_dim,
  rnd_order=False, m_swps=1)
mnist_input_map = {
  X : rdr.streams.x_src,
  Y : rdr.streams.y_src
}
num_test = 100
test_mb = rdr.next_minibatch(num_test,
  input_map=mnist_input_map)
test_acc = (1.0 - trainer.test_minibatch(test_mb)) * 100
print("Model accuracy on the %d test items = %0.2f%%" \
  % (num_test,test_acc)))

Viene creato un nuovo lettore dati. Si noti che a differenza del lettore usato per il training, il nuovo lettore non attraversa i dati in ordine casuale e che il numero di operazioni è impostato su 1. L'oggetto dizionario mnist_input_map viene ricreato. Un errore comune consiste nel provare e usare il lettore originale, ma l'oggetto rdr è cambiata e pertanto è necessario ricreare il mapping. La funzione test_minibatch restituisce l'errore di classificazione Media per il relativo argomento in mini-batch, ovvero in questo caso il set intero test di 100-item.

Dopo il training o durante il training, in genere si desidera salvare il modello. In CNTK, salvataggio si presenta come:

mdl_name = ".\\Models\\mnist_dnn.model"
model.save(mdl_name)

Verranno quindi salvati usando il formato di v2 CNTK predefinito. Un'alternativa consiste nell'usare il formato Open Neural Network Exchange (ONNX). Si noti che sarà in genere si desidera salvare l'oggetto modello (con attivazione softmax) invece dell'oggetto di rete neurale profonda (Nessuna attivazione output). Da un altro programma, è stato possibile caricare un modello salvato in memoria lungo le righe di:

mdl_name = ".\\Models\\mnist_dnn.model"
model = C.ops.functions.Function.load(mdl_name)

Dopo il caricamento, il modello può essere usato come se si è appena stato eseguito il training. Il programma demo non usa il modello con training per eseguire una stima. Codice di stima potrebbe apparire come segue:

input_list = [0.55] * 784  # [0.55, 0.55, . . 0.55]
input_vec = np.array(input_list, dtype=np.float32)
pred_probs = model.eval(input_vec)
pred_digit = np.argmax(pred_probs)
print(pred_digit)

Il input_list include un input fittizio di valori di pixel 784, ciascuno con valore 0,55 (richiamo il modello è stato eseguito il training sui dati normalizzati in modo che è necessario inserire i dati normalizzati). I valori di pixel vengono copiati in una matrice NumPy. La chiamata alla funzione eval restituirà una matrice di 10 valori di tale somma a 1,0 e regime di controllo libero può essere interpretata come probabilità. La funzione argmax restituisce l'indice (da 0 a 9) del valore più grande, che equivale a comodamente la cifra stimata. Utile, vero?

Conclusioni

Usando una rete neurale profonda utilizzato per rappresentare l'approccio più comune per la classificazione delle immagini semplice. Tuttavia, reti neurali profonde hanno almeno due limitazioni principali. In primo luogo, reti neurali profonde non scalabilità solo per le immagini con un elevato numero di pixel. In secondo luogo, reti neurali profonde non esplicitamente tener conto della geometria di pixel immagine. In un'immagine MNIST, ad esempio, un pixel che si trova direttamente sotto un secondo pixel è 28 posizioni lontano da pixel prima nel file di input.

A causa di queste limitazioni e per altri motivi, anche l'uso di una rete neurale convoluzionale (CNN) è ora più comune per la classificazione delle immagini. Ciò premesso, per la classificazione delle immagini semplice attività, usando una rete neurale profonda è più facile e spesso lo stesso (o altro ancora) efficaci rispetto all'uso di una CNN.


Dr. James McCaffreylavora per Microsoft Research Redmond, WA Ha lavorato su diversi prodotti Microsoft, tra cui Internet Explorer e Bing. Dr. È possibile contattarlo McCaffrey jamccaff@microsoft.com.

Grazie per i seguenti esperti tecnici Microsoft che ha esaminato in questo articolo: Chris Lee, Ricky Loynd, Davide Tran


Discutere di questo articolo nel forum di MSDN Magazine