Introduction to ML Deployment

Deploying models created using python in a Turi Predictive Service is very easy. This notebook walks you through the step-by-step process.


Deployment Steps

The notebook has three sections:

  1. Create a model
  2. Create a predictive service
  3. Query the model

If you are deploying a model in an existing Predictive Service instance you can go to step (2) directly.

1. Create a model

Let's train a simple random forest model and deploy it in the Predictive Service.


In [5]:
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
iris = load_iris()

model = RandomForestClassifier(n_estimators=10)
model = model.fit(iris['data'], iris['target'])
model


Out[5]:
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=None, max_features='auto', max_leaf_nodes=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
            oob_score=False, random_state=None, verbose=0,
            warm_start=False)

We can expose the trained model as a REST endpoint. This will allow other applications to consume the predictions from the model.

In order to do that, we wrap the model object in a Python function and add it to the Predictive Service. In the function you may add your own logic for transform input to the model, ensemble different models or manipulate output before returning. Checkout out user guide for more details.

The result of the function needs to be a JSON serializable object.


In [46]:
def classify(x):
    prediction = model.predict(x)

    # convert into a json serializable value
    return list(prediction)

2. Create a Predictive Service (One time)

This section shows you how to deploy a Predictive Service to EC2. The EC2 instances used by the Predictive Service will be launched in your own AWS account, so you will be responsible for the cost.

To create a Predictive Service in Amazon AWS, we first configure the EC2 Config object, which contains the configuration parameters required for launching a Predictive Service cluster in EC2. These fields are optional and include the region, instance type, CIDR rules etc. Predictive Service uses this configuration for service creation.

Having configured our EC2 Config object, we're ready to launch a Predictive Service Deployment, There are a few aspects of the Predictive Service that can be customized:

  • Number of nodes in the service - By default the number of hosts (num_hosts) is 1. To obtain good cache utility and high availability, we recommended setting num_hosts to at least 3.
  • State path to persist service state and service logs. This is a s3 location.
  • Port to be used by the server.
  • Other settings, such as SSL credentials etc.

The following code snippet shows you how to create a Predictive Service. You will have to replace the ps_state_path and credentials for your Predictive Service.


In [1]:
import graphlab as gl

# Replace with your path.
ps_state_path = 's3://<your-bucket-name>/predictive_service/ps'

# Set your AWS credentials.
gl.aws.set_credentials(<key>, <secret>)

# Create an EC2 config
ec2_config = gl.deploy.Ec2Config()

# Launch a predictive service
ps = gl.deploy.predictive_service.create(name = 'sklearn-predictive-service', 
              ec2_config = ec2_config, state_path = ps_state_path, num_hosts = 1)

Load an already created service


In [47]:
import graphlab as gl
ps = gl.deploy.predictive_service.load('s3://gl-demo-usw2/predictive_service/demolab/ps-1.6')


[WARNING] Overwritting existing Predictive Service "demolab-one-six" in local session.

In [62]:
ps


Out[62]:
Name                  : demolab-one-six
State Path            : s3://gl-demo-usw2/predictive_service/demolab/ps-1.6
Description           : None
API Key               : b437e588-0f2b-45e1-81c8-ce3acfa81ade
CORS origin           : *
Global Cache State    : enabled
Load Balancer DNS Name: demolab-one-six-2015364754.us-west-2.elb.amazonaws.com

Deployed endpoints:
	name: freshdress_kw_search, version: 3, type: alias, cache: disabled, description: Alias for freshdress_kw_search_model
	name: yelp_sentiment_most_extreme_for_place, version: 2, type: model, cache: enabled, description: 
	name: classify-sklearn, version: 2, type: model, cache: enabled, description: 
	name: freshdress_more_like_image_bw, version: 1, type: model, cache: enabled, description: 
	name: freshdress_kw_search_model, version: 2, type: model, cache: enabled, description: 
	name: composite_recommender_query, version: 1, type: model, cache: disabled, description: 
	name: freshdress_describe, version: 2, type: alias, cache: disabled, description: Alias for freshdress_describe_image_basic
	name: freshdress_more_like_image_bow, version: 3, type: model, cache: enabled, description: 
	name: yelp_sentiment_predict_text, version: 2, type: model, cache: enabled, description: 
	name: freshdress_describe_image_basic, version: 1, type: model, cache: enabled, description: 
	name: freshdress_more_like_image_color, version: 1, type: model, cache: enabled, description: 
	name: freshdress_more_like_image, version: 5, type: alias, cache: disabled, description: Alias for freshdress_more_like_image_tfidf
	name: yelp_sentiment_most_extreme, version: 2, type: model, cache: enabled, description: 
	name: freshdress_more_like_image_tfidf, version: 1, type: model, cache: enabled, description: 
	name: composite_recommender_explanation, version: 1, type: model, cache: disabled, description: 
	name: yelp_sentiment_summary, version: 2, type: model, cache: enabled, description: 

No Pending changes.

In [53]:
# ps.add('classify-sklearn', classify) (If done for the first time)
ps.update('classify-sklearn', classify)


[INFO] Endpoint 'classify-sklearn' is updated. Use apply_changes to deploy all pending changes, or continue other modification.

In [55]:
ps.apply_changes()


[INFO] There are no pending changes. No action is taken.

Query the model

You may do a test query before really deploying it to production. This will help detect errors in the function before deploying it the Predictive Service.


In [56]:
ps.test_query('classify-sklearn', x=[5.1,  3.5,  1.4,  0.2])


[INFO] Input data serializable.
[INFO] Trying to serve classify-sklearn
[INFO] Query results serializable.
Out[56]:
{u'response': [0],
 u'uuid': u'88947cb8-4646-489d-8360-81ce1d54004e',
 u'version': 1}

Now, let us query the real service.


In [69]:
# test query to make sure the model works fine
ps.query('classify-sklearn', x=[5.1,  3.5,  1.4,  0.2])


Out[69]:
{u'from_cache': True,
 u'model': u'classify-sklearn',
 u'response': [0],
 u'uuid': u'8afd2f01-6d37-4fd0-8788-5141f92459dd',
 u'version': 2}

Query from external applications via REST

Now other applications can interact with our model! In the next section we will illustrate how to consume the model. We can also use other APIs like ps.update() to update a mode, ps.remove() to remove a model.

The model query is exposed through REST API. The path is:

http(s)://<your-ps-endpoint>/data/<model-name>

And the payload is a JSON serialized string in the following format:

{"api_key": <api key>,
 "data": <data-passed-to-custom-query>}

Here the 'api key' may be obtained through ps.api_key, and data is the actual data passed to the custom predictive object in the Predictive Service. It will be passed to the query using **kwargs format

Here is a sample curl command to query your model:

curl -X POST -d '{"api_key":"b437e588-0f2b-45e1-81c8-ce3acfa81ade", "data":{"x":[5.1,  3.5,  1.4,  0.2]}}' http://demolab-one-six-2015364754.us-west-2.elb.amazonaws.com/query/classify-sklearn


You can also query though Python using the requests module

Query through Python


In [77]:
import json
import requests

def restful_query(x):
    headers = {'content-type': 'application/json'}
    payload = {'api_key':'b437e588-0f2b-45e1-81c8-ce3acfa81ade', "data":{"x": x}}
    end_point = 'http://demolab-one-six-2015364754.us-west-2.elb.amazonaws.com/query/classify-sklearn'
    return requests.post(end_point, json.dumps(payload), headers=headers).json()

In [78]:
restful_query([5.1,  3.5,  1.4,  0.2])


Out[78]:
{u'from_cache': True,
 u'model': u'classify-sklearn',
 u'response': [0],
 u'uuid': u'ea1a4314-4795-4ca6-9822-70774e4fdafd',
 u'version': 2}

In [80]:
restful_query([5.1,  3.5,  1.4,  0.3])


Out[80]:
{u'from_cache': False,
 u'model': u'classify-sklearn',
 u'response': [0],
 u'uuid': u'a96dc4e6-b3de-4e72-9526-e12174ea58af',
 u'version': 2}