File size: 7,646 Bytes
c5e83c6 99f3ba3 c5e83c6 99f3ba3 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 87defe8 c5e83c6 f165c76 c5e83c6 f165c76 c5e83c6 99f3ba3 c5e83c6 f165c76 c5e83c6 f165c76 c5e83c6 99f3ba3 c5e83c6 87defe8 c5e83c6 f165c76 c5e83c6 99f3ba3 c5e83c6 f165c76 99f3ba3 87defe8 99f3ba3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
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 |