transformers가 llama.cpp 양자화를 그대로 돌린다 — GGUF 네이티브 지원
Hugging Face 블로그의 「Transformers now runs llama.cpp quants」(Marc Sun·Arthur Zucker·Lysandre, 2026-09-22)를 정리했다. 로컬 추론은 llama.cpp 계열(Ollama·LM Studio)이, 학습·평가는 transformers가 맡던 분업이 있었는데 그 경계가 흐려지는 변화다. 며칠 전 정리한 RTX 로컬 LLM 가이드가 도구별 생태계를 훑었다면, 이번 건은 두 생태계가 같은 파일을 공유하기 시작한 이야기다. (이 글의 초안은 설치된 Hermes 에이전트가 작성했고, Claude가 원문·차트 대조 후 전면 재작성해 발행했다.)
TL;DR: transformers가 GGUF를 네이티브 지원한다.
from_pretrained(model_id, gguf_file="...")한 줄이면 llama.cpp 양자화 체크포인트가 그대로 로드된다. 내부에서 ggml Metal 커널을 재사용해 Apple Silicon에서 llama.cpp와 대등한 속도를 내고(Qwen3.5-4B 70.4 vs 71.8 tok/s), Qwen3.8-27B에서는 15.9 대 13.4로 오히려 앞선다. 다만 패킹된 추론 경로는 현재 MPS 전용이고 지원 아키텍처도 Qwen3.5·3.8 계열로 한정된다.
한 줄로 끝나는 로딩
GGUF는 llama.cpp 팀이 만든 로컬 추론용 단일 파일 포맷이다. 가중치·토크나이저·챗 템플릿이 한 파일에 들어 있고 Q4_K_M 같은 양자화 레벨로 메모리와 품질을 맞바꾼다. Hub에는 이미 수만 개의 GGUF 체크포인트가 올라와 있다.
이제 그 파일을 transformers가 직접 읽는다.
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "unsloth/Qwen3.5-4B-GGUF"
filename = "Qwen3.5-4B-Q4_K_M.gguf"
tokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)
model = AutoModelForCausalLM.from_pretrained(model_id, gguf_file=filename)
가중치가 Metal에서 패킹된 채 유지되면 ggml-org/ggml-attn 어텐션 커널이 자동으로 붙고, 조건이 안 맞으면 sdpa로 폴백한다. 서빙도 CLI 한 줄로 OpenAI 호환 엔드포인트가 뜬다.
transformers serve "unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf"
속도 — 대등하고, 한 곳에서는 앞선다
이 글에서 제일 눈에 띄는 대목이다. MacBook Pro M2 Max(32GB, macOS 26.6, PyTorch 2.12.1, kernels 0.17.0) 측정값이다.
| 모델 | transformers | llama.cpp |
|---|---|---|
| Qwen3.5-4B (Q4_K_M, 2.74GB) | 70.4 tok/s | 71.8 ± 0.4 |
| Qwen3.8-27B (UD-Q4_K_M, 16.5GB) | 15.9 tok/s | 13.4 ± 0.9 |
| Qwen3.5-35B-A3B (UD-IQ4_XS, 16.3GB) | 60.2 tok/s | 61.3 ± 0.5 |
4B와 35B-A3B는 오차 범위에 가깝게 붙었고, 27B에서는 transformers가 더 빠르다. 다만 측정 조건이 같지 않다는 점은 감안해야 한다 — transformers는 12토큰 프롬프트에서 128토큰을 생성하며 prefill을 포함한 값이고, llama.cpp는 llama-bench tg128의 디코딩 전용 값이다. 조건이 불리한 쪽이 transformers인데도 대등하게 나왔다는 의미다.
속도의 출처 두 가지
하나, ggml 레이어 커널. 양자화 가중치를 풀지 않고 그대로 읽는 ggml-quantization, 정규화를 퓨전하는 ggml-norm, Metal 플래시 어텐션 ggml-attn, Qwen3.5·3.8 하이브리드 구조의 선형 어텐션을 처리하는 ggml-gated-delta-net, 그리고 MoE 라우팅용 자체 topk가 붙는다. 양자화 커널만 켠 상태와 전체 커널을 켠 상태를 비교하면 차이가 분명하다.
| 모델 | 양자화 커널만 | 전체 레이어 커널 | 배수 |
|---|---|---|---|
| Qwen3.5-4B | 44.2 | 70.4 | 1.59× |
| Qwen3.8-27B | 10.5 | 15.9 | 1.51× |
| Qwen3.5-35B-A3B | 28.8 | 60.2 | 2.09× |
둘, 생성 루프 자체의 개선. 패딩 없는 디코더 전용 입력에서 all-ones 어텐션 마스크를 생성 시작 시 버리고(#48814), 스토핑 검사를 비동기로 미뤄 CPU가 GPU를 기다리지 않게 했다(#47975).
| 모델 | 변경 전 | 변경 후 | 배수 |
|---|---|---|---|
| Qwen3.5-4B | 49.6 | 70.4 | 1.42× |
| Qwen3.8-27B | 13.7 | 15.9 | 1.16× |
| Qwen3.5-35B-A3B | 33.7 | 60.2 | 1.79× |
이 두 번째 변경은 GGUF와 무관하게 모든 transformers 모델의 생성 속도에 적용된다. 커널은 Apple Silicon 한정이지만 루프 개선은 전역이다.
파인튜닝까지 이어진다
GGUF를 읽어 들여 디퀀타이즈한 뒤 표준 학습 워크플로로 넘길 수도 있다.
model = AutoModelForCausalLM.from_pretrained(
"unsloth/Qwen3.5-4B-GGUF",
gguf_file="Qwen3.5-4B-Q4_K_M.gguf",
quantization_config=GgufConfig(dequantize=True),
dtype=torch.bfloat16,
)
같은 체크포인트로 추론·평가·파인튜닝을 한 생태계 안에서 끝낼 수 있다. 이번 통합에서 실제로 손에 잡히는 이득은 여기다.
제약 — 아직 좁다
- MPS 전용: 패킹된 추론 경로는 현재 Apple Silicon에서만 동작한다. 다른 디바이스는 디퀀타이즈 폴백이다.
- 패딩·배치 미완성: 패딩된 배치는 마스크 최적화를 못 받아 성능이 떨어진다.
generate_batch의 MPS 확장이 예정돼 있다. - 아키텍처 커버리지: Qwen3.5 dense/MoE와 호환되는 Qwen3.8까지다. 나머지는 차차 늘린다고 한다.
읽으면서 든 생각
- 벤치마크 표를 처음 봤을 때 “transformers가 llama.cpp의 90% 수준”쯤이려니 했는데 실제로는 대등하거나 앞섰다. 전용 C++ 런타임의 속도 우위가 커널만 제대로 빌려오면 사라진다는 게 이 글의 진짜 메시지로 읽힌다.
- 커널이 텐서 단위 연산이라 GGUF 포맷에 묶이지 않는다는 대목도 중요하다. 새 아키텍처가 llama.cpp 구현을 기다릴 필요 없이 transformers 쪽에서 먼저 가속될 수 있다는 뜻이다.
- 개인 개발 환경 관점에서는 M 시리즈 맥북에서 4B~35B급을 실사용 속도로 돌리면서, 평가·비교 코드를 표준 transformers API로 통일할 수 있게 됐다. 양자화 품질 검증(원본 대 GGUF 변환본)을 같은 코드로 돌린다는 것도 실무에서 꽤 쓸모 있다.
참고
- 원문: Transformers now runs llama.cpp quants
- transformers GGUF 문서 · kernels 라이브러리
- GGUF 스펙 (ggml)
- 관련 글: RTX PC에서 로컬 LLM 시작하기
- 타이틀 사진: Unsplash