Entwicklung einer Anwendung für maschinelles Lernen mit dem Raspberry Pi

Von Stephen Evanczuk

Zur Verfügung gestellt von Nordamerikanische Fachredakteure von DigiKey

Entwicklern, die Methoden für maschinelles Lernen ausprobieren möchten, steht ein breites Spektrum von spezieller Hardware und Entwicklungsplattformen zur Verfügung, die sich oft auf spezielle Klassen der Architektur und Anwendungen des maschinellen Lernens spezialisiert haben. Obwohl diese speziellen Plattformen für viele Anwendungen für maschinelles Lernen unverzichtbar sind, sind nur wenige Neulinge auf dem Gebiet des maschinellen Lernens in der Lage, bei der Auswahl der Plattform eine wohlüberlegte Entscheidung zu treffen.

Die Entwickler benötigen eine zugänglichere Plattform, um Erfahrungen mit der Entwicklung von Anwendungen für maschinelles Lernen zu sammeln und ein tieferes Verständnis für die erforderlichen Ressourcen und die sich daraus ergebenden Möglichkeiten zu erwerben.

Wie im DigiKey-Artikel „Get Started with Machine Learning Using Readily Available Hardware and Software (Einstieg in das maschinelle Lernen mit verfügbarer Hardware und Software)“ beschrieben wird, umfasst die Entwicklung eines beliebigen Modells für überwachtes maschinelles Lernen drei wichtige Schritte:

  • Vorbereitung von Daten für das Training eines Modells
  • Implementierung des Modells
  • Training des Modells

Die Vorbereitung der Daten umfasst bekannte Datenerfassungsmethoden sowie zusätzliche Schritte zur Markierung von spezifischen Dateninstanzen zur Verwendung im Trainingsprozess. Für die letzten beiden Schritte mussten die Spezialisten für Modelle für das maschinelle Lernen vor Kurzem noch mathematische Low-Level-Bibliotheken verwenden, um die in den Modellalgorithmen verwendeten detaillierten Berechnungen zu implementieren. Die Verfügbarkeit von Frameworks für maschinelles Lernen hat die Komplexität der Implementierung und des Trainings von Modellen dramatisch reduziert.

Heute kann jeder mit Python oder einer anderen unterstützten Programmiersprache vertraute Entwickler mithilfe dieser Frameworks schnell Modelle für maschinelles Lernen entwickeln, die auf einer Vielzahl von Plattformen lauffähig sind. In diesem Artikel wird der Stapel und der Trainingsprozess für das maschinelle Lernen beschrieben, bevor die Entwicklung einer Anwendung für maschinelles Lernen auf einem Raspberry Pi 3 behandelt wird.

Stapel für das maschinelle Lernen

Zur Unterstützung der Modellentwicklung stellen die Frameworks für maschinelles Lernen einen vollständigen Ressourcenstapel bereit (Abbildung 1). An der Spitze eines typischen Stapels stellen Trainings- und Inferenzbibliotheken Dienste zum Definieren, Trainieren und Ausführen von Modellen bereit. Diese Modelle beruhen ihrerseits auf optimierten Implementierungen von Kernfunktionen wie Faltung (convolution) und Aktivierungsfunktionen wie ReLU sowie Matrixmultiplikation und anderen. Diese optimierten mathematischen Funktionen arbeiten mit Treibern auf niedriger Ebene zusammen, die eine Abstraktionsschicht zum Ansprechen einer gewöhnlichen CPU bereitstellen bzw. von spezieller Hardware, z. B. von einer eventuell vorhandenen GPU (Grafikprozessor).

Diagramm eines typischen Stapels für maschinelles Lernen

Abbildung 1: Bei einem typischen Stapel für maschinelles Lernen stellen die Bibliotheken auf hoher Ebene Funktionen zur Implementierung von neuronalen Netzen und anderen Algorithmen für maschinelles Lernen bereit und greifen dabei auf spezialisierte mathematische Bibliotheken zurück, in denen für CPUs und GPUs der zugrunde liegenden Hardwareschicht optimierte Kernfunktionen implementiert sind. (Bildquelle: Google)

Seitdem Frameworks für maschinelles Lernen wie TensorFlow verfügbar sind, die diese Stapel bereitstellen, verläuft der Entwicklungsprozess für die Implementierung des maschinellen Lernens unabhängig von der Ziel-Hardware immer gleich. Die Möglichkeit, TensorFlow über verschiedene Hardwareplattformen hinweg einzusetzen, erlaubt es den Entwicklern, die Entwicklung von Modellen auf relativ bescheidenen Hardwareplattformen in Angriff zu nehmen und diese Erfahrungen dann für die Entwicklung von Anwendungen für maschinelles Lernen auf robusterer Hardware zu übernehmen.

Es wird erwartet, dass spezialisierte Hochleistungschips für KI (Künstliche Intelligenz) die Entwickler schließlich in die Lage versetzen, ausgeklügelte Algorithmen für maschinelles Lernen zu implementieren. Bis dahin kann die Entwicklung mit der Erprobung des maschinellen Lernens beginnen und echte Anwendungen für maschinelles Lernen auf Universalplattformen wie dem Raspberry Pi 3 der Raspberry Pi Foundation oder auf einer anderen im Handel verfügbaren Entwicklungsplatine mit Universalprozessoren wie MCUs der Arm®-Cortex®-A-Serie oder der Arm-Cortex-M-Serie erstellen.

Der Raspberry Pi 3 bietet einige unmittelbare Vorteile als Entwicklungsplattform für Anwendungen für maschinelles Lernen. Der Arm-Cortex-A53-Quadcore-Prozessor verfügt über beträchtliche Leistungsreserven, und die NEON-SIMD-Erweiterungen (SIMD: Single Instruction, Multiple Data) des Kerns sind bis zu einem gewissen Grade zur Multimediaverarbeitung und zur Datenverarbeitung für maschinelles Lernen geeignet. Die Entwickler können die Basis-Hardwareplattform des Raspberry Pi 3 auch durch viele verfügbare kompatible Hardware-Add-ons erweitern.

Um ein Bilderkennungssystem für maschinelles Lernen wie unten beschrieben zu erstellen, kann der Raspberry Pi durch eine Kamera wie die 8-Megapixel-Kamera des Raspberry Pi Camera Module v2 oder die Pi NoIR-Kamera für schlechte Lichtverhältnisse erweitert werden (Abbildung 2).

Bild des Raspberry Pi 3

Abbildung 2: Kostengünstige Boards wie das Raspberry Pi 3 bieten eine nützliche Plattform für die Entwicklung für maschinelles Lernen, da sie Add-ons wie Kameramodule für die Entwicklung von Bildklassifizierungsanwendungen unterstützen. (Bildquelle: Raspberry Pi Foundation)

Softwareseitig hat die Raspberry-Pi-Community ein ebenso reichhaltiges Ökosystem geschaffen, in dem Entwickler ganze Distributionen, darunter vollständige vorkompilierte binäre Wheel-Dateien zur Installation von TensorFlow auf einem Raspberry Pi, bereitstellen. TensorFlow stellt diese Wheel-Dateien für Python 3.4 und 3.5 auf dem Raspberry-Pi-Wheels-Repository „piwheels.org“ zur Verfügung. Da Docker jetzt die Arm-Architektur offiziell unterstützt, können Entwickler als Alternative entsprechende Container von „dockerhub.com“ verwenden.

Implementierung eines Modells für maschinelles Lernen

Mit dieser Kombination von Software, einem Raspberry Pi 3 und einem Kameramodul kann unter Verwendung von Beispielcode von Arm eine einfache maschinell lernende Gestenerkennungsanwendung erstellt werden. Die Anwendung soll nur erkennen, wann der Benutzer eine bestimmte Geste ausführt: in diesem Falle die Hände als eine Art feierliche Gebärde nach oben strecken.

Zeichnen Sie zunächst mit dem Python-Script (record.py) kurze Videoclips einer Person auf, die diese Geste mehrmals ausführt. Da die Anwendung so einfach wie möglich sein soll, beginnt mit dem nächsten Schritt das Training mithilfe der in TensorFlow eingebetteten Keras-API für maschinelles Lernen. In diesem Beispiel wird das Training in einem anderen Python-Script (train.py) definiert, das die Keras-Modelldefinition und Trainingsabfolge enthält (Listing 1).

Kopieren
def main():
    .
    .
    .
    model_file = argv[1]
    recording_files = argv[2:]
    feature_extractor = PiNet()
    .
    .
    .
    for i, filename in enumerate(recording_files):
        stdout.write(' %s' % filename)
        stdout.flush()
        with open(filename, 'rb') as f:
            x = load(f)
            features = [feature_extractor.features(f) for f in x]
            label = np.zeros((len(recording_files),))
            label[i] = 1.          # Make a label with a 1 in the column for the file for this frame
            xs += features         # Add the features for the frames loaded from this file
            ys += [label] * len(x) # Add a label for each frame in the file
            class_count[i] = len(x)

    print("Creating a network to classify %s" % ', '.join(recording_files))
    classifier = make_classifier(xs[0].shape, len(recording_files))

    print("Training the network to map high-level features to %d categories" % len(recording_files))
    classifier.fit([np.array(xs)], [np.array(ys)], epochs=20, shuffle=True)

    print("Now we save this model so we can deploy it whenever we want")
    classifier.save(model_file)


def make_classifier(input_shape, num_classes):
    """ Make a very simple classifier
        Layers:
            GaussianNoise: Add random noise to prevent our classifier memorizing specific examples.
            Flatten: The input may come from a layer with shape (x, y, depth); flatten it to 1D.
            Dense: Provide one output per class scaled to sum to 1 (softmax) """

    # Define a simple neural network
    net_input = keras.layers.Input(input_shape)

    noise = keras.layers.GaussianNoise(0.3)(net_input)
    flat = keras.layers.Flatten()(noise)
    net_output = keras.layers.Dense(num_classes, activation='softmax')(flat)

    net = keras.models.Model([net_input], [net_output])

    # Compile a model before use. The loss should match the output activation function, e.g.
    # binary_crossentropy for sigmoid, categorical_crossentropy for softmax, mse for linear.
    # Adam is a solid default optimizer, we can leave the learning rate at the default.
    net.compile(optimizer=keras.optimizers.Adam(),
                loss='categorical_crossentropy',
                metrics=['accuracy'])
    return net

Listing 1: In diesem Codebeispiel aus dem Repository für Beispielanwendungen von Arm verbindet der Trainingsvorgang ein vortrainiertes Modell mit einer einfachen für diese Anwendung erforderlichen Klassifizierung. (Codequelle: Arm)

Trotz seiner scheinbaren Einfachheit ist dieses Modell recht ausgeklügelt. Es wird eine als „Transfer-Lernen“ bezeichnete Technik angewendet. Beim Transfer-Lernen wird ein mit einem Datensatz trainiertes bewährtes Modell als Startpunkt für das Training eines Modells für eine andere, aber ähnliche Problemkonstellation verwendet. In diesem Fall wird in der Anwendung ein CNN-Modell (Convolutional Neural Network, Konvolutionsnetz) aus dem MobileNet-Modellsatz von Google verwendet.

Die von Google entwickelten MobileNet-Modelle sind CNNs, die mit einer Untermenge des ImageNet-Standard-Datensatzes mit gekennzeichneten Bildern trainiert wurden. Das unterscheidende Merkmal von MobileNet-Modellen besteht darin, dass sie für die Unterstützung von reduzierten Ressourcenanforderungen konfiguriert sind, die für Mobilgeräte oder Universalplatinen wie Raspberry Pi erforderlich sind. Der Preis für diese Reduzierung von Ressourcen ist eine geringere Genauigkeit. Obwohl die Genauigkeit dieser MobilNet-Modelle erheblich unter den Anforderungen für unternehmenskritische Anwendungen liegt, können sich die Modelle bei abgeschwächten Anforderungen und als Basismodell für das Transfer-Lernen als nützlich erweisen.

Bei dieser Anwendung erstellt das Script „train.py“ mithilfe eines MobileNet-Modells, aus dem die letzte Klassifizierungsschicht entfernt wurde, einen Merkmal-Extraktor.

feature_extractor = PiNet()

Die Funktion PiNet liest einfach das im Code-Repository enthaltene modifizierte MobileNet-Modell.

Die Anwendung erstellt dann mithilfe des modifizierten Modells die Menge der Merkmale (features) für diesen Trainingsdatensatz von Bildern:

features = [feature_extractor.features(f) for f in x]

wobei x ein von „record.py“ während der anfänglichen Datenerfassung generiertes Array von Frames ist.

Schließlich wird im Script „train.py“ das Modell mit der Keras-Fit-Methode trainiert und das finale Modell mithilfe der Speichermethode zur Verwendung während der Inferenz gespeichert:

classifier.fit([np.array(xs)], [np.array(ys)], epochs=20, shuffle=True)

classifier.save(model_file)

Wenn die dabei entstehende Modelldatei zur Inferenz verwendet werden soll, muss das Script „run.py“ aufgerufen werden. Das zentrale Design-Pattern in diesem Script ist die Endlosschleife, das die Frames liest, Merkmale berechnet (extractor.features), dieselbe Klassifikation zur Inferenz verwendet (classifier.predict) und die Vorhersagen für die Markierung liefert (np.argmax). Die Vorhersagen bestehen einfach in der Aussage, ob die Zielgeste aufgetreten ist oder nicht (Listing 2).

Kopieren
    while True:
        raw_frame = camera.next_frame()

        # Use MobileNet to get the features for this frame
        z = extractor.features(raw_frame)

        # With these features we can predict a 'normal' / 'yeah' class (0 or 1)
        # Keras expects an array of inputs and produces an array of outputs
        classes = classifier.predict(np.array([z]))[0]

        # smooth the outputs - this adds latency but reduces interruptions
        smoothed = smoothed * SMOOTH_FACTOR + classes * (1.0 - SMOOTH_FACTOR)
        selected = np.argmax(smoothed) # The selected class is the one with highest probability

        # Show the class probabilities and selected class
        summary = 'Class %d [%s]' % (selected, ' '.join('%02.0f%%' % (99 * p) for p in smoothed))
        stderr.write('\r' + summary)

Listing 2: Dieses Codebeispiel aus dem Beispielscript „run.py“ von Arm demonstriert das grundlegende Design-Pattern für die Inferenz, wobei dasselbe vorher trainierte Modell zur Extraktion von Merkmalen und dieselbe Klassifikation zur Durchführung der Inferenz verwendet wird. (Codequelle: Arm)

Modelle mit mehrfacher Klassifizierung

Die Entwicklung einer Anwendung, die eine Eingabeklasse, z. B. eine einzelne Geste, erkennt, ist eine Übung auf dem Gebiet des maschinellen Lernens, aber Anwendungen für maschinelles Lernen sind typischerweise für die Klassifizierung von mehreren Klassen bestimmt. Eine weitere Beispielanwendung von Arm veranschaulicht die dafür erforderlichen Schritte und stellt ein etwas vollständigeres Beispiel für die typischerweise für die Entwicklung eines vollständigen Modells mit mehrfacher Klassifizierung erforderlichen Schritte dar. Für die Anwendung für eine einzelne Geste sind z. B. außer der Aufzeichnung der gewünschten Geste keine signifikanten Vorbereitungen in Form von Daten erforderlich. Dagegen umfasst die Anwendung mit mehrfacher Klassifizierung eine substantielle Erfassung von Daten und einen dazugehörigen Entwicklungsschritt, um das aufgezeichnete Video mithilfe eines von Arm zur Verfügung gestellten Python-Scripts mit der Zielklasse zu markieren (die Geste). In diesem Fall zeichnet der Entwickler kurze Videoclips mit verschiedenen Aktionen auf,  z.B. das Betreten oder Verlassen eines Raums, das Zeigen auf eine Leuchte (zum Ein- oder Ausschalten) sowie verschiedene Gesten zum Starten oder Beenden der Musikwiedergabe. Mithilfe des Scripts „classify.py“ können die Bilder angezeigt und die entsprechenden Markierungen (eine der jeweiligen Aktion entsprechende ganze Zahl) eingegeben werden. Nach Beendigung der Markierung sollten etwa 10 % der markierten Daten zum Testen des Modells wie unten beschrieben zurückbehalten werden.

Nachdem nun die Trainingsdaten und der reservierte Testsatz bereitstehen, besteht der nächste Schritt im Erstellen des Modells selbst. Im Gegensatz zu der Anwendung für eine einzelne Geste, in der hauptsächlich ein bereits trainiertes Modell eingesetzt wird, baut diese Anwendung ein vollständiges CNN-Modell auf. In diesem Fall wird mithilfe einer Folge von Keras-Anweisungen ein Modell Schicht für Schicht aufgebaut, wobei Keras-Funktionen eingesetzt werden, die eine 2D-Faltungsschicht (Conv2D), eine Aktivierungsschicht (Activation), eine Pooling-Schicht (MaxPooling2D) usw. aufbauen (Listing 3).

Kopieren
def main():
    if len(argv) != 3 or argv[1] == '--help':
        print("""Usage: train.py TRAIN_DIR VAL_DIR...
Save TRAIN_DIR/model.h5 after training a conv net to distinguish between images in its subdirs.""")
        exit(1)

    train_data_dir = argv[1]
    val_data_dir = argv[2]
    nb_train_samples = len(glob('%s/*/*.png' % train_data_dir))
    nb_classes = len(glob('%s/*/' % train_data_dir))
    batch_size = 100

    model = Sequential()
    model.add(Conv2D(32, (3, 3), input_shape=(128, 128, 3)))
    model.add(Activation('elu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Conv2D(32, (3, 3)))
    model.add(Activation('elu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Conv2D(64, (3, 3)))
    model.add(Activation('elu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Flatten())
    model.add(Dense(64))
    model.add(Activation('elu'))
    model.add(Dropout(0.5))
    model.add(Dense(nb_classes))
    model.add(Activation('softmax'))

    model.compile(loss='categorical_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])

Listing 3: Die Beispielanwendung für mehrfache Klassifizierung von Arm veranschaulicht die Verwendung von Keras-Funktionen zum schichtweisen Aufbau eines Konvolutionsnetzes. (Codequelle: Arm)

Dieses Modell führt auch das Konzept einer Dropout-Schicht (Dropout in Listing 3) ein, die für eine als Regularisierung bezeichnete Optimierung des Modells sorgt. In den Trainingsalgorithmen für ein Modell reduziert ein Regularisierungsfaktor die Tendenz des Modells, die einzelnen Merkmale überzubetonen, die in Modellen zu einer sogenannten Überanpassung führen kann. „Dropout“ erfüllt genau diese Aufgabe, indem eine zufällig bestimmte Anzahl von Neuronen aus der Verarbeitungskette „fallengelassen“ wird (Abbildung 3).

Diagramm eines vollständig verbundenen neuronalen Netzes (oben) und einer weniger dichten Version (unten)

Abbildung 3: Dropout führt in einem tiefen neuronalen Netz eine Regularisierung durch, indem es zufällig ausgewählte Neuronen deaktiviert, wodurch ein vollständig verbundenes neuronales Netz (oben) in eine weniger dichte Version überführt wird (unten). (Bildquelle: University of Toronto)

Nachdem nun das vollständige Modell zur Verfügung steht, können die in TensorFlow und anderen Frameworks vorhandenen herkömmlichen Trainingsmethoden eingesetzt werden. Der eigentliche Trainingsprozess kann Stunden oder Tage dauern, bei sehr komplexen Modellen sogar Wochen. Obwohl die Entwickler zur Beschleunigung des Trainings typischerweise GPUs einsetzen, kann ein Transfer-Lernen wie in den oben behandelten Anwendungen zur Reduzierung der Trainingszeiten beitragen. Die Verwendung eines vollständigen Modells bei dieser Anwendung mit Mehrfach-Klassifizierung führt aber zu einer beträchtlich längeren Trainingsdauer. Arm merkt an, dass die Trainingsdauer für diese Anwendung auf einem Raspberry Pi sehr lange sein könnte. Arm schlägt den Entwicklern daher vor, TensorFlow auf ihrer Workstation zu installieren, das Training auf der Workstation durchzuführen und das trainierte Modell auf den Raspberry Pi zurückzukopieren.

Bei diesem Modell wird der Inferenzprozess im Wesentlichen mit derselben Folge von Aufrufen durchgeführt wie bei der Anwendung für eine einzelne Geste. Bei dieser Anwendung wird für den Inferenzprozess natürlich das in Listing 3 beschriebene spezielle Modell und nicht das in Listing 1 beschriebene vortrainierte Modell mit flacher Klassifizierung für die einzelne Geste verwendet. Außer diesem offensichtlichen Unterschied ist für die Entwicklung des Modells mit Mehrfach-Klassifizierung vor dem Produktionsbetrieb ein zusätzlicher Schritt zum Testen des Modells erforderlich.

In dieser Testphase wird das Modell wie für eine Inferenz im Produktionsbetrieb ausgeführt, aber statt neuer Eingabedaten werden die 10 % vom Training zurückbehaltenen markierten Daten verwendet. Da die korrekte Antwort der Inferenz für jedes Testbild bereits bekannt ist, kann die wirkliche Genauigkeit mithilfe eines Test-Scripts mit der am Ende des Trainings erreichten Genauigkeit verglichen werden.

Neben seines grundlegenden Vorzugs als harte Messgröße kann die Testphase und die Art, wie das Modell auf Testdaten reagiert, Hinweise darauf geben, welche Schritte zur Verbesserung des Modells erforderlich sein könnten. Da die Testdaten den im Training verwendeten Datentyp entsprechen, sollte ein trainiertes Modell Vorhersagen mit derselben Genauigkeit wie im Training selbst generieren. Sollte das Modell zu Überanpassung neigen, kann die Anzahl der Eingabemerkmale reduziert und eine robustere Regularisierungsmethode verwendet werden. Bei Modellen mit Unteranpassung können dagegen weitere Merkmale hinzugefügt und die Regularisierung gelockert werden.

Anwendungen auf MCU-Basis

TensorFlow und andere Frameworks bieten jeweils einen konsistenten Ansatz für die Modellentwicklung, der über eine große Zahl von Hardwareplattformen verfolgt werden kann. Dies sind aber keineswegs die einzig möglichen Ansätze. Bei der Entwicklung von Anwendungen für maschinelles Lernen auf Arm-Plattformen können die firmeneigenen Bibliotheken eingesetzt werden.

Die zur Unterstützung von MCUs der Arm-Cortex-A-Serie entwickelte „Arm Compute Library“ bietet einen kompletten Funktionssatz zur Implementierung von CNNs und anderen Algorithmen für maschinelles Lernen. Der Arm-CMSIS (CMSIS: Cortex Microcontroller Software Interface Standard) umfasst eine Bibliothek für neuronale Netze (NN) für MCUs der Arm-Cortex-M-Serie. Ähnlich wie CMSIS-DSP eine Erweiterung von CMSIS für DSP-Anwendungen ist, bietet CMSIS-NN Funktionen für maschinelles Lernen zur Implementierung von beliebten NN-Architekturen auf Arm-Cortex-M-basierten Plattformen. Beispielsweise kann die CMSIS-NN-Bibliothek zur Implementierung von CNNs auf der Entwicklungsplatine NUCLEO-F746ZG von STMicroelectronics verwendet werden, die auf der Arm-Cortex-M7-basierten MCU STM32F746ZG von STMicroelectronics aufbaut.

Zur Implementierung eines neuronalen Netzes mit CMSIS-NN kann ein vorhandenes Modell aus TensorFlow oder einem anderen Framework importiert werden. Als Alternative kann ein CNN nativ durch eine Folge von CMSIS-NN-Funktionsaufrufen implementiert werden. Beispielsweise könnte für eine Implementierung eines CNN, das den standardisierten CIFAR-10-Bilddatensatz mit Markierungen verarbeiten kann, das Modell ähnlich wie bei der oben für Keras vorgeführten Methode Schicht für Schicht aufgebaut werden, In diesem Fall werden die CNN-Schichten durch eine Folge von CMSIS-NN-Funktionsaufrufen implementiert. Das abschließende Softmax-Layer generiert die für CIFAR-10 erforderlichen 10 Ausgabeneuronen (Listing 4).

Kopieren
int main()
{
  .
  .
  .
  // conv1 img_buffer2 -> img_buffer1
  arm_convolve_HWC_q7_RGB(img_buffer2, CONV1_IM_DIM, CONV1_IM_CH, conv1_wt, CONV1_OUT_CH, CONV1_KER_DIM, CONV1_PADDING,
                          CONV1_STRIDE, conv1_bias, CONV1_BIAS_LSHIFT, CONV1_OUT_RSHIFT, img_buffer1, CONV1_OUT_DIM,
                          (q15_t *) col_buffer, NULL);

  arm_relu_q7(img_buffer1, CONV1_OUT_DIM * CONV1_OUT_DIM * CONV1_OUT_CH);

  // pool1 img_buffer1 -> img_buffer2
  arm_maxpool_q7_HWC(img_buffer1, CONV1_OUT_DIM, CONV1_OUT_CH, POOL1_KER_DIM,
                     POOL1_PADDING, POOL1_STRIDE, POOL1_OUT_DIM, NULL, img_buffer2);

  // conv2 img_buffer2 -> img_buffer1
  arm_convolve_HWC_q7_fast(img_buffer2, CONV2_IM_DIM, CONV2_IM_CH, conv2_wt, CONV2_OUT_CH, CONV2_KER_DIM,
                           CONV2_PADDING, CONV2_STRIDE, conv2_bias, CONV2_BIAS_LSHIFT, CONV2_OUT_RSHIFT, img_buffer1,
                           CONV2_OUT_DIM, (q15_t *) col_buffer, NULL);

  arm_relu_q7(img_buffer1, CONV2_OUT_DIM * CONV2_OUT_DIM * CONV2_OUT_CH);

  // pool2 img_buffer1 -> img_buffer2
  arm_maxpool_q7_HWC(img_buffer1, CONV2_OUT_DIM, CONV2_OUT_CH, POOL2_KER_DIM,
                     POOL2_PADDING, POOL2_STRIDE, POOL2_OUT_DIM, col_buffer, img_buffer2);

// conv3 img_buffer2 -> img_buffer1
  arm_convolve_HWC_q7_fast(img_buffer2, CONV3_IM_DIM, CONV3_IM_CH, conv3_wt, CONV3_OUT_CH, CONV3_KER_DIM,
                           CONV3_PADDING, CONV3_STRIDE, conv3_bias, CONV3_BIAS_LSHIFT, CONV3_OUT_RSHIFT, img_buffer1,
                           CONV3_OUT_DIM, (q15_t *) col_buffer, NULL);

  arm_relu_q7(img_buffer1, CONV3_OUT_DIM * CONV3_OUT_DIM * CONV3_OUT_CH);

  // pool3 img_buffer-> img_buffer2
  arm_maxpool_q7_HWC(img_buffer1, CONV3_OUT_DIM, CONV3_OUT_CH, POOL3_KER_DIM,
                     POOL3_PADDING, POOL3_STRIDE, POOL3_OUT_DIM, col_buffer, img_buffer2);

  arm_fully_connected_q7_opt(img_buffer2, ip1_wt, IP1_DIM, IP1_OUT, IP1_BIAS_LSHIFT, IP1_OUT_RSHIFT, ip1_bias,
                             output_data, (q15_t *) img_buffer1);

  arm_softmax_q7(output_data, 10, output_data);

  for (int i = 0; i < 10; i++)
  {
      printf("%d: %d\n", i, output_data[i]);
  }

  return 0;
}

Listing 4: In diesem Codebeispiel aus der CIFAR-10-Beispielanwendung von Arm veranschaulicht die Hauptroutine die zum Aufbau eines CNN für CIFAR10 mithilfe der CMSIS-NN Bibliothek verwendete Folge von Aufrufen. (Codequelle: Arm)

Universellen Plattformen mangelt es an Ressourcen für die Inferenzleistung, die mit Systemen auf GPU-Basis möglich ist. Daher können diese Plattformen normalerweise keine „Echtzeit“-Inferenz von Videodaten liefern, die mit den für eine flackerfreie Darstellung üblichen Bildfrequenzen arbeitet. Trotzdem kann das oben beschriebene CMSIS-NN CIFAR-10 Inferenzraten von etwa 10 pro Sekunde liefen, was für relativ einfache Anwendungen mit nicht zu hohen Aktualisierungsraten ausreichen sollte.

Die fortlaufende Entwicklung von reduzierten Modellen wie MobileNet und Frameworks wie TensorFlow Lite und Caffe2Go von Facebook bieten weitere Optionen für die Implementierung von maschinellem Lernen auf Geräten mit beschränkten Ressourcen für das Internet der Dinge (Internet of Things, IoT) und für andere vernetzte Anwendungen.

Zusammenfassung

Anwendungen für maschinelles Lernen folgen einem typischen Entwicklungsmuster mit Datenvorbereitung und Training, das über verschiedene Zielplattformen hinweg konzeptionell unverändert bleibt. Daher sind die Entwickler in der Lage, mit kostengünstigen Entwicklungsboards in kurzer Zeit Erfahrungen mit der Implementierung von Algorithmen für maschinelles Lernen zu sammeln.

Da Bibliotheken und Frameworks für maschinelles Lernen zu Verfügung stehen, die für diese Boards optimiert sind, können auf Boards wie dem Raspberry Pi 3 oder dem NUCLEO-F746ZG von STMicroelectronics effektive Inferenz-Engines für maschinelles Lernen implementiert werden, die ansprechende Ergebnisse für Anwendungen mit bescheidenen Anforderungen liefern können.

 
DigiKey logo

Haftungsausschluss: Die Meinungen, Überzeugungen und Standpunkte der verschiedenen Autoren und/oder Forumsteilnehmer dieser Website spiegeln nicht notwendigerweise die Meinungen, Überzeugungen und Standpunkte der DigiKey oder offiziellen Politik der DigiKey wider.

Über den Autor

Image of Stephen Evanczuk

Stephen Evanczuk

Stephen Evanczuk hat mehr als 20 Jahre Erfahrung im Schreiben für und über die Elektronikindustrie zu einem breiten Spektrum von Themen wie Hardware, Software, Systeme und Anwendungen einschließlich des IoT. Er promoviertein Neurowissenschaften über neuronale Netzwerke und arbeitete in der Luft- und Raumfahrtindustrie an massiv verteilten sicheren Systemen und Methoden zur Beschleunigung von Algorithmen. Derzeit, wenn er nicht gerade Artikel über Technologie und Ingenieurwesen schreibt, arbeitet er an Anwendungen des tiefen Lernens (Deep Learning) zu Erkennungs- und Empfehlungssystemen.

Über den Verlag

Nordamerikanische Fachredakteure von DigiKey