opal-optimizer 0.3.5__tar.gz → 0.3.6__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.6}/CHANGELOG.md +10 -0
- {opal_optimizer-0.3.5/opal_optimizer.egg-info → opal_optimizer-0.3.6}/PKG-INFO +34 -22
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/README.md +30 -21
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/docs/GUIDE.md +3 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/_version.py +1 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/mailbox.py +7 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/cli.py +11 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/estimate.py +15 -5
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/report_privacy.py +1 -1
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/wrapper.py +1 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6/opal_optimizer.egg-info}/PKG-INFO +34 -22
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/pyproject.toml +5 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/skills/train-with-agent/SKILL.md +11 -4
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/LICENSE +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/MANIFEST.in +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/README.md +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/cifar10_resnet.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/diffusion_mnist.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/goldfish_swahili/README.md +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/gpt_small.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/mnist_mlp.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/modded_nanogpt/README.md +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/modded_nanogpt/opal.patch +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/README.md +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/records.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/resnet.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/splits.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/train.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/raev2_b/README.md +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/sit_b2/README.md +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/sit_b2/opal.patch +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/__init__.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/agent.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/__init__.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/_codex_client.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/_privacy.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/base.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/claude.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/codex.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/unavailable.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/context.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/defaults.json +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/defaults.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/hf.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/__init__.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/aggressive.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/audit.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/boundary.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/checkpoint.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/ddp.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/integrity.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/nested.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/neutral_analysis.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/precision.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/recovery.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/schedulers.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/setup.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/skip.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/taskfacts.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/waiting.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/programs.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/prompts/example_updater.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/prompts/neutral.md +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/report.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/report_page.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/results.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/schedule.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/summaries.py +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/SOURCES.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/dependency_links.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/entry_points.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/requires.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/top_level.txt +0 -0
- {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/setup.cfg +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.6 (2026-10-22)
|
|
4
|
+
|
|
5
|
+
- 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.
|
|
6
|
+
- `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.
|
|
7
|
+
- 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.
|
|
8
|
+
- `opal estimate` no longer offers a decision count that still waits more than 4x, and says when training ends before the first answer.
|
|
9
|
+
- The report records the training waiting time for user scripts.
|
|
10
|
+
- The privacy filter keeps numbers such as `/1000`.
|
|
11
|
+
- The PyPI page links to the repository, the guide and the blog post, and README links work outside GitHub.
|
|
12
|
+
|
|
3
13
|
## 0.3.5 (2026-10-21)
|
|
4
14
|
|
|
5
15
|
- 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.6
|
|
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
|
|
|
@@ -144,7 +156,7 @@ Each number is one run, and each agent started from the task's own recipe:
|
|
|
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
|
|
|
150
162
|
Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
|
|
@@ -178,17 +190,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
|
|
|
178
190
|
|
|
179
191
|
| Option | Default | Meaning |
|
|
180
192
|
|---|---|---|
|
|
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). |
|
|
193
|
+
| `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
194
|
| `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
|
|
183
195
|
| `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
|
|
184
196
|
| `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). |
|
|
197
|
+
| `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
198
|
| `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). |
|
|
199
|
+
| `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
200
|
| `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
201
|
|
|
190
202
|
Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
|
|
191
|
-
covered in the [guide](docs/GUIDE.md).
|
|
203
|
+
covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
|
|
192
204
|
|
|
193
205
|
### Experimental Codex backend
|
|
194
206
|
|
|
@@ -207,7 +219,7 @@ Code CLI against a local mock.
|
|
|
207
219
|
|
|
208
220
|
## Status and limitations
|
|
209
221
|
|
|
210
|
-
v0.3.
|
|
222
|
+
v0.3.6, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
|
|
211
223
|
Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
|
|
212
224
|
fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
|
|
213
225
|
tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
|
|
@@ -227,6 +239,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
|
|
|
227
239
|
|
|
228
240
|
## License
|
|
229
241
|
|
|
230
|
-
MIT, see [LICENSE](LICENSE).
|
|
242
|
+
MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
|
|
231
243
|
|
|
232
244
|
*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
|
|
|
@@ -119,7 +128,7 @@ Each number is one run, and each agent started from the task's own recipe:
|
|
|
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
|
|
|
125
134
|
Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
|
|
@@ -153,17 +162,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
|
|
|
153
162
|
|
|
154
163
|
| Option | Default | Meaning |
|
|
155
164
|
|---|---|---|
|
|
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). |
|
|
165
|
+
| `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
166
|
| `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
|
|
158
167
|
| `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
|
|
159
168
|
| `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). |
|
|
169
|
+
| `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
170
|
| `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). |
|
|
171
|
+
| `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
172
|
| `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
173
|
|
|
165
174
|
Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
|
|
166
|
-
covered in the [guide](docs/GUIDE.md).
|
|
175
|
+
covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
|
|
167
176
|
|
|
168
177
|
### Experimental Codex backend
|
|
169
178
|
|
|
@@ -182,7 +191,7 @@ Code CLI against a local mock.
|
|
|
182
191
|
|
|
183
192
|
## Status and limitations
|
|
184
193
|
|
|
185
|
-
v0.3.
|
|
194
|
+
v0.3.6, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
|
|
186
195
|
Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
|
|
187
196
|
fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
|
|
188
197
|
tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
|
|
@@ -202,6 +211,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
|
|
|
202
211
|
|
|
203
212
|
## License
|
|
204
213
|
|
|
205
|
-
MIT, see [LICENSE](LICENSE).
|
|
214
|
+
MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
|
|
206
215
|
|
|
207
216
|
*The name:* OPAL stands for OPtimizer with an Agent in the Loop.
|
|
@@ -328,7 +328,7 @@ Private locations and recorded identities are omitted from messages, code and pl
|
|
|
328
328
|
calls.
|
|
329
329
|
|
|
330
330
|
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
|
|
331
|
+
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
332
|
`max_cost_usd`. A change can have any number of these tags:
|
|
333
333
|
|
|
334
334
|
- `common practice`: standard recipes, such as Muon, warmup, cosine or linear decay, clipping and AdamW betas.
|
|
@@ -485,6 +485,8 @@ On the GPU machine, `opal doctor --backend mailbox:/scratch/me/opal-box` checks
|
|
|
485
485
|
whether a broker serves it. That machine needs no Claude Code. Set the model and effort on the broker with
|
|
486
486
|
`opal broker ... --model claude-opus-5-5 --effort high`, the defaults, rather than in the run's `backend_options`.
|
|
487
487
|
The broker's machine must stay awake for the whole run. On a Mac, use `caffeinate -i opal broker ...`.
|
|
488
|
+
The broker's ssh runs with `BatchMode=yes`, so it cannot ask for a password or a second factor. Log in with a key,
|
|
489
|
+
or open a connection it can share before you start it: `ssh -fNM -o ControlPath=~/.ssh/opal-%r@%h-%p me@gpu-host`.
|
|
488
490
|
The run waits while no broker answers, as described under "When the agent is unavailable".
|
|
489
491
|
Mailbox runs do not make code-change summaries during training.
|
|
490
492
|
Run `opal report <run> --summarize` where Claude Code is logged in to add them.
|
|
@@ -490,7 +490,13 @@ class _Folder:
|
|
|
490
490
|
def _sh(self, cmd, data=None):
|
|
491
491
|
# without data, give ssh an empty stdin: it would otherwise read (and forward) the broker's own stdin
|
|
492
492
|
io = dict(input=data) if data is not None else dict(stdin=subprocess.DEVNULL)
|
|
493
|
-
|
|
493
|
+
p = subprocess.run(self.base + [cmd], capture_output=True, timeout=120, **io)
|
|
494
|
+
if p.returncode == 255 and b"Permission denied" in p.stderr: # BatchMode: ssh cannot ask for a password
|
|
495
|
+
p.stderr = p.stderr.strip() + (
|
|
496
|
+
" (the broker's ssh cannot ask for a password: use an ssh key, or open a connection it can share "
|
|
497
|
+
f"first: ssh -fNM -o ControlPath=~/.ssh/opal-%r@%h-%p {self.ssh})"
|
|
498
|
+
).encode()
|
|
499
|
+
return p
|
|
494
500
|
|
|
495
501
|
def read(self, name):
|
|
496
502
|
if not self.ssh:
|
|
@@ -148,6 +148,7 @@ def doctor_mailbox(spec):
|
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
def doctor(a):
|
|
151
|
+
import os
|
|
151
152
|
import platform
|
|
152
153
|
import shutil
|
|
153
154
|
import subprocess
|
|
@@ -163,6 +164,13 @@ def doctor(a):
|
|
|
163
164
|
f"torch {torch.__version__}; CUDA available: {torch.cuda.is_available()}"
|
|
164
165
|
+ (f" ({torch.cuda.device_count()} x {torch.cuda.get_device_name(0)})" if torch.cuda.is_available() else "")
|
|
165
166
|
)
|
|
167
|
+
if not torch.cuda.is_available() and (shutil.which("nvidia-smi") or os.path.exists("/dev/nvidiactl")):
|
|
168
|
+
built = f"built for CUDA {torch.version.cuda}" if torch.version.cuda else "a CPU-only build"
|
|
169
|
+
print(
|
|
170
|
+
f"this machine has an NVIDIA GPU, but torch {torch.__version__} ({built}) cannot use it: for GPU training, "
|
|
171
|
+
"install the torch build for your driver first (https://pytorch.org/get-started/locally/), or check "
|
|
172
|
+
"CUDA_VISIBLE_DEVICES"
|
|
173
|
+
)
|
|
166
174
|
for name in ("codex" if a.backend == "codex" else "claude",):
|
|
167
175
|
exe = shutil.which(name)
|
|
168
176
|
ver = ""
|
|
@@ -209,7 +217,9 @@ def doctor(a):
|
|
|
209
217
|
reply = (out.get("text") or "").strip()
|
|
210
218
|
ok = out.get("returncode") == 0 and "READY" in reply
|
|
211
219
|
print(
|
|
212
|
-
f"live call: {'ok' if ok else 'failed'} ({out.get('seconds', 0):.0f} s
|
|
220
|
+
f"live call: {'ok' if ok else 'failed'} ({out.get('seconds', 0):.0f} s"
|
|
221
|
+
+ (f", ${out['cost_usd']:.2f}" if isinstance(out.get("cost_usd"), (int, float)) else "")
|
|
222
|
+
+ f"; reply {reply[:80]!r}; error {out.get('error')})"
|
|
213
223
|
)
|
|
214
224
|
if a.backend == "codex":
|
|
215
225
|
print(f"Codex thread: {out.get('session')}; provider rollout: {out.get('rollout')}")
|
|
@@ -102,16 +102,26 @@ def lines(total_steps, sec_per_step, decisions, blocking=True, max_lag=50, model
|
|
|
102
102
|
f"decisions not set: {decisions} scheduled instead of {capped_from}, so that the expected waiting stays "
|
|
103
103
|
f"within {MAX_WAIT_FACTOR:g}x the run's own time (set decisions= to choose)"
|
|
104
104
|
)
|
|
105
|
-
|
|
105
|
+
fewer = capped_decisions(decisions, run, blocking, sec_per_step, max_lag) if over else decisions
|
|
106
|
+
# a run shorter than one typical answer ends before the answer arrives when training does not wait for it
|
|
107
|
+
no_wait = (
|
|
108
|
+
"blocking=False lets training go on while the agent thinks"
|
|
109
|
+
if run >= FIRST_SECONDS[1]
|
|
110
|
+
else "blocking=False would not help: training would end before the first answer arrives"
|
|
111
|
+
)
|
|
112
|
+
if over and (
|
|
113
|
+
decisions <= 1 or not explicit or waiting(fewer, blocking, sec_per_step, max_lag) > MAX_WAIT_FACTOR * run
|
|
114
|
+
):
|
|
115
|
+
n = decisions if not explicit and decisions > 1 else 1
|
|
106
116
|
out.append(
|
|
107
|
-
f"even {'one decision' if
|
|
108
|
-
f"than {MAX_WAIT_FACTOR:g}x the run's own time;
|
|
117
|
+
f"even {'one decision' if n <= 1 else f'{n} decisions'} wait{'s' * (n <= 1)} more "
|
|
118
|
+
f"than {MAX_WAIT_FACTOR:g}x the run's own time; {no_wait}"
|
|
109
119
|
)
|
|
110
120
|
elif over:
|
|
111
121
|
out.append(
|
|
112
122
|
f"the expected waiting is more than {MAX_WAIT_FACTOR:g}x the run's own time: ask fewer times "
|
|
113
|
-
f"(decisions={
|
|
114
|
-
f"
|
|
123
|
+
f"(decisions={fewer} keeps it within {MAX_WAIT_FACTOR:g}x) or let training go on while the agent thinks "
|
|
124
|
+
f"(blocking=False)"
|
|
115
125
|
)
|
|
116
126
|
if (model, effort) != (MODEL, EFFORT):
|
|
117
127
|
out.append(
|
|
@@ -125,7 +125,7 @@ class Privacy:
|
|
|
125
125
|
def _path(self, match):
|
|
126
126
|
path = match[0]
|
|
127
127
|
# Avoid eating arithmetic such as `1 / 2`, or regex syntax such as /\w+/.
|
|
128
|
-
if path in ("/", "//"):
|
|
128
|
+
if path in ("/", "//") or not re.search(r"[A-Za-z]", path): # also `(u-1)/1000`: no letters, no location
|
|
129
129
|
return path
|
|
130
130
|
for root in sorted(self.roots, key=len, reverse=True):
|
|
131
131
|
if path == root:
|
|
@@ -1647,6 +1647,7 @@ class AgentOptimizer(
|
|
|
1647
1647
|
decisions=self.worker.k if self.worker else 0,
|
|
1648
1648
|
cost_usd=self.worker.recorded_cost if self.worker else 0,
|
|
1649
1649
|
unknown_cost_decisions=sorted(self.worker.unknown_cost_decisions) if self.worker else [],
|
|
1650
|
+
agent_wait_seconds=round(self.agent_wait_seconds, 1), # the report's "training waiting time"
|
|
1650
1651
|
**(dict(pending_decision=self.worker.k) if settling else {}),
|
|
1651
1652
|
**(dict(usage=self.worker.usage) if self.worker and self.worker.usage is not None else {}),
|
|
1652
1653
|
**(dict(eval_integrity=checked) if checked else {}),
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opal-optimizer
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
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
|
|
|
@@ -144,7 +156,7 @@ Each number is one run, and each agent started from the task's own recipe:
|
|
|
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
|
|
|
150
162
|
Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
|
|
@@ -178,17 +190,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
|
|
|
178
190
|
|
|
179
191
|
| Option | Default | Meaning |
|
|
180
192
|
|---|---|---|
|
|
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). |
|
|
193
|
+
| `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
194
|
| `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
|
|
183
195
|
| `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
|
|
184
196
|
| `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). |
|
|
197
|
+
| `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
198
|
| `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). |
|
|
199
|
+
| `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
200
|
| `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
201
|
|
|
190
202
|
Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
|
|
191
|
-
covered in the [guide](docs/GUIDE.md).
|
|
203
|
+
covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
|
|
192
204
|
|
|
193
205
|
### Experimental Codex backend
|
|
194
206
|
|
|
@@ -207,7 +219,7 @@ Code CLI against a local mock.
|
|
|
207
219
|
|
|
208
220
|
## Status and limitations
|
|
209
221
|
|
|
210
|
-
v0.3.
|
|
222
|
+
v0.3.6, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
|
|
211
223
|
Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
|
|
212
224
|
fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
|
|
213
225
|
tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
|
|
@@ -227,6 +239,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
|
|
|
227
239
|
|
|
228
240
|
## License
|
|
229
241
|
|
|
230
|
-
MIT, see [LICENSE](LICENSE).
|
|
242
|
+
MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
|
|
231
243
|
|
|
232
244
|
*The name:* OPAL stands for OPtimizer with an Agent in the Loop.
|
|
@@ -17,6 +17,11 @@ dependencies = [
|
|
|
17
17
|
"numpy>=1.26.4", # torch's NumPy bridge
|
|
18
18
|
]
|
|
19
19
|
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/xingjian-bai/opal"
|
|
22
|
+
Documentation = "https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md"
|
|
23
|
+
"Blog post" = "https://xingjianbai.com/reactive-optimizers/"
|
|
24
|
+
|
|
20
25
|
[project.optional-dependencies]
|
|
21
26
|
examples = [
|
|
22
27
|
"torchvision>=0.20; python_version < '3.13'",
|
|
@@ -18,11 +18,14 @@ Keep test data and labels out of the training process, evaluate the final checkp
|
|
|
18
18
|
1. **Find the loop.** Locate the training script and the place where the optimizer and LR scheduler are built and
|
|
19
19
|
where `optimizer.step()` is called. Note: number of optimizer steps in the run (`total_steps`), data-parallel setup
|
|
20
20
|
(single GPU, DDP, Accelerate, HF Trainer, Lightning, FSDP/DeepSpeed), gradient clipping in the loop, GradScaler,
|
|
21
|
-
gradient accumulation, EMA, checkpoint save/load
|
|
21
|
+
gradient accumulation, EMA, checkpoint save/load, and whether parameter groups are added during training (for
|
|
22
|
+
example layers unfrozen later).
|
|
22
23
|
2. **Check it is supported.** FSDP, ZeRO/DeepSpeed, tensor or pipeline parallelism and JAX are not supported: stop
|
|
23
24
|
and tell the user. With an HF Trainer, use `opal.hf.OpalTrainer` (docs/GUIDE.md, "Hugging Face Trainer").
|
|
24
25
|
With Accelerate, wrap after `accelerator.prepare` (GUIDE, "Accelerate"). Lightning is untested: say so before
|
|
25
|
-
continuing.
|
|
26
|
+
continuing. All parameter groups must exist before `wrap`: for layers unfrozen later, create their group up front
|
|
27
|
+
with `requires_grad=False` and set it to True when they are unfrozen. With DDP, every rank must run the same number
|
|
28
|
+
of steps; uneven inputs under `Join` stop with an error.
|
|
26
29
|
3. **Write the goal sentence with the user**: what the run trains and what it is judged by at the end (for example
|
|
27
30
|
"ResNet-18 on CIFAR-10, judged by test accuracy after 30 epochs"). Ask; do not invent the metric.
|
|
28
31
|
4. **Wrap.** Right after the optimizer and scheduler are created:
|
|
@@ -46,7 +49,10 @@ Keep test data and labels out of the training process, evaluate the final checkp
|
|
|
46
49
|
machine has no agent, use `backend="mailbox:<folder on the GPU machine>"`, check it there with
|
|
47
50
|
`opal doctor --backend mailbox:<folder>`, and give the user the broker command:
|
|
48
51
|
`opal broker <folder> --ssh <user@gpu-host> --backend claude --model claude-opus-5-5 --effort high`. With a
|
|
49
|
-
mailbox the model and effort are set on the broker, not in `backend_options`.
|
|
52
|
+
mailbox the model and effort are set on the broker, not in `backend_options`. Over ssh, the GPU host needs
|
|
53
|
+
`python3` (3.6 or newer) on its non-interactive PATH; the doctor checks it. The broker's ssh cannot answer a
|
|
54
|
+
password prompt: if the host asks for one, tell the user to log in with a key or to open a shared connection
|
|
55
|
+
first (`ssh -fNM -o ControlPath=~/.ssh/opal-%r@%h-%p <user@gpu-host>`). The broker's machine must stay awake
|
|
50
56
|
for the whole run (on a Mac: `caffeinate -i opal broker ...`); while no broker answers, the run waits.
|
|
51
57
|
7. **Smoke test before the real run**: run about 100 updates twice with a reduced `total_steps`, once unwrapped and
|
|
52
58
|
once wrapped with `backend="keep"` (no agent, no cost), and check that the losses match step for step. Time the
|
|
@@ -55,7 +61,8 @@ Keep test data and labels out of the training process, evaluate the final checkp
|
|
|
55
61
|
run's `total_steps` and the measured seconds per update (add `--decisions K`, `--every E`, `--no-blocking` or
|
|
56
62
|
`--mailbox` as the wrap call will have them). Show the user its lines as they are: the run's time without the
|
|
57
63
|
agent, the expected waiting for the agent (a range, and as a multiple of the run's own time) and the expected
|
|
58
|
-
cost. If `decisions` is not set, OPAL lowers it so that the waiting stays within 4x the run's own time
|
|
64
|
+
cost. If `decisions` is not set, OPAL lowers it so that the waiting stays within 4x the run's own time, but keeps
|
|
65
|
+
at least one decision, even when one already waits longer (short runs); say so.
|
|
59
66
|
Ask for `max_cost_usd`, and give the launch command only after the user confirms. Then switch to the real
|
|
60
67
|
backend, with `max_cost_usd`. The agent defaults to claude-opus-5-5 at effort high, the blog's
|
|
61
68
|
setting; set `backend_options=dict(model=..., effort=...)` only if the user asks (the run then warns that the blog's
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|