Fortschritte auf dem Gebiet der neuronalen Netze im Allgemeinen und der Mustererkennung im Besonderen haben dazu geführt, dass es den Anschein hat, als sei die Erstellung einer neuronalen Netzanwendung für die Arbeit mit Bildern eine Routineaufgabe. In gewissem Sinne ist es so - wenn Sie auf eine Idee zur Mustererkennung gekommen sind, zweifeln Sie nicht daran, dass jemand bereits so etwas geschrieben hat. Sie müssen lediglich den entsprechenden Code in Google finden und vom Autor „kompilieren“.
Es gibt jedoch immer noch zahlreiche Details, die die Aufgabe nicht so sehr unlösbar machen, sondern ... langweilig, würde ich sagen. Es dauert zu lange, besonders wenn Sie ein Anfänger sind, der Schritt für Schritt Führung benötigt, ein Projekt, das direkt vor Ihren Augen durchgeführt und von Anfang bis Ende abgeschlossen wird. Ohne das in solchen Fällen übliche Ausreden „diesen offensichtlichen Teil überspringen“.
In diesem Artikel werden wir uns mit der Aufgabe befassen, einen Hunderassen-Identifikator zu erstellen: Wir werden ein neuronales Netzwerk erstellen und trainieren, es dann auf Java für Android portieren und auf Google Play veröffentlichen.
Wenn Sie sich das fertige Ergebnis ansehen möchten, finden Sie es hier:
NeuroDog App bei Google Play.
Website mit meiner Robotik (in Bearbeitung):
robotics.snowcron.com .
Website mit dem Programm selbst, einschließlich eines Handbuchs:
NeuroDog User Guide .
Und hier ist ein Screenshot des Programms:

Erklärung des Problems
Wir werden Keras verwenden: eine Google-Bibliothek für die Arbeit mit neuronalen Netzen. Dies ist eine Bibliothek auf hoher Ebene, was bedeutet, dass sie im Vergleich zu den mir bekannten Alternativen einfacher zu verwenden ist. Wenn überhaupt - es gibt viele Lehrbücher über Keras im Netzwerk von hoher Qualität.
Wir werden CNN - Convolutional Neural Networks verwenden. CNN (und darauf basierende erweiterte Konfigurationen) sind der De-facto-Standard bei der Bilderkennung. Gleichzeitig ist das Trainieren eines solchen Netzwerks nicht immer einfach: Sie müssen die richtige Netzwerkstruktur und Trainingsparameter auswählen (all diese Lernraten, Impulse, L1 und L2 usw.). Die Aufgabe erfordert erhebliche Rechenressourcen. Um sie zu lösen, schlagen ALLE Parameter fehl.
Dies ist einer von mehreren Gründen, warum sie in den meisten Fällen das sogenannte „Transferwissen“ anstelle des sogenannten „Vanille“ -Ansatzes verwenden. Transfer Knowlege verwendet ein neuronales Netzwerk, das von jemandem vor uns (z. B. Google) trainiert wurde, und normalerweise für eine ähnliche, aber immer noch andere Aufgabe. Wir nehmen die ersten Ebenen daraus, ersetzen die letzten Ebenen durch unseren eigenen Klassifikator - und es funktioniert und es funktioniert großartig.
Ein solches Ergebnis mag zunächst überraschend sein: Wie kommt es, dass wir ein Google-Netzwerk genutzt haben, um Katzen von Stühlen zu unterscheiden, und das Hunderassen für uns erkennt? Um zu verstehen, wie dies geschieht, müssen Sie die Grundprinzipien der Arbeit von Deep Neural Networks verstehen, einschließlich derer, die für die Mustererkennung verwendet werden.
Wir haben dem Netzwerk ein Bild (also ein Array von Zahlen) als Eingabe „zugeführt“. Die erste Ebene analysiert das Bild auf einfache Muster wie „horizontale Linie“, „Bogen“ usw. Die nächste Schicht empfängt diese Muster als Eingabe und erzeugt Muster zweiter Ordnung wie „Fell“, „Augenwinkel“ ... Letztendlich erhalten wir ein Puzzle, aus dem wir den Hund rekonstruieren können: Wolle, zwei Augen und eine menschliche Hand in Zähnen.
All dies wurde mit Hilfe von vorgefertigten Schichten durchgeführt, die von uns (zum Beispiel von Google) erhalten wurden. Als nächstes fügen wir unsere Ebenen hinzu und bringen ihnen bei, Rasseninformationen aus diesen Mustern zu extrahieren. Klingt logisch.
Zusammenfassend werden wir in diesem Artikel sowohl "Vanilla" CNN als auch verschiedene "Transfer Learning" -Varianten verschiedener Netzwerktypen erstellen. Was "Vanille" betrifft: Ich werde es erstellen, aber ich habe nicht vor, es durch Auswahl von Parametern zu konfigurieren, da es viel einfacher ist, "vorab trainierte" Netzwerke zu trainieren und zu konfigurieren.
Da wir unserem neuronalen Netzwerk beibringen wollen, Hunderassen zu erkennen, müssen wir ihm Proben verschiedener Rassen „zeigen“. Glücklicherweise gibt es hier eine Reihe von Fotos, die für eine ähnliche Aufgabe erstellt wurden (das
Original ist hier ).
Dann plane ich, das beste der empfangenen Netzwerke für Android zu portieren. Das Portieren von Kerasov-Netzwerken auf Android ist relativ einfach, gut formalisiert und wir werden alle erforderlichen Schritte ausführen, sodass es nicht schwierig sein wird, diesen Teil zu reproduzieren.
Dann werden wir all dies auf Google Play veröffentlichen. Natürlich wird Google Widerstand leisten, daher werden zusätzliche Tricks verwendet. Beispielsweise ist die Größe unserer Anwendung (aufgrund eines sperrigen neuronalen Netzwerks) größer als die zulässige Größe der von Google Play akzeptierten Android-APK: Wir müssen Bundles verwenden. Darüber hinaus zeigt Google unsere Anwendung nicht in den Suchergebnissen an. Dies kann behoben werden, indem Such-Tags in der Anwendung registriert werden, oder warten Sie einfach ... ein oder zwei Wochen.
Als Ergebnis erhalten wir eine voll funktionsfähige "kommerzielle" (in Anführungszeichen, wie es kostenlos ausgelegt ist) Anwendung für Android und unter Verwendung neuronaler Netze.
Entwicklungsumgebung
Sie können für Keras auf verschiedene Arten programmieren, abhängig vom verwendeten Betriebssystem (Ubuntu empfohlen), dem Vorhandensein oder Fehlen einer Grafikkarte usw. Es gibt nichts Schlechtes an der Entwicklung auf dem lokalen Computer (und entsprechend seiner Konfiguration), außer dass dies nicht der einfachste Weg ist.
Die Installation und Konfiguration einer großen Anzahl von Tools und Bibliotheken nimmt zunächst Zeit in Anspruch. Wenn neue Versionen veröffentlicht werden, müssen Sie erneut Zeit aufwenden. Zweitens erfordern neuronale Netze eine große Rechenleistung für das Training. Sie können diesen Vorgang beschleunigen (um das 10-fache oder mehr), wenn Sie eine GPU verwenden. Zum Zeitpunkt des Schreibens dieses Artikels kosten die für diese Arbeit am besten geeigneten GPUs 2.000 bis 7.000 US-Dollar. Und ja, sie müssen auch konfiguriert werden.
Also werden wir den anderen Weg gehen. Tatsache ist, dass Google armen Igeln wie uns erlaubt, GPUs aus ihrem Cluster zu verwenden - kostenlos, für Berechnungen in Bezug auf neuronale Netze, bietet es auch eine vollständig konfigurierte Umgebung, alles zusammen wird dies als Google Colab bezeichnet. Der Dienst bietet Ihnen Zugriff auf Jupiter Notebook mit Python, Keras und einer Vielzahl anderer bereits konfigurierter Bibliotheken. Sie müssen lediglich ein Google-Konto einrichten (ein Google Mail-Konto einrichten, damit Sie auf alles andere zugreifen können).
Im Moment kann Colab hier eingestellt
werden , aber wenn Sie Google kennen, kann sich dies jederzeit ändern. Google einfach Google Colab.
Das offensichtliche Problem bei der Verwendung von Colab ist, dass es sich um einen WEB-Dienst handelt. Wie greifen wir auf unsere Daten zu? Speichern Sie das neuronale Netzwerk nach dem Training, laden Sie beispielsweise für unsere Aufgabe spezifische Daten herunter und so weiter?
Es gibt verschiedene (zum Zeitpunkt des Schreibens dieses Artikels - drei) verschiedene Möglichkeiten, wir verwenden die, die ich für am bequemsten halte - wir verwenden Google Drive.
Google Drive ist ein Cloud-basierter Datenspeicher, der ähnlich wie eine normale Festplatte funktioniert und auf Google Colab abgebildet werden kann (siehe Code unten). Danach können Sie damit arbeiten, als würden Sie mit Dateien auf einer lokalen Festplatte arbeiten. Das heißt, um auf die Fotos von Hunden zuzugreifen, um unser neuronales Netzwerk zu trainieren, müssen wir sie auf Google Drive hochladen, das ist alles.
Erstellen und Trainieren eines neuronalen Netzwerks
Unten gebe ich den Code in Python Block für Block (aus dem Jupiter-Notizbuch). Sie können diesen Code in Ihr Jupiter-Notizbuch kopieren und auch Block für Block ausführen, da Blöcke unabhängig voneinander ausgeführt werden können (natürlich können im späten Block definierte Variablen im späten Block erforderlich sein, dies ist jedoch eine offensichtliche Abhängigkeit).
Initialisierung
Lassen Sie uns zunächst Google Drive einbinden. Nur zwei Zeilen. Dieser Code sollte nur einmal in einer Colab-Sitzung ausgeführt werden (z. B. alle 6 Stunden). Wenn Sie es ein zweites Mal aufrufen, während die Sitzung noch "aktiv" ist, wird es übersprungen, da das Laufwerk bereits bereitgestellt ist.
from google.colab import drive drive.mount('/content/drive/')
Beim ersten Start werden Sie gebeten, Ihre Absichten zu bestätigen, es gibt nichts Kompliziertes. So sieht es aus:
>>> Go to this URL in a browser: ... >>> Enter your authorization code: >>> ·········· >>> Mounted at /content/drive/
Ein vollständig standardmäßiger
Include- Abschnitt; Es ist möglich, dass einige der enthaltenen Dateien nicht benötigt werden. Da ich verschiedene neuronale Netze testen werde, müssen Sie einige der enthaltenen Module für bestimmte Arten von neuronalen Netzen kommentieren / auskommentieren: Um beispielsweise InceptionV3 NN zu verwenden, die Aufnahme von InceptionV3 auskommentieren und beispielsweise ResNet50 auskommentieren. Oder auch nicht: Alles, was sich daraus ändert, ist die Größe des verwendeten Speichers, und das ist nicht sehr stark.
import datetime as dt import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from tqdm import tqdm import cv2 import numpy as np import os import sys import random import warnings from sklearn.model_selection import train_test_split import keras from keras import backend as K from keras import regularizers from keras.models import Sequential from keras.models import Model from keras.layers import Dense, Dropout, Activation from keras.layers import Flatten, Conv2D from keras.layers import MaxPooling2D from keras.layers import BatchNormalization, Input from keras.layers import Dropout, GlobalAveragePooling2D from keras.callbacks import Callback, EarlyStopping from keras.callbacks import ReduceLROnPlateau from keras.callbacks import ModelCheckpoint import shutil from keras.applications.vgg16 import preprocess_input from keras.preprocessing import image from keras.preprocessing.image import ImageDataGenerator from keras.models import load_model from keras.applications.resnet50 import ResNet50 from keras.applications.resnet50 import preprocess_input from keras.applications.resnet50 import decode_predictions from keras.applications import inception_v3 from keras.applications.inception_v3 import InceptionV3 from keras.applications.inception_v3 import preprocess_input as inception_v3_preprocessor from keras.applications.mobilenetv2 import MobileNetV2 from keras.applications.nasnet import NASNetMobile
In Google Drive erstellen wir einen Ordner für unsere Dateien. Die zweite Zeile zeigt den Inhalt an:
working_path = "/content/drive/My Drive/DeepDogBreed/data/" !ls "/content/drive/My Drive/DeepDogBreed/data" >>> all_images labels.csv models test train valid
Wie Sie sehen können, werden die Fotos der Hunde (kopiert aus dem Stanford-Datensatz (siehe oben) in Google Drive) zuerst im Ordner
all_images gespeichert . Später werden wir sie in die
Zug-, Gültigkeits- und
Testverzeichnisse kopieren. Wir speichern trainierte Modelle im Modellordner. Die Datei labels.csv ist Teil des Datensatzes mit Fotos und enthält eine Korrespondenztabelle mit den Namen von Bildern und Hunderassen.
Es gibt viele Tests, die Sie ausführen können, um zu verstehen, was genau wir für die vorübergehende Verwendung von Google erhalten haben. Zum Beispiel:
Wie Sie sehen, ist die GPU wirklich verbunden. Wenn nicht, müssen Sie diese Option in den Jupiter Notebook-Einstellungen suchen und aktivieren.
Als nächstes müssen wir einige Konstanten deklarieren, wie z. B. die Größe von Bildern usw. Wir werden Bilder mit einer Größe von 256 x 256 Pixel verwenden. Dies ist ein Bild, das groß genug ist, um keine Details zu verlieren, und klein genug, damit alles in den Speicher passt. Beachten Sie jedoch, dass einige Arten von neuronalen Netzen, die wir verwenden werden, Bilder mit 224 x 224 Pixel erwarten. In solchen Fällen kommentieren wir 256 und kommentieren 224 aus.
Der gleiche Ansatz (Kommentar eins - Kommentar) wird auf die Namen der Modelle angewendet, die wir speichern, einfach weil wir keine Dateien überschreiben möchten, die möglicherweise noch nützlich sind.
warnings.filterwarnings("ignore") os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' np.random.seed(7) start = dt.datetime.now() BATCH_SIZE = 16 EPOCHS = 15 TESTING_SPLIT=0.3
Laden von Daten
Laden Sie zunächst die Datei
labels.csv hoch und
teilen Sie sie in die Teile Training und Validierung auf. Beachten Sie, dass es noch keinen Testteil gibt, da ich schummeln werde, um mehr Trainingsdaten zu erhalten.
labels = pd.read_csv(working_path + 'labels.csv') print(labels.head()) train_ids, valid_ids = train_test_split(labels, test_size = TESTING_SPLIT) print(len(train_ids), 'train ids', len(valid_ids), 'validation ids') print('Total', len(labels), 'testing images') >>> id breed >>> 0 000bec180eb18c7604dcecc8fe0dba07 boston_bull >>> 1 001513dfcb2ffafc82cccf4d8bbaba97 dingo >>> 2 001cdf01b096e06d78e9e5112d419397 pekinese >>> 3 00214f311d5d2247d5dfe4fe24b2303d bluetick >>> 4 0021f9ceb3235effd7fcde7f7538ed62 golden_retriever >>> 7155 train ids 3067 validation ids >>> Total 10222 testing images
Kopieren Sie anschließend die Bilddateien entsprechend den Dateinamen in die Ordner für Schulung, Validierung und Test. Die folgende Funktion kopiert die Dateien, deren Namen wir in den angegebenen Ordner übertragen.
def copyFileSet(strDirFrom, strDirTo, arrFileNames): arrBreeds = np.asarray(arrFileNames['breed']) arrFileNames = np.asarray(arrFileNames['id']) if not os.path.exists(strDirTo): os.makedirs(strDirTo) for i in tqdm(range(len(arrFileNames))): strFileNameFrom = strDirFrom + arrFileNames[i] + ".jpg" strFileNameTo = strDirTo + arrBreeds[i] + "/" + arrFileNames[i] + ".jpg" if not os.path.exists(strDirTo + arrBreeds[i] + "/"): os.makedirs(strDirTo + arrBreeds[i] + "/")
Wie Sie sehen können, kopieren wir nur eine Datei für jede Hunderasse als
Test . Außerdem erstellen wir beim Kopieren Unterordner, einen für jede Rasse. Dementsprechend werden Fotos nach Rasse in Unterordner kopiert.
Dies geschieht, weil Keras mit einem Verzeichnis ähnlicher Struktur arbeiten kann und Bilddateien nach Bedarf und nicht alle gleichzeitig lädt, wodurch Speicherplatz gespart wird. Es ist eine schlechte Idee, alle 15.000 Bilder gleichzeitig hochzuladen.
Wir müssen diese Funktion nur einmal aufrufen, da sie Bilder kopiert - und nicht mehr benötigt wird. Dementsprechend müssen wir es für die zukünftige Verwendung kommentieren:
Holen Sie sich eine Liste der Hunderassen:
breeds = np.unique(labels['breed']) map_characters = {}
Bildverarbeitung
Wir werden die Keras-Bibliotheksfunktion namens ImageDataGenerators verwenden. ImageDataGenerator kann das Bild verarbeiten, skalieren, drehen usw. Es kann auch eine
Verarbeitungsfunktion akzeptieren, die Bilder zusätzlich verarbeiten kann.
def preprocess(img): img = cv2.resize(img, (IMAGE_SIZE, IMAGE_SIZE), interpolation = cv2.INTER_AREA)
Beachten Sie den folgenden Code:
Wir können in ImageDataGenerator selbst normalisieren (Unterdaten im Bereich von 0-1 anstelle der ursprünglichen 0-255). Warum brauchen wir dann einen Präprozessor? Betrachten Sie als Beispiel den unscharfen Aufruf (auskommentiert, ich verwende ihn nicht): Dies ist dieselbe benutzerdefinierte Bildmanipulation, die beliebig sein kann. Alles von Kontrast zu HDR.
Wir werden zwei verschiedene ImageDataGenerators verwenden, einen für das Training und einen für die Validierung. Der Unterschied besteht darin, dass wir für das Training Drehungen und Skalierungen benötigen, um die „Vielfalt“ der Daten zu erhöhen. Für die Validierung benötigen wir sie jedoch nicht, zumindest nicht für diese Aufgabe.
train_datagen = ImageDataGenerator( preprocessing_function=preprocess,
Erstellen eines neuronalen Netzwerks
Wie bereits erwähnt, werden wir verschiedene Arten von neuronalen Netzen erstellen. Jedes Mal, wenn wir eine andere Funktion zum Erstellen aufrufen, schließen Sie andere Dateien ein und bestimmen manchmal eine andere Bildgröße. Um zwischen verschiedenen Arten von neuronalen Netzen zu wechseln, müssen wir den entsprechenden Code kommentieren / auskommentieren.
Erstellen Sie zunächst ein "Vanille" CNN. Es funktioniert nicht gut, weil ich beschlossen habe, keine Zeit mit dem Debuggen zu verschwenden, aber es bietet zumindest eine Grundlage, die entwickelt werden kann, wenn ein Wunsch besteht (normalerweise ist dies eine schlechte Idee, da vorab trainierte Netzwerke das beste Ergebnis liefern).
def createModelVanilla(): model = Sequential()
Wenn wir mithilfe von
Transferlernen Netzwerke erstellen, ändert sich das Verfahren:
def createModelMobileNetV2():
Das Erstellen anderer Netzwerktypen erfolgt nach demselben Muster:
def createModelResNet50(): base_model = ResNet50(weights='imagenet', include_top=False, pooling='avg', input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3)) x = base_model.output x = Dense(512)(x) x = Activation('relu')(x) x = Dropout(0.5)(x) predictions = Dense(NUM_CLASSES, activation='softmax')(x) model = Model(inputs=base_model.input, outputs=predictions)
Achtung: Gewinner! Dieser NN zeigte das beste Ergebnis:
def createModelInceptionV3():
Noch eine:
def createModelNASNetMobile():
Verschiedene Arten von neuronalen Netzen können für verschiedene Aufgaben verwendet werden. Zusätzlich zu den Anforderungen an die Vorhersagegenauigkeit kann die Größe eine Rolle spielen (das mobile NN ist fünfmal kleiner als Inception) und die Geschwindigkeit (wenn wir eine Echtzeitverarbeitung eines Videostreams benötigen, muss die Genauigkeit geopfert werden).
Neuronales Netzwerktraining
Zunächst
experimentieren wir, damit wir die gespeicherten neuronalen Netze entfernen können, die wir jedoch nicht mehr verwenden. Die folgende Funktion entfernt NN, falls vorhanden:
Die Art und Weise, wie wir neuronale Netze erstellen und löschen, ist recht einfach und unkompliziert. Löschen Sie zuerst. Wenn Sie
delete (nur) aufrufen, sollten Sie berücksichtigen, dass das Jupiter-Notizbuch über eine Funktion zum Ausführen der Auswahl verfügt. Wählen Sie nur das aus, was Sie verwenden möchten, und führen Sie es aus.
Dann erstellen wir ein neuronales Netzwerk, wenn seine Datei nicht vorhanden war, oder rufen das
Laden auf, wenn es vorhanden ist: Natürlich können wir nicht "delete" aufrufen und dann erwarten, dass NN vorhanden ist. Um also ein gespeichertes neuronales Netzwerk zu verwenden, rufen Sie nicht
delete auf .
Mit anderen Worten, wir können je nach Situation und dem, mit dem wir gerade experimentieren, ein neues NN erstellen oder das vorhandene verwenden. Ein einfaches Szenario: Wir haben ein neuronales Netzwerk trainiert und sind dann in den Urlaub gefahren. Sie sind zurückgekehrt, und Google hat die Sitzung festgenagelt. Daher müssen wir die zuvor gespeicherte laden: Kommentieren Sie "Löschen" aus und kommentieren Sie "Laden" aus.
deleteSavedNet(working_path + strModelFileName)
Checkpoints sind ein sehr wichtiges Element unseres Programms. Wir können eine Reihe von Funktionen erstellen, die am Ende jeder Trainingsära aufgerufen werden sollen, und diese an den Checkpoint übergeben. Sie können beispielsweise ein neuronales Netzwerk speichern,
wenn Ergebnisse angezeigt werden, die besser sind als die bereits gespeicherten.
checkpoint = ModelCheckpoint(working_path + strModelFileName, monitor='val_acc', verbose=1, save_best_only=True, mode='auto', save_weights_only=False) callbacks_list = [ checkpoint ]
Schließlich unterrichten wir das neuronale Netzwerk auf dem Trainingsset:
Die Diagramme für Genauigkeit und Verlust für die besten Konfigurationen lauten wie folgt:


Wie Sie sehen können, lernt das neuronale Netzwerk und ist nicht schlecht.
Testen neuronaler Netze
Nach Abschluss des Trainings müssen wir das Ergebnis testen. Dafür präsentiert NN Bilder, die sie noch nie gesehen hat - die, die wir in den Testordner kopiert haben - eines für jede Hunderasse.
Exportieren Sie ein neuronales Netzwerk in eine Java-Anwendung
Zunächst müssen wir das Laden des neuronalen Netzwerks von der Festplatte organisieren. Der Grund ist klar: Der Export findet in einem anderen Codeblock statt, daher werden wir den Export höchstwahrscheinlich separat starten - wenn das neuronale Netzwerk in seinen optimalen Zustand gebracht wird. Das heißt, unmittelbar vor dem Export werden wir im selben Programmlauf das Netzwerk nicht trainieren. Wenn Sie den hier gezeigten Code verwenden, gibt es keinen Unterschied, das optimale Netzwerk wurde für Sie ausgewählt. Aber wenn Sie etwas Eigenes lernen, ist es Zeitverschwendung, alles vor dem Speichern neu zu trainieren, wenn Sie zuvor alles gespeichert haben.
Aus dem gleichen Grund - um nicht über den Code zu springen - füge ich hier die für den Export erforderlichen Dateien ein. Niemand stört Sie, sie an den Anfang des Programms zu verschieben, wenn Ihr Sinn für Schönheit dies erfordert:
from keras.models import Model from keras.models import load_model from keras.layers import * import os import sys import tensorflow as tf
Ein kleiner Test nach dem Laden eines neuronalen Netzwerks, um sicherzustellen, dass alles geladen ist - funktioniert:
img = image.load_img(working_path + "test/affenpinscher.jpg")

Als nächstes müssen wir die Namen der Eingabe- und Ausgabeschichten des Netzwerks abrufen (entweder diese oder die Erstellungsfunktion, wir müssen die Schichten explizit "benennen", was wir nicht getan haben).
model.summary() >>> Layer (type) >>> ====================== >>> input_7 (InputLayer) >>> ______________________ >>> conv2d_283 (Conv2D) >>> ______________________ >>> ... >>> dense_14 (Dense) >>> ====================== >>> Total params: 22,913,432 >>> Trainable params: 1,110,648 >>> Non-trainable params: 21,802,784
Wir werden die Namen der Eingabe- und Ausgabeebenen später verwenden, wenn wir das neuronale Netzwerk in eine Java-Anwendung importieren.
Ein weiterer Code, der das Netzwerk durchstreift, um diese Daten abzurufen:
def print_graph_nodes(filename): g = tf.GraphDef() g.ParseFromString(open(filename, 'rb').read()) print() print(filename) print("=======================INPUT===================") print([n for n in g.node if n.name.find('input') != -1]) print("=======================OUTPUT==================") print([n for n in g.node if n.name.find('output') != -1]) print("===================KERAS_LEARNING==============") print([n for n in g.node if n.name.find('keras_learning_phase') != -1]) print("===============================================") print()
Aber ich mag ihn nicht und ich empfehle ihn nicht.Der folgende Code exportiert das Keras Neural Network in das pb- Format, das wir von Android erfassen werden. def keras_to_tensorflow(keras_model, output_dir, model_name,out_prefix="output_", log_tensorboard=True): if os.path.exists(output_dir) == False: os.mkdir(output_dir) out_nodes = [] for i in range(len(keras_model.outputs)): out_nodes.append(out_prefix + str(i + 1)) tf.identity(keras_model.output[i], out_prefix + str(i + 1)) sess = K.get_session() from tensorflow.python.framework import graph_util from tensorflow.python.framework graph_io init_graph = sess.graph.as_graph_def() main_graph = graph_util.convert_variables_to_constants( sess, init_graph, out_nodes) graph_io.write_graph(main_graph, output_dir, name=model_name, as_text=False) if log_tensorboard: from tensorflow.python.tools import import_pb_to_tensorboard import_pb_to_tensorboard.import_to_tensorboard( os.path.join(output_dir, model_name), output_dir)
Aufruf dieser Funktionen zum Exportieren eines neuronalen Netzwerks:
model = load_model(working_path + strModelFileName) keras_to_tensorflow(model, output_dir=working_path + strModelFileName, model_name=working_path + "models/dogs.pb") print_graph_nodes(working_path + "models/dogs.pb")
Die letzte Zeile gibt die Struktur des resultierenden neuronalen Netzwerks aus.Erstellen einer Android-Anwendung mithilfe eines neuronalen Netzwerks
Der Export neuronaler Netze in Android ist gut formalisiert und sollte keine Schwierigkeiten verursachen. Es gibt wie immer verschiedene Möglichkeiten, die wir (zum Zeitpunkt des Schreibens) am beliebtesten verwenden.Zunächst verwenden wir Android Studio, um ein neues Projekt zu erstellen. Wir werden "Ecken abschneiden", weil unsere Aufgabe kein Android-Tutorial ist. Die Anwendung enthält also nur eine Aktivität.
Wie Sie sehen können, haben wir den Ordner "Assets" hinzugefügt und unser neuronales Netzwerk in dieses kopiert (das zuvor exportierte).Gradle-Datei
. ,
tensorflow-android . , Tensorflow (, , Keras) Java:

:
versionCode versionName . , Google Play. gdadle (, 1 -> 2 -> 3...) , « ».
Zuallererst wird unsere Anwendung "schwer" sein - 100 Mb Neural Network wird leicht in den Speicher moderner Mobiltelefone passen, aber es ist definitiv eine schlechte Idee, für jedes von Facebook "geteilte" Foto eine separate Instanz zu öffnen.Daher verbieten wir, mehr als eine Instanz unserer Anwendung zu erstellen: <activity android:name=".MainActivity" android:launchMode="singleTask">
Durch Hinzufügen von android: launchMode = "singleTask" zu MainActivity weisen wir Android an, eine vorhandene Kopie der Anwendung zu öffnen (zu aktivieren), anstatt eine andere Instanz zu erstellen.Dann müssen wir unsere Anwendung in die Liste aufnehmen, die das System anzeigt, wenn jemand das Bild „teilt“: <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> </intent-filter>
Schließlich müssen wir Funktionen und Berechtigungen anfordern, die unsere Anwendung verwenden wird: <uses-feature android:name="android.hardware.camera" android:required="true" /> <uses-permission android:name= "android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
Wenn Sie mit der Programmierung für Android vertraut sind, sollte dieser Teil keine Fragen aufwerfen.Layout-Anwendung.
Wir werden zwei Layouts erstellen, eines für Hochformat und eines für Querformat. So sieht das Portrait-Layout aus .Was wir hinzufügen werden: ein großes Feld (Ansicht) zum Anzeigen des Bildes, eine nervige Liste von Anzeigen (angezeigt, wenn die Schaltfläche mit einem Knochen gedrückt wird), die Hilfeschaltfläche, Schaltflächen zum Herunterladen eines Bilds aus der Datei / Galerie und zum Aufnehmen von der Kamera und schließlich (zunächst) versteckt) Schaltfläche "Verarbeiten" für die Bildverarbeitung.
Die Aktivität selbst enthält die gesamte Logik zum Ein- und Ausblenden sowie zum Aktivieren / Deaktivieren von Schaltflächen, abhängig vom Status der Anwendung.Hauptaktivität
Diese Aktivität erbt (erweitert) die Standard-Android-Aktivität: public class MainActivity extends Activity
Betrachten Sie den Code, der für den Betrieb des neuronalen Netzwerks verantwortlich ist.Zunächst akzeptiert das neuronale Netzwerk Bitmap. Dies ist zunächst eine große Bitmap (beliebiger Größe) von der Kamera oder aus einer Datei (m_bitmap), dann transformieren wir sie und führen zu den Standardpixeln mit 256 x 256 Pixel (m_bitmapForNn). Wir speichern auch die Bitmap-Größe (256) in einer Konstanten: static Bitmap m_bitmap = null; static Bitmap m_bitmapForNn = null; private int m_nImageSize = 256;
Wir müssen dem neuronalen Netzwerk die Namen der Eingabe- und Ausgabeschichten mitteilen. Wir haben sie früher erhalten (siehe Auflistung), aber denken Sie daran, dass sie in Ihrem Fall unterschiedlich sein können: private String INPUT_NAME = "input_7_1"; private String OUTPUT_NAME = "output_1";
Dann deklarieren wir eine Variable, die das TensofFlow-Objekt enthält. Außerdem speichern wir den Pfad zur neuronalen Netzwerkdatei (die in Assets liegt):private TensorFlowInferenceInterface tf;
private String MODEL_PATH =
"file: ///android_asset/dogs.pb";
Wir speichern die Hunderassen in der Liste, damit sie später dem Benutzer angezeigt werden und nicht die Array-Indizes: private String[] m_arrBreedsArray;
Zunächst haben wir Bitmap heruntergeladen. Das neuronale Netzwerk erwartet jedoch ein Array von RGB-Werten, und seine Ausgabe ist ein Array von Wahrscheinlichkeiten, dass diese Rasse das ist, was im Bild gezeigt wird. Dementsprechend müssen wir zwei weitere Arrays hinzufügen (beachten Sie, dass 120 die Anzahl der Hunderassen ist, die in unseren Trainingsdaten enthalten sind): private float[] m_arrPrediction = new float[120]; private float[] m_arrInput = null;
Tensorflow-Inferenzbibliothek herunterladen: static { System.loadLibrary("tensorflow_inference"); }
Da neuronale Netzwerkoperationen Zeit benötigen, müssen sie in einem separaten Thread ausgeführt werden. Andernfalls besteht die Möglichkeit, dass wir eine Systemmeldung erhalten, dass die Anwendung nicht antwortet, ganz zu schweigen von einem unzufriedenen Benutzer. class PredictionTask extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); }
In onCreate () der MainActivity müssen wir den onClickListener für die Schaltfläche "Process" hinzufügen: m_btn_process.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { processImage(); } });
Hier ruft processImage () nur den oben beschriebenen Thread auf: private void processImage() { try { enableControls(false);
Zusätzliche Hinweise
Wir planen nicht, die Details der UI-Programmierung für Android zu diskutieren, da dies sicherlich nicht für die Portierung neuronaler Netze gilt. Eines ist jedoch noch erwähnenswert.Wenn wir die Erstellung zusätzlicher Instanzen unserer Anwendung verhindert haben, haben wir auch die normale Reihenfolge der Erstellung und Löschung von Aktivitäten (Kontrollfluss) verletzt: Wenn Sie ein Bild von Facebook „freigeben“ und dann ein anderes freigeben, wird die Anwendung nicht neu gestartet. Dies bedeutet, dass die „traditionelle“ Methode zum Abfangen übertragener Daten in onCreate nicht ausreicht, da onCreate nicht aufgerufen wird.So lösen Sie dieses Problem:1. Rufen Sie in onCreate in MainActivity die Funktion onSharedIntent auf: protected void onCreate( Bundle savedInstanceState) { super.onCreate(savedInstanceState); .... onSharedIntent(); ....
Wir fügen auch einen Handler für onNewIntent hinzu: @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); onSharedIntent(); }
Hier ist die onSharedIntent-Funktion selbst: private void onSharedIntent() { Intent receivedIntent = getIntent(); String receivedAction = receivedIntent.getAction(); String receivedType = receivedIntent.getType(); if (receivedAction.equals(Intent.ACTION_SEND)) {
Jetzt verarbeiten wir die übertragenen Daten in onCreate (wenn sich die Anwendung nicht im Speicher befand) oder in onNewIntent (wenn sie früher gestartet wurde).Viel Glück
Wenn Ihnen der Artikel gefallen hat, "mögen" Sie ihn bitte auf alle möglichen Arten. Es gibt auch "soziale" Schaltflächen auf der Website .