저희가 누구인지 간단히 말씀드리자면, 저희는 Hugging Face의 PyTorch 팀입니다. 저희는 PyTorch 전반에 걸쳐 기여하고 있으며, 초점 영역 중 하나는 MPS 백엔드입니다. 여기서 목표는 사용자의 Mac을 PyTorch 모델을 실행하고 훈련하는 일급 장소로 만드는 것입니다. 2026년 초에는 심지어 작은 모델을 MPS에서 실행할 때도 지원되지 않는 연산, 성능 급락(performance cliffs), 예상치 못한 메모리 증가 등을 우회해야 하는 경우가 많았습니다. 그래서 저희는 Hugging Face Hub의 실제 모델들, 언어, 이미지, 비디오 전반에 걸쳐 실행하며 MPS가 부족한 부분을 찾아내고, PyTorch에서 상위 레벨로 수정하고 있습니다. 점점 더 자주 MPSGraph를 전용 Metal 커널로 대체하는 방식으로 작업하고 있습니다. 이 게시물의 많은 부분은 PyTorch 유지 관리자 및 커뮤니티 기여자들과 함께 진행된 이러한 작업의 결과입니다.
Mac에 FP8 도입: BF16 공간의 절반 크기에서 값을 저장하며, CUDA/CPU에서 지원되는 스케일링된 행렬 곱셈(scaled matrix multiplication) 및 모든 다른 연산들을 사용합니다!
MoE 모델이 2.14보다 5–6.5배 빠르게 디코딩: Qwen3.6-35B-A3B는 전용 디코딩 경로를 가진 네이티브 그룹 행렬 곱셈(native grouped matmul), 적은 GPU-CPU 동기화, 더 빠른 top-k 라우팅 덕분에 M3 Ultra에서 9.4 토큰/초에서 61.3 토큰/초로 향상되었습니다.
LLM당 토큰당 시간 단축: 단일 행 선형 레이어(single-row linear layers)는 특수화된 행렬-벡터 커널을 사용하며, 어휘집에 대한 소프트맥스(softmax over the vocabulary)가 약 8배 더 빠릅니다.
GPU에서 더 많은 선형 대수 연산: 복잡한 LU 및 삼각 분해(triangular solves), 더 빠른 작은 행렬 역행렬 계산(small-matrix inverses), 그리고 더 빠른 QR 분해가 가능합니다.
오디오, 비디오, 훈련에도 주목: 컨볼루션(convolutions), 패딩(padding), 드롭아웃(dropout), 보간(interpolation) 역방향 및 축소 연산에 대한 더 빠른 경로가 제공됩니다. Wan2.2의 비디오 VAE 디코드는 이제 메모리 사용량이 270 GiB 대신 최대 8 GiB로 제한되었습니다.
Python에서 더 많은 제어: 공개 MPS 스트림(public MPS streams)과 더 신뢰할 수 있는 이벤트 타이밍이 제공됩니다.
업데이트된 요구 사항: MPS는 이제 macOS 15 이상을 요구하며, PyTorch 2.15는 Python 3.11 이상을 요구합니다.
2.16 미리보기: 어텐션(attention)이 완전히 Metal에서 처리되며, 더 많은 MPSGraph-to-Metal 마이그레이션과 더 빠른 LLM 디코딩이 제공됩니다.
Mac에서 transformers, diffusers 또는 ComfyUI와 같은 도구로 모델을 실행하는 경우, 2.15 버전에서 실제적으로 변경되는 내용은 다음과 같습니다:
훨씬 빨라진 로컬 LLM 성능. Mixture-of-experts (MoE) 모델은 2.14 버전에 비해 5~6.5배 빠르게 디코딩됩니다: M3 Ultra 환경에서 torch.compile 사용 시,
Qwen3.6-35B-A3B는 초당 9.4 토큰에서 61.3 토큰으로, LFM2.5-8B-A1B는 초당 23.6 토큰에서 123.6 토큰으로 향상됩니다. 밀집(Dense) 모델 역시 더 빠른 선형 레이어와 샘플링을 위한 8배 빨라진 소프트맥스(softmax) 덕분에 이점을 얻습니다. (더 보기)메모리에 맞는 비디오 생성. Wan2.2의 타일형 VAE를 사용한 비디오 디코딩은 이전에는 드라이버 메모리를 최대 270 GiB까지 끌어올렸으나, 이제는 약 8 GiB에서 최고점을 기록합니다. (더 보기)Qwen-Image-2.1을 이용한 정확한 이미지 편집. 2.14 버전에서는 Qwen-Image-2.1 VAE 인코더가 MPS 환경에서 이미지를 조용히 손상시켜(ComfyUI에서 보고됨) 이미지 편집 워크플로우를 망가뜨렸습니다. 동일한 패딩 변경을 통해 이 문제가 해결되었으며, 이제 인코딩-디코딩 왕복 과정은 PSNR 29.95 dB로 CPU와 일치하며, 이전 대비 10.06 dB 상승했습니다.Mac에서 FP8 가중치 사용 가능. FP8 텐서는 BF16보다 메모리를 절반만 차지하며, F.scaled_mm을 통해 GPU 상에서 직접 곱셈 연산을 수행할 수 있습니다. (더 보기)
- 시작하기
- MPS에 FP8 지원
- 더 빠른 언어 모델 디코딩
- 선형 대수(Linear algebra) 성능 향상
- 컨볼루션, 패딩 및 훈련
- 소프트맥스 및 축소 연산
- 비디오 디코딩을 위한 훨씬 작은 메모리 사용량
- MPS 스트림을 통한 더 많은 제어 기능
- MPS에서 작동하는 더 많은 연산 지원
- PyTorch 2.16 미리보기
- 마무리
Apple silicon Mac과 macOS 15 이상, 그리고 Python 3.11 이상이 필요합니다 (2.15 호환성 매트릭스 참조). OS가 너무 오래되었거나 Metal GPU를 사용할 수 없는 경우, MPS 사용 가능 여부 확인 기능에서 그 이유를 알려줍니다.
별도의 환경에서 릴리스 후보 버전을 테스트하려면:
uv pip install torch==2.15.0 --index-url https://download.pytorch.org/whl/test/cpu
FP8은 Mac의 메모리를 절약하는 또 다른 방법을 제공합니다. PyTorch 2.15는 MPS에서 torch.float8_e4m3fn 텐서를 추가했는데, 이는 값당 1바이트로 FP16이나 BF16보다 저장 공간이 절반입니다. 이 텐서들은 F.scaled_mm을 사용하여 곱셈 연산을 수행할 수 있습니다.
, 이는 텐서별 및 행별 스케일링을 모두 지원합니다. 텐서당 하나의 스케일을 사용하는 작은 예시를 보여드리겠습니다:
import torch
import torch.nn.functional as F
def quantize_fp8(x):
...
모든 LLM은 prefill 단계와 decode 단계를 가집니다. 디코드 중에는 각 새로운 토큰이 단일 행으로 모델을 통과하므로, 큰 행렬 곱셈은 행렬-벡터 곱(matrix-vector products)으로 변환됩니다. PyTorch 2.15는 이 단계를 지배하는 두 가지 종류의 레이어, 즉 dense linear layers와 mixture-of-experts (MoE) layers 모두를 가속화합니다.
MPS는 2.14 버전부터 torch.mm에 대한 GEMV 커널을 가지고 있었지만, F.linear는 여전히 MPSGraph를 거쳐야 했습니다. PyTorch 2.15는 이 둘을 연결하여 일반적인 [1, 1, hidden_size] 디코딩 입력이 이를 직접 사용하게 했으며, 적격한 바이어스 추가(bias additions)까지 통합되었습니다. 실제 모델의 96개 BF16 레이어 형태를 기준으로 할 때, 이는 1.23배의 기하 평균 속도 향상을 가져왔습니다. 가장 큰 이점은 MoE 모델의 게이트와 같은 작은 투영(small projections)에서 나타나는데, 이전 경로는 대부분의 메모리 대역폭을 사용하지 못했기 때문입니다. 큰 레이어와 LM head는 이미 대역폭 제한에 가까웠기 때문에 1배 근처를 유지했습니다.
mixture-of-experts layer는 서로 다른 수의 토큰을 각 expert에게 보냅니다. Grouped matmul은 이러한 불균등한 그룹들을 단일 연산으로 처리하며, 2.15는 MPS에 네이티브 구현을 제공합니다.
2.14 버전에서는 grouped matmul이 그룹 오프셋(group offsets)을 CPU로 복사하고 그룹별로 루프를 돌렸습니다. 새로운 Metal 커널은 사용 가능한 경우 Metal Performance Primitives를, 그렇지 않은 경우에는 별도의 Metal 경로를 사용하여 오프셋을 GPU에서 직접 읽어옵니다. 디코딩 중 흔한 케이스인 작은 그룹들 역시 자체 커널을 얻게 되었습니다.
예를 들어, 64개의 토큰은 16개, 32개, 16개의 그룹으로 분할될 수 있습니다:
import torch
import torch.nn.functional as F
tokens = torch.randn(64, 64, device="mps", dtype=torch.bfloat16)
...
디코딩 커널은 토큰 처리량에 직접적인 영향을 미칩니다. M3 Ultra에서 torch.compile을 사용할 경우, MoE 모델이 초당 약 1.3배 더 많은 토큰을 생성합니다:
복잡한 텐서 작업이 더욱 쉬워졌습니다. complex64
LU 인수분해(factorization), LU 해법(solve) 및 삼각 해법(triangular solve)이 이제 MPS에서 실행되어 복소수 행렬식(complex determinants)과 같이 이를 기반으로 구축된 연산과 기울기까지 활성화합니다. GPU에서 직접 해결되는 작은 시스템을 소개합니다:
import torch
a = torch.randn(8, 8, device="mps", dtype=torch.complex64)
a = a + 8 * torch.eye(8, device="mps", dtype=torch.complex64)
...
작은 행렬도 빠른 커널을 갖게 되었습니다. 카메라 기하학 및 이미지 변환의 경우, 인수분해와 해법을 결합한 단일 Metal 커널에서 최대 4 × 4 크기의 FP32 행렬 배치를 역변환합니다. 더 큰 문제들도 주목받고 있습니다: QR은 블록 업데이트를 사용하며, 삼각 해법은 작업의 상당 부분을 효율적인 행렬 곱셈으로 전환합니다.
Gated DeltaNet 워크로드는 최소 여덟 개의 우측 항(right-hand sides)을 가진 16 × 16, 32 × 32 및 64 × 64 FP32 삼각 시스템 배치에 대한 전용 고속 경로를 얻습니다.
SVD도 수치적 극단값에서 더 견고해졌습니다. 이제 매우 작은 특이값(singular values), 매우 크거나 매우 작은 입력 크기, 그리고 full_matrices=True를 사용한 기저 완성(basis completion)을 올바르게 처리합니다.
이는 결과적으로 유사 역행렬(pseudoinverse) 및 최소 제곱(least-squares) 결과를 더 신뢰할 수 있게 만듭니다.
오디오 모델, 비디오 디코더 및 훈련 루프는 각각 비용이 많이 드는 연산들을 가지고 있습니다. 이번 릴리스에서는 이들 중 여러 개에 Metal을 통한 새로운 경로를 제공합니다:
| Operation | 2.15에서 변경된 사항 |
|---|
conv1d forward | 전문화된 커널이 MPSGraph를 대체하며, 짧은 출력을 위한 im2col-plus-matmul 경로가 추가되었습니다. 오디오 및 스트리밍 모델 형태에 유용합니다. |
| Constant padding | Metal 커널로 이동하여 타일형 비디오 VAE 디코딩 시 발생하던 과도한 드라이버 메모리 증가 문제를 해결했습니다 (아래 참조). |
| Reflection and replication padding | 더 빠르고 역방향 정확도를 수정하는 새로운 Metal 구현이 추가되었습니다. |
| Out-of-place dropout | nn.Dropout과 F.dropout이 이제 기존의 융합(fused) 커널을 사용합니다. |
| Upsampling backward | Metal gather 커널이 MPSGraph resize-gradient 경로를 대체했습니다. |
Channels-last conv2d backward | 비용이 많이 드는 스트라이드 접근을 가진 NCHW 그래프 대신 효율적인 channels-last 경로를 유지합니다. |
Token 샘플링도 2.15에서 더 빨라졌습니다. softmax와 log_softmax가 전용 Metal 커널로 실행되어, 대규모 어휘 크기를 사용하는 경우 가장 큰 이점을 얻을 수 있습니다: [1, 128256] 로짓 행에 대한 FP32 softmax는 57 µs에서 7 µs로 감소하여, MPSGraph 버전보다 약 8배 빠릅니다. 이러한 속도 향상은 다른 일반적인 어휘 크기에서도 유지됩니다:
이번 릴리스에서 가장 두드러진 결과 중 하나는 상수 패딩 변경에서 나옵니다. Wan-AI/Wan2.2-TI2V-5B-Diffusers의 VAE 디코드는 전체 28개 타일 디코드 과정 동안 8,764개의 패딩 호출을 수행합니다. Metal을 사용한 패딩은 최대 드라이버 메모리를 약 270 GiB에서 8 GiB로 낮춥니다:
| Memory metric | MPSGraph padding | Metal padding |
|---|
| Current tensor-allocation peak | 4.422 GiB | 4.095 GiB |
| ... | | |
이제 Python에서 어떤 MPS 스트림이 작업을 실행할지 직접 선택할 수 있습니다. torch.mps.Stream과 torch.mps.stream 컨텍스트 관리자가 인터페이스를 제공하며, Tensor.record_stream을 사용하면 할당기가 다른 스트림에서의 텐서 사용을 추적하도록 할 수 있습니다. | | |
별도의 스트림으로 작업을 보내고 결과를 읽기 전에 동기화하는 작은 예시가 있습니다:
import torch
x = torch.randn(1024, device="mps")
torch.mps.synchronize()
...
여러 개의 추가 연산이 이제 MPS에서 실행됩니다: torch.frexp
, torch.logspace,
, torch.take
그리고 Tensor.put_,
, torch.heaviside
, torch.special.laguerre_polynomial_l
그리고 부호 없는 정수 비교입니다. logspace는 아직 요소가 하나를 초과하는 정수 또는 불리언 출력을 지원하지 않습니다.
어텐션(Attention)도 정확성 수정이 이루어졌습니다: 스케일드 닷-프로덕트 어텐션(scaled dot-product attention)의 MPS 수학 구현이 이제 배치 및 헤드 브로드캐스팅을 올바르게 처리합니다.
2.16에 대한 작업은 이미 순조롭게 진행 중입니다. 아래 변경 사항 중 일부는 main 브랜치에 반영되어 나이틀리(nightlies)에서 사용할 수 있으며, 다른 것은 아직 검토 중이므로 출시 전에 세부 사항이 변경될 수 있습니다.
모델의 디코드 경로가 더 이상 MPSGraph 연산을 포함하지 않게 되면, GPU는 CPU가 모든 커널 인코딩을 마칠 때까지 토큰당 작동을 시작합니다. PyTorch 2.16은 매 16개 커널마다 명령 버퍼(command buffer)를 커밋하므로, CPU가 나머지 부분을 계속 인코딩하는 동안에도 GPU가 작업할 수 있습니다. 컨텍스트 길이 2048에 대한 컴파일된 BF16 디코딩의 경우:
| 모델 | 이전 (Before) | 이후 (After) | 변화 (Change) |
|---|
| LFM2.5-8B-A1B | 108.8 tokens/s | 126.0 tokens/s | +16% |
| ... | | | |
스케일드 닷-프로덕트 어텐션은 MPSGraph를 완전히 제거하고 있습니다: 모든 모양(shape)이 Metal 커널에서 실행되며, 디코딩을 위한 전용 벡터 커널과 최신 macOS 버전을 위한 하프-프리시전 프리필(half-precision prefill)에 대한 Metal Performance Primitives가 사용됩니다. 이전에 MPSGraph로 폴백하던 모양들이 가장 큰 혜택을 받습니다. M5 Pro에서 그룹화된-쿼리 어텐션(grouped-query attention, 32 쿼리 헤드, 8 KV 헤드, 헤드 차원 80, 컨텍스트 길이 4K)을 사용한 디코딩 속도는 465 µs에서 65 µs로 떨어지며, 헤드 차원 40의 4K × 4K 프리필은 42 ms에서 6 ms로 감소합니다.
MPSGraph로부터의 이탈은 계속됩니다. 배치 정규화(Batch norm) 순전파 및 역전파가 진행 중이며, MSE 손실(작은 입력에서 최대 12배 빠름), std_mean와 var_mean(최대 4배 빠름), 그리고 bincount는 이미 반영되었습니다. bincount가 가장 극적인 변화를 보입니다: 대부분의 값이 단일 빈에 속하는 경우, 4백만 개의 정수를 세는 작업은 이전에는 6.8초가 걸렸지만 이제는 반 밀리초 만에 완료됩니다.
번들된 셰이더(shaders)는 기본적으로 Metal 3.1용으로 빌드되며, Metal 4 버전은 필요한 커널에 대해서만 컴파일됩니다. 이로 인해 libtorch_cpu.dylib의 크기가 약 25% 감소하여 384 MiB에서 287 MiB가 되며, 더 빠른 matmul과 네이티브 FP8 행렬 곱셈을 지원하는 Metal 4.1 셰이더를 위한 기반을 마련합니다.
PyTorch 2.15는 Mac에 FP8을 도입하고, 밀집(dense) 및 MoE 모델의 LLM 디코딩 속도를 높이며, GPU에서 더 많은 선형 대수 연산을 제공하고, MPSGraph에서 전용 Metal 커널로 이동된 방대한 양의 연산들을 포함합니다. 릴리스 후보 버전을 사용해 보세요!
더 많은 MPS: 2.16에서는 어텐션(attention) 전체를 Metal에서 실행할 것이며, 우리는 계속해서 연산을 MPSGraph에서 제거하는 작업을 진행하고 있습니다. 그래프 브레이크(graph breaks)를 없애는 데 주력하여 더 많은 모델이 end to end로 컴파일되도록 할 것입니다. torch.compile
여러분의 아이디어: PyTorch에서 무엇을 더 개발해야 할까요? MPS 또는 다른 곳에서 느리거나, 누락되었거나, 작동하지 않는 부분이 있다면 GitHub에 이슈를 열어 알려주세요.
이 작업의 상당 부분을 검토해 준 Nikita Shulga, Kurt Mohler, Aaron Gokaslan에게 그리고 이 릴리스에서 MPS에 기여한 모든 분들께 큰 감사를 전합니다.