最近在试着用PyTorch 2.0的torch.compile优化一个7B的对话模型推理速度,但一跑就报“RuntimeError: Expected all tensors to be on the same device”,debug了半天发现是动态shape的问题。我的输入长度变化比较大,用padding后模型内部有些操作还是跨设备了。想问问大家,现在大模型用compile的最佳实践是什么?是不是必须固定输入长度,或者有什么配置能避免这种报错?另外,我试了用inductor后端,但有时候模型第一次推理能过,第二次就挂,感觉稳定性还是有点玄学。真诚求教,别笑我菜。
PyTorch 2.0 compile在LLM推理时总报错,是我打开方式不对吗?
全部回复
共 182 条这问题我上个月也踩过,动态shape在compile下确实容易触发设备断言,尤其padding后attention mask那块。我后来是把输入长度分成几个档位(比如256/512/1024),每个档位单独compile,这样既避免重编译又能过校验。inductor稳定性确实看版本,建议升到2.1以上,另外试下把dynamic=True参数显式传给torch.compile,有时候比让它自动推断靠谱。你用的是静态max length还是纯动态?
固定输入长度确实省心,或者试试给padding mask加个显式device声明,能少踩不少坑。
动态shape建议用mark_dynamic标记,inductor对这块支持还不太成熟,跑批推理前先warmup几次会稳很多。
试试把padding改成右对齐+固定batch,或者用torch._dynamo的dynamic=True参数,应该能缓解跨设备问题。
碰到这个报错太正常了,我一开始也被搞到怀疑人生。动态shape在compile下确实是雷区,尤其是7B这种大模型,内部有些op会隐式地把tensor搬到默认device上,padding一长就炸。我后来是干脆把输入长度统一截断到固定值,比如512或1024,再配合max_length的attention mask,基本就稳了,但代价就是长文本效果打折扣。
inductor后端那个“第一次能跑第二次挂”的毛病我也遇到过,感觉像是graph缓存和cudnn benchmark的兼容性问题,有时候清一下缓存或者换回默认后端反而没事。你试试把compile的mode设成reduce-overhead,然后把dynamic参数显式指定成False,应该能规避一部分设备不一致的报错。
另外,如果实在不想固定长度,可以看看torch.compile的guard机制,用torch._dynamo.mark_dynamic手动标注哪些维度会变,但这玩意儿要摸透真得花时间。我现在的土办法是,推理阶段干脆不用compile,纯用半精度和KV cache优化,速度也够,省得跟编译器的玄学较劲。
不过话说回来,7B模型能跑起来就已经很不容易了,你试过用torch.compile配合flash attention吗?有时候问题出在attention实现上,换掉反而省心。
动态shape确实是torch.compile的老大难,我之前在跑生成式模型时也踩过这个坑,后来发现把padding统一到固定长度(比如按batch内最大长度截断)能规避大部分跨设备报错。另外你可以试试给compile传dynamic=True参数,虽然会牺牲一点性能,但比手动折腾稳定多了。inductor后端偶尔抽风我也遇到过,建议回退到cudagraphs或者直接关掉compile先验证逻辑,等模型跑通了再优化。你用的什么GPU?如果是A100以下的话,有时候数据并行本身就会放大这种设备不一致的问题。
这问题我也踩过,动态shape在compile下确实容易触发跨设备检查,尤其padding后某些op的mask还是静态的。建议试试把padding到固定长度(比如256的倍数),或者用torch._dynamo的dynamic=True参数显式声明动态维度。inductor第一次过第二次挂大概率是缓存或图优化没处理好,可以试试torch._inductor.config.triton.cudagraphs=False关掉cudagraph,或者直接切回eager模式先验证逻辑对不对。
另外7B模型其实可以用vLLM或FasterTransformer这类专门库,没必要死磕torch.compile,它们对动态shape的支持成熟太多了。我上次也被这玩意折磨半天,最后发现是某个自定义attention没加mark_dynamic,加完就稳了。你先试试固定输入长度跑通再逐步放开动态范围。
试试把max_length固定成2的幂,然后padding到那个长度,我这边动态shape也踩过坑。
动态shape确实坑,试试把padding长度固定到8的倍数,能省不少心。inductor不稳定的话可以换cudagraphs试试。
动态shape确实坑,建议试试给max_length设个固定值,或者用torch._dynamo的dynamic参数。
同感,torch.compile对动态shape确实不友好,我试过在decode阶段把KV cache的shape显式声明成最大长度,然后配合mark_dynamic标记可变维度,能减少不少报错。另外inductor第一次跑会做图优化,二次挂可能是缓存问题,试试torch._dynamo.config.cache_size_limit调大点?或者直接关掉dynamic,用静态shape+padding到固定长度,推理速度反而更稳。
这问题我也踩过,动态shape建议加padding后固定到max_len,或者用torch._dynamo.mark_dynamic标记一下。
动态shape确实是compile的坑,建议先固定长度或用CUDA graphs,稳定性还得等等。
之前也踩过这坑,后来直接关掉dynamic参数,牺牲点灵活性换编译稳定,真香。
动态shape确实是compile的大坑,我之前也踩过,后来干脆把输入长度按8的倍数padding,再用mark_dynamic标记可变维度,基本能稳住。另外试试把inductor的mode换成max-autotune,虽然编译慢点但稳定性好不少。你那个跨设备报错,建议检查下tokenizer的padding_side是不是和模型一致,有时候是数据预处理的问题不是compile的锅。
动态shape在compile下确实容易踩坑,我试过把padding到固定长度(比如最大token数)然后用static shape模式,报错会少很多,但显存开销得能接受。inductor不稳定我也遇到过,后来加了个fallback策略,检测到异常就自动切回eager模式,虽然慢点但至少不崩。另外建议看看官方推荐的推理库,比如用accelerate或者vLLM的路径,有时候比直接裸torch.compile省心。
这问题太真实了,7B模型动态shape跑compile基本就是给自己找罪受。我建议你先用torch.compile(model, dynamic=True)试试,虽然会牺牲一点性能但至少能稳住不崩,另外检查下有没有用到max_length这类硬编码的缓存逻辑。至于inductor第二次挂,我猜是graph cache和你的padding逻辑冲突了,试试mode="reduce-overhead"加fullgraph=True,或者干脆把输入长度分桶固定成几个档位,比硬扛动态shape省心得多。
这问题我上周刚踩过,动态shape确实是compile的坑,尤其带padding的batch里pad_mask参与计算时特别容易触发device断言。我试下来固定长度到最大上限+左padding能稳过,但显存会浪费不少。另一个思路是试试torch._dynamo里加dynamic=True参数,让后端自适应shape范围,不过inductor对某些算子还是会抽风,第二次挂多半是缓存了错误的graph,可以试试torch._dynamo.config.cache_size_limit调大点。话说你用的什么量化方案?我怀疑跟int8/fp16混跑也有关系。
这问题我踩过一模一样的坑,动态shape跟compile天生八字不合。我后面直接改成固定最大长度+attention mask,虽然浪费点显存但至少不炸了。inductor那个玄学问题建议把mode设成max-autotune试试,我这么调完稳定了不少。另外可以看看torch._dynamo的mark_dynamic接口,能手动指定动态维度,比硬扛强点。
这报错我也踩过,动态shape得用mark_dynamic标记,或者干脆限制最小长度省心。
这问题太真实了,我最近也在折腾7B模型的compile,第一个坑就是动态shape。你那个跨设备报错大概率是padding后attention mask和位置编码在不同设备上拼接导致的,建议试试把padding到固定长度,比如max_length设为512或1024的倍数,虽然浪费点显存但能避开很多隐性bug。inductor后端确实有冷启动和重复执行状态残留的问题,我后来改成在dataloader里就统一shape,然后加torch.compile的mode=reduce-overhead,稳定性好了不少,但偶尔还是会在第二次迭代时崩,感觉像是CUDA graph和动态内存池的兼容性没完全写好。另外你可以试试把编译范围缩小,只compile那些计算密集的层比如attention和mlp,别整个模型一把梭,这样报错定位也容易点。还有个小技巧,如果你用huggingface的generate,记得把pad_token_id设好,不然内部会动态生成一些tensor导致设备不匹配。说实话这功能现在还是更适合固定shape的生产场景,研究阶段凑合用吧,等2.1的dynamic shape支持完善了可能才是真解法。你要是试出什么workaround也分享下,我这边已经被这玩意儿折磨三天了。
这问题太真实了,我当初也被动态shape坑得不行。你那个跨设备报错,大概率不是compile本身的锅,而是padding mask在某些分支里没跟着输入一起搬,或者KV cache的device在第二次跑的时候错位了。固定长度确实能绕开,但对话场景里不现实,我后来是用dynamic=True配合mark_dynamic手动标出变化的维度,报错少了一大半。inductor第一次能过第二次挂,通常是guard重新编译时把某些符号变量搞丢了,可以试试设torch._dynamo.config.cache_size_limit调大点。另外7B模型我建议先拿torch.compile只包住decoder layer,别整个model一把梭,不然编译时间能等到你怀疑人生。还有个偏方是开mode="reduce-overhead"但配静态KV cache,虽然牺牲点灵活性,稳定性确实好不少。