Building an ML App

Now that we have a machine learning model to predict the defaults, let us try to build a web application to lend loans.

It'll have two parts:

  • a form to submit the loans
  • admin panel to look at the submitted loans and their probability of defaults

The source code for the ML app is available in the github repo in credit-risk/webap folder. It has all the moving parts except integration with the model.

Start the application using:

python webapp.py

ML as a Service

While we can package the model with the webapp and use it, it created tight coupling between the two. Everytime the model changes, the webapp will have to change. What if there are more than one application using the same model?

It is lot simpler to deploy the ML model as a service exposing it's functionality through an HTTP API.

In this turorial we are going to use a tool called firefly for running the model as a service.

Introduction to Firefly

Firefly makes it very easy to deploy functions as a service without having to worry about writing a web app, managing request/response formats etc. and also provides a very simple client interface.

The detailed documentation of Firefly is available at:

http://firefly-python.readthedocs.io/

Let's try a simple example. Here We're creating a file sq.py with a square function. We'll see how deploy it a service and use in other programs.


In [ ]:
%%file sq.py

def square(n):
    return n*n

Let us run it as a service using firefly by running the following command in your terminal.

$ firefly sq.square
[2017-07-13 09:48:07 +0200] [5001] [INFO] Starting gunicorn 19.7.1
[2017-07-13 09:48:07 +0200] [5001] [INFO] Listening at: http://127.0.0.1:8000 (5001)

It takes the <module_name>.<function_name> as argument and exposes the function as an API. The argument the function takes and the return value must be JSON-friendly for it work.

Once that is running, we can try to access it using the firefly client.


In [4]:
import firefly

In [ ]:
remote_sq = firefly.Client("http://127.0.0.1:8000")

In [ ]:
remote_sq.square(n=4)

The function will be available with the same name in the client. Please note that the client functions takes parameters only by name.

If you want to run on a different port, you can specify that as an argument.

$ firefly -b 0.0.0.0:9000 sq.square

For more help on the available command-line options, try:

$ firefly --help

Problem: Write funciton add_numbers in a file add.py and deploy it as a service using Firefly. Once that is ready, try to use it in another program using the Firefly Client.


In [ ]:
%%file add.py
# your code here

In [ ]:


In [ ]:

Credit Grade Service

Banks will have a access to the credit grade of each customer. Since we haven't have real data, let us build a simple mock credit grade service.

It'll take the email address of the person and gives a grade at random.


In [ ]:
%%file credit_grade.py
"""Program to find the credit grade of a person.
"""
import zlib
import random

def find_credit_grade(email):
    """Returns the credit grade of the person identified by the given email address.
    
    The credit grade can be either A, B, C, D, E, F or G.
    """
    # since we need to give the same grade everytime the function is called
    # with the same email. Using the checksum of the string as random seed 
    # to get the same result everytime when used with the same email.
    seed = zlib.adler32(email.encode("utf-8"))
    r = random.Random(seed)
    return r.choice(["A", "B", "C", "D", "E", "F", "G"])

Deploy it as a servive using Firefly.

firefly credit_grade.find_credit_grade

In [ ]:
credit_grade_api = firefly.Client("http://127.0.0.1:8000/")

In [ ]:
credit_grade_api.find_credit_grade(email="alice@example.com")

In [ ]:


In [ ]:

Deploying the ML model

To deploy the machine learning model as a service, we need to read the model and all the encodings that we have used in building the model.


In [ ]:


In [2]:
%%file credit_risk_service.py
"""Service to expose the credit risk model as an API.
"""
from sklearn.externals import joblib

# read the encoders and the model
grade_encoder = joblib.load("../notebooks/le_grade.pkl")
ownership_encoder = joblib.load("../notebooks/le_ownership.pkl")
model = joblib.load("../notebooks/model.pkl")

def predict(amount, years, age, ownership, income, grade):
    """Returns the probablity of default for given features.
    """
    # encoders work on a vector. Wrapping in a list as we only have a single value
    ownership_code = ownership_encoder.transform([ownership])[0]
    grade_code = grade_encoder.transform([grade])[0]
    
    # important to pass the features in the same order as we built the model
    features = [amount, grade_code, years, ownership_code, income, age]
    
    # probablity for not-defaulting and defaulting
    # Again, wrapping in a list as a list of features is expected
    p0, p1 = model.predict_proba([features])[0]
    return p1


Overwriting credit_risk_service.py

Run it as a service using firefly, again from your terminal. Let us use port 9000 now as port 8000 is used by the credit grade service.

$ firefly -b 127.0.0.1:9000 credit_risk_service.predict

Now let us predict.


In [5]:
import firefly
credit_risk_api = firefly.Client("http://127.0.0.1:9000")

In [7]:
credit_risk_api.predict(amount=10000, 
                        years=2, 
                        age=35, 
                        ownership='RENT', 
                        income=12345, 
                        grade='A')


Out[7]:
0.59

In [ ]:


In [ ]:

Model Integration

Can you try to integrate the credit grade service and credit risk prediction service into the our webapp?


In [ ]:


In [ ]:


In [ ]: