web-dev-qa-db-fra.com

Comment préparer un modèle pour TensorFlow Serving REST interface avec une image encodée en base64?

Ma compréhension est que je devrais être en mesure de récupérer un modèle TensorFlow à partir du hub AI de Google, de le déployer sur TensorFlow Serving et de l'utiliser pour faire des prédictions en POSTANT des images via REST requêtes en utilisant curl.

Je n'ai pas pu trouver de prédicteurs bbox sur AI Hub pour le moment mais j'en ai trouvé un sur le modèle de zoo TensorFlow:

http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz

J'ai le modèle déployé sur TensorFlow, mais la documentation n'est pas claire en ce qui concerne exactement ce qui doit être inclus dans le JSON de la demande REST.

Ma compréhension est que

  1. La SignatureDefinition du modèle détermine à quoi devrait ressembler le JSON
  2. Je devrais coder en base64 les images

J'ai pu obtenir la définition de signature du modèle comme ceci:

>python tensorflow/tensorflow/python/tools/saved_model_cli.py show --dir /Users/alexryan/Alpine/git/tfserving-tutorial3/model-volume/models/bbox/1/ --all

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['in'] tensor_info:
        dtype: DT_UINT8
        shape: (-1, -1, -1, 3)
        name: image_tensor:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['out'] tensor_info:
        dtype: DT_FLOAT
        shape: unknown_rank
        name: detection_boxes:0
  Method name is: tensorflow/serving/predict

I pensez les informations de forme ici me disent que le modèle peut gérer des images de toutes dimensions?

La couche d'entrée ressemble à ceci dans Tensorboard: enter image description here

Mais comment convertir cette définition de signature en une demande JSON valide?
Je suppose que je suis censé utiliser l'API Predict ...

et doc de Google dit ...

URL

POST http: // Host: port/v1/models / $ {MODEL_NAME} [/ versions/$ {MODEL_VERSION}]: prédire

/ versions/$ {MODEL_VERSION} est facultatif. Si omis, la dernière version est utilisée.

Format de demande
Le corps de la demande pour l'API Predict doit être formaté en objet JSON comme suit:

{
  // (Optional) Serving signature to use.
  // If unspecifed default serving signature is used.
  "signature_name": <string>,  
  // Input Tensors in row ("instances") or columnar ("inputs") format.
  // A request can have either of them but NOT both.
  "instances": <value>|<(nested)list>|<list-of-objects>
  "inputs": <value>|<(nested)list>|<object>
}

Codage des valeurs binaires JSON utilise le codage UTF-8. Si vous avez des valeurs d'entité ou de tenseur d'entrée qui doivent être binaires (comme les octets d'image), vous devez coder Base64 les données et les encapsuler dans un objet JSON ayant b64 comme clé comme suit:

{ "b64": "base64 encoded string" }

Vous pouvez spécifier cet objet comme valeur pour une entité en entrée ou un tenseur. Le même format est également utilisé pour coder la réponse de sortie.

Une demande de classification avec des fonctionnalités d'image (données binaires) et de légende est présentée ci-dessous:

{   "signature_name": "classify_objects",   "examples": [
    {
      "image": { "b64": "aW1hZ2UgYnl0ZXM=" },
      "caption": "seaside"
    },
    {
      "image": { "b64": "YXdlc29tZSBpbWFnZSBieXRlcw==" },
      "caption": "mountains"
    }   ] }

Les incertitudes comprennent:

  • dois-je utiliser des "instances" dans mon JSON
  • dois-je coder en base64 un JPG ou PNG ou autre chose?
  • L'image doit-elle avoir une largeur et une hauteur particulières?

Dans Serving Model-Based Deep Learning Models with TensorFlow-Serving's RESTful API ce format est suggéré:

{
  "instances": [
                  {"b64": "iVBORw"},
                  {"b64": "pT4rmN"},
                  {"b64": "w0KGg2"}
                 ]
}

J'ai utilisé cette image: https://tensorflow.org/images/blogs/serving/cat.jpg

et base64 l'a encodé comme ceci:

  # Download the image
  dl_request = requests.get(IMAGE_URL, stream=True)
  dl_request.raise_for_status()

  # Compose a JSON Predict request (send JPEG image in base64).
  jpeg_bytes = base64.b64encode(dl_request.content).decode('utf-8')
  predict_request = '{"instances" : [{"b64": "%s"}]}' % jpeg_bytes

Mais quand j'utilise curl pour POST l'image encodée en base64 comme ceci:

{"instances" : [{"b64": "/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAA
...
KACiiigAooooAKKKKACiiigAooooA//Z"}]}

Je reçois une réponse comme celle-ci:

>./test_local_tfs.sh 
HEADER=|Content-Type:application/json;charset=UTF-8|
   URL=|http://127.0.0.1:8501/v1/models/saved_model/versions/1:predict|
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8501 (#0)
> POST /v1/models/saved_model/versions/1:predict HTTP/1.1
> Host: 127.0.0.1:8501
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Type:application/json;charset=UTF-8
> Content-Length: 85033
> Expect: 100-continue
> 
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 400 Bad Request
< Content-Type: application/json
< Date: Tue, 17 Sep 2019 10:47:18 GMT
< Content-Length: 85175
< 
{ "error": "Failed to process element: 0 of \'instances\' list. Error: Invalid argument: JSON Value: {\n    \"b64\": \"/9j/4AAQSkZJRgABAQAAS
...
ooooA//Z\"\n} Type: Object is not of expected type: uint8" }

J'ai essayé de convertir une version locale du même fichier en base64 comme ça (confirmant que le dtype est uint8) ...

  img = cv2.imread('cat.jpg')   
  print('dtype: ' +  str(img.dtype))                                                                                                                                                                                
  _, buf = cv2.imencode('.jpg', img)
  jpeg_bytes = base64.b64encode(buf).decode('utf-8')
  predict_request = '{"instances" : [{"b64": "%s"}]}' % jpeg_bytes

Mais la publication de ce JSON génère la même erreur.

Cependant, lorsque le json est formaté comme ça ...

{'instances': [[[[112, 71, 48], [104, 63, 40], [107, 70, 20], [108, 72, 21], [109, 77, 0], [106, 75, 0], [92, 66, 0], [106, 80, 0], [101, 80, 0], [98, 77, 0], [100, 75, 0], [104, 80, 0], [114, 88, 17], [94, 68, 0], [85, 54, 0], [103, 72, 11], [93, 62, 0], [120, 89, 25], [131, 101, 37], [125, 95, 31], [119, 91, 27], [121, 93, 29], [133, 105, 40], [119, 91, 27], [119, 96, 56], [120, 97, 57], [119, 96, 53], [102, 78, 36], [132, 103, 44], [117, 88, 28], [125, 89, 4], [128, 93, 8], [133, 94, 0], [126, 87, 0], [110, 74, 0], [123, 87, 2], [120, 92, 30], [124, 95, 33], [114, 90, 32], 
...
, [43, 24, 33], [30, 17, 36], [24, 11, 30], [29, 20, 38], [37, 28, 46]]]]}

... Ça marche. Le problème est que ce fichier json a une taille> 11 Mo.

Comment faire fonctionner la version encodée en base64 du json?

MISE À JOUR: Il semble que nous devons éditer le modèle pré-formé pour accepter les images base64 à la couche d'entrée

Cet article décrit comment modifier le modèle ... Moyen: Servir des modèles d'apprentissage en profondeur basés sur l'image avec l'API RESTful de TensorFlow-Serving ... malheureusement, il suppose que nous avons accès au code qui a généré le modèle.

la solution de user260826 fournit une solution de contournement à l'aide d'un estimateur, mais elle suppose que le modèle est un modèle à kéros. Pas vrai dans ce cas.

Existe-t-il une méthode générique pour préparer un modèle pour TensorFlow Serving REST interface avec une image encodée en base64 qui fonctionne avec l'un des formats de modèle TensorFlow?

7
Alex Ryan

La première étape consiste à exporter le modèle formé dans le format approprié. Utilisez export_inference_graph.py comme ceci

python export_inference_graph \
    --input_type encoded_image_string_tensor \
    --pipeline_config_path path/to/ssd_inception_v2.config \
    --trained_checkpoint_prefix path/to/model.ckpt \
    --output_directory path/to/exported_model_directory

dans l'extrait de code ci-dessus, il est important de spécifier

--input_type encoded_image_string_tensor

après avoir exporté le modèle, exécutez le serveur tensorflow comme d'habitude avec le modèle nouvellement exporté.

Le code d'inférence ressemblera à ceci:

from __future__ import print_function
import base64
import requests

SERVER_URL = 'http://localhost:8501/v1/models/vedNet:predict'

IMAGE_URL = 'test_images/19_inp.jpg'


def main():
    with open(IMAGE_URL, "rb") as image_file:
        jpeg_bytes = base64.b64encode(image_file.read()).decode('utf-8')
        predict_request = '{"instances" : [{"b64": "%s"}]}' % jpeg_bytes
        response = requests.post(SERVER_URL, predict_request)
        response.raise_for_status()
        prediction = response.json()['predictions'][0]

if __name__ == '__main__':
  main()

0
Vedanshu