# pylint: disable=redefined-builtin
from __future__ import annotations
from typing import List, Tuple, Dict, TypeVar
import numpy as np
from . import tflite_schema as _tflite_schema_fb
from .tflite_schema import BuiltinOperator as TfliteOpCode
from .tflite_tensor import TfliteTensor
from .tflite_types import (
TfliteActivation,
TflitePadding,
TfliteConvParams,
TfliteDepthwiseConvParams,
TfliteTransposeConvParams,
TfliteFullyConnectedParams,
TflitePoolParams
)
TfliteModel = TypeVar('TfliteModel')
[docs]class TfliteLayer:
"""Wrapper for TFLite flatbuffer layer"""
[docs] @staticmethod
def from_flatbuffer(
index:int,
model:TfliteModel,
fb_operation:_tflite_schema_fb.OperatorT
) -> TfliteLayer:
"""Instantiate a TfliteLayer from then given TfliteModel flatbuffer operation"""
fb_opcode = model.flatbuffer_model.operatorCodes[fb_operation.opcodeIndex]
# See: https://github.com/tensorflow/community/pull/285/files
# for why we return the max(DeprecatedBuiltinCode, BuiltinCode)
opcode = max(getattr(fb_opcode, 'deprecatedBuiltinCode', -1), fb_opcode.builtinCode)
opcode_version = fb_opcode.version
layer_cls = _LAYER_MAP.get(opcode, TfliteLayer)
return layer_cls(
index=index,
opcode=opcode,
opcode_version=opcode_version,
model=model,
fb_operation=fb_operation
)
[docs] def __init__(
self,
index:int,
opcode:TfliteOpCode,
opcode_version:int,
model:TfliteModel,
fb_operation:_tflite_schema_fb.OperatorT
):
self._index:int = index
self._model:TfliteModel = model
self._opcode:TfliteOpCode = opcode
self._opcode_version:int = opcode_version
self._opcode_str:str = _convert_object_value_to_string(TfliteOpCode(), self.opcode)
self._options = TfliteLayerOptions(
fb_object=fb_operation.builtinOptions,
type=fb_operation.builtinOptionsType
)
self._metadata:Dict[str,object] = {}
self._inputs : List[TfliteTensor] = []
self._outputs : List[TfliteTensor] = []
for i in fb_operation.inputs:
if i >= 0:
self.inputs.append(model.get_tensor(i))
else:
self.inputs.append(None)
for i in fb_operation.outputs:
if i >= 0:
self.outputs.append(model.get_tensor(i))
else:
self.outputs.append(None)
def __str__(self):
return self.name
@property
def index(self) -> int:
"""Index of this layer in the model"""
return self._index
@property
def name(self) -> str:
"""Name of current layer as: op<index>-<OpCodeStr>"""
return f'op{self._index}-{self._opcode_str}'
@property
def opcode(self) -> TfliteOpCode:
"""OpCode numeric value"""
return self._opcode
@property
def opcode_str(self) -> str:
"""OpCode as a string"""
return self._opcode_str
@property
def options(self) -> TfliteLayerOptions:
"""Layer-specific options/config"""
return self._options
@property
def model(self) -> TfliteModel:
"""Reference to associated TfliteModel"""
return self._model
@property
def metadata(self) -> Dict[str,object]:
"""Additional key/value data to associated with layer
NOTE: This information is generated by the framework/Python scripts
(i.e. The information does NOT come from the .tflite model)
"""
return self._metadata
@property
def inputs(self) -> List[TfliteTensor]:
"""List of layer input tensor(s)"""
return self._inputs
@property
def n_inputs(self) -> int:
"""Return the number of inputs"""
return len(self._inputs)
@property
def outputs(self) -> List[TfliteTensor]:
"""List of layer output tensor(s)"""
return self._outputs
@property
def n_outputs(self) -> int:
"""Return the number of outputs"""
return len(self._outputs)
[docs] def get_output_tensor(self, index=0) -> TfliteTensor:
"""Layer output tensor as TfliteTensor"""
if index >= self.n_outputs:
raise IndexError(f'Index overflow ({index} >= {self.n_outputs})')
return self._outputs[index]
[docs] def get_output_data(self, index=0) -> np.ndarray:
"""Layer output tensor as np.ndarray"""
if index >= self.n_outputs:
raise IndexError(f'Index overflow ({index} >= {self.n_outputs})')
return self._outputs[index].data
class TfliteLayerOptions:
"""Generic layer options object"""
def __init__(self, fb_object=None, type:_tflite_schema_fb.BuiltinOptions=None):
self._type = type
if fb_object is not None:
for x in vars(fb_object):
setattr(self, x, getattr(fb_object, x))
@property
def options_type(self) -> _tflite_schema_fb.BuiltinOptions:
""".tflite schema option code"""
return self._type
@property
def options_type_str(self) -> str:
""".tflite schema option as a string """
return _convert_object_value_to_string(_tflite_schema_fb.BuiltinOptions(), self._type)
def __str__(self):
return f'Type={self.options_type_str}'
[docs]class TfliteAddLayer(TfliteLayer):
"""ADD operation TfliteLayer"""
[docs] def __init__(self, fb_operation:_tflite_schema_fb.OperatorT, **kwargs):
TfliteLayer.__init__(self, fb_operation=fb_operation, **kwargs)
self._options = TfliteAddLayerOptions(fb_operation.builtinOptions)
@property
def options(self) -> TfliteAddLayerOptions:
"""Layer-specific options/config"""
return self._options
@property
def activation(self) -> str:
"""Fused activation"""
return self._options.activation_str
@property
def input1_tensor(self) -> TfliteTensor:
"""First input tensor data"""
return self._inputs[0]
@property
def input1_data(self) -> np.ndarray:
"""First input tensor data"""
return self.input1_tensor.data
@property
def input2_tensor(self) -> TfliteTensor:
"""Second input tensor data"""
return self._inputs[1]
@property
def input2_data(self) -> np.ndarray:
"""Second input tensor data"""
return self.input2_tensor.data
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
class TfliteAddLayerOptions(_tflite_schema_fb.AddOptionsT, TfliteLayerOptions):
"""Add layer options"""
def __init__(self, opts=None):
_tflite_schema_fb.AddOptionsT.__init__(self)
TfliteLayerOptions.__init__(self, opts,
type=_tflite_schema_fb.BuiltinOptions.AddOptions
)
self._activation = TfliteActivation(self.fusedActivationFunction)
@property
def activation_str(self) -> str:
"""Fused activation as a string"""
return self._activation.to_string()
@activation_str.setter
def activation_str(self, v):
self._activation = TfliteActivation(v)
@property
def activation(self) -> TfliteActivation:
"""Fused activation"""
return self._activation
@activation.setter
def activation(self, v:int):
self._activation = TfliteActivation(v)
def __str__(self):
return f'Activation:{self.activation_str}'
[docs]class TfliteConv2dLayer(TfliteLayer):
"""CONV_2D operation TfliteLayer"""
[docs] def __init__(self, fb_operation:_tflite_schema_fb.OperatorT, **kwargs):
TfliteLayer.__init__(self, fb_operation=fb_operation, **kwargs)
self._options = TfliteConv2DLayerOptions(fb_operation.builtinOptions)
filters_shape = self.filters_tensor.shape
self._kernel_size = (filters_shape[1], filters_shape[2])
self._filters = filters_shape[3]
@property
def options(self) -> TfliteConv2DLayerOptions:
"""Layer-specific options/config"""
return self._options
@property
def filters(self) -> int:
"""The number of filters"""
return self._filters
@property
def kernel_size(self) -> Tuple[int,int]:
"""Filters kernel size has height x width"""
return self._kernel_size
@property
def strides(self) -> Tuple[int,int]:
"""Kernel stride height x width"""
return (self._options.stride_height, self._options.stride_width)
@property
def padding(self) -> str:
"""Kernel padding"""
return self._options.padding_str
@property
def activation(self) -> str:
"""Fused activation"""
return self._options.activation_str
@property
def use_bias(self) -> bool:
"""Return if the layer uses a bias"""
return len(self._inputs) > 2
@property
def input_tensor(self) -> TfliteTensor:
"""Input tensor data"""
return self._inputs[0]
@property
def input_data(self) -> np.ndarray:
"""Input tensor data"""
return self.input_tensor.data
@property
def filters_tensor(self) -> TfliteTensor:
"""Filters tensor data"""
return self._inputs[1]
@property
def filters_data(self) -> np.ndarray:
"""Filters tensor data"""
return self.filters_tensor.data
@property
def bias_tensor(self) -> TfliteTensor:
"""Bias tensor data (None if no bias used)"""
return self._inputs[2] if self.use_bias else None
@property
def bias_data(self) -> np.ndarray:
"""Bias tensor data (None if no bias used)"""
return self.bias_tensor.data if self.use_bias else None
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
@property
def params(self) -> TfliteConvParams:
"""Calculated layer parameters"""
return TfliteConvParams.calculate(self)
[docs]class TfliteConv2DLayerOptions(_tflite_schema_fb.Conv2DOptionsT, TfliteLayerOptions):
"""Convolution layer options"""
[docs] def __init__(self, opts=None):
_tflite_schema_fb.Conv2DOptionsT.__init__(self)
TfliteLayerOptions.__init__(
self, opts,
type=_tflite_schema_fb.BuiltinOptions.Conv2DOptions
)
self._activation = TfliteActivation(self.fusedActivationFunction)
self._padding = TflitePadding(0 if opts is None else opts.padding)
@property
def activation_str(self) -> str:
"""Fused activation as a string"""
return self._activation.to_string()
@activation_str.setter
def activation_str(self, v):
self._activation = TfliteActivation(v)
@property
def activation(self) -> TfliteActivation:
"""Fused activation"""
return self._activation
@activation.setter
def activation(self, v:int):
self._activation = TfliteActivation(v)
@property
def padding_str(self) -> str:
"""Padding as a string"""
return self._padding.to_string()
@padding_str.setter
def padding_str(self, v):
self._padding = TflitePadding(v)
@property
def padding(self) -> TflitePadding:
"""Padding type"""
return self._padding
@padding.setter
def padding(self, v:int):
self._padding = TflitePadding(v)
@property
def stride_width(self) -> int:
"""Kernel stride width"""
return self.strideW
@stride_width.setter
def stride_width(self, v):
self.strideW = v
@property
def stride_height(self) -> int:
"""Kernel stride height"""
return self.strideH
@stride_height.setter
def stride_height(self, v):
self.strideH = v
def __str__(self):
return f'Padding:{self.padding_str} stride:{self.stride_width}x{self.stride_height} activation:{self.activation_str}'
[docs]class TfliteTransposeConvLayer(TfliteLayer):
"""TRANSPOSE_CONV operation TfliteLayer"""
[docs] def __init__(self, fb_operation:_tflite_schema_fb.OperatorT, **kwargs):
TfliteLayer.__init__(self, fb_operation=fb_operation, **kwargs)
self._options = TfliteTransposeConvLayerOptions(fb_operation.builtinOptions)
filters_shape = self.filters_tensor.shape
self._kernel_size = (filters_shape[1], filters_shape[2])
self._filters = filters_shape[1]
@property
def options(self) -> TfliteTransposeConvLayerOptions:
"""Layer-specific options/config"""
return self._options
@property
def filters(self) -> int:
"""The number of filters"""
return self._filters
@property
def kernel_size(self) -> Tuple[int,int]:
"""Filters kernel size has height x width"""
return self._kernel_size
@property
def strides(self) -> Tuple[int,int]:
"""Kernel stride height x width"""
return (self._options.stride_height, self._options.stride_width)
@property
def padding(self) -> str:
"""Kernel padding"""
return self._options.padding_str
@property
def use_bias(self) -> bool:
"""Return if the layer uses a bias"""
return len(self._inputs) > 3
@property
def input_tensor(self) -> TfliteTensor:
"""Input tensor data"""
return self._inputs[2]
@property
def input_data(self) -> np.ndarray:
"""Input tensor data"""
return self.input_tensor.data
@property
def filters_tensor(self) -> TfliteTensor:
"""Filters tensor data"""
return self._inputs[1]
@property
def filters_data(self) -> np.ndarray:
"""Filters tensor data"""
return self.filters_tensor.data
@property
def bias_tensor(self) -> TfliteTensor:
"""Bias tensor data (None if no bias used)"""
return self._inputs[3] if self.use_bias else None
@property
def bias_data(self) -> np.ndarray:
"""Bias tensor data (None if no bias used)"""
return self.bias_tensor.data if self.use_bias else None
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
@property
def params(self) -> TfliteTransposeConvParams:
"""Calculated layer parameters"""
return TfliteTransposeConvParams.calculate(self)
[docs]class TfliteTransposeConvLayerOptions(_tflite_schema_fb.TransposeConvOptionsT, TfliteLayerOptions):
"""Transpose convolution layer options"""
[docs] def __init__(self, opts=None):
_tflite_schema_fb.TransposeConvOptionsT.__init__(self)
TfliteLayerOptions.__init__(self, opts,
type=_tflite_schema_fb.BuiltinOptions.TransposeConvOptions
)
self._padding = TflitePadding(0 if opts is None else opts.padding)
@property
def stride_width(self) -> int:
"""Kernel stride width"""
return self.strideW
@stride_width.setter
def stride_width(self, v):
self.strideW = v
@property
def stride_height(self) -> int:
""""Kernel stride height"""
return self.strideH
@stride_height.setter
def stride_height(self, v):
self.strideH = v
@property
def padding_str(self) -> str:
"""Kernel padding as a string"""
return self._padding.to_string()
@padding_str.setter
def padding_str(self, v):
self._padding = TflitePadding(v)
@property
def padding(self) -> TflitePadding:
""""Kernel padding"""
return self._padding
@padding.setter
def padding(self, v:int):
self._padding = TflitePadding(v)
def __str__(self):
return f'Padding:{self.padding_str} stride:{self.stride_width}x{self.stride_height}'
[docs]class TfliteFullyConnectedLayer(TfliteLayer):
"""FULLY_CONNECT operation TfliteLayer"""
[docs] def __init__(self, fb_operation:_tflite_schema_fb.OperatorT, **kwargs):
TfliteLayer.__init__(self, fb_operation=fb_operation, **kwargs)
self._options = TfliteFullyConnectedLayerOptions(fb_operation.builtinOptions)
@property
def options(self) -> TfliteFullyConnectedLayerOptions:
"""Layer-specific options/config"""
return self._options
@property
def accumulator_depth(self) -> int:
"""Number of weights to accumulate"""
return self.weights_tensor.shape[-1]
@property
def units(self) -> int:
"""Number of neurons"""
return self.output_tensor.shape[-1]
@property
def activation(self) -> str:
"""Fused activation"""
return self._options.activation_str
@property
def use_bias(self) -> bool:
"""Return if the layer uses a bias"""
return len(self._inputs) > 2
@property
def input_tensor(self) -> TfliteTensor:
"""Input tensor data"""
return self._inputs[0]
@property
def input_data(self) -> np.ndarray:
"""Input tensor data"""
return self.input_tensor.data
@property
def weights_tensor(self) -> TfliteTensor:
"""Weights tensor data"""
return self._inputs[1]
@property
def weights_data(self) -> np.ndarray:
"""Weights tensor data"""
return self.weights_tensor.data
@property
def bias_tensor(self) -> TfliteTensor:
"""Bias tensor data (None if no bias used)"""
return self._inputs[2] if self.use_bias else None
@property
def bias_data(self) -> np.ndarray:
"""Bias tensor data (None if no bias used)"""
return self.bias_tensor.data if self.use_bias else None
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
@property
def params(self) -> TfliteFullyConnectedParams:
"""Calculated layer parameters"""
return TfliteFullyConnectedParams.calculate(self)
[docs]class TfliteFullyConnectedLayerOptions(_tflite_schema_fb.FullyConnectedOptionsT, TfliteLayerOptions):
"""Fully connection layer options"""
[docs] def __init__(self, opts=None):
_tflite_schema_fb.FullyConnectedOptionsT.__init__(self)
TfliteLayerOptions.__init__(self, opts,
type=_tflite_schema_fb.BuiltinOptions.FullyConnectedOptions
)
self._activation = TfliteActivation(self.fusedActivationFunction)
@property
def activation_str(self) -> str:
"""Fused activation as a string"""
return self._activation.to_string()
@activation_str.setter
def activation_str(self, v):
self._activation = TfliteActivation(v)
@property
def activation(self) -> TfliteActivation:
"""Fused activation"""
return self._activation
@activation.setter
def activation(self, v:int):
self._activation = TfliteActivation(v)
def __str__(self):
return f'Activation:{self.activation_str}'
[docs]class TfliteDepthwiseConv2dLayer(TfliteLayer):
"""DEPTHWISE_CONV_2D operation TfliteLayer"""
[docs] def __init__(self, fb_operation:_tflite_schema_fb.OperatorT, **kwargs):
TfliteLayer.__init__(self, fb_operation=fb_operation, **kwargs)
self._options = TfliteDepthwiseConv2DLayerOptions(fb_operation.builtinOptions)
filters_shape = self.filters_tensor.shape
self._kernel_size = (filters_shape[1], filters_shape[2])
@property
def options(self) -> TfliteDepthwiseConv2DLayerOptions:
"""Layer-specific options/config"""
return self._options
@property
def multiplier(self) -> int:
"""Depth multiplier"""
return self._options.multiplier
@property
def kernel_size(self) -> Tuple[int,int]:
"""Filters kernel size has height x width"""
return self._kernel_size
@property
def strides(self) -> Tuple[int,int]:
"""Kernel stride height x width"""
return (self._options.stride_height, self._options.stride_width)
@property
def padding(self) -> str:
"""Kernel padding"""
return self._options.padding_str
@property
def activation(self) -> str:
"""Fused activation"""
return self._options.activation_str
@property
def use_bias(self) -> bool:
"""Return if the layer uses a bias"""
return len(self._inputs) > 2
@property
def input_tensor(self) -> TfliteTensor:
"""Input tensor data"""
return self._inputs[0]
@property
def input_data(self) -> np.ndarray:
"""Input tensor data"""
return self.input_tensor.data
@property
def filters_tensor(self) -> TfliteTensor:
"""Filters tensor data"""
return self._inputs[1]
@property
def filters_data(self) -> np.ndarray:
"""Filters tensor data"""
return self.filters_tensor.data
@property
def bias_tensor(self) -> TfliteTensor:
"""Bias tensor data (None if no bias used)"""
return self._inputs[2] if self.use_bias else None
@property
def bias_data(self) -> np.ndarray:
"""Bias tensor data (None if no bias used)"""
return self.bias_tensor.data if self.use_bias else None
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
@property
def params(self) -> TfliteDepthwiseConvParams:
"""Calculated layer parameters"""
return TfliteDepthwiseConvParams.calculate(self)
[docs]class TfliteDepthwiseConv2DLayerOptions(_tflite_schema_fb.DepthwiseConv2DOptionsT, TfliteLayerOptions):
"""Depthwise Convolution options"""
[docs] def __init__(self, opts=None):
_tflite_schema_fb.DepthwiseConv2DOptionsT.__init__(self)
TfliteLayerOptions.__init__(self, opts,
type=_tflite_schema_fb.BuiltinOptions.DepthwiseConv2DOptions
)
self._activation = TfliteActivation(self.fusedActivationFunction)
self._padding = TflitePadding(0 if opts is None else opts.padding)
@property
def stride_width(self) -> int:
"""Kernel stride width"""
return self.strideW
@stride_width.setter
def stride_width(self, v):
self.strideW = v
@property
def stride_height(self) -> int:
""""Kernel stride height"""
return self.strideH
@stride_height.setter
def stride_height(self, v):
self.strideH = v
@property
def multiplier(self) -> int:
""""Depth multiplier"""
return self.depthMultiplier
@multiplier.setter
def multiplier(self, v):
self.depthMultiplier = v
@property
def activation_str(self) -> str:
"""Fused activation as a string"""
return self._activation.to_string()
@activation_str.setter
def activation_str(self, v):
self._activation = TfliteActivation(v)
@property
def activation(self) -> TfliteActivation:
"""Fused activation"""
return self._activation
@activation.setter
def activation(self, v:int):
self._activation = TfliteActivation(v)
@property
def padding_str(self) -> str:
"""Kernel padding as a string"""
return self._padding.to_string()
@padding_str.setter
def padding_str(self, v):
self._padding = TflitePadding(v)
@property
def padding(self) -> TflitePadding:
"""Kernel padding"""
return self._padding
@padding.setter
def padding(self, v:int):
self._padding = TflitePadding(v)
def __str__(self):
return f'Multiplier:{self.multiplier} padding:{self.padding_str} stride:{self.stride_width}x{self.stride_height} activation:{self.activation_str}'
[docs]class TflitePooling2dLayer(TfliteLayer):
"""AVERAGE_POOL_2D or MAX_POOL_2D operation TfliteLayer"""
[docs] def __init__(self, fb_operation:_tflite_schema_fb.OperatorT, **kwargs):
TfliteLayer.__init__(self, fb_operation=fb_operation, **kwargs)
self._options = TflitePool2DLayerOptions(fb_operation.builtinOptions)
@property
def options(self) -> TflitePool2DLayerOptions:
"""Layer-specific options/config"""
return self._options
@property
def pool_size(self) -> Tuple[int,int]:
"""Kernel size as height x width"""
return (self._options.filter_height, self._options.filter_width)
@property
def strides(self) -> Tuple[int,int]:
"""Kernel stride as height x width"""
return (self._options.stride_height, self._options.stride_width)
@property
def padding(self) -> str:
"""Kernel padding"""
return self._options.padding_str
@property
def activation(self) -> str:
"""Fused activation"""
return self._options.activation_str
@property
def input_tensor(self) -> TfliteTensor:
"""Input tensor data"""
return self._inputs[0]
@property
def input_data(self) -> np.ndarray:
"""Input tensor data"""
return self.input_tensor.data
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
@property
def params(self) -> TflitePoolParams:
"""Calculated layer parameters"""
return TflitePoolParams.calculate(self)
[docs]class TflitePool2DLayerOptions(_tflite_schema_fb.Pool2DOptionsT, TfliteLayerOptions):
"""Pooling layer options"""
[docs] def __init__(self, opts=None):
_tflite_schema_fb.Pool2DOptionsT.__init__(self)
TfliteLayerOptions.__init__(self, opts,
type=_tflite_schema_fb.BuiltinOptions.Pool2DOptions
)
self._activation = TfliteActivation(self.fusedActivationFunction)
self._padding = TflitePadding(0 if opts is None else opts.padding)
@property
def stride_width(self) -> int:
"""Filter stride width"""
return self.strideW
@stride_width.setter
def stride_width(self, v):
self.strideW = v
@property
def stride_height(self) -> int:
"""Filter stride height"""
return self.strideH
@stride_height.setter
def stride_height(self, v):
self.strideH = v
@property
def filter_width(self) -> int:
"""Filter width"""
return self.filterWidth
@filter_width.setter
def filter_width(self, v):
self.filterWidth = v
@property
def filter_height(self) -> int:
"""Filter height"""
return self.filterHeight
@filter_height.setter
def filter_height(self, v):
self.filterHeight = v
@property
def activation_str(self) -> str:
"""Fused activation as a string"""
return self._activation.to_string()
@activation_str.setter
def activation_str(self, v):
self._activation = TfliteActivation(v)
@property
def activation(self) -> TfliteActivation:
"""Fused activation"""
return self._activation
@activation.setter
def activation(self, v:int):
self._activation = TfliteActivation(v)
@property
def padding_str(self) -> str:
"""Filter padding as a string"""
return self._padding.to_string()
@padding_str.setter
def padding_str(self, v):
self._padding = TflitePadding(v)
@property
def padding(self) -> TflitePadding:
"""Filter padding"""
return self._padding
@padding.setter
def padding(self, v:int):
self._padding = TflitePadding(v)
def __str__(self):
return f'Padding:{self.padding_str} stride:{self.stride_width}x{self.stride_height} filter:{self.filter_width}x{self.filter_height} activation:{self.activation_str}'
[docs]class TfliteReshapeLayer(TfliteLayer):
"""RESHAPE operation TfliteLayer"""
[docs] def __init__(self, *args, **kwargs):
TfliteLayer.__init__(self, *args, **kwargs)
@property
def input_tensor(self) -> TfliteTensor:
"""Input tensor data"""
return self._inputs[0]
@property
def input_data(self) -> np.ndarray:
"""Input tensor data"""
return self.input_tensor.data
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
@property
def requires_copy(self) -> bool:
"""Return true if a memcpy is required, False if the reshape was done in-place"""
return self._inputs[0].index != self._outputs[0].index
@property
def n_input_elements(self) -> int:
"""Return the number of input elements"""
return self._inputs[0].shape.flat_size
[docs]class TfliteDequantizeLayer(TfliteLayer):
"""DEQUANTIZE operation TfliteLayer"""
@property
def input_tensor(self) -> TfliteTensor:
"""Input tensor data"""
return self._inputs[0]
@property
def input_data(self) -> np.ndarray:
"""Input tensor data"""
return self.input_tensor.data
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
[docs]class TfliteQuantizeLayer(TfliteLayer):
"""QUANTIZE operation TfliteLayer"""
@property
def input_tensor(self) -> TfliteTensor:
"""Input tensor data"""
return self._inputs[0]
@property
def input_data(self) -> np.ndarray:
"""Input tensor data"""
return self.input_tensor.data
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
class TfliteMulLayer(TfliteLayer):
"""MUL operation TfliteLayer"""
def __init__(self, fb_operation:_tflite_schema_fb.OperatorT, **kwargs):
TfliteLayer.__init__(self, fb_operation=fb_operation, **kwargs)
self._options = TfliteMulLayerOptions(fb_operation.builtinOptions)
@property
def options(self) -> TfliteMulLayerOptions:
"""Layer-specific options/config"""
return self._options
@property
def activation(self) -> str:
"""Fused activation"""
return self._options.activation_str
@property
def input1_tensor(self) -> TfliteTensor:
"""First input tensor data"""
return self._inputs[0]
@property
def input1_data(self) -> np.ndarray:
"""First input tensor data"""
return self.input1_tensor.data
@property
def input2_tensor(self) -> TfliteTensor:
"""Second input tensor data"""
return self._inputs[1]
@property
def input2_data(self) -> np.ndarray:
"""Second input tensor data"""
return self.input2_tensor.data
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
class TfliteMulLayerOptions(_tflite_schema_fb.MulOptionsT, TfliteLayerOptions):
"""Multiply layer options"""
def __init__(self, opts=None):
_tflite_schema_fb.MulOptionsT.__init__(self)
TfliteLayerOptions.__init__(self, opts,
type=_tflite_schema_fb.BuiltinOptions.MulOptions
)
self._activation = TfliteActivation(self.fusedActivationFunction)
@property
def activation_str(self) -> str:
"""Fused activation as a string"""
return self._activation.to_string()
@activation_str.setter
def activation_str(self, v):
self._activation = TfliteActivation(v)
@property
def activation(self) -> TfliteActivation:
"""Fused activation"""
return self._activation
@activation.setter
def activation(self, v:int):
self._activation = TfliteActivation(v)
def __str__(self):
return f'Activation:{self.activation_str}'
[docs]class TfliteUnidirectionalLstmLayer(TfliteLayer):
"""UNIDIRECTIONAL_SEQUENCE_LSTM operation TfliteLayer"""
[docs] def __init__(self, fb_operation:_tflite_schema_fb.OperatorT, **kwargs):
TfliteLayer.__init__(self, fb_operation=fb_operation, **kwargs)
self._options = TfliteUnidirectionalLstmLayerOptions(fb_operation.builtinOptions)
@property
def options(self) -> TfliteUnidirectionalLstmLayerOptions:
"""Layer-specific options/config"""
return self._options
@property
def activation(self) -> str:
"""Fused activation"""
return self._options.activation_str
@property
def is_time_major(self) -> bool:
"""Return if this kernel uses time major or batch major"""
return self._options.timeMajor
@property
def cell_clip(self) -> float:
"""Input tensor data"""
return self._options.cellClip
@property
def proj_clip(self) -> float:
"""Input tensor data"""
return self._options.projClip
@property
def n_cells(self) -> int:
"""Number of LSTM cells"""
return self.input_to_cell_weights_tensor.shape[0]
@property
def input_tensor(self) -> TfliteTensor:
"""Input tensor of size {n_batch, n_input}"""
return self._inputs[0]
@property
def input_data(self) -> np.ndarray:
"""Input tensor data"""
return self.input_tensor.data
@property
def output_tensor(self) -> TfliteTensor:
"""Output tensor data"""
return self._outputs[0]
@property
def output_data(self) -> np.ndarray:
"""Output tensor data"""
return self.output_tensor.data
@property
def input_to_input_weights_tensor(self) -> TfliteTensor:
"""(Optional) Input weight tensor of size: {n_cell, n_input}"""
return self._inputs[1]
@property
def input_to_forget_weights_tensor(self) -> TfliteTensor:
"""Input weight tensor of size: {n_cell, n_input}"""
return self._inputs[2]
@property
def input_to_cell_weights_tensor(self) -> TfliteTensor:
"""Input weight tensor of size: {n_cell, n_input}"""
return self._inputs[3]
@property
def input_to_output_weights_tensor(self) -> TfliteTensor:
"""Input weight tensor of size: {n_cell, n_input}"""
return self._inputs[4]
@property
def recurrent_to_input_weights_tensor(self) -> TfliteTensor:
"""(Optional) Recurrent weight tensor of size {n_cell, n_output}"""
return self._inputs[5]
@property
def recurrent_to_forget_weights_tensor(self) -> TfliteTensor:
"""Recurrent weight tensor of size {n_cell, n_output}"""
return self._inputs[6]
@property
def recurrent_to_cell_weights_tensor(self) -> TfliteTensor:
"""Recurrent weight tensor of size {n_cell, n_output}"""
return self._inputs[7]
@property
def recurrent_to_output_weights_tensor(self) -> TfliteTensor:
"""Recurrent weight tensor of size {n_cell, n_output}"""
return self._inputs[8]
@property
def cell_to_input_weights_tensor(self) -> TfliteTensor:
"""(Optional) Peephole weights tensor of size {n_cell}, representing a diagonal matrix"""
return self._inputs[9]
@property
def cell_to_forget_weights_tensor(self) -> TfliteTensor:
"""(Optional) Peephole weights tensor of size {n_cell}, representing a diagonal matrix"""
return self._inputs[10]
@property
def cell_to_output_weights_tensor(self) -> TfliteTensor:
"""(Optional) Peephole weights tensor of size {n_cell}, representing a diagonal matrix"""
return self._inputs[11]
@property
def input_gate_bias_tensor(self) -> TfliteTensor:
"""(Optional) Input gate bias tensor of size {n_cell}"""
return self._inputs[12]
@property
def forget_gate_bias_tensor(self) -> TfliteTensor:
"""Forget gate bias tensor of size {n_cell}"""
return self._inputs[13]
@property
def cell_gate_bias_tensor(self) -> TfliteTensor:
"""Cell gate bias tensor of size {n_cell}"""
return self._inputs[14]
@property
def output_gate_bias_tensor(self) -> TfliteTensor:
"""Output gate bias tensor of size {n_cell}"""
return self._inputs[15]
@property
def projection_weights_tensor(self) -> TfliteTensor:
"""(Optional) Projection weight tensor of size {n_output, n_cell}"""
return self._inputs[16]
@property
def projection_bias_tensor(self) -> TfliteTensor:
"""(Optional) Projection bias tensor of size {n_output}"""
return self._inputs[17]
@property
def output_state_tensor(self) -> TfliteTensor:
"""The output state tensor is defined as variable tensor, and will be modified at runtime"""
return self._inputs[18]
@property
def cell_state_tensor(self) -> TfliteTensor:
"""The cell state tensor is defined as variable tensor, and will be modified at runtime"""
return self._inputs[19]
@property
def input_layer_norm_coeff_tensor(self) -> TfliteTensor:
"""(Optional) Layer norm coefficient tensor of size {n_cell}, representing a diagonal matrix"""
return self._inputs[20]
@property
def forget_layer_norm_coeff_tensor(self) -> TfliteTensor:
"""(Optional) Layer norm coefficient tensor of size {n_cell}, representing a diagonal matrix"""
return self._inputs[21]
@property
def cell_layer_norm_coeff_tensor(self) -> TfliteTensor:
"""(Optional) Layer norm coefficient tensor of size {n_cell}, representing a diagonal matrix"""
return self._inputs[22]
@property
def output_layer_norm_coeff_tensor(self) -> TfliteTensor:
"""(Optional) Layer norm coefficient tensor of size {n_cell}, representing a diagonal matrix"""
return self._inputs[23]
def get_tensor_name_value_tuples(self) -> List[Tuple[str, TfliteTensor]]:
tensor_names = [
'input',
'input_to_input_weights',
'input_to_forget_weights',
'input_to_cell_weights',
'input_to_output_weights',
'recurrent_to_input_weights',
'recurrent_to_forget_weights',
'recurrent_to_cell_weights',
'recurrent_to_output_weights',
'cell_to_input_weights',
'cell_to_forget_weights',
'cell_to_output_weights',
'input_gate_bias',
'forget_gate_bias',
'cell_gate_bias',
'output_gate_bias',
'projection_weights',
'projection_bias',
'output_state',
'cell_state',
'input_layer_norm_coeff',
'forget_layer_norm_coeff',
'cell_layer_norm_coeff',
'output_layer_norm_coeff',
'output'
]
retval = []
for name in tensor_names:
retval.append((name, getattr(self, f'{name}_tensor')))
return retval
[docs]class TfliteUnidirectionalLstmLayerOptions(_tflite_schema_fb.UnidirectionalSequenceLSTMOptionsT, TfliteLayerOptions):
"""Fully connection layer options"""
[docs] def __init__(self, opts=None):
_tflite_schema_fb.UnidirectionalSequenceLSTMOptionsT.__init__(self)
TfliteLayerOptions.__init__(self, opts,
type=_tflite_schema_fb.BuiltinOptions.UnidirectionalSequenceLSTMOptions
)
self._activation = TfliteActivation(self.fusedActivationFunction)
@property
def activation_str(self) -> str:
"""Fused activation as a string"""
return self._activation.to_string()
@activation_str.setter
def activation_str(self, v):
self._activation = TfliteActivation(v)
@property
def activation(self) -> TfliteActivation:
"""Fused activation"""
return self._activation
@activation.setter
def activation(self, v:int):
self._activation = TfliteActivation(v)
def __str__(self):
return f'Time major:{self.timeMajor}, Activation:{self.activation_str}, Cell clip:{self.cellClip}'
def _convert_object_value_to_string(obj, needle:int) -> str:
for key in dir(obj):
if getattr(obj, key) == needle:
return key.lower()
return 'None'
_LAYER_MAP = {
TfliteOpCode.ADD: TfliteAddLayer,
TfliteOpCode.CONV_2D: TfliteConv2dLayer,
TfliteOpCode.TRANSPOSE_CONV: TfliteTransposeConvLayer,
TfliteOpCode.FULLY_CONNECTED: TfliteFullyConnectedLayer,
TfliteOpCode.DEPTHWISE_CONV_2D: TfliteDepthwiseConv2dLayer,
TfliteOpCode.AVERAGE_POOL_2D: TflitePooling2dLayer,
TfliteOpCode.MAX_POOL_2D: TflitePooling2dLayer,
TfliteOpCode.RESHAPE: TfliteReshapeLayer,
TfliteOpCode.QUANTIZE: TfliteQuantizeLayer,
TfliteOpCode.DEQUANTIZE: TfliteDequantizeLayer,
TfliteOpCode.MUL: TfliteMulLayer,
TfliteOpCode.UNIDIRECTIONAL_SEQUENCE_LSTM: TfliteUnidirectionalLstmLayer
}