1. Antes de começar
Neste codelab, você aprenderá a executar uma inferência de classificação de texto usando um app do Flutter com o TensorFlow Serving usando REST e gRPC.
Pré-requisitos
- Conhecimento básico de desenvolvimento do Flutter com Dart
- Conhecimento básico de machine learning com o TensorFlow, como treinamento ou implantação
- Conhecimento básico de terminais e Docker
- Codelab Treinar um modelo de detecção de spam de comentários com o TensorFlow Lite Model Maker
O que você vai aprender
- Como criar um app do Flutter simples e classificar textos usando o TensorFlow Serving (REST e gRPC).
- Como exibir os resultados na IU.
Pré-requisitos
- SDK do Flutter
- Configuração do Android ou iOS para o Flutter
- Configuração do Visual Studio Code (VS Code) para Flutter e Dart
- Docker
- Bash
- Compilador de buffer de protocolo e plug-in gRPC Dart para compilador de protocolo (apenas necessário se você quiser gerar o stub do gRPC novamente)
2. Configurar o ambiente de desenvolvimento do Flutter
Para o desenvolvimento em Flutter, você precisa de dois softwares para concluir este laboratório: o SDK do Flutter e um editor.
É possível executar o codelab usando qualquer um destes dispositivos:
- O simulador para iOS, que exige a instalação de ferramentas do Xcode.
- O Android Emulator, que requer configuração no Android Studio.
- Um navegador (o Chrome é necessário para depuração).
- Como um aplicativo para computador Windows, Linux ou macOS. Você precisa desenvolver na plataforma em que planeja implantar. Portanto, se quiser desenvolver um app para um computador Windows, você terá que desenvolver no Windows para acessar a cadeia de builds adequada. Há requisitos específicos de cada sistema operacional que são abordados em detalhes em docs.flutter.dev/desktop.
3. Começar a configuração
Para fazer o download do código para este codelab, faça o seguinte:
- Navegue até o repositório do GitHub deste codelab.
- Clique em Code > Download zip para fazer o download de todo o código para este codelab.
- Descompacte o arquivo ZIP baixado para descompactar uma pasta raiz
codelabs-main
com todos os recursos necessários.
Neste codelab, você só precisará dos arquivos no subdiretório tfserving-flutter/codelab2
do repositório, que contém duas pastas:
- A pasta
starter
contém o código inicial que você usará como base para este codelab. - A pasta
finished
contém o código concluído do app de exemplo finalizado.
4. Fazer o download das dependências do projeto
- No VS Code, clique em File > Open Folder e selecione a pasta
starter
no código-fonte que você salvou anteriormente. - Se aparecer uma caixa de diálogo pedindo para você fazer o download dos pacotes necessários para o app inicial, clique em Get packages.
- Se a caixa de diálogo não aparecer, abra o terminal e execute o comando
flutter pub get
na pastastarter
.
5. Executar o app inicial
- No VS Code, verifique se o Android Emulator ou simulador de iOS está configurado corretamente e aparece na barra de status.
Por exemplo, veja o que você verá ao usar o Pixel 5 com o Android Emulator:
Veja o que você verá ao usar o iPhone 13 com o iOS Simulator:
- Clique em
Iniciar depuração.
Executar e explorar o app
O app será iniciado no Android Emulator ou no iOS Simulator. A IU é bem direta. Há um campo de texto que permite ao usuário digitar o texto. O usuário pode escolher se quer enviar os dados para o back-end com REST ou gRPC. O back-end usa um modelo do TensorFlow para realizar a classificação de texto na entrada pré-processada e retorna o resultado da classificação para o app cliente, que, por sua vez, atualiza a IU.
Se você clicar em Classificar, nada acontecerá porque ainda não há comunicação com o back-end.
6. Implantar um modelo de classificação de texto com o TensorFlow Serving
A classificação de texto é uma tarefa de machine learning muito comum que classifica textos em categorias predefinidas. Neste codelab, você vai implantar o modelo pré-treinado do codelab Treinar um modelo de detecção de spam de comentários com o TensorFlow Lite Model Maker com o TensorFlow Serving e chamar o back-end no front-end do Flutter para classificar o texto de entrada como spam ou não é spam.
Iniciar o TensorFlow Serving
- No seu terminal, inicie o TensorFlow Serving com o Docker, mas substitua o marcador de posição
PATH/TO/SAVEDMODEL
pelo caminho absoluto da pastamm_spam_savedmodel
no seu computador.
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
Primeiro, o Docker faz o download automático da imagem do TensorFlow Serving em um minuto. Depois disso, o TensorFlow Serving será iniciado. O registro será semelhante a este snippet 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 ...
7. Tokenizar a frase de entrada
O back-end está pronto, mas, antes de enviar solicitações de cliente ao TensorFlow Serving, primeiro você precisa tokenizar a frase de entrada. Se você inspecionar o tensor de entrada do modelo, verá que ele espera uma lista de 20 números inteiros em vez de strings brutas. A tokenização ocorre quando você mapeia as palavras individuais digitadas no app para uma lista de números inteiros com base em um dicionário de vocabulário antes de enviá-las ao back-end para classificação. Por exemplo, se você digitar buy book online to learn more
, o processo de tokenização o mapeará para [32, 79, 183, 10, 224, 631, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
. Os números específicos podem variar de acordo com o dicionário do vocabulário.
- No arquivo
lib/main.dart
, adicione esse código ao métodopredict()
para criar o dicionário de vocabulário_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]);
}
}
}
- Imediatamente após o snippet de código anterior, inclua este código para implementar a tokenização:
// 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;
}
}
Esse código reduz a string de frase, remove caracteres não alfabéticos e mapeia as palavras para 20 índices inteiros com base na tabela do vocabulário.
8. Conectar o app do Flutter com o TensorFlow Serving pelo REST
Há duas maneiras de enviar solicitações ao TensorFlow Serving:
- REST
- gRPC
Enviar solicitações e receber respostas pelo REST
Há três etapas simples para enviar solicitações e receber respostas pelo REST:
- Crie a solicitação REST.
- Envie a solicitação REST para o TensorFlow Serving.
- Extraia o resultado previsto da resposta REST e renderize a IU.
Realize essas etapas no arquivo main.dart
.
Criar e enviar a solicitação REST para o TensorFlow Serving
- No momento, a função
predict()
não envia a solicitação REST para o TensorFlow Serving. Você precisa implementar a ramificação REST para criar uma solicitação desse tipo:
if (_connectionMode == ConnectionModeType.rest) {
// TODO: Create and send the REST request.
}
- Adicione este código à ramificação 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],
}),
);
Processar a resposta REST do TensorFlow Serving
- Adicione este código logo após o snippet de código anterior para processar a resposta 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');
}
O código de pós-processamento extrai a probabilidade de a frase de entrada ser uma mensagem de spam na resposta e exibe o resultado da classificação na IU.
Executar
- Clique em
Start debugging e aguarde o app carregar.
- Digite um texto e selecione REST > Classify.
9. Conectar o app do Flutter com o TensorFlow Serving pelo gRPC
Além do REST, o TensorFlow Serving também é compatível com o gRPC.
O gRPC é um framework de chamada de procedimento remoto (RPC) moderno, de código aberto e de alta performance que pode ser executado em qualquer ambiente. Ele pode conectar serviços de forma eficiente em data centers e com compatibilidade conectável para balanceamento de carga, rastreamento, verificação de integridade e autenticação. Observamos que o gRPC tem uma performance melhor do que o REST na prática.
Enviar solicitações e receber respostas com o gRPC
Há quatro etapas simples para enviar solicitações e receber respostas com o gRPC:
- Opcional: gerar o código stub do cliente gRPC
- Criar a solicitação gRPC
- Enviar a solicitação gRPC para o TensorFlow Serving
- Extrair o resultado previsto da resposta gRPC e renderizar a IU
Realize essas etapas no arquivo main.dart
.
Opcional: gerar o código stub do cliente gRPC
Para usar o gRPC com o TensorFlow Serving, é necessário seguir o fluxo de trabalho do gRPC. Caso queira saber mais, consulte a documentação do gRPC.
O TensorFlow Serving e o TensorFlow definem os arquivos .proto
automaticamente. No TensorFlow e no TensorFlow Serving 2.8, os seguintes arquivos .proto
são necessários:
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
- No terminal, navegue até a pasta
starter/lib/proto/
e gere o stub:
bash generate_grpc_stub_dart.sh
Criar a solicitação gRPC
Semelhante à solicitação REST, você cria a solicitação gRPC na ramificação gRPC.
if (_connectionMode == ConnectionModeType.rest) {
} else {
// TODO: Create and send the gRPC request.
}
- Adicione este código para criar a solicitação 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});
Observação: os nomes dos tensores de entrada e saída podem ser diferentes para cada modelo, mesmo que as arquiteturas sejam iguais. Atualize-os ao treinar seu próprio modelo.
Enviar a solicitação gRPC para o TensorFlow Serving
- Adicione este código após o snippet de código anterior para enviar a solicitação gRPC ao TensorFlow Serving:
// Send the gRPC request.
PredictResponse response = await _stub.predict(request);
Processar a resposta gRPC do TensorFlow Serving
- Adicione este código após o snippet de código anterior para implementar as funções de callback e gerenciar a resposta:
// 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');
}
Agora, o código de pós-processamento extrai o resultado da classificação da resposta e o exibe na IU.
Executar
- Clique em
Start debugging e aguarde o app carregar.
- Digite um texto e selecione gRPC > Classify.
10. Parabéns
Você usou o TensorFlow Serving para adicionar recursos de classificação de texto ao seu app.
No próximo codelab, você vai aprimorar o modelo para detectar mensagens de spam específicas que não podem ser detectadas pelo app atual.