Einfache Website zum Klassifizieren von Bildern erstellen

1. Hinweis

In diesem Codelab erfahren Sie, wie Sie mit TensorFlow Serving mit REST und gRPC eine Bildklassifizierungs-Inferenz über eine Website ausführen.

Vorbereitung

  • Grundkenntnisse in der Webentwicklung, z. B. HTML und JavaScript
  • Grundkenntnisse im Bereich maschinelles Lernen mit TensorFlow, z. B. Training und Bereitstellung
  • Grundkenntnisse zu Terminals und Docker

Lerninhalte

  • So finden Sie vortrainierte Bildklassifizierungsmodelle in TensorFlow Hub.
  • Hier erfahren Sie, wie Sie eine einfache Website erstellen und mit dem heruntergeladenen Bildklassifizierungsmodell über TensorFlow Serving (REST und gRPC) Vorhersagen treffen.
  • So rendern Sie das Erkennungsergebnis in der Benutzeroberfläche.

Voraussetzungen

2. Einrichten

So laden Sie den Code für dieses Codelab herunter:

  1. Rufen Sie dieses GitHub-Repository auf.
  2. Klicken Sie auf Code > Download zip, um den gesamten Code für dieses Codelab herunterzuladen.

a72f2bb4caa9a96.png

  1. Entpacken Sie die heruntergeladene ZIP-Datei, um einen codelabs-Stammordner mit allen benötigten Ressourcen zu entpacken.

Für dieses Codelab benötigen Sie nur die Dateien im Unterverzeichnis TFServing/ImageClassificationWeb im Repository, das zwei Ordner enthält:

  • Der Ordner starter enthält den Startcode, auf dem Sie in diesem Codelab aufbauen.
  • Der Ordner finished enthält den vollständigen Code der fertigen Beispiel-App.

3. Abhängigkeiten installieren

So installieren Sie die Abhängigkeiten:

  • Wechseln Sie im Terminal zum Ordner starter und installieren Sie dann die erforderlichen NPM-Pakete:
npm install

4. Starter-Website ausführen

So laden Sie die TFServing/ImageClassificationWeb/starter/dist/index.html-Datei mit Web Server for Chrome:

  1. Geben Sie Chrome://apps/ in die Adressleiste von Chrome ein und suchen Sie in der App-Liste nach Web Server for Chrome.
  2. Starten Sie Web Server for Chrome und wählen Sie den Ordner TFServing/ImageClassificationWeb/starter/dist/ aus.
  3. Klicken Sie auf den Schalter Web Server, um ihn zu aktivieren, und rufen Sie dann in Ihrem Browser http://localhost:8887/ auf.

f7b43cd44ebf1f1b.png

Website ausführen und erkunden

Jetzt sollte die Website angezeigt werden. Die Benutzeroberfläche ist recht einfach: Es gibt ein Katzenbild, das Sie klassifizieren möchten, und der Nutzer kann die Daten über REST oder gRPC an das Backend senden. Das Backend führt eine Bildklassifizierung für das Bild durch und gibt das Klassifizierungsergebnis an die Website zurück, die das Ergebnis anzeigt.

837d97a27c59a0b3.png

Wenn Sie auf Classify (Klassifizieren) klicken, passiert nichts, da noch keine Kommunikation mit dem Backend möglich ist.

5. Bildklassifizierungsmodell mit TensorFlow Serving bereitstellen

Die Bildklassifizierung ist eine sehr häufige ML-Aufgabe, bei der ein Bild anhand des primären Inhalts des Bildes in vordefinierte Kategorien eingeteilt wird. Hier ist ein Beispiel für die Klassifizierung von Blumen:

a6da16b4a7665db0.png

Auf TensorFlow Hub sind eine Reihe vortrainierter Bildklassifizierungsmodelle verfügbar. In diesem Codelab verwenden Sie das beliebte Modell Inception v3.

So stellen Sie das Bildklassifizierungsmodell mit TensorFlow Serving bereit:

  1. Laden Sie die Modelldatei Inception v3 herunter.
  2. Entpacken Sie die heruntergeladene Datei .tar.gz mit einem Dekomprimierungstool wie 7‑Zip.
  3. Erstellen Sie einen inception_v3-Ordner und darin einen 123-Unterordner.
  4. Verschieben Sie den extrahierten Ordner variables und die Datei saved_model.pb in den Unterordner 123.

Sie können auf den Ordner inception_v3 als Ordner SavedModel verweisen. 123 ist eine Beispielversionsnummer. Sie können auch eine andere Nummer auswählen.

Die Ordnerstruktur sollte so aussehen:

21a8675ac8d31907.png

TensorFlow Serving starten

  • Starten Sie TensorFlow Serving mit Docker in Ihrem Terminal, ersetzen Sie dabei PATH/TO/SAVEDMODEL durch den absoluten Pfad des Ordners inception_v3 auf Ihrem Computer.
docker pull tensorflow/serving

docker run -it --rm -p 8500:8500 -p 8501:8501 -v "PATH/TO/SAVEDMODEL:/models/inception" -e MODEL_NAME=inception tensorflow/serving

Docker lädt zuerst automatisch das TensorFlow Serving-Image herunter. Das dauert etwa eine Minute. Danach sollte TensorFlow Serving gestartet werden. Das Log sollte so aussehen:

2022-02-25 06:01:12.513231: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle.
2022-02-25 06:01:12.585012: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 3000000000 Hz
2022-02-25 06:01:13.395083: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/inception/123
2022-02-25 06:01:13.837562: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 1928700 microseconds.
2022-02-25 06:01:13.877848: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/inception/123/assets.extra/tf_serving_warmup_requests
2022-02-25 06:01:13.929844: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: inception version: 123}
2022-02-25 06:01:13.985848: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models
2022-02-25 06:01:13.985987: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled
2022-02-25 06:01:13.988994: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ...
[warn] getaddrinfo: address family for nodename not supported
2022-02-25 06:01:14.033872: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ...
[evhttp_server.cc : 245] NET_LOG: Entering the event loop ...

6. Envoy-Proxy einrichten

Derzeit wird der Access-Control-Allow-Origin-Header von TensorFlow Serving nicht festgelegt. Daher blockiert der Browser die Anfrage vom Frontend-JavaScript an TensorFlow Serving aus Sicherheitsgründen. Um dieses Problem zu umgehen, müssen Sie einen Proxy wie Envoy verwenden, um die Anfrage von JavaScript an das TensorFlow Serving-Backend weiterzuleiten.

Envoy starten

  • Laden Sie in Ihrem Terminal das Envoy-Image herunter und starten Sie Envoy mit Docker. Ersetzen Sie dabei den Platzhalter PATH/TO/ENVOY-CUSTOM.YAML durch den absoluten Pfad der Datei envoy-custom.yaml im Ordner starter.
docker pull envoyproxy/envoy-dev:fd3e8370ddb7a96634c192d1461516e6de1d1797

docker run --add-host host.docker.internal:host-gateway --rm -it -p 9901:9901 -p 8000:8000 -p 8080:8080 -v PATH/TO/ENVOY-CUSTOM.YAML:/envoy-custom.yaml envoyproxy/envoy-dev:fd3e8370ddb7a96634c192d1461516e6de1d1797 -c /envoy-custom.yaml

Docker lädt das Envoy-Image automatisch herunter. Danach sollte Envoy starten. Das Log sollte so aussehen:

[2022-03-02 07:51:48.563][1][info][main] [source/server/server.cc:436]   response trailer map: 152 bytes: grpc-message,grpc-status
[2022-03-02 07:51:48.681][1][info][main] [source/server/server.cc:772] runtime: {}
[2022-03-02 07:51:48.682][1][info][admin] [source/server/admin/admin.cc:134] admin address: 0.0.0.0:9901
[2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:127] loading tracing configuration
[2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:87] loading 0 static secret(s)
[2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:93] loading 2 cluster(s)
[2022-03-02 07:51:48.687][1][info][config] [source/server/configuration_impl.cc:97] loading 2 listener(s)
[2022-03-02 07:51:48.694][1][info][config] [source/server/configuration_impl.cc:109] loading stats configuration
[2022-03-02 07:51:48.696][1][info][main] [source/server/server.cc:868] starting main dispatch loop
[2022-03-02 07:51:48.881][1][info][runtime] [source/common/runtime/runtime_impl.cc:446] RTDS has finished initialization
[2022-03-02 07:51:48.881][1][info][upstream] [source/common/upstream/cluster_manager_impl.cc:207] cm init: all clusters initialized
[2022-03-02 07:51:48.881][1][info][main] [source/server/server.cc:849] all clusters initialized. initializing init manager
[2022-03-02 07:51:48.881][1][info][config] [source/server/listener_manager_impl.cc:784] all dependencies initialized. starting workers
[2022-03-02 07:51:48.902][1][warning][main] [source/server/server.cc:747] there is no configured limit to the number of allowed active connections. Set a limit via the runtime key overload.global_downstream_max_connections

7. Website über REST mit TensorFlow verbinden

Das Backend ist jetzt bereit. Sie können Clientanfragen an TensorFlow Serving senden, um Bilder zu klassifizieren. Es gibt zwei Möglichkeiten, Anfragen an TensorFlow Serving zu senden:

  • REST
  • gRPC

Anfragen über REST senden und Antworten empfangen

Es gibt drei einfache Schritte zum Senden und Empfangen von Anfragen über REST:

  1. Erstellen Sie die REST-Anfrage.
  2. Senden Sie die REST-Anfrage an TensorFlow Serving.
  3. Extrahieren Sie das vorhergesagte Ergebnis aus der REST-Antwort und zeigen Sie es an.

Diese Schritte werden in der Datei src/index.js ausgeführt.

REST-Anfrage erstellen

Derzeit wird die REST-Anfrage nicht über die Funktion classify_img() an TensorFlow Serving gesendet. Sie müssen diesen REST-Zweig implementieren, um zuerst eine REST-Anfrage zu erstellen:

if (radioButtons[0].checked) {
    console.log('Using REST');
    // TODO: Add code to send a REST request to TensorFlow Serving.

} 

TensorFlow Serving erwartet eine POST-Anfrage, die den Bildtensor für das verwendete Inception v3-Modell enthält. Sie müssen also die RGB-Werte aus jedem Pixel des Bildes in ein Array extrahieren und das Array dann in ein JSON-Objekt einbetten, das die Nutzlast der Anfrage darstellt.

  • Fügen Sie diesen Code in den REST-Zweig ein:
//Create the REST request.
let imgTensor = new Array();
let pixelArray = new Array();
context.drawImage(img, 0, 0);
for(let i=0; i<inputImgHeight; i++) {
    pixelArray[i] = new Array();
    for (let j=0; j<inputImgWidth; j++) {
        pixelArray[i][j] = new Array();
        pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[0]/255); 
        pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[1]/255); 
        pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[2]/255); 
    }
}
imgTensor.push(pixelArray);

const RESTURL = 'http://localhost:8000/v1/models/inception:predict';        
let xhr = new XMLHttpRequest();
xhr.open('POST', RESTURL);
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8;');
let data = JSON.stringify({
    instances: imgTensor
});    
xhr.onload = () => {

}
xhr.onerror = () => {
    console.log('REST request error');
}

REST-Anfrage an TensorFlow Serving senden

Jetzt können Sie die Anfrage senden.

  • Fügen Sie diesen Code direkt nach dem obigen Code im REST-Zweig ein:
// Send the REST request.
xhr.send(data);

REST-Antwort von TensorFlow Serving verarbeiten

Das Inception v3-Modell gibt ein Array von Wahrscheinlichkeiten zurück, dass das Bild zu vordefinierten Kategorien gehört. Wenn die Vorhersage erfolgreich ist, sollten Sie die wahrscheinlichste Kategorie auf der Benutzeroberfläche ausgeben.

Sie implementieren den onload()-Listener, um die Antwort zu verarbeiten.

xhr.onload = () => {

}
  • Fügen Sie diesen Code dem onload()-Listener hinzu:
// Process the REST response.
const response = JSON.parse(xhr.responseText);
const maxIndex = argmax(response['predictions'][0])
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;

Der Listener extrahiert nun die vorhergesagten Wahrscheinlichkeiten aus der Antwort, ermittelt die wahrscheinlichste Kategorie des Objekts und zeigt das Ergebnis auf der Benutzeroberfläche an.

Ausführen

  1. Wechseln Sie im Terminal zum Ordner starter und verwenden Sie webpack, um alle JavaScript-Dateien in einer einzigen Datei zu bündeln, die Sie in die Datei dist/index.html einbetten können:
npm install -g npx
npm install --save-dev webpack
npx webpack
  1. Aktualisieren Sie http://localhost:8887/ in Ihrem Browser und klicken Sie dann auf REST > Classify.

Auf der Website wird 286 als vorhergesagte Kategorie angezeigt, die dem Label Egyptian Cat im ImageNet-Dataset entspricht.

c865a93b9b58335d.png

8. Website über gRPC mit TensorFlow Serving verbinden

Neben REST unterstützt TensorFlow Serving auch gRPC.

b6f4449c2c850b0e.png

gRPC ist ein modernes Open-Source-Framework für hochleistungsfähige Remote-Prozeduraufrufe (RPCs), das in jeder Umgebung ausgeführt werden kann. Damit lassen sich Dienste in und zwischen Rechenzentren effizient verbinden. Außerdem bietet es Plug-in-Unterstützung für Load-Balancing, Tracing, Systemdiagnosen und Authentifizierung. In der Praxis hat sich gezeigt, dass gRPC leistungsfähiger als REST ist.

Anfragen senden und Antworten mit gRPC empfangen

Dazu sind nur vier einfache Schritte erforderlich:

  1. Optional: Generieren Sie den gRPC-Client-Stub-Code.
  2. Erstellen Sie die gRPC-Anfrage.
  3. Senden Sie die gRPC-Anfrage an TensorFlow Serving.
  4. Extrahieren Sie das vorhergesagte Ergebnis aus der gRPC-Antwort und zeigen Sie es in der Benutzeroberfläche an.

Sie führen diese Schritte in der Datei src/index.js aus.

Optional: gRPC-Client-Stubcode generieren

Wenn Sie gRPC mit TensorFlow Serving verwenden möchten, müssen Sie dem gRPC-Workflow folgen. Weitere Informationen finden Sie in der gRPC-Dokumentation.

a9d0e5cb543467b4.png

TensorFlow Serving und TensorFlow definieren die .proto-Dateien für Sie. Ab TensorFlow und TensorFlow Serving 2.8 sind die folgenden .proto-Dateien erforderlich:

tensorflow/core/example/example.proto
tensorflow/core/example/feature.proto
tensorflow/core/protobuf/struct.proto
tensorflow/core/protobuf/saved_object_graph.proto
tensorflow/core/protobuf/saver.proto
tensorflow/core/protobuf/trackable_object_graph.proto
tensorflow/core/protobuf/meta_graph.proto
tensorflow/core/framework/node_def.proto
tensorflow/core/framework/attr_value.proto
tensorflow/core/framework/function.proto
tensorflow/core/framework/types.proto
tensorflow/core/framework/tensor_shape.proto
tensorflow/core/framework/full_type.proto
tensorflow/core/framework/versions.proto
tensorflow/core/framework/op_def.proto
tensorflow/core/framework/graph.proto
tensorflow/core/framework/tensor.proto
tensorflow/core/framework/resource_handle.proto
tensorflow/core/framework/variable.proto

tensorflow_serving/apis/inference.proto
tensorflow_serving/apis/classification.proto
tensorflow_serving/apis/predict.proto
tensorflow_serving/apis/regression.proto
tensorflow_serving/apis/get_model_metadata.proto
tensorflow_serving/apis/input.proto
tensorflow_serving/apis/prediction_service.proto
tensorflow_serving/apis/model.proto
  • Wechseln Sie in Ihrem Terminal zum Ordner starter/src/proto/ und generieren Sie den Stub:
bash generate_grpc_stub_js.sh

gRPC-Anfrage erstellen

Ähnlich wie bei der REST-Anfrage erstellen Sie die gRPC-Anfrage im gRPC-Zweig.

if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {

}
else {
    print("Using gRPC")
    // TODO: Add code to send a gRPC request to TensorFlow Serving.
    
}
  • Fügen Sie diesen Code dem gRPC-Branch hinzu:
// Create the gRPC request.
const PredictModule = require('./proto/generated/tensorflow_serving/apis/predict_pb.js');
const PredictionServiceClientModule = require('./proto/generated/tensorflow_serving/apis/prediction_service_grpc_web_pb.js');
const ModelModule = require('./proto/generated/tensorflow_serving/apis/model_pb.js');
const TensorModule = require('./proto/generated/tensorflow/core/framework/tensor_pb.js');

const GPRCURL = 'http://localhost:8080';
const stub = new PredictionServiceClientModule.PredictionServiceClient(GPRCURL);

const modelSpec = new ModelModule.ModelSpec();
modelSpec.setName('inception');

const tensorProto = new TensorModule.TensorProto();
const tensorShapeProto = new TensorModule.TensorShapeProto();

const batchDim = (new TensorModule.TensorShapeProto.Dim()).setSize(1);
const heightDim = (new TensorModule.TensorShapeProto.Dim()).setSize(inputImgHeight);
const widthDim = (new TensorModule.TensorShapeProto.Dim()).setSize(inputImgWidth);
const channelDim = (new TensorModule.TensorShapeProto.Dim()).setSize(3);

tensorShapeProto.setDimList([batchDim, heightDim, widthDim, channelDim]);

tensorProto.setDtype(proto.tensorflow.DataType.DT_FLOAT);
tensorProto.setTensorShape(tensorShapeProto);
context.drawImage(img, 0, 0);
for(let i=0; i<inputImgHeight; i++) {
    for (let j=0; j<inputImgWidth; j++) {
        tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[0]/255); 
        tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[1]/255); 
        tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[2]/255); 
    }
}

const predictionServiceRequest = new PredictModule.PredictRequest();
predictionServiceRequest.setModelSpec(modelSpec);
predictionServiceRequest.getInputsMap().set('inputs', tensorProto);

gRPC-Anfrage an TensorFlow Serving senden

Jetzt können Sie die Anfrage senden.

  • Fügen Sie diesen Code direkt nach dem Code im gRPC-Zweig im vorherigen Code-Snippet ein:
// Send the gRPC request.
stub.predict(predictionServiceRequest, {}, function(err, response) {
    // TODO: Add code to process the response.
});

gRPC-Antwort von TensorFlow Serving verarbeiten

Schließlich implementieren Sie die Callback-Funktion oben, um die Antwort zu verarbeiten.

  • Fügen Sie diesen Code in den Funktionskörper im vorherigen Code-Snippet ein:
// Process the gRPC response.
if (err) {
    console.log(err.code);
    console.log(err.message);
} 
else {
    const maxIndex = argmax(response.getOutputsMap().get('logits').getFloatValList());
    document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
}

Der Listener extrahiert nun die vorhergesagten Wahrscheinlichkeiten aus der Antwort, ermittelt die wahrscheinlichste Kategorie des Objekts und zeigt das Ergebnis auf der Benutzeroberfläche an.

Ausführen

  1. Verwenden Sie in Ihrem Terminal webpack, um alle JavaScript-Dateien in einer einzigen Datei zu bündeln, die Sie in die index.html-Datei einbetten können:
npx webpack
  1. Aktualisieren Sie http://localhost:8887/ in Ihrem Browser.
  2. Klicken Sie auf gRPC > Classify (gRPC > Klassifizieren).

Auf der Website wird die vorhergesagte Kategorie von 286 angezeigt, die dem Label Egyptian Cat im ImageNet-Dataset entspricht.

9. Glückwunsch

Sie haben TensorFlow Serving verwendet, um Ihrer Website Funktionen zur Bildklassifizierung hinzuzufügen.

Weitere Informationen