Kitaplar / Yöntem ile Sıfırdan Dil Modeli / Bölüm 2 — Metni sayılara çevirmek
Bölüm 2 — Metni sayılara çevirmek
Sinir ağları harflerle değil sayılarla çalışır. Bu yüzden ilk iş metni bir sayı dizisine çevirmektir. Bu işe simgeleştirme (tokenization), metnin parçalarına da simge (token) denir.
Karakter düzeyinde simgeleştirme
En basit yol her karaktere bir numara vermektir. Veri kümemizdeki 178 atasözünde 28 farklı harf ve boşluk geçiyor. Bunlara bir de nokta ekliyoruz. Nokta, “atasözü burada bitti” ya da “yeni atasözü başlıyor” anlamına gelen özel bir simgedir. Böylece sözlüğümüz 30 simgeden oluşur.
İki işlem birbirinin tersidir: kodlama metni sayılara, çözme sayıları metne çevirir.
program Simgeleştirme
{ Bir dil modeli harfleri değil sayıları işler. Bu program:
1) Veri kümesindeki tüm karakterlerden bir sözlük kurar (karakter → numara).
2) Bir cümleyi sayılara çevirir (kodlama) ve geri çevirir (çözme).
3) Bayt çifti kodlamasının (BPE) fikrini gösterir: en sık yan yana gelen
iki simgeyi birleştirip yeni bir simge yapar, bunu birkaç kez tekrarlar. }
kullan Atasozleri
değişken
veri: metin dizisi
harfler: metin dizisi
numara: sözlük
kodlar: tamsayı dizisi
simgeler: metin dizisi
çiftSay: sözlük
enÇift, çift: metin
i, tur, karakterSayısı: tamsayı
yeni: metin dizisi
fonksiyon Kodla(s: metin): tamsayı dizisi
başla
sonuç := Eşle(s.Karakterler, fonksiyon(c: metin) => numara[c])
bitir
fonksiyon Çöz(k: tamsayı dizisi): metin
başla
sonuç := Birleştir(Eşle(k, fonksiyon(n: tamsayı) => harfler[n]), '')
bitir
başla
veri := Atasozleri.Hepsi
{ Sözlük: '.' cümle sonu (ve başı) işaretidir; ardından görülen tüm karakterler, sıralı }
harfler := ['.'] + Sırala(Benzersiz(Birleştir(veri, '').Karakterler))
numara := [:]
0'dan Uzunluk(harfler) - 1'e kadar her i için numara[harfler[i]] := i
karakterSayısı := Toplam(Eşle(veri, fonksiyon(a: metin) => Uzunluk(a) + 1))
yaz(Biçimle('{} atasözü, toplam {} karakter', Uzunluk(veri), karakterSayısı), 1)
yaz(Biçimle('Sözlük boyutu: {}', Uzunluk(harfler)), 1)
yaz('Sözlük: ' + Birleştir(harfler, ''), 2)
kodlar := Kodla('damlaya damlaya göl olur')
yaz('Kodlanmış : ' + kodlar, 1)
yaz('Çözülmüş : ' + Çöz(kodlar), 2)
{ BPE: başlangıçta her karakter bir simgedir }
simgeler := Birleştir(Eşle(Dilim(veri, 0, 40), fonksiyon(a: metin) => a + '.'), '').Karakterler
yaz(Biçimle('BPE başlangıcı: {} simge', Uzunluk(simgeler)), 1)
1'den 6'ya kadar her tur için
başla
çiftSay := [:]
0'dan Uzunluk(simgeler) - 2'ye kadar her i için
başla
çift := simgeler[i] + '|' + simgeler[i + 1]
eğer çiftSay.İçerir(çift) ise çiftSay[çift] := çiftSay[çift] + 1 değilse çiftSay[çift] := 1
bitir
enÇift := Sırala(çiftSay.Anahtarlar, fonksiyon(k: metin) => -çiftSay[k])[0]
{ en sık çifti tek simgeye birleştir }
yeni := []
i := 0
i < Uzunluk(simgeler) iken
başla
eğer i < Uzunluk(simgeler) - 1 ve simgeler[i] + '|' + simgeler[i + 1] = enÇift ise
başla
yeni.Ekle(simgeler[i] + simgeler[i + 1]); i := i + 2
bitir
değilse
başla
yeni.Ekle(simgeler[i]); i := i + 1
bitir
bitir
simgeler := yeni
yaz(Biçimle(' {}. birleştirme: "{}" ({} kez) → {} simge', tur,
enÇift.Değiştir('|', ''), çiftSay[enÇift], Uzunluk(simgeler)), 1)
bitir
yaz('Simgelere bölünmüş ilk satırlar: ' + Birleştir(Dilim(simgeler, 0, 45), '|'), 1)
bitir.Programın çıktısı şöyledir:
178 atasözü, toplam 5282 karakter
Sözlük boyutu: 30
Sözlük: . abcdefghiklmnoprstuvyzçöüğış
Kodlanmış : [5, 2, 13, 12, 2, 22, 2, 1, 5, 2, 13, 12, 2, 22, 2, 1, 8, 25, 12, 1, 15, 12, 20, 17]
Çözülmüş : damlaya damlaya göl olur
BPE başlangıcı: 1140 simge
1. birleştirme: "n " (43 kez) → 1097 simge
2. birleştirme: "r." (24 kez) → 1073 simge
3. birleştirme: "an " (19 kez) → 1054 simge
4. birleştirme: "de" (15 kez) → 1039 simge
5. birleştirme: "i " (14 kez) → 1025 simge
6. birleştirme: "ma" (13 kez) → 1012 simge
Simgelere bölünmüş ilk satırlar: d|a|m|l|a|y|a| |d|a|m|l|a|y|a| |g|ö|l| |o|l|u|r.|a|c|e|l|e| |i|ş|e| |ş|e|y|t|an |k|a|r|ı|ş|ıSözlükteki ikinci simge boşluk karakteridir; bu yüzden çıktıda “.” ile “a” arasında bir boşluk görünür. Kodlanmış dizide sıkça geçen 1 sayısı, boşluğun numarasıdır.
Daha büyük parçalar: bayt çifti kodlaması
Karakter düzeyinde çalışmanın bir bedeli vardır: model “an” ya da “ler” gibi çok sık görülen parçaları her seferinde harf harf kurmak zorundadır. Büyük dil modelleri bu yüzden bayt çifti kodlaması (BPE, byte pair encoding) kullanır. Fikir şudur: metinde en sık yan yana gelen iki simge bulunur ve bunlar yeni, tek bir simgeyle değiştirilir. Bu işlem binlerce kez tekrarlanır.
Programın ikinci yarısı bu birleştirmeyi ilk 40 atasözü üzerinde altı kez yapıyor. İlk birleştirme “n” ile boşluğu birleştiriyor, çünkü Türkçede pek çok sözcük “n” ile biter (“şeytan”, “yaşken”, “akıldan”). Üçüncü turda “a” bu yeni simgeye eklenerek “an” oluşuyor. Altı birleştirmeden sonra aynı metin 1140 yerine 1012 simgeyle yazılabiliyor.
Gerçek modellerde sözlük on binlerce, hatta yüz binlerce simgeden oluşur. Sık sözcükler tek simgedir; nadir sözcükler birkaç parçaya bölünür. Türkçe gibi eklemeli dillerde “gidemeyeceklermiş” gibi uzun bir sözcük genellikle birden çok simgeye ayrılır.
Bu kitapta karakter düzeyinde kalacağız. Sözlük küçük olduğu için modelimiz de küçük kalır; üstelik kayıp ve olasılıkları harf harf görmek öğrenmeyi kolaylaştırır. Bir dil modelinin geri kalanı simgelerin karakter mi, sözcük parçası mı olduğunu umursamaz; hepsi yalnızca birer numaradır.
Alıştırma. BPE döngüsünü 6 yerine 30 kez çalıştırınız. Hangi Türkçe ekler ve sözcükler tek simge haline geliyor?