Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for AIX360 explanations #1094

Merged
merged 50 commits into from
Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
b6b4f22
Rebase AIX explainer on KFServing
drewbutlerbb4 Aug 24, 2020
185ab97
Add AIXExplainer to function
drewbutlerbb4 Aug 25, 2020
4d96fa5
Add AIX explainer implementation
drewbutlerbb4 Aug 31, 2020
c05a106
Add AIX explainer server
drewbutlerbb4 Aug 31, 2020
1b48186
Fix link with local reference
drewbutlerbb4 Sep 1, 2020
b00d2db
aix api patch
Tomcli Sep 2, 2020
2b19c46
aix api patch
drewbutlerbb4 Sep 4, 2020
0684791
Add random forest predictor
drewbutlerbb4 Sep 4, 2020
071d7e5
Fix link
drewbutlerbb4 Sep 9, 2020
b3ed924
Update aix-explainer image version
drewbutlerbb4 Sep 9, 2020
dba8834
Pull master
drewbutlerbb4 Sep 9, 2020
a40fe6f
Clean up example readme
Tomcli Sep 9, 2020
fe0a3c7
Clean up example readme
drewbutlerbb4 Sep 9, 2020
7d270b6
Refactor rfserver and update image
drewbutlerbb4 Sep 10, 2020
48612e3
Update inferenceservice.yaml
Tomcli Sep 10, 2020
64fd254
Update aix-explainer.yaml
Tomcli Sep 10, 2020
74d9e16
update version format to match with KFServing
drewbutlerbb4 Sep 10, 2020
fa97fb2
update v1beta1 spec and tests
Tomcli Sep 11, 2020
b665823
update v1beta1 spec and tests for rebase
drewbutlerbb4 Sep 11, 2020
4bdf8fe
Refactor sample content and rework notebook
drewbutlerbb4 Sep 16, 2020
a078ecd
Add newline at EOF
drewbutlerbb4 Sep 18, 2020
41a056b
generate aix SDK spec and add predictor test
Tomcli Sep 21, 2020
e0c267b
generate aix SDK spec and add predictor test
Tomcli Sep 21, 2020
b25d5f3
Merge pull request #7 from Tomcli/sdk-gen
drewbutlerbb4 Sep 21, 2020
4bda3af
add aix explainer build test
Tomcli Sep 21, 2020
449b25e
Merge pull request #8 from Tomcli/sdk-gen
drewbutlerbb4 Sep 21, 2020
2a3b8a4
add missing __init__.py
Tomcli Sep 21, 2020
d9ded20
Merge pull request #9 from Tomcli/sdk-gen
drewbutlerbb4 Sep 21, 2020
bfe2a26
Add explanation e2e check
drewbutlerbb4 Sep 21, 2020
2a07d03
Add explanation e2e check
drewbutlerbb4 Sep 21, 2020
f2c4cf6
fix containerv1 spec
Tomcli Sep 21, 2020
1c0a9f3
fix conflicts
Tomcli Sep 22, 2020
ddd0b8f
rebase api spec
Tomcli Sep 22, 2020
08995e7
fix empty uri for aix framework
Tomcli Sep 22, 2020
ab5155d
Merge pull request #12 from Tomcli/trusted-ai-rebase
drewbutlerbb4 Sep 22, 2020
39d8239
Merge pull request #11 from Tomcli/sdk-gen
drewbutlerbb4 Sep 22, 2020
62b5366
remove testing logs
Tomcli Sep 22, 2020
5153c0a
Merge pull request #13 from Tomcli/trusted-ai-explainer
drewbutlerbb4 Sep 22, 2020
cb3f00e
update e2e test to match with the v1beta1 controller changes
Tomcli Sep 22, 2020
e3215d2
Merge pull request #14 from Tomcli/trusted-ai-explainer
drewbutlerbb4 Sep 22, 2020
afdeadb
Fix logging
drewbutlerbb4 Sep 23, 2020
9f13ca9
address comments on ci config
Tomcli Sep 23, 2020
5308c14
Merge pull request #15 from Tomcli/trusted-ai-explainer
drewbutlerbb4 Sep 23, 2020
645eca3
Make aixexplainer account for asynchronous predict calls
drewbutlerbb4 Sep 25, 2020
eb7625e
Fix e2e test response parsing
drewbutlerbb4 Sep 25, 2020
15d7444
updated generated hack files to align with existing format
Tomcli Sep 25, 2020
f7af3ab
update samples to use v1beta1 api
Tomcli Sep 25, 2020
d37f1f4
Merge pull request #16 from Tomcli/trusted-ai-explainer
drewbutlerbb4 Sep 25, 2020
d2ce61b
Apply suggestions from code review
drewbutlerbb4 Sep 28, 2020
fe6d5d2
Rebase to master
drewbutlerbb4 Sep 28, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/configmap/inferenceservice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ data:
"alibi": {
"image" : "gcr.io/kfserving/alibi-explainer",
"defaultImageVersion": "v0.4.0"
},
"aix": {
"image" : "aipipeline/aix-explainer",
"defaultImageVersion": "0.4.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pointer.

}
}
storageInitializer: |-
Expand Down
68 changes: 68 additions & 0 deletions config/crd/serving.kubeflow.org_inferenceservices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,40 @@ spec:
properties:
explainer:
properties:
aix:
properties:
config:
additionalProperties:
type: string
type: object
resources:
properties:
limits:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
type: object
requests:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
type: object
type: object
runtimeVersion:
type: string
storageUri:
type: string
type:
type: string
required:
- type
type: object
alibi:
properties:
config:
Expand Down Expand Up @@ -1961,6 +1995,40 @@ spec:
properties:
explainer:
properties:
aix:
properties:
config:
additionalProperties:
type: string
type: object
resources:
properties:
limits:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
type: object
requests:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
type: object
type: object
runtimeVersion:
type: string
storageUri:
type: string
type:
type: string
required:
- type
type: object
alibi:
properties:
config:
Expand Down
41 changes: 41 additions & 0 deletions docs/samples/explanation/aix/mnist/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Using AIX to get explanations for MNIST classifications

This is an example of how to explain model outputs using [AIX360](https://github.com/Trusted-AI/AIX360) on KFServing. We will be using mnist, a dataset for handwritten digits, for this model and explain how the model decides the predicted results.
drewbutlerbb4 marked this conversation as resolved.
Show resolved Hide resolved

To deploy the inferenceservice

`kubectl apply -f aix-explainer.yaml`

Then find the url.

`kubectl get inferenceservice`

```
NAME URL READY DEFAULT TRAFFIC CANARY TRAFFIC AGE
aixserver http://aixserver.somecluster/v1/models/aixserver True 100 40m
```

## Prediction
The first step is to [determine the ingress IP and ports](../../../../../README.md#determine-the-ingress-ip-and-ports) and set `INGRESS_HOST` and `INGRESS_PORT`

```
MODEL_NAME=aix-explainer
SERVICE_HOSTNAME=$(kubectl get inferenceservice ${MODEL_NAME} -o jsonpath='{.status.url}' | cut -d "/" -f 3)
python query_explain.py http://${INGRESS_HOST}:${INGRESS_PORT}/v1/models/$MODEL_NAME:explain ${SERVICE_HOSTNAME}
```

To try a different MNIST example add an integer to the end of the query between 0-10,000. The integer chosen will be the index of the image to be chosen in the MNIST dataset.

```
python query_explain.py http://${INGRESS_HOST}:${INGRESS_PORT}/v1/models/$MODEL_NAME:explain ${SERVICE_HOSTNAME} 100
```

## Stopping the Inference Service

`kubectl delete -f aix-explainer.yaml`

## Troubleshooting

`<504> Gateway Timeout <504>` - the explainer is probably taking to long and not sending a response back quickly enough. Either there aren't enough resources allocated or the number of samples the explainer is allowed to take needs to be reduced. To fix this go to aix-explainer.yaml and increase resources. Or to lower the number of allowed samples go to aix-explainer.yaml and add a flag to `explainer: command:` '--num_samples' (the default number of samples is 1000)
drewbutlerbb4 marked this conversation as resolved.
Show resolved Hide resolved

If you see `Configuration "aixserver-explainer-default" does not have any ready Revision` the container may have taken too long to download. If you run `kubectl get revision` and see your revision is stuck in `ContainerCreating` try deleting the inferenceservice and redeploying.
32 changes: 32 additions & 0 deletions docs/samples/explanation/aix/mnist/aix-explainer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: "serving.kubeflow.org/v1alpha2"
kind: "InferenceService"
metadata:
name: "aix-explainer"
namespace: default
spec:
default:
predictor:
custom:
container:
name: predictor
image: aipipeline/rf-predictor:0.4.0
command: ["python", "-m", "rfserver", "--model_name", "aix-explainer"]
imagePullPolicy: Always
resources:
requests:
memory: "2Gi"
cpu: "1"
limits:
memory: "2Gi"
cpu: "1"
explainer:
aix:
type: LimeImages
config:
num_samples: "100"
top_labels: "10"
min_weight: "0.01"
resources:
requests:
cpu: 1
memory: 2Gi
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
279 changes: 279 additions & 0 deletions docs/samples/explanation/aix/mnist/query_explain.ipynb

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions docs/samples/explanation/aix/mnist/query_explain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import os
import sys
import requests
import json
from matplotlib import pyplot as plt
import numpy as np
from aix360.datasets import MNISTDataset
from keras.applications import inception_v3 as inc_net
from keras.preprocessing import image
from keras.applications.imagenet_utils import decode_predictions
import time
from skimage.color import gray2rgb, rgb2gray, label2rgb # since the code wants color images

print('************************************************************')
print('************************************************************')
print('************************************************************')
print("starting query")

if len(sys.argv) < 3:
raise Exception("No endpoint specified. ")
endpoint = sys.argv[1]
headers = {
'Host': sys.argv[2]
}
test_num = 1002
is_file = False
if len(sys.argv) > 3:
try:
test_num = int(sys.argv[2])
except:
is_file = True

if is_file:
inputs = open(sys.argv[2])
inputs = json.load(inputs)
actual = "unk"
else:
data = MNISTDataset()
inputs = data.test_data[test_num]
labels = data.test_labels[test_num]
actual = 0
for x in range(1, len(labels)):
if labels[x] != 0:
actual = x
inputs = gray2rgb(inputs.reshape((-1, 28, 28)))
inputs = np.reshape(inputs, (28,28,3))
input_image = {"instances": [inputs.tolist()]}
print("Sending Explain Query")

x = time.time()

res = requests.post(endpoint, json=input_image, headers=headers)

print("TIME TAKEN: ", time.time() - x)

print(res)
if not res.ok:
res.raise_for_status()
res_json = res.json()
temp = np.array(res_json["explanations"]["temp"])
masks = np.array(res_json["explanations"]["masks"])
top_labels = np.array(res_json["explanations"]["top_labels"])

fig, m_axs = plt.subplots(2,5, figsize = (12,6))
for i, c_ax in enumerate(m_axs.flatten()):
mask = masks[i]
c_ax.imshow(label2rgb(mask, temp, bg_label = 0), interpolation = 'nearest')
c_ax.set_title('Positive for {}\nActual {}'.format(top_labels[i], actual))
c_ax.axis('off')
plt.show()
103 changes: 103 additions & 0 deletions docs/samples/explanation/aix/mnist/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
absl-py==0.9.0
aix360==0.2.0
appnope==0.1.0
asgiref==3.2.7
astor==0.8.1
attrs==19.3.0
backcall==0.1.0
bleach==3.1.4
certifi==2020.4.5.1
chardet==3.0.4
cvxopt==1.2.4
cvxpy==1.0.29
cycler==0.10.0
decorator==4.4.2
defusedxml==0.6.0
dill==0.3.1.1
Django==3.0.5
docutils==0.16
ecos==2.0.7.post1
entrypoints==0.3
future==0.18.2
gast==0.3.3
google-pasta==0.2.0
grpcio==1.28.1
h5py==2.10.0
idna==2.9
image==1.5.28
imageio==2.8.0
importlib-metadata==1.6.0
ipykernel==5.2.0
ipython==7.13.0
ipython-genutils==0.2.0
jedi==0.16.0
Jinja2==2.11.1
joblib==0.14.1
json5==0.9.4
jsonschema==3.2.0
jupyter-client==6.1.2
jupyter-core==4.6.3
jupyterlab==2.0.1
jupyterlab-server==1.0.7
Keras==2.3.1
Keras-Applications==1.0.8
Keras-Preprocessing==1.1.0
kiwisolver==1.2.0
lime==0.2.0.0
Markdown==3.2.1
MarkupSafe==1.1.1
matplotlib==3.2.1
mistune==0.8.4
multiprocess==0.70.9
nbconvert==5.6.1
nbformat==5.0.5
networkx==2.4
notebook==6.0.3
numpy==1.18.2
osqp==0.6.1
pandas==1.0.3
pandocfilters==1.4.2
parso==0.6.2
pexpect==4.8.0
pickleshare==0.7.5
Pillow==5.4.1
prometheus-client==0.7.1
prompt-toolkit==3.0.5
protobuf==3.11.3
ptyprocess==0.6.0
Pygments==2.6.1
pyparsing==2.4.7
pyrsistent==0.16.0
python-dateutil==2.8.1
pytz==2019.3
PyWavelets==1.1.1
PyYAML==5.3.1
pyzmq==19.0.0
requests==2.23.0
scikit-image==0.16.2
scikit-learn==0.22.2.post1
scipy==1.4.1
scs==2.1.2
Send2Trash==1.5.0
shap==0.35.0
six==1.14.0
sqlparse==0.3.1
tensorboard==1.14.0
tensorflow==1.14.0
tensorflow-estimator==1.14.0
termcolor==1.1.0
terminado==0.8.3
testpath==0.4.4
torch==1.4.0
torchvision==0.5.0
tornado==6.0.4
tqdm==4.45.0
traitlets==4.3.3
urllib3==1.25.8
wcwidth==0.1.9
webencodings==0.5.1
Werkzeug==1.0.1
wrapt==1.12.1
xgboost==1.0.2
xport==2.0.2
zipp==3.1.0
1 change: 1 addition & 0 deletions docs/samples/explanation/aix/mnist/rfserver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Random Forest for MNIST on kfserving
6 changes: 6 additions & 0 deletions docs/samples/explanation/aix/mnist/rfserver/rf.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM python:3.7

COPY . .
RUN pip install --upgrade pip && pip install kfserving==0.4.0
RUN pip install -e .
ENTRYPOINT ["python", "-m", "rfserver", "--model_name", "aixserver"]
Binary file not shown.
15 changes: 15 additions & 0 deletions docs/samples/explanation/aix/mnist/rfserver/rfserver/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2019 kubeflow.org.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from .model import RFModel