最近在调一个nlp分类模型,数据量不大(大概2万条),单卡3090训练。试了torch.compile,用默认模式跑,第一轮确实慢(编译开销),但后面几个epoch速度只提升了10%左右,和官方宣传的“训练提速30%-50%”差很多。而且用动态shape(比如变长序列padding到不同长度)直接报错,回退到eager模式。想问问大家:是不是小模型小数据量根本没必要上compile?还是我哪里设置不对?有一说一,模型里用了transformers的BertForSequenceClassification,加了个自定义分类头。有没有类似场景的老哥分享下实际收益?现在纠结要不要为了部署时的推理加速去折腾这个。
PyTorch 2.0的compile到底值不值得用?静态图加速不明显还报错
全部回复
共 88 条你这数据量太小了,编译开销都不一定回本,而且动态shape目前确实坑多,建议直接eager跑省心。
说实话transformers的模型优化空间有限,除非上大batch或者推理部署,否则别折腾compile了。
说实话你这场景我试过类似的,2万条数据+小模型确实吃不到compile红利,它那30%-50%的提速基本得靠大batch和密集算力堆出来。动态shape报错太正常了,torch.compile对变长序列的支持本来就半残,我后来直接固定max_len+padding,勉强能跑但提升也就10%出头。你不如把精力花在混合精度和gradient accumulation上,收益更实在。另外部署推理其实可以单独试试ONNX或者TensorRT,比硬啃compile省心多了。
小模型上compile确实收益有限,你这情况正常。动态shape是硬伤,建议直接关掉,省心。
这情况太真实了,小模型+小数据量确实很难吃到compile的红利,官方benchmark基本都是大模型+固定shape堆出来的。你那个10%的提速我觉得已经算正常了,动态padding这块本来就是compile的硬伤,建议要么直接放弃自适应,要么把序列长度统一成固定值试试。另外你可以看看是不是自定义分类头里有些操作没被graph捕获,有时候把forward里那些tensor操作简化一下反而效果更好。部署推理的话其实不用太纠结,torchscript或者onnx说不定比compile更省心。
说实话你这个情况我太熟了,之前用bert做序列标注也踩过同样的坑。torch.compile对动态shape的支持确实是个硬伤,尤其是padding长度不固定的时候,它得反复recompile,那点性能提升全被编译开销吃回去了,甚至可能更慢。小数据量下这玩意儿基本就是个负优化,因为它的优化收益跟模型计算密度强相关,你2万条数据加上3090这种卡,eager模式早就把GPU喂饱了,静态图那点算子融合省下的时间根本看不出来。
我自己的经验是,如果实在想用,可以试试把padding固定到某个batch内的最大长度,或者干脆用max_length统一截断,这样shape就静态了,但代价是可能丢信息。至于训练提速,我怀疑官方那个30%-50%的benchmark用的是大模型大batch,比如LLM那种,小模型根本吃不到这个红利。
不过话说回来,如果你主要纠结的是部署时的推理加速,那torch.compile还是值得搞一下的,毕竟推理时shape固定,而且还能配合torchscript或者导出onnx,收益比训练阶段明显得多。但要是只为了训练,建议直接关了,省心。你那个自定义分类头可能也有影响,可以试试把bert部分和head分开,只compilebert那一段,有时候能避开一些奇怪的报错。
另外提醒一句,transformers库的模型有些ops本身就不支持inductor,你可以看下torch._dynamo的日志,它会告诉你哪些算子fallback了,说不定你那个10%的提升就是部分算子没编译导致的。我后来换了torch 2.1版本,有些问题倒是自己解决了,你也可以升个级试试。
说实话你这个场景我太熟了,之前用electra做相似度任务也是2万条左右,上compile之后收益跟你差不多,甚至有时候还负优化。我觉得torch.compile对带自定义分类头的bert这种小模型确实不友好,主要瓶颈在transformer那块已经够优化了,图编译能省的时间基本被attention和ffn的kernel launch开销吃掉,你那个10%的提速很可能就是self-attention里某些小算子合并带来的。动态shape那个报错我只能说习惯就好,torch.compile对padding长度变化特别敏感,哪怕你用了padding_same或者mask,它只要检测到tensor shape变了就回退,这个机制在2.0里还挺保守的。要是想优化训练,我建议你先试试torch.compile的mode="reduce-overhead"配合static_shape=True,然后自己把序列长度固定到某个阈值(比如128),这样可能能压到15-20%的提速,但代价是显存和计算量上去了。至于部署推理,如果你用的是onnxruntime或者tensorrt,那compile基本没用,不如直接导出onnx再转trt,那个加速比能到2-3倍,而且动态shape支持得更好。小模型小数据量确实没必要为了训练那点提速折腾,但你要是以后要上大模型或者长序列,compile的价值才体现出来。
这题我熟,小模型上compile基本就图一乐,动态shape直接劝退,等数据量上去了再折腾吧。
同款场景,2万条数据上compile纯属给自己找事,我试过提升也就在5%-10%浮动,编译时间都够跑两轮epoch了。动态shape这坑太真实了,padding长度一变就炸,最后还得靠固定max_len或者干脆放弃compile。小模型收益确实有限,官方那个30%-50%八成是拿大模型和静态shape的benchmark吹的。你要是真在意推理速度,不如直接上onnxruntime或者TensorRT,比折腾compile省心多了。
小模型上compile收益确实有限,动态shape踩坑太真实了,你这场景不如直接eager跑省心。
建议部署推理再单独试torch.compile,训练阶段别折腾了。
小模型真没必要折腾compile,收益都在大模型和大batch上,你这情况eager跑就挺香。
我试过动态padding,compile必炸,后来直接固定长度padding,速度也就快了15%左右,不值得。
说实话你这个场景我试过类似的,2万条数据上compile确实有点鸡肋,编译那点开销摊薄下来收益就不明显了,官方那30%-50%多半是拿大模型大batch刷出来的。动态shape报错太正常了,torch.compile对变长序列支持本来就没完全跟上,我后来干脆用固定长度padding加attention mask,倒是能跑通但提升也就那样。你要是为了部署提速,不如直接上ONNX或者TensorRT,那个收益比compile实在多了,训练阶段真没必要折腾。
小模型真没必要折腾compile,我试过类似规模的,收益还不如多调两轮学习率省心。
跟你差不多情况,小模型上compile纯属给自己找事,收益低还折腾,直接eager跑就完事了。
动态shape别想了,compile对这块支持就是拉胯,等后面版本优化再说吧。
说实话你这个场景我试过类似的,2万条数据+bert这种规模,compile的编译开销占比太高了,收益自然被摊薄,官方那个30%-50%估计是大模型大batch才跑得出来。动态shape报错太正常了,torch.compile对变长序列支持一直很拉胯,我后来直接固定max_len+padding到统一长度才勉强跑通。小模型真没必要折腾,部署推理的话建议直接转ONNX或者TensorRT,收益比compile稳多了,还不用跟一堆编译报错斗智斗勇。
小模型确实没必要折腾compile,收益全被编译开销吃掉了,动态shape直接劝退。我试过类似场景,基本都回退eager,省心。
你这配置上compile纯属自找麻烦,3090跑2万条数据根本不吃性能,不如把时间花在调参上。
你这情况跟我之前跑bert-base微调几乎一模一样,compile收益主要吃模型计算量和batch size,2万条数据单卡3090确实不太能体现出来。动态shape报错太正常了,我后来干脆固定max length+padding,速度也就勉强到15%左右。要是部署时追求低延迟,可以单独把推理部分用onnx或者TensorRT试试,训练阶段真没必要折腾compile。
你这情况太正常了,小模型加动态shape基本就是compile的劝退组合。我试过类似任务,2万条数据量编译开销摊不平,提速幅度小是必然的,官方那30%-50%多半是理想静态shape大模型场景。建议要么固定max_len做padding,要么干脆别用compile,把精力放在数据加载和混合精度上,收益可能更直接。另外你说的推理加速,其实可以单独用onnx或TensorRT,比硬磕torch.compile省心多了。
同款transformers+BertForSequenceClassification,我这边数据量比你还小,1万条左右,compile开默认模式基本就是负优化,那10%的提升大概率还是运气好碰上了显存分配优化。你动态shape报错太正常了,torch.compile对变长序列的支持本来就是玄学,官方那30%-50%的benchmark基本都是固定shape+大batch+CNN或者LLM那种计算密集型架构跑出来的,小模型吃不到这个红利。我个人建议训练阶段直接关掉compile,省心比那点速度重要,真要用就等模型定下来之后在推理阶段试,而且推理时最好把padding统一到固定长度,别用动态shape。另外你自定义分类头如果是纯MLP还好,要是带了什么mask或者条件分支,compile大概率也是白搭。顺便问下你试过设置mode="max-autotune"吗?虽然编译时间长到离谱,但说不定小模型反而能榨出点性能,我懒得试了,你要是试了可以反馈下。
说实话你这个场景我太熟了,之前用electra做文本分类也踩过一模一样的坑。compile对动态shape的支持确实拉胯,尤其是transformer里那些attention mask和padding带来的shape变化,动不动就graph break然后退回eager,速度不升反降很正常。我觉得你数据量2万条、单卡3090这个规模,训练瓶颈根本不在计算图上,反而在dataloader和CPU预处理上,我建议你先profile一下看看GPU利用率是不是真跑满了。另外官方那个30%-50%的benchmark基本都是大模型、固定shape、多卡或者专门调过compile配置的,小模型小batch真没必要追求这个。如果你之后要部署推理,可以考虑用ONNX或者TensorRT,那个收益比compile实在得多,而且对变长序列的处理也更成熟。不过如果你还是想试试compile,可以给bert的forward函数里把padding相关的操作尽量提取到外面,或者用mark_dynamic标记动态维度,有时候能救回来一点。反正我个人建议是训练阶段先别折腾这个,把精力放在调参和数据处理上更实际。
同款bert分类,2万条数据的话提升10%真不算翻车,官方那个30%是拿大模型大batch堆出来的,你这个规模CPU预处理反而成瓶颈了。动态shape报错太正常了,compile默认对变长序列支持就很烂,建议固定max_len加mask,能省心不少。另外试试mode=reduce-overhead或者把classifier头单独拎出来别编译,我上次这么搞快了15%左右。部署推理的话其实不用太纠结compile,直接上ONNX或者TensorRT收益更明显。