Reputation: 5015
I successfully trained and deployed a Tensorflow Recommender model on Vertex AI.
Everything is online and to predict the output. In the notebook I do:
loaded = tf.saved_model.load(path)
scores, titles = loaded(["doctor"])
That returns:
Recommendations: [b'Nelly & Monsieur Arnaud (1995)'
b'Three Lives and Only One Death (1996)' b'Critical Care (1997)']
That is, the payload (input for the neural network) must be ["doctor"]
Then I generate the JSON for payload (the error is here):
!echo {"\""instances"\"" : [{"\""input_1"\"" : {["\""doctor"\""]}}]} > instances0.json
And submit to the endpoint:
!curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://us-west1-aiplatform.googleapis.com/v1/projects/my_project/locations/us-west1/endpoints/123456789:predict \
-d @instances0.json > results.json
However, when I use this payload, I get error 400:
code: 400
message: "Invalid JSON payload received. Expected an object key or }. s" : [{"input_1" : {["doctor"]}}]} ^"
status: "INVALID_ARGUMENT"
This below don't work either:
!echo {"inputs": {"input_1": ["doctor"]}} > instances0.json
Even with validated JSON Lint, it does not return the proper prediction.
In another Stackoverflow question is suggested to remove the " \ " in the payload, but this didn't work either.
Running:
!saved_model_cli show --dir /home/jupyter/model --all
I get:
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:
signature_def['__saved_model_init_op']:
The given SavedModel SignatureDef contains the following input(s):
The given SavedModel SignatureDef contains the following output(s):
outputs['__saved_model_init_op'] tensor_info:
dtype: DT_INVALID
shape: unknown_rank
name: NoOp
Method name is:
signature_def['serving_default']:
The given SavedModel SignatureDef contains the following input(s):
inputs['input_1'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_input_1:0
The given SavedModel SignatureDef contains the following output(s):
outputs['output_1'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 10)
name: StatefulPartitionedCall_1:0
outputs['output_2'] tensor_info:
dtype: DT_STRING
shape: (-1, 10)
name: StatefulPartitionedCall_1:1
Method name is: tensorflow/serving/predict
Concrete Functions:
Function Name: '__call__'
Option #1
Callable with:
Argument #1
input_1: TensorSpec(shape=(None,), dtype=tf.string, name='input_1')
Argument #2
DType: NoneType
Value: None
Argument #3
DType: bool
Value: True
Option #2
Callable with:
Argument #1
queries: TensorSpec(shape=(None,), dtype=tf.string, name='queries')
Argument #2
DType: NoneType
Value: None
Argument #3
DType: bool
Value: True
Option #3
Callable with:
Argument #1
input_1: TensorSpec(shape=(None,), dtype=tf.string, name='input_1')
Argument #2
DType: NoneType
Value: None
Argument #3
DType: bool
Value: False
Option #4
Callable with:
Argument #1
queries: TensorSpec(shape=(None,), dtype=tf.string, name='queries')
Argument #2
DType: NoneType
Value: None
Argument #3
DType: bool
Value: False
Function Name: '_default_save_signature'
Option #1
Callable with:
Argument #1
input_1: TensorSpec(shape=(None,), dtype=tf.string, name='input_1')
Function Name: 'call_and_return_all_conditional_losses'
Option #1
Callable with:
Argument #1
input_1: TensorSpec(shape=(None,), dtype=tf.string, name='input_1')
Argument #2
DType: NoneType
Value: None
Argument #3
DType: bool
Value: False
Option #2
Callable with:
Argument #1
queries: TensorSpec(shape=(None,), dtype=tf.string, name='queries')
Argument #2
DType: NoneType
Value: None
Argument #3
DType: bool
Value: True
Option #3
Callable with:
Argument #1
queries: TensorSpec(shape=(None,), dtype=tf.string, name='queries')
Argument #2
DType: NoneType
Value: None
Argument #3
DType: bool
Value: False
Option #4
Callable with:
Argument #1
input_1: TensorSpec(shape=(None,), dtype=tf.string, name='input_1')
Argument #2
DType: NoneType
Value: None
Argument #3
DType: bool
Value: True
The point is: I'm passing an array and I'm not sure if it must be in b64 format.
This Python code works, but returns a different result than expected:
import tensorflow as tf
import base64
from google.protobuf import json_format
from google.protobuf.struct_pb2 import Value
import numpy as np
from google.cloud import aiplatform
import os
vertex_model = tf.saved_model.load("gs://bucket/model")
serving_input = list(
vertex_model.signatures["serving_default"].structured_input_signature[1].keys()
)[0]
print("Serving input :", serving_input)
aip_endpoint_name = (
f"projects/my-project/locations/us-west1/endpoints/12345567"
)
endpoint = aiplatform.Endpoint(aip_endpoint_name)
def encode_input(input):
return base64.b64encode(np.array(input)).decode("utf-8")
instances_list = [{serving_input: {"b64": encode_input(np.array(["doctor"]))}}]
instances = [json_format.ParseDict(s, Value()) for s in instances_list]
results = endpoint.predict(instances=instances)
print(results.predictions[0]["output_2"])
['8 1/2 (1963)', 'Sword in the Stone, The (1963)', 'Much Ado About Nothing (1993)', 'Jumanji (1995)', 'As Good As It Gets (1997)', 'Age of Innocence, The (1993)', 'Double vie de Véronique, La (Double Life of Veronique, The) (1991)', 'Piano, The (1993)', 'Eat Drink Man Woman (1994)', 'Bullets Over Broadway (1994)']
Any ideas on how to fix / encode the payload ?
Upvotes: 0
Views: 1776
Reputation: 5015
I solved the issue. The problem was base64 encoding. This is working perfectly:
def encode_64(input):
message = input
message_bytes = message.encode('ascii')
base64_bytes = base64.b64encode(message_bytes)
base64_message = base64_bytes.decode('ascii')
return base64_message
instances_list = [{serving_input: {"b64": encode_64("doctor")}}]
instances = [json_format.ParseDict(s, Value()) for s in instances_list]
results = endpoint.predict(instances=instances)
print(results.predictions[0]["output_2"][:3])
['Nelly & Monsieur Arnaud (1995)', 'Three Lives and Only One Death (1996)', 'Critical Care (1997)']
Upvotes: 2