Tworzenie aplikacji Flutter do klasyfikowania tekstów

1. Zanim zaczniesz

Z tego ćwiczenia dowiesz się, jak przeprowadzać klasyfikowanie tekstu za pomocą aplikacji Flutter za pomocą TensorFlow Serve poprzez REST i gRPC.

Wymagania wstępne

Czego się nauczysz

  • Jak utworzyć prostą aplikację Flutter i sklasyfikować tekst za pomocą TensorFlow Serving (REST i gRPC).
  • Jak wyświetlać wyniki w interfejsie.

Czego potrzebujesz

2. Skonfiguruj środowisko programistyczne Flutter

Do ukończenia tego modułu potrzebujesz oprogramowania Flutter, czyli pakietu Flutter SDK i edytora.

Ćwiczenia z programowania możesz uruchomić na dowolnym z tych urządzeń:

  • Symulator iOS (wymaga zainstalowania narzędzi Xcode).
  • Emulator Androida (wymaga konfiguracji w Android Studio).
  • Przeglądarka (do przeprowadzenia debugowania wymagany jest Chrome).
  • Jako aplikacja komputerowa Windows, Linux lub macOS. Programuj na platformie, na której zamierzasz wdrożyć aplikację. Jeśli więc chcesz utworzyć aplikację komputerową na Windows, musisz ją utworzyć, by mieć dostęp do odpowiedniej łańcucha kompilacji. Istnieją szczegółowe wymagania systemowe, które szczegółowo opisujemy na stronie docs.flutter.dev/desktop.

3. Konfiguracja

Aby pobrać kod tych ćwiczeń z programowania:

  1. Przejdź do repozytorium GitHub dla tych ćwiczeń z programowania.
  2. Kliknij Kod > Pobierz plik ZIP, aby pobrać cały kod do tych ćwiczeń z programowania.

2cd45599f51fb8a2.png

  1. Rozpakuj pobrany plik ZIP, aby rozpakować folder główny aplikacji codelabs-main ze wszystkimi potrzebnymi zasobami.

Na potrzeby tego ćwiczenia z programu potrzebujesz tylko plików z podkatalogu tfserving-flutter/codelab2 repozytorium, które zawiera 2 foldery:

  • Folder starter zawiera kod początkowy, który wykorzystasz w tym ćwiczeniu z programowania.
  • Folder finished zawiera pełny kod gotowej aplikacji.

4. Pobierz zależności dla projektu

  1. W pliku VS Code kliknij File > Open folder, a następnie wybierz folder starter z pobranego wcześniej kodu źródłowego.
  2. Jeśli pojawi się okno z prośbą o pobranie pakietów wymaganych przez aplikację startową, kliknij Pobierz pakiety.
  3. Jeśli nie widzisz tego okna, otwórz terminal i uruchom polecenie flutter pub get w folderze starter.

7ada07c300f166a6.png

5. Uruchom aplikację startową

  1. W VS Code sprawdź, czy emulator Androida lub Symulator iOS jest prawidłowo skonfigurowany i pojawia się na pasku stanu.

Oto przykład tego, co widzisz, gdy używasz Pixela 5 z Emulatorem Androida:

9767649231898791.png

Oto, co możesz zobaczyć, gdy używasz iPhone'a z 13 z Symulatorem iOS:

95529e3a682268b2.png

  1. Kliknij A19a0c68bc4046e6.png Rozpocznij debugowanie.

Uruchamianie i poznawanie aplikacji

Aplikacja powinna pojawić się w emulatorze Androida lub symulatorze iOS. Interfejs jest bardzo prosty. Istnieje pole tekstowe, w którym użytkownik może wpisać tekst. Użytkownik może wybrać, czy dane mają być przesyłane do backendu z użyciem REST lub gRPC. Backend używa modelu TensorFlow, który wykonuje klasyfikację tekstu na wstępnie przetwarzanych danych wejściowych i zwraca wynik klasyfikacji do aplikacji klienckiej, co z kolei aktualizuje interfejs użytkownika.

b298f605d64dc132.png d3ef3ccd3c338108.png

Jeśli klikniesz Klasyfikuj, nic się nie stanie, ponieważ nie można jeszcze nawiązać komunikacji z backendem.

6. Wdrażanie modelu klasyfikacji tekstu za pomocą serwera w TensorFlow

Klasyfikacja tekstu to bardzo częste zadanie systemów uczących się, które klasyfikuje teksty do wstępnie zdefiniowanych kategorii. W tym ćwiczeniu z programowania wdrożysz model wytrenowany z poziomu Wytrenuj model wykrywania spamu w komentarzach za pomocą narzędzia TensorFlow Lite Kreator modeli z TensorFlow Serve i wywołaj backend z frontendu Flutter, aby sklasyfikować tekst wejściowy jako spam lub nie spam.

Rozpocznij udostępnianie TensorFlow

  • W terminalu uruchom udostępnianie TensorFlow z Dockerem, zastępując zmienną PATH/TO/SAVEDMODEL ścieżką bezwzględną folderu mm_spam_savedmodel na komputerze.
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 automatycznie pobiera najpierw obraz związany z wyświetlaniem TensorFlow, co zajmuje minutę. Po tym czasie powinno rozpocząć się udostępnianie TensorFlow. Dziennik powinien wyglądać jak ten fragment kodu:

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. Tokenowe wprowadzanie zdań

Backend jest już gotowy, więc możesz już wysyłać żądania klientów do serwera TensorFlow, ale najpierw musisz tokenizować dane wejściowe. Sprawdzając tensor wejściowy modelu, widać, że oczekuje on listy 20 liczb całkowitych, a nie nieprzetworzonych ciągów znaków. Tokenizacja polega na przypisaniu pojedynczych słów w aplikacji do listy liczb całkowitych opartych na słowniku słownikowym przed ich wysłaniem do backendu do klasyfikacji. Jeśli na przykład wpiszesz buy book online to learn more, tokenizacja zostanie zmapowana na [32, 79, 183, 10, 224, 631, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]. Konkretne liczby mogą się różnić w zależności od słownika.

  1. W pliku lib/main.dart dodaj ten kod do metody predict(), by utworzyć słownik słownikowy _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. Od razu za poprzednim fragmentem kodu dodaj ten kod, aby zaimplementować tokenizację:
// 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;
  }
}

Kod ten tworzy małym spadkiem ciąg znaków zdania, usuwa znaki inne niż alfanumeryczne i mapuje słowa na 20 indeksów liczb całkowitych na podstawie tabeli słownictwa.

8. Łączenie aplikacji Flutter z TensorFlow Serving przez REST

Istnieją dwa sposoby wysyłania żądań do obsługi TensorFlow:

  • REST
  • gRPC

Wysyłanie żądań i odbieranie odpowiedzi przez REST

Aby wysyłać żądania i otrzymywać odpowiedzi za pomocą REST, wykonaj 3 proste kroki:

  1. Utwórz żądanie REST.
  2. Wyślij żądanie REST do serwera TensorFlow Serving.
  3. Wyodrębnij przewidywany wynik z odpowiedzi REST i wyrenderuj interfejs użytkownika.

Wykonaj te czynności w pliku main.dart.

Tworzenie i wysyłanie żądania REST do udostępniania w TensorFlow

  1. Obecnie funkcja predict() nie wysyła żądania REST do serwera TensorFlow. Aby utworzyć żądanie REST, musisz wdrożyć gałąź REST:
if (_connectionMode == ConnectionModeType.rest) {
  // TODO: Create and send the REST request.

}
  1. Dodaj ten kod do gałęzi 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],
  }),
);

Przetwórz odpowiedź REST z wyświetlania w TensorFlow

  • Aby przetworzyć odpowiedź REST, dodaj ten kod tuż za poprzednim fragmentem kodu:
// 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');
}

Kod przetwarzający wyodrębnia prawdopodobieństwo, że zdanie wejściowe jest wiadomością ze spamem, i wyświetla wynik klasyfikacji w interfejsie użytkownika.

Uruchom

  1. Kliknij A19a0c68bc4046e6.png Rozpocznij debugowanie, a następnie zaczekaj na załadowanie aplikacji.
  2. Wpisz tekst i wybierz REST > Classify.

8e21d795af36d07a.png e79a0367a03c2169.png

9. Połącz aplikację Flutter z TensorFlow Serving przez gRPC

Oprócz REST, TensorFlow Serving obsługuje też gRPC.

B6F4449C2C850b0e.png

gRPC to nowoczesna platforma RPC o wysokiej wydajności, która może działać w dowolnym środowisku. Może skutecznie łączyć usługi w centrach danych i z innych, a także korzystać z wtyczek z obsługą równoważenia obciążenia, śledzenia, kontroli stanu i uwierzytelniania. Zaobserwowano, że gRPC jest w praktyce bardziej wydajny niż REST.

Wysyłanie żądań i odbieranie odpowiedzi za pomocą gRPC

Dostępne są 4 proste czynności do wysyłania żądań i odbierania odpowiedzi przy użyciu gRPC:

  1. Opcjonalnie: wygeneruj kod śledzenia klienta gRPC.
  2. Utwórz żądanie gRPC.
  3. Wyślij żądanie gRPC do wyświetlania przez TensorFlow.
  4. Wyodrębnij przewidywany wynik z odpowiedzi gRPC i wyrenderuj interfejs użytkownika.

Wykonaj te czynności w pliku main.dart.

Opcjonalnie: wygeneruj kod wewnętrzny klienta gRPC

Aby używać gRPC z udostępnianiem za pomocą TensorFlow, musisz wykonać przepływ pracy gRPC. Więcej informacji znajdziesz w dokumentacji gRPC.

a9d0e5cb543467b4.png

Pliki TensorFlow i TensorFlow definiują pliki .proto. Od TensorFlow 2.8 i TensorFlow 2.8 potrzebne są te pliki .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
  • W terminalu przejdź do folderu starter/lib/proto/ i wygeneruj atrapę:
bash generate_grpc_stub_dart.sh

Utwórz żądanie gRPC

Podobnie jak w przypadku żądania REST, żądanie gRPC tworzy się w gałęzi gRPC.

if (_connectionMode == ConnectionModeType.rest) {

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

}
  • Dodaj ten kod, aby utworzyć żądanie 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});

Uwaga: nazwy tensorów wejściowych i wyjściowych mogą się różnić w zależności od modelu, nawet jeśli architektura modelu jest taka sama. Pamiętaj o aktualizowaniu ich, jeśli trenujesz własny model.

Wyślij żądanie gRPC do udostępniania w TensorFlow

  • Dodaj ten kod po poprzednim fragmencie kodu, aby wysłać żądanie gRPC do wyświetlania w TensorFlow:
// Send the gRPC request.
PredictResponse response = await _stub.predict(request);

Przetwórz odpowiedź gRPC z udostępniania w TensorFlow

  • Dodaj ten kod po poprzednim fragmencie kodu, aby zaimplementować funkcje wywołania zwrotnego w celu obsługi odpowiedzi:
// 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');
}

Teraz kod postprocessing wyodrębnia wynik klasyfikacji z odpowiedzi i wyświetla go w interfejsie użytkownika.

Uruchom

  1. Kliknij A19a0c68bc4046e6.png Rozpocznij debugowanie, a następnie zaczekaj na załadowanie aplikacji.
  2. Wpisz tekst i wybierz gRPC > Classify.

e44e6e9a5bde2188.png 92644d723f61968c.png

10. Gratulacje

Dzięki wykorzystaniu TensorFlow dodano klasyfikację tekstu do aplikacji.

W ramach kolejnego ćwiczenia z programowania ulepszysz model, by móc wykrywać określone wiadomości ze spamem, których nie może wykryć bieżąca aplikacja.

Więcej informacji