File size: 5,695 Bytes
1dffecd
 
57b215d
 
 
 
 
1dffecd
57b215d
 
 
 
 
 
 
 
1dffecd
57b215d
 
 
 
 
 
1dffecd
 
 
 
 
 
 
 
57b215d
1dffecd
57b215d
 
1dffecd
57b215d
 
 
 
1dffecd
 
57b215d
 
 
 
 
 
 
 
1dffecd
 
57b215d
 
 
 
1dffecd
57b215d
 
 
1dffecd
57b215d
 
1dffecd
57b215d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1dffecd
57b215d
 
 
 
 
 
60be2ab
57b215d
1dffecd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57b215d
 
1dffecd
57b215d
1dffecd
 
 
57b215d
 
1dffecd
 
 
57b215d
 
1dffecd
57b215d
 
 
 
1dffecd
57b215d
1dffecd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a2eee4
1dffecd
 
 
 
 
 
 
 
 
 
 
 
bd862d2
 
 
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
176
177
# prediction_compile.py
# Import Libraries
import streamlit as st
import re
import pickle
import joblib
import nltk
import os
import numpy as np
import pandas as pd
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow import keras
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem import PorterStemmer

# --- Setup NLTK ---
nltk_data_path = os.path.join("/tmp", "nltk_data")
os.makedirs(nltk_data_path, exist_ok=True)
nltk.data.path.append(nltk_data_path)
nltk.download("stopwords", download_dir=nltk_data_path)
nltk.download("punkt", download_dir=nltk_data_path)

# --- Loading Info ---
st.markdown(
    '<p style="color:gray; font-size:14px; font-style:italic;">'
    'Loading models and resources from local storage... '
    'Please be patient and DO NOT refresh the page :)'
    '</p>',
    unsafe_allow_html=True
)

# --- Cached Loading Functions ---
@st.cache_resource
def load_sentiment_model():
    path = "./src/best_model.keras"
    return keras.models.load_model(path)

@st.cache_resource
def load_tokenizer_params():
    tokenizer_path = "./src/tokenizer.pkl"
    params_path = "./src/params.pkl"
    with open(tokenizer_path, "rb") as f:
        tokenizer = pickle.load(f)
    with open(params_path, "rb") as f:
        params = pickle.load(f)
    return tokenizer, params

@st.cache_resource
def load_topic_models():
    neg_path = "./src/fastopic_negative_model.pkl"
    pos_path = "./src/fastopic_positive_model.pkl"
    neg_model = joblib.load(neg_path)
    pos_model = joblib.load(pos_path)
    return neg_model, pos_model

# --- Load all resources once ---
sentiment_model = load_sentiment_model()
tokenizer, params = load_tokenizer_params()
topic_model_neg, topic_model_pos = load_topic_models()

max_len = params["max_len"]

# --- Preprocessing Function ---
negations = {"not", "no", "never"}
stpwrds_en = set(stopwords.words("english")) - negations
stemmer = PorterStemmer()

replacements = {
    "sia": "sq",
    "flown": "fly",
    "flew": "fly",
    "alway": "always",
    "boarding": "board",
    "told": "tell",
    "said": "say",
    "booked": "book",
    "paid": "pay",
    "well": "good",
    "aircraft": "plane"
}

def text_preprocessing(text):
    text = text.lower()
    text = re.sub(r"\\n", " ", text)
    text = text.strip()
    text = re.sub(r'[^a-z0-9\s]', ' ', text)
    tokens = word_tokenize(text)
    tokens = [replacements.get(word, word) for word in tokens]
    tokens = [word for word in tokens if word not in stpwrds_en]
    tokens = [stemmer.stem(word) for word in tokens]
    return "emptytext" if len(tokens) == 0 else ' '.join(tokens)

# --- Topic Labels ---
topic_labels_neg = {
    1: "meal and entertainment service",
    2: "refund, cancellation, and booking tickets policy",
    3: "business class/premium facility",
    4: "baggage limits and price",
    5: "hidden charges"
}

topic_labels_pos = {
    1: "good food and crew service",
    2: "excellent economy seat",
    3: "refund and cancellation policy",
    4: "meals quality",
    5: "accommodation and assistance"
}

# --- Streamlit App ---
def run():
    st.subheader("Sentiment & Topic Prediction for SQ Customer Reviews")

    st.markdown(
    """

    Enter a customer review below to predict sentiment and topic.

    """
    )

    with st.form(key='SQ-sentiment-analysis'):
        text = st.text_input('Customer Review', value='--customer review--')
        submitted = st.form_submit_button('Predict')

    if submitted:
        # Preprocess
        processed = text_preprocessing(text)
        seq = tokenizer.texts_to_sequences([processed])
        padded = pad_sequences(seq, maxlen=max_len, padding="post", truncating="post")

        # Sentiment Prediction
        pred_probs = sentiment_model.predict(padded)
        if pred_probs.shape[1] == 1:
            # Binary sigmoid
            p_pos = float(pred_probs[0][0])
            p_neg = 1 - p_pos
            sentiment_label = "Positive" if p_pos >= 0.5 else "Negative"
            confidence = max(p_pos, p_neg)
        else:
            # Softmax
            pred_class = np.argmax(pred_probs, axis=1)[0]
            label_map = {0: "Negative", 1: "Positive"}
            sentiment_label = label_map[pred_class]
            confidence = float(pred_probs[0][pred_class])

        color = "green" if sentiment_label == "Positive" else "red"
        st.markdown(
            f"<p style='font-size:22px; font-weight:bold; color:{color};'>"
            f"Predicted Sentiment: {sentiment_label} "
            f"(Confidence: {confidence:.2f})</p>",
            unsafe_allow_html=True
        )

        # Topic Prediction
        st.write("### Topic Modeling")
        if sentiment_label == "Negative":
            probs = topic_model_neg.transform([text])[0]
            topic_id = int(np.argmax(probs)) + 1
            topic_name = topic_labels_neg.get(topic_id, "Unknown Topic")
            st.write("**Using Negative Model**")
        else:
            probs = topic_model_pos.transform([text])[0]
            topic_id = int(np.argmax(probs)) + 1
            topic_name = topic_labels_pos.get(topic_id, "Unknown Topic")
            st.write("**Using Positive Model**")

        # Output
        st.markdown(
            f"<p style='font-size:20px; font-weight:bold; color:{color};'>"
            f"Topic {topic_id}: {topic_name}</p>",
            unsafe_allow_html=True
        )
        st.write("**Probabilities:**", probs.tolist())

if __name__ == "__main__":
    run()