Uçtan Uca Alana Özgü Kumru Base Modeli Eğitimi: Veri Hazırlığından Üretim Ortamına Optimizasyon…
Bu çalışmada, Enerji Piyasası Düzenleme Kurumu (EPDK) mevzuatına hakim, domain-specific bir dil modeli geliştirilmesi süreçleri uçtan uca…
Uçtan Uca Alana Özgü Kumru Base Modeli Eğitimi: Veri Hazırlığından Üretim Ortamına Optimizasyon Vaka Analizi

Bu çalışmada, Enerji Piyasası Düzenleme Kurumu (EPDK) mevzuatına hakim, domain-specific bir dil modeli geliştirilmesi süreçleri uçtan uca ele alınmıştır. Proje kapsamında, yapısal olmayan ham verilerin işlenmesinden başlayarak DAPT (Domain-Adaptive Pre-Training) ve DPO (Direct Preference Optimization) yöntemleriyle modelin eğitilmesi, vLLM motoru üzerinde deployment, AWQ quantization ve model performansı üzerindeki etkilerinin ölçülmesi aşamaları gerçekleştirilmiştir.
Çalışmanın merkezinde, 2 milyar parametreli bir küçük dil modelinin (Kumru-2B), yüksek doğruluk gerektiren regülasyon alanında bir RAG (Retrieval-Augmented Generation) olarak konumlandırılıp konumlandırılamayacağı sorusu yatmaktadır.
Bölüm 1: Veri Seti Hazırlanması
- Veri Toplama: EPDK mevzuatı, kurul kararları, yönetmelikler (PDF, Word, Excel) toplandı.
- Akıllı OCR (Qwen-VL): Standart OCR araçlarının (Tesseract) okuyamadığı tablolar ve çift sütunlu sayfalar, bir Vision-Language Model (Qwen-2.5-VL) ile metne dönüştürüldü.
- Sentetik Veri Üretimi (Gemini): Modelin sadece bilgiyi değil, “tercih edilen stili” de öğrenmesi için Google Gemini kullanılarak binlerce
(prompt, chosen, rejected)üçlüsü içeren DPO Veri Seti oluşturuldu.
Bölüm 2: Model Eğitimi
Türkçe yetenekleri kanıtlanmış ama alan bilgisi olmayan bir temel model, “EPDK Uzmanı”na dönüştürüldü.
- Base Model Seçimi:
vngrs-ai/Kumru-2B-Base(Türkçe SLM). - DAPT: Ham corpus ile terminoloji adaptasyonu.
- DPO (Direct Preference Optimization): Modele “hangi cevabın daha güvenli, resmi ve doğru olduğu” (insan tercihi) öğretildi. Reward model kullanılmadan doğrudan tercih optimizasyonu yapıldı.
Bölüm 3: Quantizasyon
Modelin farklı donanımlarda çalışabilmesi için oluşturuldu.
- GGUF: CPU’lar için (llama.cpp).
- GPTQ: Orta seviye GPU’lar için.
- AWQ (4-bit): Production (vLLM) için hız odaklı sıkıştırma.
Bölüm 4: Deployment
Model, sadece bir dosya olmaktan çıkıp canlı bir API servisine dönüştü.
- Altyapı: Modal üzerinde A10G GPU kiralandı.
- Engine: vLLM kullanılarak “Continuous Batching” ve “PagedAttention” teknolojileriyle servis edildi.
Bölüm 5: Benchmarking ve Değerlendirme
Projenin en kritik “karar anı” burasıydı. Hız ve Zeka teraziye kondu.
- Throughput, Toplam Süre (50 istek), Latency, P99 Latency
- LLM-as-a-judge
Bu vaka analizi, sadece bir model eğitim sürecini değil veriden servise uzanan modern bir LLMOps yaşam döngüsünü simüle etmek amacıyla yapılmıştır.
Bölüm 1.1: EPDK Corpus Hazırlanması
EPDK tarafından yayımlanan mevzuat, kurul kararları ve teknik tebliğler, ağırlıklı olarak taranmış PDF, Word ve Excel formatlarında, unstructured bir veri yığını halindedir. EPDK elektrik mevzuatları üzerinden hazırlanan veri kümesi, diğer EPDK mevzuatlarıyla kolayca birleştirilebilir, ardıl bölümlerden geçirilebilir ve veri tabanında saklanabilir. Dokümantasyonlar klasik olarak firecrawl, selenium gibi kütüphane yardımlarıyla yerele çekilmiştir.
Tesseract gibi OCR motorlarının, özellikle çok sütunlu sayfa düzenlerinde ve karmaşık tablo yapılarında metin akışını (text flow) bozduğu ve anlamsal bütünlüğü koruyamadığı gözlemlenmiştir. Mevzuat metinlerindeki madde/fıkra hiyerarşisinin korunması kritik olduğundan, standart yöntemler yetersiz kalmıştır. Bu yöntemle çıkartılan veri seti versiyon 1 olarak huggingface’te bulunmaktadır.
Bu sorunu aşmak için, dokümanı sadece karakter yığını olarak değil, görsel bir bütün olarak analiz edebilen Qwen-2.5-VL Vision-Language modeli bir OCR motoru olarak sisteme entegre edilmiştir. Bu yaklaşım ile:
- Çift sütunlu metinlerin doğru okuma sırasıyla dijitalleştirilmesi sağlanmış,
- Tablo içerisindeki verilerin satır/sütun ilişkisi korunarak Markdown formatına dönüştürülmesi başarılmış,
- Karakter Hata Oranı (CER — Character Error Rate) minimize edilmiştir.
Elde edilen ham metinler üzerinde Regex tabanlı temizleme işlemleri uygulanarak; sayfa numaraları, üst/alt bilgiler ve tekrarlayan gürültü verileri ayıklanmış ve nihai[**ogulcanakca/epdk_corpus](https://huggingface.co/datasets/ogulcanakca/epdk_corpus)** veri seti oluşturulmuştur. Qwen modeli qwen_ocr_batch fonksiyonunda bu şekilde kullanılmıştır:
def qwen_ocr_batch(images, prompt="You are an OCR engine. Transcribe the text exactly. Only output the text."):
"""
Qwen3-VL (v3 API) için chat-formatlı OCR fonksiyonu.
Hem path (str) hem PIL.Image nesnesi alabilir.
"""
all_texts = []
for img_in in images:
if isinstance(img_in, str):
from PIL import Image
img = Image.open(img_in).convert("RGB")
else:
img = img_in
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": img},
{"type": "text", "text": prompt},
],
}
]
text_prompt = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
image_inputs = processor.image_processor(images=[img], return_tensors="pt")["pixel_values"].to(model.device)
inputs = processor(
text=[text_prompt],
images=[img],
return_tensors="pt"
).to(model.device)
inputs["pixel_values"] = image_inputs
# Model tahmini
with torch.inference_mode():
out = model.generate(**inputs, max_new_tokens=MAX_NEW_TOKENS)
# Çıktıyı çöz
text = processor.batch_decode(out, skip_special_tokens=True)[0]
all_texts.append(text.strip())
return all_texts
Dokümanlardan veri setinin hazırlandığı pipeline’ın tam hali kaggle’da yer almaktadır. Pipeline çıktısı şu şekilde bir yapı içeren veri seti oluşturmaktadır:
{
"id": "b3f7a6d9c49e",
"text": "Enerji Piyasası Düzenleme Kurumundan:\nKURUL KARARI\nKarar No: 9284 Karar Tarihi: 02/04/2020\n...",
"kind": "docx",
"method": "python-docx"
}
Bölüm 1.2: Sentetik Veri Üretimi ve DPO Hazırlığı
Modelin eğitimi için sadece ham metin yeterli değildir. Modelin hangi soruya nasıl cevap vermesi gerektiğinin (Instruction Tuning) ve hangi cevabın “doğru/güvenli” olduğunun (Preference Learning) öğretilmesi gerekmektedir. Bu amaçla “LLM-as-a-Data-Generator” metodolojisi izlenmiştir.
Google Gemini 2.5 modelleri (Pro ve Flash) kullanılarak, EPDK corpus’u içerisindeki metinlerden otomatik olarak DPO eğitim verisi üretilmiştir. Her bir metin parçası için üçlü bir veri yapısı (Prompt, Chosen, Rejected) oluşturulması hedeflenmiştir.
Veri üretim sürecinde çıktıların tutarlılığını ve formatını garanti altına almak amacıyla Structured Output desteği ve Pydantic kütüphanesi ile katı bir schema tanımlanmıştır:
class DPO_Triplet(BaseModel):
"""
Bir DPO (Direct Preference Optimization) veri seti için
prompt, chosen (tercih edilen) ve rejected (reddedilen) cevapları
içeren yapı.
"""
prompt: str = Field(description="Sağlanan EPDK metnine dayalı olarak oluşturulan spesifik bir soru veya talimat.")
chosen: str = Field(description="Soruya verilen ideal, doğru, eksiksiz ve YALNIZCA metne sadık kalarak oluşturulan 'seçilen' cevap.")
rejected: str = Field(description="Soruya verilen ancak kasıtlı olarak kusurlu olan 'reddedilen' cevap. Bu cevap, kulağa makul gelebilir ama 'chosen' cevaptan açıkça daha kötü olmalıdır (örneğin eksik bilgi, hafif yanlış tarih/sayı, ilgisiz detay ekleme vb.).")
Seçilen Gemini modeline verilen meta prompt template bu şekildedir:
META_PROMPT_TEMPLATE = """
Sen bir DPO veri seti yaratıcısısın ve görevin Enerji Piyasası Düzenleme Kurumu (EPDK) metinlerini analiz etmektir.
YALNIZCA aşağıda sağlanan EPDK metnini bağlam (context) olarak kullanarak, {n} adet (prompt, chosen, rejected) üçlüsü oluştur.
1. **prompt:** Metin hakkında spesifik bir soru veya talimat oluştur.
2. **chosen:** Bu 'prompt' için metindeki bilgilere dayanarak mükemmel, doğru ve eksiksiz bir cevap oluştur.
3. **rejected:** Bu 'prompt' için makul görünen ama 'chosen' cevaptan açıkça daha kötü (örneğin; bilgiyi eksik veren, metindeki bir sayıyı/tarihi hafifçe yanlış yazan veya konuyu biraz saptıran) bir cevap oluştur.
Kesinlikle metnin dışından bilgi ekleme. Cevabın JSON formatında olmalı.
--- BAĞLAM (CONTEXT) METNİ ---
{epdk_metni}
--- BİTTİ ---
"""
Sentetik veri kümesi hazırlanmasının yer aldığı pipeline kaggle’da yer almaktadır. Bu süreç sonucunda, insan etiketleme maliyetine katlanılmadan, modelin “tercih edilen davranışı” öğrenmesini sağlayacak 24.500+ satırlık sentetik bir veri seti [ogulcanakca/epdk_dpo](https://huggingface.co/datasets/ogulcanakca/epdk_dpo)elde edilmiştir. Pipeline çıktısı şu şekilde bir yapı içeren veri seti oluşturmaktadır:
{
"original_id": "e7480801c5b2",
"sub_id": "e7480801c5b2_8",
"prompt": "EPİAŞ'ın elektrik piyasasında tanımları nelerdir?",
"chosen": "EPİAŞ, Enerji Piyasaları İşletme Anonim Şirketini; Kurul, Enerji Piyasası Düzenleme Kurulunu; Piyasa İşletim Gelir Tavanı ise EPİAŞ’ın bir tarife yılında toplayacağı gelirin üst sınırını ifade eder (MADDE 4/1).",
"rejected": "EPİAŞ, Enerji Piyasaları İşletme Anonim Şirketidir ve Piyasa İşletim Gelir Tavanı, EPİAŞ'ın sadece bir yıllık gelir üst sınırıdır."
}
Bölüm 2: Model Eğitimi ve Hizalama Stratejileri
Veri setinin hazırlanmasının ardından, projenin ikinci fazı olan model eğitimi sürecine geçilmiştir. Bu aşamada temel hedef, general purpose bir Türkçe dil modelinin, EPDK terminolojisine hakim, mevzuat sorularına resmi bir dille yanıt verebilen ve halüsinasyon oranı minimize edilmiş bir “Instruct” modeline dönüştürülmesidir.
Eğitim mimarisi olarak, donanım kısıtları ve inference hızı gözetilerek 2 milyar parametreli [vngrs-ai/Kumru-2B-Base](https://huggingface.co/vngrs-ai/Kumru-2B-Base) modeli temel alınmıştır. Eğitim süreci iki bölümde kurgulanmıştır: DAPT ve DPO.
Bölüm 2.1: Alan Bilgisi Yüklemesi (DAPT)
General purpose dil modellerinin, enerji piyasası gibi yüksek teknik yoğunluğa sahip alanlarda doğrudan kullanılması, terminoloji eksikliği ve bağlam kopukluğu risklerini barındırmaktadır. Bu nedenle, alanın literatürüne ve jargonuna hakim olması (Domain Adaptation) hedeflenmiştir.
Bu aşamada, hazırlanan [**ogulcanakca/epdk_corpus](https://huggingface.co/datasets/ogulcanakca/epdk_corpus) veri setindeki ham mevzuat metinleri, kurul kararları ve teknik tebliğler kullanılarak modele Continued Pre-training uygulanmıştır. Unsupervised Learning prensibiyle yürütülen bu süreçte, modelin "Next Token Prediction" **görevi üzerinden EPDK mevzuatının mantıksal akışını, kanun yapım tekniğini ve sektörel kavramları (Örn: YEKDEM, PİGT, TORETOSAF) içselleştirmesi sağlanmıştır.
Donanım verimliliğini korumak adına bu aşamada QLoRA tekniği tercih edilmiştir. Bu eğitimdeki config’ler bu şekildedir:
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
peft_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
target_modules=[
"q_proj",
"k_proj",
"v_proj",
"o_proj",
"gate_proj",
"up_proj",
"down_proj",
],
)
sft_config_args = SFTConfig(
max_length=4096,
packing=True,
dataset_text_field="text",
output_dir=output_dir,
num_train_epochs=3,
per_device_train_batch_size=1,
gradient_accumulation_steps=16,
gradient_checkpointing=True,
bf16=True,
optim="paged_adamw_8bit",
learning_rate=1e-5,
lr_scheduler_type="cosine",
warmup_ratio=0.03,
logging_strategy="steps",
logging_steps=10,
save_strategy="epoch",
save_total_limit=2,
push_to_hub=True,
hub_model_id=hub_model_id,
hub_strategy="end",
report_to="wandb",
run_name=new_model_name
)
QLoRA ile 2,392,095,744 parametresi bulunan Kumru 2B Base modelinin 16,957,440 kadar parametresi eğitilmiştir. Yüzdesel olarak bu %0.7089'a denk gelmektedir. Bu eğitimin detayları kaggle’da yer almaktadır. Çeşitli metriklerinin yer aldığı WandB raporu da burada yer almaktadır.
Bölüm 2.2: DPO ile Tercih Optimizasyonu ve Hizalama
DAPT eğitimi almış bir model, terminoloji açısından hakim olsa da, yanıtın doğruluğu ve üslubu konusunda her zaman istenilen standartta olmayabilir. Geleneksel RLHF (Reinforcement Learning from Human Feedback) yöntemlerinin aksine, ayrı bir reward model gerektirmeyen ve daha stabil bir eğitim sunan DPO yöntemi tercih edilmiştir.
Önceki bölümde Gemini kullanılarak üretilen sentetik veri seti ([ogulcanakca/epdk_dpo](https://huggingface.co/datasets/ogulcanakca/epdk_dpo)), bu aşamada devreye alınmıştır. Model, "Chosen" (Doğru/Resmi) ve "Rejected" (Hatalı/Eksik) yanıtlar arasındaki farkı (log-probability farkları üzerinden) öğrenmeye zorlanmıştır.
Eğitim stabilitesini artırmak ve overfitting’i önlemek adına IPO (Identity Preference Optimization) loss fonksiyonu kullanılmış ve kritik hiperparametreler aşağıdaki gibi yapılandırılmıştır:
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
peft_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
)
training_args = DPOConfig(
output_dir=f"./results/{run_name}",
per_device_train_batch_size=4,
gradient_accumulation_steps=2,
learning_rate=5e-5,
num_train_epochs=1,
lr_scheduler_type="cosine",
optim="paged_adamw_8bit",
logging_steps=25,
save_steps=25,
save_total_limit=2,
report_to="wandb",
run_name=run_name,
fp16=not torch.cuda.is_bf16_supported(),
bf16=torch.cuda.is_bf16_supported(),
beta=0.1,
max_length=2048,
max_prompt_length=1024,
gradient_checkpointing=True,
precompute_ref_log_probs=False,
loss_type="ipo",
eval_strategy="steps",
eval_steps=25,
generate_during_eval=True,
remove_unused_columns=False,
per_device_eval_batch_size=8,
)
Bu config’lerle gerçekleştirilen eğitim sonucunda, modelin validation setindeki “doğru cevabı seçme” (accuracy) oranının %94 seviyelerine ulaştığı ve margin değerlerinin istikrarlı bir şekilde artma trendinde olduğu gözlemlenmiştir. Bu durum, modelin sadece ezberlemediğini, tercih edilen yanıt kalıbını genelleştirebildiğini göstermektedir. Bu eğitimin detayları kaggle’da yer almaktadır. Çeşitli metriklerinin yer aldığı WandB raporu da burada yer almaktadır.
Bölüm 3: Model Birleştirme, Optimizasyon
Eğitim süreçlerinin tamamlanmasının ardından, modelin farklı donanım kısıtları altında çalışabilir hale getirilmesi hedeflenmiştir. Bu bölümde ayrı ayrı eğitilen adaptör katmanlarının merge edilmesi, modelin farklı formatlarda quantize edilmesi ve ölçeklenebilir bir inference server’ı üzerinde servis edilmesi süreçleri gerçekleştirilmiştir.
Bölüm 3.1: Adaptör Birleştirme
Önceki bölümlerde, DAPT (Alan Bilgisi) ve DPO (Davranışsal Hizalama) aşamaları için ayrı LoRA (Low-Rank Adaptation) adaptörleri eğitilmiştir. Inference sırasında ek latency maliyetinden kaçınmak ve model yönetimini basitleştirmek amacıyla, bu adaptör katmanları [vngrs-ai/Kumru-2B-Base](https://huggingface.co/vngrs-ai/Kumru-2B-Base) temel modeli ile kalıcı olarak birleştirilmiştir.
from peft import AutoPeftModelForCausalLM
from transformers import AutoTokenizer
base = "vngrs-ai/Kumru-2B"
sft_adapter = "./epdk_sft_adapter"
merged_sft = "./kumru_epdk_sft_merged"
dpo_adapter = "./epdk_dpo_adapter"
final_merged = "./kumru_epdk_final_merged"
model = AutoPeftModelForCausalLM.from_pretrained(sft_adapter, device_map="auto")
model = model.merge_and_unload()
model.save_pretrained(merged_sft, safe_serialization=True)
tok = AutoTokenizer.from_pretrained(base, use_fast=True)
tok.save_pretrained(merged_sft)
model = AutoPeftModelForCausalLM.from_pretrained(dpo_adapter, device_map="auto")
model = model.merge_and_unload()
model.save_pretrained(final_merged, safe_serialization=True)
Bu işlem sonucunda, harici bir adaptör yüklemesine ihtiyaç duymayan, EPDK mevzuatına hakim ve talimat takip yeteneği kazanmış tek bir nihai model weights elde edilmiştir. Nihai model huggingface’te, örnek prompt ve çıktısı aşağıda yer almaktadır.
{
"prompt": "2007 yılına ait Türkiye Ortalama Elektrik Toptan Satış Fiyatının (TORETOSAF) değeri nedir?",
"Instruct Model": "TORETOSAF, 2013 yılı için, 12 aylık TÜFE ile TEFE arasındaki farktır. 2013 yılı için % 10,11’dir."
}
Bölüm 3.2: Quantization Stratejileri
Modelin sadece yüksek kapasiteli sunucularda değil, kısıtlı kaynağa sahip edge device’larda veya tüketici sınıfı GPU’larda da çalışabilmesi için bir “Model Ailesi” yaklaşımı benimsenmiştir. Nihai model, kullanım senaryosuna göre üç farklı formatta optimize edilmiştir:
- GGUF (Generic GPT Unified Format) (CPU Inference): Modelin, GPU erişimi olmayan ortamlarda (örn. yerel bilgisayarlar)
llama.cppkütüphanesi ile çalıştırılabilmesi için GGUF formatına dönüştürülmüştür. - GPTQ (Generative Pre-trained Transformer Quantization) (GPU Inference): Standart PyTorch ve Hugging Face entegrasyonları ile uyumlu, orta seviye GPU’larda (T4/L4) verimli çalışması için 4-bit GPTQ sıkıştırması uygulanmıştır.
- AWQ (Production Inference): Canlı sistemde kullanılacak vLLM motoru ile en yüksek uyumluluğu ve memory bandwidth efficiency sağladığı için, activation-aware 4-bit AWQ sıkıştırması, ana dağıtım formatı olarak belirlenmiş, benchmark ve değerlendirme yapılmıştır.
Bölüm 3.2.1: GGUF
Yüksek VRAM kapasiteli GPU’lara erişimin olmadığı lokal geliştirme ortamları ve uç cihazlar (örneğin Apple Silicon veya standart laptoplar) için model GGUF formatına dönüştürülmüştür. llama.cpp ekosistemi ile tam uyumlu olan bu format sayesinde, model ağırlıkları 4-bit (Q4_K_S, Q4_K_M), 5-bit (Q5_K_M), 6-bit (Q6_K) ve 8-bit (Q8_0) seviyelerine indirilerek RAM kullanımı minimize edilmiş ve modelin standart bir CPU üzerinde makul hızlarda çalıştırılması mümkün kılınmıştır. Ek olarak 16-bit versiyonu da huggingface’te yer almaktadır.
Ubuntu WSL’de sırasıyla aşağıdaki komutlar çalıştırılmıştır:
gh repo clone ggml-org/llama.cpp
cd ~/llama.cpp
git pull
pip3 install -r requirements.txt
make clean
make -j$(nproc)
cd ~/llama.cpp
python3 convert-hf-to-gguf.py ./kumru2b_epdk_merged --outfile kumru2b_epdk_merged_f16.gguf
./quantize kumru2b_epdk_merged_f16.gguf kumru2b_epdk_merged_q4_k_s.gguf Q4_K_S
./quantize kumru2b_epdk_merged_f16.gguf kumru2b_epdk_merged_q4_k_m.gguf Q4_K_M
./quantize kumru2b_epdk_merged_f16.gguf kumru2b_epdk_merged_q5_k_m.gguf Q5_K_M
./quantize kumru2b_epdk_merged_f16.gguf kumru2b_epdk_merged_q6_k.gguf Q6_K
./quantize kumru2b_epdk_merged_f16.gguf kumru2b_epdk_merged_q8_0.gguf Q8_0
Bölüm 3.2.2: GPTQ
Orta seviye sunucu GPU’ları (örneğin NVIDIA T4 veya L4) ve standart Hugging Face transformers kütüphanesi ile entegrasyon gerektiren senaryolar için GPTQ yöntemi uygulanmıştır. Kalibrasyon veri seti olarak c4 kullanılarak gerçekleştirilen bu işlemde, ağırlık matrisleri row-wise sıkıştırılmıştır. GPTQ formatındaki model huggingface’te, config ve kod aşağıda yer almaktadır.
from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig
model_name = "ogulcanakca/Kumru-2B-EPDK-Instruct"
save_dir = "kumru-2b-epdk-gptq-4bit"
tokenizer = AutoTokenizer.from_pretrained(model_name)
quant_config = GPTQConfig(
bits=4,
group_size=128,
dataset="c4",
tokenizer=tokenizer,
)
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto",
quantization_config=quant_config,
)
model.save_pretrained(save_dir)
tokenizer.save_pretrained(save_dir)
Bölüm 3.2.3: AWQ
Production ortamında kullanılacak vLLM motoru ile en yüksek uyumluluğu ve throughput sağlaması nedeniyle, AWQ ana dağıtım formatı olarak değerlendirilmiştir. AWQ, tüm ağırlıkları eşit şekilde sıkıştırmak yerine, modelin aktivasyonlarında önemli rol oynayan ağırlıkları koruyarak hassasiyet kaybını minimize etmeyi amaçlayan bir tekniktir. Bu projede, üretim ortamındaki memory bandwidth efficiency’i maksimize etmek amacıyla model, autoawq kütüphanesi kullanılarak 4-bit hassasiyete indirgenmiştir. AWQ formatındaki model huggingface’te, config ve kod aşağıda yer almaktadır.
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_name = "ogulcanakca/Kumru-2B-EPDK-Instruct"
save_dir = "kumru-2b-epdk-awq"
model = AutoAWQForCausalLM.from_pretrained(
model_name,
safetensors=True,
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
quant_config = {
"zero_point": True,
"q_group_size": 128,
"w_bit": 4,
}
model.quantize(tokenizer, quant_config)
model.save_quantized(save_dir)
tokenizer.save_pretrained(save_dir)
Bölüm 4: Üretim Ortamı Mimarisi: vLLM ve Modal Entegrasyonu
Modelin son kullanıcılara ölçeklenebilir, yüksek performanslı bir API olarak sunulması aşamasında, serverless GPU altyapısı sağlayan Modal platformu ve Inference Engine olarak vLLM tercih edilmiştir. Bu mimari seçiminde, kaynak verimliliği ve anlık talep artışlarına (burst traffic) yanıt verebilme yeteneği belirleyici olmuştur.
Bölüm 4.1: Mikro-Servis Altyapısı ve Donanım Seçimi
Production ortamı için maliyet/performans dengesi gözetilerek NVIDIA A10G (24GB VRAM) GPU üzerinde bir mikro-servis mimarisi kurgulanmıştır. Modelin OpenAI-compatible bir API arayüzü ile sunulması sağlanarak, mevcut RAG ve Chatbot sistemlerine entegrasyonu standartlaştırılmıştır.
Bölüm 4.2: vLLM ve Config’leri
Modelin serve edilmesi sırasında, vLLM motorunun sunduğu “PagedAttention” ve “Continuous Batching” teknolojilerinden maksimum düzeyde faydalanmak için aşağıdaki spesifik config’ler uygulanmıştır:
- Cold-Start Optimizasyonu: Sunucusuz mimarilerde kritik bir metrik olan “ilk açılış süresini” minimize etmek amacıyla, vLLM’in CUDA grafiklerini derleme süreci
--enforce-eagerparametresi ile atlanmış; böylece konteynerin ayağa kalkma süresi (boot time) düşürülmüştür. - Context Window Yönetimi: EPDK mevzuat metinlerinin uzunluğu ve RAG senaryoları dikkate alınarak,
--max-model-lenparametresi 4096 token ile sınırlandırılmıştır. Bu kısıtlama, GPU belleğinde KV Cache için ayrılan alanın optimize edilmesini sağlamıştır. - Hassasiyet ve Format: Model ağırlıkları, donanım uyumluluğu ve çıkarım hızı dengesi gözetilerek FP16 (Half Precision) formatında yüklenmiştir.
cmd = [
"vllm", "serve", MODEL_NAME,
"--host", "0.0.0.0",
"--port", "8000",
"--dtype", "half", # FP16 hassasiyet
"--max-model-len", "4096", # Bağlam sınırı
"--tensor-parallel-size", "1", # Tek A10G GPU kullanımı
"--enforce-eager", # Hızlı boot (Eager Mode)
"--trust-remote-code"
]
- Tensor Paralelliği: Modelin boyutu ve GPU mimarisinin kapasitesi dikkate alınarak, vLLM’in
--tensor-parallel-sizeparametresi 1 olarak bırakılmıştır. Bu değer, A10G GPU’nun tek başına 2B parametreli bir modeli rahatlıkla çalıştırabilmesi nedeniyle tercih edilmiştir. Yük artışı veya daha büyük modellerde bu parametre yatay ölçekleme için kolayca artırılabilir yapıdadır. - Cache Yönetimi ve Disk Persistence: Model yükleme sürelerini hızlandırmak için
vllm_cacheve Hugging Facehf_cachedizinleri kalıcı Modal Volume'lar ile eşlenmiştir. Böylece tekrarlanan cold start modelin ve tokenizer'ın yeniden indirilme gereksinimi ortadan kaldırılmıştır. KV Cache ise GPU belleğinde runtime sırasında PagedAttention mekanizması ile dinamik olarak yönetilmektedir. - Uvicorn Sunucu Ayarları: vLLM servisinin OpenAI API ile uyumlu bir HTTP endpoint sunması için, altyapı Uvicorn üzerinde çalışacak şekilde ayarlanmıştır.
--uvicorn-log-level infodeğeri hem performansı hem de hata analizini optimize etmesi nedeniyle seçilmiştir. - Concurrency Optimizasyonu: Modal fonksiyonuna eklenen
@modal.concurrent(max_inputs=32)yapısı, vLLM’in Continuous Batching kabiliyetinden tam faydalanabilmesi için tasarlanmıştır. Böylece aynı anda onlarca istek tek bir büyük batch altında işlenerek GPU verimliliği artırılmıştır. - Model İsmi ve API Uyumlaştırma: vLLM tarafında model sunumu sırasında
--served-model-nameparametresi kullanılarak OpenAI formatında erişilebilen bir model adı atanmıştır (kumru-2b-epdk). Bu sayede istemci tarafında OpenAI SDK ile tamamen şeffaf ve sorunsuz kullanım mümkün hale gelmiştir.
Bu kısmın yer aldığı kod aşağıda bulunmaktadır.
# kumru_vllm_inference.py
import modal
MODEL_NAME = "ogulcanakca/Kumru-2B-EPDK-Instruct-AWQ-4bit"
SERVED_MODEL_NAME = "kumru-2b-epdk"
GPU_TYPE = "A10G"
N_GPU = 1
FAST_BOOT = True
VLLM_PORT = 8000
MIN = 60
vllm_image = (
modal.Image.from_registry(
"nvidia/cuda:12.8.0-devel-ubuntu22.04",
add_python="3.12",
)
.entrypoint([])
.uv_pip_install(
"vllm==0.11.0",
"huggingface-hub==0.36.0",
"flashinfer-python==0.3.1",
)
.env({"HF_XET_HIGH_PERFORMANCE": "1"})
)
hf_cache = modal.Volume.from_name("kumru-hf-cache", create_if_missing=True)
vllm_cache = modal.Volume.from_name("kumru-vllm-cache", create_if_missing=True)
app = modal.App("kumru-vllm-inference")
@app.function(
image=vllm_image,
gpu=GPU_TYPE,
timeout=10 * MIN,
scaledown_window=15 * MIN,
volumes={
"/root/.cache/huggingface": hf_cache,
"/root/.cache/vllm": vllm_cache,
},
)
@modal.concurrent(max_inputs=32)
@modal.web_server(port=VLLM_PORT, startup_timeout=10 * MIN)
def serve():
import subprocess
cmd = [
"vllm",
"serve",
MODEL_NAME,
"--host", "0.0.0.0",
"--port", str(VLLM_PORT),
"--quantization", "awq",
"--max-model-len", "4096",
"--tensor-parallel-size", str(N_GPU),
"--served-model-name", SERVED_MODEL_NAME,
"--trust-remote-code",
"--uvicorn-log-level", "info",
]
if FAST_BOOT:
cmd.append("--enforce-eager")
else:
cmd.append("--no-enforce-eager")
print("Çalıştırılan komut:")
print(" ".join(cmd))
subprocess.Popen(cmd)
@app.local_entrypoint()
def main():
import time, requests
url = serve.web_url
print(f"Sunucu URL'i: {url}")
for _ in range(25):
try:
if requests.get(f"{url}/health").status_code == 200:
print("✅ vLLM ayakta!")
break
except:
time.sleep(2)
print(f"\nOpenAI endpoint: {url}/v1/chat/completions")
print(f"Model adı: {SERVED_MODEL_NAME}")
modal deploy kumru_vllm_inference.py
şeklinde çalıştırılan bu kodu test etmek amaçlı aşağıdaki client hazırlanmıştır.
# test_client.py
import asyncio
from openai import AsyncOpenAI
from transformers import AutoTokenizer
BASE_URL = "https://ogulcanakca--kumru-vllm-inference-serve.modal.run"
MODEL_NAME = "ogulcanakca/Kumru-2B-EPDK-Instruct"
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
client = AsyncOpenAI(
base_url=f"{BASE_URL}/v1",
api_key="dummy"
)
def build_injected_message(query, index):
messages = [
{"role": "system", "content": "Elektrik Piyasası Denetleme Kurulu (EPDK) mevzuatları konusunda uzmansın."},
{"role": "user", "content": f"{query} (Test #{index})"}
]
template_text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
return [
{"role": "user", "content": template_text}
]
async def ask(index):
try:
injected_messages = build_injected_message(
"EPDK'nın kuruluş amacı nedir?",
index
)
response = await client.chat.completions.create(
model="kumru-2b-epdk",
messages=injected_messages,
max_tokens=200,
temperature=0.1,
top_p=0.85,
frequency_penalty=1.2,
presence_penalty=0.8
)
answer = response.choices[0].message.content
return f"\n# {index}\n{answer}\n"
except Exception as e:
return f"[ERROR] #{index} -> {e}"
async def main():
N = 20
print(f"{N} paralel request gönderiliyor...\n")
tasks = [ask(i) for i in range(1, N+1)]
results = await asyncio.gather(*tasks)
print("\n=== SONUÇLAR ===")
for r in results:
print(r)
if __name__ == "__main__":
asyncio.run(main())
python test_client.py
ile çıktılarımızı aynı anda alabildik. Loglardaki önemli çıktılar:
- Model yükleme süresi: ~1.9 s
- GPU KV cache boyutu: 523,616 token
- Prefix cache hit rate: 57.8%
- İstek duration: ~122–124 s, execution ~2–4 s
- Prompt throughput: 97.6 tokens/s
- Generation throughput: 321.3 tokens/s
- GPU KV cache kullanımı: 0% (yükleme sırasında)
Sunucu normal şekilde çalışıyor ve chat endpointleri aktif. Deploy edilen app
modal app stop kumru-vllm-inference
ile durdurulabilir. Bu mimari ile model, talep olmadığı anlarda uyku moduna geçerek (scale-to-zero) maliyet avantajı sağlarken, gelen isteklere milisaniyeler içinde yanıt verebilecek bir hazır bulunuşluk seviyesine getirilmiştir.
Bölüm 5: Benchmarking ve Evaluation
Projenin son aşamasında, canlıya alınan modelin üretim ortamındaki davranışları nicel verilerle analiz edilmiştir. Bu analizde, hız ve doğruluk olarak iki kritik başarı faktörü ele alınmıştır:
Değerlendirme sürecinde, endüstri standardı kabul edilen “Oracle RAG” (Gold Context) metodolojisi izlenmiştir. Böylece modelin retrieval hatalarından bağımsız olarak, sadece kendisine sunulan doğru bilgiyi işleme ve reasoning kapasitesi ölçülmüştür. Testler, FP16 (Orijinal Ağırlıklar) ve AWQ (4-bit quantized) modelleri üzerinde, eş zamanlı 50 kullanıcı yükü altında gerçekleştirilmiştir.
Bölüm 5.1: Hesaplama Performansı: Throughput ve Latency Analizi
Sunucu tarafında yapılan stres testlerinde, vLLM engine’inin farklı model formatlarındaki kuyruk yönetimi ve token üretim hızları ölçülmüştür.

Veriler incelendiğinde, FP16 modelinin yüksek VRAM kullanımı nedeniyle istekleri sıraya aldığı (queuing) ve P99 gecikmesinin 100 saniyeye ulaştığı görülmüştür. Buna karşın AWQ modeli, bellek bant genişliği verimliliği sayesinde darboğazı aşmış ve saniyede 850 token üretim hızına ulaşarak hesaplama performansı açısından üstünlük sağlamıştır.
Bölüm 5.2: Oracle RAG
Hız artışının modelin zekası üzerindeki etkisini ölçmek amacıyla, modelin dışarıdan verilen doğru context’i kullanarak soruyu cevaplama yeteneği RAG başarısını ölçmek amacıyla test edilmiştir. Bu testte, “LLM-as-a-Judge” yaklaşımı kullanılmış ve Google Gemini 2.5 Flash modeli, üretilen cevapları doğruluk ve tutarlılık açısından 1 ile 5 arasında puanlamıştır. Structured output desteğiyle hazırlanan değerlendirme fonksiyonu aşağıda yer almaktadır.
def get_judge_evaluation(question, reference, student_answer):
"""Gemini Structured Output kullanarak değerlendirme yapar."""
judge_prompt = f"""
Aşağıdaki soruya verilen cevabı, referans cevaba (doğru bilgiye) göre değerlendir.
SORU: {question}
REFERANS CEVAP: {reference}
ÖĞRENCİ MODELİN CEVABI: {student_answer}
GÖREV:
Öğrenci modelin cevabını objektif olarak değerlendir.
- Puanlama (1-5): 5 (Tam uyumlu), 3 (Kısmen doğru/eksik), 1 (Yanlış/Alakasız).
- Halüsinasyon olup olmadığına dikkat et.
"""
response = judge_client.models.generate_content(
model="gemini-2.5-flash-lite",
contents=judge_prompt,
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=JudgeResult,
temperature=0.1
)
)
return response.parsed

Test sonuçları, 2 milyar parametreli küçük modellerde (SLM) 4-bit sıkıştırmanın, modelin dilbilgisel yapısını ve muhakeme yeteneğini belli bir oranda bozduğunu göstermiştir. AWQ modeli, yüksek hıza rağmen, verilen bağlamı anlamlı bir cevaba dönüştürmekte başarısız sayılabilir ki bu beklenen bir şey aynı zamanda. Kumru-2B-EPDK-Instruct, yüksek hassasiyet gerektiren enerji hukuku alanında nasıl bir performans sergilediği uçtan uca analiz edilmiştir. Veri mühendisliğinden model sıkıştırmaya kadar uzanan bu süreç, özellikle Production ortamı kararlarında veriye dayalı yaklaşımın önemini ortaya koymaktadır.
Sonuç ve Sonraki Adımlar
Bu çalışmanın kapsamı, sadece modelin eğitimi ve optimizasyonu ile sınırlı kalmamıştır. Geliştirilen model, mevzuat dokümanları üzerinde işleyen Advanced RAG hattına entegre edilerek gerçek dünya senaryolarındaki doküman-sorgu performansı doğrulanmıştır. RAG tarafında reranking ve indexing stratejileri de kullanılmıştır. Vektör veri tabanı olarak Milvus’un cloud versiyonu ziliz.cloud kullanılmış olup embedding modeli gemini-embedding-001kullanılmıştır.
Buna ek olarak, canlı sistemin güvenliğini ve mevzuat uyumluluğunu garanti altına almak adına mimariye NVIDIA NeMo Guardrails katmanı dahil edilmiştir. Bu katman ile modelin uygunluk denetimi, içerik filtrelemesi ve domain-boundary kontrol altına alınmıştır. User prompt’u ve sistem çıktıları Comet Opik ile trace edilmiştir.
Makalenin teknik odağını ve akıcılığını korumak amacıyla detayları ayrı bir teknik incelemenin konusu olabilecek bu entegrasyonlar; projenin veri hazırlığından güvenli dağıtıma uzanan uçtan uca (end-to-end) ve profesyonel niteliğini tamamlayan final yapıtaşları olarak kurgulanmıştır. Ayrıca çalışmanın teknik mimarisi ve uygulama süreçleri, LLM Engineer’s Handbook: Master the Art of Engineering Large Language Models kitabında sunulan mühendislik prensipleri temel alınarak yapılandırılmıştır. Kitabın incelenmesi sürecinde oluşturulan teknik notlar ve metodolojiler, bu projede uçtan uca bir LLM lifecycle olarak pratiğe dökülmüştür.
Geliştirilen pipeline’ın endüstriyel standartlara uyumu ve sürdürülebilirliği için data versioning mekanizmalarının entegre edilmesiyle zaman içindeki data drift’in tespit edilmesi ve veri hattı tutarlılığının korunması ele alınabilir. Ayrıca, veri işleme, eğitim ve dağıtım süreçlerinin izlenebilirliği ve tekrarlanabilirliği için ZenML gibi MLOps orkestrasyon araçlarının yapıya dahil edilmesi sistemin olgunluğunu artıracaktır. Performans açısından, mikroservis iletişiminde HTTP yerine daha düşük gecikme sunan gRPC’ye geçiş yapılması ve sık tekrarlanan mevzuat sorguları için Semantic Caching veya GPTCache kullanılarak GPU yükü ile yanıt sürelerinin azaltılması ölçeklenebilirlik için önerilen başlıca iyileştirmelerdir.
메타데이터
- post_id
- dade5de0f55e
- slug
- uçtan-uca-alana-özgü-kumru-base-modeli-eğitimi-veri-hazırlığından-üretim-ortamına-optimizasyon-dade5de0f55e
- url
- https://medium.com/@ogulcanakca/u%C3%A7tan-uca-alana-%C3%B6zg%C3%BC-kumru-base-modeli-e%C4%9Fitimi-veri-haz%C4%B1rl%C4%B1%C4%9F%C4%B1ndan-%C3%BCretim-ortam%C4%B1na-optimizasyon-dade5de0f55e
- canonical_url
- https://medium.com/@ogulcanakca/u%C3%A7tan-uca-alana-%C3%B6zg%C3%BC-kumru-base-modeli-e%C4%9Fitimi-veri-haz%C4%B1rl%C4%B1%C4%9F%C4%B1ndan-%C3%BCretim-ortam%C4%B1na-optimizasyon-dade5de0f55e
- author_url
- https://medium.com/@ogulcanakca
- status
- ok
- fetched_at
- 2026-07-14 23:44:08