본문으로 건너뛰기
페이지 목차
이 페이지에서 검색

로컬 LLM

이 페이지에 묻기

로컬 LLM · 현재 차시와 목차 본문에서만 찾아 답합니다. 답을 지어내지 않고 해당하는 대목을 그대로 보여 주며, 여기에 없으면 없다고 알려 드립니다.

생성형 AI가 아니라 이 페이지 본문을 찾아 그대로 보여 주는 방식입니다. 외부로 나가는 요청이 없습니다.

LOCAL · LLM

로컬 LLM

생성 수정

로컬 모델 실행과 GPU·메모리 이해부터 서빙·RAG·도구 호출·업무형 앱 구현까지 차례로 살펴봅니다.

42개 문서 · 2일차 · 10 / 42

9차시 · LLM 메모리 구조와 GPU 자원 분석

지도 위치 : 2일차 · ①하드웨어 층과 ②모델 파일 층의 경계 | 이전: 8차시 Ollama 기반 AI 앱 구현 | 다음: 10차시 LLM 양자화 원리 키워드 : 모델 파라미터와 메모리, FP32 · FP16 · BF16, GPU VRAM, KV Cache

이 차시를 다 읽으면

  1. 가중치 ≈ 파라미터 수 × 숫자 하나의 바이트 수 계산식을 처음부터 세우고, 모델 크기만으로 계산하면 왜 모자라는지 (KV 캐시) 말할 수 있다.
  2. FP32 · FP16 · BF16의 바이트 수 를 대고, BF16과 FP16이 같은 2바이트인데 무엇이 다른지 말할 수 있다.
  3. 모델의 config.json에서 값 네 개를 찾아 토큰 하나당 KV 캐시 크기 를 계산할 수 있다.
  4. 총 VRAM 예산식 으로 "이 GPU에서 이 모델을 이 컨텍스트 길이로 몇 명이 쓸 수 있는가"를 어림할 수 있다.
  5. 모델 파일 크기와 VRAM 사용량이 왜 다른지 디스크 ↔ 메모리 축으로 설명할 수 있다.

0. 이 차시의 판별 축

#축왼쪽 (0)오른쪽 (1)이 축으로 갈리는 것큰 축 (README 5장)
1어디에 있는가디스크 (모델 파일)메모리 (VRAM에 올라간 모델)파일 크기 ↔ 실제 VRAM 사용량① 디스크 ↔ 메모리
2고정인가 늘어나는가고정 (가중치)요청에 따라 늘어남 (KV 캐시)모델을 고르면 정해지는 몫 ↔ 쓰는 방식이 정하는 몫① 디스크 ↔ 메모리 (메모리 쪽을 다시 가름)
3숫자를 몇 바이트로 담는가높은 정밀도 (FP32)낮은 정밀도 (FP16 · BF16 · INT)가중치 크기정밀도 (파생 축, 부모 ①)

이 차시가 답하는 질문은 하나입니다. "이 모델을 이 GPU에서, 이 길이로, 몇 명이 쓸 수 있는가?" README 7장 문장 1에서 "모든 것은 VRAM 예산 싸움이고, 숫자 대신 계산식을 외우라"고 했습니다. 이번 차시는 그 계산식을 처음부터 한 항씩 세우는 자리 입니다. 1절에서 모델이 정하는 몫(가중치)을 세우고, 2절에서 2차시 2.2의 KV 캐시가 차지하는 몫을 재고, 3절에서 둘을 합칩니다.


1. 가중치 — 계산식의 첫 줄 세우기

1.1 파라미터 × 바이트

2차시 1절에서 모델 파라미터 = 가중치 이고 "8B" = 80억 개라고 했습니다. 파라미터 하나는 숫자 하나 입니다. 모델을 GPU에 올린다는 것은 이 숫자 80억 개를 GPU 메모리(VRAM)에 전부 올려 둔다는 뜻입니다.

숫자 하나가 메모리를 얼마나 차지하는지는 그 숫자를 몇 비트로 적느냐 (정밀도)가 정합니다. 8비트 = 1바이트이므로, 16비트로 적은 숫자는 2바이트입니다. 그러면 계산식은 곱셈 하나입니다.

가중치 크기 ≈ 파라미터 수 × 숫자 하나의 바이트 수
           예) 80억 × 2바이트 = 160억 바이트 ≈ 16GB

💡 이 노트에서 GB는 어림 계산이 쉽도록 10억(10⁹) 바이트 로 셉니다. 그래서 "파라미터 수(B) × 바이트 = GB"가 바로 나옵니다. 도구에 따라 GiB(2³⁰ ≈ 10.7억 바이트)로 표시하면 숫자가 7% 정도 작게 보입니다.

1.2 정밀도별 바이트

모델이 바뀌면 왼쪽 항(파라미터 수)이, 형식이 바뀌면 오른쪽 항(바이트 수)이 바뀝니다. 오른쪽 항의 선택지는 아래와 같습니다.

정밀도비트바이트 / 파라미터8B 모델 가중치주로 쓰는 곳
FP32324≈ 32GB학습의 일부 계산, 예전 모델
FP16162≈ 16GB추론의 기본값(구형 GPU 포함)
BF16162≈ 16GB최근 모델의 배포 기본값, 학습
INT881≈ 8GB양자화 (10~11차시)
INT440.5≈ 4GB양자화 (10~12차시)

⭐ 같은 8B 모델이 정밀도에 따라 32GB에서 4GB까지 달라집니다. 위 세 줄(FP32 · FP16 · BF16)이 이 차시의 주제이고, 아래 두 줄(INT8 · INT4, 정수로 줄여 담기)은 10차시 양자화에서 다룹니다.

1.3 ⚠️ BF16과 FP16 — 같은 2바이트, 다른 범위

BF16 (bfloat16) — [16비트 부동소수점 형식]인데, [ FP32와 같은 크기의 지수부를 써서 아주 크거나 작은 수를 표현할 수 있는 대신 소수점 아래 정밀도를 줄였다 ]는 점에서 FP16과 구별된다.

부동소수점은 숫자를 부호 + 지수(크기의 자릿수) + 가수(세밀함)로 나눠 담습니다. 16비트를 어떻게 나눠 쓰느냐가 다릅니다.

지수 비트가수 비트표현 범위세밀함위험
FP32823넓음매우 세밀크기가 큼
FP16510좁음 (약 ±65,504)비교적 세밀큰 값이 넘침(overflow)
BF1687FP32와 같은 넓이거침미세한 차이가 뭉개짐
  • 메모리 계산에서는 둘 다 2바이트 라 똑같이 취급합니다.
  • 차이는 안정성 입니다. 학습 · 추론 중 값이 크게 튀면 FP16은 넘쳐서 inf나 nan이 나올 수 있고, BF16은 버팁니다.
  • 그래서 최근 모델은 BF16으로 배포되는 경우가 많고, 모델 카드나 config.json의 torch_dtype(버전에 따라 dtype)에 적혀 있습니다.

⚠️ 반례 — BF16이 FP16보다 "더 정밀한" 것이 아닙니다. 가수 비트는 오히려 적습니다. "범위가 넓다"와 "정밀하다"는 다른 축입니다.

💡 BF16을 지원하지 않는 오래된 GPU에서는 엔진이 FP16으로 바꿔 올립니다. vLLM의 --dtype, Transformers의 torch_dtype이 이 선택입니다(19차시).


2. ⭐ KV 캐시 — 모델 크기만으로는 모자란 이유

2.1 왜 가중치와 다른 종류의 메모리인가

2차시 2.2에서 정의했습니다. KV 캐시 = 이미 처리한 토큰들의 Key · Value 계산 결과를 저장해 재계산을 피하는 메모리 영역.

0절 축 2로 보면 가중치와 KV 캐시는 다른 종류 입니다.

가중치KV 캐시
언제 정해지나모델을 고르는 순간요청이 들어올 때마다
무엇에 비례하나파라미터 수 × 정밀도토큰 수 × 동시 요청 수
디스크에 있나있음 (모델 파일)없음 — 순수한 메모리
요청이 끝나면그대로비워짐 (엔진에 따라 재사용, 21차시)
줄이는 방법양자화 (10~12차시)컨텍스트 · 동시 요청 줄이기, KV 캐시 양자화 (13차시)

⭐ 그래서 "8B 모델 = 16GB"처럼 모델 크기만으로 필요한 VRAM을 정할 수 없습니다. KV 캐시는 쓰는 방식에 따라 0GB에 가깝기도 하고 가중치보다 커지기도 합니다. 표 하나로 고정할 수 없으니 공식으로 잽니다.

2.2 공식

토큰 1개의 KV 캐시 = 2 × 층 수 × KV 헤드 수 × 헤드 차원 × 바이트
                    │    │        │            │          └ FP16이면 2
                    │    │        │            └ 헤드 하나가 다루는 숫자 개수
                    │    │        └ Key·Value를 만드는 헤드 수 (2.5절 GQA)
                    │    └ Transformer 블록 수 (2차시 2.1)
                    └ K 하나, V 하나

값 네 개는 모두 Hugging Face 모델 저장소의 config.json 에 있습니다. 모델 저장소에는 두 종류의 파일이 들어 있습니다.

파일담긴 것예
가중치 파일 (*.safetensors 등)학습이 정한 숫자 값 수십억 개1절에서 크기를 잰 것
config.json모델의 구조 설정 — 층이 몇 개이고, 헤드가 몇 개이고, 벡터가 몇 칸인가이 절에서 읽을 것

같은 구조 설정에 다른 숫자 값을 채우면 다른 모델이 됩니다. KV 캐시의 크기는 숫자 값이 아니라 구조 가 정하므로 config.json만 보면 됩니다.

공식의 항config.json의 키비고
층 수num_hidden_layers
KV 헤드 수num_key_value_heads없으면 num_attention_heads와 같음 (GQA 아님)
헤드 차원head_dim없으면 hidden_size ÷ num_attention_heads
바이트—엔진의 KV 캐시 정밀도. 기본은 모델 정밀도(2바이트)

2.3 계산 예 — Qwen3-8B

⚠️ 아래 값은 계산 예시입니다. 실제 값은 반드시 Qwen/Qwen3-8B의 config.json에서 확인 하세요.

항값 (config 확인 필요)
num_hidden_layers36
num_key_value_heads8
head_dim128
바이트 (FP16/BF16)2
2 × 36 × 8 × 128 × 2 = 147,456 바이트 ≈ 144KB / 토큰

같은 방식으로 Llama-3.1-8B(32층, KV 헤드 8, 헤드 차원 128)는 128KB / 토큰 입니다. 같은 8B라도 KV 캐시 크기는 다릅니다. 파라미터 수만 보고 판단하면 안 되는 이유입니다.

2.4 컨텍스트 × 동시 요청 — 곱하기로 커진다

KV 캐시 총량 = 토큰당 크기 × 토큰 수 × 동시 요청 수. Qwen3-8B(144KB/토큰, FP16 KV) 기준 계산값입니다.

컨텍스트 길이1명4명 동시10명 동시 (문의봇)
4K (4,096)≈ 0.56GB≈ 2.3GB≈ 5.6GB
8K (8,192)≈ 1.1GB≈ 4.5GB≈ 11.3GB
32K (32,768)≈ 4.5GB≈ 18GB≈ 45GB

⭐ 오른쪽 아래로 갈수록 가중치(≈16GB)보다 커집니다. "8B 모델이니까 24GB면 넉넉하다"는 판단이 동시 사용자 앞에서 무너지는 지점 이 이 표입니다.

⚠️ 이 표는 "모든 요청이 그 길이를 꽉 채운다"는 최악의 경우입니다. 실제로는 요청마다 길이가 다르고, vLLM은 필요한 만큼만 블록 단위로 잡습니다(21차시 PagedAttention). 하지만 예산을 세울 때는 최악을 기준 으로 봅니다.

2.5 GQA — KV 헤드를 줄인 구조

2차시 2.2의 어텐션(Q · K · V)은 실제 모델에서 여러 벌 이 나란히 돌아갑니다.

멀티헤드 어텐션 (MHA, Multi-Head Attention) — [어텐션 구조]인데, [ 어텐션을 여러 벌(헤드) 나란히 두어 헤드마다 다른 관계를 보게 한다 ]는 점에서 어텐션을 한 벌만 쓰는 구조와 구별된다. 예를 들어 한 헤드는 "무엇이 무엇을 꾸미는가"를, 다른 헤드는 "누가 주어인가"를 봅니다.

원래의 멀티헤드 어텐션은 헤드마다 Q · K · V를 따로 가졌습니다. 그러면 KV 캐시가 헤드 수만큼 커집니다.

GQA (Grouped-Query Attention) — [어텐션 구조]인데, [ 여러 Q 헤드가 K · V 헤드 하나를 나눠 쓰게 해 KV 헤드 수를 줄인다 ]는 점에서 헤드마다 K · V를 따로 두는 멀티헤드 어텐션과 구별된다.

구조Q 헤드KV 헤드KV 캐시
멀티헤드 (MHA)3232기준 (1배)
GQA3281/4

config.json에서 num_attention_heads(Q 헤드)와 num_key_value_heads가 다르면 GQA 입니다. 최근 공개 모델 대부분이 GQA라서, 공식에 Q 헤드 수를 넣으면 몇 배 부풀린 값 이 나옵니다. ⚠️ AI가 계산해 줄 때 가장 자주 틀리는 곳입니다.


3. ⭐ 총 VRAM 예산식

필요한 VRAM ≈ 가중치  +  KV 캐시(토큰당 크기 × 컨텍스트 × 동시 요청)  +  나머지(활성값 · CUDA 컨텍스트 · 엔진 여유분, 대략 1~2GB 이상)
              ─────      ──────────────────────────────────────     ──────────────────────────────────
              모델이 정함    쓰는 방식이 정함                              엔진과 환경이 정함

셋째 항의 낯선 이름 두 개만 짚습니다. 활성값(activation) 은 계산 도중 층과 층 사이에 잠깐 생겼다 사라지는 중간 결과이고, CUDA 컨텍스트 는 프로그램이 NVIDIA GPU를 쓰기 시작할 때 드라이버와 계산 라이브러리가 먼저 잡아 두는 기본 메모리입니다. 둘 다 모델을 바꿔도 크게 변하지 않아 "1~2GB 이상"으로 어림합니다.

README 7장 "문장 1. 모든 것은 VRAM 예산 싸움이다"의 완성형입니다. 1절의 가중치 식에 2절의 KV 캐시 공식을 더하고, 나머지를 어림으로 붙인 것입니다.

3.1 문의봇에 대입하기 (계산값)

RTX 4090 24GB, Qwen3-8B FP16, 컨텍스트 8K.

항계산값
가중치8.2B × 2바이트≈ 16.4GB
나머지어림≈ 1.5GB
KV 캐시에 쓸 수 있는 몫24 − 16.4 − 1.5≈ 6GB
8K 요청 1개의 KV144KB × 8,192≈ 1.1GB
8K를 꽉 채운 요청을 동시에6 ÷ 1.1약 5개

⭐ 상담원 10명이 동시에 긴 메일을 넣으면 FP16으로는 예산이 모자랍니다. 선택지는 세 갈래로 나뉩니다.

선택지예산식의 어느 항다루는 차시
가중치를 양자화 (16GB → 8GB · 5GB)첫째 항10~12, 15차시
컨텍스트를 줄이거나 KV 캐시를 양자화둘째 항13차시
동시에 받는 수를 제한하고 줄 세우기둘째 항13, 21, 23차시

앞으로 다시 만납니다 — 19차시에서 vLLM이 기동할 때 로그에 "Maximum concurrency for 8192 tokens per request: ○○x" 를 찍습니다. 이 절에서 손으로 한 계산을 엔진이 대신 해서 보여주는 줄입니다. 그때 이 표와 비교하세요.


4. 축 ① — 파일 크기 ≠ VRAM 사용량

4.1 디스크의 모델과 메모리의 모델

README 5.1의 축 ① 디스크 ↔ 메모리 (디스크는 꺼도 남는 저장, 메모리는 실행 중에만 있는 공간)가 여기서는 이렇게 생겼습니다. 5차시에서 ollama list(디스크)와 ollama ps(메모리)를 갈랐던 그 경계입니다.

디스크 (ollama list, 파일 크기)메모리 (ollama ps, nvidia-smi)
들어 있는 것가중치 + 메타데이터가중치 + KV 캐시 + 활성값 + CUDA 컨텍스트
크기를 정하는 것파라미터 수 × 정밀도위 + 컨텍스트 설정 · 동시 요청
같은 파일이면항상 같음설정에 따라 다름

❌ "모델 파일이 5GB니까 VRAM 6GB GPU에 들어간다" — 컨텍스트를 길게 잡으면 들어가지 않습니다. ⭕ "파일 5GB + 내가 쓸 컨텍스트의 KV 캐시 + 1~2GB"로 계산합니다.

4.2 ⚠️ 측정 도구마다 보이는 숫자가 다르다

도구보이는 것주의
nvidia-smiGPU 전체에서 프로세스가 잡은 메모리엔진이 미리 확보한 몫 까지 포함. vLLM은 요청이 없어도 거의 꽉 차 보임(19차시) = 정상
ollama ps모델이 차지한 크기, CPU/GPU 분할컨텍스트 설정이 바뀌면 크기도 바뀜
PyTorch torch.cuda.max_memory_allocated()PyTorch가 실제로 쓴 최대치CUDA 컨텍스트는 빠짐 → nvidia-smi보다 작게 나옴 (11차시)

💡 그래서 숫자를 보고할 때는 "어느 도구로, 어떤 설정에서" 를 항상 같이 적습니다. 14차시 측정 규칙의 첫 번째 원칙입니다.

4.3 비유 (기억 보조)

가중치는 책상에 펼쳐 둔 두꺼운 매뉴얼 , KV 캐시는 상담원마다 쓰는 메모지 입니다. 매뉴얼은 한 권을 모두 같이 보지만, 메모지는 상담원 수와 통화 길이만큼 쌓입니다. 책상(VRAM)이 좁으면 매뉴얼을 얇게 만들거나(양자화) 메모지를 줄여야(컨텍스트 · 동시 수) 합니다.


5. 실습 — 맡기고 검수하기

계산은 손으로(또는 AI로) 하고, 실측과 맞춰 보는 것 이 이 실습의 목적입니다.

지시

목표: Qwen3-8B의 VRAM 사용량을 계산값과 실측값으로 비교한다.
맥락: Hugging Face의 Qwen/Qwen3-8B config.json 원문을 먼저 열어서 보여줘(기억으로 쓰지 말 것).
작업:
 1. config.json에서 num_hidden_layers, num_attention_heads, num_key_value_heads, head_dim, torch_dtype 값을 원문 그대로 인용.
 2. 토큰당 KV 캐시 = 2 × 층 × KV헤드 × head_dim × 2바이트 로 계산. 계산 과정을 한 줄씩 보여줘.
 3. Ollama로 qwen3:8b를 num_ctx 2048, 8192, 16384로 각각 한 번씩 질문하고, 그때마다 ollama ps와 nvidia-smi 출력을 붙여줘.
제약: 시스템 설정(드라이버, CUDA)은 건드리지 마. 모델이 이미 로드돼 있으면 ollama stop 후 다시 시작.
보고 형식:
| num_ctx | 계산한 KV 캐시 | ollama ps SIZE | ollama ps PROCESSOR | nvidia-smi memory.used |
추측하지 말고 실행 출력 원문을 붙여.

받을 증거

  • config.json 원문 일부 (값 네 개가 보이는 부분)
  • num_ctx별 ollama ps, nvidia-smi 출력 원문
  • 위 보고 형식의 표

내가 직접 볼 것

볼 것이걸로 아는 것
num_attention_heads와 num_key_value_heads가 다른가GQA인가. AI가 Q 헤드 수로 계산하지 않았는가
num_ctx를 올릴 때 ollama ps SIZE가 늘어나는가파일 크기는 그대로인데 메모리는 늘어남 = 축 ①
늘어난 양이 계산한 KV 캐시 증가량과 대략 맞는가계산식을 믿어도 되는가. 크게 다르면 Ollama가 KV 캐시를 양자화했거나 다른 설정이 있음 → 질문
PROCESSOR가 100% GPU에서 CPU 분할로 바뀌는 지점이 GPU의 예산 한계 (12차시에서 회수)

💡 Ollama의 메모리 수치는 버전에 따라 어림값으로 표시되기도 합니다. 정확히 맞추는 것이 목적이 아니라, 늘어나는 방향과 크기가 계산과 같은 쪽인지 보는 것이 목적입니다.


부록 A. 스스로 확인하기

#질문답이 있는 곳
1가중치 크기의 계산식은? 8B 모델의 가중치는 FP32 · FP16 · INT4에서 각각 몇 GB인가?1.1, 1.2
2BF16과 FP16은 같은 바이트인데 무엇이 다른가? BF16이 더 정밀한가?1.3
3가중치와 KV 캐시는 무엇에 비례하는가?2.1
4토큰당 KV 캐시 공식과 config.json의 키 네 개는?2.2
5Qwen3-8B의 토큰당 KV 캐시는 몇 KB이고, 왜 Llama-3.1-8B와 다른가?2.3
6컨텍스트 8K로 10명이 동시에 쓰면 KV 캐시가 얼마나 되는가?2.4
7GQA인지 어떻게 알아보는가?2.5
8총 VRAM 예산식의 세 항과, 각 항을 정하는 주체는?3
9모델 파일 크기로 VRAM 필요량을 판단하면 왜 틀리는가?4.1

부록 B. 자주 하는 실수

실수바로잡기
"VRAM = 모델 파일 크기"❌ 파일은 디스크 쪽 숫자. KV 캐시 + 나머지 가 더해짐
"BF16이 FP16보다 정밀하다"❌ 범위 가 넓고 세밀함 은 오히려 낮음. 다른 축
"같은 8B면 KV 캐시도 같다"❌ 층 수 · KV 헤드 수 · 헤드 차원이 모델마다 다름
KV 캐시 공식에 num_attention_heads를 넣음❌ num_key_value_heads . GQA 모델은 몇 배 차이
"24GB면 8B FP16은 넉넉하다"⚠️ 1명은 넉넉, 10명 × 8K는 부족 . 동시 요청을 곱하기
nvidia-smi가 꽉 찼으니 메모리 부족⚠️ 엔진이 미리 확보 한 것일 수 있음. 어느 도구의 숫자인지 확인

부록 C. NotebookLM 복습 프롬프트

이 9차시 노트만 근거로 6문제를 내줘. 비전공자 기준으로.
"KV 캐시 공식과 config.json 키", "컨텍스트×동시 요청 표", "파일 크기≠VRAM"은 반드시 포함하고, 계산 문제 1개를 넣어줘.
답은 내가 답한 다음에 채점하고, 틀린 이유를 노트의 절 번호를 근거로 설명해줘.