Sıfırdan LLM

Büyük Dil Modellerini sıfırdan eğitmek: Transformer mimarisinden, devasa veri setlerine, GPU kümelerine ve kayıp fonksiyonlarına kadar her şey.

model.generate("Sıfırdan LLM eğitmek...")

LLM Nedir?

Büyük Dil Modeli (Large Language Model - LLM), devasa miktarda metin verisi üzerinde eğitilmiş, insan dilinin istatistiksel yapılarını ve anlamını öğrenen derin öğrenme modelleridir. GPT-4, Llama 3, Claude gibi modeller bu kategoridedir.

"Sıfırdan" eğitmek, bir modeli önceden eğitilmiş ağırlıklar olmadan, sadece rastgele ağırlıklarla başlatarak ve kendi veri setinizle eğitmek anlamına gelir. Bu, OpenAI veya Meta'nın yaptığı şeydir ve inanılmaz miktarda hesaplama gücü, veri ve mühendislik çabası gerektirir.

💡 Pro İpucu: Günümüzde çoğu uygulama için sıfırdan eğitim gerekmez. Fine-tuning (ince ayar) veya Prompt Engineering (istem mühendisliği) çok daha pratik ve maliyet etkili yöntemlerdir.
LLM'lerin Temel Özellikleri:
  • • Parametre Sayısı: Milyarlarca hatta trilyonlarca parametre (GPT-3: 175B, GPT-4: tahminen ~1T)
  • • Eğitim Verisi: İnternetin büyük bir kısmı, kitaplar, makaleler (TB/PB seviyesinde)
  • • Mimari: Neredeyse tamamen Transformer mimarisi
  • • Görev: Genellikle sonraki token'ı tahmin etmek (dil modelleme)
Tarihçe
2017'de "Attention is All You Need" makalesiyle tanıtılan Transformer mimarisi, LLM devriminin başlangıcıdır. Daha sonra BERT (2018), GPT-2 (2019), GPT-3 (2020) ve günümüz modelleri gelmiştir.

Sıfırdan bir LLM eğitmek, sadece bir algoritma çalıştırmaktan çok daha fazlasıdır. Bu, mühendislik, matematik, sistem tasarımı ve büyük ölçekli veri işleme becerilerinin birleşimidir.

Transformer Mimarisi: Temel Taş

LLM'lerin neredeyse tamamı, 2017'de Vaswani ve arkadaşları tarafından önerilen Transformer mimarisine dayanır. Bu mimarinin gücü, dikkat mekanizması (attention mechanism) sayesinde uzun mesafeli bağlamları (context) etkili bir şekilde modelleyebilmesidir.

class TransformerBlock:
    def __init__(self, embed_dim, num_heads, ff_dim, rate=0.1):
        self.att = MultiHeadAttention(num_heads, key_dim=embed_dim)
        self.ffn = Sequential([
            Dense(ff_dim, activation="relu"),
            Dense(embed_dim)
        ])
        self.layernorm1 = LayerNormalization(epsilon=1e-6)
        self.layernorm2 = LayerNormalization(epsilon=1e-6)
        self.dropout1 = Dropout(rate)
        self.dropout2 = Dropout(rate)
    def call(self, inputs, training):
        attn_output = self.att(inputs, inputs)
        attn_output = self.dropout1(attn_output, training=training)
        out1 = self.layernorm1(inputs + attn_output)
        ffn_output = self.ffn(out1)
        ffn_output = self.dropout2(ffn_output, training=training)
        return self.layernorm2(out1 + ffn_output)
💡 Pro İpucu: Transformer'ın iki temel bileşeni vardır:
1. Multi-Head Attention: Girdinin farklı kısımlarına "dikkat" etmeyi sağlar.
2. Feed-Forward Network: Her pozisyon için ayrı ayrı uygulanan tam bağlantılı katman.

Transformer'ın Temel Bileşenleri:

  • Token Gömmeleri (Token Embeddings): Kelimeleri vektörlere dönüştürür.
  • Pozisyon Gömmeleri (Positional Embeddings): Kelimelerin cümledeki sırasını kodlar.
  • Encoder-Decoder veya Sadece Decoder: GPT serisi sadece decoder kullanır.
  • Masked Self-Attention: Geleceği görmemesi için token'ları maskeleme.
  • Residual Connections & Layer Normalization: Gradyan akışını iyileştirir.
Neden Transformer?
• RNN/CNN'lere göre paralelleştirilebilir, eğitim süresi daha kısa.
• Uzun mesafeli bağımlılıkları daha iyi modelleyebilir.
• Ölçeklenebilir, parametre sayısı artırıldıkça performans artar.

Tokenizasyon: Metni Sayılara Dönüştürmek

Bilgisayarlar metni doğrudan anlayamaz. Bu nedenle, metin önce küçük parçalara (token'lara) bölünmeli ve her bir token benzersiz bir tam sayıya (ID) dönüştürülmelidir. Bu işleme tokenizasyon denir.

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
text = "Merhaba dünya, LLM eğitiyoruz!"
# Token'lara böl
tokens = tokenizer.tokenize(text)
# Token ID'lerine çevir
input_ids = tokenizer.encode(text)
print("Tokenlar:", tokens)
print("Token ID'leri:", input_ids)
Tokenlar: ['Merhaba', 'Ġ', 'dünya', ',', 'Ġ', 'LL', 'M', 'Ġ', 'eğitiyoruz', '!'] Token ID'leri: [6720, 220, 3781, 11, 220, 20177, 46, 220, 37892, 0]
Önemli: Farklı tokenizasyon yöntemleri (Byte-Pair Encoding - BPE, WordPiece, SentencePiece) farklı sonuçlar verir. Seçtiğiniz tokenizer, modelinizin performansını doğrudan etkiler.

Tokenizasyon Türleri:

  • Kelime Seviyesi: Her kelime bir token. Sözlük çok büyük olur, bilinmeyen kelimeler sorun yaratır.
  • Karakter Seviyesi: Her karakter bir token. Sözlük küçük ama bağlam çok uzun olur.
  • Alt Kelime Seviyesi (Subword): En popüler yöntem. BPE, WordPiece gibi. Sık kelimeleri olduğu gibi, nadir kelimeleri parçalara böler.
Tokenizasyon Eğitimi
Sıfırdan LLM eğitiyorsanız, tokenizer'ınızı da sıfırdan eğitmeniz gerekir. Bu, eğitim veri setiniz üzerinde BPE algoritmasını çalıştırmak anlamına gelir.

Dev Veri Setleri: Yakıt

Bir LLM'nin kalbi, üzerinde eğitildiği devasa veri setidir. GPT-3, yaklaşık 570GB (CommonCrawl, WebText, Wikipedia, kitaplar vb.) metin üzerinde eğitilmiştir. Bu, milyarlarca kelimeye karşılık gelir.

💡 Pro İpucu: Veri kalitesi, miktarından bazen daha önemlidir. Kirli, önyargılı veya düşük kaliteli veri, modelinizi zehirler. Veri temizleme (data cleaning) aşaması çok kritiktir.
# Veri seti yükleme ve önişleme örneği (PySpark ile)
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("LLM Data Preprocessing").getOrCreate()
# Büyük veri dosyalarını oku
df = spark.read.text("s3://my-bucket/raw_data/*.txt")
# Temizleme işlemleri: HTML tag'leri kaldır, deduplikasyon yap, dil filtresi uygula
cleaned_df = df.filter(...).map(clean_html).dropDuplicates()
# Tokenizasyon öncesi kaydet
cleaned_df.write.parquet("s3://my-bucket/cleaned_data/")
Yaygın Açık Veri Setleri
The Pile: EleutherAI tarafından derlenmiş, çeşitli kaynaklardan oluşan 825GB veri.
Common Crawl: İnternetin kaba bir kopyası, çok kirli, yoğun temizlik gerektirir.
Wikipedia: Yüksek kaliteli, düzenli metin.
GitHub Kodu: Kod üretimi için.

Veri Seti Hazırlama Adımları:

  1. Toplama: Web taraması, açık kaynak veri setleri.
  2. Temizleme: Dublikatların çıkarılması, kötü kaliteli içeriğin filtrelenmesi, kişisel verilerin anonimleştirilmesi.
  3. Önişleme: Küçük harfe çevirme, özel karakterleri temizleme.
  4. Tokenizasyon: Yukarıda bahsedilen tokenizasyon işlemi.
  5. Formatlama: Modelin girdi formatına uygun hale getirme (genellikle düz metin akışı).

Model Mimarisi ve Hiperparametreler

Transformer mimarisi temelinde, modelin boyutunu ve şeklini belirleyen birçok hiperparametre vardır. Bu seçimler, modelin kapasitesini, eğitim süresini ve nihai performansını doğrudan etkiler.

# Basit bir GPT benzeri model konfigürasyonu
config = {
    "vocab_size": 50257,        # Tokenizer sözlük boyutu
    "embed_dim": 768,         # Gömme katmanı boyutu
    "num_layers": 12,         # Transformer blok sayısı
    "num_heads": 12,         # Multi-head attention baş sayısı
    "ff_dim": 3072,          # Feed-forward katman genişliği
    "dropout_rate": 0.1,       # Dropout oranı
    "max_position_embeddings": 1024 # Maksimum dizi uzunluğu
}
💡 Ölçekleme Yasası: Chinchilla makalesine göre, model boyutu (parametre sayısı) ile eğitim verisi miktarı dengeli olmalıdır. Sadece modeli büyütmek yeterli değildir, veri de artırılmalıdır.

Kritik Hiperparametreler:

  • n_layer: Transformer katman sayısı. Derinliği belirler.
  • n_head: Attention başı sayısı. Paralel dikkat mekanizmaları.
  • d_model: Model boyutu / Gömme boyutu. Genişliği belirler.
  • d_ff: Feed-forward katmanının gizli boyutu (genellikle d_model'in 4 katı).
  • context_length: Modelin bir seferde işleyebileceği maksimum token sayısı.
  • dropout: Aşırı öğrenmeyi (overfitting) önlemek için.
Başlangıç Ağırlıkları
Model, eğitimden önce rastgele ağırlıklarla başlatılır. Bu ağırlıkların nasıl başlatıldığı (örneğin, Xavier veya Kaiming başlatma) eğitim dinamiklerini etkileyebilir.
Dikkat: Bu hiperparametreler, modelin RAM ve GPU belleği tüketimini doğrudan etkiler. Çok büyük bir model, tek bir GPU'ya sığmayabilir ve model paralelleştirme teknikleri (model parallelism) gerektirebilir.

Kayıp Fonksiyonu ve Optimizasyon

LLM'ler genellikle dil modelleme görevi için eğitilir. Yani, bir dizi token verildiğinde, bir sonraki token'ı tahmin etmeye çalışırlar. Bu, çok sınıflı sınıflandırma problemi olarak modellenir ve çapraz entropi kaybı (cross-entropy loss) kullanılır.

import torch.nn.functional as F
# Model çıktıları (logits): [batch_size, sequence_length, vocab_size]
logits = model(input_ids)  # Şekil: (B, T, V)
# Hedefler: Bir sonraki token'ların ID'leri
targets = input_ids[:, 1:]  # Şekil: (B, T-1)
# Çıktıları kaydırmak (shift)
shifted_logits = logits[:, :-1, :]  # Şekil: (B, T-1, V)
# Kayıp: Cross Entropy
loss = F.cross_entropy(
    shifted_logits.reshape(-1, shifted_logits.size(-1)),
    targets.reshape(-1)
)
tensor(4.6052, grad_fn=)
💡 Pro İpucu: Kayıp değeri, modelin ne kadar "yanlış" olduğunu gösterir. Eğitim sırasında bu değerin düşmesini isteriz. Başlangıçta kayıp, log(vocab_size)'e yakındır (örneğin, 50k kelime için ~10.8).

Optimizasyon Algoritmaları:

  • AdamW: LLM'ler için en popüler optimizatör. Ağırlık çürümesi (weight decay) ile Adam'ın birleşimi.
  • SGD: Stokastik Gradyan İnişi. Daha basit ama LLM'lerde AdamW kadar etkili değil.
  • Adafactor: Bellek verimli bir optimizatör, çok büyük modeller için kullanılır.
Öğrenme Oranı (Learning Rate)
Öğrenme oranı, modelin her adımda ne kadar "öğreneceğini" belirler. Çok yüksekse eğitim ıraksar, çok düşükse çok yavaş ilerler. Genellikle cosine warmup gibi zamanla azalan öğrenme oranı planları kullanılır.

Eğitim Süreci ve Dağıtık Eğitim

Sıfırdan bir LLM eğitmek, günler, haftalar hatta aylar süren bir süreçtir. Bu süreç, veri setinin parçalara (batch'lere) bölünmesi, modelin bu batch'leri işlemesi, kaybı hesaplaması, gradyanları geriye doğru yayması (backpropagation) ve ağırlıkları güncellemesi şeklinde devam eder.

for epoch in range(num_epochs):
    for batch in dataloader:
        optimizer.zero_grad()
        outputs = model(batch['input_ids'])
        loss = compute_loss(outputs, batch['labels'])
        loss.backward()          # Gradyanları hesapla
        optimizer.step()         # Ağırlıkları güncelle
        # Gradyanları kes (gradient clipping)
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        # İlerleme kaydı
        if step % 100 == 0:
            print(f"Adım {step}, Kayıp: {loss.item():.4f}")
Kritik Teknik: Gradyan Kırpma (Gradient Clipping) gradyan patlamasını (exploding gradients) önlemek için çok önemlidir. Aksi takdirde eğitim ıraksayabilir.

Dağıtık Eğitim Stratejileri:

  • Veri Paralelliği (Data Parallelism): Farklı GPU'lar aynı modelin kopyasını çalıştırır, farklı veri batch'leri üzerinde. Gradyanlar sonra toplanır. En yaygın yöntem.
  • Model Paralelliği (Model Parallelism): Modelin farklı katmanları farklı GPU'larda çalışır. Çok büyük modeller için gerekli.
  • Pipeline Paralelliği: Model katmanları "boru hattı" gibi çalışır, veri parçalar halinde ilerler.
  • Tensor Paralelliği: Tek bir matris çarpımını bile birden fazla cihazda dağıtır.
Kütüphaneler
PyTorch Distributed: Temel dağıtık eğitim.
FSDP (Fully Sharded Data Parallel): PyTorch'un gelişmiş veri paralelliği.
DeepSpeed: Microsoft'tan, çok büyük modeller için optimize edilmiş.
Megatron-LM: NVIDIA'dan, model paralelliği için.

GPU/TPU Kümeleri: Güç

Sıfırdan bir LLM eğitmek, muazzam miktarda hesaplama gücü gerektirir. Bu güç, NVIDIA A100/H100 GPU'ların veya Google TPU'larının oluşturduğu devasa kümelerden gelir. GPT-3'ün eğitimi için binlerce GPU kullanıldığı tahmin edilmektedir.

💡 Maliyet: Bir LLM'nin eğitim maliyeti milyonlarca dolar olabilir. Bu nedenle, sıfırdan eğitim genellikle büyük şirketler veya iyi fonlanmış araştırma laboratuvarlarına mahsustur.
# PyTorch ile çoklu GPU kullanımı
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
# Dağıtık ortamı başlat
dist.init_process_group(backend="nccl")
# Modeli GPU'ya taşı ve DDP'ye sar
model = MyLLMModel().to(device)
model = DDP(model, device_ids=[local_rank])
# Optimizatörü ve DataLoader'ı ayarla
optimizer = torch.optim.AdamW(model.parameters())
dataloader = prepare_distributed_dataloader(dataset)
Performansı Artıran Donanım Özellikleri:
  • • Tensor Cores: NVIDIA GPU'larda, karışık hassasiyetli (mixed-precision) matris çarpımları için optimize edilmiş çekirdekler.
  • • Yüksek Bellek Bant Genişliği: NVLink gibi teknolojilerle GPU'lar arası veri transferi hızlandırılır.
  • • TPU v4 Pods: Google'ın özel AI çipi, çok büyük ölçekli eğitim için.
Karışık Hassasiyet (Mixed Precision)
torch.cuda.amp (Automatic Mixed Precision) gibi teknikler, hesaplamaların bir kısmını 16 bit (float16) ile yaparak bellek kullanımını azaltır ve hızı artırır. Gradyanların hassasiyetini korumak için master ağırlıklar 32 bit (float32) ile tutulur.
Zorluk: Bu kadar çok cihazda eğitim yaparken, cihazların senkronizasyonu, iletişim gecikmeleri ve hata toleransı büyük mühendislik zorluklarıdır.

Fine-Tuning ve Prompt Engineering

Sıfırdan eğitim çok maliyetli olduğundan, çoğu gerçek dünya uygulaması için fine-tuning (ince ayar) veya prompt engineering (istem mühendisliği) tercih edilir. Bu yöntemler, önceden eğitilmiş bir modeli alır ve onu spesifik bir göreve uyarlar.

# Hugging Face Transformers ile fine-tuning
from transformers import GPT2LMHeadModel, Trainer, TrainingArguments
model = GPT2LMHeadModel.from_pretrained("gpt2")
training_args = TrainingArguments(
    output_dir="./results",
    per_device_train_batch_size=4,
    num_train_epochs=3,
    save_steps=500,
    logging_steps=100,
)
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset,
    data_collator=data_collator,
)
trainer.train()
💡 Pro İpucu: LoRA (Low-Rank Adaptation) gibi parametre verimli fine-tuning (PEFT) yöntemleri, modelin sadece küçük bir kısmını günceller ve bellek/bilgi işlem gereksinimlerini büyük ölçüde azaltır.

Yöntemlerin Karşılaştırması:

Yöntem Maliyet Esneklik
Sıfırdan Eğitim Çok Yüksek Maksimum
Fine-Tuning Orta Yüksek
Prompt Engineering Düşük Düşük
Prompt Engineering Örneği
İstem: "Aşağıdaki metni özetle: [METİN] Özet:"
Bu basit "talimat", modelin davranışını özetleme görevine yönlendirir. Sıfır atış (zero-shot) veya az atış (few-shot) öğrenme için kullanılır.

Sonuç olarak, sıfırdan LLM eğitmek, AI'nın en zor ve en heyecan verici mühendislik projelerinden biridir. Bu rehber, bu yolculuğun teknik boyutlarına dair bir harita sunar. Başarılar dilerim!

Yolculuğun Başlıyor!

Son Tavsiyeler
Matematik ve Teori: Lineer cebir, olasılık, derin öğrenme teorisi çok işinize yarayacaktır.
Araştırma Makaleleri: "Attention is All You Need", "Chinchilla", "LLaMA" gibi temel makaleleri okuyun.
Topluluk: Hugging Face, GitHub, Reddit (r/MachineLearning) ve Discord topluluklarına katılın.
Deneyin: Öncelikle küçük modellerle (örneğin, nanoGPT) deneyler yapın.

model.train() # Başarılar!