7  Transformer’lar ve Attention

Bölüm durumu: çevrildi. Kaynak metin: İngilizce 7. bölüm. Bu sayfadaki attention araçları ve konumsal kodlama gösterimleri tamamen etkileşimlidir.

Öğrenme hedefleri. Bu bölümün sonunda şunları yapabileceksiniz:

7.1 Temel ilkelerden self-attention

Dil modelleri bölümünün (Bölüm 6) sonunda bir sonraki kelimeyi kestirebiliyorduk, ama o bölümün sonunda gördüğümüz tekrarlayan (recurrent) modellerde bir darboğaz vardı: modelin cümle hakkında bildiği her şey, adım adım, sabit boyutlu bir durumdan sıkıştırılarak geçmek zorundaydı. Bu mecazi değil, birebir böyleydi — bir sequence-to-sequence (diziden diziye) çeviri modeli, hedef dilde tek bir kelime bile üretmeden önce tüm kaynak cümleyi tek bir vektöre kodluyordu (Sutskever vd. 2014). Uzun menzilli ilişkiler — bir zamir ile işaret ettiği isim, bir fiil ile uzaktaki öznesi — sürekli kayboluyordu ve cümle uzadıkça çeviri kötüleşiyordu.

Attention (dikkat mekanizması) bu darboğaza verilen bir cevap olarak ortaya çıktı. Bahdanau vd. (2015), decoder’ın (kod çözücü), encoder’ın (kodlayıcı) bütün durumlarına geri bakmasına ve her çıktı adımında hangilerinin önemli olduğunu öğrenmesine izin verdi; inceledikleri çeviri görevlerinde cümle uzadıkça görülen sert kötüleşme büyük ölçüde azaldı. Tarihe dikkat edin: yıl 2015 ve altta hâlâ tekrarlayan bir ağ var.

Transformer’ın iki yıl sonraki katkısı (Vaswani vd. 2017), işin büyük kısmını bu mekanizmanın yaptığını fark edip tekrarlayan ağı tümden bırakmaktı; bloğun geri kalanında feed-forward katman, residual bağlantı ve normalleştirme duruyor. Verdiği cevap şaşırtıcı derecede dolaysız: bırakın her token, görmesine izin verilen bütün token’lara baksın ve her birinden ne kadar bilgi alacağını öğrensin. Hayatınızda bir kez ağırlıklı ortalama hesapladıysanız, çekirdek sezgi zaten sizde demektir. Attention, token vektörlerinin bir ağırlıklı ortalamasıdır; farkı, ağırlıkların kendisinin veriden kestirilmesidir.

Her token’ın vektörü üç ayrı izdüşümden geçirilir: bir query (sorgu — “ben ne arıyorum?”), bir key (anahtar — “ben ne içeriyorum?”) ve bir value (değer — “seçilirsem ne aktarırım?”). \(i\) token’ı ile \(j\) token’ı arasındaki skor, \(q_i \cdot k_j\) iç çarpımıdır. Bu skor ölçeklenir ve her satırın bir olasılık dağılımına dönüşmesi için bir softmax’tan geçirilir:

\[ \operatorname{Attention}(Q, K, V) = \operatorname{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right) V \]

Softmax’tan çıkan o satır, ağırlıklı ortalamadaki ağırlıklar kümesinin ta kendisidir. İstatistikçinin diliyle söylersek: model, her token için tüm cümle üzerinde veriye bağlı bir ağırlıklı ortalama hesaplar — katsayı kestiren bir regresyondan çok Nadaraya–Watson çekirdek regresyonuna benzer — ve şu anda hangi diğer token’ların bilgi taşıdığına karar verir. Bu ağırlıklar value vektörlerinin karışım katsayıları; tek başına “hangi kelime önemli?” sorusunun cevabı değiller, çünkü value dönüşümü, çıktı izdüşümü ve sonraki katmanlar da sonucu şekillendiriyor.

Neye bakacağız. Denklemi hazır bir tanım olarak bırakmayacağız: önce her parçasının neden orada olduğunu göreceğiz, sonra kurşun kalemle kontrol edilebilecek kadar küçük sayılar üzerinde onu bir kez elle çalıştıracağız. Ardından içinde durduğu bloğa ve tek bir head’in neden hiçbir zaman yetmediğine geçeceğiz. Son olarak denklemin dışarıda bıraktığı ama her gerçek modelin eklemek zorunda olduğu üç şeye bakacağız: konum, hangi token’ın hangisini görebileceğine dair bir seçim ve modeli çalıştırmanın maliyetini kaldırıp kaldıramayacağınızı belirleyen aritmetik.

7.1.1 Modelin bütününün şekli

Herhangi bir kutuyu açmadan önce attention’ın gerçekte nerede durduğunu görmekte fayda var. Bir transformer, tek bir parçası tekrarlanan kısa bir işlem hattıdır (pipeline):

Attention modelin bütününde nerede duruyor
TOKEN'LAR kedi sütü içti Embedding tablo araması n × d + Konum burada eklenir, ya da her blokta × L BLOK Transformer bloğu Multi-head attention Feed- forward n × d Son LayerNorm Unembedding sözlüğe n × |V| logits p(sonraki) Token sayısı n ve genişlik d her okta aynıdır: blok vektörleri yeniden yazar, şekillerini asla değiştirmez. Bu bölüm lacivert kutuyu parça parça açıyor.

Mimarinin tamamı. Bir transformer’ı transformer yapan şeyin büyük kısmı tekrarlanan bloğun içinde; iki yanındaki parçalar bir arama tablosu ile bir matris çarpımı. Ama modelin ne yapabileceğini konum bilgisi ve hangi token’ın hangisini görebileceğini belirleyen maske de belirliyor; ikisine de aşağıda geleceğiz.

Bu resmin iki özelliğini şimdiden aklınıza kazımakta fayda var, çünkü sonraki her şey onlara yaslanıyor.

Şekil hiç değişmez. \(n\) token bir bloğa \(n \times d\) matris olarak girer ve \(n \times d\) matris olarak çıkar. Blok bir yeniden yazma adımıdır, yeniden şekillendirme adımı değil; üst üste yığmanın bu kadar kolay olmasının sebebi budur: 12. blok, 1. blokla aynı arayüzü sunar, dolayısıyla derinlik serbest bir parametredir.

Yalnızca tek bir bileşen yana bakabilir. Feed-forward (ileri beslemeli) alt katmanı, normalleştirmeler ve unembedding (son vektörü sözlükteki her kelime için bir skora çeviren izdüşüm; bu ham skorlara logit denir), her token’ı tek başına ele alır. Attention, modelin tamamında \(i\) konumunun \(j\) konumunu okuyabildiği tek yerdir. Bu kitapta bir modelin uzaktaki iki kelimeyi “bağlaması” hakkında söylenen her şey, mekanik olarak, attention matrislerindeki sayılardan geçer — çoğu zaman tek bir sayıdan değil, birden fazla head ve katman boyunca, value dönüşümleri ve residual yolla birlikte.

7.1.2 Neden üç izdüşüm, bir tane değil?

Yukarıdaki denkleme yöneltilebilecek haklı bir itiraz var. Attention’ın bilmesi gereken şey hangi token’ların ilişkili olduğuysa, \(Q\), \(K\) ve \(V\)’yi kurmaya niye zahmet edelim? Token vektörlerini doğrudan \(x_i \cdot x_j\) iç çarpımıyla karşılaştırıp üç matris çarpımından kurtulmak varken?

Bu itirazı ciddiye almak gerekir, çünkü bu alternatifi gerçekten denemek tasarımın mantığını gösteriyor. Aşağıdaki çözümlü örnekteki dört boyutlu embedding’leri (gömme vektörleri) kullanalım — kedi = [0.9, 0.1, 0.0, 0.5], sütü = [0.1, 0.9, 0.0, 0.5], içti = [0.0, 0.1, 0.9, 0.5]. Ham iç çarpımlar şöyle çıkar:

\[ XX^{\top} = \begin{array}{r|rrr} & \texttt{kedi} & \texttt{sütü} & \texttt{içti}\\\hline \texttt{kedi} & \mathbf{1{,}07} & 0{,}43 & 0{,}26\\ \texttt{sütü} & 0{,}43 & \mathbf{1{,}07} & 0{,}34\\ \texttt{içti} & 0{,}26 & 0{,}34 & \mathbf{1{,}07} \end{array} \]

Bu örnekte iki sınırlama ortaya çıkıyor ve her biri bu yaklaşımı ciddi biçimde zayıflatıyor.

Benzerlik simetriktir. Zorunlu olarak \(x_i \cdot x_j = x_j \cdot x_i\)’dir. Yani “fiil nesnesini arıyor” ile “nesne fiilini arıyor” aynı skoru almaya mahkûmdur. Burada neyin zorunlu olup neyin olmadığını doğru koyalım: attention ağırlıkları yine de farklı olabilir, çünkü softmax her satırı kendi paydasıyla normalleştirir; genel olarak \(\alpha_{ij} \neq \alpha_{ji}\)’dir. Kaybolan şey skor tablosunun kendisi üzerindeki denetimdir: hiçbir head, fiil-nesnesini-arıyor ilişkisine nesne-fiilini-arıyor ilişkisinden daha yüksek bir skor veremez, çünkü ikisi tablodaki aynı girdidir. Satır normalleştirmesi ağırlıkları yine de ayırır — \(d_k = 1\) ve \(Q = K = [1, 2]^{\top}\) ile köşegen dışı iki ağırlık 0,731 ve 0,119 çıkar — yani yönlü attention ortadan kalkmaz. Ortadan kalkan şey onu seçme özgürlüğü: head yalnızca simetrik bir tabloyu satır satır normalleştirmenin ürettiği yönleri ifade edebilir. Oysa dilbilgisi tek yönlü işleyen ilişkilerle doludur ve onları öyle puanlayabilmenin yolu ayrı izdüşümlerden geçer.

Kendine benzerlik, bu örnekte, diğer her şeyi bastırır. Bir token’ın kendisine karşı skoru \(x \cdot x\), yani uzunluğunun karesidir ve burada bu değer her satırdaki en büyük girdidir. Matrisi bir softmax’tan geçirin: içti, en büyük ağırlığı olan 0,4235’i kendisine verir, kediye ise yalnızca 0,2825 ayırır. Fiil hiçbir şeye uzanmaz. Katman pahalı bir hesap yapar, ama sonuç her token’ı kendi hâline bırakmaktan pek farklı değildir.

Kesin konuşmak gerekirse \(x_i \cdot x_i \ge x_i \cdot x_j\) ilişkisi, \(\|x_j\| \le \|x_i\|\) olduğunda Cauchy–Schwarz gereği garantidir. Tersi geçerli değil: \(x_i = (1;\ 0)\) ve \(x_j = (0;\ 2)\) ile komşu daha uzundur ama köşegen yine 1’e 0 kazanır — yani daha uzun bir komşu sizi geçebilir, geçmek zorunda değildir. Üç oyuncak vektörümüzün hepsinde \(\|x\|^2 = 1{,}07\), dolayısıyla burada köşegen açık ara kazanıyor. Genel iddia daha zayıf ama yine de zarar verici: izdüşümsüz bir skor, iki token ilişkili olsun olmasın, kendine benzerliği yapısı gereği şişirir.

Şimdi aynı cümleye izdüşümleri geri ekleyelim; birazdan kuracağınız head de bu. İki sütun da softmax satırıdır ve her biri kendi genişliğinin kareköküyle ölçeklenmiştir — solda \(\sqrt{4}\), sağda \(\sqrt{2}\) — böylece karşılaştırılabilirler:

içti şuna bakıyor izdüşüm yok \(W^Q\), \(W^K\) ile
kedi (öznesi) 0,2825 0,4230
sütü (nesnesi) 0,2940 0,5012
içti (kendisi) 0,4235 0,0759

Fiil artık attention’ının %42,35’ini kendisine harcamak yerine %92,41’ini iki argümanına ayırıyor. Üstelik skor matrisi de artık simetrik değil: içti, sütüye 2,97 skoru verirken sütü, içtiye 0,27 veriyor — 11 kat asimetri, ki bütün mesele de bu. İzdüşümsüz hiçbir skor bunu üretemezdi.

Yani üç matris süs değil. Tek bir vektörün aynı anda taşıyamayacağı işleri birbirinden ayırmak için varlar:

  • \(W^Q\), query’yi kurar — ben ne arıyorum? Bakan token’dan okunur.
  • \(W^K\), key’i kurar — başkalarına neyi ilan ediyorum? Bakılan token’dan okunur.
  • \(W^V\), value’yu kurar — seçilirsem ne teslim ederim?

Tasarımın kendisi bu iş bölümüdür. \(Q\) ile \(K\) birlikte bilginin nereye aktığına karar verir; aktarılan içeriği \(V\) sağlar. (Head’in çıktısı ardından \(W^O\) ile dönüştürülüp residual akışa ekleniyor; nihai etkiyi tek başına \(V\) belirlemiyor.) Bunları ayrı tutmak, bir token’ın bir şeyi ilan edip başka bir şeyi teslim etmesine izin verir — ve birbirine hiç benzemeyen iki token’ın yine de güçlü biçimde bağlanabilmesini sağlar; bir fiil ile öznesinin ihtiyacı olan şey tam da budur.

İki matris mi, bir matris mi?

Bu alt bölüm daha derin bir ara not niteliğinde. İlk okumada bir sonraki şekle atlayıp Bölüm 7.2’den sonra buraya dönebilirsiniz.

Bu argümanın neyi kanıtladığı konusunda dikkatli olun. Skoru açıkça yazdığınızda iki izdüşüm tek bir çarpımda birleşir:

\[ q_i \cdot k_j \;=\; (x_i W^Q)(x_j W^K)^{\top} \;=\; x_i\,\underbrace{W^Q W^{K\top}}_{M,\;\; d \times d}\, x_j^{\top} \]

Skora dokunan tek şey \(M\) çarpımıdır. Öğrenilmiş tek bir \(d \times d\) matrisi aynı skorları verirdi — yine asimetrik, yine kendine benzerlik tuzağından uzak, çünkü \(M\)’yi simetrik olmaya zorlayan hiçbir şey yok. O hâlde doğru iddia “iki matris zorunludur” değil; iki matris, modelin aynı zamanda ucuz olmasını istediğinizde yapılan bir tercih. Üç somut gerekçe:

  • Parametreler. 7B sınıfı bir modelde (Llama 2 7B: \(d = 4096\), her biri \(d_k = 128\) genişliğinde 32 head): tek bir head’in \(W^Q, W^K\) çifti \(2 \times 4096 \times 128 = 1{.}048{.}576\) sayıya mal olurken, tam bir \(M\) \(4096^2 = 16{.}777{.}216\) sayı tutar — 16 kat fazla. Oran, katmanın tamamı için de aynıdır, çünkü her head’e bir \(M\) gerekirdi: \(32\) head, çarpanlara ayrılmış hâlde \(2d^2 = 33{,}5\)M, ayrılmamış hâlde \(32d^2 = 537\)M eder. Çarpanlara ayırma kasıtlı bir düşük rank kısıtıdır: \(M\), rankı \(\le d_k\) olmaya zorlanır. Tam bir \(M\) daha geniş bir skor ailesi sunardı; çarpanlara ayırma bu ifade gücünün bir kısmını maliyet karşılığında bırakıyor.
  • Aritmetik. \(Q\) ve \(K\) hazırsa çift çift skorlama \(O(n^2 d_k)\) tutar; \(M\) üzerinden skorlama \(O(n^2 d)\) — aynı sayılar için \(d/d_k = 32\) kat fazla ödersiniz. (Buna izdüşümlerin kendisi eklenir: \(Q\) ve \(K\) için \(O(n d\,d_k)\), \(XM\) için \(O(n d^2)\).)
  • Önbellek genişliği. Tasarımı asıl belirleyen gerekçe budur. \(M\) ile de önbelleğe alabilirsiniz — \(x_i^{\top} M x_j = (M^{\top} x_i) \cdot x_j\) olduğu için doğrudan \(x_j\)’yi saklarsınız — ama sakladığınız şey tam \(d\) genişliğinde kalmak zorundadır. Çarpanlara ayırma, key’leri kendilerine ait \(d_k\) genişliğinde bir uzaya taşır ve oraya yerleştikleri anda o uzayı küçültebilirsiniz: her key head’ini dört query head’ine hizmet ettirmek (32 yerine 8 key head), önbellekteki key’leri her katmanda token başına \(4096\) sayıdan \(1024\)’e indirir. Bu numaranın adı GQA’dır (grouped-query attention); Bölüm 7.6 bu konuyu ele alır. Ayrı bir \(W^K\), bu dar ve paylaşılan önbelleği doğal ve verimli kılıyor; tek bir tam \(M\) ile aynı düzeni kurmanın doğrudan bir yolu yok.

Aynı tanımlanamazlık (non-identifiability: verinin iki çarpanı ayrı ayrı belirleyememesi) value yolunda da geçerlidir: bir head’in çıktısı residual akışa \(x W^V W^O_{\text{head}}\) olarak girer; bu da yalnızca birlikte görünen iki matrisin bir başka çarpımıdır. Çarpanlara ayırma, rank ve maliyetle ilgili bir mühendislik kararıdır; bu karar aynı gerekçelerle iki kez alınmıştır.

Ortaya çıkan mekanizma, zaten bildiğiniz bir şeyin yumuşak bir sürümüdür:

Bir sözlük tek anahtarı arar; attention hepsini birden arar
SÖZLÜK — TAM EŞLEŞME, TEK DEĞER ANAHTAR DEĞER "sütü" kedi sütü içti cat milk drank milk Üç anahtardan ikisi tamamen yok sayılır. Anahtarı ıskalarsanız hiçbir cevap alamazsınız. ATTENTION — KISMİ EŞLEŞME, KARMA DEĞER ANAHTAR AĞIRLIK DEĞER q(içti) k(kedi) k(sütü) k(içti) 0,4230 0,5012 0,0759 v(kedi) v(sütü) v(içti) Her anahtara danışılır ve her değer bir şeyler katar. Hiçbir şey yok sayılmaz; yalnızca ağırlıklandırılır. Aynı üç parça — query, key, value — ama eşleşme kesin değil, dereceli; bu yüzden cevap bütün value'ların bir harmanı oluyor. Tek değişiklik bu ve attention'ı türevlenebilir kılan da bu: “hangi anahtar eşleşti” üzerinden gradyan alamazsınız, ama 0,5012 değerindeki bir ağırlık üzerinden alabilirsiniz.

Attention, türevlenebilir bir sözlüktür (Python’daki dict gibi). Sağdaki ağırlıklar, kuracağımız head’in (Bölüm 7.2) içti için ürettiği gerçek ağırlıklardır.

Bu son nokta ayrıca üzerinde durmayı hak ediyor, çünkü daha basit bir normalleştirme yerine neden softmax kullanıldığını açıklıyor. Sert bir arama, yani en iyi eşleşen tek anahtarı almak, bir basamak fonksiyonudur; basamak fonksiyonlarının gradyanı ise neredeyse her yerde sıfırdır, dolayısıyla standart geri yayılımla ondan önce gelen hiçbir parametre öğrenemezdi (ayrık seçimleri eğitmenin başka yolları var, ama hepsi daha dolambaçlı). Softmax, modeli eğitebilmenin bedelidir: “en iyi eşleşmeyi seç” davranışını özünde korur, ama her skora göre kullanılabilir bir türev bırakır.

7.1.3 Keşif: bir head neye bakıyor?

Eğitilmiş modellerde farklı attention head’lerinin farklı, kimi zaman yorumlanabilir örüntüler gösterdiği bilinir. Aşağıdaki widget ise eğitilmiş bir modelden çıkarılmadı: tek bir cümle için, üç olası örüntüyü karşılaştırmak üzere elle kurulmuş şematik bir attention matrisi. Matris maskesiz (iki yönlü): zamir head’inde it satırı kendinden sonra gelen ready’ye de küçük bir ağırlık veriyor; GPT tarzı nedensel bir modelde o hücre sıfır olurdu (Bölüm 7.5). Her satır bakan token’dır; her sütun bakılan token’dır; koyu renk daha çok ağırlık demektir. Head’ler arasında geçiş yapın ve örüntünün nasıl değiştiğini izleyin — özellikle it satırını.

Attention head aracı — “The robot poured coffee because it was ready.”

Zamir head’indeki belirsizlik gerçektir ve klasik Winograd kurgusudur. Şu iki cümle arasında yalnızca cümlenin sonu değişir, ama it’in gönderimi bir isimden diğerine kayar:

  • “The robot poured coffee because it was ready.” → it, kahveye meyleder.
  • “The robot poured coffee because it was instructed to.” → it, robottur.

Bunlardan ilki bir meyil, kesinlik değil — robot da hazır olabilir; yukarıdaki head’in kesin karar vermek yerine ağırlığı 0,46/0,34 şeklinde bölmesi de bu yüzden. Şemanın kanonik hâli bu belirsizlik payını ortadan kaldırır: the trophy does not fit in the suitcase because it is too big (kupa) ile too small (valiz) (Levesque vd. 2012). Her iki durumda da mesele aynı: sözdiziminde çifti ayıran hiçbir şey yok, dilbilgisi birebir aynı. Beklenen okumayı dünya bilgisi belirler; büyük ölçekli ön eğitimin kazandırmaya çalıştığı şeylerden biri de bu (gerçek metinde bu bilgi bile her zaman tek bir okumaya götürmez).

Türkçede aynı belirsizlik zamir hiç görünmeden yaşanır: “Robot kahveyi döktü, çünkü hazırdı” cümlesinde özne düşmüştür ve bakılacak bir it token’ı yoktur. Gönderimi çözmek için model, “hazırdı” fiilinden hem robot hem kahve token’larına bakmak zorundadır; bağlantıyı zamir değil fiil taşır.

7.2 Tek katman, üç token, bütün sayılar

O denklem dört sembol genişliğinde ve beş ayrı adımı gizliyor. Ondan korkmayı bırakmanın en hızlı yolu, kalemle kontrol edilebilecek kadar küçük sayılar üzerinde onu bir kez baştan sona çalıştırmaktır.

Cümlemiz Türkçe: kedi sütü içti. Bu cümle özellikle seçildi, çünkü Türkçe fiili sona koyar; dolayısıyla içti, ihtiyaç duyduğu iki şey için de geriye uzanmak zorundadır — içme eylemini kim yaptı ve ne içildi. Bu da aradığımız örüntüyü daha hesaba girmeden ifade etmemizi kolaylaştırıyor: attention makul bir şey yapıyorsa, içti satırı kendisine değil diğer iki token’a yaslanmalı. Matrisleri de tam bunu sağlayacak biçimde biz seçeceğiz: aşağıdaki hesap, bir head’in bu örüntüyü nasıl temsil edebileceğini gösteriyor; bir modelin özne ve nesne ilişkisini eğitimden öğrendiğini göstermiyor.

Her şey bilinçli olarak minik tutuldu. Üç token, \(d_{\text{model}} = 4\) model genişliği ve \(d_k = d_v = 2\) head boyutu (head dimension).

Bu matrisler öğrenilmedi, elle kuruldu. Aritmetik okunaklı kalsın ve her boyut adını koyabileceğiniz bir anlam taşısın diye seçildiler. Eğitilmiş bir model, daha geniş, daha yoğun ve sütun sütun yorumlanamayan matrisler bulur. Bu örnekten aklınızda kalması gereken yordam ve şekiller; bu belirli değerler değil.

Dört embedding boyutuna anlam yüklüyoruz ki “bu sayı neden büyük?” sorusunun her zaman bir cevabı olsun:

token anlamı canlı sıvı eylem sabit
kedi kedi 0,9 0,1 0,0 0,5
sütü süt (nesne) 0,1 0,9 0,0 0,5
içti içti 0,0 0,1 0,9 0,5

Dördüncü boyut üç token’da da aynı; bu yüzden ona sabit (hep açık) boyut diyoruz. Başına ne geldiğini izleyin: hiç değişmediği için bir token’ın kendisini ayırt eden herhangi bir şeyi ilan etmesine yardım edemez ve aşağıdaki bütün karşılaştırmalarda sadeleşip gider. Bu örneğin sonunda geri dönüp “sabit bir öznitelik hiçbir işe yaramaz” iddiasının gerçekten doğru olup olmadığını soracağız.

7.2.1 1. adım — her token’ı üç yöne izdüşür

Aynı girdi vektörü, bir query, bir key ve bir value üretmek için üç farklı öğrenilmiş matrisle çarpılır. Bu head’in öğrenilmiş parametreleri attention hesabına yalnızca burada girer (bloğun geri kalanında çıktı izdüşümü, normalleştirme ve feed-forward katmanının parametreleri ayrıca var).

\[ Q = XW^Q, \qquad K = XW^K, \qquad V = XW^V \]

Boyutları gözünüzün önünde tutun. \(X\)’te her token için bir satır vardır; yani \(X\), \(n \times d_{\text{model}}\) boyutundadır (burada \(3 \times 4\)). \(W^Q\) ve \(W^K\) matrisleri \(d_{\text{model}} \times d_k\), \(W^V\) ise \(d_{\text{model}} \times d_v\) boyutundadır; genel durumda \(d_v\), \(d_k\)’dan farklı olabilir. Burada ikisi de 2 (yani \(4 \times 2\)); dolayısıyla \(Q\), \(K\) ve \(V\) \(n \times 2\) olur: yine token başına bir satır, ama artık yalnızca iki sayı genişliğinde.

Üç matris üç farklı soruyu kodlar. \(W^Q\) ne arıyorum, \(W^K\) neyi ilan ediyorum, \(W^V\) ise seçilirsem ne teslim ederim sorusunun cevabını kurar:

\[ W^Q=\begin{bmatrix}2&0\\0&2\\2&2\\0&0\end{bmatrix}\; W^K=\begin{bmatrix}1{,}5&0\\0&1{,}5\\0&0\\0&0\end{bmatrix}\; W^V=\begin{bmatrix}1&0\\0&1\\0&0\\0{,}2&0{,}2\end{bmatrix} \]

\(W^Q\)’yu satır satır okuyun. Eylem satırı [2, 2]; yani fiil benzeri her şey, hem bir özne hem de bir nesne arayan bir query üretir. \(W^K\)’da ise eylem satırı [0, 0]: fiil bunların hiçbirini ilan etmez, dolayısıyla hiçbir şey ona bakmak istemeyecektir. Çarpımları yapınca:

token query \(q_i\) key \(k_i\) value \(v_i\)
kedi [1.80, 0.20] [1.35, 0.15] [1.00, 0.20]
sütü [0.20, 1.80] [0.15, 1.35] [0.20, 1.00]
içti [1.80, 2.00] [0.00, 0.15] [0.10, 0.20]

İki satır özellikle önemli. Her iki koordinatta da büyük olan tek query içtiye ait — aynı anda iki şeyin peşinde. Bir de içtinin value’su [0.10, 0.20], yani neredeyse hiç: fiilin kendine ait ne özne içeriği var ne de nesne içeriği. Bunu aklınızda tutun; işin can alıcı noktası bu.

7.2.2 2. adım — her query her key ile buluşur

\(i\) token’ını \(j\) token’ına karşı bir iç çarpımla, \(q_i \cdot k_j\) ile skorlayın. Dokuz token çifti, dokuz sayı; öyle dizilmişler ki her satır bakan token, her sütun ise bakılan token olsun:

\[ QK^{\top} = \begin{array}{r|rrr} & \texttt{kedi} & \texttt{sütü} & \texttt{içti}\\\hline \texttt{kedi} & 2{,}46 & 0{,}54 & 0{,}03\\ \texttt{sütü} & 0{,}54 & 2{,}46 & 0{,}27\\ \texttt{içti} & 2{,}73 & 2{,}97 & 0{,}30 \end{array} \]

İç çarpım, iki vektör aynı yöne baktığında büyük olur; yani bu sayılar birer benzerlik skorudur, daha egzotik bir şey değil. Tablodaki en büyük girdi \(2{,}97\) ve bu, içtinin sütüye bakışı: elle seçtiğimiz izdüşümler, fiil–nesne çiftine en yüksek skoru veriyor. içti satırındaki en küçük girdi olan \(0{,}30\) ise fiilin kendisine bakışı — tasarlandığı gibi, çünkü fiiller hiçbir şey ilan etmiyor.

7.2.3 3. adım — head boyutunun kareköküne böl

Neden bölüyoruz? Çünkü iç çarpımlar boyutla büyür ve büyük sayılar verilen bir softmax neredeyse one-hot (tek elemanı 1, gerisi 0) olur — tek bir token neredeyse bütün ağırlığı alır ve geri kalanların üzerinden geçen gradyan çok küçülür. \(q\) ve \(k\)’nın bileşenleri bağımsız, sıfır ortalamalı ve birim varyanslıysa bölmek skorların ölçeğini head genişliğinden bağımsız kılar ve bu doyumu azaltır; eğitilmiş bir head’in vektörleri bu varsayıma uymayabilir, yani bölme doyumu her durumda engellemez.

\[ \frac{QK^{\top}}{\sqrt{d_k}} = \frac{QK^{\top}}{\sqrt{2}} = \frac{QK^{\top}}{1{,}4142} \]

\[ \begin{array}{r|rrr} & \texttt{kedi} & \texttt{sütü} & \texttt{içti}\\\hline \texttt{kedi} & 1{,}7395 & 0{,}3818 & 0{,}0212\\ \texttt{sütü} & 0{,}3818 & 1{,}7395 & 0{,}1909\\ \texttt{içti} & 1{,}9304 & 2{,}1001 & 0{,}2121 \end{array} \]

Bu adım kılı kırk yaran bir hesap ayrıntısı gibi görünür, ama değildir. Rastgele iki \(d_k\) boyutlu vektörün (bileşenleri bağımsız, sıfır ortalamalı ve birim varyanslı) iç çarpımının standart sapması \(\sqrt{d_k}\) ile orantılı büyür; yani bölme olmadan head’ler genişledikçe skorlar sistematik olarak büyür — ve softmax mutlak büyüklüğe tepki verir. Bileşenleri bağımsız standart normal olan 20.000 rastgele çift üzerinde ölçtük (eğitilmiş bir head değil; varsayımın kendisini sınıyor):

\(d_k\) \(q\cdot k\)’nın std. sapması \(\sqrt{d_k}\) \(\sqrt{d_k}\)’ya bölündükten sonra
2 1,414 1,414 1,000
8 2,859 2,828 1,011
64 8,029 8,000 1,004
512 22,746 22,627 1,005

Ortadaki iki sütun yaklaşık %1 hassasiyetle uyuşuyor — \(d_k = 8\)’de %1,10, daha geniş head’lerde %0,6’nın altında. Kalan fark, 20.000 çiftlik bir kestirimin örnekleme gürültüsü, sistematik bir sapma değil. Bütün mesele de bu: ham skorların ölçülen yayılımı gerçekten \(\sqrt{d_k}\) ile orantılı büyüyor, dolayısıyla \(\sqrt{d_k}\)’ya bölmek onu gerçekten sabit bir değere indiriyor; son sütun bunu 256 katlık bir genişlik aralığında doğruluyor. Bunun sonucu, 64 rastgele key tek bir query için yarıştığında ortaya çıkıyor — aşağıda kazanan key’in aldığı ortalama ağırlık var:

\(d_k\) \(1/\sqrt{d_k}\) ile onsuz
2 0,104 0,165
8 0,105 0,420
64 0,108 0,786
512 0,107 0,928

Ölçekleme olmadan 512 boyutlu bir head, ağırlığının %93’ünü tek bir token’a veriyor — üstelik daha hiçbir şey öğrenmemişken. Bu, doyuma ulaşmış bir softmax’tır ve doymuş bir softmax’ın skorlara göre türevleri çok küçüktür; yani head’e nereye bakması gerektiğini öğretmek çok zorlaşır. Ölçekleme varken yarışma, head ne kadar geniş olursa olsun aşağı yukarı aynı sıcaklıkta kalır. Ölçeklenmiş sütun neredeyse hiç kıpırdamıyor: 0,104 · 0,105 · 0,108 · 0,107.

7.2.4 4. adım — softmax skorları ağırlığa çevirir

Softmax her satırın üstelini alır ve satırın toplamına böler; böylece her satır negatif olmayan değerlerden oluşur ve toplamı bire eşitlenir. içti satırı için baştan sona açalım:

\[ \begin{aligned} \text{ölçekli skorlar} &= [\,1{,}9304,\; 2{,}1001,\; 0{,}2121\,]\\ \exp(\cdot) &= [\,6{,}8923,\; 8{,}1670,\; 1{,}2363\,]\\ \textstyle\sum \exp(\cdot) &= 16{,}2956\\ \text{ağırlıklar} &= [\,6{,}8923,\; 8{,}1670,\; 1{,}2363\,]/16{,}2956\\ &= [\,0{,}4230,\; 0{,}5012,\; 0{,}0759\,] \end{aligned} \]

Aynı işlemi üç satıra da uygulayınca attention matrisi çıkar:

\[ A = \operatorname{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right) = \begin{array}{r|rrr} & \texttt{kedi} & \texttt{sütü} & \texttt{içti}\\\hline \texttt{kedi} & \mathbf{0{,}6961} & 0{,}1791 & 0{,}1249\\ \texttt{sütü} & 0{,}1750 & \mathbf{0{,}6804} & 0{,}1446\\ \texttt{içti} & 0{,}4230 & \mathbf{0{,}5012} & 0{,}0759 \end{array} \]

Her satırın toplamı 1,000. İki içerik kelimesinin her biri, attention’ının yaklaşık %70’ini kendisine harcıyor. Fiil ise başka bir şey yapıyor: attention’ını %42 / %50 oranında öznesi ile nesnesi arasında bölüştürüyor ve kendisine yalnızca %7,6 ayırıyor. Matrisleri seçerken hedeflediğimiz örüntü bu; eğitilmiş bir modelde isimlerin hep kendine bakacağını göstermiyor.

7.2.5 5. adım — ağırlıklı ortalama

Son olarak ağırlıkları value’larla çarpın. Burada yeni bir şey olmuyor; bu, ağırlıkları 4. adımdan gelen sıradan bir ağırlıklı ortalama.

\[ \operatorname{Attention}(Q,K,V) = AV \]

Boyutlar döngüyü kapatır: \(A\), \(n \times n\) boyutundadır (her token için bir ağırlık satırı), \(V\) ise \(n \times d_v\); dolayısıyla \(AV\) de \(n \times d_v\) olur — token başına bir çıktı vektörü, başladığımız satır sayısının aynısı.

token attention öncesi value attention sonrası çıktı
kedi [1.00, 0.20] [0.7444, 0.3433]
sütü [0.20, 1.00] [0.3256, 0.7443]
içti [0.10, 0.20] [0.5308, 0.6009]

Son satıra bakın, çünkü bütün mekanizmanın var olma sebebi o. içti elinde [0.10, 0.20] ile geldi — neredeyse hiç özne içeriği, neredeyse hiç nesne içeriği yok. Elinde [0.5308, 0.6009] ile ayrılıyor. Fiil iki argümanının içeriğini kendi konumuna topladı: sordu ve key’leri eşleşen iki token cevap verdi. Bu örnekte o eşleşmeyi matrisleri seçerek biz kurduk; eğitilmiş bir model aynı şeyi ancak veriden öğrenirse yapar. Bu çıktı ardından \(W^O\) ile dönüştürülüp residual akışa ekleniyor; bir sonraki katman 3. konuma baktığında gördüğü şey o toplam.

İki isim daha az hareket etti, üstelik beklediğiniz yönde: kedi, [1.00, 0.20] değerinden [0.74, 0.34] değerine geçti; kendi kimliğinin çoğunu korurken artık ilişkilendirildiği nesneden biraz aldı.

Yukarıdaki her adım ya bir matris çarpımı ya da bir softmax. Konumlar üzerinde bir döngü yok, tekrarlama yok ve sırayla olması gereken hiçbir şey yok — attention’ın tekrarlayan modellerin (Bölüm 6) yerini almasının sebebi de budur. Dokuz skorun hepsi aynı anda hesaplanabilir; hem de aynı anda çok sayıda şey hesaplamakta çok iyi olan bir donanım üzerinde. Bu paralellik, konumları bilinen bir metin için geçerli: eğitimde ve bir istemi okurken. Metin üretirken her yeni token yine bir öncekini bekliyor.

7.2.6 Kendiniz deneyin

Widget, yukarıdaki aritmetiği canlı olarak çalıştırıyor. Hesabı aşama aşama ilerletin; iki anahtar, mekanizmayı bilerek bozup ne olduğunu izlemenize izin veriyor.

Tek bir attention katmanı, aşama aşama — “kedi sütü içti”

Denemeniz gereken iki şey var. Nedensel maskeyi açın ve matrisin sağ üst köşesinin sıfıra gidişini izleyin — kedi artık yalnızca kendisini görebilir; bir modelin metni soldan sağa üretebilmesini sağlayan kısıt da budur (Bölüm 7.5). √dₖ’ya böl anahtarını kapatın ve bütün ağırlıkların uç değerlere kayışını izleyin. \(d_k=2\)’de etki hafiftir; sorun ancak geniş head’lerde ciddileşir ve bu widget onu göstermiyor — geniş head’deki etki için 3. adımdaki rastgele vektör deneyine bakın. Vaswani vd.’nin (2017) \(d_k = 64\) ile çalışırken ölçeklemeyi en baştan eklemesinin sebebi de budur.

7.2.7 Aynı resmin geometrisi

\(d_k = 2\) seçtiğimiz için her query ve her key düzlemde bir noktadır ve iç çarpım gözle görülebilir bir şeye dönüşür. İç çarpım \(q \cdot k = \|q\|\,\|k\|\cos\theta\) olduğu için hem açıya hem uzunluklara bağlı: bir query, iki ok benzer yöne baktığında ve ikisi de uzun olduğunda bir key’e karşı yüksek skor alır; aynı yöne bakan ama kısa bir key düşük kalabilir.

Query'ler ve key'ler aynı 2 boyutlu uzayda yaşar

içtiyi seçin. Query’si (kırmızı) çapraz olarak sağ yukarı bakıyor, bu yüzden hem kedinin key’iyle hem de sütünün key’iyle büyük bir iç çarpımı var. kediyi seçin; query yatay eksene doğru dönüyor ve kendi key’inden başka hiçbir şeyle hizalanmıyor. Attention örüntüsü bir arama tablosu değildir — her token için tekrarlanan bu geometridir. (Eksenlere “öznelik” ve “nesnelik” adını koyabilmemiz, matrisleri elle seçtiğimiz için; eğitilmiş head’lerin eksenleri genellikle böyle tek kelimelik yorumlara ayrılmaz.)

7.3 Multi-head attention ve transformer bloğu

Az önce kurduğumuz head’in dil hakkında tek bir fikri var — bana bir özne ve bir nesne bul — ve bu fikri anlamlı olsun olmasın her token’a uyguluyor. Yalnızca bunu yapabilen bir model çok dar bir alet olurdu.

Çözüm şaşırtıcı derecede basit: aynı girdi üzerinde, her birinin kendi \(W^Q\), \(W^K\), \(W^V\) matrisleri olan birkaç head’i (baş) yan yana çalıştırın ve her birinin farklı ilişkiler temsil edebilmesine izin verin. Bu düzenin adı multi-head attention’dır (çok başlı dikkat). Eğitilmiş bir modelde bir head’in tek bir işe indirgenebileceğini varsaymayın: örüntüsü bağlama göre değişebilir (aşağıdaki kutuya bakın).

7.3.1 İkinci bir head, aynı cümle

kedi sütü içti ile ilgili her şeyi olduğu gibi bırakıp farklı bir işi olan bir head daha ekleyelim. 1. head argümanlarım kimler diye soruyor. 2. head ise tam tersini soruyor — beni hangi fiil yönetiyor; fiilin sona geldiği bir dilde bu, iki ismin de ileriye, içtiye işaret etmesi demektir. Bu yüzden bu head maskesiz (iki yönlü) bir örnek: GPT tarzı nedensel bir modelde kedi ve sütü, kendilerinden sonra gelen içtiyi göremezdi:

\[ W^{Q}_{2}=\begin{bmatrix}2{,}5&0\\2{,}5&0\\0&2{,}5\\0&0\end{bmatrix}\; W^{K}_{2}=\begin{bmatrix}0&1{,}6\\0&1{,}6\\2&0\\0&0\end{bmatrix}\; W^{V}_{2}=\begin{bmatrix}0&0{,}6\\0&0{,}6\\1&0\\0&0\end{bmatrix} \]

Query boyutları artık bir eylem arıyorum ve bir isim arıyorum anlamına geliyor; key boyutları ise ben bir eylemim ve ben bir isimim demek. Aynı beş adımı çalıştırınca tamamen farklı bir matris çıkıyor:

1. head — argüman yapısı

kedi sütü içti
kedi 0,6961 0,1791 0,1249
sütü 0,1750 0,6804 0,1446
içti 0,4230 0,5012 0,0759

2. head — beni hangi fiil yönetiyor

kedi sütü içti
kedi 0,0383 0,0383 0,9234
sütü 0,0383 0,0383 0,9234
içti 0,4675 0,4675 0,0650

İki head neredeyse her satırda anlaşmazlığa düşüyor ve bütün mesele de bu anlaşmazlık. 1. head’de kedi çoğunlukla kendisine bakıyor. 2. head’de ise kedi attention’ının %92’sini fiile harcıyor. İki head’i de biz kurduk; ikisi aynı üç kelimeden iki farklı ilişkiyi okuyacak biçimde tasarlandı. Gösterdikleri, birden fazla head’in farklı bilgi akışlarını ifade edebildiği; hangisinin dilbilgisel olarak doğru olduğu ayrı bir soru.

Gerçek head’ler bu kadar derli toplu değildir. Eğitilmiş modeller üzerine yayımlanan çözümlemeler, tanınabilir işleri olan head’ler buluyor — bir önceki token’a, eşleşen paranteze, bir öbeğin sözdizimsel başına bakanlar (Clark vd. 2019) — ama head’lerin çoğu tek cümlelik hiçbir tarife sığmıyor, birçoğu birbirini gereksiz yere tekrarlıyor ve büyük bir kısmı çok az kayıpla budanabiliyor (Michel vd. 2019). “2. head fiili buluyor” ifadesini, eğitilmiş head’lerin ne yaptığına dair bir iddia olarak değil, bu elle kurulmuş örneğin tarifi olarak alın.

7.3.2 Önce birleştir, sonra izdüşür

Her head, token başına \(d_v\) genişliğinde bir vektör üretir. Bunları yan yana dizin ve sonucu bir matristen daha, \(W^O\)’dan geçirin:

\[ \operatorname{MultiHead}(X) = \operatorname{Concat}(\text{head}_1, \dots, \text{head}_h)\, W^O \]

Head'lere böl, paralel çalıştır, sonuçları geri yapıştır
X n × d HEAD 1 — ARGÜMAN YAPISI kendi W_Q, W_K, W_V'si içti → 0,4230 / 0,5012 / 0,0759 n × d/h HEAD 2 — BENİ YÖNETEN FİİL farklı W_Q, W_K, W_V içti → 0,4675 / 0,4675 / 0,0650 n × d/h Birleştir n × d W_O d × d çıktı n × d Her head yalnızca d/h genişliğinde olduğu için h head, tam genişlikte tek bir head kadar ağırlık tutar: h ne olursa olsun izdüşümler için 3d², W_O için d². Büyüyen şey skor tensörü, h·n².

kedi sütü içti üzerinde iki head. Gösterilen satırlar, her head’in içti için aşağıda hesaplanan gerçek attention ağırlıklarıdır — aynı token, aynı anda iki farklı biçimde okunuyor.

Genişliği 2 olan iki head ile birleştirme tam olarak 4 genişliğinde oluyor — yani \(d_{\text{model}}\)’e geri döndük ve bu bir rastlantı değil. Klasik düzende head boyutu, birleştirdiğinizde başladığınız yere dönesiniz diye \(d_k = d_{\text{model}} / h\) seçilir. Bu bir zorunluluk değil, bir alışkanlık: genel durumda birleştirilmiş \(h\,d_v\) genişliğindeki vektörü \(W^O\) zaten \(d_{\text{model}}\)’e izdüşürür.

token h1 özne h1 nesne h2 eylem h2 isim
kedi 0,7444 0,3433 0,8310 0,1014
sütü 0,3256 0,7443 0,8310 0,1014
içti 0,5308 0,6009 0,0585 0,5649

Sonra \(W^O\), her head’in raporunun ne kadarının modelin çalışma belleğine geri yazılacağına karar verir. Bizimki, 1. head’in özne ve nesne içeriğini canlı ve sıvı boyutlarına, 2. head’in eylem içeriğini eylem boyutuna, 2. head’in isim içeriğinin beşte birini (0,2) de hem canlı hem sıvı boyutlarına yönlendiriyor — aşağıda kedinin canlı değerinin 1. head’in verdiği 0,7444 değil de 0,7646 çıkmasının nedeni bu. Son sütunu tamamen sıfır olduğu için dördüncü boyuta hiçbir şey yazılmıyor:

\[ W^O=\begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0&1&0\\0{,}2&0{,}2&0&0\end{bmatrix} \qquad\Longrightarrow\qquad \begin{array}{r|rrrr} & \text{canlı} & \text{sıvı} & \text{eylem} & \text{sabit}\\\hline \texttt{kedi} & 0{,}7646 & 0{,}3635 & 0{,}8310 & 0{,}0000\\ \texttt{sütü} & 0{,}3458 & 0{,}7646 & 0{,}8310 & 0{,}0000\\ \texttt{içti} & 0{,}6438 & 0{,}7139 & 0{,}0585 & 0{,}0000 \end{array} \]

Multi-head attention neredeyse hiç fazladan maliyet getirmez. \(d_{\text{model}}/h\) genişliğinde \(h\) head’in toplam parametre sayısı ve matris çarpımı FLOP sayısı (FLOP: tek bir kayan noktalı işlem, yani bir çarpma ya da bir toplama), \(d_{\text{model}}\) genişliğinde tek bir head ile aynıdır: \(W^Q\), \(W^K\), \(W^V\) izdüşümleri her iki durumda da \(3d^2\), \(W^O\) buna \(d^2\) ekler ve skorlama, \(h\) ne olursa olsun \(h \cdot n^2 \cdot (d/h) = n^2 d\) tutar. Head’lere bölmek, birbirinden bağımsız birkaç attention örüntüsünü neredeyse hiç ek parametre ve FLOP olmadan sağlar; tasarımın tutmasının sebebi de budur. Gerçek gecikme ise ayrı bir konu: \(h\) ayrı skor ve softmax matrisi, bellek trafiğini ve kullanılan kernel’i değiştirebilir.

Yine de iki maliyet kalemi değişir ve bunlar bölümün ilerleyen kısımlarında önem kazanacak:

  • Skor tensörü büyür. Skorları olduğu gibi bellekte kuran naif bir uygulamada tek head \(n^2\) skor saklar; \(h\) head \(h \cdot n^2\) saklar (FlashAttention gibi bloklu uygulamalar bu tam tensörü hiç kurmaz). \(n = 1024\), \(h = 32\) için bu, 1,05M yerine 33,5M girdi demek — hem bellekte (bkz. Bölüm 7.6) hem de matris çarpımı olmayan softmax işinde 32 kat artış.
  • Her head daralır. \(d/h\) genişliğindeki bir head, softmax öncesi skor matrisinin (\(QK^{\top}\)) rankını \(\le d/h\) olmaya zorlar. \(d = 4096\) için bu, head başına 4096 yerine 128 demektir. Sınırın nerede bittiğine dikkat: satır satır softmax bu sınırı korumaz. \(d_k = 1\) ve \(Q = K = [1, 2]^{\top}\) ile \(QK^{\top}\) matrisinin rankı 1’dir, softmax’tan geçmiş matrisin determinantı ise 0,1497, yani rankı 2. Dolayısıyla yaptığınız ödünleşim (trade-off), yüksek ranklı tek bir skor ilişkisini düşük ranklı 32 ilişkiyle değiştirmektir — genellikle iyi bir ödünleşim, ama her hâlükârda bir ödünleşim.

7.3.3 Kazanç, sayılarla

Şimdi attention çıktısını her token üzerindeki bir değişim olarak okuyun. Her token, tek başına ne olduğunu söyleyen bir vektörle işe başladı; attention alt katmanı ona cümlenin geri kalanından öğrendiklerini geri veriyor:

token başlangıç attention’ın eklediği okunuşu
içti canlı 0,0 · sıvı 0,1 canlı +0,6438 · sıvı +0,7139 fiil bir özne ve bir nesne edindi
kedi eylem 0,0 eylem +0,8310 isim, ait olduğu fiili buldu
sütü eylem 0,0 eylem +0,8310 diğeri de öyle

“Bağlamsal embedding” dediğimiz şeyin aritmetikteki ilk adımı budur. (Gerçek bir modelde bu çıktı residual, normalleştirme ve feed-forward’dan da geçiyor ve sayılar elle değil eğitimle bulunuyor.) içti fiili katmana yalnızca bir eylem olduğunu bilerek girdi; kim içti ve ne içildi bilgisinin sayısal izlerini taşıyarak çıkıyor. Statik (durağan) embedding’lerle bir transformer arasındaki fark budur: statik embedding’lerde (Bölüm 3) içtinin sonsuza dek tek bir vektörü vardır; transformer’da ise bu vektör her katmanda komşularından yeniden kurulur.

sabit sütunu her satırda tam olarak sıfır kazandı — bu, elimizdeki \(W^O\)’nun bir yan ürünü; bu matrisin son sütunu tamamen sıfır olduğu için o boyuta hiçbir şey geri gönderilmiyor.

Şimdi sabit boyuta söz verdiğimiz dönüşü yapalım, çünkü onunla ilgili derli toplu hikâyenin yalnızca üçte ikisi doğru. Skoru, sabit \(c\)’nin katkıda bulunduğu parçalara ayıralım; her token’ı \(x_i = [z_i, c]\) diye yazıyoruz:

\[ q_i \cdot k_j = \underbrace{z_i A \cdot z_j B}_{\text{asıl içerik}} \;+\; \underbrace{c\,(z_i A \cdot b)}_{\text{her } j \text{ için aynı}} \;+\; \underbrace{c\,(a \cdot z_j B)}_{\textbf{j ile değişir}} \;+\; \underbrace{c^2 (a \cdot b)}_{\text{her } j \text{ için aynı}} \]

Burada \(a\) ile \(b\), \(W^Q\) ve \(W^K\)’nın sabit özniteliği okuyan satırlarıdır. Softmax, bir satırın tamamına aynı sayıyı eklemeye karşı değişmezdir; dolayısıyla ikinci ve dördüncü terimler gerçekten yok olur. Üç sonuç çıkıyor ve bunlardan yalnızca ilk ikisi tahmin edebileceğiniz türden:

  • Key tarafında, tek bir head’in skorları için, işe yaramaz. Bir sabit hiçbir şey ilan edemez. \(b\) ne olursa olsun katkısı bir query satırındaki bütün \(j\)’ler için aynıdır ve satır softmax’ında sadeleşir.
  • Value tarafında bir bias (sabit terim) gibi davranır. Bizim \(W^V\)’miz ona 0,2 ağırlık veriyor, yani her value vektörünün iki bileşenine de sabit bir \(+0{,}1\) ekliyor.
  • Query tarafında ise hiç de işe yaramaz değildir. Üçüncü terim \(j\)’ye bağlıdır ve sadeleşmez. Bu, her token’ın ne olduğuna bakmaksızın istediği, key’ler üzerinde sabit bir tercihtir — öğrenilmiş küresel bir önsel. Bizim örneğimizde yok olmasının tek sebebi, bu \(W^Q\)’da o özniteliğe karşılık gelen satırın sıfır olması; o satırı \([0{,}9, -0{,}7]\) yaparsanız attention ağırlıkları 0,1592’ye kadar oynar.

Sabit bir öznitelik hiçbir soruyu cevaplayamaz, ama bir soru sorabilir. Üçüncü terim ilginç bir ayrıntıdan ibaret değil: bir modelin attention sink kurmasının olası yollarından biri — yani bakacak belirli bir şeyi olmadığında her token’ın arta kalan ağırlığını boşalttığı bir konum. Tek başına yetmez; key geometrisi, maske ve eğitimin seyri de rol oynar.

7.3.4 Residual bağlantılar: değiştirmek değil, eklemek

Tablodaki eklediği kelimesine dikkat edin. Alt katmanın çıktısı yeni temsil değildir; eskisinin üzerine toplanır:

\[ x \;\leftarrow\; x + \operatorname{MultiHead}(x) \]

Bu, residual connection’dır (artık bağlantı) ve tek satırlık tanımının ima ettiğinden çok daha fazla iş yapar. Her alt katman, ilerleyen bir vektörün üzerine yazmak yerine ona eklediği için, modelin derinliği boyunca akan bu vektör dizisine sıklıkla residual akış denir: her katmanın okuyup yazdığı ortak bir çalışma alanı. Katkı sunacak faydalı bir şeyi olmayan bir katman sıfıra yakın bir çıktı verip akışı olduğu gibi bırakabilir; yani bir transformer’a eklenen katmanın önünde hazır bir “zarar vermeme” seçeneği var. Eğitimin o seçeneği bulacağı garanti değil, ama onu bulmak kolay.

residual connection (artık bağlantı) — bir alt katmanın girdisini çıktısına ekleyen kestirme; böylece katman tamamen yeni bir temsil yerine yalnızca bir düzeltme öğrenmek zorunda kalır. ↩︎

Gradyana da yardım eder. \(x + f(x)\)’in \(x\)’e göre türevini alırsak \(1 + f'(x)\) elde ederiz (vektörlerde Jacobian \(I + J_f\)): kayıptan daha önceki her katmana geri giden ve türevi tam olarak 1 olan doğrudan bir yol var. Bu, toplam gradyanın korunacağı anlamına gelmiyor — diğer dalın katkısı onu küçültebilir — ama kaybolmasını çok zorlaştırıyor.

Residual bağlantılar çoğu zaman kaybolan gradyanların çaresi olarak tanıtılır. He vd. (2016), çözdükleri şeyin bu olmadığını açıkça söylüyor. Ağlarında zaten batch (yığın) normalleştirmesi vardı, yani gradyanların sağlıklı olduğu doğrulanmıştı. Adını koydukları sorun bozulmaydı (degradation): 56 katmanlı düz bir ağ, eğitim hatasında 20 katmanlı olandan daha kötü skor alıyordu. Daha sığ ağı taklit etmeyi öğrenemiyordu, çünkü bir katman yığınına özdeşlik dönüşümünü (identity mapping: girdiyi değiştirmeden çıkışa aktarmayı) hesaplatmak zordur — residual yol ise bu dönüşümü hazır olarak sunar. Daha iyi gradyan akışı gerçek bir yan etkidir, ama yöntemi ortaya çıkaran sorun bu değildi.

7.3.5 Katman normalleştirmesi ve neden öne alındığı

Residual akışlar büyür. Her alt katman bir şeyler eklediği için derinlik arttıkça sayıların ölçeği yukarı kayar; hem attention (softmax üzerinden) hem de feed-forward doğrusal olmayanlığı ölçeğe duyarlıdır. Layer normalization (katman normalleştirmesi) her token’ın vektörünü tek başına yeniden ölçekler (Ba vd. 2016) — batch boyunca değil, dizi boyunca da değil, o token’ın kendi \(d_{\text{model}}\) özniteliği boyunca:

\[ \operatorname{LN}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta, \qquad \mu = \frac{1}{d}\sum_j x_j, \qquad \sigma^2 = \frac{1}{d}\sum_j (x_j - \mu)^2 \]

layer normalization (katman normalleştirmesi) — her token’ın öznitelik vektörünü sıfır ortalama ve birim varyansa yeniden ölçeklemek, sonra öğrenilmiş bir \(\gamma\) ölçeği ve \(\beta\) kaydırması uygulamak; hesap, batch boyunca değil o token’ın kendi öznitelikleri boyunca yapılır. ↩︎

Bunu bloğun gerçek sırasında uygulayın. Bir pre-norm blokta feed-forward alt katmanından önceki normalleştirme \(x + \operatorname{Attn}(\cdot)\)’yi görür; yani alt katmanın çıktısını değil, residual toplamı normalleştirin. İlk olarak bir önceki kısımdaki toplamayı yapalım:

Bir basitleştirmeyi açıkça söyleyelim. Gerçek bir pre-norm blok, attention’dan önce de normalleştirir — çözümlü örnekte sayılar okunaklı kalsın diye ham embedding’ler doğrudan \(W^Q\), \(W^K\), \(W^V\)’ye verildi. Bundan sonraki bütün hesaplar bu tercihi devralıyor. Burada izlediğiniz normalleştirme, bloğun doğru konumundaki ikinci normalleştirmesidir.

token canlı sıvı eylem sabit
kedi 1,6646 0,4635 0,8310 0,5000
sütü 0,4458 1,6646 0,8310 0,5000
içti 0,6438 0,8139 0,9585 0,5000

ve ancak ondan sonra bu satırları \(\gamma = 1\), \(\beta = 0\) ile normalleştirelim:

token canlı sıvı eylem sabit
kedi 1,6543 −0,8299 −0,0699 −0,7545
sütü −0,8509 1,6508 −0,0602 −0,7397
içti −0,4932 0,4908 1,3268 −1,3244

Artık her satırın ortalaması 0, standart sapması 1. Bunun bedeline dikkat edin: normalleştirmeden sonra bir öznitelik yalnızca o token’ın kendi diğer özniteliklerine göre anlamlıdır. içti yukarıda bir hayli canlı içerik kazandı, ama daha da fazla sıvı ve eylem içeriği kazandı; bu yüzden canlı değeri negatif çıkıyor. Bu tabloda (\(\gamma = 1\), \(\beta = 0\)) mutlak büyüklükler gitti; ayakta kalan yalnızca profil. Gerçek bir modelde öğrenilmiş \(\gamma\), \(\beta\) ve normalleştirilmeden akan residual yol, ölçek bilgisinin bir kısmını yine taşıyabiliyor.

Layer norm’u batch norm’a tercih etmemizin sebebi, onun diğer örneklerden bu şekilde bağımsız olmasıdır. Batch normalleştirmesi istatistikleri batch boyunca hesaplar. Diziler farklı uzunlukta olduğunda dolgu (padding) ve maske gibi ek düzenekler ister, eğitimi batch’in bileşimine bağımlı kılar ve çıkarım için eğitim boyunca güncellenen istatistiklerin saklanmasını gerektirir. Layer norm’un ihtiyaç duyduğu tek şey bir token’ın kendi sayılarıdır; bu yüzden batch boyutu ister 512 ister 1 olsun, birebir aynı davranır.

Nereye konduğu önemlidir. Özgün Transformer, normalleştirmeyi residual toplamadan sonra koyuyordu: \(\operatorname{LN}(x + f(x))\) — post-norm. GPT-2’den beri standart, onu alt katmandan önce koymaktır: \(x + f(\operatorname{LN}(x))\) — pre-norm. Fark şu: pre-norm, residual yolu tamamen temiz bırakır; girdi embedding’inden son katmana kadar üzerinde hiçbir işlem bulunmayan, kesintisiz bir birim bağlantı vardır. Post-norm ise akışı her blokta yeniden ölçekler, dolayısıyla birim yol artık birim değildir; Xiong vd. (2020), inceledikleri derin post-norm modellerin başlangıçta büyük gradyanlar yüzünden bir öğrenme oranı ısınması (warmup: öğrenme oranını ilk adımlarda çok küçük bir değerden yavaşça artırmak) olmadan kararlı eğitilemediğini ve pre-norm’un bu ihtiyacı büyük ölçüde kaldırdığını gösterdi. Aşağıdaki her şey pre-norm’dur.

Pratikte ise çoğu zaman layer norm bile değil. GPT-2 ve BERT layer norm kullanır; bu bölümün adını andığı yeni açık ağırlıklı model aileleri — Llama, Mistral, Qwen, Gemma — ise RMSNorm kullanıyor (Zhang ve Sennrich 2019):

\[\operatorname{RMSNorm}(x) = \frac{x}{\sqrt{\tfrac{1}{d}\sum_j x_j^2 + \epsilon}} \odot \gamma\]

İki şey gitti: ortalama çıkarma ve \(\beta\) kaydırması. Kuramla değil ablasyon deneyleriyle (bir bileşeni çıkarıp etkisini ölçen deneyler) desteklenen iddia şu: işe yarayan kısım hiçbir zaman yeniden merkezleme değildi, yalnızca yeniden ölçeklemeydi. RMSNorm’un normalleştirme katmanındaki parametre sayısı layer norm’unkinin yarısı (\(\beta\) yok; modelin toplam parametresi açısından önemsiz bir fark); ortalamayı hesaplamadığı için de daha ucuz çalışıyor — yazarları, inceledikleri modellerde %7 ile %64 arasında daha kısa çalışma süreleri bildiriyor. Kendi modelinizdeki kazanç mimariye ve uygulamaya bağlı. Bir Llama yapılandırma dosyasında layer_norm_eps değil rms_norm_eps alanı bulunmasının nedeni de bu. Normalleştirmenin nereye konduğuna dair yukarıda söylenen her şey aynen geçerli; yalnızca kutunun içindeki işlev sadeleşiyor.

“Standart”, “evrensel” demek değil. Gemma 2, her alt katmandan hem önce hem de sonra normalleştiren sandwich norm kullanıyor: \(x + \operatorname{N}_2(f(\operatorname{N}_1(x)))\). OLMo 2 ise tersine gidip girdi norm’unu atıyor ve yalnızca bir çıkış norm’u tutuyor: \(x + \operatorname{N}(f(x))\). Her ikisi de büyük ölçekli eğitimde kararlılık için benimsendi — ve her ikisinde de normalleştirme hâlâ daldadır, yani birim yol kesintisiz kalır. Vazgeçilmeyen özellik budur. Belirli bir checkpoint’in (yayımlanmış model ağırlıkları) pre-norm olduğunu varsaymadan önce yapılandırmasına bakın.

7.3.6 Feed-forward alt katmanı

Attention, bilgiyi token’lar arasında taşır ve yapabildiği şey aşağı yukarı bundan ibarettir.

Nedenini net söyleyelim. Sık duyulan “attention doğrusaldır” açıklaması yanlıştır. Attention, \(x\)’in doğrusal bir fonksiyonu değildir — bir karesel formun softmax’ı hiç de doğrusal değildir.

Asıl sınır başka yerde. Ağırlıklar verildiğinde çıktı, value vektörlerinin dışbükey bir bileşimidir (ağırlıkları negatif olmayan ve toplamı 1 olan bir ağırlıklı ortalama): yalnızca kendisine verilen value’ların dışbükey örtüsünün (convex hull), yani onların “arasında” kalan bölgenin içindeki noktaları üretebilir. Yeniden ağırlıklandırıp harmanlayabilir; girdilerinin hiçbirinde bulunmayan yeni bir özniteliği hesaplayamaz. (Bu sınır o ara hesaba ait: \(W^O\) ve residual toplama temsili başka koordinatlara taşır; yine de içeriği doğrusal olmayan biçimde işleyen bir adım gerekir.)

Dolayısıyla bir yerde, vektörün içeriğine etki eden bir doğrusal olmayanlık olmalı; bir yerde de her token’a az önce aldığını kendi başına işleme fırsatı verilmeli.

İkinci alt katman işte budur — her konuma ayrı ayrı ve birebir aynı şekilde uygulanan iki katmanlı bir MLP:

\[ \operatorname{FFN}(x) = \sigma(x W_1 + b_1)\, W_2 + b_2, \qquad W_1 \in \mathbb{R}^{d \times d_{\text{ff}}}, \qquad W_2 \in \mathbb{R}^{d_{\text{ff}} \times d} \]

\(\sigma\) doğrusal olmayan işlevdir; hangi işlevin kullanıldığı iki kez değişti. Vaswani vd. (2017) ReLU kullanıyordu: \(\max(0, z)\). GPT ve BERT GELU’ya geçti (Hendrycks ve Gimpel 2016): \(z\,\Phi(z)\) — bir girdiyi, standart normalin onun altında kalma olasılığıyla ağırlıklandıran yumuşak bir ReLU; böylece küçük negatif değerler tam sıfıra kırpılmak yerine sızabiliyor. Llama’dan beri standart ise SwiGLU (Shazeer 2020); bu, noktasal bir işlev bile değil, bir kapı:

\[ \operatorname{FFN}_{\text{SwiGLU}}(x) = \big(\operatorname{Swish}(xW_1) \odot xW_3\big)W_2, \qquad \operatorname{Swish}(z) = z\,\sigma_{\text{lojistik}}(z) \]

Bir dal ne kadar geçireceğine, diğeri neyi geçireceğine karar veriyor — aşağıdaki tablodaki üçüncü matris buradan geliyor. Tablodaki SwiGLU modellerinin \(d_{\text{ff}}\) oranları 4’ten küçük olduğu hâlde parametre sayılarının daha büyük olmasının nedeni de bu. İlerideki çözümlü örnek ReLU kullanıyor, çünkü bir tablodan okuyabileceğiniz bir eşiğin ateşlendiğini izlemek, yumuşak bir eğriyi izlemekten kolay.

Ayrı ayrı ve birebir aynı şekilde ifadesi bütün yükü taşıyor: kedinin FFN hesabı sütüyü hiç görmez. Bir transformer’daki token’lar arası her etkileşim attention’da olur, başka hiçbir yerde değil. Uzun bir belgede iki varlığı birbirine karıştıran bir modelde hata ararken bunu hatırlamakta fayda var — karışma attention’dan geçmiş olmalı; ama tek bir attention haritasından değil, çoğu zaman birden fazla head ve katman boyunca taşınmış olabilir.

Neden \(d_{\text{ff}} = 4d\)?

Kısa cevap: Vaswani vd. (2017) 4’ü seçti, işe yaradı ve sonraki birkaç yılın en bilinen modelleri — BERT, GPT-2, GPT-3 — de onu korudu. Bir türetme yok. Ama bu şekli anlamlı kılan bir okuma var: FFN, bir key–value bellek gibi davranır (Geva vd. 2021). \(W_1\)’in \(d_{\text{ff}}\) sütununun her biri — aşağıda basılan \(W_1^{\top}\)’nin satırları — bir örüntü dedektörüdür ve \(W_2\)’nin eşleşen satırı, o dedektör ateşlendiğinde residual akışa yazılan şeydir. Bu okumaya göre \(d_{\text{ff}}\) kabaca bu katmanın kaç ayrı örüntüyü tutabileceğini belirler (her ara birim ayrı bir “bilgi” değil; bu, ağı yorumlamaya yarayan bir bakış açısı) ve önce genişleyip sonra daralan şekil de buna hizmet eder: elinizdeki boyut sayısından daha fazla ayrı örüntüyü tutmak için \(d\)’den geniş bir yere ihtiyacınız var.

Çevredeki tasarım değiştiğinde ne olduğunu izleyin, çünkü o “4”ün tamamen pazarlığa açık olduğu ortaya çıkıyor. Modern modeller tek \(W_1\)’i kapılı bir birimle — SwiGLU ile — değiştiriyor; bu birim iki yerine üç matris kullanıyor, dolayısıyla alt katman \(2\,d\,d_{\text{ff}}\) yerine \(3\,d\,d_{\text{ff}}\) tutuyor. Peki araştırmacılar bununla ne yaptı?

model \(d\) \(d_{\text{ff}}\) oran FFN parametreleri
GPT-2, GELU 768 3.072 4,00 \(8d^2\)
Llama 2 7B, SwiGLU 4.096 11.008 2,69 \(8{,}1d^2\)
Llama 3 8B, SwiGLU 4.096 14.336 3,50 \(10{,}5d^2\)
Llama 3 70B, SwiGLU 8.192 28.672 3,50 \(10{,}5d^2\)
Mistral 7B, SwiGLU 4.096 14.336 3,50 \(10{,}5d^2\)

Llama 2, \(\tfrac{8}{3}d \approx 2{,}67d\) seçti (256’nın katı olacak şekilde 11.008’e yuvarlanmış hâliyle); bu, üçüncü matrise rağmen parametre sayısını \(8d^2\)’de tutan genişlik. Llama 3 ve Mistral ise farklı bir parametre ve kalite dengesi seçip FFN’ye %31 daha fazla parametre harcadı. Yani ne 4 bir yasadır ne de \(8d^2\). Değişmeden kalan iki şey var: şekil — \(d\)’yi epeyce aşacak kadar genişlet, bir doğrusal olmayanlık uygula, geri daralt — ve bir modelin parametrelerinin çoğunun bu alt katmana gittiği gerçeği. Herhangi bir sabiti, birinin türettiği bir sonuç değil, birinin ayarladığı bir hiperparametre olarak görün.

Ateşlemeyi izlemek

\(d_{\text{ff}} = 8\) alalım (\(d=4\) olan gerçek bir model 16 kullanırdı; bu, sayfaya sığsın diye küçültüldü) ve \(W_1\)’in sütunlarını, her nöron tanınabilir bir dedektör olacak şekilde elle belirleyelim; bias da nöronun ateşleme eşiği olsun. Dört girdi boyutunu canlı, sıvı, eylem, sabit sırasıyla okuyoruz:

\[ W_1^{\top}=\begin{bmatrix} 1&0&1&0\\ 0&1&1&0\\ 1&1&0&0\\ 1&0&0&0\\ 0&1&0&0\\ 0&0&1&0\\ 0{,}5&0{,}5&0&0\\ 0&0&0&1 \end{bmatrix}, \qquad b_1=\begin{bmatrix}-0{,}9\\-0{,}9\\-0{,}9\\-0{,}5\\-0{,}5\\-0{,}5\\-0{,}5\\-0{,}5\end{bmatrix} \]

Her satır bir soru, her bias ise nöronun cevap vermeden önce ne kadar ikna olması gerektiğidir. Bu katmana bir önceki kısmın normalleştirilmiş vektörlerini verelim:

dedektör \(W_1\)’deki sütunu ne soruyor kedi sütü içti
canlı + eylem bu, bir fiile bağlı canlı bir şey mi? 0,6845 0 0
sıvı + eylem bu, bir fiile bağlı bir sıvı mı? 0 0,6906 0,9176
canlı + sıvı bu aynı anda hem canlı hem de sıvı mı? 0 0 0
yalnızca canlı bu canlı mı, fiil olsun olmasın? 1,1543 0 0
yalnızca sıvı bu bir sıvı mı, fiil olsun olmasın? 0 1,1508 0
yalnızca eylem bu bir eylem mi? 0 0 0,8268
herhangi bir isim bu zayıfça canlı ya da sıvı mı? 0 0 0
sabit boyut sabit boyut büyük mü? 0 0 0

Sütunları okuyun. kedi, canlı + eylem dedektörünü ateşliyor — attention’dan sonra artık sadece canlı bir şey değil, bir fiile bağlı canlı bir şey. (Bu okunuş, sütunlara bizim verdiğimiz etiketlerden geliyor; eğitilmiş bir nöronun tek bir dilbilgisel kavrama karşılık geldiğini göstermiyor.) içti ise nesnesini soğurduğu için sıvı + eylem’i ateşliyor. ReLU, eşiğin altındaki her şeyi sıfırlar; doğrusal bir katman bir dizi açma-kapama anahtarına işte böyle dönüşür.

Şimdi ilginç işi hangi satırların yaptığına bakın; hepsi yapmıyor. Tek öznitelikli üç dedektör ham embedding’lerde de ateşlenirdi — size attention’ın zaten bilmediği hiçbir şey söylemiyorlar.

İlginç olanlar bileşik dedektörlerdir, özellikle de iki isim için: attention çalışmadan önce kedi ile sütünün eylem içeriği tam olarak 0,0’dı, yani hiçbir şey “canlı ve bir fiile bağlı” sorusunu cevaplayamazdı. Çünkü sorunun yarısı henüz ortada yoktu.

(içti de kendine ait biraz sıvı içeriğiyle başlamıştı, ama bu yetmezdi: ham embedding’i aynı normalleştirmeden geçirseydik sıvı + eylem dedektörünün ön aktivasyonu yaklaşık −0,20 olur, yani eşiğin altında kalırdı. Attention’dan sonra bu değer 0,9176.)

Katmanın asıl amacı şu: FFN, attention’ın bir alt katman önce yarattığı öznitelikleri okuyor; iki alt katmanın dönüşümlü olmasının bütün sebebi de bu.

İki gözlem daha. Birincisi, içti, canlı + eylem dedektörünü 0,066 farkla kaçırıyor — özne içeriği var, ama layer norm o özniteliği token’ın kendi ortalamasının altına itti ve eşik eşiktir. İkincisi, sabit boyut dedektörü hiçbir şey için ateşlenmiyor: normalleştirmeden sonra sabit boyut her token’ın ortalamasının altında kalıyor. Elle tasarlanmış dedektörler bu şekilde kırılgandır: seçtiğimiz eşiklere ve normalleştirmeye duyarlılar. Eğitilmiş ağlarda özellikler genellikle birden çok nörona dağılır; ama bu tablodan o dağılımın ne kadar olduğu çıkarılamaz.

Cevabı geri yazmak

Tespit etmek işin yarısı. \(W_2\), o sekiz nöronu residual akışın dört boyutuna geri indirir ve yukarıdaki okumaya göre \(W_2\)’nin her satırı, dedektörünün ateşlendiğinde yayınladığı mesajdır. Satır sırasını koruyarak ve \(b_2 = 0\) alarak:

\[ W_2=\begin{bmatrix} 0{,}4&0&0{,}5&0\\ 0&0{,}4&0{,}5&0\\ 0{,}4&0{,}4&0&0\\ 0{,}3&0&0&0\\ 0&0{,}3&0&0\\ 0&0&0{,}3&0\\ 0{,}15&0{,}15&0&0\\ 0&0&0&0 \end{bmatrix} \]

Her dedektör yalnızca sorduğu özniteliklere yazar. Bileşik olanlar en sert şekilde eylem’e yazar (\(0{,}5\)), çünkü yayınlamaya değer olan bilgi “bu isim bir fiile bağlı”dır; tek öznitelikli olanlar ise daha yumuşak bir \(0{,}3\) yazar, çünkü token’ın zaten ilan ettiği şeyi tekrar etmekten öteye gitmezler. Çarpımı yapıp residual’a ekleyelim, \(x + \operatorname{FFN}(\operatorname{N}(x))\):

token canlı sıvı eylem sabit
kedi 2,2847 0,4635 1,1733 0,5
sütü 0,4458 2,2860 1,1763 0,5
içti 0,6438 1,1810 1,6654 0,5

Blok çıktısı bu — aşağıdaki widget’ta 4. aşama. Dikkat edilecek iki şey var. Sabit sütununa dokunulmadı, çünkü \(W_2\)’nin hiçbir satırı oraya yazmıyor. Bir de 3., 7. ve 8. satırlar hiç kullanılmadı: o dedektörler üç token’ın hiçbiri için ateşlenmedi, dolayısıyla satırlarında ne olduğunun burada bir önemi olmadı. ReLU altında böyle tam sıfırlar istisna değil normal durumdur. Bunu genellemeden önce iki çekince: güncel modellerin çoğunun kullandığı GELU ve SiLU, ReLU’nun aksine negatif girdilerin tamamını sıfırlamaz, yani “kapalı” ifadesi “çok az katkı veriyor”a dönüşür; ve her iki durumda da yoğun katman bütün satırları yine de hesaplar. Bölüm 7.7’nin konusu bu ikinci nokta — aritmetiğin bir kısmını tamamen atlamak: MoE, bir token için seçilmeyen uzman ağlarının çarpımlarını atlıyor; seçilen uzmanların içindeki bütün birimler ve yönlendirici ise yine hesaplanıyor.

7.3.7 Bloğun tamamı

Tek bir pre-norm transformer bloğu
RESIDUAL AKIŞ x x' LayerNorm token başına Multi-head attention 4d² parametre + LayerNorm Feed- forward 8d² parametre + TOKEN'LAR ARASI TOKEN BAŞINA Bilginin konumlar arasında geçtiği tek yer, vurgu renkli kutudur. PARAMETRELERİN ÜÇTE İKİSİ FEED-FORWARD YARISINDADIR.

Bu bölümde kullanılan, GPT-2’den beri yaygın olan pre-norm düzeni: normalleştirme daldadır, akışın üzerinde değil (gerçek modellerin norm türü ve yerleşimi farklı olabilir; yapılandırmasına bakın); böylece girdi embedding’inden çıktıya kesintisiz bir birim yol kalır. Bu bloğu \(L\) kez üst üste yığın — GPT-2 small için 12, 70B sınıfı bir model için 80 — ve elinizde bir transformer olur.

7.3.8 Her parça ve çözdüğü sorun

O şemadaki hiçbir şey süs değil. Her parça belirli bir başarısızlığa verilmiş bir cevaptır ve bir bileşeni anlamanın en hızlı yolu, onu sildiğinizde neyin bozulduğunu sormaktır. Bu tablo, bölümün tek sayfaya sığmış hâli — her satırın gerekçesi ya yukarıda ya aşağıda tartışılıyor.

bileşen var olma sebebi olan sorun o olmadan ne bozulur
öğrenilmiş herhangi bir skor matrisi ham benzerlik \(x_i \cdot x_j\) simetriktir ve köşegeni şişirir bir ilişkinin iki yönü tek bir skoru paylaşır; her token çoğunlukla kendisine bakar
onu \(W^Q\), \(W^K\) olarak çarpanlara ayırmak head başına bir \(d \times d\) matris 16 kat parametre demektir ve önbelleği \(d\) genişliğine mıhlar eğitim maliyeti şişer ve GQA ifade edilemez hâle gelir
ayrı bir \(W^V\) bir token’ın ilan ettiği şey, teslim etmesi gereken şey değildir yönlendirme ile içerik aynı şey olmaya zorlanır
iç çarpım \(n^2\) çiftin tamamı için yoğun matris çarpımıyla hesaplanan ucuz ve karşılaştırılabilir bir skor gerekir başka skor işlevleri de var (toplamsal attention gibi), ama bu verimli matris çarpımı yolu kaybolur
\(\div\sqrt{d_k}\) bağımsız birim varyanslı bileşenlerde skor yayılımı \(\sqrt{d_k}\) gibi büyür ve softmax mutlak büyüklüğe tepki verir geniş head’lerde softmax’ın doyma riski artar ve gradyan çok küçülür
softmax ağırlıklar pozitif olmalı, toplamı 1 etmeli ve türevlenebilmelidir sert bir “en iyi eşleşme”nin standart geri yayılımda kullanılabilir gradyanı yoktur; başka normalleştirmelerde ağırlık ve gradyan davranışı değişir
birden çok head tek bir ağırlıklı ortalama, birkaç ilişkiyi aynı anda ancak sınırlı biçimde izleyebilir katman, konum başına birkaç bağımsız örüntü yerine tek bir karışım ifade eder
\(W^O\) head çıktıları ayrı alt uzaylarda bulunur ve akışa geri girmelidir head’lerin bulduklarını birleştirmenin ya da yönlendirmenin yolu kalmaz
residual toplama bir katman hiçbir şey yapmayabilmelidir; bu seçenek olmadan derin yığınlar bozulmaya (degradation) yatkındır derin ağları eğitmek çok zorlaşır; derinlik eklemek fayda etmeyi bırakıp zarar vermeye başlayabilir
layer norm (pratikte RMSNorm) akışın büyüklüğü derinlikle artar; softmax ve ReLU ölçeğe duyarlıdır aktivasyonlar kayar ve eğitim kararsızlaşır
normalleştirmenin pre-norm konumu post-norm akışı yeniden ölçekler, dolayısıyla birim yol birim değildir incelenen derin post-norm modeller öğrenme oranı ısınması olmadan kararlı eğitilemedi
feed-forward attention yalnızca karıştırır: çıktısı value’ların dışbükey bir bileşimidir token başına özel, doğrusal olmayan işleme kalmaz ve kapasitenin çoğu kaybolur
konum sinyali maskesiz attention permütasyona eşdeğişkendir (permutation equivariance: girdi sırası değişince çıktı sırası da aynı şekilde değişir) — sırayı göremez maskesiz bir encoder her cümleyi kelime torbası (bag of words) olarak okur; nedensel maskeli bir decoder ise sırayı bir ölçüde maskeden öğrenebilir (NoPE, aşağıda)

Ortadaki sütunu tek başına okuyun; elinizde bütün mimarinin tasarım gerekçesi olur. Bunların her biri somut bir kusura verilmiş bir cevap, estetik bir tercih değil (ama tek olası cevap da değil) — 2017’den beri çevresindeki neredeyse her şey değişirken bu şeklin pek az değişmesinin sebebi de budur.

7.3.9 Temsilin değişimini izleyin

Yukarıdaki her şey, dörder sayıdan oluşan üç vektörün başına geliyor. Bloğu adım adım ilerletin ve onların nasıl hareket ettiğini izleyin.

Residual akış, aşama aşama

1. aşama ile 4. aşamayı karşılaştırın. Her token bloktan, boyutu aynı kalan ama normu (uzunluğu) büyüyen bir vektörle ayrılıyor — normlar 1,03’ten kabaca 2,2–2,7’ye çıkıyor; bu, burada seçtiğimiz matrislerin sonucu, her blok için bir kural değil — ve daha önemlisi, önceden taşımadığı bir içerikle. kedi bloğa canlı olarak girdi, bir eyleme bağlı canlı olarak çıkıyor. Bu tek bir blok. On iki tanesini üst üste koyun; her biri, kendinden önceki on bir bloğun vardığı sonuçlara koşullanabilir.

7.3.10 Parametreler gerçekte nerede duruyor

Bloğun şekli sabit olduğu için büyüklüğü bir formülle hesaplanır. \(d_{\text{ff}} = 4d\) ile:

parça matrisler parametre
attention her biri \(d \times d\) olan \(W^Q, W^K, W^V, W^O\) \(4d^2\)
feed-forward \(d \times 4d\) ve \(4d \times d\) \(8d^2\)
blok toplamı \(\mathbf{12d^2}\)

Bias terimleri ve layer-norm parametreleri \(O(d)\) mertebesindedir ve yuvarlandığında yok olur. Yani:

\(d_{\text{model}}\) attention feed-forward tek blok
768 2.359.296 4.718.592 7.077.888
1.024 4.194.304 8.388.608 12.582.912
4.096 67.108.864 134.217.728 201.326.592
8.192 268.435.456 536.870.912 805.306.368

\(d_{\text{ff}} = 4d\) ve tam genişlikte attention olan bir transformer bloğunun üçte ikisi feed-forward yarısıdır. İnsanlar “attention is all you need” diyor, sonra da attention’ın ağırlıkların üçte biri olduğu bloklar kuruyor.

Blok deyin, model değil. Oran bir bloğun içinde geçerlidir; modelin tamamında embedding’ler onu seyreltir ve ne kadar seyrelteceği boyuta bağlıdır. Aşağıdaki GPT-2 small’da 12 blok, parametrelerin yalnızca %68’ini oluşturuyor; dolayısıyla feed-forward’ın model içindeki payı yaklaşık %46. \(d = 8192\)’de embedding’ler bir yuvarlama hatasına dönüşür ve model oranı yeniden üçte ikiye yakınsar. Küçük modellerin büyük kısmı sözlükten oluşur; büyük modellerinki ise feed-forward’dan.

Üstelik üçte iki bir tavan değildir. Bu, \(4d^2\) attention’a karşı \(8d^2\) feed-forward olan GPT-2 şeklini varsayıyor. Bir Llama-3 bloğu ise bu şekilden iki yönde birden ayrılır: SwiGLU feed-forward’ına \(10{,}5d^2\), attention’ına ise yalnızca \(2{,}5d^2\) harcar — grouped-query attention (GQA) \(W^K\) ile \(W^V\)’yi \(d \times d\)’den \(d \times 1024\)’e küçültüyor (Bölüm 7.6), dolayısıyla o çift \(2d^2\) yerine \(0{,}5d^2\) tutuyor. Feed-forward payı bu yüzden \(10{,}5/13 = \mathbf{\%81}\); tam genişlikte attention varsayarak bulunan %72 değil. Bu oran bu yapılandırmanın blok içi payı; Llama ailesinde son yılların iki değişikliği (GQA ve SwiGLU) ağırlığı attention’dan feed-forward’a kaydırdı, ama başka aileler farklı ara genişlikler, paylaşılan uzmanlar ya da başka attention türleri seçebiliyor. Bir oranı bir modele taşımadan önce onun yapılandırmasıyla hesaplayın.

Bir pratik kural, ancak bakıp doğrulayabileceğiniz bir sayıyı üretiyorsa işe yarar. GPT-2 small herkese açık: sözlük 50.257, \(d = 768\), 12 katman, bağlam 1.024.

parça aritmetik parametre
token embedding’leri \(50{.}257 \times 768\) 38.597.376
konum embedding’leri \(1{.}024 \times 768\) 786.432
12 blok \(12 \times 12 \times 768^2\) 84.934.656
toplam 124.318.464

Yayımlanan boyut ~124M; kuralın sonucu bundan yalnızca %0,098 sapıyor. Kalan fark gizemli değil — kuralın attığı \(O(d)\) terimlerinin ta kendisi. Onları geri ekleyince sayım tam olarak tutuyor:

atılan terim aritmetik parametre
attention bias terimleri \(12 \times (3d + d)\) 36.864
feed-forward bias terimleri \(12 \times (4d + d)\) 46.080
layer-norm \(\gamma, \beta\) \(12 \times 2 \times 2d + 2d\) 38.400
tam toplam 124.318.464 + 121.344 124.439.808

Yani kural, açıklanamayan bir sapması olan bir yaklaşıklık değil; adı konmuş üç terimi silinmiş tam bir sayımdır. (Tam sayım bu yapılandırmaya ait: 12 katman, \(d = 768\), 50.257 token’lık sözlük, öğrenilmiş 1.024 konum, girdi ve çıktı ağırlıklarının paylaşılması; başka bir checkpoint’te aynı fark beklenmez.) Kapasite planlaması için ona güvenin ve birileri sizin sayınızla model kartındaki sayının neden son altı hanede ayrıştığını sorduğunda hangi terimleri attığınızı hatırlayın.

Tablonun neyi içermediğine dikkat edin: sözlüğe geri giden bir çıkış izdüşümü. GPT-2, çıkış ağırlıklarını girdi embedding matrisine bağlar; yani o 38,6M parametre bir kez sayılır, iki kez kullanılır.

7.4 Konum bilgisi: mutlak, öğrenilmiş, RoPE, ALiBi

Attention’ın kendisi sıra körü olduğu için konum bilgisinin modele açıkça verilmesi gerekir. İngilizce anlatımlar buraya dog bites man / man bites dog çiftini koyar; Türkçede o çift işe yaramaz, çünkü özne ve nesne rolünü büyük ölçüde hâl ekleri taşır (sıra yine vurguyu ve kimi zaman anlamı etkiler) — köpek adamı ısırdı ile adam köpeği ısırdı farklı token’lardan oluşur, dolayısıyla sıraya hiç bakmayan bir model bile bunları ayırt eder. Türkçede gereken, aynı token’ları taşıyan bir çift: kalite iyi fiyat kötü ile kalite kötü fiyat iyi. Sıra körlüğü, ortalaması alınmış embedding’lerin karşılaştığı başarısızlığın aynısıdır (Bölüm 4): orada bu çiftin kosinüsü 1,0000 çıkmıştı.

7.4.1 Önce sorunun gerçek olduğunu kanıtlayalım

Bu iddiayı söylemek de, kontrol etmeden inanmak da kolay; o hâlde kontrol edelim. Çözümlü örneği alın, üç token’ı içti kedi sütü sırasına karıştırın, attention’ı tekrar çalıştırın, sonra çıktıların karışıklığını geri alıp karşılaştırın:

token çıktılar, özgün sıra çıktılar, karıştırılıp geri düzeltilmiş
kedi [0.7444, 0.3433] [0.7444, 0.3433]
sütü [0.3256, 0.7443] [0.3256, 0.7443]
içti [0.5308, 0.6009] [0.5308, 0.6009]

Tablonun herhangi bir yerindeki en büyük uyuşmazlık: \(1{,}11 \times 10^{-16}\); bu bir fark değil, kayan nokta gürültüsüdür. Maskesiz self-attention permütasyona eşdeğişkendir: girdileri karıştırın, çıktılar da onlarla birlikte karışır, başka hiçbir şey değişmez.

Bu, birinin düzeltmeyi unuttuğu bir hata değil; doğrudan tanımdan geliyor. Her token’ın çıktısı bütün token’lar üzerinden bir ağırlıklı toplamdır; bir toplamda da terimleri hangi sırayla eklediğinizin önemi yoktur. \(QK^{\top}\) çarpımının, softmax’ın ya da \(AV\)’nin hiçbir yerinde bir indisi görebilecek bir şey yoktur. Her token yine kendine ait, bağlama göre değişen bir çıktı alıyor; ama bu çıktılar sıraya bağlı değil. Konumdan bağımsız bir özetleme (örneğin ortalama) yapıldığında aynı token’ları farklı sırayla taşıyan iki cümlenin temsili birebir aynı çıkar: çok akıllıca bir ortalama alma düzeni olan bir kelime torbası (bag of words), yine de bir kelime torbasıdır.

Bu cümlede “maskesiz” kelimesi kilit rol oynuyor. Nedensel maskeyi (Bölüm 7.5) ekleyin, simetri kırılsın: 1. token tek bir konuma bakabilirken 5. token beş konuma bakabilir, yani görünür token sayısı zaten bir indisi kodlar. Dolayısıyla yalnızca decoder’dan oluşan modeller hiçbir konumsal kodlama olmadan da konumu öğrenebilir — NoPE (no positional encoding) diye anılan sonuç (Kazemnejad vd. 2023). Küçük ölçekte bu yaklaşım, eğitimde görülenden daha uzun dizilere ekstrapolasyonda (extrapolation, dışdeğerleme) inceledikleri görevlerde bazı açık şemalardan bile daha iyi sonuç veriyor. Yaygın üretici modellerin çoğu yine de açık bir sinyal ekliyor; bir sayım, sırayı öğrenmenin zayıf ve dolaylı bir yolu olabilir ve her decoder’ın konumu aynı ölçüde öğrendiği de söylenemez. Doğru ifade şu: maske konumu sızdırır. “Decoder, konumsal kodlama olmadan kördür” demek doğru olmaz.

7.4.2 Konum sinyali eklemek

Özgün Transformer’ın cevabı, her yuva için bir vektör kurup oraya düşen kelimeye eklemektir (Vaswani vd. 2017). Vektör, geometrik olarak aralıklı frekanslardaki sinüs ve kosinüslerden yapılır:

\[ \text{PE}_{(\text{pos},\, 2i)} = \sin\!\left(\frac{\text{pos}}{10000^{2i/d}}\right), \qquad \text{PE}_{(\text{pos},\, 2i+1)} = \cos\!\left(\frac{\text{pos}}{10000^{2i/d}}\right) \]

\(d = 4\)’te iki frekans var — biri hızlı, biri yüz kat daha yavaş — ve kodlamanın tamamı üç satıra sığıyor:

konum \(\sin(\text{pos})\) \(\cos(\text{pos})\) \(\sin(\text{pos}/100)\) \(\cos(\text{pos}/100)\)
0 0,0000 1,0000 0,0000 1,0000
1 0,8415 0,5403 0,0100 1,0000
2 0,9093 −0,4161 0,0200 0,9998

Embedding’lere \(0{,}3 \times \text{PE}\) ekleyin (0,3 yalnızca bu gösterimde sayıları okunaklı tutmak için seçilmiş bir ölçek, yöntemin parçası değil) ve karıştırma testini tekrar edin. İşe yaramasını sağlayan incelik şu: kelimeler yer değiştirdiğinde konum vektörleri onlarla birlikte hareket etmez. Konum 0 hâlâ konum 0’dır; yani önceden birinci sırada olup şimdi üçüncü sıraya geçen bir kelime öncekinden farklı bir vektör alır:

token çıktılar, özgün sıra çıktılar, karıştırılıp geri düzeltilmiş
kedi [0.8029, 0.7345] [1.1319, 0.5094]
sütü [0.6385, 1.0023] [0.7489, 0.6805]
içti [0.7865, 0.8342] [0.8767, 0.6607]

En büyük uyuşmazlık: 0,3289. Sıra körlüğü ortadan kalktı.

positional encoding (konumsal kodlama) — modelin token’ların konumunu ya da aralarındaki uzaklığı kullanmasını sağlayan sinyal; embedding’e eklenebilir, query ve key’e uygulanabilir (RoPE) ya da attention skoruna bias olarak katılabilir (ALiBi). Gerekli, çünkü maskesiz attention tek başına sıra körüdür. ↩︎

7.4.3 Neden özellikle sinüsler

Her yuvaya farklı bir vektör veren herhangi bir şema da bu eşitliği bozardı. Sinüslerin seçilme sebebi, ifade edilmesi kolay ve bu literatürde ender rastlanacak biçimde birebir doğru olan bir özelliktir. Bu şekilde kurulmuş bir positional encoding için,

\[ \text{PE}_{p} \cdot \text{PE}_{p+k} \;=\; \sum_{i} \cos(k\,\omega_i) \]

Açı farkı özdeşliği sayesinde \(p\) terimleri sadeleşir ve geriye kalan şey yalnızca \(k\)’ya — yani kaydırmaya — bağlıdır. Yani iki konum arasındaki benzerlik, dizide nerede durduklarının değil, birbirlerinden ne kadar uzak olduklarının bir fonksiyonudur.

Bu yalnızca yaklaşık olarak doğru olsaydı çok daha az değerli olurdu; o hâlde ölçelim. \(d = 64\)’te \(p\)’yi 256 konum boyunca tarayıp kaydırmayı sabit tutunca:

kaydırma \(k\) ortalama \(\text{PE}_p \cdot \text{PE}_{p+k}\) \(p\) üzerinden std \(p\) üzerinden aralık
0 32,000000 1,6e−15 1,1e−14
1 30,916832 7,8e−15 4,6e−14
2 28,303862 1,1e−14 7,8e−14
4 23,934362 7,4e−15 5,0e−14
8 22,413912 5,8e−15 3,9e−14
16 19,372820 5,8e−15 3,9e−14
32 19,553972 1,0e−14 7,1e−14
64 13,906089 1,2e−14 9,1e−14

Özdeşlik matematikte tam; 256 farklı mutlak konum boyunca kalan \(10^{-14}\)’lük standart sapma yalnızca sonlu duyarlıklı kayan nokta hesabından geliyor. Ayrıca her konum vektörünün uzunluğu, on hane hassasiyetle aynıdır: \(\sqrt{d/2} = 5{,}656854\). Bu, konum vektörünün bir özelliği; toplamın değil. Konum vektörünü eklemek embedding’in normunu değiştirir, çünkü \(\lVert x + p \rVert^2 = \lVert x \rVert^2 + \lVert p \rVert^2 + 2\,x \cdot p\) aradaki açıya bağlıdır. Normu olduğu gibi bırakan şema, aşağıdaki RoPE.

Ortadaki sütunu dikkatle okuyun. Ortalama, \(k\)’ya göre monoton değildir: 32 kaydırması 16 kaydırmasından yüksek skor alıyor. Bu bir kosinüsler toplamıdır, dolayısıyla salınır. Doğru iddia, “bu nicelik yalnızca kaydırmaya bağlıdır” iddiasıdır ve son iki sütun bunu tartışmasız biçimde ortaya koyuyor. Cazip iddia — “yakın konumlar uzak konumlardan daha benzer görünür” — tablonun gösterdiği şey değildir; bunu yine de söyleyen bir ders kitabı, sizden sayılar yerine bir resme güvenmenizi istiyordur.

Sinüzoidal konumsal kodlamalar, resim olarak

Düşük boyutlar (üstte) birkaç konumda bir işaret değiştiriyor; yüksek boyutlar (altta) pencerenin tamamı boyunca neredeyse hiç değişmiyor. Bütün mesele dalga boylarındaki bu yelpaze: hızlı boyutlar yakın konumları, yavaş boyutlar uzak konumları ayırt etmeye elverişli. Bir modelin bu boyutları gerçekte “hangi komşu” ya da “belgenin neresi” bilgisi için kullanıp kullanmadığı bu resimden çıkmaz. d_model değerini yukarı çekin ve altta daha fazla yavaş boyutun belirdiğini izleyin.

7.4.4 Öğrenilmiş mutlak konumlar

Sinüsler zarif, ama tek seçenek değil. BERT de GPT-2 de onların yerine konumla indekslenen sıradan bir embedding tablosu kullandı: 0. satır, 0. yuvanın vektörüdür ve diğer bütün parametreler gibi gradyan inişiyle öğrenilir (Devlin vd. 2019; Radford vd. 2019). Bu modellerde aşağı yukarı aynı işi gördü (sinüslerin her görevde gereksiz olduğunu göstermez); bedelini de yukarıdaki parametre tablosunda gördünüz — GPT-2 small buna 786.432 parametre harcıyor.

Asıl bedel başka yerde. Öğrenilmiş bir tablonun satır sayısı, tam olarak onu eğitirken kullandığınız uzunluk kadardır. GPT-2’nin tablosunda 1.024 satır var, dolayısıyla 1.025. token’ın hiç vektörü yok — bozuk bir vektörü değil, hiç. Bağlam sınırı, çıkarım anında gevşetebileceğiniz bir ayar tercihi değil; bir ağırlık matrisinin şekline gömülmüş durumda. Bu model ailesinde daha uzun girdi işlemek için tabloyu genişletmek (yeni satırları başlatıp eğitmek, eskileri interpolasyonla çoğaltmak) ya da konumu yeniden tanımlayıp modeli yeniden uyarlamak gerekir; mevcut checkpoint’te yalnızca bir çıkarım ayarını değiştirmek yetmez.

7.4.5 RoPE: eklemek yerine döndür

Bugün baskın tercih rotary position embedding’dir (döndürmeli konum embedding’i). Fikri daha iyi bir tablo değil, temelden farklı bir yaklaşım (Su vd. 2024). RoPE embedding’e hiçbir şey eklemez. Query ve key vektörlerini alır — izdüşümden sonra, her head’in içinde — ardışık koordinat çiftlerine ayırır ve her çifti konumla orantılı bir açı kadar döndürür:

\[ \begin{bmatrix} q'_{2i} \\ q'_{2i+1} \end{bmatrix} = \begin{bmatrix} \cos m\theta_i & -\sin m\theta_i \\ \sin m\theta_i & \phantom{-}\cos m\theta_i \end{bmatrix} \begin{bmatrix} q_{2i} \\ q_{2i+1} \end{bmatrix}, \qquad \theta_i = 10000^{-2i/d_k} \]

Burada \(m\) token’ın konumu, \(d_k\) ise head boyutudur; çünkü döndürme her head’in içinde yapılır. Bir vektörü \(m\theta\) kadar, bir başkasını \(n\theta\) kadar döndürüp sonra iç çarpımlarını alırsanız geriye \((m-n)\theta\) açısı kalır. Yani aynı query ve key içerikleri için attention skorunun kendisi kaydırmanın bir fonksiyonu hâline gelir (skor elbette içeriklere de bağlı; farklı query–key çiftleri aynı uzaklıkta aynı skoru almaz) — embedding’e eklenip izdüşümlerden sağ çıkması umulan bir yan kanal değil, doğrudan skorun kendisi.

Bu güçlü bir iddia; o yüzden ölçülmüş hâlini verelim. Rastgele seçilip sabit tutulan bir query ile bir key (\(d = 64\)), birbirinden çok farklı dört mutlak konumda (\(n = 0, 100, 1000, 5000\)) sabit bir kaydırmayla skorlandı:

kaydırma \(m-n\) skor dört konum arasındaki yayılım
0 2,152941 2,2e−15
1 3,606170 1,5e−13
2 5,453257 3,1e−13
5 6,457170 5,0e−13
10 8,827710 1,4e−13
50 −0,061180 1,5e−13
200 −13,740668 4,1e−14

Şimdi aynı ölçüm, konum vektörünün izdüşümden önce embedding’e eklendiği toplamalı şema için:

kaydırma \(m-n\) \(n=0\)’da skor \(n=100\)’de \(n=1000\)’de dört konumun yayılımı
0 31,19 26,98 48,15 26,3
1 31,82 27,54 47,52 26,6
10 13,74 20,55 38,85 25,1
200 1,86 6,43 25,00 23,1

Tabloda dört konumun yalnızca üçü gösteriliyor; yayılım sütunu ise dördünü birden kapsıyor. İlk iki satırda yayılımın, görünen sütunlardan tek başına hesaplanacak olandan geniş çıkmasının sebebi de bu.

\(10^{-13}\)’lük bir yayılıma karşı 26’lık bir yayılım. Toplamayla, “beş token geride” ifadesi 0. konumda başka, 5.000. konumda başka bir şey demek oluyor ve modelin bununla baş etmeyi öğrenmesi gerekiyor. Döndürmeyle ise her yerde aynı anlama geliyor, hem de kendiliğinden. Bu, göreli konumun skor geometrisine doğrudan girdiğini gösteriyor; uzun bağlamda kaliteyi ise ayrıca ölçmek gerekiyor. RoPE ayrıca vektör normlarını birebir korur — döndürme döndürmedir — dolayısıyla toplamanın yapabildiği gibi bir query’nin büyüklüğünü bozamaz.

RoPE’nin Llama, Qwen, Mistral, Gemma ve 2023’ten beri yayımlanan açık ağırlıklı modellerin çoğunda bulunmasının sebebi bu (ALiBi, öğrenilmiş ve karma düzenler de kullanılıyor; bir modelin konum şemasını yapılandırmasından doğrulayın). Bu modeller için kullanılan başlıca bağlam genişletme yöntemleri de (position interpolation, NTK-aware scaling, YaRN) yeni tablolar değil, \(\theta_i\)’nin ya da konum indeksinin yeniden ölçeklenmesi; çoğu zaman ardından uzun bağlamlı ek eğitim de geliyor.

7.4.6 ALiBi: kodlamayı bırak, mesafeyi cezalandır

En ekonomik cevap hiçbir şey kodlamamak ve doğrudan attention skorlarından mesafeyle orantılı bir ceza çıkarmak (Press vd. 2022):

\[ \text{score}(i, j) = \begin{cases} \dfrac{q_i \cdot k_j}{\sqrt{d_k}} - m\,(i - j), & j \le i \\[4pt] -\infty, & j > i \end{cases} \]

Ceza, \(\sqrt{d_k}\) ölçeklemesinden önce değil sonra çıkarılır. Bunun neden önemli olduğu makalenin değil bizim yorumumuz: ceza bölmeden önce eklenseydi etkin eğim \(m/\sqrt{d_k}\) olur ve head genişliğiyle değişirdi; sonra eklendiği için aynı \(m\) eğimleri farklı genişlikteki modellerde aynı anlama gelir.

Her head, geometrik bir diziye göre belirlenen kendi sabit \(m\) eğimini alır. Sekiz head varken eğimler \(\tfrac12, \tfrac14, \tfrac18, \dots, \tfrac{1}{256}\) olur; yani \(m = 0{,}5\) olan head 2 token sonra 1’lik bir ceza biriktirmiş olur (skor 1 azalır; diğer skorlar eşitse bu, attention ağırlığını \(e \approx 2{,}7\)’ye böler) ve yerele yatkındır, \(m = 1/256\) olan head ise aynı cezayı biriktirmek için 256 token’a ihtiyaç duyar ve küresele yatkındır. Farklı head’ler, tasarım gereği, farklı mesafe ölçeklerine yatkındır; bir head’in fiilen ne kadar yerel davrandığı içerik skorlarına da bağlı — içerik skoru yüksek uzak bir token yine yüksek ağırlık alabilir.

\(m = 0{,}5\) olan bir head’in altı token boyunca skorlara eklediği bias terimleri:

0 1 2 3 4 5
konum 0 0,0 −∞ −∞ −∞ −∞ −∞
konum 1 −0,5 0,0 −∞ −∞ −∞ −∞
konum 2 −1,0 −0,5 0,0 −∞ −∞ −∞
konum 3 −1,5 −1,0 −0,5 0,0 −∞ −∞
konum 4 −2,0 −1,5 −1,0 −0,5 0,0 −∞
konum 5 −2,5 −2,0 −1,5 −1,0 −0,5 0,0

Bu tek matris aynı anda hem nedensel maske hem de konum sinyalidir ve içinde öğrenilmiş tek bir parametre yoktur. \(i - j\) üzerinden bir formülle verildiği için her mesafede tanımlıdır. Özgün çalışma, 1.024 uzunlukta eğitilmiş 1,3 milyar parametrelik bir ALiBi modelinin 2.048 uzunlukta da iyi sonuç verdiğini bildirdi — makalenin başlığında öne çıkarılan sonuç da bu. Formülün her uzunlukta tanımlı olması, her ALiBi modelinin her uzunlukta bozulmadan çalışacağı anlamına gelmez.

7.4.7 Aralarında seçim yapmak

şema konum nasıl giriyor ek parametre eğitim uzunluğunun ötesinde
sinüzoidal embedding’lere eklenir yok formül tanımlı; kalite ayrıca ölçülmeli, çoğu zaman bozulur
öğrenilmiş mutlak embedding’lere eklenir \(n_{\text{ctx}} \times d\) tanımsız — mevcut tabloda satır yok
RoPE her head’de Q ve K’yı döndürür yok formül tanımlı; tipik olarak bozulur, genişletme yöntemleri ve çoğu zaman ek eğitimle kullanılabilir
ALiBi skorlardan çıkarılır yok formül tanımlı; özgün çalışmada eğitim uzunluğunun iki katında iyi sonuç

RoPE tek başına eğitim uzunluğunun ötesine genellikle iyi ekstrapolasyon yapmaz. \(i - j\) üzerinden bir formül olduğu için RoPE her mesafede tanımlıdır — ama 4k’da eğitilmiş bir model çoğu zaman 8k’ya varmadan çok önce dağılır, çünkü eğitim uzunluğunun ötesinde gördüğü döndürme açılarıyla daha önce hiç karşılaşmamıştır. Uzun bağlam alet çantası tam da bu başarısızlık yüzünden vardır: Position Interpolation görülmemiş konumları eğitilmiş aralığa geri sıkıştırır (Chen vd. 2023); NTK-aware scaling ve YaRN (Peng vd. 2024) ise \(\theta\)’yı tekdüze değil, frekans frekans yeniden ölçekler. Bir RoPE modeli hakkındaki “128k’ya genişletildi” duyuruları çoğu zaman bu yöntemlerden birinin ve ardından yapılan ek eğitimin sonucunu anlatır (kimi yöntemler yalnızca çıkarım anında da denenir) — RoPE’nin kendiliğinden gelen bir özelliğini değil. Beklenen kaliteyi hedef görevde ölçün.

Pratikte önemli olan sütun sonuncusu. Bir sağlayıcı bağlam penceresi duyurduğunda, eğitim verisi kadar konum şemasının bir özelliğini de anlatıyor demektir — ve bir model belli bir uzunluktan sonra tuhaf biçimde güvenilmez olmaya başladığında, ilk bakılacak yer konum şemasıdır. İlan edilen bağlam uzunluğu ile kullanılabilir bağlam uzunluğu farklı sayılardır; aradaki farkı varsaymak yerine ölçeceğiz (Bölüm 25).

7.5 Encoder-only, decoder-only, encoder-decoder

Kulağa aşırı basitleştirme gibi gelen ama çoğunlukla öyle olmayan bir gerçek: BERT tarzı ile GPT tarzı self-attention arasındaki yapısal fark, attention matrisinin hangi girdilerinin sıfırdan farklı olmasına izin verildiğidir. Bu gerçeğin iki çekincesi var ve bu kısmın geri kalanı ikisini de ele alıyor: bir encoder–decoder, cross-attention ile birbirine bağlanmış iki yığındır ve tek bir yığın üzerindeki hiçbir maske bunu üretmez; ayrıca BERT ve GPT adları belirli embedding, normalleştirme yerleşimi ve eğitim hedefi tercihlerini de taşır; kullanım biçimleri de bu yüzden ayrışır.

Aynı bloklar, aynı multi-head attention, aynı feed-forward alt katmanı, aynı residual akış. Self-attention’ın erişimi düzeyinde değişen şey, softmax’tan önce skorlara uygulanan causal mask (nedensel maske) — ve bundan çıkan sonuç: modelin ne yapmak üzere eğitilebileceği. Somut modellerin davranışını ise eğitim hedefi, veri, çıktı head’i ve diğer mimari tercihler de belirliyor.

causal mask (nedensel maske) — her konumun kendinden sonrakilere bakmasını engeller; böylece model soldan sağa üretim yapmak üzere eğitilebilir. ↩︎

7.5.1 Maske, mimarinin kendisidir

Üç desene bakın.

Hangi token hangisine bakabilir

Nedensel maskeyi seçin, sağ üst üçgen griye döner. 0. konumdaki kedi artık yalnızca kendisine bakabilir. Bir decoder’ın ham metin üzerinde verimli biçimde, tek bir paralel geçişte eğitilebilmesini sağlayan şey bu kısıttır: \(i\) konumu kendisini ve öncekileri görür, hedefi ise bir sonraki token, yani \(i+1\); dolayısıyla hedef kendi girdisinde hiç görünmez. Böylece modelden bir cümledeki bütün sonraki token’ları aynı anda tahmin etmesi istenebilir ve bu da tek bir belgeyi binlerce eğitim sinyaline çevirir. (Metin üretirken ise yeni token’lar yine teker teker seçilir.)

Mekanik olarak tek satır: maskelenen skorları softmax’tan önce \(-\infty\) yapın; böylece \(e^{-\infty} = 0\) olur ve o token’lar tam olarak sıfır ağırlık alır. Ağırlıkları softmax’tan sonra sıfırlamak eşdeğer olmazdı, çünkü kalan ağırlıkların toplamı artık 1 etmezdi.

7.5.2 Üç aile

Üç aile, tek fark: hangi hücrelere izin veriliyor
ENCODER-ONLY her token her token'ı görür boşluk doldurmak üzere eğitilir BERT — sınıflandırma, bilgi çıkarma, gömme DECODER-ONLY token yalnızca geçmişi görür sonraki token tahminiyle eğitilir GPT, Llama, Qwen — üretim ENCODER-DECODER çapraz kaynak hedef girdi tam, çıktı nedensel hedef dizi üretmek üzere eğitilir T5, BART — çeviri, özetleme Dolu hücre "bu query satırı bu key sütununu okuyabilir" demek. Üç aile arasında mimaride başka hiçbir şey değişmiyor — aynı blok, aynı matematik, farklı maske.

Satırlar query, sütunlar key. Self-attention erişimi açısından aileler hangi hücrelerin açık olduğuyla ayrışıyor; encoder–decoder ailesini tanımlamak için ayrıca iki yığını bağlayan cross-attention gerekiyor.

aile maske ne için eğitilir temsilci iyi olduğu iş
encoder-only çift yönlü maskelenmiş token’ları doldurmak BERT (Devlin vd. 2019) sınıflandırma, bilgi çıkarma, arama embedding’leri
decoder-only nedensel sonraki token’ı tahmin etmek GPT, Llama, Qwen üretim ve — ölçek geldiğinden beri — prompt’la çözülebilen birçok görev
encoder-decoder girişte çift yönlü, çıkışta nedensel hedef bir dizi üretmek T5 (Raffel vd. 2020), BART çeviri, özetleme, her türlü girdi→çıktı yeniden yazımı

Bir encoder-decoder’ın fazladan bir mekanizmaya ihtiyacı var. Decoder önce o ana kadar ürettiği token’lar üzerinde self-attention çalıştırır, ardından cross-attention (çapraz dikkat) adı verilen ikinci bir attention alt katmanı gelir: burada query’ler decoder’dan, key ve value’lar encoder’dan gelir. Bu, yukarıda adım adım yaptığınız aritmetiğin aynısıdır — tek fark, \(Q\) ile \(K, V\)’nin iki farklı diziden hesaplanmasıdır. \(m\) hedef token ve \(n\) kaynak token varsa skor matrisi \(m \times n\) boyutundadır — artık kare değildir. “Çeviriyi yazarken kaynak cümleye bak” fikri işte böyle ifade edilir.

Bazı çok modlu (multimodal) modeller görüntüyü dil modeline bu yolla bağlar: Flamingo’da (Alayrac vd. 2022) key ve value’ları görüntü encoder’ı üretir, query’leri dil modeli sağlar. Açık görüntü-dil modellerinin çoğu ise görüntü token’larını bir izdüşüm katmanıyla doğrudan dil modelinin girdi dizisine ekler (Liu vd. 2023). İkisine de ileride döneceğiz (Bölüm 21).

7.5.3 Decoder-only neden kazandı

2019’da makul tahmin, her ailenin kendi nişini koruyacağıydı. Büyük, genel amaçlı modellerde öyle olmadı; bu kitapta kullanacağınız modellerin çoğu decoder-only. Akla yatkın üç sebep var (veri, altyapı ve yatırım gibi etkenler de iç içe; bu tarihin nedenleri ölçülmüş değil):

Tek bir amaç, bütün veri. Sonraki token tahmini ne etiket ister, ne bir bozma şeması, ne de eşleştirilmiş veri. İnternetteki her belge onun için eğitim verisidir ve o belgedeki her konum bir eğitim örneğidir.

Görevin metne dönüşmesi. Bir model devam ettirmede yeterince iyi olduğunda, sınıflandırma, çeviri ve özetleme ayrı head’ler olarak inşa edilmek yerine prompt (istem) olarak yazılabilir hâle gelir. Genel bir modele kibarca sorabiliyorsanız, encoder-decoder’ın mimari uzmanlaşması masrafını çıkarmayı bırakır. Bölüm 17 tam da bu hikâyeyi anlatıyor.

Ucuz artımlı kod çözme (incremental decoding: token’ları birer birer üretmek). Nedensel maskeyle, \(i\) token’ının temsili kendinden sonraki hiçbir şeye bağlı değildir — dolayısıyla sona bir token eklediğinizde önceki bütün hesap hâlâ geçerlidir. KV önbelleğini mümkün kılan şey budur; üretimin neden bu maliyette olduğunu da KV önbelleği açıklar.

Encoder-only modeller demode değil. Hacimli sınıflandırma ve arama işlerinde, 110M parametreli BERT tarzı bir encoder belge başına üretken bir modelden çok daha ucuz olabilir ve çoğu zaman ona yakın isabet verir; çünkü token token kod çözme yapmak yerine tek bir ileri geçişte tek bir vektör verir. Ne kadar ucuz ve ne kadar isabetli olduğu modele, donanıma ve göreve bağlı; kendi işinizde ölçün. Yüksek hacimli arama sistemlerinin çoğu hâlâ encoder tabanlı embedding modelleri kullanır (Bölüm 19); embedding sıralama tablolarının tepesinde ise giderek bu işe uyarlanmış decoder’lar yer alıyor. Duygu analizi için 7B’lik bir decoder seçmek yaygın ve pahalı bir hatadır — Bölüm 4 da ucuz temsilin ne sıklıkla kazandığını zaten göstermişti.

7.6 Verimlilik: FlashAttention, GQA/MQA, KV önbelleği

Buraya kadar her şey attention’ın ne hesapladığıyla ilgiliydi. Bu kısım ise neye mal olduğuyla ilgili: hesabın dizi uzunluğu, bellek trafiği ve üretim biçimiyle nasıl büyüdüğü. Bir modeli hangi donanımda ve kaç eşzamanlı istekle çalıştırabileceğinizde bu maliyetler belirleyici.

7.6.1 Karesel terim, bayt cinsinden

\(QK^{\top}\) matrisi \(n \times n\) boyutundadır. Bütün sorun tek satırda bu. Naif yazıldığında, tek bir katmandaki tek bir head’in bu matrisi bellekte tutması gerekir. fp16’da (16 bitlik kayan noktalı sayı) her sayı 2 bayt yer kaplar; bir GiB \(2^{30}\) bayt, yani yaklaşık 1,07 GB’dir:

dizi uzunluğu \(QK^{\top}\) girdi sayısı fp16’da tek bir skor matrisi
512 262.144 0,0005 GiB
4.096 16.777.216 0,03 GiB
32.768 1.073.741.824 2,00 GiB
100.000 10.000.000.000 18,63 GiB

Tek katmandaki tek bir head için on sekiz gibibayt. Bunu 32 head ve 32 katmanla çarpın; tam skorları birlikte saklamak artık bir bellek sorunu olmaktan çıkar, hayal olur. Bu, matrisi olduğu gibi belleğe yazan naif uygulamanın hesabı; aşağıdaki FlashAttention gibi bloklu uygulamalar aynı sonucu bu matrisi hiç kurmadan hesaplıyor.

7.6.2 Attention gerçekte ne zaman baskın hâle gelir?

Karesel terim her zaman önemli olan terim değildir; insanlar onu çoğu zaman gözünde büyütür. Tam multi-head attention ve \(d_{\text{ff}} = 4d\) varsayımıyla, tam dizili bir ileri geçişte katman başına kabaca (FLOP sayısı olarak):

\[ \underbrace{24\,n\,d^2}_{\text{izdüşümler ve FFN}} \qquad\longleftrightarrow\qquad \underbrace{4\,n^2 d}_{\text{skorlar ve ağırlıklandırma}} \]

İkisini eşitlerseniz \(n = 6d\) çıkar. Bu, işlem sayısı için kaba bir eşik; gerçek gecikme bellek erişimine, kernel’e ve donanıma da bağlı. \(n\) bu değerin altındayken transformer, aritmetiğinin çoğunu gayet sıradan, dizi uzunluğuyla doğrusal büyüyen matris çarpımlarına harcar:

\(d_{\text{model}}\) dizi uzunluğu \(n\) attention’ın FLOP payı
768 1.024 %18
768 4.608 dönüm noktası %50
768 32.768 %88
4.096 4.096 %14
4.096 24.576 dönüm noktası %50
4.096 100.000 %80

Birkaç bin token’lık bağlamlarda attention, aritmetiğin yarısından azını oluşturur. 4.096 genişliğindeki bir model 4.096 token işlerken aritmetiğinin %86’sını feed-forward ve izdüşüm matrislerine harcar. Karesel terim, uzun bağlamda çarptığınız bir duvardır; her uzunlukta ödediğiniz bir vergi değil. Hangi rejimde olduğunuzu bilmek, attention’ı optimize etmenin zamanınıza değip değmeyeceğine dair iyi bir ilk ipucu; ama FLOP payı düşük olsa bile attention bellek trafiği yüzünden darboğaz olabilir (bir sonraki kısım), yani kararı ölçülen gecikmeyle verin.

7.6.3 FlashAttention: matrisi hiç kurma

Uzun bağlamı pratik hâle getiren gözlem şu: modern bir GPU’da, uzun dizilerde attention’ı çoğu zaman sınırlayan şey aritmetik değil. Sınırlayan şey, o \(n \times n\) matrisi hızlı yonga içi bellekle yavaş yüksek bant genişlikli bellek arasında taşımak. FLOP’lar ucuz; gidiş-dönüşler değil (Dao vd. 2022).

Bu yüzden FlashAttention skor matrisini hiç somutlaştırmaz (belleğe bütün hâlinde yazmaz). Diziyi küçük bloklar (tile) hâlinde dolaşır ve yol boyunca güncellenen bir softmax normalleştiricisi tutar; böylece kısmen hesaplanmış bir softmax, yeni bloklar geldikçe düzeltilebilir. Çıktıyı da doğrudan biriktirir.

FlashAttention kesin sonuç verir. (Özgün, yoğun algoritmadan söz ediyoruz; aynı makaledeki blok-seyrek varyant yaklaşıktır.) Seyrek bir yaklaşıklama, düşük ranklı bir çarpanlara ayırma ya da pencereli bir sezgisel yöntem değildir. Naif uygulamayla (implementation) aynı fonksiyonu hesaplar; ondan yalnızca neyi sakladığı bakımından ayrılır. Kesin sonuç, bit bit aynı sonuç demek değildir: çevrim içi softmax toplamları yeniden gruplar ve kayan noktalı toplamanın birleşme özelliği yoktur. Bu yüzden son basamaklar oynayabilir; tıpkı herhangi iki matris çarpımı çekirdeğinin son basamaklarda farklı sonuç vermesi gibi. Yoğun algoritmada feda edilen bir kalite yok. FlashAttention’ın yaklaşık on sekiz ayda bir makaleden varsayılan seçeneğe dönüşmesinin sebebi budur; hesaplama hâlâ karesel olduğu hâlde attention’daki bellek kullanımının artık \(n\) ile doğrusal olmasının sebebi de budur.

7.6.4 KV önbelleği: dağıtıma karar veren sayı

Üretim artımlıdır. Model 500 token ürettikten sonra 501.’yi üretir — ve maske nedensel olduğu için ilk 500’ün key ve value’ları değişmemiştir. Onları yeniden hesaplamak saf israf olurdu, o yüzden saklanırlar. Bu depoya KV önbelleği (KV cache) denir. Her adımda bütün öneki modelden yeniden geçirmek yerine yalnızca yeni token işlenir ve yeni query geçmişteki \(n\) key’e bakar: token başına attention maliyeti kareselden doğrusala iner. Üretim yine sabit zamanlı olmaz; yeni token’ın izdüşüm ve feed-forward hesabı ve ağırlıkların okunması sürüyor.

KV cache (KV önbelleği, yani anahtar-değer önbelleği) — önceki token’lardan saklanan key ve value’lar; böylece üretim onları yeniden hesaplamaz. Bir GPU’nun kaç kullanıcıya hizmet verebileceğine genelde modelin değil, bu önbelleğin boyutu karar verir. ↩︎

Bu durum, bir isteği karakteri birbirinden çok farklı iki evreye ayırır. Prompt tek seferde, eğitimdeki gibi paralel olarak işlenir ve onun key ile value’ları önbelleği doldurur; buna prefill (ön doldurma) denir. Ardından her yeni token küçük bir adımdır (decode, kod çözme adımı): adım başına aritmetik azdır, ama tam attention’lı klasik bir decoder’da her yeni token için modelin bütün ağırlıkları ve her katmanın önbelleği bellekten okunur (kayan pencere, sıkıştırılmış önbellek ya da durum-uzayı katmanları bu miktarı değiştirir; aşağıda). Üretim hızının genellikle aritmetikle değil bellek bant genişliğiyle sınırlanmasının sebebi budur; Bölüm 31 bu noktanın üzerine kurulur.

Önbellek neyi kazandırıyor, neye mal oluyor
6. TOKEN ÜRETİLİYOR — ÖNBELLEK YOK 6'sını da yeniden hesapla tok 1 k, v tok 2 k, v tok 3 k, v tok 4 k, v tok 5 k, v tok 6 k, v 6 × iş 6. TOKEN ÜRETİLİYOR — ÖNBELLEKLE 5'ini kullan, 1'ini hesapla tok 1 önbellek tok 2 önbellek tok 3 önbellek tok 4 önbellek tok 5 önbellek tok 6 YENİ 1 × iş Bunu meşru kılan şey nedensel maske: 3. token'ın key'i asla 6. token'a bağlı olamaz, yani hiç yeniden hesaplanması gerekmez. Kazandığınız aritmetik, bütçelenecek bellek olarak döner.

Üretimin ucuz, uzun bağlamın pahalı olmasının sebebi aynı şey: önbellek. Önbellek, her adımda bütün öneki yeniden işlemeyi (katman başına önekteki \(n\) token’ın izdüşümleri, feed-forward’ı ve aralarındaki attention) eşzamanlı her istek için ayrı tutulan \(O(n)\) depolamayla değiş tokuş eder. Yeni query’nin \(n\) key’e bakışı ise yine uzunlukla doğrusal büyür.

KV önbelleği bir yandan da GPU belleğinizi yiyip bitirir:

\[ \text{bayt} = 2 \times L \times h_{\text{kv}} \times d_{\text{head}} \times n_{\text{token}} \times \text{değer başına bayt} \]

Baştaki 2, K ve V içindir. Formülde neyin bulunmadığına dikkat edin: query head sayısı ve modelin parametre sayısı. Neyin bulunduğuna da dikkat edin: token sayısı; hem doğrusal olarak hem de eşzamanlı istek başına. Tablodaki MHA, her query head’inin kendi key ve value head’i bulunan standart multi-head attention’ın kısaltmasıdır.

model şekli katman KV head head boyutu bağlam KV önbelleği
7B sınıfı 32 32 128 4.096 2,1 GB
7B sınıfı 32 32 128 32.768 17,2 GB
70B sınıfı, varsayımsal MHA 80 64 128 32.768 85,9 GB
7B sınıfı 32 32 128 100.000 52,4 GB

Tablo, değerleri 16 bit, bütün katmanları tam attention’lı ve her isteğin önbelleği ayrı olan ham K/V tensörünün baytlarını veriyor (GB ondalık; 52,4 GB yaklaşık 48,8 GiB). Nicemleme, GQA, kayan pencere, sayfalama ve çalışma alanı gerçekte ayrılan belleği değiştirir. fp16’daki 7B’lik bir model 14 GB ağırlık demektir. 100.000 token’lık bağlamda KV önbelleği 52 GB — tek bir sohbet için modelin kendisinin neredeyse dört katı. “Uzun bağlam neden bu kadar pahalı?” sorusunun arkasındaki aritmetik budur ve servis sistemlerinin bu belleğin nasıl bölümlendiğine ve yeniden kullanıldığına bu kadar kafa yormasının sebebi de budur (Bölüm 31).

Üçüncü satır bilerek varsayımsal diye işaretlendi: bugünün 70B sınıfı modellerinde 64 KV head yok. İlk LLaMA 65B hâlâ tam multi-head attention kullanıyordu; ama Llama 2 70B, Llama 3, Mistral’in modelleri ve onlardan türeyenler 8 kullanıyor; sebebini bir sonraki kısım anlatıyor. Hesabı \(h_{\text{kv}} = 8\) ile yeniden yapın: 85,9 GB 10,7 GB’a iner. Bu tek değişiklik, 32k bağlamlı tek bir isteğin önbelleğinin 80 GB’lık bir kartın tamamından büyük olmasıyla (85,9 GB) kartın sekizde birine sığması (10,7 GB) arasındaki farktır. Ağırlıklar yine de bir düğüm ister — fp16’da 140 GB — ama 9. alıştırmadaki dört kartlık düğümde ağırlıklardan arta kalan 180 GB’a, yalnızca bu iki kalem hesaba katılırsa, aynı bağlamda 2 yerine 16 istek, yani sekiz kat fazlası sığar. Gerçek bir serviste çalışma alanı ve dağıtım düzeni bu üst sınırı düşürür. Bu bölümdeki en belirleyici tek sayı da budur.

KV önbelleği hesaplayıcısı

7.6.5 GQA ve MQA: daha az key, aynı query

Formülde doğrudan değiştirebileceğiniz bir yer var. Her query head’inin kendine ait bir key ve value head’i olması gerekmez — birkaç query head tek bir takımı paylaşabilir.

  • Multi-query attention bu fikri en uca taşır: bütün query head’ler tek bir K/V head’ini paylaşır (Shazeer 2019).
  • Grouped-query attention ise kazanan uzlaşma: query head’ler gruplara ayrılır, grup başına bir K/V head düşer (Ainslie vd. 2023).

Yukarıdaki 32 katmanlı, 32 head’li, 100.000 token’lık modelde:

şema KV head KV önbelleği küçülme
MHA 32 52,4 GB 1×
GQA 8 13,1 GB 4×
MQA 1 1,6 GB 32×

MQA’nın 32 katlık küçülmesi cezbedici ama çoğu zaman biraz fazla agresif — GQA makalesinin deneylerinde MQA kalite kaybettirdi ve eğitimi daha kararsızdı. 4 ya da 8 gruplu GQA, tasarrufun büyük kısmını korurken o deneylerde kalitenin neredeyse tamamını geri kazandı; Llama 2 70B, Llama 3, Mistral ve o günden beri çıkan birçok modelin onunla gelmesinin sebebi bu. Bir verimlilik hilesinin doğruluktan çok az ödün verdiği ender durumlardan biri; yaygınlaşmasının sebebi de bu (kendi modeliniz ve göreviniz için kalite–hız dengesini yine ölçün).

Son olarak şunu fark edin: bu, ayarladığınız değil, devraldığınız bir karardır. Bir MHA checkpoint’ini servis yapılandırmanızda GQA’ya çeviremezsiniz. Elbette dönüştürebilirsiniz: GQA makalesinin kendi tarifi her gruptaki key ve value head’lerinin ortalamasını alarak onları birleştirir, ardından özgün ön eğitim hesabının kabaca %5’i kadar uptraining (mimari dönüşümden sonra yapılan ek eğitim) yapar (Ainslie vd. 2023). Ama bir ön eğitim koşusunun %5’i de nihayetinde bir eğitim koşusudur: modelin ölçeğine göre bir küme, bir bütçe ve günler ile haftalar arası bir süre ister. Pratikte “KV önbelleği ne kadar büyük?” sorusu, optimizasyon listenize değil, model seçim listenize aittir.

Karesel terimden ve büyüyen önbellekten kaçmanın diğer yolları. Bu kısımdaki teknikler üç ayrı kola ayrılır. Tam attention’ı hızlandıran çekirdek (kernel) optimizasyonları — FlashAttention gibi — sonucu değiştirmez, yalnızca bellek trafiğini azaltır. Seyrek ya da pencereli attention her token’ın yalnızca bir alt kümeye bakmasına izin verir: kayan pencere (sliding-window) attention’da token yalnızca son \(w\) token’ı görür ve uygulama eski K/V kayıtlarını bırakıyorsa o katmanın önbelleği \(w\) ile sınırlı kalır (Mistral 7B’nin ilk sürümü bütün katmanlarda, Gemma 2 ve 3 katmanların bir kısmında kullanır). Önbelleği sıkıştıran ya da attention’ın yerini alan yöntemler daha da ileri gider: DeepSeek’in MLA’sı (multi-head latent attention) key ve value’ları düşük boyutlu bir gizli vektöre sıkıştırıp önbellekte onu tutar; doğrusal attention ve durum-uzayı (state-space) katmanları — Mamba, Gated DeltaNet — geçmişi sabit boyutlu bir durumda özetler ve \(n \times n\) matrisini hiç kurmaz. Güncel modellerin bir kısmı bunları melezler: kitapta ölçtüğümüz qwen3.6:35b-a3b’nin 40 katmanının 30’u Gated DeltaNet, yalnızca 10’u tam attention’dır (resmî yapılandırmasındaki layer_types alanı; 15 Ekim 2026’da kontrol edildi); dolayısıyla yukarıdaki KV önbelleği formülü o modelde yalnızca bu 10 katmana uygulanır, diğer 30 katman ise sabit boyutlu bir durum tutar.

7.7 Mixture of Experts

Her transformer bloğunun üçte ikisi feed-forward alt katmanıdır ve her token bunun tamamından geçer. Bir mixture of experts (uzman karışımı, kısaca MoE) ise her token’ın gerçekten hepsinden geçmesi gerekip gerekmediğini sorar.

Bir bloktaki tek feed-forward ağının yerine onun \(E\) kopyasını, yani uzmanları koyun. Bunlara, her token için uzmanları skorlayıp token’ı en iyi \(k\) tanesine gönderen küçük, öğrenilmiş bir router (yönlendirici) ekleyin; aşağıdaki örnekte \(k = 2\), yeni modellerin birçoğunda ise \(k = 8\). Yalnızca o \(k\) uzman çalışır. Diğer \(E - k\) tanesi o token için boşta kalır ve aynı cümledeki başka bir token farklı bir çift seçebilir. Bu, en basit MoE; gerçek modellerde uzmanların genişliği, her token’ın uğradığı paylaşılan uzmanlar ve MoE’nin kaç katmanda kullanıldığı değişebiliyor (aşağıda).

mixture of experts (uzman karışımı, MoE) — tek bir feed-forward ağının yerine çok sayıda ağ koyar ve her token’ı bunlardan birkaçına yönlendirir; böylece toplam parametre sayısı ile token başına hesap artık aynı sayı olmaktan çıkar. ↩︎

Bir token, sekiz uzman, ikisi çalışıyor
token Router 8'ini skorlar uzman 1 uzman 2 0,62 uzman 3 uzman 4 uzman 5 uzman 6 0,38 uzman 7 uzman 8 + çıktı n × d 8 UZMAN, TOP-2 YÖNLENDİRME, d = 4096 TOPLAM 36,5B — hepsi bellekte durmalı AKTİF 10,7B — token başına hesabı belirler GPU'yu ilk sayı için kiralıyorsunuz. Her token kendi çiftini seçer; aynı cümledeki başka bir kelime bambaşka bir rota izleyebilir.

Router, uzmanları skorlayıp en iyi \(k\) tanesini normalleştiren küçük ve öğrenilmiş bir katmandır. Sekiz kutudan altısı bu token için hiç aritmetik yapmaz — ama sekizi de bellekte yer kaplar.

Bu, başından beri geçerli olan bir bağı koparır: toplam parametre sayısı ile token başına hesap artık aynı sayı değildir. \(d = 4096\), 32 katman, 8 uzman, top-2 yönlendirme alalım:

toplam parametre token başına aktif
yoğun (dense) 6,4B 6,4B
MoE, 8 uzman, top-2 36,5B 10,7B

Bu varsayımsal tabloda (embedding’ler hariç) MoE modeli yoğun modelin 5,7 katı parametre tutuyor ve token başına 1,67 katı aktif parametre kullanıyor. Bellek faturasını ilk sayı belirliyor; ikincisi ise token başına aritmetiğin birinci mertebeden kestirimi, bir gecikme rakamı değil. Fiilen beklediğiniz süre, yönlendirmenin uzmanlara ne kadar dengeli dağıldığına, o dağılımın getirdiği iletişim maliyetine, batch boyutuna ve bant genişliğine de bağlı.

Belleği toplam için ayırır, aritmetiği aktif olan için yaparsınız. 36,5B parametrenin tamamı erişilebilir olmalı — router’ın seçimi token başına olduğu için herhangi bir uzmana herhangi bir anda ihtiyaç duyulabilir. Bu ağırlıklar birden çok GPU’ya bölünebilir, nicemlenebilir ya da kısmen CPU belleğine taşınabilir; her seçim bellek ihtiyacını, iletişimi ve gecikmeyi birlikte değiştirir. Bir tanıtım yazısındaki “düşük aktif parametre sayısı” size hızdan bahseder, ne satın almanız gerektiğinden değil. Açık ağırlıklı model duyurularında en çok yanlış okunan tek sayı budur ve kendi sunucunuzda barındırmanın maliyetini çıkarırken (Bölüm 31) işinizi doğrudan etkiler.

Fikir 1990’ların başına kadar uzanıyor; seyrek, kapılı biçimiyle büyük ölçekte kullanılması 2017’ye dayanıyor (Shazeer vd. 2017). Switch Transformer yönlendirmeyi token başına tek uzmana sadeleştirip ölçeği büyüttü ve yaklaşımın yaygınlaşmasında önemli bir adım oldu (Fedus vd. 2022). Pratikte zor olan kısım yönlendirme kuralı değil, yük dengeleme: router’ın neredeyse her token’ı aynı birkaç uzmana gönderip geri kalanları aç bırakmasını engelleyen hiçbir şey yok. Bu da parasını ödediğiniz parametreleri boşa harcar ve eğitimi tamamen bozabilir.

Yönlendiricinin kendisi tek bir matris. \(x\) token’ı için \(s = xW_r \in \mathbb{R}^{E}\) üretiyor, en yüksek \(k\) skoru tutuyor, onları yeniden normalleştiriyor ve yalnızca o uzmanları karıştırıyor:

\[ \operatorname{MoE}(x) = \sum_{e \,\in\, \operatorname{top-}k(s)} \tilde g_e\, E_e(x), \qquad \tilde g_e = \frac{\exp s_e}{\sum_{e' \in \operatorname{top-}k(s)} \exp s_{e'}} \]

Yük dengeleme için klasik çözüm, dengesiz kullanımı cezalandıran yardımcı bir kayıp — ana amacın seve seve düştüğü bir hata moduna karşı açıkça konulmuş bir düzeltme. Ama bu, birinciyle çatışan ikinci bir amaç ve bazı büyük açık MoE’ler onu ikinci plana attı: DeepSeek-V3 dengeyi ağırlıklı olarak uzman başına bir bias terimiyle kuruyor. Bu terim yönlendirme skoruna ekleniyor, çevrimiçi ayarlanıyor ve kendine ait bir gradyan taşımıyor. “Ağırlıklı olarak” burada kilit ifade, çünkü model bu mekanizmanın yanında küçük, tamamlayıcı bir dizi düzeyi (sequence-wise) dengeleme kaybını da koruyor. Yardımcı kaybın var ya da yok olduğunu varsaymak yerine, modelin gerçekte hangi mekanizmayı hangi oranda kullandığına bakın.

Yukarıdaki tablodaki %29’luk aktif oran, MoE’nin değil bu yapılandırmanın bir özelliği. Alanın gittiği yön daha ince taneli uzmanlara (fine-grained experts: daha çok sayıda, daha küçük uzman ağ) doğru: top-2 ile 8 büyük uzman yerine, top-8 ile 64–256 dar uzman ve her token’ın uğradığı bir ya da iki paylaşılan uzman. Aynı toplam, çok daha küçük aktif oran: toplamı yaklaşık 35B olan bir modelin aktif parametresi bu sayede 10,7B yerine 3B civarına iner; qwen3.6:35b-a3b (Bölüm 12) bunu yapıyor. Bir lansman yazısı okurken bakmanız gereken şey aktif/toplam oranıdır (uzman sayısına, top-\(k\)’ya, uzman genişliğine ve paylaşılan parçalara bağlı); bilinen yayımlanmış modellerde bu oran yaklaşık %30 ile %3 arasında değişiyor.

7.8 Ölçekleme yasaları ve neyi öngördükleri

Transformer’larda alışılmadık ölçüde temiz ve çok geniş bir aralıkta görülen bir özellik ortaya çıktı: büyüttüğünüzde, incelenen aralıkta, kayıpları önceden kestirebileceğiniz bir biçimde düşüyor. Bu özellik transformer’a özgü değil — daha önceki çalışmalar LSTM ve CNN’lerde de kuvvet yasaları bulmuştu, aşağıdaki çalışma da LSTM’lerin benzer biçimde, yalnızca daha kötü bir sabitle ölçeklendiğini raporluyor. Kayıp parametreye, veriye ve hesaba göre bir kuvvet yasası izliyor — log-log eksenlerde, çok sayıda büyüklük mertebesi boyunca düz bir çizgi (Kaplan vd. 2020). Bunlar ampirik uyumlar: ölçülen aralığın dışına taşındığında ya da veri, mimari veya eğitim düzeni değiştiğinde katsayılar ve üsler de değişebilir. Birilerinin tek bir eğitim koşusuna dokuz haneli meblağlar harcamayı göze almasının sebebi bu öngörülebilirlikti.

Analizin ilk sürümü, modeli veriden çok daha hızlı büyütmeyi öneriyordu. Yanlıştı; üstelik önemli ve öğretici bir biçimde yanlıştı, çünkü hata aritmetik değil yöntemseldi. Chinchilla yazarlarının açıklamasına göre her koşu, kaç token’la eğitildiğine bakılmaksızın aynı öğrenme oranı programını kullanmıştı; bu yüzden daha az token’daki kayıp, programı henüz tamamlanmamış koşulardan okunmuş ve düzgün programlanmış bir koşunun vereceğinden daha kötü görünmüştü. Sonraki yeniden incelemeler farkın başka kaynaklarını da gösterdi — son katmanın hesap maliyetinin sayılmaması, parametrelerin embedding’ler hariç sayılması ve küçük ölçekte yapılan analiz, ısınma süresi, ölçeğe göre yeniden ayarlanmayan optimizasyon ayarları. Öğrenme oranı programını tek neden değil, nedenlerden biri olarak okuyun. Düzgün biçimde tekrarlandığında farklı bir cevap çıktı — sabit bir hesap bütçesi için parametreler ve token’lar kabaca aynı hızda büyümeli, parametre başına yaklaşık 20 token (Hoffmann vd. 2022). Bu, sabit bir eğitim hesabı altında kaybı en aza indiren yaklaşık bir oran; veri kalitesini ve çıkarım maliyetini hesaba katmıyor (aşağıdaki ikinci çekince):

model parametre eğitim token’ı parametre başına token
Gopher 280B 300B 1,1
Chinchilla 70B 1,4T 20

Chinchilla, Gopher’ın dörtte biri büyüklüğünde; aynı hesap bütçesini kullandı ve onu geniş bir yelpazede geride bıraktı. Bir model kuşağı fena hâlde az eğitilmişti ve bu fark edilmemişti, çünkü herkes aynı ölçekleme yasasını okuyordu.

İki çekince var; ikisine de ihtiyacınız olacak.

Yasalar kaybı öngörür, yeteneği değil. Kayıp (loss), ayrılmış (held-out) metin üzerindeki ortalama negatif log-olabilirliktir. Pürüzsüz bir eğri çizer, ama sizin asıl derdiniz o değildir. Bir modelin çok adımlı bir talimatı izleyip izleyemeyeceği, kötü bir isteği reddedip reddedemeyeceği ya da aritmetiği güvenilir biçimde yapıp yapamayacağı, bu eğrilerin öngördüğü bir nicelik değildir — üstelik pürüzsüz bir kayıp eğrisi ile tırtıklı bir yetenek eğrisi arasındaki ilişki hâlâ gerçekten tartışmalıdır. Daha düşük kayıp birçok görevde işe yarar, ama bir ölçekleme grafiğinin size “bu model sizin görevinizi yapabilecek” dediğini sanmayın; hedef görevi ayrıca değerlendirin.

Hesap açısından en iyi olan, dağıtım açısından en iyi değildir. Chinchilla, verilen bir kayıp için eğitim hesabını optimize eder. Bir modeli milyonlarca kullanıcıya sunacaksanız, ömür boyu faturasına çıkarım maliyeti hâkim olabilir; o zaman daha küçük bir modeli yasanın önerdiğinden çok daha fazla token üzerinde eğitmek mantıklıdır — sonsuza kadar daha az ödemek için baştan daha çok ödemek. Llama ailesi modellerinin yaptığı budur ve “Chinchilla-optimal’in ötesinde eğitildi” ifadesinin bir hata değil bir tasarım kararı olmasının sebebi de budur. Hangi noktanın doğru olduğunu beklenen istek hacmi, eğitim ve servis donanımı ve kalite hedefi birlikte belirler. Eğitim sürecinin tamamına ileride döneceğiz (Bölüm 8).

7.9 Laboratuvar: attention’ı yazın, sonra profilini çıkarın

Bu laboratuvarın amacı, karesel terim hakkında okumak değil, onu hissetmek. Önce NumPy ile çalışın; bir çerçeveye ancak 3. adımda başvurun.

  1. Ölçeklenmiş iç çarpım attention’ını (Bölüm 7.2) denklemden yazın — izdüşümler, skorlar, ölçekleme, isteğe bağlı maske, softmax, ağırlıklı toplam. Softmax’ı kendiniz yazın ve üs almadan önce satır maksimumunu çıkarın. Hazır attention kütüphanesi yok.
  2. Bu bölümdeki sayıları birebir yeniden üretin. Aynı \(E\), \(W^Q\), \(W^K\), \(W^V\) değerlerini kullanın ve içti için attention satırının [0.4230, 0.5012, 0.0759] çıktığını doğrulayın. Çıkmıyorsa hatanız ya devrik almada ya da normalleştirdiğiniz eksendedir — başka aday yok.
  3. Bir referansla doğrulayın. Birkaç farklı tensör şeklindeki rastgele girdilerde, aynı maske, ölçek ve dropout_p=0 ile torch.nn.functional.scaled_dot_product_attention ile karşılaştırın (bu API’de boolean attn_mask’ın True değeri izin verilen konum demek) ve maksimum mutlak farkı, kullanılan backend’le birlikte raporlayın. fp32’de yaklaşık \(10^{-5}\) üstündeki fark büyük olasılıkla gerçek bir uyuşmazlıktır; fp16 ve bfloat16 için daha gevşek bir tolerans seçin.
  4. Permütasyona eşdeğişkenliği doğrulayın. Maske olmadan token’ları karıştırın, tekrar çalıştırın, çıktıları eski sırasına dizin ve maksimum farkı raporlayın. Seçtiğiniz veri tipinin sayısal toleransı içinde kalmalı (bu bölümde makine epsilonu mertebesindeydi). Sonra bir konumsal kodlama ekleyip sayıyı tekrar raporlayın. Son olarak bütün testi nedensel maskeyle ve konumsal kodlama olmadan tekrarlayın: hiçbir konum sinyali eklemediğiniz hâlde eşdeğişkenlik bozulur. Bunun sebebini, bu bölümü okumamış birine anlatın.
  5. Üssü ölçün. \(d = 64\) alın, geri kalan her şeyi sabit tutun ve attention’ın süresini \(n = 128, 256, \dots, 4096\) için, ısınma turlarından sonra, tekrarlayarak ve (GPU’daysanız) eşzamanlayarak ölçün. Log-log eksenlerde çizip yerel eğimleri kestirin. Teorik maliyet \(O(n^2 d)\) olsa da duvar saati eğiminin tam 2 olması gerekmez: küçük \(n\)’de sabit maliyetler, büyük \(n\)’de bellek ve kernel değişimleri araya girer; hangisinin nerede devreye girdiğini açıklayın.
  6. Belleği ölçün. Önce ham \(n \times n\) skor tensörünün beklenen bayt sayısını hesaplayıp tabloyla (Bölüm 7.6) karşılaştırın; sonra sürecin tepe tahsisli ve ayrılmış belleğini ayrı ayrı ölçün (geçici tensörler ve bellek ayırıcısı yüzünden farklı çıkabilirler). Makinenizin pes ettiği \(n\) değerini bulun. Raporlayın.
  7. Nedensel maske ekleyin, sonra çalıştığını gözle inceleyerek değil, davranışsal olarak kanıtlayın: bir dizide çalıştırın, sona bir token ekleyip tekrar çalıştırın ve önceki konumlardaki çıktıların değişmediğini doğrulayın. Bir sonraki adımı meşru kılan şey bu değişmezdir.
  8. Bir KV önbelleği kurun. Aynı başlangıç öneki, ağırlıklar, veri tipi ve donanımla 512 token’ı teker teker üretin; önce her adımda her şeyi yeniden hesaplayarak, sonra K ve V’yi önbelleğe alarak. Prefill süresini decode adımlarından ayrı ölçün. İkisi için de token başına gecikmeyi çizin ve 512. token’daki hızlanmayı raporlayın. Önbelleğin bayt cinsinden boyutunu (Bölüm 7.6) formülden hesaplayın ve sürecinizin gerçekte ayırdığı bellekle karşılaştırın.

Teslim edilecek: kestirdiğiniz üssü içeren log-log grafiği, token başına gecikmeyi gösteren iki eğri ve donanımınızın kaldırabildiği en uzun dizi hakkında kısa bir not — tükenen kaynağın adını da vererek.

7.10 Sezgi kontrolleri

Her birini açmadan önce cevaplayın. Bunlar, transformer’a dair standart zihinsel modelin paraya mal olacak biçimde yanlış olduğu altı yer.

Genelde doğru değil; buna inanırsanız da yanlış şeyi optimize etmeye koyulursunuz.

Katman başına iki terim şu: izdüşümler ve feed-forward için \(24nd^2\), attention için \(4n^2d\). \(n = 6d\)’de eşitleniyorlar — yani 4.096 genişliğindeki bir model için dönüm noktası 24.576 token. 4.096 token’lık bir bağlamda attention aritmetiğin %14’ü, feed-forward ve izdüşüm matrisleri ise %86’sı (tam multi-head attention ve \(d_{\text{ff}} = 4d\) varsayımıyla, işlem sayısı olarak).

Karesel terim uzun bağlamda çarptığınız bir duvardır, her uzunlukta ödediğiniz bir vergi değil. Attention’ı optimize etmeden önce hangi rejimde olduğunuzu hesaplayın; \(n < 6d\) ise aritmetiğin çoğu feed-forward tarafında. Ama \(n = 6d\) bir duvar saati eşiği değil: prefill ile decode farklı davranır ve attention bellek trafiği yüzünden yine darboğaz olabilir. Son kararı ölçülen profil verir.

Çünkü üç farklı soruyu cevaplıyorlar. Ayrım şuna göre değişir: aynı vektörü hem query hem key olarak kullanmak (tek bir paylaşılan izdüşüm) skor tablosunu simetrik yapar; tek bir öğrenilmiş \(M\) ile \(x_i M x_j^{\top}\) ise yönlü skorlar verebilir ama pahalıdır (bkz. İki matris mi, bir matris mi?). Aşağıdaki paylaşılan izdüşüm durumu.

\(Q\), bir token’ın aradığı şeydir; \(K\), bulunabilmek için ilan ettiği şeydir; \(V\) ise bulunduğunda gerçekte kattığı şeydir. \(Q\) ile \(K\) birlikte bilginin nereye aktığına karar verir; aktarılan içeriği \(V\) sağlar. Bunları birbirine bağlarsanız bir token’ın ilanı sorgusuyla aynı hâle gelir. Böylece skor tablosu simetrik bir Gram matrisi (bütün ikili iç çarpımların tablosu) olmak zorunda kalır. Satır normalleştirmesi iki yönde yine de farklı ağırlıklar üretebilir; ama iki yön de aynı elemanı okuduğu için head bu ağırlıkları seçme yeteneğini kaybeder. Bu eşitleme ayrıca her token’ı kendine yönelmeye iter, çünkü bir komşu daha uzun değilse \(x \cdot x\) satırdaki en büyük skordur; bu ikinci etki bir eğilimdir, garanti değil.

Çözümlü örnek (Bölüm 7.2) bunu somut olarak gösteriyor: örneğin işlemesini sağlayan skoru, girdilerin izdüşümü alınmamış hiçbir iç çarpımı üretemezdi.

Bu örneğin varsayımlarıyla (tam MHA, fp16, tek GPU): bellek — ama modelin belleği değil. fp16’daki 7B’lik bir model 14 GB ağırlık demek. 100.000 token’da KV önbelleği 52,4 GB — tek bir sohbet için modelin kendisinin neredeyse dört katı — ve bu rakam eşzamanlı istek başına, oysa ağırlıklar hepsi arasında paylaşılıyor.

Bu yüzden bir GPU’nun kaç kullanıcıya hizmet verebileceğini genelde parametre sayısı değil, KV önbelleği belirler. GQA’nın evrenselleşmesinin sebebi de bu: 32 KV head’den 8’e inmek aynı önbelleği 52,4 GB’tan 13,1 GB’a indiriyor; yayımlanmış deneylerde kalitede çok az bedelle 4 kat tasarruf. GQA, KV nicemleme ya da birden çok GPU devreye girince ilk tükenen kaynak bant genişliği ya da başka bir bellek kalemi olabilir.

“\(n \times n\) skor matrisi” dediyseniz, bu FlashAttention’dan önceki doğru cevaptı; FlashAttention o matrisi hiç somutlaştırmıyor.

Bunlar iki farklı cevabı olan iki farklı soru; mimarinin bütün meselesi de bu.

35B parametrenin hepsini bellekte tutmanız gerekir, çünkü herhangi bir token herhangi bir uzmana yönlenebilir. Ama token başına yalnızca en iyi \(k\) uzman çalışır — yaklaşık 3B aktif — dolayısıyla token başına aritmetik küçük sayıya göre şekillenir. Toplam parametre bellek faturanızı, aktif parametre token başına aritmetiğinizi belirler; gecikme ise buna ek olarak bant genişliğine, yönlendirmenin dengesine ve dağıtım düzenine bağlı.

Bölüm 12.9 bunu ölçtü: qwen3.6:35b-a3b bir belgeyi 0,353 saniyede sınıflandırıyor; yoğun 9,7B’lik bir model için bu süre 0,365 saniye, yoğun 27,4B için 0,592 saniye (aynı dizüstü bilgisayarda, ollama ile, dördü de 4 bit’e nicemlenmiş, belge başına medyan). Yani bu kurulumda MoE modeli ikisinden de hızlı, üstelik ikisinden de yüksek puan alıyor; diskte 23,9 GB’lık nicemlenmiş bir dosya, ki bu çalışma belleğine birebir eşit değil. Başka donanımda ve başka bir görevde sıralama değişebilir.

Öğrenilmiş mutlak konumlarla hiç iyi bir şey olmaz: tabloda 1.025. satır yoktur, dolayısıyla modelin ekleyecek bir vektörü yoktur. GPT-2’nin standart ileri geçişi daha uzun bir diziyi kabul etmez — yaşanan şey bir kalite düşüşü değil, bir arama hatasıdır (uygulamalar girdiyi kırparak ya da kayan bir pencereyle bunun etrafından dolaşabilir).

Alanın RoPE ve ALiBi’ye geçmesinin somut sebebi budur. RoPE konumu bir döndürme olarak kodlar, böylece iki token arasındaki skor yalnızca aralarındaki kaydırmaya bağlı olur. Ölçüm şöyle (Bölüm 7.4): sabit bir kaydırmadaki aynı query-key çiftini 0, 100, 1.000 ve 5.000 mutlak konumlarında değerlendirdiğimizde skorlar en fazla \(5 \times 10^{-13}\) kadar değişiyor; konum vektörü embedding’e eklendiğinde ise yayılım 26 civarındadır. Konum yalnızca bir kaydırma olarak girdiğinde, ortada eksik kalabilecek bir 1.025. satır yoktur.

Bunun neyi sağlamadığına dikkat edin: eğitim uzunluğunun çok ötesine ekstrapolasyon kaliteyi yine de çoğu zaman bozar; kaliteyi ölçmek gerekir. Sert bir başarısızlığı ortadan kaldırıp yerine yumuşak bir tanesini koyar.

Çünkü her alt katmanın okuduğu ve yazdığı tek şey odur ve genişliği hiç değişmez.

Attention da feed-forward ağı da residual akışı okur ve ona bir düzeltme ekler. Bu yüzden blokları üst üste dizmek çok kolaydır — 12. blok birebir 1. blok gibi görünür. Pre-norm’da normalleştirme her dalın girişinde: dal, residual akışı (önceki bütün toplamaları) normalleştirilmiş hâliyle okur ve kendi çıktısını normalleştirmeden akışa ekler.

Pratikteki sonuç: bir transformer’ın belli bir derinlikte “ne bildiğini” incelediğinizde, herhangi bir katmanın çıktısını değil residual akışı okursunuz. Ağın ortasındaki bir katmandan vektör çıkaran her teknik için de aynısı geçerlidir — fine-tuning (ince ayar) kısmında (Bölüm 12.3) karşılaştırma için kullanılan dondurulmuş (frozen) encoder embedding’leri de dâhil.

7.11 Mülakat soruları

Mülakatlarda transformer soruları genellikle bir mimari şemayı ezberden sayabilmenizi değil, bir sistem hakkında aritmetik yapabilmenizi ölçer. Aşağıdaki cevaplar sayılarla birlikte veriliyor.

“Attention’ı anlat” denirse, softmax formülüyle başlama dürtüsüne direnin. Attention’ın ne işe yaradığıyla başlayın — her token diğer her token’a bakabilir ve her birinden ne kadar bilgi alacağını öğrenebilir — sonra formülü türetin: Q/K skorları, softmax ağırlıkları, V karışımı. Bu sıra, hesapla işlev arasındaki bağı görünür kılar.

  1. Regresyon bilen birine self-attention’ı anlatın. Öğrenilmiş bir ağırlıklı ortalamadır. Her token bir query öne sürer, her token bir key ilan eder, iç çarpımlar softmax’tan geçerek ağırlığa dönüşür ve çıktı, bu ağırlıkların value’lara uygulanmış hâlidir. Alışılmadık olan kısım şu: eğitim Q/K/V izdüşümlerinin parametrelerini öğrenir, ama attention ağırlıkları bir kez kestirilmez, her girdi için çıkarım anında yeniden hesaplanır — aynı kelimenin farklı bir cümlede farklı bir vektör almasının sebebi budur. Sonra ölçeklemeden söz edin: skorlar \(\sqrt{d_k}\)’ya bölünür, çünkü bileşenleri bağımsız ve birim varyanslı \(d_k\) boyutlu vektörlerin iç çarpımları \(\sqrt{d_k}\) ile orantılı büyür ve ölçeklenmemiş bir softmax doyuma yaklaşır.

  2. Attention’dan sonra neden bir feed-forward ağı var? Çünkü attention token’ları karıştırır ama ağırlıkları verildiğinde bunu value’lar açısından doğrusal biçimde yapar — içerdiği doğrusal olmayanlık, taşıdığı şeyi işleme biçiminde değil, ağırlıkları hesaplama biçiminde yatar. Her token’ın kendine özgü doğrusal olmayan bir işlemden geçtiği yer feed-forward alt katmanıdır; bir okumaya göre \(W_1\) bir dedektör bankası, \(W_2\) ise her dedektörün tetiklendiğinde yaydığı şeydir. Oranı doğru bilin, çünkü adayların çoğu tersini söylüyor: bu bölüm boyunca kullanılan standart \(d_{\text{ff}} = 4d\) bloğunda bir transformer bloğunun parametrelerinin üçte ikisi feed-forward alt katmanıdır, attention değil. Oran bu \(d_{\text{ff}}\) tercihinden ve tam genişlikli attention’dan çıkar; GQA, SwiGLU ya da MoE kullanan bir blok için yeniden hesaplayın.

  3. Bir modelin GPU’nuza sığıp sığmadığını nasıl kestirirsiniz? İlk yaklaşım olarak iki sayıyı toplarsınız. Ağırlıklar: parametre × parametre başına bayt (fp16’da 7B = 14 GB). KV önbelleği: \(2 \times \text{katman} \times \text{KV head} \times \text{head boyutu} \times \text{token} \times \text{bayt}\), eşzamanlı istek başına. Çözümlü örneği verin: 32.768 token’da 7B’lik bir model 14 GB ağırlık ve 17,2 GB ham önbellek ister; yani bağlam şimdiden modelden pahalıya gelmiştir. Sonra aktivasyonlar, attention çalışma alanı, bellek ayırıcısı ve dağıtım payı için yer bırakın; gerçek ihtiyaç bundan yüksek olur. Kıdemli bir cevap, maliyeti düşürecek yöntemi de adlandırır — GQA, nicemleme ya da daha kısa bağlam — ve önce hangisine başvuracağınızı söyler.

  4. FlashAttention gerçekte neyi değiştirir? Matematiği değil: aynı fonksiyonu hesaplar. Bloklar (tile) hâlinde yapılan toplamlar kayan nokta toplamasının sırasını değiştirdiği için yalnızca son basamaklar oynar. Değiştirdiği şey belleğe neyin yazıldığıdır: \(n \times n\) skor matrisi hiç somutlaştırılmaz, bunun yerine hızlı yonga içi bellekte kalan bloklar hâlinde hesaplanır. Uzun dizilerde bu, attention’ı bellek bant genişliği sınırlı olmaktan çıkarıp hesap sınırlı olmaya yaklaştırır; duvar saati etkisini dizi uzunluğu, head boyutu ve donanımla ölçün. Motivasyonu veren sayı: 100.000 token’da tek katmandaki tek bir head için tek bir skor matrisi fp16’da 18,63 GiB. Bunu 32 head ve 32 katmanla çarpın; naif sürüm yavaş değil, imkânsızdır.

  5. Encoder-only mu, decoder-only mu, encoder-decoder mı? Self-attention düzeyinde ayrım, attention matrisinin hangi girdilerinin sıfırdan farklı olmasına izin verildiğiyle ilgilidir; encoder–decoder ise buna ek olarak iki yığını cross-attention ile birbirine bağlar. Encoder-only her şeyi iki yönlü görür ve tam bir metnin temsiline ihtiyacınız olduğunda doğru tercihtir — sınıflandırma ve arama; Bölüm 12’deki sonuçların hepsi de buna dayanıyor. Decoder-only nedensel olarak maskelenmiştir ve üretim için gereken odur. Encoder-decoder ikisini birden tutar ve decoder’ın encoder’a bakmasına izin verir; çeviride varlığını sürdürmesinin sebebi budur. Pratik sürümü söyleyin: etiketli sınıflandırmada encoder güçlü bir başlangıç noktası olmayı sürdürüyor (bu kitaptaki ölçüm için bkz. Bölüm 12.9); seçimi kalite, gecikme ve servis yüküyle kendi görevinizde verin.

  6. Transformer’lar RNN’lerin yerini neden aldı? Temelde ifade güçleri daha yüksek olduğu için değil — eğitimde paralelleştirilebildikleri için. Bir RNN \(t+1\)’inci token’dan önce \(t\)’inci token’ı işlemek zorundadır; transformer bir katmandaki bütün konumları aynı anda hesaplar (katmanlar yine sırayla gelir), bu da ardışık bir bağımlılığı matris çarpımına çevirir ve GPU’yu düzgün kullanmanızı sağlar. Bedeli, yoğun attention’daki karesel terim ve sonlu bir bağlam penceresidir (RNN’in bağlamı da ilkesel olarak sınırsızdır, ama sonlu bir gizli durumda özetlenir). Şunu net söyleyin: bu, ölçeğin önünü açan bir mühendislik zaferiydi ve yetenekleri üreten şey ölçekti. Bölüm 7.8 bu argümanı nicel olarak kuruyor.

7.12 Daha derine inmek için

Bu bölümü iyi tamamlayan üç dış görselleştirme var: adım adım şemalar için Illustrated Transformer, tarayıcınızda canlı bir GPT-2 çalıştıran Georgia Tech’in Transformer Explainer aracı ve Brendan Bycroft’un 3D LLM gezintisi.

İzlemeyi tercih ediyorsanız üç kaynaktan başlayabilirsiniz: geometri için 3Blue1Brown’ın Transformers, the tech behind LLMs ve Attention, step-by-step videoları; aynı mimarinin satır satır yazıldığı Karpathy’nin Let’s build GPT: from scratch videosu; ve — o tempo fazla hızlıysa — eğitimi ve çıkarımı da içeren, Umar Jamil’in daha yavaş Coding a Transformer from scratch in PyTorch videosu. Stanford’un CS25: Transformers United ise yazarların kendi makalelerini sunduğu bir seminer dizisi.

Birincil kaynaklar alışılmadık ölçüde okunaklı ve bu bölümden sonra onları doğrudan okuyabilecek bilgiye sahipsiniz. Vaswani vd. (2017) ile başlayın — kısa bir makale ve temel denklemlerini artık biliyorsunuz; eğitim, optimizasyon ve deney ayrıntıları ise ayrıca okunmalı. Sonra, kabaca bu bölümün onlara ihtiyaç duyduğu sırayla:

konu makale
mimarinin kendisi Vaswani vd. (2017)
residual bağlantılar He vd. (2016)
layer norm ve yerinin değişmesi Ba vd. (2016); Xiong vd. (2020)
head’ler gerçekte ne öğreniyor Clark vd. (2019); Michel vd. (2019)
döndürmeli konumlar Su vd. (2024)
mesafe cezasıyla konum Press vd. (2022)
kesin, G/Ç’yi (bellek trafiğini) gözeten attention Dao vd. (2022)
KV önbelleğini küçültmek Shazeer (2019); Ainslie vd. (2023)
seyrek uzmanlar Shazeer vd. (2017); Fedus vd. (2022)
ölçekleme yasaları ve düzeltmesi Kaplan vd. (2020); Hoffmann vd. (2022)

Bir okuma listesi yerine tüm yığını çalıştırılabilir kodla türeten tek bir kitap istiyorsanız, Dive into Deep Learning (Zhang vd. 2023) çevrimiçi ücretsiz ve attention ile transformer’ları doğrusal cebirden başlayarak işliyor.

7.13 Alıştırmalar

  1. Etkileşimli attention aracında zamir head’i ağırlığını coffee ile robot arasında kabaca 46/34 bölüyor — kahveye meyilli, ama kıl payı (ve bu matrisi elle kurduk). ready yerine konabilecek, okumayı belirgin biçimde bir tarafa itmesi beklenen üç tek kelimelik ikame yazın ve her biri için o yorumu destekleyen, dilbilgisine değil dünyaya dair varsayımı belirtin. Sonra bunun, tek bir attention haritasından anlam çıkarmanın sınırı hakkında ne söylediğini tartışın.
  2. Attention tek başına sıra körüdür. Temsil karşılaştırmasında (Bölüm 4) ortalama alındığında kosinüsü 1,0000 çıkan kelime sırası çiftini alın. Maskesiz ve konumsuz self-attention’ın token çıktılarının permütasyonla nasıl değiştiğini gösterin; sonra bir konum sinyali ya da nedensel maske eklendiğinde hangi simetrinin kırıldığını ve iki cümlenin temsilinin hangi okuma ya da özetleme (pooling) kuralıyla ayrıştığını açıklayın.
  3. 32 katmanlı, 32 head’li ve head boyutu 128 olan bir model 100.000 token’lık bağlam sunuyor. Tam multi-head attention (32 KV head) ve 16 bitlik K/V varsayarak ham KV önbelleğinin boyutunu GB ve GiB cinsinden hesaplayın. Ardından aynı şekli 8 KV head’li GQA ile karşılaştırıp grouped-query attention’ın neden var olduğunu açıklayın.
  4. Çözümlü örnekte dördüncü embedding boyutu üç token için de 0,5 ve \(W^Q\) ile \(W^K\)’nın o boyuta karşılık gelen satırları sıfır seçildiği için hiçbir attention ağırlığına katkı vermedi. Bunun ne kadar genellendiğini iki parçada bulun. (a) Key tarafının her zaman sadeleştiğini kanıtlayın: \(W^K\) sabit bir özniteliğe hangi satırı atarsa atasın, katkısının her key \(j\) için aynı olduğunu ve dolayısıyla softmax’ta yok olduğunu gösterin. (b) Sonra query tarafının sadeleşmediğini bir karşı örnekle gösterin. \(W^Q\)’ya sabit öznitelik için sıfırdan farklı bir satır verin, içtinin attention ağırlıklarını yeniden hesaplayın ve ne kadar oynadıklarını raporlayın. Aynı özniteliğin neden bir izdüşümde önemli, diğerinde önemsiz olabildiğini tek cümleyle açıklayın.
  5. \(W^Q\)’yu ve \(W^K\)’yı \(1/\sqrt{3}\) ile çarpın; böylece ikisinin ölçeklerinin çarpımı 3 yerine 1 olur. içtinin attention satırını yeniden hesaplayın. Ağırlıkları raporlayın ve gözlemlediğiniz şey üzerinden \(1/\sqrt{d_k}\) teriminin neye karşı koruduğunu açıklayın.
  6. Bölüm, \(d/h\) genişliğindeki \(h\) head’in \(d\) genişliğindeki tek bir head kadar maliyetli olduğunu iddia ediyor. Bunu \(d = 512\) için \(h = 1, 8, 64\) değerlerinde, \(d_k = d_v = d/h\) varsayımıyla parametre ve çarpma sayarak doğrulayın. Naif bir uygulamadaki \(h n^2\) öğelik skor tensörünü ve head başına skor rankı sınırını da yazın. Sonra \(h\) büyüdükçe gerçekten neyin kaybedildiğini, eşit matris FLOP’unun eşit gecikme anlamına gelip gelmediğini ve bunun hangi noktada canınızı yakmasını beklediğinizi belirtin.
  7. Sinüzoidal kodlamalar için göreli konum tablosunu alın. 32’lik kaydırmadaki ortalama, 16’lık kaydırmadakinden yüksek. Bunun sebebini \(\sum_i \cos(k\omega_i)\) formülünden açıklayın ve sinüzoidal kodlamaların yakın konumları daha benzer yaptığı yolundaki yaygın iddia açısından bunun ne anlama geldiğini söyleyin.
  8. RoPE deneyini yeniden üretin: sabit, rastgele bir query ve key alın. Bu çifti 0, 100, 1.000 ve 5.000 mutlak konumlarında, aralarında 5’lik bir kaydırma olacak biçimde skorlayın. Yayılımı raporlayın. Sonra aynısını toplamalı bir kodlamayla yapın — hangi konum vektörlerini ve izdüşümleri kullandığınızı açıkça yazın — ve o yayılımı da raporlayın. Bu iki sayının matematiksel olarak neyi gösterdiğini ve gerçek bir görevde kalite hakkında neyi göstermediğini bir paragrafta ayırın.
  9. 70B sınıfı bir modelin 80 katmanı ve 64 query head’i var; head boyutu 128. fp16 ağırlıkları 140 GB, dolayısıyla dört adet 80 GB’lık hızlandırıcıdan oluşan bir düğümde — toplam 320 GB — sunuluyor. KV önbelleği hesaplayıcısını kullanarak, yalnızca ağırlıklar ve ham KV önbelleği hesaba katıldığında dört eşzamanlı kullanıcıya sunabileceğiniz teorik en uzun bağlamı önce 64 KV head ile, sonra da bugünün 70B sınıfı modellerinde olduğu gibi 8 KV head ile bulun. Ağırlıkların kartlara dağılımı, aktivasyonlar, çalışma alanı ve güvenlik payı eklenince bunun neden uygulanabilir bir sınır olmadığını da belirtin. İki uzunluğu ve aralarındaki oranı raporlayın. Ardından önbellek formülünde değişen ve değişmeyen bütün nicelikleri adlandırın.
  10. Bir duyuru, bir modeli “toplam 120B parametre, 5B aktif” diye tanıtıyor. Bir meslektaşınız 5B küçük olduğu için modelin 24 GB’lık bir tüketici GPU’sunda çalışacağı sonucuna varıyor. Aritmetiğiyle birlikte iki cümlelik düzeltmeyi yazın; nicemlemenin, birden çok GPU’nun ya da ağırlıkları CPU belleğine taşımanın belleği azaltırken hangi gecikme ve kalite ödünleşimlerini getirdiğini de ekleyin.
  11. Gopher parametre başına 1,1 token, Chinchilla ise 20 token kullandı. Her iki iddia da bir ölçekleme yasası analizinden geldi. Aralarında neyin değiştiğini — hesap bütçesi, parametrelerin embedding dahil mi sayıldığı, öğrenme oranı programı, ölçüm aralığı — açıklayın ve yeni bir ölçekleme yasası makalesine dayanarak bir eğitim bütçesini yeniden dağıtmadan önce o makalede neyi görmeniz gerektiğini (deney aralığı, belirsizlik) belirtin.