File size: 3,659 Bytes
32dba56
 
 
 
 
 
e2b8a26
 
 
 
 
32dba56
 
 
 
e2b8a26
 
32dba56
e2b8a26
32dba56
 
 
 
 
 
 
 
 
 
e2b8a26
32dba56
e2b8a26
32dba56
 
e2b8a26
32dba56
 
 
 
 
 
 
 
 
e2b8a26
32dba56
 
 
 
 
 
 
 
 
 
e2b8a26
32dba56
 
e2b8a26
32dba56
 
 
e2b8a26
 
 
 
32dba56
 
 
 
e2b8a26
 
32dba56
 
e2b8a26
32dba56
 
e2b8a26
 
32dba56
 
e2b8a26
32dba56
e2b8a26
32dba56
 
 
 
 
 
e2b8a26
32dba56
 
 
e2b8a26
 
32dba56
 
 
 
e2b8a26
32dba56
 
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
import torch
import torch.nn.functional as F
import gradio as gr
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from bertopic import BERTopic
from sentence_transformers import SentenceTransformer
import os

# --- 1. CONFIGURATION ---
# Tránh xung đột luồng khi chạy tokenizer
os.environ["TOKENIZERS_PARALLELISM"] = "false"

ROBERTA_PATH = "roberta_base_checkpoint"
BERTOPIC_PATH = "bertopic_checkpoint"

# --- 2. INITIALIZATION (Runs once at startup) ---
print("--- System Initializing ---")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Device detected: {device.type.upper()}")

# Load RoBERTa
tokenizer = AutoTokenizer.from_pretrained(ROBERTA_PATH)
roberta_model = AutoModelForSequenceClassification.from_pretrained(ROBERTA_PATH).to(device)
roberta_model.eval()

# Load BERTopic
embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
topic_model = BERTopic.load(BERTOPIC_PATH, embedding_model=embedding_model)

print("--- All models loaded successfully! ---")

# --- 3. CORE LOGIC ---
def analyze_review(text):
    if not text.strip():
        return "Please enter some text", None, None, ""

    # --- Sentiment Analysis (RoBERTa) ---
    inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=256).to(device)
    with torch.no_grad():
        outputs = roberta_model(**inputs)
    
    probs = F.softmax(outputs.logits, dim=1)
    pred_idx = torch.argmax(probs, dim=1).item()
    
    # Matching common sentiment output formats
    sentiment_labels = ['Negative 😡', 'Neutral 😐', 'Positive 😊']
    sentiment = sentiment_labels[pred_idx]
    confidence = probs[0][pred_idx].item()

    # --- Topic Modeling (BERTopic) ---
    new_topics, _ = topic_model.transform([text])
    topic_id = int(new_topics[0])
    
    if topic_id != -1:
        keywords_data = topic_model.get_topic(topic_id)
        # Extracting top 5 keywords
        keywords_list = ", ".join([word for word, score in keywords_data[:5]])
    else:
        keywords_list = "Outlier (No specific topic detected)"

    return sentiment, f"{confidence:.2%}", topic_id, keywords_list

# --- 4. GRADIO INTERFACE (WEB & API) ---
with gr.Blocks(title="Food Review Analysis Tool") as demo:
    gr.Markdown("# 📊 Food Review Analysis System")
    gr.Markdown("An integrated pipeline using **RoBERTa** for Sentiment and **BERTopic** for Topic Extraction.")
    
    with gr.Row():
        with gr.Column():
            input_text = gr.Textbox(
                label="Review Content", 
                placeholder="e.g., The pizza was delicious but the delivery took two hours!",
                lines=5
            )
            btn = gr.Button("Analyze Now", variant="primary")
            
        with gr.Column():
            out_sentiment = gr.Label(label="Sentiment Prediction")
            out_confidence = gr.Textbox(label="Confidence Score")
            with gr.Row():
                out_topic_id = gr.Number(label="Topic ID")
                out_keywords = gr.Textbox(label="Main Keywords")

    # Event binding
    btn.click(
        fn=analyze_review, 
        inputs=input_text, 
        outputs=[out_sentiment, out_confidence, out_topic_id, out_keywords]
    )
    
    # Examples for quick testing
    gr.Examples(
        examples=[
            ["The food was amazing, very delicious!"],
            ["Service was very slow and the waiter was rude."],
            ["While the truffle-infused risotto was a masterpiece, the deafening noise made it hard to enjoy."]
        ],
        inputs=input_text
    )

# Launch the app
if __name__ == "__main__":
    demo.launch()