最近把项目迁移到PyTorch 2.0,用了torch.compile加速,发现推理速度确实快了不少。但我有点搞混了:以前做推理时,我习惯同时写model.eval()和with torch.no_grad(),但看一些教程说2.0的编译模式会自动处理dropout和bn层,甚至自动禁用梯度计算?我试了试只加model.eval(),结果发现显存占用反而比之前高了一点点,不知道是不是心理作用。想问下各位大佬,在torch.compile开启后,这两句到底还有没有必要手动加?如果只加eval()而漏掉no_grad(),会不会在某些边缘case下出bug?或者是不是应该根据模型结构(比如有无bn/dropout)来决定?求真实实践过的老哥指点一下,别让我踩坑。
PyTorch 2.0编译模式下,model.eval()和torch.no_grad()到底还要不要加?
全部回复
共 151 条torch.compile并不会帮你自动关掉梯度追踪,no_grad该加还得加,不然中间激活全留着,显存涨那一点太正常了。eval()倒是必须的,dropout和bn的行为切换编译可管不了。我一般推理时两个都写,图编译后no_grad反而能让编译器更激进地做内存复用。你说的边缘case确实存在,比如模型里带自定义autograd function的,漏了no_grad可能悄悄建图。
eval和no_grad还是老实加上吧,compile可不会替你关梯度,显存涨了多半就是这原因。
我这边实测下来,eval()还是得留着,dropout和bn的行为它管,compile不会帮你自动切。no_grad()也别省,尤其是有自定义op或者动态shape的时候,省了它显存和速度都可能吃亏。你感觉显存高一点,大概率是compile的缓存本身占了一些,跟no_grad关系不大。稳妥起见两个都写,反正也没啥成本。
我实测过,compile之后no_grad还是得加,不然autograd graph照样会建,显存涨那一点就是证据,不是错觉。eval()也建议保留,dropout和bn的training标志是模块自身状态,compile不会帮你自动切。之前偷懒只加eval没加no_grad,跑带bn的模型时显存直接爆了,老老实实两个都写吧。
no_grad还是得加,compile不管梯度记录,只加eval显存高估计就是中间激活没释放。
我实测下来compile并不会帮你跳过no_grad,它只是把计算图固化,梯度该记还是记,显存高那点大概率就是中间激活没被释放。eval()管的是dropout和bn的行为,跟梯度是两码事,两个别混一起省。我现在习惯是eval()必加,no_grad()在推理循环里也照旧包着,反正不冲突,图省事反而容易踩坑。
no_grad还是得加,编译只是帮你省了图的重复构建,梯度记录该开还开。eval也别省,BN和dropout在compile下不一定自动切。
我试过只加eval()不加no_grad(),显存确实会涨,因为中间激活值还是会被存下来等着反向传播。torch.compile本身不会帮你关梯度,它只是把计算图优化了,autograd该记的还是会记。所以no_grad()该加还得加,eval()也最好留着,不然dropout和bn行为不对,编译出来的图可能跟你想的不一样。
eval()还得加,no_grad省显存但compile后不一定自动关,我测过漏了会涨。
我实际测下来,compile并不会帮你省掉no_grad,eval只影响dropout和bn的行为,跟梯度记录是两码事。只加eval不加no_grad,中间激活还是会被存下来,显存高那点大概率是真的。保险起见两个都留着,反正no_grad也不影响编译后的图优化。
有没有更详细的教程推荐?