Kitaplar / Yöntem ile Sıfırdan Dil Modeli / Bölüm 5 — Bağlamı büyütmek
Bölüm 5 — Bağlamı büyütmek
İkili modelin en büyük zayıflığı unutkanlığıdır: “ol” yazdıktan sonra “u” mu “a” mı geleceğini bilmek için yalnızca “l” harfine bakar. Bu bölümde modeli son üç karaktere bakacak şekilde genişletiyoruz. Kuracağımız model, Yoshua Bengio ve arkadaşlarının 2003’te yayımladığı sinirsel dil modelinin küçük bir halidir.
Modelin yapısı
- Son üç karakterin her biri, 4. bölümde tanıdığımız gömme tablosundan (burada
C) 10 sayılık bir vektöre çevrilir. Tablo üç konum için ortaktır: “a” harfi nerede görülürse görülsün aynı gömmeyi alır. - Üç gömme 96 nöronlu bir gizli katmana girer. Her konumun kendi ağırlık matrisi vardır (
W1,W2,W3); böylece model “son harf” ile “üç önceki harf” arasındaki farkı bilir. - Gizli katmanın çıktısı, 30 logit üreten bir çıkış katmanından geçer.
Makalelerde genellikle üç gömme yan yana konup 30 sayılık tek bir vektör yapılır ve tek bir büyük matrisle çarpılır. Bizim yazdığımız biçim matematiksel olarak aynıdır; büyük matrisi üç parçaya bölmüş olduk.
İki yeni alışkanlık
Eğitim ve doğrulama ayrımı. Bir modelin gerçekten öğrenip öğrenmediğini, hiç görmediği veride sınamadan bilemeyiz. Atasözlerinin ilk %90’ını eğitim, son %10’unu (18 atasözü) doğrulama için ayırıyoruz. Model doğrulama atasözlerini eğitim boyunca hiç görmez.
Mini-yığınlar. 4. bölümde her adımda tüm veriyi kullanıyorduk. Burada her adımda rastgele seçilmiş 64 örnekle çalışıyoruz. Gradyan biraz gürültülü olur ama adımlar çok daha ucuzlar. Büyük modeller de böyle eğitilir; tüm internet verisini tek adımda işlemek mümkün değildir.
program BağlamlıDilModeli
{ İkili model yalnızca bir önceki karaktere bakıyordu. Bu model son BAĞLAM (3)
karaktere bakar (Bengio ve arkadaşlarının 2003'teki modelinin küçük bir hali):
1) Her karakter, öğrenilen C tablosundan 10 sayılık bir gömmeye çevrilir.
2) Üç gömme gizli katmana girer: h = tanh(e1 @ W1 + e2 @ W2 + e3 @ W3 + b)
(üç gömmeyi yan yana koyup tek bir büyük matrisle çarpmakla aynı şeydir).
3) Çıktı katmanı 30 logit üretir: logit = h @ Wç + bç
Veriyi eğitim (%90) ve doğrulama (%10) diye ayırırız; doğrulama kaybı, modelin
ezberleyip ezberlemediğini gösterir. Her adımda rastgele 64 örnekten oluşan bir
yığın (mini-batch) kullanırız. }
kullan Atasozleri, DilAraclari, YZ
sabit
BAĞLAM: tamsayı = 3
GÖMME: tamsayı = 10
GİZLİ: tamsayı = 96
YIĞIN: tamsayı = 64
değişken
veri, harfler: metin dizisi
numara: sözlük
bağlamlar: liste { her örnek: son 3 karakterin numarası }
hedefler: tamsayı dizisi
eğitimSayısı, n, i, k, adım: tamsayı
C, W1, W2, W3, b, Wç, bç, kayıp: tensör
opt: herhangi
seçilen: tamsayı dizisi
pencere: tamsayı dizisi
kar, sözcük: metin
fonksiyon Logitler(bağ: liste): tensör
{ bağ: bağlam listesi (her biri 3 numara) → her bağlam için 30 logit }
değişken e1, e2, e3, h: tensör
başla
e1 := BirSıcak(Eşle(bağ, fonksiyon(p: liste) => p[0]), Uzunluk(harfler)) @ C
e2 := BirSıcak(Eşle(bağ, fonksiyon(p: liste) => p[1]), Uzunluk(harfler)) @ C
e3 := BirSıcak(Eşle(bağ, fonksiyon(p: liste) => p[2]), Uzunluk(harfler)) @ C
h := (e1 @ W1 + e2 @ W2 + e3 @ W3 + b).Tanh
dön h @ Wç + bç
bitir
fonksiyon OrtalamaKayıp(ilk, son: tamsayı): ondalık
{ [ilk, son) aralığındaki örneklerin ortalama kaybı (eğitim yapmadan) }
başla
dön ÇaprazEntropi(Logitler(Dilim(bağlamlar, ilk, son)), Dilim(hedefler, ilk, son)).Değer
bitir
başla
Tohum(7)
veri := Atasozleri.Hepsi
harfler := DilAraclari.Harfler
n := Uzunluk(harfler)
numara := DilAraclari.Numaralar(harfler)
{ Örnekleri çıkar. Atasözlerinin ilk %90'ı eğitim, son %10'u doğrulama. }
bağlamlar := []; hedefler := []
0'dan Uzunluk(veri) - 1'e kadar her i için
başla
eğer i = Uzunluk(veri) * 9 böl 10 ise eğitimSayısı := Uzunluk(hedefler)
pencere := [0, 0, 0]
(veri[i] + '.').Karakterler içindeki her kar için
başla
bağlamlar.Ekle(pencere); hedefler.Ekle(numara[kar])
pencere := [pencere[1], pencere[2], numara[kar]]
bitir
bitir
yaz(Biçimle('{} eğitim, {} doğrulama örneği', eğitimSayısı, Uzunluk(hedefler) - eğitimSayısı), 1)
C := RastgeleTensör(n, GÖMME, 1.0, doğru)
W1 := RastgeleTensör(GÖMME, GİZLİ, 0.2, doğru)
W2 := RastgeleTensör(GÖMME, GİZLİ, 0.2, doğru)
W3 := RastgeleTensör(GÖMME, GİZLİ, 0.2, doğru)
b := Sıfırlar(GİZLİ, boş, doğru)
Wç := RastgeleTensör(GİZLİ, n, 0.1, doğru)
bç := Sıfırlar(n, boş, doğru)
opt := Adam([C, W1, W2, W3, b, Wç, bç], 0.01)
yaz(Biçimle('Parametre sayısı: {}', n * GÖMME + 3 * GÖMME * GİZLİ + GİZLİ + GİZLİ * n + n), 1)
1'den 3000'e kadar her adım için
başla
seçilen := Eşle(Aralık(YIĞIN), fonksiyon(x: tamsayı) => Rastgele(eğitimSayısı))
opt.SıfırlaGrad
kayıp := ÇaprazEntropi(Logitler(Eşle(seçilen, fonksiyon(x: tamsayı) => bağlamlar[x])),
Eşle(seçilen, fonksiyon(x: tamsayı) => hedefler[x]))
kayıp.GeriYay
opt.Adım
eğer adım = 1 veya adım mod 500 = 0 ise
yaz(Biçimle('Adım {:4}: eğitim kaybı {:.4} doğrulama kaybı {:.4}', adım,
OrtalamaKayıp(0, eğitimSayısı), OrtalamaKayıp(eğitimSayısı, Uzunluk(hedefler))), 1)
bitir
yaz('', 1); yaz('Modelin ürettiği "atasözleri":', 1)
1'den 8'e kadar her k için
başla
sözcük := ''; pencere := [0, 0, 0]
tekrarla
i := DilAraclari.Örnekle(Softmaks(Logitler([pencere])).Satır(0).Veri)
eğer i <> 0 ise sözcük := sözcük + harfler[i]
pencere := [pencere[1], pencere[2], i]
i = 0 olana dek
yaz(' ' + sözcük, 1)
bitir
bitir.Programın çıktısı şöyledir:
4724 eğitim, 558 doğrulama örneği
Parametre sayısı: 6186
Adım 1: eğitim kaybı 3.4243 doğrulama kaybı 3.4153
Adım 500: eğitim kaybı 1.8190 doğrulama kaybı 2.4013
Adım 1000: eğitim kaybı 1.5370 doğrulama kaybı 2.4427
Adım 1500: eğitim kaybı 1.4076 doğrulama kaybı 2.5698
Adım 2000: eğitim kaybı 1.3662 doğrulama kaybı 2.7467
Adım 2500: eğitim kaybı 1.2681 doğrulama kaybı 2.7659
Adım 3000: eğitim kaybı 1.2563 doğrulama kaybı 2.8837
Modelin ürettiği "atasözleri":
kın gönül sıvamahme verkendi söylemek açtırı yoğurttur
ayama kırk dibin olar
akıllüyarı söyleme ne insan gele kaz geler yoğuğa döne zotuz
davul ne gülüm üzüm üzüm uçun elin konu kokla san ne aştan ne anlı düşen kargayır gün işler ölür
gülünen olur
gülüşen olur
akılsan düşmanı yürür
kur düşen insan da da düşma meyen olurÇıktıyı okumak: ezberleme
Üretilen metin çok daha Türkçe görünüyor. “gönül”, “söylemek”, “kırk”, “insan”, “düşmanı yürür” gibi gerçek sözcükler ve “gülüşen olur” gibi atasözü tadında dizeler belirdi. Üç karakterlik bağlam, sözcük ve hece düzeyinde büyük fark yaratıyor.
Ama kayıp sütunları önemli bir uyarı veriyor. Eğitim kaybı 3000 adım boyunca düzenli olarak düşüyor (1,82 → 1,26). Doğrulama kaybı ise en iyi değerine (2,40) 500. adımda ulaşıyor, sonra yükseliyor ve 2,88’e çıkıyor. Model, eğitim atasözlerini giderek daha iyi ezberliyor ama hiç görmediği atasözlerinde giderek kötüleşiyor. Bu duruma aşırı uyum (overfitting) denir.
Nedeni basit: 6186 parametreli bir model, 4724 örneği neredeyse tek tek ezberleyebilecek kadar büyüktür. Üretilen metinde “kırk”, “davul”, “üzüm üzüm” gibi eğitim verisinden birebir parçalar görmeniz bunun işaretidir. Büyük dil modelleri bu sorunu öncelikle devasa veriyle çözer: modelin ezberleyemeyeceği kadar çok metin. Bizim verimiz küçük olduğu için 8. bölümde iki teknik kullanacağız. Erken durdurma, doğrulama kaybı en düşükken modeli kaydetmektir. Bırakma (dropout) ise eğitim sırasında ağın bir kısmını rastgele susturmaktır.
Bir karşılaştırma notu: 3. ve 4. bölümlerdeki kayıplar tüm veri üzerinde, yani modelin gördüğü veride ölçülmüştü. Bu bölümden itibaren asıl ölçütümüz doğrulama kaybıdır.
Alıştırma. GİZLİ sabitini 96’dan 24’e indiriniz ve programı yeniden çalıştırınız. Doğrulama kaybının en düşük değeri ve yükselmeye başladığı adım nasıl değişiyor? Ardından adım sayısını 500’e indirip üretilen metne bakınız.