İçeriğe geç

Kitaplar / Yöntem ile Sıfırdan Dil Modeli / Bölüm 9 — Metin üretmek

Bölüm 9 — Metin üretmek

Eğitilmiş bir dil modeli metin yazmaz; her adımda bir olasılık dağılımı verir. Bu dağılımdan bir karakteri nasıl seçtiğimiz ise çıkan metnin karakterini belirler. Aynı model, seçim yöntemine göre sıkıcı ve tekrarcı da olabilir, yaratıcı ve dağınık da. Sohbet uygulamalarındaki “sıcaklık” ayarı da tam olarak bu bölümün konusudur.

Üç yöntem

Açgözlü seçim. Her adımda en olası karakteri alırız. Rastgelelik yoktur; aynı başlangıç her zaman aynı metni verir. Kulağa en mantıklı yöntem gibi gelir ama genellikle kötü sonuç verir. Model kendi ürettiği metne bakarak karar verdiği için kolayca bir döngüye girer.

Sıcaklık. Örneklemeden önce logitleri bir T sayısına böleriz. T < 1 iken büyük logitler daha da öne çıkar; model daha emin ve tutucu davranır. T > 1 iken dağılım düzleşir; düşük olasılıklı karakterler de seçilmeye başlar. T = 1 modelin öğrendiği dağılımın kendisidir. T sıfıra yaklaştıkça açgözlü seçime yaklaşır.

PT(i)=ezi/T∑jezj/TP_T(i) = \frac{e^{z_i / T}}{\sum_j e^{z_j / T}}

Top-k. Yalnızca en olası k karakter arasından seçeriz; geri kalanların olasılığını sıfırlarız. Model bazen düşük ama sıfır olmayan bir olasılıkla saçma bir karakter seçer ve metin oradan sonra raydan çıkar. Top-k bu kuyruğu keser. Büyük modellerde benzer bir yöntem olan top-p (çekirdek örnekleme) da yaygındır: k sabit sayı yerine, olasılıkları toplamı p’yi geçene kadar en olasıdan başlayarak alınır.

Program, 8. bölümde kaydedilen ağırlıkları yükler. Dosya yoksa modeli 8. bölümle birebir aynı biçimde yeniden eğitir. Aynı tohumla başlayıp rastgele sayıları aynı sırayla çektiği için aynı ağırlıklara ulaşır. Bu, oyun alanında programlar arasında dosya taşınamadığı için gereklidir.

Yöntem
program MetinÜretmek
{ Eğitilmiş bir dil modeli her adımda bir OLASILIK DAĞILIMI verir. Bir sonraki
  karakteri bu dağılımdan nasıl seçtiğimiz, üretilen metnin karakterini belirler:
    açgözlü   : her zaman en olası karakter (hep aynı metin, çoğu zaman tekrar)
    sıcaklık  : logitler T'ye bölünür; T < 1 dağılımı sivriltir, T > 1 düzleştirir
    top-k     : yalnızca en olası k karakter arasından seçilir
  Program 8. bölümde kaydedilen ağırlıkları yükler. Dosya yoksa (örneğin oyun
  alanında) modeli 8. bölümle aynı ayarlarla yeniden eğitir; bu yaklaşık 10 dakika sürer. }
kullan Atasozleri, DilAraclari, KucukGPT, YZ, Sistem, Matematik

sabit
  BAĞLAM: tamsayı = 16
  DOSYA: metin = 'kucuk_gpt.agirlik'

değişken
  harfler: metin dizisi
  numara: sözlük
  model: GPT
  P: ondalık dizisi
  sıra: tamsayı dizisi
  baş: metin
  i, k: tamsayı
  T: ondalık

fonksiyon SonrakiDağılım(bağ: tamsayı dizisi; sıcaklık: ondalık; enİyiK: tamsayı): ondalık dizisi
{ Son karakterden sonraki karakterin olasılıkları; sıcaklık ve top-k uygulanmış. }
değişken logit: ondalık dizisi; eşik: ondalık
başla
  logit := model.İleri(bağ, 1, Uzunluk(bağ)).Satır(Uzunluk(bağ) - 1).Veri
  logit := Eşle(logit, fonksiyon(z: ondalık) => z / sıcaklık)
  eğer enİyiK > 0 ise
  başla
    eşik := Sırala(logit, fonksiyon(z: ondalık) => -z)[enİyiK - 1]
    logit := Eşle(logit, fonksiyon(z: ondalık) => eğer z >= eşik ise z değilse -1000000000.0)
  bitir
  dön Softmaks(Tensör([logit])).Satır(0).Veri
bitir

fonksiyon Üret(başlangıç: metin; sıcaklık: ondalık; enİyiK: tamsayı; açgözlü: mantıksal): metin
değişken bağ: tamsayı dizisi; j: tamsayı; dağ: ondalık dizisi
başla
  sonuç := başlangıç
  bağ := [0] + DilAraclari.Kodla(başlangıç, numara)
  tekrarla
    eğer Uzunluk(bağ) > BAĞLAM ise bağ := Dilim(bağ, Uzunluk(bağ) - BAĞLAM, Uzunluk(bağ))
    dağ := SonrakiDağılım(bağ, sıcaklık, enİyiK)
    eğer açgözlü ise j := ArgMaks(dağ) değilse j := DilAraclari.Örnekle(dağ)
    eğer j <> 0 ise sonuç := sonuç + harfler[j]
    bağ := bağ + [j]
  j = 0 veya Uzunluk(sonuç) > 70 olana dek
bitir

prosedür YenidenEğit
{ 8. bölümle birebir aynı eğitim: aynı tohum, veri, bırakma, öğrenme oranı takvimi ve
  erken durdurma. Rastgele sayılar aynı sırayla çekildiği için aynı ağırlıklar çıkar. }
değişken veri: metin dizisi; eğitim, doğrulama, g, h: tamsayı dizisi
         opt: herhangi; adım, b, s, ayrım: tamsayı; kayıp: tensör; enİyi, dk: ondalık
başla
  yaz('Ağırlık dosyası bulunamadı; model 8. bölümdeki gibi yeniden eğitiliyor (yaklaşık 10 dakika)...', 1)
  Tohum(2026)
  veri := Atasozleri.Hepsi
  ayrım := Uzunluk(veri) * 9 böl 10
  eğitim := DilAraclari.Kodla('.' + Birleştir(Dilim(veri, 0, ayrım), '.') + '.', numara)
  doğrulama := DilAraclari.Kodla('.' + Birleştir(Dilim(veri, ayrım, Uzunluk(veri)), '.') + '.', numara)
  model := GPT.Oluştur(Uzunluk(harfler), BAĞLAM, 32, 4, 2)
  model.bırakma := 0.2
  opt := Adam(model.Parametreler, 0.003)
  enİyi := 1000.0
  1'den 3000'e kadar her adım için
  başla
    g := []; h := []
    1'den 8'e kadar her b için
    başla
      s := Rastgele(Uzunluk(eğitim) - BAĞLAM - 1)
      g := g + Dilim(eğitim, s, s + BAĞLAM); h := h + Dilim(eğitim, s + 1, s + BAĞLAM + 1)
    bitir
    opt.oran := 0.003 * 0.5 * (1 + Cos(Matematik.pi * adım / 3000))
    model.eğitimde := doğru
    opt.SıfırlaGrad
    kayıp := ÇaprazEntropi(model.İleri(g, 8, BAĞLAM), h)
    kayıp.GeriYay
    opt.Adım
    eğer adım = 1 veya adım mod 250 = 0 ise
    başla
      model.eğitimde := yanlış
      dk := 0; s := 0; b := 0
      s + BAĞLAM + 1 <= Min(Uzunluk(doğrulama), 512) iken
      başla
        dk := dk + ÇaprazEntropi(model.İleri(Dilim(doğrulama, s, s + BAĞLAM), 1, BAĞLAM),
                                 Dilim(doğrulama, s + 1, s + BAĞLAM + 1)).Değer
        b := b + 1; s := s + BAĞLAM
      bitir
      eğer dk / b < enİyi ise başla enİyi := dk / b; model.Kaydet(DOSYA) bitir
    bitir
  bitir
  model.Yükle(DOSYA)
  model.eğitimde := yanlış
bitir

başla
  harfler := DilAraclari.Harfler
  numara := DilAraclari.Numaralar(harfler)
  eğer Sistem.DosyaVarMı(DOSYA) ise
  başla
    model := GPT.Oluştur(Uzunluk(harfler), BAĞLAM, 32, 4, 2)
    model.Yükle(DOSYA)
  bitir
  değilse YenidenEğit

  yaz('"damlaya damlaya g" yazıldıktan sonra modelin tahmini:', 1)
  P := SonrakiDağılım([0] + DilAraclari.Kodla('damlaya damlaya g', numara), 1.0, 0)
  sıra := Sırala(Aralık(Uzunluk(P)), fonksiyon(z: tamsayı) => -P[z])
  0'dan 4'e kadar her k için yaz(Biçimle('   "{}"  {:.3}', harfler[sıra[k]], P[sıra[k]]), 1)

  yaz('', 1); yaz('Açgözlü seçim (her çalıştırmada aynı sonuç):', 1)
  ['', 'damlaya', 'sabır', 'dost', 'göz'] içindeki her baş için
    yaz('   ' + Üret(baş, 1.0, 0, doğru), 1)

  Tohum(9)
  [0.5, 1.0, 1.5] içindeki her T için
  başla
    yaz('', 1); yaz(Biçimle('Sıcaklık {}:', T), 1)
    1'den 4'e kadar her i için yaz('   ' + Üret('', T, 0, yanlış), 1)
  bitir

  yaz('', 1); yaz('Top-k (k = 3, sıcaklık 1.0):', 1)
  1'den 4'e kadar her i için yaz('   ' + Üret('', 1.0, 3, yanlış), 1)
bitir.

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

Çıktı
"damlaya damlaya g" yazıldıktan sonra modelin tahmini:
   "a"  0.220
   "e"  0.199
   "ı"  0.188
   "ü"  0.130
   "ö"  0.114

Açgözlü seçim (her çalıştırmada aynı sonuç):
   alan yaran yerin derin derin derin derin derin derin derin derin derin 
   damlayan yerin derin derin derin derin derin derin derin derin derin de
   sabır der derin derin derin derin derin derin derin derin derin derin d
   dostun derin derin derin derin derin derin derin derin derin derin deri
   gözün alan alarar

Sıcaklık 0.5:
   dolun ye derin dişin olur güşmaz
   akarıyı yolur
   aşan der yayır
   bili biş yelinde alarır

Sıcaklık 1.0:
   kakır yülm z biş kasanı kaparı sok övük düzüş tayı bırdığa pedinin meyi
   ağacıyık dilver
   esol
   gelemeyn dolanl apırsın sörmeden binekince dermik

Sıcaklık 1.5:
   aşıgcı dok
   acilmama
   yelanca butaya koktuçt oçuknununz kebıden düzüderlcs dererin aktloşan
   ısarlın götürüsaz ork ult

Top-k (k = 3, sıcaklık 1.0):
   alın deren dolunun düşmez daranı yeri deliş
   alan kere yokmaz
   atla kaş dürün karan dalır
   alılan deri delir

Çıktıyı okumak

İlk tablo modelin ne bildiğini gösteriyor. “damlaya damlaya g” yazıldıktan sonra model, g’den sonra bir ünlü geleceğinden emindir: ilk beş tahminin hepsi ünlü. Ama doğru cevap olan “ö”yü ancak beşinci sıraya koyuyor. Bu atasözü eğitim verisinde olduğu halde model onu ezberlememiş; bırakma ve erken durdurmanın istediğimiz etkisi budur. Model, gördüğü metinlerden hecelerin ve eklerin genel kurallarını çıkarmış.

Açgözlü seçim bir döngüye takılıyor. Beş başlangıcın dördü kısa sürede “derin derin derin…” tekrarına düşüyor. Model “derin” yazdıktan sonra en olası devamın yine “derin” olduğunu düşünüyor; açgözlü seçim de bu döngüden hiç çıkamıyor. Büyük dil modellerinde de aynı sorun görülür; bu yüzden sohbet uygulamaları nadiren açgözlü seçim kullanır.

Sıcaklık bir ayar düğmesidir. Sıcaklık 0,5’te üretilen metin kısa, düzenli ve Türkçeye en yakın olanı: “olur”, “akarıyı”, “bili” gibi sözcükler ve “-maz”, “-ır” ekleri. Sıcaklık 1,5’te sözcükler dağılıyor; “düzüderlcs” gibi Türkçede mümkün olmayan harf dizileri belirliyor.

Top-k sınırlı bir özgürlük tanıyor. Top-k ile sıcaklık 1,0’da metin çeşitli kalıyor ama saçma harf dizileri kayboluyor. “alın deren dolunun düşmez” gerçek bir atasözü olmasa da kulağa bir atasözü gibi geliyor.

Uygulamada bu ayarlar birlikte kullanılır. Bir kod yazma asistanı düşük sıcaklık ister, çünkü doğru ve tutarlı olmak yaratıcı olmaktan önemlidir. Şiir yazdıran biri ise sıcaklığı yükseltebilir.

Alıştırma. Programa top-p örneklemeyi ekleyiniz: SonrakiDağılım’a bir p parametresi veriniz; olasılıkları büyükten küçüğe sıralayıp toplamı p’yi geçene kadar olanları tutunuz, kalanları sıfırlayıp yeniden normalleştiriniz. p = 0,9 ile üretilen metni top-k ile karşılaştırınız.