CodeBlanks Logo Icon CodeBlanks

Encoder bloğu

Transformer encoder bloğu tanımlarsın.

Encoder bloğunu kurmak

Dikkat katmanını artık bağlantı, LayerNorm ve FFN ile sarıp tam bir encoder bloğu yapıyoruz. Girişe konum kodu da eklenir:

def encoder(X, pad):
    H = E[X] + konum_kodu(X.shape[1], d)
    Y, A = dikkat(H @ Wq, H @ Wk, H @ Wv, pad)
    H = layer_norm(H + Y)
    H = layer_norm(H + np.maximum(0, H @ W1) @ W2)
    return H, A

Çıktı her kelime için bağlamlı bir vektördür; "iyi" kelimesinin temsili artık cümlenin geri kalanına da bağlıdır.

Cümle vektörü: maskeli ortalama

Sınıflandırma için her cümleden tek bir vektör gerekir. Basit bir yol, yalnızca gerçek kelimelerin ortalamasını almaktır (BERT bunun yerine [CLS] belirtecini kullanır):

m = (~pad)[:, :, None]                   # (5, 5, 1)
cumle = (H * m).sum(axis=1) / m.sum(axis=1)   # (5, d)

Dolguyu ortalamaya katmak, kısa cümlelerin vektörünü dolgunun temsiline doğru çekerdi.

Uygulama Encoder ve cümle vektörü

encoder(X, pad) fonksiyonunu tamamla ve maskeli ortalamayla cümle vektörlerini hesapla. H ve cümle vektörlerinin şeklini aynı satırda yazdır. Ardından "iyi" kelimesinin 1. cümledeki (konum 2) ve 3. cümledeki (konum 0) temsillerinin farklı olduğunu np.allclose ile, ve dolgunun cümle vektörünü etkilemediğini (dolgu gömmesini değiştirip yeniden hesaplayarak) np.allclose ile aynı satırda yazdır.

Başlangıç kodu

model.py
import numpy as np

def softmax(z, eksen=-1):
    e = np.exp(z - z.max(axis=eksen, keepdims=True))
    return e / e.sum(axis=eksen, keepdims=True)

metinler = ["film çok iyi", "çok kötü bir film", "iyi", "oyuncular iyi ama senaryo kötü", "kötü"]
sozluk = {"<pad>": 0}
for c in metinler:
    for k in c.split():
        sozluk.setdefault(k, len(sozluk))

def kodla(c, n=5):
    ids = [sozluk[k] for k in c.split()][:n]
    return ids + [0] * (n - len(ids))

X = np.array([kodla(c) for c in metinler])
pad = X == 0

def dikkat(Q, K, V, pad):
    d = Q.shape[-1]
    skor = Q @ K.transpose(0, 2, 1) / np.sqrt(d)
    skor = np.where(pad[:, None, :], -1e9, skor)
    A = softmax(skor)
    return A @ V, A

def layer_norm(x, eps=1e-5):
    return (x - x.mean(-1, keepdims=True)) / np.sqrt(x.var(-1, keepdims=True) + eps)

def konum_kodu(n, d):
    aci = np.arange(n)[:, None] / 10000 ** (np.arange(0, d, 2) / d)
    PE = np.zeros((n, d))
    PE[:, 0::2], PE[:, 1::2] = np.sin(aci), np.cos(aci)
    return PE

rng = np.random.default_rng(0)
d = 8
E = rng.normal(size=(len(sozluk), d)) * 0.5
Wq, Wk, Wv = rng.normal(size=(3, d, d)) * 0.5
W1 = rng.normal(size=(d, 4 * d)) * 0.3
W2 = rng.normal(size=(4 * d, d)) * 0.3

# buraya yaz

Örnek çözüm

model.py
import numpy as np

def softmax(z, eksen=-1):
    e = np.exp(z - z.max(axis=eksen, keepdims=True))
    return e / e.sum(axis=eksen, keepdims=True)

metinler = ["film çok iyi", "çok kötü bir film", "iyi", "oyuncular iyi ama senaryo kötü", "kötü"]
sozluk = {"<pad>": 0}
for c in metinler:
    for k in c.split():
        sozluk.setdefault(k, len(sozluk))

def kodla(c, n=5):
    ids = [sozluk[k] for k in c.split()][:n]
    return ids + [0] * (n - len(ids))

X = np.array([kodla(c) for c in metinler])
pad = X == 0

def dikkat(Q, K, V, pad):
    d = Q.shape[-1]
    skor = Q @ K.transpose(0, 2, 1) / np.sqrt(d)
    skor = np.where(pad[:, None, :], -1e9, skor)
    A = softmax(skor)
    return A @ V, A

def layer_norm(x, eps=1e-5):
    return (x - x.mean(-1, keepdims=True)) / np.sqrt(x.var(-1, keepdims=True) + eps)

def konum_kodu(n, d):
    aci = np.arange(n)[:, None] / 10000 ** (np.arange(0, d, 2) / d)
    PE = np.zeros((n, d))
    PE[:, 0::2], PE[:, 1::2] = np.sin(aci), np.cos(aci)
    return PE

rng = np.random.default_rng(0)
d = 8
E = rng.normal(size=(len(sozluk), d)) * 0.5
Wq, Wk, Wv = rng.normal(size=(3, d, d)) * 0.5
W1 = rng.normal(size=(d, 4 * d)) * 0.3
W2 = rng.normal(size=(4 * d, d)) * 0.3

def encoder(X, pad):
    H = E[X] + konum_kodu(X.shape[1], d)
    Y, A = dikkat(H @ Wq, H @ Wk, H @ Wv, pad)
    H = layer_norm(H + Y)
    return layer_norm(H + np.maximum(0, H @ W1) @ W2)

def cumle_vektoru(H, pad):
    m = (~pad)[:, :, None]
    return (H * m).sum(axis=1) / m.sum(axis=1)

H = encoder(X, pad)
C = cumle_vektoru(H, pad)
print(H.shape, C.shape)
E[0] += 10
C2 = cumle_vektoru(encoder(X, pad), pad)
print(np.allclose(H[0, 2], H[2, 0]), np.allclose(C, C2))

Beklenen çıktı

(5, 5, 8) (5, 8)
False True

Soru ve cevaplar

Dersin sorularını cevaplarıyla birlikte inceleyebilirsin. Kendini denemek istersen dersi interaktif olarak çöz.

1 "film çok iyi" ve "iyi" cümlelerinde "iyi" kelimesinin encoder çıktısı aynı mı olur?

  • Encoder kelimeye bakmaz
  • Hayır; bağlam ve konum farklı olduğu için değişir Doğru cevap
  • Evet, aynı kelime aynı vektördür
  • Yalnızca dolgu farklıdır

Açıklama: Gömme aynıdır, ama dikkat ve konum kodu temsili bağlama göre değiştirir.

2 Yalnızca gerçek kelimeleri topla.

Soru
cumle = (H * m).sum(axis=1) / m.____(axis=1)
Cevap
cumle = (H * m).sum(axis=1) / m.sum(axis=1)

Açıklama: Bölen, cümledeki gerçek kelime sayısıdır.

3 Eşleştir.

Kavram Anlamı
H + Y Artık bağlantı
konum_kodu Sıra bilgisi
maskeli ortalama Cümle vektörü
[CLS] BERT'in cümle belirteci

Uzman bölümündeki tüm dersler