diff --git a/docs/samples/explanation/alibi/moviesentiment/README.md b/docs/samples/explanation/alibi/moviesentiment/README.md index 040718cac1e..22e9f5b7138 100644 --- a/docs/samples/explanation/alibi/moviesentiment/README.md +++ b/docs/samples/explanation/alibi/moviesentiment/README.md @@ -199,3 +199,6 @@ make train You can then store the `model.joblib` in a bucket accessible from your Kubernetes cluster. +## Run on Notebook + +You can also run this example on [notebook](./kfserving_text_explainer.ipynb) \ No newline at end of file diff --git a/docs/samples/explanation/alibi/moviesentiment/kfserving_text_explainer.ipynb b/docs/samples/explanation/alibi/moviesentiment/kfserving_text_explainer.ipynb new file mode 100644 index 00000000000..0c766104e28 --- /dev/null +++ b/docs/samples/explanation/alibi/moviesentiment/kfserving_text_explainer.ipynb @@ -0,0 +1,252 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Sample for KFService Explainer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The notebook shows how to use KFServing SDK to create KFService with explainer, predictor." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [], + "source": [ + "from kubernetes import client\n", + "\n", + "from kfserving import KFServingClient\n", + "from kfserving import constants\n", + "from kfserving import V1alpha2EndpointSpec\n", + "from kfserving import V1alpha2PredictorSpec\n", + "from kfserving import V1alpha2ExplainerSpec\n", + "from kfserving import V1alpha2AlibiExplainerSpec\n", + "from kfserving import V1alpha2SKLearnSpec\n", + "from kfserving import V1alpha2KFServiceSpec\n", + "from kfserving import V1alpha2KFService\n", + "from kubernetes.client import V1ResourceRequirements\n", + "from kubernetes import client, config\n", + "import kubernetes.client\n", + "import os\n", + "import requests \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define KFService with Explainer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Add predictor and explainer on the endpoint spec" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "api_version = constants.KFSERVING_GROUP + '/' + constants.KFSERVING_VERSION\n", + "default_endpoint_spec = V1alpha2EndpointSpec(\n", + " predictor=V1alpha2PredictorSpec(\n", + " min_replicas=1,\n", + " sklearn=V1alpha2SKLearnSpec(\n", + " storage_uri='gs://seldon-models/sklearn/moviesentiment',\n", + " resources=V1ResourceRequirements(\n", + " requests={'cpu': '100m', 'memory': '1Gi'},\n", + " limits={'cpu': '100m', 'memory': '1Gi'}))),\n", + " explainer=V1alpha2ExplainerSpec(\n", + " min_replicas=1, \n", + " alibi=V1alpha2AlibiExplainerSpec(\n", + " type='AnchorText',\n", + " resources=V1ResourceRequirements(\n", + " requests={'cpu': '100m', 'memory': '2Gi'},\n", + " limits={'cpu': '100m', 'memory': '2Gi'}))))\n", + " \n", + "kfsvc = V1alpha2KFService(api_version=api_version,\n", + " kind=constants.KFSERVING_KIND,\n", + " metadata=client.V1ObjectMeta(\n", + " name='moviesentiment', namespace='kubeflow'),\n", + " spec=V1alpha2KFServiceSpec(default=default_endpoint_spec))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create KFService" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Call KFServingClient to create KFService." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "KFServing = KFServingClient()\n", + "KFServing.create(kfsvc)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Check the KFService" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NAME READY DEFAULT_TRAFFIC CANARY_TRAFFIC URL \n", + "moviesentiment True 100 http://moviesentiment-predict.kubeflow.example.com\n" + ] + } + ], + "source": [ + "KFServing.get('moviesentiment', namespace='kubeflow', watch=True, timeout_seconds=120)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Predict the sentiment for the sentence" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "34.67.11.82\n" + ] + } + ], + "source": [ + "api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient())\n", + "service = api_instance.read_namespaced_service(\"istio-ingressgateway\", \"istio-system\", exact='true')\n", + "cluster_ip = service.status.load_balancer.ingress[0].ip\n", + "print(cluster_ip)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "b'{\"predictions\": [0]}'\n", + "b'{\"predictions\": [1]}'\n" + ] + } + ], + "source": [ + "url = \"http://\" + cluster_ip + \"/v1/models/moviesentiment:predict\"\n", + "headers = {'Host': 'moviesentiment-predict.kubeflow.example.com'}\n", + "response = requests.post(url, '{\"instances\":[\"This is a bad book .\"]}', headers=headers)\n", + "print(response.content)\n", + "response = requests.post(url, '{\"instances\":[\"This is a good book .\"]}', headers=headers)\n", + "print(response.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Explain the Prediction" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "b'{\"names\": [\"bad\"], \"precision\": 1.0, \"coverage\": 0.5007, \"raw\": {\"feature\": [3], \"mean\": [1.0], \"precision\": [1.0], \"coverage\": [0.5007], \"examples\": [{\"covered\": [[\"This is a bad book UNK\"], [\"UNK is UNK bad book UNK\"], [\"UNK is UNK bad book .\"], [\"This UNK UNK bad book UNK\"], [\"This is UNK bad UNK .\"], [\"UNK UNK UNK bad book .\"], [\"UNK is a bad UNK UNK\"], [\"UNK UNK a bad UNK .\"], [\"UNK UNK a bad book UNK\"], [\"UNK is UNK bad book .\"]], \"covered_true\": [[\"UNK is UNK bad UNK UNK\"], [\"UNK is UNK bad UNK UNK\"], [\"UNK is UNK bad book UNK\"], [\"This UNK UNK bad book UNK\"], [\"UNK UNK a bad book .\"], [\"This is UNK bad UNK UNK\"], [\"UNK UNK UNK bad UNK UNK\"], [\"This is UNK bad UNK .\"], [\"This is UNK bad UNK .\"], [\"This UNK a bad UNK .\"]], \"covered_false\": [], \"uncovered_true\": [], \"uncovered_false\": []}], \"all_precision\": 0, \"num_preds\": 1000101, \"names\": [\"bad\"], \"positions\": [10], \"instance\": \"This is a bad book .\", \"prediction\": 0}}'\n", + "b'{\"names\": [\"good\"], \"precision\": 1.0, \"coverage\": 0.5007, \"raw\": {\"feature\": [3], \"mean\": [1.0], \"precision\": [1.0], \"coverage\": [0.5007], \"examples\": [{\"covered\": [[\"This is a good book .\"], [\"This is a good book UNK\"], [\"UNK is UNK good book UNK\"], [\"This UNK UNK good UNK UNK\"], [\"UNK UNK a good book UNK\"], [\"This UNK a good UNK UNK\"], [\"UNK is a good book UNK\"], [\"UNK is a good UNK UNK\"], [\"This is UNK good book .\"], [\"UNK UNK UNK good UNK UNK\"]], \"covered_true\": [[\"UNK UNK UNK good book UNK\"], [\"UNK is a good book .\"], [\"UNK is a good book UNK\"], [\"UNK is UNK good book .\"], [\"UNK UNK UNK good book .\"], [\"UNK is a good book .\"], [\"UNK is UNK good UNK UNK\"], [\"UNK UNK UNK good UNK .\"], [\"This UNK a good UNK UNK\"], [\"This is a good UNK .\"]], \"covered_false\": [], \"uncovered_true\": [], \"uncovered_false\": []}], \"all_precision\": 0, \"num_preds\": 1000001, \"names\": [\"good\"], \"positions\": [10], \"instance\": \"This is a good book .\", \"prediction\": 1}}'\n" + ] + } + ], + "source": [ + "url = \"http://\" + cluster_ip + \"/v1/models/moviesentiment:explain\"\n", + "headers = {'Host': 'moviesentiment-explain.kubeflow.example.com'}\n", + "response = requests.post(url, '{\"instances\":[\"This is a bad book .\"]}', headers=headers)\n", + "print(response.content)\n", + "response = requests.post(url, '{\"instances\":[\"This is a good book .\"]}', headers=headers)\n", + "print(response.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Delete the KFService" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "KFServing.delete('moviesentiment', namespace='kubeflow')\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}