Flutter-App zum Klassifizieren von Texten erstellen

1. Hinweis

In diesem Codelab erfahren Sie, wie Sie über REST und gRPC eine Textklassifizierungs-Inferenz aus einer Flutter-App mit TensorFlow Serving ausführen.

Vorbereitung

Lerninhalte

  • So erstellen Sie eine einfache Flutter-App und klassifizieren Texte über TensorFlow Serving (REST und gRPC).
  • Wie die Ergebnisse in der Benutzeroberfläche angezeigt werden.

Voraussetzungen

2. Flutter-Entwicklungsumgebung einrichten

Für die Flutter-Entwicklung benötigen Sie zwei Softwarekomponenten: das Flutter SDK und einen Editor.

Sie können das Codelab auf einem der folgenden Geräte ausführen:

  • Der iOS-Simulator (erfordert die Installation von Xcode-Tools).
  • Android Emulator (Einrichtung in Android Studio erforderlich)
  • Ein Browser (für das Debugging ist Chrome erforderlich).
  • Als Windows-, Linux- oder macOS-Desktopanwendung. Sie müssen auf der Plattform entwickeln, auf der Sie die Bereitstellung planen. Wenn Sie also eine Windows-Desktop-App entwickeln möchten, müssen Sie die Entwicklung unter Windows durchführen, um auf die entsprechende Build-Kette zuzugreifen. Es gibt betriebssystemspezifische Anforderungen, die auf docs.flutter.dev/desktop ausführlich beschrieben werden.

3. Einrichten

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

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

2cd45599f51fb8a2.png

  1. Entpacken Sie die heruntergeladene ZIP-Datei, um einen codelabs-main-Stammordner mit allen erforderlichen Ressourcen zu entpacken.

Für dieses Codelab benötigen Sie nur die Dateien im Unterverzeichnis tfserving-flutter/codelab2 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.

4. Abhängigkeiten für das Projekt herunterladen

  1. Klicken Sie in VS Code auf Datei > Ordner öffnen und wählen Sie dann den Ordner starter aus dem Quellcode aus, den Sie zuvor heruntergeladen haben.
  2. Wenn ein Dialogfeld angezeigt wird, in dem Sie aufgefordert werden, die erforderlichen Pakete für die Starter-App herunterzuladen, klicken Sie auf Pakete herunterladen.
  3. Wenn dieses Dialogfeld nicht angezeigt wird, öffnen Sie das Terminal und führen Sie den Befehl flutter pub get im Ordner starter aus.

7ada07c300f166a6.png

5. Start-App ausführen

  1. Achten Sie darauf, dass der Android-Emulator oder iOS-Simulator in VS Code richtig eingerichtet ist und in der Statusleiste angezeigt wird.

So sieht es beispielsweise aus, wenn Sie das Pixel 5 mit dem Android-Emulator verwenden:

9767649231898791.png

So sieht es aus, wenn Sie das iPhone 13 mit dem iOS-Simulator verwenden:

95529e3a682268b2.png

  1. Klicken Sie auf a19a0c68bc4046e6.png Fehlerbehebung starten.

App ausführen und ausprobieren

Die App sollte im Android-Emulator oder iOS-Simulator gestartet werden. Die Benutzeroberfläche ist ganz einfach. Es gibt ein Textfeld, in das der Nutzer den Text eingeben kann. Der Nutzer kann auswählen, ob die Daten mit REST oder gRPC an das Backend gesendet werden sollen. Das Backend verwendet ein TensorFlow-Modell, um die vorverarbeitete Eingabe zu klassifizieren, und gibt das Klassifizierungsergebnis an die Client-App zurück, die wiederum die Benutzeroberfläche aktualisiert.

b298f605d64dc132.png d3ef3ccd3c338108.png

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

6. Textklassifizierungsmodell mit TensorFlow Serving bereitstellen

Die Textklassifizierung ist eine sehr häufige Aufgabe beim maschinellen Lernen, bei der Texte in vordefinierte Kategorien klassifiziert werden. In diesem Codelab stellen Sie das vortrainierte Modell aus dem Codelab zum Trainieren eines Modells zur Erkennung von Kommentar-Spam mit TensorFlow Lite Model Maker mit TensorFlow Serving bereit und rufen das Backend über Ihr Flutter-Frontend auf, um den eingegebenen Text als Spam oder Kein Spam zu klassifizieren.

TensorFlow Serving starten

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

docker run -it --rm -p 8500:8500 -p 8501:8501 -v "PATH/TO/SAVEDMODEL:/models/spam-detection" -e MODEL_NAME=spam-detection 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/ssd_mobilenet_v2_2/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/ssd_mobilenet_v2_2/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: spam-detection 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 ...

7. Eingabesatz tokenisieren

Das Backend ist jetzt bereit. Sie können also fast Clientanfragen an TensorFlow Serving senden. Zuerst müssen Sie jedoch den Eingabesatz tokenisieren. Wenn Sie den Eingabe-Tensor des Modells untersuchen, sehen Sie, dass er eine Liste mit 20 Ganzzahlen anstelle von Rohstrings erwartet. Bei der Tokenisierung werden die einzelnen Wörter, die Sie in der App eingeben, anhand eines Vokabelwörterbuchs einer Liste von Ganzzahlen zugeordnet, bevor sie zur Klassifizierung an das Backend gesendet werden. Wenn Sie beispielsweise buy book online to learn more eingeben, wird es durch die Tokenisierung [32, 79, 183, 10, 224, 631, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] zugeordnet. Die genauen Zahlen können je nach Vokabelverzeichnis variieren.

  1. Fügen Sie in der Datei lib/main.dart diesen Code in die Methode predict() ein, um das _vocabMap-Vokabelverzeichnis zu erstellen.
// Build _vocabMap if empty.
if (_vocabMap.isEmpty) {
  final vocabFileString = await rootBundle.loadString(vocabFile);
  final lines = vocabFileString.split('\n');
  for (final l in lines) {
    if (l != "") {
      var wordAndIndex = l.split(' ');
      (_vocabMap)[wordAndIndex[0]] = int.parse(wordAndIndex[1]);
    }
  }
} 
  1. Fügen Sie direkt nach dem vorherigen Code-Snippet diesen Code ein, um die Tokenisierung zu implementieren:
// Tokenize the input sentence.
final inputWords = _inputSentenceController.text
    .toLowerCase()
    .replaceAll(RegExp('[^a-z ]'), '')
    .split(' ');
// Initialize with padding token.
_tokenIndices = List.filled(maxSentenceLength, 0);
var i = 0;
for (final w in inputWords) {
  if ((_vocabMap).containsKey(w)) {
    _tokenIndices[i] = (_vocabMap)[w]!;
    i++;
  }

  // Truncate the string if longer than maxSentenceLength.
  if (i >= maxSentenceLength - 1) {
    break;
  }
}

In diesem Code wird die Zeichenfolge des Satzes in Kleinbuchstaben umgewandelt, nicht alphabetische Zeichen werden entfernt und die Wörter werden anhand der Vokabeltabelle 20 Ganzzahlindizes zugeordnet.

8. Flutter-App über REST mit TensorFlow Serving verbinden

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, um Anfragen über REST zu senden und Antworten zu empfangen:

  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 rendern Sie die Benutzeroberfläche.

Sie führen diese Schritte in der Datei main.dart aus.

REST-Anfrage erstellen und an TensorFlow Serving senden

  1. Derzeit wird die REST-Anfrage nicht über die Funktion predict() an TensorFlow Serving gesendet. Sie müssen den REST-Zweig implementieren, um eine REST-Anfrage zu erstellen:
if (_connectionMode == ConnectionModeType.rest) {
  // TODO: Create and send the REST request.

}
  1. Fügen Sie diesen Code in den REST-Zweig ein:
//Create the REST request.
final response = await http.post(
  Uri.parse('http://' +
      _server +
      ':' +
      restPort.toString() +
      '/v1/models/' +
      modelName +
      ':predict'),
  body: jsonEncode(<String, List<List<int>>>{
    'instances': [_tokenIndices],
  }),
);

REST-Antwort von TensorFlow Serving verarbeiten

  • Fügen Sie diesen Code direkt nach dem vorherigen Code-Snippet ein, um die REST-Antwort zu verarbeiten:
// Process the REST response.
if (response.statusCode == 200) {
  Map<String, dynamic> result = jsonDecode(response.body);
  if (result['predictions']![0][1] >= classificationThreshold) {
    return 'This sentence is spam. Spam score is ' +
        result['predictions']![0][1].toString();
  }
  return 'This sentence is not spam. Spam score is ' +
      result['predictions']![0][1].toString();
} else {
  throw Exception('Error response');
}

Der Postprocessing-Code extrahiert die Wahrscheinlichkeit, dass der eingegebene Satz eine Spam-Nachricht ist, aus der Antwort und zeigt das Klassifizierungsergebnis in der Benutzeroberfläche an.

Ausführen

  1. Klicken Sie auf a19a0c68bc4046e6.png Fehlerbehebung starten und warten Sie, bis die App geladen ist.
  2. Geben Sie Text ein und wählen Sie dann REST > Classify (REST > Klassifizieren) aus.

8e21d795af36d07a.png e79a0367a03c2169.png

9. Flutter-App ü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

Es gibt vier einfache Schritte, um Anfragen mit gRPC zu senden und Antworten zu empfangen:

  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 rendern Sie die Benutzeroberfläche.

Sie führen diese Schritte in der Datei main.dart 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

google/protobuf/any.proto
google/protobuf/wrappers.proto
  • Wechseln Sie in Ihrem Terminal zum Ordner starter/lib/proto/ und generieren Sie den Stub:
bash generate_grpc_stub_dart.sh

gRPC-Anfrage erstellen

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

if (_connectionMode == ConnectionModeType.rest) {

} else {
  // TODO: Create and send the gRPC request.

}
  • Fügen Sie diesen Code hinzu, um die gRPC-Anfrage zu erstellen:
//Create the gRPC request.
final channel = ClientChannel(_server,
    port: grpcPort,
    options:
        const ChannelOptions(credentials: ChannelCredentials.insecure()));
_stub = PredictionServiceClient(channel,
    options: CallOptions(timeout: const Duration(seconds: 10)));

ModelSpec modelSpec = ModelSpec(
  name: 'spam-detection',
  signatureName: 'serving_default',
);

TensorShapeProto_Dim batchDim = TensorShapeProto_Dim(size: Int64(1));
TensorShapeProto_Dim inputDim =
    TensorShapeProto_Dim(size: Int64(maxSentenceLength));
TensorShapeProto inputTensorShape =
    TensorShapeProto(dim: [batchDim, inputDim]);
TensorProto inputTensor = TensorProto(
    dtype: DataType.DT_INT32,
    tensorShape: inputTensorShape,
    intVal: _tokenIndices);

// If you train your own model, update the input and output tensor names.
const inputTensorName = 'input_3';
const outputTensorName = 'dense_5';
PredictRequest request = PredictRequest(
    modelSpec: modelSpec, inputs: {inputTensorName: inputTensor});

Hinweis:Die Namen der Ein- und Ausgabetensoren können sich von Modell zu Modell unterscheiden, auch wenn die Modellarchitekturen gleich sind. Aktualisieren Sie sie, wenn Sie Ihr eigenes Modell trainieren.

gRPC-Anfrage an TensorFlow Serving senden

  • Fügen Sie diesen Code nach dem vorherigen Code-Snippet ein, um die gRPC-Anfrage an TensorFlow Serving zu senden:
// Send the gRPC request.
PredictResponse response = await _stub.predict(request);

gRPC-Antwort von TensorFlow Serving verarbeiten

  • Fügen Sie diesen Code nach dem vorherigen Code-Snippet ein, um die Callback-Funktionen zur Verarbeitung der Antwort zu implementieren:
// Process the response.
if (response.outputs.containsKey(outputTensorName)) {
  if (response.outputs[outputTensorName]!.floatVal[1] >
      classificationThreshold) {
    return 'This sentence is spam. Spam score is ' +
        response.outputs[outputTensorName]!.floatVal[1].toString();
  } else {
    return 'This sentence is not spam. Spam score is ' +
        response.outputs[outputTensorName]!.floatVal[1].toString();
  }
} else {
  throw Exception('Error response');
}

Der Postprocessing-Code extrahiert nun das Klassifizierungsergebnis aus der Antwort und zeigt es in der Benutzeroberfläche an.

Ausführen

  1. Klicken Sie auf a19a0c68bc4046e6.png Fehlerbehebung starten und warten Sie, bis die App geladen ist.
  2. Geben Sie etwas Text ein und wählen Sie dann gRPC > Classify aus.

e44e6e9a5bde2188.png 92644d723f61968c.png

10. Glückwunsch

Sie haben TensorFlow Serving verwendet, um Ihrer App Funktionen zur Textklassifizierung hinzuzufügen.

Im nächsten Codelab werden Sie das Modell so erweitern, dass Sie bestimmte Spamnachrichten erkennen können, die von der aktuellen App nicht erkannt werden.

Weitere Informationen