opal-optimizer 0.3.5__tar.gz → 0.3.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/CHANGELOG.md +18 -0
- {opal_optimizer-0.3.5/opal_optimizer.egg-info → opal_optimizer-0.3.7}/PKG-INFO +48 -28
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/README.md +44 -27
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/docs/GUIDE.md +8 -13
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/README.md +26 -13
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/cifar10_resnet.py +2 -3
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/diffusion_mnist.py +2 -3
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/goldfish_swahili/README.md +8 -12
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/gpt_small.py +2 -3
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/mnist_mlp.py +2 -3
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/modded_nanogpt/README.md +1 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/train.py +1 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/raev2_b/README.md +14 -22
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/sit_b2/README.md +1 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/_version.py +1 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/mailbox.py +7 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/cli.py +11 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/estimate.py +15 -5
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/report_privacy.py +1 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/wrapper.py +5 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7/opal_optimizer.egg-info}/PKG-INFO +48 -28
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/pyproject.toml +5 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/skills/train-with-agent/SKILL.md +11 -4
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/LICENSE +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/MANIFEST.in +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/modded_nanogpt/opal.patch +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/README.md +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/records.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/resnet.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/splits.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/sit_b2/opal.patch +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/__init__.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/agent.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/__init__.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/_codex_client.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/_privacy.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/base.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/claude.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/codex.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/unavailable.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/context.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/defaults.json +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/defaults.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/hf.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/__init__.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/aggressive.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/audit.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/boundary.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/checkpoint.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/ddp.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/integrity.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/nested.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/neutral_analysis.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/precision.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/recovery.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/schedulers.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/setup.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/skip.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/taskfacts.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/waiting.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/programs.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/prompts/example_updater.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/prompts/neutral.md +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/report.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/report_page.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/results.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/schedule.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/summaries.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/SOURCES.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/dependency_links.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/entry_points.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/requires.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/top_level.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/setup.cfg +0 -0
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.7 (2026-10-22)
|
|
4
|
+
|
|
5
|
+
- Documentation and examples only, apart from one printed notice; the optimizer math, the agent prompt and the agent's tools are unchanged.
|
|
6
|
+
- The README and the examples page report each task's newest OPAL run (RAEv2-B 3.19, Goldfish 77.25, LightningDiT-B/1 2.31) and no longer list older-system numbers.
|
|
7
|
+
- The examples page has a section on training without waiting for the agent (`blocking=False`), with the RAEv2-B official, blocking and non-blocking runs.
|
|
8
|
+
- The README says why long runs suit OPAL, with measured waiting times.
|
|
9
|
+
- `blocking="auto"` is no longer documented or offered by the example scripts. Using it prints that it is experimental and untested on real training runs.
|
|
10
|
+
|
|
11
|
+
## 0.3.6 (2026-10-22)
|
|
12
|
+
|
|
13
|
+
- Fixes from a from-scratch test of the install and the agent prompt; the optimizer math, the agent prompt and the agent's tools are unchanged.
|
|
14
|
+
- `opal doctor` says when the machine has an NVIDIA GPU that the installed torch cannot use; the README explains how to install the torch build for the driver first.
|
|
15
|
+
- The ssh mailbox broker explains a "Permission denied" from a host that asks for a password, and the guide and skill show how to share one connection.
|
|
16
|
+
- `opal estimate` no longer offers a decision count that still waits more than 4x, and says when training ends before the first answer.
|
|
17
|
+
- The report records the training waiting time for user scripts.
|
|
18
|
+
- The privacy filter keeps numbers such as `/1000`.
|
|
19
|
+
- The PyPI page links to the repository, the guide and the blog post, and README links work outside GitHub.
|
|
20
|
+
|
|
3
21
|
## 0.3.5 (2026-10-21)
|
|
4
22
|
|
|
5
23
|
- Reject uneven DDP inputs on every rank through the optimizer's Joinable interface instead of hanging (#2).
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opal-optimizer
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.7
|
|
4
4
|
Summary: A coding agent inside your training run, rewriting the optimizer while it trains
|
|
5
5
|
Author: Xingjian Bai, Jia-Hao Liang, Jayden Teoh, Vincent Sitzmann
|
|
6
6
|
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/xingjian-bai/opal
|
|
8
|
+
Project-URL: Documentation, https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md
|
|
9
|
+
Project-URL: Blog post, https://xingjianbai.com/reactive-optimizers/
|
|
7
10
|
Requires-Python: >=3.10
|
|
8
11
|
Description-Content-Type: text/markdown
|
|
9
12
|
License-File: LICENSE
|
|
@@ -30,9 +33,9 @@ Dynamic: license-file
|
|
|
30
33
|
|
|
31
34
|
<p align="center">
|
|
32
35
|
<a href="https://xingjianbai.com/reactive-optimizers/">Blog post</a> ·
|
|
33
|
-
<a href="docs/GUIDE.md">Guide</a> ·
|
|
36
|
+
<a href="https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md">Guide</a> ·
|
|
34
37
|
<a href="#start-with-your-coding-agent">Start with your agent</a> ·
|
|
35
|
-
<a href="examples/README.md">Examples</a>
|
|
38
|
+
<a href="https://github.com/xingjian-bai/opal/blob/main/examples/README.md">Examples</a>
|
|
36
39
|
</p>
|
|
37
40
|
|
|
38
41
|
Most networks are trained with an optimizer that is fixed before training starts. OPAL (OPtimizer with an Agent in
|
|
@@ -44,22 +47,22 @@ program, which measures whatever the agent wants to look at.
|
|
|
44
47
|
The agent can change anything that can be written as optimizer code: the update rule of each parameter group, the
|
|
45
48
|
learning rates and their schedules, momentum and β₂, preconditioning, clipping, guards against loss spikes. It is
|
|
46
49
|
asked not to touch your model, data, loss, batch size, number of steps or evaluation. Its programs run inside your
|
|
47
|
-
training process, so this is a rule, not a sandbox (see [Safety](docs/GUIDE.md#safety)). Until the agent changes
|
|
50
|
+
training process, so this is a rule, not a sandbox (see [Safety](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#safety)). Until the agent changes
|
|
48
51
|
something, the wrapped run is bit-for-bit the same as the plain one.
|
|
49
52
|
|
|
50
53
|
## Start with your coding agent
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
Open your own project in a coding agent (Claude Code, Codex, Cursor or similar) and paste this prompt. The agent
|
|
56
|
+
installs OPAL, wires it into your training script, asks you what it cannot know, and checks that nothing changed
|
|
57
|
+
before anything costs money.
|
|
55
58
|
|
|
56
59
|
```text
|
|
57
60
|
I want to train my model with OPAL, a reactive optimizer: a coding agent that rewrites the optimizer while the
|
|
58
|
-
model trains.
|
|
61
|
+
model trains. Its docs are at https://github.com/xingjian-bai/opal. Please set it up for this project:
|
|
59
62
|
|
|
60
|
-
1. Read
|
|
61
|
-
|
|
62
|
-
2. Install OPAL into the Python environment this project trains with (pip install -
|
|
63
|
+
1. Read the README there and skills/train-with-agent/SKILL.md, and follow the skill. Do not edit OPAL's own files;
|
|
64
|
+
configure everything through opal.wrap arguments.
|
|
65
|
+
2. Install OPAL into the Python environment this project trains with (pip install opal-optimizer), and
|
|
63
66
|
run `opal doctor` (free, offline) to check that torch and Claude Code are ready on the machine that will run
|
|
64
67
|
training; run `opal doctor --live` (one tiny paid agent call) only once I approve an agent run. If Claude Code is
|
|
65
68
|
missing or not logged in there, tell me what to run; do not log in for me.
|
|
@@ -71,8 +74,9 @@ model trains. OPAL is cloned at <PATH TO OPAL>. Please set it up for this projec
|
|
|
71
74
|
must match step for step. Show me both. If I only asked for a dry run, stop here.
|
|
72
75
|
7. Before the real run, time an update in the smoke test and run `opal estimate --total-steps N --sec-per-step S`;
|
|
73
76
|
show me what it prints (how long training will wait for the agent, also as a multiple of the run's own time, and
|
|
74
|
-
the cost). Ask me for a cost cap (max_cost_usd)
|
|
75
|
-
go-ahead; then give me the exact command to launch it, and how to
|
|
77
|
+
the cost). Ask me for a cost cap (max_cost_usd); leave decisions unset unless I ask, so that the waiting stays
|
|
78
|
+
within 4x the run's own time. Wait for my go-ahead; then give me the exact command to launch it, and how to
|
|
79
|
+
follow it (opal show, opal report).
|
|
76
80
|
```
|
|
77
81
|
|
|
78
82
|
## Start by hand
|
|
@@ -84,6 +88,11 @@ opal doctor # torch and Claude Code ready?
|
|
|
84
88
|
|
|
85
89
|
The package is called `opal-optimizer` on PyPI, because `opal` there is an unrelated web framework. Both install as `import opal`, so do not install both.
|
|
86
90
|
|
|
91
|
+
On a GPU machine, install OPAL into the environment you already train with, or install the PyTorch build for your
|
|
92
|
+
driver first ([pytorch.org](https://pytorch.org/get-started/locally/)). In an empty environment, `pip install
|
|
93
|
+
opal-optimizer` pulls the newest torch from PyPI, which may need a newer NVIDIA driver; `opal doctor` then shows
|
|
94
|
+
`CUDA available: False`.
|
|
95
|
+
|
|
87
96
|
```python
|
|
88
97
|
import torch, opal
|
|
89
98
|
|
|
@@ -115,8 +124,11 @@ opal show opal_runs/<folder> # one line per decision
|
|
|
115
124
|
opal report opal_runs/<folder> # one HTML page: loss, learning rates, rationales, code diffs
|
|
116
125
|
```
|
|
117
126
|
|
|
127
|
+
Each wrapped run prints a `profiler_start` / `profiler_stop` pair on stderr when a program starts: OPAL audits one
|
|
128
|
+
update of each new program. It is harmless.
|
|
129
|
+
|
|
118
130
|
The guide shows how to check the keep run against your plain run byte for byte, and how to use the Hugging Face
|
|
119
|
-
Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
|
|
131
|
+
Trainer: [Try it without an agent](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#try-it-without-an-agent).
|
|
120
132
|
|
|
121
133
|
## Prerequisites
|
|
122
134
|
|
|
@@ -128,7 +140,7 @@ Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
|
|
|
128
140
|
The short summaries of code changes in the report are not counted (`summarize=False` turns them off). A dry run
|
|
129
141
|
with `backend="keep"` needs neither and makes no model calls.
|
|
130
142
|
- **No internet or no login on the GPU machine?** Run the agent on your laptop or a login node and connect it to the
|
|
131
|
-
training job through a
|
|
143
|
+
training job through a mailbox folder, over ssh or a shared filesystem (see [the mailbox broker](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#gpu-machines-without-an-agent)).
|
|
132
144
|
|
|
133
145
|
## Results
|
|
134
146
|
|
|
@@ -137,16 +149,24 @@ Each number is one run, and each agent started from the task's own recipe:
|
|
|
137
149
|
| Example | Measure | Official recipe | Reactive optimizer |
|
|
138
150
|
|---|---|---|---|
|
|
139
151
|
| SiT-B/2, ImageNet 256, 400K updates | FID-50K ↓ | 35.7 (our evaluator) | 23.6 |
|
|
140
|
-
| RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.
|
|
141
|
-
| Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.
|
|
152
|
+
| RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.19 |
|
|
153
|
+
| Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.25 |
|
|
142
154
|
| modded-nanogpt record #36 | validation loss ↓ | 3.2781 | 3.2749 |
|
|
143
|
-
| LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 |
|
|
155
|
+
| LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 | 2.31 |
|
|
144
156
|
|
|
145
157
|
It does not always help: a run of modded-nanogpt on OPAL 0.2.0 ended at 3.2878, worse than record #36. Your
|
|
146
158
|
numbers may also differ from ours, because the agent can choose differently from one run to the next. The
|
|
147
|
-
[examples page](examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
|
|
159
|
+
[examples page](https://github.com/xingjian-bai/opal/blob/main/examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
|
|
148
160
|
one-GPU scripts, and how to rerun each setting.
|
|
149
161
|
|
|
162
|
+
**Long runs suit it best.** An agent decision takes about the same time in any run, about 40 s on average in ours,
|
|
163
|
+
so the waiting adds up to a roughly fixed cost. In our runs of several hours it came to 0.49 h of 3.75 h on RAEv2-B,
|
|
164
|
+
0.67 h of 5.65 h on Goldfish and 1.05 h of 11.2 h on SiT-B/2. In short runs it dominates: our one-GPU scripts, which
|
|
165
|
+
set 50 decisions, took 27 to 31 min with the agent against 1 to 3 min without, and with `blocking=False` they ended
|
|
166
|
+
before the first answer arrived. The small scripts are a quick way to try OPAL. If you have the compute, use it on runs
|
|
167
|
+
of a few hours or more, where `blocking=False` also
|
|
168
|
+
[kept the result](https://github.com/xingjian-bai/opal/blob/main/examples/README.md#training-without-waiting-for-the-agent).
|
|
169
|
+
|
|
150
170
|
Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
|
|
151
171
|
|
|
152
172
|
## How it works
|
|
@@ -167,8 +187,8 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
|
|
|
167
187
|
steps. If it raises an error, the run goes back to the previous program. A failure at its first step is undone
|
|
168
188
|
exactly when a copy of the parameters and optimizer state fits in `snapshot_limit_gb` (default 2); a later failure
|
|
169
189
|
is not undone, and the agent is told. Every version is kept in `programs/`.
|
|
170
|
-
- **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks
|
|
171
|
-
|
|
190
|
+
- **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks. That is
|
|
191
|
+
faster, and noisier.
|
|
172
192
|
- **What the agent sees.** Your goal sentence, the names and shapes of the parameters, the training losses, its own
|
|
173
193
|
analysis reports, and read-only copies of your training script and model code (`share_code=False` keeps them out).
|
|
174
194
|
Values you log are only recorded, unless you pass `show_agent=True`. With `validation=N` it also sees the
|
|
@@ -178,17 +198,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
|
|
|
178
198
|
|
|
179
199
|
| Option | Default | Meaning |
|
|
180
200
|
|---|---|---|
|
|
181
|
-
| `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](docs/GUIDE.md#cost-and-limits). |
|
|
201
|
+
| `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50, but at least once, even when one decision already waits longer than that. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#cost-and-limits). |
|
|
182
202
|
| `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
|
|
183
|
-
| `blocking` | `True` | `False`
|
|
203
|
+
| `blocking` | `True` | `False` to keep training while the agent thinks. |
|
|
184
204
|
| `max_cost_usd` | none | Stop asking the agent once it has spent this much. |
|
|
185
|
-
| `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](docs/GUIDE.md#cost-and-limits). |
|
|
205
|
+
| `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#cost-and-limits). |
|
|
186
206
|
| `prompt` | `"neutral"` | The agent's instructions: the facts of the run and the protocol, with no recommended values. `"aggressive"` adds a review of every group's hyperparameters at each decision. You can also pass the path of your own template. |
|
|
187
|
-
| `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](docs/GUIDE.md#what-it-can-and-cannot-change). |
|
|
207
|
+
| `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#what-it-can-and-cannot-change). |
|
|
188
208
|
| `eval_integrity` | on with `validation` | Flags a run whose program moves the parameters somewhere else for an evaluation and back afterwards. The agent is not told. |
|
|
189
209
|
|
|
190
210
|
Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
|
|
191
|
-
covered in the [guide](docs/GUIDE.md).
|
|
211
|
+
covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
|
|
192
212
|
|
|
193
213
|
### Experimental Codex backend
|
|
194
214
|
|
|
@@ -207,7 +227,7 @@ Code CLI against a local mock.
|
|
|
207
227
|
|
|
208
228
|
## Status and limitations
|
|
209
229
|
|
|
210
|
-
v0.3.
|
|
230
|
+
v0.3.7, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
|
|
211
231
|
Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
|
|
212
232
|
fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
|
|
213
233
|
tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
|
|
@@ -227,6 +247,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
|
|
|
227
247
|
|
|
228
248
|
## License
|
|
229
249
|
|
|
230
|
-
MIT, see [LICENSE](LICENSE).
|
|
250
|
+
MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
|
|
231
251
|
|
|
232
252
|
*The name:* OPAL stands for OPtimizer with an Agent in the Loop.
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
<p align="center">
|
|
7
7
|
<a href="https://xingjianbai.com/reactive-optimizers/">Blog post</a> ·
|
|
8
|
-
<a href="docs/GUIDE.md">Guide</a> ·
|
|
8
|
+
<a href="https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md">Guide</a> ·
|
|
9
9
|
<a href="#start-with-your-coding-agent">Start with your agent</a> ·
|
|
10
|
-
<a href="examples/README.md">Examples</a>
|
|
10
|
+
<a href="https://github.com/xingjian-bai/opal/blob/main/examples/README.md">Examples</a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
Most networks are trained with an optimizer that is fixed before training starts. OPAL (OPtimizer with an Agent in
|
|
@@ -19,22 +19,22 @@ program, which measures whatever the agent wants to look at.
|
|
|
19
19
|
The agent can change anything that can be written as optimizer code: the update rule of each parameter group, the
|
|
20
20
|
learning rates and their schedules, momentum and β₂, preconditioning, clipping, guards against loss spikes. It is
|
|
21
21
|
asked not to touch your model, data, loss, batch size, number of steps or evaluation. Its programs run inside your
|
|
22
|
-
training process, so this is a rule, not a sandbox (see [Safety](docs/GUIDE.md#safety)). Until the agent changes
|
|
22
|
+
training process, so this is a rule, not a sandbox (see [Safety](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#safety)). Until the agent changes
|
|
23
23
|
something, the wrapped run is bit-for-bit the same as the plain one.
|
|
24
24
|
|
|
25
25
|
## Start with your coding agent
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Open your own project in a coding agent (Claude Code, Codex, Cursor or similar) and paste this prompt. The agent
|
|
28
|
+
installs OPAL, wires it into your training script, asks you what it cannot know, and checks that nothing changed
|
|
29
|
+
before anything costs money.
|
|
30
30
|
|
|
31
31
|
```text
|
|
32
32
|
I want to train my model with OPAL, a reactive optimizer: a coding agent that rewrites the optimizer while the
|
|
33
|
-
model trains.
|
|
33
|
+
model trains. Its docs are at https://github.com/xingjian-bai/opal. Please set it up for this project:
|
|
34
34
|
|
|
35
|
-
1. Read
|
|
36
|
-
|
|
37
|
-
2. Install OPAL into the Python environment this project trains with (pip install -
|
|
35
|
+
1. Read the README there and skills/train-with-agent/SKILL.md, and follow the skill. Do not edit OPAL's own files;
|
|
36
|
+
configure everything through opal.wrap arguments.
|
|
37
|
+
2. Install OPAL into the Python environment this project trains with (pip install opal-optimizer), and
|
|
38
38
|
run `opal doctor` (free, offline) to check that torch and Claude Code are ready on the machine that will run
|
|
39
39
|
training; run `opal doctor --live` (one tiny paid agent call) only once I approve an agent run. If Claude Code is
|
|
40
40
|
missing or not logged in there, tell me what to run; do not log in for me.
|
|
@@ -46,8 +46,9 @@ model trains. OPAL is cloned at <PATH TO OPAL>. Please set it up for this projec
|
|
|
46
46
|
must match step for step. Show me both. If I only asked for a dry run, stop here.
|
|
47
47
|
7. Before the real run, time an update in the smoke test and run `opal estimate --total-steps N --sec-per-step S`;
|
|
48
48
|
show me what it prints (how long training will wait for the agent, also as a multiple of the run's own time, and
|
|
49
|
-
the cost). Ask me for a cost cap (max_cost_usd)
|
|
50
|
-
go-ahead; then give me the exact command to launch it, and how to
|
|
49
|
+
the cost). Ask me for a cost cap (max_cost_usd); leave decisions unset unless I ask, so that the waiting stays
|
|
50
|
+
within 4x the run's own time. Wait for my go-ahead; then give me the exact command to launch it, and how to
|
|
51
|
+
follow it (opal show, opal report).
|
|
51
52
|
```
|
|
52
53
|
|
|
53
54
|
## Start by hand
|
|
@@ -59,6 +60,11 @@ opal doctor # torch and Claude Code ready?
|
|
|
59
60
|
|
|
60
61
|
The package is called `opal-optimizer` on PyPI, because `opal` there is an unrelated web framework. Both install as `import opal`, so do not install both.
|
|
61
62
|
|
|
63
|
+
On a GPU machine, install OPAL into the environment you already train with, or install the PyTorch build for your
|
|
64
|
+
driver first ([pytorch.org](https://pytorch.org/get-started/locally/)). In an empty environment, `pip install
|
|
65
|
+
opal-optimizer` pulls the newest torch from PyPI, which may need a newer NVIDIA driver; `opal doctor` then shows
|
|
66
|
+
`CUDA available: False`.
|
|
67
|
+
|
|
62
68
|
```python
|
|
63
69
|
import torch, opal
|
|
64
70
|
|
|
@@ -90,8 +96,11 @@ opal show opal_runs/<folder> # one line per decision
|
|
|
90
96
|
opal report opal_runs/<folder> # one HTML page: loss, learning rates, rationales, code diffs
|
|
91
97
|
```
|
|
92
98
|
|
|
99
|
+
Each wrapped run prints a `profiler_start` / `profiler_stop` pair on stderr when a program starts: OPAL audits one
|
|
100
|
+
update of each new program. It is harmless.
|
|
101
|
+
|
|
93
102
|
The guide shows how to check the keep run against your plain run byte for byte, and how to use the Hugging Face
|
|
94
|
-
Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
|
|
103
|
+
Trainer: [Try it without an agent](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#try-it-without-an-agent).
|
|
95
104
|
|
|
96
105
|
## Prerequisites
|
|
97
106
|
|
|
@@ -103,7 +112,7 @@ Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
|
|
|
103
112
|
The short summaries of code changes in the report are not counted (`summarize=False` turns them off). A dry run
|
|
104
113
|
with `backend="keep"` needs neither and makes no model calls.
|
|
105
114
|
- **No internet or no login on the GPU machine?** Run the agent on your laptop or a login node and connect it to the
|
|
106
|
-
training job through a
|
|
115
|
+
training job through a mailbox folder, over ssh or a shared filesystem (see [the mailbox broker](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#gpu-machines-without-an-agent)).
|
|
107
116
|
|
|
108
117
|
## Results
|
|
109
118
|
|
|
@@ -112,16 +121,24 @@ Each number is one run, and each agent started from the task's own recipe:
|
|
|
112
121
|
| Example | Measure | Official recipe | Reactive optimizer |
|
|
113
122
|
|---|---|---|---|
|
|
114
123
|
| SiT-B/2, ImageNet 256, 400K updates | FID-50K ↓ | 35.7 (our evaluator) | 23.6 |
|
|
115
|
-
| RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.
|
|
116
|
-
| Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.
|
|
124
|
+
| RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.19 |
|
|
125
|
+
| Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.25 |
|
|
117
126
|
| modded-nanogpt record #36 | validation loss ↓ | 3.2781 | 3.2749 |
|
|
118
|
-
| LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 |
|
|
127
|
+
| LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 | 2.31 |
|
|
119
128
|
|
|
120
129
|
It does not always help: a run of modded-nanogpt on OPAL 0.2.0 ended at 3.2878, worse than record #36. Your
|
|
121
130
|
numbers may also differ from ours, because the agent can choose differently from one run to the next. The
|
|
122
|
-
[examples page](examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
|
|
131
|
+
[examples page](https://github.com/xingjian-bai/opal/blob/main/examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
|
|
123
132
|
one-GPU scripts, and how to rerun each setting.
|
|
124
133
|
|
|
134
|
+
**Long runs suit it best.** An agent decision takes about the same time in any run, about 40 s on average in ours,
|
|
135
|
+
so the waiting adds up to a roughly fixed cost. In our runs of several hours it came to 0.49 h of 3.75 h on RAEv2-B,
|
|
136
|
+
0.67 h of 5.65 h on Goldfish and 1.05 h of 11.2 h on SiT-B/2. In short runs it dominates: our one-GPU scripts, which
|
|
137
|
+
set 50 decisions, took 27 to 31 min with the agent against 1 to 3 min without, and with `blocking=False` they ended
|
|
138
|
+
before the first answer arrived. The small scripts are a quick way to try OPAL. If you have the compute, use it on runs
|
|
139
|
+
of a few hours or more, where `blocking=False` also
|
|
140
|
+
[kept the result](https://github.com/xingjian-bai/opal/blob/main/examples/README.md#training-without-waiting-for-the-agent).
|
|
141
|
+
|
|
125
142
|
Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
|
|
126
143
|
|
|
127
144
|
## How it works
|
|
@@ -142,8 +159,8 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
|
|
|
142
159
|
steps. If it raises an error, the run goes back to the previous program. A failure at its first step is undone
|
|
143
160
|
exactly when a copy of the parameters and optimizer state fits in `snapshot_limit_gb` (default 2); a later failure
|
|
144
161
|
is not undone, and the agent is told. Every version is kept in `programs/`.
|
|
145
|
-
- **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks
|
|
146
|
-
|
|
162
|
+
- **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks. That is
|
|
163
|
+
faster, and noisier.
|
|
147
164
|
- **What the agent sees.** Your goal sentence, the names and shapes of the parameters, the training losses, its own
|
|
148
165
|
analysis reports, and read-only copies of your training script and model code (`share_code=False` keeps them out).
|
|
149
166
|
Values you log are only recorded, unless you pass `show_agent=True`. With `validation=N` it also sees the
|
|
@@ -153,17 +170,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
|
|
|
153
170
|
|
|
154
171
|
| Option | Default | Meaning |
|
|
155
172
|
|---|---|---|
|
|
156
|
-
| `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](docs/GUIDE.md#cost-and-limits). |
|
|
173
|
+
| `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50, but at least once, even when one decision already waits longer than that. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#cost-and-limits). |
|
|
157
174
|
| `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
|
|
158
|
-
| `blocking` | `True` | `False`
|
|
175
|
+
| `blocking` | `True` | `False` to keep training while the agent thinks. |
|
|
159
176
|
| `max_cost_usd` | none | Stop asking the agent once it has spent this much. |
|
|
160
|
-
| `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](docs/GUIDE.md#cost-and-limits). |
|
|
177
|
+
| `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#cost-and-limits). |
|
|
161
178
|
| `prompt` | `"neutral"` | The agent's instructions: the facts of the run and the protocol, with no recommended values. `"aggressive"` adds a review of every group's hyperparameters at each decision. You can also pass the path of your own template. |
|
|
162
|
-
| `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](docs/GUIDE.md#what-it-can-and-cannot-change). |
|
|
179
|
+
| `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#what-it-can-and-cannot-change). |
|
|
163
180
|
| `eval_integrity` | on with `validation` | Flags a run whose program moves the parameters somewhere else for an evaluation and back afterwards. The agent is not told. |
|
|
164
181
|
|
|
165
182
|
Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
|
|
166
|
-
covered in the [guide](docs/GUIDE.md).
|
|
183
|
+
covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
|
|
167
184
|
|
|
168
185
|
### Experimental Codex backend
|
|
169
186
|
|
|
@@ -182,7 +199,7 @@ Code CLI against a local mock.
|
|
|
182
199
|
|
|
183
200
|
## Status and limitations
|
|
184
201
|
|
|
185
|
-
v0.3.
|
|
202
|
+
v0.3.7, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
|
|
186
203
|
Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
|
|
187
204
|
fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
|
|
188
205
|
tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
|
|
@@ -202,6 +219,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
|
|
|
202
219
|
|
|
203
220
|
## License
|
|
204
221
|
|
|
205
|
-
MIT, see [LICENSE](LICENSE).
|
|
222
|
+
MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
|
|
206
223
|
|
|
207
224
|
*The name:* OPAL stands for OPtimizer with an Agent in the Loop.
|
|
@@ -32,9 +32,8 @@ The picture shows `blocking=False`. With the default `blocking=True`, every rank
|
|
|
32
32
|
- A new program takes over at the next update boundary. On one GPU this happens at once. In DDP, rank 0 announces
|
|
33
33
|
it through an asynchronous broadcast every `sync_every` updates on a separate gloo group. The default is 20.
|
|
34
34
|
Every rank swaps at the same update.
|
|
35
|
-
- With `blocking=True`, all ranks wait at every decision, as in our published runs. With `blocking=
|
|
36
|
-
|
|
37
|
-
the loss turns non-finite. Waiting uses the gloo group, so a long agent turn does not trip an NCCL timeout.
|
|
35
|
+
- With `blocking=True`, all ranks wait at every decision, as in most of our published runs. With `blocking=False`, they wait
|
|
36
|
+
only once when the loss turns non-finite. Waiting uses the gloo group, so a long agent turn does not trip an NCCL timeout.
|
|
38
37
|
- A new updater runs on trial for `trial_steps` updates, by default 20. If it raises on any rank, every rank returns
|
|
39
38
|
to the previous updater. The fallback is the last updater that passed its trial, or the starting one.
|
|
40
39
|
An updater replaced during its trial does not become the fallback.
|
|
@@ -252,8 +251,7 @@ asked so far.
|
|
|
252
251
|
| `max_cost_usd` | none | Stop asking once Claude Code reports this spend. Checked before each decision. The Claude backend also caps each decision at the remainder with `--max-budget-usd`, which can overshoot by one model call. `backend_options=dict(max_budget_usd=...)` caps each decision. |
|
|
253
252
|
| `every` | 50 | Updates between checks. Decisions and the non-finite check happen at checks. |
|
|
254
253
|
| `backend_options` | `{}` | `model=` defaults to `claude-opus-5-5`, `effort=` to `high`, as in the blog's runs. Another model or effort prints a warning at the start that the blog's results may differ. `timeout=` is seconds per decision, default 3600. `shell_timeout=` is seconds per agent shell command. `max_budget_usd=` caps each decision through Claude Code's `--max-budget-usd`. It can overshoot by one model call. `retries=` retries a failed turn, such as a tool crash, from the programs it started with. Pauses start at `backoff=15` s and double up to `backoff_max=600` s while `timeout` allows. `None`, the default, retries as often as that allows. `0` never retries. Each failure's error goes to the run's log, `decisions/NNN/attempts.jsonl` and the broker's log. The broker flag is `opal broker --retries N`. An unavailable agent has a separate wait, described below. `unavailable_timeout=` sets its deadline. |
|
|
255
|
-
| `blocking` | True | Training waits for each decision, as in our published runs. False keeps training.
|
|
256
|
-
| `max_lag_updates` | 50 | With `blocking="auto"`, the predicted lag in updates above which training waits. |
|
|
254
|
+
| `blocking` | True | Training waits for each decision, as in most of our published runs. False keeps training. |
|
|
257
255
|
| `share_code` | True | False keeps the training script and source files out of the agent's folder. |
|
|
258
256
|
| `schedule` | "front" | When decisions are asked. The learning-rate schedule is `scheduler`. "front" puts a fifth evenly over the first 5% of training and the rest evenly over the remainder. "uniform" asks every total_steps // decisions updates. Segments such as `[(0.05, 20), (1.0, 30)]` give an end fraction and count: here 20 in the first 5%, then 30 over the rest. Update numbers such as `[500, 1000, 5000]` give the exact updates. A list sets the count: omit `decisions` or make it agree, or `wrap` raises. Each decision moves to the next check, every `every` updates. Two on one check merge. None falls in the last `every` updates. The first log line gives the remaining count. |
|
|
259
257
|
| `show_next_decision` | on with the neutral and aggressive prompts | True gives the next scheduled decision's update in each message. False turns it off. |
|
|
@@ -295,15 +293,10 @@ No new decision is asked meanwhile. A failed turn, such as a crash, time limit o
|
|
|
295
293
|
instead of the unavailable-agent wait.
|
|
296
294
|
|
|
297
295
|
By default, `blocking=True` waits for the answer at each decision. Its edit takes effect from the update where the
|
|
298
|
-
decision was asked, and the agent's thinking adds to wall time. `blocking=False`
|
|
296
|
+
decision was asked, and the agent's thinking adds to wall time. `blocking=False` allows faster
|
|
299
297
|
runs. Without waiting, an answer takes effect answer time / time per update updates after the state the agent saw.
|
|
300
298
|
No new decision is asked while one is in progress, so fast runs may ask far fewer times.
|
|
301
299
|
|
|
302
|
-
`blocking="auto"` waits when the answer would arrive more than `max_lag_updates` updates late, by default 50.
|
|
303
|
-
It divides the median of the last 5 answer times by the time per update. That update time is the median over the last
|
|
304
|
-
5 check intervals, excluding pauses. It always waits at the first decision.
|
|
305
|
-
Each choice, its reason and its lag are in `events.jsonl` and the report.
|
|
306
|
-
|
|
307
300
|
## The report
|
|
308
301
|
|
|
309
302
|
Each run keeps `report.html` in its folder. It shows the loss with a tick per decision, your `opt.log` values,
|
|
@@ -328,7 +321,7 @@ Private locations and recorded identities are omitted from messages, code and pl
|
|
|
328
321
|
calls.
|
|
329
322
|
|
|
330
323
|
Cached descriptions and tags come from Claude Opus at low effort through the Claude Code CLI, with no tools.
|
|
331
|
-
One call per code change costs
|
|
324
|
+
One call per code change costs a few cents ($0.02 to $0.06 in our checks). It is recorded in `decisions/NNN/summary.json` and excluded from
|
|
332
325
|
`max_cost_usd`. A change can have any number of these tags:
|
|
333
326
|
|
|
334
327
|
- `common practice`: standard recipes, such as Muon, warmup, cosine or linear decay, clipping and AdamW betas.
|
|
@@ -415,7 +408,7 @@ schedule.
|
|
|
415
408
|
The broker keeps waiting. When `close()` returns, the final `report.html` is written.
|
|
416
409
|
It waits up to 30 s for pending Claude summaries.
|
|
417
410
|
- `blocking=True` is deterministic given the agent's answers. Replaying a final program with `opal.replay` is
|
|
418
|
-
deterministic too. With `blocking=False
|
|
411
|
+
deterministic too. With `blocking=False`, timing decides when an edit takes effect.
|
|
419
412
|
Two runs with the same answers can differ.
|
|
420
413
|
- To turn the agent off mid-run, resume with `backend="keep"` or start a fresh run with `opal.replay(...)`.
|
|
421
414
|
- For the Hugging Face Trainer, use `opal.hf.OpalTrainer` in place of `Trainer`.
|
|
@@ -485,6 +478,8 @@ On the GPU machine, `opal doctor --backend mailbox:/scratch/me/opal-box` checks
|
|
|
485
478
|
whether a broker serves it. That machine needs no Claude Code. Set the model and effort on the broker with
|
|
486
479
|
`opal broker ... --model claude-opus-5-5 --effort high`, the defaults, rather than in the run's `backend_options`.
|
|
487
480
|
The broker's machine must stay awake for the whole run. On a Mac, use `caffeinate -i opal broker ...`.
|
|
481
|
+
The broker's ssh runs with `BatchMode=yes`, so it cannot ask for a password or a second factor. Log in with a key,
|
|
482
|
+
or open a connection it can share before you start it: `ssh -fNM -o ControlPath=~/.ssh/opal-%r@%h-%p me@gpu-host`.
|
|
488
483
|
The run waits while no broker answers, as described under "When the agent is unavailable".
|
|
489
484
|
Mailbox runs do not make code-change summaries during training.
|
|
490
485
|
Run `opal report <run> --summarize` where Claude Code is logged in to add them.
|
|
@@ -6,26 +6,39 @@ number comes from.
|
|
|
6
6
|
|
|
7
7
|
## Results by setting
|
|
8
8
|
|
|
9
|
-
Each number is one run, and each agent started from the task's own recipe.
|
|
10
|
-
from the harness this package grew out of
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
| [
|
|
17
|
-
| [
|
|
18
|
-
| [
|
|
19
|
-
|
|
|
20
|
-
| LightningDiT-B/1, ImageNet 256, 160 epochs (200K updates), 8× H100 | FID-50K ↓, with guidance / without | 3.22 / 10.12 | – | – | 2.31 / 7.56 |
|
|
9
|
+
Each number is one run, and each agent started from the task's own recipe. Runs on OPAL 0.3 used the neutral prompt
|
|
10
|
+
and Opus 5.5 at effort high. SiT-B/2 comes from the harness this package grew out of. In all of these runs, training
|
|
11
|
+
paused at each decision while the agent answered, which is OPAL's default.
|
|
12
|
+
|
|
13
|
+
| Example | Measure | Official recipe | Reactive optimizer | Made with |
|
|
14
|
+
|---|---|---|---|---|
|
|
15
|
+
| [SiT-B/2](sit_b2/), ImageNet 256, 400K updates, 8× H100 | FID-50K ↓ | 35.7 with our evaluator (the SiT paper reports 33.0 with the ADM evaluator; Muon: 30.3) | 23.6 | earlier harness |
|
|
16
|
+
| [RAEv2-B](raev2_b/), ImageNet 256, 20 epochs, 8× H100 | FID-50K ↓ | 3.39 | 3.19 | OPAL 0.3 |
|
|
17
|
+
| [Goldfish 125M GPT](goldfish_swahili/), 1 GB of Swahili, 1× H100 | FLORES-200 bits ↓ | 79.37 | 77.25 | OPAL 0.3 |
|
|
18
|
+
| [modded-nanogpt record #36](modded_nanogpt/), 162M GPT, 8× H100 | validation loss ↓ | 3.2781 | 3.2749 (a small gain: the track assumes σ = 0.0013 per run) | OPAL 0.1.0 |
|
|
19
|
+
| LightningDiT-B/1, ImageNet 256, 160 epochs (200K updates), 8× H100 | FID-50K ↓, with guidance / without | 3.22 / 10.12 | 2.31 / 7.56 | OPAL 0.3 |
|
|
21
20
|
|
|
22
21
|
It does not always help. On modded-nanogpt, a heavily tuned benchmark, a run on OPAL 0.2.0 with the neutral prompt
|
|
23
22
|
ended at 3.2878, worse than record #36. Your numbers may also differ from ours, because the agent can make different
|
|
24
|
-
choices from one run to the next and hosted models change between versions. Still, most of our
|
|
23
|
+
choices from one run to the next and hosted models change between versions. Still, most of our runs on OPAL 0.3
|
|
25
24
|
ended ahead of the recipe they started from. The [blog post](https://xingjianbai.com/reactive-optimizers/) has more.
|
|
26
25
|
|
|
27
26
|
LightningDiT-B/1 has no folder here. Both runs used the upstream LightningDiT training code and its 64-epoch config, changed to model B/1, 200,000 updates and `use_qknorm: true` (without query-key normalization the official run diverged near update 194,000). The official recipe trains with AdamW at a constant learning rate of 2e-4. FID-50K uses 50,000 samples against the ADM reference statistics, Euler sampling with 250 steps and timestep shift 0.3; "with guidance" is the upstream setting, a guidance scale of 10 from t = 0.10 on. The LightningDiT paper reports FID-10K for this model, which is not comparable.
|
|
28
27
|
|
|
28
|
+
## Training without waiting for the agent
|
|
29
|
+
|
|
30
|
+
With `blocking=False`, training continues on the current programs while the agent answers, and an edit takes effect
|
|
31
|
+
when its answer arrives. One run on RAEv2-B, OPAL 0.3, 8× H100:
|
|
32
|
+
|
|
33
|
+
| RAEv2-B, 20 epochs | Official recipe | OPAL, training waits (default) | OPAL, `blocking=False` |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| FID-50K ↓ after 25,000 updates | 3.39 | 3.19 | 3.17 |
|
|
36
|
+
| Run time, including evaluations | 3.21 h | 3.75 h, of which 0.49 h waiting for the agent | 3.22 h |
|
|
37
|
+
| Decisions asked / that changed code | – | 50 / 6 | 43 / 3 |
|
|
38
|
+
| Agent cost | – | $13.30 | $9.71 |
|
|
39
|
+
|
|
40
|
+
Without waiting, fewer decisions are asked, because no new decision is asked while one is in progress.
|
|
41
|
+
|
|
29
42
|
## Small examples (CPU)
|
|
30
43
|
|
|
31
44
|
`mnist_mlp.py` trains an MLP on MNIST with AdamW and a cosine schedule for 5 epochs. It doubles as the install
|
|
@@ -65,9 +65,8 @@ parser.add_argument(
|
|
|
65
65
|
nargs="?",
|
|
66
66
|
const="true",
|
|
67
67
|
default="true",
|
|
68
|
-
choices=["true", "false"
|
|
69
|
-
help="true (default): training waits for each answer; false: it keeps going
|
|
70
|
-
"only when an answer would land late",
|
|
68
|
+
choices=["true", "false"],
|
|
69
|
+
help="true (default): training waits for each answer; false: it keeps going",
|
|
71
70
|
)
|
|
72
71
|
parser.add_argument("--max_cost_usd", type=float, default=15.0, help="stop asking the agent after this much spend")
|
|
73
72
|
parser.add_argument(
|
|
@@ -52,9 +52,8 @@ parser.add_argument(
|
|
|
52
52
|
nargs="?",
|
|
53
53
|
const="true",
|
|
54
54
|
default="true",
|
|
55
|
-
choices=["true", "false"
|
|
56
|
-
help="true (default): training waits for each answer; false: it keeps going
|
|
57
|
-
"only when an answer would land late",
|
|
55
|
+
choices=["true", "false"],
|
|
56
|
+
help="true (default): training waits for each answer; false: it keeps going",
|
|
58
57
|
) # opal
|
|
59
58
|
args = parser.parse_args()
|
|
60
59
|
|
|
@@ -10,17 +10,13 @@ split at the middle character. This follows upstream's `eval_code/flores_eval_go
|
|
|
10
10
|
|
|
11
11
|
| FLORES-200 bits ↓ (one run each) | Official recipe | Reactive optimizer |
|
|
12
12
|
|---|---|---|
|
|
13
|
-
| after the last update | 79.37 | **77.
|
|
13
|
+
| after the last update | 79.37 | **77.25** |
|
|
14
14
|
|
|
15
|
-
In this run the agent ended
|
|
15
|
+
In this run the agent ended 2.12 bits below the official recipe.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
The agent run used OPAL 0.3 with the neutral prompt: 51 decisions of which 11 changed the program,
|
|
18
18
|
$17.41, 5 h 39 min on one H100. This wall time includes evaluations and waiting for the agent.
|
|
19
19
|
|
|
20
|
-
The table above comes from the earlier training system OPAL grew from in October 2026.
|
|
21
|
-
It used Claude Code, Opus 5.5 at effort `high`, with that system's neutral prompt and 100 decisions every 793 updates.
|
|
22
|
-
Training paused at each decision.
|
|
23
|
-
|
|
24
20
|
## Official code and data
|
|
25
21
|
|
|
26
22
|
- Training uses [tylerachang/goldfish](https://github.com/tylerachang/goldfish) at `d4a518a` with
|
|
@@ -73,9 +69,9 @@ Then run it once with `backend="keep"` for a few hundred updates: its losses mus
|
|
|
73
69
|
| | Official recipe | Reactive optimizer |
|
|
74
70
|
|---|---|---|
|
|
75
71
|
| Hardware | 1× H100 80GB | 1× H100 80GB |
|
|
76
|
-
| Reported run time, including agent waits | 4.8 h | 5.
|
|
77
|
-
| Agent cost | – | $17.
|
|
78
|
-
| Decisions answered / that changed code | – |
|
|
79
|
-
| FLORES-200 bits | 79.374 | 77.
|
|
72
|
+
| Reported run time, including agent waits | 4.8 h | 5.65 h, of which 0.67 h waiting for the agent |
|
|
73
|
+
| Agent cost | – | $17.41 |
|
|
74
|
+
| Decisions answered / that changed code | – | 51 / 11 |
|
|
75
|
+
| FLORES-200 bits | 79.374 | 77.250 |
|
|
80
76
|
|
|
81
|
-
The official 4.8 h sums logged update windows, including evaluations. The agent time includes its 0.
|
|
77
|
+
The official 4.8 h sums logged update windows, including evaluations. The agent time includes its 0.67 h wait.
|
|
@@ -70,9 +70,8 @@ parser.add_argument(
|
|
|
70
70
|
nargs="?",
|
|
71
71
|
const="true",
|
|
72
72
|
default="true",
|
|
73
|
-
choices=["true", "false"
|
|
74
|
-
help="true (default): training waits for each answer; false: it keeps going
|
|
75
|
-
"only when an answer would land late",
|
|
73
|
+
choices=["true", "false"],
|
|
74
|
+
help="true (default): training waits for each answer; false: it keeps going",
|
|
76
75
|
)
|
|
77
76
|
parser.add_argument("--max_cost_usd", type=float, default=15.0, help="stop asking the agent after this much spend")
|
|
78
77
|
parser.add_argument(
|
|
@@ -51,9 +51,8 @@ parser.add_argument(
|
|
|
51
51
|
nargs="?",
|
|
52
52
|
const="true",
|
|
53
53
|
default="true",
|
|
54
|
-
choices=["true", "false"
|
|
55
|
-
help="true (default): training waits for each answer; false: it keeps going
|
|
56
|
-
"only when an answer would land late",
|
|
54
|
+
choices=["true", "false"],
|
|
55
|
+
help="true (default): training waits for each answer; false: it keeps going",
|
|
57
56
|
) # opal
|
|
58
57
|
args = parser.parse_args()
|
|
59
58
|
|
|
@@ -14,7 +14,7 @@ The gap is about 2.5 times that, from one run on each side. Record #36's publish
|
|
|
14
14
|
It appears in row 36 of the [track README on master](https://github.com/KellerJordan/modded-nanogpt/blob/master/records/track_3_optimization/README.md).
|
|
15
15
|
The README at the pinned commit below predates that row and lists only records 1 to 12.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
The 3.2749 run used OPAL 0.1.0 with that version's default prompt, which this release
|
|
18
18
|
no longer ships: Claude Code (Opus 5.5, effort `high`), 100 decisions requested with checks every 10 updates (97 after
|
|
19
19
|
snapping, all answered), training paused at each decision. A later run on OPAL 0.2.0 with the neutral prompt (50 evenly
|
|
20
20
|
spaced decisions, 2 GPUs, a goal worded as reaching the target loss) ended at **3.2878, worse than record #36**.
|