본문 바로가기
C.W.K.
Stream
Lesson 07 of 07 · published

양자화는 공간과 속도를 함께 사

~14 min · llm-physics, quantization, 4-bit, bits-per-weight, fp4, no-quality-scores

Level 0스펙 시트 훑는 사람
0 XP0/91 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete
"모델이 들어가는지 정하는 바이트가 얼마나 빨리 도는지도 정해. 그걸 줄이면 둘 다 얻고, 이 퀘스트가 값을 매기길 거부하는 화폐로 값을 치러."

레버 하나, 효과 둘

16비트 부동소수점으로 학습된 모델은 가중치당 2바이트를 저장해. 양자화는 더 적게 저장해. 실험이 돌린 모든 체크포인트에서 가중치당 4비트. 작은 가중치 그룹마다 스케일과 오프셋을 더 높은 정밀도로 두고, 가중치 자체는 작은 정수로 채워 넣는 방식이야. 메모리 트랙은 들어가느냐가 바이트로 정해진다고 보여줬고, 앞 레슨들은 디코드가 토큰당 바이트로 정해진다고 보여줬어. 그건 같은 바이트야. 그러니 양자화는 두 숫자를 한꺼번에 움직여. bf16의 27B 모델은 54.7 GB라서 24 GB 맥에도 32 GB 카드에도 못 올라가. 4비트면 16.05 GB고, 같은 버스에서 디코드 상한이 3.4배 높아.

파일에서 읽은 것

코드 블록은 체크포인트의 safetensors 헤더에서 가중치 수를 세고(채워 넣은 4비트 가중치는 32비트 워드 하나에 값 여덟 개) 파일 크기를 그 수로 나눠.

체크포인트형식 (config)파라미터파일실효 가중치당 비트bf16 대비 축소증거
Qwen3.5-27B-4bit4비트 affine, 그룹 6427.36 B16.05 GB4.693.41배헤더에서 나온 물리
Qwen3.5-9B-4bit4비트 affine, 그룹 649.41 B5.95 GB5.063.16배물리
Llama-3.2-1B-Instruct-4bit4비트, 그룹 641.24 B0.70 GB4.503.56배물리

실효 수치가 4 위인 건 두 가지 이유고, 가정하지 말고 직접 볼 만해. 가중치 64개 그룹마다 16비트 스케일과 16비트 바이어스가 붙어. 가중치당 0.5비트 오버헤드고, Llama의 4.50이 정확히 그거야. 그리고 체크포인트의 일부는 더 높은 정밀도로 남겨. 9B의 안 묶인 임베딩과 비전 인코더가 그걸 5.06으로 밀어. 그러니 '4비트'는 지배적인 텐서들의 이름이고, 나머지에 대한 진실은 파일이야.

2026년의 지형

양자화는 더는 다운로더의 뒷생각만이 아니야. NVIDIA의 Blackwell 하드웨어는 FP4 형식(해당 아키텍처와 함께 도입된 'NVFP4'. FP8은 Hopper와 Blackwell 둘 다)으로 네이티브 계산해. 이 집이 저장한 체크포인트 둘은 만든 쪽이 양자화해서 배포했어. DeepSeek-V4.1-Flash는 FP4 전문가와 FP8 어텐션으로 510 GB 파일인데 bf16이면 1테라바이트를 넘었을 거고, Kimi K3는 양자화 인지 학습에서 나온 MXFP4 전문가로 1.56 TB야. 애플 자체 온디바이스 모델은 "Apple GPU 안의 전용 하드웨어 부품"이 디코딩하는 압축 방식을 써. 맥에선 MLX가 여러 비트 폭과 그룹 크기를 지원하고, mlx-community가 실험이 쓴 4비트 파일을 공개해. 방향은 분명해. 가중치당 바이트는 후처리 선택이 아니라 모델의 설계 매개변수가 되어 가고 있어.

이 퀘스트가 말하지 않을 것

모든 양자화는 모델 출력에서 뭔가를 치르고, 그 비용의 크기는 모델, 방법, 비트 폭, 작업, 프롬프트에 달렸어. 거기 숫자를 붙이면 품질 점수가 되고, 이 퀘스트엔 그게 없어. 이 집의 규칙은, 다른 데서 재고 닫은 건데, 품질은 스캐너로 순위 매길 수 있는 게 아니라는 거야. 퀘스트가 말하는 건 물리야. 4비트 파일은 bf16 파일이 못 들어가는 데 들어가고, 같은 버스에서 3.5배쯤 빠르게 디코드하고, 함대 사다리의 모든 모델이 24 GB Air에서 아예 돌 수 있었던 이유야. 답이 네 일에 충분히 좋은지는 네 작업에서 답을 읽어서 판단하는 거고, 그 판단의 정직한 라벨은 네 것이야.

Code

quant_bits.py — safetensors 헤더에서 실효 가중치당 비트·python
#!/usr/bin/env python3
"""Effective bits per weight of a quantized checkpoint, from the safetensors
headers: packed uint32 weights hold 32/bits values each; the scales and biases
per group are the overhead that turns 4 bits into ~4.5."""
import json, struct, sys
from pathlib import Path
from huggingface_hub import snapshot_download

repo = sys.argv[1] if len(sys.argv) > 1 else "mlx-community/Qwen3.5-27B-4bit"
path = Path(snapshot_download(repo, local_files_only=True))
cfg = json.loads((path / "config.json").read_text())
q = cfg.get("quantization") or cfg.get("text_config", {}).get("quantization") or {}
bits = q.get("bits", 16)

params = bytes_total = 0
for f in sorted(path.glob("*.safetensors")):
    with f.open("rb") as fh:
        header = json.loads(fh.read(struct.unpack("<Q", fh.read(8))[0]))
    for name, meta in header.items():
        if name == "__metadata__":
            continue
        n = 1
        for d in meta["shape"]:
            n *= d
        bytes_total += meta["data_offsets"][1] - meta["data_offsets"][0]
        if name.endswith(".weight") and meta["dtype"] == "U32":
            params += n * (32 // bits)          # packed quantized weights
        elif name.endswith((".scales", ".biases")):
            pass                                # quantization overhead: bytes, not weights
        else:
            params += n                          # unquantized tensors (norms, some embeddings)

print(f"{repo}: {q or 'unquantized'}")
print(f"  parameters {params/1e9:.2f} B, file {bytes_total/1e9:.2f} GB -> {bytes_total*8/params:.2f} bits per weight overall")
print(f"  bf16 would be {params*2/1e9:.1f} GB; this file is {params*2/bytes_total:.2f}x smaller -> ceiling {params*2/bytes_total:.2f}x higher")

# office, 2026-09-15:
# Qwen3.5-27B-4bit: 27.36 B params, 16.05 GB -> 4.69 bits/weight; bf16 54.7 GB; 3.41x
# Qwen3.5-9B-4bit:   9.41 B params,  5.95 GB -> 5.06 bits/weight; bf16 18.8 GB; 3.16x
# Llama-3.2-1B-4bit: 1.24 B params,  0.70 GB -> 4.50 bits/weight; bf16  2.5 GB; 3.56x

External links

Exercise

가진 모든 체크포인트에 quant_bits.py를 돌려. 4비트와 8비트(또는 bf16) 빌드가 둘 다 있는 모델 하나에 대해 상한 비율을 계산하고, 실험의 사다리 스크립트로 둘 다 재서 진짜 비율을 기록해. 마지막으로 출력에서 눈치챈 차이 하나를 숫자 없이 말로 한 문장 써.
Hint
상한 비율은 바이트 ÷ 바이트야. 실측 비율은 작은 모델에서 더 작을 거야. 고정 오버헤드는 가중치와 함께 안 줄어드니까. 출력에 대한 문장이 중요한 부분이고, 어떤 스크립트도 대신 써줄 수 없는 부분이야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.