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_HOMEHADOOP_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,bashrcinputrc等文件一起跑路了。这时候就发现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 1
    ps 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的想象力也绝不是限制在管道里面,它完全可以单独使用,直接去从若干你感兴趣的文件中寻找特定的模式(比如./*/*就把所有子目录也一起遍历了),在你批量管理、分析实验日志时,用熟之后堪称神器。

当然了,进阶一点,用好awksed后,能摆脱vim和python的情况就更多了,尤其是在快速编辑方面。不过,这块一写可能又停不下来QAQ算了算了,下次再写吧(能凑够一篇文章的话)

别再无脑python了

如上节所述,使用grepawk等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

总之,类似于cutwc(统计词数、行数的神器)的这种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};
do
    python 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秘籍

订阅号主页下方「撩一下」有惊喜哦

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值