168 lines
3.4 KiB
Python
168 lines
3.4 KiB
Python
import os
|
|
import joblib
|
|
|
|
|
|
# =========================================================
|
|
# KONFIGURASI MODEL
|
|
# =========================================================
|
|
|
|
BASE_DIR = os.path.dirname(
|
|
os.path.dirname(
|
|
os.path.abspath(__file__)
|
|
)
|
|
)
|
|
|
|
MODEL_DIR = os.path.join(
|
|
BASE_DIR,
|
|
"models"
|
|
)
|
|
|
|
MODEL_PATH = os.path.join(
|
|
MODEL_DIR,
|
|
"model_naive_bayes.pkl"
|
|
)
|
|
|
|
|
|
# =========================================================
|
|
# LOAD MODEL
|
|
# =========================================================
|
|
|
|
if not os.path.exists(MODEL_PATH):
|
|
raise FileNotFoundError(
|
|
f"Model tidak ditemukan: {MODEL_PATH}"
|
|
)
|
|
|
|
try:
|
|
model_final = joblib.load(MODEL_PATH)
|
|
|
|
except Exception as e:
|
|
raise RuntimeError(
|
|
f"Gagal memuat model: {str(e)}"
|
|
)
|
|
|
|
|
|
# =========================================================
|
|
# VALIDASI PIPELINE
|
|
# =========================================================
|
|
|
|
if not hasattr(model_final, "named_steps"):
|
|
raise ValueError(
|
|
"Model harus berupa Pipeline "
|
|
"TF-IDF + MultinomialNB."
|
|
)
|
|
|
|
|
|
# Pastikan step TF-IDF tersedia
|
|
if "tfidf" not in model_final.named_steps:
|
|
raise ValueError(
|
|
"Pipeline tidak memiliki step 'tfidf'."
|
|
)
|
|
|
|
|
|
# Pastikan step Naïve Bayes tersedia
|
|
if "nb" not in model_final.named_steps:
|
|
raise ValueError(
|
|
"Pipeline tidak memiliki step 'nb'."
|
|
)
|
|
|
|
|
|
# =========================================================
|
|
# AMBIL KOMPONEN MODEL
|
|
# =========================================================
|
|
|
|
tfidf_vectorizer = model_final.named_steps["tfidf"]
|
|
|
|
nb_model = model_final.named_steps["nb"]
|
|
|
|
|
|
# =========================================================
|
|
# PREDIKSI SENTIMEN
|
|
# =========================================================
|
|
|
|
def predict_sentiment(text):
|
|
|
|
if not text or not str(text).strip():
|
|
raise ValueError(
|
|
"Teks komentar tidak boleh kosong."
|
|
)
|
|
|
|
text = str(text).strip()
|
|
|
|
# Prediksi
|
|
prediction = model_final.predict([text])[0]
|
|
|
|
# Probabilitas
|
|
probabilities = model_final.predict_proba([text])[0]
|
|
|
|
# Konversi label
|
|
sentiment = (
|
|
"Positif"
|
|
if int(prediction) == 1
|
|
else "Negatif"
|
|
)
|
|
|
|
# Probabilitas kelas yang diprediksi
|
|
probability = float(
|
|
probabilities[
|
|
list(model_final.classes_).index(prediction)
|
|
]
|
|
)
|
|
|
|
return {
|
|
"label": int(prediction),
|
|
"sentiment": sentiment,
|
|
"probability": round(
|
|
probability,
|
|
6
|
|
)
|
|
}
|
|
|
|
|
|
# =========================================================
|
|
# TF-IDF SATU KOMENTAR
|
|
# =========================================================
|
|
|
|
def get_tfidf(text):
|
|
|
|
if not text or not str(text).strip():
|
|
return []
|
|
|
|
text = str(text).strip()
|
|
|
|
# Transform menggunakan vectorizer
|
|
# yang SUDAH FIT dari model final
|
|
vector = tfidf_vectorizer.transform(
|
|
[text]
|
|
)
|
|
|
|
feature_names = (
|
|
tfidf_vectorizer
|
|
.get_feature_names_out()
|
|
)
|
|
|
|
hasil = []
|
|
|
|
# Karena sparse matrix,
|
|
# ambil hanya fitur yang nilainya > 0
|
|
row = vector[0]
|
|
|
|
for col_index, nilai in zip(
|
|
row.indices,
|
|
row.data
|
|
):
|
|
|
|
hasil.append({
|
|
"kata": feature_names[col_index],
|
|
"nilai": round(
|
|
float(nilai),
|
|
6
|
|
)
|
|
})
|
|
|
|
# Urutkan berdasarkan nilai TF-IDF terbesar
|
|
hasil.sort(
|
|
key=lambda x: x["nilai"],
|
|
reverse=True
|
|
)
|
|
|
|
return hasil |