Sessi 4 — Representasi BoW, n‑gram, dan TF–IDF
Tujuan: memahami model ruang vektor (BoW), memperkaya konteks dengan n‑gram, dan membobotkan fitur dengan TF–IDF; pratinjau reduksi dimensi (SVD/PCA) untuk mengurangi sparsity dan meningkatkan efisiensi.
Learning Outcomes: (1) Menerapkan BoW/TF–IDF dengan
scikit-learn; (2) Mengonfigurasi ngram_range, min_df/max_df, sublinear_tf, norm; (3) Mengevaluasi dampak representasi pada retrieval & visualisasi; (4) Pratinjau Latent Semantic Analysis via TruncatedSVD.1) Konsep Inti
- Bag‑of‑Words (BoW): representasi frekuensi token tanpa urutan.
- n‑gram: fitur berurutan (kata/karakter) untuk konteks lokal: mis. (1,2) = unigram+bigram.
- TF–IDF: TF menekankan istilah sering; IDF menurunkan istilah umum. Sublinear TF (log scaling) untuk menekan efek frekuensi ekstrem.
\(\text{tfidf}(t,d) = \text{tf}(t,d) \cdot\big(\log\frac{N+1}{\text{df}(t)+1} + 1\big)\) dengan normalisasi L2 per dokumen.
2) Praktik Google Colab — BoW, n‑gram, TF–IDF
Gunakan korpus bersih dari sessi2 atau varian v1/v2 dari sessi3.
A. Setup & Data
!pip -q install pandas numpy scikit-learn matplotlib
import numpy as np, pandas as pd
import matplotlib.pyplot as plt
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# Muat data
try:
df = pd.read_csv('corpus_sessi3_variants.csv')
corpus = df['v2_stop_stemID'].fillna('').astype(str).tolist() # contoh: pakai varian stop+stem ID
print('Loaded corpus_sessi3_variants.csv (v2_stop_stemID):', len(corpus))
except:
df = pd.read_csv('corpus_sessi2_normalized.csv')
corpus = df['text'].fillna('').astype(str).tolist()
print('Loaded corpus_sessi2_normalized.csv:', len(corpus))
print('Contoh dokumen:', corpus[0][:120])
B. BoW vs n‑gram
# Unigram BoW
cv1 = CountVectorizer(ngram_range=(1,1), min_df=1)
X1 = cv1.fit_transform(corpus)
# Unigram+Bigram BoW
cv2 = CountVectorizer(ngram_range=(1,2), min_df=1)
X2 = cv2.fit_transform(corpus)
print('Unigram shape:', X1.shape, ' | Vocab size:', len(cv1.get_feature_names_out()))
print('Uni+Bi shape:', X2.shape, ' | Vocab size:', len(cv2.get_feature_names_out()))
# Periksa top 20 n‑gram teratas (berdasarkan frekuensi total)
import numpy as np
sumX2 = np.asarray(X2.sum(axis=0)).ravel()
idx = sumX2.argsort()[::-1][:20]
terms = cv2.get_feature_names_out()[idx]
counts = sumX2[idx]
for t,c in zip(terms, counts):
print(f'{t:25s} {int(c)}')
C. TF–IDF & Pengaturan Penting
# TF–IDF dengan opsi umum
vec = TfidfVectorizer(
ngram_range=(1,2),
min_df=2, # buang istilah sangat langka
max_df=0.9, # buang istilah terlalu umum
sublinear_tf=True, # log(1+tf)
norm='l2'
)
X = vec.fit_transform(corpus)
print('TF–IDF shape:', X.shape)
# Lihat beberapa fitur teratas menurut IDF terendah (paling umum)
idf = vec.idf_
feat = vec.get_feature_names_out()
ord = np.argsort(idf)[:20]
for i in ord:
print(f'{feat[i]:30s} idf={idf[i]:.3f}')
D. Retrieval dengan Cosine Similarity
def search(query, topk=5):
q = vec.transform([query])
sims = cosine_similarity(X, q).ravel()
rank = sims.argsort()[::-1][:topk]
return [(float(sims[i]), corpus[i]) for i in rank]
queries = [
'pengiriman cepat kualitas bagus',
'screen glare outdoors',
'refund proses cepat',
'bluetooth stabil',
'login delay'
]
for q in queries:
print('\nQUERY:', q)
for s,doc in search(q, topk=3):
print(f' sim={s:.3f} {doc[:100]}')
E. Pratinjau Reduksi Dimensi — TruncatedSVD (LSA)
from sklearn.decomposition import TruncatedSVD
k = 100 if X.shape[1] > 100 else max(2, X.shape[1]//2)
svd = TruncatedSVD(n_components=k, random_state=42)
Xk = svd.fit_transform(X)
print('Reduced shape:', Xk.shape)
print('Explained variance (sum top‑k):', svd.explained_variance_ratio_.sum().round(3))
# Visualisasi 2D pertama (komponen 1 vs 2)
import matplotlib.pyplot as plt
plt.figure(figsize=(6,5))
plt.scatter(Xk[:,0], Xk[:,1], s=18)
plt.title('Proyeksi LSA: Komponen 1 vs 2')
plt.xlabel('SVD-1'); plt.ylabel('SVD-2'); plt.tight_layout(); plt.show()
F. Perbandingan Karakter n‑gram (Opsional)
# Karakter n‑gram berguna untuk bahasa campuran/typo
char_vec = TfidfVectorizer(analyzer='char', ngram_range=(3,5), min_df=2)
Xc = char_vec.fit_transform(corpus)
print('Char TF–IDF shape:', Xc.shape)
G. Ekspor Artefak
import joblib
joblib.dump(vec, 'tfidf_vec_sessi4.joblib')
joblib.dump(svd, 'svd_sessi4.joblib')
print('Tersimpan: tfidf_vec_sessi4.joblib, svd_sessi4.joblib')
3) Studi Kasus & Analisis
| Kasus | Representasi Disarankan | Alasan |
|---|---|---|
| Pencarian dokumen kelas | TF–IDF unigram+bigram | Konteks frasa pendek ditangkap; cocok untuk cosine |
| Ulasan produk campuran ID/EN | TF–IDF + char n‑gram | Robust terhadap typo/variasi ejaan |
| Ringkas fitur untuk model cepat | TF–IDF → LSA (SVD 100D) | Kurangi sparsity & dimensi; akselerasi model downstream |
4) Tugas Mini (Dinilai)
- Bandingkan unigram vs uni+bi pada metrik: retrieval@5 untuk 5 query Anda.
- Uji min_df ∈ {1,2,3} dan max_df ∈ {0.85,0.9,0.95}; laporkan perubahan ukuran kosakata & kinerja retrieval.
- Gunakan LSA (SVD) → ukur perubahan waktu komputasi dan kualitas retrieval.