十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

如何用 MLX shapeless 编译避免输入形状变化导致 mx.compile 重新编译

如何用 MLX shapeless 编译避免输入形状变化导致 mx.compile 重新编译 如何用 MLX shapeless 编译避免输入形状变化导致 mx.compile 重新编译【免费下载链接】mlxMLX: An array framework for Apple silicon项目地址: https://gitcode.com/GitHub_Trending/ml/mlx在 MLX 中用mx.compile编译过的函数只要输入数组的形状发生变化就会重新走一遍编译流程第一次调用时 MLX 需要构建计算图、优化并生成编译代码这一步文档中明确说“可以相当慢”relatively slow。如果同一个编译函数会反复接收不同 batch 或不同序列长度的输入每次形状变化都触发一次重编译开销会反复累积。mx.compile提供了shapelessTrue选项编译一次后输入形状变化不会触发重新编译。本文给出该选项的用法、验证方式以及文档中明确警告的失败模式依赖输入形状的计算和规避方法。适用环境Apple silicon、原生 Python 3.10、macOS 14.0PyPI 安装即pip install mlx见 docs/src/install.rst。先确认触发重编译的条件根据 docs/src/usage/compile.rst以下情况会让编译函数被重新编译输入的形状shape或维度数number of dimensions变化任一输入的类型type变化传入函数的输入个数变化。其中部分情况只会重跑编译栈的一部分例如只改形状部分情况会完整重跑例如改类型。这正是shapeless要解决的场景只改形状、不改维度数和类型时避免重编译。用 shapelessTrue 编译函数compile的完整签名为见 python/src/transforms.cpp 中的绑定定义compile(fun: Callable, inputsNone, outputsNone, shapeless: bool False) - Callableshapeless默认False。开启后函数只编译一次之后在可变形状的输入上运行即可文档原话是“changes to the shapes of the inputs do not cause the function to be recompiled”import mlx.core as mx def fun(x, y): return mx.abs(x y) compiled_fun mx.compile(fun, shapelessTrue) x mx.array(1.0) y mx.array(-2.0) # 第一次调用触发编译 print(compiled_fun(x, y)) # 第二次调用输入形状不同但不会重新编译 x mx.array([1.0, -6.0]) y mx.array([-2.0, 3.0]) print(compiled_fun(x, y))上面的示例直接来自 docs/src/usage/compile.rst 的 Shapeless Compilation 一节可以原样运行标量输入编译一次后换用(2,)形状的输入再调用走的是同一份编译结果。仓库测试 python/tests/test_compile.py 的test_shapeless_compile还演示了行为边界可作为判断依据输入从(2,)变为(3,)不重编译输出仍然正确输入类型从 int 变为 float会重新编译输入维度数从 1 维变为 2 维会重新编译。也就是说shapelessTrue只豁免“形状值变化”不豁免“维度数变化”和“类型变化”这两点与 docs/src/usage/compile.rst 开头列出的重编译条件一致。验证编译结果是否正确文档本身给出的验证方式是“同一输入下编译函数与普通函数输出一致”。仓库测试里用的是mx.array_equal做逐元素比较例如test_shapeless_compile_with_broadcasts中def fun(x, y): return x * y cfun mx.compile(fun, shapelessTrue) x mx.ones((2, 2)) y mx.array([2, 2]) assert mx.array_equal(cfun(x, y), fun(x, y)) # 换用不同形状的输入同样与未编译版本对齐 y mx.array([[3]]) assert mx.array_equal(cfun(x, y), fun(x, y))建议按这个方式验证先用小形状输入确认cfun(...)与fun(...)输出相等再换成目标场景中的其他形状重复比较。只要编译版与未编译版在这些形状上都array_equal说明 shapeless 编译在该输入范围内是可用的。另外编译函数首次调用是用占位符输入做 tracing 的函数体内不能print数组来观察中间值文档示例中直接打印会崩溃。需要调试时用mx.disable_compile()或环境变量MLX_DISABLE_COMPILE全局关闭编译见 docs/src/usage/compile.rst 的 Debugging 一节。规避对输入形状有依赖的计算文档明确警告“Use shapeless compilations carefully. Since compilation is not triggered when shapes change, any graphs which are conditional on the input shapes will not work as expected.” 因为首次编译时形状被固化之后形状变化不会重编译依赖静态形状的计算会出错。文档给出的失败示例def fun(x): return x.reshape(x.shape[0] * x.shape[1], -1) compiled_fun mx.compile(fun, shapelessTrue) x mx.random.uniform(shape(2, 3, 4)) out compiled_fun(x) x mx.random.uniform(shape(5, 5, 3)) # 报错(5, 5, 3) 无法 reshape 到 (6, -1) out compiled_fun(x)第二次调用失败的文档解释是reshape使用了第一次调用时x的静态形状2 * 3 6所以(5, 5, 3)无法 reshape 到(6, -1)。修复方法是改用不硬编码形状的操作文档给出的写法是flattendef fun(x): return x.flatten(0, 1) compiled_fun mx.compile(fun, shapelessTrue) x mx.random.uniform(shape(2, 3, 4)) out compiled_fun(x) x mx.random.uniform(shape(5, 5, 3)) # 正常 out compiled_fun(x)因此使用shapelessTrue前应检查函数体内是否出现用x.shape[i]直接拼出目标形状再reshape、或对具体尺寸做条件分支的写法把它们改写为与形状无关的形式如flatten。限制与报错判断并非所有函数都能以shapelessTrue编译。参数文档python/src/transforms.cpp说明“Attempting to compile such functions with shapeless enabled will throw.” 即开启shapeless编译不支持的函数时会抛出异常这时需要退回普通编译或调整函数写法。即使shapelessTrue输入维度数或类型变化仍会重新编译它只消除“形状值变化”这一种重编译来源。形状条件化的计算不会触发编译但会在运行时报错如上文 reshape 示例或给出错误结果排查方向是定位函数体内依赖静态形状的操作。与编译无关的调试需求观察中间数组用mx.disable_compile()或MLX_DISABLE_COMPILE而不是关闭 shapeless 重新编译。完成上述改造后验证标准就是不同形状的输入不再触发首次调用级别的编译耗时且各形状下编译版输出与未编译版mx.array_equal。函数体内仍带有静态形状依赖时优先按 docs/src/usage/compile.rst 的 Shapeless Compilation 一节对照修改而不是退回shapelessFalse接受反复重编译。【免费下载链接】mlxMLX: An array framework for Apple silicon项目地址: https://gitcode.com/GitHub_Trending/ml/mlx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表