diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 34a0560..4137335 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,22 +1,19 @@ -name: Deploy Documentation +name: Deploy Documentation + Notebooks on: push: branches: - main - - docs-website pull_request: branches: - main workflow_dispatch: -# Sets permissions for GitHub Pages deployment permissions: contents: read pages: write id-token: write -# Allow one concurrent deployment concurrency: group: "pages" cancel-in-progress: true @@ -27,15 +24,15 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@v5 with: - python-version: '3.14' + python-version: '3.12' - name: Install uv - uses: astral-sh/setup-uv@v7 + uses: astral-sh/setup-uv@v3 - name: Install dependencies run: | @@ -46,14 +43,29 @@ jobs: sudo apt-get update sudo apt-get install -y pandoc - - name: Build documentation + - name: Build Sphinx documentation run: | uv run sphinx-build -b html docs/source build/html + - name: Setup Quarto + uses: quarto-dev/quarto-actions/setup@v2 + + - name: Render Quarto notebooks + run: | + cd notebooks + quarto render + + - name: Combine Sphinx + Notebooks + run: | + mkdir -p final_site + cp -r build/html/* final_site/ + mkdir -p final_site/notebooks + cp -r notebooks/_site/* final_site/notebooks/ + - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: - path: build/html + path: final_site deploy: needs: build diff --git a/.gitignore b/.gitignore index 5853749..e65366e 100644 --- a/.gitignore +++ b/.gitignore @@ -178,3 +178,8 @@ poetry.lock .vscode/ benchmark_results/ + +example_files/ +_site/ +.quarto/ +**/*.quarto_ipynb diff --git a/notebooks/_quarto.yml b/notebooks/_quarto.yml new file mode 100644 index 0000000..3e4b034 --- /dev/null +++ b/notebooks/_quarto.yml @@ -0,0 +1,5 @@ +project: + type: website + +website: + title: "Example notebooks" diff --git a/notebooks/example.ipynb b/notebooks/example.ipynb index 6712468..db36c32 100644 --- a/notebooks/example.ipynb +++ b/notebooks/example.ipynb @@ -25,12 +25,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "id": "1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + } + ], "source": [ "import torch\n", + "import numpy as np\n", "from sklearn.model_selection import train_test_split\n", "from sklearn.preprocessing import LabelEncoder\n", "\n", @@ -51,6 +61,7 @@ " map_attributions_to_word,\n", " plot_attributions_at_char,\n", " plot_attributions_at_word,\n", + " figshow\n", ")\n", "\n", "%load_ext autoreload\n", @@ -70,10 +81,191 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "3", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
apet_finalelibelleCJNATTYPSRFCRT
06202ACONSEIL EN SYSTEMES ET LOGICIELS INFORMATIQUESNaNNaNX0.0P
19529Zpetit bricolageNaNNaNX0.0P
24332AMenuiserie intérieure, Agencement571014M0.0P
36831ZAgent commercial en immobilierNaNNaNR0.0NaN
46820ALocation meublé d'un appartement dans le centr...NaNNaNL0.0P
........................
16933849609ZAutres services personnels n.c.a.NaNNaNNaN0.0NaN
16933859700ZActivités des ménages en tant qu'employeurs de...NaNNaNNaN0.0NaN
16933869810ZActivités indifférenciées des ménages en tant ...NaNNaNNaN0.0NaN
16933879820ZActivités indifférenciées des ménages en tant ...NaNNaNNaN0.0NaN
16933889900ZActivités des organisations et organismes extr...NaNNaNNaN0.0NaN
\n", + "

1693389 rows × 7 columns

\n", + "
" + ], + "text/plain": [ + " apet_finale libelle CJ \\\n", + "0 6202A CONSEIL EN SYSTEMES ET LOGICIELS INFORMATIQUES NaN \n", + "1 9529Z petit bricolage NaN \n", + "2 4332A Menuiserie intérieure, Agencement 5710 \n", + "3 6831Z Agent commercial en immobilier NaN \n", + "4 6820A Location meublé d'un appartement dans le centr... NaN \n", + "... ... ... ... \n", + "1693384 9609Z Autres services personnels n.c.a. NaN \n", + "1693385 9700Z Activités des ménages en tant qu'employeurs de... NaN \n", + "1693386 9810Z Activités indifférenciées des ménages en tant ... NaN \n", + "1693387 9820Z Activités indifférenciées des ménages en tant ... NaN \n", + "1693388 9900Z Activités des organisations et organismes extr... NaN \n", + "\n", + " NAT TYP SRF CRT \n", + "0 NaN X 0.0 P \n", + "1 NaN X 0.0 P \n", + "2 14 M 0.0 P \n", + "3 NaN R 0.0 NaN \n", + "4 NaN L 0.0 P \n", + "... ... ... ... ... \n", + "1693384 NaN NaN 0.0 NaN \n", + "1693385 NaN NaN 0.0 NaN \n", + "1693386 NaN NaN 0.0 NaN \n", + "1693387 NaN NaN 0.0 NaN \n", + "1693388 NaN NaN 0.0 NaN \n", + "\n", + "[1693389 rows x 7 columns]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import pandas as pd\n", "\n", @@ -93,17 +285,198 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
apet_finalelibelleCJNATTYPSRFCRT
06202ACONSEIL EN SYSTEMES ET LOGICIELS INFORMATIQUESNaNNaNX0.0P
19529Zpetit bricolageNaNNaNX0.0P
24332AMenuiserie intérieure, Agencement571014M0.0P
36831ZAgent commercial en immobilierNaNNaNR0.0NaN
46820ALocation meublé d'un appartement dans le centr...NaNNaNL0.0P
........................
16933849609ZAutres services personnels n.c.a.NaNNaNNaN0.0NaN
16933859700ZActivités des ménages en tant qu'employeurs de...NaNNaNNaN0.0NaN
16933869810ZActivités indifférenciées des ménages en tant ...NaNNaNNaN0.0NaN
16933879820ZActivités indifférenciées des ménages en tant ...NaNNaNNaN0.0NaN
16933889900ZActivités des organisations et organismes extr...NaNNaNNaN0.0NaN
\n", + "

1693389 rows × 7 columns

\n", + "
" + ], + "text/plain": [ + " apet_finale libelle CJ \\\n", + "0 6202A CONSEIL EN SYSTEMES ET LOGICIELS INFORMATIQUES NaN \n", + "1 9529Z petit bricolage NaN \n", + "2 4332A Menuiserie intérieure, Agencement 5710 \n", + "3 6831Z Agent commercial en immobilier NaN \n", + "4 6820A Location meublé d'un appartement dans le centr... NaN \n", + "... ... ... ... \n", + "1693384 9609Z Autres services personnels n.c.a. NaN \n", + "1693385 9700Z Activités des ménages en tant qu'employeurs de... NaN \n", + "1693386 9810Z Activités indifférenciées des ménages en tant ... NaN \n", + "1693387 9820Z Activités indifférenciées des ménages en tant ... NaN \n", + "1693388 9900Z Activités des organisations et organismes extr... NaN \n", + "\n", + " NAT TYP SRF CRT \n", + "0 NaN X 0.0 P \n", + "1 NaN X 0.0 P \n", + "2 14 M 0.0 P \n", + "3 NaN R 0.0 NaN \n", + "4 NaN L 0.0 P \n", + "... ... ... ... ... \n", + "1693384 NaN NaN 0.0 NaN \n", + "1693385 NaN NaN 0.0 NaN \n", + "1693386 NaN NaN 0.0 NaN \n", + "1693387 NaN NaN 0.0 NaN \n", + "1693388 NaN NaN 0.0 NaN \n", + "\n", + "[1693389 rows x 7 columns]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "df" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "6", "metadata": {}, "outputs": [], @@ -129,7 +502,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "8", "metadata": {}, "outputs": [], @@ -153,10 +526,191 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "10", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
libelleCJNATTYPSRFCRTapet_finale
0CONSEIL EN SYSTEMES ET LOGICIELS INFORMATIQUES135141201550
1petit bricolage135141201720
2Menuiserie intérieure, Agencement693701355
3Agent commercial en immobilier135141000580
4Location meublé d'un appartement dans le centr...13514601578
........................
1693384Autres services personnels n.c.a.13514800727
1693385Activités des ménages en tant qu'employeurs de...13514800728
1693386Activités indifférenciées des ménages en tant ...13514800729
1693387Activités indifférenciées des ménages en tant ...13514800730
1693388Activités des organisations et organismes extr...13514800731
\n", + "

1693389 rows × 7 columns

\n", + "
" + ], + "text/plain": [ + " libelle CJ NAT TYP \\\n", + "0 CONSEIL EN SYSTEMES ET LOGICIELS INFORMATIQUES 135 14 12 \n", + "1 petit bricolage 135 14 12 \n", + "2 Menuiserie intérieure, Agencement 69 3 7 \n", + "3 Agent commercial en immobilier 135 14 10 \n", + "4 Location meublé d'un appartement dans le centr... 135 14 6 \n", + "... ... ... ... ... \n", + "1693384 Autres services personnels n.c.a. 135 14 8 \n", + "1693385 Activités des ménages en tant qu'employeurs de... 135 14 8 \n", + "1693386 Activités indifférenciées des ménages en tant ... 135 14 8 \n", + "1693387 Activités indifférenciées des ménages en tant ... 135 14 8 \n", + "1693388 Activités des organisations et organismes extr... 135 14 8 \n", + "\n", + " SRF CRT apet_finale \n", + "0 0 1 550 \n", + "1 0 1 720 \n", + "2 0 1 355 \n", + "3 0 0 580 \n", + "4 0 1 578 \n", + "... ... ... ... \n", + "1693384 0 0 727 \n", + "1693385 0 0 728 \n", + "1693386 0 0 729 \n", + "1693387 0 0 730 \n", + "1693388 0 0 731 \n", + "\n", + "[1693389 rows x 7 columns]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "df, _ = clean_and_tokenize_df(df, text_feature=\"libelle\")\n", "X = df[[\"libelle\", \"CJ\", \"NAT\", \"TYP\", \"CRT\", \"SRF\"]].values\n", @@ -167,10 +721,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "11", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "((1693389, 6), (1693389,))" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "X.shape, y.shape" ] @@ -185,7 +750,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "13", "metadata": {}, "outputs": [], @@ -213,7 +778,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "16", "metadata": {}, "outputs": [], @@ -231,10 +796,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "18", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This tokenizer outputs tensors of size torch.Size([1, 12])\n", + "The tokens are here ['[CLS]', 'tr', '##ava', '##ux', 'd', \"'\", 'isolation', 'ex', '##ter', '##ieu', '##re', '[SEP]']\n", + "The total number of tokens is 30522\n" + ] + } + ], "source": [ "tokenizer = HuggingFaceTokenizer.load_from_pretrained(\"google-bert/bert-base-uncased\")\n", "print(\"This tokenizer outputs tensors of size \", tokenizer.tokenize(text[0]).input_ids.shape)\n", @@ -252,10 +827,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "20", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\n", + "This tokenizer outputs tensors of size torch.Size([1, 125])\n", + "The tokens are here ['[SEP]', 'travaux', 'd', \"'\", 'isolation', 'exterieure', '[CLS]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]']\n", + "The total number of tokens is 5000\n" + ] + } + ], "source": [ "tokenizer = WordPieceTokenizer(vocab_size=5000, output_dim=125)\n", "tokenizer.train(text)\n", @@ -282,10 +870,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "23", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(\"TRAVAUX D'ISOLATION EXTERIEURE \", [135, 3, 7, 1, 0], np.int64(352))" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "train_dataset = TextClassificationDataset(\n", " texts=X_train[:, 0].tolist(),\n", @@ -306,10 +905,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "25", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Input IDs shape: torch.Size([256, 125])\n" + ] + } + ], "source": [ "train_dataloader = train_dataset.create_dataloader(\n", " batch_size=256,\n", @@ -355,7 +962,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "29", "metadata": {}, "outputs": [], @@ -392,7 +999,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "31", "metadata": {}, "outputs": [], @@ -414,20 +1021,64 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "32", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "TextEmbedder(\n", + " (embedding_layer): Embedding(5000, 96, padding_idx=1)\n", + " (transformer): ModuleDict(\n", + " (h): ModuleList(\n", + " (0): Block(\n", + " (attn): SelfAttentionLayer(\n", + " (c_q): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_k): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_v): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_proj): Linear(in_features=96, out_features=96, bias=False)\n", + " )\n", + " (mlp): MLP(\n", + " (c_fc): Linear(in_features=96, out_features=384, bias=False)\n", + " (c_proj): Linear(in_features=384, out_features=96, bias=False)\n", + " )\n", + " )\n", + " )\n", + " )\n", + ")" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "text_embedder" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "33", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TextEmbedder input: tensor([[ 3, 326, 40, ..., 1, 1, 1],\n", + " [ 3, 1411, 1837, ..., 1, 1, 1],\n", + " [ 3, 199, 126, ..., 1, 1, 1],\n", + " ...,\n", + " [ 3, 1045, 1111, ..., 1, 1, 1],\n", + " [ 3, 387, 259, ..., 1, 1, 1],\n", + " [ 3, 386, 296, ..., 1, 1, 1]])\n", + "TextEmbedder output shape: torch.Size([256, 96])\n" + ] + } + ], "source": [ "# test the TextEmbedder: it takes as input a tensor of token ids and outputs a tensor of embeddings\n", "\n", @@ -457,10 +1108,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "id": "36", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "torch.Size([256, 55])\n", + "How will the categorical embedding be merged with the text one ? CategoricalForwardType.CONCATENATE_ALL\n", + "torch.Size([256, 96])\n", + "How will the categorical embedding be merged with the text one ? CategoricalForwardType.SUM_TO_TEXT\n", + "torch.Size([256, 25])\n", + "How will the categorical embedding be merged with the text one ? CategoricalForwardType.AVERAGE_AND_CONCAT\n" + ] + } + ], "source": [ "categorical_vocab_sizes = (X[:, 1:].max(axis=0) + 1).tolist()\n", "\n", @@ -510,7 +1174,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "id": "38", "metadata": {}, "outputs": [], @@ -531,10 +1195,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "id": "39", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "logits shape: torch.Size([256, 732])\n" + ] + } + ], "source": [ "x_combined = torch.cat((text_embedder_output, cat_var_net_output), dim=1)\n", "logits = classification_head(x_combined)\n", @@ -560,10 +1232,51 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "id": "42", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "TextClassificationModel(\n", + " (text_embedder): TextEmbedder(\n", + " (embedding_layer): Embedding(5000, 96, padding_idx=1)\n", + " (transformer): ModuleDict(\n", + " (h): ModuleList(\n", + " (0): Block(\n", + " (attn): SelfAttentionLayer(\n", + " (c_q): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_k): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_v): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_proj): Linear(in_features=96, out_features=96, bias=False)\n", + " )\n", + " (mlp): MLP(\n", + " (c_fc): Linear(in_features=96, out_features=384, bias=False)\n", + " (c_proj): Linear(in_features=384, out_features=96, bias=False)\n", + " )\n", + " )\n", + " )\n", + " )\n", + " )\n", + " (categorical_variable_net): CategoricalVariableNet(\n", + " (categorical_embedding_0): Embedding(136, 25)\n", + " (categorical_embedding_1): Embedding(15, 25)\n", + " (categorical_embedding_2): Embedding(15, 25)\n", + " (categorical_embedding_3): Embedding(3, 25)\n", + " (categorical_embedding_4): Embedding(5, 25)\n", + " )\n", + " (classification_head): ClassificationHead(\n", + " (net): Linear(in_features=121, out_features=732, bias=True)\n", + " )\n", + ")" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "model = TextClassificationModel(\n", " text_embedder=text_embedder,\n", @@ -575,10 +1288,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "id": "43", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "torch.Size([256, 732])" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Takes the same input as TextEmbedder + CategoricalVarNet -> same output as ClassificationHead (logits)\n", "model(input_ids=batch[\"input_ids\"], attention_mask=batch[\"attention_mask\"], categorical_vars=batch[\"categorical_vars\"]).shape" @@ -602,10 +1326,55 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "id": "46", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "TextClassificationModule(\n", + " (model): TextClassificationModel(\n", + " (text_embedder): TextEmbedder(\n", + " (embedding_layer): Embedding(5000, 96, padding_idx=1)\n", + " (transformer): ModuleDict(\n", + " (h): ModuleList(\n", + " (0): Block(\n", + " (attn): SelfAttentionLayer(\n", + " (c_q): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_k): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_v): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_proj): Linear(in_features=96, out_features=96, bias=False)\n", + " )\n", + " (mlp): MLP(\n", + " (c_fc): Linear(in_features=96, out_features=384, bias=False)\n", + " (c_proj): Linear(in_features=384, out_features=96, bias=False)\n", + " )\n", + " )\n", + " )\n", + " )\n", + " )\n", + " (categorical_variable_net): CategoricalVariableNet(\n", + " (categorical_embedding_0): Embedding(136, 25)\n", + " (categorical_embedding_1): Embedding(15, 25)\n", + " (categorical_embedding_2): Embedding(15, 25)\n", + " (categorical_embedding_3): Embedding(3, 25)\n", + " (categorical_embedding_4): Embedding(5, 25)\n", + " )\n", + " (classification_head): ClassificationHead(\n", + " (net): Linear(in_features=121, out_features=732, bias=True)\n", + " )\n", + " )\n", + " (loss): CrossEntropyLoss()\n", + " (accuracy_fn): MulticlassAccuracy()\n", + ")" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import torch\n", "\n", @@ -639,10 +1408,64 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 57, "id": "49", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "torchTextClassifiers(\n", + " tokenizer = WordPieceTokenizer \n", + " HuggingFace tokenizer: PreTrainedTokenizerFast(name_or_path='', vocab_size=5000, model_max_length=1000000000000000019884624838656, is_fast=True, padding_side='right', truncation_side='right', special_tokens={'pad_token': '[PAD]'}, clean_up_tokenization_spaces=False, added_tokens_decoder={\n", + "\t0: AddedToken(\"[UNK]\", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),\n", + "\t1: AddedToken(\"[PAD]\", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),\n", + "\t2: AddedToken(\"[CLS]\", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),\n", + "\t3: AddedToken(\"[SEP]\", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),\n", + "}\n", + "),\n", + " model = TextClassificationModel(\n", + " (text_embedder): TextEmbedder(\n", + " (embedding_layer): Embedding(5000, 96, padding_idx=1)\n", + " (transformer): ModuleDict(\n", + " (h): ModuleList(\n", + " (0): Block(\n", + " (attn): SelfAttentionLayer(\n", + " (c_q): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_k): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_v): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_proj): Linear(in_features=96, out_features=96, bias=False)\n", + " )\n", + " (mlp): MLP(\n", + " (c_fc): Linear(in_features=96, out_features=384, bias=False)\n", + " (c_proj): Linear(in_features=384, out_features=96, bias=False)\n", + " )\n", + " )\n", + " )\n", + " )\n", + " )\n", + " (categorical_variable_net): CategoricalVariableNet(\n", + " (categorical_embedding_0): Embedding(136, 25)\n", + " (categorical_embedding_1): Embedding(15, 25)\n", + " (categorical_embedding_2): Embedding(15, 25)\n", + " (categorical_embedding_3): Embedding(3, 25)\n", + " (categorical_embedding_4): Embedding(5, 25)\n", + " )\n", + " (classification_head): ClassificationHead(\n", + " (net): Linear(in_features=121, out_features=732, bias=True)\n", + " )\n", + "),\n", + " categorical_forward_type = AVERAGE_AND_CONCAT,\n", + " num_classes = 732,\n", + " embedding_dim = 96,\n", + ")" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "### Two main config objects, that mirror the parameters used above - and you're good to go !\n", "\n", @@ -657,7 +1480,7 @@ "training_config = TrainingConfig(\n", " lr=1e-3,\n", " batch_size=256,\n", - " num_epochs=10,\n", + " num_epochs=2,\n", ")\n", "\n", "ttc = torchTextClassifiers(\n", @@ -716,23 +1539,64 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 66, "id": "54", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "TextClassificationModel(\n", + " (text_embedder): TextEmbedder(\n", + " (embedding_layer): Embedding(5000, 96, padding_idx=1)\n", + " (transformer): ModuleDict(\n", + " (h): ModuleList(\n", + " (0): Block(\n", + " (attn): SelfAttentionLayer(\n", + " (c_q): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_k): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_v): Linear(in_features=96, out_features=96, bias=False)\n", + " (c_proj): Linear(in_features=96, out_features=96, bias=False)\n", + " )\n", + " (mlp): MLP(\n", + " (c_fc): Linear(in_features=96, out_features=384, bias=False)\n", + " (c_proj): Linear(in_features=384, out_features=96, bias=False)\n", + " )\n", + " )\n", + " )\n", + " )\n", + " )\n", + " (categorical_variable_net): CategoricalVariableNet(\n", + " (categorical_embedding_0): Embedding(136, 25)\n", + " (categorical_embedding_1): Embedding(15, 25)\n", + " (categorical_embedding_2): Embedding(15, 25)\n", + " (categorical_embedding_3): Embedding(3, 25)\n", + " (categorical_embedding_4): Embedding(5, 25)\n", + " )\n", + " (classification_head): ClassificationHead(\n", + " (net): Linear(in_features=121, out_features=732, bias=True)\n", + " )\n", + ")" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "ttc.pytorch_model.eval().cpu()" + "ttc.pytorch_model.eval().cuda()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 103, "id": "55", "metadata": {}, "outputs": [], "source": [ - "top_k = 5\n", - "yyy = ttc.predict(X_test[:10], top_k=top_k, explain=True)\n", + "top_k = 3\n", + "yyy = ttc.predict(X_test[:5], top_k=top_k, explain=True)\n", "\n", "text_idx = 0\n", "text = X_test[text_idx, 0]\n", @@ -744,31 +1608,53 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 108, "id": "56", "metadata": {}, "outputs": [], "source": [ - "word_attributions = map_attributions_to_word(attributions, word_ids)\n", + "words, word_attributions = map_attributions_to_word(attributions, text, word_ids, offsets)\n", "char_attributions = map_attributions_to_char(attributions, offsets, text)\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 76, "id": "57", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array(['8551Z', '9312Z', '8552Z'], dtype=object)" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "encoder.inverse_transform(np.array([predictions]).reshape(-1))" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 77, "id": "58", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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Vlbi7+8v3333nVbMzp07pXPnzuLq6ipmZmbi4OAgwcHBkpCQUKS9d955R7y9vcXW1lbMzMykYcOG8uGHH2puc/+kwlvy63oU7pPCY7K0R3R0dKnbSkRkDCqREn4enYiIiIiIiPTC71wREREREREZAYsrIiIiIiIiI2BxRUREREREZAQsroiIiIiIiIyAxRUREREREZERsLgiIiIiIiIyAv6IsA5qtRrXr1+Hra0tVCpVeadDRERERETlRERw79491KpVCyYmJZ+bYnGlw/Xr11G7du3yToOIiIiIiCqIK1euwM3NrcQYFlc62NraAnjcgXZ2duWcDRERERERlZfs7GzUrl1bUyOUhMWVDoWXAtrZ2bG4IiIiIiIivb4uxBtaEBERERERGQGLKyIiIiIiIiNgcUVERERERGQELK6IiIiIiIiMgMUVERERERGREfBugURE9Jdyn/BjqTEXZ3XVO76yxT4ZXxFiiYjIeHjmioiIiIiIyAhYXBERERERERkBiysiIiIiIiIjYHFFRERERERkBCyuiIiIiIiIjIDFFRERERERkRGwuCIiIiIiIjICFldERERERERGwOKKiIiIiIjICFhcERERERERGQGLKyIiIiIiIiNgcUVERERERGQELK6IiIiIiIiMgMUVERERERGREVSI4mrx4sVwd3eHpaUl/P39cejQoRLjN27cCE9PT1haWqJZs2bYtm1bsbEjRoyASqVCTEyMkbMmIiIiIiL6n3IvrjZs2IDIyEhER0cjOTkZLVq0QEhICDIyMnTG79+/H/3790dYWBiOHDmC0NBQhIaG4vjx40ViN2/ejN9++w21atUq680gIiIiIqK/uXIvrubNm4fw8HAMHToU3t7eiI2NhbW1NVasWKEzfv78+ejSpQvGjx8PLy8vzJgxA61atcKiRYu04q5du4b33nsPa9euhZmZ2V+xKURERERE9DemuLjKzc3FgwcPNM8vXbqEmJgY7Ny5U/HK8/PzkZSUhKCgoP8lZGKCoKAgHDhwQOcyBw4c0IoHgJCQEK14tVqNgQMHYvz48WjSpEmpeeTl5SE7O1vrQUREREREpITi4qp79+5YvXo1AODu3bvw9/fH3Llz0b17dyxdulRRW7du3UJBQQFcXFy0pru4uCAtLU3nMmlpaaXGz549G1WqVMGYMWP0ymPmzJmwt7fXPGrXrq1oO4iIiIiIiBQXV8nJyWjfvj0A4LvvvoOLiwsuXbqE1atXY8GCBUZPUKmkpCTMnz8fq1atgkql0muZiRMnIisrS/O4cuVKGWdJRERERETPG8XF1YMHD2BrawsA2LlzJ3r06AETExO89NJLuHTpkqK2HB0dYWpqivT0dK3p6enpcHV11bmMq6trifF79+5FRkYG6tSpgypVqqBKlSq4dOkS/vnPf8Ld3V1nmxYWFrCzs9N6EBERERERKaG4uGrYsCHi4uJw5coV7NixA8HBwQCAjIwMxUWJubk5WrdujYSEBM00tVqNhIQEBAQE6FwmICBAKx4A4uPjNfEDBw7EsWPHcPToUc2jVq1aGD9+PHbs2KEoPyIiIiIiIn1VUbpAVFQU3nzzTYwbNw6vvPKKpqjZuXMnWrZsqTiByMhIDB48GL6+vvDz80NMTAxycnIwdOhQAMCgQYPwwgsvYObMmQCAiIgIBAYGYu7cuejatSvWr1+PxMRELFu2DABQo0YN1KhRQ2sdZmZmcHV1RePGjRXnR0REREREpA/FxVWvXr3Qrl073LhxAy1atNBMf+WVV/DGG28oTqBv3764efMmoqKikJaWBh8fH2zfvl1z04rLly/DxOR/J9jatGmDdevWYfLkyZg0aRI8PDwQFxeHpk2bKl43ERERERGRsSguroDH33t6+jtRfn5+BicxevRojB49Wue8PXv2FJnWu3dv9O7dW+/2L168aGBmRERERERE+lFcXOXk5GDWrFlISEhARkYG1Gq11vw//vjDaMkRERERERFVFoqLq3feeQe//PILBg4ciJo1a+p9u3MiIiIiIqLnmeLi6qeffsKPP/6Itm3blkU+RERERERElZLiW7FXq1YN1atXL4tciIiIiIiIKi3FxdWMGTMQFRWFBw8elEU+RERERERElZLiywLnzp2L8+fPw8XFBe7u7jAzM9Oan5ycbLTkiIiIiIiIKgvFxVVoaGgZpEFERERERFS5KS6uoqOjyyIPIiIiIiKiSs2gHxEGgKSkJJw6dQoA0KRJE7Rs2dJoSREREREREVU2iourjIwM9OvXD3v27IGDgwMA4O7du+jYsSPWr18PJycnY+dIRERERERU4Sm+W+B7772He/fu4cSJE8jMzERmZiaOHz+O7OxsjBkzpixyJCIiIiIiqvAUn7navn07fv75Z3h5eWmmeXt7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hIRg6NChWLlyJY4dO4a5c+dq2nB3d8fBgwdx8eJF3Lp1y6AzaEREf3csroiISC/169dHcnIyOnbsiH/+859o2rQpOnfujISEBCxdulTvdoYPH44ePXqgb9++8Pf3x+3btzFy5MhSl3NycsKqVauwceNGeHt7Y9asWfjss8+0YmrUqIFdu3bh/v37CAwMROvWrfHll19qLr8LDw9H48aN4evrCycnJ+zbtw/W1tb49ddfUadOHfTo0QNeXl4ICwvDn3/+CTs7u2LzcXBwwKZNm9CpUyd4eXkhNjYW33zzDZo0aaJ3XxhLrVq1sG/fPhQUFCA4OBjNmjXD2LFj4eDgABMTE3zyySe4dOkSvvjiCwCPLxVctmwZJk+ejJSUFADA+++/D1NTU3h7e2suuSQiImVUovRbykRERERERFQEz1wREREREREZAYsrIiIiIiIiI2BxRUREREREZAQsroiIiIiIiIyAxRUREREREZERsLgiIiIiIiIyAhZXRERERERERsDiioiIiIiIyAhYXBERERERERkBiysiIiIiIiIjYHFFRERERERkBCyuiIiIiIiIjOD/ANgYBA3LAdtGAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "all_plots = plot_attributions_at_char(\n", " text=text,\n", @@ -780,13 +1666,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 110, "id": "59", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAExCAYAAABLS/K8AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAZh1JREFUeJzt3XlcTfn/B/DXLe0rUqGIbINJ1ISvpZgm+zJjaaxlX8eSZezCUI0tY9coZMkyJDsT2YcUY2yDRFkqBpWk5d7P7w+/znSntFBu8no+Hveh+zmfc+77ftx7z33fz3JkQggBIiIiIiIieic1VQdARERERERU0jFxIiIiIiIiygcTJyIiIiIionwwcSIiIiIiIsoHEyciIiIiIqJ8MHEiIiIiIiLKBxMnIiIiIiKifDBxIiIiIiIiygcTJyIiIiIionwwcSIi+sRs2LABMpkM9+/fl8qsrKzQsWPHj/L4YWFhkMlkCAsL+yiPV1ivXr3C4MGDYW5uDplMhnHjxqk6pA/m5OQEJycnVYdBRPRZY+JERPSBVq1aBZlMhsaNG+e6/caNG/D09FRKdLLvu2HDhuIN8D2V5NjysmDBAmzYsAEjRoxAYGAg+vXrp+qQSqTff/8drVq1gomJCYyNjeHg4IDAwMAc9WQyWa43b29vpXqenp651tPW1s5xzNWrV6NHjx6oUqUKZDIZ3N3dc9S5f//+Ox87++1TfI0S0aepjKoDICL61G3ZsgVWVla4ePEi7t69ixo1aihtv3HjBubMmQMnJydYWVkpbVu1ahVMTExy/eL4Lv369cP3338PLS2tIoj+3d4VW8uWLZGamgpNTc1iffz3dfz4cTRp0gSzZ89WdSglVkhICLp27YqmTZtKCc+OHTvQv39/PHv2DOPHj1eq/80336B///5KZQ0bNsz12KtXr4a+vr50X11dPUcdHx8fJCcnw8HBAU+ePMn1OBUqVMg1kQMAuVwODw8PvHr16p1xEBEVNSZOREQfIDo6GufOncPu3bsxbNgwbNmypdi+sKekpEBPTw/q6uq5fhn9WNTU1HLtRSgpEhISULdu3SI7XmZmJhQKRYlNFN/HihUrULFiRRw/flxKwIcNG4Y6depgw4YNORKnWrVqoW/fvgU6dvfu3WFiYpJnnZMnT0q9TdmTrOz09PTe+ZgzZszA8+fPsXjxYjRo0KBAcRERfSgO1SMi+gBbtmxB2bJl0aFDB3Tv3h1btmxR2r5hwwb06NEDANCqVStpeFFYWBisrKxw/fp1nDx5UirPmseSNY/p5MmTGDlyJExNTWFhYaG0Lbehf0ePHoWtrS20tbVRt25d7N69W2l7Vu/Cf/33mHnF9q45Tjt37oSdnR10dHRgYmKCvn374tGjR0p13N3doa+vj0ePHqFr167Q19dHhQoVMHHiRMjlcqW6QUFBsLOzg4GBAQwNDfHll19i2bJl7/y/yIorOjoaBw4ckOLOek4JCQkYNGgQzMzMoK2tjQYNGmDjxo1Kx8gaHrZo0SL4+vrC2toaWlpauHHjxjsfFwA2b94MBwcH6OrqomzZsmjZsiWOHj2qVGfVqlWoV68etLS0UKlSJYwaNQovX77Mcax169bB2toaOjo6cHBwwOnTp3N9zLS0NMyePRs1atSAlpYWLC0tMXnyZKSlpeUZKwAkJSWhbNmySr2WZcqUgYmJCXR0dHLdJzU1FW/evMn32EIIJCUlQQjxzjpVq1bN9XVYEKGhofDy8kL79u1zJHhERMWJiRMR0QfYsmULvvvuO2hqaqJXr164c+cOwsPDpe0tW7bEmDFjAADTpk1DYGAgAgMD8cUXX8DX1xcWFhaoU6eOVD59+nSl448cORI3btzArFmzMGXKlDxjuXPnDlxdXdGuXTt4eXmhTJky6NGjB44dO1bo51WQ2LLbsGEDevbsCXV1dXh5eWHIkCHYvXs3mjdvniM5kMvlaNOmDcqXL49FixbB0dERixcvxrp166Q6x44dQ69evVC2bFn4+PjA29sbTk5OOHv27Dtj+OKLLxAYGAgTExPY2tpKcVeoUAGpqalwcnJCYGAg+vTpg4ULF8LIyAju7u65JmMBAQFYvnw5hg4disWLF6NcuXLvfNw5c+agX79+0NDQwNy5czFnzhxYWlri+PHjUh1PT0+MGjUKlSpVwuLFi9GtWzesXbsWLi4uyMjIkOqtX78ew4YNg7m5OX7++Wc0a9YMnTt3RmxsrNJjKhQKdO7cGYsWLUKnTp2wfPlydO3aFUuXLoWrq+s7Y83i5OSE69evY+bMmbh79y6ioqIwb948XLp0CZMnT85Rf8OGDdDT04OOjg7q1q2LrVu3vvPY1atXh5GREQwMDNC3b1/Ex8fnG09BxcfHo0+fPjA3N8fGjRvfO/kiInovgoiI3sulS5cEAHHs2DEhhBAKhUJYWFiIsWPHKtXbuXOnACBOnDiR4xj16tUTjo6OOcoDAgIEANG8eXORmZmZ67bo6GiprGrVqgKA+O2336SyxMREUbFiRdGwYUOpbPbs2SK3j/7cjvmu2E6cOKH0fNLT04WpqamoX7++SE1Nlert379fABCzZs2Sytzc3AQAMXfuXKVjNmzYUNjZ2Un3x44dKwwNDXM894KoWrWq6NChg1KZr6+vACA2b94slaWnp4umTZsKfX19kZSUJIQQIjo6WgAQhoaGIiEhId/HunPnjlBTUxPffvutkMvlStsUCoUQQoiEhAShqakpXFxclOqsWLFCABD+/v5SPKampsLW1lakpaVJ9datWycAKP1fBAYGCjU1NXH69Gmlx1yzZo0AIM6ePZtn3K9evRI9e/YUMplMABAAhK6urggODs5R93//+5/w9fUVe/fuFatXrxb169cXAMSqVauU6vn6+orRo0eLLVu2iF27domxY8eKMmXKiJo1a4rExMR3xqKnpyfc3NzyjFcIIeRyufjmm2+Empparu8lIqLixh4nIqL3tGXLFpiZmaFVq1YA3q4+5urqiqCgoBzDzt7XkCFDCjyfqVKlSvj222+l+4aGhujfvz8uX76MuLi4IoknN5cuXUJCQgJGjhypNPepQ4cOqFOnDg4cOJBjn+HDhyvdb9GiBe7duyfdNzY2RkpKynv1luXm4MGDMDc3R69evaQyDQ0NjBkzBq9evcLJkyeV6nfr1g0VKlTI97jBwcFQKBSYNWsW1NSUT6lZvSG///470tPTMW7cOKU6Q4YMgaGhodQ+We04fPhwpflU7u7uMDIyUjr2zp078cUXX6BOnTp49uyZdGvdujUA4MSJE3nGraWlhVq1aqF79+7Ytm0bNm/eDHt7e/Tt2xd//PGHUt2zZ89i7Nix6Ny5M4YPH46IiAjUr18f06ZNQ2pqqlRv7NixWL58OXr37o1u3brB19cXGzduxJ07d7Bq1ap82zI/3t7eOHbsGKZPn86l2YlIJZg4ERG9B7lcjqCgILRq1QrR0dG4e/cu7t69i8aNGyM+Ph6hoaFF8jjVqlUrcN0aNWrkGLpUq1YtAMh1PlRRefDgAQCgdu3aObbVqVNH2p5FW1s7R1JStmxZvHjxQro/cuRI1KpVC+3atYOFhQUGDhyIw4cPf1CMNWvWzJHcfPHFF0rPIUtB2z0qKgpqamp5LkbxrvbR1NRE9erVpe1Z/9asWVOpnoaGBqpXr65UdufOHVy/fh0VKlRQumX9fyckJOQZ9+jRo7Fv3z4EBQXh+++/R58+ffD777+jYsWKGDt2bJ77ampqYvTo0Xj58iUiIiLyrNu7d2+Ym5vj999/z7Nefs6ePYvZs2ejRYsWXC2RiFSGq+oREb2H48eP48mTJwgKCkJQUFCO7Vu2bIGLi8sHP867Juq/r3fNCSmqHrKCKEgPmqmpKa5cuYIjR47g0KFDOHToEAICAtC/f/8cCzoUh6Ju96KmUCjw5ZdfYsmSJblut7S0fOe+6enpWL9+PSZPnqyUSGpoaKBdu3ZYsWIF0tPT81xFMOv4z58/zzdWS0vLAtV7l+fPn6NXr14wNDTE1q1bVbqiJBF93pg4ERG9hy1btsDU1BQrV67MsW337t3Ys2cP1qxZAx0dnTwnsBfl5Pa7d+9CCKF0zNu3bwOAdP2osmXLAgBevnwJY2Njqd5/e1wKE1vVqlUBAH///bc0VCzL33//LW0vLE1NTXTq1AmdOnWCQqHAyJEjsXbtWsycOTPHtbIKEuPVq1ehUCiUkoVbt24pPYfCsra2hkKhwI0bN2Bra/vOxwbetkX2nqP09HRER0fD2dlZqd6dO3eU2jEjIwPR0dFKy25bW1vjzz//xNdff13o19A///yDzMzMXJPljIwMKBSKfBPprGGV+Q1nFELg/v37H3StJXd3d8TGxmLv3r3SypJERKrAoXpERIWUmpqK3bt3o2PHjujevXuO2+jRo5GcnIyQkBAAb69HAyDXpaf19PRyLX8fjx8/xp49e6T7SUlJ2LRpE2xtbWFubg7g7RduADh16pRULyUlJddenILGZm9vD1NTU6xZs0ZpKexDhw7h5s2b6NChQ6Gfyz///KN0X01NDTY2NgBQoOW2/6t9+/aIi4vD9u3bpbLMzEwsX74c+vr6cHR0LPQxAaBr165QU1PD3LlzoVAolLaJ/1+O29nZGZqamvjll1+Uluhev349EhMTpfaxt7dHhQoVsGbNGqSnp0v1NmzYkOP/oWfPnnj06BH8/PxyxJSamoqUlJR3xmxqagpjY2Ps2bNH6XFevXqFffv2oU6dOlKP29OnT3Psn5ycDF9fX5iYmMDOzk4qz63u6tWr8fTpU7Rt2/ad8eTF19cX+/btww8//IDOnTu/1zGIiIoKe5yIiAopJCQEycnJ7/wi16RJE1SoUAFbtmyBq6srbG1toa6uDh8fHyQmJkJLSwutW7eGqakp7OzssHr1avz000+oUaMGTE1Nc/TaFFStWrUwaNAghIeHw8zMDP7+/oiPj0dAQIBUx8XFBVWqVMGgQYMwadIkqKurw9/fHxUqVEBMTIzS8Qoam4aGBnx8fDBgwAA4OjqiV69eiI+Px7Jly2BlZfVe19oZPHgwnj9/jtatW8PCwgIPHjzA8uXLYWtrK81LKoyhQ4di7dq1cHd3R0REBKysrLBr1y6cPXsWvr6+MDAwKPQxgbfzyqZPn4558+ahRYsW+O6776ClpYXw8HBUqlQJXl5eqFChAqZOnYo5c+agbdu26Ny5M/7++2+sWrUKX331lXSRVw0NDfz0008YNmwYWrduDVdXV0RHRyMgICDHHKd+/fphx44dGD58OE6cOIFmzZpBLpfj1q1b2LFjB44cOQJ7e/tcY1ZXV8fEiRMxY8YMNGnSBP3794dcLsf69evx8OFDbN68Waq7cuVKBAcHo1OnTqhSpQqePHkCf39/xMTEIDAwUGk4X9WqVeHq6oovv/wS2traOHPmDIKCgmBra4thw4YpxbBv3z78+eefAN72cl29ehU//fQTAKBz586wsbHB1atX8eOPP0JfXx8NGjRQiis7GxsbKakmIipWql3Uj4jo09OpUyehra0tUlJS3lnH3d1daGhoiGfPngkhhPDz8xPVq1cX6urqSkt5x8XFiQ4dOggDAwOlJaezlgcPDw/Pcex3LUfeoUMHceTIEWFjYyO0tLREnTp1xM6dO3PsHxERIRo3biw0NTVFlSpVxJIlS3I95rti++9y5Fm2b98uGjZsKLS0tES5cuVEnz59xMOHD5XquLm5CT09vRwx/XeZ9F27dgkXFxdhamoqxTls2DDx5MmTdzV5jrb4r/j4eDFgwABhYmIiNDU1xZdffikCAgKU6mQtR75w4cJ8Hyc7f39/6bmXLVtWODo6SsvUZ1mxYoWoU6eO0NDQEGZmZmLEiBHixYsXOY61atUqUa1aNaGlpSXs7e3FqVOnhKOjY46l4dPT04WPj4+oV6+e9Lh2dnZizpw5eS7/nWXLli3CwcFBGBsbCx0dHdG4cWOxa9cupTpHjx4V33zzjTA3NxcaGhrC2NhYuLi4iNDQ0BzHGzx4sKhbt64wMDAQGhoaokaNGuLHH3+UlnrPLmtZ+txuWf8nWa/J/G6zZ8/O97kSERUFmRB5XNqbiIiIiIiIOMeJiIiIiIgoP0yciIiIiIiI8sHEiYiIiIiIKB9MnIiIiIiIiPLBxImIiIiIiCgfTJyIiIiIiIjy8dldAFehUODx48cwMDCATCZTdThERERERKQiQggkJyejUqVKUFPLu0/ps0ucHj9+DEtLS1WHQUREREREJURsbCwsLCzyrPPZJU4GBgYA3jaOoaGhiqMhIiIiIiJVSUpKgqWlpZQj5KVEJE4rV67EwoULERcXhwYNGmD58uVwcHDId7+goCD06tULXbp0QXBwcIEeK2t4nqGhIRMnIiIiIiIq0BQelS8OsX37dnh4eGD27NmIjIxEgwYN0KZNGyQkJOS53/379zFx4kS0aNHiI0VKRERERESfK5UnTkuWLMGQIUMwYMAA1K1bF2vWrIGuri78/f3fuY9cLkefPn0wZ84cVK9e/SNGS0REREREnyOVJk7p6emIiIiAs7OzVKampgZnZ2ecP3/+nfvNnTsXpqamGDRoUL6PkZaWhqSkJKUbERERERFRYag0cXr27BnkcjnMzMyUys3MzBAXF5frPmfOnMH69evh5+dXoMfw8vKCkZGRdOOKekREREREVFglYnGIgkpOTka/fv3g5+cHExOTAu0zdepUeHh4SPezVs4gotLPasoBVYfwUdz37qDqEIiIiEo9lSZOJiYmUFdXR3x8vFJ5fHw8zM3Nc9SPiorC/fv30alTJ6lMoVAAAMqUKYO///4b1tbWSvtoaWlBS0urGKInIiIiIqLPhUqH6mlqasLOzg6hoaFSmUKhQGhoKJo2bZqjfp06dfDXX3/hypUr0q1z585o1aoVrly5wp4kIiIiIiIqFiofqufh4QE3NzfY29vDwcEBvr6+SElJwYABAwAA/fv3R+XKleHl5QVtbW3Ur19faX9jY2MAyFFORERERERUVFSeOLm6uuLp06eYNWsW4uLiYGtri8OHD0sLRsTExEBNTeWrphMRERER0WdMJoQQqg7iY0pKSoKRkRESExNhaGio6nCIqBhxcQgiIiLKS2FyA3blEBERERER5YOJExERERERUT6YOBEREREREeWDiRMREREREVE+mDgRERERERHlg4kTERERERFRPpg4ERERERER5YOJExERERERUT6YOBEREREREeWDiRMREREREVE+mDgRERERERHlg4kTERERERFRPpg4ERERERER5YOJExERERERUT6YOBEREREREeWDiRMREREREVE+mDgRERERERHlg4kTERERERFRPpg4ERERERER5YOJExERERERUT6YOBEREREREeWDiRMREREREVE+mDgRERERERHlg4kTERERERFRPpg4ERERERER5YOJExERERERUT4KnTjFxsbi4cOH0v2LFy9i3LhxWLdu3XsHsXLlSlhZWUFbWxuNGzfGxYsX31l39+7dsLe3h7GxMfT09GBra4vAwMD3fmwiIiIiIqL8FDpx6t27N06cOAEAiIuLwzfffIOLFy9i+vTpmDt3bqED2L59Ozw8PDB79mxERkaiQYMGaNOmDRISEnKtX65cOUyfPh3nz5/H1atXMWDAAAwYMABHjhwp9GMTEREREREVRKETp2vXrsHBwQEAsGPHDtSvXx/nzp3Dli1bsGHDhkIHsGTJEgwZMgQDBgxA3bp1sWbNGujq6sLf3z/X+k5OTvj222/xxRdfwNraGmPHjoWNjQ3OnDlT6McmIiIiIiIqiEInThkZGdDS0gIA/P777+jcuTMAoE6dOnjy5EmhjpWeno6IiAg4Ozv/G5CaGpydnXH+/Pl89xdCIDQ0FH///TdatmyZa520tDQkJSUp3YiIiIiIiAqj0IlTvXr1sGbNGpw+fRrHjh1D27ZtAQCPHz9G+fLlC3WsZ8+eQS6Xw8zMTKnczMwMcXFx79wvMTER+vr60NTURIcOHbB8+XJ88803udb18vKCkZGRdLO0tCxUjERERERERIVOnHx8fLB27Vo4OTmhV69eaNCgAQAgJCREGsJX3AwMDHDlyhWEh4dj/vz58PDwQFhYWK51p06disTEROkWGxv7UWIkIiIiIqLSo0xhd3BycsKzZ8+QlJSEsmXLSuVDhw6Frq5uoY5lYmICdXV1xMfHK5XHx8fD3Nz8nfupqamhRo0aAABbW1vcvHkTXl5ecHJyylFXS0tLGlpIRERERET0Pt7rOk7q6upKSRMAWFlZwdTUtFDH0dTUhJ2dHUJDQ6UyhUKB0NBQNG3atMDHUSgUSEtLK9RjExERERERFVShe5zi4+MxceJEhIaGIiEhAUIIpe1yubxQx/Pw8ICbmxvs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fPiwtGBETEwMnjx5ItVfvXo10tPT0b17d1SsWFG6LVq0qNCPTUREREREVBCF7nHq3Lkz9uzZg0mTJimV7927Fx07dnyvIEaPHq3Ug5VdWFiY0v379++/12MQERERERG9rwIlTr/88ov0d926dTF//nyEhYVJ85D++OMPnD17FhMmTCieKImIiIiIiFSoQInT0qVLle6XLVsWN27cwI0bN6QyY2Nj+Pv7Y8aMGUUbIRERERERkYoVKHH670VviYiIiIiIPifvdR0nIiIiIiKiz0mBepw8PDwwb9486OnpwcPDI8+6S5YsKZLAiIiIiIiISooCJU6XL19GRkYGACAyMhIymSzXeu8qJyIiIiIi+pQVKHE6ceKE9Pd/lwcnIiIiIiIq7Qo1xykjIwNlypTBtWvXiiseIiIiIiKiEqdQiZOGhgaqVKkCuVxeXPEQERERERGVOIVeVW/69OmYNm0anj9/XhzxEBERERERlTgFmuOU3YoVK3D37l1UqlQJVatWhZ6entL2yMjIIguOiIiIiIioJCh04tSlSxeunkdERERERJ+VQidOnp6exRAGERERERFRyVXoOU7Vq1fHP//8k6P85cuXqF69epEERUREREREVJIUOnG6f/9+rqvqpaWl4eHDh0USFBERERERUUlS4KF6ISEh0t9HjhyBkZGRdF8ulyM0NBTVqlUr2uiIiIiIiIhKgAInTl27dpX+dnNzU9qmoaEBKysrLF68uMgCIyIiIiIiKikKnDgpFAoAQLVq1RAeHg4TE5NiC4qIiIiIiKgkKfQcpzlz5sDAwCBHeXp6OjZt2lQkQREREREREZUkhV6OfMCAAWjbti1MTU2VypOTkzFgwAD079+/yIIjorxZTTmg6hA+ivveHVQdAhEREX3mCt3jJITI9QK4Dx8+VFowgoiIiIiIqLQocI9Tw4YNIZPJIJPJ8PXXX6NMmX93lcvliI6ORtu2bYslSCIiIiIiIlUq9Kp6V65cQZs2baCvry9t09TUhJWVFerXr1/kARIREREREalagROn2bNnAwCsrKzg6uoKbW1tAG/nNm3btg1Lly5FRERErhfHJSIiIiIi+pQVeo6Tm5sbtLW1cerUKbi5uaFixYpYtGgRWrdujT/++KM4YiQiIiIiIlKpQq2qFxcXhw0bNmD9+vVISkpCz549kZaWhuDgYNStW7e4YiQi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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "all_plots = plot_attributions_at_word(\n", " text=text,\n", + " words=words.values(),\n", " attributions_per_word=word_attributions,\n", " titles = list(map(lambda x: f\"Attributions for code {x}\", encoder.inverse_transform(np.array([predictions]).reshape(-1)).tolist())),\n", ")\n", diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index b272acc..27da44b 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -171,13 +171,17 @@ def run_full_pipeline(tokenizer, sample_text_data, categorical_data, labels, mod attributions = predictions["attributions"][text_idx] word_ids = predictions["word_ids"][text_idx] - word_attributions = map_attributions_to_word(attributions, word_ids) + words, word_attributions = map_attributions_to_word(attributions, text, word_ids, offsets) char_attributions = map_attributions_to_char(attributions, offsets, text) # Note: We're not actually plotting in tests, just calling the functions # to ensure they don't raise errors plot_attributions_at_char(text, char_attributions) - plot_attributions_at_word(text, word_attributions) + plot_attributions_at_word( + text=text, + words=words.values(), + attributions_per_word=word_attributions, + ) def test_wordpiece_tokenizer(sample_data, model_params): diff --git a/torchTextClassifiers/utilities/plot_explainability.py b/torchTextClassifiers/utilities/plot_explainability.py index a5ad7f8..80b3042 100644 --- a/torchTextClassifiers/utilities/plot_explainability.py +++ b/torchTextClassifiers/utilities/plot_explainability.py @@ -53,8 +53,18 @@ def map_attributions_to_char(attributions, offsets, text): np.exp(attributions_per_char), axis=1, keepdims=True ) # softmax normalization +def get_id_to_word(text, word_ids, offsets): + words = {} + for idx, word_id in enumerate(word_ids): + if word_id is None: + continue + start, end = offsets[idx] + words[int(word_id)] = text[start:end] + + return words + -def map_attributions_to_word(attributions, word_ids): +def map_attributions_to_word(attributions, text, word_ids, offsets): """ Maps token-level attributions to word-level attributions based on word IDs. Args: @@ -69,8 +79,9 @@ def map_attributions_to_word(attributions, word_ids): np.ndarray: Array of shape (top_k, num_words) containing word-level attributions. num_words is the number of unique words in the original text. """ - + word_ids = np.array(word_ids) + words = get_id_to_word(text, word_ids, offsets) # Convert None to -1 for easier processing (PAD tokens) word_ids_int = np.array([x if x is not None else -1 for x in word_ids], dtype=int) @@ -99,7 +110,7 @@ def map_attributions_to_word(attributions, word_ids): ) # zero-out non-matching tokens and sum attributions for all tokens belonging to the same word # assert word_attributions.sum(axis=1) == attributions.sum(axis=1), "Sum of word attributions per top_k must equal sum of token attributions per top_k." - return np.exp(word_attributions) / np.sum( + return words, np.exp(word_attributions) / np.sum( np.exp(word_attributions), axis=1, keepdims=True ) # softmax normalization @@ -131,7 +142,7 @@ def plot_attributions_at_char( fig, ax = plt.subplots(figsize=figsize) ax.bar(range(len(text)), attributions_per_char[i]) ax.set_xticks(np.arange(len(text))) - ax.set_xticklabels(list(text), rotation=90) + ax.set_xticklabels(list(text), rotation=45) title = titles[i] if titles is not None else f"Attributions for Top {i+1} Prediction" ax.set_title(title) ax.set_xlabel("Characters in Text") @@ -142,7 +153,7 @@ def plot_attributions_at_char( def plot_attributions_at_word( - text, attributions_per_word, figsize=(10, 2), titles: Optional[List[str]] = None + text, words, attributions_per_word, figsize=(10, 2), titles: Optional[List[str]] = None ): """ Plots word-level attributions as a heatmap. @@ -159,14 +170,13 @@ def plot_attributions_at_word( "matplotlib is required for plotting. Please install it to use this function." ) - words = text.split() top_k = attributions_per_word.shape[0] all_plots = [] for i in range(top_k): fig, ax = plt.subplots(figsize=figsize) ax.bar(range(len(words)), attributions_per_word[i]) ax.set_xticks(np.arange(len(words))) - ax.set_xticklabels(words, rotation=90) + ax.set_xticklabels(words, rotation=45) title = titles[i] if titles is not None else f"Attributions for Top {i+1} Prediction" ax.set_title(title) ax.set_xlabel("Words in Text")