当前位置: 首页 > 科技观察

为什么加班的总是我:那个每天12点上班的程序员,又准时下班陪女朋友了?!

时间:2023-03-16 21:58:54 科技观察

作为我们头发储备排名前三的程序员,我总是靠着头发努力工作,在加班的第一线折磨自己。年轻人,你为什么不加班?虽然我没有女朋友,但我有代码但是我不明白的是我旁边的那个比我上班晚,比我下班早,每天准时接女朋友,看起来就像他很好地完成了他的工作一样。当然,他的头发也很不错。除了长得比我大,他还有什么法宝吗?趁着午休时间,以一周咖啡为代价,偷走了他的法宝。得到秘诀,说不定我也能拥有一份美好的事业和爱情。直接集成NCNN的缺点。直接融合NCNN,老少皆宜。我想我是泪流满面,想用我女朋友的SK2来化妆我的脸(不,你不用,SK2和女朋友都用),为什么呢?事情是,为SqueezeNet连接NCNN,把相关的模型文件,NCNN的头文件和库,JNI调用,预处理和后处理相关的业务逻辑等等。把这些内容放到SqueezeNetSample工程中。这种简单直接的集成方法存在明显的问题。与业务耦合较多,不具有通用性。前处理和后处理都与SqueezeNcnnSample相关,不能方便的提供给其他业务组件。深思一下,如果我们把AI业务作为单独的AI组件提供给商科学生,会出现这样的情况:每个组件都必须依赖并包含NCNN库,而每个组件的开发,都必须熟悉接口NCNN的,写C调用代码,写JNI。所以我们很自然地想到提取一个NCNN组件。拔完之后,看起来顺眼多了,大概是这样的吧。AOESDK中的NCNN组件有了AOESDK,我也能操作如虎!在AOE开源SDK中,我们提供了NCNN组件。下面从四个方面来谈谈NCNN组件:NCNN组件设计如何将NCNN组件连接到SqueezeNetSample的改造应用NCNN组件的一些思考NCNN组件设计如果你不了解NCNN组件设计,即使你操作起来像老虎,你最后可能只有两块钱和五块钱。那么它的组成部分是什么?NCNN组件的设计理念是组件不包含具体的业务逻辑,只包含对NCNN接口的封装和调用。具体业务逻辑由业务方对外实现。在接口定义和设计方面,我们参考了TFLite的源码和接口设计。目前提供的对外调用接口是这样的://loadmodelandparamvoidloadModelAndParam(...)//是否初始化成功booleanisLoadModelSuccess()//输入rgba数据voidinputRgba(...)//推导voidrun(...)//多输入多输出推理voidrunForMultipleInputsOutputs(...)//得到推理结果TensorgetOutputTensor(...)//关闭并清理内存voidclose()而机智骚年自己用的是这个:├──AndroidManifest.xml├──cpp│└──ncnn│├──c_api_internal.h│├──include│├──interpreter.cpp│├──Interpreter.h│├──jni_util.cpp│├──jni_utils.h...─runtime│└──ncnn│├──Interpreter.java│├──NativeInterpreterWrapper.java│└──Tensor.java└──jniLibs├──arm64-v8a│└──libncnn.a└───armeabi-v7a└──libncnn.a解释器,提供给外部调用,提供模型加载,推断这些方法。NativeInterpreterWrapper是一个具体的实现类,它调用了native。Tensor,主要是一些数据和native层的交互。AOENCNN用的好,任务完成的早,秘诀就在这里。支持多输入多输出。使用ByteBuffer提高效率。使用Object作为输入和输出(实际上支持ByteBuffer和多维数组)。不练假招就随便说说,AOENCNN的实现过程,我来给大家详细说说。★如何支持多输入多输出为了支持多输入多输出,我们在Native层创建一个Tensor对象列表,每个Tensor对象存储相关的输入输出数据。native层的Tensor对象通过tensor_jni提供给java层,java层维护着指向native层tensor的“指针”地址。这样当有多个输入和多个输出时,只要在这个列表中获取对应的Tensor,就可以进行数据操作了。★ByteBuffer使用ByteBuffer,字节缓冲区处理分段,比传统数组效率更高。DirectByteBuffer使用堆外内存,省去了向内核拷贝数据的过程,因此比ByteBuffer效率更高。当然,ByteBuffer的使用不是我们要讲的重点。下面说说使用ByteBuffer之后给我们带来的好处:1.接口里面的字节操作更加方便,比如里面的putInt,getInt,putFloat,getFloat,flip等一系列接口,可以很方便的对数据进行操作。2、与native层交互,使用DirectByteBuffer提高效率。我们可以简单理解为java层和native层可以直接对一块“共享”内存进行操作,减少了中间的字节拷贝过程。★如何使用Object作为输入输出目前只支持ByteBuffer和MultiDimensionalArray。在实际操作过程中,如果是ByteBuffer,我们会判断它是否是直接buffer来进行不同的读写操作。如果是MultiDimensionalArray,我们会根据不同的数据类型(如int、float等)、维度等来读写数据。Sample只包含模型文件,前处理和后处理相关的业务逻辑,前处理和后处理可以使用java或c由具体业务实现决定。新的代码结构变得非常简洁,目录如下:├──AndroidManifest.xml├──assets│└──squeeze│├──model.config│├──squeezenet_v1.1.bin│├──squeezenet_v1.1.id.h│├──squeezenet_v1.1.param.bin│└──synset_words.txt└──java└──com└──didi└──aoe└──features│├──squeezenet_v1。1.id.h│├──squeezenet_v1.1.param.bin│└──synset_words.txt└──java└──com└──didi└──aoe└──features└──squeeze└──SqueezeInterpreter。java↑此Sample同样适用于其他AI业务组件调用NCNN组件。(牛逼就完了)★应用程序如何访问NCNN组件NCNN组件访问有两种方式●直接访问●通过AOESDK访问▲两种访问方式对比:没有BATTLE,我单方面公布AOESDK赢了!★对NCNN组件的总结与思考通过对NCNN组件的封装,现在与NCNN的业务集成更加快捷方便。在我们新的业务整合NCNN之前,可能需要半天到一天的时间。使用AOENCNN组件后,可能只需要1-2小时。当然,NCNN组件还有很多不完善的地方。我们还需要加深对NCNN的学习和理解。之后通过不断的学习,NCNN的组件也会不断的进行改造和优化。