🖥️ CPU vs GPU — Fundamentalna Razlika za AI
Da bismo razumjeli ZAŠTO GPU ubrzava LLM inferenzu 10-100x, moramo razumjeti fundamentalnu razliku između CPU i GPU arhitektura.
📌 CPU (Central Processing Unit) — Arhitektura
CPU je dizajniran za sekvencijalne, kompleksne zadatke. Tipičan server CPU ima 2-128 jegara (cores). Svako jezgro može izvršavati složene sekvencijalne instrukcije sa brzom cache memorijom (L1/L2/L3). Odlični za: web servere, baze podataka, operativni sistem, poslovnu logiku — gdje se izvršava mnogo različitih zadataka u sekvenci.
📌 GPU (Graphics Processing Unit) — Arhitektura
GPU je dizajniran za masivno paralelne, jednostavne operacije. Npr. NVIDIA RTX 4090 ima 16,384 CUDA cores. Svako jezgro je "gluplje" od CPU jezgra, ali imajući 16.000+ njih koji simultano rade istu operaciju na različitim podacima — GPU je nezamjenjiv za matrično množenje.
💡 Analogija: Restoran vs Fabrika
CPU je kao vrsni kuhar u restoranu: priprema svako jelo od nule, po kompleksnim receptima, personalizovano za svakog gosta — ali može kuhati samo jedno jelo odjednom.
GPU je kao fabrika prehrambenih proizvoda: ima 16.000 radnika koji svaki radi istu jednostavnu operaciju (npr. punjenje flaše) simultano. Ne mogu kuhati kompleksna jela, ali mogu napuniti 16.000 flaša u sekundi.
Transformer matematika (matrično množenje) je savršena za GPU model — ista operacija na hiljadama elemenata simultano.
Zašto je Matrično Množenje Ključno?
Sva Transformer arhitektura se svodi na jedno: matrično množenje (Matrix Multiplication). Kada model "misli" (vrši inferenzu), dešava se hiljadama matričnih množenja u svakom sloju. Za 7B model sa 32 sloja, jedan forward pass (generisanje jednog tokena) zahtijeva:
💾 VRAM Kalkulacija — Koliko Memorije Treba?
VRAM (Video RAM) je memorija unutar GPU-a. Za LLM inferenzu, cijeli model mora stati u VRAM. Ako model ne stane — pada na RAM (System RAM), i postaje puno sporiji.
📌 Formula za Izračun VRAM Potrebe
Osnovna formula (za inferenzu, bez treninga):
VRAM (GB) ≈ (Broj Parametara × Bajta po Parametru) / 1,073,741,824 + ~20% overhead
- FP32 (4 bajta/param): Llama 3 8B → 8B × 4 = 32 GB VRAM
- FP16 (2 bajta/param): Llama 3 8B → 8B × 2 = 16 GB VRAM
- INT8 (1 bajt/param): Llama 3 8B → 8B × 1 = 8 GB VRAM
- INT4 (0.5 bajta/param): Llama 3 8B → 8B × 0.5 = 4 GB VRAM
VRAM Zahtjevi Popularnih Modela
| Model | Parametri | FP16 VRAM | Q4 VRAM | Preporučeni GPU |
|---|---|---|---|---|
| Llama 3.2 3B | 3 B | 6 GB | 2 GB | GTX 1060 (6 GB) |
| Llama 3 / Mistral 7B | 7-8 B | 14-16 GB | 5-6 GB | RTX 3060 (12 GB) |
| Llama 3 13B / Mistral NeMo 12B | 12-13 B | 24-26 GB | 8-10 GB | RTX 3090 (24 GB) |
| Llama 3 70B | 70 B | 140 GB | 40-50 GB | A100 80 GB ili 2×RTX 3090 |
| Mistral Small / Gemma 2 27B | 27 B | 54 GB | 16-20 GB | RTX 4090 (24 GB) + RAM offload |
ℹ️ Šta ako nema dovoljno VRAM-a?
Ollama (i llama.cpp) automatski offloadaju slojeve modela na System RAM ako GPU VRAM nije dovoljan. Na primjer, 7B model sa Q4 kvantizacijom treba ~5 GB VRAM. Ako imate GPU sa 4 GB VRAM, llama.cpp može staviti 60% slojeva na GPU, a 40% na RAM — dobivate ~40-50% performansi punog GPU-a, što je i dalje znatno brže od čistog CPU-a.
Ovo se podešava u Ollami opcijom: OLLAMA_GPU_LAYERS=20 (broj slojeva na GPU-u)
🔢 Kvantizacija — Kompresija Modela bez Gubljenja Kvalitete
Kvantizacija je proces smanjivanja preciznosti numeričkih vrijednosti (težina modela) radi uštede memorije. Ovo je ključni koncept koji omogućava pokretanje velikih modela na dostupnom hardveru.
📌 Floating Point formati i preciznost
Kompjuteri reprezenuju decimalne brojeve u binarnom formatu. Različiti formati zauzimaju različito memoria:
- FP32 (Float 32-bit, "full precision"): 32 bita = 4 bajta po broju. Raspon: ±3.4 × 1038. Maksimalna preciznost. Koristi se za trening.
- FP16 (Float 16-bit, "half precision"): 16 bita = 2 bajta. Raspon: ±65,504. Manji raspon, ali dovoljno za inferenzu.
- INT8 (Integer 8-bit): 8 bita = 1 bajt. Samo cijeli brojevi 0-255 (ili -128 do 127). Drastično smanjenje preciznosti — ali uz scaling trick, prihvatljiv kvalitet.
- INT4 (4-bit): Samo 16 mogućih vrijednosti. Agresivna kompresija, ali moderni LLM-ovi su dovoljno robusni da zadrže ~95% kvaliteta.
💡 Analogija: Kvantizacija kao Jpeg kompresija
Zamislite originalnu fotografiju visoke rezolucije (FP32). Kada je snimite kao JPEG sa 90% kvaliteta (FP16), skoro ne vidite razliku. Na 70% kvaliteta (INT8), vidite minimalnu degradaciju. Na 40% (INT4), vidite blago zamućivanje, ali fotografija je i dalje jasno prepoznatljiva.
Kvantizacija LLM-a funkcionira slično: niste izgubili "znanje" modela, samo ste smanjili preciznost načina na koji je to znanje pohranjeno. Za većinu zadataka, razlika je neprimjetna.
GGUF Kvantizacijski Nivoi
Ollama koristi GGUF format koji definira više nivoa kvantizacije. Najvažniji:
📌 Šta znače sufiksi _K, _K_M, _K_S?
GGUF kvantizacijski tip poput Q4_K_M znači:
- Q4: 4-bit kvantizacija za većinu težina
- _K: k-quants metoda (naprednija, bolja od naivne kvantizacije)
- _M: Medium — kritičniji slojevi (attention layers) su kvantizovani na 6-bit za bolji kvalitet
- _S: Small — agresivnija kompresija, manji fajl, malo slabiji kvalitet
- _L: Large — manje agresivna, bolji kvalitet ali veći fajl
Preporuka za početnike: Koristite Q4_K_M — to je "standard" i Ollama ga
koristi po defaultu.
Uticaj Kvantizacije na Performanse — Benchmark
| Kvantizacija | Veličina (7B) | VRAM | Kvalitet odgovora | Brzina (tok/s na RTX 3060) |
|---|---|---|---|---|
| F32 | 26 GB | 28 GB+ | 100% (baseline) | ~5 tok/s |
| F16 | 13 GB | 14 GB | 99% | ~12 tok/s |
| Q8_0 | 7.7 GB | 8 GB | 98.5% | ~22 tok/s |
| Q4_K_M | 4.1 GB | 5 GB | 96% | ~35 tok/s |
| Q2_K | 2.5 GB | 3 GB | 85-88% | ~45 tok/s |
🔑 Ključni Zaključak
Sa Q4_K_M kvantizacijom, model gubi samo ~4% kvaliteta u usporedbi sa punim FP32 preciznosti, ali zauzima 6.3x manje memorije i generira tekst 7x brže. Ovo je razlog zašto je kvantizacija industrijski standard za lokalnu LLM inferenzu.
🖥️ Hardware Preporuke po Scenariju
Scenarij 1: Minimalni Setup (bez namjenskog GPU-a)
| Komponenta | Minimum | Preporučeno |
|---|---|---|
| RAM | 16 GB | 32 GB |
| CPU | Intel i5-8th gen / AMD Ryzen 5 3600 | Intel i7-12th gen / AMD Ryzen 7 5800X |
| Disk | SSD sa 20+ GB slobodnog prostora | NVMe SSD |
| Modeli | Phi-3 Mini (3.8B), Llama 3.2 3B | Llama 3.2 3B, Q4 Mistral 7B |
| Brzina | 1-3 tok/s (sporo ali radi) | 3-8 tok/s |
Scenarij 2: Namjenski GPU Setup (preporučeno)
| Komponenta | Budget Setup | Profesionalni Setup |
|---|---|---|
| GPU | RTX 3060 12 GB (~350€) | RTX 4090 24 GB (~1500€) |
| RAM | 32 GB DDR4 | 64 GB DDR5 |
| Modeli | Mistral 7B, Llama 3 8B Q4 | Llama 3 70B Q4, DeepSeek 67B |
| Brzina (7B) | 30-40 tok/s | 80-120 tok/s |
⚠️ NVIDIA vs AMD GPU za LLM-ove
Za LLM inferenzu, NVIDIA GPU-ovi su znato bolji zbog CUDA ekosistema. Gotovo svi LLM frameworki (llama.cpp, vLLM, ExLlamaV2) imaju primarnu podršku za CUDA.
AMD GPU-ovi rade putem ROCm platforme, ali podrška je manje stabilna i performanse su tipično 20-40% niže od ekvivalentnog NVIDIA GPU-a za LLM zadatke. Apple Silicon (M1/M2/M3) je odlična opcija za Mac korisnike — Metal acceleration daje izvrsne rezultate u odnosu na potrošnju energije.
LAB 3C: Mjerenje Performansi i Poređenje Kvantizacija
U ovom labu ćemo: (1) provjeriti hardware konfiguraciju, (2) mjeriti performanse modela, i (3) kreirati Python benchmark alat.
Provjera Hardware Konfiguracije
# Provjera RAM-a (ukupni i dostupni)
Get-WmiObject Win32_PhysicalMemory | Measure-Object Capacity -Sum |
Select-Object @{N="Total RAM (GB)"; E={[Math]::Round($_.Sum / 1GB, 1)}}
# Provjera GPU-a i VRAM-a
Get-WmiObject Win32_VideoController |
Select-Object Name,
@{N="VRAM (GB)"; E={[Math]::Round($_.AdapterRAM / 1GB, 1)}},
DriverVersion
# Provjera CPU-a
Get-WmiObject Win32_Processor |
Select-Object Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed
# Provjera slobodnog prostora na disku
Get-PSDrive C | Select-Object @{N="Slobodno (GB)"; E={[Math]::Round($_.Free / 1GB, 1)}}
Provjera Ollama konfiguracije i GPU detekcija
# Provjera Ollama okoline i GPU detekcije
ollama --version
# Provjera detektovanih GPU-ova (Ollama automatski prikazuje info o GPU-u)
# Pokrenite Ollamu u posebnom terminalu ako nije pokrenuta:
# ollama serve
# Ispis detalja o modelu (uključuje kvantizacijski nivo)
ollama show llama3.2
# Pokretanje modela sa verbose outputom koji pokazuje GPU korištenje
# Otvorite novi PowerShell terminal i pokrenite:
OLLAMA_DEBUG=1 ollama run llama3.2
Python Benchmark Skript za Mjerenje Brzine
Napravite fajl benchmark.py u VS Code:
"""
benchmark.py
Mjerenje performansi lokalnih Ollama modela:
- Tokens per second (tok/s) - ključna metrika brzine
- Time to first token (TTFT) - latency do prvog odgovora
- Ukupno generisanje za standardni test prompt
"""
import ollama
import time
import json
# ================================================================
# KONFIGURACIJA BENCHMARKA
# ================================================================
# Lista modela za testiranje (morat ćete imati ih preuzete!)
MODELI_ZA_TEST = ['llama3.2'] # Dodajte više modela po želji
# Standardni test prompt (isti za sve modele radi poređenja)
TEST_PROMPT = """Explain in exactly 200 words how a TCP/IP handshake works.
Include the three phases: SYN, SYN-ACK, ACK.
Describe what happens at each phase and why it's important."""
# Broj ponavljanja za svaki model (za pouzdanije rezultate)
BROJ_PONAVLJANJA = 2
# ================================================================
# BENCHMARK FUNKCIJA
# ================================================================
def benchmark_model(model_ime: str, prompt: str) -> dict:
"""
Mjeri performanse jednog modela.
Vraća dict sa svim metrikama.
"""
print(f"\n🔬 Testiram model: {model_ime}")
print(f"📝 Prompt ({len(prompt)} znakova)...")
rezultati_ponavljanja = []
for ponavljanje in range(BROJ_PONAVLJANJA):
print(f" Ponavljanje {ponavljanje + 1}/{BROJ_PONAVLJANJA}...", end='', flush=True)
# Bilježimo vrijeme početka
start_ukupno = time.perf_counter()
# Varijabla za vrijeme prvog tokena
start_prvog_tokena = time.perf_counter()
prvi_token_zabiljezen = False
ttft = 0.0
# Capture tekst odgovora za analizu
svi_tokeni = []
try:
# Streaming mode radi da mjerimo TTFT (Time to First Token)
stream = ollama.chat(
model=model_ime,
messages=[{'role': 'user', 'content': prompt}],
stream=True,
options={
'temperature': 0, # 0 = deterministički = reproducibilni rezultati
'num_predict': 250, # Ograničavamo output za konzistentno mjerenje
}
)
for chunk in stream:
# Bilježimo kada je stigao prvi token
if not prvi_token_zabiljezen:
ttft = time.perf_counter() - start_prvog_tokena
prvi_token_zabiljezen = True
# Skupljamo generirani tekst
token_tekst = chunk['message']['content']
svi_tokeni.append(token_tekst)
# Kraj generisanja
end_ukupno = time.perf_counter()
ukupno_trajanje = end_ukupno - start_ukupno
ukupni_tekst = ''.join(svi_tokeni)
# Aproksimacija: 1 token ≈ 4 znaka (gruba procjena za engleski)
aproks_tokeni = len(ukupni_tekst) / 4
tok_per_sekundi = aproks_tokeni / ukupno_trajanje
rezultat = {
'ttft_ms': ttft * 1000, # Milisekunde
'ukupno_trajanje_s': ukupno_trajanje,
'aproks_tokeni': int(aproks_tokeni),
'tok_per_sekundi': tok_per_sekundi,
'znakova_generisano': len(ukupni_tekst),
}
rezultati_ponavljanja.append(rezultat)
print(f" {tok_per_sekundi:.1f} tok/s ✅")
except Exception as e:
print(f" GREŠKA: {e} ❌")
continue
if not rezultati_ponavljanja:
return None
# Prosječne vrijednosti svih ponavljanja
prosjek = {
'model': model_ime,
'ttft_ms': sum(r['ttft_ms'] for r in rezultati_ponavljanja) / len(rezultati_ponavljanja),
'ukupno_trajanje_s': sum(r['ukupno_trajanje_s'] for r in rezultati_ponavljanja) / len(rezultati_ponavljanja),
'aproks_tokeni': int(sum(r['aproks_tokeni'] for r in rezultati_ponavljanja) / len(rezultati_ponavljanja)),
'tok_per_sekundi': sum(r['tok_per_sekundi'] for r in rezultati_ponavljanja) / len(rezultati_ponavljanja),
'znakova_generisano': int(sum(r['znakova_generisano'] for r in rezultati_ponavljanja) / len(rezultati_ponavljanja)),
}
return prosjek
# ================================================================
# POKRETANJE BENCHMARKA
# ================================================================
if __name__ == "__main__":
print("="*65)
print("⚡ OLLAMA PERFORMANCE BENCHMARK")
print("="*65)
print(f"📋 Modeli za test: {', '.join(MODELI_ZA_TEST)}")
print(f"🔁 Ponavljanja: {BROJ_PONAVLJANJA}")
print(f"📝 Prompt: {TEST_PROMPT[:80]}...")
svi_rezultati = []
# Testiranje svakog modela
for model in MODELI_ZA_TEST:
rezultat = benchmark_model(model, TEST_PROMPT)
if rezultat:
svi_rezultati.append(rezultat)
# Ispis rezultata
print("\n" + "="*65)
print("📊 REZULTATI BENCHMARKA")
print("="*65)
for r in svi_rezultati:
print(f"\n🤖 Model: {r['model']}")
print(f" ⚡ Brzina: {r['tok_per_sekundi']:.1f} tok/s")
print(f" ⏱️ Trajanje: {r['ukupno_trajanje_s']:.2f} s")
print(f" 🚀 TTFT: {r['ttft_ms']:.0f} ms (do 1. tokena)")
print(f" 📝 ~{r['aproks_tokeni']} tokena generisano")
print(f" 📄 {r['znakova_generisano']} znakova")
print("\n" + "="*65)
print("💡 INTERPRETACIJA REZULTATA:")
print(" • > 30 tok/s = Odlično (GPU inferenza)")
print(" • 10-30 tok/s = Dobro (GPU ili brz CPU)")
print(" • 3-10 tok/s = Prihvatljivo (CPU inferenza)")
print(" • < 3 tok/s = Sporo (slab CPU ili prevelik model)")
print(" • TTFT < 500 ms = Dobra latency")
print(" • TTFT > 2000 ms = Visoka latency (model se učitava)")
print("="*65)
cd C:\Users\$env:USERNAME\Desktop\AI_Kurs
.\venv\Scripts\Activate.ps1
python benchmark.py
- Tok/s (Tokens per Second): Ovo je najvažnija metrika. Označava koliko brzo model generira tekst. Tipično: 30+ tok/s znači da razgovor teče prirodno.
- TTFT (Time to First Token): Koliko brzo model "počne" odgovarati. Visoka TTFT može biti uzrokovana sporim učitavanjem modela u memoriju.
- Za kućni setap (CPU only, 16 GB RAM): Očekujte 1-5 tok/s za 7B model. Gore navedeni benchmark to jasno prikazuje.
Preuzimanje i poređenje različitih kvantizacijskih verzija
# Možete preuzeti specifičnu kvantizacijsku verziju modela:
# Format: ollama pull [ime]:[tag]
# Q4_K_M (default - ~4 GB fajl, preporučeno)
ollama pull llama3.2
# Isti model u punoj F16 preciznosti (~6 GB, za poređenje kvaliteta)
ollama pull llama3.2:latest
# Provjera svih verzija koje imate
ollama list
# Pokrenite benchmark za oba i usporedite tok/s i kvalitet odgovora!
# Zatim modificirajte MODELI_ZA_TEST listu u benchmark.py
⚙️ Optimizacijske Tehnike za Bolje Performanse
Ollama Environment Varijable
# Postavljanje Ollama environment varijabli (na Windows-u):
# Ove se postavljaju PRIJE pokretanja Ollama servisa
# OLLAMA_NUM_PARALLEL: koliko simultanih zahtjeva Ollama može obraditi
# Default je 1. Povećajte ako imate jak GPU i više korisnika.
$env:OLLAMA_NUM_PARALLEL = 2
# OLLAMA_MAX_LOADED_MODELS: koliko modela simultano čuvati u memoriji
# Default je 1 (zamjenjuje model pri svakom prebacivanju)
$env:OLLAMA_MAX_LOADED_MODELS = 2
# OLLAMA_FLASH_ATTENTION: Uključuje Flash Attention 2 optimizaciju
# Smanjuje VRAM korištenje za ~30% sa minimalnim gubitkom kvalitete
$env:OLLAMA_FLASH_ATTENTION = 1
# OLLAMA_GPU_OVERHEAD: Rezervisani VRAM za GPU overhead (u MB)
$env:OLLAMA_GPU_OVERHEAD = 512
# Pokretanje servera sa novim postavkama:
ollama serve
Odabir Pravog Modela za Zadatak
| Zadatak | Preporučeni Model | Zašto |
|---|---|---|
| Generisanje koda, debugging | CodeLlama 7B, Mistral 7B | Finetuned na kod |
| Opća konverzacija, dokumentacija | Llama 3.2 3B ili 8B | Dobar razmjer kvalitet/brzina |
| Matematika, logičko razmišljanje | Qwen2.5, Phi-3 | Finetuned za STEM |
| Brze odgovore, slabi PC | Phi-3 Mini 3.8B | Odlično za veličinu |
| Embedding generisanje | nomic-embed-text | Optimizan za vektore |
✅ Checkpoint — Provjera Razumijevanja
- GPU je 10-100x brži od CPU-a za LLM inferenzu jer matrično množenje je masivno paralelna operacija.
- VRAM = GPU memorija. Cijeli model mora stati u VRAM za optimalnu brzinu.
- Kvantizacija smanjuje preciznost težina (FP32→INT4) uz minimalan gubitak kvaliteta.
- Q4_K_M je industrijski standard za lokalne LLM-ove: 6x manje memorije, 7x brže, 96% kvaliteta.
- Benchmark metrike: tok/s (brzina) i TTFT ms (latency do prvog odgovora).
- Ollama automatski offloaduje slojeve na RAM ako VRAM nije dovoljan.
✅ Zaključak
Razumijevanje hardware zahtjeva i kvantizacije je ključno za svaki IT tim koji planira rasporediti lokalne LLM-ove. Kvantizacija nam omogućava pokretanje snažnih neruonskih mreža na standardnom office hardveru — što je revolucionarno za privatnost podataka u institucijama.
U Modulu 4 prelazimo na Prompt Engineering — nauku formulisanja efektivnih upita koji iz modela izvlače precizne, pouzdane i korisne odgovore za IT scenarije.