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

  1. 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_generator does.
  2. Every 8 mel frames are stacked (1,024 values) and projected with embedder_projection.f32le in float32: one 512-d row per 80 ms.
  3. 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.
  4. The speaker cache follows transformers' Nemotron3DiarizationSpeakerCache.update / _compress line by line. The one change is precision: the compression scores are float64 (see Verification).
  5. 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
16
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for mlboydaisuke/Nemotron-3-Diarization-CoreAI

Quantized
(13)
this model