Encoder-decoder yapısı
Önkoşul:Position-wise feed-forward ağ
Kanca
Şimdiye kadar TEK bir cümle üzerinde çalıştık. Ama çeviri gibi görevlerde İKİ cümle var: kaynak (“Kedi masaya çıktı”) ve hedef (“The cat climbed…”). Model, hedefi ÜRETİRKEN kaynağa NASIL “bakıyor”?
Sezgi
Orijinal “Attention is All You Need” mimarisi İKİ parçadan oluşur: encoder (kaynağı OKUR) ve decoder (hedefi ÜRETİR). Decoder’ın YENİ bir silahı var: “çapraz dikkat” (cross-attention) — sorgu KENDİSİNDEN, ama anahtar/değer ENCODER’DAN gelir. Böylece decoder, her kelimeyi üretirken kaynak cümlenin İLGİLİ kısmına “bakabilir”.
Mekanizma
İki dizi: kaynak ve hedef
# 10-15. derste TEK bir dizi (cümle) üzerinde çalıştık. Ama çeviri gibi görevlerde
# İKİ dizi var: KAYNAK (encoder'a girer) ve HEDEF (decoder ÜRETİR). Örnek: Türkçe'den
# İngilizce'ye çeviri.
KAYNAK = ["Kedi", "masaya", "çıktı"] # Türkçe (encoder girdisi)
HEDEF_SIMDIYE_KADAR = ["The", "cat"] # İngilizce, ŞU ANA KADAR üretilen (decoder girdisi)
print(f"Kaynak dizi (Türkçe, encoder): {KAYNAK}")
print(f"Hedef dizi (İngilizce, decoder, şimdiye kadar üretilen): {HEDEF_SIMDIYE_KADAR}")
print("\nEncoder, KAYNAK diziyi TAMAMEN görebilir (bidirectional -- 10. dersteki self-attention gibi).")
print("Decoder ise HEDEF dizide SADECE kendinden ÖNCEKİLERİ görebilir (11. dersteki nedensel maske).")Kaynak dizi (Türkçe, encoder): ['Kedi', 'masaya', 'çıktı']
Hedef dizi (İngilizce, decoder, şimdiye kadar üretilen): ['The', 'cat']
Encoder, KAYNAK diziyi TAMAMEN görebilir (bidirectional -- 10. dersteki self-attention gibi).
Decoder ise HEDEF dizide SADECE kendinden ÖNCEKİLERİ görebilir (11. dersteki nedensel maske).Önce encoder’ı çalıştıralım — 10. dersteki AYNI self-attention, ama BU SEFER kaynak cümle üzerinde:
Encoder: kaynağı oku
# Encoder: KAYNAK dizi üzerinde SIRADAN self-attention (maskesiz, TÜM kaynak
# kelimeleri birbirini görebilir).
D = 4
X_kaynak = torch.tensor([
[1.5, 0.0, 0.0, 0.0], # Kedi
[0.0, 1.5, 0.0, 0.0], # masaya
[0.3, 0.3, 1.2, 0.0], # çıktı
])
torch.manual_seed(125)
Wq_enc, Wk_enc, Wv_enc = torch.randn(D, D) * 0.5, torch.randn(D, D) * 0.5, torch.randn(D, D) * 0.5
Q_enc, K_enc, V_enc = X_kaynak @ Wq_enc, X_kaynak @ Wk_enc, X_kaynak @ Wv_enc
encoder_ciktisi, _ = dikkat(Q_enc, K_enc, V_enc)
print(f"\nEncoder çıktısı şekli: {tuple(encoder_ciktisi.shape)} (= {len(KAYNAK)} kaynak token x {D} boyut)")
print("Bu çıktı, HER kaynak kelimenin, DİĞER TÜM kaynak kelimelerle 'zenginleştirilmiş' temsilidir --")
print("decoder, üretim boyunca BUNU kullanacak.")
Encoder çıktısı şekli: (3, 4) (= 3 kaynak token x 4 boyut)
Bu çıktı, HER kaynak kelimenin, DİĞER TÜM kaynak kelimelerle 'zenginleştirilmiş' temsilidir --
decoder, üretim boyunca BUNU kullanacak.Şimdi YENİ mekanizma: çapraz dikkat. Decoder’ın sorgusu, encoder’ın çıktısındaki anahtar/değerlerle eşleşiyor:
Çapraz dikkat: decoder, encoder'a bakar
# Decoder'ın İKİNCİ (ve YENİ) dikkat türü: "çapraz dikkat" (cross-attention).
# Sorgu (Q) DECODER'DAN gelir, ama Anahtar/Değer (K, V) ENCODER çıktısından gelir --
# yani decoder, kaynak cümleye 'BAKAR'.
X_hedef = torch.tensor([
[0.1, 0.1, 0.1, 1.0], # "The" -- genel bir kelime, belirli bir kaynağa odaklanmaz
[1.4, 0.0, 0.0, 0.1], # "cat" -- 'Kedi'ye YAKIN tasarlandı
])
torch.manual_seed(10146)
Wq_dec, Wk_capraz = torch.randn(D, D) * 0.5, torch.randn(D, D) * 0.5
Q_dec = X_hedef @ Wq_dec # sorgu: DECODER'DAN
K_capraz = encoder_ciktisi @ Wk_capraz # anahtar: ENCODER çıktısından (V_capraz = encoder_ciktisi)
capraz_ciktisi, capraz_agirliklar = dikkat(Q_dec, K_capraz, encoder_ciktisi)
print(f"\nÇapraz dikkat matrisi şekli: {tuple(capraz_agirliklar.shape)} (= {len(HEDEF_SIMDIYE_KADAR)} hedef x {len(KAYNAK)} kaynak)")
print("DİKKAT: bu matris KARE DEĞİL -- 10. dersteki self-attention'ın N×N matrisinden FARKLI,")
print("çünkü sorgu ve anahtar/değer İKİ FARKLI diziden geliyor.")
for hedef_kelime, agirlik_satiri in zip(HEDEF_SIMDIYE_KADAR, capraz_agirliklar.tolist()):
print(f"\n '{hedef_kelime}' hangi kaynak kelimelere bakıyor?")
for kaynak_kelime, a in zip(KAYNAK, agirlik_satiri):
print(f" {kaynak_kelime:<10} ağırlık={a:.3f}")
en_yuksek = KAYNAK[capraz_agirliklar[1].argmax().item()]
print(f"\n'cat' üretilirken, en çok '{en_yuksek}' kaynak kelimesine bakılıyor ({capraz_agirliklar[1].max().item():.3f}) --")
print("model, 'cat' kelimesinin KARŞILIĞININ kaynak cümlede 'Kedi' olduğunu, ÖĞRENİLMİŞ dikkat ile buluyor.")
Çapraz dikkat matrisi şekli: (2, 3) (= 2 hedef x 3 kaynak)
DİKKAT: bu matris KARE DEĞİL -- 10. dersteki self-attention'ın N×N matrisinden FARKLI,
çünkü sorgu ve anahtar/değer İKİ FARKLI diziden geliyor.
'The' hangi kaynak kelimelere bakıyor?
Kedi ağırlık=0.476
masaya ağırlık=0.240
çıktı ağırlık=0.284
'cat' hangi kaynak kelimelere bakıyor?
Kedi ağırlık=0.658
masaya ağırlık=0.138
çıktı ağırlık=0.204
'cat' üretilirken, en çok 'Kedi' kaynak kelimesine bakılıyor (0.658) --
model, 'cat' kelimesinin KARŞILIĞININ kaynak cümlede 'Kedi' olduğunu, ÖĞRENİLMİŞ dikkat ile buluyor.“cat” üretilirken model, kaynak cümledeki “Kedi”ye 0.658 ağırlıkla bakıyor — çeviri karşılığını, hiçbir sözlük OLMADAN, ÖĞRENİLMİŞ dikkat ile buluyor.
Matematik
Üç dikkat türü, aynı formül
| Dikkat türü | nereden | nereden |
|---|---|---|
| Encoder self-attention | encoder | encoder |
| Decoder maskeli self-attention | decoder | decoder |
| Decoder çapraz dikkat | decoder | encoder |
Formül HİÇ değişmiyor — 9-11. derste kurduğumuz AYNI dikkat() fonksiyonu. Değişen TEK şey, , , ‘nin HANGİ diziden geldiği. Çapraz dikkatte ve FARKLI dizilerden geldiği için, dikkat matrisi KARE OLMAK ZORUNDA DEĞİLDİR.
Kod
Üç dikkat türünün karşılaştırması
# Özet karşılaştırma: encoder'daki self-attention, decoder'daki maskeli self-attention,
# ve decoder'daki çapraz dikkat -- ÜÇÜ de AYNI dikkat() fonksiyonunu kullanıyor, ama
# Q/K/V'nin NEREDEN geldiği DEĞİŞİYOR.
print(f"\n{'Dikkat türü':<28} {'Q nereden':<14} {'K, V nereden':<16} {'Matris şekli':<12}")
print(f"{'Encoder self-attention':<28} {'encoder':<14} {'encoder':<16} {'(3, 3)':<12}")
print(f"{'Decoder maskeli self-att.':<28} {'decoder':<14} {'decoder':<16} {'(2, 2)':<12}")
print(f"{'Decoder çapraz dikkat':<28} {'decoder':<14} {'ENCODER':<16} {'(2, 3)':<12}")
print("\nAYNI matematik (dikkat fonksiyonu), ÜÇ FARKLI 'nereden geliyor' kombinasyonu --")
print("bu üçü BİRLİKTE, bir Transformer encoder-decoder bloğunu oluşturuyor.")
Dikkat türü Q nereden K, V nereden Matris şekli
Encoder self-attention encoder encoder (3, 3)
Decoder maskeli self-att. decoder decoder (2, 2)
Decoder çapraz dikkat decoder ENCODER (2, 3)
AYNI matematik (dikkat fonksiyonu), ÜÇ FARKLI 'nereden geliyor' kombinasyonu --
bu üçü BİRLİKTE, bir Transformer encoder-decoder bloğunu oluşturuyor.Nerede işe yarar
Encoder-decoder yapısı, orijinal 2017 Transformer’ının TAM MİMARİSİ — ama tek kullanım şekli DEĞİL:
- Makine çevirisi, özetleme, soru-cevaplama gibi “bir diziyi başka bir diziye dönüştürme” görevleri encoder-decoder kullanır — kaynak ve hedef FARKLI uzunlukta, FARKLI dillerde/formatlarda olabilir.
- GPT gibi modeller SADECE decoder kullanır (18. ders) — çapraz dikkat YOKTUR, sadece maskeli self-attention; BERT gibi modeller SADECE encoder kullanır (maskesiz self-attention, çeviri ÜRETMEZ, TEMSİL üretir).
- Decoder’ın HER bloğunda üç alt-katman sırayla çalışır: maskeli self-attention → çapraz dikkat → FFN (14-15. ders) — her birinin ETRAFINDA artık bağlantı + layer norm VAR.
Bu 2 hatayı yaparsın:
- Çapraz dikkatin de self-attention gibi KARE bir matris ürettiğini sanmak — HAYIR, hedef ve kaynak uzunlukları FARKLI olabilir, matris dikdörtgen olabilir.
- Her Transformer modelinin encoder VE decoder içerdiğini düşünmek — GPT SADECE decoder, BERT SADECE encoder kullanır (18. ders bu farkı detaylandıracak).
Kendini test et
1. Çapraz dikkatin (cross-attention) self-attention'dan temel farkı nedir?
- Farklı bir matematiksel formül kullanır
- Sorgu (Q) BİR diziden (decoder), anahtar/değer (K, V) BAŞKA bir diziden (encoder çıktısı) gelir -- self-attention'da ÜÇÜ de AYNI diziden gelir (doğru cevap)
- Sadece kısa cümlelerde çalışır
- Softmax kullanmaz
Neden: capraz-dikkat bloğunda Q_dec decoder'ın X_hedef'inden, K_capraz ise encoder_ciktisi'nden hesaplanıyor -- oz-vs-capraz tablosunda bu ayrım AÇIKÇA özetleniyor.
2. Notebook'ta çapraz dikkat matrisinin şekli (2, 3) -- yani KARE DEĞİL. Bunun nedeni ne?
- Bir hesaplama hatası
- Hedef dizi (2 kelime, şimdiye kadar üretilen) ile kaynak dizi (3 kelime) FARKLI uzunlukta -- dikkat matrisinin şekli (hedef uzunluğu × kaynak uzunluğu) olduğu için kare olmak ZORUNDA değil (doğru cevap)
- Sadece decoder'ın son katmanında bu şekil kare olmaz
- Matris her zaman kare olmalıydı, bu bir tasarım hatasıdır
Neden: capraz-dikkat bloğunun çıktısında matris şekli (2, 3) = (len(HEDEF_SIMDIYE_KADAR), len(KAYNAK)) -- self-attention'daki N×N kısıtlaması burada YOKTUR çünkü Q ve K farklı dizilerden geliyor.
3. 'cat' üretilirken çapraz dikkat neden kaynak cümledeki 'Kedi'ye en yüksek ağırlığı (0.658) veriyor?
- Rastgele bir sonuç
- Decoder'ın 'cat' sorgusu ile encoder çıktısındaki 'Kedi' anahtarı arasındaki nokta çarpım BENZERLİĞİ en yüksek çıktığı için -- bu, gerçek modellerde EĞİTİMLE öğrenilen çeviri hizalamasının (alignment) küçük ölçekli bir örneği (doğru cevap)
- 'Kedi' kelimesi alfabetik olarak ilk sırada olduğu için
- Çapraz dikkat her zaman ilk kaynak kelimeye en yüksek ağırlığı verir
Neden: capraz-dikkat bloğunda 'cat' token'ının gömmesi ÖĞRETİCİ amaçlı 'Kedi'ye yakın tasarlandı; softmax(QKᵀ/√d) formülü bu benzerliği en yüksek ağırlığa çeviriyor -- gerçek modellerde bu hizalama veri üzerinde EĞİTİLİR.
Özet
Özet
- Encoder-decoder yapısı, kaynak diziyi (encoder) ve hedef diziyi (decoder) AYRI ele alır -- çeviri gibi "diziden diziye" görevler için tasarlanmıştır.
- Decoder'ın İKİ dikkat türü var: maskeli self-attention (kendi ürettiklerine bakar) ve çapraz dikkat (encoder çıktısına bakar).
- Çapraz dikkatte Q decoder'dan, K ve V encoder çıktısından gelir -- dikkat matrisi KARE OLMAK ZORUNDA DEĞİLDİR.
- Notebook'ta 'cat' üretilirken, model 'Kedi'ye 0.658 ağırlıkla bakarak çeviri karşılığını buldu.
- GPT sadece decoder, BERT sadece encoder kullanır -- her Transformer modelinin encoder VE decoder içermesi GEREKMEZ (18. ders).