Skip to content

Commit

Permalink
Update user guide (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
GalyaZalesskaya authored Jul 10, 2024
1 parent 72ae2c7 commit 2ce8fb1
Showing 1 changed file with 133 additions and 86 deletions.
219 changes: 133 additions & 86 deletions docs/source/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,31 @@ OpenVINO XAI API documentation can be found [here](https://openvinotoolkit.githu

Content:

- Architecture
- Explainer
- Basic usage: Auto mode
- White-Box mode
- Black-Box mode
- XAI insertion
- Example scripts
- [OpenVINO™ Explainable AI Toolkit User Guide](#openvino-explainable-ai-toolkit-user-guide)
- [OpenVINO XAI Architecture](#openvino-xai-architecture)
- [Explainer - interface to XAI algorithms](#explainer---interface-to-xai-algorithms)
- [Basic usage: Auto mode](#basic-usage-auto-mode)
- [Running without `preprocess_fn`](#running-without-preprocess_fn)
- [Specifying `preprocess_fn`](#specifying-preprocess_fn)
- [White-Box mode](#white-box-mode)
- [Black-Box mode](#black-box-mode)
- [XAI insertion (white-box usage)](#xai-insertion-white-box-usage)
- [Example scripts](#example-scripts)


## OpenVINO XAI Architecture

![OpenVINO XAI Architecture](_static/ovxai-architecture.svg)

OpenVINO XAI provides the API to explain models, using two types of methods:

- **White-box** - treats the model as a white box, making inner modifications and adding an extra XAI branch. This results in additional output from the model and relatively fast explanations.
- **Black-box** - treats the model as a black box, working on a wide range of models. However, it requires many more inference runs.

## Explainer - interface to XAI algorithms

In a nutshell, the explanation call looks like this:

```python
import openvino_xai as xai

Expand All @@ -39,206 +49,243 @@ explanation = explainer(data, explanation_parameters)

## Basic usage: Auto mode

Under the hood of the auto mode: will try to run white-box mode, if fails => will run black-box mode.
The easiest way to run the explainer is in Auto mode. Under the hood, Auto mode will try to run in `White-Box` mode first. If it fails, it will run in `Black-Box` mode.

Learn more details about [White-Box](#white-box-mode) and [Black-Box](#black-box-mode) modes below.

![Auto mode process](_static/auto_explain_mode.jpg)

See more details about white_box and black-box modes below.
Generating saliency maps involves model inference. Explainer will perform model inference but to do it, it requires `preprocess_fn` and `postprocess_fn`.

### Running without `preprocess_fn`

Here's the example how we can avoid passing `preprocess_fn` by preprocessing data beforehand (like resizing and adding a batch dimension).

```python
import cv2
import numpy as np
import openvino.runtime as ov
from openvino.runtime.utils.data_helpers.wrappers import OVDict

import openvino_xai as xai


def postprocess_fn(x: OVDict):
# Implementing our own post-process function based on the model's implementation
# Return "logits" model output
return x["logits"]

# Create ov.Model
model = ov.Core().read_model("path/to/model.xml") # type: ov.Model

# Explainer object will prepare and load the model once in the beginning
explainer = xai.Explainer(
model,
task=xai.Task.CLASSIFICATION,
postprocess_fn=postprocess_fn,
)

# Generate and process saliency maps (as many as required, sequentially)
image = cv2.imread("path/to/image.jpg")

# Pre-process the image as the model requires (resizing and adding a batch dimension)
preprocessed_image = cv2.resize(src=image, dsize=(224, 224))
preprocessed_image = np.expand_dims(preprocessed_image, 0)

# Run explanation
explanation = explainer(
preprocessed_image,
target_explain_labels=[11, 14], # indices or string labels to explain
overlay=True, # False by default
original_input_image=image, # to apply overlay on the original image instead of the preprocessed one that was used for the explainer
)

# Save saliency maps
explanation.save("output_path", "name")
```

### Specifying `preprocess_fn`

Generating saliency maps involves model inference. Explainer will perform model inference.
To infer, `preprocess_fn` and `postprocess_fn` are requested from the user, depending on the usage mode.

```python
import cv2
import numpy as np
import openvino.runtime as ov
from openvino.runtime.utils.data_helpers.wrappers import OVDict

import openvino_xai as xai
from openvino_xai.explainer.explanation_parameters import ExplanationParameters


def preprocess_fn(x: np.ndarray) -> np.ndarray:
# Implementing own pre-process function based on model's implementation
# Implementing our own pre-process function based on the model's implementation
x = cv2.resize(src=x, dsize=(224, 224))
x = np.expand_dims(x, 0)
return x

def postprocess_fn(x: OVDict):
# Implementing our own post-process function based on the model's implementation
# Return "logits" model output
return x["logits"]

# Creating model
# Create ov.Model
model = ov.Core().read_model("path/to/model.xml") # type: ov.Model

# Explainer object will prepare and load the model once in the beginning
# The Explainer object will prepare and load the model once in the beginning
explainer = xai.Explainer(
model,
task=xai.Task.CLASSIFICATION,
preprocess_fn=preprocess_fn,
postprocess_fn=postprocess_fn,
)

# Generate and process saliency maps (as many as required, sequentially)
image = cv2.imread("path/to/image.jpg")
explanation_parameters = ExplanationParameters(

# Run explanation
explanation = explainer(
image,
target_explain_labels=[11, 14], # indices or string labels to explain
)
explanation = explainer(image, explanation_parameters)

# Saving saliency maps
# Save saliency maps
explanation.save("output_path", "name")
```


## White-Box mode

White-box mode is a two-step process that includes OV model update and further inference of the updated model.
White-box mode involves two steps: updating the OV model and then running the updated model.

Updated model has additional XAI branch inserted. XAI branch generates saliency maps during model inference. Saliency maps extend the list of model outputs, i.e. saliency maps are generated along with the original model outputs. Depending on the white-box algorithm, computational overhead of inserted XAI branch may vary, but it is usually relatively modest.
The updated model has an extra XAI branch resulting in an additional `saliency_map` output. This XAI branch creates saliency maps during the model's inference. The computational load from the XAI branch varies depending on the white-box algorithm, but it's usually quite small.

`preprocess_fn` is required to be provided by the user for the white-box mode.
You need to pass either `preprocess_fn` or already preprocessed images to run the explainer in white-box mode.

```python
import cv2
import numpy as np
import openvino.runtime as ov

import openvino_xai as xai
from openvino_xai.explainer.parameters import ExplainMode, ExplanationParameters, TargetExplainGroup, VisualizationParameters
from openvino_xai.inserter.parameters import ClassificationInsertionParameters
from openvino_xai.explainer import ExplainMode


def preprocess_fn(x: np.ndarray) -> np.ndarray:
# Implementing own pre-process function based on model's implementation
# Implementing own pre-process function based on the model's implementation
x = cv2.resize(src=x, dsize=(224, 224))
x = np.expand_dims(x, 0)
return x


# Creating model
# Create ov.Model
model = ov.Core().read_model("path/to/model.xml") # type: ov.Model

# Optional - create insertion parameters
insertion_parameters = ClassificationInsertionParameters(
# target_layer="last_conv_node_name", # target_layer - node after which XAI branch will be inserted
embed_scaling=True, # True by default. If set to True, saliency map scale (0 ~ 255) operation is embedded in the model
explain_method=xai.Method.RECIPROCAM, # ReciproCAM is the default XAI method for CNNs
)

# Explainer object will prepare and load the model once in the beginning
# The Explainer object will prepare and load the model once at the beginning
explainer = xai.Explainer(
model,
task=xai.Task.CLASSIFICATION,
preprocess_fn=preprocess_fn,
explain_mode=ExplainMode.WHITEBOX,
insertion_parameters=insertion_parameters,
)

# Generate and process saliency maps (as many as required, sequentially)
image = cv2.imread("path/to/image.jpg")
voc_labels = ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable',
'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor']
explanation_parameters = ExplanationParameters(
target_explain_group=TargetExplainGroup.CUSTOM,
target_explain_labels=[11, 14], # target classes to explain, also ['dog', 'person'] is a valid input

voc_labels = [
'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable',
'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'
]

# Run explanation
explanation = explainer(
image,
explain_mode=ExplainMode.WHITEBOX,
# target_layer="last_conv_node_name", # target_layer - node after which the XAI branch will be inserted, usually the last convolutional layer in the backbone
embed_scaling=True, # True by default. If set to True, the saliency map scale (0 ~ 255) operation is embedded in the model
explain_method=xai.Method.RECIPROCAM, # ReciproCAM is the default XAI method for CNNs
label_names=voc_labels,
visualization_parameters=VisualizationParameters(overlay=True), # by default, saliency map overlay over image
target_explain_labels=[11, 14], # target classes to explain, also ['dog', 'person'] is a valid input, since label_names are provided
overlay=True, # False by default
)
explanation = explainer(image, explanation_parameters)

# Saving saliency maps
# Save saliency maps
explanation.save("output_path", "name")
```


## Black-Box mode

Black-box mode does not update the model (treating model as a black-box).
Black-box mode does not update the model (treating the model as a black box).
Black-box approaches are based on the perturbation of the input data and measurement of the model's output change.
The process is repeated many times, which requires hundreds or thousands of forward passes
and introduces significant computational overhead.

`preprocess_fn` and `postprocess_fn` are required to be provided by the user for the black-box mode.
The process is repeated many times, requiring hundreds or thousands of forward passes and introducing **significant computational overhead**.

`preprocess_fn` (or preprocessed images) and `postprocess_fn` are required to be provided by the user for black-box mode.

```python
import cv2
import numpy as np
import openvino.runtime as ov
from openvino.runtime.utils.data_helpers.wrappers import OVDict

import openvino_xai as xai
from openvino_xai.explainer.explanation_parameters import ExplainMode, ExplanationParameters
from openvino_xai.explainer import ExplainMode


def preprocess_fn(x: np.ndarray) -> np.ndarray:
# Implementing own pre-process function based on model's implementation
# Implementing our own pre-process function based on the model's implementation
x = cv2.resize(src=x, dsize=(224, 224))
x = np.expand_dims(x, 0)
return x


def postprocess_fn(x: OVDict):
# Implementing own post-process function based on model's implementation
# Output logits
return x["logits"]


# Creating model
# Create ov.Model
model = ov.Core().read_model("path/to/model.xml") # type: ov.Model

# Explainer object will prepare and load the model once in the beginning
# The Explainer object will prepare and load the model once at the beginning
explainer = xai.Explainer(
model,
task=xai.Task.CLASSIFICATION,
preprocess_fn=preprocess_fn,
postprocess_fn=postprocess_fn,
explain_mode=ExplainMode.BLACKBOX,
)

# Generate and process saliency maps (as many as required, sequentially)
image = cv2.imread("path/to/image.jpg")
explanation_parameters = ExplanationParameters(
target_explain_labels=[11, 14], # indices or string labels to explain
)

# Run explanation
explanation = explainer(
image,
explanation_parameters,
num_masks=1000, # kwargs of the RISE algo
explain_mode=ExplainMode.BLACKBOX,
target_explain_labels=[11, 14], # target classes to explain
# target_explain_labels=-1, # explain all classes
overlay=True, # False by default
num_masks=1000, # kwargs for the RISE algorithm
)

# Saving saliency maps
# Save saliency maps
explanation.save("output_path", "name")

```


## XAI insertion (white-box usage)

As mentioned above, saliency map generation requires model inference.
In the above use cases, OVXAI performs model inference using provided processing functions.
Alternative approach is to use OVXAI just to insert XAI branch into the model and infer it in the original pipeline.
As mentioned above, saliency map generation requires model inference. In the above use cases, OpenVINO XAI performs model inference using provided processing functions. An alternative approach is to use XAI to insert the XAI branch into the model and infer it in the original pipeline.

`insert_xai()` API is used for insertion.

Note: original model outputs are not affected and the model should be inferable by the original inference pipeline.
**Note**: The original model outputs are not affected, and the model should be inferable by the original inference pipeline.

```python
import openvino.runtime as ov

import openvino_xai as xai
from openvino_xai.inserter.parameters import ClassificationInsertionParameters


# Creating model
# Create an ov.Model
model = ov.Core().read_model("path/to/model.xml") # type: ov.Model

# Optional - create insertion parameters
insertion_parameters = ClassificationInsertionParameters(
# target_layer="last_conv_node_name", # target_layer - node after which XAI branch will be inserted
embed_scaling=True, # True by default. If set to True, saliency map scale (0 ~ 255) operation is embedded in the model
explain_method=xai.Method.RECIPROCAM, # ReciproCAM is the default XAI method for CNNs
)

# Inserting XAI branch into the model graph
# Insert XAI branch into the model graph
model_xai = xai.insert_xai(
model=model,
task=xai.Task.CLASSIFICATION,
insertion_parameters=insertion_parameters,
# target_layer="last_conv_node_name", # target_layer - the node after which the XAI branch will be inserted, usually the last convolutional layer in the backbone
embed_scaling=True, # True by default. If set to True, the saliency map scale (0 ~ 255) operation is embedded in the model
explain_method=xai.Method.RECIPROCAM, # ReciproCAM is the default XAI method for CNNs
) # type: ov.Model

# ***** Downstream task: user's code that infers model_xai and picks 'saliency_map' output *****
Expand All @@ -247,7 +294,7 @@ model_xai = xai.insert_xai(

## Example scripts

More usage scenarios are available in [examples](../../examples).
More usage scenarios that can be used with your own models and images as arguments are available in [examples](../../examples).

```python
# Retrieve models by running tests
Expand All @@ -256,6 +303,6 @@ pytest tests/test_classification.py

# Run a bunch of classification examples
# All outputs will be stored in the corresponding output directory
python examples/run_classification.py .data/otx_models/mlc_mobilenetv3_large_voc.xml \
python examples/run_classification.py .data/otx_models/mlc_mobilenetv3_large_voc.xml
tests/assets/cheetah_person.jpg --output output
```

0 comments on commit 2ce8fb1

Please sign in to comment.