Eine einfache Website erstellen, auf der Bilder klassifiziert werden

1. Hinweis

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

Vorbereitung

  • Grundkenntnisse in der Webentwicklung wie HTML und JavaScript
  • Grundkenntnisse in Machine Learning mit TensorFlow, z. B. für Training und Bereitstellung
  • Grundkenntnisse zu Terminals und Docker

Lerninhalte

  • Vortrainierte Modelle zur Bildklassifizierung in TensorFlow Hub finden
  • Anleitung zum Erstellen einer einfachen Website und zum Erstellen von Vorhersagen mit dem heruntergeladenen Bildklassifizierungsmodell über TensorFlow Serving (REST und gRPC).
  • Rendering des Erkennungsergebnisses in der UI

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 gt; ZIP herunterladen, um den gesamten Code für dieses Codelab herunterzuladen.

a72f2bb4caa9a96

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

Für dieses Codelab benötigen Sie nur die Dateien im Unterverzeichnis TFServing/ImageClassificationWeb des Repositorys mit zwei Ordnern:

  • Der Ordner starter enthält den Startcode, den Sie für dieses Codelab erstellen.
  • Der Ordner finished enthält den abgeschlossenen Code für die fertige Beispiel-App.

3. Abhängigkeiten installieren

So installieren Sie die Abhängigkeiten:

  • Gehen Sie in Ihrem Terminal zum Ordner starter und installieren Sie die erforderlichen NPM-Pakete:
npm install

4. Starter-Website ausführen

Verwende Web Server für Chrome, um die Datei TFServing/ImageClassificationWeb/starter/dist/index.html zu laden:

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

f7b43cd44ebf1f1b

Die Website entdecken und erkunden

Du solltest die Website jetzt sehen. Die Benutzeroberfläche ist ziemlich unkompliziert: Es gibt ein Katzenbild, das Sie klassifizieren möchten und der Nutzer die Daten mit REST oder gRPC an das Back-End senden können. Das Back-End führt eine Bildklassifizierung für das Bild durch und gibt das Klassifizierungsergebnis an die Website zurück, auf der das Ergebnis angezeigt wird.

837d97a27c59a0b3

Wenn Sie auf Klassifizieren klicken, passiert nichts, da die Kommunikation mit dem Back-End noch nicht möglich ist.

5. Bildklassifizierungsmodell mit TensorFlow Serving bereitstellen

Die Bildklassifizierung ist eine sehr gängige ML-Aufgabe, die ein Bild basierend auf dem Hauptinhalt des Bildes in vordefinierte Kategorien einteilt. Hier ein Beispiel für die Klassifizierung von Blumen:

a6da16b4a7665db0.png

Auf TensorFlow Hub gibt es eine Reihe von vortrainierten Modellen zur Bildklassifizierung. Für dieses Codelab verwenden Sie ein beliebtes Inception v3-Modell.

So stellen Sie das Bildklassifizierungsmodell mit TensorFlow Serving bereit:

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

Du kannst den Ordner inception_v3 als Ordner SavedModel verwenden. 123 ist eine Beispielversionsnummer. Wenn du möchtest, kannst du eine andere Nummer auswählen.

Die Ordnerstruktur sollte so aussehen:

21a8675ac8d31907

TensorFlow-Bereitstellung starten

  • Starten Sie in Ihrem Terminal TensorFlow Serving mit Docker, ersetzen Sie jedoch PATH/TO/SAVEDMODEL durch den absoluten Pfad des inception_v3-Ordners 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 automatisch das TensorFlow Serving-Image herunter. Das dauert eine Minute. Danach sollte die TensorFlow-Bereitstellung beginnen. Das Protokoll 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 stellt TensorFlow Serving den Access-Control-Allow-Origin-Header nicht fest, sodass der Browser die Anfrage aus Sicherheitsgründen vom Front-End-JavaScript an TensorFlow Serving blockiert. Sie können das Problem umgehen, indem Sie einen Proxy wie Envoy verwenden, um die JavaScript-Anfrage an das TensorFlow Serving-Back-End zu senden.

Envoy starten

  • Laden Sie auf Ihrem Terminal das Envoy-Image herunter und starten Sie Envoy mit Docker. Ersetzen Sie jedoch 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 automatisch das Envoy-Image herunter. Anschließend sollte Envoy starten. Das Protokoll 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 TensorFlow mit TensorFlow verbinden

Das Back-End ist jetzt bereit, damit Sie Clientanfragen zur Klassifizierung von Bildern an TensorFlow Serving senden können. Es gibt zwei Möglichkeiten, Anfragen an die TensorFlow-Bereitstellung zu senden:

  • REST
  • gRPC

Anfragen senden und Antworten über REST erhalten

Das Senden und Empfangen von Anfragen über REST umfasst drei einfache Schritte:

  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 das Ergebnis an.

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

REST-Anfrage erstellen

Aktuell sendet die Funktion classify_img() die REST-Anfrage nicht an TensorFlow Serving. Sie müssen diesen REST-Branch implementieren, um eine REST-Anfrage erstellen zu können:

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

} 

Für TensorFlow Serving wird eine POST-Anfrage erwartet, die den Bildtensor für das von Ihnen verwendete Inception v3-Modell enthält. Daher müssen Sie die RGB-Werte aus jedem Pixel des Bildes in einem Array extrahieren und das Array in einer JSON-Datei zusammenfassen (die Nutzlast der Anfrage).

  • Fügen Sie diesen Code dem REST-Branch hinzu:
//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

Sie können die Anfrage jetzt senden.

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

REST-Antwort von TensorFlow Serving verarbeiten

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

Du implementierst den onload()-Listener für die Antwort.

xhr.onload = () => {

}
  • Füge 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 vorhergesagte Wahrscheinlichkeiten aus der Antwort, identifiziert die wahrscheinlichste Kategorie des Objekts und zeigt das Ergebnis in der Benutzeroberfläche an.

Ausführen

  1. Wechseln Sie in Ihrem 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 prognostizierte Kategorie angezeigt. Diese ist dem Label Egyptian Cat im ImageNet-Dataset zugeordnet.

c865a93b9b58335d.png

8. Website mit TensorFlow über gRPC verbinden

Zusätzlich zur REST-Unterstützung unterstützt TensorFlow Serving auch BeyondCorp.

b6f4449c2c850b0e.png

gRPC ist ein modernes, leistungsstarkes Open-Source-RPC-Framework (Remote Procedure Call), das in jeder Umgebung ausgeführt werden kann. Dank der flexiblen Unterstützung für Load-Balancing, Tracing, Systemdiagnose und Authentifizierung lassen sich Dienste effizient in und zwischen Rechenzentren verbinden. Es wurde festgestellt, dass gRPC in der Praxis leistungsstärker ist als REST.

Anfragen mit gRPC senden und Antworten erhalten

Es sind vier einfache Schritte:

  1. Optional: Erstellen 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 UI an.

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

Optional: gRPC-Client-Stub-Code generieren

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

a9d0e5cb543467b4.png

Die Bereitstellung von .proto-Dateien wird durch TensorFlow Serving und TensorFlow definiert. Ab TensorFlow und TensorFlow Serving 2.8 sind diese .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
  • Gehen Sie in Ihrem Terminal zum Ordner starter/src/proto/ und generieren Sie die Stubs:
bash generate_grpc_stub_js.sh

gRPC-Anfrage erstellen

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

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

Sie können die Anfrage jetzt senden.

  • Fügen Sie diesen Code direkt nach dem Code im gRPC-Branch 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

Zum Abschluss implementieren Sie die obige Callback-Funktion, um die Antwort zu verarbeiten.

  • Fügen Sie folgenden Code in den Funktionstext 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 vorhergesagte Wahrscheinlichkeiten aus der Antwort, identifiziert die wahrscheinlichste Kategorie des Objekts und zeigt das Ergebnis in der Benutzeroberfläche an.

Ausführen

  1. Verwende im Terminal mit Webpack alle JavaScript-Dateien in einer einzigen Datei, die du in die Datei index.html einbetten kannst:
npx webpack
  1. Aktualisieren Sie http://localhost:8887/ in Ihrem Browser.
  2. Klicken Sie auf GRP > Classify.

Auf der Website wird die prognostizierte Kategorie von 286 angezeigt. Diese ist dem Label Egyptian Cat im ImageNet-Dataset zugeordnet.

9. Glückwunsch

Sie haben TensorFlow Serving genutzt, um Ihre Website mit Klassifizierungsfunktionen für Bilder zu versehen.

Weitere Informationen