From ebc348cbcfee362a5ca010f84368dafd372c2627 Mon Sep 17 00:00:00 2001 From: carolinef35 Date: Wed, 19 Nov 2025 17:26:42 -0600 Subject: [PATCH 1/9] added two colab files under the quickstart folder in examples --- .../quickstart/influence_function_lds.ipynb | 238 +++++++++++ .../influence_function_noisy_label.ipynb | 377 ++++++++++++++++++ 2 files changed, 615 insertions(+) create mode 100644 examples/quickstart/influence_function_lds.ipynb create mode 100644 examples/quickstart/influence_function_noisy_label.ipynb diff --git a/examples/quickstart/influence_function_lds.ipynb b/examples/quickstart/influence_function_lds.ipynb new file mode 100644 index 000000000..9117183c6 --- /dev/null +++ b/examples/quickstart/influence_function_lds.ipynb @@ -0,0 +1,238 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# This example shows how to use the IF to calculate LDS scores to detect noisy labels in the MNIST dataset." + ], + "metadata": { + "id": "GrM_LjGcxl_v" + } + }, + { + "cell_type": "markdown", + "source": [ + "Install dependencies that are not in Colaboratory by default." + ], + "metadata": { + "id": "lAg59xgUpsGX" + } + }, + { + "cell_type": "code", + "source": [ + "!pip install dattri" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Vh91mxvupuBQ", + "outputId": "cca73eae-a777-41e7-9e68-91154c3e01bc" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting dattri\n", + " Downloading dattri-0.2.0-py3-none-any.whl.metadata (12 kB)\n", + "Requirement already satisfied: numpy>=1.25 in /usr/local/lib/python3.12/dist-packages (from dattri) (2.0.2)\n", + "Requirement already satisfied: scipy>=1.11 in /usr/local/lib/python3.12/dist-packages (from dattri) (1.16.3)\n", + "Requirement already satisfied: pyyaml in /usr/local/lib/python3.12/dist-packages (from dattri) (6.0.3)\n", + "Collecting pretty-midi (from dattri)\n", + " Downloading pretty_midi-0.2.11.tar.gz (5.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.6/5.6 MB\u001b[0m \u001b[31m77.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting mido>=1.1.16 (from pretty-midi->dattri)\n", + " Downloading mido-1.3.3-py3-none-any.whl.metadata (6.4 kB)\n", + "Requirement already satisfied: six in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (1.17.0)\n", + "Requirement already satisfied: importlib_resources in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (6.5.2)\n", + "Requirement already satisfied: packaging in /usr/local/lib/python3.12/dist-packages (from mido>=1.1.16->pretty-midi->dattri) (25.0)\n", + "Downloading dattri-0.2.0-py3-none-any.whl (173 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m173.9/173.9 kB\u001b[0m \u001b[31m17.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading mido-1.3.3-py3-none-any.whl (54 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m54.6/54.6 kB\u001b[0m \u001b[31m5.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hBuilding wheels for collected packages: pretty-midi\n", + " Building wheel for pretty-midi (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for pretty-midi: filename=pretty_midi-0.2.11-py3-none-any.whl size=5595886 sha256=040d236e342933211d5e6aa0d420205d4acc00910f6c13a15c2ac289abe8733c\n", + " Stored in directory: /root/.cache/pip/wheels/f4/ad/93/a7042fe12668827574927ade9deec7f29aad2a1001b1501882\n", + "Successfully built pretty-midi\n", + "Installing collected packages: mido, pretty-midi, dattri\n", + "Successfully installed dattri-0.2.0 mido-1.3.3 pretty-midi-0.2.11\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Import libraries needed to run code.\n", + "\n", + "Note: If \"\"----- WARNING: CUDA devices not detected. This will cause the model to run very slow! -----\" message appears, change your runtime type to GPU by going to Runtime -> Change runtime type and selecting 'GPU'." + ], + "metadata": { + "id": "WzfoDddNpxty" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SU8NDpOPpg-Z" + }, + "outputs": [], + "source": [ + "import argparse\n", + "\n", + "import torch\n", + "from torch import nn\n", + "from torch.utils.data import DataLoader\n", + "\n", + "from dattri.algorithm.influence_function import IFAttributorCG\n", + "from dattri.benchmark.load import load_benchmark\n", + "from dattri.metric import lds\n", + "from dattri.task import AttributionTask" + ] + }, + { + "cell_type": "markdown", + "source": [ + "LDS Score: metric used to evaluate the influence or importance of individual training data points on the model's predictions for specific test data points.\n", + "\n", + "\n", + "* A higher LDS score for a training data point suggests that removing or perturbing that data point would have a more significant impact on the model's behavior or predictions.\n", + "\n" + ], + "metadata": { + "id": "w7x4js5WvpTN" + } + }, + { + "cell_type": "code", + "source": [ + "if __name__ == \"__main__\":\n", + " # initialize argument parser to handle command-line arguments\n", + " parser = argparse.ArgumentParser()\n", + " # dynamically set device to 'cuda' if available, otherwise 'cpu'\n", + " parser.add_argument(\"--device\", type=str, default=\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + " args = parser.parse_args([])\n", + "\n", + " # download the pre-trained benchmark\n", + " # includes some trained model and ground truth\n", + " model_details, groundtruth = load_benchmark(\n", + " model=\"mlp\", dataset=\"mnist\", metric=\"lds\"\n", + " )\n", + "\n", + " # define a functional loss function 'f' that calculates CrossEntropyLoss\n", + " # takes model parameters and a data-target pair (image, label) as input\n", + " def f(params, data_target_pair):\n", + " image, label = data_target_pair\n", + " loss = nn.CrossEntropyLoss()\n", + " # apply the model with given parameters to the image.\n", + " yhat = torch.func.functional_call(model_details[\"model\"], params, image)\n", + " return loss(yhat, label.long())\n", + "\n", + " # initialize the AttributionTask with the model, loss function, and model checkpoints\n", + " task = AttributionTask(\n", + " model=model_details[\"model\"].to(args.device),\n", + " loss_func=f,\n", + " checkpoints=model_details[\"models_full\"][0], # use one full model checkpoint\n", + " )\n", + "\n", + " # initialize the IFAttributorCG (Influence Function Attributor using Conjugate Gradient)\n", + " # requires the task, device, regularization parameter, and max iterations\n", + " attributor = IFAttributorCG(\n", + " task=task, device=args.device, regularization=5e-3, max_iter=10\n", + " )\n", + " # cache the training data using a DataLoader\n", + " # pre-processes/stores training data for attribution\n", + " attributor.cache(\n", + " DataLoader(\n", + " model_details[\"train_dataset\"],\n", + " batch_size=5000,\n", + " sampler=model_details[\"train_sampler\"],\n", + " )\n", + " )\n", + "\n", + " # perform attribution without gradient calculation (inference mode)\n", + " with torch.no_grad():\n", + " # calculate influence scores of training data on test data\n", + " score = attributor.attribute(\n", + " DataLoader(\n", + " model_details[\"train_dataset\"],\n", + " batch_size=5000,\n", + " sampler=model_details[\"train_sampler\"],\n", + " ),\n", + " DataLoader(\n", + " model_details[\"test_dataset\"],\n", + " batch_size=5000,\n", + " sampler=model_details[\"test_sampler\"],\n", + " ),\n", + " )\n", + "\n", + " # calculate the LDS (Leave-one-out Data Sensitivity) score\n", + " lds_score = lds(score, groundtruth)[0]\n", + " # print the mean of the non-null LDS scores\n", + " print(\"lds:\", torch.mean(lds_score[~torch.isnan(lds_score)]))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4TclgMyVp18L", + "outputId": "931d75ff-17de-4927-9942-5d3b64f0c5c7" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 9.91M/9.91M [00:00<00:00, 14.6MB/s]\n", + "100%|██████████| 28.9k/28.9k [00:00<00:00, 482kB/s]\n", + "100%|██████████| 1.65M/1.65M [00:00<00:00, 4.52MB/s]\n", + "100%|██████████| 4.54k/4.54k [00:00<00:00, 9.62MB/s]\n", + "calculating gradient of training set...: 0%| | 0/1 [00:00=1.25 in /usr/local/lib/python3.12/dist-packages (from dattri) (2.0.2)\n", + "Requirement already satisfied: scipy>=1.11 in /usr/local/lib/python3.12/dist-packages (from dattri) (1.16.3)\n", + "Requirement already satisfied: pyyaml in /usr/local/lib/python3.12/dist-packages (from dattri) (6.0.3)\n", + "Collecting pretty-midi (from dattri)\n", + " Downloading pretty_midi-0.2.11.tar.gz (5.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.6/5.6 MB\u001b[0m \u001b[31m52.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting mido>=1.1.16 (from pretty-midi->dattri)\n", + " Downloading mido-1.3.3-py3-none-any.whl.metadata (6.4 kB)\n", + "Requirement already satisfied: six in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (1.17.0)\n", + "Requirement already satisfied: importlib_resources in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (6.5.2)\n", + "Requirement already satisfied: packaging in /usr/local/lib/python3.12/dist-packages (from mido>=1.1.16->pretty-midi->dattri) (25.0)\n", + "Downloading dattri-0.2.0-py3-none-any.whl (173 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m173.9/173.9 kB\u001b[0m \u001b[31m14.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading mido-1.3.3-py3-none-any.whl (54 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m54.6/54.6 kB\u001b[0m \u001b[31m3.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hBuilding wheels for collected packages: pretty-midi\n", + " Building wheel for pretty-midi (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for pretty-midi: filename=pretty_midi-0.2.11-py3-none-any.whl size=5595886 sha256=fd5739ba3229b2d5d8d6f2a149d3ef913fab1df96517502ff5a2b98bf9bdbee7\n", + " Stored in directory: /root/.cache/pip/wheels/f4/ad/93/a7042fe12668827574927ade9deec7f29aad2a1001b1501882\n", + "Successfully built pretty-midi\n", + "Installing collected packages: mido, pretty-midi, dattri\n", + "Successfully installed dattri-0.2.0 mido-1.3.3 pretty-midi-0.2.11\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Import libraries needed to run code." + ], + "metadata": { + "id": "_3J9JbbIdJOI" + } + }, + { + "cell_type": "code", + "source": [ + "import argparse\n", + "from functools import partial\n", + "\n", + "import torch\n", + "from torch import nn\n", + "\n", + "from dattri.algorithm.influence_function import (\n", + " IFAttributorArnoldi,\n", + " IFAttributorCG,\n", + " IFAttributorDataInf,\n", + " IFAttributorExplicit,\n", + " IFAttributorLiSSA,\n", + ")\n", + "from dattri.benchmark.datasets.mnist import create_mnist_dataset, train_mnist_lr\n", + "from dattri.benchmark.utils import SubsetSampler, flip_label\n", + "from dattri.metric import mislabel_detection_auc\n", + "from dattri.task import AttributionTask" + ], + "metadata": { + "id": "PDYn9ys1dN1X" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Dictionary to manage and intialize different influence function algorithms with their specific configurations. Each key is a specific arritbution method and the corresponding value is a class constructor with some of its arguments already pre-filled." + ], + "metadata": { + "id": "yKAFh2xKeVxo" + } + }, + { + "cell_type": "code", + "source": [ + "ATTRIBUTOR_MAP = {\n", + " \"explicit\": partial(IFAttributorExplicit, regularization=0.01),\n", + " \"cg\": partial(IFAttributorCG, regularization=0.01),\n", + " \"lissa\": partial(IFAttributorLiSSA, recursion_depth=100),\n", + " \"datainf\": partial(IFAttributorDataInf, regularization=0.01),\n", + " \"arnoldi\": partial(IFAttributorArnoldi, regularization=0.01, max_iter=10),\n", + "}" + ], + "metadata": { + "id": "tW-PS8aPfSqD" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Influence Score: how much a single trainign data point affects the model's parameters or its predictions on other data points.\n", + "\n", + "\n", + "* Higher influence indicates that a particular data point is problematic for the model.\n", + "* Mislabeled samples will exert a stronger, often negative, influence on the model's traning process.\n", + "\n", + "\n", + "AUC Score: the probability that the influence function method ranks a randomly chosen positive example (a truly mislabled sample) higher than a randomly chosen negative example (a correctly labeled sample).\n", + "\n", + "\n", + "* Higher AUC valyes indicate better performance.\n", + "* For mislabel detection, an AUC close to 1.0 means the influence scores are very effective at identifying the flipped labels among the correctly labeled ones.\n", + "\n" + ], + "metadata": { + "id": "qLP_rEsrf8jU" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6WwN6-LHbElK", + "outputId": "ea8e92d7-2715-4960-d9da-15baab9db36e" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 9.91M/9.91M [00:00<00:00, 86.8MB/s]\n", + "100%|██████████| 28.9k/28.9k [00:00<00:00, 35.9MB/s]\n", + "100%|██████████| 1.65M/1.65M [00:00<00:00, 62.4MB/s]\n", + "100%|██████████| 4.54k/4.54k [00:00<00:00, 6.65MB/s]\n", + "calculating gradient of training set...: 0%| | 0/1 [00:00" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAH6CAYAAADocQXsAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAok1JREFUeJzs3Xd8VMX6x/FvgEBCb6F3ARGkqAiIIEGUqhTpqNQfInYUy0UUEBs2LCgiKqDoFUVRsWANelUURYErApYLCIhAQOk98/vjmJDdmUM2m03Z8Hm/XnnBeXbO2dnNs7Mns2fniTHGGAEAAAAAAAAAAEuB3O4AAAAAAAAAAAB5FZPoAAAAAAAAAAD4YBIdAAAAAAAAAAAfTKIDAAAAAAAAAOCDSXQAAAAAAAAAAHwwiQ4AAAAAAAAAgA8m0QEAAAAAAAAA8MEkOgAAAAAAAAAAPphEBwAAAAAAAADAB5PoAIA8KSYmJtM/iYmJud1t7d+/X++8846uueYaNW3aVCVKlFDhwoVVvXp1DRgwQF9++WWGx3jttdeUmJioMmXKqFixYmratKkeeOABHTlyJNP9mThxYtrzk5CQcMJjbNmyRYUKFUprP3fu3EzfX25bvHhxWv8j6aefflLPnj1VoUIFFSxYUDExMZo4caIkKTExUTExMVq8eHFE7zMvqVWrlmJiYrR+/fqIHC/c39GuXbt09913q2XLlipVqpRiY2NVsWJFNW7cWJdffrlmzJihffv2RaSPecn69esVExOjWrVq5XZXLOnHmNSfuLg4VahQQU2bNtXQoUP10ksv6eDBg7nd1XwpGsef1HyOiYlRfHy8Nm3a5Ns29T0pt8ee7JY6xs6ePTtb7ycnH39eHrcAAAhHodzuAAAALkOGDLFif/75pz744APf2xs0aJDt/crIyy+/rJEjR0qSatasqQ4dOqhQoUJasWKF5s2bp1dffVWTJ0/W7bff7tz/hhtu0GOPPaZChQrp/PPPV/HixfXpp5/q1ltv1cKFC/Xhhx8qPj4+rL4lJyfr7bffVu/evZ23z5kzR8eOHQvr2JGyfv161a5dWzVr1ozYpElW7du3T926ddP69evVvHlzderUSQULFlSzZs1yu2sRMXToUM2ZM0ezZs3S0KFDc7s7vtauXasLLrhAmzZtUpEiRdSyZUtVqVJFBw8e1OrVqzV37lzNnTtX5557rk4//fTc7u5Jp2LFiurcubMk6dixY9q1a5fWrFmjOXPmaM6cObrhhhv0xBNPaMCAARG7z8TERH322WdKSkrK1Q9R8+K4FS0OHjyoO++8U88//3xudwUAAOCEmEQHAORJrquxFi9enDaJnt1Xa4UrNjZWw4cP1zXXXKMzzjgjLW6M0dSpU3XTTTdp/PjxatOmjdq1axew75tvvqnHHntMxYsX12effaYzzzxTkjf5ff755+uLL77QHXfcoYceeijT/WrevLm+++47Pf/8876T6LNmzVKRIkV06qmnauXKlZm+j/zq22+/1fr169W6deuQvkmQH33yySc6cuSIqlatmmt9uOyyy7Rp0ya1b99e8+bNU0JCQsDtv//+u+bMmaPixYvnUg9Pbg0aNHCOy7/99psmTpyouXPnauDAgdq5c6euuuqqnO8g8pyYmBgVKVJEL7zwgm666SY1atQo2+9z9erV2X4fAAAgf2I5FwAAImjIkCF67rnnAibQJW+y4MYbb1SHDh0kSS+++KK177333itJuu2229Im0CWpfPnyeuqppyRJ06ZN065duzLdr6ZNm+rMM8/UBx98oD/++MO6/T//+Y9+/vln9ezZU2XKlMn08fOz33//XZJUr169XO5J7jnllFPUoEEDxcbG5sr9//bbb/ruu+8kSU8//bQ1gS5JNWrU0B133MHSAXnMKaecohdffFE333yzJOn666/X//73v1zuFfKCAgUK6Nprr9WxY8c0bty4HLnPBg0a5IlvrQEAgOjDJDoAIN/YtGmTrr32WtWrV09xcXEqVaqUzj33XM2YMcO5TMns2bMVExOjoUOHaseOHbr66qtVo0YNFSlSRDVr1tSYMWP0119/RbSPqZPrGzduDIhv3rxZ3377rSRp0KBB1n5t2rRR9erVdejQIb333nth3ffw4cN17NgxzZkzx7ot9av0w4cPz/A4r7zyijp06KCyZcumPVfDhw/Xzz//7Gy/ZcsWXX/99apfv77i4uJUtGhRVa9eXR06dAi4qn7o0KGqXbu2JGnDhg3WOstZlX5d76SkJHXs2FFlypRRfHy8zjzzTL3wwgsB7VPXV09dOmjOnDmZ6k9GaxWnriWdurZ6sGXLlunSSy9Ny8myZcuqU6dOvr//zD6+1PVqU/Nh2LBhAY8vfb/81kTfsGGDpkyZovPPPz+tn6VL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X1544QX16dNHjzzyiB555BGVLVtWLVu21IUXXqjLL79c5cuXz/C+w5GcnCzJG4fKlCkjKTI57yf1+bn88st1+eWXh3Ts1FyuXLly2BPUwcJ9jKk5cKL3tlq1amnFihWZ6k84r4mbb75ZX3zxhT7++GN17txZsbGxatq0qc477zwNGDBAZ599dqb6EKxgwYK699571bNnT02YMEGDBg1SXFycs21G7/mS+/3ITzjvU9ddd53+/e9/68knn9SVV14pSVq5cqW++OILVaxYUX369MnwfrMiq+Oa33OXGk8//qS+jp577jk999xzJ+xXKK/R7M4lAACyC5PoAACEwBiT6X2OHTumSy+9VG+88YaqV6+upKQk1axZ09k29SrwjRs3+h4v9bYTXTEeimHDhumuu+7SM888ow0bNqhUqVK65JJLsnRMPwUKFNDcuXM1btw4vfvuu/ryyy/15Zdfavr06Zo+fbouvvhiLViwQAULFsyW+/frU16Q/qrc4Fjx4sXVu3fvsI6bU49v//796tmzp7Zu3aphw4Zp9OjRqlu3rkqWLKmCBQvq559/1qmnnhrWayczatasqbvuuktlypTRjTfeqA8//FAHDhxQfHy8JKlPnz5atWqVLrroIt1yyy1q2LChSpYsqdjYWB0+fDjDSc0TPZ/Z+VyH8ryl5kubNm3SJg39NGrUKO3/bdu21fr16/Xuu+/qs88+01dffaUPPvhA77//viZMmKAFCxaoQ4cOWXsADt9//70kqUGDBmkfPkQi5/2kHrtz586qWLHiCdv6jc2R7Ed2PMZwhPOaKFq0qD766CN9++23WrRokb766it99dVX+u677/TII4/oqquuSrsKO1w9evRQ69at9dVXX+mJJ57QzTffnKXjhSqc96lWrVqpRYsWWrp0qT777DO1a9cu7fFfccUVKly4cLb2OavjWkbSjz+p+dusWTM1bdr0hPu1bNkyw2PnRC4BAJAdmEQHAES9qlWrSjp+tZTLunXrAtq6bnNJXRqiWrVqmerTsWPHdNlll+nVV19Nm0A/0VVzZ5xxhiRpx44dWrdunbPtd999J0k688wzM9WXYDVr1tT555+vTz75RJJ05ZVXpk04+kl93nbs2KHdu3c7r0ZPff5dz3HDhg3VsGFD3XzzzTLG6NNPP9WgQYO0cOFCvfDCC2lLweQnqZMoe/bscd6+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AqZAgQLmggsuMMOGDTMjRowwb731VobHTv39Fi5c2HTs2NEMGDDANGjQwMTExJjbb7897fUYzO935OeHH35I26dYsWKmTZs2pn///qZXr16mWbNmabc1a9bMbNu2LWDf66+/3kgyBQoUMO3atTMDBw40Z555ppFkxo8f79uXE/Vx3bp1vo/NmON52K5du4B4uGPOie5v8+bNac9BsWLFTOvWrc2AAQPMJZdcYpo1a2YKFixoJJkDBw4EPJexsbHm7LPPNv369TP9+vUzZ5xxhpFkYmJizLPPPuvzm7CljjEVK1ZMG98uv/xy0717d1O/fn0TExNjJJmEhAQzb9483+OEk/PvvPNOWv5ddNFFZvjw4WbEiBHmyy+/TGvz448/mlq1ahlJpnTp0ua8884zgwYNMj179jQNGzY0MTExpmLFilZ/7rnnnrS+N2jQwPTv3990797dNGzY0Pk6CmXcCucxrlq1yiQkJKSNo/369TMXXXSRKVq0qGnVqpU555xznOPPiYTzmmjatKmRZGrXrm0uvvhic+mll5qOHTua+Ph4I8mcf/755siRIyHdf2o+FyxY0LfN0KFD0/rhGnuSk5PT+lSqVCnTs2dP06dPn7Tnqnbt2s6x0PXYwnmfSrV161ZTpEgRI8k0adIkpMfvkjrG1qlTx7Rs2dL3Z9myZcaYrI9rN9xwg4mJiTHnnXeeGThwoGncuHHa7+S1116z9tu9e7fp0KFD2ustdezo27evOfvss03hwoWNJLN69eq0ffzGrUjmEgAAOYlJdABA1DjRBKkxxvz+++/m6quvNnXq1DGFCxc2JUqUMOecc46ZPn268w+y9JOj27ZtM6NGjTLVqlUzhQsXNtWrVzfXXXed2bFjR6b6mDqhlNFP8ORaevPmzTPnnXeeKVmypImPjzenn366uf/++60PATLTn0hMoqd6+eWXTWJioildurSJjY011atXN0OHDjVr1qyx2n7++efmhhtuMC1atDCVKlUyhQsXNpUqVTLnnHOOeeKJJ8zevXutfXbs2GFGjRplatSoYWJjYzM14ZqXJtGNMeavv/4y11xzjalWrZqJjY01VatWNVdccYXZunVr2u/GNYlujDH//e9/zRVXXGHq1atn4uLiTNGiRU2dOnVMp06dzOOPP242b94ckcdnjDELFiww5557rilRokTaxGH6fvkd+/Dh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qyDptm3bMtG7yAg3p/JqPr333ntWrFOnTiHtu3LlyoDtd955J6T9HnjggZDaBTt27JgVC3fsKVasmBU7evSoFTt06FBYxw8VYxQiLb+NUZHkKhialJQU0r6uIqKTJk3KsE20Y4w6sVatWlmxJUuWWLGUlBQr9s0331ixqVOnBmy/9tprWehd3sQYhUiK9jHK9Xe16++ne++9N6TYgQMHItOxkxhjFCIp2seo3FCpUiUrdsUVV1ix8ePHW7FChQplePzgeVbJPe7u2bMnw2PlhoxyiivRAQAAAAAAAADwwSQ6AAAAAAAAAAA+mEQHAAAAAAAAAMAHk+gAAAAAAAAAAPjIl4VFJ06caMVci+KH8phchRV3794dUj+qVasWsB0XFxfSfh9//LEV6927d8D23r17QzpWdqOQQ/apX7++FVu2bFnAdtGiRa02ruJajzzyiBV78MEHA7aTk5Mz28VsEc3FZoJf85L0+eefW7EaNWpkaz9cz0Uoz+v27dut2KuvvhpWHy677DIrtmHDBit2zjnnWLFIFhtljEKkRfMYld2y8npr3769FcuPhUSDMUadmKsQlatQoOvcp0AB+1qh4HZvvPGG1Sa4+Kgkff311yfsZ16S38ao0qVLW7EBAwZYsXr16lmx//u//wvYLlmypNXGlTuhCs6xTZs2WW3uueceK/bss89aMVfx9bwg2saoXr16BWzPnj3bauP6+8nF9btzzTMgc/LbGBUqV/H1CRMmBGx/9tlnVhty7sSibYzKbsHzDGeddZbV5sknn7RiFStWzLY+Se7Cpc8991y23me4KCwKAAAAAAAAAECYmEQHAAAAAAAAAMAHk+gAAAAAAAAAAPhgEh0AAAAAAAAAAB+FcrsDWRUfH2/FunbtasVcRQOCCym6Fth3FX75448/Qupb8CL+zz//vNXm9NNPt2IXXHCBFevUqVPA9uuvvx5SHxAdatWqZcU++eQTKxZKIRxXEdFbb701rH4hc1xFrVxFRPfv32/FXMWLevbsGbDtypOEhITQO5gB17GuvvrqiB3fVag5KwW9AOSucIs5TZo0yYqdDEVEkbF+/foFbLuKiLrO6V1FRENp5zp+nz59rNi5555rxaKp2Gi0GDlypBW76aabrFjdunXDOv7Bgwet2HfffRfSvlWqVLFiwedllStXttpMmzbNirnGzhkzZoTUD5zY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b+Q6hmxRtGjRgG1XQbf+/fuHdCxXXowfPz5g+4knnshE75DXuQqar169OmLHdxVHb968ecB2x44drTaugtfnnXdexPoFf6EUs8tMu7zA9fflXXfdZcVCeUz33HNPRPqUX7zwwgtWrFSpUhnu9/fff1ux4PczSdq2bZsV27lzZ8D20qVLrTau8/d169ZZsf9v796DrSrrPwAvFTQZcwpBQhnMQmzU8dIFlFLTRtOUgXHGy6R4gVKLpEwNwZwwEZxBTEml8MKQMSXewFQSdbwRimmZM5k6ajZIUxGaoiCi0B+/+dWs9X1Xe7HPOpy9z3me/97PvHudV9i8e+3XM+uT2u+K1x8zZkyYkyr+7N27d8iK6z/uuOPCnGXLloWMzZMquN1jjz1Clvo3XizYrlquPGfOnJClSkSLP3P58uVhTqqgfcqUKSFLfdcoWrRoUcM5dL1zzjknZKl7prPOOmtLLKft+E10AAAAAAAo4RAdAAAAAABKOEQHAAAAAIASDtEBAAAAAKBESxeLbrfddrnxAQccEOZceeWVIataNlMsfOjbt2+YkyoAfPXVV0O29957V/qZRffcc0+l69M5TjnllJDNmDGj6evNnz8/N77ggguavlYVkyZNClmzxaLTp0/v6HK6tREjRoSsWCicKuFMSRURt0KJaMrw4cND9stf/jJkxX33gw8+CHOmTp1a38K6gdRnTvFzbtasWWFO1bKi4mdolsXCn1T58YoVK0L27rvvhiz1ni3uSdtss02YkyrUXrBgQchoLan30+OPP54bp+6FUgW3VffKL33pS7mxYtF6pMped9lll5Cl/rxTpdHNSt3DdLYvf/nLufFFF10U5hx88MEhSxW2Fe/56vyzoftIFQXut99+DV+X+txduXJlLWvqLgYMGNBwzo033hiyCy+8MGSpQtJXXnmluYV1wOmnn54bpwoke/WKRzgbNmwIWXGvVyLaOYol6JujeP/79ttvhzn33ntvyA4//PBK17/pppty41Sh5EEHHRSyKiWiTz31VMhSZ3O0niFDhlSa99hjj3XyStqT30QHAAAAAIASDtEBAAAAAKCEQ3QAAAAAACjhEB0AAAAAAEq0dLFosTxt6dKlYc5WW20VslSJ1dy5c0M2ePDghmv42Mc+FrIqJSZV3XzzzbVdi8336KOPhuyf//xnyPr371/peoccckhu3K9f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\n" + }, + "metadata": {} + } + ] + } + ] +} \ No newline at end of file From dcf032bcfea361539da2b5af4f6a4c0094a6fa2a Mon Sep 17 00:00:00 2001 From: carolinef35 Date: Wed, 17 Dec 2025 14:22:05 -0600 Subject: [PATCH 2/9] changed two colab files under the quickstart folder in examples --- .../quickstart/influence_function_lds.ipynb | 113 ++++++------- .../influence_function_noisy_label.ipynb | 159 +++++++++--------- 2 files changed, 135 insertions(+), 137 deletions(-) diff --git a/examples/quickstart/influence_function_lds.ipynb b/examples/quickstart/influence_function_lds.ipynb index 9117183c6..40badb98c 100644 --- a/examples/quickstart/influence_function_lds.ipynb +++ b/examples/quickstart/influence_function_lds.ipynb @@ -19,7 +19,7 @@ { "cell_type": "markdown", "source": [ - "# This example shows how to use the IF to calculate LDS scores to detect noisy labels in the MNIST dataset." + "# This example shows an LDS score calculation." ], "metadata": { "id": "GrM_LjGcxl_v" @@ -115,10 +115,9 @@ { "cell_type": "markdown", "source": [ - "LDS Score: metric used to evaluate the influence or importance of individual training data points on the model's predictions for specific test data points.\n", + "LDS Score: used to evaluate the overall performance of a data attribution method.\n", "\n", - "\n", - "* A higher LDS score for a training data point suggests that removing or perturbing that data point would have a more significant impact on the model's behavior or predictions.\n", + "* A score near 1 means the attribution method accurately predicts the model's response to data changes\n", "\n" ], "metadata": { @@ -128,70 +127,70 @@ { "cell_type": "code", "source": [ - "if __name__ == \"__main__\":\n", - " # initialize argument parser to handle command-line arguments\n", - " parser = argparse.ArgumentParser()\n", - " # dynamically set device to 'cuda' if available, otherwise 'cpu'\n", - " parser.add_argument(\"--device\", type=str, default=\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", - " args = parser.parse_args([])\n", "\n", - " # download the pre-trained benchmark\n", - " # includes some trained model and ground truth\n", - " model_details, groundtruth = load_benchmark(\n", - " model=\"mlp\", dataset=\"mnist\", metric=\"lds\"\n", - " )\n", + "# initialize argument parser to handle command-line arguments\n", + "parser = argparse.ArgumentParser()\n", + "# dynamically set device to 'cuda' if available, otherwise 'cpu'\n", + "parser.add_argument(\"--device\", type=str, default=\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "args = parser.parse_args([])\n", "\n", - " # define a functional loss function 'f' that calculates CrossEntropyLoss\n", - " # takes model parameters and a data-target pair (image, label) as input\n", - " def f(params, data_target_pair):\n", - " image, label = data_target_pair\n", - " loss = nn.CrossEntropyLoss()\n", - " # apply the model with given parameters to the image.\n", - " yhat = torch.func.functional_call(model_details[\"model\"], params, image)\n", - " return loss(yhat, label.long())\n", + "# download the pre-trained benchmark\n", + "# includes some trained model and ground truth\n", + "model_details, groundtruth = load_benchmark(\n", + " model=\"mlp\", dataset=\"mnist\", metric=\"lds\"\n", + ")\n", "\n", - " # initialize the AttributionTask with the model, loss function, and model checkpoints\n", - " task = AttributionTask(\n", - " model=model_details[\"model\"].to(args.device),\n", - " loss_func=f,\n", - " checkpoints=model_details[\"models_full\"][0], # use one full model checkpoint\n", - " )\n", + "# define a functional loss function 'f' that calculates CrossEntropyLoss\n", + "# takes model parameters and a data-target pair (image, label) as input\n", + "def f(params, data_target_pair):\n", + " image, label = data_target_pair\n", + " loss = nn.CrossEntropyLoss()\n", + " # apply the model with given parameters to the image.\n", + " yhat = torch.func.functional_call(model_details[\"model\"], params, image)\n", + " return loss(yhat, label.long())\n", + "\n", + "# initialize the AttributionTask with the model, loss function, and model checkpoints\n", + "task = AttributionTask(\n", + " model=model_details[\"model\"].to(args.device),\n", + " loss_func=f,\n", + " checkpoints=model_details[\"models_full\"][0], # use one full model checkpoint\n", + ")\n", "\n", - " # initialize the IFAttributorCG (Influence Function Attributor using Conjugate Gradient)\n", - " # requires the task, device, regularization parameter, and max iterations\n", - " attributor = IFAttributorCG(\n", - " task=task, device=args.device, regularization=5e-3, max_iter=10\n", + "# initialize the IFAttributorCG (Influence Function Attributor using Conjugate Gradient)\n", + "# requires the task, device, regularization parameter, and max iterations\n", + "attributor = IFAttributorCG(\n", + " task=task, device=args.device, regularization=5e-3, max_iter=10\n", + ")\n", + "# cache the training data using a DataLoader\n", + "# pre-processes/stores training data for attribution\n", + "attributor.cache(\n", + " DataLoader(\n", + " model_details[\"train_dataset\"],\n", + " batch_size=5000,\n", + " sampler=model_details[\"train_sampler\"],\n", " )\n", - " # cache the training data using a DataLoader\n", - " # pre-processes/stores training data for attribution\n", - " attributor.cache(\n", + ")\n", + "\n", + "# perform attribution without gradient calculation (inference mode)\n", + "with torch.no_grad():\n", + " # calculate influence scores of training data on test data\n", + " score = attributor.attribute(\n", " DataLoader(\n", " model_details[\"train_dataset\"],\n", " batch_size=5000,\n", " sampler=model_details[\"train_sampler\"],\n", - " )\n", + " ),\n", + " DataLoader(\n", + " model_details[\"test_dataset\"],\n", + " batch_size=5000,\n", + " sampler=model_details[\"test_sampler\"],\n", + " ),\n", " )\n", "\n", - " # perform attribution without gradient calculation (inference mode)\n", - " with torch.no_grad():\n", - " # calculate influence scores of training data on test data\n", - " score = attributor.attribute(\n", - " DataLoader(\n", - " model_details[\"train_dataset\"],\n", - " batch_size=5000,\n", - " sampler=model_details[\"train_sampler\"],\n", - " ),\n", - " DataLoader(\n", - " model_details[\"test_dataset\"],\n", - " batch_size=5000,\n", - " sampler=model_details[\"test_sampler\"],\n", - " ),\n", - " )\n", - "\n", - " # calculate the LDS (Leave-one-out Data Sensitivity) score\n", - " lds_score = lds(score, groundtruth)[0]\n", - " # print the mean of the non-null LDS scores\n", - " print(\"lds:\", torch.mean(lds_score[~torch.isnan(lds_score)]))" + "# calculate the LDS score\n", + "lds_score = lds(score, groundtruth)[0]\n", + "# print the mean of the non-null LDS scores\n", + "print(\"lds:\", torch.mean(lds_score[~torch.isnan(lds_score)]))" ], "metadata": { "colab": { diff --git a/examples/quickstart/influence_function_noisy_label.ipynb b/examples/quickstart/influence_function_noisy_label.ipynb index ebca61fc9..18b818b6b 100644 --- a/examples/quickstart/influence_function_noisy_label.ipynb +++ b/examples/quickstart/influence_function_noisy_label.ipynb @@ -145,7 +145,7 @@ { "cell_type": "markdown", "source": [ - "Influence Score: how much a single trainign data point affects the model's parameters or its predictions on other data points.\n", + "Influence Score: how much a single training data point affects the model's parameters or its predictions on other data points.\n", "\n", "\n", "* Higher influence indicates that a particular data point is problematic for the model.\n", @@ -155,7 +155,7 @@ "AUC Score: the probability that the influence function method ranks a randomly chosen positive example (a truly mislabled sample) higher than a randomly chosen negative example (a correctly labeled sample).\n", "\n", "\n", - "* Higher AUC valyes indicate better performance.\n", + "* Higher AUC values indicate better performance.\n", "* For mislabel detection, an AUC close to 1.0 means the influence scores are very effective at identifying the flipped labels among the correctly labeled ones.\n", "\n" ], @@ -218,95 +218,94 @@ } ], "source": [ - "if __name__ == \"__main__\":\n", - " # initialize argument parser for command-line arguments\n", - " parser = argparse.ArgumentParser()\n", - " # add argument for choosing the influence function method\n", - " parser.add_argument(\"--method\", type=str, default=\"explicit\")\n", - " # dynamically set device to 'cuda' if available, otherwise 'cpu'\n", - " parser.add_argument(\"--device\", type=str, default=\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", - " # parse arguments (passing an empty list to ignore Jupyter/IPython specific args)\n", - " args = parser.parse_args([])\n", + "# initialize argument parser for command-line arguments\n", + "parser = argparse.ArgumentParser()\n", + "# add argument for choosing the influence function method\n", + "parser.add_argument(\"--method\", type=str, default=\"explicit\")\n", + "# dynamically set device to 'cuda' if available, otherwise 'cpu'\n", + "parser.add_argument(\"--device\", type=str, default=\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "# parse arguments (passing an empty list to ignore Jupyter/IPython specific args)\n", + "args = parser.parse_args([])\n", "\n", - " # load the training dataset\n", - " dataset, _ = create_mnist_dataset(\"./data\")\n", + "# load the training dataset\n", + "dataset, _ = create_mnist_dataset(\"./data\")\n", "\n", - " # flip 10% of the labels in the first 1000 data points to simulate noisy data\n", - " # 'flip_index' stores the indices of the samples whose labels were flipped\n", - " dataset.targets[0:1000], flip_index = flip_label(dataset.targets[0:1000], p=0.1)\n", + "# flip 10% of the labels in the first 1000 data points to simulate noisy data\n", + "# 'flip_index' stores the indices of the samples whose labels were flipped\n", + "dataset.targets[0:1000], flip_index = flip_label(dataset.targets[0:1000], p=0.1)\n", "\n", - " # create a DataLoader for full model training with a batch size of 64\n", - " train_loader_full = torch.utils.data.DataLoader(\n", - " dataset,\n", - " batch_size=64,\n", - " sampler=SubsetSampler(range(1000)), # only use the first 1000 samples for training\n", - " )\n", + "# create a DataLoader for full model training with a batch size of 64\n", + "train_loader_full = torch.utils.data.DataLoader(\n", + " dataset,\n", + " batch_size=64,\n", + " sampler=SubsetSampler(range(1000)), # only use the first 1000 samples for training\n", + ")\n", "\n", - " # create another DataLoader specifically for attribution calculations\n", - " # uses a larger batch size (1000) to speed up the influence function computation\n", - " train_loader = torch.utils.data.DataLoader(\n", - " dataset,\n", - " batch_size=1000,\n", - " sampler=SubsetSampler(range(1000)), # only use the first 1000 samples\n", - " )\n", + "# create another DataLoader specifically for attribution calculations\n", + "# uses a larger batch size (1000) to speed up the influence function computation\n", + "train_loader = torch.utils.data.DataLoader(\n", + " dataset,\n", + " batch_size=1000,\n", + " sampler=SubsetSampler(range(1000)), # only use the first 1000 samples\n", + ")\n", "\n", - " # train a Logistic Regression model\n", - " model = train_mnist_lr(train_loader_full)\n", - " # move the model to the specified device (GPU or CPU)\n", - " model.to(args.device)\n", - " # set the model to evaluation mode (disables dropout, batchnorm updates, etc.)\n", - " model.eval()\n", + "# train a Logistic Regression model\n", + "model = train_mnist_lr(train_loader_full)\n", + "# move the model to the specified device (GPU or CPU)\n", + "model.to(args.device)\n", + "# set the model to evaluation mode (disables dropout, batchnorm updates, etc.)\n", + "model.eval()\n", "\n", - " # define the loss function to be used for attribution\n", - " # function takes model parameters and a data-target pair, calculates the cross-entropy loss, and returns it\n", - " def f(params, data_target_pair):\n", - " image, label = data_target_pair\n", - " loss = nn.CrossEntropyLoss()\n", - " # use functional_call to compute loss with specific parameters (for IF calculation)\n", - " yhat = torch.func.functional_call(model, params, image)\n", - " return loss(yhat, label.long())\n", + "# define the loss function to be used for attribution\n", + "# function takes model parameters and a data-target pair, calculates the cross-entropy loss, and returns it\n", + "def f(params, data_target_pair):\n", + " image, label = data_target_pair\n", + " loss = nn.CrossEntropyLoss()\n", + " # use functional_call to compute loss with specific parameters (for IF calculation)\n", + " yhat = torch.func.functional_call(model, params, image)\n", + " return loss(yhat, label.long())\n", "\n", - " # create an AttributionTask object, encapsulating the loss function, model, and initial checkpoints\n", - " task = AttributionTask(loss_func=f, model=model, checkpoints=model.state_dict())\n", + "# create an AttributionTask object, encapsulating the loss function, model, and initial checkpoints\n", + "task = AttributionTask(loss_func=f, model=model, checkpoints=model.state_dict())\n", "\n", - " # initialize the chosen attributor method (e.g., 'explicit', 'cg') from the ATTRIBUTOR_MAP\n", - " attributor = ATTRIBUTOR_MAP[args.method](\n", - " task=task,\n", - " device=args.device,\n", - " )\n", + "# initialize the chosen attributor method (e.g., 'explicit', 'cg') from the ATTRIBUTOR_MAP\n", + "attributor = ATTRIBUTOR_MAP[args.method](\n", + " task=task,\n", + " device=args.device,\n", + ")\n", "\n", - " # cache necessary components for the attributor (e.g., gradients of training samples)\n", - " attributor.cache(train_loader)\n", - " # calculate influence scores. torch.no_grad() is used as we don't need gradients for this step\n", - " # .diag() extracts the diagonal elements, representing self-influence of each training sample\n", - " with torch.no_grad():\n", - " score = attributor.attribute(train_loader, train_loader).diag()\n", + "# cache necessary components for the attributor (e.g., gradients of training samples)\n", + "attributor.cache(train_loader)\n", + "# calculate influence scores. torch.no_grad() is used as we don't need gradients for this step\n", + "# .diag() extracts the diagonal elements, representing self-influence of each training sample\n", + "with torch.no_grad():\n", + " score = attributor.attribute(train_loader, train_loader).diag()\n", "\n", - " # rank the influence scores from largest to lowest\n", - " _, indices = torch.sort(-score) # negative score for descending sort\n", - " cr = 0 # counter for found flipped samples\n", - " cr_list = [] # list to store (checked_samples_count, found_flipped_samples_count)\n", - " for idx, index in enumerate(indices):\n", - " if idx % 100 == 0: # record progress every 100 samples checked\n", - " cr_list.append((idx, cr))\n", - " # check if the current ranked sample's original index is among the known flipped indices\n", - " if int(index) in set(flip_index):\n", - " cr += 1 # increment counter if a flipped sample is found\n", + "# rank the influence scores from largest to lowest\n", + "_, indices = torch.sort(-score) # negative score for descending sort\n", + "cr = 0 # counter for found flipped samples\n", + "cr_list = [] # list to store (checked_samples_count, found_flipped_samples_count)\n", + "for idx, index in enumerate(indices):\n", + " if idx % 100 == 0: # record progress every 100 samples checked\n", + " cr_list.append((idx, cr))\n", + " # check if the current ranked sample's original index is among the known flipped indices\n", + " if int(index) in set(flip_index):\n", + " cr += 1 # increment counter if a flipped sample is found\n", "\n", - " # print the results of the mislabel detection ranking\n", - " print(cr_list)\n", - " print(f\"{'Checked Data Sample':<25}{'Found flipped Sample':25}\")\n", - " print(\"-\" * 50)\n", - " for row in cr_list:\n", - " print(f\"{row[0]:<25}{row[1]:<25}\")\n", - " print(\"-\" * 50)\n", + "# print the results of the mislabel detection ranking\n", + "print(cr_list)\n", + "print(f\"{'Checked Data Sample':<25}{'Found flipped Sample':25}\")\n", + "print(\"-\" * 50)\n", + "for row in cr_list:\n", + " print(f\"{row[0]:<25}{row[1]:<25}\")\n", + "print(\"-\" * 50)\n", "\n", - " # create a ground truth tensor: 1 for flipped samples, 0 for correctly labeled ones\n", - " ground_truth = torch.zeros(1000)\n", - " ground_truth[flip_index] = 1\n", - " # calculate the Area Under the Curve (AUC) for mislabel detection\n", - " # this metric evaluates how well the influence scores separate flipped from non-flipped samples.\n", - " print(\"AUC: \", float(mislabel_detection_auc(score, ground_truth)[0]))" + "# create a ground truth tensor: 1 for flipped samples, 0 for correctly labeled ones\n", + "ground_truth = torch.zeros(1000)\n", + "ground_truth[flip_index] = 1\n", + "# calculate the Area Under the Curve (AUC) for mislabel detection\n", + "# this metric evaluates how well the influence scores separate flipped from non-flipped samples.\n", + "print(\"AUC: \", float(mislabel_detection_auc(score, ground_truth)[0]))" ] }, { @@ -318,7 +317,7 @@ "Data Visualization:\n", "- Each image is one of the top samples identified by the influence function as potentially noisy.\n", "- 'Label' indicates the label associated with the image in the training dataset.\n", - "- 'Flipped' indicates whether the label of this specific sample was artificially flipped during the dataset creation process. If 'True' (in red), the influence function correctly identified a mislabeled sample." + "- 'Flipped' indicates whether the label of this specific sample was flipped during the dataset creation process. If 'True' (in red), the influence function correctly identified a mislabeled sample." ] }, { From df9bd86515bc3164df295ea70d938e0148ad99df Mon Sep 17 00:00:00 2001 From: carolinef35 Date: Tue, 6 Jan 2026 18:47:52 -0600 Subject: [PATCH 3/9] added badge and clarified installation instructions --- examples/quickstart/influence_function_lds.ipynb | 13 +++++++++++-- .../quickstart/influence_function_noisy_label.ipynb | 11 ++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/examples/quickstart/influence_function_lds.ipynb b/examples/quickstart/influence_function_lds.ipynb index 40badb98c..6f8d5cac5 100644 --- a/examples/quickstart/influence_function_lds.ipynb +++ b/examples/quickstart/influence_function_lds.ipynb @@ -19,7 +19,7 @@ { "cell_type": "markdown", "source": [ - "# This example shows an LDS score calculation." + "# This example shows a pre-trained Mnist10 + MLP benchmark setting and evaluates Influence Function (CG) algorithm by LDS." ], "metadata": { "id": "GrM_LjGcxl_v" @@ -28,7 +28,16 @@ { "cell_type": "markdown", "source": [ - "Install dependencies that are not in Colaboratory by default." + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://github.com/carolinef35/dattri/blob/colab_examples/examples/quickstart/influence_function_lds.ipynb)" + ], + "metadata": { + "id": "uJbPntZez7ut" + } + }, + { + "cell_type": "markdown", + "source": [ + "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can me found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." ], "metadata": { "id": "lAg59xgUpsGX" diff --git a/examples/quickstart/influence_function_noisy_label.ipynb b/examples/quickstart/influence_function_noisy_label.ipynb index 18b818b6b..9a607e62e 100644 --- a/examples/quickstart/influence_function_noisy_label.ipynb +++ b/examples/quickstart/influence_function_noisy_label.ipynb @@ -26,7 +26,16 @@ { "cell_type": "markdown", "source": [ - "Install dependencies that are not in Colaboratory by default." + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://github.com/carolinef35/dattri/blob/colab_examples/examples/quickstart/influence_function_noisy_label.ipynb)" + ], + "metadata": { + "id": "AcYrF4vZ1kbM" + } + }, + { + "cell_type": "markdown", + "source": [ + "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can me found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." ], "metadata": { "id": "o2mEZymgc0a4" From 58ce547ab83c7e2be33f72d32a6289696430081f Mon Sep 17 00:00:00 2001 From: carolinef35 Date: Tue, 6 Jan 2026 18:59:59 -0600 Subject: [PATCH 4/9] fixed badge link --- .../quickstart/influence_function_lds.ipynb | 166 +++++++++--------- .../influence_function_noisy_label.ipynb | 146 +++++++-------- 2 files changed, 156 insertions(+), 156 deletions(-) diff --git a/examples/quickstart/influence_function_lds.ipynb b/examples/quickstart/influence_function_lds.ipynb index 6f8d5cac5..26a346d83 100644 --- a/examples/quickstart/influence_function_lds.ipynb +++ b/examples/quickstart/influence_function_lds.ipynb @@ -1,53 +1,35 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [], - "gpuType": "T4" - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - }, - "accelerator": "GPU" - }, "cells": [ { "cell_type": "markdown", - "source": [ - "# This example shows a pre-trained Mnist10 + MLP benchmark setting and evaluates Influence Function (CG) algorithm by LDS." - ], "metadata": { "id": "GrM_LjGcxl_v" - } + }, + "source": [ + "# This example shows a pre-trained Mnist10 + MLP benchmark setting and evaluates Influence Function (CG) algorithm by LDS." + ] }, { "cell_type": "markdown", - "source": [ - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://github.com/carolinef35/dattri/blob/colab_examples/examples/quickstart/influence_function_lds.ipynb)" - ], "metadata": { "id": "uJbPntZez7ut" - } + }, + "source": [ + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/carolinef35/dattri/blob/colab_examples/examples/quickstart/influence_function_lds.ipynb)" + ] }, { "cell_type": "markdown", - "source": [ - "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can me found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." - ], "metadata": { "id": "lAg59xgUpsGX" - } + }, + "source": [ + "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can me found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." + ] }, { "cell_type": "code", - "source": [ - "!pip install dattri" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -55,11 +37,10 @@ "id": "Vh91mxvupuBQ", "outputId": "cca73eae-a777-41e7-9e68-91154c3e01bc" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Collecting dattri\n", " Downloading dattri-0.2.0-py3-none-any.whl.metadata (12 kB)\n", @@ -88,18 +69,21 @@ "Successfully installed dattri-0.2.0 mido-1.3.3 pretty-midi-0.2.11\n" ] } + ], + "source": [ + "!pip install dattri" ] }, { "cell_type": "markdown", + "metadata": { + "id": "WzfoDddNpxty" + }, "source": [ "Import libraries needed to run code.\n", "\n", "Note: If \"\"----- WARNING: CUDA devices not detected. This will cause the model to run very slow! -----\" message appears, change your runtime type to GPU by going to Runtime -> Change runtime type and selecting 'GPU'." - ], - "metadata": { - "id": "WzfoDddNpxty" - } + ] }, { "cell_type": "code", @@ -123,18 +107,58 @@ }, { "cell_type": "markdown", + "metadata": { + "id": "w7x4js5WvpTN" + }, "source": [ "LDS Score: used to evaluate the overall performance of a data attribution method.\n", "\n", "* A score near 1 means the attribution method accurately predicts the model's response to data changes\n", "\n" - ], - "metadata": { - "id": "w7x4js5WvpTN" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4TclgMyVp18L", + "outputId": "931d75ff-17de-4927-9942-5d3b64f0c5c7" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 9.91M/9.91M [00:00<00:00, 14.6MB/s]\n", + "100%|██████████| 28.9k/28.9k [00:00<00:00, 482kB/s]\n", + "100%|██████████| 1.65M/1.65M [00:00<00:00, 4.52MB/s]\n", + "100%|██████████| 4.54k/4.54k [00:00<00:00, 9.62MB/s]\n", + "calculating gradient of training set...: 0%| | 0/1 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", @@ -366,20 +365,21 @@ "\n", "plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # adjust layout to prevent title overlap\n", "plt.show()" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": [ - "
" - ], - "image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAH6CAYAAADocQXsAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAok1JREFUeJzs3Xd8VMX6x/FvgEBCb6F3ARGkqAiIIEGUqhTpqNQfInYUy0UUEBs2LCgiKqDoFUVRsWANelUURYErApYLCIhAQOk98/vjmJDdmUM2m03Z8Hm/XnnBeXbO2dnNs7Mns2fniTHGGAEAAAAAAAAAAEuB3O4AAAAAAAAAAAB5FZPoAAAAAAAAAAD4YBIdAAAAAAAAAAAfTKIDAAAAAAAAAOCDSXQAAAAAAAAAAHwwiQ4AAAAAAAAAgA8m0QEAAAAAAAAA8MEkOgAAAAAAAAAAPphEBwAAAAAAAADAB5PoAIA8KSYmJtM/iYmJud1t7d+/X++8846uueYaNW3aVCVKlFDhwoVVvXp1DRgwQF9++WWGx3jttdeUmJioMmXKqFixYmratKkeeOABHTlyJNP9mThxYtrzk5CQcMJjbNmyRYUKFUprP3fu3EzfX25bvHhxWv8j6aefflLPnj1VoUIFFSxYUDExMZo4caIkKTExUTExMVq8eHFE7zMvqVWrlmJiYrR+/fqIHC/c39GuXbt09913q2XLlipVqpRiY2NVsWJFNW7cWJdffrlmzJihffv2RaSPecn69esVExOjWrVq5XZXLOnHmNSfuLg4VahQQU2bNtXQoUP10ksv6eDBg7nd1XwpGsef1HyOiYlRfHy8Nm3a5Ns29T0pt8ee7JY6xs6ePTtb7ycnH39eHrcAAAhHodzuAAAALkOGDLFif/75pz744APf2xs0aJDt/crIyy+/rJEjR0qSatasqQ4dOqhQoUJasWKF5s2bp1dffVWTJ0/W7bff7tz/hhtu0GOPPaZChQrp/PPPV/HixfXpp5/q1ltv1cKFC/Xhhx8qPj4+rL4lJyfr7bffVu/evZ23z5kzR8eOHQvr2JGyfv161a5dWzVr1ozYpElW7du3T926ddP69evVvHlzderUSQULFlSzZs1yu2sRMXToUM2ZM0ezZs3S0KFDc7s7vtauXasLLrhAmzZtUpEiRdSyZUtVqVJFBw8e1OrVqzV37lzNnTtX5557rk4//fTc7u5Jp2LFiurcubMk6dixY9q1a5fWrFmjOXPmaM6cObrhhhv0xBNPaMCAARG7z8TERH322WdKSkrK1Q9R8+K4FS0OHjyoO++8U88//3xudwUAAOCEmEQHAORJrquxFi9enDaJnt1Xa4UrNjZWw4cP1zXXXKMzzjgjLW6M0dSpU3XTTTdp/PjxatOmjdq1axew75tvvqnHHntMxYsX12effaYzzzxTkjf5ff755+uLL77QHXfcoYceeijT/WrevLm+++47Pf/8876T6LNmzVKRIkV06qmnauXKlZm+j/zq22+/1fr169W6deuQvkmQH33yySc6cuSIqlatmmt9uOyyy7Rp0ya1b99e8+bNU0JCQsDtv//+u+bMmaPixYvnUg9Pbg0aNHCOy7/99psmTpyouXPnauDAgdq5c6euuuqqnO8g8pyYmBgVKVJEL7zwgm666SY1atQo2+9z9erV2X4fAAAgf2I5FwAAImjIkCF67rnnAibQJW+y4MYbb1SHDh0kSS+++KK177333itJuu2229Im0CWpfPnyeuqppyRJ06ZN065duzLdr6ZNm+rMM8/UBx98oD/++MO6/T//+Y9+/vln9ezZU2XKlMn08fOz33//XZJUr169XO5J7jnllFPUoEEDxcbG5sr9//bbb/ruu+8kSU8//bQ1gS5JNWrU0B133MHSAXnMKaecohdffFE333yzJOn666/X//73v1zuFfKCAgUK6Nprr9WxY8c0bty4HLnPBg0a5IlvrQEAgOjDJDoAIN/YtGmTrr32WtWrV09xcXEqVaqUzj33XM2YMcO5TMns2bMVExOjoUOHaseOHbr66qtVo0YNFSlSRDVr1tSYMWP0119/RbSPqZPrGzduDIhv3rxZ3377rSRp0KBB1n5t2rRR9erVdejQIb333nth3ffw4cN17NgxzZkzx7ot9av0w4cPz/A4r7zyijp06KCyZcumPVfDhw/Xzz//7Gy/ZcsWXX/99apfv77i4uJUtGhRVa9eXR06dAi4qn7o0KGqXbu2JGnDhg3WOstZlX5d76SkJHXs2FFlypRRfHy8zjzzTL3wwgsB7VPXV09dOmjOnDmZ6k9GaxWnriWdurZ6sGXLlunSSy9Ny8myZcuqU6dOvr//zD6+1PVqU/Nh2LBhAY8vfb/81kTfsGGDpkyZovPPPz+tn6VL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X1544QX16dNHjzzyiB555BGVLVtWLVu21IUXXqjLL79c5cuXz/C+w5GcnCzJG4fKlCkjKTI57yf1+bn88st1+eWXh3Ts1FyuXLly2BPUwcJ9jKk5cKL3tlq1amnFihWZ6k84r4mbb75ZX3zxhT7++GN17txZsbGxatq0qc477zwNGDBAZ599dqb6EKxgwYK699571bNnT02YMEGDBg1SXFycs21G7/mS+/3ITzjvU9ddd53+/e9/68knn9SVV14pSVq5cqW++OILVaxYUX369MnwfrMiq+Oa33OXGk8//qS+jp577jk999xzJ+xXKK/R7M4lAACyC5PoAACEwBiT6X2OHTumSy+9VG+88YaqV6+upKQk1axZ09k29SrwjRs3+h4v9bYTXTEeimHDhumuu+7SM888ow0bNqhUqVK65JJLsnRMPwUKFNDcuXM1btw4vfvuu/ryyy/15Zdfavr06Zo+fbouvvhiLViwQAULFsyW+/frU16Q/qrc4Fjx4sXVu3fvsI6bU49v//796tmzp7Zu3aphw4Zp9OjRqlu3rkqWLKmCBQvq559/1qmnnhrWayczatasqbvuuktlypTRjTfeqA8//FAHDhxQfHy8JKlPnz5atWqVLrroIt1yyy1q2LChSpYsqdjYWB0+fDjDSc0TPZ/Z+VyH8ryl5kubNm3SJg39NGrUKO3/bdu21fr16/Xuu+/qs88+01dffaUPPvhA77//viZMmKAFCxaoQ4cOWXsADt9//70kqUGDBmkfPkQi5/2kHrtz586qWLHiCdv6jc2R7Ed2PMZwhPOaKFq0qD766CN9++23WrRokb766it99dVX+u677/TII4/oqquuSrsKO1w9evRQ69at9dVXX+mJJ57QzTffnKXjhSqc96lWrVqpRYsWWrp0qT777DO1a9cu7fFfccUVKly4cLb2OavjWkbSjz+p+dusWTM1bdr0hPu1bNkyw2PnRC4BAJAdmEQHAES9qlWrSjp+tZTLunXrAtq6bnNJXRqiWrVqmerTsWPHdNlll+nVV19Nm0A/0VVzZ5xxhiRpx44dWrdunbPtd999J0k688wzM9WXYDVr1tT555+vTz75RJJ05ZVXpk04+kl93nbs2KHdu3c7r0ZPff5dz3HDhg3VsGFD3XzzzTLG6NNPP9WgQYO0cOFCvfDCC2lLweQnqZMoe/bscd6+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AqZAgQLmggsuMMOGDTMjRowwb731VobHTv39Fi5c2HTs2NEMGDDANGjQwMTExJjbb7897fUYzO935OeHH35I26dYsWKmTZs2pn///qZXr16mWbNmabc1a9bMbNu2LWDf66+/3kgyBQoUMO3atTMDBw40Z555ppFkxo8f79uXE/Vx3bp1vo/NmON52K5du4B4uGPOie5v8+bNac9BsWLFTOvWrc2AAQPMJZdcYpo1a2YKFixoJJkDBw4EPJexsbHm7LPPNv369TP9+vUzZ5xxhpFkYmJizLPPPuvzm7CljjEVK1ZMG98uv/xy0717d1O/fn0TExNjJJmEhAQzb9483+OEk/PvvPNOWv5ddNFFZvjw4WbEiBHmyy+/TGvz448/mlq1ahlJpnTp0ua8884zgwYNMj179jQNGzY0MTExpmLFilZ/7rnnnrS+N2jQwPTv3990797dNGzY0Pk6CmXcCucxrlq1yiQkJKSNo/369TMXXXSRKVq0qGnVqpU555xznOPPiYTzmmjatKmRZGrXrm0uvvhic+mll5qOHTua+Ph4I8mcf/755siRIyHdf2o+FyxY0LfN0KFD0/rhGnuSk5PT+lSqVCnTs2dP06dPn7Tnqnbt2s6x0PXYwnmfSrV161ZTpEgRI8k0adIkpMfvkjrG1qlTx7Rs2dL3Z9myZcaYrI9rN9xwg4mJiTHnnXeeGThwoGncuHHa7+S1116z9tu9e7fp0KFD2ustdezo27evOfvss03hwoWNJLN69eq0ffzGrUjmEgAAOYlJdABA1DjRBKkxxvz+++/m6quvNnXq1DGFCxc2JUqUMOecc46ZPn268w+y9JOj27ZtM6NGjTLVqlUzhQsXNtWrVzfXXXed2bFjR6b6mDqhlNFP8ORaevPmzTPnnXeeKVmypImPjzenn366uf/++60PATLTn0hMoqd6+eWXTWJioildurSJjY011atXN0OHDjVr1qyx2n7++efmhhtuMC1atDCVKlUyhQsXNpUqVTLnnHOOeeKJJ8zevXutfXbs2GFGjRplatSoYWJjYzM14ZqXJtGNMeavv/4y11xzjalWrZqJjY01VatWNVdccYXZunVr2u/GNYlujDH//e9/zRVXXGHq1atn4uLiTNGiRU2dOnVMp06dzOOPP242b94ckcdnjDELFiww5557rilRokTaxGH6fvkd+/Dh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qyDptm3bMtG7yAg3p/JqPr333ntWrFOnTiHtu3LlyoDtd955J6T9HnjggZDaBTt27JgVC3fsKVasmBU7evSoFTt06FBYxw8VYxQiLb+NUZHkKhialJQU0r6uIqKTJk3KsE20Y4w6sVatWlmxJUuWWLGUlBQr9s0331ixqVOnBmy/9tprWehd3sQYhUiK9jHK9Xe16++ne++9N6TYgQMHItOxkxhjFCIp2seo3FCpUiUrdsUVV1ix8ePHW7FChQplePzgeVbJPe7u2bMnw2PlhoxyiivRAQAAAAAAAADwwSQ6AAAAAAAAAAA+mEQHAAAAAAAAAMAHk+gAAAAAAAAAAPjIl4VFJ06caMVci+KH8phchRV3794dUj+qVasWsB0XFxfSfh9//LEV6927d8D23r17QzpWdqOQQ/apX7++FVu2bFnAdtGiRa02ruJajzzyiBV78MEHA7aTk5Mz28VsEc3FZoJf85L0+eefW7EaNWpkaz9cz0Uoz+v27dut2KuvvhpWHy677DIrtmHDBit2zjnnWLFIFhtljEKkRfMYld2y8npr3769FcuPhUSDMUadmKsQlatQoOvcp0AB+1qh4HZvvPGG1Sa4+Kgkff311yfsZ16S38ao0qVLW7EBAwZYsXr16lmx//u//wvYLlmypNXGlTuhCs6xTZs2WW3uueceK/bss89aMVfx9bwg2saoXr16BWzPnj3bauP6+8nF9btzzTMgc/LbGBUqV/H1CRMmBGx/9tlnVhty7sSibYzKbsHzDGeddZbV5sknn7RiFStWzLY+Se7Cpc8991y23me4KCwKAAAAAAAAAECYmEQHAAAAAAAAAMAHk+gAAAAAAAAAAPhgEh0AAAAAAAAAAB+FcrsDWRUfH2/FunbtasVcRQOCCym6Fth3FX75448/Qupb8CL+zz//vNXm9NNPt2IXXHCBFevUqVPA9uuvvx5SHxAdatWqZcU++eQTKxZKIRxXEdFbb701rH4hc1xFrVxFRPfv32/FXMWLevbsGbDtypOEhITQO5gB17GuvvrqiB3fVag5KwW9AOSucIs5TZo0yYqdDEVEkbF+/foFbLuKiLrO6V1FRENp5zp+nz59rNi5555rxaKp2Gi0GDlypBW76aabrFjdunXDOv7Bgwet2HfffRfSvlWqVLFiwedllStXttpMmzbNirnGzhkzZoTUD5zY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b+Q6hmxRtGjRgG1XQbf+/fuHdCxXXowfPz5g+4knnshE75DXuQqar169OmLHdxVHb968ecB2x44drTaugtfnnXdexPoFf6EUs8tMu7zA9fflXXfdZcVCeUz33HNPRPqUX7zwwgtWrFSpUhnu9/fff1ux4PczSdq2bZsV27lzZ8D20qVLrTau8/d169ZZsf9v796DrSrrPwAvFTQZcwpBQhnMQmzU8dIFlFLTRtOUgXHGy6R4gVKLpEwNwZwwEZxBTEml8MKQMSXewFQSdbwRimmZM5k6ajZIUxGaoiCi0B+/+dWs9X1Xe7HPOpy9z3me/97PvHudV9i8e+3XM+uT2u+K1x8zZkyYkyr+7N27d8iK6z/uuOPCnGXLloWMzZMquN1jjz1Clvo3XizYrlquPGfOnJClSkSLP3P58uVhTqqgfcqUKSFLfdcoWrRoUcM5dL1zzjknZKl7prPOOmtLLKft+E10AAAAAAAo4RAdAAAAAABKOEQHAAAAAIASDtEBAAAAAKBESxeLbrfddrnxAQccEOZceeWVIataNlMsfOjbt2+YkyoAfPXVV0O29957V/qZRffcc0+l69M5TjnllJDNmDGj6evNnz8/N77ggguavlYVkyZNClmzxaLTp0/v6HK6tREjRoSsWCicKuFMSRURt0KJaMrw4cND9stf/jJkxX33gw8+CHOmTp1a38K6gdRnTvFzbtasWWFO1bKi4mdolsXCn1T58YoVK0L27rvvhiz1ni3uSdtss02YkyrUXrBgQchoLan30+OPP54bp+6FUgW3VffKL33pS7mxYtF6pMped9lll5Cl/rxTpdHNSt3DdLYvf/nLufFFF10U5hx88MEhSxW2Fe/56vyzoftIFQXut99+DV+X+txduXJlLWvqLgYMGNBwzo033hiyCy+8MGSpQtJXXnmluYV1wOmnn54bpwoke/WKRzgbNmwIWXGvVyLaOYol6JujeP/79ttvhzn33ntvyA4//PBK17/pppty41Sh5EEHHRSyKiWiTz31VMhSZ3O0niFDhlSa99hjj3XyStqT30QHAAAAAIASDtEBAAAAAKCEQ3QAAAAAACjhEB0AAAAAAEq0dLFosTxt6dKlYc5WW20VslSJ1dy5c0M2ePDghmv42Mc+FrIqJSZV3XzzzbVdi8336KOPhuyf//xnyPr371/peoccckhu3K9f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\n" - }, - "metadata": {} - } ] } - ] -} \ No newline at end of file + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} From d7f37271573f9e4c6e3ee672093810d08fc9583c Mon Sep 17 00:00:00 2001 From: Caroline Feng Date: Tue, 3 Feb 2026 17:20:23 -0600 Subject: [PATCH 5/9] Update examples_test.yml Added test scripts to test the two quick start notebooks. --- .github/workflows/examples_test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/examples_test.yml b/.github/workflows/examples_test.yml index c1247addd..faec4ab2f 100644 --- a/.github/workflows/examples_test.yml +++ b/.github/workflows/examples_test.yml @@ -19,6 +19,7 @@ jobs: pip uninstall -y dattri python -m pip install --upgrade pip pip install -e .[test] + pip install jupyter nbconvert - name: Run examples run: | python examples/noisy_label_detection/influence_function_noisy_label.py --method cg --device cpu @@ -32,6 +33,10 @@ jobs: python examples/brittleness/mnist_lr_brittleness.py --method cg --device cpu python examples/data_cleaning/influence_function_data_cleaning.py --device cpu --train_size 1000 --val_size 100 --test_size 100 --remove_number 10 python examples/relatIF/influence_function_comparison.py --no_output + jupyter nbconvert --to script examples/quickstart/influence_function_lds.ipynb + python examples/quickstart/influence_function_lds.py + jupyter nbconvert --to script examples/quickstart/influence_function_noisy_label.ipynb + python examples/quickstart/influence_function_noisy_label.py - name: Uninstall the package run: | pip uninstall -y dattri From ae26e6b1be46828bb60366727def28dc69b18e89 Mon Sep 17 00:00:00 2001 From: carolinef35 Date: Wed, 11 Feb 2026 21:53:37 -0600 Subject: [PATCH 6/9] moved colab files, fixed title, and replaced pip install command --- .github/workflows/examples_test.yml | 8 ++++---- .../influence_function_lds.ipynb | 4 ++-- .../influence_function_noisy_label.ipynb | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename {examples/quickstart => quickstart}/influence_function_lds.ipynb (98%) rename {examples/quickstart => quickstart}/influence_function_noisy_label.ipynb (99%) diff --git a/.github/workflows/examples_test.yml b/.github/workflows/examples_test.yml index faec4ab2f..3365a35a4 100644 --- a/.github/workflows/examples_test.yml +++ b/.github/workflows/examples_test.yml @@ -33,10 +33,10 @@ jobs: python examples/brittleness/mnist_lr_brittleness.py --method cg --device cpu python examples/data_cleaning/influence_function_data_cleaning.py --device cpu --train_size 1000 --val_size 100 --test_size 100 --remove_number 10 python examples/relatIF/influence_function_comparison.py --no_output - jupyter nbconvert --to script examples/quickstart/influence_function_lds.ipynb - python examples/quickstart/influence_function_lds.py - jupyter nbconvert --to script examples/quickstart/influence_function_noisy_label.ipynb - python examples/quickstart/influence_function_noisy_label.py + jupyter nbconvert --to script quickstart/influence_function_lds.ipynb + python quickstart/influence_function_lds.py + jupyter nbconvert --to script quickstart/influence_function_noisy_label.ipynb + python quickstart/influence_function_noisy_label.py - name: Uninstall the package run: | pip uninstall -y dattri diff --git a/examples/quickstart/influence_function_lds.ipynb b/quickstart/influence_function_lds.ipynb similarity index 98% rename from examples/quickstart/influence_function_lds.ipynb rename to quickstart/influence_function_lds.ipynb index 26a346d83..9f095915a 100644 --- a/examples/quickstart/influence_function_lds.ipynb +++ b/quickstart/influence_function_lds.ipynb @@ -6,7 +6,7 @@ "id": "GrM_LjGcxl_v" }, "source": [ - "# This example shows a pre-trained Mnist10 + MLP benchmark setting and evaluates Influence Function (CG) algorithm by LDS." + "# Evaluate Influence Function (CG) on Mnist10 + MLP through LDS. " ] }, { @@ -71,7 +71,7 @@ } ], "source": [ - "!pip install dattri" + "!pip install git+[https://github.com/TRAIS-Lab/dattri.git@main](https://github.com/TRAIS-Lab/dattri.git@main)" ] }, { diff --git a/examples/quickstart/influence_function_noisy_label.ipynb b/quickstart/influence_function_noisy_label.ipynb similarity index 99% rename from examples/quickstart/influence_function_noisy_label.ipynb rename to quickstart/influence_function_noisy_label.ipynb index d81ca623d..37a7a3737 100644 --- a/examples/quickstart/influence_function_noisy_label.ipynb +++ b/quickstart/influence_function_noisy_label.ipynb @@ -6,7 +6,7 @@ "id": "GoM6J73LcrEK" }, "source": [ - "# This example shows how to use the IF to detect noisy labels in the MNIST." + "# Detect Noisy Labels in the MNIST using Influence Functions." ] }, { @@ -72,7 +72,7 @@ } ], "source": [ - "!pip install dattri" + "!pip install git+[https://github.com/TRAIS-Lab/dattri.git@main](https://github.com/TRAIS-Lab/dattri.git@main)" ] }, { From 142da2bc3ea6fef7b510a0efd2a9a5f3763eb523 Mon Sep 17 00:00:00 2001 From: Caroline Feng Date: Wed, 18 Feb 2026 21:54:45 -0600 Subject: [PATCH 7/9] Update examples_test.yml Corrected converting issue. --- .github/workflows/examples_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/examples_test.yml b/.github/workflows/examples_test.yml index 4dc393e41..f59758688 100644 --- a/.github/workflows/examples_test.yml +++ b/.github/workflows/examples_test.yml @@ -53,9 +53,9 @@ jobs: python examples/pretrained_benchmark/logra_wikitext2_gpt2_lds.py sed -i 's/range(1000)/range(100)/g' examples/customized_retraining/mnist.py python examples/customized_retraining/mnist.py --device cpu --path ./tmp/mnist_ckpt - jupyter nbconvert --to script quickstart/influence_function_lds.ipynb + jupyter nbconvert --to python quickstart/influence_function_lds.ipynb python quickstart/influence_function_lds.py - jupyter nbconvert --to script quickstart/influence_function_noisy_label.ipynb + jupyter nbconvert --to python quickstart/influence_function_noisy_label.ipynb python quickstart/influence_function_noisy_label.py - name: Uninstall the package run: | From 5b50b0a8a67e44e76f80e8b86067ab86dff57ece Mon Sep 17 00:00:00 2001 From: carolinef35 Date: Tue, 3 Mar 2026 14:28:08 -0600 Subject: [PATCH 8/9] Fixed Lint with Ruff and removed pip install line in converted scripts --- .github/workflows/examples_test.yml | 2 + quickstart/influence_function_lds.ipynb | 490 +++++------ .../influence_function_noisy_label.ipynb | 774 +++++++++--------- 3 files changed, 662 insertions(+), 604 deletions(-) diff --git a/.github/workflows/examples_test.yml b/.github/workflows/examples_test.yml index 4dc393e41..e5d3adf17 100644 --- a/.github/workflows/examples_test.yml +++ b/.github/workflows/examples_test.yml @@ -55,8 +55,10 @@ jobs: python examples/customized_retraining/mnist.py --device cpu --path ./tmp/mnist_ckpt jupyter nbconvert --to script quickstart/influence_function_lds.ipynb python quickstart/influence_function_lds.py + sed -i '/get_ipython().system.*pip install/d' quickstart/influence_function_lds.py jupyter nbconvert --to script quickstart/influence_function_noisy_label.ipynb python quickstart/influence_function_noisy_label.py + sed -i '/get_ipython().system.*pip install/d' quickstart/influence_function_noisy_label.py - name: Uninstall the package run: | pip uninstall -y dattri diff --git a/quickstart/influence_function_lds.ipynb b/quickstart/influence_function_lds.ipynb index 9f095915a..a159d20d5 100644 --- a/quickstart/influence_function_lds.ipynb +++ b/quickstart/influence_function_lds.ipynb @@ -1,246 +1,268 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "GrM_LjGcxl_v" - }, - "source": [ - "# Evaluate Influence Function (CG) on Mnist10 + MLP through LDS. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "uJbPntZez7ut" - }, - "source": [ - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/carolinef35/dattri/blob/colab_examples/examples/quickstart/influence_function_lds.ipynb)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lAg59xgUpsGX" - }, - "source": [ - "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can me found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "Vh91mxvupuBQ", - "outputId": "cca73eae-a777-41e7-9e68-91154c3e01bc" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting dattri\n", - " Downloading dattri-0.2.0-py3-none-any.whl.metadata (12 kB)\n", - "Requirement already satisfied: numpy>=1.25 in /usr/local/lib/python3.12/dist-packages (from dattri) (2.0.2)\n", - "Requirement already satisfied: scipy>=1.11 in /usr/local/lib/python3.12/dist-packages (from dattri) (1.16.3)\n", - "Requirement already satisfied: pyyaml in /usr/local/lib/python3.12/dist-packages (from dattri) (6.0.3)\n", - "Collecting pretty-midi (from dattri)\n", - " Downloading pretty_midi-0.2.11.tar.gz (5.6 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.6/5.6 MB\u001b[0m \u001b[31m77.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting mido>=1.1.16 (from pretty-midi->dattri)\n", - " Downloading mido-1.3.3-py3-none-any.whl.metadata (6.4 kB)\n", - "Requirement already satisfied: six in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (1.17.0)\n", - "Requirement already satisfied: importlib_resources in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (6.5.2)\n", - "Requirement already satisfied: packaging in /usr/local/lib/python3.12/dist-packages (from mido>=1.1.16->pretty-midi->dattri) (25.0)\n", - "Downloading dattri-0.2.0-py3-none-any.whl (173 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m173.9/173.9 kB\u001b[0m \u001b[31m17.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading mido-1.3.3-py3-none-any.whl (54 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m54.6/54.6 kB\u001b[0m \u001b[31m5.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hBuilding wheels for collected packages: pretty-midi\n", - " Building wheel for pretty-midi (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for pretty-midi: filename=pretty_midi-0.2.11-py3-none-any.whl size=5595886 sha256=040d236e342933211d5e6aa0d420205d4acc00910f6c13a15c2ac289abe8733c\n", - " Stored in directory: /root/.cache/pip/wheels/f4/ad/93/a7042fe12668827574927ade9deec7f29aad2a1001b1501882\n", - "Successfully built pretty-midi\n", - "Installing collected packages: mido, pretty-midi, dattri\n", - "Successfully installed dattri-0.2.0 mido-1.3.3 pretty-midi-0.2.11\n" - ] - } - ], - "source": [ - "!pip install git+[https://github.com/TRAIS-Lab/dattri.git@main](https://github.com/TRAIS-Lab/dattri.git@main)" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "GrM_LjGcxl_v" + }, + "source": [ + "# Evaluate Influence Function (CG) on Mnist10 + MLP through LDS. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uJbPntZez7ut" + }, + "source": [ + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/carolinef35/dattri/blob/colab_examples/examples/quickstart/influence_function_lds.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lAg59xgUpsGX" + }, + "source": [ + "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can be found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "id": "Vh91mxvupuBQ", + "outputId": "cca73eae-a777-41e7-9e68-91154c3e01bc" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "WzfoDddNpxty" - }, - "source": [ - "Import libraries needed to run code.\n", - "\n", - "Note: If \"\"----- WARNING: CUDA devices not detected. This will cause the model to run very slow! -----\" message appears, change your runtime type to GPU by going to Runtime -> Change runtime type and selecting 'GPU'." - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting dattri\n", + " Downloading dattri-0.2.0-py3-none-any.whl.metadata (12 kB)\n", + "Requirement already satisfied: numpy>=1.25 in /usr/local/lib/python3.12/dist-packages (from dattri) (2.0.2)\n", + "Requirement already satisfied: scipy>=1.11 in /usr/local/lib/python3.12/dist-packages (from dattri) (1.16.3)\n", + "Requirement already satisfied: pyyaml in /usr/local/lib/python3.12/dist-packages (from dattri) (6.0.3)\n", + "Collecting pretty-midi (from dattri)\n", + " Downloading pretty_midi-0.2.11.tar.gz (5.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.6/5.6 MB\u001b[0m \u001b[31m77.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting mido>=1.1.16 (from pretty-midi->dattri)\n", + " Downloading mido-1.3.3-py3-none-any.whl.metadata (6.4 kB)\n", + "Requirement already satisfied: six in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (1.17.0)\n", + "Requirement already satisfied: importlib_resources in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (6.5.2)\n", + "Requirement already satisfied: packaging in /usr/local/lib/python3.12/dist-packages (from mido>=1.1.16->pretty-midi->dattri) (25.0)\n", + "Downloading dattri-0.2.0-py3-none-any.whl (173 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m173.9/173.9 kB\u001b[0m \u001b[31m17.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading mido-1.3.3-py3-none-any.whl (54 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m54.6/54.6 kB\u001b[0m \u001b[31m5.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hBuilding wheels for collected packages: pretty-midi\n", + " Building wheel for pretty-midi (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for pretty-midi: filename=pretty_midi-0.2.11-py3-none-any.whl size=5595886 sha256=040d236e342933211d5e6aa0d420205d4acc00910f6c13a15c2ac289abe8733c\n", + " Stored in directory: /root/.cache/pip/wheels/f4/ad/93/a7042fe12668827574927ade9deec7f29aad2a1001b1501882\n", + "Successfully built pretty-midi\n", + "Installing collected packages: mido, pretty-midi, dattri\n", + "Successfully installed dattri-0.2.0 mido-1.3.3 pretty-midi-0.2.11\n" + ] + } + ], + "source": [ + "!pip install git+[https://github.com/TRAIS-Lab/dattri.git@main](https://github.com/TRAIS-Lab/dattri.git@main)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WzfoDddNpxty" + }, + "source": [ + "Import libraries needed to run code.\n", + "\n", + "Note: If \"\"----- WARNING: CUDA devices not detected. This will cause the model to run very slow! -----\" message appears, change your runtime type to GPU by going to Runtime -> Change runtime type and selecting 'GPU'." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SU8NDpOPpg-Z" + }, + "outputs": [], + "source": [ + "import argparse\n", + "from typing import Iterable, Tuple\n", + "\n", + "import torch\n", + "from torch import nn\n", + "from torch.utils.data import DataLoader\n", + "\n", + "from dattri.algorithm.influence_function import IFAttributorCG\n", + "from dattri.benchmark.load import load_benchmark\n", + "from dattri.metric import lds\n", + "from dattri.task import AttributionTask" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "w7x4js5WvpTN" + }, + "source": [ + "Linear Datamodeling Score (LDS): a metric used to evaluate the performance of data attribution methods on the counterfactual estimation task of predicting model behavior given different subsets of the training set.\n", + "\n", + "* LDS close to 1 means the attribution method accurately predicts the model's response to data changes\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "id": "4TclgMyVp18L", + "outputId": "931d75ff-17de-4927-9942-5d3b64f0c5c7" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "SU8NDpOPpg-Z" - }, - "outputs": [], - "source": [ - "import argparse\n", - "\n", - "import torch\n", - "from torch import nn\n", - "from torch.utils.data import DataLoader\n", - "\n", - "from dattri.algorithm.influence_function import IFAttributorCG\n", - "from dattri.benchmark.load import load_benchmark\n", - "from dattri.metric import lds\n", - "from dattri.task import AttributionTask" - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 9.91M/9.91M [00:00<00:00, 14.6MB/s]\n", + "100%|██████████| 28.9k/28.9k [00:00<00:00, 482kB/s]\n", + "100%|██████████| 1.65M/1.65M [00:00<00:00, 4.52MB/s]\n", + "100%|██████████| 4.54k/4.54k [00:00<00:00, 9.62MB/s]\n", + "calculating gradient of training set...: 0%| | 0/1 [00:00 torch.Tensor:\n", + " \"\"\"Calculates cross-entropy loss for given parameters and data.\n", + "\n", + " Args:\n", + " params: Model parameters (iterable of tensors).\n", + " data_target_pair: Tuple containing (image_tensor, label_tensor).\n", + "\n", + " Returns:\n", + " The calculated cross-entropy loss tensor.\n", + " \"\"\"\n", + " image, label = data_target_pair\n", + " loss = nn.CrossEntropyLoss()\n", + " # apply the model with given parameters to the image.\n", + " yhat = torch.func.functional_call(model_details[\"model\"], params, image)\n", + " return loss(yhat, label.long())\n", + "\n", + "\n", + "# initialize the AttributionTask with the model, loss function, and model checkpoints\n", + "task = AttributionTask(\n", + " model=model_details[\"model\"].to(args.device),\n", + " loss_func=f,\n", + " checkpoints=model_details[\"models_full\"][0], # use one full model checkpoint\n", + ")\n", + "\n", + "# initialize the IFAttributorCG (Influence Function Attributor using Conjugate Gradient)\n", + "# requires the task, device, regularization parameter, and max iterations\n", + "attributor = IFAttributorCG(\n", + " task=task, device=args.device, regularization=5e-3, max_iter=10,\n", + ")\n", + "# cache the training data using a DataLoader\n", + "# pre-processes/stores training data for attribution\n", + "attributor.cache(\n", + " DataLoader(\n", + " model_details[\"train_dataset\"],\n", + " batch_size=5000,\n", + " sampler=model_details[\"train_sampler\"],\n", + " ),\n", + ")\n", + "\n", + "# perform attribution without gradient calculation (inference mode)\n", + "with torch.no_grad():\n", + " # calculate influence scores of training data on test data\n", + " score = attributor.attribute(\n", + " DataLoader(\n", + " model_details[\"train_dataset\"],\n", + " batch_size=5000,\n", + " sampler=model_details[\"train_sampler\"],\n", + " ),\n", + " DataLoader(\n", + " model_details[\"test_dataset\"],\n", + " batch_size=5000,\n", + " sampler=model_details[\"test_sampler\"],\n", + " ),\n", + " )\n", + "\n", + "# calculate the LDS score\n", + "lds_score = lds(score, groundtruth)[0]\n", + "# print the mean of the non-null LDS scores\n", + "print(\"lds:\", torch.mean(lds_score[~torch.isnan(lds_score)])) # noqa: T201" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/quickstart/influence_function_noisy_label.ipynb b/quickstart/influence_function_noisy_label.ipynb index 37a7a3737..e20ff76bf 100644 --- a/quickstart/influence_function_noisy_label.ipynb +++ b/quickstart/influence_function_noisy_label.ipynb @@ -1,385 +1,419 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "GoM6J73LcrEK" - }, - "source": [ - "# Detect Noisy Labels in the MNIST using Influence Functions." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "AcYrF4vZ1kbM" - }, - "source": [ - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/carolinef35/dattri/blob/colab_examples/examples/quickstart/influence_function_noisy_label.ipynb)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "o2mEZymgc0a4" - }, - "source": [ - "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can me found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "iIInNEHvbJ7e", - "outputId": "8955f365-31db-4a5d-952a-18897d75ddd7" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting dattri\n", - " Downloading dattri-0.2.0-py3-none-any.whl.metadata (12 kB)\n", - "Requirement already satisfied: numpy>=1.25 in /usr/local/lib/python3.12/dist-packages (from dattri) (2.0.2)\n", - "Requirement already satisfied: scipy>=1.11 in /usr/local/lib/python3.12/dist-packages (from dattri) (1.16.3)\n", - "Requirement already satisfied: pyyaml in /usr/local/lib/python3.12/dist-packages (from dattri) (6.0.3)\n", - "Collecting pretty-midi (from dattri)\n", - " Downloading pretty_midi-0.2.11.tar.gz (5.6 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.6/5.6 MB\u001b[0m \u001b[31m52.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting mido>=1.1.16 (from pretty-midi->dattri)\n", - " Downloading mido-1.3.3-py3-none-any.whl.metadata (6.4 kB)\n", - "Requirement already satisfied: six in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (1.17.0)\n", - "Requirement already satisfied: importlib_resources in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (6.5.2)\n", - "Requirement already satisfied: packaging in /usr/local/lib/python3.12/dist-packages (from mido>=1.1.16->pretty-midi->dattri) (25.0)\n", - "Downloading dattri-0.2.0-py3-none-any.whl (173 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m173.9/173.9 kB\u001b[0m \u001b[31m14.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading mido-1.3.3-py3-none-any.whl (54 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m54.6/54.6 kB\u001b[0m \u001b[31m3.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hBuilding wheels for collected packages: pretty-midi\n", - " Building wheel for pretty-midi (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for pretty-midi: filename=pretty_midi-0.2.11-py3-none-any.whl size=5595886 sha256=fd5739ba3229b2d5d8d6f2a149d3ef913fab1df96517502ff5a2b98bf9bdbee7\n", - " Stored in directory: /root/.cache/pip/wheels/f4/ad/93/a7042fe12668827574927ade9deec7f29aad2a1001b1501882\n", - "Successfully built pretty-midi\n", - "Installing collected packages: mido, pretty-midi, dattri\n", - "Successfully installed dattri-0.2.0 mido-1.3.3 pretty-midi-0.2.11\n" - ] - } - ], - "source": [ - "!pip install git+[https://github.com/TRAIS-Lab/dattri.git@main](https://github.com/TRAIS-Lab/dattri.git@main)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_3J9JbbIdJOI" - }, - "source": [ - "Import libraries needed to run code." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "PDYn9ys1dN1X" - }, - "outputs": [], - "source": [ - "import argparse\n", - "from functools import partial\n", - "\n", - "import torch\n", - "from torch import nn\n", - "\n", - "from dattri.algorithm.influence_function import (\n", - " IFAttributorArnoldi,\n", - " IFAttributorCG,\n", - " IFAttributorDataInf,\n", - " IFAttributorExplicit,\n", - " IFAttributorLiSSA,\n", - ")\n", - "from dattri.benchmark.datasets.mnist import create_mnist_dataset, train_mnist_lr\n", - "from dattri.benchmark.utils import SubsetSampler, flip_label\n", - "from dattri.metric import mislabel_detection_auc\n", - "from dattri.task import AttributionTask" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "yKAFh2xKeVxo" - }, - "source": [ - "Dictionary to manage and intialize different influence function algorithms with their specific configurations. Each key is a specific arritbution method and the corresponding value is a class constructor with some of its arguments already pre-filled." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "tW-PS8aPfSqD" - }, - "outputs": [], - "source": [ - "ATTRIBUTOR_MAP = {\n", - " \"explicit\": partial(IFAttributorExplicit, regularization=0.01),\n", - " \"cg\": partial(IFAttributorCG, regularization=0.01),\n", - " \"lissa\": partial(IFAttributorLiSSA, recursion_depth=100),\n", - " \"datainf\": partial(IFAttributorDataInf, regularization=0.01),\n", - " \"arnoldi\": partial(IFAttributorArnoldi, regularization=0.01, max_iter=10),\n", - "}" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "GoM6J73LcrEK" + }, + "source": [ + "# Detect Noisy Labels in the MNIST using Influence Functions." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AcYrF4vZ1kbM" + }, + "source": [ + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/carolinef35/dattri/blob/colab_examples/examples/quickstart/influence_function_noisy_label.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o2mEZymgc0a4" + }, + "source": [ + "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can be found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "collapsed": true, + "id": "iIInNEHvbJ7e", + "outputId": "8955f365-31db-4a5d-952a-18897d75ddd7" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "qLP_rEsrf8jU" - }, - "source": [ - "Influence Score: how much a single training data point affects the model's parameters or its predictions on other data points.\n", - "\n", - "\n", - "* Higher influence indicates that a particular data point is problematic for the model.\n", - "* Mislabeled samples will exert a stronger, often negative, influence on the model's traning process.\n", - "\n", - "\n", - "AUC Score: the probability that the influence function method ranks a randomly chosen positive example (a truly mislabled sample) higher than a randomly chosen negative example (a correctly labeled sample).\n", - "\n", - "\n", - "* Higher AUC values indicate better performance.\n", - "* For mislabel detection, an AUC close to 1.0 means the influence scores are very effective at identifying the flipped labels among the correctly labeled ones.\n", - "\n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting dattri\n", + " Downloading dattri-0.2.0-py3-none-any.whl.metadata (12 kB)\n", + "Requirement already satisfied: numpy>=1.25 in /usr/local/lib/python3.12/dist-packages (from dattri) (2.0.2)\n", + "Requirement already satisfied: scipy>=1.11 in /usr/local/lib/python3.12/dist-packages (from dattri) (1.16.3)\n", + "Requirement already satisfied: pyyaml in /usr/local/lib/python3.12/dist-packages (from dattri) (6.0.3)\n", + "Collecting pretty-midi (from dattri)\n", + " Downloading pretty_midi-0.2.11.tar.gz (5.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.6/5.6 MB\u001b[0m \u001b[31m52.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting mido>=1.1.16 (from pretty-midi->dattri)\n", + " Downloading mido-1.3.3-py3-none-any.whl.metadata (6.4 kB)\n", + "Requirement already satisfied: six in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (1.17.0)\n", + "Requirement already satisfied: importlib_resources in /usr/local/lib/python3.12/dist-packages (from pretty-midi->dattri) (6.5.2)\n", + "Requirement already satisfied: packaging in /usr/local/lib/python3.12/dist-packages (from mido>=1.1.16->pretty-midi->dattri) (25.0)\n", + "Downloading dattri-0.2.0-py3-none-any.whl (173 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m173.9/173.9 kB\u001b[0m \u001b[31m14.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading mido-1.3.3-py3-none-any.whl (54 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m54.6/54.6 kB\u001b[0m \u001b[31m3.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hBuilding wheels for collected packages: pretty-midi\n", + " Building wheel for pretty-midi (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for pretty-midi: filename=pretty_midi-0.2.11-py3-none-any.whl size=5595886 sha256=fd5739ba3229b2d5d8d6f2a149d3ef913fab1df96517502ff5a2b98bf9bdbee7\n", + " Stored in directory: /root/.cache/pip/wheels/f4/ad/93/a7042fe12668827574927ade9deec7f29aad2a1001b1501882\n", + "Successfully built pretty-midi\n", + "Installing collected packages: mido, pretty-midi, dattri\n", + "Successfully installed dattri-0.2.0 mido-1.3.3 pretty-midi-0.2.11\n" + ] + } + ], + "source": [ + "!pip install git+[https://github.com/TRAIS-Lab/dattri.git@main](https://github.com/TRAIS-Lab/dattri.git@main)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_3J9JbbIdJOI" + }, + "source": [ + "Import libraries needed to run code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PDYn9ys1dN1X" + }, + "outputs": [], + "source": [ + "import argparse\n", + "from functools import partial\n", + "from typing import Iterable, Tuple\n", + "\n", + "import torch\n", + "from torch import nn\n", + "\n", + "from dattri.algorithm.influence_function import (\n", + " IFAttributorArnoldi,\n", + " IFAttributorCG,\n", + " IFAttributorDataInf,\n", + " IFAttributorExplicit,\n", + " IFAttributorLiSSA,\n", + ")\n", + "from dattri.benchmark.datasets.mnist import create_mnist_dataset, train_mnist_lr\n", + "from dattri.benchmark.utils import SubsetSampler, flip_label\n", + "from dattri.metric import mislabel_detection_auc\n", + "from dattri.task import AttributionTask\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yKAFh2xKeVxo" + }, + "source": [ + "Dictionary to manage different influence function algorithms with their specific configurations. Each key is a specific attribution method and the corresponding value is a class constructor with default arguments." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tW-PS8aPfSqD" + }, + "outputs": [], + "source": [ + "ATTRIBUTOR_MAP = {\n", + " \"explicit\": partial(IFAttributorExplicit, regularization=0.01),\n", + " \"cg\": partial(IFAttributorCG, regularization=0.01),\n", + " \"lissa\": partial(IFAttributorLiSSA, recursion_depth=100),\n", + " \"datainf\": partial(IFAttributorDataInf, regularization=0.01),\n", + " \"arnoldi\": partial(IFAttributorArnoldi, regularization=0.01, max_iter=10),\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qLP_rEsrf8jU" + }, + "source": [ + "Influence Score: how much a single training data point affects the model's parameters or its predictions on other data points.\n", + "\n", + "\n", + "* Higher influence indicates that a particular data point is problematic for the model.\n", + "* Mislabeled samples will exert a stronger, often negative, influence on the model's traning process.\n", + "\n", + "\n", + "AUC Score: the probability that the influence function method ranks a randomly chosen positive example (a truly mislabled sample) higher than a randomly chosen negative example (a correctly labeled sample).\n", + "\n", + "\n", + "* Higher AUC values indicate better performance.\n", + "* For mislabel detection, an AUC close to 1.0 means the influence scores are very effective at identifying the flipped labels among the correctly labeled ones.\n", + "\n", + "Self Attribution: measures the influence of a training sample on the model's own prediction for that specific sample.\n", + "* It quantifies how much the model’s \"belief\" about a sample changes if that sample were removed from the training set.\n", + "* A high self-attribution score typically identifies outliers or mislabeled samples. This is because the model \"memorizes\" the sample that contradicts the patterns found in the rest of the data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "id": "6WwN6-LHbElK", + "outputId": "ea8e92d7-2715-4960-d9da-15baab9db36e" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "6WwN6-LHbElK", - "outputId": "ea8e92d7-2715-4960-d9da-15baab9db36e" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 9.91M/9.91M [00:00<00:00, 86.8MB/s]\n", - "100%|██████████| 28.9k/28.9k [00:00<00:00, 35.9MB/s]\n", - "100%|██████████| 1.65M/1.65M [00:00<00:00, 62.4MB/s]\n", - "100%|██████████| 4.54k/4.54k [00:00<00:00, 6.65MB/s]\n", - "calculating gradient of training set...: 0%| | 0/1 [00:00" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "# number of top influential samples\n", - "num_display = 20\n", - "\n", - "# get the indices of the top influential samples\n", - "top_influential_indices = indices[:num_display]\n", - "\n", - "plt.figure(figsize=(15, 6))\n", - "plt.suptitle(f'Top {num_display} Most Influential Samples Detected as Noisy Labels', fontsize=16)\n", - "\n", - "for i, original_idx in enumerate(top_influential_indices):\n", - " original_idx = int(original_idx)\n", - " image, label = dataset[original_idx]\n", - "\n", - " # check if this sample was actually a flipped label\n", - " is_flipped = original_idx in set(flip_index)\n", - "\n", - " plt.subplot(2, (num_display + 1) // 2, i + 1)\n", - " plt.imshow(image.squeeze().numpy(), cmap='gray')\n", - " plt.title(f'Label: {label}\\nFlipped: {is_flipped}', color='red' if is_flipped else 'black')\n", - " plt.axis('off')\n", - "\n", - "plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # adjust layout to prevent title overlap\n", - "plt.show()" - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "\r" + ] } - ], - "metadata": { + ], + "source": [ + "# initialize argument parser for command-line arguments\n", + "parser = argparse.ArgumentParser()\n", + "# add argument for choosing the influence function method\n", + "parser.add_argument(\"--method\", type=str, default=\"explicit\")\n", + "# dynamically set device to 'cuda' if available, otherwise 'cpu'\n", + "parser.add_argument(\"--device\", type=str, default=\"cuda\" if\n", + " torch.cuda.is_available() else \"cpu\")\n", + "# parse arguments (passing an empty list to ignore Jupyter/IPython specific args)\n", + "args = parser.parse_args([])\n", + "\n", + "# load the training dataset\n", + "dataset, _ = create_mnist_dataset(\"./data\")\n", + "\n", + "# flip 10% of the labels in the first 1000 data points to simulate noisy data\n", + "# 'flip_index' stores the indices of the samples whose labels were flipped\n", + "dataset.targets[0:1000], flip_index = flip_label(dataset.targets[0:1000], p=0.1)\n", + "\n", + "# create a DataLoader for full model training with a batch size of 64\n", + "train_loader_full = torch.utils.data.DataLoader(\n", + " dataset,\n", + " batch_size=64,\n", + " sampler=SubsetSampler(range(1000)), # only use the first 1000 samples for training\n", + ")\n", + "\n", + "# create another DataLoader specifically for attribution calculations\n", + "# uses a larger batch size (1000) to speed up the influence function computation\n", + "train_loader = torch.utils.data.DataLoader(\n", + " dataset,\n", + " batch_size=1000,\n", + " sampler=SubsetSampler(range(1000)), # only use the first 1000 samples\n", + ")\n", + "\n", + "# train a Logistic Regression model\n", + "model = train_mnist_lr(train_loader_full)\n", + "# move the model to the specified device (GPU or CPU)\n", + "model.to(args.device)\n", + "# set the model to evaluation mode (disables dropout, batchnorm updates, etc.)\n", + "model.eval()\n", + "\n", + "\n", + "# define the loss function to be used for attribution\n", + "# function takes model parameters and a data-target pair, calculates the cross-entropy\n", + "# loss, and returns it\n", + "def f(params: Iterable[torch.Tensor],\n", + " data_target_pair: Tuple[torch.Tensor, torch.Tensor]) -> torch.Tensor:\n", + " \"\"\"Calculates cross-entropy loss for given parameters and data.\n", + "\n", + " Args:\n", + " params: Model parameters (iterable of tensors).\n", + " data_target_pair: Tuple containing (image_tensor, label_tensor).\n", + "\n", + " Returns:\n", + " The calculated cross-entropy loss tensor.\n", + " \"\"\"\n", + " image, label = data_target_pair\n", + " loss = nn.CrossEntropyLoss()\n", + " # use functional_call to compute loss with specific parameters (for IF calculation)\n", + " yhat = torch.func.functional_call(model, params, image)\n", + " return loss(yhat, label.long())\n", + "\n", + "\n", + "# create an AttributionTask object, encapsulating the loss function, model,\n", + "# and initial checkpoints\n", + "task = AttributionTask(loss_func=f, model=model, checkpoints=model.state_dict())\n", + "\n", + "# initialize the chosen attributor method (e.g., 'explicit', 'cg') from\n", + "# the ATTRIBUTOR_MAP\n", + "attributor = ATTRIBUTOR_MAP[args.method](\n", + " task=task,\n", + " device=args.device,\n", + ")\n", + "\n", + "# cache necessary components for the attributor (e.g., gradients of training samples)\n", + "attributor.cache(train_loader)\n", + "# calculate influence scores. torch.no_grad() is used as we don't need gradients\n", + "# for this step\n", + "# .diag() extracts the diagonal elements, representing self-influence of each\n", + "# training sample\n", + "with torch.no_grad():\n", + " score = attributor.attribute(train_loader, train_loader).diag()\n", + "\n", + "# rank the influence scores from largest to lowest\n", + "_, indices = torch.sort(-score) # negative score for descending sort\n", + "cr = 0 # counter for found flipped samples\n", + "cr_list = [] # list to store (checked_samples_count, found_flipped_samples_count)\n", + "for idx, index in enumerate(indices):\n", + " if idx % 100 == 0: # record progress every 100 samples checked\n", + " cr_list.append((idx, cr))\n", + " # check if the current ranked sample's original index is among the known\n", + " # flipped indices\n", + " if int(index) in set(flip_index):\n", + " cr += 1 # increment counter if a flipped sample is found\n", + "\n", + "# print the results of the mislabel detection ranking\n", + "print(cr_list) # noqa: T201\n", + "print(f\"{'Checked Data Sample':<25}{'Found flipped Sample':25}\") # noqa: T201\n", + "print(\"-\" * 50) # noqa: T201\n", + "for row in cr_list:\n", + " print(f\"{row[0]:<25}{row[1]:<25}\") # noqa: T201\n", + "print(\"-\" * 50) # noqa: T201\n", + "\n", + "# create a ground truth tensor: 1 for flipped samples, 0 for correctly labeled ones\n", + "ground_truth = torch.zeros(1000)\n", + "ground_truth[flip_index] = 1\n", + "# calculate the Area Under the Curve (AUC) for mislabel detection\n", + "# this metric evaluates how well the influence scores separate flipped\n", + "# from non-flipped samples.\n", + "print(\"AUC: \", float(mislabel_detection_auc(score, ground_truth)[0])) # noqa: T201" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "24d1eeaa" + }, + "source": [ + "Data Visualization:\n", + "- Each image is one of the top samples identified by the influence function as potentially noisy.\n", + "- 'Label' indicates the label associated with the image in the training dataset.\n", + "- 'Flipped' indicates whether the label of this specific sample was flipped during the dataset creation process. If 'True' (in red), the influence function correctly identified a mislabeled sample." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" + "base_uri": "https://localhost:8080/", + "height": 385 }, - "language_info": { - "name": "python" + "id": "3a8df971", + "outputId": "358be887-37c9-48e6-a19c-fb198085f6f6" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAH6CAYAAADocQXsAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAok1JREFUeJzs3Xd8VMX6x/FvgEBCb6F3ARGkqAiIIEGUqhTpqNQfInYUy0UUEBs2LCgiKqDoFUVRsWANelUURYErApYLCIhAQOk98/vjmJDdmUM2m03Z8Hm/XnnBeXbO2dnNs7Mns2fniTHGGAEAAAAAAAAAAEuB3O4AAAAAAAAAAAB5FZPoAAAAAAAAAAD4YBIdAAAAAAAAAAAfTKIDAAAAAAAAAOCDSXQAAAAAAAAAAHwwiQ4AAAAAAAAAgA8m0QEAAAAAAAAA8MEkOgAAAAAAAAAAPphEBwAAAAAAAADAB5PoAIA8KSYmJtM/iYmJud1t7d+/X++8846uueYaNW3aVCVKlFDhwoVVvXp1DRgwQF9++WWGx3jttdeUmJioMmXKqFixYmratKkeeOABHTlyJNP9mThxYtrzk5CQcMJjbNmyRYUKFUprP3fu3EzfX25bvHhxWv8j6aefflLPnj1VoUIFFSxYUDExMZo4caIkKTExUTExMVq8eHFE7zMvqVWrlmJiYrR+/fqIHC/c39GuXbt09913q2XLlipVqpRiY2NVsWJFNW7cWJdffrlmzJihffv2RaSPecn69esVExOjWrVq5XZXLOnHmNSfuLg4VahQQU2bNtXQoUP10ksv6eDBg7nd1XwpGsef1HyOiYlRfHy8Nm3a5Ns29T0pt8ee7JY6xs6ePTtb7ycnH39eHrcAAAhHodzuAAAALkOGDLFif/75pz744APf2xs0aJDt/crIyy+/rJEjR0qSatasqQ4dOqhQoUJasWKF5s2bp1dffVWTJ0/W7bff7tz/hhtu0GOPPaZChQrp/PPPV/HixfXpp5/q1ltv1cKFC/Xhhx8qPj4+rL4lJyfr7bffVu/evZ23z5kzR8eOHQvr2JGyfv161a5dWzVr1ozYpElW7du3T926ddP69evVvHlzderUSQULFlSzZs1yu2sRMXToUM2ZM0ezZs3S0KFDc7s7vtauXasLLrhAmzZtUpEiRdSyZUtVqVJFBw8e1OrVqzV37lzNnTtX5557rk4//fTc7u5Jp2LFiurcubMk6dixY9q1a5fWrFmjOXPmaM6cObrhhhv0xBNPaMCAARG7z8TERH322WdKSkrK1Q9R8+K4FS0OHjyoO++8U88//3xudwUAAOCEmEQHAORJrquxFi9enDaJnt1Xa4UrNjZWw4cP1zXXXKMzzjgjLW6M0dSpU3XTTTdp/PjxatOmjdq1axew75tvvqnHHntMxYsX12effaYzzzxTkjf5ff755+uLL77QHXfcoYceeijT/WrevLm+++47Pf/8876T6LNmzVKRIkV06qmnauXKlZm+j/zq22+/1fr169W6deuQvkmQH33yySc6cuSIqlatmmt9uOyyy7Rp0ya1b99e8+bNU0JCQsDtv//+u+bMmaPixYvnUg9Pbg0aNHCOy7/99psmTpyouXPnauDAgdq5c6euuuqqnO8g8pyYmBgVKVJEL7zwgm666SY1atQo2+9z9erV2X4fAAAgf2I5FwAAImjIkCF67rnnAibQJW+y4MYbb1SHDh0kSS+++KK177333itJuu2229Im0CWpfPnyeuqppyRJ06ZN065duzLdr6ZNm+rMM8/UBx98oD/++MO6/T//+Y9+/vln9ezZU2XKlMn08fOz33//XZJUr169XO5J7jnllFPUoEEDxcbG5sr9//bbb/ruu+8kSU8//bQ1gS5JNWrU0B133MHSAXnMKaecohdffFE333yzJOn666/X//73v1zuFfKCAgUK6Nprr9WxY8c0bty4HLnPBg0a5IlvrQEAgOjDJDoAIN/YtGmTrr32WtWrV09xcXEqVaqUzj33XM2YMcO5TMns2bMVExOjoUOHaseOHbr66qtVo0YNFSlSRDVr1tSYMWP0119/RbSPqZPrGzduDIhv3rxZ3377rSRp0KBB1n5t2rRR9erVdejQIb333nth3ffw4cN17NgxzZkzx7ot9av0w4cPz/A4r7zyijp06KCyZcumPVfDhw/Xzz//7Gy/ZcsWXX/99apfv77i4uJUtGhRVa9eXR06dAi4qn7o0KGqXbu2JGnDhg3WOstZlX5d76SkJHXs2FFlypRRfHy8zjzzTL3wwgsB7VPXV09dOmjOnDmZ6k9GaxWnriWdurZ6sGXLlunSSy9Ny8myZcuqU6dOvr//zD6+1PVqU/Nh2LBhAY8vfb/81kTfsGGDpkyZovPPPz+tn6VL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X1544QX16dNHjzzyiB555BGVLVtWLVu21IUXXqjLL79c5cuXz/C+w5GcnCzJG4fKlCkjKTI57yf1+bn88st1+eWXh3Ts1FyuXLly2BPUwcJ9jKk5cKL3tlq1amnFihWZ6k84r4mbb75ZX3zxhT7++GN17txZsbGxatq0qc477zwNGDBAZ599dqb6EKxgwYK699571bNnT02YMEGDBg1SXFycs21G7/mS+/3ITzjvU9ddd53+/e9/68knn9SVV14pSVq5cqW++OILVaxYUX369MnwfrMiq+Oa33OXGk8//qS+jp577jk999xzJ+xXKK/R7M4lAACyC5PoAACEwBiT6X2OHTumSy+9VG+88YaqV6+upKQk1axZ09k29SrwjRs3+h4v9bYTXTEeimHDhumuu+7SM888ow0bNqhUqVK65JJLsnRMPwUKFNDcuXM1btw4vfvuu/ryyy/15Zdfavr06Zo+fbouvvhiLViwQAULFsyW+/frU16Q/qrc4Fjx4sXVu3fvsI6bU49v//796tmzp7Zu3aphw4Zp9OjRqlu3rkqWLKmCBQvq559/1qmnnhrWayczatasqbvuuktlypTRjTfeqA8//FAHDhxQfHy8JKlPnz5atWqVLrroIt1yyy1q2LChSpYsqdjYWB0+fDjDSc0TPZ/Z+VyH8ryl5kubNm3SJg39NGrUKO3/bdu21fr16/Xuu+/qs88+01dffaUPPvhA77//viZMmKAFCxaoQ4cOWXsADt9//70kqUGDBmkfPkQi5/2kHrtz586qWLHiCdv6jc2R7Ed2PMZwhPOaKFq0qD766CN9++23WrRokb766it99dVX+u677/TII4/oqquuSrsKO1w9evRQ69at9dVXX+mJJ57QzTffnKXjhSqc96lWrVqpRYsWWrp0qT777DO1a9cu7fFfccUVKly4cLb2OavjWkbSjz+p+dusWTM1bdr0hPu1bNkyw2PnRC4BAJAdmEQHAES9qlWrSjp+tZTLunXrAtq6bnNJXRqiWrVqmerTsWPHdNlll+nVV19Nm0A/0VVzZ5xxhiRpx44dWrdunbPtd999J0k688wzM9WXYDVr1tT555+vTz75RJJ05ZVXpk04+kl93nbs2KHdu3c7r0ZPff5dz3HDhg3VsGFD3XzzzTLG6NNPP9WgQYO0cOFCvfDCC2lLweQnqZMoe/bscd6+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AqZAgQLmggsuMMOGDTMjRowwb731VobHTv39Fi5c2HTs2NEMGDDANGjQwMTExJjbb7897fUYzO935OeHH35I26dYsWKmTZs2pn///qZXr16mWbNmabc1a9bMbNu2LWDf66+/3kgyBQoUMO3atTMDBw40Z555ppFkxo8f79uXE/Vx3bp1vo/NmON52K5du4B4uGPOie5v8+bNac9BsWLFTOvWrc2AAQPMJZdcYpo1a2YKFixoJJkDBw4EPJexsbHm7LPPNv369TP9+vUzZ5xxhpFkYmJizLPPPuvzm7CljjEVK1ZMG98uv/xy0717d1O/fn0TExNjJJmEhAQzb9483+OEk/PvvPNOWv5ddNFFZvjw4WbEiBHmyy+/TGvz448/mlq1ahlJpnTp0ua8884zgwYNMj179jQNGzY0MTExpmLFilZ/7rnnnrS+N2jQwPTv3990797dNGzY0Pk6CmXcCucxrlq1yiQkJKSNo/369TMXXXSRKVq0qGnVqpU555xznOPPiYTzmmjatKmRZGrXrm0uvvhic+mll5qOHTua+Ph4I8mcf/755siRIyHdf2o+FyxY0LfN0KFD0/rhGnuSk5PT+lSqVCnTs2dP06dPn7Tnqnbt2s6x0PXYwnmfSrV161ZTpEgRI8k0adIkpMfvkjrG1qlTx7Rs2dL3Z9myZcaYrI9rN9xwg4mJiTHnnXeeGThwoGncuHHa7+S1116z9tu9e7fp0KFD2ustdezo27evOfvss03hwoWNJLN69eq0ffzGrUjmEgAAOYlJdABA1DjRBKkxxvz+++/m6quvNnXq1DGFCxc2JUqUMOecc46ZPn268w+y9JOj27ZtM6NGjTLVqlUzhQsXNtWrVzfXXXed2bFjR6b6mDqhlNFP8ORaevPmzTPnnXeeKVmypImPjzenn366uf/++60PATLTn0hMoqd6+eWXTWJioildurSJjY011atXN0OHDjVr1qyx2n7++efmhhtuMC1atDCVKlUyhQsXNpUqVTLnnHOOeeKJJ8zevXutfXbs2GFGjRplatSoYWJjYzM14ZqXJtGNMeavv/4y11xzjalWrZqJjY01VatWNVdccYXZunVr2u/GNYlujDH//e9/zRVXXGHq1atn4uLiTNGiRU2dOnVMp06dzOOPP242b94ckcdnjDELFiww5557rilRokTaxGH6fvkd+/Dh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qyDptm3bMtG7yAg3p/JqPr333ntWrFOnTiHtu3LlyoDtd955J6T9HnjggZDaBTt27JgVC3fsKVasmBU7evSoFTt06FBYxw8VYxQiLb+NUZHkKhialJQU0r6uIqKTJk3KsE20Y4w6sVatWlmxJUuWWLGUlBQr9s0331ixqVOnBmy/9tprWehd3sQYhUiK9jHK9Xe16++ne++9N6TYgQMHItOxkxhjFCIp2seo3FCpUiUrdsUVV1ix8ePHW7FChQplePzgeVbJPe7u2bMnw2PlhoxyiivRAQAAAAAAAADwwSQ6AAAAAAAAAAA+mEQHAAAAAAAAAMAHk+gAAAAAAAAAAPjIl4VFJ06caMVci+KH8phchRV3794dUj+qVasWsB0XFxfSfh9//LEV6927d8D23r17QzpWdqOQQ/apX7++FVu2bFnAdtGiRa02ruJajzzyiBV78MEHA7aTk5Mz28VsEc3FZoJf85L0+eefW7EaNWpkaz9cz0Uoz+v27dut2KuvvhpWHy677DIrtmHDBit2zjnnWLFIFhtljEKkRfMYld2y8npr3769FcuPhUSDMUadmKsQlatQoOvcp0AB+1qh4HZvvPGG1Sa4+Kgkff311yfsZ16S38ao0qVLW7EBAwZYsXr16lmx//u//wvYLlmypNXGlTuhCs6xTZs2WW3uueceK/bss89aMVfx9bwg2saoXr16BWzPnj3bauP6+8nF9btzzTMgc/LbGBUqV/H1CRMmBGx/9tlnVhty7sSibYzKbsHzDGeddZbV5sknn7RiFStWzLY+Se7Cpc8991y23me4KCwKAAAAAAAAAECYmEQHAAAAAAAAAMAHk+gAAAAAAAAAAPhgEh0AAAAAAAAAAB+FcrsDWRUfH2/FunbtasVcRQOCCym6Fth3FX75448/Qupb8CL+zz//vNXm9NNPt2IXXHCBFevUqVPA9uuvvx5SHxAdatWqZcU++eQTKxZKIRxXEdFbb701rH4hc1xFrVxFRPfv32/FXMWLevbsGbDtypOEhITQO5gB17GuvvrqiB3fVag5KwW9AOSucIs5TZo0yYqdDEVEkbF+/foFbLuKiLrO6V1FRENp5zp+nz59rNi5555rxaKp2Gi0GDlypBW76aabrFjdunXDOv7Bgwet2HfffRfSvlWqVLFiwedllStXttpMmzbNirnGzhkzZoTUD5zY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b+Q6hmxRtGjRgG1XQbf+/fuHdCxXXowfPz5g+4knnshE75DXuQqar169OmLHdxVHb968ecB2x44drTaugtfnnXdexPoFf6EUs8tMu7zA9fflXXfdZcVCeUz33HNPRPqUX7zwwgtWrFSpUhnu9/fff1ux4PczSdq2bZsV27lzZ8D20qVLrTau8/d169ZZsf9v796DrSrrPwAvFTQZcwpBQhnMQmzU8dIFlFLTRtOUgXHGy6R4gVKLpEwNwZwwEZxBTEml8MKQMSXewFQSdbwRimmZM5k6ajZIUxGaoiCi0B+/+dWs9X1Xe7HPOpy9z3me/97PvHudV9i8e+3XM+uT2u+K1x8zZkyYkyr+7N27d8iK6z/uuOPCnGXLloWMzZMquN1jjz1Clvo3XizYrlquPGfOnJClSkSLP3P58uVhTqqgfcqUKSFLfdcoWrRoUcM5dL1zzjknZKl7prPOOmtLLKft+E10AAAAAAAo4RAdAAAAAABKOEQHAAAAAIASDtEBAAAAAKBESxeLbrfddrnxAQccEOZceeWVIataNlMsfOjbt2+YkyoAfPXVV0O29957V/qZRffcc0+l69M5TjnllJDNmDGj6evNnz8/N77ggguavlYVkyZNClmzxaLTp0/v6HK6tREjRoSsWCicKuFMSRURt0KJaMrw4cND9stf/jJkxX33gw8+CHOmTp1a38K6gdRnTvFzbtasWWFO1bKi4mdolsXCn1T58YoVK0L27rvvhiz1ni3uSdtss02YkyrUXrBgQchoLan30+OPP54bp+6FUgW3VffKL33pS7mxYtF6pMped9lll5Cl/rxTpdHNSt3DdLYvf/nLufFFF10U5hx88MEhSxW2Fe/56vyzoftIFQXut99+DV+X+txduXJlLWvqLgYMGNBwzo033hiyCy+8MGSpQtJXXnmluYV1wOmnn54bpwoke/WKRzgbNmwIWXGvVyLaOYol6JujeP/79ttvhzn33ntvyA4//PBK17/pppty41Sh5EEHHRSyKiWiTz31VMhSZ3O0niFDhlSa99hjj3XyStqT30QHAAAAAIASDtEBAAAAAKCEQ3QAAAAAACjhEB0AAAAAAEq0dLFosTxt6dKlYc5WW20VslSJ1dy5c0M2ePDghmv42Mc+FrIqJSZV3XzzzbVdi8336KOPhuyf//xnyPr371/peoccckhu3K9f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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# number of top influential samples\n", + "num_display = 20\n", + "\n", + "# get the indices of the top influential samples\n", + "top_influential_indices = indices[:num_display]\n", + "\n", + "plt.figure(figsize=(15, 6))\n", + "plt.suptitle(f\"Top {num_display} Most Influential Samples Detected as Noisy Labels\",\n", + " fontsize=16)\n", + "\n", + "for i, raw_idx in enumerate(top_influential_indices):\n", + " original_idx = int(raw_idx)\n", + " image, label = dataset[original_idx]\n", + "\n", + " # check if this sample was actually a flipped label\n", + " is_flipped = original_idx in set(flip_index)\n", + "\n", + " plt.subplot(2, (num_display + 1) // 2, i + 1)\n", + " plt.imshow(image.squeeze().numpy(), cmap=\"gray\")\n", + " plt.title(f\"Label: {label}\\nFlipped: {is_flipped}\", color=\"red\"\n", + " if is_flipped else \"black\")\n", + " plt.axis(\"off\")\n", + "\n", + "plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # adjust layout to prevent title overlap\n", + "plt.show()" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } From 2af85fc92c42421cfec633bfe6c971da8895216e Mon Sep 17 00:00:00 2001 From: carolinef35 Date: Fri, 6 Mar 2026 16:14:00 -0600 Subject: [PATCH 9/9] Removed Ruff check --- pyproject.toml | 1 + quickstart/influence_function_lds.ipynb | 38 +++---------- .../influence_function_noisy_label.ipynb | 53 +++++-------------- 3 files changed, 20 insertions(+), 72 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 66fad2903..c355916e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ exclude = [ "dattri/benchmark/models", "examples/", "experiments/", + "quickstart/", ] [tool.ruff.lint.per-file-ignores] diff --git a/quickstart/influence_function_lds.ipynb b/quickstart/influence_function_lds.ipynb index a159d20d5..eba72dd98 100644 --- a/quickstart/influence_function_lds.ipynb +++ b/quickstart/influence_function_lds.ipynb @@ -27,15 +27,6 @@ "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can be found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations" - ] - }, { "cell_type": "code", "execution_count": null, @@ -102,9 +93,6 @@ }, "outputs": [], "source": [ - "import argparse\n", - "from typing import Iterable, Tuple\n", - "\n", "import torch\n", "from torch import nn\n", "from torch.utils.data import DataLoader\n", @@ -170,12 +158,8 @@ } ], "source": [ - "# initialize argument parser to handle command-line arguments\n", - "parser = argparse.ArgumentParser()\n", - "# dynamically set device to 'cuda' if available, otherwise 'cpu'\n", - "parser.add_argument(\"--device\", type=str, default=\"cuda\" if\n", - " torch.cuda.is_available() else \"cpu\")\n", - "args = parser.parse_args([])\n", + "# set device to 'cuda' if available, otherwise 'cpu'\n", + "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", "\n", "# download the pre-trained benchmark\n", "# includes some trained model and ground truth\n", @@ -186,17 +170,7 @@ "\n", "# define a functional loss function 'f' that calculates CrossEntropyLoss\n", "# takes model parameters and a data-target pair (image, label) as input\n", - "def f(params: Iterable[torch.Tensor],\n", - " data_target_pair: Tuple[torch.Tensor, torch.Tensor]) -> torch.Tensor:\n", - " \"\"\"Calculates cross-entropy loss for given parameters and data.\n", - "\n", - " Args:\n", - " params: Model parameters (iterable of tensors).\n", - " data_target_pair: Tuple containing (image_tensor, label_tensor).\n", - "\n", - " Returns:\n", - " The calculated cross-entropy loss tensor.\n", - " \"\"\"\n", + "def f(params, data_target_pair):\n", " image, label = data_target_pair\n", " loss = nn.CrossEntropyLoss()\n", " # apply the model with given parameters to the image.\n", @@ -206,7 +180,7 @@ "\n", "# initialize the AttributionTask with the model, loss function, and model checkpoints\n", "task = AttributionTask(\n", - " model=model_details[\"model\"].to(args.device),\n", + " model=model_details[\"model\"].to(device),\n", " loss_func=f,\n", " checkpoints=model_details[\"models_full\"][0], # use one full model checkpoint\n", ")\n", @@ -214,7 +188,7 @@ "# initialize the IFAttributorCG (Influence Function Attributor using Conjugate Gradient)\n", "# requires the task, device, regularization parameter, and max iterations\n", "attributor = IFAttributorCG(\n", - " task=task, device=args.device, regularization=5e-3, max_iter=10,\n", + " task=task, device=device, regularization=5e-3, max_iter=10,\n", ")\n", "# cache the training data using a DataLoader\n", "# pre-processes/stores training data for attribution\n", @@ -245,7 +219,7 @@ "# calculate the LDS score\n", "lds_score = lds(score, groundtruth)[0]\n", "# print the mean of the non-null LDS scores\n", - "print(\"lds:\", torch.mean(lds_score[~torch.isnan(lds_score)])) # noqa: T201" + "print(\"lds:\", torch.mean(lds_score[~torch.isnan(lds_score)]))" ] } ], diff --git a/quickstart/influence_function_noisy_label.ipynb b/quickstart/influence_function_noisy_label.ipynb index e20ff76bf..6e89b5e2f 100644 --- a/quickstart/influence_function_noisy_label.ipynb +++ b/quickstart/influence_function_noisy_label.ipynb @@ -27,15 +27,6 @@ "Note: The installation block in the notebook is specifically designed for Google Colab and the use cases in this notebook. Standard installation instructions can be found in the [README](https://github.com/TRAIS-Lab/dattri/blob/main/README.md#quick-start)." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations" - ] - }, { "cell_type": "code", "execution_count": null, @@ -101,9 +92,7 @@ }, "outputs": [], "source": [ - "import argparse\n", "from functools import partial\n", - "from typing import Iterable, Tuple\n", "\n", "import torch\n", "from torch import nn\n", @@ -226,15 +215,9 @@ } ], "source": [ - "# initialize argument parser for command-line arguments\n", - "parser = argparse.ArgumentParser()\n", - "# add argument for choosing the influence function method\n", - "parser.add_argument(\"--method\", type=str, default=\"explicit\")\n", - "# dynamically set device to 'cuda' if available, otherwise 'cpu'\n", - "parser.add_argument(\"--device\", type=str, default=\"cuda\" if\n", - " torch.cuda.is_available() else \"cpu\")\n", - "# parse arguments (passing an empty list to ignore Jupyter/IPython specific args)\n", - "args = parser.parse_args([])\n", + "# set device to 'cuda' if available, otherwise 'cpu'\n", + "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", + "method = \"explicit\"\n", "\n", "# load the training dataset\n", "dataset, _ = create_mnist_dataset(\"./data\")\n", @@ -261,7 +244,7 @@ "# train a Logistic Regression model\n", "model = train_mnist_lr(train_loader_full)\n", "# move the model to the specified device (GPU or CPU)\n", - "model.to(args.device)\n", + "model.to(device)\n", "# set the model to evaluation mode (disables dropout, batchnorm updates, etc.)\n", "model.eval()\n", "\n", @@ -269,17 +252,7 @@ "# define the loss function to be used for attribution\n", "# function takes model parameters and a data-target pair, calculates the cross-entropy\n", "# loss, and returns it\n", - "def f(params: Iterable[torch.Tensor],\n", - " data_target_pair: Tuple[torch.Tensor, torch.Tensor]) -> torch.Tensor:\n", - " \"\"\"Calculates cross-entropy loss for given parameters and data.\n", - "\n", - " Args:\n", - " params: Model parameters (iterable of tensors).\n", - " data_target_pair: Tuple containing (image_tensor, label_tensor).\n", - "\n", - " Returns:\n", - " The calculated cross-entropy loss tensor.\n", - " \"\"\"\n", + "def f(params, data_target_pair):\n", " image, label = data_target_pair\n", " loss = nn.CrossEntropyLoss()\n", " # use functional_call to compute loss with specific parameters (for IF calculation)\n", @@ -293,9 +266,9 @@ "\n", "# initialize the chosen attributor method (e.g., 'explicit', 'cg') from\n", "# the ATTRIBUTOR_MAP\n", - "attributor = ATTRIBUTOR_MAP[args.method](\n", + "attributor = ATTRIBUTOR_MAP[method](\n", " task=task,\n", - " device=args.device,\n", + " device=device,\n", ")\n", "\n", "# cache necessary components for the attributor (e.g., gradients of training samples)\n", @@ -320,12 +293,12 @@ " cr += 1 # increment counter if a flipped sample is found\n", "\n", "# print the results of the mislabel detection ranking\n", - "print(cr_list) # noqa: T201\n", - "print(f\"{'Checked Data Sample':<25}{'Found flipped Sample':25}\") # noqa: T201\n", - "print(\"-\" * 50) # noqa: T201\n", + "print(cr_list)\n", + "print(f\"{'Checked Data Sample':<25}{'Found flipped Sample':25}\")\n", + "print(\"-\" * 50) \n", "for row in cr_list:\n", - " print(f\"{row[0]:<25}{row[1]:<25}\") # noqa: T201\n", - "print(\"-\" * 50) # noqa: T201\n", + " print(f\"{row[0]:<25}{row[1]:<25}\")\n", + "print(\"-\" * 50)\n", "\n", "# create a ground truth tensor: 1 for flipped samples, 0 for correctly labeled ones\n", "ground_truth = torch.zeros(1000)\n", @@ -333,7 +306,7 @@ "# calculate the Area Under the Curve (AUC) for mislabel detection\n", "# this metric evaluates how well the influence scores separate flipped\n", "# from non-flipped samples.\n", - "print(\"AUC: \", float(mislabel_detection_auc(score, ground_truth)[0])) # noqa: T201" + "print(\"AUC: \", float(mislabel_detection_auc(score, ground_truth)[0]))" ] }, {