W tym poście dowiesz się jak wdrożyć model TensorFlow do wykrywania obiektów w Google Cloud ML Engine! Pokażę jak odpowiednio przygotować model, jak utworzyć nowy model do predykcji w ML Engine i jak wykorzystać przygotowany model.
ML Engine
W Google Cloud dostępna jest usługa ML Engine pozwalająca trenować i udostępniać modele głębokich sieci neuronowych. W tym poście skupimy się na wykorzystaniu ML Engine do udostępnienia wytrenowanego przez nas modelu. Usługa pozwala na wdrażanie modeli scikit-learn, XGBoost i oczywiście Tensorflow.
Pierwszym krokiem, jeżeli nigdy wcześniej nie korzystaliśmy z ML Engine, włączamy ML Engine Models API. Po kliknięciu „Enable API” musimy odczekać kilka chwil
Mamy już włączone API dla modeli, teraz możemy utworzyć pierwszy model.
Aby utworzyć model potrzebujemy podać jego nazwę, opis jest opcjonalny. Ja jako nazwę podaję ssd_mobilenet_v2_coco. Klikamy SAVE.
Po utworzeniu naszego pierwszego modelu w ML Engine pojawia nam się lista modeli z naszym dopiero utworzonym modelem.
Aby wykorzystać tak stworzony model potrzebujemy utworzyć jego wersję, do tego przyda nam się gotowy model, więc za chwilę wrócimy do ML Engine.
Przygotowanie modelu do wykrywania obiektów
Dla uproszczenia wykorzystamy model z Object Detection Model ZOO, będzie to SSD MobileNet V2. Model ten został nauczony na zbiorze MS COCO. Żeby dodatkowo uprościć proces, niczego nie będziemy instalować na naszych komputerach, wykorzystamy Cloud Shell.
Pobierzemy i rozpakujemy model SSD MobileNet V2:
wget http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz tar zxvf ssd_mobilenet_v2_coco_2018_03_29.tar.gz
Pojawił nam się katalog ssd_mobilenet_v2_coco_2018_03_29 a w nim nasz model w 3 postaciach:
- frozen_inference_graph.pb – Zamrożony graf, wszystkie zmienne ustawione jako stałe. Model do predykcji
- model.ckpt.* – model powstały jako wynik treningu, checkpoint od którego możemy kontynuować trening
- saved_model – model który nas interesuje! Jego wagi nie są zamrożone, jest kompatybilny z ML Engine
Ale zaraz, co gdy trenuję model? Wtedy mam tylko checkpoint, tak? Tak! Całą paczkę w tym formacie możesz wygenerować za pomocą Model Exportera. Zrobimy to tutaj z uwagi na to, że modele z ZOO nie są w idealnej postaci do wdrożenia. Zobaczmy o co chodzi. Wykorzystamy narzędzie saved_model_cli.
saved_model_cli show --dir ssd_mobilenet_v2_coco_2018_03_29/saved_model --all
Rezultat powinien wyglądać mniej więcej tak:
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs: signature_def['serving_default']: The given SavedModel SignatureDef contains the following input(s): inputs['inputs'] tensor_info: dtype: DT_UINT8 shape: (-1, -1, -1, 3) name: image_tensor:0 The given SavedModel SignatureDef contains the following output(s): outputs['detection_boxes'] tensor_info: dtype: DT_FLOAT shape: (-1, 100, 4) name: detection_boxes:0 outputs['detection_classes'] tensor_info: dtype: DT_FLOAT shape: (-1, 100) name: detection_classes:0 outputs['detection_scores'] tensor_info: dtype: DT_FLOAT shape: (-1, 100) name: detection_scores:0 outputs['num_detections'] tensor_info: dtype: DT_FLOAT shape: (-1) name: num_detections:0 Method name is: tensorflow/serving/predict
Narzędzie saved_model_cli pokazuje nam informacje o wejściach i wyjściach naszego modelu w formacie saved_model. Z tych informacji możemy wyczytać że wejściowy format to macierz 4 wymiarowa typu UINT8 (liczby 0-255) – pierwszy wymiar to numer zdjęcia, bo może być ich wiele, drugi wymiar to wysokość zdjęcia, trzeci zaś szerokość, ostatni wymiar to liczba kanałów równa 3 – RGB. Z uwagi na to, że będziemy chcieli wykonywać predykcje poprzez zapytania json, będziemy chcieli przesyłać obrazy zakodowane w base64, a nie koniecznie macierz pikseli jako ASCII (oj ile by to miejsca zajęło!).
Wykonamy ponowny eksport modelu wymuszając wejście jako string, by móc podać zakodowany w base64 obraz. Pobierzemy repozytorium z exporterem i przygotujemy się do przekonwertowania modelu. Postępujemy zgodnie z Installation
git clone https://github.com/tensorflow/models.git cd models/research protoc object_detection/protos/*.proto --python_out=. export PYTHONPATH=$PYTHONPATH:$HOME/models/research:$HOME/models/research/slim echo "export PYTHONPATH=$PYTHONPATH:$HOME/models/research:$HOME/models/research/slim" >> ~/.bashrc
Jesteśmy prawie gotowi na uruchomienie, jeszcze tylko biblioteki. Korzystamy z TensorFlow 1.10 z uwagi na kompatybilność wyeksportowanego modelu z wersją TF w ML Engine. Obencie TF jest w wersji 1.12, a w ML Engine możemy skorzystać maksymalnie z 1.10.
pip3 install tensorflow==1.10.0 --upgrade pip3 install --user Cython contextlib2 pillow lxml jupyter matplotlib
A więc czyżby to już? Tak i nie, my jesteśmy gotowi, ale w konfiguracji ściągniętego modelu jest błąd (Tak, jest tam błąd), więc poprawimy go najpierw usuwajc linię:
batch_norm_trainable: true
z pliku ssd_mobilenet_v2_coco_2018_03_29/pipeline.config
vi ssd_mobilenet_v2_coco_2018_03_29/pipeline.config
Teraz już możemy uruchomić export modelu:
python3 models/research/object_detection/export_inference_graph.py --input_type encoded_image_string_tensor --pipeline_config_path ssd_mobilenet_v2_coco_2018_03_29/pipeline.config --trained_checkpoint_prefix ssd_mobilenet_v2_coco_2018_03_29/model.ckpt --output_directory ssd_model_string
Dużo outputu. Dużo, ale powinien pojawić nam się katalog ssd_model_string/ z wyeksportowanym modelem. Sprawdzimy wejście naszego modelu:
saved_model_cli show --dir ssd_model_string/saved_model --all
I teraz wejście naszego modelu to string:
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs: signature_def['serving_default']: The given SavedModel SignatureDef contains the following input(s): inputs['inputs'] tensor_info: dtype: DT_STRING shape: (-1) name: encoded_image_string_tensor:0 The given SavedModel SignatureDef contains the following output(s): outputs['detection_boxes'] tensor_info: dtype: DT_FLOAT shape: (-1, 100, 4) name: detection_boxes:0 outputs['detection_classes'] tensor_info: dtype: DT_FLOAT shape: (-1, 100) name: detection_classes:0 outputs['detection_scores'] tensor_info: dtype: DT_FLOAT shape: (-1, 100) name: detection_scores:0 outputs['num_detections'] tensor_info: dtype: DT_FLOAT shape: (-1) name: num_detections:0 Method name is: tensorflow/serving/predict
Upload modelu do Storage Bucketu
Nasz wyeksportowany model jest gotowy, nasz model w ML Engine również, teraz chcielibyśmy utworzyć jego pierwszą wersję, ale do tego potrzebujemy przesłać model do Storage Bucketu. Jeżeli masz już jakiś, możesz go wykorzystać, jeśli nie to utwórz nowy podając lokalizację i unikalną nazwę.
Skopiujemy nasz model do naszego Bucketu wykorzystując narzędzie gsutil
gsutil cp -r ssd_model_string gs://NAZWA_TWOJEGO_BUCKETU
Tworzenie wersji modelu w ML Engine
Mamy już wszystko co niezbędne, klikamy w nasz model w ML Engine Models i klikamy create version.
Uzupełniamy dane i klikamy save.
Po dłuższej chwili model powinien zacząć działać – zielona ikona. To dobry moment na przerwę, ja byłem w sklepie w międzyczasie. Tak to wygląda:
Predykcja z wykorzystaniem modelu
Przygotujemy zdjęcie do predykcji i zapiszamy je do pliku instance.json w formacie base64.
wget https://raw.githubusercontent.com/tensorflow/models/master/research/object_detection/test_images/image1.jpg echo {\"inputs\":{\"b64\":\"$(base64 image1.jpg)\"}} > instance.json
A następnie wykonamy predykcję:
gcloud ml-engine predict --model ssd_mobilenet_v2_coco --version version0 --json-instances instance.json
I otrzymujemy wynik:
DETECTION_BOXES DETECTION_CLASSES DETECTION_SCORES NUM_DETECTIONS [[0.09466058015823364, 0.3744485080242157, 0.9318303465843201, 0.981620192527771], [0.01541098952293396, 0.012350127100944519, 0.8634837865829468, 0.32115042209625244], [0.009715914726257324, 0.2869885563850403, 0.7213219404220581, 0.6651993989944458], [0.0, 0.3330763280391693, 0.514610767364502, 0.6734130382537842], [0.04345259070396423, 0.26753389835357666, 0.8989118337631226, 0.781010627746582], [0.004425108432769775, 0.3637745976448059, 0.3391636610031128, 0.6960598826408386], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]] [18.0, 18.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] [0.9828254580497742, 0.8657804131507874, 0.7905182838439941, 0.5871506929397583, 0.43748635053634644, 0.41519099473953247, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 6.0
Otrzymaliśmy 6 wykryć
- klasa 18 (dog)
bbox: 0.0946, 0.374, 0.932, 0.982
score: 0.983 - klasa 18 (dog)
bbox: 0.015, 0.012, 0.863, 0.321
score: 0.866 - klasa 1 (person)
bbox: 0.010, 0.287, 0.721, 0.665
score: 0.791 - klasa 1 (person)
bbox: 0.0, 0.333, 0.515, 0.673
score: 0.587 - klasa 1 (person)
bbox: 0.043, 0.268, 0.899, 0.781
score: 0.437 - klasa 1 (person)
bbox: 0.004, 0.364, 0.339, 0.696
score: 0.415
Istnieje jeszcze możliwość predykcji z pythona, ale to zostawiam na później!
https://cloud.google.com/ml-engine/docs/tensorflow/online-predict
Link
- Object Detection Model ZOO – tu znajdziesz gotowe modele które możesz wykorzystać
- Model SSD MobileNet V2 z Object Detection ZOO
- Zbiór danych MS COCO
- ML Engine Models API
- Model Exporter
- saved_model_cli
- Tensorflow Object Detection Installation
- https://cloud.google.com/ml-engine/docs/tensorflow/online-predict