mirror of
https://github.com/gradio-app/gradio.git
synced 2025-03-07 11:46:51 +08:00
Final typing: interpretation.py
and helpers.py
(#2911)
* started pathlib * blocks.py * more changes * fixes * typing * formatting * typing * renaming files * changelog * script * changelog * lint * routes * renamed * state * formatting * state * type check script * remove strictness * switched to pyright * switched to pyright * fixed flaky tests * fixed test xray * fixed load test * fixed blocks tests * formatting * fixed components test * uncomment tests * fixed interpretation tests * formatting * last tests hopefully * argh lint * component * fixed based on review * refactor * components.py t yping * components.py * formatting * lint script * merge * merge * lint * pathlib * lint * events too * lint script * fixing tests * lint * examples * serializing * more files * formatting * flagging.py * added to lint script * fixed tab * interface.py * attempt fix * refactoring interface * interface refactor * formatting * fix for live interfaces * lint * refactor interpret * more typing * undo blocks change * undo test comp change * created classes * interpretation * fix * final fixes to interpretation * type check * helpers * type hintg * label * initialize interpret * fixed progress bar * removed unnecessary assertion
This commit is contained in:
parent
ad42e80dca
commit
c02001da7d
@ -82,5 +82,7 @@ from gradio.templates import (
|
||||
Webcam,
|
||||
)
|
||||
|
||||
current_pkg_version = pkgutil.get_data(__name__, "version.txt").decode("ascii").strip()
|
||||
current_pkg_version = (
|
||||
(pkgutil.get_data(__name__, "version.txt") or b"").decode("ascii").strip()
|
||||
)
|
||||
__version__ = current_pkg_version
|
||||
|
@ -46,6 +46,7 @@ from gradio.events import (
|
||||
Submittable,
|
||||
Uploadable,
|
||||
)
|
||||
from gradio.interpretation import NeighborInterpretable, TokenInterpretable
|
||||
from gradio.layouts import Column, Form, Row
|
||||
from gradio.processing_utils import TempFileManager
|
||||
from gradio.serializing import (
|
||||
@ -186,8 +187,6 @@ class IOComponent(Component, Serializable):
|
||||
if callable(load_fn):
|
||||
self.load_event = self.attach_load_event(load_fn, every)
|
||||
|
||||
self.set_interpret_parameters()
|
||||
|
||||
def get_config(self):
|
||||
return {
|
||||
"label": self.label,
|
||||
@ -196,38 +195,6 @@ class IOComponent(Component, Serializable):
|
||||
**super().get_config(),
|
||||
}
|
||||
|
||||
def set_interpret_parameters(self):
|
||||
"""
|
||||
Set any parameters for interpretation.
|
||||
"""
|
||||
return self
|
||||
|
||||
def get_interpretation_neighbors(self, x: Any) -> Tuple[List, Dict, bool]:
|
||||
"""
|
||||
Generates values similar to input to be used to interpret the significance of the input in the final output.
|
||||
Parameters:
|
||||
x: Input to interface
|
||||
Returns: (neighbor_values, interpret_kwargs, interpret_by_removal)
|
||||
neighbor_values: Neighboring values to input x to compute for interpretation
|
||||
interpret_kwargs: Keyword arguments to be passed to get_interpretation_scores
|
||||
interpret_by_removal: If True, returned neighbors are values where the interpreted subsection was removed. If False, returned neighbors are values where the interpreted subsection was modified to a different value.
|
||||
"""
|
||||
return [], {}, True
|
||||
|
||||
def get_interpretation_scores(
|
||||
self, x: Any, neighbors: List[Any], scores: List[float], **kwargs
|
||||
) -> List:
|
||||
"""
|
||||
Arrange the output values from the neighbors into interpretation scores for the interface to render.
|
||||
Parameters:
|
||||
x: Input to interface
|
||||
neighbors: Neighboring values to input x used for interpretation.
|
||||
scores: Output value corresponding to each neighbor in neighbors
|
||||
Returns:
|
||||
Arrangement of interpretation scores for interfaces to render.
|
||||
"""
|
||||
return []
|
||||
|
||||
def generate_sample(self) -> Any:
|
||||
"""
|
||||
Returns a sample value of the input that would be accepted by the api. Used for api documentation.
|
||||
@ -275,7 +242,13 @@ class FormComponent:
|
||||
|
||||
@document("change", "submit", "blur", "style")
|
||||
class Textbox(
|
||||
FormComponent, Changeable, Submittable, Blurrable, IOComponent, SimpleSerializable
|
||||
FormComponent,
|
||||
Changeable,
|
||||
Submittable,
|
||||
Blurrable,
|
||||
IOComponent,
|
||||
SimpleSerializable,
|
||||
TokenInterpretable,
|
||||
):
|
||||
"""
|
||||
Creates a textarea for user to enter string input or display string output.
|
||||
@ -324,7 +297,6 @@ class Textbox(
|
||||
self.lines = lines
|
||||
self.max_lines = max_lines if type == "text" else 1
|
||||
self.placeholder = placeholder
|
||||
self.interpret_by_tokens = True
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -336,6 +308,7 @@ class Textbox(
|
||||
value=value,
|
||||
**kwargs,
|
||||
)
|
||||
TokenInterpretable.__init__(self)
|
||||
self.cleared_value = ""
|
||||
self.test_input = value
|
||||
self.type = type
|
||||
@ -456,7 +429,13 @@ class Textbox(
|
||||
|
||||
@document("change", "submit", "style")
|
||||
class Number(
|
||||
FormComponent, Changeable, Submittable, Blurrable, IOComponent, SimpleSerializable
|
||||
FormComponent,
|
||||
Changeable,
|
||||
Submittable,
|
||||
Blurrable,
|
||||
IOComponent,
|
||||
SimpleSerializable,
|
||||
NeighborInterpretable,
|
||||
):
|
||||
"""
|
||||
Creates a numeric field for user to enter numbers as input or display numeric output.
|
||||
@ -492,7 +471,6 @@ class Number(
|
||||
precision: Precision to round input/output to. If set to 0, will round to nearest integer and covert type to int. If None, no rounding happens.
|
||||
"""
|
||||
self.precision = precision
|
||||
self.interpret_by_tokens = False
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -504,6 +482,7 @@ class Number(
|
||||
value=value,
|
||||
**kwargs,
|
||||
)
|
||||
NeighborInterpretable.__init__(self)
|
||||
self.test_input = self.value if self.value is not None else 1
|
||||
|
||||
@staticmethod
|
||||
@ -627,7 +606,9 @@ class Number(
|
||||
|
||||
|
||||
@document("change", "style")
|
||||
class Slider(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
class Slider(
|
||||
FormComponent, Changeable, IOComponent, SimpleSerializable, NeighborInterpretable
|
||||
):
|
||||
"""
|
||||
Creates a slider that ranges from `minimum` to `maximum` with a step size of `step`.
|
||||
Preprocessing: passes slider value as a {float} into the function.
|
||||
@ -689,9 +670,9 @@ class Slider(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
value=value,
|
||||
**kwargs,
|
||||
)
|
||||
NeighborInterpretable.__init__(self)
|
||||
self.cleared_value = self.value
|
||||
self.test_input = self.value
|
||||
self.interpret_by_tokens = False
|
||||
|
||||
def get_config(self):
|
||||
return {
|
||||
@ -764,15 +745,6 @@ class Slider(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
{},
|
||||
)
|
||||
|
||||
def get_interpretation_scores(
|
||||
self, x, neighbors, scores: List[float], **kwargs
|
||||
) -> List[float]:
|
||||
"""
|
||||
Returns:
|
||||
Each value represents the score corresponding to an evenly spaced range of inputs between the minimum and maximum slider values.
|
||||
"""
|
||||
return scores
|
||||
|
||||
def style(
|
||||
self,
|
||||
*,
|
||||
@ -790,7 +762,9 @@ class Slider(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
|
||||
|
||||
@document("change", "style")
|
||||
class Checkbox(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
class Checkbox(
|
||||
FormComponent, Changeable, IOComponent, SimpleSerializable, NeighborInterpretable
|
||||
):
|
||||
"""
|
||||
Creates a checkbox that can be set to `True` or `False`.
|
||||
|
||||
@ -823,7 +797,6 @@ class Checkbox(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
"""
|
||||
self.test_input = True
|
||||
self.interpret_by_tokens = False
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -835,6 +808,7 @@ class Checkbox(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
value=value,
|
||||
**kwargs,
|
||||
)
|
||||
NeighborInterpretable.__init__(self)
|
||||
|
||||
def get_config(self):
|
||||
return {
|
||||
@ -863,12 +837,6 @@ class Checkbox(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
def generate_sample(self):
|
||||
return True
|
||||
|
||||
def set_interpret_parameters(self):
|
||||
"""
|
||||
Calculates interpretation score of the input by comparing the output against the output when the input is the inverse boolean value of x.
|
||||
"""
|
||||
return self
|
||||
|
||||
def get_interpretation_neighbors(self, x):
|
||||
return [not x], {}
|
||||
|
||||
@ -884,7 +852,9 @@ class Checkbox(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
|
||||
|
||||
@document("change", "style")
|
||||
class CheckboxGroup(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
class CheckboxGroup(
|
||||
FormComponent, Changeable, IOComponent, SimpleSerializable, NeighborInterpretable
|
||||
):
|
||||
"""
|
||||
Creates a set of checkboxes of which a subset can be checked.
|
||||
Preprocessing: passes the list of checked checkboxes as a {List[str]} or their indices as a {List[int]} into the function, depending on `type`.
|
||||
@ -928,7 +898,6 @@ class CheckboxGroup(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
)
|
||||
self.type = type
|
||||
self.test_input = self.choices
|
||||
self.interpret_by_tokens = False
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -940,6 +909,7 @@ class CheckboxGroup(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
value=value,
|
||||
**kwargs,
|
||||
)
|
||||
NeighborInterpretable.__init__(self)
|
||||
|
||||
def get_config(self):
|
||||
return {
|
||||
@ -1006,12 +976,6 @@ class CheckboxGroup(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
y = [y]
|
||||
return y
|
||||
|
||||
def set_interpret_parameters(self):
|
||||
"""
|
||||
Calculates interpretation score of each choice in the input by comparing the output against the outputs when each choice in the input is independently either removed or added.
|
||||
"""
|
||||
return self
|
||||
|
||||
def get_interpretation_neighbors(self, x):
|
||||
leave_one_out_sets = []
|
||||
for choice in self.choices:
|
||||
@ -1057,7 +1021,9 @@ class CheckboxGroup(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
|
||||
|
||||
@document("change", "style")
|
||||
class Radio(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
class Radio(
|
||||
FormComponent, Changeable, IOComponent, SimpleSerializable, NeighborInterpretable
|
||||
):
|
||||
"""
|
||||
Creates a set of radio buttons of which only one can be selected.
|
||||
Preprocessing: passes the value of the selected radio button as a {str} or its index as an {int} into the function, depending on `type`.
|
||||
@ -1101,7 +1067,6 @@ class Radio(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
)
|
||||
self.type = type
|
||||
self.test_input = self.choices[0] if len(self.choices) else None
|
||||
self.interpret_by_tokens = False
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -1113,6 +1078,7 @@ class Radio(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
value=value,
|
||||
**kwargs,
|
||||
)
|
||||
NeighborInterpretable.__init__(self)
|
||||
self.cleared_value = self.value
|
||||
|
||||
def get_config(self):
|
||||
@ -1166,12 +1132,6 @@ class Radio(FormComponent, Changeable, IOComponent, SimpleSerializable):
|
||||
+ ". Please choose from: 'value', 'index'."
|
||||
)
|
||||
|
||||
def set_interpret_parameters(self):
|
||||
"""
|
||||
Calculates interpretation score of each choice by comparing the output against each of the outputs when alternative choices are selected.
|
||||
"""
|
||||
return self
|
||||
|
||||
def get_interpretation_neighbors(self, x):
|
||||
choices = list(self.choices)
|
||||
choices.remove(x)
|
||||
@ -1274,6 +1234,7 @@ class Image(
|
||||
Uploadable,
|
||||
IOComponent,
|
||||
ImgSerializable,
|
||||
TokenInterpretable,
|
||||
):
|
||||
"""
|
||||
Creates an image component that can be used to upload/draw images (as an input) or display images (as an output).
|
||||
@ -1343,7 +1304,6 @@ class Image(
|
||||
self.tool = tool
|
||||
self.invert_colors = invert_colors
|
||||
self.test_input = deepcopy(media_data.BASE64_IMAGE)
|
||||
self.interpret_by_tokens = True
|
||||
self.streaming = streaming
|
||||
if streaming and source != "webcam":
|
||||
raise ValueError("Image streaming only available if source is 'webcam'.")
|
||||
@ -1359,6 +1319,7 @@ class Image(
|
||||
value=value,
|
||||
**kwargs,
|
||||
)
|
||||
TokenInterpretable.__init__(self)
|
||||
|
||||
def get_config(self):
|
||||
return {
|
||||
@ -1860,6 +1821,7 @@ class Audio(
|
||||
IOComponent,
|
||||
FileSerializable,
|
||||
TempFileManager,
|
||||
TokenInterpretable,
|
||||
):
|
||||
"""
|
||||
Creates an audio component that can be used to upload/record audio (as an input) or display audio (as an output).
|
||||
@ -1911,7 +1873,6 @@ class Audio(
|
||||
)
|
||||
self.type = type
|
||||
self.test_input = deepcopy(media_data.BASE64_AUDIO)
|
||||
self.interpret_by_tokens = True
|
||||
self.streaming = streaming
|
||||
if streaming and source != "microphone":
|
||||
raise ValueError(
|
||||
@ -1929,6 +1890,7 @@ class Audio(
|
||||
value=value,
|
||||
**kwargs,
|
||||
)
|
||||
TokenInterpretable.__init__(self)
|
||||
|
||||
def get_config(self):
|
||||
return {
|
||||
@ -2080,15 +2042,6 @@ class Audio(
|
||||
masked_inputs.append(masked_data)
|
||||
return masked_inputs
|
||||
|
||||
def get_interpretation_scores(
|
||||
self, x, neighbors, scores, masks=None, tokens=None
|
||||
) -> List[float]:
|
||||
"""
|
||||
Returns:
|
||||
Each value represents the interpretation score corresponding to an evenly spaced subsection of audio.
|
||||
"""
|
||||
return list(scores)
|
||||
|
||||
def generate_sample(self):
|
||||
return deepcopy(media_data.BASE64_AUDIO)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
"""Pydantic data models and other dataclasses. This is the only file that uses Optional[]
|
||||
typing syntax instead of | None syntax to work with pydantic"""
|
||||
|
||||
from enum import Enum, auto
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
|
@ -1,327 +0,0 @@
|
||||
"""
|
||||
Defines helper methods useful for loading and caching Interface examples.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import csv
|
||||
import os
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Callable, List
|
||||
|
||||
from gradio import utils
|
||||
from gradio.components import Dataset
|
||||
from gradio.context import Context
|
||||
from gradio.documentation import document, set_documentation_group
|
||||
from gradio.flagging import CSVLogger
|
||||
|
||||
if TYPE_CHECKING: # Only import for type checking (to avoid circular imports).
|
||||
from gradio.components import IOComponent
|
||||
|
||||
CACHED_FOLDER = "gradio_cached_examples"
|
||||
LOG_FILE = "log.csv"
|
||||
|
||||
set_documentation_group("component-helpers")
|
||||
|
||||
|
||||
def create_examples(
|
||||
examples: List[Any] | List[List[Any]] | str,
|
||||
inputs: IOComponent | List[IOComponent],
|
||||
outputs: IOComponent | List[IOComponent] | None = None,
|
||||
fn: Callable | None = None,
|
||||
cache_examples: bool = False,
|
||||
examples_per_page: int = 10,
|
||||
_api_mode: bool = False,
|
||||
label: str | None = None,
|
||||
elem_id: str | None = None,
|
||||
run_on_click: bool = False,
|
||||
preprocess: bool = True,
|
||||
postprocess: bool = True,
|
||||
batch: bool = False,
|
||||
):
|
||||
"""Top-level synchronous function that creates Examples. Provided for backwards compatibility, i.e. so that gr.Examples(...) can be used to create the Examples component."""
|
||||
examples_obj = Examples(
|
||||
examples=examples,
|
||||
inputs=inputs,
|
||||
outputs=outputs,
|
||||
fn=fn,
|
||||
cache_examples=cache_examples,
|
||||
examples_per_page=examples_per_page,
|
||||
_api_mode=_api_mode,
|
||||
label=label,
|
||||
elem_id=elem_id,
|
||||
run_on_click=run_on_click,
|
||||
preprocess=preprocess,
|
||||
postprocess=postprocess,
|
||||
batch=batch,
|
||||
_initiated_directly=False,
|
||||
)
|
||||
utils.synchronize_async(examples_obj.create)
|
||||
return examples_obj
|
||||
|
||||
|
||||
@document()
|
||||
class Examples:
|
||||
"""
|
||||
This class is a wrapper over the Dataset component and can be used to create Examples
|
||||
for Blocks / Interfaces. Populates the Dataset component with examples and
|
||||
assigns event listener so that clicking on an example populates the input/output
|
||||
components. Optionally handles example caching for fast inference.
|
||||
|
||||
Demos: blocks_inputs, fake_gan
|
||||
Guides: more_on_examples_and_flagging, using_hugging_face_integrations, image_classification_in_pytorch, image_classification_in_tensorflow, image_classification_with_vision_transformers, create_your_own_friends_with_a_gan
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
examples: List[Any] | List[List[Any]] | str,
|
||||
inputs: IOComponent | List[IOComponent],
|
||||
outputs: IOComponent | List[IOComponent] | None = None,
|
||||
fn: Callable | None = None,
|
||||
cache_examples: bool = False,
|
||||
examples_per_page: int = 10,
|
||||
_api_mode: bool = False,
|
||||
label: str | None = "Examples",
|
||||
elem_id: str | None = None,
|
||||
run_on_click: bool = False,
|
||||
preprocess: bool = True,
|
||||
postprocess: bool = True,
|
||||
batch: bool = False,
|
||||
_initiated_directly: bool = True,
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
examples: example inputs that can be clicked to populate specific components. Should be nested list, in which the outer list consists of samples and each inner list consists of an input corresponding to each input component. A string path to a directory of examples can also be provided but it should be within the directory with the python file running the gradio app. If there are multiple input components and a directory is provided, a log.csv file must be present in the directory to link corresponding inputs.
|
||||
inputs: the component or list of components corresponding to the examples
|
||||
outputs: optionally, provide the component or list of components corresponding to the output of the examples. Required if `cache` is True.
|
||||
fn: optionally, provide the function to run to generate the outputs corresponding to the examples. Required if `cache` is True.
|
||||
cache_examples: if True, caches examples for fast runtime. If True, then `fn` and `outputs` need to be provided
|
||||
examples_per_page: how many examples to show per page.
|
||||
label: the label to use for the examples component (by default, "Examples")
|
||||
elem_id: an optional string that is assigned as the id of this component in the HTML DOM.
|
||||
run_on_click: if cache_examples is False, clicking on an example does not run the function when an example is clicked. Set this to True to run the function when an example is clicked. Has no effect if cache_examples is True.
|
||||
preprocess: if True, preprocesses the example input before running the prediction function and caching the output. Only applies if cache_examples is True.
|
||||
postprocess: if True, postprocesses the example output after running the prediction function and before caching. Only applies if cache_examples is True.
|
||||
batch: If True, then the function should process a batch of inputs, meaning that it should accept a list of input values for each parameter. Used only if cache_examples is True.
|
||||
"""
|
||||
if _initiated_directly:
|
||||
warnings.warn(
|
||||
"Please use gr.Examples(...) instead of gr.examples.Examples(...) to create the Examples.",
|
||||
)
|
||||
|
||||
if cache_examples and (fn is None or outputs is None):
|
||||
raise ValueError("If caching examples, `fn` and `outputs` must be provided")
|
||||
|
||||
if not isinstance(inputs, list):
|
||||
inputs = [inputs]
|
||||
if outputs and not isinstance(outputs, list):
|
||||
outputs = [outputs]
|
||||
|
||||
working_directory = Path().absolute()
|
||||
|
||||
if examples is None:
|
||||
raise ValueError("The parameter `examples` cannot be None")
|
||||
elif isinstance(examples, list) and (
|
||||
len(examples) == 0 or isinstance(examples[0], list)
|
||||
):
|
||||
pass
|
||||
elif (
|
||||
isinstance(examples, list) and len(inputs) == 1
|
||||
): # If there is only one input component, examples can be provided as a regular list instead of a list of lists
|
||||
examples = [[e] for e in examples]
|
||||
elif isinstance(examples, str):
|
||||
if not Path(examples).exists():
|
||||
raise FileNotFoundError(
|
||||
"Could not find examples directory: " + examples
|
||||
)
|
||||
working_directory = examples
|
||||
if not (Path(examples) / LOG_FILE).exists():
|
||||
if len(inputs) == 1:
|
||||
examples = [[e] for e in os.listdir(examples)]
|
||||
else:
|
||||
raise FileNotFoundError(
|
||||
"Could not find log file (required for multiple inputs): "
|
||||
+ LOG_FILE
|
||||
)
|
||||
else:
|
||||
with open(Path(examples) / LOG_FILE) as logs:
|
||||
examples = list(csv.reader(logs))
|
||||
examples = [
|
||||
examples[i][: len(inputs)] for i in range(1, len(examples))
|
||||
] # remove header and unnecessary columns
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
"The parameter `examples` must either be a string directory or a list"
|
||||
"(if there is only 1 input component) or (more generally), a nested "
|
||||
"list, where each sublist represents a set of inputs."
|
||||
)
|
||||
|
||||
input_has_examples = [False] * len(inputs)
|
||||
for example in examples:
|
||||
for idx, example_for_input in enumerate(example):
|
||||
if not (example_for_input is None):
|
||||
try:
|
||||
input_has_examples[idx] = True
|
||||
except IndexError:
|
||||
pass # If there are more example components than inputs, ignore. This can sometimes be intentional (e.g. loading from a log file where outputs and timestamps are also logged)
|
||||
|
||||
inputs_with_examples = [
|
||||
inp for (inp, keep) in zip(inputs, input_has_examples) if keep
|
||||
]
|
||||
non_none_examples = [
|
||||
[ex for (ex, keep) in zip(example, input_has_examples) if keep]
|
||||
for example in examples
|
||||
]
|
||||
|
||||
self.examples = examples
|
||||
self.non_none_examples = non_none_examples
|
||||
self.inputs = inputs
|
||||
self.inputs_with_examples = inputs_with_examples
|
||||
self.outputs = outputs
|
||||
self.fn = fn
|
||||
self.cache_examples = cache_examples
|
||||
self._api_mode = _api_mode
|
||||
self.preprocess = preprocess
|
||||
self.postprocess = postprocess
|
||||
self.batch = batch
|
||||
|
||||
with utils.set_directory(working_directory):
|
||||
self.processed_examples = [
|
||||
[
|
||||
component.postprocess(sample)
|
||||
for component, sample in zip(inputs, example)
|
||||
]
|
||||
for example in examples
|
||||
]
|
||||
self.non_none_processed_examples = [
|
||||
[ex for (ex, keep) in zip(example, input_has_examples) if keep]
|
||||
for example in self.processed_examples
|
||||
]
|
||||
if cache_examples:
|
||||
for example in self.examples:
|
||||
if len([ex for ex in example if ex is not None]) != len(self.inputs):
|
||||
warnings.warn(
|
||||
"Examples are being cached but not all input components have "
|
||||
"example values. This may result in an exception being thrown by "
|
||||
"your function. If you do get an error while caching examples, make "
|
||||
"sure all of your inputs have example values for all of your examples "
|
||||
"or you provide default values for those particular parameters in your function."
|
||||
)
|
||||
break
|
||||
|
||||
with utils.set_directory(working_directory):
|
||||
self.dataset = Dataset(
|
||||
components=inputs_with_examples,
|
||||
samples=non_none_examples,
|
||||
type="index",
|
||||
label=label,
|
||||
samples_per_page=examples_per_page,
|
||||
elem_id=elem_id,
|
||||
)
|
||||
|
||||
self.cached_folder = Path(CACHED_FOLDER) / str(self.dataset._id)
|
||||
self.cached_file = Path(self.cached_folder) / "log.csv"
|
||||
self.cache_examples = cache_examples
|
||||
self.run_on_click = run_on_click
|
||||
|
||||
async def create(self) -> None:
|
||||
"""Caches the examples if self.cache_examples is True and creates the Dataset
|
||||
component to hold the examples"""
|
||||
|
||||
async def load_example(example_id):
|
||||
if self.cache_examples:
|
||||
processed_example = self.non_none_processed_examples[
|
||||
example_id
|
||||
] + await self.load_from_cache(example_id)
|
||||
else:
|
||||
processed_example = self.non_none_processed_examples[example_id]
|
||||
return utils.resolve_singleton(processed_example)
|
||||
|
||||
if Context.root_block:
|
||||
if self.cache_examples and self.outputs:
|
||||
targets = self.inputs_with_examples
|
||||
else:
|
||||
targets = self.inputs
|
||||
self.dataset.click(
|
||||
load_example,
|
||||
inputs=[self.dataset],
|
||||
outputs=targets, # type: ignore
|
||||
postprocess=False,
|
||||
queue=False,
|
||||
)
|
||||
if self.run_on_click and not self.cache_examples:
|
||||
if self.fn is None:
|
||||
raise ValueError("Cannot run_on_click if no function is provided")
|
||||
self.dataset.click(
|
||||
self.fn,
|
||||
inputs=self.inputs, # type: ignore
|
||||
outputs=self.outputs, # type: ignore
|
||||
)
|
||||
|
||||
if self.cache_examples:
|
||||
await self.cache()
|
||||
|
||||
async def cache(self) -> None:
|
||||
"""
|
||||
Caches all of the examples so that their predictions can be shown immediately.
|
||||
"""
|
||||
if Path(self.cached_file).exists():
|
||||
print(
|
||||
f"Using cache from '{Path(self.cached_folder).resolve()}' directory. If method or examples have changed since last caching, delete this folder to clear cache."
|
||||
)
|
||||
else:
|
||||
if Context.root_block is None:
|
||||
raise ValueError("Cannot cache examples if not in a Blocks context")
|
||||
|
||||
print(f"Caching examples at: '{Path(self.cached_file).resolve()}'")
|
||||
cache_logger = CSVLogger()
|
||||
|
||||
# create a fake dependency to process the examples and get the predictions
|
||||
dependency = Context.root_block.set_event_trigger(
|
||||
event_name="fake_event",
|
||||
fn=self.fn,
|
||||
inputs=self.inputs_with_examples, # type: ignore
|
||||
outputs=self.outputs, # type: ignore
|
||||
preprocess=self.preprocess and not self._api_mode,
|
||||
postprocess=self.postprocess and not self._api_mode,
|
||||
batch=self.batch,
|
||||
)
|
||||
|
||||
fn_index = Context.root_block.dependencies.index(dependency)
|
||||
assert self.outputs is not None
|
||||
cache_logger.setup(self.outputs, self.cached_folder)
|
||||
for example_id, _ in enumerate(self.examples):
|
||||
processed_input = self.processed_examples[example_id]
|
||||
if self.batch:
|
||||
processed_input = [[value] for value in processed_input]
|
||||
prediction = await Context.root_block.process_api(
|
||||
fn_index=fn_index, inputs=processed_input, request=None, state={}
|
||||
)
|
||||
output = prediction["data"]
|
||||
if self.batch:
|
||||
output = [value[0] for value in output]
|
||||
cache_logger.flag(output)
|
||||
# Remove the "fake_event" to prevent bugs in loading interfaces from spaces
|
||||
Context.root_block.dependencies.remove(dependency)
|
||||
Context.root_block.fns.pop(fn_index)
|
||||
|
||||
async def load_from_cache(self, example_id: int) -> List[Any]:
|
||||
"""Loads a particular cached example for the interface.
|
||||
Parameters:
|
||||
example_id: The id of the example to process (zero-indexed).
|
||||
"""
|
||||
with open(self.cached_file) as cache:
|
||||
examples = list(csv.reader(cache))
|
||||
example = examples[example_id + 1] # +1 to adjust for header
|
||||
output = []
|
||||
assert self.outputs is not None
|
||||
for component, value in zip(self.outputs, example):
|
||||
try:
|
||||
value_as_dict = ast.literal_eval(value)
|
||||
assert utils.is_update(value_as_dict)
|
||||
output.append(value_as_dict)
|
||||
except (ValueError, TypeError, SyntaxError, AssertionError):
|
||||
output.append(component.serialize(value, self.cached_folder))
|
||||
return output
|
@ -12,14 +12,15 @@ import tempfile
|
||||
import threading
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Callable, Iterable, List, Optional, Tuple
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Tuple
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import PIL
|
||||
import PIL.Image
|
||||
|
||||
from gradio import processing_utils, routes, utils
|
||||
from gradio import components, processing_utils, routes, utils
|
||||
from gradio.context import Context
|
||||
from gradio.documentation import document, set_documentation_group
|
||||
from gradio.flagging import CSVLogger
|
||||
@ -85,13 +86,13 @@ class Examples:
|
||||
self,
|
||||
examples: List[Any] | List[List[Any]] | str,
|
||||
inputs: IOComponent | List[IOComponent],
|
||||
outputs: Optional[IOComponent | List[IOComponent]] = None,
|
||||
fn: Optional[Callable] = None,
|
||||
outputs: IOComponent | List[IOComponent] | None = None,
|
||||
fn: Callable | None = None,
|
||||
cache_examples: bool = False,
|
||||
examples_per_page: int = 10,
|
||||
_api_mode: bool = False,
|
||||
label: str = "Examples",
|
||||
elem_id: Optional[str] = None,
|
||||
label: str | None = "Examples",
|
||||
elem_id: str | None = None,
|
||||
run_on_click: bool = False,
|
||||
preprocess: bool = True,
|
||||
postprocess: bool = True,
|
||||
@ -123,7 +124,7 @@ class Examples:
|
||||
|
||||
if not isinstance(inputs, list):
|
||||
inputs = [inputs]
|
||||
if not isinstance(outputs, list):
|
||||
if outputs and not isinstance(outputs, list):
|
||||
outputs = [outputs]
|
||||
|
||||
working_directory = Path().absolute()
|
||||
@ -139,12 +140,12 @@ class Examples:
|
||||
): # If there is only one input component, examples can be provided as a regular list instead of a list of lists
|
||||
examples = [[e] for e in examples]
|
||||
elif isinstance(examples, str):
|
||||
if not os.path.exists(examples):
|
||||
if not Path(examples).exists():
|
||||
raise FileNotFoundError(
|
||||
"Could not find examples directory: " + examples
|
||||
)
|
||||
working_directory = examples
|
||||
if not os.path.exists(os.path.join(examples, LOG_FILE)):
|
||||
if not (Path(examples) / LOG_FILE).exists():
|
||||
if len(inputs) == 1:
|
||||
examples = [[e] for e in os.listdir(examples)]
|
||||
else:
|
||||
@ -153,7 +154,7 @@ class Examples:
|
||||
+ LOG_FILE
|
||||
)
|
||||
else:
|
||||
with open(os.path.join(examples, LOG_FILE)) as logs:
|
||||
with open(Path(examples) / LOG_FILE) as logs:
|
||||
examples = list(csv.reader(logs))
|
||||
examples = [
|
||||
examples[i][: len(inputs)] for i in range(1, len(examples))
|
||||
@ -219,10 +220,8 @@ class Examples:
|
||||
)
|
||||
break
|
||||
|
||||
from gradio.components import Dataset
|
||||
|
||||
with utils.set_directory(working_directory):
|
||||
self.dataset = Dataset(
|
||||
self.dataset = components.Dataset(
|
||||
components=inputs_with_examples,
|
||||
samples=non_none_examples,
|
||||
type="index",
|
||||
@ -231,8 +230,8 @@ class Examples:
|
||||
elem_id=elem_id,
|
||||
)
|
||||
|
||||
self.cached_folder = os.path.join(CACHED_FOLDER, str(self.dataset._id))
|
||||
self.cached_file = os.path.join(self.cached_folder, "log.csv")
|
||||
self.cached_folder = Path(CACHED_FOLDER) / str(self.dataset._id)
|
||||
self.cached_file = Path(self.cached_folder) / "log.csv"
|
||||
self.cache_examples = cache_examples
|
||||
self.run_on_click = run_on_click
|
||||
|
||||
@ -250,19 +249,24 @@ class Examples:
|
||||
return utils.resolve_singleton(processed_example)
|
||||
|
||||
if Context.root_block:
|
||||
if self.cache_examples and self.outputs:
|
||||
targets = self.inputs_with_examples
|
||||
else:
|
||||
targets = self.inputs
|
||||
self.dataset.click(
|
||||
load_example,
|
||||
inputs=[self.dataset],
|
||||
outputs=self.inputs_with_examples
|
||||
+ (self.outputs if self.cache_examples else []),
|
||||
outputs=targets, # type: ignore
|
||||
postprocess=False,
|
||||
queue=False,
|
||||
)
|
||||
if self.run_on_click and not self.cache_examples:
|
||||
if self.fn is None:
|
||||
raise ValueError("Cannot run_on_click if no function is provided")
|
||||
self.dataset.click(
|
||||
self.fn,
|
||||
inputs=self.inputs,
|
||||
outputs=self.outputs,
|
||||
inputs=self.inputs, # type: ignore
|
||||
outputs=self.outputs, # type: ignore
|
||||
)
|
||||
|
||||
if self.cache_examples:
|
||||
@ -272,29 +276,30 @@ class Examples:
|
||||
"""
|
||||
Caches all of the examples so that their predictions can be shown immediately.
|
||||
"""
|
||||
if os.path.exists(self.cached_file):
|
||||
if Path(self.cached_file).exists():
|
||||
print(
|
||||
f"Using cache from '{os.path.abspath(self.cached_folder)}' directory. If method or examples have changed since last caching, delete this folder to clear cache."
|
||||
f"Using cache from '{Path(self.cached_folder).resolve()}' directory. If method or examples have changed since last caching, delete this folder to clear cache."
|
||||
)
|
||||
else:
|
||||
if Context.root_block is None:
|
||||
raise ValueError("Cannot cache examples if not in a Blocks context")
|
||||
|
||||
print(f"Caching examples at: '{os.path.abspath(self.cached_file)}'")
|
||||
print(f"Caching examples at: '{Path(self.cached_file).resolve()}'")
|
||||
cache_logger = CSVLogger()
|
||||
|
||||
# create a fake dependency to process the examples and get the predictions
|
||||
dependency = Context.root_block.set_event_trigger(
|
||||
event_name="fake_event",
|
||||
fn=self.fn,
|
||||
inputs=self.inputs_with_examples,
|
||||
outputs=self.outputs,
|
||||
inputs=self.inputs_with_examples, # type: ignore
|
||||
outputs=self.outputs, # type: ignore
|
||||
preprocess=self.preprocess and not self._api_mode,
|
||||
postprocess=self.postprocess and not self._api_mode,
|
||||
batch=self.batch,
|
||||
)
|
||||
|
||||
fn_index = Context.root_block.dependencies.index(dependency)
|
||||
assert self.outputs is not None
|
||||
cache_logger.setup(self.outputs, self.cached_folder)
|
||||
for example_id, _ in enumerate(self.examples):
|
||||
processed_input = self.processed_examples[example_id]
|
||||
@ -320,6 +325,7 @@ class Examples:
|
||||
examples = list(csv.reader(cache))
|
||||
example = examples[example_id + 1] # +1 to adjust for header
|
||||
output = []
|
||||
assert self.outputs is not None
|
||||
for component, value in zip(self.outputs, example):
|
||||
try:
|
||||
value_as_dict = ast.literal_eval(value)
|
||||
@ -333,13 +339,13 @@ class Examples:
|
||||
class TrackedIterable:
|
||||
def __init__(
|
||||
self,
|
||||
iterable: Iterable,
|
||||
iterable: Iterable | None,
|
||||
index: int | None,
|
||||
length: int | None,
|
||||
desc: str | None,
|
||||
unit: str | None,
|
||||
_tqdm=None,
|
||||
progress: float = None,
|
||||
progress: float | None = None,
|
||||
) -> None:
|
||||
self.iterable = iterable
|
||||
self.index = index
|
||||
@ -373,16 +379,14 @@ class Progress(Iterable):
|
||||
def __init__(
|
||||
self,
|
||||
track_tqdm: bool = False,
|
||||
_active: bool = False,
|
||||
_callback: Callable = None,
|
||||
_event_id: str = None,
|
||||
_callback: Callable | None = None, # for internal use only
|
||||
_event_id: str | None = None,
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
track_tqdm: If True, the Progress object will track any tqdm.tqdm iterations with the tqdm library in the function.
|
||||
"""
|
||||
self.track_tqdm = track_tqdm
|
||||
self._active = _active
|
||||
self._callback = _callback
|
||||
self._event_id = _event_id
|
||||
self.iterables: List[TrackedIterable] = []
|
||||
@ -397,7 +401,7 @@ class Progress(Iterable):
|
||||
"""
|
||||
Updates progress tracker with next item in iterable.
|
||||
"""
|
||||
if self._active:
|
||||
if self._callback:
|
||||
current_iterable = self.iterables[-1]
|
||||
while (
|
||||
not hasattr(current_iterable.iterable, "__next__")
|
||||
@ -408,9 +412,10 @@ class Progress(Iterable):
|
||||
event_id=self._event_id,
|
||||
iterables=self.iterables,
|
||||
)
|
||||
assert current_iterable.index is not None, "Index not set."
|
||||
current_iterable.index += 1
|
||||
try:
|
||||
return next(current_iterable.iterable)
|
||||
return next(current_iterable.iterable) # type: ignore
|
||||
except StopIteration:
|
||||
self.iterables.pop()
|
||||
raise StopIteration
|
||||
@ -421,7 +426,7 @@ class Progress(Iterable):
|
||||
self,
|
||||
progress: float | Tuple[int, int | None] | None,
|
||||
desc: str | None = None,
|
||||
total: float | None = None,
|
||||
total: int | None = None,
|
||||
unit: str = "steps",
|
||||
_tqdm=None,
|
||||
):
|
||||
@ -433,7 +438,7 @@ class Progress(Iterable):
|
||||
total: estimated total number of steps.
|
||||
unit: unit of iterations.
|
||||
"""
|
||||
if self._active:
|
||||
if self._callback:
|
||||
if isinstance(progress, tuple):
|
||||
index, total = progress
|
||||
progress = None
|
||||
@ -450,8 +455,8 @@ class Progress(Iterable):
|
||||
def tqdm(
|
||||
self,
|
||||
iterable: Iterable | None,
|
||||
desc: str = None,
|
||||
total: float = None,
|
||||
desc: str | None = None,
|
||||
total: int | None = None,
|
||||
unit: str = "steps",
|
||||
_tqdm=None,
|
||||
*args,
|
||||
@ -465,15 +470,16 @@ class Progress(Iterable):
|
||||
total: estimated total number of steps.
|
||||
unit: unit of iterations.
|
||||
"""
|
||||
if iterable is None:
|
||||
new_iterable = TrackedIterable(None, 0, total, desc, unit, _tqdm)
|
||||
self.iterables.append(new_iterable)
|
||||
self._callback(event_id=self._event_id, iterables=self.iterables)
|
||||
return
|
||||
length = len(iterable) if hasattr(iterable, "__len__") else None
|
||||
self.iterables.append(
|
||||
TrackedIterable(iter(iterable), 0, length, desc, unit, _tqdm)
|
||||
)
|
||||
if self._callback:
|
||||
if iterable is None:
|
||||
new_iterable = TrackedIterable(None, 0, total, desc, unit, _tqdm)
|
||||
self.iterables.append(new_iterable)
|
||||
self._callback(event_id=self._event_id, iterables=self.iterables)
|
||||
return self
|
||||
length = len(iterable) if hasattr(iterable, "__len__") else None # type: ignore
|
||||
self.iterables.append(
|
||||
TrackedIterable(iter(iterable), 0, length, desc, unit, _tqdm)
|
||||
)
|
||||
return self
|
||||
|
||||
def update(self, n=1):
|
||||
@ -482,8 +488,9 @@ class Progress(Iterable):
|
||||
Parameters:
|
||||
n: number of steps completed.
|
||||
"""
|
||||
if self._active and len(self.iterables) > 0:
|
||||
if self._callback and len(self.iterables) > 0:
|
||||
current_iterable = self.iterables[-1]
|
||||
assert current_iterable.index is not None, "Index not set."
|
||||
current_iterable.index += n
|
||||
self._callback(
|
||||
event_id=self._event_id,
|
||||
@ -496,7 +503,7 @@ class Progress(Iterable):
|
||||
"""
|
||||
Removes iterable with given _tqdm.
|
||||
"""
|
||||
if self._active:
|
||||
if self._callback:
|
||||
for i in range(len(self.iterables)):
|
||||
if id(self.iterables[i]._tqdm) == id(_tqdm):
|
||||
self.iterables.pop(i)
|
||||
@ -511,9 +518,7 @@ class Progress(Iterable):
|
||||
|
||||
def create_tracker(root_blocks, event_id, fn, track_tqdm):
|
||||
|
||||
progress = Progress(
|
||||
_active=True, _callback=root_blocks._queue.set_progress, _event_id=event_id
|
||||
)
|
||||
progress = Progress(_callback=root_blocks._queue.set_progress, _event_id=event_id)
|
||||
if not track_tqdm:
|
||||
return progress, fn
|
||||
|
||||
@ -677,7 +682,7 @@ def make_waveform(
|
||||
audio: str | Tuple[int, np.ndarray],
|
||||
*,
|
||||
bg_color: str = "#f3f4f6",
|
||||
bg_image: str = None,
|
||||
bg_image: str | None = None,
|
||||
fg_alpha: float = 0.75,
|
||||
bars_color: str | Tuple[str, str] = ("#fbbf24", "#ea580c"),
|
||||
bar_count: int = 50,
|
||||
@ -748,7 +753,7 @@ def make_waveform(
|
||||
plt.axis("off")
|
||||
plt.margins(x=0)
|
||||
tmp_img = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
|
||||
savefig_kwargs = {"bbox_inches": "tight"}
|
||||
savefig_kwargs: Dict[str, Any] = {"bbox_inches": "tight"}
|
||||
if bg_image is not None:
|
||||
savefig_kwargs["transparent"] = True
|
||||
else:
|
||||
|
@ -1,13 +1,75 @@
|
||||
"""Contains classes and methods related to interpretation for components in Gradio."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import math
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
from gradio import utils
|
||||
from gradio.components import Label, Number
|
||||
from gradio import components, utils
|
||||
|
||||
if TYPE_CHECKING: # Only import for type checking (is False at runtime).
|
||||
from gradio import Interface
|
||||
|
||||
|
||||
async def run_interpret(interface, raw_input):
|
||||
class Interpretable(ABC):
|
||||
def __init__(self) -> None:
|
||||
self.set_interpret_parameters()
|
||||
|
||||
def set_interpret_parameters(self):
|
||||
"""
|
||||
Set any parameters for interpretation. Properties can be set here to be
|
||||
used in get_interpretation_neighbors and get_interpretation_scores.
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_interpretation_scores(
|
||||
self, x: Any, neighbors: List[Any] | None, scores: List[float], **kwargs
|
||||
) -> List:
|
||||
"""
|
||||
Arrange the output values from the neighbors into interpretation scores for the interface to render.
|
||||
Parameters:
|
||||
x: Input to interface
|
||||
neighbors: Neighboring values to input x used for interpretation.
|
||||
scores: Output value corresponding to each neighbor in neighbors
|
||||
Returns:
|
||||
Arrangement of interpretation scores for interfaces to render.
|
||||
"""
|
||||
return scores
|
||||
|
||||
|
||||
class TokenInterpretable(Interpretable, ABC):
|
||||
@abstractmethod
|
||||
def tokenize(self, x: Any) -> Tuple[List, List, None]:
|
||||
"""
|
||||
Interprets an input data point x by splitting it into a list of tokens (e.g
|
||||
a string into words or an image into super-pixels).
|
||||
"""
|
||||
return [], [], None
|
||||
|
||||
@abstractmethod
|
||||
def get_masked_inputs(self, tokens: List, binary_mask_matrix: List[List]) -> List:
|
||||
return []
|
||||
|
||||
|
||||
class NeighborInterpretable(Interpretable, ABC):
|
||||
@abstractmethod
|
||||
def get_interpretation_neighbors(self, x: Any) -> Tuple[List, Dict]:
|
||||
"""
|
||||
Generates values similar to input to be used to interpret the significance of the input in the final output.
|
||||
Parameters:
|
||||
x: Input to interface
|
||||
Returns: (neighbor_values, interpret_kwargs, interpret_by_removal)
|
||||
neighbor_values: Neighboring values to input x to compute for interpretation
|
||||
interpret_kwargs: Keyword arguments to be passed to get_interpretation_scores
|
||||
"""
|
||||
return [], {}
|
||||
|
||||
|
||||
async def run_interpret(interface: Interface, raw_input: List):
|
||||
"""
|
||||
Runs the interpretation command for the machine learning model. Handles both the "default" out-of-the-box
|
||||
interpretation for a certain set of UI component types, as well as the custom interpretation case.
|
||||
@ -31,7 +93,7 @@ async def run_interpret(interface, raw_input):
|
||||
if interp == "default":
|
||||
input_component = interface.input_components[i]
|
||||
neighbor_raw_input = list(raw_input)
|
||||
if input_component.interpret_by_tokens:
|
||||
if isinstance(input_component, TokenInterpretable):
|
||||
tokens, neighbor_values, masks = input_component.tokenize(x)
|
||||
interface_scores = []
|
||||
alternative_output = []
|
||||
@ -73,7 +135,7 @@ async def run_interpret(interface, raw_input):
|
||||
tokens=tokens,
|
||||
)
|
||||
)
|
||||
else:
|
||||
elif isinstance(input_component, NeighborInterpretable):
|
||||
(
|
||||
neighbor_values,
|
||||
interpret_kwargs,
|
||||
@ -114,9 +176,13 @@ async def run_interpret(interface, raw_input):
|
||||
raw_input[i],
|
||||
neighbor_values,
|
||||
interface_scores,
|
||||
**interpret_kwargs
|
||||
**interpret_kwargs,
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Component {input_component} does not support interpretation"
|
||||
)
|
||||
elif interp == "shap" or interp == "shapley":
|
||||
try:
|
||||
import shap # type: ignore
|
||||
@ -125,7 +191,7 @@ async def run_interpret(interface, raw_input):
|
||||
"The package `shap` is required for this interpretation method. Try: `pip install shap`"
|
||||
)
|
||||
input_component = interface.input_components[i]
|
||||
if not (input_component.interpret_by_tokens):
|
||||
if not isinstance(input_component, TokenInterpretable):
|
||||
raise ValueError(
|
||||
"Input component {} does not support `shap` interpretation".format(
|
||||
input_component
|
||||
@ -136,6 +202,7 @@ async def run_interpret(interface, raw_input):
|
||||
|
||||
# construct a masked version of the input
|
||||
def get_masked_prediction(binary_mask):
|
||||
assert isinstance(input_component, TokenInterpretable)
|
||||
masked_xs = input_component.get_masked_inputs(tokens, binary_mask)
|
||||
preds = []
|
||||
for masked_x in masked_xs:
|
||||
@ -162,9 +229,14 @@ async def run_interpret(interface, raw_input):
|
||||
nsamples=int(interface.num_shap * num_total_segments),
|
||||
silent=True,
|
||||
)
|
||||
assert shap_values is not None, "SHAP values could not be calculated"
|
||||
scores.append(
|
||||
input_component.get_interpretation_scores(
|
||||
raw_input[i], None, shap_values[0], masks=masks, tokens=tokens
|
||||
raw_input[i],
|
||||
None,
|
||||
shap_values[0].tolist(),
|
||||
masks=masks,
|
||||
tokens=tokens,
|
||||
)
|
||||
)
|
||||
alternative_outputs.append([])
|
||||
@ -174,7 +246,7 @@ async def run_interpret(interface, raw_input):
|
||||
else:
|
||||
raise ValueError("Unknown intepretation method: {}".format(interp))
|
||||
return scores, alternative_outputs
|
||||
else: # custom interpretation function
|
||||
elif interface.interpretation: # custom interpretation function
|
||||
processed_input = [
|
||||
input_component.preprocess(raw_input[i])
|
||||
for i, input_component in enumerate(interface.input_components)
|
||||
@ -184,9 +256,11 @@ async def run_interpret(interface, raw_input):
|
||||
if len(raw_input) == 1:
|
||||
interpretation = [interpretation]
|
||||
return interpretation, []
|
||||
else:
|
||||
raise ValueError("No interpretation method specified.")
|
||||
|
||||
|
||||
def diff(original, perturbed):
|
||||
def diff(original: Any, perturbed: Any) -> int | float:
|
||||
try: # try computing numerical difference
|
||||
score = float(original) - float(perturbed)
|
||||
except ValueError: # otherwise, look at strict difference in label
|
||||
@ -194,12 +268,14 @@ def diff(original, perturbed):
|
||||
return score
|
||||
|
||||
|
||||
def quantify_difference_in_label(interface, original_output, perturbed_output):
|
||||
def quantify_difference_in_label(
|
||||
interface: Interface, original_output: List, perturbed_output: List
|
||||
) -> int | float:
|
||||
output_component = interface.output_components[0]
|
||||
post_original_output = output_component.postprocess(original_output[0])
|
||||
post_perturbed_output = output_component.postprocess(perturbed_output[0])
|
||||
|
||||
if isinstance(output_component, Label):
|
||||
if isinstance(output_component, components.Label):
|
||||
original_label = post_original_output["label"]
|
||||
perturbed_label = post_perturbed_output["label"]
|
||||
|
||||
@ -212,7 +288,7 @@ def quantify_difference_in_label(interface, original_output, perturbed_output):
|
||||
score = diff(original_label, perturbed_label)
|
||||
return score
|
||||
|
||||
elif isinstance(output_component, Number):
|
||||
elif isinstance(output_component, components.Number):
|
||||
score = diff(post_original_output, post_perturbed_output)
|
||||
return score
|
||||
|
||||
@ -225,14 +301,14 @@ def quantify_difference_in_label(interface, original_output, perturbed_output):
|
||||
|
||||
|
||||
def get_regression_or_classification_value(
|
||||
interface, original_output, perturbed_output
|
||||
):
|
||||
interface: Interface, original_output: List, perturbed_output: List
|
||||
) -> int | float:
|
||||
"""Used to combine regression/classification for Shap interpretation method."""
|
||||
output_component = interface.output_components[0]
|
||||
post_original_output = output_component.postprocess(original_output[0])
|
||||
post_perturbed_output = output_component.postprocess(perturbed_output[0])
|
||||
|
||||
if type(output_component) == Label:
|
||||
if isinstance(output_component, components.Label):
|
||||
original_label = post_original_output["label"]
|
||||
perturbed_label = post_perturbed_output["label"]
|
||||
|
||||
|
@ -5,5 +5,4 @@ pip_required
|
||||
|
||||
pip install --upgrade pip
|
||||
pip install pyright
|
||||
cd gradio
|
||||
pyright blocks.py components.py context.py data_classes.py deprecation.py documentation.py encryptor.py events.py examples.py exceptions.py external.py external_utils.py serializing.py layouts.py flagging.py interface.py mix.py networking.py pipelines.py processing_utils.py queueing.py reload.py routes.py serializing.py strings.py tunneling.py utils.py templates.py
|
||||
pyright gradio/*.py
|
||||
|
Loading…
Reference in New Issue
Block a user