在C#项目中集成人工智能推理能力,最实用的方式之一是将模型导出为ONNX格式,然后使用ONNX Runtime进行加载与执行。ONNX(Open Neural Network Exchange)作为一种开放的模型表示标准,能够让不同训练框架产出的模型在多种运行环境中复用。对于C#开发者来说,不需要依赖Python环境,就能在桌面程序、Web后端或微服务中完成高效的模型推理。

一、环境准备与NuGet包安装
要在C#中使用ONNX Runtime,第一步是通过NuGet引入对应的运行时包。微软官方维护了Microsoft.ML.OnnxRuntime包,它包含了原生的执行引擎,支持CPU以及CUDA、TensorRT等加速后端。如果你的应用只需要基础CPU推理,安装CPU版本的包即可;若要在生产环境利用显卡,可以选择Microsoft.ML.OnnxRuntime.Gpu。
在Visual Studio中,可以通过包管理器控制台执行以下命令完成安装:
// 使用NuGet Package Manager Console安装CPU版本 Install-Package Microsoft.ML.OnnxRuntime // 如果需要GPU支持,可安装GPU版本 // Install-Package Microsoft.ML.OnnxRuntime.Gpu
安装完成后,项目就会引用相关的动态库。需要注意的是,ONNX Runtime的版本最好与导出模型时使用的ONNX算子集版本保持兼容。如果遇到不支持的算子,通常会抛出明确的异常信息,此时可以考虑升级运行时或转换模型时指定更低的opset版本。
二、加载ONNX模型并创建推理会话
模型加载的核心类是InferenceSession,它负责读取.onnx文件并构建内部执行图。创建会话时,可以传入模型文件路径,也可以从字节流加载,方便将模型嵌入到程序资源中。会话对象创建后,可以通过其InputMetadata和OutputMetadata属性查看模型期望的输入输出名称、数据类型和维度。
下面是一段典型的模型加载代码,展示了如何打开会话并输出元数据信息:
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 模型文件路径,可以是相对或绝对路径
string modelPath = "model/resnet50.onnx";
// 创建推理会话
using var session = new InferenceSession(modelPath);
// 输出输入元数据,方便确认名称和形状
foreach (var input in session.InputMetadata)
{
Console.WriteLine($"输入名称: {input.Key}");
Console.WriteLine($"维度: {string.Join(',', input.Value.Dimensions)}");
Console.WriteLine($"类型: {input.Value.ElementType}");
}
}
}
通过打印元数据,开发者能够明确模型需要的输入张量形状。例如图像分类模型常要求输入为[1, 3, 224, 224]的NCHW格式,即一张图、三个通道、高宽各224像素。如果不清楚这些信息,后续构造输入时极易出现形状不匹配的错误。
三、构造输入张量并执行推理
ONNX Runtime使用Tensor<T>类来表达多维数组。我们需要把业务数据(如图片像素)转换为对应类型的扁平数组,并按照模型要求的维度布局填充。以float类型的图像输入为例,要先将图像缩放到目标尺寸,再做归一化,最后按通道顺序写入一维数组。
以下示例演示了如何构造输入并调用Run方法获得输出:
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using System;
using System.Collections.Generic;
class InferenceDemo
{
static void Run()
{
string modelPath = "model/resnet50.onnx";
using var session = new InferenceSession(modelPath);
// 假设已经准备好长度为 1*3*224*224 的float数组 data
float[] data = new float[1 * 3 * 224 * 224];
// 这里省略图像预处理细节,实际应填入归一化后的R,G,B值
// 构造输入张量,维度为 NCHW
var inputTensor = new DenseTensor<float>(data, new int[] { 1, 3, 224, 224 });
var inputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("input", inputTensor)
};
// 执行推理
using var results = session.Run(inputs);
// 读取第一个输出
var output = results[0].AsTensor<float>();
var scores = output.ToArray();
Console.WriteLine($"输出长度: {scores.Length}");
}
}
在上面的代码中,NamedOnnxValue.CreateFromTensor的的第一个参数必须与模型输入元数据中的名称完全一致,否则会报找不到输入的错误。Run方法返回的结果也是一个NamedOnnxValue集合,通过索引或名称可以取出对应的输出张量。
如果模型有多个输入,只需在inputs列表中按顺序或名称添加多个张量即可。推理本身通常很快,但首次运行会因内部图编译产生一定延迟,这属于正常现象。在Web服务中,建议将会话对象设为单例,避免重复加载带来的开销。
四、输出结果解析与常见误区
模型推理得到的输出往往是原始分数或概率分布。对于分类模型,输出一般是一个一维float数组,每个位置代表某个类别的置信度。如果需要最高分类别,可以遍历数组取最大值索引,必要时结合Softmax将分数转为概率。
很多开发者在接入时会踩到两个坑:一是输入数据未做归一化,直接把0到255的像素值送入网络,导致输出完全错误;二是维度顺序弄反,把HWC格式当成NCHW输入。可以通过在Python侧打印同一张图的输入数组前后几个值,与C#侧构造的数组对比来验证。
// 简单取最大分数对应的类别索引
int maxIndex = 0;
float maxScore = scores[0];
for (int i = 1; i < scores.Length; i++)
{
if (scores[i] > maxScore)
{
maxScore = scores[i];
maxIndex = i;
}
}
Console.WriteLine($"预测类别索引: {maxIndex}, 分数: {maxScore}");
此外,当模型包含动态维度(标记为-1)时,需要在运行时根据实际批次大小构造对应形状的张量。如果部署在Linux服务器,还要注意原生库依赖如libonnxruntime.so的权限和路径,确保程序启动时能够加载到正确版本。
五、在生产中集成的最佳实践
将ONNX推理嵌入C#生产系统,推荐将会话创建、输入预处理、输出后处理封装为独立服务类,对外只暴露业务友好的方法,例如PredictImageAsync(byte[] imageBytes)。这样上层调用方不需要了解ONNX细节,也方便后续替换模型文件而不改动业务代码。
对于高并发场景,ONNX Runtime的Session本身是线程安全的,可以被多个线程共享调用,但每个Run调用最好传入独立的输入容器。如果显存或内存紧张,可以限制并发数或采用模型量化版本减小资源占用。配合异步编程模型,还能在ASP.NET Core中提供稳定的AI接口。
public class ModelService
{
private readonly InferenceSession _session;
public ModelService(string modelPath)
{
_session = new InferenceSession(modelPath);
}
public int Predict(float[] inputData)
{
var tensor = new DenseTensor<float>(inputData, new int[] { 1, 3, 224, 224 });
var inputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("input", tensor)
};
using var result = _session.Run(inputs);
var scores = result[0].AsTensor<float>().ToArray();
// 返回最大索引逻辑省略
return 0;
}
}
通过上述方式,C#工程能够以较低门槛融合AI能力,既保留了原有系统的开发效率,又获得了跨平台推理的灵活性。随着模型优化手段的丰富,后续还可以引入OpenVINO或TensorRT执行提供者进一步提升吞吐。
C#ONNX_Runtime模型推理修改时间:2026-08-02 00:00:36