Era vektor statis

Pengenalan GloVe dan fastText serta vektor praterlatih sebagai komoditas.

Daftar isi

Setelah word2vec, dua model melengkapi peta era vektor statis (static embedding), yaitu periode ketika setiap kata memakai satu vektor tetap tanpa memandang kalimat tempat kata itu muncul. Keduanya adalah GloVe yang memfaktorkan statistik ko-okurensi global dan fastText yang memecah kata menjadi n-gram karakter. Topik ini menjelaskan keduanya secara konseptual, menjalankan demo fastText pada korpus kecil untuk menunjukkan vektor bagi kata yang tidak ada di kosakata, lalu menempatkan vektor praterlatih sebagai komoditas beserta batasnya.

GloVe: faktorisasi statistik global#

GloVe (Global Vectors) membangun vektor dari statistik yang lebih luas daripada jendela per kalimat. Seluruh korpus dihitung sekali menjadi matriks ko-okurensi global XX, lalu matriks itu difaktorkan sehingga hasil perkalian titik antarvektor kata meniru logaritma frekuensi ko-okurensinya. Tujuan pelatihannya adalah mengurangi nilai berikut:

J=∑i,jf(Xij)(wi⊤w~j+bi+b~j−log⁡Xij)2 J = \sum_{i,j} f(X_{ij}) \left( w_i^\top \tilde{w}_j + b_i + \tilde{b}_j - \log X_{ij} \right)^2

di mana wiw_i adalah vektor kata pusat, w~j\tilde{w}_j vektor kata konteks, bib_i dan b~j\tilde{b}_j bias, dan f(Xij)f(X_{ij}) bobot yang menahan pengaruh pasangan yang terlalu jarang. Perbedaannya dengan word2vec terletak pada sumber sinyal: word2vec belajar dari jendela yang lewat satu per satu, sedangkan GloVe menyusun statistik global lebih dahulu lalu menyesuaikan vektor terhadapnya. Hasil akhir keduanya sama-sama vektor statis dan lazim ditukar dalam praktik.

Karena itu, demo GloVe tidak dijalankan pada topik ini dan penjelasannya berhenti pada tingkat konsep. Pola kerjanya tetap penting dikenal karena masih dipakai ketika korpus khusus sudah memiliki matriks ko-okurensi sendiri dan vektor ingin dibangun dari statistik yang ada.

fastText: kata sebagai kumpulan n-gram karakter#

fastText mengubah satuan pelatihan dari kata menjadi potongan huruf. Setiap kata diwakili gabungan n-gram karakter (character n-grams), yakni potongan huruf berurutan dengan panjang 3 hingga 6 sesuai default Gensim, misalnya potongan per, sta, dan usta pada kata perpustakaan. Vektor kata merupakan gabungan vektor semua n-gramnya, sehingga kata yang tidak pernah muncul saat pelatihan tetap memperoleh vektor selama n-gramnya pernah terlihat.

Karakteristik itu menjadikan fastText kuat pada kata jarang, perubahan morfologi, dan salah tik (typo). Kata di luar kosakata (OOV) pun tetap dapat diberi vektor tanpa penolakan. Bagian n-gram disimpan dalam tabel bucket berukuran 2 juta entri pada Gensim, dengan fungsi hash menentukan posisi setiap n-gram, sehingga jumlah n-gram yang mungkin tidak terikat pada jumlah kata.

Saat pelatihan, vektor n-gram diperbarui setiap kali n-gramnya terlihat pada kata yang sedang dilatih, sehingga potongan huruf yang dipakai kata-kata sering menjadi representasi yang mantap. Kata baru sebagian besar meminjam potongan yang sudah terlatih itu, dan itulah mekanisme yang diuji demo di bawah. Antarmuka kelas FastText di Gensim juga meniru kelas Word2Vec sepenuhnya, sehingga perpindahan antara keduanya hanya mengganti satu nama kelas.

Demo: vektor untuk kata yang salah tik#

Skrip berikut melatih fastText pada korpus yang sama persis dengan demo word2vec, sehingga perbedaan keluaran murni berasal dari perbedaan model. Ujiannya adalah kata perpusatakaan, salah tik dari perpustakaan, dan kata itu tidak pernah muncul di korpus. Sebagai pembanding, skrip juga melatih word2vec pada korpus yang sama lalu mencoba mengambil vektor kata yang sama dari model itu.

python latih_fasttext.py
import warnings
warnings.filterwarnings("ignore")
import logging
logging.getLogger("gensim").setLevel(logging.ERROR)

import numpy as np
from gensim.models import FastText
from gensim.models import Word2Vec

def buat_korpus():
    kalimat = []
    # tema perpustakaan
    for nama in ["Budi", "Citra", "Dewi"]:
        for objek in ["buku", "majalah"]:
            kalimat.append(f"{nama} membaca {objek} di perpustakaan")
            kalimat.append(f"{nama} meminjam {objek} di perpustakaan")
    kalimat.append("petugas menata buku di rak")
    kalimat.append("petugas menata majalah di rak")
    kalimat.append("Budi mencari buku di rak")
    kalimat.append("Citra mencari majalah di rak")
    # tema pasar
    for nama in ["Budi", "Citra", "Dewi"]:
        for objek in ["sayur", "buah"]:
            kalimat.append(f"{nama} membeli {objek} di pasar")
            kalimat.append(f"{nama} memilih {objek} di pasar")
    kalimat.append("Citra menghitung harga sayur")
    kalimat.append("Dewi menghitung harga buah")
    # tema jadwal belajar
    for nama in ["Budi", "Citra", "Dewi"]:
        kalimat.append(f"{nama} mencatat materi pelajaran")
        kalimat.append(f"{nama} mengerjakan latihan soal")
    kalimat.append("Citra menyalin jadwal belajar")
    kalimat.append("Dewi menempel jadwal belajar di dinding")
    kalimat.append("Budi mengisi jadwal belajar")
    return [k.split() for k in kalimat]

kalimat_token = buat_korpus()

fasttext = FastText(
    sentences=kalimat_token,
    vector_size=100,
    window=2,
    min_count=1,
    workers=1,
    seed=42,
    sg=1,
    epochs=30,
)

typo = "perpusatakaan"
vektor = fasttext.wv[typo]
print("kata uji:", typo)
print("ada di kosakata fastText:", typo in fasttext.wv.key_to_index)
print("bentuk vektor:", vektor.shape, "norm:", round(np.linalg.norm(vektor), 3))

print()
print("kata paling mirip dengan typo tersebut:")
for kata, skor in fasttext.wv.most_similar(typo, topn=3):
    print(f"  {kata}: {skor:.3f}")

word2vec = Word2Vec(
    sentences=kalimat_token,
    vector_size=100,
    window=2,
    min_count=1,
    workers=1,
    seed=42,
    sg=1,
    epochs=30,
)

print()
print("ada di kosakata word2vec:", typo in word2vec.wv.key_to_index)
try:
    word2vec.wv[typo]
except KeyError as e:
    print("word2vec menolak kata tersebut:", e)
text Keluaran
kata uji: perpusatakaan
ada di kosakata fastText: False
bentuk vektor: (100,) norm: 0.008

kata paling mirip dengan typo tersebut:
  perpustakaan: 0.572
  membeli: 0.338
  memilih: 0.248

ada di kosakata word2vec: False
word2vec menolak kata tersebut: "Key 'perpusatakaan' not present"

fastText tetap mengembalikan vektor berdimensi 100 untuk perpusatakaan meski kata itu tidak ada di kosakata, dan tiga tetangga teratasnya diawali perpustakaan dengan skor 0.572. Panjang vektornya kecil, hanya 0.008, karena vektor tersebut dirangkai dari bucket n-gram yang sebagian besar jarang dilatih pada korpus mungil, tetapi arahnya sudah cukup untuk menunjuk kata aslinya. Skor 0.572 bukan skor tinggi menurut standar korpus besar, tetapi pada korpus mungil ini skor itu sudah cukup untuk memunculkan kata yang benar di urutan pertama. word2vec menolak permintaan yang sama dengan error KeyError karena kosakatanya tertutup, yakni hanya kata yang terlihat saat pelatihan yang punya vektor. Perbedaan perilaku inilah alasan fastText dipilih ketika teks pengguna bebas, misalnya kata kunci pencarian atau tulisan tanpa koreksi.

Vektor praterlatih sebagai komoditas#

GloVe dan fastText telah dilatih pada korpus raksasa seperti Wikipedia dan Common Crawl, dan hasilnya dibagikan siap pakai di halaman proyek masing-masing. Berkas praterlatih menyingkirkan langkah pelatihan ketika kebutuhan hanya mencari kemiripan kata atau menyiapkan fitur semantik sederhana, sehingga embedding berubah status dari hasil riset menjadi komoditas yang diunduh. fastText juga menerbitkan vektor untuk ratusan bahasa, termasuk bahasa Indonesia, sehingga pengembang dengan korpus berbahasa Indonesia memiliki titik awal tanpa pelatihan.

Batas komoditas ini tetap perlu diperhitungkan. Berkas praterlatih menempati ratusan megabyte hingga beberapa gigabyte, kosakatanya hanya memuat kata yang terlihat saat pelatihan, dan pemilihan domainnya menentukan kualitas: vektor Wikipedia memperlakukan kata menurut penggunaannya di ensiklopedia, sedangkan korpus khusus seperti catatan medis menempatkan kata yang sama dalam konteks lain. Apabila domain korpus jauh dari korpus pelatihan, melatih vektor sendiri di korpus sendiri sering menaikkan kualitas daripada tetap memakai berkas umum. Keputusan ini juga menyangkut biaya: melatih sendiri menuntut korpus besar dan waktu komputasi, sedangkan berkas praterlatih menghemat keduanya dengan cakupan domain yang lebih umum.

Kesalahan umum#

  1. Mencari kelas pelatihan GloVe di Gensim dan menyimpulkan pustakanya tidak lengkap. GloVe memang tidak dilatih di Gensim. Gunakan alat resmi Stanford, lalu muat berkas hasilnya sebagai KeyedVectors.
  2. Mengasumsikan vektor praterlatih menutup seluruh kata domain sendiri. Kosakata praterlatih hanya memuat kata yang terlihat saat pelatihan, sehingga istilah khusus tetap jatuh ke OOV. Periksa cakupan kosakata terhadap korpus sebelum memakainya.
  3. Memperlakukan vektor salah tik sekuat vektor kata asli. Vektor OOV dirangkai dari bucket n-gram dan keandalannya menurun bila salah tiknya jauh dari kata nyata. Gunakan vektor OOV sebagai perkiraan, bukan kepastian.

Ringkasan#

  • GloVe menyusun statistik ko-okurensi global lalu memfaktorkannya, sedangkan word2vec belajar dari jendela yang lewat satu per satu.
  • fastText mewakili kata sebagai gabungan n-gram karakter, sehingga kata di luar kosakata tetap memperoleh vektor dari n-gramnya.
  • Pada demo, fastText mengarahkan salah tik perpusatakaan ke perpustakaan dengan skor 0.572, sedangkan word2vec menolak kata itu dengan KeyError.
  • Vektor praterlatih menghemat pelatihan, tetapi ukurannya besar, kosakatanya terbatas, dan cakupan domainnya perlu diperiksa.

Referensi#