1. Prima di iniziare
In questo codelab, imparerai a eseguire un'inferenza di classificazione del testo da un'app Flutter con TensorFlow Serving tramite REST e gRPC.
Prerequisiti
- Conoscenza di base dello sviluppo Flutter con Dart
- Conoscenza di base del machine learning con TensorFlow, ad esempio addestramento e deployment
- Conoscenza di base di terminali e Docker
- Addestra un modello di rilevamento dello spam nei commenti con il codelab TensorFlow Lite Model Maker
Obiettivi didattici
- Come creare una semplice app Flutter e classificare i testi tramite TensorFlow Serving (REST e gRPC).
- Come visualizzare i risultati nell'interfaccia utente.
Che cosa ti serve
- SDK Flutter
- Configurazione di Android o iOS per Flutter
- Configurazione di Visual Studio Code (VS Code) per Flutter e Dart
- Docker
- Bash
- Compilatore del buffer di protocollo e plug-in gRPC Dart per il compilatore di protocolli (necessario solo se vuoi rigenerare lo stub gRPC autonomamente)
2. Configura l'ambiente di sviluppo Flutter
Per lo sviluppo in Flutter, per completare questo lab hai bisogno di due software: l'SDK Flutter e un editor.
Puoi eseguire il codelab utilizzando uno qualsiasi di questi dispositivi:
- Il simulatore iOS (richiede l'installazione degli strumenti Xcode).
- L'emulatore Android (richiede la configurazione in Android Studio).
- Un browser (Chrome è necessario per il debug).
- Come applicazione desktop Windows, Linux o macOS. Devi sviluppare sulla piattaforma in cui prevedi di eseguire il deployment. Pertanto, se vuoi sviluppare un'app desktop Windows, devi svilupparla su Windows per accedere alla catena di build appropriata. Esistono requisiti specifici per il sistema operativo che sono trattati in dettaglio su docs.flutter.dev/desktop.
3. Configurazione
Per scaricare il codice per questo codelab:
- Vai al repository GitHub per questo codelab.
- Fai clic su Code > Download zip per scaricare tutto il codice di questo codelab.
- Decomprimi il file ZIP scaricato per estrarre una cartella principale
codelabs-main
con tutte le risorse necessarie.
Per questo codelab, ti servono solo i file nella sottodirectory tfserving-flutter/codelab2
del repository, che contiene due cartelle:
- La cartella
starter
contiene il codice iniziale su cui si basa questo codelab. - La cartella
finished
contiene il codice completato per l'app di esempio finita.
4. Scarica le dipendenze per il progetto
- In VS Code, fai clic su File > Apri cartella e poi seleziona la cartella
starter
dal codice sorgente che hai scaricato in precedenza. - Se viene visualizzata una finestra di dialogo che ti chiede di scaricare i pacchetti richiesti per l'app iniziale, fai clic su Scarica pacchetti.
- Se non vedi questa finestra di dialogo, apri il terminale ed esegui il comando
flutter pub get
nella cartellastarter
.
5. Esegui l'app di base
- In VS Code, assicurati che l'emulatore Android o il simulatore iOS sia configurato correttamente e venga visualizzato nella barra di stato.
Ad esempio, ecco cosa vedi quando utilizzi Pixel 5 con Android Emulator:
Ecco cosa vedi quando utilizzi iPhone 13 con iOS Simulator:
- Fai clic su
Avvia debug.
Eseguire ed esplorare l'app
L'app dovrebbe avviarsi sull'emulatore Android o sul simulatore iOS. L'interfaccia utente è piuttosto semplice. È presente un campo di testo che consente all'utente di digitare il testo. L'utente può scegliere se inviare i dati al backend con REST o gRPC. Il backend utilizza un modello TensorFlow per eseguire la classificazione del testo sull'input preelaborato e restituisce il risultato della classificazione all'app client, che a sua volta aggiorna la UI.
Se fai clic su Classifica, non succede nulla perché non può ancora comunicare con il backend.
6. Esegui il deployment di un modello di classificazione del testo con TensorFlow Serving
La classificazione del testo è un'attività di machine learning molto comune che classifica i testi in categorie predefinite. In questo codelab, esegui il deployment del modello preaddestrato dal codelab Addestra un modello di rilevamento dello spam nei commenti con TensorFlow Lite Model Maker con TensorFlow Serving e chiama il backend dal frontend Flutter per classificare il testo di input come spam o non spam.
Avvia TensorFlow Serving
- Nel terminale, avvia TensorFlow Serving con Docker, ma sostituisci il segnaposto
PATH/TO/SAVEDMODEL
con il percorso assoluto della cartellamm_spam_savedmodel
sul 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 scarica automaticamente l'immagine TensorFlow Serving, il che richiede un minuto. Dopodiché, TensorFlow Serving dovrebbe avviarsi. Il log dovrebbe avere l'aspetto seguente:
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. Tokenizza la frase di input
Il backend è pronto, quindi sei quasi pronto per inviare le richieste del client a TensorFlow Serving, ma prima devi tokenizzare la frase di input. Se esamini il tensore di input del modello, puoi notare che si aspetta un elenco di 20 numeri interi anziché stringhe non elaborate. La tokenizzazione consiste nel mappare le singole parole che digiti nell'app a un elenco di numeri interi in base a un dizionario del vocabolario prima di inviarle al backend per la classificazione. Ad esempio, se digiti buy book online to learn more
, la procedura di tokenizzazione lo mappa a [32, 79, 183, 10, 224, 631, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
. I numeri specifici possono variare in base al dizionario del vocabolario.
- Nel file
lib/main.dart
, aggiungi questo codice al metodopredict()
per creare il dizionario del vocabolario_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]);
}
}
}
- Subito dopo lo snippet di codice precedente, aggiungi questo codice per implementare la tokenizzazione:
// 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;
}
}
Questo codice converte la stringa della frase in minuscolo, rimuove i caratteri non alfabetici e mappa le parole a 20 indici interi in base alla tabella del vocabolario.
8. Collega l'app Flutter a TensorFlow Serving tramite REST
Esistono due modi per inviare richieste a TensorFlow Serving:
- REST
- gRPC
Inviare richieste e ricevere risposte tramite REST
Esistono tre semplici passaggi per inviare richieste e ricevere risposte tramite REST:
- Crea la richiesta REST.
- Invia la richiesta REST a TensorFlow Serving.
- Estrai il risultato previsto dalla risposta REST e visualizza la UI.
Completa questi passaggi nel file main.dart
.
Crea e invia la richiesta REST a TensorFlow Serving
- Al momento, la funzione
predict()
non invia la richiesta REST a TensorFlow Serving. Devi implementare il ramo REST per creare una richiesta REST:
if (_connectionMode == ConnectionModeType.rest) {
// TODO: Create and send the REST request.
}
- Aggiungi questo codice al ramo 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],
}),
);
Elaborare la risposta REST di TensorFlow Serving
- Aggiungi questo codice subito dopo lo snippet di codice precedente per gestire la risposta 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');
}
Il codice di post-elaborazione estrae dalla risposta la probabilità che la frase di input sia un messaggio di spam e mostra il risultato della classificazione nell'interfaccia utente.
Esegui
- Fai clic su
Avvia debug e attendi il caricamento dell'app.
- Inserisci del testo e seleziona REST > Classifica.
9. Collega l'app Flutter a TensorFlow Serving tramite gRPC
Oltre a REST, TensorFlow Serving supporta anche gRPC.
gRPC è un framework RPC (Remote Procedure Call) moderno, open source e ad alte prestazioni che può essere eseguito in qualsiasi ambiente. Può connettere in modo efficiente i servizi all'interno e tra i data center con supporto plug-in per bilanciamento del carico, tracciamento, controllo di integrità e autenticazione. È stato osservato che gRPC è più performante di REST nella pratica.
Inviare richieste e ricevere risposte con gRPC
Per inviare richieste e ricevere risposte con gRPC, segui questi quattro semplici passaggi:
- (Facoltativo) Genera il codice stub del client gRPC.
- Crea la richiesta gRPC.
- Invia la richiesta gRPC a TensorFlow Serving.
- Estrai il risultato previsto dalla risposta gRPC e visualizza la UI.
Completa questi passaggi nel file main.dart
.
(Facoltativo) Genera il codice dello stub client gRPC
Per utilizzare gRPC con TensorFlow Serving, devi seguire il flusso di lavoro gRPC. Per saperne di più, consulta la documentazione di gRPC.
TensorFlow Serving e TensorFlow definiscono i file .proto
per te. A partire da TensorFlow e TensorFlow Serving 2.8, questi sono i file .proto
necessari:
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
- Nel terminale, vai alla cartella
starter/lib/proto/
e genera lo stub:
bash generate_grpc_stub_dart.sh
Crea la richiesta gRPC
Analogamente alla richiesta REST, crei la richiesta gRPC nel ramo gRPC.
if (_connectionMode == ConnectionModeType.rest) {
} else {
// TODO: Create and send the gRPC request.
}
- Aggiungi questo codice per creare la richiesta 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});
Nota:i nomi dei tensori di input e output potrebbero variare da modello a modello, anche se le architetture dei modelli sono le stesse. Assicurati di aggiornarli se addestri un modello personalizzato.
Invia la richiesta gRPC a TensorFlow Serving
- Aggiungi questo codice dopo lo snippet di codice precedente per inviare la richiesta gRPC a TensorFlow Serving:
// Send the gRPC request.
PredictResponse response = await _stub.predict(request);
Elaborare la risposta gRPC da TensorFlow Serving
- Aggiungi questo codice dopo lo snippet di codice precedente per implementare le funzioni di callback per gestire la risposta:
// 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');
}
Ora il codice di post-elaborazione estrae il risultato della classificazione dalla risposta e lo visualizza nell'UI.
Esegui
- Fai clic su
Avvia debug e attendi il caricamento dell'app.
- Inserisci del testo, quindi seleziona gRPC > Classifica.
10. Complimenti
Hai utilizzato TensorFlow Serving per aggiungere funzionalità di classificazione del testo alla tua app.
Nel prossimo codelab, migliorerai il modello in modo da poter rilevare messaggi di spam specifici che non possono essere rilevati dall'app attuale.