Создайте приложение Flutter для классификации текстов

1. Прежде чем начать

В этой лабораторной работе вы узнаете, как выполнить вывод классификации текста из приложения Flutter с помощью TensorFlow Serving через REST и gRPC.

Предпосылки

Чему вы научитесь

  • Как создать простое приложение Flutter и классифицировать тексты с помощью TensorFlow Serving (REST и gRPC).
  • Как отобразить результаты в пользовательском интерфейсе.

Что вам понадобится

2. Настройте среду разработки Flutter

Для разработки на Flutter вам понадобятся два программных обеспечения для выполнения этой лабораторной работы: Flutter SDK и редактор .

Вы можете запустить практическую работу, используя любое из этих устройств:

  • Симулятор iOS (требуется установка инструментов Xcode).
  • Эмулятор Android (требуется настройка в Android Studio).
  • Браузер (для отладки требуется Chrome).
  • В качестве настольного приложения для Windows , Linux или macOS . Разработка должна осуществляться на той платформе, на которой планируется её развертывание. Таким образом, если вы хотите разработать настольное приложение для Windows, вам необходимо разрабатывать его в Windows, чтобы получить доступ к соответствующей цепочке сборки. Существуют специфические требования к операционной системе, которые подробно описаны на сайте docs.flutter.dev/desktop .

3. Приступайте к настройке

Чтобы загрузить код для этой лабораторной работы:

  1. Перейдите в репозиторий GitHub для этой лабораторной работы.
  2. Нажмите Код > Загрузить zip , чтобы загрузить весь код для этой лабораторной работы.

2cd45599f51fb8a2.png

  1. Распакуйте загруженный zip-файл, чтобы распаковать корневую папку codelabs-main со всеми необходимыми вам ресурсами.

Для этой лабораторной работы вам понадобятся только файлы в подкаталоге tfserving-flutter/codelab2 в репозитории, который содержит две папки:

  • В starter папке содержится стартовый код, на основе которого вы будете строить эту лабораторную работу.
  • finished папка содержит готовый код для готового примера приложения.

4. Загрузите зависимости для проекта.

  1. В VS Code нажмите Файл > Открыть папку , а затем выберите starter папку из исходного кода, который вы скачали ранее.
  2. Если появится диалоговое окно с предложением загрузить необходимые пакеты для стартового приложения, нажмите кнопку Получить пакеты .
  3. Если вы не видите это диалоговое окно, откройте терминал и выполните команду flutter pub get в starter папке.

7ada07c300f166a6.png

5. Запустите стартовое приложение.

  1. В VS Code убедитесь, что Android Emulator или iOS Simulator правильно настроен и отображается в строке состояния.

Например, вот что вы видите при использовании Pixel 5 с эмулятором Android:

9767649231898791.png

Вот что вы видите при использовании iPhone 13 с iOS Simulator:

95529e3a682268b2.png

  1. Щелкните a19a0c68bc4046e6.png Начать отладку .

Запустите и изучите приложение

Приложение должно запускаться на вашем эмуляторе Android или симуляторе iOS. Пользовательский интерфейс довольно прост. Есть текстовое поле, в которое пользователь может ввести текст. Пользователь может выбрать, отправлять ли данные на бэкенд с помощью REST или gRPC. Бэкенд использует модель TensorFlow для классификации текста на основе предварительно обработанных входных данных и возвращает результат классификации клиентскому приложению, которое, в свою очередь, обновляет пользовательский интерфейс.

b298f605d64dc132.pngd3ef3ccd3c338108.png

Если нажать кнопку «Классифицировать» , ничего не произойдет, поскольку система пока не может связаться с бэкэндом.

6. Разверните модель классификации текста с помощью TensorFlow Serving

Классификация текста — очень распространённая задача машинного обучения, которая классифицирует тексты по предопределённым категориям. В этой лабораторной работе вы развернёте предварительно обученную модель из лабораторной работы «Обучение модели обнаружения спама в комментариях с помощью TensorFlow Lite Model Maker» с TensorFlow Serving и вызовете бэкенд из фронтенда Flutter, чтобы классифицировать входящий текст как спам или нет .

Запустить TensorFlow Serving

  • В терминале запустите TensorFlow Serving с Docker, но замените заполнитель PATH/TO/SAVEDMODEL на абсолютный путь к папке mm_spam_savedmodel на вашем компьютере.
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 автоматически загрузит образ TensorFlow Serving, что займёт около минуты. После этого TensorFlow Serving должен запуститься. Журнал должен выглядеть следующим образом:

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. Токенизировать входящее предложение

Бэкэнд готов, поэтому вы почти готовы отправлять клиентские запросы в TensorFlow Serving, но сначала вам нужно токенизировать входное предложение. Если вы проверите входной тензор модели, вы увидите, что он ожидает список из 20 целых чисел вместо необработанных строк. Токенизация — это когда вы сопоставляете отдельные слова, которые вы вводите в приложении, со списком целых чисел на основе словарного словаря, прежде чем отправить их на бэкэнд для классификации. Например, если вы вводите buy book online to learn more , процесс токенизации сопоставляет его с [32, 79, 183, 10, 224, 631, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] . Конкретные числа могут различаться в зависимости от словарного словаря.

  1. В файле lib/main.dart добавьте этот код в метод predict() для создания словаря слов _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. Сразу после предыдущего фрагмента кода добавьте этот код для реализации токенизации:
// 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;
  }
}

Этот код переводит строку предложения в нижний регистр, удаляет символы, не входящие в алфавитный список, и сопоставляет слова с 20 целочисленными индексами на основе таблицы словаря.

8. Подключите приложение Flutter к TensorFlow Serving через REST.

Существует два способа отправки запросов в TensorFlow Serving:

  • ОТДЫХ
  • gRPC

Отправка запросов и получение ответов через REST

Для отправки запросов и получения ответов через REST необходимо выполнить три простых шага:

  1. Создайте REST-запрос.
  2. Отправьте REST-запрос в TensorFlow Serving.
  3. Извлеките прогнозируемый результат из ответа REST и отобразите пользовательский интерфейс.

Эти шаги выполняются в файле main.dart .

Создайте и отправьте REST-запрос в TensorFlow Serving

  1. В настоящее время функция predict() не отправляет REST-запрос в TensorFlow Serving. Для создания REST-запроса необходимо реализовать ветку REST:
if (_connectionMode == ConnectionModeType.rest) {
  // TODO: Create and send the REST request.

}
  1. Добавьте этот код в ветку 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],
  }),
);

Обработать ответ REST от TensorFlow Serving

  • Добавьте этот код сразу после предыдущего фрагмента кода для обработки ответа 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');
}

Код постобработки извлекает из ответа вероятность того, что входящее предложение является спам-сообщением, и отображает результат классификации в пользовательском интерфейсе.

Запусти это

  1. Щелкните a19a0c68bc4046e6.png Начните отладку и дождитесь загрузки приложения.
  2. Введите текст и выберите REST > Классифицировать .

8e21d795af36d07a.pnge79a0367a03c2169.png

9. Подключите приложение Flutter к TensorFlow Serving через gRPC.

Помимо REST, TensorFlow Serving также поддерживает gRPC .

b6f4449c2c850b0e.png

gRPC — это современная высокопроизводительная платформа удалённого вызова процедур (RPC) с открытым исходным кодом, которая может работать в любой среде. Она обеспечивает эффективное подключение сервисов внутри и между центрами обработки данных благодаря подключаемым модулям для балансировки нагрузки, трассировки, проверки работоспособности и аутентификации. На практике gRPC демонстрирует более высокую производительность, чем REST.

Отправка запросов и получение ответов с помощью gRPC

Для отправки запросов и получения ответов с помощью gRPC необходимо выполнить четыре простых шага:

  1. Необязательно: сгенерируйте заглушку кода клиента gRPC.
  2. Создайте запрос gRPC.
  3. Отправьте запрос gRPC в TensorFlow Serving.
  4. Извлеките прогнозируемый результат из ответа gRPC и отобразите пользовательский интерфейс.

Эти шаги выполняются в файле main.dart .

Необязательно: сгенерируйте заглушку кода клиента gRPC.

Чтобы использовать gRPC с TensorFlow Serving, необходимо следовать рабочему процессу gRPC. Подробнее см. в документации gRPC .

a9d0e5cb543467b4.png

TensorFlow Serving и TensorFlow определяют файлы .proto автоматически. Начиная с TensorFlow и TensorFlow Serving версии 2.8, необходимы следующие файлы .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
  • В терминале перейдите в папку starter/lib/proto/ и сгенерируйте заглушку:
bash generate_grpc_stub_dart.sh

Создайте запрос gRPC

Подобно запросу REST, запрос gRPC создается в ветке gRPC.

if (_connectionMode == ConnectionModeType.rest) {

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

}
  • Добавьте этот код для создания запроса 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});

Примечание: Имена входных и выходных тензоров могут различаться в разных моделях, даже если их архитектура одинакова. Обязательно обновите их, если вы обучаете собственную модель.

Отправьте запрос gRPC в TensorFlow Serving

  • Добавьте этот код после предыдущего фрагмента кода для отправки запроса gRPC в TensorFlow Serving:
// Send the gRPC request.
PredictResponse response = await _stub.predict(request);

Обработать ответ gRPC от TensorFlow Serving

  • Добавьте этот код после предыдущего фрагмента кода, чтобы реализовать функции обратного вызова для обработки ответа:
// 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');
}

Теперь код постобработки извлекает результат классификации из ответа и отображает его в пользовательском интерфейсе.

Запусти это

  1. Щелкните a19a0c68bc4046e6.png Начните отладку и дождитесь загрузки приложения.
  2. Введите текст, а затем выберите gRPC > Классифицировать .

e44e6e9a5bde2188.png92644d723f61968c.png

10. Поздравления

Вы использовали TensorFlow Serving для добавления возможностей классификации текста в свое приложение!

В следующей лабораторной работе вы улучшите модель, чтобы иметь возможность обнаруживать определенные спам-сообщения, которые не могут быть обнаружены текущим приложением.

Узнать больше