import gradio as gr import torch from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, pipeline ) import pandas as pd import numpy as np from datetime import datetime import warnings warnings.filterwarnings('ignore') # Constants MODEL_PATH = "./imdb_sentiment_final" TITLE = "đŦ IMDB Sentiment Classifier" DESCRIPTION = """ A binary sentiment classifier fine-tuned on IMDB movie reviews using LoRA. The model classifies text as **Positive** or **Negative** sentiment. """ # Device configuration device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"Using device: {device}") # Load model and tokenizer @gr.Cache() def load_model(): """Load the model and tokenizer with caching""" print("Loading model...") try: tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH) model = AutoModelForSequenceClassification.from_pretrained(MODEL_PATH) model.to(device) model.eval() print("Model loaded successfully!") return model, tokenizer except Exception as e: print(f"Error loading model: {e}") # Fallback to a default model from transformers import DistilBertForSequenceClassification model = DistilBertForSequenceClassification.from_pretrained("distilbert-base-uncased", num_labels=2) tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased") model.to(device) model.eval() return model, tokenizer # Initialize model model, tokenizer = load_model() # Create inference pipeline sentiment_pipeline = pipeline( "text-classification", model=model, tokenizer=tokenizer, device=0 if torch.cuda.is_available() else -1 ) def predict_sentiment(text, batch_size=1): """ Predict sentiment for single or multiple texts """ if not text or not text.strip(): return {"error": "Please enter some text"} # Single text if isinstance(text, str): try: result = sentiment_pipeline(text)[0] confidence = result['score'] label = "Positive" if result['label'] == "LABEL_1" else "Negative" return { "text": text[:200] + "..." if len(text) > 200 else text, "sentiment": label, "confidence": f"{confidence:.2%}", "icon": "đ" if label == "Positive" else "đ", "color": "green" if label == "Positive" else "red" } except Exception as e: return {"error": f"Prediction error: {str(e)}"} # Batch processing (if text is a list) else: results = [] for t in text: if t.strip(): try: result = sentiment_pipeline(t)[0] confidence = result['score'] label = "Positive" if result['label'] == "LABEL_1" else "Negative" results.append({ "text": t[:100] + "..." if len(t) > 100 else t, "sentiment": label, "confidence": f"{confidence:.2%}", "icon": "đ" if label == "Positive" else "đ" }) except: results.append({"text": t[:100], "sentiment": "Error", "confidence": "N/A"}) return {"batch_results": results} def batch_predict(input_text): """ Handle batch input from text area """ texts = [t.strip() for t in input_text.split('\n') if t.strip()] if not texts: return {"error": "No valid text provided"} if len(texts) == 1: return predict_sentiment(texts[0]) else: return predict_sentiment(texts) def analyze_file(file): """ Analyze sentiment from uploaded text file """ if file is None: return {"error": "No file uploaded"} try: # Read file if file.name.endswith('.txt'): with open(file.name, 'r', encoding='utf-8') as f: content = f.read() elif file.name.endswith('.csv'): df = pd.read_csv(file.name) if 'review' in df.columns: content = "\n".join(df['review'].astype(str).tolist()) else: content = "\n".join(df.iloc[:, 0].astype(str).tolist()) else: return {"error": "Unsupported file format. Use .txt or .csv"} texts = [t.strip() for t in content.split('\n') if t.strip()] if len(texts) > 100: # Limit for demo texts = texts[:100] results = [] for text in texts: result = predict_sentiment(text) if "error" not in result: results.append({ "Text": text[:50] + "..." if len(text) > 50 else text, "Sentiment": result['sentiment'], "Confidence": result['confidence'] }) if results: df_results = pd.DataFrame(results) summary = df_results['Sentiment'].value_counts() return { "results_table": df_results, "summary": f"Positive: {summary.get('Positive', 0)}, Negative: {summary.get('Negative', 0)}", "total_reviews": len(results) } else: return {"error": "No valid text found in file"} except Exception as e: return {"error": f"File processing error: {str(e)}"} # Example reviews for demo EXAMPLE_REVIEWS = [ "This movie was absolutely fantastic! The acting was superb and the storyline kept me engaged throughout.", "Terrible film. I regret wasting two hours of my life watching this boring nonsense.", "The cinematography was beautiful but the plot was weak and predictable.", "An emotional rollercoaster with brilliant performances from the entire cast.", "Not bad, but nothing special either. Average entertainment for a lazy Sunday." ] # Custom CSS for better styling custom_css = """ .gradio-container { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .sentiment-positive { color: green; font-weight: bold; } .sentiment-negative { color: red; font-weight: bold; } .result-box { padding: 20px; border-radius: 10px; margin: 10px 0; background: #f5f5f5; } """ # Create Gradio interface with gr.Blocks(css=custom_css, title=TITLE) as demo: gr.HTML(f"""
Fine-tuned on 50K movie reviews using LoRA with DistilBERT (⤠110M params)
Model Type: Binary Sentiment Classification (Positive/Negative)