最近在调一个nlp分类模型,数据量不大(大概2万条),单卡3090训练。试了torch.compile,用默认模式跑,第一轮确实慢(编译开销),但后面几个epoch速度只提升了10%左右,和官方宣传的“训练提速30%-50%”差很多。而且用动态shape(比如变长序列padding到不同长度)直接报错,回退到eager模式。想问问大家:是不是小模型小数据量根本没必要上compile?还是我哪里设置不对?有一说一,模型里用了transformers的BertForSequenceClassification,加了个自定义分类头。有没有类似场景的老哥分享下实际收益?现在纠结要不要为了部署时的推理加速去折腾这个。
PyTorch 2.0的compile到底值不值得用?静态图加速不明显还报错
全部回复
共 88 条你这场景收益确实有限,数据量小编译开销都抵不回来,动态shape报错更是硬伤,建议直接放弃compile先保训练稳定。
我试过类似情况,小模型用eager模式反而省心,真要提速不如先优化数据加载和混合精度。
说实话你这个场景我太感同身受了吧,之前拿compile跑过DistilBERT的微调,也是2万条左右的数据,提升大概也就12%上下,跟你差不多,后来查了下发现小batch下CUDA graph的启动开销占比太高,而且transformers里那些动态padding和attention mask的复杂控制流,本身就很难让inductor生成高效融合内核。你说的动态shape报错我碰得更多,几乎每次eval阶段变长序列都得回退,后来干脆只在固定max_len的batch上试compile,稍微稳一点。我觉得官方宣传的数据大概率是拿那种大模型、大batch、固定shape的CV或者生成式任务测的,跟你这个nlp分类场景根本不是一回事。另外你那个自定义分类头如果里面有什么自定义loss或者tensor操作,也可能打断graph优化,建议先profile一下看是不是卡在数据加载或者GPU利用率上。部署推理的话其实可以单独用torchscript或者ONNX导出试试,未必非得跟compile绑定,毕竟训练阶段那点提升在3090上真不太值得折腾。倒是想问问你试过设置mode=“max-autotune”或者把dynamic参数调成True吗?虽然编译时间会长一点,但有时候小模型反而能榨出点收益。
小模型上compile收益确实有限,动态shape直接劝退,我试过几次也放弃了,部署推理还是用onnx稳一点。
说实话你这情况我太熟了,之前用bert做长文本分类也是2万出头样本,单卡跑起来compile的收益确实就那样。小模型在小batch下,图优化能抠出来的计算密度提升本来就有限,而且transformer的瓶颈很多在attention的访存和softmax上,compile对这些的优化空间不如大模型明显。动态shape报错这个太正常了,torch.compile对变长序列的支持一直很鸡肋,你就算把padding到固定长度也可能因为mask的shape变化触发重新编译,反而更慢。我建议你干脆别在训练阶段折腾compile了,把精力放在推理上,用onnxruntime或者torchscript做静态导出,配合半精度能稳定拉到2倍左右。另外你可以试试把自定义分类头改成更简单的线性层,很多时候瓶颈反而不在bert主干上。要是真想用compile,试试mode=“reduce-overhead”或者关掉dynamic=True,但别指望质变。反正我后来是彻底放弃训练阶段compile了,只留推理优化,省心多了。
说实话你这个情况我太熟了,之前用electra做文本分类也是2万条左右,3090单卡,torch.compile带来的收益基本就5%-8%,后来我干脆把编译关了。我觉得官方那个30%-50%的benchmark大概率是跑在那种大模型大batch、计算密集的场景下,像咱们这种小batch小模型的场景,瓶颈根本不在算子上,反而在数据加载和CPU侧的前处理。动态shape报错这个我也踩过,transformers的tokenizer输出attention_mask和input_ids长度不一致时,后端图优化直接崩,最后我是用固定长度padding到max_len才勉强能跑,但提速依然感人。你要真想省时间,不如把精力放在混合精度和gradient accumulation上,这两个收益比compile实在多了。至于部署推理,如果服务端没有强实时性要求,直接上onnxruntime或者TensorRT可能更靠谱,compile在推理场景的坑比训练还多。反正我现在的结论是,小数据量小模型阶段别折腾compile,等模型大到单卡显存吃紧、计算占比上去了再考虑也不迟。
跟你情况差不多,我拿BERT做序列标注,数据量也就三万条左右,3090单卡。试过compile,默认模式确实只有个位数到十几个点的提升,后来开了mode="max-autotune"稍微好点,但编译时间巨长,训练小模型感觉纯亏。动态shape这块我直接放弃,变长序列在compile下基本就是灾难,报错信息还晦涩,最后只能统一padding到固定长度才跑通,但这样又损失了效率,有点得不偿失。
我觉得官方那个30%-50%的加速大概率是拿大模型、大batch、静态shape测出来的,像咱们这种中小规模任务,瓶颈根本不在算子执行上,反而在数据加载和Python侧的开销,compile优化不到点子上。不过你说部署推理,这个倒是可以认真考虑下,我试过把训练好的模型在3090上做inference,用compile加reduce-overhead模式,单条样本延迟能降个20%左右,但前提是输入shape固定且batch不大,否则还是会回退。
你现在这个情况,如果训练时间还能接受,建议先别折腾compile,把精力放在数据加载和混合精度上,收益可能更明显。推理那边可以单独写个脚本测一下,看看固定shape下到底能快多少,再决定要不要上。另外查一下你自定义分类头里有没有什么动态操作,比如Python循环或者条件分支,这种很可能就是触发回退的元凶,把它改成静态张量操作试试。
说实话你这情况我也踩过坑,小模型加动态shape确实是compile的重灾区,收益主要在静态shape和大batch上,你这10%算正常水平。建议试试把padding固定到最长序列或者用max_length截断,让shape完全静态,可能提速能到20%左右,但别指望官方那个数字。另外transformers的模型有些算子本来就不支持inductor,报错回退太正常了,我后来干脆只在推理阶段用compile,训练还是eager省心。
跟你情况差不多,我之前跑过一个小规模的文本匹配模型,compile默认模式收益确实就10%出头,后来发现把mode换成max-autotune能到20%左右,但编译时间直接翻倍,小数据集上算总账不太划算。动态shape这块我直接放弃了,每次padding到固定长度反而省心,报错回退eager太折腾。你要是主要为了部署推理,不如直接上int8量化,那提升比compile实在多了。
你这场景收益确实有限,建议别折腾了,留着精力搞推理优化更实在。
小模型真没必要折腾compile,收益大头都在大模型和CNN上,动态shape更是硬伤。
我试过类似场景,提升也就5%-10%,纯属浪费时间,不如直接关掉省心。
实话实说,你这种情况我遇到过类似的,2万条数据对bert来说确实太小了,compile的优化大头在kernel融合和显存分配上,数据量不够根本摊不平编译开销,10%算正常。动态shape报错那个无解,torch.compile对变长序列支持一直很拉胯,要么统一padding到固定长度,要么干脆别用。你如果想尝鲜,可以试试把compile关掉,换torch.inference_mode加half精度,推理提速比这实在。反正训练阶段我是不太建议折腾,收益低还闹心,部署时用onnxruntime或者tensorrt更稳。
这情况太真实了,我拿GPT2试过也是差不多,小模型上compile的收益基本被显存分配和算子调度开销吃掉了。你数据量2万条,训练时间本来就不长,省那10%真不如把精力放在调learning rate和batch size上。动态shape报错那个确实坑,transformers的tokenizer输出长度不固定,建议要么固定padding策略,要么干脆别在训练阶段用compile,推理时再单独搞个静态图版本。另外官方那个30%-50%基本是拿大模型大batch堆出来的,咱这场景参考意义不大。
小模型上compile收益确实有限,我试过类似场景也就快10%出头,动态shape报错太真实了,部署时直接换ONNX吧。
我这边3090跑过3万条文本分类,compile提速基本可以忽略,还多了一堆兼容性坑,纯纯浪费调优时间。
跟你情况差不多,小模型上compile收益确实鸡肋,动态shape更是直接劝退,建议等模型大了再折腾。
动态shape问题无解,transformers配合compile本来就不稳定,我试过直接放弃,老老实实eager。
同款配置,2万条数据上compile确实收益有限,我试过大概也就快8%-12%的样子,跟官方benchmark差距大主要是他们拿大模型大batch测的,小模型吃不满优化。动态shape那个坑我也踩过,后来干脆固定max_len加padding,或者直接用torch.compile的fullgraph=False模式,至少能避免一半的报错。你要是纠结部署推理,不如先试试onnx或者TensorRT,小模型收益比compile明显得多。
我是觉得你这情况没必要硬上compile,数据量小,训练时间本来就不长,省那10%不如把心思花在调参上。之前我拿bert-base试过,动态shape确实一堆坑,后来改成bucket batching才勉强跑通。部署的话如果走CPU,openvino可能比compile更省心,GPU就直接TRT,别在compile一棵树上吊死。
说实话小模型加小数据,compile那点提速真的不够看,我这边一个textcnn跑起来甚至还会变慢,编译开销都抵不上收益。你那个报错大概率是transformers里有些操作不支持动态shape,可以试试先把padding改成固定长度,或者用torch._dynamo的dynamic=False强行关掉动态支持。推理阶段如果追求极致,建议直接转成torchscript或者ONNX,比compile稳定多了。
说实话你这个场景我试过类似的,2万条数据+bert这个量级,compile那点提升真不够看,主要是编译开销摊不平。动态shape那个坑我也踩过,padding长度一变就重新编译,反而更慢,建议直接pad到固定长度或者干脆别用compile。你如果真想提速,试试把batch size调大点或者用混合精度,收益比这个实在。部署推理的话,onnx或者torchscript可能更稳,别在compile上死磕。
跟你情况差不多,2万条数据上compile确实有点鸡肋,那10%的收益基本就是吃不满显存带宽带来的,小模型算力瓶颈不在图优化上。动态shape报错太正常了,torch.compile对变长序列的支持目前就是个半成品,建议要么固定长度padding到底,要么干脆别折腾。我试过把bert的forward里那个自定义head单独拎出来编译,主体保持eager,这样至少不报错,但收益也就5%上下,属实不值得为这点提升牺牲调试体验。部署推理阶段如果追求极致延迟,可能换成ONNX TensorRT更实在,compile在你这场景纯属提前优化了。
小模型真没必要硬上compile,你这提升幅度算正常,动态shape直接劝退,老老实实eager保平安。
你这场景我用过,2万条数据真没必要上compile,收益全被编译开销吃了,动态shape直接劝退。
我试过类似配置,提速也就5-8%,不如把精力放在数据加载和混合精度上,部署时再考虑静态图优化吧。
数据量小真没必要折腾compile,动态shape直接劝退,老老实实eager跑完得了。