1. Zanim zaczniesz
W tym module dowiesz się, jak uruchamiać wnioskowanie klasyfikacji tekstu z aplikacji Flutter za pomocą TensorFlow Serving przez REST i gRPC.
Wymagania wstępne
- Podstawowa wiedza na temat tworzenia aplikacji w Flutterze za pomocą języka Dart.
- Podstawowa wiedza o uczeniu maszynowym za pomocą TensorFlow, np. różnice między trenowaniem a wdrażaniem.
- Podstawowa znajomość terminali i Dockera
- Ćwiczenia z programowania dotyczące trenowania modelu wykrywania spamu w komentarzach za pomocą narzędzia TensorFlow Lite Model Maker
Czego się nauczysz
- Jak utworzyć prostą aplikację Flutter i klasyfikować teksty za pomocą TensorFlow Serving (REST i gRPC).
- Jak wyświetlać wyniki w interfejsie.
Czego potrzebujesz
- Pakiet SDK Flutter
- Konfiguracja Android lub iOS w przypadku Fluttera
- Konfigurowanie Visual Studio Code (VS Code) na potrzeby Flutter i Dart
- Docker
- Bash
- Kompilator bufora protokołu i wtyczka gRPC Dart do kompilatora protokołu (wymagane tylko wtedy, gdy chcesz samodzielnie ponownie wygenerować stub gRPC)
2. Konfigurowanie środowiska programistycznego Fluttera
Aby ukończyć ten moduł, potrzebujesz 2 programów: pakietu Flutter SDK i edytora.
Codelab 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 debugowania wymagana jest Chrome);
- Jako aplikacja na komputery z systemem Windows, Linux lub macOS. Musisz tworzyć aplikację na platformie, na której zamierzasz ją wdrożyć. Jeśli chcesz opracować aplikację na komputery z systemem Windows, musisz to zrobić na komputerze z tym systemem, aby mieć dostęp do odpowiedniego łańcucha kompilacji. Wymagania dotyczące poszczególnych systemów operacyjnych są szczegółowo opisane na stronie docs.flutter.dev/desktop.
3. Konfiguracja
Aby pobrać kod do tego ćwiczenia:
- Otwórz repozytorium GitHub dla tego laboratorium.
- Aby pobrać cały kod do tych ćwiczeń z programowania, kliknij Code > Download zip (Kod > Pobierz plik ZIP).
- Rozpakuj pobrany plik ZIP, aby wyodrębnić folder główny
codelabs-main
ze wszystkimi potrzebnymi zasobami.
W tym ćwiczeniu potrzebne są tylko pliki z podkatalogu tfserving-flutter/codelab2
w repozytorium, który zawiera 2 foldery:
- Folder
starter
zawiera kod początkowy, na którym będziesz pracować w tym laboratorium. - Folder
finished
zawiera gotowy kod ukończonej przykładowej aplikacji.
4. Pobierz zależności projektu
- W VS Code kliknij File > Open folder (Plik > Otwórz folder), a następnie wybierz folder
starter
z pobranego wcześniej kodu źródłowego. - Jeśli pojawi się okno z prośbą o pobranie wymaganych pakietów dla aplikacji startowej, kliknij Pobierz pakiety.
- Jeśli nie widzisz tego okna, otwórz terminal i uruchom polecenie
flutter pub get
w folderzestarter
.
5. Uruchamianie aplikacji wyjściowej
- W VS Code sprawdź, czy emulator Androida lub symulator iOS jest prawidłowo skonfigurowany i wyświetla się na pasku stanu.
Oto przykład tego, co zobaczysz, gdy użyjesz Pixela 5 z emulatorem Androida:
Oto, co zobaczysz, gdy użyjesz iPhone'a 13 z symulatorem iOS:
- Kliknij
Rozpocznij debugowanie.
Uruchamianie i poznawanie aplikacji
Aplikacja powinna się uruchomić w emulatorze Androida lub symulatorze iOS. Interfejs jest dość prosty. Jest tam pole tekstowe, w którym użytkownik może wpisać tekst. Użytkownik może wybrać, czy chce wysyłać dane do backendu za pomocą REST czy gRPC. Backend wykorzystuje model TensorFlow do klasyfikowania tekstu na podstawie wstępnie przetworzonych danych wejściowych i zwraca wynik klasyfikacji do aplikacji klienckiej, która z kolei aktualizuje interfejs.
Jeśli klikniesz Classify (Klasyfikuj), nic się nie stanie, ponieważ nie można jeszcze nawiązać komunikacji z backendem.
6. Wdrażanie modelu klasyfikacji tekstu za pomocą TensorFlow Serving
Klasyfikacja tekstu to bardzo popularne zadanie uczenia maszynowego, które polega na klasyfikowaniu tekstów w wstępnie zdefiniowanych kategoriach. W tych ćwiczeniach z programowania wdrożysz wstępnie wytrenowany model z ćwiczeń z programowania dotyczących trenowania modelu wykrywania spamu w komentarzach za pomocą narzędzia TensorFlow Lite Model Maker za pomocą TensorFlow Serving i wywołasz backend z frontendu Flutter, aby sklasyfikować tekst wejściowy jako spam lub nie spam.
Uruchamianie TensorFlow Serving
- W terminalu uruchom TensorFlow Serving za pomocą Dockera, ale zastąp obiekt zastępczy
PATH/TO/SAVEDMODEL
bezwzględną ścieżką do folderumm_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 najpierw automatycznie pobierze obraz TensorFlow Serving, co zajmie minutę. Następnie powinna się uruchomić usługa TensorFlow Serving. 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. Tokenizacja zdania wejściowego
Backend jest już gotowy, więc możesz prawie wysyłać żądania klienta do TensorFlow Serving, ale najpierw musisz podzielić zdanie wejściowe na tokeny. Jeśli sprawdzisz tensor wejściowy modelu, zobaczysz, że oczekuje on listy 20 liczb całkowitych zamiast surowych ciągów znaków. Tokenizacja polega na mapowaniu poszczególnych słów wpisywanych w aplikacji na listę liczb całkowitych na podstawie słownika słownictwa przed wysłaniem ich do backendu w celu klasyfikacji. Jeśli na przykład wpiszesz buy book online to learn more
, proces tokenizacji zmapuje go 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.
- W pliku
lib/main.dart
dodaj ten kod do metodypredict()
, aby utworzyć słownik_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]);
}
}
}
- Bezpośrednio po poprzednim fragmencie 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;
}
}
Ten kod zamienia ciąg znaków zdania na małe litery, usuwa znaki niealfabetyczne i mapuje słowa na 20 indeksów całkowitych na podstawie tabeli słownictwa.
8. Łączenie aplikacji Flutter z TensorFlow Serving za pomocą interfejsu REST
Żądania do TensorFlow Serving można wysyłać na 2 sposoby:
- REST
- gRPC
Wysyłanie żądań i otrzymywanie odpowiedzi za pomocą interfejsu REST
Wysyłanie żądań i otrzymywanie odpowiedzi za pomocą interfejsu REST odbywa się w 3 prostych krokach:
- Utwórz żądanie REST.
- Wyślij żądanie REST do TensorFlow Serving.
- Wyodrębnij przewidywany wynik z odpowiedzi REST i wyświetl interfejs.
Te czynności wykonuje się w pliku main.dart
.
Utwórz i wyślij żądanie REST do TensorFlow Serving
- Obecnie funkcja
predict()
nie wysyła żądania REST do TensorFlow Serving. Aby utworzyć żądanie REST, musisz zaimplementować gałąź REST:
if (_connectionMode == ConnectionModeType.rest) {
// TODO: Create and send the REST request.
}
- 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],
}),
);
Przetwarzanie odpowiedzi REST z TensorFlow Serving
- Aby obsłużyć odpowiedź interfejsu REST, dodaj ten kod zaraz po poprzednim fragmencie 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 przetwarzania końcowego wyodrębnia z odpowiedzi prawdopodobieństwo, że zdanie wejściowe jest spamem, i wyświetla wynik klasyfikacji w interfejsie.
Uruchom
- Kliknij
Rozpocznij debugowanie, a następnie poczekaj na wczytanie aplikacji.
- Wpisz tekst, a potem kliknij REST > Classify (REST > Klasyfikuj).
9. Łączenie aplikacji Flutter z TensorFlow Serving za pomocą gRPC
Oprócz REST TensorFlow Serving obsługuje też gRPC.
gRPC to nowoczesna, otwarta i wydajna platforma zdalnego wywoływania procedur (RPC), która może działać w dowolnym środowisku. Umożliwia wydajne łączenie usług w centrach danych i między nimi dzięki wtyczkom do równoważenia obciążenia, śledzenia, sprawdzania stanu i uwierzytelniania. Zaobserwowano, że w praktyce gRPC jest wydajniejszy niż REST.
Wysyłanie żądań i otrzymywanie odpowiedzi za pomocą gRPC
Aby wysyłać żądania i otrzymywać odpowiedzi za pomocą gRPC, wykonaj 4 proste czynności:
- Opcjonalnie: wygeneruj kod namiestnika klienta gRPC.
- Utwórz żądanie gRPC.
- Wysyłanie żądania gRPC do TensorFlow Serving.
- Wyodrębnij prognozowany wynik z odpowiedzi gRPC i wyświetl interfejs.
Te czynności wykonuje się w pliku main.dart
.
Opcjonalnie: generowanie kodu namiestnika klienta gRPC
Aby używać gRPC z TensorFlow Serving, musisz postępować zgodnie z procesem gRPC. Więcej informacji znajdziesz w dokumentacji gRPC.
TensorFlow Serving i TensorFlow definiują za Ciebie pliki .proto
. W TensorFlow i TensorFlow Serving w wersji 2.8 wymagane są te .proto
pliki:
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 otwórz folder
starter/lib/proto/
i wygeneruj stub:
bash generate_grpc_stub_dart.sh
Tworzenie żądania gRPC
Podobnie jak w przypadku żądania REST, żądanie gRPC tworzysz w gałęzi gRPC.
if (_connectionMode == ConnectionModeType.rest) {
} else {
// TODO: Create and send the gRPC request.
}
- Aby utworzyć żądanie gRPC, dodaj ten kod:
//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 architektury modeli są takie same. Jeśli wytrenujesz własny model, pamiętaj o ich aktualizacji.
Wysyłanie żądania gRPC do TensorFlow Serving
- Aby wysłać żądanie gRPC do TensorFlow Serving, dodaj ten kod po poprzednim fragmencie kodu:
// Send the gRPC request.
PredictResponse response = await _stub.predict(request);
Przetwarzanie odpowiedzi gRPC z TensorFlow Serving
- Aby zaimplementować wywołania zwrotne do obsługi odpowiedzi, dodaj ten kod po poprzednim fragmencie kodu:
// 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');
}
Kod przetwarzania końcowego wyodrębnia teraz z odpowiedzi wynik klasyfikacji i wyświetla go w interfejsie.
Uruchom
- Kliknij
Rozpocznij debugowanie, a następnie poczekaj na wczytanie aplikacji.
- Wpisz tekst, a następnie wybierz gRPC > Classify (gRPC > Klasyfikuj).
10. Gratulacje
Używasz TensorFlow Serving do dodawania do aplikacji funkcji klasyfikacji tekstu.
W kolejnych zajęciach z programowania ulepszysz model, aby wykrywać konkretne wiadomości spamowe, których nie wykrywa obecna aplikacja.