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,并不加载运行,只是为其他程序提供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'
|
|
|
|
|
代码摘要
#统一转换到负数(非正),这样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行,应该也是可以的
这么写是符合了原来的逻辑,但一定效果会好吗?还需要验证
|
|
|
|
|
|
|
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包含多少个数值,取决于模型输出
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
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]) |
|
|