Explainability design pattern

In the Explainability design pattern, we look at approaches to understanding how and why models make predictions, with the goal of improving user trust in ML systems.


In [2]:
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
import tensorflow as tf

Explaining simpler models

Here we'll use the learned coefficients from a linear regression model as an explainability approach. Note: be cautious when drawing conclusions from learned weights, see the Explainability section in the book for more details.


In [ ]:
!gsutil cp gs://ml-design-patterns/auto-mpg.csv .

data = pd.read_csv('auto-mpg.csv', na_values='?')
data = data.dropna()

In [10]:
data = data.drop(columns=['car name'])

In [11]:
data = pd.get_dummies(data, columns=['origin'])

In [12]:
data.head()


Out[12]:
mpg cylinders displacement horsepower weight acceleration model year origin_1 origin_2 origin_3
0 18.0 8 307.0 130.0 3504 12.0 70 1 0 0
1 15.0 8 350.0 165.0 3693 11.5 70 1 0 0
2 18.0 8 318.0 150.0 3436 11.0 70 1 0 0
3 16.0 8 304.0 150.0 3433 12.0 70 1 0 0
4 17.0 8 302.0 140.0 3449 10.5 70 1 0 0

In [13]:
labels = data['mpg']
data = data.drop(columns=['mpg', 'cylinders'])

In [14]:
x,y = data,labels
x_train,x_test,y_train,y_test = train_test_split(x,y)

Train a Scikit-learn linear regression model on the data and print the learned coefficients


In [15]:
model = LinearRegression().fit(x_train, y_train)

In [17]:
coefficients = model.coef_
coefdf = pd.DataFrame(coefficients, index=data.columns.tolist(), columns=['Learned coefficients'])

In [18]:
coefdf


Out[18]:
Learned coefficients
displacement 0.018701
horsepower -0.008291
weight -0.007285
acceleration 0.125849
model year 0.762888
origin_1 -1.754593
origin_2 0.981848
origin_3 0.772745

Feature attributions with SHAP

Using the same dataset, we'll train a deep neural net with TensorFlow and use the SHAP library to get feature attributions.


In [19]:
model = tf.keras.Sequential([
  tf.keras.layers.Dense(16, activation='relu', input_shape=[len(x_train.iloc[0])]),
  tf.keras.layers.Dense(16, activation='relu'),
  tf.keras.layers.Dense(1)
])

optimizer = tf.keras.optimizers.RMSprop(0.001)

model.compile(loss='mse',
              optimizer=optimizer,
              metrics=['mae', 'mse'])

In [ ]:
model.fit(x_train, y_train, epochs=1000)

In [23]:
!pip install shap


Collecting shap
  Downloading https://files.pythonhosted.org/packages/a8/77/b504e43e21a2ba543a1ac4696718beb500cfa708af2fb57cb54ce299045c/shap-0.35.0.tar.gz (273kB)
     |████████████████████████████████| 276kB 4.6MB/s 
Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from shap) (1.18.5)
Requirement already satisfied: scipy in /usr/local/lib/python3.6/dist-packages (from shap) (1.4.1)
Requirement already satisfied: scikit-learn in /usr/local/lib/python3.6/dist-packages (from shap) (0.22.2.post1)
Requirement already satisfied: pandas in /usr/local/lib/python3.6/dist-packages (from shap) (1.0.5)
Requirement already satisfied: tqdm>4.25.0 in /usr/local/lib/python3.6/dist-packages (from shap) (4.41.1)
Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.6/dist-packages (from scikit-learn->shap) (0.15.1)
Requirement already satisfied: python-dateutil>=2.6.1 in /usr/local/lib/python3.6/dist-packages (from pandas->shap) (2.8.1)
Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.6/dist-packages (from pandas->shap) (2018.9)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.6/dist-packages (from python-dateutil>=2.6.1->pandas->shap) (1.12.0)
Building wheels for collected packages: shap
  Building wheel for shap (setup.py) ... done
  Created wheel for shap: filename=shap-0.35.0-cp36-cp36m-linux_x86_64.whl size=394130 sha256=a1b289e9cdf28be3cf7d8cd34cf57873896a44839030ecf3c6195152e2cbdd86
  Stored in directory: /root/.cache/pip/wheels/e7/f7/0f/b57055080cf8894906b3bd3616d2fc2bfd0b12d5161bcb24ac
Successfully built shap
Installing collected packages: shap
Successfully installed shap-0.35.0

In [24]:
import shap

In [25]:
# Create an explainer object and get feature attributions for the first 10 examples in our test dataset
explainer = shap.DeepExplainer(model, x_train[:200])
shap_values = explainer.shap_values(x_test.values[:10])


Using TensorFlow backend.
keras is no longer supported, please use tf.keras instead.

In [26]:
# Print the feature attributions for the first example in our test set
shap_values[0][0]


Out[26]:
array([ 0.45134509, -0.17676763,  2.64649224, -0.14349272,  2.01079445,
       -0.02381918, -0.17432044,  1.1733783 ])

In [30]:
# This is the baseline value shap is using
explainer.expected_value.numpy()


Out[30]:
array([22.054289], dtype=float32)

In [31]:
shap.initjs()
shap.force_plot(explainer.expected_value[0].numpy(), shap_values[0][0,:], x_test.iloc[0,:])


Out[31]:
Visualization omitted, Javascript library not loaded!
Have you run `initjs()` in this notebook? If this notebook was from another user you must also trust this notebook (File -> Trust notebook). If you are viewing this notebook on github the Javascript has been stripped for security. If you are using JupyterLab this error is because a JupyterLab extension has not yet been written.

In [32]:
shap.summary_plot(shap_values, feature_names=data.columns.tolist(), class_names=['MPG'])


Feature attributions with Explainable AI

This part is coming soon :) In the mean time , see the docs.

Copyright 2020 Google Inc. 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