{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "### NLPExplainer on IMDB dataset" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The class `NLPExplainer` is designed for NLP tasks, acting as a factory of the supported NLP explainers such as integrated-gradient and LIME. `NLPExplainer` provides a unified easy-to-use interface for all the supported explainers. Because the supported NLP explainers in the current version are limited, one can either use `NLPExplainer` or a specific explainer in the package `omnixai.explainers.nlp` to generate explanations." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# This default renderer is used for sphinx docs only. Please delete this cell in IPython.\n", "import plotly.io as pio\n", "pio.renderers.default = \"png\"" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import torch\n", "import torch.nn as nn\n", "import sklearn\n", "from sklearn.datasets import fetch_20newsgroups\n", "\n", "from omnixai.data.text import Text\n", "from omnixai.preprocessing.text import Word2Id\n", "from omnixai.explainers.tabular.agnostic.L2X.utils import Trainer, InputData, DataLoader\n", "from omnixai.explainers.nlp import NLPExplainer\n", "from omnixai.visualization.dashboard import Dashboard" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We apply a simple CNN model for this text classification task. Note that the method `forward` has two inputs `inputs` (token ids) and `masks` (the sentence masks). Note that the first input of the model must be the token ids." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "class TextModel(nn.Module):\n", "\n", " def __init__(self, num_embeddings, num_classes, **kwargs):\n", " super().__init__()\n", " self.num_embeddings = num_embeddings\n", " self.embedding_size = kwargs.get(\"embedding_size\", 50)\n", " self.embedding = nn.Embedding(self.num_embeddings, self.embedding_size)\n", " self.embedding.weight.data.normal_(mean=0.0, std=0.01)\n", " \n", " hidden_size = kwargs.get(\"hidden_size\", 100)\n", " kernel_sizes = kwargs.get(\"kernel_sizes\", [3, 4, 5])\n", " if type(kernel_sizes) == int:\n", " kernel_sizes = [kernel_sizes]\n", "\n", " self.activation = nn.ReLU()\n", " self.conv_layers = nn.ModuleList([\n", " nn.Conv1d(self.embedding_size, hidden_size, k, padding=k // 2) for k in kernel_sizes])\n", " self.dropout = nn.Dropout(0.2)\n", " self.output_layer = nn.Linear(len(kernel_sizes) * hidden_size, num_classes)\n", "\n", " def forward(self, inputs, masks):\n", " embeddings = self.embedding(inputs)\n", " x = embeddings * masks.unsqueeze(dim=-1)\n", " x = x.permute(0, 2, 1)\n", " x = [self.activation(layer(x).max(2)[0]) for layer in self.conv_layers]\n", " outputs = self.output_layer(self.dropout(torch.cat(x, dim=1)))\n", " if outputs.shape[1] == 1:\n", " outputs = outputs.squeeze(dim=1)\n", " return outputs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `Text` object is used to represent a batch of texts/sentences. The package `omnixai.preprocessing.text` provides some transforms related to text data such as `Tfidf` and `Word2Id`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Load the training and test datasets\n", "train_data = pd.read_csv('/home/ywz/data/imdb/labeledTrainData.tsv', sep='\\t')\n", "n = int(0.8 * len(train_data))\n", "x_train = Text(train_data[\"review\"].values[:n])\n", "y_train = train_data[\"sentiment\"].values[:n].astype(int)\n", "x_test = Text(train_data[\"review\"].values[n:])\n", "y_test = train_data[\"sentiment\"].values[n:].astype(int)\n", "class_names = [\"negative\", \"positive\"]\n", "# The transform for converting words/tokens to IDs\n", "transform = Word2Id().fit(x_train)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The preprocessing function converts a batch of texts into token IDs and the masks. The outputs of the preprocessing function must fit the inputs of the model." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "max_length = 256\n", "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", "\n", "def preprocess(X: Text):\n", " samples = transform.transform(X)\n", " max_len = 0\n", " for i in range(len(samples)):\n", " max_len = max(max_len, len(samples[i]))\n", " max_len = min(max_len, max_length)\n", " inputs = np.zeros((len(samples), max_len), dtype=int)\n", " masks = np.zeros((len(samples), max_len), dtype=np.float32)\n", " for i in range(len(samples)):\n", " x = samples[i][:max_len]\n", " inputs[i, :len(x)] = x\n", " masks[i, :len(x)] = 1\n", " return inputs, masks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now train the CNN model and evaluate its performance." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " |████████████████████████████████████████| 100.0% Complete, Loss 0.0010\n" ] } ], "source": [ "model = TextModel(\n", " num_embeddings=transform.vocab_size,\n", " num_classes=len(class_names)\n", ").to(device)\n", "\n", "Trainer(\n", " optimizer_class=torch.optim.AdamW,\n", " learning_rate=1e-3,\n", " batch_size=128,\n", " num_epochs=10,\n", ").train(\n", " model=model,\n", " loss_func=nn.CrossEntropyLoss(),\n", " train_x=transform.transform(x_train),\n", " train_y=y_train,\n", " padding=True,\n", " max_length=max_length,\n", " verbose=True\n", ")" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Test accuracy: 0.8492442322991249\n" ] } ], "source": [ "model.eval()\n", "data = transform.transform(x_test)\n", "data_loader = DataLoader(\n", " dataset=InputData(data, [0] * len(data), max_length),\n", " batch_size=32,\n", " collate_fn=InputData.collate_func,\n", " shuffle=False\n", ")\n", "outputs = []\n", "for inputs in data_loader:\n", " value, mask, target = inputs\n", " y = model(value.to(device), mask.to(device))\n", " outputs.append(y.detach().cpu().numpy())\n", "outputs = np.concatenate(outputs, axis=0)\n", "predictions = np.argmax(outputs, axis=1)\n", "print('Test accuracy: {}'.format(\n", " sklearn.metrics.f1_score(y_test, predictions, average='binary')))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similar to `TabularExplainer`, to initialize `NLPExplainer`, we need to set the following parameters:\n", "\n", " - `explainers`: The names of the explainers to apply, e.g., [\"ig\", \"lime\"].\n", " - `model`: The ML model to explain, e.g., a scikit-learn model, a tensorflow model or a pytorch model.\n", " - `preprocess`: The preprocessing function converting the raw data (a `Text` instance) into the inputs of model.\n", " - `postprocess`: The postprocessing function transforming the outputs of model to a user-specific form, e.g., the predicted probability for each class.\n", " - `mode`: The task type, e.g., \"classification\" or \"regression\".\n", "\n", "The preprocessing function takes a `Text` instance as its input and outputs the processed features that the ML model consumes, e.g., the `Text` object is converted into pytorch tensors in this example.\n", "\n", "The postprocessing function converts the outputs into class probabilities." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# The preprocessing function\n", "preprocess_func = lambda x: tuple(torch.tensor(y).to(device) for y in preprocess(x))\n", "# The postprocessing function\n", "postprocess_func = lambda logits: torch.nn.functional.softmax(logits, dim=1)\n", "# Initialize a NLPExplainer\n", "explainer = NLPExplainer(\n", " explainers=[\"ig\", \"lime\", \"polyjuice\"],\n", " mode=\"classification\",\n", " model=model,\n", " preprocess=preprocess_func,\n", " postprocess=postprocess_func,\n", " params={\"ig\": {\"embedding_layer\": model.embedding, \n", " \"id2token\": transform.id_to_word}}\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is no \"global explanation\" for `NLPExplainer` currently. One can simply call explainer.explain to generate local explanations for NLP tasks." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:polyjuice.polyjuice_wrapper:Setup Polyjuice.\n", "INFO:polyjuice.polyjuice_wrapper:Setup SpaCy processor.\n", "INFO:polyjuice.polyjuice_wrapper:Setup perplexity scorer.\n", "Asking to truncate to max_length but no maximum length is provided and the model has no predefined maximum length. Default to no truncation.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Integrated gradient results:\n" ] }, { "data": { "text/html": [ "
Instance 0: Class positive
\n", "
it was a fantastic performance

\n", "
Instance 1: Class positive
\n", "
best film ever

\n", "
Instance 2: Class positive
\n", "
such a great show

\n", "
Instance 3: Class negative
\n", "
it was a horrible movie

\n", "
Instance 4: Class negative
\n", "
i never watched something as bad

" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "LIME results:\n" ] }, { "data": { "text/html": [ "
Instance 0: Class positive
\n", "
fantastic performance was it a

\n", "
Instance 1: Class positive
\n", "
best ever film

\n", "
Instance 2: Class positive
\n", "
great such a show

\n", "
Instance 3: Class negative
\n", "
horrible was it movie a

\n", "
Instance 4: Class negative
\n", "
bad never i something as ve watched

" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Counterfactual results:\n" ] }, { "data": { "image/png": "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" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "x = Text([\n", " \"it was a fantastic performance!\",\n", " \"best film ever\",\n", " \"such a great show!\",\n", " \"it was a horrible movie\",\n", " \"i've never watched something as bad\"\n", "])\n", "# Generates explanations\n", "local_explanations = explainer.explain(x)\n", "\n", "print(\"Integrated gradient results:\")\n", "local_explanations[\"ig\"].ipython_plot(class_names=class_names)\n", "print(\"LIME results:\")\n", "local_explanations[\"lime\"].ipython_plot(class_names=class_names)\n", "print(\"Counterfactual results:\")\n", "local_explanations[\"polyjuice\"].ipython_plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Given the generated explanations, we can launch a dashboard (a Dash app) for visualization by setting the test instances and the generated local explanations." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dash is running on http://127.0.0.1:8050/\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:omnixai.visualization.dashboard:Dash is running on http://127.0.0.1:8050/\n", "\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " * Serving Flask app \"omnixai.visualization.dashboard\" (lazy loading)\n", " * Environment: production\n", "\u001b[31m WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n", "\u001b[2m Use a production WSGI server instead.\u001b[0m\n", " * Debug mode: off\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:werkzeug: * Running on http://127.0.0.1:8050/ (Press CTRL+C to quit)\n" ] } ], "source": [ "# Launch a dashboard for visualization\n", "dashboard = Dashboard(\n", " instances=x,\n", " local_explanations=local_explanations,\n", " class_names=class_names\n", ")\n", "dashboard.show()" ] } ], "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.7.5" } }, "nbformat": 4, "nbformat_minor": 2 }