#!/usr/bin/env python
# ******************************************************************************
# Copyright 2023 Brainchip Holdings Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ******************************************************************************
__all__ = ["InputQuantizer", "Dequantizer"]
import numpy as np
from onnx.helper import make_node
from .base_layer import OnnxLayer
from ..graph_tools.tensor import TENSOR_SHAPE, value_info_to_tensor_shape, array_to_tp
from ..quantization.input_scale import input_zp_scale
[docs]class Dequantizer(OnnxLayer):
"""Intermediate representation of DequantizeLinear(), use to dequantize the input.
Args:
name (str, optional): the node name. Defaults to ''.
"""
def __init__(self, name=''):
super().__init__("Dequantizer", name=name)
def __build__(self, input_ts, downscale=True):
assert input_ts.dtype in (np.int8, np.int32)
# Compute output shape
output_ts = TENSOR_SHAPE(input_ts.shape, np.dtype("float32"))
return output_ts
def quantize(self, qinput):
# To keep homogenity with the other layers, this function is called 'quantize'
# even though it does the opposite (dequantize): apply scale in the input integers.
if self._output is None or self._input is None:
# Build the layer if required
self.build(qinput.output)
# Scale to set in weights is the reciprocal of ONNX calibrated one.
i_scale = qinput.weights["scale"]
scale = np.array(1 / i_scale, dtype=np.float32)
# Return ONNX node and weights
weights = {f"{self.name}/deq_scale": scale}
inputs = [ts.name for ts in self._input] + list(weights)
onnx_node = self.make_node(inputs, [self.output.name])
onnx_weights = array_to_tp(**weights)
return onnx_node, onnx_weights
@staticmethod
def build_subgraph(op_type):
return [make_node('DequantizeLinear', inputs=["X", 'scale'], outputs=["Y"])]