[Bug已解决] torch.linalg.cross 在 MPS(Apple Silicon)上对 bool 输入报 RuntimeError 解决方案
[Bug已解决] torch.linalg.cross 在 MPS(Apple Silicon)上对 bool 输入报 RuntimeError 解决方案
一、现象长什么样
你在 Apple Silicon(M1/M2/M3/M4) Mac 上,用 PyTorch 的 MPS(Metal Performance Shaders)后端跑torch.linalg.cross(叉积)时,如果输入是bool(布尔)张量,可能遇到:
RuntimeError: ... (MPS backend) ... for bool input也就是官方描述的:
torch.linalg.cross - MPS RuntimeError for bool inputtorch.linalg.cross计算两个三维向量的叉积(cross product)。数学上叉积定义在实数向量上。但你如果「不小心」把一个 bool 张量喂进去,CPU / CUDA 上可能悄悄转成数值算(或给个清晰错误),而MPS 后端直接 RuntimeError,因为它对 bool 的 kernel 路径没实现 / 不支持。本文讲清楚 MPS 后端的特点、为什么 bool 输入会炸、以及如何规避。
二、MPS 后端是什么
MPS(Metal Performance Shaders)是 PyTorch 在 Apple Silicon(及部分 Intel Mac 配 AMD)上的 GPU 加速后端。它把 PyTorch 算子映射到 Apple 的 Metal GPU 框架。
import torch device = "mps" if torch.backends.mps.is_available() else "cpu" x = torch.randn(3, device=device)MPS 不是「CUDA 的完全平替」——它支持的算子集合、dtype 支持度和 CUDA 有差异。某些算子在 MPS 上对**冷门 dtype(如 bool、complex 的某些组合)**支持不完整,于是直接 RuntimeError,而不是默默算。
三、为什么 bool 输入会让 cross 在 MPS 上炸
叉积的数学定义(以三维为例):
c = a × b c_x = a_y*b_z - a_z*b_y c_y = a_z*b_x - a_x*b_z c_z = a_x*b_y - a_y*b_x它本质是「乘加运算」。bool 张量(True/False)严格说没有「叉积」定义。在 CPU / CUDA 上,PyTorch 可能把 bool 提升为数值(True=1, False=0)然后算;但 MPS 的 cross kernel 没实现 bool 路径,遇到 bool 直接报错。
这其实是「MPS 对 bool 支持不完整」的具体表现。报错虽恼人,但比「悄悄算错」好——它至少让你知道 dtype 不对。
四、可运行:复现与对比
下面脚本对比 cross 在 CPU / MPS 上对 bool 输入的行为(纯 CPU 部分可跑):
import torch def demo_cross(): a = torch.tensor([True, False, True]) b = torch.tensor([False, True, True]) # CPU:可能把 bool 当 0/1 算,或报错 try: c_cpu = torch.linalg.cross(a, b) print("CPU cross(bool) 结果:", c_cpu, c_cpu.dtype) except Exception as e: print("CPU 也报错:", e) # MPS if torch.backends.mps.is_available(): a_m = a.to("mps") b_m = b.to("mps") try: c_m = torch.linalg.cross(a_m, b_m) print("MPS cross(bool) 结果:", c_m) except Exception as e: print("MPS 报错(符合预期,bool 不支持):", type(e).__name__) else: print("无 MPS 设备,跳过") if __name__ == "__main__": demo_cross()如果你在 MPS 上看到 RuntimeError,就复现了该问题。
五、解决方案一:把 bool 转成数值 dtype 再算
最稳的修复:cross 之前把 bool 提升为 float / long,叉积就有定义了:
import torch def safe_cross(a, b, device): # 确保是数值 dtype(bool -> float) if a.dtype == torch.bool: a = a.float() if b.dtype == torch.bool: b = b.float() return torch.linalg.cross(a.to(device), b.to(device)) # 使用 a = torch.tensor([True, False, True]) b = torch.tensor([False, True, True]) device = "mps" if torch.backends.mps.is_available() else "cpu" out = safe_cross(a, b, device) print("正确的叉积:", out)bool提升到float后,MPS 的 cross kernel 正常工作(True=1.0, False=0.0)。
六、解决方案二:如果本意是「逻辑操作」,别用 cross
有时你用 bool 是想做「逻辑与 / 或 / 异或」,而不是真的叉积。那根本不该用torch.linalg.cross:
import torch a = torch.tensor([True, False, True]) b = torch.tensor([False, True, True]) # 逻辑操作应该用对应算子 and_ = a & b # 按位与 or_ = a | b # 按位或 xor_ = a ^ b # 按位异或 print("AND:", and_, "OR:", or_, "XOR:", xor_)确认你的真实意图:要「叉积」就用数值 dtype;要「逻辑运算」就用& | ^,别混用。
七、解决方案三:统一设备前统一 dtype(避免 CPU 能跑 MPS 不能)
一个常见坑:你在 CPU 上用 bool 测过 cross 能跑(或报别的错),到了 MPS 就 RuntimeError。为保持一致,应该在「进设备前」就规整 dtype:
import torch def prepare_for_device(x, device): # 进 MPS 前,bool 一律转 float,规避 MPS 对 bool 不支持 if device == "mps" and x.dtype == torch.bool: x = x.float() return x.to(device) device = "mps" if torch.backends.mps.is_available() else "cpu" a = prepare_for_device(torch.tensor([True, False, True]), device) b = prepare_for_device(torch.tensor([False, True, True]), device) out = torch.linalg.cross(a, b)这样 CPU / MPS 行为一致,不会出现「换个设备就崩」。
八、解决方案四:用 try/except 优雅降级到 CPU
如果你的程序要在「支持 bool cross 的环境」和「MPS」之间移植,可以降级:
import torch def cross_robust(a, b): device = a.device try: return torch.linalg.cross(a, b) except RuntimeError: # MPS 不支持时,退回 CPU 算(bool 也行),再搬回原设备 a_cpu, b_cpu = a.cpu(), b.cpu() if a_cpu.dtype == torch.bool: a_cpu, b_cpu = a_cpu.float(), b_cpu.float() c = torch.linalg.cross(a_cpu, b_cpu) return c.to(device) a = torch.tensor([True, False, True], device="mps") if torch.backends.mps.is_available() else torch.tensor([True, False, True]) b = torch.tensor([False, True, True], device="mps") if torch.backends.mps.is_available() else torch.tensor([False, True, True]) out = cross_robust(a, b) print("稳健叉积结果:", out)九、解决方案五:升级 PyTorch(MPS 支持持续补全)
MPS 后端是 PyTorch 较新、持续完善的。新版本可能已补全 cross 对 bool 的支持(或给出更清晰错误)。查看版本:
import torch print("PyTorch:", torch.__version__) print("MPS 可用:", torch.backends.mps.is_available())十、如何判断你踩的是同一条
- 设备是 Apple Silicon(MPS 后端);
- 调用
torch.linalg.cross(或torch.cross); - 输入是 bool 张量;
- 报错是 RuntimeError,且提到 MPS / bool;
- 把 bool 转 float 后正常。
命中即说明是 MPS 对 bool 的 cross 不支持。
十一、小结
torch.linalg.cross在 MPS 上对 bool 输入报 RuntimeError,是MPS 后端对 bool dtype 支持不完整的具体表现。应对:
- cross 之前把 bool
.float()/.long()提升为数值 dtype(第五节); - 本意是逻辑运算就用
& | ^,别用 cross(第六节); - 「进设备前」统一 dtype,避免 CPU 能跑 MPS 不能(第七节);
- 必要时 try/except 降级到 CPU 算(第八节);
- 升级到补全 MPS bool 支持的 PyTorch(第九节)。
MPS 是 Apple Silicon 上宝贵的 GPU 加速,但它不是 CUDA 的完全镜像——尤其对冷门 dtype(bool / 某些 complex)支持有缺口。写「跨设备可移植」的代码时,把 dtype 在进设备前规整好,是最省心的习惯。
