当前位置: 首页 > news >正文

找人建个网站多少钱上海人才招聘网

找人建个网站多少钱,上海人才招聘网,国外二手表网站,手机网站免费生成appONNX初探 转载自#xff1a;https://blog.csdn.net/just_sort/article/details/112912272 0x0. 背景 最近看了一些ONNX的资料#xff0c;一个最大的感受就是这些资料太凌乱了。大多数都是在介绍ONNX模型转换中碰到的坑点以及解决办法。很少有文章可以系统的介绍ONNX的背景…ONNX初探 转载自https://blog.csdn.net/just_sort/article/details/112912272 0x0. 背景 最近看了一些ONNX的资料一个最大的感受就是这些资料太凌乱了。大多数都是在介绍ONNX模型转换中碰到的坑点以及解决办法。很少有文章可以系统的介绍ONNX的背景分析ONNX格式ONNX简化方法等。所以综合了相当多资料之后我准备写一篇ONNX相关的文章希望对大家有用。 0x1. 什么是ONNX 简单描述一下官方介绍开放神经网络交换Open Neural Network Exchange简称ONNX是微软和Facebook提出用来表示深度学习模型的开放格式。所谓开放就是ONNX定义了一组和环境平台均无关的标准格式来增强各种AI模型的可交互性。 换句话说无论你使用何种训练框架训练模型比如TensorFlow/Pytorch/OneFlow/Paddle在训练完毕后你都可以将这些框架的模型统一转换ONNX这种统一的格式进行存储。注意ONNX文件不仅仅存储了神经网络模型的权重同时也存储了模型的结构信息以及网络中每一层的输入输出和一些其它的辅助信息。我们直接从onnx的官方模型仓库拉一个yolov3-tiny的onnx模型地址为https://github.com/onnx/models/tree/master/vision/object_detection_segmentation/tiny-yolov3/model用Netron可视化一下看看ONNX模型长什么样子。 这里我们可以看到ONNX的版本信息这个ONNX模型是由Keras导出来的以及模型的输入输出等信息如果你对模型的输入输出有疑问可以直接看https://github.com/onnx/models/blob/master/vision/object_detection_segmentation/tiny-yolov3/README.md。 在获得ONNX模型之后模型部署人员自然就可以将这个模型部署到兼容ONNX的运行环境中去。这里一般还会设计到额外的模型转换工作典型的比如在Android段利用NCNN部署模型那么就需要将ONNX利用NCNN的转换工具转换到NCNN所支持的bin和param格式。 但在实际使用ONNX的过程中大多数人对ONNX了解得并不多仅仅认为它只是一个模型转换工具人而已可以利用它完成模型转换和部署。正是因为对ONNX的不了解在模型转换过程中出现的各种不兼容或者不支持让很多人浪费了大量时间。这篇文章将从理论和实践2个方面谈一谈ONNX。 0x2. ProtoBuf简介 在分析ONNX组织格式前我们需要了解ProtoBuf, 如果你非常了解ProtoBuf可以略过此节。 ONNX作为一个文件格式我们自然需要一定的规则去读取我们想要的信息或者是写入我们需要保存信息。ONNX使用的是ProtoBuf这个序列化数据结构去存储神经网络的权重信息。熟悉Caffe或者Caffe2的同学应该知道它们的模型存储数据结构协议也是Protobuf。这个从安装ONNX包的时候也可以看到 Protobuf是一种轻便高效的结构化数据存储格式可以用于结构化数据串行化或者说序列化。它很适合做数据存储或数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C、Java、Python 三种语言的 API摘自官方介绍。 Protobuf协议是一个以***.proto后缀文件为基础的这个文件描述了用户自定义的数据结构。如果需要了解更多细节请参考后面的资料3这里只是想表达ONNX是基于Protobuf来做数据存储和传输那么自然onnx.proto**就是ONNX格式文件了接下来我们就分析一下ONNX格式。 0x3. ONNX格式分析 这一节我们来分析一下ONNX的组织格式上面提到ONNX中最核心的部分就是onnx.protohttps://github.com/onnx/onnx/blob/master/onnx/onnx.proto这个文件了它定义了ONNX这个数据协议的规则和一些其它信息。现在是2020年1月这个文件有700多行我们没有必要把这个文件里面的每一行都贴出来我们只要搞清楚里面的核心部分即可。在这个文件里面以message关键字开头的对象是我们需要关心的。我们列一下最核心的几个对象并解释一下它们之间的关系。 ModelProtoGraphProtoNodeProtoValueInfoProtoTensorProtoAttributeProto 当我们加载了一个ONNX之后我们获得的就是一个ModelProto它包含了一些版本信息生产者信息和一个GraphProto。在GraphProto里面又包含了四个repeated数组它们分别是node(NodeProto类型)input(ValueInfoProto类型)output(ValueInfoProto类型)和initializer(TensorProto类型)其中node中存放了模型中所有的计算节点input存放了模型的输入节点output存放了模型中所有的输出节点initializer存放了模型的所有权重参数。 我们知道要完整的表达一个神经网络不仅仅要知道网络的各个节点信息还要知道它们的拓扑关系。这个拓扑关系在ONNX中是如何表示的呢ONNX的每个计算节点都会有input和output两个数组这两个数组是string类型通过input和output的指向关系我们就可以利用上述信息快速构建出一个深度学习模型的拓扑图。这里要注意一下GraphProto中的input数组不仅包含我们一般理解中的图片输入的那个节点还包含了模型中所有的权重。例如Conv层里面的W权重实体是保存在initializer中的那么相应的会有一个同名的输入在input中其背后的逻辑应该是把权重也看成模型的输入并通过initializer中的权重实体来对这个输入做初始化即一个赋值的过程。 最后每个计算节点中还包含了一个AttributeProto数组用来描述该节点的属性比如Conv节点或者说卷积层的属性包含grouppadstrides等等每一个计算节点的属性输入输出信息都详细记录在https://github.com/onnx/onnx/blob/master/docs/Operators.md。 0x4. onnx.helper 现在我们知道ONNX是把一个网络的每一层或者说一个算子当成节点node使用这些Node去构建一个Graph即一个网络。最后将Graph和其它的生产者信息版本信息等合并在一起生成一个model也即是最终的ONNX模型文件。 在构建ONNX模型的时候https://github.com/onnx/onnx/blob/master/onnx/helper.py这个文件非常重要我们可以利用它提供的make_nodemake_graphmake_tensor等等接口完成一个ONNX模型的构建一个示例如下 import onnx from onnx import helper from onnx import AttributeProto, TensorProto, GraphProto# The protobuf definition can be found here: # https://github.com/onnx/onnx/blob/master/onnx/onnx.proto# Create one input (ValueInfoProto) X helper.make_tensor_value_info(X, TensorProto.FLOAT, [3, 2]) pads helper.make_tensor_value_info(pads, TensorProto.FLOAT, [1, 4])value helper.make_tensor_value_info(value, AttributeProto.FLOAT, [1])# Create one output (ValueInfoProto) Y helper.make_tensor_value_info(Y, TensorProto.FLOAT, [3, 4])# Create a node (NodeProto) - This is based on Pad-11 node_def helper.make_node(Pad, # node name[X, pads, value], # inputs[Y], # outputsmodeconstant, # attributes )# Create the graph (GraphProto) graph_def helper.make_graph([node_def],test-model,[X, pads, value],[Y], )# Create the model (ModelProto) model_def helper.make_model(graph_def, producer_nameonnx-example)print(The model is:\n{}.format(model_def)) onnx.checker.check_model(model_def) print(The model is checked!)这个官方示例为我们演示了如何使用onnx.helper的make_tensormake_tensor_value_infomake_attributemake_nodemake_graphmake_node等方法来完整构建了一个ONNX模型。需要注意的是在上面的例子中输入数据是一个一维Tensor初始维度为[2]这也是为什么经过维度为[1,4]的Pad操作之后获得的输出Tensor维度为[3,4]。另外由于Pad操作是没有带任何权重信息的所以当你打印ONNX模型时ModelProto的GraphProto是没有initializer这个属性的。 0x5. onnx-simplifier 原本这里是要总结一些使用ONNX进行模型部署经常碰到一些因为版本兼容性或者各种框架OP没有对齐等原因导致的各种BUG。但是这样的话显得篇幅会很长所以这里以一个经典的Pytorch转ONNX的reshape问题为例子来尝试讲解一下大老师的onnx-simplifier个人认为这个问题是基于ONNX模型部署最经典的问题希望解决这个问题的过程中大家能有所收获。 问题发生在当我们想把下面这段代码导出ONNX模型时 import torchclass JustReshape(torch.nn.Module):def __init__(self):super(JustReshape, self).__init__()def forward(self, x):return x.view((x.shape[0], x.shape[1], x.shape[3], x.shape[2]))net JustReshape() model_name just_reshape.onnx dummy_input torch.randn(2, 3, 4, 5) torch.onnx.export(net, dummy_input, model_name, input_names[input], output_names[output])由于这个模型输入维度是固定的所以我们期望模型是这样的 但是即使使用了ONNX的polished工具也只能获得下面的模型 要解决这个问题有两种方法第一种是做一个强制类型转换将x.shape[0]类似的变量强制转换为常量即int(x.shape[0])或者使用大老师的onnx-simplifer来解决这一问题。之前一直好奇onnx-simplifer是怎么做的最近对ONNX有了一些理解之后也能逐步看懂做法了。我来尝试解释一下。onnx-simplifer的核心思路就是利用onnxruntime推断一遍ONNX的计算图然后使用常量输出替代冗余的运算OP。 def simplify(model: Union[str, onnx.ModelProto], check_n: int 0, perform_optimization: bool True,skip_fuse_bn: bool False, input_shapes: Optional[TensorShapes] None, skipped_optimizers: Optional[Sequence[str]] None, skip_shape_inferenceFalse) \- Tuple[onnx.ModelProto, bool]:if input_shapes is None:input_shapes {}if type(model) str:# 加载ONNX模型model onnx.load(model)# 检查ONNX模型格式是否正确图结构是否完整节点是否正确等onnx.checker.check_model(model)# 深拷贝一份原始ONNX模型model_ori copy.deepcopy(model)if not skip_shape_inference:# 获取ONNX模型中特征图的尺寸model infer_shapes(model)input_shapes check_and_update_input_shapes(model, input_shapes)if perform_optimization:model optimize(model, skip_fuse_bn, skipped_optimizers)const_nodes get_constant_nodes(model)res forward_for_node_outputs(model, const_nodes, input_shapesinput_shapes)const_nodes clean_constant_nodes(const_nodes, res)model eliminate_const_nodes(model, const_nodes, res)onnx.checker.check_model(model)if not skip_shape_inference:model infer_shapes(model)if perform_optimization:model optimize(model, skip_fuse_bn, skipped_optimizers)check_ok check(model_ori, model, check_n, input_shapesinput_shapes)return model, check_ok上面有一行model infer_shapes(model) 是获取ONNX模型中特征图的尺寸它的具体实现如下 def infer_shapes(model: onnx.ModelProto) - onnx.ModelProto:try:model onnx.shape_inference.infer_shapes(model)except:passreturn model我们保存一下调用了这个接口之后的ONNX模型并将其可视化看一下 相对于原始的ONNX模型现在每一条线都新增了一个shape信息代表它的前一个特征图的shape是怎样的。 接着程序使用到了check_and_update_input_shapes接口这个接口的代码示例如下它可以用来判断输入的格式是否正确以及输入模型是否有所有的指定输入节点。 def check_and_update_input_shapes(model: onnx.ModelProto, input_shapes: TensorShapes) - TensorShapes:input_names get_input_names(model)if None in input_shapes:if len(input_names) 1:input_shapes[input_names[0]] input_shapes[None]del input_shapes[None]else:raise RuntimeError(The model has more than 1 inputs, please use the format input_name:dim0,dim1,...,dimN in --input-shape)for x in input_shapes:if x not in input_names:raise RuntimeError(The model doesn\t have input named {}.format(x))return input_shapes在这个例子中如果我们指定input_shapes为{‘input’: [2, 3, 4, 5]}那么这个函数的输出也为{‘input’: [2, 3, 4, 5]}。如果不指定输出就是{}。验证这个函数的调用代码如下所示 确定了输入没有问题之后程序会根据用户指定是否优化ONNX模型进入优化函数函数定义如下 def optimize(model: onnx.ModelProto, skip_fuse_bn: bool, skipped_optimizers: Optional[Sequence[str]]) - onnx.ModelProto::model参数: 待优化的ONXX模型.:return: 优化之后的ONNX模型.简化之前, 使用这个方法产生会在forward_all用到的ValueInfo简化之后使用这个方法去折叠前一步产生的常量到initializer中并且消除没被使用的常量onnx.checker.check_model(model)onnx.helper.strip_doc_string(model)optimizers_list [eliminate_deadend,eliminate_nop_dropout,eliminate_nop_cast,eliminate_nop_monotone_argmax, eliminate_nop_pad,extract_constant_to_initializer, eliminate_unused_initializer,eliminate_nop_transpose,eliminate_nop_flatten, eliminate_identity,fuse_add_bias_into_conv,fuse_consecutive_concats,fuse_consecutive_log_softmax,fuse_consecutive_reduce_unsqueeze, fuse_consecutive_squeezes,fuse_consecutive_transposes, fuse_matmul_add_bias_into_gemm,fuse_pad_into_conv, fuse_transpose_into_gemm, eliminate_duplicate_initializer]if not skip_fuse_bn:optimizers_list.append(fuse_bn_into_conv)if skipped_optimizers is not None:for opt in skipped_optimizers:try:optimizers_list.remove(opt)except ValueError:passmodel onnxoptimizer.optimize(model, optimizers_list,fixed_pointTrue)onnx.checker.check_model(model)return model 这个函数的功能是对原始的ONNX模型做一些图优化工作比如merge_bnfuse_add_bias_into_conv等等。我们使用onnx.save保存一下这个例子中图优化后的模型可以发现它和优化前的可视化效果是一样的如下图所示 这是因为在这个模型中是没有上面列举到的那些可以做图优化的情况但是当我们打印一下ONNX模型我们会发现optimize过后的ONNX模型多出一些initializer数组 这些数组存储的就是这个图中那些常量OP的具体值通过这个处理我们就可以调用get_constant_nodes函数来获取ONNX模型的常量OP了这个函数的详细解释如下 def get_constant_nodes(m: onnx.ModelProto) - List[onnx.NodeProto]:const_nodes []# 如果节点的name在ONNX的GraphProto的initizlizer数组里面它就是静态的tensorconst_tensors [x.name for x in m.graph.initializer]# 显示的常量OP也加进来const_tensors.extend([node.output[0]for node in m.graph.node if node.op_type Constant])# 一些节点的输出shape是由输入节点决定的我们认为这个节点的输出shape并不是常量# 所以我们不需要简化这种节点dynamic_tensors []# 判断是否为动态OPdef is_dynamic(node):if node.op_type in [NonMaxSuppression, NonZero, Unique] and node.input[0] not in const_tensors:return Trueif node.op_type in [Reshape, Expand, Upsample, ConstantOfShape] and len(node.input) 1 and node.input[1] not in const_tensors:return Trueif node.op_type in [Resize] and ((len(node.input) 2 and node.input[2] not in const_tensors) or (len(node.input) 3 and node.input[3] not in const_tensors)):return Truereturn Falsefor node in m.graph.node:if any(x in dynamic_tensors for x in node.input):dynamic_tensors.extend(node.output)elif node.op_type Shape:const_nodes.append(node)const_tensors.extend(node.output)elif is_dynamic(node):dynamic_tensors.extend(node.output)elif all([x in const_tensors for x in node.input]):const_nodes.append(node)const_tensors.extend(node.output)# 深拷贝return copy.deepcopy(const_nodes)在这个例子中我们打印一下通过这个获取常量OP二点函数之后Graph中有哪些节点被看成了常量节点。 获取了模型中所有的常量OP之后我们需要把所有的静态节点扩展到ONNX Graph的输出节点列表中然后利用onnxruntme执行一次forward def forward_for_node_outputs(model: onnx.ModelProto, nodes: List[onnx.NodeProto],input_shapes: Optional[TensorShapes] None) - Dict[str, np.ndarray]:if input_shapes is None:input_shapes {}model copy.deepcopy(model)# nodes 是Graph中所有的静态OPadd_features_to_output(model, nodes)res forward(model, input_shapesinput_shapes)return res其中add_features_to_output的定义如下 def add_features_to_output(m: onnx.ModelProto, nodes: List[onnx.NodeProto]) - None:Add features to output in pb, so that ONNX Runtime will output them.:param m: the model that will be run in ONNX Runtime:param nodes: nodes whose outputs will be added into the graph outputs# ONNX模型的graph扩展输出节点获取所有静态OP的输出和原始输出节点的输出for node in nodes:for output in node.output:m.graph.output.extend([onnx.ValueInfoProto(nameoutput)])最后的forward函数就是利用onnxruntime推理获得我们指定的输出节点的值。这个函数这里不进行解释。推理完成之后进入下一个函数clean_constant_nodes这个函数的定义如下 def clean_constant_nodes(const_nodes: List[onnx.NodeProto], res: Dict[str, np.ndarray]):It seems not needed since commit 6f2a72, but maybe it still prevents some unknown bug:param const_nodes: const nodes detected by get_constant_nodes:param res: The dict containing all tensors, got by forward_all:return: The constant nodes which have an output in resreturn [node for node in const_nodes if node.output[0] in res]这个函数是用来清洗那些没有被onnxruntime推理的静态节点但通过上面的optimize逻辑我们的graph中其实已经不存在这个情况了没有被onnxruntime推理的静态节点在图优化阶段会被优化掉因此这个函数理论上是可以删除的。这个地方是为了避免删除掉有可能引发其它问题就保留了。 不过从一些实际经验来看还是保留吧毕竟不能保证ONNX的图优化就完全正确前段时间刚发现了TensorRT图优化出了一个BUG。保留这个函数可以提升一些程序的稳定性。 接下来就是这个onnx-simplifier最核心的步骤了即将常量节点从原始的ONNX删除函数接口为eliminate_const_nodes def eliminate_const_nodes(model: onnx.ModelProto, const_nodes: List[onnx.NodeProto],res: Dict[str, np.ndarray]) - onnx.ModelProto::model参数: 原始ONNX模型:const_nodes参数: 使用get_constant_nodes获得的静态OP:res参数: 包含所有输出Tensor的字典:return: 简化后的模型. 所有冗余操作都已删除.for i, node in enumerate(model.graph.node):if node in const_nodes:for output in node.output:new_node copy.deepcopy(node)new_node.name node_ outputnew_node.op_type Constantnew_attr onnx.helper.make_attribute(value,onnx.numpy_helper.from_array(res[output], nameoutput))del new_node.input[:]del new_node.attribute[:]del new_node.output[:]new_node.output.extend([output])new_node.attribute.extend([new_attr])insert_elem(model.graph.node, i 1, new_node)del model.graph.node[i]return model运行这个函数之后我们获得的ONNX模型可视化结果是这样子的 注意这里获得的ONNX模型中虽然常量节点已经从Graph中断开了即相当于这个DAG里面多了一些单独的点但是这些点还是存在的。因此我们最后还需要执行一次optimize就可以获得最终简化后的ONNX模型了。最终简化后的ONNX模型如下图所示 0x6. 总结 好了介于篇幅原因介绍ONNX的第一篇文章就介绍到这里了,后续可能会结合更多实践的经验来谈谈ONNX了例如OneFlow模型导出ONNX进行部署。总之文章很长谢谢你的观看希望这篇文章有帮助到你。最后欢迎star大老师的onnx-simplifier。 0x7. 参考资料 【1】https://zhuanlan.zhihu.com/p/86867138【2】https://oldpan.me/archives/talk-about-onnx【3】https://blog.csdn.net/chengzi_comm/article/details/53199278【4】https://www.jianshu.com/p/a24c88c0526a【5】https://bindog.github.io/blog/2020/03/13/deep-learning-model-convert-and-depoly/【6】 https://github.com/daquexian/onnx-simplifier
http://www.huolong8.cn/news/317284/

相关文章:

  • 毕业视频代做网站品牌推广建议
  • 宁夏网站seo工程造价信息网站
  • 商贸网站建设做网站推广选择什么最好
  • 建网站方案安装wordpress linux
  • 哪里有手机网站制作公司建设银行梅州分行网站
  • 网站建设推广咨询平台工厂做网站
  • 不用写代码做网站国内网站推广
  • 查看网站开发平台企业标准查询网官网
  • 百度云资源搜索网站泉州(晋江)网站建设
  • linux 配置网站域名深圳罗湖企业网站
  • 网站虚拟主机博客和网站的区别
  • 个人备案网站可以做商城展示网站制作公司怎么收费
  • 网站开发人才需求wordpress自定义小工具
  • 导航特效网站wordpress顶部菜单设置
  • 衡水城乡建设局网站首页放置文件
  • 郑州网站建设怎样搜索框html代码
  • 百度做的网站后台怎么更新网站建设改版目的
  • 世界杯网站开发网站网站怎么做的
  • 人是用什么做的视频网站惠州软件开发公司
  • 搜狗站长工具平台怎么知道网站开发语言
  • 全球最受欢迎的网站排名中铁建设集团有限公司梅洪亮
  • 黄金网站app下载免费手表网站错误怎么办
  • 东莞网站建设排名怎么做那些盗号网站
  • 网站tag聚合怎么做软件制作过程
  • 合山市网站视频软件下载app
  • 制作网站图片不显示网页游戏设计培训学校
  • 京东网站开发框架做携程网站的技术
  • 视频网站 做综艺 电视台做图片可以卖给那些网站
  • 网站案例演示做爰全过程免费网站的视频教程
  • 网站 谁建设谁负责外贸阿里巴巴国际站