10. 모델 파일: 포맷, 양자화, 서빙 구조
ollama가 모델을 어떻게 로드하는지. 양자화가 뭐고 어떤 레벨을 선택해야 하는지. GGUF vs SafeTensors. ollama의 내부 서빙 구조.
9편에서 하드웨어와 VRAM 계산법을 다뤘다. 이번 편에서는 모델 파일 자체를 파고든다. ollama가 모델을 어떻게 로드하고, 양자화가 뭐고, 어떤 레벨을 선택해야 하는지.
모델 파일 포맷
ollama에서 ollama pull qwen2.5:7b를 실행하면 모델 파일을 받는다. 이 파일의 포맷이 뭔지 알아두면 나중에 HuggingFace에서 직접 모델을 받을 때 유용하다.
-- 주요 포맷 --
GGUF: ollama/llama.cpp가 사용하는 포맷.
CPU/GPU 혼합 추론 지원. 양자화 포함.
로컬 LLM의 사실상 표준.
SafeTensors: HuggingFace 표준.
PyTorch/Transformers에서 직접 로드.
보통 FP16/BF16. 학습/파인튜닝에 사용.
ONNX: 범용 추론 포맷. 다양한 런타임 지원.대부분의 경우 ollama를 쓰면 GGUF를 쓰게 된다. HuggingFace에서 직접 모델을 받아서 파인튜닝하려면 SafeTensors를 쓴다.
config.json — 모델의 설계도
모델 파일에 같이 딸려오는 config.json에 모델의 구조가 전부 적혀 있다. 이걸 읽을 줄 알면 모델의 크기, 성능, VRAM 사용량을 파일을 받기 전에 판단할 수 있다. HuggingFace에서 모델을 고를 때 이 파일부터 열어보는 습관을 들이면 시행착오가 확 줄어든다.
Qwen2.5-7B의 config.json (핵심 필드만):
{
"hidden_size": 3584, <- 벡터 차원 (2편 임베딩)
"num_hidden_layers": 28, <- 레이어 수 (5편 조립)
"num_attention_heads": 28, <- 어텐션 헤드 수 (3편)
"num_key_value_heads": 4, <- GQA용 KV 헤드 수
"intermediate_size": 18944, <- FFN 확장 차원 (4편)
"vocab_size": 152064, <- 어휘 사전 크기 (2편)
"max_position_embeddings": 131072, <- 최대 컨텍스트
"torch_dtype": "bfloat16" <- 원본 정밀도
}
이 숫자들로 파라미터 수를 대략 계산할 수 있다:
어텐션: 3584 x 3584 x 4 (Q,K,V,O) x 28레이어 = ~14.4억
FFN: 3584 x 18944 x 3 (W1,W_gate,W2) x 28레이어 = ~57억
임베딩 + 출력: 152064 x 3584 x 2 = ~10.9억
합계: ~72억 = 약 7Bnum_key_value_heads: 4는 GQA(Grouped-Query Attention)를 뜻한다. 어텐션 헤드 28개가 KV 헤드 4개를 공유한다. 헤드 7개가 같은 K, V를 쓰는 거다. KV 캐시의 VRAM 사용량을 28/4 = 7배 줄이는 효과가 있다. 6편에서 계산한 KV 캐시 크기가 실제로는 GQA 덕분에 훨씬 작아진다.
HuggingFace에서 모델 고르는 법
ollama에서 ollama pull로 받는 모델은 이미 양자화가 되어 있어서 편하지만, 선택지가 제한적이다. HuggingFace에서 직접 모델을 찾으면 더 다양한 옵션이 있다.
HuggingFace에서 GGUF 모델 찾기:
1. 모델 검색: "qwen2.5 7b gguf" 또는 "llama 8b gguf"
2. 파일 목록에서 양자화 레벨 선택:
qwen2.5-7b-instruct-q4_k_m.gguf <- 4.4GB, 가장 추천
qwen2.5-7b-instruct-q5_k_m.gguf <- 5.1GB
qwen2.5-7b-instruct-q8_0.gguf <- 7.7GB
3. ollama에서 사용:
ollama create my-model -f Modelfile
(Modelfile에 GGUF 경로 지정)
주의:
"instruct" = 지시 튜닝 완료 (대화용)
"base" = 사전 학습만 (문장 이어쓰기만 함, 대화 불가)
-> 대화용으로 쓰려면 반드시 instruct 버전.base 모델과 instruct 모델의 차이는 8편에서 다뤘다. base 모델은 사전 학습만 된 상태라 문장을 이어쓰기만 하고, instruct 모델은 지시 튜닝과 RLHF까지 거쳐서 질문에 답하는 형태가 된 거다.
양자화 — 파라미터를 압축하는 방법
양자화는 파라미터의 정밀도를 낮춰서 크기를 줄이는 거다. 7B 모델을 FP16 그대로 쓰면 14GB인데, Q4로 양자화하면 4.4GB가 된다. 숫자를 반올림하는 것과 비슷한데, 그러면 품질은 얼마나 떨어질까?
원래 W의 값: 0.0234375 (FP16, 16비트, 2바이트)
양자화 = 이 숫자의 정밀도를 낮춰서 크기를 줄이는 것.
FP16: 0.0234375 (16비트, 원본)
Q8: 0.023 (8비트, 거의 동일)
Q4: 0.02 (4비트, 약간 손실)
Q2: 0.0 (2비트, 많이 손실)
-- 양자화 레벨별 비교 (7B 모델) --
이름 비트 크기 품질
--------- ----- ------- --------
FP16 16bit 14 GB 원본
Q8_0 8bit 7 GB 거의 동일
Q6_K 6bit 5.5 GB 매우 좋음
Q4_K_M 4bit 4.4 GB 실용적 스윗스팟 <-- 가장 많이 사용
Q4_K_S 4bit 4.1 GB 약간 낮음
Q3_K_M 3bit 3.3 GB 눈에 띄는 저하
Q2_K 2bit 2.7 GB 많이 저하, 비추
실무 추천: Q4_K_M이 크기 대비 품질의 스윗스팟.Q4_K_M이 가장 많이 쓰인다. 크기는 FP16의 1/3이지만 품질 저하는 미미하다. Q8이면 거의 원본과 구분이 안 되지만 크기가 크고, Q2는 눈에 띄게 품질이 떨어진다.
실무에서 선택 기준은 단순하다. VRAM에 올라가는 가장 큰 모델의 가장 높은 양자화 레벨을 쓰면 된다. GTX 1060(6GB)이면 7B Q4_K_M이 거의 한계다. RTX 4090(24GB)이면 32B Q4_K_M이나 14B Q8이 가능하다.
ollama 서빙 구조 상세
ollama run qwen2.5:7b를 실행하면 내부에서 이런 일이 벌어진다.
$ ollama run qwen2.5:7b "로그 분석해줘: ..."
[1] 모델 로드 (최초 1회):
model.bin (4.4GB) -> VRAM에 적재
tokenizer.json (7MB) -> RAM에 적재
config.json 읽어서 모델 구조 파악
[2] 요청 처리:
+-- CPU ------------------------------------+
| 토크나이저: 텍스트 -> 토큰 ID 배열 |
| 특수 토큰 삽입 (<|im_start|> 등) |
+------------------+------------------------+
| 토큰 ID 배열 -> GPU 전송
v
+-- GPU ------------------------------------+
| Prefill: 입력 전체 처리, KV 캐시 생성 |
| Decode: 토큰 1개씩 생성, KV 캐시 누적 |
| (매 토큰마다 모든 W를 읽음) |
+------------------+------------------------+
| 생성된 토큰 ID -> CPU 반환
v
+-- CPU ------------------------------------+
| 토크나이저 역변환: 토큰 ID -> 텍스트 |
| 사용자에게 스트리밍 출력 |
| EOS 또는 max_tokens까지 반복 |
+-------------------------------------------+모델 로드는 최초 1회만 한다. ollama가 백그라운드에서 모델을 VRAM에 올려놓고 있어서, 두 번째 요청부터는 바로 처리된다. 처음 ollama run을 실행했을 때 잠깐 기다리는 시간이 모델 로드다.
요청이 오면 CPU에서 토크나이저가 텍스트를 토큰 ID로 바꾸고, GPU로 전송하고, GPU에서 Prefill/Decode를 하고, 결과를 CPU로 돌려받아서 다시 텍스트로 바꿔서 스트리밍한다.
여기까지가 모델 파일과 서빙 구조다. 다음 편은 마지막 편이다. 지금까지 배운 것들을 실무에 어떻게 적용하는지, 프롬프트 설계부터 RAG, 파인튜닝, 계층형 파이프라인까지 다룬다.
이 글이 어떠셨나요?
관련 포스트
6. 추론: Prefill, Decode, KV 캐시
LLM이 토큰을 생성할 때 왜 전체를 다시 계산하지 않는지. KV 캐시의 동작 원리, VRAM 비용, Prefill과 Decode의 차이.
2026. 04. 07. 오후 10:00DevOps1. LLM은 텍스트를 어떻게 처리하는가
LLM이 텍스트 한 줄을 받아서 다음 토큰을 예측하기까지의 전체 과정을 4단계로 따라간다. 토크나이저, 임베딩, 트랜스포머 레이어, 출력까지 "OOM killer가 nginx를" 한 문장이 모델 안에서 겪는 여행.
2026. 03. 03. 오후 10:00DevOps9. 하드웨어: GPU, 메모리, 속도의 관계
LLM에 GPU가 필요한 이유. 진짜 병목이 메모리 대역폭인 이유. 내 GPU에서 어떤 모델을 돌릴 수 있는지 VRAM 사용량을 직접 계산하는 방법.
2026. 04. 28. 오후 10:00뉴스레터 구독
새 글이 올라오면 이메일로 알려드려요.