Improves formatting based on black, consolidates imports
This commit is contained in:
Родитель
05ce5d72f4
Коммит
9c3cea0aac
|
@ -23,16 +23,15 @@
|
|||
# In this noteook, we will go through the steps to load the ResNet152 model, pre-process the images to the required format and call the model to find the top predictions.
|
||||
|
||||
# +
|
||||
import PIL
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import numpy as np
|
||||
import torchvision
|
||||
from torchvision import datasets, models, transforms
|
||||
|
||||
import numpy as np
|
||||
import PIL
|
||||
from PIL import Image
|
||||
import wget
|
||||
from PIL import Image
|
||||
from torchvision import models, transforms
|
||||
|
||||
# -
|
||||
|
||||
print(torch.__version__)
|
||||
|
|
|
@ -22,11 +22,6 @@
|
|||
|
||||
# In this notebook, we will develop the API that will call our model. This module initializes the model, transforms the input so that it is in the appropriate format and defines the scoring method that will produce the predictions. The API will expect the input to be in JSON format. Once a request is received, the API will convert the json encoded request body into the image format. There are two main functions in the API. The first function loads the model and returns a scoring function. The second function process the images and uses the first function to score them.
|
||||
|
||||
import logging
|
||||
from testing_utilities import img_url_to_json
|
||||
|
||||
# We use the writefile magic to write the contents of the below cell to driver.py which includes the driver methods.
|
||||
|
||||
# +
|
||||
# %%writefile driver.py
|
||||
import base64
|
||||
|
@ -36,26 +31,29 @@ import os
|
|||
import timeit as t
|
||||
from io import BytesIO
|
||||
from pprint import pprint
|
||||
|
||||
import PIL
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import numpy as np
|
||||
import torchvision
|
||||
from torchvision import datasets, models, transforms
|
||||
import PIL
|
||||
from PIL import Image, ImageOps
|
||||
from PIL import Image
|
||||
from testing_utilities import img_url_to_json
|
||||
from torchvision import models, transforms
|
||||
|
||||
_LABEL_FILE = os.getenv('LABEL_FILE', "synset.txt")
|
||||
# We use the writefile magic to write the contents of the below cell to driver.py which includes the driver methods.
|
||||
|
||||
_LABEL_FILE = os.getenv("LABEL_FILE", "synset.txt")
|
||||
_NUMBER_RESULTS = 3
|
||||
|
||||
|
||||
def _create_label_lookup(label_path):
|
||||
with open(label_path, 'r') as f:
|
||||
with open(label_path, "r") as f:
|
||||
label_list = [l.rstrip() for l in f]
|
||||
|
||||
|
||||
def _label_lookup(*label_locks):
|
||||
return [label_list[l] for l in label_locks]
|
||||
|
||||
|
||||
return _label_lookup
|
||||
|
||||
|
||||
|
@ -65,80 +63,86 @@ def _load_model():
|
|||
model = model.cuda()
|
||||
softmax = nn.Softmax(dim=1).cuda()
|
||||
model = model.eval()
|
||||
|
||||
preprocess_input = transforms.Compose([
|
||||
torchvision.transforms.Resize((224, 224), interpolation=PIL.Image.BICUBIC),
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
|
||||
])
|
||||
|
||||
|
||||
preprocess_input = transforms.Compose(
|
||||
[
|
||||
torchvision.transforms.Resize((224, 224), interpolation=PIL.Image.BICUBIC),
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
|
||||
]
|
||||
)
|
||||
|
||||
def predict_for(image):
|
||||
image = preprocess_input(image)
|
||||
with torch.no_grad():
|
||||
image = image.expand(1,3,224,224)
|
||||
image = image.expand(1, 3, 224, 224)
|
||||
image_gpu = image.type(torch.float).cuda()
|
||||
outputs = model(image_gpu)
|
||||
pred_proba = softmax(outputs)
|
||||
return pred_proba.cpu().numpy().squeeze()
|
||||
|
||||
|
||||
return predict_for
|
||||
|
||||
|
||||
def _base64img_to_pil_image(base64_img_string):
|
||||
if base64_img_string.startswith('b\''):
|
||||
if base64_img_string.startswith("b'"):
|
||||
base64_img_string = base64_img_string[2:-1]
|
||||
base64Img = base64_img_string.encode('utf-8')
|
||||
base64Img = base64_img_string.encode("utf-8")
|
||||
|
||||
# Preprocess the input data
|
||||
startPreprocess = t.default_timer()
|
||||
# Preprocess the input data
|
||||
decoded_img = base64.b64decode(base64Img)
|
||||
img_buffer = BytesIO(decoded_img)
|
||||
|
||||
# Load image with PIL (RGB)
|
||||
pil_img = Image.open(img_buffer).convert('RGB')
|
||||
pil_img = Image.open(img_buffer).convert("RGB")
|
||||
return pil_img
|
||||
|
||||
|
||||
def create_scoring_func(label_path=_LABEL_FILE):
|
||||
logger = logging.getLogger("model_driver")
|
||||
|
||||
|
||||
start = t.default_timer()
|
||||
labels_for = _create_label_lookup(label_path)
|
||||
predict_for = _load_model()
|
||||
end = t.default_timer()
|
||||
|
||||
loadTimeMsg = "Model loading time: {0} ms".format(round((end-start)*1000, 2))
|
||||
loadTimeMsg = "Model loading time: {0} ms".format(round((end - start) * 1000, 2))
|
||||
logger.info(loadTimeMsg)
|
||||
|
||||
|
||||
def call_model(image, number_results=_NUMBER_RESULTS):
|
||||
pred_proba = predict_for(image).squeeze()
|
||||
selected_results = np.flip(np.argsort(pred_proba), 0)[:number_results]
|
||||
labels = labels_for(*selected_results)
|
||||
return list(zip(labels, pred_proba[selected_results].astype(np.float64)))
|
||||
|
||||
return call_model
|
||||
|
||||
|
||||
def get_model_api():
|
||||
logger = logging.getLogger("model_driver")
|
||||
scoring_func = create_scoring_func()
|
||||
|
||||
|
||||
def process_and_score(images_dict, number_results=_NUMBER_RESULTS):
|
||||
start = t.default_timer()
|
||||
|
||||
results = {}
|
||||
for key, base64_img_string in images_dict.items():
|
||||
rgb_image = _base64img_to_pil_image(base64_img_string)
|
||||
results[key]=scoring_func(rgb_image, number_results=_NUMBER_RESULTS)
|
||||
|
||||
results[key] = scoring_func(rgb_image, number_results=number_results)
|
||||
|
||||
end = t.default_timer()
|
||||
|
||||
logger.info("Predictions: {0}".format(results))
|
||||
logger.info("Predictions took {0} ms".format(round((end-start)*1000, 2)))
|
||||
return (results, 'Computed in {0} ms'.format(round((end-start)*1000, 2)))
|
||||
logger.info("Predictions took {0} ms".format(round((end - start) * 1000, 2)))
|
||||
return (results, "Computed in {0} ms".format(round((end - start) * 1000, 2)))
|
||||
|
||||
return process_and_score
|
||||
|
||||
|
||||
def version():
|
||||
return torch.__version__
|
||||
|
||||
|
||||
# -
|
||||
|
||||
# Let's test the module.
|
||||
|
@ -157,7 +161,7 @@ predict_for = get_model_api()
|
|||
|
||||
jsonimg = img_url_to_json(IMAGEURL)
|
||||
json_load_img = json.loads(jsonimg)
|
||||
body = json_load_img['input']
|
||||
body = json_load_img["input"]
|
||||
resp = predict_for(body)
|
||||
|
||||
pprint(resp[0])
|
||||
|
|
|
@ -25,15 +25,15 @@
|
|||
|
||||
# %load_ext autoreload
|
||||
# %autoreload 2
|
||||
import os
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from testing_utilities import to_img, img_url_to_json, plot_predictions
|
||||
from testing_utilities import to_img, img_url_to_json, plot_predictions
|
||||
import requests
|
||||
from dotenv import get_key
|
||||
|
||||
# %matplotlib inline
|
||||
|
||||
image_name = get_key('.env', 'docker_login') + '/' +get_key('.env', 'image_repo')
|
||||
image_name = get_key(".env", "docker_login") + "/" + get_key(".env", "image_repo")
|
||||
image_name
|
||||
|
||||
# Run the Docker conatainer in the background and open port 80. Notice we are using nvidia-docker and not docker command.
|
||||
|
@ -55,30 +55,34 @@ IMAGEURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Lynx_lynx_
|
|||
plt.imshow(to_img(IMAGEURL))
|
||||
|
||||
jsonimg = img_url_to_json(IMAGEURL)
|
||||
jsonimg[:100]
|
||||
jsonimg[:100]
|
||||
|
||||
headers = {'content-type': 'application/json'}
|
||||
headers = {"content-type": "application/json"}
|
||||
# %time r = requests.post('http://0.0.0.0:80/score', data=jsonimg, headers=headers)
|
||||
print(r)
|
||||
r.json()
|
||||
|
||||
# Let's try a few more images.
|
||||
|
||||
images = ('https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Lynx_lynx_poing.jpg/220px-Lynx_lynx_poing.jpg',
|
||||
'https://upload.wikimedia.org/wikipedia/commons/3/3a/Roadster_2.5_windmills_trimmed.jpg',
|
||||
'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Harmony_of_the_Seas_%28ship%2C_2016%29_001.jpg/1920px-Harmony_of_the_Seas_%28ship%2C_2016%29_001.jpg',
|
||||
'http://yourshot.nationalgeographic.com/u/ss/fQYSUbVfts-T7pS2VP2wnKyN8wxywmXtY0-FwsgxpiZv_E9ZfPsNV5B0ER8-bOdruvNfMD5EbP4SznWz4PYn/',
|
||||
'https://cdn.arstechnica.net/wp-content/uploads/2012/04/bohol_tarsier_wiki-4f88309-intro.jpg',
|
||||
'http://i.telegraph.co.uk/multimedia/archive/03233/BIRDS-ROBIN_3233998b.jpg')
|
||||
images = (
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Lynx_lynx_poing.jpg/220px-Lynx_lynx_poing.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/3/3a/Roadster_2.5_windmills_trimmed.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Harmony_of_the_Seas_%28ship%2C_2016%29_001.jpg/1920px-Harmony_of_the_Seas_%28ship%2C_2016%29_001.jpg",
|
||||
"http://yourshot.nationalgeographic.com/u/ss/fQYSUbVfts-T7pS2VP2wnKyN8wxywmXtY0-FwsgxpiZv_E9ZfPsNV5B0ER8-bOdruvNfMD5EbP4SznWz4PYn/",
|
||||
"https://cdn.arstechnica.net/wp-content/uploads/2012/04/bohol_tarsier_wiki-4f88309-intro.jpg",
|
||||
"http://i.telegraph.co.uk/multimedia/archive/03233/BIRDS-ROBIN_3233998b.jpg",
|
||||
)
|
||||
|
||||
url = 'http://0.0.0.0:80/score'
|
||||
results = [requests.post(url, data=img_url_to_json(img), headers=headers) for img in images]
|
||||
url = "http://0.0.0.0:80/score"
|
||||
results = [
|
||||
requests.post(url, data=img_url_to_json(img), headers=headers) for img in images
|
||||
]
|
||||
|
||||
plot_predictions(images, results)
|
||||
|
||||
# Next let's quickly check what the request response performance is for the locally running Docker container.
|
||||
|
||||
image_data = list(map(img_url_to_json, images)) # Retrieve the images and data
|
||||
image_data = list(map(img_url_to_json, images)) # Retrieve the images and data
|
||||
|
||||
timer_results = list()
|
||||
for img in image_data:
|
||||
|
@ -87,7 +91,7 @@ for img in image_data:
|
|||
|
||||
timer_results
|
||||
|
||||
print('Average time taken: {0:4.2f} ms'.format(10**3 * np.mean(timer_results)))
|
||||
print("Average time taken: {0:4.2f} ms".format(10 ** 3 * np.mean(timer_results)))
|
||||
|
||||
# + {"active": "ipynb", "language": "bash"}
|
||||
# docker stop $(docker ps -q)
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#
|
||||
# This guide assumes is designed to be run on linux and requires that the Azure CLI is installed.
|
||||
|
||||
import os
|
||||
import json
|
||||
from testing_utilities import write_json_to_file
|
||||
from dotenv import set_key, get_key
|
||||
|
|
|
@ -27,14 +27,15 @@ import numpy as np
|
|||
from testing_utilities import img_url_to_json, to_img, plot_predictions
|
||||
import requests
|
||||
import json
|
||||
|
||||
# %matplotlib inline
|
||||
|
||||
service_json = !kubectl get service azure-dl -o json
|
||||
service_dict = json.loads(''.join(service_json))
|
||||
app_url = service_dict['status']['loadBalancer']['ingress'][0]['ip']
|
||||
# service_json = !kubectl get service azure-dl -o json
|
||||
service_dict = json.loads("".join(service_json))
|
||||
app_url = service_dict["status"]["loadBalancer"]["ingress"][0]["ip"]
|
||||
|
||||
scoring_url = 'http://{}/score'.format(app_url)
|
||||
version_url = 'http://{}/version'.format(app_url)
|
||||
scoring_url = "http://{}/score".format(app_url)
|
||||
version_url = "http://{}/version".format(app_url)
|
||||
|
||||
# Quickly check the web application is working
|
||||
|
||||
|
@ -47,26 +48,33 @@ IMAGEURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Lynx_lynx_
|
|||
plt.imshow(to_img(IMAGEURL))
|
||||
|
||||
# headers = {'content-type': 'application/json','X-Marathon-App-Id': app_id}
|
||||
headers = {'content-type': 'application/json'}
|
||||
headers = {"content-type": "application/json"}
|
||||
jsonimg = img_url_to_json(IMAGEURL)
|
||||
r = requests.post(scoring_url, data=jsonimg, headers=headers) # Run the request twice since the first time takes a
|
||||
# little longer due to the loading of the model
|
||||
r = requests.post(
|
||||
scoring_url, data=jsonimg, headers=headers
|
||||
) # Run the request twice since the first time takes a
|
||||
# little longer due to the loading of the model
|
||||
# %time r = requests.post(scoring_url, data=jsonimg, headers=headers)
|
||||
r.json()
|
||||
|
||||
# From the results above we can see that the model correctly classifies this as an Lynx.
|
||||
# From the results above we can see that the model correctly classifies this as an Lynx.
|
||||
# The computation took around 70 ms and the whole round trip around 240 ms. The round trip time will depend on where the resuests are being made.
|
||||
|
||||
# Lets try a few more images
|
||||
|
||||
images = ('https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Lynx_lynx_poing.jpg/220px-Lynx_lynx_poing.jpg',
|
||||
'https://upload.wikimedia.org/wikipedia/commons/3/3a/Roadster_2.5_windmills_trimmed.jpg',
|
||||
'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Harmony_of_the_Seas_%28ship%2C_2016%29_001.jpg/1920px-Harmony_of_the_Seas_%28ship%2C_2016%29_001.jpg',
|
||||
'http://yourshot.nationalgeographic.com/u/ss/fQYSUbVfts-T7pS2VP2wnKyN8wxywmXtY0-FwsgxpiZv_E9ZfPsNV5B0ER8-bOdruvNfMD5EbP4SznWz4PYn/',
|
||||
'https://cdn.arstechnica.net/wp-content/uploads/2012/04/bohol_tarsier_wiki-4f88309-intro.jpg',
|
||||
'http://i.telegraph.co.uk/multimedia/archive/03233/BIRDS-ROBIN_3233998b.jpg')
|
||||
images = (
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Lynx_lynx_poing.jpg/220px-Lynx_lynx_poing.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/3/3a/Roadster_2.5_windmills_trimmed.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Harmony_of_the_Seas_%28ship%2C_2016%29_001.jpg/1920px-Harmony_of_the_Seas_%28ship%2C_2016%29_001.jpg",
|
||||
"http://yourshot.nationalgeographic.com/u/ss/fQYSUbVfts-T7pS2VP2wnKyN8wxywmXtY0-FwsgxpiZv_E9ZfPsNV5B0ER8-bOdruvNfMD5EbP4SznWz4PYn/",
|
||||
"https://cdn.arstechnica.net/wp-content/uploads/2012/04/bohol_tarsier_wiki-4f88309-intro.jpg",
|
||||
"http://i.telegraph.co.uk/multimedia/archive/03233/BIRDS-ROBIN_3233998b.jpg",
|
||||
)
|
||||
|
||||
results = [requests.post(scoring_url, data=img_url_to_json(img), headers=headers) for img in images]
|
||||
results = [
|
||||
requests.post(scoring_url, data=img_url_to_json(img), headers=headers)
|
||||
for img in images
|
||||
]
|
||||
|
||||
plot_predictions(images, results)
|
||||
|
||||
|
@ -74,7 +82,7 @@ plot_predictions(images, results)
|
|||
|
||||
# Next lets quickly check what the request response performance is for our deployed model.
|
||||
|
||||
image_data = list(map(img_url_to_json, images)) # Retrieve the images and data
|
||||
image_data = list(map(img_url_to_json, images)) # Retrieve the images and data
|
||||
|
||||
timer_results = list()
|
||||
for img in image_data:
|
||||
|
@ -83,6 +91,6 @@ for img in image_data:
|
|||
|
||||
timer_results
|
||||
|
||||
print('Average time taken: {0:4.2f} ms'.format(10**3 * np.mean(timer_results)))
|
||||
print("Average time taken: {0:4.2f} ms".format(10 ** 3 * np.mean(timer_results)))
|
||||
|
||||
# We have tested that the model works and we can mode on to the [next notebook to get sense of its throughput](06_SpeedTestWebApp.ipynb)
|
||||
# We have tested that the model works and we can mode on to the [next notebook to get sense of its throughput](06_SpeedTestWebApp.ipynb)
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
# +
|
||||
import asyncio
|
||||
import json
|
||||
import urllib.request
|
||||
from timeit import default_timer
|
||||
|
||||
import aiohttp
|
||||
|
@ -41,16 +40,16 @@ print(aiohttp.__version__)
|
|||
# We will test our deployed service with 100 calls. We will only have 4 requests concurrently at any time. We have only deployed one pod on one node and increasing the number of concurrent calls does not really increase throughput. Feel free to try different values and see how the service responds.
|
||||
|
||||
NUMBER_OF_REQUESTS = 100 # Total number of requests
|
||||
CONCURRENT_REQUESTS = 4 # Number of requests at a time
|
||||
CONCURRENT_REQUESTS = 4 # Number of requests at a time
|
||||
|
||||
# Get the IP address of our service
|
||||
|
||||
service_json = !kubectl get service azure-dl -o json
|
||||
service_dict = json.loads(''.join(service_json))
|
||||
app_url = service_dict['status']['loadBalancer']['ingress'][0]['ip']
|
||||
service_dict = json.loads("".join(service_json))
|
||||
app_url = service_dict["status"]["loadBalancer"]["ingress"][0]["ip"]
|
||||
|
||||
scoring_url = 'http://{}/score'.format(app_url)
|
||||
version_url = 'http://{}/version'.format(app_url)
|
||||
scoring_url = "http://{}/score".format(app_url)
|
||||
version_url = "http://{}/version".format(app_url)
|
||||
|
||||
!curl $version_url # Reports the Tensorflow Version
|
||||
|
||||
|
@ -59,32 +58,40 @@ plt.imshow(to_img(IMAGEURL))
|
|||
|
||||
# Here, we use varitions of the same image to test the service.
|
||||
|
||||
url_list = [[scoring_url, jsonimg] for jsonimg in gen_variations_of_one_image(IMAGEURL, NUMBER_OF_REQUESTS)]
|
||||
url_list = [
|
||||
[scoring_url, jsonimg]
|
||||
for jsonimg in gen_variations_of_one_image(IMAGEURL, NUMBER_OF_REQUESTS)
|
||||
]
|
||||
|
||||
|
||||
def decode(result):
|
||||
return json.loads(result.decode("utf-8"))
|
||||
|
||||
|
||||
async def fetch(url, session, data, headers):
|
||||
start_time = default_timer()
|
||||
async with session.request('post', url, data=data, headers=headers) as response:
|
||||
async with session.request("post", url, data=data, headers=headers) as response:
|
||||
resp = await response.read()
|
||||
elapsed = default_timer() - start_time
|
||||
return resp, elapsed
|
||||
|
||||
|
||||
async def bound_fetch(sem, url, session, data, headers):
|
||||
# Getter function with semaphore.
|
||||
async with sem:
|
||||
return await fetch(url, session, data, headers)
|
||||
|
||||
|
||||
async def await_with_progress(coros):
|
||||
results=[]
|
||||
results = []
|
||||
for f in tqdm(asyncio.as_completed(coros), total=len(coros)):
|
||||
result = await f
|
||||
results.append((decode(result[0]),result[1]))
|
||||
results.append((decode(result[0]), result[1]))
|
||||
return results
|
||||
|
||||
|
||||
async def run(url_list, num_concurrent=CONCURRENT_REQUESTS):
|
||||
headers = {'content-type': 'application/json'}
|
||||
headers = {"content-type": "application/json"}
|
||||
tasks = []
|
||||
# create instance of Semaphore
|
||||
sem = asyncio.Semaphore(num_concurrent)
|
||||
|
@ -98,21 +105,26 @@ async def run(url_list, num_concurrent=CONCURRENT_REQUESTS):
|
|||
tasks.append(task)
|
||||
return await await_with_progress(tasks)
|
||||
|
||||
|
||||
# Below we run the 100 requests against our deployed service
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
start_time = default_timer()
|
||||
complete_responses = loop.run_until_complete(asyncio.ensure_future(run(url_list, num_concurrent=CONCURRENT_REQUESTS)))
|
||||
complete_responses = loop.run_until_complete(
|
||||
asyncio.ensure_future(run(url_list, num_concurrent=CONCURRENT_REQUESTS))
|
||||
)
|
||||
elapsed = default_timer() - start_time
|
||||
print('Total Elapsed {}'.format(elapsed))
|
||||
print('Avg time taken {0:4.2f} ms'.format(1000*elapsed/len(url_list)))
|
||||
print("Total Elapsed {}".format(elapsed))
|
||||
print("Avg time taken {0:4.2f} ms".format(1000 * elapsed / len(url_list)))
|
||||
|
||||
# Below we can see the output of some of our calls
|
||||
|
||||
complete_responses[:3]
|
||||
|
||||
num_succesful=[i[0]['result'][0]['image'][0][0] for i in complete_responses].count('n02127052 lynx, catamount')
|
||||
print('Succesful {} out of {}'.format(num_succesful, len(url_list)))
|
||||
num_succesful = [i[0]["result"][0]["image"][0][0] for i in complete_responses].count(
|
||||
"n02127052 lynx, catamount"
|
||||
)
|
||||
print("Succesful {} out of {}".format(num_succesful, len(url_list)))
|
||||
|
||||
# Example response
|
||||
plt.imshow(to_img(IMAGEURL))
|
||||
|
|
Загрузка…
Ссылка в новой задаче