no_grad
no_grad
Status: Public API in
netcl.autograd
no_grad is a context manager that disables autograd recording. Inside a
no_grad block, ops run normally but no Node is added to the tape and no
grad_fn closure is built. The output tensors have requires_grad=False.
Use it for evaluation, inference, and any code path that should not contribute to gradients.
API
import netcl.autograd as ag
# Context manager
with ag.no_grad():
out = model(x) # no nodes recorded, no grad_fn allocated
# Functional form
ag.set_grad_enabled(False)
out = model(x)
ag.set_grad_enabled(True)
# Query
enabled = ag.is_grad_enabled() # True by default
How it works
A module-global flag _GRAD_MODE (default True) controls recording.
no_grad sets it to False for the duration of the context block.
apply_op checks the flag before creating a Node:
_GRAD_MODE = True→ output is aNode(recorded on tape if one is active)_GRAD_MODE = False→ output is a rawTensor(no node, no tape entry)
All built-in layers (Linear, Conv2d, BatchNorm2d, ResNet blocks, etc.)
accept both Node and raw Tensor inputs — no_grad works transparently
with the full model API.
Standard evaluation loop
model.eval()
correct, total = 0, 0
with ag.no_grad():
for x, y in test_loader:
logits = model(x) # raw Tensor, no graph built
pred = logits.value.to_host().argmax(-1) if hasattr(logits, 'value') else logits.to_host().argmax(-1)
correct += (pred == y_np).sum()
total += y_np.shape[0]
print(f"accuracy: {correct / total:.2%}")
Simpler when you know the model is in eval mode and inputs are plain Tensor:
with ag.no_grad():
out = model(x_tensor) # returns Tensor directly
probs = softmax(out.to_host())
Memory and performance
| with tape | with no_grad |
|
|---|---|---|
| Node allocated per op | yes | no |
grad_fn closure built |
yes | no |
| Activation tensors kept alive | yes (until backward) | no |
| Peak VRAM | ~2–3× inference | 1× inference |
| Backward possible | yes | no |
For a ResNet18 at batch 24 the tape adds ~600 MB of intermediate activations
over a plain forward pass. no_grad eliminates this entirely.
Common patterns
Inference script
ag.set_grad_enabled(False) # disable globally for the whole script
model = load_model(checkpoint_path)
model.eval()
for batch in dataloader:
out = model(batch)
process(out)
Mixed training / eval in one loop
for x, y in loader:
# --- training step ---
with ag.Tape() as tape:
loss = criterion(model(x), y)
tape.backward(loss)
optimizer.step()
optimizer.zero_grad()
# --- quick eval on same batch ---
with ag.no_grad():
acc = accuracy(model(x), y)
Gradient checkpointing workaround (memory vs compute)
If VRAM is tight, run the three triplet branches sequentially each on its own tape instead of one shared tape — this thirds peak activation memory at the cost of 3× serial compute:
optimizer.zero_grad()
for branch in [anchor, positive, negative]:
with ag.Tape() as tape:
loss = partial_loss(encoder(branch))
tape.backward(loss) # grads accumulate; activations freed after each step
optimizer.step()
See also
- Tape — the structure
no_graddisables recording on. - Autograd API —
apply_op,set_grad_enabled,is_grad_enabled. - Tensor —
requires_grad,gradfields.