Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4ee48c8
Added HPC Mamba install instructions
ErykMajoch Oct 29, 2025
3c8970e
Fix vllm CUDA installation
ErykMajoch Oct 29, 2025
c97edcb
Update Mamba installation instructions with wget
yourGrand Oct 30, 2025
6022baf
Fix formatting in install_mamba.md
yourGrand Oct 30, 2025
5c2f2d3
Fixed dataset loading errors
ErykMajoch Oct 30, 2025
c75c33a
Resort to huggingface cache
ErykMajoch Oct 30, 2025
8c74eaa
Merge branch 'main' of https://github.com/TuringK/AbstentionBench int…
ErykMajoch Oct 30, 2025
33be3bf
Update install_mamba.sh
ErykMajoch Oct 30, 2025
d31afb7
added support for Qwen2.5-1.5B-Instruct
david-gasinski Nov 1, 2025
39c09de
Merge branch 'main' of github.com:TuringK/AbstentionBench
david-gasinski Nov 1, 2025
d6f26bd
fixed typo in qwen model config; increased Qwen2.5 model context wind…
david-gasinski Nov 1, 2025
f37895a
added support for google/gemma-3-1b-it
david-gasinski Nov 2, 2025
a914c2d
expanded run_datasets_local to allow multiple models to be queued
david-gasinski Nov 2, 2025
e3c6e03
added support for allenai/Llama-3.1-Tulu-3.1-8B
david-gasinski Nov 2, 2025
ba65437
fix typo
david-gasinski Nov 2, 2025
77dfc6a
added package global declaration to fix breaking change in new models
david-gasinski Nov 2, 2025
fd60ddd
updated allenai class to mirror llama parameters
david-gasinski Nov 3, 2025
e84e245
updated depdencies to support newer models
david-gasinski Nov 3, 2025
d4b3efc
reverting to older version to remain compatible with CUDA 12.1.1
david-gasinski Nov 3, 2025
22a32e7
updated vLLM version in setup.sh
david-gasinski Nov 3, 2025
43120ed
removed vLLM from environment.yml
david-gasinski Nov 3, 2025
336c23d
typo
david-gasinski Nov 3, 2025
41b3a86
revet vllm version; need to update CUDA to 12.4
david-gasinski Nov 3, 2025
18be3dd
working on making gemma3 models run on the benchmark with judge, mode…
yourGrand Nov 5, 2025
bcb3dba
all models can be submitted to slurm with judges for all datasets
yourGrand Nov 7, 2025
e9dbbb3
example master sbatch script
yourGrand Nov 7, 2025
4b4c822
fixed typo
yourGrand Nov 7, 2025
b38b000
updated configs
yourGrand Nov 7, 2025
5a199e6
config edit, csv results, table script
yourGrand Nov 10, 2025
d8b59bf
prompt tuning work
Dec 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,11 @@ __marimo__/


playground*.ipynb
data/*
data/*

logs/
*.arrow
PromptTuning/datasets
PromptTuning/results
PromptTuning/trained_models
PromptTuning/results_archive
126 changes: 126 additions & 0 deletions PromptTuning/analyse_groundtruth_results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import json
from pathlib import Path
import argparse


def parse_args():
parser = argparse.ArgumentParser(
description='Analyse abstention results from model evaluations'
)
parser.add_argument(
'--results-path',
type=str,
required=True,
help='Path to the results directory (e.g., .../Qwen2_5_1_5B_Instruct_Benchmark/results)'
)
parser.add_argument(
'--model-name',
type=str,
required=True,
help='Name of the model for display in output (e.g., "Qwen 2.5 1.5B Instruct")'
)
return parser.parse_args()


def analyse_dataset(json_path):
with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f)

positive_examples = 0 # should_abstain = True and is_abstention = True
negative_examples = 0 # should_abstain = True and is_abstention = False

for response in data.get('responses', []):
prompt = response.get('prompt', {})
should_abstain = prompt.get('should_abstain', False)
is_abstention = response.get('is_abstention', False)

if should_abstain:
if is_abstention:
positive_examples += 1
else:
negative_examples += 1

return positive_examples, negative_examples

def main():
args = parse_args()

base_path = Path(args.results_path)

if not base_path.exists():
print(f"Error: Results path does not exist: {base_path}")
return

# Find all `GroundTruthAbstentionEvaluator.json` files
results = {}

for dataset_dir in sorted(base_path.iterdir()):
if dataset_dir.is_dir():
dataset_name = dataset_dir.name

evaluator_files = list(dataset_dir.rglob("GroundTruthAbstentionEvaluator.json"))

if evaluator_files:
json_path = evaluator_files[0]
try:
positive, negative = analyse_dataset(json_path)
results[dataset_name] = {
'positive': positive,
'negative': negative,
'total_should_abstain': positive + negative
}
except Exception as e:
print(f"Error processing {dataset_name}: {e}")

# Print results
print("=" * 80)
print(f"{args.model_name} - Abstention Analysis")
print("=" * 80)
print()

total_positive = 0
total_negative = 0

# Calculate percentages for sorting
dataset_list = []
for dataset_name, stats in results.items():
positive = stats['positive']
negative = stats['negative']
total = stats['total_should_abstain']

if total > 0:
positive_pct = (positive / total) * 100
else:
positive_pct = 0

dataset_list.append((dataset_name, positive, negative, total, positive_pct))

# Sort by percentage descending
dataset_list.sort(key=lambda x: x[4], reverse=True)

for dataset_name, positive, negative, total, positive_pct in dataset_list:

print(f"- Dataset: {dataset_name}")
print(f"Positive Examples (Correctly Abstained):\t{positive:4d} / {total} ({positive_pct:.1f}%)")
print(f"Negative Examples (Failed to Abstain):\t{negative:4d} / {total}")
print()

total_positive += positive
total_negative += negative

# Overall statistics
print("=" * 80)
print(f"Overall Statistics")
print("=" * 80)
grand_total = total_positive + total_negative
if grand_total > 0:
overall_pct = (total_positive / grand_total) * 100
else:
overall_pct = 0

print(f"Total Positive Examples (Correctly Abstained): {total_positive:4d} / {grand_total} ({overall_pct:.1f}%)")
print(f"Total Negative Examples (Failed to Abstain): {total_negative:4d} / {grand_total}")
print()

if __name__ == "__main__":
main()
180 changes: 180 additions & 0 deletions PromptTuning/analyse_results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"""
Analyse abstention results from a single evaluation JSON file.

Usage:
python analyse_results.py --json-path /path/to/GroundTruthAbstentionEvaluator.json
python analyse_results.py --json-path /path/to/GroundTruthAbstentionEvaluator.json --model-name "Gemma 3 1B Soft Prompt"
"""

import json
from pathlib import Path
import argparse


def parse_args():
parser = argparse.ArgumentParser(
description='Analyse abstention results from a single evaluation JSON file'
)
parser.add_argument(
'--json-path',
type=str,
required=True,
help='Path to the GroundTruthAbstentionEvaluator.json file'
)
parser.add_argument(
'--model-name',
type=str,
default="Model",
help='Name of the model for display in output (default: "Model")'
)
return parser.parse_args()


def analyse_results(json_path):
"""Analyse a single evaluation JSON file and return detailed metrics."""
with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f)

# Confusion matrix components
true_positive = 0 # should_abstain=True, is_abstention=True (correct abstention)
false_negative = 0 # should_abstain=True, is_abstention=False (missed abstention)
false_positive = 0 # should_abstain=False, is_abstention=True (unnecessary abstention)
true_negative = 0 # should_abstain=False, is_abstention=False (correct answer)

# For detailed breakdown
results_by_dataset = {}

for response in data.get('responses', []):
prompt = response.get('prompt', {})
should_abstain = prompt.get('should_abstain', False)
is_abstention = response.get('is_abstention', False)

# Get dataset name from metadata if available
metadata = prompt.get('metadata', {})
dataset_name = metadata.get('dataset', 'unknown')

if dataset_name not in results_by_dataset:
results_by_dataset[dataset_name] = {'tp': 0, 'fn': 0, 'fp': 0, 'tn': 0}

if should_abstain and is_abstention:
true_positive += 1
results_by_dataset[dataset_name]['tp'] += 1
elif should_abstain and not is_abstention:
false_negative += 1
results_by_dataset[dataset_name]['fn'] += 1
elif not should_abstain and is_abstention:
false_positive += 1
results_by_dataset[dataset_name]['fp'] += 1
else: # not should_abstain and not is_abstention
true_negative += 1
results_by_dataset[dataset_name]['tn'] += 1

return {
'tp': true_positive,
'fn': false_negative,
'fp': false_positive,
'tn': true_negative,
'by_dataset': results_by_dataset
}


def compute_metrics(tp, fn, fp, tn):
"""Compute precision, recall, F1, and accuracy."""
total = tp + fn + fp + tn

# Accuracy
accuracy = (tp + tn) / total if total > 0 else 0

# Precision: of all predicted abstentions, how many were correct?
precision = tp / (tp + fp) if (tp + fp) > 0 else 0

# Recall: of all cases that should abstain, how many did we catch?
recall = tp / (tp + fn) if (tp + fn) > 0 else 0

# F1 score
f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0

return {
'accuracy': accuracy,
'precision': precision,
'recall': recall,
'f1': f1
}


def main():
args = parse_args()

json_path = Path(args.json_path)

if not json_path.exists():
print(f"Error: JSON file does not exist: {json_path}")
return

# Analyse results
results = analyse_results(json_path)

tp = results['tp']
fn = results['fn']
fp = results['fp']
tn = results['tn']

metrics = compute_metrics(tp, fn, fp, tn)

# Print results
print("=" * 70)
print(f"{args.model_name} - Abstention Analysis")
print("=" * 70)
print()

# Confusion Matrix
print("Confusion Matrix:")
print("-" * 50)
print(f" Predicted")
print(f" Abstain Answer")
print(f"Actual Should {tp:4d} {fn:4d} (Total: {tp+fn})")
print(f" Abstain")
print(f" Should {fp:4d} {tn:4d} (Total: {fp+tn})")
print(f" Answer")
print()

# Metrics
print("Metrics:")
print("-" * 50)
print(f"Accuracy: {metrics['accuracy']*100:6.2f}% (Overall correctness)")
print(f"Precision: {metrics['precision']*100:6.2f}% (When it abstains, is it right?)")
print(f"Recall: {metrics['recall']*100:6.2f}% (Does it catch cases needing abstention?)")
print(f"F1 Score: {metrics['f1']*100:6.2f}% (Harmonic mean of precision & recall)")
print()

# Breakdown by category
print("Breakdown:")
print("-" * 50)
print(f"True Positives: {tp:4d} (Correctly abstained)")
print(f"False Negatives: {fn:4d} (Should have abstained but didn't)")
print(f"False Positives: {fp:4d} (Abstained unnecessarily)")
print(f"True Negatives: {tn:4d} (Correctly answered)")
print(f"Total: {tp+fn+fp+tn:4d}")
print()

# Per-dataset breakdown if available
if len(results['by_dataset']) > 1 or 'unknown' not in results['by_dataset']:
print("Per-Dataset Breakdown:")
print("-" * 50)
for dataset_name, stats in sorted(results['by_dataset'].items()):
d_tp, d_fn, d_fp, d_tn = stats['tp'], stats['fn'], stats['fp'], stats['tn']
d_metrics = compute_metrics(d_tp, d_fn, d_fp, d_tn)
d_total = d_tp + d_fn + d_fp + d_tn
print(f"\n{dataset_name} ({d_total} samples):")
print(f" Accuracy: {d_metrics['accuracy']*100:.1f}% | "
f"Precision: {d_metrics['precision']*100:.1f}% | "
f"Recall: {d_metrics['recall']*100:.1f}% | "
f"F1: {d_metrics['f1']*100:.1f}%")
print(f" TP: {d_tp} | FN: {d_fn} | FP: {d_fp} | TN: {d_tn}")

print()
print("=" * 70)


if __name__ == "__main__":
main()
Loading