In [1]:
from IPython.display import Image
import graphlab
graphlab.canvas.set_target('ipynb')

Overview: Deploying the Dress Recommender

Let's say you want to make an app which can recommend dresses to you based on a photo you took. You need a way to deploy the model previously built. Turi Predictive Services helps do this in an easy and scalable way. In this notebook, we demonstrate how do that for the dress recommender model.


Deployment Steps

The notebook has three sections:

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

1. Create a model

Let us try and deploy the dress recommender. First, we define a function similar_dress that takes a query and returns similar dresses.

We start by loading the already trained models and datasets.


In [8]:
reference_sf = graphlab.SFrame('data/sf_processed.sframe/')
pretrained_model = graphlab.load_model('data/imagenet_model')
nn_model = graphlab.load_model('data/nearest_dress_model')

In [135]:
reference_sf


Out[135]:
_id name brand price image
0 Print Crêpe de Chine
Halter Maxi Dress ...
Eliza J Was: $158.00 Height: 256 Width: 256
1 Cheetah Print Silk Wrap
Dress (Nordstrom ...
Diane von Furstenberg Was: $498.00 Height: 256 Width: 256
2 Monofilament Mesh Dress Junya Watanabe Was: $2,455.00 Height: 256 Width: 256
3 Print Cutout One-Shoulder
Chiffon Gown ...
Aidan by Aidan Mattox Was: $350.00 Height: 256 Width: 256
4 Cutaway A-Line Dress 3.1 Phillip Lim Was: $595.00 Height: 256 Width: 256
5 Embroidered Sleeveless
Blouson Dress (Regular & ...
Caslon® Was: $78.00,Was: $78.00 Height: 256 Width: 256
6 V-Neck Skater Dress EVERLY Was: $46.00 Height: 256 Width: 256
7 Blouson Chiffon Skater
Dress ...
Lush Was: $46.00 Height: 256 Width: 256
8 Belted Embroidered Lace
Gown (Regular & Petite) ...
Tadashi Shoji Was: $408.00 Height: 256 Width: 256
9 Embellished Shoulder
Layered Chiffon Gown ...
Eliza J Was: $248.00 Height: 256 Width: 256
features
[0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 5.05821704865, ...
[0.0, 0.0, 0.0,
0.35272860527, 0.0, 0.0, ...
[0.0, 0.0, 0.0,
0.12568038702, ...
[0.0, 0.0, 0.0, 0.0,
1.32276856899, 0.0, 0.0, ...
[0.0, 0.0,
0.808826684952, 0.0, ...
[0.0, 0.0, 1.86694121361,
0.0151504278183, 0.0, ...
[0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 10.0536394119, ...
[0.0, 0.0, 0.0,
1.33359932899, 0.0, 0.0, ...
[0.0, 0.0, 2.31556797028,
0.0, 0.947912573814, ...
[0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 13.2484369278, ...
[7748 rows x 6 columns]
Note: Only the head of the SFrame is printed.
You can use print_rows(num_rows=m, num_columns=n) to print more rows and columns.


In [9]:
def dress_similar(url):
    img = graphlab.Image(url)
    image_sf = graphlab.SFrame()
    image_sf['image'] = [img]
    image_sf['features'] = pretrained_model.extract_features(image_sf)
    ans = nn_model.query(image_sf, k=5)
    return ans

In [11]:
QUERY_URL = 'http://static.ddmcdn.com/gif/blue-dress.jpg'
Image(QUERY_URL)


Out[11]:

In [58]:
def retrieve_image(nearest_neighbors_output, input_sframe):
    joined = input_sframe.join(nearest_neighbors_output, on={'_id':'reference_label'})
    sorted_sf = joined.sort('rank')
    return sorted_sf['image']

In [85]:
images = retrieve_image(dress_similar(QUERY_URL), reference_sf)


PROGRESS: Images being resized.
PROGRESS: Starting pairwise querying.
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 0            | 1       | 0.0129066   | 14.926ms     |
PROGRESS: | Done         |         | 100         | 729.81ms     |
PROGRESS: +--------------+---------+-------------+--------------+

In [87]:
images.show()


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.

We add our similar_dress function to the predictive service, so that it can be queried by the outside world. Note: In order to follow along here, you need AWS credentials. You can then uncomment the code below. Be sure that the deployment state path is in an s3 bucket you have access to.


In [88]:
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 [98]:
import graphlab as gl
ps = gl.deploy.predictive_service.load('s3://gl-demo-usw2/predictive_service/demolab/ps-1.6')
ps


[WARNING] Overwritting existing Predictive Service "demolab-one-six" in local session.
Out[98]:
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: 4, 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_avg_color, version: 2, 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: 3, type: alias, cache: disabled, description: Alias for freshdress_describe_image_basic
	name: freshdress_debug_model, version: 1, type: model, cache: enabled, description: 
	name: freshdress_more_like_image_bow, version: 4, type: model, cache: enabled, description: 
	name: yelp_sentiment_predict_text, version: 2, type: model, cache: enabled, description: 
	name: freshdress_more_like_image_tfidf, version: 2, type: model, cache: enabled, description: 
	name: freshdress_more_like_image_color, version: 5, type: model, cache: enabled, description: 
	name: freshdress_more_like_image, version: 27, type: alias, cache: disabled, description: Alias for freshdress_more_like_image_bw
	name: yelp_sentiment_most_extreme, version: 2, type: model, cache: enabled, description: 
	name: freshdress_more_like_image_bw, version: 5, type: model, cache: enabled, description: 
	name: dress_similar, version: 1, type: model, cache: enabled, description: 
	name: freshdress_describe_image_basic, 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 [93]:
#ps.add('dress_similar', dress_similar)
ps.update('dress_similar', dress_similar)
ps.apply_changes()


[INFO] Endpoint 'dress_similar' is added. Use apply_changes() to deploy all pending changes, or continue with other modification.
[INFO] Uploading local path /var/folders/9h/1m96s7vn5z72cxt_q7g9p86h0000gn/T/predictive_object_l3R0fm to s3 path: s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1
upload: ../../../../../../var/folders/9h/1m96s7vn5z72cxt_q7g9p86h0000gn/T/predictive_object_l3R0fm/061ac1dd-d6d4-4d26-bbe2-56f95ade6005/dir_archive.ini to s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1/061ac1dd-d6d4-4d26-bbe2-56f95ade6005/dir_archive.ini
upload: ../../../../../../var/folders/9h/1m96s7vn5z72cxt_q7g9p86h0000gn/T/predictive_object_l3R0fm/caf2a748-33fd-4f0b-8b6a-35d72ba04a1f/dir_archive.ini to s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1/caf2a748-33fd-4f0b-8b6a-35d72ba04a1f/dir_archive.ini
upload: ../../../../../../var/folders/9h/1m96s7vn5z72cxt_q7g9p86h0000gn/T/predictive_object_l3R0fm/caf2a748-33fd-4f0b-8b6a-35d72ba04a1f/m_e11386cb504556dc.sidx to s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1/caf2a748-33fd-4f0b-8b6a-35d72ba04a1f/m_e11386cb504556dc.sidx
upload: ../../../../../../var/folders/9h/1m96s7vn5z72cxt_q7g9p86h0000gn/T/predictive_object_l3R0fm/version to s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1/version
upload: ../../../../../../var/folders/9h/1m96s7vn5z72cxt_q7g9p86h0000gn/T/predictive_object_l3R0fm/pickle_archive to s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1/pickle_archive
upload: ../../../../../../var/folders/9h/1m96s7vn5z72cxt_q7g9p86h0000gn/T/predictive_object_l3R0fm/caf2a748-33fd-4f0b-8b6a-35d72ba04a1f/objects.bin to s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1/caf2a748-33fd-4f0b-8b6a-35d72ba04a1f/objects.bin
upload: ../../../../../../var/folders/9h/1m96s7vn5z72cxt_q7g9p86h0000gn/T/predictive_object_l3R0fm/061ac1dd-d6d4-4d26-bbe2-56f95ade6005/objects.bin to s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1/061ac1dd-d6d4-4d26-bbe2-56f95ade6005/objects.bin
Completed 47 of 47 part(s) with 1 file(s) remaining
[INFO] Successfully uploaded to s3 path s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1
[INFO] Notifying: ec2-52-88-112-32.us-west-2.compute.amazonaws.com
upload: ../../../../../../var/folders/9h/1m96s7vn5z72cxt_q7g9p86h0000gn/T/predictive_object_l3R0fm/caf2a748-33fd-4f0b-8b6a-35d72ba04a1f/m_e11386cb504556dc.0000 to s3://gl-demo-usw2/predictive_service/demolab/ps-1.6/predictive_objects/dress_similar/1/caf2a748-33fd-4f0b-8b6a-35d72ba04a1f/m_e11386cb504556dc.0000

Query the model

The Predictive Service is now up! Let's query it and explore the response.


In [99]:
ps.query('dress_similar', url=QUERY_URL)


Out[99]:
{u'from_cache': False,
 u'model': u'dress_similar',
 u'response': [{u'distance': 63.13673203429012,
   u'query_label': 0,
   u'rank': 1,
   u'reference_label': 1884},
  {u'distance': 63.15239434089158,
   u'query_label': 0,
   u'rank': 2,
   u'reference_label': 4120},
  {u'distance': 63.32169413350411,
   u'query_label': 0,
   u'rank': 3,
   u'reference_label': 1792},
  {u'distance': 64.08738619303438,
   u'query_label': 0,
   u'rank': 4,
   u'reference_label': 7557},
  {u'distance': 64.23238230815427,
   u'query_label': 0,
   u'rank': 5,
   u'reference_label': 2365}],
 u'uuid': u'7e09c590-279e-49be-b498-47acca5a8674',
 u'version': 1}

Query via REST

Query from anywhere. Here, we issue a request via the requests library, and convert the returning JSON back into an SFrame. This could easily be done from outside of Python, though.


In [127]:
import json
import requests

def restful_query(url):
    # Query the service.
    headers = {'content-type': 'application/json' }
    payload = {'api_key':'b437e588-0f2b-45e1-81c8-ce3acfa81ade', 'data':{'url':url}}
    end_point = 'http://demolab-one-six-2015364754.us-west-2.elb.amazonaws.com/query/dress_similar'
    resp = requests.post(end_point,json.dumps(payload), headers=headers)

    # Join with existing data.
    ans = gl.SArray(json.loads(resp.content)['response']).unpack('')
    return retrieve_image(ans, reference_sf)

In [129]:
restful_query('http://static.ddmcdn.com/gif/blue-dress.jpg').show()