Model conversion is not a packaging step at the end of an ML project. It is a compatibility boundary between the model you evaluated in Python and the model that will make decisions in an app. If shapes are accidentally fixed during export, or the converted outputs drift beyond an acceptable tolerance, a successful build can still ship the wrong behavior.
Core AI’s PyTorch extensions provide a route from an exported PyTorch program to a portable .aimodel asset. The reliable workflow is small: define the production input contract, export with its real shape constraints, convert it, then compare the converted result against the original model before integration work begins.
Export the input contract, not one example
An example input is required for export, but it should not silently define the only input your app can use. Sequence models are the common failure case. A test batch with five tokens may work in Python while the actual product needs a conversation, a stream of sensor readings, or a variable-length game history.
Use torch.export.Dim to declare the dynamic dimensions that are genuinely supported, and put a meaningful upper bound on them. The upper bound is part of the product budget: it affects the model's valid range, its memory needs, and the latency you must test.
import torch
import coreai_torch
example = torch.randn(1, 8, 16)
sequence_length = torch.export.Dim("sequence_length", min=1, max=256)
exported = torch.export.export(
model,
args=(example,),
dynamic_shapes={"features": {1: sequence_length}},
)
exported = exported.run_decompositions(coreai_torch.get_decomp_table())Do not mark a dimension dynamic only because it is convenient. If a batch size, image size, or context length is fixed by the app, model it as fixed. A narrower contract is easier to validate and usually easier to optimize.
Give the converted graph stable names
The converter needs names for the values that become your Swift integration boundary. Name inputs and outputs after their role in the feature, not after a temporary Python variable. Those names appear in the function signature you inspect in Xcode and in the dictionaries passed to InferenceFunction.run.
program = coreai_torch.TorchConverter().add_exported_program(
exported,
input_names=["features"],
output_names=["logits"],
).to_coreai()
program.save_asset("ActionModel.aimodel")When the model has multiple entry points, export them deliberately. A separate embedding function and scoring function can make an app pipeline clearer, but only if their input and output shapes remain easy to reason about. Avoid exposing internal implementation details as a public model interface.
Compare outputs before measuring speed
Conversion can change operation decomposition, precision, or layout. Performance work is meaningless until the converted graph still produces acceptable values. Build a small set of fixed evaluation cases that includes normal inputs, the edges of each dynamic range, and data that resembles the feature's real workload.
For each case, run both the original PyTorch model and the converted Core AI function, then measure an error that suits the model. A classification model may compare top-label agreement and logit tolerance. An embedding model may compare cosine similarity. A numerical model may use an absolute or relative error threshold.
The key is to choose the threshold before looking at the result. A number that looks small is not automatically harmless. A tiny change near a decision boundary can change the label an app presents to a person.
Use the debugger when aggregate metrics are not enough
If a whole-output comparison fails, do not guess which conversion step caused it. Core AI Debugger can inspect the converted operation graph, execute the model, and compare intermediate tensor values with a reference run. The first divergent tensor is a much better starting point than a final output that is merely "different."
Embed the debug metadata required by the tool while authoring the asset. It preserves the route from a converted operation back to the Python source or PyTorch module that introduced it. This is especially useful for a model with many repeated transformer blocks where a final mismatch reveals little about its origin.
Treat conversion tests as release checks
Keep a small conversion-validation suite near the model export code. Run it whenever the model, PyTorch version, conversion package, or input preprocessing changes. The Swift project should receive a model only after that check passes.
This also makes iteration safer. You can quantize, change an attention implementation, or expand a dynamic range, then compare quality and performance with a known baseline instead of discovering a regression through the app UI.
Key takeaway
The model asset is an interface with a contract. Export the ranges the app actually needs, give the interface stable names, and test its numerical behavior before treating it as ready for Swift integration.
