Update app.py
Browse files
app.py
CHANGED
@@ -62,19 +62,24 @@ RHETORICAL_DEVICES = {
|
|
62 |
|
63 |
class SpeechAnalyzer:
|
64 |
def __init__(self):
|
65 |
-
|
66 |
self.moral_model_path = "MMADS/MoralFoundationsClassifier"
|
67 |
self.moral_tokenizer = RobertaTokenizer.from_pretrained(self.moral_model_path)
|
68 |
self.moral_model = RobertaForSequenceClassification.from_pretrained(self.moral_model_path)
|
69 |
|
70 |
-
|
71 |
self.label_names = ['care', 'fairness', 'loyalty', 'authority', 'sanctity']
|
72 |
-
|
73 |
# Other pipelines remain the same
|
74 |
self.sentiment_pipeline = pipeline("sentiment-analysis")
|
75 |
self.ner_tokenizer = AutoTokenizer.from_pretrained("dslim/bert-base-NER")
|
76 |
self.ner_model = AutoModelForTokenClassification.from_pretrained("dslim/bert-base-NER")
|
77 |
self.ner_pipeline = pipeline("ner", model=self.ner_model, tokenizer=self.ner_tokenizer)
|
|
|
|
|
|
|
|
|
|
|
78 |
|
79 |
def split_text(self, text, max_length=256, overlap=50):
|
80 |
"""Split long text into overlapping segments"""
|
@@ -335,68 +340,187 @@ def main():
|
|
335 |
st.write(f"**{MORAL_FOUNDATIONS[foundation]}**: {score:.2%}")
|
336 |
|
337 |
with tab2:
|
|
|
|
|
338 |
st.subheader("Speech Trajectory Analysis")
|
339 |
-
col1, col2, col3 = st.columns(3)
|
340 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
341 |
segments = analyzer.split_text(text, max_length=512)
|
342 |
num_segments = len(segments)
|
343 |
segment_labels = [f"{i+1}" for i in range(num_segments)]
|
344 |
|
345 |
-
|
346 |
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
358 |
)
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
tickvals=[0, 0.25, 0.5, 0.75, 1],
|
368 |
-
range=[0, 1],
|
369 |
-
gridcolor='lightgray'
|
370 |
-
),
|
371 |
-
plot_bgcolor='white'
|
372 |
-
)
|
373 |
-
st.plotly_chart(sentiment_fig)
|
374 |
-
|
375 |
-
# Rest of the code remains the same for col2 (Moral Foundations)
|
376 |
|
377 |
-
|
378 |
-
|
379 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
380 |
'Segment': segment_labels,
|
381 |
-
'
|
|
|
382 |
})
|
383 |
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
y='Emotion',
|
388 |
-
color='Emotion',
|
389 |
-
title='Basic Emotions Flow',
|
390 |
-
category_orders={'Emotion': ['joy', 'sadness', 'anger', 'fear', 'surprise', 'neutral']}
|
391 |
-
)
|
392 |
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
|
|
398 |
)
|
399 |
-
st.plotly_chart(emotions_fig)
|
400 |
|
401 |
with tab3:
|
402 |
status_text.text('Analyzing Linguistic Features...')
|
|
|
62 |
|
63 |
class SpeechAnalyzer:
|
64 |
def __init__(self):
|
65 |
+
# Load MoralFoundations model
|
66 |
self.moral_model_path = "MMADS/MoralFoundationsClassifier"
|
67 |
self.moral_tokenizer = RobertaTokenizer.from_pretrained(self.moral_model_path)
|
68 |
self.moral_model = RobertaForSequenceClassification.from_pretrained(self.moral_model_path)
|
69 |
|
70 |
+
# Define label names directly
|
71 |
self.label_names = ['care', 'fairness', 'loyalty', 'authority', 'sanctity']
|
72 |
+
|
73 |
# Other pipelines remain the same
|
74 |
self.sentiment_pipeline = pipeline("sentiment-analysis")
|
75 |
self.ner_tokenizer = AutoTokenizer.from_pretrained("dslim/bert-base-NER")
|
76 |
self.ner_model = AutoModelForTokenClassification.from_pretrained("dslim/bert-base-NER")
|
77 |
self.ner_pipeline = pipeline("ner", model=self.ner_model, tokenizer=self.ner_tokenizer)
|
78 |
+
|
79 |
+
# Add emotion classifier
|
80 |
+
self.emotion_classifier = pipeline("text-classification",
|
81 |
+
model="j-hartmann/emotion-english-distilroberta-base")
|
82 |
+
|
83 |
|
84 |
def split_text(self, text, max_length=256, overlap=50):
|
85 |
"""Split long text into overlapping segments"""
|
|
|
340 |
st.write(f"**{MORAL_FOUNDATIONS[foundation]}**: {score:.2%}")
|
341 |
|
342 |
with tab2:
|
343 |
+
status_text.text('Processing Emotional Trajectory...')
|
344 |
+
progress_bar.progress(40)
|
345 |
st.subheader("Speech Trajectory Analysis")
|
|
|
346 |
|
347 |
+
compare_mode = st.toggle("Enable Side-by-Side Comparison", value=False)
|
348 |
+
|
349 |
+
viz_options = st.multiselect(
|
350 |
+
"Select visualizations to display:",
|
351 |
+
["Sentiment Flow", "Moral Foundations Flow", "Basic Emotions Flow"],
|
352 |
+
default=["Sentiment Flow"]
|
353 |
+
)
|
354 |
+
|
355 |
+
if len(viz_options) > 1 and compare_mode:
|
356 |
+
cols = st.columns(len(viz_options))
|
357 |
+
else:
|
358 |
+
cols = [st] # Single column mode
|
359 |
+
|
360 |
+
# Create consistent segments for analyses
|
361 |
segments = analyzer.split_text(text, max_length=512)
|
362 |
num_segments = len(segments)
|
363 |
segment_labels = [f"{i+1}" for i in range(num_segments)]
|
364 |
|
365 |
+
current_col = 0
|
366 |
|
367 |
+
if "Sentiment Flow" in viz_options:
|
368 |
+
with cols[current_col]:
|
369 |
+
sentiment_scores = analyzer.analyze_emotional_trajectory(text)
|
370 |
+
|
371 |
+
trajectory_fig = go.Figure(data=go.Scatter(
|
372 |
+
x=segment_labels,
|
373 |
+
y=sentiment_scores,
|
374 |
+
mode='lines+markers',
|
375 |
+
line=dict(color='#1f77b4', width=3),
|
376 |
+
marker=dict(
|
377 |
+
size=8,
|
378 |
+
color=['#ff4444' if score < -0.3 else '#44ff44' if score > 0.3 else '#888888' for score in sentiment_scores],
|
379 |
+
symbol='circle'
|
380 |
+
)
|
381 |
+
))
|
382 |
+
|
383 |
+
trajectory_fig.update_layout(
|
384 |
+
title='Emotional Flow Throughout the Speech',
|
385 |
+
xaxis_title='Speech Segments',
|
386 |
+
yaxis_title='Emotional Tone',
|
387 |
+
yaxis=dict(
|
388 |
+
ticktext=['Very Negative', 'Negative', 'Neutral', 'Positive', 'Very Positive'],
|
389 |
+
tickvals=[-1, -0.5, 0, 0.5, 1],
|
390 |
+
range=[-1.1, 1.1],
|
391 |
+
gridcolor='lightgray'
|
392 |
+
),
|
393 |
+
hovermode='x unified',
|
394 |
+
showlegend=False,
|
395 |
+
plot_bgcolor='white',
|
396 |
+
height=500
|
397 |
)
|
398 |
+
|
399 |
+
trajectory_fig.update_traces(
|
400 |
+
hovertemplate="Segment: %{x}<br>Score: %{y:.2f}<extra></extra>"
|
401 |
+
)
|
402 |
+
|
403 |
+
st.plotly_chart(trajectory_fig, use_container_width=True)
|
404 |
+
if compare_mode:
|
405 |
+
current_col += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
406 |
|
407 |
+
if "Moral Foundations Flow" in viz_options:
|
408 |
+
with cols[current_col]:
|
409 |
+
moral_trajectories = {
|
410 |
+
'care': [], 'fairness': [], 'loyalty': [],
|
411 |
+
'authority': [], 'sanctity': []
|
412 |
+
}
|
413 |
+
|
414 |
+
for segment in segments:
|
415 |
+
moral_scores = analyzer.analyze_moral_foundations(segment)
|
416 |
+
for foundation in moral_trajectories.keys():
|
417 |
+
moral_trajectories[foundation].append(moral_scores[foundation])
|
418 |
+
|
419 |
+
moral_fig = go.Figure()
|
420 |
+
colors = px.colors.qualitative.Set3[:5]
|
421 |
+
|
422 |
+
for idx, (foundation, scores) in enumerate(moral_trajectories.items()):
|
423 |
+
moral_fig.add_trace(go.Scatter(
|
424 |
+
x=segment_labels,
|
425 |
+
y=scores,
|
426 |
+
name=MORAL_FOUNDATIONS[foundation],
|
427 |
+
mode='lines+markers',
|
428 |
+
line=dict(color=colors[idx], width=2),
|
429 |
+
marker=dict(size=6, color=colors[idx])
|
430 |
+
))
|
431 |
+
|
432 |
+
moral_fig.update_layout(
|
433 |
+
title='Moral Foundations Flow',
|
434 |
+
xaxis_title='Speech Segments',
|
435 |
+
yaxis_title='Foundation Strength',
|
436 |
+
yaxis=dict(range=[0, 1]),
|
437 |
+
hovermode='x unified',
|
438 |
+
plot_bgcolor='white',
|
439 |
+
showlegend=True,
|
440 |
+
height=500,
|
441 |
+
legend=dict(
|
442 |
+
orientation="h",
|
443 |
+
yanchor="bottom",
|
444 |
+
y=1.02,
|
445 |
+
xanchor="right",
|
446 |
+
x=1
|
447 |
+
)
|
448 |
+
)
|
449 |
+
|
450 |
+
moral_fig.update_traces(
|
451 |
+
hovertemplate="Segment: %{x}<br>Strength: %{y:.2f}<extra></extra>"
|
452 |
+
)
|
453 |
+
|
454 |
+
st.plotly_chart(moral_fig, use_container_width=True)
|
455 |
+
if compare_mode:
|
456 |
+
current_col += 1
|
457 |
+
|
458 |
+
if "Basic Emotions Flow" in viz_options:
|
459 |
+
with cols[current_col]:
|
460 |
+
emotions = []
|
461 |
+
for segment in segments:
|
462 |
+
try:
|
463 |
+
result = analyzer.emotion_classifier(segment[:512])[0]
|
464 |
+
emotions.append(result['label'])
|
465 |
+
except:
|
466 |
+
emotions.append('neutral')
|
467 |
+
|
468 |
+
emotions_df = pd.DataFrame({
|
469 |
+
'Segment': segment_labels,
|
470 |
+
'Emotion': emotions
|
471 |
+
})
|
472 |
+
|
473 |
+
emotions_fig = px.bar(
|
474 |
+
emotions_df,
|
475 |
+
x='Segment',
|
476 |
+
y='Emotion',
|
477 |
+
color='Emotion',
|
478 |
+
title='Basic Emotions Flow',
|
479 |
+
color_discrete_sequence=px.colors.qualitative.Set2
|
480 |
+
)
|
481 |
+
|
482 |
+
emotions_fig.update_layout(
|
483 |
+
xaxis_title='Speech Segments',
|
484 |
+
yaxis_title='Emotion',
|
485 |
+
showlegend=True,
|
486 |
+
plot_bgcolor='white',
|
487 |
+
height=500,
|
488 |
+
legend=dict(
|
489 |
+
orientation="h",
|
490 |
+
yanchor="bottom",
|
491 |
+
y=1.02,
|
492 |
+
xanchor="right",
|
493 |
+
x=1
|
494 |
+
)
|
495 |
+
)
|
496 |
+
|
497 |
+
emotions_fig.update_traces(
|
498 |
+
hovertemplate="Segment: %{x}<br>Emotion: %{y}<extra></extra>"
|
499 |
+
)
|
500 |
+
|
501 |
+
st.plotly_chart(emotions_fig, use_container_width=True)
|
502 |
+
if compare_mode:
|
503 |
+
current_col += 1
|
504 |
+
|
505 |
+
# Add download button
|
506 |
+
if viz_options:
|
507 |
+
results_df = pd.DataFrame({
|
508 |
'Segment': segment_labels,
|
509 |
+
'Sentiment': sentiment_scores if "Sentiment Flow" in viz_options else None,
|
510 |
+
'Emotion': emotions if "Basic Emotions Flow" in viz_options else None
|
511 |
})
|
512 |
|
513 |
+
if "Moral Foundations Flow" in viz_options:
|
514 |
+
for foundation, scores in moral_trajectories.items():
|
515 |
+
results_df[f'Moral_{foundation}'] = scores
|
|
|
|
|
|
|
|
|
|
|
516 |
|
517 |
+
csv = results_df.to_csv(index=False)
|
518 |
+
st.download_button(
|
519 |
+
label="Download Analysis Results",
|
520 |
+
data=csv,
|
521 |
+
file_name="speech_analysis_results.csv",
|
522 |
+
mime="text/csv"
|
523 |
)
|
|
|
524 |
|
525 |
with tab3:
|
526 |
status_text.text('Analyzing Linguistic Features...')
|