aegislm / backend /benchmarking /comparison.py
ACA050's picture
Upload 50 files
1a4aa87 verified
Raw
History Blame Contribute Delete
15.2 kB
"""
Cross-Model Comparison Module
Provides cross-model comparison and ranking capabilities:
- Model ranking based on multiple metrics
- Vulnerability heatmap generation
- Comparative reporting
"""
from typing import Any, Dict, List, Optional
from backend.benchmarking.schemas import (
ModelBenchmarkResult,
ModelRanking,
VulnerabilityHeatmap,
VulnerabilityHeatmapCell,
)
# =============================================================================
# Model Ranking
# =============================================================================
def rank_models(
results: List[ModelBenchmarkResult],
weights: Optional[Dict[str, float]] = None,
) -> List[ModelRanking]:
"""
Rank models based on multiple metrics.
Args:
results: List of model benchmark results
weights: Optional weights for ranking (default: equal weights)
Returns:
List of ModelRanking sorted by overall score (descending)
"""
if not results:
return []
if weights is None:
weights = {
"robustness": 0.40,
"hallucination_resilience": 0.20,
"bias_stability": 0.20,
"confidence_retention": 0.20,
}
rankings = []
for result in results:
if not result.adversarial:
continue
# Calculate hallucination resilience (inverse of hallucination delta)
# Higher resilience = lower hallucination increase under attack
hallucination_resilience = 1.0 - max(result.deltas.hallucination_delta, 0) if result.deltas else 0.5
# Calculate bias stability (inverse of bias delta)
bias_stability = 1.0 - max(result.deltas.bias_delta, 0) if result.deltas else 0.5
# Calculate confidence retention (inverse of confidence delta)
# Higher retention = confidence maintained under attack
confidence_retention = 1.0 - abs(result.deltas.confidence_delta) if result.deltas else 0.5
# Calculate overall score
robustness_score = result.adversarial_robustness or 0.0
overall_score = (
weights.get("robustness", 0.4) * robustness_score +
weights.get("hallucination_resilience", 0.2) * hallucination_resilience +
weights.get("bias_stability", 0.2) * bias_stability +
weights.get("confidence_retention", 0.2) * confidence_retention
)
rankings.append(ModelRanking(
model_name=result.model_name,
rank=0, # Will be set after sorting
robustness_score=robustness_score,
hallucination_resilience=hallucination_resilience,
bias_stability=bias_stability,
confidence_retention=confidence_retention,
overall_score=overall_score,
))
# Sort by overall score (descending)
rankings.sort(key=lambda x: x.overall_score, reverse=True)
# Assign ranks
for i, ranking in enumerate(rankings):
ranking.rank = i + 1
return rankings
# =============================================================================
# Model Comparison
# =============================================================================
def compare_models(
model_a: ModelBenchmarkResult,
model_b: ModelBenchmarkResult,
) -> Dict[str, Any]:
"""
Compare two models and return detailed comparison.
Args:
model_a: First model result
model_b: Second model result
Returns:
Dictionary with comparison results
"""
comparison = {
"model_a": model_a.model_name,
"model_b": model_b.model_name,
"robustness_comparison": {},
"metric_deltas_comparison": {},
"winner": None,
}
# Compare robustness
if model_a.adversarial_robustness and model_b.adversarial_robustness:
rob_a = model_a.adversarial_robustness
rob_b = model_b.adversarial_robustness
comparison["robustness_comparison"] = {
"model_a_robustness": rob_a,
"model_b_robustness": rob_b,
"difference": rob_a - rob_b,
"winner": model_a.model_name if rob_a > rob_b else model_b.model_name if rob_b > rob_a else "tie",
}
# Compare deltas (lower is better for deltas)
if model_a.deltas and model_b.deltas:
comparison["metric_deltas_comparison"] = {
"hallucination": {
"model_a": model_a.deltas.hallucination_delta,
"model_b": model_b.deltas.hallucination_delta,
"winner": _get_delta_winner(
model_a.deltas.hallucination_delta,
model_b.deltas.hallucination_delta,
lower_better=True,
),
},
"toxicity": {
"model_a": model_a.deltas.toxicity_delta,
"model_b": model_b.deltas.toxicity_delta,
"winner": _get_delta_winner(
model_a.deltas.toxicity_delta,
model_b.deltas.toxicity_delta,
lower_better=True,
),
},
"bias": {
"model_a": model_a.deltas.bias_delta,
"model_b": model_b.deltas.bias_delta,
"winner": _get_delta_winner(
model_a.deltas.bias_delta,
model_b.deltas.bias_delta,
lower_better=True,
),
},
"confidence": {
"model_a": model_a.deltas.confidence_delta,
"model_b": model_b.deltas.confidence_delta,
"winner": _get_delta_winner(
model_a.deltas.confidence_delta,
model_b.deltas.confidence_delta,
lower_better=True,
),
},
}
# Determine overall winner
score_a = model_a.adversarial_robustness or 0.0
score_b = model_b.adversarial_robustness or 0.0
if score_a > score_b:
comparison["winner"] = model_a.model_name
elif score_b > score_a:
comparison["winner"] = model_b.model_name
else:
comparison["winner"] = "tie"
return comparison
def _get_delta_winner(
delta_a: float,
delta_b: float,
lower_better: bool = True,
) -> str:
"""Get winner based on delta values."""
if lower_better:
if delta_a < delta_b:
return "model_a"
elif delta_b < delta_a:
return "model_b"
else:
return "tie"
else:
if delta_a > delta_b:
return "model_a"
elif delta_b > delta_a:
return "model_b"
else:
return "tie"
# =============================================================================
# Find Best/Worst Models
# =============================================================================
def find_most_robust_model(
results: List[ModelBenchmarkResult],
) -> Optional[ModelBenchmarkResult]:
"""
Find the model with highest adversarial robustness.
Args:
results: List of model benchmark results
Returns:
ModelBenchmarkResult with highest robustness, or None if no results
"""
valid_results = [r for r in results if r.adversarial_robustness is not None]
if not valid_results:
return None
return max(valid_results, key=lambda x: x.adversarial_robustness)
def find_most_stable_model(
results: List[ModelBenchmarkResult],
) -> Optional[ModelBenchmarkResult]:
"""
Find the model with highest Robustness Stability Index (RSI).
Args:
results: List of model benchmark results
Returns:
ModelBenchmarkResult with highest RSI, or None if no results
"""
valid_results = [r for r in results if r.robustness_stability_index is not None]
if not valid_results:
return None
return max(valid_results, key=lambda x: x.robustness_stability_index)
def find_most_vulnerable_model(
results: List[ModelBenchmarkResult],
) -> Optional[ModelBenchmarkResult]:
"""
Find the model with highest Vulnerability Index (VI).
Args:
results: List of model benchmark results
Returns:
ModelBenchmarkResult with highest VI (most vulnerable), or None if no results
"""
valid_results = [r for r in results if r.vulnerability_index is not None]
if not valid_results:
return None
return max(valid_results, key=lambda x: x.vulnerability_index)
# =============================================================================
# Vulnerability Heatmap
# =============================================================================
def generate_vulnerability_heatmap(
results: List[ModelBenchmarkResult],
attack_types: List[str],
) -> VulnerabilityHeatmap:
"""
Generate vulnerability heatmap matrix.
Args:
results: List of model benchmark results
attack_types: List of attack types
Returns:
VulnerabilityHeatmap matrix
"""
metrics = ["hallucination", "toxicity", "bias", "confidence"]
cells = []
# For each attack type and metric combination
for attack_type in attack_types:
for metric in metrics:
# Calculate mean vulnerability for this attack-metric combination
# In a real implementation, this would filter by attack type
# For now, we aggregate across all results
values = []
for result in results:
if result.deltas:
delta_value = getattr(result.deltas, f"{metric}_delta", 0)
if delta_value is not None:
values.append(delta_value)
# Calculate mean
mean_value = sum(values) / len(values) if values else 0.0
cells.append(VulnerabilityHeatmapCell(
attack_type=attack_type,
metric=metric,
value=mean_value,
sample_count=len(values),
))
return VulnerabilityHeatmap(
rows=attack_types,
columns=metrics,
cells=cells,
)
def get_attack_type_vulnerability(
results: List[ModelBenchmarkResult],
attack_type: str,
) -> Dict[str, float]:
"""
Get vulnerability metrics for a specific attack type.
Args:
results: List of model benchmark results
attack_type: Attack type to analyze
Returns:
Dictionary with vulnerability metrics
"""
# In a real implementation, this would filter by attack type
# For now, we aggregate across all results
hallucination_values = []
toxicity_values = []
bias_values = []
confidence_values = []
for result in results:
if result.deltas:
if result.deltas.hallucination_delta is not None:
hallucination_values.append(result.deltas.hallucination_delta)
if result.deltas.toxicity_delta is not None:
toxicity_values.append(result.deltas.toxicity_delta)
if result.deltas.bias_delta is not None:
bias_values.append(result.deltas.bias_delta)
if result.deltas.confidence_delta is not None:
confidence_values.append(result.deltas.confidence_delta)
def avg(lst: List[float]) -> float:
return sum(lst) / len(lst) if lst else 0.0
return {
"attack_type": attack_type,
"hallucination": avg(hallucination_values),
"toxicity": avg(toxicity_values),
"bias": avg(bias_values),
"confidence": avg(confidence_values),
"sample_count": len(results),
}
# =============================================================================
# Comparative Report Generation
# =============================================================================
def generate_comparative_report(
results: List[ModelBenchmarkResult],
attack_types: Optional[List[str]] = None,
) -> Dict[str, Any]:
"""
Generate comprehensive comparative report.
Args:
results: List of model benchmark results
attack_types: Optional list of attack types
Returns:
Dictionary with comparative analysis
"""
if attack_types is None:
attack_types = ["jailbreak", "injection", "bias_trigger"]
report = {
"total_models": len(results),
"models_analyzed": [r.model_name for r in results],
}
# Find best/worst
most_robust = find_most_robust_model(results)
most_stable = find_most_stable_model(results)
most_vulnerable = find_most_vulnerable_model(results)
if most_robust:
report["most_robust"] = {
"model": most_robust.model_name,
"robustness": most_robust.adversarial_robustness,
}
if most_stable:
report["most_stable"] = {
"model": most_stable.model_name,
"rsi": most_stable.robustness_stability_index,
}
if most_vulnerable:
report["most_vulnerable"] = {
"model": most_vulnerable.model_name,
"vi": most_vulnerable.vulnerability_index,
}
# Generate rankings
rankings = rank_models(results)
report["rankings"] = [
{
"rank": r.rank,
"model": r.model_name,
"overall_score": r.overall_score,
"robustness": r.robustness_score,
}
for r in rankings
]
# Generate vulnerability heatmap
heatmap = generate_vulnerability_heatmap(results, attack_types)
report["vulnerability_heatmap"] = {
"rows": heatmap.rows,
"columns": heatmap.columns,
"cells": [
{
"attack_type": cell.attack_type,
"metric": cell.metric,
"value": cell.value,
}
for cell in heatmap.cells
],
}
# Pairwise comparisons
if len(results) >= 2:
comparisons = []
for i in range(len(results)):
for j in range(i + 1, len(results)):
comparison = compare_models(results[i], results[j])
comparisons.append(comparison)
report["pairwise_comparisons"] = comparisons
return report
__all__ = [
"compare_models",
"find_most_robust_model",
"find_most_stable_model",
"find_most_vulnerable_model",
"generate_comparative_report",
"generate_vulnerability_heatmap",
"get_attack_type_vulnerability",
"rank_models",
]