opsml.model.interfaces.tf

  1from pathlib import Path
  2from typing import Any, Dict, List, Optional, Tuple, Union
  3
  4import joblib
  5import numpy as np
  6from numpy.typing import NDArray
  7from pydantic import model_validator
  8
  9from opsml.helpers.utils import get_class_name
 10from opsml.model.interfaces.base import (
 11    ModelInterface,
 12    get_model_args,
 13    get_processor_name,
 14)
 15from opsml.types import CommonKwargs, Suffix, TrainedModelType
 16
 17try:
 18    import tensorflow as tf
 19
 20    ArrayType = Union[NDArray[Any], tf.Tensor]
 21
 22    class TensorFlowModel(ModelInterface):
 23        """Model interface for Tensorflow models.
 24
 25        Args:
 26            model:
 27                Tensorflow model
 28            preprocessor:
 29                Optional preprocessor
 30            sample_data:
 31                Sample data to be used for type inference and ONNX conversion/validation.
 32                This should match exactly what the model expects as input. ArrayType = Union[NDArray[Any], tf.Tensor]
 33            task_type:
 34                Task type for model. Defaults to undefined.
 35            model_type:
 36                Optional model type. This is inferred automatically.
 37            preprocessor_name:
 38                Optional preprocessor. This is inferred automatically if a
 39                preprocessor is provided.
 40
 41        Returns:
 42            TensorFlowModel
 43        """
 44
 45        model: Optional[tf.keras.Model] = None
 46        sample_data: Optional[Union[ArrayType, Dict[str, ArrayType], List[ArrayType], Tuple[ArrayType]]] = None
 47        preprocessor: Optional[Any] = None
 48        preprocessor_name: str = CommonKwargs.UNDEFINED.value
 49
 50        @property
 51        def model_class(self) -> str:
 52            return TrainedModelType.TF_KERAS.value
 53
 54        @classmethod
 55        def _get_sample_data(cls, sample_data: Any) -> Any:
 56            """Check sample data and returns one record to be used
 57            during type inference and ONNX conversion/validation.
 58
 59            Returns:
 60                Sample data with only one record
 61            """
 62
 63            if isinstance(sample_data, (np.ndarray, tf.Tensor)):
 64                return sample_data[0:1]
 65
 66            if isinstance(sample_data, list):
 67                return [data[0:1] for data in sample_data]
 68
 69            if isinstance(sample_data, tuple):
 70                return (data[0:1] for data in sample_data)
 71
 72            if isinstance(sample_data, dict):
 73                sample_dict = {}
 74                for key, value in sample_data.items():
 75                    sample_dict[key] = value[0:1]
 76                return sample_dict
 77
 78            raise ValueError("Provided sample data is not a valid type")
 79
 80        @model_validator(mode="before")
 81        @classmethod
 82        def check_model(cls, model_args: Dict[str, Any]) -> Dict[str, Any]:
 83            model = model_args.get("model")
 84
 85            if model_args.get("modelcard_uid", False):
 86                return model_args
 87
 88            model, module, bases = get_model_args(model)
 89
 90            assert isinstance(model, tf.keras.Model), "Model must be a tensorflow keras model"
 91
 92            if "keras" in module:
 93                model_args[CommonKwargs.MODEL_TYPE.value] = model.__class__.__name__
 94
 95            else:
 96                for base in bases:
 97                    if "keras" in base:
 98                        model_args[CommonKwargs.MODEL_TYPE.value] = "subclass"
 99
100            sample_data = cls._get_sample_data(sample_data=model_args[CommonKwargs.SAMPLE_DATA.value])
101            model_args[CommonKwargs.SAMPLE_DATA.value] = sample_data
102            model_args[CommonKwargs.DATA_TYPE.value] = get_class_name(sample_data)
103            model_args[CommonKwargs.PREPROCESSOR_NAME.value] = get_processor_name(
104                model_args.get(CommonKwargs.PREPROCESSOR.value),
105            )
106
107            return model_args
108
109        def save_model(self, path: Path) -> None:
110            """Save tensorflow model to path
111
112            Args:
113                path:
114                    pathlib object
115            """
116            assert self.model is not None, "Model is not initialized"
117            self.model.save(path)
118
119        def load_model(self, path: Path, **kwargs: Any) -> None:
120            """Load tensorflow model from path
121
122            Args:
123                path:
124                    pathlib object
125                kwargs:
126                    Additional arguments to be passed to load_model
127            """
128            self.model = tf.keras.models.load_model(path, **kwargs)
129
130        def save_preprocessor(self, path: Path) -> None:
131            """Saves preprocessor to path if present. Base implementation use Joblib
132
133            Args:
134                path:
135                    Pathlib object
136            """
137            assert self.preprocessor is not None, "No preprocessor detected in interface"
138            joblib.dump(self.preprocessor, path)
139
140        def load_preprocessor(self, path: Path) -> None:
141            """Load preprocessor from pathlib object
142
143            Args:
144                path:
145                    Pathlib object
146            """
147            self.preprocessor = joblib.load(path)
148
149        @property
150        def preprocessor_suffix(self) -> str:
151            """Returns suffix for storage"""
152            return Suffix.JOBLIB.value
153
154        @property
155        def model_suffix(self) -> str:
156            """Returns suffix for storage"""
157            return ""
158
159        @staticmethod
160        def name() -> str:
161            return TensorFlowModel.__name__
162
163except ModuleNotFoundError:
164    from opsml.model.interfaces.backups import (
165        TensorFlowModelNoModule as TensorFlowModel,
166    )
class TensorFlowModel(opsml.model.interfaces.base.ModelInterface):
 23    class TensorFlowModel(ModelInterface):
 24        """Model interface for Tensorflow models.
 25
 26        Args:
 27            model:
 28                Tensorflow model
 29            preprocessor:
 30                Optional preprocessor
 31            sample_data:
 32                Sample data to be used for type inference and ONNX conversion/validation.
 33                This should match exactly what the model expects as input. ArrayType = Union[NDArray[Any], tf.Tensor]
 34            task_type:
 35                Task type for model. Defaults to undefined.
 36            model_type:
 37                Optional model type. This is inferred automatically.
 38            preprocessor_name:
 39                Optional preprocessor. This is inferred automatically if a
 40                preprocessor is provided.
 41
 42        Returns:
 43            TensorFlowModel
 44        """
 45
 46        model: Optional[tf.keras.Model] = None
 47        sample_data: Optional[Union[ArrayType, Dict[str, ArrayType], List[ArrayType], Tuple[ArrayType]]] = None
 48        preprocessor: Optional[Any] = None
 49        preprocessor_name: str = CommonKwargs.UNDEFINED.value
 50
 51        @property
 52        def model_class(self) -> str:
 53            return TrainedModelType.TF_KERAS.value
 54
 55        @classmethod
 56        def _get_sample_data(cls, sample_data: Any) -> Any:
 57            """Check sample data and returns one record to be used
 58            during type inference and ONNX conversion/validation.
 59
 60            Returns:
 61                Sample data with only one record
 62            """
 63
 64            if isinstance(sample_data, (np.ndarray, tf.Tensor)):
 65                return sample_data[0:1]
 66
 67            if isinstance(sample_data, list):
 68                return [data[0:1] for data in sample_data]
 69
 70            if isinstance(sample_data, tuple):
 71                return (data[0:1] for data in sample_data)
 72
 73            if isinstance(sample_data, dict):
 74                sample_dict = {}
 75                for key, value in sample_data.items():
 76                    sample_dict[key] = value[0:1]
 77                return sample_dict
 78
 79            raise ValueError("Provided sample data is not a valid type")
 80
 81        @model_validator(mode="before")
 82        @classmethod
 83        def check_model(cls, model_args: Dict[str, Any]) -> Dict[str, Any]:
 84            model = model_args.get("model")
 85
 86            if model_args.get("modelcard_uid", False):
 87                return model_args
 88
 89            model, module, bases = get_model_args(model)
 90
 91            assert isinstance(model, tf.keras.Model), "Model must be a tensorflow keras model"
 92
 93            if "keras" in module:
 94                model_args[CommonKwargs.MODEL_TYPE.value] = model.__class__.__name__
 95
 96            else:
 97                for base in bases:
 98                    if "keras" in base:
 99                        model_args[CommonKwargs.MODEL_TYPE.value] = "subclass"
100
101            sample_data = cls._get_sample_data(sample_data=model_args[CommonKwargs.SAMPLE_DATA.value])
102            model_args[CommonKwargs.SAMPLE_DATA.value] = sample_data
103            model_args[CommonKwargs.DATA_TYPE.value] = get_class_name(sample_data)
104            model_args[CommonKwargs.PREPROCESSOR_NAME.value] = get_processor_name(
105                model_args.get(CommonKwargs.PREPROCESSOR.value),
106            )
107
108            return model_args
109
110        def save_model(self, path: Path) -> None:
111            """Save tensorflow model to path
112
113            Args:
114                path:
115                    pathlib object
116            """
117            assert self.model is not None, "Model is not initialized"
118            self.model.save(path)
119
120        def load_model(self, path: Path, **kwargs: Any) -> None:
121            """Load tensorflow model from path
122
123            Args:
124                path:
125                    pathlib object
126                kwargs:
127                    Additional arguments to be passed to load_model
128            """
129            self.model = tf.keras.models.load_model(path, **kwargs)
130
131        def save_preprocessor(self, path: Path) -> None:
132            """Saves preprocessor to path if present. Base implementation use Joblib
133
134            Args:
135                path:
136                    Pathlib object
137            """
138            assert self.preprocessor is not None, "No preprocessor detected in interface"
139            joblib.dump(self.preprocessor, path)
140
141        def load_preprocessor(self, path: Path) -> None:
142            """Load preprocessor from pathlib object
143
144            Args:
145                path:
146                    Pathlib object
147            """
148            self.preprocessor = joblib.load(path)
149
150        @property
151        def preprocessor_suffix(self) -> str:
152            """Returns suffix for storage"""
153            return Suffix.JOBLIB.value
154
155        @property
156        def model_suffix(self) -> str:
157            """Returns suffix for storage"""
158            return ""
159
160        @staticmethod
161        def name() -> str:
162            return TensorFlowModel.__name__

Model interface for Tensorflow models.

Arguments:
  • model: Tensorflow model
  • preprocessor: Optional preprocessor
  • sample_data: Sample data to be used for type inference and ONNX conversion/validation. This should match exactly what the model expects as input. ArrayType = Union[NDArray[Any], tf.Tensor]
  • task_type: Task type for model. Defaults to undefined.
  • model_type: Optional model type. This is inferred automatically.
  • preprocessor_name: Optional preprocessor. This is inferred automatically if a preprocessor is provided.
Returns:

TensorFlowModel

model: Optional[keras.engine.training.Model]
sample_data: Union[numpy.ndarray[Any, numpy.dtype[Any]], tensorflow.python.framework.ops.Tensor, Dict[str, Union[numpy.ndarray[Any, numpy.dtype[Any]], tensorflow.python.framework.ops.Tensor]], List[Union[numpy.ndarray[Any, numpy.dtype[Any]], tensorflow.python.framework.ops.Tensor]], Tuple[Union[numpy.ndarray[Any, numpy.dtype[Any]], tensorflow.python.framework.ops.Tensor]], NoneType]
preprocessor: Optional[Any]
preprocessor_name: str
model_class: str
51        @property
52        def model_class(self) -> str:
53            return TrainedModelType.TF_KERAS.value
@model_validator(mode='before')
@classmethod
def check_model(cls, model_args: Dict[str, Any]) -> Dict[str, Any]:
 81        @model_validator(mode="before")
 82        @classmethod
 83        def check_model(cls, model_args: Dict[str, Any]) -> Dict[str, Any]:
 84            model = model_args.get("model")
 85
 86            if model_args.get("modelcard_uid", False):
 87                return model_args
 88
 89            model, module, bases = get_model_args(model)
 90
 91            assert isinstance(model, tf.keras.Model), "Model must be a tensorflow keras model"
 92
 93            if "keras" in module:
 94                model_args[CommonKwargs.MODEL_TYPE.value] = model.__class__.__name__
 95
 96            else:
 97                for base in bases:
 98                    if "keras" in base:
 99                        model_args[CommonKwargs.MODEL_TYPE.value] = "subclass"
100
101            sample_data = cls._get_sample_data(sample_data=model_args[CommonKwargs.SAMPLE_DATA.value])
102            model_args[CommonKwargs.SAMPLE_DATA.value] = sample_data
103            model_args[CommonKwargs.DATA_TYPE.value] = get_class_name(sample_data)
104            model_args[CommonKwargs.PREPROCESSOR_NAME.value] = get_processor_name(
105                model_args.get(CommonKwargs.PREPROCESSOR.value),
106            )
107
108            return model_args
def save_model(self, path: pathlib.Path) -> None:
110        def save_model(self, path: Path) -> None:
111            """Save tensorflow model to path
112
113            Args:
114                path:
115                    pathlib object
116            """
117            assert self.model is not None, "Model is not initialized"
118            self.model.save(path)

Save tensorflow model to path

Arguments:
  • path: pathlib object
def load_model(self, path: pathlib.Path, **kwargs: Any) -> None:
120        def load_model(self, path: Path, **kwargs: Any) -> None:
121            """Load tensorflow model from path
122
123            Args:
124                path:
125                    pathlib object
126                kwargs:
127                    Additional arguments to be passed to load_model
128            """
129            self.model = tf.keras.models.load_model(path, **kwargs)

Load tensorflow model from path

Arguments:
  • path: pathlib object
  • kwargs: Additional arguments to be passed to load_model
def save_preprocessor(self, path: pathlib.Path) -> None:
131        def save_preprocessor(self, path: Path) -> None:
132            """Saves preprocessor to path if present. Base implementation use Joblib
133
134            Args:
135                path:
136                    Pathlib object
137            """
138            assert self.preprocessor is not None, "No preprocessor detected in interface"
139            joblib.dump(self.preprocessor, path)

Saves preprocessor to path if present. Base implementation use Joblib

Arguments:
  • path: Pathlib object
def load_preprocessor(self, path: pathlib.Path) -> None:
141        def load_preprocessor(self, path: Path) -> None:
142            """Load preprocessor from pathlib object
143
144            Args:
145                path:
146                    Pathlib object
147            """
148            self.preprocessor = joblib.load(path)

Load preprocessor from pathlib object

Arguments:
  • path: Pathlib object
preprocessor_suffix: str
150        @property
151        def preprocessor_suffix(self) -> str:
152            """Returns suffix for storage"""
153            return Suffix.JOBLIB.value

Returns suffix for storage

model_suffix: str
155        @property
156        def model_suffix(self) -> str:
157            """Returns suffix for storage"""
158            return ""

Returns suffix for storage

@staticmethod
def name() -> str:
160        @staticmethod
161        def name() -> str:
162            return TensorFlowModel.__name__
model_config = {'protected_namespaces': ('protect_',), 'arbitrary_types_allowed': True, 'validate_assignment': False, 'validate_default': True, 'extra': 'allow'}
model_fields = {'model': FieldInfo(annotation=Union[Model, NoneType], required=False), 'sample_data': FieldInfo(annotation=Union[ndarray[Any, dtype[Any]], Tensor, Dict[str, Union[ndarray[Any, dtype[Any]], Tensor]], List[Union[ndarray[Any, dtype[Any]], Tensor]], Tuple[Union[ndarray[Any, dtype[Any]], Tensor]], NoneType], required=False), 'onnx_model': FieldInfo(annotation=Union[OnnxModel, NoneType], required=False), 'task_type': FieldInfo(annotation=str, required=False, default='undefined'), 'model_type': FieldInfo(annotation=str, required=False, default='undefined'), 'data_type': FieldInfo(annotation=str, required=False, default='undefined'), 'modelcard_uid': FieldInfo(annotation=str, required=False, default=''), 'preprocessor': FieldInfo(annotation=Union[Any, NoneType], required=False), 'preprocessor_name': FieldInfo(annotation=str, required=False, default='undefined')}
model_computed_fields = {}
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
opsml.model.interfaces.base.ModelInterface
onnx_model
task_type
model_type
data_type
modelcard_uid
check_modelcard_uid
save_onnx
convert_to_onnx
load_onnx_model
save_sample_data
load_sample_data
get_sample_prediction
data_suffix