로컬 LLM 파일 형식과 정밀도 입문
목표: Hugging Face 저장소에서
model-00001-of-00004.safetensors,Qwen3-8B-Q4_K_M.gguf,-AWQ,-FP8,pytorch_model.bin같은 이름을 봤을 때 무슨 포장에 어떤 정밀도로 담긴 파일인지 읽고, 내 컴퓨터에서 돌릴 파일을 고를 수 있기.
정밀도·메모리 계산의 GPU 쪽 배경은 GPU와 CUDA 입문 §5–6, 원격 서버에서 돌리는 법은 원격 GPU 서버 사용 입문, 터미널·가상환경은 컴퓨터 기본 도구와 개발 환경 입문에서 다룬다.
한 줄로 먼저
LLM 파일은 “포장(파일 형식)“과 “숫자 하나의 크기(정밀도·양자화)” 두 축으로 읽는다. GPU 서버용 원본은 safetensors + BF16/FP8, 개인 PC·Mac용은 GGUF + 4~8비트 양자화가 지금의 표준이다. pickle(.bin, .pt)은 열기만 해도 코드가 실행될 수 있어 밀려났다.
Qwen3-8B-Instruct-Q4_K_M.gguf
└─┬─┘ └┬┘ └──┬───┘ └──┬──┘ └┬─┘
모델 크기 용도 정밀도 포장
이름 (8B) (대화용) (4비트, (GGUF)
k-quant M)0. 무엇이 들어 있나: 가중치 + 구조 + 토크나이저
모델을 돌리려면 세 가지가 필요하다.
| 구성 요소 | 내용 | 원본 저장소의 파일 |
|---|---|---|
| 가중치(weights) | 학습으로 정해진 수십억 개의 숫자 | *.safetensors (또는 옛 *.bin) |
| 구조(config) | 층 수, hidden 크기, 헤드 수, 어떤 아키텍처인지 | config.json, generation_config.json |
| 토크나이저 | 글자를 토큰 ID로 바꾸는 규칙, chat template | tokenizer.json, tokenizer_config.json, chat_template.jinja |
chat template은 사용자 메시지를 모델이 학습 때 본 형식(<|im_start|>user ... 같은 특수 토큰 틀)으로 감싸는 규칙이다. 이게 틀리면 모델이 이상하게 대답한다.
포장 형식마다 이 셋을 따로 두느냐, 한 파일에 합치느냐가 다르다. safetensors는 가중치만 담고, GGUF는 셋을 한 파일에 담는다.
1. 파일 형식: 가중치를 담는 포장
pickle 계열: .bin, .pt, .pth, .ckpt
PyTorch가 기본으로 쓰던 저장 방식이다. torch.save는 Python의 pickle(Python 객체를 통째로 바이트로 바꾸는 표준 직렬화)을 쓴다.
- 문제: pickle은 “이 함수를 이 인자로 불러서 객체를 복원하라”는 명령을 담을 수 있다. 그래서 악의적인 파일은 여는 순간 임의 코드를 실행한다. Python 공식 문서도 “신뢰할 수 없는 데이터를 unpickle하지 말라”고 경고한다.
- 대응: PyTorch 2.6부터
torch.load의 기본값이weights_only=True가 됐다. 텐서와 기본 자료형만 복원하고 임의 객체는 거부한다. Hugging Face Hub도 업로드된 pickle 파일을 검사해 위험한 import를 표시한다. - 현재: 옛 저장소(
pytorch_model.bin)와 연구 코드 체크포인트에 남아 있다. 새 모델 배포에는 거의 쓰지 않는다.
safetensors: .safetensors
Hugging Face가 pickle을 대신하려고 만든 형식이다. 지금 새 모델 원본은 거의 다 이 형식이다.
[8바이트: 헤더 길이 N (little-endian u64)]
[N바이트: JSON 헤더]
{"model.layers.0.self_attn.q_proj.weight":
{"dtype": "BF16", "shape": [4096, 4096], "data_offsets": [0, 33554432]},
...,
"__metadata__": {...}}
[나머지: 텐서 바이트를 이어 붙인 덩어리]- 안전: 헤더는 JSON, 나머지는 숫자뿐이다. 코드가 들어갈 자리가 없어서 불러와도 아무것도 실행되지 않는다.
- 빠름: 헤더만 읽으면 각 텐서가 파일 어디에 있는지 안다. 그래서 mmap(파일을 메모리에 복사하지 않고 필요한 부분을 바로 읽는 OS 기능)으로 필요한 텐서만 읽을 수 있다. 여러 GPU에 나눠 올릴 때 각자 자기 몫만 읽는다.
- 샤딩(sharding): 큰 모델은 수 GB 단위 조각으로 나뉜다.
model.safetensors.index.json이 “이 텐서는 몇 번 조각에 있다”는 지도를 준다.
config.json
generation_config.json
model-00001-of-00004.safetensors
model-00002-of-00004.safetensors
model-00003-of-00004.safetensors
model-00004-of-00004.safetensors
model.safetensors.index.json
tokenizer.json
tokenizer_config.json- 가중치만 담는다. 구조와 토크나이저는 옆 파일에 있어야 한다. 그래서
.safetensors하나만 받아서는 못 돌린다. 폴더째 받는다(huggingface-cli download <repo>또는hf download <repo>). - LoRA 어댑터도 같은 형식(
adapter_model.safetensors+adapter_config.json)으로 배포된다. 기반 모델 위에 얹어 쓰는 작은 파일이다(LoRA).
GGUF: .gguf
llama.cpp 프로젝트가 2023년 8월에 만든 형식이다. 그 전의 GGML·GGJT 형식을 대체했다. 이름은 흔히 GPT-Generated Unified Format의 약자로 소개된다.
[magic "GGUF"][버전][텐서 개수][메타데이터 개수]
[메타데이터 key-value]
general.architecture = "qwen3"
qwen3.block_count = 36
qwen3.context_length = 40960
tokenizer.ggml.model = "gpt2"
tokenizer.ggml.tokens = [...]
tokenizer.chat_template = "{% for message in messages %}..."
general.file_type = ... (양자화 종류)
[텐서 정보: 이름, shape, 양자화 타입, 위치]
[정렬용 패딩]
[텐서 데이터]- 한 파일에 다 들어 있다. 구조, 토크나이저, chat template, 양자화된 가중치가 모두 이 파일 안에 있다.
.gguf하나만 받으면 돌아간다. - CPU·Apple Silicon에 강하다. llama.cpp가 CPU SIMD(한 명령으로 여러 숫자를 동시에 계산), Metal(Mac GPU), CUDA, Vulkan을 모두 지원한다. 일부 층만 GPU에 올리고 나머지는 CPU에 두는 것도 된다(
-ngl옵션, offload). - 쓰는 프로그램: llama.cpp, Ollama, LM Studio, koboldcpp, Jan 등.
- 만드는 법: 보통 원본 safetensors를 받아 llama.cpp의 변환 스크립트(
convert_hf_to_gguf.py)로 GGUF로 바꾸고,llama-quantize로 비트를 줄인다. 저장소 이름에-GGUF가 붙어 있으면 누군가 이 작업을 해서 올린 것이다. - 큰 모델은 GGUF도 나뉜다:
-00001-of-00003.gguf. 첫 조각만 지정하면 나머지를 같이 읽는다. - 비전 모델: 이미지 인코더를
mmproj-*.gguf로 따로 두는 경우가 많다. 본체 GGUF와 함께 받아야 이미지를 읽는다.
그 밖의 형식
| 형식 | 실체 | 쓰는 곳 |
|---|---|---|
| MLX | safetensors + MLX용 config. 양자화된 버전은 MLX 고유 방식 | Apple MLX, mlx-lm, LM Studio(Mac). mlx-community 저장소에 4bit/8bit 변환본이 많다 |
ONNX (.onnx) | 계산 그래프 + 가중치 | ONNX Runtime, 브라우저(WebGPU)·모바일·Windows 추론 |
| TensorRT-LLM engine | 특정 GPU·설정에 맞춰 미리 컴파일한 결과물 | NVIDIA 서버 추론. 다른 GPU로 옮기면 다시 빌드 |
| EXL2 / EXL3 | safetensors 안에 ExLlama 고유 양자화 | ExLlamaV2/V3 (NVIDIA GPU 전용) |
2. 정밀도: 숫자 하나에 몇 비트를 쓰나
가중치는 결국 실수 수십억 개다. 실수 하나를 몇 비트로 저장하느냐가 정밀도(precision) 다. 부동소수점은 비트를 지수(얼마나 큰 수까지 담나, 범위)와 가수(얼마나 세밀하게 담나, 정밀도)로 나눈다.
| 형식 | 바이트 | 지수 / 가수 | 최댓값 감 | 쓰임 |
|---|---|---|---|---|
| FP32 | 4 | 8 / 23 | 약 3.4 × 10³⁸ | 학습의 기준. 배포에는 거의 안 씀 |
| FP16 | 2 | 5 / 10 | 65504 | 옛 모델 배포, 추론. 값이 크면 inf로 넘침 |
| BF16 | 2 | 8 / 7 | FP32와 같음 | 요즘 원본 배포의 기본 |
| FP8 E4M3 | 1 | 4 / 3 | 448 | 최신 GPU 추론·학습. 가중치·활성값용 |
| FP8 E5M2 | 1 | 5 / 2 | 57344 | 범위가 더 필요할 때(주로 gradient) |
| FP4 (E2M1) | 0.5 | 2 / 1 | 6 | MXFP4·NVFP4처럼 블록 scale과 함께만 씀 |
FP16 vs BF16
같은 2바이트를 다르게 나눴다. FP16은 가수에 비트를 더 줬고, BF16은 지수에 비트를 더 줬다. 비유하면 FP16은 눈금이 촘촘하지만 짧은 자이고, BF16은 눈금이 성기지만 FP32만큼 긴 자다.
LLM은 활성값이 가끔 크게 튀기 때문에 범위가 넓은 BF16이 이겼다. BF16 원본을 FP16으로 바꿔 돌리면 대개 괜찮지만, 큰 값이 나오는 모델에서는 inf나 NaN이 생길 수 있다. 원본이 BF16이면 BF16 그대로 쓰는 것이 안전하다. 오래된 GPU(V100, T4 등)는 BF16 가속이 없어서 FP16으로 돌린다(GPU와 CUDA 입문 §4).
FP8과 FP4: 하드웨어가 따라와야 의미가 있다
- FP8은 NVIDIA Ada(RTX 40, L40S)·Hopper(H100) 이후 GPU가 하드웨어로 계산한다. DeepSeek-V3는 처음부터 FP8 혼합 정밀도로 학습해 FP8 가중치로 공개했다. 저장소 이름 끝의
-FP8이 이런 버전이다. - FP4는 숫자 하나로는 거의 아무것도 못 담는다(비트 패턴이 16개뿐이고 최댓값이 6). 그래서 블록 scale을 붙인다. 숫자 여러 개를 한 블록으로 묶고, 블록마다 공유 배율을 하나 둔다.
- MXFP4: OCP(Open Compute Project) Microscaling 표준. 32개 묶음마다 8비트 지수 scale(E8M0). OpenAI의 gpt-oss가 MoE 가중치를 MXFP4로 공개했다.
- NVFP4: NVIDIA Blackwell 세대 형식. 16개 묶음마다 FP8(E4M3) scale.
- FP8·FP4를 지원하지 않는 GPU나 CPU에서도 돌릴 수는 있다. 하지만 계산할 때 더 높은 정밀도로 풀어서 쓰므로 속도 이득이 없다. 메모리 절약만 남는다.
3. 양자화: 로컬에서 실제로 받는 파일
양자화(quantization) 는 학습이 끝난 가중치를 4~8비트 정수 등으로 줄여 저장하는 일이다. 기본 아이디어는 같다.
원래 값 (BF16): [0.12, -0.03, 0.40, -0.25, ...] (블록 하나, 예: 32개)
scale = 블록 최댓값 / 정수 최댓값
저장 값 (INT4): [2, -1, 7, -4, ...] + scale 하나
복원: 정수 × scale ≈ 원래 값블록을 작게 잡을수록 오차가 줄지만 scale 저장 비용이 는다. 그래서 “4비트 양자화”라도 실제 평균 비트는 4보다 조금 크다. 이것을 bpw(bits per weight, 가중치 하나당 평균 비트)로 표시한다.
GGUF 양자화 이름 읽는 법
Q4_K_M
│ │ │
│ │ └─ S / M / L : 중요한 층(attention 출력, FFN 일부 등)을 얼마나 높은 비트로 남겼나
│ └─── K : k-quant. 큰 블록(super-block) 안에 작은 블록을 두고 scale도 양자화
└───── Q4 : 기본 비트 수 4| 이름 | 평균 bpw (약) | 성격 |
|---|---|---|
F16 / BF16 | 16 | 양자화 안 함. 변환 중간 산출물 |
Q8_0 | 8.5 | 원본과 거의 구분 안 됨 |
Q6_K | 6.6 | 거의 손실 없음 |
Q5_K_M | 5.7 | 좋은 절충 |
Q4_K_M | 4.8–4.9 | 가장 많이 받는 기본값 |
Q4_0 | 4.5 | 옛 방식. 일부 하드웨어 가속 경로에서 쓰임 |
IQ4_XS | 4.3 | i-quant. 같은 크기에서 품질이 조금 더 좋음 |
Q3_K_M / IQ3_M | 3.7–3.9 | 메모리가 정말 모자랄 때 |
Q2_K / IQ2_XXS | 2–3 | 품질이 눈에 띄게 떨어짐. 아주 큰 모델을 억지로 올릴 때 |
bpw는 모델 구조와 llama.cpp 버전에 따라 조금씩 다르다. 정확한 값은 저장소의 파일 크기를 보는 것이 가장 확실하다.
- i-quant (
IQ*): 가중치 묶음을 미리 정한 격자(코드북)의 점으로 근사하는 방식이다. 3비트 이하에서 k-quant보다 품질이 낫다. CPU에서는 k-quant보다 조금 느릴 수 있다. - imatrix(importance matrix): 예시 텍스트를 모델에 통과시켜 “어떤 가중치가 출력에 크게 기여하는지”를 잰 통계다. 양자화할 때 중요한 가중치의 오차를 줄이는 데 쓴다. 저장소 설명에 “imatrix”가 있으면 이 과정을 거쳤다는 뜻이다. 낮은 비트일수록 효과가 크다.
- 같은 이름이어도 만든 사람마다 다를 수 있다. imatrix에 쓴 데이터, llama.cpp 버전이 다르기 때문이다. bartowski, unsloth, 모델 제작사 공식 GGUF처럼 많이 쓰이는 저장소가 무난하다.
GPU용 양자화: 대부분 safetensors 안에 들어 있다
| 이름 | 방식 | 언제 양자화하나 | 쓰는 곳 |
|---|---|---|---|
| GPTQ | 층마다 2차 정보(Hessian 근사)로 오차를 보정하며 순서대로 양자화 | 미리 (보정 데이터 필요) | vLLM, transformers, ExLlama |
| AWQ | 활성값이 큰 채널의 가중치를 보호하도록 scale을 조정 | 미리 | vLLM, transformers |
| bitsandbytes (INT8, NF4) | LLM.int8()은 이상값 채널만 FP16으로 분리. NF4는 정규분포에 맞춘 4비트 | 불러오는 순간 (load_in_4bit=True) | transformers, QLoRA 미세조정 |
| FP8 / MXFP4 / NVFP4 | 부동소수점 저비트 + scale | 미리, 또는 공식 배포 | vLLM, SGLang, TensorRT-LLM |
저장소 이름 끝의 -AWQ, -GPTQ-Int4, -FP8, -bnb-4bit가 이 표시다. config.json 안의 quantization_config 항목을 보면 정확한 방식이 적혀 있다.
4. 용량 어림 계산
파일 크기 ≈ 파라미터 수 × bpw ÷ 8 바이트. 8B(80억) 모델이면 대략 이렇다.
| 형식 | 계산 | 크기 (약) |
|---|---|---|
| BF16 | 8 × 10⁹ × 16 ÷ 8 | 16 GB |
| Q8_0 | 8 × 10⁹ × 8.5 ÷ 8 | 8.5 GB |
| Q6_K | 8 × 10⁹ × 6.6 ÷ 8 | 6.6 GB |
| Q4_K_M | 8 × 10⁹ × 4.85 ÷ 8 | 4.9 GB |
| IQ3_M | 8 × 10⁹ × 3.7 ÷ 8 | 3.7 GB |
실제로 돌릴 때는 파일 크기보다 메모리가 더 든다.
- KV cache: 앞 토큰의 key·value를 저장하는 공간. 문맥 길이에 비례해 커진다. 계산 예는 GPU와 CUDA 입문 §6에 있다. llama.cpp·Ollama는 기본 문맥 길이를 모델 최대치보다 작게 잡는 경우가 많다. 문맥을 늘리면 메모리가 크게 는다. KV cache도 8비트·4비트로 줄이는 옵션이 있다.
- 계산용 버퍼와 런타임: 수백 MB~수 GB.
- MoE 모델: 전체 파라미터와 한 토큰에 쓰는 파라미터(active)가 다르다. 예를 들어 “30B-A3B”는 전체 30B, 토큰당 3B를 쓴다는 뜻이다. 메모리는 전체 30B 기준으로 필요하고, 속도는 3B 모델에 가깝다.
어림 기준: 파일 크기 + 1~3 GB(짧은 문맥 기준) 가 비어 있어야 한다.
5. 내 환경에서 무엇을 고르나
| 환경 | 추천 | 프로그램 |
|---|---|---|
| Mac (Apple Silicon) | GGUF Q4_K_M~Q8_0, 또는 MLX 4bit/8bit | Ollama, LM Studio, llama.cpp, mlx-lm |
| NVIDIA GPU, VRAM 넉넉 | 원본 safetensors BF16, 최신 GPU면 FP8 | vLLM, SGLang, transformers |
| NVIDIA GPU, VRAM 부족 | AWQ/GPTQ 4bit, 또는 GGUF(일부 CPU offload) | vLLM, llama.cpp, Ollama |
| CPU만 | GGUF Q4_K_M 이하, 작은 모델(1~8B) | llama.cpp, Ollama |
| 미세조정할 때 | 원본 safetensors (QLoRA면 불러올 때 4bit) | transformers + PEFT, Unsloth |
- Mac 통합 메모리: CPU와 GPU가 같은 메모리를 쓴다. 기본 설정에서 GPU가 쓸 수 있는 양은 전체의 약 2/3
3/4로 제한된다. 16 GB Mac이면 8B Q4Q6, 32 GB면 14~32B Q4, 64 GB 이상이면 70B Q4 정도가 감이다. - “같은 메모리라면 큰 모델의 낮은 비트 > 작은 모델의 높은 비트” 라는 경험칙이 있다. 다만 3비트 아래로 내려가면 이 관계가 깨지기 쉽다.
- 서버 엔진(vLLM 등)은 원래 safetensors 계열 모델을 기본으로 쓰고, GGUF 지원은 실험적이거나 제한적이다. 반대로 Ollama·LM Studio는 GGUF가 기본이다. 엔진을 먼저 정하고, 그 엔진이 읽는 형식을 받는다.
6. 보안: 어떤 파일이 코드를 실행하나
| 대상 | 코드 실행 위험 |
|---|---|
.bin, .pt, .pth, .ckpt (pickle) | 있음. 출처를 모르면 열지 않는다. torch.load(..., weights_only=True)로 연다 |
.safetensors, .gguf | 형식 자체에는 없음. 다만 파서 버그는 가끔 보고되므로 프로그램을 최신으로 유지한다 |
trust_remote_code=True | 있음. 저장소에 있는 .py 파일(모델 코드)을 그대로 실행한다. 파일 형식과 무관하다 |
| GGUF 안의 chat template | Jinja 템플릿이다. 실행 환경이 샌드박스로 처리하지만 템플릿 처리 취약점이 보고된 적이 있다 |
trust_remote_code는 새 아키텍처라 transformers에 아직 코드가 없을 때 필요하다. 공식 제작사 저장소인지 확인하고 켠다. 이름이 비슷한 가짜 저장소를 조심한다.
7. 아주 짧은 암기
- 두 축으로 읽는다: 포장(파일 형식) × 숫자 크기(정밀도·양자화).
- pickle(
.bin,.pt)은 열면 코드가 돌 수 있다. safetensors·GGUF는 데이터뿐이다. - safetensors = 가중치만. 폴더째 받는다. GGUF = 한 파일에 전부.
- 원본 배포는 BF16. BF16은 FP32의 범위, FP16은 더 촘촘하지만 65504에서 넘친다.
- FP8·FP4는 지원 GPU에서만 빨라진다. 아니면 메모리만 준다.
Q4_K_M= 4비트 k-quant 중간 크기, 약 4.8 bpw. 로컬의 기본값.IQ*는 저비트에서 더 좋은 i-quant. imatrix는 중요 가중치 통계.-AWQ,-GPTQ,-FP8은 GPU 서버용.-GGUF는 llama.cpp 계열용.- 파일 크기 ≈ 파라미터 수 × bpw ÷ 8. 실행에는 KV cache가 더 든다.
- MoE는 메모리는 전체, 속도는 active 파라미터 기준.
trust_remote_code=True는 남의 Python 코드를 실행한다.
입문자가 조심할 점
.safetensors하나만 받아서 돌리려 하지 않는다.config.json과 토크나이저 파일이 같이 있어야 한다.- GGUF 변환본의 chat template이 틀린 경우가 있다. 대답이 이상하거나 특수 토큰이 그대로 출력되면 공식 저장소의 template과 비교한다. 모델 출시 직후의 변환본은 llama.cpp가 아직 그 아키텍처를 완전히 지원하지 않아서 다시 올라오는 경우가 많다.
- 같은
Q4_K_M이라도 저장소마다 품질이 다를 수 있다. 만든 사람, 날짜, imatrix 여부를 본다. - 벤치마크 점수는 대개 원본 BF16 기준이다. 양자화 버전은 특히 수학·코드·긴 문맥에서 점수가 더 떨어질 수 있다. 실험 결과를 남길 때는 정확한 파일 이름(양자화 종류 포함)과 엔진 버전을 함께 적는다.
- “FP16”과 “BF16”을 같은 것으로 취급하지 않는다. 둘 다 2바이트지만 넘침 특성이 다르다.
- 라이선스를 확인한다. 가중치가 공개돼 있어도 상업 이용·재배포 조건이 모델마다 다르다. 변환본도 원본 라이선스를 따른다.
출처
- Python Docs, pickle — Python object serialization (신뢰할 수 없는 데이터 경고). https://docs.python.org/3/library/pickle.html
- PyTorch Docs, torch.load (
weights_only). https://pytorch.org/docs/stable/generated/torch.load.html - Hugging Face, Pickle Scanning (Hub 보안). https://huggingface.co/docs/hub/security-pickle
- Hugging Face, safetensors documentation & repository. https://huggingface.co/docs/safetensors · https://github.com/huggingface/safetensors
- ggml, GGUF specification. https://github.com/ggml-org/ggml/blob/master/docs/gguf.md
- Hugging Face, GGUF (Hub 문서, 양자화 타입 목록). https://huggingface.co/docs/hub/gguf
- llama.cpp repository (변환 스크립트,
llama-quantize, imatrix). https://github.com/ggml-org/llama.cpp - Apple, MLX / mlx-lm. https://github.com/ml-explore/mlx · https://github.com/ml-explore/mlx-lm
- Kalamkar, D. et al. (2019). A Study of BFLOAT16 for Deep Learning Training. https://arxiv.org/abs/1905.12322
- Micikevicius, P. et al. (2022). FP8 Formats for Deep Learning. https://arxiv.org/abs/2209.05433
- Open Compute Project (2023). OCP Microscaling Formats (MX) Specification v1.0. https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf
- Dettmers, T. et al. (2022). LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale. https://arxiv.org/abs/2208.07339
- Frantar, E. et al. (2022). GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers. https://arxiv.org/abs/2210.17323
- Lin, J. et al. (2023). AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration. https://arxiv.org/abs/2306.00978
- Dettmers, T. et al. (2023). QLoRA: Efficient Finetuning of Quantized LLMs (NF4). https://arxiv.org/abs/2305.14314
- DeepSeek-AI (2024). DeepSeek-V3 Technical Report (FP8 학습·배포). https://arxiv.org/abs/2412.19437
- OpenAI (2025). gpt-oss-120b & gpt-oss-20b Model Card (MXFP4 배포). https://arxiv.org/abs/2508.10925