chore(components/pytorch): Samples Unit tests and lint fixes (#6288)

* Applying yapf on all the python files in samples

Signed-off-by: Shrinath Suresh <shrinath@ideas2it.com>

* Test compile yaml fixes

Signed-off-by: Shrinath Suresh <shrinath@ideas2it.com>

* Remove unused file

Signed-off-by: Shrinath Suresh <shrinath@ideas2it.com>

* Lint fixes: generate_templates.py

Signed-off-by: Shrinath Suresh <shrinath@ideas2it.com>

* Lint fixes:  gen_image_timestamp.py

Signed-off-by: Shrinath Suresh <shrinath@ideas2it.com>

* Lint fixes

Signed-off-by: Shrinath Suresh <shrinath@ideas2it.com>

* Cifar10 handler lint fixes

Signed-off-by: Shrinath Suresh <shrinath@ideas2it.com>

* Lint fixes for the remaining files

Signed-off-by: Shrinath Suresh <shrinath@ideas2it.com>
This commit is contained in:
shrinath-suresh 2021-08-12 11:29:02 +05:30 committed by GitHub
parent 6f4a4f1f83
commit b4ad3d1488
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 234 additions and 259 deletions

View File

@ -34,7 +34,7 @@ implementation:
# For GPU use
# image: public.ecr.aws/pytorch-samples/kfp_samples:latest-gpu
image: "docker image name" # Ex: public.ecr.aws/pytorch-samples/kfp_samples:latest
command: "command to execute" # Ex: ['python3', 'cifar10/cifar10_pytorch.py']
command: ["command to execute"] # Ex: ['python3', 'cifar10/cifar10_pytorch.py']
args:
- --dataset_path
- {inputPath: input_data}

View File

@ -24,7 +24,7 @@ implementation:
# For GPU use
# image: public.ecr.aws/pytorch-samples/kfp_samples:latest-gpu
image: public.ecr.aws/pytorch-samples/kfp_samples:latest
command: "command to execute. Ex: ['python3', 'bert/bert_pre_process.py']"
command: ["command to execute"] #Ex: ['python3', 'bert/bert_pre_process.py']
args:
- --output_path
- {outputPath: output_data}

View File

@ -29,7 +29,7 @@ implementation:
# For GPU use
# image: public.ecr.aws/pytorch-samples/kfp_samples:latest-gpu
image: public.ecr.aws/pytorch-samples/kfp_samples:latest
command: "command to execute. Ex: ['python3', 'bert/agnews_classification_pytorch.py']"
command: ["command to execute"] #Ex: ['python3', 'bert/agnews_classification_pytorch.py']
args:
- --dataset_path
- {inputPath: input_data}

View File

@ -14,6 +14,7 @@
# limitations under the License.
#pylint: disable=not-callable,unused-variable
"""Test for component compilation."""
import os
import unittest
import json
import pytest
@ -22,27 +23,36 @@ from kfp.components import load_component_from_file
from kfp import dsl
from kfp import compiler
tests_dir, _ = os.path.split(os.path.abspath(__file__))
templates_dir = os.path.join(os.path.dirname(tests_dir), "templates")
BERT_COMPONENTS = {
"component_bert_prep":
components.
load_component_from_file("bert/yaml/pre_process/component.yaml"),
load_component_from_file(f"{templates_dir}/preprocess_component.yaml"),
"component_bert_train":
components.load_component_from_file("bert/yaml/train/component.yaml"),
components.
load_component_from_file(f"{templates_dir}/train_component.yaml"),
}
CIFAR_COMPONENTS = {
"component_cifar10_prep":
components.
load_component_from_file("./cifar10/yaml/pre_process/component.yaml"),
load_component_from_file(f"{templates_dir}/preprocess_component.yaml"),
"component_cifar10_train":
components.
load_component_from_file("./cifar10/yaml/train/component.yaml"),
load_component_from_file(f"{templates_dir}/train_component.yaml"),
}
COMPONENT_TB = load_component_from_file("common/tensorboard/component.yaml")
COMPONENT_DEPLOY = load_component_from_file("common/deploy/component.yaml")
COMPONENT_MNIO = components.load_component_from_file(
"./common/minio/component.yaml"
COMPONENT_TB = load_component_from_file(
f"{templates_dir}/tensorboard_component.yaml"
)
PRED_OP = load_component_from_file("./common/prediction/component.yaml")
COMPONENT_DEPLOY = load_component_from_file(
f"{templates_dir}/deploy_component.yaml"
)
COMPONENT_MNIO = components.load_component_from_file(
f"{templates_dir}/minio_component.yaml"
)
PRED_OP = load_component_from_file(f"{templates_dir}/prediction_component.yaml")
class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instance-attributes
@ -50,7 +60,7 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
def setUp(self):
"""Set up."""
super(ComponentCompileTest, self).setUp()
super().setUp()
self.input_request = "./compile_test.json"
self.deploy_name_bert = "bertserve"
self.namespace = "kubeflow-user-example-com"
@ -76,18 +86,18 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
description="Cifar 10 dataset pipeline",
) #pylint: disable=too-many-arguments,too-many-locals
def pytorch_cifar10(
minio_endpoint=self.minio_endpoint,
log_bucket=self.log_bucket,
log_dir=f"tensorboard/logs/{dsl.RUN_ID_PLACEHOLDER}",
mar_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/model-store",
config_prop_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/config",
model_uri=f"s3://mlpipeline/mar/{dsl.RUN_ID_PLACEHOLDER}",
tf_image=self.tensorboard_image,
deploy=self.deploy_name_cifar,
namespace=self.namespace,
confusion_matrix_log_dir=f"confusion_matrix"
f"/{dsl.RUN_ID_PLACEHOLDER}/",
checkpoint_dir=f"checkpoint_dir/cifar10",
minio_endpoint=self.minio_endpoint,
log_bucket=self.log_bucket,
log_dir=f"tensorboard/logs/{dsl.RUN_ID_PLACEHOLDER}",
mar_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/model-store",
config_prop_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/config",
model_uri=f"s3://mlpipeline/mar/{dsl.RUN_ID_PLACEHOLDER}",
tf_image=self.tensorboard_image,
deploy=self.deploy_name_cifar,
namespace=self.namespace,
confusion_matrix_log_dir=f"confusion_matrix"
f"/{dsl.RUN_ID_PLACEHOLDER}/",
checkpoint_dir="checkpoint_dir/cifar10",
):
"""Cifar10 pipelines."""
pod_template_spec = json.dumps({
@ -145,15 +155,20 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
)
component_cifar10_train = CIFAR_COMPONENTS["component_cifar10_train"
]
confusion_matrix_url = \
f"minio://{log_bucket}/{confusion_matrix_log_dir}"
script_args = f"model_name=resnet.pth," \
f"confusion_matrix_url={confusion_matrix_url}"
# For gpus, set number of gpus and accelerator type
ptl_args = "max_epochs=1, " \
"gpus=0, " \
"accelerator=None, " \
"profiler=pytorch"
train_task = (
component_cifar10_train(
input_data=prep_task.outputs["output_data"],
profiler="pytorch",
confusion_matrix_url=
f"minio://{log_bucket}/{confusion_matrix_log_dir}",
# For GPU set gpu count and accelerator type
gpus=0,
accelerator="None",
script_args=script_args,
ptl_arguments=ptl_args
).after(prep_task).set_display_name("Training")
)
@ -270,17 +285,19 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
name="Training pipeline", description="Sample training job test"
) #pylint: disable=too-many-arguments,too-many-locals
def pytorch_bert(
minio_endpoint=self.minio_endpoint,
log_bucket=self.log_bucket,
log_dir=f"tensorboard/logs/{dsl.RUN_ID_PLACEHOLDER}",
mar_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/model-store",
config_prop_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/config",
model_uri=f"s3://mlpipeline/mar/{dsl.RUN_ID_PLACEHOLDER}",
tf_image=self.tensorboard_image,
deploy=self.deploy_name_bert,
namespace=self.namespace,
confusion_matrix_log_dir=f"confusion_matrix/{dsl.RUN_ID_PLACEHOLDER}/",
num_samples=1000,
minio_endpoint=self.minio_endpoint,
log_bucket=self.log_bucket,
log_dir=f"tensorboard/logs/{dsl.RUN_ID_PLACEHOLDER}",
mar_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/model-store",
config_prop_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/config",
model_uri=f"s3://mlpipeline/mar/{dsl.RUN_ID_PLACEHOLDER}",
tf_image=self.tensorboard_image,
deploy=self.deploy_name_bert,
namespace=self.namespace,
confusion_matrix_log_dir=
f"confusion_matrix/{dsl.RUN_ID_PLACEHOLDER}/",
num_samples=1000,
max_epochs=1
):
"""Bert Pipeline."""
prepare_tb_task = COMPONENT_TB(
@ -335,16 +352,21 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
set_display_name("Preprocess & Transform")
)
component_bert_train = BERT_COMPONENTS["component_bert_train"]
confusion_matrix_url = \
f"minio://{log_bucket}/{confusion_matrix_log_dir}"
script_args = f"model_name=bert.pth," \
f"num_samples={num_samples}," \
f"confusion_matrix_url={confusion_matrix_url}"
# For gpus, set number of gpus and accelerator type
ptl_args = f"max_epochs={max_epochs}," \
"profiler=pytorch," \
"gpus=0," \
"accelerator=None"
train_task = (
component_bert_train(
input_data=prep_task.outputs["output_data"],
profiler="pytorch",
confusion_matrix_url=
f"minio://{log_bucket}/{confusion_matrix_log_dir}",
num_samples=num_samples,
# For GPU set gpu count and accelerator type
gpus=0,
accelerator="None",
script_args=script_args,
ptl_arguments=ptl_args
).after(prep_task).set_display_name("Training")
)
@ -388,7 +410,7 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
storageUri: {}
resources:
limits:
memory: 4Gi
memory: 4Gi
""".format(deploy, namespace, model_uri)
# For GPU inference use below yaml with gpu count and accelerator
@ -407,7 +429,7 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
storageUri: {}
resources:
limits:
memory: 4Gi
memory: 4Gi
nvidia.com/gpu: {}
nodeSelector:
cloud.google.com/gke-accelerator: {}
@ -431,18 +453,18 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
description="Cifar 10 dataset pipeline",
) #pylint: disable=too-many-arguments,too-many-locals
def pytorch_cifar10(
minio_endpoint=self.minio_endpoint,
log_bucket=self.log_bucket,
log_dir=f"tensorboard/logs/{dsl.RUN_ID_PLACEHOLDER}",
mar_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/model-store",
config_prop_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/config",
model_uri=f"s3://mlpipeline/mar/{dsl.RUN_ID_PLACEHOLDER}",
tf_image=self.tensorboard_image,
deploy=self.deploy_name_cifar,
namespace=self.namespace,
confusion_matrix_log_dir=f"confusion_matrix"
f"/{dsl.RUN_ID_PLACEHOLDER}/",
checkpoint_dir=f"checkpoint_dir/cifar10",
minio_endpoint=self.minio_endpoint,
log_bucket=self.log_bucket,
log_dir=f"tensorboard/logs/{dsl.RUN_ID_PLACEHOLDER}",
mar_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/model-store",
config_prop_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/config",
model_uri=f"s3://mlpipeline/mar/{dsl.RUN_ID_PLACEHOLDER}",
tf_image=self.tensorboard_image,
deploy=self.deploy_name_cifar,
namespace=self.namespace,
confusion_matrix_log_dir=f"confusion_matrix"
f"/{dsl.RUN_ID_PLACEHOLDER}/", #pylint: disable=f-string-without-interpolation
checkpoint_dir=f"checkpoint_dir/cifar10",
):
"""Cifar10 pipelines."""
pod_template_spec = json.dumps({
@ -498,16 +520,21 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
component_cifar10_prep().after(prepare_tb_task).
set_display_name("Preprocess & Transform")
)
confusion_matrix_url = \
f"minio://{log_bucket}/{confusion_matrix_log_dir}"
script_args = f"model_name=resnet.pth," \
f"confusion_matrix_url={confusion_matrix_url}"
# For gpus, set number of gpus and accelerator type
ptl_args = "max_epochs=1, " \
"gpus=0, " \
"accelerator=None, " \
"profiler=pytorch"
component_cifar10_train = ""
train_task = (
component_cifar10_train(
input_data=prep_task.outputs["output_data"],
profiler="pytorch",
confusion_matrix_url=
f"minio://{log_bucket}/{confusion_matrix_log_dir}",
# For GPU set gpu count and accelerator type
gpus=0,
accelerator="None",
script_args=script_args,
ptl_arguments=ptl_args
).after(prep_task).set_display_name("Training")
)
@ -581,7 +608,7 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
storageUri: {}
resources:
limits:
memory: 4Gi
memory: 4Gi
nvidia.com/gpu: {}
nodeSelector:
cloud.google.com/gke-accelerator: {}
@ -612,10 +639,12 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
inference_type="explain",
).after(pred_task).set_display_name("Explanation")
)
with pytest.raises(TypeError):
compiler.Compiler().compile(
pytorch_cifar10, "pytorch.tar.gz", type_check=True
)
def test_bert_compile_fail(self):
"""Test bert yamls compilation."""
@ -623,17 +652,19 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
name="Training pipeline", description="Sample training job test"
) #pylint: disable=too-many-arguments,too-many-locals
def pytorch_bert(
minio_endpoint=self.minio_endpoint,
log_bucket=self.log_bucket,
log_dir=f"tensorboard/logs/{dsl.RUN_ID_PLACEHOLDER}",
mar_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/model-store",
config_prop_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/config",
model_uri=f"s3://mlpipeline/mar/{dsl.RUN_ID_PLACEHOLDER}",
tf_image=self.tensorboard_image,
deploy=self.deploy_name_bert,
namespace=self.namespace,
confusion_matrix_log_dir=f"confusion_matrix/{dsl.RUN_ID_PLACEHOLDER}/",
num_samples=1000,
minio_endpoint=self.minio_endpoint,
log_bucket=self.log_bucket,
log_dir=f"tensorboard/logs/{dsl.RUN_ID_PLACEHOLDER}",
mar_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/model-store",
config_prop_path=f"mar/{dsl.RUN_ID_PLACEHOLDER}/config",
model_uri=f"s3://mlpipeline/mar/{dsl.RUN_ID_PLACEHOLDER}",
tf_image=self.tensorboard_image,
deploy=self.deploy_name_bert,
namespace=self.namespace,
confusion_matrix_log_dir=
f"confusion_matrix/{dsl.RUN_ID_PLACEHOLDER}/",
num_samples=1000,
max_epochs=1
):
"""Bert Pipeline."""
prepare_tb_task = COMPONENT_TB(
@ -687,17 +718,22 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
component_bert_prep().after(prepare_tb_task).
set_display_name("Preprocess & Transform")
)
confusion_matrix_url = \
f"minio://{log_bucket}/{confusion_matrix_log_dir}"
script_args = f"model_name=bert.pth," \
f"num_samples={num_samples}," \
f"confusion_matrix_url={confusion_matrix_url}"
# For gpus, set number of gpus and accelerator type
ptl_args = f"max_epochs={max_epochs}," \
"profiler=pytorch," \
"gpus=0," \
"accelerator=None"
component_bert_train = ""
train_task = (
component_bert_train(
input_data=prep_task.outputs["output_data"],
profiler="pytorch",
confusion_matrix_url=
f"minio://{log_bucket}/{confusion_matrix_log_dir}",
num_samples=num_samples,
# For GPU set gpu count and accelerator type
gpus=0,
accelerator="None",
script_args=script_args,
ptl_arguments=ptl_args
).after(prep_task).set_display_name("Training")
)
@ -741,7 +777,7 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
storageUri: {}
resources:
limits:
memory: 4Gi
memory: 4Gi
""".format(deploy, namespace, model_uri)
# For GPU inference use below yaml with gpu count and accelerator
@ -760,7 +796,7 @@ class ComponentCompileTest(unittest.TestCase): #pylint: disable=too-many-instan
storageUri: {}
resources:
limits:
memory: 4Gi
memory: 4Gi
nvidia.com/gpu: {}
nodeSelector:
cloud.google.com/gke-accelerator: {}

View File

@ -27,7 +27,7 @@ from pytorch_kfp_components.components.mar.component import MarGeneration
from pytorch_kfp_components.components.utils.argument_parsing import parse_input_args
# Argument parser for user defined paths
import pytorch_lightning
print("Using Pytorch Lighting: {}".format(pytorch_lightning.__version__))
print("Using Pytorch Lighting: {}".format(pytorch_lightning.__version__)) #pylint: disable=no-member
parser = ArgumentParser()
parser.add_argument(
@ -236,4 +236,3 @@ if trainer.ptl_trainer.global_rank == 0:
mlpipeline_metrics=args["mlpipeline_metrics"],
markdown=markdown_dict,
)

View File

@ -103,7 +103,7 @@ class NewsClassifierHandler(BaseHandler): # pylint: disable=too-many-instance-a
self.vocab_file
)
)
self.input_ids = torch.tensor( # pylint: disable=attribute-defined-outside-init,not-callable
self.input_ids = torch.tensor( # pylint: disable=attribute-defined-outside-init,not-callable,no-member
[
self.tokenizer.encode(self.text, add_special_tokens=True)
]
@ -114,7 +114,8 @@ class NewsClassifierHandler(BaseHandler): # pylint: disable=too-many-instance-a
"""Predict the class for a review / sentence whether
it is belong to world / sports / business /sci-tech.
Args:
encoding: Input encoding to be passed through the layers for prediction
encoding:
Input encoding to be passed through the layers for prediction
Returns:
output - predicted output
@ -197,7 +198,8 @@ class NewsClassifierHandler(BaseHandler): # pylint: disable=too-many-instance-a
"""Captum explanations handler.
Args:
data_preprocess (Torch Tensor): Preprocessed data to be used for captum
data_preprocess (Torch Tensor):
Preprocessed data to be used for captum
raw_data (list): The unprocessed data to get target from the request
Returns:
dict : A dictionary response with the explanations response.
@ -206,7 +208,7 @@ class NewsClassifierHandler(BaseHandler): # pylint: disable=too-many-instance-a
tokenizer = BertTokenizer(self.vocab_file)
model_wrapper.eval()
model_wrapper.zero_grad()
input_ids = torch.tensor([
input_ids = torch.tensor([ # pylint: disable=no-member
tokenizer.encode(self.text, add_special_tokens=True)
])
input_ids = input_ids.to(self.device)
@ -223,7 +225,9 @@ class NewsClassifierHandler(BaseHandler): # pylint: disable=too-many-instance-a
return_convergence_delta=True,
target=1,
)
tokens = tokenizer.convert_ids_to_tokens(input_ids[0].cpu().numpy().tolist())
tokens = tokenizer.convert_ids_to_tokens(
input_ids[0].cpu().numpy().tolist()
)
feature_imp_dict = {}
feature_imp_dict["words"] = tokens
attributions_sum = self.summarize_attributions(attributions)

View File

@ -29,7 +29,8 @@ if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument(
"--dataset_url",
default="https://kubeflow-dataset.s3.us-east-2.amazonaws.com/ag_news_csv.tar.gz",
default=
"https://kubeflow-dataset.s3.us-east-2.amazonaws.com/ag_news_csv.tar.gz", #pylint: disable=line-too-long
type=str,
help="URL to download AG News dataset",
)
@ -68,7 +69,9 @@ if __name__ == "__main__":
print(run_code.stdout)
visualization_arguments = {
"inputs": {"dataset_url": args["dataset_url"]},
"inputs": {
"dataset_url": args["dataset_url"]
},
"output": {
"mlpipeline_ui_metadata": args["mlpipeline_ui_metadata"],
},

View File

@ -119,7 +119,6 @@ class BertNewsClassifier(pl.LightningModule): #pylint: disable=too-many-ancesto
output - Type of news for the given news snippet
"""
embedding_input = self.bert_model.embeddings(input_ids)
# output = self.bert_model(input_ids=input_ids, attention_mask=attention_mask)
outputs = self.compute_bert_outputs(self.bert_model, embedding_input)
pooled_output = outputs[1]
output = F.relu(self.fc1(pooled_output))
@ -169,7 +168,7 @@ class BertNewsClassifier(pl.LightningModule): #pylint: disable=too-many-ancesto
self.preds += y_hat.tolist()
self.target += targets.tolist()
self.log("test_acc", self.test_acc.compute())
return {"test_acc": torch.tensor(test_acc)} #pylint: disable=not-callable
return {"test_acc": torch.tensor(test_acc)} #pylint: disable=no-member
def validation_step(self, val_batch, batch_idx):
"""Performs validation of data in batches.

View File

@ -54,7 +54,8 @@ class NewsDataset(Dataset):
item: Index of sample review
Returns:
Returns the dictionary of review text, input ids, attention mask, targets
Returns the dictionary of review text,
input ids, attention mask, targets
"""
review = str(self.reviews[item])
target = self.targets[item]

View File

@ -57,10 +57,8 @@ class AGNewsmodelWrapper(nn.Module):
if head_mask is not None:
if head_mask.dim() == 1:
head_mask = (
head_mask.unsqueeze(0)
.unsqueeze(0)
.unsqueeze(-1)
.unsqueeze(-1)
head_mask.unsqueeze(0).unsqueeze(0).unsqueeze(-1).
unsqueeze(-1)
)
head_mask = head_mask.expand(
model_bert.config.num_hidden_layers, -1, -1, -1, -1

View File

@ -11,29 +11,29 @@
# 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.
# pylint: disable=no-self-use,too-many-arguments,unused-argument,not-callable
# pylint: disable=no-self-use,too-many-arguments,unused-argument,not-callable,no-member,attribute-defined-outside-init
""" Cifar10 Custom Handler."""
from abc import ABC
import io
import os
import base64
import io
import json
import logging
import os
from abc import ABC
from base64 import b64encode
from io import BytesIO
import numpy as np
import torch
from PIL import Image
from matplotlib.colors import LinearSegmentedColormap
from captum.attr import visualization as viz
from captum.attr import (
IntegratedGradients, Occlusion, LayerGradCam, LayerAttribution
)
from ts.torch_handler.image_classifier import ImageClassifier
from captum.attr import visualization as viz
from classifier import CIFAR10CLASSIFIER
import logging
from matplotlib.colors import LinearSegmentedColormap
from torchvision import transforms
import torch
from io import BytesIO
from base64 import b64encode
from matplotlib import pyplot as plt
from ts.torch_handler.image_classifier import ImageClassifier
logger = logging.getLogger(__name__)
@ -43,7 +43,7 @@ class CIFAR10Classification(ImageClassifier, ABC):
Base class for all vision handlers
"""
def initialize(self, ctx):
def initialize(self, ctx): # pylint: disable=arguments-differ
"""In this initialize function, the CIFAR10 trained model is loaded and
the Integrated Gradients,occlusion and layer_gradcam Algorithm for
Captum Explanations is initialized here.
@ -73,8 +73,8 @@ class CIFAR10Classification(ImageClassifier, ABC):
mapping_file_path = os.path.join(model_dir, "class_mapping.json")
if os.path.isfile(mapping_file_path):
print("Mapping file present")
with open(mapping_file_path) as f:
self.mapping = json.load(f)
with open(mapping_file_path) as pointer:
self.mapping = json.load(pointer)
else:
print("Mapping file missing")
logger.warning("Missing the class_mapping.json file.")
@ -96,8 +96,8 @@ class CIFAR10Classification(ImageClassifier, ABC):
def _get_img(self, row):
"""Compat layer: normally the envelope should just return the data
directly, but older version of KFServing envelope and Torchserve in general
didn't have things set up right
directly, but older version of KFServing envelope and
Torchserve in general didn't have things set up right
"""
if isinstance(row, dict):
@ -112,11 +112,13 @@ class CIFAR10Classification(ImageClassifier, ABC):
return image
def preprocess(self, data):
"""The preprocess function of cifar10 program converts the input data to a float tensor
"""The preprocess function of cifar10 program
converts the input data to a float tensor
Args:
data (List): Input data from the request is in the form of a Tensor
Returns:
list : The preprocess function returns the input image as a list of float tensors.
list : The preprocess function returns
the input image as a list of float tensors.
"""
images = []
@ -136,13 +138,15 @@ class CIFAR10Classification(ImageClassifier, ABC):
return torch.stack(images).to(self.device)
def attribute_image_features(self, algorithm, data, **kwargs):
"""Calculate tensor attributions"""
self.model.zero_grad()
tensor_attributions = algorithm.attribute(data, target=0, **kwargs)
return tensor_attributions
def output_bytes(self, fig):
"""Convert image to bytes"""
fout = BytesIO()
fig.savefig(fout, format='png')
fig.savefig(fout, format="png")
fout.seek(0)
return fout.getvalue()

View File

@ -281,4 +281,4 @@ if trainer.ptl_trainer.global_rank == 0:
)
checpoint_dir_contents = os.listdir(CHECKPOINT_DIR)
print(f"Checkpoint Directory Contents: {checpoint_dir_contents}")
print(f"Checkpoint Directory Contents: {checpoint_dir_contents}")

View File

@ -1,113 +0,0 @@
import pandas as pd
import json
import os
from sklearn.metrics import confusion_matrix
from io import StringIO
import boto3
class Visualization:
def __init__(self):
self.parser_args = None
def _generate_confusion_matrix_metadata(self, confusion_matrix_path, vocab):
print("Generating Confusion matrix Metadata")
metadata = {
"type": "confusion_matrix",
"format": "csv",
"schema": [
{"name": "target", "type": "CATEGORY"},
{"name": "predicted", "type": "CATEGORY"},
{"name": "count", "type": "NUMBER"},
],
"source": confusion_matrix_path,
# Convert vocab to string because for bealean values we want "True|False" to match csv data.
"labels": list(map(str, vocab)),
}
self._write_ui_metadata(
metadata_filepath="/mlpipeline-ui-metadata.json", metadata_dict=metadata
)
def _write_ui_metadata(self, metadata_filepath, metadata_dict, key="outputs"):
if not os.path.exists(metadata_filepath):
metadata = {key: [metadata_dict]}
else:
with open(metadata_filepath) as fp:
metadata = json.load(fp)
metadata_outputs = metadata[key]
metadata_outputs.append(metadata_dict)
print("Writing to file: {}".format(metadata_filepath))
with open(metadata_filepath, "w") as fp:
json.dump(metadata, fp)
def _enable_tensorboard_visualization(self, tensorboard_root):
print("Enabling Tensorboard Visualization")
metadata = {
"type": "tensorboard",
"source": tensorboard_root,
}
import os
os.environ["AWS_REGION"] = "us-east-2"
self._write_ui_metadata(
metadata_filepath="/mlpipeline-ui-metadata.json", metadata_dict=metadata
)
def _visualize_accuracy_metric(self, accuracy):
metadata = {
"name": "accuracy-score",
"numberValue": accuracy,
"format": "PERCENTAGE",
}
self._write_ui_metadata(
metadata_filepath="/mlpipeline-metrics.json", metadata_dict=metadata, key="metrics"
)
def _generate_confusion_matrix(self, confusion_matrix_dict):
actuals = confusion_matrix_dict["actuals"]
preds = confusion_matrix_dict["preds"]
bucket_name = confusion_matrix_dict["bucket_name"]
folder_name = confusion_matrix_dict["folder_name"]
# Generating confusion matrix
df = pd.DataFrame(list(zip(actuals, preds)), columns=["target", "predicted"])
vocab = list(df["target"].unique())
cm = confusion_matrix(df["target"], df["predicted"], labels=vocab)
data = []
for target_index, target_row in enumerate(cm):
for predicted_index, count in enumerate(target_row):
data.append((vocab[target_index], vocab[predicted_index], count))
confusion_matrix_df = pd.DataFrame(data, columns=["target", "predicted", "count"])
confusion_matrix_key = os.path.join(folder_name, "confusion_df.csv")
# Logging confusion matrix to ss3
csv_buffer = StringIO()
confusion_matrix_df.to_csv(csv_buffer, index=False, header=False)
s3_resource = boto3.resource("s3")
s3_resource.Object(bucket_name, confusion_matrix_key).put(Body=csv_buffer.getvalue())
# Generating metadata
confusion_matrix_s3_path = s3_path = "s3://" + bucket_name + "/" + confusion_matrix_key
self._generate_confusion_matrix_metadata(confusion_matrix_s3_path, vocab)
def generate_visualization(
self, tensorboard_root=None, accuracy=None, confusion_matrix_dict=None
):
print("Tensorboard Root: {}".format(tensorboard_root))
print("Accuracy: {}".format(accuracy))
if tensorboard_root:
self._enable_tensorboard_visualization(tensorboard_root)
if accuracy:
self._visualize_accuracy_metric(accuracy=accuracy)
if confusion_matrix_dict:
self._generate_confusion_matrix(confusion_matrix_dict=confusion_matrix_dict)

View File

@ -11,6 +11,7 @@
# 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.
"""Utility to upload files/folders into minio"""
import os
from argparse import ArgumentParser
from pytorch_kfp_components.components.minio.component import MinIO
@ -72,7 +73,12 @@ print("File to be uploaded: {}".format(input_path))
print("Uploading file to : {}".format(folder_name))
MinIO(source=input_path, bucket_name=bucket_name, destination=folder_name, endpoint=endpoint)
MinIO(
source=input_path,
bucket_name=bucket_name,
destination=folder_name,
endpoint=endpoint
)
inputs = {}
@ -88,7 +94,7 @@ if filename:
outputs["minio_url"] = s3_url
visualization_arguments = {"inputs" : inputs, "outputs" : outputs}
visualization_arguments = {"inputs": inputs, "outputs": outputs}
markdown_dict = {"storage": "inline", "source": visualization_arguments}
@ -96,4 +102,3 @@ visualization = Visualization(
mlpipeline_ui_metadata=args["mlpipeline_ui_metadata"],
markdown=markdown_dict,
)

View File

@ -1,5 +1,19 @@
#!/usr/bin/env/python3
# Copyright (c) Facebook, Inc. and its affiliates.
# 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.
"""Utility to generate timestamp"""
from datetime import datetime
dateTimeObj = datetime.now()
timestampStr = dateTimeObj.strftime("%d-%m-%Y-%H-%M-%S.%f")
print(timestampStr, end='')
print(timestampStr, end="")

View File

@ -1,19 +1,37 @@
#!/usr/bin/env/python3
# Copyright (c) Facebook, Inc. and its affiliates.
# 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.
"""Generate component.yaml from templates"""
import json
import os
import yaml
import shutil
import sys
import json
import yaml
CURRENT_FOLDER = os.path.dirname(os.path.abspath(__file__))
PIPELINES_HOME = os.path.join(CURRENT_FOLDER.split("pipelines")[0], "pipelines")
TEMPLATE_PATH = os.path.join(PIPELINES_HOME, "components/PyTorch/pytorch-kfp-components/templates")
TEMPLATE_PATH = os.path.join(
PIPELINES_HOME, "components/PyTorch/pytorch-kfp-components/templates"
)
OUTPUT_YAML_FOLDER = "yaml"
def create_output_folder():
"""Removes the `yaml` folder and recreates it"""
if os.path.exists(OUTPUT_YAML_FOLDER):
shutil.rmtree(OUTPUT_YAML_FOLDER)
@ -21,13 +39,15 @@ def create_output_folder():
def get_templates_list():
"""Get the list of template files from `templates` directory"""
assert os.path.exists(TEMPLATE_PATH)
templates_list = os.listdir(TEMPLATE_PATH)
return templates_list
def read_template(template_path: str):
with open(template_path, 'r') as stream:
"""Read the `componanent.yaml` template"""
with open(template_path, "r") as stream:
try:
template_dict = yaml.safe_load(stream)
except yaml.YAMLError as exc:
@ -37,6 +57,7 @@ def read_template(template_path: str):
def replace_keys_in_template(template_dict: dict, mapping: dict):
"""Replace the keys, values in `component.yaml` based on `mapping` dict"""
# Sample mapping will be as below
# { "implementation.container.image" : "image_name" }
@ -44,7 +65,7 @@ def replace_keys_in_template(template_dict: dict, mapping: dict):
# parse through each nested key
keys = nested_key.split('.')
keys = nested_key.split(".")
accessable = template_dict
for k in keys[:-1]:
accessable = accessable[k]
@ -54,16 +75,17 @@ def replace_keys_in_template(template_dict: dict, mapping: dict):
def write_to_yaml_file(template_dict: dict, yaml_path: str):
with open(yaml_path, 'w') as fp:
yaml.dump(template_dict, fp)
"""Write yaml output into file"""
with open(yaml_path, "w") as pointer:
yaml.dump(template_dict, pointer)
def generate_component_yaml(mapping_template_path: str):
"""Method to generate component.yaml based on the template"""
mapping: dict = {}
if os.path.exists(mapping_template_path):
with open(mapping_template_path) as fp:
mapping = json.load(fp)
with open(mapping_template_path) as pointer:
mapping = json.load(pointer)
create_output_folder()
template_list = get_templates_list()
@ -87,9 +109,12 @@ def generate_component_yaml(mapping_template_path: str):
write_to_yaml_file(template_dict=template_dict, yaml_path=dest)
if __name__ == '__main__':
if __name__ == "__main__":
if len(sys.argv) != 2:
raise Exception("\n\nUsage: "
"python utils/generate_templates.py cifar10/template_mapping.json\n\n")
mapping_template_path = sys.argv[1]
generate_component_yaml(mapping_template_path=mapping_template_path)
raise Exception(
"\n\nUsage: "
"python utils/generate_templates.py "
"cifar10/template_mapping.json\n\n"
)
input_template_path = sys.argv[1]
generate_component_yaml(mapping_template_path=input_template_path)