Block-Components

- create general structure
- move Textbox into component.py
This commit is contained in:
Ömer Faruk Özdemir 2022-03-02 21:59:37 +03:00
parent c8411c75c6
commit 9451597224
5 changed files with 258 additions and 196 deletions

View File

@ -24,8 +24,10 @@ with xray_blocks:
with gr.Row():
xray_scan = gr.inputs.Image()
xray_results = gr.outputs.JSON()
new_results = gr.components.Textbox()
xray_run = gr.Button("Run")
xray_run.click(xray_model, inputs=[disease, xray_scan], outputs=xray_results)
xray_run.click(xray_model, inputs=[disease, xray_scan], outputs=new_results)
with gr.Tab("CT Scan"):
with gr.Row():

View File

@ -1,5 +1,8 @@
import pkg_resources
import gradio.component as components
import gradio.inputs as inputs
import gradio.outputs as outputs
from gradio.blocks import Blocks, Column, Row, Tab
from gradio.flagging import (
CSVLogger,

View File

@ -1,6 +1,11 @@
from __future__ import annotations
import os
import shutil
from typing import Any, Dict, Optional
import warnings
from typing import Any, Dict, List, Optional, Tuple
import numpy as np
from gradio import processing_utils
from gradio.blocks import Block
@ -8,25 +13,36 @@ from gradio.blocks import Block
class Component(Block):
"""
A class for defining the methods that all gradio input and output components should have.
A base class for defining the methods that all gradio input and output components should have.
"""
def __init__(self, label, requires_permissions=False):
def __init__(
self, label: str, requires_permissions: bool = False, optional: bool = False
):
self.label = label
self.requires_permissions = requires_permissions
self.component_type = "input"
self.set_interpret_parameters()
self.optional = optional
self.input = False
self.output = False
super().__init__()
def __str__(self):
return self.__repr__()
def __repr__(self):
return '{}(label="{}")'.format(type(self).__name__, self.label)
return f"{type(self).__name__} (label={self.label})"
def get_template_context(self):
"""
:return: a dictionary with context variables for the javascript file associated with the context
"""
return {"name": self.__class__.__name__.lower(), "label": self.label}
return {
"name": self.__class__.__name__.lower(),
"label": self.label,
"optional": self.optional,
}
@classmethod
def get_shortcut_implementations(cls):
@ -95,3 +111,233 @@ class Component(Block):
for shortcut, parameters in sub_cls.get_shortcut_implementations().items():
shortcuts[shortcut] = (sub_cls, parameters)
return shortcuts
# Input Functionalities
def preprocess(self, x: Any) -> Any:
"""
Any preprocessing needed to be performed on function input.
"""
return x
def serialize(self, x: Any, called_directly: bool) -> Any:
"""
Convert from a human-readable version of the input (path of an image, URL of a video, etc.) into the interface to a serialized version (e.g. base64) to pass into an API. May do different things if the interface is called() vs. used via GUI.
Parameters:
x (Any): Input to interface
called_directly (bool): if true, the interface was called(), otherwise, it is being used via the GUI
"""
return x
def preprocess_example(self, x: Any) -> Any:
"""
Any preprocessing needed to be performed on an example before being passed to the main function.
"""
return x
def set_interpret_parameters(self):
"""
Set any parameters for interpretation.
"""
return self
def get_interpretation_neighbors(self, x: Any) -> Tuple[List[Any], Dict[Any], bool]:
"""
Generates values similar to input to be used to interpret the significance of the input in the final output.
Parameters:
x (Any): Input to interface
Returns: (neighbor_values, interpret_kwargs, interpret_by_removal)
neighbor_values (List[Any]): Neighboring values to input x to compute for interpretation
interpret_kwargs (Dict[Any]): Keyword arguments to be passed to get_interpretation_scores
interpret_by_removal (bool): 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[Any]:
"""
Arrange the output values from the neighbors into interpretation scores for the interface to render.
Parameters:
x (Any): Input to interface
neighbors (List[Any]): Neighboring values to input x used for interpretation.
scores (List[float]): Output value corresponding to each neighbor in neighbors
kwargs (Dict[str, Any]): Any additional arguments passed from get_interpretation_neighbors.
Returns:
(List[Any]): Arrangement of interpretation scores for interfaces to render.
"""
pass
def generate_sample(self) -> Any:
"""
Returns a sample value of the input that would be accepted by the api. Used for api documentation.
"""
pass
# Output Functionalities
def postprocess(self, y):
"""
Any postprocessing needed to be performed on function output.
"""
return y
def deserialize(self, x):
"""
Convert from serialized output (e.g. base64 representation) from a call() to the interface to a human-readable version of the output (path of an image, etc.)
"""
return x
class Textbox(Component):
"""
Component creates a textbox for user to enter input. Provides a string as an argument to the wrapped function.
Input type: str
Demos: hello_world, diff_texts
Component creates a textbox to render output text or number.
Output type: Union[str, float, int]
Demos: hello_world, sentence_builder
"""
def __init__(
self,
lines: int = 1,
placeholder: Optional[str] = None,
default: str = "",
label: Optional[str] = None,
numeric: Optional[bool] = False,
type: Optional[str] = "str",
optional: bool = False,
):
"""
Parameters:
lines (int): number of line rows to provide in textarea.
placeholder (str): placeholder hint to provide behind textarea.
default (str): default text to provide in textarea.
label (str): component name in interface.
numeric (bool): DEPRECATED.
type (str): DEPRECATED.
optional (bool): DEPRECATED
"""
self.lines = lines
self.placeholder = placeholder
self.default = default
self.type = "str"
if numeric:
warnings.warn(
"The 'numeric' type has been deprecated. Use the Number component instead.",
DeprecationWarning,
)
if type:
warnings.warn(
"The 'type' parameter has been deprecated. Use the Number component instead if you need it.",
DeprecationWarning,
)
self.test_input = default
self.interpret_by_tokens = True
super().__init__(label)
def get_template_context(self):
return {
"lines": self.lines,
"placeholder": self.placeholder,
"default": self.default,
**super().get_template_context(),
}
@classmethod
def get_shortcut_implementations(cls):
return {
"text": {},
"textbox": {"lines": 7},
}
def preprocess(self, x: str) -> str | float:
"""
Parameters:
x (str): text input
"""
if self.type == "str":
return x
elif self.type == "number":
return float(x)
else:
raise ValueError(
"Unknown type: "
+ str(self.type)
+ ". Please choose from: 'str', 'number'."
)
def preprocess_example(self, x: str) -> str:
"""
Returns:
(str): Text representing function input
"""
return x
def set_interpret_parameters(
self, separator: str = " ", replacement: Optional[str] = None
):
"""
Calculates interpretation score of characters in input by splitting input into tokens, then using a "leave one out" method to calculate the score of each token by removing each token and measuring the delta of the output value.
Parameters:
separator (str): Separator to use to split input into tokens.
replacement (str): In the "leave one out" step, the text that the token should be replaced with. If None, the token is removed altogether.
"""
self.interpretation_separator = separator
self.interpretation_replacement = replacement
return self
def tokenize(self, x: str) -> Tuple[List[str], List[str], None]:
"""
Tokenizes an input string by dividing into "words" delimited by self.interpretation_separator
"""
tokens = x.split(self.interpretation_separator)
leave_one_out_strings = []
for index in range(len(tokens)):
leave_one_out_set = list(tokens)
if self.interpretation_replacement is None:
leave_one_out_set.pop(index)
else:
leave_one_out_set[index] = self.interpretation_replacement
leave_one_out_strings.append(
self.interpretation_separator.join(leave_one_out_set)
)
return tokens, leave_one_out_strings, None
def get_masked_inputs(
self, tokens: List[str], binary_mask_matrix: List[List[int]]
) -> List[str]:
"""
Constructs partially-masked sentences for SHAP interpretation
"""
masked_inputs = []
for binary_mask_vector in binary_mask_matrix:
masked_input = np.array(tokens)[np.array(binary_mask_vector, dtype=bool)]
masked_inputs.append(self.interpretation_separator.join(masked_input))
return masked_inputs
def get_interpretation_scores(
self, x, neighbors, scores: List[float], tokens: List[str], masks=None
) -> List[Tuple[str, float]]:
"""
Returns:
(List[Tuple[str, float]]): Each tuple set represents a set of characters and their corresponding interpretation score.
"""
result = []
for token, score in zip(tokens, scores):
result.append((token, score))
result.append((self.interpretation_separator, 0))
return result
def generate_sample(self) -> str:
return "Hello World"
def postprocess(self, y: str):
"""
Parameters:
y (str): text output
Returns:
str(y)
"""
return str(y)

View File

@ -19,7 +19,7 @@ import PIL
from ffmpy import FFmpeg
from gradio import processing_utils, test_data
from gradio.component import Component
from gradio.component import Component, Textbox
if TYPE_CHECKING: # Only import for type checking (is False at runtime).
from gradio import Interface
@ -108,151 +108,6 @@ class InputComponent(Component):
}
class Textbox(InputComponent):
"""
Component creates a textbox for user to enter input. Provides a string as an argument to the wrapped function.
Input type: str
Demos: hello_world, diff_texts
"""
def __init__(
self,
lines: int = 1,
placeholder: Optional[str] = None,
default: str = "",
numeric: Optional[bool] = False,
type: Optional[str] = "str",
label: Optional[str] = None,
optional: bool = False,
):
"""
Parameters:
lines (int): number of line rows to provide in textarea.
placeholder (str): placeholder hint to provide behind textarea.
default (str): default text to provide in textarea.
numeric (bool): DEPRECATED. Whether the input should be parsed as a number instead of a string.
type (str): DEPRECATED. Type of value to be returned by component. "str" returns a string, "number" returns a float value. Use Number component in place of number type.
label (str): component name in interface.
optional (bool): this parameter is ignored.
"""
self.lines = lines
self.placeholder = placeholder
self.default = default
if numeric or type == "number":
warnings.warn(
"The 'numeric' type has been deprecated. Use the Number input component instead.",
DeprecationWarning,
)
self.type = "number"
else:
self.type = type
if default == "":
self.test_input = {
"str": "the quick brown fox jumped over the lazy dog",
"number": 786.92,
}.get(type)
else:
self.test_input = default
self.interpret_by_tokens = True
super().__init__(label)
def get_template_context(self):
return {
"lines": self.lines,
"placeholder": self.placeholder,
"default": self.default,
**super().get_template_context(),
}
@classmethod
def get_shortcut_implementations(cls):
return {
"text": {},
"textbox": {"lines": 7},
}
def preprocess(self, x: str) -> str | float:
"""
Parameters:
x (str): text input
"""
if self.type == "str":
return x
elif self.type == "number":
return float(x)
else:
raise ValueError(
"Unknown type: "
+ str(self.type)
+ ". Please choose from: 'str', 'number'."
)
def preprocess_example(self, x: str) -> str:
"""
Returns:
(str): Text representing function input
"""
return x
def set_interpret_parameters(
self, separator: str = " ", replacement: Optional[str] = None
):
"""
Calculates interpretation score of characters in input by splitting input into tokens, then using a "leave one out" method to calculate the score of each token by removing each token and measuring the delta of the output value.
Parameters:
separator (str): Separator to use to split input into tokens.
replacement (str): In the "leave one out" step, the text that the token should be replaced with. If None, the token is removed altogether.
"""
self.interpretation_separator = separator
self.interpretation_replacement = replacement
return self
def tokenize(self, x: str) -> Tuple[List[str], List[str], None]:
"""
Tokenizes an input string by dividing into "words" delimited by self.interpretation_separator
"""
tokens = x.split(self.interpretation_separator)
leave_one_out_strings = []
for index in range(len(tokens)):
leave_one_out_set = list(tokens)
if self.interpretation_replacement is None:
leave_one_out_set.pop(index)
else:
leave_one_out_set[index] = self.interpretation_replacement
leave_one_out_strings.append(
self.interpretation_separator.join(leave_one_out_set)
)
return tokens, leave_one_out_strings, None
def get_masked_inputs(
self, tokens: List[str], binary_mask_matrix: List[List[int]]
) -> List[str]:
"""
Constructs partially-masked sentences for SHAP interpretation
"""
masked_inputs = []
for binary_mask_vector in binary_mask_matrix:
masked_input = np.array(tokens)[np.array(binary_mask_vector, dtype=bool)]
masked_inputs.append(self.interpretation_separator.join(masked_input))
return masked_inputs
def get_interpretation_scores(
self, x, neighbors, scores: List[float], tokens: List[str], masks=None
) -> List[Tuple[str, float]]:
"""
Returns:
(List[Tuple[str, float]]): Each tuple set represents a set of characters and their corresponding interpretation score.
"""
result = []
for token, score in zip(tokens, scores):
result.append((token, score))
result.append((self.interpretation_separator, 0))
return result
def generate_sample(self) -> str:
return "Hello World"
class Number(InputComponent):
"""
Component creates a field for user to enter numeric input. Provides a number as an argument to the wrapped function.

View File

@ -21,7 +21,7 @@ import PIL
from ffmpy import FFmpeg
from gradio import processing_utils
from gradio.component import Component
from gradio.component import Component, Textbox
if TYPE_CHECKING: # Only import for type checking (is False at runtime).
from gradio import Interface
@ -49,50 +49,6 @@ class OutputComponent(Component):
return x
class Textbox(OutputComponent):
"""
Component creates a textbox to render output text or number.
Output type: Union[str, float, int]
Demos: hello_world, sentence_builder
"""
def __init__(self, type: str = "auto", label: Optional[str] = None):
"""
Parameters:
type (str): Type of value to be passed to component. "str" expects a string, "number" expects a float value, "auto" detects return type.
label (str): component name in interface.
"""
self.type = type
super().__init__(label)
def get_template_context(self):
return {**super().get_template_context()}
@classmethod
def get_shortcut_implementations(cls):
return {
"text": {"type": "str"},
"textbox": {"type": "str"},
"number": {"type": "number"},
}
def postprocess(self, y):
"""
Parameters:
y (str): text output
Returns:
(Union[str, number]): output value
"""
if self.type == "str" or self.type == "auto":
return str(y)
elif self.type == "number":
return y
else:
raise ValueError(
"Unknown type: " + self.type + ". Please choose from: 'str', 'number'"
)
class Label(OutputComponent):
"""
Component outputs a classification label, along with confidence scores of top categories if provided. Confidence scores are represented as a dictionary mapping labels to scores between 0 and 1.