Core AI is Apple's on-device ML runtime in iOS 27 / macOS 27 and the successor to Core ML: PyTorch models are exported with Apple's coreai-torch (LLMs: coreai.llm.export) into .aimodel bundles that run on the GPU or the Neural Engine.
This model has no row on DeviceMark, the on-device LLM leaderboard.
Nemotron-3-Diarization — Core AI
nvidia/Nemotron-3-Diarization (OpenMDW-1.1,
99.2M parameters) converted to Apple Core AI. It is streaming speaker diarization, "who spoke when",
for up to 8 speakers at 10 ms resolution, on-device. The graph is the per-chunk network. The host
keeps the state: the log-mel front end, the speaker cache and its FIFO.
It succeeds the 4-speaker Streaming-Sortformer-Diar-CoreAI. Another Core AI conversion of this checkpoint exists: smdesai/Nemotron-3-Diarization-CoreAI.
Files
| file | what |
|---|---|
n3d_streaming_float16.aimodel |
the graph, fp16 weights, T = 541 packed rows; all three streaming modes; macOS (198.0 MB) |
n3d_offline_float16.aimodel |
the same graph at T = 684, for the offline profile; macOS (198.0 MB) |
n3d_streaming_float16.h18p.aimodelc |
the streaming graph compiled ahead of time for the iPhone 17 Pro GPU (h18p, 198.2 MB) |
n3d_offline_float16.h18p.aimodelc |
the offline graph, same target (198.3 MB) |
embedder_projection.f32le |
[512, 1024]: the projection of 8 stacked mel frames (a host matmul) |
silence_embeds.f32le |
[512]: the learned silence row of the speaker cache |
mel_filters_128x257.f32le |
[128, 257]: the slaney mel filterbank, bit-identical to the transformers feature extractor's |
hann_window_400.f32le |
[400]: torch.hann_window(400, periodic=False), bit for bit |
metadata.json |
the graph contract, the host constants of each profile, the mel parameters, and for every asset and bundle its sha256 (bundles also: size, target, compute preference) |
host/ |
the four .f32le files and metadata.json again, in one directory: what a client that fetches a directory (CoreAIKit's catalog) takes |
config.json, processor_config.json |
the source repo's, unchanged (revision f667ed7) |
LICENSE, NOTICE |
the OpenMDW-1.1 agreement; the origin of the weights and what was converted |
SHA256SUMS |
every file above |
.f32le files are raw little-endian float32 in C order.
Graph contract
inputs: packed [1, T, 512] float32 [speaker cache | FIFO | chunk + look-ahead] rows, left-packed in [0, L); rows [L, T) zero
valid [1, T] float32 1.0 for rows < L, 0.0 after
outputs: logits [1, T*8, 8] float32 speaker logits at 10 ms; rows [0, L*8) are real
T = 541 for the three streaming modes, 684 offline. Both fp16 bundles take and return float32.
Host
- Log-mel: pre-emphasis 0.97 (the first sample kept), 512-point STFT with hop 160 and the 400-sample
window centered, |X|², 128 slaney mel bins, log(x + 2⁻²⁴). No normalization. Each streaming chunk
computes its mel from its own audio slice, as the source card's
inputs_generatordoes. - Every 8 mel frames are stacked (1,024 values) and projected with
embedder_projection.f32lein float32: one 512-d row per 80 ms. - Each step packs [cache | FIFO | chunk + look-ahead] and runs the graph. It keeps
logits[:L*8]and average-pools the sigmoid 8× to the cache rate, then emits the chunk's frames. - The speaker cache follows transformers'
Nemotron3DiarizationSpeakerCache.update/_compressline by line. The one change is precision: the compression scores are float64 (see Verification). - Turns: per speaker, every run of probability > 0.5. Overlapping speech stays overlapping.
| profile | chunk + look-ahead (encoder frames of 80 ms) | FIFO / update period | graph |
|---|---|---|---|
| low latency | 9 + 4 (0.72 s + 0.32 s) | 264 / 222 | T = 541 |
| very low latency | 6 + 2 (0.48 s + 0.16 s) | 264 / 222 | T = 541 |
| ultra low latency | 3 + 1 (0.24 s + 0.08 s) | 264 / 222 | T = 541 |
| offline | 340 + 40, over the whole recording's mel | 40 / 300 | T = 684 |
The speaker cache holds 264 rows in every profile. metadata.json carries all of these constants.
Verification
The reference is transformers Nemotron3DiarizationForAudioFrameClassification in fp32. Two clips were
used: 97.6 s (the transformers integration-test audio, where the cache compresses 4 times in low latency)
and 21.5 s. The score is speaker-activity agreement at 0.5 over every 10 ms frame × 8 speakers, with a bar
of 99.9 %. Offline leaves out the last 16 frames, as the transformers integration test does.
fp16 graph on the Mac GPU, full streaming loop:
| clip | profile | agreement@0.5 (differing / elements) | max |Δp| | turns: reference / ours / matched |
|---|---|---|---|---|
| 97.6 s | low latency | 99.9987 % (1 / 78,072) | 0.025 | 37 / 38 / 36 |
| 97.6 s | very low latency | 99.9424 % (45 / 78,072) | 0.362 | 43 / 43 / 42 |
| 97.6 s | ultra low latency | 99.9949 % (4 / 78,072) | 0.011 | 61 / 60 / 59 |
| 97.6 s | offline | 99.9987 % (1 / 77,952) | 0.029 | 29 / 29 / 29 |
| 21.5 s | low / very low / ultra low | 100 % (0 / 17,192 each) | ≤ 0.0031 | 10 / 10 / 10 |
| 21.5 s | offline | 99.9941 % (1 / 17,072) | 0.0037 | 9 / 9 / 9 |
- The Python host and the Swift host give these numbers with bit-identical logits in all 8 runs.
- The fp32 bundle on the CPU alone, 97.6 s low latency: 100 % (max |Δp| 5.5e-6).
- The compression scores are float64 because transformers' own fp32 top-k choice sits 2 ulp from its boundary in 3 of the 16 compressions of these clips. A float32 host flips one of them in low latency.
- The Mac Neural Engine (the same fp16 graph, one ANE region) reaches 99.7746 % on 97.6 s low latency. That is below the bar, so no ANE bundle is shipped. On 21.5 s it reaches 100 %.
- iPhone 17 Pro (iOS 27.0, build 24A437, thermal state nominal throughout, 2026-09-24), the h18p GPU bundles: 97.6 s low latency 99.9987 % (1 / 78,072), 21.5 s 100 %; offline 99.9987 % and 99.9941 %. Its decisions equal the Mac GPU's on 78,070 of 78,072 elements. The h18p Neural Engine compile reaches 99.7758 % on 97.6 s low latency, the same as on the Mac, so it is not shipped.
Speed. One chunk is 0.72 s of audio in low latency; the offline graph takes 30.4 s per chunk.
| device | ms per chunk, median / p90 | 97.6 s end to end (real-time factor) | load, first / second |
|---|---|---|---|
| iPhone 17 Pro GPU, streaming graph (h18p, iOS 27.0) | 30.1 / 30.5 | 4.16 s (0.043) | 0.68 s / 0.12 s |
| iPhone 17 Pro GPU, offline graph (h18p) | 34.5 / 35.8 | 0.17 s (0.0018) | 0.55 s / 0.06 s |
| M4 Max GPU, macOS 27.0, shared with other jobs | 15.4–15.9 / 15.6–16.1 | 2.1–2.3 s (0.022); one run of six took 6.2 s | 0.12 s warm |
On the iPhone the first graph call after a load took 2.7 s (streaming) and 0.8 s (offline); the numbers above are the calls after it.
Use
The Swift host is the NemotronDiarizer package in
conversion/nemotron3_diar/swift
(no dependencies; it links the system CoreAI framework):
let assets = try N3DAssets(directory: repoDir) // this repo, downloaded
let diarizer = try await N3DDiarizer(assets: assets, computeUnits: .gpu,
profile: .streamingProfile(mode: .lowLatency))
let out = try await diarizer.process(samples: pcm16kMono) // out.probs: [frames, 8]
let turns = N3DDiarizer.segments(from: out.probs) // speaker, start, end
On iOS it picks the .h18p.aimodelc beside the .aimodel. The Python host is host_loop.py in the same
directory. Conversion, gates and the lessons of this port:
conversion/nemotron3_diar.
License
OpenMDW-1.1, the source model's license. LICENSE (the agreement) and NOTICE (origin, revision, what was
converted) ship in this repo. Keep both with any part of it you redistribute.
- Downloads last month
- -
Model tree for mlboydaisuke/Nemotron-3-Diarization-CoreAI
Base model
nvidia/Nemotron-3-Diarization