00 Çıkarım Zorluğu
LLM inference neden bu kadar pahalı ve quantization neden kaçınılmaz bir çözüm haline geldi.
Bir large language model çalıştırmak, her token üretimi için modelin tüm ağırlık matrislerinin bellekte hazır bulunmasını gerektirir. Üstelik bu ağırlıklar her forward pass'te sıfırdan yeniden okunur — hesaplama yapmadan önce RAM/VRAM'den çekilmesi gereken devasa bir veri yüküdür bu.
7 milyar parametreli bir model varsayalım. Her parametre float32 (32 bit = 4 byte) ile saklanıyorsa:
01 7,000,000,000 parametre × 4 byte = 28 GB VRAM 02 float16 (2 byte) → 14 GB VRAM 03 int8 (1 byte) → 7 GB VRAM 04 int4 (0.5 byte) → 3.5 GB VRAM
Tüketici GPU'larının büyük çoğunluğu 8–24 GB arası VRAM'e sahipken, 70B modeller float32'de 280 GB gerektirir. Quantization bu uçurumu kapatmanın temel yöntemidir.
Throughput ve Latency
Inference performansını değerlendirirken iki metrik kritiktir. Throughput, saniyede üretilen token sayısıdır (tok/s) ve toplu iş yüklerinde önemlidir. Latency (veya time-to-first-token, TTFT), kullanıcının ilk token'ı ne kadar sürede aldığıdır — etkileşimli uygulamalarda baskın metriktir.
| Model | Parametre | FP32 VRAM | FP16 VRAM | INT4 VRAM | Tipik Donanım |
|---|---|---|---|---|---|
| Llama-3.1 8B | 8B | 32 GB | 16 GB | 4.5 GB | RTX 3060 (12 GB) |
| Llama-3.1 70B | 70B | 280 GB | 140 GB | 40 GB | 2× A100 80GB |
| Mistral 7B | 7B | 28 GB | 14 GB | 4 GB | RTX 3070 (8 GB) |
| Mixtral 8×7B | 47B aktif: 13B | 94 GB | 47 GB | 26 GB | 2× RTX 3090 |
| Phi-3 Mini | 3.8B | 15 GB | 7.6 GB | 2.2 GB | Laptop GPU |
VRAM hesaplarına ek olarak KV cache bellek maliyetini de eklemeniz gerekir. Uzun context window'larda (32k, 128k token) KV cache, model ağırlıklarından daha fazla yer kaplayabilir.
01 Quantization Temelleri
Ağırlıkları daha az bit ile temsil etmenin matematiksel temeli ve her düzeyin kalite-boyut değiş tokuşu.
Quantization, bir sürekli değer aralığını daha az bit ile temsil etme işlemidir. Sinir ağlarında ağırlıklar ve/veya aktivasyonlar bu şekilde sıkıştırılır.
Bit Genişliği Hiyerarşisi
FP32 32 bit — IEEE 754 tam kayan nokta, eğitim standardı FP16 16 bit — yarı hassasiyet, GPU'larda hızlı, mixed precision BF16 16 bit — Brain Float, FP32 ile aynı üs aralığı, TPU/A100 INT8 8 bit — tam sayı, ~2× sıkıştırma, hafif kalite kaybı INT4 4 bit — ~4× sıkıştırma, dikkatli seçim gerektirir INT2 2 bit — deneysel, kalite ciddi biçimde düşer
Symmetric ve Asymmetric Quantization
Symmetric quantization'da sıfır noktası (zero-point) 0'a sabitlenir ve değer aralığı [-max, +max] şeklinde simetrik tutulur. Hesaplama daha basittir. Asymmetric quantization'da ise zero-point serbest bir değer alır; bu durum sıfır merkezli olmayan dağılımlar (örn. ReLU çıktıları) için daha az hata üretir.
Her iki durumda da iki parametre kullanılır:
x_real = s × x_intimport numpy as np
def quantize_asymmetric(x: np.ndarray, n_bits: int = 8):
"""Asimetrik quantization: scale + zero_point hesapla."""
x_min, x_max = x.min(), x.max()
q_min, q_max = 0, 2**n_bits - 1 # int8: 0..255
scale = (x_max - x_min) / (q_max - q_min)
zero_point = round(q_min - x_min / scale)
zero_point = int(np.clip(zero_point, q_min, q_max))
x_q = np.clip(np.round(x / scale + zero_point), q_min, q_max).astype(np.uint8)
return x_q, scale, zero_point
def dequantize(x_q: np.ndarray, scale: float, zero_point: int):
"""Quantize edilmiş değeri geri float'a çevir."""
return scale * (x_q.astype(np.float32) - zero_point)
# Örnek kullanım
weights = np.random.randn(1000).astype(np.float32)
w_q, s, z = quantize_asymmetric(weights)
w_reconstructed = dequantize(w_q, s, z)
error = np.abs(weights - w_reconstructed).mean()
print(f"Ortalama mutlak hata: {error:.6f}") # ~0.001
PTQ ve QAT
Post-Training Quantization (PTQ), eğitilmiş modeli tekrar eğitmeden quantize eder — hızlı ve pratiktir ancak kalite kaybı daha yüksek olabilir. Quantization-Aware Training (QAT), eğitim sırasında simüle edilmiş quantization uygular; model bu gürültüye adapte olur ve sonuç kalitesi çok daha yüksektir, ancak eğitim maliyeti artar.
| Yöntem | Bit | Perplexity Artışı | VRAM Tasarrufu | Hız |
|---|---|---|---|---|
| FP32 (baseline) | 32 | — | — | 1× |
| FP16 | 16 | ~0% | 50% | 1.5–2× |
| INT8 (PTQ) | 8 | <1% | 75% | 2–3× |
| INT4 GPTQ | 4 | 2–5% | 87.5% | 3–4× |
| INT4 AWQ | 4 | 1–3% | 87.5% | 3–4× |
| INT2 | 2 | >20% | 93.75% | 5× |
02 GPTQ — Gradient Tabanlı PTQ
Hessian matrisini kullanarak katman katman hassas quantization yapan GPTQ yöntemi ve AutoGPTQ kütüphanesi.
GPTQ (Generative Pre-trained Transformer Quantization), 2022'de Frantar ve ark. tarafından önerilmiştir. Temel fikir şudur: bir katmanın ağırlıklarını quantize ederken, kalan ağırlıkları bu hatayı telafi edecek şekilde güncelle. Bu güncelleme için Hessian matrisi (ikinci türev bilgisi) kullanılır.
Algoritma Özeti
01 Kalibrasyon verisi ile her katmanın Hessian'ını hesapla 02 Bir lineer katmanı al: W ∈ R^{d_out × d_in} 03 Sütunları soldan sağa quantize et (OBQ algoritması) 04 Her sütun quantize edildikten sonra Cholesky güncelleme 05 Kalan sütunlar hatayı absorbe edecek şekilde güncellenir 06 group_size=128: 128 ağırlık için ortak scale
Group Quantization
Tüm katman için tek bir scale faktörü kullanmak yerine, group_size parametresi ile her 128 ağırlık grubu kendi scale'ine sahip olur. Bu, kaliteyi önemli ölçüde artırır (daha küçük grup = daha iyi kalite, ama daha fazla overhead).
# pip install auto-gptq transformers accelerate
from auto_gptq import AutoGPTQForCausalLM
from transformers import AutoTokenizer
model_name = "TheBloke/Llama-2-7B-GPTQ"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoGPTQForCausalLM.from_quantized(
model_name,
use_safetensors=True,
device="cuda:0",
inject_fused_attention=False,
bits=4,
group_size=128,
)
prompt = "Yapay zeka nedir?"
inputs = tokenizer(prompt, return_tensors="pt").to("cuda:0")
output = model.generate(
**inputs,
max_new_tokens=200,
temperature=0.7,
do_sample=True,
)
print(tokenizer.decode(output[0], skip_special_tokens=True))
GPTQ ile Kendi Modelini Quantize Etmek
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
from transformers import AutoTokenizer
import torch
pretrained_model_name = "meta-llama/Llama-3.1-8B"
quantized_model_dir = "./llama-3.1-8b-gptq-4bit"
# Quantization konfigürasyonu
quantize_config = BaseQuantizeConfig(
bits=4, # 4-bit quantization
group_size=128, # 128 ağırlık başına bir scale
desc_act=False, # activation ordering (kaliteyi artırır ama yavaşlatır)
)
tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name)
model = AutoGPTQForCausalLM.from_pretrained(
pretrained_model_name, quantize_config
)
# Kalibrasyon verisi — model kalibrasyon için bu örneklere bakacak
examples = [
tokenizer("Transformer mimarisi attention mekanizmasını kullanır.",
return_tensors="pt"),
tokenizer("Python programlama dili veri biliminde yaygın kullanılır.",
return_tensors="pt"),
# ... daha fazla örnek ekleyin (~128 öneri)
]
model.quantize(examples)
model.save_quantized(quantized_model_dir, use_safetensors=True)
tokenizer.save_pretrained(quantized_model_dir)
print(f"Model kaydedildi: {quantized_model_dir}")
Kalibrasyon verisi kalitesi GPTQ sonuçlarını doğrudan etkiler. Modelin kullanılacağı domain'den örnekler seçin; genel web metni yerine görev-spesifik veri daha iyi sonuç verir.
03 AWQ — Activation-Aware Quantization
Aktivasyon istatistiklerini kullanarak önemli ağırlıkları koruyan AWQ, genellikle GPTQ'ya göre daha iyi kalite sunar.
AWQ (Activation-Aware Weight Quantization), 2023'te Lin ve ark. tarafından önerilmiştir. Gözlem şudur: tüm ağırlıklar eşit önemde değildir. Bazı ağırlık kanallarına karşılık gelen aktivasyonlar çok büyük değerler alır — bu ağırlıkları 4 bit'e düşürmek orantısız hata üretir.
AWQ bu kritik kanalları tespit edip, onlara özgün ölçekleme faktörleri uygular. Bu sayede hassas kanallar korunurken diğerleri agresif quantization'a tabi tutulur.
GPTQ ile Karşılaştırma
| Özellik | GPTQ | AWQ |
|---|---|---|
| Kalibrasyon | Hessian (yavaş) | Aktivasyon istatistikleri (hızlı) |
| Kalite (INT4) | İyi | Genellikle daha iyi |
| Quantization hızı | Yavaş (saatler) | Hızlı (dakikalar) |
| Hardware support | CUDA, CPU | CUDA, Metal, CPU |
| Popüler HF modeller | TheBloke/*-GPTQ | TheBloke/*-AWQ |
| group_size | Ayarlanabilir | Varsayılan 128 |
# pip install autoawq transformers
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_name = "TheBloke/Llama-2-7B-AWQ"
tokenizer = AutoTokenizer.from_pretrained(
model_name, trust_remote_code=True
)
model = AutoAWQForCausalLM.from_quantized(
model_name,
fuse_layers=True, # kernel fusion → daha hızlı inference
trust_remote_code=True,
safetensors=True,
)
# Basit metin üretimi
prompt = "[INST] Makine öğrenmesini kısaca açıkla. [/INST]"
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=256,
do_sample=True,
temperature=0.8,
top_p=0.95,
)
response = tokenizer.decode(
outputs[0][inputs["input_ids"].shape[1]:],
skip_special_tokens=True
)
print(response)
Kendi Modelini AWQ ile Quantize Etmek
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_path = "meta-llama/Llama-3.1-8B-Instruct"
quant_path = "./llama-3.1-8b-awq"
quant_config = {
"zero_point": True, # asymmetric quantization
"q_group_size": 128,
"w_bit": 4,
"version": "GEMM", # GEMM veya GEMV (düşük batch için GEMV)
}
model = AutoAWQForCausalLM.from_pretrained(
model_path, low_cpu_mem_usage=True
)
tokenizer = AutoTokenizer.from_pretrained(
model_path, trust_remote_code=True
)
model.quantize(tokenizer, quant_config=quant_config)
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)
04 GGUF/GGML Format
llama.cpp'nin kullandığı GGUF formatı, tek dosyada model metadata ve ağırlıkları birleştirir; CPU inference için optimize edilmiştir.
GGUF (GPT-Generated Unified Format), Georgi Gerganov tarafından geliştirilmiş ve llama.cpp ekosisteminin temel dosya formatıdır. Önceki GGML formatının yerini almıştır. GGUF, model metadata'sını (tokenizer, hiper parametreler, quantization konfigürasyonu) ve ağırlıkları tek bir dosyada saklar.
GGUF Quantization Tipleri
| Tip | Bit/Ağırlık | 7B Dosya Boyutu | Kalite | Öneri |
|---|---|---|---|---|
| Q2_K | ~2.5 bit | ~2.7 GB | Düşük | Yalnızca bellek çok kısıtlıysa |
| Q3_K_M | ~3.3 bit | ~3.5 GB | Orta-düşük | Çok kısıtlı sistem |
| Q4_K_M | ~4.5 bit | ~4.8 GB | İyi | Genel kullanım için tavsiye |
| Q5_K_M | ~5.5 bit | ~5.7 GB | Çok iyi | Kalite/boyut dengesi |
| Q6_K | ~6.6 bit | ~6.1 GB | Mükemmele yakın | Yüksek kalite gerektiriyorsa |
| Q8_0 | 8 bit | ~7.7 GB | Neredeyse kayıpsız | VRAM yeterliyse |
| F16 | 16 bit | ~14 GB | Kayıpsız | Geliştirme/test |
İsimlerdeki K harfi k-quant (karma bit derinliği) anlamına gelir: bazı katmanlar daha yüksek bit ile korunur. M "medium" yani orta büyüklük demektir (S = small, L = large da mevcuttur).
HuggingFace Hub'dan GGUF İndirmek
# pip install huggingface_hub
huggingface-cli download \
TheBloke/Llama-2-7B-Chat-GGUF \
llama-2-7b-chat.Q4_K_M.gguf \
--local-dir ./models \
--local-dir-use-symlinks False
from huggingface_hub import hf_hub_download
model_path = hf_hub_download(
repo_id="bartowski/Llama-3.2-3B-Instruct-GGUF",
filename="Llama-3.2-3B-Instruct-Q4_K_M.gguf",
local_dir="./models",
)
print(f"İndirildi: {model_path}")
HuggingFace Hub'da GGUF modelleri genellikle TheBloke, bartowski veya lmstudio-community organizasyonları tarafından yayınlanmaktadır. Orijinal model adına -GGUF ekleyerek arama yapabilirsiniz.
05 llama.cpp ile CPU Inference
Saf C/C++ ile yazılmış llama.cpp, tüketici donanımlarında LLM inference için en verimli CPU çözümüdür.
llama.cpp, Georgi Gerganov tarafından geliştirilmiş, bağımlılık gerektirmeyen saf C/C++ LLM inference motorudur. CPU inference için SIMD optimizasyonları (AVX2, AVX-512, ARM NEON) içerir; aynı zamanda CUDA, Metal, OpenCL ve Vulkan backend'lerini destekler.
Derleme ve Kurulum
# Kaynak kodu indir
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
# CPU-only derleme
cmake -B build
cmake --build build --config Release -j $(nproc)
# CUDA ile derleme
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j $(nproc)
# macOS Metal ile derleme
cmake -B build -DGGML_METAL=ON
cmake --build build --config Release -j $(nproc)
Komut Satırı Inference
# Tek seferlik sorgu
./build/bin/llama-cli \
-m ./models/llama-3.2-3b-q4_k_m.gguf \
-p "Transformer mimarisi nedir?" \
-n 200 \ # max token sayısı
--threads 8 # CPU thread sayısı
# İnteraktif sohbet modu
./build/bin/llama-cli \
-m ./models/llama-3.2-3b-q4_k_m.gguf \
-i \ # interactive mode
-c 4096 \ # context window
--threads 8 \
--temp 0.7 \ # temperature
--top-p 0.9 \ # nucleus sampling
--top-k 40 # top-k sampling
Python Binding ile Kullanım
# pip install llama-cpp-python
# CUDA: CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python
from llama_cpp import Llama
llm = Llama(
model_path="./models/llama-3.2-3b-q4_k_m.gguf",
n_ctx=4096, # context window uzunluğu
n_threads=8, # CPU thread sayısı (fiziksel çekirdek)
n_gpu_layers=0, # GPU'ya yüklenen katman: 0=CPU-only, -1=tümü
verbose=False,
)
# Basit tamamlama
output = llm(
"Makine öğrenmesi nedir? Kısaca açıkla:",
max_tokens=256,
temperature=0.7,
top_p=0.9,
top_k=40,
stop=["\n\n", "###"],
echo=False,
)
print(output["choices"][0]["text"])
# Chat completion (OpenAI uyumlu)
response = llm.create_chat_completion(
messages=[
{"role": "system", "content": "Sen yardımcı bir asistansın."},
{"role": "user", "content": "Türkiye'nin başkenti neresidir?"},
],
temperature=0.1,
max_tokens=128,
)
print(response["choices"][0]["message"]["content"])
GPU Offloading
GPU'nuz modelin tamamını barındırmaya yetmiyorsa hibrit mod kullanabilirsiniz: bir kısım katmanı GPU'ya, kalanı CPU'ya yükleyin. n_gpu_layers=20 gibi bir değer deneyerek VRAM'i ne kadar doldurduğunu gözlemleyin.
llm = Llama(
model_path="./models/llama-3.1-8b-q4_k_m.gguf",
n_ctx=2048,
n_threads=6,
n_gpu_layers=20, # 20 katman GPU'ya (RTX 3060 8GB için uygun)
n_batch=512, # prompt işleme batch boyutu
)
06 Ollama — Lokal Model Sunucusu
Ollama, LLM modellerini Docker benzeri bir deneyimle indirip çalıştırmanı sağlayan lokal model yönetim platformudur.
Ollama, llama.cpp üzerine inşa edilmiş bir model sunucu ve yönetim aracıdır. Docker'ın imaj sistemi gibi, modelleri "pull" ederek indirip "run" ile başlatırsınız. Otomatik GPU/CPU algılama, model versiyon yönetimi ve REST API ile hem geliştirme hem üretim için uygundur.
Kurulum ve Temel Komutlar
# Linux/macOS kurulumu
curl -fsSL https://ollama.com/install.sh | sh
# Model indir ve çalıştır
ollama pull llama3.2
ollama run llama3.2
# Diğer faydalı komutlar
ollama list # indirilen modelleri listele
ollama show llama3.2 # model bilgilerini göster
ollama rm llama3.2 # modeli sil
ollama ps # çalışan modelleri listele
ollama serve # sunucuyu manuel başlat (genelde otomatik)
REST API
Ollama varsayılan olarak http://localhost:11434 adresinde bir REST API sunar. OpenAI uyumlu endpoint'ler de mevcuttur.
# Generate endpoint
curl http://localhost:11434/api/generate -d '{
"model": "llama3.2",
"prompt": "Yapay zeka nedir?",
"stream": false
}'
# Chat endpoint (OpenAI uyumlu)
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.2",
"messages": [{"role": "user", "content": "Merhaba!"}]
}'
Modelfile ile Özel Model Tanımı
# Temel model
FROM llama3.2
# Sistem prompt'u
SYSTEM """Sen Türkçe konuşan, yardımsever bir teknik asistansın.
Cevaplarında her zaman kod örnekleri ver ve açık bir dil kullan."""
# Parametreler
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER top_k 40
PARAMETER num_ctx 4096
# Sohbet şablonu (opsiyonel)
TEMPLATE """<|system|>
<|end|>
<|user|>
<|end|>
<|assistant|>
<|end|>"""
# Modelfile'dan özel model oluştur
ollama create teknik-asistan -f ./Modelfile
ollama run teknik-asistan
Python ile Ollama API
# pip install ollama
import ollama
# Basit sorgu
response = ollama.chat(
model="llama3.2",
messages=[
{"role": "user", "content": "Python'da decorators ne işe yarar?"}
]
)
print(response["message"]["content"])
# Streaming yanıt
for chunk in ollama.chat(
model="llama3.2",
messages=[{"role": "user", "content": "Fibonacci serisini açıkla"}],
stream=True,
):
print(chunk["message"]["content"], end="", flush=True)
# OpenAI uyumlu istemci ile
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama", # herhangi bir değer olabilir
)
response = client.chat.completions.create(
model="llama3.2",
messages=[{"role": "user", "content": "Merhaba!"}],
)
print(response.choices[0].message.content)
07 KV Cache ve Flash Attention
Inference verimliliğini dramatik biçimde artıran iki temel optimizasyon: KV cache ve IO-aware Flash Attention.
KV Cache Nedir?
Transformer'ın attention mekanizması, her token için Key ve Value matrislerini hesaplar. Autoregressive üretimde (bir seferde bir token üretme), önceki token'ların K ve V değerleri değişmez — bunları yeniden hesaplamak gereksiz iştir. KV cache, bu değerleri bellekte saklayarak her adımda yalnızca yeni token'ın K/V'sini hesaplar.
KV cache olmadan: Her token üretiminde tüm geçmiş token'lar için K,V hesapla → O(n²) maliyet KV cache ile: Yalnızca yeni token K,V hesapla, önbellekten oku → O(n) maliyet
Ancak KV cache bellek maliyeti yüksektir: bir 7B model için 2048 token context window'da birkaç GB yer kaplayabilir.
PagedAttention (vLLM)
PagedAttention, işletim sistemlerinin sanal bellek sayfalama konseptini KV cache'e uygular. Cache bloklara (page) ayrılır; bu sayede bellek fragmantasyonu önlenir ve farklı uzunluktaki istek grupları daha verimli işlenir.
Flash Attention
Standart attention hesaplaması büyük ara matrisleri HBM (yüksek bant genişlikli bellek, GPU'nun ana VRAM'i) üzerine yazar ve geri okur. Flash Attention, tüm attention hesaplamasını SRAM (on-chip, çok hızlı ama küçük) üzerinde gerçekleştirir; HBM okuma/yazma sayısını dramatik biçimde azaltır.
Standart: Q,K,V → HBM'e yaz → S=QK^T okuyup yaz → P=softmax(S) yaz → O=PV yaz Flash: Q,K,V → küçük bloklar halinde SRAM'e al → hepsini SRAM'de hesapla → tek seferde HBM'e yaz
# pip install flash-attn --no-build-isolation
# (CUDA 11.8+ ve PyTorch 2.0+ gerektirir)
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
model_name = "meta-llama/Llama-3.1-8B-Instruct"
# Flash Attention 2 ile model yükle
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
attn_implementation="flash_attention_2", # anahtar parametre
device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
# Bellek kullanımını karşılaştır
def measure_memory(model, prompt, max_new_tokens=100):
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
torch.cuda.reset_peak_memory_stats()
_ = model.generate(**inputs, max_new_tokens=max_new_tokens)
peak_mem = torch.cuda.max_memory_allocated() / 1e9
return peak_mem
mem = measure_memory(model, "Transformer nedir?")
print(f"Peak memory (Flash Attn 2): {mem:.2f} GB")
Flash Attention 2 yalnızca CUDA GPU'larında çalışır ve derlenmiş bir C++ uzantısı gerektirir. Kurulum için CUDA toolkit'in sisteminizde yüklü olması şarttır. CPU ya da Metal backend kullanıyorsanız bu parametreyi kaldırın.
08 vLLM ile Production Serving
PagedAttention ve continuous batching ile yüksek throughput LLM servisini sağlayan vLLM, üretim ortamlarının tercihi.
vLLM, UC Berkeley'den araştırmacılar tarafından geliştirilen yüksek performanslı LLM inference ve serving motorudur. Temel inovasyonu olan PagedAttention, KV cache kullanımını 2–4× daha verimli hale getirir; continuous batching ise gelen istekleri dinamik olarak gruplayarak GPU'yu meşgul tutar.
Kurulum ve Sunucu Başlatma
# pip install vllm
# OpenAI uyumlu API sunucusu başlat
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct \
--dtype bfloat16 \
--max-model-len 8192 \
--tensor-parallel-size 1 \ # GPU sayısı
--gpu-memory-utilization 0.90 \ # VRAM kullanım oranı
--port 8000
Python Client
from openai import OpenAI
# vLLM OpenAI uyumlu endpoint
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="vllm",
)
response = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[
{"role": "system", "content": "Sen yardımcı bir asistansın."},
{"role": "user", "content": "Attention mekanizmasını açıkla."},
],
max_tokens=512,
temperature=0.7,
)
print(response.choices[0].message.content)
# Toplu istek örneği (throughput testi)
import time
prompts = [
"Python nedir?",
"Makine öğrenmesi nedir?",
"Transformer ne anlama gelir?",
"GPU neden AI için önemlidir?",
]
start = time.time()
responses = []
for p in prompts:
r = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[{"role": "user", "content": p}],
max_tokens=128,
)
responses.append(r)
elapsed = time.time() - start
total_tokens = sum(r.usage.completion_tokens for r in responses)
print(f"Toplam: {total_tokens} token | Süre: {elapsed:.2f}s | Throughput: {total_tokens/elapsed:.1f} tok/s")
Tensor Parallelism
Birden fazla GPU'ya sahipseniz --tensor-parallel-size ile model ağırlıklarını GPU'lara dağıtabilirsiniz. 70B gibi büyük modeller için 4× veya 8× GPU gerekebilir.
Throughput Karşılaştırması
| Motor | Donanım | Throughput (tok/s) | Latency (TTFT) | Notlar |
|---|---|---|---|---|
| HuggingFace (vanilla) | A100 80GB | ~40–60 | ~800ms | batch_size=1, FP16 |
| vLLM | A100 80GB | ~150–250 | ~200ms | continuous batching, PagedAttn |
| llama.cpp (CPU) | 32-core CPU | ~15–25 | ~2s | Q4_K_M, 8 thread |
| llama.cpp (GPU) | RTX 4090 | ~80–120 | ~300ms | Q4_K_M, tam GPU offload |
| Ollama | RTX 4090 | ~70–100 | ~350ms | llama.cpp backend |
Throughput değerleri, model boyutuna, batch size'a, prompt uzunluğuna ve donanım konfigürasyonuna göre önemli ölçüde değişir. Bu değerler Llama 3.1 8B için yaklaşık referans değerlerdir.