Natural Language Processing: Panduan Machine Learning untuk Pemula #13
Model machine learning hanya bisa menghitung angka. Sementara teks berupa rangkaian huruf yang maknanya bergantung pada urutan, konteks, dan bahkan hal yang tidak tertulis.
Seluruh bidang Natural Language Processing pada dasarnya menjawab satu pertanyaan: bagaimana mengubah teks menjadi angka tanpa kehilangan maknanya?
Artikel ini mengikuti perjalanan jawaban atas pertanyaan itu, dari cara paling sederhana yang masih sangat berguna sampai teknologi di balik ChatGPT. Semua contoh memakai bahasa Indonesia, karena penanganan bahasa Indonesia punya tantangan tersendiri.
Daftar Isi
- Kenapa Teks Berbeda dari Data Tabel
- Istilah Dasar yang Perlu Dipahami
- Menyiapkan Data Contoh
- Membersihkan Teks
- Tokenisasi
- Bag of Words
- TF-IDF
- Word Embedding
- Transformer
- Ekosistem Hugging Face
- Memilih Pendekatan yang Tepat
- Kesalahan Pemula yang Sering Terjadi
Kenapa Teks Berbeda dari Data Tabel
Masalah Utamanya
Pada data tabel, kolom umur berisi angka 35 yang bisa langsung dihitung. Pada teks, kalimat "barangnya bagus banget" tidak punya angka sama sekali.
Anda harus mengubahnya dulu. Dan cara mengubahnya menentukan seberapa banyak makna yang bertahan.
Empat Tantangan Khusus Bahasa
Urutan itu penting. "Anjing menggigit orang" dan "orang menggigit anjing" memakai kata yang sama persis, tapi maknanya berbeda jauh.
Satu kata bisa banyak makna. Kata "bisa" pada "saya bisa berenang" dan "bisa ular" adalah dua hal berbeda.
Panjangnya berbeda-beda. Satu ulasan berisi 5 kata, ulasan lain 500 kata. Model butuh masukan berukuran tetap.
Bahasa Indonesia punya imbuhan yang rumit. Kata "makan", "memakan", "dimakan", "termakan", dan "makanan" berasal dari akar yang sama, tapi bagi komputer semuanya kata berbeda.
Peta Perjalanan
Artikel ini bergerak dari yang paling sederhana ke yang paling modern.
| Pendekatan | Idenya | Tahun populer |
|---|---|---|
| Bag of Words | Hitung kemunculan kata | 1990-an |
| TF-IDF | Beri bobot berdasarkan kelangkaan kata | 1990-an sampai sekarang |
| Word Embedding | Kata menjadi vektor bermakna | 2013 |
| Transformer | Vektor yang bergantung konteks kalimat | 2018 sampai sekarang |
Yang menarik, pendekatan lama tidak menjadi usang. TF-IDF masih menjadi pilihan terbaik untuk banyak kasus nyata karena cepat, murah, dan hasilnya bisa dijelaskan.
Istilah Dasar yang Perlu Dipahami
Korpus adalah kumpulan seluruh teks yang Anda punya. Kalau Anda punya 10.000 ulasan produk, itulah korpus Anda.
Dokumen adalah satu satuan teks. Bisa satu ulasan, satu artikel, atau satu pesan.
Token adalah potongan terkecil hasil pemotongan teks. Biasanya satu kata, tapi bisa juga potongan kata.
Vocabulary adalah daftar semua token unik yang ada di korpus Anda.
Vektorisasi adalah proses mengubah teks menjadi deretan angka.
Corpus, dokumen, token sering tertukar. Ingat urutannya: korpus berisi banyak dokumen, dokumen berisi banyak token.
Menyiapkan Data Contoh
Kita akan membuat kumpulan ulasan produk berbahasa Indonesia beserta label sentimennya.
import numpy as np
import pandas as pd
np.random.seed(42)
positif_awal = ["Barangnya bagus", "Produk mantap", "Kualitas oke",
"Sangat memuaskan", "Pengiriman cepat", "Recommended banget"]
positif_akhir = ["sesuai deskripsi", "harga terjangkau", "packing rapi",
"penjual ramah", "bakal beli lagi", "worth it"]
negatif_awal = ["Barangnya jelek", "Produk mengecewakan", "Kualitas buruk",
"Sangat kecewa", "Pengiriman lama", "Tidak recommended"]
negatif_akhir = ["tidak sesuai deskripsi", "harga kemahalan", "packing rusak",
"penjual tidak responsif", "kapok beli di sini", "buang buang uang"]
ulasan, label = [], []
for _ in range(400):
a, b = np.random.choice(positif_awal), np.random.choice(positif_akhir)
ulasan.append(f"{a}, {b}."); label.append(1)
a, b = np.random.choice(negatif_awal), np.random.choice(negatif_akhir)
ulasan.append(f"{a}, {b}."); label.append(0)
df = pd.DataFrame({"teks": ulasan, "sentimen": label})
df = df.sample(frac=1, random_state=42).reset_index(drop=True)
print(df.head(8))
print("\nJumlah data:", len(df))
print("Proporsi positif:", df["sentimen"].mean())
Membersihkan Teks
Kenapa Perlu Dibersihkan
Bagi komputer, "Bagus", "bagus", dan "bagus!" adalah tiga hal berbeda. Pembersihan menyeragamkan hal-hal semacam itu supaya sinyalnya tidak terpecah.
Menyeragamkan Huruf Besar Kecil
teks = "Barangnya BAGUS Banget!"
print(teks.lower())
Ini langkah paling dasar dan hampir selalu dilakukan. Pengecualiannya adalah kalau huruf kapital membawa makna, misalnya untuk mendeteksi nama orang atau tempat.
Membersihkan Karakter Pengganggu
import re
def bersihkan(teks):
teks = teks.lower()
teks = re.sub(r"http\S+|www\.\S+", " ", teks) # buang tautan
teks = re.sub(r"@\w+", " ", teks) # buang mention
teks = re.sub(r"[^a-z\s]", " ", teks) # sisakan huruf saja
teks = re.sub(r"\s+", " ", teks).strip() # rapikan spasi
return teks
contoh = "Barangnya BAGUS banget!!! Cek di http://toko.com @adminstore 100% puas"
print("Sebelum:", contoh)
print("Sesudah:", bersihkan(contoh))
Hati-hati Membuang Angka
Pada contoh di atas angka ikut terbuang. Untuk analisis sentimen itu biasanya tidak masalah. Tapi kalau Anda menganalisis ulasan yang menyebut "garansi 2 tahun" atau "diskon 50%", angka itu justru penting.
Selalu sesuaikan pembersihan dengan tujuannya, jangan asal menyalin fungsi dari internet.
Stopwords
Stopwords adalah kata yang sangat sering muncul tapi biasanya tidak membawa makna khusus. Dalam bahasa Indonesia contohnya "yang", "di", "dan", "ini", "itu".
stopwords_id = {"yang", "di", "ke", "dari", "dan", "atau", "ini", "itu",
"adalah", "untuk", "dengan", "pada", "ada", "juga", "saya",
"aku", "kamu", "dia", "kami", "mereka", "akan", "sudah"}
def buang_stopwords(teks, daftar):
return " ".join([k for k in teks.split() if k not in daftar])
kalimat = "produk ini adalah yang paling bagus di antara semua"
print("Sebelum:", kalimat)
print("Sesudah:", buang_stopwords(kalimat, stopwords_id))
Kapan Stopwords Jangan Dibuang
Ini penting dan sering diabaikan.
Untuk analisis sentimen, kata "tidak" sangat menentukan. Kalau ikut terbuang, "tidak bagus" berubah menjadi "bagus" dan maknanya terbalik.
stopwords_berbahaya = {"tidak", "bukan", "jangan", "kurang", "belum", "tanpa"}
kalimat = "produk ini tidak bagus dan tidak sesuai"
print("Kalau 'tidak' dibuang:", buang_stopwords(kalimat, {"tidak", "ini", "dan"}))
Aturan praktisnya: untuk analisis sentimen, pertahankan kata negasi. Untuk pengelompokan topik, membuang stopwords biasanya aman.
Bahkan lebih jauh lagi, dengan model modern seperti transformer, membuang stopwords justru merugikan karena modelnya memang dirancang memahami struktur kalimat lengkap.
Stemming untuk Bahasa Indonesia
Stemming memotong imbuhan untuk mendapatkan kata dasar. Ini penting untuk bahasa Indonesia yang imbuhannya kaya.
# pip install Sastrawi
from Sastrawi.Stemmer.StemmerFactory import StemmerFactory
stemmer = StemmerFactory().create_stemmer()
contoh_kata = ["memakan", "dimakan", "makanan", "termakan",
"mengecewakan", "kekecewaan", "pengiriman", "dikirimkan"]
for kata in contoh_kata:
print(f"{kata:15s} -> {stemmer.stem(kata)}")
Sastrawi adalah pustaka stemming khusus bahasa Indonesia. Tanpa ini, "mengecewakan" dan "kekecewaan" dianggap dua kata yang sama sekali tidak berhubungan.
Kapan Stemming Membantu
Membantu kalau kosakata Anda besar dan data latih terbatas, karena menggabungkan variasi kata mengurangi jumlah kolom fitur.
Kurang membantu, bahkan merugikan, kalau memakai model transformer. Model itu sudah dilatih memahami bentuk kata utuh.
Peringatan: Jangan Berlebihan Membersihkan
Ini kesalahan umum pemula. Pembersihan yang terlalu agresif membuang informasi.
Tanda seru berulang bisa menandakan emosi kuat. Emoji sering membawa sentimen jelas. Huruf kapital semua bisa berarti berteriak.
Aturan praktisnya: bersihkan seperlunya, lalu uji apakah pembersihan itu benar-benar menaikkan skor. Kalau tidak, jangan lakukan.
Tokenisasi
Apa Itu Tokenisasi
Tokenisasi adalah memotong teks menjadi bagian-bagian kecil.
kalimat = "barangnya bagus dan pengirimannya cepat"
# Cara paling sederhana: pisah berdasarkan spasi
print(kalimat.split())
Masalah dengan Pemisahan Berdasarkan Spasi
Cara ini terlihat mudah, tapi punya beberapa masalah.
Kata yang tidak dikenal. Kalau ada kata yang tidak pernah muncul di data latih, model tidak tahu harus berbuat apa. Bahasa gaul, salah ketik, dan istilah baru selalu bermunculan.
Kosakata membengkak. "makan", "makanan", "memakan", "dimakan" jadi empat entri terpisah.
Bahasa Indonesia informal. Di media sosial ada "gk", "ga", "nggak", "engga", "kaga" yang semuanya berarti "tidak".
Tokenisasi Subword
Model modern memakai pendekatan berbeda: memotong kata menjadi potongan yang lebih kecil.
Contohnya, kata "pengiriman" bisa dipotong menjadi ["peng", "##irim", "##an"].
Kenapa Ini Lebih Baik
Tidak ada kata yang benar-benar asing. Kata baru apa pun bisa disusun dari potongan yang sudah dikenal.
Kosakata tetap kecil. Biasanya sekitar 30.000 potongan sudah cukup untuk menangani bahasa apa pun.
Hubungan antar kata terjaga. "pengiriman" dan "dikirimkan" berbagi potongan yang sama.
# pip install transformers
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("indobenchmark/indobert-base-p1")
kalimat = "pengirimannya sangat mengecewakan"
token = tokenizer.tokenize(kalimat)
print("Token:", token)
print("ID angka:", tokenizer.encode(kalimat))
Perhatikan tanda ## pada beberapa token. Itu menandakan potongan tersebut adalah lanjutan dari kata sebelumnya, bukan kata baru.
Kode ini butuh koneksi internet saat pertama dijalankan, karena modelnya diunduh dulu. Setelah itu tersimpan di komputer Anda.
Bag of Words
Idenya
Bayangkan tiap dokumen dimasukkan ke dalam kantong, lalu semua katanya dituang keluar dan dihitung. Urutannya hilang, yang tersisa hanya berapa kali tiap kata muncul.
Itulah kenapa namanya "kantong kata".
Contoh Kecil
from sklearn.feature_extraction.text import CountVectorizer
dokumen = [
"barangnya bagus pengiriman cepat",
"barangnya jelek pengiriman lama",
"bagus bagus sangat bagus",
]
cv = CountVectorizer()
matriks = cv.fit_transform(dokumen)
hasil = pd.DataFrame(matriks.toarray(), columns=cv.get_feature_names_out())
print(hasil)
Tiap baris adalah satu dokumen, tiap kolom adalah satu kata, dan isinya berapa kali kata itu muncul.
Sekarang teks sudah berubah menjadi angka dan bisa dimasukkan ke model machine learning apa pun.
Kelemahannya
Urutan hilang sepenuhnya. "tidak bagus" dan "bagus tidak" menghasilkan angka yang sama persis.
Dimensi sangat besar. Kalau korpus Anda punya 50.000 kata unik, matriksnya punya 50.000 kolom.
Matriks hampir seluruhnya nol. Satu ulasan pendek hanya memakai belasan kata dari 50.000 yang ada. Sisanya nol semua.
Tidak tahu makna. Bagi model, "bagus" dan "mantap" adalah dua kolom yang tidak berhubungan sama sekali.
N-gram: Menyelamatkan Sebagian Urutan
Solusi sebagian untuk masalah urutan adalah menghitung pasangan kata, bukan hanya kata tunggal.
cv2 = CountVectorizer(ngram_range=(1, 2)) # kata tunggal dan pasangan
matriks2 = cv2.fit_transform(["produk ini tidak bagus"])
print(cv2.get_feature_names_out())
Sekarang "tidak bagus" menjadi satu fitur tersendiri, sehingga maknanya bisa dibedakan dari "bagus" saja.
ngram_range=(1, 2) berarti ambil kata tunggal dan pasangan dua kata. Ini pengaturan yang sangat umum dipakai.
Kelemahannya, jumlah fitur meledak. Batasi dengan min_df yang membuang n-gram yang terlalu jarang muncul.
TF-IDF
Masalah yang Diselesaikan
Pada Bag of Words, kata "yang" yang muncul di semua dokumen mendapat angka tinggi, padahal tidak membedakan apa pun.
Sebaliknya, kata "mengecewakan" yang hanya muncul di beberapa dokumen justru sangat informatif, tapi angkanya kecil.
TF-IDF memperbaiki ketimpangan ini.
Cara Kerjanya
Nama TF-IDF berasal dari dua bagian.
TF (Term Frequency) menghitung seberapa sering kata muncul dalam satu dokumen. Makin sering, makin penting untuk dokumen itu.
IDF (Inverse Document Frequency) menghitung seberapa langka kata itu di seluruh korpus. Kata yang muncul di semua dokumen mendapat nilai rendah, kata yang langka mendapat nilai tinggi.
Keduanya dikalikan. Hasilnya: kata yang sering muncul di satu dokumen tapi jarang di dokumen lain mendapat bobot tertinggi. Kata seperti itulah yang paling membedakan.
Melihat Perbedaannya
from sklearn.feature_extraction.text import TfidfVectorizer
dokumen = [
"produk ini bagus sekali",
"produk ini jelas mengecewakan",
"produk ini biasa saja",
]
tfidf = TfidfVectorizer()
hasil = pd.DataFrame(tfidf.fit_transform(dokumen).toarray(),
columns=tfidf.get_feature_names_out()).round(3)
print(hasil)
Perhatikan kolom "produk" dan "ini". Karena muncul di semua dokumen, bobotnya rendah. Kata seperti "mengecewakan" yang hanya muncul sekali mendapat bobot jauh lebih tinggi.
TF-IDF melakukan penyaringan stopwords secara otomatis, tanpa perlu daftar manual.
Membuat Pengklasifikasi Teks Lengkap
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
X_train, X_test, y_train, y_test = train_test_split(
df["teks"], df["sentimen"], test_size=0.25,
stratify=df["sentimen"], random_state=42
)
model = Pipeline([
("tfidf", TfidfVectorizer(
lowercase=True,
ngram_range=(1, 2), # kata tunggal dan pasangan
min_df=2, # buang kata yang muncul di bawah 2 dokumen
max_df=0.9, # buang kata yang muncul di atas 90% dokumen
sublinear_tf=True, # redam efek pengulangan berlebihan
)),
("model", LogisticRegression(max_iter=2000, class_weight="balanced")),
])
model.fit(X_train, y_train)
print(classification_report(y_test, model.predict(X_test), digits=3))
Argumen yang Perlu Diketahui
min_df=2 membuang kata yang terlalu jarang. Kata yang cuma muncul sekali biasanya salah ketik atau tidak berguna.
max_df=0.9 membuang kata yang muncul di hampir semua dokumen, karena tidak membedakan apa pun.
sublinear_tf=True membuat kata yang muncul 10 kali tidak dianggap 10 kali lebih penting daripada yang muncul sekali. Biasanya menaikkan performa.
Melihat Kata Paling Berpengaruh
Ini keunggulan besar TF-IDF: hasilnya bisa dijelaskan.
nama_fitur = model.named_steps["tfidf"].get_feature_names_out()
bobot = model.named_steps["model"].coef_[0]
skor = pd.Series(bobot, index=nama_fitur).sort_values()
print("Paling menandakan NEGATIF:")
print(skor.head(10).round(3))
print("\nPaling menandakan POSITIF:")
print(skor.tail(10).round(3))
Anda bisa langsung menunjukkan daftar ini ke atasan atau klien. Model transformer tidak bisa memberi penjelasan sesederhana ini.
Mencoba pada Teks Baru
teks_baru = [
"barangnya bagus sekali, pengiriman cepat",
"sangat kecewa, produk rusak dan penjual tidak responsif",
"lumayan lah, sesuai harga",
]
for t in teks_baru:
prob = model.predict_proba([t])[0][1]
label = "POSITIF" if prob > 0.5 else "NEGATIF"
print(f"{label} ({prob:.1%}) : {t}")
Kapan TF-IDF Masih Pilihan Terbaik
Jangan mengira TF-IDF sudah usang. Ia masih pilihan terbaik kalau:
- Data latih Anda sedikit, di bawah beberapa ribu dokumen
- Anda butuh hasil yang bisa dijelaskan
- Kecepatan penting, karena TF-IDF ribuan kali lebih cepat daripada transformer
- Anda tidak punya GPU
- Masalahnya sederhana, misalnya membedakan kategori yang kosakatanya jelas berbeda Selalu jadikan TF-IDF sebagai baseline sebelum mencoba yang lebih rumit.
Word Embedding
Masalah yang Diselesaikan
TF-IDF tidak tahu bahwa "bagus" dan "mantap" punya makna mirip. Bagi TF-IDF, keduanya sekadar dua kolom berbeda yang tidak berhubungan.
Kalau model dilatih dengan ulasan yang memakai kata "bagus", lalu bertemu ulasan yang memakai "mantap", model tidak bisa memindahkan pengetahuannya.
Idenya
Ubah tiap kata menjadi vektor berisi beberapa ratus angka, di mana kata dengan makna mirip punya vektor yang berdekatan.
Ini mengubah kata dari sekadar label menjadi posisi dalam sebuah ruang makna.
Perbedaan dengan Bag of Words
Bag of Words menghasilkan vektor panjang dan hampir seluruhnya nol. Kata "bagus" mungkin jadi [0, 0, ..., 1, ..., 0] dengan 50.000 angka.
Embedding menghasilkan vektor pendek dan padat. Kata "bagus" mungkin jadi [0.23, -0.51, 0.88, ...] dengan 300 angka, dan tiap angka membawa sedikit informasi tentang maknanya.
Sifat yang Menakjubkan
Embedding menangkap hubungan antar kata sebagai operasi matematika.
vektor("raja") - vektor("pria") + vektor("wanita") ≈ vektor("ratu")
Ini bukan diprogram. Sifat itu muncul sendiri dari cara model dilatih.
Bagaimana Model Belajar Makna
Idenya berasal dari satu prinsip: kata dikenali dari teman-temannya.
Kalau kata "mantap" dan "bagus" sering muncul di lingkungan kata yang mirip ("barangnya ___ sekali", "kualitasnya ___"), model menyimpulkan keduanya bermakna serupa.
Model dilatih dengan tugas sederhana: tebak kata yang hilang dari kalimat. Untuk bisa menebak dengan baik, model terpaksa mempelajari makna kata.
Melatih Embedding Sendiri
# pip install gensim
from gensim.models import Word2Vec
kalimat_token = [bersihkan(t).split() for t in df["teks"]]
w2v = Word2Vec(
sentences=kalimat_token,
vector_size=100, # panjang vektor tiap kata
window=5, # berapa kata di kiri dan kanan yang dilihat
min_count=2, # abaikan kata yang muncul kurang dari 2 kali
workers=4,
seed=42,
)
print("Ukuran kosakata:", len(w2v.wv))
print("\nKata mirip dengan 'bagus':")
for kata, skor in w2v.wv.most_similar("bagus", topn=5):
print(f" {kata:15s} {skor:.4f}")
Catatan Penting
Hasil di atas mungkin terlihat aneh karena data contoh kita terlalu sedikit. Word2Vec butuh jutaan kata untuk menghasilkan embedding yang bagus.
Untuk pemakaian nyata, hampir selalu lebih baik memakai embedding yang sudah dilatih orang lain dengan korpus besar, daripada melatih sendiri dari data kecil.
Memakai Embedding untuk Klasifikasi
Cara paling sederhana: rata-ratakan vektor semua kata dalam satu dokumen.
def vektor_dokumen(teks, model):
kata = [k for k in bersihkan(teks).split() if k in model.wv]
if not kata:
return np.zeros(model.vector_size)
return np.mean([model.wv[k] for k in kata], axis=0)
X_vektor = np.array([vektor_dokumen(t, w2v) for t in df["teks"]])
print("Bentuk matriks:", X_vektor.shape)
Sekarang tiap dokumen menjadi satu vektor berisi 100 angka, yang bisa dimasukkan ke model apa pun.
Kelemahan Word2Vec
Satu kata hanya punya satu vektor. Kata "bisa" pada "bisa ular" dan "saya bisa" mendapat vektor yang sama persis, padahal maknanya berbeda.
Urutan tetap hilang kalau vektornya sekadar dirata-ratakan.
Kata baru tetap masalah. Kalau kata tidak ada di kosakata, tidak ada vektornya. FastText memperbaiki ini dengan memakai potongan huruf.
Kelemahan pertama itulah yang diselesaikan transformer.
Transformer
Masalah yang Diselesaikan
Perhatikan dua kalimat ini:
- "Bank ini memberikan bunga tinggi"
- "Kami duduk di bank taman" Kata "bank" sama, maknanya berbeda. Word2Vec memberi vektor yang sama untuk keduanya.
Transformer memberi vektor yang berbeda, karena vektornya dihitung dengan melihat seluruh kalimat.
Inilah yang disebut embedding kontekstual, dan ini lompatan besar dalam NLP.
Ide Attention
Mekanisme inti transformer disebut attention, atau perhatian.
Analogi
Bayangkan Anda membaca kalimat: "Andi memberikan buku itu kepada Budi karena dia sudah selesai membacanya."
Untuk memahami kata "dia", otak Anda otomatis menoleh kembali ke kata "Andi". Anda memberi perhatian lebih pada kata itu daripada kata lain.
Attention melakukan hal yang sama. Saat memproses tiap kata, model menghitung seberapa besar perhatian yang harus diberikan pada setiap kata lain dalam kalimat.
Hasilnya, makna tiap kata dihitung dengan mempertimbangkan seluruh konteksnya.
Pre-training dan Fine-tuning
Ini konsep yang membuat transformer sangat praktis.
Pre-training adalah tahap model dilatih dengan teks dalam jumlah sangat besar, misalnya seluruh Wikipedia. Tugasnya sederhana: menebak kata yang disembunyikan. Tahap ini butuh ribuan jam GPU dan sudah dilakukan oleh perusahaan besar.
Fine-tuning adalah tahap Anda mengambil model hasil pre-training itu, lalu melatihnya sedikit lagi dengan data Anda sendiri untuk tugas spesifik. Tahap ini bisa selesai dalam hitungan menit.
Analogi
Pre-training seperti pendidikan umum: model belajar bahasa Indonesia secara luas.
Fine-tuning seperti pelatihan kerja: model diajari tugas khusus, misalnya menilai sentimen ulasan produk.
Anda tidak perlu mengulang pendidikan umumnya. Cukup ambil yang sudah lulus, lalu latih untuk pekerjaan Anda.
Model untuk Bahasa Indonesia
Model berbahasa Inggris tidak bekerja baik untuk teks Indonesia. Untungnya sudah ada beberapa model khusus.
| Model | Kegunaan |
|---|---|
indobenchmark/indobert-base-p1 | Umum, pilihan pertama untuk bahasa Indonesia |
indolem/indobert-base-uncased | Alternatif, dilatih dengan korpus berbeda |
cahya/bert-base-indonesian-522M | Alternatif lain |
firqaaa/indo-sentence-bert-base | Khusus untuk membandingkan kemiripan kalimat |
Ekosistem Hugging Face
Apa Itu Hugging Face
Hugging Face adalah tempat komunitas berbagi model NLP siap pakai, beserta pustaka Python untuk menggunakannya.
Tiga bagian yang perlu diketahui:
Model Hub adalah kumpulan lebih dari ratusan ribu model yang bisa diunduh gratis.
Pustaka transformers adalah kode Python untuk memakai model-model itu.
Pustaka datasets adalah kumpulan dataset siap pakai.
Pemasangan
pip install transformers torch
Model akan diunduh otomatis saat pertama kali dipakai, jadi butuh koneksi internet di awal.
Cara Tercepat: Pipeline
pipeline adalah fungsi yang menangani semua langkah sekaligus, dari tokenisasi sampai keluaran akhir.
from transformers import pipeline
# Analisis sentimen dengan model yang sudah jadi
penilai = pipeline("sentiment-analysis",
model="w11wo/indonesian-roberta-base-sentiment-classifier")
teks = [
"barangnya bagus banget, pengiriman cepat",
"sangat kecewa, barang rusak dan penjual tidak responsif",
"biasa saja sih, sesuai harga",
]
for hasil, t in zip(penilai(teks), teks):
print(f"{hasil['label']:8s} ({hasil['score']:.2%}) : {t}")
Tiga baris kode, dan Anda sudah punya pengklasifikasi sentimen yang bagus tanpa melatih apa pun.
Tugas Lain yang Tersedia
# Mengisi kata yang hilang
pengisi = pipeline("fill-mask", model="indobenchmark/indobert-base-p1")
for h in pengisi("Ibu pergi ke [MASK] untuk membeli sayur.")[:3]:
print(f"{h['token_str']:15s} {h['score']:.4f}")
# Menjawab pertanyaan dari sebuah teks
# penjawab = pipeline("question-answering", model="...")
# Meringkas teks
# peringkas = pipeline("summarization", model="...")
# Menerjemahkan
# penerjemah = pipeline("translation", model="...")
Memakai Model untuk Membuat Embedding Kalimat
Ini pemakaian yang sangat berguna: mengubah kalimat menjadi vektor yang menangkap maknanya.
from transformers import AutoTokenizer, AutoModel
import torch
nama_model = "indobenchmark/indobert-base-p1"
tokenizer = AutoTokenizer.from_pretrained(nama_model)
model_bert = AutoModel.from_pretrained(nama_model)
def buat_embedding(daftar_teks):
masukan = tokenizer(daftar_teks, padding=True, truncation=True,
max_length=128, return_tensors="pt")
with torch.no_grad():
keluaran = model_bert(**masukan)
# Rata-ratakan vektor semua token, dengan memperhitungkan padding
topeng = masukan["attention_mask"].unsqueeze(-1).float()
jumlah = (keluaran.last_hidden_state * topeng).sum(1)
return (jumlah / topeng.sum(1)).numpy()
kalimat = [
"barangnya bagus sekali",
"produknya mantap banget", # makna mirip kalimat pertama
"pengiriman sangat lambat", # makna berbeda
]
vektor = buat_embedding(kalimat)
print("Bentuk:", vektor.shape)
from sklearn.metrics.pairwise import cosine_similarity
kemiripan = cosine_similarity(vektor)
print("\nKemiripan antar kalimat:")
print(pd.DataFrame(kemiripan.round(3),
index=["bagus", "mantap", "lambat"],
columns=["bagus", "mantap", "lambat"]))
Perhatikan bahwa kalimat pertama dan kedua punya kemiripan lebih tinggi meski tidak berbagi satu kata pun. Inilah yang tidak bisa dilakukan TF-IDF.
Memakai Embedding untuk Klasifikasi
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
# Ambil sebagian data agar cepat
sampel = df.sample(300, random_state=42)
X_emb = buat_embedding(sampel["teks"].tolist())
y_emb = sampel["sentimen"].values
Xe_tr, Xe_te, ye_tr, ye_te = train_test_split(
X_emb, y_emb, test_size=0.25, stratify=y_emb, random_state=42)
clf = LogisticRegression(max_iter=2000).fit(Xe_tr, ye_tr)
print(classification_report(ye_te, clf.predict(Xe_te), digits=3))
Pendekatan ini sering disebut feature extraction. Model transformer dipakai hanya untuk membuat fitur, lalu model sederhana yang melakukan klasifikasi.
Ini kompromi yang baik: lebih akurat daripada TF-IDF, tapi jauh lebih murah daripada fine-tuning penuh.
Fine-tuning
Kalau Anda punya cukup data dan butuh akurasi maksimal, latih ulang seluruh modelnya.
from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainer
from datasets import Dataset
data_hf = Dataset.from_pandas(df.rename(columns={"sentimen": "labels"}))
data_hf = data_hf.map(
lambda x: tokenizer(x["teks"], truncation=True, padding="max_length",
max_length=64),
batched=True,
)
data_split = data_hf.train_test_split(test_size=0.2, seed=42)
model_klasifikasi = AutoModelForSequenceClassification.from_pretrained(
nama_model, num_labels=2)
pengaturan = TrainingArguments(
output_dir="./hasil",
num_train_epochs=3,
per_device_train_batch_size=16,
learning_rate=2e-5, # jauh lebih kecil dari biasanya
eval_strategy="epoch",
logging_steps=50,
)
pelatih = Trainer(
model=model_klasifikasi,
args=pengaturan,
train_dataset=data_split["train"],
eval_dataset=data_split["test"],
)
# pelatih.train() # butuh waktu, sebaiknya dijalankan dengan GPU
Catatan Penting untuk Pemula
learning_rate=2e-5 jauh lebih kecil daripada yang biasa dipakai untuk model lain. Ini disengaja: model sudah pintar, Anda hanya ingin menyesuaikannya sedikit, bukan mengubahnya total.
Fine-tuning butuh GPU untuk data berukuran nyata. Tanpa GPU, bisa memakan waktu berjam-jam. Google Colab menyediakan GPU gratis untuk latihan.
Mencari Model di Hub
Kunjungi huggingface.co/models, lalu saring berdasarkan bahasa Indonesia dan jenis tugas.
Sebelum memakai sebuah model, periksa tiga hal: berapa banyak orang mengunduhnya, apakah ada penjelasan cara pakainya, dan apa lisensinya.
Memilih Pendekatan yang Tepat
Tabel Perbandingan
| Aspek | TF-IDF | Word Embedding | Transformer |
|---|---|---|---|
| Kecepatan | Sangat cepat | Cepat | Lambat |
| Butuh GPU | Tidak | Tidak | Sebaiknya ya |
| Data minimal | Ratusan | Ribuan | Ratusan (dengan fine-tuning) |
| Paham konteks | Tidak | Sebagian | Ya |
| Paham sinonim | Tidak | Ya | Ya |
| Bisa dijelaskan | Sangat bisa | Sulit | Sangat sulit |
| Ukuran model | Kecil | Sedang | Besar (ratusan MB) |
| Biaya operasional | Sangat murah | Murah | Mahal |
Urutan yang Disarankan
Langkah 1. Mulai dari TF-IDF dengan regresi logistik. Cepat, dan sering sudah cukup baik.
Langkah 2. Kalau kurang memuaskan, coba pipeline Hugging Face dengan model siap pakai. Tidak perlu melatih apa pun.
Langkah 3. Kalau masih kurang, ekstrak embedding dari transformer lalu latih model sederhana di atasnya.
Langkah 4. Kalau butuh akurasi maksimal dan punya cukup data serta GPU, lakukan fine-tuning.
Jangan langsung melompat ke langkah 4. Banyak masalah nyata sudah selesai di langkah 1.
Pertimbangan Praktis
Model transformer berukuran ratusan megabita dan butuh sumber daya besar. Kalau aplikasi Anda harus merespons dalam milidetik atau berjalan di perangkat kecil, TF-IDF mungkin satu-satunya pilihan yang masuk akal.
Selalu pertimbangkan biaya operasional, bukan hanya skor.
Kesalahan Pemula yang Sering Terjadi
Membuang Kata Negasi pada Analisis Sentimen
Membuang "tidak" mengubah "tidak bagus" menjadi "bagus". Selalu periksa daftar stopwords sebelum memakainya.
Membersihkan Teks Terlalu Agresif
Tanda seru, emoji, dan huruf kapital bisa membawa informasi sentimen. Uji dulu apakah pembersihan benar-benar membantu.
Melakukan Vektorisasi Sebelum Membagi Data
Ini bentuk kebocoran data. Kalau TfidfVectorizer di-fit pada seluruh data, informasi dari data uji ikut masuk. Selalu bungkus dalam Pipeline.
Memakai Model Berbahasa Inggris untuk Teks Indonesia
Hasilnya akan buruk. Cari model yang memang dilatih dengan bahasa Indonesia.
Melatih Word2Vec dari Data Kecil
Embedding butuh jutaan kata untuk menghasilkan makna yang berguna. Untuk data kecil, pakai model yang sudah dilatih orang lain.
Langsung Memakai Transformer Tanpa Baseline
Anda tidak akan tahu apakah kerumitannya sepadan. Buat baseline TF-IDF dulu, selalu.
Melakukan Stemming Sebelum Memakai Transformer
Model transformer sudah dilatih memahami kata utuh beserta imbuhannya. Stemming justru merusak masukannya.
Mengabaikan Panjang Maksimal Token
Model BERT biasanya hanya menerima 512 token. Teks yang lebih panjang akan terpotong diam-diam. Kalau dokumen Anda panjang, potong menjadi beberapa bagian atau pakai model khusus dokumen panjang.
Fine-tuning dengan Learning Rate Terlalu Besar
Learning rate biasa seperti 0,001 akan merusak pengetahuan yang sudah dimiliki model. Untuk fine-tuning, pakai kisaran 2e-5 sampai 5e-5.
Menganggap Skor Tinggi Berarti Model Paham Bahasa
Model bisa mencapai akurasi tinggi hanya dengan menghafal beberapa kata kunci. Selalu uji dengan kalimat yang bentuknya berbeda dari data latih.
Penutup
Perjalanan NLP dari Bag of Words sampai transformer adalah cerita tentang memperbaiki satu masalah pada satu waktu. Bag of Words kehilangan urutan, embedding menambahkan makna, transformer menambahkan konteks.
Tiga hal untuk diingat:
Pertama, TF-IDF belum usang. Untuk banyak masalah nyata, ia masih pilihan terbaik karena cepat, murah, dan bisa dijelaskan. Jadikan selalu sebagai baseline.
Kedua, jangan melatih model dari nol. Ekosistem Hugging Face menyediakan model yang sudah dilatih dengan sumber daya yang tidak mungkin Anda tandingi. Ambil, lalu sesuaikan seperlunya.
Ketiga, untuk bahasa Indonesia, pilih alat yang memang dirancang untuknya. Sastrawi untuk stemming, IndoBERT untuk transformer. Memakai alat berbahasa Inggris pada teks Indonesia adalah penyebab kegagalan yang paling mudah dihindari.