Komputer tidak membaca kata. Baginya “kucing” dan “anjing” sama sekali tidak lebih dekat daripada “kucing” dan “pajak” — semua huruf, semua angka. Seluruh NLP (Natural Language Processing) pada dasarnya adalah usaha menjembatani jurang itu: mengubah teks manusia jadi angka yang bisa dihitung, tanpa membuang maknanya. Episode ini catatan saya tentang tiga fondasinya: tokenisasi, stopword, dan word embedding.
Tokenisasi: Memotong Teks Jadi Unit
Tokenisasi adalah proses memecah teks jadi unit terkecil yang diproses model. Kedengarannya sepele — tinggal split by spasi? Tidak juga. Bahasa Indonesia punya tantangan sendiri: kata ulang (“anak-anak”), imbuhan (“memakan”, “dimakan”, “termakan” — satu akar kata, empat token berbeda kalau dipotong naif), dan tanda baca yang kadang bermakna.
- Word-level: tiap kata satu token. Sederhana, tapi vocabulary membengkak dan kata baru tak dikenal.
- Character-level: tiap huruf satu token. Vocabulary kecil, tapi urutan jadi sangat panjang.
- Subword: jalan tengah modern — kata langka dipecah jadi potongan yang sering muncul. Inilah yang dipakai BERT dan GPT (BPE, WordPiece).
import re
text = "Saya sedang belajar NLP, karena NLP menyenangkan!"
# tokenizer naif ala regex
tokens = re.findall(r"\w+", text.lower())
print(tokens)
# ['saya', 'sedang', 'belajar', 'nlp', 'karena', 'nlp', 'menyenangkan']
# subword sederhana: pecah kata panjang/langka
def subword(token, vocab):
return token if token in vocab else [token[i:i+3] for i in range(0, len(token), 3)]
print(subword("menyenangkan", {"saya", "belajar", "nlp"}))
# ['men', 'yne', 'nga', 'n']Stopword: Kata Kecil yang Sering Dibuang
Stopword adalah kata super-frekuens yang maknanya minim untuk banyak tugas klasik: “yang”, “dan”, “di”, “dengan”, “adalah”. Di era TF-IDF dan search sederhana, membuangnya wajar — mereka muncul di hampir semua dokumen sehingga tak membantu membedakan. Tapi catatan penting dari pengalaman: untuk analisis sentimen, stopword bisa krusial. Kalimat “tidak bagus” kehilangan seluruh maknanya jika “tidak” dibuang sebagai stopword. Aturan praktis saya: buang stopword hanya jika tugasnya butuh topik, bukan nuansa.
| Tugas | Buang stopword? | Alasan |
|---|---|---|
| Pencarian dokumen / topic modeling | Boleh | Fokus ke kata pembeda topik |
| Sentimen analysis | Hati-hati | “Tidak”, “justru”, “malah” pembawa polaritas |
| Model Transformer modern | Jangan | Model belajar bobot kata sendiri; konteks urutan penting |
Word Embedding: Kata Jadi Vektor Bermakna
Cara paling naif mengubah kata jadi angka adalah one-hot encoding: vektor sepanjang ukuran vocabulary, satu posisi bernilai 1 sisanya 0. Masalahnya fatal: vektor semua kata saling ortogonal — “kucing” dan “anjing” sama jauhnya dengan “kucing” dan “pesawat”. Tak ada notion kemiripan sama sekali.
Word embedding memecahkannya dengan vektor padat berdimensi rendah (misalnya 100–300 angka) yang dipelajari dari data: kata yang muncul dalam konteks serupa mendapat vektor yang berdekatan. Efek sampingnya indah: operasi aritmetika vektor menangkap relasi semantik, contoh terkenalnya king − man + woman ≈ queen. Word2Vec dan GloVe adalah metode klasik pembuat embedding; model modern seperti BERT membuat embedding kontekstual — vektor kata yang sama berubah sesuai kalimatnya.
import numpy as np
# ilustrasi: embedding hasil training (bukan angka asli dataset manapun)
emb = {
"kucing": np.array([0.9, 0.1]),
"anjing": np.array([0.8, 0.2]),
"pesawat": np.array([-0.5, 0.9]),
}
def cosine(a, b):
return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))
print(cosine(emb["kucing"], emb["anjing"])) # ~0.998, mirip
print(cosine(emb["kucing"], emb["pesawat"])) # ~-0.63, jauhWord2Vec: CBOW vs Skip-gram
Word2Vec hadir dalam dua arsitektur. CBOW (Continuous Bag of Words) memprediksi kata tengah dari kata-kata di sekitarnya — cocok untuk dataset besar karena lebih cepat. Skip-gram kebalikannya: dari satu kata tengah, memprediksi kata-kata konteksnya — lebih lambat tapi lebih baik pada kata langka. Analogi sederhana: CBOW seperti tebak kata dari petunjuk (kata-kata sekitar); Skip-gram seperti menyebarkan petunjuk dari satu kata. Keduanya menangkap idiom lama: “You shall know a word by the company it keeps” (J.R. Firth, 1957).
from gensim.models import Word2Vec
sentences = [
["saya", "sedang", "belajar", "nlp"],
["nlp", "adalah", "bagian", "dari", "ai"],
["word", "embedding", "mengubah", "kata", "jadi", "vektor"],
]
# CBOW (default, sg=0) vs Skip-gram (sg=1)
model_cbow = Word2Vec(sentences, vector_size=50, window=2, min_count=1, sg=0)
model_sg = Word2Vec(sentences, vector_size=50, window=2, min_count=1, sg=1)
print(model_cbow.wv["nlp"].shape) # (50,)
print(model_sg.wv["nlp"].shape) # (50,)GloVe: Global Vectors
GloVe (Global Vectors for Word Representation) dari Stanford berbeda pendekatan: bukan dari konteks lokal seperti Word2Vec, tapi dari matriks ko-occurrence global — kata mana sering muncul berdampingan di seluruh korpus. Kemudian matriks itu difaktorisasi dengan target dot product yang menangkap rasio co-occurrence. Keunggulannya: training lebih transparan secara matematis, dan sering mengungguli Word2Vec pada tes analogi kata. Cocok untuk analisis semantik statis.
FastText: Subword ke Level Karakter
FastText dari Facebook Research menambahkan trik cerdas: setiap kata direpresentasikan sebagai jumlah dari vektor subword (n-gram karakter). Contoh: “belajar” mungkin terdiri dari vektor “bel”, “ela”, “laj”, “aja”, “jar”, “ar” ditambah vektor kata utuh. Konsekuensinya, FastText bisa menghasilkan embedding untuk kata baru yang tak ada di corpus latih — selama ada overlap subword-nya. Untuk Bahasa Indonesia yang banyak kata berimbuhan, ini keunggulan signifikan. Satu “melengkapkan” yang tak dikenal masih bisa dipahami dari “lengkap”.
Visualisasi Embedding Space
Salah satu momen paling memuaskan dalam belajar NLP adalah melihat embedding space dipetakan — tiba-tiba makna abstrak jadi titik-titik di 2D yang bisa dilihat mata. PCA (Principal Component Analysis) mereduksi dimensi tinggi ke 2D dengan mempertahankan variasi terbesar; t-SNE lebih artistik, menjaga tetangga lokal lebih rapat meski global agak berantakan.
import numpy as np
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
# asumsi: model = Word2Vec yang sudah dilatih
words = list(model_sg.wv.index_to_key[:20]) # ambil 20 kata pertama
vecs = np.array([model_sg.wv[w] for w in words])
# PCA → 2D
pca = PCA(n_components=2)
coords_pca = pca.fit_transform(vecs)
plt.figure(figsize=(8, 6))
plt.scatter(coords_pca[:, 0], coords_pca[:, 1])
for i, w in enumerate(words):
plt.annotate(w, (coords_pca[i, 0], coords_pca[i, 1]))
plt.title("Embedding Space — PCA")
plt.show()Bag of Words vs TF-IDF: Representasi Klasik
Sebelum era embedding, dua metode mendominasi representasi teks: Bag of Words (BoW) dan TF-IDF. Keduanya sederhana, tapi penting dipahami karena masih jadi baseline kuat dan bahan pemahaman sebelum masuk ke embedding vektor.
Bag of Words: Hitung frekuensi tiap kata di tiap dokumen. Vektor “tas” (bag) — urutan kata diabaikan, hanya jumlah yang penting. Sederhana, cepat, tapi kehilangan nuansa urutan dan konteks.
TF-IDF (Term Frequency–Inverse Document Frequency): Sama seperti BoW, tapi setiap kata dibobot berdasarkan betapa uniknya ia di dokumen ini dibanding korpus. Kata yang muncul di semua dokumen (misal “yang”) dapat bobot rendah; kata yang hanya ada di satu dokumen dapat bobot tinggi.
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
docs = [
"kucing makan ikan di dapur",
"anjing berlari di taman",
"kucing dan anjing adalah hewan peliharaan",
]
# Bag of Words
bow = CountVectorizer()
bow_matrix = bow.fit_transform(docs)
print("BoW features:", bow.get_feature_names_out())
print(bow_matrix.toarray())
# TF-IDF
tfidf = TfidfVectorizer()
tfidf_matrix = tfidf.fit_transform(docs)
print("\nTF-IDF features:", tfidf.get_feature_names_out())
print(tfidf_matrix.toarray().round(2))Tokenizer Modern: BPE dan SentencePiece
Dua pendekatan subword modern yang wajib diketahui:
BPE (Byte Pair Encoding): Dimulai dari tokenisasi karakter, lalu secara iteratif menggabungkan pasangan karakter/token yang paling sering muncul menjadi satu token baru. Proses ini diulangi hingga vocabulary mencapai ukuran yang diinginkan. GPT-2, GPT-3, dan BERT semuanya memakai variasi BPE. Kelebihannya: vocabulary tetap terukur, dan kata apapun (termasuk typo) bisa direpresentasikan.
SentencePiece: Tool dari Google yang tidak membutuhkan preprocessing tokenisasi awal — ia bekerja langsung pada raw text (termasuk spasi sebagai karakter biasa). Sangat berguna untuk bahasa yang tidak memisahkan kata dengan spasi seperti Jepang, atau Bahasa Indonesia yang punya fenomena agglutinative (kata berimbuhan saling menempel). SentencePiece juga mendukung unigram language model sebagai alternatif BPE.
Analisis Sentimen: Pipeline Lengkap
Saat saya pertama kali menyusun pipeline analisis sentimen sendiri, ada rasa puas tersendiri melihat teks ulasan produk berubah jadi label positif/negatif secara otomatis. Berikut pipeline klasik yang saya gunakan:
import re
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
# contoh dataset kecil
reviews = [
("Film ini sangat bagus dan menghibur", 1),
("Saya kecewa dengan kualitas produk", 0),
("Pelayanan restoran ini luar biasa", 1),
("Barang rusak dan pengiriman lambat", 0),
("Ceritanya menarik, akting sempurna", 1),
("Harga terlalu mahal untuk kualitas begini", 0),
("Senang sekali dengan hasilnya", 1),
("Pengalaman terburuk dalam hidup saya", 0),
]
texts = [r[0] for r in reviews]
labels = np.array([r[1] for r in reviews])
# langkah 1: tokenisasi + lowercase
def tokenize(text):
return re.findall(r"\w+", text.lower())
# langkah 2: TF-IDF (stopword juga bisa ditangani di sini)
tfidf = TfidfVectorizer(tokenizer=tokenize, token_pattern=None)
X = tfidf.fit_transform(texts)
# langkah 3: split + train classifier
X_train, X_test, y_train, y_test = train_test_split(
X, labels, test_size=0.25, random_state=42
)
clf = MultinomialNB()
clf.fit(X_train, y_train)
# langkah 4: evaluasi
y_pred = clf.predict(X_test)
print(classification_report(y_test, y_pred, target_names=["negatif", "positif"]))
# langkah 5: prediksi baru
new_text = ["Produk ini bagus sekali"]
new_vec = tfidf.transform(new_text)
print(f"Prediksi: {'positif' if clf.predict(new_vec)[0] == 1 else 'negatif'}")Tugas NLP Lainnya: Pandangan Luas
Tiga konsep yang dibahas di atas hanyalah fondasi. Dunia NLP jauh lebih luas:
- NER (Named Entity Recognition): Mengidentifikasi entitas bernama — orang, organisasi, lokasi, waktu — dalam teks. Contoh: dari “Rudy tinggal di Jakarta sejak 2020”, NER mengekstrak {Rudy: PERSON, Jakarta: LOCATION, 2020: DATE}.
- POS Tagging (Part-of-Speech): Menandai setiap kata dengan peran gramatikalnya (kata benda, kata kerja, kata sifat, dst). Dasar untuk parsing kalimat dan analisis syntactic.
- Machine Translation: Menerjemahkan dari satu bahasa ke bahasa lain. Dari statistik (IBM Models) ke neural (Transformer, attention mechanism) — revolusi terbesar di dekade ini.
- Text Summarization: Merangkum dokumen panjang jadi ringkasan padat. Dua pendekatan: ekstraktif (ambil kalimat penting) dan abstraktif (generate kalimat baru, seperti yang dilakukan LLM).
Tabel Perbandingan Representasi Teks
| Metode | Dimensi | Menangkap Semantik? | Menangkap Urutan? | Butuh Training? |
|---|---|---|---|---|
| One-hot Encoding | |V| (besar) | Tidak | Tidak | Tidak |
| Bag of Words | |V| (besar) | Individu tidak | Tidak | Tidak |
| TF-IDF | |V| (besar) | Individu tidak, tapi weight cerdas | Tidak | Tidak |
| Word2Vec / GloVe | 100–300 | Ya (static) | Tidak | Ya |
| FastText | 100–300 | Ya + generalisasi kata baru | Tidak | Ya |
| BERT Embedding | 768–1024 | Ya (kontekstual) | Ya | Ya (pretrained) |
Tantangan Bahasa Indonesia
Banyak resource NLP dibangun untuk Bahasa Inggris. Bahasa Indonesia punya karakter unik yang membuat beberapa aspek lebih sulit: sifat agglutinative — imbuhan menempel pada kata dasar tanpa spasi, menciptakan kombinasi kata sangat banyak (“mempermasalahkannya” = satu kata, banyak makna). Tidak ada konsensus universal tentang stopword list — “yang” bisa saja bermakna dalam konteks tertentu. Corpus berlabel (untuk training) juga relatif sedikit dibandingkan Inggris. Kabar baiknya: model multilingual seperti mBERT dan XLM-R sudah cukup baik menangani Indonesia karena termasuk dalam pre-training data mereka.
Catatan Pribadi: Saat Teks Jadi Angka
Ada momen spesifik yang saya ingat saat belajar NLP ini: pertama kali saya memplot embedding hasil training dan melihat kata-kata bertema serupai berkerumun di area yang sama. “Kucing” berdekatan dengan “anjing”, “kota” berdekatan dengan “negara” — tanpa ada yang memberitahu model tentang relasi makna itu. Ia belajar sendiri dari pola ko-occurrence di korpus. Itu momen yang membuat saya benar-benar paham bahwa representasi vektor bukan sekadar trik rekayasa fitur — ia benar-benar menangkap struktur makna. Dari situ saya mulai melihat bahwa NLP bukan hanya preprocessing teks, tapi sebuah dunia di mana bahasa manusia dan matematika bertemu.
Dengan ketiga konsep ini, pipeline NLP klasik lengkap: teks ditokenisasi, opsional dibersihkan dari stopword, lalu tiap token direpresentasikan sebagai vektor untuk dimodelkan. Dan representasi vektor inilah pintu masuk menuju episode berikutnya: Transformer, arsitektur yang memakan dunia NLP.
Komentar Terbaru