赞
踩
快半个月前遇到的问题了,还是记录一下
在用LG训练的时候,train_batch_size=8,eval_batch_size=4。按道理这种情况下GPU占用峰值停留在train, 而eval的时候应该很低才对,因为没有计算图的accumulate,GPU上只包括model和data。
但是问题就是出现的那么惊悚,我在训练的时候老是炸显存,而且每次炸的时候都是在eval.后来继续调小train和eval的batch_size,居然发现eval的时候显存占用居然是train的2倍左右,而test的时候则是3、4倍.
根据观察,很明显,eval和test的时候,网络仍然在积累计算图,因此eval的时候由于train的计算图还在,所以大概会有两倍的消耗。
google上到处查阅,发现出现这种问题原因是没有加上torch.no_grad()
,但是自己的代码从始至终都没有漏掉no_grad(),后面还在各个和eval有关的function外面包上no_grad。可谓是里里外外包了好几层no_grad,但是问题依旧…
终于在一个forum找到了问题的原因:
原来是因为自己的代码写的所谓的"太规整了",我充分地运用面向对象思维,将一个model封装一层又一层,因为考虑到eval和train都需要forward model,所以把forward和compute loss等,那些共同的部分全部抽象出来,到一个父类里面,结果就和上面说的那样,still return loss,也就是计算loss的那个fuc return出来,一层层返回到最外层,然后return了好几层之后才loss.backward()
而出现这个问题的原因就和python本身的生态有关系了,因为python的每个function会保留里面的variable.所以要是把loss写在另一个function里面,通过return调用返回,而trian和eval都是共用一块代码的,那么eval的时候,train留下的reference还会保留,而eval的时候no_grad会失效,不管你包几层都没用,因为你计算loss,to.device()等和GPU有关的操作不在一片代码块。
所以说,这塔玛不是吃力不讨好,一顿操作秀翻自己???
由于自己是SE出身,现在本科阶段学了不少OOP思想,啥封装隐藏,共用抽象,代码重用、复用,以及大量的文档注释这些工程化思维。可以略微看一下我的代码:
这还是我后面改过的,最原始的版本那真的是工整他ma给工整开门——工整到了家
啊!注释写的比代码多,你敢信???
要问,这些工程化的编码习惯好不好呢?
答案是,当然好了,应该说是非常好! 本科阶段就养成那么好的编码习惯以后进工业可以少吃很多无知亏。
但是——没必要,我说的没必要是指现在没必要大花心思取保证自己的代码工程化程度,因为说实在的,在整个project代码量本来就称不上一个工程的时候,费尽心思,吹毛求疵地用工程化思想框住自己的代码,那就是个错误,或者说纯属吃饱了撑。
SE这个课程本身传授的就不是啥算法啊,啥很具体技术,它传授的是前人的经验,是血泪,是前人在面对巨大代码量软件开发过程中,因为缺乏工程化思想严格把控,而流下的血泪!是几十个、几百个人的大团队,在真刀真枪开发软件的时候才能派的上用场的!
我这么个半吊子三脚猫的丹童,就跑点experiment,先不说代码量算不算的上一个工程(虽然我觉得我这个exp 用python写了上千行了,应该快算得上了),关键是这代码本来就不是用在工程上的。就好比,在练习九阳神功的时候,挥刀自宫了。这拟码真没必要啊…
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。