""" Models.py - Modul Model Klasifikasi dan Vektorisasi TF-IDF Proyek Tugas Akhir: Analisis Sentimen Komentar Berbasis TF-IDF & Logistic Regression Modul ini menangani: 1. Pemuatan pre-trained TF-IDF Vectorizer dan model Logistic Regression 2. Transformasi teks bersih ke vektor TF-IDF 3. Perhitungan skor probabilitas dan decision value 4. Ekstraksi fitur n-gram dominan berserta bobot koefisiennya """ import os import time import warnings import joblib from typing import Dict, Any warnings.filterwarnings("ignore") BASE_DIR = os.path.dirname(os.path.abspath(__file__)) MODEL_PATH = os.path.join(BASE_DIR, "model_logistic_regression.pkl") TFIDF_PATH = os.path.join(BASE_DIR, "tfidf_vectorizer.pkl") # Memuat model dan vectorizer saat modul diimpor model = joblib.load(MODEL_PATH) tfidf = joblib.load(TFIDF_PATH) def predict_sentiment(teks_bersih: str) -> Dict[str, Any]: """ Melakukan inferensi klasifikasi sentimen berdasarkan teks yang telah diproses: - X = tfidf.transform([teks_bersih]) - Prediksi label: POSITIF / NEGATIF - Probabilitas Sigmoid untuk tiap kelas - Nilai Decision Function (z = w*x + b) - Ekstraksi kontribusi bobot fitur dominan """ start_time = time.time() if not teks_bersih or not teks_bersih.strip(): return { "success": True, "sentimen": "GAGAL DIANALISIS", "preprocessing": "", "probabilitas": { "negatif": 0.0, "positif": 0.0 }, "keyakinan": 0.0, "decision_value": 0.0, "fitur_dominan": [], "elapsed_ms": 0.5, "keterangan": "Hasil preprocessing kosong (tidak dapat dianalisis)." } # Transformasi TF-IDF X = tfidf.transform([teks_bersih]) # Ekstraksi Fitur N-Gram Dominan feature_names = tfidf.get_feature_names_out() non_zero_indices = X.nonzero()[1] has_features = len(non_zero_indices) > 0 elapsed_ms = round((time.time() - start_time) * 1000, 1) # Bila fitur kata tidak ditemukan pada TF-IDF, jangan gunakan bias model if not has_features: return { "success": True, "sentimen": "FITUR TIDAK DITEMUKAN", "preprocessing": teks_bersih, "probabilitas": { "negatif": 0.0, "positif": 0.0 }, "keyakinan": 0.0, "decision_value": 0.0, "fitur_dominan": [], "fitur_ditemukan": False, "keterangan": "Fitur tidak ditemukan", "elapsed_ms": max(1.0, elapsed_ms) } # Prediksi Label dan Probabilitas hanya bila fitur kata ditemukan hasil_prediksi = model.predict(X)[0] proba = model.predict_proba(X)[0] prob_negatif = float(proba[0]) prob_positif = float(proba[1]) decision_val = float(model.decision_function(X)[0]) features_info = [] for idx in non_zero_indices: token = feature_names[idx] val = float(X[0, idx]) weight = float(model.coef_[0, idx]) features_info.append({ "token": token, "tfidf": round(val, 4), "weight": round(weight, 4) }) # Urutkan berdasarkan nilai absolut kontribusi fitur features_info.sort(key=lambda item: abs(item['weight'] * item['tfidf']), reverse=True) top_features = features_info[:8] keyakinan = (prob_negatif if hasil_prediksi == 'NEGATIF' else prob_positif) * 100 return { "success": True, "sentimen": str(hasil_prediksi), "preprocessing": teks_bersih, "probabilitas": { "negatif": round(prob_negatif, 4), "positif": round(prob_positif, 4) }, "keyakinan": round(keyakinan, 1), "decision_value": round(decision_val, 4), "fitur_dominan": top_features, "fitur_ditemukan": True, "keterangan": "Fitur berhasil diekstraksi", "elapsed_ms": max(1.0, elapsed_ms) } if __name__ == "__main__": test_clean = "sok cantik banget" hasil = predict_sentiment(test_clean) print("Hasil Prediksi:", hasil)