Voice et bot modif
This commit is contained in:
parent
189d56026b
commit
7333a22bcd
10774 changed files with 634644 additions and 933308 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -353,6 +353,14 @@ class MinMaxCalibrater(CalibraterBase):
|
|||
return opset_import.version
|
||||
raise RuntimeError(f"Model does not contain a version for '{op_type}'.")
|
||||
|
||||
def insert_nodes(tensor_name, new_nodes):
|
||||
index = next(
|
||||
(i for i, x in enumerate(self.model.graph.node) if tensor_name in x.input), len(self.model.graph.node)
|
||||
)
|
||||
for node in new_nodes:
|
||||
self.model.graph.node.insert(index, node)
|
||||
index += 1
|
||||
|
||||
def add_reduce_min_max(tensor_name, reduce_op_name):
|
||||
# When doing ReduceMax/ReduceMin, ORT can't reduce on dim with value of 0 if 'keepdims' is false.
|
||||
# To make the code simple, we always let keepdims to be 1.
|
||||
|
|
@ -396,7 +404,7 @@ class MinMaxCalibrater(CalibraterBase):
|
|||
reduce_node.input.append(reduce_axes_name)
|
||||
self.model.graph.initializer.append(reduce_axes)
|
||||
|
||||
self.model.graph.node.extend([reduce_node, reshape_node])
|
||||
insert_nodes(tensor_name, [reduce_node, reshape_node])
|
||||
self.model.graph.output.append(helper.make_tensor_value_info(reduce_output, onnx_type, [None]))
|
||||
|
||||
for tensor in tensors:
|
||||
|
|
@ -417,7 +425,14 @@ class MinMaxCalibrater(CalibraterBase):
|
|||
inputs = data_reader.get_next()
|
||||
if not inputs:
|
||||
break
|
||||
self.intermediate_outputs.append(self.infer_session.run(None, inputs))
|
||||
self.intermediate_outputs.append(
|
||||
[
|
||||
value if sess_o.name not in self.model_original_outputs else None
|
||||
for sess_o, value in zip(
|
||||
self.infer_session.get_outputs(), self.infer_session.run(None, inputs), strict=False
|
||||
)
|
||||
]
|
||||
)
|
||||
if (
|
||||
self.max_intermediate_outputs is not None
|
||||
and len(self.intermediate_outputs) == self.max_intermediate_outputs
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -6,15 +6,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import onnx
|
||||
|
||||
from ....tools.onnx_model_utils import fix_output_shapes, make_input_shape_fixed
|
||||
from ....tools.onnx_model_utils import fix_output_shapes, make_input_shape_fixed, optimize_model
|
||||
from ....tools.remove_initializer_from_input import remove_initializer_from_input
|
||||
from ...fusions import FusionGelu, FusionLayerNormalization
|
||||
from ...onnx_model import ONNXModel
|
||||
from ...quant_utils import save_and_reload_model_with_shape_infer
|
||||
from .fusion_lpnorm import FusionLpNormalization
|
||||
from .fusion_spacetodepth import FusionSpaceToDepth
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ def qnn_preprocess_model(
|
|||
"""
|
||||
modified = False
|
||||
model = model_input if isinstance(model_input, onnx.ModelProto) else onnx.load_model(model_input)
|
||||
model = save_and_reload_model_with_shape_infer(model)
|
||||
model = save_and_reload_optimize_model(model, shape_infer=True)
|
||||
onnx_model = ONNXModel(model)
|
||||
|
||||
# Optionally, fix the dynamic input shapes.
|
||||
|
|
@ -178,6 +178,24 @@ def qnn_preprocess_model(
|
|||
return modified
|
||||
|
||||
|
||||
def save_and_reload_optimize_model(model: onnx.ModelProto, shape_infer: bool) -> onnx.ModelProto:
|
||||
with tempfile.TemporaryDirectory(prefix="ort.qnn_preproc.") as qnn_preproc_tmp_dir:
|
||||
model_in_path = Path(qnn_preproc_tmp_dir).joinpath("qnn_proc_input.onnx")
|
||||
onnx.save_model(model, model_in_path, save_as_external_data=True)
|
||||
if shape_infer:
|
||||
model_infer_path = Path(qnn_preproc_tmp_dir).joinpath("qnn_proc_infer.onnx")
|
||||
onnx.shape_inference.infer_shapes_path(str(model_in_path), str(model_infer_path))
|
||||
model_in_path = model_infer_path
|
||||
model_out_path = Path(qnn_preproc_tmp_dir).joinpath("qnn_proc_output.onnx")
|
||||
optimize_model(model_in_path, model_out_path)
|
||||
ret_model = onnx.load_model(model_out_path)
|
||||
ret_metaprops = {"onnx.infer": "onnxruntime.tools.qnn.preprocess"}
|
||||
if ret_model.metadata_props:
|
||||
ret_metaprops.update(ret_model.metadata_props)
|
||||
onnx.helper.set_model_props(ret_model, ret_metaprops)
|
||||
return ret_model
|
||||
|
||||
|
||||
class InputOutputNameMap:
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -331,23 +331,6 @@ class QnnCompatibilityOverrides:
|
|||
|
||||
if not self.per_channel:
|
||||
self._make_static_inputs_use_default_weight_type(node)
|
||||
return
|
||||
|
||||
has_weight_no_overrides = node.input[1] in self.initializers and node.input[1] not in self.overrides
|
||||
has_bias_no_overrides = (
|
||||
len(node.input) > 2
|
||||
and node.input[2]
|
||||
and node.input[2] in self.initializers
|
||||
and node.input[2] not in self.overrides
|
||||
)
|
||||
|
||||
if has_weight_no_overrides or has_bias_no_overrides:
|
||||
# TODO: Make bias input not per-channel. QNN needs it to be per-tensor, but quantizer
|
||||
# tries to makes it per-channel if the weight is also per-channel.
|
||||
raise ValueError(
|
||||
"get_qnn_qdq_config() does not currently support the global per_channel option with LayerNormalization."
|
||||
" Please try using custom overrides that make bias per-tensor quantized."
|
||||
)
|
||||
|
||||
def _process_sigmoid(self, node: onnx.NodeProto):
|
||||
"""
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -33,6 +33,16 @@ class FusionLayerNormalization(Fusion):
|
|||
| |
|
||||
+-------------------------------------------------+
|
||||
|
||||
Or, using Mul instead of Pow:
|
||||
|
||||
+----------------------+
|
||||
| |
|
||||
| v
|
||||
[Root] --> ReduceMean --> Sub --> Mul --> ReduceMean --> Add --> Sqrt --> Div --> Mul --> Add
|
||||
(axis=2 or -1) | (in0=in1) (axis=2 or -1) (E-6 or E-12 or 0) ^
|
||||
| |
|
||||
+-------------------------------------------------+
|
||||
|
||||
It also handles cases of duplicated sub nodes exported from older version of PyTorch:
|
||||
|
||||
+----------------------+
|
||||
|
|
@ -40,7 +50,7 @@ class FusionLayerNormalization(Fusion):
|
|||
| +-------> Sub-----------------------------------------------+
|
||||
| | |
|
||||
| | v
|
||||
[Root] --> ReduceMean --> Sub --> Pow --> ReduceMean --> Add --> Sqrt --> Div --> Mul --> Add
|
||||
[Root] --> ReduceMean --> Sub --> (Pow or Mul) --> ReduceMean --> Add --> Sqrt --> Div --> Mul --> Add
|
||||
| ^
|
||||
| |
|
||||
+----------------------+
|
||||
|
|
@ -70,10 +80,9 @@ class FusionLayerNormalization(Fusion):
|
|||
div_node,
|
||||
[
|
||||
(["Sqrt", "Add", "ReduceMean", "Pow", "Sub"], [1, 0, 0, 0, 0]),
|
||||
(
|
||||
["Sqrt", "Add", "ReduceMean", "Pow", "Cast", "Sub"],
|
||||
[1, 0, 0, 0, 0, 0],
|
||||
),
|
||||
(["Sqrt", "Add", "ReduceMean", "Pow", "Cast", "Sub"], [1, 0, 0, 0, 0, 0]),
|
||||
(["Sqrt", "Add", "ReduceMean", "Mul", "Sub"], [1, 0, 0, 0, 0]),
|
||||
(["Sqrt", "Add", "ReduceMean", "Mul", "Cast", "Sub"], [1, 0, 0, 0, 0, 0]),
|
||||
],
|
||||
output_name_to_node,
|
||||
)
|
||||
|
|
@ -90,8 +99,10 @@ class FusionLayerNormalization(Fusion):
|
|||
# Skip fusion since epsilon value is not expected.
|
||||
return
|
||||
|
||||
pow_node = parent_nodes[3]
|
||||
if self.find_constant_input(pow_node, 2.0) != 1:
|
||||
pow_or_mul_node = parent_nodes[3]
|
||||
if pow_or_mul_node.op_type == "Pow" and self.find_constant_input(pow_or_mul_node, 2.0) != 1:
|
||||
return
|
||||
elif pow_or_mul_node.op_type == "Mul" and pow_or_mul_node.input[0] != pow_or_mul_node.input[1]:
|
||||
return
|
||||
|
||||
mul_node = input_name_to_nodes[div_node.output[0]][0]
|
||||
|
|
|
|||
|
|
@ -11,12 +11,19 @@ import copy
|
|||
import logging
|
||||
import os
|
||||
|
||||
import ml_dtypes
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import onnx
|
||||
import onnx_ir as ir
|
||||
from onnx.onnx_pb import GraphProto, ModelProto, NodeProto, TensorProto
|
||||
|
||||
from onnxruntime.capi._pybind_state import quantize_matmul_4bits, quantize_matmul_8bits, quantize_qdq_matmul_4bits
|
||||
from onnxruntime.capi._pybind_state import (
|
||||
quantize_matmul_2bits,
|
||||
quantize_matmul_4bits,
|
||||
quantize_matmul_8bits,
|
||||
quantize_qdq_matmul_4bits,
|
||||
)
|
||||
|
||||
from .calibrate import CalibrationDataReader
|
||||
from .neural_compressor import gptq_quantize, rtn_quantize
|
||||
|
|
@ -816,9 +823,13 @@ class DefaultWeightOnlyQuantizer:
|
|||
|
||||
# block wise quantization, each block comes from a single column
|
||||
packed = np.zeros((cols, k_blocks, blob_size), dtype="uint8")
|
||||
zero_point = np.zeros(cols * ((k_blocks + kpack - 1) // kpack), dtype="uint8")
|
||||
scales = np.zeros((cols * k_blocks), dtype=fp32weight.dtype)
|
||||
if qbits == 8:
|
||||
zero_point = np.zeros((cols, ((k_blocks + kpack - 1) // kpack)), dtype="uint8")
|
||||
scales = np.zeros((cols, k_blocks), dtype=fp32weight.dtype)
|
||||
if qbits == 2:
|
||||
quantize_matmul_2bits(
|
||||
packed, fp32weight, scales, zero_point, block_size, cols, rows, self.config.is_symmetric
|
||||
)
|
||||
elif qbits == 8:
|
||||
quantize_matmul_8bits(
|
||||
packed, fp32weight, scales, zero_point, block_size, cols, rows, self.config.is_symmetric
|
||||
)
|
||||
|
|
@ -857,21 +868,27 @@ class DefaultWeightOnlyQuantizer:
|
|||
logger.info("MatMul doesn't have const weight. Skip to quantize")
|
||||
return [node] # only care about constant weight
|
||||
|
||||
b_ndarray = onnx.numpy_helper.to_array(b_tensor)
|
||||
b_ndarray = ir.from_proto(b_tensor).numpy()
|
||||
if len(b_ndarray.shape) != 2:
|
||||
logger.info("MatMul weight is not 2D. Skip to quantize")
|
||||
return [node] # can only process 2-D matrix
|
||||
|
||||
bfloat16 = b_ndarray.dtype == "bfloat16"
|
||||
if bfloat16:
|
||||
b_ndarray = b_ndarray.astype(np.float32)
|
||||
|
||||
packed, scales, zero_points = self.qbits_block_quant(b_ndarray)
|
||||
if bfloat16:
|
||||
scales = scales.astype(ml_dtypes.bfloat16)
|
||||
|
||||
if self.config.quant_format == QuantFormat.QOperator:
|
||||
b_quant = onnx.numpy_helper.from_array(packed, b_tensor.name + f"_Q{bits}")
|
||||
scales_tensor = onnx.numpy_helper.from_array(scales, b_tensor.name + "_scales")
|
||||
b_quant = ir.serde.serialize_tensor(ir.Tensor(packed, name=b_tensor.name + f"_Q{bits}"))
|
||||
scales_tensor = ir.serde.serialize_tensor(ir.Tensor(scales, name=b_tensor.name + "_scales"))
|
||||
else:
|
||||
b_quant = onnx.helper.make_tensor(
|
||||
b_tensor.name + f"_DQ_Q{bits}", qtype, b_ndarray.shape, packed.tobytes(), True
|
||||
)
|
||||
scales_tensor = onnx.numpy_helper.from_array(scales, b_tensor.name + "_DQ_scales")
|
||||
scales_tensor = ir.serde.serialize_tensor(ir.Tensor(scales, name=b_tensor.name + "_DQ_scales"))
|
||||
|
||||
# if QDQ, CW and SYM enabled, optimize for Intel NPU, tranpose the weight to NHWC format will increase performance
|
||||
qdq_opt_for_intel_npu_enabled = (
|
||||
|
|
@ -886,7 +903,7 @@ class DefaultWeightOnlyQuantizer:
|
|||
b_quant = onnx.helper.make_tensor(
|
||||
b_tensor.name + f"_DQ_Q{bits}", qtype, [cols, rows], packed.tobytes(), True
|
||||
)
|
||||
scales_tensor = onnx.numpy_helper.from_array(scales, b_tensor.name + "_DQ_scales")
|
||||
scales_tensor = ir.serde.serialize_tensor(ir.Tensor(scales, name=b_tensor.name + "_DQ_scales"))
|
||||
|
||||
for input in b_graph.input:
|
||||
if input.name == input_b:
|
||||
|
|
@ -1206,7 +1223,7 @@ class MatMulNBitsQuantizer:
|
|||
MatMul MatMulNBits DeQuantizeLinear -> MatMul
|
||||
Gather GatherBlockQuantized Gather, Gather, Gather (optional) -> DequantizeLinear
|
||||
|
||||
Perform 4/8 bits quantization of constant weights for target nodes.
|
||||
Perform 2/4/8 bits quantization of constant weights for target nodes.
|
||||
If algo_config.quant_format is QOperator:
|
||||
- nodes are replaced by the corresponding QOperator nodes.
|
||||
- quantized weights are stored in the contrib ops.
|
||||
|
|
@ -1224,6 +1241,7 @@ class MatMulNBitsQuantizer:
|
|||
def __init__(
|
||||
self,
|
||||
model: ModelProto | str,
|
||||
bits: int = 4, # default to 4bit
|
||||
block_size: int = 128,
|
||||
is_symmetric: bool = False,
|
||||
accuracy_level: int | None = None,
|
||||
|
|
@ -1239,6 +1257,7 @@ class MatMulNBitsQuantizer:
|
|||
nodes_to_exclude = []
|
||||
self.model = ONNXModel(onnx.load(model)) if isinstance(model, str) else ONNXModel(model)
|
||||
self.model_path = model if isinstance(model, str) else None
|
||||
self.bits = bits
|
||||
self.block_size = block_size
|
||||
self.is_symmetric = is_symmetric
|
||||
self.accuracy_level = accuracy_level
|
||||
|
|
@ -1254,13 +1273,13 @@ class MatMulNBitsQuantizer:
|
|||
quant_format=quant_format,
|
||||
op_types_to_quantize=op_types_to_quantize,
|
||||
quant_axes=quant_axes,
|
||||
bits=4, # default to 4 bits
|
||||
bits=bits,
|
||||
channel_wised_quantize=channel_wised_quantize,
|
||||
)
|
||||
|
||||
self.algo_config = algo_config
|
||||
if hasattr(self.algo_config, "bits"):
|
||||
assert self.algo_config.bits in [4, 8], "Only support 4 or 8 bits quantization"
|
||||
assert self.algo_config.bits in [2, 4, 8], "Only support 2, 4 or 8 bits quantization"
|
||||
|
||||
if algo_config.algorithm == "HQQ":
|
||||
self.node_quantizer = HQQWeightOnlyQuantizer(self.algo_config)
|
||||
|
|
@ -1609,6 +1628,7 @@ if __name__ == "__main__":
|
|||
|
||||
quant = MatMulNBitsQuantizer(
|
||||
model=model,
|
||||
bits=args.bits,
|
||||
accuracy_level=args.accuracy_level,
|
||||
nodes_to_exclude=args.nodes_to_exclude,
|
||||
nodes_to_include=args.nodes_to_include,
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -21,12 +21,12 @@
|
|||
import copy
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from collections import deque
|
||||
from pathlib import Path
|
||||
|
||||
import onnx
|
||||
import onnx.external_data_helper
|
||||
import onnx_ir as ir
|
||||
|
||||
from .util import MAXIMUM_PROTOBUF, find_by_name
|
||||
|
||||
|
|
@ -73,26 +73,11 @@ class ONNXModel:
|
|||
|
||||
def check_is_large_model(self):
|
||||
"""Check model > 2GB."""
|
||||
init_size = 0
|
||||
for init in self._model.graph.initializer:
|
||||
# if initializer has external data location, return True
|
||||
if init.HasField("data_location") and init.data_location == onnx.TensorProto.EXTERNAL:
|
||||
self._is_large_model = True
|
||||
return
|
||||
# if raise error of initializer size > 2GB, return True
|
||||
try:
|
||||
init_bytes = init.SerializeToString()
|
||||
init_size += sys.getsizeof(init_bytes)
|
||||
except Exception as e:
|
||||
if "exceeds maximum protobuf size of 2GB" in str(e):
|
||||
self._is_large_model = True
|
||||
return
|
||||
else: # pragma: no cover
|
||||
raise e
|
||||
if init_size > MAXIMUM_PROTOBUF:
|
||||
self._is_large_model = True
|
||||
return
|
||||
self._is_large_model = False
|
||||
ir_graph = ir.from_proto(self._model.graph)
|
||||
initializer_size = sum(
|
||||
v.const_value.nbytes for v in ir_graph.initializers.values() if v.const_value is not None
|
||||
)
|
||||
self._is_large_model = initializer_size > MAXIMUM_PROTOBUF
|
||||
|
||||
@property
|
||||
def is_large_model(self):
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -12,7 +12,6 @@ from typing import Any
|
|||
|
||||
import numpy as np
|
||||
import onnx
|
||||
import onnx.numpy_helper
|
||||
from onnx import TensorProto
|
||||
from onnx import onnx_pb as onnx_proto
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from pathlib import Path
|
|||
|
||||
import numpy
|
||||
import onnx
|
||||
from ml_dtypes import float8_e4m3fn, int4, uint4
|
||||
from onnx import ModelProto, TensorProto, external_data_helper
|
||||
from onnx import onnx_pb as onnx_proto
|
||||
from onnx.helper import make_graph, make_model, make_node, make_tensor_value_info
|
||||
|
|
@ -21,19 +22,6 @@ from onnx.reference import ReferenceEvaluator
|
|||
|
||||
from onnxruntime import GraphOptimizationLevel, InferenceSession, SessionOptions
|
||||
|
||||
try:
|
||||
from onnx.reference.custom_element_types import float8e4m3fn
|
||||
except ImportError:
|
||||
float8e4m3fn = None
|
||||
|
||||
# INT4 np.dtypes added in ONNX 1.16. These map to np.int8/np.uint8 because numpy
|
||||
# does not support sub-byte types.
|
||||
try:
|
||||
from onnx.reference.custom_element_types import int4, uint4
|
||||
except ImportError:
|
||||
int4 = None
|
||||
uint4 = None
|
||||
|
||||
try:
|
||||
from onnx.reference.op_run import to_array_extended
|
||||
except ImportError:
|
||||
|
|
@ -149,9 +137,9 @@ ONNX_TYPE_TO_NP_TYPE = {
|
|||
onnx_proto.TensorProto.UINT8: numpy.dtype("uint8"),
|
||||
onnx_proto.TensorProto.INT16: numpy.dtype("int16"),
|
||||
onnx_proto.TensorProto.UINT16: numpy.dtype("uint16"),
|
||||
onnx_proto.TensorProto.FLOAT8E4M3FN: float8e4m3fn,
|
||||
onnx_proto.TensorProto.INT4: int4, # base_dtype is np.int8
|
||||
onnx_proto.TensorProto.UINT4: uint4, # base_dtype is np.uint8
|
||||
onnx_proto.TensorProto.FLOAT8E4M3FN: float8_e4m3fn,
|
||||
onnx_proto.TensorProto.INT4: int4,
|
||||
onnx_proto.TensorProto.UINT4: uint4,
|
||||
}
|
||||
|
||||
ONNX_INT_TYPE_RANGE = {
|
||||
|
|
@ -164,9 +152,7 @@ ONNX_INT_TYPE_RANGE = {
|
|||
}
|
||||
|
||||
ONNX_INT_TYPE_SYMMETRIC_RANGE = {
|
||||
onnx_proto.TensorProto.UINT8: (numpy.array(0, dtype=numpy.uint8), numpy.array(254, dtype=numpy.uint8)),
|
||||
onnx_proto.TensorProto.INT8: (numpy.array(-127, dtype=numpy.int8), numpy.array(127, dtype=numpy.int8)),
|
||||
onnx_proto.TensorProto.UINT16: (numpy.array(0, dtype=numpy.uint16), numpy.array(65534, dtype=numpy.uint16)),
|
||||
onnx_proto.TensorProto.INT16: (numpy.array(-32767, dtype=numpy.int16), numpy.array(32767, dtype=numpy.int16)),
|
||||
}
|
||||
|
||||
|
|
@ -175,7 +161,7 @@ ONNX_INT_TYPE_REDUCED_RANGE = {
|
|||
onnx_proto.TensorProto.INT8: (numpy.array(-64, dtype=numpy.int8), numpy.array(64, dtype=numpy.int8)),
|
||||
onnx_proto.TensorProto.UINT16: (numpy.array(0, dtype=numpy.uint16), numpy.array(32767, dtype=numpy.uint16)),
|
||||
onnx_proto.TensorProto.INT16: (numpy.array(-16384, dtype=numpy.int16), numpy.array(16384, dtype=numpy.int16)),
|
||||
onnx_proto.TensorProto.UINT4: (numpy.array(0, dtype=int4), numpy.array(7, dtype=int4)),
|
||||
onnx_proto.TensorProto.UINT4: (numpy.array(0, dtype=uint4), numpy.array(7, dtype=uint4)),
|
||||
onnx_proto.TensorProto.INT4: (numpy.array(-4, dtype=int4), numpy.array(3, dtype=int4)),
|
||||
}
|
||||
|
||||
|
|
@ -324,11 +310,10 @@ def compute_scale_zp_float8(element_type, std):
|
|||
zp_dtype = None
|
||||
if element_type not in FLOAT8_DISTRIBUTIONS:
|
||||
if element_type == TensorProto.FLOAT8E4M3FN:
|
||||
from onnx.numpy_helper import float8e4m3_to_float32 # noqa: PLC0415
|
||||
from onnx.reference.custom_element_types import float8e4m3fn # noqa: PLC0415
|
||||
from ml_dtypes import float8_e4m3fn # noqa: PLC0415
|
||||
|
||||
zp_dtype = float8e4m3fn
|
||||
all_values = [float8e4m3_to_float32(i) for i in range(256)]
|
||||
zp_dtype = float8_e4m3fn
|
||||
all_values = [float(i) for i in range(256)]
|
||||
values = numpy.array(
|
||||
[f for f in all_values if not numpy.isnan(f) and not numpy.isinf(f)], dtype=numpy.float32
|
||||
)
|
||||
|
|
@ -336,9 +321,9 @@ def compute_scale_zp_float8(element_type, std):
|
|||
raise ValueError(f"Quantization to element_type={element_type} not implemented.")
|
||||
FLOAT8_DISTRIBUTIONS[element_type] = values
|
||||
elif element_type == TensorProto.FLOAT8E4M3FN:
|
||||
from onnx.reference.custom_element_types import float8e4m3fn # noqa: PLC0415
|
||||
from ml_dtypes import float8_e4m3fn # noqa: PLC0415
|
||||
|
||||
zp_dtype = float8e4m3fn
|
||||
zp_dtype = float8_e4m3fn
|
||||
|
||||
if zp_dtype is None:
|
||||
raise TypeError(f"Unexpected element_type {element_type}.")
|
||||
|
|
@ -449,7 +434,7 @@ def quantize_data(
|
|||
)
|
||||
if qType == TensorProto.FLOAT8E4M3FN:
|
||||
quantized_data = quantize_nparray(qType, data, scale, zero_point)
|
||||
if any((quantized_data.astype(numpy.uint8).ravel() & 127) == 127):
|
||||
if any((quantized_data.view(numpy.uint8).ravel() & 127) == 127):
|
||||
np_data = numpy.asarray(data)
|
||||
raise RuntimeError(
|
||||
f"One of the quantized value is NaN data in [{np_data.min()}, {np_data.max()}], "
|
||||
|
|
@ -533,7 +518,7 @@ def quantize_onnx_initializer(
|
|||
f"\nraw={str(q_weight_initializer)[:200]}."
|
||||
)
|
||||
elif quant_type in (onnx.TensorProto.INT4, onnx.TensorProto.UINT4):
|
||||
if q_weight_data.dtype not in (numpy.int8, numpy.uint8):
|
||||
if q_weight_data.dtype not in (int4, uint4):
|
||||
raise RuntimeError(f"Quantized weights for {q_weight_name} must be 8-bit before packing as 4-bit values.")
|
||||
|
||||
# We do not use onnx.helper.pack_float32_to_4bit() due to performance.
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ QDQRegistry = {
|
|||
"LayerNormalization": QDQNormalization,
|
||||
"BatchNormalization": QDQNormalization,
|
||||
"TopK": QDQDirect8BitOp,
|
||||
"CumSum": QDQOperatorBase,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -74,34 +74,29 @@ def quant_pre_process(
|
|||
|
||||
with tempfile.TemporaryDirectory(prefix="pre.quant.") as quant_tmp_dir:
|
||||
temp_path = Path(quant_tmp_dir)
|
||||
model = None
|
||||
model = input_model if isinstance(input_model, onnx.ModelProto) else onnx.load(input_model)
|
||||
|
||||
# Since Upsample is deprecated after opset v10, and the model's opset will
|
||||
# be upgraded to at least v11 during quantization, we need to replace Upsample
|
||||
# with Resize first to avoid generating an invalid model.
|
||||
ai_onnx_domain = [opset for opset in model.opset_import if not opset.domain or opset.domain == "ai.onnx"]
|
||||
if len(ai_onnx_domain) == 1:
|
||||
opset_version = ai_onnx_domain[0].version
|
||||
if opset_version <= 10:
|
||||
ReplaceUpsampleWithResize(ONNXModel(model), opset_version).apply()
|
||||
model = onnx.version_converter.convert_version(model, 11)
|
||||
model = save_and_reload_model_with_shape_infer(model)
|
||||
|
||||
if not skip_symbolic_shape:
|
||||
logger.info("Performing symbolic shape inference...")
|
||||
loaded_model = input_model if isinstance(input_model, onnx.ModelProto) else onnx.load(input_model)
|
||||
model = SymbolicShapeInference.infer_shapes(
|
||||
loaded_model,
|
||||
model,
|
||||
int_max,
|
||||
auto_merge,
|
||||
guess_output_rank,
|
||||
verbose,
|
||||
)
|
||||
|
||||
# Since Upsample is deprecated after opset v10, and the model's opset will
|
||||
# be upgraded to at least v11 during quantization, we need to replace Upsample
|
||||
# with Resize first to avoid generating an invalid model.
|
||||
if model:
|
||||
ai_onnx_domain = [opset for opset in model.opset_import if not opset.domain or opset.domain == "ai.onnx"]
|
||||
if len(ai_onnx_domain) == 1:
|
||||
opset_version = ai_onnx_domain[0].version
|
||||
if opset_version < 10:
|
||||
ReplaceUpsampleWithResize(ONNXModel(model), opset_version).apply()
|
||||
model.opset_import.remove(ai_onnx_domain[0])
|
||||
opset_version = 11
|
||||
model.opset_import.extend([onnx.helper.make_opsetid("", opset_version)])
|
||||
model = onnx.version_converter.convert_version(model, opset_version)
|
||||
model = save_and_reload_model_with_shape_infer(model)
|
||||
|
||||
if not skip_optimization:
|
||||
# Use ORT optimizers (native code) to optimize model
|
||||
if not skip_symbolic_shape:
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class OnnxModelCalibrationDataReader(CalibrationDataReader):
|
|||
name2tensors = []
|
||||
for data_dir in data_dirs:
|
||||
name2tensor = {}
|
||||
data_paths = [os.path.join(data_dir, a) for a in sorted(os.listdir(data_dir))]
|
||||
data_paths = [os.path.join(data_dir, f"input_{input_idx}.pb") for input_idx in range(len(model_inputs))]
|
||||
data_ndarrays = [self.read_onnx_pb_data(data_path) for data_path in data_paths]
|
||||
for model_input, data_ndarray in zip(model_inputs, data_ndarrays, strict=False):
|
||||
name2tensor[model_input.name] = data_ndarray
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue