C# ONNX使用入门教程

背景

有新入坑的老哥不太了解C# onnx 运行的机理,我这边详细介绍一下,之前直接放官方的样例有点草率了。

准备[python环境]

1、要使用onnx,首先我们就自己生成一个onnx文件,请大家准备一下以下需要的[python]环境

cpp 复制代码
python 版本>=3.8 我的机子比较老,只能装3.8
安装torch,可以使用命令行pip install torch
安装netron,可以使用命令行pip install netron
安装onnx,可以使用命令行pip install onnx

2、做好以上准备工作,我们就可以编写一个py文件,命名为create_onnx.py,内容如下:

python 复制代码
#======================================================================#

# 导入依赖库
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,内容如下:

python 复制代码
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内容改为:

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的小伙伴知悉。

相关推荐
Coisinilove5 分钟前
MATLAB——循环语句
开发语言·算法·matlab
机 _ 长7 分钟前
Mamba项目实战-Ubuntu
人工智能·深度学习·ubuntu
傻啦嘿哟19 分钟前
为什么写Python脚本时要加上if __name__ == ‘__main__‘?
开发语言·python
十年一梦实验室34 分钟前
【C++】相机标定源码笔记- 立体视觉相机的校准和图像矫正类
开发语言·c++·笔记·数码相机
山茶花开时。38 分钟前
[SAP ABAP] 版本管理
开发语言·sap·abap
量化交易学徒41 分钟前
【DevOps】Java内存分配与JVM参数详解
java·开发语言·jvm·参数调优
大柏怎么被偷了42 分钟前
【C++】认识使用string类
开发语言·c++
那个那个鱼1 小时前
C#面:请写出C#中的单例模式
开发语言·单例模式·c#·.net
子龙烜1 小时前
数据分析三剑客-Matplotlib
python·数据挖掘·数据分析·matplotlib
图灵追慕者1 小时前
python绘制领域矩形
开发语言·python·领域