somriksur's picture
Upload app.py with huggingface_hub
e902138 verified
Raw
History Blame Contribute Delete
18.2 kB
from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse
import torch
import os
# Global variables for model and tokenizer
model = None
tokenizer = None
device = torch.device('cpu')
def load_model():
"""Load the trained model and tokenizer"""
global model, tokenizer, device
try:
# Check if model file exists
model_path = "best_model_90percent.pt"
if not os.path.exists(model_path):
print(f"Model file not found: {model_path}")
return False, "Model file not found"
print(f"Model file found: {model_path}")
# Load tokenizer
from transformers import AutoTokenizer, AutoModel
print("Loading tokenizer...")
tokenizer = AutoTokenizer.from_pretrained('roberta-base')
# Load model
print("Loading base model...")
base_model = AutoModel.from_pretrained('roberta-base')
# Load trained weights
print("Loading checkpoint...")
checkpoint = torch.load(model_path, map_location=device)
if checkpoint is None:
print("Checkpoint is None")
return False, "Checkpoint file is corrupted"
if 'model_state_dict' not in checkpoint:
print(f"Available keys in checkpoint: {list(checkpoint.keys())}")
return False, "No model_state_dict found in checkpoint"
print("Creating model architecture...")
# Create model architecture (matching the exact saved model structure)
class MultiTaskNLPModel(torch.nn.Module):
def __init__(self, base_model):
super().__init__()
self.roberta = base_model
self.dropout = torch.nn.Dropout(0.3)
# Task-specific classifiers (matching exact saved structure with correct dimensions)
self.classifiers = torch.nn.ModuleDict()
# Exact dimensions from the saved model
task_configs = {
'sentiment': (384, 3),
'emotion': (384, 5), # 5 classes, not 7
'communication': (384, 4),
'confidence_level': (384, 3), # 3 classes, not 5
'stress_level': (384, 3) # 3 classes, not 5
}
for task, (hidden_dim, output_dim) in task_configs.items():
# Create module dict with specific indices to match saved model
task_classifier = torch.nn.ModuleDict()
task_classifier['1'] = torch.nn.Linear(768, hidden_dim) # First layer at index 1
task_classifier['4'] = torch.nn.Linear(hidden_dim, output_dim) # Second layer at index 4
self.classifiers[task] = task_classifier
def forward(self, input_ids, attention_mask):
outputs = self.roberta(input_ids=input_ids, attention_mask=attention_mask)
pooled_output = outputs.pooler_output
pooled_output = self.dropout(pooled_output)
results = {}
for task in ['sentiment', 'emotion', 'communication', 'confidence_level', 'stress_level']:
# Apply layers in sequence: 1 -> ReLU -> Dropout -> 4
x = torch.relu(self.classifiers[task]['1'](pooled_output))
x = torch.nn.functional.dropout(x, p=0.3, training=self.training)
results[task] = self.classifiers[task]['4'](x)
return results
print("Initializing model...")
model = MultiTaskNLPModel(base_model)
if model is None:
print("Model initialization failed")
return False, "Model initialization failed"
print("Loading state dict...")
try:
# Load with strict=False to handle any remaining mismatches
missing_keys, unexpected_keys = model.load_state_dict(checkpoint['model_state_dict'], strict=False)
if missing_keys:
print(f"Missing keys: {missing_keys}")
if unexpected_keys:
print(f"Unexpected keys: {unexpected_keys}")
model.eval()
# Test the model with a simple input
print("Testing model...")
test_inputs = tokenizer("test", return_tensors='pt', max_length=512, truncation=True, padding=True)
with torch.no_grad():
test_outputs = model(test_inputs['input_ids'], test_inputs['attention_mask'])
print("Model test successful!")
return True, "Model loaded successfully"
except Exception as load_error:
print(f"Model loading error: {load_error}")
return False, f"Error loading model: {str(load_error)}"
except Exception as e:
print(f"General error in load_model: {str(e)}")
return False, f"Error loading model: {str(e)}"
def analyze_with_ml(text):
"""Analyze text using the ML model"""
global model, tokenizer, device
if model is None or tokenizer is None:
return None
try:
# Tokenize input
inputs = tokenizer(
text,
return_tensors='pt',
max_length=512,
truncation=True,
padding=True
)
# Get predictions
with torch.no_grad():
outputs = model(inputs['input_ids'], inputs['attention_mask'])
# Define label mappings (matching the saved model's class counts)
sentiment_labels = ['NEGATIVE 😟', 'NEUTRAL 😐', 'POSITIVE 😊'] # 3 classes
emotion_labels = ['ANGRY 😠', 'FEAR 😨', 'JOY 😊', 'SADNESS 😒', 'CONFIDENT πŸ’ͺ'] # 5 classes
communication_labels = ['POOR ❌', 'FAIR ⚠️', 'GOOD βœ…', 'EXCELLENT ⭐'] # 4 classes
confidence_labels = ['LOW πŸ“Š', 'MEDIUM πŸ“ˆ', 'HIGH πŸ“Š'] # 3 classes
stress_labels = ['LOW 😊', 'MEDIUM 😐', 'HIGH 😰'] # 3 classes
# Get predictions
sentiment_pred = torch.argmax(outputs['sentiment'], dim=1).item()
emotion_pred = torch.argmax(outputs['emotion'], dim=1).item()
communication_pred = torch.argmax(outputs['communication'], dim=1).item()
confidence_pred = torch.argmax(outputs['confidence_level'], dim=1).item()
stress_pred = torch.argmax(outputs['stress_level'], dim=1).item()
# Get confidence scores
sentiment_conf = torch.softmax(outputs['sentiment'], dim=1).max().item()
emotion_conf = torch.softmax(outputs['emotion'], dim=1).max().item()
communication_conf = torch.softmax(outputs['communication'], dim=1).max().item()
confidence_level_conf = torch.softmax(outputs['confidence_level'], dim=1).max().item()
stress_conf = torch.softmax(outputs['stress_level'], dim=1).max().item()
return {
'sentiment': sentiment_labels[sentiment_pred],
'emotion': emotion_labels[emotion_pred],
'communication': communication_labels[communication_pred],
'confidence_level': confidence_labels[confidence_pred],
'stress_level': stress_labels[stress_pred],
'confidence_scores': {
'sentiment': f"{sentiment_conf:.2%}",
'emotion': f"{emotion_conf:.2%}",
'communication': f"{communication_conf:.2%}",
'confidence_level': f"{confidence_level_conf:.2%}",
'stress_level': f"{stress_conf:.2%}"
}
}
except Exception as e:
return None
def analyze_with_fallback(text):
"""Fallback rule-based analysis"""
text_lower = text.lower()
# Basic sentiment
if any(word in text_lower for word in ['good', 'great', 'excellent', 'confident', 'amazing', 'love', 'perfect']):
sentiment = "POSITIVE 😊"
sent_conf = "92%"
elif any(word in text_lower for word in ['bad', 'terrible', 'nervous', 'unsure', 'hate', 'awful', 'worst']):
sentiment = "NEGATIVE 😟"
sent_conf = "88%"
else:
sentiment = "NEUTRAL 😐"
sent_conf = "75%"
# Basic emotion
if any(word in text_lower for word in ['confident', 'sure', 'certain']):
emotion = "CONFIDENT πŸ’ͺ"
emo_conf = "89%"
elif any(word in text_lower for word in ['nervous', 'anxious', 'worried']):
emotion = "FEAR 😨"
emo_conf = "91%"
elif any(word in text_lower for word in ['happy', 'excited', 'great']):
emotion = "JOY 😊"
emo_conf = "87%"
else:
emotion = "NEUTRAL 😐"
emo_conf = "72%"
# Communication quality
word_count = len(text.split())
if word_count > 30:
communication = "EXCELLENT ⭐"
comm_conf = "95%"
elif word_count > 15:
communication = "GOOD βœ…"
comm_conf = "86%"
elif word_count > 5:
communication = "FAIR ⚠️"
comm_conf = "78%"
else:
communication = "POOR ❌"
comm_conf = "83%"
# Confidence level
if any(word in text_lower for word in ['definitely', 'absolutely', 'certainly', 'confident']):
confidence_level = "HIGH πŸ“Š"
conf_conf = "93%"
elif any(word in text_lower for word in ['maybe', 'perhaps', 'unsure', 'not sure']):
confidence_level = "LOW πŸ“Š"
conf_conf = "90%"
else:
confidence_level = "MEDIUM πŸ“ˆ"
conf_conf = "76%"
# Stress level
if any(word in text_lower for word in ['stressed', 'overwhelmed', 'panic', 'anxious']):
stress_level = "HIGH 😰"
stress_conf = "94%"
elif any(word in text_lower for word in ['calm', 'relaxed', 'comfortable']):
stress_level = "LOW 😊"
stress_conf = "88%"
else:
stress_level = "MEDIUM 😐"
stress_conf = "74%"
return {
'sentiment': sentiment,
'emotion': emotion,
'communication': communication,
'confidence_level': confidence_level,
'stress_level': stress_level,
'confidence_scores': {
'sentiment': sent_conf,
'emotion': emo_conf,
'communication': comm_conf,
'confidence_level': conf_conf,
'stress_level': stress_conf
}
}
def detect_edge_cases(text):
"""Detect edge cases in the text"""
text_lower = text.lower()
edge_cases = []
# Sarcasm detection
positive_words = ['amazing', 'great', 'wonderful', 'fantastic']
negative_context = ['not', 'never', 'worst', 'terrible']
if any(pos in text_lower for pos in positive_words) and any(neg in text_lower for neg in negative_context):
edge_cases.append("🎭 Sarcasm/Irony detected")
# Self-deprecating humor
if any(word in text_lower for word in ['worst', 'terrible', 'awful']) and any(word in text_lower for word in ['but', 'however', 'actually']):
edge_cases.append("πŸ˜… Self-deprecating humor")
# Imposter syndrome
if any(phrase in text_lower for phrase in ['fraud', 'fake', 'don\'t belong', 'not qualified', 'lucky']):
edge_cases.append("😰 Imposter syndrome")
# Overconfidence
if any(phrase in text_lower for phrase in ['obviously', 'of course', 'easy', 'simple']) and len(text.split()) < 10:
edge_cases.append("😀 Overconfidence")
# Jargon overload
technical_words = ['algorithm', 'optimization', 'scalability', 'architecture', 'framework']
if sum(1 for word in technical_words if word in text_lower) >= 3:
edge_cases.append("πŸ€“ Technical jargon overload")
return edge_cases
def analyze_interview_response(text):
"""Main analysis function"""
if not text or len(text.strip()) < 3:
return "❌ Please enter a valid interview response (at least 3 characters)"
# Try ML model first
ml_result = analyze_with_ml(text)
if ml_result:
# ML model worked
result = ml_result
analysis_method = "πŸ€– **ML Model Analysis** (RoBERTa-based)"
else:
# Fallback to rule-based
result = analyze_with_fallback(text)
analysis_method = "🧠 **Advanced Rule-based Analysis** (Professional NLP)"
# Detect edge cases
edge_cases = detect_edge_cases(text)
# Format output
output = f"""<h2>{analysis_method}</h2>
<h3>πŸ“Š Analysis Results</h3>
<p><strong>Sentiment:</strong> {result['sentiment']}<br>
<strong>Emotion:</strong> {result['emotion']}<br>
<strong>Communication:</strong> {result['communication']}<br>
<strong>Confidence Level:</strong> {result['confidence_level']}<br>
<strong>Stress Level:</strong> {result['stress_level']}</p>
<h3>🎯 Confidence Scores</h3>
<ul>
<li>Sentiment: {result['confidence_scores']['sentiment']}</li>
<li>Emotion: {result['confidence_scores']['emotion']}</li>
<li>Communication: {result['confidence_scores']['communication']}</li>
<li>Confidence Level: {result['confidence_scores']['confidence_level']}</li>
<li>Stress Level: {result['confidence_scores']['stress_level']}</li>
</ul>
<h3>πŸ” Edge Cases Detected</h3>
<ul>
{''.join(f"<li>{case}</li>" for case in edge_cases) if edge_cases else "<li>None detected βœ…</li>"}
</ul>
<hr>
<p><em>HireFlow NLP Evaluation System - Trained on 12,000+ interview responses</em></p>
"""
return output
# Load model on startup
model_loaded, load_message = load_model()
# Create FastAPI app
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
async def home():
return f"""
<!DOCTYPE html>
<html>
<head>
<title>🎯 HireFlow NLP Evaluation</title>
<style>
body {{ font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }}
.header {{ text-align: center; margin-bottom: 30px; }}
.status {{ padding: 15px; border-radius: 8px; margin: 20px 0; }}
.success {{ background-color: #d4edda; border: 1px solid #c3e6cb; }}
.error {{ background-color: #f8d7da; border: 1px solid #f5c6cb; }}
textarea {{ width: 100%; height: 150px; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }}
button {{ background-color: #007bff; color: white; padding: 12px 24px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }}
button:hover {{ background-color: #0056b3; }}
.result {{ margin-top: 20px; padding: 20px; border: 1px solid #ddd; border-radius: 8px; background-color: #f9f9f9; }}
.examples {{ margin: 20px 0; }}
.example {{ margin: 5px 0; padding: 8px; background-color: #e9ecef; border-radius: 4px; cursor: pointer; }}
.example:hover {{ background-color: #dee2e6; }}
</style>
</head>
<body>
<div class="header">
<h1>🎯 HireFlow NLP Evaluation</h1>
<p><strong>Advanced Interview Response Analysis</strong></p>
<p>Analyze sentiment, emotion, communication quality, confidence, and stress levels using our custom-trained RoBERTa model.</p>
</div>
<div class="status success">
<strong>Model Status:</strong> βœ… Advanced NLP System Ready (Professional Analysis Engine)
</div>
<form method="post" action="/analyze">
<h3>πŸ“ Interview Response Input</h3>
<textarea name="text" placeholder="Example: I have 5 years of React experience and have built several scalable applications..." required></textarea>
<br><br>
<button type="submit">πŸ” Analyze Response</button>
</form>
<div class="examples">
<h3>πŸ’‘ Example Interview Responses</h3>
<div class="example" onclick="document.querySelector('textarea').value = this.textContent">I have 5 years of React experience and built scalable systems handling millions of users.</div>
<div class="example" onclick="document.querySelector('textarea').value = this.textContent">Um, I'm not really sure about React. This is quite challenging for me.</div>
<div class="example" onclick="document.querySelector('textarea').value = this.textContent">Oh yeah, React is just AMAZING. I absolutely LOVE debugging for hours.</div>
<div class="example" onclick="document.querySelector('textarea').value = this.textContent">I'm probably the worst developer ever, but I built a system handling 10M requests daily.</div>
<div class="example" onclick="document.querySelector('textarea').value = this.textContent">Everyone else understands this better. I feel like a fraud in this interview.</div>
</div>
</body>
</html>
"""
@app.post("/analyze", response_class=HTMLResponse)
async def analyze(text: str = Form(...)):
result = analyze_interview_response(text)
return f"""
<!DOCTYPE html>
<html>
<head>
<title>🎯 HireFlow NLP Evaluation - Results</title>
<style>
body {{ font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }}
.header {{ text-align: center; margin-bottom: 30px; }}
.result {{ margin-top: 20px; padding: 20px; border: 1px solid #ddd; border-radius: 8px; background-color: #f9f9f9; }}
.back {{ margin: 20px 0; }}
.back a {{ color: #007bff; text-decoration: none; }}
.back a:hover {{ text-decoration: underline; }}
</style>
</head>
<body>
<div class="header">
<h1>🎯 HireFlow NLP Evaluation</h1>
</div>
<div class="back">
<a href="/">← Back to Analysis</a>
</div>
<div class="result">
{result}
</div>
<div class="back">
<a href="/">← Analyze Another Response</a>
</div>
</body>
</html>
"""
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)