|
import gradio as gr |
|
import pickle as pk |
|
import pandas as pd |
|
from sklearn.feature_extraction.text import TfidfVectorizer |
|
import os |
|
|
|
|
|
current_dir = os.path.dirname(os.path.abspath(__file__)) |
|
model_path = os.path.join(current_dir, 'model.pkl') |
|
scaler_path = os.path.join(current_dir, 'scaler.pkl') |
|
|
|
def load_model_and_vectorizer(): |
|
try: |
|
if not os.path.exists(model_path) or not os.path.exists(scaler_path): |
|
return None, None, f"Error: Model files not found at:\n{model_path}\n{scaler_path}" |
|
|
|
model = pk.load(open(model_path, 'rb')) |
|
vectorizer = pk.load(open(scaler_path, 'rb')) |
|
return model, vectorizer, None |
|
except Exception as e: |
|
return None, None, f"Error loading model: {str(e)}" |
|
|
|
|
|
model, vectorizer, error = load_model_and_vectorizer() |
|
|
|
def predict_sentiment(review): |
|
if error: |
|
return error |
|
|
|
if not review: |
|
return "Please enter a review first!" |
|
|
|
try: |
|
|
|
review_vector = vectorizer.transform([review]) |
|
|
|
result = model.predict(review_vector)[0] |
|
|
|
if result == 0: |
|
return "Negative Review 😞" |
|
else: |
|
return "Positive Review 😊" |
|
except Exception as e: |
|
return f"Error during prediction: {str(e)}" |
|
|
|
|
|
css = """ |
|
body { |
|
background: linear-gradient(180deg, #2c3e50 0%, #3498db 100%); |
|
margin: 0; |
|
font-family: 'Roboto', -apple-system, BlinkMacSystemFont, sans-serif; |
|
color: #ecf0f1; |
|
} |
|
|
|
.gradio-container { |
|
max-width: 800px; |
|
margin: 3rem auto; |
|
background: rgba(44, 62, 80, 0.9); |
|
border-radius: 20px; |
|
padding: 2.5rem; |
|
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3); |
|
backdrop-filter: blur(10px); |
|
transition: transform 0.3s ease; |
|
} |
|
|
|
.gradio-container:hover { |
|
transform: translateY(-5px); |
|
} |
|
|
|
.header { |
|
text-align: center; |
|
margin-bottom: 2rem; |
|
animation: fadeIn 1s ease-in-out; |
|
} |
|
|
|
.header h1 { |
|
font-size: 2.4rem; |
|
background: linear-gradient(45deg, #e74c3c, #f1c40f); |
|
-webkit-background-clip: text; |
|
-webkit-text-fill-color: transparent; |
|
margin: 0; |
|
font-weight: 700; |
|
} |
|
|
|
.header p { |
|
font-size: 1.2rem; |
|
color: #bdc3c7; |
|
margin-top: 0.5rem; |
|
font-weight: 300; |
|
} |
|
|
|
.review-input { |
|
background: rgba(255, 255, 255, 0.1); |
|
border: 2px solid #4ECDC4; |
|
border-radius: 12px; |
|
padding: 1.5rem; |
|
font-size: 1.1rem; |
|
color: #ecf0f1; |
|
width: 100%; |
|
box-sizing: border-box; |
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.review-input:focus { |
|
border-color: #f1c40f; |
|
box-shadow: 0 6px 20px rgba(241, 196, 15, 0.3); |
|
outline: none; |
|
transform: scale(1.01); |
|
} |
|
|
|
.review-input::placeholder { |
|
color: #95a5a6; |
|
font-style: italic; |
|
} |
|
|
|
.submit-btn { |
|
background: linear-gradient(45deg, #FF6B6B, #4ECDC4); |
|
color: white; |
|
border: none; |
|
padding: 1rem 3rem; |
|
border-radius: 50px; |
|
font-size: 1.1rem; |
|
font-weight: 600; |
|
cursor: pointer; |
|
margin: 1.5rem auto; |
|
display: block; |
|
transition: all 0.3s ease; |
|
box-shadow: 0 6px 20px rgba(78, 205, 196, 0.4); |
|
} |
|
|
|
.submit-btn:hover { |
|
background: linear-gradient(45deg, #4ECDC4, #FF6B6B); |
|
transform: translateY(-3px) scale(1.05); |
|
box-shadow: 0 10px 25px rgba(78, 205, 196, 0.5); |
|
} |
|
|
|
.submit-btn:active { |
|
transform: translateY(0); |
|
box-shadow: 0 4px 15px rgba(78, 205, 196, 0.3); |
|
} |
|
|
|
.output-container { |
|
margin-top: 2rem; |
|
padding: 1.5rem; |
|
background: rgba(255, 255, 255, 0.05); |
|
border-radius: 15px; |
|
text-align: center; |
|
min-height: 80px; |
|
font-size: 1.3rem; |
|
color: #f1c40f; |
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); |
|
animation: slideUp 0.5s ease-out; |
|
} |
|
|
|
.error-message { |
|
background: rgba(231, 76, 60, 0.2); |
|
color: #e74c3c; |
|
padding: 1.2rem; |
|
border-radius: 12px; |
|
margin-bottom: 1.5rem; |
|
border-left: 5px solid #e74c3c; |
|
font-weight: 500; |
|
animation: shake 0.4s ease-in-out; |
|
} |
|
|
|
@keyframes fadeIn { |
|
from { opacity: 0; transform: translateY(-10px); } |
|
to { opacity: 1; transform: translateY(0); } |
|
} |
|
|
|
@keyframes slideUp { |
|
from { opacity: 0; transform: translateY(20px); } |
|
to { opacity: 1; transform: translateY(0); } |
|
} |
|
|
|
@keyframes shake { |
|
0%, 100% { transform: translateX(0); } |
|
20%, 60% { transform: translateX(-8px); } |
|
40%, 80% { transform: translateX(8px); } |
|
} |
|
|
|
@media (max-width: 600px) { |
|
.gradio-container { |
|
margin: 1rem; |
|
padding: 1.5rem; |
|
} |
|
|
|
.header h1 { |
|
font-size: 2rem; |
|
} |
|
|
|
.review-input { |
|
padding: 1rem; |
|
} |
|
|
|
.submit-btn { |
|
padding: 0.8rem 2rem; |
|
} |
|
} |
|
""" |
|
|
|
|
|
with gr.Blocks( |
|
title="Sentiment Analysis on Traveler Reviews", |
|
theme=gr.themes.Default( |
|
primary_hue="teal", |
|
secondary_hue="pink", |
|
neutral_hue="gray", |
|
radius_size="lg", |
|
), |
|
css=css |
|
) as demo: |
|
gr.Markdown(""" |
|
<div class="header"> |
|
<h1> Sentiment Analysis on Traveler Reviews</h1> |
|
<p>Type your review and let our AI uncover its vibe!</p> |
|
</div> |
|
""") |
|
|
|
if error: |
|
gr.Markdown(f""" |
|
<div class="error-message"> |
|
⚠️ {error} |
|
</div> |
|
""") |
|
|
|
review_input = gr.Textbox( |
|
placeholder="Share your thoughts about the your experience...", |
|
lines=5, |
|
elem_classes="review-input", |
|
show_label=False |
|
) |
|
|
|
predict_btn = gr.Button( |
|
"Analyze Sentiment", |
|
elem_classes="submit-btn" |
|
) |
|
|
|
output = gr.Textbox( |
|
placeholder="The sentiment will appear here...", |
|
elem_classes="output-container", |
|
show_label=False, |
|
interactive=False |
|
) |
|
|
|
predict_btn.click( |
|
fn=predict_sentiment, |
|
inputs=review_input, |
|
outputs=output |
|
) |
|
|
|
if __name__ == "__main__": |
|
demo.launch(share=True) |