pytorch·onnx·注意事项

 
import torch
from torch import nn

class DLCorr(nn.Module):
    def __init__(self):
        """相似推荐,从指定数据集寻找最自己最接近的数据
        - 要从x2中找到与x1相似的数据

        return
        -------------------------
        返回与自己最相近的数据的索引
        """
        super().__init__()

    def forward(self, X):
        """正向传播
        - 计算目标数据中与自己相似度最高的数据的索引
        """
        X = X.unsqueeze(1)  # [B, 1, D]
        x1 = X[0,0,:]
        x1 = x1.unsqueeze(0)
        x1 = x1.unsqueeze(0)
        x2 = X[1:,:,:]
        
        # 计算距离,每个样本与所有样本的距离
        distance_matrix = torch.sqrt(torch.sum(X ** 2, dim=2))
        print("distance_matrix:",distance_matrix.shape)
        index = torch.argmin(distance_matrix, dim=0) 
        return index
    

 
X = X.unsqueeze(1)  # [B, 1, D]
x1 = X[0,0,:]
x1 = x1.unsqueeze(0)
x1 = x1.unsqueeze(0)
x2 = X[1:,:,:]

数据拆分onnx支持,但可能不友好,能不用就不用
onnx对广播机器支持的不太好,尽量自己将矩阵的shape对齐 
 

 
# 计算距离,每个样本与所有样本的距离
distance_matrix = torch.sqrt(torch.sum(X ** 2, dim=2))

对于矩阵计算,numpy也能现实,但要转Onnx就需要全部使用torch的方法
distance_matrix = np.sqrt(((X[:, np.newaxis] - self.X)**2).sum(axis=2)) 
    

 
return index

返回结果最好是列表,而不是标量 
    

 
修改前的代码有维度判断
X.ndim != 2
这可能导致模型无法正确处理输入,进而导致 ONNX 导出失败。
已删除该代码

 
X = X.unsqueeze(1)  # [B, 1, D]

这一行代码将一个2维矩阵转为三维
这是因为深度学习输入一般至少3维,就是文本处理,[B,C,L]
如果是图像处理,那么是4维,[B,C,H,W]

但这里要实现的,其实类似于机器学习,是2维数表
为了迎合深度学习的格式,增加了一维

其实没有必要
因为不管是[B,C,L],还是[B,C,H,W]这种格式,
是线性变换的需要
本算法求数据第1个元素与后面元素的相似度,根本就没有使用线性变换

因此不需要故意地转换维度

修改代码如下 
    

 
class DLCorr(nn.Module):
    def __init__(self):
        """相似推荐,从指定数据集寻找最自己最接近的数据
        - 求数据第1个元素与后面元素的相似度

        return
        -------------------------
        返回与自己最相近的数据的索引
        """
        super().__init__()

    def forward(self, X):
        """正向传播
        - 计算目标数据中与自己相似度最高的数据的索引
        """
        x1 = X[:1,:]
        x2 = X[1:,:]
        
        # 计算距离,每个样本与所有样本的距离
        distance_matrix = torch.sqrt(torch.sum((x1-x2) ** 2, dim=1))
        index = torch.argmin(distance_matrix, dim=0) 
        return index
    

 
求距离这一行可以优化为
distance_matrix = torch.sum((x1-x2) ** 2, dim=1)

开方是为了套公式
不开方为了工程优化,就是求一个极值,开不开方一样的; 但工程上少计算了一个环节
    

 

    

 

    

 

    

 


 

  

 


pytorch模型转onnx

将pytorch模型保存为onnx

 
仅将pytorch模型保存为onnx,并不加载运行,只是为其他程序提供onnx格式的模型,
这时仅安装pytorch包就可以

 
import torch
import torchvision
    
model = torchvision.models.resnet18()
model.eval()


#[B,C,H,W],trace需要通过实际运行一遍模型导出其静态图,故需要一个输入数据
h0 = torch.zeros(1, 3, 32, 32)

# trace方式,在模型设计时不要用for循环,
# 能在模型外完成的数据操作不要在模型中写,
# 不用inplace等高大上的语法,保持简单,简洁,否则onnx可能无法完全转换过去
torch.onnx.export(
    model=model, 

    # model的参数,就是原来y_out = model(args)的args在这里指定了
    # 有其shape能让模型运行一次就行,不需要真实数据
    args=(h0,), 

    # 储存的文件路径
    f="model02.onnx",  
    
    # 导出模型参数,默认为True
    export_params = True, 
    
    # eval推理模式,dropout,BatchNorm等超参数固定或不生效
    training=torch.onnx.TrainingMode.EVAL,  

    # 打印详细信息
    verbose=True, 

    # 为输入和输出节点指定名称,方便后面查看或者操作
    input_names=["input1"], 
    output_names=["output1"], 

    # 这里的opset,指各类算子以何种方式导出,对应于symbolic_opset11
    opset_version=11, 

    # batch维度是动态的,其他的避免动态
    dynamic_axes={
        "input1": {0: "batch"},
        "output1": {0: "batch"},
    }
)

加载onnx

 
import onnx
model_onnx = onnx.load("model02.onnx")  # 加载onnx模型
onnx.checker.check_model(model_onnx)  # 验证onnx模型是否加载成功

调用onnx

 
import onnxruntime
import numpy as np

# 创建会话
session = onnxruntime.InferenceSession("model02.onnx",providers=[ 'CPUExecutionProvider'])
# session = onnxruntime.InferenceSession("model02.onnx",providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
x = np.random.randn(1, 3, 32, 32)
ort_input = {session.get_inputs()[0].name: x.astype(np.float32)}
ort_output = session.run(None, ort_input)

 

    

 

    

 

    

 

    

示例1

 
#[B,C,H,W],trace需要通过实际运行一遍模型导出其静态图,故需要一个输入数据
h0 = torch.zeros(1, 1350)

# trace方式,在模型设计时不要用for循环,
# 能在模型外完成的数据操作不要在模型中写,
# 不用inplace等高大上的语法,保持简单,简洁,否则onnx可能无法完全转换过去
torch.onnx.export(
    model=model, 

    # model的参数,就是原来y_out = model(args)的args在这里指定了
    # 有其shape能让模型运行一次就行,不需要真实数据
    args=(h0,), 

    # 储存的文件路径
    f=f"model/{pm.model_name}_{pm.model_version}.onnx",  
    
    # 导出模型参数,默认为True
    export_params = True, 
    
    # eval推理模式,dropout,BatchNorm等超参数固定或不生效
    training=torch.onnx.TrainingMode.EVAL,  

    # 打印详细信息
    verbose=True, 

    # 为输入和输出节点指定名称,方便后面查看或者操作
    input_names=["input"], 
    output_names=["output"], 

    # 这里的opset,指各类算子以何种方式导出,对应于symbolic_opset11
    opset_version=11, 

    # batch维度是动态的,其他的避免动态
    dynamic_axes={
        "input": {0: "batch"},
        "output": {0: "batch"},
    }
)


 
import onnxruntime
import numpy as np

# 创建会话
session = onnxruntime.InferenceSession(f"model/{pm.model_name}_{pm.model_version}.onnx",providers=[ 'CPUExecutionProvider'])
# session = onnxruntime.InferenceSession("model02.onnx",providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
x = np.random.randn(1, 1350)
ort_input = {session.get_inputs()[0].name: x.astype(np.float32)}
ort_output = session.run(None, ort_input)


ort_output
[array([0.48087886, 0.51912105], dtype=float32)]


session.get_inputs()[0].name
'input'
    

 


 

  

 

    
softmax转onnx处理

代码摘要

 
#统一转换到负数(非正),这样exp运算后也不会出现极大的数
x = torch.exp(x-x.max())
out =  (x[0]/x[0].sum()).unsqueeze(dim=0)
for i in range(1,batch_size):
    row =  (x[i]/x[i].sum()).unsqueeze(dim=0)
    out = torch.cat((out, row), dim=0)
    

下面的代码,虽然简洁,但对数据做了内部修改,这是梯度计算不允许的

 
batch_size = x.shape[0]

#统一转换到负数(非正),这样exp运算后也不会出现极大的数
x = torch.exp(x-x.max())
for i in range(batch_size):
    x[i]=x[i]/x[i].sum()
    

全代码

 
import torch
from torch import nn
import torchvision
    

 
class DLModel(nn.Module):
"""模型定义
"""

def __init__(self, in_features, out_features):
    """参数网络设计
    - 总体来说,做的事件是将数据从一个维度转换到另外一个维度
    """
    super(DLModel, self).__init__()
    
    self.linear = nn.Linear(in_features=in_features, out_features=out_features)
    
def forward(self, X):
    """正向传播
    - 调用定义的参数网络
    - 让数据流过参数网络,常量数据流过不过的参数产生不同的值
    - 这个过程参数本身不会变
    - 让参数变化的是后面的优化器 
    """
    x = self.linear(X)
    
    batch_size = x.shape[0]
    print("batch_size:",batch_size)
    
    #统一转换到负数(非正),这样exp运算后也不会出现极大的数
    x = torch.exp(x-x.max())
    for i in range(batch_size):
        x[i]=x[i]/x[i].sum()
        
    out = x
    
    return out
    
model = DLModel(in_features=32, out_features=32)

#[B,C,H,W]
h0 = torch.zeros(64, 3, 32, 32)

y_out = model(X=h0)

y_out.shape

    

 
#[B,C,H,W],trace需要通过实际运行一遍模型导出其静态图,故需要一个输入数据
h0 = torch.zeros(1, 3, 32, 32)

# trace方式,在模型设计时不要用for循环,
# 能在模型外完成的数据操作不要在模型中写,
# 不用inplace等高大上的语法,保持简单,简洁,否则onnx可能无法完全转换过去
torch.onnx.export(
    model=model, 

    # model的参数,就是原来y_out = model(args)的args在这里指定了
    # 有其shape能让模型运行一次就行,不需要真实数据
    args=(h0,), 

    # 储存的文件路径
    f="model02.onnx",  
    
    # 导出模型参数,默认为True
    export_params = True, 
    
    # eval推理模式,dropout,BatchNorm等超参数固定或不生效
    training=torch.onnx.TrainingMode.EVAL,  

    # 打印详细信息
    verbose=True, 

    # 为输入和输出节点指定名称,方便后面查看或者操作
    input_names=["input1"], 
    output_names=["output1"], 

    # 这里的opset,指各类算子以何种方式导出,对应于symbolic_opset11
    opset_version=11, 

    # batch维度是动态的,其他的避免动态
    dynamic_axes={
        "input1": {0: "batch"},
        "output1": {0: "batch"},
    }
)
    

 
batch_size: tensor(1)
Exported graph: graph(%input1 : Float(*, 3, 32, 32, strides=[3072, 1024, 32, 1], requires_grad=0, device=cpu),
        %linear.bias : Float(32, strides=[1], requires_grad=1, device=cpu),
        %onnx::MatMul_62 : Float(32, 32, strides=[1, 32], requires_grad=0, device=cpu)):
    %/linear/MatMul_output_0 : Float(*, 3, 32, 32, device=cpu) = onnx::MatMul[onnx_name="/linear/MatMul"](%input1, %onnx::MatMul_62), scope: __main__.DLModel::/torch.nn.modules.linear.Linear::linear # /ai/app/anaconda3/lib/python3.9/site-packages/torch/nn/modules/linear.py:114:0
    ...
    ...
    ...
    %/Concat_2_output_0 : Long(4, strides=[1], device=cpu) = onnx::Concat[axis=0, onnx_name="/Concat_2"](%/Constant_11_output_0, %/Slice_2_output_0), scope: __main__.DLModel:: # /tmp/ipykernel_482/950586334.py:31:0
    %/Reshape_2_output_0 : Float(*, *, *, *, device=cpu) = onnx::Reshape[onnx_name="/Reshape_2"](%/Expand_2_output_0, %/Concat_2_output_0), scope: __main__.DLModel:: # /tmp/ipykernel_482/950586334.py:31:0
    %output1 : Float(*, 3, 32, 32, strides=[3072, 1024, 32, 1], requires_grad=1, device=cpu) = onnx::ScatterND[onnx_name="/ScatterND_2"](%/ScatterND_1_output_0, %/Constant_12_output_0, %/Reshape_2_output_0), scope: __main__.DLModel:: # /tmp/ipykernel_482/950586334.py:31:0
    return (%output1)

重写代码

 
import torch
from torch import nn 
softmax=nn.Softmax(dim=1)
a= torch.tensor([[2,2,5],[2,3,5]],dtype=torch.float32)

# 将原来微小的差距变大了,但并不影响最终到业务的转换
softmax(a)
tensor([[0.0453, 0.0453, 0.9094],
        [0.0420, 0.1142, 0.8438]])


batch_size = 2 
a1=torch.exp(a)
a2=a1.sum()
a=a1/a2
a=a*batch_size
a
tensor([[0.0436, 0.0436, 0.8754],
        [0.0436, 0.1185, 0.8754]])

逻辑说明

 
pytorch 转onnx时,不支持softmax,解决办法就是上面的代码

sum求和是整个批次的和,因为转Onnx时不支持指定维度
这导致最终结果多除一个批次
折中办法是为最终结果再乘一个批次

效果
训练时与原来有些出入,
从局部看,部分行少的部分是多到别的行上去了

预测时,通常预测的是一行,批次为1,除以1或乘以1结果无区别,
其结果概率和为1,与使用softmax完全一致 

 
另外一个比较笨的方法是,知道batch=32
就写
a1[0] = a1[0]/a1[0].sum()
a1[1] = a1[1]/a1[1].sum()
...
a1[31] = a1[31]/a1[31].sum()
也不用循环,就这么写32行,应该也是可以的 

这么写是符合了原来的逻辑,但一定效果会好吗?还需要验证
    

 


 

  

 

    
java调用onnx

Get Started with ORT for Java

 
DOC
https://onnxruntime.ai/docs/get-started/with-java.html

API
https://onnxruntime.ai/docs/api/
https://onnxruntime.ai/docs/api/java/index.html


https://sider.ai/share/b19ad768ed777cfc59fa5a26923494c6


实验环境

 
os:ubantu 
maven:国内镜像
jdk:1.8 

maven依赖

 
<dependency>
    <groupId>com.microsoft.onnxruntime</groupId>
    <artifactId>onnxruntime</artifactId>
    <version>1.15.0</version>
</dependency>

Java代码

 
package org.example;

import ai.onnxruntime.*;
import ai.onnxruntime.OrtSession.Result;
import java.util.Collections;

/**
* onnx demo
*/
public class App 
{
    public static void main( String[] args ) throws OrtException {
        OrtEnvironment env = OrtEnvironment.getEnvironment();

        float[][] sourceArray = new float[1][1350];
        OnnxTensor tensorFromArray = OnnxTensor.createTensor(env,sourceArray);

        String onnx_path = "/opt/tpf/aiwks/code/aisty/jupyter/work/model/SFModel1_1.onnx";
        OrtSession session = env.createSession(onnx_path, new OrtSession.SessionOptions());

        try (Result results = session.run(Collections.singletonMap("input", tensorFromArray))) {
            OnnxValue value = results.get(0);

            //要转换为什么类型,取决于模型输出的shape
            //这里模型输出的是一个浮点列表,因此转换为1维float数组
            float[] res = (float[])(value.getValue());
            System.out.println(res[0]);  //交易判定为正常的概率
            System.out.println(res[1]);  //交易判定为欺诈的概率
        }

        System.out.println( "----onnx demo over!---" );
    }
}

输出 
0.52192116
0.47807884
----onnx demo over!---

Java代码说明

 
Onnx模型由pytroch的神经网络书写,
输入是二维数据,类似于csv中一行行数据
一行是一个样本,共1350行,行数就是批次 
模型的输入是二维数表,
输出是一维数表,共两个值,第1位代表正常的概率,第2位代表欺诈的概率

session.run中指定的input就是当初输出onnx时指定的名称

 

    

实验环境

 
os:ubantu 
maven:阿里镜像
jdk:21

maven依赖

 
<dependency>
    <groupId>com.microsoft.onnxruntime</groupId>
    <artifactId>onnxruntime</artifactId>
    <version>1.20.0</version>
</dependency>

java代码

 
package com.example.onnx;

import ai.onnxruntime.*;
import ai.onnxruntime.OrtSession.Result;
import java.util.Collections;

public class TestOnnx {
    public static void main(String[] args) throws OrtException {
        OrtEnvironment env = OrtEnvironment.getEnvironment();

        String onnx_path = "/home/xt/wks/spring/demo/src/main/resources/model_knn2.onnx";
        OrtSession session = env.createSession(onnx_path, new OrtSession.SessionOptions());
        float[][] target = new float[1][3];
        target[0][0] = 0.3745f;
        target[0][1] = 0.9507f;
        target[0][2] = 0.7320f;

        float[][] hisArray = new float[2][3];
        /*
        [[0.5987, 0.1560, 0.1560],
        [0.0581, 0.8662, 0.6011]]
        * */
        hisArray[0][0] = 0.5987f;
        hisArray[0][1] = 0.1560f;
        hisArray[0][2] = 0.1560f;

        hisArray[1][0] = 0.0581f;
        hisArray[1][1] = 0.8662f;
        hisArray[1][2] = 0.6011f;

        float[][] sourceArray = new float[3][3];
        /*数据格式说明
        [[0.3745, 0.9507, 0.7320],
        [0.5987, 0.1560, 0.1560],
        [0.0581, 0.8662, 0.6011]]
        第1行为要对比的目标数据,之后两行为历史数据
        * */
        sourceArray[0][0] = target[0][0];
        sourceArray[0][1] = target[0][1];
        sourceArray[0][2] = target[0][2];

        sourceArray[1][0] = hisArray[0][0];
        sourceArray[1][1] = hisArray[0][1];
        sourceArray[1][2] = hisArray[0][2];

        sourceArray[2][0] = hisArray[1][0];
        sourceArray[2][1] = hisArray[1][1];
        sourceArray[2][2] = hisArray[1][2];

        OnnxTensor tensorFromArray = OnnxTensor.createTensor(env,sourceArray);
        String inputName = session.getInputNames().iterator().next();

        try (Result results = session.run(Collections.singletonMap(inputName, tensorFromArray))) {
            OnnxValue value = results.get(0);
            System.out.println(value.toString());
            //要转换为什么类型,取决于模型输出类型
            System.out.println(value.getValue());
            long[] res = (long[])(value.getValue());
            int index = (int)res[0];
            float[] corr = hisArray[index];
            System.out.print("[");
            for (float v : corr) {
                System.out.printf("%.4f ", v);
            }
            System.out.print("]");

//            float[] res = (float[])(value.getValue());
//            System.out.println(res[0]);
//            System.out.println(res[1]);
        }

        System.out.println( "----onnx demo over!---" );
    }
}
    
    

 
与示例1的主要区别在于
- 名称可以动态取,不必固定于Input 
- 返回结果如果是Int类型,则使用Long 
- res包含多少个数值,取决于模型输出
    

 

    

 


 

  

 


lightgbm转onnx

 
pip install lightgbm onnx onnxruntime skl2onnx onnxmltools

 

import numpy as np
from lightgbm import LGBMClassifier
from sklearn.datasets import load_iris
import onnxruntime as rt
from skl2onnx import update_registered_converter, to_onnx
from skl2onnx.common.shape_calculator import calculate_linear_classifier_output_shapes
from onnxmltools.convert.lightgbm.operator_converters.LightGbm import convert_lightgbm

# 加载数据
data = load_iris()
X = data.data[:, :2]
y = data.target

# 训练模型
model = LGBMClassifier(n_estimators=3)
model.fit(X, y)

# 注册LightGBM模型的转换器
update_registered_converter(
    LGBMClassifier,
    "LightGbmLGBMClassifier",
    calculate_linear_classifier_output_shapes,
    convert_lightgbm,
    options={"nocl": [True, False], "zipmap": [True, False, "columns"]}
)

# 转换模型为ONNX格式
model_onnx = to_onnx(model, X[:1].astype(np.float32), target_opset={"": 12, "ai.onnx.ml": 2})

# 保存ONNX模型
with open("model.onnx", "wb") as f:
    f.write(model_onnx.SerializeToString())

# 加载ONNX模型
sess = rt.InferenceSession("model.onnx", providers=["CPUExecutionProvider"])

# 查看模型的输入名称
input_name = sess.get_inputs()[0].name
print("Input name:", input_name)

# 进行预测
pred_onx = sess.run(None, {input_name: X[:5].astype(np.float32)})
print("predict", pred_onx[0])
print("predict_proba", pred_onx[1][:1])
    

 
Input name: X
predict [0 0 0 0 0]
predict_proba [{0: 0.519957959651947, 1: 0.2454928159713745, 2: 0.23454922437667847}]
    

 

    

 
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import lightgbm as lgb
from onnxconverter_common.data_types import FloatTensorType
import onnxmltools

iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y)
train_data = lgb.Dataset(X_train, label=y_train)


test_data = lgb.Dataset(X_test, label=y_test)
param = {'num_leaves': 3, 'objective': 'multiclass','num_class':3}
param['metric'] = ['multi_logloss','multi_error']
num_round = 10
bst = lgb.train(param, train_data, num_round, valid_sets=[test_data])
bst.save_model('model.txt')

 
with open('model.txt') as fd:
    for line in fd:
      if line.startswith("max_feature_idx"):
        max_feature_idx = int(line.split("=")[1])
  
lgb_regression_model = lgb.Booster(model_file='model.txt')
initial_type = [("float_input", FloatTensorType([None, max_feature_idx+1]))]
onnx_model = onnxmltools.convert_lightgbm(lgb_regression_model, initial_types = initial_type, target_opset=9 )
onnxmltools.utils.save_model(onnx_model, 'model.onnx')

    

 
import onnxruntime as rt
import numpy
sess = rt.InferenceSession("model.onnx")
input_name = sess.get_inputs()[0].name
label_name = sess.get_outputs()[0].name
pred_onx = sess.run([label_name], {input_name: X_test.astype(numpy.float32)})[0]
print(pred_onx)

[1 1 2 0 2 1 0 2 0 0 1 0 1 2 0 0 1 2 2 0 2 2 1 2 2 2 2 0 0 2 2 0 0 0 0 1 2
 1]
2025-02-13 17:32:25.156722713 [W:onnxruntime:, execution_frame.cc:870 VerifyOutputSizes] Expected shape from model of {1} does not match actual shape of {38} for output label

    

 
import numpy as np
# 加载ONNX模型
sess = rt.InferenceSession("model.onnx", providers=["CPUExecutionProvider"])

# 查看模型的输入名称
input_name = sess.get_inputs()[0].name
print("Input name:", input_name)

# 进行预测
pred_onx = sess.run(None, {input_name: X[:5].astype(np.float32)})
print("predict", pred_onx[0])
print("predict_proba", pred_onx[1][:1])
    

 
Input name: float_input
predict [0 0 0 0 0]
predict_proba [{0: 0.7972142696380615, 1: 0.10541584342718124, 2: 0.09736990183591843}]
2025-02-13 17:32:25.171188105 [W:onnxruntime:, execution_frame.cc:870 VerifyOutputSizes] Expected shape from model of {1} does not match actual shape of {5} for output label

    

 


 

  

 


knn转Onnx

 
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

# 加载数据集
iris = load_iris()
X, y = iris.data, iris.target

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 训练KNN模型
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
    

 
X_train.shape                     #(120, 4)
    
knn.predict_proba(X_test).shape   #(30, 3)

 

    

 
pip install scikit-learn onnx skl2onnx
    

 
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType

#当输入数据维度可变时,需特别声明输入数据的维度:
variable_dim=X_train.shape[1]
initial_types = [('input', FloatTensorType([None, variable_dim]))]  
initial_types  #[('input', FloatTensorType(shape=[None, 4]))]
    

 
# 将模型转换为ONNX格式
onnx_model = convert_sklearn(knn, initial_types=initial_types)

# 保存ONNX模型
import onnx
onnx.save_model(onnx_model, 'knn_model.onnx')
    

 

    

 
import onnxruntime as ort

# 加载ONNX模型
ort_session = ort.InferenceSession('knn_model.onnx',providers=["CPUExecutionProvider"])

# 获取输入和输出的名称
input_name = ort_session.get_inputs()[0].name
output_name = ort_session.get_outputs()[0].name

# 准备输入数据
# X_test_onnx = X_test.astype(np.float32).tolist()  # ONNX Runtime期望输入为float32类型的列表

X_test_onnx = X_test.astype(np.float32)  # ONNX Runtime期望输入为float32类型的列表

# 进行推理
# result = ort_session.run([output_name], {input_name: X_test_onnx})

#第1个参数为None才会输出概率
result = ort_session.run(None, {input_name: X_test_onnx})

#分类
# print(result[0])

# 概率
print(result[1][0])  #{0: 0.0, 1: 1.0, 2: 0.0}


 
特别注意以下两行

# 这种方式不会输出概率,输出结果只有分类
# result = ort_session.run([output_name], {input_name: X_test_onnx})

#第1个参数为None才会输出概率
result = ort_session.run(None, {input_name: X_test_onnx})

 
y_test_pred2[:7]  #array([1, 0, 2, 1, 1, 0, 1], dtype=int64)

y_test[:7]       # array([1, 0, 2, 1, 1, 0, 1])

mse = ((y_test_pred2 - y_test)**2).mean()
mse #0.0 

 

  

 
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

# 加载数据集
iris = load_iris()
X, y = iris.data, iris.target

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 训练KNN模型
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)

  

 
from tpf.mlib import save_onnx_ml,run_onnx_ml 
save_onnx_ml(knn,in_features=X_train.shape[1],file_path="knn1.onnx")
y_pred = run_onnx_ml(file_path="knn1.onnx",X=X_test,proba=False)
((y_pred-y_test)**2).sum()  #0
  

 
y_pred[:7]
array([1, 0, 2, 1, 1, 0, 1], dtype=int64)

y_test[:7]
array([1, 0, 2, 1, 1, 0, 1])

 


 


参考