-
Notifications
You must be signed in to change notification settings - Fork 60
Add Whisper VitisAI recipes #425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
360b11e
Add Whisper VitisAI recipes
bf44ef3
Merge branch 'main' into whisperPR
NiranjanRaviGit bc3fd27
Merge branch 'main' into whisperPR
NiranjanRaviGit 8c37116
Merge branch 'main' into whisperPR
NiranjanRaviGit dd5ebb6
Merge branch 'main' into whisperPR
NiranjanRaviGit 1aa2ae9
Update LICENSE
NiranjanRaviGit 24ff5b7
Update LICENSE
NiranjanRaviGit aee8ac9
Update LICENSE
NiranjanRaviGit 62a055d
Delete openai-whisper-medium/VitisAI/LICENSE
NiranjanRaviGit d7d47c9
Update LICENSE
NiranjanRaviGit 808675a
Delete openai-whisper-large-v3-turbo/VitisAI/LICENSE
NiranjanRaviGit 7ff4498
adding license info to root folder and removing it from child folders
08d3dbc
Merge branch 'microsoft:main' into whisperPR
NiranjanRaviGit 7e4cf7d
Merge branch 'main' into whisperPR
NiranjanRaviGit e4ca201
Merge branch 'main' into whisperPR
NiranjanRaviGit 6507123
Merge branch 'main' into whisperPR
NiranjanRaviGit 36b8c77
cleanup of default falgs
9403cea
Merge branch 'microsoft:main' into whisperPR
NiranjanRaviGit b8bcdd3
code cleanup
a0b07ce
Merge branch 'main' into whisperPR
NiranjanRaviGit 939bd65
Merge branch 'main' into whisperPR
NiranjanRaviGit ca38336
Merge branch 'main' into whisperPR
NiranjanRaviGit 0efb4fa
code revoew changes
a16d000
Clarify Whisper ONNX source
5010f27
Merge branch 'main' into whisperPR
NiranjanRaviGit e7cb592
Potential fix for pull request finding
NiranjanRaviGit 8463c2a
Merge branch 'main' into whisperPR
NiranjanRaviGit 0b99354
review fixes
c8a3d73
Merge branch 'main' into whisperPR
NiranjanRaviGit File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| # Whisper on WinML — Speech-to-Text on the Edge with ONNX Runtime Vitis EP | ||
|
|
||
| --- | ||
|
|
||
| ## Installation | ||
|
|
||
| ### 1. Create and activate conda environment | ||
|
|
||
| ```bash | ||
| conda create -n winml_whisper python==3.11 | ||
| conda activate winml_whisper | ||
| pip install --pre -r requirements.txt | ||
| ``` | ||
|
|
||
| ### 2. Check installed WASDK version | ||
|
|
||
| ```bash | ||
| conda list | findstr wasdk | ||
| ``` | ||
|
|
||
| > **Expected Output:** | ||
| > ``` | ||
| > wasdk-microsoft-windows-ai-machinelearning 1.8.260209005 pypi_0 pypi | ||
| > wasdk-microsoft-windows-applicationmodel-dynamicdependency-bootstrap 1.8.260209005 pypi_0 pypi | ||
| > ``` | ||
|
|
||
| ### 3. Install Windows App SDK | ||
|
|
||
| Download and install the Windows App SDK matching your `wasdk` version (e.g., `1.8.260209005`): | ||
|
|
||
| ```bash | ||
| curl -L -o windowsappruntimeinstall-x86.exe "https://aka.ms/windowsappsdk/1.8/1.8.260209005/windowsappruntimeinstall-x86.exe" | ||
| windowsappruntimeinstall-x86.exe --quiet | ||
| ``` | ||
|
|
||
| ### 4. Download ONNX encoder | ||
|
|
||
| Download the encoder ONNX model from the Hugging Face repo: | ||
|
|
||
| | Model | Hugging Face repo | | ||
| |-------|-------------------| | ||
| | **Large-v3-turbo** | [amd/whisper-large-turbo-onnx-npu](https://huggingface.co/amd/whisper-large-turbo-onnx-npu/tree/main) — get `encoder_model.onnx`, `encoder_model.onnx.data` (if present) | | ||
|
|
||
| This recipe uses the pre-exported encoder ONNX from Hugging Face; it does not run Olive conversion as part of the recipe. | ||
|
|
||
| Place the encoder ONNX file(s) in your working directory (or a path you will pass to `--enc_onnx`). | ||
|
|
||
| --- | ||
|
|
||
| ## Run Inference and Transcribe | ||
|
|
||
| Use the encoder ONNX from the Hugging Face repo listed above for this recipe — set `--enc_onnx` to that model’s `encoder_model.onnx` (or your local path to it) and `--model` to the matching Whisper model name. | ||
|
|
||
| ```bash | ||
| python run_whisper.py \ | ||
| --audio ".\audio_sample.wav" \ | ||
| --model turbo \ | ||
| --enc_onnx ".\encoder_model.onnx" | ||
| ``` | ||
| --- | ||
| ## Notes | ||
| In this experiment, we are using encoder portion of the model with ORT and decoder portion of the model with Pytorch+kv cache. | ||
|
|
||
| ## Command-Line Arguments | ||
|
|
||
| --- | ||
|
|
||
| | Argument | Required | Default | Description | | ||
| |---|---|---|---| | ||
| | `--audio` | Yes | — | Path to input audio WAV file | | ||
| | `--model` | No | `turbo` | Whisper model name: `small`, `medium`, `turbo` (should match encoder ONNX) | | ||
| | `--enc_onnx` | No | `encoder_model.onnx` | Path to encoder ONNX model file | | ||
| | `--download_root` | No | Script directory | Directory to download/cache the Whisper PyTorch model | | ||
|
|
||
| --- | ||
|
|
||
| ## Credits | ||
|
|
||
| This project builds on [OpenAI Whisper](https://github.com/openai/whisper). We have borrowed code from that repository and extended it with ONNX Runtime Vitis AI EP integration, WinML execution providers, and the changes documented in this README. Whisper is licensed under the [MIT License](https://github.com/openai/whisper/blob/main/LICENSE). | ||
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| arch: whisper | ||
| recipes: | ||
| - name: openai-whisper-large-v3-turbo-vitisai | ||
| file: run_whisper.py | ||
| devices: npu | ||
| eps: VitisAIExecutionProvider |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # MIT License | ||
| # | ||
| # Copyright (c) 2022 OpenAI | ||
| # | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to deal | ||
| # in the Software without restriction, including without limitation the rights | ||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| # copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # | ||
| # The above copyright notice and this permission notice shall be included in all | ||
| # copies or substantial portions of the Software. | ||
| # | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| # SOFTWARE. | ||
|
|
||
| --extra-index-url https://pypi.org/simple | ||
| # Copyright (C) [2026] Advanced Micro Devices, Inc. All Rights Reserved. | ||
|
|
||
|
|
||
| --index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/ORT-Nightly/pypi/simple | ||
|
|
||
| numpy | ||
| onnxruntime-winml | ||
| onnxscript | ||
| scipy==1.17.1 | ||
| tiktoken==0.12.0 | ||
| torch | ||
| transformers | ||
| wasdk-Microsoft.Windows.AI.MachineLearning[all]==1.8.260209005 | ||
| wasdk-Microsoft.Windows.ApplicationModel.DynamicDependency.Bootstrap[all]==1.8.260209005 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| # MIT License | ||
| # | ||
| # Copyright (C) 2026, Advanced Micro Devices, Inc | ||
| # | ||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| # of this software and associated documentation files (the "Software"), to deal | ||
| # in the Software without restriction, including without limitation the rights | ||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| # copies of the Software, and to permit persons to whom the Software is | ||
| # furnished to do so, subject to the following conditions: | ||
| # | ||
| # The above copyright notice and this permission notice shall be included in all | ||
| # copies or substantial portions of the Software. | ||
| # | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| # SOFTWARE. | ||
|
|
||
| # Copyright (C) [2026] Advanced Micro Devices, Inc. All Rights Reserved. | ||
|
|
||
| import time | ||
| import os | ||
| import argparse | ||
| import json | ||
| import sys | ||
| import numpy as np | ||
| import whisper | ||
| import subprocess | ||
| import onnxruntime as ort | ||
| from scipy.io import wavfile | ||
| from scipy.signal import resample | ||
|
|
||
| SAMPLE_RATE = 16000 # Whisper expects 16 kHz | ||
|
|
||
| _SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) | ||
|
|
||
|
|
||
| def register_execution_providers(script_dir=None): | ||
| """Register WinML execution providers. script_dir: directory containing winml.py (default: this script's dir).""" | ||
| base = script_dir or _SCRIPT_DIR | ||
| worker_script = os.path.join(base, "winml.py") | ||
| result = subprocess.check_output([sys.executable, worker_script], text=True) | ||
| paths = json.loads(result) | ||
| for name, lib_path in paths.items(): | ||
| if not lib_path or not os.path.exists(lib_path): | ||
| continue | ||
| ort.register_execution_provider_library(name, lib_path) | ||
|
|
||
| def load_audio_no_ffmpeg(path: str) -> np.ndarray: | ||
| """Load WAV as mono 16 kHz float32 (no ffmpeg, no torchcodec). Uses scipy.""" | ||
| sr, data = wavfile.read(path) | ||
| if data.dtype == np.int16: | ||
| data = data.astype(np.float32) / 32768.0 | ||
| elif data.dtype == np.int32: | ||
| data = data.astype(np.float32) / 2147483648.0 | ||
| if data.ndim == 2: | ||
| data = data.mean(axis=1) | ||
| if sr != SAMPLE_RATE: | ||
| n = int(len(data) * SAMPLE_RATE / sr) | ||
| data = resample(data, n).astype(np.float32) | ||
| return data | ||
|
|
||
|
|
||
| def run_whisper( | ||
| audio_path: str = None, | ||
| audio: np.ndarray = None, | ||
| *, | ||
| enc_onnx: str = "encoder_model.onnx", | ||
| enc_cache_dir: str = "cacheDir", | ||
| vitisai_config: str = "vitisai_config.json", | ||
| model: str = "turbo", | ||
| download_root: str = None, | ||
| ) -> dict: | ||
| """ | ||
| Run Whisper E2E: load audio, run encoder (ONNX on NPU) + decoder, return transcription and metrics. | ||
|
|
||
| Provide either audio_path (path to WAV) or audio (float32 mono 16 kHz array). All other args are optional. | ||
|
|
||
| Returns dict with: text, detected_language, rtf, elapsed_sec, audio_duration_sec. | ||
| """ | ||
| if audio_path is None and audio is None: | ||
| raise ValueError("Provide either audio_path or audio") | ||
| if audio_path is not None and audio is not None: | ||
| raise ValueError("Provide only one of audio_path or audio") | ||
|
|
||
| if download_root is None: | ||
| download_root = _SCRIPT_DIR | ||
|
|
||
| if audio_path is not None: | ||
| audio = load_audio_no_ffmpeg(audio_path) | ||
| audio_duration_sec = min(audio.shape[0] / SAMPLE_RATE, 30.0) | ||
| audio = whisper.pad_or_trim(audio) | ||
|
|
||
| model_obj = whisper.load_model(model, download_root=download_root) | ||
| mel = whisper.log_mel_spectrogram(audio, n_mels=model_obj.dims.n_mels).to(model_obj.device) | ||
|
|
||
| _, probs = model_obj.detect_language(mel) | ||
| detected_language = max(probs, key=probs.get) | ||
|
|
||
| register_execution_providers() | ||
|
|
||
| cache_key = "encoder_model" | ||
| options = whisper.DecodingOptions( | ||
| enc_use_onnx=bool(enc_onnx), | ||
| enc_onnx_fname=enc_onnx or "", | ||
| use_winml=True, | ||
| enc_use_vitis=True, | ||
| enc_cache_dir=enc_cache_dir, | ||
| enc_cache_key=cache_key, | ||
| enc_config_json=vitisai_config, | ||
| ) | ||
|
|
||
| t0 = time.perf_counter() | ||
| result = whisper.decode(model_obj, mel, options) | ||
| elapsed_sec = time.perf_counter() - t0 | ||
| rtf = elapsed_sec / audio_duration_sec | ||
|
|
||
| return { | ||
| "text": result.text, | ||
| "detected_language": detected_language, | ||
| "rtf": rtf, | ||
| "elapsed_sec": elapsed_sec, | ||
| "audio_duration_sec": audio_duration_sec, | ||
| } | ||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser() | ||
| parser.add_argument( | ||
| "--enc_onnx", | ||
| type=str, | ||
| default="encoder_model.onnx", | ||
| help="Path to encoder ONNX model file", | ||
| ) | ||
| parser.add_argument("--audio", type=str, required=True, help="Path to input audio WAV file") | ||
| parser.add_argument( | ||
| "--download_root", | ||
| type=str, | ||
| default=None, | ||
| help="Directory to download/cache Whisper PyTorch model (default: script directory)", | ||
| ) | ||
| parser.add_argument( | ||
| "--model", | ||
| type=str, | ||
| default="turbo", | ||
| choices=["small", "medium", "turbo"], | ||
| help="Whisper model name for load_model (default: turbo)", | ||
| ) | ||
| args = parser.parse_args() | ||
|
|
||
| out = run_whisper( | ||
| audio_path=args.audio, | ||
| enc_onnx=args.enc_onnx, | ||
| model=args.model, | ||
| download_root=args.download_root, | ||
| ) | ||
| print("\n") | ||
| print("Transcription results:") | ||
| print(f"Detected language: {out['detected_language']}") | ||
| print(f"RTF: {out['rtf']:.4f} (decode: {out['elapsed_sec']:.3f}s, audio: {out['audio_duration_sec']:.3f}s)") | ||
| print(out["text"]) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| { | ||
| "passes": [ | ||
| { | ||
| "name": "init", | ||
| "plugin": "vaip-pass_init" | ||
| }, | ||
| { | ||
| "name": "vaiml_partition", | ||
| "plugin": "vaip-pass_vaiml_partition", | ||
| "vaiml_config": { | ||
| "fe_experiment": "use-accurate-mode=LayerNorm2PassAdf" | ||
| } | ||
| } | ||
| ], | ||
| "target": "VAIML", | ||
| "targets": [ | ||
| { | ||
| "name": "VAIML", | ||
| "pass": [ | ||
| "init", | ||
| "vaiml_partition" | ||
| ] | ||
| } | ||
| ] | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.