structure saas with tools
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
from google.cloud.aiplatform.compat.types import (
|
||||
explanation as explanation_compat,
|
||||
explanation_metadata as explanation_metadata_compat,
|
||||
)
|
||||
|
||||
ExplanationMetadata = explanation_metadata_compat.ExplanationMetadata
|
||||
|
||||
# ExplanationMetadata subclasses
|
||||
InputMetadata = ExplanationMetadata.InputMetadata
|
||||
OutputMetadata = ExplanationMetadata.OutputMetadata
|
||||
|
||||
# InputMetadata subclasses
|
||||
Encoding = InputMetadata.Encoding
|
||||
FeatureValueDomain = InputMetadata.FeatureValueDomain
|
||||
Visualization = InputMetadata.Visualization
|
||||
|
||||
|
||||
ExplanationParameters = explanation_compat.ExplanationParameters
|
||||
FeatureNoiseSigma = explanation_compat.FeatureNoiseSigma
|
||||
|
||||
ExplanationSpec = explanation_compat.ExplanationSpec
|
||||
|
||||
# Classes used by ExplanationParameters
|
||||
IntegratedGradientsAttribution = explanation_compat.IntegratedGradientsAttribution
|
||||
SampledShapleyAttribution = explanation_compat.SampledShapleyAttribution
|
||||
SmoothGradConfig = explanation_compat.SmoothGradConfig
|
||||
XraiAttribution = explanation_compat.XraiAttribution
|
||||
Presets = explanation_compat.Presets
|
||||
Examples = explanation_compat.Examples
|
||||
|
||||
|
||||
__all__ = (
|
||||
"Encoding",
|
||||
"ExplanationSpec",
|
||||
"ExplanationMetadata",
|
||||
"ExplanationParameters",
|
||||
"FeatureNoiseSigma",
|
||||
"FeatureValueDomain",
|
||||
"InputMetadata",
|
||||
"IntegratedGradientsAttribution",
|
||||
"OutputMetadata",
|
||||
"SampledShapleyAttribution",
|
||||
"SmoothGradConfig",
|
||||
"Visualization",
|
||||
"XraiAttribution",
|
||||
"Presets",
|
||||
"Examples",
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,482 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from google.cloud import aiplatform
|
||||
from typing import Dict, List, Mapping, Optional, Tuple, Union
|
||||
|
||||
try:
|
||||
from lit_nlp.api import dataset as lit_dataset
|
||||
from lit_nlp.api import dtypes as lit_dtypes
|
||||
from lit_nlp.api import model as lit_model
|
||||
from lit_nlp.api import types as lit_types
|
||||
from lit_nlp import notebook
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"LIT is not installed and is required to get Dataset as the return format. "
|
||||
'Please install the SDK using "pip install google-cloud-aiplatform[lit]"'
|
||||
)
|
||||
|
||||
try:
|
||||
import tensorflow as tf
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Tensorflow is not installed and is required to load saved model. "
|
||||
'Please install the SDK using "pip install google-cloud-aiplatform[lit]"'
|
||||
)
|
||||
|
||||
try:
|
||||
import pandas as pd
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Pandas is not installed and is required to read the dataset. "
|
||||
'Please install Pandas using "pip install google-cloud-aiplatform[lit]"'
|
||||
)
|
||||
|
||||
|
||||
class _VertexLitDataset(lit_dataset.Dataset):
|
||||
"""LIT dataset class for the Vertex LIT integration.
|
||||
|
||||
This is used in the create_lit_dataset function.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dataset: pd.DataFrame,
|
||||
column_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
):
|
||||
"""Construct a VertexLitDataset.
|
||||
Args:
|
||||
dataset:
|
||||
Required. A Pandas DataFrame that includes feature column names and data.
|
||||
column_types:
|
||||
Required. An OrderedDict of string names matching the columns of the dataset
|
||||
as the key, and the associated LitType of the column.
|
||||
"""
|
||||
self._examples = dataset.to_dict(orient="records")
|
||||
self._column_types = column_types
|
||||
|
||||
def spec(self):
|
||||
"""Return a spec describing dataset elements."""
|
||||
return dict(self._column_types)
|
||||
|
||||
|
||||
class _EndpointLitModel(lit_model.Model):
|
||||
"""LIT model class for the Vertex LIT integration with a model deployed to an endpoint.
|
||||
|
||||
This is used in the create_lit_model function.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
endpoint: Union[str, aiplatform.Endpoint],
|
||||
input_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
output_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
model_id: Optional[str] = None,
|
||||
):
|
||||
"""Construct a VertexLitModel.
|
||||
Args:
|
||||
model:
|
||||
Required. The name of the Endpoint resource. Format:
|
||||
``projects/{project}/locations/{location}/endpoints/{endpoint}``
|
||||
input_types:
|
||||
Required. An OrderedDict of string names matching the features of the model
|
||||
as the key, and the associated LitType of the feature.
|
||||
output_types:
|
||||
Required. An OrderedDict of string names matching the labels of the model
|
||||
as the key, and the associated LitType of the label.
|
||||
model_id:
|
||||
Optional. A string of the specific model in the endpoint to create the
|
||||
LIT model from. If this is not set, any usable model in the endpoint is
|
||||
used to create the LIT model.
|
||||
Raises:
|
||||
ValueError if the model_id was not found in the endpoint.
|
||||
"""
|
||||
if isinstance(endpoint, str):
|
||||
self._endpoint = aiplatform.Endpoint(endpoint)
|
||||
else:
|
||||
self._endpoint = endpoint
|
||||
self._model_id = model_id
|
||||
self._input_types = input_types
|
||||
self._output_types = output_types
|
||||
# Check if the model with the model ID has explanation enabled
|
||||
if model_id:
|
||||
deployed_model = next(
|
||||
filter(
|
||||
lambda model: model.id == model_id, self._endpoint.list_models()
|
||||
),
|
||||
None,
|
||||
)
|
||||
if not deployed_model:
|
||||
raise ValueError(
|
||||
"A model with id {model_id} was not found in the endpoint {endpoint}.".format(
|
||||
model_id=model_id, endpoint=endpoint
|
||||
)
|
||||
)
|
||||
self._explanation_enabled = bool(deployed_model.explanation_spec)
|
||||
# Check if all models in the endpoint have explanation enabled
|
||||
else:
|
||||
self._explanation_enabled = all(
|
||||
model.explanation_spec for model in self._endpoint.list_models()
|
||||
)
|
||||
|
||||
def predict_minibatch(
|
||||
self, inputs: List[lit_types.JsonDict]
|
||||
) -> List[lit_types.JsonDict]:
|
||||
"""Retun predictions based on a batch of inputs.
|
||||
Args:
|
||||
inputs: Requred. a List of instances to predict on based on the input spec.
|
||||
Returns:
|
||||
A list of predictions based on the output spec.
|
||||
"""
|
||||
instances = []
|
||||
for input in inputs:
|
||||
instance = [input[feature] for feature in self._input_types]
|
||||
instances.append(instance)
|
||||
if self._explanation_enabled:
|
||||
prediction_object = self._endpoint.explain(instances)
|
||||
else:
|
||||
prediction_object = self._endpoint.predict(instances)
|
||||
outputs = []
|
||||
for prediction in prediction_object.predictions:
|
||||
if isinstance(prediction, Mapping):
|
||||
outputs.append({key: prediction[key] for key in self._output_types})
|
||||
else:
|
||||
outputs.append(
|
||||
{key: prediction[i] for i, key in enumerate(self._output_types)}
|
||||
)
|
||||
if self._explanation_enabled:
|
||||
for i, explanation in enumerate(prediction_object.explanations):
|
||||
attributions = explanation.attributions
|
||||
outputs[i]["feature_attribution"] = lit_dtypes.FeatureSalience(
|
||||
attributions
|
||||
)
|
||||
return outputs
|
||||
|
||||
def input_spec(self) -> lit_types.Spec:
|
||||
"""Return a spec describing model inputs."""
|
||||
return dict(self._input_types)
|
||||
|
||||
def output_spec(self) -> lit_types.Spec:
|
||||
"""Return a spec describing model outputs."""
|
||||
output_spec_dict = dict(self._output_types)
|
||||
if self._explanation_enabled:
|
||||
output_spec_dict["feature_attribution"] = lit_types.FeatureSalience(
|
||||
signed=True
|
||||
)
|
||||
return output_spec_dict
|
||||
|
||||
|
||||
class _TensorFlowLitModel(lit_model.Model):
|
||||
"""LIT model class for the Vertex LIT integration with a TensorFlow saved model.
|
||||
|
||||
This is used in the create_lit_model function.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model: str,
|
||||
input_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
output_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
attribution_method: str = "sampled_shapley",
|
||||
):
|
||||
"""Construct a VertexLitModel.
|
||||
Args:
|
||||
model:
|
||||
Required. A string reference to a local TensorFlow saved model directory.
|
||||
The model must have at most one input and one output tensor.
|
||||
input_types:
|
||||
Required. An OrderedDict of string names matching the features of the model
|
||||
as the key, and the associated LitType of the feature.
|
||||
output_types:
|
||||
Required. An OrderedDict of string names matching the labels of the model
|
||||
as the key, and the associated LitType of the label.
|
||||
attribution_method:
|
||||
Optional. A string to choose what attribution configuration to
|
||||
set up the explainer with. Valid options are 'sampled_shapley'
|
||||
or 'integrated_gradients'.
|
||||
"""
|
||||
self._load_model(model)
|
||||
self._input_types = input_types
|
||||
self._output_types = output_types
|
||||
self._input_tensor_name = next(iter(self._kwargs_signature))
|
||||
self._attribution_explainer = None
|
||||
if os.environ.get("LIT_PROXY_URL"):
|
||||
self._set_up_attribution_explainer(model, attribution_method)
|
||||
|
||||
@property
|
||||
def attribution_explainer(
|
||||
self,
|
||||
) -> Optional["AttributionExplainer"]: # noqa: F821
|
||||
"""Gets the attribution explainer property if set."""
|
||||
return self._attribution_explainer
|
||||
|
||||
def predict_minibatch(
|
||||
self, inputs: List[lit_types.JsonDict]
|
||||
) -> List[lit_types.JsonDict]:
|
||||
"""Retun predictions based on a batch of inputs.
|
||||
Args:
|
||||
inputs: Requred. a List of instances to predict on based on the input spec.
|
||||
Returns:
|
||||
A list of predictions based on the output spec.
|
||||
"""
|
||||
instances = []
|
||||
for input in inputs:
|
||||
instance = [input[feature] for feature in self._input_types]
|
||||
instances.append(instance)
|
||||
prediction_input_dict = {
|
||||
self._input_tensor_name: tf.convert_to_tensor(instances)
|
||||
}
|
||||
prediction_dict = self._loaded_model.signatures[
|
||||
tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY
|
||||
](**prediction_input_dict)
|
||||
predictions = prediction_dict[next(iter(self._output_signature))].numpy()
|
||||
outputs = []
|
||||
for prediction in predictions:
|
||||
outputs.append(
|
||||
{
|
||||
label: value
|
||||
for label, value in zip(self._output_types.keys(), prediction)
|
||||
}
|
||||
)
|
||||
# Get feature attributions
|
||||
if self.attribution_explainer:
|
||||
attributions = self.attribution_explainer.explain(
|
||||
[{self._input_tensor_name: i} for i in instances]
|
||||
)
|
||||
for i, attribution in enumerate(attributions):
|
||||
outputs[i]["feature_attribution"] = lit_dtypes.FeatureSalience(
|
||||
attribution.feature_importance()
|
||||
)
|
||||
return outputs
|
||||
|
||||
def input_spec(self) -> lit_types.Spec:
|
||||
"""Return a spec describing model inputs."""
|
||||
return dict(self._input_types)
|
||||
|
||||
def output_spec(self) -> lit_types.Spec:
|
||||
"""Return a spec describing model outputs."""
|
||||
output_spec_dict = dict(self._output_types)
|
||||
if self.attribution_explainer:
|
||||
output_spec_dict["feature_attribution"] = lit_types.FeatureSalience(
|
||||
signed=True
|
||||
)
|
||||
return output_spec_dict
|
||||
|
||||
def _load_model(self, model: str):
|
||||
"""Loads a TensorFlow saved model and populates the input and output signature attributes of the class.
|
||||
Args:
|
||||
model: Required. A string reference to a TensorFlow saved model directory.
|
||||
Raises:
|
||||
ValueError if the model has more than one input tensor or more than one output tensor.
|
||||
"""
|
||||
self._loaded_model = tf.saved_model.load(model)
|
||||
serving_default = self._loaded_model.signatures[
|
||||
tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY
|
||||
]
|
||||
_, self._kwargs_signature = serving_default.structured_input_signature
|
||||
self._output_signature = serving_default.structured_outputs
|
||||
|
||||
if len(self._kwargs_signature) != 1:
|
||||
raise ValueError("Please use a model with only one input tensor.")
|
||||
|
||||
if len(self._output_signature) != 1:
|
||||
raise ValueError("Please use a model with only one output tensor.")
|
||||
|
||||
def _set_up_attribution_explainer(
|
||||
self, model: str, attribution_method: str = "integrated_gradients"
|
||||
):
|
||||
"""Populates the attribution explainer attribute of the class.
|
||||
Args:
|
||||
model: Required. A string reference to a TensorFlow saved model directory.
|
||||
attribution_method:
|
||||
Optional. A string to choose what attribution configuration to
|
||||
set up the explainer with. Valid options are 'sampled_shapley'
|
||||
or 'integrated_gradients'.
|
||||
"""
|
||||
try:
|
||||
import explainable_ai_sdk
|
||||
from explainable_ai_sdk.metadata.tf.v2 import SavedModelMetadataBuilder
|
||||
except ImportError:
|
||||
logging.info(
|
||||
"Skipping explanations because the Explainable AI SDK is not installed."
|
||||
'Please install the SDK using "pip install explainable-ai-sdk"'
|
||||
)
|
||||
return
|
||||
|
||||
builder = SavedModelMetadataBuilder(model)
|
||||
builder.get_metadata()
|
||||
builder.set_numeric_metadata(
|
||||
self._input_tensor_name,
|
||||
index_feature_mapping=list(self._input_types.keys()),
|
||||
)
|
||||
builder.save_metadata(model)
|
||||
if attribution_method == "integrated_gradients":
|
||||
explainer_config = explainable_ai_sdk.IntegratedGradientsConfig()
|
||||
else:
|
||||
explainer_config = explainable_ai_sdk.SampledShapleyConfig()
|
||||
|
||||
self._attribution_explainer = explainable_ai_sdk.load_model_from_local_path(
|
||||
model, explainer_config
|
||||
)
|
||||
self._load_model(model)
|
||||
|
||||
|
||||
def create_lit_dataset(
|
||||
dataset: pd.DataFrame,
|
||||
column_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
) -> lit_dataset.Dataset:
|
||||
"""Creates a LIT Dataset object.
|
||||
Args:
|
||||
dataset:
|
||||
Required. A Pandas DataFrame that includes feature column names and data.
|
||||
column_types:
|
||||
Required. An OrderedDict of string names matching the columns of the dataset
|
||||
as the key, and the associated LitType of the column.
|
||||
Returns:
|
||||
A LIT Dataset object that has the data from the dataset provided.
|
||||
"""
|
||||
return _VertexLitDataset(dataset, column_types)
|
||||
|
||||
|
||||
def create_lit_model_from_endpoint(
|
||||
endpoint: Union[str, aiplatform.Endpoint],
|
||||
input_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
output_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
model_id: Optional[str] = None,
|
||||
) -> lit_model.Model:
|
||||
"""Creates a LIT Model object.
|
||||
Args:
|
||||
model:
|
||||
Required. The name of the Endpoint resource or an Endpoint instance.
|
||||
Endpoint name format: ``projects/{project}/locations/{location}/endpoints/{endpoint}``
|
||||
input_types:
|
||||
Required. An OrderedDict of string names matching the features of the model
|
||||
as the key, and the associated LitType of the feature.
|
||||
output_types:
|
||||
Required. An OrderedDict of string names matching the labels of the model
|
||||
as the key, and the associated LitType of the label.
|
||||
model_id:
|
||||
Optional. A string of the specific model in the endpoint to create the
|
||||
LIT model from. If this is not set, any usable model in the endpoint is
|
||||
used to create the LIT model.
|
||||
Returns:
|
||||
A LIT Model object that has the same functionality as the model provided.
|
||||
"""
|
||||
return _EndpointLitModel(endpoint, input_types, output_types, model_id)
|
||||
|
||||
|
||||
def create_lit_model(
|
||||
model: str,
|
||||
input_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
output_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
attribution_method: str = "sampled_shapley",
|
||||
) -> lit_model.Model:
|
||||
"""Creates a LIT Model object.
|
||||
Args:
|
||||
model:
|
||||
Required. A string reference to a local TensorFlow saved model directory.
|
||||
The model must have at most one input and one output tensor.
|
||||
input_types:
|
||||
Required. An OrderedDict of string names matching the features of the model
|
||||
as the key, and the associated LitType of the feature.
|
||||
output_types:
|
||||
Required. An OrderedDict of string names matching the labels of the model
|
||||
as the key, and the associated LitType of the label.
|
||||
attribution_method:
|
||||
Optional. A string to choose what attribution configuration to
|
||||
set up the explainer with. Valid options are 'sampled_shapley'
|
||||
or 'integrated_gradients'.
|
||||
Returns:
|
||||
A LIT Model object that has the same functionality as the model provided.
|
||||
"""
|
||||
return _TensorFlowLitModel(model, input_types, output_types, attribution_method)
|
||||
|
||||
|
||||
def open_lit(
|
||||
models: Dict[str, lit_model.Model],
|
||||
datasets: Dict[str, lit_dataset.Dataset],
|
||||
open_in_new_tab: bool = True,
|
||||
):
|
||||
"""Open LIT from the provided models and datasets.
|
||||
Args:
|
||||
models:
|
||||
Required. A list of LIT models to open LIT with.
|
||||
input_types:
|
||||
Required. A lit of LIT datasets to open LIT with.
|
||||
open_in_new_tab:
|
||||
Optional. A boolean to choose if LIT open in a new tab or not.
|
||||
Raises:
|
||||
ImportError if LIT is not installed.
|
||||
"""
|
||||
widget = notebook.LitWidget(models, datasets)
|
||||
widget.render(open_in_new_tab=open_in_new_tab)
|
||||
|
||||
|
||||
def set_up_and_open_lit(
|
||||
dataset: Union[pd.DataFrame, lit_dataset.Dataset],
|
||||
column_types: "OrderedDict[str, lit_types.LitType]", # noqa: F821
|
||||
model: Union[str, lit_model.Model],
|
||||
input_types: Union[List[str], Dict[str, lit_types.LitType]],
|
||||
output_types: Union[str, List[str], Dict[str, lit_types.LitType]],
|
||||
attribution_method: str = "sampled_shapley",
|
||||
open_in_new_tab: bool = True,
|
||||
) -> Tuple[lit_dataset.Dataset, lit_model.Model]:
|
||||
"""Creates a LIT dataset and model and opens LIT.
|
||||
Args:
|
||||
dataset:
|
||||
Required. A Pandas DataFrame that includes feature column names and data.
|
||||
column_types:
|
||||
Required. An OrderedDict of string names matching the columns of the dataset
|
||||
as the key, and the associated LitType of the column.
|
||||
model:
|
||||
Required. A string reference to a TensorFlow saved model directory.
|
||||
The model must have at most one input and one output tensor.
|
||||
input_types:
|
||||
Required. An OrderedDict of string names matching the features of the model
|
||||
as the key, and the associated LitType of the feature.
|
||||
output_types:
|
||||
Required. An OrderedDict of string names matching the labels of the model
|
||||
as the key, and the associated LitType of the label.
|
||||
attribution_method:
|
||||
Optional. A string to choose what attribution configuration to
|
||||
set up the explainer with. Valid options are 'sampled_shapley'
|
||||
or 'integrated_gradients'.
|
||||
open_in_new_tab:
|
||||
Optional. A boolean to choose if LIT open in a new tab or not.
|
||||
Returns:
|
||||
A Tuple of the LIT dataset and model created.
|
||||
Raises:
|
||||
ImportError if LIT or TensorFlow is not installed.
|
||||
ValueError if the model doesn't have only 1 input and output tensor.
|
||||
"""
|
||||
if not isinstance(dataset, lit_dataset.Dataset):
|
||||
dataset = create_lit_dataset(dataset, column_types)
|
||||
|
||||
if not isinstance(model, lit_model.Model):
|
||||
model = create_lit_model(
|
||||
model, input_types, output_types, attribution_method=attribution_method
|
||||
)
|
||||
|
||||
open_lit(
|
||||
{"model": model},
|
||||
{"dataset": dataset},
|
||||
open_in_new_tab=open_in_new_tab,
|
||||
)
|
||||
|
||||
return dataset, model
|
||||
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
"""Base abstract class for metadata builders."""
|
||||
|
||||
import abc
|
||||
|
||||
_ABC = abc.ABCMeta("ABC", (object,), {"__slots__": ()})
|
||||
|
||||
|
||||
class MetadataBuilder(_ABC):
|
||||
"""Abstract base class for metadata builders."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_metadata(self):
|
||||
"""Returns the current metadata as a dictionary."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_metadata_protobuf(self):
|
||||
"""Returns the current metadata as ExplanationMetadata protobuf"""
|
||||
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,171 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from google.protobuf import json_format
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from google.cloud.aiplatform.compat.types import explanation_metadata
|
||||
from google.cloud.aiplatform.explain.metadata import metadata_builder
|
||||
|
||||
|
||||
class SavedModelMetadataBuilder(metadata_builder.MetadataBuilder):
|
||||
"""Metadata builder class that accepts a TF1 saved model."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_path: str,
|
||||
tags: Optional[List[str]] = None,
|
||||
signature_name: Optional[str] = None,
|
||||
outputs_to_explain: Optional[List[str]] = None,
|
||||
) -> None:
|
||||
"""Initializes a SavedModelMetadataBuilder object.
|
||||
|
||||
Args:
|
||||
model_path:
|
||||
Required. Local or GCS path to load the saved model from.
|
||||
tags:
|
||||
Optional. Tags to identify the model graph. If None or empty,
|
||||
TensorFlow's default serving tag will be used.
|
||||
signature_name:
|
||||
Optional. Name of the signature to be explained. Inputs and
|
||||
outputs of this signature will be written in the metadata. If not
|
||||
provided, the default signature will be used.
|
||||
outputs_to_explain:
|
||||
Optional. List of output names to explain. Only single output is
|
||||
supported for now. Hence, the list should contain one element.
|
||||
This parameter is required if the model signature (provided via
|
||||
signature_name) specifies multiple outputs.
|
||||
|
||||
Raises:
|
||||
ValueError: If outputs_to_explain contains more than 1 element or
|
||||
signature contains multiple outputs.
|
||||
"""
|
||||
if outputs_to_explain:
|
||||
if len(outputs_to_explain) > 1:
|
||||
raise ValueError(
|
||||
"Only one output is supported at the moment. "
|
||||
f"Received: {outputs_to_explain}."
|
||||
)
|
||||
self._output_to_explain = next(iter(outputs_to_explain))
|
||||
|
||||
try:
|
||||
import tensorflow.compat.v1 as tf
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Tensorflow is not installed and is required to load saved model. "
|
||||
'Please install the SDK using "pip install "tensorflow>=1.15,<2.0""'
|
||||
)
|
||||
|
||||
if not signature_name:
|
||||
signature_name = tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY
|
||||
self._tags = tags or [tf.saved_model.tag_constants.SERVING]
|
||||
self._graph = tf.Graph()
|
||||
|
||||
with self.graph.as_default():
|
||||
self._session = tf.Session(graph=self.graph)
|
||||
self._metagraph_def = tf.saved_model.loader.load(
|
||||
sess=self.session, tags=self._tags, export_dir=model_path
|
||||
)
|
||||
if signature_name not in self._metagraph_def.signature_def:
|
||||
raise ValueError(
|
||||
f"Serving sigdef key {signature_name} not in the signature def."
|
||||
)
|
||||
serving_sigdef = self._metagraph_def.signature_def[signature_name]
|
||||
if not outputs_to_explain:
|
||||
if len(serving_sigdef.outputs) > 1:
|
||||
raise ValueError(
|
||||
"The signature contains multiple outputs. Specify "
|
||||
'an output via "outputs_to_explain" parameter.'
|
||||
)
|
||||
self._output_to_explain = next(iter(serving_sigdef.outputs.keys()))
|
||||
|
||||
self._inputs = _create_input_metadata_from_signature(serving_sigdef.inputs)
|
||||
self._outputs = _create_output_metadata_from_signature(
|
||||
serving_sigdef.outputs, self._output_to_explain
|
||||
)
|
||||
|
||||
@property
|
||||
def graph(self) -> "tf.Graph": # noqa: F821
|
||||
return self._graph
|
||||
|
||||
@property
|
||||
def session(self) -> "tf.Session": # noqa: F821
|
||||
return self._session
|
||||
|
||||
def get_metadata(self) -> Dict[str, Any]:
|
||||
"""Returns the current metadata as a dictionary.
|
||||
|
||||
Returns:
|
||||
Json format of the explanation metadata.
|
||||
"""
|
||||
return json_format.MessageToDict(self.get_metadata_protobuf()._pb)
|
||||
|
||||
def get_metadata_protobuf(self) -> explanation_metadata.ExplanationMetadata:
|
||||
"""Returns the current metadata as a Protobuf object.
|
||||
|
||||
Returns:
|
||||
ExplanationMetadata object format of the explanation metadata.
|
||||
"""
|
||||
return explanation_metadata.ExplanationMetadata(
|
||||
inputs=self._inputs,
|
||||
outputs=self._outputs,
|
||||
)
|
||||
|
||||
|
||||
def _create_input_metadata_from_signature(
|
||||
signature_inputs: Dict[str, "tf.Tensor"] # noqa: F821
|
||||
) -> Dict[str, explanation_metadata.ExplanationMetadata.InputMetadata]:
|
||||
"""Creates InputMetadata from signature inputs.
|
||||
|
||||
Args:
|
||||
signature_inputs:
|
||||
Required. Inputs of the signature to be explained. If not provided,
|
||||
the default signature will be used.
|
||||
|
||||
Returns:
|
||||
Inferred input metadata from the model.
|
||||
"""
|
||||
input_mds = {}
|
||||
for key, tensor in signature_inputs.items():
|
||||
input_mds[key] = explanation_metadata.ExplanationMetadata.InputMetadata(
|
||||
input_tensor_name=tensor.name
|
||||
)
|
||||
return input_mds
|
||||
|
||||
|
||||
def _create_output_metadata_from_signature(
|
||||
signature_outputs: Dict[str, "tf.Tensor"], # noqa: F821
|
||||
output_to_explain: Optional[str] = None,
|
||||
) -> Dict[str, explanation_metadata.ExplanationMetadata.OutputMetadata]:
|
||||
"""Creates OutputMetadata from signature inputs.
|
||||
|
||||
Args:
|
||||
signature_outputs:
|
||||
Required. Inputs of the signature to be explained. If not provided,
|
||||
the default signature will be used.
|
||||
output_to_explain:
|
||||
Optional. Output name to explain.
|
||||
|
||||
Returns:
|
||||
Inferred output metadata from the model.
|
||||
"""
|
||||
output_mds = {}
|
||||
for key, tensor in signature_outputs.items():
|
||||
if not output_to_explain or output_to_explain == key:
|
||||
output_mds[key] = explanation_metadata.ExplanationMetadata.OutputMetadata(
|
||||
output_tensor_name=tensor.name
|
||||
)
|
||||
return output_mds
|
||||
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,139 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from google.protobuf import json_format
|
||||
from typing import Optional, List, Dict, Any, Tuple
|
||||
|
||||
from google.cloud.aiplatform.explain.metadata import metadata_builder
|
||||
from google.cloud.aiplatform.compat.types import explanation_metadata
|
||||
|
||||
|
||||
class SavedModelMetadataBuilder(metadata_builder.MetadataBuilder):
|
||||
"""Class for generating metadata for a model built with TF 2.X Keras API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_path: str,
|
||||
signature_name: Optional[str] = None,
|
||||
outputs_to_explain: Optional[List[str]] = None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
"""Initializes a SavedModelMetadataBuilder object.
|
||||
|
||||
Args:
|
||||
model_path:
|
||||
Required. Local or GCS path to load the saved model from.
|
||||
signature_name:
|
||||
Optional. Name of the signature to be explained. Inputs and
|
||||
outputs of this signature will be written in the metadata. If not
|
||||
provided, the default signature will be used.
|
||||
outputs_to_explain:
|
||||
Optional. List of output names to explain. Only single output is
|
||||
supported for now. Hence, the list should contain one element.
|
||||
This parameter is required if the model signature (provided via
|
||||
signature_name) specifies multiple outputs.
|
||||
**kwargs:
|
||||
Any keyword arguments to be passed to tf.saved_model.save() function.
|
||||
|
||||
Raises:
|
||||
ValueError: If outputs_to_explain contains more than 1 element.
|
||||
ImportError: If tf is not imported.
|
||||
"""
|
||||
if outputs_to_explain and len(outputs_to_explain) > 1:
|
||||
raise ValueError(
|
||||
'"outputs_to_explain" can only contain 1 element.\n'
|
||||
"Got: %s" % len(outputs_to_explain)
|
||||
)
|
||||
self._explain_output = outputs_to_explain
|
||||
self._saved_model_args = kwargs
|
||||
|
||||
try:
|
||||
import tensorflow as tf
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Tensorflow is not installed and is required to load saved model. "
|
||||
'Please install the SDK using "pip install google-cloud-aiplatform[full]"'
|
||||
)
|
||||
|
||||
if not signature_name:
|
||||
signature_name = tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY
|
||||
self._loaded_model = tf.saved_model.load(model_path)
|
||||
self._inputs, self._outputs = self._infer_metadata_entries_from_model(
|
||||
signature_name
|
||||
)
|
||||
|
||||
def _infer_metadata_entries_from_model(
|
||||
self, signature_name: str
|
||||
) -> Tuple[
|
||||
Dict[str, explanation_metadata.ExplanationMetadata.InputMetadata],
|
||||
Dict[str, explanation_metadata.ExplanationMetadata.OutputMetadata],
|
||||
]:
|
||||
"""Infers metadata inputs and outputs.
|
||||
|
||||
Args:
|
||||
signature_name:
|
||||
Required. Name of the signature to be explained. Inputs and outputs of this signature will be written in the metadata. If not provided, the default signature will be used.
|
||||
|
||||
Returns:
|
||||
Inferred input metadata and output metadata from the model.
|
||||
|
||||
Raises:
|
||||
ValueError: If specified name is not found in signature outputs.
|
||||
"""
|
||||
|
||||
loaded_sig = self._loaded_model.signatures[signature_name]
|
||||
_, input_sig = loaded_sig.structured_input_signature
|
||||
output_sig = loaded_sig.structured_outputs
|
||||
input_mds = {}
|
||||
for name, tensor_spec in input_sig.items():
|
||||
input_mds[name] = explanation_metadata.ExplanationMetadata.InputMetadata(
|
||||
input_tensor_name=name,
|
||||
modality=None if tensor_spec.dtype.is_floating else "categorical",
|
||||
)
|
||||
|
||||
output_mds = {}
|
||||
for name in output_sig:
|
||||
if not self._explain_output or self._explain_output[0] == name:
|
||||
output_mds[
|
||||
name
|
||||
] = explanation_metadata.ExplanationMetadata.OutputMetadata(
|
||||
output_tensor_name=name,
|
||||
)
|
||||
break
|
||||
else:
|
||||
raise ValueError(
|
||||
"Specified output name cannot be found in given signature outputs."
|
||||
)
|
||||
return input_mds, output_mds
|
||||
|
||||
def get_metadata(self) -> Dict[str, Any]:
|
||||
"""Returns the current metadata as a dictionary.
|
||||
|
||||
Returns:
|
||||
Json format of the explanation metadata.
|
||||
"""
|
||||
return json_format.MessageToDict(self.get_metadata_protobuf()._pb)
|
||||
|
||||
def get_metadata_protobuf(self) -> explanation_metadata.ExplanationMetadata:
|
||||
"""Returns the current metadata as a Protobuf object.
|
||||
|
||||
Returns:
|
||||
ExplanationMetadata object format of the explanation metadata.
|
||||
"""
|
||||
return explanation_metadata.ExplanationMetadata(
|
||||
inputs=self._inputs,
|
||||
outputs=self._outputs,
|
||||
)
|
||||
Reference in New Issue
Block a user