Setup Your Flask Service
- Create a new file named application.py
- Import the needed packages
- numpy as np
- flash
- sys
- keras.models
- os
- StandardScalar from sklearn.preprocessing
Reference Material
https://docs.python.org/3/reference/import.html
Add the following to the file application.py:
# import the necessary packages import numpy as np import flask import sys from keras.models import model_from_json import os from sklearn.preprocessing import StandardScaler
- Create a variable named basepath, it should either be the current directory or the content of the environment variable ‘WEBROOT_PATH’. (For Azure)
- Append the folder out to the basepath
Reference Material
https://docs.python.org/3.6/library/os.path.html
https://docs.python.org/2/library/os.html#process-parameters
Read the environment with this command:
os.environ.get(???)
Specify both the variable to examine and the default of “.”:
basepath = os.environ.get('WEBROOT_PATH', '.')
Use join to create the desired path:
basepath = os.path.join (basepath, "out")
# find the right path for batch ai vs local basepath = os.environ.get('WEBROOT_PATH', '.') basepath = os.path.join (basepath, "out")
NOTE: This main loop should go to the very bottom of your file and stay there, everything else need to be defined before it. Initializing Flash should go at the beginning.
- Initialize flask with a parameter of __name__
- Set the variable model equal to none
- Create a main loop for flask (don’t spend time on this, just cut and paste the code, it is not obvious how to make this both local and Azure friendly)
Reference Material
http://flask.pocoo.org/docs/1.0/api/
http://flask.pocoo.org/snippets/20/
Initialize Flask:
app = flask.Flask(__name__)
Define Model:
model = None
Create the Main loop waiting for input:
# if this is the main thread of execution first load the model and # then start the server if __name__ == "__main__": print(("* Loading Keras model and Flask starting server..." "please wait until server has fully started")) load_model() app.run() if __name__ == "main": load_model()
# initialize our Flask application and the Keras model app = flask.Flask(__name__) model = None # if this is the main thread of execution first load the model and # then start the server if __name__ == "__main__": print(("* Loading Keras model and Flask starting server..." "please wait until server has fully started")) load_model() app.run() if __name__ == "main": load_model()
Load the Model during Startup
- Create a function named load_model
- Initialize two global variables named model and scalar
Reference Material
https://www.w3schools.com/python/python_functions.asp
https://www.python-course.eu/python3_global_vs_local_variables.php
Add the following function, this should be defined near the top of the module and must before any calls to load_model:
def load_model():
Create the globals:
global model global scalar
[collapse]
def load_model(): global model global scalar
- For the sake of speed, cut and paste the solution, and examine it, also check out the reference on exception handling
Reference Material
https://realpython.com/python-exceptions/#the-try-and-except-block-handling-exceptions
Update the load_model function to look like this:
def load_model(): global model global scalar try: # load json and create model json_file = open(os.path.join (basepath, 'model.json'), 'r') loaded_model_json = json_file.read() json_file.close() model = model_from_json(loaded_model_json) #deserialize the scalar to pickle import pickle scalar = pickle.load(open (os.path.join (basepath, "standardscalar.pickle"), "rb")) # load weights into new model model.load_weights(os.path.join (basepath, 'model.h5')) print("Loaded model from disk") except OSError as err: output = "OSError: " + err.strerror + " Filename: " + err.filename app.logger.error(output) print(output) except (Exception): output = "Error: " + sys.exc_info() app.logger.error(output) print(output)
Create REST Handlers
- Create a Flask ErrorHandler that responds with a 500 after an exception and log the error to the console.
Reference Material
http://flask.pocoo.org/docs/1.0/api/
Define an internal_error function:
def internal_error(exception): return 0
Decorate it with an errorhandler adding this line above the internal_error function:
@app.errorhandler(500)
Log the error adding this line to the beginning of the function:
app.logger.error(output)
@app.errorhandler(500) def internal_error(exception): app.logger.error(exception) return 0
- Define a predict function that handles POST for the uri “/predict”
- Create the stub for the code
Reference Material
https://keras.io/models/sequential/
http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html
Create your predict function like this:
@app.route("/predict", methods=["POST"]) def predict():
Add the skeleton to get this as the complete answer:
@app.route("/predict", methods=["POST"]) def predict(): # initialize the data dictionary that will be returned from the # view data = {"success": False} # ensure an image was properly uploaded to our endpoint if flask.request.method == "POST": # indicate that the request was a success data["success"] = True # return the data dictionary as a JSON response resp = flask.jsonify(data) resp.headers.add('Access-Control-Allow-Origin', '*') return resp
- Read the JSON body of the document that was posted.
- Preprocess the input placing it in an array and converting it to float32.
- Apply the same Scalar Transform that was applied during training.
- Perform a predict on the model with the input, print it to the console for easy debugging.
- Return a JSON document containing the raw prediction and whether or not the job was won or not (> .5 = true).
Reference Material
http://flask.pocoo.org/docs/1.0/api/
https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.array.html
http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html
https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.ndarray.astype.html
https://keras.io/models/model/
https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.append.html
http://flask.pocoo.org/docs/1.0/api/
Read the JSON body:
jsonIn = flask.request.get_json()
Preprocess the input placing it in an array and converting it to float32:
input = np.array([[jsonIn['RouteToMarket_Other'], jsonIn['RouteToMarket_Reseller'], jsonIn['RouteToMarket_Telecoverage'], jsonIn['RouteToMarket_Telesales'], jsonIn['SalesStageChangeCount'], jsonIn['TotalDaysIdentifiedThroughClosing'], jsonIn['TotalDaysIdentifiedThroughQualified'], jsonIn['RevenueFromClientPastTwoYears'], jsonIn['RatioDaysIdentifiedToTotalDays'], jsonIn['RatioDaysValidatedToTotalDays'], jsonIn['RatioDaysQualifiedToTotalDays'], jsonIn['DealSizeCategory']]]) input = input.astype('float32')
Apply the same Scalar Transform that was applied during training:
input = scalar.transform
Perform a predict on the model with the input, print it to the console for easy debugging:
preds = model.predict(input) print(preds)
Return a JSON document containing the raw prediction and whether or not the job was won or not (> .5 = true):
if (preds[0][0] > 0.5): won = "True" else: won = "False" data["predictions"] = [] data["predictions"].append({"probability": float(preds[0][0]), "won": won})
Place this code after the If statement checking to see that it is a POST:
# read the json doc jsonIn = flask.request.get_json() # preprocess the data with the standard scalar input = scalar.transform(np.array([[jsonIn['RouteToMarket_Other'], jsonIn['RouteToMarket_Reseller'], jsonIn['RouteToMarket_Telecoverage'], jsonIn['RouteToMarket_Telesales'], jsonIn['SalesStageChangeCount'], jsonIn['TotalDaysIdentifiedThroughClosing'], jsonIn['TotalDaysIdentifiedThroughQualified'], jsonIn['RevenueFromClientPastTwoYears'], jsonIn['RatioDaysIdentifiedToTotalDays'], jsonIn['RatioDaysValidatedToTotalDays'], jsonIn['RatioDaysQualifiedToTotalDays'], jsonIn['DealSizeCategory']]])) input = input.astype('float32') # classify the input image and then initialize the list # of predictions to return to the client preds = model.predict(input) print(preds) if (preds[0][0] > 0.5): won = "True" else: won = "False" data["predictions"] = [] data["predictions"].append({"probability": float(preds[0][0]), "won": won}) # indicate that the request was a success data["success"] = True
Try your Model
- Add the following to a file named request.json:
{ "RouteToMarket_Other": 0, "RouteToMarket_Reseller": 1, "RouteToMarket_Telecoverage": 0, "RouteToMarket_Telesales": 0, "SalesStageChangeCount": 9, "TotalDaysIdentifiedThroughClosing": 119, "TotalDaysIdentifiedThroughQualified": 97, "RevenueFromClientPastTwoYears": 0, "RatioDaysIdentifiedToTotalDays": 0.81, "RatioDaysValidatedToTotalDays": 0.644, "RatioDaysQualifiedToTotalDays": 0.245, "DealSizeCategory": 6 }
- Run your web service.
- Open an Anaconda Prompt.
- Enter the command:
curl -X POST -d @request.json "http://localhost:5000/predict" -H "Content-Type: application/json"
Lab Complete!
Extra Credit – Output the model
Keras has a lot to offer, explore the documentation:
https://keras.io