機器學習、深度學習的基礎已打好,怎麼用最快的速度解決算法問題

機器學習、深度學習的基礎已打好,怎麼用最快的速度解決算法問題

本文為多內容,建議閱讀12分鐘

本文是寫給萌新的“科學煉丹”手冊。

機器學習、深度學習的基礎已打好,怎麼用最快的速度解決算法問題

前言

近期在訂閱號後臺和文章評論區review了一下大家的問題,發現很多小夥伴現在已經把機器學習、深度學習的理論基礎打好了,但是真正要解決一個現實中的算法問題的時候經常兩手抓瞎,一頓毫無目的亂試,甚至認為模型表現不好一定是調參不夠仔細。

新手最大的問題在於解決問題的邏輯不夠清晰,喜歡使用“蠻力法”,因此很容易陷入一個

“找到一份開源代碼“

“跑了一下,調了調參“

”效果不好,下一個”

“效果好,拿去做集成”

死循環中。就像下圖一樣

機器學習、深度學習的基礎已打好,怎麼用最快的速度解決算法問題

倘若恰好碰到一份代碼,讓他的精度上了兩個點,於是他開始吹噓“這個模型太牛逼了,吊打XXXX”。而對於跑了一下沒什麼提升的算法,他就會說“肯定論文造假了,一點用都沒有”。

然後以這種暴力搜索的方式解決問題久了之後,他開始對別人吹噓。

“害,俺們深度學習這一行,就是跑跑代碼調調參,沒有一點競爭力”。

自嘲的話語被奉為圭臬可能是深度學習領域的最大悲劇。

今天跟大家分享一下我覺得應該瞭解的一些的煉丹常識,希望小夥伴們在看完本文後,可以幫助初學者提高煉丹能力和效率,走出無腦調參的怪圈。

具體來說,希望幫助大家實現下面幾個目標:

  • :以最快的速度解決一個算法問題
  • :以最少的試錯次數找出最優策略
  • :以最不容易出錯的方式管理實驗

在講“快”之前,先來講講“準”字訣,因為準是最重要的問題,而且是快的前提。

調研做好,找準起點

接手一個算法問題後,如果時間很充裕,就可以先定位一下該算法問題所對口的學術會議或期刊:

比如你要解決query-doc相關性匹配的問題,那麼你就要優先考慮SIGIR、CIKM等IR強相關的會議,而不是NLP的會議;

如果你要解決NLI、問答、對話這種語義匹配的問題,那麼你就要優先考慮ACL、EMNLP、NAACL、COLING這種NLP會議,而不是IR會議了;

如果你把匹配模型做好了,想壓縮一下變得更小更快,那就要優先考慮ICLR、NIPS這種更general的深度學習、神經網絡會議了。

定位不出來算法問題的對口會議?最起碼可以逛逛AAAI和IJCAI吧(雖然魚龍混雜問題比較嚴重)。

然後根據文章title,找幾篇跟你的算法問題最接近的近兩年的paper,慢慢調研。通過這些paper的related work章節實驗章節,還很容易追溯出更早的工作,類似下圖這種(來自@小鹿鹿鹿的一篇paper),所以一般沒有必要去手動調研更早期的paper。

機器學習、深度學習的基礎已打好,怎麼用最快的速度解決算法問題

於是很順理成章的你就能找到這個算法問題的比較前沿的解法和比較經(簡)典(單)的方法啦,進而就有了不錯的baseline,迭代幾版策略後說不定就出來一篇新paper。

不過,如果時間很不充裕,要解決的問題又比較簡單(比如就是個典型的文本分類問題、序列標註問題等),在知乎上搜一下也經常能發現驚喜,實在不行還可以問小夕嘛( ̄∇ ̄)當然了,問題太複雜的話更建議通過知乎私信右上角進行交流(//∇//)\\

總之,非常不建議直接去github一個repo一個repo的蠻力調參,大量的寶藏方法是很難通過通用搜索引擎來找到的(雖然這種行為在比賽刷榜的時候隨處可見)。

構建策略迭代閉環,找準努力方向

避免蠻力試錯的第二步就是構建完整的策略迭代閉環。由於不同的問題有不同的限定,因此不存在一個絕對的流程可以恰好適合所有算法問題,一個我自己最常用的迭代閉環就是

數據集分析 - 預處理策略 - 算法策略 - 模型評價 - case study

對於小白,往往是在第三步和第四步陷入死循環,看不到前兩個環節和最後一個環節。

對於大白,往往還能額外考慮一下預處理策略。

1. 數據集分析

很多小白拿到數據集後就開始迫不及待的調參之路了,其實在開始之前對數據集做個簡單的分析,可能有助於大大降低你之後的體力勞動(提前排除不靠譜的策略和不敏感的超參數),並大大降低初次接觸新任務時犯致命錯誤的概率。

比如,簡單統計一下樣本長度分佈,你就可以知道max sequence length這個參數的大體取值範圍,沒有必要把它當成一個正兒八經的超參數從小調到大;簡單統計一下類別分佈,你就不會在正負樣本比9:1的情況下為一個90%的準確率沾沾自喜,誤導決策;多掃幾眼數據集,你就不會在初次接觸文本風格相關任務時把英文單詞統一小寫了。

2.預處理策略與算法策略

這個環節不用太多贅述啦,最直接方法就是搬運上一節的調研結果,將一些paper中比較有效的策略搬過來進行驗證。不過,尤其是注意一個meaningful的問題,即我搬運這個策略,甚至設計一個新的策略,目的是什麼?要解決什麼問題?畢竟很多paper中的策略的適用場景是很侷限的,毫無目的的搬運可能會大大增加無用功。

3.模型評價

模型評價的問題在打比賽時一般不會遭遇,在比較成熟的算法任務中一般也被解決了。比如談到文本分類,就能想到acc、f1等指標;談到機器翻譯,就能想到bleu等。然而有很多算法問題是很難找到一個無偏且自動的評價指標的。

一個典型的例子就是開放域對話生成問題。

雖然與機器翻譯一樣,這也是個生成問題,但是如果你沿用BLEU作為評價指標,那麼BLEU對對話生成來說就是一個有偏的評價指標,你刷得再高也難以真實反映對話生成模型的質量(對話生成問題中不存在機器翻譯中的強的對齊關係)。更糟糕的是,由於找不到無偏的自動指標,因此每迭代一次策略,就需要讓一群人轟轟烈烈地標註打分,還要去檢驗是否存在異常標註者(說不定有個寶寶就耍脾氣了給你亂打一通),這無疑是效率非常低的,縱然你代碼寫得再快,也會被評價問題所拖累。

業務中更是可能有一些模稜兩可的算法任務,比如“小夕,來個更好的句子表示吧”,那麼如何無偏地評價一個表示的好壞,就需要你在大規模開搞之前仔細設計清楚了。沒有一個客觀、無偏且自動的評價指標,策略迭代無疑會非常緩慢甚至到後期推翻重來。

4.case study

像accuracy、f1、bleu等標量型評價指標可以指導當前策略整體上好不好,但是卻無法幫助你發現更細粒度的問題。很多小白在入行時,喜歡把各種花裡胡哨的算法和各種不著邊際的想法一頓亂試,以為有了模型評價指標就可以很輕鬆地評判一個算法“是不是有用”,以及可以因此純拼體力的煉丹。

但!是!當你額外地做一下case study之後,可能你會突然發現,很多自己之前的嘗試完全就是多餘的:

你以為數據不均衡問題很嚴重,case study才發現模型其實很輕鬆;
你以為推理問題離自己很遙遠,case study才發現一大半的錯例是推理問題導致的;
你以為領域問題不重要,case study才發現太多模型沒見過的領域術語了;


你以為數據集很乾淨,case study卻發現了大量錯別字導致的錯誤決策;

總之,在經驗不足的情況下,通過case study可以幫助你排除大量的不必要嘗試,並有助於發現當前策略的瓶頸,針對性的尋找策略和創新。

重視bug,找準翻車原因

小白經常在跑了一輪迭代閉環之後就受挫:“效果好差啊”。這裡經常存在一個思維誤區:“精度不夠一定是算法/參數不好”。

比如,小白覺得自己上了BERT能達到95% accuracy,結果跑了個baseline後發現acc只有70%,然後就開始1個點1個點的開始迭代策略。努力錯了方向,自然最終結果也不會太好。

實際上在這種策略迭代過程的情況下,優先考慮是存在bug。比如用了BERT-uncased預訓練模型,卻忘了對輸入文本進行lowercase處理;用了個char-level的模型,卻給文本切了詞;萬事俱備後,bash腳本里卻忘了載入預訓練模型等。

有時候調參和使用一些算法策略可以緩解bug帶來的影響,導致小白誤以為繼續賣力地調參和瘋狂試錯就一定能把這個鴻溝填平。實際上,比起算法和超參,bug往往致命得多。當然了,對於一些特殊的算法問題(比如眾所周知的RL問題),超參數確實極其敏感,需要具體問題具體分析。

擺脫“潔癖”,提高寫代碼速度

算法探索具有極強的不確定性,很可能你寫了半天的代碼最後由於不work而完全廢棄,因此,從代碼風格上來說,一定要避免把代碼寫成系統,各種面向對象的封裝技巧一頓亂懟是非常不必要的。允許一些“垃圾代碼”的存在可以大大提高實驗迭代的效率。

問題來了,假如你生產了一堆“垃圾代碼片段”,該怎麼高效利用它們呢?直接丟掉,還是複製粘貼重構代碼?

最簡單的方法是直接使用粘合劑“Bash Script”。即將功能零散的代碼片段通過bash管道命令連接起來,這樣還能通過“&”+wait命令的組合拳實現對大規模數據集的(多進程)並行處理。

對shell實在不熟悉的小夥伴也可以使用jupyter notebook來進行粘合。不過,

強烈建議每個NLP算法工程師熟練使用bash和vim,相當多的數據處理和分析是不需要使用python的,習慣了之後這些bash命令和vim技巧會對煉丹效率有非常明顯的提升。

而對於更加碎片化的小代碼(比如邊分析邊修改邏輯生成一個小字典),則可以考慮使用ipython,完成任務後一條magic命令%save就讓這些碎片代碼變得可複用了。

不僅在生產代碼上可以大大提速,在debug和調參問題上依然是有學問的。

分規模驗證,快速完成實驗

這個問題寫出來時感覺很白痴,但是據我觀察,大部分新手都存在這個問題。如果你給他100萬規模的訓練集,他就會拿整個訓練集去調試;你給他1000萬規模的訓練集,他還是拿整個訓練集去調試,甚至不忘抱怨“數據載入太費時間了,調試好花時間呀”。

機器學習、深度學習的基礎已打好,怎麼用最快的速度解決算法問題

親,debug也是分階段的呀....

第一階段:調通代碼。這時候象徵性的掛幾百條樣本就夠了,修正語法錯誤和嚴重的邏輯錯誤。

第二階段:驗證收斂性。很多bug不會報錯,但會導致訓練完全崩潰或者壓根就沒在訓練。可以對幾百條或者幾千條樣本進行訓練,看看若干epoch之後訓練loss是否能降低到接近0。

第三階段:小規模實驗。在萬級或十萬級別的小樣本集上驗證模型表現,分析超參數敏感性。這一階段在數據規模不大時(比如幾十萬或一二百萬)其實可有可無,當訓練數據極其龐大時(十億級甚至百億級的話)還是必要的。有一些很細微的bug雖然不會影響收斂,卻會顯著影響最終模型的表現。此外也有助於發現一些離譜的超參數設置。

第四階段:大規模實驗。即,有多少訓練數據,就上多少,甚至多訓練幾個epoch。進行到第四階段時,應當絕對保證代碼是高度靠譜的,基本無需調參的,否則試錯代價往往難以承受。

理性調參,把算力和時間留給策略探索

初學者喜歡把什麼都作為超參數來調。

“文本截斷長度不確定設置多少?掛10組實驗調一調”
“官方代碼裡有個warmup不知道有啥用?掛10組實驗調一調”
“聽說batch size對性能有影響?掛10組實驗調一調”
一切盡在調參中╮(╯▽╰)╭

這種做法無疑是極其浪費時間和計算資源的,有的超參數完全可以算出來合理範圍,有的取決於其他超參數和所處環境,有的與網絡結構和預訓練模型強耦合等等。因此,調參的第一步,也是最重要的一步是進行超參數敏感性分析,找到對當前任務性能影響最大的幾個超參數之後再進行精調。

而要確定各個超參數的敏感性,一方面可以根據自身經驗來定,一方面可以根據各paper中的取值(差異大的超參數可能是敏感超參,大家都取值相同的一般不敏感),實在不確定,跑兩三組實驗就夠確定敏感性了,完全沒有必要來個“網格搜索”。

篇幅原因,本文只講通用的方法,一些細節性的魔性調參資料在網上有很多了,這裡就不展開了。

說完了“準”和“快”的問題,下面就到了最容易讓初學者頭疼的“穩”字問題啦。

很多初學者都出現過這種手忙腳亂的情況:

“誒?明明我記得這個腳本能跑出來95%的準確率,再一跑怎麼成92%了?”
“我的模型去哪了???”
“這個模型怎麼訓出來的來著。。”
“這倆策略有哪些diff來著”

機器學習、深度學習的基礎已打好,怎麼用最快的速度解決算法問題

問題就出在了實驗管理和代碼版本管理上。

一個工具就穩了

顧名思義,實驗管理就是要記錄下來每一次實驗的策略名和對應的實驗結果,一般以表格的形式記錄。這裡可以用excel、markdown編輯器等記錄,當然更建議使用支持雲端同步的工具來記錄(比如石墨文檔、印象筆記或內網的相關工具等),以防電腦被偷、文件誤刪等意外導致的悲劇。

但是,有時候實驗著急,對策略的描述不夠仔細怎麼辦?比如某次實驗同時改變了具體策略、超參數、預訓練模型等一堆東西,不能用一個名字概括全部,怎麼辦呢?

最簡單的做法就是與版本管理工具配合,再也不用擔心未來settings丟失、模型無法復現、模型無法追溯環境等問題了。

而要實現版本管理,也很簡單,

Git自然是不二之選。

萌新們注意啦,是Git,不是GitHub!你可以不用GitHub,但是不能不用Git!

怎麼用Git管理版本和實驗迭代呢?

首先,務必保證訓練日誌、eval日誌是以文件的形式存了下來,而不是打印到屏幕上變成過眼雲煙了;此外,需要保證每一次運行時的settings(比如超參數、數據集版本、ckpt存儲路徑等)都能保存到日誌文件中,且儘量封裝一個run.sh來維護訓練任務的啟動環境。

之後就是看每個人自己的習慣啦。我個人的習慣是

  • 主線策略每成功推進一步,就調用git tag打個tag。這裡的tag即策略名,與實驗管理的表格中的策略名對齊
  • 如果要在某個策略的基礎上嘗試一個很不靠譜的探索,那麼可以在當前策略的基礎上拉一個分支出來,在這個分支上完成相應事情後切回主分支。當然啦,萬一這個分支上的策略work了,就可以考慮將其轉正,合入主分支並打上相關tag

這樣將來你想review某個策略時,只需要切換到相應的tag下面或者分支下面就可以啦,完整復現整個環境,並能直接追溯出跑該策略時的一切相關設置,以及該策略下的各種調參結果。

最後,“準”字問題上還要考慮最後一種極端情況,就是整個實驗環境被連根拔起╮( ̄▽ ̄"")╭比如硬盤損壞之類的嚴重故障。因此一定要記得做好備份工作,即週期性地將環境中的關鍵代碼push到github等遠程倉庫。當然了,對於ckpt、數據集這種大型文件,可以寫入.gitignore文件中以免把倉庫撐爆,這些大型文件的最佳歸宿當然就是hadoop集群啦。

科研任務是可以通過開源數據集進行評估的,但業務算法的評估還是要以業務數據為準。

校對:譚佳瑤

—完—

關注清華-青島數據科學研究院官方微信公眾平臺“ AI數據派 ”及姊妹號“ 數據派THU ”獲取更多講座福利及優質內容。


分享到:


相關文章: