PLJ-TIF_E41253311/backend/app/ml_model.py

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