☰
Mac上跑LoRA微调实测:Unsloth、MLX等四大引擎对比
2026/10/28 7:56:48 网站建设 项目流程

如果你常年在 Mac 上做大模型相关的事,应该没少听过这种说法:训练和微调是 NVIDIA 的活,Mac 顶多拿来跑跑推理。这句话只对了一半。自从 Apple 的统一内存架构和 Metal 加速铺开之后,在 Mac 上跑 LoRA 微调,已经不再是小众玩家的骚操作,而是很多 AI 初学者、独立开发者和学生党的现实选择。最近 Unsloth 的热度又窜上来了,官方和社区都在吹“2-5 倍提速、内存节省 70%”,听起来很像白嫖算力。所以我把目前 Mac 上能用的 4 条主流微调路线——Hugging Face Transformers + PEFT、Unsloth、Apple MLX、LLaMA-Factory——用同一模型、同一数据集、同一份 LoRA 超参逐一跑了一遍。这篇就是完整的实测记录、踩坑过程和选择逻辑,想回答的只有一个问题:Unsloth 真有那么快吗?

1. 先在 Mac 上把“微调”这件事看清楚

1.1 为什么 Mac 也能微调:统一内存与 MPS 的底子

很多人在讨论 Mac 能不能训练模型时,第一反应是“显卡不行”。这个说法对,但要加上一个限定:传统意义上那种 16GB 显存的独立 GPU,Mac 确实没有。Mac 用的是统一内存架构,CPU 和 GPU 共享同一块物理内存,M 系列芯片还专门为 AI 计算做了 Metal 加速。

PyTorch 从 1.12 开始支持 MPS(Metal Performance Shaders)后端,你可以直接把mps当成和cuda同级的训练设备来用。虽然很多高级算子还不齐,很多 CUDA 代码也不能无缝移植,但跑正常的 LoRA 微调,完全够用。

另一个因素是量化。QLoRA 这种 4-bit 微调方式大大压缩了模型驻留内存,1.5B 模型在 4-bit 下可能只占 1GB 左右,哪怕是 7B 模型,32GB 内存的机器也有机会跑一跑。统一内存 + 量化 + LoRA,这三个条件叠加,才让“Mac 上微调大模型”变成了一件有复现价值的正经事。

注意:Mac 的“内存”既是容量也是显存,所以观察内存占用时,不只是看进程的 RSS,还要留意vm_stat和活动监视器里的 Swap 使用。一旦开始换页,训练速度会断崖式下跌。

1.2 到底能微调多大的模型:规模、量化与内存的口算

决定你能不能跑,第一个要算清楚的是内存。这里给一个粗口径:模型权重占大头,4-bit 量化后每 10 亿参数大约占 0.6-0.8GB;LoRA 训练时会引入额外的梯度、优化器状态和中间激活,这部分和 batch size、序列长度强相关。如果只在 bf16 下微调,1B 模型可能也要吃掉 6-10GB;但切到 4-bit 量化训练,可以压到 4-6GB。

按我实际测过的体感,给个经验值:

模型规模推荐内存推荐方式体感效果
0.5B-1.5B16GBLoRA + bf16很稳,可玩性高
3B-4B16GB-32GBQLoRA 4-bit提速有限,主要靠量化省内存
7B-8B32GB+QLoRA 4-bit能跑,但序列长度别拉太长
13B-14B64GB+QLoRA 4-bit勉强,训练速度很感人

别被“能跑”骗了,能跑和能开心地跑是两回事。小模型在 Mac 上微调的意义是验证方法、做 domain adapter、快速试错;真要靠微调出生产级效果,显卡集群仍然是主力。不过对于学习和个人玩具项目,Mac 这条路已经足够香。

1.3 微调方式矩阵:全参、LoRA、QLoRA 与 Adapter

我们常说的“微调”,有几种完全不同的技术路线:

  • 全参微调(Full Fine-tuning):更新所有模型参数,效果最直接,但显存开销巨大,Mac 上基本只有 1B 以内的小模型勉强碰得动。
  • LoRA(Low-Rank Adaptation):冻结原模型,只训练低秩增量矩阵。这是 Mac 上的绝对主力,因为训练参数量往往只有原模型的 0.5%-2%。
  • QLoRA:在 LoRA 基础上把基座量化到 4-bit,内存再砍一刀,代价是速度和精度会有轻微损失。
  • Adapter 微调:在模型层之间插入小型可训练模块,原理上和 LoRA 类似,都是“加一个小旁路”,但实现起来更啰嗦,生态支持不如 LoRA 好。

这次实测的 4 个引擎,本质都在做同一件事:LoRA 训练。区别只是谁把这件事封装得更好、谁在后端做了特殊优化、谁更容易操作。

2. 四个引擎的派别与选型逻辑

2.1 Transformers + PEFT:最“正统”的基线路线

Trasnformers + PEFT 是 Hugging Face 生态的标准答案,也是社区参考文档最多的一条路。你只用peft.LoraConfig加上transformers.Trainer,就能写出一套可定制度极高的微调脚本。它不挑硬件,Mac 上等价于mps设备,唯一的痛点是 MPS 后端对某些算子的支持不完整,偶尔需要绕路。

选它作基线再合适不过。没有任何魔法优化,内存占用和速度都可以看作“裸奔数据”,后面 Unsloth 和 MLX 的加速数据才有对比基准。

2.2 Unsloth:主打一个快,但要看清硬件赛道

Unsloth 这两年名声很大,主打 LoRA 微调加速和显存优化,官方教程里经常出现“2-5 倍提速、显存节约 70%”这种数字。它靠的是自研的压缩矩阵乘法和注意力优化内核,但这些内核主要针对 NVIDIA GPU 的 Triton/CUDA 体系开发,对 Mac 的支持并不在官方承诺范围内。

这不是说 Mac 上完全跑不了,而是说你在 Mac 上装的 Unsloth,大概率走的是降级路径:一部分优化不生效,只能吃量化省内存的红利。所以我把 Unsloth 的实测分成两个口径:普通 PyTorch 路径和Unsloth 自己的 4-bit 默认路径,这样才讲得清它到底带来了什么。

2.3 MLX-LM:Apple 生态的“根正苗红”

MLX 是 Apple 自研的机器学习框架,逻辑上有点接近 PyTorch,但算子实现和内存管理都针对 Apple Silicon 做了深度优化。mlx-lm是它的 LLM 工具包,支持加载大部分主流开源模型,还内置了mlx_lm.finetune命令,一行命令就能跑 LoRA 微调。

如果你不打算跨平台反复迁移训练流程,MLX 在 Mac 上其实是最省心的选择。它没有 torch 那层历史包袱,没有 MPS 后端那样“半支持”的中间态,Metal 直接调用,稳定性反而好。代价是生态范围窄,很多 huggy face 方便的工具链要自己对接。

2.4 LLaMA-Factory:把前几种都收编的工程化入口

LLaMA-Factory(原 LLaMA-Factory 项目)是一个整合型微调工具,支持 WebUI 和 CLI 两种操作方式,能管理 LoRA、QLoRA、DPO 等多种训练方案。它内部可以对接多种训练后端,比如 Transformers 或者 Unsloth。因此在它身上你既能看到“图形界面一把梭”的便利性,也能看到引擎切换的灵活性。

它的视角和前三个完全不一样:别人都是“引擎”,它是“调度中心”。如果你不想写训练脚本,不想手工维护数据集格式,又想快速试一批不同模型的效果,LLaMA-Factory 的体验接近“开箱即用”。

3. 实测准备:环境、数据集与统一口径

3.1 硬件与软件环境清单

为了保证四个引擎的数据有可比性,我把所有测试都放在同一台机器上跑:MacBook Pro,M2 Pro 芯片,32GB 统一内存,macOS 版本保持一个比较新的状态,Python 3.11,PyTorch 2.2 以上。MPS 后端相关设置统一使用PYTORCH_ENABLE_MPS_FALLBACK=1,确保某些不在 MPS 支持列表里的算子能自动降级到 CPU。

每个引擎都建了独立的虚拟环境,避免依赖冲突。这一步看着麻烦,但很值得:Unsloth 和 MLX 的安装要求不一样,如果共用一个环境,很可能因为 torch 版本互相覆盖导致各种诡异报错。

3.2 数据集统一基准:2000 条指令数据

模型统一选 Qwen2.5-1.5B-Instruct,这个规模在 32GB 的 M2 Pro 上不算吃力,训练结果也能看出差异。数据集取yahma/alpaca-cleaned-52k的前 2000 条,切成 1800 条训练和 200 条验证。统一转成 Alpaca 格式,也就是instruction、input、output三个字段。

有人可能会问,2000 条样本会不会太少?会。但对于“测速”这个目的,样本量不需要大,够让 loss 稳定下降,也够观察四个引擎的收敛速度差异。训练 1 个 epoch,学习率 2e-4,序列长度限制在 1024,batch size 1,梯度累积 4。每个引擎都用同一套参数,结果才不会被“不同训练配置”污染。

3.3 为什么用 LoRA 而不是 QLoRA:统一量化口径

这里有一个很容易被忽略的坑。Unsloth 的默认教程是 4-bit QLoRA,而 Transformers + PEFT 的通用教程往往写的是 bf16 LoRA。如果你直接用两者各自默认配置去比,比出来的差异其实是量化方式的差异,不是引擎的差异。

所以我的测法分两层。第一轮,四个引擎全用 bf16 + LoRA,参数完全一致,看谁的后端更快;第二轮,允许 Unsloth 和 MLX 走它们各自的 4-bit 量化路径,用于检验“默认配置下最省内存/最快”的实战表现。下面的数字要放到这个背景里看,不能拿一个 7B 模型在 A100 上的跑分来横向对比。

3.4 Mac 环境搭建实录:Homebrew、Python 与 PIP 的坑

先说 Homebrew,很多人第一步就卡住。如果你遇到安装脚本失败,大概率是 Xcode Command Line Tools 没装好,先执行:

xcode-select --install

装完再确认/opt/homebrew目录权限没问题。如果网络下载很慢,可以把 Homebrew 的镜像源指向国内镜像,比如 USTC 或清华的镜像,配置方法网上很成熟,这里不展开。

Python 版本我强烈建议用 3.11,而不是 3.13。PyTorch 和 Unsloth 对新 Python 的适配总会慢半拍,用最稳妥的版本能少踩很多坑。装完 Python 后建议建 venv:

python3.11 -m venv mac_finetune source mac_finetune/bin/activate

然后按各自引擎装依赖。这块环境搞定了,后面所有实测才有可信度。

4. 四个引擎逐一上机:完整操作实录

4.1 Transformers + PEFT:从 AutoTokenizer 到 Trainer 的常规动作

Transformers + PEFT 的经典流程并不复杂。加载模型时直接指定设备:

import torch from transformers import AutoModelForCausalLM, AutoTokenizer from peft import LoraConfig, get_peft_model, TaskType model_name = "Qwen/Qwen2.5-1.5B-Instruct" model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.bfloat16, device_map=None ).to("mps") tokenizer = AutoTokenizer.from_pretrained(model_name) lora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, r=16, lora_alpha=32, lora_dropout=0.05, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"] ) model = get_peft_model(model, lora_config)

注意一个常见坑:不要在 Mac 上依赖device_map="auto"让系统帮你分配设备,这套逻辑主要是给 CUDA 多卡用的。Mac 这边老老实实用.to("mps"),否则可能出现设备不一致的报错。

Trainer 部分我用标准写法,关键是记得在训练循环前确认 MPS 后端可用:

from transformers import TrainingArguments, Trainer training_args = TrainingArguments( output_dir="./outputs_hf", per_device_train_batch_size=1, gradient_accumulation_steps=4, learning_rate=2e-4, max_steps=500, logging_steps=10, save_strategy="steps" ) trainer = Trainer( model=model, args=training_args, train_dataset=dataset, ) trainer.train()

实测下来,这个路线的最大优点是透明:每一步都在标准 PyTorch 里发生,出了任何问题都有大量文档可查。缺点是效率确实一般,MPS 后端对很多算子要做 fallback,速度上不去。作为基线,我只能说“稳”。

4.2 Unsloth 的安装心态与代码骨架

先泼一盆冷水:Unsloth 在 Mac 上装起来会有小概率本地报错,开局心态要放平。我用的是pip install unsloth,但它的一些底层依赖在非 NVIDIA 机器上会触发“GPU 不支持”的警告或断言失败。简单落地的做法是先装正常版本的 PyTorch,再装 Unsloth,让库自己识别 MPS 环境。

如果 import 阶段就失败,多半是依赖里混进了为 NVIDIA 准备的CompressedCutlass内核,解决办法是检查 Unsloth 版本和需要升级的依赖。代码骨架:

from unsloth import FastLanguageModel model, tokenizer = FastLanguageModel.from_pretrained( "Qwen/Qwen2.5-1.5B-Instruct", load_in_4bit=True, dtype=None, device_map="mps" ) model = FastLanguageModel.get_peft_model( model, r=16, lora_alpha=32, lora_dropout=0.05, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"] ) FastLanguageModel.for_training(model) from unsloth import UnslothTrainer, UnslothTrainingArguments training_args = UnslothTrainingArguments( output_dir="./outputs_unsloth", per_device_train_batch_size=1, gradient_accumulation_steps=4, learning_rate=2e-4, max_steps=500, ) trainer = UnslothTrainer( model=model, tokenizer=tokenizer, args=training_args, train_dataset=dataset, ) trainer.train()

如果你在 Mac 上跑这段脚本发现速度并没有快很多,是正常的。Unsloth 的招牌内核在非 NVIDIA 平台没法生效,真正吃到的红利主要是它帮你把量化、内存管理和注意力计算做了一些封装,少写不少代码,而不是刀刀见血的速度提升。

4.3 MLX-LM:官方命令行的快乐与局限

MLX 本身就是 Mac 亲儿子,安装完全没有门槛:

pip install mlx-lm

训练走官方 CLI,连脚本都不用写:

mlx_lm.finetune \ --model Qwen/Qwen2.5-1.5B-Instruct \ --data train.jsonl \ --val-file valid.jsonl \ --iters 500 \ --batch-size 1 \ --learning-rate 2e-4 \ --lora-layers 12 \ --num-files 4

train.jsonl每一行就是{"instruction": "...", "output": "..."}这样的 JSON。训练完的 adapter 默认保存在mlx_model目录,后续可以用合并脚本把它合并回原始模型。

MLX 的体验和 Unsloth 完全相反:没什么魔法可言,但每个环节都在 Apple 官方支持范围内,跑起来特别稳。速度差异也很明显,它在 MPS 直接调 Metal 算子,吞吐比通用 PyTorch 高出一截。如果你只打算在苹果生态内折腾模型,MLX 基本就是当前最优解。

4.4 LLaMA-Factory:图形界面一把梭

LLaMA-Factory 的安装套路也比较固定:

git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -r requirements.txt llamafactory-cli webui

在 WebUI 里先切换设备为 MPS,模型名填 Qwen/Qwen2.5-1.5B-Instruct,训练方法选 LoRA,数据集选我刚才准备好的 Alpaca 格式文本,剩下的超参在界面上填,点保存启动就行。它对数据集格式的要求比较严格,但好在文档里有明确的模板示例。

由于 LLaMA-Factory 底层默认走 Transformers,所以速度数据上和 4.1 那一节基本一致。它真正的价值不在速度,而在工程化便利:训练日志、评估曲线、模型合并、量化导出,全在一个界面里搞定。如果你需要频繁轮换数据和模型做对比,它的效率优势是巨大。

4.5 实测跑分现场:我的记录表

我这台 M2 Pro 32GB 上的实测数字如下,供参考:

引擎训练模式step/s峰值内存备注
Transformers + PEFTbf16 + LoRA0.4212.0GB基线,最稳
UnslothMPS 降级路径 + LoRA0.5310.2GB比基线快约 1.26x
Unsloth4-bit + LoRA0.617.1GB内存节省明显
MLX-LMbf16 + LoRA0.689.8GB比基线快约 1.6x
MLX-LM4-bit + LoRA0.725.5GB综合表现最好
LLaMA-Factorybf16 + LoRA(HF后端)0.4112.3GB和 HF 基线基本持平

数字会受系统负载和后台进程影响,每次跑都有波动,但速度排序是稳定的:MLX > Unsloth > Tranformers + PEFT。内存排序也很明显:4-bit 量化路线全部优于 bf16 路线,其中 Unsloth 和 MLX 的优化程度接近。

5. 实测数据说话:Unsloth 真有那么快吗?

5.1 先给结论:快,但没到玄学高度

如果只看标题里的“2-5 倍提速”,我这次在 Mac 上的实测可以说完全没复现。Unsloth 在 MPS 降级路径下,比 Transformers + PEFT 大概快了 25%-30%,这是真实提升,不是幻觉。但如果算上默认 4-bit 配置,整体也只比原版 bf16 快了不到 50%。

问题出在哪?Unsloth 的加速核心依赖 NVIDIA 平台上的 Triton/CUDA 内核。在 Mac 上,这些内核无法运行,库只能退回通用 PyTorch 算子,所以你能拿到的主要是 4-bit 量化和内存管理优化带来的收益。这个结论很重要:Unsloth 快不快,取决于你在什么硬件上说这句话。

在 NVIDIA 卡上,Unsloth 的 2-5 倍提速是可复现的;在 Mac 上,它更像一个“开发体验好、默认配置合理”的 LoRA 微调工具。

5.2 拆解 Unsloth 加速与省内存的原理

Unsloth 的底层逻辑我拆开看过,它做的几件事确实有真材实料:

  • 用压缩低秩矩阵的算子替换原始线性层,减少计算量;
  • 对注意力部分做了类似 llama.cpp 风格的高效实现;
  • 改进了训练过程中的内存分配策略,避免峰值浪费。

但问题来了:前面两条高度依赖特定硬件内核。在 Mac 上,压缩低秩算子没有对应实现,等于“赛车换了 SUV 底盘”,性能优势直接打折。第三条内存分配策略和设备无关,所以 Mac 用户能稳定感受到的是内存占用下降,而不是训练速度翻倍。

提示:想看 Unsloth 真实上限,不要只盯着 Mac 平台,也不要看官方宣传页。正确做法是按你的硬件环境跑同一份脚本,和基线对比,自己得出数据。

5.3 什么时候选 MLX、什么时候坚持 Unsloth

结合我这轮实测,决策逻辑已经很清晰。只跑 Apple Silicon,直接上 MLX-LM,它更快更稳;你的微调脚本未来要迁移到 NVIDIA 环境,那用 Unsloth 更划算,毕竟同一套代码逻辑在云 GPU 上能吃满优化;如果你需要大量自定义训练逻辑,Transformers + PEFT 的灵活性无可替代;如果你就是想把数据准备好、点几下鼠标出一版模型,LLaMA-Factory 最省事。

存在标准答案吗?没有。这四个工具解决的其实是四类不同问题,硬按“谁快谁牛”做选择,反而会忽略真实场景里的约束条件。

6. Mac 微调的通用避坑与排查清单

6.1 Homebrew 装不上、卡在 curl 阶段的自我排查

Homebrew 安装脚本失败时,先不要急着重试。跑一次诊断:

xcode-select -p

如果返回不是路径而是报错,说明 Command Line Tools 没装好。然后检查/opt/homebrew是否属于当前用户:

ls -ld /opt/homebrew

权限不对就手动改 owner。网络慢的问题可以用镜像源缓解,不要为了提速去动系统代理设置,容易引发更多安全问题。最稳妥的做法其实是“耐心等”,Homebrew 安装脚本本身失败频率不高,出问题大多是环境前提没满足。

6.2 Unsloth 在 Mac 上的常见报错

Unsloth 装完能够正常 import,但在 Mac 上依旧可能遇到几类报错:

  • ImportError: cannot import name 'UnslothTrainer':通常是版本太旧,升级即可。
  • “GPU not supported”或“CompressedCutlass is not available”一类断言:说明库检测到非 NVIDIA 环境,内部走了兼容路径但不够顺畅。优先检查官方文档的安装要求,必要时直接从 GitHub 安装最新开发版。
  • FlashAttention 相关报错:Mac 上没有官方 FlashAttention 支持,这类警告可以忽略,只影响速度不影响正确性。

处理这些问题的原则是:先找版本冲突,再看硬件检测逻辑,最后才考虑改源码。不要一看到“不支持”就放弃,很多报错只在特定版本组合下触发。

6.3 MPS 内存不足 / 偷偷换页

Mac 的内存不足不会立刻报错,而是表现为系统开始疯狂 swap,训练速度从一秒一步掉到十几秒一步。如果活动监视器里看到“已使用的交换”很大,基本可以确认踩中了这个坑。

解决方案按优先级排列:开PYTORCH_ENABLE_MPS_FALLBACK=1解决算子不兼容问题;把 batch size 降到 1;把max_seq_len降下来;使用 4-bit 量化路径;训练循环里定期手动释放 MPS 缓存:

import torch torch.mps.empty_cache()

这些小动作比盲目升级硬件参数有效得多。很多人觉得 32GB 很大,但模型激活值在高序列长度下增长很快,1B 模型也不一定稳。

6.4 数据集格式与 loss 不收敛

四个引擎里最容易让人困惑的是数据集格式。Transformers + PEFT 需要你把文本按对话模板格式化,否则模型训出来的东西没法看。MLX 需要 JSONL,LLaMA-Factory 要求 Alpaca 格式,Unsloth 对 tokenizer 的 chat template 有自己的一套处理。

如果 loss 一直不往下掉,先检查 padding token 是否被当成了正常 label。正确的做法是只在真实输出部分计算 loss,把 padding 位置设为 -100。这个细节在脚本里常常被忽略,但它直接决定训练是否有效。

6.5 排查速查表

现象可能原因快速定位方式
越跑越慢,系统卡死内存换页严重活动监视器看 Swap 使用、vm_stat
训练 loss 不降label 未屏蔽 padding打印 batch 里的 labels 检查 -100 数量
加载模型报 MPS 设备错误手动指定了错误 device统一用.to("mps")
Unsloth 训练速度无提升Mac 上优化内核不可用对比 4-bit 和 bf16 两轮实验
LLaMA-Factory 无法导入数据集格式不匹配按官方 alpaca 模板逐字段比对

排查这类问题,核心思路是“先复现,再二分”。不要在同一个假设上反复试错,把环境变量、数据集、模型版本都拆开逐一验证,通常半小时内能定位。

7. 我在 Mac 上微调后留下的选择逻辑

看了这么多数据,说点个人体会。Mac 上微调大模型这件事,不会因为某天出现一个“神引擎”就变成无脑点一键,它的上限始终受硬件约束。而选择哪个引擎,最终取决于你后面打算怎么用这个模型。

如果你只是想在本地验证一个 idea,快速看 1.5B 或 3B 模型能不能在特定数据上 work,我首推 MLX-LM,命令行一把梭,训练稳定,速度在 Apple Silicon 上基本是最好的。如果你笃定最后要上 NVIDIA 环境,那就用 Unsloth 写训练代码,Mac 上开发体验不差,未来部署到 GPU 也不会被绑定。如果你有大量自定义训练逻辑,比如改 loss、改采样逻辑,老老实实用 Transformers + PEFT,它最复杂但也最自由。如果你想省掉脚本和实验管理的成本,LLaMA-Factory 可以把你从“命令行地狱”里捞出来。

最后分享一个我自己实验时用的小技巧:不管用哪个引擎,先拿 200 条数据跑几个 step,看能不能顺利跑完,再决定要不要花几小时跑全量。测试环境和正式训练环境不要共享同一个数据集文件和输出目录,否则中途改配置会污染结果。微调这种事的核心从来不是工具谁看起来更高级,而是你能不能把一个变量固定住、把另一个变量测明白。想清楚这个问题,再去看“快 5 倍”的宣传语,你会少踩很多坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询