Source code for lenslapse.onnx_f16
"""Halve ONNX file size by storing float32 initializers as float16 + a Cast node.
All computation stays in fp32 (ONNX Runtime constant-folds Cast(initializer) at session load),
so outputs differ from fp32 only by fp16 rounding of the weights. This avoids
onnxconverter_common.float16, which fails on dynamo-exported graphs containing Cast nodes.
"""
import numpy as np
import onnx
from onnx import TensorProto, helper, numpy_helper
MIN_ELEMENTS = 1024 # leave tiny tensors (layernorm params, biases) in fp32
[docs]
def convert_initializers_to_f16(model: onnx.ModelProto) -> onnx.ModelProto:
graph = model.graph
new_inits = []
casts = []
for init in graph.initializer:
arr = numpy_helper.to_array(init)
if init.data_type == TensorProto.FLOAT and arr.size >= MIN_ELEMENTS:
f16_name = f"{init.name}__f16"
new_inits.append(numpy_helper.from_array(arr.astype(np.float16), f16_name))
casts.append(helper.make_node("Cast", [f16_name], [init.name], to=TensorProto.FLOAT))
else:
new_inits.append(init)
del graph.initializer[:]
graph.initializer.extend(new_inits)
# Cast nodes must precede any consumer; prepend them.
old_nodes = list(graph.node)
del graph.node[:]
graph.node.extend(casts + old_nodes)
return model
[docs]
def save_f16(src_path: str, dst_path: str) -> None:
model = onnx.load(src_path)
onnx.save(convert_initializers_to_f16(model), dst_path)