Compare commits

...
3 Commits
Author SHA1 Message Date
5803025041 0364638834 "dataset and gap" 2026-09-27 04:39:40 +07:00
5803025041 1e8e7e39dd Initial commit 2026-09-26 23:33:28 +07:00
5803025041 8f8acbc24b Selesai eksperimen 08 sentiment gap dan perbaikan model 2026-09-26 23:24:20 +07:00
7 changed files with 300 additions and 1 deletions
+290
View File
@@ -0,0 +1,290 @@
import os
os.environ["TF_USE_LEGACY_KERAS"] = "1" # Bypass Keras 3 & optree
import re
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datasets import load_dataset
from sklearn.model_selection import train_test_split
from sklearn.metrics import (
classification_report,
confusion_matrix,
ConfusionMatrixDisplay
)
import tensorflow as tf
import tf_keras as keras
from tf_keras import layers
from tf_keras.callbacks import EarlyStopping
from tf_keras.preprocessing.text import Tokenizer # type: ignore
from tf_keras.preprocessing.sequence import pad_sequences
# ============================================================
# KONFIGURASI DIREKTORI PENYIMPANAN
# ============================================================
VOCAB_SIZE = 10000
MAX_LEN = 60
EMBED_DIM = 64
EPOCHS = 20
BATCH_SIZE = 64
RANDOM_STATE = 42
FIGURES_DIR = "results/figures"
REPORTS_DIR = "results/reports"
MODELS_DIR = "results/models"
os.makedirs(FIGURES_DIR, exist_ok=True)
os.makedirs(REPORTS_DIR, exist_ok=True)
os.makedirs(MODELS_DIR, exist_ok=True)
# ============================================================
# SEL 7 — LOAD DATASET TOKOPEDIA
# ============================================================
print("=" * 60)
print("SEL 7 — LOAD DATASET")
print("=" * 60)
dataset = load_dataset(
"farhamu/tokopedia-product-reviews-2019"
)
df = dataset["train"].to_pandas()
# Buang rating 3
df = df[df["rating"] != 3].copy()
# Rating 4-5 = positif (1), Rating 1-2 = negatif (0)
df["label"] = (df["rating"] >= 4).astype(int)
# Rename text menjadi teks
df = df.rename(
columns={"text": "teks"}
)[["teks", "label"]].reset_index(drop=True)
print(df.head(5))
print(f"\nShape : {df.shape}")
print(f"Kolom : {df.columns.tolist()}")
print(f"\nDistribusi label:\n{df['label'].value_counts().sort_index()}")
print(f"\nNull : {df.isnull().sum().sum()}")
# ============================================================
# DISTRIBUSI LABEL
# ============================================================
label_counts = df["label"].value_counts().sort_index()
plt.figure(figsize=(7, 5))
plt.bar(
["Negatif", "Positif"],
[label_counts.get(0, 0), label_counts.get(1, 0)]
)
plt.title("Distribusi Label Sentimen Tokopedia")
plt.xlabel("Sentimen")
plt.ylabel("Jumlah Review")
plt.tight_layout()
plt.savefig(f"{FIGURES_DIR}/distribusi_label_sentimen.png")
plt.close()
# ============================================================
# CEK PANJANG TEKS
# ============================================================
df["panjang"] = df["teks"].apply(lambda x: len(str(x).split()))
print(f"\nPanjang teks:\n{df['panjang'].describe()}")
# ============================================================
# SEL 8 — TEXT PREPROCESSING
# ============================================================
print("\n" + "=" * 60)
print("SEL 8 — TEXT PREPROCESSING")
print("=" * 60)
def clean_text(teks):
teks = str(teks).lower()
teks = re.sub(r"[^a-z\s]", "", teks)
teks = re.sub(r"\s+", " ", teks).strip()
return teks
df["teks_bersih"] = df["teks"].apply(clean_text)
# Train/Test Split
X = np.array(df["teks_bersih"].tolist())
y = np.array(df["label"].tolist(), dtype=int)
X_tr, X_te, y_tr, y_te = train_test_split(
X, y,
test_size=0.2,
random_state=RANDOM_STATE,
stratify=y
)
print(f"Training data : {len(X_tr)}")
print(f"Testing data : {len(X_te)}")
# Tokenizer
tokenizer = Tokenizer(num_words=VOCAB_SIZE, oov_token="<OOV>")
tokenizer.fit_on_texts(X_tr)
# Text -> Integer Sequence
X_tr_seq = tokenizer.texts_to_sequences(X_tr)
X_te_seq = tokenizer.texts_to_sequences(X_te)
# Padding
X_tr_pad = pad_sequences(X_tr_seq, maxlen=MAX_LEN, padding="post")
X_te_pad = pad_sequences(X_te_seq, maxlen=MAX_LEN, padding="post")
print(f"\nX_tr_pad shape : {X_tr_pad.shape}")
print(f"X_te_pad shape : {X_te_pad.shape}")
print(f"Vocab size : {len(tokenizer.word_index)}")
# ============================================================
# SEL 9 — MODEL GAP
# ============================================================
print("\n" + "=" * 60)
print("SEL 9 — MODEL GAP")
print("=" * 60)
sent_model = keras.Sequential([
layers.Embedding(input_dim=VOCAB_SIZE, output_dim=EMBED_DIM, input_length=MAX_LEN),
layers.GlobalAveragePooling1D(),
layers.Dense(64, activation="relu"),
layers.Dropout(0.4),
layers.Dense(1, activation="sigmoid")
], name="Sentimen_GAP")
sent_model.compile(
optimizer="adam",
loss="binary_crossentropy",
metrics=["accuracy"]
)
sent_model.summary()
# ============================================================
# TRAINING
# ============================================================
print("\n" + "=" * 60)
print("TRAINING MODEL GAP")
print("=" * 60)
early_stop = EarlyStopping(
monitor="val_loss",
patience=4,
restore_best_weights=True
)
history_sent = sent_model.fit(
X_tr_pad, y_tr,
epochs=EPOCHS,
batch_size=BATCH_SIZE,
validation_split=0.15,
callbacks=[early_stop],
verbose=1
)
# ============================================================
# TEST ACCURACY & HISTORY PLOTS
# ============================================================
loss_s, acc_s = sent_model.evaluate(X_te_pad, y_te, verbose=0)
print(f"\nSentimen GAP → Test accuracy: {acc_s:.4f}")
# Plot Accuracy & Loss
plt.figure(figsize=(8, 5))
plt.plot(history_sent.history["accuracy"], label="Training Accuracy")
plt.plot(history_sent.history["val_accuracy"], label="Validation Accuracy")
plt.title("Training vs Validation Accuracy — GAP")
plt.xlabel("Epoch")
plt.ylabel("Accuracy")
plt.legend()
plt.tight_layout()
plt.savefig(f"{FIGURES_DIR}/histori_gap_sentimen.png")
plt.close()
# ============================================================
# SEL 11 — EVALUATION
# ============================================================
print("\n" + "=" * 60)
print("EVALUATION")
print("=" * 60)
y_pred_prob = sent_model.predict(X_te_pad, verbose=0)
y_pred = (y_pred_prob > 0.5).astype("int").flatten()
report = classification_report(
y_te, y_pred,
target_names=["Negatif (0)", "Positif (1)"]
)
print("\nClassification Report:")
print(report)
# Simpan Classification Report Txt
with open(f"{REPORTS_DIR}/classification_report_gap_sentimen.txt", "w", encoding="utf-8") as f:
f.write(report)
# Confusion Matrix Plot
cm = confusion_matrix(y_te, y_pred)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=["Negatif", "Positif"])
fig, ax = plt.subplots(figsize=(6, 6))
disp.plot(ax=ax, cmap="Blues")
plt.title("Confusion Matrix — Model Sentimen GAP")
plt.tight_layout()
plt.savefig(f"{FIGURES_DIR}/confusion_matrix_gap_sentimen.png")
plt.close()
# ============================================================
# SEL 12 — INFERENCE (PREDIKSI 5 ULASAN)
# ============================================================
def prediksi_sentimen(teks, model, tokenizer, maxlen):
teks_bersih = clean_text(teks)
seq = tokenizer.texts_to_sequences([teks_bersih])
padded = pad_sequences(seq, maxlen=maxlen, padding="post")
prob = model.predict(padded, verbose=0)[0][0]
label = "Positif" if prob > 0.5 else "Negatif"
return label, prob
print("\n" + "=" * 60)
print("PREDIKSI 5 ULASAN")
print("=" * 60)
ulasan_uji = [
"Produk berkualitas tinggi, pengiriman cepat, packing aman!",
"Kecewa banget, barang datang rusak dan customer service tidak responsif",
"Lumayan sih, tapi harganya kemahalan untuk kualitas segitu",
"Tidak sesuai deskripsi sama sekali, mau return tapi ribet",
"Barang bagus, sesuai dengan foto dan penjual sangat ramah"
]
for i, ulasan in enumerate(ulasan_uji, start=1):
label, prob = prediksi_sentimen(ulasan, sent_model, tokenizer, MAX_LEN)
print(f"\n{i}. {ulasan}\n Prediksi : {label}\n Confidence: {prob:.3f}")
# ============================================================
# SIMPAN MODEL
# ============================================================
sent_model.save(f"{MODELS_DIR}/model_gap_sentimen.keras")
print("\nModel GAP berhasil disimpan di folder results/models/")
+2 -1
View File
@@ -7,7 +7,8 @@ scikit-learn>=1.3.0
# Scientific Computing & Data Manipulation
numpy>=1.24.0
pandas>=2.0.0
datasets>=2.0.0
# Data Visualization
matplotlib>=3.7.0
seaborn>=0.12.0
seaborn>=0.12.0
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.
@@ -0,0 +1,8 @@
precision recall f1-score support
Negatif (0) 0.58 0.34 0.42 185
Positif (1) 0.98 0.99 0.99 7572
accuracy 0.98 7757
macro avg 0.78 0.66 0.71 7757
weighted avg 0.97 0.98 0.98 7757
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB