126 lines
4.0 KiB
Python
126 lines
4.0 KiB
Python
"""
|
|
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)
|