This is an unofficial MLX conversion of IBM's Granite Speech 5.0 470M TurboCTC, with 8-bit affine weight quantization, in the layout used by the Granite-MLX Swift library, for speech recognition on Apple Silicon Macs.
Source: ibm-granite/granite-speech-5.0-470m-turboctc at revision 91dcb0605daf65f88dbb04fef015c423241d941d. This is the multilingual release of 21 October 2026: English, French, German, Spanish, Portuguese, Italian and Japanese, with native punctuation and capitalization.
All model credit belongs to IBM Granite. This repository changes the weight layout (MLX safetensors as read by Granite-MLX), the weight precision (8-bit affine), config.json (adds the quantization block), one tokenizer setting, and adds change notices. IBM has not reviewed or endorsed it.
IBM describes the model as 16 conformer blocks trained with CTC, with a 16,384-token multilingual BPE head. It uses 8× temporal subsampling (100 Hz to 12.5 Hz), block attention over 128 frames, and self-conditioned CTC from the middle layer: 470M parameters in total. For training data, intended use, benchmarks and the technical report, see the IBM model card at the pinned revision.
IBM releases this model under the Apache License 2.0. The pinned source revision declares license: apache-2.0 in its card metadata, and its README states:
License: Apache 2.0
Apache-2.0 section 2 grants the right to "reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form". Section 4 requires that redistribution include a copy of the licence and a notice of changes. Accordingly:
LICENSE is the verbatim Apache-2.0 text from apache.org (SHA-256 cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30). The source repository ships no separate LICENSE or NOTICE file at this revision.MODIFICATIONS.md states what was changed, and each changed file carries its own notice: the safetensors header metadata has license: Apache-2.0 and a modification_notice field, tokenizer_config.json has a _modification_notice key, and config.json has a _modification_notice key. Unchanged source files are byte-identical.Do not confuse this model with ibm-granite/granite-speech-5.0-470m-turboctc-nc, a different, English-only model under CC-BY-NC-SA-4.0. That model was not used here.
| Format | MLX safetensors (model.safetensors) plus config.json, in the layout read by Granite-MLX (GraniteModelLoader) |
| Weights | 8-bit affine weight-only quantization, group size 128 (64 for encoder.input_linear, whose 320-wide input does not divide by 128); 179 matrices quantized; other tensors fp16 |
| Input | 16 kHz mono audio; Granite-MLX computes the log-mel + delta features in Swift and runs variable-length input (no fixed shape, no padding) |
| Not compatible with | mlx-audio or mlx-lm loaders (different layout); use Granite-MLX |
granite-mlx-manifest.json |
converter manifest: source revision, source weight hash, skipped and transposed tensors, quantization |
How it was produced. convert_granite.py from Granite-MLX at commit 7ad6b19c698aac5aa8328c911e4a6eead456c5a1, run unmodified: --precision fp16 --quantization-bits 8 --group-size 128 --source-model-id ibm-granite/granite-speech-5.0-470m-turboctc --source-revision 91dcb0605daf65f88dbb04fef015c423241d941d. It keeps the 534 source tensors (stored as 892 arrays, because each of the 179 quantized matrices adds scale and bias arrays), drops 16 BatchNorm num_batches_tracked counters (training-only), and transposes the 16 depthwise Conv1d weights from [O,I,K] to MLX's [O,K,I]. The license and modification_notice entries were then added to the safetensors header metadata, with the tensor bytes unchanged (hash-verified). Versions: Python 3.12, mlx 0.31.2, torch 2.13.0, transformers 5.16.1 (reference inference). Run on macOS 27.0.1, Apple M2 Max, on 2026-11-12/13 (America/Los_Angeles). Full provenance is in CONVERSION.json.
| File | Bytes | SHA-256 |
|---|---|---|
model.safetensors |
488,666,432 | d493ebfa6dcfe553809ff39c381d8195e33f74b098a964dfc98062843108d8b6 |
granite-mlx-manifest.json |
2,847 | 78ab762c9eff2cade1197c373eca51290e0b2e857b43681b6551151cc4f93511 |
config.json |
1,291 | e6de52753332c4125c273a4d6a4aed5c3ff38fba66d30189ae558f7ac5fbfe43 |
config.json |
source file plus the converter's quantization block and a _modification_notice key |
|
tokenizer.json, processor_config.json, generation_config.json |
byte-identical to the source revision | |
tokenizer_config.json |
source file plus two keys (see below) |
Every file's SHA-256 is listed in SHA256SUMS; check with shasum -a 256 -c SHA256SUMS.
Tokenizer deviation. tokenizer_config.json adds "clean_up_tokenization_spaces": false and a _modification_notice string (the Apache-2.0 §4(b) change notice; Python transformers and swift-transformers both load the file with it, tested); nothing else differs from the source file (source SHA-256 2f61ece3468bf7ca7a1b6dd3fd5b809814315bea9c298feea035fda4931b9f02). swift-transformers 1.3.3 turns that cleanup on when the key is absent, and it strips the space before ? ! : ;, which corrupts French typography. Python transformers already leaves it off for this tokenizer, so Python output is unchanged.
Tested with the granite-mlx CLI 0.1.1, built from Granite-MLX 7ad6b19 with swift build -c release --product granite-mlx (Swift 6.4), on a local copy of this repository:
granite-mlx transcribe audio.wav --model /path/to/granite-speech-5.0-470m-turboctc-mlx-8bit --backend mlx \
--no-punctuate --output-format txt --output-dir out
--no-punctuate disables Granite-MLX's separate punctuation model; this Granite release punctuates natively. Use --output-format txt: compatibility was validated for raw text only. The CLI's default subtitle output (SRT) and other word-timed formats run words together with this tokenizer (see Limitations). Loading by Hugging Face repository id was not tested.
No Python loader for this layout was tested.
Method. The reference is IBM's model in transformers 5.16.1, fp32 on CPU, variable length with an attention mask, with greedy CTC (official inference). This package was run through the Granite-MLX Swift CLI, which uses its own Swift feature frontend.
The fixed corpus has 141 unique clips:
hf-internal-testing/librispeech_asr_dummy);Long-audio behaviour was tested separately on 4 constructed clips of 286.5–328 s. Text is compared after NFKC normalization, lower-casing and removing punctuation; WER is used for European languages, and CER with whitespace removed for Japanese. All rates are corpus-level per set. With 10 clips per FLEURS language these are small samples: they show conversion fidelity, not benchmark accuracy.
Results for this 8-bit package, in percent:
| Set (clips) | vs our MLX fp16 package | vs official IBM inference (exact text) | vs ground-truth transcripts (official IBM model in brackets) |
|---|---|---|---|
| LibriSpeech en (73) | 0.09 | 0.09 (71/73) | 1.71 (1.71) |
| FLEURS fr (10) | 0.00 | 0.00 (10/10) | 7.19 (7.19) |
| FLEURS de (10) | 0.00 | 0.00 (10/10) | 2.86 (2.86) |
| FLEURS es (10) | 0.00 | 0.00 (10/10) | 3.40 (3.40) |
| FLEURS pt (10) | 0.39 | 0.00 (9/10) | 10.77 (10.77) |
| FLEURS it (10) | 0.43 | 0.43 (9/10) | 2.55 (2.13) |
| FLEURS ja (10, CER) | 0.00 | 0.00 (9/10) | 4.97 (4.97) |
Against a chunked official reference that uses the same 286.72 s policy as Granite-MLX, the long clips differ by 1 normalized word in 3,672.
Size and speed. model.safetensors is 488,666,432 bytes. Measured with the Granite-MLX CLI, 5 interleaved rounds: median 0.10 s on an 11 s clip, 0.16 s on a 1-minute clip and 0.72 s on a 328 s clip, peak RSS 0.65 GB. These timings were measured on a busy machine (1-minute load average about 6–7) and are indicative only.
config.json loses its quantization block, the Granite-MLX 7ad6b19 CLI crashes (SIGTRAP, addmm shape mismatch) instead of reporting an error. Verify SHA256SUMS before use.<|nospeech|> token, which decode returns as text unless you filter it.--audio-chunk-duration 286.72 --audio-chunk-context 20.48, as tested) into windows with 20.48 s of context on each side, trimming context in frame space before CTC collapse. Results near chunk joins depend on that policy (see Validation).7ad6b19 loads this tokenizer only by forcing the GPT-2 tokenizer class (the source class is ParakeetTokenizer, which swift-transformers 1.3.3 rejects). Decoding is correct for all seven languages in our tests when the shipped tokenizer_config.json is used.7ad6b19. The CLI's default SRT output (and other word-timed formats) joins words without spaces, for example Andso,myfellowAmericans,..., because the timing path decodes one token at a time and this tokenizer's decoder strips each token's leading space. Raw text (--output-format txt) is correct. A fix belongs in Granite-MLX.