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. 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-master
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.
3. 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
.
4. 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.
5. 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 ...
6. 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.
7. 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.
8. Conectar o app 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.
9. 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.