Juli 2018

Band 33, Nummer 7

Test Run: Einführung in die DNN-Bildklassifizierung mithilfe von CNTK

Durch James McCaffrey

James McCaffreyBildklassifizierung umfasst das Ermitteln welcher Kategorie ein eingabebildern speichereffiziente angehört, z. B. identifizieren ein Foto als eine mit "Apples" oder "Orangen" oder "Bananas". Die beiden am häufigsten verwendeten Ansätze für die bildklassifizierung ein standard deep neural Network (DNN) oder eine convolutional neural (Network, CNN). In diesem Artikel werde ich erläutern, die DNN-Ansatz, mit der CNTK-Bibliothek.

Werfen Sie einen Blick auf Abbildung 1, um zu sehen, worum es in diesem Artikel geht. Das Demoprogramm erstellt ein bildklassifizierungsmodell für eine Teilmenge des Datasets geändert National Institute of Standards und Technology (MNIST). Das Demo-Trainings-Dataset besteht aus 1.000 Bildern für handgeschriebene Ziffern. Jedes Bild ist 28 hoch und 28 Pixel breit (784 Pixel) und eine Ziffer, 0 bis 9 dar.

Bildklassifizierung mit einem DNN mit CNTK
Abbildung 1 Bildklassifizierung mithilfe ein DNN mit CNTK

Das Demoprogramm erstellt ein standard neuronales Netzwerk mit 784 Eingabeknoten (einen für jedes Pixel), zwei verborgene Ebenen (jedes mit 400 Knoten) und 10 Ausgabeknoten (eine für jede mögliche Ziffer). Das Modell wird trainiert mit 10.000 Iterationen. Der Verlust (auch bekannt als trainingsfehler) langsam verringert und die vorhersagegenauigkeit langsam erhöht, der angibt, dass die Schulung ausgeführt wird.

Nach dem Training abgeschlossen ist, gilt der Demos des trainierten Modells für ein testdataset 100 Elementen. Die Genauigkeit des Modells ist 84.00 Prozent, also 84 der 100-Test-Images richtig klassifiziert wurden.

In diesem Artikel wird vorausgesetzt, Sie über mittlere oder fortgeschrittene Programmierkenntnisse verfügen, die in einer C-Sprache, aber nicht davon ausgegangen, dass Sie viel über CNTK oder neuronale Netzwerke wissen. Das Demoprogramm wurde mit Python programmiert, aber auch wenn Sie Python nicht kennen, sollten Sie es ohne große Schwierigkeiten nachvollziehen können. Der Code für das Demoprogramm wird in diesem Artikel in seiner Gesamtheit vorgestellt. Die beiden Daten verwendete Dateien sind im Download, der diesen Artikel begleitet.

Verstehen der Daten

Das vollständige MNIST-Dataset besteht aus 60.000 Bildern für Trainings- und 10.000 Bilder zum Testen. Ein wenig ungewöhnlich, ist der Trainingssatz enthalten, in zwei Dateien: eine, die alle Pixelwerte enthält und eines, das die zugeordnete Bezeichnung-Werte (0 bis 9) enthält. Der Test-Images sind auch in zwei Dateien enthalten.

Darüber hinaus werden die vier Quelldateien in einem binären Format gespeichert. Beim Arbeiten mit dnns Abrufen der Daten in eine verwendbare Form fast immer zeitaufwendig und schwierig ist. Abbildung 2 zeigt den Inhalt des ersten Bilds Training. Der wichtigste Punkt ist, dass jedes Bild 784 Pixel, und jedes Pixel ein Wert zwischen 00h (0 Dezimalstellen) und FFh (255 Dezimal ist).

Ein MNIST-Bild
Abbildung 2: ein MNIST-Bild

Vor dem Schreiben des Demoprogramms, habe ich ein Dienstprogramm-Programm, um die binären Dateien lesen und schreiben eine Teilmenge von deren Inhalt um Textdateien, die von einem CNTK-Reader-Objekt einfach genutzt werden können. File mnist_train_1000_cntk.txt looks like:

|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.

Abrufen von MNIST binären Rohdaten in CNTK-Format ist nicht trivial. Der Quellcode für das Hilfsprogramm-Programm finden Sie unter: bit.ly/2ErcCbw.

Es gibt 1.000 Zeilen von Daten aus, und jeder ein Bild darstellt. Die Tags "| Digit" und "| Pixel" das Starten der Wert vorherzusagende und die prädiktorwerte anzugeben. Die Bezeichnung Ziffern ist codiert, in dem die Position des Bits 1 gibt an, die Ziffer One-hot. Aus diesem Grund repräsentieren im obigen Code, die ersten beiden Bilder, eine "5" und "1". Jede Zeile der Daten hat 784 Pixelwerten, von die jede zwischen 0 und 255 ist. Datei mnist_test_100_cntk.txt hat 100 Bilder und verwendet das gleiche CNTK-freundlichen Format.

In die meisten neuronale Netzwerkprobleme möchten Sie die prädiktorwerte normalisieren. Statt direkt normalisiert die Pixelwerte in den Datendateien, normalisiert das Demoprogramm die Daten dynamisch, wie Sie bald sehen werden.

Das Demoprogramm

Das vollständige Demoprogramm (mit einigen kleinen Bearbeitungen, um Platz zu sparen) wird in Abbildung 3 gezeigt. Alle normalen Fehlerüberprüfungen wurden entfernt. Ich für den Einzug zwei Leerzeichen anstelle der üblichen vier um Platz zu sparen. Beachten Sie, dass das Zeichen "\" von Python für die zeilenfortsetzung verwendet wird.

Abbildung 3 vollständige Demo Programmliste

# 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()

Die Demo mnist_dnn.py hat es sich um eine Hilfsfunktion Create_reader. Die gesamte Steuerungslogik ist in der einzigen Hauptfunktion zusammengefasst. Da CNTK und in der fortlaufenden Entwicklung, ist es eine gute Idee, einen Kommentar mit, welche Version verwendet wird (in diesem Fall 2.4) hinzufügen.

Installieren des CNTK werden etwas schwieriger, wenn Sie noch nicht mit der Python-Welt sind. Zuerst installieren Sie eine Verteilung von Anaconda Python, die die erforderlichen Python-Interpreter enthält, die erforderlichen Pakete wie NumPy und SciPy sowie nützliche Hilfsprogramme wie Pip. Ich habe Anaconda3 4.1.1.1 (64-Bit) verwendet. Diese Version beinhaltet Python 3.5. Nach der Installation von Anaconda installieren Sie CNTK als Python-Paket, ein eigenständiges-System, mit dem Pip-Hilfsprogramm. Aus einer gewöhnlichen Shell habe ich den folgenden Befehl verwendet:

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

Beachten Sie, dass die "cp35" in der wheeldatei, die die Datei gibt an, für die Verwendung mit der Python 3.5. Achten Sie darauf; fast alle CNTK-Installationsfehler, die ich gesehen habe, wurden aufgrund von Inkompatibilitäten der Anaconda-und CNTK-Version.

Die Signatur der Funktion Reader ist Create_reader (Pfad, Input_dim, Output_dim, Rnd_order, M_swps). Path-Parameter verweist auf eine etwas Trainings- oder Testdaten-Datei, die CNTK-Format aufweist. Der Rnd_order-Parameter ist ein boolesches Flag, die auf "true" für die Trainingsdaten, da Sie die Trainingsdaten in zufälliger Reihenfolge aus, um zu verhindern, ohne dass den Fortschritt des Trainings oszilliert verarbeiten möchten festgelegt werden. Der Parameter wird auf False festgelegt werden beim Lesen von Testdaten modellgenauigkeit ausgewertet, da die Reihenfolge nicht wichtig ist dann. Der M_swps-Parameter ("maximale Sweep") wird auf die Konstante INFINITELY_REPEAT für Trainingsdaten (damit es wiederholt verarbeitet werden kann), und legen für die Bewertung von Test-Daten auf 1 festgelegt.

Erstellen des Modells

Die Demo wird ein tiefes neuronales Netzwerk mit vorbereitet:

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

Es ist in der Regel eine gute Idee, CNTK globale Startwert für Zufallszahlen explizit festlegen, damit Ihre Ergebnisse reproduziert werden können. Die Anzahl der Eingabe- und Ausgabeknoten wird durch Ihre Daten bestimmt, die Anzahl der verborgenen Verarbeitungsknoten ist jedoch ein freier Parameter und muss durch Versuch und Irrtum ermittelt werden. Verwenden von 32-Bit-Variablen ist der Standardwert für CNTK und typisch für neuronale Netzwerke, da die Genauigkeit, die unter Verwendung von 64 Bits haben die Leistungseinbußen Wert ist es entstehen.

Das Netzwerk wird folgendermaßen erstellt:

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

Die Python-Anweisung „with“ ist eine syntaktische Abkürzung, um eine Reihe von allgemeinen Argumenten auf mehrere Funktionen anzuwenden. Hier wird es verwendet, alle netzwerkgewichtungen in zufälligen Werte zwischen -0,01 und +0.01 initialisiert werden. Die X-Objekt enthält die 784 Eingabewerte für ein Bild. Beachten Sie, dass jeder Wert durch 255 geteilt werden, damit die tatsächlichen Werte für die Eingabe im Bereich [0,0; 1,0] werden normalisiert wurde.

 Der normalisierte Eingabewerte Vorgang als Eingabe an der ersten verborgenen Schicht. Die Ausgaben von der ersten verborgenen Schicht dienen als Eingaben in der zweiten verdeckten Schicht. Anschließend werden die Ausgaben der zweiten verdeckten Schicht auf die Ausgabeschicht gesendet. Die zwei verdeckten Schichten verwenden Sie die Aktivierung Rectified (relu lineare Einheiten), die für die bildklassifizierung, häufig eine bessere Leistung als standard Tanh-Aktivierung funktioniert.

Beachten Sie, dass auf die Ausgabeknoten keine Aktivierung angewendet wird. Dies ist eine Eigenart von CNTK, da die CNTK-Trainingsfunktion nicht aktivierte Rohwerte erwartet. Das Dnn-Objekt ist nur ein praktischer Alias. Das Modellobjekt verfügt über eine Softmax-Aktivierung, sodass es nach dem Training für Vorhersagen verwendet werden kann. Da Python durch Verweis zuordnet, trainiert das Trainieren von Dnn-Objekts auch das Modellobjekt.

Trainieren des neuronalen Netzwerks

Das neuronale Netzwerk wird für das Training wie folgt vorbereitet:

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])

Das Training Loss (Tr_loss)-Objekt, informiert CNTK Gewusst wie: Fehler beim training gemessen. Die Kreuzentropie-Fehlerfunktion ist normalerweise die beste Wahl für klassifizierungsprobleme auftreten. Das Training Classification Fehlerobjekt (Tr_eror) kann verwendet werden, um den Prozentsatz falscher Vorhersagen während des Trainings oder nach dem Training automatisch zu berechnen. Eine verlustfunktion die Angabe ist erforderlich, aber das Angeben einer Klassifizierung Error-Funktion ist optional.

Die Werte für die maximale Anzahl der trainingsiterationen,-die Anzahl der Elemente in einem Batch zum Trainieren, und die Lernrate sind alles freie Parameter, die durch Versuch und Irrtum ermittelt werden müssen. Sie können sich vorstellen als einen Algorithmus das Learner-Objekt und das Trainer-Objekt als das Objekt, das den Learner verwendet, um gute Werte für das neuronale Netzwerk-Gewichtungen und biaswerten zu finden. Das lernmodul stochastischen gradientenverfahren (Sgd) ist, der meisten primitiven-Algorithmus jedoch eignet sich gut für einfache Probleme. Alternativen sind adaptive Moment Schätzung (Adam) "und" Wurzel des mittleren quadratischen Weitergabe (Rmsprop).

Ein Reader-Objekt, für die Trainingsdaten wird mit diesen Anweisungen erstellt:

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
}

Wenn Sie die Create_reader-Code in untersuchen Abbildung 3, wie Sie sehen, wird die Tagnamen ("Pixel" und "Digit"), der in der Datendatei verwendet. Sie können Create_reader und den Code zum Erstellen eines Reader-Objekts als Codebeispiele für klassifizierungsprobleme von DNN-Image in Betracht ziehen. Sie ändern müssen lediglich die Tagnamen und den Namen des zuordnungswörterbuchs (Mnist_input_map).

Nachdem alles vorbereitet wurde, Schulung ausgeführt wird, siehe Abbildung 4.

Abbildung 4-Training

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))

Das Demoprogramm ist so konzipiert, dass jede Iteration ein Nachrichtenbatch trainingselemente verarbeitet. Viele neural Network-Bibliotheken verwenden den Begriff "Epoche", um auf einem einzigen Durchlauf durch alle trainingselemente verweisen. In diesem Beispiel da 1.000 trainingselemente vorhanden sind, und die Batchgröße auf 50 festgelegt ist wäre eine Epoche 20 Iterationen.

Eine Alternative zum Training mit einer festen Anzahl von Iterationen ist das Training beendet, wenn der Verlust/Fehler unter einen Schwellenwert fällt. Es ist wichtig, dass während des Trainings Verluste/Fehler angezeigt werden, da Trainingsfehler eher die Regel als die Ausnahme sind. Kreuzentropie-Fehlerfunktion ist schwierig, die direkt zu interpretieren, aber Sie sollten Werte sehen, die tendenziell kleiner zu werden. Anstatt durchschnittliche klassifizierungsverluste-Fehler ("25 Prozent falsch"), die Demo berechnet werden soll, und gibt die durchschnittliche klassifizierungsgenauigkeit ("75 Prozent korrigieren"), die meiner Meinung nach eine natürlichere Metrik ist.

Auswerten und Verwenden des Modells

Nachdem eine bildklassifizierung trainiert wurde, sollten Sie in der Regel das trainierte Modell anhand von Testdaten zu bewerten, die zurückgehalten wurden. Die Demo berechnet die klassifikationsgenauigkeit im Wesentlichen, siehe Abbildung 5.

Abbildung 5 berechnen Klassifikationsgenauigkeit im Wesentlichen

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)))

Ein neuer Datenleser wird erstellt. Beachten Sie, dass der neue Leser im Gegensatz zu dem für das Training verwendeten Leser die Daten nicht in zufälliger Reihenfolge durchläuft und dass die Anzahl der Durchläufe auf 1 festgelegt ist. Das Wörterbuchobjekt Mnist_input_map wird neu erstellt. Ein häufiger Fehler besteht, versuchen Sie es aus, und verwenden den ursprünglichen Reader – aber das Rdr-Objekt wurde geändert, daher Sie die Zuordnung erneut erstellen müssen. Die Funktion "test_minibatch" gibt den durchschnittlichen Klassifizierungsfehler für ihr Mini-Batch-Argument, in diesem Fall wird der gesamte 100 Elementen Testsatz zurück.

Nach dem Training oder während des Trainings möchten Sie das Modell in der Regel speichern. In CNTK sieht wie die speichern:

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

Dadurch werden gespeichert, das Standardformat von CNTK v2 verwenden. Eine Alternative ist die Verwendung des ONNX-Formats (Open Neural Network Exchange). Beachten Sie, dass Sie in der Regel das Modellobjekt (mit Softmax-Aktivierung) speichern möchten, anstatt das Dnn-Objekt (keine ausgabeaktivierung). Aus einem anderen Programm kann ein gespeichertes Modell wie folgt in den Speicher geladen werden:

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

Nach dem Laden kann das Modell so verwendet werden, als ob es gerade erst trainiert worden wäre. Das Demoprogramm verwendet das trainierte Modell nicht, um eine Vorhersage zu treffen. Vorhersage Code könnte so aussehen:

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)

Die Input_list verfügt über eine dummy Eingabe 784 Pixelwerte, jeweils mit dem Wert 0,55 (Beachten Sie, die das Modell auf normalisierte Daten trainiert wurde, damit Sie normalisierte Daten eingeben müssen). Die Pixelwerte werden in ein NumPy-Array kopiert. Der Aufruf von der eval-Funktion zurück ein Array von 10 Werte dieser Summe 1,0 und kann lose als Wahrscheinlichkeiten interpretiert werden. Die Argmax-Funktion gibt den Index (0 bis 9), der den größten Wert, der bequem die vorhergesagten Ziffer identisch ist. Äußerst praktisch!

Zusammenfassung

Mithilfe eines tiefen neuronalen Netzwerks verwendet, um die am häufigsten verwendete Ansatz für die einfache bildklassifizierung werden. DNNs müssen jedoch mindestens zwei wichtige Einschränkungen. Zunächst nicht DNNs mit Bildern geeignet, die eine große Anzahl von Pixeln aufweisen. Zweitens berücksichtigt nicht DNNs explizit die Geometrie des Pixel. Beispielsweise ist eine Pixel, die direkt unterhalb einer zweiten Pixel in einem Bild MNIST 28 Positionen von erste Pixel in der Eingabedatei.

Aufgrund dieser Einschränkungen sowie aus anderen Gründen, ist jetzt die Verwendung von einem convolutional neural (Network, CNN) eher üblich, für die bildklassifizierung. Dies bedeutet, dass für die einfache bildklassifizierung Aufgaben unter Verwendung ein DNN ist einfacher und oft einfach als (oder noch vieles mehr) als die Verwendung einer CNN wirksam.


Dr. James McCaffreyist in Redmond (Washington) für Microsoft Research tätig. Er hat an verschiedenen Microsoft-Produkten mitgearbeitet, unter anderem an Internet Explorer und Bing. Dr. McCaffrey erreichen Sie unter jamccaff@microsoft.com.

Unser Dank gilt den folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: Chris Lee, Ricky Loynd, Ken Tran


Diesen Artikel im MSDN Magazine-Forum diskutieren