processed image (16)

Uçtan Uca Üretken Yapay Zeka ve NLP Rehberi

NLP LOG
Doğal Dil İşleme & Üretken Yapay Zeka Mühendisliği

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.

Yazar Raşit Tekin Kapsam Transformers · PEFT/LoRA · Gradio Repo generative-ai-nlp-huggingface
00Giriş

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.

Serinin kapsadığı model ve teknolojiler
AlanModel / TeknolojiAçıklama
Metin SınıflandırmaDistilBERTEmotion veri seti üzerinde duygu analizi ve model değerlendirme.
Token SınıflandırmaXLM-RoBERTaAlmanca üzerinde eğitilip TR, EN, FR, IT dillerine genellenen çok dilli NER.
Metin ÜretimiGPT-2, Mistral-7B, Phi-4 MiniGreedy, Beam Search, Top-k ve Top-p (Nucleus) sampling stratejileri.
LLM Fine-TuningMistral-7B, BitsAndBytes, LoRA4-bit NormalFloat (NF4) Quantization ve Adapter bazlı fine-tuning.
DeploymentGradio, Hugging Face Spacesİnteraktif web arayüzleri ve bulut üzerinde canlı sunum.

Serinin Yol Haritası

  1. Bölüm 1 — Mimarinin DoğuşuRNN/LSTM'in darboğazından Self-Attention'a, Hugging Face pipeline API'sine.
  2. Bölüm 2 — Metin SınıflandırmaEmotion veri seti, Subword Tokenization ve DistilBERT fine-tuning.
  3. Bölüm 3 — Token SınıflandırmaXLM-RoBERTa, SentencePiece ve Zero-Shot Cross-Lingual NER.
  4. Bölüm 4 — Metin ÜretimiGreedy/Beam Search, Temperature, Top-k ve Top-p Sampling.
  5. Bölüm 5 — Büyük Dil ModelleriMistral 7B mimarisi, Sliding Window Attention, Chat Templates.
  6. Bölüm 6 — PEFT / QLoRAQuantization, LoRA matematiği ve SFTTrainer ile fine-tuning.
  7. Bölüm 7 — ProductionGradio, Hugging Face Spaces ve RAG vs Fine-Tuning karşılaştırması.
i
Kaynak kodBu seride ele alınan tüm notebook ve uygulamalara açık kaynak GitHub reposundan erişebilirsiniz: Rasittekin18/generative-ai-nlp-huggingface
01Bölüm 1

Mimarinin 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.

geleneksel dizisel işleme (rnn / lstm)
"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.
TIP
Attention'ın DoğuşuDarboğaz sorununu aşmak için geliştirilen Attention mekanizması sayesinde Decoder, Encoder'ın ürettiği tüm zaman adımlarındaki gizli durumlara erişim kazandı ve her çıktı adımında girdinin hangi kelimelerine daha çok ağırlık vereceğini öğrendi.

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ı.

self-attention: tüm kelimeler aynı anda birbiriyle ilişkilendirilir
"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:

Scaled Dot-Product Attention $$\text{Attention}(Q,K,V) = \text{softmax}\!\left(\frac{QK^{T}}{\sqrt{d_k}}\right)V$$

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.

python
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:

python
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.

02Bölüm 2

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).

python
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()
!
Sınıf dengesizliğijoy 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.
python
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

distilbert sınıflandırma mimarisi
[Input IDs] ──▶ [ DistilBERT Base (Transformer Layers) ] ──▶ [Hidden State (768-dim)] ──▶ [ Linear Head (6 Outputs) ]
python
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

python
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ı.

03Bölüm 3

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 vs token sınıflandırma
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.

python
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ı?

subword etiket hizalama
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.

python
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.

python
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()
Diller arası sıfır transfer (zero-shot cross-lingual) sonuçları
DilDurumF1-Score
AlmancaEğitildiği dil%86
FransızcaSıfır transfer%74
İtalyancaSıfır transfer%67
İngilizceSıfır transfer%58
TürkçeSı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.

python
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.

04Bölüm 4

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.

Koşullu olasılık dağılımı $$P(w_t \mid w_1, w_2, \ldots, w_{t-1})$$
auto-regressive üretim döngüsü
[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)
i
Kritik noktaÜretilen metnin kalitesi yalnızca modelin büyüklüğüne değil; olasılık dağılımından bir sonraki token'ın nasıl seçildiğine (decoding strategy) doğrudan bağlıdır.

4.2 — Arama Algoritmaları

Greedy Search, her adımda en yüksek olasılığa sahip tek token'ı açgözlü şekilde seçer:

python
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:

Cümle olasılığı $$P(\text{cümle}) = \prod_{t} P(w_t)$$
beam search — hipotez uzayı
[The] ──┬──▶ [dog]   (0.6) ──▶ [house] (0.6) = Toplam: 0.36  ◀── Seçilen En İyi Dizi
        └──▶ [night] (0.5) ──▶ [woman] (0.4) = Toplam: 0.20
python
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 ile ölçeklenmiş softmax $$P(w_i) = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)}$$
  • 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ı k token 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.
python
# 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?

Görev bazlı decoding stratejisi seçimi
SenaryoTavsiye Edilen StratejiNeden
Kod yazma, matematik, soru-cevapGreedy Search / düşük TemperatureDoğruluk ve kararlılık öncelikli, rastgelelik istenmez.
Çeviri, özetlemeBeam Search + N-Gram PenaltyKaynağa sadık kalınmalı, tekrarlar önlenmeli.
Hikaye anlatımı, yaratıcı yazarlıkTop-k + Top-p + orta-yüksek TemperatureMetin 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.

05Bölüm 5

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.

MMLU benchmark karşılaştırması
ModelMMLU 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.

sliding window attention (w=2)
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

  1. Pre-trained (Base) ModelTrilyonlarca kelimelik ham metinle eğitilmiş temel iskelet; sohbet etmek yerine cümleyi devam ettirmeye çalışır.
  2. Fine-Tuned ModelBelirli bir alan (tıp, hukuk, kodlama) verisiyle uzmanlaştırılmış hali.
  3. Instruction-Tuned (Instruct) ModelTalimat-yanıt formatındaki verilerle eğitilmiş, kullanıcı komutlarına doğrudan yanıt üretir.
  4. 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.

python
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:

python
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.

06Bölüm 6

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.

python
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:

LoRA ayrıştırması $$W \in \mathbb{R}^{d \times k} \qquad \Delta W = B \cdot A,\ \ A \in \mathbb{R}^{r \times k},\ B \in \mathbb{R}^{d \times r},\ \ r \ll \min(d,k)$$
lora — dondurulmuş ağırlığa paralel adaptö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).

python
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%
i
Sonuç7.2 milyar parametreli Mistral 7B'nin yalnızca %0.28'lik kısmı (~21 milyon parametre) eğitilir.

6.4 — Databricks Dolly 15k ile SFTTrainer

python
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)
python
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)

python
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.

07Bölüm 7

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ü

python — app.py
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

bash
# 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
i
Altın kuralModele yeni bilgi kazandırıp halüsinasyonu önlemek istiyorsanız RAG; modele belirli bir üslup, çıktı formatı veya spesifik görev uzmanlığı kazandırmak istiyorsanız Fine-Tuning tercih edilmelidir. Şirket ölçeğindeki uygulamalarda iki yöntem sıklıkla birlikte kullanılır.
08Sonuç

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.

  1. Transformer & Self-AttentionDarboğaz probleminin Attention mekanizmasıyla nasıl çözüldüğünü ve pipeline API'sini öğrendik.
  2. DistilBERT ile Duygu AnaliziTrainer API kullanımını ve Confusion Matrix ile hata analizini uyguladık.
  3. XLM-RoBERTa ile Çok Dilli NERSubword alignment ve Zero-Shot Cross-Lingual transferin gücünü gördük.
  4. Metin Üretim AlgoritmalarıGreedy, Beam Search, Temperature, Top-k ve Top-p sampling'i kodladık.
  5. Mistral 7B & Chat TemplatesSliding Window Attention ve rol bazlı prompt yönetimini inceledik.
  6. PEFT / QLoRA4-bit quantization ile parametre verimli LLM fine-tuning yaptık.
  7. Gradio & Hugging Face SpacesProjeyi canlı bir web servisine dönüştürdük.
i
Kaynak kodBu seride ele alınan tüm notebook, uygulama kodu ve detaylara GitHub reposundan ulaşabilir, kendi projelerinizde özgürce kullanabilirsiniz: Rasittekin18/generative-ai-nlp-huggingface

Bilgisayarlı 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! 🚀

Raşit Tekin

Yapay Zeka & NLP Mühendisi. Hugging Face ekosistemi, Transformer mimarileri ve açık kaynak üzerine yazıyorum.

© Raşit Tekin — Doğal Dil İşleme & Üretken Yapay Zeka üzerine mühendislik notları.

Yorum bırakın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir