# ONNX Runtime GPU

> **跨平台、硬件加速的机器学习推理 — 部署来自任何框架的任意模型**

ONNX Runtime（ORT）是微软为 ONNX（开放神经网络交换）模型提供的开源推理引擎。它通过统一的 API 在 CPU、GPU 和专用加速器上提供硬件加速的推理。无论您的模型是在 PyTorch、TensorFlow、Scikit-learn 还是 XGBoost 中训练的 — 如果您能将其导出为 ONNX 格式，ORT 都能更快地运行它。

**GitHub：** [microsoft/onnxruntime](https://github.com/microsoft/onnxruntime) — 14K+ ⭐

***

## 为什么选择 ONNX Runtime？

| 功能           | ONNX Runtime      | TorchScript  | TensorFlow Serving |
| ------------ | ----------------- | ------------ | ------------------ |
| 框架无关         | ✅                 | ❌ 仅限 PyTorch | ❌ 仅限 TF            |
| GPU 加速       | ✅ CUDA/TensorRT   | ✅            | ✅                  |
| INT8/FP16 量化 | ✅                 | 部分支持         | 部分支持               |
| 移动/边缘部署      | ✅                 | 有限           | 有限                 |
| 算子融合         | ✅                 | 部分支持         | ✅                  |
| 易于集成         | ✅ Python/C++/Java | Python       | Python/gRPC        |

{% hint style="success" %}
**主要优势：** 启用 CUDA 执行提供程序的 ONNX Runtime 通常可带来 **1.5–3 倍的加速** 相比原生 PyTorch 推理，在计算机视觉和自然语言处理模型上。
{% endhint %}

***

## 支持的执行提供程序

ONNX Runtime 支持多种硬件后端（执行提供程序）：

| 提供程序                        | 硬件            | 在 Clore.ai 上的预估费用 |
| --------------------------- | ------------- | ----------------- |
| `CUDAExecutionProvider`     | NVIDIA GPU    | 通用 GPU 推理         |
| `TensorrtExecutionProvider` | NVIDIA GPU    | 最大吞吐量             |
| `CPUExecutionProvider`      | CPU           | 回退 / 边缘           |
| `ROCMExecutionProvider`     | AMD GPU       | AMD 硬件            |
| `CoreMLExecutionProvider`   | Apple Silicon | macOS/iOS         |
| `OpenVINOExecutionProvider` | Intel         | Intel CPU/GPU     |

***

## 先决条件

* 具有 GPU 租用的 Clore.ai 账户
* 基础的 Python 知识
* 已训练的模型（PyTorch、TensorFlow 或预先导出的 ONNX）

***

## 步骤 1 — 在 Clore.ai 上租用 GPU

1. 前往 [clore.ai](https://clore.ai) → **市场**
2. 任何 NVIDIA GPU 都可 — 小模型可用 RTX 3070， 大型 Transformer 可用 A100
3. **对于 Transformer 模型：** 建议使用 RTX 4090 或 A100
4. **对于计算机视觉：** RTX 3090 或 RTX 4090 即可满足需求

***

## 步骤 2 — 部署您的容器

ONNX Runtime 没有官方预构建容器，但 NVIDIA CUDA 基础镜像是理想选择：

**Docker 镜像：**

```
nvcr.io/nvidia/cuda:12.2.0-cudnn8-devel-ubuntu22.04
```

**端口：**

```
22
```

**环境变量：**

```
NVIDIA_VISIBLE_DEVICES=all
NVIDIA_DRIVER_CAPABILITIES=compute,utility
```

{% hint style="info" %}
或者，使用 `pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime` 该镜像包含 CUDA 和一个可用于安装 ORT 的 Python 环境。
{% endhint %}

***

## 步骤 3 — 安装支持 GPU 的 ONNX Runtime

```bash
ssh root@<server-ip> -p <ssh-port>

# 更新软件包
apt-get update && apt-get install -y \
    python3-pip \
    python3-dev \
    wget \
    git \
    libgomp1

# 安装带 CUDA 支持的 ONNX Runtime
pip install onnxruntime-gpu

# 安装支持包
pip install \
    onnx \
    numpy \
    Pillow \
    transformers \
    torch \
    torchvision \
    fastapi \
    uvicorn

# 验证安装
python3 << 'EOF'
import onnxruntime as ort
print(f"ORT Version: {ort.__version__}")
print(f"Available providers: {ort.get_available_providers()}")
# 应该包含：CUDAExecutionProvider、TensorrtExecutionProvider、CPUExecutionProvider
EOF
```

***

## 步骤 4 — 将模型导出为 ONNX

### PyTorch 模型导出

```python
import torch
import torch.nn as nn
import onnx

# 示例：导出 ResNet50
model = torch.hub.load('pytorch/vision:v0.10.0', 'resnet50', pretrained=True)
model.eval()

# 创建虚拟输入（batch=1，RGB 图像 224x224）
dummy_input = torch.randn(1, 3, 224, 224)

# 导出为 ONNX
torch.onnx.export(
    model,
    dummy_input,
    "resnet50.onnx",
    export_params=True,
    opset_version=17,              # 使用最新稳定的 opset
    do_constant_folding=True,      # 优化常量折叠
    input_names=["input"],
    output_names=["output"],
    dynamic_axes={
        "input": {0: "batch_size"},    # 动态批大小
        "output": {0: "batch_size"}
    }
)
print("模型导出成功！")

# 验证导出模型
onnx_model = onnx.load("resnet50.onnx")
onnx.checker.check_model(onnx_model)
print("ONNX 模型有效！")
```

### HuggingFace Transformers 导出

```bash
# 安装 optimum 以支持 HuggingFace 的 ONNX 导出
pip install optimum[exporters]

# 导出用于文本分类的 BERT
optimum-cli export onnx \
    --model bert-base-uncased \
    --task text-classification \
    ./bert_onnx/

# 使用优化导出
optimum-cli export onnx \
    --model microsoft/phi-2 \
    --task text-generation \
    --optimize O2 \
    ./phi2_onnx/
```

### 使用 ORT 优化导出

```python
from optimum.onnxruntime import ORTModelForSequenceClassification
from optimum.onnxruntime.configuration import OptimizationConfig, ORTConfig
from optimum.onnxruntime import ORTOptimizer

# 加载并优化
model = ORTModelForSequenceClassification.from_pretrained(
    "distilbert-base-uncased-finetuned-sst-2-english",
    export=True
)

optimizer = ORTOptimizer.from_pretrained(model)
optimization_config = OptimizationConfig(
    optimization_level=2,
    optimize_for_gpu=True,
    fp16=True
)

optimizer.optimize(
    save_dir="./distilbert_optimized",
    optimization_config=optimization_config
)
```

***

## 步骤 5 — 使用 ONNX Runtime 运行推理

### 基础 GPU 推理

```python
import onnxruntime as ort
import numpy as np
from PIL import Image
import torchvision.transforms as transforms

# 使用 GPU 执行提供程序配置会话
# 提供程序按顺序尝试 — 先 CUDA，然后 CPU 回退
providers = [
    ("CUDAExecutionProvider", {
        "device_id": 0,
        "arena_extend_strategy": "kNextPowerOfTwo",
        "gpu_mem_limit": 4 * 1024 * 1024 * 1024,  # 4GB 限制
        "cudnn_conv_algo_search": "EXHAUSTIVE",
        "do_copy_in_default_stream": True,
    }),
    "CPUExecutionProvider"
]

# 性能相关的会话选项
opts = ort.SessionOptions()
opts.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
opts.intra_op_num_threads = 8
opts.execution_mode = ort.ExecutionMode.ORT_PARALLEL

# 加载模型
session = ort.InferenceSession(
    "resnet50.onnx",
    sess_options=opts,
    providers=providers
)

print(f"Running on: {session.get_providers()}")

# 准备输入
transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

img = Image.open("test_image.jpg").convert("RGB")
img_tensor = transform(img).unsqueeze(0).numpy()

# 运行推理
outputs = session.run(None, {"input": img_tensor})
probabilities = outputs[0][0]
top5_idx = probabilities.argsort()[-5:][::-1]
print("前 5 个预测：", top5_idx, probabilities[top5_idx])
```

### 批量推理以提高吞吐量

```python
import onnxruntime as ort
import numpy as np
import base64

session = ort.InferenceSession(
    "resnet50.onnx",
    providers=["CUDAExecutionProvider"]
)

# 预热 GPU
dummy = np.random.randn(1, 3, 224, 224).astype(np.float32)
for _ in range(10):
    session.run(None, {"input": dummy})

# 基准测试不同批量大小
for batch_size in [1, 4, 8, 16, 32, 64]:
    inputs = np.random.randn(batch_size, 3, 224, 224).astype(np.float32)
    
    start = time.time()
    n_iter = 100
    for _ in range(n_iter):
        session.run(None, {"input": inputs})
    elapsed = time.time() - start
    
    throughput = (batch_size * n_iter) / elapsed
    latency = (elapsed / n_iter) * 1000  # 毫秒
    
    print(f"Batch {batch_size:3d}: {throughput:7.1f} img/sec, {latency:.1f}ms/batch")
```

***

## 步骤 6 — TensorRT 执行提供程序（极致性能）

对于 NVIDIA GPU，TensorRT EP 可提供更佳性能：

```python
import onnxruntime as ort
import numpy as np

# TensorRT 执行提供程序配置
tensorrt_provider_options = {
    "trt_max_workspace_size": 4 * 1024 * 1024 * 1024,  # 4GB
    "trt_fp16_enable": True,          # 启用 FP16 以加速推理
    "trt_int8_enable": False,
    "trt_engine_cache_enable": True,   # 缓存已编译的引擎
    "trt_engine_cache_path": "/tmp/trt_cache",
    "trt_max_partition_iterations": 1000,
    "trt_min_subgraph_size": 1,
    "trt_timing_cache_enable": True,
}

providers = [
    ("TensorrtExecutionProvider", tensorrt_provider_options),
    ("CUDAExecutionProvider", {"device_id": 0}),
    "CPUExecutionProvider"
]

session = ort.InferenceSession("resnet50.onnx", providers=providers)
print("活动提供程序：", session.get_providers()[0])

# 第一次运行会编译 TensorRT 引擎（可能需要 1-3 分钟）
# 后续运行使用缓存的引擎，速度很快
```

{% hint style="warning" %}
**TensorRT 引擎编译** 在第一次推理时发生，可能需要 1–5 分钟。启用缓存（`trt_engine_cache_enable: True`）以便已编译的引擎可在会话间重用。
{% endhint %}

***

## 步骤 7 — 为最大速度进行 INT8 量化

```python
from onnxruntime.quantization import quantize_dynamic, quantize_static, QuantType
import onnxruntime as ort
import numpy as np

# 动态 INT8 量化（不需要校准数据）
quantize_dynamic(
    model_input="resnet50.onnx",
    model_output="resnet50_int8_dynamic.onnx",
    weight_type=QuantType.QInt8
)

# 静态 INT8 量化（需要校准数据）
from onnxruntime.quantization import CalibrationDataReader

class ImageCalibrationReader(CalibrationDataReader):
    def __init__(self, data_dir, input_name="input"):
        self.data_dir = data_dir
        self.input_name = input_name
        self.images = self._load_images()
        self.idx = 0
    
    def _load_images(self):
        # 加载 100 张校准图像
        import glob, torchvision.transforms as T
        from PIL import Image
        transform = T.Compose([T.Resize(256), T.CenterCrop(224), T.ToTensor()])
        images = []
        for path in glob.glob(f"{self.data_dir}/*.jpg")[:100]:
            img = Image.open(path).convert("RGB")
            images.append(transform(img).numpy())
        return images
    
    def get_next(self):
        if self.idx >= len(self.images):
            return None
        data = {self.input_name: self.images[self.idx:self.idx+1]}
        self.idx += 1
        return data

from onnxruntime.quantization import quantize_static, QuantFormat
quantize_static(
    model_input="resnet50.onnx",
    model_output="resnet50_int8_static.onnx",
    calibration_data_reader=ImageCalibrationReader("/data/calibration_images"),
    quant_format=QuantFormat.QDQ,
    weight_type=QuantType.QInt8
)
```

***

## 步骤 8 — 构建推理 API

```bash
cat > /workspace/onnx_api.py << 'EOF'
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse
import onnxruntime as ort
import numpy as np
from PIL import Image
import io
import torchvision.transforms as transforms
import json

app = FastAPI(title="ONNX Runtime 推理 API")

# 在启动时加载模型
session = ort.InferenceSession(
    "resnet50.onnx",
    providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
)

# 加载 ImageNet 类别标签
with open("imagenet_classes.json") as f:
    classes = json.load(f)

transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

@app.get("/health")
async def health():
    return {"status": "ok", "providers": session.get_providers()}

@app.post("/predict")
async def predict(file: UploadFile = File(...), topk: int = 5):
    image_data = await file.read()
    img = Image.open(io.BytesIO(image_data)).convert("RGB")
    tensor = transform(img).unsqueeze(0).numpy()
    
    outputs = session.run(None, {"input": tensor})[0][0]
    top_indices = outputs.argsort()[-topk:][::-1]
    
    results = [
        {"label": classes[str(i)], "score": float(outputs[i])}
        for i in top_indices
    ]
    return JSONResponse({"predictions": results})

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8080)
EOF

python3 /workspace/onnx_api.py &

# 测试 API
curl -X POST "http://localhost:8080/predict" \
    -H "accept: application/json" \
    -F "file=@test_image.jpg"
```

***

## 步骤 9 — 监控 GPU 使用情况

```bash
# 推理期间的实时 GPU 监控
watch -n 0.5 nvidia-smi

# 或使用 nvitop 以获得更好的界面
pip install nvitop
nvitop
```

***

## 性能基准

| 模型        | GPU      | 提供程序          | 吞吐量（推理/秒） |
| --------- | -------- | ------------- | --------- |
| ResNet50  | RTX 4090 | CUDA          | \~4,200   |
| ResNet50  | RTX 4090 | TensorRT FP16 | \~8,500   |
| BERT Base | RTX 4090 | CUDA          | \~380     |
| BERT Base | RTX 4090 | TensorRT FP16 | \~720     |
| YOLOv8n   | RTX 3090 | CUDA          | \~1,800   |
| YOLOv8x   | A100     | TensorRT FP16 | \~920     |

***

## 故障排除

### CUDA 提供程序不可用

```bash
# 检查是否安装了带 CUDA 的 ORT（而非仅 CPU 版本）
pip uninstall onnxruntime
pip install onnxruntime-gpu

python3 -c "import onnxruntime as ort; print(ort.get_available_providers())"
```

### TensorRT 编译错误

```bash
# 检查 TensorRT 版本兼容性
python3 -c "import tensorrt; print(tensorrt.__version__)"

# 改用 CUDA EP
providers = ["CUDAExecutionProvider"]  # 跳过 TensorRT EP
```

### 形状不匹配错误

```python
# 检查模型输入/输出形状
for input in session.get_inputs():
    print(f"Input: {input.name}, shape: {input.shape}, type: {input.type}")

for output in session.get_outputs():
    print(f"Output: {output.name}, shape: {output.shape}, type: {output.type}")
```

***

## 高级：多模型流水线

```python
import onnxruntime as ort
import numpy as np

class MultiModelPipeline:
    def __init__(self):
        providers = ["CUDAExecutionProvider"]
        self.detector = ort.InferenceSession("detector.onnx", providers=providers)
        self.classifier = ort.InferenceSession("classifier.onnx", providers=providers)
    
    def run(self, image: np.ndarray) -> list:
        # 阶段 1：目标检测
        boxes = self.detector.run(None, {"image": image})[0]
        
        results = []
        for box in boxes:
            # 裁剪检测到的区域
            crop = self._crop(image, box)
            
            # 阶段 2：对每个区域进行分类
            label = self.classifier.run(None, {"input": crop})[0]
            results.append({"box": box.tolist(), "label": int(label.argmax())})
        
        return results
    
    def _crop(self, image, box):
        x1, y1, x2, y2 = box.astype(int)
        return image[:, :, y1:y2, x1:x2]

pipeline = MultiModelPipeline()
```

***

## 附加资源

* [ONNX Runtime GitHub](https://github.com/microsoft/onnxruntime)
* [ONNX Runtime 文档](https://onnxruntime.ai/docs/)
* [Hugging Face Optimum](https://huggingface.co/docs/optimum/)
* [ONNX 模型库](https://github.com/onnx/models) — 预先导出的模型
* [Netron](https://netron.app/) — ONNX 模型可视化工具
* [ONNX Runtime Python API](https://onnxruntime.ai/docs/api/python/)

***

*在 Clore.ai 上使用 ONNX Runtime 是为需要以最高 GPU 效率服务来自不同框架模型的生产推理服务的理想选择。*

***

## Clore.ai 的 GPU 建议

| 在 Clore.ai 上的预估费用 | 开发/测试             | RTX 3090（24GB） |
| ----------------- | ----------------- | -------------- |
| \~$0.12/每 GPU/每小时 | 生产                | RTX 4090（24GB） |
| 生产级推理             | 大规模               | A100 80GB      |
| 大规模部署             | 💡 本指南中的所有示例均可部署在 | Clore.ai       |

> GPU 服务器上。浏览可用 GPU 并按小时租用 — 无需承诺，提供完整的 root 访问权限。 [Clore.ai](https://clore.ai/marketplace) GPU 服务器。浏览可用 GPU 并按小时租用 — 无需承诺，提供完整的 root 访问权限。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-zh/gpu-devops/onnx-runtime.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
