背景
有新入坑的老哥不太了解C# onnx 运行的机理,我这边详细介绍一下,之前直接放官方的样例有点草率了。
准备[python环境]
1、要使用onnx,首先我们就自己生成一个onnx文件,请大家准备一下以下需要的[python]环境
python 版本>=3.8 我的机子比较老,只能装3.8
安装torch,可以使用命令行pip install torch
安装netron,可以使用命令行pip install netron
安装onnx,可以使用命令行pip install onnx
2、做好以上准备工作,我们就可以编写一个py文件,命名为create_onnx.py,内容如下:
#======================================================================#
# 导入依赖库
import torch
#======================================================================#
# 打印pytorch基本信息
print("pytorch版本:",torch.__version__)
print("已安装pytorch版本是否支持英伟达显卡",torch.cuda.is_available())
# 自定义网络必须直接或间接继承自torch.nn.Module
class Net(torch.nn.Module):
def __init__(self):
super(self.__class__,self).__init__()
self.linear=torch.nn.Linear(1,1)
def forward(self,x):
return self.linear(x)
#======================================================================#
# 训练自定义网络
# 实例化一个自定义网络对象,用于以下训练
net=Net()
# 选择平方差为标准损失函数
critision=torch.nn.MSELoss()
# 选择随机梯度下降的优化器,学习率设为0.01
optimizer=torch.optim.Adam(net.parameters(),lr=0.01)
# 训练的输入
x=torch.FloatTensor([[0],[1],[2],[3]])
# 训练的输出
y=torch.FloatTensor([[5],[7],[9],[11]])
# 训练3000轮
for epoch in range(1,3001):
Y=net(x)
loss=critision(Y,y)
# 每训练100轮打印一次损失值以便于观察是否收敛
if epoch%100==0:
print(f"epoch:{epoch},loss:{loss.item()}")
# 自动求导
optimizer.zero_grad()
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
#======================================================================#
# 保存模型为onnx格式,方便c#调用
# 输入参数格式为1行1列的数组
input_parameter=torch.randn(1,1)
# 导出onnx格式前务必先调用eval函数
net.eval()
with torch.no_grad():
torch.onnx.export(net,# 自定义的网络对象
input_parameter, # 输入参数格式
"linear.onnx", # 导出onnx格式文件名称
opset_version=11, # 导出onnx指定算子版本
input_names=["input"], # 输入参数名称
output_names=["output"]) # 输出参数名称
#======================================================================#
命令行直接运行它,过一会它训练完毕,就会给我们生成onnx文件,名称为linear.onnx
3、编写另一个py文件,命名为watch_onnx.py,内容如下:
import netron
netron.start("linear.onnx")
命令行直接运行它,过一会浏览器会直接显示出此onnx文件的推理图全过程参数
4、观察此onnx文件的输入输出,其输入为1x1的数组,输出也是1x1的数组。同时注意到它的意义,输入是[[x]],输出是[[y]],于是我们可以开始编写C#程序了。
正题[C#调用onnx]
1、新建一个DotNet控制台项目,千万别选成了DotNetFramework,命名为UseOnnx
2、NuGet中安装onnxruntime,由于只是简单入门介绍 ,这里我们选择cpu版本的onnx运行时安装即可,图中就是cpu的onnx运行时库
3、项目和依赖都准备好了,开始着手C#程序的编写,把Program.cs内容改为:
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using System;
using System.Collections.Generic;
namespace UseOnnx
{
class Program
{
static void Main(string[] args)
{
//onnx文件路径,注意把onnx文件放到我们生成程序同级目录下
string onnx_path = @"linear.onnx";
//根据onnx文件路径实例化一个推理对象
using InferenceSession session = new InferenceSession(onnx_path);
//稠密张量,其维度与模型的输入参数格式一致,1x1==>>new int[]{1,1}
DenseTensor<float> tensor = new DenseTensor<float>(new int[] { 1, 1 });
//测试的输入为101
float x = 101f;
//把输入赋值到张量中
tensor[0, 0] = x;
//构建输入
List<NamedOnnxValue> inputs = new List<NamedOnnxValue>()
{
//onnx推理图的输入名称为input,参数格式为1x1,因此这样构建输入
NamedOnnxValue.CreateFromTensor("input",tensor)
};
//进行推理,得到结果
using IDisposableReadOnlyCollection<DisposableNamedOnnxValue> outputs = session.Run(inputs);
//输出是二维数组
float[,] results = (float[,])outputs[0].Value;
//输出只有一个,它就是onnx推理结果
float y = results[0, 0];
Console.WriteLine($"when x is {x} , onnx calculate y is {y:0.0}");
}
}
}
开始运行,如果没报错的话,应该可以看到近似结果
207.0
由于我的系统是win7,跑程序的时候报错没找到onnxruntime.dll,就算把这个动态链接库拷贝到同级目录下也报同样错误,所以没法最后一步截图演示了。希望win10、win11的小伙伴知悉。