Plain PyTorch¶
Where step() goes¶
Call mon.step(loss) once per optimizer step, after loss.backward() and before optimizer.step(). At that point every gradient for the step exists and nothing has used it yet. Called before backward(), the monitor sees no gradients: it writes a no_gradients warning and the reports say NOT MONITORED instead of healthy.
from ormas_monitor import Monitor
mon = Monitor(model, optimizer, out="runs/exp1", name="resnet18 baseline")
for epoch in range(epochs):
for x, y in loader:
loss = loss_fn(model(x), y)
optimizer.zero_grad(set_to_none=True)
loss.backward()
mon.step(loss)
optimizer.step()
mon.close()
Or as a context manager, which calls close() even if training raises:
with Monitor(model, optimizer, out="runs/exp1") as mon:
for x, y in loader:
...
mon.step(loss)
optimizer.step()
close() writes run_end, then the reports, and returns the path of the engineer report. Pass close(report=False) to skip the reports; you can build them later with ormas-monitor report runs/exp1.
A new Monitor on a directory that already holds a record raises FileExistsError. Use a fresh out= per run, or overwrite=True to replace the old record on purpose.
Gradient accumulation¶
With accumulation, there is one optimizer step per k backward passes. Call step() only on the backward pass that is followed by optimizer.step():
for i, (x, y) in enumerate(loader):
loss = loss_fn(model(x), y) / k
loss.backward()
if (i + 1) % k == 0:
mon.step(loss) # once per optimizer step
optimizer.step()
optimizer.zero_grad()
If the loss is computed somewhere other than where you call step(), pass it with mon.note_loss(loss) and call mon.step() without an argument.
Gradient clipping¶
Call step() before clipping if you want alarms on the raw gradient, which is usually what you want: clipping hides exploding gradients.
loss.backward()
mon.step(loss)
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
The arguments you will use most¶
| Argument | Default | Meaning |
|---|---|---|
out |
"ormas_run" |
Run directory for the record, metrics and reports. |
repair |
False |
True repairs certified, constant and decayed channels; "all" also silent ones. See Repair. |
check_every |
50 |
Optimizer steps between checks. Hooks do nothing on the other steps. |
patience |
4 |
Checks a channel must stay stuck before it counts as silent or is repaired. |
sigma |
2.5 |
How far, in standard deviations, a layer may move before an alarm. |
warmup |
8 |
Checks before alarms are allowed. |
name |
run directory name | A label shown in the reports. |
sinks |
() |
Where else to send events. See Sinks. |
verbose |
True |
Print one line per event. |
config |
None |
A MonitorConfig; the shortcuts above override its fields. |
scaler |
None |
A torch.amp.GradScaler. See Mixed precision. |
layers |
None |
fnmatch patterns restricting which layers are watched. See Large models. |
overwrite |
False |
Replace a record already in out. By default an existing record raises FileExistsError. |
All of them, and the configuration object, are in MonitorConfig.
What the monitor needs from your model¶
- Nothing for alarms and loss checks. Any
nn.Moduleworks. - For channel checks and the certificate:
Conv1d/2d/3dorLinearlayers, optionally followed byBatchNorm1d/2d/3dorSyncBatchNorm, followed by annn.ReLUornn.ReLU6module that is the only consumer of that output, and the optimizer passed toMonitor(SGDor the Adam family; see the conditions). A functionalF.relucall is not a module and cannot be seen by a hook, so those channels are reported as constant, never certified.
The monitor learns the wiring from the first sampled step: which tensor each BatchNorm reads during the forward pass, and the autograd graph of the loss passed to step() (or of the model's outputs if no loss with a grad_fn is given). Run a training forward pass before each step(), which is what a normal loop does. Without an autograd graph at that step, no certificate is issued for the run; the manifest event says why.