天天看點

三年計算機視覺算法工程師經驗血淚總結!

作者:程式員城哥
程式設計有難度,城哥有态度!關注我,帶領大家一起學習起來
三年計算機視覺算法工程師經驗血淚總結!

1 前言

從事CV算法這個職業已經三年有餘了,如果加上畢業前玩命轉行跳坑(生化環材)的那兩年,到現在已經差不多快五年了。這五年來,CV越來越卷這個說法的聲音越來越強,我個人也是比較贊成這個說法的。不論是出于什麼原因,在這個各行各業都在尋求所謂的AI賦能的時代下,CV絕對是轉行跳坑之人最容易與計算機産生關系的行業了。

正因為這樣,這個所謂的卷,給我自己的感覺就是分母太多,炮灰太多。今年也陸陸續續面了不少人,有太多人我隻能稱之為”做過CV“,僅此而已。絕大部分人基礎不牢、或是代碼能力弱、或是對技術沒有思考、或是項目經驗太水。是以不求做一個手握幾篇頂會的大神,做一個基本功過硬、項目不水的合格的CV算法工程師,在市場上還是會有一定競争力的(不要被貴乎人均3篇CVPR打擊到自信)。

寫下這篇的文章的目的,也是來分享一下本人最近幾年(尤其是工作這三年)的一些心得,希望可以為剛入行或正在轉行的CVers提供一些參考。

後文分為基礎技能和實戰能力兩個部分。基礎技能篇包含我認為CV算法工程師一些必備的重要硬技能,可能不全,歡迎補充;實戰能力篇包含我在實際項目中的一些體會。

2 基礎技能

俗話說得好,基礎不牢,地動山搖。我曾經就見過因為基礎不牢間接導緻項目夭折,甚至是丢掉工作的案例。是以無論是數理基礎、圖像處理/ML/DL基礎,或是程式設計基礎,都是越紮實越好。

2.1 理論基礎必須夯實

這裡把非coding的基礎全部劃歸于理論基礎,包括但不限于圖像處理、機器學習、深度學習、模型設計、優化方法、評估名額、基礎任務等等。這些基礎不牢靠,其他能力再強也都是空中樓閣。

比如基本的圖像操作,包括數字圖像運算、濾波、ROI、仿射變換等等都必須了如指掌。這些雖然可能不作為這個崗位的核心競争力,但也絕對是必備技能,隻要涉及到CV,就繞不開這些。

再比如卷積、池化、BN、一些基礎loss等等這些最基本的CNN操作,它們的原理、計算方式、特點都必須要做到爛熟于心,不能有一絲一毫的含糊。因為隻有做到這樣,才可能為你在項目裡設計模型、設計loss這些工作中掃清障礙。舉個簡單例子,設計模型的時候要考慮每一層的感受野,要考慮淺層和深層的feature的差異,要根據不同的任務考慮平移不變性或平移等變性,要考慮每一層卷積的計算量,設計loss的時候可能要考慮困難樣本的挖掘,這些都是基于牢固的基礎才有可能完成。

除了基礎,還需要掌握CV領域各個基礎任務的正常算法。個人認為這裡的基礎任務至少包含分類、檢測、分割、GAN。工業界大部分的實際任務都可以抽象成這幾個基礎任務或者是這幾個基礎任務的擴充,是以多了解一些它們的經典算法也有助于實際項目的快速開展。這裡簡單列舉一些。

分類:AlexNet、VGG、ResNet、MobileNet、ShuffleNet、GhostNet、SqueezeNet等。

anchor-based檢測:RCNN系列、SSD、YOLO系列(不包含YOLOv1)

anchor-free檢測:YOLOv1、FCOS、CenterNet等

分割:FCN、SegNet、UNet、Deeplab系列等

除此之外,還有許多雜七雜八的知識點和技巧最好也能熟練掌握,比如CNN一些簡單好用的小子產品(SE、CBAM等)、模型蒸餾、Vit(self-attention)、算子融合、結構重參數等。

三年計算機視覺算法工程師經驗血淚總結!

2.2 代碼能力必須過關

斟酌了一下,這個小标題裡用了「過關」而不是「過硬」,是因為我發現很多所謂的算法工程師,代碼能力根本不及格。是以代碼能力這塊也是我想強調的重中之重。

我所秉承的代碼兩條原則:

  1. 盡量自己寫代碼!
  2. 使用别人的代碼必須自己先讀懂,堅決杜絕不讀代碼就直接使用别人的代碼!!

先說第一條,有人會問網上代碼都是現成的,為什麼要自己寫,我想說最簡單的原因就是你是個工程師,寫代碼就是你的看家本領啊!而且不可能永遠都用别人的代碼吧,總有一些子產品是必須自己實作的。

具體來說,自己寫代碼會讓項目變的相對可控,畢竟代碼是自己一行一行敲的,一旦需要修改或者優化都會更友善;自己寫代碼還有一個重要的好處,就是寫代碼的過程其實對各種原理深入了解的過程,你一旦能獨立實作一個算法邏輯,那說明你百分之百掌握了它。

寫代碼是個潛移默化的過程,堅持自己寫,寫的久了,會發現自己的bug定位解決能力、項目代碼架構、代碼細節、算法原理各個方面都有提升。

是以千萬不要犯懶,不要眼高手低,一定要自己多寫,寫的過程中多寫test case多測,培養自己的動手能力。

再來看第二條,其實直接用别人的代碼也是挺常見的一件事了,畢竟沒必要重複造輪子。但我一直認為,程式員要時刻保證自己的代碼是可控的,也就是一旦出了問題你能以最快的速度定位到。這就意味着,在使用别人代碼之前,必須要認認真真的讀一遍,至少要搞清楚每一行的輸入輸出都是什麼,這樣你在修改或者優化的時候,你才知道在哪改啊(畢竟大部分情況下别人的代碼僅僅隻是能用,但都不是最适配于你這個項目的)。

說到讀代碼,我發現有很多剛入行的朋友,自己寫一點還勉強能寫,但一旦讀别人的代碼就會有相當大的障礙。這個問題其實很好解決,隻要掌握好方法并且克服心理恐懼,就可以了。

讀一個項目的代碼的時候,一定不要先去讀一些核心方法,一定要先從整個項目的入口開始讀起。用一個比較好用的IDE(比如我自己用jetbrains),從入口開始,碰到調用的子產品就點進去看明白,一層一層的由外向内深入,讀的過程中也可以配合着紙筆,邊讀邊畫一下項目的架構。

總而言之,無論是寫代碼還是讀代碼,都要一點一點的啃,沒有捷徑!隻有不辭辛苦、不怕麻煩的人才能獲得代碼能力上的提升。

三年計算機視覺算法工程師經驗血淚總結!

2.3 關于程式設計語言和工具

經常看到有人問做算法工程師,隻會python行不行,除了python以外學個java好還是學個c++好之類的問題。先說結論:隻會python不行,第二門語言必須是c++,sql最好也懂一點。想繼續拓展的話java和golang都行,我個人推薦golang。

我自己在當年上學剛轉行的時候也被同樣的問題困擾了很久,記得那時候想學第二門語言,java看了看,c++看了看,甚至還有一段時間在想要不要把再matlab系統學一遍,也是走了很多彎路。

先說python。python确實相對其他語言簡單一些,畢竟自己不用編譯,文法也不難。但我的觀點的是不要把它想的過分簡單了,寫的時候還是有很多基礎細節點需要注意的(比如我很早之前在一個項目裡被一個深淺拷貝的bug折騰了一周,就是典型的基礎有問題)。除了python基礎之外,平時用python的重中之重就是和各種package打交道了,opencv、numpy,pytorch/tf這些等等。這些要做到的就是常用的api最好非常熟悉,否則每次都要查用法的話效率真的會很低,自己也累。

這裡多說兩句:

  1. 因為每天都在面對矩陣運算,是以不管是numpy還是pytorch,設計到矩陣的操作比如cat、次元變換、切片、各種mask等這些操作一定要熟悉,否則會很難受。
  2. 對于訓練架構Pytorch/tf等等,不隻是要熟悉api,還要了解程式設計範式,比如資料怎麼load,訓練流程怎麼寫,這些最好多讀讀别人的代碼,訓練架構的社群也挺成熟的,可以多去逛逛,最終最好形成自己的一套關于訓練架構的程式設計範式。

再說C++。剛才說了,C++是必備技能。原因很簡單,一定要牢記自己的title是算法工程師,既然是工程師就離不開模型部署,部署就繞不開C++。關于部署與C++的關系,可以看我這條回答,這裡就不重複了。我也是在工作之後的實際部署項目裡學的C++,算是現學現賣,過程很痛苦,雖然到現在也沒有精通,但起碼是能獨立部署模型,能用C++寫業務邏輯,勉強夠用了。關于C++的學習,我個人的一點愚見就是一定要多動手,多寫,最好能去實戰一兩個項目,千萬别想着速成,市面上那些什麼多少天速成C++的一個都不要信。順便吐個槽,C++這玩意兒真是一個月不寫就手生。

除了C++本身之外,還需要懂一些編譯的知識,至少要會用cmake編譯項目,畢竟這玩意兒跟python不一樣,你得自己編譯。關于cmake編譯,可以看「基礎知識」gcc/g++編譯過程及gcc/g++與cmake對應關系。

書的話,就看C primer plus和C++ Primer我覺得就夠了。

3 實戰能力

這一趴包含工業界實際項目中我的一些心得,主要強調軟實力。大家都調侃深度學習是玄學,那在這種條件下,在實際項目中總結出一些心得感悟就顯得尤為重要了。

3.1 時刻牢記以業務需求為導向

如果你的崗位是算法研究員或算法科學家等research崗位,那這一部分可以忽略。但算法工程師,畢竟不是純研究職位,其主要職責還是要去服務于具體業務的。但我發現有很多人(也包括我自己)都會偶爾鑽進牛角尖,追求最新最fancy的技術,而忽略的真實的業務需求。這并不能稱之為一件壞事,但在實際業務中,這卻是一種效率低下、針對性差的做法。不是說新技術就不能用,是不能以使用新技術為目的,而必須以比對業務需求為導向,即使是新技術,也要考慮它是否可以滿足具體項目中具體的需求。在這一部分我總結了以下兩點。

  1. 必須明确項目上線後的應用場景。應用場景拆分的盡量細緻一些,比如宏觀上的場景有安防、交通、使用者直播小視訊等,細緻一些比如室内or室外場景、使用者是否露臉、使用者距離鏡頭的距離等等。資料集盡量要比對應用場景,最好使用線上資料,就算要用開源資料,也要盡可能的比對特定的應用場景,切忌随便拿過來一個popular的資料集就直接用。
  2. 必須要在效果和性能上做平衡。無論是移動端還是服務端的模型,大部分情況下我們都無法兼顧效果和性能。很多時候在推理算力的限制下,我們甚至要犧牲一些效果來滿足性能要求。這個trade-off是算法工程師不得不面對的,有時候好不容易做出個不錯的效果,但是手機端上幀率隻有幾幀,那也是沒辦法上線的。公司不會單為了你的這點功能去擴容,使用者也不會因為你的功能去換一台高性能手機。滿足性能要求是底線,如果覺得效果上有問題,并且在模型側難以解決,可以嘗試一些後處理的業務邏輯去彌補。

3.2 調研/論文閱讀

我個人認為,基于項目的發展順序,調研工作應該分為競品調研、資料調研和論文調研。見過有人不習慣或者懶得做調研,拿到任務亂試一氣,幾個月都沒什麼像樣成果。是以千萬不能忽視調研工作,調研做到位了,能起到事半功倍的效果。

先說競品調研。無論是自己測試競品app、demo、sdk還是直接打聽,目的都是為了确定大方向,大方向指的是項目應該用分類做?分割做?檢測做?分類+檢測?需要後處理?GAN?總之就是在宏觀上确定項目是CV中的哪類或哪幾類任務。

再說資料調研。這裡主要是收集資料,如果有線上的标注資料最好,如果沒有那就根據具體場景去找一些開源資料,有的時候我們苦于隻有原資料但沒有标簽,這個時候找一些AI開放平台的sdk去生成一波标簽也不是未嘗不可,但畢竟是機器生成,還是需要人工過一遍。當然了,在缺資料的情況下最好還是能向公司申請,從資料标注公司買資料,這是最省心省力的方式了。

最後說一下論文閱讀。确定了大方向之後,肯定要去讀一下這個方向的論文,看看有什麼具體的做法。這裡要明确兩點。

  1. 不要讀超出自己能力範圍的論文,讀論文要循序漸進。如果你對一個任務方向不是很熟悉,或者說,這個方向的過去幾年的經典論文都沒讀過,那一定不要上來就看一些比較新的論文。我一開始就犯了這樣的錯誤,對自己不熟的方向,直接就想看下最新的論文,看看能不能直接用上。結果就是很難看懂啊,很多方法、術語都不明白。是以急功近利是不可取的,這種情況下一定要按照時間順序,依次讀一下這個方向的經典論文。比如做生成任務,都不了解vanilla GAN就直接去看cycle GAN,那肯定是不行的。
  2. 不要被五花八門的論文困住了,隻去精讀經典高引論文即可。之前很讓我苦惱的一點是,嘗試了很多論文中的各種方法,但模型就是沒提升,甚至還下降,讓我不禁感歎這年頭水論文也太多了吧,全都是面向資料集優化,換個資料集換個場景全都GG。後來我看了 @周縱葦 大佬的 周縱葦:研習U-Net 這篇文章,非常精彩,裡面有個醍醐灌頂的觀點,簡單總結一下:很多論文都是站在高引論文上做微創,讀論文要重點讀高引的具有0-1開創性工作的論文,并且要分析它的底層邏輯,比如Unet的拓撲結構。确實,微創的工作在工業界,大機率換個場景換個資料集就不行了,但開創性的工作之是以能得到高引,就是因為它堅實的底層結構使其通用性更強,基本上保證了下限。

3.3 資料是最重要的

關于資料,最經典的莫過于那句「資料和特征決定了機器學習的上限,而模型和算法隻是逼近這個上限而已」。其實在CV工業界我還想再加一句,資料也基本決定了下限,資料到位了,讓狗随便寫個模型都能起飛。雖然有點誇張,但其實就是想強調工業落地中資料是最重要的一環。

這裡我必須說明的是,資料做到位,絕對不是無腦堆資料集。我很負責的說,無腦堆資料集增加資料量的做法,大機率作用不大。想要在資料上下功夫,資料量固然重要,但就像我之前說的,首先一定要考慮具體的應用場景,堆資料也要有目的性的堆,堅決杜絕小鹿亂撞。其次,要習慣于分析資料,計算資料集的各種統計資訊,根據分析得到的認知,仔細設計資料前處理的過程(比如檢測裡anchor和gt的比對過程)。

同時也要有分析bad case的習慣,每訓練出一個模型都要去總結一下所有bad case的場景特性,比如是光照條件下效果差,運動條件下效果差,目标不全的情況下效果差,等等。根據bad case的場景特性,在資料上做針對性的優化,缺啥補啥。是以,資料增強也不是随便增強的,而是有針對性的。用到的每一種增強方法都應該有它背後想解決的問題,絕對不是别人做了個平移翻轉,你就也要做個平移翻轉。

3.4 分析問題和解決問題

分析問題和解決問題的能力基本會出現在每一條招聘的JD上。我一開始也覺得這一條隻是用來湊字的,但其實,在工作中這一條至關重要。從每一次debug,到對項目整體效果和性能上的把控,分析和解決問題的能力都要發揮作用。

分析問題和解決問題聽起來挺玄幻,但我認為,在算法工程師這個職業中,這項能力也是可以去拆解的。我個人的觀點是:紮實的基礎、良好的邏輯性、工匠精神以及經驗的總結,共同構築這項玄幻的能力。

首先,紮實的基礎,這個不用多說了。比如想要優化一下模型,如果連CNN都不夠熟悉,那其他的都是空談,再分析也是亂分析。

其次,良好的邏輯性,是要透過現象看本質。要做的就是先去分析現象(比如模型效果到底哪裡不好,在什麼場景下不好),然後通過這些現象的總結,逐個分析可能的原因,比如是資料原因、還是模型設計的不好、還是訓練手段或者loss設計的有問題?再比如若是loss的原因,具體是什麼原因呢,是不是要從前向傳播和反向傳播兩個角度再分析一下呢?這一點其實就像警察叔叔破案一樣,層層深入。把所有可能的原因分析到位後,再逐個擊破。

第三,工匠精神。列出這一點的原因的是在分析并解決深度學習的問題時,往往要做大量的實驗,這些實驗往往不能對猜想做到快速驗證,是以會非常的耗費時間和精力。根據我的個人經曆來看,做實驗是平時工作中做的最多的事情了,非常容易産生厭煩情緒,是以還是要調整心态、不怕麻煩、勤動手、具備工匠精神。

最後,要善于總結經驗。有的時候,不同的任務遇到的問題是相似的,如果善于總結經驗,那遇到問題時有可能會用過去的任務經驗快速解決。在模型問題上,我個人大體上的方法論是一定先從資料入手,往往資料問題得到解決後模型效果會有不小的提升;其次再去分析模型設計、訓練技巧、loss函數這些問題,大多數情況下解決這些問題也會有一些提升,但是提升的幅度會偏小一些。

除去以上這四點,另一個小感悟是一旦遇到問題,一定要先嘗試自行分析解決,盡量不要一上來就問leader或同僚。自行分析解決的好處是,在這個過程中自己的感悟會更深,利于總結經驗,也容易形成一套适合自己的方法論;同時這個過程也比較容易發現基礎上的漏洞,起到查漏補缺的作用。自行嘗試無果後,再去咨詢一下leader或同僚的想法,看看别人的idea,這樣對自己的成長幫助是最大的。

4 結語

從勸退專業轉碼到現在已經有5年的時間了,工作也已三年有餘,說長不長,說短不短。這一路上自己也走了許多彎路,遇到了各種各樣的坑。這篇文章一定有許多經不起推敲的地方,但這确實是我這幾年來基于自己的經曆,得出的适用于我自己的經驗心得。我相信每一個算法新人最終都會形成一套适用于自己的工作經驗或方法論,也許這篇文章提到的内容并不适用于所有人,但也希望能起到抛磚引玉的作用,給剛入行迷茫的新人一些啟發,幫助新人快速的找到适合自己的道路,少走彎路。

同時,工作三年作為一個裡程碑式的節點,做這樣一個記錄我認為是很有意義的。期待接下來的三年大家都能有更長足的進步。

繼續閱讀