QuantizeML API
Layers
Reshaping
- class quantizeml.layers.QuantizedFlatten(*args, **kwargs)[source]
A Flatten layer that operates on quantized inputs
- class quantizeml.layers.QuantizedPermute(*args, **kwargs)[source]
A Permute layer that operates on quantized inputs
Note: Keras Permute layer simply wraps the Tensorflow transpose op.
- Parameters
dims (tuple of ints) – Permutation pattern does not include the samples dimension. Indexing starts at 1. For instance, (2, 1) permutes the first and second dimensions of the input.
Activations
- class quantizeml.layers.QuantizedReLU(*args, **kwargs)[source]
Quantized version of the ReLU activation layer applicable on FixedPoint tensor.
- Parameters
max_value (float, optional) – ReLU maximum value. Defaults to 6.
quant_config (dict, optional) – the serialized quantization configuration. Defaults to None.
Attention
- class quantizeml.layers.Attention(*args, **kwargs)[source]
Dot-product attention layer with configurable softmax.
Inputs are a tuple of tensors:
a query tensor of shape [batch, tokens, hidden],
a key tensor of shape [batch, tokens, hidden],
a value tensor of shape [batch, tokens, hidden].
The calculation follows the steps:
Split query, key, value per attention heads
q, k, v : [batch, tokens, hidden] -> [batch, token, num_heads, dim]
Calculate cross-token scores as a query-key dot product:
scores = tf.matmul(query, key, transpose_b=True)
scores : [batch, num_heads, token, token]
Rescale score by dividing by the squared-root of dim.
Use scores to calculate a mask
mask = softmax(scores)
Combine mask with value
output = tf.matmul(mask, value)
output: [batch, num_heads, token, dim]
Merge heads to get back to 2D
output: [batch, num_heads, token, dim] -> [batch, token, hidden]
- Parameters
num_heads (int) – the number of attention heads
softmax (str, optional) – ‘softmax’ or ‘shiftmax’. Defaults to ‘softmax’
- class quantizeml.layers.QuantizedAttention(*args, **kwargs)[source]
An Attention layer that operates on quantized inputs
- Parameters
num_heads (int) – the number of attention heads
quant_config (dict, optional) – the serialized quantization configuration. Defaults to None.
softmax (str, optional) – ‘softmax’ or ‘shiftmax’. Defaults to ‘shiftmax’
Normalization
- class quantizeml.layers.QuantizedBatchNormalization(*args, **kwargs)[source]
Layer that normalizes its inputs, on the last axis.
The normalization is applied like this:
\[\begin{split}y = \\frac{(x - \\mu) \\cdot \\gamma}{\\sigma} + \\beta \\ = \\frac{x \\cdot \\gamma}{\\sigma} - \\ \\frac{\\mu\\cdot \\gamma}{\\gamma} + \\beta\end{split}\]if we consider:
\[\begin{split}a = \\frac{\\gamma}{\\sigma}\end{split}\]and
\[\begin{split}b = -\\frac{\\mu\\cdot \\gamma}{\\sigma} + \\beta\end{split}\]The normalization can be re-written as:
\[\begin{split}y = a \\cdot x + b\end{split}\]Note that this layer will hold variables with names gamma, beta, moving_mean (\(\\mu\)), and moving_variance (\(\\sigma = \\sqrt{moving\_variance + \\epsilon}\)), so they can be converted from a BatchNormalization layer. However, it’s a and b that are going to be quantized.
- Parameters
quant_config (dict, optional) – the serialized quantization configuration. Defaults to None.
axis (int, optional) – The axis that was normalized on the BatchNormalization layer. The only supported value is the last dimension.
epsilon (float, optional) – Small value to avoid dividing by zero. Defaults to 1e-3.
- class quantizeml.layers.LayerMadNormalization(*args, **kwargs)[source]
Approximates the keras.layers.LayerNormalization (LN), replacing the computation of the standard deviation by the mean average deviation (mad).
Taking into account the complexity of computing the standard deviation, the LayerMadNormalization (LMN) is intended to replace the \(std(x)\) by \(mad(x)\) defined as:
\[mad(x) = \frac{sum(|x - mean(x)|)}{nb\_channels}\]To simplify it even more and make it more hardware-friendly, \(mean(x)\) is zeroed:
\[mad(x) = \frac{sum(|x|)}{nb\_channels}\]Then, the equation of the layer is defined as:
\[LMN(x) = \gamma\frac{x}{mad(x)} + \beta\]Note
A tuning step in the switching procedure between the LN to LMN layer will be required to find the \((\gamma, \beta)\) parameters that match the standard deviation changes.
Convolution
- class quantizeml.layers.PaddedConv2D(*args, **kwargs)[source]
A convolutional layer that can use custom padding values.
Note that when padding values are provided, padding ‘SAME’ will be applied with the provided value (overriding ‘padding’ parameter).
- Parameters
padding_value (float, list, tensor, optional) – the value or the list of values used when padding for the ‘same’ convolution type. Padding is per-tensor if one value is provided or per-channel otherwise. If None, zero-padding is used. Defaults to None.
- class quantizeml.layers.QuantizedConv2D(*args, **kwargs)[source]
A convolutional layer that operates on quantized inputs and weights.
Note that when padding values are provided, padding ‘SAME’ will be applied with the provided value (overriding ‘padding’ parameter).
- Parameters
quant_config (dict, optional) – the serialized quantization configuration. Defaults to None.
padding_value (float, list, tensor, optional) – the value or the list of values used when padding for the ‘same’ convolution type. Padding is per-tensor if one value is provided or per-channel otherwise. If None, zero-padding is used. Defaults to None.
Depthwise convolution
- class quantizeml.layers.QuantizedDepthwiseConv2D(*args, **kwargs)[source]
A depthwise convolutional layer that operates on quantized inputs and weights.
- Parameters
quant_config (dict, optional) – the serialized quantization configuration. Defaults to None.
Separable convolution
Dense
Skip connection
- class quantizeml.layers.Add(*args, **kwargs)[source]
Wrapper class of keras.layers.Add that allows to average inputs.
We only support a tuple of two inputs with the same shape.
- Parameters
average (bool, optional) – if True, compute the average across all inputs. Defaults to False.
activation (bool, optional) – If True, apply an activation function after the addition. Defaults to False.
- class quantizeml.layers.QuantizedAdd(*args, **kwargs)[source]
Sums two inputs and quantize the output.
The two inputs must be provided as a list or tuple of FixedPoint or Tensors.
The outputs are quantized according to the specified quantization configuration.
- Parameters
quant_config (dict, optional) – the serialized quantization configuration. Defaults to None.
Pooling
Shiftmax
- class quantizeml.layers.Shiftmax(*args, **kwargs)[source]
Wrapper class of shiftmax function, that calculates a softmax-like activation.
Note that shiftmax operation is performed always along the last axis.
- class quantizeml.layers.QuantizedShiftmax(*args, **kwargs)[source]
A quantized layer to do a quantized function similar to the softmax, but using base 2 instead of e. So we replace
\[softmax(x_i) = \frac{e^{x_i}}{sum(e^{x_k})}\]With this:
\[softmax2(x_i) = \frac{2^{x_i}}{sum(2^{x_k})}\]This approximation is close enough to the original function. In order to make it more hardware friendly, we also approximated the \(sum(2^{x_k})\) to the closest power of two:
\[shiftmax(x_i) = \frac{2^{x_i}}{2^{round(log2(sum(2^{x_k})))}}\]So it can be implemented with a simple shift operation.
Implementation is inspired from this paper:
Cardarilli, G.C., Di Nunzio, L., Fazzolari, R. et al. A pseudo-softmax function for hardware-based high speed image classification. Sci Rep 11, 15307 (2021). https://doi.org/10.1038/s41598-021-94691-7
- Parameters
quant_config (dict, optional) – the serialized quantization configuration. Defaults to None.
- quantizeml.layers.shiftmax(logits, axis=- 1)[source]
Computes softmax-like activations, but using base 2 for the exponential.
Used as approximation of the softmax activation.
This function performs the equivalent of
>>> logits = tf.floor(logits) >>> exp = 2 ** logits >>> sum_exp_shift = tf.round(tf.log2(tf.reduce_sum(exp, axis, keepdims=True))) >>> softmax = exp / 2 ** sum_exp_shift = 2 ** (logits - sum_exp_shift)
When 2 **
sum_exp_shift
is an approximated of sum_exp as a Power-of-Two (PoT)To avoid a high exponential (and a tf.inf representation by tensorflow), we adopt the following equivalence:
Making the variable change \(y=logits-x0\), we reach the same result as \(p=shiftmax(logits)\), because,
\[p' = \frac{2^y}{sum(2^y)} = \frac{2^{logits-x0}}{sum(2^{logits-x0})} = \frac{2^{logits} * 2^{-x0}}{2^{-x0} * sum(2^{logits})} = \frac{2^{logits}}{sum(2^{logits})} = p\]We take \(x0 = max(logits)\).
- Parameters
logits (tf.Tensor) – a non-empty Tensor.
axis (int, list, optional) – the dimension shiftmax would be performed on. The default is -1 which indicates the last dimension.
- Returns
value of shiftmax function with the same type and shape as logits.
- Return type
tf.Tensor
- Raises
InvalidArgumentError – if logits is empty or axis is beyond the last dimension of logits.
Note
We floor the
logits
to approximate the results to those expected when quantizing the operation.
Transformers
- class quantizeml.layers.ClassToken(*args, **kwargs)[source]
Append a class token to an input layer.
- Parameters
initializer (keras.initializers.Initializer) – Initializer for the class variable. Defaults to None.
- class quantizeml.layers.QuantizedClassToken(*args, **kwargs)[source]
Quantize the
ClassToken
layer, allowing quantization of the output.- Parameters
quant_config (dict, optional) – the serialized quantization configuration. Defaults to None.
- class quantizeml.layers.AddPositionEmbs(*args, **kwargs)[source]
Adds (optionally learned) positional embeddings to the inputs.
- Parameters
initializer (keras.initializers.Initializer) – Initializer for the class variable. Defaults to None.
- class quantizeml.layers.QuantizedAddPositionEmbs(*args, **kwargs)[source]
Quantize the
AddPositionEmbs
layer, allowing operations in FixedPoint domain.- Parameters
quant_config (dict, optional) – the serialized quantization configuration. Defaults to None.
Rescaling
- class quantizeml.layers.QuantizedRescaling(*args, **kwargs)[source]
A layer that multiplies integer inputs by a scale
This is a simplified version of the keras Rescaling layer:
it only supports a scalar scale,
it only supports zero offsets.
This layer assumes the inputs are 8-bit integer: it simply wraps them into an 8-bit per-tensor QFloat with the specified scale.
- Parameters
scale (float) – a scalar scale.
Dropout
Quantizers
- class quantizeml.layers.Quantizer(*args, **kwargs)[source]
The base class for all quantizers.
The bitwidth defines the number of quantization levels on which the values will be quantized. For a quantizer that accepts unsigned values, the maximum quantization level is \(2 ^ {bitwidth} - 1\). For a quantizer that accepts signed values, we lose one bit of precision to store the sign. When the quantizer is signed, the quantization interval is asymmetric around zero (i.e range: \([- 2 ^ {bitwidth - 1}, 2 ^ {bitwidth - 1} - 1]\)).
- Parameters
bitwidth (int) – the quantization bitwidth.
signed (bool, optional) – whether the quantizer expects signed values or unsigned. Defaults to True.
- class quantizeml.layers.WeightQuantizer(*args, **kwargs)[source]
Bases:
quantizeml.layers.quantizers.quantizers.Quantizer
A uniform quantizer that converts a float Tensor to a QFloat representation.
In order, the WeightQuantizer:
evaluates the scales required to align the values on optimal ranges for FixedPoint quantization,
quantizes the rescaled Tensor as a FixedPoint and returns a QFloat.
- Parameters
bitwidth (int, optional) – the quantization bitwidth, defaults to 4.
signed (bool, optional) – whether the quantizer expects signed values or unsigned. Defaults to True.
axis (int, optional) – the quantization range is a scalar (None) or a vector corresponding to the given axis. Defaults to -1.
fp_quantizer (bool, optional) – True to enable FixedPoint quantization, QFloat otherwise. Defaults to False.
Methods:
build
(input_shape)Build the layer.
call
(inputs)Quantize the float inputs
Get the config of the layer.
- build(input_shape)[source]
Build the layer.
- Parameters
input_shape (list) – the shape of input tensor.
- class quantizeml.layers.AlignedWeightQuantizer(*args, **kwargs)[source]
Bases:
quantizeml.layers.quantizers.quantizers.Quantizer
A uniform quantizer that converts a float Tensor to a QFloat representation.
Unlike its sibling the WeightQuantizer, it does not evaluate the fractional bits and scales of the resulting QFloat, but instead aligns them on those of another QFloat input.
- Parameters
bitwidth (int, optional) – the quantization bitwidth. Defaults to 8.
signed (bool, optional) – whether the quantizer expects signed values or unsigned. Defaults to True.
Methods:
call
(inputs, other)Quantize the float inputs, aligned on another QFloat
- call(inputs, other)[source]
Quantize the float inputs, aligned on another QFloat
The quantization is done in several steps:
Compute the quantization ranges,
Evaluate the maximum fractional bits,
Quantize the inputs as a QFloat,
Align the QFloat fractional bits on the other.
- Parameters
inputs (tf.Tensor) – the inputs tensor.
other (
QFloat
) – a tensor to align on.
- Returns
a quantized tensor with the same scales and frac_bits as other.
- Return type
QFloat
- class quantizeml.layers.OutputQuantizer(*args, **kwargs)[source]
Bases:
quantizeml.layers.quantizers.quantizers.Quantizer
A uniform FixedPoint quantizer that selects the optimal number of fractional bits for the range of its inputs and updates them accordingly.
The typical use case is to decrease the bitwidth of the result of a quantized layer operation to avoid a saturation in downstream operations.
If the input is a QFloat, it is converted to a FixedPoint before updating its bitwidth.
- Parameters
bitwidth (int, optional) – the quantization bitwidth. Defaults to 8.
signed (bool, optional) – whether the quantizer expects signed values or unsigned. Defaults to True.
axis (str, optional) – the quantization range is a scalar (‘per-tensor’) or a vector corresponding to the last axis (‘per-axis’). Defaults to ‘per-tensor’.
scale_bits – (int, optional): the bitwidth to use when quantizing output scales. Defaults to 8.
buffer_bitwidth – (int, optional): buffer bitwidth value. Defaults to 32.
Methods:
build
(input_shape)Build the layer.
call
(inputs)Quantize the QTensor inputs to a lower bitwidth.
Get the config of the layer.
- build(input_shape)[source]
Build the layer.
- Parameters
input_shape (list) – the shape of input tensor.
- call(inputs)[source]
Quantize the QTensor inputs to a lower bitwidth.
The quantization happens with the following steps:
Evaluate the nearest power(s) of two containing the quantization range(s)
Quantize the inputs.
- Parameters
inputs (
QTensor
) – the inputs tensor.- Returns
the quantized tensor.
- Return type
FixedPoint
Calibration
- class quantizeml.layers.OutputObserver(*args, **kwargs)[source]
Calibration layer.
This layer is used to compute the future range_max of the equivalent OutputQuantizer in the quantized model. It is placed where the OutputQuantizer will be inserted (end of blocks) and accumulates the observed maximum values (with momentum) for input in the float model.
- Parameters
axis (str) – the quantization range is a scalar (‘per-tensor’) or a vector corresponding to the last axis (‘per-axis’). Defaults to ‘per-tensor’.
momentum (float) – the momentum for the moving average. Defaults to 0.9.
Recording
- quantizeml.layers.recording(enable)[source]
Enable or disable recording.
- Parameters
enable (bool) – True to enable recording, False to disable it
- class quantizeml.layers.TensorRecorder(*args, name='', **kwargs)[source]
Wrapper class to store and retrieve a tf.Tensor extracted from a graph.
This is mainly used to recover FixedPoint alignment shift information.
- class quantizeml.layers.FixedPointRecorder(name='')[source]
Wrapper class to store and retrieve a FixedPoint extracted from a graph.
This is mainly used to recover FixedPoint quantized weights.
- class quantizeml.layers.QFloatRecorder(name='')[source]
Wrapper class to store and retrieve a QFloat extracted from a graph.
This is mainly used to recover QFloat quantized weights.
Models
Transforms
- quantizeml.models.transforms.align_rescaling(model)[source]
Aligns the Rescaling layer of the model to make it quantization ready.
This folds the offset into the bias of next layer.
The resulting Rescaling is therefore compatible with a quantization to a QuantizedRescaling.
If the source model does not contain a Rescaling or if its Rescaling is already aligned, then the original model is returned.
- Parameters
model (keras.Model) – the source Keras model
- Returns
the original model or a new model with Rescaling layer aligned
- Return type
keras.Model
- quantizeml.models.transforms.invert_batchnorm_pooling(model)[source]
Inverts pooling and BatchNormalization layers in a model to have BN layer before pooling.
Returns a new model where pooling and batch normalization layers are inverted. From a Keras model where pooling layers precede batch normalization layers, this function places the BN layers before pooling layers. This is the first step before folding BN layers into processing layers.
Note
Inversion of layers is equivalent only if the gammas of BN layers are positive. The function raises an error if not.
- Parameters
model (keras.Model) – a model
- Returns
the updated model
- Return type
keras.Model
- Raises
RuntimeError – if a candidate BatchNormalization layer has gamma values that are not strictly positive.
- quantizeml.models.transforms.fold_batchnorms(model)[source]
Returns a new model where BatchNormalization layers are folded into previous layers.
From a Keras model where BN layers follow processing layers, this function removes the BN layers and updates the preceding layers weights and bias accordingly. The new model is strictly equivalent to the previous one.
- Parameters
model (keras.Model) – a model
- Returns
the original model or a model with BN folded
- Return type
keras.Model
- quantizeml.models.transforms.insert_layer(model, target_layer_name, new_layer)[source]
Inserts the given layer in the model after the layer with the name target_layer_name.
Note that new_layer type is restricted to (OutputQuantizer, Dequantizer).
- Parameters
model (keras.Model) – the model to update
target_layer_name (str) – name of the layer after which to insert a layer
new_layer (keras.layers.Layer) – layer to insert
- Raises
ValueError – when target_layer_name is not found in model or new_layer is not in (OutputQuantizer, Dequantizer)
- Returns
the new model
- Return type
keras.Model
- quantizeml.models.transforms.insert_rescaling(model, scale, offset)[source]
Inserts a Rescaling as first layer of the Model (after the Input)
- Parameters
model (keras.Model) – the model to update
scale (float) – the Rescaling scale
offset (float) – the Rescaling offset
- Raises
ValueError – when the Model does not have an Input layer.
- Returns
the new model
- Return type
keras.Model
- quantizeml.models.transforms.invert_relu_maxpool(model)[source]
Inverts ReLU and MaxPool2D layers in a model to have MaxPool2D before ReLU.
This transformation produces a strictly equivalent model.
- Parameters
model (keras.Model) – a model
- Returns
keras.Model: the original model or the updated model
- Return type
keras.Model
- quantizeml.models.transforms.remove_zeropadding2d(model)[source]
Removes ZeroPadding2D layers from a model.
ZeroPadding2D layers will not be supported by quantization so this transform adds support so that when the ZeroPadding2D layers are immediately followed by a convolution layer with ‘valid’ padding, they are removed and the following convolution is updated with a ‘same’ padding instead. This can however only happen when the padding specified in ZeroPadding2D actually corresponds to a ‘same’ padding.
- Parameters
model (keras.Model) – the model to update
- Returns
the original model or a new model with ZeroPadding2D removed
- Return type
keras.Model
- quantizeml.models.transforms.replace_lambda(model)[source]
Replaces lambda layers from a model with their equivalent Keras layer.
This transform handles the following replacements:
Lambda(relu) or Activation(‘relu’) → ReLU,
Lambda(transpose) → Permute,
Lambda(reshape) → Reshape,
Lambda(add) → Add,
Lambda(‘gelu’) → Activation(‘gelu’),
Lambda(‘silu’) → Activation(‘silu’).
- Parameters
model (keras.Model) – the model of interest
- Returns
the original model or a new one with lambda replaced.
- Return type
keras.Model
- quantizeml.models.transforms.sanitize(model)[source]
Sanitize a model preparing it for quantization.
This is a wrapping successive calls to several model transformations which aims at making the model quantization ready.
- Parameters
model (keras.Model) – the input model
- Returns
the sanitized model
- Return type
keras.Model
Quantization
- quantizeml.models.quantize(model, q_config=None, qparams=QuantizationParams(activation_bits=8, per_tensor_activations=False, weight_bits=8, output_bits=8, input_weight_bits=8, buffer_bits=32), samples=None, num_samples=1024, batch_size=None, epochs=1, quantize_until=None)[source]
Quantizes a Keras or ONNX model using the provided configuration or parameters.
Details on how this function behaves:
q_config has priority over qparams, meaning that when a match is found in q_config the given configuration will be used instead of qparams. This is useful to handle specific cases (e.g per-tensor output quantizer). This is only used when quantizing Keras models.
when no configuration is given, quantization parameters are deduced from qparams and OutputQuantizers are automatically set on appropriate layers.
qparams are only applied to ‘float’ Keras layers when they are first quantized. As a result, when re-quantizing a model, one must provide a complete q_config. This is made easy with the dump_config helper. Note the only configuration supported when quantizing ONNX models is 8-bit for weights and activations, but per_tensor_activations param will be taken into account.
If not already present, a final Dequantizer will be added at the end of the Model.
The model will also be calibrated using the provided (or randomly generated inputs).
- Parameters
model (keras.Model or ModelProto) – the model to quantize
q_config (dict, optional) – quantization configuration as a dictionary mapping layer names to their quantization configuration. Defaults to None.
qparams (QuantizationParams, optional) – global quantization parameters. Defaults to QuantizationParams().
samples (tf.Dataset, np.array or generator, optional) – calibration samples. When no samples are provided, random samples are generated. Defaults to None.
num_samples (int, optional) – number of samples to use in the provided samples or number of samples to generate. Defaults to 1024.
batch_size (int, optional) – the batch size. Defaults to None.
epochs (int, optional) – the number of epochs. This parameter must be 1 for ONNX models. Defaults to 1.
quantize_until (str, optional) – name of the layer/node until which to quantize: other ones after it will stay unchanged. Defaults to None.
- Returns
the quantized model
- Return type
keras.Model or ModelProto
- quantizeml.models.dump_config(model)[source]
Dump the quantization configuration of a quantized model, exporting the configuration for each quantized layer.
- Parameters
model (keras.Model) – a quantized model.
- Returns
the configuration of the model.
- Return type
dict
- quantizeml.models.record_quantization_variables(model)[source]
Helper method to record quantization objects in the graph.
Passing a dummy sample through the model in recording mode, this triggers the recording of all dynamic quantization objects.
- Parameters
model (keras.Model) – model for which objects need to be recorded.
Quantization parameters
- class quantizeml.models.QuantizationParams(activation_bits=8, per_tensor_activations=False, weight_bits=8, output_bits=8, input_weight_bits=8, buffer_bits=32)[source]
Class that holds quantization parameters.
This is a read-only data class.
- Parameters
activation_bits (int, optional) – activations quantization bitwidth. Defaults to 8.
per_tensor_activations (bool, optional) – whether to quantize activation per-tensor or per-axis. Defaults to False.
weight_bits (int, optional) – weights quantization bitwidth. Defaults to 8.
output_bits (int, optional) – outputs quantization bitwidth. Defaults to 8.
input_weight_bits (int, optional) – weights quantization bitwidth for the first layer. Defaults to 8.
buffer_bits (int, optional) – maximal buffer bitwidth allowed in operations. Defaults to 32.
- quantizeml.models.get_quantization_params()[source]
Returns global quantization parameters.
- Returns
the quantization parameters
- Return type
- quantizeml.models.quantization(qparams)[source]
Sets quantization parameters in a context.
- Parameters
qparams (QuantizationParams) – quantization parameters
Calibration
- quantizeml.models.calibrate(model, qmodel, samples=None, num_samples=1024, batch_size=None, epochs=1)[source]
Calibrates the model using the provided samples.
With TENN models only np.array samples are supported for calibration. Those should have a temporally coherent data, which means that their expected shape is [batch_size*Seq, dim_0,, …, dim_n] for spatiotemporal TENNs where:
batch_size is the same batch_size provided to the calibration.
Seq is a dataset parameter that defines the temporally coherent data (eg number of frames per video clips).
and [batch_size, (model.input_shape)] for recurrent TENNs.
When no samples are provided, random samples are generated.
- Parameters
model (keras.Model) – the original model
qmodel (keras.Model) – the quantized model to calibrate
samples (tf.Dataset, np.array or generator, optional) – calibration samples. When no samples are provided, random samples are generated. Defaults to None.
num_samples (int, optional) – number of samples to use in the provided samples or number of samples to generate. Defaults to 1024.
batch_size (int, optional) – the batch size. Defaults to None.
epochs (int, optional) – the number of epochs. Defaults to 1.
- quantizeml.models.calibration_required(model)[source]
Checks if a model requires calibration.
If one of the ‘OutputQuantizer’ layers in the model has its range_max variable set to 1, it requires calibration.
- Parameters
model (keras.Model) – the model to check
- Returns
True if calibration is required, False otherwise.
- Return type
bool
Utils
- quantizeml.models.apply_weights_to_model(model, weights, verbose=True)[source]
Loads weights from a dictionary and apply it to a model.
Go through the dictionary of weights, find the corresponding variable in the model and partially load its weights.
- Parameters
model (keras.Model) – the model to update
weights (dict) – the dictionary of weights
verbose (bool, optional) – if True, throw warning messages if a dict item is not found in the model. Defaults to True.
Tensors
QTensor
- class quantizeml.tensors.QTensor(shape: tensorflow.python.framework.tensor_shape.TensorShape)[source]
Bases:
tensorflow.python.framework.extension_type.ExtensionType
Abstract class to exchange quantized tensors between layers
Classes:
alias of
quantizeml.tensors.qtensor.QTensor.Spec
Methods:
Asserts that a QTensor is quantized per-tensor
clone
()Returns a copy of the QTensor
to_float
()Returns a float representation of the QTensor
Attributes:
Returns the QTensor name
Returns if QTensor is quantized per-tensor
- Spec
alias of
quantizeml.tensors.qtensor.QTensor.Spec
Classes:value_type
alias of
quantizeml.tensors.qtensor.QTensor
- property name
Returns the QTensor name
- Returns
the QTensor name
- Return type
str
- property per_tensor
Returns if QTensor is quantized per-tensor
- Returns
True if QTensor is quantized per-tensor or False on per-axis case.
- Return type
bool
FixedPoint
- class quantizeml.tensors.FixedPoint(values, value_bits, frac_bits)[source]
Bases:
quantizeml.tensors.qtensor.QTensor
A Tensor of integer values representing fixed-point numbers
The value_bits parameter sets the maximum integer values that can be stored:
\[int\_max = 2^{bits} - 1.\]When a FixedPoint is created, its values are clipped to [-int_max-1, int_max].
- Parameters
values (tf.Tensor) – a tensor of integer values
value_bits (int) – the number of value bits.
frac_bits (tf.Tensor) – an integer tensor of fractional bits.
Classes:
alias of
quantizeml.tensors.fixed_point.FixedPoint.Spec
Methods:
abs
()Returns the absolute value of the FixedPoint
align
(other[, value_bits])Align fractional bits
downscale
(frac_bits)Reduce the precision of a FixedPoint
expand
(value_bits)Expand the FixedPoint to the specified bitwidth
floor
()Floors the FixedPoint
max_frac_bits
(value_bits, ranges[, clamp])Evaluate the maximum fractional bit index for the quantization ranges.
promote
(bits)Increase the number of value bits
quantize
(x, value_bits[, frac_bits])Converts a float Tensor to a FixedPoint
rescale
(frac_bits[, value_bits])Rescale a FixedPoint to a specified precision and bitwidth
shift
(s)Apply a tensor-wide left or right shift.
to_float
()Returns a float representation of the QTensor
upscale
(frac_bits[, value_bits])Align a FixedPoint to a specified precision
Attributes:
Returns the QTensor name
Returns if QTensor is quantized per-tensor
Returns the sign of the FixedPoint
- Spec
alias of
quantizeml.tensors.fixed_point.FixedPoint.Spec
Classes:value_type
- align(other, value_bits=None)[source]
Align fractional bits
This returns an equivalent FixedPoint with a scalar fractional bit corresponding to the maximum of the current and other FixedPoint on all channels.
This is required before performing an operation that adds or subtracts elements along the last dimension, to make sure all these elements are in the same scale.
- Parameters
other (
FixedPoint
) – a FixedPoint to align tovalue_bits (int, optional) – the target value bits. Defaults to None.
- Returns
a new FixedPoint with aligned fractional bits and the shift that was applied.
- Return type
tuple(
FixedPoint
, tf.Tensor)
- downscale(frac_bits)[source]
Reduce the precision of a FixedPoint
- Parameters
frac_bits (tf.Tensor) – the target fractional bits
- Returns
the downscaled FixedPoint
- Return type
- expand(value_bits)[source]
Expand the FixedPoint to the specified bitwidth
This returns an equivalent FixedPoint with a higher or equal number of value bits and a scalar fractional bit corresponding to the maximum of the initial fractional bits on all channels.
This is mostly used to recover a per-tensor FixedPoint that has been compressed to a lower number of value bits.
- Parameters
value_bits (int) – the target value_bits
- Returns
a new FixedPoint with expanded fractional bits and the shift that was applied.
- Return type
tuple(
FixedPoint
, tf.Tensor)
- floor()[source]
Floors the FixedPoint
- Returns
a new FixedPoint without fractional bits and the shift that was applied.
- Return type
tuple(
FixedPoint
, tf.Tensor)
- static max_frac_bits(value_bits, ranges, clamp=True)[source]
Evaluate the maximum fractional bit index for the quantization ranges.
This method evaluates the minimum number of integer bits required to cover the specified quantization ranges (this can be a negative number if the ranges are strictly lower than 0.5).
From that it deduces the rightmost fractional bit indices.
The resulting frac_bits can be a negative number if the ranges are higher than the biggest integer that can be represented with the specified value bits.
If specified, the maximum fractional bits are clamped to the available value_bits.
- Parameters
value_bits (int) – the number of value bits.
(tf (ranges) – Tensor): a tensor of float quantization ranges.
clamp (bool, optional) – clamp the results to self.value_bits. Defaults to True.
- Returns
Tensor: a tensor of fractional bits.
- Return type
tf
- property name
Returns the QTensor name
- Returns
the QTensor name
- Return type
str
- property per_tensor
Returns if QTensor is quantized per-tensor
- Returns
True if QTensor is quantized per-tensor or False on per-axis case.
- Return type
bool
- promote(bits)[source]
Increase the number of value bits
- Parameters
bits (int) – the new number of value bits
- Returns
a FixedPoint with increased value bits
- Return type
- static quantize(x, value_bits, frac_bits=None)[source]
Converts a float Tensor to a FixedPoint
It converts the original float values into integer values so that:
\[{x_{int}} = round(x * 2^{frac\_bits})\]Note: \(2^{-frac\_bits}\) represents the FixedPoint precision.
Before returning, the resulting integer values are clipped to the maximum integer values that can be stored for the specified value bits:
\[[-2^{value\_bits}, 2^{value\_bits} - 1]\]If frac_bits is not specified, the method starts by evaluating the number of bits to dedicate to represent the integer part of the float tensor, clipped to value_bits:
\[int\_bits = ceil(log2(x))\]Note: this number can be negative when x < 0.5.
It then evaluates the offset of the least significant bit of the fractional part of the float starting from zero. This represents the fractional bits:
\[frac\_bits = value\_bits - int\_bits\]- Parameters
x (tf.Tensor) – a tensor of float values.
value_bits (int) – the number of value bits
frac_bits (tf.Tensor, optional) – an integer tensor of fractional bits. Defaults to None.
- Returns
the FixedPoint tensor
- Return type
- rescale(frac_bits, value_bits=None)[source]
Rescale a FixedPoint to a specified precision and bitwidth
This primarily rescales the FixedPoint values to match the precision specified by the target fractional bits.
Optionally, this adjusts the value bits to the specified bitwidth.
The rescaling operation is:
a left shift of the values when their precision increases,
a rounded right shift of the values when their precision decreases.
This method can be used to:
compress a FixedPoint to a lower bitwidth after having reduced its precision,
expand a FixedPoint to a larger bitwidth after having increased its precision.
- Parameters
frac_bits (tf.Tensor) – the target fractional bits
value_bits (int, optional) – the target value bits
- Returns
the rescaled FixedPoint
- Return type
- shift(s)[source]
Apply a tensor-wide left or right shift.
This takes a tensor of shift values and apply them on each item of the FixedPoint values.
The shift values should positive or negative integer:
if the value is positive, it is a left-shift,
if the value is negative, it is a right-shift.
The resulting FixedPoint has the same value bits and fractional bits as the source FixedPoint, which means that clipping is applied on left-shift and flooring is applied on right-shift.
- Parameters
s (tf.Tensor) – the shift values for each pixel.
- Returns
the result as a FixedPoint
- Return type
- property sign
Returns the sign of the FixedPoint
- Returns
the sign as a FixedPoint.
- Return type
- to_float()[source]
Returns a float representation of the QTensor
- Returns
the float representation.
- Return type
tf.Tensor
- upscale(frac_bits, value_bits=None)[source]
Align a FixedPoint to a specified precision
The target precision must be higher than the current one.
- Parameters
frac_bits (tf.Tensor) – the target fractional bits
value_bits (int, optional) – the target value bits
- Returns
the upscaled FixedPoint
- Return type
QFloat
- class quantizeml.tensors.QFloat(fp, scales)[source]
Bases:
quantizeml.tensors.qtensor.QTensor
A Tensor of FixedPoint values and scales representing float numbers
The QFloat is a dual representation of a float Tensor combining FixedPoint values and float scales.
The QFloat is typically used to represent float tensors whose quantization range is not ‘optimal’ for FixedPoint quantization: the original tensor is first divided by the scales to be aligned on optimal ranges, then quantized to FixedPoint values.
When converting back to float, values are dequantized and multiplied by the scales to obtain the approximated float tensor.
- Parameters
fp (
FixedPoint
) – a FixedPoint of valuesscales (tf.Tensor) – a Tensor of scales
Classes:
alias of
quantizeml.tensors.qfloat.QFloat.Spec
Methods:
expand
(value_bits)Expand the QFloat to the specified bitwidth
max_frac_bits
(value_bits, ranges, scales[, ...])Evaluate the maximum fractional bit index for the quantization ranges.
optimal_scales
(ranges, value_bits)Evaluates the optimal QFloat scales for quantization ranges.
promote
(bits)Increase the number of value bits
quantize
(x, value_bits, scales[, frac_bits])Converts a float Tensor to a QFloat
quantize_scales
(scales, scale_bits)Quantizes the QFloat scales with the specified bitwidth.
to_fixed_point
([scale_bits])Returns a FixedPoint representation of the QFloat
to_float
()Returns a float representation of the QFloat
upscale
(frac_bits[, value_bits])Align a QFloat to a specified precision
Attributes:
Returns the QTensor name
Returns if QTensor is quantized per-tensor
- Spec
alias of
quantizeml.tensors.qfloat.QFloat.Spec
Classes:value_type
alias of
quantizeml.tensors.qfloat.QFloat
- expand(value_bits)[source]
Expand the QFloat to the specified bitwidth
This returns an equivalent QFloat with a higher or equal number of value bits and a scalar fractional bit corresponding to the maximum of the initial fractional bits on all channels. The scales remains unchanged.
This is mostly used to recover a per-tensor QFloat that has been compressed to a lower number of value bits.
Note that even if the frac_bits are aligned the scales remained unchanged.
- Parameters
value_bits (int) – the target value_bits
- Returns
a new QFloat with expanded fractional bits and the shift that was applied.
- Return type
tuple(
QFloat
, tf.Tensor)
- static max_frac_bits(value_bits, ranges, scales, clamp=True)[source]
Evaluate the maximum fractional bit index for the quantization ranges.
This method evaluates the minimum number of integer bits required to cover the specified quantization ranges after having rescaled them with the specified scales. It simply calls the equivalent FixedPoint method on the rescaled ranges. If specified, it clamps the results to the available value_bits.
- Parameters
value_bits (int) – the number of value bits.
ranges (tf.Tensor) – a tensor of float quantization ranges.
scales (tf.Tensor) – the scales to apply to the quantization ranges.
clamp (bool, optional) – clamp the results to self.value_bits. Defaults to True.
- Returns
a tensor of fractional bits.
- Return type
tf.Tensor
- property name
Returns the QTensor name
- Returns
the QTensor name
- Return type
str
- static optimal_scales(ranges, value_bits)[source]
Evaluates the optimal QFloat scales for quantization ranges.
We choose the optimal quantization range for a given bitwidth as:
[-int_max, int_max], with \(int\_max = 2^{bits} - 1\).
This methods evaluates the scales as the ratio to align the specified ranges to the optimal ranges.
- Parameters
ranges (tf.Tensor) – a tensor of quantization ranges.
value_bits (int) – the number of value bits.
- Returns
the optimal scales.
- Return type
tf.Tensor
- property per_tensor
Returns if QTensor is quantized per-tensor
- Returns
True if QTensor is quantized per-tensor or False on per-axis case.
- Return type
bool
- promote(bits)[source]
Increase the number of value bits
- Parameters
bits (int) – the new number of value bits
- Returns
a QFloat with increased value bits
- Return type
- static quantize(x, value_bits, scales, frac_bits=0.0)[source]
Converts a float Tensor to a QFloat
It first evaluates and quantizes the scales required to align the quantization ranges to the optimal range for the specified value bits.
It then quantizes the inputs with the quantized scales.
The resulting integer values are clipped to [-int_max-1, int_max].
- Parameters
x (tf.Tensor) – a tensor of float values.
value_bits (int) – the number of value bits.
scales (tf.Tensor) – a tensor of alignment scales.
frac_bits (int) – the inner FixedPoint fractional bits (defaults to 0).
- Returns
the QFloat representation.
- Return type
- static quantize_scales(scales, scale_bits)[source]
Quantizes the QFloat scales with the specified bitwidth.
- Parameters
scales (tf.Tensor) – a tensor of float scales.
scale_bits (int) – the number of scales bits.
- Returns
the FixedPoint scales.
- Return type
- to_fixed_point(scale_bits=8)[source]
Returns a FixedPoint representation of the QFloat
- Parameters
scale_bits (int, optional) – the scales quantization bitwidth. Defaults to 8.
- Returns
the FixedPoint representation and scales.
- Return type
- to_float()[source]
Returns a float representation of the QFloat
- Returns
the float representation.
- Return type
tf.Tensor
- upscale(frac_bits, value_bits=None)[source]
Align a QFloat to a specified precision
The target precision must be higher than the current one.
- Parameters
frac_bits (tf.Tensor) – the target fractional bits
value_bits (int, optional) – the target value bits (defaults to current value bits)
- Returns
the upscaled FixedPoint
- Return type
ONNX support
Layers
- quantizeml.onnx_support.layers.OnnxLayer(base_name, name='', **kwargs)[source]
Abstract class that represents an onnx subgraph in brainchip domain.
Child must define the attributes on __init__ and return the node list (subgraph) on build_subgraph(). If these requirements are met, make_node() could be used to define/register the custom node.
- Parameters
base_name (str) – the operation type base name.
name (str, optional) – the node name. Defaults to ‘’.
kwargs (dict, optional) – the custom attributes. Each attribute type will be infered by
onnx.helper.make_attribute()
. Defaults to {}.
- quantizeml.onnx_support.layers.QuantizedConv2D(strides=[1, 1], pool_type='none', pool_size=(2, 2), pool_strides=(2, 2), pool_pads=[0, 0, 0, 0], activation=False, name='')[source]
Intermediate representation of QLinearConv() + MaxPool() + ReLU() as an exportable node.
- Parameters
strides (list of int, optional) – the convolutional strides. Defaults to [1, 1].
pool_type (str, optional) – the pool type, one of {“none”, “max”, “gap”}. Defaults to “none”.
pool_size (list of int, optional) – the kernel pool shape. Ignore it when pool_type != “max”. Defaults to (2, 2).
pool_stride (list of int, optional) – the kernel strides. Ignore it when pool_type != “max”. Defaults to (2, 2).
pool_pads (list of int, optional) – the size of each padding dimension. Ignore it when pool_type != “max”. Defaults to [0, 0, 0, 0].
input_conv (bool, optional) – whether it is extended the set of operations of the basic QuantizedConv2D, allowing to modify the padding value per input channel. Defaults to False.
activation (bool, optional) – whether to apply relu operation. Defaults to False.
name (str, optional) – the node name. Defaults to ‘’.
- quantizeml.onnx_support.layers.QuantizedDepthwise2D(strides=[1, 1], activation=False, name='')[source]
Intermediate representation of Conv() + MaxPool() + ReLU() as an exportable node.
- Parameters
strides (list of int, optional) – the convolutional strides. Defaults to [1, 1].
activation (bool, optional) – whether to apply relu operation. Defaults to False.
name (str, optional) – the node name. Defaults to ‘’.
- quantizeml.onnx_support.layers.QuantizedConv2DTranspose(strides=[1, 1], activation=False, name='')[source]
Intermediate representation of the upsampling layer QuantizedConv2DTranspose().
- Parameters
strides (list of int, optional) – the convolutional strides. Defaults to [1, 1].
activation (bool, optional) – whether to apply relu operation. Defaults to False.
name (str, optional) – the node name. Defaults to ‘’.
- quantizeml.onnx_support.layers.QuantizedDepthwise2DTranspose(strides=[1, 1], activation=False, name='')[source]
Intermediate representation of the upsampling layer QuantizedDepthwise2DTranspose. Inherits from QuantizedConv2DTranspose: only different attribute is group.
- Parameters
strides (list of int, optional) – the convolutional strides. Defaults to [1, 1].
activation (bool, optional) – whether to apply relu operation. Defaults to False.
name (str, optional) – the node name. Defaults to ‘’.
- quantizeml.onnx_support.layers.QuantizedDense1D(flatten=False, activation=False, name='')[source]
Intermediate representation of Flatten() + QGemm() + ReLU() as an exportable node.
- Parameters
flatten (bool, optional) – whether to flatten the inputs. Defaults to False.
activation (bool, optional) – whether to apply relu operation. Defaults to False.
name (str, optional) – the node name. Defaults to ‘’.
- quantizeml.onnx_support.layers.QuantizedAdd(activation=False, name='')[source]
Intermediate representation of Add() as an exportable node.
- Parameters
activation (bool, optional) – whether to apply relu operation. Defaults to False.
name (str, optional) – the node name. Defaults to ‘’.
- quantizeml.onnx_support.layers.QuantizedConcat(axis, name='')[source]
Intermediate representation of Concatenate() as an exportable node.
- Parameters
name (str, optional) – the node name. Defaults to ‘’.
- quantizeml.onnx_support.layers.InputQuantizer(input_tp, perm=[], input_signed=False, name='')[source]
Intermediate representation of QuantizeLinear(), use to quantize the input.
- Parameters
input_tp (TensorProto) – the input of the ONNX model.
perm (list, optional) – list representing the permutations of the rescale node. Defaults to [].
input_signed (bool, optional) – whether the input is signed. Defaults to False.
name (str, optional) – the node name. Defaults to ‘’.
Custom patterns
- quantizeml.onnx_support.quantization.custom_pattern_scope(patterns)[source]
Register a custom pattern in the context to be used at quantization time.
A pattern is understood as a sequence of continuous operations in the graph, whose representation can converge in an
OnnxLayer
.- Parameters
patterns (dict) – a list of sequence of nodes (keys) and their mapper function (values).
Model I/O
- quantizeml.load_model(model_path, custom_layers=None, compile_model=True)[source]
Loads an Onnx or Keras model. An error is raised if the provided model extension is not supported.
- Parameters
model_path (str) – path of the model to load.
custom_layers (dict, optional) – custom layers to add to the Keras model. Defaults to None.
compile_model (bool, optional) – whether to compile the Keras model. Defaults to True.
- Returns
Loaded model.
- Return type
keras.models.Model or onnx.ModelProto
- Raises
ValueError – if the model could not be loaded using Keras and ONNX loaders.
- quantizeml.save_model(model, path)[source]
Save an ONNX or Keras model into a path.
Note extension is overwritten given the model type.
- Parameters
model (keras.Model, keras.Sequential or onnx.ModelProto) – model to serialize.
model_path (str) – path to save the model.
- Returns
the path where the model was saved.
- Return type
str
- Raises
ValueError – if the model to save is not a Keras or ONNX model.
Analysis
Kernel distribution
- quantizeml.analysis.plot_kernel_distribution(model, logdir)[source]
Plot the kernel distribution of each layer/node in the model.
Distributions are plotted in two ways: histogram and boxplot
After exporting them, the plots can be plotted through the command-line:
>>> tensorboard --logdir=`logdir`
- Parameters
model (onnx.ModelProto or tf.keras.Model) – the model to plot the kernel distribution
logdir (str) – the directory to save the plots
Quantization error
- quantizeml.analysis.measure_layer_quantization_error(fmodel, qmodel, target_layer=None, batch_size=16, seed=None)[source]
Measures the layer quantization error
Returns a dictionary where the keys are the name of each layer and the values are a dictionary composed of the set of the following metrics:
Symmetrical Mean Absolute Percentage Error (SMAPE):
tools.metrics.SMAPE()
Saturation: Percentage of how many values in the quantized layer saturate
Example
>>> summary = measure_layer_quantization_error(fmodel, qmodel) >>> assert isinstance(summary[a_layer_name], dict) >>> assert "SMAPE" in summary[a_layer_name]
- Parameters
fmodel (onnx.ModelProto or tf.keras.Model) – the float model.
qmodel (onnx.ModelProto or tf.keras.Model) – the quantized version of fmodel.
target_layer (str, optional) – computation error is performed only in the target layer/node, expanding the analysis to each output channel. Defaults to None.
batch_size (int, optional) – the batch size of the samples to be generated. It allows a better metrics generalization, but consumes more resources. Defaults to 16.
seed (int, optional) – a random seed. Defaults to None.
- Returns
the quantization error for each layer
- Return type
dict
Notes
Layers/Nodes that do not produce quantization errors will not be taken into account (e.g. QuantizedReshape).
- quantizeml.analysis.measure_cumulative_quantization_error(fmodel, qmodel, target_layer=None, batch_size=16, seed=None)[source]
Measures the cumulative quantization error
Returns a dictionary where the keys are the name of each layer and the values are a dictionary composed of the set of the following metrics:
Symmetrical Mean Absolute Percentage Error (SMAPE):
tools.metrics.SMAPE()
Saturation: Percentage of how many values in the quantized layer saturate
Each metric measures the quantization error from the input to the layer.
Example
>>> summary = measure_cumulative_quantization_error(fmodel, qmodel) >>> assert isinstance(summary[a_layer_name], dict) >>> assert "SMAPE" in summary[a_layer_name]
- Parameters
fmodel (onnx.ModelProto or tf.keras.Model) – the float model.
qmodel (onnx.ModelProto or tf.keras.Model) – the quantized version of fmodel.
target_layer (str, optional) – error computation is performed only in the target layer/node, expanding the analysis to each output channel. Defaults to None.
batch_size (int, optional) – the batch size of the samples to be generated. It allows a better metrics generalization, but consumes more resources. Defaults to 16.
seed (int, optional) – a random seed. Defaults to None.
- Returns
the quantization error for each layer
- Return type
dict
Notes
Layers/Nodes that do not produce quantization errors will not be taken into account (e.g. QuantizedReshape).
Metrics
- quantizeml.analysis.tools.SMAPE(*args, **kwargs)[source]
Compute the Symmetric Mean Absolute Percentage Error (SMAPE) as:
>>> mean(abs(x - y) / (abs(x) + abs(y)))
Reference: https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error
- Parameters
name (str, optional) – name of the metric. Defaults to “smape”.
- quantizeml.analysis.tools.Saturation(*args, **kwargs)[source]
Returns the percentage of saturating values.
We consider a value saturated if it is one of {min_value, max_value}
- Parameters
min_value (np.ndarray, optional) – the minimum of values. If not provided, it is inferred from the values type. Defaults to None.
max_value (np.ndarray, optional) – the maximum of values. If not provided, it is inferred from the values type. Defaults to None.
- quantizeml.analysis.tools.print_metric_table(summary, model_name='')[source]
Print a table with the results of a set of metrics.
The following format is expected:
# Format for metrics # 1. Simple set of metrics metrics_for_key_1 = {"metric_1": key1_metric1_value, "metric_2": key1_metric2_value} # 2. List of simple set of metrics metrics_for_key_2 = [{"metric_1": key2_metric1_value1, "metric_2": key2_metric2_value1}, {"metric_1": key2_metric1_value2, "metric_2": key2_metric2_value2}] # 3. List of complex set of metrics metrics_for_key_3 = [metrics_for_key_2, metrics_for_key_2] # Summary summary = { "key_1": metrics_for_key_1, "key_2": metrics_for_key_2, "key_3": metrics_for_key_3, }
- Parameters
summary (dict) – summary of metrics to draw
model_name (str, optional) – A model name to display. Defaults to “”.
Note
All metrics must contain the same set of measures