ravel / src /visualization.py
minhy112's picture
Upload RAVEL revision project without data or checkpoints
ea8bfa1 verified
Raw
History Blame Contribute Delete
11.1 kB
"""
CLARA Visualization Module
Functions for visualizing results, confusion matrices, and attention weights.
"""
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import torch
from sklearn.metrics import confusion_matrix
from typing import List, Dict, Optional
import os
# Set style
plt.style.use('seaborn-v0_8-darkgrid')
sns.set_palette("husl")
def plot_confusion_matrix(
y_true: np.ndarray,
y_pred: np.ndarray,
class_names: List[str],
save_path: Optional[str] = None,
figsize: tuple = (8, 6),
cmap: str = 'Blues'
):
"""
Plot confusion matrix
Args:
y_true: True labels
y_pred: Predicted labels
class_names: List of class names
save_path: Path to save figure
figsize: Figure size
cmap: Colormap
"""
cm = confusion_matrix(y_true, y_pred)
cm_normalized = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
fig, ax = plt.subplots(figsize=figsize)
sns.heatmap(
cm_normalized,
annot=True,
fmt='.2%',
cmap=cmap,
xticklabels=class_names,
yticklabels=class_names,
ax=ax,
cbar_kws={'label': 'Normalized Frequency'}
)
ax.set_xlabel('Predicted Label', fontsize=12)
ax.set_ylabel('True Label', fontsize=12)
ax.set_title('Confusion Matrix', fontsize=14, fontweight='bold')
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"✅ Confusion matrix saved to: {save_path}")
plt.show()
def plot_training_curves(
history: Dict[str, List[float]],
save_path: Optional[str] = None,
figsize: tuple = (15, 5)
):
"""
Plot training curves (loss, accuracy, F1)
Args:
history: Training history dictionary
save_path: Path to save figure
figsize: Figure size
"""
fig, axes = plt.subplots(1, 3, figsize=figsize)
epochs = range(1, len(history['train_loss']) + 1)
# Loss
axes[0].plot(epochs, history['train_loss'], 'o-', label='Train', linewidth=2)
axes[0].plot(epochs, history['val_loss'], 's-', label='Validation', linewidth=2)
axes[0].set_xlabel('Epoch', fontsize=11)
axes[0].set_ylabel('Loss', fontsize=11)
axes[0].set_title('Training Loss', fontsize=12, fontweight='bold')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Accuracy
axes[1].plot(epochs, history['train_acc'], 'o-', label='Train', linewidth=2)
axes[1].plot(epochs, history['val_acc'], 's-', label='Validation', linewidth=2)
axes[1].set_xlabel('Epoch', fontsize=11)
axes[1].set_ylabel('Accuracy', fontsize=11)
axes[1].set_title('Training Accuracy', fontsize=12, fontweight='bold')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
# F1 Score
axes[2].plot(epochs, history['train_f1'], 'o-', label='Train', linewidth=2)
axes[2].plot(epochs, history['val_f1'], 's-', label='Validation', linewidth=2)
axes[2].set_xlabel('Epoch', fontsize=11)
axes[2].set_ylabel('F1 Score', fontsize=11)
axes[2].set_title('Training F1 Score', fontsize=12, fontweight='bold')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"✅ Training curves saved to: {save_path}")
plt.show()
def plot_per_class_metrics(
metrics: Dict[str, np.ndarray],
class_names: List[str],
save_path: Optional[str] = None,
figsize: tuple = (10, 6)
):
"""
Plot per-class precision, recall, and F1 scores
Args:
metrics: Dictionary with 'precision', 'recall', 'f1' arrays
class_names: List of class names
save_path: Path to save figure
figsize: Figure size
"""
x = np.arange(len(class_names))
width = 0.25
fig, ax = plt.subplots(figsize=figsize)
ax.bar(x - width, metrics['precision'], width, label='Precision', alpha=0.8)
ax.bar(x, metrics['recall'], width, label='Recall', alpha=0.8)
ax.bar(x + width, metrics['f1'], width, label='F1 Score', alpha=0.8)
ax.set_xlabel('Class', fontsize=12)
ax.set_ylabel('Score', fontsize=12)
ax.set_title('Per-Class Performance Metrics', fontsize=14, fontweight='bold')
ax.set_xticks(x)
ax.set_xticklabels(class_names)
ax.legend()
ax.grid(True, axis='y', alpha=0.3)
ax.set_ylim([0, 1.0])
# Add value labels on bars
for container in ax.containers:
ax.bar_label(container, fmt='%.2f', padding=3)
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"✅ Per-class metrics saved to: {save_path}")
plt.show()
def plot_performance_comparison(
results: Dict[str, Dict[str, float]],
metrics: List[str] = ['accuracy', 'f1'],
save_path: Optional[str] = None,
figsize: tuple = (10, 6)
):
"""
Plot performance comparison between models
Args:
results: Dictionary mapping model names to metrics
metrics: List of metrics to plot
save_path: Path to save figure
figsize: Figure size
"""
models = list(results.keys())
x = np.arange(len(metrics))
width = 0.8 / len(models)
fig, ax = plt.subplots(figsize=figsize)
for i, model_name in enumerate(models):
values = [results[model_name][metric] for metric in metrics]
ax.bar(x + i * width, values, width, label=model_name, alpha=0.8)
ax.set_xlabel('Metric', fontsize=12)
ax.set_ylabel('Score', fontsize=12)
ax.set_title('Model Performance Comparison', fontsize=14, fontweight='bold')
ax.set_xticks(x + width * (len(models) - 1) / 2)
ax.set_xticklabels([m.replace('_', ' ').title() for m in metrics])
ax.legend()
ax.grid(True, axis='y', alpha=0.3)
ax.set_ylim([0, 1.0])
# Add value labels
for container in ax.containers:
ax.bar_label(container, fmt='%.3f', padding=3)
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"✅ Performance comparison saved to: {save_path}")
plt.show()
def plot_ablation_study(
ablation_results: Dict[str, float],
baseline_name: str = 'Full CLARA',
metric_name: str = 'Weighted F1 (%)',
save_path: Optional[str] = None,
figsize: tuple = (10, 6)
):
"""
Plot ablation study results
Args:
ablation_results: Dictionary mapping variant names to scores
baseline_name: Name of the baseline (full model)
metric_name: Name of the metric
save_path: Path to save figure
figsize: Figure size
"""
variants = list(ablation_results.keys())
scores = list(ablation_results.values())
baseline_score = ablation_results[baseline_name]
# Calculate differences from baseline
differences = [score - baseline_score for score in scores]
colors = ['green' if d >= 0 else 'red' for d in differences]
fig, ax = plt.subplots(figsize=figsize)
bars = ax.barh(variants, scores, color=colors, alpha=0.7)
ax.axvline(baseline_score, color='black', linestyle='--', linewidth=2, label='Baseline')
ax.set_xlabel(metric_name, fontsize=12)
ax.set_title('Ablation Study Results', fontsize=14, fontweight='bold')
ax.legend()
ax.grid(True, axis='x', alpha=0.3)
# Add value labels
for i, (score, diff) in enumerate(zip(scores, differences)):
label = f"{score:.2f}"
if diff != 0:
label += f" ({diff:+.2f})"
ax.text(score + 0.5, i, label, va='center')
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"✅ Ablation study saved to: {save_path}")
plt.show()
def visualize_predictions(
images: List,
texts: List[str],
true_labels: List[str],
pred_labels: List[str],
confidences: List[float],
save_path: Optional[str] = None,
figsize: tuple = (15, 10),
max_samples: int = 6
):
"""
Visualize sample predictions
Args:
images: List of PIL images
texts: List of text captions
true_labels: List of true labels
pred_labels: List of predicted labels
confidences: List of confidence scores
save_path: Path to save figure
figsize: Figure size
max_samples: Maximum number of samples to show
"""
n_samples = min(len(images), max_samples)
cols = 3
rows = (n_samples + cols - 1) // cols
fig, axes = plt.subplots(rows, cols, figsize=figsize)
axes = axes.flatten() if n_samples > 1 else [axes]
for i in range(n_samples):
ax = axes[i]
ax.imshow(images[i])
ax.axis('off')
# Title with prediction info
color = 'green' if true_labels[i] == pred_labels[i] else 'red'
title = f"True: {true_labels[i]}\nPred: {pred_labels[i]} ({confidences[i]:.2%})"
ax.set_title(title, fontsize=10, color=color, fontweight='bold')
# Add text caption below image
ax.text(
0.5, -0.1, f'"{texts[i]}"',
transform=ax.transAxes,
ha='center',
fontsize=8,
style='italic',
wrap=True
)
# Hide extra subplots
for i in range(n_samples, len(axes)):
axes[i].axis('off')
plt.suptitle('Sample Predictions', fontsize=16, fontweight='bold', y=1.02)
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"✅ Predictions visualization saved to: {save_path}")
plt.show()
def save_all_figures(
history: Dict,
results: Dict,
y_true: np.ndarray,
y_pred: np.ndarray,
class_names: List[str],
output_dir: str
):
"""
Save all visualization figures
Args:
history: Training history
results: Evaluation results
y_true: True labels
y_pred: Predicted labels
class_names: List of class names
output_dir: Directory to save figures
"""
os.makedirs(output_dir, exist_ok=True)
# Training curves
plot_training_curves(
history,
save_path=os.path.join(output_dir, 'training_curves.png')
)
# Confusion matrix
plot_confusion_matrix(
y_true, y_pred, class_names,
save_path=os.path.join(output_dir, 'confusion_matrix.png')
)
# Per-class metrics
if 'precision_per_class' in results and 'recall_per_class' in results and 'f1_per_class' in results:
plot_per_class_metrics(
{
'precision': np.array(results['precision_per_class']),
'recall': np.array(results['recall_per_class']),
'f1': np.array(results['f1_per_class'])
},
class_names,
save_path=os.path.join(output_dir, 'per_class_metrics.png')
)
print(f"\n✅ All figures saved to: {output_dir}")