Open In Colab  View Notebook on GitHub

๐Ÿ•ต๏ธโ€โ™€๏ธ Analize predictions with explainability methods#

In this tutorial you will learn to log and explore NLP model explanations using Transformers and the following Libraries

  • Transformers Interpret

  • Shap

Interpretability and explanation information in Argilla is not limited to these two libraries. You can populate this information using your method of choice to highlight specific tokens.

This tutorial is useful to get started and understand the underlying structure of explanation information in Argilla records.

monitoring-textclassification-shaptransformersinterpret-explainability

Beyond browsing examples during model development and evaluation, storing explainability information in Argilla can be really useful for monitoring and assessing production models (more tutorials on this soon!)

Letโ€™s get started!

Running Argilla#

For this tutorial, you will need to have an Argilla server running. There are two main options for deploying and running Argilla:

Deploy Argilla on Hugging Face Spaces: If you want to run tutorials with external notebooks (e.g., Google Colab) and you have an account on Hugging Face, you can deploy Argilla on Spaces with a few clicks:

deploy on spaces

For details about configuring your deployment, check the official Hugging Face Hub guide.

Launch Argilla using Argillaโ€™s quickstart Docker image: This is the recommended option if you want Argilla running on your local machine. Note that this option will only let you run the tutorial locally and not with an external notebook service.

For more information on deployment options, please check the Deployment section of the documentation.

Tip

This tutorial is a Jupyter Notebook. There are two options to run it:

  • Use the Open in Colab button at the top of this page. This option allows you to run the notebook directly on Google Colab. Donโ€™t forget to change the runtime type to GPU for faster model training and inference.

  • Download the .ipynb file by clicking on the View source link at the top of the page. This option allows you to download the notebook and run it on your local machine or on a Jupyter notebook tool of your choice.

[ ]:
%pip install argilla transformers-interpret==0.5.2 datasets transformers shap==0.40.0 numba==0.53.1 -qqq

Letโ€™s import the Argilla module for reading and writing data:

[ ]:
import argilla as rg
[ ]:
If you are running Argilla using the Docker quickstart image or Hugging Face Spaces, you need to init the Argilla client with the `URL` and `API_KEY`:
[ ]:
# Replace api_url with the url to your HF Spaces URL if using Spaces
# Replace api_key if you configured a custom API key
rg.init(
    api_url="http://localhost:6900",
    api_key="team.apikey"
)

Finally, letโ€™s include the imports we need:

[ ]:
from datasets import load_dataset

import transformers
from transformers import AutoModelForSequenceClassification, AutoTokenizer

import torch

from transformers_interpret import SequenceClassificationExplainer
import shap

from argilla import TokenAttributions, TextClassificationRecord

from sklearn.preprocessing import MinMaxScaler

Token attributions and what do highlight colors mean?#

Argilla enables you to register token attributions as part of the dataset records. For getting token attributions, you can use methods such as Integrated Gradients or SHAP. These methods try to provide a mechanism to interpret model predictions. The attributions work as follows:

  • [0,1] Positive attributions (in blue) reflect those tokens that are making the model predict the specific predicted label.

  • [-1, 0] Negative attributions (in red) reflect those tokens that can influence the model to predict a label other than the specific predicted label.

Using Transformers Interpret#

In this example, we will use the sst sentiment dataset and a distilbert-based sentiment classifier. For getting model explanation information, we will use the excellent Transformers Interpret library by Charles Pierse.

Warning

Computing model explanation information is computationally intensive and might be really slow if you donโ€™t have a GPU. Even if you have a GPU, it could take around 3-4 minutes to compute this information for 500 records of this dataset. Try reducing the number of records using modifying the .select(range(1500)) method call.

Create a fully searchable dataset with model predictions and explanations#

[ ]:
# Load Stanford sentiment treebank test set
dataset = load_dataset("sst", "default", split="test")

# Let's use a sentiment classifier fine-tuned on sst
model_name = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(model_name)

# use gpu if available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

tokenizer = AutoTokenizer.from_pretrained(model_name)

# Define the explainer using transformers_interpret
cls_explainer = SequenceClassificationExplainer(model, tokenizer)

# remove overlapping ##tokens
def merge_word_attributions(word_attributions):
    sentence =[]
    i = 0
    for attribution in word_attributions:
        word = attribution[0]
        score =  attribution[1]

        if "##" in word:
            previous_word = sentence[i-1][0]
            previous_score = sentence[i-1][1]
            sentence[i-1] = (previous_word+ word[2:], previous_score if abs(previous_score) > abs(score) else score)
        else:
            sentence.append(attribution)
            i+=1
    return sentence


records = []
for example in dataset.select(range(500)):
    # Build Token attributions objects
    word_attributions = merge_word_attributions(cls_explainer(example["sentence"]))
    token_attributions = [
        TokenAttributions(
            token=token, attributions={cls_explainer.predicted_class_name: score}
        )  # ignore first (CLS) and last (SEP) tokens
        for token, score in word_attributions[1:-1]
    ]
    # Build Text classification records
    record = rg.TextClassificationRecord(
        text=example["sentence"],
        prediction=[(cls_explainer.predicted_class_name, cls_explainer.pred_probs)],
        explanation={"text": token_attributions},
    )
    records.append(record)

# Build Argilla dataset with interpretations for each record
rg.log(records, name="transformers_interpret_example")

Using Shap#

In this example, we will use the widely-used Shap library by Scott Lundberg.

Create a fully searchable dataset with model predictions and explanations#

This example is very similar to the one above. The main difference is that we need to scale the values from Shap to match the range required by Argilla UI. This restriction is for visualization purposes. If you are more interested in monitoring use cases you might not need to rescale.

Warning

Computing model explanation information is computationally intensive and might be really slow if you donโ€™t have a GPU. Even if you have a GPU, it could take around 1-2 minutes to compute this information for 100 records of this dataset. Try reducing the number of records using modifying the .select(range(1500)) method call.

[ ]:
from pygments.token import Text
# Let's use a sentiment classifier fine-tuned on sst
model_name = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)


# use gpu if available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

# Transformers pipeline model
pipeline = transformers.pipeline(
    "sentiment-analysis",
    model=model,
    tokenizer=tokenizer,
    device=device,
    top_k=None
)

# Load Stanford treebank dataset only the first 100 records for testing
sst = load_dataset("sst2", split="train[0:100]")

# Use shap's library text explainer
explainer = shap.Explainer(pipeline)
shap_values = explainer(sst["sentence"])

# Instantiate the scaler
scaler = MinMaxScaler(feature_range=[-1, 1])

predictions = pipeline(sst["sentence"])

for i in range(0, len(shap_values.values)):
    # Scale shap values betweeen -1 and 1 (using e.g., scikit-learn MinMaxScaler
    scaled = scaler.fit_transform(shap_values.values[i])

    # get prediction label idx for indexing attributions and shap_values
    # sorts by score to get the max score prediction
    sorted_predictions = sorted(predictions[i], key=lambda d: d["score"], reverse=True)
    label_idx = 0 if sorted_predictions[0]["label"] == "NEGATIVE" else 1

    # Build token attributions
    token_attributions = [
        TokenAttributions(
            token=token, attributions={shap_values.output_names[label_idx]: score}
        )
        for token, score in zip(shap_values.data[i], [row[label_idx] for row in scaled])
    ]
    # build annotation label
    annotation = "POSITIVE" if sst["label"][i] == 1 else "NEGATIVE"

    # Build Argilla record
    record = TextClassificationRecord(
        Text=sst["sentence"][i],
        prediction=[(pred["label"], pred["score"]) for pred in predictions[i]],
        annotation=label,
        explanation={"text": token_attributions},
    )
    # add record
    records.append(record)
# Log records
rg.log(records, name="argilla_shap_example")

Next steps#

If you want to continue learning Argilla:

๐Ÿ™‹โ€โ™€๏ธ Join the Argilla Slack community!

โญ Argilla Github repo to stay updated.

๐Ÿ“š Argilla documentation for more guides and tutorials.