Créer une application Flutter pour classer des textes

1. Avant de commencer

Dans cet atelier de programmation, vous allez apprendre à générer une inférence de classification de texte à partir d'une application Flutter avec TensorFlow Serving via REST et gRPC.

Prérequis

Objectifs de l'atelier

  • Créer une application Flutter simple et classer des textes via TensorFlow Serving (REST et gRPC)
  • Afficher les résultats dans l'UI

Ce dont vous avez besoin

2. Configuration

Pour télécharger le code de cet atelier de programmation, procédez comme suit :

  1. Accédez au dépôt GitHub pour cet atelier de programmation.
  2. Cliquez sur Code > Download ZIP (Code > Télécharger le fichier ZIP) afin de télécharger l'ensemble du code pour cet atelier de programmation.

2cd45599f51fb8a2.png

  1. Décompressez le fichier ZIP téléchargé pour accéder au dossier racine codelabs-master contenant toutes les ressources nécessaires.

Pour cet atelier de programmation, vous n'avez besoin que des fichiers du sous-répertoire tfserving-flutter/codelab2 dans le dépôt, qui contient deux dossiers :

  • Le dossier starter contient le code de démarrage sur lequel s'appuie cet atelier de programmation.
  • Le dossier finished contient le code final de l'application exemple.

3. Télécharger les dépendances pour le projet

  1. Dans VS Code, cliquez sur File > Open folder (Fichier > Ouvrir le dossier), puis sélectionnez le dossier starter dans le code source que vous avez téléchargé précédemment.
  2. Si une boîte de dialogue vous invite à télécharger les packages requis pour l'application de départ, cliquez sur Get packages (Télécharger les packages).
  3. Si cette boîte de dialogue ne s'affiche pas, ouvrez votre terminal, puis exécutez la commande flutter pub get dans le dossier starter.

7ada07c300f166a6.png

4. Exécuter l'application de départ

  1. Dans VS Code, assurez-vous qu'Android Emulator ou le simulateur iOS est correctement configuré et s'affiche dans la barre d'état.

Par exemple, voici ce que vous voyez lorsque vous utilisez le Pixel 5 avec Android Emulator :

9767649231898791.png

Voici ce qui s'affiche lorsque vous utilisez l'iPhone 13 avec le simulateur iOS :

95529e3a682268b2.png

  1. Cliquez sur l'icône Démarrer le débogage a19a0c68bc4046e6.png.

Exécuter et explorer l'application

L'application devrait se lancer sur Android Emulator ou le simulateur iOS. L'UI est assez simple. Un champ de texte permet à l'utilisateur de saisir du texte. Il peut choisir d'envoyer les données au backend avec REST ou gRPC. Le backend se sert d'un modèle TensorFlow pour effectuer la classification de texte sur l'entrée prétraitée et renvoie le résultat de la classification à l'application cliente, laquelle met à jour l'UI.

b298f605d64dc132.png d3ef3ccd3c338108.png

Si vous cliquez sur Classify (Classer), rien ne se passe, car la communication avec le backend n'est pas encore établie.

5. Déployer un modèle de classification de texte avec TensorFlow Serving

La classification de texte est une tâche de machine learning très courante qui classe les textes dans des catégories prédéfinies. Dans cet atelier de programmation, vous allez déployer le modèle pré-entraîné de l'atelier de programmation "Entraîner un modèle de détection des spams dans les commentaires avec TensorFlow Lite Model Maker" à l'aide de TensorFlow Serving, puis appeler le backend depuis votre interface Flutter pour classer le texte saisi comme spam ou non spam.

Démarrer TensorFlow Serving

  • Dans votre terminal, démarrez TensorFlow Serving avec Docker, en remplaçant l'espace réservé PATH/TO/SAVEDMODEL par le chemin absolu du dossier mm_spam_savedmodel sur votre ordinateur.
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 commence par télécharger automatiquement l'image TensorFlow Serving, ce qui prend une minute. Le service TensorFlow Serving devrait alors démarrer. Le journal doit se présenter comme cet extrait de code :

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 ...

6. Tokeniser la phrase d'entrée

Le backend est prêt pour que vous puissiez envoyer des requêtes client à TensorFlow Serving. Cependant, vous devez d'abord tokeniser la phrase d'entrée. Si vous inspectez le Tensor d'entrée du modèle, vous pouvez voir qu'il attend une liste de 20 nombres entiers au lieu de chaînes brutes. La segmentation consiste à mapper les mots que vous saisissez dans une application à une liste d'entiers basés sur un dictionnaire de vocabulaire, avant de les envoyer au backend pour les classer. Par exemple, si vous saisissez buy book online to learn more, le processus de tokenisation effectue le mappage sur [32, 79, 183, 10, 224, 631, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]. Les chiffres indiqués peuvent varier en fonction du dictionnaire de vocabulaire.

  1. Dans le fichier lib/main.dart, ajoutez ce code à la méthode predict() pour créer le dictionnaire de vocabulaire _vocabMap.
// 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. Immédiatement après l'extrait de code précédent, ajoutez ce code pour implémenter la tokenisation :
// 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;
  }
}

Ce code met en minuscules la chaîne de phrase, supprime les caractères non alphabétiques et mappe les mots sur 20 index d'entiers en fonction de la table de vocabulaire.

7. Connecter l'application Flutter à TensorFlow Serving via REST

Il existe deux façons d'envoyer des requêtes à TensorFlow Serving :

  • REST
  • gRPC

Envoyer des requêtes et recevoir des réponses via REST

La procédure pour envoyer des requêtes et recevoir des réponses via REST comporte trois étapes simples :

  1. Créez la requête REST.
  2. Envoyez la requête REST à TensorFlow Serving.
  3. Extrayez le résultat prédit de la réponse REST et affichez l'UI.

Vous devez effectuer ces étapes dans le fichier main.dart.

Créer et envoyer la requête REST à TensorFlow Serving

  1. Pour le moment, la fonction predict() n'envoie pas la requête REST à TensorFlow Serving. Vous devez implémenter la branche REST pour créer une requête REST :
if (_connectionMode == ConnectionModeType.rest) {
  // TODO: Create and send the REST request.

}
  1. Ajoutez ce code à la branche REST :
//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],
  }),
);

Traiter la réponse REST à partir de TensorFlow Serving

  • Ajoutez ce code immédiatement après l'extrait de code précédent pour gérer la réponse REST :
// 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');
}

Le code de post-traitement extrait la probabilité que la phrase d'entrée soit un message spam de la réponse et affiche le résultat de la classification dans l'UI.

Exécuter l'application

  1. Cliquez sur l'icône Démarrer le débogage a19a0c68bc4046e6.png, puis attendez que l'application se charge.
  2. Saisissez du texte et sélectionnez ensuite REST > Classify (REST > Classer).

8e21d795af36d07a.png e79a0367a03c2169.png

8. Connecter l'application Flutter à TensorFlow Serving via gRPC

TensorFlow Serving est non seulement compatible avec REST, mais aussi avec gRPC.

b6f4449c2c850b0e.png

gRPC est un framework d'appel de procédure à distance (RPC) moderne, Open Source et haute performance qui peut être exécuté dans n'importe quel environnement. Il permet de connecter efficacement les services dans et entre les centres de données avec compatibilité par plug-in pour l'équilibrage de charge, le traçage, la vérification de l'état et l'authentification. Selon les observations, gRPC s'avère plus performant que REST dans la pratique.

Envoyer des requêtes et recevoir des réponses avec gRPC

La procédure pour envoyer des requêtes et recevoir des réponses avec gRPC comporte quatre étapes simples :

  1. (Facultatif) Générez le code du bouchon pour le client gRPC.
  2. Créez la requête gRPC.
  3. Envoyez la requête gRPC à TensorFlow Serving.
  4. Extrayez le résultat prédit de la réponse gRPC, puis affichez l'UI.

Vous devez effectuer ces étapes dans le fichier main.dart.

(Facultatif) Générer le code du bouchon pour le client gRPC

Pour utiliser gRPC avec TensorFlow Serving, vous devez suivre le workflow gRPC. Pour en savoir plus, consultez la documentation gRPC.

a9d0e5cb543467b4.png

TensorFlow Serving et TensorFlow définissent les fichiers .proto automatiquement. Pour TensorFlow et TensorFlow Serving 2.8, vous avez besoin des fichiers .proto suivants :

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
  • Dans votre terminal, accédez au dossier starter/lib/proto/ et générez le bouchon :
bash generate_grpc_stub_dart.sh

Créer la requête gRPC

Comme pour la requête REST, vous devez créer la requête gRPC dans la branche gRPC.

if (_connectionMode == ConnectionModeType.rest) {

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

}
  • Ajoutez ce code pour créer la requête gRPC :
//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});

Remarque : Les noms des Tensors d'entrée et de sortie peuvent varier d'un modèle à l'autre, même si les architectures de modèles sont identiques. Veillez à les mettre à jour si vous entraînez votre propre modèle.

Envoyer la requête gRPC à TensorFlow Serving

  • Ajoutez ce code après l'extrait de code précédent pour envoyer la requête gRPC à TensorFlow Serving :
// Send the gRPC request.
PredictResponse response = await _stub.predict(request);

Traiter la réponse gRPC de TensorFlow Serving

  • Ajoutez ce code après l'extrait de code précédent pour implémenter les fonctions de rappel permettant de gérer la réponse :
// 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');
}

Le code de post-traitement extrait alors le résultat de la classification et l'affiche dans l'UI.

Exécuter l'application

  1. Cliquez sur l'icône Démarrer le débogage a19a0c68bc4046e6.png, puis attendez que l'application se charge.
  2. Saisissez du texte et sélectionnez ensuite gRPC > Classify (gRPC > Classer)

e44e6e9a5bde2188.png 92644d723f61968c.png

9. Félicitations

Vous avez utilisé TensorFlow Serving pour ajouter des fonctionnalités de classification de texte à votre application.

Dans l'atelier de programmation suivant, vous allez améliorer le modèle de façon à pouvoir détecter des spams spécifiques qui ne peuvent pas être détectés par l'application actuelle.

En savoir plus