Negoti_Arena / training /plot_curves.py
Arshad-1's picture
initial commit
8e52a2e
Raw
History Blame Contribute Delete
6.37 kB
"""
training/plot_curves.py โ€” Generate reward curve HTML from training results
==========================================================================
Reads checkpoints/cpu_run/reward_curve.csv and produces a self-contained
HTML file with interactive Plotly charts โ€” no matplotlib, no GPU.
Run:
python -m training.plot_curves --input checkpoints/cpu_run/reward_curve.csv
# Opens reward_curves.html in browser
"""
from __future__ import annotations
import argparse
import csv
import json
import os
import webbrowser
def load_csv(path: str) -> dict[str, list]:
data: dict[str, list] = {}
with open(path) as f:
reader = csv.DictReader(f)
for row in reader:
for k, v in row.items():
data.setdefault(k, []).append(float(v))
return data
def generate_html(data: dict[str, list], output_path: str) -> str:
steps = data.get("step", [])
f1 = data.get("overseer_f1", [])
det = data.get("detection_rate", [])
fp = data.get("false_positive_rate", [])
# Compute improvement annotations
f1_before = f1[0] if f1 else 0
f1_after = f1[-1] if f1 else 0
gap = f1_after - f1_before
html = f"""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>NegotiArena Training Curves</title>
<script src="https://cdn.plot.ly/plotly-2.26.0.min.js"></script>
<style>
body {{ background:#0f0f1a; color:#e0e0e0; font-family:sans-serif;
max-width:1100px; margin:40px auto; padding:20px; }}
h1 {{ color:#4a9eff; }}
h3 {{ color:#aaa; font-weight:400; }}
.metric-row {{ display:flex; gap:20px; margin:24px 0; }}
.card {{ background:#1a1a2e; border:1px solid #333; border-radius:8px;
padding:20px; flex:1; text-align:center; }}
.card .val {{ font-size:2.2em; font-weight:bold; margin:8px 0; }}
.card .label {{ color:#aaa; font-size:0.9em; }}
.green {{ color:#00cc66; }}
.red {{ color:#ff4a4a; }}
.blue {{ color:#4a9eff; }}
footer {{ color:#555; font-size:0.8em; margin-top:40px; text-align:center; }}
</style>
</head>
<body>
<h1>๐Ÿ›๏ธ NegotiArena โ€” Training Results</h1>
<h3>REINFORCE on CPU โ†’ same reward signal used for GRPO on GPU</h3>
<div class="metric-row">
<div class="card">
<div class="label">Overseer F1 (Before)</div>
<div class="val red">{f1_before:.3f}</div>
<div class="label">Random baseline</div>
</div>
<div class="card">
<div class="label">Overseer F1 (After)</div>
<div class="val green">{f1_after:.3f}</div>
<div class="label">After {int(steps[-1]) if steps else 0} training steps</div>
</div>
<div class="card">
<div class="label">Improvement</div>
<div class="val blue">+{gap:.3f}</div>
<div class="label">{gap/f1_before*100:.0f}% relative gain</div>
</div>
<div class="card">
<div class="label">FP Rate (After)</div>
<div class="val green">{fp[-1]:.1%}</div>
<div class="label">Was {fp[0]:.1%}</div>
</div>
</div>
<div id="chart_f1"></div>
<div id="chart_det"></div>
<script>
const steps = {json.dumps(steps)};
const f1 = {json.dumps(f1)};
const det = {json.dumps(det)};
const fp = {json.dumps(fp)};
const layout = {{
paper_bgcolor: '#0f0f1a',
plot_bgcolor: '#1a1a2e',
font: {{ color: '#cccccc' }},
xaxis: {{ title: 'Training Step', gridcolor: '#333' }},
legend: {{ bgcolor: '#1a1a2e' }},
height: 360,
margin: {{ t: 50, b: 50 }},
}};
// Chart 1: F1 curve with before/after annotation
Plotly.newPlot('chart_f1', [
{{
x: steps, y: f1, mode: 'lines+markers',
name: 'Overseer F1', line: {{ color: '#00cc66', width: 3 }},
marker: {{ size: 6 }}
}},
{{
x: [steps[0]], y: [f1[0]], mode: 'markers',
name: 'Before', marker: {{ color: '#ff4a4a', size: 14, symbol: 'circle' }}
}},
{{
x: [steps[steps.length-1]], y: [f1[f1.length-1]], mode: 'markers',
name: 'After', marker: {{ color: '#00cc66', size: 14, symbol: 'star' }}
}}
], {{
...layout,
title: {{ text: 'Overseer F1 โ€” Coalition Detection Improvement', font: {{ color: '#4a9eff', size: 16 }} }},
yaxis: {{ title: 'F1 Score', range: [0, 1], gridcolor: '#333' }},
annotations: [{{
x: steps[steps.length-1], y: f1[f1.length-1],
text: ` After: ${{f1[f1.length-1].toFixed(3)}}`,
showarrow: true, arrowhead: 2,
font: {{ color: '#00cc66', size: 13 }},
bgcolor: '#1a2e1a', bordercolor: '#00cc66',
}}]
}});
// Chart 2: Detection rate vs FP rate
Plotly.newPlot('chart_det', [
{{
x: steps, y: det, mode: 'lines', name: 'Detection Rate',
line: {{ color: '#4a9eff', width: 2 }}
}},
{{
x: steps, y: fp, mode: 'lines', name: 'False Positive Rate',
line: {{ color: '#ff9a4a', width: 2, dash: 'dot' }}
}}
], {{
...layout,
title: {{ text: 'Detection Rate vs False Positive Rate', font: {{ color: '#4a9eff', size: 16 }} }},
yaxis: {{ title: 'Rate', range: [0, 1], gridcolor: '#333' }},
}});
</script>
<footer>
NegotiArena | Meta ร— Scaler OpenEnv Hackathon | Theme 1: Multi-Agent Interactions<br>
CPU training uses REINFORCE with the same reward signal as GPU GRPO training.
</footer>
</body>
</html>"""
with open(output_path, "w", encoding="utf-8") as f:
f.write(html)
return output_path
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--input", default="checkpoints/cpu_run/reward_curve.csv")
parser.add_argument("--output", default="reward_curves.html")
parser.add_argument("--open", action="store_true", default=True,
help="Open in browser after generating")
args = parser.parse_args()
if not os.path.exists(args.input):
print(f"โŒ File not found: {args.input}")
print(" Run training first: python -m training.train_cpu --steps 200")
return
data = load_csv(args.input)
out = generate_html(data, args.output)
print(f"โœ… Chart saved to {out}")
if args.open:
webbrowser.open(f"file://{os.path.abspath(out)}")
if __name__ == "__main__":
main()