Bottleneck context vector memaksa seluruh makna kalimat sumber melewati satu vektor berukuran tetap. Bahdanau, Cho, dan Bengio (2014) mengusulkan jalan keluar yang kelak menjadi fondasi hampir seluruh deep learning modern: biarkan decoder melihat kembali seluruh hidden state encoder, lalu hitung rangkuman yang berbeda untuk setiap kata yang sedang diterjemahkan. Mekanisme pemilihan ini dinamai attention — perhatian — karena model belajar memperhatikan bagian masukan yang relevan pada tiap langkah. Chapter ini membedah bentuk paling sederhananya dengan angka konkret: tiga kata sumber, tiga kata target, dan bobot yang berubah dari kata ke kata.

Dari Satu Rangkuman ke Rata-rata Berbobot #

Pada seq2seq klasik, decoder hanya menerima h_N — hidden state terakhir encoder. Attention memperluas aksesnya: seluruh hidden state h_1 sampai h_N tetap disimpan, dan setiap langkah decoder menghitung context vector sendiri lewat tiga langkah berurutan:

  1. skor relevansi e_j menilai kecocokan antara state decoder saat ini dan tiap hidden state h_j;
  2. softmax mengubah skor menjadi bobot α_1 sampai α_N yang jumlahnya tepat 1;
  3. context c = Σ_j α_j h_j — rata-rata berbobot seluruh hidden state.
KomponenPeran
skor relevansi e_jmenilai kecocokan state decoder dengan hidden state h_j
bobot α = softmax(e)porsi perhatian tiap kata sumber, jumlah tepat 1
context c = Σ α_j h_jrangkuman yang berbeda untuk tiap kata target

Karena α dihasilkan softmax, context selalu berada di antara hidden state para kata sumber — tidak ada informasi baru yang masuk, yang berubah hanyalah takarannya. Bentuk ini disebut soft attention: seluruh kata sumber ikut berkontribusi dalam kadar berbeda. Lawannya, hard attention yang memilih satu posisi saja, jarang dipakai karena pemilihan diskrit tidak menghasilkan gradient yang bisa dilatih.

Diagram di atas merangkum alurnya: satu state decoder dan tiga hidden state masuk ke perhitungan skor, softmax menakar bobotnya, lalu context dirangkai sebagai rata-rata berbobot — rantai yang sama seperti tiga langkah bernomor di atas.

Skor Additive ala Bahdanau #

Dalam makalah aslinya, skor dihitung oleh jaringan kecil yang dilatih: e_j = w^T tanh(W @ s + U @ h_j) — disebut additive attention karena state decoder dan hidden state encoder dijumlahkan di dalam tanh sebelum dinilai vektor bobot w. Jaringan penilai ini efektif, tetapi ia menuntut parameter tambahan dan dihitung pasangan demi pasangan. Ketika Transformer menggantikan skor additive dengan perkalian titik pada chapter Scaled Dot-Product Attention , salah satu alasannya adalah perkalian titik bisa dilakukan untuk semua pasangan sekaligus sebagai satu operasi matriks.

Tiga Kata Sumber, Tiga Kata Target #

Untuk melihat inti mekanismenya tanpa beban training, kita memakai skor relevansi manual pada kalimat mainan kucing hitam tidur. Skor dipilih dengan tangan agar pola bobotnya mudah dibaca; pada model terlatih, skor yang sama dihasilkan jaringan penilai:

python
"""Attention ala Bahdanau: konteks = rata-rata berbobot hidden state encoder.

Tiga kata sumber ("kucing", "hitam", "tidur") memiliki hidden state sendiri.
Untuk tiap kata target yang sedang diterjemahkan, kita tetapkan skor
relevansi terhadap ketiga kata sumber, mengubahnya menjadi bobot softmax,
lalu merangkai context vector sebagai rata-rata berbobot. Dua kata isi
menunjukkan fokus sempit; satu kata fungsi menunjukkan sebaran merata.
"""

import numpy as np


def softmax(v):
    e = np.exp(v - v.max())
    return e / e.sum()


np.set_printoptions(precision=3, suppress=True)

# Hidden state encoder: satu baris per kata sumber (dimensi 4)
sumber = ["kucing", "hitam", "tidur"]
H = np.array(
    [
        [1.0, 0.2, 0.1, 0.0],  # h_1 untuk "kucing"
        [0.1, 1.0, 0.2, 0.0],  # h_2 untuk "hitam"
        [0.2, 0.1, 1.0, 0.5],  # h_3 untuk "tidur"
    ]
)

# Skor relevansi manual (cara menghitungnya dibahas di badan chapter)
kasus = [
    ("hitam", np.array([1.0, 4.0, 0.5])),
    ("tidur", np.array([0.5, 1.0, 4.0])),
    ("the", np.array([1.0, 1.0, 1.0])),
]

print("Kata sumber :", sumber)
print("Hidden state:")
print(H)
print()

for kata, skor in kasus:
    alpha = softmax(skor)
    konteks = alpha @ H
    print(f"Kata target '{kata}' sedang diterjemahkan:")
    print(f"  skor relevansi e : {skor}")
    print(f"  bobot alpha      : {alpha}   (jumlah = {alpha.sum():.1f})")
    print(f"  konteks = alpha @ H : {konteks}")
    if alpha.max() > 0.5:
        fokus = int(alpha.argmax())
        print(f"  -> fokus sempit pada '{sumber[fokus]}' "
              f"(bobot {alpha[fokus]:.3f})")
    else:
        print("  -> sebaran merata, tidak ada fokus tunggal")
    print()

Output yang Dihasilkan:

text
Kata sumber : ['kucing', 'hitam', 'tidur']
Hidden state:
[[1.  0.2 0.1 0. ]
 [0.1 1.  0.2 0. ]
 [0.2 0.1 1.  0.5]]

Kata target 'hitam' sedang diterjemahkan:
  skor relevansi e : [1.  4.  0.5]
  bobot alpha      : [0.046 0.926 0.028]   (jumlah = 1.0)
  konteks = alpha @ H : [0.144 0.938 0.218 0.014]
  -> fokus sempit pada 'hitam' (bobot 0.926)

Kata target 'tidur' sedang diterjemahkan:
  skor relevansi e : [0.5 1.  4. ]
  bobot alpha      : [0.028 0.046 0.926]   (jumlah = 1.0)
  konteks = alpha @ H : [0.218 0.144 0.938 0.463]
  -> fokus sempit pada 'tidur' (bobot 0.926)

Kata target 'the' sedang diterjemahkan:
  skor relevansi e : [1. 1. 1.]
  bobot alpha      : [0.333 0.333 0.333]   (jumlah = 1.0)
  konteks = alpha @ H : [0.433 0.433 0.433 0.167]
  -> sebaran merata, tidak ada fokus tunggal

Mari kita telaah kode di atas baris per baris:

  1. Baris 13-15: fungsi softmax mengurangi nilai terbesar sebelum exp agar eksponen tidak meluap — pola standar yang juga kita pakai pada chapter-chapter berikutnya.
  2. Baris 21-28: tiga hidden state encoder disusun sebagai matriks H berbentuk (3, 4) — satu baris per kata sumber, persis keluaran yang disimpan encoder seq2seq.
  3. Baris 31-35: tiga kasus uji. Kata target hitam dan tidur membawa skor tinggi pada satu kata sumber; kata fungsi the membawa skor seragam karena tidak punya pasangan makna di kalimat sumber.
  4. Baris 42-44: inti attention dalam dua baris — alpha = softmax(skor) menakar bobot, konteks = alpha @ H merangkai rata-rata berbobot seluruh hidden state.
  5. Baris 46-54: pencetakan hasil plus deteksi pola: bobot terbesar di atas 0,5 dilaporkan sebagai fokus sempit, selebihnya sebagai sebaran merata.

Perhatikan dua hal pada hasilnya. Pertama, context vector berubah dari kata ke kata: [0.144; 0.938; 0.218; 0.014] saat menerjemahkan hitam, lalu [0.218; 0.144; 0.938; 0.463] saat menerjemahkan tidur — decoder menerima rangkuman yang menyesuaikan kebutuhan, bukan satu vektor tetap seperti pada chapter Sequence-to-Sequence . Kedua, konteks untuk hitam nyaris menyalin hidden state hitam [0.1; 1.0; 0.2; 0.0] karena bobot 0,926 hampir menelan seluruh takaran — softmax tetap membagi sisa 0,074 ke dua kata lain, sehingga informasi latar tetap ikut dalam porsi kecil.

Tutorial resmi PyTorch tentang terjemahan seq2seq dengan attention memuat implementasi additive attention dalam bentuk nn.Module yang bisa dilatih — kelanjutan yang tepat setelah konsep di sini dikuasai.

Dengan ini hambatan ketiga dari chapter sebelumnya terjawab: context vector tidak lagi sempit karena takarannya dinamis. Dua pertanyaan berikutnya justru lahir dari solusi ini — bagaimana menghitung skor tanpa jaringan terpisah, dan bagaimana memakai attention tanpa struktur encoder-decoder? Keduanya dijawab Transformer lewat tiga peran baru: query, key, dan value — pokok bahasan chapter berikutnya.