深度学习框架大PK:TNN决战MNN,ncnn依旧经典

近年来,开发者社区中,「开源」成了新流行趋势。

尤其是深度学习框架,自腾讯2017年将ncnn开源之后,各大AI实验室都「慷慨」的将自己的框架开源,以实现较为快速的创新。

今年6月10日,腾讯又宣布基于ncnn设计的深度学习推理框架TNN也将开源。TNN开源后,许多开发者都将其与ncnn,MNN等开源框架作比较。

TNN性能略胜一筹,「剑指」移动端;MNN通用性表现更佳

TNN设计之初一方面是为了提高算力,另一方面强调了对移动设备的支持。不然用户就没必要放弃生态很好的MNN和大家都熟悉的ncnn来使用TNN了。

TNN是基于ncnn开发的,对其进行重构升级。除了算力显著的提升以外,基于TNN开发者还可以将深度学习算法移植到手机端,开发移动端的AI产品。

为了比较MNN,ncnn和TNN的性能,在以下的主流平台做了测试:

可以看到性能方面TNN还是有优势的。但是有些环境下的测试其实和MNN还是不相上下,阿里的开发团队也说,TNN的性能激励了MNN的提高,但相比当年2000ms到700ms的飞越还是差点意思。

TNN性能提升的关键是低精度计算,之前讲的没有办法移植到移动端的本质原因是手机处理器比较薄弱,因此框架适应中低端机是非常重要的。TNN在这方面做的很好,在中低端机上的性能提升约20%。

通用、轻便也是TNN框架的亮点之一,解决了一部分不同框架间的模型转换问题。但是TNN的模型转换需要基于ONNX,而不像MNN那样直接支持。操作上只是多了一步,通用性上又大打折扣了。

通用性上MNN的优势也不容置喙,MNN在通用性上下了很大的功夫,你看支持的算子数目就可以知道这算是MNN的一个工作重点。

算子实现上,MNN在计算后端的支持也是最广的。除了ARM v8.2,MNN在GPU算子的支持上也远超于其他两个框架。

再讲讲开源的一个重要问题:代码可读性。尽管CPU的代码框架上,TNN和ncnn非常像,但是风格和结构远超ncnn,更加的易读。这点MNN也做得很不错,MNN和TNN在代码各结构上还是有很多相似点。这些相似点让MNN到TNN做代码迁移变的没有那么复杂。

总的来说,ncnn作为2017年第一个开源的框架,非常经典。在TNN和MNN的性能差距不大的情况下,选择哪个框架就属于因人而异的事情,各有各的优势。

从ncnn开始,深度学习框架开源已成大趋势

虽然企鹅最近因为一些花边新闻老是被调侃,但是深度学习框架开源这方面,真的要感谢这个傻白甜鹅在2017年7月开源的ncnn。几乎开启了深度学习框架开源的「热潮」。

在业界各方人士三年不懈努力(内卷)下,VGG16 在 RK3399 上单核心的推理速度从 2000 ms+ 压榨到了 700 ms+。这之中也正是因为开源,才能够集众人智慧,得到如此卓越的性能提升。

ncnn作为元老级别的框架,本身就是为移动平台优化的高性能神经网络推理计算来开发的。从设计之初,ncnn就在手机上进行部署和使用深思熟虑。ncnn当前在许多腾讯应用程序中使用,包括我们熟悉的微信、QQ等。

MNN也是具有重大意义的一个开源框架。MNN是一个高效,轻量的深度学习框架。它支持深度模型推理与训练,尤其是在端侧的推理与训练性能在其中领先领先。目前,MNN已经在阿里巴巴的手机淘宝,手机天猫,优酷,钉钉,闲鱼等20个应用中使用。

MNN对于性能的提升有着卓越的贡献,因此也成为开发者的框架首选。

TNN作为2020年鹅厂开源的一款移动端推理框架,具有高性能、轻量级的特点,拥有跨平台、高性能、模型压缩、代码裁剪等众多优势。目前TNN已经在手机QQ、微视、P图等应用中落地。

除了这三个主流的框架,还有一些也经常被使用的框架,例如PPL。但是PPL现在还没有开源,可能商汤还是想「有所保留」的。其实ncnn开源的时候PPL就出来说过他们的性能更好,PPL其实也是被开发者们经常使用的框架之一了。

百度开源了一个叫做Padle-Lite的框架。Paddle-Mobile是一个开放源代码的深度学习框架,旨在简化在移动,嵌入式和IoT设备上的推理。它与PaddlePaddle和其他来源的预训练模型兼容。

开源框架甚至有雷总的身影,小米主体业务还是在硬件方面,开源框架确实很稀奇。MACE是一个深度学习推理框架,针对Android,iOS,Linux和Windows设备上的移动异构计算进行了优化。

 

 

最后,方便大家使用,放上开源框架大礼包:

ncnn:https://github.com/Tencent/ncnn

MNN:https://github.com/alibaba/MNN

TNN:https://github.com/Tencent/TNN

PL:https://github.com/PaddlePaddle/Paddle-Lite

MACE:https://github.com/XiaoMi/mace

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
对于输入大小不确定的图片,我们可以在分类网络中使用自适应池化层(adaptive pooling layer)来动态调整输入图片的大小,以满足分类网络的输入大小要求。具体实现方式如下: 1. 在网络定义中添加自适应池化层,例如: ```c++ tnn::Net net; net.AddLayer(tnn::CreateLayer("Input", "", input_dims)); net.AddLayer(tnn::CreateLayer("Conv", "Conv", conv_params)); net.AddLayer(tnn::CreateLayer("ReLU", "ReLU", relu_params)); net.AddLayer(tnn::CreateLayer("AdaptivePooling", "AdaptivePooling", adaptive_pooling_params)); net.AddLayer(tnn::CreateLayer("Flatten", "Flatten", flatten_params)); net.AddLayer(tnn::CreateLayer("InnerProduct", "InnerProduct", inner_product_params)); net.AddLayer(tnn::CreateLayer("Softmax", "Softmax", softmax_params)); ``` 其中,adaptive_pooling_params是自适应池化层的参数,可以通过以下代码设置: ```c++ tnn::LayerParam adaptive_pooling_params; adaptive_pooling_params.name = "AdaptivePooling"; adaptive_pooling_params.type = TNN_LAYER_ADAPTIVE_POOLING; tnn::AdaptivePoolingLayerParam* adaptive_pooling_param = new tnn::AdaptivePoolingLayerParam(); adaptive_pooling_param->pool_type = tnn::POOLING_TYPE_AVE; adaptive_pooling_param->output_shape.push_back(7); // 自适应池化层输出高度 adaptive_pooling_param->output_shape.push_back(7); // 自适应池化层输出宽度 adaptive_pooling_params.param = std::shared_ptr<tnn::LayerParam>(adaptive_pooling_param); ``` 2. 在图像预处理中进行裁剪,以保证输入图片的长宽比与自适应池化层输出的长宽比一致,例如: ```c++ cv::Mat input_image = cv::imread("input_image.jpg"); cv::Mat resized_image; cv::Size input_size(256, 256); // 先将图片缩放到较大的尺寸 cv::resize(input_image, resized_image, input_size); float input_scale = 1.0 / 255; // 图像数据缩放比例 float input_mean[] = {0.485, 0.456, 0.406}; // 图像数据均值 float input_std[] = {0.229, 0.224, 0.225}; // 图像数据标准差 int input_height = resized_image.rows; int input_width = resized_image.cols; float input_ratio = (float)input_width / (float)input_height; float output_ratio = (float)7 / (float)7; // 自适应池化层输出的长宽比为1:1 int crop_width, crop_height; if (input_ratio > output_ratio) { crop_width = input_height * output_ratio; crop_height = input_height; } else { crop_width = input_width; crop_height = input_width / output_ratio; } int x = (input_width - crop_width) / 2; int y = (input_height - crop_height) / 2; cv::Rect crop_region(x, y, crop_width, crop_height); cv::Mat cropped_image = resized_image(crop_region); // 将裁剪后的图片转换为TNNTensor格式 std::vector<int> input_dims = {1, 3, 7, 7}; // 输入维度为1x3x7x7 tnn::Mat input_mat(TNN_CPU, tnn::N8UC3, input_dims, cropped_image.data); input_mat.ScaleMat(0, input_scale); // 缩放图像数据 input_mat.MeanMat(0, input_mean); // 减去均值 input_mat.DivMat(0, input_std); // 除以标准差 std::shared_ptr<tnn::Mat> input_mat_ptr = std::make_shared<tnn::Mat>(input_mat); ``` 以上代码中,我们先将输入图片缩放到较大的尺寸,再根据自适应池化层的输出大小计算出需要裁剪的区域,并将图片裁剪成正确的大小。然后,我们将裁剪后的图片转换为TNNTensor格式,并进行图像预处理(缩放、减均值、除标准差),最后将TNNTensor作为输入传入分类网络即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值