Abu
Abu

Reputation: 61

Score.py AzureML for Images

I hae deployed a model using Azure ML service (documentation sucks). My project is around computer vision and I wanna test the web service but it returns Unauthorized, no Authorization header. But when I post to the URL it returns a 200 response (it's okay) but not which class an image belongs and how accurate the model is.

I've built a REST API of the model and I will share the codes. This is the REST API that works standalone (Locally). When I use Postman, I get a return with predictions and accuracy score.

# import the necessary packages
from keras.models import load_model
import tensorflow as tf
from keras.preprocessing.image import img_to_array
from PIL import Image
from PIL import ImageFile
import numpy as np
from keras import backend as K

from flask import Flask
import flask

#Azure stuff I don't know but are needed.
from azureml.contrib.services.aml_request import AMLRequest, rawhttp
from azureml.contrib.services.aml_response import AMLResponse

# manipulate files
import io
import os 
import json
import requests

# AzureML stuff to consider, checks for the registered models.
from azureml.core.model import Model 

app = Flask(__name__)
def init():
    global model
    #Get the path where the deployed model can be found.
    #load models
    model = Model.get_model_path(model_name='large_models_1')
    global graph
    graph = tf.get_default_graph()
    print("* Model Loaded *, this is the init() * ")

# prepares image for prediction
def prepare_image(image, target):
  #  if the image mode is not RGB, convert it
    if image.mode != "RGB":
        image = image.convert("RGB")

    # resize the input image and preprocess it
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    image = image.resize(target)
    image = img_to_array(image)/255.
    image = np.expand_dims(image, axis=0)
    image = np.vstack([image])
    return image


@app.route("/predict", methods=["POST"])
def run():

    print("This is run () ")
    K.clear_session() # making new predictions.
    # wanna see the error.
    try:
        if flask.request.method  == 'POST':
            if flask.request.files.get("image"):
                image = flask.request.files["image"].read()
                image = Image.open(io.BytesIO(image))      
                image_files = prepare_image(image, target=(160,160)) # prepares the images to be loaded to the model.

                with graph.as_default():
                    prediction = model.predict(image_files).tolist()
                    print(prediction)
                    response = {
                         "prediction":{
                                "anomaly": prediction[0][0],
                                "normal": prediction[0][1],
                        
                                    } # JSON response 
                            } #end response
                    return json.dumps(response, indent=4)

        else:
            print("Error 500, bad request dumb ass !.")
    

    # Let's see the error.
    except Exception as e:
        result = str(e)
        return json.dumps(result, indent=4)

And here is the problem, I've used AKS(Azure Kubernetes Service) for deployment and had to change the score.py to match for binary data (images), this is from the tutorials on this link "https://learn.microsoft.com/en-us/azure/machine-learning/service/how-to-deploy-and-where"

from azureml.contrib.services.aml_request import AMLRequest, rawhttp
from azureml.contrib.services.aml_response import AMLResponse


def init():
    print("This is init()")


@rawhttp
def run(request):
    print("This is run()")
    print("Request: [{0}]".format(request))
    if request.method == 'GET':
        # For this example, just return the URL for GETs.
        respBody = str.encode(request.full_path)
        return AMLResponse(respBody, 200)
    elif request.method == 'POST':
        reqBody = request.get_data(False)
        # For a real-world solution, you would load the data from reqBody
        # and send it to the model. Then return the response.

        # For demonstration purposes, this example just returns the posted data as the response.
        return AMLResponse(reqBody, 200)
    else:
        return AMLResponse("bad request", 500)

My question is how do I perform the correct testing when I post images to the URL? I've tried using the tutorial but seems like it didn't help and I don't understand it. By the way, I have a feeling this deals with score.py and the run() function.

Upvotes: 4

Views: 2110

Answers (1)

Eric Craeymeersch
Eric Craeymeersch

Reputation: 151

I started from the same tutorial and this is working init() and run() (at least for me) : Note that I dont use AKS but I use Azure Container Instances. I'm fairly sure it doesn(t change anything except the autorization header mentionned in the test part (see below)

%%writefile scorebinary.py
import json
import numpy as np
import os
from tensorflow.keras.models import load_model
import PIL
from io import BytesIO
from azureml.contrib.services.aml_request import AMLRequest, rawhttp
from azureml.contrib.services.aml_response import AMLResponse

def DataPrepImage(rawimage):
    Def=200
    img = rawimage.resize((Def,Def), resample=PIL.Image.BILINEAR)
    img = (np.array(img)/255).reshape(-1,Def,Def,1)
    return img

def init():
    global network    

    # AZUREML_MODEL_DIR is an environment variable created during deployment.
    # It is the path to the model folder (./azureml-models/$MODEL_NAME/$VERSION)
    # For multiple models, it points to the folder containing all deployed models (./azureml-models)
    folder = os.getenv('AZUREML_MODEL_DIR')
    if (folder==None): #Test hors docker
        folder = "."

    model_path = os.path.join(folder, 'Reseau_Siamois_3_36.h5')

    #On charge le model Keras
    network = load_model(model_path)

@rawhttp
def run(request):
    if request.method == 'POST':
        reqBody = request.get_data(False)
        myImage = PIL.Image.open(BytesIO(reqBody))
        myImage = myImage.convert('L')

        #Dataprep de l'image
        imgprepped = DataPrepImage(myImage)

        # make prediction  
        embed = network.predict(imgprepped)

        return {'emb':embed.tolist(),'imgpreped':imgprepped.tolist()}
    else:
        return AMLResponse("bad request, use POST", 500)

To test it, i use that. Now for AKS look at the comments (not mine, they come from Azure tutorials), it might solve your 4xx error problem

import requests
import PIL
import json
import matplotlib.pyplot as plt
%matplotlib inline

img = open('cat.jpg', 'rb').read()
headers = {'Content-Type':'application/json'}

# for AKS deployment you'd need to the service key in the header as well
# api_key = service.get_key()
# headers = {'Content-Type':'application/json',  'Authorization':('Bearer '+ api_key)} 

resp = requests.post(service.scoring_uri,data=img, headers=headers)

#print("POST to url", service.scoring_uri)
responsedata = json.loads(resp.text)
emb = responsedata['emb']

print("prediction:", emb)
img = np.array(responsedata['imgpreped'])
plt.axis("off")
plt.imshow(img[0,:,:,0],vmin=0, vmax=1,cmap='Greys')

Finaly dont forget to load necessary packages:

from azureml.core.conda_dependencies import CondaDependencies 

myenv = CondaDependencies()
myenv.add_conda_package("tensorflow")
myenv.add_conda_package("pillow")
myenv.add_pip_package("azureml-contrib-services")


with open("myenv.yml","w") as f:
    f.write(myenv.serialize_to_string())
with open("myenv.yml","r") as f:
    print(f.read())

Upvotes: 2

Related Questions