Source code for mltk.core.tflite_model.tflite_layer

# 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_input_tensor(self, index=0) -> TfliteTensor: """Get layer input tensor as TfliteTensor""" if index >= self.n_inputs: raise IndexError(f'Index overflow ({index} >= {self.n_inputs})') return self._inputs[index]
[docs] def get_input_data(self, index=0) -> np.ndarray: """Get layer input tensor as np.ndarray""" if index >= self.n_inputs: raise IndexError(f'Index overflow ({index} >= {self.n_inputs})') return self._inputs[index].data
[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 }