之前帮兄弟部署模型上线,他问我:“哥,我们有 100 个模型要同时服务,用啥框架?TensorFlow Serving 只支持 TensorFlow,好头疼。”

我说用 Triton + GE Backend,支持所有框架。

好问题。今天一次说清楚。

triton-inference-server-ge-backend 是啥?

triton-inference-server-ge-backend = Triton Inference Server GraphExecutor Backend,昇腾为 Triton 推理服务器开发的 GE(GraphExecutor)后端。

一句话说清楚:triton-inference-server-ge-backend 是昇腾的 Triton 推理服务后端,让你用 Triton 统一管理昇腾 NPU 上的所有模型(TensorFlow、PyTorch、ONNX…),一个框架搞定所有推理服务。

你说气人不气人,之前要为每个框架搭一套服务,现在一个 Triton 全搞定。

为什么要用 triton-inference-server-ge-backend?

三个字:统一管

不用 Triton GE Backend(各自为战)

# TensorFlow 模型 → 搭 TensorFlow Serving
docker run -p 8501:8501 tensorflow/serving

# PyTorch 模型 → 搭 TorchServe
docker run -p 8080:8080 pytorch/torchserve

# ONNX 模型 → 搭 ONNX Runtime Server
docker run -p 8001:8001 onnxruntime/server

# 问题:
# 1. 每个框架一套服务(维护成本高)
# 2. 资源不能共享(NPU 利用率低)
# 3. 监控要 each 看一眼(麻烦)
# 4. 版本管理混乱

用 Triton GE Backend(统一服务)

# 一个 Triton 服务,管理所有模型
$ docker run -p 8000:8000 -p 8001:8001 -p 8002:8002 \
    triton-inference-server-ge-backend:latest

# 查看模型仓库
$ curl localhost:8000/v2/health/ready
{"ready": true}

# 查看所有模型
$ curl localhost:8000/v2/models
{"models": ["resnet50", "bert", "yolo", "gpt"]}

# 推理(统一 API)
$ curl -X POST localhost:8000/v2/models/resnet50/infer \
    -d '{"inputs": [{"name": "input", "shape": [1, 3, 224, 224], "datatype": "FP32", "data": [...]}]}'

你说气人不气人,一个框架搞定所有推理服务。

核心概念就三个

1. Triton Inference Server

Triton 是开源推理服务框架:

# Triton 架构
Triton Inference Server
├── HTTP/REST API (端口 8000)
├── gRPC API (端口 8001)
├── Metrics API (端口 8002)
├── Model Repository (模型仓库)
│   ├── resnet50/
│   │   ├── config.pbtxt
│   │   └── 1/
│   │       └── model.graphdef (或 .onnx / .pt)
│   ├── bert/
│   └── yolo/
└── Backends (后端)
    ├── tensorrt (NVIDIA GPU)
    ├── onnxruntime (CPU/GPU)
    └── ge (昇腾 NPU)← 我们关注的

2. GE Backend

GE Backend 让 Triton 支持昇腾 NPU:

# model_repository/resnet50/config.pbtxt
name: "resnet50"
platform: "graph_executor"  # ← 用 GE 后端
max_batch_size: 32

input [
  {
    name: "input"
    data_type: TYPE_FP32
    format: FORMAT_NCHW
    dims: [3, 224, 224]
  }
]

output [
  {
    name: "output"
    data_type: TYPE_FP32
    dims: [1000]
  }
]

# GE 后端配置
parameters [
  {
    key: "EXECUTOR_TYPE"
    value: { string_value: "graph" }
  },
  {
    key: "DEVICE_ID"
    value: { string_value: "0" }
  }
]

3. 模型仓库

模型按目录组织:

model_repository/
├── resnet50/           # 模型名
│   ├── config.pbtxt    # 模型配置
│   └── 1/              # 版本 1
│       └── model.graphdef  # 模型文件(昇腾格式)
│
├── bert/
│   ├── config.pbtxt
│   └── 1/
│       └── model.graphdef
│
└── ensemble_model/      # 集成模型
    ├── config.pbtxt
    └── 1/
        └── model.graphdef

为什么要用 triton-inference-server-ge-backend?

三个理由:

1. 统一 API

所有模型一个 API:

# 推理 ResNet-50
$ curl -X POST localhost:8000/v2/models/resnet50/infer \
    -d '{"inputs": [...]}'

# 推理 BERT
$ curl -X POST localhost:8000/v2/models/bert/infer \
    -d '{"inputs": [...]}'

# 推理 YOLO
$ curl -X POST localhost:8000/v2/models/yolo/infer \
    -d '{"inputs": [...]}'

# 同一个 API,只是模型名不同

2. 动态批处理

自动合并请求,提升吞吐:

# config.pbtxt
name: "resnet50"
platform: "graph_executor"

# 动态批处理
dynamic_batching {
  preferred_batch_size: [4, 8, 16]
  max_queue_delay_microseconds: 5000  # 最多等 5ms
}
# 效果:
# 原来:每个请求单独推理 → 吞吐 125 img/s
# 现在:4 个请求合并推理 → 吞吐 450 img/s(3.6x)

3. 模型集成

多个模型串起来:

# ensemble_model/config.pbtxt
name: "preprocess_resnet_postprocess"
platform: "ensemble"

# 步骤 1:预处理
step [
  {
    model_name: "preprocess"
    model_version: 1
    input_map {
      key: "input"
      value: "raw_image"
    }
    output_map {
      key: "output"
      value: "preprocessed_image"
    }
  }
]

# 步骤 2:推理
step [
  {
    model_name: "resnet50"
    model_version: 1
    input_map {
      key: "input"
      value: "preprocessed_image"
    }
    output_map {
      key: "output"
      value: "logits"
    }
  }
]

# 步骤 3:后处理
step [
  {
    model_name: "postprocess"
    model_version: 1
    input_map {
      key: "input"
      value: "logits"
    }
    output_map {
      key: "output"
      value: "predictions"
    }
  }
]

怎么用?代码示例

示例 1:部署 ResNet-50

# 1. 准备模型仓库
$ mkdir -p model_repository/resnet50/1

# 2. 转换模型(PyTorch → 昇腾格式)
$ python convert_to_ge.py \
    --input_model resnet50.pth \
    --output_model model_repository/resnet50/1/model.graphdef \
    --input_shape [1,3,224,224] \
    --output_shape [1,1000]

# 3. 写配置文件
$ cat > model_repository/resnet50/config.pbtxt << EOF
name: "resnet50"
platform: "graph_executor"
max_batch_size: 32

input [
  {
    name: "input"
    data_type: TYPE_FP32
    format: FORMAT_NCHW
    dims: [3, 224, 224]
  }
]

output [
  {
    name: "output"
    data_type: TYPE_FP32
    dims: [1000]
  }
]

dynamic_batching {
  preferred_batch_size: [4, 8, 16]
  max_queue_delay_microseconds: 5000
}
EOF

# 4. 启动 Triton
$ docker run -p 8000:8000 -p 8001:8001 -p 8002:8002 \
    -v $(pwd)/model_repository:/models \
    triton-inference-server-ge-backend:latest \
    tritonserver --model-repository=/models

# 5. 测试推理
$ curl -X POST localhost:8000/v2/models/resnet50/infer \
    -H "Content-Type: application/json" \
    -d '{
      "inputs": [
        {
          "name": "input",
          "shape": [1, 3, 224, 224],
          "datatype": "FP32",
          "data": [0.1, 0.2, ...]  # 224*224*3 = 150528 个值
        }
      ]
    }'

# 输出:
# {
#   "model_name": "resnet50",
#   "model_version": "1",
#   "outputs": [
#     {
#       "name": "output",
#       "shape": [1, 1000],
#       "datatype": "FP32",
#       "data": [...]
#     }
#   ]
# }

示例 2:部署 BERT

# 1. 准备模型仓库
$ mkdir -p model_repository/bert/1

# 2. 转换模型(TensorFlow → 昇腾格式)
$ python convert_to_ge.py \
    --input_model bert_pretrained \
    --output_model model_repository/bert/1/model.graphdef \
    --input_shape [1,128] \
    --input_type INT32

# 3. 写配置文件
$ cat > model_repository/bert/config.pbtxt << EOF
name: "bert"
platform: "graph_executor"
max_batch_size: 16

input [
  {
    name: "input_ids"
    data_type: TYPE_INT32
    dims: [128]
  },
  {
    name: "attention_mask"
    data_type: TYPE_INT32
    dims: [128]
  }
]

output [
  {
    name: "pooled_output"
    data_type: TYPE_FP32
    dims: [768]
  }
]

dynamic_batching {
  preferred_batch_size: [4, 8]
  max_queue_delay_microseconds: 5000
}
EOF

# 4. 启动 Triton(如果还没启动)
# 如果已经启动,Triton 会自动加载新模型

# 5. 测试推理
$ curl -X POST localhost:8000/v2/models/bert/infer \
    -H "Content-Type: application/json" \
    -d '{
      "inputs": [
        {
          "name": "input_ids",
          "shape": [1, 128],
          "datatype": "INT32",
          "data": [101, 2023, ..., 102]  # token IDs
        },
        {
          "name": "attention_mask",
          "shape": [1, 128],
          "datatype": "INT32",
          "data": [1, 1, ..., 1, 0, 0]  # attention mask
        }
      ]
    }'

示例 3:客户端代码(Python)

import tritonclient.http as httpclient
import numpy as np

# 连接到 Triton
client = httpclient.InferenceServerClient(url="localhost:8000")

# 检查服务状态
print("Server ready:", client.is_server_ready())
print("Model ready:", client.is_model_ready("resnet50"))

# 准备输入
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)

# 构造推理请求
inputs = [
    httpclient.InferInput("input", input_data.shape, "FP32")
]
inputs[0].set_data_from_numpy(input_data)

outputs = [
    httpclient.InferRequestedOutput("output")
]

# 发送推理请求
results = client.infer(
    model_name="resnet50",
    inputs=inputs,
    outputs=outputs
)

# 获取结果
output_data = results.as_numpy("output")
print(f"Output shape: {output_data.shape}")
print(f"Top-5 predictions: {np.argsort(output_data[0])[-5:][::-1]}")

示例 4:性能监控

# 1. 查看 Metrics
$ curl localhost:8002/metrics

# 输出(节选):
# triton_inference_count{model="resnet50"} 1000
# triton_inference_count{model="bert"} 500
# triton_inference_exec_count{model="resnet50"} 1000
# triton_inference_exec_count{model="bert"} 500
# triton_inference_request_duration_us{model="resnet50",le="1000"} 950
# triton_inference_queue_duration_us{model="resnet50",le="100"} 980

# 2. 查看 GPU/NPU 利用率(另开终端)
$ watch -n 1 npu-smi stats -i 0

# 3. 压力测试
$ python benchmark.py \
    --model resnet50 \
    --concurrency 10 \
    --requests 1000

# 输出:
# Throughput: 1250 req/s
# Latency (p50): 26ms
# Latency (p99): 45ms
# GPU/NPU Utilization: 85%

性能数据

用 Triton GE Backend 的性能提升:

场景 不用 Triton 用 Triton 提升
单模型推理 125 img/s 125 img/s 1x(一样)
动态批处理 125 img/s 450 img/s 3.6x
多模型并发 手动调度 自动调度 2x
资源利用率 40% 85% 2.1x

你说气人不气人,动态批处理直接快 3.6 倍。

跟其他仓库的关系

triton-inference-server-ge-backend 在 CANN 架构里属于第 4 层(昇腾计算执行层),是推理服务后端

依赖关系:

triton-inference-server-ge-backend(Triton 后端)
    ↓ 调用
GE / GraphExecutor(图执行器)
    ↓ 调用
Runtime(运行时)
    ↓ 调用
硬件(昇腾 NPU)

解释一下:

  • Triton:开源推理服务框架
  • GE Backend:Triton 的昇腾后端
  • GE / GraphExecutor:昇腾图执行器
  • 硬件:昇腾 NPU

简单说:triton-inference-server-ge-backend 是 Triton 和昇腾之间的桥梁。想用 Triton 管理昇腾模型,就用它。

triton-inference-server-ge-backend 的核心内容

1. 后端实现

// src/graph_executor_backend.cc
#include "triton/backend/backend_model.h"
#include "ge/ge_api.h"

class GraphExecutorBackend : public triton::backend::BackendModel {
 public:
  void Infer(...) override {
    // 1. 准备输入
    std::vector<ge::Tensor> inputs = PrepareInputs(...);
    
    // 2. 运行推理
    std::vector<ge::Tensor> outputs;
    ge::GraphExecutor executor;
    executor.Run(inputs, &outputs);
    
    // 3. 返回输出
    ProcessOutputs(outputs, ...);
  }
};

2. 模型配置

# config.pbtxt
name: "..."
platform: "graph_executor"
max_batch_size: 32

input [...]
output [...]

dynamic_batching {...}

3. 客户端 SDK

# Python 客户端
import tritonclient.http as httpclient

client = httpclient.InferenceServerClient(url="localhost:8000")
results = client.infer(model_name="resnet50", inputs=[...], outputs=[...])

4. 监控

# Metrics 端点
curl localhost:8002/metrics

# 关键指标:
# - triton_inference_count
# - triton_inference_request_duration_us
# - triton_inference_queue_duration_us

适用场景

什么情况下用 triton-inference-server-ge-backend:

  • 多框架模型:TensorFlow + PyTorch + ONNX
  • 生产部署:要监控、要动态批处理
  • 多模型服务:100+ 模型要同时服务

什么情况下不用:

  • 单模型调试:用 torch.jit.trace() 就行
  • 离线推理:用 torch.inference_mode()

总结

triton-inference-server-ge-backend 就是昇腾的"Triton 推理服务后端":

  • 统一 API:所有模型一个接口
  • 动态批处理:自动合并请求,提升吞吐
  • 模型集成:多个模型串起来
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐