<jupyter_start><jupyter_text>OverviewThis tutorial shows how to train an image classification model and get feature attributions on your trained model. Dataset[ImageNet](http://www.image-net.org/) is an image database. The images in the database are organized into a hierarchy, with each node of the hierarchy depicted by hundreds and thousands of images. ObjectiveIn this notebook, you will learn how to use AI Explanations with an image model you trained. The purpose of this tutorial is to demonstrate how to explain your model locally using the Explainable AI SDK, without having to deploy to AI Platform. The steps performed include: * Train an ImageNet model.* Get explanations locally in the AI Platform Notebooks environment* \[Optional\] Deploy the model to AI Platform. Costs This tutorial uses billable components of Google Cloud Platform (GCP):* AI Platform Prediction, if you decide to deploy the modelLearn about [Cloud AI Platformpricing](https://cloud.google.com/ml-engine/docs/pricing), and use the [PricingCalculator](https://cloud.google.com/products/calculator/)to generate a cost estimate based on your projected usage.  Before you beginThis notebook is intended to run on [AI Platform Notebooks](https://cloud.google.com/ai-platform-notebooks) since it contains necessary dependencies including (but not limited to) [Explainable AI SDK](https://github.com/GoogleCloudPlatform/explainable_ai_sdk). GPU run-time**Running this notebook in a GPU runtime will make you run training cells much faster.**  Import libraries and define constants<jupyter_code>import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import explainable_ai_sdk
from explainable_ai_sdk.model import configs
import tensorflow_hub as hub
from base64 import b64encode

from explainable_ai_sdk.metadata.tf.v2 import SavedModelMetadataBuilder
from PIL import Image<jupyter_output><empty_output><jupyter_text>Build the ModelBuild an ImageNet model using a pretrained model from TF Hub as a layer.<jupyter_code>model = tf.keras.Sequential([
    tf.keras.Input(shape=(224, 224, 3)),                     
    hub.KerasLayer("https://tfhub.dev/google/imagenet/mobilenet_v2_035_224/classification/4"),
    tf.keras.layers.Softmax()
])
model.summary()<jupyter_output>Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
keras_layer (KerasLayer)     (None, 1001)              1692489   
_________________________________________________________________
softmax (Softmax)            (None, 1001)              0         
=================================================================
Total params: 1,692,489
Trainable params: 0
Non-trainable params: 1,692,489
_________________________________________________________________<jupyter_text>Save the Model Before saving the model, add some preprocessing to the model so that you can just call it with an image without having to resize and decode every time. Here, the model is saved with 3 signatures:1. Serving signature that takes image as bytes and outputs probabilities2. Model signature that receives image as (224, 224)-shaped float array and outputs probabilities. Provide this signature to the metadata builder to pick up the correct inputs for the model.3. Preprocessing signature that takes as image bytes and outputs preprocessed image as an array.<jupyter_code>model_path = 'imagenet_model'

def _preprocess(bytes_input):
    decoded = tf.io.decode_jpeg(bytes_input, channels=3)
    decoded = tf.image.convert_image_dtype(decoded, tf.float32)
    resized = tf.image.resize(decoded, size=(224, 224))
    return resized

@tf.function(input_signature=[tf.TensorSpec([None], tf.string)])
def preprocess_fn(bytes_inputs):
    decoded_images = tf.map_fn(_preprocess, bytes_inputs, dtype=tf.float32, back_prop=False)
    return {"numpy_inputs": decoded_images}  # Make note of the key.

m_call = tf.function(model.call).get_concrete_function(
    [tf.TensorSpec(shape=[None, 224, 224, 3], dtype=tf.float32, name="numpy_inputs")])

@tf.function(input_signature=[tf.TensorSpec([None], tf.string)])
def serving_fn(bytes_inputs):
    images = preprocess_fn(bytes_inputs)
    prob = m_call(**images)
    return prob

tf.saved_model.save(model, model_path, signatures={
    'serving_default': serving_fn,
    'xai_preprocess': preprocess_fn, # Required for XAI
    'xai_model': m_call # Required for XAI since the default fn takes bytes.
    })<jupyter_output><empty_output><jupyter_text>Get ExplanationsIn this section, you will explain the output of the model for input images. The explanation workflow includes the following steps:* Create an explanation metadata file to specify your model's inputs and outputs.* Reload the model through [Explainable AI SDK](https://github.com/GoogleCloudPlatform/explainable_ai_sdk) with explanation parameters.* Call `explain` function with images to get feature attributions.* Use `visualize_attributions` function to visualize the model explanations.  Create Explanation MetadataMetadata specifies which inputs you want to explain in the model and other parameters such as baselines. Use `explainable_ai_sdk` helpers to create and save the metadata in the model folder.<jupyter_code># Provide the model path and signature to explain.
md_builder = SavedModelMetadataBuilder(model_path, signature_name='xai_model')

# Call md_builder.get_metadata() to see inferred inputs and outputs.
md_builder.set_image_metadata('numpy_inputs')  # Set 'numpy_inputs' input as image input.
md_builder.save_metadata(model_path)  # Save the metadata in the model path.<jupyter_output><empty_output><jupyter_text>Reload the Model through Explainable AI SDKWhile reloading the model, you can specify which explanation technique to use. For image models, XRAI technique is recommended. The only parameter XRAI configuration class takes is `step_count`, which specifies the number of intervals for integral approximation.<jupyter_code>lm = explainable_ai_sdk.load_model_from_local_path(
    model_path,  # Model path containing explanation metadata JSON.
    explainable_ai_sdk.XraiConfig()  # XRAI config with default step_count=50.
)<jupyter_output><empty_output><jupyter_text>Explain an InputDownload an image from the web and explain it.<jupyter_code># Download a jellyfish image.
! wget 'https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg' -O 'jellyfish.jpg'
# Show image
im = Image.open('jellyfish.jpg').convert('RGB')
im = im.resize((224, 224))
plt.imshow(im)
# Provide the input as a list of examples.
with open('jellyfish.jpg', 'rb') as f:
    image_bytes = f.read()
attributions = lm.explain([{'bytes_inputs': image_bytes}])<jupyter_output><empty_output><jupyter_text>Visualize AttributionsCall `visualize_attributions` to see the input image with attribution regions highlighted.<jupyter_code># Attributions contain an attribution per given example.
attributions[0].visualize_attributions()

# Get the label
labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
imagenet_labels = np.array(open(labels_path).read().splitlines())
print('Predicted label:', imagenet_labels[108])<jupyter_output>Label Index 108
Example Score: 0.9676
Baseline Score: 0.0128
Approximation Error: 0.0123<jupyter_text>Explore Different ParametersThere are various parameters you can change. `step_count` in XRAI config, baselines, label indices are some of them. Use `params_override` in the `explain` call to change these parameters. Alternatively, you can create a new model instance with updated parameters. To reset other parameters such as [visualization](https://cloud.google.com/ai-platform/prediction/docs/ai-explanations/visualizing-explanations), you need to recreate the metadata while specifying a dictionary for the `visualization` argument in the `set_image_metadata` function.Check out the [github repo](https://github.com/GoogleCloudPlatform/explainable_ai_sdk) for Explainable AI SDK for the API and parameters.<jupyter_code># Explain a different label (conch).
print('Explaining label:', imagenet_labels[113])
attributions = lm.explain([{'bytes_inputs': image_bytes}],
                          params=configs.AttributionParameters(label_indices=[113]))

# Visualize why the model might have thought the image was a conch.
attributions[0].visualize_attributions()<jupyter_output>Explaining label: conch
Label Index 113
Example Score: 0.0043
Baseline Score: 0.0005
Approximation Error: 0.0130<jupyter_text>[Optional] Deploy the Model to AI PlatformIn this optional step, you will deploy the model to AI Platform to have a hosted solution to get model explanations.<jupyter_code>PROJECT_ID = "your-project-id"  # Change to your project ID.<jupyter_output><empty_output><jupyter_text>Create the Model**You only need to run this once.**<jupyter_code>MODEL = 'imagenet'
!gcloud ai-platform models create $MODEL --enable-logging --regions=us-central1<jupyter_output>Using endpoint [https://ml.googleapis.com/]
Created ml engine model [projects/caipe-dev/models/imagenet].<jupyter_text>Save the Model to GCSYou need to save the model to a GCS bucket to deploy to AI Platform.  Create a bucket**You need to run this only once.**Note that you can use an existing GCS bucket instead of running this gsutil command, which creates a new one.<jupyter_code>BUCKET_NAME = PROJECT_ID + "_imagenet_model"  # Rename to your liking
REGION = "us-central1"
! gsutil mb -l $REGION gs://$BUCKET_NAME<jupyter_output><empty_output><jupyter_text>Call `save_model_with_metadata` function to save the model and the metadata to your bucket.This time, you can set different visualization settings and observe the differences.<jupyter_code>md_builder.set_image_metadata('numpy_inputs', visualization={
    "type": "Pixels",  # Recommended for XRAI
    "polarity": "positive",
    "clip_below_percentile": 60,
    "clip_above_percentile": 100,
    "color_map": "viridis"
})

model_gcs_path = "gs://" + BUCKET_NAME + "/model"
md_builder.save_model_with_metadata(model_gcs_path)<jupyter_output><empty_output><jupyter_text>Create a Model Version.<jupyter_code># Each time you create a version the name should be unique
MODEL_VERSION = 'xrai_v1'
# Create the version with gcloud
!gcloud beta ai-platform versions create $MODEL_VERSION \
--model $MODEL \
--origin $model_gcs_path \
--runtime-version 2.1 \
--framework TENSORFLOW \
--python-version 3.7 \
--machine-type n1-standard-4 \
--explanation-method xrai \
--num-integral-steps 50
# Make sure the XRAI model deployed correctly. State should be `READY` in the following log
!gcloud ai-platform versions describe $MODEL_VERSION --model $MODEL<jupyter_output><empty_output><jupyter_text>**Make sure that the state of the model is `READY` before moving on.**  Explain an Image with the Deployed ModelNow, call the deployed model using `explainable_ai_sdk` to get attributions for the same image. First, create a remote model instance through `explainable_ai_sdk` providing project, model, and version parameters. Then, you can call the `explain` function to receive the explanations from the deployed model the same way you would from a local model above.*Note: Deployed models don't support parameter overriding. Hence if you provide `params` in the `explain` call, it won't have any effect in the explanations.*<jupyter_code># Create the model
rm = explainable_ai_sdk.load_model_from_ai_platform(PROJECT_ID, MODEL, MODEL_VERSION)<jupyter_output><empty_output><jupyter_text>Encode the image bytes as a base64 string to be sent over.<jupyter_code>b64str = b64encode(image_bytes).decode('utf-8')

attributions = rm.explain([{'bytes_inputs': {'b64': b64str}}])
# Note the difference in visualization due to different visualization settings.
attributions[0].visualize_attributions()<jupyter_output>Label Index 108
Example Score: 0.9676
Baseline Score: 0.0128
Approximation Error: 0.0123<jupyter_text>Cleaning up*Run these steps only if you deployed a model to AI Platform. Otherwise, feel free to ignore.*To clean up all GCP resources used in this project, you can [delete the GCPproject](https://cloud.google.com/resource-manager/docs/creating-managing-projectsshutting_down_projects) you used for the tutorial.<jupyter_code># Delete model version resource
# Note that if there are multiple model versions, you need to delete them
# one by one.
! gcloud ai-platform versions delete $MODEL_VERSION --quiet --model $MODEL

# Delete model resource
! gcloud ai-platform models delete $MODEL --quiet

# Delete Cloud Storage objects that were created
! gsutil -m rm -r gs://$BUCKET_NAME<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09169278996865204, 'fraction_numerical': 0.014655172413793103, 'mean_word_length': 5.632536382536382, 'pattern_counts': {'":': 6, '<': 60, '<?xml version=': 0, '>': 60, 'https://': 12, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '587358', 'license': 'Apache-2.0', 'n_tokens_mistral': 3778, 'n_tokens_neox': 3560, 'n_words': 1270, 'path': 'gc-ai-notebook-tutorials/tutorials/explainable_ai/sdk_tutorial.ipynb', 'repo_name': 'kalona/training-data-analyst', 'size': '12760'}