İçeriğe geç

Kitaplar / Yöntem ile Sıfırdan Dil Modeli / Bölüm 4 — Sinir ağıyla ikili model

Bölüm 4 — Sinir ağıyla ikili model

Saymak küçük bir pencere için işe yarar ama pencere büyüdükçe imkânsızlaşır. Son 10 karaktere bakan bir model için 30¹⁰, yani yaklaşık 590 trilyon satırlık bir tablo gerekirdi ve bu satırların neredeyse hepsi veride hiç görülmemiş olurdu. Çıkış yolu, tabloyu doldurmak yerine olasılıkları hesaplayan bir fonksiyon öğrenmektir: bir sinir ağı.

Bu bölümde aynı ikili modeli sinir ağıyla kurup sayarak bulduğumuz sonuca ulaşıp ulaşamadığımıza bakacağız. Bu, sonraki bölümlerde kullanacağımız tüm parçaların en sade halidir.

Dört adım

  1. Tek-sıcak kodlama. Karakter numarası 3 ise, 30 sayılık bir vektörün 3. yeri 1, geri kalanı 0 olur. BirSıcak fonksiyonu bunu yapar.
  2. Logitler. Bu vektörü 30 × 30’luk bir ağırlık matrisi W ile çarparız. Sonuç 30 sayıdır; her biri, o karakterin sırada gelme ihtimali için ham bir puandır. Bu puanlara logit denir. Logitler eksi de olabilir, toplamları 1 olmak zorunda değildir.
  3. Softmaks. Logitleri olasılığa çevirir: her birinin üstelini alır, toplama böler.
  4. Çapraz entropi. Doğru karaktere verilen olasılığın eksi logaritmasıdır; 3. bölümdeki kayıpla aynı şeydir.

P(b∣a)=eWab∑xeWaxP(b \mid a) = \frac{e^{W_{ab}}}{\sum_{x} e^{W_{ax}}}

Bu formülün sürpriz bir sonucu vardır. Tek-sıcak bir vektörü W ile çarpmak, W’nin bir satırını seçmekten başka bir şey değildir. Yani ağ, her karakter için bir satır sayı öğreniyor. Bu satıra o karakterin gömmesi (embedding) denir. Büyük dil modellerinin ilk katmanı da tam olarak budur: her simge için bir gömme satırı olan dev bir tablo.

Yöntem
program SinirAğıylaİkiliModel
{ Aynı ikili modeli bu kez sinir ağıyla kuruyoruz. Saymak yerine bir ağırlık
  matrisi W öğreniriz:
     girdi   : önceki karakterin tek-sıcak vektörü x (30 sayı, biri 1, kalanı 0)
     logit   : x @ W        (30 puan, her olası sonraki karakter için bir tane)
     olasılık: softmaks(logit)
  Kayıp çapraz entropidir: doğru sonraki karaktere verilen olasılığın -log'u.
  x tek-sıcak olduğu için x @ W, W'nin bir satırını seçer; bu satıra o karakterin
  "gömmesi" (embedding) denir. Büyük dil modellerinin ilk katmanı tam olarak budur. }
kullan Atasozleri, DilAraclari, YZ

değişken
  veri, harfler: metin dizisi
  numara: sözlük
  girdiler, hedefler: tamsayı dizisi
  a, kar: metin
  i, j, n, önceki, adım: tamsayı
  X, W, kayıp, P: tensör
  opt: herhangi
  sözcük: metin

başla
  Tohum(42)
  veri := Atasozleri.Hepsi
  harfler := DilAraclari.Harfler
  n := Uzunluk(harfler)
  numara := DilAraclari.Numaralar(harfler)

  { Eğitim çiftleri: (önceki karakter, sonraki karakter) }
  girdiler := []; hedefler := []
  veri içindeki her a için
  başla
    önceki := 0
    (a + '.').Karakterler içindeki her kar için
    başla
      girdiler.Ekle(önceki); hedefler.Ekle(numara[kar]); önceki := numara[kar]
    bitir
  bitir
  yaz(Biçimle('{} eğitim çifti', Uzunluk(girdiler)), 1)

  X := BirSıcak(girdiler, n)                   { 5282 × 30 }
  W := RastgeleTensör(n, n, 0.1, doğru)        { öğrenilecek 900 ağırlık }
  opt := Adam([W], 0.1)
  1'den 300'e kadar her adım için
  başla
    opt.SıfırlaGrad
    kayıp := ÇaprazEntropi(X @ W, hedefler)
    kayıp.GeriYay
    opt.Adım
    eğer adım = 1 veya adım mod 50 = 0 ise
      yaz(Biçimle('Adım {:3}: kayıp {:.4}', adım, kayıp.Değer), 1)
  bitir

  yaz('', 1); yaz('Sinir ağının ürettiği "atasözleri":', 1)
  1'den 8'e kadar her j için
  başla
    sözcük := ''; i := 0
    tekrarla
      P := Softmaks(BirSıcak([i], n) @ W)       { 1 × 30 olasılık }
      i := DilAraclari.Örnekle(P.Satır(0).Veri)
      eğer i <> 0 ise sözcük := sözcük + harfler[i]
    i = 0 olana dek
    yaz('  ' + sözcük, 1)
  bitir
bitir.

Programın çıktısı şöyledir:

Çıktı
5282 eğitim çifti
Adım   1: kayıp 3.4135
Adım  50: kayıp 2.3569
Adım 100: kayıp 2.3475
Adım 150: kayıp 2.3449
Adım 200: kayıp 2.3436
Adım 250: kayıp 2.3429
Adım 300: kayıp 2.3424

Sinir ağının ürettiği "atasözleri":
  n ayekacu suşi n a baker
  k sttin klazü
  tükolde dör
  an hvar
  yaven çi mazarüşen sa paye yağ hanevademaserırgere ktademaşmen seyvu dü yema dışiştakomağır bacayliltlezan dancayağadi yoll inden muş
  sugöz umir ban k
  sır
  itanı yolürar

Çıktıyı okumak

Kayıp 3,41’den başlıyor; bu, 3. bölümde hesapladığımız “hiçbir şey bilmeyen model” değerine (ln 30 ≈ 3,40) çok yakındır. Ağırlıklar küçük ve rastgele olduğu için model başta tüm karakterlere hemen hemen eşit olasılık veriyor.

300 adımda kayıp 2,3424’e iniyor. Düzleştirmesiz saymanın kaybı 2,3408 idi. Sinir ağı neredeyse aynı yere vardı ve vardığı yer bir rastlantı değildir: ikili bir model için en iyi olasılıklar, sayımdan çıkan oranlardır. Gradyan inişi bu oranları kendi başına buldu. Üretilen metnin 3. bölümdekine bu kadar benzemesinin nedeni de budur.

Değişen şey yöntemdir. Artık elimizde kayıp fonksiyonunu otomatik olarak küçülten bir düzenek vardır: kayıp.GeriYay her ağırlığın kayba etkisini (gradyanını) hesaplıyor, opt.Adım da ağırlıkları kaybı azaltacak yöne biraz kaydırıyor. Bu düzenek ağ ne kadar karmaşık olursa olsun aynı kalacak. Kitabın geri kalanında yalnızca X @ W kısmını daha akıllı bir fonksiyonla değiştireceğiz.

Neden ÇaprazEntropi’ye logit veriyoruz?

Dikkat ederseniz eğitimde ÇaprazEntropi(X @ W, hedefler) yazıyoruz; araya Softmaks koymuyoruz. ÇaprazEntropi softmaksı kendisi uygular ve bunu sayısal olarak daha güvenli bir yolla yapar. Metin üretirken ise olasılıkları kendimiz istediğimiz için Softmaks’ı elle çağırıyoruz.

Alıştırma. Eğitimden sonra “ş” harfinin satırına softmaks uygulayınız: Softmaks(BirSıcak([numara['ş']], n) @ W). En büyük beş olasılığı 3. bölümdeki “ş harfinden sonra en sık gelenler” listesiyle karşılaştırınız.