Capstone: Production Edge AI System¶
This capstone assembles every module of Level 4 into one coherent product design: a fleet of battery-powered environmental monitoring nodes running an anomaly-detection model, managed and improved over years of deployment. It's the fleet-scale, long-lived counterpart to Level 3's single-device Accelerated Vision Node project — where that project was about one device's runtime pipeline, this one is about the systems and processes that keep a fleet of such devices correct, updated, and compliant for the years they're actually deployed.
System overview¶
+---------------------------+
| Cloud backend |
| |
| Model registry + rollout |<-- Module 02
| gates (staged canary, |
| automatic rollback) |
| |
| Fleet telemetry aggregator |<-- Module 03
| + drift dashboards |
| |
| Federated aggregation |<-- Module 05
| server (FedAvg rounds) |
| |
| DP-protected telemetry |<-- Module 09
| release + budget tracker |
+--------------|--------------+
| (intermittent uplink,
| redacted payloads only)
v
+--------------------------------------------------------+
| Edge node (per device) |
| |
| Wake-on-event trigger (always-on comparator) <- Module 07|
| | |
| v |
| Inference (quantized model, NPU/MCU per hardware rev) |
| | (architecture chosen per Module 08 co-design) |
| v |
| Local anomaly score + confidence |
| | |
| v |
| Local fine-tuning buffer (last-layer adaptation) <- Module 04|
| | |
| v |
| Redacted telemetry batch (DP-noised aggregates only) |
| |
| Dual-slot model store, boot-time hash+version check |
+--------------------------------------------------------+
Design decision 1: fleet heterogeneity and staged rollout, combined¶
The fleet spans multiple hardware revisions (Module 01), so a model release first has to pass an eligibility filter, then a staged rollout gate, before reaching any device:
import hashlib
from dataclasses import dataclass
@dataclass
class DeviceProfile:
device_id: str
npu_present: bool
ram_kb: int
current_model_version: int
def eligible_for_release(device, model_min_ram_kb, model_requires_npu, model_version):
if device.current_model_version >= model_version:
return False
if device.ram_kb < model_min_ram_kb:
return False
if model_requires_npu and not device.npu_present:
return False
return True
def rollout_cohort(device_id, rollout_percentage):
digest = hashlib.sha256(device_id.encode()).hexdigest()
bucket = int(digest[:8], 16) / 0xFFFFFFFF
return bucket < rollout_percentage
def should_receive_update(device, model_min_ram_kb, model_requires_npu,
model_version, rollout_percentage):
return (eligible_for_release(device, model_min_ram_kb, model_requires_npu, model_version)
and rollout_cohort(device.device_id, rollout_percentage))
fleet = [
# v2 hardware (every 3rd device) ships with both an NPU and more RAM;
# v1 hardware (the rest) has neither.
DeviceProfile(f"node-{i:04d}", npu_present=(i % 3 == 0),
ram_kb=512 if i % 3 == 0 else 256, current_model_version=5)
for i in range(200)
]
for pct in [0.05, 0.25, 1.0]:
receiving = sum(1 for d in fleet
if should_receive_update(d, 400, True, 6, pct))
eligible_total = sum(1 for d in fleet
if eligible_for_release(d, 400, True, 6))
print(f"rollout {pct*100:>5.1f}%: {receiving}/{eligible_total} eligible devices selected "
f"({eligible_total}/{len(fleet)} total devices are eligible at all)")
Running this prints:
rollout 5.0%: 5/67 eligible devices selected (67/200 total devices are eligible at all)
rollout 25.0%: 14/67 eligible devices selected (67/200 total devices are eligible at all)
rollout 100.0%: 67/67 eligible devices selected (67/200 total devices are eligible at all)
133 of 200 devices (the v1 hardware majority: no NPU, only 256 KB RAM) never become eligible at all for a model requiring an NPU and 400 KB of RAM — the rollout percentage only ever applies within the 67-device eligible subset, which is exactly the compounding effect Module 01's per-hardware-revision compatibility tracking exists to make visible rather than silently mask: a "25% rollout" here reaches only 14 devices fleet-wide, not 50, because two-thirds of the fleet was never a candidate.
Design decision 2: privacy-respecting telemetry that still enables drift detection¶
Module 03's drift detection needs aggregate statistics from the fleet; Module 09's data minimization and differential privacy constrain what those statistics can reveal about any individual device or its environment. Combining them means releasing DP-noised aggregates, never raw per-device readings:
import numpy as np
def release_fleet_confidence_histogram(all_device_confidences, bins, epsilon, rng):
"""Each device's individual confidence readings never leave it in
raw form -- only a DP-noised count per histogram bin, aggregated
across the whole reporting fleet, crosses the cloud boundary. This
is the fleet-level analogue of Module 09's private_mean_release,
applied to the confidence-drift signal from Module 03."""
counts, _ = np.histogram(all_device_confidences, bins=bins)
sensitivity = 1 # one device's reading can move exactly one bin's count by 1
noisy_counts = [c + rng.laplace(0, sensitivity / epsilon) for c in counts]
return np.array(noisy_counts)
rng = np.random.default_rng(5)
fleet_confidences = np.clip(rng.normal(0.85, 0.12, 5000), 0, 1) # 5000 readings, fleet-wide
bins = np.linspace(0, 1, 11)
true_counts, _ = np.histogram(fleet_confidences, bins=bins)
noisy_counts = release_fleet_confidence_histogram(fleet_confidences, bins, epsilon=1.0, rng=rng)
print("true bin counts: ", true_counts)
print("DP-released: ", np.round(noisy_counts, 1))
Running this prints:
true bin counts: [ 0 0 0 0 11 82 418 1106 1627 1756]
DP-released: [ -1.6 2.9 -3.8 0.4 11.6 83.9 418.0 1106.0 1626.8 1752.4]
For the large bins (hundreds to low thousands of counts), the DP noise is small relative to the true count — bin 8's true 1627 releases as 1626.8, essentially undisturbed. For the near-empty low-confidence bins, though, the same absolute noise scale is large relative to the (near-zero) true count, even producing a nonsensical negative released count for a bin whose true count was 0. This is a real, important limitation, not a bug to smooth over: uniform per-bin noise protects sparse bins far worse in relative terms than dense ones, so any consumer of a DP-released histogram (Module 03's drift detector included) must expect noisy or even implausible values in sparsely-populated bins and should not treat a single low-count bin's exact value as trustworthy — the fleet-wide dense bins, not the sparse ones, are where this mechanism's signal is reliable.
Design decision 3: the update loop across a device's lifetime¶
Putting the whole lifecycle together for one hypothetical node:
- Manufacture & provisioning: device gets a signed initial model, hardware profile registered in the fleet database (Module 01).
- Normal operation: wake-on-event triggers inference (Module 07), local last-layer adaptation runs on confirmed corrections (Module 04), redacted + DP-noised telemetry uploads periodically (Module 09).
- Fleet-wide learning: the cloud periodically runs FedAvg rounds using participating devices' local model deltas, improving the shared base model without ever centralizing raw sensor data (Module 05).
- Model release: a new base model passes the eligibility and staged rollout gates above, widening only if canary telemetry (Module 03's drift signals, Module 02's error-rate gate) stays within bounds.
- Ongoing monitoring: fleet-level drift aggregation flags cohort-wide degradation before it's visible in any single device's behavior.
- Incident response: if a rollout regresses, the dual-slot rollback (Module 02) reverts affected devices to their previous verified model without a re-download, and the safety-case documentation (Module 09) defines what happens operationally while the issue is investigated.
What this capstone deliberately leaves unresolved¶
Real fleet systems need a device-identity and authentication scheme (how does the cloud know a telemetry payload genuinely came from the device it claims to), a concrete transport protocol (MQTT, CoAP, or a proprietary scheme) for the intermittent uplink, and organizational processes (who approves a rollout widening, who's on call for a fleet-wide alert) that are product- and company-specific rather than technical decisions this course can make generically. What this capstone demonstrates is that the nine preceding modules aren't nine separate techniques — they compose into one system, each module's output feeding a specific, identifiable input of another.
How It Actually Works¶
Why eligibility filtering and rollout-percentage sampling must compose
in that specific order, not the reverse. should_receive_update first
calls eligible_for_release (a hard, deterministic pass/fail on
hardware/version compatibility) and only then checks rollout_cohort (a
probabilistic hash-bucket membership test) on whatever survives. Reversing
this order — sampling a rollout cohort from the full fleet, then checking
eligibility within it — would make the effective rollout percentage
depend on how much of the sampled cohort happens to be eligible, which
varies fleet to fleet and release to release in a way nobody set
intentionally. Filtering first means "25% rollout" always means exactly
"25% of the devices this release could possibly run on," which is
precisely why the module surfaces the 14-of-67 (not 14-of-200) arithmetic
explicitly: the rollout percentage's denominator is the eligible
population, and hiding that distinction would make a "25% rollout"
silently mean something different depending on fleet composition on any
given day.
Why uniform per-bin DP noise protects sparse and dense histogram bins
asymmetrically, and why that's an unavoidable property of the mechanism,
not a tuning miss. release_fleet_confidence_histogram adds
i.i.d. Laplace noise with a fixed scale (sensitivity/epsilon) to every
bin, because each bin's count has the same sensitivity — one device's
reading can move any single bin's count by at most 1, regardless of that
bin's current size. The absolute noise magnitude is therefore identical
across bins by construction, but its impact relative to the true count
is not: a fixed noise standard deviation is negligible against a true
count in the thousands and can dwarf (or exceed, producing the observed
negative "count") a true count of 0. This is a direct, structural
consequence of using a sensitivity-based noise calibration uniformly
across bins of wildly different magnitude — the fix (used by more
sophisticated DP histogram releases) is bin-size-aware noise or
post-processing that clips to non-negative integers, not a larger
epsilon, which would weaken the privacy guarantee for every bin
including the already-reliable dense ones.
Why this system's nine modules compose into a strict dependency chain rather than nine independent features. Each design decision in the capstone's lifecycle only functions correctly because an earlier module's output is exactly the input it needs: staged rollout gates (Module 02) only work because eligibility filtering (Module 01) has already narrowed the candidate pool to compatible hardware; drift detection (Module 03) only has data to work with because telemetry was collected and minimized according to Module 09's redaction rules; federated aggregation (Module 05) only produces a trustworthy improved model because each device's local update came from the last-layer adaptation mechanism of Module 04, not raw uncoordinated retraining. This is precisely why the module states the nine modules "compose into one system" rather than listing nine separate techniques — removing any single stage doesn't just weaken that stage's own guarantee, it invalidates an assumption a later stage was built on (an ineligible device receiving an update it can't run, an unredacted payload defeating the DP layer downstream, or an un-adapted local model polluting the federated average).
Stretch goals¶
- Extend the
should_receive_updateeligibility+rollout combination to log why each ineligible device was excluded (RAM, NPU, already up-to-date), and produce a summary report grouping the 200-device fleet by exclusion reason — the kind of report a real rollout dashboard would show an engineer deciding whether to proceed. - Wire the DP-noised histogram release into Module 03's
detect_drift_ksby reconstructing an approximate sample from the noisy bin counts (sample proportionally to each bin's noisy count) and running the KS test against that reconstruction instead of raw data — then measure how much detection sensitivity is lost compared to running the KS test on the true underlying data, as the concrete cost of the privacy layer. - Add a
PrivacyBudgetTracker(from Module 09's exercise) to the telemetry release loop above, and simulate what happens to a device's reporting capability once its epsilon budget for a reporting period is exhausted — should it stop reporting, report with less precision, or fall back to only reporting whether an alert threshold was crossed (a single bit, minimal privacy cost) instead of a full histogram?