ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Mac深度学习GPU加速实战:从MPS到TensorFlow Metal的配置与优化

Mac深度学习GPU加速实战:从MPS到TensorFlow Metal的配置与优化

不夸张地说,Mac上跑机器学习,过去十年最大的痛就是“GPU加速”这四个字。标题里这组词,放到今天已经不是能不能用的问题,而是怎么用得顺手、怎么让显存、算子、框架各自到位的问题。我用Mac做过不少图像分类和小型生成模型的训练,从早期被CPU训到怀疑人生,到现在M系列芯片上TensorFlow、PyTorch顺手调用GPU,中间踩过的坑远比想象中多。这篇就把我目前觉得最靠谱的配置路径和排查经验整理出来,给正准备用Mac入坑或者已经在坑里的同学一个可复现的参考。

这篇文章不打算写成官方文档的复读机,重点落在三块:为什么Mac的GPU加速今天才变得实用、怎么从零搭出一套能真正调用GPU的环境、以及训练过程中那些不翻文档根本猜不到的问题。适合刚买M系列机器想跑深度学习的小白,也适合已经被CPU折磨过、想确认自己有没有真把GPU用起来的进阶玩家。

1. 整体思路:Mac上的GPU加速到底改变了什么

1.1 以前为什么Mac被戏称为“ML测试机”

早几年在Mac上跑深度学习,最直观的感受就是“能用,但别指望快”。那个时候主流框架的GPU支持基本绑定在CUDA上,而CUDA只认某一家显卡生态。Intel芯片的Mac就算配了独立显卡,在框架眼里也基本等于摆设,绝大多数计算都落在CPU上,训练一个稍微像样点的卷积网络,动辄以小时为单位。所以那个年代大家默认Mac只适合写代码、调逻辑,真正训练必须去服务器。这也是“Mac是ML测试机”这个说法流传已久的根源。

真正改变局面的是统一内存架构。M系列芯片把CPU、GPU、神经网络加速单元放在同一颗SoC里,共享同一块物理内存。传统显卡有独立的显存,CPU侧数据要先拷贝到显存才能计算,拷贝本身就消耗时间;而统一内存背景下,CPU和GPU访问的是同一块数据,理论上连拷贝这一步都省了。这也是为什么M系列跑大模型推理时,能出现“60G内存的Mac能装得下70B量化模型”这种反常识现象,本质上是物理内存即显存。

1.2 三种主流方案的定位:Metal、MPS、MLX

搞清楚底层原理之后,选择就变得清晰了。目前Mac上做GPU加速,绕不开下面几条路线。

第一是TensorFlow官方支持的Metal插件,也就是tensorflow-metal。它走的是PluggableDevice机制,装上之后TensorFlow能直接调用Metal来跑训练和推理。这套方案推出得比较早,生态相对成熟,但前提是系统版本和Python版本都要卡在支持范围内,版本错位就容易出现装上却识别不到GPU的情况。

第二是PyTorch的MPS后端。MPS全称是Metal Performance Shaders,PyTorch从支持这个后端开始,代码里只需要做一个简单的判断,就能让张量跑在GPU上。它的优势是接入成本低,而且社区迭代很快,很多常用算子都已经迁移到Metal上执行。

第三是Apple自己推出的MLX框架。它不是给PyTorch做适配,而是一个真正面向Apple Silicon重新设计的深度学习框架,核心思路就是利用统一内存和Metal做高性能计算。MLX的API风格和NumPy、PyTorch比较接近,上手不算难,但它的生态还在早期,很多预训练模型和第三方代码需要自己动手适配。

选择哪条路,主要看你到底要做什么。如果是继续用熟悉的框架跑实验,PyTorch MPS和TensorFlow Metal更合适;如果你想把模型落地到iOS或macOS应用里,最终大概率要走到Core ML那条线,用coremltools把训练好的模型转换出去。Core ML严格说不算训练框架,而是苹果自家的推理优化引擎,配合Metal在后端做加速,是部署环节绕不开的角色。

1.3 为什么先确认架构再动手配置

很多人在Mac上配机器学习环境失败,不是命令敲错,而是根本没搞明白自己机器是哪种架构。Apple Silicon和Intel Mac在安装TensorFlow、PyTorch时,下载的wheel包是完全不同的,很多坑都源于装了CPU版本。做任何配置前,第一件事是确认芯片类型。M系芯片在终端里能看到arm64,Intel机型一般是x86_64。这里多说一句,网上有些教程是Intel时代留下的,照着抄很可能装上x86版本的Python和依赖,然后在M系列上绕圈子。

确认架构之后,还要理解一个关键概念:GPU加速不是“所有的计算都会变快”。卷积、矩阵乘法、Transformer里的自注意力这些大规模并行计算,才是GPU的强项;小矩阵运算、数据加载、张量拼接,如果频繁触发GPU和CPU之间切换,反而可能更慢。所以配置好环境之后做的第一步实验,应该是一个足够大的矩阵运算或者一个真实的小网络训练,而不是跑个Hello World就宣布成功。

2. 硬件需求与环境配置的核心细节

2.1 先看好这几个硬性门槛

想做GPU加速,硬件前提是Apple Silicon芯片,也就是M1、M2、M3、M4以及后续的Pro、Max、Ultra变体。理论上带AMD独立显卡的Intel Mac也能通过Metal做一些加速,但支持范围非常有限,很多框架版本甚至直接放弃了对Intel Mac的GPU支持,所以如果你主力机是Intel Mac,建议别把时间花在折腾GPU上,老老实实用CPU跑或者上服务器。

系统版本方面,tensorflow-metal要求macOS 12.3以上,MPS后端从PyTorch某个早期版本开始支持后,后续版本基本都内置了。建议直接把macOS升级到当代稳定版,新框架往往依赖新系统里的Metal特性,老系统会莫名少一些算子支持。

内存大小是另一个容易被忽略的瓶颈。统一内存虽然让“显存”变大,但Mac的内存条没法后期加,买机器的时候如果知道自己要跑模型,建议优先选大内存配置。我自己的体会是,16G跑中小型模型没问题,到了7B以上参数量的微调就会比较紧张;日常玩图像分类、目标检测、文本分类这些任务,16G足够入门。

2.2 Python环境管理:别再用系统自带Python

我见过太多Mac用户直接在系统Python里pip install tensorflow,结果装了一堆权限混乱的包,最后连Python版本都分不清。在Mac上做机器学习,第一步永远是建一个独立的虚拟环境。工具选Miniconda或者Python自带的venv都可以,我更推荐Miniconda。原因很现实:它能把Python版本和包都隔离干净,换项目、换框架、删环境都很方便,不用和系统环境较劲。

安装Miniconda之后,建议给每个项目单独建环境。比如用Python 3.10建一个环境,统一管理TensorFlow或PyTorch。这里专门说一下版本选择:不要盲目追最新Python。很多深度学习库的预编译MPS/Metal支持,往往对某个Python版本段支持得最稳。我目前用得比较顺手的是3.10,新一点的3.11也能用,但如果遇到诡异报错,先把环境切回官方示例里的Python版本,有时候问题直接消失。

2.3 各框架的安装命令与版本要点

确定环境稳定后,就可以开始装框架了。下面是我实际验证过比较稳的组合。

TensorFlow这边,命令是:

pip install tensorflow tensorflow-metal

tensorflow-metal就是桥接TensorFlow和Metal的插件。装完后在Python里跑一下检测代码,如果能看到Metal设备,说明TensorFlow已经识别到GPU。需要留意的是TensorFlow本身版本和metal插件版本的对应关系,经常是TensorFlow升级之后,旧版metal插件就识别不到GPU了,所以出问题时第一步建议升级tensorflow-metal到最新版。

PyTorch这边更简单。官方稳定版已经内置MPS支持,直接装就行:

pip install torch torchvision

不用像TensorFlow那样额外装插件。代码里判断MPS是否可用的标准写法是:

import torch if torch.backends.mps.is_available(): device = torch.device("mps") else: device = torch.device("cpu")

MLX更轻量,一条命令:

pip install mlx

装完测试是否识别GPU,用下面这条:

import mlx.core as mx print(mx.metal.is_available())

这里必须提醒一个细节:安装这些框架时,确保你的shell当前已经激活目标conda环境,否则很容易装到全局环境里,导致不同项目间互相污染。我自己早期就栽过这个跟头,环境混乱到只能删掉重来。

3. 从零跑通GPU加速:完整实操过程

3.1 第一步:确认机器状态

打开终端,先看架构:

uname -m

打印arm64,说明是Apple Silicon机器,可以走完整的GPU加速流程;打印x86_64,则可能是Intel机型,也可能是在Rosetta转译下运行的终端,后面配置就需要重新检查。

再看系统信息和内存:

system_profiler SPHardwareDataType

重点看Chip那一行,确认是M系列,以及Memory的大小。顺便用系统报告里的图形卡选项看一眼Metal支持情况,M系列机器默认都支持。

如果之前没装过Xcode Command Line Tools,建议先装:

xcode-select --install

这步不是必须,但很多框架编译或调用Metal相关工具时依赖系统命令行环境,先装好能少踩很多坑。

3.2 第二步:建立conda环境并安装框架

我习惯的流程是:

conda create -n mlmac python=3.10 -y conda activate mlmac

然后根据方案装框架。如果主力是PyTorch:

pip install torch torchvision

如果还要对比TensorFlow,可以同一个环境里都装上,只要注意pip解决依赖冲突:

pip install tensorflow tensorflow-metal

MLX也可以一起装:

pip install mlx

这里出现过一个容易踩的坑:很多人在这一步极容易手滑装成CPU版tensorflow,因为TensorFlow官方在PyPI上的默认arm64包其实是CPU版本,真正让GPU生效的是tensorflow-metal这个插件。所以检测GPU时如果发现TensorFlow看不到设备,优先检查metal插件是否安装成功。

3.3 第三步:验证GPU是否真的被框架识别

环境装完后,不要急着跑训练,先把下面三组验证代码跑一遍。

PyTorch验证:

import torch print("MPS available:", torch.backends.mps.is_available()) print("MPS built:", torch.backends.mps.is_built())

TensorFlow验证:

import tensorflow as tf print(tf.config.list_physical_devices('GPU'))

MLX验证:

import mlx.core as mx print("Metal available:", mx.metal.is_available())

正常的时候,PyTorch会输出MPS available: True,TensorFlow会列出一个名为Metal的物理设备。如果输出对应False或空列表,别急着怀疑机器,先看版本和系统是否匹配。

3.4 第四步:小实验看真实性能提升

验证框架认到了GPU,只是第一步,下面这个矩阵乘法小测试能直观看出差距。

import torch import time a = torch.randn(5000, 5000) b = torch.randn(5000, 5000) # CPU计算 start = time.time() c_cpu = torch.matmul(a, b) print("CPU time:", time.time() - start) # MPS计算 a_gpu = a.to("mps") b_gpu = b.to("mps") start = time.time() c_gpu = torch.matmul(a_gpu, b_gpu) print("GPU time:", time.time() - start)

我用M系列基础款实测下来,这种规模的大矩阵乘法,GPU通常比CPU快一个数量级,感知非常明显。不过要注意,这个对比里GPU的时间已经把数据搬到设备的时间排除在外了,真实训练中数据传输也是成本,所以小模型不一定有这么大收益。如果想更贴近真实场景,可以跑一个两三层的卷积网络训练几个epoch,看单epoch时间。

4. 训练实操中的调优与性能观察

4.1 统一内存的优势与隐患并存

M系列的统一内存给炼丹带来的最大优势,是能塞下传统显卡根本放不下的模型和批量大小。但正因为内存共享,GPU吃内存和系统吃内存是同一个池子,一旦模型过大,整个系统会变得极度卡顿,甚至出现微信、浏览器先崩掉的情况。我经常看到有人问“为什么我没有在深度学习程序里看到爆显存,反而是整个电脑死机”,答案就出在这。

训练时建议随时打开活动监视器观察内存压力。如果发现内存压力长时间处在高位,第一反应是调小batch size,而不是上什么黑魔法优化。另外一类解决办法是把一些临时数据放到磁盘,但速度会下降,只能在崩溃和速度之间做权衡。

个人习惯是聊天应用、视频渲染这些吃内存大户全部关掉再开训。如果你只有16G内存,这个问题尤其明显,我后来换到32G,体感完全是两个世界。

4.2 哪些算子真的加速了,哪些只是“看着在GPU上跑”

PyTorch MPS支持了常用深度学习算子,但远没到完整覆盖CUDA的程度。卷积、线性层、LayerNorm、Softmax、ReLU这类基础算子,在MPS上都很顺畅;但某些特殊算子在MPS上不支持,框架只能自动回退到CPU。这里有一个陷阱:张量已经被移动到mps设备上,算子回退到CPU时框架要做设备间同步,反而比全程CPU慢,而且你还不一定能立刻察觉。

遇到这种情况,我建议先跑一个“算子级测试”。把你模型里用到的特殊函数一个个放到MPS上执行,看是否报错或者明显变慢。比如一些自定义损失函数、F.pad的特定模式、部分插值算法,都是容易在MPS上栽跟头的对象。PyTorch官方维护了算子支持列表,跑之前可以对着列表扫一眼自己的模型结构,能省很多排查时间。

TensorFlow这边同理,Metal插件覆盖的是训练主路径,不够常见的op很可能跑在CPU上,日志里有相关信息,训练时多看一眼终端输出,能发现不少“虚假GPU加速”的陷阱。

4.3 框架选择:我什么时候用PyTorch,什么时候用MLX

跑普通项目,我默认用PyTorch加MPS,因为社区成熟,遇到问题搜得到答案。TensorFlow Metal虽然也稳,但相关实践经验分享明显少一些,适合本来就熟悉TensorFlow的人继续用。

MLX我一般用在两个场景:一是想省内存跑大一点模型的实验,它的统一内存利用比PyTorch更彻底,在同样内存压力下能塞下更大batch;二是需要部署到苹果生态的时候,MLX训练的模型可以更顺畅地导出Core ML。缺点也明显,某些预训练权重格式不兼容,需要转换脚本,这部分网上资料还在积累阶段。

简单说,如果你只能记住一句话:日常训练优先选择PyTorch MPS,模型准备上架App时用coremltools转成Core ML格式,至于MLX,可以作为优化内存问题的后备武器。绝不推荐三种框架混在一个项目里反复横跳,只会让你陷入环境地狱。

5. 常见问题与排查技巧实录

5.1 框架明明装了,却识别不到GPU

这是频率最高的求助帖。原因集中在三类:第一,没装tensorflow-metal或版本太旧,尤其是TensorFlow用户;第二,系统版本不满足要求,Metal插件需要macOS 12.3以上;第三,终端跑在Rosetta转译模式下,把arm64装成了x86_64版本。

排查路径我建议按顺序来:先uname -m确认架构,再看sw_vers确认系统版本,然后pip list看包版本,最后写一段最简单的检测脚本单步走。大多数情况下,问题在第一步或第三步就能定位。

5.2 MPS跑起来直接崩溃或结果全是NaN

这个坑我遇到过两次,印象极深。一次是自定义损失函数里用了一个MPS不支持的算子,导致梯度计算出错,训练几步后loss全部变成NaN。另一次是PyTorch版本和macOS版本配合不好,同样代码在旧系统上MPS行为异常。

处理办法也不复杂:先用纯CPU模式复现问题,把device强制改成cpu,如果问题消失,基本断定是MPS算子或设备后端问题。然后逐个替换模型里的非常规算子,找问题根源。如果CPU也报错,那和GPU无关,需要检查数据和模型逻辑本身。

5.3 内存占用异常高,系统卡到鼠标都飘

统一内存既是优势也是负担。训练时如果发现系统变卡,直接看活动监视器的“内存”标签,找到占用最高的进程。深度学习进程动不动吃掉二三十G,在16G机器上就是灾难。解法有几种:减少batch size;启用梯度累积;用混合精度训练,MPS支持float16,能把内存占用降下来不少。

还有一个偏方:把PyTorch的分配器行为调成“在内存压力高时自动释放缓存”,代码层面的处理是在训练循环里定期执行torch.mps.empty_cache。如果你用的服务器版PyTorch,可能不推荐这么做,但在Mac上实测对缓解系统卡顿有帮助。

5.4 常见问题速查表

现象可能原因解决办法
PyTorch显示MPS不可用系统版本过老、PyTorch版本太旧升级macOS和torch
TensorFlow找不到GPU没装tensorflow-metal或版本错位安装/升级tensorflow-metal
明明设置了MPS却还是很慢算子回退CPU、循环频繁切换设备检查算子支持表,减少设备转换
训练途中系统崩溃内存压力过大降低batch size,开启混合精度
损失值NaN自定义算子MPS兼容问题CPU复现定位,替换特殊算子
安装时下载了x86版本终端运行在Rosetta模式重开arm64终端再装

排查问题最重要的是冷静,不要上来就重装系统。大多数问题都能通过“确认架构、确认版本、回归CPU模式”这三板斧解决。

最后再分享一个我自己很依赖的小习惯:每次在Mac上新建一个深度学习项目,我会先在配置完环境后跑一遍那个5000乘5000的矩阵乘法基准,把CPU和GPU耗时记录到项目README里。之后如果改了环境或者系统升级,再跑一次对比。这个方法帮我快速判断新环境到底有没有退化,也省去了很多“凭感觉觉得变慢了”的内耗。GPU加速这件事,说到底就是让设备各司其职,你用Mac训练模型时如果遇到性能瓶颈,不妨先从这些细节里找答案。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进