From 6659a59b9bad005636fee66742a12e33f03cf8b8 Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Fri, 20 Sep 2024 09:18:47 +0200 Subject: [PATCH 01/52] Update README.md Remove broken link to dashboard screenshot. Placeholder for a new one. --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6e70b59e..6405f189 100644 --- a/README.md +++ b/README.md @@ -197,9 +197,11 @@ The default hyperparameters used in DIANNA for each explainer as well as the val Explore the explanations of your trained model using the DIANNA dashboard (for now images, text and time series classification is supported). [Click here](https://github.com/dianna-ai/dianna/tree/main/dianna/dashboard) for more information. - - Dianna dashboard screenshot - +_Dianna dashboard screenshot here_ + ## Datasets From 54aa78d0c32f54b557bd6cf827a363146cba7178 Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Fri, 20 Sep 2024 09:28:32 +0200 Subject: [PATCH 02/52] Update dashboard readme.md Removed broken link old dashboard screenshot. Placeholder for a new one. --- dianna/dashboard/readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dianna/dashboard/readme.md b/dianna/dashboard/readme.md index c49a339a..46d7d164 100644 --- a/dianna/dashboard/readme.md +++ b/dianna/dashboard/readme.md @@ -1,6 +1,7 @@ # DIANNA dashboard -![Dianna dashboard screenshot](./dashboard-screenshot.png) + +_Dianna dashboard screenshot here_ The DIANNA dashboard can be used for simple exploration of your trained model explained by DIANNA. The dashboard produces the visual explanation of your selected XAI method. Additionally it allows you to compare the results of different XAI methods, as well as explanations of the top ranked predicted labels. The dashboard was created using [streamlit](https://streamlit.io/). From 6782d9da71cf338875ceab4b0121a65527a5c9a6 Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 20 Sep 2024 10:28:10 +0200 Subject: [PATCH 03/52] added more initial text (from Laura in old branch, commit c7cb4358770b5f8f859b7610e65fd4a586951960) --- dianna/dashboard/Home.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index f3cd87e2..85ae1718 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -41,10 +41,25 @@ st.image(str(data_directory / 'logo.png')) st.markdown(""" - DIANNA is a Python package that brings explainable AI (XAI) to your research project. + DIANNA (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) to your research project. It wraps carefully selected XAI methods in a simple, uniform interface. It's built by, with and for (academic) researchers and research software engineers working on machine - learning projects. + learning projects. DIANNA supports the de-facto standard of neural network models - ONNX. + + ### Dashboard + The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained + for the tasks and datasets presented in the + [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks ). + The dashboard shows the visual explanation of a models' decision on a selected data item by a + selected XAI method (explainer). It allows you to compare the results of different explainers, + as well as explanations of the top ranked predicted labels. The dashboard was created using + [streamlit](https://streamlit.io/). + + This dashboard provides pages for the different data items for which DIANNA can be used; + Image data, Tabular data, Text data, and Time series data. + Here you can upload a trained (onnx) model, data item and the other data required for the + specific data item. You can then select the explainer you want to use and set the different + explainer variables. It is also possible to load example data items on each page. ### More information From 6012accdf1b2db1dc17b4c38b2d5bcf4dbd7f289 Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 20 Sep 2024 11:40:40 +0200 Subject: [PATCH 04/52] a smaller logo for the dashboard HOME page --- dianna/data/logo_smaller.png | Bin 0 -> 59260 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dianna/data/logo_smaller.png diff --git a/dianna/data/logo_smaller.png b/dianna/data/logo_smaller.png new file mode 100644 index 0000000000000000000000000000000000000000..c6291cdca21632ba91c55f672708c5c48bfd8abd GIT binary patch literal 59260 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 literal 0 HcmV?d00001 From d16ced19b345ab275175584cf3305d34304bc1bf Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Fri, 20 Sep 2024 11:44:47 +0200 Subject: [PATCH 05/52] Update Home.py making linter happy --- dianna/dashboard/Home.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 85ae1718..85212e7e 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -45,7 +45,7 @@ It wraps carefully selected XAI methods in a simple, uniform interface. It's built by, with and for (academic) researchers and research software engineers working on machine learning projects. DIANNA supports the de-facto standard of neural network models - ONNX. - + ### Dashboard The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks and datasets presented in the From 8e8d328fe410f8466a9cd9aeeff4e078ae12f6ea Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Fri, 20 Sep 2024 11:49:06 +0200 Subject: [PATCH 06/52] Update Home.py linting --- dianna/dashboard/Home.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 85212e7e..7972d437 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -45,17 +45,15 @@ It wraps carefully selected XAI methods in a simple, uniform interface. It's built by, with and for (academic) researchers and research software engineers working on machine learning projects. DIANNA supports the de-facto standard of neural network models - ONNX. - ### Dashboard The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks and datasets presented in the - [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks ). + [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). The dashboard shows the visual explanation of a models' decision on a selected data item by a selected XAI method (explainer). It allows you to compare the results of different explainers, as well as explanations of the top ranked predicted labels. The dashboard was created using [streamlit](https://streamlit.io/). - - This dashboard provides pages for the different data items for which DIANNA can be used; + This dashboard provides pages for the different data items for which DIANNA can be used: Image data, Tabular data, Text data, and Time series data. Here you can upload a trained (onnx) model, data item and the other data required for the specific data item. You can then select the explainer you want to use and set the different From 82002500ad7e2d042aa69c8c889d49a5544d8008 Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Fri, 20 Sep 2024 11:59:28 +0200 Subject: [PATCH 07/52] Update Home.py Explain what the dashboard is. Use a smaller logo on landing page. --- dianna/dashboard/Home.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 7972d437..c0bf433f 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -38,26 +38,19 @@ # Display the content of the selected page if selected == "Home": - st.image(str(data_directory / 'logo.png')) + st.image(str(data_directory / 'logo_smaller.png')) st.markdown(""" DIANNA (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) to your research project. - It wraps carefully selected XAI methods in a simple, uniform interface. It's built by, - with and for (academic) researchers and research software engineers working on machine - learning projects. DIANNA supports the de-facto standard of neural network models - ONNX. + It wraps carefully selected XAI methods (explainers) in a simple, uniform interface. It's built by, with and for (academic) researchers and research software engineers working on machine learning projects. DIANNA supports the de-facto standard of neural network models - ONNX. ### Dashboard - The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained - for the tasks and datasets presented in the - [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). - The dashboard shows the visual explanation of a models' decision on a selected data item by a - selected XAI method (explainer). It allows you to compare the results of different explainers, - as well as explanations of the top ranked predicted labels. The dashboard was created using - [streamlit](https://streamlit.io/). - This dashboard provides pages for the different data items for which DIANNA can be used: - Image data, Tabular data, Text data, and Time series data. - Here you can upload a trained (onnx) model, data item and the other data required for the - specific data item. You can then select the explainer you want to use and set the different - explainer variables. It is also possible to load example data items on each page. + The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). + The dashboard shows the visual explanation of a models' decision on a selected data item by a selected XAI method (explainer). It allows you to compare the results of different explainers, as well as explanations of the top ranked predicted labels. + The dashboard was created using [streamlit](https://streamlit.io/). + This dashboard has separate interfaces to the different data modalities supported by DIANNA: Image, Text, Tabular, and Time series data. + It primarily illustrates the examples from the DIANNA tutorials. + It is also possible to upload own trained (onnx) model, a data item for which you would like the model's decision explanation and other data required by the specific explainer. You can then select the explainer you want to use and set its hyperparameters. + ### More information From 9ad06d4f376dc16846dbf3ba12d03e0110b234d8 Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Fri, 20 Sep 2024 12:01:56 +0200 Subject: [PATCH 08/52] Update Home.py shorter lines to make linter happy --- dianna/dashboard/Home.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index c0bf433f..4d23d655 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -42,14 +42,19 @@ st.markdown(""" DIANNA (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) to your research project. - It wraps carefully selected XAI methods (explainers) in a simple, uniform interface. It's built by, with and for (academic) researchers and research software engineers working on machine learning projects. DIANNA supports the de-facto standard of neural network models - ONNX. + It wraps carefully selected XAI methods (explainers) in a simple, uniform interface. + It's built by, with and for (academic) researchers and research software engineers working on machine learning projects. + DIANNA supports the de-facto standard of neural network models - ONNX. ### Dashboard - The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). - The dashboard shows the visual explanation of a models' decision on a selected data item by a selected XAI method (explainer). It allows you to compare the results of different explainers, as well as explanations of the top ranked predicted labels. + The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks + and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). + The dashboard shows the visual explanation of a models' decision on a selected data item by a selected XAI method (explainer). + It allows you to compare the results of different explainers, as well as explanations of the top ranked predicted labels. The dashboard was created using [streamlit](https://streamlit.io/). This dashboard has separate interfaces to the different data modalities supported by DIANNA: Image, Text, Tabular, and Time series data. It primarily illustrates the examples from the DIANNA tutorials. - It is also possible to upload own trained (onnx) model, a data item for which you would like the model's decision explanation and other data required by the specific explainer. You can then select the explainer you want to use and set its hyperparameters. + It is also possible to upload own trained (onnx) model, a data item for which you would like the model's decision explanation + and other data required by the specific explainer. You can then select the explainer you want to use and set its hyperparameters. ### More information From 170cdc1a0270741dba312fc53bd1a973084f0d29 Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 20 Sep 2024 12:07:02 +0200 Subject: [PATCH 09/52] trying to fix whitespaces... --- dianna/dashboard/Home.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 4d23d655..ce14af6f 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -41,22 +41,21 @@ st.image(str(data_directory / 'logo_smaller.png')) st.markdown(""" - DIANNA (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) to your research project. - It wraps carefully selected XAI methods (explainers) in a simple, uniform interface. + DIANNA (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) to your research project. + It wraps carefully selected XAI methods (explainers) in a simple, uniform interface. It's built by, with and for (academic) researchers and research software engineers working on machine learning projects. DIANNA supports the de-facto standard of neural network models - ONNX. ### Dashboard The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). - The dashboard shows the visual explanation of a models' decision on a selected data item by a selected XAI method (explainer). + The dashboard shows the visual explanation of a models' decision on a selected data item by a selected XAI method (explainer). It allows you to compare the results of different explainers, as well as explanations of the top ranked predicted labels. The dashboard was created using [streamlit](https://streamlit.io/). This dashboard has separate interfaces to the different data modalities supported by DIANNA: Image, Text, Tabular, and Time series data. It primarily illustrates the examples from the DIANNA tutorials. It is also possible to upload own trained (onnx) model, a data item for which you would like the model's decision explanation and other data required by the specific explainer. You can then select the explainer you want to use and set its hyperparameters. - - + ### More information - [Source code](https://github.com/dianna-ai/dianna) From bbc09746714ae20cddbd2b4c4ea79dc3cc61973d Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 20 Sep 2024 12:25:04 +0200 Subject: [PATCH 10/52] trying to fix whitespaces... --- dianna/dashboard/Home.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index ce14af6f..14479246 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -41,9 +41,11 @@ st.image(str(data_directory / 'logo_smaller.png')) st.markdown(""" - DIANNA (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) to your research project. + DIANNA (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) + to your research project. It wraps carefully selected XAI methods (explainers) in a simple, uniform interface. - It's built by, with and for (academic) researchers and research software engineers working on machine learning projects. + It's built by, with and for (academic) researchers and research software engineers + working on machine learning projects. DIANNA supports the de-facto standard of neural network models - ONNX. ### Dashboard The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks @@ -55,7 +57,6 @@ It primarily illustrates the examples from the DIANNA tutorials. It is also possible to upload own trained (onnx) model, a data item for which you would like the model's decision explanation and other data required by the specific explainer. You can then select the explainer you want to use and set its hyperparameters. - ### More information - [Source code](https://github.com/dianna-ai/dianna) From 30f4bc6ac18d4bec5d70226a8b999d887dc2026d Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 20 Sep 2024 12:28:03 +0200 Subject: [PATCH 11/52] fixing too long lines --- dianna/dashboard/Home.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 14479246..c393d992 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -49,14 +49,19 @@ DIANNA supports the de-facto standard of neural network models - ONNX. ### Dashboard The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks - and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). - The dashboard shows the visual explanation of a models' decision on a selected data item by a selected XAI method (explainer). - It allows you to compare the results of different explainers, as well as explanations of the top ranked predicted labels. + and datasets presented + in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). + The dashboard shows the visual explanation of a models' decision on a selected data item + by a selected XAI method (explainer). + It allows you to compare the results of different explainers, as well as explanations + of the top ranked predicted labels. The dashboard was created using [streamlit](https://streamlit.io/). - This dashboard has separate interfaces to the different data modalities supported by DIANNA: Image, Text, Tabular, and Time series data. + This dashboard has separate interfaces to the different data modalities supported by DIANNA: + Image, Text, Tabular, and Time series data. It primarily illustrates the examples from the DIANNA tutorials. - It is also possible to upload own trained (onnx) model, a data item for which you would like the model's decision explanation - and other data required by the specific explainer. You can then select the explainer you want to use and set its hyperparameters. + It is also possible to upload own trained (onnx) model, a data item for which you would like + the model's decision explanation and other data required by the specific explainer. + You can then select the explainer you want to use and set its hyperparameters. ### More information - [Source code](https://github.com/dianna-ai/dianna) From 41117c6c9dcafb25b4697a981ff96f273c877bcb Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 20 Sep 2024 12:46:43 +0200 Subject: [PATCH 12/52] first simple version of the text on the landing page. --- dianna/dashboard/Home.py | 13 ++++++++++--- dianna/data/logo_smaller.png | Bin 59260 -> 0 bytes 2 files changed, 10 insertions(+), 3 deletions(-) delete mode 100644 dianna/data/logo_smaller.png diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index c393d992..526fcc96 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -38,31 +38,38 @@ # Display the content of the selected page if selected == "Home": - st.image(str(data_directory / 'logo_smaller.png')) + st.image(str(data_directory / 'logo.png'), width = 360) st.markdown(""" DIANNA (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) to your research project. + It wraps carefully selected XAI methods (explainers) in a simple, uniform interface. It's built by, with and for (academic) researchers and research software engineers working on machine learning projects. + DIANNA supports the de-facto standard of neural network models - ONNX. + ### Dashboard The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). + The dashboard shows the visual explanation of a models' decision on a selected data item by a selected XAI method (explainer). It allows you to compare the results of different explainers, as well as explanations of the top ranked predicted labels. The dashboard was created using [streamlit](https://streamlit.io/). - This dashboard has separate interfaces to the different data modalities supported by DIANNA: + + There are separate interfaces to the different data modalities supported by DIANNA: Image, Text, Tabular, and Time series data. + It primarily illustrates the examples from the DIANNA tutorials. It is also possible to upload own trained (onnx) model, a data item for which you would like the model's decision explanation and other data required by the specific explainer. You can then select the explainer you want to use and set its hyperparameters. - ### More information + + ### More information - [Source code](https://github.com/dianna-ai/dianna) - [Documentation](https://dianna.readthedocs.io/) diff --git a/dianna/data/logo_smaller.png b/dianna/data/logo_smaller.png deleted file mode 100644 index c6291cdca21632ba91c55f672708c5c48bfd8abd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59260 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 From 5466e47d86a53ac4dffb171cdf1d0436e16d1c3a Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 20 Sep 2024 14:43:25 +0200 Subject: [PATCH 13/52] used colormap --- dianna/data/colormap.png | Bin 0 -> 58485 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dianna/data/colormap.png diff --git a/dianna/data/colormap.png b/dianna/data/colormap.png new file mode 100644 index 0000000000000000000000000000000000000000..87af3c13ef0298ad76fd28fbb53041c9e8c80bf3 GIT binary patch literal 58485 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%0Ao_8K&BkS}*^^#WZFwOhtRWOQVaR1$Z|AysSZ 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 bV14r=rzAhQg*IN|EvqS=QOuP$zw^HUOh#Hn literal 0 HcmV?d00001 From 985db10d013f2724e79a1b407230f2c1ea7a6b39 Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 20 Sep 2024 15:33:53 +0200 Subject: [PATCH 14/52] final proposal dashboard's landing page text (and images) --- dianna/dashboard/Home.py | 50 +++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 526fcc96..2cf66d8e 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -41,38 +41,52 @@ st.image(str(data_directory / 'logo.png'), width = 360) st.markdown(""" - DIANNA (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) - to your research project. - - It wraps carefully selected XAI methods (explainers) in a simple, uniform interface. - It's built by, with and for (academic) researchers and research software engineers - working on machine learning projects. - - DIANNA supports the de-facto standard of neural network models - ONNX. + **DIANNA** (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) + to your research project.
+ It wraps _systematically_ selected XAI methods (**explainers**) in a simple, uniform interface.
+ It's built by, with and for academic researchers and research software engineers + who use AI, but users not need to be XAI experts!
+ DIANNA supports the de-facto standard format of neural network models - [ONNX](https://onnx.ai/:). ### Dashboard - The DIANNA dashboard can be used for explanation of the behaviour of several ONNX models trained for the tasks + The DIANNA dashboard can be used for explanation of the outcomes of several ONNX models trained for the tasks and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). - The dashboard shows the visual explanation of a models' decision on a selected data item - by a selected XAI method (explainer). + The dashboard shows the visual explanation of a models' outcome + on a selected data _instance_ by one or more selected explainers.
It allows you to compare the results of different explainers, as well as explanations - of the top ranked predicted labels. - The dashboard was created using [streamlit](https://streamlit.io/). + of the top ranked predicted model outcomes. + + There are separate sections for each of the different _data modalities_ supported by DIANNA: + :gray-background[**Image**], :gray-background[**Text**], + :gray-background[**Tabular**], and :gray-background[**Time series**] data.
+ The visual explanation is an overlaid on the data instance :rainbow-background[**heatmap**] + highlighting the relevance (attribution) of each data instance _element_ to a selected model's outcome.
+ The data element for images is a (super)pixel, for text - a word, for tabular data - an attribute, + and for time-series - a time interval. Attributions can be positive, negative or irrelevant.
+ To interpret heatmaps, note that attributions are bound between -1 and 1. + The maximum (positive) value is set to 1 and the minimum (negative) value to -1.
+ The dashboard uses the _bwr (blue white red)_ colormap assigning :blue[**blue**] color to negative + relevances, **white** color to near-zero values, and :red[**red**] color to positive values. + + """, + unsafe_allow_html=True) - There are separate interfaces to the different data modalities supported by DIANNA: - Image, Text, Tabular, and Time series data. + st.image(str(data_directory / 'colormap.png'), width = 660) + + st.markdown(""" + The dashboard _primarily_ illustrates the examples from the DIANNA tutorials. - It primarily illustrates the examples from the DIANNA tutorials. - It is also possible to upload own trained (onnx) model, a data item for which you would like - the model's decision explanation and other data required by the specific explainer. + It is also possible to upload _own_ trained (ONNX) model and data item for which you would like + the model's decision explanation.
You can then select the explainer you want to use and set its hyperparameters. ### More information - [Source code](https://github.com/dianna-ai/dianna) - [Documentation](https://dianna.readthedocs.io/) + - [XAI choice](https://blog.esciencecenter.nl/how-to-find-your-artificial-intelligence-explainer-dbb1ac608009) """, unsafe_allow_html=True) From ce5292bd50d152f3ff80da45b8fb503508e232cf Mon Sep 17 00:00:00 2001 From: Laura Ootes Date: Mon, 23 Sep 2024 09:32:02 +0200 Subject: [PATCH 15/52] update test --- tests/test_dashboard_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dashboard_setup.py b/tests/test_dashboard_setup.py index 75e1c602..08bdfea0 100644 --- a/tests/test_dashboard_setup.py +++ b/tests/test_dashboard_setup.py @@ -79,7 +79,7 @@ def test_page_load(page: Page): for selector in ( page.get_by_role('img', name='0'), - page.get_by_text('More information'), + page.get_by_text('Dashboard'), ): expect(selector).to_be_visible() From b92afc657ec9662c72550dede8973144e7aa94a6 Mon Sep 17 00:00:00 2001 From: Laura Ootes Date: Mon, 23 Sep 2024 10:40:30 +0200 Subject: [PATCH 16/52] update tests --- tests/test_dashboard_setup.py | 4 ++-- tests/test_dashboard_tabular.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_dashboard_setup.py b/tests/test_dashboard_setup.py index 08bdfea0..dc4ca92c 100644 --- a/tests/test_dashboard_setup.py +++ b/tests/test_dashboard_setup.py @@ -78,8 +78,8 @@ def test_page_load(page: Page): expect(page).to_have_title("Dianna's dashboard") for selector in ( - page.get_by_role('img', name='0'), - page.get_by_text('Dashboard'), + page.get_by_role("img", name="0").first, + page.get_by_role("heading", name="Dashboard"), ): expect(selector).to_be_visible() diff --git a/tests/test_dashboard_tabular.py b/tests/test_dashboard_tabular.py index f1fb4e76..1fb56f9d 100644 --- a/tests/test_dashboard_tabular.py +++ b/tests/test_dashboard_tabular.py @@ -169,7 +169,6 @@ def test_tabular_penguin(page: Page): page.get_by_text('Running...').wait_for(state='detached', timeout=300_000) for selector in ( - page.get_by_text('Predicted class:'), page.get_by_test_id('stMetricValue').get_by_text('Gentoo'), page.get_by_role('heading', name='RISE').get_by_text('RISE'), page.get_by_role('heading', name='KernelSHAP').get_by_text('KernelSHAP'), From 9c9a0648f6830e8ab38951da9fe4fbcd4fa0bd27 Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 25 Sep 2024 11:19:33 +0200 Subject: [PATCH 17/52] more centered text on landing page --- dianna/dashboard/Home.py | 108 +++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 2cf66d8e..69f786e3 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -38,57 +38,63 @@ # Display the content of the selected page if selected == "Home": - st.image(str(data_directory / 'logo.png'), width = 360) - - st.markdown(""" - **DIANNA** (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) - to your research project.
- It wraps _systematically_ selected XAI methods (**explainers**) in a simple, uniform interface.
- It's built by, with and for academic researchers and research software engineers - who use AI, but users not need to be XAI experts!
- DIANNA supports the de-facto standard format of neural network models - [ONNX](https://onnx.ai/:). - - ### Dashboard - The DIANNA dashboard can be used for explanation of the outcomes of several ONNX models trained for the tasks - and datasets presented - in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). - - The dashboard shows the visual explanation of a models' outcome - on a selected data _instance_ by one or more selected explainers.
- It allows you to compare the results of different explainers, as well as explanations - of the top ranked predicted model outcomes. - - There are separate sections for each of the different _data modalities_ supported by DIANNA: - :gray-background[**Image**], :gray-background[**Text**], - :gray-background[**Tabular**], and :gray-background[**Time series**] data.
- The visual explanation is an overlaid on the data instance :rainbow-background[**heatmap**] - highlighting the relevance (attribution) of each data instance _element_ to a selected model's outcome.
- The data element for images is a (super)pixel, for text - a word, for tabular data - an attribute, - and for time-series - a time interval. Attributions can be positive, negative or irrelevant.
- To interpret heatmaps, note that attributions are bound between -1 and 1. - The maximum (positive) value is set to 1 and the minimum (negative) value to -1.
- The dashboard uses the _bwr (blue white red)_ colormap assigning :blue[**blue**] color to negative - relevances, **white** color to near-zero values, and :red[**red**] color to positive values. - - """, - unsafe_allow_html=True) - - st.image(str(data_directory / 'colormap.png'), width = 660) - - st.markdown(""" - The dashboard _primarily_ illustrates the examples from the DIANNA tutorials. - - It is also possible to upload _own_ trained (ONNX) model and data item for which you would like - the model's decision explanation.
- You can then select the explainer you want to use and set its hyperparameters. - - ### More information - - - [Source code](https://github.com/dianna-ai/dianna) - - [Documentation](https://dianna.readthedocs.io/) - - [XAI choice](https://blog.esciencecenter.nl/how-to-find-your-artificial-intelligence-explainer-dbb1ac608009) - """, - unsafe_allow_html=True) + + _, col, _ = st.columns([1, 3, 1]) + with col: + st.markdown("""#""") + + + st.image(str(data_directory / 'logo.png'), width = 360) + + st.markdown(""" + **DIANNA** (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) + to your research project.
+ It wraps _systematically_ selected XAI methods (**explainers**) in a simple, uniform interface.
+ It's built by, with and for academic researchers and research software engineers + who use AI, but users not need to be XAI experts!
+ DIANNA supports the de-facto standard format of neural network models - [ONNX](https://onnx.ai/:). + + ### Dashboard + The DIANNA dashboard can be used for explanation of the outcomes of several ONNX models trained for the tasks + and datasets presented + in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). + + The dashboard shows the visual explanation of a models' outcome + on a selected data _instance_ by one or more selected explainers.
+ It allows you to compare the results of different explainers, as well as explanations + of the top ranked predicted model outcomes. + + There are separate sections for each of the different _data modalities_ supported by DIANNA: + :gray-background[**Image**], :gray-background[**Text**], + :gray-background[**Tabular**], and :gray-background[**Time series**] data.
+ The visual explanation is an overlaid on the data instance :rainbow-background[**heatmap**] + highlighting the relevance (attribution) of each data instance _element_ to a selected model's outcome.
+ The data element for images is a (super)pixel, for text - a word, for tabular data - an attribute, + and for time-series - a time interval. Attributions can be positive, negative or irrelevant.
+ To interpret heatmaps, note that attributions are bound between -1 and 1. + The maximum (positive) value is set to 1 and the minimum (negative) value to -1.
+ The dashboard uses the _bwr (blue white red)_ colormap assigning :blue[**blue**] color to negative + relevances, **white** color to near-zero values, and :red[**red**] color to positive values. + + """, + unsafe_allow_html=True) + + st.image(str(data_directory / 'colormap.png'), width = 660) + + st.markdown(""" + The dashboard _primarily_ illustrates the examples from the DIANNA tutorials. + + It is also possible to upload _own_ trained (ONNX) model and data item for which you would like + the model's decision explanation.
+ You can then select the explainer you want to use and set its hyperparameters. + + ### More information + + - [Source code](https://github.com/dianna-ai/dianna) + - [Documentation](https://dianna.readthedocs.io/) + - [XAI choice](https://blog.esciencecenter.nl/how-to-find-your-artificial-intelligence-explainer-dbb1ac608009) + """, + unsafe_allow_html=True) else: # Dynamically import and execute the page From 7e7639618739e57543571e13a4195d9d256459c2 Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 25 Sep 2024 12:11:34 +0200 Subject: [PATCH 18/52] addressed review remarks on the landing page text --- dianna/dashboard/Home.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 69f786e3..6e994182 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -50,6 +50,9 @@ **DIANNA** (Deep Insight And Neural Network Analysis) is a Python package that brings explainable AI (XAI) to your research project.
It wraps _systematically_ selected XAI methods (**explainers**) in a simple, uniform interface.
+ The currently supported explainers are [RISE](http://bmvc2018.org/contents/papers/1064.pdf), + [LIME](https://www.kdd.org/kdd2016/papers/files/rfp0573-ribeiroA.pdf) and + [KernelSHAP](https://proceedings.neurips.cc/paper/2017/file/8a20a8621978632d76c43dfd28b67767-Paper.pdf).
It's built by, with and for academic researchers and research software engineers who use AI, but users not need to be XAI experts!
DIANNA supports the de-facto standard format of neural network models - [ONNX](https://onnx.ai/:). @@ -69,10 +72,11 @@ :gray-background[**Tabular**], and :gray-background[**Time series**] data.
The visual explanation is an overlaid on the data instance :rainbow-background[**heatmap**] highlighting the relevance (attribution) of each data instance _element_ to a selected model's outcome.
- The data element for images is a (super)pixel, for text - a word, for tabular data - an attribute, - and for time-series - a time interval. Attributions can be positive, negative or irrelevant.
- To interpret heatmaps, note that attributions are bound between -1 and 1. - The maximum (positive) value is set to 1 and the minimum (negative) value to -1.
+ The data element for images is a (super)pixel, for text a word, for tabular data an attribute, + and for time-series a time interval. Attributions can be positive, negative or irrelevant.
+ To interpret heatmaps, note that attributions for LIME and KernelSHAP are bound between -1 and 1 and + for RISE between 0 and 1. + The maximum (positive) value is set to 1 and the minimum (negative) value to -1 (1 and 0 for RISE).
The dashboard uses the _bwr (blue white red)_ colormap assigning :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. @@ -84,8 +88,8 @@ st.markdown(""" The dashboard _primarily_ illustrates the examples from the DIANNA tutorials. - It is also possible to upload _own_ trained (ONNX) model and data item for which you would like - the model's decision explanation.
+ It is also possible to upload _own_ trained (ONNX) model, the task-specific class labels, + and data item for which you would like the model's decision explanation.
You can then select the explainer you want to use and set its hyperparameters. ### More information From af4497dfd7957306415f9ef6d3c57a14061223c9 Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 25 Sep 2024 12:53:08 +0200 Subject: [PATCH 19/52] data instance' for coherence --- dianna/dashboard/Home.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 6e994182..59a86841 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -89,7 +89,7 @@ The dashboard _primarily_ illustrates the examples from the DIANNA tutorials. It is also possible to upload _own_ trained (ONNX) model, the task-specific class labels, - and data item for which you would like the model's decision explanation.
+ and the data instance for which you would like the model's decision explanation.
You can then select the explainer you want to use and set its hyperparameters. ### More information From d425bee4582295c59707df7ae79b6803785ffd37 Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 25 Sep 2024 13:25:03 +0200 Subject: [PATCH 20/52] changing page names --- dianna/dashboard/pages/Images.py | 2 +- dianna/dashboard/pages/Tabular.py | 2 +- dianna/dashboard/pages/Text.py | 2 +- dianna/dashboard/pages/Time_series.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index c418db01..33d98243 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -14,7 +14,7 @@ add_sidebar_logo() -st.title('Image explanation') +st.title('Explaining Image data classification') st.sidebar.header('Input data') diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index 55dc5834..fd1296e5 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -22,7 +22,7 @@ add_sidebar_logo() -st.title('Tabular data explanation') +st.title('Explaining Tabular data classification/regression') st.sidebar.header('Input data') diff --git a/dianna/dashboard/pages/Text.py b/dianna/dashboard/pages/Text.py index b414d5f1..c20dcec8 100644 --- a/dianna/dashboard/pages/Text.py +++ b/dianna/dashboard/pages/Text.py @@ -14,7 +14,7 @@ add_sidebar_logo() -st.title('Text explanation') +st.title('Explaining Textual data classification') st.sidebar.header('Input data') diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index a8657da0..83f32313 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -15,7 +15,7 @@ from dianna.utils.downloader import download from dianna.visualization import plot_timeseries -st.title('Time series explanation') +st.title('Explaining Time series data classification') add_sidebar_logo() st.sidebar.header('Input data') From e2caa3132e0624245bee92277b20d69547e4f13c Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 25 Sep 2024 14:51:37 +0200 Subject: [PATCH 21/52] Added explanatory text and colormap image for the Image classification page. Added short explanation text for the Hand-digit recogniiotn example. --- dianna/dashboard/pages/Images.py | 40 ++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index 33d98243..ebbcb108 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -1,4 +1,5 @@ import streamlit as st +import sys from _image_utils import open_image from _model_utils import load_labels from _model_utils import load_model @@ -12,10 +13,34 @@ from dianna.utils.downloader import download from dianna.visualization import plot_image +if sys.version_info < (3, 10): + from importlib_resources import files +else: + from importlib.resources import files +data_directory = files('dianna.data') + add_sidebar_logo() st.title('Explaining Image data classification') +st.markdown( + """ + The explanation is visualised as a **relevance heatmap** overlayed on top of the input image.
+ The heatmap consists of the relevance _attributions_ of all individual pixels/super-pixels of the image + to a **pretrained model**'s classification.
+ The attribution heatmap can be computed for any class. + + To interpret heatmaps, note that the attributions for the LIME and KernelSHAP explainers are bound between + -1 and 1 and for the RISE explainer between 0 and 1.
+ The _bwr (blue white red)_ attribution colormap + assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, + and :red[**red**] color to positive values. + """, + unsafe_allow_html=True + + ) +st.image(str(data_directory / 'colormap.png'), width = 660) + st.sidebar.header('Input data') input_type = st.sidebar.radio( @@ -45,14 +70,15 @@ st.markdown( """ - This example demonstrates the use of DIANNA on a pretrained binary - [MNIST](https://yann.lecun.com/exdb/mnist/) model using a hand-written digit images. - The model predict for an image of a hand-written 0 or 1, which of the two it most - likely is. - This example visualizes the relevance attributions for each pixel/super-pixel by - displaying them on top of the input image. - """ + ************************************************************************************ + This example demonstrates the use of DIANNA on explaining a + [**binary MNIST model**](https://zenodo.org/records/5907177) pretrained on **only** images of + the hand-written digits 0 and 1.
+ The model classifies an image of a hand-written digit as displaying 0 or 1. + """, + unsafe_allow_html=True ) + else: st.info('Select an example in the left panel to coninue') st.stop() From 0f65ffb96e262a933eaba5f99137d01a99a36488 Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 25 Sep 2024 14:57:33 +0200 Subject: [PATCH 22/52] fixing linter issues --- dianna/dashboard/pages/Images.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index ebbcb108..3a4232ce 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -36,9 +36,9 @@ assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. """, - unsafe_allow_html=True - + unsafe_allow_html=True ) + st.image(str(data_directory / 'colormap.png'), width = 660) st.sidebar.header('Input data') @@ -76,7 +76,7 @@ the hand-written digits 0 and 1.
The model classifies an image of a hand-written digit as displaying 0 or 1. """, - unsafe_allow_html=True + unsafe_allow_html=True ) else: From 6312bfcfdd70157de51ade049c949d699e54eec1 Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 25 Sep 2024 15:01:20 +0200 Subject: [PATCH 23/52] auto fixing linter issues --- dianna/dashboard/pages/Images.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index 3a4232ce..fec19255 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -1,5 +1,5 @@ -import streamlit as st import sys +import streamlit as st from _image_utils import open_image from _model_utils import load_labels from _model_utils import load_model From 2302d73e0a2b921b5f49cd1372fb52edbd7022c3 Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 25 Sep 2024 16:15:51 +0200 Subject: [PATCH 24/52] added explanatory texts to Tabular page and examples --- dianna/dashboard/pages/Tabular.py | 62 ++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index fd1296e5..36139753 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -24,6 +24,23 @@ st.title('Explaining Tabular data classification/regression') +st.markdown( + """ + The explanation is visualised as a **relevance bar-chart** for top up to 10 most + relevant _attributes (features)_.
+ The chart displays the relevance _attributions_ of the individual features of the tabular data + to a **pretrained model**'s classification or regresson prediciton.
+ The attribution chart can be computed for any predicted outcome. + + To interpret the chart, note that the attributions for the LIME and KernelSHAP explainers are bound between + -1 and 1 and for the RISE explainer between 0 and 1.
+ The attribution colormap + assigns :blue[**blue**] color to negative relevances, + and :red[**red**] color to positive values. + """, + unsafe_allow_html=True + ) + st.sidebar.header('Input data') input_type = st.sidebar.radio( @@ -38,12 +55,12 @@ if input_type == 'Use an example': load_example = st.sidebar.radio( label='Use example', - options=('Sunshine hours prediction', 'Penguin identification'), + options=('Sunshine hours prediction (regression)', 'Penguin identification (classification)'), index = None, on_change = reset_method, key='Tabular_load_example') - if load_example == "Sunshine hours prediction": + if load_example == "Sunshine hours prediction (regression)": tabular_data_file = download('weather_prediction_dataset_light.csv', 'data') tabular_model_file = download('sunshine_hours_regression_model.onnx', 'model') tabular_training_data_file = tabular_data_file @@ -55,20 +72,23 @@ mode = 'regression' st.markdown( """ - This example demonstrates the use of DIANNA on a pre-trained regression - [model to predict tomorrow's sunshine hours](https://zenodo.org/records/10580833) + ********************************************************************************** + This example demonstrates the use of DIANNA on a pre-trained [regression + model](https://zenodo.org/records/10580833) to predict tomorrow's sunshine hours based on meteorological data from today. The model is trained on the - [weather prediction dataset](https://zenodo.org/records/5071376). - The meteorological data includes for various European cities the - cloud coverage,humidity, air pressure, global radiation, precipitation, and - mean, min and max temeprature. + [weather prediction dataset](https://zenodo.org/records/5071376).
+ The meteorological data includes measurements (features) of + _cloud coverage, humidity, air pressure, global radiation, precipitation_, and + _mean, min_ and _max temeprature_ + for variuos European cities. DIANNA's visualisation shows the top most important features contributing to the - sunshine hours prediction, where features contrinuting positively are indicated in red - and those who contribute negatively in blue. - """) - elif load_example == 'Penguin identification': + sunshine hours prediction (positive attributions are indicated in :red[red], negative in :blue[blue]). + """, + unsafe_allow_html=True ) + + elif load_example == 'Penguin identification (classification)': tabular_model_file = download('penguin_model.onnx', 'model') data_penguins = sns.load_dataset('penguins') labels = data_penguins['species'].unique() @@ -79,18 +99,18 @@ st.markdown( """ - This example demonstrates the use of DIANNA on a pre-trained classification - [model to classify penguins in to three different species](https://zenodo.org/records/10580743) - based on a number of measurable physical characteristics. + ********************************************************************************* + This example demonstrates the use of DIANNA on a pre-trained [classification + model](https://zenodo.org/records/10580743) to identify is a penguin belongs to one of three different species + based on a number of measurable physical characteristics.
The model is trained on the - [weather prediction dataset](https://zenodo.org/records/5071376). The data is obtained from - the Python seaborn package - The penguin characteristics include the bill length, bill depth, flipper length and body mass. + [penguin dataset](https://www.kaggle.com/code/parulpandey/penguin-dataset-the-new-iris). + The penguin characteristics include the _bill length, _bill depth_, _flipper length_, and _body mass_. DIANNA's visualisation shows the top most important characteristics contributing to the - penguin species classification, where characteristics contributing positively are indicated in red - and those who contribute negatively in blue. - """) + penguin species identification (positive attributions are indicated in :red[red], negative in :blue[blue]). + """, + unsafe_allow_html=True) else: st.info('Select an example in the left panel to coninue') st.stop() From f447e2981b80ca7de2a3f3029864446c0f642677 Mon Sep 17 00:00:00 2001 From: elboyran Date: Thu, 26 Sep 2024 16:30:38 +0200 Subject: [PATCH 25/52] Explanatory test for the Text page and example --- dianna/dashboard/pages/Text.py | 44 +++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/dianna/dashboard/pages/Text.py b/dianna/dashboard/pages/Text.py index c20dcec8..91397cbd 100644 --- a/dianna/dashboard/pages/Text.py +++ b/dianna/dashboard/pages/Text.py @@ -1,3 +1,4 @@ +import sys import streamlit as st from _model_utils import load_labels from _model_utils import load_model @@ -12,10 +13,33 @@ from dianna.utils.downloader import download from dianna.visualization.text import highlight_text +if sys.version_info < (3, 10): + from importlib_resources import files +else: + from importlib.resources import files +data_directory = files('dianna.data') + add_sidebar_logo() st.title('Explaining Textual data classification') +st.markdown( + """ + The explanation is visualised as a **relevance heatmap** overlayed on top of the input text.
+ The heatmap consists of the relevance _attributions_ of all individual words of the text + to a **pretrained model**'s classification.
+ The attribution heatmap can be computed for any class. + + To interpret heatmaps, note that the attributions for the LIME and KernelSHAP explainers are bound between + -1 and 1 and for the RISE explainer between 0 and 1.
+ The _bwr (blue white red)_ attribution colormap + assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, + and :red[**red**] color to positive values. + """, + unsafe_allow_html=True + ) + +st.image(str(data_directory / 'colormap.png'), width = 660) st.sidebar.header('Input data') input_type = st.sidebar.radio( @@ -30,27 +54,31 @@ if input_type == 'Use an example': load_example = st.sidebar.radio( label='Use example', - options=('Movie sentiment',), + options=('Movie sentiment classification',), index = None, on_change = reset_method, key='Text_load_example') - if load_example == 'Movie sentiment': + if load_example == 'Movie sentiment classification': text_input = st.sidebar.text_input( - 'Input string', + 'Input text', value='The movie started out great but the ending was disappointing') text_model_file = download('movie_review_model.onnx', 'model') text_label_file = download('labels_text.txt', 'label') st.markdown( """ + *********************************************************************** This example demonstrates the use of DIANNA on the [Stanford Sentiment Treebank dataset](https://nlp.stanford.edu/sentiment/index.html) which - contains one-sentence movie reviews. A pre-trained neural network - classifier is used, which identifies whether a movie review is positive - or negative. The input string to which the model is applied can be modified - in the left menu. - """) + contains one-sentence movie reviews.
A pre-trained [neural network + classifier](https://zenodo.org/record/5910598) is used, which classifies a movie review + as positive or negative.
+ :blue-background[The input sentence which the model will classify can be modified in + the editable Input text field in the left panel.] + """, + unsafe_allow_html=True + ) else: st.info('Select an example in the left panel to coninue') st.stop() From e82bbfc866187dc629728cd1641413253871ea54 Mon Sep 17 00:00:00 2001 From: elboyran Date: Thu, 26 Sep 2024 17:01:31 +0200 Subject: [PATCH 26/52] Time series page explanatory text. --- dianna/dashboard/pages/Time_series.py | 43 +++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index 83f32313..dc2bca24 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -1,3 +1,4 @@ +import sys import numpy as np import streamlit as st from _model_utils import load_labels @@ -15,8 +16,31 @@ from dianna.utils.downloader import download from dianna.visualization import plot_timeseries +if sys.version_info < (3, 10): + from importlib_resources import files +else: + from importlib.resources import files +data_directory = files('dianna.data') + st.title('Explaining Time series data classification') +st.markdown( + """ + The explanation is visualised as a **relevance heatmap** overlayed on top of the time series.
+ The heatmap consists of the relevance _attributions_ of all individual data points per time moment + of the series to a **pretrained model**'s classification.
+ The attribution heatmap can be computed for any class. + + To interpret heatmaps, note that the attributions for the LIME and KernelSHAP explainers are bound between + -1 and 1 and for the RISE explainer between 0 and 1.
+ The _bwr (blue white red)_ attribution colormap + assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, + and :red[**red**] color to positive values. + """, + unsafe_allow_html=True + ) + +st.image(str(data_directory / 'colormap.png'), width = 660) add_sidebar_logo() st.sidebar.header('Input data') @@ -32,13 +56,13 @@ if input_type == 'Use an example': load_example = st.sidebar.radio( label='Select example', - options = ('Weather', 'Scientific case: FRB'), + options = ('Summer or winter?', 'Scientific use-case (astronomy): Fast Radio Burst detection'), index = None, on_change = reset_method, key = 'TS_load_example' ) - if load_example == "Weather": + if load_example == "Summer or winter?": ts_data_file = download('weather_data.npy', 'data') ts_model_file = download( 'season_prediction_model_temp_max_binary.onnx', 'model') @@ -48,6 +72,7 @@ st.markdown( """ + ****************************************************************** This example demonstrates the use of DIANNA on a pre-trained binary classification model for season prediction. The input data is the [weather prediction @@ -57,8 +82,10 @@ relevance scores are displayed on top of the timeseries. The days contributing positively towards the classification decision are indicated in red and those who contribute negatively in blue. - """) - elif load_example == "Scientific case: FRB": + """, + unsafe_allow_html=True + ) + elif load_example == "Scientific use-case (astronomy): Fast Radio Burst detection": ts_model_file = download('apertif_frb_dynamic_spectrum_model.onnx', 'model') ts_label_file = download('apertif_frb_classes.txt', 'label') ts_data_file = download('FRB211024.npy', 'data') @@ -77,13 +104,17 @@ def preprocess(data): param_key = 'FRB_TS_cb' st.markdown( - """This example demonstrates the use of DIANNA + """ + ************************************************************ + This example demonstrates the use of DIANNA on a pre-trained binary classification model trained to classify Fast Radio Burst (FRB) timeseries data. The goal of the pre-trained convolutional neural network is to determine whether or not the input data contains an FRB-like signal, whereby the two classes are noise and FRB. - """) + """, + unsafe_allow_html=True + ) else: st.info('Select an example in the left panel to coninue') st.stop() From 5f96ce33ef8be63cba0a16ed750e7b4b72c0f08c Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 27 Sep 2024 11:31:20 +0200 Subject: [PATCH 27/52] TS weather example: explanation text --- dianna/dashboard/pages/Time_series.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index dc2bca24..69edf9b6 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -56,13 +56,14 @@ if input_type == 'Use an example': load_example = st.sidebar.radio( label='Select example', - options = ('Summer or winter?', 'Scientific use-case (astronomy): Fast Radio Burst detection'), + options = ('Season prediction from temperature: warm or cold?', + 'Scientific case - radio astronomy: Fast Radio Burst (FRB) detection'), index = None, on_change = reset_method, key = 'TS_load_example' ) - if load_example == "Summer or winter?": + if load_example == "Season prediction from temperature: warm or cold?": ts_data_file = download('weather_data.npy', 'data') ts_model_file = download( 'season_prediction_model_temp_max_binary.onnx', 'model') @@ -74,18 +75,16 @@ """ ****************************************************************** This example demonstrates the use of DIANNA - on a pre-trained binary classification model for season prediction. The - input data is the [weather prediction - dataset](https://zenodo.org/records/5071376). This classification model - uses time (days) as function of mean temperature to predict if the whole - time series is either summer or winter. Using a chosen XAI method the - relevance scores are displayed on top of the timeseries. The days - contributing positively towards the classification decision are - indicated in red and those who contribute negatively in blue. + on a pre-trained binary [classification model](https://zenodo.org/records/7543883) + for season prediction.
The input data is the + [weather prediction dataset](https://zenodo.org/records/5071376). + The binary classification is simplified to warm or cold (conditionally labelled _summer_ or _winter_)
+ The model uses _mean temperature_ as function of time (in days) to predict if the whole + time series is either from a warm (_summer_) or a cold (_winter_) season. """, unsafe_allow_html=True ) - elif load_example == "Scientific use-case (astronomy): Fast Radio Burst detection": + elif load_example == "Scientific case - radio astronomy: Fast Radio Burst (FRB) detection": ts_model_file = download('apertif_frb_dynamic_spectrum_model.onnx', 'model') ts_label_file = download('apertif_frb_classes.txt', 'label') ts_data_file = download('FRB211024.npy', 'data') From 0b576d92fe385e0b0326ca89e3815306f4e9abc3 Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 27 Sep 2024 11:45:44 +0200 Subject: [PATCH 28/52] TS FRB example: explanation text --- dianna/dashboard/pages/Time_series.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index 69edf9b6..9f2ed028 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -106,11 +106,12 @@ def preprocess(data): """ ************************************************************ This example demonstrates the use of DIANNA - on a pre-trained binary classification model trained to classify - Fast Radio Burst (FRB) timeseries data. - The goal of the pre-trained convolutional neural network is to - determine whether or not the input data contains an - FRB-like signal, whereby the two classes are noise and FRB. + on a pre-trained [binary model](https://zenodo.org/records/10656614) for classification of + radio astronomical dynamic spectra, also known as frequency-time data.
+ The scientifically relevant goal is to + determine whether the input data contains a + Fast Radio Burst (FRB)- like signal.
+ The output of the clasisfier is a label for each data point - either noise or FRB. """, unsafe_allow_html=True ) From 3400d33786e7405f2db4b9d5ecbf3685815f7464 Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 27 Sep 2024 14:06:20 +0200 Subject: [PATCH 29/52] TS FRB example fixing case name string in code --- dianna/dashboard/pages/Time_series.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index 9f2ed028..2696e80f 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -143,7 +143,7 @@ def preprocess(data): st.info('Add your input data in the left panel to continue') st.stop() -if load_example != "Scientific case: FRB": +if load_example != "Scientific case - radio astronomy: Fast Radio Burst (FRB) detection": # For normal cases, the input data does not need transformation for either the # model explainer nor the model predictor ts_data_explainer = ts_data_predictor = open_timeseries(ts_data_file) @@ -153,7 +153,7 @@ def preprocess(data): labels = load_labels(ts_label_file) -if load_example == "Scientific case: FRB": +if load_example == "Scientific case - radio astronomy: Fast Radio Burst (FRB) detection": choices = ('RISE',) else: choices = ('RISE', 'LIME') From e2454f1493a48e40d454d3fc84ddcc4a2d7a4201 Mon Sep 17 00:00:00 2001 From: elboyran Date: Fri, 27 Sep 2024 14:25:07 +0200 Subject: [PATCH 30/52] TS test fixed locator name (Weatehr -> Season) --- tests/test_dashboard_time_series.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_dashboard_time_series.py b/tests/test_dashboard_time_series.py index a4baacd1..1d36ef77 100644 --- a/tests/test_dashboard_time_series.py +++ b/tests/test_dashboard_time_series.py @@ -82,12 +82,12 @@ def test_timeseries_page(page: Page): page.locator("label").filter(has_text="Use an example").locator("div").nth(1).click() expect(page.get_by_text("Select an example in the left")).to_be_visible(timeout=200_000) - expect(page.get_by_text("Weather")).to_be_visible() + expect(page.get_by_text("Season")).to_be_visible() expect(page.get_by_text("FRB")).to_be_visible() # Test weather example page.locator("label").filter(has_text="Use an example").locator("div").nth(1).click() - page.locator("label").filter(has_text="Weather").locator("div").nth(1).click() + page.locator("label").filter(has_text="Season").locator("div").nth(1).click() expect(page.get_by_text("Select a method to continue")).to_be_visible(timeout=100_000) time.sleep(2) From cf7872d97522fdc28ec72eb019b60244a996fc95 Mon Sep 17 00:00:00 2001 From: Laura Ootes Date: Fri, 27 Sep 2024 15:52:44 +0200 Subject: [PATCH 31/52] fix example name --- dianna/dashboard/pages/Time_series.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index 2696e80f..44ebd85a 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -189,7 +189,7 @@ def preprocess(data): for col, method in zip(columns, methods): kwargs = method_params[method].copy() kwargs['labels'] = [index] - if load_example == "Scientific case: FRB": + if load_example == "Scientific case - radio astronomy: Fast Radio Burst (FRB) detection": kwargs['_preprocess_function'] = preprocess func = explain_ts_dispatcher[method] @@ -198,7 +198,7 @@ def preprocess(data): with st.spinner(f'Running {method}'): explanation = func(serialized_model, ts_data=ts_data_explainer, **kwargs) - if load_example == "Scientific case: FRB": + if load_example == "Scientific case - radio astronomy: Fast Radio Burst (FRB) detection": fig, axes = plt.subplots(ncols=2, figsize=(14, 5)) # FRB: plot original data ax = axes[0] From 23a5b99a4fb76982219a2ab82d72d90ec867a94d Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Wed, 2 Oct 2024 13:06:53 +0200 Subject: [PATCH 32/52] Update dianna/dashboard/Home.py missing coma Co-authored-by: Laura <95367983+laurasootes@users.noreply.github.com> --- dianna/dashboard/Home.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 59a86841..203af106 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -53,7 +53,7 @@ The currently supported explainers are [RISE](http://bmvc2018.org/contents/papers/1064.pdf), [LIME](https://www.kdd.org/kdd2016/papers/files/rfp0573-ribeiroA.pdf) and [KernelSHAP](https://proceedings.neurips.cc/paper/2017/file/8a20a8621978632d76c43dfd28b67767-Paper.pdf).
- It's built by, with and for academic researchers and research software engineers + It's built by, with, and for academic researchers and research software engineers who use AI, but users not need to be XAI experts!
DIANNA supports the de-facto standard format of neural network models - [ONNX](https://onnx.ai/:). From b0ba71268e3d61434988fed7f41a1180ff791c78 Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Wed, 2 Oct 2024 13:07:43 +0200 Subject: [PATCH 33/52] Update dianna/dashboard/Home.py Co-authored-by: Laura <95367983+laurasootes@users.noreply.github.com> --- dianna/dashboard/Home.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 203af106..e8a0440b 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -58,7 +58,7 @@ DIANNA supports the de-facto standard format of neural network models - [ONNX](https://onnx.ai/:). ### Dashboard - The DIANNA dashboard can be used for explanation of the outcomes of several ONNX models trained for the tasks + The DIANNA dashboard can be used to visualise the explanation of the outcomes of several ONNX models trained for the tasks and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). From 96637689beea1a15bcb3ee947eaf53ea80fe09d2 Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Wed, 2 Oct 2024 13:08:18 +0200 Subject: [PATCH 34/52] Update dianna/dashboard/pages/Tabular.py Co-authored-by: Laura <95367983+laurasootes@users.noreply.github.com> --- dianna/dashboard/pages/Tabular.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index 36139753..c0df945a 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -81,7 +81,7 @@ The meteorological data includes measurements (features) of _cloud coverage, humidity, air pressure, global radiation, precipitation_, and _mean, min_ and _max temeprature_ - for variuos European cities. + for various European cities. DIANNA's visualisation shows the top most important features contributing to the sunshine hours prediction (positive attributions are indicated in :red[red], negative in :blue[blue]). From 063048a275f95875080813378b3f4801e91c0d6f Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Wed, 2 Oct 2024 13:08:53 +0200 Subject: [PATCH 35/52] Update dianna/dashboard/pages/Tabular.py Co-authored-by: Laura <95367983+laurasootes@users.noreply.github.com> --- dianna/dashboard/pages/Tabular.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index c0df945a..b32638e3 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -26,7 +26,7 @@ st.markdown( """ - The explanation is visualised as a **relevance bar-chart** for top up to 10 most + The explanation is visualised as a **relevance bar-chart** for the top (up to 10) most relevant _attributes (features)_.
The chart displays the relevance _attributions_ of the individual features of the tabular data to a **pretrained model**'s classification or regresson prediciton.
From c4ab945183028bdf38346e53946e5789eaa1592e Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Wed, 2 Oct 2024 13:09:13 +0200 Subject: [PATCH 36/52] Update dianna/dashboard/pages/Tabular.py fixing typo Co-authored-by: Laura <95367983+laurasootes@users.noreply.github.com> --- dianna/dashboard/pages/Tabular.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index b32638e3..1169c34e 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -101,7 +101,7 @@ """ ********************************************************************************* This example demonstrates the use of DIANNA on a pre-trained [classification - model](https://zenodo.org/records/10580743) to identify is a penguin belongs to one of three different species + model](https://zenodo.org/records/10580743) to identify if a penguin belongs to one of three different species based on a number of measurable physical characteristics.
The model is trained on the [penguin dataset](https://www.kaggle.com/code/parulpandey/penguin-dataset-the-new-iris). From afba4b515c5744e71e2d24e6b1f61dc17b1a892a Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Wed, 2 Oct 2024 13:09:38 +0200 Subject: [PATCH 37/52] Update dianna/dashboard/pages/Tabular.py fixing typo Co-authored-by: Laura <95367983+laurasootes@users.noreply.github.com> --- dianna/dashboard/pages/Tabular.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index 1169c34e..08822fc1 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -105,7 +105,7 @@ based on a number of measurable physical characteristics.
The model is trained on the [penguin dataset](https://www.kaggle.com/code/parulpandey/penguin-dataset-the-new-iris). - The penguin characteristics include the _bill length, _bill depth_, _flipper length_, and _body mass_. + The penguin characteristics include the _bill length_, _bill depth_, _flipper length_, and _body mass_. DIANNA's visualisation shows the top most important characteristics contributing to the penguin species identification (positive attributions are indicated in :red[red], negative in :blue[blue]). From a28ab1bd84e6c98faf90c138db78d7d640160c76 Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Wed, 2 Oct 2024 13:12:46 +0200 Subject: [PATCH 38/52] Update dianna/dashboard/pages/Tabular.py fixing typo Co-authored-by: Laura <95367983+laurasootes@users.noreply.github.com> --- dianna/dashboard/pages/Tabular.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index 08822fc1..27f0f6b5 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -29,7 +29,7 @@ The explanation is visualised as a **relevance bar-chart** for the top (up to 10) most relevant _attributes (features)_.
The chart displays the relevance _attributions_ of the individual features of the tabular data - to a **pretrained model**'s classification or regresson prediciton.
+ to a **pretrained model**'s classification or regression prediciton. The attribution chart can be computed for any predicted outcome. To interpret the chart, note that the attributions for the LIME and KernelSHAP explainers are bound between From a065ad3353e8709bcb8766caa09ae8fb14e574bf Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 2 Oct 2024 13:26:19 +0200 Subject: [PATCH 39/52] fixing linter error --- dianna/dashboard/Home.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index e8a0440b..5be6c3d1 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -58,8 +58,8 @@ DIANNA supports the de-facto standard format of neural network models - [ONNX](https://onnx.ai/:). ### Dashboard - The DIANNA dashboard can be used to visualise the explanation of the outcomes of several ONNX models trained for the tasks - and datasets presented + The DIANNA dashboard can be used to visualise the explanation of the outcomes of several ONNX models + trained for the tasks and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). The dashboard shows the visual explanation of a models' outcome From 725607befddc3cfa513db273ce5950b3ba214be9 Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 2 Oct 2024 13:30:28 +0200 Subject: [PATCH 40/52] removed redundat explanations in Tabular page --- dianna/dashboard/pages/Tabular.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index 27f0f6b5..bfb72941 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -82,9 +82,6 @@ _cloud coverage, humidity, air pressure, global radiation, precipitation_, and _mean, min_ and _max temeprature_ for various European cities. - - DIANNA's visualisation shows the top most important features contributing to the - sunshine hours prediction (positive attributions are indicated in :red[red], negative in :blue[blue]). """, unsafe_allow_html=True ) @@ -106,9 +103,6 @@ The model is trained on the [penguin dataset](https://www.kaggle.com/code/parulpandey/penguin-dataset-the-new-iris). The penguin characteristics include the _bill length_, _bill depth_, _flipper length_, and _body mass_. - - DIANNA's visualisation shows the top most important characteristics contributing to the - penguin species identification (positive attributions are indicated in :red[red], negative in :blue[blue]). """, unsafe_allow_html=True) else: From ccd6136ed52ad1f1951b6e720b7923914b0e91b2 Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 2 Oct 2024 13:40:30 +0200 Subject: [PATCH 41/52] fixing linter error --- dianna/dashboard/Home.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index 5be6c3d1..c00814fd 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -58,7 +58,7 @@ DIANNA supports the de-facto standard format of neural network models - [ONNX](https://onnx.ai/:). ### Dashboard - The DIANNA dashboard can be used to visualise the explanation of the outcomes of several ONNX models + The DIANNA dashboard can be used to visualise the explanation of the outcomes of several ONNX models trained for the tasks and datasets presented in the [DIANNA Tutorials](https://github.com/dianna-ai/dianna/tree/main/tutorials#datasets-and-tasks). From b4fdc097e9ee4cacf49a2610e49baffbebd35bba Mon Sep 17 00:00:00 2001 From: elboyran Date: Wed, 2 Oct 2024 13:56:13 +0200 Subject: [PATCH 42/52] removed the scaling explanations. --- dianna/dashboard/Home.py | 3 --- dianna/dashboard/pages/Images.py | 2 -- dianna/dashboard/pages/Tabular.py | 2 -- dianna/dashboard/pages/Text.py | 2 -- dianna/dashboard/pages/Time_series.py | 2 -- 5 files changed, 11 deletions(-) diff --git a/dianna/dashboard/Home.py b/dianna/dashboard/Home.py index c00814fd..8cb1cfe5 100644 --- a/dianna/dashboard/Home.py +++ b/dianna/dashboard/Home.py @@ -74,9 +74,6 @@ highlighting the relevance (attribution) of each data instance _element_ to a selected model's outcome.
The data element for images is a (super)pixel, for text a word, for tabular data an attribute, and for time-series a time interval. Attributions can be positive, negative or irrelevant.
- To interpret heatmaps, note that attributions for LIME and KernelSHAP are bound between -1 and 1 and - for RISE between 0 and 1. - The maximum (positive) value is set to 1 and the minimum (negative) value to -1 (1 and 0 for RISE).
The dashboard uses the _bwr (blue white red)_ colormap assigning :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index fec19255..d86c17c7 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -30,8 +30,6 @@ to a **pretrained model**'s classification.
The attribution heatmap can be computed for any class. - To interpret heatmaps, note that the attributions for the LIME and KernelSHAP explainers are bound between - -1 and 1 and for the RISE explainer between 0 and 1.
The _bwr (blue white red)_ attribution colormap assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index bfb72941..a0cc19be 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -32,8 +32,6 @@ to a **pretrained model**'s classification or regression prediciton. The attribution chart can be computed for any predicted outcome. - To interpret the chart, note that the attributions for the LIME and KernelSHAP explainers are bound between - -1 and 1 and for the RISE explainer between 0 and 1.
The attribution colormap assigns :blue[**blue**] color to negative relevances, and :red[**red**] color to positive values. diff --git a/dianna/dashboard/pages/Text.py b/dianna/dashboard/pages/Text.py index 91397cbd..9c677d4f 100644 --- a/dianna/dashboard/pages/Text.py +++ b/dianna/dashboard/pages/Text.py @@ -30,8 +30,6 @@ to a **pretrained model**'s classification.
The attribution heatmap can be computed for any class. - To interpret heatmaps, note that the attributions for the LIME and KernelSHAP explainers are bound between - -1 and 1 and for the RISE explainer between 0 and 1.
The _bwr (blue white red)_ attribution colormap assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index 44ebd85a..b6305441 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -31,8 +31,6 @@ of the series to a **pretrained model**'s classification.
The attribution heatmap can be computed for any class. - To interpret heatmaps, note that the attributions for the LIME and KernelSHAP explainers are bound between - -1 and 1 and for the RISE explainer between 0 and 1.
The _bwr (blue white red)_ attribution colormap assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. From e05557de0f68c2f4edfa468e3887f1982cf929af Mon Sep 17 00:00:00 2001 From: Laura Ootes Date: Wed, 2 Oct 2024 17:24:23 +0200 Subject: [PATCH 43/52] collapse description of the explanation --- dianna/dashboard/pages/Images.py | 42 +++++++++++++++++++-------- dianna/dashboard/pages/Tabular.py | 23 +++++++++++---- dianna/dashboard/pages/Text.py | 34 +++++++++++++++++----- dianna/dashboard/pages/Time_series.py | 30 +++++++++++++++---- 4 files changed, 97 insertions(+), 32 deletions(-) diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index d86c17c7..9d9a0427 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -1,3 +1,4 @@ +import base64 import sys import streamlit as st from _image_utils import open_image @@ -17,27 +18,40 @@ from importlib_resources import files else: from importlib.resources import files + data_directory = files('dianna.data') - -add_sidebar_logo() - -st.title('Explaining Image data classification') - -st.markdown( - """ - The explanation is visualised as a **relevance heatmap** overlayed on top of the input image.
+colormap_path = str(data_directory / 'colormap.png') +with open(colormap_path, "rb") as img_file: + colormap = base64.b64encode(img_file.read()).decode() + +def description_explainer(open='open'): + """Expandable text section with image.""" + return (st.markdown( + f""" +
+ Description of the explanation + + The explanation is visualised as a **relevance heatmap** overlayed on top of the time series.
The heatmap consists of the relevance _attributions_ of all individual pixels/super-pixels of the image to a **pretrained model**'s classification.
- The attribution heatmap can be computed for any class. + The attribution heatmap can be computed for any class.

The _bwr (blue white red)_ attribution colormap assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, - and :red[**red**] color to positive values. + and :red[**red**] color to positive values.

+ +
Colormap

+
""", unsafe_allow_html=True + ), + st.text("") ) -st.image(str(data_directory / 'colormap.png'), width = 660) + +add_sidebar_logo() + +st.title('Explaining Image data classification') st.sidebar.header('Input data') @@ -68,7 +82,6 @@ st.markdown( """ - ************************************************************************************ This example demonstrates the use of DIANNA on explaining a [**binary MNIST model**](https://zenodo.org/records/5907177) pretrained on **only** images of the hand-written digits 0 and 1.
@@ -78,6 +91,7 @@ ) else: + description_explainer() st.info('Select an example in the left panel to coninue') st.stop() @@ -100,13 +114,17 @@ imagekey = 'Image_cb' if input_type is None: + description_explainer() st.info('Select which input type to use in the left panel to continue') st.stop() if not (image_file and image_model_file and image_label_file): + description_explainer() st.info('Add your input data in the left panel to continue') st.stop() +description_explainer("") + image, _ = open_image(image_file) model = load_model(image_model_file) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index a0cc19be..1c32ea3c 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -1,3 +1,4 @@ +import base64 import numpy as np import seaborn as sns import streamlit as st @@ -22,10 +23,13 @@ add_sidebar_logo() -st.title('Explaining Tabular data classification/regression') +def description_explainer(open='open'): + """Expandable text section with image.""" + return (st.markdown( + f""" +
+ Description of the explanation -st.markdown( - """ The explanation is visualised as a **relevance bar-chart** for the top (up to 10) most relevant _attributes (features)_.
The chart displays the relevance _attributions_ of the individual features of the tabular data @@ -35,10 +39,15 @@ The attribution colormap assigns :blue[**blue**] color to negative relevances, and :red[**red**] color to positive values. +
""", unsafe_allow_html=True + ), + st.text("") ) +st.title('Explaining Tabular data classification/regression') + st.sidebar.header('Input data') input_type = st.sidebar.radio( @@ -70,7 +79,6 @@ mode = 'regression' st.markdown( """ - ********************************************************************************** This example demonstrates the use of DIANNA on a pre-trained [regression model](https://zenodo.org/records/10580833) to predict tomorrow's sunshine hours based on meteorological data from today. @@ -94,7 +102,6 @@ st.markdown( """ - ********************************************************************************* This example demonstrates the use of DIANNA on a pre-trained [classification model](https://zenodo.org/records/10580743) to identify if a penguin belongs to one of three different species based on a number of measurable physical characteristics.
@@ -104,6 +111,7 @@ """, unsafe_allow_html=True) else: + description_explainer() st.info('Select an example in the left panel to coninue') st.stop() @@ -115,6 +123,7 @@ tabular_label_file = st.sidebar.file_uploader('Select labels in case of classification model', type='txt') if not (tabular_data_file and tabular_model_file and tabular_training_data_file): + description_explainer() st.info('Add your input data in the left panel to continue') st.stop() @@ -130,15 +139,17 @@ mode = 'regression' if input_type is None: + description_explainer() st.info('Select which input type to use in the left panel to continue') st.stop() +description_explainer("") + model = load_model(tabular_model_file) serialized_model = model.SerializeToString() choices = ('RISE', 'LIME', 'KernelSHAP') -st.text("") st.text("") # Get predictions and create parameter box diff --git a/dianna/dashboard/pages/Text.py b/dianna/dashboard/pages/Text.py index 9c677d4f..19b371e1 100644 --- a/dianna/dashboard/pages/Text.py +++ b/dianna/dashboard/pages/Text.py @@ -1,3 +1,4 @@ +import base64 import sys import streamlit as st from _model_utils import load_labels @@ -17,14 +18,19 @@ from importlib_resources import files else: from importlib.resources import files -data_directory = files('dianna.data') -add_sidebar_logo() +data_directory = files('dianna.data') +colormap_path = str(data_directory / 'colormap.png') +with open(colormap_path, "rb") as img_file: + colormap = base64.b64encode(img_file.read()).decode() -st.title('Explaining Textual data classification') +def description_explainer(open='open'): + """Expandable text section with image.""" + return (st.markdown( + f""" +
+ Description of the explanation -st.markdown( - """ The explanation is visualised as a **relevance heatmap** overlayed on top of the input text.
The heatmap consists of the relevance _attributions_ of all individual words of the text to a **pretrained model**'s classification.
@@ -33,11 +39,19 @@ The _bwr (blue white red)_ attribution colormap assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. + +
Colormap

+
""", unsafe_allow_html=True + ), + st.text("") ) -st.image(str(data_directory / 'colormap.png'), width = 660) +add_sidebar_logo() + +st.title('Explaining Textual data classification') + st.sidebar.header('Input data') input_type = st.sidebar.radio( @@ -66,18 +80,19 @@ st.markdown( """ - *********************************************************************** This example demonstrates the use of DIANNA on the [Stanford Sentiment Treebank dataset](https://nlp.stanford.edu/sentiment/index.html) which contains one-sentence movie reviews.
A pre-trained [neural network classifier](https://zenodo.org/record/5910598) is used, which classifies a movie review as positive or negative.
+
:blue-background[The input sentence which the model will classify can be modified in the editable Input text field in the left panel.] """, unsafe_allow_html=True ) else: + description_explainer() st.info('Select an example in the left panel to coninue') st.stop() @@ -95,13 +110,17 @@ type='txt') if input_type is None: + description_explainer() st.info('Select which input type to use in the left panel to continue') st.stop() if not (text_input and text_model_file and text_label_file): + description_explainer() st.info('Add your input data in the left panel to continue') st.stop() +description_explainer("") + model = load_model(text_model_file) serialized_model = model.SerializeToString() @@ -109,7 +128,6 @@ choices = ('RISE', 'LIME') -st.text("") st.text("") with st.container(border=True): diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index b6305441..9bcd0869 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -1,3 +1,4 @@ +import base64 import sys import numpy as np import streamlit as st @@ -20,12 +21,19 @@ from importlib_resources import files else: from importlib.resources import files + data_directory = files('dianna.data') +colormap_path = str(data_directory / 'colormap.png') +with open(colormap_path, "rb") as img_file: + colormap = base64.b64encode(img_file.read()).decode() -st.title('Explaining Time series data classification') +def description_explainer(open='open'): + """Expandable text section with image.""" + return (st.markdown( + f""" +
+ Description of the explanation -st.markdown( - """ The explanation is visualised as a **relevance heatmap** overlayed on top of the time series.
The heatmap consists of the relevance _attributions_ of all individual data points per time moment of the series to a **pretrained model**'s classification.
@@ -34,12 +42,19 @@ The _bwr (blue white red)_ attribution colormap assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. + +
Colormap

+
""", unsafe_allow_html=True + ), + st.text("") ) -st.image(str(data_directory / 'colormap.png'), width = 660) +st.title('Explaining Time series data classification') + add_sidebar_logo() + st.sidebar.header('Input data') input_type = st.sidebar.radio( @@ -71,7 +86,6 @@ st.markdown( """ - ****************************************************************** This example demonstrates the use of DIANNA on a pre-trained binary [classification model](https://zenodo.org/records/7543883) for season prediction.
The input data is the @@ -102,7 +116,6 @@ def preprocess(data): st.markdown( """ - ************************************************************ This example demonstrates the use of DIANNA on a pre-trained [binary model](https://zenodo.org/records/10656614) for classification of radio astronomical dynamic spectra, also known as frequency-time data.
@@ -114,6 +127,7 @@ def preprocess(data): unsafe_allow_html=True ) else: + description_explainer() st.info('Select an example in the left panel to coninue') st.stop() @@ -134,10 +148,12 @@ def preprocess(data): param_key = 'TS_cb' if input_type is None: + description_explainer() st.info('Select which input type to use in the left panel to continue') st.stop() if not (ts_data_file and ts_model_file and ts_label_file): + description_explainer() st.info('Add your input data in the left panel to continue') st.stop() @@ -146,6 +162,8 @@ def preprocess(data): # model explainer nor the model predictor ts_data_explainer = ts_data_predictor = open_timeseries(ts_data_file) +description_explainer("") + model = load_model(ts_model_file) serialized_model = model.SerializeToString() From 9f90af4dbef0eff253cdd6ce8035cdf69901916a Mon Sep 17 00:00:00 2001 From: Laura <95367983+laurasootes@users.noreply.github.com> Date: Wed, 2 Oct 2024 22:59:31 +0200 Subject: [PATCH 44/52] remove unused import --- dianna/dashboard/pages/Tabular.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index 1c32ea3c..bba9ca18 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -1,4 +1,3 @@ -import base64 import numpy as np import seaborn as sns import streamlit as st From e09520eb408847ff8bcd4d6c2dd58b04c11b38d9 Mon Sep 17 00:00:00 2001 From: Laura <95367983+laurasootes@users.noreply.github.com> Date: Thu, 3 Oct 2024 08:23:05 +0200 Subject: [PATCH 45/52] fix linting --- dianna/dashboard/pages/Images.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index 9d9a0427..fbb45af0 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -18,7 +18,7 @@ from importlib_resources import files else: from importlib.resources import files - + data_directory = files('dianna.data') colormap_path = str(data_directory / 'colormap.png') with open(colormap_path, "rb") as img_file: From d9d7cd61b3d12c358d8ccd3a073b4d25019e1cae Mon Sep 17 00:00:00 2001 From: Laura Ootes Date: Thu, 3 Oct 2024 11:59:30 +0200 Subject: [PATCH 46/52] move description of explanation above description of example --- dianna/dashboard/pages/Images.py | 15 ++++++++------- dianna/dashboard/pages/Tabular.py | 7 ++++--- dianna/dashboard/pages/Text.py | 14 +++++++------- dianna/dashboard/pages/Time_series.py | 18 +++++++++--------- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index fbb45af0..074222f4 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -80,6 +80,7 @@ def description_explainer(open='open'): imagekey = 'Digits_Image_cb' + description_explainer("") st.markdown( """ This example demonstrates the use of DIANNA on explaining a @@ -113,18 +114,18 @@ def description_explainer(open='open'): imagekey = 'Image_cb' + if not (image_file and image_model_file and image_label_file): + description_explainer() + st.info('Add your input data in the left panel to continue') + st.stop() + else: + description_explainer("") + if input_type is None: description_explainer() st.info('Select which input type to use in the left panel to continue') st.stop() -if not (image_file and image_model_file and image_label_file): - description_explainer() - st.info('Add your input data in the left panel to continue') - st.stop() - -description_explainer("") - image, _ = open_image(image_file) model = load_model(image_model_file) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index bba9ca18..ad74558a 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -76,6 +76,7 @@ def description_explainer(open='open'): labels = None mode = 'regression' + description_explainer("") st.markdown( """ This example demonstrates the use of DIANNA on a pre-trained [regression @@ -98,7 +99,7 @@ def description_explainer(open='open'): training_data, data = load_penguins(data_penguins) mode = 'classification' - + description_explainer("") st.markdown( """ This example demonstrates the use of DIANNA on a pre-trained [classification @@ -125,6 +126,8 @@ def description_explainer(open='open'): description_explainer() st.info('Add your input data in the left panel to continue') st.stop() + else: + description_explainer("") data = load_data(tabular_data_file) model = load_model(tabular_model_file) @@ -142,8 +145,6 @@ def description_explainer(open='open'): st.info('Select which input type to use in the left panel to continue') st.stop() -description_explainer("") - model = load_model(tabular_model_file) serialized_model = model.SerializeToString() diff --git a/dianna/dashboard/pages/Text.py b/dianna/dashboard/pages/Text.py index 19b371e1..c8e76195 100644 --- a/dianna/dashboard/pages/Text.py +++ b/dianna/dashboard/pages/Text.py @@ -78,6 +78,7 @@ def description_explainer(open='open'): text_model_file = download('movie_review_model.onnx', 'model') text_label_file = download('labels_text.txt', 'label') + description_explainer("") st.markdown( """ This example demonstrates the use of DIANNA on the [Stanford Sentiment @@ -108,19 +109,18 @@ def description_explainer(open='open'): text_label_file = st.sidebar.file_uploader('Select labels', type='txt') + if not (text_input and text_model_file and text_label_file): + description_explainer() + st.info('Add your input data in the left panel to continue') + st.stop() + else: + description_explainer("") if input_type is None: description_explainer() st.info('Select which input type to use in the left panel to continue') st.stop() -if not (text_input and text_model_file and text_label_file): - description_explainer() - st.info('Add your input data in the left panel to continue') - st.stop() - -description_explainer("") - model = load_model(text_model_file) serialized_model = model.SerializeToString() diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index 9bcd0869..2499eeda 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -83,7 +83,7 @@ def description_explainer(open='open'): ts_label_file = download('weather_data_labels.txt', 'label') param_key = 'Weather_TS_cb' - + description_explainer("") st.markdown( """ This example demonstrates the use of DIANNA @@ -113,7 +113,7 @@ def preprocess(data): ts_data_predictor = ts_data[None, ..., None] param_key = 'FRB_TS_cb' - + description_explainer("") st.markdown( """ This example demonstrates the use of DIANNA @@ -147,23 +147,23 @@ def preprocess(data): param_key = 'TS_cb' + if not (ts_data_file and ts_model_file and ts_label_file): + description_explainer() + st.info('Add your input data in the left panel to continue') + st.stop() + else: + description_explainer("") + if input_type is None: description_explainer() st.info('Select which input type to use in the left panel to continue') st.stop() -if not (ts_data_file and ts_model_file and ts_label_file): - description_explainer() - st.info('Add your input data in the left panel to continue') - st.stop() - if load_example != "Scientific case - radio astronomy: Fast Radio Burst (FRB) detection": # For normal cases, the input data does not need transformation for either the # model explainer nor the model predictor ts_data_explainer = ts_data_predictor = open_timeseries(ts_data_file) -description_explainer("") - model = load_model(ts_model_file) serialized_model = model.SerializeToString() From b178f073cc3fcdbeecbb231bfa318982c4ebba5d Mon Sep 17 00:00:00 2001 From: Laura Ootes Date: Thu, 3 Oct 2024 12:15:03 +0200 Subject: [PATCH 47/52] remove centering --- dianna/dashboard/pages/Images.py | 2 +- dianna/dashboard/pages/Text.py | 2 +- dianna/dashboard/pages/Time_series.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index 074222f4..0e14f4ed 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -40,7 +40,7 @@ def description_explainer(open='open'): assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values.

-
Colormap

+ Colormap
""", unsafe_allow_html=True diff --git a/dianna/dashboard/pages/Text.py b/dianna/dashboard/pages/Text.py index c8e76195..eb7ba08e 100644 --- a/dianna/dashboard/pages/Text.py +++ b/dianna/dashboard/pages/Text.py @@ -40,7 +40,7 @@ def description_explainer(open='open'): assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. -
Colormap

+ Colormap
""", unsafe_allow_html=True diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index 2499eeda..5c8215f1 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -43,7 +43,7 @@ def description_explainer(open='open'): assigns :blue[**blue**] color to negative relevances, **white** color to near-zero values, and :red[**red**] color to positive values. -
Colormap

+ Colormap
""", unsafe_allow_html=True From e0692337b13177e8218149a584e3d9fd2035bbc0 Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Thu, 3 Oct 2024 13:49:07 +0200 Subject: [PATCH 48/52] Update Images.py separating line between description explanation and the example text for images --- dianna/dashboard/pages/Images.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dianna/dashboard/pages/Images.py b/dianna/dashboard/pages/Images.py index 0e14f4ed..127effe3 100644 --- a/dianna/dashboard/pages/Images.py +++ b/dianna/dashboard/pages/Images.py @@ -83,6 +83,7 @@ def description_explainer(open='open'): description_explainer("") st.markdown( """ + ********************************************************************************************* This example demonstrates the use of DIANNA on explaining a [**binary MNIST model**](https://zenodo.org/records/5907177) pretrained on **only** images of the hand-written digits 0 and 1.
From 025ff48511b2294d53bc2defb35c2ab030f77b9a Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Thu, 3 Oct 2024 13:50:39 +0200 Subject: [PATCH 49/52] Update Tabular.py separating line between description explanation and the example text Tabular --- dianna/dashboard/pages/Tabular.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index ad74558a..3522615e 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -79,6 +79,7 @@ def description_explainer(open='open'): description_explainer("") st.markdown( """ + ***************************************************************************** This example demonstrates the use of DIANNA on a pre-trained [regression model](https://zenodo.org/records/10580833) to predict tomorrow's sunshine hours based on meteorological data from today. From 684a7f0020746cbea5596722726f55421032101c Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Thu, 3 Oct 2024 13:51:25 +0200 Subject: [PATCH 50/52] Update Text.py separating line between description explanation and the example text: Text --- dianna/dashboard/pages/Text.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dianna/dashboard/pages/Text.py b/dianna/dashboard/pages/Text.py index eb7ba08e..298de9d1 100644 --- a/dianna/dashboard/pages/Text.py +++ b/dianna/dashboard/pages/Text.py @@ -81,6 +81,7 @@ def description_explainer(open='open'): description_explainer("") st.markdown( """ + ********************************************************************** This example demonstrates the use of DIANNA on the [Stanford Sentiment Treebank dataset](https://nlp.stanford.edu/sentiment/index.html) which contains one-sentence movie reviews.
A pre-trained [neural network From 15791227d4f5a9b02ec6ac200fc35b240a5d2d3e Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Thu, 3 Oct 2024 13:52:09 +0200 Subject: [PATCH 51/52] Update Tabular.py separating line between description explanation and the example text: Tabular classification --- dianna/dashboard/pages/Tabular.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dianna/dashboard/pages/Tabular.py b/dianna/dashboard/pages/Tabular.py index 3522615e..ca8e3210 100644 --- a/dianna/dashboard/pages/Tabular.py +++ b/dianna/dashboard/pages/Tabular.py @@ -103,6 +103,7 @@ def description_explainer(open='open'): description_explainer("") st.markdown( """ + **************************************************************************** This example demonstrates the use of DIANNA on a pre-trained [classification model](https://zenodo.org/records/10580743) to identify if a penguin belongs to one of three different species based on a number of measurable physical characteristics.
From 3bd155fbc8934314a20e903d58e0e041342ac1d8 Mon Sep 17 00:00:00 2001 From: Elena Ranguelova Date: Thu, 3 Oct 2024 13:53:03 +0200 Subject: [PATCH 52/52] Update Time_series.py separating line between description explanation and the example text: Time series --- dianna/dashboard/pages/Time_series.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dianna/dashboard/pages/Time_series.py b/dianna/dashboard/pages/Time_series.py index 5c8215f1..0fda2a26 100644 --- a/dianna/dashboard/pages/Time_series.py +++ b/dianna/dashboard/pages/Time_series.py @@ -86,6 +86,7 @@ def description_explainer(open='open'): description_explainer("") st.markdown( """ + ******************************************************************************************* This example demonstrates the use of DIANNA on a pre-trained binary [classification model](https://zenodo.org/records/7543883) for season prediction.
The input data is the @@ -116,6 +117,7 @@ def preprocess(data): description_explainer("") st.markdown( """ + ************************************************************************************************ This example demonstrates the use of DIANNA on a pre-trained [binary model](https://zenodo.org/records/10656614) for classification of radio astronomical dynamic spectra, also known as frequency-time data.