Ken2707's picture
Update app.py
8bc20f3 verified
Raw History Blame Contribute Delete
7.53 kB
from fastapi import FastAPI, UploadFile, File, Form, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import joblib
import pandas as pd
import numpy as np
import re
import contractions
import nltk
import pickle
from nltk.tokenize import word_tokenize
from io import StringIO
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.sequence import pad_sequences
import tensorflow as tf
def make_safe_layer(layer_class):
class SafeLayer(layer_class):
def __init__(self, **kwargs):
kwargs.pop('quantization_config', None)
super().__init__(**kwargs)
return SafeLayer
safe_objects = {
'Embedding': make_safe_layer(tf.keras.layers.Embedding),
'Dense': make_safe_layer(tf.keras.layers.Dense),
'LSTM': make_safe_layer(tf.keras.layers.LSTM),
'Dropout': make_safe_layer(tf.keras.layers.Dropout),
'SpatialDropout1D': make_safe_layer(tf.keras.layers.SpatialDropout1D),
'Bidirectional': make_safe_layer(tf.keras.layers.Bidirectional)
}
nltk.download('punkt', quiet=True)
nltk.download('stopwords', quiet=True)
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
models_dict = {
"fake_review": {"lr": None, "rf": None, "xgb": None, "lstm": None, "tfidf": None, "tokenizer": None},
"spam": {"lr": None, "rf": None, "xgb": None, "lstm": None, "tfidf": None, "tokenizer": None}
}
def load_pkl(path):
try: return joblib.load(path)
except: return None
def load_keras(path):
try:
print(f"Mencoba load model Keras: {path}")
return load_model(path, custom_objects=safe_objects)
except Exception as e:
print(f"❌ ERROR KERAS ({path}): {str(e)}")
return None
def load_tokenizer(path):
try:
with open(path, 'rb') as f: return pickle.load(f)
except: return None
print("Loading Models... Mohon tunggu.")
models_dict["fake_review"]["tfidf"] = load_pkl("models/klasik/tfidf_vectorizer.pkl")
models_dict["fake_review"]["lr"] = load_pkl("models/klasik/logistic_regression_model.pkl")
models_dict["fake_review"]["rf"] = load_pkl("models/klasik/random_forest_model.pkl")
models_dict["fake_review"]["xgb"] = load_pkl("models/klasik/xgboost_model.pkl")
models_dict["fake_review"]["tokenizer"] = load_tokenizer("models/deep_learning/tokenizer.pkl")
models_dict["fake_review"]["lstm"] = load_keras("models/deep_learning/lstm_word2vec_model.h5")
models_dict["spam"]["tfidf"] = load_pkl("models/klasik_spam/spam_tfidf_vectorizer.pkl")
models_dict["spam"]["lr"] = load_pkl("models/klasik_spam/spam_logistic_regression_model.pkl")
models_dict["spam"]["rf"] = load_pkl("models/klasik_spam/spam_random_forest_model.pkl")
models_dict["spam"]["xgb"] = load_pkl("models/klasik_spam/spam_xgboost_model.pkl")
models_dict["spam"]["tokenizer"] = load_tokenizer("models/deep_learning_spam/spam_tokenizer.pkl")
models_dict["spam"]["lstm"] = load_keras("models/deep_learning_spam/spam_lstm_word2vec_model.h5")
print("Selesai loading model!")
def preprocessing(statement):
if not isinstance(statement, str): return ""
statement = statement.lower()
statement = contractions.fix(statement)
statement = re.sub(r'[^\w\s]', '', statement)
statement = re.sub(r'\d+', '', statement)
tokens = word_tokenize(statement)
tokens = [token for token in tokens if token.isalpha() and len(token) > 2]
return ' '.join(tokens)
def analyze_bulk(texts: list, mode: str, model_type: str):
if mode not in ["fake_review", "spam"]: return {"error": "Mode invalid."}
if not texts: return []
clean_texts = [preprocessing(t) for t in texts]
if model_type == "lstm":
tokenizer = models_dict[mode]["tokenizer"]
lstm_model = models_dict[mode]["lstm"]
if not tokenizer or not lstm_model:
return {"error": f"File model LSTM untuk {mode} tidak ditemukan."}
seqs = tokenizer.texts_to_sequences(clean_texts)
padded = pad_sequences(seqs, maxlen=100, padding='post', truncating='post')
predictions = lstm_model.predict(padded, batch_size=32, verbose=0).flatten()
scores = [float(p) * 100 for p in predictions]
elif model_type in ["lr", "rf", "xgb"]:
tfidf = models_dict[mode]["tfidf"]
clf_model = models_dict[mode][model_type]
if not tfidf or not clf_model:
return {"error": f"File model {model_type} untuk {mode} tidak ditemukan."}
vec_texts = tfidf.transform(clean_texts)
probas = clf_model.predict_proba(vec_texts)
scores = [float(p[1]) * 100 for p in probas]
else:
return {"error": "Model type tidak dikenali."}
results = []
for text, score in zip(texts, scores):
is_flagged = score > 50
confidence = score if is_flagged else 100 - score
if mode == "spam":
label = "Spam" if is_flagged else "Safe (Ham)"
reason = "The text contains word order patterns similar to those found in promotional messages or scams." if is_flagged else "The sentence appears similar to ordinary conversational message."
else:
label = "Fake" if is_flagged else "Genuine"
reason = "The writing style bears a strong resemblance to paid review templates or no clear context." if is_flagged else "The review has a natural variety of vocabulary and recounts the experience in detail."
results.append({
"text": text,
"label": label,
"confidence": confidence,
"is_flagged": is_flagged,
"reason": reason
})
return results
class TextRequest(BaseModel):
text: str
mode: str
model_type: str
model_config = {
"protected_namespaces": ()
}
@app.post("/api/analyze/text")
def analyze_single(req: TextRequest):
result = analyze_bulk([req.text], req.mode, req.model_type)
if isinstance(result, dict) and "error" in result:
raise HTTPException(status_code=500, detail=result["error"])
return {"status": "success", "result": result[0]}
@app.post("/api/analyze/file")
async def analyze_file(mode: str = Form(...), model_type: str = Form(...), file: UploadFile = File(...)):
if not file.filename.endswith(('.csv', '.txt')):
raise HTTPException(status_code=400, detail="Hanya mendukung file .csv dan .txt")
content = await file.read()
decoded = content.decode('utf-8')
texts = []
if file.filename.endswith('.txt'):
texts = [line.strip() for line in decoded.split('\n') if line.strip()]
elif file.filename.endswith('.csv'):
df = pd.read_csv(StringIO(decoded))
text_col = next((col for col in ['text', 'review', 'v2', 'text_'] if col in df.columns), df.columns[0])
texts = df[text_col].dropna().astype(str).tolist()
results = analyze_bulk(texts, mode, model_type)
if isinstance(results, dict) and "error" in results:
raise HTTPException(status_code=500, detail=results["error"])
return {"status": "success", "total": len(results), "results": results}