Add split Whisper encoder-decoder support - #1407
ssss141414 wants to merge 4 commits into
Conversation
Independent reviewer verdict: REQUEST_CHANGESReviewed exact candidate SHA: Blocking issue
Verified passing evidence
This is a skill-level reviewer opinion posted as a normal PR comment, not a GitHub Review state. No product code or PR metadata was modified. |
c07a51a to
ce28631
Compare
Explainer resolution of baseline-currentness requestResolved the provenance gap raised in the prior
This is an explainer provenance update posted as a normal PR comment. The prior reviewer opinion remains the latest reviewer verdict pending independent re-review; no GitHub Review state was created or changed. |
Independent reviewer verdict: APPROVEAPPROVE for
This is a skill-level opinion in a normal PR conversation comment. It does not change GitHub Review state, draft/readiness, labels, body, branch, or thread resolution. |
Qiong Wu (qiowu) (DingmaomaoBJTU)
left a comment
There was a problem hiding this comment.
Reviewed the split Whisper runtime/export integration, ASR evaluator, and build-stage forwarding. The reported CI checks are green, but I am withholding approval for the evaluator behavior below. A minimal execution of the actual compute method and WER helpers reproduces the issue; full Whisper model export/inference was not rerun locally.
922eb44 to
e31829c
Compare
Independent reviewer verdict: APPROVEAPPROVE for
This is a skill-level reviewer opinion posted as one normal PR issue comment. It does not submit GitHub Review state or mutate draft/readiness, labels, body, branch, merge state, or review threads. |
Summary
This contribution adds
openai/whisper-mediumautomatic speech recognition as a split Whisper encoder and cached autoregressive decoder, with CPU FP32 and FP16 recipes and a generalized ASR evaluator. It ships at Effort/Outcome L2 with full required-tuple coverage and reaches the committed ceiling at L3 PASS. The final candidate builds and executes both precisions, preserves PyTorch parity through bounded generation, completes a real LibriSpeech functional smoke, and scores empty ASR hypotheses as deletion errors instead of aborting evaluation.Model metadata
What the model does
Whisper Medium is a multilingual audio-to-text encoder-decoder Transformer. It converts up to 30 seconds of 16 kHz speech into log-Mel features, encodes them, and autoregressively generates text tokens for same-language transcription or speech translation.
openai/whisper-mediumrevisionabdf7c39ab9d0397620ccaea8974cc764cd0953e; the pinned model card describes an encoder-decoder model for ASR and speech translation trained on 680k hours of labelled speech; the pinned config hasarchitectures=[WhisperForConditionalGeneration],is_encoder_decoder=true, andnum_mel_bins=80.Primary user stories
Supported tasks
automatic-speech-recognitionpipeline_tag=automatic-speech-recognition; Optimum TasksManager registerswhisperforautomatic-speech-recognitionandautomatic-speech-recognition-with-past; WinMLmaininspection resolved ASR through vendor defaults, but only to a generic monolithic inference class.speech-translationtranslatecontext token.Model architecture
A 769M-class sequence-to-sequence Transformer with a convolutional log-Mel encoder, 24 encoder layers, a cached autoregressive 24-layer decoder with self- and cross-attention, and a vocabulary projection head.
abdf7c39ab9d0397620ccaea8974cc764cd0953e, Transformers 4.57.6modeling_whisper.pyconcrete class construction and forward paths, and the clean-mainHTP hierarchy of 686 modules, 173 traced modules, and 2903 tagged ONNX nodes (verified).Validation and support evidence
Baseline
The baseline is WinML
0.3.1at current pinnedmaincommitda5dbcd5812daaabb64b89b47c24db2da2d4e1c7. The refresh decision is PARTIAL-RERUN from last validated main2ff69221a34700aaf3027dfc798a3511511b0835and original baseline evidence commitf831830cbf2168b4833d44d8b3413ea0ecfd0dea. The complete 33-file moved-main range was classified by dependency reachability: model profile, Optimum probe, inspect/config, baseline FP32 build, baseline Eval schema, candidate FP32 build/parity, and candidate FP32 Analyze were retained with original provenance; baseline perf, FP16 build/parity/Analyze, all component perf, FP32 Eval, affected quality partitions, Ruff, mypy, and exact-SHA hosted checks were rerun.Starting auto-configuration resolved
automatic-speech-recognitionthrough vendor defaults to a generic monolithicAutoModelForSpeechSeq2Seq/Whisper path. Optimum already exposedaudio-classification,automatic-speech-recognition,automatic-speech-recognition-with-past,feature-extraction, andfeature-extraction-with-past; WinML added no tasks, so the Optimum probe verdict was VENDOR-ONLY.f831830c): the monolithic FP32 build completed in309.5s. The missing artifact was reconstructed operationally on the exact base and matched the historical model content; this did not relabel the historical execution.3077.163 ms, P503076.132 ms, throughput0.32 samples/s, RSS total delta372.34 MiB. This is not autoregressive transcription latency. GPU memory was unavailable under the current nullable/provenance schema.2ff69221):UNSUPPORTED-TASK, exit2;automatic-speech-recognitionwas absent from the WinML Eval registry, and no moved-main change reached that registry or evaluator path.Goal
Outcome
CPUExecutionProvider / cpu / fp32andCPUExecutionProvider / cpu / fp16tuples.e31829c410abe7143b7798b8811c79efca093757is based onda5dbcd5812daaabb64b89b47c24db2da2d4e1c7. The four contribution commits are PATCH-EQUIVALENT to pre-rebase head922eb4489581e4e93bc3d972baaf76ddbfe178f9: every orderedrange-diffrow is=, stable patch IDs match, the 16-path name-status inventory matches, all 16 owned blobs match, and the integrated candidate tree equals the independently predicted merge tree. There were no conflicts, manual resolutions, dropped or added commits, or semantic patch changes.examples/recipes/openai_whisper-medium/cpu/cpu/automatic-speech-recognition_fp32_encoder_config.json,automatic-speech-recognition_fp32_decoder_config.json,automatic-speech-recognition_fp16_encoder_config.json, andautomatic-speech-recognition_fp16_decoder_config.jsonin the same directory.src/winml/modelkit/loader/task.py;src/winml/modelkit/models/hf/__init__.py;src/winml/modelkit/models/hf/whisper.py;src/winml/modelkit/models/winml/encoder_decoder.py;src/winml/modelkit/commands/build.py;src/winml/modelkit/eval/__init__.py;src/winml/modelkit/eval/evaluate.py;src/winml/modelkit/eval/automatic_speech_recognition_evaluator.py;src/winml/modelkit/utils/eval_utils.py;tests/unit/models/whisper/test_onnx_config.py;tests/unit/commands/test_build.py;tests/unit/eval/test_automatic_speech_recognition_evaluator.py.whisper-001throughwhisper-006, thewhisper-002counterexample update, methodology findings_meta-115through_meta-117, and paired tester/reviewer contract updates are on separate draft ModelKitArtifacts PR #350, preserving the Lane A/Lane B boundary.Per-EP/device/precision results and Functional smoke Eval
Goal ladder
1d2a2eac6516401570c4912c7baa8bc9ba50758aonly after exact patch/tree/blob equivalence. CPU FP16 encoder and decoder were rebuilt on final SHAe31829c410abe7143b7798b8811c79efca093757; ONNX checker, public I/O, 48-in/48-out decoder cache, initializer precision, and adjacent external-data contracts passed.1d2a2eacafter equivalence. FP16 encoder, first decoder step, cached decoder step, and bounded 32-step generation parity were freshly rerun and passed one31829c4.0.09090909090909091with requested/processed/skipped1/1/0. The formal mixed empty-hypothesis regression produced WER2/3with2/2/0accounting and no exception.Per-tuple structure and perf
These are component/first-step measurements, not full autoregressive transcription latency. GPU memory is unavailable for these CPU runs.
FP16 structural evidence: encoder
729nodes,368FLOAT16 initializers,614432768external-data bytes, ratio0.5000; decoder1517nodes,584FLOAT16 initializers,1019509760external-data bytes, ratio0.5000, with 48 cache inputs and 48 outputs. Both adjacent external-data checks passed.FP16 parity: encoder cosine
0.9999933185733331; first/cached logits cosine0.9999999999696552/0.9999994068226346; minimum generation cosine0.9999994068226346; worst max-absolute difference0.03837871551513672; exact tokens and transcript matched across32steps.Functional smoke Eval
PASS on final candidate
e31829c410abe7143b7798b8811c79efca093757: FP32,CPUExecutionProvider, devicecpu. This is functional-smoke operability evidence only, not representative accuracy or benchmark quality. No Eval accuracy was measured for FP16 or another EP/device tuple.openslr/librispeech_asrrevision71cacbfb7e2354c4226d01e70d77d5fca3d04ba1, configclean, splitvalidation, deterministic first row.1; requested1; processed1; skipped0; selection seed42.1beam; one utterance capped at30 seconds / 3000 log-Mel frames; generation length32;return_sequences=1.seq2seq.wer = 0.09090909090909091.UNSUPPORTED-TASKat exit2; this contribution registers generalized CTC/seq2seq ASR evaluation with raw bytes/path audio decoding, mono conversion/resampling, Whisper feature extraction, bounded generation, transcript decoding, corpus WER, and exact accounting.['hello world', 'recognized'], hypotheses['', 'recognized']; two word errors over three reference words; WER2/3; requested/processed/skipped2/2/0; no abort. The empty hypothesis contributes two deletions. Empty references and zero selected rows still fail closed.Quality and hosted checks
446source files.10 passed; Eval partition:697 passed; optim partition:883 passed; models partition:1568 passed; analyze partition:1529 passed.3968 passed, 1 failed; exact base reproduced the same installed OpenVINO plugin dependency failure. Remaining local:982 passed, 1 failed; exact base reproduced the same PyPI TLS handshake failure. These are host/package conditions; the corresponding exact-SHA hostedtest (commands)andtest (remaining)checks succeeded.e31829c410abe7143b7798b8811c79efca093757reached terminal SUCCESS: CodeQL, license/cla, lint, test (optim), test (commands), test (models), test (analyze), test (remaining), and Analyze (Python).Delta
The monolithic baseline recipe is intentionally changed into separate encoder and decoder recipes. FP16 uses the same architecture-driven split contracts and additionally declares
quant.mode=fp16. The production recipe README remains untouched./export/input_tensorsinput_features float32 [1,80,3000] range [-1,1]; decoder_input_ids int32 [1,16] range [0,2]input_features float32 [1,80,3000] range [0,1]/export/output_tensorslogits; encoder_last_hidden_stateencoder_hidden_states/loadertask=automatic-speech-recognition, model_class=AutoModelForSpeechSeq2Seq, model_type=whispertask=feature-extraction, model_class=WhisperEncoderWrapper, model_type=whisper/export/input_tensorsinput_features float32 [1,80,3000]; decoder_input_ids int32 [1,16]decoder_input_ids int32 [1,1]; encoder_hidden_states float32 [1,1500,1024]; decoder_attention_mask bool [1,448]; cache_position int64 [1]; past_{0..23}_{key,value} float32 [1,16,448,64]/export/output_tensorslogits; encoder_last_hidden_statelogits; present_{0..23}_{key,value}/loadertask=automatic-speech-recognition, model_class=AutoModelForSpeechSeq2Seq, model_type=whispertask=text2text-generation, model_class=WhisperDecoderWrapper, model_type=whisper/quantnullmode=fp16, task=feature-extraction, model_id=openai/whisper-medium, model_type=whisper, fp16_keep_io_types=true/quantnullmode=fp16, task=text2text-generation, model_id=openai/whisper-medium, model_type=whisper, fp16_keep_io_types=true/export and /loadermonolithic baseline export/loader/export and /loadermonolithic baseline export/loaderThe change remains reducibility-consistent with the charter. Recipe-free architecture acceptance passed and emitted split encoder/decoder artifacts; this is architecture acceptance, not a Goal-tier or precision verdict.
Bug fix explanation: architecture-specific split export
openai/whisper-mediumfor ASR selected a generic monolithic forward graph rather than an end-to-end transcription-capable split composite.AutoModelForSpeechSeq2Seq.WhisperEncoderWrapper,WhisperDecoderWrapper,WhisperEncoderIOConfig,WhisperDecoderIOConfig, andWinMLWhisperModel, with loader/HF registration, derive separate encoder and decoder exports and register the composite runtime.Bug fix explanation: shared encoder-decoder generation runtime
WinMLEncoderDecoderModeldid not generalize all routing, prompt, and static-cache behavior required by Whisper.WinMLEncoderDecoderModelgeneralizes encoder input routing, generation metadata, prompt prefill, and static-cache handling.28.540037900034804s.Bug fix explanation: automatic speech recognition evaluator
winml eval --schema --task automatic-speech-recognitionreturnedUNSUPPORTED-TASKwith exit2.WinMLAutomaticSpeechRecognitionEvaluator,_asr_mode,_word_error_counts,_word_error_rate,_EVALUATOR_REGISTRY, and_DEFAULT_DATASETSadd pinned LibriSpeech selection, bytes/path decoding, mono/resample/cap preprocessing, explicit CTC versus seq2seq dispatch, bounded generation, and corpus WER.0, verifies semantics, processes1/1/0, and emits WER0.09090909090909091; focused ASR reports10 passed, Eval reports697 passed, and hosted checks succeed.Bug fix explanation: empty ASR hypotheses
computerejected empty prediction strings before predictions/references reached the existing corpus word-error helpers, even though those helpers correctly represent the reference words as deletions.WinMLAutomaticSpeechRecognitionEvaluator.computeremoves only the empty-hypothesis exception; the mixed regression intest_automatic_speech_recognition_evaluator.pypreserves empty predictions for corpus scoring.['hello world', 'recognized']and hypotheses['', 'recognized']produce two errors over three reference words, WER2/3, accounting2/2/0, and no exception; focused ASR10 passed, Eval697 passed, Ruff/mypy pass, and all nine exact-SHA hosted checks succeed.Bug fix explanation:
--no-optimizestage control--no-optimizebut still executed the Optimize stage._build_hf_pipelineand_build_onnx_pipelinethread--no-optimize/config.skip_optimizeto the executing sink.Bug fix explanation: evaluator override typing repair
prepare_pipelineoverride return type.PipelineunderTYPE_CHECKING, usesPipeline | Nonewith the established suppression, and explicitly returnsNone.446source files, Ruff passes, focused ASR reports10 passed, and all nine exact-SHA hosted checks succeed.Analyze summary - component level and op level
Static Analyze status is PASS for all four artifacts; this is rule-based compatibility analysis, not runtime execution. FP32 Analyze was reused from
c07a51acb6eaf6e19a9e0c50a0c6e80066a6e936after exact equivalence, while FP16 encoder/decoder Analyze was freshly rerun on final candidatee31829c410abe7143b7798b8811c79efca093757. There are no component mapping gaps.Component-level summary
model.encoder.conv_frontend;model.encoder.layers[]model.decoder.embeddings;model.decoder.layers[];proj_outmodel.encoder.conv_frontend;model.encoder.layers[]model.decoder.embeddings;model.decoder.layers[];proj_outOp-level summary
Rule-less
CUDAExecutionProvider/GPU,MIGraphXExecutionProvider/GPU,TensorrtExecutionProvider/GPU, andDmlExecutionProvider/GPUrows have no runtime-support classification. The actionable decoder findings above preserve partial, unsupported, and unknown static-rule outcomes; they are not runtime-support claims.Reproduce commands