算法、技术及其它

  在和刘同学长谈之后,我再次对前一段时间的想法进行了反思,结合聊天中的新感受,整理在这里。(注:标题里的算法,指机器学习算法,或者说“算法工程师”这个职位名称里的“算法”,不是“算法与数据结构”里的那个算法。谁能告诉我有没有什么更好的名字来区别这它们,或许是“机器学习算法”与“传统算法”?)

  算法与算法工程师先来一段我在知乎里回答“做算法工程师是一种怎样的体验?”的答案(其中的思想并非原创,而是山寨自新加坡某大学一门Quantitative Investment课程的ppt)

  理想中的算法工程师:提出假设->收集数据->训练模型->解释结果。

  实际中的算法工程师:提出假设->收集数据->预处理->预处理->训练模型->调试->调试->重新收集数据->预处理->收集更多数据->调试->调试->调试->…->放弃。

  这个答案被点了几十个赞,在24个答案中排在第二位,说明具有一定的普遍性。排名第一的有100+赞,而他的观点是:每天最重要的就是跑数据!这不是段子,而是事实。为什么“高大上”的算法工程师实际上是个数据民工,要寻找这种理想与现实的差距的原因,首先要理解一个事实:只有人能够理解数据,机器不能。不管我们用什么机器学习算法——无论是LR,SVM,k-means,EM——对于它们来说,输入数据都是一堆浮点数组成的矩阵而以(如果说的更本质一点,只是一堆01序列)。如果有一个特征是“小时”,而它出现了25,任何一个智商正常的人类都能明白,这是一个错误,然后在数据清洗的时候把这样的数据排除。但是机器就无法理解这一点。要具备小时的概念,又要理解什么是时间,一天有多少个小时…机器怎么能自动化完成这样的数据清洗工作?更进一步,如果人发现“小时”这个特征中大部分数据是0到12,而混入少量13(但13的数量又不是太少以至不能被当成离群点排除),人就会怀疑,是不是使用了12小时制而13是一个错误。机器目前是无法做到这一点的。

再说人肉特征。

  一个是特征变换,比如需要一个特征是某两列数据的比率,这种除法是线性模型不能涵盖的。当然可以增大模型的假设空间,但是太小涵盖不了需要的变换,太大又容易过拟合。另一个是加特征,比如我认为点击率和屏幕分辨率有关系。于是我去找屏幕分辨率数据加入特征,如果没有还要想办法采集。这些机器都做不了。但是,人一但把数据准备好,接下来就是机器学习算法发挥的时候了。但是,算法工程师的主要工作不在这里,这是因为软件有个特点,可以近乎无成本的复制。只要这个世界上有一个人实现了LR(知识产权的问题这里不考虑,更何况开源软件很多),其他需要用LR的人都可以拿过来用了。显然,这些算法工程师们也正是这么做的。然而,等算法输出结果以后,又需要人的工作了——怎样用结果解释实际问题,应用到业务中去。显然这个过程和前面数据清洗、人肉特征的性质类似,都是只有人能完成,机器做不到的任务。做过数学建模的同学对这个过程可能很熟悉——如何把一个问题描述成数学问题,再如何把结果应用到实际问题上。这有点类似于通信中的“最后一公里”问题,主干网的光纤建设的很强大,而最终用户的接入却成了一个麻烦事。对于机器学习的应用问题来说,算法和相应的软件包都是标准化、通用化的,像骨干网;而数据如何“接入”,则是只能由人完成。因为,只有人能够理解数据。技术与技术人员这个问题可以推广到整个计算机领域。把算法工程师代换成程序员,把机器学习算法代换成软件,这个观点就变成了:大部分程序员所解决的,是通用的计算机工具和具体的实际业务之间的“最后一公里”接入问题。为什么这么说,我们先来看历史:计算机技术发展了几十年,程序员的入门门槛是逐步降低的。最初的程序,要在裸机上写汇编。后来有了unix,c语言,程序员至少不用亲自调度进程了。java出现之后,连内存都不用管了。而(世界上最伟大的)php出现之后,网络编程的门槛进一步降低,任何人都可以在短时间内搭建一个网站。原来的那些问题去哪儿了呢?被少数造“”轮子的程序员们解决了——那些写操作系统、编译器、虚拟机、运行时环境、框架…等等的程序员们。这个趋势一直在持续——新兴的rust、golang等语言试图解决多核时代出现的并发问题,hadoop、spark、mesos试图屏蔽分布式系统底层的细节……可以预见,以后的并行编程和分布式编程门槛将会大大降低。这个过程是必然的,因为一项技术的发展,就是为了让更多的人能更方便的使用它。而这些计算机工具不能直接应用于业务,因为计算机不能理解人类的语言,所以就有了大量的程度员存在,把人类的语言“翻译”成计算机语言。这些程序员是使用“轮子”的。

  当然这之间并不是非黑即白的,一个软件在多大程度上可以被称为轮子,取决于它的复用性。如果一段代码只能在一个地方使用,它显然不能称为轮子。而事实是,大部分为具体的业务逻辑所写的代码,复用程度很低。对于把通用计算机工具应用到具体业务这个过程,中间到底有多少问题是技术性的?大部分技术困难被操作系统、编译器、虚拟机解决掉了,剩下的主要是大型软件(如果这是个大型软件)的复杂性控制——而这个问题又主要由少数高级别的架构师负责。对于写具体代码的程序员,剩下的技术性困难已经很少了。举一个我供职过的公司,这是一家互联网公司,整个网站99%的代码是php,基本上没有java。没有专门的前端工程师,php、html和javascript代码混在一起。测试几乎等于没有,基本都是开发人员自测。上线流程只是个形式,质量控制部门唯二的作用就是向服务器上同步代码和出现事故之后给开发人员定责任。我曾经和另一个部门合作,他调用我提供的接口,而他在我的接口没上线的时候就上线了,导致一场事故。我本来是算法工程师,写php只是客串,而在这个过程中,没有任何上线依赖的控制,连提示也没有,甚至没有人对我进行上线流程的培训。然而,这是一家中等规模的互联网公司,己经发展了十来年,占据了所在细分市场领域的头号份额,并且己经上市。我举这个例子并不是要黑它,而是想用事实支持上面的观点:大部分程序员,大部分所谓的“科技”公司,所面临的技术问题比想象的要少的多(这也许是那家公司没有CTO的原因)。这不是个别情况,大多数公司都存在类似的问题——从技术角度看,它就是个渣,你会很奇怪它怎么还没死。然而事实是,它不但没死,反而活的生机勃勃,甚至上市了。公司的拥有者们早已实现了屌丝逆袭迎娶白富美的理想,而辱骂他们的程序员们还在苦苦的为房贷或者首付挣扎。这里面,有大量的非技术因素起着关键作用,尽管它们都自称科技公司。越来越多的人意识到了技术的局限性。年初,一个同学找工作,他向来是个“纯技术流”的工程师,曾经写过很好的技术博客,甚至发起过开源项目。然而这次他说,“不想再做最底层的工程师了”,希望能做一些“高层的、能看到项目整体的”、以及“和人打交道,能够把自己的想法向外推动,并产生价值”的工作。于是,他去某公司负责带几个小弟去了。当我把这些转述给另一个同学的时候,他的反应是“我最近也有这样的想法”。还有个同学,说写了几年C++,技术上没学到多少,反而是接触的业务知识比较多。再比如我之前的leader,他是数学博士出身,曾经对算法有一种近乎天真的信仰;在我离职的时候,他已经完全转型为业务和产品导向了。而某个几年前就开始淡化技术,聚会时大讲“软实力”的同学,早已在BAT做了Team Leader,生活滋润,终日以跑步为乐。为何?其实原因很简单:公司里没有那么多技术问题需要解决。

  《代码大全》里有个比喻,如果你的问题是给自己的爱犬造一个小窝,那么动手做就是了,如果出了什么错误,大不了重做一个,最多浪费一下午的时间。而造一个摩天大楼就不同了。所以,如果写一份“狗窝”级别的程序,算法、数据结构、设计模式这些又有多大意义呢?甚至违反了DRY原则也没关系,反正一段代码也拷贝不了几次,出了bug就改,大不了重写一次,最多浪费一下午。而且,说不定这个项目两周之后就被砍掉了。如果你做的是一份“造狗窝”的工作,就算你有造摩天大楼的技术,和屠龙之技又有什么分别呢?唯一的“好处”就是你会据此向老板提出更多的加薪要求,以至于老板对你“另眼相看”。程序员应当破除对技术不切实际的幻想——这不是说技术不重要,而是说要实事求是的分清,哪些是造狗窝的工作,哪些是建普通楼房的工作,哪些是造摩天大楼的工作。

再谈算法

  同理,算法工程师应当破除对算法不切实际的幻想,把注意力集中到数据的理解、清洗、预处理、人肉特征、业务应用(而这些往往和屌丝、苦逼等形容词联系在一起)上来。未来,机器学习工具将更加标准化、平台化、通用化,并且进一步降低使用门槛。与算法本质无关的工程细节,比如数据存储方式、梯度下降过程、并行化、分布式计算等,将被制造“轮子”的程序员们屏蔽。算法工程师可能只需用类似Hive的方式,写几个类似SQL的语句就可以完成模型的训练、交叉验证、参数优化等工作。而机器唯一不能替代的就是对数据的理解,这是算法工程师存在的价值。而数据是和业务强相关的,算法工程师将更加接近产品经理的角色,而不是程序员。深入理解数据、业务和产品,寻找模型和它们的结合点,将成为算法工程师的核心竞争力。插一句,相对于本文的观点,Deep Learning在某种程度上是一种的例外。它试图解决特征工程的问题,也就是在某种程度上代替人提取特征。当然,它还比较初级,另外它最多只能解决特征变换问题,仍然处理不了数据清洗和预处理中需要用到领域知识的情况。这里刘同学提出一个问题,那就是算法工程师对算法需要理解到何种程度?事实是,即使从算法的应用出发,工程师也需要掌握模型的优缺点、适用场景、模型选择、参数调优等技术。这是毫无疑问的,从这一点上说,算法工程师需要一定的技术能力,这点又和产品经理不同。但是这就有另外一个问题:模型选择和参数调优技术,是否是通用的?还是和具体的数据高度相关的?比如,是否存在这样的现象,同样的调优技术,在(比如说)电商数据上表现很好,到了社交数据上就不行了?这个问题我暂时没有答案,如果谁知道,请告诉我。不过,一个现象是,目前做机器学习模型相关的项目,在改进的时候,基本上都采用试错的方式,就是先做出改动,然后上线观察效果;如果不好,就换种方法;如果效果有所改进,也往往没有人知道为什么。如果存在一种通用的判断模型优劣的技术,我们为什么还要采取这种近乎穷举的方式呢?从“IT精英”到“IT民工”或者“码农”,这种称呼上的转变并非笑谈,而是真实的反应了计算机编程领域门槛逐步降低的过程。所以,我们应当给听上去高大上的“算法工程师”或者“数据科学家”起一个类似的外号,比如“数据民工”、“机农”或者“蒜农”之类,以免不明真相的孩子们被“高大上”的称号吸引而误入歧途。

  其它看的出我是一个比较纯粹的技术人员,因为对于非技术的东西,我了解不够,说不出那是什么,只能用“其它”一词概括。这“其它”,基本上是“人”的问题——比如前面提到的“如何推动自己的想法”,“软实力”之类,大的包括机遇,小到“发邮件应该抄送给谁”这种细节。当然,如果你是个对技术本身感兴趣的人,这些讨论不适用,因为对于这类人,技术本身就是目的,不是手段。这里的视角,仅仅是社会普遍意义上的职业发展角度。无论是想在公司内部获得升迁,还是通过跳槽而得到晋升,还是自行创业而实现人生目标,技术都只是你的一种技能。如果再想想大部分公司里提供的是一份“造狗窝”级别的职位,这种技能起的作用又有多大呢?不过多说一句,要求程序员“对技术感兴趣”,甚至“在业余时间以写代码为消遣”,是一种相当荒谬的事情。试想,招聘销售人员的时候,从未有人要求求职者“对喝酒应酬感兴趣”;招聘财务人员的时候,也没有人要求“对加减数字感兴趣”;招聘外科医生的时候,也绝不会要求“平时以解剖人体为消遣”。为什么程序员这种职业就要搞特殊?究其原因,大概是大家还沉浸在对技术的一种非理性崇拜之中(当然崇拜和亵渎往往并存)——“技术改变世界”这句话常常被提到。这句话没错,但是要搞清楚,“技术改变世界”不等于“每一项技术都能改变世界”,更不等于“每一个技术人员都能改变世界”。其实,程序员这一行和其它任何一个需要专业技能的行业没什么区别,只是一种谋生的手段而已。大部分所谓的“科技公司”也并不是真正的科技公司,顶多是“使用科技的公司”。

  其实,在金融领域,对IT的要求要高多了,各大银行也有自己的软件开发部门,但是没人把它们归到IT行业,而是属于金融行业。然而,那些开商店的,开饭店的,卖房子的,给人说媒的,集资的,他们似乎只要做个网站,就成了“科技公司”了,这难道不是很荒谬吗?(当然,像亚马逊这种从一个卖书的起家,居然后来搞起了云计算、推荐系统、无人飞行器等技术创新的,不在此列。)在这些公司当中,技术到底起多大作用呢?也许相当一部分程序员们会自以为技术很重要,他们沉浸在对技术的憧憬和信仰中,内心深处坚定的相信自己可以通过技术能力的提升,来谋取更高的职位,走向人生巅峰。然而,大多数时候这只是一种自欺欺人的幻想罢了。天朝的程序员们有一种矛盾心态,一方面自称“民工”,认为编程是一种只适合30岁之前的年轻人从事的体力劳动,而另一方面却又把技术看的非常重要,甚至在业余时间也喜欢大谈技术,或者以攻击其他程序员使用的技术为乐。如此抱着技术不放,并不是因为多么热爱技术,而是因为他们只会技术。没有人愿意在别人面前展示自己的劣势。把技术的地位抬的越高,仿佛自己就显得越重要,而那些在需要人际交往、推动自己的想法、和产品经理讨论需求的时候所表现出的能力低下,似乎就不重要了。这是人性的弱点——对自己某种能力盲目而过分的自信,甚至把它作为自己的精神支柱。也许他在这个方面的确很擅长,但是自我评估却比实际更高。诚然,自信是必要的,也是人生存和立足的精神基础之一。然而自信是把双刃剑——不切实际的自信(也许应该叫自大了),会蒙蔽人的双眼,扭曲事实。那么我们应该怎么做呢?首先比较悲观的一点是,如果你从事着一份技术上处于“造狗窝”级别的工作,那么很遗憾,提高自己的技术水平恐怕对于在公司内部的职业发展没什么帮助。

  如果你是一个真正对技术有兴趣的人,可以考虑一下《黑客与画家》里提到的一类“真正的程序员”的工作方式:他们求的一份“白天的工作”,这份工作仅仅用来生存,而在业余时间写一些“真正有价值”的代码。恐怕大部分程序员都不是对技术有兴趣的吧?如果你的目标是事业上的发展,无非是跟人混和创业两种方式。跟人混,要么内部晋升,要么通过跳槽。前者需要老板认为你牛逼;后者需要别的公司的老板认为你牛逼。注意,这里有个关键词“认为”,因为人的主观印象和客观事实之间总是有差距的,而且这个差距往往超乎人的想象。所以重点是制造一种“牛逼”的印象,而实际上牛不牛逼并不重要,牛逼更好,不牛逼也可。如果你想走技术路线,可以考虑去找一份“建造大楼”级别的工作,在那里,技术成为决定产品成败的主要因素。这种级别的项目,一般只有大公司做的起。老板对于员工技术能力的评估,还是比较容易做到客观的,因为代码在那里,牛不牛逼,一运行便知。但是对于所谓“软实力”,往往就不好评判了,主观性很大。也正因如此,往往有很多人觉得自己很牛逼,而老板不这么认为(错的不一定是老板,也可能是这个人自大),所以一怒之下走上创业的道路。自己给自己当老板,终于不用在意老板的印象和事实之间的差距。然而这条路往往更为艰难,它对人的综合素质要求比较高。如果一个程序员在工作中不能和同事顺利的合作,那很难想象他能够满足创业者所需要的各种素质。所以,要走这条路,得有心理准备。

总结

  技术是为人服务的,IT业的发展过程,是在逐步降低计算机的使用门槛,使得越来越多的人能够使用这种工具。这是好的,但它同时也降低了程序员这种职业的技术含量。如果真的想做技术,那么去做一些真正的技术。否则,就需要多多关注技术以外的东西,单纯寄希望于技术,只能用来安慰自己,而不能获得真正的职业发展。

原文地址:http://www.cnblogs.com/aquastone/p/algorithm-technology-and-others.html

原创作品,转载时请务必以超链接形式标明文章原始出处: http://www.dapalm.com/?p=143,作者:DaPalm-大数据,怕了么?
 最近一直在研究模型量化。这块可以用到的论文和实现太少了,真是折腾人,没办法估计最后还是要用TensorRT来实现了。总结一下工作。

量化相关论文

我的目的是让网络能够用int8整形数据来进行并行卷积运算,所以根据paper所要达到的主要目的来进行分类:1.提高前向运算性能,2.压缩模型用于移动端。
- 压缩weight值的范围
1. Alvarez R, Prabhavalkar R, Bakhtin A. On the efficient representation and execution of deep acoustic models[J]. arXiv preprint arXiv:1607.04683, 2016.(声学深度模型的有效表示和执行)
 在本文中,我们提出了一个简单而计算效率高的量化方案,使我们能够将神经网络参数的分辨率从32位浮点值降低到8位整数值。 所提出的量化方案导致显着的存储器节省,并且使得能够使用优化的硬件指令来进行整数运算,从而显着降低了推理的成本。 最后,我们提出了一个“量化意识”训练过程,在网络训练期间应用所提出的方案,并发现它使得我们能够恢复由量化引入的大部分精度损失。

 主要思想是以量化int8数据进行前向卷积,然后输出时恢复成float,再进行方向传播更新全精度的weights值,相当于让weights值拟合量化int8的数据。它的量化方法比较简单
- 不压缩weight值的范围

原创作品,转载时请务必以超链接形式标明文章原始出处:http://www.dapalm.com/?p=116,作者:DaPalm-大数据,怕了么?

一、TensorRT基本概念

  TensorRT(GIE)是一个C++库,适用于Jetson TX1和Pascal架构显卡,支持fp16特性,也就是半精度运算。由于采取“精度换速度”的策略,在精度无明显下降的前提下,其对inference的加速明显,往往有1倍以上的性能提升。

二、TensorRT编程流程

使用TensorRT主要有连个步骤:

 - 构建阶段:构建一个网络定义(network),执行优化,并生产推理引擎(inference engine)

 - 执行阶段:引擎运行推理任务(只需要输入/输出buffers),主要是通过 IExecutionContext::execute和 IExecutionContext::enqueue同步或异步执行推理

目前支持的层(更新到TensorRT 2.1将支持自定义层,在下篇博客中介绍):
 - Convolution: 2D
 - Activation: ReLu, tanh and sigmoid
 - Pooling: max and average
 - ElementWise: sum, product or max of two tensors
 - LRN: cross-channel only
 - Fully-connected: with or without bias
 - SoftMax: cross-channel only
 - Deconvolution
1.主要两个头文件、两个库:
 1.1.头文件目录:/usr/include/x86_64-linux-gnu/
         NvCaffeParser.h NvInfer.h
 1.2.库目录:/usr/lib/x86_64-linux-gnu
       libnvcaffe_parser.so libnvinfer.so
  demo目录:/usr/src/gie_samples
  doc目录:/usr/share/doc/gie
2.软件主要流程(代码只列出主要部分):
由于会用到cuda需要包含cuda_runtime_api.h
以sampleMNIST为例:
首先从main()开始:
caffe model转gie model,创建序列化engine;
读入需要处理的图像;
用caffe parser解析均值文件(.binaryproto),与需要处理图像相减;
反序列化gieModelStream到engine,创建引擎执行的环境(context )
执行推理doInference(*context, data, prob, 1);//参数:环境,输入,输出,batch大小

关键步骤1:Caffe model转成GIE model以便提供给cuda engine进行inference:

void caffeToGIEModel(const std::string& deployFile, const std::string& modelFile, const std::vector& output, unsigned int maxBatchSize, std::ostream& gieModelStream)
//创建builder
IBuilder* builder = createInferBuilder(gLogger);
//解析caffe model到network,然后输出
INetworkDefinition* network = builder->createNetwork();
ICaffeParser* parser = createCaffeParser();
const IBlobNameToTensor* blobNameToTensor = parser->parse(deployFile,modelFile, *network,DataType::kFLOAT);
//指定哪个tensors是输出
for(auto& s : outputs)   //outputs是传入的string引用。包含需要输出的blob名字
network->markOutput(*blobNameToTensor->find(s.c_str()));
//创建engine(需要给builder传入最大batchsize和workspacesize),最后把network传入builder
ICudaEngine* engine = builder->buildCudaEngine(*network)
//最后销毁不需要的network,paser
*->destory() ;
//序列化engine,最后销毁engine、builder
engine->serialize(gieModelStream);

关键步骤2:对得到的Model的流反序列化到Cuda引擎,并创建用于执行推理的上下文环境context

gieModelStream.seekg(0, gieModelStream.beg);
IRuntime* runtime = createInferRuntime(gLogger);
ICudaEngine* engine = runtime->deserializeCudaEngine(gieModelStream);
IExecutionContext* context = engine->createExecutionContext();

关键步骤3:推理void doInference(IExecutionContext& context, float* input, float* output, int batchSize)

//从上下文环境获取引擎
const ICudaEngine& engine = context.getEngine();
//获得绑定索引数量,只有输出/输出,这值返回2
assert(engine.getNbBindings() == 2)
//通过输入输出tensors返回输入输出索引
int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME),
outputIndex = engine.getBindingIdex(OUTPUT_BLOB_NAME);
//创建GPU buffers和stream。过程与malloc类似
CHECK(cudaMalloc(&buffer[inputIndex], batchSize * INPUT_H * INPUT_W * sizeof(float)));
CHECK(cudaMalloc(&buffer[inputIndex], batchSize * OUTPUT_H * OUTPUT_W * sizeof(float)));
//创建流进程
cudaStream_t stream;
CHECK(cudaStreamCreate(&stream));
//***DMA方式将input传入GPU,异步执行batch数据的运算,最后DMA回传output到CPU***
CHECK(cudaMemcpyAsync(buffers[inputIndex]), input, batchSize*INPUT_H*INPUT_W*sizeof(float),cudaMemcpyHostToDevice, stream);
context.enqueue(batchSize, buffers, stream, nullptr);
CHECK(cudaMemcpyAsync(output, buffers[outputIndex], batchSize*OUTPUT_SIZE*sizeof(float), cudaMemcpyDeviceToHost, stream));
cudaStreamSynchronize(stream);//同步
//最后释放stream和buffers
cudaStreamDestroy(stream);
CHECK(cudaFree(buffers[inputIndex]));
CHECK(cudaFree(buffers[outputIndex]));

Weighted-Entropy-based Quantization for Deep Neural Networks
原创作品,转载时请务必以超链接形式标明文章原始出处:http://www.dapalm.com/?p=88,作者:DaPalm-大数据,怕了么

 

论文: Weighted-Entropy-based Quantization for Deep Neural Networks  (CVPR2017)

链接:http://openaccess.thecvf.com/content_cvpr_2017/papers/Park_Weighted-Entropy-Based_Quantization_for_CVPR_2017_paper.pdf

代码地址:https://github.com/EunhyeokPark/script_for_WQ

1.绪论部分:

量化是优化神经网络模型前向计算耗时的最有效的方法之一,以便它们部署到资源受限的移动或嵌入式系统中。在这类方法中,最重要是提供低精度损失量化。在这篇论文中,作者提出了一种基于加权熵概念的量化权值和激活值的方法。它不像最近的二值化神经网络,作者提出的方法是根据目标精度来选择量化的比特位数。这种方法更加方便的去权衡精度与性能,以便更合理的选择量化级别。虽然,作者提供了这种自动量化策略,但是对于传统训练算法来说也是很轻易使用的。作者进行大量实验,如分类(AlexNet,GoogleNet,ResNet-50/101),检测(R-FCN with ResNet-50)和语言模型(LSTM网络),不用多说,肯定是有效果的。

2.相关工作:

1.无法权衡精度和性能

2.即使有,也是部分有效,需要大量修改网络,而且不量化第一层和最后一层

提到了一些方法:LogQuan、XNOR-Net、DoReFa-Net,有兴趣可以找出来看看。

3.动机和灵感:

首先对权值和激活值的概率分布进行了分析,呈现钟型分布(bell-shaped distribution),由三部分组成:1.接近0的值占主要部分,但对结果影响不大,分配相对较少的量化级;2.两端大的值相对较少,对结果影响大,但是为了充分利用量化级只分配少量的量化级给这部分;3.既不大又不小的值相对较多,对结果影响相对较大,所以分配更多的量化级。如图右下角就是WQ量化分布图。

4.量化与权值熵:

4.1权值量化

权值量化的高层次思想是将权值分成N个聚类,从而有更多聚类代表重要权值,为每个聚类分配具有代表性的值,然后量化所有权值到某个对应聚类的值。这里最主要的是Weight Entropy,下面公式中的S,这里分成N个聚类C0,...,CN-1。Pn(概率)表示有多少比例的权值在Cn这个聚类范围内;In(重要性)表示Cn这个聚类里面所有权值的平均重要性。i(n,m)表示第n个聚类中的第m个权值的重要性,其与那个权值的二次方成比例。因为值大的权重分布比较稀疏,值小的权重的分布比较密集,所以值较大的Cn会有较大的In和较小的Pn。简单说就是高重要性低概率;高频率低重要性。

(权值量化)提供训练数据(mini-batch input)和期望的logN-bit精度(聚类个数),找聚类N中的最大权值熵。聚类代表的值对应权值量化的level。

权值熵理论限制,不能同时处理正负值,因此分成两个组负值和非负值,分别应应用算法到每组(N/2 levels)。

重要算法1

1.输入(聚类数量N,权值w)

2-3.计算每个权值的重要性

4.重要性排序s=[]

5.初始化聚类边界C0,...CN(假如s=[1,2,3,4],N=2,那么就可以得到初始化的分割边界:c0=0,c1=2,c2=4,也就是将s分成了C0={1,2}和C1={3,4}两份)

6-11.找最大权值熵S的过程中更新聚类边界。第7行遍历所有聚类1~N-1,第8行ck在ck-1和ck+1这个范围遍历的时候,找到最大S,并更新边界对应的聚类ck,即新边界聚类

12-15.遍历每个聚类,第13行计算每个聚类的重要性Ik,第14行根据Ik计算出这个聚类代表的值rk(Figure1的纵轴),第15行根据边界重要性s[ck]计算出权值的边界(Figure1的横轴)

16-17.返回r0-rN-1和b0-bN,分别表示每个聚类的值和边界

4.2激活值量化

 

 

 

Weighted-Entropy-based Quantization for Deep Neural Networks

原创作品,转载时请务必以超链接形式标明文章原始出处:http://www.dapalm.com/?p=69,作者:DaPalm-大数据,怕了么?

 

论文:Receptive Field Block Net for Accurate and Fast Object Detection  (CVPR2017)

链接:https://arxiv.org/abs/1711.07767

代码地址:https://github.com/ruinmessi/RFBNet

1 绪论部分:

当前顶级目标检测器依赖于非常深的CNN主干网络,例如ResNet-101和Inception,优点是它们具有强大的特征表现能力,但是耗时严重。相反地,一些基于轻量级模型的检测器满足实时处理,但是精度是诟病。

在RFBNet这篇论文中,主要想利用一些技巧使用轻量级模型达到速度和精度并举的检测器。灵感来自人类视觉的感受野结构Receptive Fields (RFs) ,提出了新奇的RF block(RFB)模块,来验证感受野尺寸和方向性的对提高有鉴别鲁棒特征的关系。RFBNet是以主干网络(backbone)为VGG16的SSD来构建的。下面是讨论其有效性,两项基准测试实验和结果显示。RFBNet具备同非常深的主干网络检测器的精度,但是保持了实时性。

2 相关工作:

这部分就不介绍了,主要是对目前一步法和两步法的一些利弊总结。有个比喻挺形象:YOLO定位精度差,小目标检出率低;SSD是YOLO的多尺度版本,对小目标检出有改善。

3.2 感受野模块:

RFB是一个类似Inception模块的多分支卷积模块,它的内部结构可分为两个组件:多分支卷积层以及随后的膨胀卷积层。如下图

 

3.2.1.多分支卷积层:
根据RF的定义,用多种尺寸的卷积核来实现比固定尺寸更好。具体设计:1.瓶颈结构,1x1-s2卷积减少通道特征,然后加上一个nxn卷积。2.替换5x5卷积为两个3x3卷积去减少参数,然后是更深的非线性层。有些例子,使用1xn和nx1代替nxn卷积;shortcut直连设计来自于ResNet和Inception ResNet V2。3.为了输出,卷积经常有stride=2或者是减少通道,所以直连层用一个不带非线性激活的1x1卷积层。

3.2.2.膨胀卷积层:

最初来自Deeplab,在保持参数量和同样感受野的情况下,用来获取更高分辨率的特征。图4,5展示两种RFB结构,每个分支都是一个正常卷积后面加一个膨胀卷积,主要是尺寸和膨胀因子不同。没有使用膨胀池化,主要是在组织不同RFs尺寸的时候,不够灵活。对于RFB,卷积核尺寸kernel size和膨胀因子dilate对检测器有细微影响,下面会讲。

 

3.3 RFBNet检测结构

RFBNet检测器利用多尺度一步检测框架SSD,在其中嵌入RFB模块,使得轻量级主干SSD网络也能更快更准。RFB结构可以很容易集成到CNNs中,所以RFBNet检测网络保留了大部分SSD结构。主要修改是替换顶层卷积层为RFB,这些改动小但有用。如下图。

3.3.1.轻量级主干

为了便于比较直接使用SSD原始的主干网络,也就是VGG16。它是将fc6和fc7层转换成卷积层用于下采样参数,并且将pool5从2x2-s2改成3x3-s1.膨胀卷积用来填充空缺和所有dropout层,并移除fc8层。

3.3.2.多尺度特征图RFB

原始SSD,基础主干网络后面接着一系列重叠的卷积层,得到一系列空间分辨率减小而感受野增大的特征图。RFBNet保持了这种结构,也做了些改进,用RFB-b模块替换了原来L2归一化,达到从前面卷积层获取包含小目标的底层特征图。早期版本的RFB,使用的是膨胀max pool来模拟感受野方向性的影响。由于特征图太小,最后的卷积层只保留了部分,像5x5卷积核过大,无法使用而去掉。

3.3.3微调RFB参数

主要就是调节RFB模块中卷积核kernel size以及膨胀因子dilate。RFB-a是调整后的结构,然后从conv4_3层连出。再在后面接一些小的滤波器,对性能有一定提升。

 

4.实验

通过实验结果,和自己试验,超过了目前所有检测方法。显存和时间都有保证。