万万没想到,我的炼丹炉玩坏了
一只小狐貍帶你解鎖NLP/ML/DL秘籍
作者:夕小瑤
前記
眾所周知,夕小瑤是個做NLP的小可愛。
雖然懂點DL框架層知識,懂點CUDA和底層,但是我是做算法的哎,平時debug很少會遇到深度學習框架層的bug(上一次還是三年前被pytorch坑),更從沒遇到過CUDA層甚至硬件層的bug。直到有一天....
這個bug徹底顛覆了我的debug思路,從這個bug開始,我再也不認為做算法的就做好算法就好了。
?
事故現場還原
當時的背景是這樣的,我平時在一臺8卡的GPU服務器上debug代碼,這臺服務器很少會把8卡全用滿,畢竟這臺機器只是用來debug代碼而已,要跑那種好幾天的8卡訓練任務的話肯定就提交集群了,否則你如果占了全部8張卡,那自己和別人都沒法debug了。
當時恰逢deadline扎堆,0-5卡都被其他蹭機器的小伙伴占了,只有6、7卡能用。于是我就用這兩張卡來debug代碼,訓練和預測都是多卡并行,于是debug的時候這兩張是同時用的。
結果把新的idea實現后,跑了一下,發現一個step都沒有跑完就掛掉了,而且全部的報錯信息就兩個單詞:
$ bash run.sh segmentation fault???我寫的都是python代碼呀,我不是在做NLP么,怎么跑出來段錯誤segmentation fault了??眾所周知,段錯誤是寫C++代碼時非常讓人頭疼的錯誤
隨手一搜
果然,自己攤上大麻煩了╮(╯▽╰)╭
雖然,連掛掉時的代碼出錯位置都沒有打印;雖然,煉丹以來第一次遇到這個報錯。但!是!依然難不倒見過大風大浪的本寶寶的!
進一步加關鍵詞限制來強行Google了一下,發現可能的錯誤原因依然太多了,各種類型的代碼里都可能遇到。算了算了,這次就不Google oriented debug了,是時候展示真正的技術了!
首先,先看看是計算圖compiled之前還是過程中還是runtime掛的。
【此處省略一頓怒插數十斷點的操作】
于是一路debug發現計算圖可以順利建立,但是在深度學習框架完成計算圖編譯,開始runtime計算的時候,就掛了。emmmm,這說明。。。
果然是最糟糕的情況哇!!
基于靜態圖的DL框架就怕在runtime出問題,很多小伙伴可能不清楚runtime該怎么debug,于是紛紛覺得pytorch真香。其實對于絕大部分錯誤,靜態圖還是蠻好debug的,秘訣也很簡單,那就是往圖里插入debug op。
像tensorflow、paddlepaddle這種支持靜態圖的深度學習框架,都是有大量debug op的,最常用的就是print op(tensorflow中的tf.Print/tf.print,paddle中的layers.Print),它可以在計算圖的任意位置打印出運行到此位置時的任意Variable/Tensor的runtime值,當然也可以搭配shape op(TF中的tf.shape,paddle中的layers.shape)打印出Tensor的runtime形狀等信息。除了print op外,還有assert op、is op等保證運行正確性、輔助debug的op,也就是說,python中常用來debug的一些關鍵字和函數調用,其實成熟的靜態圖框架里基本都能找到對應的op。熟悉了映射關系后,靜態圖debug起來也不會太費力。
但!是!有兩種情況依然比較頭疼,一種是計算圖已經完全建立了,但是第一個op還沒有跑到的時候就掛了(這時候插入的debug op完全沒被run到);還有一種是前向正確的跑完了,但是計算梯度的時候掛掉了(這時候錯誤可能發生在整張圖的任意位置,甚至不在圖里)。當它們再疊加上多機多卡問題時,就是最慘的情況。
不幸的是,我遇到了(。 ?︿ ?。)
首先,如前所述,本寶寶很淡定的在計算圖里插入了一堆Print結點,然后發現前向計算結果貌似非常正確,輕松的定位到錯誤發生在runtime計算反向梯度的時候,也就是
optimizer.optimize()算了算了,也不是第一次掛在這兒,報錯信息似乎也提供不了太多指導(報錯信息里有一些路徑類信息,比較敏感就不貼出來了)。再多插入幾個debug op來驗證前向路徑的正確性
【此處省略另一頓插op的操作】
結果,萬萬沒想到,插入debug op之后,竟然第一個step跑完了,這次掛在了第二個step!
???
debug op還有強行續命的功效???
我!不!信!于是把debug op又都注釋掉了,重新run!
結果,
又掛在了第一個step!!!
我當時狠掐了自己一把,這特喵的一定是在做一個很荒唐的夢!
令我沒想到的是,現實果然比夢境更荒唐。我又重復的run了好多好多次,發現沒有插入debug op的時候確實永遠是第一個step就會掛掉。于是我把這個震驚的現象告訴了大家,然后果然,大家都覺得我瘋了。
小夕:”你們都過來!我來親自演示一遍!“
小夕:”你們看!沒有插入debug op的時候,第一個step就報了segment fault的錯誤叭~“
大家:”哦“
小夕:”然后看好哦,我把這里的debug op都插上,然后,run!“
誒誒誒???怎么還是第一個step就掛了,被瘋狂打臉(′Д` )
大家:小夕你要是累了就先去睡會兒,別太累了,大家散了散了╮(╯▽╰)╭
嚶嚶嚶怎么會(。 ?︿ ?。)
算了算了,不糾結這個了。回到debug本身,emmm,話說前向能正常跑完,永遠都是掛在反向階段,那么說明要么前向哪里埋了一個坑暫時被強行計算了,但是這個節點處的梯度其實是無法計算的;要么可能是優化器本身就有bug。
于是先掃了一遍代碼,確實沒有使用也沒有cast很奇怪的數據類型,也沒有使用一些很奇怪的op,基本可以排除前一種情況。那應該就是后一種了!而優化器用的其實就是adam,沒什么改動,那問題就肯定出在自己新加的多卡梯度合并這塊了!
于是果斷的在6卡上跑了一下單卡的代碼,果然正常訓練!我真是福爾摩夕呀,馬上就要揪出bug真兇了好激動。
繼續!回去check了一下多卡邏輯,燃鵝似乎。。沒問題鴨?天吶,難不成這回要去框架源碼里插斷點了?
看來,我只能祭出我的殺手锏了,那就是
滾!去!寫!demo!
由于代碼邏輯有點復雜,并且會在多卡梯度合并的時候要進行一些額外的處理,當時覺得應該還是代碼哪里寫的有問題,于是決定先開ipython寫個多卡計算的小demo。
【此處省略一個demo】
嗯,前向計算順利通過,符合預期。好,加大難度,引入梯度合并。
結果,
又又又segmentation fault 了!!!我還沒引入我的idea呢,直接就掛了。
不對哇!!這不就是多卡訓練的標準玩法么,難道最新版的框架有bug???天吶我發現了這么大的一個bug!于是卸掉重裝了一個舊版的非常穩定的包,重新跑這個demo代碼
結果,又又又又 segmentation fault 了!!!
不會吧。。。又check了一下環境,確保CPU、內存、磁盤、GPU及其顯存都是夠用的,CUDA、CUDNN、NCCL也沒問題(自帶的tool都能check過)。難不成是python的鍋?不至于吧,我也太多疑了。。。
這時我出現了一個大膽的想法。
難不成這兩張顯卡里,有一張是壞的??
前面測試CUDA時剛在GPU6上跑了一把,發現好好的。那么。。。GPU7!狼人肯定是你了!!
于是又去GPU7上跑,滿心等著它掛掉,結果。。
訓練一切正常
強迫自己冷靜了一下。我是不是太多疑了,竟然都懷疑到硬件身上了。但是,基本的運行環境都check過了鴨,如果不是硬件,也不是python和框架的問題,那難不成。。。是中間的glibc的問題???聯想到segment fault這個報錯信息,更加確信了!glibc就是狼人,這次跑不掉了!
然而眾所周知,glibc這種級別的庫,是非常底層的,一旦改壞了,會導致ls、cd這種級別的命令都掛掉。我看了一眼GPU0-5卡上跑的好好的別人的任務,再次陷入了迷茫。。。
【此處省略長達數十分鐘的卡頓】
嗯,先低調一些,先把demo放到一個嶄新的環境中跑下!于是把demo放到另一臺機器上跑了一下,果然,多卡也是能正常跑的。莫非真是glibc?但是為什么別人的任務沒問題呢。似乎一切現象都在導向最后一種可能
GPU6到GPU7的底層通信鏈路出故障了!
于是我耐心的等著其他小伙伴的任務跑完,重新測試!
GPU5+GPU6:訓練正常
GPU5+GPU7:訓練失敗
GPU0+GPU1+GPU2+GPU3+GPU4+GPU5+GPU6:訓練正常
GPU0+GPU1+GPU2+GPU3+GPU4+GPU5+GPU6+GPU7:訓練失敗
實錘!!!激動萬分的跑去找管機器的小哥哥了,語無倫次的給解釋了大半天。終于,小哥哥將信將疑的安排人去進一步測試了一下
焦急的等了若干天。。。
小哥哥:“是的,GPU7壞掉了,無法與其他卡通信,更換GPU7后測試正常“
聽到這個消息后,小夕非常激動的重新測了一遍各種跟GPU7的排列組合,成功!!
?
后記
這時候我突然想起來之前那個見鬼的插debug op會導致多跑一個step的現象,而且僅出現了一次,于是跑去問小哥哥,小哥哥悠悠的說
“那大概就叫做回光返照吧,GPU7盡力了“
可
能
喜
歡
跨平臺NLP/ML文章索引
訓練太慢,GPU利用率上不去?快來試試別人家的tricks
如何優雅的追到女神夕小瑤
他和她,一個兩年前的故事
求關注 求投喂 拉你進高端群哦~
總結
以上是生活随笔為你收集整理的万万没想到,我的炼丹炉玩坏了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有福利! 好书推荐:从《实用推荐系统》学
- 下一篇: HuggingFace又出炼丹神器!稀疏