Source code for onnx_diagnostic.helpers.optim_helper
from typing import Optional, Union
import pprint
import onnx
[docs]
def optimize_model(
algorithm: str,
model: Union[onnx.ModelProto, str],
output: Optional[str] = None,
processor: Optional[str] = None,
infer_shapes: bool = True,
remove_shape_info: bool = False,
verbose: int = 1,
):
"""
Optimizes an onnx model by fusing nodes. It looks for patterns in the graphs
and replaces them by the corresponding nodes. It also does basic optimization
such as removing identity nodes or unused nodes.
:param algorithm: algorithm to choose
:param model: model to optimize as a proto or a filename
:param output: if not empty, the optimized model is saved
:param processor: optimization are done for the processor
:param infer_shapes: infer shapes before optimizing, this might not be
available for all algorithm
:param remove_shape_info: remove shape information before saving the model
:param verbose: verbosity level
:return: optimized model
The goal is to make the model faster.
Argument patterns defines the patterns to apply or the set of patterns.
It is possible to show statistics or to remove a particular pattern.
Here are some environment variables which can be used to trigger
these displays.
Available options algorithms, default and default+runtime:
- ``DROPPATTERN=<pattern1,patterns2,...>``: do not apply
those patterns when optimizing a model
- ``DUMPPATTERNS=<folder>``: dumps all matched and applied nodes when a pattern is applied
- ``PATTERN=<pattern1,pattern2,...>``: increase verbosity
for specific patterns to understand why one pattern was not applied,
this shows which line is rejecting a pattern if it seems one pattern was missed
"""
if isinstance(model, str):
if verbose:
print(f"[optimize_model] load {model!r}")
proto = onnx.load(model)
if verbose:
print("[optimize_model] done loading.")
else:
proto = model
if verbose:
print(f"[optimize_model] optimize with {algorithm!r}")
if algorithm in {"default", "default+onnxruntime"}:
from experimental_experiment.xoptim import get_pattern_list
from experimental_experiment.xbuilder import GraphBuilder, OptimizationOptions
pats = get_pattern_list(algorithm)
gr = GraphBuilder(
proto,
infer_shapes_options=infer_shapes,
optimization_options=OptimizationOptions(
patterns=pats,
verbose=verbose,
remove_unused=True,
constant_folding=True,
remove_identity=True,
max_iter=max(100, len(proto.graph.node) // 2),
processor=processor or "CPU",
),
)
if verbose:
print(f"[optimize_model] starts optimizing with {len(pats)} patterns")
print(f"[optimize_model] model has {len(proto.graph.node)} nodes")
opt_onx, report = gr.to_onnx(optimize=True, return_optimize_report=True)
if verbose:
print("[optimize_model] optimization report")
pprint.pprint(report)
print("[optimize_model] done")
elif algorithm == "slim":
import onnxslim
opt_onx = onnxslim.slim(proto, no_shape_infer=not infer_shapes)
elif algorithm in {"ir", "os_ort"}:
import onnx_ir
import onnxscript.optimizer
from onnxscript.rewriter.ort_fusions import optimize_for_ort
model_ir = onnx_ir.from_proto(proto)
if algorithm == "ir":
onnxscript.optimizer.optimize(model_ir)
else:
optimize_for_ort(model_ir)
opt_onx = onnx_ir.serde.serialize_model(model_ir)
del proto
if verbose:
print(f"[optimize_model] done optimizing, model has {len(opt_onx.graph.node)} nodes")
if remove_shape_info:
if verbose:
print(f"[optimize_model] remove shape information {len(opt_onx.graph.value_info)}")
del opt_onx.graph.value_info[:]
if verbose:
print("[optimize_model] done removing shape info")
if output:
if verbose:
print(f"[optimize_model] save file into {output!r}")
onnx.save(opt_onx, output, save_as_external_data=True)
if verbose:
print("[optimize_model] done saving")
return opt_onx