python-3.xamazon-web-servicesaws-lambdaaws-api-gatewayamazon-sagemaker

How to make inference to a keras model hosted on AWS SageMaker via AWS Lambda function?


I have a pre-trained keras model which I have hosted on AWS using AWS SageMaker. I've got an endpoint and I can make successful predictions using the Amazon SageMaker Notebook instance.

What I do there is that I serve a .PNG image like the following and the model gives me correct prediction.

file= s3.Bucket(bucketname).download_file(filename_1, 'normal.png')
file_name_1='normal.png'


import sagemaker
from sagemaker.tensorflow.model import TensorFlowModel

endpoint = 'tensorflow-inference-0000-11-22-33-44-55-666' #endpoint

predictor=sagemaker.tensorflow.model.TensorFlowPredictor(endpoint, sagemaker_session)
data = np.array([resize(imread(file_name), (137, 310, 3))])
predictor.predict(data)

Now I wanted to make predictions using a mobile application. For that I wrote a Lambda function in python and attached an API gateway to it. My Lambda function is the following.

import os
import sys

CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, os.path.join(CWD, "lib"))

import json
import base64
import boto3
import numpy as np
from scipy import signal
from scipy.signal import butter, lfilter
from scipy.io import wavfile
import scipy.signal as sps
import io
from io import BytesIO
import matplotlib.pylab as plt
from matplotlib import pyplot as plt
import matplotlib.image as mpimg
from datetime import datetime
from skimage.io import imread
from skimage.transform import resize
from PIL import Image

ENDPOINT_NAME = 'tensorflow-inference-0000-11-22-33-44-55-666'
runtime= boto3.client('runtime.sagemaker')

def lambda_handler(event, context):
    s3 = boto3.client("s3")
    
    # retrieving data from event.
    get_file_content_from_postman = event["content"]
    
    # decoding data.
    decoded_file_name = base64.b64decode(get_file_content_from_postman)
    
    image = Image.open(io.BytesIO(decoded_file_name))

    data = np.array([resize(imread(image), (137, 310, 3))])
    
    response = runtime.invoke_endpoint(EndpointName=ENDPOINT_NAME, ContentType='text/csv', Body=data)
        
    result = json.loads(response['Body'].read().decode())
    
    return result

The third last line is giving me error 'PngImageFile' object has no attribute 'read'. Any idea what I am missing here?


Solution

  • I was missing one thing which was causing this error. After receiving the image data I used python list and then json.dump that list (of lists). Below is the code for reference.

    import os
    import sys
    
    CWD = os.path.dirname(os.path.realpath(__file__))
    sys.path.insert(0, os.path.join(CWD, "lib"))
    
    import json
    import base64
    import boto3
    import numpy as np
    import io
    from io import BytesIO
    from skimage.io import imread
    from skimage.transform import resize
    
    # grab environment variable of Lambda Function
    ENDPOINT_NAME = os.environ['ENDPOINT_NAME']
    runtime= boto3.client('runtime.sagemaker')
    
    def lambda_handler(event, context):
        s3 = boto3.client("s3")
        
        # retrieving data from event.
        get_file_content_from_postman = event["content"]
        
        # decoding data.
        decoded_file_name = base64.b64decode(get_file_content_from_postman)
        
        data = np.array([resize(imread(io.BytesIO(decoded_file_name)), (137, 310, 3))])
        
        payload = json.dumps(data.tolist())
        
        response = runtime.invoke_endpoint(EndpointName=ENDPOINT_NAME, ContentType='application/json', Body=payload)
            
        result = json.loads(response['Body'].read().decode())
        
        return result