All in Linux:一个算法工程师的IDE断奶之路
一只小狐貍帶你解鎖 煉丹術&NLP?秘籍
在合格的煉丹師面前,python可能被各種嫌棄
前不久賣萌屋的lulu寫了一篇vim的分享《算法工程師的效率神器——vim篇》,突然想起來自己也有一篇攢了幾年灰的稿子,在小伙伴的慫恿下跟小夕強行翻新了一下,于是有了本文。
已經習慣了IDE的小伙伴可能在煉丹時也慣性的使用pycharm了。這在windows、mac本地煉丹的時候往往沒什么,但是一旦要在GPU server端,初學者就往往開始捉急了,更不必說讓他all in這個server端的小黑框了。
小夕早期也試過一些笨辦法和部分場景下有效的“巧辦法”,比如
本地端用pycharm瘋狂debug,一切測試ready后,使用scp將相關代碼傳到服務器起任務
通過syncthing這種同步工具來自動同步本地端和server端代碼
利用pycharm內置的遠端同步功能
server端啟動jupyter notebook,本地端瀏覽器訪問
直到遇到了公司跳板機:scp、pycharm、jupyter notebook等通通失聯,本地端與遠程GPU服務器被跳板機生生拆散了。于是我問同組的小伙伴這該怎么煉丹,ta說:
“咦?直接在服務器上寫就好了呀,linux不香么,vim不香么?難道你不會?”
“我。。我會。。。一點╮( ̄▽ ̄"")╭”
回想自己的煉丹之路,其實完全斷奶IDE確實不是一件容易的事情。早期一直是用pycharm連接遠程GPU服務器來湊合,一直覺得效率還行。直到后來投身工業界后,發現身邊的大佬都是在黑框框里裸奔,煉丹效率完全秒殺我,這才下決心徹底斷奶。掙扎幾個月斷奶成功后,愈發覺得
黑框框真香╮(╯▽╰)╭
不過,畢竟自己不是專業玩linux的,只是煉丹之余摸索了一些自己用著舒服的不成體系的方法,所以相關玩法可能會有些“歪門邪道”,如有更優做法,請大佬們在評論區不吝賜教噢~
本文目錄:
準備工作與環境遷移問題
與機器解耦
別忘寫個自動配置腳本
bash function用起來
與bettertouchtool的組合技
別再無腦vim了
別再無腦python了
(其實還能寫更多,太困了,還是下次吧QAQ
準備工作與環境遷移問題
其實,如果只是湊合一下,那么在黑框里煉丹基本不需要什么準備工作。換到新的服務器之后,基本就是vimrc里隨手寫幾條tab擴展、換行自動縮進、文件編碼之類的,bash里隨手建幾條alias就可以了,剩下就是一頓pip install。
不過,如果你是一個比較深度的煉丹師,往往python環境里存了一些自己得心應手的小工具包,還有一些自己臨時hack了源碼的庫;可能還要隨身攜帶hadoop客戶端以便在集群上煉丹和管理實驗;還可能對cuda版本、cudnn版本也有一些要求,需要隨身攜帶一些常用版本在服務器之間穿梭;此外,更不用說vim和bash的一堆個性化配置啦。
這就導致了一個問題,如果煉丹準備階段,絲毫不顧及煉丹環境與機器的解耦和環境的遷移性,那么一旦機器掛掉,或者你要換機器(老板給你買了新機器),這就會變得非常痛苦。所以在打磨自己的工具鏈之后,花點時間將工具與環境解耦是非常必要的。
與機器解耦
比較欣慰的是,煉丹依賴的大部分工具和庫本身就是跟機器解耦的,比如cuda、cudnn、hadoop client等,挪到新環境后,基本只需要把各bin的路徑配置到環境變量PATH里,把各lib配置到LD_LIBRARY_PATH里就可以用了,最多再export一下CUDA_HOME,HADOOP_HOME等。然而,python環境的遷移就相對麻煩一些了。
早期的時候,小夕在本地搞了一堆miniconda的環境,然而換機器的時候才發現遷移時這些環境非常容易損壞,或者遷移后發現有時出現奇奇怪怪的問題。當時一頓亂試,也沒能很完美的解決,不知道今天miniconda或anaconda是否有改善(本地端的體驗真的無敵贊)。
此外,也嘗試過用docker管理環境。雖然docker的遷移性很贊,自帶云端同步,但是,當你面對一臺無法連接外網且沒有安裝docker的服務器時,你就感覺到絕望了。。。一頓離線安裝折騰到瘋,機器環境差點時,各種安裝依賴會搞到懷疑人生。此外,docker也有一定的上手成本,理解概念、熟悉常用命令和提高生產力的命令方面會比miniconda明顯成本高。
最終發現,python環境的遷移性方面還是pyenv+virtualenv的組合能應對的情況更多一些,使用virtualenv建好一個環境后,若要將這個環境遷移到其他機器上,分兩種情況:
如果你沒有修改某些庫的源碼,所有的庫都是pip install的,那自然直接pip freeze一下,將當前環境里的各個庫和版本號直接輸出重定向到命名為requirements.txt文件中就可以啦。到新的機器的新環境里,直接pip install -r requirements.txt就哦了。
然而,如果你已經深度定制了某些庫,這就意味著你需要把整個離線環境打包帶走了。這時首先要保證目標機器跟現有機器的大環境差不多(否則一些系統底層庫版本差異很大,可能導致上層的庫調用出錯),然后借助virtualenv自帶的命令就非常容易了,直接一行
virtualenv -relocatable就可以將環境里絕對路徑改成相對路徑,從而完成跟當前機器的解耦。
不過親測對有的包解耦不徹底,尤其是ipython這種有bin文件的。這就需要手動的將環境里bin目錄下的這些bin文件首行的解釋器路徑改成相對的(比如直接/bin/env python)。最后就是改改bin目錄下的activate系列文件中的絕對路徑啦。至此就可以放心的帶著小環境穿梭在各大機器了,且無需考慮目標機器是不是離線機器。
別忘寫個自動配置腳本
上一節將python環境,cuda、cudnn、nccl、hadoop等與機器解耦后,就可以再帶上vimrc,bashrc,?inputrc等文件一起跑路了。這時候就發現bash script真香了,把你的配置流程直接寫進腳本里,這樣到了新環境直接run一下這個腳本,你的全套家當就搬過來啦。
懶人的alias
說完了環境準備和搬家的事情,然后就可以開始談談怎么偷懶了。
雖然精力旺盛的時候,多敲幾個字母也沒什么,燃鵝,當你深夜面對實驗結果甚為沮喪時,你就會發現敲個nvidia-smi都是那么無力。所以,干脆就提前把懶提前偷一下。
貼一個自己的常用懶人alias,可以考慮寫入到你的~/.bashrc
function enshort() {alias l='ls -a'alias d='du -h --max-depth 1' alias n='nvidia-smi'alias vb='vi ~/.bashrc'alias sb='. ~/.bashrc'alias sc='screen'alias nvfind='fuser -v /dev/nvidia*'alias nving='watch -n 0.1 nvidia-smi' }不過需要強調一下,如果多人共用機器,記得像上面一樣把你的私人alias封到函數里,以免與他人命令沖突,造成不便。為了進一步偷個懶,你可以把這個函數的啟動封裝在virtualenv的啟動腳本里,或者在外面再封一層更高level的環境啟動函數。這樣就避免了每次登陸都要手動啟動一堆函數的麻煩。
bash function用起來
雖然對算法工程師來說,bash一般用來寫一些非常高level的流程,大多數只是順序執行一堆命令外加寫幾個分支,不過,巧用bash中的function來封裝一些高頻的操作往往也可以大大提升日常煉丹效率。
例如,平時煉丹實驗掛一堆之后,我們可能時不時想看看某個實驗的cpu利用率,內存占用等,每次用ps一頓操作都要敲不少東西,于是機智的小夕還是選擇封裝一個小function
# Usage: psfind <keywords> (RegExp supported) function psfind() {ps aux | head -n 1ps aux | grep -E $1 | grep -v grep }這個函數既支持psfind <進程號>,也同樣支持匹配進程中的關鍵詞,且支持正則表達式來支持復雜查找。最主要還是格式化了一下輸出,這樣打印出來會看起來很清晰。
覺得好用的話歡迎拿走(記得留個贊
與bettertouchtool的組合技
提高linux環境中的煉丹效率,除了上述這種在.bashrc中作文章,mac用戶還可以通過一個眾所周知的神器APPbettertouchtool來組合提升日常效率。大部分mac用戶可能只是用它來強化觸控板手勢了,其實它的鍵位映射也是炒雞無敵有用的鴨~
例如,眾所周知,在命令行里或者vim的插入模式下,我們可以通過ctrl+w來向前刪除一個單詞,這個命令雖然無敵有用,但是早已習慣了ctrl/option/cmd+delete的我們還是會覺得這個設定比較讓人精分,導致影響了它的實用性。不過,用btt完全可以把這個ctrl+w這個鍵位映射到我們習慣的option+delete來刪除一個單詞(mac系統中大都支持opt+del刪除一個單詞的設定),如下圖
當然了,如果你用的不是mac自帶的終端,而是iterm等第三方的話,也可以考慮通過內置的鍵位映射功能來完全這個操作~或者,就適應一下ctrl+w的設定啦╮( ̄▽ ̄"")╭
別再無腦vim了
linux新手在去查看一個(文本)數據集或日志文件時,基本就是直接用vim打開后開始上下左右的看。其實,對NLPer來說,相當多的場景是無需反復去vim的,有時反而會更加問題的復雜度,或閱讀體驗更糟。
先說說最簡單的,關于隨便瞥一眼數據集的開頭。
當數據集比較大時,哪怕是用sublime、vim這種高性能、輕量級文本編輯器打開,都要等待很久很久(雖然vim可以ctrl+c打斷),超大單文件數據集更是噩夢了。其實只是瞥一眼的話,完全可以使用head命令來快速取出一個文件的前多少行,比如
head -100 train.csv就可以直接print出來train.csv這個文件的前100行數據,完全沒必要把整個文件都load進內存里。
此外,有的文件內容可能是實時變化的(比如模型訓練時的訓練日志文件),如果希望持續track一個文件的內容,可以通過tail命令:
tail -f log如果不僅要瞥一眼,而且還想查看或獲取到文件中某些特定的行。
有的小伙伴就覺得必須要叫vim爸爸來幫忙了,甚至要求助python。其實這種高頻場景也完全沒有必要的,直接用grep就能找出你感興趣文件中的那些你感興趣的模式。
這方面尤其在處理訓練/eval日志時非常高效
例如,這么一份訓練日志
我們eval了幾十上百個ckpt,每次eval都往里面打了一堆日志,但是我們希望只看一下各個step的dev evaluation的結果,那么借助管道和grep可以非常輕松的完成
cat eval.log | grep '\[dev'而grep的想象力也絕不是限制在管道里面,它完全可以單獨使用,直接去從若干你感興趣的文件中尋找特定的模式(比如./*/*就把所有子目錄也一起遍歷了),在你批量管理、分析實驗日志時,用熟之后堪稱神器。
當然了,進階一點,用好awk或sed后,能擺脫vim和python的情況就更多了,尤其是在快速編輯方面。不過,這塊一寫可能又停不下來QAQ算了算了,下次再寫吧(能湊夠一篇文章的話)
別再無腦python了
如上節所述,使用grep,?awk等linux的一些神器命令可以在很多高頻場景下替代文本編輯器,大大提升煉丹效率。其實在coding層面上,也是如此。
例如,如果我們想把上一節末尾的圖片中的日志,按照step從小到大排列后展示出來,怎么做呢?
可能又有小伙伴要說上python了。殊不知linux中內置了很多方便易用的命令,包括排序sort命令,去重uniq命令等,因此,這個排序的需求,在上一節末尾的grep命令處通過管道連接一個sort就直接完成了
cat eval.log | grep '\[dev' | sort在數據集處理方面,還有一個NLPer必知的命令,就是cut啦。這個命令堪稱是處理csv/tsv格式文件的神器了,當你需要從CSV數據集文件中提取出某些列時,可以直接幫你免去開python的麻煩
例如,如下眾所周知的Quora Question Pairs(QQP)數據集:
從第一行可以看到這個數據集包含6列:id ?qid1 ? ?qid2 ? ?question1 ? question2 ? is_duplicate
但實際上我們單純訓練一個q-q文本匹配模型的話,只需要最后三列就夠了。不懂linux的小伙伴可能會用python打開文件,然后一頓for循環和str.split了。
但實際上,用上cut之后配合管道只需要一行
cat train.tsv | cut -f 4,5,6 > train.tsv.cut就把數據集的第4,5,6列提出來了,并且存儲在了train.tsv.cut文件中。如果需要的數據來自于兩個文件,可以分別cut出來再將這些列拼到一起(新文件是m+n列,可以理解成cat是縱向連接兩個或若干個文件,而paste則是橫向連接):
paste file1.txt file2.txt最后,訓練數據的去重和shuffle,也完全可以不用python去寫,分別用uniq和shuf結合管道就能一行命令搞定:
sort train.tsv.cut | uniq | shuf > train.tsv總之,類似于cut,wc(統計詞數、行數的神器)的這種linux中的小命令很多,很大程度上幫我們解決了一些簡單高頻的數據集處理的需求(尤其CSV),避免了無腦使用python導致的低效時間開銷。
另外,不僅簡單場景可以直接用linux命令組合替代python,一些高端場景下linux命令與python的配合更是容易讓復雜的事情變得非常簡單。一個很常見的就是對大型數據集的單機并行處理:
&+wait:最簡單的單機并行數據預處理
當數據集比較大時,我們要跑分詞、shuffle、抽文本特征之類的可能會非常非常慢(千萬級以上就明顯感覺不行了,過億之后要命了),如果強行把這些邏輯都丟到訓練里面,又可能明顯拖慢訓練速度(雖然可以通過異步數據讀取來緩解下),且極大的增加了訓練階段的CPU資源消耗。然而,如果我們用單核來跑全量數據,這個核用上洪荒之力可能也要跑一晚上(處理閱讀理解這種數據的會就更久了)。要迭代預處理策略就會變得非常困難,怎么辦呢?
用python手擼多線性代碼?確實可以,但我嫌麻煩╮(╯▽╰)╭
最簡單的做法就是用類似map reduce的范式來寫,讓python腳本僅僅去處理一個“單位”的數據,這里的單位可以是一個文本行,也可以是一個數據集的文件part。
例如,我們一個CSV文件里有1億條樣本,那么我們可以先用split命令把它切成100個part(先用wc -l確定行數),然后并行處理后再merge起來,隨手寫下:
for i in {0..99}; dopython process.py train.tsv.part$i > train.tsv.part$i.tmp & done wait cat *.tmp > train.tsv.processed rm *part*就坐等小服務器上所有的CPU核心被打滿進行數據處理啦~
但!是!如果代碼bug了怎么辦?等所有進程跑完嗎?nonono,當然是一鍵kill啦~這里一種是偷懶的辦法,直接用pkill -f <xxx>來殺死所有包含某個名字的進程
pkill -f 'process.py'不過這樣可能存在誤殺的情況,需要注意名字不要取得太過于危險╮( ̄▽ ̄"")╭
此外,由于深度學習框架和一些庫的原因,或者我們不小心打印了過量的內容到屏幕上,這時候也容易出現進程退不出來的情況(很多小伙伴選擇了斷開ssh鏈接),這時候可以使用ctrl+z將前臺進程強行丟到后臺并掛起,然后kill %N來殺掉它(這里的N一般是1,除非之前已經將一些前臺進程掛后臺了)
按下ctrl+z之后:
[1]+? Stopped ? ? ? ? ? ? ? ? top
[xixiaoyao@xxx.com emmm]$ kill %1
好困QAQ發現篇幅有點長了,還是寫到這里戛然而止吧。喜歡本文,期待后續煉丹技巧分享的小伙伴們記得持續關注小屋&星標小屋?哦。
夕小瑤的賣萌屋
_
關注&星標小夕,帶你解鎖AI秘籍
訂閱號主頁下方「撩一下」有驚喜哦
總結
以上是生活随笔為你收集整理的All in Linux:一个算法工程师的IDE断奶之路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中文BERT上分新技巧,多粒度信息来帮忙
- 下一篇: ELECTRA模型精讲