前言
受到疫情的影响,小区规定外地归宁的需要在家隔离14天,现在终于可以好好写这篇博客了。其实很早就想写了,但是这4年发生了很多事情,要对这4年做个总结不知道从何说起,一直都没有什么头绪,所以过年在家期间也没有动手写,在家看了两本书:《异类》和《阿里工程师的自我修养》(只有电子版)。
其中《异类》是我在大概3,4年前看的,但是没看完,中间断断续续看了一些,但是直到这次才全部看完。多年前我一直以为这本书就是一本心灵鸡汤,但是读着读着发现并不是这样的,这本书通过大量案例揭示了成功背后的奥秘,可能很多人看完之后有种宿命的感慨,其实作者发现的这些成功背后的启示意义在于,对于我们普通人来说能够更好的理解成功背后的奥秘,能够更加客观全面的看待成功,而不是一味地把成功归纳为某一种原因(比如高智商,富二代等),其次,对于普通人来说,了解了成功背后的奥秘之后,可以充分利用这些奥秘,甚至在某些情况下,人为创造一些机遇,比如改变小孩出生的时间,充分利用时代机遇,充分利用家庭资源等。《阿里工程师的自我修养》这本书给我的启发要更大一点,让我从更高的角度去看待技术和人生,也学到了一些新的思维习惯,比如结构化思维。看完这两本书之后,突然给我写这篇博客带来了一些灵感,下面的几天里整理了一下思路,才有了今天的这篇博客。
我在小视的4年
2016.02.26是我在小视工作的第一天,由于当时还没有毕业,所以只是一名实习生。后面每年的02.26都会收到一条祝福短信,小视其实还是非常人性化的一家公司,没有太多尔虞我诈,勾心斗角,各种福利都非常不错。
记得2016.02.25接到通知让我明天去二面,那天一早我就去了,二面是人事面试,当时是涓姐面试的,面完之后居然通知我就开始工作了!感觉好突然。当时公司还很小,跟其他公司合租的一个地方。
第一天入职的场景仿佛就在眼前,走道旁的绿色墙面上挂满了相册和装饰物,整个楼层中间有两个类似于鸟笼的休息区供大家休息或者办公,记得冉总和东哥就坐我斜对面,涓姐的工位离我稍远一点,那一个个鲜活的人还坐在位置上,物是人非,这些人有的还在公司,有的早已不知去向。
经过了4年的发展,小视逐渐发展到了200~300人左右的规模,不能说发展的很好,但也不算差了,去年一年整个CV领域其实都不是特别好,就连头部的大公司都比较困难,但是我依然坚定的相信AI的未来。在小视的4年其实成长非常快,学到了很多东西,打算从技术和管理两个方面来说。
技术
刚来小视的时候,我连caffe是什么都不知道,公司之所以要我,我想大概是看重了我的图像基础和工程能力了,研究生期间是学low level图像的(从我以前的博客就可以看出),之前在海康也实习过几个月,参与过真实的软件项目开发,还写过一篇博客总结了自己的实习经历:海康实习小结,所以来公司做的第一个项目就是开发一个人脸识别系统(demo),这个系统是使用QT实现的,集成了人脸检测,跟踪,识别功能,活体功能。
由于当时对人脸检测,跟踪,识别功能,活体这些算法的原理还不是很懂,所以开发过程中遇到了很多困难,整个开发进度也比较缓慢,印象最深的一个就是识别结果无法实时显示,由于2016年OpenCV的dnn、ncnn等还没有开源,公司使用的是自己的前向引擎,速度较慢,单线程实现从检测到跟踪再到识别和活体这一整套流程速度非常慢,无法实时显示识别结果,最后采用了多线程的方案,识别线程识别的结果返回的时候,如果该对象还在画面中就显示,这样就可以达到实时显示的效果,如上图所示。这个项目做完之后还是非常有成就感的,而且这个项目做完之后,就在公司得到了广泛使用,主要用于公司销售对外宣传产品时的演示软件,同时一直用于公司的展厅中,向客户展示公司的人脸识别和活体技术。
接着到了2016年年底的时候我开始了我人生第一个深度学习项目:活体算法的开发。其实2016年市面上基本上没有什么活体技术,就连旷世,商汤等公司活体应该也没有出来。那个时候其实也不太懂深度学习,自己是一边学习一边工作,白天在公司干活,晚上回去学习,经常会学习到很晚,那段时间虽然比较忙,但还是比较开心的。大约经过了半年的时间,最终活体算法还是上线了,虽然精度不是特别高。活体算法的出现推动了公司人脸识别算法的落地。其实对于我,这个项目最大的收获是学习了深度学习的基本知识,了解了开发一个深度学习算法的基本流程(需求分析,数据采集和数据标注,算法选型,算法迭代和优化,模型部署)。我的第一篇深度学习博客:LeNet论文的翻译与CNN三大核心思想的解读,就是在这个项目完成之后写的,也算是我那段时间学习深度学习基础知识的一个总结了。活体这个项目后来交接给其他小伙伴了,直到现在还在继续优化。
2017年下半年我就转到检测方向了,这个方向是我一直很感兴趣的方向。做的第一个检测项目就是基于MTCNN的头肩、人脸检测以及landmark检测。其实MTCNN是2016年出来的,一出来,就受到了广泛的关注,各家都在复现论文,我们算是起步较晚的,到了2017年的时候,其实已经有开源的出来了,所以我们就直接使用的开源的,但是只有caffe的windows版本的,通过一番努力,成功将windows移植到了ubuntu上了,大大加快了训练速度。刚开始做的时候,由于不懂原理,做起来比较吃力,于是那段时间,每天早上起得很早,利用早上的时间研究了论文和源码,大概花了一周所有的时间,各个方面就比较了解了,做起来才顺手了很多。后面在开发过程中,也在不断深化对MTCNN的理解。开发过程中遇到的最大的一个困难是误检特别多,但是想了很多方法,最终通过离线mining的方式很好的控制了误检。这个项目做完之后,对目标检测的基本原理就非常了解了,为后面理解SSD奠定了非常重要的基础。
自己还深深体会到一点:只有深入理解了算法原理,才能够真正使用好算法,深入理解算法原理的必经之路就是研究论文和源码。
2017年的下半年做过一段时间的人脸识别相关工作,大概有两个月左右的时间,期间预研了SphereFace这篇论文,通过这段时间的学习了解了人脸识别的基本原理。
2017年同步还在开发跟踪算法,其实早在2016年团队就开始研发人脸跟踪算法用于人脸识别项目,最初项目中采用的是一种基于传统V-J人脸检测器的跟踪算法,该算法的基本原理很简单,就是先做检测,然后在检测到的目标区域周围选择一个候选区域作为目标下一帧中可能出现的位置,然后在下一帧中只在该候选区域做检测,由于只在局部区域做检测所以相比于对全图做检测速度会大大提高,在选择候选区域的时候,同时采用了运动估计,确保候选区域的准确性。但是该算法存在下面缺点:
- 传统的V-J检测器的检测精度较低,跟踪的时候在局部区域经常会检测不到人脸,导致跟踪失败。
- 传统的V-J检测器在局部区域做检测的时候,依然存在速度较慢的情况,特别是在嵌入式设备上。
当时预研过很多算法:基于模板匹配的跟踪方法,基于KCF的跟踪方法,基于TLD的跟踪方法,但是效果都不好。除了上面在算法上的技术难点外,还有另外一个技术难点,最初实现的跟踪算法框架是单线程的,没有对输入视频流做缓存,当跟踪算法处理相机输入的实时视频流时,由于实时视频流的速度较快,而检测算法速度较慢,所以当检测算法检测完一帧图像再去处理下面的帧时,就会丢掉中间的一些帧,这样就会影响后面的跟踪效果。下图是单线程跟踪算法示意图。
有一段时间一直在琢磨如何提高跟踪效果,突然有一天大脑中出现一个灵感,就是目前跟踪算法中的局部区域的检测可以等价于MTCNN中的回归问题,于是尝试实现了一下,结果效果非常好,显著提高了跟踪的速度和精度,该算法在后来的人脸识别项目的测试中,精度和速度都要优于模板匹配、KCF、TLD、Sort等算法,广泛应用于公司的各类产品中。因为这个项目,工资涨了不少。其实这个算法的原理非常简单,下面简单阐述一下MTCNN用于跟踪的基本原理。
目标检测任务其实本质上可以划分为两个独立任务,即分类和回归,分类就是判断该候选区域是哪一类目标,回归任务就是对该候选区域的位置进行调整使其更加接近真实位置。MTCNN中的三个网络采用了上述思想,每个网络都采用了分类加回归的多任务学习的策略。
上图是坐标回归的示意图,其中上面的框为候选框,下面的框为ground truth,回归的4个偏移量为如下形式:
R
1
=
(
x
1
−
x
1
’
)
/
w
’
R1=(x1-x1’)/w’
R1=(x1−x1’)/w’
R
2
=
(
y
1
−
y
1
’
)
/
h
’
R2=(y1-y1’)/h’
R2=(y1−y1’)/h’
R
3
=
(
x
2
−
x
1
’
)
/
w
’
R3=(x2- x1’)/w’
R3=(x2−x1’)/w’
R
4
=
(
y
2
−
y
1
’
)
/
h
’
R4=(y2- y1’)/h’
R4=(y2−y1’)/h’
其中(x1’,y1’)表示的是候选框左上角坐标,w’和h’表示候选框的宽高,(x1,y1)表示ground truth的左上角坐标,(x2,y2)表示ground truth的右下角坐标,在预测阶段,对于给定的候选框,我们可以通过网络输出的四个偏移量计算出目标的真实位置,因此回归任务可以看成是在局部区域(即候选区域)做检测,利用这个原理,我们就可以将MTCNN中的RNet或者ONet用作局部区域的检测器,替换掉原来的V-J检测器,在实际项目中使用的是RNet,由于RNet这个网络非常小,速度非常快,而且精度也很高,替换掉原来的V-J检测器后,跟踪精度和速度得到了显著提高。下图的黄色框为系统预测出来的下一帧中目标可能出现的位置,在该区域使用RNet做回归之后,可以得到下一帧目标实际的位置。
虽然跟踪精度和速度得到了显著提高,但是还有一个问题没有解决,就是由于检测速度较慢导致的丢帧问题,在目标移动速度较快的时候容易导致跟踪失败,针对这个问题,将原来单线程跟踪框架修改为了多线程跟踪框架,多线程跟踪框架示意图如下图所示。
下图为最终的算法运行效果图,效果还是非常不错的。
2017年年底OpenCV开源了DNN模块,这让之前的MTCNN和跟踪算法速度又有了一个较大的提高,也为后面SSD的研究提供了重要基础。
到了2018年,我开始带一些小伙伴了,同时也全面转到了SSD的研究,期间精读了SSD的论文和源码,写了几篇博客(比如SSD原理解读-从入门到精通),并完成了部门SSD算法的培训工作。在理解了SSD源码之后发现,SSD和MTCNN的本质都是一样的,都是对滑动窗口的分类和回归,而且我对SSD的理解很大程度上要归功于之前对MTCNN的理解。对SSD的探索过程相比较MTCNN要漫长曲折,期间也对SSD做了很多优化,包括优化backbone,优化anchor的设计,加入FPN结构,优化mining机制等,其中部分工作是2019年才完成的,期间很多工作也是在团队小伙伴的协助下完成的,这里非常感谢检测组的全体小伙伴。目前的SSD无论在速度还是精度上,都大大超越了之前的MTCNN算法,在公司产品中也是完全替换了MTCNN算法,也算没有辜负我们组对SSD的研究了。
到2018年年底,检测组的训练框架,测试流程,测试工具基本上已经完成了,虽然那个时候我也带了几个小伙伴,但是他们也都还没成长起来,重要的工作还是需要自己亲自动手,所以绝大部分工作都由我一个人来完成,从2017年检测组0技术积累开始到2018年年底整个框架流程全部搭建完成,中间经历了很多曲折和困难,但是也让我得到了很大的提高。
到了2019年,整个2019年其实都是在对之前的工作进行迭代和优化,期间也探索过一些新的方向,包括YOLOV3和anchor free系列,但是都没有什么突破,速度和精度还没有完全超越目前生产上使用的我们自研的SSD网络。2019年我印象最深刻的是海思AI芯片上SSD代码的移植和优化。由于海思AI芯片上的CPU算力非常有限,导致了SSD的CPU部分耗时较大,给实际部署带来了巨大挑战,然后我就尝试着去优化,其实当时也没有想过能把优化做好,心里想着大厂官方实现的代码应该是做了很多优化的,但是经过几天的研究发现官方代码还是有一些地方可以优化的,通过努力,在模型精度保持不变的前提下,CPU速度提高了一倍。 具体优化细节见博客:海思AI芯片 HI3516DV300上SSD代码的优化。完成这项工作后,还是非常有成就感的,这件事情给我的启发就是不要迷信权威,要相信自己,要敢于突破,说不定会有奇迹出现。
像流水账一样记录了这4年技术上的成长。从完全不懂深度学习,到现在能够熟练掌握分类,检测,以及跟踪的基本原理,特别是在检测领域,对很多算法有过深入研究。站在现在这个点回头看,这4年来技术上的成长还是非常大的。感谢小视提供的这个平台,感谢小伙伴对我的支持和帮助,更要感谢那个曾经很努力的自己。
学习方法
其实这4年里比学到具体知识更重要的是我掌握了一套适合自己的学习方法。大约在5年前,我看过一本书,刘未鹏写的《暗时间》,里面讲了很多很好的学习方法,当时给我很大的启发,后来还写了一篇博客如何高效学习-《暗时间》读后感,但是那个时候也仅仅是有所启发,还并为真正消化吸收为自己的东西。经过了差不多5年时间的实践,直到现在才形成了一套适合自己的学习方法。这让我深深体会到要彻底改变一个人的思维习惯和行为习惯需要经过长时间反复练习,正如10000个小时定律一样。也告诉我一个道理,做任何事情,都不能急于求成,要持之以恒,总有一天量变会发生质变的。
管理
我虽然从2018年就开始带小伙伴了,但是说实话感觉并没有太多管理上面的经验,这里分享一些自己的心得体会吧,有些想法也是受到了《阿里工程师的自我修养》这本书的启发。
俗话说:招对了人,团队就已经成功一半了。我遇到的第一个问题就是招聘。一开始,我是没有任何招聘经验的,刚开始招聘的时候,标准完全是参考了自己对算法工程师的理解,而且更多是对一个人技术方面的考察,但是当我渐渐了解了一些人,目睹过一些小伙伴离职之后,我发现我的理解是不对的,后面又经过了很多次的修正,才逐渐形成了自己招聘的一些方法论。这里分享一下自己的招聘标准,就当抛砖引玉了。
如何招聘
下面是我这两年来形成的招聘标准
一开始招聘的时候,我只关注专业基础,后来逐渐发现了一些问题,有些人虽然专业基础还可以(不排除背题之嫌),但是到了实际工作中却发现工作态度不好,交给他的任务没有责任心,每项工作只追求"不求有功,但求无过",以这种心态工作,工作质量可想而知。还有一些人,虽然专业基础合格,工作态度也还不错,挺负责的,但是呢缺乏深度思考能力,工作上面没有什么突破和亮点,个人成长也有很大局限性。在意识到这些问题之后,就逐渐改变了自己的招聘标准。
对于算法工程师来说,专业基础是必须要具备的。一般我会围绕简历上某个项目问,对于应届生,如果没有什么项目,我会单独出一些题目,这部分一般是比较好量化的。但是后面几项:工作态度,深度思考能力以及更高层次的动机,是没有办法量化的,也无法在面试中完全体现,那怎么办呢?这两年里对于这些问题还是有自己的一些体会的。
关于工作态度的考察
我会观察这个人的简历排版和格式,笔试题的书写规范,答题的认真程度,以及说话的语速、语气以及措辞,甚至我会观察当天你的穿着打扮。一般简历排版和格式清晰工整,笔试题书写比较规范,答题也比较认真的人工作态度都会比较好。
关于深度思考能力的考察
相比于工作态度和动机,这一点其实是比较好量化的,我一般会看这个人的在校成绩和获奖情况,比如高等数学、线性代数或者程序设计考试95+的,拿过国家奖学金,拿过ACM奖甚至是蓝桥杯比较好的名次的,这些人的学习能力一般都不差。
关于动机的考察
我觉得这个是最难考察的。虽然我觉得对于程序员来说,兴趣是最好的老师,但是真正能够对所从事的工作有强烈热情且能够坚持下去的寥寥无几,甚至可以说是万里挑一。以前看过一句话:一个人最大的天赋就是从什么事情中获得乐趣。 我觉得太对了。但是在面试这么短的时间内考察一个人的动机是非常困难的。一般我会考察这个人是否有阅读的习惯,是否有写技术博客的习惯,github上是否有出彩的项目。
上面就是我关于招聘的一些个人看法了,这些经验在我两年的管理中也得到了验证,现在是我的最佳实践了。
团队文化的建设
人招来了之后,是要管理了,其中最重要的就是团队文化的建设了。
我个人最大的体会就是,作为团队leader,最重要的一条就是:以身作则,言传身教。自己做不到的事情,又怎么能够要求别人做的很好。比如关键项目一定要深度参与一线工作,制定的规范标准和制度自己要起到带头作用起到表率作用,这一点也是我一直都在实践的,在博客中应该也可以体现出来这一点。作为leader还需要公平,公正,坦诚。坦诚不仅仅要对那些表现良好的人,还要对那些表现糟糕的人。敢于指出他们的问题,对于表现不好的员工要敢于批评和管理,例如为什么解雇你。还有一点比较重要,就是培养小伙伴主人翁(Owner)的意识,培养小伙伴认真负责,积极主动的工作态度,可能一些有能力的小伙伴有点放不开,有自己的想法也不敢提,所有决定都需要通过leader,对于这些小伙伴,我一般会放权比较多,我只会制定目标和任务,具体的执行都是由他们来做,这样反而效果会比较好。对于能力一般的,我管的会细一点,但是等他们成长了之后,我就会放权给他们自己完成。总之,关于管理还有很多东西需要继续摸索。
上面的就是自己这两年来做管理的一些体会了,不算是真知灼见,但是也是自己亲身实践总结出来的,有些经验在实践中反复得到了验证。
离开,是为了更好的开始
天下无不散之宴席,感谢在小视的成长的同时,也明显感觉到了自己的瓶颈,整个检测组的重要工作基本上在2019年之前已经完成了。检测组的各种测试工具,测试流程也都在2019年之前全部完成了,一直用到了现在,整个2019年基本上没有什么突破和亮点,自己也陷入了深入学习玄学调参之中,这个为什么能work?那个为什么不能work?这个idea不能work,自己再努力努力,改进改进,希望能有所突破,但是其实自己都不知道什么时候才能有突破,通常都是做了一周甚至一个月的实验,一点进展都没有,经常陷入深深的困惑和迷茫中。
2019年公司的经营状况不是很好,年底也在裁员,部门内部也遇到了一些管理上的问题,我的一些理念和领导也发生了较大的分歧。过年期间我一直在想:是不是到了一个拐点了,我是不是需要跳出来看看外面的世界了?在一个地方呆久了,逐渐产生了惰性,逐渐丧失了热情和动力。所幸我现在手头上已经没有固定的工作了,跟其他项目也没有太多牵连,下面的小伙伴已经能够胜任各自岗位上的工作了,这正是我离开的最佳时机。经过几天纠结,我终于正式向公司提出了离职申请。
离开,是为了更好的开始。我依旧会带着对AI的热情继续探索和前进!
2020-2-15 19:28:36