模型部署 - onnx 的导出和分析 -(1) - PyTorch 导出 ONNX - 学习记录
onnx 的导出和分析
- 一、PyTorch 导出 ONNX 的方法
- 1.1、一个简单的例子 -- 将线性模型转成 onnx
- 1.2、导出多个输出头的模型
- 1.3、导出含有动态维度的模型
- 二、pytorch 导出 onnx 不成功的时候如何解决
- 2.1、修改 opset 的版本
- 2.2、替换 pytorch 中的算子组合
- 2.3、在 pytorch 登记( 注册 ) onnx 中某些算子
- 案例一:
- 2.3.1、注册方法一
- 2.3.2、注册方法二
- 案例二:
- 2.4、直接修改 onnx,创建 plugin
一、PyTorch 导出 ONNX 的方法
1.1、一个简单的例子 – 将线性模型转成 onnx
首先我们用 pytorch 定义一个线性模型,nn.Linear : 线性层执行的操作是 y = x * W^T + b,其中 x 是输入,W 是权重,b 是偏置。(实际上就是一个矩阵乘法)
class Model(torch.nn.Module):def __init__(self, in_features, out_features, weights, bias=False):super().__init__()self.linear = nn.Linear(in_features, out_features, bias)with torch.no_grad():self.linear.weight.copy_(weights)def forward(self, x):x = self.linear(x)return x
然后我们再定义一个函数,用于导出 onnx
def export_onnx():input = torch.zeros(1, 1, 1, 4)weights = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)model = Model(4, 3, weights)model.eval() #添加eval防止权重继续更新torch.onnx.export(model = model, args = (input,),f = "model.onnx",input_names = ["input0"],output_names = ["output0"],opset_version = 12)print("Finished onnx export")
可以看到,这里面的关键在函数 torch.onnx.export(),这是 pytorch 导出 onnx 的基本方式,这个函数的参数有很多,但只要一些基本的参数即可导出模型,下面是一些基本参数的定义:
- model (torch.nn.Module): 需要导出的PyTorch模型
- args (tuple or Tensor): 一个元组,其中包含传递给模型的输入张量
- f (str): 要保存导出模型的文件路径。
- input_names (list of str): 输入节点的名字的列表
- output_names (list of str): 输出节点的名字的列表
- opset_version (int): 用于导出模型的 ONNX 操作集版本
最后我们完整的运行一下代码:
import torch
import torch.nn as nn
import torch.onnxclass Model(torch.nn.Module):def __init__(self, in_features, out_features, weights, bias=False):super().__init__()self.linear = nn.Linear(in_features, out_features, bias)with torch.no_grad():self.linear.weight.copy_(weights)def forward(self, x):x = self.linear(x)return xdef export_onnx():input = torch.zeros(1, 1, 1, 4)weights = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)model = Model(4, 3, weights)model.eval() #添加eval防止权重继续更新torch.onnx.export(model = model, args = (input,),f = "model.onnx",input_names = ["input0"],output_names = ["output0"],opset_version = 12)print("Finished onnx export")if __name__ == "__main__":export_onnx()
导出模型后,我们用 netron 查看模型,在终端输入
netron model.onnx

1.2、导出多个输出头的模型
第一步:定义一个多输出的模型:
class Model(torch.nn.Module):def __init__(self, in_features, out_features, weights1, weights2, bias=False):super().__init__()self.linear1 = nn.Linear(in_features, out_features, bias)self.linear2 = nn.Linear(in_features, out_features, bias)with torch.no_grad():self.linear1.weight.copy_(weights1)self.linear2.weight.copy_(weights2)def forward(self, x):x1 = self.linear1(x)x2 = self.linear2(x)return x1, x2
第二步:编写导出 onnx 的函数
def export_onnx():input = torch.zeros(1, 1, 1, 4)weights1 = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)weights2 = torch.tensor([[2, 3, 4, 5],[3, 4, 5, 6],[4, 5, 6, 7]],dtype=torch.float32)model = Model(4, 3, weights1, weights2)model.eval() #添加eval防止权重继续更新torch.onnx.export(model = model, args = (input,),f = "model.onnx",input_names = ["input0"],output_names = ["output0", "output1"],opset_version = 12)print("Finished onnx export")
可以看到,和例 1.1 不一样的地方是 torch.onnx.export 的 output_names
例1.1:output_names = [“output0”]
例1.2:output_names = [“output0”, “output1”]
运行一下完整代码:
import torch
import torch.nn as nn
import torch.onnxclass Model(torch.nn.Module):def __init__(self, in_features, out_features, weights1, weights2, bias=False):super().__init__()self.linear1 = nn.Linear(in_features, out_features, bias)self.linear2 = nn.Linear(in_features, out_features, bias)with torch.no_grad():self.linear1.weight.copy_(weights1)self.linear2.weight.copy_(weights2)def forward(self, x):x1 = self.linear1(x)x2 = self.linear2(x)return x1, x2def export_onnx():input = torch.zeros(1, 1, 1, 4)weights1 = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)weights2 = torch.tensor([[2, 3, 4, 5],[3, 4, 5, 6],[4, 5, 6, 7]],dtype=torch.float32)model = Model(4, 3, weights1, weights2)model.eval() #添加eval防止权重继续更新torch.onnx.export(model = model, args = (input,),f = "model.onnx",input_names = ["input0"],output_names = ["output0", "output1"],opset_version = 12)print("Finished onnx export")if __name__ == "__main__":export_onnx()
用 netron 查看模型,结果如下,模型多出了一个输出结果

1.3、导出含有动态维度的模型
完整运行代码如下:
import torch
import torch.nn as nn
import torch.onnxclass Model(torch.nn.Module):def __init__(self, in_features, out_features, weights, bias=False):super().__init__()self.linear = nn.Linear(in_features, out_features, bias)with torch.no_grad():self.linear.weight.copy_(weights)def forward(self, x):x = self.linear(x)return xdef export_onnx():input = torch.zeros(1, 1, 1, 4)weights = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)model = Model(4, 3, weights)model.eval() #添加eval防止权重继续更新torch.onnx.export(model = model, args = (input,),f = "model.onnx",input_names = ["input0"],output_names = ["output0"],dynamic_axes = {'input0': {0: 'batch'},'output0': {0: 'batch'}},opset_version = 12)print("Finished onnx export")if __name__ == "__main__":export_onnx()
可以看到,比例 1.1 多了一行 torch.onnx.export 的 dynamic_axes 。我们可以用 dynamic_axes 来指定动态维度,其中 'input0': {0: 'batch'} 中的 0 表示在第 0 维度上的元素是动态的,这里取名为 ‘batch’
用 netron 查看模型:

可以看到相对于例1.1,他的维度 0 变成了动态的,并且名为 ‘batch’
二、pytorch 导出 onnx 不成功的时候如何解决
上面是 onnx 可以直接被导出的情况,是因为对应的 pytorch 和 onnx 版本都有相应支持的算子在里面。但是有些时候,我们不能顺利的导出 onnx,下面记录一下常见的解决思路 。
2.1、修改 opset 的版本
这是首先应该考虑的思路,因为有可能只是版本过低然后有些算子还不支持,所以考虑提高 opset 的版本。
比如下面的这个报错,提示当前 onnx 的 opset 版本不支持这个算子,那我们可以去官方手册搜索一下是否在高的版本支持了这个算子

官方手册地址:https://github.com/onnx/onnx/blob/main/docs/Operators.md

又比如说 Acosh 这个算子,在 since version 9 才开始支持,那我们用 7 的时候就是不合适的,升级 opset 版本即可
2.2、替换 pytorch 中的算子组合
有些时候 pytorch 中的一些算子操作在 onnx 中并没有,那我们可以把这些算子替换成 onnx 支持的算子
2.3、在 pytorch 登记( 注册 ) onnx 中某些算子
案例一:
有些算子在 onnx 中是有的,但是在 pytorch 中没被登记,则需要注册一下
比如下面这个案例,我们想要导出 asinh 这个算子的模型
import torch
import torch.onnxclass Model(torch.nn.Module):def __init__(self):super().__init__()def forward(self, x):x = torch.asinh(x)return xdef export_norm_onnx():input = torch.rand(1, 5)model = Model()model.eval()file = "asinh.onnx"torch.onnx.export(model = model, args = (input,),f = file,input_names = ["input0"],output_names = ["output0"],opset_version = 9)print("Finished normal onnx export")if __name__ == "__main__":export_norm_onnx()
但是报错,提示 opset_version = 9 不支持这个算子

但是我们打开官方手册去搜索发现 asinh 在 version 9 又是支持的

这里的问题是 PyTorch 与 onnx 之间没有建立 asinh 的映射 (没有搭建桥梁),所以我们编写一个注册代码,来手动注册一下这个算子
2.3.1、注册方法一
完整代码如下:
import torch
import torch.onnx
import onnxruntime
from torch.onnx import register_custom_op_symbolicdef asinh_symbolic(g, input, *, out=None):return g.op("Asinh", input)
register_custom_op_symbolic('aten::asinh', asinh_symbolic, 12)class Model(torch.nn.Module):def __init__(self):super().__init__()def forward(self, x):x = torch.asinh(x)return xdef validate_onnx():input = torch.rand(1, 5)# PyTorch的推理model = Model()x = model(input)print("result from Pytorch is :", x)# onnxruntime的推理sess = onnxruntime.InferenceSession('asinh.onnx')x = sess.run(None, {'input0': input.numpy()})print("result from onnx is: ", x)def export_norm_onnx():input = torch.rand(1, 5)model = Model()model.eval()file = "asinh.onnx"torch.onnx.export(model = model, args = (input,),f = file,input_names = ["input0"],output_names = ["output0"],opset_version = 12)print("Finished normal onnx export")if __name__ == "__main__":export_norm_onnx()# 自定义完onnx以后必须要进行一下验证validate_onnx()
这段代码的关键在于 算子的注册:
1、定义 asinh_symbolic 函数
def asinh_symbolic(g, input, *, out=None):return g.op("Asinh", input)
- 函数必须是 asinh_symbolic 这个名字
- g: 就是 graph,计算图 (在计算图中添加onnx算子)
- input :symblic的参数需要与Pytorch的asinh接口函数的参数对齐
(def asinh( input: Tensor, *, out: Optional[Tensor]=None) -> Tensor: … )- 符号函数内部调用 g.op, 为 onnx 计算图添加 Asinh 算子
- g.op中的第一个参数是onnx中的算子名字: Asinh
2、使用 register_custom_op_symbolic 函数
register_custom_op_symbolic('aten::asinh', asinh_symbolic, 12)
- aten 是"a Tensor Library"的缩写,是一个实现张量运算的C++库
- asinh 是在名为 aten 的一个c++命名空间下进行实现的
- 将 asinh_symbolic 这个符号函数,与PyTorch的 asinh 算子绑定
- register_op 中的第一个参数是PyTorch中的算子名字: aten::asinh
- 最后一个参数表示从第几个 opset 开始支持(可自己设置)
3、自定义完 onnx 以后必须要进行一下验证,可使用 onnxruntime
2.3.2、注册方法二
import torch
import torch.onnx
import onnxruntime
import functools
from torch.onnx import register_custom_op_symbolic
from torch.onnx._internal import registration_onnx_symbolic = functools.partial(registration.onnx_symbolic, opset=9)@_onnx_symbolic('aten::asinh')
def asinh_symbolic(g, input, *, out=None):return g.op("Asinh", input)class Model(torch.nn.Module):def __init__(self):super().__init__()def forward(self, x):x = torch.asinh(x)return xdef validate_onnx():input = torch.rand(1, 5)# PyTorch的推理model = Model()x = model(input)print("result from Pytorch is :", x)# onnxruntime的推理sess = onnxruntime.InferenceSession('asinh2.onnx')x = sess.run(None, {'input0': input.numpy()})print("result from onnx is: ", x)def export_norm_onnx():input = torch.rand(1, 5)model = Model()model.eval()file = "asinh2.onnx"torch.onnx.export(model = model, args = (input,),f = file,input_names = ["input0"],output_names = ["output0"],opset_version = 12)print("Finished normal onnx export")if __name__ == "__main__":export_norm_onnx()# 自定义完onnx以后必须要进行一下验证validate_onnx()
与上面例子不同的是,这个注册方式跟底层文件的写法是一样的(文件在虚拟环境中的 torch/onnx/symbolic_opset*.py )
通过torch._internal 中的 registration 来注册这个算子,让这个算子可以与底层C++实现的 aten::asinh 绑定
_onnx_symbolic = functools.partial(registration.onnx_symbolic, opset=9)
@_onnx_symbolic('aten::asinh')
def asinh_symbolic(g, input, *, out=None):return g.op("Asinh", input)
案例二:
对于下面这个案例,我们想导出这个算子,这个算子在pytorch 中是存在的,并且可以运行 ,但是直接导出会报错
import torch
import torch.nn as nn
import torchvision
import torch.onnxclass Model(torch.nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(3, 18, 3)self.conv2 = torchvision.ops.DeformConv2d(3, 3, 3)def forward(self, x):x = self.conv2(x, self.conv1(x))return xdef infer():input = torch.rand(1, 3, 5, 5)model = Model()x = model(input)print("input is: ", input.data)print("result is: ", x.data)def export_norm_onnx():input = torch.rand(1, 3, 5, 5)model = Model()model.eval()file = "../models/sample-deformable-conv.onnx"torch.onnx.export(model = model, args = (input,),f = file,input_names = ["input0"],output_names = ["output0"],opset_version = 12)print("Finished normal onnx export")if __name__ == "__main__":infer()export_norm_onnx()
运行报错如下:torchvision.ops.DeformConv2d 这个算子无法被识别

所以我们需要注册一下,写一个注册函数:
@parse_args("v", "v", "v", "v", "v", "i", "i", "i", "i", "i","i", "i", "i", "none")
def dcn_symbolic(g,input,weight,offset,mask,bias,stride_h, stride_w,pad_h, pad_w,dil_h, dil_w,n_weight_grps,n_offset_grps,use_mask):return g.op("custom::deform_conv2d", input, offset)register_custom_op_symbolic("torchvision::deform_conv2d", dcn_symbolic, 12)
注意:
- 这里需要把args的各个参数的类型都指定
- 注册的时候指定
"torchvision::deform_conv2d"和dcn_symbolic绑定 dcn_symbolic的每个参数要跟"torchvision::deform_conv2d"一一对应
2.4、直接修改 onnx,创建 plugin
直接手动创建一个 onnx (这是一个思路,会在后续博客进行总结记录)
参考链接
相关文章:
模型部署 - onnx 的导出和分析 -(1) - PyTorch 导出 ONNX - 学习记录
onnx 的导出和分析 一、PyTorch 导出 ONNX 的方法1.1、一个简单的例子 -- 将线性模型转成 onnx1.2、导出多个输出头的模型1.3、导出含有动态维度的模型 二、pytorch 导出 onnx 不成功的时候如何解决2.1、修改 opset 的版本2.2、替换 pytorch 中的算子组合2.3、在 pytorch 登记&…...
【鸿蒙 HarmonyOS 4.0】多设备响应式布局
一、背景 在渲染页面时,需要根据不同屏幕大小渲染出不同的效果,动态的判断设备屏幕大小,便需要采用多设备响应式布局。这种设计方法能够动态适配各种屏幕大小,确保网站在不同设备上都能呈现出最佳的效果。 二、媒体查询…...
Android ANR 日志分析定位
ANR 是 Android 应用程序中的 "Application Not Responding" 的缩写,中文意思是 "应用程序无响应"。这是当应用程序在 Android 系统上运行时,由于某种原因不能及时响应用户输入事件或执行一个操作,导致界面无法更新&…...
Optional 详解
Optional 详解 1、Optional 介绍2、创建 Optional 对象3、Optional 常用方法1. 判断值是否存在 — isPresent()2. 非空表达式 — ifPresent()3. 设置(获取)默认值 — orElse()、orElseGet()4. 获取值 — get()5. 过滤值 — filter()6. 转换值 — map() 作为一名 Java 程序员&am…...
(科目三)数据库基础知识
1、基本概念 1.1 数据库 1、数据、信息和数据处理 数据是指表达信息的某种物理符号; 信息是对客观事物的反映,是为某一特定目的二提供的决策数据; 数据处理是指将数据转换成信息的过程,是对各类型的数据进行收集、整理、存储、…...
Unity性能优化篇(十) 模型优化之网格合并 Easy Mesh Combine Tool插件使用以及代码实现网格合并
把多个模型的网格合并为一个网格。可以使用自己写代码,使用Unity自带的CombineMeshes方法,也可以使用资源商店的插件,在资源商店搜Mesh Combine可以搜索到相关的插件,例如Easy Mesh Combine Tool等插件。 可大幅度减少Batches数量…...
0.8秒一张图40hx矿卡stable diffusion webui 高质极速出图组合(24.3.3)
新消息是。经过三个月的等待,SD Webui (automatic1111)终于推出了新版本1.8.0,本次版本最大的更新,可能就是pytorch更新到2.1.2, 不过还是晚了pytorch 2.2.2版。 不过这版的一些更新,在forget分支上早就实现了,所以。…...
手写分布式配置中心(四)增加实时刷新功能(长轮询)
上一篇文章中实现了短轮询,不过短轮询的弊端也很明显,如果请求的频率较高,那么就会导致服务端压力大(并发高);如果请求的频率放低,那么客户端感知变更的及时性就会降低。所以我们来看另一种轮询…...
03 | 事务隔离:为什么你改了我还看不见?
提到事务,你肯定不陌生,和数据库打交道的时候,我们总是会用到事务。最经典的例子就是转账,你要给朋友小王转 100 块钱,而此时你的银行卡只有 100 块钱。 转账过程具体到程序里会有一系列的操作,比如查询余…...
Jmeter读取与使用Redis数据
Jmeter 作为当前非常受欢迎的接口测试和性能测试的工具,在企业中得到非常广泛的使用,而 Redis 作为缓存数据库,也在企业中得到普遍使用, Redis服务和客户端安装 windows下安装 默认端口:6379 下载地址: …...
flask 支持跨域访问 非常简单的方式 flask_cors
安装 pip install -U flask-cors from flask import Flask from flask_cors import CORSapp Flask(__name__) CORS(app)app.route("/") def helloWorld():return "Hello, cross-origin-world!"参考 https://www.cnblogs.com/anxminise/p/9814326.html …...
Hololens 2应用开发系列(1)——使用MRTK在Unity中设置混合现实场景并进行程序模拟
Hololens 2应用开发系列(1)——使用MRTK在Unity中进行程序模拟 一、前言二、创建和设置MR场景三、MRTK输入模拟的开启 一、前言 在前面的文章中,我介绍了Hololens 2开发环境搭建和项目生成部署等相关内容,使我们能生成一个简单Ho…...
Newtonsoft.Json
目录 引言 1、简单使用 1.1、官方案例 1.2、JsonConvert 2、特性 2.1、默认模式[JsonObject(MemberSerialization.OptIn/OptOut)] 2.2、序列化为集合JsonArrayAttribute/JsonDictionaryAttribute 2.3、序列化该元素JsonProperty 2.4、忽略元素JsonIgnoreAttribute 2.5、…...
速卖通平台的API返回结果有哪些数据字段?
速卖通(AliExpress)作为阿里巴巴旗下的国际电商平台,提供了API接口供开发者使用,以获取商品、订单、物流等各种信息。然而,速卖通API返回的具体数据字段可能会随着API版本、接口类型以及时间的变化而有所不同。 在编写…...
C++ 标准模板库(STL)
1、vector 动态数组,可随时添加删除元素,在堆空间开辟内存。 方法含义front() 返回第一个元素O(1) back()返回最后一个元素O(1)pop_back()删除最后一个元素O(1)push_back(ele)在末尾插入元素O(1)size()返回实际元素个数O(1)clear()清除所有元素O(N)resi…...
【Javascript】设计模式之发布订阅模式
文章目录 1、现实中的发布-订阅模式2、DOM 事件3、简单的发布-订阅模式4、通用的发布-订阅模式5、先发布再订阅6、小结 发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于…...
DataLoader
import torchvision from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter# 准备的测试数据集 数据放在了CIFAR10文件夹下test_data torchvision.datasets.CIFAR10("./CIFAR10",trainFalse, transformtorchvision.transfor…...
持续集成(CICD)- Jenkins+Git+gogs综合实战(笔记二)
文章目录 七、拉取代码方法一:直接填写命令进行拉取(不建议用这种)方法二:使用源码管理拉取代码步骤一:确认环境(检查自己是否有Git插件)步骤二:构建项目时对项目的源码管理选择 Git步骤三:输入你仓库的SSH地址或者https地址,并且添加gitee的用户名和密方法一和方法二…...
VUE:key属性的作用
在 Vue.js 中,key属性的主要作用是帮助 Vue 在进行 DOM 更新时,能够更准确地识别哪些节点可以复用。 当key值发生变化时,Vue 会执行以下步骤: 1.查找旧节点:Vue 会查找虚拟 DOM 中具有旧key值的节点。 2.匹配新节点…...
linux的通信方案(SYSTEM V)
文章目录 共享内存(Share Memory)信号队列(Message Queue)信号量(semaphore) 进程间通信的核心理念:让不同的进程看见同一块资源 linux下的通信方案: SYSTEM V 共享内存(Share Memory) 特点:1.共享内存是进程见通信最…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
