00 Neden Evaluation Zor?
Dil modellerini değerlendirmek, deterministik yazılım testinden farklıdır; "doğru cevap" çoğu zaman tek değildir ve bağlama göre değişir.
"Daha iyi model" ifadesi bağlam olmaksızın anlamsızdır. Kod üretiminde üstün olan bir model, uzun belge özetlemede zayıf kalabilir. Çeviri kalitesini yüksek gösteren BLEU skoru, yüksek anlam tutarlılığını garanti etmez. Bu nedenle modern LLM değerlendirme pratiği, tek bir sayıya indirgenemez; farklı bakış açılarını kapsamaya çalışan bir metrik bütününü gerektirir.
Temel Zorluklar
Evaluation Yaklaşımları
01 Automated Metrics — BLEU, ROUGE, BERTScore, Exact Match, F1 02 Benchmark Harness — MMLU, HumanEval, HellaSwag, ARC, TruthfulQA 03 LLM-as-Judge — GPT-4 veya başka güçlü model ile puanlama 04 Human Evaluation — Gerçek kullanıcılar veya anotasyon ekibi 05 Domain-Specific — Belirli görev için özel test seti
Evaluation stratejisi, üretim hedefine göre belirlenir. Müşteri desteği botu için faithfulness ve hallüsinasyon tespiti öncelikli iken, kod asistanı için pass@k ve unit test doğruluğu öne çıkar. Önce neyi ölçeceğinize, sonra nasıl ölçeceğinize karar verin.
01 Otomatik Metrikler
Otomatik metrikler referans çıktıyla model çıktısı arasındaki benzerliği sayısal olarak ölçer; hızlı ve ucuzdur ancak anlam düzeyindeki kaliteyi tam yansıtmayabilir.
BLEU — Çeviri Metriği
BLEU (Bilingual Evaluation Understudy), model çıktısındaki n-gram'ların referans çıktıda bulunma oranını ölçer. Precision tabanlıdır: model çıktısındaki her n-gram'ın referansta geçip geçmediğini kontrol eder. 1-gram (unigram) ile 4-gram (4-gram) arasındaki precision skorlarını geometrik ortalamayla birleştirir, ardından çok kısa çıktıları cezalandırmak için brevity penalty uygular. Makine çevirisi için standart metrik olmakla birlikte, anlamsal olarak eşdeğer ama kelime düzeyinde farklı çeviriler için düşük skor üretmesi temel zayıflığıdır.
ROUGE — Özetleme Metriği
ROUGE (Recall-Oriented Understudy for Gisting Evaluation) recall tabanlıdır: referanstaki n-gram'ların model çıktısında bulunma oranını ölçer. ROUGE-1 (unigram), ROUGE-2 (bigram) ve ROUGE-L (en uzun ortak altdizi) olarak üç sürümü yaygın olarak kullanılır. Özetleme görevlerinde referansın önemli kısımlarını modelin kapsayıp kapsamadığını ölçmek için tercih edilir.
BERTScore — Semantik Benzerlik
BERTScore, BLEU ve ROUGE'un kelime eşleştirme yetersizliğini gidermek amacıyla BERT embedding'lerini kullanır. Model çıktısındaki her token'ı referansın en benzer token'ıyla cosine similarity ile eşleştirir. Anlamsal olarak benzer cümleler yüksek BERTScore alır.
import evaluate
# ─── BLEU ──────────────────────────────────────────────────
bleu = evaluate.load("bleu")
predictions = ["the cat is on the mat"]
references = [["the cat sat on the mat"]] # iç liste: birden fazla referans mümkün
bleu_result = bleu.compute(predictions=predictions, references=references)
print(bleu_result)
# {'bleu': 0.6194, 'precisions': [0.833, 0.6, 0.5, 0.4], 'brevity_penalty': 1.0, ...}
# ─── ROUGE ─────────────────────────────────────────────────
rouge = evaluate.load("rouge")
summaries = [
"The researchers developed a new method for protein folding using deep learning.",
]
reference_summaries = [
"Scientists created a deep learning approach to predict protein structure.",
]
rouge_result = rouge.compute(predictions=summaries, references=reference_summaries)
print(rouge_result)
# {'rouge1': 0.421, 'rouge2': 0.133, 'rougeL': 0.368, 'rougeLsum': 0.368}
# ─── BERTScore ─────────────────────────────────────────────
bertscore = evaluate.load("bertscore")
bert_result = bertscore.compute(
predictions=["The cat is sitting on the mat."],
references=["A cat rests on the mat."],
lang="en",
model_type="distilbert-base-uncased",
)
print(f"Precision: {bert_result['precision'][0]:.4f}")
print(f"Recall : {bert_result['recall'][0]:.4f}")
print(f"F1 : {bert_result['f1'][0]:.4f}")
# Precision: 0.9374 Recall: 0.9291 F1: 0.9332
# ─── Exact Match + F1 (QA için) ────────────────────────────
squad = evaluate.load("squad")
qa_predictions = [{"id": "1", "prediction_text": "Paris"}]
qa_references = [{"id": "1", "answers": {"text": ["Paris"], "answer_start": [4]}}]
squad_result = squad.compute(predictions=qa_predictions, references=qa_references)
print(squad_result)
# {'exact_match': 100.0, 'f1': 100.0}
Metriklerin Zayıflıkları
02 LLM-as-Judge
LLM-as-Judge yaklaşımında, insan etiketçi yerine güçlü bir dil modeli (genellikle GPT-4 veya Claude) değerlendirilen modelin çıktısını puanlar.
Birçok senaryoda referans yanıt üretmek imkânsızdır veya son derece pahalıdır. LLM-as-judge yöntemi bu sorunu şöyle çözer: güçlü bir modele hem soru hem de değerlendirilen modelin cevabı verilir; model belirli bir rubrik doğrultusunda 1-5 arası puan verir ve gerekçe açıklar. Bu yöntem, MT-Bench ve Chatbot Arena gibi geniş çaplı değerlendirme projelerinde de kullanılmaktadır.
Position Bias Sorunu
İki yanıtı karşılaştıran judge prompt'larında position bias önemli bir sorundur: yargı modeli her zaman ilk veya son verilen cevabı tercih etme eğiliminde olabilir. Bunu gidermek için aynı çifti hem A-B hem B-A sırasıyla değerlendirip sonuçları ortalamalısınız.
from openai import OpenAI
import json, re
client = OpenAI()
JUDGE_SYSTEM = """Sen bir dil modeli çıktısı değerlendirme uzmanısın.
Sana bir soru ve bir model yanıtı verilecek.
Aşağıdaki kriterlere göre 1-5 arası puan ver ve kısa gerekçe yaz.
Kriterler:
- Doğruluk (1-5): Yanıt gerçekle örtüşüyor mu?
- Tamlık (1-5): Soruyu tam olarak yanıtladı mı?
- Netlik (1-5): Anlaşılır ve iyi organize mi?
Yanıtını her zaman JSON formatında döndür:
{
"accuracy": <1-5>,
"completeness": <1-5>,
"clarity": <1-5>,
"overall": <1-5>,
"reasoning": ""
}"""
def judge_response(question: str, answer: str) -> dict:
"""Tek bir yanıtı GPT-4 ile değerlendir."""
user_msg = f"""Soru: {question}
Model Yanıtı:
{answer}
Lütfen yukarıdaki yanıtı değerlendir ve JSON formatında puan ver."""
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": JUDGE_SYSTEM},
{"role": "user", "content": user_msg},
],
temperature=0.0, # Determinizm için sıfır sıcaklık
response_format={"type": "json_object"},
)
return json.loads(response.choices[0].message.content)
def pairwise_judge(question: str, answer_a: str, answer_b: str) -> str:
"""İki yanıtı karşılaştır — position bias için çift yönlü değerlendir."""
def compare(first, second):
msg = f"""Soru: {question}
Yanıt A:
{first}
Yanıt B:
{second}
Hangi yanıt daha iyi? Sadece "A", "B" veya "tie" döndür."""
r = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": msg}],
temperature=0.0,
)
return r.choices[0].message.content.strip()
# A-B ve B-A sıralamasıyla iki kez değerlendir (position bias azalt)
vote_1 = compare(answer_a, answer_b) # A önce
vote_2 = compare(answer_b, answer_a) # B önce
# Tutarsızsa tie say
if vote_1 == "A" and vote_2 == "B":
return "A wins"
elif vote_1 == "B" and vote_2 == "A":
return "B wins"
else:
return "tie"
# Örnek kullanım
q = "Python'da liste ve tuple arasındaki fark nedir?"
a = "Liste değiştirilebilir, tuple değiştirilemez."
result = judge_response(q, a)
print(json.dumps(result, ensure_ascii=False, indent=2))
# {"accuracy": 4, "completeness": 3, "clarity": 5, "overall": 4, "reasoning": "..."}
Self-evaluation (bir modelin kendi çıktısını değerlendirmesi) ciddi önyargı içerir. Değerlendirici model, değerlendirilen modelden farklı ve genellikle daha güçlü olmalıdır. Ayrıca judge model sürümünü sabitleyin; model güncellemeleri puanlama davranışını değiştirebilir.
03 MMLU Benchmark
MMLU (Massive Multitask Language Understanding), 57 farklı konu alanında 14.000'i aşkın çoktan seçmeli soruyla modelin genel bilgi ve muhakeme kapasitesini ölçer.
2020 yılında Hendrycks ve arkadaşları tarafından yayımlanan MMLU, lise ve üniversite düzeyi bilgi sorularını kapsar. STEM (matematik, fizik, bilgisayar bilimi, tıp), beşeri bilimler (tarih, hukuk, felsefe), sosyal bilimler (ekonomi, psikoloji) ve diğer kategorileri içerir. Her soru dört seçenekli çoktan seçmeli formattadır ve tek doğru cevabı vardır. Standart değerlendirme protokolü 5-shot'tır: modele sorudan önce 5 örnek verilir.
Değerlendirme Protokolü
01 5 örnek soru + cevap → prompt'a ekle (few-shot context) 02 Değerlendirme sorusu → prompt'a ekle (cevapsız) 03 Modelden A/B/C/D token'larından birini üretmesini iste 04 log-probability — en yüksek olasılıklı harf seçimi 05 Doğru cevapla karşılaştır, accuracy hesapla
import torch
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForCausalLM
from tqdm import tqdm
CHOICES = ["A", "B", "C", "D"]
def format_mmlu_prompt(sample, few_shots: list) -> str:
"""5-shot MMLU prompt'u oluştur."""
prompt = ""
for ex in few_shots:
prompt += f"Question: {ex['question']}\n"
for i, choice in enumerate(ex["choices"]):
prompt += f"{CHOICES[i]}. {choice}\n"
answer_letter = CHOICES[ex["answer"]]
prompt += f"Answer: {answer_letter}\n\n"
prompt += f"Question: {sample['question']}\n"
for i, choice in enumerate(sample["choices"]):
prompt += f"{CHOICES[i]}. {choice}\n"
prompt += "Answer:"
return prompt
def eval_mmlu_subset(model_id: str, subject: str = "high_school_mathematics"):
# Model ve tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id, torch_dtype=torch.bfloat16, device_map="auto"
)
model.eval()
# MMLU dataset yükle
ds = load_dataset("cais/mmlu", subject)
test_ds = ds["test"]
few_shot_pool = list(ds["dev"]) # 5 örnek buradan alınır
# A/B/C/D token ID'lerini bul
choice_ids = [tokenizer.encode(c, add_special_tokens=False)[0] for c in CHOICES]
correct = 0
for sample in tqdm(test_ds):
prompt = format_mmlu_prompt(sample, few_shot_pool[:5])
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits[0, -1, :] # son token logit'leri
# Sadece A/B/C/D logit'lerini karşılaştır
choice_logits = logits[choice_ids]
predicted_idx = choice_logits.argmax().item()
if predicted_idx == sample["answer"]:
correct += 1
accuracy = correct / len(test_ds)
print(f"[{subject}] Accuracy: {accuracy:.4f} ({correct}/{len(test_ds)})")
return accuracy
# Örnek: Llama 3.2 1B ile matematik alt kümesini değerlendir
eval_mmlu_subset("meta-llama/Llama-3.2-1B-Instruct", "high_school_mathematics")
MMLU'nun 57 konusunun tamamını değerlendirmek küçük modeller için bile saatler sürebilir. Hızlı karşılaştırma için high_school_mathematics, college_computer_science ve abstract_algebra gibi birkaç temsili konu seçin. Tam değerlendirme için bir sonraki bölümde anlatılan lm-evaluation-harness kullanın.
04 HumanEval — Kod Üretimi
HumanEval, OpenAI tarafından yayımlanan 164 Python programlama problemini içerir ve modelin fonksiyonel doğruluğunu unit test çalıştırarak ölçer.
HumanEval, geleneksel metin eşleştirme metriklerinin kod değerlendirmede yetersiz kalması üzerine geliştirilmiştir. Modele bir fonksiyon imzası ve docstring verilir; modelin gövdeyi tamamlaması beklenir. Değerlendirme, sağlanan unit test'lerle gerçek çalıştırma yapılarak yapılır: ya çalışır ya da çalışmaz.
pass@k Metriği
pass@k, modelden k farklı çözüm üretildiğinde en az birinin testleri geçip geçmediğini ölçer. pass@1, tek denemede başarı oranıdır; pass@10 ve pass@100 ise modelin daha fazla deneme ile başarıya ulaşma kapasitesini gösterir. Doğrudan k çözüm üretip test etmek yerine, n ≥ k çözüm üretip unbiased estimator formülüyle hesaplamak istatistiksel açıdan daha güvenilirdir.
import subprocess, textwrap, tempfile, os
from datasets import load_dataset
from transformers import pipeline
import numpy as np
def run_code_safely(code: str, test: str, timeout: int = 5) -> bool:
"""Kodu geçici dosyada çalıştır, test geçerse True döndür."""
full_code = code + "\n\n" + test
with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as f:
f.write(full_code)
fname = f.name
try:
result = subprocess.run(
["python", fname],
capture_output=True,
timeout=timeout,
)
return result.returncode == 0
except subprocess.TimeoutExpired:
return False
finally:
os.unlink(fname)
def estimate_pass_at_k(n: int, c: int, k: int) -> float:
"""Unbiased pass@k estimator.
n: üretilen toplam çözüm sayısı
c: testleri geçen çözüm sayısı
k: hedef deneme sayısı
"""
if n - c < k:
return 1.0
return 1.0 - np.prod(1.0 - k / np.arange(n - c + 1, n + 1))
def eval_humaneval(model_id: str, n_samples: int = 10, k: int = 1):
# Dataset yükle
he_ds = load_dataset("openai/openai_humaneval", split="test")
gen = pipeline("text-generation", model=model_id, device_map="auto")
all_pass_at_k = []
for problem in he_ds.select(range(20)): # ilk 20 problem
prompt = problem["prompt"]
test = problem["test"]
entry = problem["entry_point"]
# n adet çözüm üret
outputs = gen(
prompt,
max_new_tokens=256,
num_return_sequences=n_samples,
do_sample=True,
temperature=0.8,
pad_token_id=gen.tokenizer.eos_token_id,
)
n_correct = 0
for out in outputs:
code = out["generated_text"]
if run_code_safely(code, test):
n_correct += 1
pk = estimate_pass_at_k(n_samples, n_correct, k)
all_pass_at_k.append(pk)
mean_pass = np.mean(all_pass_at_k)
print(f"pass@{k}: {mean_pass:.4f}")
return mean_pass
Model tarafından üretilen kodu çalıştırmak güvenlik riski içerir. Üretim ortamında mutlaka sandbox (Docker, gVisor, subprocess timeout) kullanın. Yukarıdaki kod yalnızca güvenilir ortamlarda araştırma amaçlı çalıştırılmalıdır.
05 RAG Faithfulness
RAG sistemlerinde hallüsinasyon tespiti, modelin verilen context'e dayanarak mı yoksa uydurarak mı yanıt ürettiğini ölçen faithfulness metriğiyle yapılır.
RAG pipeline'ının temel vaadi şudur: modele context verilirse, context'teki bilgilerden yanıt üretir, bilmediği şeyleri uydurmaz. Faithfulness metriği bu vaadi test eder: model çıktısındaki her önerme, verilen context tarafından desteklenebiliyor mu? Desteklenemeyen önermeler hallüsinasyon olarak işaretlenir.
RAGAS Framework
RAGAS (Retrieval Augmented Generation Assessment), RAG pipeline'ının bileşenlerini ayrı ayrı değerlendiren dört ana metrik sunar.
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_precision,
context_recall,
)
from datasets import Dataset
# Değerlendirme için veri hazırla
# Her örnek: soru, model yanıtı, alınan context'ler, referans yanıt
eval_data = {
"question": [
"Transformer modelinin attention mekanizması nasıl çalışır?",
"LoRA neden bellek verimlidir?",
],
"answer": [
"Attention mekanizması Query, Key ve Value matrislerini kullanarak her token'ın diğer token'larla ilişkisini hesaplar. Softmax ile normalize edilmiş ağırlıklar Value matrisine uygulanır.",
"LoRA, büyük ağırlık matrislerini dondurup küçük düşük-rankli adaptör matrisleri eğitir. Bu sayede eğitilen parametre sayısı orijinalin %1'inden azına düşer.",
],
"contexts": [
[
"The attention function maps a query and a set of key-value pairs to an output. The output is computed as a weighted sum of the values, where the weight assigned to each value is computed by a compatibility function of the query with the corresponding key.",
"Scaled dot-product attention computes Q·K^T / sqrt(d_k) then applies softmax before multiplying by V.",
],
[
"LoRA (Low-Rank Adaptation) freezes the pretrained model weights and injects trainable rank decomposition matrices into each layer of the Transformer architecture.",
"LoRA reduces the number of trainable parameters by 10,000 times and the GPU memory requirement by 3 times compared to full fine-tuning.",
],
],
"ground_truth": [
"Attention mekanizması Q, K, V matrisleriyle scaled dot-product hesaplaması yapar ve softmax ile normalize eder.",
"LoRA büyük matrisleri dondurup küçük rank ayrışımı matrisleri eğiterek parametre sayısını dramatik biçimde azaltır.",
],
}
dataset = Dataset.from_dict(eval_data)
# RAGAS değerlendirmesi çalıştır
# (LLM-as-judge kullandığından OPENAI_API_KEY gerekir)
result = evaluate(
dataset,
metrics=[faithfulness, answer_relevancy, context_precision, context_recall],
)
print(result.to_pandas()[
["faithfulness", "answer_relevancy", "context_precision", "context_recall"]
])
# faithfulness answer_relevancy context_precision context_recall
# 1.00 0.934 1.00 0.91
# 1.00 0.921 0.95 0.89
Manuel Faithfulness Kontrolü
from openai import OpenAI
import json
client = OpenAI()
def check_faithfulness(answer: str, context: str) -> dict:
"""Her önermenin context tarafından desteklenip desteklenmediğini kontrol et."""
prompt = f"""Aşağıdaki Context ve Answer verilmiştir.
Context:
{context}
Answer:
{answer}
Görev:
1. Answer'daki her önermeyi listele.
2. Her önermenin Context tarafından desteklenip desteklenmediğini belirt.
3. Faithfulness skoru = desteklenen önerme sayısı / toplam önerme sayısı
JSON formatında döndür:
claims
],
"faithfulness_score": 0.0-1.0
}}"""
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"},
temperature=0.0,
)
return json.loads(resp.choices[0].message.content)
06 lm-evaluation-harness
EleutherAI'ın lm-evaluation-harness framework'ü, 200'den fazla benchmark'ı standart bir arayüzle çalıştırmayı sağlar ve açık kaynak model karşılaştırmalarının fiili standardı haline gelmiştir.
Hugging Face Open LLM Leaderboard dahil pek çok resmi karşılaştırma tablosu, arka planda lm-evaluation-harness kullanmaktadır. Tek bir CLI komutuyla MMLU, HellaSwag, ARC, TruthfulQA, WinoGrande ve daha fazlasını aynı anda çalıştırabilirsiniz. Çıktı JSON formatında alınır ve kolayca işlenir.
Kurulum ve Temel Kullanım
# Kurulum
pip install lm-eval
# Hugging Face modeli ile MMLU çalıştır
lm_eval \
--model hf \
--model_args pretrained=meta-llama/Llama-3.2-1B-Instruct,dtype=bfloat16 \
--tasks mmlu \
--num_fewshot 5 \
--batch_size 8 \
--output_path ./results/llama32-1b-mmlu.json
# Birden fazla task aynı anda
lm_eval \
--model hf \
--model_args pretrained=mistralai/Mistral-7B-v0.3,dtype=bfloat16 \
--tasks mmlu,hellaswag,arc_easy,arc_challenge,winogrande \
--num_fewshot 5 \
--batch_size auto \
--output_path ./results/mistral7b-full.json
# Mevcut task listesini göster
lm_eval --tasks list
Sonuçları Okuma ve Analiz
import json
from pathlib import Path
import pandas as pd
def parse_lm_eval_results(result_path: str) -> pd.DataFrame:
"""lm-evaluation-harness JSON çıktısını DataFrame'e dönüştür."""
with open(result_path) as f:
data = json.load(f)
rows = []
for task_name, task_result in data["results"].items():
row = {"task": task_name}
for metric, value in task_result.items():
if not metric.endswith("_stderr") and isinstance(value, float):
row[metric] = round(value * 100, 2) # 0-100 scale
rows.append(row)
df = pd.DataFrame(rows).set_index("task")
return df
# Ön işlem yapılmış iki modeli karşılaştır
df_llama = parse_lm_eval_results("./results/llama32-1b-mmlu.json")
df_mistral = parse_lm_eval_results("./results/mistral7b-full.json")
comparison = pd.concat(
[df_llama.rename(columns=lambda c: f"llama32_1b_{c}"),
df_mistral.rename(columns=lambda c: f"mistral7b_{c}")],
axis=1,
)
print(comparison.to_string())
# MMLU kategorileri ortalaması
mmlu_tasks = [t for t in df_mistral.index if t.startswith("mmlu_")]
mmlu_avg = df_mistral.loc[mmlu_tasks, "acc"].mean()
print(f"MMLU Average: {mmlu_avg:.2f}")
Desteklenen Önemli Benchmark'lar
| Benchmark | Konu | Metrik | Few-shot |
|---|---|---|---|
| mmlu | Genel bilgi (57 konu) | accuracy | 5 |
| hellaswag | Commonsense reasoning | acc_norm | 10 |
| arc_easy / arc_challenge | Bilim soruları | acc_norm | 25 |
| winogrande | Commonsense (zamir) | accuracy | 5 |
| truthfulqa_mc1 | Doğruluk / yanıltmama | accuracy | 0 |
| gsm8k | İlkokul matematiği | exact_match | 8 |
| humaneval | Python kod üretimi | pass@1 | 0 |
07 deepeval ile Custom Evals
deepeval, özel değerlendirme metrikleri ve test suite'leri oluşturmak için tasarlanmış bir framework'tür; CI/CD pipeline'ına entegre edilerek her model değişikliğinde otomatik değerlendirme çalıştırılabilir.
Generik benchmark'lar her uygulamanın ihtiyacını karşılamaz. Müşteri desteği botu için müşteri hizmetleri tonuyla ilgili özel kriter, hukuki asistan için yasal terminoloji doğruluğu önemlidir. deepeval bu tür alan-özgü değerlendirmeleri standart bir test framework'ü syntax'ıyla yazmanızı sağlar.
Temel Kavramlar
import deepeval
from deepeval import evaluate, assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import (
AnswerRelevancyMetric,
FaithfulnessMetric,
ContextualRecallMetric,
HallucinationMetric,
)
# ─── Built-in Metrikler ─────────────────────────────────────
relevancy_metric = AnswerRelevancyMetric(threshold=0.7)
faithfulness_metric = FaithfulnessMetric(threshold=0.8)
test_case = LLMTestCase(
input="Python'da decorator ne işe yarar?",
actual_output="Decorator'lar bir fonksiyonu veya sınıfı değiştirmek için kullanılır. @ sembolüyle uygulanır.",
expected_output="Decorator'lar fonksiyon veya sınıfların davranışını sarmalayarak değiştiren higher-order function'lardır.",
retrieval_context=[
"Python decorators are functions that modify the behavior of other functions or classes.",
"They are applied using the @ symbol before the function definition.",
],
)
assert_test(test_case, [relevancy_metric, faithfulness_metric])
# ─── Custom Metric ──────────────────────────────────────────
from deepeval.metrics import BaseMetric
from deepeval.test_case import LLMTestCase
from openai import OpenAI
import json
class TechnicalAccuracyMetric(BaseMetric):
"""Teknik doğruluk custom metriği."""
def __init__(self, threshold: float = 0.7):
self.threshold = threshold
self.client = OpenAI()
def measure(self, test_case: LLMTestCase) -> float:
prompt = f"""Aşağıdaki yanıtı teknik doğruluk açısından değerlendir (0.0-1.0).
Soru: {test_case.input}
Yanıt: {test_case.actual_output}
Beklenen: {test_case.expected_output}
Sadece sayı döndür."""
resp = self.client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0.0,
)
score_str = resp.choices[0].message.content.strip()
self.score = float(score_str)
self.success = self.score >= self.threshold
return self.score
def is_successful(self) -> bool:
return self.success
@property
def __name__(self):
return "TechnicalAccuracy"
# ─── Test Suite — Toplu Değerlendirme ───────────────────────
tech_metric = TechnicalAccuracyMetric(threshold=0.75)
test_cases = [
LLMTestCase(
input="Transformer'da self-attention nasıl hesaplanır?",
actual_output="Q, K, V matrislerinden scores = QK^T/sqrt(d_k) sonra softmax(scores)·V.",
expected_output="Scaled dot-product attention: softmax(QK^T / sqrt(d_k)) · V",
),
LLMTestCase(
input="GPU ve CPU arasındaki temel fark nedir?",
actual_output="GPU daha fazla kor içerir ve paralel hesaplama için optimize edilmiştir. CPU seri görevlerde daha güçlüdür.",
expected_output="GPU paralel işleme, CPU seri işleme için tasarlanmıştır.",
),
]
results = evaluate(test_cases, [tech_metric, relevancy_metric])
print(results)
CI/CD Entegrasyonu
name: LLM Evaluation
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
evaluate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: pip install deepeval openai
- name: Run evaluation suite
env:
OPENAI_API_KEY: $
run: python -m pytest tests/eval_suite.py -v
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: eval-results
path: eval_results/
CI/CD entegrasyonu için LLM-as-judge çağrıları maliyet oluşturur. Hızlı pipeline'lar için daha ucuz modeller (gpt-4o-mini, Llama 3.1 8B) kullanın. Kritik metrik değerlendirmesi için güçlü modeli yalnızca PR merge öncesi çalışacak şekilde kısıtlayın.