Kısım III · Merak · Bölüm 55

Bigram'ı Sinir Ağıyla Kurmak

Aynı tahmine öğrenerek varmak (The neural-network bigram)

Neden burada · Neye dayanıyor

Neden burada: Bölüm 53 harf çiftlerini sayarak bir model kurdu; Bölüm 54 onun ortalama negatif log olabilirliğini yaklaşık 2,454 buldu. Şimdi rastgele ağırlıklardan başlayıp gradyan inişiyle aynı olasılıklara yaklaşacağız. Değişen, modelin gördüğü bilgi değil, parametrelerin bulunma yolu.

Neye dayanıyor: Bölüm 51: ağırlıklı toplam. Bölüm 52: eğitim döngüsü. Bölüm 53: harf numaraları ve çiftler. Bölüm 54: ortalama NLL. Geri yayılımı yeniden kurmayacağız; motor Bölüm 50’de.

  1. Kat 10Uygulama olarak yapay zekâModel, kayıp ve eğitim döngüsü: başka programlar gibi çalışan bir program.
  2. Kat 5–9Python, kütüphaneler ve işletim sistemiHesabı ifade eder, dosyayı okur, sayıları bellekte tutar.
  3. Kat 3CPU, RAM ve GPUMatrislerdeki toplama ve çarpmaların fiziksel olarak yürütüldüğü yer. Bölüm 6.5'e dönüyoruz.
  4. Kat 0–2Bit, mantık kapısı, devreAğırlık, harf numarası ve olasılık da sonuçta bitlerle temsil edilir.

Bölüm sonunda olasılıklar = softmax(tek_sıcak_girdi @ W) satırını okuyabileceksin: harfin nasıl temsil edildiğini, çarpımın hangi ağırlıkları seçtiğini, softmax’ın neden dağılım verdiğini bileceksin. Karpathy’nin yazılı bigram not defterindeki yol budur; aşağıdaki deneyler names.txt üzerinde saf Python’la yeniden çalıştırıldı.12

55.1 Tek-sıcak kodlama

Girdimiz hâlâ bir önceki harf. emma isminin beş eğitim çifti değişmedi: .e, em, mm, ma, a.. Nokta başlangıçta "isim başlıyor", çıktı olarak seçildiğinde "isim bitti" demek. Bölüm 53.2'deki numaralarla bunlar şu iki listedir; x girdi, y gerçekleşmiş sonraki harf, yani etikettir:1

x = [0, 5, 13, 13, 1]   # .  e  m  m  a
y = [5, 13, 13, 1, 0]   # e  m  m  a  .

13’ün iki kez görünmesi önemlidir: mm ve ma aynı girdiye farklı etiketler verir. Bu bir veri hatası değildir. Ağın görevi m’den sonra tek bir zorunlu cevap ezberlemek değil, olası devamların dağılımını öğrenmektir.

Harf numarası neden tek başına nöron girdisi olmasın?

Bir nörona doğrudan 13 verirsek ağırlığını 13 ile, 26 verirsek iki katıyla çarpar. Fakat bunlar miktar değil kimliktir: z, m’nin iki katı değildir. Numaralar yalnızca adresleme içindi. Bu kimliği miktar gibi işlemeden ağırlıklı toplama sokmak için not defteri tek-sıcak gösterimi kullanır.1

Tek-sıcak kodlama (one-hot encoding), her kimliğe ayrı bir yer ayırıp yalnızca seçilenin yerini 1, diğerlerini 0 yapar. Burada 27 yer gerekir. Bu liste, Bölüm 48’deki vektör (vector) fikridir. Kodlama burada temsil kuralı demek, kod yazmak değil.

GirdiNumara27 elemanlı gösterim
.0[1, 0, 0, 0, …, 0]
a1[0, 1, 0, 0, …, 0]
b2[0, 0, 1, 0, …, 0]
m13Yalnızca 13 numaralı yer 1; kalan 26 yer 0.

Numaraların sıfırdan başladığını unutma: 13 numaralı yer, listede on dördüncü yerdir. Saf Python'da tek bir girdiyi çevirmek şöyle yazılır:

girdi = [0.0] * 27
girdi[13] = 1.0

İlk satır 27 sıfır oluşturur; ikincisi m’nin yerini açar. Harfin anlamını temsil etmiyoruz, kullanılacak ağırlıkları seçiyoruz. Benzer harflerin benzer temsiller öğrenmesi Bölüm 56.2’deki gömmenin konusu.

Bu UTF-8'in yerine geçen bir kodlama değil

Bölüm 2.5’te UTF-8 dosyadaki byte’ları harflere bağlıyordu. Burada dosya okunmuş durumda: byte → harf → bizim sözlüğümüzde numara → tek-sıcak vektör. Bunlar ayrı temsil kararlarıdır; m’nin modeldeki numarası Unicode kod noktası değildir.

55.2 Logit, softmax: sayıları olasılığa çevirmek

Her sonraki harf için bir ham skor

Her olası sonraki sembole bir ham sayı verelim. Olasılık olmayan bu sayı logit (logit)’tir: negatif veya 1’den büyük olabilir, toplamları 1 olmak zorunda değildir. Not defteri bunları “log-counts”, sayımların logaritmaları gibi okunabilen skorlar olarak adlandırır.1

W, 27 satır × 27 sütunluk ağırlık tablosudur: satır önceki, sütun sonraki harf. Her çıktı nöronunun 27 ağırlığı bir sütunda durur. Sapma veya gizli katman eklemiyoruz: toplam 729 ağırlık. Bölüm 51.4’ün parametre hesabında bu kez sapmaların sayısı sıfırdır.1

Birazdan çarpımı açacağız; şimdilik sonucu kullan: m’nin tek-sıcak girdisi W ile çarpılınca m satırındaki 27 logit gelir. Parametreler aynıyken aynı harf aynı satırı verir; daha eski harfler ağa verilmediğinden hatırlanamaz.

Ham skorları neden yalnızca toplamlarına bölmüyoruz?

Bölüm 53.4'te sayımlar negatif değildi; satır toplamına bölmek yeterliydi. Logit'ler ise negatif olabilir ve toplamları sıfır çıkabilir. Önce bütün değerleri pozitif hale getiren bir dönüşüm gerekir. Softmax (softmax) bunu üstel fonksiyonla yapar, ardından her sonucu aynı satırın toplamına böler. PyTorch'un resmî tanımındaki kural şudur:3

pozitif_skor[j] = exp(logit[j])
olasılık[j]     = pozitif_skor[j] / sum(pozitif_skor)

exp(z), e’nin z’nci kuvvetidir; doğal logaritmanın tersidir. Üstel değerler pozitiftir; hepsini aynı toplama bölmek toplamı 1 yapar. Büyük logit daha büyük pay alır. Eğitimde doğru harfin payı ölçülür, üretimde dağılımdan çekiliş yapılır.

Yalnızca hesabı görmek için, isim modelimizin 27 çıktısını geçici olarak üç çıktıya indirelim. Logit'ler [0, 1, 2] olsun. Python'la hesaplanan softmax sonucu:

LogitOlasılıkYorum
00,090031En düşük skor, en düşük pay.
10,244728Ortadaki skorun payı.
20,665241En yüksek skor, yine de kesinlik değil.

Softmax en büyüğü seçip diğerlerini atmaz: sonucun bir dağılım olması gerekir. Bütün logit’ler sıfırsa paylar eşittir. Bizim 27 sembollü modelimizde her pay 1/27, kayıp ln(27) = 3,295837 olur; bu Bölüm 54’ün taban çizgisidir.

Sayısal güvenlik: büyük sayıyı üretmeden aynı sonucu bulmak

Kayan noktanın sınırları (Bölüm 4.4) nedeniyle büyük bir logit’in üssü taşabilir. Bütün logit’lerden aynı sayıyı çıkarmak softmax’ı değiştirmez: pay ile payda aynı üstel çarpana bölünür. En büyük logit’i çıkarırsak en büyük üs exp(0) olur. Bu yeni bir model değil, aynı hesabın güvenli yazımıdır.

import math

def softmax(satir):
    en_buyuk = max(satir)
    usler = [math.exp(z - en_buyuk) for z in satir]
    toplam = sum(usler)
    return [u / toplam for u in usler]

max en büyük sayıyı bulur; liste işlemi onu her skordan çıkarıp üs alır; sum paydadır; son satır oranları verir. Bu fonksiyonla [1000,1001,1002] de [0,1,2] ile aynı dağılımı verdi. Aşırı küçük olasılıkların sıfıra yuvarlanmasını bu tek önlem çözmez; kaybı doğrudan logit’lerden hesaplamaya döneceğiz.

Sayım ile softmax'ın tam buluştuğu yer

Pozitif sayımlarda logit’leri log(sayım) seçmek, softmax’ı sayım/toplam yapar. Sıfır sayımın sonlu logaritması olmadığından eşitliği +1 yumuşatılmış tabloyla sınadık: W[i][j]=log(N[i][j]+1) seçince sonuç (N[i][j]+1)/(satır_toplamı+27) olur. İki yoldan hesaplanan olasılıkların farkı 10−14’ten küçük çıktı. Bu bir eşdeğerlik kontrolü; eğitimde W’yi bu değerlerle başlatmayacağız.

55.3 Tensör ve matris çarpımı

Bir sayıyı değil, sayılar düzenini işlemek

Bölüm 52.5’te geçen tensör (tensor), sayıları eksenlerde tutar: tek sayı sıfır boyutlu, vektör bir boyutlu, satır–sütunlu matris (matrix) iki boyutludur. Daha çok eksen de mümkündür. PyTorch girdileri, çıktıları ve parametreleri bu ortak yapıda tutar.4

Tensörün şekli (shape) her eksenin uzunluğudur: (5,27), beş satırda yirmi yedişer değer demektir. Veri tipi sayı gösterimini, aygıt (device) verinin CPU’da mı başka hesap aygıtında mı tutulduğunu belirtir. Bunlar sayısal içerikten ayrı bilgilerdir.4

ParçaBeş çift için şekilHer elemanın anlamı
xs(5,)Önceki harfin numarası.
ys(5,)Gerçekleşmiş sonraki harfin numarası.
xenc(5, 27)Her girdi için tek-sıcak gösterim.
W(27, 27)Önceki–sonraki harf ağırlığı.
logits, probs(5, 27)Her örnek ve her olası çıktı için skor ya da olasılık.

(5,) beş eksen değil, uzunluğu beş olan tek eksendir. Bütün veride örnek ekseni 228.146 olur; eğitim parametresi W ise yine 27 × 27 kalır.

Matris çarpımı: satır ile sütunun buluşması

Matris çarpımı (matrix multiplication), soldaki satırla sağdaki sütunun karşılıklı elemanlarını çarpıp toplar. İç boyutlar eşleşir: (B,27) @ (27,27) sonucu (B,27); B örnek sayısıdır. @ bu çarpım, * aynı konumdaki elemanların çarpımıdır.45

logit[örnek, sonraki] =
    xenc[örnek, 0] * W[0, sonraki]
  + xenc[örnek, 1] * W[1, sonraki]
  + ...
  + xenc[örnek, 26] * W[26, sonraki]

Bu uzun görünen toplam, Bölüm 51.1'deki nöronun toplamıdır. Tek-sıcak girdide bir çarpan 1, diğerleri 0 olduğundan bütün terimler silinir, biri kalır. Böylece tek-sıcak vektör ile matris çarpımı, matrisin bir satırını seçer. Küçük ve gerçekten hesaplanmış örnek:

                  [1, 2]
[0, 1, 0]    @     [3, 4]    =    [3, 4]
                  [5, 6]

ilk çıktı  = 0*1 + 1*3 + 0*5 = 3
ikinci çıktı = 0*2 + 1*4 + 0*6 = 4

Sayım modeli "önceki harfin satırını getir", ağ "tek-sıcak girdiyi W ile çarp" der. İkisi de satır seçer; ilkinde satır olasılıkları, ikincisinde öğrenilecek logit’leri tutar. makemore’un Bigram sınıfı bu eşdeğerlikle self.logits[idx] üzerinden doğrudan satır getirir; get_block_size() bir harflik bağlamı belirtir.6

Satır toplamının şeklini kaybetme: yayınlama

Bölüm 53.4'te ertelenen tuzak şimdi anlaşılabilir. PyTorch bazı işlemlerde boyutları uyumlu tensörleri otomatik olarak genişletilmiş gibi işler; buna yayınlama (broadcasting) denir. Kurallar boyutları sağdan hizalar; iki boyut eşit olmalı veya biri 1 olmalıdır. Böylece (5, 27) biçimli skorları (5, 1) biçimli satır toplamlarına bölersen her satır kendi toplamına bölünür.7

counts.sum(dim=1, keepdim=True)   # (5, 27) → (5, 1)
counts.sum(dim=1)                 # (5, 27) → (5,)

dim=1 sütunlar boyunca toplar; keepdim=True toplanan ekseni uzunluğu 1 olarak korur. Onsuz çıkan (5,), 27 sütunla hizalanamaz. Daha sinsi durum (27,27) tablosudur: (27,) şeklen uyumludur, ama her sütunu farklı bir satır toplamına böler. Programın çalışması hesabın doğru olduğunu göstermez.

İki satırlık örneği saf Python'la doğruladık: [[1,2],[3,4]] için satır toplamları 3 ve 7'dir. Doğru bölme [[1/3,2/3],[3/7,4/7]]; ekseni kaybolan toplamın sütunlara hizalandığı yanlış bölme [[1/3,2/7],[3/3,4/7]] olur. İkinci tabloda satırlar 1'e toplanmaz. Öğretici sürümde keepdim=True yaz, ardından her olasılık satırının toplamının yaklaşık 1 olduğunu sına. Hazır F.softmax(logits, dim=1) çağrısı aynı satır yönünü açıkça seçer.3

PyTorch'taki ileri geçişi artık okuyabiliriz

import torch
import torch.nn.functional as F

xenc = F.one_hot(xs, num_classes=27).float()
logits = xenc @ W
probs = F.softmax(logits, dim=1)
loss = -probs[torch.arange(len(ys)), ys].log().mean()

İki import satırı PyTorch’u ve fonksiyonları için F kısa adını getirir. Burada xs ile ys, harf numaralarını tutan tamsayı tensörleri; W, 27 × 27 ağırlık matrisidir. Sonraki üç satır kimliği temsil eder, ağırlık satırını seçer, onu dağılıma çevirir. Son satırda torch.arange(len(ys)) örnek numaralarını, ys doğru sütunları verir. İkisi her örneğin doğru harf olasılığını seçer. log() logaritma, mean() ortalamadır; eksi işareti bunu Bölüm 54’ün kaybına çevirir.1

Kayıp için hazır F.cross_entropy(logits, ys) çağrısı kullanılabilir. Çapraz entropi (cross entropy), burada doğru sınıf indeksleriyle aynı ortalama NLL’yi hesaplar; olasılık değil logit bekler.8 Elle hesapta da kaybı log(sum(exp(logit))) − doğru_logit biçiminde, en büyük logit’i çıkararak bulmak, önce çok küçük olasılık üretmekten kaçınır.

Eğitim: aynı döngü, yeni parametre matrisi

Bölüm 52.3’ün döngüsü aynı: ileri geçiş kaybı verir, geri yayılım her W ağırlığının eğimini bulur, güncelleme ters yönde gider. Not defterindeki öğrenme oranı 50 bu modele aittir; evrensel bir iyi değer değildir.1

W.grad = None
loss.backward()
W.data += -50 * W.grad

İlk satır önceki gradyanı temizler; ikincisi bu turun eğimlerini bulur; üçüncüsü W’yi değiştirir. W.data not defterinin öğretici yazımıdır. Kayıp "ne kadar kötü", gradyan "hangi ağırlığı hangi yöne değiştirirsem ne olur" sorusunu cevaplar.

Deneyimizi nasıl çalıştırdık?

Ortamda NumPy/PyTorch bulunmadığından aynı modeli standart Python listeleriyle eğittik. Veri: 32.033 satır, 228.146 çift, 27 sembol.2 Bütün veriyi kullandık; aşağıdaki ölçüler eğitim ölçüleridir. Görülmeyen veri başarısını değil, iki yolun aynı hedefe yaklaşmasını sınar. Ayrı kümeler Bölüm 56.4’te.

Hesabı hızlandıran ama modeli değiştirmeyen bir eşdeğerlik kullandık: aynı (önceki, sonraki) çift yüzlerce kez görülüyorsa aynı kayıp terimini yüzlerce kez hesaplamak yerine, bir kez hesaplayıp görülme sayısıyla çarparız. N[i][j] çiftin sayısı, R[i] i satırının toplamı, T toplam çift sayısı olsun. Eğitim kaybı ve onun ağırlığa göre türevi şu hale gelir:

L = -(1/T) * sum(N[i][j] * log(P[i][j]))

grad[i][j] = (R[i] * P[i][j] - N[i][j]) / T

Bu türev, "model dağılımı eksi gözlenen dağılım" farkını satırın veri içindeki payıyla çarpar; sıfır sayım kayıp toplamına katkı yapmaz. Genel otomatik türev motoru yerine bu özel ağın gradyanını elle yazıyoruz. Dört ağırlıktaki küçük sayısal değişimle türev farkı 10−9’un altında kaldı. Gruplu kayıp ile 228.146 çiftin tek tek kaybı da aynı çıktı: 3,8132613430754767.

Tekrarlamak isteyen için aşağıdaki kod, names.txt ile aynı klasörde çalıştırılabilecek tam bir saf Python eğitimidir. İlk sayımlar hazır olasılık yapmak için değil, tekrar eden kayıp terimlerini gruplayabilmek için kullanılır. W rastgele başlar; N/R doğrudan W'ye atanmaz.

from pathlib import Path
import math, random

words = Path("names.txt").read_text().splitlines()
chars = "." + "".join(sorted(set("".join(words))))
stoi = {c: i for i, c in enumerate(chars)}
V = len(chars)
N = [[0] * V for _ in range(V)]
for word in words:
    s = "." + word + "."
    for a, b in zip(s, s[1:]):
        N[stoi[a]][stoi[b]] += 1
R = [sum(row) for row in N]
T = sum(R)
rng = random.Random(2147483647)
W = [[rng.gauss(0, 1) for _ in range(V)] for _ in range(V)]

for step in range(10001):
    P, nll = [], 0.0
    for i, row in enumerate(W):
        m = max(row)
        e = [math.exp(z - m) for z in row]
        total = sum(e)
        P.append([u / total for u in e])
        log_total = m + math.log(total)
        nll += sum(n * (log_total - row[j])
                   for j, n in enumerate(N[i]))
    nll /= T
    if step in (0, 1, 10, 100, 1000, 10000):
        print(step, round(nll, 6))
    if step == 10000:
        break
    for i in range(V):
        for j in range(V):
            grad = (R[i] * P[i][j] - N[i][j]) / T
            W[i][j] -= 50 * grad

İlk blok sözlüğü ve sayımları kurar; R satır toplamları, T çift sayısıdır. gauss(0,1) sıfır ortalama ve bir standart sapmayla rastgele ağırlık çeker. Döngüde e kaydırılmış üsleri, P olasılıkları tutar. log_total - row[j] o harfin NLL’sidir; N ile çarpım tekrarları hesaba katar, T’ye bölme ortalamayı verir. Son iki döngü aynı turun P’sinden gradyanları bulup W’yi değiştirir. Son tur yalnızca ölçer: 10.000 güncelleme yapılır.

Tamamlanan güncellemeEğitimde ortalama NLLOkuması
03,813261Rastgele başlangıç.
13,436566İlk düzeltme.
102,662318Genel harf geçişleri belirginleşiyor.
1002,472420Sayım modeline yaklaşıyor.
1.0002,455362Fark küçüldü.
10.0002,454145Ham sayımın 2,454014 değerine yakın.

Bunlar not defterinden kopyalanmış değil, yukarıdaki Python üreteciyle hesaplanmış sonuçlardır. Aynı tohum PyTorch’ta aynı başlangıç dizisini garanti etmez. Rastgele ağırlıklar da eşit dağılım değildir; yanlış harflere fazla pay ayırabildiklerinden başlangıçtaki 3,813, taban çizgisi 3,296’dan kötü olabilir.

"Aynı sonuca varmak" ne kadar aynı?

Aynılık ağırlıkların aynılığı değildir: bir satıra sabit eklemek dağılımı değiştirmez. Ham sayımda 102 sıfır hücre vardır; ideal gerçek sayı hesabında sonlu logit’lerin softmax’ı sıfır veremez, ancak yaklaşır. Dolayısıyla sonlu adımla bütün hücreler eşitlendi demiyoruz. Bilgisayarda çok küçük değerler ayrıca sıfıra yuvarlanabilir.

Gradyan sıfıra yaklaşınca R[i]*P[i][j] − N[i][j] küçülür: P gözlenen N/R oranına yaklaşır. Eğitim sonunda P(n|a)≈0,160484; sayım da aynı altı ondalığı verir. Eğitim yolu değişti, temsil gücü değişmedi: ağ hâlâ yalnızca bir önceki harfe bakıyor.

Yumuşatmanın akrabası: düzenlileştirme

Not defteri kayba 0.01*(W**2).mean() ekler. Bu düzenlileştirme (regularization), ağırlıkların karelerinin ortalamasını cezalandırır.1 Sıfır logit eşit dağılım verdiğinden, ağırlıkları sıfıra çeken baskı aşırı keskinleşmeye karşı çalışır. +1 yumuşatmayla benzer ihtiyaca yönelir, ama aynı formül değildir.

Aynı başlangıçla ve aynı 10.000 güncellemeyle bir de bu cezayı ekledik. Toplam amaç L = NLL + 0.01*ortalama(W²), gradyana ek katkı 2*0.01*W[i][j]/729 oldu. Üç sayıyı ayrı tutmak zorundayız:

Ölçü10.000 güncelleme sonrası
Yalnızca verinin ortalama NLL'si2,460362
Ağırlık cezası0,020037
Enküçültülen toplam amaç2,480399

Ham sayımla veri uyumunu karşılaştırırken 2,480399 toplamına değil, 2,460362 NLL’sine bak. Cezalı deneyde görülmemiş jq çiftine yaklaşık 0,002299; cezasız deneyde 0,000093 olasılık verildi. İkisinde de olasılık pozitifti; düzenlileştirme pozitifliği ilk kez yaratmadı, ne kadar küçüleceğini etkiledi. Ayrı sınama yapmadığımızdan bunun yeni veriye mutlaka yarar sağladığını söyleyemeyiz.

Eğitilmiş ağı kullanmak

Bölüm 53.5’in çekiliş döngüsünde artık P[ix] yerine softmax(W[ix]) kullanılır; diğer adımlar aynı kalır. Üretimde gradyan ve güncelleme yoktur. Cezasız W ile aynı Python tohumu kullanıldığında ilk on örnek sayım modelindekilerle denk geldi; bu bütün olasılıkların eşitliğini kanıtlamaz. Tek harfli ve tuhaf uzun isimler de kaldı: ağın bağlamı hâlâ bir harf.

55.4 GPU neden: aynı işlemi binlerce kez paralel yapmak

Bölüm 6.5’e dönelim. Matris çarpımındaki birçok çıktı hücresi aynı tür çarpma–toplamayla, paralel hesaplanabilir. Grafik işlemci (graphics processing unit, GPU) yüksek paralellik içeren işlere uygundur. NVIDIA’nın rehberi farkı, CPU’nun bir komut dizisini hızlı yürütmesi ile GPU’nun binlerce iş parçacığıyla yüksek toplam iş hacmi hedeflemesi üzerinden açıklar.9

GPU modeli daha zeki yapmaz; aynı matematiğin uygun donanımda yürütülmesini sağlar. CPU da program akışı ve veri hazırlama gibi işleri sürdürebilir. Değişen, uygun hesapların süresidir; bağlam ve kayıp kendiliğinden değişmez.

Tensörler donanıma giden ortak kapıdır

PyTorch’ta tensörün aygıtı bellidir; onu hızlandırıcıya taşımak açık bir işlemdir. Programcı her çarpma için donanım komutu yazmak yerine toplu tensör işlemini çağırır. Ancak aygıtlar arasında veri kopyalamak zaman ve bellek gerektirir; resmî öğretici bunu özellikle belirtir.4

Bu, Bölüm 1.4’ün soyutlamasıdır: üstte matris çarpımı arayüzü, altta donanıma uygun gerçekleme. Tek tek Python döngüsüyle toplu matris çağrısı aynı matematiği ifade edebilir; ama aynı yürütme biçimini istemez.

Bu küçük model GPU'yu zorunlu kılıyor mu?

Hayır: iki eğitimi de saf Python’la CPU’da yaptık. Üstelik burada çarpım satır seçmeye eşdeğer; sayımın tek geçişte çözdüğü işi eğitimle tekrarlamamızın amacı hız değil, daha sonraki ağların öğrenme dilini kurmaktı. makemore README’si de özel donanımı zorunlu tutmaz.10

Bütün tek-sıcak girdiyi 32 bit kayan noktayla saklamak 228146×27=6.159.942 sayı, yalnızca sayı yükü için 24.639.768 byte, yaklaşık 23,50 MiB ister. Python listelerinin ek yükü ve ara tensörler buna dahil değil. Doğrudan satır getirme, sıfırları saklayıp çarpmayı önler. Önce "işi azaltabilir miyim?", sonra "hangi donanım?" diye sor. GPU hızlanmasını ölçmedik; bir kat sayısı veremeyiz.

Uygulamamızda

Hukuk-Duolingo uygulamamızın puan, seri, can ve leaderboard kuralları için bu ağ gerekli değildir. Model deneyi yapılacaksa, örneğin uydurma deneme kullanıcı adları üretmek için, eğitim–çıkarım ayrımı ile gerçek kullanıcı verisine erişim kararını ayırırız. Vanilla HTML/CSS/JS prototipinin kodunu görmedik; böyle bir özellik içerdiğini varsaymıyoruz.

Planlanan Expo + TypeScript, Go, Postgres ve Redis kendi görevlerini korur. Agent’ın deneyinde veri kaynağı, ölçü ve üretim verisine yazma yasağı AGENTS.md ile belirlenip ayrı git çalışma ağacında incelenebilir. Bu bir ürün vaadi değil, Bölüm 46’daki görev sınırının örneğidir.

Bölüm 51–54 ile bağlantı

Bölüm 51’in ağırlıklı toplamları burada logit oldu; tanh/ReLU’lu gizli katman eklemedik. Bölüm 52’nin döngüsü W’yi güncelledi. Bölüm 53’ün satır seçimi tek-sıcak çarpımda yeniden göründü. Bölüm 54’ün NLL’si küçültülen sayı oldu. Dört bölüm artık tek bir çalışan düzenektir.

Bu bölümde neler kuruldu

Tek-sıcak kodlama kimliği ağırlık seçicisine çevirdi; matris çarpımı bir satırı getirdi. Softmax logit’lerden olasılık, NLL bunlardan kayıp üretti. Tensör şekilleri ile yayınlama tuzağını gördük. Eğitim kaybını 3,813261’den 2,454145’e indirip sayımın 2,454014 değerine yaklaştırdık. Düzenlileştirme eklenince veri kaybını toplam amaçtan ayırdık. GPU bu matematiği değiştiren zekâ değil, uygun hesapları paralel yürüten donanımdır. Sonraki adım bigram’ı daha uzun eğitmek değil, bağlamını büyütmek.

Yeni terimler sözlükte, Bölüm 55 başlığı altında; tensörün ilk kısa tanımı Bölüm 52 sözlüğünde.

Birincil okuma

Karpathy, makemore_part1_bigrams.ipynb: “create the training set of bigrams (x,y)”, “randomly initialize 27 neurons’ weights” ve “gradient descent” yorumlarıyla başlayan hücreler. Bu, yazılı kod ve çıktı içeren not defteridir. Derinleşme: makemore.py — Bigram sınıfı; Tensors — Attributes of a Tensor; Operations on Tensors.

Takıldığın yeri yapay zekâ öğretmenine sor. Üç sembollü sözlük ve 3 × 3 W matrisi iste: tek-sıcak girdiyi yaz, çarpımı elle yap, softmax’ı hesaplat. Sonra "aynı girdi için iki farklı etiket gelirse hangi satır değişir?" sorusunu, eksenlerin anlamını söyleyerek cevapla.

Bölüm sonu soruları

Kitaba bakmadan cevapla. Sayı gereken yerde kâğıt kalem kullan.

1. m'nin numarası 13 ise, 27 elemanlı tek-sıcak gösteriminde ne bulunur?
Tek-sıcak gösterim bir miktarı değil kimliği seçer: 13 numaralı yer 1, diğer bütün yerler 0 olur. Bkz. 55.1.
2. Bir logit için hangisi doğrudur?
Logit ham skordur; negatif olabilir, toplamı 1 olmak zorunda değildir. Olasılık dağılımını softmax oluşturur. Bkz. 55.2.
3. Bir satırdaki bütün logit'lerden aynı sabit çıkarılınca softmax nasıl değişir?
Pay ile paydadaki bütün üstel terimler aynı çarpanla değişir; oranlar sabit kalır. En büyük logit'i çıkarmak bu nedenle hesabı daha güvenli yapabilir. Bkz. 55.2.
4. (5, 27) @ (27, 27) matris çarpımının sonuç şekli nedir?
İçteki 27 boyutları eşleşir; sonuç soldakinin satır sayısını ve sağdakinin sütun sayısını alır. Bkz. 55.3.
5. Tek-sıcak bir vektörü W ile çarpmak, bu modelde neye eşdeğerdir?
Girdi satır vektöründeki tek 1, W'nin aynı numaralı satırını bırakır; diğer satırların katkısı sıfır olur. Bkz. 55.3.
6. Düzenlileştirmeli deneyde 2,460362 veri kaybı, 0,020037 ceza, 2,480399 toplam amaç ölçüldü. Ham sayımın yalnızca NLL'si ile hangisi karşılaştırılır?
Aynı tür ölçü karşılaştırılmalıdır: ham sayımın NLL'si yalnızca veriye uyumu ölçer; ceza eklenmiş amaç farklı bir büyüklüktür. Bkz. 55.3.
7. Sayım yerine sinir ağıyla öğrenilen bu bigram'ın bağlamı nasıl değişmiştir?
Eğitim yöntemi değişti, girdi değişmedi. W'nin seçilen satırı hâlâ yalnızca önceki harfin numarasına bağlıdır. Bkz. 55.1 ve 55.3.
8. Matris çarpımı gibi işler GPU'ya neden uygun olabilir?
Donanımın katkısı uygun hesapları paralel yürütme kapasitesidir; bağlamı veya kayıp seçimini kendiliğinden değiştirmez. Küçük işte hızlanma ayrıca ölçülmelidir. Bkz. 55.4.
9. Kendi cümlenle: tek-sıcak girdi → W ile çarpım → softmax → NLL → güncelleme zincirini anlat. Sayım tablosuyla ortak olan ve farklı olan kısımlar hangileridir?
Örnek cevabı göster

Tek-sıcak girdi W’nin ilgili logit satırını seçer; softmax dağılımı verir. Doğru harfin negatif log olasılığının ortalaması kayıptır; gradyan W’yi ters yönde günceller. Sayımda da satır seçilir, fakat olasılıklar yinelemeli eğitimle değil sayım/toplamla bulunur. İkisi de yalnızca önceki harfi görür. Bkz. 55.1, 55.2, 55.3.

10. W'nin satırları [1,2], [3,4], [5,6]; girdi [0,1,0] olsun. Çarpımın iki elemanını hesapla. Sonra aynı işlemi doğrudan satır seçimiyle nasıl yazacağını ve bunun donanım seçmeden önce ne kazandırdığını söyle.
Örnek cevabı göster

Birinci sonuç 0·1 + 1·3 + 0·5 = 3; ikinci sonuç 0·2 + 1·4 + 0·6 = 4. Doğrudan W[1] aynı [3,4] satırını getirir. Böylece tek-sıcak listenin sıfırlarını oluşturmak ve onlarla çarpmak gerekmez. Hesabı azaltmak başka şey, kalan hesabı GPU'ya taşımak başka şeydir. Bkz. 55.3 ve 55.4.

Kaynaklar

  1. Karpathy, makemore_part1_bigrams.ipynb: tek-sıcak girdi, 27 × 27 W, softmax, NLL, geri geçiş, oran 50, ağırlık kareleri cezası ve örnekleme. Sayısal deneyler bu kitap için yeniden çalıştırılmıştır.
  2. Karpathy, names.txt: 32.033 satır ve 228.146 çift. Eğitimler, gruplu/tek tek kayıp, sonlu farkla türev, softmax eşdeğerliği ve örnekleme saf Python’la doğrulanmıştır; ayrı test başarısı ölçülmemiştir.
  3. PyTorch, softmax: formül ve seçilen eksende normalleştirme. Sabit kaydırma eşdeğerliği formülden çıkarılıp Python’la sınanmıştır.
  4. PyTorch, Tensors — Attributes of a Tensor; Operations on Tensors: şekil, tip, aygıt, çarpma türleri, aygıtlar arası kopyalama.
  5. PyTorch, torch.matmul: vektör ve matris çarpımlarının şekilleri.
  6. Karpathy, makemore.py — class Bigram: sıfır logit matrisi, doğrudan satır getirme ve bir harflik bağlam; not defterinden ayrı gerçekleme.
  7. PyTorch, Broadcasting semantics — General semantics: sağdan hizalama ve boyut uyumu. 2 × 2 karşı örnek Python’la hesaplanmıştır.
  8. PyTorch, cross_entropy: logit girdisi, sınıf indeksleri ve ortalama kayıp.
  9. NVIDIA, CUDA Programming Guide — 1.1 Introduction; 1.1.2 The Benefits of Using GPUs: CPU/GPU hedefleri ve paralel iş hacmi. GPU hız kıyaslaması yapılmamıştır.
  10. Karpathy, makemore README — Usage: eğitim için özel donanım zorunluluğu olmaması.