Skip to content

Add Granite Speech 5 CTC support - #712

Open
justinchuby wants to merge 2 commits into
mainfrom
justinchuby-add-granite-speech-5
Open

Add Granite Speech 5 CTC support#712
justinchuby wants to merge 2 commits into
mainfrom
justinchuby-add-granite-speech-5

Conversation

@justinchuby

Copy link
Copy Markdown
Member

Summary

  • add native granite_speech5_ctc support for ibm-granite/granite-speech-5.0-470m-turboctc
  • pin every config, weight, processor, integration, and golden path to 7e74c6438b7cfb5090cb6a131538f5e8515a7de3
  • export processor-accurate float32 [B, T, 320] features, int64 valid-frame masks, CTC logits, and frame lengths
  • commit independently generated CUDA L4/L5 goldens for a public nonzero LibriSpeech fixture

Architecture

Implements the native Transformers architecture merged in huggingface/transformers@da7234ac435f6d7c75d8b88d1ac32f53fb1f19a6: 16 pre-norm Macaron Conformer blocks; 128-frame chunk-local Shaw relative attention; Linear+GLU/depthwise-convolution modules; stride-2 residual/conv subsampling in blocks 0 and 1; and mid-encoder self-conditioned CTC. The final CTC projection reuses encoder.out, matching the tied HF ctc_head. Frozen convolution normalization and pairwise pooling use fp32 accumulation so the checkpoint's native bf16 route executes on ORT CUDA.

The native feature extractor contract is preserved: 16 kHz float32 mono audio, 80-bin log-mel, first deltas, adjacent-frame stacking, 320-wide features, and int64 1=valid masks. Runtime metadata contains only real graph ports and explicitly records that onnxruntime-genai 0.15.2 does not orchestrate feature-input CTC.

Evidence

  • L1 graph + weights: 14 passed across the dedicated model tests, build_graph_test.py, weight_alignment_test.py, and targeted L3 collection; ONNX checker and shape/type assertions pass.
  • L2 full config: 3 passed for pinned config download, parse, and full-size graph build.
  • L3 synthetic: every valid padded-batch logit matches native Transformers at atol=rtol=1e-5.
  • Real weights / batch / EP: float32 full valid-frame logits match HF on two distinguishable rows; CPU and CUDA match; emitted lengths are exact; sliced solo-row inference matches the padded batch.
  • Dtypes: fp32 passes at 3e-3; fp16 has max error 0.04321289 with only 2/1,867,776 logits outside 0.03; bf16 has max 0.421875, mean 0.06981443, cosine >0.9999. fp16 and bf16 both match every one of 114 frame IDs and the exact transcript.
  • L4 CUDA: 1 passed against the committed pinned-HF bf16 top-k golden.
  • L5 CUDA: 1 passed; exact 114-frame sequence and transcript: cauliflower mayonnaise take cold boiled cauliflower break into branches adding salt pepper and vinegar to season.
  • CLI/package: pinned bf16 export produced model.onnx + 948,109,312-byte external data, tokenizer assets, exact audio_processor.json, graph-truthful genai_config.json, and runtime compatibility metadata; the persisted package reproduces all 114 golden IDs on CUDA.
  • Quantization: repository suite 6 passed. Olive 0.13.0 MatMul-only dynamic INT8 produced a 484,924,093-byte model with 164 MatMulInteger projections and 16 unchanged standard Attention ops; CPU inference preserves all 114 frames and the exact transcript. An all-linear NF4 attempt was rejected because it degenerated to blanks.
  • Export regressions: 216 ORT GenAI export tests passed; YAML/model coverage: 1,072 passed, 275 skipped.
  • Lint: lintrunner -a reports ok No lint issues; final specialist review reports no defects.

Narrow waivers / environment notes

  • lintrunner f --output oneline --all-files executed its patch phase but its Ruff check process hits Windows WinError 206 after expanding the all-files response list; repeating from a short drive mapping has the same adapter limitation. The required exact lintrunner -a then ran the pinned Ruff tools on the complete diff and is clean.
  • The broad non-integration aggregate recorded 9,758 passes, 72 skips, and 80 unrelated baseline/environment failures: Windows symlink-creation privilege errors, ORT 1.26 lacking newer QMoE attributes, and pre-existing unrelated numeric/evidence failures. All changed and Granite-selected surfaces pass.
  • ORT CUDA reports many host/device Memcpy nodes for scalar shape/control subgraphs. Correctness and CPU/CUDA parity are proven here; no performance claim is made.

Implement the native chunked Shaw-relative Conformer encoder, in-block temporal subsampling, self-conditioned CTC head, processor-accurate feature contracts, and truthful runtime metadata for granite_speech5_ctc.

Add pinned L1-L5 coverage, real-weight CPU/CUDA and multi-dtype parity, exact frame/transcript goldens, CLI packaging checks, and public audio fixture provenance.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
@justinchuby
justinchuby requested review from a team and a lite review from Copilot August 31, 2026 14:21
Comment thread src/mobius/models/granite_speech5.py Fixed
Comment thread src/mobius/models/granite_speech5.py Fixed
Comment thread src/mobius/models/granite_speech5.py Fixed
Comment thread src/mobius/models/granite_speech5_test.py Fixed
Comment thread src/mobius/models/granite_speech5_test.py Fixed
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Performance Comparison

Comparing dda71aab4ea7ed

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 68 68 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 66 66 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 105 105 +0.0%
gpt2 model_size_bytes 324 KB 324 KB +0.0%
gpt2 num_nodes 54 54 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 60 60 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 56 56 +0.0%
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0%
mamba (ssm-text-generation) num_nodes 94 94 +0.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 58 58 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 54 54 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 60 60 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 56 56 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 265 265 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 127 127 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 450 450 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 176 176 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

Comment thread src/mobius/models/granite_speech5.py Fixed
Format the two newly added Granite Speech files with the pinned Ruff version and remove the unused import that escaped the earlier diff-scoped lint pass while the files were untracked.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are a few correctness/documentation issues to fix (notably the Transformers from_pretrained dtype keyword and the FeatureCTC task docstring lagging behind the new frame_lengths output).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds first-class/native support for IBM Granite Speech 5 Turbo CTC (granite_speech5_ctc) to mobius’ model registry/task stack, including ORT GenAI export metadata, synthetic/integration parity tests, and committed L4/L5 goldens to validate end-to-end decoding.

Changes:

  • Implement GraniteSpeech5ForCTCModel + GraniteSpeech5CTCConfig, register granite_speech5_ctc, and add tiny-config/unit tests.
  • Extend FeatureCTCAsrTask to support processor-accurate feature/mask dtypes and (optionally) emit frame_lengths; update ORT GenAI export to handle feature-CTC packages and emit audio_processor.json for Granite Speech 5.
  • Add L3 synthetic parity, real-checkpoint integration tests, and L4/L5 golden case files for Granite Speech 5 Turbo CTC.
File summaries
File Description
tests/synthetic_parity_test.py Adds L3 synthetic parity coverage for Granite Speech 5 CTC vs native Transformers.
tests/granite_speech5_integration_test.py Adds real-checkpoint CUDA parity tests and transcript validation across dtypes.
tests/e2e_golden_test.py Adjusts feature-CTC feeds (int64 mask for Granite) and makes exact-match token logging/decoding robust to non-ndarray token containers.
tests/_test_configs.py Adds a representative tiny Granite Speech 5 CTC config entry for graph-build test coverage.
testdata/golden/audio/granite-speech-5.0-470m-turboctc.json Adds L4 golden logits snapshot for the new Granite Speech 5 CTC case.
testdata/golden/audio/granite-speech-5.0-470m-turboctc_generation.json Adds L5 golden generation output (token IDs + transcript) for e2e validation.
testdata/cases/audio/granite-speech-5.0-470m-turboctc.yaml Adds a pinned revision L4+L5 golden test case definition for Granite Speech 5 CTC.
src/mobius/tasks/_ctc_asr.py Extends FeatureCTC ASR task to support configurable input dtypes/shapes and optional frame_lengths output.
src/mobius/models/granite_speech5.py Introduces the Granite Speech 5 encoder + CTC head implementation (chunked Shaw Conformer + self-conditioned CTC).
src/mobius/models/granite_speech5_test.py Adds config-extraction tests, graph contract/weight-name tests, and ORT GenAI export metadata tests for the new model.
src/mobius/models/init.py Exports GraniteSpeech5ForCTCModel from the models package.
src/mobius/integrations/ort_genai/auto_export.py Emits Granite Speech 5 audio_processor.json and improves feature-CTC output introspection + runtime capability warnings.
src/mobius/_registry.py Registers granite_speech5_ctc with task/config/test metadata.
src/mobius/_configs/_base.py Adds GraniteSpeech5CTCConfig with validation + Transformers config extraction.
src/mobius/_configs/init.py Exposes GraniteSpeech5CTCConfig via the configs public API.
scripts/generate_golden.py Generates CTC goldens using the case dtype and ensures last-frame logits are extracted in fp32.
README.md Lists Granite Speech 5 (and Parakeet CTC) under Speech-to-Text supported models.
docs/model-catalog.md Documents the new granite_speech5_ctc model type, module class, and task.
docs/_generate_models.py Updates Speech-to-Text category description to include CTC models.
Review details

Suppressed comments (1)

src/mobius/tasks/_ctc_asr.py:86

  • FeatureCTCAsrTask.build() now conditionally emits a frame_lengths output when the module provides frame_lengths(), but the task docstring still documents only logits. Please document frame_lengths so downstream callers know it may be present.
    Output:
        ``logits`` — (batch, subsampled_frames, vocab_size) CTC scores
    """
  • Files reviewed: 19/19 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/mobius/models/granite_speech5.py Outdated
Comment on lines +57 to +61
model = transformers.AutoModelForCTC.from_pretrained(
_MODEL_ID,
revision=_REVISION,
dtype=dtype,
).eval()
@github-actions

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing dda71aab4ea7ed

Model Sub-model Changes Status
bert (feature-extraction) model 0
falcon model 0
gemma2 model 0
gemma4 (gemma4) decoder 0
gemma4 (gemma4) embedding 0
gemma4 (gemma4) vision_encoder 0
gemma4_text model 0
gpt2 model 0
llama model 0
llama (static-cache) model 0
mamba (ssm-text-generation) model 0
phi3 model 0
phi3 (static-cache) model 0
qwen model 0
qwen (static-cache) model 0
qwen2 model 0
qwen2 (static-cache) model 0
qwen2_moe model 0
qwen2_moe (static-cache) model 0
qwen3 model 0
qwen3 (static-cache) model 0
qwen3_5_moe (hybrid-text-generation) model 0
qwen3_5_text (hybrid-text-generation) model 0
qwen3_5_vl (hybrid-qwen-vl) decoder 0
qwen3_5_vl (hybrid-qwen-vl) embedding 0
qwen3_5_vl (hybrid-qwen-vl) vision_encoder 0
qwen3_moe model 0
qwen3_moe (static-cache) model 0
qwen3_next (hybrid-text-generation) model 0
t5 (seq2seq) decoder 0
t5 (seq2seq) encoder 0
whisper (speech-to-text) decoder 0
whisper (speech-to-text) encoder 0

No architecture changes detected.


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants