1. Antes de comenzar
En este codelab, aprenderás a ejecutar una inferencia de clasificación de texto desde una app de Flutter con TensorFlow Serving a través de REST y gRPC.
Requisitos previos
- Conocimientos básicos sobre el desarrollo de Flutter con Dart
- Conocimientos básicos del aprendizaje automático con TensorFlow, como el entrenamiento y la implementación
- Conocimientos básicos de las terminales y Docker
- El codelab Entrena un modelo de detección de comentarios spam con Model Maker de TensorFlow Lite
Qué aprenderás
- Cómo crear una app sencilla de Flutter y clasificar textos a través de TensorFlow Serving (REST y gRPC)
- Cómo mostrar los resultados en la IU
Requisitos
- SDK de Flutter
- Configuración de Android o iOS para Flutter
- Configuración de Visual Studio Code (VS Code) para Flutter y Dart
- Docker
- Bash
- Compilador de búfer de protocolo y complemento de Dart de gRPC para el compilador de protocolos (solo es necesario si quieres volver a generar el stub de gRPC por tu cuenta)
2. Prepárate
Para descargar el código de este codelab, haz lo siguiente:
- Navega al repositorio de GitHub de este codelab.
- Haz clic en Code > Download ZIP para descargar todo el código de este codelab.
- Descomprime el archivo ZIP descargado para descomprimir una carpeta raíz
codelabs-master
con todos los recursos que necesitas.
Para este codelab, solo necesitas los archivos del subdirectorio tfserving-flutter/codelab2
en el repositorio, que contiene dos carpetas:
- La carpeta
starter
contiene el código de inicio en el que se basa este codelab. - La carpeta
finished
contiene el código completado de la app de ejemplo finalizada.
3. Descarga las dependencias del proyecto
- En VS Code, haz clic en Archivo > Abrir carpeta y, luego, selecciona la carpeta
starter
del código fuente que descargaste antes. - Si aparece un diálogo en el que se te pide descargar los paquetes necesarios para la app de inicio, haz clic en Obtener paquetes (Get packages).
- Si no ves este diálogo, abre la terminal y, luego, ejecuta el comando
flutter pub get
en la carpetastarter
.
4. Ejecuta la app de inicio
- En VS Code, asegúrate de que Android Emulator o el simulador de iOS estén configurados correctamente y aparezcan en la barra de estado.
Por ejemplo, a continuación, se muestra lo que ves cuando usas un Pixel 5 con Android Emulator:
A continuación, se muestra lo que ves cuando usas un iPhone 13 con el simulador de iOS:
- Haz clic en Iniciar depuración.
Ejecuta y explora la app
La app debe iniciarse en Android Emulator o el simulador de iOS. La IU es bastante sencilla. Hay un campo de texto que le permite al usuario escribir el texto. El usuario puede elegir si desea enviar los datos al backend con REST o gRPC. El backend usa un modelo de TensorFlow para realizar la clasificación de texto en la entrada procesada previamente y muestra el resultado de la clasificación a la app cliente, que, a su vez, actualiza la IU.
Si haces clic en Classify, no sucede nada porque todavía no puede comunicarse con el backend.
5. Implementa un modelo de clasificación de texto con TensorFlow Serving
La clasificación de texto es una tarea de aprendizaje automático muy común, que clasifica textos en categorías predefinidas. En este codelab, implementarás el modelo previamente entrenado del codelab Entrena un modelo de detección de comentarios spam con Model Maker de TensorFlow Lite con TensorFlow Serving y llama al backend desde el frontend de Flutter para clasificar el texto de entrada en formato spam o no spam.
Inicia TensorFlow Serving
- En tu terminal, inicia TensorFlow Serving con Docker, pero reemplaza el marcador de posición
PATH/TO/SAVEDMODEL
por la ruta de acceso absoluta de la carpetamm_spam_savedmodel
en tu computadora.
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 descarga automáticamente la imagen de TensorFlow Serving primero, lo cual tarda un minuto. Luego, TensorFlow Serving debería iniciarse. El registro debería verse como este fragmento de código:
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. Asigna un token a la oración de entrada
Ahora el backend está preparado, así que estás casi listo para enviar solicitudes de clientes a TensorFlow Serving, pero primero debes asignar un token a la oración de entrada. Si inspeccionas el tensor de entrada del modelo, verás que espera una lista de 20 números enteros, en lugar de strings sin procesar. La asignación de token se produce cuando asignas las palabras individuales que escribes en la app a una lista de números enteros basada en un diccionario de vocabulario antes de enviarlas al backend para su clasificación. Por ejemplo, si escribes buy book online to learn more
, el proceso de asignación de token lo asigna a [32, 79, 183, 10, 224, 631, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
. Las cantidades específicas pueden variar en función del diccionario de vocabulario.
- En el archivo
lib/main.dart
, agrega este código al métodopredict()
a fin de compilar el diccionario de vocabulario_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]);
}
}
}
- Inmediatamente después del fragmento de código anterior, agrega este código para implementar la asignación de token:
// 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;
}
}
Este código coloca la string de la oración en minúsculas, quita los caracteres que no son alfabéticos y asigna las palabras a 20 índices enteros según la tabla de vocabulario.
7. Conecta la app de Flutter con TensorFlow Serving mediante REST
Existen dos métodos de enviar solicitudes a TensorFlow Serving:
- REST
- gRPC
Envía solicitudes y recibe respuestas a través de REST
Existen tres pasos sencillos para enviar solicitudes y recibir respuestas a través de REST:
- Crea la solicitud REST.
- Envía la solicitud REST a TensorFlow Serving.
- Extrae el resultado previsto de la respuesta de REST y renderiza la IU.
Completa estos pasos en el archivo main.dart
.
Crea y envía la solicitud de REST a TensorFlow Serving
- En este momento, la función
predict()
no envía la solicitud de REST a TensorFlow Serving. Debes implementar la rama de REST para crear una solicitud de REST:
if (_connectionMode == ConnectionModeType.rest) {
// TODO: Create and send the REST request.
}
- Agrega este código a la rama de 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],
}),
);
Procesa la respuesta de REST de TensorFlow Serving
- Agrega este código inmediatamente después del fragmento de código anterior para controlar la respuesta de 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');
}
El código de procesamiento posterior extrae la probabilidad de que la oración de entrada sea un mensaje de spam de la respuesta y muestra el resultado de la clasificación en la IU.
Ejecuta la app
- Haz clic en Iniciar depuración y espera a que se cargue la app.
- Ingresa texto y, luego, selecciona REST > Classify.
8. Conecta la app de Flutter con TensorFlow Serving mediante gRPC
Además de REST, TensorFlow Serving también admite gRPC.
gRPC es un framework de llamada de procedimiento remoto (RPC) moderno de código abierto y alto rendimiento, que se puede ejecutar en cualquier entorno. Puede conectar de forma eficaz servicios en centros de datos (y entre ellos) y ofrece compatibilidad conectable con balanceo de cargas, seguimiento, verificación de estado y autenticación. Se observó que gRPC tiene un mejor rendimiento que REST en la práctica.
Envía solicitudes y recibe respuestas con gRPC
Existen cuatro pasos sencillos para enviar solicitudes y recibir respuestas con gRPC:
- Genera el código de stub del cliente de gRPC (opcional).
- Crea la solicitud de gRPC.
- Envía la solicitud de gRPC a TensorFlow Serving.
- Extrae el resultado previsto de la respuesta de gRPC y renderiza la IU.
Completa estos pasos en el archivo main.dart
.
Genera el código de stub del cliente de gRPC (opcional)
Para usar gRPC con TensorFlow Serving, debes seguir el flujo de trabajo de gRPC. Si quieres obtener más información, consulta la documentación de gRPC.
TensorFlow Serving y TensorFlow definen los archivos .proto
por ti. A partir de TensorFlow y TensorFlow Serving 2.8, se necesitan estos archivos .proto
:
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
- En tu terminal, navega a la carpeta
starter/lib/proto/
y genera el stub:
bash generate_grpc_stub_dart.sh
Crea la solicitud de gRPC
Al igual que con la solicitud de REST, debes crear la solicitud de gRPC en la rama de gRPC.
if (_connectionMode == ConnectionModeType.rest) {
} else {
// TODO: Create and send the gRPC request.
}
- Agrega este código para crear la solicitud de 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: Los nombres de los tensores de entrada y salida podrían diferir de un modelo a otro, incluso si las arquitecturas del modelo son iguales. Asegúrate de actualizarlas si entrenas tu propio modelo.
Envía la solicitud de gRPC a TensorFlow Serving
- Agrega este código después del fragmento de código anterior para enviar la solicitud de gRPC a TensorFlow Serving:
// Send the gRPC request.
PredictResponse response = await _stub.predict(request);
Procesa la respuesta de gRPC de TensorFlow Serving
- Agrega este código después del fragmento de código anterior para implementar las funciones de devolución de llamada a fin de controlar la respuesta:
// 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');
}
Ahora, el código de procesamiento posterior extrae el resultado de la clasificación de la respuesta y lo muestra en la IU.
Ejecuta la app
- Haz clic en Iniciar depuración y espera a que se cargue la app.
- Ingresa texto y, luego, selecciona gRPC > Classify.
9. Felicitaciones
Usaste TensorFlow Serving para agregar capacidades de clasificación de texto a tu app.
En el siguiente codelab, mejorarás el modelo, de manera que puedas detectar mensajes de spam específicos que la app actual no puede detectar.