precision_cost
→dict[str, float]precision_cost(model: Module, example: object, weights: WeightPrecision | Palettize | Sparsify = WeightPrecision.FLOAT, output_field: str | None = None)What each precision costs this model, measured rather than assumed.
The Neural Engine runs float16 and nothing else, so reaching the accelerator means accepting whatever float16 does to a particular network — and that varies by more than an order of magnitude between architectures that look alike. Measured across fourteen families on an untrained forward: convolutional stacks land near 1e-3, ViT at 1e-2, and MaxViT at 1.6e-1. Nothing in an export says which kind a given model is.
So this exports it twice and compares both against the eager model, relative to each output's own magnitude. The packages are written to a temporary directory and removed: the answer is the point, not the artifacts.
The float16 figure is Core ML's, which is better than running the same model in half precision throughout — a ViT does that at 1.5 relative, against 1e-2 here — because the runtime keeps the parts that need range in float32. It is not a bound on your own half precision arithmetic elsewhere.
Parameters
modelnn.Moduleeval() mode.export takes it.output_field(str or None, optional, keyword - only)= Noneexport takes
it.Returns
dict[str, float]{"float32": err, "float16": err} — the worst relative
disagreement with the eager model, per precision.
Raises
ValueErrorExamples
>>> import lucid, lucid.coreml, lucid.nn as nn
>>> model = nn.Sequential(nn.Conv2d(3, 16, 3, padding=1), nn.ReLU()).eval()
>>> cost = lucid.coreml.precision_cost(model, lucid.randn(1, 3, 32, 32))
>>> sorted(cost)
['float16', 'float32']
>>> cost["float16"] < 1e-2
True