Instructions to use CatSnowdrop/universal_onnxruntime with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- TensorRT
How to use CatSnowdrop/universal_onnxruntime with TensorRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Upload 379 files
Browse files---
license: mit
tags:
- onnxruntime
- onnx
- inference
- cuda
- tensorrt
- openvino
- directml
- windows
---
# Universal ONNX Runtime (UORT) 1.30.1 — Windows x64 package folder
A self-contained build of **ONNX Runtime 1.30.1** for **Windows x64 / CPython 3.12** with five
execution providers compiled in and every native dependency bundled next to the Python
extension: **TensorRT, CUDA, OpenVINO, DirectML and CPU**.
This repository publishes the package as a **plain directory tree** — `onnxruntime/` at the repo
root, exactly as it has to appear in `Lib\site-packages`. There is no archive to unpack: what you
download is already the installable folder.
The point of the package is that you install it by copying one folder — no CUDA Toolkit, no
cuDNN download, no TensorRT SDK, no `pip install onnxruntime-gpu`. Everything the providers need
is in `capi/`, and the package registers that directory with the Windows loader on import.
| | |
|---|---|
| Contents | `onnxruntime/` — **379 files** in **31 subdirectories** |
| Stored / transferred size | **5 585 491 685 bytes** (5.20 GiB) |
| Python modules | 317 `.py` files |
| Native binaries | 52 `.dll` + 1 `.pyd`, all in `capi/` |
| `__pycache__` | none (removed before publishing) |
| Package version | `uort-python` 1.30.1 |
| Platform | Windows 10/11 x64 only |
| Python ABI | CPython 3.12 x64 (`python312.dll`) |
| Licence | MIT (ONNX Runtime) + third-party terms, see [Licences](#licences) |
## Integrity
There is no single archive hash, because there is no archive. Every file is stored as an
LFS / Xet object keyed by the **SHA-256 of its content**, and both `hf download` and
`git lfs` verify that hash while writing the file — a corrupted transfer is rejected, not
silently accepted.
Spot-check values if you want to confirm a few key binaries yourself:
| File | Bytes | SHA-256 |
|---|---|---|
| `capi\onnxruntime.dll` | 24 535 040 | `a4599b0168c8b3e485e50c4493559280778f7c533d66bbb3ea372073f316b67f` |
| `capi\onnxruntime_pybind11_state.pyd` | 25 851 904 | `e54c4900fc60c521dc597dc0b0c87835a18ea6ee2efd38af78c70ac6f10abcbb` |
| `capi\onnxruntime_providers_cuda.dll` | 689 375 232 | `2c744e0d1683518fa88e229ca006304aad6a29b81b9f0229596969c89ba80c12` |
| `capi\nvinfer_builder_resource_10.dll` | 1 971 797 024 | `2b8496c57a8434534e8a3c56e732207f1c74bc37ce7300d8f0d3542cdff02e12` |
```powershell
(Get-FileHash .\onnxruntime\capi\onnxruntime.dll -Algorithm SHA256).Hash.ToLower()
```
## Download
Grab the whole folder — 379 separate HTTP requests by hand is not a good idea.
```powershell
# Hugging Face CLI: chunked, parallel, resumable
hf download CatSnowdrop/universal_onnxruntime --local-dir .\uort
# result: .\uort\onnxruntime\__init__.py, .\uort\onnxruntime\capi\...
# or git (needs git-lfs; Xet accelerates it if hf_xet is installed)
git clone https://huggingface.co/CatSnowdrop/universal_onnxruntime uort
```
```python
from huggingface_hub import snapshot_download
path = snapshot_download(
repo_id="CatSnowdrop/universal_onnxruntime",
local_dir="uort",
max_workers=8,
)
print(path) # -> uort\onnxruntime
```
Single files can be fetched directly; the endpoint supports HTTP `Range`, so a large `.dll`
resumes instead of restarting:
```powershell
curl.exe -L -C - -o onnxruntime_providers_cuda.dll `
"https://huggingface.co/CatSnowdrop/universal_onnxruntime/resolve/main/onnxruntime/capi/onnxruntime_providers_cuda.dll"
```
`resolve/main` redirects to a commit-pinned CDN path, so re-uploading never invalidates a
download that is already running.
> **Expect ~5.20 GiB of traffic.** Unlike a zip, DLLs are stored uncompressed, so the folder is
> ~1.5× larger on the wire than the equivalent archive (3.48 GiB). In exchange there is no
> unpack step and no second copy on disk: peak usage is 5.20 GiB, not 8.8 GiB.
## What's inside
### Execution providers
`onnxruntime.get_available_providers()` reports, in this order:
```python
['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'OpenVINOExecutionProvider',
'DmlExecutionProvider', 'CPUExecutionProvider']
```
| EP | Bundled runtime | Notes |
|---|---|---|
| `TensorrtExecutionProvider` | TensorRT 10.9, `nvonnxparser`, `nvinfer_dispatch` | engines are built at session creation; requires compute capability ≥ 7.5 |
| `CUDAExecutionProvider` | CUDA 12.9 runtime, cuBLAS, cuFFT, cuRAND, cuDNN 9 | driver is the only system prerequisite |
| `OpenVINOExecutionProvider` | OpenVINO 2026.2.1 + Intel CPU/GPU/NPU plugins, TBB | CPU, GPU and NPU devices |
| `DmlExecutionProvider` | `DirectML.dll` 17.67 MB | any DirectX 12 GPU (NVIDIA/AMD/Intel) |
| `CPUExecutionProvider` | MLAS kernels inside `onnxruntime.dll` | always available |
GPU kernels are compiled for `sm_61;75;80;86;89;90;100;120`. Note that the **TensorRT** EP,
unlike CUDA, does not support 6.1: TensorRT 10 dropped Pascal, so a P10x/GTX 10x0 card can use
CUDA or DirectML but not TensorRT.
### Why the package is 5.2 GiB
Almost all of the volume is the CUDA / TensorRT / OpenVINO runtimes; the Python side is ~4 MiB.
| Group | Main files | Size |
|---|---|---|
| TensorRT | `nvinfer_builder_resource_10.dll` (1880 MB), `nvinfer_10.dll` (421 MB), `nvinfer_plugin_10.dll` (49 MB) | ~2.30 GiB |
| CUDA | `onnxruntime_providers_cuda.dll` (657 MB), `cublasLt64_12.dll` (638 MB), `cudnn_engines_precompiled64_9.dll` (490 MB), `cufft64_11.dll` (274 MB), `cudnn_adv64_9.dll` (269 MB), `cudnn_ops64_9.dll` (121 MB) | ~2.64 GiB |
| OpenVINO | `openvino.dll`, `openvino_intel_{cpu,gpu,npu}_plugin.dll`, `openvino_intel_npu_compiler.dll`, frontends, TBB | ~200 MiB |
| Core | `onnxruntime.dll` (23 MB), `onnxruntime_pybind11_state.pyd` (25 MB), `onnxruntime_providers_shared.dll`, Python modules | ~50 MiB |
| DirectML + MSVC runtime | `DirectML.dll` (18 MB), `msvcp140*`, `vcruntime140*` | ~19 MiB |
`nvinfer_builder_resource_10.dll` (1.88 GiB of precompiled CUDA kernels) is loaded **only** when
a TensorRT engine has to be built from scratch. Do not delete it unless you never use
`TensorrtExecutionProvider`: `nvinfer_10.dll` loads fine without it, but engine construction
fails at runtime.
### Layout
The tree below is the repository tree; drop the `onnxruntime/` prefix and it is a valid
site-packages package.
```
onnxruntime/
├── __init__.py # public API: InferenceSession, get_available_providers, ...
├── uort.py # create_session(): resilient provider fallback helper
├── LICENSE Privacy.md ThirdPartyNotices.txt
├── capi/ # 63 files, 5.20 GiB — everything native
│ ├── onnxruntime.dll # core runtime (C API; also usable via ctypes)
│ ├── onnxruntime_pybind11_state.pyd
│ ├── onnxruntime_providers_{cuda,tensorrt,openvino,shared}.dll
│ ├── nvinfer* / nvonnxparser_10.dll
│ ├── cublas* / cudnn* / cufft* / curand* / cudart64_12.dll
│ ├── openvino*.dll / tbb*.dll / DirectML.dll / msvcp140*.dll / vcruntime140*.dll
│ ├── _uort_dll_dirs.py # registers capi/ with AddDllDirectory + PATH on import
│ ├── _pybind_state.py # patched to call _uort_dll_dirs before loading the extension
│ ├── build_and_package_info.py
│ └── version_info.py
├── backend/ 3 files # ORT EP unit-test harness
├── datasets/ 4 files # three tiny .onnx models used by those tests
├── quantization/ 59 files # Python tooling
├── tools/ 80 files # Python tooling
└── transformers/ 165 files # HF transformers integration layer
```
No C/C++ headers are included — for native code use the `onnxruntime.dll` C API together with
the public ORT 1.30.1 headers.
## Requirements
- **Windows 10/11 x64.**
- **CPython 3.12 x64.** The extension links `python312.dll`; 3.11/3.13 raise a DLL load error.
- **No NVIDIA/Intel SDKs are needed** — CUDA, cuDNN, TensorRT, OpenVINO and the MSVC runtime are
all in `capi/`.
- The **GPU driver stays a system prerequisite** (it is not, and cannot be, bundled):
- CUDA EP: NVIDIA driver with CUDA 12.x support (≥ 527.41; 551+ recommended for CUDA 12.9).
- TensorRT EP: the same driver **and** compute capability ≥ 7.5.
- DirectML EP: any DX12-capable GPU with a WDDM 2.x driver.
- OpenVINO EP: Intel CPU, or Intel GPU/NPU with a recent graphics driver.
- For `import onnxruntime` from a normal Python process nothing else is required; the package
calls `os.add_dll_directory(capi)` and prepends it to `PATH` itself.
- If you load `capi/onnxruntime.dll` from a **non-Python** host, add `capi` to `PATH` (or use
`SetDllDirectoryW`) before loading it.
## Installation
The folder is **not a pip wheel** (there is no `*.dist-info`), so `pip` neither sees nor
uninstalls it. Copy the folder.
### Option A — into a Python environment
```powershell
# the download landed in .\uort\onnxruntime
$sp = python -c "import sysconfig; print(sysconfig.get_paths()['purelib'])"
Remove-Item "$sp\onnxruntime" -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item .\uort\onnxruntime "$sp\onnxruntime" -Recurse -Force
# make sure no other ONNX Runtime is installed next to it
python -m pip uninstall -y onnxruntime onnxruntime-gpu onnxruntime-directml onnxruntime-openvino
```
### Option B — into an application folder
Copy `onnxruntime\` next to your `python.exe` / `python312.dll`, or into
`<app>\_internal\python\Lib\site-packages\onnxruntime`, keeping the directory name exactly
`onnxruntime`. This is how the folder is used by the DeepFaceLive build it was made for.
### Verify
```python
import onnxruntime as ort
print(ort.__version__) # 1.30.1
print(ort.get_available_providers()) # all five providers (compile-time list)
print(ort.__file__) # ...\site-packages\onnxruntime\__init__.py
```
Then confirm that a provider really works on this machine by building a session — see below.
`get_available_providers()` lists what the **build** contains; whether a device can actually be
used depends on the driver, the GPU and the model.
## Usage
### CPU
```python
import onnxruntime as ort
sess = ort.I
- .gitattributes +46 -0
- onnxruntime/LICENSE +21 -0
- onnxruntime/Privacy.md +34 -0
- onnxruntime/ThirdPartyNotices.txt +0 -0
- onnxruntime/__init__.py +500 -0
- onnxruntime/backend/__init__.py +6 -0
- onnxruntime/backend/backend.py +214 -0
- onnxruntime/backend/backend_rep.py +76 -0
- onnxruntime/capi/DirectML.dll +3 -0
- onnxruntime/capi/__init__.py +4 -0
- onnxruntime/capi/_ld_preload.py +7 -0
- onnxruntime/capi/_pybind_state.py +43 -0
- onnxruntime/capi/_uort_dll_dirs.py +32 -0
- onnxruntime/capi/build_and_package_info.py +3 -0
- onnxruntime/capi/concrt140.dll +3 -0
- onnxruntime/capi/convert_npz_to_onnx_adapter.py +48 -0
- onnxruntime/capi/cublas64_12.dll +3 -0
- onnxruntime/capi/cublasLt64_12.dll +3 -0
- onnxruntime/capi/cudart64_12.dll +3 -0
- onnxruntime/capi/cudnn64_9.dll +3 -0
- onnxruntime/capi/cudnn_adv64_9.dll +3 -0
- onnxruntime/capi/cudnn_cnn64_9.dll +3 -0
- onnxruntime/capi/cudnn_engines_precompiled64_9.dll +3 -0
- onnxruntime/capi/cudnn_engines_runtime_compiled64_9.dll +3 -0
- onnxruntime/capi/cudnn_graph64_9.dll +3 -0
- onnxruntime/capi/cudnn_heuristic64_9.dll +3 -0
- onnxruntime/capi/cudnn_ops64_9.dll +3 -0
- onnxruntime/capi/cufft64_11.dll +3 -0
- onnxruntime/capi/curand64_10.dll +3 -0
- onnxruntime/capi/msvcp140.dll +3 -0
- onnxruntime/capi/msvcp140_1.dll +0 -0
- onnxruntime/capi/msvcp140_2.dll +3 -0
- onnxruntime/capi/msvcp140_atomic_wait.dll +0 -0
- onnxruntime/capi/msvcp140_codecvt_ids.dll +0 -0
- onnxruntime/capi/nvinfer_10.dll +3 -0
- onnxruntime/capi/nvinfer_builder_resource_10.dll +3 -0
- onnxruntime/capi/nvinfer_dispatch_10.dll +3 -0
- onnxruntime/capi/nvinfer_plugin_10.dll +3 -0
- onnxruntime/capi/nvonnxparser_10.dll +3 -0
- onnxruntime/capi/onnxruntime.dll +3 -0
- onnxruntime/capi/onnxruntime_collect_build_info.py +47 -0
- onnxruntime/capi/onnxruntime_inference_collection.py +1930 -0
- onnxruntime/capi/onnxruntime_providers_cuda.dll +3 -0
- onnxruntime/capi/onnxruntime_providers_openvino.dll +3 -0
- onnxruntime/capi/onnxruntime_providers_shared.dll +0 -0
- onnxruntime/capi/onnxruntime_providers_tensorrt.dll +3 -0
- onnxruntime/capi/onnxruntime_pybind11_state.pyd +3 -0
- onnxruntime/capi/onnxruntime_validation.py +154 -0
- onnxruntime/capi/openvino.dll +3 -0
- onnxruntime/capi/openvino_auto_batch_plugin.dll +3 -0
|
@@ -33,3 +33,49 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
onnxruntime/capi/concrt140.dll filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
onnxruntime/capi/cublas64_12.dll filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
onnxruntime/capi/cublasLt64_12.dll filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
onnxruntime/capi/cudart64_12.dll filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
onnxruntime/capi/cudnn_adv64_9.dll filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
onnxruntime/capi/cudnn_cnn64_9.dll filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
onnxruntime/capi/cudnn_engines_precompiled64_9.dll filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
onnxruntime/capi/cudnn_engines_runtime_compiled64_9.dll filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
onnxruntime/capi/cudnn_graph64_9.dll filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
onnxruntime/capi/cudnn_heuristic64_9.dll filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
onnxruntime/capi/cudnn_ops64_9.dll filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
onnxruntime/capi/cudnn64_9.dll filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
onnxruntime/capi/cufft64_11.dll filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
onnxruntime/capi/curand64_10.dll filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
onnxruntime/capi/DirectML.dll filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
onnxruntime/capi/msvcp140_2.dll filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
onnxruntime/capi/msvcp140.dll filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
onnxruntime/capi/nvinfer_10.dll filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
onnxruntime/capi/nvinfer_builder_resource_10.dll filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
onnxruntime/capi/nvinfer_dispatch_10.dll filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
onnxruntime/capi/nvinfer_plugin_10.dll filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
onnxruntime/capi/nvonnxparser_10.dll filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
onnxruntime/capi/onnxruntime_providers_cuda.dll filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
onnxruntime/capi/onnxruntime_providers_openvino.dll filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
onnxruntime/capi/onnxruntime_providers_tensorrt.dll filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
onnxruntime/capi/onnxruntime_pybind11_state.pyd filter=lfs diff=lfs merge=lfs -text
|
| 62 |
+
onnxruntime/capi/onnxruntime.dll filter=lfs diff=lfs merge=lfs -text
|
| 63 |
+
onnxruntime/capi/openvino_auto_batch_plugin.dll filter=lfs diff=lfs merge=lfs -text
|
| 64 |
+
onnxruntime/capi/openvino_auto_plugin.dll filter=lfs diff=lfs merge=lfs -text
|
| 65 |
+
onnxruntime/capi/openvino_c.dll filter=lfs diff=lfs merge=lfs -text
|
| 66 |
+
onnxruntime/capi/openvino_hetero_plugin.dll filter=lfs diff=lfs merge=lfs -text
|
| 67 |
+
onnxruntime/capi/openvino_intel_cpu_plugin.dll filter=lfs diff=lfs merge=lfs -text
|
| 68 |
+
onnxruntime/capi/openvino_intel_gpu_plugin.dll filter=lfs diff=lfs merge=lfs -text
|
| 69 |
+
onnxruntime/capi/openvino_intel_npu_compiler_loader.dll filter=lfs diff=lfs merge=lfs -text
|
| 70 |
+
onnxruntime/capi/openvino_intel_npu_compiler.dll filter=lfs diff=lfs merge=lfs -text
|
| 71 |
+
onnxruntime/capi/openvino_intel_npu_plugin.dll filter=lfs diff=lfs merge=lfs -text
|
| 72 |
+
onnxruntime/capi/openvino_ir_frontend.dll filter=lfs diff=lfs merge=lfs -text
|
| 73 |
+
onnxruntime/capi/openvino_onnx_frontend.dll filter=lfs diff=lfs merge=lfs -text
|
| 74 |
+
onnxruntime/capi/openvino_paddle_frontend.dll filter=lfs diff=lfs merge=lfs -text
|
| 75 |
+
onnxruntime/capi/openvino_pytorch_frontend.dll filter=lfs diff=lfs merge=lfs -text
|
| 76 |
+
onnxruntime/capi/openvino_tensorflow_frontend.dll filter=lfs diff=lfs merge=lfs -text
|
| 77 |
+
onnxruntime/capi/openvino_tensorflow_lite_frontend.dll filter=lfs diff=lfs merge=lfs -text
|
| 78 |
+
onnxruntime/capi/openvino.dll filter=lfs diff=lfs merge=lfs -text
|
| 79 |
+
onnxruntime/capi/tbb12.dll filter=lfs diff=lfs merge=lfs -text
|
| 80 |
+
onnxruntime/capi/tbbmalloc.dll filter=lfs diff=lfs merge=lfs -text
|
| 81 |
+
onnxruntime/capi/vcruntime140.dll filter=lfs diff=lfs merge=lfs -text
|
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) Microsoft Corporation
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Privacy
|
| 2 |
+
|
| 3 |
+
## Data Collection
|
| 4 |
+
The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft's privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
|
| 5 |
+
|
| 6 |
+
***
|
| 7 |
+
|
| 8 |
+
### Official Builds
|
| 9 |
+
ONNX Runtime collects trace events with the goal of improving product quality. On Windows, it uses the platform's built-in ETW telemetry system; on supported Linux architectures, macOS, Android, and iOS, it uses the cross-platform 1DS telemetry SDK that is built into ONNX Runtime. Targets without a supported telemetry provider, including WebAssembly, tvOS, visionOS, Mac Catalyst, AIX, and RISC-V, do not include telemetry. In all cases, collection is subject to user consent and handled following Microsoft's privacy practices.
|
| 10 |
+
|
| 11 |
+
Telemetry is turned **ON** by default in the official builds ([see here](../README.md#binaries)). Both providers are accessed through ONNX Runtime's common telemetry interface (see [telemetry.h](../onnxruntime/core/platform/telemetry.h)).
|
| 12 |
+
|
| 13 |
+
### Private Builds
|
| 14 |
+
The build driver enables telemetry by default for supported native platforms. The standard Windows `build.bat` wrapper explicitly passes `--no_telemetry`, so private builds made with that wrapper perform no data collection. Targets without a supported provider and builds that disable C++ exceptions automatically exclude telemetry. For information on how to disable telemetry in other builds, see [Disabling Telemetry](#disabling-telemetry) below.
|
| 15 |
+
|
| 16 |
+
#### Technical Details
|
| 17 |
+
|
| 18 |
+
**Windows.** The Windows provider uses the [TraceLogging](https://docs.microsoft.com/en-us/windows/win32/tracelogging/trace-logging-about) API for its implementation. This enables ONNX Runtime trace events to be collected by the operating system, and based on user consent, this data may be periodically sent to Microsoft servers following GDPR and privacy regulations for anonymity and data access controls. Windows ML and ONNX Runtime C APIs allow Trace Logging to be turned on/off (see [API pages](../README.md#api-documentation) for details); there are equivalent APIs in the C#, Python, and Java language bindings as well.
|
| 19 |
+
|
| 20 |
+
**Non-Windows (Linux, macOS, Android, iOS).** These platforms use the cross-platform 1DS SDK (cpp_client_telemetry) to send the same trace events to Microsoft's telemetry backend over HTTPS. Based on user consent, this data is handled following GDPR and privacy regulations for anonymity and data access controls. ONNX Runtime C APIs allow 1DS to be turned on/off (see [API pages](../README.md#api-documentation) for details); there are equivalent APIs in the C#, Python, and Java language bindings as well.
|
| 21 |
+
|
| 22 |
+
For ways to disable telemetry, see the [Disabling Telemetry](#disabling-telemetry) section below.
|
| 23 |
+
|
| 24 |
+
### Disabling Telemetry
|
| 25 |
+
|
| 26 |
+
Telemetry can be disabled in any of these ways:
|
| 27 |
+
|
| 28 |
+
- **Disable it at build time.** Pass `--no_telemetry` to `build.py` or `build.sh`. This omits the 1DS provider from non-Windows builds and disables the Microsoft telemetry configuration on Windows. The standard Windows `build.bat` wrapper does this automatically. Unsupported targets and exception-free builds never include telemetry.
|
| 29 |
+
- **Disable all telemetry at runtime (non-Windows).** Set `ORT_DISABLE_TELEMETRY=1` before ONNX Runtime initializes. This prevents the uploader, events, and persistent device identifier from being created for the process lifetime.
|
| 30 |
+
- **Disable non-essential events via the API.** The C API (and the C#, Python, and Java bindings) can suppress non-essential telemetry. ONNX Runtime may already have emitted a minimal initialization event before the API can be called. On **Windows**, ETW events are recorded only when an external trace session is collecting.
|
| 31 |
+
|
| 32 |
+
Telemetry-enabled static Linux builds use static curl and mbedTLS. FetchContent packages include those archives;
|
| 33 |
+
vcpkg builds resolve them from the same vcpkg installation. A static consumer that links another curl or mbedTLS
|
| 34 |
+
copy into the same final binary must build ORT with `--no_telemetry` to avoid ordinary static-symbol collisions.
|
|
The diff for this file is too large to render.
See raw diff
|
|
|
|
@@ -0,0 +1,500 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -------------------------------------------------------------------------
|
| 2 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 3 |
+
# Licensed under the MIT License.
|
| 4 |
+
# --------------------------------------------------------------------------
|
| 5 |
+
"""
|
| 6 |
+
ONNX Runtime is a performance-focused scoring engine for Open Neural Network Exchange (ONNX) models.
|
| 7 |
+
For more information on ONNX Runtime, please see `aka.ms/onnxruntime <https://aka.ms/onnxruntime/>`_
|
| 8 |
+
or the `Github project <https://github.com/microsoft/onnxruntime/>`_.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
import contextlib
|
| 12 |
+
|
| 13 |
+
__version__ = "1.30.1"
|
| 14 |
+
__author__ = "Microsoft"
|
| 15 |
+
|
| 16 |
+
# we need to do device version validation (for example to check Cuda version for an onnxruntime-training package).
|
| 17 |
+
# in order to know whether the onnxruntime package is for training it needs
|
| 18 |
+
# to do import onnxruntime.training.ortmodule first.
|
| 19 |
+
# onnxruntime.capi._pybind_state is required before import onnxruntime.training.ortmodule.
|
| 20 |
+
# however, import onnxruntime.capi._pybind_state will already raise an exception if a required Cuda version
|
| 21 |
+
# is not found.
|
| 22 |
+
# here we need to save the exception and continue with Cuda version validation in order to post
|
| 23 |
+
# meaningful messages to the user.
|
| 24 |
+
# the saved exception is raised after device version validation.
|
| 25 |
+
try:
|
| 26 |
+
from onnxruntime.capi._pybind_state import (
|
| 27 |
+
ExecutionMode, # noqa: F401
|
| 28 |
+
ExecutionOrder, # noqa: F401
|
| 29 |
+
GraphOptimizationLevel, # noqa: F401
|
| 30 |
+
LoraAdapter, # noqa: F401
|
| 31 |
+
ModelMetadata, # noqa: F401
|
| 32 |
+
NodeArg, # noqa: F401
|
| 33 |
+
OrtAllocatorType, # noqa: F401
|
| 34 |
+
OrtArenaCfg, # noqa: F401
|
| 35 |
+
OrtCompileApiFlags, # noqa: F401
|
| 36 |
+
OrtDeviceMemoryType, # noqa: F401
|
| 37 |
+
OrtEpAssignedNode, # noqa: F401
|
| 38 |
+
OrtEpAssignedSubgraph, # noqa: F401
|
| 39 |
+
OrtEpDevice, # noqa: F401
|
| 40 |
+
OrtExecutionProviderDevicePolicy, # noqa: F401
|
| 41 |
+
OrtExternalInitializerInfo, # noqa: F401
|
| 42 |
+
OrtHardwareDevice, # noqa: F401
|
| 43 |
+
OrtHardwareDeviceType, # noqa: F401
|
| 44 |
+
OrtMemoryInfo, # noqa: F401
|
| 45 |
+
OrtMemoryInfoDeviceType, # noqa: F401
|
| 46 |
+
OrtMemType, # noqa: F401
|
| 47 |
+
OrtSparseFormat, # noqa: F401
|
| 48 |
+
OrtSyncStream, # noqa: F401
|
| 49 |
+
RunOptions, # noqa: F401
|
| 50 |
+
SessionIOBinding, # noqa: F401
|
| 51 |
+
SessionOptions, # noqa: F401
|
| 52 |
+
create_and_register_allocator, # noqa: F401
|
| 53 |
+
create_and_register_allocator_v2, # noqa: F401
|
| 54 |
+
disable_telemetry_events, # noqa: F401
|
| 55 |
+
enable_telemetry_events, # noqa: F401
|
| 56 |
+
get_all_providers, # noqa: F401
|
| 57 |
+
get_available_providers, # noqa: F401
|
| 58 |
+
get_build_info,
|
| 59 |
+
get_device, # noqa: F401
|
| 60 |
+
get_ep_devices, # noqa: F401
|
| 61 |
+
get_version_string, # noqa: F401
|
| 62 |
+
has_collective_ops, # noqa: F401
|
| 63 |
+
register_execution_provider_library,
|
| 64 |
+
set_default_logger_severity, # noqa: F401
|
| 65 |
+
set_default_logger_verbosity, # noqa: F401
|
| 66 |
+
set_global_thread_pool_sizes, # noqa: F401
|
| 67 |
+
set_seed, # noqa: F401
|
| 68 |
+
unregister_execution_provider_library, # noqa: F401
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
import_capi_exception = None
|
| 72 |
+
except Exception as e:
|
| 73 |
+
import_capi_exception = e
|
| 74 |
+
|
| 75 |
+
from onnxruntime.capi import onnxruntime_validation
|
| 76 |
+
|
| 77 |
+
if import_capi_exception:
|
| 78 |
+
raise import_capi_exception
|
| 79 |
+
|
| 80 |
+
from onnxruntime.capi.onnxruntime_inference_collection import (
|
| 81 |
+
AdapterFormat, # noqa: F401
|
| 82 |
+
InferenceSession, # noqa: F401
|
| 83 |
+
IOBinding, # noqa: F401
|
| 84 |
+
ModelCompiler, # noqa: F401
|
| 85 |
+
OrtDevice, # noqa: F401
|
| 86 |
+
OrtDeviceVendorId, # noqa: F401
|
| 87 |
+
OrtValue, # noqa: F401
|
| 88 |
+
SparseTensor, # noqa: F401
|
| 89 |
+
copy_tensors, # noqa: F401
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
# TODO: thiagofc: Temporary experimental namespace for new PyTorch front-end
|
| 93 |
+
try: # noqa: SIM105
|
| 94 |
+
from . import experimental # noqa: F401
|
| 95 |
+
except ImportError:
|
| 96 |
+
pass
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
package_name, version, cuda_version = onnxruntime_validation.get_package_name_and_version_info()
|
| 100 |
+
|
| 101 |
+
if version:
|
| 102 |
+
__version__ = version
|
| 103 |
+
|
| 104 |
+
onnxruntime_validation.check_distro_info()
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def _get_cuda_plugin_ep_library_path() -> str | None:
|
| 108 |
+
import os # noqa: PLC0415
|
| 109 |
+
import sys # noqa: PLC0415
|
| 110 |
+
|
| 111 |
+
if ", cuda-plugin-ep=" not in get_build_info():
|
| 112 |
+
return None
|
| 113 |
+
|
| 114 |
+
if sys.platform == "win32":
|
| 115 |
+
library_name = "onnxruntime_providers_cuda.dll"
|
| 116 |
+
elif sys.platform == "darwin":
|
| 117 |
+
library_name = "libonnxruntime_providers_cuda.dylib"
|
| 118 |
+
else:
|
| 119 |
+
library_name = "libonnxruntime_providers_cuda.so"
|
| 120 |
+
|
| 121 |
+
library_path = os.path.join(os.path.dirname(__file__), "capi", library_name)
|
| 122 |
+
return library_path if os.path.isfile(library_path) else None
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def _register_bundled_cuda_plugin_ep(warn_on_failure: bool = False):
|
| 126 |
+
library_path = _get_cuda_plugin_ep_library_path()
|
| 127 |
+
if not library_path:
|
| 128 |
+
return
|
| 129 |
+
|
| 130 |
+
try:
|
| 131 |
+
register_execution_provider_library("CUDAExecutionProvider", library_path)
|
| 132 |
+
except Exception as e:
|
| 133 |
+
if "already registered" not in str(e).lower():
|
| 134 |
+
if warn_on_failure:
|
| 135 |
+
import warnings # noqa: PLC0415
|
| 136 |
+
|
| 137 |
+
warnings.warn(f"Failed to register bundled CUDA plugin EP from {library_path}: {e}")
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
_register_bundled_cuda_plugin_ep(warn_on_failure=True)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def _get_package_version(package_name: str):
|
| 144 |
+
from importlib.metadata import PackageNotFoundError, version # noqa: PLC0415
|
| 145 |
+
|
| 146 |
+
try:
|
| 147 |
+
package_version = version(package_name)
|
| 148 |
+
except PackageNotFoundError:
|
| 149 |
+
package_version = None
|
| 150 |
+
return package_version
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def _get_package_root(package_name: str, directory_name: str | None = None):
|
| 154 |
+
from importlib.metadata import PackageNotFoundError, distribution # noqa: PLC0415
|
| 155 |
+
|
| 156 |
+
root_directory_name = directory_name or package_name
|
| 157 |
+
try:
|
| 158 |
+
dist = distribution(package_name)
|
| 159 |
+
files = dist.files or []
|
| 160 |
+
|
| 161 |
+
for file in files:
|
| 162 |
+
if file.name.endswith("__init__.py") and root_directory_name in file.parts:
|
| 163 |
+
return file.locate().parent
|
| 164 |
+
|
| 165 |
+
# Fallback to the first __init__.py
|
| 166 |
+
if not directory_name:
|
| 167 |
+
for file in files:
|
| 168 |
+
if file.name.endswith("__init__.py"):
|
| 169 |
+
return file.locate().parent
|
| 170 |
+
except PackageNotFoundError:
|
| 171 |
+
# package not found, do nothing
|
| 172 |
+
pass
|
| 173 |
+
|
| 174 |
+
return None
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def _extract_cuda_major_version(version_str: str) -> str:
|
| 178 |
+
"""Extract CUDA major version from version string (e.g., '12.1' -> '12').
|
| 179 |
+
|
| 180 |
+
Args:
|
| 181 |
+
version_str: CUDA version string to parse
|
| 182 |
+
|
| 183 |
+
Returns:
|
| 184 |
+
Major version as string, or "12" if parsing fails
|
| 185 |
+
"""
|
| 186 |
+
return version_str.split(".", maxsplit=1)[0] if version_str else "12"
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def _get_cufft_version(cuda_major_version: str) -> str:
|
| 190 |
+
"""Get cufft library version based on CUDA major version.
|
| 191 |
+
|
| 192 |
+
Args:
|
| 193 |
+
cuda_major_version: CUDA major version as string (e.g., "12", "13")
|
| 194 |
+
|
| 195 |
+
Returns:
|
| 196 |
+
cufft version as string
|
| 197 |
+
"""
|
| 198 |
+
# cufft versions: CUDA 12.x -> 11, CUDA 13.x -> 12
|
| 199 |
+
return "12" if int(cuda_major_version) >= 13 else "11"
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def _get_nvidia_dll_paths(is_windows: bool, cuda: bool = True, cudnn: bool = True):
|
| 203 |
+
# Dynamically determine CUDA major version from build info.
|
| 204 |
+
# build_cuda_version defaults to the version this package was built with; it is a parameter for testability.
|
| 205 |
+
cuda_major_version = _extract_cuda_major_version(cuda_version)
|
| 206 |
+
cufft_version = _get_cufft_version(cuda_major_version)
|
| 207 |
+
|
| 208 |
+
# Starting with CUDA 13, NVIDIA consolidated the per-component CUDA Toolkit wheels
|
| 209 |
+
# (cublas, cufft, cuda_runtime, cuda_nvrtc, curand, ...) into a single "nvidia/cu{major}"
|
| 210 |
+
# package and dropped the "-cuNN" suffix from those package names. On Windows the DLLs
|
| 211 |
+
# moved into an architecture sub-folder ("bin/<arch>", e.g. "bin/x86_64"); on Linux the
|
| 212 |
+
# libraries are placed directly in "lib" (the wheel itself is architecture specific, so
|
| 213 |
+
# there is no arch sub-folder). cuDNN keeps its own "nvidia/cudnn" package and layout.
|
| 214 |
+
use_consolidated_layout = cuda_major_version.isdigit() and int(cuda_major_version) >= 13
|
| 215 |
+
|
| 216 |
+
if use_consolidated_layout:
|
| 217 |
+
cuda_dir = f"cu{cuda_major_version}"
|
| 218 |
+
if is_windows:
|
| 219 |
+
import platform # noqa: PLC0415
|
| 220 |
+
|
| 221 |
+
arch = "arm64" if platform.machine().lower() in ("arm64", "aarch64") else "x86_64"
|
| 222 |
+
cuda_dll_paths = [
|
| 223 |
+
("nvidia", cuda_dir, "bin", arch, f"cublasLt64_{cuda_major_version}.dll"),
|
| 224 |
+
("nvidia", cuda_dir, "bin", arch, f"cublas64_{cuda_major_version}.dll"),
|
| 225 |
+
("nvidia", cuda_dir, "bin", arch, f"cufft64_{cufft_version}.dll"),
|
| 226 |
+
("nvidia", cuda_dir, "bin", arch, f"cudart64_{cuda_major_version}.dll"),
|
| 227 |
+
]
|
| 228 |
+
else: # Linux
|
| 229 |
+
# cublas64 depends on cublasLt64, so cublasLt64 should be loaded first.
|
| 230 |
+
cuda_dll_paths = [
|
| 231 |
+
("nvidia", cuda_dir, "lib", f"libcublasLt.so.{cuda_major_version}"),
|
| 232 |
+
("nvidia", cuda_dir, "lib", f"libcublas.so.{cuda_major_version}"),
|
| 233 |
+
("nvidia", cuda_dir, "lib", f"libnvrtc.so.{cuda_major_version}"),
|
| 234 |
+
("nvidia", cuda_dir, "lib", "libcurand.so.10"),
|
| 235 |
+
("nvidia", cuda_dir, "lib", f"libcufft.so.{cufft_version}"),
|
| 236 |
+
("nvidia", cuda_dir, "lib", f"libcudart.so.{cuda_major_version}"),
|
| 237 |
+
]
|
| 238 |
+
elif is_windows:
|
| 239 |
+
# CUDA 12 and earlier: each component ships its own "nvidia/<component>" package.
|
| 240 |
+
# Path is relative to site-packages directory.
|
| 241 |
+
cuda_dll_paths = [
|
| 242 |
+
("nvidia", "cublas", "bin", f"cublasLt64_{cuda_major_version}.dll"),
|
| 243 |
+
("nvidia", "cublas", "bin", f"cublas64_{cuda_major_version}.dll"),
|
| 244 |
+
("nvidia", "cufft", "bin", f"cufft64_{cufft_version}.dll"),
|
| 245 |
+
("nvidia", "cuda_runtime", "bin", f"cudart64_{cuda_major_version}.dll"),
|
| 246 |
+
]
|
| 247 |
+
else: # Linux
|
| 248 |
+
# cublas64 depends on cublasLt64, so cublasLt64 should be loaded first.
|
| 249 |
+
cuda_dll_paths = [
|
| 250 |
+
("nvidia", "cublas", "lib", f"libcublasLt.so.{cuda_major_version}"),
|
| 251 |
+
("nvidia", "cublas", "lib", f"libcublas.so.{cuda_major_version}"),
|
| 252 |
+
("nvidia", "cuda_nvrtc", "lib", f"libnvrtc.so.{cuda_major_version}"),
|
| 253 |
+
("nvidia", "curand", "lib", "libcurand.so.10"),
|
| 254 |
+
("nvidia", "cufft", "lib", f"libcufft.so.{cufft_version}"),
|
| 255 |
+
("nvidia", "cuda_runtime", "lib", f"libcudart.so.{cuda_major_version}"),
|
| 256 |
+
]
|
| 257 |
+
|
| 258 |
+
# cuDNN keeps its own "nvidia/cudnn" package layout in both old and consolidated schemes.
|
| 259 |
+
if is_windows:
|
| 260 |
+
cudnn_dll_paths = [
|
| 261 |
+
("nvidia", "cudnn", "bin", "cudnn_engines_runtime_compiled64_9.dll"),
|
| 262 |
+
("nvidia", "cudnn", "bin", "cudnn_engines_precompiled64_9.dll"),
|
| 263 |
+
("nvidia", "cudnn", "bin", "cudnn_heuristic64_9.dll"),
|
| 264 |
+
("nvidia", "cudnn", "bin", "cudnn_ops64_9.dll"),
|
| 265 |
+
("nvidia", "cudnn", "bin", "cudnn_adv64_9.dll"),
|
| 266 |
+
("nvidia", "cudnn", "bin", "cudnn_graph64_9.dll"),
|
| 267 |
+
("nvidia", "cudnn", "bin", "cudnn64_9.dll"),
|
| 268 |
+
("nvidia", "cudnn", "bin", "cudnn_engines_tensor_ir64_9.dll"),
|
| 269 |
+
]
|
| 270 |
+
else: # Linux
|
| 271 |
+
# Do not load cudnn sub DLLs (they will be dynamically loaded later) to be consistent with PyTorch in Linux.
|
| 272 |
+
cudnn_dll_paths = [
|
| 273 |
+
("nvidia", "cudnn", "lib", "libcudnn.so.9"),
|
| 274 |
+
]
|
| 275 |
+
|
| 276 |
+
return (cuda_dll_paths if cuda else []) + (cudnn_dll_paths if cudnn else [])
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
def print_debug_info():
|
| 280 |
+
"""Print information to help debugging."""
|
| 281 |
+
import importlib.util # noqa: PLC0415
|
| 282 |
+
import os # noqa: PLC0415
|
| 283 |
+
import platform # noqa: PLC0415
|
| 284 |
+
from importlib.metadata import distributions # noqa: PLC0415
|
| 285 |
+
|
| 286 |
+
print(f"{package_name} version: {__version__}")
|
| 287 |
+
if cuda_version:
|
| 288 |
+
print(f"CUDA version used in build: {cuda_version}")
|
| 289 |
+
print("platform:", platform.platform())
|
| 290 |
+
|
| 291 |
+
print("\nPython package, version and location:")
|
| 292 |
+
ort_packages = []
|
| 293 |
+
for dist in distributions():
|
| 294 |
+
package = dist.metadata["Name"]
|
| 295 |
+
if package == "onnxruntime" or package.startswith(("onnxruntime-", "ort-")):
|
| 296 |
+
# Exclude packages whose root directory name is not onnxruntime.
|
| 297 |
+
location = _get_package_root(package, "onnxruntime")
|
| 298 |
+
if location and (package not in ort_packages):
|
| 299 |
+
ort_packages.append(package)
|
| 300 |
+
print(f"{package}=={dist.version} at {location}")
|
| 301 |
+
|
| 302 |
+
if len(ort_packages) > 1:
|
| 303 |
+
print(
|
| 304 |
+
"\033[33mWARNING: multiple onnxruntime packages are installed to the same location. "
|
| 305 |
+
"Please 'pip uninstall` all above packages, then `pip install` only one of them.\033[0m"
|
| 306 |
+
)
|
| 307 |
+
|
| 308 |
+
if cuda_version:
|
| 309 |
+
# Print version of installed packages that is related to CUDA or cuDNN DLLs.
|
| 310 |
+
cuda_major = _extract_cuda_major_version(cuda_version)
|
| 311 |
+
|
| 312 |
+
# Starting with CUDA 13, NVIDIA dropped the "-cuNN" suffix from the per-component
|
| 313 |
+
# CUDA Toolkit packages (cuDNN keeps its suffixed package name).
|
| 314 |
+
cuda_pkg_suffix = "" if (cuda_major.isdigit() and int(cuda_major) >= 13) else f"-cu{cuda_major}"
|
| 315 |
+
|
| 316 |
+
packages = [
|
| 317 |
+
"torch",
|
| 318 |
+
f"nvidia-cuda-runtime{cuda_pkg_suffix}",
|
| 319 |
+
f"nvidia-cudnn-cu{cuda_major}",
|
| 320 |
+
f"nvidia-cublas{cuda_pkg_suffix}",
|
| 321 |
+
f"nvidia-cufft{cuda_pkg_suffix}",
|
| 322 |
+
f"nvidia-curand{cuda_pkg_suffix}",
|
| 323 |
+
f"nvidia-cuda-nvrtc{cuda_pkg_suffix}",
|
| 324 |
+
f"nvidia-nvjitlink{cuda_pkg_suffix}",
|
| 325 |
+
]
|
| 326 |
+
for package in packages:
|
| 327 |
+
directory_name = "nvidia" if package.startswith("nvidia-") else None
|
| 328 |
+
version = _get_package_version(package)
|
| 329 |
+
if version:
|
| 330 |
+
print(f"{package}=={version} at {_get_package_root(package, directory_name)}")
|
| 331 |
+
else:
|
| 332 |
+
print(f"{package} not installed")
|
| 333 |
+
|
| 334 |
+
if platform.system() == "Windows":
|
| 335 |
+
print(f"\nEnvironment variable:\nPATH={os.environ.get('PATH', '(unset)')}")
|
| 336 |
+
elif platform.system() == "Linux":
|
| 337 |
+
print(f"\nEnvironment variable:\nLD_LIBRARY_PATH={os.environ.get('LD_LIBRARY_PATH', '(unset)')}")
|
| 338 |
+
|
| 339 |
+
if importlib.util.find_spec("psutil"):
|
| 340 |
+
|
| 341 |
+
def is_target_dll(path: str):
|
| 342 |
+
target_keywords = ["vcruntime140", "msvcp140"]
|
| 343 |
+
if cuda_version:
|
| 344 |
+
target_keywords = ["cufft", "cublas", "cudart", "nvrtc", "curand", "cudnn", *target_keywords]
|
| 345 |
+
return any(keyword in path for keyword in target_keywords)
|
| 346 |
+
|
| 347 |
+
import psutil # noqa: PLC0415
|
| 348 |
+
|
| 349 |
+
p = psutil.Process(os.getpid())
|
| 350 |
+
|
| 351 |
+
print("\nList of loaded DLLs:")
|
| 352 |
+
for lib in p.memory_maps():
|
| 353 |
+
if is_target_dll(lib.path.lower()):
|
| 354 |
+
print(lib.path)
|
| 355 |
+
|
| 356 |
+
if cuda_version:
|
| 357 |
+
if importlib.util.find_spec("cpuinfo") and importlib.util.find_spec("py3nvml"):
|
| 358 |
+
from .transformers.machine_info import get_device_info # noqa: PLC0415
|
| 359 |
+
|
| 360 |
+
print("\nDevice information:")
|
| 361 |
+
print(get_device_info())
|
| 362 |
+
else:
|
| 363 |
+
print("please `pip install py-cpuinfo py3nvml` to show device information.")
|
| 364 |
+
else:
|
| 365 |
+
print("please `pip install psutil` to show loaded DLLs.")
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
def preload_dlls(cuda: bool = True, cudnn: bool = True, msvc: bool = True, directory=None):
|
| 369 |
+
"""Preload CUDA 12.x+ and optional cuDNN 9.x DLLs in Windows or Linux, and MSVC runtime DLLs in Windows.
|
| 370 |
+
|
| 371 |
+
When the installed PyTorch is compatible (using same major version of CUDA and cuDNN),
|
| 372 |
+
there is no need to call this function if `import torch` is done before `import onnxruntime`.
|
| 373 |
+
|
| 374 |
+
Args:
|
| 375 |
+
cuda (bool, optional): enable loading CUDA DLLs. Defaults to True.
|
| 376 |
+
cudnn (bool, optional): enable loading cuDNN DLLs. Defaults to True. Missing cuDNN DLLs are ignored.
|
| 377 |
+
msvc (bool, optional): enable loading MSVC DLLs in Windows. Defaults to True.
|
| 378 |
+
directory(str, optional): a directory contains CUDA or cuDNN DLLs. It can be an absolute path,
|
| 379 |
+
or a path relative to the directory of this file.
|
| 380 |
+
If directory is None (default value), the search order: the lib directory of compatible PyTorch in Windows,
|
| 381 |
+
nvidia site packages, default DLL loading paths.
|
| 382 |
+
If directory is empty string (""), the search order: nvidia site packages, default DLL loading paths.
|
| 383 |
+
If directory is a path, the search order: the directory, default DLL loading paths.
|
| 384 |
+
"""
|
| 385 |
+
import ctypes # noqa: PLC0415
|
| 386 |
+
import os # noqa: PLC0415
|
| 387 |
+
import platform # noqa: PLC0415
|
| 388 |
+
import sys # noqa: PLC0415
|
| 389 |
+
|
| 390 |
+
if platform.system() not in ["Windows", "Linux"]:
|
| 391 |
+
return
|
| 392 |
+
|
| 393 |
+
is_windows = platform.system() == "Windows"
|
| 394 |
+
if is_windows and msvc:
|
| 395 |
+
try:
|
| 396 |
+
ctypes.CDLL("vcruntime140.dll")
|
| 397 |
+
ctypes.CDLL("msvcp140.dll")
|
| 398 |
+
if platform.machine() != "ARM64":
|
| 399 |
+
ctypes.CDLL("vcruntime140_1.dll")
|
| 400 |
+
except OSError:
|
| 401 |
+
print("Microsoft Visual C++ Redistributable is not installed, this may lead to the DLL load failure.")
|
| 402 |
+
print("It can be downloaded at https://aka.ms/vs/17/release/vc_redist.x64.exe.")
|
| 403 |
+
|
| 404 |
+
# Check if CUDA version is supported (12.x or 13.x+)
|
| 405 |
+
ort_cuda_major = None
|
| 406 |
+
if cuda_version:
|
| 407 |
+
try:
|
| 408 |
+
ort_cuda_major = int(cuda_version.split(".")[0])
|
| 409 |
+
if ort_cuda_major < 12 and (cuda or cudnn):
|
| 410 |
+
print(
|
| 411 |
+
f"\033[33mWARNING: {package_name} is built with CUDA {cuda_version}, which is not supported for preloading. "
|
| 412 |
+
f"CUDA 12.x or newer is required. Call preload_dlls with cuda=False and cudnn=False.\033[0m"
|
| 413 |
+
)
|
| 414 |
+
return
|
| 415 |
+
except ValueError:
|
| 416 |
+
print(
|
| 417 |
+
f"\033[33mWARNING: Unable to parse CUDA version '{cuda_version}'. "
|
| 418 |
+
"Skipping DLL preloading. Call preload_dlls with cuda=False and cudnn=False.\033[0m"
|
| 419 |
+
)
|
| 420 |
+
return
|
| 421 |
+
elif cuda or cudnn:
|
| 422 |
+
# No CUDA version info available but CUDA/cuDNN preloading requested
|
| 423 |
+
return
|
| 424 |
+
|
| 425 |
+
is_cuda_cudnn_imported_by_torch = False
|
| 426 |
+
|
| 427 |
+
if is_windows:
|
| 428 |
+
torch_version = _get_package_version("torch")
|
| 429 |
+
# Check if torch CUDA version matches onnxruntime CUDA version
|
| 430 |
+
torch_cuda_major = None
|
| 431 |
+
if torch_version and "+cu" in torch_version:
|
| 432 |
+
with contextlib.suppress(ValueError):
|
| 433 |
+
# Extract CUDA version from torch (e.g., "2.0.0+cu121" -> 12)
|
| 434 |
+
cu_part = torch_version.split("+cu")[1]
|
| 435 |
+
torch_cuda_major = int(cu_part[:2]) # First 2 digits are major version
|
| 436 |
+
|
| 437 |
+
is_torch_cuda_compatible = (
|
| 438 |
+
torch_cuda_major == ort_cuda_major if (torch_cuda_major and ort_cuda_major) else False
|
| 439 |
+
)
|
| 440 |
+
|
| 441 |
+
if "torch" in sys.modules:
|
| 442 |
+
is_cuda_cudnn_imported_by_torch = is_torch_cuda_compatible
|
| 443 |
+
if torch_cuda_major and ort_cuda_major and torch_cuda_major != ort_cuda_major:
|
| 444 |
+
print(
|
| 445 |
+
f"\033[33mWARNING: The installed PyTorch {torch_version} uses CUDA {torch_cuda_major}.x, "
|
| 446 |
+
f"but {package_name} is built with CUDA {ort_cuda_major}.x. "
|
| 447 |
+
f"Please install PyTorch for CUDA {ort_cuda_major}.x to be compatible.\033[0m"
|
| 448 |
+
)
|
| 449 |
+
|
| 450 |
+
if is_torch_cuda_compatible and directory is None:
|
| 451 |
+
torch_root = _get_package_root("torch", "torch")
|
| 452 |
+
if torch_root:
|
| 453 |
+
directory = os.path.join(torch_root, "lib")
|
| 454 |
+
|
| 455 |
+
base_directory = directory or ".."
|
| 456 |
+
if not os.path.isabs(base_directory):
|
| 457 |
+
base_directory = os.path.join(os.path.dirname(__file__), base_directory)
|
| 458 |
+
base_directory = os.path.normpath(base_directory)
|
| 459 |
+
if not os.path.isdir(base_directory):
|
| 460 |
+
raise RuntimeError(f"Invalid parameter of directory={directory}. The directory does not exist!")
|
| 461 |
+
|
| 462 |
+
if is_cuda_cudnn_imported_by_torch:
|
| 463 |
+
# In Windows, PyTorch has loaded CUDA and cuDNN DLLs during `import torch`, no need to load them again.
|
| 464 |
+
print("Skip loading CUDA and cuDNN DLLs since torch is imported.")
|
| 465 |
+
return
|
| 466 |
+
|
| 467 |
+
# Try load DLLs from nvidia site packages.
|
| 468 |
+
dll_paths = _get_nvidia_dll_paths(is_windows, cuda, cudnn)
|
| 469 |
+
optional_dll_filenames = {relative_path[-1] for relative_path in _get_nvidia_dll_paths(is_windows, False, cudnn)}
|
| 470 |
+
loaded_dlls = []
|
| 471 |
+
for relative_path in dll_paths:
|
| 472 |
+
dll_path = (
|
| 473 |
+
os.path.join(base_directory, relative_path[-1])
|
| 474 |
+
if directory
|
| 475 |
+
else os.path.join(base_directory, *relative_path)
|
| 476 |
+
)
|
| 477 |
+
if os.path.isfile(dll_path):
|
| 478 |
+
try:
|
| 479 |
+
_ = ctypes.CDLL(dll_path)
|
| 480 |
+
loaded_dlls.append(relative_path[-1])
|
| 481 |
+
except Exception as e:
|
| 482 |
+
if relative_path[-1] not in optional_dll_filenames:
|
| 483 |
+
print(f"Failed to load {dll_path}: {e}")
|
| 484 |
+
|
| 485 |
+
# Try load DLLs with default path settings.
|
| 486 |
+
has_failure = False
|
| 487 |
+
for relative_path in dll_paths:
|
| 488 |
+
dll_filename = relative_path[-1]
|
| 489 |
+
if dll_filename not in loaded_dlls:
|
| 490 |
+
try:
|
| 491 |
+
_ = ctypes.CDLL(dll_filename)
|
| 492 |
+
except Exception as e:
|
| 493 |
+
if dll_filename not in optional_dll_filenames:
|
| 494 |
+
has_failure = True
|
| 495 |
+
print(f"Failed to load {dll_filename}: {e}")
|
| 496 |
+
|
| 497 |
+
if has_failure:
|
| 498 |
+
print("Please follow https://onnxruntime.ai/docs/install/#cuda-and-cudnn to install CUDA.")
|
| 499 |
+
|
| 500 |
+
_register_bundled_cuda_plugin_ep(warn_on_failure=True)
|
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -------------------------------------------------------------------------
|
| 2 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 3 |
+
# Licensed under the MIT License.
|
| 4 |
+
# --------------------------------------------------------------------------
|
| 5 |
+
|
| 6 |
+
from .backend import is_compatible, prepare, run, supports_device # noqa: F401
|
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -------------------------------------------------------------------------
|
| 2 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 3 |
+
# Licensed under the MIT License.
|
| 4 |
+
# --------------------------------------------------------------------------
|
| 5 |
+
"""
|
| 6 |
+
Implements ONNX's backend API.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import os
|
| 10 |
+
import unittest
|
| 11 |
+
|
| 12 |
+
import packaging.version
|
| 13 |
+
from onnx import ModelProto, helper, version # noqa: F401
|
| 14 |
+
from onnx.backend.base import Backend
|
| 15 |
+
from onnx.checker import check_model
|
| 16 |
+
|
| 17 |
+
from onnxruntime import InferenceSession, SessionOptions, get_available_providers, get_device
|
| 18 |
+
from onnxruntime.backend.backend_rep import OnnxRuntimeBackendRep
|
| 19 |
+
|
| 20 |
+
# Allowlist of SessionOptions attributes that are safe to set via the backend API.
|
| 21 |
+
# Dangerous attributes intentionally excluded:
|
| 22 |
+
# optimized_model_filepath — triggers Model::Save(), overwrites arbitrary files
|
| 23 |
+
# profile_file_prefix — writes profiling JSON to arbitrary path
|
| 24 |
+
# enable_profiling — causes uncontrolled file writes to cwd
|
| 25 |
+
_ALLOWED_SESSION_OPTIONS = frozenset(
|
| 26 |
+
{
|
| 27 |
+
"enable_cpu_mem_arena",
|
| 28 |
+
"enable_mem_pattern",
|
| 29 |
+
"enable_mem_reuse",
|
| 30 |
+
"execution_mode",
|
| 31 |
+
"execution_order",
|
| 32 |
+
"graph_optimization_level",
|
| 33 |
+
"inter_op_num_threads",
|
| 34 |
+
"intra_op_num_threads",
|
| 35 |
+
"log_severity_level",
|
| 36 |
+
"log_verbosity_level",
|
| 37 |
+
"logid",
|
| 38 |
+
"use_deterministic_compute",
|
| 39 |
+
"use_per_session_threads",
|
| 40 |
+
}
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class OnnxRuntimeBackend(Backend):
|
| 45 |
+
"""
|
| 46 |
+
Implements
|
| 47 |
+
`ONNX's backend API <https://github.com/onnx/onnx/blob/main/docs/ImplementingAnOnnxBackend.md>`_
|
| 48 |
+
with *ONNX Runtime*.
|
| 49 |
+
The backend is mostly used when you need to switch between
|
| 50 |
+
multiple runtimes with the same API.
|
| 51 |
+
`Importing models from ONNX to Caffe2 <https://github.com/onnx/tutorials/blob/master/tutorials/OnnxCaffe2Import.ipynb>`_
|
| 52 |
+
shows how to use *caffe2* as a backend for a converted model.
|
| 53 |
+
Note: This is not the official Python API.
|
| 54 |
+
"""
|
| 55 |
+
|
| 56 |
+
allowReleasedOpsetsOnly = bool(os.getenv("ALLOW_RELEASED_ONNX_OPSET_ONLY", "1") == "1") # noqa: N815
|
| 57 |
+
|
| 58 |
+
@classmethod
|
| 59 |
+
def is_compatible(cls, model, device=None, **kwargs):
|
| 60 |
+
"""
|
| 61 |
+
Return whether the model is compatible with the backend.
|
| 62 |
+
|
| 63 |
+
:param model: unused
|
| 64 |
+
:param device: None to use the default device or a string (ex: `'CPU'`)
|
| 65 |
+
:return: boolean
|
| 66 |
+
"""
|
| 67 |
+
if device is None:
|
| 68 |
+
device = get_device()
|
| 69 |
+
return cls.supports_device(device)
|
| 70 |
+
|
| 71 |
+
@classmethod
|
| 72 |
+
def is_opset_supported(cls, model):
|
| 73 |
+
"""
|
| 74 |
+
Return whether the opset for the model is supported by the backend.
|
| 75 |
+
When By default only released onnx opsets are allowed by the backend
|
| 76 |
+
To test new opsets env variable ALLOW_RELEASED_ONNX_OPSET_ONLY should be set to 0
|
| 77 |
+
|
| 78 |
+
:param model: Model whose opsets needed to be verified.
|
| 79 |
+
:return: boolean and error message if opset is not supported.
|
| 80 |
+
"""
|
| 81 |
+
if cls.allowReleasedOpsetsOnly:
|
| 82 |
+
for opset in model.opset_import:
|
| 83 |
+
domain = opset.domain if opset.domain else "ai.onnx"
|
| 84 |
+
try:
|
| 85 |
+
key = (domain, opset.version)
|
| 86 |
+
if key not in helper.OP_SET_ID_VERSION_MAP:
|
| 87 |
+
error_message = (
|
| 88 |
+
"Skipping this test as only released onnx opsets are supported."
|
| 89 |
+
"To run this test set env variable ALLOW_RELEASED_ONNX_OPSET_ONLY to 0."
|
| 90 |
+
f" Got Domain '{domain}' version '{opset.version}'."
|
| 91 |
+
)
|
| 92 |
+
return False, error_message
|
| 93 |
+
except AttributeError:
|
| 94 |
+
# for some CI pipelines accessing helper.OP_SET_ID_VERSION_MAP
|
| 95 |
+
# is generating attribute error. TODO investigate the pipelines to
|
| 96 |
+
# fix this error. Falling back to a simple version check when this error is encountered
|
| 97 |
+
if (domain == "ai.onnx" and opset.version > 12) or (domain == "ai.ommx.ml" and opset.version > 2):
|
| 98 |
+
error_message = (
|
| 99 |
+
"Skipping this test as only released onnx opsets are supported."
|
| 100 |
+
"To run this test set env variable ALLOW_RELEASED_ONNX_OPSET_ONLY to 0."
|
| 101 |
+
f" Got Domain '{domain}' version '{opset.version}'."
|
| 102 |
+
)
|
| 103 |
+
return False, error_message
|
| 104 |
+
return True, ""
|
| 105 |
+
|
| 106 |
+
@classmethod
|
| 107 |
+
def supports_device(cls, device):
|
| 108 |
+
"""
|
| 109 |
+
Check whether the backend is compiled with particular device support.
|
| 110 |
+
In particular it's used in the testing suite.
|
| 111 |
+
"""
|
| 112 |
+
if device == "CUDA":
|
| 113 |
+
device = "GPU"
|
| 114 |
+
return "-" + device in get_device() or device + "-" in get_device() or device == get_device()
|
| 115 |
+
|
| 116 |
+
@classmethod
|
| 117 |
+
def prepare(cls, model, device=None, **kwargs):
|
| 118 |
+
"""
|
| 119 |
+
Load the model and creates an :class:`onnxruntime.backend.backend_rep.OnnxRuntimeBackendRep`
|
| 120 |
+
ready to be used as a backend.
|
| 121 |
+
|
| 122 |
+
:param model: the model to prepare — accepts a file path (str), serialized
|
| 123 |
+
model (bytes), :class:`onnx.ModelProto`, :class:`onnxruntime.InferenceSession`,
|
| 124 |
+
or :class:`onnxruntime.backend.backend_rep.OnnxRuntimeBackendRep` (returned as-is)
|
| 125 |
+
:param device: requested device for the computation,
|
| 126 |
+
None means the default one which depends on
|
| 127 |
+
the compilation settings
|
| 128 |
+
:param kwargs: only a safe subset of :class:`onnxruntime.SessionOptions` attributes are
|
| 129 |
+
accepted; see ``_ALLOWED_SESSION_OPTIONS`` for the list
|
| 130 |
+
:return: :class:`onnxruntime.backend.backend_rep.OnnxRuntimeBackendRep`
|
| 131 |
+
"""
|
| 132 |
+
if isinstance(model, OnnxRuntimeBackendRep):
|
| 133 |
+
return model
|
| 134 |
+
elif isinstance(model, InferenceSession):
|
| 135 |
+
return OnnxRuntimeBackendRep(model)
|
| 136 |
+
elif isinstance(model, (str, bytes)):
|
| 137 |
+
options = SessionOptions()
|
| 138 |
+
for k, v in kwargs.items():
|
| 139 |
+
if k in _ALLOWED_SESSION_OPTIONS:
|
| 140 |
+
setattr(options, k, v)
|
| 141 |
+
elif hasattr(options, k):
|
| 142 |
+
raise RuntimeError(
|
| 143 |
+
f"SessionOptions attribute '{k}' is not permitted via the backend API. "
|
| 144 |
+
f"Allowed attributes: {', '.join(sorted(_ALLOWED_SESSION_OPTIONS))}"
|
| 145 |
+
)
|
| 146 |
+
# else: silently ignore unknown keys
|
| 147 |
+
|
| 148 |
+
excluded_providers = os.getenv("ORT_ONNX_BACKEND_EXCLUDE_PROVIDERS", default="").split(",")
|
| 149 |
+
providers = [x for x in get_available_providers() if (x not in excluded_providers)]
|
| 150 |
+
|
| 151 |
+
inf = InferenceSession(model, sess_options=options, providers=providers)
|
| 152 |
+
# backend API is primarily used for ONNX test/validation. As such, we should disable session.run() fallback
|
| 153 |
+
# which may hide test failures.
|
| 154 |
+
inf.disable_fallback()
|
| 155 |
+
if device is not None and not cls.supports_device(device):
|
| 156 |
+
raise RuntimeError(f"Incompatible device expected '{device}', got '{get_device()}'")
|
| 157 |
+
return cls.prepare(inf, device, **kwargs)
|
| 158 |
+
else:
|
| 159 |
+
# type: ModelProto
|
| 160 |
+
# check_model serializes the model anyways, so serialize the model once here
|
| 161 |
+
# and reuse it below in the cls.prepare call to avoid an additional serialization
|
| 162 |
+
# only works with onnx >= 1.10.0 hence the version check
|
| 163 |
+
onnx_version = packaging.version.parse(version.version) or packaging.version.Version("0")
|
| 164 |
+
onnx_supports_serialized_model_check = onnx_version.release >= (1, 10, 0)
|
| 165 |
+
bin_or_model = model.SerializeToString() if onnx_supports_serialized_model_check else model
|
| 166 |
+
check_model(bin_or_model)
|
| 167 |
+
opset_supported, error_message = cls.is_opset_supported(model)
|
| 168 |
+
if not opset_supported:
|
| 169 |
+
raise unittest.SkipTest(error_message)
|
| 170 |
+
# Now bin might be serialized, if it's not we need to serialize it otherwise we'll have
|
| 171 |
+
# an infinite recursive call
|
| 172 |
+
bin = bin_or_model
|
| 173 |
+
if not isinstance(bin, (str, bytes)):
|
| 174 |
+
bin = bin.SerializeToString()
|
| 175 |
+
return cls.prepare(bin, device, **kwargs)
|
| 176 |
+
|
| 177 |
+
@classmethod
|
| 178 |
+
def run_model(cls, model, inputs, device=None, **kwargs):
|
| 179 |
+
"""
|
| 180 |
+
Compute the prediction.
|
| 181 |
+
|
| 182 |
+
:param model: the model to run — accepts a file path (str), serialized
|
| 183 |
+
model (bytes), :class:`onnx.ModelProto`, :class:`onnxruntime.InferenceSession`,
|
| 184 |
+
or :class:`onnxruntime.backend.backend_rep.OnnxRuntimeBackendRep`
|
| 185 |
+
:param inputs: inputs
|
| 186 |
+
:param device: requested device for the computation,
|
| 187 |
+
None means the default one which depends on
|
| 188 |
+
the compilation settings
|
| 189 |
+
:param kwargs: ``run_model()`` forwards kwargs to both ``prepare()`` and ``rep.run()``.
|
| 190 |
+
``prepare()`` validates and applies ``_ALLOWED_SESSION_OPTIONS`` only when creating
|
| 191 |
+
a new session from a model path or bytes; if ``model`` is already an
|
| 192 |
+
``InferenceSession`` or ``OnnxRuntimeBackendRep``, session-option kwargs are
|
| 193 |
+
silently ignored. ``rep.run()`` always validates against ``_ALLOWED_RUN_OPTIONS``
|
| 194 |
+
and raises ``RuntimeError`` for known-but-blocked run attributes.
|
| 195 |
+
Logging-related kwargs (``log_severity_level``, ``log_verbosity_level``, ``logid``)
|
| 196 |
+
appear in both allowlists.
|
| 197 |
+
:return: predictions
|
| 198 |
+
"""
|
| 199 |
+
rep = cls.prepare(model, device, **kwargs)
|
| 200 |
+
return rep.run(inputs, **kwargs)
|
| 201 |
+
|
| 202 |
+
@classmethod
|
| 203 |
+
def run_node(cls, node, inputs, device=None, outputs_info=None, **kwargs):
|
| 204 |
+
"""
|
| 205 |
+
This method is not implemented as it is much more efficient
|
| 206 |
+
to run a whole model than every node independently.
|
| 207 |
+
"""
|
| 208 |
+
raise NotImplementedError("It is much more efficient to run a whole model than every node independently.")
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
is_compatible = OnnxRuntimeBackend.is_compatible
|
| 212 |
+
prepare = OnnxRuntimeBackend.prepare
|
| 213 |
+
run = OnnxRuntimeBackend.run_model
|
| 214 |
+
supports_device = OnnxRuntimeBackend.supports_device
|
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -------------------------------------------------------------------------
|
| 2 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 3 |
+
# Licensed under the MIT License.
|
| 4 |
+
# --------------------------------------------------------------------------
|
| 5 |
+
"""
|
| 6 |
+
Implements ONNX's backend API.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
from onnx.backend.base import BackendRep
|
| 10 |
+
|
| 11 |
+
from onnxruntime import RunOptions
|
| 12 |
+
|
| 13 |
+
# Allowlist of RunOptions attributes that are safe to set via the backend API.
|
| 14 |
+
# 'terminate' excluded: setting it True would deny the current inference call.
|
| 15 |
+
# 'training_mode' excluded: silently switches inference behavior in training builds.
|
| 16 |
+
_ALLOWED_RUN_OPTIONS = frozenset(
|
| 17 |
+
{
|
| 18 |
+
"log_severity_level",
|
| 19 |
+
"log_verbosity_level",
|
| 20 |
+
"logid",
|
| 21 |
+
"only_execute_path_to_fetches",
|
| 22 |
+
}
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class OnnxRuntimeBackendRep(BackendRep):
|
| 27 |
+
"""
|
| 28 |
+
Wraps an :class:`onnxruntime.InferenceSession` to implement ONNX's
|
| 29 |
+
:class:`onnx.backend.base.BackendRep` interface for running predictions.
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
def __init__(self, session):
|
| 33 |
+
"""
|
| 34 |
+
:param session: :class:`onnxruntime.InferenceSession`
|
| 35 |
+
"""
|
| 36 |
+
self._session = session
|
| 37 |
+
|
| 38 |
+
def run(self, inputs, **kwargs): # type: (Any, **Any) -> Tuple[Any, ...]
|
| 39 |
+
"""
|
| 40 |
+
Computes the prediction.
|
| 41 |
+
See :meth:`onnxruntime.InferenceSession.run`.
|
| 42 |
+
|
| 43 |
+
:param inputs: a list of input arrays (one per model input) or a single
|
| 44 |
+
array when the model has exactly one input
|
| 45 |
+
:param kwargs: only a safe subset of :class:`onnxruntime.RunOptions` attributes are
|
| 46 |
+
accepted; see ``_ALLOWED_RUN_OPTIONS`` for the list
|
| 47 |
+
:return: list of output arrays
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
options = RunOptions()
|
| 51 |
+
for k, v in kwargs.items():
|
| 52 |
+
if k in _ALLOWED_RUN_OPTIONS:
|
| 53 |
+
setattr(options, k, v)
|
| 54 |
+
elif hasattr(options, k):
|
| 55 |
+
raise RuntimeError(
|
| 56 |
+
f"RunOptions attribute '{k}' is not permitted via the backend API. "
|
| 57 |
+
f"Allowed attributes: {', '.join(sorted(_ALLOWED_RUN_OPTIONS))}"
|
| 58 |
+
)
|
| 59 |
+
# else: silently ignore unknown keys
|
| 60 |
+
|
| 61 |
+
if isinstance(inputs, list):
|
| 62 |
+
inps = {}
|
| 63 |
+
for i, inp in enumerate(self._session.get_inputs()):
|
| 64 |
+
inps[inp.name] = inputs[i]
|
| 65 |
+
outs = self._session.run(None, inps, options)
|
| 66 |
+
if isinstance(outs, list):
|
| 67 |
+
return outs
|
| 68 |
+
else:
|
| 69 |
+
output_names = [o.name for o in self._session.get_outputs()]
|
| 70 |
+
return [outs[name] for name in output_names]
|
| 71 |
+
else:
|
| 72 |
+
inp = self._session.get_inputs()
|
| 73 |
+
if len(inp) != 1:
|
| 74 |
+
raise RuntimeError(f"Model expect {len(inp)} inputs")
|
| 75 |
+
inps = {inp[0].name: inputs}
|
| 76 |
+
return self._session.run(None, inps, options)
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9c9e6d822561c6c41b90e6994b3e8857cf1d66dbfb1e0c4c799c7c89b4e92da1
|
| 3 |
+
size 18527776
|
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -------------------------------------------------------------------------
|
| 2 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 3 |
+
# Licensed under the MIT License.
|
| 4 |
+
# --------------------------------------------------------------------------
|
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -------------------------------------------------------------------------
|
| 2 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 3 |
+
# Licensed under the MIT License.
|
| 4 |
+
# --------------------------------------------------------------------------
|
| 5 |
+
|
| 6 |
+
# This file can be modified by setup.py when building a manylinux2010 wheel
|
| 7 |
+
# When modified, it will preload some libraries needed for the python C extension
|
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# --- UORT bundle ------------------------------------------------------------
|
| 2 |
+
# Registers the directory of this module with the Windows loader before the
|
| 3 |
+
# pybind11 extension is imported. Added by scripts\runtime\stage_python_bundle.ps1.
|
| 4 |
+
try:
|
| 5 |
+
from ._uort_dll_dirs import add_dll_dirs as _uort_add_dll_dirs
|
| 6 |
+
|
| 7 |
+
_uort_add_dll_dirs()
|
| 8 |
+
except Exception: # noqa: BLE001 - a plain package layout has no _uort_dll_dirs
|
| 9 |
+
pass
|
| 10 |
+
# ----------------------------------------------------------------------------
|
| 11 |
+
# -------------------------------------------------------------------------
|
| 12 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 13 |
+
# Licensed under the MIT License.
|
| 14 |
+
# --------------------------------------------------------------------------
|
| 15 |
+
"""
|
| 16 |
+
Ensure that dependencies are available and then load the extension module.
|
| 17 |
+
"""
|
| 18 |
+
import os
|
| 19 |
+
import platform
|
| 20 |
+
import warnings
|
| 21 |
+
|
| 22 |
+
from . import _ld_preload # noqa: F401
|
| 23 |
+
|
| 24 |
+
if platform.system() == "Windows":
|
| 25 |
+
from . import version_info
|
| 26 |
+
|
| 27 |
+
# If on Windows, check if this import error is caused by the user not installing the 2019 VC Runtime
|
| 28 |
+
# The VC Redist installer usually puts the VC Runtime dlls in the System32 folder, but it may also be found
|
| 29 |
+
# in some other locations.
|
| 30 |
+
# TODO, we may want to try to load the VC Runtime dlls instead of checking if the hardcoded file path
|
| 31 |
+
# is valid, and raise ImportError if the load fails
|
| 32 |
+
if version_info.vs2019 and platform.architecture()[0] == "64bit":
|
| 33 |
+
system_root = os.getenv("SystemRoot") or "C:\\Windows"
|
| 34 |
+
if not os.path.isfile(os.path.join(system_root, "System32", "vcruntime140_1.dll")):
|
| 35 |
+
warnings.warn("Please install the 2019 Visual C++ runtime and then try again. "
|
| 36 |
+
"If you've installed the runtime in a non-standard location "
|
| 37 |
+
"(other than %SystemRoot%\\System32), "
|
| 38 |
+
"make sure it can be found by setting the correct path.")
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
from .onnxruntime_pybind11_state import * # noqa
|
| 43 |
+
|
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""UORT: make the DLLs bundled next to this module visible to the Windows loader.
|
| 2 |
+
|
| 3 |
+
The bundle keeps every native dependency of the pybind11 module (CUDA, cuDNN,
|
| 4 |
+
TensorRT, OpenVINO) in the same directory as the module. CPython already
|
| 5 |
+
searches that directory for the direct imports of the extension module, but
|
| 6 |
+
cuDNN, cuFFT, TensorRT and OpenVINO resolve several libraries themselves with
|
| 7 |
+
plain LoadLibrary, which only looks at the process directory and PATH.
|
| 8 |
+
Registering the directory both ways covers all of them: AddDllDirectory works
|
| 9 |
+
for LoadLibraryEx with LOAD_LIBRARY_SEARCH_USER_DIRS, PATH for plain
|
| 10 |
+
LoadLibrary.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
from __future__ import annotations
|
| 14 |
+
|
| 15 |
+
import os
|
| 16 |
+
|
| 17 |
+
_DIR = os.path.dirname(os.path.abspath(__file__))
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def add_dll_dirs() -> str:
|
| 21 |
+
"""Register the bundle directory; returns the directory that was added."""
|
| 22 |
+
if os.name != "nt":
|
| 23 |
+
return _DIR
|
| 24 |
+
try:
|
| 25 |
+
os.add_dll_directory(_DIR)
|
| 26 |
+
except (AttributeError, OSError):
|
| 27 |
+
pass
|
| 28 |
+
current = os.environ.get("PATH", "")
|
| 29 |
+
parts = [p for p in current.split(os.pathsep) if p]
|
| 30 |
+
if _DIR not in parts:
|
| 31 |
+
os.environ["PATH"] = _DIR + os.pathsep + current
|
| 32 |
+
return _DIR
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package_name = 'uort-python'
|
| 2 |
+
__version__ = '1.30.1'
|
| 3 |
+
cuda_version = '12.9'
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2405355f0a58067b258f8df33c327e3a3d716eaac5a3a5aebb757842d85bd376
|
| 3 |
+
size 324208
|
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 2 |
+
# Licensed under the MIT License.
|
| 3 |
+
|
| 4 |
+
# This script helps converting .npz files to .onnx_adapter files
|
| 5 |
+
|
| 6 |
+
import argparse
|
| 7 |
+
import os
|
| 8 |
+
import sys
|
| 9 |
+
|
| 10 |
+
import numpy as np
|
| 11 |
+
|
| 12 |
+
import onnxruntime as ort
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def get_args() -> argparse:
|
| 16 |
+
parser = argparse.ArgumentParser()
|
| 17 |
+
parser.add_argument("--npz_file_path", type=str, required=True)
|
| 18 |
+
parser.add_argument("--output_file_path", type=str, required=True)
|
| 19 |
+
parser.add_argument("--adapter_version", type=int, required=True)
|
| 20 |
+
parser.add_argument("--model_version", type=int, required=True)
|
| 21 |
+
return parser.parse_args()
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def export_lora_parameters(
|
| 25 |
+
npz_file_path: os.PathLike, adapter_version: int, model_version: int, output_file_path: os.PathLike
|
| 26 |
+
):
|
| 27 |
+
"""The function converts lora parameters in npz to onnx_adapter format"""
|
| 28 |
+
adapter_format = ort.AdapterFormat()
|
| 29 |
+
adapter_format.set_adapter_version(adapter_version)
|
| 30 |
+
adapter_format.set_model_version(model_version)
|
| 31 |
+
name_to_ort_value = {}
|
| 32 |
+
with np.load(npz_file_path) as data:
|
| 33 |
+
for name, np_arr in data.items():
|
| 34 |
+
ort_value = ort.OrtValue.ortvalue_from_numpy(np_arr)
|
| 35 |
+
name_to_ort_value[name] = ort_value
|
| 36 |
+
|
| 37 |
+
adapter_format.set_parameters(name_to_ort_value)
|
| 38 |
+
adapter_format.export_adapter(output_file_path)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def main() -> int:
|
| 42 |
+
args = get_args()
|
| 43 |
+
export_lora_parameters(args.npz_file_path, args.adapter_version, args.model_version, args.output_file_path)
|
| 44 |
+
return 0
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
if __name__ == "__main__":
|
| 48 |
+
sys.exit(main())
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:90052a83efd1b57a8e3616a6590b335855f81b814a4f16eecb7b5bf6d1b1d4eb
|
| 3 |
+
size 102518272
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c3a05ea244c937314afec09f87b91f814c7e27977681f6c67eb51bb06ced3a4a
|
| 3 |
+
size 668669952
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:760c38928bbe5759f7b31ed6692599eb7ec83cedd5702e84c2b72028a89837e1
|
| 3 |
+
size 583680
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9edbcdff73b0af070eb160b2ce66e59feca04aa017351d8eedcc5e8e149967d2
|
| 3 |
+
size 266288
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f202e8ff36209ac4292fe2ef79d4539487495b3b948f0c978cc63298f43a1850
|
| 3 |
+
size 282445872
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3f03dec3b81f137f049e1d1c06ff469248eef54ff9424b3b9045fc0f69559d1a
|
| 3 |
+
size 4618272
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ebc2c1f74366b029a4350f7f9a3b109ccd5aa9b4ae02527950793dd29f7b74fb
|
| 3 |
+
size 513926688
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:93ab6351ca0f2b6ca842f83d284e56c427525778c14657229801a13cf8121bd8
|
| 3 |
+
size 20201008
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a933efeb280a3252297f92d79f7e83003bcbc6f665872d11645d1dbbab9cb598
|
| 3 |
+
size 2420256
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:458175f12771a35de0ea39c04250d727c2424d42a7ffe0bc987ae81204748fba
|
| 3 |
+
size 56823328
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:851d8f4191a24b322b0be738d191a3159455f0da4db47a19f5cc7ad6e55edf61
|
| 3 |
+
size 126508576
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5b90655ca9cdf5d91cbdf2c72b995ec937eed0aef2d6954370c711b45c929b35
|
| 3 |
+
size 287136768
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4d6d16c9941c4f7a7e49d5c43df25d408a2a8503d8cc1eb9808e0e0c2cde36d4
|
| 3 |
+
size 79197696
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0f885b509a685d2bbfa652fed26b5fb31d88fbdab0a978c641d1c7b8aa460aa9
|
| 3 |
+
size 557728
|
|
Binary file (36 kB). View file
|
|
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3ea06f0ee098b4823cb79599df3780e7f23cce52c19aac31d2a0d47efe33a5e9
|
| 3 |
+
size 280200
|
|
Binary file (50.3 kB). View file
|
|
|
|
Binary file (31.9 kB). View file
|
|
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:55c7ec9272a4b74830d3caaae9106f277fc7df37690067f69fe1fdfd1dbc52ff
|
| 3 |
+
size 440983072
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2b8496c57a8434534e8a3c56e732207f1c74bc37ce7300d8f0d3542cdff02e12
|
| 3 |
+
size 1971797024
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4100a0e46f96088a3e79664c1d719beb462ce1f067b4cadf24feeebc7dd693e1
|
| 3 |
+
size 624672
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:420aee53c0c2b4b35e76335daba57e0cabd7e22f2b946e47ed77dfb3019ca5a9
|
| 3 |
+
size 51002400
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d4ca07f3f392f6f39f1738ed5e615910448d1f98b657551a01c7575881584a2a
|
| 3 |
+
size 2993696
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a4599b0168c8b3e485e50c4493559280778f7c533d66bbb3ea372073f316b67f
|
| 3 |
+
size 24535040
|
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -------------------------------------------------------------------------
|
| 2 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 3 |
+
# Licensed under the MIT License.
|
| 4 |
+
# --------------------------------------------------------------------------
|
| 5 |
+
import ctypes
|
| 6 |
+
import sys
|
| 7 |
+
import warnings
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def find_cudart_versions(build_env=False, build_cuda_version=None):
|
| 11 |
+
# ctypes.CDLL and ctypes.util.find_library load the latest installed library.
|
| 12 |
+
# it may not the the library that would be loaded by onnxruntime.
|
| 13 |
+
# for example, in an environment with Cuda 11.1 and subsequently
|
| 14 |
+
# conda cudatoolkit 10.2.89 installed. ctypes will find cudart 10.2. however,
|
| 15 |
+
# onnxruntime built with Cuda 11.1 will find and load cudart for Cuda 11.1.
|
| 16 |
+
# for the above reason, we need find all versions in the environment and
|
| 17 |
+
# only give warnings if the expected cuda version is not found.
|
| 18 |
+
# in onnxruntime build environment, we expected only one Cuda version.
|
| 19 |
+
if not sys.platform.startswith("linux"):
|
| 20 |
+
warnings.warn("find_cudart_versions only works on Linux")
|
| 21 |
+
return None
|
| 22 |
+
|
| 23 |
+
cudart_possible_versions = {None, build_cuda_version}
|
| 24 |
+
|
| 25 |
+
def get_cudart_version(find_cudart_version=None):
|
| 26 |
+
cudart_lib_filename = "libcudart.so"
|
| 27 |
+
if find_cudart_version:
|
| 28 |
+
cudart_lib_filename = cudart_lib_filename + "." + find_cudart_version
|
| 29 |
+
|
| 30 |
+
try:
|
| 31 |
+
cudart = ctypes.CDLL(cudart_lib_filename)
|
| 32 |
+
cudart.cudaRuntimeGetVersion.restype = int
|
| 33 |
+
cudart.cudaRuntimeGetVersion.argtypes = [ctypes.POINTER(ctypes.c_int)]
|
| 34 |
+
version = ctypes.c_int()
|
| 35 |
+
status = cudart.cudaRuntimeGetVersion(ctypes.byref(version))
|
| 36 |
+
if status != 0:
|
| 37 |
+
return None
|
| 38 |
+
except Exception:
|
| 39 |
+
return None
|
| 40 |
+
|
| 41 |
+
return version.value
|
| 42 |
+
|
| 43 |
+
# use set to avoid duplications
|
| 44 |
+
cudart_found_versions = {get_cudart_version(cudart_version) for cudart_version in cudart_possible_versions}
|
| 45 |
+
|
| 46 |
+
# convert to list and remove None
|
| 47 |
+
return [ver for ver in cudart_found_versions if ver]
|
|
@@ -0,0 +1,1930 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -------------------------------------------------------------------------
|
| 2 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 3 |
+
# Licensed under the MIT License.
|
| 4 |
+
# --------------------------------------------------------------------------
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import collections
|
| 8 |
+
import collections.abc
|
| 9 |
+
import os
|
| 10 |
+
import typing
|
| 11 |
+
import warnings
|
| 12 |
+
import weakref
|
| 13 |
+
from collections.abc import Callable, Sequence
|
| 14 |
+
from enum import IntEnum
|
| 15 |
+
from typing import Any
|
| 16 |
+
|
| 17 |
+
import numpy as np
|
| 18 |
+
|
| 19 |
+
from onnxruntime.capi import _pybind_state as C
|
| 20 |
+
|
| 21 |
+
if typing.TYPE_CHECKING:
|
| 22 |
+
import numpy.typing as npt
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def get_ort_device_type(device_type: str) -> int:
|
| 26 |
+
if device_type == "cuda":
|
| 27 |
+
return C.OrtDevice.cuda()
|
| 28 |
+
elif device_type == "cann":
|
| 29 |
+
return C.OrtDevice.cann()
|
| 30 |
+
elif device_type == "cpu":
|
| 31 |
+
return C.OrtDevice.cpu()
|
| 32 |
+
elif device_type == "dml":
|
| 33 |
+
return C.OrtDevice.dml()
|
| 34 |
+
elif device_type == "webgpu":
|
| 35 |
+
return C.OrtDevice.webgpu()
|
| 36 |
+
elif device_type == "gpu":
|
| 37 |
+
return C.OrtDevice.gpu()
|
| 38 |
+
elif device_type == "npu":
|
| 39 |
+
return C.OrtDevice.npu()
|
| 40 |
+
else:
|
| 41 |
+
raise Exception("Unsupported device type: " + device_type)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class OrtDeviceVendorId(IntEnum):
|
| 45 |
+
"""Vendor IDs aligned with OrtDevice::VendorIds in ortdevice.h."""
|
| 46 |
+
|
| 47 |
+
NONE = 0x0000
|
| 48 |
+
AMD = 0x1002
|
| 49 |
+
NVIDIA = 0x10DE
|
| 50 |
+
ARM = 0x13B5
|
| 51 |
+
MICROSOFT = 0x1414
|
| 52 |
+
HUAWEI = 0x19E5
|
| 53 |
+
QUALCOMM = 0x5143
|
| 54 |
+
INTEL = 0x8086
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def get_vendor_id_for_device_type(device_type: str) -> OrtDeviceVendorId | None:
|
| 58 |
+
if device_type == "cuda":
|
| 59 |
+
return OrtDeviceVendorId.NVIDIA
|
| 60 |
+
elif device_type == "dml":
|
| 61 |
+
return OrtDeviceVendorId.MICROSOFT
|
| 62 |
+
elif device_type == "cann":
|
| 63 |
+
return OrtDeviceVendorId.HUAWEI
|
| 64 |
+
elif device_type == "webgpu":
|
| 65 |
+
return OrtDeviceVendorId.NONE
|
| 66 |
+
else:
|
| 67 |
+
return None
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
_GPU_GRAPH_ID_RUN_CONFIG_KEY = "gpu_graph_id"
|
| 71 |
+
# Mirrors InferenceSession::kGraphAnnotationSkip; core skips capture and replay for this ID.
|
| 72 |
+
_GRAPH_ANNOTATION_SKIP = -1
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def _graph_annotation_id(run_options) -> int:
|
| 76 |
+
"""Return the effective ``gpu_graph_id`` for a run, defaulting to 0 when unset."""
|
| 77 |
+
if run_options is None:
|
| 78 |
+
return 0
|
| 79 |
+
try:
|
| 80 |
+
entry = run_options.get_run_config_entry(_GPU_GRAPH_ID_RUN_CONFIG_KEY)
|
| 81 |
+
except RuntimeError:
|
| 82 |
+
return 0
|
| 83 |
+
if not entry:
|
| 84 |
+
return 0
|
| 85 |
+
try:
|
| 86 |
+
return int(entry)
|
| 87 |
+
except ValueError:
|
| 88 |
+
raise ValueError(f"Run option '{_GPU_GRAPH_ID_RUN_CONFIG_KEY}' must be an integer, got {entry!r}.") from None
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _is_ortvalue_session_compatible(ortvalue, target_session) -> bool:
|
| 92 |
+
"""Whether ``target_session`` can share ``ortvalue``'s buffer.
|
| 93 |
+
|
| 94 |
+
Sessionless values come from a shared allocator. A session-owned WebGPU buffer is
|
| 95 |
+
usable by any session on the same WebGPU context.
|
| 96 |
+
"""
|
| 97 |
+
if ortvalue._session is None or target_session is None or ortvalue._session is target_session:
|
| 98 |
+
return True
|
| 99 |
+
return ortvalue._is_webgpu_buffer and ortvalue._session.webgpu_context_id() == target_session.webgpu_context_id()
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def _validate_ortvalue_session_compatibility(ortvalue, target_session, action) -> None:
|
| 103 |
+
"""Reject an OrtValue that the target session cannot share buffers with.
|
| 104 |
+
|
| 105 |
+
``action`` is the verb used in the message, e.g. ``"used with"`` or ``"bound to"``.
|
| 106 |
+
"""
|
| 107 |
+
if not _is_ortvalue_session_compatible(ortvalue, target_session):
|
| 108 |
+
raise ValueError(f"Session-scoped OrtValue must be {action} the session that created it.")
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
class AdapterFormat:
|
| 112 |
+
"""
|
| 113 |
+
This class is used to create adapter files from python structures
|
| 114 |
+
"""
|
| 115 |
+
|
| 116 |
+
def __init__(self, adapter=None) -> None:
|
| 117 |
+
if adapter is None:
|
| 118 |
+
self._adapter = C.AdapterFormat()
|
| 119 |
+
else:
|
| 120 |
+
self._adapter = adapter
|
| 121 |
+
|
| 122 |
+
@staticmethod
|
| 123 |
+
def read_adapter(file_path: os.PathLike) -> AdapterFormat:
|
| 124 |
+
return AdapterFormat(C.AdapterFormat.read_adapter(file_path))
|
| 125 |
+
|
| 126 |
+
def export_adapter(self, file_path: os.PathLike):
|
| 127 |
+
"""
|
| 128 |
+
This function writes a file at the specified location
|
| 129 |
+
in onnxrunitme adapter format containing Lora parameters.
|
| 130 |
+
|
| 131 |
+
:param file_path: absolute path for the adapter
|
| 132 |
+
"""
|
| 133 |
+
self._adapter.export_adapter(file_path)
|
| 134 |
+
|
| 135 |
+
def get_format_version(self) -> int:
|
| 136 |
+
return self._adapter.format_version
|
| 137 |
+
|
| 138 |
+
def set_adapter_version(self, adapter_version: int) -> None:
|
| 139 |
+
self._adapter.adapter_version = adapter_version
|
| 140 |
+
|
| 141 |
+
def get_adapter_version(self) -> int:
|
| 142 |
+
return self._adapter.adapter_version
|
| 143 |
+
|
| 144 |
+
def set_model_version(self, model_version: int) -> None:
|
| 145 |
+
self._adapter.model_version = model_version
|
| 146 |
+
|
| 147 |
+
def get_model_version(self) -> int:
|
| 148 |
+
return self._adapter.model_version
|
| 149 |
+
|
| 150 |
+
def set_parameters(self, params: dict[str, OrtValue]) -> None:
|
| 151 |
+
"""Set adapter parameters for export."""
|
| 152 |
+
self._adapter.parameters = {k: v._ortvalue for k, v in params.items()}
|
| 153 |
+
|
| 154 |
+
def get_parameters(self) -> dict[str, OrtValue]:
|
| 155 |
+
"""Get adapter parameters as a dict of name -> OrtValue.
|
| 156 |
+
|
| 157 |
+
On read instances, the returned OrtValues are zero-copy views; the
|
| 158 |
+
backing memory stays alive as long as any returned OrtValue is referenced.
|
| 159 |
+
"""
|
| 160 |
+
return {k: OrtValue(v) for k, v in self._adapter.parameters.items()}
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def check_and_normalize_provider_args(
|
| 164 |
+
providers: Sequence[str | tuple[str, dict[Any, Any]]] | None,
|
| 165 |
+
provider_options: Sequence[dict[Any, Any]] | None,
|
| 166 |
+
available_provider_names: Sequence[str],
|
| 167 |
+
):
|
| 168 |
+
"""
|
| 169 |
+
Validates the 'providers' and 'provider_options' arguments and returns a
|
| 170 |
+
normalized version.
|
| 171 |
+
|
| 172 |
+
:param providers: Optional sequence of providers in order of decreasing
|
| 173 |
+
precedence. Values can either be provider names or tuples of
|
| 174 |
+
(provider name, options dict).
|
| 175 |
+
:param provider_options: Optional sequence of options dicts corresponding
|
| 176 |
+
to the providers listed in 'providers'.
|
| 177 |
+
:param available_provider_names: The available provider names.
|
| 178 |
+
|
| 179 |
+
:return: Tuple of (normalized 'providers' sequence, normalized
|
| 180 |
+
'provider_options' sequence).
|
| 181 |
+
|
| 182 |
+
'providers' can contain either names or names and options. When any options
|
| 183 |
+
are given in 'providers', 'provider_options' should not be used.
|
| 184 |
+
|
| 185 |
+
The normalized result is a tuple of:
|
| 186 |
+
1. Sequence of provider names in the same order as 'providers'.
|
| 187 |
+
2. Sequence of corresponding provider options dicts with string keys and
|
| 188 |
+
values. Unspecified provider options yield empty dicts.
|
| 189 |
+
"""
|
| 190 |
+
if providers is None:
|
| 191 |
+
return [], []
|
| 192 |
+
|
| 193 |
+
provider_name_to_options = collections.OrderedDict()
|
| 194 |
+
|
| 195 |
+
def set_provider_options(name, options):
|
| 196 |
+
if name not in available_provider_names:
|
| 197 |
+
warnings.warn(
|
| 198 |
+
"Specified provider '{}' is not in available provider names.Available providers: '{}'".format(
|
| 199 |
+
name, ", ".join(available_provider_names)
|
| 200 |
+
)
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
if name in provider_name_to_options:
|
| 204 |
+
warnings.warn(f"Duplicate provider '{name}' encountered, ignoring.")
|
| 205 |
+
return
|
| 206 |
+
|
| 207 |
+
normalized_options = {str(key): str(value) for key, value in options.items()}
|
| 208 |
+
provider_name_to_options[name] = normalized_options
|
| 209 |
+
|
| 210 |
+
if not isinstance(providers, collections.abc.Sequence):
|
| 211 |
+
raise ValueError("'providers' should be a sequence.")
|
| 212 |
+
|
| 213 |
+
if provider_options is not None:
|
| 214 |
+
if not isinstance(provider_options, collections.abc.Sequence):
|
| 215 |
+
raise ValueError("'provider_options' should be a sequence.")
|
| 216 |
+
|
| 217 |
+
if len(providers) != len(provider_options):
|
| 218 |
+
raise ValueError("'providers' and 'provider_options' should be the same length if both are given.")
|
| 219 |
+
|
| 220 |
+
if not all(isinstance(provider, str) for provider in providers):
|
| 221 |
+
raise ValueError("Only string values for 'providers' are supported if 'provider_options' is given.")
|
| 222 |
+
|
| 223 |
+
if not all(isinstance(options_for_provider, dict) for options_for_provider in provider_options):
|
| 224 |
+
raise ValueError("'provider_options' values must be dicts.")
|
| 225 |
+
|
| 226 |
+
for name, options in zip(providers, provider_options, strict=False):
|
| 227 |
+
set_provider_options(name, options)
|
| 228 |
+
|
| 229 |
+
else:
|
| 230 |
+
for provider in providers:
|
| 231 |
+
if isinstance(provider, str):
|
| 232 |
+
set_provider_options(provider, {})
|
| 233 |
+
elif (
|
| 234 |
+
isinstance(provider, tuple)
|
| 235 |
+
and len(provider) == 2
|
| 236 |
+
and isinstance(provider[0], str)
|
| 237 |
+
and isinstance(provider[1], dict)
|
| 238 |
+
):
|
| 239 |
+
set_provider_options(provider[0], provider[1])
|
| 240 |
+
else:
|
| 241 |
+
raise ValueError("'providers' values must be either strings or (string, dict) tuples.")
|
| 242 |
+
|
| 243 |
+
return list(provider_name_to_options.keys()), list(provider_name_to_options.values())
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
class Session:
|
| 247 |
+
"""
|
| 248 |
+
This is the main class used to run a model.
|
| 249 |
+
"""
|
| 250 |
+
|
| 251 |
+
def __init__(self, enable_fallback: bool = True):
|
| 252 |
+
# self._sess is managed by the derived class and relies on bindings from C.InferenceSession
|
| 253 |
+
self._sess = None
|
| 254 |
+
self._enable_fallback = enable_fallback
|
| 255 |
+
# Captured graphs retain buffer signatures without extending the IOBinding lifetime.
|
| 256 |
+
self._captured_graph_bindings: dict[int, tuple[weakref.ref, tuple]] = {}
|
| 257 |
+
|
| 258 |
+
def get_session_options(self) -> C.SessionOptions:
|
| 259 |
+
"Return the session options. See :class:`onnxruntime.SessionOptions`."
|
| 260 |
+
return self._sess_options
|
| 261 |
+
|
| 262 |
+
def get_inputs(self) -> Sequence[C.NodeArg]:
|
| 263 |
+
"Return the inputs metadata as a list of :class:`onnxruntime.NodeArg`."
|
| 264 |
+
return self._inputs_meta
|
| 265 |
+
|
| 266 |
+
def get_outputs(self) -> Sequence[C.NodeArg]:
|
| 267 |
+
"Return the outputs metadata as a list of :class:`onnxruntime.NodeArg`."
|
| 268 |
+
return self._outputs_meta
|
| 269 |
+
|
| 270 |
+
def get_overridable_initializers(self) -> Sequence[C.NodeArg]:
|
| 271 |
+
"Return the inputs (including initializers) metadata as a list of :class:`onnxruntime.NodeArg`."
|
| 272 |
+
return self._overridable_initializers
|
| 273 |
+
|
| 274 |
+
def get_modelmeta(self) -> C.ModelMetadata:
|
| 275 |
+
"Return the metadata. See :class:`onnxruntime.ModelMetadata`."
|
| 276 |
+
return self._model_meta
|
| 277 |
+
|
| 278 |
+
def get_input_memory_infos(self) -> Sequence[C.OrtMemoryInfo]:
|
| 279 |
+
"Return the memory info for the inputs."
|
| 280 |
+
return self._input_meminfos
|
| 281 |
+
|
| 282 |
+
def get_output_memory_infos(self) -> Sequence[C.OrtMemoryInfo]:
|
| 283 |
+
"Return the memory info for the outputs."
|
| 284 |
+
return self._output_meminfos
|
| 285 |
+
|
| 286 |
+
def get_input_epdevices(self) -> Sequence[C.OrtEpDevice]:
|
| 287 |
+
"Return the execution providers for the inputs."
|
| 288 |
+
return self._input_epdevices
|
| 289 |
+
|
| 290 |
+
def get_providers(self) -> Sequence[str]:
|
| 291 |
+
"Return list of registered execution providers."
|
| 292 |
+
return self._providers
|
| 293 |
+
|
| 294 |
+
def get_provider_options(self):
|
| 295 |
+
"Return registered execution providers' configurations."
|
| 296 |
+
return self._provider_options
|
| 297 |
+
|
| 298 |
+
def get_provider_graph_assignment_info(self) -> Sequence[C.OrtEpAssignedSubgraph]:
|
| 299 |
+
"""
|
| 300 |
+
Get information about the subgraphs assigned to each execution provider and the nodes within.
|
| 301 |
+
|
| 302 |
+
Application must enable the recording of graph assignment information by setting the session configuration
|
| 303 |
+
for the key "session.record_ep_graph_assignment_info" to "1".
|
| 304 |
+
"""
|
| 305 |
+
return self._sess.get_provider_graph_assignment_info()
|
| 306 |
+
|
| 307 |
+
def set_providers(self, providers=None, provider_options=None) -> None:
|
| 308 |
+
"""
|
| 309 |
+
Register the input list of execution providers. The underlying session is re-created.
|
| 310 |
+
|
| 311 |
+
:param providers: Optional sequence of providers in order of decreasing
|
| 312 |
+
precedence. Values can either be provider names or tuples of
|
| 313 |
+
(provider name, options dict). If not provided, then all available
|
| 314 |
+
providers are used with the default precedence.
|
| 315 |
+
:param provider_options: Optional sequence of options dicts corresponding
|
| 316 |
+
to the providers listed in 'providers'.
|
| 317 |
+
|
| 318 |
+
'providers' can contain either names or names and options. When any options
|
| 319 |
+
are given in 'providers', 'provider_options' should not be used.
|
| 320 |
+
|
| 321 |
+
The list of providers is ordered by precedence. For example
|
| 322 |
+
`['CUDAExecutionProvider', 'CPUExecutionProvider']`
|
| 323 |
+
means execute a node using CUDAExecutionProvider if capable,
|
| 324 |
+
otherwise execute using CPUExecutionProvider.
|
| 325 |
+
"""
|
| 326 |
+
# recreate the underlying C.InferenceSession
|
| 327 |
+
self._reset_session(providers, provider_options)
|
| 328 |
+
|
| 329 |
+
def disable_fallback(self) -> None:
|
| 330 |
+
"""
|
| 331 |
+
Disable session.run() fallback mechanism.
|
| 332 |
+
"""
|
| 333 |
+
self._enable_fallback = False
|
| 334 |
+
|
| 335 |
+
def enable_fallback(self) -> None:
|
| 336 |
+
"""
|
| 337 |
+
Enable session.Run() fallback mechanism. If session.Run() fails due to an internal Execution Provider failure,
|
| 338 |
+
reset the Execution Providers enabled for this session.
|
| 339 |
+
If GPU is enabled, fall back to CUDAExecutionProvider.
|
| 340 |
+
otherwise fall back to CPUExecutionProvider.
|
| 341 |
+
"""
|
| 342 |
+
self._enable_fallback = True
|
| 343 |
+
|
| 344 |
+
def _validate_input(self, feed_input_names):
|
| 345 |
+
missing_input_names = []
|
| 346 |
+
for input in self._inputs_meta:
|
| 347 |
+
if input.name not in feed_input_names and not input.type.startswith("optional"):
|
| 348 |
+
missing_input_names.append(input.name)
|
| 349 |
+
if missing_input_names:
|
| 350 |
+
raise ValueError(
|
| 351 |
+
f"Required inputs ({missing_input_names}) are missing from input feed ({feed_input_names})."
|
| 352 |
+
)
|
| 353 |
+
|
| 354 |
+
def _validate_ortvalue_ownership(self, values):
|
| 355 |
+
for value in values:
|
| 356 |
+
if not isinstance(value, OrtValue):
|
| 357 |
+
continue
|
| 358 |
+
_validate_ortvalue_session_compatibility(value, self._sess, "used with")
|
| 359 |
+
|
| 360 |
+
def _validate_graph_capture_run_api(self, run_options=None):
|
| 361 |
+
if not self._sess.is_webgpu_graph_capture_enabled():
|
| 362 |
+
return
|
| 363 |
+
if _graph_annotation_id(run_options) == _GRAPH_ANNOTATION_SKIP:
|
| 364 |
+
# gpu_graph_id=-1 skips capture, so transient feeds remain valid.
|
| 365 |
+
return
|
| 366 |
+
raise ValueError(
|
| 367 |
+
"WebGPU graph capture requires fixed device OrtValues and run_with_iobinding. "
|
| 368 |
+
f"Set the '{_GPU_GRAPH_ID_RUN_CONFIG_KEY}' run option to "
|
| 369 |
+
f"{_GRAPH_ANNOTATION_SKIP} to opt a single run out of capture."
|
| 370 |
+
)
|
| 371 |
+
|
| 372 |
+
def run(self, output_names, input_feed, run_options=None) -> Sequence[np.ndarray | SparseTensor | list | dict]:
|
| 373 |
+
"""
|
| 374 |
+
Compute the predictions.
|
| 375 |
+
|
| 376 |
+
WebGPU graph capture requires fixed device OrtValues bound with
|
| 377 |
+
:meth:`run_with_iobinding`; do not use this convenience API for captured replay.
|
| 378 |
+
|
| 379 |
+
:param output_names: name of the outputs
|
| 380 |
+
:param input_feed: dictionary ``{ input_name: input_value }``
|
| 381 |
+
:param run_options: See :class:`onnxruntime.RunOptions`.
|
| 382 |
+
:return: list of results, every result is either a numpy array,
|
| 383 |
+
a sparse tensor, a list or a dictionary.
|
| 384 |
+
|
| 385 |
+
::
|
| 386 |
+
|
| 387 |
+
sess.run([output_name], {input_name: x})
|
| 388 |
+
"""
|
| 389 |
+
self._validate_graph_capture_run_api(run_options)
|
| 390 |
+
self._validate_input(list(input_feed.keys()))
|
| 391 |
+
self._validate_ortvalue_ownership(input_feed.values())
|
| 392 |
+
if not output_names:
|
| 393 |
+
output_names = [output.name for output in self._outputs_meta]
|
| 394 |
+
try:
|
| 395 |
+
return self._sess.run(output_names, input_feed, run_options)
|
| 396 |
+
except C.EPFail as err:
|
| 397 |
+
if self._enable_fallback:
|
| 398 |
+
print(f"EP Error: {err!s} using {self._providers}")
|
| 399 |
+
print(f"Falling back to {self._fallback_providers} and retrying.")
|
| 400 |
+
self.set_providers(self._fallback_providers)
|
| 401 |
+
# Fallback only once.
|
| 402 |
+
self.disable_fallback()
|
| 403 |
+
self._validate_ortvalue_ownership(input_feed.values())
|
| 404 |
+
return self._sess.run(output_names, input_feed, run_options)
|
| 405 |
+
raise
|
| 406 |
+
|
| 407 |
+
def run_async(self, output_names, input_feed, callback, user_data, run_options=None):
|
| 408 |
+
"""
|
| 409 |
+
Compute the predictions asynchronously in a separate cxx thread from ort intra-op threadpool.
|
| 410 |
+
|
| 411 |
+
:param output_names: name of the outputs
|
| 412 |
+
:param input_feed: dictionary ``{ input_name: input_value }``
|
| 413 |
+
:param callback: python function that accept array of results, and a status string on error.
|
| 414 |
+
The callback will be invoked by a cxx thread from ort intra-op threadpool.
|
| 415 |
+
:param run_options: See :class:`onnxruntime.RunOptions`.
|
| 416 |
+
|
| 417 |
+
::
|
| 418 |
+
class MyData:
|
| 419 |
+
def __init__(self):
|
| 420 |
+
# ...
|
| 421 |
+
def save_results(self, results):
|
| 422 |
+
# ...
|
| 423 |
+
|
| 424 |
+
def callback(results: np.ndarray, user_data: MyData, err: str) -> None:
|
| 425 |
+
if err:
|
| 426 |
+
print (err)
|
| 427 |
+
else:
|
| 428 |
+
# save results to user_data
|
| 429 |
+
|
| 430 |
+
sess.run_async([output_name], {input_name: x}, callback)
|
| 431 |
+
"""
|
| 432 |
+
self._validate_graph_capture_run_api(run_options)
|
| 433 |
+
self._validate_input(list(input_feed.keys()))
|
| 434 |
+
self._validate_ortvalue_ownership(input_feed.values())
|
| 435 |
+
if not output_names:
|
| 436 |
+
output_names = [output.name for output in self._outputs_meta]
|
| 437 |
+
return self._sess.run_async(output_names, input_feed, callback, user_data, run_options)
|
| 438 |
+
|
| 439 |
+
def run_with_ort_values(self, output_names, input_dict_ort_values, run_options=None) -> Sequence[OrtValue]:
|
| 440 |
+
"""
|
| 441 |
+
Compute the predictions.
|
| 442 |
+
|
| 443 |
+
:param output_names: name of the outputs
|
| 444 |
+
:param input_dict_ort_values: dictionary ``{ input_name: input_ort_value }``
|
| 445 |
+
See ``OrtValue`` class how to create `OrtValue`
|
| 446 |
+
from numpy array or `SparseTensor`
|
| 447 |
+
:param run_options: See :class:`onnxruntime.RunOptions`.
|
| 448 |
+
:return: an array of `OrtValue`
|
| 449 |
+
|
| 450 |
+
::
|
| 451 |
+
|
| 452 |
+
sess.run([output_name], {input_name: x})
|
| 453 |
+
"""
|
| 454 |
+
|
| 455 |
+
def invoke(sess, output_names, input_dict_ort_values, run_options):
|
| 456 |
+
input_dict = {}
|
| 457 |
+
for n, v in input_dict_ort_values.items():
|
| 458 |
+
input_dict[n] = v._get_c_value()
|
| 459 |
+
result = sess.run_with_ort_values(input_dict, output_names, run_options)
|
| 460 |
+
if not isinstance(result, C.OrtValueVector):
|
| 461 |
+
raise TypeError("run_with_ort_values() must return a instance of type 'OrtValueVector'.")
|
| 462 |
+
ort_values = [OrtValue(v) for v in result]
|
| 463 |
+
return ort_values
|
| 464 |
+
|
| 465 |
+
self._validate_graph_capture_run_api(run_options)
|
| 466 |
+
self._validate_input(list(input_dict_ort_values.keys()))
|
| 467 |
+
self._validate_ortvalue_ownership(input_dict_ort_values.values())
|
| 468 |
+
if not output_names:
|
| 469 |
+
output_names = [output.name for output in self._outputs_meta]
|
| 470 |
+
try:
|
| 471 |
+
return invoke(self._sess, output_names, input_dict_ort_values, run_options)
|
| 472 |
+
except C.EPFail as err:
|
| 473 |
+
if self._enable_fallback:
|
| 474 |
+
print(f"EP Error: {err!s} using {self._providers}")
|
| 475 |
+
print(f"Falling back to {self._fallback_providers} and retrying.")
|
| 476 |
+
self.set_providers(self._fallback_providers)
|
| 477 |
+
# Fallback only once.
|
| 478 |
+
self.disable_fallback()
|
| 479 |
+
self._validate_ortvalue_ownership(input_dict_ort_values.values())
|
| 480 |
+
return invoke(self._sess, output_names, input_dict_ort_values, run_options)
|
| 481 |
+
raise
|
| 482 |
+
|
| 483 |
+
def end_profiling(self):
|
| 484 |
+
"""
|
| 485 |
+
End profiling and return results in a file.
|
| 486 |
+
|
| 487 |
+
The results are stored in a filename if the option
|
| 488 |
+
:meth:`onnxruntime.SessionOptions.enable_profiling`.
|
| 489 |
+
"""
|
| 490 |
+
return self._sess.end_profiling()
|
| 491 |
+
|
| 492 |
+
def get_profiling_start_time_ns(self):
|
| 493 |
+
"""
|
| 494 |
+
Return the nanoseconds of profiling's start time
|
| 495 |
+
Comparable to time.monotonic_ns() after Python 3.3
|
| 496 |
+
On some platforms, this timer may not be as precise as nanoseconds
|
| 497 |
+
For instance, on Windows and MacOS, the precision will be ~100ns
|
| 498 |
+
"""
|
| 499 |
+
return self._sess.get_profiling_start_time_ns
|
| 500 |
+
|
| 501 |
+
def io_binding(self) -> IOBinding:
|
| 502 |
+
"Return an onnxruntime.IOBinding object`."
|
| 503 |
+
return IOBinding(self)
|
| 504 |
+
|
| 505 |
+
def create_ortvalue_from_shape_and_type(
|
| 506 |
+
self,
|
| 507 |
+
shape: Sequence[int],
|
| 508 |
+
element_type,
|
| 509 |
+
device_type: str,
|
| 510 |
+
device_id: int = 0,
|
| 511 |
+
vendor_id: int | OrtDeviceVendorId = -1,
|
| 512 |
+
) -> OrtValue:
|
| 513 |
+
"""Create an OrtValue using this session's allocator.
|
| 514 |
+
|
| 515 |
+
The value retains this session and may only be updated or bound through it.
|
| 516 |
+
A matching allocator and a numeric tensor type are required.
|
| 517 |
+
"""
|
| 518 |
+
device = OrtDevice.make(device_type, device_id, vendor_id)._get_c_device()
|
| 519 |
+
if isinstance(element_type, int):
|
| 520 |
+
ortvalue = self._sess.create_ortvalue_from_shape_and_onnx_type(shape, element_type, device)
|
| 521 |
+
else:
|
| 522 |
+
ortvalue = self._sess.create_ortvalue_from_shape_and_type(shape, element_type, device)
|
| 523 |
+
return OrtValue(ortvalue, session=self._sess)
|
| 524 |
+
|
| 525 |
+
def release_captured_graph(self, graph_annotation_id: int = 0) -> None:
|
| 526 |
+
"""Release a captured graph and unpin its IOBinding.
|
| 527 |
+
|
| 528 |
+
``graph_annotation_id`` matches the capture's ``gpu_graph_id`` and defaults to zero.
|
| 529 |
+
EPs without captured-graph release support treat this as a no-op.
|
| 530 |
+
"""
|
| 531 |
+
self._sess.release_captured_graph(graph_annotation_id)
|
| 532 |
+
pinned = self._captured_graph_bindings.pop(graph_annotation_id, None)
|
| 533 |
+
if pinned is not None:
|
| 534 |
+
iobinding = pinned[0]()
|
| 535 |
+
if iobinding is not None:
|
| 536 |
+
iobinding._pinned_graph_ids.discard(graph_annotation_id)
|
| 537 |
+
|
| 538 |
+
def run_with_iobinding(self, iobinding, run_options=None):
|
| 539 |
+
"""
|
| 540 |
+
Compute the predictions.
|
| 541 |
+
|
| 542 |
+
:param iobinding: the iobinding object that has graph inputs/outputs bind.
|
| 543 |
+
:param run_options: See :class:`onnxruntime.RunOptions`.
|
| 544 |
+
|
| 545 |
+
WebGPU capture requires static shapes, disabled memory patterns, fixed WebGPU OrtValues
|
| 546 |
+
(session-owned or shared), and no CPU compute nodes other than shape-only nodes. Each
|
| 547 |
+
``gpu_graph_id`` pins one IOBinding and its buffers until :meth:`release_captured_graph`.
|
| 548 |
+
Update inputs in place and use :meth:`IOBinding.copy_outputs_to_cpu` for readback;
|
| 549 |
+
``gpu_graph_id=-1`` disables capture.
|
| 550 |
+
"""
|
| 551 |
+
if iobinding._session is not self._sess:
|
| 552 |
+
raise ValueError("IOBinding must be used with the session that created it.")
|
| 553 |
+
|
| 554 |
+
graph_annotation_id = _graph_annotation_id(run_options)
|
| 555 |
+
capturing = self._sess.is_webgpu_graph_capture_enabled() and graph_annotation_id != _GRAPH_ANNOTATION_SKIP
|
| 556 |
+
if capturing:
|
| 557 |
+
iobinding._validate_capture_bindings()
|
| 558 |
+
signature = iobinding._capture_signature()
|
| 559 |
+
pinned = self._captured_graph_bindings.get(graph_annotation_id)
|
| 560 |
+
if pinned is not None:
|
| 561 |
+
pinned_iobinding, pinned_signature = pinned[0](), pinned[1]
|
| 562 |
+
if pinned_iobinding is not iobinding:
|
| 563 |
+
raise ValueError(
|
| 564 |
+
f"WebGPU graph {graph_annotation_id} was captured with a different "
|
| 565 |
+
"IOBinding. Replay re-issues the buffers recorded at capture and would "
|
| 566 |
+
"silently ignore this binding. Reuse the original IOBinding, or call "
|
| 567 |
+
f"release_captured_graph({graph_annotation_id}) first."
|
| 568 |
+
)
|
| 569 |
+
if pinned_signature != signature:
|
| 570 |
+
raise ValueError(
|
| 571 |
+
f"WebGPU graph {graph_annotation_id} was captured with different I/O "
|
| 572 |
+
"buffers. Replay writes to the buffers recorded at capture, so the "
|
| 573 |
+
"rebound values would never be read or written. Update the original "
|
| 574 |
+
"buffers in place, or call "
|
| 575 |
+
f"release_captured_graph({graph_annotation_id}) first."
|
| 576 |
+
)
|
| 577 |
+
|
| 578 |
+
self._sess.run_with_iobinding(iobinding._iobinding, run_options)
|
| 579 |
+
|
| 580 |
+
if capturing and graph_annotation_id not in self._captured_graph_bindings:
|
| 581 |
+
self._captured_graph_bindings[graph_annotation_id] = (weakref.ref(iobinding), signature)
|
| 582 |
+
iobinding._pinned_graph_ids.add(graph_annotation_id)
|
| 583 |
+
|
| 584 |
+
def set_ep_dynamic_options(self, options: dict[str, str]):
|
| 585 |
+
"""
|
| 586 |
+
Set dynamic options for execution providers.
|
| 587 |
+
|
| 588 |
+
:param options: Dictionary of key-value pairs where both keys and values are strings.
|
| 589 |
+
These options will be passed to the execution providers to modify
|
| 590 |
+
their runtime behavior.
|
| 591 |
+
"""
|
| 592 |
+
self._sess.set_ep_dynamic_options(options)
|
| 593 |
+
|
| 594 |
+
def get_tuning_results(self):
|
| 595 |
+
return self._sess.get_tuning_results()
|
| 596 |
+
|
| 597 |
+
def set_tuning_results(self, results, *, error_on_invalid=False):
|
| 598 |
+
return self._sess.set_tuning_results(results, error_on_invalid)
|
| 599 |
+
|
| 600 |
+
def run_with_ortvaluevector(self, run_options, feed_names, feeds, fetch_names, fetches, fetch_devices):
|
| 601 |
+
"""
|
| 602 |
+
Compute the predictions similar to other run_*() methods but with minimal C++/Python conversion overhead.
|
| 603 |
+
|
| 604 |
+
:param run_options: See :class:`onnxruntime.RunOptions`.
|
| 605 |
+
:param feed_names: list of input names.
|
| 606 |
+
:param feeds: list of input OrtValue.
|
| 607 |
+
:param fetch_names: list of output names.
|
| 608 |
+
:param fetches: list of output OrtValue.
|
| 609 |
+
:param fetch_devices: list of output devices.
|
| 610 |
+
"""
|
| 611 |
+
# Same capture check as run(), run_with_ort_values() and run_async(): replay re-issues the
|
| 612 |
+
# buffers recorded at capture, so transient vectors would be silently ignored. Nothing about
|
| 613 |
+
# a raw vector is unsafe outside capture, including on a WebGPU session.
|
| 614 |
+
self._validate_graph_capture_run_api(run_options)
|
| 615 |
+
self._sess.run_with_ortvaluevector(run_options, feed_names, feeds, fetch_names, fetches, fetch_devices)
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
class InferenceSession(Session):
|
| 619 |
+
"""
|
| 620 |
+
This is the main class used to run a model.
|
| 621 |
+
"""
|
| 622 |
+
|
| 623 |
+
def __init__(
|
| 624 |
+
self,
|
| 625 |
+
path_or_bytes: str | bytes | os.PathLike,
|
| 626 |
+
sess_options: C.SessionOptions | None = None,
|
| 627 |
+
providers: Sequence[str | tuple[str, dict[Any, Any]]] | None = None,
|
| 628 |
+
provider_options: Sequence[dict[Any, Any]] | None = None,
|
| 629 |
+
**kwargs,
|
| 630 |
+
) -> None:
|
| 631 |
+
"""
|
| 632 |
+
:param path_or_bytes: Filename or serialized ONNX or ORT format model in a byte string.
|
| 633 |
+
:param sess_options: Session options.
|
| 634 |
+
:param providers: Optional sequence of providers in order of decreasing
|
| 635 |
+
precedence. Values can either be provider names or tuples of
|
| 636 |
+
(provider name, options dict). If not provided, then all available
|
| 637 |
+
providers are used with the default precedence.
|
| 638 |
+
:param provider_options: Optional sequence of options dicts corresponding
|
| 639 |
+
to the providers listed in 'providers'.
|
| 640 |
+
|
| 641 |
+
The model type will be inferred unless explicitly set in the SessionOptions.
|
| 642 |
+
To explicitly set:
|
| 643 |
+
|
| 644 |
+
::
|
| 645 |
+
|
| 646 |
+
so = onnxruntime.SessionOptions()
|
| 647 |
+
# so.add_session_config_entry('session.load_model_format', 'ONNX') or
|
| 648 |
+
so.add_session_config_entry('session.load_model_format', 'ORT')
|
| 649 |
+
|
| 650 |
+
A file extension of '.ort' will be inferred as an ORT format model.
|
| 651 |
+
All other filenames are assumed to be ONNX format models.
|
| 652 |
+
|
| 653 |
+
'providers' can contain either names or names and options. When any options
|
| 654 |
+
are given in 'providers', 'provider_options' should not be used.
|
| 655 |
+
|
| 656 |
+
The list of providers is ordered by precedence. For example
|
| 657 |
+
`['CUDAExecutionProvider', 'CPUExecutionProvider']`
|
| 658 |
+
means execute a node using `CUDAExecutionProvider`
|
| 659 |
+
if capable, otherwise execute using `CPUExecutionProvider`.
|
| 660 |
+
"""
|
| 661 |
+
super().__init__(enable_fallback=int(kwargs.get("enable_fallback", 1)) == 1)
|
| 662 |
+
|
| 663 |
+
if isinstance(path_or_bytes, (str, os.PathLike)):
|
| 664 |
+
self._model_path = os.fspath(path_or_bytes)
|
| 665 |
+
self._model_bytes = None
|
| 666 |
+
elif isinstance(path_or_bytes, bytes):
|
| 667 |
+
self._model_path = None
|
| 668 |
+
self._model_bytes = path_or_bytes # TODO: This is bad as we're holding the memory indefinitely
|
| 669 |
+
else:
|
| 670 |
+
raise TypeError(f"Unable to load from type '{type(path_or_bytes)}'")
|
| 671 |
+
|
| 672 |
+
self._sess_options = sess_options
|
| 673 |
+
self._sess_options_initial = sess_options
|
| 674 |
+
if "read_config_from_model" in kwargs:
|
| 675 |
+
self._read_config_from_model = int(kwargs["read_config_from_model"]) == 1
|
| 676 |
+
else:
|
| 677 |
+
self._read_config_from_model = os.environ.get("ORT_LOAD_CONFIG_FROM_MODEL") == "1"
|
| 678 |
+
|
| 679 |
+
# internal parameters that we don't expect to be used in general so aren't documented
|
| 680 |
+
disabled_optimizers = kwargs.get("disabled_optimizers")
|
| 681 |
+
|
| 682 |
+
try:
|
| 683 |
+
self._create_inference_session(providers, provider_options, disabled_optimizers)
|
| 684 |
+
except (ValueError, RuntimeError) as e:
|
| 685 |
+
if self._enable_fallback:
|
| 686 |
+
try:
|
| 687 |
+
print("*************** EP Error ***************")
|
| 688 |
+
print(f"EP Error {e} when using {providers}")
|
| 689 |
+
print(f"Falling back to {self._fallback_providers} and retrying.")
|
| 690 |
+
print("****************************************")
|
| 691 |
+
self._create_inference_session(self._fallback_providers, None)
|
| 692 |
+
# Fallback only once.
|
| 693 |
+
self.disable_fallback()
|
| 694 |
+
return
|
| 695 |
+
except Exception as fallback_error:
|
| 696 |
+
raise fallback_error from e
|
| 697 |
+
# Fallback is disabled. Raise the original error.
|
| 698 |
+
raise e
|
| 699 |
+
|
| 700 |
+
def _create_inference_session(self, providers, provider_options, disabled_optimizers=None):
|
| 701 |
+
available_providers = C.get_available_providers()
|
| 702 |
+
|
| 703 |
+
# Validate that TensorrtExecutionProvider and NvTensorRTRTXExecutionProvider are not both specified
|
| 704 |
+
if providers:
|
| 705 |
+
has_tensorrt = any(
|
| 706 |
+
provider == "TensorrtExecutionProvider"
|
| 707 |
+
or (isinstance(provider, tuple) and provider[0] == "TensorrtExecutionProvider")
|
| 708 |
+
for provider in providers
|
| 709 |
+
)
|
| 710 |
+
has_tensorrt_rtx = any(
|
| 711 |
+
provider == "NvTensorRTRTXExecutionProvider"
|
| 712 |
+
or (isinstance(provider, tuple) and provider[0] == "NvTensorRTRTXExecutionProvider")
|
| 713 |
+
for provider in providers
|
| 714 |
+
)
|
| 715 |
+
if has_tensorrt and has_tensorrt_rtx:
|
| 716 |
+
raise ValueError(
|
| 717 |
+
"Cannot enable both 'TensorrtExecutionProvider' and 'NvTensorRTRTXExecutionProvider' "
|
| 718 |
+
"in the same session."
|
| 719 |
+
)
|
| 720 |
+
# Tensorrt and TensorRT RTX can fall back to CUDA if it's explicitly assigned. All others fall back to CPU.
|
| 721 |
+
if "NvTensorRTRTXExecutionProvider" in available_providers:
|
| 722 |
+
if (
|
| 723 |
+
providers
|
| 724 |
+
and any(
|
| 725 |
+
provider == "CUDAExecutionProvider"
|
| 726 |
+
or (isinstance(provider, tuple) and provider[0] == "CUDAExecutionProvider")
|
| 727 |
+
for provider in providers
|
| 728 |
+
)
|
| 729 |
+
and any(
|
| 730 |
+
provider == "NvTensorRTRTXExecutionProvider"
|
| 731 |
+
or (isinstance(provider, tuple) and provider[0] == "NvTensorRTRTXExecutionProvider")
|
| 732 |
+
for provider in providers
|
| 733 |
+
)
|
| 734 |
+
):
|
| 735 |
+
self._fallback_providers = ["CUDAExecutionProvider", "CPUExecutionProvider"]
|
| 736 |
+
else:
|
| 737 |
+
self._fallback_providers = ["CPUExecutionProvider"]
|
| 738 |
+
elif "TensorrtExecutionProvider" in available_providers:
|
| 739 |
+
if (
|
| 740 |
+
providers
|
| 741 |
+
and any(
|
| 742 |
+
provider == "CUDAExecutionProvider"
|
| 743 |
+
or (isinstance(provider, tuple) and provider[0] == "CUDAExecutionProvider")
|
| 744 |
+
for provider in providers
|
| 745 |
+
)
|
| 746 |
+
and any(
|
| 747 |
+
provider == "TensorrtExecutionProvider"
|
| 748 |
+
or (isinstance(provider, tuple) and provider[0] == "TensorrtExecutionProvider")
|
| 749 |
+
for provider in providers
|
| 750 |
+
)
|
| 751 |
+
):
|
| 752 |
+
self._fallback_providers = ["CUDAExecutionProvider", "CPUExecutionProvider"]
|
| 753 |
+
else:
|
| 754 |
+
self._fallback_providers = ["CPUExecutionProvider"]
|
| 755 |
+
else:
|
| 756 |
+
self._fallback_providers = ["CPUExecutionProvider"]
|
| 757 |
+
|
| 758 |
+
# validate providers and provider_options before other initialization
|
| 759 |
+
providers, provider_options = check_and_normalize_provider_args(
|
| 760 |
+
providers, provider_options, available_providers
|
| 761 |
+
)
|
| 762 |
+
|
| 763 |
+
# Print a warning if user passed providers to InferenceSession() but the SessionOptions instance
|
| 764 |
+
# already has provider information (e.g., via add_provider_for_devices()). The providers specified
|
| 765 |
+
# here will take precedence.
|
| 766 |
+
if self._sess_options is not None and (providers or provider_options) and self._sess_options.has_providers():
|
| 767 |
+
warnings.warn(
|
| 768 |
+
"Specified 'providers'/'provider_options' when creating InferenceSession but SessionOptions has "
|
| 769 |
+
"already been configured with providers. InferenceSession will only use the providers "
|
| 770 |
+
"passed to InferenceSession()."
|
| 771 |
+
)
|
| 772 |
+
|
| 773 |
+
session_options = self._sess_options if self._sess_options else C.get_default_session_options()
|
| 774 |
+
|
| 775 |
+
self._register_ep_custom_ops(session_options, providers, provider_options, available_providers)
|
| 776 |
+
|
| 777 |
+
if self._model_path:
|
| 778 |
+
sess = C.InferenceSession(session_options, self._model_path, True, self._read_config_from_model)
|
| 779 |
+
else:
|
| 780 |
+
sess = C.InferenceSession(session_options, self._model_bytes, False, self._read_config_from_model)
|
| 781 |
+
|
| 782 |
+
if disabled_optimizers is None:
|
| 783 |
+
disabled_optimizers = set()
|
| 784 |
+
elif not isinstance(disabled_optimizers, set):
|
| 785 |
+
# convert to set. assumes iterable
|
| 786 |
+
disabled_optimizers = set(disabled_optimizers)
|
| 787 |
+
|
| 788 |
+
# initialize the C++ InferenceSession
|
| 789 |
+
sess.initialize_session(providers, provider_options, disabled_optimizers)
|
| 790 |
+
|
| 791 |
+
self._sess = sess
|
| 792 |
+
self._sess_options = self._sess.session_options
|
| 793 |
+
self._inputs_meta = self._sess.inputs_meta
|
| 794 |
+
self._outputs_meta = self._sess.outputs_meta
|
| 795 |
+
self._overridable_initializers = self._sess.overridable_initializers
|
| 796 |
+
self._input_meminfos = self._sess.input_meminfos
|
| 797 |
+
self._output_meminfos = self._sess.output_meminfos
|
| 798 |
+
self._input_epdevices = self._sess.input_epdevices
|
| 799 |
+
self._model_meta = self._sess.model_meta
|
| 800 |
+
self._providers = self._sess.get_providers()
|
| 801 |
+
self._provider_options = self._sess.get_provider_options()
|
| 802 |
+
self._profiling_start_time_ns = self._sess.get_profiling_start_time_ns
|
| 803 |
+
|
| 804 |
+
def _release_captured_graphs(self) -> None:
|
| 805 |
+
"""Release every graph captured by the current session handle and unpin its IOBinding.
|
| 806 |
+
|
| 807 |
+
A captured graph belongs to the ``C.InferenceSession`` that captured it, so it must be
|
| 808 |
+
released before that handle is replaced. Otherwise the stale bookkeeping would reject an
|
| 809 |
+
IOBinding created by the replacement session and a later ``release_captured_graph`` would
|
| 810 |
+
act on the replacement session while unpinning the old binding.
|
| 811 |
+
"""
|
| 812 |
+
for graph_annotation_id in sorted(self._captured_graph_bindings):
|
| 813 |
+
self.release_captured_graph(graph_annotation_id)
|
| 814 |
+
|
| 815 |
+
def _reset_session(self, providers, provider_options) -> None:
|
| 816 |
+
"release underlying session object."
|
| 817 |
+
# Captured graphs outlive neither the session handle nor its bookkeeping. Release them
|
| 818 |
+
# first so a failure here leaves the session intact instead of half torn down.
|
| 819 |
+
self._release_captured_graphs()
|
| 820 |
+
|
| 821 |
+
# meta data references session internal structures
|
| 822 |
+
# so they must be set to None to decrement _sess reference count.
|
| 823 |
+
self._sess_options = None
|
| 824 |
+
self._inputs_meta = None
|
| 825 |
+
self._outputs_meta = None
|
| 826 |
+
self._overridable_initializers = None
|
| 827 |
+
self._input_meminfos = None
|
| 828 |
+
self._output_meminfos = None
|
| 829 |
+
self._input_epdevices = None
|
| 830 |
+
self._model_meta = None
|
| 831 |
+
self._providers = None
|
| 832 |
+
self._provider_options = None
|
| 833 |
+
self._profiling_start_time_ns = None
|
| 834 |
+
|
| 835 |
+
# create a new C.InferenceSession
|
| 836 |
+
self._sess = None
|
| 837 |
+
self._sess_options = self._sess_options_initial
|
| 838 |
+
self._create_inference_session(providers, provider_options)
|
| 839 |
+
|
| 840 |
+
def _register_ep_custom_ops(self, session_options, providers, provider_options, available_providers):
|
| 841 |
+
for i in range(len(providers)):
|
| 842 |
+
if providers[i] in available_providers and providers[i] == "TensorrtExecutionProvider":
|
| 843 |
+
C.register_tensorrt_plugins_as_custom_ops(session_options, provider_options[i])
|
| 844 |
+
elif (
|
| 845 |
+
isinstance(providers[i], tuple)
|
| 846 |
+
and providers[i][0] in available_providers
|
| 847 |
+
and providers[i][0] == "TensorrtExecutionProvider"
|
| 848 |
+
):
|
| 849 |
+
C.register_tensorrt_plugins_as_custom_ops(session_options, providers[i][1])
|
| 850 |
+
|
| 851 |
+
if providers[i] in available_providers and providers[i] == "NvTensorRTRTXExecutionProvider":
|
| 852 |
+
C.register_nv_tensorrt_rtx_plugins_as_custom_ops(session_options, provider_options[i])
|
| 853 |
+
elif (
|
| 854 |
+
isinstance(providers[i], tuple)
|
| 855 |
+
and providers[i][0] in available_providers
|
| 856 |
+
and providers[i][0] == "NvTensorrtRTXExecutionProvider"
|
| 857 |
+
):
|
| 858 |
+
C.register_nv_tensorrt_rtx_plugins_as_custom_ops(session_options, providers[i][1])
|
| 859 |
+
|
| 860 |
+
|
| 861 |
+
def make_get_initializer_location_func_wrapper(
|
| 862 |
+
get_initializer_location_func: GetInitializerLocationFunc,
|
| 863 |
+
) -> GetInitializerLocationWrapperFunc:
|
| 864 |
+
"""
|
| 865 |
+
Wraps a user's "get initializer location" function. The returned wrapper function adheres to the
|
| 866 |
+
signature expected by ORT.
|
| 867 |
+
|
| 868 |
+
Need this wrapper to:
|
| 869 |
+
- Convert the `initializer_value` parameter from `C.OrtValue` to `onnxruntime.OrtValue`, which is more
|
| 870 |
+
convenient for the user's function to use.
|
| 871 |
+
- Allow the user's function to return the original `external_info` parameter (this wrapper makes a copy)
|
| 872 |
+
"""
|
| 873 |
+
|
| 874 |
+
def get_initializer_location_func_wrapper(
|
| 875 |
+
initializer_name: str,
|
| 876 |
+
initializer_value: C.OrtValue,
|
| 877 |
+
external_info: C.OrtExternalInitializerInfo | None,
|
| 878 |
+
) -> C.OrtExternalInitializerInfo | None:
|
| 879 |
+
ret_val: C.OrtExternalInitializerInfo | None = get_initializer_location_func(
|
| 880 |
+
initializer_name, OrtValue(initializer_value), external_info
|
| 881 |
+
)
|
| 882 |
+
if ret_val is not None and ret_val == external_info:
|
| 883 |
+
# User returned `external_info` (const and owned by ORT). ORT expects the returned value to be
|
| 884 |
+
# a new instance (that it deletes), so make a copy.
|
| 885 |
+
ret_val = C.OrtExternalInitializerInfo(ret_val.filepath, ret_val.file_offset, ret_val.byte_size)
|
| 886 |
+
return ret_val
|
| 887 |
+
|
| 888 |
+
return get_initializer_location_func_wrapper
|
| 889 |
+
|
| 890 |
+
|
| 891 |
+
class ModelCompiler:
|
| 892 |
+
"""
|
| 893 |
+
This class is used to compile an ONNX model. A compiled ONNX model has EPContext nodes that each
|
| 894 |
+
encapsulates a subgraph compiled/optimized for a specific execution provider.
|
| 895 |
+
|
| 896 |
+
Refer to the EPContext design document for more information about EPContext models:
|
| 897 |
+
https://onnxruntime.ai/docs/execution-providers/EP-Context-Design.html
|
| 898 |
+
|
| 899 |
+
::
|
| 900 |
+
|
| 901 |
+
sess_options = onnxruntime.SessionOptions()
|
| 902 |
+
sess_options.add_provider("SomeExecutionProvider", {"option1": "value1"})
|
| 903 |
+
# Alternatively, allow ONNX Runtime to select the provider automatically given a policy:
|
| 904 |
+
# sess_options.set_provider_selection_policy(onnxrt.OrtExecutionProviderDevicePolicy.PREFER_NPU)
|
| 905 |
+
|
| 906 |
+
model_compiler = onnxruntime.ModelCompiler(sess_options, "input_model.onnx")
|
| 907 |
+
model_compiler.compile_to_file("output_model.onnx")
|
| 908 |
+
"""
|
| 909 |
+
|
| 910 |
+
def __init__(
|
| 911 |
+
self,
|
| 912 |
+
sess_options: C.SessionOptions,
|
| 913 |
+
input_model_path_or_bytes: str | os.PathLike | bytes,
|
| 914 |
+
embed_compiled_data_into_model: bool = False,
|
| 915 |
+
external_initializers_file_path: str | os.PathLike | None = None,
|
| 916 |
+
external_initializers_size_threshold: int = 1024,
|
| 917 |
+
flags: int = C.OrtCompileApiFlags.NONE,
|
| 918 |
+
graph_optimization_level: C.GraphOptimizationLevel = C.GraphOptimizationLevel.ORT_DISABLE_ALL,
|
| 919 |
+
get_initializer_location_func: GetInitializerLocationFunc | None = None,
|
| 920 |
+
):
|
| 921 |
+
"""
|
| 922 |
+
Creates a ModelCompiler instance.
|
| 923 |
+
|
| 924 |
+
:param sess_options: Session options containing the providers for which the model will be compiled.
|
| 925 |
+
Refer to SessionOptions.add_provider() and SessionOptions.set_provider_selection_policy().
|
| 926 |
+
:param input_model_path_or_bytes: The path to the input model file or bytes representing a serialized
|
| 927 |
+
ONNX model.
|
| 928 |
+
:param embed_compiled_data_into_model: Defaults to False. Set to True to embed compiled binary data into
|
| 929 |
+
EPContext nodes in the compiled model.
|
| 930 |
+
:param external_initializers_file_path: Defaults to None. Set to a path for a file that will store the
|
| 931 |
+
initializers for non-compiled nodes.
|
| 932 |
+
:param external_initializers_size_threshold: Defaults to 1024. Ignored if `external_initializers_file_path`
|
| 933 |
+
is None or empty. Initializers larger than this threshold are stored in the external initializers file.
|
| 934 |
+
:param flags: Additional boolean options to enable. Set this parameter to a bitwise OR of
|
| 935 |
+
flags in onnxruntime.OrtCompileApiFlags.
|
| 936 |
+
:param graph_optimization_level: The graph optimization level.
|
| 937 |
+
Defaults to onnxruntime.GraphOptimizationLevel.ORT_DISABLE_ALL.
|
| 938 |
+
:param get_initializer_location_func: Optional function called for every initializer to allow user to specify
|
| 939 |
+
whether an initializer should be stored within the model or externally. Example:
|
| 940 |
+
```
|
| 941 |
+
def get_initializer_location(
|
| 942 |
+
initializer_name: str,
|
| 943 |
+
initializer_value: onnxrt.OrtValue,
|
| 944 |
+
external_info: onnxrt.OrtExternalInitializerInfo | None,
|
| 945 |
+
) -> onnxrt.OrtExternalInitializerInfo | None:
|
| 946 |
+
byte_size = initializer_value.tensor_size_in_bytes()
|
| 947 |
+
|
| 948 |
+
if byte_size < 64:
|
| 949 |
+
return None # Store small initializer within compiled model.
|
| 950 |
+
|
| 951 |
+
# Else, write initializer to new external file.
|
| 952 |
+
value_np = initializer_value.numpy()
|
| 953 |
+
file_offset = ext_init_file.tell()
|
| 954 |
+
ext_init_file.write(value_np.tobytes())
|
| 955 |
+
return onnxrt.OrtExternalInitializerInfo(initializer_file_path, file_offset, byte_size)
|
| 956 |
+
```
|
| 957 |
+
"""
|
| 958 |
+
input_model_path: str | os.PathLike | None = None
|
| 959 |
+
input_model_bytes: bytes | None = None
|
| 960 |
+
if isinstance(input_model_path_or_bytes, (str, os.PathLike)):
|
| 961 |
+
if not input_model_path_or_bytes:
|
| 962 |
+
raise ValueError("Input model path is empty")
|
| 963 |
+
input_model_path = os.fspath(input_model_path_or_bytes)
|
| 964 |
+
elif isinstance(input_model_path_or_bytes, bytes):
|
| 965 |
+
if len(input_model_path_or_bytes) == 0:
|
| 966 |
+
raise ValueError("Input model bytes array is empty")
|
| 967 |
+
input_model_bytes = input_model_path_or_bytes
|
| 968 |
+
else:
|
| 969 |
+
raise TypeError(f"Unable to load from type '{type(input_model_path_or_bytes)}'")
|
| 970 |
+
|
| 971 |
+
if external_initializers_file_path:
|
| 972 |
+
if not isinstance(external_initializers_file_path, (str, os.PathLike)):
|
| 973 |
+
arg_type = type(external_initializers_file_path)
|
| 974 |
+
raise TypeError(f"Output external initializer filepath is of unexpected type '{arg_type}'")
|
| 975 |
+
external_initializers_file_path = os.fspath(external_initializers_file_path)
|
| 976 |
+
else:
|
| 977 |
+
external_initializers_file_path = ""
|
| 978 |
+
|
| 979 |
+
if get_initializer_location_func is not None:
|
| 980 |
+
if external_initializers_file_path:
|
| 981 |
+
raise ValueError(
|
| 982 |
+
"Cannot initialize ModelCompiler with both `external_initializers_file_path` "
|
| 983 |
+
"and `get_initializer_location_func`"
|
| 984 |
+
)
|
| 985 |
+
self.get_initializer_location_func_wrapper = make_get_initializer_location_func_wrapper(
|
| 986 |
+
get_initializer_location_func
|
| 987 |
+
)
|
| 988 |
+
else:
|
| 989 |
+
self.get_initializer_location_func_wrapper = None
|
| 990 |
+
|
| 991 |
+
if input_model_path:
|
| 992 |
+
self._model_compiler = C.ModelCompiler(
|
| 993 |
+
sess_options,
|
| 994 |
+
input_model_path,
|
| 995 |
+
True, # is path
|
| 996 |
+
embed_compiled_data_into_model,
|
| 997 |
+
external_initializers_file_path,
|
| 998 |
+
external_initializers_size_threshold,
|
| 999 |
+
flags,
|
| 1000 |
+
graph_optimization_level,
|
| 1001 |
+
self.get_initializer_location_func_wrapper,
|
| 1002 |
+
)
|
| 1003 |
+
else:
|
| 1004 |
+
self._model_compiler = C.ModelCompiler(
|
| 1005 |
+
sess_options,
|
| 1006 |
+
input_model_bytes,
|
| 1007 |
+
False, # is bytes
|
| 1008 |
+
embed_compiled_data_into_model,
|
| 1009 |
+
external_initializers_file_path,
|
| 1010 |
+
external_initializers_size_threshold,
|
| 1011 |
+
flags,
|
| 1012 |
+
graph_optimization_level,
|
| 1013 |
+
self.get_initializer_location_func_wrapper,
|
| 1014 |
+
)
|
| 1015 |
+
|
| 1016 |
+
def compile_to_file(self, output_model_path: str | None = None):
|
| 1017 |
+
"""
|
| 1018 |
+
Compiles to an output file. If an output file path is not provided,
|
| 1019 |
+
the output file path is generated based on the input model path by replacing
|
| 1020 |
+
'.onnx' with '_ctx.onnx'. Ex: The generated output file is 'model_ctx.onnx' for
|
| 1021 |
+
an input model with path 'model.onnx'.
|
| 1022 |
+
|
| 1023 |
+
Raises an 'InvalidArgument' exception if the compilation options are invalid.
|
| 1024 |
+
|
| 1025 |
+
:param output_model_path: Defaults to None. The path for the output/compiled model.
|
| 1026 |
+
"""
|
| 1027 |
+
if output_model_path:
|
| 1028 |
+
if not isinstance(output_model_path, (str, os.PathLike)):
|
| 1029 |
+
raise TypeError(f"Output model's filepath is of unexpected type '{type(output_model_path)}'")
|
| 1030 |
+
output_model_path = os.fspath(output_model_path)
|
| 1031 |
+
self._model_compiler.compile_to_file(output_model_path)
|
| 1032 |
+
|
| 1033 |
+
def compile_to_bytes(self) -> bytes:
|
| 1034 |
+
"""
|
| 1035 |
+
Compiles to bytes representing the serialized compiled ONNX model.
|
| 1036 |
+
|
| 1037 |
+
Raises an 'InvalidArgument' exception if the compilation options are invalid.
|
| 1038 |
+
|
| 1039 |
+
:return: A bytes object representing the compiled ONNX model.
|
| 1040 |
+
"""
|
| 1041 |
+
return self._model_compiler.compile_to_bytes()
|
| 1042 |
+
|
| 1043 |
+
def compile_to_stream(self, write_function: Callable[[bytes], None]):
|
| 1044 |
+
"""
|
| 1045 |
+
Compiles the input model and writes the serialized ONNX bytes to a stream using the provided write function.
|
| 1046 |
+
Raises an 'InvalidArgument' exception if the compilation options are invalid.
|
| 1047 |
+
:param write_function: A callable that accepts a bytes buffer to write.
|
| 1048 |
+
"""
|
| 1049 |
+
self._model_compiler.compile_to_stream(write_function)
|
| 1050 |
+
|
| 1051 |
+
|
| 1052 |
+
class IOBinding:
|
| 1053 |
+
"""
|
| 1054 |
+
This class provides API to bind input/output to a specified device, e.g. GPU.
|
| 1055 |
+
"""
|
| 1056 |
+
|
| 1057 |
+
def __init__(self, session: Session):
|
| 1058 |
+
self._session = session._sess
|
| 1059 |
+
self._is_webgpu_session = "WebGpuExecutionProvider" in session.get_providers()
|
| 1060 |
+
self._is_webgpu_graph_capture_enabled = self._session.is_webgpu_graph_capture_enabled()
|
| 1061 |
+
self._iobinding = C.SessionIOBinding(session._sess)
|
| 1062 |
+
self._numpy_obj_references = {}
|
| 1063 |
+
# Capture tracks fixed OrtValues; None marks host or raw-pointer bindings.
|
| 1064 |
+
self._bound_inputs: dict[str, OrtValue | None] = {}
|
| 1065 |
+
self._bound_outputs: dict[str, OrtValue | None] = {}
|
| 1066 |
+
# Captured graph IDs freeze these bindings until release.
|
| 1067 |
+
self._pinned_graph_ids: set[int] = set()
|
| 1068 |
+
|
| 1069 |
+
def _reject_if_pinned(self, action: str) -> None:
|
| 1070 |
+
if self._pinned_graph_ids:
|
| 1071 |
+
pinned = ", ".join(str(graph_id) for graph_id in sorted(self._pinned_graph_ids))
|
| 1072 |
+
raise ValueError(
|
| 1073 |
+
f"Cannot {action} while captured WebGPU graph(s) [{pinned}] still reference this "
|
| 1074 |
+
"IOBinding. Replay re-issues the buffers recorded at capture, so a change here "
|
| 1075 |
+
"would not affect replay and releasing the buffers could invalidate it. Call "
|
| 1076 |
+
"release_captured_graph(id) first."
|
| 1077 |
+
)
|
| 1078 |
+
|
| 1079 |
+
def _capture_signature(self):
|
| 1080 |
+
"""Return bound OrtValues whose identity and lifetime must remain fixed during replay."""
|
| 1081 |
+
return (
|
| 1082 |
+
tuple(sorted(self._bound_inputs.items(), key=lambda item: item[0])),
|
| 1083 |
+
tuple(sorted(self._bound_outputs.items(), key=lambda item: item[0])),
|
| 1084 |
+
)
|
| 1085 |
+
|
| 1086 |
+
def _validate_capture_bindings(self) -> None:
|
| 1087 |
+
"""Every binding participating in capture must be a fixed WebGPU device OrtValue."""
|
| 1088 |
+
for kind, bound in (("input", self._bound_inputs), ("output", self._bound_outputs)):
|
| 1089 |
+
for name, value in bound.items():
|
| 1090 |
+
if value is None or not value._is_webgpu_buffer:
|
| 1091 |
+
raise ValueError(
|
| 1092 |
+
f"WebGPU graph capture requires fixed WebGPU device OrtValues; {kind} "
|
| 1093 |
+
f"'{name}' is not one. Bind values created by this session or backed by an "
|
| 1094 |
+
f"environment-registered shared allocator, or set the "
|
| 1095 |
+
f"'{_GPU_GRAPH_ID_RUN_CONFIG_KEY}' run option to {_GRAPH_ANNOTATION_SKIP} "
|
| 1096 |
+
"to run without capture."
|
| 1097 |
+
)
|
| 1098 |
+
|
| 1099 |
+
def bind_cpu_input(self, name, arr_on_cpu):
|
| 1100 |
+
"""
|
| 1101 |
+
bind an input to array on CPU
|
| 1102 |
+
:param name: input name
|
| 1103 |
+
:param arr_on_cpu: input values as a python array on CPU
|
| 1104 |
+
"""
|
| 1105 |
+
self._reject_if_pinned("rebind inputs")
|
| 1106 |
+
|
| 1107 |
+
# Hold a reference to the numpy object as the bound OrtValue is backed
|
| 1108 |
+
# directly by the data buffer of the numpy object and so the numpy object
|
| 1109 |
+
# must be around until this IOBinding instance is around
|
| 1110 |
+
self._numpy_obj_references[name] = arr_on_cpu
|
| 1111 |
+
self._iobinding.bind_input(name, arr_on_cpu)
|
| 1112 |
+
self._bound_inputs[name] = None
|
| 1113 |
+
|
| 1114 |
+
def bind_input(self, name, device_type, device_id, element_type, shape, buffer_ptr):
|
| 1115 |
+
"""
|
| 1116 |
+
:param name: input name
|
| 1117 |
+
:param device_type: e.g. cpu, cuda, cann
|
| 1118 |
+
:param device_id: device id, e.g. 0
|
| 1119 |
+
:param element_type: input element type. It can be either numpy type (like numpy.float32) or an integer for onnx type (like onnx.TensorProto.BFLOAT16)
|
| 1120 |
+
:param shape: input shape
|
| 1121 |
+
:param buffer_ptr: memory pointer to input data
|
| 1122 |
+
"""
|
| 1123 |
+
self._reject_if_pinned("rebind inputs")
|
| 1124 |
+
self._iobinding.bind_input(
|
| 1125 |
+
name,
|
| 1126 |
+
OrtDevice.make(device_type, device_id)._get_c_device(),
|
| 1127 |
+
element_type,
|
| 1128 |
+
shape,
|
| 1129 |
+
buffer_ptr,
|
| 1130 |
+
)
|
| 1131 |
+
self._bound_inputs[name] = None
|
| 1132 |
+
|
| 1133 |
+
def bind_ortvalue_input(self, name, ortvalue):
|
| 1134 |
+
"""
|
| 1135 |
+
:param name: input name
|
| 1136 |
+
:param ortvalue: OrtValue instance to bind
|
| 1137 |
+
"""
|
| 1138 |
+
_validate_ortvalue_session_compatibility(ortvalue, self._session, "bound to")
|
| 1139 |
+
self._reject_if_pinned("rebind inputs")
|
| 1140 |
+
self._iobinding.bind_ortvalue_input(name, ortvalue._ortvalue)
|
| 1141 |
+
self._bound_inputs[name] = ortvalue
|
| 1142 |
+
|
| 1143 |
+
def synchronize_inputs(self):
|
| 1144 |
+
self._iobinding.synchronize_inputs()
|
| 1145 |
+
|
| 1146 |
+
def bind_output(
|
| 1147 |
+
self,
|
| 1148 |
+
name,
|
| 1149 |
+
device_type="cpu",
|
| 1150 |
+
device_id=0,
|
| 1151 |
+
element_type=None,
|
| 1152 |
+
shape=None,
|
| 1153 |
+
buffer_ptr=None,
|
| 1154 |
+
):
|
| 1155 |
+
"""
|
| 1156 |
+
:param name: output name
|
| 1157 |
+
:param device_type: e.g. cpu, cuda, cann, cpu by default
|
| 1158 |
+
:param device_id: device id, e.g. 0
|
| 1159 |
+
:param element_type: output element type. It can be either numpy type (like numpy.float32) or an integer for onnx type (like onnx.TensorProto.BFLOAT16)
|
| 1160 |
+
:param shape: output shape
|
| 1161 |
+
:param buffer_ptr: memory pointer to output data
|
| 1162 |
+
"""
|
| 1163 |
+
|
| 1164 |
+
self._reject_if_pinned("rebind outputs")
|
| 1165 |
+
|
| 1166 |
+
# Follow the `if` path when the user has not provided any pre-allocated buffer but still
|
| 1167 |
+
# would like to bind an output to a specific device (e.g. cuda).
|
| 1168 |
+
# Pre-allocating an output buffer may not be an option for the user as :
|
| 1169 |
+
# (1) They may not want to use a custom allocator specific to the device they want to bind the output to,
|
| 1170 |
+
# in which case ORT will allocate the memory for the user
|
| 1171 |
+
# (2) The output has a dynamic shape and hence the size of the buffer may not be fixed across runs
|
| 1172 |
+
if buffer_ptr is None:
|
| 1173 |
+
self._iobinding.bind_output(
|
| 1174 |
+
name,
|
| 1175 |
+
OrtDevice.make(device_type, device_id)._get_c_device(),
|
| 1176 |
+
)
|
| 1177 |
+
else:
|
| 1178 |
+
if element_type is None or shape is None:
|
| 1179 |
+
raise ValueError("`element_type` and `shape` are to be provided if pre-allocated memory is provided")
|
| 1180 |
+
self._iobinding.bind_output(
|
| 1181 |
+
name,
|
| 1182 |
+
OrtDevice.make(device_type, device_id)._get_c_device(),
|
| 1183 |
+
element_type,
|
| 1184 |
+
shape,
|
| 1185 |
+
buffer_ptr,
|
| 1186 |
+
)
|
| 1187 |
+
self._bound_outputs[name] = None
|
| 1188 |
+
|
| 1189 |
+
def bind_ortvalue_output(self, name, ortvalue):
|
| 1190 |
+
"""
|
| 1191 |
+
:param name: output name
|
| 1192 |
+
:param ortvalue: OrtValue instance to bind
|
| 1193 |
+
"""
|
| 1194 |
+
_validate_ortvalue_session_compatibility(ortvalue, self._session, "bound to")
|
| 1195 |
+
self._reject_if_pinned("rebind outputs")
|
| 1196 |
+
self._iobinding.bind_ortvalue_output(name, ortvalue._ortvalue)
|
| 1197 |
+
self._bound_outputs[name] = ortvalue
|
| 1198 |
+
|
| 1199 |
+
def synchronize_outputs(self):
|
| 1200 |
+
self._iobinding.synchronize_outputs()
|
| 1201 |
+
|
| 1202 |
+
def get_outputs(self):
|
| 1203 |
+
"""
|
| 1204 |
+
Returns the output OrtValues from the Run() that preceded the call.
|
| 1205 |
+
The data buffer of the obtained OrtValues may not reside on CPU memory
|
| 1206 |
+
"""
|
| 1207 |
+
outputs = self._iobinding.get_outputs()
|
| 1208 |
+
if not isinstance(outputs, C.OrtValueVector):
|
| 1209 |
+
raise TypeError("get_outputs() must return an instance of type 'OrtValueVector'.")
|
| 1210 |
+
result = []
|
| 1211 |
+
for index in range(len(outputs)):
|
| 1212 |
+
ortvalue = outputs[index]
|
| 1213 |
+
result.append(
|
| 1214 |
+
OrtValue(
|
| 1215 |
+
ortvalue,
|
| 1216 |
+
session=self._session if ortvalue._is_webgpu_buffer() else None,
|
| 1217 |
+
)
|
| 1218 |
+
)
|
| 1219 |
+
return result
|
| 1220 |
+
|
| 1221 |
+
def get_outputs_as_ortvaluevector(self):
|
| 1222 |
+
"""Return the raw OrtValueVector of outputs from the Run() that preceded the call.
|
| 1223 |
+
|
| 1224 |
+
The vector is a reference into this IOBinding (pybind ``reference_internal``), so it keeps the
|
| 1225 |
+
IOBinding alive, which in turn keeps the session alive. Device-resident outputs are therefore
|
| 1226 |
+
safe to hold past the session going out of scope.
|
| 1227 |
+
"""
|
| 1228 |
+
return self._iobinding.get_outputs()
|
| 1229 |
+
|
| 1230 |
+
def copy_outputs_to_cpu(self):
|
| 1231 |
+
"""Copy output contents to CPU."""
|
| 1232 |
+
return self._iobinding.copy_outputs_to_cpu()
|
| 1233 |
+
|
| 1234 |
+
def clear_binding_inputs(self):
|
| 1235 |
+
self._reject_if_pinned("clear input bindings")
|
| 1236 |
+
self._iobinding.clear_binding_inputs()
|
| 1237 |
+
self._bound_inputs.clear()
|
| 1238 |
+
|
| 1239 |
+
def clear_binding_outputs(self):
|
| 1240 |
+
self._reject_if_pinned("clear output bindings")
|
| 1241 |
+
self._iobinding.clear_binding_outputs()
|
| 1242 |
+
self._bound_outputs.clear()
|
| 1243 |
+
|
| 1244 |
+
|
| 1245 |
+
class OrtValue:
|
| 1246 |
+
"""
|
| 1247 |
+
A data structure that supports all ONNX data formats (tensors and non-tensors) that allows users
|
| 1248 |
+
to place the data backing these on a device, for example, on a CUDA supported device.
|
| 1249 |
+
This class provides APIs to construct and deal with OrtValues.
|
| 1250 |
+
"""
|
| 1251 |
+
|
| 1252 |
+
def __init__(
|
| 1253 |
+
self,
|
| 1254 |
+
ortvalue: C.OrtValue,
|
| 1255 |
+
numpy_obj: np.ndarray | None = None,
|
| 1256 |
+
session: C.InferenceSession | None = None,
|
| 1257 |
+
):
|
| 1258 |
+
if isinstance(ortvalue, C.OrtValue):
|
| 1259 |
+
self._ortvalue = ortvalue
|
| 1260 |
+
# Hold a ref count to the numpy object if the OrtValue is backed directly
|
| 1261 |
+
# by its data buffer so that it isn't destroyed when the OrtValue is in use
|
| 1262 |
+
self._numpy_obj = numpy_obj
|
| 1263 |
+
# Session-scoped device allocators can be invalidated when their session is destroyed.
|
| 1264 |
+
self._session = session
|
| 1265 |
+
else:
|
| 1266 |
+
# An end user won't hit this error
|
| 1267 |
+
raise ValueError(
|
| 1268 |
+
"`Provided ortvalue` needs to be of type `onnxruntime.capi.onnxruntime_pybind11_state.OrtValue`"
|
| 1269 |
+
)
|
| 1270 |
+
|
| 1271 |
+
@property
|
| 1272 |
+
def _is_webgpu_buffer(self) -> bool:
|
| 1273 |
+
"""Whether this value is backed by a WebGPU buffer.
|
| 1274 |
+
|
| 1275 |
+
Resolved from the native OrtValue on every access and deliberately read-only: graph-capture
|
| 1276 |
+
validation and the copy-path checks below refuse host memory, so a settable attribute would
|
| 1277 |
+
let a CPU tensor pass itself off as a device tensor.
|
| 1278 |
+
"""
|
| 1279 |
+
return self._ortvalue._is_webgpu_buffer()
|
| 1280 |
+
|
| 1281 |
+
def _get_c_value(self) -> C.OrtValue:
|
| 1282 |
+
return self._ortvalue
|
| 1283 |
+
|
| 1284 |
+
@classmethod
|
| 1285 |
+
def ortvalue_from_numpy(
|
| 1286 |
+
cls, numpy_obj: np.ndarray, /, device_type="cpu", device_id=0, vendor_id: int | OrtDeviceVendorId = -1
|
| 1287 |
+
) -> OrtValue:
|
| 1288 |
+
"""
|
| 1289 |
+
Factory method to construct an OrtValue (which holds a Tensor) from a given Numpy object
|
| 1290 |
+
A copy of the data in the Numpy object is held by the OrtValue only if the device is NOT cpu
|
| 1291 |
+
|
| 1292 |
+
:param numpy_obj: The Numpy object to construct the OrtValue from
|
| 1293 |
+
:param device_type: e.g. cpu, cuda, cann, cpu by default
|
| 1294 |
+
:param device_id: device id, e.g. 0
|
| 1295 |
+
:param vendor_id: The device's PCI vendor id as an int or OrtDeviceVendorId. If provided, the device_type should be "gpu" or "npu".
|
| 1296 |
+
"""
|
| 1297 |
+
# Hold a reference to the numpy object (if device_type is 'cpu') as the OrtValue
|
| 1298 |
+
# is backed directly by the data buffer of the numpy object and so the numpy object
|
| 1299 |
+
# must be around until this OrtValue instance is around
|
| 1300 |
+
return cls(
|
| 1301 |
+
C.OrtValue.ortvalue_from_numpy(
|
| 1302 |
+
numpy_obj,
|
| 1303 |
+
OrtDevice.make(device_type, device_id, vendor_id)._get_c_device(),
|
| 1304 |
+
),
|
| 1305 |
+
numpy_obj if device_type.lower() == "cpu" else None,
|
| 1306 |
+
)
|
| 1307 |
+
|
| 1308 |
+
@classmethod
|
| 1309 |
+
def ortvalue_from_numpy_with_onnx_type(cls, data: np.ndarray, /, onnx_element_type: int) -> OrtValue:
|
| 1310 |
+
"""
|
| 1311 |
+
This method creates an instance of OrtValue on top of the numpy array.
|
| 1312 |
+
No data copy is made and the lifespan of the resulting OrtValue should never
|
| 1313 |
+
exceed the lifespan of bytes object. The API attempts to reinterpret
|
| 1314 |
+
the data type which is expected to be the same size. This is useful
|
| 1315 |
+
when we want to use an ONNX data type that is not supported by numpy.
|
| 1316 |
+
|
| 1317 |
+
:param data: numpy.ndarray.
|
| 1318 |
+
:param onnx_element_type: a valid onnx TensorProto::DataType enum value
|
| 1319 |
+
"""
|
| 1320 |
+
return cls(C.OrtValue.ortvalue_from_numpy_with_onnx_type(data, onnx_element_type), data)
|
| 1321 |
+
|
| 1322 |
+
@classmethod
|
| 1323 |
+
def ortvalue_from_shape_and_type(
|
| 1324 |
+
cls,
|
| 1325 |
+
shape: Sequence[int],
|
| 1326 |
+
element_type,
|
| 1327 |
+
device_type: str = "cpu",
|
| 1328 |
+
device_id: int = 0,
|
| 1329 |
+
vendor_id: int | OrtDeviceVendorId = -1,
|
| 1330 |
+
memory_info: C.OrtMemoryInfo | None = None,
|
| 1331 |
+
) -> OrtValue:
|
| 1332 |
+
"""
|
| 1333 |
+
Factory method to construct an OrtValue (which holds a Tensor) from given shape and element_type
|
| 1334 |
+
|
| 1335 |
+
:param shape: List of integers indicating the shape of the OrtValue
|
| 1336 |
+
:param element_type: The data type of the elements. It can be either numpy type (like numpy.float32) or an integer for onnx type (like onnx.TensorProto.BFLOAT16).
|
| 1337 |
+
:param device_type: e.g. cpu, cuda, cann, cpu by default
|
| 1338 |
+
:param device_id: device id, e.g. 0
|
| 1339 |
+
:param vendor_id: The device's PCI vendor id as an int or OrtDeviceVendorId. If provided, the device type should be "gpu" or "npu".
|
| 1340 |
+
:param memory_info: An OrtMemoryInfo from an OrtEpDevice (e.g. via ep_device.memory_info(OrtDeviceMemoryType.HOST_ACCESSIBLE)). When provided, the allocator matching this memory info is used directly, which allows allocating HOST_ACCESSIBLE memory for zero-copy numpy interop. The device_type, device_id, and vendor_id parameters are ignored when memory_info is provided.
|
| 1341 |
+
"""
|
| 1342 |
+
|
| 1343 |
+
if memory_info is not None:
|
| 1344 |
+
if device_type != "cpu" or device_id != 0 or vendor_id != -1:
|
| 1345 |
+
warnings.warn(
|
| 1346 |
+
"device_type, device_id, and vendor_id are ignored when memory_info is provided.",
|
| 1347 |
+
stacklevel=2,
|
| 1348 |
+
)
|
| 1349 |
+
if isinstance(element_type, int):
|
| 1350 |
+
return cls(
|
| 1351 |
+
C.OrtValue.ortvalue_from_shape_and_onnx_type_for_memory_info(
|
| 1352 |
+
shape,
|
| 1353 |
+
element_type,
|
| 1354 |
+
memory_info,
|
| 1355 |
+
)
|
| 1356 |
+
)
|
| 1357 |
+
return cls(
|
| 1358 |
+
C.OrtValue.ortvalue_from_shape_and_type_for_memory_info(
|
| 1359 |
+
shape,
|
| 1360 |
+
element_type,
|
| 1361 |
+
memory_info,
|
| 1362 |
+
)
|
| 1363 |
+
)
|
| 1364 |
+
|
| 1365 |
+
device = OrtDevice.make(device_type, device_id, vendor_id)._get_c_device()
|
| 1366 |
+
|
| 1367 |
+
# Integer for onnx element type (see https://onnx.ai/onnx/api/mapping.html).
|
| 1368 |
+
# This is helpful for some data type (like TensorProto.BFLOAT16) that is not available in numpy.
|
| 1369 |
+
if isinstance(element_type, int):
|
| 1370 |
+
return cls(
|
| 1371 |
+
C.OrtValue.ortvalue_from_shape_and_onnx_type(
|
| 1372 |
+
shape,
|
| 1373 |
+
element_type,
|
| 1374 |
+
device,
|
| 1375 |
+
)
|
| 1376 |
+
)
|
| 1377 |
+
|
| 1378 |
+
return cls(
|
| 1379 |
+
C.OrtValue.ortvalue_from_shape_and_type(
|
| 1380 |
+
shape,
|
| 1381 |
+
element_type,
|
| 1382 |
+
device,
|
| 1383 |
+
)
|
| 1384 |
+
)
|
| 1385 |
+
|
| 1386 |
+
@classmethod
|
| 1387 |
+
def ort_value_from_sparse_tensor(cls, sparse_tensor: SparseTensor) -> OrtValue:
|
| 1388 |
+
"""
|
| 1389 |
+
The function will construct an OrtValue instance from a valid SparseTensor
|
| 1390 |
+
The new instance of OrtValue will assume the ownership of sparse_tensor
|
| 1391 |
+
"""
|
| 1392 |
+
return cls(C.OrtValue.ort_value_from_sparse_tensor(sparse_tensor._get_c_tensor()))
|
| 1393 |
+
|
| 1394 |
+
def as_sparse_tensor(self) -> SparseTensor:
|
| 1395 |
+
"""
|
| 1396 |
+
The function will return SparseTensor contained in this OrtValue
|
| 1397 |
+
"""
|
| 1398 |
+
return SparseTensor(self._ortvalue.as_sparse_tensor())
|
| 1399 |
+
|
| 1400 |
+
def data_ptr(self) -> int:
|
| 1401 |
+
"""
|
| 1402 |
+
Returns the address of the first element in the OrtValue's data buffer
|
| 1403 |
+
|
| 1404 |
+
WebGPU buffers are opaque handles and do not expose a data pointer.
|
| 1405 |
+
"""
|
| 1406 |
+
return self._ortvalue.data_ptr()
|
| 1407 |
+
|
| 1408 |
+
def device_name(self) -> str:
|
| 1409 |
+
"""
|
| 1410 |
+
Returns the name of the device where the OrtValue's data buffer resides e.g. cpu, cuda, cann, webgpu
|
| 1411 |
+
"""
|
| 1412 |
+
return self._ortvalue.device_name().lower()
|
| 1413 |
+
|
| 1414 |
+
def shape(self) -> Sequence[int]:
|
| 1415 |
+
"""
|
| 1416 |
+
Returns the shape of the data in the OrtValue
|
| 1417 |
+
"""
|
| 1418 |
+
return self._ortvalue.shape()
|
| 1419 |
+
|
| 1420 |
+
def data_type(self) -> str:
|
| 1421 |
+
"""
|
| 1422 |
+
Returns the data type of the data in the OrtValue. E.g. 'tensor(int64)'
|
| 1423 |
+
"""
|
| 1424 |
+
return self._ortvalue.data_type()
|
| 1425 |
+
|
| 1426 |
+
def element_type(self) -> int:
|
| 1427 |
+
"""
|
| 1428 |
+
Returns the proto type of the data in the OrtValue
|
| 1429 |
+
if the OrtValue is a tensor.
|
| 1430 |
+
"""
|
| 1431 |
+
return self._ortvalue.element_type()
|
| 1432 |
+
|
| 1433 |
+
def tensor_size_in_bytes(self) -> int:
|
| 1434 |
+
"""
|
| 1435 |
+
Returns the size of the data in the OrtValue in bytes
|
| 1436 |
+
if the OrtValue is a tensor.
|
| 1437 |
+
"""
|
| 1438 |
+
return self._ortvalue.tensor_size_in_bytes()
|
| 1439 |
+
|
| 1440 |
+
def has_value(self) -> bool:
|
| 1441 |
+
"""
|
| 1442 |
+
Returns True if the OrtValue corresponding to an
|
| 1443 |
+
optional type contains data, else returns False
|
| 1444 |
+
"""
|
| 1445 |
+
return self._ortvalue.has_value()
|
| 1446 |
+
|
| 1447 |
+
def is_tensor(self) -> bool:
|
| 1448 |
+
"""
|
| 1449 |
+
Returns True if the OrtValue contains a Tensor, else returns False
|
| 1450 |
+
"""
|
| 1451 |
+
return self._ortvalue.is_tensor()
|
| 1452 |
+
|
| 1453 |
+
def is_sparse_tensor(self) -> bool:
|
| 1454 |
+
"""
|
| 1455 |
+
Returns True if the OrtValue contains a SparseTensor, else returns False
|
| 1456 |
+
"""
|
| 1457 |
+
return self._ortvalue.is_sparse_tensor()
|
| 1458 |
+
|
| 1459 |
+
def is_tensor_sequence(self) -> bool:
|
| 1460 |
+
"""
|
| 1461 |
+
Returns True if the OrtValue contains a Tensor Sequence, else returns False
|
| 1462 |
+
"""
|
| 1463 |
+
return self._ortvalue.is_tensor_sequence()
|
| 1464 |
+
|
| 1465 |
+
def numpy(self) -> np.ndarray:
|
| 1466 |
+
"""
|
| 1467 |
+
Returns a Numpy object from the OrtValue.
|
| 1468 |
+
Valid only for OrtValues holding Tensors. Throws for OrtValues holding non-Tensors.
|
| 1469 |
+
Use accessors to gain a reference to non-Tensor objects such as SparseTensor
|
| 1470 |
+
WebGPU device values require explicit readback with
|
| 1471 |
+
:meth:`IOBinding.copy_outputs_to_cpu`.
|
| 1472 |
+
"""
|
| 1473 |
+
return self._ortvalue.numpy()
|
| 1474 |
+
|
| 1475 |
+
def __array__(self, dtype=None, copy=None) -> np.ndarray:
|
| 1476 |
+
"""
|
| 1477 |
+
Supports ``numpy.asarray(ortvalue)`` and ``numpy.array(ortvalue)`` via the
|
| 1478 |
+
`numpy __array__ protocol <https://numpy.org/devdocs/user/basics.interoperability.html>`_.
|
| 1479 |
+
|
| 1480 |
+
Valid only for OrtValues holding Tensors on CPU.
|
| 1481 |
+
|
| 1482 |
+
:param dtype: Optional numpy dtype to cast the result to.
|
| 1483 |
+
:param copy: Optional bool (numpy >= 2.0). If ``False``, a copy will
|
| 1484 |
+
only be made if necessary. If ``True``, a copy is always forced.
|
| 1485 |
+
If ``None`` (default), a copy will be made only if needed.
|
| 1486 |
+
:return: A numpy array with the same data as the OrtValue.
|
| 1487 |
+
"""
|
| 1488 |
+
arr = self.numpy()
|
| 1489 |
+
|
| 1490 |
+
if copy is not None:
|
| 1491 |
+
# numpy >= 2.0 added the copy kwarg to np.asarray;
|
| 1492 |
+
# np.array has always accepted it but with weaker semantics pre-2.0.
|
| 1493 |
+
arr = np.array(arr, dtype=dtype, copy=copy)
|
| 1494 |
+
elif dtype is not None:
|
| 1495 |
+
# np.asarray avoids a copy when the dtype already matches,
|
| 1496 |
+
# preserving memory sharing with the underlying OrtValue.
|
| 1497 |
+
arr = np.asarray(arr, dtype=dtype)
|
| 1498 |
+
|
| 1499 |
+
return arr
|
| 1500 |
+
|
| 1501 |
+
def __dlpack__(self, *, stream=None):
|
| 1502 |
+
"""
|
| 1503 |
+
Returns a DLPack capsule representing the tensor (part of the
|
| 1504 |
+
`DLPack protocol <https://dmlc.github.io/dlpack/latest/>`_).
|
| 1505 |
+
|
| 1506 |
+
This enables interoperability with other frameworks via
|
| 1507 |
+
``from_dlpack(ortvalue)`` (e.g. ``torch.from_dlpack``,
|
| 1508 |
+
``jax.dlpack.from_dlpack``, ``numpy.from_dlpack``).
|
| 1509 |
+
|
| 1510 |
+
The OrtValue must hold a contiguous tensor. No data is copied;
|
| 1511 |
+
the consumer shares memory with this OrtValue, which must remain
|
| 1512 |
+
alive while the capsule is in use.
|
| 1513 |
+
WebGPU OrtValues cannot be exported through DLPack because their data
|
| 1514 |
+
is stored in opaque WebGPU buffers rather than CUDA-addressable memory.
|
| 1515 |
+
|
| 1516 |
+
:param stream: Optional stream on which the tensor data is accessible.
|
| 1517 |
+
Currently unused; included for protocol compliance.
|
| 1518 |
+
:return: A PyCapsule holding a DLManagedTensor.
|
| 1519 |
+
"""
|
| 1520 |
+
return self._ortvalue.__dlpack__(stream=stream)
|
| 1521 |
+
|
| 1522 |
+
def __dlpack_device__(self) -> tuple[int, int]:
|
| 1523 |
+
"""
|
| 1524 |
+
Returns ``(device_type, device_id)`` indicating where the tensor data
|
| 1525 |
+
resides (part of the `DLPack protocol
|
| 1526 |
+
<https://dmlc.github.io/dlpack/latest/>`_).
|
| 1527 |
+
|
| 1528 |
+
WebGPU OrtValues do not expose a DLPack device.
|
| 1529 |
+
|
| 1530 |
+
:return: Tuple of ``(device_type, device_id)`` as ints following DLPack
|
| 1531 |
+
``DLDeviceType`` enum values.
|
| 1532 |
+
"""
|
| 1533 |
+
return self._ortvalue.__dlpack_device__()
|
| 1534 |
+
|
| 1535 |
+
@classmethod
|
| 1536 |
+
def from_dlpack(cls, data, /) -> OrtValue:
|
| 1537 |
+
"""
|
| 1538 |
+
Construct an OrtValue from an object that implements the DLPack protocol.
|
| 1539 |
+
|
| 1540 |
+
Accepts either:
|
| 1541 |
+
|
| 1542 |
+
* An object with ``__dlpack__`` / ``__dlpack_device__`` methods
|
| 1543 |
+
(e.g. a PyTorch tensor, JAX array, or numpy array).
|
| 1544 |
+
* A raw DLPack PyCapsule (legacy path).
|
| 1545 |
+
|
| 1546 |
+
Boolean tensors are automatically detected when the source object
|
| 1547 |
+
exposes a ``dtype`` attribute (numpy, PyTorch, etc.) or is an
|
| 1548 |
+
``OrtValue``. For raw DLPack capsules where the original dtype cannot
|
| 1549 |
+
be inspected, bool tensors encoded as uint8 by older DLPack versions
|
| 1550 |
+
are not distinguishable from true uint8 tensors and will be imported
|
| 1551 |
+
as uint8.
|
| 1552 |
+
|
| 1553 |
+
No data is copied; the new OrtValue shares memory with the source.
|
| 1554 |
+
|
| 1555 |
+
:param data: A tensor object supporting the DLPack protocol, or a raw
|
| 1556 |
+
DLPack PyCapsule.
|
| 1557 |
+
:return: An OrtValue wrapping the tensor data.
|
| 1558 |
+
"""
|
| 1559 |
+
# Detect boolean dtype from the source object before consuming it,
|
| 1560 |
+
# because DLPack encodes bool as uint8 and the capsule alone cannot
|
| 1561 |
+
# distinguish between the two.
|
| 1562 |
+
is_bool = False
|
| 1563 |
+
if isinstance(data, OrtValue):
|
| 1564 |
+
is_bool = data.data_type() == "tensor(bool)"
|
| 1565 |
+
elif hasattr(data, "dtype"):
|
| 1566 |
+
dtype_obj = data.dtype
|
| 1567 |
+
# Use .name when available (numpy, cupy, tensorflow all expose it).
|
| 1568 |
+
# Fall back to str() for frameworks that don't (e.g. PyTorch).
|
| 1569 |
+
dtype_name = getattr(dtype_obj, "name", str(dtype_obj))
|
| 1570 |
+
is_bool = dtype_name in ("bool", "bool_", "torch.bool")
|
| 1571 |
+
|
| 1572 |
+
# If the input supports the __dlpack__ protocol, call it to get the capsule.
|
| 1573 |
+
if hasattr(data, "__dlpack__"):
|
| 1574 |
+
capsule = data.__dlpack__()
|
| 1575 |
+
else:
|
| 1576 |
+
capsule = data
|
| 1577 |
+
|
| 1578 |
+
return cls(C.OrtValue.from_dlpack(capsule, is_bool))
|
| 1579 |
+
|
| 1580 |
+
def update_inplace(self, data) -> None:
|
| 1581 |
+
"""
|
| 1582 |
+
Update the OrtValue in place. The source data is copied over to the device
|
| 1583 |
+
memory backing the OrtValue. It can be used to update the input values for
|
| 1584 |
+
an InferenceSession with CUDA graph enabled or other scenarios where the
|
| 1585 |
+
OrtValue needs to be updated while the memory address can not be changed.
|
| 1586 |
+
|
| 1587 |
+
:param data: The source data, which can be a Numpy array or another OrtValue.
|
| 1588 |
+
When an OrtValue is provided, data can be copied between devices (e.g.,
|
| 1589 |
+
GPU to GPU) without going through the CPU.
|
| 1590 |
+
"""
|
| 1591 |
+
if isinstance(data, OrtValue):
|
| 1592 |
+
if self._is_webgpu_buffer != data._is_webgpu_buffer:
|
| 1593 |
+
raise ValueError(
|
| 1594 |
+
"WebGPU OrtValue copies require WebGPU source and destination values; "
|
| 1595 |
+
"use IOBinding.copy_outputs_to_cpu for readback."
|
| 1596 |
+
)
|
| 1597 |
+
if not _is_ortvalue_session_compatible(data, self._session):
|
| 1598 |
+
raise ValueError("Session-scoped OrtValues must originate from the same session.")
|
| 1599 |
+
|
| 1600 |
+
self._ortvalue.update_inplace(data._ortvalue)
|
| 1601 |
+
return
|
| 1602 |
+
|
| 1603 |
+
if not isinstance(data, np.ndarray):
|
| 1604 |
+
raise TypeError("data must be a numpy.ndarray or an OrtValue.")
|
| 1605 |
+
|
| 1606 |
+
# Every copy path requires contiguous source storage.
|
| 1607 |
+
if not data.flags.c_contiguous:
|
| 1608 |
+
data = np.ascontiguousarray(data)
|
| 1609 |
+
|
| 1610 |
+
self._ortvalue.update_inplace(data)
|
| 1611 |
+
|
| 1612 |
+
|
| 1613 |
+
_DEFAULT_WEBGPU_CONTEXT_ID = 0
|
| 1614 |
+
|
| 1615 |
+
|
| 1616 |
+
def _session_webgpu_context_id(session: C.InferenceSession) -> int:
|
| 1617 |
+
"""Return a session's WebGPU context id, or -1 when it has no WebGPU EP."""
|
| 1618 |
+
return session.webgpu_context_id()
|
| 1619 |
+
|
| 1620 |
+
|
| 1621 |
+
def _has_foreign_webgpu_context(
|
| 1622 |
+
values: Sequence[OrtValue],
|
| 1623 |
+
context_id_getter=_session_webgpu_context_id,
|
| 1624 |
+
) -> bool:
|
| 1625 |
+
"""Whether any value provably belongs to a WebGPU context other than the default one.
|
| 1626 |
+
|
| 1627 |
+
The environment data transfer resolves the default WebGPU context, so copying a buffer that
|
| 1628 |
+
belongs to a caller-supplied context would silently run on the wrong device. A value with no
|
| 1629 |
+
owning session cannot be attributed to a context and is treated as default-context, as is a
|
| 1630 |
+
session with no WebGPU EP.
|
| 1631 |
+
|
| 1632 |
+
TODO: this can only attribute a value that carries session provenance, because every WebGPU
|
| 1633 |
+
allocation reports OrtDevice(GPU, VendorIds::NONE, 0) regardless of its context. Supporting a
|
| 1634 |
+
custom external WebGPU context requires the shared allocator and its data transfer to retain and
|
| 1635 |
+
use that external device instead of resolving WebGpuContextFactory::DefaultContext(), which
|
| 1636 |
+
would also let the transfer itself reject the copy instead of relying on this Python-side guard.
|
| 1637 |
+
"""
|
| 1638 |
+
for value in values:
|
| 1639 |
+
if not value._is_webgpu_buffer or value._session is None:
|
| 1640 |
+
continue
|
| 1641 |
+
if context_id_getter(value._session) not in (-1, _DEFAULT_WEBGPU_CONTEXT_ID):
|
| 1642 |
+
return True
|
| 1643 |
+
return False
|
| 1644 |
+
|
| 1645 |
+
|
| 1646 |
+
def copy_tensors(src: Sequence[OrtValue], dst: Sequence[OrtValue], stream=None) -> None:
|
| 1647 |
+
"""
|
| 1648 |
+
Copy tensor data from source OrtValue sequence to destination OrtValue sequence.
|
| 1649 |
+
|
| 1650 |
+
WebGPU values belonging to the default context are supported in either direction. A value from a
|
| 1651 |
+
caller-supplied WebGPU context is not, because the shared data transfer is bound to the default
|
| 1652 |
+
context.
|
| 1653 |
+
"""
|
| 1654 |
+
if _has_foreign_webgpu_context([*src, *dst]):
|
| 1655 |
+
raise ValueError(
|
| 1656 |
+
"copy_tensors cannot copy a WebGPU OrtValue that belongs to a custom WebGPU context "
|
| 1657 |
+
"because the shared data transfer is bound to the default context; use IOBinding "
|
| 1658 |
+
"readback instead."
|
| 1659 |
+
)
|
| 1660 |
+
c_sources = [s._get_c_value() for s in src]
|
| 1661 |
+
c_dsts = [d._get_c_value() for d in dst]
|
| 1662 |
+
C.copy_tensors(c_sources, c_dsts, stream)
|
| 1663 |
+
|
| 1664 |
+
|
| 1665 |
+
class OrtDevice:
|
| 1666 |
+
"""
|
| 1667 |
+
A data structure that exposes the underlying C++ OrtDevice
|
| 1668 |
+
"""
|
| 1669 |
+
|
| 1670 |
+
def __init__(self, c_ort_device):
|
| 1671 |
+
"""
|
| 1672 |
+
Internal constructor
|
| 1673 |
+
"""
|
| 1674 |
+
if isinstance(c_ort_device, C.OrtDevice):
|
| 1675 |
+
self._ort_device = c_ort_device
|
| 1676 |
+
else:
|
| 1677 |
+
# An end user won't hit this error
|
| 1678 |
+
raise ValueError(
|
| 1679 |
+
"`Provided object` needs to be of type `onnxruntime.capi.onnxruntime_pybind11_state.OrtDevice`"
|
| 1680 |
+
)
|
| 1681 |
+
|
| 1682 |
+
def _get_c_device(self):
|
| 1683 |
+
"""
|
| 1684 |
+
Internal accessor to underlying object
|
| 1685 |
+
"""
|
| 1686 |
+
return self._ort_device
|
| 1687 |
+
|
| 1688 |
+
@staticmethod
|
| 1689 |
+
def make(ort_device_name, device_id, vendor_id: int | OrtDeviceVendorId = -1):
|
| 1690 |
+
if vendor_id < 0:
|
| 1691 |
+
# Preserve the historical convenience aliases ("cuda", "dml", "cann")
|
| 1692 |
+
# while making them work with plugin EP shared allocators. Those
|
| 1693 |
+
# allocators are keyed by vendor-specific OrtDevice values even when the
|
| 1694 |
+
# Python package itself was built without the corresponding built-in EP.
|
| 1695 |
+
alias_vendor_id = get_vendor_id_for_device_type(ort_device_name)
|
| 1696 |
+
if alias_vendor_id is not None:
|
| 1697 |
+
return OrtDevice(
|
| 1698 |
+
C.OrtDevice(
|
| 1699 |
+
get_ort_device_type(ort_device_name),
|
| 1700 |
+
C.OrtDevice.default_memory(),
|
| 1701 |
+
int(alias_vendor_id),
|
| 1702 |
+
device_id,
|
| 1703 |
+
)
|
| 1704 |
+
)
|
| 1705 |
+
|
| 1706 |
+
# backwards compatibility with generic predefined OrtDevice names
|
| 1707 |
+
return OrtDevice(
|
| 1708 |
+
C.OrtDevice(
|
| 1709 |
+
get_ort_device_type(ort_device_name),
|
| 1710 |
+
C.OrtDevice.default_memory(),
|
| 1711 |
+
device_id,
|
| 1712 |
+
)
|
| 1713 |
+
)
|
| 1714 |
+
else:
|
| 1715 |
+
# generic. use GPU or NPU for ort_device_name and provide a vendor id.
|
| 1716 |
+
# vendor id of 0 is valid in some cases (e.g. webgpu is generic and does not have a vendor id)
|
| 1717 |
+
return OrtDevice(
|
| 1718 |
+
C.OrtDevice(
|
| 1719 |
+
get_ort_device_type(ort_device_name),
|
| 1720 |
+
C.OrtDevice.default_memory(),
|
| 1721 |
+
int(vendor_id),
|
| 1722 |
+
device_id,
|
| 1723 |
+
)
|
| 1724 |
+
)
|
| 1725 |
+
|
| 1726 |
+
def device_id(self):
|
| 1727 |
+
return self._ort_device.device_id()
|
| 1728 |
+
|
| 1729 |
+
def device_type(self):
|
| 1730 |
+
return self._ort_device.device_type()
|
| 1731 |
+
|
| 1732 |
+
def device_vendor_id(self):
|
| 1733 |
+
return self._ort_device.vendor_id()
|
| 1734 |
+
|
| 1735 |
+
def device_mem_type(self):
|
| 1736 |
+
return self._ort_device.mem_type()
|
| 1737 |
+
|
| 1738 |
+
|
| 1739 |
+
class SparseTensor:
|
| 1740 |
+
"""
|
| 1741 |
+
A data structure that project the C++ SparseTensor object
|
| 1742 |
+
The class provides API to work with the object.
|
| 1743 |
+
Depending on the format, the class will hold more than one buffer
|
| 1744 |
+
depending on the format
|
| 1745 |
+
"""
|
| 1746 |
+
|
| 1747 |
+
def __init__(self, sparse_tensor: C.SparseTensor):
|
| 1748 |
+
"""
|
| 1749 |
+
Internal constructor
|
| 1750 |
+
"""
|
| 1751 |
+
if isinstance(sparse_tensor, C.SparseTensor):
|
| 1752 |
+
self._tensor = sparse_tensor
|
| 1753 |
+
else:
|
| 1754 |
+
# An end user won't hit this error
|
| 1755 |
+
raise ValueError(
|
| 1756 |
+
"`Provided object` needs to be of type `onnxruntime.capi.onnxruntime_pybind11_state.SparseTensor`"
|
| 1757 |
+
)
|
| 1758 |
+
|
| 1759 |
+
def _get_c_tensor(self) -> C.SparseTensor:
|
| 1760 |
+
return self._tensor
|
| 1761 |
+
|
| 1762 |
+
@classmethod
|
| 1763 |
+
def sparse_coo_from_numpy(
|
| 1764 |
+
cls,
|
| 1765 |
+
dense_shape: npt.NDArray[np.int64],
|
| 1766 |
+
values: np.ndarray,
|
| 1767 |
+
coo_indices: npt.NDArray[np.int64],
|
| 1768 |
+
ort_device: OrtDevice,
|
| 1769 |
+
) -> SparseTensor:
|
| 1770 |
+
"""
|
| 1771 |
+
Factory method to construct a SparseTensor in COO format from given arguments
|
| 1772 |
+
|
| 1773 |
+
:param dense_shape: 1-D numpy array(int64) or a python list that contains a dense_shape of the sparse tensor
|
| 1774 |
+
must be on cpu memory
|
| 1775 |
+
:param values: a homogeneous, contiguous 1-D numpy array that contains non-zero elements of the tensor
|
| 1776 |
+
of a type.
|
| 1777 |
+
:param coo_indices: contiguous numpy array(int64) that contains COO indices for the tensor. coo_indices may
|
| 1778 |
+
have a 1-D shape when it contains a linear index of non-zero values and its length must be equal to
|
| 1779 |
+
that of the values. It can also be of 2-D shape, in which has it contains pairs of coordinates for
|
| 1780 |
+
each of the nnz values and its length must be exactly twice of the values length.
|
| 1781 |
+
:param ort_device: - describes the backing memory owned by the supplied nummpy arrays. Only CPU memory is
|
| 1782 |
+
suppored for non-numeric data types.
|
| 1783 |
+
|
| 1784 |
+
For primitive types, the method will map values and coo_indices arrays into native memory and will use
|
| 1785 |
+
them as backing storage. It will increment the reference count for numpy arrays and it will decrement it
|
| 1786 |
+
on GC. The buffers may reside in any storage either CPU or GPU.
|
| 1787 |
+
For strings and objects, it will create a copy of the arrays in CPU memory as ORT does not support those
|
| 1788 |
+
on other devices and their memory can not be mapped.
|
| 1789 |
+
"""
|
| 1790 |
+
return cls(C.SparseTensor.sparse_coo_from_numpy(dense_shape, values, coo_indices, ort_device._get_c_device()))
|
| 1791 |
+
|
| 1792 |
+
@classmethod
|
| 1793 |
+
def sparse_csr_from_numpy(
|
| 1794 |
+
cls,
|
| 1795 |
+
dense_shape: npt.NDArray[np.int64],
|
| 1796 |
+
values: np.ndarray,
|
| 1797 |
+
inner_indices: npt.NDArray[np.int64],
|
| 1798 |
+
outer_indices: npt.NDArray[np.int64],
|
| 1799 |
+
ort_device: OrtDevice,
|
| 1800 |
+
) -> SparseTensor:
|
| 1801 |
+
"""
|
| 1802 |
+
Factory method to construct a SparseTensor in CSR format from given arguments
|
| 1803 |
+
|
| 1804 |
+
:param dense_shape: 1-D numpy array(int64) or a python list that contains a dense_shape of the
|
| 1805 |
+
sparse tensor (rows, cols) must be on cpu memory
|
| 1806 |
+
:param values: a contiguous, homogeneous 1-D numpy array that contains non-zero elements of the tensor
|
| 1807 |
+
of a type.
|
| 1808 |
+
:param inner_indices: contiguous 1-D numpy array(int64) that contains CSR inner indices for the tensor.
|
| 1809 |
+
Its length must be equal to that of the values.
|
| 1810 |
+
:param outer_indices: contiguous 1-D numpy array(int64) that contains CSR outer indices for the tensor.
|
| 1811 |
+
Its length must be equal to the number of rows + 1.
|
| 1812 |
+
:param ort_device: - describes the backing memory owned by the supplied nummpy arrays. Only CPU memory is
|
| 1813 |
+
suppored for non-numeric data types.
|
| 1814 |
+
|
| 1815 |
+
For primitive types, the method will map values and indices arrays into native memory and will use them as
|
| 1816 |
+
backing storage. It will increment the reference count and it will decrement then count when it is GCed.
|
| 1817 |
+
The buffers may reside in any storage either CPU or GPU.
|
| 1818 |
+
For strings and objects, it will create a copy of the arrays in CPU memory as ORT does not support those
|
| 1819 |
+
on other devices and their memory can not be mapped.
|
| 1820 |
+
"""
|
| 1821 |
+
return cls(
|
| 1822 |
+
C.SparseTensor.sparse_csr_from_numpy(
|
| 1823 |
+
dense_shape,
|
| 1824 |
+
values,
|
| 1825 |
+
inner_indices,
|
| 1826 |
+
outer_indices,
|
| 1827 |
+
ort_device._get_c_device(),
|
| 1828 |
+
)
|
| 1829 |
+
)
|
| 1830 |
+
|
| 1831 |
+
def values(self) -> np.ndarray:
|
| 1832 |
+
"""
|
| 1833 |
+
The method returns a numpy array that is backed by the native memory
|
| 1834 |
+
if the data type is numeric. Otherwise, the returned numpy array that contains
|
| 1835 |
+
copies of the strings.
|
| 1836 |
+
"""
|
| 1837 |
+
return self._tensor.values()
|
| 1838 |
+
|
| 1839 |
+
def as_coo_view(self):
|
| 1840 |
+
"""
|
| 1841 |
+
The method will return coo representation of the sparse tensor which will enable
|
| 1842 |
+
querying COO indices. If the instance did not contain COO format, it would throw.
|
| 1843 |
+
You can query coo indices as:
|
| 1844 |
+
|
| 1845 |
+
::
|
| 1846 |
+
|
| 1847 |
+
coo_indices = sparse_tensor.as_coo_view().indices()
|
| 1848 |
+
|
| 1849 |
+
which will return a numpy array that is backed by the native memory.
|
| 1850 |
+
"""
|
| 1851 |
+
return self._tensor.get_coo_data()
|
| 1852 |
+
|
| 1853 |
+
def as_csrc_view(self):
|
| 1854 |
+
"""
|
| 1855 |
+
The method will return CSR(C) representation of the sparse tensor which will enable
|
| 1856 |
+
querying CRS(C) indices. If the instance dit not contain CSR(C) format, it would throw.
|
| 1857 |
+
You can query indices as:
|
| 1858 |
+
|
| 1859 |
+
::
|
| 1860 |
+
|
| 1861 |
+
inner_ndices = sparse_tensor.as_csrc_view().inner()
|
| 1862 |
+
outer_ndices = sparse_tensor.as_csrc_view().outer()
|
| 1863 |
+
|
| 1864 |
+
returning numpy arrays backed by the native memory.
|
| 1865 |
+
"""
|
| 1866 |
+
return self._tensor.get_csrc_data()
|
| 1867 |
+
|
| 1868 |
+
def as_blocksparse_view(self):
|
| 1869 |
+
"""
|
| 1870 |
+
The method will return coo representation of the sparse tensor which will enable
|
| 1871 |
+
querying BlockSparse indices. If the instance did not contain BlockSparse format, it would throw.
|
| 1872 |
+
You can query coo indices as:
|
| 1873 |
+
|
| 1874 |
+
::
|
| 1875 |
+
|
| 1876 |
+
block_sparse_indices = sparse_tensor.as_blocksparse_view().indices()
|
| 1877 |
+
|
| 1878 |
+
which will return a numpy array that is backed by the native memory
|
| 1879 |
+
"""
|
| 1880 |
+
return self._tensor.get_blocksparse_data()
|
| 1881 |
+
|
| 1882 |
+
def to_cuda(self, ort_device):
|
| 1883 |
+
"""
|
| 1884 |
+
Returns a copy of this instance on the specified cuda device
|
| 1885 |
+
|
| 1886 |
+
:param ort_device: with name 'cuda' and valid gpu device id
|
| 1887 |
+
|
| 1888 |
+
The method will throw if:
|
| 1889 |
+
|
| 1890 |
+
- this instance contains strings
|
| 1891 |
+
- this instance is already on GPU. Cross GPU copy is not supported
|
| 1892 |
+
- CUDA is not present in this build
|
| 1893 |
+
- if the specified device is not valid
|
| 1894 |
+
"""
|
| 1895 |
+
return SparseTensor(self._tensor.to_cuda(ort_device._get_c_device()))
|
| 1896 |
+
|
| 1897 |
+
def format(self):
|
| 1898 |
+
"""
|
| 1899 |
+
Returns a OrtSparseFormat enumeration
|
| 1900 |
+
"""
|
| 1901 |
+
return self._tensor.format
|
| 1902 |
+
|
| 1903 |
+
def dense_shape(self) -> npt.NDArray[np.int64]:
|
| 1904 |
+
"""
|
| 1905 |
+
Returns a numpy array(int64) containing a dense shape of a sparse tensor
|
| 1906 |
+
"""
|
| 1907 |
+
return self._tensor.dense_shape()
|
| 1908 |
+
|
| 1909 |
+
def data_type(self) -> str:
|
| 1910 |
+
"""
|
| 1911 |
+
Returns a string data type of the data in the OrtValue
|
| 1912 |
+
"""
|
| 1913 |
+
return self._tensor.data_type()
|
| 1914 |
+
|
| 1915 |
+
def device_name(self) -> str:
|
| 1916 |
+
"""
|
| 1917 |
+
Returns the name of the device where the SparseTensor data buffers reside e.g. cpu, cuda
|
| 1918 |
+
"""
|
| 1919 |
+
return self._tensor.device_name().lower()
|
| 1920 |
+
|
| 1921 |
+
|
| 1922 |
+
# Type hint for user-specified function that allows the user to specify initializer locations when compiling a model.
|
| 1923 |
+
GetInitializerLocationFunc = Callable[
|
| 1924 |
+
[str, OrtValue, C.OrtExternalInitializerInfo | None], C.OrtExternalInitializerInfo | None
|
| 1925 |
+
]
|
| 1926 |
+
|
| 1927 |
+
# Type hint that adheres to the signature expected by ORT.
|
| 1928 |
+
GetInitializerLocationWrapperFunc = Callable[
|
| 1929 |
+
[str, C.OrtValue, C.OrtExternalInitializerInfo | None], C.OrtExternalInitializerInfo | None
|
| 1930 |
+
]
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2c744e0d1683518fa88e229ca006304aad6a29b81b9f0229596969c89ba80c12
|
| 3 |
+
size 689375232
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:15664f6d9bad3f65705df0ec6275eb029cc7efa2310e0243e30349a9195b71ce
|
| 3 |
+
size 1345024
|
|
Binary file (10.8 kB). View file
|
|
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:67264756a9b187e9329c8a9b6c013bb39dfe3a52eaa4f44bc19be283d68afcac
|
| 3 |
+
size 744448
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e54c4900fc60c521dc597dc0b0c87835a18ea6ee2efd38af78c70ac6f10abcbb
|
| 3 |
+
size 25851904
|
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -------------------------------------------------------------------------
|
| 2 |
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
| 3 |
+
# Licensed under the MIT License.
|
| 4 |
+
# --------------------------------------------------------------------------
|
| 5 |
+
"""
|
| 6 |
+
Check OS requirements for ONNX Runtime Python Bindings.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import linecache
|
| 10 |
+
import platform
|
| 11 |
+
import warnings
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def check_distro_info():
|
| 15 |
+
__my_distro__ = ""
|
| 16 |
+
__my_distro_ver__ = ""
|
| 17 |
+
__my_system__ = platform.system().lower()
|
| 18 |
+
|
| 19 |
+
__OS_RELEASE_FILE__ = "/etc/os-release" # noqa: N806
|
| 20 |
+
__LSB_RELEASE_FILE__ = "/etc/lsb-release" # noqa: N806
|
| 21 |
+
|
| 22 |
+
if __my_system__ == "windows":
|
| 23 |
+
__my_distro__ = __my_system__
|
| 24 |
+
__my_distro_ver__ = platform.release().lower()
|
| 25 |
+
|
| 26 |
+
if __my_distro_ver__ not in ["10", "11", "2016server", "2019server", "2022server", "2025server"]:
|
| 27 |
+
warnings.warn(
|
| 28 |
+
f"Unsupported Windows version ({__my_distro_ver__}). ONNX Runtime supports Windows 10 and above, or Windows Server 2016 and above."
|
| 29 |
+
)
|
| 30 |
+
elif __my_system__ == "linux":
|
| 31 |
+
"""Although the 'platform' python module for getting Distro information works well on standard OS images
|
| 32 |
+
running on real hardware, it is not accurate when running on Azure VMs, Git Bash, Cygwin, etc.
|
| 33 |
+
The returned values for release and version are unpredictable for virtualized or emulated environments.
|
| 34 |
+
/etc/os-release and /etc/lsb_release files, on the other hand, are guaranteed to exist and have standard values
|
| 35 |
+
in all OSes supported by onnxruntime. The former is the current standard file to check OS info and the latter
|
| 36 |
+
is its predecessor.
|
| 37 |
+
"""
|
| 38 |
+
# Newer systems have /etc/os-release with relevant distro info
|
| 39 |
+
__my_distro__ = linecache.getline(__OS_RELEASE_FILE__, 3)[3:-1]
|
| 40 |
+
__my_distro_ver__ = linecache.getline(__OS_RELEASE_FILE__, 6)[12:-2]
|
| 41 |
+
|
| 42 |
+
# Older systems may have /etc/os-release instead
|
| 43 |
+
if not __my_distro__:
|
| 44 |
+
__my_distro__ = linecache.getline(__LSB_RELEASE_FILE__, 1)[11:-1]
|
| 45 |
+
__my_distro_ver__ = linecache.getline(__LSB_RELEASE_FILE__, 2)[16:-1]
|
| 46 |
+
|
| 47 |
+
# Instead of trying to parse distro specific files,
|
| 48 |
+
# warn the user ONNX Runtime may not work out of the box
|
| 49 |
+
__my_distro__ = __my_distro__.lower()
|
| 50 |
+
__my_distro_ver__ = __my_distro_ver__.lower()
|
| 51 |
+
elif __my_system__ == "darwin":
|
| 52 |
+
__my_distro__ = __my_system__
|
| 53 |
+
__my_distro_ver__ = platform.release().lower()
|
| 54 |
+
|
| 55 |
+
if int(__my_distro_ver__.split(".")[0]) < 11:
|
| 56 |
+
warnings.warn(
|
| 57 |
+
f"Unsupported macOS version ({__my_distro_ver__}). ONNX Runtime supports macOS 11.0 or later."
|
| 58 |
+
)
|
| 59 |
+
elif __my_system__ == "aix":
|
| 60 |
+
import subprocess # noqa: PLC0415
|
| 61 |
+
|
| 62 |
+
returned_output = subprocess.check_output("oslevel")
|
| 63 |
+
__my_distro_ver__str = returned_output.decode("utf-8")
|
| 64 |
+
__my_distro_ver = __my_distro_ver__str[:3]
|
| 65 |
+
else:
|
| 66 |
+
warnings.warn(
|
| 67 |
+
f"Unsupported platform ({__my_system__}). ONNX Runtime supports Linux, macOS, AIX and Windows platforms, only."
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def get_package_name_and_version_info():
|
| 72 |
+
package_name = ""
|
| 73 |
+
version = ""
|
| 74 |
+
cuda_version = ""
|
| 75 |
+
|
| 76 |
+
try:
|
| 77 |
+
from .build_and_package_info import __version__ as version # noqa: PLC0415
|
| 78 |
+
from .build_and_package_info import package_name # noqa: PLC0415
|
| 79 |
+
|
| 80 |
+
try: # noqa: SIM105
|
| 81 |
+
from .build_and_package_info import cuda_version # noqa: PLC0415
|
| 82 |
+
except ImportError:
|
| 83 |
+
# cuda_version is optional. For example, cpu only package does not have the attribute.
|
| 84 |
+
pass
|
| 85 |
+
except Exception as e:
|
| 86 |
+
warnings.warn("WARNING: failed to collect package name and version info")
|
| 87 |
+
print(e)
|
| 88 |
+
|
| 89 |
+
return package_name, version, cuda_version
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def check_training_module():
|
| 93 |
+
import_ortmodule_exception = None
|
| 94 |
+
|
| 95 |
+
has_ortmodule = False
|
| 96 |
+
try:
|
| 97 |
+
from onnxruntime.training.ortmodule import ORTModule # noqa: F401, PLC0415
|
| 98 |
+
|
| 99 |
+
has_ortmodule = True
|
| 100 |
+
except ImportError:
|
| 101 |
+
# ORTModule not present
|
| 102 |
+
has_ortmodule = False
|
| 103 |
+
except Exception as e:
|
| 104 |
+
# this may happen if Cuda is not installed, we want to raise it after
|
| 105 |
+
# for any exception other than not having ortmodule, we want to continue
|
| 106 |
+
# device version validation and raise the exception after.
|
| 107 |
+
try:
|
| 108 |
+
from onnxruntime.training.ortmodule._fallback import ORTModuleInitException # noqa: PLC0415
|
| 109 |
+
|
| 110 |
+
if isinstance(e, ORTModuleInitException):
|
| 111 |
+
# ORTModule is present but not ready to run yet
|
| 112 |
+
has_ortmodule = True
|
| 113 |
+
except Exception:
|
| 114 |
+
# ORTModule not present
|
| 115 |
+
has_ortmodule = False
|
| 116 |
+
|
| 117 |
+
if not has_ortmodule:
|
| 118 |
+
import_ortmodule_exception = e
|
| 119 |
+
|
| 120 |
+
# collect onnxruntime package name, version, and cuda version
|
| 121 |
+
package_name, version, cuda_version = get_package_name_and_version_info()
|
| 122 |
+
|
| 123 |
+
if has_ortmodule and cuda_version:
|
| 124 |
+
try:
|
| 125 |
+
# collect cuda library build info. the library info may not be available
|
| 126 |
+
# when the build environment has none or multiple libraries installed
|
| 127 |
+
try:
|
| 128 |
+
from .build_and_package_info import cudart_version # noqa: PLC0415
|
| 129 |
+
except ImportError:
|
| 130 |
+
warnings.warn("WARNING: failed to get cudart_version from onnxruntime build info.")
|
| 131 |
+
cudart_version = None
|
| 132 |
+
|
| 133 |
+
def print_build_package_info():
|
| 134 |
+
warnings.warn(f"onnxruntime training package info: package_name: {package_name}")
|
| 135 |
+
warnings.warn(f"onnxruntime training package info: __version__: {version}")
|
| 136 |
+
warnings.warn(f"onnxruntime training package info: cuda_version: {cuda_version}")
|
| 137 |
+
warnings.warn(f"onnxruntime build info: cudart_version: {cudart_version}")
|
| 138 |
+
|
| 139 |
+
# collection cuda library info from current environment.
|
| 140 |
+
from onnxruntime.capi.onnxruntime_collect_build_info import find_cudart_versions # noqa: PLC0415
|
| 141 |
+
|
| 142 |
+
local_cudart_versions = find_cudart_versions(build_env=False, build_cuda_version=cuda_version)
|
| 143 |
+
if cudart_version and local_cudart_versions and cudart_version not in local_cudart_versions:
|
| 144 |
+
print_build_package_info()
|
| 145 |
+
warnings.warn("WARNING: failed to find cudart version that matches onnxruntime build info")
|
| 146 |
+
warnings.warn(f"WARNING: found cudart versions: {local_cudart_versions}")
|
| 147 |
+
except Exception as e:
|
| 148 |
+
warnings.warn("WARNING: failed to collect onnxruntime version and build info")
|
| 149 |
+
print(e)
|
| 150 |
+
|
| 151 |
+
if import_ortmodule_exception:
|
| 152 |
+
raise import_ortmodule_exception
|
| 153 |
+
|
| 154 |
+
return has_ortmodule, package_name, version, cuda_version
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7040243fc8037d65482f974ae2dd1a99394b4fa522e1be05616ef5881f8737ec
|
| 3 |
+
size 16048120
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:db26036e4f1335f5ed7b9da3e334f159dc28b79496c2e8cdfe0455fa04121c72
|
| 3 |
+
size 317944
|