mixle.task.edge module¶
Edge distillation under hard device budgets.
mixle.task.tune_recipe() tunes one family’s knobs with a soft cost penalty. An edge deployment
is a different problem: the budget is hard (bytes of flash, ops per inference, “no torch on the
device”), and the largest gains often come from choosing the right kind of student – a hashed-feature MLP
versus a structured probabilistic classifier (a learned Bayesian network: kilobytes, torch-free,
exactly calibrated posteriors) – not from tweaking widths inside one kind.
This module makes both choices searchable, and makes the search itself a model:
DeviceSpec/EdgeFootprint– the deployment budget and a student’s measured cost (serialized bytes, per-inference op count, torch dependence).EdgeSpace– one design space spanning family (structure) and each family’s training recipe (process), decoded from a unit cube so any optimizer can drive it.DesignModel– the model that writes the model: GP surrogates over design -> (quality, budget violations), proposing the next design by feasibility-weighted expected improvement (mixle.doe.propose_next_constrained()). It persists (to_json/from_json) and keys every row on atask_fingerprint(), so design knowledge accumulates across tasks: the surrogate learns task-similarity through the fingerprint coords, and a warm-started search – even on a different task – skips the designs that never work anywhere near it.distill_for_edge()– the front door: screen candidates at reduced fidelity, promote the promising ones to full training, return the best student that fits the device, with an adaptive selection Pareto front, an independent qualification score, and the updatedDesignModel.distill_designer()– distill the accumulated design ledger into a torch-free structured student that predicts whether a design is worth training.
Teachers are consulted once per dataset (labels are cached), never per candidate.
- class DeviceSpec(max_bytes=None, max_ops=None, max_inference_seconds=None, torch_free=False)[source]
Bases:
objectA hard deployment budget.
Noneleaves an axis unconstrained.max_bytesgates the exact exported artifact.max_inference_secondsgates a benchmark of the actual serving path.max_opsis retained as a structural compatibility constraint, but cannot be used without an actual latency bound.torch_freeexcludes torch-backed artifacts.- Parameters:
- classmethod for_latency(max_ms, ops_per_second, *, max_bytes=None, torch_free=False)[source]
A budget from a latency target:
max_ops = ops_per_second * max_ms / 1000.ops_per_secondmust come from a probe run on the target device for the student kind you deploy (measure_ops_per_second()measures it for a representative student) – throughput differs by orders of magnitude across devices and student kinds, so there is no portable built-in constant.
- violations(fp)[source]
Normalized constraint values, feasible when
<= 0(the form constrained BO consumes).
- class EdgeFootprint(bytes, ops, torch_free, inference_seconds=None, artifact_digest=None, ops_kind='structural_estimate', payload_bytes=None)[source]
Bases:
objectExact exported bytes plus execution-path evidence.
opsis an explicitly labeled structural estimate.inference_secondsis measured by running the serving object’s realbatchpath.bytesis what you flash to the device: weights and the manifest, adapter spec, labels and metadata.payload_bytesis the parameter payload alone (weights, biases, per-layer scales), which is the quantity a compression ratio is about – at small model sizes the constant-size manifest dominatesbytes, so a 4x/8x weight claim is only checkable againstpayload_bytes. Gate deployment onbytes; quote compression frompayload_bytes.
- class EdgeSpace(families=('mlp', 'structured'), dim_choices=(64, 128, 256, 512), hidden_range=(4, 96), epochs_range=(40, 320), log10_lr_range=(-3.0, -1.0), bits_choices=(32, 8), ngram=3, components_range=(1, 4), bins_range=(2, 8), max_its_range=(10, 60), min_gain_range=(0.0, 5.0))[source]
Bases:
objectOne unit-cube design space over family (structure) and each family’s recipe (process).
Coordinate 0 selects the family; 1..4 decode family-specifically.
familiesdefaults to what the input kind and device allow: hashed-MLP students need torch; structured students need fixed-schema records. Decode is deterministic, so a design point is a reproducible recipe.- Parameters:
ngram (int)
- bounds()[source]
Return normalized design-space bounds for DOE search.
- signature()[source]
Fingerprint of the space so persisted design knowledge is only reused where it applies.
- Return type:
- class DesignModel(signature, n_constraints, n_fingerprint=0)[source]
Bases:
objectA probabilistic model of the design space itself: design point -> (quality, budget violations).
Every evaluated design is a row; GP surrogates fitted on the rows drive
propose()(the next design worth training, by feasibility-weighted expected improvement) andpredict()(mean, sd, and probability-of-fitting-the-device for untrained designs). It serializes, so what was learned designing students for one task warm-starts the next – the design knowledge is itself a model artifact. (Anddistill_designer()compresses it into a student – models all the way down, each level a real artifact.)- add(point, quality, violations, *, fingerprint=None, **tag)[source]
Append one evaluated design point and its feasibility metadata.
- propose(bounds, *, seed=None, n_candidates=256, prefilter=None, max_tries=8, fingerprint=None)[source]
The next design worth training: feasibility-weighted EI over everything seen so far.
prefiltercloses the designer loop: pass a design judge – typically the compact student fromdistill_designer(), called asprefilter(point_tuple) -> label– and any proposal it labels"weak"is vetoed and re-drawn (fresh acquisition seed), up tomax_tries. The distilled design knowledge thus skips known weak designs before a single training run is spent; if every retry is vetoed the last proposal is returned anyway (the judge advises, the surrogate decides).fingerprintconditions the proposal on the current task (see_fingerprint_bounds()); the returned point has design coords only.
- predict(points, *, fingerprint=None)[source]
For untrained designs: predicted quality (mean, sd) and P(fits the device).
pointscarry design coords only;fingerprint(required when the ledger is fingerprinted) selects which task’s slice the prediction conditions on.
- to_json()[source]
Serialize the design ledger for reuse across search runs.
- class EdgeDistillResult(model, family, recipe, agreement, selection_agreement, test_size, footprint, feasible, pareto, design, trials=<factory>)[source]
Bases:
objectOutcome of edge search and independent deployment qualification.
selection_agreementandparetodescribe adaptive evidence from the search panel.agreementis measured once for the frozen winner on a disjoint test panel and is the only reported deployment-quality estimate.
- distill_for_edge(teacher, train_data, val_data, device, *, test_data=None, test_frac=0.25, labels=None, train_labels=None, val_labels=None, test_labels=None, space=None, design=None, designer=None, n_init=4, n_iter=6, screen_fidelity=0.3, promote=2, seed=0, task='')[source]
Search structure x process, then qualify the frozen winner independently.
Candidate screening, promotion, winner selection, and the returned Pareto front use only
val_data(the search panel). The selected artifact is then scored exactly once on untouchedtest_data. Iftest_datais omitted,val_datais deterministically split into disjoint search and test panels. Accordingly,result.selection_agreementand every Pareto point are adaptive search evidence;result.agreementis independent deployment evidence.The teacher labels each panel once, or callers may provide every matching label panel and pass
teacher=None. Validation or test labels never expand the class vocabulary: whenlabelsis omitted it is derived from training labels alone. Pass a previous compatibledesignto warm-start or a distilleddesignerto prefilter known-weak proposals. If nothing fits the device, the least-infeasible selected student is returned withfeasible=False.
- distill_designer(design, *, quality_quantile=0.5, seed=0)[source]
Distill the design ledger into a compact torch-free student that judges designs: point -> good/weak.
The
DesignModel’s rows are records (the design coordinates); a row is labeledgoodwhen it was feasible on the device and its quality reached the ledger’squality_quantile.distill_structured_from_labels()– the same machinery the design model tunes – compresses that knowledge into a kilobyte Bayesian-network student usable as a zero-cost pre-filter for future searches. Teacher trains student; a model designs the students; the designer is distilled into a student: each level of the tower is a real artifact.
- footprint(student, *, probe_inputs=None, repeats=3)[source]
Measure the exact exported artifact and optionally benchmark serving.
The artifact byte count comes from
TaskModel.save(), so weights use their actual dtype and the manifest, adapter, labels, configuration, and metadata are included.payload_bytesreports the parameter payload on its own, because the manifest is a near-constant cost that swamps the weights on small students – an int4 student whose weights are 8x smaller than fp32 still exports a comparable manifest.opsremains a structural estimate and is never sufficient by itself for a hard latency gate.
- task_fingerprint(data, labels)[source]
A fixed small vector describing which task this is: the coords cross-task warm start keys on.
(log10 #examples, #labels, #fields, fraction of categorical fields, normalized label entropy)– low-overhead invariants of the dataset, O(1)-scaled so the design surrogate’s default lengthscale treats similar tasks as informative neighbors and dissimilar ones as weakly coupled.
- measure_inference_seconds(student, inputs, *, repeats=5)[source]
Median measured wall-clock seconds per single-input inference for
studenton this host.Runs
student.batch(inputs)repeatstimes (after one untimed warm-up) and reports the median per-item time. Measured, not modeled – run it on the machine whose latency you care about (the deploy device, not the dev laptop) for a number that means anything there.
- measure_ops_per_second(student, inputs, *, repeats=5)[source]
Measured throughput (footprint ops / measured second) for this student kind on this host.
The calibration constant that turns a latency budget into
DeviceSpecmax_ops(DeviceSpec.for_latency()): probe once per (device, student kind), reuse across searches.