diff --git a/deploy_service.py b/deploy_service.py index 72646dc..a0eded2 100644 --- a/deploy_service.py +++ b/deploy_service.py @@ -21,7 +21,7 @@ def deploy_service(args): image = Image(workspace=workspace, name=args.image_name, version=args.image_id) # Get webservice configuration - aci_config = AciWebservice.deploy_configuration(cpu_cores=1, memory_gb=1, + aci_config = AciWebservice.deploy_configuration(cpu_cores=1, memory_gb=5, description='Predict digit value from images ' 'of handwritten digits') diff --git a/environment.yml b/environment.yml index 87c6b46..489cb5d 100644 --- a/environment.yml +++ b/environment.yml @@ -9,5 +9,6 @@ dependencies: - python=3.6.2 - tensorflow==1.10.0 - numpy==1.16.3 + - pillow==6.0.0 - pip: - azureml-defaults diff --git a/notebooks/inference_graph_test.ipynb b/notebooks/inference_graph_test.ipynb index e85b698..f390d5f 100644 --- a/notebooks/inference_graph_test.ipynb +++ b/notebooks/inference_graph_test.ipynb @@ -11,18 +11,9 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 1, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The autoreload extension is already loaded. To reload it, use:\n", - " %reload_ext autoreload\n" - ] - } - ], + "outputs": [], "source": [ "# Configure Notebook\n", "import warnings\n", @@ -89,17 +80,9 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "INFO:tensorflow:Restoring parameters from /home/sebastiangoodfellow/Documents/Code/mnist-azure/assets/outputs/checkpoints/model\n" - ] - } - ], + "outputs": [], "source": [ "# Get workspace\n", "ws = Workspace.get(name='mnist-azure', subscription_id='', \n", diff --git a/notebooks/test_service.ipynb b/notebooks/test_service.ipynb index eb9e0cb..c070574 100644 --- a/notebooks/test_service.ipynb +++ b/notebooks/test_service.ipynb @@ -11,9 +11,18 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 70, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + } + ], "source": [ "# Configure Notebook\n", "import warnings\n", @@ -51,14 +60,14 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 71, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "['val_0.jpg', 'val_1.jpg', 'val_10.jpg', 'val_100.jpg', 'val_1000.jpg']\n" + "['val_2670.jpg', 'val_4752.jpg', 'val_7445.jpg', 'val_7043.jpg', 'val_3452.jpg']\n" ] } ], @@ -73,12 +82,13 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 72, "metadata": {}, "outputs": [], "source": [ "# Get workspace\n", - "workspace = Workspace.get(name='mnist-azure', subscription_id='', resource_group='')\n", + "workspace = Workspace.get(name='mnist-azure', subscription_id='', \n", + " resource_group='')\n", "\n", "# Get web service\n", "service = workspace.webservices['mnist-tf']\n", @@ -87,9 +97,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 73, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "10c76329db2b4f759dde82e9336dd327", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(IntSlider(value=0, description='Image ID', max=9999), Output()), _dom_classes=('widget-i…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "def plot_prediction(file_name_id, file_names):\n", "\n", @@ -99,36 +124,31 @@ " # Get prediction\n", " image_array = imread(os.path.join(DATA_PATH, 'images', file_name)).reshape(image_shape)\n", " \n", - " test_samples = json.dumps({'data': image_array.tolist()})\n", + " test_samples = json.dumps({'data': np.expand_dims(image_array, axis=0).tolist()})\n", " test_samples = bytes(test_samples, encoding='utf8')\n", + " test_samples = json.dumps([{'name': 'row1', 'image': encode_image(image_array)}])\n", " print(test_samples)\n", " \n", - " result = json.loads(service.run(input_data=test_samples))\n", - " print(result)\n", - " \n", + " # prediction = json.loads(service.run(input_data=test_samples))[0]\n", + " service.run(input_data=test_samples)\n", + "\n", " # Plot image\n", " fig = plt.figure(figsize=(5, 5), facecolor='w')\n", " fig.subplots_adjust(wspace=0, hspace=1.2)\n", " ax1 = plt.subplot2grid((1, 1), (0, 0))\n", - "# ax1.set_title('Prediction: {}\\nScore: {} %'.format(np.argmax(prediction), \n", - "# int(prediction[0][np.argmax(prediction)] * 100)), \n", - "# fontsize=16)\n", + " ax1.set_title('Prediction: {}\\nScore: {} %'.format(np.argmax(prediction), \n", + " int(prediction[np.argmax(prediction)] * 100)), \n", + " fontsize=16)\n", " ax1.imshow(image_array[:, :, 0], cmap='gray', vmin=0, vmax=255)\n", " ax1.axes.get_xaxis().set_visible(False)\n", " ax1.axes.get_yaxis().set_visible(False)\n", " \n", " plt.show()\n", - "\n", - " \n", - "def get_prediction(file_name):\n", - " image_array = imread(os.path.join(DATA_PATH, 'images', file_name)).reshape(image_shape)\n", - " if graph_type is 'array':\n", - " return image_array, sess.run(fetches=[prediction], feed_dict={images: [image_array]})[0]\n", - " elif graph_type is 'string':\n", - " image = cv2.imencode('.jpg', image_array)[1].tostring()\n", - " # image = open(os.path.join(DATA_PATH, 'images', file_name), 'rb').read()\n", - " return image_array, sess.run(fetches=[prediction], feed_dict={images: [image]})[0]\n", " \n", + "def encode_image(image):\n", + " \"\"\"b64 encode RGB numpy array.\"\"\"\n", + " image_string = cv2.imencode('.jpg', image)[1].tostring()\n", + " return base64.b64encode(image_string).decode(\"utf-8\")\n", "\n", "# Launch interactive plotting widget\n", "_ = interact(\n", @@ -137,6 +157,34 @@ " file_names=fixed(file_names)\n", ") # 2634" ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'test_samples' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtest_samples\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'test_samples' is not defined" + ] + } + ], + "source": [ + "test_samples" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/scoring.py b/scoring.py index 7319b6b..0682f23 100644 --- a/scoring.py +++ b/scoring.py @@ -6,7 +6,10 @@ # 3rd party imports import os import json +import base64 import numpy as np +from PIL import Image +from io import BytesIO import tensorflow as tf from azureml.core.model import Model @@ -44,7 +47,17 @@ def init(): def run(raw_data): - data = np.array(json.loads(raw_data)['data']) - out = predictions.eval(session=sess, feed_dict={images: data}) - y_hat = np.argmax(out, axis=1) - return json.dumps(y_hat.tolist()) + """Run model inference.""" + # Convert raw data to a numpy array + # data = np.array(json.loads(raw_data)['data']) + + # Load raw data + data = json.loads(raw_data) + + # Get image arrays + inputs = np.array([np.array(Image.open(BytesIO(base64.b64decode(row['image']))), dtype=np.uint8) for row in data]) + + # Run model inverse with input data + output = predictions.eval(session=sess, feed_dict={images: inputs}) + + return json.dumps(output.tolist())