reyhanadr's picture
add cleaned_text for response
87defe8
import torch
import emoji
import re
from transformers import BertTokenizer, BertForSequenceClassification
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Dict, List
# ====================================================================
# 1. KELAS LOGIKA ANDA (Tidak ada perubahan)
# ====================================================================
# Impor library yang dibutuhkan
import re # Untuk operasi regular expression
import emoji # Untuk menangani emoji
# Definisikan sebuah kelas untuk mengelompokkan semua fungsi pembersihan teks
class TextCleaner:
# Metode constructor, dieksekusi saat objek TextCleaner dibuat
def __init__(self):
# Inisialisasi daftar karakter yang akan diperiksa untuk pengulangan.
# Daftar ini berisi berbagai tanda baca dan simbol.
self.character = ['.', ',', ';', ':', '?', '!', '(', ')', '[', ']', '{', '}', '<', '>', '"', '/', '\'', '-', '@']
# Tambahkan semua huruf abjad (a-z) ke dalam daftar karakter di atas.
self.character.extend([chr(i) for i in range(ord('a'), ord('z') + 1)])
# Metode untuk membersihkan karakter yang berulang lebih dari 2 kali (misal: "haaiiii" -> "haai").
def repeatcharClean(self, text):
# Ulangi untuk setiap karakter dalam daftar 'self.character'
for char_to_clean in self.character:
# Buat pola regex untuk menemukan karakter yang berulang 3 kali atau lebih secara berurutan.
# Contoh: jika char_to_clean adalah 'a', polanya akan mencari 'aaa' atau 'aaaa', dst.
pattern = re.compile(re.escape(char_to_clean) + r'{3,}')
# Ganti urutan karakter yang berulang tersebut dengan satu karakter saja.
# Contoh: "good moooorning" menjadi "good morning".
text = pattern.sub(char_to_clean, text)
return text
# Metode utama untuk menjalankan seluruh proses pembersihan teks
def clean_review(self, text):
# 1. Ubah semua teks menjadi huruf kecil (lowercase) untuk konsistensi.
text = text.lower()
# 2. Ganti spasi, tab, atau baris baru yang berlebih dengan satu spasi saja.
text = re.sub(r'\s+', ' ', text)
# 3. Hapus karakter non-ASCII (seperti karakter Cina, Arab, atau beberapa emoji kompleks).
text = re.sub(r'[^\x00-\x7F]+', ' ', text)
# 4. Ganti @mention dan URL dengan token placeholder.
new_text = []
# Pecah teks menjadi kata-kata
for word in text.split(" "):
# Jika kata diawali dengan '@' dan lebih dari 1 karakter, ganti dengan '@USER'.
word = '@USER' if word.startswith('@') and len(word) > 1 else word
# Jika kata diawali dengan 'http', ganti dengan 'HTTPURL'.
word = 'HTTPURL' if word.startswith('http') else word
new_text.append(word)
# Gabungkan kembali kata-kata menjadi satu kalimat.
text = " ".join(new_text)
# 5. Ubah emoji menjadi representasi teksnya (misal: 😊 -> ':smiling_face:').
text = emoji.demojize(text)
# 6. Hapus representasi teks emoji yang polanya seperti ':nama_emoji:'.
text = re.sub(r':[A-Za-z_-]+:', ' ', text)
# 7. Hapus emoticon umum berbasis teks seperti :), :D, :(, xD, dll.
text = re.sub(r"([xX;:]'?[dDpPvVoO3)(])", ' ', text)
# 8. Hapus semua tanda baca dan simbol yang tersisa.
text = re.sub(r'["#$%&()*+,./:;<=>\[\]\\^_`{|}~]', ' ', text)
# 9. Panggil metode 'repeatcharClean' untuk membersihkan karakter yang berulang.
text = self.repeatcharClean(text)
# 10. Lakukan pembersihan spasi terakhir dan hapus spasi di awal/akhir kalimat.
text = re.sub(r'\s+', ' ', text).strip()
# Kembalikan teks yang sudah bersih
return text
class SentimentPredictor:
def __init__(self, tokenizer, model):
self.tokenizer = tokenizer
self.model = model
self.device = torch.device("cpu")
self.model.to(self.device)
self.label_mapping = {0: 'Positif', 1: 'Netral', 2: 'Negatif'}
def predict(self, text: str) -> (str, float, Dict[str, float]):
inputs = self.tokenizer(text, return_tensors='pt', truncation=True, padding=True, max_length=280)
inputs = {k: v.to(self.device) for k, v in inputs.items()}
with torch.no_grad():
outputs = self.model(**inputs)
logits = outputs.logits
probabilities = torch.softmax(logits, dim=1)[0]
confidence_score = probabilities.max().item()
predicted_label_id = probabilities.argmax().item()
sentiment = self.label_mapping[predicted_label_id]
all_scores = {self.label_mapping[i]: prob.item() for i, prob in enumerate(probabilities)}
return sentiment, confidence_score, all_scores
# ====================================================================
# 2. INISIALISASI MODEL & APLIKASI FASTAPI (Tidak ada perubahan)
# ====================================================================
print("Memuat model dan tokenizer...")
tokenizer = BertTokenizer.from_pretrained('indolem/indobertweet-base-uncased')
model = BertForSequenceClassification.from_pretrained('indolem/indobertweet-base-uncased', num_labels=3)
model_path = 'model_indoBERTweet_100Epochs_sentiment.pth'
state_dict = torch.load(model_path, map_location=torch.device('cpu'))
model.load_state_dict(state_dict, strict=False)
model.eval()
print("Model berhasil dimuat.")
text_cleaner = TextCleaner()
sentiment_predictor = SentimentPredictor(tokenizer, model)
app = FastAPI(
title="API Klasifikasi Sentimen",
description="Sebuah API untuk menganalisis sentimen teks Bahasa Indonesia."
)
# ====================================================================
# 3. DEFINISIKAN MODEL INPUT & OUTPUT API
# ====================================================================
class TextInput(BaseModel):
text: str
# --- [PERBAIKAN] --- Menambahkan definisi BatchTextInput ---
# Model ini memberitahu FastAPI bahwa endpoint batch akan menerima
# sebuah objek JSON dengan satu key "texts" yang berisi daftar string.
class BatchTextInput(BaseModel):
texts: List[str]
# -----------------------------------------------------------
class PredictionOutput(BaseModel):
cleaned_text: str = None # Optional, hanya diisi pada batch
sentiment: str
confidence: float
all_scores: Dict[str, float]
# ====================================================================
# 4. BUAT ENDPOINT (Tidak ada perubahan logika)
# ====================================================================
@app.get("/")
def read_root():
return {"message": "Selamat datang di API Klasifikasi Sentimen"}
@app.post("/predict", response_model=PredictionOutput)
def predict_sentiment(request: TextInput):
cleaned_text = text_cleaner.clean_review(request.text)
sentiment, confidence, all_scores = sentiment_predictor.predict(cleaned_text)
return PredictionOutput(
sentiment=sentiment,
confidence=confidence,
all_scores=all_scores
)
@app.post("/predict-batch", response_model=List[PredictionOutput])
def predict_sentiment_batch(request: BatchTextInput):
results = []
for text in request.texts:
cleaned_text = text_cleaner.clean_review(text)
sentiment, confidence, all_scores = sentiment_predictor.predict(cleaned_text)
results.append(PredictionOutput(
cleaned_text=cleaned_text,
sentiment=sentiment,
confidence=confidence,
all_scores=all_scores
))
return results