Kısım III · Merak · Bölüm 53
makemore 1: Bigram
Harf sayarak isim üretmek (Bigram language model by counting)
Neden burada: Şimdiye kadar küçük ağları sayılarla eğittik: dört örnek, dört istenen çıktı. Bu bölümde asıl konuya geçiyoruz: metin. Bir dil modelinin yaptığı iş, aslında şudur: şimdiye kadar gördüğüne bakıp bir sonraki parçayı tahmin etmek. Bunu en küçük haliyle, harf harf, isimler üzerinde yapacağız. Hiçbir sinir ağı yok; yalnızca sayma. Neden sayma? Çünkü en basit dil modeli budur ve sonraki üç bölümün hepsi, "bunu sayarak yaptık; şimdi başka yoldan yapacağız ve aynı sonucu bulmalıyız" mantığıyla bu bölümün üzerine kuruluyor.
Neye dayanıyor: Bölüm 47: model, eğitim, çıkarım. Burada "eğitim" sayma olacak, "çıkarım" ise üretim. Ayrıca Bölüm 11'den sözlük (dictionary) veri yapısı, Bölüm 2.5'ten harflerin sayı olması ve temel bir olasılık sezgisi: bir olayın olasılığı 0 (asla) ile 1 (kesin) arasındadır ve bütün olasılıklar toplanınca 1 eder. Gerisini burada kuruyoruz.
- Kat 10Uygulamalar: bizimki ve içinde yaşayan yapay zekâBir dil modeli, bir tablo ve bir rastgele sayı üretecidir; Python ile yazılmış bir uygulama.
- Kat 4–9Python, dosya, işletim sistemiİsim dosyasını okuyan, tabloyu bellekte tutan katlar. Bölüm 13–21.
- Kat 3İşlemci ve GPU228.146 küçük artırımı saniyenin kesirinde yapan donanım. Bölüm 6.
- Kat 0–2Bit, kapı, devreHarf = sayı = bit. Bölüm 2.5.
Bu bölümün sonunda bir dil modelinin ne olduğunu, 32.033 isimden oluşan bir veri kümesini, harf çiftlerini sayarak bir tablo çıkarmayı, o tabloyu olasılıklara çevirmeyi ve olasılıklardan yeni isimler üretmeyi (örneklemeyi) bilecek, hepsini Python'da yazabilecek durumda olacaksın. Üretilen isimlerin neden ikna edici olmadığını da, yani bir sonraki bölümlerdeki her iyileştirmenin neden gerektiğini göreceksin.
53.1 Dil modeli nedir: sonrakini tahmin etmek
Telefonunda bir mesaj yazarken klavyenin üstünde belirmeye başlayan önerileri düşün: "Merhaba, nasıl" yazınca "sın", "gidiyor", "bir" gibi öneriler çıkar. Telefon bir sonraki kelimeyi tahmin ediyor. Bu tahmin edici bir dil modeli (language model)'dir: yazılmış metnin bir parçasını görüp devamının ne olabileceğine dair olasılıklar veren model. Bu tanım bu kadar sadedir ve ChatGPT, Claude ve diğer büyük dil modellerinin çekirdeği de aynıdır (Bölüm 58); yalnızca ölçek ve yöntem farklıdır.
Karpathy'nin makemore deposu bu fikri en küçük ölçekte öğretmek için yazılmıştır. README'sinin tanımı: makemore, her satırı bir eğitim örneği sayılan tek bir metin dosyası alır ve "buna benzer" yeni örnekler üretir; içeride, bigram'dan Transformer'a kadar geniş bir model yelpazesine sahip, harf düzeyinde (character-level) ve otoregresif bir dil modelidir.1 İki yeni kelime:
- Harf düzeyinde (character-level): modelin tahmin ettiği parça tek bir harftir (kelime ya da kelimenin parçası değil). Bölüm 58'de, gerçek modellerin harf yerine "simge" kullandığını göreceğiz.
- Otoregresif (autoregressive): metni soldan sağa, bir parça bir parça üretir ve her yeni parçayı üretirken önceki parçalara bakar. "Önce şunu yazdım, sıradaki ne olmalı?" Her üretilen harf, bir sonrakinin girdisi olur.
makemore'un örnek kullanımı isimdir: bir isim listesi verirsin, o "isim gibi duran ama listede olmayan" yeni isimler üretir; bebek ismi fikri, şirket ismi fikri, hatta İngilizce benzeri uydurma kelimeler.1 Biz de isimleri seçiyoruz: kısa (ortalama 6 harf), eğitim verisi küçük, sonuçları gözünle okuyup "isme benziyor mu?" diye yargılayabilirsin, ve hesabın tamamı bir dizüstü bilgisayarda saniyeler alır.
Dil modeli "şu harf gelecek" demez, "şu harfler şu olasılıklarla gelebilir" der. Yani çıktısı bir cevap değil, bir olasılık dağılımı (probability distribution): her olası harf için bir sayı (0 ile 1 arasında) ve hepsinin toplamı 1. Neden tek cevap değil de dağılım? Çünkü gerçek hayatta "ka" ile başlayan bir ismin devamı hem "rem" hem "ya" hem "n" olabilir; hepsi mümkün, bazıları daha olası. Sonra bu dağılımdan çekiliş yaparak bir harf seçeriz (53.5). Hukuk dilinde: model bir hüküm değil, bir olasılık değerlendirmesi üretir.
53.2 Veri kümesi: 32.000 isim; başlangıç ve bitiş işaretleri
Her öğrenme, bir veri kümesi (dataset) ile başlar: modelin üzerinden öğreneceği örnekler topluluğu. makemore deposunda names.txt dosyası var: README'ye göre ssa.gov'dan (ABD sosyal güvenlik idaresi) alınmış 2018 yılının en yaygın 32 bin ismi.1 Dosyada her satır bir isim: emma, olivia, ava, isabella…. Dosyayı indirip ben sayarak doğruladım:
| Ölçü | Değer |
|---|---|
| İsim sayısı (satır) | 32.033 |
| Birbirinden farklı isim sayısı | 29.494 (2.539 tekrar) |
| En kısa isim uzunluğu | 2 (örn. an, jo) |
| En uzun isim uzunluğu | 15 (örn. muhammadibrahim) |
| Ortalama uzunluk | 6,12 harf |
| Kullanılan farklı harf | 26 (a–z, küçük, ASCII) |
Dosyadaki isimler sıralanmış değil: en yaygın olanlardan başlıyor (emma, olivia, ava, isabella, sophia…). Hepsi 26 küçük ASCII harfinden oluşur; Bölüm 2.5'te gördüğün gibi, ASCII bu iş için yeter. Türkçe karakterli (ş, ı) bir isim veri kümesi kursaydık, tablomuz 26 yerine daha çok harf gerektirirdi: model, harflerin kendisini değil, numaralarını kullanır.
İsimleri nasıl bir "eğitim örneği"ne çeviririz?
Bir isimde bir sonraki harfi tahmin etmeyi öğretmek istiyoruz. emma isminden hangi örnekler çıkar? Şöyle düşün: modele "e" harfini versek bir sonrakini (m) tahmin etmesini isteriz; sonra "m"yi versek "m" gelmesini; sonra "m"yi versek "a"yı. Peki ismin başlangıcında hangi harfe bakacak? İsmin sonunda ne diyecek? Burada bir hile kullanılır: isme, her iki uca da özel bir başlangıç ve bitiş işareti (start and end token) eklenir. Karpathy'nin ders defterinde bu iki uç için aynı işaret kullanılır: . (nokta).2 Yani emma isminin işaretlerle yazılışı:
. e m m a .
Ve komşu çiftleri şunlar: .e, em, mm, ma, a.. Bunlar beş eğitim örneği: ".'dan sonra e gelir", "e'den sonra m", "m'den sonra m", "m'den sonra a", "a'dan sonra isim biter". Her iki işaret için tek bir işaret kullanmak, bir kolaylıktır: başta nokta "isim şimdi başlıyor", sonda nokta "isim şimdi bitti" demektir. Model "başlangıç bağlamını" ve "bitiş kararını" öğrenmek zorundadır: bir isim nerede biter? (Bölüm 56'da bu fikri genişleteceğiz.) 32.033 ismin tamamında bu çiftlerden toplam 228.146 tane çıkıyor (her isim için uzunluğu + 1 çift; kontrol: 32.033 + 196.113 harf = 228.146).
Harflere numara vermek
Bilgisayar harfle değil sayıyla çalışır; bunu Bölüm 2'de gördük. Modelin tablosunu bir ızgara olarak kuracağız ve ızgaranın satır ve sütunları numaralarla adreslenir. O yüzden her sembole bir numara veririz: nokta 0, a 1, b 2, … z 26. Toplam 27 sembol. Kodu:
words = open('names.txt', 'r').read().splitlines() # her satır bir isim
chars = sorted(set(''.join(words))) # isimlerdeki farklı harfler, sıralı
stoi = {s: i+1 for i, s in enumerate(chars)} # harf -> numara (a=1, b=2, ...)
stoi['.'] = 0 # başlangıç/bitiş işareti = 0
itos = {i: s for s, i in stoi.items()} # numara -> harf (ters sözlük)
Satır satır: open(...).read().splitlines(): dosyayı aç, tamamını oku, satırlara böl; sonuç, her elemanı bir isim olan bir liste (Bölüm 16'daki dosya okuma). ''.join(words): bütün isimleri uç uca yapıştırır; set(...) tekrarları atar; sorted(...) sıralar: 26 harf. enumerate(chars): her harfi sırasıyla numarasıyla (0, 1, 2…) verir; i+1 ile 1'den başlatıyoruz, çünkü 0'ı nokta için ayırdık. stoi ("string to integer", harften sayıya) bir sözlüktür: stoi['a'] 1'dir. itos tersini yapar: numaradan harfe. Bu kodun eşdeğeri Karpathy'nin ders defterindeki satırlardır; ben aynı mantığı yalın Python'da yazıyorum.2
53.3 Bigram: ardışık harf çiftlerini saymak; sayım tablosu
Şimdi modelin kendisine geliyoruz ve şaşıracaksın: model sadece saymak. Bigram (bigram), art arda gelen iki parçalık çifttir (iki harf, iki kelime). Bir bigram dil modeli, bir sonraki harfi tahmin ederken yalnızca bir önceki harfe bakar, bundan öncesini görmez. "ka" ile başlayan bir isimde "r" harfinden sonrasını tahmin ederken, modelin görebildiği tek şey "r"dir; ismin başının "ka" olduğunu bilmez. Bu çok kısıtlayıcı görünür ve öyledir; ama işe yarayan bir başlangıç noktasıdır.
Tahmin için ne gerekir? Bir önceki harf şu olduğunda, sonraki harf kaç kez şu olmuştur? Bu sorunun cevabı bir sayımdır. 27 × 27'lik bir ızgara düşün: satırlar "önceki harf", sütunlar "sonraki harf". Hücre [a, b], "a'dan hemen sonra b gelen çift sayısı". Bu ızgaraya sayım tablosu (count table) diyeceğiz. Ders defterinin yaptığı gibi, bütün isimleri dolaşıp her komşu çifti bir artırarak doldurulur:
N = [[0]*27 for _ in range(27)] # 27 satır x 27 sütun, hepsi 0
for w in words: # her isim için
chs = ['.'] + list(w) + ['.'] # '.emma.' gibi
for ch1, ch2 in zip(chs, chs[1:]): # komşu çiftler: (., e), (e, m), ...
N[stoi[ch1]][stoi[ch2]] += 1 # ilgili hücreyi bir artır
Satır satır: [[0]*27 for _ in range(27)]: her biri 27 sıfırdan oluşan 27 satırlık iç içe liste (matris). chs[1:]: listenin ilk elemanı atılmış hali; zip(chs, chs[1:]) ikisini yan yana tutup tam olarak komşu çiftleri verir. N[stoi[ch1]][stoi[ch2]] += 1: "ch1'in numarasındaki satır, ch2'nin numarasındaki sütun" hücresine bir ekle. (Ders defteri aynı tabloyu PyTorch tensörüyle kurar; tensörleri Bölüm 55.3'te göreceğiz, burada saf listeler yeterli.)2
Tablonun içinden bazı sonuçlar
Kodu 32.033 isim üzerinde çalıştırdım. Toplam 228.146 çift sayıldı. En sık on beş çift:
| Çift | Sayı | Anlamı |
|---|---|---|
n. | 6.763 | isim "n" ile bitiyor |
a. | 6.640 | isim "a" ile bitiyor |
an | 5.438 | "a"dan sonra "n" |
.a | 4.410 | isim "a" ile başlıyor |
e. | 3.983 | isim "e" ile bitiyor |
ar | 3.264 | |
el | 3.248 | |
ri | 3.033 | |
na | 2.977 | |
.k | 2.963 | isim "k" ile başlıyor |
le | 2.921 | |
en | 2.675 | |
la | 2.623 | |
ma | 2.590 | |
.m | 2.538 | isim "m" ile başlıyor |
İngilizce isimlerin üstünlüğü, bu veri kümesinin bir yan etkisidir ("n" ve "a" ile bitmeye hevesli). Veri kümesi neyse model odur: Türkçe isimlerden öğrenseydik tablo çok farklı olurdu (bu tabloya dayanan sonraki bölümlerde de unutma).
Tablonun küçük bir kesiti (satır: önceki harf, sütun: sonraki harf; . başlangıç/bitiş):
| . | a | b | c | d | e | k | n | m | y | |
|---|---|---|---|---|---|---|---|---|---|---|
| . | 0 | 4410 | 1306 | 1542 | 1690 | 1531 | 2963 | 1146 | 2538 | 535 |
| a | 6640 | 556 | 541 | 470 | 1042 | 692 | 568 | 5438 | 1634 | 2050 |
| b | 114 | 321 | 38 | 1 | 65 | 655 | 0 | 4 | 0 | 83 |
| e | 3983 | 679 | 121 | 153 | 384 | 1271 | 178 | 2675 | 769 | 1070 |
| k | 363 | 1731 | 2 | 2 | 2 | 895 | 20 | 26 | 9 | 379 |
| n | 6763 | 2977 | 8 | 213 | 704 | 1359 | 58 | 1906 | 19 | 465 |
Okuma: "a" satırı, "n" sütunu: 5.438; yani isimlerde "an" bitişik çifti 5.438 kez geçiyor. "b" satırında "k" sütunu 0: veride "bk" hiç yok. Nokta satırı: isimlerin ilk harfi; "a" ile başlayan 4.410, "k" ile başlayan 2.963 isim; "." sütunu: isimlerin son harfi. Hücre [., .] hep sıfır; çünkü hiçbir ismin uzunluğu sıfır değil, yani başlangıç işaretinden hemen sonra bitiş işareti gelmez.
729 hücrenin 627'si dolu, 102'si sıfır. Sıfır olanlardan biri [., .], diğer 101'i "vq", "jq", "qz", "xv" gibi bu veride hiç görülmeyen çiftler. Bunun etkisini 54.3'te göreceğiz.
Bir an dur ve şuna dikkat et: bu bölümde yazdığımız model, o 27 × 27 = 729 sayılık tablonun kendisi. "Eğitim", veriyi bir kere dolaşıp saymaktan ibaret; gradyan, kayıp, öğrenme oranı yok. Parametreler tablodaki sayılar. Bölüm 47'deki "model: veriden öğrenilmiş ayarlanabilir sayılar" tanımına bu tam uyuyor; yalnızca ayarlama yolu gradyan inişi değil, sayma. Bölüm 55'te aynı modeli gradyan inişiyle öğreneceğiz ve ikisinin birbirine eşdeğer çıktığını göreceğiz.
53.4 Sayımdan olasılığa: normalleştirme
Tablodaki sayılar ham: "an" 5.438, "ae" 692. Ama modelin istediği şey olasılık: "a'dan sonra n gelme ihtimali yüzde kaç?" Sayıdan olasılığa geçmek için bir satırın toplamına bölmemiz gerekir. Neden? "a" satırındaki sayıların toplamı 33.885: veride "a" harfi bu kadar kez geçiyor (başka bir harfle ya da bitiş işaretiyle devam edip). "a"dan sonra "n" gelenlerin oranı 5.438 / 33.885 ≈ 0,1605. Yani "a"dan sonra "n" gelme olasılığı yaklaşık yüzde 16. Bir satırdaki bütün sayıları o satırın toplamına bölmek, satırın toplamını 1 yapar. Bu işleme normalleştirme (normalization) denir.
Notasyon: P(b | a), "a gördükten sonra b'nin olasılığı" diye okunur (koşullu olasılık (conditional probability)). Dikey çizgi "verildiğinde" demek. Tablodan:
P(b | a) = N[a][b] / (N[a] satırının toplamı)
Birkaç sayı (hesapladım):
| Olasılık | Hesap | Değer |
|---|---|---|
| İsim "a" ile başlar: P(a | .) | 4.410 / 32.033 | 0,1377 |
| İsim "k" ile başlar: P(k | .) | 2.963 / 32.033 | 0,0925 |
| "a"dan sonra "n": P(n | a) | 5.438 / 33.885 | 0,1605 |
| "a"dan sonra isim biter: P(. | a) | 6.640 / 33.885 | 0,1960 |
| "q"dan sonra "u": P(u | q) | 206 / 272 | 0,7574 |
Hesapların hepsi kendi çalıştırmamdan. Birkaç yorum. İlk harf için dağılım tabloda nokta satırında; en yaygın başlangıç "a" (yüzde 13,8), onu "k" (9,3), "m" (7,9), "j" (7,6), "s" (6,4), "d" (5,3) izliyor. Bu dağılım "eşit olasılık" olsaydı her harf 1/27 ≈ 0,037 alırdı; model bu eşit dağılımdan çok daha bilgili. "q"dan sonra "u" gelme olasılığı yüzde 76: harf "q"nun neredeyse hep "u" ile devam ettiğini sayı kendi başına öğrendi, kimse bunu kurala yazmadı. Bu örnek bölümün küçük bir mucizesi: dil bilgisi (ya da en azından yazım alışkanlığı), sayımın içinden çıkıyor.
Tuzak: satır mı sütun mu?
Normalleştirmeyi kodla yaparken sık yapılan bir hata, satır yerine sütun toplamlarına bölmektir. Karpathy'nin ders defterinde bu, PyTorch'un "yayınlama" (broadcasting) kurallarıyla bağlantılı bir tuzak olarak uzun uzun işlenir: P.sum(1, keepdim=True) ile P.sum(1) aynı sonucu vermez; biri (27, 1), diğeri (27,) şekli üretir ve bölme farklı davranır.2 Bunun ayrıntısı Bölüm 55.3'te. Bizim saf Python versiyonumuzda tuzak yok: her satırı kendi toplamına açıkça böleriz:
P = []
for i in range(27):
toplam = sum(N[i]) # i. satırın toplamı
P.append([N[i][j] / toplam for j in range(27)]) # her hücreyi toplama böl
Her satırı doğru bölmüş olduğumuzu bir kontrolle görebiliriz: her satır toplamı 1 olmalı. (Bölüm 42'deki test fikri: bir şeyin doğru olup olmadığını, değişmez bir kuralı sınayarak bulursun.)
53.5 Örnekleme: modelden yeni isimler üretmek
Tablomuz bir isim değerlendirebilir (54. bölüm). Peki yaratabilir mi? Evet: örnekleme (sampling), bir olasılık dağılımına uygun olarak rastgele seçim yapmaktır. Bir torbada yüzde 14 "a", yüzde 9 "k" … yazılı toplar olduğunu düşün; torbadan körlemesine bir top çek; çıkan şey, o olasılıklarla seçilmiş bir harftir. Zarlı bir oyun gibi ama zarın yüzleri eşit olasılıklı değil. Üretim algoritması dört adımdır:
- Nokta (başlangıç işareti) ile başla.
- Şu anki harfin satırındaki dağılımdan bir sonraki harfi çek.
- Çekilen harf nokta ise dur (isim bitti). Değilse harfi ismin sonuna ekle ve 2. adıma dön.
- Biriken harfleri isim olarak ver.
Python'da:
import random
def isim_uret(P, rng):
ix = 0 # 0 = başlangıç işareti
harfler = []
while True:
ix = rng.choices(range(27), weights=P[ix])[0] # satırdaki olasılıklara göre çek
if ix == 0: # bitiş işareti çekildi
break
harfler.append(itos[ix])
return ''.join(harfler)
rng = random.Random(2147483647) # tohum: aynı tohum, aynı sonuç
print([isim_uret(P, rng) for _ in range(10)])
Satır satır: rng.choices(range(27), weights=P[ix]): 0'dan 26'ya kadar numaralardan birini, P[ix] satırındaki olasılıklara uygun olarak seç (Karpathy bunu torch.multinomial ile yapar).2 [0]: sonuç bir liste, ilk elemanı alıyoruz. while True ve break: ta ki bitiş işareti çıkana kadar dön (Bölüm 1'deki döngü). random.Random(2147483647): rastgele sayı üretecini sabit bir tohum (seed) ile başlatır. Bilgisayardaki "rastgelelik" aslında deterministik bir hesaptır; aynı tohum aynı dizi demektir, bu yüzden sonuçlarımız tekrarlanabilir.
İlk üretilenler
Bu kodu (tohum 2147483647, Karpathy'nin ders defterinde de kullandığı sayı; ama Python'un üretecinin ürettiği dizi PyTorch'unkinden farklıdır, bu yüzden sonuçlar onunkilerle aynı değil) çalıştırınca on isim çıktı:
erauirabenerey o ki r mi jayson iyahiayrialinta awarallll anedatsttzyavilanah mai
Dürüst bir değerlendirme: bazıları isim gibi (jayson, mai, ki), bazıları saçma (anedatsttzyavilanah, iyahiayrialinta, awarallll), ve bir kısmı çok kısa (o, r). Daha sistemli bakmak için 20.000 isim ürettim ve gerçek veriyle karşılaştırdım:
| Ölçü | Üretilen | Gerçek veri |
|---|---|---|
| Ortalama uzunluk | 6,11 harf | 6,12 harf |
| Tek harfli ya da boş isim oranı | %12,0 | %0 (en kısa isim 2 harf) |
| 12 harf ve üstü isim oranı | %13,4 | %0,2 (67 isim) |
| Veri kümesinde aynen bulunan isim oranı | %11,3 | — |
Burada kayda değer üç şey var. Birincisi, ortalama uzunluk tablonun dağılımını doğru yakalamış: 6,11 ≈ 6,12. Model, bitiş kararını ortalama olarak doğru veriyor. İkincisi, yine de yüzde 12 oranında tek harf üretiyor, oysa gerçek veride hiç yok: çünkü bigram bir sonraki harfi yalnızca önceki harfe bakarak seçiyor; başlangıç işaretinden sonra "m" çektiğinde, "m"den sonra bitiş gelme olasılığı yüzde 8 civarında ve model "bu ismin yalnızca 1 harf olduğunu" göremiyor. Üçüncüsü, çok uzun (12 harf ve üstü) isimler gerçek veride yüzde 0,2 iken üretilen isimlerde yüzde 13: model "uzunluğu" hatırlamıyor, bir harften sonra devam mı bitiş mi kararını her adımda yeniden zar atarak veriyor. Her ikisi de bigram'ın kör noktası.
Bir de referans: bağlamı hiç kullanmayan, her sonraki harfi 27 sembolden eşit olasılıkla çeken bir "model" kursaydık sonuçlar şöyle olurdu: ptuysx, lyqxclfno, egxtducpc, weezwgynreyrzxhidcahp. Bigram'ınkiler bunun yanında belirgin biçimde daha isim-benzeri: jayson, mai. Yani sayım gerçekten bir şey öğrendi, ama çok değil.
Modelin tek bilgisi bir önceki harf. Ayrıca isim "bitiş" kararını da bu tek harften veriyor. İki harfe (trigram) ya da üç harfe bakan tablolar da kurulabilir, ama tablo üstel büyür: bir harflik bağlamda 27 × 27 = 729 hücre varken iki harflik bağlamda 27 × 27 × 27 = 19.683, üç harflikte 531.441 hücre olur; veri aynı kaldığı için çoğu hücre boş kalır ve olasılıklar güvenilmez olur. Bu yüzden sayım tablosu bir noktadan sonra kullanışsızlaşır; sinir ağları bu sorunu çözmek için kullanılır (Bölüm 56). Ama önce, bu basit modelin ne kadar iyi olduğunu ölçmeyi öğrenmeliyiz (Bölüm 54).
Uygulamamızda bigram fikri harf için değil, ders geçişi için aklına gelebilir: kullanıcıların bir bölümden sonra hangi bölüme geçtiği kayıt altına alınırsa ("Anayasa Hukuku 1" → "Anayasa Hukuku 2"), her bölüm çifti için bir sayaç tutulur ve sayaçlar satır toplamına bölünerek "bu bölümden sonra kullanıcılar çoğunlukla şuraya geçiyor" tablosu kurulur: bu tam olarak bu bölümün tablosu. Veritabanında (Bölüm 38–39) bir tablo olarak iki sütunlu bir sayaç, ya da Redis'te (Bölüm 40) bir sayaç kümesi olurdu. Bu bir düşünce örneği: uygulamada böyle bir özellik planlanmış değil, ve bizim durumumuzda amaç dil üretmek değil bir öneri yapmak olurdu. Ama yapı aynı: ham sayımlar, normalleştirme, olasılık; hiçbir "yapay zekâ" gerekmeden de bir tahmin elde edilebilir. Prototipinde bir sözlük (JavaScript'te {} nesnesi) ile bile yazılabilir.
Bölüm 47'de "model, parametre, eğitim, çıkarım" demiştik. Bu bölümde dört kavram da en yalın haliyle ortaya çıktı: model 27 × 27'lik tablo, parametreler 729 sayı, eğitim veriyi dolaşıp saymak, çıkarım tablodan örnekleme. Bölüm 52'deki gradyan inişi burada yok; çünkü bu modelin en iyi parametreleri doğrudan hesaplanabiliyor (sayarak). Ama modelin "ne kadar iyi" olduğunu söylemek için Bölüm 52'deki gibi bir kayıp gerekecek: bir sonraki bölümün konusu.
Bu bölümde neler kuruldu
Bir dil modeli, metnin sonraki parçasını tahmin eden, çıktısı bir olasılık dağılımı olan modeldir; makemore bunu harf düzeyinde ve otoregresif olarak yapar. Veri kümesi 32.033 isimdir (names.txt); her isim başa ve sona bir başlangıç/bitiş işareti (.) alır, her komşu çift bir eğitim örneğidir (toplam 228.146). Bigram modeli yalnızca bir önceki harfe bakar; 27 × 27'lik bir sayım tablosu bütün modeldir. Her satırı toplamına bölmek (normalleştirme) sayıları olasılığa çevirir: P(n | a) ≈ 0,16. Tablodan örnekleme yaparak yeni isimler üretilir; ortalama uzunluk doğru yakalanır, ama bir harflik hafıza yüzünden tek harfli isimler gibi hatalar da çıkar.
Terimler sözlükte, Bölüm 53 başlığı altında.
Karpathy, nn-zero-to-hero, makemore_part1_bigrams.ipynb: ilk yarısı (veri kümesinin okunması, sayım tablosu, normalleştirme, örnekleme). Derinleşmek için: makemore README'si (ne yaptığı, model yelpazesi) ve names.txt dosyasının kendisi: dosyayı bir kez gözünle gez.
Takıldığın her yeri yapay zekâ öğretmenine sor. Bölüme özgü alıştırma: ondan, 5 kısa isimlik bir mini veri kümesi (örneğin ali, ayse, ada, ela, eda) kurmasını ve sayım tablosunu seninle elle doldurmasını iste; sonra "a"dan sonraki harf için normalleştirilmiş olasılıkları kâğıtta hesapla.
Bölüm sonu soruları
Kitaba bakmadan cevapla. Kâğıt kalem serbest.
.) eklenir?Örnek cevabı göster
Bigram modelinin parametreleri 27 × 27 sayım tablosunun hücreleridir (ya da onların satır toplamına bölünmüş olasılıkları). Eğitim, veri kümesindeki her komşu harf çiftini bir kez dolaşıp ilgili hücreyi bir artırmaktan ibaret; gradyan, kayıp ya da öğrenme oranı yok. Gradyan inişinde parametreler küçük adımlarla, kayıp azalsın diye yinelemeli olarak ayarlanır; burada en iyi parametreler tek geçişte doğrudan sayılarak bulunur. Bölüm 55'te aynı modeli gradyan inişiyle de kuracağız.
ali, ada. İşaretlerle yazıp bütün komşu çiftleri say ve P(l | a) ile P(d | a) hesapla.Cevabı göster
İşaretli: .ali. ve .ada.; çiftler: .a, al, li, i. ve .a, ad, da, a. . "a" satırı: al (1), ad (1), a. (1); toplam 3. P(l | a) = 1/3 ≈ 0,33 ve P(d | a) = 1/3 ≈ 0,33; kalan üçte bir, "a"dan sonra ismin bitmesi. (".a" çiftleri "a" satırına değil nokta satırına girer; "a" satırı yalnızca "a"dan sonra gelenleri sayar.)
Kaynaklar
- Karpathy, makemore README'si: makemore'un tanımı (tek metin dosyası, her satır bir örnek, harf düzeyinde otoregresif dil modeli, bigram'dan Transformer'a),
names.txt'nin ssa.gov 2018'in en yaygın 32 bin ismi olduğu, model listesi. - Karpathy, nn-zero-to-hero,
makemore_part1_bigrams.ipynb:stoi/itos, başlangıç/bitiş için tek bir.işareti, sayım tablosu, normalleştirme,torch.multinomialile örnekleme,keepdimtuzağı.