Hugging Face ile Uçtan Uca NLP Mühendisliği: Transformer'dan LLM Fine-Tuning'e
Self-Attention mekanizmasının doğuşundan DistilBERT ile duygu analizine, XLM-RoBERTa ile çok dilli varlık tanımadan Mistral 7B'yi QLoRA ile ince ayar yapıp Gradio & Hugging Face Spaces üzerinde canlıya almaya uzanan 7 bölümlük uygulamalı bir mühendislik rehberi.
Transformer Devriminden Production'a: Bir NLP Yolculuğu
Son birkaç yılda yapay zeka dünyasında yaşanan kırılmaya hep birlikte tanıklık ediyoruz. ChatGPT'nin hayatımıza girmesiyle başlayan süreç, Büyük Dil Modellerinin (LLM) ve Üretken Yapay Zekanın (Generative AI) sınırları nasıl zorlayabileceğini tüm dünyaya kanıtladı. Ancak bu devrimin arkasında tek bir mimari taşı duruyor: Transformer Mimarisi.
Bu rehber serisinde, Hugging Face ekosistemini kullanarak teorik temellerden pratik kodlamaya, çok dilli (multilingual) modellerden QLoRA ile LLM İnce Ayarına (Fine-Tuning) ve Gradio/Spaces ile canlıya alma (deployment) süreçlerine uzanan uçtan uca bir NLP mühendisliği yolculuğuna çıkıyoruz. Her bölüm; teorik arka planı, matematiksel formülasyonu ve doğrudan çalıştırılabilir kod örnekleriyle birlikte ele alınıyor.
| Alan | Model / Teknoloji | Açıklama |
|---|---|---|
| Metin Sınıflandırma | DistilBERT | Emotion veri seti üzerinde duygu analizi ve model değerlendirme. |
| Token Sınıflandırma | XLM-RoBERTa | Almanca üzerinde eğitilip TR, EN, FR, IT dillerine genellenen çok dilli NER. |
| Metin Üretimi | GPT-2, Mistral-7B, Phi-4 Mini | Greedy, Beam Search, Top-k ve Top-p (Nucleus) sampling stratejileri. |
| LLM Fine-Tuning | Mistral-7B, BitsAndBytes, LoRA | 4-bit NormalFloat (NF4) Quantization ve Adapter bazlı fine-tuning. |
| Deployment | Gradio, Hugging Face Spaces | İnteraktif web arayüzleri ve bulut üzerinde canlı sunum. |
Serinin Yol Haritası
- Bölüm 1 — Mimarinin DoğuşuRNN/LSTM'in darboğazından Self-Attention'a, Hugging Face pipeline API'sine.
- Bölüm 2 — Metin SınıflandırmaEmotion veri seti, Subword Tokenization ve DistilBERT fine-tuning.
- Bölüm 3 — Token SınıflandırmaXLM-RoBERTa, SentencePiece ve Zero-Shot Cross-Lingual NER.
- Bölüm 4 — Metin ÜretimiGreedy/Beam Search, Temperature, Top-k ve Top-p Sampling.
- Bölüm 5 — Büyük Dil ModelleriMistral 7B mimarisi, Sliding Window Attention, Chat Templates.
- Bölüm 6 — PEFT / QLoRAQuantization, LoRA matematiği ve SFTTrainer ile fine-tuning.
- Bölüm 7 — ProductionGradio, Hugging Face Spaces ve RAG vs Fine-Tuning karşılaştırması.
Rasittekin18/generative-ai-nlp-huggingfaceMimarinin Doğuşu ve Hugging Face Ekosistemi
1.1 — Geleneksel NLP'den Transformer Devrimine
2017 öncesinde Doğal Dil İşleme alanındaki baskın yaklaşım, tekrarlı sinir ağlarına (RNN, LSTM, GRU) dayanıyordu. Bu mimarilerin temel çalışma prensibi, metindeki kelimeleri zaman adımlarına (time steps) bölerek sıralı (sequential) biçimde işlemek üzerine kuruluydu.
"Yapay" (t=1) ──▶ "Zeka" (t=2) ──▶ "NLP'yi" (t=3) ──▶ "Dönüştürüyor" (t=4) ──▶ [Gizli Durum / Bottleneck]
Bu dizisel yapının beraberinde getirdiği iki kritik kısıt vardı:
- Paralelleştirilememe (Computational Bottleneck): Bir kelimenin işlenebilmesi için bir önceki kelimenin ağdan geçmesi gerekiyordu; bu da GPU'ların paralel hesaplama gücünün tam kullanılmasını engelliyordu.
- Darboğaz ve Uzun Mesafe Bağlam Kaybı: Seq2Seq modellerde girdinin tüm anlamı tek bir sabit boyutlu gizli duruma (hidden state) sıkıştırılıyor, metin uzadıkça ilk kelimelerin anlamı kayboluyordu.
1.2 — Self-Attention ve Transformer Mimarisi
2017 yılında Google araştırmacılarının yayımladığı "Attention Is All You Need" makalesi, RNN ve LSTM yapılarını tamamen devreden çıkararak yalnızca dikkat mekanizmasına dayanan Transformer mimarisini tanıttı. Bu mimari, metindeki tüm kelimeleri aynı anda (paralel) işleme yeteneği kazandırdı.
"Yapay" ────────┐ "Zeka" ────────┼──▶ Matris Çarpımları (Q, K, V) ──▶ Paralel Hesaplama "NLP'yi" ────────┤ "Dönüştürüyor" ────────┘
Bir kelimenin diğer kelimelerle olan anlamsal bağını hesaplamak için girdi vektörlerinden üç temel matris türetilir: Query (Q) arama yapan vektör, Key (K) dizinlenen vektör, Value (V) kelimenin taşıdığı gerçek anlamsal değer. Dikkat skoru matematiksel olarak şöyle hesaplanır:
Model, "Nehir kenarındaki bankta oturduk" cümlesindeki bank kelimesi ile "Bankadaki hesabıma para yattı" cümlesindeki bank kelimesinin aynı anlamda olmadığını, matris seviyesinde hesaplanan dikkat ağırlıkları (attention weights) sayesinde çözer.
Transfer Learning: Bilgisayarlı Görüşten NLP'ye
Pre-training (Ön Eğitim)
- Devasa etiketlenmemiş metin kümelerinde (Wikipedia, Common Crawl) yapılır
- Denetimsiz / yarı denetimli süreç
- Dilin grameri, kelime ilişkileri ve dünya bilgisi öğrenilir
Fine-Tuning (İnce Ayar)
- Modelin gövdesi (body) korunur, baş kısmı (head) değişir
- Küçük, etiketli, hedef göreve özel veri kullanılır
- Örnek: metin sınıflandırma, NER, soru-cevap
1.3 — Hugging Face Standartlaşması ve pipeline API
2018 sonrasında GPT (Decoder-only) ve BERT (Encoder-only) gibi modellerin farklı laboratuvarlarda PyTorch veya TensorFlow ile birbirinden bağımsız geliştirilmesi kod karmaşasına yol açmıştı. Hugging Face, geliştirdiği transformers kütüphanesiyle tüm bu mimarileri ortak bir standart altında topladı. En pratik soyutlama katmanı ise pipeline() API'sidir — model yükleme, tokenization ve çıkarım adımlarını birkaç satır koda indirger.
from transformers import pipeline
# 1. Duygu Analizi (Sentiment Analysis)
classifier = pipeline("sentiment-analysis")
print("1. Sentiment:", classifier("Hugging Face makes working with LLMs extremely easy!")[0])
# 2. Sıfır-Örnekli Sınıflandırma (Zero-Shot Classification)
zero_shot = pipeline("zero-shot-classification")
res_zs = zero_shot(
"This tutorial covers deep learning and natural language processing.",
candidate_labels=["education", "finance", "sports"]
)
print("2. Zero-Shot:", res_zs["labels"][0])
# 3. Metin Üretimi (Text Generation)
generator = pipeline("text-generation", model="distilgpt2")
print("3. Text Gen:", generator("In this project, we aim to", max_length=25)[0]["generated_text"])
# 4. İsimlendirilmiş Varlık Tanıma (NER)
ner = pipeline("ner", grouped_entities=True)
print("4. NER:", ner("My name is Rasit and I live in Istanbul, working at Google."))
# 5. Soru-Cevap (Question Answering)
qa = pipeline("question-answering")
print("5. QA:", qa(question="Where do I live?", context="My name is Rasit and I live in Istanbul."))
# 6. Metin Özetleme (Summarization)
summarizer = pipeline("summarization")
long_text = "Transformer models have revolutionized NLP by replacing RNNs with self-attention mechanisms..."
print("6. Summary:", summarizer(long_text, max_length=30, min_length=10)[0]["summary_text"])
# 7. Çeviri (Translation)
translator = pipeline("translation_en_to_de")
print("7. Translation (EN->DE):", translator("Learning NLP is very exciting.")[0]["translation_text"])
Hugging Face yalnızca metin odaklı modellerle sınırlı değildir. automatic-speech-recognition hattı üzerinden ses dosyalarını da metne dönüştürebilirsiniz:
from datasets import load_dataset, Audio
from transformers import pipeline
asr_pipe = pipeline("automatic-speech-recognition")
ds = load_dataset("speech_colab/mint14", name="en_us", split="train[:5]")
ds = ds.cast_column("audio", Audio(sampling_rate=asr_pipe.feature_extractor.sampling_rate))
sample_audio = ds[0]["audio"]["array"]
result = asr_pipe(sample_audio)
print("Transkript:", result["text"])
Tekrarlı ağların dizisel işlem kısıtları Self-Attention ile aşıldı; Pre-training + Fine-Tuning yaklaşımı NLP projelerinde daha az veri ve kaynakla yüksek başarım sağladı; Hugging Face pipeline API'si de karmaşık mimarileri tek satıra indirgeyerek hızlı prototiplemeyi mümkün kıldı. Sırada, kendi verimizle bir modeli ince ayar yapmak var.
Metin Sınıflandırma ve İnce Ayar (Sentiment Analysis & DistilBERT)
2.1 — Veri Analizi (EDA)
Çoğu duygu analizi veri seti yalnızca Pozitif/Negatif iki sınıftan oluşur; gerçek dünya senaryolarında ise duygular çok daha karmaşıktır. Bu bölümde Twitter (Ex) gönderilerinden oluşan ve altı temel duyguyu barındıran Emotion veri setini kullanacağız: sadness (0), joy (1), love (2), anger (3), fear (4), surprise (5).
from datasets import load_dataset
import pandas as pd
import matplotlib.pyplot as plt
# 1. Emotion veri setini Hugging Face Hub'dan yükleme
emotions = load_dataset("emotion")
print(emotions) # train: 16.000, validation: 2.000, test: 2.000
# 2. EDA için Dataset -> Pandas DataFrame
emotions.set_format(type="pandas")
df_train = emotions["train"][:]
def label_int2str(row):
return emotions["train"].features["label"].int2str(row["label"])
df_train["label_name"] = df_train["label"].apply(label_int2str)
# 3. Sınıf dağılımını görselleştirme
df_train["label_name"].value_counts(ascending=True).plot.barh()
plt.title("Emotion Veri Seti - Sınıf Dağılımı")
plt.show()
joy ve sadness sınıfları en yüksek örnek sayısına sahipken, surprise ve love çok daha az temsil edilir. Bu tür dengesiz veri setlerinde yalnızca Accuracy'ye güvenmek yerine F1-Score ve Confusion Matrix analizine bakmak gerekir.2.2 — Tokenization Derin Dalış
Derin öğrenme modelleri ham metinleri doğrudan işleyemez. Modern NLP'de üç temel tokenization yaklaşımı vardır:
- Character-based:
"Learning" → ['L','e','a','r','n','i','n','g']— sözlük küçük ama kalıp/anlam kaybı büyük. - Word-based:
"Learning NLP" → ['Learning','NLP']— çekimlenmiş kelimeler devasa sözlük boyutu (vocabulary) yaratır. - Subword-based (standart):
"Learning NLP" → ['learn','##ing','NL','##P']— sık kelimeler bütün kalır, nadir kelimeler parçalanır.
from transformers import AutoTokenizer
model_ckpt = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
text = "It is fascinating to learn NLP using Hugging Face."
encoded_text = tokenizer(text)
tokens = tokenizer.convert_ids_to_tokens(encoded_text["input_ids"])
print("Tokens:", tokens)
# ['[CLS]', 'it', 'is', 'fascinating', ..., '[SEP]']
def tokenize(batch):
return tokenizer(batch["text"], padding=True, truncation=True)
emotions_encoded = emotions.map(tokenize, batched=True, batch_size=None)
Çıktıda cümlenin başına [CLS], sonuna [SEP] özel token'ları eklenir; attention_mask ise modelin hangi token'ları dikkate alacağını belirtir. padding=True parametresi batch içindeki en uzun metne göre matris boyutlarını eşitler.
2.3 — PyTorch ve Trainer API ile Fine-Tuning
[Input IDs] ──▶ [ DistilBERT Base (Transformer Layers) ] ──▶ [Hidden State (768-dim)] ──▶ [ Linear Head (6 Outputs) ]
import torch
import numpy as np
import evaluate
from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainer
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
num_labels = 6
model = AutoModelForSequenceClassification.from_pretrained(
model_ckpt, num_labels=num_labels
).to(device)
accuracy_metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
predictions, labels = eval_pred
preds = np.argmax(predictions, axis=1)
return accuracy_metric.compute(predictions=preds, references=labels)
training_args = TrainingArguments(
output_dir="distilbert-emotion-classification",
num_train_epochs=2,
per_device_train_batch_size=64,
per_device_eval_batch_size=64,
learning_rate=2e-5,
weight_decay=0.01,
evaluation_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
report_to="none"
)
trainer = Trainer(
model=model,
args=training_args,
compute_metrics=compute_metrics,
train_dataset=emotions_encoded["train"],
eval_dataset=emotions_encoded["validation"],
tokenizer=tokenizer
)
trainer.train() # 2 epoch sonunda ~%93 doğruluk
2.4 — Confusion Matrix ile Hata Analizi
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
preds_output = trainer.predict(emotions_encoded["validation"])
y_preds = np.argmax(preds_output.predictions, axis=1)
y_true = emotions_encoded["validation"]["label"]
labels = emotions["train"].features["label"].names
cm = confusion_matrix(y_true, y_preds, normalize="true")
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=labels)
disp.plot(cmap="Blues", values_format=".2f", colorbar=False)
plt.show()
sadness ve joy sınıflarında model %95+ doğruluk gösterirken, örnek sayısı az olan surprise zaman zaman joy veya fear ile karışıyor. Eğitilen modeli trainer.push_to_hub() ile Hugging Face Hub'a yükleyip pipeline() ile anında çıkarım yapabiliriz.
Bir sonraki durak: tek bir etiketi tüm cümleye atadığımız metin sınıflandırmanın ötesine geçip, her token'ı ayrı ayrı etiketlediğimiz Token Sınıflandırma dünyası.
Token Sınıflandırma ve Çok Dilli Modeller (Multilingual NER & XLM-RoBERTa)
3.1 — Metin Sınıflandırmadan Token Sınıflandırmaya
Metin Sınıflandırma (Sentiment): "Raşit İstanbul'da harika bir proje geliştiriyor." ──▶ [ POSITIVE ] Token Sınıflandırma (NER): "Raşit" ──▶ B-PER (Kişi başlangıcı) "İstanbul'da" ──▶ B-LOC (Lokasyon başlangıcı) "harika" ──▶ O (Varlık değil) "bir" ──▶ O "proje" ──▶ O "geliştiriyor."──▶ O
NER analizi; arama motorlarında bilginin yapılandırılması, finansal raporlardan şirket isimlerinin çıkarılması ve sağlık sektöründe ilaç/hastalık isimlerinin tespiti gibi alanlarda kritik rol oynar.
3.2 — XLM-RoBERTa ve SentencePiece Tokenizer
Her dil için ayrı model eğitmek maliyetli ve sürdürülemezdir. Meta tarafından geliştirilen XLM-RoBERTa, 100 farklı dilde masked language modeling ile eğitilmiş çok dilli bir Transformer modelidir. Klasik BERT'in aksine, metni Unicode karakter dizisi olarak işleyen SentencePiece tokenizer'ı kullanır — bu sayede Japonca/Çince gibi boşluksuz dillerde bile dilden bağımsız parçalama yapabilir.
from transformers import AutoTokenizer
model_ckpt = "xlm-roberta-base"
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
text = "Teams Supplies San Diego"
tokens = tokenizer.convert_ids_to_tokens(tokenizer(text)["input_ids"])
print("Tokens:", tokens)
# ['<s>', '▁Team', 's', '▁Supp', 'lies', '▁San', '▁Diego', '</s>']
Önündeki ▁ (alt çizgi) sembolü kelimenin başlangıcını ifade eder; önünde bu sembol olmayan token'lar (s, lies) bir önceki token'ın devamı olan alt kelimelerdir (subwords).
Subword Alignment ve -100 Kuralı
Token sınıflandırmanın en kritik teknik detayı etiket hizalamadır. Orijinal veri setinde Supplies kelimesine tek bir etiket verilir; ancak tokenizer bunu Supp + lies olarak ikiye böler. İkinci parçanın etiketi ne olmalı?
Kelime: 2000 Wagner en
Token: _2000 _Wag ner _en
Etiket ID: 0 1 -100 0
│
└──▶ PyTorch tarafından ignore edilir (-100)
Kural şudur: kelimenin ilk alt parçasına orijinal etiket verilir; takip eden alt parçalara ve özel token'lara (<s>, </s>) -100 atanır. PyTorch'un CrossEntropyLoss fonksiyonu -100 etiketlerini ignore_index olarak otomatik göz ardı eder.
def tokenize_and_align_labels(examples):
tokenized_inputs = tokenizer(
examples["tokens"], truncation=True, is_split_into_words=True
)
labels = []
for i, label in enumerate(examples["ner_tags"]):
word_ids = tokenized_inputs.word_ids(batch_index=i)
previous_word_idx = None
label_ids = []
for word_idx in word_ids:
if word_idx is None:
label_ids.append(-100) # özel token'lara -100
elif word_idx != previous_word_idx:
label_ids.append(label[word_idx]) # ilk alt parça -> orijinal etiket
else:
label_ids.append(-100) # takip eden alt parçalar -> -100
previous_word_idx = word_idx
labels.append(label_ids)
tokenized_inputs["labels"] = labels
return tokenized_inputs
3.3 — Eğitim ve Diller Arası Sıfır Transfer
Modelimizi çok dilli PAN-X (XTREME) veri setinin yalnızca Almanca (de) alt kümesinde eğiteceğiz. Etiketlerimiz: PER, LOC, ORG. Token sınıflandırmada accuracy yanıltıcı olabileceğinden (çoğu token O etiketli) değerlendirme metriği olarak seqeval üzerinden F1-Score kullanılır.
import evaluate
from transformers import (
AutoModelForTokenClassification, TrainingArguments, Trainer, DataCollatorForTokenClassification
)
num_labels = 7 # B-PER, I-PER, B-LOC, I-LOC, B-ORG, I-ORG, O
model = AutoModelForTokenClassification.from_pretrained(model_ckpt, num_labels=num_labels)
data_collator = DataCollatorForTokenClassification(tokenizer)
seqeval = evaluate.load("seqeval")
label_list = ["O", "B-PER", "I-PER", "B-LOC", "I-LOC", "B-ORG", "I-ORG"]
def compute_metrics(p):
predictions, labels = p
predictions = np.argmax(predictions, axis=2)
true_predictions = [[label_list[p] for (p, l) in zip(pr, lb) if l != -100]
for pr, lb in zip(predictions, labels)]
true_labels = [[label_list[l] for (p, l) in zip(pr, lb) if l != -100]
for pr, lb in zip(predictions, labels)]
results = seqeval.compute(predictions=true_predictions, references=true_labels)
return {"f1": results["overall_f1"]}
training_args = TrainingArguments(
output_dir="xlm-roberta-ner-german",
num_train_epochs=3,
per_device_train_batch_size=24,
per_device_eval_batch_size=24,
evaluation_strategy="epoch",
learning_rate=2e-5,
weight_decay=0.01,
report_to="none"
)
trainer = Trainer(
model=model, args=training_args,
train_dataset=panx_de_encoded["train"], eval_dataset=panx_de_encoded["validation"],
tokenizer=tokenizer, data_collator=data_collator, compute_metrics=compute_metrics
)
trainer.train()
| Dil | Durum | F1-Score |
|---|---|---|
| Almanca | Eğitildiği dil | %86 |
| Fransızca | Sıfır transfer | %74 |
| İtalyanca | Sıfır transfer | %67 |
| İngilizce | Sıfır transfer | %58 |
| Türkçe | Sıfır transfer | ~%58 |
XLM-RoBERTa ön eğitim aşamasında diller arasındaki anlamsal ilişkileri ortak bir vektör uzayında (multilingual representation space) eşleştirdiği için; yalnızca Almanca ile ince ayar yapılan model, hiç Türkçe veri görmemiş olsa dahi Türkçe cümledeki bir kişi veya yer isminin ne olduğunu anlayabilir.
3.4 — Gradio ile Canlıya Alma
Subword parçalanmalarını birleştirmek için aggregation_strategy="simple" kullanılır — böylece Tren + dyol gibi parçalar kullanıcıya tek bir bütün isim (Trendyol) olarak gösterilir.
import gradio as gr
from transformers import pipeline
ner_pipeline = pipeline(
"token-classification", model=model, tokenizer=tokenizer,
aggregation_strategy="simple"
)
def ner_inference(text):
return {"text": text, "entities": ner_pipeline(text)}
demo = gr.Interface(
fn=ner_inference,
inputs=gr.Textbox(lines=2, placeholder="Metin giriniz..."),
outputs=gr.HighlightedText(label="Tespit Edilen Varlıklar"),
title="🌍 Multilingual NER App (XLM-RoBERTa)",
examples=[
["Benim adım Ali ve Trendyol şirketinde çalışıyorum."],
["Tim Cook lives in California and works at Google."],
["Angela Merkel wohnt in Berlin."]
]
)
demo.launch(share=True)
Hazırlanan app.py ve requirements.txt dosyaları Hugging Face Spaces alanına aktarılarak proje tüm dünyayla ücretsiz paylaşılabilir. Sırada, üretken yapay zekanın kalbi: metin üretimi.
Metin Üretim Stratejileri (Text Generation Decoding Strategies)
4.1 — Auto-Regressive Modellerin Çalışma Prensibi
Metin üreten modeller (GPT serisi, Mistral, Llama) Transformer'ın Decoder kısmını kullanır ve Auto-regressive (Öz-Yinelemeli) çalışırlar: model her adımda sözlüğündeki tüm kelimeler için bir koşullu olasılık dağılımı hesaplar ve seçilen stratejiye göre bir sonraki token tahmin edilip girdinin sonuna eklenir.
[Prompt: "In this tutorial, we will"] ──▶ [LLM Decoder] ──▶ Tahmin: "learn" [Girdi: "In this tutorial, we will learn"] ──▶ [LLM Decoder] ──▶ Tahmin: "how" [Girdi: "In this tutorial, we will learn how"] ──▶ ... (Max Token veya EOS)
4.2 — Arama Algoritmaları
Greedy Search, her adımda en yüksek olasılığa sahip tek token'ı açgözlü şekilde seçer:
from transformers import AutoTokenizer, AutoModelForCausalLM
tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2")
prompt = "I enjoy walking with my dog"
input_ids = tokenizer(prompt, return_tensors="pt").input_ids
greedy_output = model.generate(input_ids, max_new_tokens=40)
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))
Hızlı ve basittir; fakat anlık en yüksek olasılıklı kelimeyi seçtiği için uzun vadede daha iyi cümle yapılarını kaçırır ve kendini tekrar eden döngülere takılabilir.
Beam Search ise her adımda en yüksek olasılığa sahip N hipotezi (num_beams) hafızada tutar; toplam cümle olasılığı en yüksek diziyi seçer:
[The] ──┬──▶ [dog] (0.6) ──▶ [house] (0.6) = Toplam: 0.36 ◀── Seçilen En İyi Dizi
└──▶ [night] (0.5) ──▶ [woman] (0.4) = Toplam: 0.20
beam_output = model.generate(
input_ids,
max_new_tokens=40,
num_beams=5,
no_repeat_ngram_size=2, # aynı 2'li kelime grubunun tekrarını engeller
early_stopping=True
)
print(tokenizer.decode(beam_output[0], skip_special_tokens=True))
Beam Search, Çeviri ve Özetleme gibi rastgeleliğin az olması gereken deterministik görevlerde başarılıdır; ancak hikaye anlatımı gibi yaratıcı üretimde "sıkıcı" sonuçlar verir.
4.3 — Sampling ve Yaratıcı Metin Üretimi
Daha doğal, çeşitli metinler için olasılık dağılımından örnekleme (do_sample=True) kullanılır. Bunu kontrol eden üç temel parametre vardır:
- Temperature (T): Düşük T (< 1.0) dağılımı sivriltir, model daha kararlı olur; yüksek T (> 1.0) dağılımı düzleştirir, model daha "yaratıcı" hale gelir.
- Top-k Sampling: Bir sonraki kelime yalnızca en yüksek olasılıklı
ktoken arasından seçilir, geri kalanların olasılığı sıfırlanır. - Top-p (Nucleus) Sampling: Sabit sayı yerine kümülatif olasılık eşiği (
p, örn. 0.92) belirlenir; toplamı bu eşiğe ulaşan token'lar dinamik bir havuz oluşturur.
# Temperature + Top-k + Top-p birlikte (best practice)
final_output = model.generate(
input_ids,
max_new_tokens=50,
do_sample=True,
temperature=0.7, # dengeli rastgelelik
top_k=50, # en iyi 50 kelimeye odaklan
top_p=0.92, # kümülatif %92 olasılık havuzu
no_repeat_ngram_size=2,
num_return_sequences=3
)
for i, sample in enumerate(final_output):
print(f"\n--- Alternatif {i+1} ---")
print(tokenizer.decode(sample, skip_special_tokens=True))
4.4 — Hangi Durumda Hangi Yöntem?
| Senaryo | Tavsiye Edilen Strateji | Neden |
|---|---|---|
| Kod yazma, matematik, soru-cevap | Greedy Search / düşük Temperature | Doğruluk ve kararlılık öncelikli, rastgelelik istenmez. |
| Çeviri, özetleme | Beam Search + N-Gram Penalty | Kaynağa sadık kalınmalı, tekrarlar önlenmeli. |
| Hikaye anlatımı, yaratıcı yazarlık | Top-k + Top-p + orta-yüksek Temperature | Metin akıcı, çeşitli ve sıkıcı olmayan yapıda olmalı. |
| Sohbet robotları | Top-p + orta Temperature | İnsan benzeri, doğal ve esnek yanıtlar üretir. |
Bir sonraki adım: bu üretim stratejilerinin üzerine inşa edilen, milyarlarca parametreli Büyük Dil Modelleri.
Büyük Dil Modelleri (LLMs) & Mistral 7B Ekosistemi
5.1 — Açık Kaynak LLM Çağı ve Mistral 7B
GPT-3/GPT-4 gibi kapalı kaynaklı devasa modeller yüksek başarım sunsa da, kapalı yapıları ve kullanım maliyetleri özelleştirmeyi sınırlar. Meta'nın Llama serisi ve Mistral AI'ın modelleriyle açık kaynak LLM dünyasında yeni bir dönem başladı. Mistral 7B (7.3 milyar parametre), Apache 2.0 lisansıyla serbestçe erişilebilir ve kendi boyutundaki Llama 2 7B'yi tüm benchmark testlerinde geride bırakır.
| Model | MMLU Skoru |
|---|---|
| Mistral 7B | %60.1 |
| Llama 2 13B | %55.0 |
| Llama 2 7B | %44.0 |
Sliding Window Attention
Klasik Transformer'da her token, öncesindeki tüm token'larla dikkat hesabı yapar; dizi uzunluğu (N) arttıkça bellek gereksinimi karesel (O(N²)) büyür. Mistral 7B bu kısıtı aşmak için Sliding Window Attention (SWA) kullanır: her katmandaki token yalnızca sabit bir pencere boyutu (W) içindeki komşularına doğrudan erişir; katmanlar üst üste bindikçe alt katmanlardan aktarılan bilgi sayesinde model, W × katman sayısı kadar geriye giden bağlama ulaşabilir.
Katman 3: [ T1 ] ◀─── [ T2 ] ◀─── [ T3 ] ◀─── [ T4 ]
│ │ │ │
Katman 2: [ T1 ] ◀─── [ T2 ] ◀─── [ T3 ] ◀─── [ T4 ]
│ │ │ │
Katman 1: [ T1 ] [ T2 ] [ T3 ] [ T4 ]
Bu yöntem KV Cache bellek kullanımını dramatik şekilde düşürür ve daha küçük donanımlarda yüksek hızda çalışmayı mümkün kılar.
5.2 — LLM Model Türleri
- Pre-trained (Base) ModelTrilyonlarca kelimelik ham metinle eğitilmiş temel iskelet; sohbet etmek yerine cümleyi devam ettirmeye çalışır.
- Fine-Tuned ModelBelirli bir alan (tıp, hukuk, kodlama) verisiyle uzmanlaştırılmış hali.
- Instruction-Tuned (Instruct) ModelTalimat-yanıt formatındaki verilerle eğitilmiş, kullanıcı komutlarına doğrudan yanıt üretir.
- RLHF / Alignment Modelİnsan tercihlerine dayalı Ödül Modelleri ve DPO teknikleriyle hizalanmış, zararlı çıktı üretmesi engellenmiş sürüm.
5.3 — Chat Templates ile Sohbet Yönetimi
Sohbet robotlarında mesajlar belirli rollerle beslenir: system (modelin kimliği), user (kullanıcı girdisi), assistant (model yanıtı). Her mimari (Llama, Mistral, Falcon) sohbet geçmişini farklı özel token'larla birleştirir — bunu elle yazmak yerine apply_chat_template fonksiyonu kullanılır.
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.1")
messages = [
{"role": "user", "content": "Can you recommend 2 cities to visit in Turkey?"},
{"role": "assistant", "content": "1. Istanbul\n2. Cappadocia"},
{"role": "user", "content": "What about a coastal city?"}
]
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
print(prompt)
# <s>[INST] Can you recommend 2 cities... [/INST]1. Istanbul...</s>[INST] What about a coastal city? [/INST]
Hazırlanan formatı doğrudan AutoModelForCausalLM üzerinden çalıştırarak modeli test edebiliriz:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "mistralai/Mistral-7B-Instruct-v0.1"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype=torch.bfloat16, device_map="auto")
messages = [{"role": "user", "content": "Explain Overfitting to a 10-year-old in 2 sentences."}]
inputs = tokenizer.apply_chat_template(messages, return_tensors="pt").to("cuda")
outputs = model.generate(inputs, max_new_tokens=100, do_sample=True, temperature=0.7, top_p=0.95)
response = tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokens=True)
print("Mistral Yanıtı:\n", response)
Sırada, bu devasa modeli tüketici sınıfı bir GPU'da kendi verimizle nasıl ince ayar yapacağımız var: PEFT ve QLoRA.
Parameter-Efficient Fine-Tuning (PEFT / QLoRA)
6.1 — Yüksek Bellek Sorunu
7 milyar parametreli bir modeli tam hassasiyette (FP32) eğitmek; ağırlıklar, gradyanlar, optimizer durumları ve aktivasyonlar için 80-100 GB+ VRAM gerektirir. Bu problem iki teknolojinin birleşimiyle çözülür:
- Quantization: Model ağırlıklarını 32/16-bit'ten 4-bit hassasiyete indirgemek.
- LoRA (Low-Rank Adaptation): Orijinal ağırlıkları dondurup araya küçük, eğitilebilir matrisler eklemek.
Bu ikilinin birleşimi QLoRA (Quantized Low-Rank Adaptation) olarak adlandırılır.
6.2 — Quantization ve BitsAndBytes
Ağırlıklar 4-bit NormalFloat4 (NF4) formatına dönüştürülür — 15 GB yer kaplayan Mistral 7B, bellekte yalnızca ~4.5-5 GB yer tutar. Depolama 4-bit'te kalırken, matris çarpımları esnasında hesaplama dinamik olarak 16-bit (bfloat16) hassasiyetine çevrilerek performans kaybı engellenir.
import torch
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True # çift quantization ile ekstra bellek tasarrufu
)
6.3 — LoRA Mimarisi ve Matematiği
Geleneksel ince ayarda ağırlık matrisinin tamamı güncellenir. LoRA'da ise orijinal ağırlık matrisi tamamen dondurulur (frozen); güncellenecek ağırlık değişimi, rütbesi (rank) çok daha düşük iki küçük matrisin çarpımına ayrıştırılır:
[Girdi X]
│
┌─────────────┼────────────────────────────┐
│ │ │
│ [ Dondurulmuş Ağırlık Matrisi W₀ (4-bit) ] ──────────┐
│ │ │
│ [ Eğitilebilir Matris A (r × k) ] ├──▶ [Çıktı Y]
│ │ │
│ [ Eğitilebilir Matris B (d × r) ] ── (× α/r) ─────────┘
└──────────────────────────────────────────────────────────
Örneğin 4096×4096 (16.7 milyon parametreli) bir matriste r=8 seçildiğinde: A matrisi 4096×8 = 32.768, B matrisi 8×4096 = 32.768 parametre — toplam yalnızca 65.536 eğitilebilir parametre (%99.6 azalma).
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
model = prepare_model_for_kbit_training(model)
peft_config = LoraConfig(
r=16, # LoRA matris rütbesi (rank)
lora_alpha=32, # ölçekleme faktörü
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"
],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
# trainable params: 20,971,520 || all params: 7,262,703,616 || trainable%: 0.2887%
6.4 — Databricks Dolly 15k ile SFTTrainer
from datasets import load_dataset
dataset = load_dataset("databricks/databricks-dolly-15k", split="train[:2000]")
def format_prompts(sample):
instruction = f"### Instruction:\n{sample['instruction']}\n"
context = f"### Context:\n{sample['context']}\n" if sample['context'] else ""
response = f"### Response:\n{sample['response']}"
full_prompt = f"<s>[INST] {instruction}{context} [/INST] {response}</s>"
return {"text": full_prompt}
formatted_dataset = dataset.map(format_prompts)
from transformers import TrainingArguments
from trl import SFTTrainer
training_args = TrainingArguments(
output_dir="mistral-7b-dolly-lora",
per_device_train_batch_size=4,
gradient_accumulation_steps=4, # efektif batch size = 16
learning_rate=2e-4,
max_steps=100,
optim="paged_adamw_32bit", # paged AdamW ile ekstra VRAM tasarrufu
bf16=True,
save_strategy="steps",
save_steps=50,
report_to="none"
)
trainer = SFTTrainer(
model=model,
train_dataset=formatted_dataset,
peft_config=peft_config,
dataset_text_field="text",
max_seq_length=512,
tokenizer=tokenizer,
args=training_args
)
trainer.train()
# Yalnızca ~80 MB'lık LoRA adaptörünü kaydet / Hub'a yükle
trainer.model.save_pretrained("mistral-7b-dolly-adapter")
trainer.model.push_to_hub("rasittekin/mistral-7b-dolly-adapter")
6.5 — Base Model + LoRA Adapter Birleştirme (Inference)
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
base_model_id = "mistralai/Mistral-7B-v0.1"
adapter_model_id = "rasittekin/mistral-7b-dolly-adapter"
tokenizer = AutoTokenizer.from_pretrained(base_model_id)
base_model = AutoModelForCausalLM.from_pretrained(base_model_id, torch_dtype=torch.bfloat16, device_map="auto")
fine_tuned_model = PeftModel.from_pretrained(base_model, adapter_model_id)
prompt = "<s>[INST] ### Instruction:\nWhat is Machine Learning? [/INST]"
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = fine_tuned_model.generate(**inputs, max_new_tokens=100)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
fine_tuned_model.merge_and_unload() metodu çağrılarak LoRA adaptör matrisleri ana model ağırlıklarına kalıcı olarak eklenip tek bir bütün model çıktısı da elde edilebilir. Serinin son durağı: bu modeli gerçek bir servise dönüştürmek.
Uçtan Uca Proje ve Canlıya Alma (End-to-End LLM Deployment)
7.1 — Üretim Ortamında LLM Sunumu
Bir modeli eğitip notebook'ta çalıştırmak işin ilk aşamasıdır. Üretime (production) taşımak beraberinde şu zorlukları getirir:
- Donanım maliyetleri: Yüksek VRAM ve işlem gücü gerektiren GPU sunucularda düşük gecikmeli host etme.
- Ön yüz & bağımlılıklar: Model kodlarını API servisine dönüştürme, arayüz yazma, kütüphane versiyonlarını eşleme.
- MLOps: Canlıdaki modelin yanıt süresini (latency), doğruluğunu ve güncelliğini sürekli izleme.
Hugging Face ekosistemindeki Gradio ve Hugging Face Spaces araçları bu karmaşık yayınlama sürecini birkaç komut satırına indirger.
7.2 — Open-Source LLM ile Chatbot Arayüzü
import gradio as gr
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "microsoft/Phi-4-mini-instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float32,
device_map="auto" if torch.cuda.is_available() else "cpu"
)
def generate_response(message, history):
messages = [{"role": "system", "content": "You are a helpful and polite AI Assistant."}]
for user_msg, assistant_msg in history:
messages.append({"role": "user", "content": user_msg})
messages.append({"role": "assistant", "content": assistant_msg})
messages.append({"role": "user", "content": message})
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=256, do_sample=True, temperature=0.7, top_p=0.92)
return tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
demo = gr.ChatInterface(
fn=generate_response,
title="🤖 Open-Source LLM Chatbot",
description="Hugging Face ve Gradio ile canlıya alınmış açık kaynaklı Üretken Yapay Zeka uygulaması.",
examples=[
"Bana yapay zeka ve NLP hakkında 3 cümlelik bir özet çıkar.",
"Python'da list comprehension nasıl kullanılır?",
"Hugging Face Spaces nedir ve ne işe yarar?"
]
)
if __name__ == "__main__":
demo.launch()
7.3 — Hugging Face Spaces & Git Workflow
# 1. Hub'a token ile giriş
huggingface-cli login
# 2. Yeni bir Space deposunu klonlama (Gradio SDK)
git clone https://huggingface.co/spaces/rasittekin/chatbot-demo
cd chatbot-demo
# 3. Dosyaları ekle, commit et, canlıya push et
git add app.py requirements.txt
git commit -m "Add Chatbot App and requirements"
git push
git push komutu çalıştırıldığı anda Hugging Face Spaces arka planda bir Docker konteyneri ayağa kaldırır, requirements.txt içindeki paketleri yükler ve app.py'yi çalıştırarak uygulamayı 7/24 erişilebilir bir web adresine dönüştürür.
7.4 — RAG (Retrieval-Augmented Generation) vs Fine-Tuning
Fine-Tuning
- Modeli belirli bir üslup/formata uzmanlaştırır
- Sabit, etiketlenmiş veri seti gerekir
- Zaman ve GPU maliyeti yüksektir
- Halüsinasyon riski vardır (öğrendiğini uydurabilir)
- Güncellemek için yeniden eğitim gerekir
RAG
- Dış veritabanından güncel, dinamik bilgi çeker
- Vektör veritabanında (Vector DB) saklanan dokümanları sorgular
- Halüsinasyon riski düşüktür — kaynağa dayalı yanıt üretir
- Şeffaf kaynak gösterimi sunar
- Yeni doküman eklemek kadar kolay güncellenir
Seri Sonu: Öğrenilen Teknikler ve Gelecek Bakış
Bu 7 bölümlük uçtan uca rehber serisinde, Transformer mimarisinin matematiksel temellerinden başlayıp gerçek bir LLM servisini canlıya almaya kadar uzanan bütün bir mühendislik hattını adım adım kat ettik.
- Transformer & Self-AttentionDarboğaz probleminin Attention mekanizmasıyla nasıl çözüldüğünü ve pipeline API'sini öğrendik.
- DistilBERT ile Duygu AnaliziTrainer API kullanımını ve Confusion Matrix ile hata analizini uyguladık.
- XLM-RoBERTa ile Çok Dilli NERSubword alignment ve Zero-Shot Cross-Lingual transferin gücünü gördük.
- Metin Üretim AlgoritmalarıGreedy, Beam Search, Temperature, Top-k ve Top-p sampling'i kodladık.
- Mistral 7B & Chat TemplatesSliding Window Attention ve rol bazlı prompt yönetimini inceledik.
- PEFT / QLoRA4-bit quantization ile parametre verimli LLM fine-tuning yaptık.
- Gradio & Hugging Face SpacesProjeyi canlı bir web servisine dönüştürdük.
Rasittekin18/generative-ai-nlp-huggingfaceBilgisayarlı görme dünyasında olduğu gibi NLP tarafında da gelişmeler Vision-Language modelleri, açık kaynaklı daha küçük ve verimli LLM'ler (Phi-4 Mini gibi) ve RAG mimarileri ekseninde hızla ilerliyor. Ancak bu ileri sistemlerin arkasındaki karar mekanizmalarını doğru anlamak, Self-Attention'ın temel matematiğine ve tokenization'ın çalışma prensiplerine hâkim olmaktan geçiyor.
Üretken Yapay Zeka dünyasında öğrenme bitmeyen bir maratondur. Ellerimizi kodla kirletmeye ve geliştirmeye devam! 🚀

