赞
踩
最近的一篇工作里面需要频繁的从网络forward path中提取中间结果,因为ncnn比较好魔改,所以就选了ncnn来做工作本身的实现。同时也非常感谢nihui同学,中间获得了nihui同学大量的帮助。这里稍微整理一下,如何正确在ncnn中取出结果。
(如果不知道什么是ncnn的话,点击下面卡片,是一个超好用的端侧inference框架!)
Github - Tencent/ncnngithub.com这里需要理解ncnn的几个重要的concept,方便后面解释。
ncnn的layer对应着实际的计算node,比如说conv或者add。
需要注意的是,在转换pytorch或者tf的模型到ncnn中时,除了对于基本graph的翻译之外,ncnn还会插入额外的node,最常见也是最重要的就是split。split node本身的意义是将输入的blob(也就是tensor)浅拷贝一份。ncnn不像tf或者torch,和tflite的结构类似,blob是producer和consumer是严格一一对应的,单一producer只支持单一consumer,不允许一个producer对应多个consumer。
2. Blob
这个概念在pytorch和tf中并不存在,所以可能广大pythoner比较难以理解。blob的本质是构建一个producer-consumer对,其内容是tensor。在param中的表达是一个blob name,(也就是我们在input和extract中填写的那个)。一个producer 只能对应一个consumer,如果要一对多,需要借助split layer来实行producer的拷贝。
3. Extractor
ncnn是将网络的结构用blob链接构成一个DAG(有向无环图),给定input以及extract之后,从extract向上查找,找到通往input的路径,让后再顺序路径forward下来。所以流程中对于output不必需要的node不会被计算,同时,如果extract一次之后,再重新extract,重复计算过的node也不会被计算。
那么,有些同学可能要问了,如果ncnn每一层都缓存,会不会占用内存特别大呢?实际上ncnn并不是每一层都会缓存,只有分叉的split,那么ncnn会在分叉的位置缓存blob。
在CPU上比较简单,只需要简单的构建一个生成器,给定输入数据,再extract就行了。
ncnn
这里的out_mat是网络结构中的浅拷贝,并不需要实际搬运数据。如果存在packing或者使用了低精度的计算,比如说fp16/bf16,那么extractor默认也会还原成无packing的fp32状态。
如果需要在网络中抽取多次,同时这些还原的操作不是必要的,比如说我还需要把得到的结果feed进其他layer里处理,那么可以直接extract不经过还原的mat。
ncnn
给定extract的flag为1,那么就会输出不经过还原的mat了,实测还是可以节省几个ms的。(mobilenetv2,抽10层的中间结果,高通骁龙855)
在GPU上,如果你不在意gpu到cpu上的缓存拷贝,那么可以完全参考cpu上的实现,那么这里主要介绍一下,如果我需要实现zero copy的提取gpu上的Vulkan Mat(VkMat)应该怎么做。
ncnn
需要先新建一个vkCmd,这样可以保证操作的都是同一个设备,之后使用extractor的重载,就可以直接得到GPU上的tensor了。
需要注意的是,虽然这样不存在拷贝时间了,但是Host和Device(GPU)的Sync依旧需要一定的时间(几百us到几个毫秒),频繁的extract还是会很耗时的。
大概就是这样)下次有空讲讲怎么样在程序中自己构建一个layer,并forward Mat或者vkMat。感谢nihui,小字母和大家的帮助qwq)
顺路带货)Keras用户也可以用ncnn啦!支持tf1或tf2导出的keras h5文件,如果遇到不能支持的layer,只要ncnn支持,开issue都会解决的qwq
https://github.com/MarsTechHAN/keras2ncnngithub.comCopyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。