Kitaplar / Yöntem ile Sıfırdan Dil Modeli / Bölüm 3 — Sayarak dil modeli
Bölüm 3 — Sayarak dil modeli
İlk dil modelimiz hiç eğitilmeyecek; yalnızca sayacak. Fikir şudur: bir sonraki karakter yalnızca bir önceki karaktere bağlıymış gibi davranalım. “ş” harfinden sonra neler geldiğini veri kümesinde sayarsak, “ş”den sonra ne geleceğinin olasılıklarını da bulmuş oluruz. İki karakterlik bu pencereye ikili (bigram) model denir.
Her atasözünü noktayla başlatıp noktayla bitiriyoruz. Böylece “nokta → d” çifti “atasözleri d harfiyle ne sıklıkla başlar?” sorusunu, “r → nokta” çifti de “r ile biten bir sözcükten sonra atasözü ne sıklıkla biter?” sorusunu yanıtlar.
program SayarakDilModeli
{ İkili (bigram) dil modeli: bir sonraki karakterin olasılığı yalnızca bir önceki
karaktere bağlıdır. Model eğitilmez, sayılır:
P(sonraki = b | şimdiki = a) = say(a, b) / say(a, herhangi)
Her atasözünün başına ve sonuna '.' koyarız; böylece model nerede başlayıp
nerede bitireceğini de öğrenir. Sonra modelden yeni "atasözleri" örnekleriz
ve modelin veriye ne kadar şaşırdığını ölçeriz. }
kullan Atasozleri, Matematik
değişken
veri, harfler: metin dizisi
numara: sözlük
say: tamsayı dizisi dizisi
P: ondalık dizisi dizisi
a: metin
i, k, n, öncekiNo, sonrakiNo: tamsayı
satırToplamı: tamsayı
logToplam: ondalık
sözcük, kar: metin
sıra: tamsayı dizisi
fonksiyon Örnekle(olasılıklar: ondalık dizisi): tamsayı
{ Olasılık dağılımından bir numara çeker: [0, 1) arasında bir sayı tutup
olasılıkları sırayla toplar, toplam o sayıyı geçtiği yerde durur. }
değişken r, birikim: ondalık; i: tamsayı
başla
r := Rastgele(1000000) / 1000000
birikim := 0
0'dan Uzunluk(olasılıklar) - 1'e kadar her i için
başla
birikim := birikim + olasılıklar[i]
eğer r < birikim ise dön i
bitir
dön Uzunluk(olasılıklar) - 1
bitir
başla
Tohum(42)
veri := Atasozleri.Hepsi
harfler := ['.'] + Sırala(Benzersiz(Birleştir(veri, '').Karakterler))
n := Uzunluk(harfler)
numara := [:]
0'dan n - 1'e kadar her i için numara[harfler[i]] := i
{ 1) Say: say[i][j] = i. karakterden sonra j. karakterin kaç kez geldiği }
say := Eşle(Aralık(n), fonksiyon(x: tamsayı) => Eşle(Aralık(n), fonksiyon(z: tamsayı) => 0))
veri içindeki her a için
başla
öncekiNo := 0
(a + '.').Karakterler içindeki her kar için
başla
sonrakiNo := numara[kar]
say[öncekiNo][sonrakiNo] := say[öncekiNo][sonrakiNo] + 1
öncekiNo := sonrakiNo
bitir
bitir
{ 2) Olasılığa çevir. Her sayıma 1 eklemek (düzleştirme) hiç görülmemiş
bir çiftin olasılığının sıfır olmasını engeller. }
P := []
0'dan n - 1'e kadar her i için
başla
satırToplamı := Toplam(say[i]) + n
P.Ekle(Eşle(say[i], fonksiyon(c: tamsayı) => (c + 1) / satırToplamı))
bitir
yaz('Atasözleri en sık şu harflerle başlar:', 1)
sıra := Sırala(Aralık(n), fonksiyon(x: tamsayı) => -P[0][x])
0'dan 4'e kadar her k için
yaz(Biçimle(' "{}" olasılık {:.3}', harfler[sıra[k]], P[0][sıra[k]]), 1)
yaz('"ş" harfinden sonra en sık gelenler:', 1)
i := numara['ş']
sıra := Sırala(Aralık(n), fonksiyon(x: tamsayı) => -P[i][x])
0'dan 4'e kadar her k için
yaz(Biçimle(' "{}" olasılık {:.3}', harfler[sıra[k]], P[i][sıra[k]]), 1)
{ 3) Örnekle: '.' ile başla, bir sonraki karakteri çek, '.' gelene kadar sürdür }
yaz('', 1); yaz('Modelin ürettiği "atasözleri":', 1)
1'den 8'e kadar her k için
başla
sözcük := ''; i := 0
tekrarla
i := Örnekle(P[i])
eğer i <> 0 ise sözcük := sözcük + harfler[i]
i = 0 olana dek
yaz(' ' + sözcük, 1)
bitir
{ 4) Değerlendirme: ortalama negatif log-olabilirlik (kayıp) ve şaşkınlık }
logToplam := 0; k := 0
veri içindeki her a için
başla
öncekiNo := 0
(a + '.').Karakterler içindeki her kar için
başla
sonrakiNo := numara[kar]
logToplam := logToplam - Ln(P[öncekiNo][sonrakiNo])
k := k + 1
öncekiNo := sonrakiNo
bitir
bitir
yaz('', 1)
yaz(Biçimle('Ortalama kayıp (karakter başına): {:.4}', logToplam / k), 1)
yaz(Biçimle('Şaşkınlık (perplexity): {:.2} (rastgele tahmin: {})', Üstel(logToplam / k), n), 1)
bitir.Programın çıktısı şöyledir:
Atasözleri en sık şu harflerle başlar:
"a" olasılık 0.106
"b" olasılık 0.091
"d" olasılık 0.087
"k" olasılık 0.077
"s" olasılık 0.062
"ş" harfinden sonra en sık gelenler:
" " olasılık 0.164
"e" olasılık 0.119
"ı" olasılık 0.119
"a" olasılık 0.090
"i" olasılık 0.067
Modelin ürettiği "atasözleri":
p ayekacu suşl ol a dene mavminaker yı
uz
siladesi bi hz olz
tin çi mazhoğıllat asese ydückerun hectatıri or
ktademaşmin shzuz
dü yema dışlır domağıraccaktlkayolır hacin vağadokuter
giraparyı
suboğrmayolale k
Ortalama kayıp (karakter başına): 2.4175
Şaşkınlık (perplexity): 11.22 (rastgele tahmin: 30)Çıktıyı okumak
Sayılar veri hakkında gerçek şeyler söylüyor. Atasözlerinin %10’dan fazlası “a” ile başlıyor (“acele”, “ağaç”, “akıl”, “armut”). “ş” harfinden sonra en sık boşluk geliyor, çünkü “taş”, “iş”, “ateş” gibi sözcükler ş ile biter.
Üretilen “atasözleri” ise neredeyse anlamsız. Yine de bir şeyler öğrenilmiş: sözcük uzunlukları makul, ünlüler ile ünsüzler sırayla geliyor, “ol”, “bi”, “ya” gibi Türkçe heceler görünüyor. Model yalnızca bir önceki harfi bildiği için bir sözcüğün ortasında olduğunu, bir cümlenin neresinde bulunduğunu hatırlayamıyor.
Kayıp ve şaşkınlık
Bir dil modelinin ne kadar iyi olduğunu tek sayıyla ölçebiliriz. Verideki her karakter için modelin o karaktere verdiği olasılığın eksi logaritmasını alıp ortalarız. Bu, 1. bölümde tanıdığımız kayıptır.
Kayıp, sezgisel bir sözcükle de anlatılabilir: şaşkınlık (perplexity). Şaşkınlığı 11,2 olan bir model, her adımda 11 eşit olasılıklı seçenek arasından seçim yapıyormuş gibi kararsızdır. Hiçbir şey bilmeyen bir model 30 karakterin hepsine eşit olasılık verirdi; şaşkınlığı 30, kaybı ln 30 ≈ 3,40 olurdu. Bu sayıyı akılda tutmakta yarar vardır: eğitilmemiş her modelimiz bu civarda başlayacaktır.
Program, sayımlara 1 ekleyerek düzleştirme yapıyor. Bu olmasaydı veride hiç görülmemiş bir çiftin olasılığı 0 olurdu ve log 0 tanımsız olduğu için kayıp sonsuza giderdi. Düzleştirmenin küçük bir bedeli vardır: düzleştirmesiz sayımın kaybı 2,341 iken düzleştirilmiş modelinki 2,418’dir.
Alıştırma. Programı üçlü (trigram) modele çeviriniz: olasılığı son iki karaktere göre hesaplayınız. Tablonuz kaç satırlık olur? Kayıp nasıl değişiyor ve üretilen metin daha Türkçe görünüyor mu?