Converts Keras model to TFLite model.
Parameters:
Example:
# Create simple dataset
test_x = np.random.rand(1000, 64).astype(np.float32)
test_y = np.random.randint(0, 10, 1000).astype(np.int32)
# Create a dense model and train
model = keras.Sequential([
keras.layers.Dense(64, activation="relu", input_shape=(64,)),
keras.layers.Dense(32, activation="relu"),
keras.layers.Dense(10, activation="softmax"),
])
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
model.fit(test_x, test_y, epochs=1, validation_split=0.2)
# Create converter and convert to TFLite w/ FP32 quantization
converter = nse.converters.tflite.TfLiteKerasConverter(model=model)
tflite_content = converter.convert(
test_x,
quantization=nse.converters.tflite.QuantizationType.FP32,
io_type="float32"
)
y_pred_tfl = converter.predict(test_x)
y_pred_tf = model.predict(test_x)
print(np.allclose(y_pred_tf, y_pred_tfl, atol=1e-3))
Source code in neuralspot_edge/converters/tflite/converter.py
| def __init__(
self,
model: keras.Model,
):
"""Converts Keras model to TFLite model.
Args:
model (keras.Model): Keras model
Example:
```python
# Create simple dataset
test_x = np.random.rand(1000, 64).astype(np.float32)
test_y = np.random.randint(0, 10, 1000).astype(np.int32)
# Create a dense model and train
model = keras.Sequential([
keras.layers.Dense(64, activation="relu", input_shape=(64,)),
keras.layers.Dense(32, activation="relu"),
keras.layers.Dense(10, activation="softmax"),
])
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
model.fit(test_x, test_y, epochs=1, validation_split=0.2)
# Create converter and convert to TFLite w/ FP32 quantization
converter = nse.converters.tflite.TfLiteKerasConverter(model=model)
tflite_content = converter.convert(
test_x,
quantization=nse.converters.tflite.QuantizationType.FP32,
io_type="float32"
)
y_pred_tfl = converter.predict(test_x)
y_pred_tf = model.predict(test_x)
print(np.allclose(y_pred_tf, y_pred_tfl, atol=1e-3))
```
"""
self.model = model
self.representative_dataset = None
self._converter: tf.lite.TFLiteConverter | None = None
self._tflite_content: str | None = None
self.tf_model_path = tempfile.TemporaryDirectory()
|
Functions
from_saved_model
classmethod
from_saved_model(model_path: Path) -> TfLiteKerasConverter
Create converter from saved keras model
Parameters:
Returns:
Source code in neuralspot_edge/converters/tflite/converter.py
| @classmethod
def from_saved_model(cls, model_path: Path) -> "TfLiteKerasConverter":
"""Create converter from saved keras model
Args:
model_path (Path): Path to saved model
Returns:
TfLiteKerasConverter: Converter
"""
return cls(model=load_model(model_path))
|
convert
convert(test_x: npt.NDArray | None = None, quantization: QuantizationType = QuantizationType.FP32, io_type: str | None = None, mode: ConversionType = ConversionType.KERAS, strict: bool = True, verbose: int = 2) -> str
Convert TF model into TFLite model content
Parameters:
-
test_x
(NDArray | None, default:
None
)
–
Test dataset. Defaults to None.
-
quantization
(QuantizationType, default:
FP32
)
–
Quantization type. Defaults to QuantizationType.FP32.
-
io_type
(str | None, default:
None
)
–
Input/Output type. Defaults to None.
-
mode
(ConversionType, default:
KERAS
)
–
Conversion mode. Defaults to ConversionType.KERAS.
-
strict
(bool, default:
True
)
–
Strict mode. Defaults to True.
-
verbose
(int, default:
2
)
–
Verbosity level (0,1,2). Defaults to 2.
Returns:
Source code in neuralspot_edge/converters/tflite/converter.py
| def convert(
self,
test_x: npt.NDArray | None = None,
quantization: QuantizationType = QuantizationType.FP32,
io_type: str | None = None,
mode: ConversionType = ConversionType.KERAS,
strict: bool = True,
verbose: int = 2,
) -> str:
"""Convert TF model into TFLite model content
Args:
test_x (npt.NDArray | None, optional): Test dataset. Defaults to None.
quantization (QuantizationType, optional): Quantization type. Defaults to QuantizationType.FP32.
io_type (str | None, optional): Input/Output type. Defaults to None.
mode (ConversionType, optional): Conversion mode. Defaults to ConversionType.KERAS.
strict (bool, optional): Strict mode. Defaults to True.
verbose (int, optional): Verbosity level (0,1,2). Defaults to 2.
Returns:
str: TFLite content
"""
quantization = QuantizationType(quantization)
feat_shape = self.model.input_shape[1:]
input_shape = (1,) + feat_shape # Add 1 for batch dimension
input_spec = tf.TensorSpec(shape=input_shape, dtype=self.model.input_dtype)
match mode:
case ConversionType.KERAS:
converter = tf.lite.TFLiteConverter.from_keras_model(model=self.model)
case ConversionType.SAVED_MODEL:
self.model.export(self.tf_model_path.name, format="tf_saved_model")
converter = tf.lite.TFLiteConverter.from_saved_model(self.tf_model_path.name)
# Following case is a workaround for bug (https://github.com/tensorflow/tflite-micro/issues/2319)
# Default TFLiteConverter generates equivalent graph w/ SpaceToBatchND operations but losses dilation_rate factor.
case ConversionType.CONCRETE:
model_func = tf.function(func=self.model)
model_cf = model_func.get_concrete_function(input_spec)
converter = tf.lite.TFLiteConverter.from_concrete_functions([model_cf])
case _:
raise ValueError(f"Invalid conversion mode: {mode}")
# END MATCH
if test_x is None:
test_x = np.random.rand(1000, *feat_shape)
def rep_dataset():
"""Helper function to generate representative dataset"""
for i in range(test_x.shape[0]):
yield [test_x[i : i + 1]]
self.representative_dataset = rep_dataset
match quantization:
# float32 weights, bias, activation
case QuantizationType.FP32:
pass
# float16 weights, bias, activation
case QuantizationType.FP16:
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
# int8 weights, bias, activation
case QuantizationType.INT8:
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
io_dtype = tf.dtypes.as_dtype(io_type) if io_type else tf.int8
converter.inference_input_type = io_dtype
converter.inference_output_type = io_dtype
converter.representative_dataset = self.representative_dataset
# int8 weights, int64 bias, int16 activation
case QuantizationType.INT16X8:
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [
tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8
]
io_dtype = tf.dtypes.as_dtype(io_type) if io_type else tf.float32
converter.inference_input_type = io_dtype
converter.inference_output_type = io_dtype
converter.representative_dataset = self.representative_dataset
# END MATCH
# For fallback append tf.lite.OpsSet.TFLITE_BUILTINS for INT8 and INT16X8
if not strict and quantization in [
QuantizationType.INT8,
QuantizationType.INT16X8,
]:
converter.target_spec.supported_ops.append(tf.lite.OpsSet.TFLITE_BUILTINS)
# END IF
# Convert model
self._converter = converter
self._tflite_content = converter.convert()
return self._tflite_content
|
debug_quantization
debug_quantization() -> pd.DataFrame
Debug quantized TFLite model content.
Source code in neuralspot_edge/converters/tflite/converter.py
| def debug_quantization(self) -> pd.DataFrame:
"""Debug quantized TFLite model content."""
if self._converter is None:
raise ValueError("No TFLite content to debug. Run convert() first.")
if self.representative_dataset is None:
raise ValueError("No representative dataset provided. Run convert() with test_x first.")
# Debug model
debugger = tf.lite.experimental.QuantizationDebugger(
converter=self._converter, debug_dataset=self.representative_dataset
)
debugger.run()
with io.StringIO() as f:
debugger.layer_statistics_dump(f)
f.seek(0)
layer_stats = pd.read_csv(f)
# END WITH
# Add custom metrics
layer_stats["range"] = 255.0 * layer_stats["scale"]
layer_stats["rmse/scale"] = layer_stats.apply(
lambda row: np.sqrt(row["mean_squared_error"]) / row["scale"], axis=1
)
return layer_stats
|
evaluate
evaluate(x: npt.NDArray, y: npt.NDArray, input_name: str | None = None, output_name: str | None = None) -> npt.NDArray
Evaluate TFLite model
Parameters:
-
x
(NDArray)
–
-
y
(NDArray)
–
-
(
str | None, default:
None
)
–
Input layer name. Defaults to None.
-
output_name
(str | None, default:
None
)
–
Output layer name. Defaults to None.
Returns:
Source code in neuralspot_edge/converters/tflite/converter.py
| def evaluate(
self,
x: npt.NDArray,
y: npt.NDArray,
input_name: str | None = None,
output_name: str | None = None,
) -> npt.NDArray:
"""Evaluate TFLite model
Args:
x (npt.NDArray): Input samples
y (npt.NDArray): Input labels
input_name (str | None, optional): Input layer name. Defaults to None.
output_name (str | None, optional): Output layer name. Defaults to None.
Returns:
npt.NDArray: Loss values
"""
y_pred = self.predict(
x=x,
input_name=input_name,
output_name=output_name,
)
loss_function = keras.losses.get(self.model.loss)
loss = loss_function(y, y_pred).numpy()
return loss
|
export
Export TFLite model content to file
Parameters:
Source code in neuralspot_edge/converters/tflite/converter.py
| def export(self, tflite_path: str):
"""Export TFLite model content to file
Args:
tflite_path (str): TFLite file path
"""
if self._tflite_content is None:
raise ValueError("No TFLite content to export. Run convert() first.")
with open(tflite_path, "wb") as f:
f.write(self._tflite_content)
|
export_header(header_path: str, name: str = 'model')
Export TFLite model as C header file.
Parameters:
-
(
str)
–
-
(
str, default:
'model'
)
–
Variable name. Defaults to "model".
Source code in neuralspot_edge/converters/tflite/converter.py
| def export_header(self, header_path: str, name: str = "model"):
"""Export TFLite model as C header file.
Args:
header_path (str): Header file path
name (str, optional): Variable name. Defaults to "model".
"""
with tempfile.NamedTemporaryFile() as f:
self.export(f.name)
xxd_c_dump(
src_path=f.name,
dst_path=header_path,
var_name=name,
chunk_len=20,
is_header=True,
)
|
cleanup
Cleanup temporary files
Source code in neuralspot_edge/converters/tflite/converter.py
| def cleanup(self):
"""Cleanup temporary files"""
self.tf_model_path.cleanup()
|