media-profiling / eval_single_output.py
Claude
Add single-outlet evaluation script and sample hybrid output
a3b1d3c
Raw
History Blame Contribute Delete
5.57 kB
"""
Evaluate a single pre-computed output against gold labels.
Usage:
python eval_single_output.py --input evaluation_dataset.json --output hybrid_output.json --outlet "Newfoundland Independent"
Where hybrid_output.json contains the raw_output from the hybrid run.
"""
import json
import argparse
import logging
from metrics import MetricsCalculator
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def evaluate_output(gold_item: dict, generated: dict) -> dict:
"""Compute all metrics for a single outlet given gold and generated data."""
metrics_calc = MetricsCalculator()
# Construct text blocks (same logic as run_benchmark.py evaluate_single)
gen_text = " ".join(filter(None, [
generated.get('bias_category_description'),
generated.get('overall_summary'),
generated.get('analysis'),
generated.get('history'),
generated.get('ownership'),
])).strip()
gold_text_full = "\n\n".join(filter(None, [
gold_item.get('bias_category_description', ''),
gold_item.get('overall_summary', ''),
gold_item.get('history', ''),
gold_item.get('analysis', ''),
gold_item.get('ownership', ''),
]))
gold_summary = " ".join(filter(None, [
gold_item.get('bias_category_description', ''),
gold_item.get('overall_summary', ''),
gold_item.get('analysis', ''),
gold_item.get('history', ''),
gold_item.get('ownership', ''),
]))
# A. Text Overlap
logger.info("Computing ROUGE-L and METEOR...")
text_m = metrics_calc.calculate_text_metrics([gold_summary], [gen_text])
# B. FactScore Precision: Generated -> Gold
logger.info("Computing FACTScore Precision...")
fs_precision = metrics_calc.check_fact_recall(gold_text_full, gen_text)
# C. Fact Recall: Gold -> Generated
logger.info("Computing FACTScore Recall...")
fs_recall = metrics_calc.check_gold_fact_recall(gold_text_full, gen_text)
# D. Fact Check Detection
logger.info("Computing Fact Check Detection...")
gold_fc = gold_item.get('failed_fact_checks', [])
gen_fc_list = generated.get('failed_fact_checks', [])
fc_m = MetricsCalculator.evaluate_fact_checks(gold_fc, gen_text, gen_fc_list)
result = {
"name": gold_item['name'],
"source_url": gold_item.get('source_url', ''),
"gold_bias": gold_item.get('bias_score', 0),
"pred_bias": generated.get('bias_score'),
"gold_factuality": gold_item.get('factual_score', 0),
"pred_factuality": generated.get('factual_score'),
"rougeL": text_m['rougeL'],
"meteor": text_m['meteor'],
"factscore_precision": fs_precision.get('factscore', 0.0),
"error_rate": fs_precision.get('error_rate', 0.0),
"factscore_recall": fs_recall.get('gold_recall', 0.0),
"fact_check_hit": fc_m,
}
return result
def main():
parser = argparse.ArgumentParser(description="Evaluate a single pre-computed output")
parser.add_argument("--input", type=str, default="evaluation_dataset.json",
help="Path to evaluation dataset JSON")
parser.add_argument("--output", type=str, required=True,
help="Path to JSON file with the generated output (raw_output)")
parser.add_argument("--outlet", type=str, required=True,
help="Name of the outlet to evaluate")
args = parser.parse_args()
# Load gold dataset
with open(args.input) as f:
dataset = json.load(f)
# Find gold item
gold_item = None
for item in dataset:
if item['name'].lower() == args.outlet.lower():
gold_item = item
break
if not gold_item:
logger.error(f"Outlet '{args.outlet}' not found in {args.input}")
return
# Load generated output
with open(args.output) as f:
generated = json.load(f)
# If the file has a 'raw_output' key (from benchmark results), use that
if 'raw_output' in generated:
generated = generated['raw_output']
# If wrapped in 'output' key (from hybrid runner results)
if 'output' in generated:
generated = generated['output']
logger.info(f"Evaluating: {gold_item['name']}")
logger.info(f" Gold bias: {gold_item.get('bias_score')}, Pred bias: {generated.get('bias_score')}")
logger.info(f" Gold factuality: {gold_item.get('factual_score')}, Pred factuality: {generated.get('factual_score')}")
result = evaluate_output(gold_item, generated)
# Print results
print("\n" + "=" * 60)
print(f"EVALUATION RESULTS: {result['name']}")
print("=" * 60)
print(f" Bias: gold={result['gold_bias']}, pred={result['pred_bias']}")
print(f" Factuality: gold={result['gold_factuality']}, pred={result['pred_factuality']}")
print(f" ROUGE-L: {result['rougeL']:.4f}")
print(f" METEOR: {result['meteor']:.4f}")
print(f" FACTScore Prec: {result['factscore_precision']:.4f}")
print(f" Error Rate: {result['error_rate']:.4f}")
print(f" FACTScore Recall: {result['factscore_recall']:.4f}")
print(f" Fact Check Hit: {result['fact_check_hit']}")
print("=" * 60)
# Save full result
out_path = args.output.replace('.json', '_evaluated.json')
with open(out_path, 'w') as f:
json.dump(result, f, indent=2, default=str)
print(f"\nFull result saved to: {out_path}")
if __name__ == "__main__":
main()